-
Notifications
You must be signed in to change notification settings - Fork 1
feat: add execution error decode command #604
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 17 commits
e34e5e4
78f922f
e4e37e3
5e9eb28
e0ee561
f08823d
5c38ff9
ce0d6b1
3c1d736
d498d3c
7bce725
17ebe46
8552fde
bdc1508
8fbde60
c7bbbfe
e2f81c7
607b6dc
42702ae
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "chainlink-deployments-framework": minor | ||
| --- | ||
|
|
||
| add error decode command |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -30,6 +30,8 @@ import ( | |||||
|
|
||||||
| const noRevertData = "(no revert data)" | ||||||
|
|
||||||
| type errorSelector [4]byte | ||||||
|
|
||||||
| type traceConfig struct { | ||||||
| DisableStorage bool `json:"disableStorage,omitempty"` | ||||||
| DisableMemory bool `json:"disableMemory,omitempty"` | ||||||
|
|
@@ -57,25 +59,25 @@ type ErrSig struct { | |||||
| TypeVer string | ||||||
| Name string | ||||||
| Inputs abi.Arguments | ||||||
| id [4]byte | ||||||
| id errorSelector | ||||||
| } | ||||||
|
|
||||||
| // ErrDecoder indexes custom-error selectors across many ABIs. | ||||||
| type ErrDecoder struct { | ||||||
| bySelector map[[4]byte][]ErrSig | ||||||
| bySelector map[errorSelector][]ErrSig | ||||||
| registry analyzer.EVMABIRegistry | ||||||
| } | ||||||
|
|
||||||
| // NewErrDecoder builds an index from EVM ABI registry. | ||||||
| func NewErrDecoder(registry analyzer.EVMABIRegistry) (*ErrDecoder, error) { | ||||||
| idx := make(map[[4]byte][]ErrSig) | ||||||
| idx := make(map[errorSelector][]ErrSig) | ||||||
| for tv, jsonABI := range registry.GetAllABIs() { | ||||||
| a, err := abi.JSON(strings.NewReader(jsonABI)) | ||||||
| if err != nil { | ||||||
| return nil, fmt.Errorf("parse ABI for %s: %w", tv, err) | ||||||
| } | ||||||
| for name, e := range a.Errors { | ||||||
| var key [4]byte | ||||||
| var key errorSelector | ||||||
| copy(key[:], e.ID[:4]) // selector is first 4 bytes of the keccak(sig) | ||||||
| idx[key] = append(idx[key], ErrSig{ | ||||||
| TypeVer: tv, | ||||||
|
|
@@ -181,7 +183,7 @@ func (d *ErrDecoder) decodeRecursive(revertData []byte, preferredABIJSON string) | |||||
| } | ||||||
|
|
||||||
| // --- B) Registry lookup | ||||||
| var key [4]byte | ||||||
| var key errorSelector | ||||||
| copy(key[:], sel) | ||||||
| cands, ok := d.bySelector[key] | ||||||
| if !ok { | ||||||
|
|
@@ -619,6 +621,160 @@ func prettyRevertFromError(err error, preferredABIJSON string, dec *ErrDecoder) | |||||
| return "", false | ||||||
| } | ||||||
|
|
||||||
| // DecodedExecutionError contains the decoded revert reasons from an ExecutionError. | ||||||
| type DecodedExecutionError struct { | ||||||
| RevertReason string | ||||||
| RevertReasonDecoded bool | ||||||
| UnderlyingReason string | ||||||
| UnderlyingReasonDecoded bool | ||||||
| } | ||||||
|
|
||||||
| // tryDecodeExecutionError decodes an evm.ExecutionError into human-readable strings. | ||||||
| // It first checks for RevertReasonDecoded and UnderlyingReasonDecoded fields. | ||||||
| // If those are not available, it extracts RevertReasonRaw and UnderlyingReasonRaw from the struct | ||||||
| // and decodes them using the provided ErrDecoder to match error selectors against the ABI registry. | ||||||
| func tryDecodeExecutionError(execError *evm.ExecutionError, dec *ErrDecoder) DecodedExecutionError { | ||||||
| if execError == nil { | ||||||
| return DecodedExecutionError{} | ||||||
| } | ||||||
|
|
||||||
| revertReason, revertDecoded := decodeRevertReasonWithStatus(execError, dec) | ||||||
| underlyingReason, underlyingDecoded := decodeUnderlyingReasonWithStatus(execError, dec) | ||||||
|
|
||||||
| return DecodedExecutionError{ | ||||||
| RevertReason: revertReason, | ||||||
| RevertReasonDecoded: revertDecoded, | ||||||
| UnderlyingReason: underlyingReason, | ||||||
| UnderlyingReasonDecoded: underlyingDecoded, | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| // decodeRevertReasonWithStatus decodes the revert reason and returns both the reason and decoded status. | ||||||
| func decodeRevertReasonWithStatus(execError *evm.ExecutionError, dec *ErrDecoder) (string, bool) { | ||||||
| if execError.RevertReasonDecoded != "" { | ||||||
| return execError.RevertReasonDecoded, true | ||||||
| } | ||||||
|
|
||||||
| if execError.RevertReasonRaw == nil { | ||||||
| return "", false | ||||||
| } | ||||||
|
|
||||||
| hasData := len(execError.RevertReasonRaw.Data) > 0 | ||||||
| hasSelector := execError.RevertReasonRaw.Selector != errorSelector{} | ||||||
|
|
||||||
| if hasData { | ||||||
| if reason, decoded := tryDecodeFromData(execError.RevertReasonRaw, dec); decoded { | ||||||
| return reason, true | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| if hasSelector && !hasData { | ||||||
|
||||||
| if hasSelector && !hasData { | |
| if hasSelector { |
Uh oh!
There was an error while loading. Please reload this page.