From 0269de5d9ed2267f433c05d30192173f863bde0a Mon Sep 17 00:00:00 2001 From: therealemjy Date: Thu, 16 Jan 2025 10:25:40 +0100 Subject: [PATCH] refactor: update contracts library to support viem --- .changeset/young-mails-bake.md | 5 ++++ .../getAllowance/__tests__/index.spec.ts | 26 +++++++++++-------- .../clients/api/queries/getAllowance/index.ts | 21 ++++++++++----- .../queries/getAllowance/useGetAllowance.ts | 24 ++++++++--------- .../api/queries/getCurrentVotes/index.spec.ts | 24 ++++++++++------- .../api/queries/getCurrentVotes/index.ts | 18 +++++++++---- .../getCurrentVotes/useGetCurrentVotes.ts | 15 ++++++++--- .../__snapshots__/index.spec.ts.snap | 8 +++--- .../generateAbis/__tests__/index.spec.ts | 2 +- .../generateAbis/abiTemplate.hbs | 1 + .../generateContracts/generateAbis/index.ts | 16 +++++++++++- .../genericContractGettersTemplate.hbs | 2 ++ .../uniqueContractGettersTemplate.hbs | 2 ++ .../uniquePerPoolContractGettersTemplate.hbs | 2 ++ .../getUniqueContractAddress/index.ts | 6 ++++- 15 files changed, 117 insertions(+), 55 deletions(-) create mode 100644 .changeset/young-mails-bake.md create mode 100644 apps/evm/src/libs/contracts/scripts/generateContractRecords/generateContracts/generateAbis/abiTemplate.hbs diff --git a/.changeset/young-mails-bake.md b/.changeset/young-mails-bake.md new file mode 100644 index 0000000000..9132b8045c --- /dev/null +++ b/.changeset/young-mails-bake.md @@ -0,0 +1,5 @@ +--- +"@venusprotocol/evm": minor +--- + +update contracts library to support viem diff --git a/apps/evm/src/clients/api/queries/getAllowance/__tests__/index.spec.ts b/apps/evm/src/clients/api/queries/getAllowance/__tests__/index.spec.ts index 999bedc752..95e0e260a3 100644 --- a/apps/evm/src/clients/api/queries/getAllowance/__tests__/index.spec.ts +++ b/apps/evm/src/clients/api/queries/getAllowance/__tests__/index.spec.ts @@ -1,10 +1,9 @@ import BigNumber from 'bignumber.js'; import { BigNumber as BN } from 'ethers'; +import type { PublicClient } from 'viem'; import fakeAddress from '__mocks__/models/address'; -import fakeSigner from '__mocks__/models/signer'; - -import type { Vrt } from 'libs/contracts'; +import { vrt } from '__mocks__/models/tokens'; import getAllowance from '..'; @@ -14,21 +13,26 @@ describe('api/queries/getAllowance', () => { test('returns the allowance on success', async () => { const fakeAllowanceMantissa = BN.from(10000); - const vrtAllowanceMock = vi.fn(async () => fakeAllowanceMantissa); + const readContractMock = vi.fn(async () => fakeAllowanceMantissa); - const fakeContract = { - allowance: vrtAllowanceMock, - signer: fakeSigner, - } as unknown as Vrt; + const fakePublicClient = { + readContract: readContractMock, + } as unknown as PublicClient; const response = await getAllowance({ - tokenContract: fakeContract, + token: vrt, + publicClient: fakePublicClient, spenderAddress: fakeSpenderAddress, accountAddress: fakeAddress, }); - expect(vrtAllowanceMock).toHaveBeenCalledTimes(1); - expect(vrtAllowanceMock).toHaveBeenCalledWith(fakeAddress, fakeSpenderAddress); + expect(readContractMock).toHaveBeenCalledTimes(1); + expect(readContractMock).toHaveBeenCalledWith({ + abi: expect.any(Object), + address: vrt.address, + functionName: 'allowance', + args: [fakeAddress, fakeSpenderAddress], + }); expect(response.allowanceMantissa instanceof BigNumber).toBe(true); expect(response).toEqual({ allowanceMantissa: new BigNumber(fakeAllowanceMantissa.toString()), diff --git a/apps/evm/src/clients/api/queries/getAllowance/index.ts b/apps/evm/src/clients/api/queries/getAllowance/index.ts index 95cc737a78..0a6c1fde1a 100644 --- a/apps/evm/src/clients/api/queries/getAllowance/index.ts +++ b/apps/evm/src/clients/api/queries/getAllowance/index.ts @@ -1,9 +1,12 @@ -import BigNumber from 'bignumber.js'; +import type { Address, PublicClient } from 'viem'; -import type { Bep20, Vai, Vrt, Xvs } from 'libs/contracts'; +import BigNumber from 'bignumber.js'; +import { Bep20Abi } from 'libs/contracts'; +import type { Token } from 'types'; export interface GetAllowanceInput { - tokenContract: Vai | Bep20 | Vrt | Xvs; + publicClient: PublicClient; + token: Token; accountAddress: string; spenderAddress: string; } @@ -13,14 +16,20 @@ export type GetAllowanceOutput = { }; const getAllowance = async ({ - tokenContract, + publicClient, + token, accountAddress, spenderAddress, }: GetAllowanceInput): Promise => { - const res = await tokenContract.allowance(accountAddress, spenderAddress); + const allowanceMantissa = await publicClient.readContract({ + abi: Bep20Abi, + address: token.address as Address, + functionName: 'allowance', + args: [accountAddress as Address, spenderAddress as Address], + }); return { - allowanceMantissa: new BigNumber(res.toString()), + allowanceMantissa: new BigNumber(allowanceMantissa.toString()), }; }; diff --git a/apps/evm/src/clients/api/queries/getAllowance/useGetAllowance.ts b/apps/evm/src/clients/api/queries/getAllowance/useGetAllowance.ts index d23be7115c..d544a77023 100644 --- a/apps/evm/src/clients/api/queries/getAllowance/useGetAllowance.ts +++ b/apps/evm/src/clients/api/queries/getAllowance/useGetAllowance.ts @@ -5,12 +5,13 @@ import getAllowance, { type GetAllowanceOutput, } from 'clients/api/queries/getAllowance'; import FunctionKey from 'constants/functionKey'; -import { useGetTokenContract } from 'libs/contracts'; import { useChainId } from 'libs/wallet'; import type { ChainId, Token } from 'types'; -import { callOrThrow } from 'utilities'; +import { usePublicClient } from 'wagmi'; -type TrimmedGetAllowanceInput = Omit & { token: Token }; +type TrimmedGetAllowanceInput = Omit & { + token: Token; +}; export type UseGetAllowanceQueryKey = [ FunctionKey.GET_TOKEN_ALLOWANCE, @@ -33,7 +34,7 @@ const useGetAllowance = ( options?: Partial, ) => { const { chainId } = useChainId(); - const tokenContract = useGetTokenContract({ token }); + const publicClient = usePublicClient(); const queryKey: UseGetAllowanceQueryKey = [ FunctionKey.GET_TOKEN_ALLOWANCE, @@ -47,16 +48,13 @@ const useGetAllowance = ( return useQuery({ queryKey: queryKey, - queryFn: () => - callOrThrow({ tokenContract }, params => - getAllowance({ - spenderAddress, - accountAddress, - ...params, - }), - ), - + getAllowance({ + publicClient, + spenderAddress, + accountAddress, + token, + }), ...options, }); }; diff --git a/apps/evm/src/clients/api/queries/getCurrentVotes/index.spec.ts b/apps/evm/src/clients/api/queries/getCurrentVotes/index.spec.ts index ed50c8eb1f..1fb8b2f411 100644 --- a/apps/evm/src/clients/api/queries/getCurrentVotes/index.spec.ts +++ b/apps/evm/src/clients/api/queries/getCurrentVotes/index.spec.ts @@ -1,7 +1,6 @@ import BigNumber from 'bignumber.js'; import { BigNumber as BN } from 'ethers'; - -import type { XvsVault } from 'libs/contracts'; +import type { PublicClient } from 'viem'; import getCurrentVotes from '.'; @@ -10,20 +9,27 @@ const fakeAccountAddress = '0x000000000000000000000000000000000AcCoUnt'; describe('api/queries/getCurrentVotes', () => { test('returns current votes on success', async () => { const fakeOutput = BN.from(10000); + const fakeXvsVaultContractAddress = '0x00000000000000000000000000000000XVsVault'; - const getCurrentVotesMock = vi.fn(async () => fakeOutput); + const readContractMock = vi.fn(async () => fakeOutput); - const fakeContract = { - getCurrentVotes: getCurrentVotesMock, - } as unknown as XvsVault; + const fakePublicClient = { + readContract: readContractMock, + } as unknown as PublicClient; const response = await getCurrentVotes({ - xvsVaultContract: fakeContract, + xvsVaultContractAddress: fakeXvsVaultContractAddress, + publicClient: fakePublicClient, accountAddress: fakeAccountAddress, }); - expect(getCurrentVotesMock).toHaveBeenCalledTimes(1); - expect(getCurrentVotesMock).toHaveBeenCalledWith(fakeAccountAddress); + expect(readContractMock).toHaveBeenCalledTimes(1); + expect(readContractMock).toHaveBeenCalledWith({ + abi: expect.any(Object), + address: fakeXvsVaultContractAddress, + functionName: 'getCurrentVotes', + args: [fakeAccountAddress], + }); expect(response).toEqual({ votesMantissa: new BigNumber(fakeOutput.toString()), }); diff --git a/apps/evm/src/clients/api/queries/getCurrentVotes/index.ts b/apps/evm/src/clients/api/queries/getCurrentVotes/index.ts index 8495483be5..a992a83b5f 100644 --- a/apps/evm/src/clients/api/queries/getCurrentVotes/index.ts +++ b/apps/evm/src/clients/api/queries/getCurrentVotes/index.ts @@ -1,9 +1,11 @@ import BigNumber from 'bignumber.js'; +import { XvsVaultAbi } from 'libs/contracts'; -import type { XvsVault } from 'libs/contracts'; +import type { Address, PublicClient } from 'viem'; export interface GetCurrentVotesInput { - xvsVaultContract: XvsVault; + publicClient: PublicClient; + xvsVaultContractAddress: string; accountAddress: string; } @@ -12,13 +14,19 @@ export type GetCurrentVotesOutput = { }; const getCurrentVotes = async ({ - xvsVaultContract, + publicClient, + xvsVaultContractAddress, accountAddress, }: GetCurrentVotesInput): Promise => { - const resp = await xvsVaultContract.getCurrentVotes(accountAddress); + const votesMantissa = await publicClient.readContract({ + address: xvsVaultContractAddress as Address, + abi: XvsVaultAbi, + functionName: 'getCurrentVotes', + args: [accountAddress as Address], + }); return { - votesMantissa: new BigNumber(resp.toString()), + votesMantissa: new BigNumber(votesMantissa.toString()), }; }; diff --git a/apps/evm/src/clients/api/queries/getCurrentVotes/useGetCurrentVotes.ts b/apps/evm/src/clients/api/queries/getCurrentVotes/useGetCurrentVotes.ts index 179c3cecf0..6c9233a307 100644 --- a/apps/evm/src/clients/api/queries/getCurrentVotes/useGetCurrentVotes.ts +++ b/apps/evm/src/clients/api/queries/getCurrentVotes/useGetCurrentVotes.ts @@ -1,15 +1,19 @@ import { type QueryObserverOptions, useQuery } from '@tanstack/react-query'; +import { usePublicClient } from 'wagmi'; import getCurrentVotes, { type GetCurrentVotesInput, type GetCurrentVotesOutput, } from 'clients/api/queries/getCurrentVotes'; import FunctionKey from 'constants/functionKey'; -import { useGetXvsVaultContract } from 'libs/contracts'; +import { useGetXvsVaultContractAddress } from 'libs/contracts'; import { governanceChain } from 'libs/wallet'; import { callOrThrow } from 'utilities'; -type TrimmedGetCurrentVotesInput = Omit; +type TrimmedGetCurrentVotesInput = Omit< + GetCurrentVotesInput, + 'xvsVaultContractAddress' | 'publicClient' +>; export type UseGetCurrentVotesQueryKey = [ FunctionKey.GET_CURRENT_VOTES, @@ -25,14 +29,17 @@ type Options = QueryObserverOptions< >; const useGetCurrentVotes = (input: TrimmedGetCurrentVotesInput, options?: Partial) => { - const xvsVaultContract = useGetXvsVaultContract({ + const publicClient = usePublicClient({ chainId: governanceChain.id, }); + const xvsVaultContractAddress = useGetXvsVaultContractAddress(); return useQuery({ queryKey: [FunctionKey.GET_CURRENT_VOTES, input], queryFn: () => - callOrThrow({ xvsVaultContract }, params => getCurrentVotes({ ...params, ...input })), + callOrThrow({ xvsVaultContractAddress }, params => + getCurrentVotes({ publicClient, ...params, ...input }), + ), ...options, }); }; diff --git a/apps/evm/src/libs/contracts/scripts/generateContractRecords/generateContracts/generateAbis/__tests__/__snapshots__/index.spec.ts.snap b/apps/evm/src/libs/contracts/scripts/generateContractRecords/generateContracts/generateAbis/__tests__/__snapshots__/index.spec.ts.snap index e67d9ef15c..5196736ada 100644 --- a/apps/evm/src/libs/contracts/scripts/generateContractRecords/generateContracts/generateAbis/__tests__/__snapshots__/index.spec.ts.snap +++ b/apps/evm/src/libs/contracts/scripts/generateContractRecords/generateContracts/generateAbis/__tests__/__snapshots__/index.spec.ts.snap @@ -12,8 +12,8 @@ exports[`generateAbis > calls writeFile with the right arguments 1`] = ` exports[`generateAbis > calls writeFile with the right arguments 2`] = ` [ { - "content": "[{"inputs":[{"internalType":"address","name":"market","type":"address"},{"internalType":"enum ComptrollerStorage.Action","name":"action","type":"uint8"}],"name":"ActionPaused","type":"error"}]", - "outputPath": "fake/output/director/path/IsolatedPoolComptroller.json", + "content": "export const abi = [{"inputs":[{"internalType":"address","name":"poolRegistryAddress","type":"address"},{"internalType":"address","name":"asset","type":"address"}],"name":"getPoolsSupportedByAsset","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"}] as const;", + "outputPath": "fake/output/director/path/PoolLens.ts", }, ] `; @@ -21,8 +21,8 @@ exports[`generateAbis > calls writeFile with the right arguments 2`] = ` exports[`generateAbis > calls writeFile with the right arguments 3`] = ` [ { - "content": "[{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"amountMax","type":"uint256"}],"name":"ExcessiveInputAmount","type":"error"}]", - "outputPath": "fake/output/director/path/SwapRouter.json", + "content": "[{"inputs":[{"internalType":"address","name":"market","type":"address"},{"internalType":"enum ComptrollerStorage.Action","name":"action","type":"uint8"}],"name":"ActionPaused","type":"error"}]", + "outputPath": "fake/output/director/path/IsolatedPoolComptroller.json", }, ] `; diff --git a/apps/evm/src/libs/contracts/scripts/generateContractRecords/generateContracts/generateAbis/__tests__/index.spec.ts b/apps/evm/src/libs/contracts/scripts/generateContractRecords/generateContracts/generateAbis/__tests__/index.spec.ts index 551005a5ab..a6ca639f05 100644 --- a/apps/evm/src/libs/contracts/scripts/generateContractRecords/generateContracts/generateAbis/__tests__/index.spec.ts +++ b/apps/evm/src/libs/contracts/scripts/generateContractRecords/generateContracts/generateAbis/__tests__/index.spec.ts @@ -14,7 +14,7 @@ describe('generateAbis', () => { contractConfigs: fakeContractConfigs, }); - expect(writeFile).toHaveBeenCalledTimes(fakeContractConfigs.length); + expect(writeFile).toHaveBeenCalledTimes(fakeContractConfigs.length * 2); fakeContractConfigs.forEach((_fakeContractConfig, index) => expect((writeFile as Vi.Mock).mock.calls[index]).toMatchSnapshot(), diff --git a/apps/evm/src/libs/contracts/scripts/generateContractRecords/generateContracts/generateAbis/abiTemplate.hbs b/apps/evm/src/libs/contracts/scripts/generateContractRecords/generateContracts/generateAbis/abiTemplate.hbs new file mode 100644 index 0000000000..16001743ed --- /dev/null +++ b/apps/evm/src/libs/contracts/scripts/generateContractRecords/generateContracts/generateAbis/abiTemplate.hbs @@ -0,0 +1 @@ +export const abi = {{{abi}}} as const; \ No newline at end of file diff --git a/apps/evm/src/libs/contracts/scripts/generateContractRecords/generateContracts/generateAbis/index.ts b/apps/evm/src/libs/contracts/scripts/generateContractRecords/generateContracts/generateAbis/index.ts index 0d5a5d6f3f..65baeec557 100644 --- a/apps/evm/src/libs/contracts/scripts/generateContractRecords/generateContracts/generateAbis/index.ts +++ b/apps/evm/src/libs/contracts/scripts/generateContractRecords/generateContracts/generateAbis/index.ts @@ -1,6 +1,13 @@ +import { readFileSync } from 'node:fs'; +import { compile } from 'handlebars'; import type { ContractConfig } from 'libs/contracts/config'; import writeFile from 'utilities/writeFile'; +const ABI_TEMPLATE_FILE_PATH = `${__dirname}/abiTemplate.hbs`; + +const abiTemplateBuffer = readFileSync(ABI_TEMPLATE_FILE_PATH); +const abiTemplate = compile(abiTemplateBuffer.toString()); + export interface GenerateTypesInput { contractConfigs: ContractConfig[]; outputDirectoryPath: string; @@ -9,8 +16,15 @@ export interface GenerateTypesInput { export const generateAbis = ({ contractConfigs, outputDirectoryPath }: GenerateTypesInput) => // Go through config and extract ABIs into separate files contractConfigs.forEach(contractConfig => { + const abi = JSON.stringify(contractConfig.abi); + writeFile({ outputPath: `${outputDirectoryPath}/${contractConfig.name}.json`, - content: JSON.stringify(contractConfig.abi), + content: abi, + }); + + writeFile({ + outputPath: `${outputDirectoryPath}/${contractConfig.name}.ts`, + content: abiTemplate({ abi }), }); }); diff --git a/apps/evm/src/libs/contracts/scripts/generateContractRecords/generateContracts/generateGetters/templates/genericContractGettersTemplate.hbs b/apps/evm/src/libs/contracts/scripts/generateContractRecords/generateContracts/generateGetters/templates/genericContractGettersTemplate.hbs index 1fed7af98d..edf0d02ddf 100644 --- a/apps/evm/src/libs/contracts/scripts/generateContractRecords/generateContracts/generateGetters/templates/genericContractGettersTemplate.hbs +++ b/apps/evm/src/libs/contracts/scripts/generateContractRecords/generateContracts/generateGetters/templates/genericContractGettersTemplate.hbs @@ -7,6 +7,8 @@ import { {{contractName}} } from 'libs/contracts/generated/infos/contractTypes'; import { useProvider, useSigner } from 'libs/wallet'; import { ChainId } from 'types'; +export { abi as {{contractName}}Abi } from 'libs/contracts/generated/infos/abis/{{contractName}}'; + interface Get{{contractName}}ContractInput { address: string; signerOrProvider: Signer | Provider; diff --git a/apps/evm/src/libs/contracts/scripts/generateContractRecords/generateContracts/generateGetters/templates/uniqueContractGettersTemplate.hbs b/apps/evm/src/libs/contracts/scripts/generateContractRecords/generateContracts/generateGetters/templates/uniqueContractGettersTemplate.hbs index 9bd35bfe3b..e8a78886b5 100644 --- a/apps/evm/src/libs/contracts/scripts/generateContractRecords/generateContracts/generateGetters/templates/uniqueContractGettersTemplate.hbs +++ b/apps/evm/src/libs/contracts/scripts/generateContractRecords/generateContracts/generateGetters/templates/uniqueContractGettersTemplate.hbs @@ -8,6 +8,8 @@ import { getUniqueContractAddress } from 'libs/contracts/utilities/getUniqueCont import { useChainId, useProvider, useSigner } from 'libs/wallet'; import { ChainId } from 'types'; +export { abi as {{contractName}}Abi } from 'libs/contracts/generated/infos/abis/{{contractName}}'; + interface Get{{contractName}}ContractAddressInput { chainId: ChainId; } diff --git a/apps/evm/src/libs/contracts/scripts/generateContractRecords/generateContracts/generateGetters/templates/uniquePerPoolContractGettersTemplate.hbs b/apps/evm/src/libs/contracts/scripts/generateContractRecords/generateContracts/generateGetters/templates/uniquePerPoolContractGettersTemplate.hbs index ae6fe481e7..5d21483b42 100644 --- a/apps/evm/src/libs/contracts/scripts/generateContractRecords/generateContracts/generateGetters/templates/uniquePerPoolContractGettersTemplate.hbs +++ b/apps/evm/src/libs/contracts/scripts/generateContractRecords/generateContracts/generateGetters/templates/uniquePerPoolContractGettersTemplate.hbs @@ -8,6 +8,8 @@ import { {{contractName}} } from 'libs/contracts/generated/infos/contractTypes'; import { useChainId, useProvider, useSigner } from 'libs/wallet'; import { ChainId } from 'types'; +export { abi as {{contractName}}Abi } from 'libs/contracts/generated/infos/abis/{{contractName}}'; + interface Get{{contractName}}ContractAddressInput { comptrollerContractAddress: string; chainId: ChainId; diff --git a/apps/evm/src/libs/contracts/utilities/getUniqueContractAddress/index.ts b/apps/evm/src/libs/contracts/utilities/getUniqueContractAddress/index.ts index 21c798f3d3..8fc2a3c091 100644 --- a/apps/evm/src/libs/contracts/utilities/getUniqueContractAddress/index.ts +++ b/apps/evm/src/libs/contracts/utilities/getUniqueContractAddress/index.ts @@ -1,13 +1,17 @@ import addresses from 'libs/contracts/generated/infos/addresses'; import type { UniqueContractName } from 'libs/contracts/generated/infos/types'; import type { ChainId } from 'types'; +import type { Address } from 'viem'; export type GetUniqueContractAddressInput = { name: UniqueContractName; chainId: ChainId; }; -export const getUniqueContractAddress = ({ name, chainId }: GetUniqueContractAddressInput) => { +export const getUniqueContractAddress = ({ + name, + chainId, +}: GetUniqueContractAddressInput): Address | undefined => { const contractAddresses = addresses[name]; return Object.prototype.hasOwnProperty.call(contractAddresses, chainId)