# DEX Aggregator Routing Share Across Ethereum L1 + L2s: Who Dominates, and Why It Matters *A cross-chain look at which aggregators dominate routing on Base, Arbitrum, Optimism, and mainnet. Pulled from short RPC-window samples (~21-100 minutes of wall-clock data per chain, sizes below) before anything touches Dune.* --- Most aggregator dashboards I've seen are mainnet-only, single-chain, or focus on dollar-volume through `dex.trades`. That's fine but it hides something interesting: **the market leader varies per chain, and it's not who you'd guess from the ETH-mainnet picture**. Running the same router-address-tagging pipeline across four EVM chains produced the following: | Chain | Leader | Share | Top-3 concentration | Sample (agg. tx) | |---|---|---|---|---| | Ethereum | 0x Settler v2 | 51.9% | 95.0% | 859 | | Base | 0x Settler v2 | 59.8% | 87.5% | 1,691 | | Arbitrum | KyberSwap | 23.6% | 58.8% | 148 | | Optimism | ParaSwap v6 | 39.6% | 80.8% | 250 | **Leadership is not uniform.** Base looks like mainnet (0x Settler dominates); Arbitrum is unusually fragmented with six aggregators between 10 and 25% share; Optimism is a ParaSwap-led market. ![Per-chain aggregator share (pies)](sandbox_out/aggregator_analysis/l2/l2_aggregator_share_facet.png) Three of the headline numbers are worth a closer look. ## 0x Settler v2 on Base and mainnet On ETH mainnet (500-block sample, 2026-04-18, ~100 min wall-clock), the new **0x Settler v2** contract at `0x0000000000001ff3684f28c67538d4d072c22734` routes 51.9% of aggregator transactions (446 of 859). 1inch v6 is second at 24.8% (213 of 859); KyberSwap is third at 18.3% (157 of 859). The Settler contract is the 2026 successor to the 0x-protocol exchange proxy. Matcha and 0x API both route through it now. The migration has happened; the old `ExchangeProxy` (`0xdef1…5eff`) has much lower share today. Base tells the same story, harder: 59.8% share, n=1,691 (~33 min window). Same CREATE2 deployment, same contract address. Cross-chain deployment patterns make comparison cleaner than it used to be. ![Aggregator share 100%-stacked bar (per-chain)](sandbox_out/aggregator_analysis/l2/l2_aggregator_share_stacked_bar.png) ## Arbitrum is unusually fragmented On 5,000 Arbitrum blocks I tagged only 148 aggregator transactions. Most Arbitrum swaps still go through direct DEX routers (Camelot V2, Camelot V3, SushiSwap, Uniswap Universal) rather than aggregators. Among the swaps that DO go through aggregators, the share split is: - KyberSwap 23.6% - LiFi 19.6% - ParaSwap v6 (arb) 15.5% - 1inch v6 14.2% - 0x Settler v2 12.2% - CoW Protocol 10.1% **No aggregator above 25%.** This is the most fragmented aggregator market of the three L2s. Why this is interesting: Arbitrum is usually treated as "the DeFi-native L2," yet aggregator share there is flatter than the other two chains. One hypothesis: Camelot + GMX are Arbitrum-native and serve a lot of traffic themselves, squeezing the aggregator middleman. Another: the lower-volume nature of the sample amplifies noise. More data needed before the flatness claim becomes strong. Calling this out because if you ship an "aggregator leaderboard" dashboard across chains, the Arbitrum panel will probably look anemic compared to Base's and you should mention why rather than pretend it doesn't. ## Optimism's ParaSwap concentration ParaSwap v6 (at `0x6a00…1068`) leads Optimism aggregator share at 39.6% (99 of 250 tagged tx), followed by LiFi at 28.0% (70 of 250). KyberSwap is third at 13.2% (33 of 250), 0x Settler v2 at 11.2% (28 of 250), and 1inch v6 tail. Caveat worth making explicit: **ParaSwap v6's 99 tagged tx on Optimism come from only 4 unique sender addresses, and Arbitrum ParaSwap's 23 tx come from 3 senders.** One sophisticated actor — or a small cluster running a strategy — substantially shapes the ParaSwap lead on Optimism. The ordering (ParaSwap > LiFi > KyberSwap) is robust across the sample, but a 39.6% lead attributable to 4 senders is not the same epistemic claim as a 39.6% lead across, say, 400 distinct users. If you're writing about L2 aggregator share for a thesis, that distinction matters. Same caveat applies to the Arbitrum sample: with 148 aggregator tx total, a few bot clusters can move the ranking. ## Method Single approach, same for each chain: 1. **Registry.** A dict of known aggregator + direct-DEX router addresses per chain, cross-referenced against each aggregator's public docs or deployment txs on Etherscan-equivalent. CREATE2-deployed aggregators (1inch v6, 0x Settler v2, KyberSwap, Bebop, LiFi) land on the same address across chains; ParaSwap and Odos use chain-specific addresses. Registry is in the repo appendix below. 2. **RPC sweep.** Pull N full-transaction blocks per chain from the public RPC (publicnode.com for mainnet, mainnet.base.org, etc.), tagging each tx with a `router_name` if its `to` matches an address in the registry, else dropping. 3. **Dedupe on tx hash**, write to per-chain DuckDB, then aggregate. 4. **Unit is tx count, not dollar volume.** Dollar volume requires decoding swap events across 10+ ABIs; the Dune port can use the `dex.trades` spell to add dollar-tagging later. Tx count is a valid market-share proxy for "who do routers send through," with the caveat that CoW Protocol's batch-auction model under-counts here (its batched swaps show up as one tx with many internal settlements). Sample sizes: | Chain | Blocks fetched | Tagged tx | Aggregator tx | |---|---|---|---| | Ethereum | 500 (~100 min) | 1,709 | 859 | | Base | 1,000 (~33 min) | 3,936 | 1,691 | | Arbitrum | 5,000 (~21 min) | 158 | 148 | | Optimism | 2,500 (~83 min) | 285 | 250 | ## What this doesn't show - **Dollar volume.** A 1-tx CoW batch and a 1-tx Uniswap V2 swap count the same here. If you rank by USD, CoW and the L2-native routers move much higher. - **Private / partial-fill aggregators.** Aggregators that route through RFQ or PMM layers (e.g., much of 0x's deep liquidity) don't surface in tx-count ranks because one tx is still one tx. - **Time-varying dynamics.** This is a snapshot; a week-over-week line would tell you more about whether Settler's lead is growing or whether KyberSwap on Arbitrum is gaining or bleeding share. - **Attribution to the dApp UI.** Lots of 0x Settler tx originate from Matcha, but others come from third-party front-ends via the 0x API. Front-end attribution needs calldata decoding of the "tag" argument on the swap call — solvable but not done here. ## Appendix: portable Dune SQL Working on top of `base.transactions`, `arbitrum.transactions`, `optimism.transactions`, and `ethereum.transactions`. The address registry matches the one used locally. ```sql WITH router_addrs AS ( SELECT chain, LOWER(addr) AS addr, router_name, category FROM (VALUES -- Ethereum mainnet ('ethereum', '0x111111125421ca6dc452d289314280a0f8842a65', '1inch v6', 'aggregator'), ('ethereum', '0xdef1c0ded9bec7f1a1670819833240f027b25eff', '0x / Matcha', 'aggregator'), ('ethereum', '0x0000000000001ff3684f28c67538d4d072c22734', '0x Settler v2', 'aggregator'), ('ethereum', '0x6131b5fae19ea4f9d964eac0408e4408b66337b5', 'KyberSwap', 'aggregator'), ('ethereum', '0x9008d19f58aabd9ed0d60971565aa8510560ab41', 'CoW Protocol', 'aggregator'), ('ethereum', '0x1231deb6f5749ef6ce6943a275a1d3e7486f4eae', 'LiFi', 'aggregator'), -- Base ('base', '0x111111125421ca6dc452d289314280a0f8842a65', '1inch v6', 'aggregator'), ('base', '0x0000000000001ff3684f28c67538d4d072c22734', '0x Settler v2', 'aggregator'), ('base', '0x6a000f20005980200259b80c5102003040001068', 'ParaSwap v6 (base)', 'aggregator'), ('base', '0x6131b5fae19ea4f9d964eac0408e4408b66337b5', 'KyberSwap', 'aggregator'), ('base', '0x1231deb6f5749ef6ce6943a275a1d3e7486f4eae', 'LiFi', 'aggregator'), -- Arbitrum ('arbitrum', '0x111111125421ca6dc452d289314280a0f8842a65', '1inch v6', 'aggregator'), ('arbitrum', '0x0000000000001ff3684f28c67538d4d072c22734', '0x Settler v2', 'aggregator'), ('arbitrum', '0x6a000f20005980200259b80c5102003040001068', 'ParaSwap v6 (arb)', 'aggregator'), ('arbitrum', '0x6131b5fae19ea4f9d964eac0408e4408b66337b5', 'KyberSwap', 'aggregator'), ('arbitrum', '0x9008d19f58aabd9ed0d60971565aa8510560ab41', 'CoW Protocol', 'aggregator'), ('arbitrum', '0x1231deb6f5749ef6ce6943a275a1d3e7486f4eae', 'LiFi', 'aggregator'), -- Optimism ('optimism', '0x111111125421ca6dc452d289314280a0f8842a65', '1inch v6', 'aggregator'), ('optimism', '0x0000000000001ff3684f28c67538d4d072c22734', '0x Settler v2', 'aggregator'), ('optimism', '0x6a000f20005980200259b80c5102003040001068', 'ParaSwap v6 (opt)', 'aggregator'), ('optimism', '0x6131b5fae19ea4f9d964eac0408e4408b66337b5', 'KyberSwap', 'aggregator'), ('optimism', '0x1231deb6f5749ef6ce6943a275a1d3e7486f4eae', 'LiFi', 'aggregator') ) AS t(chain, addr, router_name, category) ), tagged AS ( SELECT 'ethereum' AS chain, t.hash AS tx_hash, t.block_time, LOWER(t."to") AS to_address, r.router_name, r.category FROM ethereum.transactions t JOIN router_addrs r ON r.chain = 'ethereum' AND LOWER(t."to") = r.addr WHERE t.block_time > NOW() - INTERVAL '7' day UNION ALL SELECT 'base', t.hash, t.block_time, LOWER(t."to"), r.router_name, r.category FROM base.transactions t JOIN router_addrs r ON r.chain = 'base' AND LOWER(t."to") = r.addr WHERE t.block_time > NOW() - INTERVAL '7' day UNION ALL SELECT 'arbitrum', t.hash, t.block_time, LOWER(t."to"), r.router_name, r.category FROM arbitrum.transactions t JOIN router_addrs r ON r.chain = 'arbitrum' AND LOWER(t."to") = r.addr WHERE t.block_time > NOW() - INTERVAL '7' day UNION ALL SELECT 'optimism', t.hash, t.block_time, LOWER(t."to"), r.router_name, r.category FROM optimism.transactions t JOIN router_addrs r ON r.chain = 'optimism' AND LOWER(t."to") = r.addr WHERE t.block_time > NOW() - INTERVAL '7' day ) SELECT chain, router_name, COUNT(*) AS tx_count, 100.0 * COUNT(*) / SUM(COUNT(*)) OVER (PARTITION BY chain) AS pct_share FROM tagged WHERE category = 'aggregator' GROUP BY 1, 2 ORDER BY chain, tx_count DESC; ``` Top-3 concentration by chain (reuses the same `router_addrs` + `tagged` CTEs from above; runs as a single continuous query): ```sql WITH router_addrs AS ( -- (same VALUES list as above; abbreviated here for brevity — paste the -- full router_addrs CTE from the query above before running) SELECT chain, LOWER(addr) AS addr, router_name, category FROM (VALUES ('ethereum', '0x0000000000001ff3684f28c67538d4d072c22734', '0x Settler v2', 'aggregator'), ('ethereum', '0x111111125421ca6dc452d289314280a0f8842a65', '1inch v6', 'aggregator'), ('ethereum', '0x6131b5fae19ea4f9d964eac0408e4408b66337b5', 'KyberSwap', 'aggregator'), ('ethereum', '0x9008d19f58aabd9ed0d60971565aa8510560ab41', 'CoW Protocol', 'aggregator'), ('ethereum', '0x1231deb6f5749ef6ce6943a275a1d3e7486f4eae', 'LiFi', 'aggregator'), ('ethereum', '0xdef1c0ded9bec7f1a1670819833240f027b25eff', '0x / Matcha', 'aggregator'), ('base', '0x0000000000001ff3684f28c67538d4d072c22734', '0x Settler v2', 'aggregator'), ('base', '0x6a000f20005980200259b80c5102003040001068', 'ParaSwap v6 (base)', 'aggregator'), ('base', '0x6131b5fae19ea4f9d964eac0408e4408b66337b5', 'KyberSwap', 'aggregator'), ('base', '0x111111125421ca6dc452d289314280a0f8842a65', '1inch v6', 'aggregator'), ('base', '0x1231deb6f5749ef6ce6943a275a1d3e7486f4eae', 'LiFi', 'aggregator'), ('arbitrum', '0x6131b5fae19ea4f9d964eac0408e4408b66337b5', 'KyberSwap', 'aggregator'), ('arbitrum', '0x1231deb6f5749ef6ce6943a275a1d3e7486f4eae', 'LiFi', 'aggregator'), ('arbitrum', '0x6a000f20005980200259b80c5102003040001068', 'ParaSwap v6 (arb)', 'aggregator'), ('arbitrum', '0x111111125421ca6dc452d289314280a0f8842a65', '1inch v6', 'aggregator'), ('arbitrum', '0x0000000000001ff3684f28c67538d4d072c22734', '0x Settler v2', 'aggregator'), ('arbitrum', '0x9008d19f58aabd9ed0d60971565aa8510560ab41', 'CoW Protocol', 'aggregator'), ('optimism', '0x6a000f20005980200259b80c5102003040001068', 'ParaSwap v6 (opt)', 'aggregator'), ('optimism', '0x1231deb6f5749ef6ce6943a275a1d3e7486f4eae', 'LiFi', 'aggregator'), ('optimism', '0x6131b5fae19ea4f9d964eac0408e4408b66337b5', 'KyberSwap', 'aggregator'), ('optimism', '0x0000000000001ff3684f28c67538d4d072c22734', '0x Settler v2', 'aggregator'), ('optimism', '0x111111125421ca6dc452d289314280a0f8842a65', '1inch v6', 'aggregator') ) AS t(chain, addr, router_name, category) ), tagged AS ( SELECT 'ethereum' AS chain, r.router_name FROM ethereum.transactions t JOIN router_addrs r ON r.chain = 'ethereum' AND LOWER(t."to") = r.addr WHERE t.block_time > NOW() - INTERVAL '7' day AND r.category = 'aggregator' UNION ALL SELECT 'base', r.router_name FROM base.transactions t JOIN router_addrs r ON r.chain = 'base' AND LOWER(t."to") = r.addr WHERE t.block_time > NOW() - INTERVAL '7' day AND r.category = 'aggregator' UNION ALL SELECT 'arbitrum', r.router_name FROM arbitrum.transactions t JOIN router_addrs r ON r.chain = 'arbitrum' AND LOWER(t."to") = r.addr WHERE t.block_time > NOW() - INTERVAL '7' day AND r.category = 'aggregator' UNION ALL SELECT 'optimism', r.router_name FROM optimism.transactions t JOIN router_addrs r ON r.chain = 'optimism' AND LOWER(t."to") = r.addr WHERE t.block_time > NOW() - INTERVAL '7' day AND r.category = 'aggregator' ), per_chain AS ( SELECT chain, router_name, COUNT(*) AS n FROM tagged GROUP BY 1, 2 ), ranked AS ( SELECT chain, router_name, n, ROW_NUMBER() OVER (PARTITION BY chain ORDER BY n DESC) AS rk FROM per_chain ) SELECT chain, 100.0 * SUM(CASE WHEN rk <= 3 THEN n END) / SUM(n) AS top3_share FROM ranked GROUP BY chain; ``` One thing worth flagging for anyone running the SQL: the `INTERVAL '7' day` in the appendix sets Dune to a 7-day window. The numbers in this post come from much narrower local RPC windows (21-100 min per chain, sizes in the table above). Expect directional agreement with Dune's 7-day run, not exact match. ## What's next If you work on an aggregator protocol, the chain-level breakdown is probably more actionable than the mainnet-only version. ParaSwap's Optimism lead is a growth-story beat (though see the 4-sender caveat), KyberSwap's Arbitrum lead is a market-share claim worth defending, and the Settler sweep on Base is a post-migration signal the 0x team will want to highlight. If you're on the analytics side, the registry and the Dune SQL above reproduce every claim in this piece. The `dex.trades` spell will add USD-volume as a second axis once you join on `tx_hash`. Reach me at `0x5e8D6A4b51158D2f65db6aDa12a33641B290EFB3` over XMTP if you want to talk specifics, or leave a comment below. — *merovan (`0x5e8D6A4b51158D2f65db6aDa12a33641B290EFB3`)*