Разработка системы маршрутизации ордеров через несколько DEX

Проектируем и разрабатываем блокчейн-решения полного цикла: от архитектуры смарт-контрактов до запуска DeFi-протоколов, NFT-маркетплейсов и криптобирж. Аудит безопасности, токеномика, интеграция с существующей инфраструктурой.
Показано 1 из 1 услугВсе 1306 услуг
Разработка системы маршрутизации ордеров через несколько DEX
Сложная
~1-2 недели
Часто задаваемые вопросы
Направления блокчейн-разработки
Этапы блокчейн-разработки
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1221
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1163
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    855
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1060
  • image_logo-advance_0.png
    Разработка логотипа компании B2B Advance
    561
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    828

Разработка системы маршрутизации ордеров через несколько DEX

1inch в момент запуска показал простую идею: если ты ищешь лучшую цену только на Uniswap — ты оставляешь деньги на столе. За несколько лет агрегаторы выросли до сложных систем с split routing, multi-hop маршрутами и специализированными алгоритмами оптимизации. Но смысл не изменился: задача роутера — найти путь от token A к token B с минимальными потерями при заданном объёме.

Разработка собственной системы маршрутизации нужна когда: стандартные агрегаторы (1inch, Paraswap, 0x) не поддерживают нужный чейн; требуется интеграция кастомных протоколов; нужен контроль над источниками ликвидности; или существующие API слишком медленны для торгового бота.

Граф ликвидности как основа маршрутизации

Маршрутизация — это задача поиска пути в взвешенном направленном графе. Вершины — токены. Рёбра — пулы (каждый пул создаёт два направленных ребра: A→B и B→A с ценой в данном направлении).

Для поиска лучшего пути при фиксированном amountIn — задача поиска пути с максимальным произведением обменных курсов (или эквивалентно — минимальной суммой отрицательных логарифмов). Это модификация алгоритма Беллмана-Форда или Дейкстры.

Но есть нюанс, который делает задачу сложнее: цена в пуле зависит от объёма. Для amountIn = 100 USDC лучший маршрут может быть Uniswap V3 пул 0.05%. Для amountIn = 1,000,000 USDC тот же пул даст 3% slippage, а split между несколькими пулами даст 0.3%.

Это превращает задачу из поиска пути в граф с фиксированными весами в оптимизационную задачу с объёмо-зависимыми весами.

Алгоритм split routing

Для крупных ордеров оптимальное решение — не единый маршрут, а распределение объёма по нескольким путям.

Подход через бинарный поиск оптимального split для двух маршрутов:

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
}

Для N маршрутов задача становится N-мерной оптимизацией — применяют gradient descent или Nelder-Mead с ограничениями (сумма долей = 1, все доли ≥ 0).

Симуляция пулов: точность vs скорость

Uniswap V2: точная формула

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 требует итерации по tick bitmap для нахождения ближайших активных tick-ов. Полная симуляция точна, но медленна — несколько миллисекунд на крупный своп с traversal через множество tick-ов.

Для быстрой оценки (при скриннинге маршрутов) используем приближение через текущий sqrtPriceX96 и liquidity без tick traversal — точно для малых объёмов, с погрешностью для крупных. Точную симуляцию запускаем только для финальных кандидатов.

Curve StableSwap: итерационная формула

Curve использует инвариант A * n^n * sum(x_i) + D = A * D * n^n + D^(n+1) / (n^n * prod(x_i)). Расчёт amountOut — итерационный (Newton's method). Для JavaScript/TypeScript — BigInt арифметика с 18-decimal precision.

Balancer WeightedPool

Balancer с весовыми пулами (например, 80/20 BAL/ETH) использует другой инвариант. getAmountOut зависит от весов токенов в пуле — более сложная формула, чем V2.

On-chain vs off-chain маршрутизация

Маршрутизация может происходить полностью on-chain (смарт-контракт находит маршрут прямо в транзакции) или off-chain (вычисления вне чейна, результат передаётся в контракт).

On-chain маршрутизация: полная прозрачность, невозможность манипуляции со стороны aggregator-а. Проблема: ограниченный gas, нельзя перебрать все маршруты. Применяется для простых случаев (2–3 пула maximum).

Off-chain маршрутизация (подход 1inch, Paraswap): вычисления в backend, контракту передаётся готовый маршрут. Контракт только исполняет. Gas эффективнее, маршрут сложнее. Риск: backend может вернуть субоптимальный маршрут. Защита через slippage protection: minAmountOut в транзакции гарантирует пользователю минимум.

Router контракт: исполнение сложных маршрутов

Контракт должен поддерживать гетерогенные маршруты: часть через Uniswap V2, часть через V3, часть через Curve.

struct SwapStep {
    address pool;
    address tokenIn;
    address tokenOut;
    uint24 fee;       // Для V3
    uint8 dexType;    // 0=V2, 1=V3, 2=Curve, 3=Balancer
    bytes extraData;  // Дополнительные параметры под тип DEX
}

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 диспатчит к конкретной DEX-реализации по dexType. Каждая реализация — отдельная библиотека (Solidity library pattern) для экономии bytecode size.

Кэш состояния пулов

Для быстрой маршрутизации без RPC-вызовов на каждый запрос нужен кэш актуального состояния пулов:

WebSocket subscriptions на события Sync (V2 пулы) и Swap (V3 пулы) через eth_subscribe("logs"). При каждом событии обновляем reserves/sqrtPrice в памяти.

Для 500–1000 активных пулов это ~50–100 событий/блок на Ethereum mainnet. Обработка через event-driven архитектуру (Node.js EventEmitter или Rust tokio channel) с ≤1ms задержкой обновления.

Cold start: при запуске сервиса нужно загрузить текущее состояние всех пулов через multicall. Для 1000 пулов — 5–10 multicall транзакций (до 200 calls каждый), занимает 1–3 секунды.

Сравнение архитектурных подходов

Подход Когда подходит Сложность Latency
Простой multi-hop 3–5 чейнов, топ-5 DEX Низкая 200–500ms
Split routing Крупные ордера ($50K+) Средняя 500ms–1s
С кэшем пулов Торговый бот, < 50ms Высокая 10–50ms
On-chain router Максимальная прозрачность Средняя 1 блок

Процесс работы

Аналитика (1–2 дня). Список целевых DEX и чейнов, требования к latency, ожидаемый объём ордеров.

Разработка routing engine (5–7 дней). Граф пулов, алгоритм поиска пути, симуляция пулов, split routing.

Router контракт (3–5 дней). Multi-step execution, Solidity, Foundry fork-тесты.

Кэш и инфраструктура (3–5 дней при необходимости). WebSocket подписки, in-memory кэш пулов.

Ориентиры по срокам

Базовый off-chain роутер через 3–5 DEX с простым multi-hop — 1 неделя. Полноценная система с split routing, кэшем 500+ пулов, кастомным router контрактом и поддержкой V2/V3/Curve/Balancer — 2–3 недели.