From 9af48c17e35f3f4154876e8d5c6e6937b6ae9372 Mon Sep 17 00:00:00 2001 From: Nicholas Rodrigues Lordello Date: Thu, 8 Aug 2024 14:18:59 +0200 Subject: [PATCH] Fix TypeScript Lints --- examples/4337-gas-metering/pimlico/pimlico.ts | 2 +- examples/4337-gas-metering/utils/abi.ts | 30 +-- examples/4337-gas-metering/utils/erc20.ts | 172 +++++------------- .../4337-gas-metering/utils/nativeTransfer.ts | 6 +- examples/4337-gas-metering/utils/safe.ts | 24 +-- examples/4337-gas-metering/utils/userOps.ts | 51 +++++- 6 files changed, 124 insertions(+), 161 deletions(-) diff --git a/examples/4337-gas-metering/pimlico/pimlico.ts b/examples/4337-gas-metering/pimlico/pimlico.ts index 619d2ca5..4372af0b 100644 --- a/examples/4337-gas-metering/pimlico/pimlico.ts +++ b/examples/4337-gas-metering/pimlico/pimlico.ts @@ -260,7 +260,7 @@ if (transactionType === UserOperationType.VerifyingPaymaster) { if (senderUSDCBalance < usdcAmount) { console.log(`\nTransferring ${usdcAmount / usdcDenomination} USDC Token for paying the Paymaster from Sender to Safe.`) - await transferERC20Token(usdcTokenAddress, publicClient, signer, senderAddress, usdcAmount, chain, paymaster) + await transferERC20Token(usdcTokenAddress, publicClient, signer, senderAddress, usdcAmount, paymaster) while (senderUSDCBalance < usdcAmount) { await setTimeout(15000) senderUSDCBalance = await getERC20Balance(usdcTokenAddress, publicClient, senderAddress) diff --git a/examples/4337-gas-metering/utils/abi.ts b/examples/4337-gas-metering/utils/abi.ts index ede7f311..77c6f3f1 100644 --- a/examples/4337-gas-metering/utils/abi.ts +++ b/examples/4337-gas-metering/utils/abi.ts @@ -10,7 +10,7 @@ export const ERC20_TOKEN_APPROVE_ABI = [ stateMutability: 'nonpayable', type: 'function', }, -] +] as const export const ERC20_TOKEN_TRANSFER_ABI = [ { @@ -24,7 +24,7 @@ export const ERC20_TOKEN_TRANSFER_ABI = [ stateMutability: 'nonpayable', type: 'function', }, -] +] as const export const ERC20_TOKEN_DECIMALS_ABI = [ { @@ -34,7 +34,7 @@ export const ERC20_TOKEN_DECIMALS_ABI = [ type: 'function', stateMutability: 'view', }, -] +] as const export const ERC20_TOKEN_BALANCE_OF_ABI = [ { @@ -44,7 +44,7 @@ export const ERC20_TOKEN_BALANCE_OF_ABI = [ type: 'function', stateMutability: 'view', }, -] +] as const export const ERC20_TOKEN_MINT_ABI = [ { @@ -57,7 +57,7 @@ export const ERC20_TOKEN_MINT_ABI = [ type: 'function', stateMutability: 'public', }, -] +] as const export const ERC721_TOKEN_SAFEMINT_ABI = [ { @@ -68,7 +68,7 @@ export const ERC721_TOKEN_SAFEMINT_ABI = [ stateMutability: 'nonpayable', type: 'function', }, -] +] as const export const MULTISEND_ABI = [ { @@ -78,7 +78,7 @@ export const MULTISEND_ABI = [ stateMutability: 'payable', type: 'function', }, -] +] as const export const SAFE_NONCE_ABI = [ { @@ -88,7 +88,7 @@ export const SAFE_NONCE_ABI = [ type: 'function', stateMutability: 'view', }, -] +] as const export const SAFE_EXECTRANSACTION_ABI = [ { @@ -110,7 +110,7 @@ export const SAFE_EXECTRANSACTION_ABI = [ stateMutability: 'external', type: 'function', }, -] +] as const export const SAFE_SETUP_ABI = [ { @@ -161,7 +161,7 @@ export const SAFE_SETUP_ABI = [ stateMutability: 'nonpayable', type: 'function', }, -] +] as const export const SAFE_ENABLE_MODULES_ABI = [ { @@ -177,7 +177,7 @@ export const SAFE_ENABLE_MODULES_ABI = [ stateMutability: 'nonpayable', type: 'function', }, -] +] as const export const SAFE_FACTORY_CREATE_PROXY_WITH_NONCE_ABI = [ { @@ -209,7 +209,7 @@ export const SAFE_FACTORY_CREATE_PROXY_WITH_NONCE_ABI = [ stateMutability: 'nonpayable', type: 'function', }, -] +] as const export const SAFE_4337_EXECUTE_USEROP_ABI = [ { @@ -240,7 +240,7 @@ export const SAFE_4337_EXECUTE_USEROP_ABI = [ stateMutability: 'nonpayable', type: 'function', }, -] +] as const export const SAFE_FACTORY_PROXY_CREATION_CODE_ABI = [ { @@ -256,7 +256,7 @@ export const SAFE_FACTORY_PROXY_CREATION_CODE_ABI = [ stateMutability: 'pure', type: 'function', }, -] +] as const export const SAFE_4337_MODULE_ABI = [ { inputs: [{ internalType: 'address', name: 'entryPoint', type: 'address' }], stateMutability: 'nonpayable', type: 'constructor' }, @@ -476,4 +476,4 @@ export const SAFE_4337_MODULE_ABI = [ stateMutability: 'nonpayable', type: 'function', }, -] +] as const diff --git a/examples/4337-gas-metering/utils/erc20.ts b/examples/4337-gas-metering/utils/erc20.ts index 290a981e..acc00061 100644 --- a/examples/4337-gas-metering/utils/erc20.ts +++ b/examples/4337-gas-metering/utils/erc20.ts @@ -1,6 +1,5 @@ import dotenv from 'dotenv' -import { HttpTransport, http, Address, encodeFunctionData, createWalletClient, PrivateKeyAccount, PublicClient } from 'viem' -import { baseSepolia, goerli, sepolia } from 'viem/chains' +import { Address, Chain, PrivateKeyAccount, PublicClient, Transport, createWalletClient, encodeFunctionData, http } from 'viem' import { ERC20_TOKEN_APPROVE_ABI, ERC20_TOKEN_BALANCE_OF_ABI, @@ -32,95 +31,48 @@ export const generateTransferCallData = (to: Address, value: bigint) => { return transferData } -export const getERC20Decimals = async ( +export const getERC20Decimals = async ( erc20TokenAddress: Address, - publicClient: PublicClient, -): Promise => { - const erc20Decimals = (await publicClient.readContract({ + publicClient: PublicClient, C>, +): Promise => { + const erc20Decimals = await publicClient.readContract({ abi: ERC20_TOKEN_DECIMALS_ABI, address: erc20TokenAddress, functionName: 'decimals', - })) as bigint + }) return erc20Decimals } -export const getERC20Balance = async ( +export const getERC20Balance = async ( erc20TokenAddress: Address, - publicClient: PublicClient, + publicClient: PublicClient, C>, owner: Address, ): Promise => { - const senderERC20Balance = (await publicClient.readContract({ + const senderERC20Balance = await publicClient.readContract({ abi: ERC20_TOKEN_BALANCE_OF_ABI, address: erc20TokenAddress, functionName: 'balanceOf', args: [owner], - })) as bigint + }) return senderERC20Balance } -export const mintERC20Token = async ( +export const mintERC20Token = async ( erc20TokenAddress: Address, - publicClient: PublicClient, + publicClient: PublicClient, C>, signer: PrivateKeyAccount, to: Address, amount: bigint, - chain: string, paymaster: string, ) => { - let walletClient - if (paymaster == 'pimlico') { - if (chain == 'sepolia') { - walletClient = createWalletClient({ - account: signer, - chain: sepolia, - transport: http(pimlicoRPCURL), - }) - } else if (chain == 'base-sepolia') { - walletClient = createWalletClient({ - account: signer, - chain: baseSepolia, - transport: http(pimlicoRPCURL), - }) - } else { - throw new Error('Current code only support limited networks. Please make required changes if you want to use custom network.') - } - } else if (paymaster == 'alchemy') { - if (chain == 'sepolia') { - walletClient = createWalletClient({ - account: signer, - chain: sepolia, - transport: http(alchemyRPCURL), - }) - } else if (chain == 'goerli') { - walletClient = createWalletClient({ - account: signer, - chain: goerli, - transport: http(alchemyRPCURL), - }) - } else { - throw new Error('Current code only support limited networks. Please make required changes if you want to use custom network.') - } - } else if (paymaster == 'gelato') { - if (chain == 'sepolia') { - walletClient = createWalletClient({ - account: signer, - chain: sepolia, - transport: http(gelatoRPCURL), - }) - } else if (chain == 'base-sepolia') { - walletClient = createWalletClient({ - account: signer, - chain: baseSepolia, - transport: http(gelatoRPCURL), - }) - } else { - throw new Error('Current code only support limited networks. Please make required changes if you want to use custom network.') - } - } else { - throw new Error('Current code only support Pimlico and Alchemy. Please make required changes if you want to use a different Paymaster.') - } + const walletClient = createWalletClient({ + account: signer, + chain: publicClient.chain, + transport: getTransport(paymaster), + }) + const { request } = await publicClient.simulateContract({ address: erc20TokenAddress, abi: ERC20_TOKEN_MINT_ABI, @@ -128,70 +80,27 @@ export const mintERC20Token = async ( args: [to, amount], account: signer, }) - await walletClient.writeContract(request) + + // I cannot get Viem to accept the `request` type here, and it seems to be related to this + // function being generic on the chain type. Using concrete chain types helps, but doesn't + // completely solve the issue either. + // eslint-disable-next-line @typescript-eslint/no-explicit-any + await walletClient.writeContract(request as any) } -export const transferERC20Token = async ( +export const transferERC20Token = async ( erc20TokenAddress: Address, - publicClient: PublicClient, + publicClient: PublicClient, C>, signer: PrivateKeyAccount, to: Address, amount: bigint, - chain: string, paymaster: string, ) => { - let walletClient - if (paymaster == 'pimlico') { - if (chain == 'sepolia') { - walletClient = createWalletClient({ - account: signer, - chain: sepolia, - transport: http(pimlicoRPCURL), - }) - } else if (chain == 'base-sepolia') { - walletClient = createWalletClient({ - account: signer, - chain: baseSepolia, - transport: http(pimlicoRPCURL), - }) - } else { - throw new Error('Current code only support limited networks. Please make required changes if you want to use custom network.') - } - } else if (paymaster == 'alchemy') { - if (chain == 'sepolia') { - walletClient = createWalletClient({ - account: signer, - chain: sepolia, - transport: http(alchemyRPCURL), - }) - } else if (chain == 'goerli') { - walletClient = createWalletClient({ - account: signer, - chain: goerli, - transport: http(alchemyRPCURL), - }) - } else { - throw new Error('Current code only support limited networks. Please make required changes if you want to use custom network.') - } - } else if (paymaster == 'gelato') { - if (chain == 'sepolia') { - walletClient = createWalletClient({ - account: signer, - chain: sepolia, - transport: http(gelatoRPCURL), - }) - } else if (chain == 'base-sepolia') { - walletClient = createWalletClient({ - account: signer, - chain: baseSepolia, - transport: http(gelatoRPCURL), - }) - } else { - throw new Error('Current code only support limited networks. Please make required changes if you want to use custom network.') - } - } else { - throw new Error('Current code only support Pimlico and Alchemy. Please make required changes if you want to use a different Paymaster.') - } + const walletClient = createWalletClient({ + account: signer, + chain: publicClient.chain, + transport: getTransport(paymaster), + }) const signerERC20Bal = await getERC20Balance(erc20TokenAddress, publicClient, signer.address) if (signerERC20Bal < amount) { @@ -206,5 +115,22 @@ export const transferERC20Token = async ( args: [to, amount], account: signer, }) - await walletClient.writeContract(request) + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + await walletClient.writeContract(request as any) +} + +const getTransport = (paymaster: string): Transport<'http'> => { + switch (paymaster) { + case 'pimlico': + return http(pimlicoRPCURL) + case 'alchemy': + return http(alchemyRPCURL) + case 'gelato': + return http(gelatoRPCURL) + default: + throw new Error( + 'Current code only support Alchemy, Pimlico and Gelato. Please make required changes if you want to use a different Paymaster.', + ) + } } diff --git a/examples/4337-gas-metering/utils/nativeTransfer.ts b/examples/4337-gas-metering/utils/nativeTransfer.ts index be4d2f62..e9b645c4 100644 --- a/examples/4337-gas-metering/utils/nativeTransfer.ts +++ b/examples/4337-gas-metering/utils/nativeTransfer.ts @@ -1,5 +1,5 @@ import dotenv from 'dotenv' -import { http, createWalletClient, PrivateKeyAccount, Address, WalletClient, HttpTransport, Chain, Account, PublicClient } from 'viem' +import { Account, Address, Chain, HttpTransport, PrivateKeyAccount, PublicClient, WalletClient, createWalletClient, http } from 'viem' import { baseSepolia, goerli, sepolia } from 'viem/chains' import { setTimeout } from 'timers/promises' @@ -8,8 +8,8 @@ const pimlicoRPCURL = process.env.PIMLICO_RPC_URL const alchemyRPCURL = process.env.ALCHEMY_RPC_URL const gelatoRPCURL = process.env.GELATO_RPC_URL -export const transferETH = async ( - publicClient: PublicClient, +export const transferETH = async ( + publicClient: PublicClient, signer: PrivateKeyAccount, receiver: Address, amount: bigint, diff --git a/examples/4337-gas-metering/utils/safe.ts b/examples/4337-gas-metering/utils/safe.ts index b205aab1..b1556733 100644 --- a/examples/4337-gas-metering/utils/safe.ts +++ b/examples/4337-gas-metering/utils/safe.ts @@ -1,5 +1,6 @@ import { Address, + Chain, Hex, PrivateKeyAccount, PublicClient, @@ -16,7 +17,6 @@ import { import { InternalTx, encodeMultiSend } from './multisend' import { generateApproveCallData, generateTransferCallData, getERC20Balance, getERC20Decimals, mintERC20Token } from './erc20' import { setTimeout } from 'timers/promises' -import { baseSepolia, sepolia } from 'viem/chains' import { generateMintingCallData } from './erc721' import { transferETH } from './nativeTransfer' import { @@ -38,7 +38,7 @@ export interface MetaTransaction { nonce: bigint } -export const getGelatoCallData = async ({ +export const getGelatoCallData = async ({ safe, owner, publicClient, @@ -48,7 +48,7 @@ export const getGelatoCallData = async ({ }: { safe: Address owner: PrivateKeyAccount - publicClient: PublicClient, typeof sepolia | typeof baseSepolia> + publicClient: PublicClient, C> txType: string erc20TokenAddress: Address erc721TokenAddress: Address @@ -176,7 +176,7 @@ const getInitializerCode = async ({ }) } -const getGelatoInitializerCode = async ({ +const getGelatoInitializerCode = async ({ owner, client, txType, @@ -187,7 +187,7 @@ const getGelatoInitializerCode = async ({ erc721TokenAddress, }: { owner: Address - client: PublicClient, typeof sepolia | typeof baseSepolia> + client: PublicClient, C> txType: string safeModuleSetupAddress: Address safe4337ModuleAddress: Address @@ -242,7 +242,7 @@ const getGelatoInitializerCode = async ({ }) } -export const prepareForGelatoTx = async ({ +export const prepareForGelatoTx = async ({ signer, chain, publicClient, @@ -252,7 +252,7 @@ export const prepareForGelatoTx = async ({ }: { signer: PrivateKeyAccount chain: string - publicClient: PublicClient, typeof sepolia | typeof baseSepolia> + publicClient: PublicClient, C> txType: string senderAddress: Address erc20TokenAddress: Address @@ -267,7 +267,7 @@ export const prepareForGelatoTx = async ({ // Trying to mint tokens (Make sure ERC20 Token Contract is mintable by anyone). if (senderERC20Balance < erc20Amount) { console.log('\nMinting ERC20 Tokens to Safe Wallet.') - await mintERC20Token(erc20TokenAddress, publicClient, signer, senderAddress, erc20Amount, chain, 'gelato') + await mintERC20Token(erc20TokenAddress, publicClient, signer, senderAddress, erc20Amount, 'gelato') while (senderERC20Balance < erc20Amount) { await setTimeout(15000) @@ -341,7 +341,7 @@ export const getAccountInitCode = async ({ return initCodeCallData } -export const getGelatoAccountInitCode = async ({ +export const getGelatoAccountInitCode = async ({ owner, client, txType, @@ -354,7 +354,7 @@ export const getGelatoAccountInitCode = async ({ erc721TokenAddress, }: { owner: Address - client: PublicClient, typeof sepolia | typeof baseSepolia> + client: PublicClient, C> txType: string safeModuleSetupAddress: Address safe4337ModuleAddress: Address @@ -393,7 +393,7 @@ export const encodeCallData = (params: { to: Address; value: bigint; data: `0x${ }) } -export const getAccountAddress = async ({ +export const getAccountAddress = async ({ owner, client, txType = '', @@ -409,7 +409,7 @@ export const getAccountAddress = async ({ isGelato = false, }: { owner: Address - client: any + client: PublicClient, C> txType?: string safeModuleSetupAddress: Address safe4337ModuleAddress: Address diff --git a/examples/4337-gas-metering/utils/userOps.ts b/examples/4337-gas-metering/utils/userOps.ts index 3f5836f5..e75b2f9c 100644 --- a/examples/4337-gas-metering/utils/userOps.ts +++ b/examples/4337-gas-metering/utils/userOps.ts @@ -1,5 +1,17 @@ import dotenv from 'dotenv' -import { parseEther, type Hex, type PrivateKeyAccount, type Address, formatEther, concat, pad, toHex } from 'viem' +import { + parseEther, + type Hex, + type PrivateKeyAccount, + type Address, + type Chain, + type PublicClient, + type Transport, + formatEther, + concat, + pad, + toHex, +} from 'viem' import { encodeCallData } from './safe' import { EIP712_SAFE_OPERATION_TYPE } from './type' import { setTimeout } from 'timers/promises' @@ -31,10 +43,35 @@ export type UserOperation = { paymasterAndData?: never } +export type PackedUserOperation = { + sender: Address + nonce: bigint + initCode: Hex + callData: Hex + accountGasLimits: Hex + preVerificationGas: bigint + gasFees: Hex + paymasterAndData: Hex + signature: Hex +} + +export type UserOperationReceipt = { + actualGasUsed: bigint + receipt: { + transactionHash: Address + gasUsed: bigint + } +} + +export interface BundlerClient { + sendUserOperation: (args: { userOperation: UserOperation; entryPoint: Address }) => Promise
+ getUserOperationReceipt: (args: { hash: Address }) => Promise +} + export const submitUserOperationPimlico = async ( userOperation: UserOperation, - bundlerClient: any, - entryPointAddress: string, + bundlerClient: BundlerClient, + entryPointAddress: Address, chain: string, ) => { const userOperationHash = await bundlerClient.sendUserOperation({ @@ -110,9 +147,9 @@ export const signUserOperation = async ( return signatureBytes } -export const createCallData = async ( +export const createCallData = async ( chain: string, - publicClient: any, + publicClient: PublicClient, C>, signer: PrivateKeyAccount, txType: string, senderAddress: `0x${string}`, @@ -137,7 +174,7 @@ export const createCallData = async ( // Trying to mint tokens (Make sure ERC20 Token Contract is mintable by anyone). if (senderERC20Balance < erc20Amount) { console.log('\nMinting ERC20 Tokens to Safe Wallet.') - await mintERC20Token(erc20TokenAddress, publicClient, signer, senderAddress, erc20Amount, chain, paymaster) + await mintERC20Token(erc20TokenAddress, publicClient, signer, senderAddress, erc20Amount, paymaster) while (senderERC20Balance < erc20Amount) { await setTimeout(15000) @@ -222,7 +259,7 @@ export function getPaymasterAndData(unpackedUserOperation: UserOperation) { : '0x' } -export function toPackedUserOperation(unpackedUserOperation: UserOperation): Record { +export function toPackedUserOperation(unpackedUserOperation: UserOperation): PackedUserOperation { return { sender: unpackedUserOperation.sender, nonce: unpackedUserOperation.nonce,