Skip to content

feat: Implement Forest.EthTraceCall#6412

Open
akaladarshi wants to merge 20 commits intomainfrom
akaladarshi/eth-trace-call
Open

feat: Implement Forest.EthTraceCall#6412
akaladarshi wants to merge 20 commits intomainfrom
akaladarshi/eth-trace-call

Conversation

@akaladarshi
Copy link
Collaborator

@akaladarshi akaladarshi commented Jan 13, 2026

Summary of changes

Changes introduced in this pull request:

Reference issue to close (if applicable)

Closes #6185

Other information and links

Change checklist

  • I have performed a self-review of my own code,
  • I have made corresponding changes to the documentation. All new code adheres to the team's documentation standards,
  • I have added tests that prove my fix is effective or that my feature works (if possible),
  • I have made sure the CHANGELOG is up-to-date. All user-facing changes should be reflected in this document.

Summary by CodeRabbit

  • New Features

    • Added trace_call RPC with optional state-diff output and richer trace formats.
  • Documentation

    • Added comprehensive user and developer guides for trace_call usage, examples, and troubleshooting.
  • Tests

    • Added integration test harness and on-chain Tracer contract to validate traces, state diffs, balances, and storage scenarios.
  • Chores

    • Added new dependency to support storage diffing and tracing functionality.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 13, 2026

Walkthrough

Implements a new Eth trace_call RPC with trace and optional state-diff results, storage/account diffing via KAMT, VM/state_manager plumbing to optionally flush state roots, new types and tests, a Tracer contract and integration script, and user/developer documentation.

Changes

Cohort / File(s) Summary
RPC surface & types
src/rpc/mod.rs, src/rpc/methods/eth.rs, src/rpc/methods/eth/types.rs
Adds EthTraceCall, EthTraceType, EthTraceResults; new StateDiff/AccountDiff/Delta types and serialization; registers RPC method and adjusts parameter types.
Trace & state-diff logic
src/rpc/methods/eth/trace.rs
Implements state-diff construction (account/code/balance/nonce/storage deltas), EVM storage KAMT extraction, helpers to build AccountDiff/StateDiff, and integrates diffing into trace path.
State manager / VM plumbing
src/state_manager/mod.rs, src/rpc/methods/gas.rs
call_with_gas / apply_on_state_with_gas gain a flush: bool param and return optional state CID; callers updated to accept extra return values.
EVM actor accessors
src/shim/actors/builtin/evm/mod.rs
Adds bytecode(), bytecode_hash(), and contract_state() accessors on EVM actor State.
KAMT dependency
Cargo.toml
Adds dependency fvm_ipld_kamt = "0.4.5" for KAMT-based EVM storage handling.
Tests, tooling & contracts
scripts/tests/trace_call_integration_test.sh, src/tool/subcommands/api_cmd/contracts/tracer/Tracer.sol, src/tool/subcommands/api_cmd/..., src/tool/subcommands/api_cmd/stateful_tests.rs
Adds a Bash integration harness comparing Forest vs Anvil, a comprehensive Tracer.sol to exercise traces/state-diffs, and updates test call sites to use move/copy semantics (clone removals).
Types, serialization & tests
src/rpc/methods/eth/types.rs, tests in RPC modules
Introduces Delta/ChangedType/AccountDiff/StateDiff types, serialization, and unit tests for delta/state-diff behavior.
Call-site ownership / clone cleanup
src/rpc/methods/eth/filter/mod.rs, src/tool/subcommands/api_cmd/api_compare_tests.rs, src/tool/subcommands/api_cmd/...
Replaces many .clone() usages with moved/copied values for EthHash/EthAddress to reduce allocations and align ownership.
Docs & dictionary
docs/docs/developers/guides/trace_call_guide.md, docs/docs/users/knowledge_base/trace_call.md, docs/dictionary.txt
Adds developer guide, user knowledge base article, and dictionary entries for trace_call-related terminology.
Snapshots / ignore list
src/tool/subcommands/api_cmd/test_snapshots_ignored.txt
Adds Forest.EthTraceCall to ignored snapshots.

Sequence Diagram(s)

