Skip to content

feat: decode evm errors [skip-line-limit]#1427

Merged
ctrlc03 merged 4 commits into
mainfrom
feat/evm-errors
Mar 16, 2026
Merged

feat: decode evm errors [skip-line-limit]#1427
ctrlc03 merged 4 commits into
mainfrom
feat/evm-errors

Conversation

@ctrlc03

@ctrlc03 ctrlc03 commented Mar 16, 2026

Copy link
Copy Markdown
Collaborator

fix #148

Summary by CodeRabbit

  • New Features

    • Added EVM error decoding with human-readable error names and exposed retry variant that leverages decoded errors; added new contract artifact.
  • Bug Fixes

    • Improved error formatting and logs to show decoded EVM errors across on-chain interactions and retry flows.
  • Chores

    • Updated deployment data/templates, switched test readiness to JSON‑RPC checks, removed a proactive EVM subscription staleness check, and added CI tooling step.

@ctrlc03 ctrlc03 requested review from 0xjei, cedoor and hmzakhalid March 16, 2026 09:28
@vercel

vercel Bot commented Mar 16, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
crisp Ready Ready Preview, Comment Mar 16, 2026 2:29pm
enclave-docs Ready Ready Preview, Comment Mar 16, 2026 2:29pm

Request Review

@coderabbitai

coderabbitai Bot commented Mar 16, 2026

Copy link
Copy Markdown
Contributor

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds an EVM error decoder module and threads decoded error names into retry logic and logs; replaces hard-coded revert codes with symbolic names; removes subscription staleness checks; updates deployment artifacts, templates, CI, and integration scripts.

Changes

Cohort / File(s) Summary
Error Decoder Core
crates/evm/src/error_decoder.rs, crates/evm/src/lib.rs
New public module implementing ABI-based decoding for Enclave, CiphernodeRegistryOwnable, and SlashingManager; exports decode_error, decode_error_from_str, and format_evm_error.
Retry Helpers
crates/evm-helpers/src/retry.rs
Added call_with_retry_and_decoder accepting an error decoder; call_with_retry delegates to it; retry decisions and logs now consider decoded error names.
EVM Helpers Integration
crates/evm/src/helpers.rs
should_retry_error signature now accepts decoded_error: Option<&str>; send_tx_with_retry decodes errors, uses pretty-printed/display_error, and uses decoded content for retry logic and logging.
Writers: logging & symbolic codes
crates/evm/src/ciphernode_registry_sol.rs, crates/evm/src/enclave_sol_writer.rs, crates/evm/src/slashing_manager_sol_writer.rs
Replaced raw {:?} error prints with format_evm_error(&err) and substituted hard-coded hex revert codes with descriptive symbolic names in logs and RPC args.
Subscription behavior
crates/evm/src/evm_read_interface.rs
Removed subscription staleness timer and related reconnection logic; live stream relies on stream progression/shutdown for reconnection.
Deployment & artifacts
packages/enclave-contracts/deployed_contracts.json, packages/enclave-contracts/artifacts/.../CiphernodeRegistryOwnable.json, packages/enclave-contracts/.gitignore
Updated deployed addresses/blockNumbers and proxy records; added full CiphernodeRegistryOwnable artifact and adjusted .gitignore to keep specific artifact JSONs.
Templates & tests
templates/default/deployed_contracts.json, templates/default/enclave.config.yaml, templates/default/tests/integration.spec.ts
Adjusted deploy block numbers and reduced integration test duration (900 → 700).
Integration scripts
tests/integration/base.sh, tests/integration/persist.sh, tests/integration/fns.sh, tests/integration/enclave.config.yaml
Readiness checks switched from plain HTTP to JSON-RPC eth_blockNumber POST; anvil launch simplified; chain name/addresses updated for integration tests.
CI / Tooling
.github/workflows/ci.yml
Added a Foundry install step to the CI workflow.
Minor
(various small edits)
Formatting, logging, unit tests for decoder, and small refactors to integrate the decoder across codebase.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant RetryHandler as Retry Handler
    participant Decoder as Error Decoder
    participant Contract
    participant Logger

    Client->>RetryHandler: call_with_retry_and_decoder(operation)
    RetryHandler->>Contract: submit transaction
    Contract-->>RetryHandler: Err (raw revert / error string)
    RetryHandler->>Decoder: decode_error_from_str(raw_err)
    Decoder-->>RetryHandler: decoded_error (optional)
    RetryHandler->>RetryHandler: should_retry_error(raw_err, decoded_error, retry_on_errors)
    alt Retry condition met
        RetryHandler->>Logger: Log retry attempt (uses display_error)
        RetryHandler->>Contract: retry transaction
    else Max retries exceeded
        RetryHandler->>Logger: Log final failure (uses display_error)
        RetryHandler-->>Client: return error
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

