diff --git a/.changeset/rotten-goats-draw.md b/.changeset/rotten-goats-draw.md new file mode 100644 index 00000000..6b59db12 --- /dev/null +++ b/.changeset/rotten-goats-draw.md @@ -0,0 +1,5 @@ +--- +"permissionless": patch +--- + +Improved `getSenderAddress` to avoid relying on EntryPoint reverts with RPC. diff --git a/packages/permissionless/actions/public/getSenderAddress.test.ts b/packages/permissionless/actions/public/getSenderAddress.test.ts index 762aa082..956b9671 100644 --- a/packages/permissionless/actions/public/getSenderAddress.test.ts +++ b/packages/permissionless/actions/public/getSenderAddress.test.ts @@ -78,7 +78,7 @@ describe("getSenderAddress", () => { entryPointAddress: "0x0000000000000000000000000000000000000000", initCode: concatHex([factory, factoryData]) }) - ).rejects.toThrowError(/not a valid entry point/) + ).rejects.toThrowError() }) testWithRpc("getSenderAddress_V07", async ({ rpc }) => { const { anvilRpc, altoRpc } = rpc diff --git a/packages/permissionless/actions/public/getSenderAddress.ts b/packages/permissionless/actions/public/getSenderAddress.ts index d5d1c881..1bdb9ee3 100644 --- a/packages/permissionless/actions/public/getSenderAddress.ts +++ b/packages/permissionless/actions/public/getSenderAddress.ts @@ -2,22 +2,40 @@ import { type Address, BaseError, type Client, - type ContractFunctionExecutionErrorType, - ContractFunctionRevertedError, type Hex, - InvalidInputRpcError, type OneOf, type Prettify, - RawContractError, - RpcRequestError, - UnknownRpcError, concat, - decodeErrorResult + encodeDeployData, + getAddress } from "viem" -import { simulateContract } from "viem/actions" +import { call } from "viem/actions" import { getAction } from "viem/utils" +// https://github.com/pimlicolabs/entrypoint-estimations/blob/main/src/GetSenderAddressHelper.sol +const GetSenderAddressHelperByteCode = + "0x608060405260405161058a38038061058a83398181016040528101906100259190610341565b5f808373ffffffffffffffffffffffffffffffffffffffff16639b249f6960e01b8460405160240161005791906103ed565b604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040516100c19190610447565b5f604051808303815f865af19150503d805f81146100fa576040519150601f19603f3d011682016040523d82523d5f602084013e6100ff565b606091505b50915091505f8261015f576004825111156101245760248201519050805f526014600cf35b6040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610156906104dd565b60405180910390fd5b6040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101919061056b565b60405180910390fd5b5f604051905090565b5f80fd5b5f80fd5b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6101d4826101ab565b9050919050565b6101e4816101ca565b81146101ee575f80fd5b50565b5f815190506101ff816101db565b92915050565b5f80fd5b5f80fd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6102538261020d565b810181811067ffffffffffffffff821117156102725761027161021d565b5b80604052505050565b5f61028461019a565b9050610290828261024a565b919050565b5f67ffffffffffffffff8211156102af576102ae61021d565b5b6102b88261020d565b9050602081019050919050565b8281835e5f83830152505050565b5f6102e56102e084610295565b61027b565b90508281526020810184848401111561030157610300610209565b5b61030c8482856102c5565b509392505050565b5f82601f83011261032857610327610205565b5b81516103388482602086016102d3565b91505092915050565b5f8060408385031215610357576103566101a3565b5b5f610364858286016101f1565b925050602083015167ffffffffffffffff811115610385576103846101a7565b5b61039185828601610314565b9150509250929050565b5f81519050919050565b5f82825260208201905092915050565b5f6103bf8261039b565b6103c981856103a5565b93506103d98185602086016102c5565b6103e28161020d565b840191505092915050565b5f6020820190508181035f83015261040581846103b5565b905092915050565b5f81905092915050565b5f6104218261039b565b61042b818561040d565b935061043b8185602086016102c5565b80840191505092915050565b5f6104528284610417565b915081905092915050565b5f82825260208201905092915050565b7f67657453656e64657241646472657373206661696c656420776974686f7574205f8201527f6461746100000000000000000000000000000000000000000000000000000000602082015250565b5f6104c760248361045d565b91506104d28261046d565b604082019050919050565b5f6020820190508181035f8301526104f4816104bb565b9050919050565b7f67657453656e6465724164647265737320646964206e6f7420726576657274205f8201527f6173206578706563746564000000000000000000000000000000000000000000602082015250565b5f610555602b8361045d565b9150610560826104fb565b604082019050919050565b5f6020820190508181035f83015261058281610549565b905091905056fe" + +const GetSenderAddressHelperAbi = [ + { + inputs: [ + { + internalType: "address", + name: "_entryPoint", + type: "address" + }, + { + internalType: "bytes", + name: "initCode", + type: "bytes" + } + ], + stateMutability: "payable", + type: "constructor" + } +] + export type GetSenderAddressParams = OneOf< | { initCode: Hex @@ -88,186 +106,24 @@ export const getSenderAddress = async ( ) } - 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])] + const formattedInitCode = + initCode || concat([factory as Hex, factoryData as Hex]) + + const { data } = await getAction( + client, + call, + "call" + )({ + data: encodeDeployData({ + abi: GetSenderAddressHelperAbi, + bytecode: GetSenderAddressHelperByteCode, + args: [entryPointAddress, formattedInitCode] }) - } catch (e) { - const revertError = (e as ContractFunctionExecutionErrorType).walk( - (err) => - err instanceof ContractFunctionRevertedError || - err instanceof RpcRequestError || - err instanceof InvalidInputRpcError || - err instanceof UnknownRpcError - ) - - if (!revertError) { - 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 + if (!data) { + throw new Error("Failed to get sender address") } - throw new InvalidEntryPointError({ entryPointAddress }) + return getAddress(data) }