sequenceDiagram
    participant Client as Client
    participant RPC as Forest RPC (EthTraceCall)
    participant StateMgr as StateManager
    participant VM as VM/Executor
    participant Store as Blockstore/KAMT
    participant Trace as TraceBuilder

    Client->>RPC: Eth_traceCall(params)
    RPC->>StateMgr: load pre-state (actors & touched addresses)
    StateMgr->>Store: read pre-state actors & storage
    RPC->>VM: execute simulated call (no broadcast)
    VM-->>RPC: execution trace, logs, return/revert
    RPC->>StateMgr: load post-state (if requested)
    StateMgr->>Store: read post-state actors & storage
    RPC->>Trace: build traces and build_state_diff(pre, post, touched)
    Trace-->>RPC: EthTraceResults (trace + optional StateDiff)
    RPC-->>Client: response
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~70 minutes

Possibly related PRs

Suggested labels

RPC

Suggested reviewers

  • LesnyRumcajs
  • sudo-shashank
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 59.57% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: Implement Forest.EthTraceCall' clearly and specifically describes the main change—implementing the EthTraceCall RPC method in Forest, which aligns perfectly with the changeset's primary objective.
Linked Issues check ✅ Passed The PR fully addresses all coding requirements from issue #6185: implements the trace_call RPC method with complete type definitions and logic [src/rpc/methods/eth.rs, src/rpc/methods/eth/trace.rs], includes comprehensive test coverage [scripts/tests/trace_call_integration_test.sh], and provides extensive documentation [docs/docs/developers/guides/trace_call_guide.md, docs/docs/users/knowledge_base/trace_call.md].
Out of Scope Changes check ✅ Passed All changes are directly related to implementing the trace_call RPC method: core implementation, documentation, tests, test utilities, supporting type definitions, and dependency additions (fvm_ipld_kamt). No unrelated refactorings or feature additions are present outside the scope of the PR objectives.

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

✨ Finishing Touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch akaladarshi/eth-trace-call

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

@akaladarshi akaladarshi changed the title feat: Implement Forest.EthTraceCall feat: Implement Forest.EthTraceCall [Skip CI] Jan 13, 2026
@akaladarshi akaladarshi force-pushed the akaladarshi/eth-trace-call branch 2 times, most recently from 701d095 to a69d125 Compare January 29, 2026 15:28
@akaladarshi akaladarshi force-pushed the akaladarshi/eth-trace-call branch from a69d125 to 47b7aa3 Compare February 3, 2026 14:16
@LesnyRumcajs
Copy link
Member

As discussed, let's have a short guide in docs outlining its usage. This is important in light of various formats used across the Ethereum ecosystem for the output of this method.

Copy link
Member

Choose a reason for hiding this comment

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

The developers guides are intended for Forest maintainers. This should be in user space. Looking briefly, it's less of a guide more of a something to add to the knowledge base section (perhaps in a /RPC subsection). It's fine but let's rename the title to something along the lines of trace_call usage.

Copy link
Member

Choose a reason for hiding this comment

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

Okay, going through it all I think it should be split into two parts:

  1. User guide/knowledge post with basic usage info.
  2. Dev guide with all of the Anvil stuff and comparing things.

All in all, solid piece of work, seems to work fine.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/state_manager/mod.rs (1)

681-799: ⚠️ Potential issue | 🟡 Minor

Update public API docs for flush and the optional state root return.

These are signature‑level changes and should be reflected in doc comments.

📝 Suggested doc updates
+    /// Applies a message with gas estimation against a tipset/state.
+    /// If `flush` is true, returns the post-execution state root in the tuple.
     pub async fn apply_on_state_with_gas(
         self: &Arc<Self>,
         tipset: Option<Tipset>,
         msg: Message,
         state_lookup: StateLookupPolicy,
         flush: bool,
     ) -> anyhow::Result<(ApiInvocResult, Option<Cid>)> {
         ...
     }

     /// Computes message on the given [Tipset] state, after applying other
     /// messages and returns the values computed in the VM.
