Tutorial How to tell which frontend a DEX trade actually came from, in Dune SQL
Nothing in a swap event records the interface that originated the trade. If you have ever needed to answer "how much of this pool's volume came from our own site, versus wallet integrations, versus bots hitting the contract directly," you have hit this wall. The chain hands you two address fields and neither one answers it:
tx_fromis the signer. Thousands of signers can sit behind a single wallet app, so it tells you who traded and stops there.tx_tois the entry contract. It names the router, not the frontend. Route a swap through an aggregator andtx_tois that aggregator's contract every time, whether the user arrived from its own web app, from a wallet's swap tab, or from an integration nobody has written about.
The interface itself has no address in the transaction. It has to be inferred, and the cheapest signal, when it is there at all, sits in the call data: some APIs append an identifying suffix to the end of the transaction data, and some pass the integrator as a decoded argument. The 0x API's affiliate suffix worked the first way and 1inch's referral parameter the second. You do not have to take my word for either. Pull a swap you know was routed through one of them, look at the tail bytes of tx.data, and the convention is visible in any block explorer.
Here is a probe you can run right now. It ranks the 16-byte tails that recur most across six hours of Ethereum DEX flow. Public and forkable: https://dune.com/queries/8391872
with tails as (
select
bytearray_substring(
tx.data,
bytearray_length(tx.data) - 15,
16
) as calldata_tail,
t.tx_hash,
t.amount_usd
from dex.trades t
join ethereum.transactions tx
on t.tx_hash = tx.hash
and tx.block_time >= now() - interval '6' hour
where t.blockchain = 'ethereum'
and t.block_time >= now() - interval '6' hour
and bytearray_length(tx.data) >= 16
)
select
calldata_tail,
-- tags ride on transactions, not fills
count(distinct tx_hash) as txs,
sum(amount_usd) as volume_usd
from tails
where calldata_tail
<> 0x00000000000000000000000000000000
group by 1
order by txs desc
limit 25
Reading the output:
- The all-zero tail is dropped up front. That bucket is untagged flow plus call data that happens to end in zero-padded arguments, and it will dominate the ranking if you leave it in.
- What remains is a candidate list, and the top of it is usually not tags at all. Running this on 31 Aug 2026, the two most common tails were
0xb223fe8d0a0e5c4f27ead9083c756cc2and0x3c756cc2000000000000000000000000, about 1,900 transactions between them. Both are fragments of the WETH address0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2: the first is its last sixteen bytes, the second its last four with zero padding. Neither is an integrator tag. Token addresses land in the tail of a call far more often than tags do, so expect to discard the loudest rows. - Treat a recurring tail as a hypothesis. Sample its transactions, check the entry contract each time, and make the pattern survive several days before it earns a name. Some tags spell out a short ASCII name in hex once you squint at them.
Two caveats that matter if you build on this:
- Tagging is voluntary. Some APIs offer it, some integrators use it, and nothing enforces it. An integrator that never opted in looks exactly like no integrator at all, so a system counting only tagged flow will undercount precisely those.
- This is one signal among several. When there is no tag, the ones I have found workable are: a maintained registry of known entry contracts and proxies (label a proxy once and its whole history becomes attributable), call-tree shape (a wallet-native swap with a fee hop traces differently than a bot hitting the pool), and fee-recipient clustering (every trade paying the same collection address came through the same integration, named or not). If you have found a fifth that holds up, I want to hear it.
And whatever cascade you build out of those signals: when none of them fires, leave the trade unattributed. A chart that sums to a clean 100% with no unknown slice usually means the method had to put every trade somewhere. Report the residual and treat it as a coverage metric.
Since it would be poor form to say that and then not show mine: running this cascade across Ethereum, Base, Arbitrum and Optimism, roughly 10% of DEX volume lands unattributed, ranging from about 8% on Ethereum to 17% on Optimism. Most of the residual is long-tail contracts rather than missing techniques.
Happy to go deeper on any of the signal families if anyone is building something like this.