Only this pageAll pages
Powered by GitBook
1 of 84

ston-fi/docs

User section

Loading...

Loading...

Loading...

Loading...

Loading...

Developer section

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Help

Loading...

unstake from farm

This section contains SDK example for unstaking tokens in Farm

Unstake funds from Farm NFT

import TonWeb from "tonweb";
import { FARM } from "@ston-fi/sdk";

const farm = new FARM.v3.NftItem({
  tonApiClient: new TonWeb.HttpProvider(),
  address: "EQA18JnBVNZZ8Wz-Kn6Mc5cy9pv798Pn8tfScjKw9NLPK3U2", // Farm v3 nft address
});

// Unstake all staked funds from Farm NFT back to the owner's wallet
const txParams = farm.buildUnstakeTxParams({
  queryId: 12345,
});

// To execute the transaction, you need to send a transaction to the blockchain.
// This code will be different based on the wallet you are using to send the tx from
// logging is used for demonstration purposes
console.log({
  to: txParams.to,
  amount: txParams.gasAmount,
  payload: txParams.payload,
});

Farm guide

This section contains SDK reference for Farm contracts

The remaining sections of the documentation will demonstrate specific examples of the Farm usage:

  • stake in farm

  • claim farm rewards

  • unstake from farm

  • destroy farm NFT

Whitepaper

Download pdf Link

Current version: 0.6, released Aug 2023

About

The STON.fi Docs

is a decentralized automated market maker (AMM) built on the TON blockchain providing virtually zero fees, low slippage, an extremely easy interface, and direct integration with TON wallets.

STON.fi is live! See

Website:

Web App:

Github:

Twitter:

Telegram:

Reddit:

This documentation describes the design of the system, provides an explanation of the core concepts, and documents the technical details to enable integration with the protocol.

What is an Automated Market Maker (AMM)?

You could think of an Automated Market Maker (AMM) as a robot always willing to quote you a price so that you can swap from one asset to another.

You can swap without trust or reliance upon a central party.

Anyone can provide liquidity to a liquidity pool, earning fees in exchange for usage of these assets.

This is a shift from conventional markets and centralized exchanges, which typically offer a central limit order book mechanism of exchange.

As a result, AMMs are a fundamental and critical part of any broader Decentralized Finance (DeFi) ecosystem.

Why STON.fi

STON.fi was founded in 2022. It aims at building a user-friendly crypto exchange for mass-adoption through access to Telegram audience. Putting a high premium on the Community, STON.fi represents a DEX with a human face, providing users with fast support and taking into account their opinions.

An architecture of TON blockchain with sharding allows STON.fi DEX users conduct millions of transactions per second.

Transaction sending guide

This section contains a guide for sending transactions in TON blockchain

As a result of the SDK get*TxParams methods calls, you will get the object with the following structure

This object describes to what address the transaction should be sent and what amount of TON to pay for the gas. And body contains information for the contract of what action you want to perform.

Now, to actually send the transaction to the blockchain we need to use some king of sendTransaction method provided by a TON SDK you are using in your project. There is an extensive , and everyone has a slightly different syntax for sending transactions. Usually, you can find the information about how to send the tx on each TON SDK docs, but for a few of them, we got examples right here:

stake in farm

This section contains SDK example for staking tokens in Farm

Staking of the LP tokens in Farm

To execute the transaction, you need to send a transaction with these parameters to the blockchain. This code will be different based on the wallet you are using to send the tx from, so please check our with examples for different libraries.

Farm guide

This section contains SDK reference for Farm contracts

The remaining sections of the documentation will demonstrate specific examples of the Farm usage:

DEX guide

This section contains SDK reference for DEX

The remaining sections of the documentation will demonstrate specific examples of the DEX usage:

via ton

This section contains a guide for sending transactions in TON blockchain using @ton/ton

Ton package uses the sendTransfer method to send a transaction to the blockchain. An example of the usage is on their .

Fees

This section contains fee values used in STON.fi AMM

Swap fees

While doing a swap, thus a trade of your tokens, on STON.fi, you will interact with liquidity pools. This will lead to Price Impact and Slippage and trading fees being charged on your swap.

The trading fee for basic Constant Product Pools is 0.3%:

  • 0.2% fee goes to the liquidity providers as a payment by increasing the size of the pools.

  • 0.1% fee goes to the protocol.

stake in farm

This section contains SDK example for staking tokens in Farm

Staking of the LP tokens in Farm

destroy farm NFT

This section contains SDK example for destroying unstaked farm NFT

Destroy of the Farm NFT is a transfer of the Farm NFT from the user wallet to the zero address.

To execute the transaction, you need to send a transaction with these parameters to the blockchain. This code will be different based on the wallet you are using to send the tx from, so please check our with examples for different libraries.

refund liquidity

This section contains SDK example for refunding undeposited liquidity in DEX

Refund tokens deposited on liquidity pool account but not added to a liquidity pool yet.

burn liquidity tokens

This section contains SDK example for withdrawing liquidity from DEX

Free liquidity by buring liquidity pool tokens.

Transaction sending guide

This section contains a guide for sending transactions in TON blockchain

As a result of the SDK build*TxParams methods calls, you will get the object with the following structure

This object describes to what address the transaction should be sent and what amount of TON to pay for the gas. And payload contains information for the contract of what action you want to perform.

Now, to actually send the transaction to the blockchain we need to use some king of sendTransaction method provided by a TON SDK you are using in your project. There is an extensive , and everyone has a slightly different syntax for sending transactions. Usually, you can find the information about how to send the tx on each TON SDK docs, but for a few of them, we got examples right here:

Contact Us

Follow us on

Join our community on

Join our community on

Join our community on

Browse

import TonWeb from "tonweb";
import { FARM } from "@ston-fi/sdk";

const farm = new FARM.v3.NftMinter({
  tonApiClient: new TonWeb.HttpProvider(),
  address: "EQATBSfNArrEFmchmy1XabKmxMNp9KezscqjPzfmstCU7VXO", // STON/TON Farm v3
});

// Stake 1 STON/TON LP token in v3 farm
const txParams = farm.buildStakeTxParams({
  userWalletAddress: "", // ! replace with your address
  jettonAddress: "EQDtZHOtVWaf9UIU6rmjLPNLTGxNLNogvK5xUZlMRgZwQ4Gt", // STON/TON pool LP token address
  jettonAmount: "1000000000",
  queryId: 12345,
});

// To execute the transaction, you need to send a transaction to the blockchain.
// This code will be different based on the wallet you are using to send the tx from
// logging is used for demonstration purposes
console.log({
  to: txParams.to,
  amount: txParams.gasAmount,
  payload: txParams.payload,
});
import TonWeb from 'tonweb';

import { Router, ROUTER_REVISION, ROUTER_REVISION_ADDRESS } from '@ston-fi/sdk';

/**
 * This example shows how to refund liquidity from the your lp-account
 */
(async () => {
  const WALLET_ADDRESS = ''; // ! replace with your address
  const JETTON0 = 'EQA2kCVNwVsil2EM2mB0SkXytxCqQjS4mttjDpnXmwG9T6bO'; // STON
  const JETTON1 = 'EQBynBO23ywHy_CgarY9NK9FTz0yDsG82PtcbSTQgGoXwiuA'; // jUSDT

  const provider = new TonWeb.HttpProvider();

  const router = new Router(provider, {
    revision: ROUTER_REVISION.V1,
    address: ROUTER_REVISION_ADDRESS.V1,
  });

  const pool = await router.getPool({
    jettonAddresses: [JETTON0, JETTON1],
  });

  if (!pool) {
    throw Error(`Pool for ${JETTON0}/${JETTON1} not found`);
  }

  const lpAccount = await pool.getLpAccount({ ownerAddress: WALLET_ADDRESS });

  if (!lpAccount) {
    throw Error(
      `LpAccount for ${WALLET_ADDRESS} at ${JETTON0}/${JETTON1} pool not found`,
    );
  }

  // transaction to refund all tokens from JETTON0/JETTON1 lp-account contract
  const refundTxParams = await lpAccount.buildRefundTxParams({
    // query id to identify your transaction in the blockchain (optional)
    queryId: 12345,
  });

  // to execute the transaction you need to send transaction to the blockchain
  // (replace with your wallet implementation, logging is used for demonstration purposes)
  console.log({
    to: refundTxParams.to,
    amount: refundTxParams.gasAmount,
    payload: refundTxParams.payload,
  });
})();
import TonWeb from 'tonweb';

import { Router, ROUTER_REVISION, ROUTER_REVISION_ADDRESS } from '@ston-fi/sdk';

/**
 * This example shows how to burn LP tokens and get back your liquidity
 */
(async () => {
  const WALLET_ADDRESS = ''; // ! replace with your address
  const JETTON0 = 'EQA2kCVNwVsil2EM2mB0SkXytxCqQjS4mttjDpnXmwG9T6bO'; // STON
  const JETTON1 = 'EQBynBO23ywHy_CgarY9NK9FTz0yDsG82PtcbSTQgGoXwiuA'; // jUSDT

  const provider = new TonWeb.HttpProvider();

  const router = new Router(provider, {
    revision: ROUTER_REVISION.V1,
    address: ROUTER_REVISION_ADDRESS.V1,
  });

  const pool = await router.getPool({
    jettonAddresses: [JETTON0, JETTON1],
  });

  if (!pool) {
    throw Error(`Pool for ${JETTON0}/${JETTON1} not found`);
  }

  const lpTokenWallet = await pool.getJettonWallet({
    ownerAddress: WALLET_ADDRESS,
  });
  const lpTokenWalletData = await lpTokenWallet.getData();

  // transaction to burn all LP tokens
  const burnTxParams = await pool.buildBurnTxParams({
    // amount of LP tokens to burn
    amount: lpTokenWalletData.balance, // all LP tokens
    // address to receive the liquidity
    responseAddress: WALLET_ADDRESS,
    // query id to identify your transaction in the blockchain (optional)
    queryId: 12345,
  });

  // to execute the transaction you need to send transaction to the blockchain
  // (replace with your wallet implementation, logging is used for demonstration purposes)
  console.log({
    to: burnTxParams.to,
    amount: burnTxParams.gasAmount,
    payload: burnTxParams.payload,
  });
})();
{
    to: Address;
    value: bigint;
    body?: Cell | null;
}
list of TON SDKs
ton
tonweb
tonconnect
import { TonClient, toNano } from "@ton/ton";
import { FARM } from "@ston-fi/sdk";

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

const farm = client.open(FARM.v3.NftMinter.create(
  "EQATBSfNArrEFmchmy1XabKmxMNp9KezscqjPzfmstCU7VXO", // STON/TON Farm v3
));

// Stake 1 STON/TON LP token in v3 farm
const txParams = await farm.getStakeTxParams({
  userWalletAddress: "", // ! replace with your address
  jettonAddress: "EQDtZHOtVWaf9UIU6rmjLPNLTGxNLNogvK5xUZlMRgZwQ4Gt", // STON/TON pool LP token address
  jettonAmount: toNano("1"),
  queryId: 12345,
});
doc section about transaction sending guide
stake in farm
claim farm rewards
unstake from farm
destroy farm NFT
perform a swap operation
provide liquidity
refund liquidity
burn liquidity tokens
import { TonClient, WalletContractV4, internal, toNano } from "@ton/ton";
import { mnemonicToPrivateKey } from "@ton/crypto";
import { DEX, pTON } from "@ston-fi/sdk";

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

const mnemonics = Array.from(
  { length: 24 },
  (_, i) => `your mnemonic word ${i + 1}`
); // replace with your mnemonic
const keyPair = await mnemonicToPrivateKey(mnemonics);

const workchain = 0;
const wallet = WalletContractV4.create({
  workchain,
  publicKey: keyPair.publicKey,
});
const contract = client.open(wallet);

const dex = client.open(new DEX.v1.Router());

// swap 1 TON for a STON but not less than 0.1 STON
const txArgs = {
  offerAmount: toNano("1"),
  askJettonAddress: "EQA2kCVNwVsil2EM2mB0SkXytxCqQjS4mttjDpnXmwG9T6bO",
  minAskAmount: toNano("0.1"),
  proxyTon: new pTON.v1(),
  userWalletAddress: wallet.address.toString(),
};

// you can instantly send the transaction using the router method with send suffix
await dex.sendSwapTonToJetton(contract.sender(keyPair.secretKey), txArgs);

// or you can get the transaction parameters
const txParams = await dex.getSwapTonToJettonTxParams(txArgs);

// and send it manually later
await contract.sendTransfer({
  seqno: await contract.getSeqno(),
  secretKey: keyPair.secretKey,
  messages: [internal(txParams)],
});
README
STON.fi
import { TonClient } from "@ton/ton";
import { FARM } from "@ston-fi/sdk";

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

const farmNft = client.open(FARM.v3.NftItem.create(
  "EQA18JnBVNZZ8Wz-Kn6Mc5cy9pv798Pn8tfScjKw9NLPK3U2", // Farm v3 nft address
));

// Destroy farm NFT from the owner's wallet
const txParams = await farmNft.getDestroyTxParams({
  queryId: 12345,
});
doc section about transaction sending guide
{
    to: Address;
    payload: Cell;
    gasAmount: BN;
}
list of TON SDKs
ton
tonweb
tonconnect
STON.fi
app.ston.fi
https://ston.fi/
https://app.ston.fi/
https://github.com/ston-fi/
@ston_fi
@stonfidex
https://reddit.com/r/STONFi/
Twitter
Telegram
Discord
Reddit
STON.fi

burn liquidity tokens

This section contains SDK example for withdrawing liquidity from DEX

Burn all liquidity tokens to free liquidity from a pool

import { TonClient } from "@ton/ton";
import { DEX } from "@ston-fi/sdk";

const USER_WALLET_ADDRESS = ""; // ! replace with your address

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

const router = client.open(new DEX.v1.Router());

const pool = client.open(await router.getPool({
  token0: "EQA2kCVNwVsil2EM2mB0SkXytxCqQjS4mttjDpnXmwG9T6bO", // STON
  token1: "EQBX6K9aXVl3nXINCyPPL86C4ONVmQ8vK360u6dykFKXpHCa", // GEMSTON
}));

const lpTokenWallet = client.open(await pool.getJettonWallet({
  ownerAddress: USER_WALLET_ADDRESS,
}));

const lpTokenWalletData = await lpTokenWallet.getWalletData();

const txParams = await pool.getBurnTxParams({
  amount: lpTokenWalletData.balance,
  responseAddress: USER_WALLET_ADDRESS,
  queryId: 12345,
});

To execute the transaction, you need to send a transaction with these parameters to the blockchain. This code will be different based on the wallet you are using to send the tx from, so please check our doc section about transaction sending guide with examples for different libraries.

DEX v1 guide

This section contains SDK reference for DEX

The remaining sections of the documentation will demonstrate specific examples of the DEX usage:

  • methods reference

  • perform a swap operation

  • provide liquidity

  • refund liquidity

  • burn liquidity tokens

Referral Fees

DEX v1 pays the referrer fee directly inside each swap transaction. The rate is fixed at 0.1 % and the specified referrer_address receives the tokens immediately.

Track payouts using the Stats & Vaults REST API:

  • GET /v1/stats/fee_accruals – shows all operations leading to fee accrual for the referrer, filterable by period

  • GET /v1/stats/fee_withdrawals – lists withdrawals from the referrer's vaults by period

  • GET /v1/stats/fees – returns aggregated referral fee information such as total accrued USD value

See the Omniston referral fees guide (remember: although the guide focuses on Omniston, the linked paragraph explains DEX V1 referral fees in detail). Full API docs are available in the Swagger UI.

refund liquidity

This section contains SDK example for refunding undeposited liquidity in DEX

Refund tokens that were sent to LP account but weren't added to a liquidity pool yet

import { TonClient } from "@ton/ton";
import { DEX } from "@ston-fi/sdk";

const client = new TonClient({
  endpoint: "https://testnet.toncenter.com/api/v2/jsonRPC",
});

const router = client.open(
  DEX.v2_1.Router.CPI.create(
    "kQALh-JBBIKK7gr0o4AVf9JZnEsFndqO0qTCyT-D-yBsWk0v" // CPI Router v2.1.0
  )
);

const pool = client.open(
  await router.getPool({
    token0: "kQDLvsZol3juZyOAVG8tWsJntOxeEZWEaWCbbSjYakQpuYN5", // TestRED
    token1: "kQB_TOJSB7q3-Jm1O8s0jKFtqLElZDPjATs5uJGsujcjznq3", // TestBLUE
  })
);

const lpAccount = client.open(
  await pool.getLpAccount({
    ownerAddress: "", // ! replace with your address
  })
);

const txParams = await lpAccount.getRefundTxParams({
  queryId: 12345,
});

To execute the transaction, you need to send a transaction with these parameters to the blockchain. This code will be different based on the wallet you are using to send the tx from, so please check our doc section about transaction sending guide with examples for different libraries.

Glossary

This section contains glossary with common terminology used throughout this documentation

Automated Market Maker / "AMM"

An automated market maker is a smart contract on TON Blockchain that holds liquidity reserves. Users can trade against these reserves at prices determined by a fixed formula. Anyone may contribute liquidity to these smart contracts, earning pro-rata trading fees in return.

Asset

While a digital asset can take many forms, the STON.fi Protocol supports jetton pairs.

Liquidity Provider / "LP"

A liquidity provider is someone who deposits jettons into a given liquidity pool. Liquidity providers take on price risk and are compensated with trading fees.

Liquidity

Digital assets that are stored in a STON.fi pool contract, and are able to be traded against by traders.

Pair

A smart contract deployed from a STON.fi that enables trading between two jettons.

Pool

A contract deployed by the STON.fi protocol that pairs two assets.

Mid Price

The price between the available buy and sell prices. In STON.fi protocol, this is the ratio of the two jettons reserves.

Price Impact

The difference between the mid-price and the execution price of a trade.

Protocol Fees

Fees that are rewarded to the protocol itself, rather than to liquidity providers.

Swap Fees

Fees collected upon swapping which are rewarded to liquidity providers.

Slippage

An amount the price moves in a trading pair between when a transaction is submitted and when it is executed.

Basic token unit

A basic token unit is a minimum amount of a certain token, e.g. for $TON a basic token unit is nanoTon. In order to convert an amount of tokens to their representation in basic token units one must multiply an amount of tokens and 10 to the power of number of decimal places used for the token: 1 $TON = 1000000000 nanoTons, 1 $USDC = 1000000 basic USDC token units, etc.

Impermanent loss

Impermanent loss happens when you provide liquidity to a liquidity pool and the price of your deposited assets changes compared to when you deposited them. The bigger this change is, the more you are exposed to impermanent loss. In this case, the loss means less dollar value at the time of withdrawal than at the time of deposit. Pools that contain assets that remain in a relatively small price range will be less exposed to impermanent loss. Stablecoins or different wrapped (pegged) versions of a token, for example, will stay in a relatively contained price range. Impermanent Loss (IL) is a risk associated with providing liquidity to an Automated Market Maker (AMM). As a result, the AMM compensates liquidity providers with fees generated from trades from the pool.

withdraw fee from vault

This section contains SDK example for performing fee withdrawal from the vault contract

const client = new TonClient({
  endpoint: "https://testnet.toncenter.com/api/v2/jsonRPC",
});

const router = client.open(
  DEX.v2_1.Router.CPI.create(
    "kQALh-JBBIKK7gr0o4AVf9JZnEsFndqO0qTCyT-D-yBsWk0v" // CPI Router v2.1.0
  )
);

const vault = client.open(
  await router.getVault({
    tokenMinter: "kQDLvsZol3juZyOAVG8tWsJntOxeEZWEaWCbbSjYakQpuYN5", // TestRED
    user: "" // ! replace with your address
  })
);

const txParams = await vault.getWithdrawFeeTxParams({
  queryId: 12345,
});

To execute the transaction, you need to send a transaction with these parameters to the blockchain. This code will be different based on the wallet you are using to send the tx from, so please check our doc section about transaction sending guide with examples for different libraries.

For a comprehensive overview of referral fees, see the Omniston referral fees guide (remember: it is an Omniston-focused guide, but the highlighted paragraph details the DEX V2 referral process).

burn liquidity tokens

This section contains SDK example for withdrawing liquidity from DEX

Burn all liquidity tokens to free liquidity from a pool

import { TonClient } from "@ton/ton";
import { DEX } from "@ston-fi/sdk";

const USER_WALLET_ADDRESS = ""; // ! replace with your address

const client = new TonClient({
  endpoint: "https://testnet.toncenter.com/api/v2/jsonRPC",
});

const router = client.open(
  DEX.v2_1.Router.CPI.create(
    "kQALh-JBBIKK7gr0o4AVf9JZnEsFndqO0qTCyT-D-yBsWk0v" // CPI Router v2.1.0
  )
);

const pool = client.open(
  await router.getPool({
    token0: "kQDLvsZol3juZyOAVG8tWsJntOxeEZWEaWCbbSjYakQpuYN5", // TestRED
    token1: "kQB_TOJSB7q3-Jm1O8s0jKFtqLElZDPjATs5uJGsujcjznq3", // TestBLUE
  })
);

const lpWallet = client.open(
  await pool.getJettonWallet({
    ownerAddress: USER_WALLET_ADDRESS,
  })
);

const lpWalletData = await lpWallet.getWalletData();

const txParams = await pool.getBurnTxParams({
  amount: lpWalletData.balance,
  userWalletAddress: USER_WALLET_ADDRESS,
  queryId: 12345,
});

To execute the transaction, you need to send a transaction with these parameters to the blockchain. This code will be different based on the wallet you are using to send the tx from, so please check our doc section about transaction sending guide with examples for different libraries.

claim farm rewards

This section contains SDK example for claiming rewards in Farm

Claim rewards from Farm NFT

import { TonClient } from "@ton/ton";
import { FARM } from "@ston-fi/sdk";

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

const farmNft = client.open(FARM.v3.NftItem.create(
  "EQA18JnBVNZZ8Wz-Kn6Mc5cy9pv798Pn8tfScjKw9NLPK3U2", // Farm v3 nft address
));

// Unstake all staked funds from Farm NFT back to the owner's wallet
const txParams = await farmNft.getClaimRewardsTxParams({
  queryId: 12345,
});

To execute the transaction, you need to send a transaction with these parameters to the blockchain. This code will be different based on the wallet you are using to send the tx from, so please check our doc section about transaction sending guide with examples for different libraries.

claim farm rewards

This section contains SDK example for claiming rewards in Farm

Claim rewards from Farm NFT

import TonWeb from "tonweb";
import { FARM } from "@ston-fi/sdk";

const farm = new FARM.v3.NftItem({
  tonApiClient: new TonWeb.HttpProvider(),
  address: "EQA18JnBVNZZ8Wz-Kn6Mc5cy9pv798Pn8tfScjKw9NLPK3U2", // Farm v3 nft address
});

// Claim occurred farming rewards from the NFT to the owner's wallet
const txParams = farm.buildClaimRewardsTxParams({
  queryId: 12345,
});

// To execute the transaction, you need to send a transaction to the blockchain.
// This code will be different based on the wallet you are using to send the tx from
// logging is used for demonstration purposes
console.log({
  to: txParams.to,
  amount: txParams.gasAmount,
  payload: txParams.payload,
});

via ton

This section contains a guide for sending transactions in TON blockchain using @ton/ton

Ton package uses the sendTransfer method to send a transaction to the blockchain. An example of the usage is on their README.

import TonWeb from "tonweb";
import { TonClient, WalletContractV4, internal, toNano, Cell } from "@ton/ton";
import { mnemonicToPrivateKey } from "@ton/crypto";
import { DEX, pTON } from "@ston-fi/sdk";

const MNEMONIC = Array.from({ length: 24 }, (_, i) => `your mnemonic word ${i + 1}`); // replace with your mnemonic
const TON_API_URL = "https://toncenter.com/api/v2/jsonRPC";

const client = new TonClient({
    endpoint: TON_API_URL,
});

const keyPair = await mnemonicToPrivateKey(MNEMONIC);

const wallet = WalletContractV4.create({
    workchain: 0,
    publicKey: keyPair.publicKey,
});

const contract = client.open(wallet);

const dex = new DEX.v1.Router({
    tonApiClient: new TonWeb.HttpProvider(TON_API_URL),
})

const swapTxParams = await dex.buildSwapTonToJettonTxParams({
    offerAmount: toNano('1'), // swap 1 TON
    askJettonAddress: 'EQA2kCVNwVsil2EM2mB0SkXytxCqQjS4mttjDpnXmwG9T6bO', // for a STON
    minAskAmount: toNano('0.1'), // but not less than 0.1 STON
    proxyTonAddress: pTON.v1.address.toString(),
    userWalletAddress: wallet.address.toString(),
});

await contract.sendTransfer({
    seqno: await contract.getSeqno(),
    secretKey: keyPair.secretKey,
    messages: [internal({
        to: swapTxParams.to.toString(),
        value: BigInt(swapTxParams.gasAmount.toString()),
        body: Cell.fromBase64(TonWeb.utils.bytesToBase64(await swapTxParams.payload.toBoc())),
    })]
});

create a custom router revision

This section describes how to create a custom router revision

Create a custom router revision.

import TonWeb from 'tonweb';
import {
  LpAccountRevisionV1,
  PoolRevision,
  PoolRevisionV1,
  Router,
  RouterRevision,
  RouterRevisionV1,
} from '@ston-fi/sdk';

/**
 * This example shows how to create custom revision
 * for the router, pool, and lp-account classes
 */

class MyRouterRevision extends RouterRevisionV1 {
  // here you can override any method from default revision with your own implementation

  // if you will need custom pool revision, you need to override constructPoolRevision method
  public override constructPoolRevision: RouterRevision['constructPoolRevision'] = (
    router,
  ) => new MyPoolRevision();
}

class MyPoolRevision extends PoolRevisionV1 {
  // here you can override any method from default revision with your own implementation

  // if you will need custom lp account revision, you need to override constructLpAccountRevision method
  public override constructLpAccountRevision: PoolRevision['constructLpAccountRevision'] =
    (pool) => new MyLpAccountRevision();
}

class MyLpAccountRevision extends LpAccountRevisionV1 {
  // here you can override any method from default revision with your own implementation
}

const customRouter = new Router(new TonWeb.HttpProvider(), {
  revision: new MyRouterRevision(),
  address: 'EQB3ncyBUTjZUA5EnFKR5_EnOMI9V1tTEAAPaiU71gc4TiUt',
});

refund liquidity

This section contains SDK example for refunding undeposited liquidity in DEX

Refund tokens that were sent to LP account but weren't added to a liquidity pool yet

import TonWeb from "tonweb";
import { DEX } from "@ston-fi/sdk";

const USER_WALLET_ADDRESS = ""; // ! replace with your address
const JETTON_0_ADDRESS = "EQA2kCVNwVsil2EM2mB0SkXytxCqQjS4mttjDpnXmwG9T6bO"; // STON
const JETTON_1_ADDRESS = "EQBX6K9aXVl3nXINCyPPL86C4ONVmQ8vK360u6dykFKXpHCa"; // GEMSTON

const router = new DEX.v1.Router({
  tonApiClient: new TonWeb.HttpProvider(),
});

const pool = await router.getPool({
  token0: JETTON_0_ADDRESS,
  token1: JETTON_1_ADDRESS,
});

if (!pool) {
  throw Error(`Pool for ${JETTON_0_ADDRESS}/${JETTON_1_ADDRESS} not found`);
}

const lpAccount = await pool.getLpAccount({ ownerAddress: USER_WALLET_ADDRESS });

if (!lpAccount) {
  throw Error(
    `LpAccount for ${USER_WALLET_ADDRESS} at ${JETTON_0_ADDRESS}/${JETTON_1_ADDRESS} pool not found`
  );
}

const txParams = await lpAccount.buildRefundTxParams({
  queryId: 12345,
});

// To execute the transaction, you need to send a transaction to the blockchain.
// This code will be different based on the wallet you are using to send the tx from
// logging is used for demonstration purposes
console.log({
  to: txParams.to,
  amount: txParams.gasAmount,
  payload: txParams.payload,
});

v0.5 (deprecated)

This section contains information about SDK for STON.fi protocol

v0.5 version of the SDK is deprecated. Please use latest SDK version instead

STON.fi SDK

Welcome to the STON.fi SDK docs section.

  • SDK source code

  • SDK on NPM

Getting Started

Overview

The SDK is written in TypeScript and designed to be a thin wrapper on top of the STON.fi contracts, which will help STON.fi protocol to be used more easily in JS/TS projects

The SDK reflects the structure of contracts so that access to contract methods is possible through the use of the corresponding class from the SDK.

TonWeb

SDK is built based on the TonWeb library, which provides primitives for working with data on the TON blockchain, as well as bare contract instances that our SDK expands with unique contract methods

The TonWeb library allows you to make on-chain requests and for that uses toncenter API with a rate limit of 1 request per second. You can obtain higher limits by:

  • requesting an API key from toncenter with a 10 request per minute limit

  • use open TON API instance, for example, from ORBS

  • found a paid instance of TON API with suitable rate limits for your case

  • selfhost an instance of TON API

Installation

To install SDK via npm use:

npm install tonweb @ston-fi/sdk

To install SDK with yarn use:

yarn add tonweb @ston-fi/sdk

To install SDK with pnpm use:

pnpm install tonweb @ston-fi/sdk

Usage

We recommend you check the documentation sections dedicated to the architecture of contracts and their methods to get more context about STON.fi protocol in general before starting to interact with the contracts via the SDK

The remaining sections of the documentation will demonstrate specific examples of the SDK usage:

  • DEX

  • Farm

LpWallet

This section contains api reference of the lp wallet contract

This is a standard Jetton token wallet for holding liquidity tokens. Only specific modifications for this implementation will be described.

User message handlers

burn (0x595f07bc)

Burn an amount of liquidity tokens.

TL-B

burn#595f07bc query_id:uint64 amount:Grams response_destination:MsgAddress custom_payload:Maybe ^Cell = InternalMsgBody;

Message body

Name
Type
Description

op

uint32

Operation code is equal to burn

query_id

uint64

Query id

amount

coins

Amount of coins to burn (in basic token units)

response_destination

address

Address of a user

custom_payloads

maybe_ref

Payloads for token0 and token1

custom_payloads

Name
Type
Description

payload_0

maybe_ref

Payload used for amount0; can be cross_swap

payload_1

maybe_ref

Payload used for amount1; can be cross_swap

Outgoing messages

Sends a message with burn_notification_ext op code to the router contract with the amount of token burnt.

burn liquidity tokens

This section contains SDK example for withdrawing liquidity from DEX

Burn all liquidity tokens to free liquidity from a pool

import TonWeb from "tonweb";
import { DEX } from "@ston-fi/sdk";

const USER_WALLET_ADDRESS = ""; // ! replace with your address
const JETTON_0_ADDRESS = "EQA2kCVNwVsil2EM2mB0SkXytxCqQjS4mttjDpnXmwG9T6bO"; // STON
const JETTON_1_ADDRESS = "EQBX6K9aXVl3nXINCyPPL86C4ONVmQ8vK360u6dykFKXpHCa"; // GEMSTON

const router = new DEX.v1.Router({
  tonApiClient: new TonWeb.HttpProvider(),
});

const pool = await router.getPool({
  token0: JETTON_0_ADDRESS,
  token1: JETTON_1_ADDRESS,
});

if (!pool) {
  throw Error(`Pool for ${JETTON_0_ADDRESS}/${JETTON_1_ADDRESS} not found`);
}

const lpTokenWallet = await pool.getJettonWallet({
  ownerAddress: USER_WALLET_ADDRESS,
});
const lpTokenWalletData = await lpTokenWallet.getData();

const txParams = await pool.buildBurnTxParams({
  amount: lpTokenWalletData.balance,
  responseAddress: USER_WALLET_ADDRESS,
  queryId: 12345,
});

// To execute the transaction, you need to send a transaction to the blockchain.
// This code will be different based on the wallet you are using to send the tx from
// logging is used for demonstration purposes
console.log({
  to: txParams.to,
  amount: txParams.gasAmount,
  payload: txParams.payload,
});

destroy farm NFT

This section contains SDK example for destroying unstaked farm NFT

Destroy of the Farm NFT is a transfer of the Farm NFT from the user wallet to the zero address.

import TonWeb from "tonweb";
import { FARM } from "@ston-fi/sdk";

const farm = new FARM.v3.NftItem({
  tonApiClient: new TonWeb.HttpProvider(),
  address: "EQA18JnBVNZZ8Wz-Kn6Mc5cy9pv798Pn8tfScjKw9NLPK3U2", // Farm v3 nft address
});

// Destroy farm NFT from the owner's wallet
const txParams = farm.buildDestroyTxParams({
  queryId: 12345,
});

// To execute the transaction, you need to send a transaction to the blockchain.
// This code will be different based on the wallet you are using to send the tx from
// logging is used for demonstration purposes
console.log({
  to: txParams.to,
  amount: txParams.gasAmount,
  payload: txParams.payload,
});

LpWallet

This section contains api reference of the lp wallet contract

This is a standard Jetton token wallet for holding liquidity tokens. Only specific modifications for this implementation will be described.

User message handlers

burn (0x595f07bc)

Burn an amount of liquidity tokens.

TL-B

burn#595f07bc query_id:uint64 amount:Grams response_destination:MsgAddress custom_payload:Maybe ^Cell = InternalMsgBody;

Message body

Name
Type
Description

op

uint32

Operation code is equal to burn

query_id

uint64

Query id

amount

coins

Amount of coins to burn (in basic token units)

response_destination

address

Address of a user

Outgoing messages

Sends a message with burn_notification op code to the router contract with the amount of token burnt.

via tonweb

This section contains a guide for sending transactions in TON blockchain using tonweb

Tonweb package uses the transfer method to send a transaction to the blockchain. An example of the usage is on their README.

import TonWeb from "tonweb";
import TonWebMnemonic from "tonweb-mnemonic";
import { DEX, pTON } from "@ston-fi/sdk";

const MNEMONIC = Array.from({ length: 24 }, (_, i) => `your mnemonic word ${i + 1}`); // replace with your mnemonic

const tonWeb = new TonWeb();

const client = new TonWeb.HttpProvider();

const keyPair = await TonWebMnemonic.mnemonicToKeyPair(MNEMONIC);

const wallet = new tonWeb.wallet.all.v4R2(client, {
    publicKey: keyPair.publicKey,
});

const dex = new DEX.v1.Router({
    tonApiClient: client,
})

const swapTxParams = await dex.buildSwapTonToJettonTxParams({
    offerAmount: TonWeb.utils.toNano('1'), // swap 1 TON
    askJettonAddress: 'EQA2kCVNwVsil2EM2mB0SkXytxCqQjS4mttjDpnXmwG9T6bO', // for a STON
    minAskAmount: TonWeb.utils.toNano('0.1'), // but not less than 0.1 STON
    proxyTonAddress: pTON.v1.address.toString(),
    userWalletAddress: (await wallet.getAddress()).toString(),
});

await wallet.methods.transfer({
    secretKey: keyPair.secretKey,
    toAddress: swapTxParams.to,
    amount: swapTxParams.gasAmount,
    seqno: await wallet.methods.seqno().call() ?? 0,
    payload: swapTxParams.payload,
    sendMode: 3,
}).send();

via tonconnect

This section contains a guide for sending transactions in TON blockchain using @tonconnect

Tonconnect package uses the sendTransaction method to send a transaction to the blockchain. An example of the usage is on their DOCs.

import React from 'react';
import TonWeb from 'tonweb';
import { DEX, pTON } from '@ston-fi/sdk';
import { useTonConnectUI, useTonAddress } from '@tonconnect/ui-react';

const dex = new DEX.v1.Router({
  tonApiClient: new TonWeb.HttpProvider(),
});

export const Example = () => {
  const wallet = useTonAddress();
  const [tonConnectUI] = useTonConnectUI();

  return (
    <button
      onClick={async () => {
        const swapTxParams = await dex.buildSwapTonToJettonTxParams({
          offerAmount: TonWeb.utils.toNano('1'), // swap 1 TON
          askJettonAddress: 'EQA2kCVNwVsil2EM2mB0SkXytxCqQjS4mttjDpnXmwG9T6bO', // for a STON
          minAskAmount: TonWeb.utils.toNano('0.1'), // but not less than 0.1 STON
          proxyTonAddress: pTON.v1.address.toString(),
          userWalletAddress: wallet,
        });

        await tonConnectUI.sendTransaction({
          validUntil: Date.now() + 1000000,
          messages: [
            {
              address: swapTxParams.to.toString(),
              amount: swapTxParams.gasAmount.toString(),
              payload: TonWeb.utils.bytesToBase64(
                await swapTxParams.payload.toBoc(),
              ),
            },
          ],
        });
      }}
    >
      Swap 1 TON to STON
    </button>
  );
};

DEX API

This section contains information about public DEX API for STON.fi protocol

Alongside contracts, we provide REST API to simplify interactions with the STON.fi protocol.

The DEX API provides data from the STON.fi contracts over time and organizes data about pools, jettons, STON.fi protocol as a whole, and more.

DEX API live here api.ston.fi

Limits

Currently, there are no limits for DEX API use.

Swagger

You can always found detailed information about the DEX API in Swagger documentation

SDK

To simplify DEX API integration in your projects, we also have a tiny Typescript SDK package

OMNISTON

This section contains information about OMNISTON protocol

Overview

Omniston is a decentralized liquidity aggregation protocol designed specifically for TON Blockchain. It aggregates liquidity from various sources, including:

  • AMM DEXs

  • On-chain RFQ resolvers (TON)

The Request for Quote (RFQ) mechanism allows users to get the most competitive price quote from resolvers, including external DEX platforms.

Omniston demo App is live! See omniston.ston.fi

Key benefits

Omniston offers several advantages:

  • Provides users with the best pricing conditions

  • Access to a wide range of tokens across different liquidity sources within the ecosystem

  • Flexibility to set fees

  • Allows seamless integration by providing access to all liquidity sources in one unified platform, eliminating the need for third-party solutions

Use Case

For example, when a user wants to swap Token A for Token B, they initiate a swap request through Omniston. The protocol aggregates quotes from all connected liquidity sources, including external DEX platforms, and presents the user with the most favorable offer.

Swap

For instructions on performing a swap using the Omniston protocol, see the Swap overview.

SDK

Omniston SDK packages for developers:

  • Node.js SDK for JavaScript/TypeScript applications.

  • React SDK for use in React or Next.js applications.

For information about implementing referral fees in your application, see the Referral Fees guide.

Application

  • Source code for the demo App for application developers.

API reference v1

This section describes different op codes of AMM smart contracts

Overview

The section contains separate documents for each smart contract used in AMM:

  • Pool

  • Router

  • LpAccount

  • LpWallet

Source code of all smart contracts can be found in this Github repo

Definitions of terminology used can be found in Glossary

Referral Fees

DEX v1 pays the referrer share of a swap fee directly inside the swap transaction. The rate is fixed at 0.1 % and the amount is transferred to the provided referrer_address automatically. See the Omniston referral fees guide (note: although the guide is Omniston-centric, the referenced paragraph specifically covers DEX V1 referral fees).

You can monitor referral payouts using the Stats & Vaults REST API:

  • GET /v1/stats/fee_accruals – shows all operations that led to fee accrual for the referrer, filterable by period

  • GET /v1/stats/fee_withdrawals – lists all fee withdrawals from the referrer's vaults by period

  • GET /v1/stats/fees – returns aggregated referral fee information, such as total accrued USD value, for the selected time frame

The full specification is available in the Swagger UI.

v0.4 (deprecated)

This section contains SDK reference for DEX

v0.4 version of the SDK is deprecated. Please use latest SDK version instead

The remaining sections of the documentation will demonstrate specific examples of the SDK usage:

  • perform a swap operation

  • provide liquidity

  • refund liquidity

  • burn liquidity tokens

  • using get methods

  • create a custom router revision

Vault examples

This section contains example schemes Vault operations

Token vault is used to store referral fees on a separate contract similar to lp account. This will allow us to decrease tx fees for swaps since users won't have to pay for additional jetton transfer tx.

Vault address is defined by router_address, owner_address and router_token_Wallet_address, so for each token each user can have a dedicated vault contract.

Swap diagram with referral

Bob is user doing the swap, Alice is referrer, Send is send token, Receive is receive token.

Withdraw from vault

Withdraw op can be called by anyone, not only the Vault owner. All excesses are sent to the owner.

Notes:

  • the Vault contract is deleted on withdraw in order to not pay storage fees (each deposit sends state init so it will be redeployed as soon as any new deposit occurs)

For broader details on collecting referral fees, see the Omniston referral fees guide (note: while the guide is Omniston-centric, the specified section covers DEX V2 referral mechanics thoroughly).

Swap examples

This section contains example schemes for swap operations

Simple swap with referral

Simple swap of one token to another.

Bob is user doing the swap, Alice is referrer, Send is send token, Receive is receive token.

Cross-swap on the same Router

Swap of one token to another using an intermediary token. This method is used if there's no available pool for a pair of tokens but there're pools for each token and some other (the same for both) intermediary token. This type of swap is not limited to 1 intermediary: in practice an arbitrary amount of intermediaries can be used. This type of swap can be combined with cross-swap using multiple Routers.

Bob is user doing the swap, Send is send token, Mid is intermediary token, Receive is receive token.

Cross-swap using multiple Routers

Swap of one token to another using different Routers. This method is used if there's no available pool for a pair of tokens on the same Router but there're pools for each token and some other (the same for both) intermediary token on both Routers. This type of swap is not limited to 1 intermediary: in practice an arbitrary amount of intermediaries can be used. This type of swap can be combined with cross-swap on the same Router.

Bob is user doing the swap, Send is send token, Mid is intermediary token, Receive is receive token.

Refund swap

Refund swap if there's not enough liquidity in the pool or the expected amount of receive tokens is less than expected minimum. Since multi-contract transactions in TON are not atomic it is not possible to fully refund cross swap (on the same router or multiple), in such event the user will receive some intermediate token based on the pool used to route the swap.

Bob is user doing the swap, Send is send token, Receive is receive token.

STON.fi Protocol

This section contains basic description of STON.fi protocol

Introduction

is a decentralized exchange (DEX) on TON blockchain. More specifically it is an Automated Market Maker (AMM) exchange employing the Constant Product Market Maker algorithm. The web based app () is a visual interface for interacting with a set of smart contracts deployed to the TON blockchain.

The exchange is fully decentralized and non-custodian. Funds are held in permissionless smart contract accounts. This means the only methods to withdraw funds from the pool accounts are those encoded in the smart contract. At a high level this code only allows withdrawals in exchange for an appropriate amount of another asset or by liquidity owners in exchange for their Pool Tokens. Furthermore, the contracts are fully permissionless. This means that any account can create a pool by issuing the correct set of transactions and that no account has authority over the pool's assets or functionality. There is no mechanism to revert or adjust transactions even if they are made in error. Contracts, except the router, are immutable. This means no account has the authority to update or delete pool contracts and funds cannot be stolen. For transparency, all router contract upgrades are time-locked for seven days and a user may withdraw their provided liquidity during this period if they find the changes disagreeable.

STON.fi Protocol

  • Fully decentralized system. All transactions are made directly between traders, and do not require a third-party. STON.fi doesn’t have access to your coins/tokens and never asks for your personal information, never collect IP or Geo data, or anything else that may be used for user identification.

  • Virtually zero trading fees. STON.fi runs on the TON blockchain with much lower transaction costs and higher speed compared to Bitcoin or Ethereum blockchains.

  • Low to zero slippage. As an AMM, STON.fi relies on liquidity pools and simple, yet sophisticated algorithms to determine token prices. This guarantees that you get the rates provided before the transaction.

  • The front running is not an issue at STON.fi due to advanced sharding and async nature of TON blockchain.

refund liquidity

This section contains SDK example for refunding undeposited liquidity in DEX

Refund tokens that were sent to LP account but weren't added to a liquidity pool yet

To execute the transaction, you need to send a transaction with these parameters to the blockchain. This code will be different based on the wallet you are using to send the tx from, so please check our with examples for different libraries.

SDK

This section contains information about SDK for STON.fi protocol

Welcome to the docs section.

Getting Started

Overview

The SDK is written in TypeScript and designed to be a thin wrapper on top of the STON.fi contracts, which will help STON.fi protocol to be used more easily in JS/TS projects

The SDK reflects the structure of contracts so that access to contract methods is possible through the use of the corresponding class from the SDK.

@ton package

SDK is built based on the package, which provides primitives for working with data on the TON blockchain, as well as bare contract instances that our SDK expands with unique contract methods

The @ton library allows you to make on-chain requests and for that with a rate limit of 1 request per second. You can obtain higher limits by:

  • requesting an API key from with a 10 request per minute limit

  • use open TON API instance, for example, from

  • found a paid instance of TON API with suitable rate limits for your case

  • selfhost an instance of TON API

Installation

Firstly install the package following their

Then, add SDK package using the package manager of your choice.

Installation of release candidate version

Sometimes, we publish a release candidate version of the SDK package to give you early access to the next version. To install this version, you need to expressly specify that you are interested in the next version during the installation.

Usage

We recommend you check the documentation sections dedicated to the architecture of contracts and their methods to get more context about STON.fi protocol in general before starting to interact with the contracts via the SDK

The remaining sections of the documentation will demonstrate specific examples of the SDK usage:

Live demo

We are providing a simple but fully functional demo app with the SDK usage in the next.js app to demonstrate the SDK functionality. The source code is open-sourced and can be found . Try this app at

via tonconnect

This section contains a guide for sending transactions in TON blockchain using @tonconnect

Tonconnect package uses the sendTransaction method to send a transaction to the blockchain. An example of the usage is on their .

via tonweb

This section contains a guide for sending transactions in TON blockchain using tonweb

Tonweb package uses the transfer method to send a transaction to the blockchain. An example of the usage is on their .

Quickstart Guides

This section contains quickstart guides to help you integrate STON.fi features

Welcome to the STON.fi Quickstart Guides section. These guides provide step-by-step instructions for implementing STON.fi features in your applications with minimal effort.

Each guide is designed to be beginner-friendly and focuses on practical implementation. The guides include complete code examples that you can use as a starting point for your own projects.

Available Guides

Currently, we offer the following quickstart guides:

  • - Learn how to build a simple token swap interface using React and the STON.fi SDK

  • - Learn how to use STON.fi's liquidity aggregation protocol in your application

  • - Learn how to provide liquidity to STON.fi pools

What You'll Learn

Our quickstart guides typically cover:

  • Project setup with the necessary dependencies

  • Integration with TON wallets using TonConnect

  • Fetching data from STON.fi API

  • Executing transactions on the TON blockchain

  • Building user interfaces for STON.fi features

Prerequisites

While our guides are designed to be accessible, having basic knowledge of the following will be helpful:

  • JavaScript/TypeScript

  • React (for frontend guides)

  • Basic understanding of blockchain concepts

Additional Resources

After completing these quickstart guides, you may want to explore more detailed documentation:

  • - For advanced SDK usage

  • - For using STON.fi's liquidity aggregation protocol

  • - For direct interaction with smart contracts

Feedback

We're constantly working to improve our documentation. If you have suggestions or encounter any issues with these guides, please .

LpProvide examples

This section contains example schemes for liquidity provision

Deposit liquidity (both tokens)

Liquidity provision is done via 2 transactions: deposit of the first token and deposit of the second token.

Deposit of the first token

The amount of the first token sent is stored on a user's lp account awaiting a transaction of the second token.

Deposit of the second token

When the second token is sent, lp account automatically initiates liquidity addition into the pool. As a result, full amounts of deposited tokens are used as new liquidity and the user receives lp tokens.

Notes:

  • the Lp Account contract is deleted on cb_add_liquidity in order to not pay storage fees (each deposit sends state init so it will be redeployed as soon as any new deposit occurs)

  • when depositing initial liquidity 0.000001001 lp tokens will be reserved on pool, the rest of liquidity will be sent to the user

Deposit liquidity (single side provision)

It is possible to deposit liquidity by sending just 1 type of token, the pool will automatically perform a swap and use the resulting amount to mint lp tokens

Deposit liquidity (with payload)

It is possible to specify who will receive lp tokens and to include a payload which will be sent as transfer_notification (e.g. Farm contract)

Refund deposited liquidity

In the event that the issued amount of lp tokens doesn't match the specified minimum value, all deposited liquidity is returned to lp account and can be withdrawn by user.

Liquidity is refunded to lp account

Withdraw refunded liquidity

Withdraw liquidly

Liquidly is withdrawn from a pool by burning lp tokens, a user then receives both pool tokens based on the current exchange rate.

Providing already deposited liquidity

A user can deposit liquidity after it was refunded to lp account without doing a withdraw.

DEX v2 guide

This section contains SDK reference for DEX

The remaining sections of the documentation will demonstrate specific examples of the DEX usage:

Referral Fees

DEX v2 stores each swap's referral portion in a dedicated Vault contract (one per referrer × token pair). The fee percentage can be set between 0.01 % and 1 % and must be withdrawn later by the referrer.

Inspect vault balances and history using the Stats & Vaults REST API:

  • GET /v1/wallets/{addr_str}/fee_vaults – lists all known vaults per referrer

  • GET /v1/stats/fee_accruals – shows all operations leading to fee accrual for the referrer, filterable by period

  • GET /v1/stats/fee_withdrawals – lists withdrawals from the referrer's vaults by period

  • GET /v1/stats/fees – returns aggregated referral fee information, for example total accrued USD value

See the Omniston (note: although the guide is Omniston-oriented, the referenced paragraph explains DEX V2 referral fees in detail). Full API documentation is available in the .

unstake from farm

This section contains SDK example for unstaking tokens in Farm

Unstake funds from Farm NFT

To execute the transaction, you need to send a transaction with these parameters to the blockchain. This code will be different based on the wallet you are using to send the tx from, so please check our with examples for different libraries.

STON.fi
app.ston.fi
import { TonClient } from "@ton/ton";
import { DEX } from "@ston-fi/sdk";

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

const router = client.open(new DEX.v1.Router());

const pool = client.open(await router.getPool({
  token0: "EQA2kCVNwVsil2EM2mB0SkXytxCqQjS4mttjDpnXmwG9T6bO", // STON
  token1: "EQBX6K9aXVl3nXINCyPPL86C4ONVmQ8vK360u6dykFKXpHCa", // GEMSTON
}));

const lpAccount = client.open(await pool.getLpAccount({
  ownerAddress: "", // ! replace with your address
}));

const txParams = await lpAccount.getRefundTxParams({
  queryId: 12345,
});
doc section about transaction sending guide
npm install @ston-fi/sdk
yarn add @ston-fi/sdk
pnpm install @ston-fi/sdk
npm install @ston-fi/sdk@latest
yarn add @ston-fi/sdk@latest
pnpm install @ston-fi/sdk@latest
STON.fi
SDK
SDK source code
SDK on NPM
@ton
uses toncenter API
toncenter
ORBS
@ton/ton
installation guide
DEX
Farm
here
sdk-demo-app.ston.fi
import React from "react";
import { TonClient, toNano } from "@ton/ton";
import { DEX, pTON } from "@ston-fi/sdk";
import { useTonConnectUI, useTonAddress } from "@tonconnect/ui-react";

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

const dex = client.open(new DEX.v1.Router());

export const Example = () => {
  const wallet = useTonAddress();
  const [tonConnectUI] = useTonConnectUI();

  return (
    <button
      onClick={async () => {
        const txParams = await dex.getSwapTonToJettonTxParams({
          offerAmount: toNano("1"), // swap 1 TON
          askJettonAddress: "EQA2kCVNwVsil2EM2mB0SkXytxCqQjS4mttjDpnXmwG9T6bO", // for a STON
          minAskAmount: toNano("0.1"), // but not less than 0.1 STON
          proxyTon: new pTON.v1(),
          userWalletAddress: wallet,
        });

        await tonConnectUI.sendTransaction({
          validUntil: Date.now() + 1000000,
          messages: [
            {
              address: txParams.to.toString(),
              amount: txParams.value.toString(),
              payload: txParams.body?.toBoc().toString("base64"),
            },
          ],
        });
      }}
    >
      Swap 1 TON to STON
    </button>
  );
};
DOCs
import TonWeb from "tonweb";
import TonWebMnemonic from "tonweb-mnemonic";
import { TonClient, toNano } from "@ton/ton";
import { DEX, pTON } from "@ston-fi/sdk";

const tonWeb = new TonWeb();
const client = new TonWeb.HttpProvider();

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

const mnemonics = Array.from(
  { length: 24 },
  (_, i) => `your mnemonic word ${i + 1}`
); // replace with your mnemonic
const keyPair = await TonWebMnemonic.mnemonicToKeyPair(mnemonics);

const wallet = new tonWeb.wallet.all.v4R2(client, {
  publicKey: keyPair.publicKey,
});

const dex = tonClient.open(new DEX.v1.Router());

const txParams = await dex.getSwapTonToJettonTxParams({
  offerAmount: toNano("1"), // swap 1 TON
  askJettonAddress: "EQA2kCVNwVsil2EM2mB0SkXytxCqQjS4mttjDpnXmwG9T6bO", // for a STON
  minAskAmount: toNano("0.1"), // but not less than 0.1 STON
  proxyTon: new pTON.v1(),
  userWalletAddress: (await wallet.getAddress()).toString(),
});

await wallet.methods
  .transfer({
    secretKey: keyPair.secretKey,
    toAddress: txParams.to.toString(),
    amount: new tonWeb.utils.BN(txParams.value),
    seqno: (await wallet.methods.seqno().call()) ?? 0,
    payload: TonWeb.boc.Cell.oneFromBoc(
      TonWeb.utils.base64ToBytes(txParams.body?.toBoc().toString("base64"))
    ),
    sendMode: 3,
  })
  .send();
README
Swap Guide
Omniston Guide
Liquidity Providing Guide
SDK Documentation
OMNISTON Documentation
API References
contact us
perform a swap operation
provide liquidity
refund liquidity
burn liquidity tokens
withdraw fee from vault
referral fees guide
Swagger UI
import { TonClient } from "@ton/ton";
import { FARM } from "@ston-fi/sdk";

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

const farmNft = client.open(FARM.v3.NftItem.create(
  "EQA18JnBVNZZ8Wz-Kn6Mc5cy9pv798Pn8tfScjKw9NLPK3U2", // Farm v3 nft address
));

// Unstake all staked funds from Farm NFT back to the owner's wallet
const txParams = await farmNft.getUnstakeTxParams({
  queryId: 12345,
});
doc section about transaction sending guide

provide liquidity

This section contains SDK example for providing liquidity in DEX

Provide liquidity for a pool

Jetton/Jetton pool deposit

import { TonClient, toNano } from "@ton/ton";
import { DEX } from "@ston-fi/sdk";

const USER_WALLET_ADDRESS = ""; // ! replace with your address
const JETTON_0_ADDRESS = "EQA2kCVNwVsil2EM2mB0SkXytxCqQjS4mttjDpnXmwG9T6bO"; // STON
const JETTON_1_ADDRESS = "EQBX6K9aXVl3nXINCyPPL86C4ONVmQ8vK360u6dykFKXpHCa"; // GEMSTON

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

const router = client.open(new DEX.v1.Router());

const txsParams = await Promise.all([
  // deposit 0.5 STON to the STON/GEMSTON pool and get at least 1 nano LP token
  router.getProvideLiquidityJettonTxParams({
    userWalletAddress: USER_WALLET_ADDRESS,
    sendTokenAddress: JETTON_0_ADDRESS,
    sendAmount: toNano("0.5"),
    otherTokenAddress: JETTON_1_ADDRESS,
    minLpOut: "1",
    queryId: 12345,
  }),
  // deposit 2 GEMSTON to the STON/GEMSTON pool and get at least 1 nano LP token
  router.getProvideLiquidityJettonTxParams({
    userWalletAddress: USER_WALLET_ADDRESS,
    sendTokenAddress: JETTON_1_ADDRESS,
    sendAmount: toNano("2.0"),
    otherTokenAddress: JETTON_0_ADDRESS,
    minLpOut: "1",
    queryId: 123456,
  }),
]);

To execute the transaction, you need to send a transaction with these parameters to the blockchain. This code will be different based on the wallet you are using to send the tx from, so please check our doc section about transaction sending guide with examples for different libraries.

TON/Jetton pool deposit

import { TonClient, toNano } from "@ton/ton";
import { DEX, pTON } from "@ston-fi/sdk";

const USER_WALLET_ADDRESS = ""; // ! replace with your address
const JETTON_0_ADDRESS = "EQA2kCVNwVsil2EM2mB0SkXytxCqQjS4mttjDpnXmwG9T6bO"; // STON

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

const router = client.open(new DEX.v1.Router());

const txsParams = await Promise.all([
  // deposit 1 TON to the STON/TON pool and get at least 1 nano LP token
  router.getProvideLiquidityTonTxParams({
    userWalletAddress: USER_WALLET_ADDRESS,
    proxyTon: new pTON.v1(),
    sendAmount: toNano("1"),
    otherTokenAddress: JETTON_0_ADDRESS,
    minLpOut: "1",
    queryId: 12345,
  }),
  // deposit 0.5 STON to the STON/TON pool and get at least 1 nano LP token
  router.getProvideLiquidityJettonTxParams({
    userWalletAddress: USER_WALLET_ADDRESS,
    sendTokenAddress: JETTON_0_ADDRESS,
    sendAmount: toNano("0.5"),
    otherTokenAddress: new pTON.v1().address,
    minLpOut: "1",
    queryId: 123456,
  }),
]);

To execute the transaction, you need to send a transaction with these parameters to the blockchain. This code will be different based on the wallet you are using to send the tx from, so please check our doc section about transaction sending guide with examples for different libraries.

provide liquidity

This section contains SDK example for providing liquidity in DEX

Provide liquidity for a pool

Jetton/Jetton pool deposit

import TonWeb from "tonweb";
import { DEX } from "@ston-fi/sdk";

const USER_WALLET_ADDRESS = ""; // ! replace with your address
const JETTON_0_ADDRESS = "EQA2kCVNwVsil2EM2mB0SkXytxCqQjS4mttjDpnXmwG9T6bO"; // STON
const JETTON_1_ADDRESS = "EQBX6K9aXVl3nXINCyPPL86C4ONVmQ8vK360u6dykFKXpHCa"; // GEMSTON

const router = new DEX.v1.Router({
  tonApiClient: new TonWeb.HttpProvider(),
});

const txsParams = await Promise.all([
  // deposit 5 STON to the STON/GEMSTON pool and get at least 1 nano LP token
  router.buildProvideLiquidityJettonTxParams({
    userWalletAddress: USER_WALLET_ADDRESS,
    sendTokenAddress: JETTON_0_ADDRESS,
    sendAmount: new TonWeb.utils.BN("500000000"),
    otherTokenAddress: JETTON_1_ADDRESS,
    minLpOut: new TonWeb.utils.BN("1"),
    queryId: 12345,
  }),
  // deposit 2 GEMSTON to the STON/GEMSTON pool and get at least 1 nano LP token
  router.buildProvideLiquidityJettonTxParams({
    userWalletAddress: USER_WALLET_ADDRESS,
    sendTokenAddress: JETTON_1_ADDRESS,
    sendAmount: new TonWeb.utils.BN("200000000"),
    otherTokenAddress: JETTON_0_ADDRESS,
    minLpOut: new TonWeb.utils.BN("1"),
    queryId: 123456,
  }),
]);

// To execute the transaction, you need to send a transaction to the blockchain.
// This code will be different based on the wallet you are using to send the tx from
// logging is used for demonstration purposes
txsParams.map((txParams) => console.log({
  to: txParams.to,
  amount: txParams.gasAmount,
  payload: txParams.payload,
}));

TON/Jetton pool deposit

import TonWeb from "tonweb";
import { DEX, pTON } from "@ston-fi/sdk";

const USER_WALLET_ADDRESS = ""; // ! replace with your address
const JETTON_0_ADDRESS = "EQA2kCVNwVsil2EM2mB0SkXytxCqQjS4mttjDpnXmwG9T6bO"; // STON
const pTON_ADDRESS = pTON.v1.address;

const router = new DEX.v1.Router({
  tonApiClient: new TonWeb.HttpProvider(),
});

const txsParams = await Promise.all([
  // deposit 1 TON to the STON/TON pool and get at least 1 nano LP token
  router.buildProvideLiquidityTonTxParams({
    userWalletAddress: USER_WALLET_ADDRESS,
    proxyTonAddress: pTON_ADDRESS,
    sendAmount: new TonWeb.utils.BN("100000000"),
    otherTokenAddress: JETTON_0_ADDRESS,
    minLpOut: new TonWeb.utils.BN("1"),
    queryId: 12345,
  }),
  // deposit 5 STON to the STON/TON pool and get at least 1 nano LP token
  router.buildProvideLiquidityJettonTxParams({
    userWalletAddress: USER_WALLET_ADDRESS,
    sendTokenAddress: JETTON_0_ADDRESS,
    sendAmount: new TonWeb.utils.BN("500000000"),
    otherTokenAddress: pTON_ADDRESS,
    minLpOut: new TonWeb.utils.BN("1"),
    queryId: 123456,
  }),
]);

// To execute the transaction, you need to send a transaction to the blockchain.
// This code will be different based on the wallet you are using to send the tx from
// logging is used for demonstration purposes
txsParams.map((txParams) => console.log({
  to: txParams.to,
  amount: txParams.gasAmount,
  payload: txParams.payload,
}));

swap

This section contains SDK example for performing swap in DEX

In this section, to illustrate all three possible types of a swap, we will do following exchange chain

  1. swap 1 TON to STON (ton to jetton swap)

  2. swap STON to GEMSTON (jetton to jetton swap)

  3. swap GEMSTON back to TON (jetton to ton swap)

Swap TON to jetton

import TonWeb from "tonweb";
import { DEX, pTON } from "@ston-fi/sdk";

const router = new DEX.v1.Router({
  tonApiClient: new TonWeb.HttpProvider(),
});

// swap 1 TON to STON but not less than 1 nano STON
const txParams = await router.buildSwapTonToJettonTxParams({
  userWalletAddress: "", // ! replace with your address
  proxyTonAddress: pTON.v1.address,
  offerAmount: new TonWeb.utils.BN("1000000000"),
  askJettonAddress: "EQA2kCVNwVsil2EM2mB0SkXytxCqQjS4mttjDpnXmwG9T6bO", // STON
  minAskAmount: new TonWeb.utils.BN("1"),
  queryId: 12345,
});

// To execute the transaction, you need to send a transaction to the blockchain.
// This code will be different based on the wallet you are using to send the tx from
// logging is used for demonstration purposes
console.log({
  to: txParams.to,
  amount: txParams.gasAmount,
  payload: txParams.payload,
});

Swap jetton to jetton

import TonWeb from "tonweb";
import { DEX, pTON } from "@ston-fi/sdk";

const router = new DEX.v1.Router({
  tonApiClient: new TonWeb.HttpProvider(),
});

// swap 1 STON to GEMSTON but not less than 1 nano GEMSTON
const txParams = await router.buildSwapJettonToJettonTxParams({
  userWalletAddress: "", // ! replace with your address
  offerJettonAddress: "EQA2kCVNwVsil2EM2mB0SkXytxCqQjS4mttjDpnXmwG9T6bO", // STON
  offerAmount: new TonWeb.utils.BN("1000000000"),
  askJettonAddress: "EQBX6K9aXVl3nXINCyPPL86C4ONVmQ8vK360u6dykFKXpHCa", // GEMSTON
  minAskAmount: new TonWeb.utils.BN("1"),
  queryId: 12345,
});

// To execute the transaction, you need to send a transaction to the blockchain.
// This code will be different based on the wallet you are using to send the tx from
// logging is used for demonstration purposes
console.log({
  to: txParams.to,
  amount: txParams.gasAmount,
  payload: txParams.payload,
});

Swap jetton to TON

import TonWeb from "tonweb";
import { DEX, pTON } from "@ston-fi/sdk";

const router = new DEX.v1.Router({
  tonApiClient: new TonWeb.HttpProvider(),
});

// swap 1 GEMSTON to TON but not less than 1 nano TON
const txParams = await router.buildSwapJettonToTonTxParams({
  userWalletAddress: "", // ! replace with your address
  offerJettonAddress: "EQBX6K9aXVl3nXINCyPPL86C4ONVmQ8vK360u6dykFKXpHCa", // GEMSTON
  offerAmount: new TonWeb.utils.BN("1000000000"),
  proxyTonAddress: pTON.v1.address,
  minAskAmount: new TonWeb.utils.BN("1"),
  queryId: 12345,
});

// To execute the transaction, you need to send a transaction to the blockchain.
// This code will be different based on the wallet you are using to send the tx from
// logging is used for demonstration purposes
console.log({
  to: txParams.to,
  amount: txParams.gasAmount,
  payload: txParams.payload,
});

Recommended gas values

Below are recommended values for TON sent and forward gas for each type of the swap:

Type
Tx TON
Forward TON

pTON -> Jetton

swap_amount + 0.215

0.215

Jetton -> Jetton

0.265

0.205

Jetton -> pTON

0.185

0.125

Formulas:

pTON -> Jetton

// pton_transfer + (pay_to + jetton_transfer) * 2 + (jetton_notification + swap)
fwd_amount = 0.01 + (0.02 + 0.06) * 2 + 0.045; // = 0.215
// jetton_transfer + fwd_amount
tx_amount = swap_amount + fwd_amount; // = swap_amount + 0.215

Jetton -> Jetton

// (jetton_notification + swap) + (pay_to + jetton_transfer) * 2
fwd_amount = 0.045 + (0.02 + 0.06) * 2; // = 0.205
// jetton_transfer + fwd_amount
tx_amount = 0.06 + fwd_amount; // = 0.265

Jetton -> pTON

// (jetton_notification + swap) + (pay_to + pton_transfer) * 2
fwd_amount = 0.045 + (0.02 + 0.02) * 2; // = 0.125
// jetton_transfer + fwd_amount
tx_amount = 0.06 + fwd_amount; // = 0.185

API reference v2

This section describes different op codes of AMM smart contracts v2

API reference

Overview

The section contains separate documents for each smart contract used in AMM:

  • Pool

  • Router

  • LpAccount

  • LpWallet

  • Vault

Definitions of terminology used can be found in Glossary

Example schemes

Example schemes can be found here:

  • Swap examples

  • Lp provide examples

  • Vault examples

Op code list

A table with DEX v2 op codes:

  • Op Codes

New DEX v2 features:

Swap

  • custom payload & nested operations after swaps

  • chain multiple swaps on the same Router

  • chain multiple swaps on different v2 Routers

  • custom refund address and payload on swap failure

  • deadline for tx completion

Liquidity

  • custom payload after liquidly provision

  • improved initial liquidity locking management, no coins are lost anymore

  • now always mints a maximum possible amount of lp tokens to user even if provision ratio is different from current one in Pool

  • single side liquidity provision

  • deadline for tx completion

Referral

  • referral fees are stored in Vault contract

  • custom referral fee value in each swap (maximum 1%)

Referral Fees

DEX v2 accumulates each swap's referral portion in a dedicated Vault contract (one per referrer × token pair). Fees can be configured in the0.01 %–1 % range and must later be withdrawn by the referrer. See the Omniston referral fees guide (note: although the guide is Omniston-oriented, the referenced paragraph explains in detail how DEX V2 referral fees work).

You can inspect vault balances and accrual history using the Stats & Vaults REST API:

  • GET /v1/wallets/{addr_str}/fee_vaults – lists all known vaults per referrer

  • GET /v1/stats/fee_accruals – shows all operations that led to fee accrual for the referrer, filterable by period

  • GET /v1/stats/fee_withdrawals – lists all withdrawals from the referrer's vaults, filterable by period

  • GET /v1/stats/fees – returns aggregated referral fee metrics (for example, total accrued USD value) by time frame

All endpoints are documented in the Swagger UI.

pTON v2

  • now uses a custom op code for ton transfers

  • ton transfer to user is non-bouncable

  • improved gas management

  • can chain ton transfers between 2 pTON wallets (to chain pTON swaps on v2 Routers)

Other

  • LpAccount and Vault are deleted if they have 0 tokens on balance to avoid paying storage cost

  • better error management: no coins are lost if Pool doesn't exist / payload is not correct

  • complete refactoring of the codebase and usage of libs in masterchain to make all operations cheaper

  • fixed various excesses issues

  • fixed some Pools having broken get_jetton_data

Removed

Pool

  • off-chain get_expected_outputs

  • off-chain get_expected_tokens

  • off-chain get_expected_liquidity

  • on-chain getter_expected_outputs

  • on-chain getter_expected_tokens

  • on-chain getter_expected_liquidity

  • user-called collect_fees

using get methods

This section describes the usage of get methods

Example of usage of various get methods of the SDK.

import TonWeb from 'tonweb';

import { Router, ROUTER_REVISION, ROUTER_REVISION_ADDRESS } from '@ston-fi/sdk';

(async () => {
  const OWNER_ADDRESS = '';

  const JETTON0 = 'EQDQoc5M3Bh8eWFephi9bClhevelbZZvWhkqdo80XuY_0qXv';
  const JETTON1 = 'EQC_1YoM8RBixN95lz7odcF3Vrkc_N8Ne7gQi7Abtlet_Efi';

  const provider = new TonWeb.HttpProvider();

  const router = new Router(provider, {
    revision: ROUTER_REVISION.V1,
    address: ROUTER_REVISION_ADDRESS.V1,
  });

  const routerData = await router.getData();

  const {
    isLocked,
    adminAddress,
    tempUpgrade,
    poolCode,
    jettonLpWalletCode,
    lpAccountCode,
  } = routerData;

  const pool = await router.getPool({ jettonAddresses: [JETTON0, JETTON1] });

  if (!pool) {
    throw Error(`Pool for ${JETTON0}/${JETTON1} not found`);
  }

  const poolAddress = await pool.getAddress();

  const poolData = await pool.getData();
  const {
    reserve0,
    reserve1,
    token0WalletAddress,
    token1WalletAddress,
    lpFee,
    protocolFee,
    refFee,
    protocolFeeAddress,
    collectedToken0ProtocolFee,
    collectedToken1ProtocolFee,
  } = poolData;

  const expectedLiquidityData = await pool.getExpectedLiquidity({
    jettonAmount: new TonWeb.utils.BN(500000000),
  });

  const { amount0, amount1 } = expectedLiquidityData;

  const expectedLpTokensAmount = await pool.getExpectedTokens({
    amount0: new TonWeb.utils.BN(500000000),
    amount1: new TonWeb.utils.BN(200000000),
  });

  if (token0WalletAddress) {
    const expectedOutputsData = await pool.getExpectedOutputs({
      amount: new TonWeb.utils.BN(500000000),
      jettonWallet: token0WalletAddress,
    });

    const { jettonToReceive, protocolFeePaid, refFeePaid } =
      expectedOutputsData;
  }

  const lpAccountAddress = await pool.getLpAccountAddress({
    ownerAddress: OWNER_ADDRESS,
  });

  const lpAccount = await pool.getLpAccount({ ownerAddress: OWNER_ADDRESS });

  if (lpAccount) {
    const lpAccountData = await lpAccount.getData();
    const { userAddress, poolAddress, amount0, amount1 } = lpAccountData;
  }
})();

Architecture

This document explains the logic behind STON.fi Jetton-to-Jetton DEX

Actors

Router

The router is the contract that acts as an entrypoint for all DEX calls. It is responsible for routing all Jetton calls with transfer_notification op to the correct pool contract.

It acts as a sovereign over the DEX, and can be used to lock/unlock trading on all pools, to change fees on a certain pool or to upgrade its own contract. The router is the only contract that can be upgraded. Each Jetton that goes through the DEX is owned by the router. The router does not store anything about pairs.

Pool

The pool is the contract that stores the AMM data for a certain pair and is responsible for handling "swaps" or providing liquidity for a certain pool. For each pair (e.g. TOKEN/USDT), there is only a single pool contract. The pool is also a Jetton Minter, and handles minting/burning of Liquidity Provider Jettons. All the swap/lp calculations are done in the pool contract.

Account

The account contract holds information about the liquidity provided by the user before minting new liquidity. It interacts only with a single pool contract. For each user, there is single account contract for each pool. The router "routes" the temporary liquidity to the correct account contract. Then the account contract calls the pool contract again to mint new liquidity (once it satisfies some requirements).

Wallet (for LP jettons)

The wallet contract is a standard Jetton wallet and is used to hold the LP Jetton minted by Pools.

Calls descriptions

Swap

Lets assume that Alice wants to buy TOKEN using USDT. She should initialize a simple transfer to her own USDT Wallet with a custom payload. Once the Router receives the confirmation of the transfer, it will call the Pool contract to perform the real swap. Then the Pool contract will return the TOKEN Jetton calling pay_to op to the Router.

Providing liquidity (both amounts)

Now Alice wants to provide liquidity to the TOKEN/USDT pair. This process must be done via two different calls. Obviously, since wallets support up to 4 transactions at same time, it is possible to call both calls at the same time (at least, from the user's perspective).

At the beginning, she should start a transfer to her own USDT Wallet with a custom payload. Once the Router receives the confirmation of the transfer, it will call the Pool contract, and then the Pool contract will route the request to her own Account contract. Once the request arrives to the Account contract, the first phase ends here. This time the Account contract doesn't generate any transactions.

In Dex v1 it is possible to provide liquidity using a different ratio than the current one in the pool, thus the unbalanced part will be lost to the user and distributed across all liquidity providers. In Dex v2 always a max amount of liquidity is minted to the user, meaning the unbalanced amount of a tokens is automatically swapped using the current token ratio in the pool.

Now, Alice makes another transfer of the other Jetton (in our case, TOKEN) in the same way as before. This time, the Account contract will generate a new message to the Pool contract to mint new liquidity.

Providing liquidity (single side provision)

Dex v2 supports liquidity provision using only one token by automatically performing a swap before minting liquidity

Creating a pool

V1

To create a new Pool, just provide the minimum amount of liquidity to pair (1001 Jettons). This initial liquidity will be sent to null address. Any LP calls after this will mint LP Jettons to a user.

V2

To create a new Pool, just provide the minimum amount of liquidity to pair (1001 Jettons). A basic amount of 1001 lp tokens will be reserved on pool on initial liquidity deposit with the rest going to the user.

Stableswap

Creation of a stableswap pool is handled directly by STON.fi and cannot be done by users.

Vault

This section contains api reference of the vault contract

Check for in-depth message flow.

Off-chain get methods

get_vault_data

Returns Vault data

Arguments

None

Result

Returns VaultData structure containing current state of the Vault.

VaultData structure

Key
Type
Index
Description

Owner operation handlers

Handles calls from the Vault owner

withdraw_fee (0x354bcdf4)

Burn an amount of liquidity tokens.

TL-B

Message body

Name
Type
Description

Outgoing messages

Sends a message with vault_pay_to op code to the router contract with the amount of token to be payed.

Internal message handlers

Handles calls from the Router owner

deposit_ref_fee (0x0490f09b)

Increase ref fee tracker for the user.

TL-B

Message body

Name
Type
Description

Outgoing messages

Sends excesses to excesses_address.

(slice, slice, slice, int) get_vault_data() method_id;

owner_address

address

0

Owner of this Vault

token_address

address

1

Address of Router's jetton wallet for s token'

router_address

address

2

Router's address

deposited_amount

bigint

3

Amount of tokens collected

withdraw_fee#354bcdf4 query_id:uint64 = InternalMsgBody;

op

uint32

Operation code

query_id

uint64

Query id

deposit_ref_fee#490f09b query_id:uint64 jetton_amount:Coins excesses_address:MsgAddress = InternalMsgBody;

op

uint32

Operation code

query_id

uint64

Query id

jetton_amount

bigint

Amount of the tokens

excesses_address

address

Address to receive TON excesses

examples

swap

This section contains SDK example for performing swap in DEX

In this section, to illustrate all three possible types of a swap, we will do following exchange chain

  1. swap 1 TON to STON (ton to jetton swap)

  2. swap STON to GEMSTON (jetton to jetton swap)

  3. swap GEMSTON back to TON (jetton to ton swap)

Swap TON to jetton

import { TonClient, toNano } from "@ton/ton";
import { DEX, pTON } from "@ston-fi/sdk";

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

const router = client.open(new DEX.v1.Router());

// swap 1 TON to STON but not less than 1 nano STON
const txParams = await router.getSwapTonToJettonTxParams({
  userWalletAddress: "", // ! replace with your address
  proxyTon: new pTON.v1(),
  offerAmount: toNano("1"),
  askJettonAddress: "EQA2kCVNwVsil2EM2mB0SkXytxCqQjS4mttjDpnXmwG9T6bO", // STON
  minAskAmount: "1",
  queryId: 12345,
});

To execute the transaction, you need to send a transaction with these parameters to the blockchain. This code will be different based on the wallet you are using to send the tx from, so please check our doc section about transaction sending guide with examples for different libraries.

Swap jetton to jetton

import { TonClient, toNano } from "@ton/ton";
import { DEX, pTON } from "@ston-fi/sdk";

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

const router = client.open(new DEX.v1.Router());

// swap 1 STON to GEMSTON but not less than 1 nano GEMSTON
const txParams = await router.getSwapJettonToJettonTxParams({
  userWalletAddress: "", // ! replace with your address
  offerJettonAddress: "EQA2kCVNwVsil2EM2mB0SkXytxCqQjS4mttjDpnXmwG9T6bO", // STON
  offerAmount: toNano("1"),
  askJettonAddress: "EQBX6K9aXVl3nXINCyPPL86C4ONVmQ8vK360u6dykFKXpHCa", // GEMSTON
  minAskAmount: "1",
  queryId: 12345,
});

To execute the transaction, you need to send a transaction with these parameters to the blockchain. This code will be different based on the wallet you are using to send the tx from, so please check our doc section about transaction sending guide with examples for different libraries.

Swap jetton to TON

import { TonClient, toNano } from "@ton/ton";
import { DEX, pTON } from "@ston-fi/sdk";

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

const router = client.open(new DEX.v1.Router());

// swap 1 GEMSTON to TON but not less than 1 nano TON
const txParams = await router.getSwapJettonToTonTxParams({
  userWalletAddress: "", // ! replace with your address
  offerJettonAddress: "EQBX6K9aXVl3nXINCyPPL86C4ONVmQ8vK360u6dykFKXpHCa", // GEMSTON
  offerAmount: toNano("1"),
  proxyTon: new pTON.v1(),
  minAskAmount: "1",
  queryId: 12345,
});

To execute the transaction, you need to send a transaction with these parameters to the blockchain. This code will be different based on the wallet you are using to send the tx from, so please check our doc section about transaction sending guide with examples for different libraries.

Recommended gas values

Below are recommended values for TON sent and forward gas for each type of the swap:

Type
Tx TON
Forward TON

pTON -> Jetton

swap_amount + 0.185

0.185

Jetton -> Jetton

0.22

0.175

Jetton -> pTON

0.17

0.125

Formulas:

pTON -> Jetton

// pton_transfer + (pay_to + jetton_transfer) * 2 + (jetton_notification + swap)
fwd_amount = 0.01 + (0.02 + 0.045) * 2 + 0.045; // = 0.185
// jetton_transfer + fwd_amount
tx_amount = swap_amount + fwd_amount; // = swap_amount + 0.185

Jetton -> Jetton

// (jetton_notification + swap) + (pay_to + jetton_transfer) * 2
fwd_amount = 0.045 + (0.02 + 0.045) * 2; // = 0.175
// jetton_transfer + fwd_amount
tx_amount = 0.045 + fwd_amount; // = 0.22

Jetton -> pTON

// (jetton_notification + swap) + (pay_to + pton_transfer) * 2
fwd_amount = 0.045 + (0.02 + 0.02) * 2; // = 0.125
// jetton_transfer + fwd_amount
tx_amount = 0.045 + fwd_amount; // = 0.17

Swap grpc

This guide describes how to use the Omniston swap functionality over the gRPC API from a trader's perspective. It parallels Swap overview (WebSocket) but shows the gRPC specifics.

Key Differences from WebSocket API

  1. Protocol Field For the gRPC API, the protocol field in each swap chunk must be a string:

    • "StonFiV1", "StonFiV2", "DeDust", or "TonCo". In contrast, the WebSocket API uses numeric codes 1, 2, 3, or 4.

  2. Extra Version Currently, only extraVersion = 1 is supported.

  3. Steps vs. Chunks

    • Each SwapStep moves from one asset to another (e.g., TON → USDC).

    • Each step can have multiple SwapChunk objects, but on TON you typically see exactly one chunk per step.

  4. Method Names In the gRPC API, traders use similar calls to requestForQuote, buildTransfer, trackTrade, etc., but these are exposed via protobuf messages, not JSON-RPC. The underlying fields are the same.

Typical Flow (Traders)

A typical gRPC-based swap flow from a trader's viewpoint is:

  1. Send a Quote Request Using Omniston.requestForQuote (or the corresponding gRPC call), providing:

    • bidAssetAddress, askAssetAddress

    • The desired amount (either bidUnits or askUnits)

    • Optional referrerAddress and referrerFeeBps

    • settlementMethods = [SETTLEMENT_METHOD_SWAP] if you want a swap

    • settlementParams such as maxPriceSlippageBps or maxOutgoingMessages (for TON)

    You receive an Observable (RxJS style in the SDK) or a stream of QuoteResponseEvent messages over gRPC. The server may update your quote repeatedly if a more favorable price arrives.

  2. Build the Transfer When you have a valid Quote, call Omniston.buildTransfer with:

    • The quote you accepted

    • Your sourceAddress (on bidBlockchain)

    • Your destinationAddress (on askBlockchain) The API returns an object with blockchain-specific transaction data (for TON, a list of messages).

  3. Sign/Send the Transaction

    • On TON, you typically pass these messages to your wallet (e.g., TonConnect, Tonkeeper, etc.).

    • Wait for the transaction to be mined.

  4. Track the Trade You can then call Omniston.trackTrade to receive streaming updates about the trade status. This includes whether the trade was filled, partially filled, or aborted.

Important Data Types

  • Blockchain A numeric code following SLIP-044. For TON, this is 607.

  • Address An object:

    {
      "blockchain": 607,
      "address": "EQDA3..."
    }
  • Protocol Must be one of:

    • "StonFiV1"

    • "StonFiV2"

    • "DeDust"

    • "TonCo"

  • SettlementMethod For now, typically [0] for swap in numerical form, or (in your SDK) SettlementMethod.SETTLEMENT_METHOD_SWAP.

  • SwapStep and SwapChunk

    • Each step is a transition from bidAssetAddress to askAssetAddress.

    • For TON: typically exactly one chunk per step.

    • A chunk includes:

      string protocol = 1;       // e.g. "StonFiV2"
      string offer_amount = 2;
      string ask_amount = 3;
      uint64 extra_version = 4;  // must be 1
      bytes extra = 5;           // base64-encoded data for your DEX
  • Quote The server provides a quoteId, bidUnits, askUnits, quoteTimestamp, tradeStartDeadline, etc. Includes params with settlement info for the chosen method (SwapSettlementParams).

Rejected Quotes

If you pass extraVersion != 1 (e.g., 3), the quote or the route gets rejected with a message like:

Invalid extra version for step 0: 3

Make sure to use extraVersion = 1.

Next Steps

For a complete, low-level definition of the gRPC service (resolver side, plus the underlying messages), see Resolver Integration Guide. For Node.js or TypeScript usage, see omniston-nodejs.md or the omniston-react.md for React apps. Both rely on the same underlying gRPC/WS backend.

If you have any questions or issues, please reach out in our STON.fi Discord.

provide liquidity

This section contains SDK example for providing liquidity in DEX

Jetton1/Jetton2 pool

Provide liquidity for a liquidity pool of 2 Jettons.

Proxy-ton/Jetton pool

Provide liquidity for a liquidity pool with proxy ton.

perform a swap operation

This section contains SDK example for performing swap in DEX

Jetton1/Jetton2

Perform a swap operation.

Proxy-ton/Jetton

Perform a swap operation using proxy-ton.

Recommended gas values

Below are recommended values for TON sent and forward gas for each type of the swap:

Type
Tx TON
Forward TON

Formulas:

Jetton -> Jetton

Jetton -> pTON

pTON -> Jetton

import TonWeb from 'tonweb';

import { Router, ROUTER_REVISION, ROUTER_REVISION_ADDRESS } from '@ston-fi/sdk';

/**
 * This example shows how to provide liquidity to the pool
 * As a result, you will get LP tokens that can be used to refund your tokens from the pool.
 */
(async () => {
  const WALLET_ADDRESS = ''; // ! replace with your address
  const JETTON0 = 'EQA2kCVNwVsil2EM2mB0SkXytxCqQjS4mttjDpnXmwG9T6bO'; // STON
  const JETTON1 = 'EQBynBO23ywHy_CgarY9NK9FTz0yDsG82PtcbSTQgGoXwiuA'; // jUSDT

  const provider = new TonWeb.HttpProvider();

  const router = new Router(provider, {
    revision: ROUTER_REVISION.V1,
    address: ROUTER_REVISION_ADDRESS.V1,
  });

  // transaction to provide 0.5 JETTON0 to JETTON0/JETTON1 pool
  const jetton0ProvisionTxParams =
    await router.buildProvideLiquidityJettonTxParams({
      // address of the wallet that holds jetton you want to provide
      userWalletAddress: WALLET_ADDRESS,
      // address of the jetton you want to provide
      sendTokenAddress: JETTON0,
      // amount of the jetton you want to provide
      sendAmount: new TonWeb.utils.BN('500000000'),
      // address of the second jetton you want to provide
      otherTokenAddress: JETTON1,
      // minimal amount of the LP tokens you want to receive as a result of the provision
      minLpOut: new TonWeb.utils.BN(1),
      // query id to identify your transaction in the blockchain (optional)
      queryId: 12345,
    });

  // to execute the transaction you need to send transaction to the blockchain
  // (replace with your wallet implementation, logging is used for demonstration purposes)
  console.log({
    to: jetton0ProvisionTxParams.to,
    amount: jetton0ProvisionTxParams.gasAmount,
    payload: jetton0ProvisionTxParams.payload,
  });

  // transaction to provide 0.2 JETTON1 to to JETTON0/JETTON1 pool
  const jetton1ProvisionTxParams =
    await router.buildProvideLiquidityJettonTxParams({
      // address of the wallet that holds jetton you want to provide
      userWalletAddress: WALLET_ADDRESS,
      // address of the jetton you want to provide
      sendTokenAddress: JETTON1,
      // amount of the jetton you want to provide
      sendAmount: new TonWeb.utils.BN('200000000'),
      // address of the second jetton you want to provide
      otherTokenAddress: JETTON0,
      // minimal amount of the LP tokens you want to receive as a result of the provision
      minLpOut: new TonWeb.utils.BN(1),
      // query id to identify your transaction in the blockchain (optional)
      queryId: 12345,
    });

  // to execute the transaction you need to send transaction to the blockchain
  // (replace with your wallet implementation, logging is used for demonstration purposes)
  console.log({
    to: jetton1ProvisionTxParams.to,
    amount: jetton1ProvisionTxParams.gasAmount,
    payload: jetton1ProvisionTxParams.payload,
  });

  // after execution of both transactions liquidity provision process will be completed
  // and you will receive pool LP tokens
})();
import TonWeb from 'tonweb';

import { Router, ROUTER_REVISION, ROUTER_REVISION_ADDRESS } from '@ston-fi/sdk';

/**
 * This example shows how to provide liquidity to the pool where one of the tokens is TON
 * As a result, you will get LP tokens that can be used to refund your tokens from the pool.
 */
(async () => {
  const WALLET_ADDRESS = ''; // ! replace with your address
  const JETTON0 = 'EQA2kCVNwVsil2EM2mB0SkXytxCqQjS4mttjDpnXmwG9T6bO'; // STON
  const PROXY_TON = 'EQCM3B12QK1e4yZSf8GtBRT0aLMNyEsBc_DhVfRRtOEffLez'; // ProxyTON

  const provider = new TonWeb.HttpProvider();

  const router = new Router(provider, {
    revision: ROUTER_REVISION.V1,
    address: ROUTER_REVISION_ADDRESS.V1,
  });

  // Because TON is not a jetton, to be able to swap TON to jetton
  // you need to use special SDK method to build transaction to swap TON to jetton
  // using proxy jetton contract.

  // transaction to provide 1.0 TON to STON/TON pool
  const tonProvisionTxParams =
    await router.buildProvideLiquidityProxyTonTxParams({
      // address of the wallet that holds jetton you want to provide
      userWalletAddress: WALLET_ADDRESS,
      proxyTonAddress: PROXY_TON,
      // amount of TON you want to provide
      sendAmount: new TonWeb.utils.BN('1000000000'),
      // address of the second jetton you want to provide
      otherTokenAddress: JETTON0,
      // minimal amount of the LP tokens you want to receive as a result of the provision
      minLpOut: new TonWeb.utils.BN(1),
      // query id to identify your transaction in the blockchain (optional)
      queryId: 12345,
    });

  // to execute the transaction you need to send transaction to the blockchain
  // (replace with your wallet implementation, logging is used for demonstration purposes)
  console.log({
    to: tonProvisionTxParams.to,
    amount: tonProvisionTxParams.gasAmount,
    payload: tonProvisionTxParams.payload,
  });

  // transaction to provide 0.5 STON to STON/TON pool
  const jettonProvisionTxParams =
    await router.buildProvideLiquidityJettonTxParams({
      // address of the wallet that holds jetton you want to provide
      userWalletAddress: WALLET_ADDRESS,
      // address of the jetton you want to provide
      sendTokenAddress: JETTON0,
      // amount of the jetton you want to provide
      sendAmount: new TonWeb.utils.BN('500000000'),
      // address of the second jetton you want to provide
      otherTokenAddress: PROXY_TON,
      // minimal amount of the LP tokens you want to receive as a result of the provision
      minLpOut: new TonWeb.utils.BN(1),
      // query id to identify your transaction in the blockchain (optional)
      queryId: 12345,
    });

  // to execute the transaction you need to send transaction to the blockchain
  // (replace with your wallet implementation, logging is used for demonstration purposes)
  console.log({
    to: jettonProvisionTxParams.to,
    amount: jettonProvisionTxParams.gasAmount,
    payload: jettonProvisionTxParams.payload,
  });

  // after execution of both transactions liquidity provision process will be completed
  // and you will receive pool LP tokens
})();
import TonWeb from 'tonweb';

import { Router, ROUTER_REVISION, ROUTER_REVISION_ADDRESS } from '@ston-fi/sdk';

/**
 * This example shows how to swap two jettons using the router contract
 */
(async () => {
  const WALLET_ADDRESS = ''; // ! replace with your address
  const JETTON0 = 'EQA2kCVNwVsil2EM2mB0SkXytxCqQjS4mttjDpnXmwG9T6bO'; // STON
  const JETTON1 = 'EQBynBO23ywHy_CgarY9NK9FTz0yDsG82PtcbSTQgGoXwiuA'; // jUSDT

  const provider = new TonWeb.HttpProvider();

  const router = new Router(provider, {
    revision: ROUTER_REVISION.V1,
    address: ROUTER_REVISION_ADDRESS.V1,
  });

  // transaction to swap 1.0 JETTON0 to JETTON1 but not less than 1 nano JETTON1
  const swapTxParams = await router.buildSwapJettonTxParams({
    // address of the wallet that holds offerJetton you want to swap
    userWalletAddress: WALLET_ADDRESS,
    // address of the jetton you want to swap
    offerJettonAddress: JETTON0,
    // amount of the jetton you want to swap
    offerAmount: new TonWeb.utils.BN('1000000000'),
    // address of the jetton you want to receive
    askJettonAddress: JETTON1,
    // minimal amount of the jetton you want to receive as a result of the swap.
    // If the amount of the jetton you want to receive is less than minAskAmount
    // the transaction will bounce
    minAskAmount: new TonWeb.utils.BN(1),
    // query id to identify your transaction in the blockchain (optional)
    queryId: 12345,
    // address of the wallet to receive the referral fee (optional)
    referralAddress: undefined,
  });

  // to execute the transaction you need to send transaction to the blockchain
  // (replace with your wallet implementation, logging is used for demonstration purposes)
  console.log({
    to: swapTxParams.to,
    amount: swapTxParams.gasAmount,
    payload: swapTxParams.payload,
  });

  // reverse transaction is the same,
  // you just need to swap `offerJettonAddress` and `askJettonAddress` values
  // and adjust `offerAmount` and `minAskAmount` accordingly
})();
import TonWeb from 'tonweb';

import { Router, ROUTER_REVISION, ROUTER_REVISION_ADDRESS } from '@ston-fi/sdk';

/**
 * This example shows how to swap TON to jetton using the router contract
 */
(async () => {
  const WALLET_ADDRESS = ''; // ! replace with your address
  const JETTON0 = 'EQA2kCVNwVsil2EM2mB0SkXytxCqQjS4mttjDpnXmwG9T6bO'; // STON
  const PROXY_TON = 'EQCM3B12QK1e4yZSf8GtBRT0aLMNyEsBc_DhVfRRtOEffLez'; // ProxyTON

  const provider = new TonWeb.HttpProvider();

  const router = new Router(provider, {
    revision: ROUTER_REVISION.V1,
    address: ROUTER_REVISION_ADDRESS.V1,
  });

  // Because TON is not a jetton, to be able to swap TON to jetton
  // you need to use special SDK method to build transaction to swap TON to jetton
  // using proxy jetton contract.

  // transaction to swap 1.0 TON to JETTON0 but not less than 1 nano JETTON0
  const tonToJettonTxParams = await router.buildSwapProxyTonTxParams({
    // address of the wallet that holds TON you want to swap
    userWalletAddress: WALLET_ADDRESS,
    proxyTonAddress: PROXY_TON,
    // amount of the TON you want to swap
    offerAmount: new TonWeb.utils.BN('1000000000'),
    // address of the jetton you want to receive
    askJettonAddress: JETTON0,
    // minimal amount of the jetton you want to receive as a result of the swap.
    // If the amount of the jetton you want to receive is less than minAskAmount
    // the transaction will bounce
    minAskAmount: new TonWeb.utils.BN(1),
    // query id to identify your transaction in the blockchain (optional)
    queryId: 12345,
    // address of the wallet to receive the referral fee (optional)
    referralAddress: undefined,
  });

  // to execute the transaction you need to send transaction to the blockchain
  // (replace with your wallet implementation, logging is used for demonstration purposes)
  console.log({
    to: tonToJettonTxParams.to,
    amount: tonToJettonTxParams.gasAmount,
    payload: tonToJettonTxParams.payload,
  });

  // reverse transaction is just a regular swap transaction where `askJettonAddress` is a ProxyTon address.
  // see swap example for more details
})();

Jetton -> Jetton

0.265

0.205

Jetton -> pTON

0.185

0.125

pTON -> Jetton

swap_amount + 0.215

0.215

// (jetton_notification + swap) + (pay_to + jetton_transfer) * 2
fwd_amount = 0.045 + (0.02 + 0.06) * 2 // = 0.205
// jetton_transfer + fwd_amount
tx_amount = 0.06 + fwd_amount // = 0.265
// (jetton_notification + swap) + (pay_to + pton_transfer) * 2
fwd_amount = 0.045 + (0.02 + 0.02) * 2 // = 0.125
// jetton_transfer + fwd_amount
tx_amount = 0.06 + fwd_amount // = 0.185
// pton_transfer + (pay_to + jetton_transfer) * 2 + (jetton_notification + swap)
fwd_amount = 0.01 + (0.02 + 0.06) * 2 + 0.045 // = 0.215
// jetton_transfer + fwd_amount
tx_amount = swap_amount + fwd_amount // = swap_amount + 0.215

provide liquidity

This section contains SDK example for providing liquidity in DEX

If you are simulating a liquidity provision with the simulate liquidity provision endpoint and your LP account already holds some tokens (e.g., "lp_account_token_a_balance": "more than 0" or "lp_account_token_b_balance": "more than 0"), setting token_a_units or token_b_units in request might lead to an inaccurate min_lp_units in the simulation result. This could result in permanent loss of funds. We strongly encourage you to refund any old LP account tokens before proceeding.

Provide liquidity for a pool

Jetton/Jetton pool deposit (two sides)

import { TonClient, toNano } from "@ton/ton";
import { DEX } from "@ston-fi/sdk";

const USER_WALLET_ADDRESS = ""; // ! replace with your address
const JETTON_0_ADDRESS = "kQDLvsZol3juZyOAVG8tWsJntOxeEZWEaWCbbSjYakQpuYN5"; // TestRED
const JETTON_1_ADDRESS = "kQB_TOJSB7q3-Jm1O8s0jKFtqLElZDPjATs5uJGsujcjznq3"; // TestBLUE

const client = new TonClient({
  endpoint: "https://testnet.toncenter.com/api/v2/jsonRPC",
});

const router = client.open(
  DEX.v2_1.Router.CPI.create(
    "kQALh-JBBIKK7gr0o4AVf9JZnEsFndqO0qTCyT-D-yBsWk0v" // CPI Router v2.1.0
  )
);

const txParams = await Promise.all([
  // deposit 1 TestRED to the TestRED/TestBLUE pool and get at least 1 nano LP token
  router.getProvideLiquidityJettonTxParams({
    userWalletAddress: USER_WALLET_ADDRESS,
    sendTokenAddress: JETTON_0_ADDRESS,
    sendAmount: toNano("1"),
    otherTokenAddress: JETTON_1_ADDRESS,
    minLpOut: "1",
    queryId: 12345,
  }),
  // deposit 1 TestBLUE to the TestRED/TestBLUE pool and get at least 1 nano LP token
  router.getProvideLiquidityJettonTxParams({
    userWalletAddress: USER_WALLET_ADDRESS,
    sendTokenAddress: JETTON_1_ADDRESS,
    sendAmount: toNano("1"),
    otherTokenAddress: JETTON_0_ADDRESS,
    minLpOut: "1",
    queryId: 123456,
  }),
]);

To execute the transaction, you need to send a transaction with these parameters to the blockchain. This code will be different based on the wallet you are using to send the tx from, so please check our doc section about transaction sending guide with examples for different libraries.

Jetton/Jetton pool deposit (single sides)

const USER_WALLET_ADDRESS = ""; // ! replace with your address
const JETTON_0_ADDRESS = "kQDLvsZol3juZyOAVG8tWsJntOxeEZWEaWCbbSjYakQpuYN5"; // TestRED
const JETTON_1_ADDRESS = "kQB_TOJSB7q3-Jm1O8s0jKFtqLElZDPjATs5uJGsujcjznq3"; // TestBLUE

const client = new TonClient({
  endpoint: "https://testnet.toncenter.com/api/v2/jsonRPC",
});

const router = client.open(
  DEX.v2_1.Router.CPI.create(
    "kQALh-JBBIKK7gr0o4AVf9JZnEsFndqO0qTCyT-D-yBsWk0v" // CPI Router v2.1.0
  )
);

// deposit 1 TestRED to the TestRED/TestBLUE pool and get at least 1 nano LP token
const txParams = await router.getSingleSideProvideLiquidityJettonTxParams({
  userWalletAddress: USER_WALLET_ADDRESS,
  sendTokenAddress: JETTON_0_ADDRESS,
  sendAmount: toNano("1"),
  otherTokenAddress: JETTON_1_ADDRESS,
  minLpOut: "1",
  queryId: 123456,
});

To execute the transaction, you need to send a transaction with these parameters to the blockchain. This code will be different based on the wallet you are using to send the tx from, so please check our doc section about transaction sending guide with examples for different libraries.

TON/Jetton pool deposit (two sides)

import { TonClient, toNano } from "@ton/ton";
import { DEX, pTON } from "@ston-fi/sdk";

const USER_WALLET_ADDRESS = ""; // ! replace with your address
const JETTON_0_ADDRESS = "kQDLvsZol3juZyOAVG8tWsJntOxeEZWEaWCbbSjYakQpuYN5"; // TestRED

const client = new TonClient({
  endpoint: "https://testnet.toncenter.com/api/v2/jsonRPC",
});

const router = client.open(
  DEX.v2_1.Router.CPI.create(
    "kQALh-JBBIKK7gr0o4AVf9JZnEsFndqO0qTCyT-D-yBsWk0v" // CPI Router v2.1.0
  )
);

const proxyTon = pTON.v2_1.create(
  "kQACS30DNoUQ7NfApPvzh7eBmSZ9L4ygJ-lkNWtba8TQT-Px" // pTON v2.1.0
);

const txParams = await Promise.all([
  // deposit 1 TON to the TON/TestRED pool and get at least 1 nano LP token
  router.getProvideLiquidityTonTxParams({
    userWalletAddress: USER_WALLET_ADDRESS,
    proxyTon,
    sendAmount: toNano("1"),
    otherTokenAddress: JETTON_0_ADDRESS,
    minLpOut: "1",
    queryId: 12345,
  }),
  // deposit 1 TestRED to the TON/TestRED pool and get at least 1 nano LP token
  router.getProvideLiquidityJettonTxParams({
    userWalletAddress: USER_WALLET_ADDRESS,
    sendTokenAddress: JETTON_0_ADDRESS,
    sendAmount: toNano("1"),
    otherTokenAddress: proxyTon.address,
    minLpOut: "1",
    queryId: 123456,
  }),
]);

To execute the transaction, you need to send a transaction with these parameters to the blockchain. This code will be different based on the wallet you are using to send the tx from, so please check our doc section about transaction sending guide with examples for different libraries.

TON/Jetton pool deposit (single sides)

import { TonClient, toNano } from "@ton/ton";
import { DEX, pTON } from "@ston-fi/sdk";

const USER_WALLET_ADDRESS = ""; // ! replace with your address
const JETTON_0_ADDRESS = "kQDLvsZol3juZyOAVG8tWsJntOxeEZWEaWCbbSjYakQpuYN5"; // TestRED

const client = new TonClient({
  endpoint: "https://testnet.toncenter.com/api/v2/jsonRPC",
});

const router = client.open(
  DEX.v2_1.Router.CPI.create(
    "kQALh-JBBIKK7gr0o4AVf9JZnEsFndqO0qTCyT-D-yBsWk0v" // CPI Router v2.1.0
  )
);

const proxyTon = pTON.v2_1.create(
  "kQACS30DNoUQ7NfApPvzh7eBmSZ9L4ygJ-lkNWtba8TQT-Px" // pTON v2.1.0
);

// deposit 1 TON to the TON/TestRED pool and get at least 1 nano LP token
const txParams = await router.getSingleSideProvideLiquidityTonTxParams({
  userWalletAddress: USER_WALLET_ADDRESS,
  proxyTon,
  sendAmount: toNano("1"),
  otherTokenAddress: JETTON_0_ADDRESS,
  minLpOut: "1",
  queryId: 12345,
});

To execute the transaction, you need to send a transaction with these parameters to the blockchain. This code will be different based on the wallet you are using to send the tx from, so please check our doc section about transaction sending guide with examples for different libraries.

v0.5 > v1.0.0 migration guide

This section contains migration guide for SDK v0.5.* -> v1.0.0

Introduction

In the SDK v1.0.0 release, we introduce some breaking changes. You can find the complete list of the release changes in the CHANGELOG.md file, but in this docs section, we will be focused on some real code snippet migration from the SDK v0.5.* to v1.0.0

Let's migrate the v0.5.* jetton to TON swap example code to the v1.0.0:

import TonWeb from "tonweb";
import { DEX, pTON } from "@ston-fi/sdk";

const router = new DEX.v1.Router({
  tonApiClient: new TonWeb.HttpProvider(),
});

const tonToJettonTxParams = await router.buildSwapJettonToTonTxParams({
  userWalletAddress: "", // ! replace with your address
  offerJettonAddress: "EQA2kCVNwVsil2EM2mB0SkXytxCqQjS4mttjDpnXmwG9T6bO", // STON
  offerAmount: new TonWeb.utils.BN("1000000000"),
  proxyTonAddress: pTON.v1.address,
  minAskAmount: new TonWeb.utils.BN("1"),
  queryId: 12345,
});

TonWeb package replacement with @ton/ton

The main source of this release's breaking changes is the migration from the tonweb to the @ton/ton package. So, the first step is to add the @ton/ton package to your project. To do that, follow the installation step from their docs.

As a result of this step, we need to be able to create an instance of TonClient that @ton-ton package uses to make on-chain requests.

import { TonClient } from "@ton/ton";

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

Contract constructor interface change

Since the @ton package makes an on-chain request by injecting the provider into open contract methods, SDK contracts no longer need to require a tonApiClient in constructor parameters. So, the contract's constructor interface has been changed to accept the address as a first parameter and the configuration object as a second parameter.

import { DEX } from "@ston-fi/sdk";

type ContractsConstructorParameters = ConstructorParameters<
  typeof DEX.v1.Router
>;
// ^: [address: Address | string, (RouterV1Options | undefined)?]

That means router contract creation in the code we are migrating needed to be changed to the following:

- import TonWeb from 'tonweb';

const router = new DEX.v1.Router(
-  {
-    tonApiClient: new TonWeb.HttpProvider(),
-  }
);

Router.v1 has a known default address, so the first constructor parameter, in this case, is optional. For other contracts, there is no default address, so it should be specified

const pool = new DEX.v1.Pool(
  "EQCl-ax-5QC06ub096s2bqTbhYZWigZ1-FkJm2IVIMSazp7U" // STON/GEMSTON pool
);

If you specified custom gasConstants for a contract, it is still possible to do so. Only instead of passing them as part of the first constructor parameter object, you need to pass them in a second object

const pool = new DEX.v1.Pool(
+ 'EQCl-ax-5QC06ub096s2bqTbhYZWigZ1-FkJm2IVIMSazp7U',
  {
-    address: 'EQCl-ax-5QC06ub096s2bqTbhYZWigZ1-FkJm2IVIMSazp7U',
    gasConstants: {
        burn: DEX.v1.Pool.gasConstants.burn,
    }
  }
);

"Opening" of the contracts

The @ton/ton package needs to be opened for use in making on-chain requests from the contract.

import { TonClient } from "@ton/ton";
import { DEX } from '@ston-fi/sdk';

const client = new TonClient({
  endpoint: 'https://toncenter.com/api/v2/jsonRPC',
});

- const router = new DEX.v1.Router();
+ const router = client.open(new DEX.v1.Router());

build*TxParams methods renaming

All contracts build*TxParams methods were renamed to get*TxParams. This was needed to math @ton/ton package convention for methods that could make on-chain request

- const tonToJettonTxParams = await router.buildSwapJettonToTonTxParams({

+ const tonToJettonTxParams = await router.getSwapJettonToTonTxParams({

BN.js replacement with native BigInt

In the previous version of the SDK package, to represent the amount, we used BN.js, which came as a part of the TonWeb package and was accessible via TonWeb.utils.BN. @ton/ton package uses native javascript BigInt instead of BN.js.

And the migration is peaty easy - since the constructors of BN and BigInt are comparable. We only need to replace Class that represents the amount

-  offerAmount: new TonWeb.utils.BN('1000000000'),
+  offerAmount: BigInt('1000000000'),
-  minAskAmount: new TonWeb.utils.BN('1'),
+  minAskAmount: BigInt('1'),

Operations with pTON

Previously, every operation that worked with a native TON requested a proxyTonAddress. But since we are developing the next version of the proxyTon contract with some differences in the ton sending transaction instead of only the address, we need to know more about the proxyTon.

So now operations with native ton require the instance of pTON contracts.

const tonToJettonTxParams = await router.getSwapJettonToTonTxParams({
-  proxyTonAddress: pTON.v1.address,
+  proxyTon: new pTON.v1(),
});

txParams shape change and MessageData type drop

To better match @ton/ton package interfaces, we slightly changed the object's shape that returns from the get*TxParams methods. The change is only in names; the purpose of this field stayed unchanged. It describes the transaction that, if sent, will cause the requested action on the DEX.

This change lets us use the get*TxParams methods output directly in the @ton/ton send functionality.

Before

type TxParams = typeof tonToJettonTxParams;
// ^: { to: Address, gasAmount: BN, payload: Cell }

After

type TxParams = typeof tonToJettonTxParams;
// ^: { to: Address, value: BigInt, body?: Cell | null }

Because we switched from our MessageData interface to @ton/ton SenderArguments type, MessageData export from the SDK package was removed

Contracts method renaming

In v1.0.0, some of the contract methods were renamed:

v0.5.*

v1.0.0

RouterV1.getData

RouterV1.getRouterData

PoolV1.getData

PoolV1.getPoolData

LpAccountV1.getData

LpAccountV1.getLpAccountData

Conclusion

And in the end this is a code we have migrated to

import { TonClient } from "@ton/ton";
import { DEX, pTON } from "@ston-fi/sdk";

const client = new TonClient({
  endpoint: "https://ton-api.ston.fi/jsonRPC",
});

const router = client.open(new DEX.v1.Router());

const tonToJettonTxParams = await router.getSwapJettonToTonTxParams({
    userWalletAddress: "", // ! replace with your address
    offerJettonAddress: "EQA2kCVNwVsil2EM2mB0SkXytxCqQjS4mttjDpnXmwG9T6bO", // STON
    offerAmount: BigInt("1000000000"),
    proxyTon: new pTON.v1(),
    minAskAmount: BigInt("1"),
    queryId: 12345,
  });

Swap extra

This document explains how to populate and interpret the extra field in SwapChunk (as referenced in omniston-swap.md), specifically when using protocols like StonFiV1, StonFiV2, DeDust, or TonCo.

Overview

In the Omniston Swap protocol, each SwapChunk contains two fields for protocol-specific data:

  • extra_version (integer) - Indicates the version of the data structure being used

  • extra (bytes) - Contains the protocol-specific data as a base64-encoded byte array

These fields allow different DEX protocols to include their own parameters and configuration for executing swaps. This document describes the format of that data and how to encode it properly.

Protocol Definitions

message ExtraData {
  oneof extra {
    StonFiV1Extra ston_fi_v1 = 1;
    StonFiV2Extra ston_fi_v2 = 2;
    DeDustExtra de_dust = 3;
    TonCoExtra ton_co = 4;
  }
}

message StonFiV1Extra {
  string pool_address = 1;
  string min_ask_amount = 2;

  // Filled by Omniston service
  string recommended_min_ask_amount = 50;
}

message StonFiV2Extra {
  string pool_address = 1;
  string min_ask_amount = 2;

  // Filled by Omniston service
  string recommended_min_ask_amount = 50;
}

message DeDustExtra {
  string pool_address = 1;
  string min_ask_amount = 2;
  string referrer_address = 3;  // Specified in `referralAddress` swap parameter in DeDust SDK

  // Filled by Omniston service
  string recommended_min_ask_amount = 50;
}

message TonCoExtra {
  string pool_address = 1;
  string min_ask_amount = 2;
  string limit_sqrt_price = 3;

  // Filled by Omniston service
  string recommended_min_ask_amount = 50;
}

Field Descriptions

Common Fields

All protocol extras share these common fields:

  • pool_address (string)

    • The address of the DEX pool contract that will execute the swap

    • This is specific to the token pair being swapped and the protocol being used

  • min_ask_amount (string)

    • The minimum amount of ask tokens that must be received for this chunk

    • If the protocol cannot provide at least this amount, the swap will be aborted

    • Used to protect against slippage and ensure minimum received amount

  • recommended_min_ask_amount (string)

    • The recommended minimum amount of ask tokens (filled by Omniston service)

    • This field provides an optimized slippage protection value calculated by the platform

    • Available for all protocols starting with extra version 1

Protocol-Specific Fields

DeDust Protocol

The DeDust protocol includes an additional field:

  • referrer_address (string)

    • Optional address that may receive referral rewards/fees according to DeDust's rules

    • This is specified in the referralAddress parameter when using the DeDust SDK

TonCo Protocol

The TonCo protocol includes an additional field:

  • limit_sqrt_price (string)

    • Square root price limit for the swap operation

    • Used to control price impact and provide additional slippage protection

    • This parameter is specific to TonCo's concentrated liquidity model

Usage Example

Here's an example of how to construct and encode the extra data for a StonFiV2 swap:

// 1. Create the protocol-specific extra data
const stonFiV2Extra = {
  pool_address: "EQA...", // Address of the StonFi pool
  min_ask_amount: "1000000", // Minimum tokens to receive
};

// 2. Wrap in ExtraData message
const extraData = {
  extra: {
    oneofKind: "ston_fi_v2",
    ston_fi_v2: stonFiV2Extra,
  },
};

// 3. Serialize to protobuf bytes
const extraBytes = ExtraData.encode(extraData).finish();

// 4. Base64 encode for JSON transport
const extraBase64 = Buffer.from(extraBytes).toString("base64");

// 5. Use in SwapChunk
const chunk = {
  protocol: "StonFiV2",
  offer_amount: "2000000",
  ask_amount: "1950000",
  extra_version: 1,
  extra: extraBase64,
};

Integration with Swap Flow

When the Omniston server processes a swap request:

  1. It examines the protocol field of each SwapChunk to determine which DEX to use

  2. Based on the extra_version, it knows how to decode the extra field

  3. The decoded data provides the necessary parameters (pool address, minimum amounts, etc.) to execute the swap through the chosen DEX

For more details on the overall swap flow and how chunks fit into routes and steps, see omniston-swap.md.

Version History

  • Version 1: Initial version supporting StonFiV1, StonFiV2, DeDust, and TonCo protocols. Note: Currently, only extra_version = 1 is supported for all protocols.

    • Added recommended_min_ask_amount field filled by Omniston service for optimized slippage protection

    • Added support for TonCo protocol with limit_sqrt_price parameter

See Also

  • omniston-swap.md - Main swap protocol documentation

  • omniston-nodejs.md - Node.js SDK usage

  • omniston-react.md - React components and hooks

Resolvers (How to become a resolver)

This guide explains how to become a resolver (Market Maker) in the Omniston protocol and integrate with its gRPC API.

Overview

A resolver is a service that provides token exchange rates and executes trades. To become a resolver, you need to:

  1. Register by obtaining a Soul-Bound Token (SBT)

  2. Connect to the gRPC API

  3. Handle quote requests and trades

Registration Process

Step 1: Generate Keys

First, generate one or more Ed25519 key pairs that will be used for authentication:

JavaScript implementation

Rust implementation

For other languages, you can use any standard Ed25519 implementation library available in your preferred programming language

Step 2: Prepare Metadata

Create a JSON document containing:

  • Your resolver name

  • Your public key(s)

Example:

{
  "name": "MyResolver",
  "publicKeys": [
    "b951254b184fae6906a61ab01e37296fbb28961494cacf7befac9f638fcfe40c",
    "9397ddd52a0d6033da4a32e654b4afbddcc5d832575e396c0a6f5b213faa199f"
  ]
}

Step 3: Submit Registration

Link to google form

  1. Your metadata will be stored in an SBT NFT collection

  2. You'll receive your SBT stake address for API authentication

Connecting to the API

Authentication

  1. Create a connection payload:

message ConnectPayload {
  uint64 connect_timestamp = 1;  // Current timestamp in milliseconds
  string stake_address = 2;      // Your SBT stake address
}
  1. Sign the payload:

  • Serialize the ConnectPayload to bytes

  • Sign using your Ed25519 private key

  • Base64 encode the signature

  1. Send a ConnectRequest:

message ConnectRequest {
  bytes payload = 1;    // Serialized ConnectPayload
  bytes public_key = 2; // Your Ed25519 public key
  bytes signature = 3;  // Signature of the payload
}

Example connection request:

{
  "connect": {
    "payload": "CPKnlt2xYxIwRVFDWFNzMnhaMmRoazlUQXh6R3pYcmEyRWJHX1MyU3F5TjhUZmk2Zko4MkVZaVZq",
    "public_key": "TAPsnPvWhlOvxEK19rONv4sueMeQzOzlrrIFUOKsi34=",
    "signature": "us7nMd9wmUOkuPk0otg6dvUojZwj8VcyXU6HD13BDQhVrzV8sKgyXtKze+9+j9FC1Ghxkx7Jo5FIDeE8ljbADQjyp5bdsWMSMEVRQ1hTczJ4WjJkaGs5VEF4ekd6WHJhMkViR19TMlNxeU44VGZpNmZKODJFWWlWag=="
  },
  "seqno": 1
}

Message Flow

The resolver API uses a bidirectional gRPC stream. After connecting:

1. Incoming Events

You'll receive these events from the server:

message ResolverEvent {
  oneof mux {
    uint64 seqno = 1;    // Server-generated event ID
    uint64 reply_to = 2;  // References your request seqno
  }

  oneof event {
    QuoteRequestedEvent quote_requested = 10;
    QuoteRequestCancelledEvent quote_request_cancelled = 11;
    QuoteAcceptedEvent quote_accepted = 20;
    QuoteRejectedEvent quote_rejected = 21;
    QuoteInvalidatedEvent quote_invalidated = 22;
    KeepAlive keep_alive = 100;
  }
}

Key events:

  • QuoteRequestedEvent: New quote request from a trader

  • QuoteRequestCancelledEvent: Trader cancelled their request

  • QuoteAcceptedEvent: Your quote was accepted

  • QuoteRejectedEvent: Your quote was rejected

  • QuoteInvalidatedEvent: Confirmation of quote invalidation

2. Outgoing Requests

You can send these requests to the server:

message ResolverRequest {
  oneof mux {
    uint64 seqno = 1;    // Your request ID
    uint64 reply_to = 2;  // References server event seqno
  }

  oneof request {
    ConnectRequest connect = 10;
    UpdateQuoteRequest update_quote = 11;
    InvalidateQuoteRequest invalidate_quote = 12;
  }
}

Key requests:

  • UpdateQuoteRequest: Provide or update a quote

  • InvalidateQuoteRequest: Cancel a previously provided quote

Quote Flow

1. Receiving Quote Requests

When you receive a QuoteRequestedEvent:

message QuoteRequestedEvent {
  string rfq_id = 1;                      // Quote request ID
  QuoteRequest quote_request = 2;         // The actual request
  uint32 protocol_fee_bps = 3;           // Protocol fee in basis points
  sint64 request_timestamp = 4;          // When request was made
  sint64 quote_validity_timeout = 5;     // How long quote should be valid
  sint64 deposit_settling_delay = 6;     // Min delay before settlement
  sint64 resolve_timeout = 7;            // Max time to complete trade
}

2. Providing Quotes

Respond with an UpdateQuoteRequest:

message UpdateQuoteRequest {
  string rfq_id = 1;                    // From QuoteRequestedEvent
  string bid_units = 2;               // Amount trader pays
  string ask_units = 3;                 // Amount trader receives
  uint64 trade_start_deadline = 4;      // Quote expiration time

  oneof params {
    ResolverSwapParams swap = 20;       // For swap settlement
    ResolverEscrowParams escrow = 21;   // For escrow settlement
    ResolverHtlcParams htlc = 22;       // For HTLC settlement
  }
}

For swap settlement, include route information:

message ResolverSwapParams {
  repeated SwapRoute routes = 1;  // Possible swap paths
}

Important: When specifying the protocol in a SwapChunk, the gRPC API expects it as one of the strings "StonFiV1", "StonFiV2", "DeDust", or "TonCo". Currently, only extra_version = 1 is supported.

3. Quote Lifecycle

  1. You receive QuoteRequestedEvent

  2. You send UpdateQuoteRequest

  3. The server validates your quote and responds with:

    • QuoteAcceptedEvent: Your quote was validated and accepted, includes quote_id.

    • QuoteRejectedEvent: Your quote was rejected. The event includes a code field from the QuoteRejectedCode enum. Possible reasons include:

      • UNDEFINED (0) - Uncategorized error

      • INVALID_PARAMETERS (1) - Invalid value of a parameter

      • INVALID_AMOUNTS (2) - Asset amount restrictions don't pass RFQ requirements

      • ROUTE_PROHIBITED (3) - Route uses a prohibited intermediate asset

      • POOL_PROHIBITED (4) - Route uses a unverified or prohibited liquidity pool

      • EMULATION_RESULT_MISMATCH (5) - Transaction emulation produced a result different from the quote

      • INTERNAL_ERROR (101) - Server errors

  4. At any time, you can:

    • Send new UpdateQuoteRequest to update the quote

    • Send InvalidateQuoteRequest to cancel the quote

Best Practices

  1. Sequence Numbers

    • Use monotonically increasing seqno for your requests

    • Match reply_to with event seqno when responding

    • Track request/response correlation using seqno

  2. Quote Management

    • Honor your quotes until trade_start_deadline

    • Invalidate quotes you can't fulfill

    • Include all fees in your quoted amounts

  3. Error Handling

    • Reconnect on connection loss

    • Handle all event types

    • Log rejected quotes for monitoring

  4. Performance

    • Maintain a single gRPC connection

    • Process events asynchronously

    • Buffer outgoing requests

API Endpoints

Production:

  • gRPC: https://omni-grpc.ston.fi

Sandbox:

  • Demo site: https://omniston-sandbox.ston.fi/

  • WS API: wss://omni-ws-sandbox.ston.fi

  • gRPC API: https://omni-grpc-sandbox.ston.fi

See Also

  • Swap Protocol Documentation

v0.4 > v0.5 migration guide

This section contains migration guide for SDK v0.4.* -> v0.5.*

Introduction

In the SDK v0.5 release, we introduce some breaking changes. You can find the full list of the release changes in the CHANGELOG.md file, but in this docs section, we will be focused on some real code snippet migration from the SDK v0.4 to v0.5

Let's migrate the v0.4 jetton to TON swap example code to the v0.5:

import TonWeb from 'tonweb';
import { Router, ROUTER_REVISION, ROUTER_REVISION_ADDRESS } from '@ston-fi/sdk';

const WALLET_ADDRESS = ''; // ! replace with your address
const JETTON0 = 'EQA2kCVNwVsil2EM2mB0SkXytxCqQjS4mttjDpnXmwG9T6bO'; // STON
const PROXY_TON = 'EQCM3B12QK1e4yZSf8GtBRT0aLMNyEsBc_DhVfRRtOEffLez'; // ProxyTON

const provider = new TonWeb.HttpProvider();

const router = new Router(provider, {
  revision: ROUTER_REVISION.V1,
  address: ROUTER_REVISION_ADDRESS.V1,
});

const tonToJettonTxParams = await router.buildSwapJettonTxParams({
  userWalletAddress: WALLET_ADDRESS,
  offerJettonAddress: JETTON0,
  offerAmount: new TonWeb.utils.BN('1000000000'),
  askJettonAddress: PROXY_TON,
  minAskAmount: new TonWeb.utils.BN('1'),
  queryId: 12345,
});

Contracts export change

The first thing you will face after the SDK update is that Module '"@ston-fi/sdk"' has no exported member Router.

This is because contracts (such as Router, Pool, LpAccount, etc) now no longer exported by their names. We start to use the export of domain related objects that will contain all the contracts.

At the current state SDK exports 3 domain object DEX, FARM, pTON.

So the first required change to our code is to replace the import of Router contract with the DEX object.

- import { Router, ROUTER_REVISION, ROUTER_REVISION_ADDRESS } from '@ston-fi/sdk';

+ import { DEX, ROUTER_REVISION, ROUTER_REVISION_ADDRESS } from '@ston-fi/sdk';

Contract revision drop

Because multiple versions of the contracts appeared with uncomparable interfaces, we were forced to drop the architecture with revisions and the root class that obtained multiple revisions.

before:

.
└── dex
    └── Router
        └── Router
            ├── RouterRevisionV1
            └── RouterRevisionV2

after:

.
└── dex
    └── Router
        ├── RouterV1
        └── RouterV2

If you use extends or overrides contract revisions from the SDK anywhere in your code, you will now need to override the contract class instead.

If you use the override of the revision to define custom gas constants, it's now possible to pass gas constants as constructor parameters.

Contract version

Since we dropped revisions but still need to be able to separate different contracts, every contract now provides the statice field version. And to math this, *_REVISION enums was renamed to *_VERSION.

Now you can use it like this:

import { DEX, DEX_VERSION } from '@ston-fi/sdk';

DEX.v1.Router.version === DEX_VERSION.v1

*_VERSION enum values is in lover case (v1) to match the STON.fi API responses

Back to our code - you can just remove import and usage of the ROUTER_REVISION completely and instead, to determine witch version of the DEX contract you would like to use, use version key of the domain object (DEX in this case)

- import { DEX, ROUTER_REVISION, ROUTER_REVISION_ADDRESS } from '@ston-fi/sdk';

+ import { DEX, ROUTER_REVISION_ADDRESS } from '@ston-fi/sdk';

-  const router = new Router(provider, {
-   revision: ROUTER_REVISION.V1,
+  const router = new Dex.v1.Router(provider, {
    address: ROUTER_REVISION_ADDRESS.V1,
  });

Contract address constant drop

For contracts with known addresses (for example, a router), the address is available as a static field on the class and is no longer required to be specified in the constructor.

import { DEX, pTON } from '@ston-fi/sdk';

DEX.v1.Router.address; // EQB3ncyBUTjZUA5EnFKR5_EnOMI9V1tTEAAPaiU71gc4TiUt
pTON.v1.address; // EQCM3B12QK1e4yZSf8GtBRT0aLMNyEsBc_DhVfRRtOEffLez

The following changes need to be made on this step

- import { DEX, ROUTER_REVISION_ADDRESS } from '@ston-fi/sdk';
+ import { DEX } from '@ston-fi/sdk';

  const router = new Dex.v1.Router(provider, {
-  address: ROUTER_REVISION_ADDRESS.V1,
  });

Contract constructor parameters

All contract constructors now require 1 object with tonApiClient field to be passed. tonApiClient is a name for what in TonWeb is named provider.

-   const provider = new TonWeb.HttpProvider();

- const router = new Dex.v1.Router(provider, {
+ const router = new DEX.v1.Router({
+    tonApiClient: new TonWeb.HttpProvider(),
  });

Optionally, you can pass an instance of stonApiClient. This is a class from @ston-fi/api package. If it is passed, STON.fi public REST API will be used to fetch data instead of using direct on-chain methods called via TON API.

At the current state of the package development, this client is optional, but we expect it to be required in the future when it no longer is possible to get some data required for the SDK to work from on-chain and

Contracts method renaming

In v0.5, some of the contract methods were renamed:

v0.4

v0.5

buildSwapJettonTxParams

buildSwapJettonToJettonTxParams & buildSwapJettonToTonTxParams

buildSwapProxyTonTxParams

buildSwapTonToJettonTxParams

buildProvideLiquidityProxyTonTxParams

buildProvideLiquidityTonTxParams

Our code snippet using renamed fn buildSwapJettonTxParams that was split in two: buildSwapJettonToJettonTxParams & buildSwapJettonToTonTxParams. Since we are swapping a jetton to TON, we will use a new fn buildSwapJettonToTonTxParams

-   const tonToJettonTxParams = await router.buildSwapJettonTxParams({

+   const tonToJettonTxParams = await router.buildSwapJettonToTonTxParams({

buildSwapJettonToTonTxParams fn now explicitly requires us to pass the address of the pTON contract to be used in the swap. Previously, it required the same, but under the askJettonAddress name, that was confusing.

-  askJettonAddress: PROXY_TON,

+  proxyTonAddress: PROXY_TON,

pTON contract

Because pTON class is now exported from the SDK we can use address from the pTON contract instead of a hardcoded string value in our code

- import { DEX } from '@ston-fi/sdk';
+ import { DEX, pTON } from '@ston-fi/sdk';

- const PROXY_TON = 'EQCM3B12QK1e4yZSf8GtBRT0aLMNyEsBc_DhVfRRtOEffLez'; // ProxyTON
+ const PROXY_TON = pTON.v1.address;

Conclusion

And in the end this is a code we have migrated to

import TonWeb from 'tonweb';
import { DEX, pTON } from '@ston-fi/sdk';

const router = new DEX.v1.Router({
  tonApiClient: new TonWeb.HttpProvider(),
});

const tonToJettonTxParams = await router.buildSwapJettonToTonTxParams({
  userWalletAddress: "", // ! replace with your address
  offerJettonAddress: 'EQA2kCVNwVsil2EM2mB0SkXytxCqQjS4mttjDpnXmwG9T6bO', // STON
  offerAmount: new TonWeb.utils.BN('1000000000'),
  proxyTonAddress: pTON.v1.address,
  minAskAmount: new TonWeb.utils.BN('1'),
  queryId: 12345,
});

swap

This section contains SDK example for performing swap in DEX

In this section, to illustrate all three possible types of a swap, we will do following exchange chain

  1. swap 1 TON to TesREED (ton to jetton swap)

  2. swap TesREED to TestBlue (jetton to jetton swap)

  3. swap TestBlue back to TON (jetton to ton swap)

Swap TON to jetton

Referral Fees: You can specify a referral fee in your swap if needed. For bigger context, see the Omniston referral fees guide (although Omniston-oriented, the linked paragraph details how DEX V2 referral fees operate).

import { TonClient, toNano } from "@ton/ton";
import { DEX, pTON } from "@ston-fi/sdk";

const client = new TonClient({
  endpoint: "https://testnet.toncenter.com/api/v2/jsonRPC",
});

const router = client.open(
  DEX.v2_1.Router.CPI.create(
    "kQALh-JBBIKK7gr0o4AVf9JZnEsFndqO0qTCyT-D-yBsWk0v" // CPI Router v2.1.0
  )
);

const proxyTon = pTON.v2_1.create(
  "kQACS30DNoUQ7NfApPvzh7eBmSZ9L4ygJ-lkNWtba8TQT-Px" // pTON v2.1.0
);

// swap 1 TON to TestRED but not less than 1 nano TestRED
const txParams = await router.getSwapTonToJettonTxParams({
  userWalletAddress: "", // ! replace with your address
  proxyTon: proxyTon,
  offerAmount: toNano("1"),
  askJettonAddress: "kQDLvsZol3juZyOAVG8tWsJntOxeEZWEaWCbbSjYakQpuYN5", // TestRED
  minAskAmount: "1",
  // Optional referral parameters:
  referralAddress: "", // your referral TON address
  referralValue: 10,   // e.g. 10 => 0.1% fee
  queryId: 12345,
});

To execute the transaction, you need to send a transaction with these parameters to the blockchain. This code will be different based on the wallet you are using to send the tx from, so please check our doc section about transaction sending guide with examples for different libraries.

Swap jetton to jetton

Referral Fees: You can specify a referral fee in your swap if needed. For bigger context, see the Omniston referral fees guide (although Omniston-oriented, the linked paragraph details how DEX V2 referral fees operate).

import { TonClient, toNano } from "@ton/ton";
import { DEX, pTON } from "@ston-fi/sdk";

const client = new TonClient({
  endpoint: "https://testnet.toncenter.com/api/v2/jsonRPC",
});

const router = client.open(
  DEX.v2_1.Router.CPI.create(
    "kQALh-JBBIKK7gr0o4AVf9JZnEsFndqO0qTCyT-D-yBsWk0v" // CPI Router v2.1.0
  )
);

// swap 1 TesREED to TestBlue but not less than 1 nano TestBlue
const txParams = await router.getSwapJettonToJettonTxParams({
  userWalletAddress: "", // ! replace with your address
  offerJettonAddress: "kQDLvsZol3juZyOAVG8tWsJntOxeEZWEaWCbbSjYakQpuYN5", // TesREED
  offerAmount: toNano("1"),
  askJettonAddress: "kQB_TOJSB7q3-Jm1O8s0jKFtqLElZDPjATs5uJGsujcjznq3", // TestBlue
  minAskAmount: "1",
  // Optional referral parameters:
  referralAddress: "", // your referral TON address
  referralValue: 25,   // e.g. 25 => 0.25% fee
  queryId: 12345,
});

To execute the transaction, you need to send a transaction with these parameters to the blockchain. This code will be different based on the wallet you are using to send the tx from, so please check our doc section about transaction sending guide with examples for different libraries.

Swap jetton to TON

Referral Fees: You can specify a referral fee in your swap if needed. For bigger context, see the Omniston referral fees guide (although Omniston-oriented, the linked paragraph details how DEX V2 referral fees operate).

import { TonClient, toNano } from "@ton/ton";
import { DEX, pTON } from "@ston-fi/sdk";

const client = new TonClient({
  endpoint: "https://testnet.toncenter.com/api/v2/jsonRPC",
});

const router = client.open(
  DEX.v2_1.Router.CPI.create(
    "kQALh-JBBIKK7gr0o4AVf9JZnEsFndqO0qTCyT-D-yBsWk0v" // CPI Router v2.1.0
  )
);

const proxyTon = pTON.v2_1.create(
  "kQACS30DNoUQ7NfApPvzh7eBmSZ9L4ygJ-lkNWtba8TQT-Px" // pTON v2.1.0
);

// swap 1 TestRED to TON but not less than 1 nano TON
const txParams = await router.getSwapJettonToTonTxParams({
  userWalletAddress: "", // ! replace with your address
  offerJettonAddress: "kQDLvsZol3juZyOAVG8tWsJntOxeEZWEaWCbbSjYakQpuYN5", // TestRED
  offerAmount: toNano("1"),
  minAskAmount: "1",
  proxyTon,
  // Optional referral parameters:
  referralAddress: "", // your referral TON address
  referralValue: 50,   // e.g. 50 => 0.5% fee
  queryId: 12345,
});

To execute the transaction, you need to send a transaction with these parameters to the blockchain. This code will be different based on the wallet you are using to send the tx from, so please check our doc section about transaction sending guide with examples for different libraries.

Referral fees

This section contains information about referral fees in the Omniston protocol, covering DEX v1, DEX v2, and DeDust flow.

This guide explains how referral fees are applied in the Omniston liquidity‑aggregation protocol when trades route through DEX v1, DEX v2, or DeDust. For general Ston.fi protocol fees, see .


Specifying Referral Parameters

When you request a quote you can attach referral data so that a share of the swap fee is redirected to your address.

Parameter
Description

Example JSON in a quote request

Implementation‑specific details are in:


Referral Fees with DEX v1

Characteristic
Details

Nothing else is required—fees arrive automatically.


Referral Fees with DEX v2

1 · How fees are accumulated

  • Fee rate is configurable from 0.01 % to 1 % per swap.

  • Instead of being paid instantly, fees are deposited into a Vault (one vault per token × referral‑address pair).

2 · Withdrawing your fees

You have two options:

Method
When to use

For a fully-working reference open the Vault withdrawal demo inside our SDK mono-repo:

How it works in practice

  • Suppose a trader swaps A → B and a referral fee is specified.

  • During that swap the Router deploys (or re-uses) a Vault whose address is derived deterministically from the pool address and the router address.

  • To locate the vault later you therefore need two pieces of data:

    1. the pool address used for the swap, and

    2. the router address (each pool stores a pointer to the router that served it).

  • With those addresses in hand, instantiate a Router from the SDK and call the helper (e.g. withdrawFees()) to collect the accumulated tokens via TonConnect or any preferred wallet integration.

The linked example shows this discover → instantiate → withdraw flow end-to-end, including robust TypeScript patterns and TonConnect wiring.

Note For TON-denominated fees in DEX v2, the tokenMinter is the pTON master address (ptonMasterAddress).


Referral Fees with DeDust

For DeDust swaps, we implement our own referral fee mechanism through a stand-alone Fee-Vault Minter contract. This flow bypasses the Router used in Ston.fi DEX v2, so the existing SDK helpers cannot be used—you must call the contract methods directly on-chain.

1 · Find your personal vault

Each jetton has its own vault contract. To find your vault address for a specific jetton:

  1. Create the minter's jetton-wallet address Every Fee-Vault Minter owns a regular jetton wallet. Retrieve it once via jettonMinter.getWalletAddress(minter.address).

  2. Query your personal vault by calling minter.getVaultAddress({ owner: <your_wallet>, jettonWallet: <minter_jetton_wallet> }).

The getter returns:

  • 0:0 – no fees accrued yet for this jetton

  • vault address – your dedicated Fee-Vault contract for this jetton

Vault addresses are derived deterministically based on both the referrer address and jetton address.

2 · Collect the fees

  1. Send an internal message to your vault calling collectFees() (no payload) with about 0.3 TON attached for gas.

  2. The vault immediately transfers the accumulated fee-tokens to your wallet and resets its balance.

  3. For TON fees, they are credited instantly to the referrer address during the swap transaction.

The complete flow consists of two or three on-chain calls:

Step
Contract
Method
Purpose

Summary

Protocol
Fee mechanism
Range
How to receive funds

Stats & Vaults API Endpoints

In addition to on-chain reading or direct contract calls, you can track and manage referral fees using our REST API. These endpoints are available for both v1 and v2. (They are not related to DeDust—only Ston.fi DEX v1 and v2.) In v1, there are no fee vaults, but all referral fee accruals will still appear in the listed endpoints:

  • GET /v1/wallets/{addr_str}/fee_vaults Lists all known vaults per referrer (not supported in v1).

  • GET /v1/stats/fee_accruals Shows all operations leading to fee accrual for the referrer, filterable by time period.

  • GET /v1/stats/fee_withdrawals Shows all withdrawals from the referrer's vaults, filterable by time period.

  • GET /v1/stats/fees Returns aggregated referral fee metrics (like total accrued USD value) by time period.

  • Swagger UI Access the interactive API documentation at .


Additional Resources

referrer_address

TON address that receives the referral fee

referrer_fee_bps

Fee rate in basis‑points (1 bps = 0.01 %)

{
  "referrer_address": {
    "blockchain": 607,
    "address": "EQCXSs2xZ2dhk9TAxzGzXra2EbG_S2SqyN8Tfi6fJ82EYiVj"
  },
  "referrer_fee_bps": 10
}

Fee rate

Fixed 0.1 % (10 bps) enforced by the pool contract

Settlement

Paid on‑chain in the same swap tx to referrer_address

UI

Quick manual withdraws – open the Vault UI and paste the pool address into the Pool address: field

SDK / code

Automated dashboards, integrations, or batch claiming

// --- TypeScript example ------------------------------------------------------
const minterAddress = "EQBCAUfvMMSn-WHP0bKDs4wUVOK1r55OuHJIq25_QnVFCFye";
const minter = provider.open(
  Minter.createFromAddress(minterAddress),
); 

const jetton = "USDT_jetton_master_address";
const senderAddress = "your_wallet_address";

const jettonMinter = provider.open(
  JettonMinterContract.createFromAddress(jetton),
);
const minterWalletAddress = await jettonMinter.getWalletAddress(minter.address);

const vaultAddress = await minter.getVaultAddress({
  owner: senderAddress,
  jettonWallet: minterWalletAddress,
});

// Need more than just the address? Query on-chain data with getVaultData():
const vault = provider.open(VaultContract.createFromAddress(vaultAddress));
const vaultData = await vault.getVaultData();
// vaultData.balance, vaultData.lastCollectTime, …

Read

Fee-Vault Minter

getVaultAddress(user, jetton)

Get your vault address

Read (optional)

Your Fee-Vault

getVaultData()

Check balance before collecting

Write

Your Fee-Vault

collectFees()

Transfer accumulated fees to your wallet

DEX v1

Direct payment inside the swap tx

0.1 %

Automatic

DEX v2

Fees accrue in a vault (token × referrer)

0.01 – 1 %

UI or code withdrawal

DeDust

Token-aware vaults (one per jetton)

0.01 – 1 %

TON: instant, Jettons: getVaultAddress → collectFees()

Protocol Fees Introduction
Node.js SDK
React SDK
gRPC
WebSocket
https://github.com/ston-fi/sdk/tree/main/examples/next-js-app/app/vault
https://api.ston.fi/swagger-ui/#/
Omniston Quick‑start
Glossary
Contact Support

LpAccount

This section contains api reference of the lp account contract

Off-chain get methods

get_lp_account_data

Returns current state of the lp account: owner address, pool address and amount of both Jetton tokens.

(slice, slice, int, int) get_lp_account_data() method_id;

Arguments

None

Result

Returns the current state of the lp account.

Return structure

Key
Type
Index
Description

user_address

address

0

Owner's address

pool_address

address

1

Pool's address

amount0

coins

2

Balance of th e first Jetton token (in basic token units)

amount1

coins

3

Balance of the second Jetton token (in basic token units)

On-chain queries

On-chain counterparts of getter methods.

Operations table

Name
Value
Description

getter_lp_account_data

0x1d439ae0

Sends a message with the current state of the pool

getter_lp_account_data (0x1d439ae0)

Sends a message with current state of the lp account. On-chain equivalent of get_lp_account_data.

TL-B

getter_lp_account_data#1d439ae0 query_id:uint64 = InternalMsgBody;

Message body

None

Outgoing messages

Sends a message with current state of the lp account to sender_address

Response message body

Name
Type
Description

op

uint32

Operation code equal to getter_lp_account_data

query_id

uint64

Query id

user_address

address

Owner's address

pool_address

address

Pool's address

amount0

coins

Balance of the first Jetton token (in basic token units)

amount1

coins

Balance of the second Jetton token (in basic token units)

Pool message handlers

Handles incoming messages from a pool

Operations table

Name
Value
Description

add_liquidity

0x3ebe5431

Add liquidity

add_liquidity (0x3ebe5431)

Stores the sent amount of tokens by the user to be added as new liquidity. Upon receiving an appropriate amount of both tokens sends a message with those amounts to a pool to be added as new liquidity. The automatic liquidity addition happens only if the amount of both tokens if greater than 1000 and non-zero min_lp_out was specified, otherwise the user can keep increasing the amount of stored tokens.

TL-B

add_liquidity#3ebe5431 query_id:uint64 new_amount0:Grams new_amount1:Grams min_lp_out:Grams = InternalMsgBody;

Message body

Name
Type
Description

new_amount0

coins

Amount of the first Jetton tokens added (in basic token units)

new_amount1

coins

Amount of the second Jetton tokens added (in basic token units)

min_lp_out

coins

Minimum required amount of received new liquidity tokens (in basic token units)

Notes:

  • addition of liquidity will fail if a user should receive less than min_lp_out of lp tokens as a result

Outgoing messages

Sends a message to the pool with cb_add_liquidity op

Response message body

Name
Type
Description

op

uint32

Operation code, equal to cb_add_liquidity

query_id

uint64

Query id

amount0

coins

Amount of the first Jetton tokens added (in basic token units)

amount1

coins

Amount of the second Jetton tokens added (in basic token units)

user_address

address

Owner's address

min_lp_out

coins

Minimum amount of received liquidity tokens (in basic token units)

Notes:

  • addition of liquidity will fail if a user should receive less than min_lp_out of lp tokens as a result

User message handlers

Handles incoming messages from a user

Operations table

Name
Value
Description

refund_me

0xbf3f447

Return previously sent tokens back to the user

direct_add_liquidity

0x4cf82803

Directly add liquidity with specified amount of tokens and a minimum amount of received liquidity tokens

reset_gas

0x42a0fb43

Reset gas

refund_me (0xbf3f447)

Initiates a refund of stored tokens by add_liquidity if the user decides to cancel the addition of new liquidity. The amount of stored tokens will be sent to the pool which will initiate a transfer back to the user.

TL-B

refund_me#bf3f447 query_id:uint64 = InternalMsgBody;

Message body

None

Outgoing messages

Sends a message with cb_refund_me op code and amount of stored tokens to a pool. The pool will send a message to refund those tokens back to the user.

Response message body

Name
Type
Description

op

uint32

Operation code

query_id

uint64

Query id

amount0

coins

Amount of the first Jetton tokens (in basic token units)

amount1

coins

Amount of the second Jetton tokens (in basic token units)

user_address

address

Owner's address

direct_add_liquidity (0x4cf82803)

Initiates an addition of new liquidity to a pool with a specified amount of both tokens and a minimum amount of received liquidity. The operation is successful only if there's an equal or greater amount of both tokens stored in the account (the amount of both tokens must be greater than 1000). This method is useful if an automatic liquidity addition has not been triggered upon deposition of tokens.

TL-B

direct_add_liquidity#4cf82803 query_id:uint64 amount0:Grams amount1:Grams min_lp_out:Grams = InternalMsgBody;

Message body

Name
Type
Description

amount0

coins

Amount of the first Jetton tokens (in basic token units)

amount1

coins

Amount of the second Jetton tokens (in basic token units)

min_lp_out

coins

Minimum amount of received liquidity tokens (in basic token units)

Notes:

  • addition of liquidity will fail if a user should receive less than min_lp_out of lp tokens as a result

  • min_lp_out value must be greater than zero for this operation to proceed

Outgoing messages

Sends a message with cb_add_liquidity op code and amount of both tokens to be added as new liquidity.

Response message body

Name
Type
Description

op

uint32

Operation code

query_id

uint64

Query id

am0

coins

Amount of the first Jetton tokens (in basic token units)

am1

coins

Amount of the second Jetton tokens (in basic token units)

user_address

address

Owner's address

min_lp_out

coins

Minimum amount of received liquidity tokens (in basic token units)

Notes:

  • addition of liquidity will fail if a user should receive less than min_lp_out of lp tokens as a result

reset_gas (0x42a0fb43)

Updates the amount of $TON (in nanoTons) on the lp account to REQUIRED_TON_RESERVE (10000000) of the account. The remaining $TON will be sent back to the user_address

TL-B

reset_gas#42a0fb43 query_id:uint64 = InternalMsgBody;

Message body

None

Outgoing messages

Sends an empty message back to the user with the remaining $TON

Constants

Name
Value
Description

WORKCHAIN

0

Workchain id

REQUIRED_TON_RESERVE

10000000

Amount of $TON (in nanoTons) to be left on the lp account contract as gas

Nodejs

Getting started

This package acts as a typescript wrapper on top of the Ston.fi and provides RxJS styled observables on top of the WebSocket API connection

Installation

via NPM

via YARN

via PNPM

Create an instance

Create an Omniston instance, specifying the API URL.

The constructor takes the following parameters:

Name
Type
Description

Send a quote request

Send a request for quote to swap an asset to another asset.

A QuoteRequest has the following properties:

Name
Type
Description

RequestSettlementParams

Name
Type
Description

Note: Some parameters in RequestSettlementParams may only be relevant for certain blockchains or certain settlement methods. If your settlement method or wallet does not require them, you can omit them or set them to default values.

Gasless Settlement Options:

  • GASLESS_SETTLEMENT_PROHIBITED: Disables gasless settlement completely

  • GASLESS_SETTLEMENT_POSSIBLE: Allows gasless settlement if available (recommended)

  • GASLESS_SETTLEMENT_REQUIRED: Forces gasless settlement (will fail if not available)

The server returns Observable<QuoteResponseEvent>, which is a stream of quote updates.

A QuoteResponseEvent might be one of the following:

  • { type: "quoteUpdated"; quote: Quote; }

  • { type: "noQuote"; }

  • { type: "unsubscribed"; }

A Quote has the following properties:

Name
Type
Description

Build a transaction

Now that we have a quote, we should request a server to build a transaction to initiate the trade that the user can verify and sign.

The buildTransfer method takes a TransactionRequest object as a parameter, having the following properties:

Name
Type
Description

Sign the transaction

You can send messages to any library of your choice. Take a look at our with examples of different popular packages

Listen for trade status updates

After the user has signed and initiated the transaction, we can track the trade status.

The trackTrade method has the following parameters:

Name
Type
Description

It returns Observable<TradeStatus>. For the different trade status values, see the source code. We are interested in the trade result enum which can be read from status.tradeSettled?.result? field. The enum has the following values:

Name
Description
npm install @ston-fi/omniston-sdk
yarn add @ston-fi/omniston-sdk
pnpm install @ston-fi/omniston-sdk
import { Omniston } from "@ston-fi/omniston-sdk";

const omniston = new Omniston({
  apiUrl: "wss://omni-ws.ston.fi",
});

client

ApiClient | undefined

Optional. Provide this if you want to override the default API client. By default, this will be an ApiClient using ReconnectingTransport

apiUrl

URL | string

Omniston WebSocket API URL.

import { GaslessSettlement } from "@ston-fi/omniston-sdk";

omniston
  .requestForQuote({
    settlementMethods: [SettlementMethod.SETTLEMENT_METHOD_SWAP],
    askAssetAddress: {
      blockchain: Blockchain.TON,
      address: "EQA2kCVNwVsil2EM2mB0SkXytxCqQjS4mttjDpnXmwG9T6bO", // STON
    },
    bidAssetAddress: {
      blockchain: Blockchain.TON,
      address: "EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs", // USDT
    },
    amount: {
      bidUnits: "1000000", // 1 USDT
    },
    settlementParams: {
      maxPriceSlippageBps: 0,
      gaslessSettlement: GaslessSettlement.GASLESS_SETTLEMENT_POSSIBLE,
      maxOutgoingMessages: 4, // default for TON
    },
  })
  .subscribe((quoteResponseEvent) => {
    // process the quote event
  });

settlementMethods

SettlementMethod[]

Supported methods of swap settlement

askAssetAddress

Address

Blockchain-specific address of ask asset

bidAssetAddress

Address

Blockchain-specific address of bid asset

amount

{ bidUnits: string } | { askUnits: string }

Either the amount of bid asset or ask asset

amount.bidUnits

string

The amount of bid asset the trader wants to pay, including all fees, in base asset units

amount.askUnits

string

The amount of ask asset the trader wants to get after all fees, in base asset units

referrerAddress

Address | undefined

The address of referrer that will receive the fees

referrerFeeBps

number | undefined

The amount of fees required by the referrer in basis points (1/10000 or 0.01%)

settlementParams

RequestSettlementParams | undefined

Additional parameters of RFQ related to settlement. See the table below.

max_price_slippage_bps

number | undefined

Maximum price slippage, in basis points (0.01%). For example, 100 = 1% slippage. Set to 0 to use recommended slippage.

gasless_settlement

GaslessSettlement | undefined

Gasless settlement preference. Options: GASLESS_SETTLEMENT_PROHIBITED (no gasless), GASLESS_SETTLEMENT_POSSIBLE (allow gasless), GASLESS_SETTLEMENT_REQUIRED (require gasless).

max_outgoing_messages

number | undefined

Maximum number of outgoing messages supported by the wallet. For TON blockchain, this defaults to 4 if omitted.

quoteId

string

ID of the quote generated by the platform (32 characters)

resolverId

string

ID of the resolver

resolverName

string

Name of the resolver

bidAssetAddress

Address

Blockchain-specific address of bid asset

askAssetAddress

Address

Blockchain-specific address of ask asset

bidUnits

string

The amount of bid asset the trader must pay, including all fees

askUnits

string

The amount of ask asset the trader will get after all fees

referrerAddress

Address | undefined

The address of referrer that will receive the fees

referrerFeeAsset

Address

The asset of the fees that the referrer will get

referrerFeeUnits

string

The amount of fees that the referrer will get (in units of referrerFeeAsset)

protocolFeeAsset

Address

The asset of the fees charged by the protocol

protocolFeeUnits

string

The amount of fees charged by the protocol (in units of protocolFeeAsset).

quoteTimestamp

number

The timestamp (UTC seconds) of Quote sent by resolver

tradeStartDeadline

number

Max timestamp (UTC seconds) of start of the trade

gasBudget

string

The amount of gas budget for the trade

estimatedGasConsumption

string

Estimated amount of gas units that will be spent to perform the trade

params

object | undefined

Various parameters specific to the settlement method. See the source code for more details.

const tx = await omniston.buildTransfer({
  quote,
  sourceAddress: {
    blockchain: Blockchain.TON,
    address: "", // sender wallet address on `offerBlockchain`
  },
  destinationAddress: {
    blockchain: Blockchain.TON,
    address: "", // receiver wallet address on `askBlockchain`
  },
  gasExcessAddress: {
    blockchain: Blockchain.TON,
    address: "", // the address that will receive the gas not spent by the trade
  },
  useRecommendedSlippage: true, // Use recommended slippage from the quote
});

const messages = tx.ton?.messages ?? [];

quote

Quote

The valid quote received from Omniston.requestForQuote

sourceAddress

Address

The address on offerBlockchain that will send initial transaction to start the trade

destinationAddress

Address

The address on askBlockchain that will receive result of the trade

gasExcessAddress

Address

The address that will receive the gas not spent by the trade

useRecommendedSlippage

boolean | undefined

Whether to use the recommended slippage from the quote. Defaults to false.

const tradeStatus = omniston.trackTrade({
  quoteId: quote.quoteId,
  traderWalletAddress: {
    blockchain: Blockchain.TON,
    address: "", // sender wallet address on `offerBlockchain`
  },
  outgoingTxHash: "", // Replace with the actual outgoingTxHash
});

tradeStatus.subscribe((status) => {
  console.log(JSON.stringify(status));
});

quoteId

string

ID of the quote received from Omniston.requestFromQuote

traderWalletAddress

Address

The address of trader's wallet that initiated transaction

outgoingTxHash

string

Hash of the transaction that initiated the trade

TRADE_RESULT_FULLY_FILLED

The trade has been completed and fully filled.

TRADE_RESULT_PARTIALLY_FILLED

The trade has been partially filled. Something went wrong.

TRADE_RESULT_ABORTED

The trade has been aborted.

TRADE_RESULT_UNKNOWN

UNRECOGNIZED

Omniston protocol API
transaction sending guide

LpAccount

This section contains api reference of the lp account contract

Off-chain get methods

get_lp_account_data

Returns current state of the LpAccount.

(slice, slice, int, int) get_lp_account_data() method_id;

Arguments

None

Result

Returns LpAccountData structure containing current state of the lp account.

LpAccountData structure

Key
Type
Index
Description

user_address

address

0

Owner's address

pool_address

address

1

Pool's address

amount0

coins

2

Balance of the first Jetton token (in basic token units)

amount1

coins

3

Balance of the second Jetton token (in basic token units)

On-chain queries

On-chain counterparts of getter methods.

Operations table

Name
Value
Description

getter_lp_account_data

0x24cfc100

Sends a message with the current state of the pool

getter_lp_account_data (0x24cfc100)

Sends a message with current state of the lp account. On-chain equivalent of get_lp_account_data.

TL-B

getter_lp_account_data#24cfc100 query_id:uint64 = InternalMsgBody;

Message body

None

Outgoing messages

Sends a message with current state of the lp account to sender_address

Response message body

Name
Type
Description

op

uint32

Operation code equal to getter_lp_account_data

query_id

uint64

Query id

user_address

address

Owner's address

pool_address

address

Pool's address

amount0

coins

Balance of the first Jetton token (in basic token units)

amount1

coins

Balance of the second Jetton token (in basic token units)

Pool message handlers

Handles incoming messages from a pool

Operations table

Name
Value
Description

add_liquidity

0x50c6a654

Add liquidity

add_liquidity (0x50c6a654)

Stores the sent amount of tokens by the user to be added as new liquidity. Upon receiving an appropriate amount of both tokens sends a message with those amounts to a pool to be added as new liquidity. The automatic liquidity addition happens only if the amount of both tokens if greater than 1000 and non-zero min_lp_out was specified, otherwise the user can keep increasing the amount of stored tokens.

TL-B

add_liquidity#50c6a654 query_id:uint64 new_amount0:Coins new_amount1:Coins min_lp_out:Coins fwd_amount:Coins both_positive:uint1 to_user:MsgAddress custom_payload_cs:(Maybe ^Cell) additional_fields:^[refund_address:MsgAddress excess_address:MsgAddress] = InternalMsgBody;

Message body

Name
Type
Description

new_amount0

coins

Amount of the first Jetton tokens added (in basic token units)

new_amount1

coins

Amount of the second Jetton tokens added (in basic token units)

min_lp_out

coins

Minimum required amount of received new liquidity tokens (in basic token units)

fwd_amount

coins

Forward amount used to send custom_payload (if present) in transfer_notification

both_positive

uint1

Trigger liquidity deposit only if both token amounts are non-zero

to_address

address

Owner of new liquidity tokens

custom_payload

maybe_ref

Payload sent in transfer_notification upon receiving tokens

additional_fields

ref

See table below

additional_fields

Name
Type
Description

refund_address

address

Address of the owner of LpAccount where tokens will be refunded if liquidity mint wasn't successful

excess_address

address

Address where all TON excesses will be sent

Notes:

  • addition of liquidity will fail if a user should receive less than min_lp_out of lp tokens as a result

Outgoing messages

Sends a message to the pool with cb_add_liquidity op

Response message body

Name
Type
Description

op

uint32

Operation code, equal to cb_add_liquidity

query_id

uint64

Query id

amount0

coins

Amount of the first Jetton tokens added (in basic token units)

amount1

coins

Amount of the second Jetton tokens added (in basic token units)

user_address

address

Owner's address

min_lp_out

coins

Minimum amount of received liquidity tokens (in basic token units)

fwd_amount

coins

Forward amount used to send custom_payload (if present) in transfer_notification

custom_payload

maybe_ref

Payload sent in transfer_notification upon receiving tokens

additional_data

ref

See table below

additional_data

Name
Type
Description

to_address

address

Owner of new liquidity tokens

refund_address

address

Address of the owner of LpAccount where tokens will be refunded if liquidity mint wasn't successful

excess_address

address

Address where all TON excesses will be sent

Notes:

  • addition of liquidity will fail if a user should receive less than min_lp_out of lp tokens as a result

  • LpAccount is deleted upon sending this message (Pool always sends msgs with state_init)

User message handlers

Handles incoming messages from a user

Operations table

Name
Value
Description

refund_me

0x132b9a2c

Return previously sent tokens back to the user

direct_add_liquidity

0x0ff8bfc6

Directly add liquidity with specified amount of tokens and a minimum amount of received liquidity tokens

reset_gas

0x29d22935

Reset gas

refund_me (0x132b9a2c)

Initiates a refund of stored tokens by add_liquidity if the user decides to cancel the addition of new liquidity. The amount of stored tokens will be sent to the pool which will initiate a transfer back to the user.

TL-B

refund_me#132b9a2c query_id:uint64 left_maybe_payload:(Maybe ^Cell) right_maybe_payload:(Maybe ^Cell) = InternalMsgBody;

Message body

None

Outgoing messages

Sends a message with cb_refund_me op code and amount of stored tokens to a pool. The pool will send a message to refund those tokens back to the user.

Response message body

Name
Type
Description

op

uint32

Operation code

query_id

uint64

Query id

amount0

coins

Amount of the first Jetton tokens (in basic token units)

amount1

coins

Amount of the second Jetton tokens (in basic token units)

user_address

address

Owner's address

payload_0

maybe_ref

Payload used for amount0; can be cross_swap

payload_1

maybe_ref

Payload used for amount1; can be cross_swap

direct_add_liquidity (0x0ff8bfc6)

Initiates an addition of new liquidity to a pool with a specified amount of both tokens and a minimum amount of received liquidity. The operation is successful only if there's an equal or greater amount of both tokens stored in the account (the amount of both tokens must be greater than 1000). This method is useful if an automatic liquidity addition has not been triggered upon deposition of tokens.

TL-B

direct_add_liquidity#ff8bfc6 query_id:uint64 am0:Coins am1:Coins min_lp_out:Coins fwd_amount:Coins to_user:MsgAddress custom_payload_cs:(Maybe ^Cell) additional_fields:^[refund_address:MsgAddress excess_address:MsgAddress] = InternalMsgBody;

Message body

Name
Type
Description

amount0

coins

Amount of the first Jetton tokens (in basic token units)

amount1

coins

Amount of the second Jetton tokens (in basic token units)

min_lp_out

coins

Minimum amount of received liquidity tokens (in basic token units)

fwd_amount

coins

Forward amount used to send custom_payload (if present) in transfer_notification

to_address

address

Owner of new liquidity tokens

custom_payload

maybe_ref

Payload sent in transfer_notification upon receiving tokens

additional_fields

ref

See table below

additional_data

Name
Type
Description

refund_address

address

Address of the owner of LpAccount where tokens will be refunded if liquidity mint wasn't successful

excess_address

address

Address where all TON excesses will be sent

Notes:

  • addition of liquidity will fail if a user should receive less than min_lp_out of lp tokens as a result

  • min_lp_out value must be greater than zero for this operation to proceed

Outgoing messages

Sends a message with cb_add_liquidity op code and amount of both tokens to be added as new liquidity.

reset_gas (0x29d22935)

Updates the amount of TON (in nanoTons) on the lp account to storage_fee::lp_account (10000000) of the account. The remaining TON will be sent back to the user_address

TL-B

reset_gas#29d22935 query_id:uint64 = InternalMsgBody;

Message body

None

Outgoing messages

Sends an empty message back to the user with the remaining TON

Constants

Name
Value
Description

storage_fee::lp_account

1000000

Amount of TON (in nanoTons) to be left on the lp account contract as gas

React

Getting started

This package provides binding for to the React ecosystem. Using this package, you can access all Omniston methods as hooks powered by (out-of-box loading states, retries, error handling, and match more)

Take a look onto our that use NextJs and omniston-sdk-react package

Installation

via NPM

via YARN

via PNPM

Wrap you app in Omniston provider

The provider takes the following parameters:

Name
Type
Description

Send a quote request

Send a request for quote to swap an asset to another asset.

A QuoteRequest has the following properties:

Name
Type
Description

RequestSettlementParams

Name
Type
Description

Note: Some parameters in RequestSettlementParams may only be relevant for certain blockchains or certain settlement methods. If your settlement method or wallet does not require them, you can omit them or set them to default values.

Gasless Settlement Options:

  • GASLESS_SETTLEMENT_PROHIBITED: Disables gasless settlement completely

  • GASLESS_SETTLEMENT_POSSIBLE: Allows gasless settlement if available (recommended)

  • GASLESS_SETTLEMENT_REQUIRED: Forces gasless settlement (will fail if not available)

The server returns Observable<QuoteResponseEvent>, which is a stream of quote updates.

A QuoteResponseEvent might be one of the following:

  • { type: "quoteUpdated"; quote: Quote; }

  • { type: "noQuote"; }

  • { type: "unsubscribed"; }

A Quote has the following properties:

Name
Type
Description

Build a transaction

Now that we have a quote, we should request a server to build a transaction to initiate the trade that the user can verify and sign.

The buildTransfer method takes a TransactionRequest object as a parameter, having the following properties:

Name
Type
Description

Sign the transaction

You can found the instruction on how to send transaction using the @tonconnect/ui-react package .

Get the outgoing transaction hash

See for detailed instructions.

For tutorial purposes, we'll use the @ton/core package to get the external transaction hash and find the internal hash with TonAPI v2.

Listen for trade status updates

After the user has signed and initiated the transaction, we can track the trade status.

The trackTrade method has the following parameters:

Name
Type
Description

It returns Observable<TradeStatus>. For the different trade status values, see the source code. We are interested in the trade result enum which can be read from status.tradeSettled?.result? field. The enum has the following values:

Name
Description
npm install @ston-fi/omniston-sdk-react
yarn add @ston-fi/omniston-sdk-react
pnpm install @ston-fi/omniston-sdk-react
import { OmnistonProvider } from "@ston-fi/omniston-sdk-react";

const App = () => (
  <OmnistonProvider apiUrl="wss://omni-ws.ston.fi">{children}</OmnistonProvider>
);

client

ApiClient | undefined

Optional. Provide this if you want to override the default API client. By default, this will be an ApiClient using ReconnectingTransport

apiUrl

URL | string

Omniston WebSocket API URL.

import { useRfq, GaslessSettlement } from "@ston-fi/omniston-sdk-react";

const {
  isLoading,
  error,
  data: quote,
  ...restQuery
} = useRfq({
  settlementMethods: [SettlementMethod.SETTLEMENT_METHOD_SWAP],
  askAssetAddress: {
    blockchain: Blockchain.TON,
    address: "EQA2kCVNwVsil2EM2mB0SkXytxCqQjS4mttjDpnXmwG9T6bO", // STON
  },
  bidAssetAddress: {
    blockchain: Blockchain.TON,
    address: "EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs", // USDT
  },
  amount: {
    bidUnits: "1000000", // 1 USDT
  },
  settlementParams: {
    maxPriceSlippageBps: 0,
    gaslessSettlement: GaslessSettlement.GASLESS_SETTLEMENT_POSSIBLE,
    maxOutgoingMessages: 4, // default for TON
  },
});

settlementMethods

SettlementMethod[]

Supported methods of swap settlement

askAssetAddress

Address

Blockchain-specific address of ask asset

bidAssetAddress

Address

Blockchain-specific address of bid asset

amount

{ bidUnits: string } | { askUnits: string }

Either the amount of bid asset or ask asset

amount.bidUnits

string

The amount of bid asset the trader wants to pay, including all fees, in base asset units

amount.askUnits

string

The amount of ask asset the trader wants to get after all fees, in base asset units

referrerAddress

Address | undefined

The address of referrer that will receive the fees

referrerFeeBps

number | undefined

The amount of fees required by the referrer in basis points (1/10000 = 0.01%)

settlementParams

RequestSettlementParams | undefined

Additional parameters of RFQ related to settlement. See the table below.

max_price_slippage_bps

number | undefined

Maximum price slippage, in basis points (0.01%). For example, 100 = 1% slippage. Set to 0 to use recommended slippage.

gasless_settlement

GaslessSettlement | undefined

Gasless settlement preference. Options: GASLESS_SETTLEMENT_PROHIBITED (no gasless), GASLESS_SETTLEMENT_POSSIBLE (allow gasless), GASLESS_SETTLEMENT_REQUIRED (require gasless).

max_outgoing_messages

number | undefined

Maximum number of outgoing messages supported by the wallet. For TON blockchain, this defaults to 4 if omitted.

quoteId

string

ID of the quote generated by the platform (32 characters)

resolverId

string

ID of the resolver

resolverName

string

Name of the resolver

bidAssetAddress

Address

Blockchain-specific address of bid asset

askAssetAddress

Address

Blockchain-specific address of ask asset

bidUnits

string

The amount of bid asset the trader must pay, including all fees

askUnits

string

The amount of ask asset the trader will get after all fees

referrerAddress

Address | undefined

The address of referrer that will receive the fees

referrerFeeAsset

Address

The asset of the fees that the referrer will get

referrerFeeUnits

string

The amount of fees that the referrer will get (in units of referrerFeeAsset)

protocolFeeAsset

Address

The asset of the fees charged by the protocol

protocolFeeUnits

string

The amount of fees charged by the protocol (in units of protocolFeeAsset).

quoteTimestamp

number

The timestamp (UTC seconds) of Quote sent by resolver

tradeStartDeadline

number

Max timestamp (UTC seconds) of start of the trade

gasBudget

string

The amount of gas budget for the trade

estimatedGasConsumption

string

Estimated amount of gas units that will be spent to perform the trade

params

object | undefined

Various parameters specific to the settlement method. See the source code for more details.

import { useOmniston } from "@ston-fi/omniston-sdk-react";

const omniston = useOmniston();

const tx = await omniston.buildTransfer({
  quote,
  sourceAddress: {
    blockchain: Blockchain.TON,
    address: "", // sender wallet address on `offerBlockchain`
  },
  destinationAddress: {
    blockchain: Blockchain.TON,
    address: "", // receiver wallet address on `askBlockchain`
  },
  gasExcessAddress: {
    blockchain: Blockchain.TON,
    address: "", // the address that will receive the gas not spent by the trade
  },
  useRecommendedSlippage: true, // Use recommended slippage from the quote
});

const messages = tx.ton?.messages ?? [];

quote

Quote

The valid quote received from Omniston.requestForQuote

sourceAddress

Address

The address on offerBlockchain that will send initial transaction to start the trade

destinationAddress

Address

The address on askBlockchain that will receive result of the trade

gasExcessAddress

Address

The address that will receive the gas not spent by the trade

useRecommendedSlippage

boolean | undefined

Whether to use the recommended slippage from the quote. Defaults to false.

import { useTonConnectUI } from "@tonconnect/ui-react";

const [tonConnect] = useTonConnectUI();

await tonConnect.sendTransaction({
  validUntil: Date.now() + 1000000,
  messages: messages.map((message) => ({
    address: message.targetAddress,
    amount: message.sendAmount,
    payload: message.payload,
  })),
});
import { Cell } from "@ton/core";

const externalTxHash = Cell.fromBase64(boc).hash().toString("hex");
const response = await fetch(`https://tonapi.io/v2/traces/${externalTxHash}`);
const outgoingTxHash = (await response.json()).transaction.hash;
import { useTrackTrade } from "@ston-fi/omniston-sdk-react";
import { useTonAddress } from "@tonconnect/ui-react";

const walletAddress = useTonAddress();

const {
  isLoading,
  error,
  data: status,
  ...restQuery
} = useTrackTrade({
  quoteId: quote.quoteId,
  traderWalletAddress: {
    blockchain: Blockchain.TON,
    address: walletAddress,
  },
  outgoingTxHash: "", // Replace with the actual outgoingTxHash
});

quoteId

string

ID of the quote received from Omniston.requestFromQuote

traderWalletAddress

Address

The address of trader's wallet that initiated transaction

outgoingTxHash

string

Hash of the transaction that initiated the trade

TRADE_RESULT_FULLY_FILLED

The trade has been completed and fully filled.

TRADE_RESULT_PARTIALLY_FILLED

The trade has been partially filled. Something went wrong.

TRADE_RESULT_ABORTED

The trade has been aborted.

TRADE_RESULT_UNKNOWN

UNRECOGNIZED

omniston-sdk
TanStack react query
demo app
here
TON Cookbook

Op Codes

This section contains table of DEX v2 op codes

Token transfer payloads

Name
Value
Description

swap

0x6664de2a

Token transfer payload op for swap

provide_lp

0x37c096df

Token transfer payload op for liquidity addition

cross_swap

0x69cf1a5b

Custom payload op in swap payload to chain swaps on the same Router

Router admin

Name
Value
Description

set_fees

0x58274069

Sent by admin to Router to set new Pool fees

reset_gas

0x29d22935

Sent by admin to Router to reset gas

reset_pool_gas

0x66d0dff2

Sent by admin to Router to reset Pool gas

update_status

0x38a6022f

Sent by admin to Router to change Router lock status

init_code_upgrade

0x03601fc8

Sent by admin to Router to initiate code upgrade

init_admin_upgrade

0x0b02fd5b

Sent by admin to Router to initiate admin update

cancel_code_upgrade

0x1f72111a

Sent by admin to Router to cancel code upgrade

cancel_admin_upgrade

0x72d6b3b4

Sent by admin to Router to cancel admin update

finalize_upgrades

0x4e6707b7

Sent by admin to Router to apply all pending updates if cooldown passed

update_pool_status

0x2af4607c

Sent by admin to Router to change Pool lock status

set_params

0x2b8b3b62

Sent by admin to Router to update pool-specific params if present

Other calls

Name
Value
Description

direct_add_liquidity

0x0ff8bfc6

Sent by user to LpAccount to initiate liquidity addition

refund_me

0x132b9a2c

Sent by user to LpAccount to refund deposited tokens

withdraw_fee

0x354bcdf4

Sent by user to Vault to collect their tokens

set_rate

0x4a2bddb0

Sent by setter to Pool to change ratio variable (if present)

collect_fees

0x1ee4911e

Sent by protocol address to Pool to collect fees

Internal transactions

Name
Value
Description

internal_set_fees

0x58274069

Sent from Router to Pool to change fees

reset_gas

0x29d22935

Sent from Router to Pool to rest gas

internal_update_status

0x62752512

Sent from Router to Pool to change Pool lock status

internal_set_params

0x7163444a

Sent from Router to Pool to update pool-specific params if present

deposit_ref_fee

0x0490f09b

Sent from Router to Vault to deposit tokens in Vault

pay_to

0x657b54f5

Sent from Pool to Router to initiate token transfer

add_liquidity

0x50c6a654

Sent from Pool to LpAccount to add tokens

pay_vault

0x63381632

Sent from Pool to Router to deposit tokens in Vault

cb_add_liquidity

0x06ecd527

Sent from LpAccount to Pool to add liquidity

cb_refund_me

0x0f98e2b8

Sent from LpAccount to Pool to refund tokens

burn_notification_ext

0x297437cf

Sent from LpWallet to Pool after lp tokens burn

vault_pay_to

0x2100c922

Sent from Vault to Router to transfer tokens from Vault to user

Transfer exit codes

Name
Value
Description

swap_refund_no_liq

0x5ffe1295

No liquidity in Pool

swap_refund_tx_expired

0x1ec28412

Swap transaction expired on Pool

swap_refund_reserve_err

0x38976e9b

Not enough liquidity to perform a swap

swap_refund_0_out

0x5f954434

Swap out token amount is 0

swap_refund_slippage

0x39603190

Swap out token amount is less than provided minimum value

swap_pool_locked

0x365c484d

Pool is locked

swap_fee_out_of_bounds

0xa768c0d1

Ref fee (referrer fee) is too big

swap_ok

0xc64370e5

Transfer after swap

burn_ok

0xdda48b6a

Transfer after liquidity withdraw (lp token burn)

refund_ok

0xde7dbbc2

Transfer after LpAccount refund

transfer_bounce_locked

0x0a0dbdcb

Router is locked

transfer_bounce_invalid_pool

0x09a8afbf

Incorrect Pool (both token addresses are the same)

transfer_bounce_wrong_wc

0x720f5b17

Call was done from the wrong workchain

transfer_bounce_low_gas

0x8368a711

Not enough gas to preform operation

transfer_bounce_invalid_request

0x19727ea8

Incorrect token transfer payload op

transfer_bounce_tx_expired

0x0f5681d3

Transaction expired on Router

provide_refund_wrong_workchain

0x4e7405a8

Receiver of liquidity is in the wrong workchain

provide_refund_tx_expired

0xd6a53fd8

Provide transaction expired on Pool

Getters

Name
Value
Description

provide_wallet_address

0x2c76b973

Received by Pool to return lp wallet address for a specified user

take_wallet_address

0xd1735400

Send as a response to provide_wallet_address with lp wallet address

getter_lp_account_data

0x24cfc100

On-chain getter with LpAccount data

getter_pool_data

0x26df39fc

On-chain getter with Pool common data

getter_lp_account_address

0x15fbca95

On-chain getter with LpAccount address

getter_pool_address

0x2993ade0

On-chain getter with Pool address

Router

This section contains api reference of the router contract

Off-chain get methods

get_pool_address

Returns an address of a pool for a specified pair of assets.

It's necessary to specify addresses of Jetton wallets of the router as the arguments of this method. These addresses can be retrieved with get_wallet_address of the Jetton minter.

(slice) get_pool_address(slice token0, slice token1) method_id;

Arguments

Key
Type
Index
Description

token0

address

0

The address of the router's wallet of first Jetton

token1

address

1

The address of the router's wallet of second Jetton

Result

Returns single slice with the address of liquidity pool for specified pair of Jettons.

get_router_data

Returns the current state of the router: is the router locked, admin address, pending code or admin upgrades, pool contract code, lp wallet code, lp account code.

(int, slice, cell, cell, cell, cell) get_router_data() method_id;

Arguments

None

Result

Returns current state of the Router

Result structure

Key
Type
Index
Description

is_locked

bool

0

if operations are locked (swap, provide_lp)

admin_address

address

1

Address of contract's admin account

temp_upgrade

cell

2

A structure describing state of contract's code & admin upgrade; zero values indicate that no upgrade is pending

pool_code

cell

3

Code of the router's liquidity pool contract

jetton_lp_wallet_code

cell

4

Code of lp wallet contract

lp_account_code

cell

5

Code of lp account contract


transfer_notification operations

transfer_notification (0x7362d09c)

Messages received from Jetton wallets belonging to the router upon getting a token transfer from a user. The user must include a custom payload with additional op code (either swap or provide_lp) and appropriate data in order for those messages to be processed correctly.

TL-B

transfer_notification#7362d09c query_id:uint64 jetton_amount:Grams from_user:MsgAddress ref_msg_data:^DexPayload = InternalMsgBody;

Message body

Name
Type
Description

op

uint32

Operation code; equal to transfer_notification

query_id

uint64

Query id

jetton_amount

coins

Jetton amount (in basic token units)

from_user

address

User address

ref_msg_data

cell

Cell with data

ref_msg_data body

Name
Type
Description

transferred_op

uint32

Additional operation code

token_wallet1

address

The address of the router's wallet of second Jetton

Notes:

  • ref_msg_data contains other fields depending on which op code is called

Transfer operations table

Name
Value
Description

swap

0x25938561

Swap one type of Jetton tokens for another

provide_lp

0xfcf9e58f

Provide liquidity; route call to the correct pool

swap (0x25938561)

Swap tokens. This message is received when the Router's Jetton token wallet sends a message upon confirmation of token transfer from a user with a custom payload. The router then sends a swap message to the appropriate pool contract address.

TL-B

swap_op#25938561 = SwapOP;
swap$_ transferred_op:SwapOP token_wallet1:MsgAddress min_out:Grams to_address:MsgAddress ref_address:(Maybe MsgAddress) = DexPayload;

ref_msg_data body

Name
Type
Description

transferred_op

uint32

Additional operation code; equal to swap

token_wallet1

address

The address of the router's wallet of second Jetton

min_out

coins

Minimum amount out (in basic token units)

to_address

address

User address

has_ref

uint1

If referral is present

ref_address

address

Referrer address; present only if has_ref is 1

Outgoing messages

Sends a message with op swap to a liquidity pool

provide_lp (0xfcf9e58f)

Provide liquidity for a pool. This message is received when Router's token wallet sends a message upon confirmation of token transfer from a user with a custom payload. The router then sends a provide_lp message to the appropriate pool contract address.

TL-B

provide_lp_op#fcf9e58f = ProvideLpOP;
provide_lp$_ transferred_op:ProvideLpOP token_wallet1:MsgAddress min_lp_out:Grams = DexPayload;

ref_msg_data body

Name
Type
Description

transferred_op

uint32

Additional operation code; equal to provide_lp

token_wallet1

address

The address of the router's wallet of second Jetton

min_lp_out

coins

Minimum amount of created liquidity tokens (in basic token units)

Outgoing messages

Sends a message with op provide_lp to a liquidity pool


On-chain queries

On-chain counterparts of getter methods

Operations table

Name
Value
Description

getter_pool_address

0xd1db969b

Sends a message with a pool address for a requested token pair; counterpart to get_pool_address

getter_pool_address (0xd1db969b)

Sends a message with an address of a pool for a specified pair of assets; counterpart to get_pool_address

TL-B

getter_pool_address#d1db969b query_id:uint64 token0:MsgAddress token1:MsgAddress = InternalMsgBody;

Message Body

Name
Type
Description

token0

address

The address of the router's wallet of first Jetton

token1

address

The address of the router's wallet of second Jetton

Outgoing messages

Sends a message back to the sender with the pool address

Response message body

Name
Type
Description

op

uint32

Operation code equal to getter_pool_address

query_id

uint64

Query id

pool_address

address

Address of a pool


On-chain admin operations

Handles governance message from admin to change pool parameters.

The admin can lock/unlock trading on all pools, change fees on a certain pool, upgrade the router code, etc.

Operations table

Name
Value
Description

set_fees

0x355423e5

Change fee parameters of a pool

collect_fees

0x1fcb7d3d

Collect protocol fees from a pool

lock

0x878f9b0e

Lock the router (locks all swaps and liquidity providing)

unlock

0x6ae4b0ef

Unlock the router

init_code_upgrade

0xdf1e233d

Initiate code upgrade for the router

init_admin_upgrade

0x2fb94384

Initiate admin change for the router

cancel_admin_upgrade

0xa4ed9981

Cancel an admin change

cancel_code_upgrade

0x357ccc67

Cancel a code upgrade

finalize_upgrades

0x6378509f

Finalize upgrades of the router code and admin change

reset_gas

0x42a0fb43

Update the amount of $TON (in nanoTons) on the router to REQUIRED_TON_RESERVE of the router

reset_pool_gas

0xf6aa9737

Update the amount of $TON (in nanoTons) on a pool to REQUIRED_TON_RESERVE of a pool

Notes:

  • REQUIRED_TON_RESERVE for the router is 100000

set_fees (0x355423e5)

Change fees of a pool including liquidity pool fees, protocol fees and referral fees. It's necessary to provide correct Jetton token addresses for a target liquidity pool.

TL-B

ref_wallets$_ jetton_wallet0:MsgAddress jetton_wallet1:MsgAddress = RefWallets;
set_fees#355423e5 query_id:uint64 new_lp_fee:uint8 new_protocol_fee:uint8 new_ref_fee:uint8 new_protocol_fee_address:MsgAddress ref_wallets:^RefWallets = InternalMsgBody;

Arguments

Name
Type
Description

new_lp_fee

uint8

New liquidity pool fee ratio (multiplied by FEE_DIVIDER)

new_protocol_fee

uint8

New protocol fee ratio (multiplied by FEE_DIVIDER)

new_ref_fee

uint8

New referral fee ratio (multiplied by FEE_DIVIDER)

new_protocol_fee_address

address

Address for receiving protocol fees

ref_wallets

cell

Cell with wallet addresses

ref_wallets body

Name
Type
Description

jetton_wallet0

address

The address of the router's wallet of first Jetton

jetton_wallet1

address

The address of the router's wallet of second Jetton

Notes:

  • fee ratio is the value of fee divided by FEE_DIVIDER (10000); so to set a fee to 1% the value must be 100

  • fees must be between MIN_FEE (0) and MAX_FEE (100)

Outgoing messages

Sends a message with op set_fees to a liquidity pool.

collect_fees (0x1fcb7d3d)

Collect protocol fees from a pool. The appropriate Jetton wallet addresses must be provided.

TL-B

collect_fees#1fcb7d3d query_id:uint64 jetton_wallet0:MsgAddress jetton_wallet1:MsgAddress = InternalMsgBody;

Arguments

Name
Type
Description

jetton_wallet0

address

The address of the router's wallet of first Jetton

jetton_wallet1

address

The address of the router's wallet of second Jetton

Outgoing messages

Sends a message to a liquidity pool with collect_fees op code.

lock (0x878f9b0e)

Stops all transfer_notification messages from going through. Effectively blocks swap and provide_lp transactions through this router.

TL-B

lock#878f9b0e query_id:uint64 = InternalMsgBody;

Arguments

None.

Outgoing messages

None.

unlock (0x6ae4b0ef)

Allows all transfer_notification messages to go through. Enables swap and provide_lp transactions through this router.

TL-B

unlock#6ae4b0ef query_id:uint64 = InternalMsgBody;

Arguments

None

Outgoing messages

None.

init_code_upgrade (0xdf1e233d)

Initiates code upgrade for the router. An appropriate data with the new router code must be provided. The changes won't take effect until finalize_upgrades is received by the router. The minimum delay between initiating code upgrade and finalizing the changes in seven days.

TL-B

init_code_upgrade#df1e233d query_id:uint64 code:^Cell = InternalMsgBody;

Arguments

Name
Type
Description

code

cell

Code of the new router contract

Outgoing messages

None

init_admin_upgrade (0x2fb94384)

Initiates admin change for the router. An appropriate address for a new router admin must be provided. The changes won't take effect until finalize_upgrades is received by the router. The minimum delay between initiating admin change and finalizing the changes in two days.

TL-B

init_admin_upgrade#2fb94384 query_id:uint64 admin:MsgAddress = InternalMsgBody;

Arguments

Name
Type
Description

admin

address

New admin address

Outgoing messages

None

cancel_admin_upgrade (0xa4ed9981)

Cancels an admin change if there's a pending admin change.

TL-B

cancel_admin_upgrade#a4ed9981 query_id:uint64 = InternalMsgBody;

Arguments

None

Outgoing messages

None

cancel_code_upgrade (0x357ccc67)

Cancels a code upgrade if there's a pending code change.

TL-B

cancel_code_upgrade#357ccc67 query_id:uint64 = InternalMsgBody;

Arguments

None

Outgoing messages

None

finalize_upgrades (0x6378509f)

Finalizes upgrades of the router code and admin change. In order for the changes to take effect an appropriate amount of time must pass since initializing and upgrade. For code upgrade the delay is seven days, for an admin change the delay is two days.

TL-B

finalize_upgrades#6378509f query_id:uint64 = InternalMsgBody;

Arguments

None

Outgoing messages

None

reset_gas (0x42a0fb43)

Updates the amount of $TON (in nanoTons) on the router to REQUIRED_TON_RESERVE (100000) of the router. The remaining $TON will be sent to the router admin.

TL-B

reset_gas#42a0fb43 query_id:uint64 = InternalMsgBody;

Arguments

None

Outgoing messages

Sends an empty message back to admin with the remaining $TON

reset_pool_gas (0xf6aa9737)

Updates the amount of $TON (in nanoTons) on a pool to REQUIRED_TON_RESERVE of a pool. Forwards this message to a pool contract for provided pair of Jetton tokens.

TL-B

reset_pool_gas#2fb94384 query_id:uint64 jetton_wallet0:MsgAddress jetton_wallet1:MsgAddress = InternalMsgBody;

Arguments

Name
Type
Description

jetton_wallet0

address

The address of the router's wallet of first Jetton

jetton_wallet1

address

The address of the router's wallet of second Jetton

Outgoing messages

Sends a message to a liquidity pool with reset_gas op code carrying remaining gas


Internal message handlers

pay_to (0xf93bb43f)

Initiates a Jetton token transfer from wallets belonging to this router, called from pools (on swap, liquidity providing, refund, etc).

TL-B

ref_coins_data$_ amount0_out:Grams token0_address:MsgAddress amount1_out:Grams token1_address:MsgAddress = RefCoinsData;
pay_to#f93bb43f query_id:uint64 owner:MsgAddress exit_code:uint32 ref_coins_data:^RefCoinsData = InternalMsgBody;

Message body

Name
Type
Description

op

uint32

Operation code; equal to pay_to

query_id

uint64

Query id

owner

address

Address of a receiver

exit_code

uint32

Exit code

ref_coins_data

cell

Cell with Jetton wallet addresses and token amounts

ref_coins_data body

Name
Type
Description

amount0_out

coins

Amount of the first Jetton token (in basic token units)

token0_address

address

The address of the router's wallet of the first Jetton

amount1_out

coins

Amount of the second Jetton token (in basic token units)

token1_address

address

The address of the router's wallet of the second Jetton

Outgoing messages

Sends transfer of amount0_out tokens to owner message to token0_address wallet if amount0_out > 0

Sends transfer of amount1_out tokens to owner message to token1_address wallet if amount1_out > 0


Constants

Name
Value
Description

WORKCHAIN

0

Workchain id

REQUIRED_TON_RESERVE

100000

Amount of $TON (in nanoTons) to be left on the router contract as gas

TWODAYS

172800

Two days is seconds

SEVENDAYS

604800

Seven days in seconds

Swap Guide

This guide will walk you through creating a basic token swap app using the STON.fi SDK and API in a React project. We'll integrate wallet connectivity with TonConnect (via @tonconnect/ui-react) to allow users to connect their TON wallet and perform a swap. The guide is beginner-friendly and assumes minimal React experience.

Note: In this demo, we will leverage Tailwind CSS for styling instead of using custom CSS. The setup for Tailwind CSS is already included in the instructions below, so you don't need to set it up separately.

Note: You can use any package manager (npm, yarn, pnpm, or bun) to set up your React project. In this tutorial, we'll demonstrate with pnpm.


Table of Contents

  1. Introduction

  2. Setting Up the Project

    1. Create a React App

    2. Installing the Required Packages

  3. Connecting the Wallet

    1. Add the TonConnect Provider

    2. Create the TonConnect Manifest

    3. Add the Connect Wallet Button

  4. Fetching Available Assets

  5. Simulating a Swap

  6. Executing a Swap Transaction

  7. Testing Your Swap

  8. Conclusion

  9. Live Demo

  10. Advanced Example App


1. Introduction

In this quickstart, we will build a minimal React app to:

  • Connect to a TON wallet (via TonConnect UI).

  • Fetch available tokens from STON.fi (via @ston-fi/api).

  • Simulate a swap (to see expected output).

  • Execute a swap transaction on-chain (via @ston-fi/sdk).

We will use:

  • @ston-fi/sdk – Helps build the payload for the actual swap transaction.

  • @ston-fi/api – Lets us fetch asset lists, pool data, and run swap simulations.

  • @tonconnect/ui-react – Provides a React-based TON wallet connect button and utilities.


2. Setting Up the Project

2.1 Create a React App

First, let's check if pnpm is installed on your system:

pnpm --version

If you see a version number (like 10.4.0), pnpm is installed. If you get an error, you'll need to install pnpm first:

npm install -g pnpm

Now we'll create a new React project using Vite. However, you can use any React setup you prefer (Next.js, CRA, etc.).

Run the following command to create a new Vite-based React project:

pnpm create vite --template react

When prompted, type your desired project name (e.g., stonfi-swap-app):

Project name: » stonfi-swap-app

Then enter the folder:

cd stonfi-swap-app

2.2 Installing the Required Packages

Within your new React project directory, install the STON.fi packages, TonConnect UI, and the TON SDK:

pnpm add @ston-fi/sdk @ston-fi/api @tonconnect/ui-react @ton/ton

Next, install Tailwind CSS and its Vite plugin:

pnpm add tailwindcss @tailwindcss/vite

Additionally, install the Node.js polyfills plugin for Vite, which is necessary to provide Buffer and other Node.js APIs in the browser environment (required by TON libraries):

pnpm add vite-plugin-node-polyfills

Configure the Vite plugin by updating vite.config.js file:

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import tailwindcss from '@tailwindcss/vite'
import { nodePolyfills } from 'vite-plugin-node-polyfills'

// https://vite.dev/config/
export default defineConfig({
  plugins: [react(), tailwindcss(), nodePolyfills()],
})

Then, import Tailwind CSS in your main CSS file. Open src/index.css and replace all code with:

@import "tailwindcss";

You can also remove src/App.css we don't need it anymore, and remove the import statement import './App.css' from src/App.jsx.

After making these changes, you can verify that your app still runs correctly by starting the development server:

pnpm install
pnpm dev

This should launch your app in development mode, typically at http://localhost:5173. You should see the Vite + React logo and text on a plain white background. Since we've removed the default styling (App.css), the page will look simpler than the default template.

If you see the logo and text, it means your Vite + React setup is working correctly. Make sure everything loads without errors before proceeding to the next step.


3. Connecting the Wallet

3.1 Add the TonConnect Provider

Open src/main.jsx (Vite's default entry point) and wrap your application with the TonConnectUIProvider. This provider makes the TonConnect context available to your app for wallet connectivity. Also, point it to a manifest file (which we will create next) that describes your app to wallets.

// src/main.jsx
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import { TonConnectUIProvider } from '@tonconnect/ui-react';
import './index.css'
import App from './App.jsx'

createRoot(document.getElementById('root')).render(
  <StrictMode>
    <TonConnectUIProvider manifestUrl={`${window.location.origin}/tonconnect-manifest.json`}>
      <App />
    </TonConnectUIProvider>
  </StrictMode>,
)

This wraps the <App /> component with TonConnectUIProvider. The manifestUrl points to a tonconnect-manifest.json file that should be served at the root of your app (we'll create this below). Using window.location.origin ensures it picks the correct host (e.g., http://localhost:5173/tonconnect-manifest.json in development).


3.2 Create the TonConnect Manifest

In the public folder of your project, create a file named tonconnect-manifest.json. This manifest provides wallet apps with information about your application (like name and icon). You should customize this manifest for your own application. Here's an example:

{
    "url": "https://ton.vote",
    "name": "TON Vote",
    "iconUrl": "https://ton.vote/logo.png"
}

Make sure to update these fields for your application:

  • url: The base URL where your app is served

  • name: Your application's display name (this is what wallets will show to users)

  • iconUrl: A link to your app's icon (should be a 180×180 PNG image)

Make sure this file is accessible. When the dev server runs, you should be able to fetch it in your browser at http://localhost:5173/tonconnect-manifest.json.


3.3 Add the Connect Wallet Button

In your main App component (e.g., src/App.jsx), import and include the TonConnectButton. For example:

// src/App.jsx
import { TonConnectButton } from '@tonconnect/ui-react';

function App() {
  return (
    <div className="flex flex-col items-center justify-center min-h-screen p-4">
      <h1 className="text-2xl font-bold mb-4">STON.fi Swap Demo</h1>
      <TonConnectButton />
    </div>
  );
}

export default App;

This will render a "Connect Wallet" button. When clicked, it opens a modal with available TON wallets. After the user connects, the button will automatically display the wallet address or account info. TonConnect UI handles the connection state for you.

4. Fetching Available Assets

Next, let's retrieve the list of tokens (assets) that can be swapped on STON.fi. We use the STON.fi API client (@ston-fi/api) for this. It's important to note that while many tokens on the TON blockchain use a decimal precision of 9, some tokens, like jUSDT, have a decimal precision of 6. Therefore, we rely on the token's metadata to dynamically determine the decimal precision, ensuring compatibility across different tokens.

  1. Initialize the API client: In the component where you will handle swapping (we can continue working in App.jsx for simplicity), create a client instance from StonApiClient. This has methods to get assets, pools, simulate swaps, etc.

  2. Fetch assets on load: Using React's effect hook, fetch the asset list when the component mounts. Store the assets in state so you can display them.

// src/App.jsx
import { useEffect, useState } from 'react';
import { TonConnectButton } from '@tonconnect/ui-react';
import { StonApiClient, AssetTag } from '@ston-fi/api';

function App() {
  const [assets, setAssets] = useState([]);
  const [fromAsset, setFromAsset] = useState(null);
  const [toAsset, setToAsset] = useState(null);
  const [amount, setAmount] = useState(0);

  // Single function to handle changes in "From", "To", and "Amount"
  // Clears the simulation result each time any input changes
  const handleChange = (setter) => (e) => {
    const value = e.target.value;
    
    if (setter === setFromAsset || setter === setToAsset) {
      const selectedAsset = assets.find(asset => asset.contractAddress === value);
      setter(selectedAsset);
    } else {
      setter(value);
    }
  };

  // Helper to find an asset by address and return a consistent object
  const getAssetInfo = (asset) => {
    if (!asset) return { symbol: 'token', decimals: 10 ** 9 };

    // Determine display symbol
    const symbol = asset.meta?.symbol || asset.meta?.displayName || 'token';

    // Always take the decimal property from metadata, fallback to 9 if missing
    const decimals = 10 ** (asset.meta?.decimals ?? 9);

    return { symbol, decimals };
  };

  useEffect(() => {
    const fetchAssets = async () => {
      try {
        const client = new StonApiClient();
        const condition = [
          AssetTag.LiquidityVeryHigh,
          AssetTag.LiquidityHigh,
          AssetTag.LiquidityMedium,
        ].join(' | ');
        const assetList = await client.queryAssets({ condition });

        setAssets(assetList);
        if (assetList[0]) setFromAsset(assetList[0]);
        if (assetList[1]) setToAsset(assetList[1]);
      } catch (err) {
        console.error('Failed to fetch assets:', err);
      }
    };
    fetchAssets();
  }, []);

  // Shortcut to display either symbol or 'token'
  const displaySymbol = (asset) => getAssetInfo(asset).symbol;

  return (
    <div className="flex flex-col items-center justify-center min-h-screen bg-gradient-to-b from-blue-50 to-indigo-100 p-6">
      <div className="w-full max-w-md bg-white rounded-xl shadow-lg p-6 space-y-6">
        <div className="flex justify-between items-center">
          <h1 className="text-3xl font-bold text-indigo-700">STON.fi Swap</h1>
          <TonConnectButton />
        </div>

        <div className="h-px bg-gray-200 w-full my-4"></div>

        {assets.length > 0 ? (
          <div className="space-y-6">
            {/* From */}
            <div className="flex flex-col">
              <label className="text-sm font-medium text-gray-600 mb-1">
                From
              </label>
              <select
                value={fromAsset?.contractAddress || ''}
                onChange={handleChange(setFromAsset)}
                className="w-full p-3 bg-gray-50 border border-gray-200 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 transition-all"
              >
                {assets.map((asset) => (
                  <option key={asset.contractAddress} value={asset.contractAddress}>
                    {asset.meta?.symbol || asset.meta?.displayName || 'token'}
                  </option>
                ))}
              </select>
            </div>

            {/* To */}
            <div className="flex flex-col">
              <label className="text-sm font-medium text-gray-600 mb-1">
                To
              </label>
              <select
                value={toAsset?.contractAddress || ''}
                onChange={handleChange(setToAsset)}
                className="w-full p-3 bg-gray-50 border border-gray-200 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 transition-all"
              >
                {assets.map((asset) => (
                  <option key={asset.contractAddress} value={asset.contractAddress}>
                    {asset.meta?.symbol || asset.meta?.displayName || 'token'}
                  </option>
                ))}
              </select>
            </div>

            {/* Amount */}
            <div className="flex flex-col">
              <label className="text-sm font-medium text-gray-600 mb-1">
                Amount
              </label>
              <div className="relative">
                <input
                  type="number"
                  placeholder="0.0"
                  value={amount}
                  onChange={handleChange(setAmount)}
                  className="w-full p-3 bg-gray-50 border border-gray-200 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 transition-all"
                />
                <div className="absolute right-3 top-1/2 -translate-y-1/2 text-gray-500 text-sm">
                  {displaySymbol(fromAsset)}
                </div>
              </div>
            </div>

            {/* Buttons */}
            <div className="flex space-x-3">
              <button
                className="flex-1 bg-indigo-100 hover:bg-indigo-200 text-indigo-700 font-medium py-3 px-4 rounded-lg transition-all"
              >
                Simulate
              </button>
              <button className="flex-1 bg-indigo-600 hover:bg-indigo-700 text-white font-medium py-3 px-4 rounded-lg transition-all">
                Swap
              </button>
            </div>
          </div>
        ) : (
          <div className="flex justify-center items-center py-10">
            <div className="animate-pulse flex space-x-2">
              <div className="h-2 w-2 bg-indigo-500 rounded-full"></div>
              <div className="h-2 w-2 bg-indigo-500 rounded-full"></div>
              <div className="h-2 w-2 bg-indigo-500 rounded-full"></div>
            </div>
            <p className="ml-3 text-gray-600">Loading assets...</p>
          </div>
        )}
      </div>

      <div className="mt-6 text-center text-xs text-gray-500">
        Powered by STON.fi
      </div>
    </div>
  );
}

export default App;

Now your app shows:

  • A "Connect Wallet" button

  • Two dropdowns with tokens from STON.fi

  • An amount input

  • "Simulate" and "Swap" buttons (not functional yet).


5. Simulating a Swap

Before executing a swap transaction, it's good practice to simulate it. Simulation tells us how much of the target token we should receive given an input amount, factoring in liquidity and slippage. We'll use the simulateSwap function from StonApiClient.

// Step #5 changes in App.jsx
// keep all imports from step #4

function App() {
  // keep existing state from step #4 and add simulationResult state
  const [simulationResult, setSimulationResult] = useState(null);

  // update handleChange from step #4 to reset simulationResult when changing inputs
  const handleChange = (setter) => (e) => {
  // keep existing code from step #4
    
    setSimulationResult(null);
  };

  // add the handleSimulate function
  const handleSimulate = async () => {
    if (!fromAsset || !toAsset || !amount) return;
    try {
      const { decimals: fromDecimals } = getAssetInfo(fromAsset);
      const client = new StonApiClient();

      // Convert user-facing amount (e.g. 10.5) to blockchain units (e.g. 10500000000)
      // by multiplying by token decimals and converting to string for the API
      const offerUnits = (Number(amount) * fromDecimals).toString();
      
      const result = await client.simulateSwap({
        offerAddress: fromAsset.contractAddress,
        askAddress: toAsset.contractAddress,
        slippageTolerance: '0.01',
        offerUnits,
      });
      setSimulationResult(result);
    } catch (err) {
      console.error('Simulation failed:', err);
      setSimulationResult(null);
    }
  };

  // Convert blockchain units (e.g. 10500000000) back to user-friendly format (e.g. 10.5000)
  // This reverses the process done for input, dividing by token decimals
  const formattedOutputAmount = simulationResult
    ? (Number(simulationResult.minAskUnits) / getAssetInfo(toAsset).decimals).toFixed(4)
    : '';

  return (
    // keep the same JSX wrapper from step #4
    <div className="flex flex-col items-center justify-center min-h-screen bg-gradient-to-b from-blue-50 to-indigo-100 p-6">
        {/* ... existing code from step #4 ... */}

        {/* Update the Simulate button to attach handler */}
        <button
          onClick={handleSimulate}
          className="flex-1 bg-indigo-100 hover:bg-indigo-200 text-indigo-700 font-medium py-3 px-4 rounded-lg transition-all"
        >
          Simulate
        </button>
        {/* ... existing code from step #4 ... */}
            
        {/* Show simulation result if present */}
        
        {/* add simulation result */}
        {simulationResult && (
          <div className="mt-4 w-full max-w-md bg-white rounded-xl shadow-lg p-4">
            <div className="text-center">
              <p className="text-lg font-medium text-gray-800">Swap Summary</p>
              <div className="flex justify-center items-center space-x-2 mt-2">
                <p className="text-md font-bold">
                  {amount} {displaySymbol(fromAsset)}
                </p>
                <span className="text-gray-500">→</span>
                <p className="text-md font-bold">
                  {formattedOutputAmount}{' '}
                  {displaySymbol(toAsset)}
                </p>
              </div>
            </div>
          </div>
        )}
      <div className="mt-6 text-center text-xs text-gray-500">
        Powered by STON.fi
      </div>
    </div>
  );
}

export default App;

That's it for simulation. Now if you:

  • Select "From" and "To" tokens

  • Enter an amount

  • Click "Simulate"

…you should see an expected output.


6. Executing a Swap Transaction

Finally, let's make the Swap button actually send a transaction. We'll use the @ston-fi/sdk to build the swap parameters and pass them to the wallet via TonConnect.

We just add one more handler, handleSwap, and wire it to the Swap button. We'll also import everything needed from @ston-fi/sdk, plus the TonConnect UI hook to send transactions.

In App.jsx, near the top:

// Step #6 changes

// add extra imports:
import { dexFactory, Client } from "@ston-fi/sdk";
import { TonConnectButton, useTonConnectUI, useTonAddress } from '@tonconnect/ui-react';

Inside the App component, after handleSimulate, add:

// continue in App.jsx
const [tonConnectUI] = useTonConnectUI();
const userAddress = useTonAddress();

  const handleSwap = async () => {
    if (!fromAsset || !toAsset || !amount || !userAddress) {
      alert('Please connect wallet and enter swap details.');
      return;
    }

    if(!simulationResult) {
      alert('Please simulate the swap first.');
      return;
    }
  
    try {
      // 1. Initialize API client
      const tonApiClient = new Client({
        endpoint: "https://toncenter.com/api/v2/jsonRPC",
      });
      
      // 2. Get router metadata and create DEX instance
      const client = new StonApiClient();
      const routerMetadata = await client.getRouter(simulationResult.routerAddress);
      const dexContracts = dexFactory(routerMetadata);
      const router = tonApiClient.open(
        dexContracts.Router.create(routerMetadata.address)
      );
      
      // 3. Prepare common transaction parameters
      const sharedTxParams = {
        userWalletAddress: userAddress,
        offerAmount: simulationResult.offerUnits,
        minAskAmount: simulationResult.minAskUnits,
      };
      
      // 4. Determine swap type and get transaction parameters
      const getSwapParams = () => {
        // TON -> Jetton
        if (fromAsset.kind === 'Ton') {
          return router.getSwapTonToJettonTxParams({
            ...sharedTxParams,
            proxyTon: dexContracts.pTON.create(routerMetadata.ptonMasterAddress),
            askJettonAddress: simulationResult.askAddress,
          });
        } 
        // Jetton -> TON
        if (toAsset.kind === 'Ton') {
          return router.getSwapJettonToTonTxParams({
            ...sharedTxParams,
            proxyTon: dexContracts.pTON.create(routerMetadata.ptonMasterAddress),
            offerJettonAddress: simulationResult.offerAddress,
          });
        }
        // Jetton -> Jetton (no proxyTon needed)
        return router.getSwapJettonToJettonTxParams({
          ...sharedTxParams,
          offerJettonAddress: simulationResult.offerAddress,
          askJettonAddress: simulationResult.askAddress,
        });
      };
      
      const swapParams = await getSwapParams();
      
      // 5. Send transaction via TonConnect
      await tonConnectUI.sendTransaction({
        validUntil: Date.now() + 5 * 60 * 1000,
        messages: [
          {
            address: swapParams.to.toString(),
            amount: swapParams.value.toString(),
            payload: swapParams.body?.toBoc().toString("base64"),
          }
        ]
      });
    } catch (err) {
      console.error('Swap failed:', err);
      alert('Swap transaction failed. See console for details.');
    }
  };

Finally, attach this new handler to the Swap button:

// in the render, near the Simulate button
<button
  className="flex-1 bg-indigo-600 hover:bg-indigo-700 text-white font-medium py-3 px-4 rounded-lg transition-all"
  onClick={handleSwap}
>
  Swap
</button>

Everything else stays the same as in Step #5.


7. Testing Your Swap

Now that your app is running, you can test the swap functionality:

  1. Connect your wallet by clicking the "Connect Wallet" button and selecting your TON wallet from the modal.

  2. Select tokens from the dropdown menus:

    • Choose a token you own in the "From" dropdown

    • Select a token you want to receive in the "To" dropdown

  3. Enter an amount you wish to swap (make sure it's an amount you actually have in your wallet).

  4. Simulate the swap by clicking the "Simulate" button to see the expected output amount before committing to the transaction.

  5. Execute the swap by clicking the "Swap" button. This will prompt your wallet to approve the transaction.

  6. Confirm in your wallet when the approval request appears.

Upon successful completion, the transaction will be processed on-chain, and your wallet balances will update accordingly. The whole process typically takes just a few seconds to complete.


8. Conclusion

You now have a React + Vite app with Tailwind CSS that:

  • Connects to a TON wallet using TonConnect.

  • Fetches available tokens from STON.fi.

  • Simulates swaps (via simulateSwap).

  • Builds and sends swap transactions (via @ston-fi/sdk).

Feel free to expand this demo with:

  • Improved decimal handling for each token.

  • Custom slippage tolerance settings.

  • More robust error/success messages.

Happy building on TON and STON.fi!


9. Live Demo

With this Replit demo, you can:

  • Open the project directly in your browser

  • Fork the Replit to make your own copy

  • Run the application to see it in action

  • Explore and modify the code to learn how it works

  • Experiment with different features and UI changes

10. Advanced Example App

For those seeking a feature-rich, more advanced approach, we also have a Next.js Demo App that:

  • Uses Next.js for a scalable framework

  • Utilizes hooks and providers for an elegant architecture

  • Demonstrates better error handling, robust state management, and additional STON.fi features

You can explore the code in our repository:

STON.fi SDK Next.js Demo App

Or see it in action at our live demo:

SDK Demo App

Liquidity Providing Guide

This guide will walk you through creating a basic liquidity provision app using the STON.fi SDK and API in a React project. We'll integrate wallet connectivity with TonConnect (via @tonconnect/ui-react) to allow users to connect their TON wallet and provide liquidity to pools. The guide is beginner-friendly and assumes minimal React experience.

Note: In this demo, we will leverage Tailwind CSS for styling instead of using custom CSS. The setup for Tailwind CSS is already included in the instructions below, so you don't need to set it up separately.

Note: You can use any package manager (npm, yarn, pnpm, or bun) to set up your React project. In this tutorial, we'll demonstrate with pnpm.


Table of Contents


1. Introduction

In this quickstart, we will build a minimal React app to:

  • Connect to a TON wallet (via TonConnect UI).

  • Fetch available tokens from STON.fi (via @ston-fi/api).

  • Simulate liquidity provision (to see expected LP tokens).

  • Execute a liquidity provision transaction on-chain (via @ston-fi/sdk).

We will use:

  • @ston-fi/sdk – Helps build the payload for the actual liquidity provision transaction.

  • @ston-fi/api – Lets us fetch asset lists and run liquidity provision simulations.

  • @tonconnect/ui-react – Provides a React-based TON wallet connect button and utilities.


2. Setting Up the Project

2.1 Create a React App

First, check if you have pnpm installed:

If pnpm is not installed, install it globally:

Create a new React + Vite project:

Provide a project name (e.g., stonfi-liquidity-app) and then:

2.2 Installing the Required Packages

In your project directory, install the required packages:

Install Tailwind CSS and the Node.js polyfills plugin for Vite:

Configure Vite by updating vite.config.ts:

In src/index.css, import Tailwind:

You can also remove src/App.css (we don't need it), and remove the import statement import './App.css' from src/App.tsx.

After making these changes, you can verify that your app still runs correctly by starting the development server:

Open http://localhost:5173. If you see a Vite/React starter page, everything is working correctly.


3. Connecting the Wallet

3.1 Add the TonConnect Provider

Open src/main.tsx and wrap your app in TonConnectUIProvider:

Note: For the purposes of this demo, we're serving the manifest from a static source. In a real application, you should replace this with your own manifest URL that you'll create in the next step.

3.2 Create the TonConnect Manifest

Create a file named tonconnect-manifest.json in your public folder:

Update with your own app name, domain, and icon.

3.3 Add the Connect Wallet Button

In src/App.tsx, import and add the TonConnectButton:


4. Fetching Available Assets

Let's fetch token data from STON.fi using StonApiClient. We'll filter by liquidity tags to keep the list manageable.

First, add new imports to the top of src/App.tsx:

Initialize the STON.fi API client:

Add state variables for token management:

Add the token fetching logic:

Add the token change handler:

Replace the return statement with the enhanced UI:


5. Simulating Liquidity Provision

We'll call the simulateLiquidityProvision function on the StonApiClient to get simulation results.

Add additional imports to the top of the file:

Add utility functions for converting token amounts:

Add wallet address and simulation state:

Add useEffect to reset simulation when tokens change:

Add the simulation handler after the existing handleTokenChange function:

Add the simulation button after the amount input fields:

Add the simulation results display after the simulate button:


6. Building the Transaction

Go to and get your API key.

Then create a .env file in the root of your project and add your API key there:

Now let's build the transaction using @ston-fi/sdk:

Add new imports to top of file and initialize TON JSON-RPC client:

Add handleProvideLiquidityClick callback after handleSimulationClick callback:

Add Provide Liquidity button after the simulation results div:


7. Executing the Provision

After clicking "Provide Liquidity", your wallet will prompt you to confirm and sign. The transaction will:

  1. Send token A and token B to the router contract

  2. Add liquidity to the pool

  3. Mint LP tokens to your LP wallet


8. Testing Your Provision

  1. Start the dev server:

  1. Open http://localhost:5173

  2. Connect your TON wallet

  3. Select two tokens and enter amounts

  4. Simulate to see expected LP tokens

  5. Click "Provide Liquidity" to execute the transaction

  6. Check your wallet for the new LP tokens


9. Conclusion

You've built a minimal React app that:

  • Connects to a TON wallet

  • Fetches tokens from STON.fi

  • Simulates liquidity provision

  • Handles both new and existing pools

  • Executes the provision transaction


10. Live Demo

With this Replit demo, you can:

  • Open the project directly in your browser

  • Fork the Replit to make your own copy

  • Run the application to see it in action

  • Explore and modify the code to learn how it works

  • Experiment with different features and UI changes

Alternatively, you can run this example locally by cloning the GitHub repository:

This will start the development server and you can access the app at http://localhost:5173.

Also remeber to add your TON API key to the .env file from step.


11. Next steps

For more advanced features you can add:

  • Dynamic slippage controls

  • read about it in the SDK docs

  • See the for more details

12. Advanced Example App

For those seeking a feature-rich, more advanced approach, we also have a Next.js demo app that:

  • Uses Next.js for a scalable framework

  • Utilizes hooks and providers for an elegant architecture

  • Demonstrates better error handling, robust state management, and additional STON.fi features

You can explore the code in our repository:

Or see it in action at our live demo:

pnpm --version
npm install -g pnpm
pnpm create vite --template react-ts
cd stonfi-liquidity-app
pnpm add @ston-fi/sdk @ston-fi/api @tonconnect/ui-react @ton/ton
pnpm add tailwindcss @tailwindcss/vite vite-plugin-node-polyfills
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import tailwindcss from "@tailwindcss/vite";
import { nodePolyfills } from "vite-plugin-node-polyfills";

export default defineConfig({
  plugins: [react(), tailwindcss(), nodePolyfills()],
});
@import "tailwindcss";
pnpm install
pnpm dev
import React from "react";
import ReactDOM from "react-dom/client";
import { TonConnectUIProvider } from "@tonconnect/ui-react";
import "./index.css";
import App from "./App.tsx";

ReactDOM.createRoot(document.getElementById("root")!).render(
  <React.StrictMode>
    <TonConnectUIProvider 
    // For demo purposes, we're using a static manifest URL
    // Replace with your own: manifestUrl={`${window.location.origin}/tonconnect-manifest.json`}
    manifestUrl="https://gist.githubusercontent.com/mrruby/243180339f492a052aefc7a666cb14ee/raw/"
    >

      <App />
    </TonConnectUIProvider>
  </React.StrictMode>
);
{
    "url": "https://stonfi-liquidity-demo.example.com",
    "name": "STON.fi Liquidity Provider",
    "iconUrl": "https://stonfi-liquidity-demo.example.com/icon-192x192.png"
}
import { TonConnectButton } from "@tonconnect/ui-react";

function App() {
  return (
    <div className="flex flex-col items-center justify-center min-h-screen p-6">
      <h1 className="text-2xl font-bold mb-4">STON.fi Liquidity Demo</h1>
      <TonConnectButton />
    </div>
  );
}

export default App;
import { useEffect, useState } from "react";
import { TonConnectButton } from "@tonconnect/ui-react";
import { StonApiClient, AssetTag, type AssetInfoV2  } from "@ston-fi/api";
const stonApiClient = new StonApiClient();
function App() {
  const [tokens, setTokens] = useState<AssetInfoV2[]>([]);
  const [tokenA, setTokenA] = useState<AssetInfoV2 | undefined>();
  const [tokenB, setTokenB] = useState<AssetInfoV2 | undefined>();
  const [amountA, setAmountA] = useState("");
  const [amountB, setAmountB] = useState("");
  // Fetch assets at startup
  useEffect(() => {
    const fetchTokens = async () => {
      try {
        const assets = await stonApiClient.queryAssets({
          // Query only assets with medium or higher liquidity to ensure tradability
          condition: `${AssetTag.LiquidityVeryHigh} | ${AssetTag.LiquidityHigh} | ${AssetTag.LiquidityMedium}`,
        });

        setTokens(assets);

        // Initialize default selections
        setTokenA(assets[0]);
        setTokenB(assets[1]);
      } catch (err) {
        console.error("Failed to fetch tokens:", err);
      }
    };
    fetchTokens();
  }, []);
  // Factory function for creating onChange handlers for token dropdowns
  // Uses composition to create reusable handlers for both token selectors
  const handleTokenChange =
    (setter: typeof setTokenA | typeof setTokenB) =>
    (event: { target: { value: string } }) => {
      const selected = tokens.find(
        (t) => t.contractAddress === event.target.value
      );
      if (selected) {
        setter(selected);
      }
    };
  return (
    <div className="flex flex-col items-center justify-center min-h-screen bg-gradient-to-b from-blue-50 to-indigo-100 p-6">
      <div className="max-w-md w-full bg-white rounded-lg shadow p-6 space-y-6">
        {/* Application header with branding and wallet connection */}
        <div className="flex justify-between items-center">
          <h1 className="text-2xl font-bold text-indigo-700">
            STON.fi Liquidity
          </h1>
          <TonConnectButton />
        </div>
        <hr className="border-gray-200" />

        {/* Main application interface (conditional on token data availability) */}
        {tokens.length > 0 ? (
          <>
            {/* Token A selection dropdown */}
            <div>
              <label className="block mb-1 text-sm font-medium text-gray-600">
                Token A
              </label>
              <select
                className="w-full p-2 border rounded"
                onChange={handleTokenChange(setTokenA)}
                value={tokenA?.contractAddress || ""}
              >
                {tokens.map((tok) => (
                  <option key={tok.contractAddress} value={tok.contractAddress}>
                    {tok.meta?.symbol ?? "Token"}
                  </option>
                ))}
              </select>
            </div>

            {/* Token B selection dropdown */}
            <div>
              <label className="block mb-1 text-sm font-medium text-gray-600">
                Token B
              </label>
              <select
                className="w-full p-2 border rounded"
                onChange={handleTokenChange(setTokenB)}
                value={tokenB?.contractAddress || ""}
              >
                {tokens.map((token) => (
                  <option
                    key={token.contractAddress}
                    value={token.contractAddress}
                  >
                    {token.meta?.symbol ?? "Token"}
                  </option>
                ))}
              </select>
            </div>

            {/* Amount input fields (side by side layout) */}
            <div className="flex space-x-4">
              <div className="flex-1">
                <label className="block mb-1 text-sm font-medium text-gray-600">
                  Amount A
                </label>
                <input
                  type="number"
                  className="w-full p-2 border rounded"
                  placeholder="0.0"
                  value={amountA}
                  onChange={(e) => setAmountA(e.target.value)}
                />
              </div>
              <div className="flex-1">
                <label className="block mb-1 text-sm font-medium text-gray-600">
                  Amount B
                </label>
                <input
                  type="number"
                  className="w-full p-2 border rounded"
                  placeholder="0.0"
                  value={amountB}
                  onChange={(e) => setAmountB(e.target.value)}
                />
              </div>
            </div>
          </>
        ) : (
          <p>Loading tokens...</p>
        )}
      </div>
    </div>
  );
}
import { useTonAddress } from "@tonconnect/ui-react";
import { type LiquidityProvisionSimulation } from "@ston-fi/api";
import { fromNano } from "@ton/ton";
// Convert floating point string amount into integer base units string
// Essential for blockchain transactions which use integer arithmetic
function toBaseUnits(amount: string, decimals?: number) {
  return Math.floor(parseFloat(amount) * 10 ** (decimals ?? 9)).toString();
}

// Convert integer base units back to a fixed 2-decimal string for display
function fromBaseUnits(baseUnits: string, decimals?: number) {
  return (parseInt(baseUnits) / 10 ** (decimals ?? 9)).toFixed(2);
}
function App() {
  const walletAddress = useTonAddress();
  
  // ... existing state variables ...
  
  const [simulation, setSimulation] = useState<
    LiquidityProvisionSimulation | Error | undefined
  >();
  // Reset simulation when tokens change
  useEffect(() => {
    setSimulation(undefined);
  }, [tokenA, tokenB]);
  const handleSimulationClick = async () => {
    if (!tokenA || !tokenB || !amountA || !amountB) {
      alert("Please select tokens and enter amounts");
      return;
    }

    try {
      // Retrieve available pools for tokens pair
      const pools = (
        await stonApiClient.getPoolsByAssetPair({
          asset0Address: tokenA.contractAddress,
          asset1Address: tokenB.contractAddress,
        })
      ).filter((pool) => !pool.deprecated);

      const pool = pools[0];

      // Retrieve simulation depending on pool availability
      const simulation = pool
        ? await stonApiClient.simulateLiquidityProvision({
            provisionType: "Balanced",
            tokenA: tokenA.contractAddress,
            tokenB: tokenB.contractAddress,
            tokenAUnits: toBaseUnits(amountA, tokenA?.meta?.decimals),
            poolAddress: pool.address,
            slippageTolerance: "0.001",
            walletAddress,
          })
        : await stonApiClient.simulateLiquidityProvision({
            provisionType: "Initial",
            tokenA: tokenA.contractAddress,
            tokenB: tokenB.contractAddress,
            tokenAUnits: toBaseUnits(amountA, tokenA?.meta?.decimals),
            tokenBUnits: toBaseUnits(amountB, tokenB?.meta?.decimals),
            slippageTolerance: "0.001",
            walletAddress,
          });

      setSimulation(simulation);
      setAmountB(fromBaseUnits(simulation.tokenBUnits, tokenB?.meta?.decimals));
    } catch (e) {
      setSimulation(new Error(e instanceof Error ? e.message : String(e)));
    }
  };
            <div className="flex space-x-4">
              <div className="flex-1">
                <label className="block mb-1 text-sm font-medium text-gray-600">
                  Amount A
                </label>
                <input
                  type="number"
                  className="w-full p-2 border rounded"
                  placeholder="0.0"
                  value={amountA}
                  onChange={(e) => setAmountA(e.target.value)}
                />
              </div>
              <div className="flex-1">
                <label className="block mb-1 text-sm font-medium text-gray-600">
                  Amount B
                </label>
                <input
                  type="number"
                  className="w-full p-2 border rounded"
                  placeholder="0.0"
                  value={amountB}
                  onChange={(e) => setAmountB(e.target.value)}
                />
              </div>
            </div>

            {/* Simulation trigger button with validation */}
            <button
              onClick={handleSimulationClick}
              className="w-full bg-indigo-500 hover:bg-indigo-600 text-white font-medium py-2 rounded"
            >
              Simulate
            </button>
            {simulation ? (
              simulation instanceof Error ? (
                <p className="text-red-600 text-sm">{simulation.message}</p>
              ) : (
                <>
                  <div className="p-4 bg-gray-50 rounded border border-gray-200 text-sm overflow-x-auto break-all max-w-full">
                    <p className="font-semibold text-gray-800">
                      Simulation Result
                    </p>
                    <ul className="list-disc list-inside mt-2 space-y-1 text-gray-700">
                      {Object.entries({
                        "Provision Type": simulation.provisionType,
                        "Pool Address": simulation.poolAddress,
                        "Router Address": simulation.routerAddress,
                        "Token A": simulation.tokenA,
                        "Token B": simulation.tokenB,
                        "Token A Units": fromBaseUnits(
                          simulation.tokenAUnits,
                          tokenA?.meta?.decimals
                        ),
                        "Token B Units": fromBaseUnits(
                          simulation.tokenBUnits,
                          tokenB?.meta?.decimals
                        ),
                        "LP Account": simulation.lpAccountAddress,
                        "Estimated LP": fromNano(simulation.estimatedLpUnits),
                        "Min LP": fromNano(simulation.minLpUnits),
                        "Price Impact": simulation.priceImpact,
                      }).map(([label, value]) => (
                        <li key={label}>
                          <span className="font-medium">{label}:</span>{" "}
                          <span className="break-all">{value}</span>
                        </li>
                      ))}
                    </ul>
                  </div>
                </>
              )
            ) : null}
VITE_TON_API_KEY=your_api_key_here
import { useTonConnectUI } from "@tonconnect/ui-react";
import { dexFactory } from "@ston-fi/sdk";
import { TonClient, fromNano } from "@ton/ton";

// TON JSON-RPC client for blockchain interactions
const tonApiClient = new TonClient({
  endpoint: "https://toncenter.com/api/v2/jsonRPC",
  apiKey: import.meta.env.VITE_TON_API_KEY,
});
const [tonConnectUI] = useTonConnectUI();

const handleProvideLiquidityClick = async () => {
  if (!simulation || simulation instanceof Error) {
    alert("Simulation is not valid");
    return;
  }

  const tonAsset = tokens.find((token) => token.kind === "Ton");
  if (!tonAsset) {
    alert("TON asset info not found");
    return;
  }

  try {
    // Retrieve router metadata
    const routerMetadata = await stonApiClient.getRouter(
      simulation.routerAddress
    );

    // Create contract instances
    const { Router, pTON } = dexFactory(routerMetadata);
    const router = tonApiClient.open(Router.create(routerMetadata.address));
    const pTon = pTON.create(routerMetadata.ptonMasterAddress);

    const isTonAsset = (contractAddress: string) =>
      contractAddress === tonAsset.contractAddress;

    const buildTransaction = async (args: {
      sendAmount: string;
      sendTokenAddress: string;
      otherTokenAddress: string;
    }) => {
      const params = {
        userWalletAddress: walletAddress,
        minLpOut: simulation.minLpUnits,
        sendAmount: args.sendAmount,
        otherTokenAddress: isTonAsset(args.otherTokenAddress)
          ? pTon.address
          : args.otherTokenAddress,
      };

      // TON requires proxy contract, Jettons use direct transfer
      if (isTonAsset(args.sendTokenAddress)) {
        return await router.getProvideLiquidityTonTxParams({
          ...params,
          proxyTon: pTon,
        });
      } else {
        return await router.getProvideLiquidityJettonTxParams({
          ...params,
          sendTokenAddress: args.sendTokenAddress,
        });
      }
    };

    // Generate transaction parameters for both tokens
    // Different methods are used for TON vs Jetton tokens due to blockchain mechanics
    const txParams = await Promise.all([
      buildTransaction({
        sendAmount: simulation.tokenAUnits,
        sendTokenAddress: simulation.tokenA,
        otherTokenAddress: simulation.tokenB,
      }),
      buildTransaction({
        sendAmount: simulation.tokenBUnits,
        sendTokenAddress: simulation.tokenB,
        otherTokenAddress: simulation.tokenA,
      }),
    ]);

    // Format transaction messages for TonConnect sendTransaction interface
    const messages = txParams.map((txParam) => ({
      address: txParam.to.toString(),
      amount: txParam.value.toString(),
      payload: txParam.body?.toBoc().toString("base64"),
    }));

    // Trigger TonConnect modal for user transaction approval
    await tonConnectUI.sendTransaction({
      validUntil: Date.now() + 5 * 60 * 1000, // Transaction valid for 5 minutes
      messages,
    });
  } catch (e) {
    alert(`Error: ${e}`);
  }
};
                    {Object.entries({
                      "Provision Type": simulation.provisionType,
                      "Pool Address": simulation.poolAddress,
                      "Router Address": simulation.routerAddress,
                      "Token A": simulation.tokenA,
                      "Token B": simulation.tokenB,
                      "Token A Units": fromBaseUnits(
                        simulation.tokenAUnits,
                        tokenA?.meta?.decimals
                      ),
                      "Token B Units": fromBaseUnits(
                        simulation.tokenBUnits,
                        tokenB?.meta?.decimals
                      ),
                      "LP Account": simulation.lpAccountAddress,
                      "Estimated LP": fromNano(simulation.estimatedLpUnits),
                      "Min LP": fromNano(simulation.minLpUnits),
                      "Price Impact": simulation.priceImpact,
                    }).map(([label, value]) => (
                      <li key={label}>
                        <span className="font-medium">{label}:</span>{" "}
                        <span className="break-all">{value}</span>
                      </li>
                    ))}
                  </ul>
                </div>

                <button
                  onClick={handleProvideLiquidityClick}
                  className="w-full bg-green-500 hover:bg-green-600 text-white font-medium py-2 rounded mt-4"
                >
                  Provide Liquidity
                </button>
                </>

              )
            ) : null}
pnpm dev
git clone https://github.com/mrruby/stonfi-liquidity-app.git
cd omniston-swap-app
pnpm install
pnpm dev
Introduction
Setting Up the Project
Create a React App
Installing the Required Packages
Connecting the Wallet
Add the TonConnect Provider
Create the TonConnect Manifest
Add the Connect Wallet Button
Fetching Available Assets
Simulating Liquidity Provision
Handling "Pool Already Exists"
Building the Transaction
Executing the Provision
Testing Your Provision
Conclusion
Live Demo
Next steps
Advanced Example App
TON Center
Building the Transaction
Burn liquidity
SDK Liquidity docs
STON.fi SDK Next.js Demo App
SDK Demo App

Swap overview

Swap settlement method

Settlement of the trade by direct swap using third-party contracts.

Actors

Trader

A protocol user who performs the token exchange.

Resolver

Service providing a token exchange rate, also known as Market Maker.

WebSocket API

Data types used:

  • Blockchain Number. Blockchain code as defined by SLIP-044. (https://github.com/satoshilabs/slips/blob/master/slip-0044.md) For TON, it is 607.

  • Address Object. It consists of two fields:

  • blockchain - blockchain of the asset,

  • address - address of assets, wallets in the specified Blockchain.

For example:

{
  "blockchain": 607,
  "address": "EQA2kCVNwVsil2EM2mB0SkXytxCqQjS4mttjDpnXmwG9T6bO"
}
  • Amount String. Amount of assets in units.

  • SettlementMethod Number. The method of trade settlement. Value of enum of settlement methods. Currently, supports only Swap settlement method, which is 0.

  • Protocol\

    • WebSocket API uses a numeric code:

      • StonFiV1 = 1

      • StonFiV2 = 2

      • DeDust = 3

      • TonCo = 4

For gRPC usage, see the Resolver Integration Guide.

  • GaslessSettlement\

    • WebSocket API uses a numeric code:

      • GASLESS_SETTLEMENT_PROHIBITED = 0 — Gasless settlement either unsupported or prohibited by trader.

      • GASLESS_SETTLEMENT_POSSIBLE = 1 — Gasless settlement is allowed if supported by the resolver.

      • GASLESS_SETTLEMENT_REQUIRED = 2 — Gasless settlement is mandatory.

  • Number Integer number.

  • RequestSettlementParams Object. Additional parameters for the quote request (RFQ):

    • max_price_slippage_bps: Maximum price slippage, in basis points (0.01%). For example, 100 = 1% slippage.

    • max_outgoing_messages: Maximum number of outgoing messages supported by the wallet. For TON blockchain, this defaults to 4 if omitted.

    • gasless_settlement: GaslessSettlement enum value specifying trader preference for gasless settlement (see GaslessSettlement above).

    Note: These parameters are general RFQ settings and may be used across different settlement methods. They are not specific to any particular settlement method.

  • SwapSettlementParams Object. Settlement parameters specific to the swap settlement method. This type is used in the Quote's params field when the settlement method is swap. Contains:

    • routes: Array of SwapRoute objects that define the possible paths for the swap.

    Each SwapRoute consists of:

    • steps: Array of SwapStep objects describing each step in the swap route

    • gas_budget: String indicating the gas budget to complete the transfer

    Each SwapStep contains:

    • bid_asset_address: Address object for the bid asset

    • ask_asset_address: Address object for the asset being requested

    • chunks: Array of SwapChunk objects describing each step in the swap route.

    Note on Steps vs. Chunks:

    • A "step" transforms from one asset to another (e.g. TON → USDC).

    • Each step may contain one or more "chunks," each representing an individual swap operation for that step.

    • On the TON blockchain, you will generally see exactly one chunk per step. We keep the ability to specify multiple chunks in a single step for other blockchains if needed.

    Each SwapChunk represents a single swap operation:

    • protocol: Protocol used for this swap operation. In this doc, numeric codes are used for the WebSocket API. For gRPC usage, see Resolver Integration Guide.

    • bid_amount: Amount of the bid asset

    • ask_amount: Expected amount of the asked asset

    • extra_version: Currently, only 1 is supported.

    • extra: Bytes array used by the underlying protocol to coordinate the swap (base64-encoded in JSON). See Swap Extra doc

  • quote_id String. A 32-byte identifier of the quote.

API Methods

Every method name contains version of API. The latest version is v1beta7. Previous versions may be supported for backward compatibility.

Subscription method quote

Allows you to create a new request for quote (RFQ).

Accepts the following parameters as input:

Name
Type
Description

bid_asset_address

Address

Blockchain-specific address of bid asset.

ask_asset_address

Address

Blockchain-specific address of ask asset.

amount

Amount

Either the amount of bid asset or ask asset: bid_units - The amount of bid asset the trader wants to pay, including all fees. ask_units - The amount of ask asset the trader wants to get after all fees.

referrer_address

Address

The address of referrer that will receive the fees or empty string if referrer is not specified.

referrer_fee_bps

Number

The amount of fees required by the referrer in basis points (1/10000 or 0.01%)

settlement_methods

Array(SettlementMethod)

Supported methods of swap settlement. The protocol limits settlement methods in quotes to specified methods. Different combinations of bid & ask chains might support different methods.

Sample:

{
  "jsonrpc": "2.0",
  "id": "1",
  "method": "v1beta7.quote",
  "params": {
    "bid_asset_address": {
      "blockchain": 607,
      "address": "EQDB8JYMzpiOxjCx7leP5nYkchF72PdbWT1LV7ym1uAedINh"
    },
    "amount": {
      "ask_units": "1000"
    },
    "ask_asset_address": {
      "blockchain": 607,
      "address": "EQCH-yP4S3nA_j7K7EIV1QIhVTWMyNJfxeYzacUH76ks2hUF"
    },
    "referrer_address": {
      "blockchain": 607,
      "address": "EQCXSs2xZ2dhk9TAxzGzXra2EbG_S2SqyN8Tfi6fJ82EYiVj"
    },
    "referrer_fee_bps": 10,
    "settlement_methods": [0],
    "settlement_params": {
      "max_price_slippage_bps": 100,
      "max_outgoing_messages": 4,
      "gasless_settlement": 1
    }
  }
}

After the request (RFQ), the channel will start receiving quote_updated events from resolvers, which consist of a quote. A Quote in the channel is updated if a more profitable deal for the trader comes in. The Quote includes the following fields:

Name
Type
Description

quote_id

String

ID of the quote generated by the platform (32 bytes)

resolver_id

String

ID of the Resolver

resolver_name

String

Name of the Resolver

bid_asset_address

Address

Blockchain-specific address of bid asset.

ask_asset_address

Address

Blockchain-specific address of ask asset.

bid_units

Amount

The amount of bid asset the trader must pay, including all fees.

ask_units

Amount

The amount of ask asset the trader will get after all fees.

referrer_address

Address

The address of referrer that will receive the fees or empty string if referrer is not specified.

referrer_fee_asset

Address

The asset of the fees that the referrer will get

referrer_fee_units

Number

The amount of fees that the referrer will get (in units of referrer_fee_asset)

protocol_fee_asset

Address

The asset of the fees charged by the protocol

protocol_fee_units

Number

The amount of fees charged by the protocol (in units of protocol_fee_asset).

quote_timestamp

Number

The timestamp (UTC seconds) of Quote sent by resolver.

trade_start_deadline

Number

Max timestamp (UTC seconds) of start of the trade. The start of the trade is defined as the reception of bid asset by the corresponding smart contract. The resolver may still settle the trades started after this deadline has passed at own discretion.

estimated_gas_consumption

Number

Estimated amount of gas units that will be spent to perform the trade

params

Object

Additional parameters specific to settlement method. Details see below

For Swap settlement method the params is:

Name
Type
Description

routes

Array(SwapRoute)

Array of Swap routes

min_ask_amount

Amount

Minimum amount to receive (filled by Omniston service)

recommended_min_ask_amount

Amount

Recommended minimum amount (filled by Omniston service)

recommended_slippage_bps

Number

Recommended slippage in basis points

Object SwapRoute:

Name
Type
Description

steps

Array(SwapStep)

Array of Swap steps

gas_budget

Amount

The amount of gas budget to complete transfer

Object SwapStep:

Name
Type
Description

bid_asset_address

Address

Blockchain-specific address of bid asset.

ask_asset_address

Address

Blockchain-specific address of ask asset.

chunks

Array(SwapChunk)

Array of swap chunks

Object SwapChunk:

Name
Type
Description

protocol

Protocol

Protocol used for this swap operation. In this doc, numeric codes are used for the WebSocket API. For gRPC usage, see .

bid_amount

Amount

The amount of bid asset the trader must pay, including all fees.

ask_amount

Amount

The expected amount of ask asset the trader will get after all fees.

extra_version

Number

Currently, only 1 is supported. Attempts to use any other version will be rejected.

extra

bytes

Bytes array used by the underlying protocol to coordinate the swap (base64-encoded in JSON). See

For more details on how to populate or interpret the extra field for these protocols, see the (Swap Extra doc).

Sample:

{
  "jsonrpc": "2.0",
  "method": "event",
  "params": {
    "subscription": 608688935135881,
    "result": {
      "event": {
        "quote_updated": {
          "quote_id": "98616cab2097d3f9e64e9447f2f3b736",
          "bid_asset_address": {
            "blockchain": 607,
            "address": "EQAIcb1WqNr0E7rOXgO0cbAZQnVbS06mgH2vgBvtBE6p0T2a"
          },
          "ask_asset_address": {
            "blockchain": 607,
            "address": "EQDk2VTvn04SUKJrW7rXahzdF8_Qi6utb0wj43InCu9vdjrR"
          },
          "bid_units": "100000",
          "ask_units": "33757",
          "referrer_address": {
            "blockchain": 607,
            "address": "EQCXSs2xZ2dhk9TAxzGzXra2EbG_S2SqyN8Tfi6fJ82EYiVj"
          },
          "referrer_fee_units": "0",
          "protocol_fee_units": "0",
          "quote_timestamp": 1723144166,
          "trade_start_deadline": 1723144766,
          "params": {
            "swap": {
              "routes": [
                {
                  "steps": [
                    {
                      "bid_asset_address": {
                        "blockchain": 607,
                        "address": "EQAIcb1WqNr0E7rOXgO0cbAZQnVbS06mgH2vgBvtBE6p0T2a"
                      },
                      "ask_asset_address": {
                        "blockchain": 607,
                        "address": "EQDk2VTvn04SUKJrW7rXahzdF8_Qi6utb0wj43InCu9vdjrR"
                      },
                      "chunks": [
                        {
                          "protocol": "StonFiV1",
                          "bid_amount": "100000",
                          "ask_amount": "33757",
                          "extra_version": 1,
                          "extra": "SGVsbG8sIHRoaXMgaXMgYW4gZXhhbXBsZSBiYXNlNjQgZW5jb2RlZCBzdHJpbmcgZm9yIHRoZSBleHRyYSBmaWVsZC4="
                        }
                      ]
                    }
                  ],
                  "gas_budget": "300000000"
                }
              ]
            }
          }
        }
      }
    }
  }
}

Subscription method trade.track

Method that allows you to track the status of a token exchange.

Accepts the following parameters as input:

Name
Type
Description

quote_id

String

(32 bytes) ID of the quote

trader_wallet_address

Address

The address of trader's wallet that initiated transaction

outgoing_tx_hash

String

Hash of the transaction that initiated the trade

Sample:

{
  "jsonrpc": "2.0",
  "id": "1",
  "method": "v1beta7.trade.track",
  "params": {
    "quote_id": "cf1f9bf159a26a7be14616da637882df",
    "trader_wallet_address": {
      "blockchain": 607,
      "address": "0QCXSs2xZ2dhk9TAxzGzXra2EbG_S2SqyN8Tfi6fJ82EYsMs"
    },
    "outgoing_tx_hash": "b35fb27b0aa9867e4072905cb967c3d08c6be8b0a66398fe08d2a72cb7c45082"
  }
}

The channel will start receiving records about the current status of the transfer. The following statuses are available:

Status
Description
Additional Fields

awaiting_transfer

Waiting for the trader to initiate the trade.

transferring

Initial transaction found, waiting for transfer of funds to complete.

swapping

(SWAP method only) Awaiting swap transactions in the pools.

  • routes - Info about partial filling of the trade. Array of RouteStatus.

awaiting_fill

(ESCROW / HTLC only) Waiting for the trade to be filled.

claim_available

(HTLC only) Resolver's deposit is claimable.

refund_available

(ESCROW / HTLC) Deposit timeout has expired, need to refund it.

receiving_funds

The transaction with incoming funds found, waiting for it to mine.

  • routes - Info about partial filling of the trade.

trade_settled

The trade has completed (fully or partially filled or fully aborted)

result - Result of the trade: - 0 - Unknown - 1 - fully filled - 2 - partially filled - 3 - aborted routes - Info about partial filling of the trade. Array of RouteStatus

Object Descriptions

RouteStatus

  • steps - Array of StepStatus objects.

StepStatus

  • chunks - Array of ChunkStatus objects.

ChunkStatus

Name
Type
Description

target_address

Address

Address of the contract that processes this chunk. Generally, this address receives bid tokens. More specifically, it might be the address of a protocol or liquidity pool.

bid_units

Amount

The amount of bid asset transferred from trader wallet

expected_ask_units

Amount

The expected amount of ask asset to be transferred to trader wallet

actual_ask_units

Amount

The actual amount of ask asset transferred to trader wallet

result

Number

Result of the chunk: - 0 - Processing - 1 - Filled - 2 - Aborted

protocol

Protocol

Protocol of this swap operation

tx_hash

String

Hash of the transaction that executed this chunk

Samples:

{
  "jsonrpc": "2.0",
  "method": "status",
  "params": {
    "subscription": 4374307320472544,
    "result": {
      "status": {
        "awaiting_transfer": {}
      }
    }
  }
}
{
  "jsonrpc": "2.0",
  "method": "status",
  "params": {
    "subscription": 8556742379230871,
    "result": {
      "status": {
        "transferring": {}
      }
    }
  }
}
{
  "jsonrpc": "2.0",
  "method": "status",
  "params": {
    "subscription": 8556742379230871,
    "result": {
      "status": {
        "trade_settled": {
          "result": 1,
          "routes": [
            {
              "steps": [
                {
                  "chunks": [
                    {
                      "target_address": {
                        "blockchain": 607,
                        "address": "EQACpXPHyLueReUMCmStesRXA77IK_ZAhHnLKAsQ5MjMUliK"
                      },
                      "bid_units": "1000000",
                      "expected_ask_units": "3379",
                      "actual_ask_units": "3378",
                      "result": 1,
                      "protocol": "StonFiV1",
                      "tx_hash": "97c06867d0e891369fa0f621e0b7d549c888af8f34e4bde6752d580c6da508c2"
                    }
                  ]
                }
              ]
            }
          ]
        }
      }
    }
  }
}

Method transaction.build_transfer

A request to generate unsigned transfer to initiate the trade.

Accepts the following parameters as input:

Name
Type
Description

source_address

Address

The address on bid_blockchain that will send initial transaction to start the trade

destination_address

Address

The address on ask_blockchain that will receive result of the trade

gas_excess_address

Address

The address that will receive the gas not spent by the trade

quote

Quote

The valid quote received from quote subscription

use_recommended_slippage

Boolean

Use the slippage recommended by Omniston instead of trader-provided slippage

Sample:

{
  "jsonrpc": "2.0",
  "id": 3,
  "method": "v1beta7.transaction.build_transfer",
  "params": {
    "source_address": {
      "blockchain": 607,
      "address": "0:02a573c7c8bb9e45e50c0a64ad7ac45703bec82bf6408479cb280b10e4c8cc52"
    },
    "destination_address": {
      "blockchain": 607,
      "address": "0:02a573c7c8bb9e45e50c0a64ad7ac45703bec82bf6408479cb280b10e4c8cc52"
    },
    "gas_excess_address": {
      "blockchain": 607,
      "address": "0:02a573c7c8bb9e45e50c0a64ad7ac45703bec82bf6408479cb280b10e4c8cc52"
    },
    "quote": {
      "quote_id": "3d1d9ad4656f883ce63cb80c67b8698c",
      "bid_asset_address": {
        "blockchain": 607,
        "address": "EQAIcb1WqNr0E7rOXgO0cbAZQnVbS06mgH2vgBvtBE6p0T2a"
      },
      "ask_asset_address": {
        "blockchain": 607,
        "address": "EQDk2VTvn04SUKJrW7rXahzdF8_Qi6utb0wj43InCu9vdjrR"
      },
      "bid_units": "100000000",
      "ask_units": "33787874",
      "referrer_address": "",
      "referrer_fee_units": "0",
      "protocol_fee_units": "0",
      "quote_timestamp": 1723146414,
      "trade_start_deadline": 1723144766,
      "params": {
        "swap": {
          "routes": [
            {
              "steps": [
                {
                  "bid_asset_address": {
                    "blockchain": 607,
                    "address": "EQAIcb1WqNr0E7rOXgO0cbAZQnVbS06mgH2vgBvtBE6p0T2a"
                  },
                  "ask_asset_address": {
                    "blockchain": 607,
                    "address": "EQDk2VTvn04SUKJrW7rXahzdF8_Qi6utb0wj43InCu9vdjrR"
                  },
                  "chunks": [
                    {
                      "protocol": 0,
                      "bid_amount": "100000",
                      "ask_amount": "33757",
                      "extra": "SGVsbG8sIHRoaXMgaXMgYW4gZXhhbXBsZSBiYXNlNjQgZW5jb2RlZCBzdHJpbmcgZm9yIHRoZSBleHRyYSBmaWVsZC4=",
                      "extra_version": 1
                    }
                  ]
                }
              ],
              "gas_budget": "300000000"
            }
          ]
        }
      }
    }
  }
}

The response contains a ton object with the blockchain-specific transaction data:

Name
Type
Description

ton

Object

Contains TON-specific transaction data

ton.messages

Array(Message)

Array of messages to be sent

Each Message object contains:

Name
Type
Description

target_address

String

The address of the recipient

send_amount

String

Amount in nanotons to send as attached value

payload

String

Hex-encoded or base64-encoded transaction data

{
  "jsonrpc": "2.0",
  "result": {
    "ton": {
      "messages": [
        {
          "target_address": "EQCJ7SWUt1weFuOCN32XNzT6dOM2stAe2LZZtOelF0hyoIRv",
          "send_amount": "300000000",
          "payload": "b5ee9c7241010201008800016d0f8a7ea5e63cb80c67b8698c405f5e1008013d9e00e89901bff71ffe5d1c1c92610888072c54e8fa27242807d7c6976a36ac103b9aca0301009725938561801af7bf08bea0d52dcdcf23d2c039f33a4184f23406bf0351e922a6bf6431e78ba803d391610000a95cf1f22ee791794302992b5eb115c0efb20afd90211e72ca02c43932331490056baf1e"
        }
      ]
    }
  },
  "id": 3
}
Resolver Integration Guide
Swap Extra doc

Omniston Guide

This guide will walk you through creating a basic token swap app using the Omniston protocol to swap assets across different DEXes (STON.fi V1, STON.fi V2, DeDust, etc.). We'll integrate wallet connectivity with TonConnect (via @tonconnect/ui-react) to allow users to connect their TON wallet and perform a swap. The guide is beginner-friendly and assumes minimal React experience.

Note: In this demo, we will leverage Tailwind CSS for styling instead of using custom CSS. The setup for Tailwind CSS is already included in the instructions below, so you don't need to set it up separately.

Note: You can use any package manager (npm, yarn, pnpm, or bun) to set up your React project. In this tutorial, we'll demonstrate with pnpm.


Table of Contents

  1. Introduction

  2. Setting Up the Project

    1. Create a React App

    2. Installing the Required Packages

  3. Connecting the Wallet

    1. Add nessary providers

    2. Create the TonConnect Manifest

    3. Add the Connect Wallet Button

  4. Fetching Available Assets

  5. Requesting a Quote

  6. Building a Transaction

  7. Tracking Your Trade

    1. Install the TON Package

    2. Using the useTrackTrade Hook

  8. Testing Your Swap

  9. Conclusion

  10. Live Demo

  11. Advanced Example App


1. Introduction

In this quickstart, we will build a minimal React app to:

  • Connect to a TON wallet (via TonConnect UI).

  • Fetch available tokens from STON.fi and display them in dropdowns.

  • Request a quote (RFQ) from Omniston for the best swap route (no separate "Simulate" step needed; Omniston fetches quotes automatically).

  • Build and execute a swap transaction across multiple DEXes.

  • Track the trade status until completion.

We will use:

  • @ston-fi/omniston-sdk-react – React hooks to interact with Omniston (request quotes, track trades, etc.).

  • @ston-fi/api – Fetch token lists from STON.fi (and potentially other data).

  • @tonconnect/ui-react – Provides a React-based TON wallet connect button and utilities.

  • @ton/core – TON low-level library used for advanced functionality.


2. Setting Up the Project

2.1 Create a React App

First, let's check if pnpm is installed on your system:

pnpm --version

If you see a version number (like 10.4.0), pnpm is installed. If you get an error, you'll need to install pnpm first:

npm install -g pnpm

Now we'll create a new React project using Vite. However, you can use any React setup you prefer (Next.js, CRA, etc.).

Run the following command to create a new Vite-based React project:

pnpm create vite --template react-ts

When prompted, type your desired project name (e.g., omniston-swap-app):

Project name: » omniston-swap-app

Then enter the folder:

cd omniston-swap-app

2.2 Installing the Required Packages

Within your new React project directory, install the Omniston SDK, TonConnect UI, the TON core library, and STON.fi API library:

pnpm add @ston-fi/omniston-sdk-react @tonconnect/ui-react @ton/core @ston-fi/api

Next, install Tailwind CSS and its Vite plugin:

pnpm add tailwindcss @tailwindcss/vite

Additionally, install the Node.js polyfills plugin for Vite, which is necessary to provide Buffer and other Node.js APIs in the browser environment (required by TON libraries):

pnpm add vite-plugin-node-polyfills

Configure the Vite plugin by updating vite.config.js file:

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import tailwindcss from '@tailwindcss/vite'
import { nodePolyfills } from 'vite-plugin-node-polyfills'

// https://vite.dev/config/
export default defineConfig({
  plugins: [react(), tailwindcss(), nodePolyfills()],
})

Then, import Tailwind CSS in your main CSS file. Open src/index.css and replace any existing code with:

@import "tailwindcss";

You can also remove src/App.css (we don't need it), and remove the import statement import './App.css' from src/App.tsx.

After making these changes, you can verify that your app still runs correctly by starting the development server:

pnpm install
pnpm dev

This should launch your app in development mode, typically at http://localhost:5173. You should see the Vite + React logo and text on a plain white background. Since we've removed the default styling (App.css), the page will look simpler than the default template.

If you see the logo and text, it means your Vite + React setup is working correctly. Make sure everything loads without errors before proceeding to the next step.


3. Connecting the Wallet

3.1 Add nessary providers

Open src/main.tsx (Vite's default entry point) and wrap your application with both the TonConnectUIProvider and OmnistonProvider. The TonConnectUIProvider makes the TonConnect context available to your app for wallet connectivity, while the OmnistonProvider enables Omniston's functionality throughout your application. Also, point the TonConnect provider to a manifest file (which we will create next) that describes your app to wallets.

// src/main.tsx
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import { TonConnectUIProvider } from '@tonconnect/ui-react';
import { OmnistonProvider } from '@ston-fi/omniston-sdk-react';
import './index.css'
import App from './App.tsx'

createRoot(document.getElementById('root')!).render(
  <StrictMode>
    <TonConnectUIProvider 
      // For demo purposes, we're using a static manifest URL
      // Replace with your own: manifestUrl={`${window.location.origin}/tonconnect-manifest.json`}
      manifestUrl="https://gist.githubusercontent.com/mrruby/243180339f492a052aefc7a666cb14ee/raw/">
      <OmnistonProvider apiUrl="wss://omni-ws.ston.fi">
        <App />
      </OmnistonProvider>
    </TonConnectUIProvider>
  </StrictMode>,
)

3.2 Create the TonConnect Manifest

In the public folder of your project, create a file named tonconnect-manifest.json. This manifest provides wallet apps with information about your application (like name and icon). You should customize this manifest for your own application. Here's an example:

{
  "url": "https://omniston-demo.example.com",
  "name": "Omniston Swap Demo",
  "iconUrl": "https://omniston-demo.example.com/icon-192x192.png"
}

Make sure to update these fields for your application:

  • url: The base URL where your app is served

  • name: Your application's display name (this is what wallets will show to users)

  • iconUrl: A link to your app's icon (should be a 180×180 PNG image)

Make sure this file is accessible. When the dev server runs, you should be able to fetch it in your browser at http://localhost:5173/tonconnect-manifest.json.


3.3 Add the Connect Wallet Button

In your main App component (e.g., src/App.tsx), import and include the TonConnectButton. For example:

// src/App.tsx
import { TonConnectButton } from '@tonconnect/ui-react';

function App() {
  return (
    <div className="flex flex-col items-center justify-center min-h-screen p-4">
      <h1 className="text-2xl font-bold mb-4">Omniston Swap Demo</h1>
      <TonConnectButton />
    </div>
  );
}

export default App;

4. Fetching Available Assets

Next, let's dynamically retrieve the list of tokens (assets) that can be swapped on STON.fi. We use the STON.fi API client (@ston-fi/api) for this. Here's a simplified example that filters assets by liquidity (high to medium). We'll store them in state and present them in From/To dropdowns.

First, add the necessary imports to what we have in src/App.tsx:

import { useEffect, useState } from 'react';
import { StonApiClient, AssetTag, , type AssetInfoV2 } from '@ston-fi/api';

Initialize the state variables in your App component:

function App() {
  const [assets, setAssets] = useState<AssetInfoV2[]>([]);
  const [fromAsset, setFromAsset] = useState<AssetInfoV2 | undefined>();
  const [toAsset, setToAsset] = useState<AssetInfoV2 | undefined>();
  const [amount, setAmount] = useState('');

Add the asset fetching logic with useEffect:

  // fetch assets on mount
  useEffect(() => {
    const fetchAssets = async () => {
      try {
        const client = new StonApiClient();
        // Filter out top liquidity tokens for brevity
        const condition = [
          AssetTag.LiquidityVeryHigh,
          AssetTag.LiquidityHigh,
          AssetTag.LiquidityMedium
        ].join(' | ');
        const assetList = await client.queryAssets({ condition });

        setAssets(assetList);
        if (assetList.length > 0) {
          setFromAsset(assetList[0]);
        }
        if (assetList.length > 1) {
          setToAsset(assetList[1]);
        }
      } catch (err) {
        console.error('Failed to fetch assets:', err);
      }
    };
    fetchAssets();
  }, []);

Create the main UI structure:

  return (
    <div className="flex flex-col items-center justify-center min-h-screen bg-gradient-to-b from-blue-50 to-indigo-100 p-6">
      <div className="w-full max-w-md bg-white rounded-xl shadow-lg p-6 space-y-6">
        <div className="flex justify-between items-center">
          <h1 className="text-3xl font-bold text-indigo-700">Omniston Swap</h1>
          <TonConnectButton />
        </div>

        <div className="h-px bg-gray-200 w-full my-4"></div>

Add the token selection dropdowns:

        {assets.length > 0 ? (
          <div className="space-y-6">
            {/* From */}
            <div className="flex flex-col">
              <label className="text-sm font-medium text-gray-600 mb-1">
                From
              </label>
              <select
                value={fromAsset?.contractAddress || ''}
                onChange={(e) => {
                  const selected = assets.find(a => a.contractAddress === e.target.value);
                  setFromAsset(selected);
                }}
                className="w-full p-3 bg-gray-50 border border-gray-200 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 transition-all"
              >
                {assets.map(asset => (
                  <option
                    key={asset.contractAddress}
                    value={asset.contractAddress}
                  >
                    {asset.meta?.symbol || asset.meta?.displayName || 'token'}
                  </option>
                ))}
              </select>
            </div>

            {/* To */}
            <div className="flex flex-col">
              <label className="text-sm font-medium text-gray-600 mb-1">
                To
              </label>
              <select
                value={toAsset?.contractAddress || ''}
                onChange={(e) => {
                  const selected = assets.find(a => a.contractAddress === e.target.value);
                  setToAsset(selected);
                }}
                className="w-full p-3 bg-gray-50 border border-gray-200 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 transition-all"
              >
                {assets.map(asset => (
                  <option
                    key={asset.contractAddress}
                    value={asset.contractAddress}
                  >
                    {asset.meta?.symbol || asset.meta?.displayName || 'token'}
                  </option>
                ))}
              </select>
            </div>

            {/* Amount */}
            <div className="flex flex-col">
              <label className="text-sm font-medium text-gray-600 mb-1">
                Amount
              </label>
              <input
                type="text"
                value={amount}
                onChange={(e) => setAmount(e.target.value)}
                placeholder="0.0"
                className="w-full p-3 bg-gray-50 border border-gray-200 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 transition-all"
              />
            </div>
          </div>

Add the loading state and close the component:

        ) : (
          <div className="flex justify-center items-center py-10">
            <div className="animate-pulse flex space-x-2">
              <div className="h-2 w-2 bg-indigo-500 rounded-full"></div>
              <div className="h-2 w-2 bg-indigo-500 rounded-full"></div>
              <div className="h-2 w-2 bg-indigo-500 rounded-full"></div>
            </div>
            <p className="ml-3 text-gray-600">Loading assets...</p>
          </div>
        )}
      </div>

      <div className="mt-6 text-center text-xs text-gray-500">
        Powered by Ston.fi
      </div>
    </div>
  );
}

export default App;

5. Requesting a Quote

We'll use the useRfq hook from @ston-fi/omniston-sdk-react to request a quote. It will fetch quotes automatically based on the parameters given.

Add additional imports to the top of the file:

import { useRfq, SettlementMethod, Blockchain, GaslessSettlement } from "@ston-fi/omniston-sdk-react";
import { fromNano } from "@ton/ton";

Add utility functions for converting token amounts:

// Convert floating point string amount into integer base units string
// Essential for blockchain transactions which use integer arithmetic
function toBaseUnits(amount: string, decimals?: number) {
  return Math.floor(parseFloat(amount) * 10 ** (decimals ?? 9)).toString();
}

// Convert integer base units back to a fixed 2-decimal string for display
function fromBaseUnits(baseUnits: string, decimals?: number) {
  return (parseInt(baseUnits) / 10 ** (decimals ?? 9)).toFixed(2);
}

Set up the useRfq hook to automatically fetch quotes:

function App() {
  ...
  const { data: quote, isLoading: quoteLoading, error: quoteError } = useRfq({
    settlementMethods: [SettlementMethod.SETTLEMENT_METHOD_SWAP],
    bidAssetAddress: fromAsset
      ? { blockchain: Blockchain.TON, address: fromAsset.contractAddress }
      : undefined,
    askAssetAddress: toAsset
      ? { blockchain: Blockchain.TON, address: toAsset.contractAddress }
      : undefined,
    amount: {
      bidUnits: fromAsset ? toBaseUnits(amount, fromAsset.meta?.decimals) : '0'
    },
    settlementParams: {
      gaslessSettlement: GaslessSettlement.GASLESS_SETTLEMENT_POSSIBLE,
      maxPriceSlippageBps: 500,
    },
  }, {
    enabled: !!fromAsset?.contractAddress && !!toAsset?.contractAddress && amount !== ''
  });

Add the quote display section to your tsx (insert after the amount input field):

            {/* Quote section */}
            <div className="pt-4">
              {quoteLoading && <p>Loading quote...</p>}
              {quoteError && <p className="text-red-500">Error: {String(quoteError)}</p>}
              {quote && 'quote' in quote && (
                <div className="p-4 bg-gray-50 rounded-lg border border-gray-200">
                  <p className="font-semibold text-gray-700">Quote Info</p>
                  <p className="text-sm text-gray-600">Resolver: {quote.quote.resolverName}</p>
                  <p className="text-sm text-gray-600">Bid Units: {fromBaseUnits(quote.quote.bidUnits, fromAsset?.meta?.decimals)}  {fromAsset?.meta?.symbol}</p>
                  <p className="text-sm text-gray-600">Ask Units: {fromBaseUnits(quote.quote.askUnits, toAsset?.meta?.decimals)} {toAsset?.meta?.symbol}</p>
                </div>
              )}
            </div>

Any time the user changes the token or amount, useRfq automatically refreshes the quote.


6. Building a Transaction and Sending It

Once we have a quote, we can build the transaction that will execute the swap. We'll use the useOmniston hook to access the Omniston instance and build the transaction.

Replace imports for @tonconnect/ui-react and @ston-fi/omniston-sdk-react with:

import { TonConnectButton, useTonAddress, useTonConnectUI } from "@tonconnect/ui-react";
import {
  useRfq,
  SettlementMethod,
  Blockchain,
  GaslessSettlement,
  useOmniston,
  type QuoteResponseEvent_QuoteUpdated,
} from "@ston-fi/omniston-sdk-react";

Add wallet connection hooks and omniston instance:

function App() {  
  // ... existing state variables ...
  const walletAddress = useTonAddress();
  const [tonConnect] = useTonConnectUI();
  const omniston = useOmniston();

Create the transaction building function:

  // ... after useRfq hook ...
  async function buildTx(willTradedQuote: QuoteResponseEvent_QuoteUpdated | undefined) {
    if (!willTradedQuote || !walletAddress) {
      alert("Please connect your wallet and ensure a valid quote is loaded.");
      return null;
    }

    try {
      const tx = await omniston.buildTransfer({
        quote: willTradedQuote.quote,
        sourceAddress: {
          blockchain: Blockchain.TON,
          address: walletAddress, // the wallet sending the offer token
        },
        destinationAddress: {
          blockchain: Blockchain.TON,
          address: walletAddress, // the same wallet receiving the ask token
        },
        gasExcessAddress: {
          blockchain: Blockchain.TON,
          address: walletAddress, // excess gas returns to sender
        },
        useRecommendedSlippage: false, // Use recommended slippage from the quote
      });

      return tx.ton?.messages || [];
    } catch (err) {
      console.error("Error building transaction:", err);
      alert("Failed to build transaction. Check console for details.");
      return null;
    }
  }

Add the swap execution function:

  async function handleSwap() {
    if (!quote || quote.type !== 'quoteUpdated') {
      alert("No valid quote available");
      return;
    }
    const willTradedQuote = quote;
    const messages = await buildTx(willTradedQuote);
    if (!messages) return;
    
    try {
      await tonConnect.sendTransaction({
        validUntil: Date.now() + 1000000,
        messages: messages.map((message) => ({
          address: message.targetAddress,
          amount: message.sendAmount,
          payload: message.payload,
        })),
      });
    } catch (err) {
      console.error("Error sending transaction:", err);
      alert("Failed to send transaction. Check console for details.");
    }
  }

Add the Execute Swap button (insert after in quote section):

        {quote && 'quote' in quote && (
        <>
          <div className="p-4 bg-gray-50 rounded-lg border border-gray-200">
            <p className="font-semibold text-gray-700">Quote Info</p>
            <p className="text-sm text-gray-600">Resolver: {quote.quote.resolverName}</p>
            <p className="text-sm text-gray-600">Bid Units: {fromBaseUnits(quote.quote.bidUnits, fromAsset?.meta?.decimals)}  {fromAsset?.meta?.symbol}</p>
            <p className="text-sm text-gray-600">Ask Units: {fromBaseUnits(quote.quote.askUnits, toAsset?.meta?.decimals)} {toAsset?.meta?.symbol}</p>
          </div>
          <button
            onClick={handleSwap}
            className="mt-4 w-full bg-indigo-500 hover:bg-indigo-600 text-white font-medium py-3 px-4 rounded-lg transition-all"
          >
            Execute Swap
          </button>
        </>
      )}

7. Tracking Your Trade

7.1 Install the TON Package

We'll track trades using the useTrackTrade hook from @ston-fi/omniston-sdk-react. First, ensure you have the @ton/ton package installed if you haven't already:

pnpm add @ton/ton

7.2 Using the useTrackTrade Hook

After you've built and sent the swap transaction, you can track its status with useTrackTrade. This hook takes the quoteId of the trade, your wallet address, and the outgoing transaction hash. It periodically checks the trade's on-chain status, letting you know if it's pending, settled, or partially filled.

Replace imports for @ton/ton and @ston-fi/omniston-sdk-react for trade tracking:

import {
  useRfq,
  SettlementMethod,
  Blockchain,
  GaslessSettlement,
  useOmniston,
  useTrackTrade,
  type QuoteResponseEvent_QuoteUpdated,
  type TradeStatus,
} from "@ston-fi/omniston-sdk-react";
import { TonClient, Address, Cell, beginCell, storeMessage } from "@ton/ton";

Add state variables for tracking:

function App() {
  // ... existing state variables ...
  const [outgoingTxHash, setOutgoingTxHash] = useState("");
  const [tradedQuote, setTradedQuote] = useState<QuoteResponseEvent_QuoteUpdated | null>(null);

Reset tracking state when inputs change:

  // Reset outgoingTxHash and tradedQuote when inputs change
  useEffect(() => {
    setTradedQuote(null);
    setOutgoingTxHash("");
  }, [fromAsset, toAsset, amount]);

Update the useRfq hook to stop fetching quotes during trade execution:

  const {
    data: quote,
    isLoading: quoteLoading,
    error: quoteError,
  } = useRfq({
    // ... existing useRfq configuration ...
  }, {
    enabled:
      !!fromAsset?.contractAddress &&
      !!toAsset?.contractAddress &&
      amount !== "" &&
      // add this to stop getting new quotes when we make a transaction
      !outgoingTxHash,
  });

Set up the trade tracking hook:

  const {
    isLoading: trackingLoading,
    error: trackingError,
    data: tradeStatus,
  } = useTrackTrade({
    quoteId: tradedQuote?.quote?.quoteId || '',
    traderWalletAddress: {
      blockchain: Blockchain.TON,
      address: walletAddress || '',
    },
    outgoingTxHash,
  }, {
    enabled: !!tradedQuote?.quote?.quoteId && !!walletAddress && !!outgoingTxHash,
  });

Add helper function to translate trade results:

  // Function to translate trade result to human-readable text
const getTradeResultText = (status: TradeStatus) => {
    if (!status?.status?.tradeSettled) return "";
    
    const result = status.status.tradeSettled.result;
    switch (result) {
      case "TRADE_RESULT_FULLY_FILLED":
        return "Trade completed successfully and fully filled";
      case "TRADE_RESULT_PARTIALLY_FILLED":
        return "Trade partially filled - something went wrong";
      case "TRADE_RESULT_ABORTED":
        return "Trade was aborted";
      case "TRADE_RESULT_UNKNOWN":
      case "UNRECOGNIZED":
      default:
        return "Unknown trade result";
    }
  };

Add utility functions for transaction hash extraction:

    // Utility function to retry an async operation
    const retry = async (fn: () => Promise<string>, { retries = 5, delay = 1000 }): Promise<string> => {
      try {
        return await fn();
      } catch (error) {
        if (retries === 0) throw error;
        await new Promise(resolve => setTimeout(resolve, delay));
        return retry(fn, { retries: retries - 1, delay });
      }
    };

    const getTxByBOC = async (exBoc: string, walletAddress: string): Promise<string> => {
      if (!exBoc || !walletAddress) {
        throw new Error('Missing required parameters for transaction tracking');
      }
  
      const client = new TonClient({
        endpoint: 'https://toncenter.com/api/v2/jsonRPC'
      });
  
      const myAddress = Address.parse(walletAddress);
  
      return retry(async () => {
        const transactions = await client.getTransactions(myAddress, {
          limit: 5,
        });
  
        for (const tx of transactions) {
          const inMsg = tx.inMessage;
          if (inMsg?.info.type === 'external-in') {
            const inBOC = inMsg?.body;
            if (typeof inBOC === 'undefined') {
              continue;
            }
  
            const extHash = Cell.fromBase64(exBoc).hash().toString('hex');
            const inHash = beginCell().store(storeMessage(inMsg)).endCell().hash().toString('hex');
  
            if (extHash === inHash) {
              return tx.hash().toString('hex');
            }
          }
        }
        throw new Error('Transaction not found');
      }, { retries: 30, delay: 1000 });
    }; 

Update the handleSwap function to capture transaction details:

 async function handleSwap() {
    if (!quote || quote.type !== 'quoteUpdated') {
      alert("No valid quote available");
      return;
    }
    const messages = await buildTx(quote);
    if (!messages) return;
    
    try {
      setTradedQuote(quote);

      const res = await tonConnect.sendTransaction({
        validUntil: Date.now() + 1000000,
        messages: messages.map((message) => ({
          address: message.targetAddress,
          amount: message.sendAmount,
          payload: message.payload,
        })),
      });

      const exBoc = res.boc;
      const txHash = await getTxByBOC(exBoc, walletAddress);
      setOutgoingTxHash(txHash);

    } catch (err) {
      setTradedQuote(null);
      console.error("Error sending transaction:", err);
      alert("Failed to send transaction. Check console for details.");
    }
  }

Add the trade status display (insert after the divider line):

        {/* Trade status */}
        {/* right after <div className="h-px bg-gray-200 w-full my-4"></div> */}
        <div className="mt-4 p-4 bg-gray-50 rounded-lg border border-gray-200">
          {trackingLoading && <p className="text-sm text-blue-600">Tracking trade...</p>}
          {trackingError && (
            <p className="text-sm text-orange-600">Trade tracking error: {String(trackingError)}</p>
          )}
          {tradeStatus?.status?.tradeSettled && (
            <p className="text-sm text-green-600">
              Trade Result: {getTradeResultText(tradeStatus)}
            </p>
          )}
        </div>

Update the error display in the quote section

        {quoteError && !outgoingTxHash && <p className="text-red-500">Error: {String(quoteError)}</p>}

8. Testing Your Swap

  1. Start the development server:

pnpm dev
  1. Open your app in the browser at http://localhost:5173.

  2. Connect your TON wallet via the "Connect Wallet" button.

  3. Select tokens from the dropdowns and enter an amount.

  4. Omniston automatically fetches and displays a quote. Confirm it's valid.

  5. Click "Execute Swap" to finalize the transaction. Approve it in your wallet.

  6. Once the swap completes on-chain, your wallet balances should update accordingly.


9. Conclusion

Congratulations! You've built a minimal React + Vite app with Tailwind CSS that:

  • Connects to a TON wallet using TonConnect.

  • Dynamically fetches available tokens from STON.fi.

  • Requests real-time quotes (RFQs) from Omniston automatically.

  • Builds and sends swap transactions.

Feel free to expand this demo with:

  • Custom slippage settings.

  • Better error-handling and success notifications.

  • Additional settlement methods or cross-chain logic.

  • Learn how to add referral fees to your Omniston swaps by reading the Referral Fees guide.

Happy building with Omniston!

10. Live Demo

With this Replit demo, you can:

  • Open the project directly in your browser

  • Fork the Replit to make your own copy

  • Run the application to see it in action

  • Explore and modify the code to learn how it works

  • Experiment with different features and UI changes

Alternatively, you can run this example locally by cloning the GitHub repository:

git clone https://github.com/mrruby/omniston-swap-app.git
cd omniston-swap-app
pnpm install
pnpm dev

This will start the development server and you can access the app at http://localhost:5173.

11. Advanced Example App

For those seeking a feature-rich, more advanced approach, we also have a Next.js Omniston Demo App that:

  • Uses Next.js for a scalable framework

  • Utilizes hooks and providers for an elegant architecture

  • Demonstrates better error handling, robust state management, and additional STON.fi and Omniston features

You can explore the code in our repository:

Omniston SDK Next.js Demo App

Or see it in action at our live demo:

Omniston Demo App

Pool

This section contains api reference of the pool contract

Off-chain get methods

get_pool_data

Returns the current state of the pool: Jetton token reserves, Jetton wallet addresses and fee parameters.

(int, int, slice, slice, int, int, int, slice, int, int) get_pool_data() method_id;

Arguments

None

Result

Returns current state of the Pool

Result structure

Key
Type
Index
Description

reserve0

coins

0

Amount of the first token (in basic token units)

reserve1

coins

1

Amount of the second token (in basic token units)

token0_wallet_address

address

2

Address of the first Jetton token

token1_wallet_address

address

3

Address of the second Jetton token

lp_fee

uint8

4

Liquidity pool fee value

protocol_fee

uint8

5

Protocol fee

ref_fee

uint8

6

Referrer fee

protocol_fee_address

address

7

Address for receiving protocol fees

collected_token0_protocol_fee

coins

8

Amount of collected protocol fees of the first token (in basic token units)

collected_token1_protocol_fee

coins

9

Amount of collected protocol fees of the second token (in basic token units)

Notes:

  • fee ratio is the value of fee divided by FEE_DIVIDER (10000); so a fee of 1% has a value of 100

get_expected_outputs

Estimate expected result of the amount of jettonWallet tokens swapped to the other type of tokens of the pool.

(int, int, int) get_expected_outputs(int amount, slice token_wallet) method_id;

Arguments

Name
Type
Description

amount

coins

Amount of tokens to swap (in basic token units)

token_wallet

address

Token Jetton address (must be equal to one of the Jetton addresses of the pool)

Result

Returns expected result of a token swap

Result structure

Key
Type
Index
Description

jetton_to_receive

coins

0

Amount of tokens received (in basic token units)

protocol_fee_paid

coins

1

Amount tokens paid for protocol fees (in basic token units)

ref_fee_paid

coins

2

Amount tokens paid for referrer fees (in basic token units)

get_expected_tokens

Estimate an expected amount of lp tokens minted when providing liquidity.

(int) get_expected_tokens(int amount0, int amount1) method_id;

Arguments

Key
Type
Description

amount0

coins

Amount of tokens for the first Jetton (in basic token units)

amount1

coins

Amount of tokens for the second Jetton (in basic token units)

Result

Returns an estimated amount of liquidity tokens to be minted

get_expected_liquidity

Estimate expected liquidity freed upon burning liquidity tokens.

(int, int) get_expected_liquidity(int jetton_amount) method_id;

Arguments

Key
Type
Description

jetton_amount

coins

Amount of liquidity tokens (in basic token units)

Result

Returns expected freed liquidity

Return structure

Key
Type
Index
Description

amount0

coins

0

Amount of tokens for the first Jetton (in basic token units)

amount1

coins

1

Amount of tokens for the second Jetton (in basic token units)

get_lp_account_address

Get the lp account address of a user

slice get_lp_account_address(slice owner_address) method_id;

Arguments

Key
Type
Description

owner_address

address

Address of a user

Result

Function the lp account address of a user

get_jetton_data

Standard jetton 'get' methods from TonWeb JettonMinter.

(int, int, slice, cell, cell) get_jetton_data() method_id;

Arguments

None

Result

Returns a structure with Jetton data

Return structure

Key
Type
Index
Description

total_supply

coins

0

Total token supply (in basic token units)

is_mintable

bool

1

If new tokens can be minted

admin_address

address

2

Admin address

jetton_content_uri

string

3

Offchain uri with Jetton data

jetton_wallet_code

cell

4

Code of the lp Jetton wallet

get_wallet_address

Get lp wallet address of a user.

slice get_wallet_address(slice owner_address) method_id;

Arguments

Name
Type
Description

owner_address

address

Address of a user

Result

Returns calculated lp wallet address of a user

On-chain queries

On-chain counterparts of getter methods.

Operations table

Name
Value
Description

getter_pool_data

0x43c034e6

Sends a message with the current state of the pool

getter_expected_outputs

0xed4d8b67

Sends a message with an estimated result of the token swap

getter_lp_account_address

0x9163a98a

Sends a message with the lp account address of a user

getter_expected_tokens

0x9ce632c5

Sends a message with an estimated amount of lp tokens minted when providing liquidity

getter_expected_liquidity

0x8751801f

Sends a message with estimated liquidity freed upon burning of liquidity tokens

provide_wallet_address

0x2c76b973

Sends a message with the lp wallet address of a user

getter_pool_data (0x43c034e6)

Sends a message with the current state of the pool. On-chain equivalent of get_pool_data.

TL-B

getter_pool_data#43c034e6 query_id:uint64 = InternalMsgBody;

Message body

None

Outgoing messages

Sends a message with current pool data to the sender_address

Response message body

Name
Type
Description

op

uint32

Operation code is equal to getter_pool_data

query_id

uint64

Query id

reserve0

coins

Amount of the first token (in basic token units)

reserve1

coins

Amount of the second token (in basic token units)

token0_address

address

Address of the first Jetton token

token1_address

address

Address of the second Jetton token

ref_fee_data

cell

Cell with fee data

ref_fee_data body

Name
Type
Description

lp_fee

uint8

Liquidity pool fee value

protocol_fee

uint8

Protocol fee

ref_fee

uint8

Referrer fee

protocol_fee_address

address

Address for receiving protocol fees

collected_token0_protocol_fee

coins

Amount of collected protocol fees of the first token (in basic token units)

collected_token1_protocol_fee

coins

Amount of collected protocol fees of the second token (in basic token units)

getter_expected_outputs (0xed4d8b67)

Sends a message with an estimated result of the token swap. On-chain equivalent of get_expected_outputs.

TL-B

getter_expected_outputs#ed4d8b67 query_id:uint64 amount:Grams token_wallet:MsgAddress = InternalMsgBody;

Message body

Name
Type
Description

amount

coins

Amount of tokens (in basic token units)

token_wallet

address

Token Jetton address

Outgoing messages

Sends a message with an estimated result of the token swap to sender_address

Response message body

Name
Type
Description

op

uint32

Operation code is equal to getter_expected_outputs

query_id

uint64

Query id

out

coins

Amount of tokens to be received (in basic token units)

protocol_fee_out

coins

Amount tokens paid for protocol fees (in basic token units)

ref_fee_out

coins

Amount tokens paid for referrer fees (in basic token units)

getter_lp_account_address (0x9163a98a)

Sends a message with the lp account address of a user. On-chain equivalent of get_lp_account_address.

TL-B

getter_lp_account_address#9163a98a query_id:uint64 user_address:MsgAddress = InternalMsgBody;

Message body

Name
Type
Description

user_address

address

Address of a user

Outgoing messages

Sends a message with the lp account address of a user to sender_address

Response message body

Name
Type
Description

op

uint32

Operation code is equal to getter_expected_outputs

query_id

uint64

Query id

lp_account_address

address

lp account address of a user

Notes:

  • the current version of the contract returns a message with getter_expected_outputs op code instead of getter_lp_account_address so the user must differentiate those messages with some other means

getter_expected_tokens (0x9ce632c5)

Sends a message with an estimated amount of lp tokens minted when providing liquidity. On-chain equivalent of get_expected_tokens

TL-B

getter_expected_tokens#9ce632c5 query_id:uint64 user_address:MsgAddress amount0:Grams amount1:Grams = InternalMsgBody;

Message body

Name
Type
Description

user_address

address

User address (unused)

amount0

coins

Amount of tokens for the first Jetton (in basic token units)

amount1

coins

Amount of tokens for the second Jetton (in basic token units)

Outgoing messages

Sends a message with an estimated amount of liquidity tokens to be minted to sender_address

Response message body

Name
Type
Description

op

uint32

Operation code is equal to getter_expected_tokens

query_id

uint64

Query id

liquidity

coins

Expected amount of liquidity tokens (in basic token units)

getter_expected_liquidity (0x8751801f)

Sends a message with estimated liquidity freed upon burning liquidity tokens. On-chain equivalent of get_expected_liquidity.

TL-B

getter_expected_liquidity#8751801f query_id:uint64 jetton_amount:Grams = InternalMsgBody;

Message body

Name
Type
Description

jetton_amount

coins

Amount of liquidity tokens to burn (in basic token units)

Outgoing messages

Sends a message with estimated liquidity to sender_address

Response message body

Name
Type
Description

op

uint32

Operation code is equal to getter_expected_tokens

query_id

uint64

Query id

amount0_out

coins

Amount of tokens for the first Jetton (in basic token units)

amount1_out

coins

Amount of tokens for the second Jetton (in basic token units)

provide_wallet_address (0x2c76b973)

Sends a message with the lp wallet address of a user. On-chain equivalent of get_wallet_address.

TL-B

provide_wallet_address#2c76b973 query_id:uint64 owner_address:MsgAddress include_address?:Bool = InternalMsgBody;

Message body

Name
Type
Description

owner_address

address

Address of a user

include_address?

uint1

Include user address in the response message

Outgoing messages

Sends a message back to sender with the calculated lp wallet address of a user

Response message body

Name
Type
Description

op

uint32

Operation code is equal to take_wallet_address (0xd1735400)

query_id

uint64

Query id

lp_wallet_address

address

Calculated lp wallet address

ref_address

cell | null

Cell with data if include_address? evaluates to true

ref_address body

Name
Type
Description

included_address

address

Address of a user

Jetton handlers

Handles operations sent from a Jetton wallet.

Operations table

Name
Value
Description

burn_notification

0x7bdd97de

Sent by LP wallet after burning LP jettons to release liquidity

burn_notification (0x7bdd97de)

Sent by LP wallet after burning LP jettons to release liquidity.

TL-B

burn_notification#7bdd97de query_id:uint64 jetton_amount:Grams from_address:MsgAddress response_address:MsgAddress = InternalMsgBody;

Message body

Name
Type
Description

jetton_amount

coins

Amount of liquidity tokens to burn (in basic token units)

from_address

address

User address

response_address

address

Address for a response message

Outgoing messages

Sends a message with op excesses (0xd53276db) to response_address

Sends a message with a released amount of both tokens to be received by a user as a result of the burn operation to the router, which initiates pay_to operation to from_address

Router internal message handlers

Handles messages from the router.

Operations table

Name
Value
Description

swap

0x25938561

Swap tokens

provide_lp

0xfcf9e58f

Provide liquidity

reset_gas

0x42a0fb43

Reset gas

collect_fees

0x1fcb7d3d

Collect fees

set_fees

0x355423e5

Set new fee parameters

swap (0x25938561)

Swap tokens. This message is received from the router when the user initiates a token swap.

TL-B

ref_bodycell$_ from_real_user:MsgAddress ref_address:MsgAddress = RefBodyCell;
swap#25938561 query_id:uint64 from_user:MsgAddress token_wallet:MsgAddress amount:Grams min_out:Grams ref_bodycell:(Either RefBodyCell ^RefBodyCell) = InternalMsgBody;

Message body

Name
Type
Description

from_user_address

address

User address

token_wallet

address

Incoming Jetton wallet address

amount

coins

Amount of incoming tokens (in basic token units)

min_out

coins

Minimum amount of received tokens (in basic token units)

has_ref

uint1

If referral is present

ref_bodycell

cell

Cell with data

Notes:

  • swap will fail if a user should receive less than min_out of tokens as a result

ref_bodycell body

Name
Type
Description

from_real_user

address

Who initialized the swap

ref_address

address

Referral address; present only if has_ref is 1

Outgoing messages

Sends a message with an amount of the other tokens to be received by a user as a result of the swap to the router, which initiates pay_to operation

Additionally may send a message with referrer fees to the router, which initiates pay_to operation to ref_address

provide_lp (0xfcf9e58f)

Provide liquidity for the pool. A user must submit an amount of both tokens to receive lp tokens and add new liquidity to a pool. This message is routed to liquidity pool account with add_liquidity operation code.

TL-B

provide_lp#fcf9e58f query_id:uint64 owner_addr:MsgAddress min_lp_out:Grams amount0:Grams amount1:Grams = InternalMsgBody;

Message body

Name
Type
Description

owner_addr

address

User address

min_lp_out

coins

Minimum amount of received liquidity tokens (in basic token units)

amount0

coins

Amount of the first Jetton token (in basic token units)

amount1

coins

Amount of the second Jetton token (in basic token units)

Outgoing messages

Sends a message to liquidity pool account with add_liquidity operation code.

reset_gas (0x42a0fb43)

Updates the amount of $TON (in nanoTons) on the pool to REQUIRED_TON_RESERVE (10000000) of the pool. The remaining amount of $TON will be sent back to the router.

TL-B

reset_gas#42a0fb43 query_id:uint64 = InternalMsgBody;

Message body

None

Outgoing messages

Sends an empty message back to the router with the remaining $TON

collect_fees (0x1fcb7d3d)

Collect protocol fees which will be sent to protocol_fee_address. A user which initiates this operation receives a bounty equal to 1/1000 of collected amount of both tokens.

TL-B

collect_fees#1fcb7d3d query_id:uint64 = InternalMsgBody;

Message body

None

Outgoing messages

Sends a message with collected fees (minus the bounty) to protocol_fee_address

Sends a message with an amount of tokens to be received by a user as a bounty (equal to collected fees divided by 1000) to the router, which initiates pay_to operation

set_fees (0x355423e5)

Set new fee values including liquidity pool fees, protocol fees and referrer fees as well as an address for receiving collected protocol fees.

TL-B

set_fees#355423e5 query_id:uint64 new_lp_fee:uint8 new_protocol_fee:uint8 new_ref_fee:uint8 new_protocol_fee_address:MsgAddress = InternalMsgBody;

Message body

Name
Type
Description

new_lp_fee

uint8

New liquidity pool fee ratio (multiplied by FEE_DIVIDER)

new_protocol_fee

uint8

New protocol fee ratio (multiplied by FEE_DIVIDER)

new_ref_fee

uint8

New referrer fee ratio (multiplied by FEE_DIVIDER)

new_protocol_fee_address

address

Address for receiving protocol fees

Notes:

  • fee ratio is the value of fee divided by FEE_DIVIDER (10000); so to set a fee to 1% the value must be 100

  • fees must be between MIN_FEE (0) and MAX_FEE (100)

Outgoing messages

None


LP Account internal message handlers

Handles messages from an lp account.

Operations table

Name
Value
Description

cb_add_liquidity

0x56dfeb8a

Sent by user's lp_account after adding liquidity

cb_refund_me

0x89446a42

Sent by user's lp_account after adding liquidity

cb_add_liquidity (0x56dfeb8a)

Add new liquidity to the pool. Sent by user's lp account after an appropriate amount (greater than 1000) of both Jetton tokens is sent by a user. The resulting added liquidity must be greater than min_lp_out for the operation to be successful.

TL-B

cb_add_liquidity#56dfeb8a query_id:uint64 tot_am0:Grams tot_am1:Grams user_address:MsgAddress min_lp_out:Grams = InternalMsgBody;

Message body

Name
Type
Description

tot_am0

coins

Amount of the first Jetton token (in basic token units)

tot_am1

coins

Amount of the second Jetton token (in basic token units)

user_address

address

User address

min_lp_out

coins

Minimum amount of received liquidity tokens (in basic token units)

Notes:

  • addition of liquidity will fail if a user should receive less than min_lp_out of lp tokens as a result

  • cannot add liquidity if a supply of either tokens becomes greater than MAX_COINS (2^120 - 1)

Outgoing messages

Sends a message with internal_transfer (0x178d4519) op code to the lp wallet of user_address with minted liquidity tokens; on initial liquidity addition tokens are minted to addr_none

cb_refund_me (0x89446a42)

Sent by user's lp account after a user initiates refund_me operation to cancel addition of new liquidity. The amount of previously stored tokens will be sent back to the user.

TL-B

cb_refund_me#89446a42 query_id:uint64 tot_am0:Grams tot_am1:Grams user_address:MsgAddress = InternalMsgBody;

Message body

Name
Type
Description

tot_am0

coins

Amount of the first Jetton token (in basic token units)

tot_am1

coins

Amount of the second Jetton token (in basic token units)

user_address

address

User address

Outgoing messages

Sends a message with tot_am0 of the first token and tot_am1 of the second token to the router, which initiates pay_to operation


User message handlers

Handles direct messages from a user.

Operations table

Name
Value
Description

collect_fees

0x1fcb7d3d

Called by anyone; collect fees; the caller receives a bounty

collect_fees (0x1fcb7d3d)

Collect protocol fees which will be sent to protocol_fee_address. A user which initiates this operation receives a bounty equal to 1/1000 of collected amount of both tokens.

TL-B

collect_fees#1fcb7d3d query_id:uint64 = InternalMsgBody;

Message body

None

Outgoing messages

Sends a message with collected fees (minus the bounty) to protocol_fee_address

Sends a message with an amount of tokens to be received by a user as a bounty (equal to collected fees divided by 1000) to the router, which initiates pay_to operation


Constants

Name
Value
Description

WORKCHAIN

0

Workchain id

REQUIRED_TON_RESERVE

10000000

Amount of $TON (in nanoTons) to be left on the pool contract as gas

REQUIRED_MIN_LIQUIDITY

1000

Minimum amount of liquidity required

REQUIRED_MIN_COLLECT_FEES

1000000

Minimum amount of tokens (in basic token units) collected as fees

MAX_COINS

2^120 - 1

Maximum amount of tokens (in basic token units) stored as liquidity

PROVIDE_ADD_GAS_CONSUMPTION

10000000

Additional gas (in nanoTons) for providing a wallet

FEE_DIVIDER

10000

Fee values are divided by this value

MIN_FEE

0

Minimum fee allowed

MAX_FEE

100

Maximum fee allowed


Pool

This section contains api reference of the pool contract

Off-chain get methods

get_pool_data

Returns the current state of the Pool

_ get_pool_data() method_id;

Arguments

None

Result (common)

Returns PoolData structure containing current state of the pool. Typed pools may return additional arguments if present

PoolData structure

Key
Type
Index
Description

is_locked

bool

0

true if transfer_notification operations are locked (swap, provide_lp)

router_address

address

1

Address of the Router

total_supply

coins

2

Total supply of lp tokens

reserve0

coins

3

Amount of the first token (in basic token units)

reserve1

coins

4

Amount of the second token (in basic token units)

token0_wallet_address

address

5

Address of the first Jetton token

token1_wallet_address

address

6

Address of the second Jetton token

lp_fee

uint16

7

Liquidity pool fee value

protocol_fee

uint16

8

Protocol fee

protocol_fee_address

address

9

Address for receiving protocol fees

collected_token0_protocol_fee

coins

10

Amount of collected protocol fees of the first token (in basic token units)

collected_token1_protocol_fee

coins

11

Amount of collected protocol fees of the second token (in basic token units)

Notes:

  • fee ratio is the value of fee divided by params::fee_divider (10000); so a fee of 1% has a value of 100

Result (stableswap)

Additional data in PoolData structure specific to stableswap pools

PoolData structure additional params

Key
Type
Index
Description

amp

uint32

12

Stableswap amplification parameter

Result (weighted_stableswap)

Additional data in PoolData structure specific to weighted stableswap pools

PoolData structure additional params

Key
Type
Index
Description

amp

uint128

12

Weighted stableswap amplification parameter

rate

uint128

13

Stable token ratio parameter

w0

uint128

14

Token 0 weight parameter

rate_setter

address

15

Address with rights to change rate

Result (weighted_const_product)

Additional data in PoolData structure specific to weighted constant product pools

PoolData structure additional params

Key
Type
Index
Description

w0

uint128

12

Token 0 weight parameter

get_lp_account_address

Get the lp account address of a user

slice get_lp_account_address(slice owner_address) method_id;

Arguments

Key
Type
Description

owner_address

address

Address of a user

Result

Returns the lp account address of a user

get_jetton_data

Standard jetton 'get' methods from TonWeb JettonMinter.

(int, int, slice, cell, cell) get_jetton_data() method_id;

Arguments

None

Result

Returns a structure with Jetton data

JettonData structure

Key
Type
Index
Description

total_supply

coins

0

Total token supply of lp tokens (in basic token units)

mintable

bool

1

always true

admin_address

address

2

Router address

jetton_content_uri

string

3

Offchain uri with Jetton data

jetton_wallet_code

cell

4

Code of the lp Jetton wallet

get_wallet_address

Get lp wallet address of a user.

slice get_wallet_address(slice owner_address) method_id;

Arguments

Name
Type
Description

owner_address

address

Address of a user

Result

Returns a calculated lp wallet address of a user

get_pool_type

Get Pool type, equals to dex_type param in get_router_data

_ get_pool_type() method_id;

Arguments

None

Result

Return pool type of this pool as string:

  • constant_product

  • stableswap

  • weighted_stableswap

  • weighted_const_product

On-chain queries

On-chain counterparts of getter methods.

Operations table

Name
Value
Description

getter_pool_data

0x26df39fc

Sends a message with the current state of the pool

getter_lp_account_address

0x15fbca95

Sends a message with the lp account address of a user

provide_wallet_address

0x2c76b973

Sends a message with the lp wallet address of a user

getter_pool_data (0x26df39fc)

Sends a message with the current state of the pool. On-chain equivalent of get_pool_data.

TL-B

getter_pool_data#26df39fc query_id:uint64 = InternalMsgBody;

Message body

None

Outgoing messages

Sends a message with current pool data to the sender_address

Response message body

Name
Type
Description

op

uint32

Operation code is equal to getter_pool_data

query_id

uint64

Query id

is_locked

uint1

If this Pool is locked

reserve0

coins

Amount of the first token (in basic token units)

reserve1

coins

Amount of the second token (in basic token units)

token0_address

address

Address of the first Jetton token

token1_address

address

Address of the second Jetton token

additional_data

cell

Cell with additional data

additional_data body

Name
Type
Description

lp_fee

uint16

Liquidity pool fee value

protocol_fee

uint16

Protocol fee

router_address

address

Address of the Router

protocol_fee_address

address

Address for receiving protocol fees

total_supply

coins

Total amount of minted lp tokens

collected_token0_protocol_fee

coins

Amount of collected protocol fees of the first token (in basic token units)

collected_token1_protocol_fee

coins

Amount of collected protocol fees of the second token (in basic token units)

getter_lp_account_address (0x15fbca95)

Sends a message with the lp account address of a user. On-chain equivalent of get_lp_account_address.

TL-B

getter_lp_account_address#15fbca95 query_id:uint64 user_address:MsgAddress = InternalMsgBody;

Message body

Name
Type
Description

user_address

address

Address of a user

Outgoing messages

Sends a message with the lp account address of a user to sender_address

Response message body

Name
Type
Description

op

uint32

Operation code is equal to getter_expected_outputs

query_id

uint64

Query id

lp_account_address

address

lp account address of a user

provide_wallet_address (0x2c76b973)

Sends a message with the lp wallet address of a user. On-chain equivalent of get_wallet_address.

TL-B

provide_wallet_address#2c76b973 query_id:uint64 owner_address:MsgAddress include_address?:Bool = InternalMsgBody;

Message body

Name
Type
Description

owner_address

address

Address of a user

include_address?

uint1

Include user address in the response message

Outgoing messages

Sends a message back to sender with the calculated lp wallet address of a user

Response message body

Name
Type
Description

op

uint32

Operation code is equal to take_wallet_address (0xd1735400)

query_id

uint64

Query id

lp_wallet_address

address

Calculated lp wallet address

additional_data

maybe_ref

Cell with data if include_address? evaluates to true

additional_data body

Name
Type
Description

included_address

address

Address of a user

Jetton handlers

Handles operations sent from a Jetton wallet.

Operations table

Name
Value
Description

burn_notification_ext

0x297437cf

Sent by LP wallet after burning LP jettons to release liquidity

burn_notification_ext (0x297437cf)

Sent by LpWallet after burning LP jettons to release liquidity.

TL-B

burn_notification_ext#297437cf query_id:uint64 jetton_amount:Coins from_address:MsgAddress response_address:MsgAddress maybe_custom_payload:(Maybe ^Cell) = InternalMsgBody;

Message body

Name
Type
Description

jetton_amount

coins

Amount of liquidity tokens to burn (in basic token units)

from_address

address

User address

response_address

address

Address for a response message

custom_payloads

maybe_ref

Payloads for token0 and token1

custom_payloads

Name
Type
Description

payload_1

maybe_ref

Payload used for amount0; can be cross_swap

payload_2

maybe_ref

Payload used for amount1; can be cross_swap

Outgoing messages

Sends a message with op excesses (0xd53276db) to response_address

Sends a message with a released amount of both tokens to be received by a user as a result of the burn operation to the router, which initiates pay_to operation to from_address

Router internal message handlers

Handles messages from the router.

Operations table

Name
Value
Description

swap

0x6664de2a

Swap tokens

provide_lp

0x37c096df

Provide liquidity

reset_gas

0x29d22935

Reset gas

set_fees

0x58274069

Set new fee parameters

swap (0x6664de2a)

Swap tokens. This message is received from the router when the user initiates a token swap.

TL-B

swap#6664de2a query_id:uint64 from_user:MsgAddress left_amount:Coins right_amount:Coins dex_payload:^[transferred_op:uint32 token_wallet1:MsgAddress refund_address:MsgAddress excesses_address:MsgAddress tx_deadline:uint64 swap_body:^[min_out:Coins receiver:MsgAddress fwd_gas:Coins custom_payload:(Maybe ^Cell) refund_fwd_gas:Coins refund_payload:(Maybe ^Cell) ref_fee:uint16 ref_address:MsgAddress]] = InternalMsgBody;

Message body

Name
Type
Description

from_user

address

User address

amount0

coins

Amount of incoming first token

amount1

coins

Amount of incoming second token

payload

cell

Cell with dex payload

payload body

Name
Type
Description

op

uint32

Swap op

other_token_wallet

address

Address of the other Routertoken wallet

refund_address

address

Address where refund will be sent if swap fails

excesses_address

address

Address where TON excesses will be sent

additional_data

cell

Cell with additional data

additional_data body

Name
Type
Description

min_out

coins

Minimum required amount of tokens to receive

receiver_address

address

Address where tokens will be sent after swap

fwd_gas

coins

Gas used to forward a message in transfer_notification after swap if custom_payload is present

custom_payload

maybe_ref

Payload sent in transfer_notification after swap

refund_fwd_gas

coins

Gas used to forward a message in transfer_notification if swap fails if refund_payload is present

refund_payload

maybe_ref

Payload sent in transfer_notification if swap fails

ref_fee

uint16

Referral fee

referral_address

address

Referral address

Notes:

  • swap will fail if a user should receive less than min_out of tokens as a result

  • max allowed value of ref_fee is 100 (1%)

Outgoing messages

Sends a message with an amount of the other tokens to be received by a user as a result of the swap to the router, which initiates pay_to operation

Additionally may send a message with referrer fees to the router, which initiates pay_vault operation to Vault of referral_address

provide_lp (0x37c096df)

Provide liquidity for the pool. A user must submit an amount of both tokens to receive lp tokens and add new liquidity to a pool. This message is routed to liquidity pool account with add_liquidity operation code.

TL-B

provide_lp#37c096df query_id:uint64 from_user:MsgAddress left_amount:Coins right_amount:Coins dex_payload:^[transferred_op:uint32 token_wallet1:MsgAddress refund_address:MsgAddress excesses_address:MsgAddress tx_deadline:uint64 provide_lp_body:^[min_lp_out:Coins to_address:MsgAddress both_positive:uint1 fwd_amount:Coins custom_payload:(Maybe ^Cell)]] = InternalMsgBody;

Message body

Name
Type
Description

from_user

address

User address

amount0

coins

Amount of incoming first token

amount1

coins

Amount of incoming second token

payload

cell

Cell with dex payload

payload body

Name
Type
Description

op

uint32

Provide lp op

other_token_wallet

address

Address of the other Router token wallet

refund_address

address

Address where refund will be sent if swap fails

excesses_address

address

Address where TON excesses will be sent

additional_data

cell

Cell with additional data

additional_data body

Name
Type
Description

min_lp_out

coins

Minimum required amount of lp tokens to receive

receiver_address

address

Address where lp tokens will be sent

both_positive

uint1

Trigger liquidity deposit only if both token amounts are non-zero

fwd_gas

coins

Gas used to forward a message in transfer_notification after mint if custom_payload is present

custom_payload

maybe_ref

Payload sent in transfer_notification after lp mint

Outgoing messages

Sends a message to liquidity pool account with add_liquidity operation code.

reset_gas (0x29d22935)

Updates the amount of TON (in nanoTons) on the pool to storage_fee::pool (10000000) of the pool. The remaining amount of TON will be sent to excesses_address.

TL-B

reset_gas#29d22935 query_id:uint64 = InternalMsgBody;

Message body

Name
Type
Description

excesses_address

address

Address where excess ton will be sent

Outgoing messages

Sends a message to excesses_address with the remaining TON

internal_set_fees (0x75930d63)

Set new fee values including liquidity pool fees, protocol fees and referral fees as well as an address for receiving collected protocol fees.

TL-B

internal_set_fees#75930d63 query_id:uint64 new_lp_fee:uint16 new_protocol_fee:uint16 new_protocol_fee_address:MsgAddress excesses_address:MsgAddress = InternalMsgBody;

Message body

Name
Type
Description

new_lp_fee

uint16

New liquidity pool fee ratio (multiplied by params::fee_divider)

new_protocol_fee

uint16

New protocol fee ratio (multiplied by params::fee_divider)

new_protocol_fee_address

address

Address for receiving protocol fees

excesses_address

address

Address where TON excesses will be sent

Notes:

  • fee ratio is the value of fee divided by params::fee_divider (10000); so to set a fee to 1% the value must be 100

  • fees must be between params::min_fee (0) and params::max_fee (100)

Outgoing messages

Sends a message to excesses_address with the remaining TON


Protocol address internal message handlers

Handles messages from the protocol_fee_address.

Operations table

Name
Value
Description

collect_fees

0x1ee4911e

Collect fees

collect_fees (0x1ee4911e)

Collect protocol fees. The amount of fees in both tokens will be sent to protocol_fee_address address.

TL-B

collect_fees#1ee4911e query_id:uint64 maybe_payload0:(Maybe ^Cell) maybe_payload1:(Maybe ^Cell) = InternalMsgBody;

Message body

Name
Type
Description

payload_0

maybe_ref

Payload used for amount0; can be cross_swap

payload_1

maybe_ref

Payload used for amount1; can be cross_swap

Outgoing messages

Sends a message with collected fees in both tokens to the router, which initiates pay_to operation to protocol_fee_address.

LP Account internal message handlers

Handles messages from an lp account.

Operations table

Name
Value
Description

cb_add_liquidity

0x06ecd527

Sent by user's lp_account after adding liquidity

cb_refund_me

0x0f98e2b8

Sent by user's lp_account after adding liquidity

cb_add_liquidity (0x06ecd527)

Add new liquidity to the pool. Sent by user's lp account after both or one amounts tokens is sent by a user. The resulting added liquidity must be greater than min_lp_out for the operation to be successful.

TL-B

cb_add_liquidity#6ecd527 query_id:uint64 tot_am0:Coins tot_am1:Coins user_address:MsgAddress min_lp_out:Coins fwd_amount:Coins custom_payload_cs:(Maybe ^Cell) additional_fields:^[to_user_address:MsgAddress refund_address:MsgAddress excess_address:MsgAddress] = InternalMsgBody;

Message body

Name
Type
Description

amount0

coins

Amount of the first Jetton tokens added (in basic token units)

amount1

coins

Amount of the second Jetton tokens added (in basic token units)

user_address

address

Owner's address

min_lp_out

coins

Minimum amount of received liquidity tokens (in basic token units)

fwd_amount

coins

Forward amount used to send custom_payload (if present) in transfer_notification

custom_payload

maybe_ref

Payload sent in transfer_notification upon receiving tokens

additional_data

ref

See table below

additional_data

Name
Type
Description

to_address

address

Owner of new liquidity tokens

refund_address

address

Address of the owner of LpAccount where tokens will be refunded if liquidity mint wasn't successful

excess_address

address

Address where all TON excesses will be sent

Notes:

  • addition of liquidity will fail if a user should receive less than min_lp_out of lp tokens as a result

  • cannot add liquidity if a supply of either tokens becomes greater than MAX_COINS (2^120 - 1)

Outgoing messages

Sends a message with internal_transfer (0x178d4519) op code to the lp wallet of to_address with minted liquidity tokens

cb_refund_me (0x0f98e2b8)

Sent by user's lp account after a user initiates refund_me operation to cancel addition of new liquidity. The amount of previously stored tokens will be sent back to the user.

TL-B

cb_refund_me#f98e2b8 query_id:uint64 tot_am0:Coins tot_am1:Coins user_address:MsgAddress left_maybe_payload:(Maybe ^Cell) right_maybe_payload:(Maybe ^Cell) = InternalMsgBody;

Message body

Name
Type
Description

amount0

coins

Amount of the first Jetton tokens (in basic token units)

amount1

coins

Amount of the second Jetton tokens (in basic token units)

user_address

address

Owner's address

payload_0

maybe_ref

Payload used for amount0; can be cross_swap

payload_1

maybe_ref

Payload used for amount1; can be cross_swap

Outgoing messages

Sends a message with amount0 of the first token and amount1 of the second token to the router, which initiates pay_to operation


Constants

Name
Value
Description

storage_fee::pool

10000000

Amount of TON (in nanoTons) to be left on the pool contract as gas

params::required_min_liquidity

1001

Minimum amount of liquidity required

MAX_COINS

2^120 - 1

Maximum amount of tokens (in basic token units) stored as liquidity

gas::pool::provide_wallet_address

20000000

Additional gas (in nanoTons) for providing a wallet

params::fee_divider

10000

Fee values are divided by this value

params::min_fee

0

Minimum fee allowed (0%)

params::max_fee

100

Maximum fee allowed (1%)

Router

This section contains api reference of the router contract

Off-chain get methods

get_vault_address

Returns an address of a vault for a specified asset and user.

(slice) get_vault_address(slice user, slice token_wallet) method_id;

Arguments

Key
Type
Index
Description

user

address

0

The address of the Vault's owner

token

address

1

The address of the Router's jetton wallet

Result

Returns VaultV2 object for Vault's' address.

get_pool_address

Returns an address of a pool for a specified pair of assets.

It's necessary to specify addresses of Jetton wallets of the Router as the arguments of this method. These addresses can be retrieved with get_wallet_address of the Jetton minter.

(slice) get_pool_address(slice token_wallet0, slice token_wallet1) method_id;

Arguments

Key
Type
Index
Description

token0

address

0

The address of the Router's wallet of first Jetton

token1

address

1

The address of the Router's wallet of second Jetton

Result

Returns Pool address.

get_router_data

Returns the current state of the Router

(int, slice, int, slice, cell, cell, cell, cell, cell) get_router_data() method_id;

Arguments

None

Result

Returns RouterData structure containing current state of the Router.

RouterData structure

Key
Type
Index
Description

id

uint32

0

Router id

dex_type

string

1

Router type

is_locked

bool

2

true if transfer_notification operations are locked (swap, provide_lp)

admin_address

address

3

Address of contract's admin account

temp_upgrade

cell

4

A structure describing state of contract's code & admin upgrade; zero values indicate that no upgrade is pending

pool_code

cell

5

Code of the Router's liquidity pool contract

jetton_lp_wallet_code

cell

6

Code of lp wallet contract

lp_account_code

cell

7

Code of lp account contract

vault_code

cell

8

Code of lp account contract

Notes:

  • possible dex_type values:

    • constant_product

    • stableswap

    • weighted_stableswap

    • weighted_const_product

get_router_version

Returns the current Router version

(int, int, slice) get_router_version() method_id;

Arguments

None

Result

Returns VersionData structure containing Router version.

RouterData structure

Key
Type
Index
Description

major

uint

0

Major version

minor

uint

1

Minor version

development

string

2

Contains additional version info


transfer_notification operations

transfer_notification (0x7362d09c)

Messages received from Jetton wallets belonging to the Router upon getting a token transfer from a user. The user must include a custom payload with additional op code (either swap or provide_lp) and appropriate data in order for those messages to be processed correctly.

TL-B

transfer_notification#7362d09c query_id:uint64 jetton_amount:Grams from_user:MsgAddress forward_payload:^DexPayload = InternalMsgBody;

Message body

Name
Type
Description

op

uint32

Operation code; equal to transfer_notification

query_id

uint64

Query id

jetton_amount

coins

Jetton amount (in basic token units)

from_user

address

User address

payload

cell

Cell with data

payload body

Name
Type
Description

transferred_op

uint32

Additional operation code

token_wallet1

address

The address of the Router's wallet of second Jetton

Notes:

  • payload contains other fields depending on which op code is called

Transfer operations table

Name
Value
Description

swap

0x6664de2a

Swap one type of Jetton tokens for another

provide_lp

0x37c096df

Provide liquidity; route call to the correct pool

swap (0x6664de2a)

Swap tokens. This message is received when the Router's Jetton token wallet sends a message upon confirmation of token transfer from a user with a custom payload. The Router then sends a swap message to the appropriate pool contract address.

Check examples for in-depth message flow.

TL-B

swap#6664de2a token_wallet1:MsgAddress refund_address:MsgAddress excesses_address:MsgAddress tx_deadline:uint64 cross_swap_body:^[min_out:Coins receiver:MsgAddress fwd_gas:Coins custom_payload:(Maybe ^Cell) refund_fwd_gas:Coins refund_payload:(Maybe ^Cell) ref_fee:uint16 ref_address:MsgAddress] = DexPayload;

payload body

Name
Type
Description

op

uint32

Swap op

other_token_wallet

address

Address of the other Router token wallet

refund_address

address

Address where refund will be sent if swap fails

excesses_address

address

Address where TON excesses will be sent

deadline

uint64

Timestamp of execution deadline for this tx

additional_data

cell

Cell with additional data

additional_data body

Name
Type
Description

min_out

coins

Minimum required amount of tokens to receive

receiver_address

address

Address where tokens will be sent after swap

fwd_gas

coins

Gas used to forward a message in transfer_notification after swap if custom_payload is present

custom_payload

maybe_ref

Payload sent in transfer_notification after swap

refund_fwd_gas

coins

Gas used to forward a message in transfer_notification if swap fails if refund_payload is present

refund_payload

maybe_ref

Payload sent in transfer_notification if swap fails

ref_fee

uint16

Referrer fee

referral_address

address

Referrer address

Notes:

  • swap will fail if a user should receive less than min_out of tokens as a result

  • max allowed value of ref_fee is 100 (1%)

Outgoing messages

Sends a message with op swap to Pool

provide_lp (0x37c096df)

Provide liquidity for a pool. This message is received when Router's token wallet sends a message upon confirmation of token transfer from a user with a custom payload. The Router then sends a provide_lp message to the appropriate pool contract address.

Check examples for in-depth message flow.

TL-B

provide_lp#37c096df token_wallet1:MsgAddress refund_address:MsgAddress excesses_address:MsgAddress tx_deadline:uint64 cross_provide_lp_body:^[min_lp_out:Coins to_address:MsgAddress both_positive:uint1 fwd_amount:Coins custom_payload:(Maybe ^Cell)] = DexPayload;

payload body

Name
Type
Description

op

uint32

Provide lp op

other_token_wallet

address

Address of the other Router token wallet

refund_address

address

Address where refund will be sent if swap fails

excesses_address

address

Address where TON excesses will be sent

deadline

uint64

Timestamp of execution deadline for this tx

additional_data

cell

Cell with additional data

additional_data body

Name
Type
Description

min_lp_out

coins

Minimum required amount of lp tokens to receive

receiver_address

address

Address where lp tokens will be sent

both_positive

uint1

Trigger liquidity deposit only if both token amounts are non-zero

fwd_gas

coins

Gas used to forward a message in transfer_notification after mint if custom_payload is present

custom_payload

maybe_ref

Payload sent in transfer_notification after lp mint

Outgoing messages

Sends a message with op provide_lp to a liquidity pool


On-chain queries

On-chain counterparts of getter methods

Operations table

Name
Value
Description

getter_pool_address

0x2993ade0

Sends a message with a pool address for a requested token pair; counterpart to get_pool_address

getter_pool_address (0x2993ade0)

Sends a message with an address of a pool for a specified pair of assets; counterpart to get_pool_address

TL-B

getter_pool_address#2993ade0 query_id:uint64 token0:MsgAddress token1:MsgAddress = InternalMsgBody;

Message Body

Name
Type
Description

token0

address

The address of the Router's wallet of first Jetton

token1

address

The address of the Router's wallet of second Jetton

Outgoing messages

Sends a message back to the sender with the pool address

Response message body

Name
Type
Description

op

uint32

Operation code equal to getter_pool_address

query_id

uint64

Query id

pool_address

address

Address of a pool


On-chain admin operations

Handles governance message from admin to change pool parameters.

The admin can lock/unlock trading on all pools, change fees on a certain pool, upgrade the Router code, etc.

Common operations table

Name
Value
Description

set_fees

0x58274069

Change fee parameters of a pool

reset_pool_gas

0x66d0dff2

Update the amount of TON (in nanoTons) on a pool to storage_fee::router of a pool

update_pool_status

0x2af4607c

Change Pool status

update_status

0x38a6022f

Change Router status

init_code_upgrade

0x03601fc8

Initiate code upgrade for the Router

init_admin_upgrade

0x0b02fd5b

Initiate admin change for the Router

cancel_admin_upgrade

0x72d6b3b4

Cancel an admin change

cancel_code_upgrade

0x1f72111a

Cancel a code upgrade

finalize_upgrades

0x4e6707b7

Finalize upgrades of the Router code and admin change

reset_gas

0x29d22935

Update the amount of TON (in nanoTons) on the Router to storage_fee::router of the Router

set_params

0x2b8b3b62

Change pool-specific parameters

set_fees (0x58274069)

Change fees of a pool including liquidity pool fees, protocol fees and referral fees. It's necessary to provide correct Jetton token addresses for a target liquidity pool.

TL-B

set_fees#58274069 query_id:uint64 new_lp_fee:uint16 new_protocol_fee:uint16 new_protocol_fee_address:MsgAddress jetton_info:^[jetton_wallet0:MsgAddress jetton_wallet1:MsgAddress excesses_recipient:MsgAddress] = InternalMsgBody;

Arguments

Name
Type
Description

new_lp_fee

uint16

New liquidity pool fee ratio

new_protocol_fee

uint16

New protocol fee ratio

new_protocol_fee_address

address

Address for receiving protocol fees

additional_data

cell

Cell with wallet addresses

additional_data body

Name
Type
Description

jetton_wallet0

address

The address of the Router's wallet of first Jetton

jetton_wallet1

address

The address of the Router's wallet of second Jetton

excesses_address

address

The address where TON excesses will be sent

Notes:

  • fee ratio is the value of fee divided by params::fee_divider (10000); so to set a fee to 1% the value must be 100

  • fees must be between MIN_FEE (0) and MAX_FEE (100)

Outgoing messages

Sends a message with op set_fees to a liquidity pool.

update_status (0x38a6022f)

Changes Router is_locked var to the opposite value. If is_locked == 1 blocks transfer_notification messages from going through. Effectively blocks swap and provide_lp transactions through this Router.

TL-B

update_status#38a6022f query_id:uint64 = InternalMsgBody;

Arguments

None.

Outgoing messages

None.

init_code_upgrade (0x03601fc8)

Initiates code upgrade for the Router. An appropriate data with the new Router code must be provided. The changes won't take effect until finalize_upgrades is received by the Router. The minimum delay between initiating code upgrade and finalizing the changes in seven days.

TL-B

init_code_upgrade#3601fc8 query_id:uint64 new_code:^Cell = InternalMsgBody;

Arguments

Name
Type
Description

new_code

cell

Code of the new Router contract

Outgoing messages

Excesses will be sent to admin_address

init_admin_upgrade (0x0b02fd5b)

Initiates admin change for the Router. An appropriate address for a new Router admin must be provided. The changes won't take effect until finalize_upgrades is received by the Router. The minimum delay between initiating admin change and finalizing the changes in two days.

TL-B

init_admin_upgrade#b02fd5b query_id:uint64 new_admin_address:MsgAddress = InternalMsgBody;

Arguments

Name
Type
Description

new_admin

address

New admin address

Outgoing messages

Excesses will be sent to admin_address

cancel_admin_upgrade (0x72d6b3b4)

Cancels an admin change if there's a pending admin change.

TL-B

cancel_admin_upgrade#72d6b3b4 query_id:uint64 = InternalMsgBody;

Arguments

None

Outgoing messages

Excesses will be sent to admin_address

cancel_code_upgrade (0x1f72111a)

Cancels a code upgrade if there's a pending code change.

TL-B

cancel_code_upgrade#1f72111a query_id:uint64 = InternalMsgBody;

Arguments

None

Outgoing messages

Excesses will be sent to admin_address

finalize_upgrades (0x4e6707b7)

Finalizes upgrades of the Router code and admin changes. In order for the changes to take effect an appropriate amount of time must pass since initializing and upgrade. For code upgrade the delay is seven days, for an admin change the delay is two days.

TL-B

finalize_upgrades#4e6707b7 query_id:uint64 = InternalMsgBody;

Arguments

None

Outgoing messages

None

reset_gas (0x29d22935)

Updates the amount of TON (in nanoTons) on the Router to storage_fee::router (100000) of the Router. The remaining TON will be sent to the Router admin.

TL-B

reset_gas#42a0fb43 query_id:uint64 = InternalMsgBody;

Arguments

None

Outgoing messages

Excesses will be sent to admin_address

reset_pool_gas (0x66d0dff2)

Updates the amount of TON (in nanoTons) on a pool to storage_fee::router of a pool. Forwards this message to a pool contract for provided pair of Jetton tokens.

TL-B

reset_pool_gas#66d0dff2 query_id:uint64 jetton_wallet0:MsgAddress jetton_wallet1:MsgAddress excesses_recipient:MsgAddress = InternalMsgBody;

Arguments

Name
Type
Description

jetton_wallet0

address

The address of the Router's wallet of first Jetton

jetton_wallet1

address

The address of the Router's wallet of second Jetton

excesses_address

address

The address where TON excesses will be sent

Outgoing messages

Sends a message to a liquidity pool with reset_gas op code carrying remaining gas

update_pool_status (0x2af4607c)

Changes Pool is_locked var to the opposite value. If is_locked == 1 blocks swaps and liquidity withdraws from this Pool

TL-B

update_pool_status#2af4607c query_id:uint64 jetton_wallet0:MsgAddress jetton_wallet1:MsgAddress excesses_recipient:MsgAddress = InternalMsgBody;

Arguments

Name
Type
Description

jetton_wallet0

address

The address of the Router's wallet of first Jetton

jetton_wallet1

address

The address of the Router's wallet of second Jetton

excesses_address

address

The address where TON excesses will be sent

Outgoing messages

Excesses will be sent to excesses_address


Internal message handlers

pay_to (0x657b54f5)

Initiates a Jetton token transfer from wallets belonging to this Router, called from pools (on swap, liquidity providing, refund, etc).

TL-B

pay_to#657b54f5 query_id:uint64 to_address:MsgAddress excesses_address:MsgAddress original_caller:MsgAddress exit_code:uint32 custom_payload:(Maybe ^Cell) additional_info:^[fwd_ton_amount:Coins amount0_out:Coins token0_address:MsgAddress amount1_out:Coins token1_address:MsgAddress] = InternalMsgBody;

Message body

Name
Type
Description

op

uint32

Operation code; equal to pay_to

query_id

uint64

Query id

to_address

address

Address of a receiver

excesses_address

address

Address where TON excesses will be sent

original_caller

address

Address of the original tx sender

exit_code

uint32

Exit code

custom_payload

maybe_ref

Payload sent in transfer_notification

additional_data

cell

Cell with additional data

additional_data body

Name
Type
Description

fwd_gas

coins

Gas used to forward custom_payload if present

amount0_out

coins

Amount of the first Jetton token (in basic token units)

token0_address

address

The address of the Router's wallet of the first Jetton

amount1_out

coins

Amount of the second Jetton token (in basic token units)

token1_address

address

The address of the Router's wallet of the second Jetton

Outgoing messages

Sends transfer of amount0_out tokens to owner message to token0_address wallet if amount0_out > 0

or

Sends transfer of amount1_out tokens to owner message to token1_address wallet if amount1_out > 0

or

Sends swap to some Pool if custom_payload is cross_swap

pay_vault (0x63381632)

Deposit a token amount into a Vault contract; used if swap was done with referral

TL-B

pay_vault#63381632 query_id:uint64 owner:MsgAddress excesses_address:MsgAddress additional_info:^[amount0_out:Coins token0_address:MsgAddress amount1_out:Coins token1_address:MsgAddress] = InternalMsgBody;

Message body

Name
Type
Description

op

uint32

Operation code; equal to pay_vault

query_id

uint64

Query id

to_address

address

Address of a receiver

excesses_address

address

The address where TON excesses will be sent

additional_data

cell

Cell with additional data

additional_data body

Name
Type
Description

amount0_out

coins

Amount of the first Jetton token (in basic token units)

token0_address

address

The address of the Router's wallet of the first Jetton

amount1_out

coins

Amount of the second Jetton token (in basic token units)

token1_address

address

The address of the Router's wallet of the second Jetton

Outgoing messages

Sends deposit_ref_fee to a corresponding token Vault of to_address

vault_pay_to (0x2100c922)

Initiates a Jetton token transfer from wallets belonging to this Router, called from a Vault when an owner withdraws their collected referral fees.

TL-B

vault_pay_to#2100c922 query_id:uint64 amount_out:Coins token_address:MsgAddress to_address:MsgAddress = InternalMsgBody;

Message body

Name
Type
Description

op

uint32

Operation code; equal to vault_pay_to

query_id

uint64

Query id

amount_out

coins

Amount of the Jetton token (in basic token units)

token_address

address

The address of the Router's wallet of the Jetton

to_address

address

Address of a receiver

Outgoing messages

Sends transfer of amount_out tokens to to_address message to token_address wallet


Custom swap payloads

Forward swap to another router

Use a normal swap payload as custom_payload in the initial swap with the other Router address as the receiver_address. The value of fwd_gas must be non-zero and enough to perform the next swap.

Cross-swap payload

This payload allows chaining of swap operations on the same Router; fwd_gas is ignored.

TL-B

cross_swap#69cf1a5b token_wallet1:MsgAddress refund_address:MsgAddress excesses_address:MsgAddress tx_deadline:uint64 cross_swap_body:^[min_out:Coins receiver:MsgAddress fwd_gas:Coins custom_payload:(Maybe ^Cell) refund_fwd_gas:Coins refund_payload:(Maybe ^Cell) ref_fee:uint16 ref_address:MsgAddress] = DexPayload;

custom_payload body

Name
Type
Description

op

uint32

Cross-swap op (0x69cf1a5b)

other_token_wallet

address

Address of the other Router token wallet

refund_address

address

Address where refund will be sent if swap fails

excesses_address

address

Address where TON excesses will be sent

deadline

uint64

Timestamp of execution deadline for this tx

additional_data

cell

Cell with additional data

additional_data body

Name
Type
Description

min_out

coins

Minimum required amount of tokens to receive

receiver_address

address

Address where tokens will be sent after swap

fwd_gas

coins

Gas used to forward a message in transfer_notification after swap if custom_payload is present

custom_payload

maybe_ref

Payload sent in transfer_notification after swap

refund_fwd_gas

coins

Gas used to forward a message in transfer_notification if swap fails if refund_payload is present

refund_payload

maybe_ref

Payload sent in transfer_notification if swap fails

ref_fee

uint16

Referrer fee

referral_address

address

Referrer address


Constants

Name
Value
Description

storage_fee::router

10000000

Amount of TON (in nanoTons) to be left on the Router contract as gas

params::twodays

172800

Two days is seconds

params::sevendays

604800

Seven days in seconds

reference

This section contains sdk methods reference

Router

address

Address of the Router contract

gasConstants

Getters

getPoolAddress

async getPoolAddress(
    provider: ContractProvider,
    params: {
      token0: Address | string;
      token1: Address | string;
    },
): Promise<Address>

Arguments

Name
Type
Description

provider

ContractProvider

Instance of @ton/core contract provider

Name
Type
Description

token0

Address | string

The address of the router's wallet of first Jetton

token1

Address | string

The address of the router's wallet of second Jetton

Result

Returns an address of a pool for a specified pair of assets.

getPoolAddressByJettonMinters

async getPoolAddressByJettonMinters(
    provider: ContractProvider,
    params: {
      token0: Address | string;
      token1: Address | string;
    },
): Promise<Address>

Arguments

Name
Type
Description

provider

ContractProvider

Instance of @ton/core contract provider

Name
Type
Description

token0

Address | string

The address of the first pool Jetton Minter wallet

token1

Address | string

The address of the second pool Jetton Minter wallet

Result

Returns an address of a pool for a specified pair of assets.

getPool

async getPool(
    provider: ContractProvider,
    params: {
      token0: Address | string;
      token1: Address | string;
    },
): Promise<PoolV1>

Arguments

Name
Type
Description

provider

ContractProvider

Instance of @ton/core contract provider

Name
Type
Description

token0

Address | string

The address of the first pool Jetton Minter wallet

token1

Address | string

The address of the second pool Jetton Minter wallet

Result

Returns an instance of PoolV1 with specified Jetton token addresses.

getRouterData

async getRouterData(provider: ContractProvider): Promise<{
    isLocked: boolean;
    adminAddress: Address;
    tempUpgrade: Cell;
    poolCode: Cell;
    jettonLpWalletCode: Cell;
    lpAccountCode: Cell;
}>

Arguments

Name
Type
Description

provider

ContractProvider

Instance of @ton/core contract provider

Result

Returns structure containing current state of the router.

Name
Type
Description

isLocked

boolean

true if transfer_notification operations are locked (swap, provide_lp)

adminAddress

Address

Address of contract's admin account

tempUpgrade

Cell

A structure describing state of contract's code & admin upgrade; zero values indicate that no upgrade is pending

poolCode

Cell

Code of the router's liquidity pool contract

jettonLpWalletCode

Cell

Code of lp wallet contract

lpAccountCode

Cell

Code of lp account contract

Operations

createSwapBody

async createSwapBody(params: {
    userWalletAddress: Address | string;
    minAskAmount: bigint | number | string;
    askJettonWalletAddress: Address | string;
    referralAddress?: Address | string;
}): Promise<Cell>
Name
Type
Description

userWalletAddress

Address | string

User's address

minAskAmount

bigint | number | string

Minimum amount of tokens received (in basic token units)

askJettonWalletAddress

Address | string

Jetton router's wallet address of tokens to be received

referralAddress

Address | string | undefined

referral address

getSwapJettonToJettonTxParams

Build all data required to execute a jetton to jetton swap transaction

async getSwapJettonToJettonTxParams(
    provider: ContractProvider,
    params: {
        userWalletAddress: Address | string;
        offerJettonAddress: Address | string;
        askJettonAddress: Address | string;
        offerAmount: bigint | number | string;
        minAskAmount: bigint | number | string;
        referralAddress?: Address | string;
        gasAmount?: bigint | number | string;
        forwardGasAmount?: bigint | number | string;
        queryId?: number | bigint;
    },
): Promise<SenderArguments>

Arguments

Name
Type
Description

provider

ContractProvider

Instance of @ton/core contract provider

Name
Type
Description

userWalletAddress

Address | string

User's address

offerJettonAddress

Address | string

Jetton Minter address of a token to be swapped

askJettonAddress

Address | string

Jetton Minter address of a token to be received

offerAmount

bigint | number | string

Amount of tokens to be swapped (in basic token units)

minAskAmount

bigint | number | string

Minimum amount of tokens to be received (in basic token units)

referralAddress

Address | string | undefined

Referral address

gasAmount

bigint | number | string | undefined

Transaction gas

forwardGasAmount

bigint | number | string | undefined

Forward amount of gas for the next transaction (in nanoTons)

queryId

bigint | number | undefined

Query id

Result

Name
Type
Description

to

Address

Address of the router's Jetton wallet for the swapped token

value

bigint

Recommended amount of TON (in nanoTons) sent as gas

body

Cell | null | undefined

Optional Cell with data to be send

sendSwapJettonToJetton

getSwapJettonToTonTxParams

Build all data required to execute a jetton to ton swap transaction

async getSwapJettonToTonTxParams(
    provider: ContractProvider,
    params: {
        userWalletAddress: Address | string;
        offerJettonAddress: Address | string;
        proxyTon: PtonV1;
        offerAmount: bigint | number | string;
        minAskAmount: bigint | number | string;
        referralAddress?: Address | string;
        gasAmount?: bigint | number | string;
        forwardGasAmount?: bigint | number | string;
        queryId?: number | bigint;
    },
): Promise<SenderArguments>

Arguments

Name
Type
Description

provider

ContractProvider

Instance of @ton/core contract provider

Name
Type
Description

userWalletAddress

Address | string

User's address

offerJettonAddress

Address | string

Jetton Minter address of a token to be swapped

proxyTon

PtonV1

instance of pTON contract

offerAmount

bigint | number | string

Amount of tokens to be swapped (in basic token units)

minAskAmount

bigint | number | string

Minimum amount of tokens to be received (in basic token units)

referralAddress

Address | string | undefined

Referral address

gasAmount

bigint | number | string | undefined

Transaction gas

forwardGasAmount

bigint | number | string | undefined

Forward amount of gas for the next transaction (in nanoTons)

queryId

bigint | number | undefined

Query id

Result

Name
Type
Description

to

Address

Address of the router's Jetton wallet for the swapped token

value

bigint

Recommended amount of TON (in nanoTons) sent as gas

body

Cell | null | undefined

Optional Cell with data to be send

sendSwapJettonToTon

getSwapTonToJettonTxParams

Build all data required to execute a ton to jetton swap transaction

async getSwapTonToJettonTxParams(
    provider: ContractProvider,
    params: {
        userWalletAddress: Address | string;
        proxyTon: PtonV1;
        askJettonAddress: Address | string;
        offerAmount: bigint | number | string;
        minAskAmount: bigint | number | string;
        referralAddress?: Address | string;
        forwardGasAmount?: bigint | number | string;
        queryId?: number | bigint;
    },
): Promise<SenderArguments>

Arguments

Name
Type
Description

provider

ContractProvider

Instance of @ton/core contract provider

Name
Type
Description

userWalletAddress

Address | string

User's address

proxyTon

PtonV1

instance of pTON contract

askJettonAddress

Address | string

Jetton Minter address of a token to be received

offerAmount

bigint | number | string

Amount of tokens to be swapped (in basic token units)

minAskAmount

bigint | number | string

Minimum amount of tokens to be received (in basic token units)

referralAddress

Address | string | undefined

Referral address

forwardGasAmount

bigint | number | string | undefined

Forward amount of gas for the next transaction (in nanoTons)

queryId

bigint | number | undefined

Query id

Result

Name
Type
Description

to

Address

Address of the router's Jetton wallet for the swapped token

value

bigint

Recommended amount of TON (in nanoTons) sent as gas

body

Cell | null | undefined

Optional Cell with data to be send

sendSwapTonToJetton

createProvideLiquidityBody

async createProvideLiquidityBody(params: {
    routerWalletAddress: Address | string;
    minLpOut: bigint | number | string;
}): Promise<Cell>
Name
Type
Description

routerWalletAddress

Address | string

Address of the router's Jetton token wallet

minLpOut

bigint | number | string

Minimum amount of created liquidity tokens (in basic token units)

getProvideLiquidityJettonTxParams

Collect all data required to execute a jetton provide_lp transaction

async getProvideLiquidityJettonTxParams(
    provider: ContractProvider,
    params: {
        userWalletAddress: Address | string;
        sendTokenAddress: Address | string;
        otherTokenAddress: Address | string;
        sendAmount: bigint | number | string;
        minLpOut: bigint | number | string;
        gasAmount?: bigint | number | string;
        forwardGasAmount?: bigint | number | string;
        queryId?: number | bigint;
    },
): Promise<SenderArguments>

Arguments

Name
Type
Description

provider

ContractProvider

Instance of @ton/core contract provider

Name
Type
Description

userWalletAddress

Address | string

User's address

sendTokenAddress

Address | string

Address of the first Jetton token or a proxy ton contract

otherTokenAddress

Address | string

Address of the second Jetton token

sendAmount

bigint | number | string

Amount of the first/second tokens deposited as liquidity (in basic token units)

minLpOut

bigint | number | string

Minimum amount of created liquidity tokens (in basic token units)

gasAmount

bigint | number | string | undefined

Transaction gas

forwardGasAmount

bigint | number | string | undefined

Forward amount of gas for the next transaction (in nanoTons)

queryId

bigint | number | undefined

Query id

Result

Name
Type
Description

to

Address

Address of the router's Jetton wallet for the swapped token

value

bigint

Recommended amount of TON (in nanoTons) sent as gas

body

Cell | null | undefined

Optional Cell with data to be send

sendProvideLiquidityJetton

getProvideLiquidityTonTxParams

Collect all data required to execute a proxy ton provide_lp transaction

public async getProvideLiquidityTonTxParams(
    provider: ContractProvider,
    params: {
        userWalletAddress: Address | string;
        proxyTon: PtonV1;
        otherTokenAddress: Address | string;
        sendAmount: bigint | number | string;
        minLpOut: bigint | number | string;
        forwardGasAmount?: bigint | number | string;
        queryId?: number | bigint;
    },
): Promise<SenderArguments>

Arguments

Name
Type
Description

provider

ContractProvider

Instance of @ton/core contract provider

Name
Type
Description

userWalletAddress

Address | string

User's address

proxyTon

PtonV1

instance of pTON contract

otherTokenAddress

Address | string

Address of the second Jetton token

sendAmount

bigint | number | string

Amount of the first/second tokens deposited as liquidity (in basic token units)

minLpOut

bigint | number | string

Minimum amount of created liquidity tokens (in basic token units)

forwardGasAmount

bigint | number | string | undefined

Forward amount of gas for the next transaction (in nanoTons)

queryId

bigint | number | undefined

Query id

Result

Name
Type
Description

to

Address

Address of the router's Jetton wallet for the swapped token

value

bigint

Recommended amount of TON (in nanoTons) sent as gas

body

Cell | null | undefined

Optional Cell with data to be send

sendProvideLiquidityTon

Pool

address

Address of the Pool contract

gasConstants

Getters

getPoolData

async getPoolData(provider: ContractProvider): Promise<{
    reserve0: bigint;
    reserve1: bigint;
    token0WalletAddress: Address;
    token1WalletAddress: Address;
    lpFee: bigint;
    protocolFee: bigint;
    refFee: bigint;
    protocolFeeAddress: Address;
    collectedToken0ProtocolFee: bigint;
    collectedToken1ProtocolFee: bigint;
}>

Arguments

Name
Type
Description

provider

ContractProvider

Instance of @ton/core contract provider

Result

Returns structure containing current state of the pool.

Name
Type
Description

reserve0

bigint

Amount of the first token (in basic token units)

reserve1

bigint

Amount of the second token (in basic token units)

token0WalletAddress

Address

Address of the first Jetton token

token1WalletAddress

Address

Address of the second Jetton token

lpFee

bigint

Liquidity pool fee value

protocolFee

bigint

Protocol fee

refFee

bigint

Referral fee

protocolFeeAddress

Address

Address for receiving protocol fees

collectedToken0ProtocolFee

bigint

Amount of collected protocol fees of the first token (in basic token units)

collectedToken1ProtocolFee

bigint

Amount of collected protocol fees of the second token (in basic token units)

Notes:

  • fee ratio is the value of fee divided by FEE_DIVIDER (10000); so a fee of 1% has a value of 100

getExpectedOutputs

Estimate expected result of the amount of jettonWallet tokens swapped to the other type of tokens of the pool

async getExpectedOutputs(
    provider: ContractProvider,
    params: {
        amount: bigint | number | string;
        jettonWallet: Address | string;
    },
): Promise<{
    jettonToReceive: bigint;
    protocolFeePaid: bigint;
    refFeePaid: bigint;
}>

Arguments

Name
Type
Description

provider

ContractProvider

Instance of @ton/core contract provider

Name
Type
Description

amount

bigint | number | string

Amount of tokens to swap (in basic token units)

jettonWallet

Address | string

Token Jetton address (must be equal to one of the Jetton addresses of the pool)

Result

Returns structure with expected result of a token swap

Name
Type
Description

jettonToReceive

bigint

Amount of tokens received (in basic token units)

protocolFeePaid

bigint

Amount tokens paid for protocol fees (in basic token units)

refFeePaid

bigint

Amount tokens paid for referral fees (in basic token units)

getExpectedTokens

Estimate an expected amount of lp tokens minted when providing liquidity.

async getExpectedTokens(
    provider: ContractProvider,
    params: {
        amount0: bigint | number | string;
        amount1: bigint | number | string;
    },
): Promise<bigint>

Arguments

Name
Type
Description

provider

ContractProvider

Instance of @ton/core contract provider

Name
Type
Description

amount0

bigint | number | string

Amount of tokens for the first Jetton (in basic token units)

amount1

bigint | number | string

Amount of tokens for the second Jetton (in basic token units)

Result

Returns an estimated amount of liquidity tokens to be minted

getExpectedLiquidity

Estimate expected liquidity freed upon burning liquidity tokens.

async getExpectedLiquidity(
    provider: ContractProvider,
    params: {
        jettonAmount: bigint | number | string;
    },
): Promise<{
    amount0: bigint;
    amount1: bigint;
}>

Arguments

Name
Type
Description

provider

ContractProvider

Instance of @ton/core contract provider

Name
Type
Description

jettonAmount

bigint | number | string

Amount of liquidity tokens (in basic token units)

Result

Returns structure with expected freed liquidity

Name
Type
Description

amount0

bigint

Amount of tokens for the first Jetton (in basic token units)

amount1

bigint

Amount of tokens for the second Jetton (in basic token units)

getLpAccountAddress

async getLpAccountAddress(
    provider: ContractProvider,
    params: {
        ownerAddress: Address | string,
    },
): Promise<Address>

Arguments

Name
Type
Description

provider

ContractProvider

Instance of @ton/core contract provider

Name
Type
Description

ownerAddress

Address | string

Address of a user

Result

Function getLpAccountAddress returns the lp account address of a user

getLpAccount

async getLpAccount(
    provider: ContractProvider,
    params: {
        ownerAddress: Address | string
    },
): Promise<LpAccountV1>

Arguments

Name
Type
Description

provider

ContractProvider

Instance of @ton/core contract provider

Name
Type
Description

ownerAddress

Address | string

Address of a user

Result

returns an instance of LpAccountV1

getJettonData

async getJettonData(
    provider: ContractProvider,
): Promise<{
    totalSupply: bigint,
    isMintable: boolean,
    adminAddress: Address|null,
    jettonContentUri: string|null,
    jettonWalletCode: Cell
}>

Arguments

Name
Type
Description

provider

ContractProvider

Instance of @ton/core contract provider

Result

Returns a structure with Jetton data

Name
Type
Description

totalSupply

bigint

Total token supply (in basic token units)

isMintable

boolean

If mintable

adminAddress

Address

Admin address

jettonContentUri

string

Offchain uri with Jetton data

jettonWalletCode

Cell

Code of the lp Jetton wallet

getWalletAddress

getWalletAddress(
    provider: ContractProvider,
    ownerAddress: Address | string,
): Promise<Address>

Arguments

Name
Type
Description

provider

ContractProvider

Instance of @ton/core contract provider

ownerAddress

Address | string

Address of a user

Result

returns a calculated lp wallet address of a user

getJettonWallet

async getJettonWallet(
    provider: ContractProvider,
    params: {
        ownerAddress: Address | string;
    },
): Promise<JettonWallet>

Arguments

Name
Type
Description

ownerAddress

Address | string

Address of a user

Name
Type
Description

ownerAddress

Address | string

Address of a user

Result

Function getJettonWallet returns a JettonWallet instance for an address returned by getWalletAddress

Operations

createCollectFeesBody

async createCollectFeesBody(params: {
    queryId?: QueryIdType
}): Promise<Cell>

getCollectFeeTxParams

Build all data required to execute a collect_fees transaction.

async getCollectFeeTxParams(
    provider: ContractProvider,
    params?: {
        gasAmount?: bigint | number | string;
        queryId?: number | bigint;
    },
): Promise<SenderArguments>

Arguments

Name
Type
Description

provider

ContractProvider

Instance of @ton/core contract provider

Name
Type
Description

gasAmount

bigint | number | string | undefined

Transaction gas

queryId

bigint | number | undefined

Query id

Result

Name
Type
Description

to

Address

Address of the router's Jetton wallet for the swapped token

value

bigint

Recommended amount of TON (in nanoTons) sent as gas

body

Cell | null | undefined

Optional Cell with data to be send

Notes:

  • param is entirely optional

  • the default value for gasAmount is equal to 1.1 TON

sendCollectFees

createBurnBody

async createBurnBody(params: {
    amount: bigint | number | string;
    responseAddress: Address | string;
    queryId?: number | bigint;
}): Promise<Cell>

getBurnTxParams

Build all data required to execute a burn transaction.

async getBurnTxParams(
    provider: ContractProvider,
    params: {
        amount: bigint | number | string;
        responseAddress: Address | string;
        gasAmount?: bigint | number | string;
        queryId?: number | bigint;
    },
): Promise<SenderArguments>

Arguments

Name
Type
Description

provider

ContractProvider

Instance of @ton/core contract provider

Name
Type
Description

amount

bigint | number | string

Lp tokens to burn

responseAddress

Address | string

Excesses address

gasAmount

bigint | number | string | undefined

Transaction gas

queryId

bigint | number | undefined

Query id

sendBurn

LpAccount

address

Address of the LpAccount contract

gasConstants

Getters

getLpAccountData

async getLpAccountData(provider: ContractProvider): Promise<{
    userAddress: Address;
    poolAddress: Address;
    amount0: bigint;
    amount1: bigint;
}>

Arguments

Name
Type
Description

provider

ContractProvider

Instance of @ton/core contract provider

Result

Returns structure containing current state of the lp account.

Name
Type
Description

userAddress

Address

Owner's address

poolAddress

Address

Pool's address

amount0

bigint

Balance of the first Jetton token (in basic token units)

amount1

bigint

Balance of the second Jetton token (in basic token units)

Operations

createRefundBody

async createRefundBody(params?: {
    queryId?: QueryIdType
}): Promise<Cell>

getRefundTxParams

Build all data required to execute a refund_me transaction.

async getRefundTxParams(
    provider: ContractProvider,
    params?: {
        gasAmount?: bigint | number | string;
        queryId?: number | bigint;
    },
): Promise<SenderArguments>

Arguments

Name
Type
Description

provider

ContractProvider

Instance of @ton/core contract provider

Name
Type
Description

gasAmount

bigint | number | string | undefined

Transaction gas

queryId

bigint | number | undefined

Query id

Result

Name
Type
Description

to

Address

Address of the router's Jetton wallet for the swapped token

value

bigint

Recommended amount of TON (in nanoTons) sent as gas

body

Cell | null | undefined

Optional Cell with data to be send

sendRefund

createDirectAddLiquidityBody

async createDirectAddLiquidityBody(params: {
    amount0: bigint | number | string;
    amount1: bigint | number | string;
    minimumLpToMint?: bigint | number | string;
    queryId?: number | bigint;
}): Promise<Cell>

getDirectAddLiquidityTxParams

Build all data required to execute a direct_add_liquidity transaction.

async getDirectAddLiquidityTxParams(
    provider: ContractProvider,
    params: {
        amount0: bigint | number | string;
        amount1: bigint | number | string;
        minimumLpToMint?: bigint | number | string;
        gasAmount?: bigint | number | string;
        queryId?: number | bigint;
    },
): Promise<SenderArguments>

Arguments

Name
Type
Description

provider

ContractProvider

Instance of @ton/core contract provider

Name
Type
Description

amount0

bigint | number | string

Amount of the first Jetton tokens (in basic token units)

amount1

bigint | number | string

Amount of the second Jetton tokens (in basic token units)

minimumLpToMint

bigint | number | string | undefined

Minimum amount of received liquidity tokens (in basic token units)

gasAmount

bigint | number | string | undefined

Transaction gas

queryId

bigint | number | undefined

Query id

Result

Name
Type
Description

to

Address

Address of the router's Jetton wallet for the swapped token

value

bigint

Recommended amount of TON (in nanoTons) sent as gas

body

Cell | null | undefined

Optional Cell with data to be send

Notes:

  • addition of liquidity will fail if a user should receive less than minimumLpToMint of lp tokens as a result

  • the default value for gasAmount is equal to 0.3 TON

sendDirectAddLiquidity

createResetGasBody

async createResetGasBody(params?: {
    queryId?: QueryIdType
}): Promise<Cell>

getResetGasTxParams

Build all data required to execute a reset_gas transaction.

async getResetGasTxParams(
    provider: ContractProvider,
    params?: {
        gasAmount?: bigint | number | string;
        queryId?: number | bigint;
    },
): Promise<SenderArguments>

Arguments

Name
Type
Description

provider

ContractProvider

Instance of @ton/core contract provider

Name
Type
Description

gasAmount

bigint | number | string | undefined

Transaction gas

queryId

bigint | number | undefined

Query id

Result

Name
Type
Description

to

Address

Address of the router's Jetton wallet for the swapped token

value

bigint

Recommended amount of TON (in nanoTons) sent as gas

body

Cell | null | undefined

Optional Cell with data to be send

Notes:

  • params is entirely optional

  • the default value for gasAmount is equal to 0.3 TON

sendResetGas