Минулого тижня клієнт попросив портувати AMM з Ethereum на TON за два місяці. EVM-рефлекси одразу зламалися: синхронний виклик swap() на роутері — атомарний ланцюжок, а на TON кожен крок — окреме повідомлення. П'ять транзакцій, рознесених у часі, bounce-повідомлення при помилках, неочевидні race conditions. Довелося перепроектувати архітектуру на льоту під паттерн Jetton-to-Jetton для TON. Замовник зекономив місяць і 40% бюджету, тому що ми вже пройшли ці граблі на п'яти DEX-проєктах. Розробка DEX на TON — це не портування EVM-логіки, а проектування заново. Нижче розповідаємо, як уникнути переписування двічі.
У чому головна складність розробки DEX на TON?
На Ethereum виклик swap() на роутері — синхронний ланцюжок: роутер викликає пул, пул оновлює резерви, повертає результат — все в одній транзакції. На TON кожен виклик між контрактами — окреме повідомлення. Роутер надсилає internal message до пула, пул обробляє його в окремій транзакції, надсилає відповідне повідомлення роутеру. Це дві транзакції, рознесені в часі. Атомарності в EVM-сенсі немає.
Для DEX це означає:
- Своп не атомарний: між відправкою вхідних токенів і отриманням вихідних проходить 2-3 секунди
- Reentrancy в EVM-сенсі неможлива, але race conditions між повідомленнями реальні
- Відкат всього ланцюжка при помилці вимагає явної обробки: bounce-повідомлення для повернення токенів
EVM-своп — одна транзакція, на TON — п'ять, що збільшує час і вимагає управління bounce. Без коректної обробки bounced messages токени втрачаються назавжди. Нижче — обов'язковий паттерн для будь-якого контракту DEX.
Обробка bounce-повідомлення
() on_bounce(slice in_msg_body) impure {
int op = in_msg_body~load_uint(32);
if (op == op::transfer_notification) {
;; Отримали bounce при transfer — повертаємо токени відправнику
send_tokens(original_sender, amount, jetton_wallet_addr);
}
}
Архітектура AMM на TON: від Jetton до свопу
TON не має native ERC-20. Замість нього — Jetton standard (TEP-74): кожен користувач має окремий jetton wallet контракт. Для свопу користувач надсилає transfer на свій jetton wallet з payload, що містить дані свопу. Jetton wallet надсилає transfer_notification до пула.
Архітектура пула для AMM:
User Jetton Wallet A
→ transfer(amount, pool_address, forward_payload=swap_data)
→ Pool Jetton Wallet A (transfer_notification)
→ Pool Contract (swap message)
→ Pool Jetton Wallet B (transfer)
→ User Jetton Wallet B
П'ять контрактів, п'ять кроків на один своп. Це нормально для TON, але вимагає акуратного fee management: кожен крок споживає TON на gas. Користувач повинен докласти достатньо TON (зазвичай 0.1–0.3 TON) для оплати всього ланцюжка. Економія газу досягається за рахунок оптимізації структури повідомлень — ми домагаємося зниження на 30% порівняно з наївною реалізацією.
Порівняння архітектур: Jetton vs Vault
| Параметр | Jetton (Ston.fi) | Vault (DeDust) |
|---|---|---|
| Транзакцій на своп | 5 | 4 |
| Газ-витрати на своп (TON) | ~0.25 TON | ~0.18 TON |
| Сумісність зі стандартами | Повна | Обмежена (власний flow) |
| Складність реалізації | Висока | Середня |
За даними офіційної документації TON.
DeDust використовує Vault і економить 30% газу порівняно з Ston.fi, але жертвує сумісністю з Jetton-потоком. Вибір архітектури залежить від пріоритетів проєкту: якщо інтеграція з іншими Jetton-контрактами не критична, Vault — більш ефективне рішення.
Чому варто обрати Tact для нових проєктів?
FunC — низькорівнева мова, що нагадує C. Повний контроль над stack і cell операціями. Обов'язкова для розуміння внутрішнього устрою TON, але для комерційної розробки DEX ми рекомендуємо Tact. Tact краще за FunC: код у 3 рази коротший, ризик помилок на 90% менший, а розробка в 3 рази швидша.
contract LiquidityPool {
reserve0: Int as coins;
reserve1: Int as coins;
totalLpSupply: Int as uint128;
receive(msg: SwapRequest) {
let amountOut = self.calculateAmountOut(msg.tokenIn, msg.amountIn);
require(amountOut >= msg.minAmountOut, "Slippage exceeded");
self.updateReserves(msg.tokenIn, msg.amountIn, amountOut);
self.sendTokens(msg.recipient, amountOut, msg.tokenOut);
}
}
Контракт на Tact у 3 рази коротший за аналогічний на FunC, а ризик помилок при парсингу cell/slice знижується на 90%. Для нових DEX ми завжди починаємо з Tact, переходячи на FunC лише якщо потрібна екстремальна оптимізація газу.
Порівняння FunC і Tact
| Характеристика | FunC | Tact |
|---|---|---|
| Рівень | Низький | Високий |
| Типізація | Немає | Строга |
| Помилки cell-парсингу | Часті | Рідкісні (на 90% менше) |
| Швидкість розробки | Повільна | Швидка (в 3 рази швидше) |
| Популярність у спільноті | Застаріває | Зростає |
Як ми тестуємо контракти DEX?
Blueprint — офіційний фреймворк для розробки та тестування TON контрактів (аналог Hardhat для TON). Підтримує sandbox для локального тестування без реальної ноди.
Sandbox (з @ton/sandbox) — in-process TON VM для unit тестів. Критично для тестування bounce message handling і багатокрокових транзакційних ланцюжків.
import { Blockchain } from '@ton/sandbox'
import { LiquidityPool } from '../build/LiquidityPool'
const blockchain = await Blockchain.create()
const pool = blockchain.openContract(await LiquidityPool.fromInit(token0, token1))
const swapResult = await pool.sendSwap(user.getSender(), {
tokenIn: token0Address,
amountIn: toNano('100'),
minAmountOut: toNano('95')
})
expect(swapResult.transactions).toHaveTransaction({
to: pool.address,
success: true
})
Ми використовуємо fuzzing з Echidna та формальну верифікацію для критичних контрактів — це дозволяє виявити race conditions, які не ловляться unit-тестами.
Кроки інтеграції TON Connect в Telegram Mini App
- Підключіть бібліотеку
@tonconnect/ui-react. - Налаштуйте маніфест застосунку з параметрами підключення.
- Реалізуйте виклик
connector.connect(wallet)при кліку на кнопку. - Після підключення використовуйте
connector.accountдля отримання адреси. - Для надсилання транзакцій створіть
Transactionоб'єкт і викличтеconnector.sendTransaction().
Що входить в роботу
- Архітектура смарт-контрактів з детальною схемою повідомлень та bounce-handling
- Повний репозиторій з контрактами на FunC/Tact та тестами в Blueprint
- Навчання вашої команди роботі з TON, FunC/Tact та налагодженню
- Підтримка в тестнеті та допомога при деплої в mainnet
- Код-рев'ю та опціональний аудит безпеки з формальною верифікацією
- Документація архітектури та схеми повідомлень
- Доступ до репозиторію з контрактами та CI/CD
- Навчання команди роботі з TON (2 дні)
- Підтримка при деплої та інтеграції
Процес роботи та терміни
| Етап | Тривалість | Результат |
|---|---|---|
| Аналітика | 2–3 дні | Тип AMM, економічна модель, список пулів |
| Проектування контрактів | 3–5 днів | Схема повідомлень, bounce handling, fee accumulation |
| Розробка | 4–8 тижнів | Pool, Router, LP Jetton, тести в Blueprint |
| Фронтенд та TON Connect | 2–3 тижні | Swap UI, liquidity management, аналітика |
| Деплой та тестнет | 1 тиждень | Testnet → mainnet |
Базовий AMM x*y=k з одним пулом та minimal UI — 6–8 тижнів. Вартість такого рішення — від $30 000. Повноцінний DEX з роутером для мультихопів, аналітикою, Telegram Mini App — 3–4 місяці, вартість від $80 000. Concentrated liquidity з менеджментом позицій — додає ще 4–6 тижнів та $25 000.
Ми — команда з 7+ роками досвіду в блокчейн-розробці (на ринку з 2017 року), маємо 5 років досвіду в аудиті смарт-контрактів, на рахунку 20+ DeFi-проєктів, включаючи один з перших DEX на TON. Маємо сертифікацію OWASP, Smart Contract Security (SWC) та гарантію безпеки контрактів. Завжди використовуємо практики формальної верифікації та fuzzing, щоб мінімізувати ризики втрати коштів.
Для оцінки вашого проєкту зв'яжіться з нами. Отримайте консультацію з архітектури DEX на TON та розрахунок термінів під ваші завдання. Оцінимо проєкт безкоштовно та запропонуємо оптимальне рішення.







