Skip to content

Commit

Permalink
Add raw revert data to ContractFunctionRevertedError
Browse files Browse the repository at this point in the history
Exposes otherwise missing raw revert data via a new rawData field
on the ContractFunctionRevertedError

Fixes wevm#3235
  • Loading branch information
will-af committed Jan 22, 2025
1 parent 5fb0e52 commit 5aa5d8b
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 43 deletions.
5 changes: 5 additions & 0 deletions .changeset/two-crabs-push.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"viem": minor
---

Added raw revert data field to `ContractFunctionRevertedError` so that raw revert data is always programmatically accessible if provided, regardless of whether the error was successfully decoded via the provided ABI.
103 changes: 60 additions & 43 deletions src/errors/contract.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -317,98 +317,115 @@ describe('ContractFunctionExecutionError', () => {

describe('ContractFunctionRevertedError', () => {
test('default', () => {
expect(
new ContractFunctionRevertedError({
abi: ErrorsExample.abi,
message: 'oh no',
functionName: 'totalSupply',
}),
).toMatchInlineSnapshot(`
const err = new ContractFunctionRevertedError({
abi: ErrorsExample.abi,
message: 'oh no',
functionName: 'totalSupply',
})

expect(err).toMatchInlineSnapshot(`
[ContractFunctionRevertedError: The contract function "totalSupply" reverted with the following reason:
oh no
Version: viem@x.y.z]
`)

expect(err.rawData).be.undefined
})

test('data: Error(string)', () => {
expect(
new ContractFunctionRevertedError({
abi: ErrorsExample.abi,
data: '0x08c379a000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000022456e756d657261626c655365743a20696e646578206f7574206f6620626f756e6473000000000000000000000000000000000000000000000000000000000000',
functionName: 'totalSupply',
}),
).toMatchInlineSnapshot(`
const data =
'0x08c379a000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000022456e756d657261626c655365743a20696e646578206f7574206f6620626f756e6473000000000000000000000000000000000000000000000000000000000000'
const err = new ContractFunctionRevertedError({
abi: ErrorsExample.abi,
data,
functionName: 'totalSupply',
})
expect(err).toMatchInlineSnapshot(`
[ContractFunctionRevertedError: The contract function "totalSupply" reverted with the following reason:
EnumerableSet: index out of bounds
Version: viem@x.y.z]
`)

expect(err.rawData).toEqual(data)
})

test('data: Panic(uint256)', () => {
expect(
new ContractFunctionRevertedError({
abi: ErrorsExample.abi,
data: '0x4e487b710000000000000000000000000000000000000000000000000000000000000001',
functionName: 'totalSupply',
}),
).toMatchInlineSnapshot(`
const data =
'0x4e487b710000000000000000000000000000000000000000000000000000000000000001'
const err = new ContractFunctionRevertedError({
abi: ErrorsExample.abi,
data,
functionName: 'totalSupply',
})
expect(err).toMatchInlineSnapshot(`
[ContractFunctionRevertedError: The contract function "totalSupply" reverted with the following reason:
An \`assert\` condition failed.
Version: viem@x.y.z]
`)

expect(err.rawData).toEqual(data)
})

test('data: custom error', () => {
expect(
new ContractFunctionRevertedError({
abi: ErrorsExample.abi,
data: '0xdb731cf4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000450000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000004500000000000000000000000000000000000000000000000000000000000000066275676765720000000000000000000000000000000000000000000000000000',
functionName: 'customComplexError',
}),
).toMatchInlineSnapshot(`
const data =
'0xdb731cf4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000450000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000004500000000000000000000000000000000000000000000000000000000000000066275676765720000000000000000000000000000000000000000000000000000'
const err = new ContractFunctionRevertedError({
abi: ErrorsExample.abi,
data,
functionName: 'customComplexError',
})
expect(err).toMatchInlineSnapshot(`
[ContractFunctionRevertedError: The contract function "customComplexError" reverted.
Error: ComplexError((address sender, uint256 bar), string message, uint256 number)
({"sender":"0x0000000000000000000000000000000000000000","bar":"69"}, bugger, 69)
Version: viem@x.y.z]
`)

expect(err.rawData).toEqual(data)
})

test('data: zero data', () => {
expect(
new ContractFunctionRevertedError({
abi: ErrorsExample.abi,
data: '0x',
functionName: 'customComplexError',
}),
).toMatchInlineSnapshot(`
const data = '0x'
const err = new ContractFunctionRevertedError({
abi: ErrorsExample.abi,
data,
functionName: 'customComplexError',
})
expect(err).toMatchInlineSnapshot(`
[ContractFunctionRevertedError: The contract function "customComplexError" reverted.
Version: viem@x.y.z]
`)

expect(err.rawData).toEqual(data)
})

test('data: error signature does not exist on ABI', () => {
expect(
new ContractFunctionRevertedError({
abi: ErrorsExample.abi,
data: '0xdb731cfa000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000450000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000004500000000000000000000000000000000000000000000000000000000000000066275676765720000000000000000000000000000000000000000000000000000',
functionName: 'totalSupply',
}),
).toMatchInlineSnapshot(`
const data =
'0xdb731cfa000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000450000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000004500000000000000000000000000000000000000000000000000000000000000066275676765720000000000000000000000000000000000000000000000000000'
const err = new ContractFunctionRevertedError({
abi: ErrorsExample.abi,
data,
functionName: 'totalSupply',
})
expect(err).toMatchInlineSnapshot(`
[ContractFunctionRevertedError: The contract function "totalSupply" reverted with the following signature:
0xdb731cfa
Unable to decode signature "0xdb731cfa" as it was not found on the provided ABI.
Make sure you are using the correct ABI and that the error exists on it.
You can look up the decoded signature here: https://openchain.xyz/signatures?query=0xdb731cfa.
Raw revert data: "${data}"
Docs: https://viem.sh/docs/contract/decodeErrorResult
Version: viem@x.y.z]
`)

expect(err.rawData).toEqual(data)
})
})
3 changes: 3 additions & 0 deletions src/errors/contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ export type ContractFunctionRevertedErrorType =
}
export class ContractFunctionRevertedError extends BaseError {
data?: DecodeErrorResultReturnType | undefined
rawData?: Hex | undefined
reason?: string | undefined
signature?: Hex | undefined

Expand Down Expand Up @@ -232,6 +233,7 @@ export class ContractFunctionRevertedError extends BaseError {
`Unable to decode signature "${signature}" as it was not found on the provided ABI.`,
'Make sure you are using the correct ABI and that the error exists on it.',
`You can look up the decoded signature here: https://openchain.xyz/signatures?query=${signature}.`,
`Raw revert data: "${data}"`,
]
}

Expand All @@ -251,6 +253,7 @@ export class ContractFunctionRevertedError extends BaseError {
},
)

this.rawData = data
this.data = decodedData
this.reason = reason
this.signature = signature
Expand Down

0 comments on commit 5aa5d8b

Please sign in to comment.