+    /// When `flush` is true, the last tuple element contains the new state root.
     pub async fn call_with_gas(
         self: &Arc<Self>,
         message: &mut ChainMessage,
         prior_messages: &[ChainMessage],
         tipset: Option<Tipset>,
         trace_config: VMTrace,
         state_lookup: StateLookupPolicy,
         flush: bool,
     ) -> Result<(InvocResult, ApplyRet, Duration, Option<Cid>), Error> {
         ...
     }

As per coding guidelines: Document all public functions and structs with doc comments.

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

In `@src/state_manager/mod.rs` around lines 681 - 799, The public API lacks doc
comments explaining the flush parameter and the optional state root in
apply_on_state_with_gas and call_with_gas; add/update Rust doc comments for the
public functions apply_on_state_with_gas and call_with_gas (and any public types
used in their signatures such as ApiInvocResult) that clearly state what the
flush bool does (i.e., whether it persists VM changes and causes a returned
state root) and that the returned Option<Cid> may be None when flush is false or
when no state root was produced; keep the docs short, include behavior for error
cases and tracing (VMTrace) where relevant, and ensure the comments appear above
the function signatures so they are included in generated docs.
🧹 Nitpick comments (1)
docs/docs/users/knowledge_base/trace_call.md (1)

1-13: Consider a slightly more playful intro tone.

A small, friendly line up front would better match the docs voice while keeping the content clear.

✏️ Optional tone tweak
-`trace_call` executes an EVM call and returns detailed execution traces without creating a transaction on the blockchain. It's useful for:
+`trace_call` executes an EVM call and returns detailed execution traces without creating a transaction on the blockchain. Think of it as a dress rehearsal before you put a transaction on-chain. It's useful for:

Based on learnings: Maintain a playful, approachable tone in Forest project documentation (Markdown files) rather than strictly formal/corporate language. Allow humor or light jokes where appropriate, as long as the content remains clear and helpful.

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

