refactor(hindsight): make per-transaction decode RPC-free - #391
Open
kayibal wants to merge 2 commits into
Open
Conversation
kayibal
force-pushed
the
feat/hindsight-relay-reverts
branch
from
July 31, 2026 14:42
134e666 to
1792c69
Compare
kayibal
force-pushed
the
feat/hindsight-pure-decode
branch
from
July 31, 2026 14:48
283da8d to
53eefb8
Compare
kayibal
force-pushed
the
feat/hindsight-relay-reverts
branch
from
July 31, 2026 17:41
1792c69 to
44908ea
Compare
kayibal
force-pushed
the
feat/hindsight-pure-decode
branch
from
July 31, 2026 17:52
53eefb8 to
ac9a2fe
Compare
tamaralipows
left a comment
Contributor
There was a problem hiding this comment.
Just stumbled upon this. This is a great idea and looks so much cleaner
kayibal
force-pushed
the
feat/hindsight-relay-reverts
branch
from
August 2, 2026 12:12
44908ea to
4aa0fea
Compare
kayibal
force-pushed
the
feat/hindsight-pure-decode
branch
from
August 2, 2026 12:14
ac9a2fe to
dfe4f9a
Compare
kayibal
force-pushed
the
feat/hindsight-relay-reverts
branch
2 times, most recently
from
August 3, 2026 17:32
10616a3 to
29c9ec7
Compare
Remove the last RPC escape hatch from DecodeContext: decode is now a pure function of a receipt, its trace, and its calldata. The only production RPC use inside decoding was intents/netting.rs's is_contract check (eth_getCode via DecodeContext.provider + .code_cache), used to tell an intent-fill candidate EOA apart from a contract. Hoist that check to the decoder driver: Decoder::prefetch_contract_flags runs once per block, before per-tx decode, for exactly the candidates intents::netting::intent_candidates would enumerate for each settled, intent-role transaction in the block -- not every address in every ledger, and not reverted candidates, which never reach the TraderFlow decoder chain at all (decode_reverted reads only the trace and the solver frame's calldata). Results join the existing cross-block code_cache. DecodeContext.provider and .code_cache are replaced by contract_flags: &HashMap<Address, bool>, a plain lookup; find_intent_trade becomes a synchronous function taking that map instead of a provider. Dropping DecodeContext's RPC dependency removes the now-unused P: Provider generic from TradeDecoder, DecodeContext, decoders_for, recover, try_decoders, and every TradeDecoder impl (netting_decoders::SenderNetting, every venues/* decoder, intents::cow::CowSettlement, intents::netting::IntentNetting), along with the mocked-provider test scaffolding each of those carried solely to satisfy the type. decode_settled now takes &self (not &mut self) for the same reason, matching decode_reverted and the decode_transaction dispatcher that calls either. Behavior is unchanged: same decode results, same trades. Verified with a new end-to-end test (decoder::tests::test_intent_fill_prefetches_the_candidate_contract_flag) that runs decode_block for an intent-fill transaction through a mocked provider and confirms the prefetched eth_getCode result reaches IntentNetting via contract_flags. Manual verification (pre-commit hook not run -- cargo nextest run --workspace SIGABRTs on an unrelated fynd-core sim_guard test on this machine, a known conda/cc PATH issue breaking panic unwinding): - cargo check --workspace --all-features: pass - cargo clippy --locked --workspace --all-targets --all-features -- -D warnings: pass - cargo nextest run -p hindsight --all-features: 311 passed, 1 skipped - cargo +nightly fmt --all -- --check: pass - RUSTDOCFLAGS="-D warnings" cargo doc for hindsight, fynd-core, fynd-rpc-types, fynd-rpc, fynd-client: pass Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Update the decode pipeline section and the TradeDecoder trait sketch for DecodeContext dropping its RPC provider and code_cache in favor of contract_flags, and the decoder driver's prefetch_contract_flags step that gathers them before per-tx decode runs. Pre-commit hook not run (see prior commits: cargo nextest run --workspace --bin fynd SIGABRTs on an unrelated fynd-core test on this machine). Docs-only change; no code checks apply. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
kayibal
force-pushed
the
feat/hindsight-pure-decode
branch
from
August 3, 2026 17:34
dfe4f9a to
53a576c
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Removes the last RPC escape hatch from
DecodeContext: decoding a matched transaction is now apure function of its receipt, trace, and calldata. No
TradeDecoderissues RPC of its own.Changes
intents/netting.rs'sis_contractcheck(
eth_getCode, used to tell an intent-fill candidate EOA apart from a contract), is hoisted tothe decoder driver:
Decoder::prefetch_contract_flagsruns once per block, before per-txdecode, for exactly the candidates
intents::netting::intent_candidateswould enumerate foreach intent-role transaction in that block — not every address in every ledger, since intent
fills are a small fraction of matched transactions. Results join the existing cross-block
code_cache, so a recurring candidate (a router, a pool) costs one RPC call for the life of therun.
DecodeContext.providerand.code_cacheare replaced bycontract_flags: &HashMap<Address, bool>— a plain lookup, prefetched ahead of decode.find_intent_tradebecomes a synchronousfunction taking that map instead of a provider.
DecodeContext's RPC dependency removes the now-unusedP: Providergeneric fromTradeDecoder,DecodeContext,decoders_for,recover,try_decoders, and everyTradeDecoderimpl (SenderNetting, everyvenues/*decoder,CowSettlement,IntentNetting) — along with the mocked-provider test scaffolding each of those carried solelyto satisfy the type.
decode_transactiontakes&selfinstead of&mut self, since it no longer needs mutableaccess to the code cache during decode.
Behavior
Unchanged: same decode results, same trades. Covered by a new end-to-end test
(
decoder::tests::test_intent_fill_prefetches_the_candidate_contract_flag) that runsdecode_blockfor an intent-fill transaction through a mocked provider and confirms theprefetched
eth_getCoderesult reachesIntentNettingviacontract_flags.Testing
cargo check --workspace --all-features,cargo clippy --locked --workspace --all-targets --all-features -- -D warnings,cargo nextest run -p hindsight(303 passed, 1 skipped),cargo +nightly fmt --check, andRUSTDOCFLAGS="-D warnings" cargo doc --no-deps -p hindsight -p fynd-core -p fynd-rpc-types -p fynd-rpc -p fynd-client— all clean.Pre-commit hook not used for the commits on this branch:
cargo nextest run --workspace --bin fyndSIGABRTs on this machine on an unrelatedfynd-coretest (aconda/ccPATH issuebreaking Rust panic unwinding, environmental). Verified manually instead; see above.
🤖 Generated with Claude Code