sdk

Suggested reviewers

  • cedoor
  • hmzakhalid
  • ryardley

Poem

🐇 I nibble hex and scent the stray,

Bytes unspool, names hop out to play,
Retries count carrots, logs glow bright,
Decoded echoes lead the night,
Hop on—errors told, the code sleeps light.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The PR includes out-of-scope changes to test infrastructure and deployment configuration files that are unrelated to EVM error decoding, such as integration test timeout adjustments, EVM node launch mechanism changes, and deployed contract block number updates. Consider separating test infrastructure and deployment configuration changes from the core EVM error decoding feature into a separate PR to maintain focused, reviewable changesets.
Docstring Coverage ⚠️ Warning Docstring coverage is 71.05% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: decode evm errors' clearly and concisely describes the main feature added in the changeset: EVM error decoding functionality.
Linked Issues check ✅ Passed The PR successfully implements EVM error decoding as required by issue #148, introducing error_decoder module with decode functions and integrating decoded errors throughout the codebase for human-readable logging.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/evm-errors
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Tip

CodeRabbit can approve the review once all CodeRabbit's comments are resolved.

Enable the reviews.request_changes_workflow setting to automatically approve the review once all CodeRabbit's comments are resolved.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@crates/evm-helpers/src/error_decoder.rs`:
- Around line 49-52: The current decode_error_from_str implementation only picks
the longest 0x... blob via extract_revert_data and thus can miss short
selector-only custom errors when a longer unrelated hex (tx
hash/calldata/address) is present; change the logic in decode_error_from_str to
scan all hex blobs extracted from the input (use or extend extract_revert_data
to return all 0x tokens), try decode_error on each blob (short-to-long or try
all) and return the first successful decoding from decode_error, falling back to
None if none decode; update the analogous logic in the related function(s)
around lines 61-78 to the same approach, and add a regression test that feeds a
string containing both a short revert selector (e.g. CommitteeNotRequested
selector) and a longer unrelated hex token to ensure the short selector is
decoded correctly.

In `@crates/evm-helpers/src/retry.rs`:
- Around line 45-47: The code currently uses format!("{}", e) which loses the
anyhow source chain; change the construction of error_str in retry.rs (where
error_str, decode_error_from_str, and display_error are used) to preserve the
full source chain by formatting the error with a multi-line debug/pretty
representation (e.g., use format!("{e:#}") or equivalent) so that
decode_error_from_str can see inner revert payloads; update only the error_str
assignment so decoded.as_deref().unwrap_or(&error_str) continues to work.

In `@crates/evm/src/enclave_sol_writer.rs`:
- Around line 225-228: The retry predicate passed to send_tx_with_retry
currently only matches the decoded revert string "CiphertextOutputNotPublished",
which is brittle if decoding fails or the message text changes; update the retry
logic used in the closure given to send_tx_with_retry (the call site around
send_tx_with_retry and the string "CiphertextOutputNotPublished") to also detect
the raw revert selector or numeric error code fallback (e.g., match the revert
selector bytes/prefix or known error id) and consider unknown/undecodable
reverts that match that selector as retryable so retries still occur when
decoding fails or message text differs.