In `@docs/docs/users/knowledge_base/trace_call.md` around lines 1 - 13, Update the
intro of the "trace_call API Guide" to use a slightly more playful, friendly
tone: add a short welcoming one-liner before the Overview (e.g., a light,
approachable sentence that references the `trace_call` RPC method) and adjust
the first paragraph to keep the same technical links (`Parity/OpenEthereum`,
`reth`) and facts but with warmer wording; ensure the heading "trace_call API
Guide" and the mention of `trace_call` remain intact and do not change any
technical details or links.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/docs/developers/guides/trace_call_guide.md`:
- Around line 72-75: The phrase uses unhyphenated compound adjectives; update
the wording to use hyphenated forms by changing "Geth style" to "Geth-style" and
"Parity style" to "Parity-style" in the sentence describing tracing (the parts
mentioning debug_traceCall/prestateTracer and trace_call/stateDiff) so the
compound adjectives are grammatically correct.

In `@scripts/tests/trace_call_integration_test.sh`:
- Around line 53-62: The deployment block may leave ANVIL_CONTRACT empty causing
later steps to run against a null address; after running the forge create
command that sets ANVIL_CONTRACT (using CONTRACT_PATH and the forge create
invocation), check that ANVIL_CONTRACT is non-empty and a valid address (e.g.,
non-zero length or matches 0x... pattern) and if not, print a clear error
including the raw command output and exit non‑zero so the test suite fails fast;
ensure this guard follows the ANVIL_CONTRACT assignment and references
DEPLOY_CONTRACT, ANVIL_CONTRACT and the forge create/jq pipeline so it triggers
only when deployment was attempted.

In `@src/rpc/methods/eth.rs`:
- Around line 3988-3990: The trace result construction currently swallows decode
errors by calling decode_payload(...).ok(); instead, update the code around
EthTraceResults (and the similar block at the other occurrence) to propagate the
decode error back to the RPC caller rather than returning null: replace the
.ok() usage on decode_payload with proper error handling (e.g., use the ?
operator or map_err to convert/return the decode error) so get_trace_output /
EthTraceResults returns an explicit failure when decode_payload fails instead of
a None/serialized null.
- Around line 474-501: Public types EthTraceType and EthTraceResults lack
struct/enum-level doc comments; add concise top-level doc comments above the
enum EthTraceType and the struct EthTraceResults describing their purpose and
when each field is used (e.g., that EthTraceType selects Trace vs StateDiff
behavior and EthTraceResults contains output, optional state_diff, and trace),
so the public API has docs consistent with project guidelines and Rust
doc-comment style (///).

In `@src/shim/actors/builtin/evm/mod.rs`:
- Around line 56-65: The new public accessor methods bytecode, bytecode_hash,
and contract_state lack doc comments; add concise Rust doc comments (/// ...)
above each public fn describing what the method returns, the units/type (e.g.,
Cid or [u8; 32]), and any important semantics (e.g., whether it is the deployed
bytecode, the hash algorithm, or the CID for contract state), so each of
bytecode(), bytecode_hash(), and contract_state() is documented per public API
guidelines.

---

Outside diff comments:
In `@src/state_manager/mod.rs`:
- Around line 681-799: The public API lacks doc comments explaining the flush
parameter and the optional state root in apply_on_state_with_gas and
call_with_gas; add/update Rust doc comments for the public functions
apply_on_state_with_gas and call_with_gas (and any public types used in their
signatures such as ApiInvocResult) that clearly state what the flush bool does
(i.e., whether it persists VM changes and causes a returned state root) and that
the returned Option<Cid> may be None when flush is false or when no state root
was produced; keep the docs short, include behavior for error cases and tracing
(VMTrace) where relevant, and ensure the comments appear above the function
signatures so they are included in generated docs.

---

Nitpick comments:
In `@docs/docs/users/knowledge_base/trace_call.md`:
- Around line 1-13: Update the intro of the "trace_call API Guide" to use a
slightly more playful, friendly tone: add a short welcoming one-liner before the
Overview (e.g., a light, approachable sentence that references the `trace_call`
RPC method) and adjust the first paragraph to keep the same technical links
(`Parity/OpenEthereum`, `reth`) and facts but with warmer wording; ensure the
heading "trace_call API Guide" and the mention of `trace_call` remain intact and
do not change any technical details or links.

Comment on lines +53 to +62
if [ "$DEPLOY_CONTRACT" = true ]; then
command -v forge &>/dev/null || { echo "Error: forge is required for --deploy"; exit 1; }
echo -e "${YELLOW}Deploying Tracer contract on Anvil...${NC}"
CONTRACT_PATH="src/tool/subcommands/api_cmd/contracts/tracer/Tracer.sol"
ANVIL_CONTRACT=$(forge create "$CONTRACT_PATH:Tracer" \
--rpc-url "$ANVIL_RPC_URL" \
--private-key "$ANVIL_PRIVATE_KEY" \
--broadcast --json 2>/dev/null | jq -r '.deployedTo')
echo -e "Deployed to: ${GREEN}$ANVIL_CONTRACT${NC}"
fi
Copy link
Contributor

@coderabbitai coderabbitai bot Feb 19, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fail fast if deployment doesn’t return a contract address.

This avoids running the rest of the suite with a null/empty $ANVIL_CONTRACT.

🛡️ Suggested guard
     ANVIL_CONTRACT=$(forge create "$CONTRACT_PATH:Tracer" \
         --rpc-url "$ANVIL_RPC_URL" \
         --private-key "$ANVIL_PRIVATE_KEY" \
         --broadcast --json 2>/dev/null | jq -r '.deployedTo')
+    if [[ -z "$ANVIL_CONTRACT" || "$ANVIL_CONTRACT" == "null" ]]; then
+        echo -e "${RED}Error: deployment failed; no contract address returned${NC}"
+        exit 1
+    fi
     echo -e "Deployed to: ${GREEN}$ANVIL_CONTRACT${NC}"
 fi
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/tests/trace_call_integration_test.sh` around lines 53 - 62, The
deployment block may leave ANVIL_CONTRACT empty causing later steps to run
against a null address; after running the forge create command that sets
ANVIL_CONTRACT (using CONTRACT_PATH and the forge create invocation), check that
ANVIL_CONTRACT is non-empty and a valid address (e.g., non-zero length or
matches 0x... pattern) and if not, print a clear error including the raw command
output and exit non‑zero so the test suite fails fast; ensure this guard follows
the ANVIL_CONTRACT assignment and references DEPLOY_CONTRACT, ANVIL_CONTRACT and
the forge create/jq pipeline so it triggers only when deployment was attempted.

