refactor(hindsight): decoders built once, solver knowledge as handles - #428
Closed
tamaralipows wants to merge 12 commits into
Closed
refactor(hindsight): decoders built once, solver knowledge as handles#428tamaralipows wants to merge 12 commits into
tamaralipows wants to merge 12 commits into
Conversation
Attribution now carries the solver's SolverKnowledge trait object, resolved when the solver is named. Callers consult the trait on the handle; the per-method free functions that re-derived the implementation from the solver name on every call (swap_intent, output_recipient) are deleted. A solver with no implementation resolves to a shared no-op handle, so call sites never branch on whether a solver has code. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Venue decoders are built when the registry loads, each holding its venue's addresses as state, and live on the venue's registry entry. The decode path resolves the entry point (an Address) to that entry and calls trait objects — the per-transaction string match and the per-transaction boxing are gone. The one name-to-code binding left is venues::DECODERS, a registration table consulted once at load; an address-book venue with no row fails the load, replacing has_decoder. TradeDecoder loses its provider type parameter: a constructor table cannot be generic, so DecodeContext carries alloy's DynProvider and the Decoder erases its provider once at construction. The sender and intent decoder lists are built once per Decoder (EntityDecoders). DecodeContext.venue is deleted — decoders already hold their addresses. Tests build contexts through a shared CtxFixture instead of hand-assembling every field. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Every decoder used to set GasScope on its flow; the same three-line rule now lives in one place (decode::gas_scope), derived from facts the role and the flow already establish: gas is charged only when the flow tracks the sender and the sender net-sent the input token — whole transaction for a direct solver entry, the solver frame for a venue entry, nothing for intent fills. TraderFlow loses the field. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
netting_decoders.rs and intents/netting.rs merge into netting.rs: the engine (sender_flow, venue_flow, find_intent_trade) plus the two generic decoders (SenderNetting, IntentNetting) in one place. Venue netting decoders stay in venues/ and call the engine. Decoder name labels in the records are unchanged. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
solvers::settled_intent does the venue-agnostic half of a calldata-primary decode in one call: find the solver frame, resolve the solver's knowledge handle, recover its swap intent and declared output recipient. RelayCalldata shrinks to that call plus Relay's own guards and fee basis; the next venue's calldata decoder is the same thin shape. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
README and CLAUDE.md now describe decoders as built once — venue decoders on the venue's registry entry via venues::DECODERS, solver knowledge behind one resolved handle, gas derived by decode::gas_scope, one netting module — and say why matching stays venue-keyed while decoding centers on the solver call. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The orchestrator skipped the intent-vs-flow disagreement warning by comparing the winning decoder's name to a string literal — renaming the decoder would silently disable the skip. The property is now a defaulted TradeDecoder method the calldata decoder overrides, and the decode step hands back the decoder itself instead of its name. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A venue's decoders were each cloning the whole address-book section, three copies per venue counting the registry entry. Each decoder now holds exactly what it reads — fee collectors for most, plus entry points for Relay's rebalance decode; MetaMask keeps the full section since it also reads the solver-alias table. Also extracts the intent recovery out of decode_transaction to stay under the line limit. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Decoders received a live RPC provider plus a raw code cache through the decode context, though the only question they ever ask beyond the transaction is whether an address holds contract code. That question is now a trait the decode layer owns (ContractCode); the RPC-backed answer with its cross-block cache is an adapter owned by the Decoder. Netting tests exercise the EOA/contract distinction with an in-memory fake instead of a mocked RPC client, and the test fixture no longer builds a transport at all. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The headline verdict and every dashboard number compare gross amounts since July; the net-of-gas figures were secondary record columns with no consumer. Removed end to end: the trader-paid gas derivation, the solver-frame gas isolation from the trace, the settled_gas trade field, the settled_gas_cost / settled_amount_out_net_gas / net_bps record columns, and the gas-to-token price conversion. The report's per-trade and per-group bps switch to the gross delta (raw_bps), which every recorded dataset already carries. Fynd's own quotes stay gas-aware; this removes only the settled-side bookkeeping. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
"Intent" already names the trader role for orders a solver settles on the trader's behalf (CoW, 1inch orders). The calldata type is a different thing — the swap's terms as the solver's calldata declares them — so it is now DeclaredSwap, read via SolverKnowledge:: declared_swap, with the shared one-step read returning a SolverDeclaration. Record columns are unchanged. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The handle resolver still searched IMPLEMENTATIONS by name at trade time, and callers reached it by converting a solver address to a name first. The join now happens once, at address-book load: each registry solver entry carries its SolverKnowledge handle, and every address-based path (solver frame, entry point, veto and integrator log scans) resolves address -> entry -> trait call, with no string in between. The name-keyed resolver remains only where the name itself is the on-chain datum: a venue-declared solver id and the fallback attribution labels. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Contributor
Author
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.
Explanation here: https://propeller-heads.atlassian.net/wiki/spaces/GEN/pages/edit-v2/3897393161?draftShareId=e00f8bab-1060-4521-bc44-8aadb99992b2