Added output (returndata) decoding APIs to CallDecoder#59
Added output (returndata) decoding APIs to CallDecoder#59Pratham6392 wants to merge 4 commits intoenviodev:mainfrom
Conversation
📝 WalkthroughWalkthroughAdds a new example script demonstrating trace call input/output decoding, fixes input-decoding forwarding bugs, introduces async/sync output-decoding APIs in CallDecoder (Python surface), updates stream recv wrappers, and implements Rust-side output-decoding helpers. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant Example as Example Script
participant Client as HypersyncClient
participant Envio as Envio API
participant DecoderPy as CallDecoder (Python)
participant DecoderRs as Decoder (Rust)
rect rgba(200,230,201,0.5)
Example->>Client: build client (ENVIO_API_TOKEN)
Example->>Client: send Query (trace call filter)
Client->>Envio: fetch traces
Envio-->>Client: traces list
Client-->>Example: QueryResponse (traces)
end
rect rgba(187,222,251,0.5)
Example->>DecoderPy: decode_inputs / decode_traces_input (inputs)
DecoderPy->>DecoderRs: spawn_blocking decode_inputs
DecoderRs-->>DecoderPy: decoded inputs
DecoderPy-->>Example: decoded input results
end
rect rgba(255,224,178,0.5)
Example->>DecoderPy: decode_outputs / decode_traces_output (outputs + signatures)
DecoderPy->>DecoderRs: spawn_blocking decode_outputs
DecoderRs-->>DecoderPy: decoded outputs (or None)
DecoderPy-->>Example: decoded output results
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related issues
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (1)
hypersync/__init__.py (1)
221-237: Return type annotations inconsistent withdecode_inputs_sync.The newly fixed
decode_inputs_synchas return typelist[Optional[list[DecodedSolValue]]], but these existing methods still uselist[list[DecodedSolValue]]. The Rust implementations returnVec<Option<Vec<DecodedSolValue>>>for all of them.Consider updating the type hints for consistency:
📝 Proposed type annotation fix
def decode_transactions_input_sync( self, txs: list[Transaction] - ) -> list[list[DecodedSolValue]]: + ) -> list[Optional[list[DecodedSolValue]]]: """Parse log and return decoded event. Returns None if topic0 not found.""" return self.inner.decode_transactions_input_sync(txs) def decode_traces_input_sync( self, traces: list[Trace] - ) -> list[list[DecodedSolValue]]: + ) -> list[Optional[list[DecodedSolValue]]]: """Parse log and return decoded event. Returns None if topic0 not found.""" return self.inner.decode_traces_input_sync(traces)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@hypersync/__init__.py` around lines 221 - 237, Update the return type annotations for decode_transactions_input_sync, decode_traces_input, and decode_traces_input_sync to match decode_inputs_sync and the Rust implementation: change from list[list[DecodedSolValue]] to list[Optional[list[DecodedSolValue]]]; ensure any docstrings or comments remain accurate and that callers handle Optional entries accordingly so the Python signatures align with Vec<Option<Vec<DecodedSolValue>>> semantics.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@examples/trace_call_watch.py`:
- Around line 16-25: The function make_client currently hardcodes the API token;
change it to read from an environment variable (e.g., ENVIO_API_TOKEN) instead
of the literal string, remove the dead-code check caused by the hardcoded value,
and raise a ValueError if the env var is missing; keep the rest of the client
construction (hypersync.HypersyncClient and hypersync.ClientConfig with url and
api_token) unchanged so make_client fails fast when the token is not provided
via the environment.
In `@hypersync/__init__.py`:
- Around line 896-899: The recv method in QueryResponseStream currently awaits
self.inner.recv() but doesn't return its result; change QueryResponseStream.recv
to return await self.inner.recv() (i.e., return the value from
self.inner.recv()) and fix the extra leading space/indentation so the return is
properly aligned within the async def recv(self) -> Optional[QueryResponse]:
block.
In `@src/decode_call.rs`:
- Around line 184-200: The function decode_traces_output_sync currently zips
traces and signatures which silently truncates when their lengths differ; add an
explicit length check at the start of decode_traces_output_sync (e.g.,
assert_eq!(traces.len(), signatures.len(), "mismatched traces and signatures
lengths") or return/raise a clear error) so the mismatch is detected instead of
truncating, then proceed to call decode_output_impl as before; reference
decode_traces_output_sync and decode_output_impl when making the change.
- Around line 171-182: The current decode_outputs_sync uses zip which silently
truncates when outputs and signatures differ; add an explicit length check at
the start of decode_outputs_sync (compare outputs.len() and signatures.len())
and return a clear failure if they differ (e.g., panic! or return an Err if you
change the signature) so mismatches are not silently dropped, then iterate by
index or enumerate and call decode_output_impl(output.as_str(), sig.as_str(),
py) for each pair to preserve full alignment between outputs and signatures.
---
Nitpick comments:
In `@hypersync/__init__.py`:
- Around line 221-237: Update the return type annotations for
decode_transactions_input_sync, decode_traces_input, and
decode_traces_input_sync to match decode_inputs_sync and the Rust
implementation: change from list[list[DecodedSolValue]] to
list[Optional[list[DecodedSolValue]]]; ensure any docstrings or comments remain
accurate and that callers handle Optional entries accordingly so the Python
signatures align with Vec<Option<Vec<DecodedSolValue>>> semantics.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: c501c1fc-8ff1-430f-8c73-5a506435d5ba
📒 Files selected for processing (3)
examples/trace_call_watch.pyhypersync/__init__.pysrc/decode_call.rs
There was a problem hiding this comment.
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 `@examples/trace_call_watch.py`:
- Line 71: The code constructs target = "0x" + ADDR.lower() which
double-prefixes when ADDR already starts with "0x"; update the logic that builds
target (the target variable using ADDR) to first normalize ADDR by stripping any
leading "0x"/"0X" and whitespace, then lowercasing and prefixing a single "0x"
(e.g., normalize ADDR before using it where target is created) so valid traces
are not incorrectly filtered; locate the target assignment in
examples/trace_call_watch.py and change it to use the normalized ADDR value.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 3afc4a1a-034b-4a69-8345-248e0790a9ac
📒 Files selected for processing (1)
examples/trace_call_watch.py
There was a problem hiding this comment.
♻️ Duplicate comments (1)
src/decode_call.rs (1)
189-205:⚠️ Potential issue | 🟡 MinorMissing length validation for
tracesandsignatures.Unlike
decode_outputs_syncwhich now validates lengths viaassert_eq!, this method still useszip()without any length check. Mismatched lengths will silently discard trailing elements.🛡️ Proposed fix to add length validation
pub fn decode_traces_output_sync( &self, traces: Vec<Trace>, signatures: Vec<String>, py: Python, ) -> Vec<Option<Vec<DecodedSolValue>>> { + assert_eq!( + traces.len(), + signatures.len(), + "traces and signatures must have the same length" + ); traces .into_iter() .zip(signatures.into_iter())🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/decode_call.rs` around lines 189 - 205, The function decode_traces_output_sync uses zip on traces and signatures which silently drops trailing items when lengths differ; add an explicit length check (e.g., assert_eq! or a Result-returning check) comparing traces.len() and signatures.len() at the start of decode_traces_output_sync to ensure they match (mirroring the validation used in decode_outputs_sync), so that mismatched lengths are detected instead of silently truncating; keep the rest of the logic using trace.output and decode_output_impl unchanged.
🤖 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/decode_call.rs`:
- Around line 189-205: The function decode_traces_output_sync uses zip on traces
and signatures which silently drops trailing items when lengths differ; add an
explicit length check (e.g., assert_eq! or a Result-returning check) comparing
traces.len() and signatures.len() at the start of decode_traces_output_sync to
ensure they match (mirroring the validation used in decode_outputs_sync), so
that mismatched lengths are detected instead of silently truncating; keep the
rest of the logic using trace.output and decode_output_impl unchanged.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: b523dc71-1140-4752-89f5-55a7c5f597a7
📒 Files selected for processing (2)
hypersync/__init__.pysrc/decode_call.rs
🚧 Files skipped from review as they are similar to previous changes (1)
- hypersync/init.py
Summary
This PR exposes the upstream hypersync_client::CallDecoder::decode_output capability in the Python client by adding PyO3 bindings and Python wrapper methods. It enables users to decode ABI-encoded return data (returndata) from raw hex blobs and from trace outputs.
Key Changes
New Rust bindings in src/decode_call.rs:
Python SDK additions in hypersync/init.py:
Tests:
Implementation Details
Mirrors existing async/sync patterns:
Error behavior matches existing decoder conventions:
No dependency or config changes are required (uses existing hypersync-client API).
This closes a feature gap where Python users can decode calldata (decode_input) but not returndata (decode_output), even though the upstream Rust client already supports it. Decoding outputs is essential for interpreting trace results and eth_call return values in indexing/debugging pipelines, and the implementation follows the established wrapper/binding patterns in this repo.
Summary by CodeRabbit
New Features
Bug Fixes