UNPKG

@zerodev/sdk

Version:

A utility library for working with ERC-4337

493 lines (486 loc) 19.2 kB
import type { Abi, Chain, Client, ContractFunctionArgs, ContractFunctionName, Hash, Prettify, Transport, TypedData, WriteContractParameters } from "viem" import type { EntryPointVersion, SmartAccount } from "viem/account-abstraction" import { getKernelV3ModuleCurrentNonce } from "../../actions/account-client/getKernelV3ModuleCurrentNonce.js" import { type GetUserOperationGasPriceReturnType, getUserOperationGasPrice } from "../../actions/account-client/getUserOperationGasPrice.js" import { type InvalidateNonceParameters, invalidateNonce } from "../../actions/account-client/invalidateNonce.js" import { sendTransaction } from "../../actions/account-client/sendTransaction.js" import { signMessage } from "../../actions/account-client/signMessage.js" import { signTypedData } from "../../actions/account-client/signTypedData.js" import { type UpgradeKernelParameters, upgradeKernel } from "../../actions/account-client/upgradeKernel.js" import { writeContract } from "../../actions/account-client/writeContract.js" import type { ChangeSudoValidatorParameters, SignUserOperationReturnType, UninstallPluginParameters } from "../../actions/index.js" import { changeSudoValidator, signUserOperation, uninstallPlugin } from "../../actions/index.js" import { type EstimateGasInERC20Parameters, type EstimateGasInERC20ReturnType, estimateGasInERC20 } from "../../actions/paymaster/estimateGasInERC20.js" import { type SponsorUserOperationParameters, type SponsorUserOperationReturnType, sponsorUserOperation } from "../../actions/paymaster/sponsorUserOperation.js" import type { ZeroDevPaymasterClient } from "../paymasterClient.js" export type ZeroDevPaymasterClientActions = { /** * Returns paymasterAndData & updated gas parameters required to sponsor a userOperation. */ sponsorUserOperation: ( args: SponsorUserOperationParameters ) => Promise<SponsorUserOperationReturnType> estimateGasInERC20: ( args: EstimateGasInERC20Parameters ) => Promise<EstimateGasInERC20ReturnType> } export const zerodevPaymasterActions = <entryPointVersion extends EntryPointVersion>() => (client: Client): ZeroDevPaymasterClientActions => ({ sponsorUserOperation: async (args: SponsorUserOperationParameters) => sponsorUserOperation( client as ZeroDevPaymasterClient<entryPointVersion>, { ...args } ), estimateGasInERC20: async (args: EstimateGasInERC20Parameters) => estimateGasInERC20( client as ZeroDevPaymasterClient<entryPointVersion>, args ) }) export type KernelAccountClientActions< TChain extends Chain | undefined = Chain | undefined, TSmartAccount extends SmartAccount | undefined = SmartAccount | undefined > = { /** * Signs a user operation with the given transport, chain, and smart account. * * @param args - Parameters for the signUserOperation function * @returns A promise that resolves to the result of the signUserOperation function */ signUserOperation: < accountOverride extends SmartAccount | undefined = undefined, calls extends readonly unknown[] = readonly unknown[] >( args: Parameters< typeof signUserOperation< TSmartAccount, TChain, accountOverride, calls > >[1] ) => Promise<SignUserOperationReturnType> /** * Returns the live gas prices that you can use to send a user operation. * * @returns maxFeePerGas & maxPriorityFeePerGas {@link GetUserOperationGasPriceReturnType} */ getUserOperationGasPrice: () => Promise< Prettify<GetUserOperationGasPriceReturnType> > /** * Creates, signs, and sends an uninstall kernel plugin transaction to the network. * This function also allows you to sponsor this transaction if sender is a smartAccount * * * @param args - {@link UninstallPermissionParameters} * @returns The [Transaction](https://viem.sh/docs/glossary/terms.html#transaction) hash. {@link SendTransactionReturnType} */ uninstallPlugin: < accountOverride extends SmartAccount | undefined = undefined, calls extends readonly unknown[] = readonly unknown[] >( args: UninstallPluginParameters<TSmartAccount, accountOverride, calls> ) => Promise<Hash> /** * Creates, signs, and sends a user operation to change sudo validator to the network. * This function also allows you to sponsor this transaction if sender is a smartAccount */ changeSudoValidator: < accountOverride extends SmartAccount | undefined = undefined, calls extends readonly unknown[] = readonly unknown[] >( args: ChangeSudoValidatorParameters< TSmartAccount, accountOverride, calls > ) => Promise<Hash> /** * Creates, signs, and sends a kernel v3 module nonce invalidation transaction to the network. * This function also allows you to sponsor this transaction if sender is a smartAccount * * * @param args - {@link InvalidateNonceParameters} * @returns The [Transaction](https://viem.sh/docs/glossary/terms.html#transaction) hash. {@link SendTransactionReturnType} */ invalidateNonce: < accountOverride extends SmartAccount | undefined = undefined, calls extends readonly unknown[] = readonly unknown[] >( args: InvalidateNonceParameters<TSmartAccount, accountOverride, calls> ) => Promise<Hash> /** * Creates, signs, and sends a transaction to fetch KernelV3 module nonce to the network. * This function also allows you to sponsor this transaction if sender is a smartAccount * * * @returns nonce */ getKernelV3ModuleCurrentNonce: () => Promise<number> /** * Creates, signs, and sends a transaction to upgrade the kernel to the network. * This function also allows you to sponsor this transaction if sender is a smartAccount * * @param args - {@link UpgradeKernelParameters} * @returns The [Transaction](https://viem.sh/docs/glossary/terms.html#transaction) hash. {@link SendTransactionReturnType} */ upgradeKernel: < accountOverride extends SmartAccount | undefined = undefined, calls extends readonly unknown[] = readonly unknown[] >( args: UpgradeKernelParameters<TSmartAccount, accountOverride, calls> ) => Promise<Hash> /** * Creates, signs, and sends a new transaction to the network. * This function also allows you to sponsor this transaction if sender is a smartAccount * * - Docs: https://viem.sh/docs/actions/wallet/sendTransaction.html * - Examples: https://stackblitz.com/github/wagmi-dev/viem/tree/main/examples/transactions/sending-transactions * - JSON-RPC Methods: * - JSON-RPC Accounts: [`eth_sendTransaction`](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_sendtransaction) * - Local Accounts: [`eth_sendRawTransaction`](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_sendrawtransaction) * * @param args - {@link SendTransactionParameters} * @returns The [Transaction](https://viem.sh/docs/glossary/terms.html#transaction) hash. {@link SendTransactionReturnType} * * @example * import { createWalletClient, custom } from 'viem' * import { mainnet } from 'viem/chains' * * const client = createWalletClient({ * chain: mainnet, * transport: custom(window.ethereum), * }) * const hash = await client.sendTransaction({ * account: '0xA0Cf798816D4b9b9866b5330EEa46a18382f251e', * to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', * value: 1000000000000000000n, * }) * * @example * // Account Hoisting * import { createWalletClient, http } from 'viem' * import { privateKeyToAccount } from 'viem/accounts' * import { mainnet } from 'viem/chains' * * const client = createWalletClient({ * account: privateKeyToAccount('0x…'), * chain: mainnet, * transport: http(), * }) * const hash = await client.sendTransaction({ * to: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8', * value: 1000000000000000000n, * }) */ sendTransaction: < TChainOverride extends Chain | undefined = undefined, accountOverride extends SmartAccount | undefined = undefined, calls extends readonly unknown[] = readonly unknown[] >( args: Parameters< typeof sendTransaction< TSmartAccount, TChain, accountOverride, TChainOverride, calls > >[1] ) => Promise<Hash> /** * Calculates an Ethereum-specific signature in [EIP-191 format](https://eips.ethereum.org/EIPS/eip-191): `keccak256("\x19Ethereum Signed Message:\n" + len(message) + message))`. * * - Docs: https://viem.sh/docs/actions/wallet/signMessage.html * - JSON-RPC Methods: * - JSON-RPC Accounts: [`personal_sign`](https://docs.metamask.io/guide/signing-data.html#personal-sign) * - Local Accounts: Signs locally. No JSON-RPC request. * * With the calculated signature, you can: * - use [`verifyMessage`](https://viem.sh/docs/utilities/verifyMessage.html) to verify the signature, * - use [`recoverMessageAddress`](https://viem.sh/docs/utilities/recoverMessageAddress.html) to recover the signing address from a signature. * * @param args - {@link SignMessageParameters} * @returns The signed message. {@link SignMessageReturnType} * * @example * import { createWalletClient, custom } from 'viem' * import { mainnet } from 'viem/chains' * * const client = createWalletClient({ * chain: mainnet, * transport: custom(window.ethereum), * }) * const signature = await client.signMessage({ * account: '0xA0Cf798816D4b9b9866b5330EEa46a18382f251e', * message: 'hello world', * }) * * @example * // Account Hoisting * import { createWalletClient, http } from 'viem' * import { privateKeyToAccount } from 'viem/accounts' * import { mainnet } from 'viem/chains' * * const client = createWalletClient({ * account: privateKeyToAccount('0x…'), * chain: mainnet, * transport: http(), * }) * const signature = await client.signMessage({ * message: 'hello world', * }) */ signMessage: ( args: Parameters<typeof signMessage<TSmartAccount>>[1] ) => ReturnType<typeof signMessage<TSmartAccount>> /** * Signs typed data and calculates an Ethereum-specific signature in [EIP-191 format](https://eips.ethereum.org/EIPS/eip-191): `keccak256("\x19Ethereum Signed Message:\n" + len(message) + message))`. * * - Docs: https://viem.sh/docs/actions/wallet/signTypedData.html * - JSON-RPC Methods: * - JSON-RPC Accounts: [`eth_signTypedData_v4`](https://docs.metamask.io/guide/signing-data.html#signtypeddata-v4) * - Local Accounts: Signs locally. No JSON-RPC request. * * @param client - Client to use * @param args - {@link SignTypedDataParameters} * @returns The signed data. {@link SignTypedDataReturnType} * * @example * import { createWalletClient, custom } from 'viem' * import { mainnet } from 'viem/chains' * * const client = createWalletClient({ * chain: mainnet, * transport: custom(window.ethereum), * }) * const signature = await client.signTypedData({ * account: '0xA0Cf798816D4b9b9866b5330EEa46a18382f251e', * domain: { * name: 'Ether Mail', * version: '1', * chainId: 1, * verifyingContract: '0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC', * }, * types: { * Person: [ * { name: 'name', type: 'string' }, * { name: 'wallet', type: 'address' }, * ], * Mail: [ * { name: 'from', type: 'Person' }, * { name: 'to', type: 'Person' }, * { name: 'contents', type: 'string' }, * ], * }, * primaryType: 'Mail', * message: { * from: { * name: 'Cow', * wallet: '0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826', * }, * to: { * name: 'Bob', * wallet: '0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB', * }, * contents: 'Hello, Bob!', * }, * }) * * @example * // Account Hoisting * import { createWalletClient, http } from 'viem' * import { privateKeyToAccount } from 'viem/accounts' * import { mainnet } from 'viem/chains' * * const client = createWalletClient({ * account: privateKeyToAccount('0x…'), * chain: mainnet, * transport: http(), * }) * const signature = await client.signTypedData({ * domain: { * name: 'Ether Mail', * version: '1', * chainId: 1, * verifyingContract: '0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC', * }, * types: { * Person: [ * { name: 'name', type: 'string' }, * { name: 'wallet', type: 'address' }, * ], * Mail: [ * { name: 'from', type: 'Person' }, * { name: 'to', type: 'Person' }, * { name: 'contents', type: 'string' }, * ], * }, * primaryType: 'Mail', * message: { * from: { * name: 'Cow', * wallet: '0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826', * }, * to: { * name: 'Bob', * wallet: '0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB', * }, * contents: 'Hello, Bob!', * }, * }) */ signTypedData: < const TTypedData extends TypedData | { [key: string]: unknown }, TPrimaryType extends string >( args: Parameters< typeof signTypedData<TTypedData, TPrimaryType, TSmartAccount> >[1] ) => ReturnType< typeof signTypedData<TTypedData, TPrimaryType, TSmartAccount> > /** * Executes a write function on a contract. * This function also allows you to sponsor this transaction if sender is a smartAccount * * - Docs: https://viem.sh/docs/contract/writeContract.html * - Examples: https://stackblitz.com/github/wagmi-dev/viem/tree/main/examples/contracts/writing-to-contracts * * A "write" function on a Solidity contract modifies the state of the blockchain. These types of functions require gas to be executed, and hence a [Transaction](https://viem.sh/docs/glossary/terms.html) is needed to be broadcast in order to change the state. * * Internally, uses a [Wallet Client](https://viem.sh/docs/clients/wallet.html) to call the [`sendTransaction` action](https://viem.sh/docs/actions/wallet/sendTransaction.html) with [ABI-encoded `data`](https://viem.sh/docs/contract/encodeFunctionData.html). * * __Warning: The `write` internally sends a transaction – it does not validate if the contract write will succeed (the contract may throw an error). It is highly recommended to [simulate the contract write with `contract.simulate`](https://viem.sh/docs/contract/writeContract.html#usage) before you execute it.__ * * @param args - {@link WriteContractParameters} * @returns A [Transaction Hash](https://viem.sh/docs/glossary/terms.html#hash). {@link WriteContractReturnType} * * @example * import { createWalletClient, custom, parseAbi } from 'viem' * import { mainnet } from 'viem/chains' * * const client = createWalletClient({ * chain: mainnet, * transport: custom(window.ethereum), * }) * const hash = await client.writeContract({ * address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2', * abi: parseAbi(['function mint(uint32 tokenId) nonpayable']), * functionName: 'mint', * args: [69420], * }) * * @example * // With Validation * import { createWalletClient, custom, parseAbi } from 'viem' * import { mainnet } from 'viem/chains' * * const client = createWalletClient({ * chain: mainnet, * transport: custom(window.ethereum), * }) * const { request } = await client.simulateContract({ * address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2', * abi: parseAbi(['function mint(uint32 tokenId) nonpayable']), * functionName: 'mint', * args: [69420], * } * const hash = await client.writeContract(request) */ writeContract: < const TAbi extends Abi | readonly unknown[], TFunctionName extends ContractFunctionName< TAbi, "nonpayable" | "payable" > = ContractFunctionName<TAbi, "nonpayable" | "payable">, TArgs extends ContractFunctionArgs< TAbi, "nonpayable" | "payable", TFunctionName > = ContractFunctionArgs<TAbi, "nonpayable" | "payable", TFunctionName>, TChainOverride extends Chain | undefined = undefined >( args: WriteContractParameters< TAbi, TFunctionName, TArgs, TChain, TSmartAccount, TChainOverride > ) => ReturnType< typeof writeContract< TChain, TSmartAccount, TAbi, TFunctionName, TArgs, TChainOverride > > } export function kernelAccountClientActions() { return < TChain extends Chain | undefined = Chain | undefined, TSmartAccount extends SmartAccount | undefined = | SmartAccount | undefined >( client: Client<Transport, TChain, TSmartAccount> ): KernelAccountClientActions<TChain, TSmartAccount> => ({ signUserOperation: (args) => signUserOperation(client, args), getUserOperationGasPrice: async () => getUserOperationGasPrice(client), uninstallPlugin: async (args) => uninstallPlugin(client, args), changeSudoValidator: async (args) => changeSudoValidator(client, args), invalidateNonce: async (args) => invalidateNonce(client, args), getKernelV3ModuleCurrentNonce: async () => getKernelV3ModuleCurrentNonce(client), upgradeKernel: async (args) => upgradeKernel(client, args), // biome-ignore lint/suspicious/noExplicitAny: <explanation> sendTransaction: (args) => sendTransaction(client, args as any), signMessage: (args) => signMessage(client, args), signTypedData: (args) => signTypedData(client, args), writeContract: (args) => writeContract(client, args) }) }