In `@tests/integration/persist.sh`:
- Line 16: The readiness loop in tests/integration/persist.sh uses curl
targeting 0.0.0.0 which is a bind address and can hang on some runners; update
the curl POST command inside the until loop (the line starting with "until curl
-sf -X POST ...") to use localhost instead of 0.0.0.0 so the readiness check
reliably connects like tests/integration/base.sh.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 7915be2d-6fa0-468d-9f79-3c4300c2d3ff

📥 Commits

Reviewing files that changed from the base of the PR and between af8c542 and da472ae.

⛔ Files ignored due to path filters (2)
  • Cargo.lock is excluded by !**/*.lock
  • examples/CRISP/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (22)
  • crates/evm-helpers/Cargo.toml
  • crates/evm-helpers/src/error_decoder.rs
  • crates/evm-helpers/src/lib.rs
  • crates/evm-helpers/src/retry.rs
  • crates/evm/Cargo.toml
  • crates/evm/src/ciphernode_registry_sol.rs
  • crates/evm/src/enclave_sol_writer.rs
  • crates/evm/src/error_decoder.rs
  • crates/evm/src/evm_read_interface.rs
  • crates/evm/src/helpers.rs
  • crates/evm/src/lib.rs
  • crates/evm/src/slashing_manager_sol_writer.rs
  • examples/CRISP/server/src/cli/commands.rs
  • examples/CRISP/server/src/server/indexer.rs
  • packages/enclave-contracts/deployed_contracts.json
  • templates/default/deployed_contracts.json
  • templates/default/enclave.config.yaml
  • templates/default/tests/integration.spec.ts
  • tests/integration/base.sh
  • tests/integration/enclave.config.yaml
  • tests/integration/fns.sh
  • tests/integration/persist.sh
💤 Files with no reviewable changes (1)
  • crates/evm/src/evm_read_interface.rs

Comment thread crates/evm-helpers/src/error_decoder.rs Outdated
Comment thread crates/evm-helpers/src/retry.rs Outdated
Comment thread crates/evm/src/enclave_sol_writer.rs
Comment thread tests/integration/persist.sh Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
crates/evm-helpers/src/retry.rs (1)

14-21: Consider extracting shared retry logic.

The should_retry_error function is duplicated between helpers.rs (crates/evm) and this file. While acceptable given the crate boundary, if more shared retry logic emerges, consider extracting to a common location.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/evm-helpers/src/retry.rs` around lines 14 - 21, The should_retry_error
function is duplicated between retry.rs and helpers.rs; extract this shared
retry logic into a single reusable location (e.g., a new module or crate-level
utility) and have both retry.rs and helpers.rs call that centralized function;
specifically, move the implementation of should_retry_error into a shared module
(e.g., evm_utils::retry or a common crate) and replace the local definitions in
retry.rs and helpers.rs with calls to that shared function to avoid duplication
and keep behavior consistent.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@crates/evm-helpers/src/retry.rs`:
- Around line 14-21: The should_retry_error function is duplicated between
retry.rs and helpers.rs; extract this shared retry logic into a single reusable
location (e.g., a new module or crate-level utility) and have both retry.rs and
helpers.rs call that centralized function; specifically, move the implementation
of should_retry_error into a shared module (e.g., evm_utils::retry or a common
crate) and replace the local definitions in retry.rs and helpers.rs with calls
to that shared function to avoid duplication and keep behavior consistent.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2de45838-2c70-4842-ae4c-b6049c23b803

📥 Commits

Reviewing files that changed from the base of the PR and between da472ae and 8388d3b.

📒 Files selected for processing (4)
  • crates/evm-helpers/src/retry.rs
  • crates/evm/src/error_decoder.rs
  • crates/evm/src/helpers.rs
  • tests/integration/persist.sh

@vercel vercel Bot temporarily deployed to Preview – enclave-docs March 16, 2026 10:01 Inactive
@vercel vercel Bot temporarily deployed to Preview – crisp March 16, 2026 10:01 Inactive
@ctrlc03 ctrlc03 changed the title feat: decode evm errors feat: decode evm errors [skip-line-length] Mar 16, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/enclave-contracts/.gitignore`:
- Around line 14-17: Remove the trailing whitespace at the end of the artifacts
un-ignore line for Enclave.json (the line containing
"!/artifacts/contracts/Enclave.sol/Enclave.json") so it exactly matches the
other un-ignored artifact lines; optionally, for consistency with other
sections, add an explicit un-ignore of the ".sol/" directory (e.g.,
"!/artifacts/contracts/Enclave.sol/") before the Enclave.json entry to mirror
the pattern used for other contracts like CiphernodeRegistryOwnable.json.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: bf611da9-7386-4060-a231-55bafa5f1551

📥 Commits

Reviewing files that changed from the base of the PR and between 8388d3b and 5a6aeed.

📒 Files selected for processing (3)
  • packages/enclave-contracts/.gitignore
  • packages/enclave-contracts/artifacts/contracts/Enclave.sol/Enclave.json
  • packages/enclave-contracts/artifacts/contracts/registry/CiphernodeRegistryOwnable.sol/CiphernodeRegistryOwnable.json
✅ Files skipped from review due to trivial changes (1)
  • packages/enclave-contracts/artifacts/contracts/registry/CiphernodeRegistryOwnable.sol/CiphernodeRegistryOwnable.json

Comment thread packages/enclave-contracts/.gitignore
@vercel vercel Bot temporarily deployed to Preview – enclave-docs March 16, 2026 10:13 Inactive
@vercel vercel Bot temporarily deployed to Preview – crisp March 16, 2026 10:13 Inactive
@ctrlc03 ctrlc03 changed the title feat: decode evm errors [skip-line-length] feat: decode evm errors [skip-line-limit] Mar 16, 2026

@0xjei 0xjei left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

utACK

@ctrlc03 ctrlc03 enabled auto-merge (squash) March 16, 2026 14:27

@cedoor cedoor left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

utACK

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
crates/evm-helpers/src/retry.rs (1)

58-66: Verify the borrow of decode_error across async boundary.

The reference decode_fn = &decode_error is taken outside the async move block and used inside it. This works because:

  1. decode_error is owned by call_with_retry_and_decoder's stack frame
  2. The reference is Copy, so it can be moved into the async block
  3. retry_with_backoff awaits completion before returning, so decode_error outlives all usages

The pattern is sound, but the comment on line 58 could be clearer about the actual reason (avoiding the closure being consumed on each retry iteration since D: Fn, not FnOnce).

📝 Suggested comment improvement
-            // Decode before entering the async block to avoid moving the closure
+            // Borrow the decoder here; the reference is Copy and can move into the async block
+            // without consuming the closure, allowing it to be reused across retry iterations
             let decode_fn = &decode_error;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/evm-helpers/src/retry.rs` around lines 58 - 66, Update the comment
describing why we take a reference to decode_error before the async block to
clearly state that we're preventing the closure from being moved/consumed on
each retry because D implements Fn (not FnOnce), rather than implying lifetime
juggling across the async boundary; mention the specific symbols decode_error
and decode_fn and that this keeps the decoder callable across retry iterations
inside call_with_retry_and_decoder/retry_with_backoff without cloning or
consuming the closure.
crates/evm/src/error_decoder.rs (1)

60-65: Consider using {:#} for consistency with retry logic.

format_evm_error uses {err:?} (Debug format) while helpers.rs and retry.rs use {e:#} (alternate Display). Both work, but {:?} produces a more verbose "Caused by:" chain format, whereas {:#} produces a colon-separated chain on a single line.

If this function is used in log output alongside the retry logic, the formatting inconsistency could be visually jarring. This is a minor stylistic concern.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/evm/src/error_decoder.rs` around lines 60 - 65, The function
format_evm_error currently formats the anyhow error using the Debug formatter
"{err:?}", causing a verbose multi-line "Caused by:" chain; change it to use the
alternate Display formatting (e.g. format!("{err:#}") or "{:#}") so it matches
the style used in helpers.rs and retry.rs and produces a single-line
colon-separated chain, then pass that string into decode_error_from_str as
before (function: format_evm_error; helper: decode_error_from_str).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/enclave-contracts/deployed_contracts.json`:
- Around line 257-304: Remove the stale/orphaned verifier entries from
deployed_contracts.json: delete the objects for DkgESmShareComputationVerifier,
DkgShareDecryptionVerifier, DkgShareEncryptionVerifier,
DkgSkShareComputationVerifier, ThresholdPkGenerationVerifier, and
ThresholdShareDecryptionVerifier; ensure the JSON remains syntactically valid
after removal (no trailing commas) and that remaining verifier entries (e.g.,
RecursiveAggregationFoldVerifier, ThresholdDecryptedSharesAggregationBnVerifier,
ThresholdDecryptedSharesAggregationModVerifier, ThresholdPkAggregationVerifier,
ThresholdUserDataEncryptionCt0Verifier, ThresholdUserDataEncryptionCt1Verifier)
are untouched so the deployment script can continue to discover actual contracts
dynamically.

---

Nitpick comments:
In `@crates/evm-helpers/src/retry.rs`:
- Around line 58-66: Update the comment describing why we take a reference to
decode_error before the async block to clearly state that we're preventing the
closure from being moved/consumed on each retry because D implements Fn (not
FnOnce), rather than implying lifetime juggling across the async boundary;
mention the specific symbols decode_error and decode_fn and that this keeps the
decoder callable across retry iterations inside
call_with_retry_and_decoder/retry_with_backoff without cloning or consuming the
closure.

In `@crates/evm/src/error_decoder.rs`:
- Around line 60-65: The function format_evm_error currently formats the anyhow
error using the Debug formatter "{err:?}", causing a verbose multi-line "Caused
by:" chain; change it to use the alternate Display formatting (e.g.
format!("{err:#}") or "{:#}") so it matches the style used in helpers.rs and
retry.rs and produces a single-line colon-separated chain, then pass that string
into decode_error_from_str as before (function: format_evm_error; helper:
decode_error_from_str).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 5101bf86-a2da-4f80-84de-dcabfbd75002

📥 Commits

Reviewing files that changed from the base of the PR and between 1378b97 and cb71d6d.

📒 Files selected for processing (20)
  • .github/workflows/ci.yml
  • crates/evm-helpers/src/retry.rs
  • crates/evm/src/ciphernode_registry_sol.rs
  • crates/evm/src/enclave_sol_writer.rs
  • crates/evm/src/error_decoder.rs
  • crates/evm/src/evm_read_interface.rs
  • crates/evm/src/helpers.rs
  • crates/evm/src/lib.rs
  • crates/evm/src/slashing_manager_sol_writer.rs
  • packages/enclave-contracts/.gitignore
  • packages/enclave-contracts/artifacts/contracts/Enclave.sol/Enclave.json
  • packages/enclave-contracts/artifacts/contracts/registry/CiphernodeRegistryOwnable.sol/CiphernodeRegistryOwnable.json
  • packages/enclave-contracts/deployed_contracts.json
  • templates/default/deployed_contracts.json
  • templates/default/enclave.config.yaml
  • templates/default/tests/integration.spec.ts
  • tests/integration/base.sh
  • tests/integration/enclave.config.yaml
  • tests/integration/fns.sh
  • tests/integration/persist.sh
💤 Files with no reviewable changes (1)
  • crates/evm/src/evm_read_interface.rs
🚧 Files skipped from review as they are similar to previous changes (6)
  • crates/evm/src/ciphernode_registry_sol.rs
  • templates/default/tests/integration.spec.ts
  • .github/workflows/ci.yml
  • tests/integration/fns.sh
  • templates/default/deployed_contracts.json
  • tests/integration/persist.sh

Comment thread packages/enclave-contracts/deployed_contracts.json
@ctrlc03 ctrlc03 merged commit 662cb0a into main Mar 16, 2026
27 checks passed
@github-actions github-actions Bot deleted the feat/evm-errors branch March 24, 2026 03:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Decode EVM errors before logging them

3 participants