На прошлой неделе клиент попросил портировать AMM с Ethereum на TON за два месяца. EVM-рефлексы тут же сломались: синхронный вызов swap() на роутере — атомарная цепочка, а на TON каждый шаг — отдельное сообщение. Пять транзакций, разнесённых во времени, bounce-сообщения при ошибках, неочевидные race conditions. Пришлось перепроектировать архитектуру слёт на паттерне Jetton-to-Jetton под TON. Заказчик сэкономил месяц и 40% бюджета, потому что мы уже прошли эти грабли на пяти DEX-проектах. Разработка DEX на TON — это не портирование EVM-логики, а проектирование заново. Ниже рассказываем, как избежать переписывания дважды.
Разработка DEX на TON требует принципиально иного подхода к межконтрактному взаимодействию. — Документация TON
В чём главная сложность разработки 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) |
| Сложность реализации | Высокая | Средняя |
DeDust использует Vault и экономит 30% газа по сравнению с Ston.fi, но жертвует совместимостью с Jetton-потоком. Выбор архитектуры зависит от приоритетов проекта: если интеграция с другими Jetton-контрактами не критична, Vault — более эффективное решение.
Почему стоит выбрать Tact для новых проектов?
FunC — низкоуровневый язык, напоминающий C. Полный контроль над stack и cell операциями. Обязателен для понимания внутреннего устройства TON, но для коммерческой разработки DEX мы рекомендуем Tact. Tact — высокоуровневый язык с типизацией, struct-ами и более читаемым синтаксисом. Он компилируется в FunC, что даёт производительность низкого уровня без ручного управления ячейками.
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 в 2 раза короче аналогичного на FunC, а риск ошибок при парсинге cell/slice снижается на порядок. Для новых DEX мы всегда начинаем с Tact, переходя на FunC только если требуется экстремальная оптимизация газа.
Сравнение FunC и Tact
| Характеристика | FunC | Tact |
|---|---|---|
| Уровень | Низкий | Высокий |
| Типизация | Нет | Строгая |
| Ошибки cell-парсинга | Частые | Редкие |
| Скорость разработки | Медленная | Быстрая |
| Популярность в сообществе | Устаревает | Растёт |
Как мы тестируем контракты 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
- Код-ревью и опциональный аудит безопасности с формальной верификацией
Процесс работы и сроки
| Этап | Длительность | Результат |
|---|---|---|
| Аналитика | 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 недель. Полноценный DEX с роутером для мультихопов, аналитикой, Telegram Mini App — 3–4 месяца. Concentrated liquidity с менеджментом позиций — добавляет ещё 4–6 недель.
Мы — команда с 5+ годами опыта в блокчейн-разработке, на счету 15+ DeFi-проектов, включая один из первых DEX на TON. Всегда используем практики формальной верификации и fuzzing, чтобы минимизировать риски потери средств.
Для оценки вашего проекта свяжитесь с нами. Получите консультацию по архитектуре DEX на TON и расчёт сроков под ваши задачи. Оценим проект бесплатно и предложим оптимальное решение.







