Своп токенов через Ston.fi API

Сделаешь своп Gram → jetton через Ston.fi: симуляция, расчёт slippage, сборка транзакции и отправка через TonConnect.

Своп через Ston.fi

Своп на Ston.fi — это три шага: симулировать маршрут через API, собрать параметры транзакции через SDK, отправить их кошельку (TonConnect). API api.ston.fi обслуживает только mainnet.


Gram → jUSDT: полный цикл

typescript
// @ston-fi/sdk@2.7.0  |  @ston-fi/api@0.32.0  |  @tonconnect/ui-react@2.0.9
import { dexFactory, Client } from "@ston-fi/sdk";
import { StonApiClient } from "@ston-fi/api";
import { useTonConnectUI, useTonAddress } from "@tonconnect/ui-react";
import { toNano } from "@ton/core";

// jUSDT master — mainnet
const JUSDT = "EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs";

const tonClient = new Client({
  endpoint: "https://toncenter.com/api/v2/jsonRPC",
});
const api = new StonApiClient();

export async function swapTonToJusdt(
  userWalletAddress: string,
  sendTransaction: (tx: {
    validUntil: number;
    messages: { address: string; amount: string; payload?: string }[];
  }) => Promise<void>,
) {
  const offerUnits = toNano("1").toString(); // 1 Gram
  const slippageTolerance = "0.01"; // 1% = 0.01

  // 1. Симуляция: маршрут, minAsk, адрес router
  const simulation = await api.simulateSwap({
    offerAddress: "ton",
    askAddress: JUSDT,
    offerUnits,
    slippageTolerance,
  });

  // minAskUnits уже посчитан API с учётом slippage:
  // minAsk ≈ expectedAsk * (1 - slippageTolerance)
  const { router: routerInfo, offerUnits: simOffer, minAskUnits } = simulation;

  const dex = dexFactory(routerInfo);
  const router = tonClient.open(dex.Router.create(routerInfo.address));
  const proxyTon = dex.pTON.create(routerInfo.ptonMasterAddress);

  // 2. Параметры транзакции — только из simulation, не пересчитывай вручную
  const txParams = await router.getSwapTonToJettonTxParams({
    userWalletAddress,
    offerAmount: simOffer,
    minAskAmount: minAskUnits,
    askJettonAddress: simulation.askAddress,
    proxyTon,
    queryId: BigInt(Date.now()),
  });

  // 3. Отправка через TonConnect
  await sendTransaction({
    validUntil: Math.floor(Date.now() / 1000) + 300,
    messages: [
      {
        address: txParams.to.toString(),
        amount: txParams.value.toString(),
        payload: txParams.body?.toBoc().toString("base64"),
      },
    ],
  });

  return { expectedMinAsk: minAskUnits, router: routerInfo.address };
}

В React-компоненте:

typescript
const [tonConnectUI] = useTonConnectUI();
const address = useTonAddress();

await swapTonToJusdt(address, (tx) => tonConnectUI.sendTransaction(tx));

Как это работает

simulateSwap() — off-chain расчёт: путь через пулы, ожидаемый выход, minAskUnits, метаданные router. Без этой симуляции ты не знаешь актуальный router.

slippageTolerance: "0.01" — допустимое проскальзывание 1%. API закладывает его в minAskUnits. Если к моменту исполнения цена ушла сильнее — контракт отклонит своп, а не отдаст худший курс.

dexFactory(routerInfo) — фабрика контрактов под версию router из ответа API. Не импортируй DEX.v2.Router с захардкоженным адресом для mainnet.

getSwapTonToJettonTxParams() — собирает to, value, body для сообщения кошельку. Gram идёт через pTON; имя метода и обёртки исторические.

sendTransaction() — кошелёк подписывает и шлёт сообщение. Между симуляцией и подписью цена может измениться — поэтому minAsk обязателен.

Другие направления

НаправлениеМетод router
Gram → jettongetSwapTonToJettonTxParams
jetton → jettongetSwapJettonToJettonTxParams
jetton → GramgetSwapJettonToTonTxParams

Для jetton→jetton и jetton→Gram в simulateSwap укажи master-адрес offer-jetton вместо "ton". Те же offerUnits / minAskUnits из симуляции передавай в tx-параметры без изменений.

Частые ошибки

Считаешь minAsk сам и округляешь в большую сторону → используй simulation.minAskUnits как есть. Завышенный minAsk гарантирует revert.

Шлёшь своп через минуту после симуляции → резервы ушли, сделка отклонена по slippage. Симулируй непосредственно перед sendTransaction, показывай пользователю свежий quote.

Путаешь единицыofferUnits в минимальных единицах блокчейна (toNano для Gram с 9 decimals). Для jUSDT (6 decimals) 1 USDT = 1000000, не toNano("1").


Что дальше

Материалы gramdocs.tech носят образовательный характер и не являются финансовой, юридической или инвестиционной рекомендацией. Работа с блокчейном TON и токеном Gram связана с рисками потери средств. Правовая информация