Attribution
Recovering the trader behind a router without paying for a single request.
Finding a trade is half the job. The other half is working out whose trade it was, and on this chain the obvious answer is wrong twice over.
The sender is not the trader
A swap log carries a sender, and on almost every trade that sender is a
router contract. Attribute to it and the leaderboard becomes a list of routers,
with SwapRouter02 as the chain's greatest trader by a wide margin.
So the trader is the transaction's own origin. Which means the engine needs
tx.from for every trade it sees, and that is where this gets expensive.
Asking costs money. Listening does not.
The naive approach is eth_getTransactionByHash for every trade. On a chain
mining around ninety transactions a second, that is a request per trade,
forever, against an endpoint that starts refusing you. Attribution by RPC on a
busy chain draws HTTP 429 and then the whole pipeline stalls behind it.
The sequencer relay solves it for free.
That relay pushes raw sequenced transaction bytes at anyone who connects. The sender is recoverable from those bytes locally with a secp256k1 recovery, which is arithmetic rather than a network call. No request, no key, no rate limit, no cost.
Only what the relay did not see falls back to asking. That means boot backfills and gap repairs, which are bursts rather than steady state.
The result, measured over a live seventy two second window in steady state, was 2164 traders resolved from the feed against one over RPC. Effectively all of it, for nothing.
Account abstraction breaks it again
ERC-4337 is in heavy use here, and on a bundled transaction tx.from is the
bundler. Attribute to it and one infrastructure address absorbs the profit of
everyone whose transactions it packaged.
The real account comes from the UserOperationEvent instead. That event was
added to the existing log query's topic filter, so it costs zero additional
requests. The logs were already being fetched.
When a bundle carries more than one account, the trade is dropped and counted rather than assigned to a guess. Attributing money to the wrong wallet is worse than attributing it to nobody, especially on a screen people use to decide who to copy.
What gets dropped
About 3.38% of trades seen, and it is concentrated rather than spread out.
It happens in the sweeps right after a restart. Those are transactions older than the feed's four minute raw memory, so the bytes are gone and the only route left is an RPC lookup that eventually ages out. Rather than guess an owner, the trade is dropped and the drop is counted.
Never quote the free path percentage without measuring it that day
The obvious claim is that attribution runs about 99% free, and in steady state it does.
Checked before publishing it once, the health endpoint read 79.36%, because the engine had restarted that morning and a backfill's transactions are older than the feed's raw memory by definition. They resolve the expensive way or not at all.
Read attribution.fromFeed against attribution.fromRpc on the day you need
the number, or make the structural claim with no percentage attached to it.
The ratio is a property of how recently the engine restarted, not just of the
architecture.
Reading it yourself
The health endpoint carries the counters live.
curl -H "Authorization: Bearer $HOUND_TOKEN" \
https://api.houndterminal.trade/healthattribution.fromFeed should dominate attribution.fromRpc once the engine
has been up for a while. If it does not, the free path is broken and the
metered one is quietly absorbing the work, which shows up as a bill rather than
as an error.
That is the second of the two canaries. The first is in Coverage, and both exist because this system's characteristic failure mode is continuing to look like it works.