From c49b1d5c845c1c623bf8f0d34b7f9e5ce95b1649 Mon Sep 17 00:00:00 2001 From: Fawad Ali Date: Fri, 24 May 2024 17:12:58 +0200 Subject: [PATCH 1/7] Create contract handler for caching and loading contracts --- lib/services/contract-handler.ts | 64 ++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 lib/services/contract-handler.ts diff --git a/lib/services/contract-handler.ts b/lib/services/contract-handler.ts new file mode 100644 index 0000000..8d4124b --- /dev/null +++ b/lib/services/contract-handler.ts @@ -0,0 +1,64 @@ +import { + Abi, + Address, + Client, + GetContractReturnType, + PublicClient, + WalletClient, + getContract, +} from 'viem'; + +type KeyedClient = { + wallet: Client; + public: Client; +}; + +/** + * Abstraction for loading contracts. + */ +export class ContractHandler { + private memoizedContracts: { + [address: string]: ReturnType; + }; + + constructor( + private walletClient: WalletClient, + private publicClient?: PublicClient, + ) { + this.memoizedContracts = {}; + } + + /** + * Get contract and cache it. + * + * @param address Address of the contract. + * @param abi ABI of the contract. + * @returns The viem interface of a contract with wallet and public + * actions. + */ + public getContract( + address: Address, + abi: TAbi, + ) { + if (address in this.memoizedContracts) { + return this.memoizedContracts[address] as GetContractReturnType< + TAbi, + KeyedClient, + Address + >; + } + + const contract = getContract({ + address, + abi, + client: { + wallet: this.walletClient, + public: this.publicClient, + }, + }); + + this.memoizedContracts[address] = contract; + + return contract; + } +} From 893a2f5bde59c3fd91f1a253bcf7506bbba2632f Mon Sep 17 00:00:00 2001 From: Fawad Ali Date: Mon, 27 May 2024 11:49:55 +0200 Subject: [PATCH 2/7] Update PufferVault address and add balanceOf and pufETHRate methods --- lib/api/puffer-client.ts | 40 +++++++++++++++++++++++++------- lib/contracts/addresses.ts | 5 ++-- lib/services/contract-handler.ts | 5 ++-- 3 files changed, 37 insertions(+), 13 deletions(-) diff --git a/lib/api/puffer-client.ts b/lib/api/puffer-client.ts index 89e2549..90e4088 100644 --- a/lib/api/puffer-client.ts +++ b/lib/api/puffer-client.ts @@ -74,15 +74,7 @@ export class PufferClient { * transaction. */ public depositETH(walletAddress: Address) { - const contract = getContract({ - address: this.chainAddresses.PufferVault as Address, - abi: this.chainAbis.PufferVaultV2, - client: { - wallet: this.walletClient, - // Public client is needed for simulation. - public: this.publicClient, - }, - }); + const contract = this.getPufferVaultContract(); const transact = async (value: bigint) => await contract.write.depositETH([walletAddress], { @@ -98,4 +90,34 @@ export class PufferClient { return { transact, estimate }; } + + /** + * Check the pufETH balance of the wallet. + * + * @param walletAddress Wallet address to check the balance of. + * @returns pufETH balance in wei. + */ + public async balanceOf(walletAddress: Address) { + const contract = this.getPufferVaultContract(); + return await contract.read.balanceOf([walletAddress]); + } + + public async pufETHRate() { + const onePufETH = BigInt(1e18); + const contract = this.getPufferVaultContract(); + + return await contract.read.previewDeposit([onePufETH]); + } + + private getPufferVaultContract() { + return getContract({ + address: this.chainAddresses.PufferVault, + abi: this.chainAbis.PufferVaultV2, + client: { + wallet: this.walletClient, + // Public client is needed for simulation. + public: this.publicClient, + }, + }); + } } diff --git a/lib/contracts/addresses.ts b/lib/contracts/addresses.ts index 5649d9e..9afe206 100644 --- a/lib/contracts/addresses.ts +++ b/lib/contracts/addresses.ts @@ -1,13 +1,14 @@ +import { Address } from 'viem'; import { Chain } from '../chains/constants'; // Source of truth: // https://github.com/PufferFinance/Deployments-and-ACL/tree/main/docs/deployments -export const CHAIN_ADDRESSES = { +export const CHAIN_ADDRESSES: { [key in Chain]: { [key: string]: Address } } = { [Chain.Mainnet]: { PufferVault: '0xD9A442856C234a39a81a089C06451EBAa4306a72', }, [Chain.Holesky]: { - PufferVault: '0x98408eadD0C7cC9AebbFB2AD2787c7473Db7A1fa', + PufferVault: '0x9196830bB4c05504E0A8475A0aD566AceEB6BeC9', }, [Chain.Anvil]: { PufferVault: '0xf770bF9384c5aaD8b8a6EFAb5951CF089656A371', diff --git a/lib/services/contract-handler.ts b/lib/services/contract-handler.ts index 8d4124b..d64d22f 100644 --- a/lib/services/contract-handler.ts +++ b/lib/services/contract-handler.ts @@ -39,7 +39,8 @@ export class ContractHandler { public getContract( address: Address, abi: TAbi, - ) { + ): GetContractReturnType { + // TODO: Should memoization be removed? if (address in this.memoizedContracts) { return this.memoizedContracts[address] as GetContractReturnType< TAbi, @@ -59,6 +60,6 @@ export class ContractHandler { this.memoizedContracts[address] = contract; - return contract; + return contract as GetContractReturnType; } } From 0c1efbba7a0aff7ed26e38f56467a99fb80ab857 Mon Sep 17 00:00:00 2001 From: Fawad Ali Date: Mon, 27 May 2024 12:16:02 +0200 Subject: [PATCH 3/7] Add depositStETH method --- lib/api/puffer-client.ts | 40 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/lib/api/puffer-client.ts b/lib/api/puffer-client.ts index 90e4088..d7f2dc1 100644 --- a/lib/api/puffer-client.ts +++ b/lib/api/puffer-client.ts @@ -102,11 +102,46 @@ export class PufferClient { return await contract.read.balanceOf([walletAddress]); } + /** + * Get the rate of pufETH compared to ETH. + * + * @returns Rate of 1 pufETH compared to 1 ETH. + */ public async pufETHRate() { - const onePufETH = BigInt(1e18); + const oneWei = BigInt(1e18); const contract = this.getPufferVaultContract(); - return await contract.read.previewDeposit([onePufETH]); + return await contract.read.previewDeposit([oneWei]); + } + + /** + * Deposit stETH to the given wallet address. This doesn't make the + * transaction but returns two methods namely `transact` and + * `estimate`. + * + * @param walletAddress Wallet address to get the ETH from. + * @param value Value in wei of the stETH to deposit. + * @returns `transact: () => Promise
` - Used to make the + * transaction with the given value. + * + * `estimate: () => Promise` - Gas estimate of the + * transaction. + */ + public async depositStETH(walletAddress: Address, value: bigint) { + const contract = this.getPufferVaultContract(); + + const transact = async () => + await contract.write.depositStETH([value, walletAddress], { + account: walletAddress, + chain: this.viemChain, + }); + + const estimate = async () => + await contract.estimateGas.depositStETH([value, walletAddress], { + account: walletAddress, + }); + + return { transact, estimate }; } private getPufferVaultContract() { @@ -115,7 +150,6 @@ export class PufferClient { abi: this.chainAbis.PufferVaultV2, client: { wallet: this.walletClient, - // Public client is needed for simulation. public: this.publicClient, }, }); From 40979d382bdc09aeefad939946552be8b1713bc5 Mon Sep 17 00:00:00 2001 From: Fawad Ali Date: Mon, 27 May 2024 14:10:49 +0200 Subject: [PATCH 4/7] Add a separate handler for PufferVaultV2 to separate concerns and make code modular --- lib/api/puffer-client.ts | 130 ++++------------------- lib/contracts/addresses.ts | 3 +- lib/contracts/puffer-vault-handler.ts | 142 ++++++++++++++++++++++++++ lib/services/contract-handler.ts | 65 ------------ 4 files changed, 162 insertions(+), 178 deletions(-) create mode 100644 lib/contracts/puffer-vault-handler.ts delete mode 100644 lib/services/contract-handler.ts diff --git a/lib/api/puffer-client.ts b/lib/api/puffer-client.ts index d7f2dc1..f435d05 100644 --- a/lib/api/puffer-client.ts +++ b/lib/api/puffer-client.ts @@ -1,55 +1,57 @@ import { - Address, PublicClient, WalletClient, createPublicClient, createWalletClient, - getContract, http, } from 'viem'; -import { Chain, VIEM_CHAINS, ViemChain } from '../chains/constants'; -import { CHAIN_ADDRESSES } from '../contracts/addresses'; -import { ValueOf } from '../utils/types'; -import { CHAIN_ABIS } from '../contracts/abis/abis'; +import { Chain, VIEM_CHAINS } from '../chains/constants'; +import { PufferVaultHandler } from '../contracts/puffer-vault-handler'; /** * The core class and the main entry point of the Puffer SDK. */ export class PufferClient { - private chainAddresses: ValueOf; - private chainAbis: ValueOf; - - private viemChain: ViemChain; private walletClient: WalletClient; private publicClient: PublicClient; + // Contract Handlers + public vault: PufferVaultHandler; + /** * Create the Puffer Client. + * * @param chain Chain to use for the client. - * @param walletClient The wallet client to use for wallet interactions. - * @param publicClient The public client to use for public interactions. + * @param walletClient The wallet client to use for wallet + * interactions. + * @param publicClient The public client to use for public + * interactions. */ constructor( chain: Chain, walletClient?: WalletClient, publicClient?: PublicClient, ) { - this.chainAddresses = CHAIN_ADDRESSES[chain]; - this.chainAbis = CHAIN_ABIS[chain]; - this.viemChain = VIEM_CHAINS[chain]; + const viemChain = VIEM_CHAINS[chain]; this.walletClient = walletClient ?? createWalletClient({ - chain: this.viemChain, + chain: viemChain, transport: http(), }); this.publicClient = publicClient ?? createPublicClient({ - chain: this.viemChain, + chain: viemChain, transport: http(), }); + + this.vault = new PufferVaultHandler( + chain, + this.walletClient, + this.publicClient, + ); } /** @@ -60,98 +62,4 @@ export class PufferClient { public async requestAddresses() { return await this.walletClient.requestAddresses(); } - - /** - * Deposit ETH to the given wallet address. This doesn't make the - * transaction but returns two methods namely `transact` and - * `estimate`. - * - * @param walletAddress Wallet address to get the ETH from. - * @returns `transact: (value: bigint) => Promise
` - Used to - * make the transaction with the given value. - * - * `estimate: () => Promise` - Gas estimate of the - * transaction. - */ - public depositETH(walletAddress: Address) { - const contract = this.getPufferVaultContract(); - - const transact = async (value: bigint) => - await contract.write.depositETH([walletAddress], { - account: walletAddress, - chain: this.viemChain, - value, - }); - - const estimate = async () => - await contract.estimateGas.depositETH([walletAddress], { - account: walletAddress, - }); - - return { transact, estimate }; - } - - /** - * Check the pufETH balance of the wallet. - * - * @param walletAddress Wallet address to check the balance of. - * @returns pufETH balance in wei. - */ - public async balanceOf(walletAddress: Address) { - const contract = this.getPufferVaultContract(); - return await contract.read.balanceOf([walletAddress]); - } - - /** - * Get the rate of pufETH compared to ETH. - * - * @returns Rate of 1 pufETH compared to 1 ETH. - */ - public async pufETHRate() { - const oneWei = BigInt(1e18); - const contract = this.getPufferVaultContract(); - - return await contract.read.previewDeposit([oneWei]); - } - - /** - * Deposit stETH to the given wallet address. This doesn't make the - * transaction but returns two methods namely `transact` and - * `estimate`. - * - * @param walletAddress Wallet address to get the ETH from. - * @param value Value in wei of the stETH to deposit. - * @returns `transact: () => Promise
` - Used to make the - * transaction with the given value. - * - * `estimate: () => Promise` - Gas estimate of the - * transaction. - */ - public async depositStETH(walletAddress: Address, value: bigint) { - const contract = this.getPufferVaultContract(); - - const transact = async () => - await contract.write.depositStETH([value, walletAddress], { - account: walletAddress, - chain: this.viemChain, - }); - - const estimate = async () => - await contract.estimateGas.depositStETH([value, walletAddress], { - account: walletAddress, - }); - - return { transact, estimate }; - } - - private getPufferVaultContract() { - return getContract({ - address: this.chainAddresses.PufferVault, - abi: this.chainAbis.PufferVaultV2, - client: { - wallet: this.walletClient, - public: this.publicClient, - }, - }); - } } diff --git a/lib/contracts/addresses.ts b/lib/contracts/addresses.ts index 9afe206..1b522e7 100644 --- a/lib/contracts/addresses.ts +++ b/lib/contracts/addresses.ts @@ -1,9 +1,8 @@ -import { Address } from 'viem'; import { Chain } from '../chains/constants'; // Source of truth: // https://github.com/PufferFinance/Deployments-and-ACL/tree/main/docs/deployments -export const CHAIN_ADDRESSES: { [key in Chain]: { [key: string]: Address } } = { +export const CHAIN_ADDRESSES = { [Chain.Mainnet]: { PufferVault: '0xD9A442856C234a39a81a089C06451EBAa4306a72', }, diff --git a/lib/contracts/puffer-vault-handler.ts b/lib/contracts/puffer-vault-handler.ts new file mode 100644 index 0000000..325a4e7 --- /dev/null +++ b/lib/contracts/puffer-vault-handler.ts @@ -0,0 +1,142 @@ +import { + Address, + Chain as ViemChain, + PublicClient, + WalletClient, + getContract, +} from 'viem'; +import { CHAIN_ABIS } from './abis/abis'; +import { Chain, VIEM_CHAINS } from '../chains/constants'; +import { CHAIN_ADDRESSES } from './addresses'; + +/** + * Handler for the `PufferVaultV2` contract exposing methods to interact + * with the contract. + */ +export class PufferVaultHandler { + private viemChain: ViemChain; + + /** + * Create the handler for the `PufferVaultV2` contract exposing + * methods to interact with the contract. + * + * @param chain Chain to use for the client. + * @param walletClient The wallet client to use for wallet + * interactions. + * @param publicClient The public client to use for public + * interactions. + */ + constructor( + private chain: Chain, + private walletClient: WalletClient, + private publicClient: PublicClient, + ) { + this.viemChain = VIEM_CHAINS[chain]; + } + + // This is a method because the typings are complex and lost when + // trying to make it a member. + private getContract() { + return getContract({ + address: CHAIN_ADDRESSES[this.chain].PufferVault as Address, + abi: CHAIN_ABIS[this.chain].PufferVaultV2, + client: { + wallet: this.walletClient, + public: this.publicClient, + }, + }); + } + + /** + * Deposit ETH to the given wallet address. This doesn't make the + * transaction but returns two methods namely `transact` and + * `estimate`. + * + * @param walletAddress Wallet address to get the ETH from. + * @returns `transact: (value: bigint) => Promise
` - Used to + * make the transaction with the given value. + * + * `estimate: () => Promise` - Gas estimate of the + * transaction. + */ + public depositETH(walletAddress: Address) { + const transact = async (value: bigint) => + await this.getContract().write.depositETH([walletAddress], { + account: walletAddress, + chain: this.viemChain, + value, + }); + + const estimate = async () => + await this.getContract().estimateGas.depositETH([walletAddress], { + account: walletAddress, + }); + + return { transact, estimate }; + } + + /** + * Check the pufETH balance of the wallet. + * + * @param walletAddress Wallet address to check the balance of. + * @returns pufETH balance in wei. + */ + public async balanceOf(walletAddress: Address) { + return await this.getContract().read.balanceOf([walletAddress]); + } + + /** + * Get the rate of pufETH compared to ETH. + * + * @returns Rate of 1 pufETH compared to 1 ETH. + */ + public async pufETHRate() { + const oneWei = BigInt(1e18); + return await this.getContract().read.previewDeposit([oneWei]); + } + + /** + * Deposit stETH to the given wallet address. This doesn't make the + * transaction but returns two methods namely `transact` and + * `estimate`. + * + * @param walletAddress Wallet address to get the ETH from. + * @param value Value in wei of the stETH to deposit. + * @returns `transact: () => Promise
` - Used to make the + * transaction with the given value. + * + * `estimate: () => Promise` - Gas estimate of the + * transaction. + */ + public async depositStETH(walletAddress: Address, value: bigint) { + const transact = async () => + await this.getContract().write.depositStETH([value, walletAddress], { + account: walletAddress, + chain: this.viemChain, + }); + + const estimate = async () => + await this.getContract().estimateGas.depositStETH( + [value, walletAddress], + { + account: walletAddress, + }, + ); + + return { transact, estimate }; + } + + /** + * Get the allowance for the given owner and spender. + * + * @param ownerAddress Address of the owner. + * @param spenderAddress Address of the spender. + * @returns Allowance for the given owner and spender. + */ + public async getAllowance(ownerAddress: Address, spenderAddress: Address) { + return await this.getContract().read.allowance([ + ownerAddress, + spenderAddress, + ]); + } +} diff --git a/lib/services/contract-handler.ts b/lib/services/contract-handler.ts deleted file mode 100644 index d64d22f..0000000 --- a/lib/services/contract-handler.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { - Abi, - Address, - Client, - GetContractReturnType, - PublicClient, - WalletClient, - getContract, -} from 'viem'; - -type KeyedClient = { - wallet: Client; - public: Client; -}; - -/** - * Abstraction for loading contracts. - */ -export class ContractHandler { - private memoizedContracts: { - [address: string]: ReturnType; - }; - - constructor( - private walletClient: WalletClient, - private publicClient?: PublicClient, - ) { - this.memoizedContracts = {}; - } - - /** - * Get contract and cache it. - * - * @param address Address of the contract. - * @param abi ABI of the contract. - * @returns The viem interface of a contract with wallet and public - * actions. - */ - public getContract( - address: Address, - abi: TAbi, - ): GetContractReturnType { - // TODO: Should memoization be removed? - if (address in this.memoizedContracts) { - return this.memoizedContracts[address] as GetContractReturnType< - TAbi, - KeyedClient, - Address - >; - } - - const contract = getContract({ - address, - abi, - client: { - wallet: this.walletClient, - public: this.publicClient, - }, - }); - - this.memoizedContracts[address] = contract; - - return contract as GetContractReturnType; - } -} From 847b86e52cfb260a67d37ad632acbed050d03e13 Mon Sep 17 00:00:00 2001 From: Fawad Ali Date: Mon, 27 May 2024 14:28:05 +0200 Subject: [PATCH 5/7] Update guides and code to use PufferClient.vault --- docs/docs/getting-started.mdx | 2 +- docs/docs/guides/depositing-eth.md | 4 ++-- lib/api/puffer-client.test.ts | 2 +- lib/api/puffer-client.ts | 2 +- lib/contracts/{ => handlers}/puffer-vault-handler.ts | 6 +++--- 5 files changed, 8 insertions(+), 8 deletions(-) rename lib/contracts/{ => handlers}/puffer-vault-handler.ts (96%) diff --git a/docs/docs/getting-started.mdx b/docs/docs/getting-started.mdx index 668b6b9..702f434 100644 --- a/docs/docs/getting-started.mdx +++ b/docs/docs/getting-started.mdx @@ -78,7 +78,7 @@ const [walletAddress] = await pufferClient.requestAddresses(); Mint pufETH to your `walletAddress` or set a target recipient: ```ts -const { transact, estimate } = pufferClient.depositETH(walletAddress); +const { transact, estimate } = pufferClient.vault.depositETH(walletAddress); // Returns gas estimate of the transaction. const gasEstimate = await estimate(); diff --git a/docs/docs/guides/depositing-eth.md b/docs/docs/guides/depositing-eth.md index 3227755..11ff880 100644 --- a/docs/docs/guides/depositing-eth.md +++ b/docs/docs/guides/depositing-eth.md @@ -29,7 +29,7 @@ const [walletAddress] = await pufferClient.requestAddresses(); With your address at hand, make the transaction to deposit ETH to mint pufETH. ```ts -const { transact, estimate } = pufferClient.depositETH(walletAddress); +const { transact, estimate } = pufferClient.vault.depositETH(walletAddress); const weiAmount = new BigInt(1e18); @@ -42,7 +42,7 @@ const txHash = await transact(weiAmount); Alternatively, you can set the pufETH recipient to a different address. ```ts -const { transact, estimate } = pufferClient.depositETH(recipientAddress); +const { transact, estimate } = pufferClient.vault.depositETH(recipientAddress); const weiAmount = new BigInt(1e18); diff --git a/lib/api/puffer-client.test.ts b/lib/api/puffer-client.test.ts index 4617de7..3913fa4 100644 --- a/lib/api/puffer-client.test.ts +++ b/lib/api/puffer-client.test.ts @@ -38,7 +38,7 @@ describe('PufferClient', () => { walletClient, publicClient, ); - const { transact, estimate } = pufferClient.depositETH(mockAddress); + const { transact, estimate } = pufferClient.vault.depositETH(mockAddress); expect(await transact(BigInt(1))).toBe(mockAddress); expect(await estimate()).toBe(mockGas); diff --git a/lib/api/puffer-client.ts b/lib/api/puffer-client.ts index f435d05..f5688fc 100644 --- a/lib/api/puffer-client.ts +++ b/lib/api/puffer-client.ts @@ -6,7 +6,7 @@ import { http, } from 'viem'; import { Chain, VIEM_CHAINS } from '../chains/constants'; -import { PufferVaultHandler } from '../contracts/puffer-vault-handler'; +import { PufferVaultHandler } from '../contracts/handlers/puffer-vault-handler'; /** * The core class and the main entry point of the Puffer SDK. diff --git a/lib/contracts/puffer-vault-handler.ts b/lib/contracts/handlers/puffer-vault-handler.ts similarity index 96% rename from lib/contracts/puffer-vault-handler.ts rename to lib/contracts/handlers/puffer-vault-handler.ts index 325a4e7..bbfff94 100644 --- a/lib/contracts/puffer-vault-handler.ts +++ b/lib/contracts/handlers/puffer-vault-handler.ts @@ -5,9 +5,9 @@ import { WalletClient, getContract, } from 'viem'; -import { CHAIN_ABIS } from './abis/abis'; -import { Chain, VIEM_CHAINS } from '../chains/constants'; -import { CHAIN_ADDRESSES } from './addresses'; +import { CHAIN_ABIS } from '../abis/abis'; +import { Chain, VIEM_CHAINS } from '../../chains/constants'; +import { CHAIN_ADDRESSES } from '../addresses'; /** * Handler for the `PufferVaultV2` contract exposing methods to interact From 14c140cf75aeb6e595f86268c6688109e850e7f3 Mon Sep 17 00:00:00 2001 From: Fawad Ali Date: Mon, 27 May 2024 17:31:20 +0200 Subject: [PATCH 6/7] Finally mock the eth_call in tests --- .../handlers/puffer-vault-handler.test.ts | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 lib/contracts/handlers/puffer-vault-handler.test.ts diff --git a/lib/contracts/handlers/puffer-vault-handler.test.ts b/lib/contracts/handlers/puffer-vault-handler.test.ts new file mode 100644 index 0000000..cdb5f8a --- /dev/null +++ b/lib/contracts/handlers/puffer-vault-handler.test.ts @@ -0,0 +1,51 @@ +import { toHex } from 'viem'; +import { mockRpcRequest } from '../../../test/mocks/mock-request'; +import { + setupMockWalletClient, + setupMockPublicClient, +} from '../../../test/mocks/setup-mock-clients'; +import { Chain } from '../../chains/constants'; +import { PufferVaultHandler } from './puffer-vault-handler'; + +describe('PufferVaultHandler', () => { + it('should check pufETH balance', async () => { + const mockAddress = '0x8d37d81e29d11cd7557ceaca25e4e4a4255b1159'; + const mockBalance = BigInt(1); + const mockCallHex = toHex(mockBalance, { size: 32 }); + + const walletClient = setupMockWalletClient(); + const publicRequest = mockRpcRequest({ eth_call: mockCallHex }); + const publicClient = setupMockPublicClient(publicRequest); + + const handler = new PufferVaultHandler( + Chain.Anvil, + walletClient, + publicClient, + ); + const balance = await handler.balanceOf(mockAddress); + + expect(balance).toBe(mockBalance); + }); + + it('should be able to deposit ETH', async () => { + const mockAddress = '0xEB77D02f8122B32273444a1b544C147Fb559CB41'; + const mockGas = BigInt(1); + + const walletRequest = mockRpcRequest({ + eth_sendTransaction: mockAddress, + }); + const walletClient = setupMockWalletClient(walletRequest); + const publicRequest = mockRpcRequest({ eth_estimateGas: mockGas }); + const publicClient = setupMockPublicClient(publicRequest); + + const handler = new PufferVaultHandler( + Chain.Anvil, + walletClient, + publicClient, + ); + const { transact, estimate } = handler.depositETH(mockAddress); + + expect(await transact(BigInt(1))).toBe(mockAddress); + expect(await estimate()).toBe(mockGas); + }); +}); From c0453751fbe97198edb914f6077e617c52ee0990 Mon Sep 17 00:00:00 2001 From: Fawad Ali Date: Mon, 27 May 2024 17:56:40 +0200 Subject: [PATCH 7/7] Write tests for new methods in PufferVaultHandler --- .../handlers/puffer-vault-handler.test.ts | 84 ++++++++++++++++--- .../handlers/puffer-vault-handler.ts | 42 +++++----- 2 files changed, 94 insertions(+), 32 deletions(-) diff --git a/lib/contracts/handlers/puffer-vault-handler.test.ts b/lib/contracts/handlers/puffer-vault-handler.test.ts index cdb5f8a..4fc5248 100644 --- a/lib/contracts/handlers/puffer-vault-handler.test.ts +++ b/lib/contracts/handlers/puffer-vault-handler.test.ts @@ -8,13 +8,15 @@ import { Chain } from '../../chains/constants'; import { PufferVaultHandler } from './puffer-vault-handler'; describe('PufferVaultHandler', () => { - it('should check pufETH balance', async () => { - const mockAddress = '0x8d37d81e29d11cd7557ceaca25e4e4a4255b1159'; - const mockBalance = BigInt(1); - const mockCallHex = toHex(mockBalance, { size: 32 }); + it('should deposit ETH', async () => { + const mockAddress = '0xEB77D02f8122B32273444a1b544C147Fb559CB41'; + const mockGas = BigInt(1); - const walletClient = setupMockWalletClient(); - const publicRequest = mockRpcRequest({ eth_call: mockCallHex }); + const walletRequest = mockRpcRequest({ + eth_sendTransaction: mockAddress, + }); + const walletClient = setupMockWalletClient(walletRequest); + const publicRequest = mockRpcRequest({ eth_estimateGas: mockGas }); const publicClient = setupMockPublicClient(publicRequest); const handler = new PufferVaultHandler( @@ -22,14 +24,16 @@ describe('PufferVaultHandler', () => { walletClient, publicClient, ); - const balance = await handler.balanceOf(mockAddress); + const { transact, estimate } = handler.depositETH(mockAddress); - expect(balance).toBe(mockBalance); + expect(await transact(BigInt(1))).toBe(mockAddress); + expect(await estimate()).toBe(mockGas); }); - it('should be able to deposit ETH', async () => { + it('should deposit stETH', async () => { const mockAddress = '0xEB77D02f8122B32273444a1b544C147Fb559CB41'; const mockGas = BigInt(1); + const mockValue = BigInt(1); const walletRequest = mockRpcRequest({ eth_sendTransaction: mockAddress, @@ -43,9 +47,67 @@ describe('PufferVaultHandler', () => { walletClient, publicClient, ); - const { transact, estimate } = handler.depositETH(mockAddress); + const { transact, estimate } = handler.depositStETH(mockAddress, mockValue); - expect(await transact(BigInt(1))).toBe(mockAddress); + expect(await transact()).toBe(mockAddress); expect(await estimate()).toBe(mockGas); }); + + it('should check pufETH balance', async () => { + const mockAddress = '0xEB77D02f8122B32273444a1b544C147Fb559CB41'; + const mockBalance = BigInt(1); + const mockCallHex = toHex(mockBalance, { size: 32 }); + + const walletClient = setupMockWalletClient(); + const publicRequest = mockRpcRequest({ eth_call: mockCallHex }); + const publicClient = setupMockPublicClient(publicRequest); + + const handler = new PufferVaultHandler( + Chain.Anvil, + walletClient, + publicClient, + ); + const balance = await handler.balanceOf(mockAddress); + + expect(balance).toBe(mockBalance); + }); + + it('should check pufETH rate', async () => { + const mockRate = BigInt(1e18); + const mockCallHex = toHex(mockRate, { size: 32 }); + + const walletClient = setupMockWalletClient(); + const publicRequest = mockRpcRequest({ eth_call: mockCallHex }); + const publicClient = setupMockPublicClient(publicRequest); + + const handler = new PufferVaultHandler( + Chain.Anvil, + walletClient, + publicClient, + ); + const rate = await handler.getPufETHRate(); + + expect(rate).toBe(mockRate); + }); + + it('should get allowance', async () => { + const mockAllowance = BigInt(1); + const mockCallHex = toHex(mockAllowance, { size: 32 }); + + const walletClient = setupMockWalletClient(); + const publicRequest = mockRpcRequest({ eth_call: mockCallHex }); + const publicClient = setupMockPublicClient(publicRequest); + + const handler = new PufferVaultHandler( + Chain.Anvil, + walletClient, + publicClient, + ); + const rate = await handler.getAllowance( + '0xEB77D02f8122B32273444a1b544C147Fb559CB41', + '0x92e01fbccae21eed10ab2f278f47905d482df202', + ); + + expect(rate).toBe(mockAllowance); + }); }); diff --git a/lib/contracts/handlers/puffer-vault-handler.ts b/lib/contracts/handlers/puffer-vault-handler.ts index bbfff94..5ec17b1 100644 --- a/lib/contracts/handlers/puffer-vault-handler.ts +++ b/lib/contracts/handlers/puffer-vault-handler.ts @@ -75,26 +75,6 @@ export class PufferVaultHandler { return { transact, estimate }; } - /** - * Check the pufETH balance of the wallet. - * - * @param walletAddress Wallet address to check the balance of. - * @returns pufETH balance in wei. - */ - public async balanceOf(walletAddress: Address) { - return await this.getContract().read.balanceOf([walletAddress]); - } - - /** - * Get the rate of pufETH compared to ETH. - * - * @returns Rate of 1 pufETH compared to 1 ETH. - */ - public async pufETHRate() { - const oneWei = BigInt(1e18); - return await this.getContract().read.previewDeposit([oneWei]); - } - /** * Deposit stETH to the given wallet address. This doesn't make the * transaction but returns two methods namely `transact` and @@ -108,7 +88,7 @@ export class PufferVaultHandler { * `estimate: () => Promise` - Gas estimate of the * transaction. */ - public async depositStETH(walletAddress: Address, value: bigint) { + public depositStETH(walletAddress: Address, value: bigint) { const transact = async () => await this.getContract().write.depositStETH([value, walletAddress], { account: walletAddress, @@ -126,6 +106,26 @@ export class PufferVaultHandler { return { transact, estimate }; } + /** + * Check the pufETH balance of the wallet. + * + * @param walletAddress Wallet address to check the balance of. + * @returns pufETH balance in wei. + */ + public async balanceOf(walletAddress: Address) { + return await this.getContract().read.balanceOf([walletAddress]); + } + + /** + * Get the rate of pufETH compared to ETH. + * + * @returns Rate of pufETH compared to 1 ETH. + */ + public async getPufETHRate() { + const oneWei = BigInt(1e18); + return await this.getContract().read.previewDeposit([oneWei]); + } + /** * Get the allowance for the given owner and spender. *