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

Проєктуємо та розробляємо блокчейн-рішення повного циклу: від архітектури смарт-контрактів до запуску DeFi-протоколів, NFT-маркетплейсів та криптобірж. Аудит безпеки, токеноміка, інтеграція з наявною інфраструктурою.
Показано 1 з 1Усі 1306 послуг
Розробка системи маршрутизації ордерів через кілька DEX
Складний
~1-2 тижні
Часті запитання

Напрямки блокчейн-розробки

Етапи блокчейн-розробки

Останні роботи

  • image_website-b2b-advance_0.webp
    Розробка сайту компанії B2B ADVANCE
    1285
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1198
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    902
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1121
  • image_logo-advance_0.webp
    Розробка логотипу компанії B2B Advance
    589
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    858

Розроблення системи маршрутизації ордерів через кілька 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 — ітераційний (метод Ньютона). Для JavaScript/TypeScript — BigInt арифметика з 18-decimal точністю.

Balancer WeightedPool

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

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

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

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

Off-chain маршрутизація (підхід 1inch, Paraswap): обчислення в backend, контракту передається готовий маршрут. Контракт тільки виконує. Газ ефективніше, маршрут складніше. Ризик: 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 тижні.