Copy link
Member

Choose a reason for hiding this comment

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

@akaladarshi does it make sense?

Copy link
Contributor

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Comment on lines +474 to +501
#[derive(PartialEq, Debug, Clone, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub enum EthTraceType {
/// Requests a structured call graph, showing the hierarchy of calls (e.g., `call`, `create`, `reward`)
/// with details like `from`, `to`, `gas`, `input`, `output`, and `subtraces`.
Trace,
/// Requests a state difference object, detailing changes to account states (e.g., `balance`, `nonce`, `storage`, `code`)
/// caused by the simulated transaction.
///
/// It shows `"from"` and `"to"` values for modified fields, using `"+"`, `"-"`, or `"="` for code changes.
StateDiff,
}

lotus_json_with_self!(EthTraceType);

#[derive(PartialEq, Debug, Clone, Default, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct EthTraceResults {
/// Output bytes from the transaction execution
pub output: Option<EthBytes>,
#[serde(skip_serializing_if = "Option::is_none")]
/// State diff showing all account changes (only when StateDiff trace type requested)
pub state_diff: Option<StateDiff>,
/// Call trace hierarchy (only when Trace trace type requested)
pub trace: Vec<EthTrace>,
}

lotus_json_with_self!(EthTraceResults);
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add top-level docs for the new public trace result types.
EthTraceResults is public but lacks a struct-level doc comment. Consider adding short type docs (same for EthTraceType) to meet the public docs rule.

