UNPKG

viem

Version:

TypeScript Interface for Ethereum

148 lines (134 loc) 4.17 kB
import type { Abi, Address } from 'abitype' import { BaseError } from '../../../errors/base.js' import { ContractFunctionExecutionError, ContractFunctionRevertedError, ContractFunctionZeroDataError, } from '../../../errors/contract.js' import type { ErrorType } from '../../../errors/utils.js' import type { Call } from '../../../types/calls.js' import type { Hex } from '../../../types/misc.js' import { decodeErrorResult } from '../../../utils/abi/decodeErrorResult.js' import type { GetContractErrorReturnType } from '../../../utils/errors/getContractError.js' import { ExecutionRevertedError } from '../../errors/bundler.js' import { UserOperationExecutionError, type UserOperationExecutionErrorType, } from '../../errors/userOperation.js' import type { UserOperation } from '../../types/userOperation.js' import { type GetBundlerErrorParameters, getBundlerError, } from './getBundlerError.js' type GetNodeErrorReturnType = ErrorType export type GetUserOperationErrorParameters = UserOperation & { calls?: readonly unknown[] | undefined docsPath?: string | undefined } export type GetUserOperationErrorReturnType<cause = ErrorType> = Omit< UserOperationExecutionErrorType, 'cause' > & { cause: cause | GetNodeErrorReturnType } export type GetUserOperationErrorErrorType = ErrorType export function getUserOperationError<err extends ErrorType<string>>( err: err, { calls, docsPath, ...args }: GetUserOperationErrorParameters, ): GetUserOperationErrorReturnType<err> { const cause = (() => { const cause = getBundlerError( err as {} as BaseError, args as GetBundlerErrorParameters, ) if (calls && cause instanceof ExecutionRevertedError) { const revertData = getRevertData(cause) const contractCalls = calls?.filter( (call: any) => call.abi, ) as readonly Call[] if (revertData && contractCalls.length > 0) return getContractError({ calls: contractCalls, revertData }) } return cause })() return new UserOperationExecutionError(cause, { docsPath, ...args, }) as GetUserOperationErrorReturnType<err> } ///////////////////////////////////////////////////////////////////////////////// function getRevertData(error: BaseError) { let revertData: Hex | undefined error.walk((e) => { const error = e as any if ( typeof error.data === 'string' || typeof error.data?.revertData === 'string' || (!(error instanceof BaseError) && typeof error.message === 'string') ) { const match = ( error.data?.revertData || error.data || error.message ).match?.(/(0x[A-Za-z0-9]*)/) if (match) { revertData = match[1] return true } } return false }) return revertData } function getContractError(parameters: { calls: readonly Call[] revertData: Hex }) { const { calls, revertData } = parameters const { abi, functionName, args, to } = (() => { const contractCalls = calls?.filter((call) => Boolean(call.abi), ) as readonly Call[] if (contractCalls.length === 1) return contractCalls[0] const compatContractCalls = contractCalls.filter((call) => { try { return Boolean( decodeErrorResult({ abi: call.abi, data: revertData, }), ) } catch { return false } }) if (compatContractCalls.length === 1) return compatContractCalls[0] return { abi: [], functionName: contractCalls.reduce( (acc, call) => `${acc ? `${acc} | ` : ''}${call.functionName}`, '', ), args: undefined, to: undefined, } })() as { abi: Abi functionName: string args: unknown[] to: Address } const cause = (() => { if (revertData === '0x') return new ContractFunctionZeroDataError({ functionName }) return new ContractFunctionRevertedError({ abi, data: revertData, functionName, }) })() return new ContractFunctionExecutionError(cause as BaseError, { abi, args, contractAddress: to, functionName, }) as GetContractErrorReturnType }