diff --git a/lib/api/puffer-client.ts b/lib/api/puffer-client.ts index d32c23c..6020180 100644 --- a/lib/api/puffer-client.ts +++ b/lib/api/puffer-client.ts @@ -9,6 +9,9 @@ import { Chain, VIEM_CHAINS } from '../chains/constants'; import { PufferVaultHandler } from '../contracts/handlers/puffer-vault-handler'; import { PufferDepositorHandler } from '../contracts/handlers/puffer-depositor-handler'; import { PufTokenHandler } from '../contracts/handlers/puf-token-handler'; +import { PufferL2DepositorHandler } from '../contracts/handlers/puffer-l2-depositor-handler'; +import { ERC20PermitHandler } from '../contracts/handlers/erc20-permit-handler'; +import { PufLockerHandler } from '../contracts/handlers/puf-locker-handler'; /** * The core class and the main entry point of the Puffer SDK. @@ -18,12 +21,18 @@ export class PufferClient { private publicClient: PublicClient; // Contract Handlers + /** Handler for the `ERC20Permit` contract. */ + public erc20Permit: ERC20PermitHandler; /** Handler for the `PufferVaultV2` contract. */ public vault: PufferVaultHandler; /** Handler for the `PufferDepositor` contract. */ public depositor: PufferDepositorHandler; + /** Handler for the `PufferL2Depositor` contract. */ + public l2Depositor: PufferL2DepositorHandler; /** Handler for the `PufToken` contract. */ public pufToken: PufTokenHandler; + /** Handler for the `PufLocker` contract. */ + public pufLocker: PufLockerHandler; /** * Create the Puffer Client. @@ -54,23 +63,36 @@ export class PufferClient { transport: http(), }); + this.erc20Permit = new ERC20PermitHandler( + chain, + this.walletClient, + this.publicClient, + ); this.vault = new PufferVaultHandler( chain, this.walletClient, this.publicClient, ); - this.depositor = new PufferDepositorHandler( chain, this.walletClient, this.publicClient, ); - this.pufToken = new PufTokenHandler( chain, this.walletClient, this.publicClient, ); + this.l2Depositor = new PufferL2DepositorHandler( + chain, + this.walletClient, + this.publicClient, + ); + this.pufLocker = new PufLockerHandler( + chain, + this.walletClient, + this.publicClient, + ); } /** diff --git a/lib/contracts/abis/mainnet/PufLocker.ts b/lib/contracts/abis/mainnet/PufLocker.ts new file mode 100644 index 0000000..0738a3d --- /dev/null +++ b/lib/contracts/abis/mainnet/PufLocker.ts @@ -0,0 +1,357 @@ +export const PufLocker = [ + { inputs: [], stateMutability: 'nonpayable', type: 'constructor' }, + { + inputs: [{ internalType: 'address', name: 'authority', type: 'address' }], + name: 'AccessManagedInvalidAuthority', + type: 'error', + }, + { + inputs: [ + { internalType: 'address', name: 'caller', type: 'address' }, + { internalType: 'uint32', name: 'delay', type: 'uint32' }, + ], + name: 'AccessManagedRequiredDelay', + type: 'error', + }, + { + inputs: [{ internalType: 'address', name: 'caller', type: 'address' }], + name: 'AccessManagedUnauthorized', + type: 'error', + }, + { + inputs: [{ internalType: 'address', name: 'target', type: 'address' }], + name: 'AddressEmptyCode', + type: 'error', + }, + { + inputs: [{ internalType: 'address', name: 'account', type: 'address' }], + name: 'AddressInsufficientBalance', + type: 'error', + }, + { inputs: [], name: 'DepositLocked', type: 'error' }, + { + inputs: [ + { internalType: 'address', name: 'implementation', type: 'address' }, + ], + name: 'ERC1967InvalidImplementation', + type: 'error', + }, + { inputs: [], name: 'ERC1967NonPayable', type: 'error' }, + { inputs: [], name: 'FailedInnerCall', type: 'error' }, + { inputs: [], name: 'InvalidAmount', type: 'error' }, + { inputs: [], name: 'InvalidDepositIndex', type: 'error' }, + { inputs: [], name: 'InvalidInitialization', type: 'error' }, + { inputs: [], name: 'InvalidLockPeriod', type: 'error' }, + { inputs: [], name: 'InvalidRecipientAddress', type: 'error' }, + { inputs: [], name: 'NoWithdrawableAmount', type: 'error' }, + { inputs: [], name: 'NotInitializing', type: 'error' }, + { + inputs: [{ internalType: 'address', name: 'token', type: 'address' }], + name: 'SafeERC20FailedOperation', + type: 'error', + }, + { inputs: [], name: 'TokenNotAllowed', type: 'error' }, + { inputs: [], name: 'UUPSUnauthorizedCallContext', type: 'error' }, + { + inputs: [{ internalType: 'bytes32', name: 'slot', type: 'bytes32' }], + name: 'UUPSUnsupportedProxiableUUID', + type: 'error', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'address', + name: 'authority', + type: 'address', + }, + ], + name: 'AuthorityUpdated', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: 'address', name: 'user', type: 'address' }, + { + indexed: true, + internalType: 'address', + name: 'token', + type: 'address', + }, + { + indexed: false, + internalType: 'uint128', + name: 'amount', + type: 'uint128', + }, + { + indexed: false, + internalType: 'uint128', + name: 'releaseTime', + type: 'uint128', + }, + ], + name: 'Deposited', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'uint64', + name: 'version', + type: 'uint64', + }, + ], + name: 'Initialized', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'uint128', + name: 'previousMinLock', + type: 'uint128', + }, + { + indexed: false, + internalType: 'uint128', + name: 'newMinLock', + type: 'uint128', + }, + { + indexed: false, + internalType: 'uint128', + name: 'previousMaxLock', + type: 'uint128', + }, + { + indexed: false, + internalType: 'uint128', + name: 'newMaxLock', + type: 'uint128', + }, + ], + name: 'LockPeriodsChanged', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'token', + type: 'address', + }, + { indexed: false, internalType: 'bool', name: 'allowed', type: 'bool' }, + ], + name: 'SetTokenIsAllowed', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'implementation', + type: 'address', + }, + ], + name: 'Upgraded', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { indexed: true, internalType: 'address', name: 'user', type: 'address' }, + { + indexed: true, + internalType: 'address', + name: 'token', + type: 'address', + }, + { + indexed: false, + internalType: 'uint128', + name: 'amount', + type: 'uint128', + }, + { + indexed: true, + internalType: 'address', + name: 'recipient', + type: 'address', + }, + ], + name: 'Withdrawn', + type: 'event', + }, + { + inputs: [], + name: 'UPGRADE_INTERFACE_VERSION', + outputs: [{ internalType: 'string', name: '', type: 'string' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'authority', + outputs: [{ internalType: 'address', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'token', type: 'address' }, + { internalType: 'uint128', name: 'lockPeriod', type: 'uint128' }, + { + components: [ + { internalType: 'uint256', name: 'deadline', type: 'uint256' }, + { internalType: 'uint256', name: 'amount', type: 'uint256' }, + { internalType: 'uint8', name: 'v', type: 'uint8' }, + { internalType: 'bytes32', name: 'r', type: 'bytes32' }, + { internalType: 'bytes32', name: 's', type: 'bytes32' }, + ], + internalType: 'struct Permit', + name: 'permitData', + type: 'tuple', + }, + ], + name: 'deposit', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'token', type: 'address' }, + { internalType: 'address', name: 'depositor', type: 'address' }, + ], + name: 'getAllDeposits', + outputs: [ + { + components: [ + { internalType: 'uint128', name: 'amount', type: 'uint128' }, + { internalType: 'uint128', name: 'releaseTime', type: 'uint128' }, + ], + internalType: 'struct IPufLocker.Deposit[]', + name: '', + type: 'tuple[]', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'user', type: 'address' }, + { internalType: 'address', name: 'token', type: 'address' }, + { internalType: 'uint256', name: 'start', type: 'uint256' }, + { internalType: 'uint256', name: 'limit', type: 'uint256' }, + ], + name: 'getDeposits', + outputs: [ + { + components: [ + { internalType: 'uint128', name: 'amount', type: 'uint128' }, + { internalType: 'uint128', name: 'releaseTime', type: 'uint128' }, + ], + internalType: 'struct IPufLocker.Deposit[]', + name: '', + type: 'tuple[]', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'getLockPeriods', + outputs: [ + { internalType: 'uint128', name: '', type: 'uint128' }, + { internalType: 'uint128', name: '', type: 'uint128' }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'accessManager', type: 'address' }, + ], + name: 'initialize', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'isConsumingScheduledOp', + outputs: [{ internalType: 'bytes4', name: '', type: 'bytes4' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'proxiableUUID', + outputs: [{ internalType: 'bytes32', name: '', type: 'bytes32' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'newAuthority', type: 'address' }, + ], + name: 'setAuthority', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'token', type: 'address' }, + { internalType: 'bool', name: 'allowed', type: 'bool' }, + ], + name: 'setIsAllowedToken', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'uint128', name: 'minLock', type: 'uint128' }, + { internalType: 'uint128', name: 'maxLock', type: 'uint128' }, + ], + name: 'setLockPeriods', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'newImplementation', type: 'address' }, + { internalType: 'bytes', name: 'data', type: 'bytes' }, + ], + name: 'upgradeToAndCall', + outputs: [], + stateMutability: 'payable', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'token', type: 'address' }, + { internalType: 'uint256[]', name: 'depositIndexes', type: 'uint256[]' }, + { internalType: 'address', name: 'recipient', type: 'address' }, + ], + name: 'withdraw', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, +]; diff --git a/lib/contracts/abis/mainnet/PufferL2Depositor.ts b/lib/contracts/abis/mainnet/PufferL2Depositor.ts new file mode 100644 index 0000000..8a6c4cd --- /dev/null +++ b/lib/contracts/abis/mainnet/PufferL2Depositor.ts @@ -0,0 +1,284 @@ +export const PufferL2Depositor = [ + { + inputs: [ + { internalType: 'address', name: 'accessManager', type: 'address' }, + { internalType: 'address', name: 'weth', type: 'address' }, + ], + stateMutability: 'nonpayable', + type: 'constructor', + }, + { + inputs: [{ internalType: 'address', name: 'authority', type: 'address' }], + name: 'AccessManagedInvalidAuthority', + type: 'error', + }, + { + inputs: [ + { internalType: 'address', name: 'caller', type: 'address' }, + { internalType: 'uint32', name: 'delay', type: 'uint32' }, + ], + name: 'AccessManagedRequiredDelay', + type: 'error', + }, + { + inputs: [{ internalType: 'address', name: 'caller', type: 'address' }], + name: 'AccessManagedUnauthorized', + type: 'error', + }, + { + inputs: [{ internalType: 'address', name: 'target', type: 'address' }], + name: 'AddressEmptyCode', + type: 'error', + }, + { + inputs: [{ internalType: 'address', name: 'account', type: 'address' }], + name: 'AddressInsufficientBalance', + type: 'error', + }, + { inputs: [], name: 'FailedInnerCall', type: 'error' }, + { inputs: [], name: 'InvalidAccount', type: 'error' }, + { inputs: [], name: 'InvalidToken', type: 'error' }, + { + inputs: [{ internalType: 'address', name: 'token', type: 'address' }], + name: 'SafeERC20FailedOperation', + type: 'error', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'address', + name: 'authority', + type: 'address', + }, + ], + name: 'AuthorityUpdated', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'token', + type: 'address', + }, + { + indexed: false, + internalType: 'uint256', + name: 'oldDepositCap', + type: 'uint256', + }, + { + indexed: false, + internalType: 'uint256', + name: 'newDepositCap', + type: 'uint256', + }, + ], + name: 'DepositCapUpdated', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'token', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'depositor', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'account', + type: 'address', + }, + { + indexed: false, + internalType: 'uint256', + name: 'tokenAmount', + type: 'uint256', + }, + { + indexed: false, + internalType: 'uint256', + name: 'referralCode', + type: 'uint256', + }, + ], + name: 'DepositedToken', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'migrator', + type: 'address', + }, + { indexed: false, internalType: 'bool', name: 'isAllowed', type: 'bool' }, + ], + name: 'SetIsMigratorAllowed', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'token', + type: 'address', + }, + { + indexed: false, + internalType: 'address', + name: 'pufToken', + type: 'address', + }, + ], + name: 'TokenAdded', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'token', + type: 'address', + }, + { + indexed: false, + internalType: 'address', + name: 'pufToken', + type: 'address', + }, + ], + name: 'TokenRemoved', + type: 'event', + }, + { + inputs: [], + name: 'WETH', + outputs: [{ internalType: 'address', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [{ internalType: 'address', name: 'token', type: 'address' }], + name: 'addNewToken', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'authority', + outputs: [{ internalType: 'address', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'token', type: 'address' }, + { internalType: 'address', name: 'account', type: 'address' }, + { + components: [ + { internalType: 'uint256', name: 'deadline', type: 'uint256' }, + { internalType: 'uint256', name: 'amount', type: 'uint256' }, + { internalType: 'uint8', name: 'v', type: 'uint8' }, + { internalType: 'bytes32', name: 'r', type: 'bytes32' }, + { internalType: 'bytes32', name: 's', type: 'bytes32' }, + ], + internalType: 'struct Permit', + name: 'permitData', + type: 'tuple', + }, + { internalType: 'uint256', name: 'referralCode', type: 'uint256' }, + ], + name: 'deposit', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'account', type: 'address' }, + { internalType: 'uint256', name: 'referralCode', type: 'uint256' }, + ], + name: 'depositETH', + outputs: [], + stateMutability: 'payable', + type: 'function', + }, + { + inputs: [{ internalType: 'address', name: 'migrator', type: 'address' }], + name: 'isAllowedMigrator', + outputs: [{ internalType: 'bool', name: 'isAllowed', type: 'bool' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'isConsumingScheduledOp', + outputs: [{ internalType: 'bytes4', name: '', type: 'bytes4' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'revertIfPaused', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'newAuthority', type: 'address' }, + ], + name: 'setAuthority', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'token', type: 'address' }, + { internalType: 'uint256', name: 'newDepositCap', type: 'uint256' }, + ], + name: 'setDepositCap', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: 'migrator', type: 'address' }, + { internalType: 'bool', name: 'allowed', type: 'bool' }, + ], + name: 'setMigrator', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [{ internalType: 'address', name: 'token', type: 'address' }], + name: 'tokens', + outputs: [{ internalType: 'address', name: 'pufToken', type: 'address' }], + stateMutability: 'view', + type: 'function', + }, +]; diff --git a/lib/contracts/abis/puf-locker-abis.ts b/lib/contracts/abis/puf-locker-abis.ts new file mode 100644 index 0000000..4963b8b --- /dev/null +++ b/lib/contracts/abis/puf-locker-abis.ts @@ -0,0 +1,7 @@ +import { Chain } from '../../chains/constants'; +import { PufLocker } from './mainnet/PufLocker'; + +export const PUF_LOCKER_ABIS = { + [Chain.Mainnet]: { PufLocker }, + [Chain.Holesky]: { PufLocker }, +}; diff --git a/lib/contracts/abis/puffer-depositor-abis.ts b/lib/contracts/abis/puffer-depositor-abis.ts index be5d770..786c2c9 100644 --- a/lib/contracts/abis/puffer-depositor-abis.ts +++ b/lib/contracts/abis/puffer-depositor-abis.ts @@ -1,6 +1,7 @@ import { Chain } from '../../chains/constants'; import { PufferDepositor as MainnetPufferDepositor } from './mainnet/PufferDepositor'; import { PufferDepositor as HoleskyPufferDepositor } from './holesky/PufferDepositor'; +import { PufferL2Depositor } from './mainnet/PufferL2Depositor'; export const PUFFER_DEPOSITOR_ABIS = { [Chain.Mainnet]: { @@ -10,3 +11,8 @@ export const PUFFER_DEPOSITOR_ABIS = { PufferDepositor: HoleskyPufferDepositor, }, }; + +export const PUFFER_L2_DEPOSITOR_ABIS = { + [Chain.Mainnet]: { PufferL2Depositor }, + [Chain.Holesky]: { PufferL2Depositor }, +}; diff --git a/lib/contracts/addresses.ts b/lib/contracts/addresses.ts index 5e6b2f7..dbdc4f0 100644 --- a/lib/contracts/addresses.ts +++ b/lib/contracts/addresses.ts @@ -6,9 +6,17 @@ export const CHAIN_ADDRESSES = { [Chain.Mainnet]: { PufferVault: '0xD9A442856C234a39a81a089C06451EBAa4306a72', PufferDepositor: '0x4aa799c5dfc01ee7d790e3bf1a7c2257ce1dceff', + // TODO: Update the addresses once the contracts are deployed on chain. + // See https://dev.azure.com/pufferfi/Frontend/_workitems/edit/1797. + PufferL2Depositor: '0x0000000000000000000000000000000000000000', + PufLocker: '0x0000000000000000000000000000000000000000', }, [Chain.Holesky]: { PufferVault: '0x9196830bB4c05504E0A8475A0aD566AceEB6BeC9', PufferDepositor: '0x824AC05aeb86A0aD770b8acDe0906d2d4a6c4A8c', + // TODO: Update the addresses once the contracts are deployed on chain. + // See https://dev.azure.com/pufferfi/Frontend/_workitems/edit/1797. + PufferL2Depositor: '0x03D8bE7CaAD9A95cef9800249eC663Aa28A2F776', + PufLocker: '0xf7C6B760A6B8274dB69D7c689eb84F80223D2abD', }, }; diff --git a/lib/contracts/handlers/tokens-handler.test.ts b/lib/contracts/handlers/erc20-permit-handler.test.ts similarity index 63% rename from lib/contracts/handlers/tokens-handler.test.ts rename to lib/contracts/handlers/erc20-permit-handler.test.ts index 82e1a0a..3d87e89 100644 --- a/lib/contracts/handlers/tokens-handler.test.ts +++ b/lib/contracts/handlers/erc20-permit-handler.test.ts @@ -6,17 +6,30 @@ import { import { mockAccount, testingUtils } from '../../../test/setup-tests'; import { Token } from '../tokens'; import { ERC20PERMIT_ABI } from '../abis/tokens-abis'; -import { TokensHandler } from './tokens-handler'; -import { isHex, serializeSignature } from 'viem'; +import { ERC20PermitHandler } from './erc20-permit-handler'; +import { + PublicClient, + WalletClient, + isHash, + isHex, + serializeSignature, +} from 'viem'; -describe('TokensHandler', () => { +describe('ERC20PermitHandler', () => { const contractTestingUtils = testingUtils.generateContractUtils(ERC20PERMIT_ABI); + let handler: ERC20PermitHandler; + let walletClient: WalletClient; + let publicClient: PublicClient; - it('should get the permit signature for the given token', async () => { - const walletClient = setupTestWalletClient(); - const publicClient = setupTestPublicClient(); + beforeEach(() => { + walletClient = setupTestWalletClient(); + publicClient = setupTestPublicClient(); + + handler = new ERC20PermitHandler(Chain.Holesky, walletClient, publicClient); + }); + it('should get the permit signature for the given token', async () => { contractTestingUtils.mockCall('nonces', [10n]); contractTestingUtils.mockCall('name', ['Ethereum Staking Mock']); @@ -32,11 +45,6 @@ describe('TokensHandler', () => { .spyOn(walletClient, 'signTypedData') .mockReturnValue(Promise.resolve(mockSignature)); - const handler = new TokensHandler( - Chain.Holesky, - walletClient, - publicClient, - ); const signature = await handler .withToken(Token.stETH) .getPermitSignature(mockAccount, 1n); @@ -48,4 +56,11 @@ describe('TokensHandler', () => { expect(yParity).toBeTruthy(); expect(typeof deadline).toBe('bigint'); }); + + it('should approve the usage of token for a spender', async () => { + contractTestingUtils.mockTransaction('approve'); + + const txHash = await handler.approve(mockAccount, 1n); + expect(isHash(txHash)).toBeTruthy(); + }); }); diff --git a/lib/contracts/handlers/tokens-handler.ts b/lib/contracts/handlers/erc20-permit-handler.ts similarity index 77% rename from lib/contracts/handlers/tokens-handler.ts rename to lib/contracts/handlers/erc20-permit-handler.ts index abbd393..e84d9e5 100644 --- a/lib/contracts/handlers/tokens-handler.ts +++ b/lib/contracts/handlers/erc20-permit-handler.ts @@ -5,16 +5,17 @@ import { Address, parseSignature, } from 'viem'; -import { Chain } from '../../chains/constants'; +import { Chain, VIEM_CHAINS, ViemChain } from '../../chains/constants'; import { ERC20PERMIT_ABI } from '../abis/tokens-abis'; import { TOKENS_ADDRESSES, Token } from '../tokens'; import { CHAIN_ADDRESSES } from '../addresses'; import { getTimestampInSeconds } from '../../utils/time'; /** - * Handler for performing operations for and with tokens. + * Handler for performing operations for and with ERC20Permit tokens. */ -export class TokensHandler { +export class ERC20PermitHandler { + private viemChain: ViemChain; private token: Token; /** @@ -31,6 +32,7 @@ export class TokensHandler { private walletClient: WalletClient, private publicClient: PublicClient, ) { + this.viemChain = VIEM_CHAINS[chain]; this.token = Token.ETH; } @@ -45,6 +47,12 @@ export class TokensHandler { return this; } + /** + * Get the contract. This is a method because the typings are complex + * and lost when trying to make it a member. + * + * @returns The viem contract. + */ public getContract() { return getContract({ address: TOKENS_ADDRESSES[this.token][this.chain], @@ -112,4 +120,18 @@ export class TokensHandler { return '1'; } + + /** + * Approve transaction for the spender to spend the owner's tokens. + * + * @param spenderAddress Address of the spender. + * @param value Value to approve for the spender. + * @returns Hash of the transaction. + */ + public approve(spenderAddress: Address, value: bigint) { + return this.getContract().write.approve([spenderAddress, value], { + account: spenderAddress, + chain: this.viemChain, + }); + } } diff --git a/lib/contracts/handlers/puf-locker-handler.test.ts b/lib/contracts/handlers/puf-locker-handler.test.ts new file mode 100644 index 0000000..62be451 --- /dev/null +++ b/lib/contracts/handlers/puf-locker-handler.test.ts @@ -0,0 +1,92 @@ +import { isHash } from 'viem'; +import { mockPermitSignature } from '../../../test/mocks/permit-signature'; +import { + setupTestWalletClient, + setupTestPublicClient, +} from '../../../test/setup-test-clients'; +import { mockAccount, testingUtils } from '../../../test/setup-tests'; +import { Chain } from '../../chains/constants'; +import { PUF_LOCKER_ABIS } from '../abis/puf-locker-abis'; +import { Token } from '../tokens'; +import { PufLockerHandler } from './puf-locker-handler'; + +describe('PufTokenHandler', () => { + const contractTestingUtils = testingUtils.generateContractUtils( + PUF_LOCKER_ABIS[Chain.Holesky].PufLocker, + ); + let handler: PufLockerHandler; + + beforeEach(() => { + const walletClient = setupTestWalletClient(); + const publicClient = setupTestPublicClient(); + + handler = new PufLockerHandler(Chain.Holesky, walletClient, publicClient); + }); + + it('should get all locked deposits', async () => { + const mockDeposit = { amount: 1n, releaseTime: 1n }; + contractTestingUtils.mockCall('getAllDeposits', [[mockDeposit]]); + + const allDeposits = await handler.getAllDeposits( + Token.pufWETH, + mockAccount, + ); + + expect(allDeposits).toEqual([mockDeposit]); + }); + + it('should get deposits based on index', async () => { + const mockDeposit = { amount: 1n, releaseTime: 1n }; + contractTestingUtils.mockCall('getDeposits', [[mockDeposit]]); + + const allDeposits = await handler.getDeposits( + Token.pufWETH, + mockAccount, + 0n, + 1n, + ); + + expect(allDeposits).toEqual([mockDeposit]); + }); + + it('should get minimum and maximum lock periods for deposits', async () => { + const lockPeriods = [0n, 10n]; + contractTestingUtils.mockCall('getLockPeriods', lockPeriods); + + const [minLock, maxLock] = await handler.getLockPeriods(); + + expect(minLock).toBe(lockPeriods[0]); + expect(maxLock).toBe(lockPeriods[1]); + }); + + it('should deposit the given token into the locker', async () => { + contractTestingUtils.mockTransaction('deposit'); + jest + .spyOn((handler as any).erc20PermitHandler, 'getPermitSignature') + .mockReturnValue(Promise.resolve(mockPermitSignature)); + + const { transact, estimate } = await handler.deposit( + Token.pufWETH, + mockAccount, + 1n, + 10n, + ); + + expect(typeof (await estimate())).toBe('bigint'); + expect(isHash(await transact())).toBe(true); + }); + + it('should withdraw the given token from the locker', async () => { + contractTestingUtils.mockTransaction('withdraw'); + + const { transact, estimate } = handler.withdraw( + Token.pufWETH, + mockAccount, + mockAccount, + [0n], + ); + + expect(typeof (await estimate())).toBe('bigint'); + expect(isHash(await transact())).toBe(true); + }); +}); diff --git a/lib/contracts/handlers/puf-locker-handler.ts b/lib/contracts/handlers/puf-locker-handler.ts new file mode 100644 index 0000000..2d69501 --- /dev/null +++ b/lib/contracts/handlers/puf-locker-handler.ts @@ -0,0 +1,182 @@ +import { WalletClient, PublicClient, getContract, Address } from 'viem'; +import { Chain, VIEM_CHAINS, ViemChain } from '../../chains/constants'; +import { PUF_LOCKER_ABIS } from '../abis/puf-locker-abis'; +import { CHAIN_ADDRESSES } from '../addresses'; +import { PufToken, TOKENS_ADDRESSES } from '../tokens'; +import { ERC20PermitHandler } from './erc20-permit-handler'; + +/** + * Handler for the `PufLocker` contract exposing methods to interact + * with the contract. + */ +export class PufLockerHandler { + private viemChain: ViemChain; + private erc20PermitHandler: ERC20PermitHandler; + + /** + * Create the handler for the `PufLocker` 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.erc20PermitHandler = new ERC20PermitHandler( + chain, + walletClient, + publicClient, + ); + } + + /** + * Get the contract. This is a method because the typings are complex + * and lost when trying to make it a member. + * + * @returns The viem contract. + */ + public getContract() { + return getContract({ + address: CHAIN_ADDRESSES[this.chain].PufLocker as Address, + abi: PUF_LOCKER_ABIS[this.chain].PufLocker, + client: { + wallet: this.walletClient, + public: this.publicClient, + }, + }); + } + + /** + * Get all deposits of the given account address. + * + * @param pufToken The PufToken to get the deposits for. + * @param walletAddress The wallet address to get the deposits for. + * @returns The amount and deposits of the given account address. + */ + public getAllDeposits(pufToken: PufToken, walletAddress: Address) { + return this.getContract().read.getAllDeposits([ + TOKENS_ADDRESSES[pufToken][this.chain], + walletAddress, + ]); + } + + /** + * Get the user's deposits for the given token and deposit index. + * + * @param pufToken PufToken to get the deposits of. + * @param userAddress User address to get the deposits for. + * @param start The starting index of the deposits. + * @param limit The maximum number of deposits to retrieve. + * @returns The amount and release time of the deposits. + */ + public getDeposits( + pufToken: PufToken, + userAddress: Address, + start: bigint, + limit: bigint, + ) { + return this.getContract().read.getDeposits([ + userAddress, + TOKENS_ADDRESSES[pufToken][this.chain], + start, + limit, + ]); + } + + /** + * Get the minimum and maximum lock periods allowed for deposits. + * + * @returns The minimum and maximum lock period. (`[minLock, + * maxLock]`) + */ + public getLockPeriods() { + return this.getContract().read.getLockPeriods(); + } + + /** + * Deposit the given PufToken into the locker. + * + * @param pufToken PufToken to deposit. + * @param walletAddress Wallet address of the depositor. + * @param value Amount of the deposit. + * @param lockPeriod The period for the deposit. + * @returns The transaction hash of the deposit. + */ + public async deposit( + pufToken: PufToken, + walletAddress: Address, + value: bigint, + lockPeriod: bigint, + ) { + const { r, s, v, yParity, deadline } = await this.erc20PermitHandler + .withToken(pufToken) + .getPermitSignature(walletAddress, value); + const permitData = { + r, + s, + v: Number(v ?? yParity), + deadline, + amount: value, + }; + + const depositArgs = [ + TOKENS_ADDRESSES[pufToken][this.chain], + lockPeriod, + permitData, + ]; + + const transact = () => + this.getContract().write.deposit(depositArgs, { + account: walletAddress, + chain: this.viemChain, + }); + const estimate = () => + this.getContract().estimateGas.deposit(depositArgs, { + account: walletAddress, + }); + + return { transact, estimate }; + } + + /** + * Withdraw the deposits identified by the deposit indexes from the + * locker. + * + * @param pufToken PufToken to withdraw. + * @param walletAddress Address of the account making the transaction. + * @param recipient Recipient of the withdrawal. + * @param depositIndexes Deposit indexes to withdraw. + * @returns Hash of the withdrawal transaction. + */ + public withdraw( + pufToken: PufToken, + walletAddress: Address, + recipient: Address, + depositIndexes: bigint[], + ) { + const withdrawArgs = [ + TOKENS_ADDRESSES[pufToken][this.chain], + depositIndexes, + recipient, + ]; + + const transact = () => + this.getContract().write.withdraw(withdrawArgs, { + account: walletAddress, + chain: this.viemChain, + }); + const estimate = () => + this.getContract().estimateGas.withdraw(withdrawArgs, { + account: walletAddress, + }); + + return { transact, estimate }; + } +} diff --git a/lib/contracts/handlers/puf-token-handler.test.ts b/lib/contracts/handlers/puf-token-handler.test.ts index 8e6b854..801b1b1 100644 --- a/lib/contracts/handlers/puf-token-handler.test.ts +++ b/lib/contracts/handlers/puf-token-handler.test.ts @@ -7,7 +7,7 @@ import { mockAccount, testingUtils } from '../../../test/setup-tests'; import { Chain } from '../../chains/constants'; import { PUF_TOKEN_ABIS } from '../abis/puf-token-abis'; import { PufTokenHandler } from './puf-token-handler'; -import { PUF_TOKEN_ADDRESSES, PufToken } from '../puf-tokens'; +import { TOKENS_ADDRESSES, Token } from '../tokens'; describe('PufTokenHandler', () => { const contractTestingUtils = testingUtils.generateContractUtils( @@ -101,10 +101,10 @@ describe('PufTokenHandler', () => { }); it('should use the contract of the selected token', () => { - const contract = handler.withPufToken(PufToken.pufALT).getContract(); + const contract = handler.withPufToken(Token.pufALT).getContract(); expect(contract.address).toBe( - PUF_TOKEN_ADDRESSES[PufToken.pufALT][Chain.Holesky], + TOKENS_ADDRESSES[Token.pufALT][Chain.Holesky], ); }); }); diff --git a/lib/contracts/handlers/puf-token-handler.ts b/lib/contracts/handlers/puf-token-handler.ts index 87b1fb2..eb9a268 100644 --- a/lib/contracts/handlers/puf-token-handler.ts +++ b/lib/contracts/handlers/puf-token-handler.ts @@ -1,7 +1,7 @@ import { WalletClient, PublicClient, getContract, Address } from 'viem'; import { Chain, VIEM_CHAINS, ViemChain } from '../../chains/constants'; import { PUF_TOKEN_ABIS } from '../abis/puf-token-abis'; -import { PUF_TOKEN_ADDRESSES, PufToken } from '../puf-tokens'; +import { PufToken, Token, TOKENS_ADDRESSES } from '../tokens'; /** * Handler for the `PufToken` contract exposing methods to interact with @@ -27,7 +27,7 @@ export class PufTokenHandler { private walletClient: WalletClient, private publicClient: PublicClient, ) { - this.pufToken = PufToken.pufWETH; + this.pufToken = Token.pufWETH; this.viemChain = VIEM_CHAINS[chain]; } @@ -42,11 +42,15 @@ export class PufTokenHandler { return this; } - // This is a method because the typings are complex and lost when - // trying to make it a member. + /** + * Get the contract. This is a method because the typings are complex + * and lost when trying to make it a member. + * + * @returns The viem contract. + */ public getContract() { return getContract({ - address: PUF_TOKEN_ADDRESSES[this.pufToken][this.chain], + address: TOKENS_ADDRESSES[this.pufToken][this.chain], abi: PUF_TOKEN_ABIS[this.chain].PufToken, client: { wallet: this.walletClient, @@ -141,7 +145,6 @@ export class PufTokenHandler { walletAddress: Address, value: bigint, ) { - console.log(this.getContract().address); const transact = () => this.getContract().write.deposit( [depositorAddress, walletAddress, value], diff --git a/lib/contracts/handlers/puffer-depositor-handler.test.ts b/lib/contracts/handlers/puffer-depositor-handler.test.ts index f4f124c..f701730 100644 --- a/lib/contracts/handlers/puffer-depositor-handler.test.ts +++ b/lib/contracts/handlers/puffer-depositor-handler.test.ts @@ -1,8 +1,9 @@ -import { Address, toHex } from 'viem'; +import { toHex } from 'viem'; import { setupTestPublicClient, setupTestWalletClient, } from '../../../test/setup-test-clients'; +import { mockPermitSignature } from '../../../test/mocks/permit-signature'; import { mockAccount, testingUtils } from '../../../test/setup-tests'; import { Chain } from '../../chains/constants'; import { PufferDepositorHandler } from './puffer-depositor-handler'; @@ -24,15 +25,8 @@ describe('PufferDepositorHandler', () => { publicClient, ); - const mockPermitSignature = { - r: `0x${new Array(64).fill(0).join('')}` as Address, - s: `0x${new Array(64).fill(0).join('')}` as Address, - v: 0n, - yParity: 1, - deadline: 0n, - }; jest - .spyOn(handler.tokensHandler, 'getPermitSignature') + .spyOn((handler as any).erc20PermitHandler, 'getPermitSignature') .mockReturnValue(Promise.resolve(mockPermitSignature)); const { transact, estimate } = await handler.depositStETH(mockAccount, 1n); @@ -57,15 +51,8 @@ describe('PufferDepositorHandler', () => { publicClient, ); - const mockPermitSignature = { - r: `0x${new Array(64).fill(0).join('')}` as Address, - s: `0x${new Array(64).fill(0).join('')}` as Address, - v: 0n, - yParity: 1, - deadline: 0n, - }; jest - .spyOn(handler.tokensHandler, 'getPermitSignature') + .spyOn((handler as any).erc20PermitHandler, 'getPermitSignature') .mockReturnValue(Promise.resolve(mockPermitSignature)); const { transact, estimate } = await handler.depositWstETH(mockAccount, 1n); diff --git a/lib/contracts/handlers/puffer-depositor-handler.ts b/lib/contracts/handlers/puffer-depositor-handler.ts index 5849a6a..0449ef7 100644 --- a/lib/contracts/handlers/puffer-depositor-handler.ts +++ b/lib/contracts/handlers/puffer-depositor-handler.ts @@ -2,7 +2,7 @@ import { Address, PublicClient, WalletClient, getContract } from 'viem'; import { Chain, VIEM_CHAINS, ViemChain } from '../../chains/constants'; import { PUFFER_DEPOSITOR_ABIS } from '../abis/puffer-depositor-abis'; import { CHAIN_ADDRESSES } from '../addresses'; -import { TokensHandler } from './tokens-handler'; +import { ERC20PermitHandler } from './erc20-permit-handler'; import { Token } from '../tokens'; /** @@ -11,7 +11,7 @@ import { Token } from '../tokens'; */ export class PufferDepositorHandler { private viemChain: ViemChain; - public tokensHandler: TokensHandler; + private erc20PermitHandler: ERC20PermitHandler; /** * Create the handler for the `PufferDepositor` contract exposing @@ -29,9 +29,19 @@ export class PufferDepositorHandler { private publicClient: PublicClient, ) { this.viemChain = VIEM_CHAINS[chain]; - this.tokensHandler = new TokensHandler(chain, walletClient, publicClient); + this.erc20PermitHandler = new ERC20PermitHandler( + chain, + walletClient, + publicClient, + ); } + /** + * Get the contract. This is a method because the typings are complex + * and lost when trying to make it a member. + * + * @returns The viem contract. + */ public getContract() { return getContract({ address: CHAIN_ADDRESSES[this.chain].PufferDepositor as Address, @@ -57,7 +67,7 @@ export class PufferDepositorHandler { * transaction. */ public async depositStETH(walletAddress: Address, value: bigint) { - const { r, s, v, yParity, deadline } = await this.tokensHandler + const { r, s, v, yParity, deadline } = await this.erc20PermitHandler .withToken(Token.stETH) .getPermitSignature(walletAddress, value); const permitData = { @@ -96,7 +106,7 @@ export class PufferDepositorHandler { * transaction. */ public async depositWstETH(walletAddress: Address, value: bigint) { - const { r, s, v, yParity, deadline } = await this.tokensHandler + const { r, s, v, yParity, deadline } = await this.erc20PermitHandler .withToken(Token.wstETH) .getPermitSignature(walletAddress, value); const permitData = { diff --git a/lib/contracts/handlers/puffer-l2-depositor-handler.test.ts b/lib/contracts/handlers/puffer-l2-depositor-handler.test.ts new file mode 100644 index 0000000..5b11f9f --- /dev/null +++ b/lib/contracts/handlers/puffer-l2-depositor-handler.test.ts @@ -0,0 +1,61 @@ +import { isHash } from 'viem'; +import { + setupTestWalletClient, + setupTestPublicClient, +} from '../../../test/setup-test-clients'; +import { mockAccount, testingUtils } from '../../../test/setup-tests'; +import { mockPermitSignature } from '../../../test/mocks/permit-signature'; +import { Chain } from '../../chains/constants'; +import { PUFFER_L2_DEPOSITOR_ABIS } from '../abis/puffer-depositor-abis'; +import { PufferL2DepositorHandler } from './puffer-l2-depositor-handler'; +import { Token } from '../tokens'; + +describe('PufferL2DepositorHandler', () => { + const contractTestingUtils = testingUtils.generateContractUtils( + PUFFER_L2_DEPOSITOR_ABIS[Chain.Holesky].PufferL2Depositor, + ); + let handler: PufferL2DepositorHandler; + + beforeEach(() => { + const walletClient = setupTestWalletClient(); + const publicClient = setupTestPublicClient(); + + handler = new PufferL2DepositorHandler( + Chain.Holesky, + walletClient, + publicClient, + ); + }); + + // TODO: This test is not working because the suggested parameters + // from the PufferL2Depositor contract are not exactly correct. See + // https://github.com/PufferFinance/puffer-contracts/blob/d3e318f3c45d744bfa2dbadfa1abe998fa49d4b5/mainnet-contracts/src/interface/IPufferL2Depositor.sol#L51-L57 + // it('should deposit pre-approved token', async () => { + // contractTestingUtils.mockTransaction('deposit'); + + // const { transact, estimate } = handler.depositAfterApproval( + // Token.stETH, + // mockAccount, + // 10n, + // ); + + // expect(typeof (await estimate())).toBe('bigint'); + // expect(isHash(await transact())).toBe(true); + // }); + + it('should deposit token after requesting permit', async () => { + contractTestingUtils.mockTransaction('deposit'); + jest + .spyOn((handler as any).erc20PermitHandler, 'getPermitSignature') + .mockReturnValue(Promise.resolve(mockPermitSignature)); + + const { transact, estimate } = await handler.deposit( + Token.stETH, + mockAccount, + 10n, + ); + + expect(typeof (await estimate())).toBe('bigint'); + expect(isHash(await transact())).toBe(true); + }); +}); diff --git a/lib/contracts/handlers/puffer-l2-depositor-handler.ts b/lib/contracts/handlers/puffer-l2-depositor-handler.ts new file mode 100644 index 0000000..318ce12 --- /dev/null +++ b/lib/contracts/handlers/puffer-l2-depositor-handler.ts @@ -0,0 +1,146 @@ +import { WalletClient, PublicClient, getContract, Address } from 'viem'; +import { Chain, VIEM_CHAINS, ViemChain } from '../../chains/constants'; +import { PUFFER_L2_DEPOSITOR_ABIS } from '../abis/puffer-depositor-abis'; +import { CHAIN_ADDRESSES } from '../addresses'; +import { NonPufToken, TOKENS_ADDRESSES } from '../tokens'; +import { ERC20PermitHandler } from './erc20-permit-handler'; + +/** + * Handler for the `PufferL2Depositor` contract exposing methods to + * interact with the contract. + */ +export class PufferL2DepositorHandler { + private viemChain: ViemChain; + private erc20PermitHandler: ERC20PermitHandler; + + /** + * Create the handler for the `PufferL2Depositor` 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.erc20PermitHandler = new ERC20PermitHandler( + chain, + walletClient, + publicClient, + ); + } + + /** + * Get the contract. This is a method because the typings are complex + * and lost when trying to make it a member. + * + * @returns The viem contract. + */ + public getContract() { + return getContract({ + address: CHAIN_ADDRESSES[this.chain].PufferL2Depositor as Address, + abi: PUFFER_L2_DEPOSITOR_ABIS[this.chain].PufferL2Depositor, + client: { + wallet: this.walletClient, + public: this.publicClient, + }, + }); + } + + /** + * Deposit the given token which is pre-approved using + * `token.approve()` in exchange for pufETH. This doesn't make the + * transaction but returns two methods namely `transact` and + * `estimate`. + * + * @param token Token to deposit. + * @param walletAddress Wallet address to take the token from. + * @param value Value in wei of the token to deposit. + * @returns `transact: () => Promise
` - Used to make the + * transaction. + * + * `estimate: () => Promise` - Gas estimate of the + * transaction. + */ + public depositAfterApproval( + token: NonPufToken, + walletAddress: Address, + value: bigint, + ) { + const depositArgs = [ + TOKENS_ADDRESSES[token][this.chain], + walletAddress, + // Only `amount` is needed if `token.approve()` is already called. + { amount: 0n } as any, + value, + ]; + + const transact = () => + this.getContract().write.deposit(depositArgs, { + account: walletAddress, + chain: this.viemChain, + }); + const estimate = () => + this.getContract().estimateGas.deposit(depositArgs, { + account: walletAddress, + }); + + return { transact, estimate }; + } + + /** + * Deposit the given token in exchange for pufETH. This doesn't make + * the transaction but returns two methods namely `transact` and + * `estimate`. + * + * @param token Token to deposit. + * @param walletAddress Wallet address to take the token from. + * @param value Value in wei of the token to deposit. + * @returns `transact: () => Promise
` - Used to make the + * transaction. + * + * `estimate: () => Promise` - Gas estimate of the + * transaction. + */ + public async deposit( + token: NonPufToken, + walletAddress: Address, + value: bigint, + ) { + const { r, s, v, yParity, deadline } = await this.erc20PermitHandler + .withToken(token) + .getPermitSignature(walletAddress, value); + const permitData = { + r, + s, + v: Number(v ?? yParity), + deadline, + amount: value, + }; + + const depositArgs = [ + TOKENS_ADDRESSES[token][this.chain], + walletAddress, + permitData, + value, + ]; + + const transact = () => + this.getContract().write.deposit(depositArgs, { + account: walletAddress, + chain: this.viemChain, + }); + const estimate = () => + this.getContract().estimateGas.deposit(depositArgs, { + account: walletAddress, + }); + + return { transact, estimate }; + } +} diff --git a/lib/contracts/handlers/puffer-vault-handler.ts b/lib/contracts/handlers/puffer-vault-handler.ts index 993c3fd..08cd2f5 100644 --- a/lib/contracts/handlers/puffer-vault-handler.ts +++ b/lib/contracts/handlers/puffer-vault-handler.ts @@ -28,8 +28,12 @@ export class PufferVaultHandler { this.viemChain = VIEM_CHAINS[chain]; } - // This is a method because the typings are complex and lost when - // trying to make it a member. + /** + * Get the contract. This is a method because the typings are complex + * and lost when trying to make it a member. + * + * @returns The viem contract. + */ public getContract() { return getContract({ address: CHAIN_ADDRESSES[this.chain].PufferVault as Address, diff --git a/lib/contracts/puf-tokens.ts b/lib/contracts/puf-tokens.ts deleted file mode 100644 index 62a8998..0000000 --- a/lib/contracts/puf-tokens.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { Address } from 'viem'; -import { Chain } from '../chains/constants'; - -export enum PufToken { - pufWETH = 'pufWETH', - pufStETH = 'pufStETH', - pufWstETH = 'pufWstETH', - pufALT = 'pufALT', - pufEETH = 'pufEETH', -} - -// TODO: Update the addresses once the contracts are deployed on chain. -// See https://dev.azure.com/pufferfi/Frontend/_workitems/edit/1797. -export const PUF_TOKEN_ADDRESSES: { - [key in PufToken]: { [chain in Chain]: Address }; -} = { - [PufToken.pufWETH]: { - [Chain.Mainnet]: '0x0000000000000000000000000000000000000000', - [Chain.Holesky]: '0xfcf6c4e0387a523b73691d5604e5a6da1607c8a0', - }, - [PufToken.pufStETH]: { - [Chain.Mainnet]: '0x0000000000000000000000000000000000000000', - [Chain.Holesky]: '0x0000000000000000000000000000000000000000', - }, - [PufToken.pufWstETH]: { - [Chain.Mainnet]: '0x0000000000000000000000000000000000000000', - [Chain.Holesky]: '0x0000000000000000000000000000000000000000', - }, - [PufToken.pufALT]: { - [Chain.Mainnet]: '0x0000000000000000000000000000000000000000', - [Chain.Holesky]: '0x0000000000000000000000000000000000000000', - }, - [PufToken.pufEETH]: { - [Chain.Mainnet]: '0x0000000000000000000000000000000000000000', - [Chain.Holesky]: '0x0000000000000000000000000000000000000000', - }, -}; diff --git a/lib/contracts/tokens.ts b/lib/contracts/tokens.ts index eb632b4..17a90dc 100644 --- a/lib/contracts/tokens.ts +++ b/lib/contracts/tokens.ts @@ -3,14 +3,36 @@ import { Chain } from '../chains/constants'; export enum Token { ETH = 'ETH', + wETH = 'wETH', stETH = 'stETH', wstETH = 'wstETH', pufETH = 'pufETH', + // Wrapped PufTokens. + pufpufETH = 'pufpufETH', + pufWETH = 'pufWETH', + pufStETH = 'pufStETH', + pufWstETH = 'pufWstETH', + pufALT = 'pufALT', + pufEETH = 'pufEETH', } +export type NonPufToken = Extract< + Token, + 'ETH' | 'wETH' | 'stETH' | 'wstETH' | 'pufETH' +>; + +export type PufToken = Extract< + Token, + 'pufWETH' | 'pufStETH' | 'pufWstETH' | 'pufALT' | 'pufEETH' +>; + export const TOKENS_ADDRESSES: { [key in Token]: { [chain in Chain]: Address }; } = { + [Token.wETH]: { + [Chain.Mainnet]: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', + [Chain.Holesky]: '0x94373a4919b3240d86ea41593d5eba789fef3848', + }, [Token.stETH]: { [Chain.Mainnet]: '0xae7ab96520de3a18e5e111b5eaab095312d7fe84', [Chain.Holesky]: '0x3F1c547b21f65e10480dE3ad8E19fAAC46C95034', @@ -24,4 +46,31 @@ export const TOKENS_ADDRESSES: { [Chain.Holesky]: '0x9196830bB4c05504E0A8475A0aD566AceEB6BeC9', }, [Token.ETH]: {}, + // TODO: Update the addresses once the contracts are deployed on chain. + // See https://dev.azure.com/pufferfi/Frontend/_workitems/edit/1797. + // Wrapper PufTokens. + [Token.pufpufETH]: { + [Chain.Mainnet]: '0x0000000000000000000000000000000000000000', + [Chain.Holesky]: '0x0000000000000000000000000000000000000000', + }, + [Token.pufWETH]: { + [Chain.Mainnet]: '0x0000000000000000000000000000000000000000', + [Chain.Holesky]: '0xfcf6c4e0387a523b73691d5604e5a6da1607c8a0', + }, + [Token.pufStETH]: { + [Chain.Mainnet]: '0x0000000000000000000000000000000000000000', + [Chain.Holesky]: '0x0000000000000000000000000000000000000000', + }, + [Token.pufWstETH]: { + [Chain.Mainnet]: '0x0000000000000000000000000000000000000000', + [Chain.Holesky]: '0x0000000000000000000000000000000000000000', + }, + [Token.pufALT]: { + [Chain.Mainnet]: '0x0000000000000000000000000000000000000000', + [Chain.Holesky]: '0x0000000000000000000000000000000000000000', + }, + [Token.pufEETH]: { + [Chain.Mainnet]: '0x0000000000000000000000000000000000000000', + [Chain.Holesky]: '0x0000000000000000000000000000000000000000', + }, }; diff --git a/lib/main.ts b/lib/main.ts index 315bcbf..edbacf7 100644 --- a/lib/main.ts +++ b/lib/main.ts @@ -2,4 +2,3 @@ export * from './api/puffer-client'; export * from './api/puffer-client-helpers'; export * from './chains/constants'; export * from './contracts/tokens'; -export * from './contracts/puf-tokens'; diff --git a/test/mocks/permit-signature.ts b/test/mocks/permit-signature.ts new file mode 100644 index 0000000..baece92 --- /dev/null +++ b/test/mocks/permit-signature.ts @@ -0,0 +1,9 @@ +import { Address } from 'viem'; + +export const mockPermitSignature = { + r: `0x${new Array(64).fill(0).join('')}` as Address, + s: `0x${new Array(64).fill(0).join('')}` as Address, + v: 0n, + yParity: 1, + deadline: 0n, +};