✍️ Suggested doc comments
+/// Trace output selector for `trace_call`.
 #[derive(PartialEq, Debug, Clone, Serialize, Deserialize, JsonSchema)]
 #[serde(rename_all = "camelCase")]
 pub enum EthTraceType {
@@
+/// Result payload returned by `trace_call`.
 #[derive(PartialEq, Debug, Clone, Default, Serialize, Deserialize, JsonSchema)]
 #[serde(rename_all = "camelCase")]
 pub struct EthTraceResults {
As per coding guidelines: "Document all public functions and structs with doc comments".
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
#[derive(PartialEq, Debug, Clone, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub enum EthTraceType {
/// Requests a structured call graph, showing the hierarchy of calls (e.g., `call`, `create`, `reward`)
/// with details like `from`, `to`, `gas`, `input`, `output`, and `subtraces`.
Trace,
/// Requests a state difference object, detailing changes to account states (e.g., `balance`, `nonce`, `storage`, `code`)
/// caused by the simulated transaction.
///
/// It shows `"from"` and `"to"` values for modified fields, using `"+"`, `"-"`, or `"="` for code changes.
StateDiff,
}
lotus_json_with_self!(EthTraceType);
#[derive(PartialEq, Debug, Clone, Default, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct EthTraceResults {
/// Output bytes from the transaction execution
pub output: Option<EthBytes>,
#[serde(skip_serializing_if = "Option::is_none")]
/// State diff showing all account changes (only when StateDiff trace type requested)
pub state_diff: Option<StateDiff>,
/// Call trace hierarchy (only when Trace trace type requested)
pub trace: Vec<EthTrace>,
}
lotus_json_with_self!(EthTraceResults);
/// Trace output selector for `trace_call`.
#[derive(PartialEq, Debug, Clone, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub enum EthTraceType {
/// Requests a structured call graph, showing the hierarchy of calls (e.g., `call`, `create`, `reward`)
/// with details like `from`, `to`, `gas`, `input`, `output`, and `subtraces`.
Trace,
/// Requests a state difference object, detailing changes to account states (e.g., `balance`, `nonce`, `storage`, `code`)
/// caused by the simulated transaction.
///
/// It shows `"from"` and `"to"` values for modified fields, using `"+"`, `"-"`, or `"="` for code changes.
StateDiff,
}
lotus_json_with_self!(EthTraceType);
/// Result payload returned by `trace_call`.
#[derive(PartialEq, Debug, Clone, Default, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct EthTraceResults {
/// Output bytes from the transaction execution
pub output: Option<EthBytes>,
#[serde(skip_serializing_if = "Option::is_none")]
/// State diff showing all account changes (only when StateDiff trace type requested)
pub state_diff: Option<StateDiff>,
/// Call trace hierarchy (only when Trace trace type requested)
pub trace: Vec<EthTrace>,
}
lotus_json_with_self!(EthTraceResults);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/rpc/methods/eth.rs` around lines 474 - 501, Public types EthTraceType and
EthTraceResults lack struct/enum-level doc comments; add concise top-level doc
comments above the enum EthTraceType and the struct EthTraceResults describing
their purpose and when each field is used (e.g., that EthTraceType selects Trace
vs StateDiff behavior and EthTraceResults contains output, optional state_diff,
and trace), so the public API has docs consistent with project guidelines and
Rust doc-comment style (///).

Comment on lines +3988 to +3990
let mut trace_results = EthTraceResults {
output: get_trace_output(&msg, &invoke_result),
..Default::default()
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Don’t swallow return-data decode errors.
decode_payload(...).ok() turns decode failures into a None output (serialized as null) instead of surfacing an error, diverging from eth_call behavior. Prefer propagating the decode error so clients get a clear failure.

🐛 Suggested fix
-        let mut trace_results = EthTraceResults {
-            output: get_trace_output(&msg, &invoke_result),
+        let mut trace_results = EthTraceResults {
+            output: get_trace_output(&msg, &invoke_result)?,
             ..Default::default()
         };
-fn get_trace_output(msg: &Message, invoke_result: &ApiInvocResult) -> Option<EthBytes> {
+fn get_trace_output(msg: &Message, invoke_result: &ApiInvocResult) -> Result<Option<EthBytes>> {
     if msg.to() == FilecoinAddress::ETHEREUM_ACCOUNT_MANAGER_ACTOR {
-        return Some(EthBytes::default());
+        return Ok(Some(EthBytes::default()));
     }
 
-    let msg_rct = invoke_result.msg_rct.as_ref()?;
+    let msg_rct = invoke_result.msg_rct.as_ref().context("no message receipt")?;
     let return_data = msg_rct.return_data();
 
     if return_data.is_empty() {
-        return Some(EthBytes::default());
+        return Ok(Some(EthBytes::default()));
     }
 
-    decode_payload(&return_data, CBOR).ok()
+    let bytes = decode_payload(&return_data, CBOR)?;
+    Ok(Some(bytes))
 }

Also applies to: 4030-4044

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

In `@src/rpc/methods/eth.rs` around lines 3988 - 3990, The trace result
construction currently swallows decode errors by calling
decode_payload(...).ok(); instead, update the code around EthTraceResults (and
the similar block at the other occurrence) to propagate the decode error back to
the RPC caller rather than returning null: replace the .ok() usage on
decode_payload with proper error handling (e.g., use the ? operator or map_err
to convert/return the decode error) so get_trace_output / EthTraceResults
returns an explicit failure when decode_payload fails instead of a
None/serialized null.

Comment on lines +56 to +65
pub fn bytecode(&self) -> Cid {
delegate_state!(self.bytecode)
}

pub fn bytecode_hash(&self) -> [u8; 32] {
delegate_state!(self.bytecode_hash.into())
}

pub fn contract_state(&self) -> Cid {
delegate_state!(self.contract_state)
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add doc comments for the new public accessors.

These are public APIs and need doc comments for clarity and guideline compliance.

📝 Suggested doc comments
+    /// Returns the CID of the actor bytecode.
     pub fn bytecode(&self) -> Cid {
         delegate_state!(self.bytecode)
     }

+    /// Returns the 32-byte hash of the actor bytecode.
     pub fn bytecode_hash(&self) -> [u8; 32] {
         delegate_state!(self.bytecode_hash.into())
     }

+    /// Returns the CID of the contract state root.
     pub fn contract_state(&self) -> Cid {
         delegate_state!(self.contract_state)
     }

As per coding guidelines: Document all public functions and structs with doc comments.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
pub fn bytecode(&self) -> Cid {
delegate_state!(self.bytecode)
}
pub fn bytecode_hash(&self) -> [u8; 32] {
delegate_state!(self.bytecode_hash.into())
}
pub fn contract_state(&self) -> Cid {
delegate_state!(self.contract_state)
/// Returns the CID of the actor bytecode.
pub fn bytecode(&self) -> Cid {
delegate_state!(self.bytecode)
}
/// Returns the 32-byte hash of the actor bytecode.
pub fn bytecode_hash(&self) -> [u8; 32] {
delegate_state!(self.bytecode_hash.into())
}
/// Returns the CID of the contract state root.
pub fn contract_state(&self) -> Cid {
delegate_state!(self.contract_state)
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/shim/actors/builtin/evm/mod.rs` around lines 56 - 65, The new public
accessor methods bytecode, bytecode_hash, and contract_state lack doc comments;
add concise Rust doc comments (/// ...) above each public fn describing what the
method returns, the units/type (e.g., Cid or [u8; 32]), and any important
semantics (e.g., whether it is the deployed bytecode, the hash algorithm, or the
CID for contract state), so each of bytecode(), bytecode_hash(), and
contract_state() is documented per public API guidelines.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
src/rpc/methods/eth.rs (1)

3944-3952: Add documentation for EthTraceCall.

The public EthTraceCall enum lacks a doc comment. Per coding guidelines, all public types should have documentation describing their purpose and usage.

+/// Executes a call and returns traces for the transaction.
+/// 
+/// Supports both call traces (showing the hierarchy of calls) and state diffs
+/// (showing account state changes) based on the requested trace types.
 pub enum EthTraceCall {}

As per coding guidelines: "Document all public functions and structs with doc comments".

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

In `@src/rpc/methods/eth.rs` around lines 3944 - 3952, Add a doc comment for the
public enum EthTraceCall explaining its purpose and usage: describe that
EthTraceCall implements RpcMethod for the "Forest.EthTraceCall"/"trace_call" RPC
to return transaction traces, summarize expected parameters (tx, traceTypes,
blockParam), note permissions (Permission::Read) and any important
behavior/return expectations; place the documentation immediately above the `pub
enum EthTraceCall {}` declaration so it appears in generated docs and IDE
tooltips.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/rpc/methods/eth.rs`:
- Around line 474-501: Add concise top-level documentation comments for the
public enum EthTraceType and the public struct EthTraceResults: place
triple-slash doc comments (///) immediately above the EthTraceType enum
describing its purpose (to select the trace output format returned by
eth_call/trace APIs, e.g., structured call traces vs state diffs) and
immediately above EthTraceResults describing that it encapsulates the returned
trace output (output bytes, optional state_diff, and trace vector) and when each
field is populated; keep wording brief and public-facing so crate docs surface
the intent of these types.
- Around line 3993-3996: The current code silently converts payload decode
failures into None by calling get_trace_output(&msg, &invoke_result) when
building EthTraceResults, which hides decode errors; change get_trace_output to
return a Result<Option<T>, E> (or otherwise surface the decode_payload error)
and update the EthTraceResults construction site to propagate that error instead
of swallowing it—i.e., call get_trace_output(...)? (or handle Err by returning
the error) so decode_payload failures are returned to the eth_call client rather
than converted into null output.
- Around line 4035-4049: The get_trace_output function currently swallows decode
errors by calling decode_payload(...).ok(); change get_trace_output (and its
callers) to return Result<Option<EthBytes>, RpcError> (or the project’s standard
error type) instead of Option<EthBytes>, replace the final .ok() with a
?-propagated decode_payload(...) call so failure returns an Err with the decode
error, and ensure callers of get_trace_output (and any upstream RPC response
builders) are updated to handle and surface that Err to clients; reference
get_trace_output, decode_payload, ApiInvocResult, and EthBytes when making these
changes.

---

Nitpick comments:
In `@src/rpc/methods/eth.rs`:
- Around line 3944-3952: Add a doc comment for the public enum EthTraceCall
explaining its purpose and usage: describe that EthTraceCall implements
RpcMethod for the "Forest.EthTraceCall"/"trace_call" RPC to return transaction
traces, summarize expected parameters (tx, traceTypes, blockParam), note
permissions (Permission::Read) and any important behavior/return expectations;
place the documentation immediately above the `pub enum EthTraceCall {}`
declaration so it appears in generated docs and IDE tooltips.

@akaladarshi akaladarshi changed the title feat: Implement Forest.EthTraceCall [Skip CI] feat: Implement Forest.EthTraceCall Feb 19, 2026
@codecov
Copy link

codecov bot commented Feb 19, 2026

Codecov Report

❌ Patch coverage is 79.49836% with 188 lines in your changes missing coverage. Please review.
✅ Project coverage is 63.42%. Comparing base (3f6089c) to head (74a2ab8).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
src/rpc/methods/eth/trace.rs 81.01% 85 Missing and 23 partials ⚠️
src/rpc/methods/eth.rs 64.42% 73 Missing and 1 partial ⚠️
src/shim/actors/builtin/evm/mod.rs 66.66% 3 Missing ⚠️
src/state_manager/mod.rs 93.54% 0 Missing and 2 partials ⚠️
src/rpc/methods/eth/filter/mod.rs 92.30% 1 Missing ⚠️
Additional details and impacted files
Files with missing lines Coverage Δ
src/rpc/methods/eth/types.rs 75.56% <100.00%> (+7.42%) ⬆️
src/rpc/methods/gas.rs 86.87% <100.00%> (+0.03%) ⬆️
src/rpc/mod.rs 88.37% <ø> (ø)
src/rpc/methods/eth/filter/mod.rs 87.61% <92.30%> (-0.13%) ⬇️
src/state_manager/mod.rs 69.76% <93.54%> (+0.21%) ⬆️
src/shim/actors/builtin/evm/mod.rs 82.85% <66.66%> (+5.93%) ⬆️
src/rpc/methods/eth.rs 69.24% <64.42%> (-0.42%) ⬇️
src/rpc/methods/eth/trace.rs 75.76% <81.01%> (+8.52%) ⬆️

... and 6 files with indirect coverage changes


Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update a9f8cf1...74a2ab8. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

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

Duplicate comments:
In `@src/rpc/methods/eth.rs`:
- Around line 3988-4077: The output decoding in trace_call currently swallows
decode errors via decode_payload(...).ok(); locate get_trace_output (and the
other occurrence around the 4079–4093 area) and replace the .ok() usage so the
decode error is propagated instead: call decode_payload(...) and use ? to return
the error, adding context with .context("failed to decode return data for
trace_call") (or an equivalent descriptive message) so the error flows back
through the existing Result/ServerError handling rather than producing a null
output.

@sudo-shashank
Copy link
Contributor

sudo-shashank commented Feb 19, 2026

@akaladarshi enable RPC check on this PR and resolve all comments

@akaladarshi akaladarshi added the RPC requires calibnet RPC checks to run on CI label Feb 19, 2026
Copy link
Contributor

@sudo-shashank sudo-shashank left a comment

Choose a reason for hiding this comment

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

LGTM

Copy link
Member

Choose a reason for hiding this comment

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

I can imagine more methods being explained; let's put it into an knowledge base/RPC directory.

Copy link
Member

Choose a reason for hiding this comment

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

some of the entries don't look like something that should be added; if it's a variable name or a method name, it should be done inline within the doc.

}

impl EthAddress {
#[allow(clippy::wrong_self_convention)]
Copy link
Member

Choose a reason for hiding this comment

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

why?

Some(ts.clone()),
trace_config,
StateLookupPolicy::Enabled,
false,
Copy link
Member

Choose a reason for hiding this comment

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

I recommend not using booleans as parameters unless it's obvious what they do. When you're reading call_with_gas (and don't recall what's the method signature), you probably can imagine what StateLookupPolicy::Enabled signifies; on the other hand, false doesn't tell you anything.

I'd move it to an enumeration.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yeah I total agree here, that's why I suggested to go with the boolean for now as I am going to raise a subsequent PR to refactor the state_manager in that we will be having more structure to the methods and there i can add this as well, WDYT?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

RPC requires calibnet RPC checks to run on CI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement trace_call RPC method

4 participants

Comments