From b14917fdf28e668dc10b10c92c5de102c5ec2980 Mon Sep 17 00:00:00 2001 From: Novus Nota <68142933+novusnota@users.noreply.github.com> Date: Thu, 26 Feb 2026 09:28:27 +0100 Subject: [PATCH] feat(appkit): how to perform swaps --- docs.json | 3 +- ecosystem/appkit/overview.mdx | 7 + ecosystem/appkit/swap.mdx | 282 ++++++++++++++++++++++++++++++++++ 3 files changed, 291 insertions(+), 1 deletion(-) create mode 100644 ecosystem/appkit/swap.mdx diff --git a/docs.json b/docs.json index d18fb48fc..8d2046503 100644 --- a/docs.json +++ b/docs.json @@ -141,7 +141,8 @@ "ecosystem/appkit/overview", "ecosystem/appkit/init", "ecosystem/appkit/toncoin", - "ecosystem/appkit/jettons" + "ecosystem/appkit/jettons", + "ecosystem/appkit/swaps" ] }, { diff --git a/ecosystem/appkit/overview.mdx b/ecosystem/appkit/overview.mdx index 6ec799bc0..8a69bcead 100644 --- a/ecosystem/appkit/overview.mdx +++ b/ecosystem/appkit/overview.mdx @@ -50,6 +50,13 @@ TON Connect's **AppKit** is an open-source SDK that integrates web2 and web3 app horizontal="true" href="/ecosystem/appkit/jettons" /> + + ## See also diff --git a/ecosystem/appkit/swap.mdx b/ecosystem/appkit/swap.mdx new file mode 100644 index 000000000..ed957c79f --- /dev/null +++ b/ecosystem/appkit/swap.mdx @@ -0,0 +1,282 @@ +--- +title: "How to swap tokens using AppKit" +sidebarTitle: "Swap tokens" +--- + +import { Aside } from '/snippets/aside.jsx'; + + + +AppKit supports on-chain token swaps through [pluggable swap providers](#create-a-custom-swap-provider). Supported swap kinds are Toncoin to jetton and jetton to jetton. + +The swap flow has two steps: [get a quote](#get-a-swap-quote) for the desired trade, then [build and send the swap transaction](#build-and-send-the-swap-transaction). + + + +## Get a swap quote + +A swap quote estimates how many tokens are received for a given input amount. The quote requires the source token, destination token, and the amount to swap. + + + ```tsx title="React" icon="react" + import { useSwapQuote } from '@ton/appkit-react'; + + export const SwapQuoteCard = ({ fromAddress, toAddress, amount }) => { + const { + data: quote, + isLoading, + error, + } = useSwapQuote({ + // Source token: what to swap + from: { + // Either a source jetton master (minter) contract address + // or a Toncoin identifier in its stead + address: fromAddress ?? 'ton', + + // Decimal precision of the token to calculate raw unit amounts + // For example, Toncoin = 9, USDT (jetton) = 6 + decimals: 9, + }, + + // Target token: what to receive + to: { + address: toAddress ?? 'ton', + decimals: 9, + } + + // Amount to swap in fractional units + // For example, '0.1' or '1' + amount, + + // Direction of the swap + // If true, `amount` sets the target amount of tokens to receive (buy) + // If false, `amount` sets the source amount of tokens to spend (sell) + // Defaults to false + isReverseSwap: false, + }); + + if (isLoading) { + return
Loading quote...
; + } + + if (error) { + return
Error: {error.message}
; + } + + return ( +
+

Swap quote

+ {quote && ( +
+

From: {quote.from.address}

+

To: {quote.to.address}

+

Expected output: {quote.toAmount}

+

Price impact: {quote.priceAmount}

+
+ )} +
+ ); + }; + ``` + + ```ts title="TypeScript" icon="globe" + import { type AppKit, getSwapQuote } from '@ton/appkit'; + + async function fetchSwapQuote( + /** Initialized AppKit instance */ + kit: AppKit, + /** Source token address */ + fromToken: string, + /** Destination token address */ + toToken: string, + /** Amount to swap in raw units */ + amount: string, + ) { + const quote = await getSwapQuote(kit, { + from: fromToken, + to: toToken, + amount, + }); + console.log('Swap quote:', quote); + return quote; + } + ``` +
+ +## Build and send the swap transaction + +After obtaining a quote, build the swap transaction and send it through TON Connect: + + + ```tsx title="React" icon="react" + import { + useSwapQuote, + useBuildSwapTransaction, + useSelectedWallet, + sendTransaction, + useAppKit, + } from '@ton/appkit-react'; + + export const SwapForm = ({ fromToken, toToken, amount }) => { + const appKit = useAppKit(); + const [wallet, _] = useSelectedWallet(); + const { data: quote } = useSwapQuote({ from: fromToken, to: toToken, amount }); + const { mutateAsync: buildTransaction, isPending: isBuilding } = useBuildSwapTransaction(); + + const handleSwap = async () => { + if (!quote || !wallet) return; + + // Build the swap transaction from the quote + const transaction = await buildTransaction({ + quote, + userAddress: wallet.getAddress(), + }); + + // Sign and send via TON Connect + const result = await sendTransaction(appKit, { transaction }); + console.log('Swap transaction sent:', result.boc); + }; + + return ( +
+ {quote && ( +
+

Output amount: {quote.toAmount}

+ +
+ )} +
+ ); + }; + ``` + + ```ts title="TypeScript" icon="globe" + import { + type AppKit, + getSwapQuote, + buildSwapTransaction, + sendTransaction, + getSelectedWallet, + } from '@ton/appkit'; + + async function executeSwap( + /** Initialized AppKit instance */ + kit: AppKit, + /** Source token address */ + fromToken: string, + /** Destination token address */ + toToken: string, + /** Amount to swap in raw units */ + amount: string, + ) { + // Step 1: Get a quote + const quote = await getSwapQuote(kit, { + from: fromToken, + to: toToken, + amount, + }); + + // Step 2: Build the swap transaction + const selectedWallet = getSelectedWallet(kit); + const transaction = await buildSwapTransaction(kit, { + quote, + userAddress: selectedWallet?.getAddress() ?? '', + }); + + // Step 3: Sign and send via TON Connect + const result = await sendTransaction(kit, { transaction }); + console.log('Swap transaction sent:', result.boc); + } + ``` +
+ +## Create a custom swap provider + +AppKit ships with the [Omniston](https://ston.fi/omniston) swap provider, but custom providers can be added to integrate other DEXes or aggregators. + +A swap provider implements the `SwapProvider` interface — two methods for quoting and transaction building: + +```ts title="TypeScript" +import type { + SwapProvider, + SwapQuote, + SwapQuoteParams, + SwapParams, +} from '@ton/appkit'; +import type { TransactionRequest } from '@ton/appkit'; + +class CustomSwapProvider implements SwapProvider { + // Unique identifier for this provider + readonly id = 'custom-dex'; + + async getQuote( + params: SwapQuoteParams, + ): Promise { + // Fetch a quote from the DEX API + const response = await fetch( + `https://api.example-dex.com/quote?from=${params.from}&to=${params.to}&amount=${params.amount}`, + ); + const data = await response.json(); + return { + from: params.from, + to: params.to, + fromAmount: params.amount, + toAmount: data.outputAmount, + providerData: data, + }; + } + + async buildSwapTransaction( + params: SwapParams, + ): Promise { + // Build the transaction payload using the quote data + const response = await fetch('https://api.example-dex.com/build', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + quote: params.quote, + userAddress: params.userAddress, + }), + }); + return response.json(); + } +} +``` + +### Register the provider + +Register the custom provider during [AppKit initialization](/ecosystem/appkit/init#providers) or [dynamically](/ecosystem/appkit/init#add-new-providers) at runtime: + + + ```ts title="At initialization" + import { AppKit } from '@ton/appkit'; + + const kit = new AppKit({ + providers: [new CustomSwapProvider()], + }); + ``` + + ```ts title="At runtime" + kit.swapManager.registerProvider(new CustomSwapProvider()); + ``` + + +### Specify the provider + +Once registered, the provider is available through `getSwapQuote` and `buildSwapTransaction`. Target it by passing `providerId: 'custom-dex'` when fetching quotes. + +## See also + +- [AppKit overview](/ecosystem/appkit/overview) +- [TON Connect overview](/ecosystem/ton-connect)