Skip to content

Commit

Permalink
refactor: update contracts library to support viem
Browse files Browse the repository at this point in the history
  • Loading branch information
therealemjy committed Jan 16, 2025
1 parent 9592d46 commit 0269de5
Show file tree
Hide file tree
Showing 15 changed files with 117 additions and 55 deletions.
5 changes: 5 additions & 0 deletions .changeset/young-mails-bake.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@venusprotocol/evm": minor
---

update contracts library to support viem
Original file line number Diff line number Diff line change
@@ -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 '..';

Expand All @@ -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()),
Expand Down
21 changes: 15 additions & 6 deletions apps/evm/src/clients/api/queries/getAllowance/index.ts
Original file line number Diff line number Diff line change
@@ -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;
}
Expand All @@ -13,14 +16,20 @@ export type GetAllowanceOutput = {
};

const getAllowance = async ({
tokenContract,
publicClient,
token,
accountAddress,
spenderAddress,
}: GetAllowanceInput): Promise<GetAllowanceOutput> => {
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()),
};
};

Expand Down
24 changes: 11 additions & 13 deletions apps/evm/src/clients/api/queries/getAllowance/useGetAllowance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<GetAllowanceInput, 'tokenContract'> & { token: Token };
type TrimmedGetAllowanceInput = Omit<GetAllowanceInput, 'publicClient'> & {
token: Token;
};

export type UseGetAllowanceQueryKey = [
FunctionKey.GET_TOKEN_ALLOWANCE,
Expand All @@ -33,7 +34,7 @@ const useGetAllowance = (
options?: Partial<Options>,
) => {
const { chainId } = useChainId();
const tokenContract = useGetTokenContract({ token });
const publicClient = usePublicClient();

const queryKey: UseGetAllowanceQueryKey = [
FunctionKey.GET_TOKEN_ALLOWANCE,
Expand All @@ -47,16 +48,13 @@ const useGetAllowance = (

return useQuery({
queryKey: queryKey,

queryFn: () =>
callOrThrow({ tokenContract }, params =>
getAllowance({
spenderAddress,
accountAddress,
...params,
}),
),

getAllowance({
publicClient,
spenderAddress,
accountAddress,
token,
}),
...options,
});
};
Expand Down
24 changes: 15 additions & 9 deletions apps/evm/src/clients/api/queries/getCurrentVotes/index.spec.ts
Original file line number Diff line number Diff line change
@@ -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 '.';

Expand All @@ -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()),
});
Expand Down
18 changes: 13 additions & 5 deletions apps/evm/src/clients/api/queries/getCurrentVotes/index.ts
Original file line number Diff line number Diff line change
@@ -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;
}

Expand All @@ -12,13 +14,19 @@ export type GetCurrentVotesOutput = {
};

const getCurrentVotes = async ({
xvsVaultContract,
publicClient,
xvsVaultContractAddress,
accountAddress,
}: GetCurrentVotesInput): Promise<GetCurrentVotesOutput> => {
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()),
};
};

Expand Down
Original file line number Diff line number Diff line change
@@ -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<GetCurrentVotesInput, 'xvsVaultContract'>;
type TrimmedGetCurrentVotesInput = Omit<
GetCurrentVotesInput,
'xvsVaultContractAddress' | 'publicClient'
>;

export type UseGetCurrentVotesQueryKey = [
FunctionKey.GET_CURRENT_VOTES,
Expand All @@ -25,14 +29,17 @@ type Options = QueryObserverOptions<
>;

const useGetCurrentVotes = (input: TrimmedGetCurrentVotesInput, options?: Partial<Options>) => {
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,
});
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,17 @@ 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",
},
]
`;

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",
},
]
`;
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const abi = {{{abi}}} as const;
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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 }),
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
@@ -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)
Expand Down

0 comments on commit 0269de5

Please sign in to comment.