Multi-Chain DEX Aggregation: How Lunark Finds the Best Swap Rates
Berke (pzzaworks)
March 27th, 2025
When users ask Lunark to swap tokens, they expect the best rate. But "best rate" is surprisingly complex. Different DEXes have different liquidity, fee structures, and routing algorithms. Uniswap V3 has multiple fee tiers. Some pairs only exist on certain protocols. And all of this varies by network.
Lunark's swap system handles this complexity by aggregating quotes from five DEX protocols across eight networks, then selecting the optimal route automatically.
Each protocol has different strengths. Uniswap V3's concentrated liquidity often provides better rates for popular pairs. Curve excels at stablecoin swaps. SushiSwap might have better liquidity for long-tail tokens.
Uniswap V3 is unique because it has multiple fee tiers for each pair. While the protocol supports four tiers (0.01%, 0.05%, 0.3%, and 1%), the system tries three of them in a specific order: 0.3% (medium) first, then 0.05% (low), then 1% (high). It takes the first successful quote rather than comparing all options:
TypeScript
// Trial order: MEDIUM -> LOW -> HIGH (0.01% tier is not tried)constFEE_TIERS=[3000,500,10000];// 0.3%, 0.05%, 1%asyncfunctiongetUniswapV3Quote( fromToken:string, toToken:string, amount: bigint, chainId:number):Promise<SwapQuote |null>{const quoter =getQuoterContract(chainId);for(const feeTier ofFEE_TIERS){try{const output =await quoter.quoteExactInputSingle({ tokenIn: fromToken, tokenOut: toToken, amountIn: amount, fee: feeTier, sqrtPriceLimitX96:0,});// Return first successful quotereturn{ dex:'uniswap-v3', outputAmount: output.amountOut, feeTier, path:encodePath([fromToken, toToken],[feeTier]),};}catch{// Pool doesn't exist for this fee tier, try nextcontinue;}}returnnull;}
The 0.3% tier is tried first since it's the most common for typical trading pairs. The 0.01% tier is not checked as it's primarily used for stablecoin pairs with very high volume.
Before swapping, the router contract needs approval to spend the user's tokens. The system checks allowance and prepares an approval transaction if needed:
TypeScript
asyncfunctionprepareSwap( fromToken:string, toToken:string, amount:string, context: ToolContext
):Promise<SwapResult>{const quotes =awaitgetSwapQuotes( fromToken, toToken,parseUnits(amount, fromDecimals), context.chainId
);if(quotes.length ===0){return{ error:'No liquidity found for this pair'};}const bestQuote = quotes[0];const routerAddress =getRouterAddress(bestQuote.dex, context.chainId);// Check if approval is neededconst tokenContract =newContract(fromToken,ERC20_ABI, provider);const allowance =await tokenContract.allowance(context.userAddress, routerAddress);let approvalTx =null;if(allowance < bestQuote.inputAmount){ approvalTx ={ to: fromToken, data: tokenContract.interface.encodeFunctionData('approve',[ routerAddress, MaxUint256,// Unlimited approval]),};}const swapTx =buildSwapTransaction( bestQuote, context.userAddress, Math.floor(Date.now()/1000)+20*60,// 20 min deadline50,// 0.5% slippage in basis points);return{ quote: bestQuote, approvalTx, swapTx, alternatives: quotes.slice(1,4),// Show top 3 alternatives};}
Slippage is the difference between expected and actual output. In volatile markets, prices can move between quote and execution. The system uses a default 0.5% slippage tolerance:
A deep dive into how Lunark uses the Astreus framework to create an AI agent that can execute blockchain operations through natural language. Covers the tool system, agent caching, context injection, and the challenges of building autonomous blockchain agents.
How VoidDex's backend finds the best swap routes across multiple DEXes, covering the quote service architecture, route optimization strategies, and fee calculation system.
How Lunark implements real-time message streaming using Socket.IO, including the challenges of React state batching, room-based broadcasting, and coordinating blockchain transactions with chat responses.