Multi-DEX Order Routing System Development

We design and develop full-cycle blockchain solutions: from smart contract architecture to launching DeFi protocols, NFT marketplaces and crypto exchanges. Security audits, tokenomics, integration with existing infrastructure.
Showing 1 of 1 servicesAll 1306 services
Multi-DEX Order Routing System Development
Complex
~1-2 weeks
FAQ
Blockchain Development Services
Blockchain Development Stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1214
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1161
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    852
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1041
  • image_logo-advance_0.png
    B2B Advance company logo design
    561
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    823

Developing a Multi-DEX Order Routing System

When 1inch launched, it showed a simple idea: if you're only looking for the best price on Uniswap, you're leaving money on the table. Over the years, aggregators evolved into complex systems with split routing, multi-hop routes, and specialized optimization algorithms. But the core idea hasn't changed: a router's job is to find the path from token A to token B with minimal losses at a given volume.

Developing your own routing system is necessary when: standard aggregators (1inch, Paraswap, 0x) don't support your chain; you need custom protocol integration; you need control over liquidity sources; or existing APIs are too slow for a trading bot.

Liquidity Graph as Routing Foundation

Routing is a pathfinding problem in a weighted directed graph. Vertices are tokens. Edges are pools (each pool creates two directed edges: A→B and B→A with the price in each direction).

To find the best path at fixed amountIn — the task is pathfinding with maximum product of exchange rates (or equivalently — minimum sum of negative logarithms). This is a modification of Bellman-Ford or Dijkstra's algorithm.

But there's a nuance that makes it harder: price in a pool depends on volume. For amountIn = 100 USDC the best route might be Uniswap V3 0.05% pool. For amountIn = 1,000,000 USDC that same pool gives 3% slippage, while splitting between several pools gives 0.3%.

This turns pathfinding in a graph with fixed weights into an optimization task with volume-dependent weights.

Split Routing Algorithm

For large orders, the optimal solution is not a single route, but volume distribution across multiple paths.

Approach through binary search for optimal split between two routes:

function findOptimalSplit(
  routeA: Route,
  routeB: Route,
  totalAmount: bigint,
  steps: number = 20
): { splitA: bigint; splitB: bigint; totalOut: bigint } {
  let bestSplit = { splitA: 0n, splitB: totalAmount, totalOut: 0n }
  
  for (let i = 0; i <= steps; i++) {
    const fraction = i / steps
    const amountA = BigInt(Math.floor(Number(totalAmount) * fraction))
    const amountB = totalAmount - amountA
    
    const outA = amountA > 0n ? simulateRoute(routeA, amountA) : 0n
    const outB = amountB > 0n ? simulateRoute(routeB, amountB) : 0n
    const totalOut = outA + outB
    
    if (totalOut > bestSplit.totalOut) {
      bestSplit = { splitA: amountA, splitB: amountB, totalOut }
    }
  }
  
  return bestSplit
}

For N routes, the problem becomes N-dimensional optimization — gradient descent or Nelder-Mead with constraints (sum of fractions = 1, all fractions ≥ 0).

Pool Simulation: Accuracy vs Speed

Uniswap V2: Exact Formula

function getAmountOutV2(amountIn: bigint, reserveIn: bigint, reserveOut: bigint): bigint {
  const amountInWithFee = amountIn * 997n
  const numerator = amountInWithFee * reserveOut
  const denominator = reserveIn * 1000n + amountInWithFee
  return numerator / denominator
}

Uniswap V3: Tick Traversal

V3 requires iterating through tick bitmap to find nearest active ticks. Full simulation is accurate but slow — several milliseconds for large swaps with traversal through many ticks.

For quick estimates (when screening routes) we use approximation through current sqrtPriceX96 and liquidity without tick traversal — accurate for small volumes, with error for large ones. Run full simulation only for final candidates.

Curve StableSwap: Iterative Formula

Curve uses the invariant A * n^n * sum(x_i) + D = A * D * n^n + D^(n+1) / (n^n * prod(x_i)). Calculating amountOut is iterative (Newton's method). For JavaScript/TypeScript — BigInt arithmetic with 18-decimal precision.

Balancer WeightedPool

Balancer with weighted pools (e.g., 80/20 BAL/ETH) uses a different invariant. getAmountOut depends on token weights in the pool — more complex formula than V2.

On-Chain vs Off-Chain Routing

Routing can happen entirely on-chain (smart contract finds route in the transaction) or off-chain (computations off-chain, result passed to contract).

On-chain routing: full transparency, no aggregator manipulation possible. Problem: limited gas, can't check all routes. Used for simple cases (2–3 pools maximum).

Off-chain routing (1inch, Paraswap approach): backend computations, contract gets ready-made route. Contract only executes. More gas-efficient, more complex routes. Risk: backend may return suboptimal route. Protection through slippage protection: minAmountOut in transaction guarantees minimum to user.

Router Contract: Executing Complex Routes

The contract must support heterogeneous routes: partly through Uniswap V2, partly V3, partly Curve.

struct SwapStep {
    address pool;
    address tokenIn;
    address tokenOut;
    uint24 fee;       // For V3
    uint8 dexType;    // 0=V2, 1=V3, 2=Curve, 3=Balancer
    bytes extraData;  // Additional parameters per DEX type
}

function multiSwap(
    SwapStep[] calldata steps,
    uint256 amountIn,
    uint256 minAmountOut,
    address recipient
) external returns (uint256 amountOut) {
    IERC20(steps[0].tokenIn).transferFrom(msg.sender, address(this), amountIn);
    
    uint256 currentAmount = amountIn;
    for (uint256 i = 0; i < steps.length; i++) {
        currentAmount = _executeStep(steps[i], currentAmount);
    }
    
    require(currentAmount >= minAmountOut, "Slippage exceeded");
    IERC20(steps[steps.length-1].tokenOut).transfer(recipient, currentAmount);
    return currentAmount;
}

_executeStep dispatches to specific DEX implementation per dexType. Each implementation is a separate library (Solidity library pattern) to save bytecode size.

Pool State Cache

For fast routing without RPC calls for each request, an in-memory cache of current pool state is needed:

WebSocket subscriptions to Sync events (V2 pools) and Swap events (V3 pools) via eth_subscribe("logs"). Each event updates reserves/sqrtPrice in memory.

For 500–1000 active pools this is ~50–100 events/block on Ethereum mainnet. Process via event-driven architecture (Node.js EventEmitter or Rust tokio channel) with ≤1ms update latency.

Cold start: when launching the service, load current state of all pools via multicall. For 1000 pools — 5–10 multicall transactions (up to 200 calls each), takes 1–3 seconds.

Architectural Approach Comparison

Approach When It Fits Complexity Latency
Simple multi-hop 3–5 chains, top-5 DEXes Low 200–500ms
Split routing Large orders ($50K+) Medium 500ms–1s
With pool cache Trading bot, < 50ms High 10–50ms
On-chain router Maximum transparency Medium 1 block

Work Process

Analytics (1–2 days). List of target DEXes and chains, latency requirements, expected order volume.

Routing engine development (5–7 days). Pool graph, pathfinding algorithm, pool simulation, split routing.

Router contract (3–5 days). Multi-step execution, Solidity, Foundry fork-tests.

Cache and infrastructure (3–5 days if needed). WebSocket subscriptions, in-memory pool cache.

Timeline Guidelines

A basic off-chain router through 3–5 DEXes with simple multi-hop — 1 week. A complete system with split routing, 500+ pool cache, custom router contract supporting V2/V3/Curve/Balancer — 2–3 weeks.