From 7295cc8a0aaf2d0a0ece1a0173f9f39b8b20ad7b Mon Sep 17 00:00:00 2001 From: Novus Nota <68142933+novusnota@users.noreply.github.com> Date: Tue, 2 Dec 2025 08:48:45 +0100 Subject: [PATCH 1/5] feat(walletkit): how to work with Toncoin --- ecosystem/ton-connect/wallet.mdx | 55 +--- ecosystem/ton-connect/walletkit/overview.mdx | 13 +- .../ton-connect/walletkit/web/events.mdx | 11 + .../ton-connect/walletkit/web/toncoin.mdx | 310 ++++++++++++++++++ 4 files changed, 343 insertions(+), 46 deletions(-) create mode 100644 ecosystem/ton-connect/walletkit/web/toncoin.mdx diff --git a/ecosystem/ton-connect/wallet.mdx b/ecosystem/ton-connect/wallet.mdx index ac24968ab..5849b2d45 100644 --- a/ecosystem/ton-connect/wallet.mdx +++ b/ecosystem/ton-connect/wallet.mdx @@ -4,7 +4,6 @@ sidebarTitle: "Integrate a wallet" --- import { Aside } from '/snippets/aside.jsx'; -import { Stub } from '/snippets/stub.jsx'; This guide helps you integrate your custodial or non-custodial wallet with TON or partially build a basic, fresh one from scratch with the help of [TON Connect](/ecosystem/ton-connect/overview) and [WalletKit](/ecosystem/ton-connect/walletkit/overview). @@ -14,64 +13,32 @@ To proceed with a WalletKit integration, select your framework or environment: - - -Additionally, explore the complete demo wallets: - -## Usage - -Once the wallet is [integrated](#integration), follow one of these common usage recipes: +Additionally, explore the complete demo wallets: - - - - - - - +- [Demo wallet with WalletKit integration](https://walletkit-demo-wallet.vercel.app) +- [Demo wallet, GitHub repository](https://github.com/ton-connect/kit/tree/main/apps/demo-wallet) ## See also Read more about the TON Connect and WalletKit themselves: - - - - - +- [TON Connect overview](/ecosystem/ton-connect/overview) +- [WalletKit overview](/ecosystem/ton-connect/walletkit/overview) Skim the reference pages with more in-depth information: diff --git a/ecosystem/ton-connect/walletkit/overview.mdx b/ecosystem/ton-connect/walletkit/overview.mdx index 6c312e094..479e0acbb 100644 --- a/ecosystem/ton-connect/walletkit/overview.mdx +++ b/ecosystem/ton-connect/walletkit/overview.mdx @@ -11,11 +11,15 @@ It's designed for institutions, non-custodians, and custodians that need full co ## Features - +- TON Connect protocol: Handles connect, disconnect, transaction and data sign requests +- Wallet management: Support for multiple TON wallets at once, with persistent storage and optional use of custom signers +- Action previews: Transaction emulation with money flow analysis +- Asset support: Toncoin (TON), Jettons (including USDTs), NFTs with metadata +- Platform availability: Use on the Web, on mobile (Android and iOS), and in browser extensions ## Use cases - +From a hobby wallet service to a production-grade, extensive cross-chain system. ## Quick start @@ -50,6 +54,11 @@ Then, follow relevant usage recipes: title="Handle other events" href="/ecosystem/ton-connect/walletkit/web/events" /> + + diff --git a/ecosystem/ton-connect/walletkit/web/events.mdx b/ecosystem/ton-connect/walletkit/web/events.mdx index a16eaa599..53311581d 100644 --- a/ecosystem/ton-connect/walletkit/web/events.mdx +++ b/ecosystem/ton-connect/walletkit/web/events.mdx @@ -130,6 +130,17 @@ await kit.onRequestError(async (event) => { }); ``` +## Next steps + + + + + ## See also - [WalletKit overview](/ecosystem/ton-connect/walletkit) diff --git a/ecosystem/ton-connect/walletkit/web/toncoin.mdx b/ecosystem/ton-connect/walletkit/web/toncoin.mdx new file mode 100644 index 000000000..9f24745e7 --- /dev/null +++ b/ecosystem/ton-connect/walletkit/web/toncoin.mdx @@ -0,0 +1,310 @@ +--- +title: "How to work with Toncoin using WalletKit on the Web platform" +sidebarTitle: "Work with Toncoin" +--- + +import { Aside } from '/snippets/aside.jsx'; + + + +To work with Toncoin, the wallet service needs to handle [contract balances](#balances) and perform transfers initiated [from dApps](#transfers-from-dapps) and [from within the wallet service itself](#transfers-from-the-wallet-service). + +## Balances + +Blockchain state changes constantly as new blocks are produced. This has implications for when and how to check TON wallet contract balances: + +- [Discrete one-off checks](#on-demand-balance-check) have almost no value on their own — the state might change immediately after the query completes, invalidating its results. Thus, such checks are only practical when handling `transaction` requests. +- [Continuous monitoring](#continuous-balance-monitoring) is useful for UI display, showing the most recent balance to users, but should not be used for transaction confirmations. + +Notice that both cases require querying the blockchain data via the API client set during the [WalletKit initialization](/ecosystem/ton-connect/walletkit/web/init#param-api-client). Obtain and provide the key from the selected client to access higher requests-per-second limits. + +### On-demand balance check + +Use the `getBalance()` method to check the wallet contract balance in TON wallets managed by WalletKit. The balance is returned in nanoToncoin, with 1 Toncoin equal to $10^9$ nanoToncoin. + + + +```ts title="TypeScript" +async function getBalance(address: string): Promise { + // Get TON wallet instance + const wallet = kit.getWallet(address); + if (!wallet) return; + + // Query its balance in nanoToncoin + return await wallet.getBalance(); +} +``` + +Since the kit helps to manage initialized TON wallets, all outgoing transfers are controlled and will not happen on their own. + +Therefore, the most practical use of one-off balance checks is right before approving a transaction request. At this point, the actual wallet contract balance cannot be less than the checked amount, though it might be higher if new funds arrived right after the check. + +```ts title="TypeScript" +// An enumeration of various common error codes +import { SEND_TRANSACTION_ERROR_CODES } from '@ton/walletkit'; + +kit.onTransactionRequest(async (event) => { + const wallet = kit.getWallet(event.walletAddress ?? ''); + if (!wallet) { + console.error('Wallet not found for a transaction request', event); + await kit.rejectTransactionRequest(event, { + code: SEND_TRANSACTION_ERROR_CODES.UNKNOWN_ERROR, + message: 'Wallet not found', + }); + return; + } + + // Calculate the minimum balance needed for this transaction + const balance = await wallet.getBalance(); + const minNeededBalance = event.request.messages.reduce( + (acc, message) => acc + BigInt(message.amount), + 0n, + ); + + // Reject early if balance is clearly insufficient + if (balance < minNeededBalance) { + await kit.rejectTransactionRequest(event, { + code: SEND_TRANSACTION_ERROR_CODES.BAD_REQUEST_ERROR, + message: 'Insufficient balance', + }); + return; + } + + // Proceed with the regular transaction flow + // ... +}); +``` + +### Continuous balance monitoring + +Poll the balance at regular intervals to keep the displayed value up to date. Use an appropriate interval based on UX requirements — shorter intervals provide fresher data but increase API usage. + +This example should be modified according to the wallet service's logic: + +```ts title="TypeScript" expandable +// Configuration +const POLLING_INTERVAL_MS = 10_000; + +/** + * Starts the monitoring of a given wallet contract `address`, + * calling `onBalanceUpdate()` each `intervalMs` milliseconds + * + * @returns a function to stop monitoring + */ +export function startBalanceMonitoring( + address: string, + onBalanceUpdate: (balance: bigint) => void, + intervalMs: number = POLLING_INTERVAL_MS, +): () => void { + let isRunning = true; + + const poll = async () => { + while (isRunning) { + const wallet = kit.getWallet(address); + if (wallet) { + const balance = await wallet.getBalance(); + onBalanceUpdate(balance); + } + await new Promise((resolve) => setTimeout(resolve, intervalMs)); + } + }; + + // Start monitoring + poll(); + + // Return a cleanup function to stop monitoring + return () => { + isRunning = false; + }; +} + +// Usage +const stopMonitoring = startBalanceMonitoring( + walletAddress, + // The updateUI() function is exemplary and should be replaced by + // an existing function in your wallet service that refreshes the + // state of the balance displayed in the interface + (balance) => updateUI(balance), +); + +// Stop monitoring once it is no longer needed +stopMonitoring(); +``` + +## Transfers from dApps + +When a connected dApp requests a Toncoin transfer, the wallet service follows this flow: + +```mermaid +sequenceDiagram + participant dApp + participant Bridge + participant WalletKit + participant User + participant Blockchain + + dApp->>Bridge: Send transaction request + Bridge->>WalletKit: Forward request + WalletKit->>WalletKit: Emulate transaction + WalletKit->>User: Show preview (money flow) + User-->>WalletKit: Approve / Decline + alt Approved + WalletKit->>Blockchain: Send transaction + WalletKit->>Bridge: Return success + BoC + Bridge->>dApp: Transaction sent + else Declined + WalletKit->>Bridge: Return rejection + Bridge->>dApp: User rejected + end +``` + +### Emulation and preview + +WalletKit automatically emulates every incoming transaction request before presenting it to the user. The emulation result is available in the `event.preview` object: + +```ts title="TypeScript" +kit.onTransactionRequest(async (event) => { + if (event.preview.result === 'success') { + // Emulation succeeded — show the predicted money flow + const { ourTransfers } = event.preview.moneyFlow; + + // This is an array of values, + // where positive amounts mean incoming funds + // and negative amounts — outgoing funds + console.log('Predicted transfers:', ourTransfers); + } else { + // Emulation failed — warn the user but allow proceeding + console.warn('Transaction emulation failed:', event.preview); + } + + // Present the preview to the user and await their decision + // ... +}); +``` + + + +### Approve or reject + +After showing the preview, handle the user's decision: + +```ts title="TypeScript" expandable +// An enumeration of various common error codes +import { SEND_TRANSACTION_ERROR_CODES } from '@ton/walletkit'; + +kit.onTransactionRequest(async (event) => { + try { + // Show the emulation preview to the wallet service user + const preview = event.preview; + const isEmulationSuccessful = preview.result === 'success'; + + // Build a confirmation message + let confirmMessage = 'Confirm this transaction?'; + if (isEmulationSuccessful) { + const transfers = preview.moneyFlow.ourTransfers; + confirmMessage = `Send ${formatTransfers(transfers)}?`; + } else { + confirmMessage = 'Emulation failed. Proceed anyway?'; + } + + // Handle user's decision + if (confirm(confirmMessage)) { + // Approve — this sends the transaction to the blockchain + // and returns the signed message BoC to the dApp + await kit.approveTransactionRequest(event); + console.log('Transaction approved and sent'); + } else { + // Reject — notify the dApp that the user declined + await kit.rejectTransactionRequest(event, { + code: SEND_TRANSACTION_ERROR_CODES.USER_REJECTS_ERROR, + message: 'User rejected the transaction', + }); + } + } catch (error) { + console.error('Transaction handler error:', error); + await kit.rejectTransactionRequest(event, { + code: SEND_TRANSACTION_ERROR_CODES.UNKNOWN_ERROR, + message: 'Transaction processing failed', + }); + } +}); + +function formatTransfers( + transfers: Array<{ amount: bigint; asset: string }>, +): string { + return transfers + .map((t) => `${Number(t.amount) / 1e9} ${t.asset}`) + .join(', '); +} +``` + +### Confirm transaction delivery + +TON achieves transaction [finality](https://en.wikipedia.org/wiki/Blockchain#Finality) after a single masterchain block confirmation, where new blocks are produced approximately every 5 seconds. Once a transaction appears in a masterchain block, it becomes irreversible. + +Therefore, to reliably confirm the transaction delivery and status, one needs to check whether a transaction has achieved masterchain finality using the selected API client. + +That said, the wallet service should not block the UI while waiting for such confirmation. After all, with [continuous wallet balance monitoring](#continuous-balance-monitoring) and subsequent transaction requests, users will receive the latest information either way. Confirmations are only needed to reliably display a list of past transactions, including the most recent ones. + +For detailed transaction tracking, message lookups, and payment processing, the [message lookup guide](/ecosystem/ton-connect/message-lookup) covers finding transactions by external message hash, waiting for confirmations, and applying message normalization. + +## Transfers in the wallet service + +Transactions can be created directly from the wallet service (not from dApps) and fed into the regular approval flow via `handleNewTransaction()` method of the WalletKit. It creates a new [transaction request event](/ecosystem/ton-connect/walletkit/web/events#handle-ontransactionrequest), enabling the same UI confirmation-to-transaction flow for both dApp-initiated and wallet-initiated transactions. + +This example should be modified according to the wallet service's logic: + +```ts title="TypeScript" +import { type TonTransferParams } from '@ton/walletkit'; + +async function sendToncoin( + // Recipient's TON wallet contract address as a string + address: string, + // Amount in nanoToncoins + nanoAmount: BigInt, + // Optional comment string + comment?: string, + // Optional payload body as a BoC in Base64-encoded string + payload?: string, +) { + if (comment && payload) { + console.error('Cannot attach both a comment or a payload body'); + return; + } + + const from = kit.getWallet(address); + if (!from) { + console.error('No wallet contract found'); + return; + } + + const transferParams: TonTransferParams = { + toAddress: address, + amount: nanoAmount.toString(), + // Optional comment OR payload, not both + ...(comment && { comment: comment }), + } + + // Build transaction content + const tx = await from.createTransferTonTransaction(transferParams); + + // Route into the normal flow, + // triggering the onTransactionRequest() handler + await kit.handleNewTransaction(from, tx); +} +``` + +## See also + +- [Handle transaction requests](/ecosystem/ton-connect/walletkit/web/events#handle-ontransactionrequest) +- [Transaction fees](/foundations/fees) +- [WalletKit overview](/ecosystem/ton-connect/walletkit) +- [TON Connect overview](/ecosystem/ton-connect) From 0b9607a3c1ccfa22b1b23c8fd5291b625c5e65ef Mon Sep 17 00:00:00 2001 From: Novus Nota <68142933+novusnota@users.noreply.github.com> Date: Thu, 4 Dec 2025 09:31:18 +0100 Subject: [PATCH 2/5] nav entry --- docs.json | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/docs.json b/docs.json index 0d3ba5f86..fd412dfa8 100644 --- a/docs.json +++ b/docs.json @@ -128,7 +128,8 @@ "ecosystem/ton-connect/walletkit/web/init", "ecosystem/ton-connect/walletkit/web/wallets", "ecosystem/ton-connect/walletkit/web/connections", - "ecosystem/ton-connect/walletkit/web/events" + "ecosystem/ton-connect/walletkit/web/events", + "ecosystem/ton-connect/walletkit/web/toncoin" ] }, { @@ -175,7 +176,9 @@ "ecosystem/tma/telegram-ui/platform-and-palette", { "group": "Reference", - "pages": ["ecosystem/tma/telegram-ui/reference/avatar"] + "pages": [ + "ecosystem/tma/telegram-ui/reference/avatar" + ] } ] }, @@ -231,14 +234,20 @@ }, { "group": "Staking", - "pages": ["ecosystem/staking/overview"] + "pages": [ + "ecosystem/staking/overview" + ] }, "ecosystem/analytics" ] }, { "group": "Payment processing", - "pages": ["payments/overview", "payments/toncoin", "payments/jettons"] + "pages": [ + "payments/overview", + "payments/toncoin", + "payments/jettons" + ] }, { "group": "Standard contracts", @@ -255,7 +264,10 @@ "standard/wallets/v4", { "group": "V5", - "pages": ["standard/wallets/v5", "standard/wallets/v5-api"] + "pages": [ + "standard/wallets/v5", + "standard/wallets/v5-api" + ] }, { "group": "Highload Wallets", @@ -273,7 +285,9 @@ }, { "group": "Highload Wallet v2", - "pages": ["standard/wallets/highload/v2/specification"] + "pages": [ + "standard/wallets/highload/v2/specification" + ] } ] }, @@ -459,7 +473,10 @@ { "group": "Libraries", "expanded": true, - "pages": ["languages/func/stdlib", "languages/func/libraries"] + "pages": [ + "languages/func/stdlib", + "languages/func/libraries" + ] }, "languages/func/changelog", "languages/func/known-issues" From 8a1d3c3955fe4d6a1fe4e0a8beb61d17aa9426ef Mon Sep 17 00:00:00 2001 From: Novus Nota <68142933+novusnota@users.noreply.github.com> Date: Thu, 4 Dec 2025 10:24:02 +0100 Subject: [PATCH 3/5] Apply suggestions from AI code review Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- ecosystem/ton-connect/walletkit/overview.mdx | 2 +- ecosystem/ton-connect/walletkit/web/toncoin.mdx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ecosystem/ton-connect/walletkit/overview.mdx b/ecosystem/ton-connect/walletkit/overview.mdx index 479e0acbb..7e1cbc588 100644 --- a/ecosystem/ton-connect/walletkit/overview.mdx +++ b/ecosystem/ton-connect/walletkit/overview.mdx @@ -19,7 +19,7 @@ It's designed for institutions, non-custodians, and custodians that need full co ## Use cases -From a hobby wallet service to a production-grade, extensive cross-chain system. +WalletKit supports both small wallet services and large cross-chain systems. ## Quick start diff --git a/ecosystem/ton-connect/walletkit/web/toncoin.mdx b/ecosystem/ton-connect/walletkit/web/toncoin.mdx index 9f24745e7..15cb858c8 100644 --- a/ecosystem/ton-connect/walletkit/web/toncoin.mdx +++ b/ecosystem/ton-connect/walletkit/web/toncoin.mdx @@ -128,7 +128,7 @@ export function startBalanceMonitoring( const stopMonitoring = startBalanceMonitoring( walletAddress, // The updateUI() function is exemplary and should be replaced by - // an existing function in your wallet service that refreshes the + // a wallet service function that refreshes the // state of the balance displayed in the interface (balance) => updateUI(balance), ); From 973657eff100f1073a145a69a85e814fd588bc3f Mon Sep 17 00:00:00 2001 From: Novus Nota <68142933+novusnota@users.noreply.github.com> Date: Mon, 8 Dec 2025 16:14:07 +0100 Subject: [PATCH 4/5] Apply suggestions from the review --- docs.json | 2 +- .../ton-connect/walletkit/web/toncoin.mdx | 23 +++++++++++++++---- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/docs.json b/docs.json index f7ddff93c..3141bcfcc 100644 --- a/docs.json +++ b/docs.json @@ -3270,7 +3270,7 @@ "source": "/languages/tolk/from-func/in-detail", "destination": "/languages/tolk/from-func/tolk-vs-func", "permanent": true - }, + }, { "source": "/languages/tolk/from-func/mutability", "destination": "/languages/tolk/syntax/mutability", diff --git a/ecosystem/ton-connect/walletkit/web/toncoin.mdx b/ecosystem/ton-connect/walletkit/web/toncoin.mdx index 15cb858c8..212020003 100644 --- a/ecosystem/ton-connect/walletkit/web/toncoin.mdx +++ b/ecosystem/ton-connect/walletkit/web/toncoin.mdx @@ -41,9 +41,11 @@ async function getBalance(address: string): Promise { } ``` -Since the kit helps to manage initialized TON wallets, all outgoing transfers are controlled and will not happen on their own. +The most practical use of one-off balance checks is right before approving a transaction request. At this point, the actual wallet contract balance usually is not less than the checked amount, though it might be higher if new funds arrived right after the check. -Therefore, the most practical use of one-off balance checks is right before approving a transaction request. At this point, the actual wallet contract balance cannot be less than the checked amount, though it might be higher if new funds arrived right after the check. + ```ts title="TypeScript" // An enumeration of various common error codes @@ -240,15 +242,28 @@ kit.onTransactionRequest(async (event) => { function formatTransfers( transfers: Array<{ amount: bigint; asset: string }>, ): string { + const formatNano = (value: bigint, decimals: number = 9): string => { + const isNegative = value < 0n; + const str = (isNegative ? -value : value) + .toString() + .padStart(decimals + 1, '0'); + + const intPart = str.slice(0, -decimals) || '0'; + const fracPart = str.slice(-decimals).replace(/0+$/, ''); + + const formatted = fracPart ? `${intPart}.${fracPart}` : intPart; + return `${isNegative ? '-' : ''}${formatted}`; + }; + return transfers - .map((t) => `${Number(t.amount) / 1e9} ${t.asset}`) + .map((t) => `${formatNano(t.amount)} ${t.asset}`) .join(', '); } ``` ### Confirm transaction delivery -TON achieves transaction [finality](https://en.wikipedia.org/wiki/Blockchain#Finality) after a single masterchain block confirmation, where new blocks are produced approximately every 5 seconds. Once a transaction appears in a masterchain block, it becomes irreversible. +TON achieves transaction [finality](https://en.wikipedia.org/wiki/Blockchain#Finality) after a single masterchain block confirmation, where new blocks are produced approximately every 3 seconds. Once a transaction appears in a masterchain block, it becomes irreversible. Therefore, to reliably confirm the transaction delivery and status, one needs to check whether a transaction has achieved masterchain finality using the selected API client. From f591f899a4de16f492044d6979cccf45eeaf47f3 Mon Sep 17 00:00:00 2001 From: Novus Nota <68142933+novusnota@users.noreply.github.com> Date: Tue, 9 Dec 2025 10:45:30 +0100 Subject: [PATCH 5/5] a less detailed note --- ecosystem/ton-connect/walletkit/web/toncoin.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ecosystem/ton-connect/walletkit/web/toncoin.mdx b/ecosystem/ton-connect/walletkit/web/toncoin.mdx index 212020003..c1d41494c 100644 --- a/ecosystem/ton-connect/walletkit/web/toncoin.mdx +++ b/ecosystem/ton-connect/walletkit/web/toncoin.mdx @@ -44,7 +44,7 @@ async function getBalance(address: string): Promise { The most practical use of one-off balance checks is right before approving a transaction request. At this point, the actual wallet contract balance usually is not less than the checked amount, though it might be higher if new funds arrived right after the check. ```ts title="TypeScript"