Интеграция бота с DeDust SDK (TON)
TON — не EVM-совместимый блокчейн. Его архитектура принципиально отличается от Ethereum: асинхронная модель сообщений, actor model вместо синхронных вызовов, FunC/Tact вместо Solidity. Разработчик, пришедший с EVM опытом, первым делом ищет аналог ethers.js — и находит @ton/ton SDK. Но паттерны взаимодействия совершенно другие, и это становится понятно при первой попытке сделать свап на DeDust.
Как работает DeDust и чем он отличается от Uniswap
DeDust — AMM DEX на TON, использующий архитектуру Volatile Pool (аналог Uniswap V2) и Stable Pool (аналог Curve). Основное отличие от EVM DEX в том, что взаимодействие с пулом происходит через отправку сообщений на контракт кошелька токена, а не напрямую на пул.
Схема свапа TON → Jetton (аналог ERC-20 в TON):
- Отправить TON на
NativeVaultконтракт с payload, содержащим адрес пула и параметры свапа -
NativeVaultпередаёт сообщение вPool -
Poolвыполняет расчёт и отправляет Jetton на адрес получателя
Схема свапа Jetton → TON:
- Отправить
transferсообщение на Jetton Wallet сforward_payloadдля DeDust - Jetton Wallet отправляет
transfer_notificationнаJettonVault -
JettonVaultпередаёт вPool, пул отправляет TON обратно
Ключевой момент: каждый шаг — отдельное on-chain сообщение. Нет атомарности в EVM-смысле. Если какой-то шаг упадёт (недостаточно gas на промежуточном контракте), токены могут «зависнуть» в vault. Поэтому queryId — не просто параметр, а механизм идентификации для bounce-сообщений и отслеживания состояния транзакции.
Интеграция через DeDust SDK
DeDust предоставляет официальный TypeScript SDK @dedust/sdk. Базовый свап через SDK:
import { Factory, MAINNET_FACTORY_ADDR, VaultNative, PoolType, Asset, ReadinessStatus } from "@dedust/sdk";
import { TonClient4, WalletContractV4, internal } from "@ton/ton";
const client = new TonClient4({ endpoint: "https://mainnet-v4.tonhubapi.com" });
const factory = client.open(Factory.createFromAddress(MAINNET_FACTORY_ADDR));
// Получаем адреса vault и пула
const tonVault = client.open(await factory.getNativeVault());
const pool = client.open(await factory.getPool(PoolType.VOLATILE, [
Asset.native(),
Asset.jetton(JETTON_ADDRESS)
]));
// Проверяем готовность пула
if ((await pool.getReadinessStatus()) !== ReadinessStatus.READY) {
throw new Error("Pool not ready");
}
// Отправляем свап
await tonVault.sendSwap(wallet.sender(keyPair.secretKey), {
poolAddress: pool.address,
amount: toNano("1"), // 1 TON
gasAmount: toNano("0.25"),
// limit: минимальное количество токенов на выходе
});
Параметр gasAmount — критичный. Недостаточно gas → сообщение не доходит до пула, TON возвращается через bounce. Избыток → лишние расходы. Для Jetton → TON свапа gas больше: нужно покрыть transfer_notification + обработку в vault + отправку TON обратно. Рекомендуемые значения: 0.25-0.3 TON для TON→Jetton, 0.3-0.4 TON для Jetton→TON.
Отслеживание исполнения транзакции
В отличие от Ethereum, где await tx.wait() подтверждает финальность, в TON нужно отслеживать цепочку сообщений. Транзакция может завершиться успешно, но одно из сообщений в цепочке — с ошибкой.
Паттерн мониторинга через queryId:
const queryId = BigInt(Date.now()); // Уникальный ID
// Передаём queryId в параметры свапа
// Мониторинг через polling транзакций целевого кошелька
async function waitForSwapResult(wallet: Address, queryId: bigint, timeout: number) {
const deadline = Date.now() + timeout;
while (Date.now() < deadline) {
const txs = await client.getTransactions(wallet, { limit: 10 });
const completed = txs.find(tx =>
tx.inMessage?.body.beginParse().loadUint(32) === 0x7362d09c // transfer_notification
// парсим queryId и сравниваем
);
if (completed) return completed;
await sleep(2000);
}
throw new Error("Swap timeout");
}
Для production бота правильнее использовать TON HTTP API v2 с webhooks или IndexerAPI для более надёжного мониторинга событий.
Расчёт slippage и минимального вывода
DeDust использует формулу CPMM (x*y=k) для Volatile Pool. Расчёт ожидаемого вывода:
const [reserve0, reserve1] = await pool.getReserves();
const amountIn = toNano("1");
const fee = 3n; // 0.3% = 30 basis points из 10000
// Формула с fee
const amountInWithFee = amountIn * (10000n - fee);
const amountOut = (amountInWithFee * reserve1) / (reserve0 * 10000n + amountInWithFee);
// Минимальный вывод с slippage tolerance 1%
const minAmountOut = amountOut * 99n / 100n;
Параметр limit в sendSwap принимает именно этот minAmountOut. Если реальный вывод окажется меньше — транзакция отклоняется, TON возвращается через bounce.
Особенности для торгового бота
Nonce и sequence number. В TON нет nonce в EVM-смысле. Вместо него — seqno кошелька. Два параллельных сообщения с одинаковым seqno → второе будет отклонено. Для бота с высокой частотой транзакций нужно либо использовать отдельные кошельки для каждого направления, либо очередь с sequential отправкой.
Мультикошельковая архитектура. Если бот работает на нескольких парах одновременно — рекомендуем отдельный кошелёк на каждую торговую пару. Это избегает seqno конфликтов и упрощает учёт баланса.
TON Connect vs backend signing. Для торгового бота — только backend signing через мнемонику или keystore. TON Connect предназначен для пользовательских dApp, не для автоматических операций.
Ориентиры по срокам
Базовая интеграция с DeDust SDK (один направление свапа, мониторинг) — 3-4 дня. Полноценный торговый бот с двусторонними свапами, slippage защитой, мониторингом позиций — 1 неделя. Стоимость рассчитывается индивидуально.







