Skip to content

Commit

Permalink
feat: expose data on ExecutionRevertedError - fixes #3149
Browse files Browse the repository at this point in the history
  • Loading branch information
jxom committed Dec 22, 2024
1 parent 1ffb683 commit ca0e956
Show file tree
Hide file tree
Showing 7 changed files with 69 additions and 7 deletions.
5 changes: 5 additions & 0 deletions .changeset/chatty-gifts-itch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"viem": patch
---

**Account Abstraction:** Exposed `data` on `ExecutionRevertedError`.
11 changes: 10 additions & 1 deletion src/account-abstraction/errors/bundler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,17 @@ export class ExecutionRevertedError extends BaseError {
static code = -32521
static message = /execution reverted/

data?: { revertData?: Hex } | undefined

constructor({
cause,
data,
message,
}: { cause?: BaseError | undefined; message?: string | undefined } = {}) {
}: {
cause?: BaseError | undefined
data?: { revertData?: Hex } | undefined
message?: string | undefined
} = {}) {
const reason = message
?.replace('execution reverted: ', '')
?.replace('execution reverted', '')
Expand All @@ -48,6 +55,8 @@ export class ExecutionRevertedError extends BaseError {
name: 'ExecutionRevertedError',
},
)

this.data = data
}
}

Expand Down
3 changes: 3 additions & 0 deletions src/account-abstraction/utils/errors/getBundlerError.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ test('ExecutionReverted', () => {
body: {},
error: {
code: -32521,
data: {
revertData: '0xdeadbeef',
},
message: 'execution reverted: get good',
},
url: '',
Expand Down
3 changes: 2 additions & 1 deletion src/account-abstraction/utils/errors/getBundlerError.ts
Original file line number Diff line number Diff line change
Expand Up @@ -253,12 +253,13 @@ export function getBundlerError(

const error = err.walk((e) =>
bundlerErrors.some((error) => error.code === (e as { code: number }).code),
) as BaseError & { code: number }
) as BaseError & { code: number; data: any }

if (error) {
if (error.code === ExecutionRevertedError.code)
return new ExecutionRevertedError({
cause: err,
data: error.data,
message: error.details,
}) as any
if (error.code === InvalidFieldsError.code)
Expand Down
4 changes: 3 additions & 1 deletion src/errors/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,14 +76,15 @@ export type RpcRequestErrorType = RpcRequestError & {
}
export class RpcRequestError extends BaseError {
code: number
data?: unknown

constructor({
body,
error,
url,
}: {
body: { [x: string]: unknown } | { [y: string]: unknown }[]
error: { code: number; message: string }
error: { code: number; data?: unknown; message: string }
url: string
}) {
super('RPC Request failed.', {
Expand All @@ -93,6 +94,7 @@ export class RpcRequestError extends BaseError {
name: 'RpcRequestError',
})
this.code = error.code
this.data = error.data
}
}

Expand Down
36 changes: 36 additions & 0 deletions src/utils/errors/getContractError.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { accounts } from '~test/src/constants.js'
import { AbiDecodingZeroDataError } from '../../errors/abi.js'
import { BaseError } from '../../errors/base.js'
import { RawContractError } from '../../errors/contract.js'
import { RpcRequestError } from '../../errors/request.js'

import { getContractError } from './getContractError.js'

Expand Down Expand Up @@ -179,6 +180,41 @@ describe('getContractError', () => {
`)
})

test('rpc error', () => {
const error = getContractError(
new BaseError('An RPC error occurred', {
cause: new RpcRequestError({
body: {},
error: { code: 3, message: 'ah no' },
url: '',
}),
}),
{
abi: baycContractConfig.abi,
functionName: 'mintApe',
args: [1n],
sender: accounts[0].address,
},
)
expect(error).toMatchInlineSnapshot(`
[ContractFunctionExecutionError: The contract function "mintApe" reverted with the following reason:
ah no
Contract Call:
function: mintApe(uint256 numberOfTokens)
args: (1)
sender: 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266
Version: [email protected]]
`)
expect(error.cause).toMatchInlineSnapshot(`
[ContractFunctionRevertedError: The contract function "mintApe" reverted with the following reason:
ah no
Version: [email protected]]
`)
})

test('unknown function', () => {
const error = getContractError(
new BaseError('An RPC error occurred', {
Expand Down
14 changes: 10 additions & 4 deletions src/utils/errors/getContractError.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
type ContractFunctionZeroDataErrorType,
RawContractError,
} from '../../errors/contract.js'
import { RpcRequestError } from '../../errors/request.js'
import { InternalRpcError } from '../../errors/rpc.js'
import type { ErrorType } from '../../errors/utils.js'

Expand Down Expand Up @@ -44,26 +45,31 @@ export function getContractError<err extends ErrorType<string>>(
sender?: Address | undefined
},
): GetContractErrorReturnType {
const { code, data, message, shortMessage } = (
const error = (
err instanceof RawContractError
? err
: err instanceof BaseError
? err.walk((err) => 'data' in (err as Error)) || err.walk()
: {}
) as RawContractError
) as BaseError
const { code, data, details, message, shortMessage } =
error as RawContractError

const cause = (() => {
if (err instanceof AbiDecodingZeroDataError)
return new ContractFunctionZeroDataError({ functionName })
if (
[EXECUTION_REVERTED_ERROR_CODE, InternalRpcError.code].includes(code) &&
(data || message || shortMessage)
(data || details || message || shortMessage)
) {
return new ContractFunctionRevertedError({
abi,
data: typeof data === 'object' ? data.data : data,
functionName,
message: shortMessage ?? message,
message:
error instanceof RpcRequestError
? details
: (shortMessage ?? message),
})
}
return err
Expand Down

0 comments on commit ca0e956

Please sign in to comment.