DEX aggregator development
A developer integrates swap directly into one Uniswap v3 pool into a dApp — and users complain about poor rates. The reason: for large swaps ($50K+) a single pool's liquidity isn't optimal. Splitting across multiple sources (split routing) can give 0.3-0.8% price improvement. On a $100K swap that's $300-800 difference. This is exactly what an aggregator does — finds the optimal route through multiple DEXs simultaneously.
Technical complexity: routing algorithm
DEX liquidity graph and path finding
Routing task is finding the optimal path in a directed graph where:
- Nodes = tokens
- Edges = pools (each pool creates two edges: token0→token1 and back)
- Edge weight = output amount for given input
For simple swap A→B you need the shortest path (maximizing output). For split routing — you split input into K parts and find K paths that together give maximum output.
Naive approach — enumerate all paths of length 1-3 hops, compare outputs. Works with few pools. With 10,000+ pools (Uniswap v3 on mainnet has >8,000 active pools) — you need optimization.
Practical approach:
- Pre-filtering: only pools with TVL > $100K and volume > $10K in 24h
- Bellman-Ford for finding all paths up to 3 hops
- For split routing: simulate several proportions (100/0, 80/20, 60/40, 50/50) through each route, choose maximum
For EVM chains with high gas (Ethereum mainnet) 3-way split is already impractical: savings from better price can be offset by extra gas. On Arbitrum/Optimism (gas ~$0.01-0.05) split routing is profitable even for small swaps.
Output simulation off-chain
Key requirement: calculate amountOut for each route quickly and accurately without on-chain calls (they're expensive and slow).
Uniswap v2 (x*y=k): analytical formula:
amountOut = (amountIn * 997 * reserveOut) / (reserveIn * 1000 + amountIn * 997)
Reserve data through getReserves() — one RPC call per pool.
Uniswap v3 (concentrated liquidity): no analytical formula for arbitrary amounts. Need to simulate tick-by-tick. QuoterV2.quoteExactInputSingle does this on-chain, but it's an RPC call with gas simulation. For fast routing — use off-chain tick math implementation (@uniswap/v3-sdk) with cached tick data from subgraph.
Curve: get_dy(i, j, dx) — view function, static call. For each Curve pool you need a separate RPC call, but they batch through Multicall3.
Data staleness and solution
Reserve and tick data becomes stale every block. During volatile markets a price can shift significantly in 1-2 blocks. Update strategies:
-
Event subscriptions:
Sync(Uniswap v2),Swap(Uniswap v3/Curve) through WebSocket. Update cache for specific pool on each event - Periodic polling: every 5-10 seconds for less liquid pools
- On-demand refresh: on quote request — update data for top-10 pools of route through Multicall
Our approach: WebSocket events for top-100 pools by TVL, polling every 15 seconds for rest.
Aggregator architecture
On-chain vs Off-chain routing
Fully off-chain: routing engine calculates route, returns ready calldata for swap router. Smart contract — just executor, no path selection logic. This is 1inch v5 Aggregation Router approach. Minimal on-chain gas, but trust to backend.
Hybrid: routing off-chain, on-chain verification of minimum output. Contract receives path + amountOutMinimum, executes through Uniswap/Curve routers, checks require(amountOut >= amountOutMinimum). Revert on mismatch. This is our recommended approach.
Aggregator Router contract
contract AggregatorRouter {
function swap(
SwapParams calldata params
) external payable returns (uint256 amountOut) {
// For each step of the route
for (uint i = 0; i < params.steps.length; i++) {
amountOut = _executeStep(params.steps[i], amountOut);
}
require(amountOut >= params.minAmountOut, "Insufficient output");
// Transfer output tokens to recipient
IERC20(params.tokenOut).safeTransfer(params.recipient, amountOut);
}
}
SwapStep contains: protocol (uniswap_v2/v3/curve/balancer), poolAddress, tokenIn, tokenOut, portion (for split routing — what share goes through this step).
Aggregator commission
Aggregators take fees two ways:
- Spread: show user a quote slightly worse than real, keep the difference. Opaque.
- Explicit fee: take N bps (basis points) from output. Transparent, better for reputation.
Typically: 5-30 bps (0.05-0.30%) depending on swap size. Implemented in contract as feeAmount = amountOut * feeBps / 10000.
Multichain and bridging
Extending aggregator to cross-chain swap: user sends USDC on Ethereum, receives MATIC on Polygon. Under the hood: swap USDC→bridgeToken on Ethereum, bridge through Across/Stargate, swap bridgeToken→MATIC on Polygon.
Integration with Across Protocol v3: SpokePool.deposit() with destination calldata for final swap. Bridge latency: 1-5 minutes. Gas: significantly higher than single swap, worthwhile from $1000+ amount.
Development stack
Backend routing engine: TypeScript, viem for RPC calls, Redis for caching pool data, WebSocket for event subscriptions. Smart contracts: Solidity 0.8.x + Foundry. Frontend: React + wagmi + token import through Uniswap Token Lists standard.
For subgraph data (TVL, volume, Uniswap v3 ticks): The Graph hosted service or custom subgraph on Graph Node.
Work process
Routing engine (1-2 weeks). Pool graph, route finding algorithm, output simulation, caching.
Smart contract (1 week). Aggregator router + mainnet fork tests.
API and frontend (1-2 weeks). Quote API, swap UI with route visualization.
Testing. Quote comparison with 1inch/Paraswap as benchmark.
Timeline estimates
Aggregator for 2-3 DEXs in one chain: 2-3 weeks. Multichain aggregator with cross-chain swap and custom subgraph: 2-3 months.







