UNPKG

@zerodev/sdk

Version:

A utility library for working with ERC-4337

277 lines (248 loc) 8.48 kB
// Copied from: https://github.com/pimlicolabs/permissionless.js/blob/main/packages/permissionless/actions/public/getSenderAddress.ts import { type Address, BaseError, type Client, type ContractFunctionExecutionErrorType, ContractFunctionRevertedError, type Hex, InvalidInputRpcError, type OneOf, type Prettify, RawContractError, RpcRequestError, UnknownRpcError, concat, decodeErrorResult } from "viem" import { simulateContract } from "viem/actions" import { getAction } from "viem/utils" export type GetSenderAddressParams = OneOf< | { initCode: Hex entryPointAddress: Address factory?: never factoryData?: never } | { entryPointAddress: Address factory: Address factoryData: Hex initCode?: never } > export class InvalidEntryPointError extends BaseError { override name = "InvalidEntryPointError" constructor({ cause, entryPointAddress }: { cause?: BaseError; entryPointAddress?: Address } = {}) { super( `The entry point address (\`entryPoint\`${ entryPointAddress ? ` = ${entryPointAddress}` : "" }) is not a valid entry point. getSenderAddress did not revert with a SenderAddressResult error.`, { cause } ) } } /** * Returns the address of the account that will be deployed with the given init code. * * - Docs: https://docs.pimlico.io/permissionless/reference/public-actions/getSenderAddress * * @param client {@link Client} that you created using viem's createPublicClient. * @param args {@link GetSenderAddressParams} initCode & entryPoint * @returns Sender's Address * * @example * import { createPublicClient } from "viem" * import { getSenderAddress } from "permissionless/actions" * * const publicClient = createPublicClient({ * chain: goerli, * transport: http("https://goerli.infura.io/v3/your-infura-key") * }) * * const senderAddress = await getSenderAddress(publicClient, { * initCode, * entryPoint * }) * * // Return '0x7a88a206ba40b37a8c07a2b5688cf8b287318b63' */ export const getSenderAddress = async ( client: Client, args: Prettify<GetSenderAddressParams> ): Promise<Address> => { const { initCode, entryPointAddress, factory, factoryData } = args if (!initCode && !factory && !factoryData) { throw new Error( "Either `initCode` or `factory` and `factoryData` must be provided" ) } try { await getAction( client, simulateContract, "simulateContract" )({ address: entryPointAddress, abi: [ { inputs: [ { internalType: "address", name: "sender", type: "address" } ], name: "SenderAddressResult", type: "error" }, { inputs: [ { internalType: "bytes", name: "initCode", type: "bytes" } ], name: "getSenderAddress", outputs: [], stateMutability: "nonpayable", type: "function" } ], functionName: "getSenderAddress", args: [initCode || concat([factory as Hex, factoryData as Hex])] }) } catch (e) { const revertError = (e as ContractFunctionExecutionErrorType).walk( (err) => err instanceof ContractFunctionRevertedError || err instanceof RpcRequestError || err instanceof InvalidInputRpcError || err instanceof UnknownRpcError ) if (!revertError) { // biome-ignore lint/suspicious/noExplicitAny: const cause = (e as ContractFunctionExecutionErrorType).cause as any const errorName = cause?.data?.errorName ?? "" if ( errorName === "SenderAddressResult" && cause?.data?.args && cause?.data?.args[0] ) { return cause.data?.args[0] as Address } } if (revertError instanceof ContractFunctionRevertedError) { const errorName = revertError.data?.errorName ?? "" if ( errorName === "SenderAddressResult" && revertError.data?.args && revertError.data?.args[0] ) { return revertError.data?.args[0] as Address } } if (revertError instanceof RpcRequestError) { const hexStringRegex = /0x[a-fA-F0-9]+/ // biome-ignore lint/suspicious/noExplicitAny: const match = (revertError as unknown as any).cause.data.match( hexStringRegex ) if (!match) { throw new Error( "Failed to parse revert bytes from RPC response" ) } const data: Hex = match[0] const error = decodeErrorResult({ abi: [ { inputs: [ { internalType: "address", name: "sender", type: "address" } ], name: "SenderAddressResult", type: "error" } ], data }) return error.args[0] as Address } if (revertError instanceof InvalidInputRpcError) { const { data: data_ } = ( e instanceof RawContractError ? e : e instanceof BaseError ? e.walk((err) => "data" in (err as Error)) || e.walk() : {} ) as RawContractError const data = typeof data_ === "string" ? data_ : data_?.data if (data === undefined) { throw new Error( "Failed to parse revert bytes from RPC response" ) } const error = decodeErrorResult({ abi: [ { inputs: [ { internalType: "address", name: "sender", type: "address" } ], name: "SenderAddressResult", type: "error" } ], data }) return error.args[0] as Address } if (revertError instanceof UnknownRpcError) { const parsedBody = JSON.parse( // biome-ignore lint/suspicious/noExplicitAny: (revertError as unknown as any).cause.body ) const revertData = parsedBody.error.data const hexStringRegex = /0x[a-fA-F0-9]+/ const match = revertData.match(hexStringRegex) if (!match) { throw new Error( "Failed to parse revert bytes from RPC response" ) } const data: Hex = match[0] const error = decodeErrorResult({ abi: [ { inputs: [ { internalType: "address", name: "sender", type: "address" } ], name: "SenderAddressResult", type: "error" } ], data }) return error.args[0] as Address } throw e } throw new InvalidEntryPointError({ entryPointAddress }) }