From 75363c5a1b316bd08da35006eef4a10baf1bb64b Mon Sep 17 00:00:00 2001 From: Coalus Date: Fri, 10 Apr 2026 22:56:28 +0300 Subject: [PATCH 1/5] feat(standard): migrate ton connect cookbook examples --- standard/tokens/jettons/transfer.mdx | 190 ++++++++++++++++++++ standard/tokens/nft/transfer.mdx | 249 +++++++++++++++++++++++++++ 2 files changed, 439 insertions(+) diff --git a/standard/tokens/jettons/transfer.mdx b/standard/tokens/jettons/transfer.mdx index 8c003a9ee..f52f99d61 100644 --- a/standard/tokens/jettons/transfer.mdx +++ b/standard/tokens/jettons/transfer.mdx @@ -86,6 +86,196 @@ async function main() { void main(); ``` +## Transfer via TON Connect + +To send jettons from a dApp through [TON Connect](/ecosystem/ton-connect/overview), build the [TEP-74](https://github.com/ton-blockchain/TEPs/blob/master/text/0074-jettons-standard.md#1-transfer) transfer body and submit it to the sender's jetton wallet contract: + + + ```tsx title="React" icon="react" + import { useTonConnectUI } from '@tonconnect/ui-react'; + import { beginCell, toNano, Address } from '@ton/ton'; + + // transfer#0f8a7ea5 query_id:uint64 amount:(VarUInteger 16) destination:MsgAddress + // response_destination:MsgAddress custom_payload:(Maybe ^Cell) + // forward_ton_amount:(VarUInteger 16) forward_payload:(Either Cell ^Cell) + // = InternalMsgBody; + + const body = beginCell() + .storeUint(0x0f8a7ea5, 32) // jetton transfer op code + .storeUint(0, 64) // query_id:uint64 + .storeCoins(toNano('0.001')) // amount — decimals vary per token (6 for USDT, 9 default) + .storeAddress(Address.parse('')) // destination:MsgAddress + .storeAddress(Address.parse('')) // response_destination:MsgAddress + .storeUint(0, 1) // custom_payload:(Maybe ^Cell) + .storeCoins(toNano('0.05')) // forward_ton_amount — if >0, sends notification + .storeBit(1) // forward_payload as reference + .storeRef(beginCell().endCell()) // empty payload reference + .endCell(); + + const transaction = { + validUntil: Math.floor(Date.now() / 1000) + 360, + messages: [ + { + address: '', // sender's jetton wallet address + amount: toNano('0.05').toString(), // for gas fees, excess is returned + payload: body.toBoc().toString('base64'), + }, + ], + }; + + export const TransferJetton = () => { + const [tonConnectUI] = useTonConnectUI(); + + return ( + + ); + }; + ``` + + ```ts title="TypeScript" icon="globe" + import TonConnectUI from '@tonconnect/ui'; + import { beginCell, toNano, Address } from '@ton/ton'; + + const body = beginCell() + .storeUint(0x0f8a7ea5, 32) + .storeUint(0, 64) + .storeCoins(toNano('0.001')) + .storeAddress(Address.parse('')) + .storeAddress(Address.parse('')) + .storeUint(0, 1) + .storeCoins(toNano('0.05')) + .storeBit(1) + .storeRef(beginCell().endCell()) + .endCell(); + + const transaction = { + validUntil: Math.floor(Date.now() / 1000) + 360, + messages: [ + { + address: '', + amount: toNano('0.05').toString(), + payload: body.toBoc().toString('base64'), + }, + ], + }; + + const result = await tonConnectUI.sendTransaction(transaction); + ``` + + +Where: + +- `` — the recipient's TON wallet address. +- `` — the sender's TON wallet address (receives excess fees). +- `` — the sender's jetton wallet contract address. To find it, call the `get_wallet_address` get method on the jetton master contract, passing the sender's wallet address. See the [low-level example](#transfer-using-a-wallet-contract) below for the full derivation. + + + +### Transfer with a comment + +To attach a text comment, serialize it in the `forward_payload` field with opcode `0` (text comment). This snippet builds only the body — send it the same way as a [regular transfer](#transfer-via-ton-connect) above: + +```ts +import { beginCell, toNano, Address } from '@ton/ton'; + +const forwardPayload = beginCell() + .storeUint(0, 32) // 0 opcode = text comment + .storeStringTail('Hello, TON!') + .endCell(); + +const body = beginCell() + .storeUint(0x0f8a7ea5, 32) + .storeUint(0, 64) + .storeCoins(toNano('5')) // jetton amount (decimals vary) + .storeAddress(Address.parse('')) + .storeAddress(Address.parse('')) + .storeBit(0) // no custom payload + .storeCoins(toNano('0.02')) // forward amount + .storeBit(1) // forward_payload as reference + .storeRef(forwardPayload) + .endCell(); +``` + +### Burn via TON Connect + +The burn body follows [TEP-74 burn](https://github.com/ton-blockchain/TEPs/blob/master/text/0074-jettons-standard.md#2-burn) (`burn#595f07bc`): + + + ```tsx title="React" icon="react" + import { useTonConnectUI } from '@tonconnect/ui-react'; + import { beginCell, toNano, Address } from '@ton/ton'; + + // burn#595f07bc query_id:uint64 amount:(VarUInteger 16) + // response_destination:MsgAddress custom_payload:(Maybe ^Cell) + + const body = beginCell() + .storeUint(0x595f07bc, 32) // jetton burn op code + .storeUint(0, 64) // query_id + .storeCoins(toNano('0.001')) // burn amount (decimals vary) + .storeAddress(Address.parse('')) // response_destination + .storeUint(0, 1) // no custom payload + .endCell(); + + const transaction = { + validUntil: Math.floor(Date.now() / 1000) + 360, + messages: [ + { + address: '', // owner's jetton wallet + amount: toNano('0.05').toString(), + payload: body.toBoc().toString('base64'), + }, + ], + }; + + export const BurnJetton = () => { + const [tonConnectUI] = useTonConnectUI(); + + return ( + + ); + }; + ``` + + ```ts title="TypeScript" icon="globe" + import TonConnectUI from '@tonconnect/ui'; + import { beginCell, toNano, Address } from '@ton/ton'; + + const body = beginCell() + .storeUint(0x595f07bc, 32) + .storeUint(0, 64) + .storeCoins(toNano('0.001')) + .storeAddress(Address.parse('')) + .storeUint(0, 1) + .endCell(); + + const transaction = { + validUntil: Math.floor(Date.now() / 1000) + 360, + messages: [ + { + address: '', + amount: toNano('0.05').toString(), + payload: body.toBoc().toString('base64'), + }, + ], + }; + + const result = await tonConnectUI.sendTransaction(transaction); + ``` + + +Where: + +- `` — the wallet address of the jetton owner (receives excess fees). +- `` — the owner's jetton wallet contract address. + +## Transfer using a wallet contract + For reference, here's a low-level example of the process, where message serialization is done manually. ```ts expandable diff --git a/standard/tokens/nft/transfer.mdx b/standard/tokens/nft/transfer.mdx index c48500d64..c5854273a 100644 --- a/standard/tokens/nft/transfer.mdx +++ b/standard/tokens/nft/transfer.mdx @@ -73,3 +73,252 @@ where: - `` — the address of the NFT item to be transferred. - `` — the address of the recipient. + +## Transfer via TON Connect + +To send an NFT from a dApp through [TON Connect](/ecosystem/ton-connect/overview), build the transfer body and submit it to the NFT item contract: + + + ```tsx title="React" icon="react" + import { useTonConnectUI } from '@tonconnect/ui-react'; + import { beginCell, toNano, Address } from '@ton/ton'; + + // transfer#5fcc3d14 query_id:uint64 new_owner:MsgAddress response_destination:MsgAddress + // custom_payload:(Maybe ^Cell) forward_amount:(VarUInteger 16) forward_payload:(Either Cell ^Cell) + + const body = beginCell() + .storeUint(0x5fcc3d14, 32) // NFT transfer op code + .storeUint(0, 64) // query_id + .storeAddress(Address.parse('')) // new_owner + .storeAddress(Address.parse('')) // response_destination (excess fees) + .storeUint(0, 1) // no custom_payload + .storeCoins(toNano('0.0001')) // forward_amount + .storeUint(0, 1) // no forward_payload + .endCell(); + + const transaction = { + validUntil: Math.floor(Date.now() / 1000) + 360, + messages: [ + { + address: '', // NFT item contract address + amount: toNano('0.05').toString(), // for gas fees, excess is returned + payload: body.toBoc().toString('base64'), + }, + ], + }; + + export const TransferNft = () => { + const [tonConnectUI] = useTonConnectUI(); + + return ( + + ); + }; + ``` + + ```ts title="TypeScript" icon="globe" + import TonConnectUI from '@tonconnect/ui'; + import { beginCell, toNano, Address } from '@ton/ton'; + + const body = beginCell() + .storeUint(0x5fcc3d14, 32) + .storeUint(0, 64) + .storeAddress(Address.parse('')) + .storeAddress(Address.parse('')) + .storeUint(0, 1) + .storeCoins(toNano('0.0001')) + .storeUint(0, 1) + .endCell(); + + const transaction = { + validUntil: Math.floor(Date.now() / 1000) + 360, + messages: [ + { + address: '', + amount: toNano('0.05').toString(), + payload: body.toBoc().toString('base64'), + }, + ], + }; + + const result = await tonConnectUI.sendTransaction(transaction); + ``` + + +Where: + +- `` — the TON wallet address of the new NFT owner. +- `` — the current owner's wallet address (receives excess fees). +- `` — the address of the NFT item contract to transfer. + +### List an NFT for sale (GetGems) + +To place an NFT on the [GetGems](https://getgems.io) marketplace, transfer it to a sale contract using [nft-fixprice-sale-v3r3](https://github.com/getgems-io/nft-contracts/blob/main/packages/contracts/sources/nft-fixprice-sale-v3r3.fc). The transfer body wraps a `do_sale` opcode with the sale contract's state init and body. This snippet shows only the transfer body — the full expandable example below includes `stateInitCell` and `saleBody` construction: + +```ts +import { beginCell, toNano, Address } from '@ton/ton'; + +const transferNftBody = beginCell() + .storeUint(0x5fcc3d14, 32) // NFT transfer op code + .storeUint(0, 64) // query_id + .storeAddress(Address.parse('')) // new_owner — GetGems sale deployer + .storeAddress(Address.parse('')) // response_destination + .storeBit(0) // no custom_payload + .storeCoins(toNano('0.2')) // forward_amount + .storeBit(0) // forward_payload in this cell + .storeUint(0x0fe0ede, 31) // do_sale opcode (31 bits — prior 0 bit is consumed) + .storeRef(stateInitCell) + .storeRef(saleBody) + .endCell(); +``` + + + +The full algorithm constructs the sale contract state init (with price, fees, royalties) and the sale body: + +```ts expandable +import { + Address, + beginCell, + StateInit, + storeStateInit, + toNano, + Cell, +} from '@ton/ton'; + +async function main() { + // If GetGems updates its sale contract, obtain the new code from: + // https://github.com/getgems-io/nft-contracts/blob/main/packages/contracts/nft-fixprice-sale-v3/NftFixpriceSaleV3.source.ts + const NftFixPriceSaleV3R3CodeBoc = + 'te6ccgECDwEAA5MAART/APSkE/S88sgLAQIBYgIDAgLNBAUCASANDgL30A6GmBgLjYSS+CcH0gGHaiaGmAaY/9IH0gfSB9AGppj+mfmBg4KYVjgGAASpiFaY+F7xDhgEoYBWmfxwjFsxsLcxsrZBZjgsk5mW8oBfEV4ADJL4dwEuuk4QEWQIEV3RXgAJFZ2Ngp5OOC2HGBFWAA+WjKFkEINjYQQF1AYHAdFmCEAX14QBSYKBSML7y4cIk0PpA+gD6QPoAMFOSoSGhUIehFqBSkCH6RFtwgBDIywVQA88WAfoCy2rJcfsAJcIAJddJwgKwjhtQRSH6RFtwgBDIywVQA88WAfoCy2rJcfsAECOSNDTiWoMAGQwMWyy1DDQ0wchgCCw8tGVIsMAjhSBAlj4I1NBobwE+CMCoLkTsPLRlpEy4gHUMAH7AATwU8fHBbCOXRNfAzI3Nzc3BPoA+gD6ADBTIaEhocEB8tGYBdD6QPoA+kD6ADAwyDICzxZY+gIBzxZQBPoCyXAgEEgQNxBFEDQIyMsAF8sfUAXPFlADzxYBzxYB+gLMyx/LP8ntVOCz4wIwMTcowAPjAijAAOMCCMACCAkKCwCGNTs7U3THBZJfC+BRc8cF8uH0ghAFE42RGLry4fX6QDAQSBA3VTIIyMsAF8sfUAXPFlADzxYBzxYB+gLMyx/LP8ntVADiODmCEAX14QAYvvLhyVNGxwVRUscFFbHy4cpwIIIQX8w9FCGAEMjLBSjPFiH6Astqyx8Vyz8nzxYnzxYUygAj+gITygDJgwb7AHFwVBcAXjMQNBAjCMjLABfLH1AFzxZQA88WAc8WAfoCzMsfyz/J7VQAGDY3EDhHZRRDMHDwBQAgmFVEECQQI/AF4F8KhA/y8ADsIfpEW3CAEMjLBVADzxYB+gLLaslx+wBwIIIQX8w9FMjLH1Iwyz8kzxZQBM8WE8oAggnJw4D6AhLKAMlxgBjIywUnzxZw+gLLaswl+kRbyYMG+wBxVWD4IwEIyMsAF8sfUAXPFlADzxYBzxYB+gLMyx/LP8ntVACHvOFnaiaGmAaY/9IH0gfSB9AGppj+mfmC3ofSB9AH0gfQAYKaFQkNDggPlozJP9Ii2TfSItkf0iLcEIIySsKAVgAKrAQAgb7l72omhpgGmP/SB9IH0gfQBqaY/pn5gBaH0gfQB9IH0AGCmxUJDQ4ID5aM0U/SItlH0iLZH9Ii2F4ACFiBqqiU'; + const NftFixPriceSaleV3R3CodeCell = Cell.fromBoc( + Buffer.from(NftFixPriceSaleV3R3CodeBoc, 'base64'), + )[0]; + + const marketplaceAddress = Address.parse( + 'EQBYTuYbLf8INxFtD8tQeNk5ZLy-nAX9ahQbG_yl1qQ-GEMS', + ); // GetGems marketplace + const marketplaceFeeAddress = Address.parse( + 'EQCjk1hh952vWaE9bRguFkAhDAL5jj3xj9p0uPWrFBq_GEMS', + ); // GetGems fee address + const destinationAddress = Address.parse( + 'EQAIFunALREOeQ99syMbO6sSzM_Fa1RsPD5TBoS0qVeKQ-AR', + ); // GetGems sale deployer + + const walletAddress = Address.parse(''); + const royaltyAddress = Address.parse(''); + const nftAddress = Address.parse(''); + const price = toNano('5'); // 5 TON + + const feesData = beginCell() + .storeAddress(marketplaceFeeAddress) + .storeCoins((price / BigInt(100)) * BigInt(5)) // 5% GetGems fee + .storeAddress(royaltyAddress) + .storeCoins((price / BigInt(100)) * BigInt(5)) // 5% royalty + .endCell(); + + const saleData = beginCell() + .storeBit(0) // is_complete + .storeUint(Math.round(Date.now() / 1000), 32) // created_at + .storeAddress(marketplaceAddress) + .storeAddress(nftAddress) + .storeAddress(walletAddress) // previous_owner + .storeCoins(price) + .storeRef(feesData) + .storeUint(0, 32) // sold_at + .storeUint(0, 64) // query_id + .endCell(); + + const stateInit: StateInit = { + code: NftFixPriceSaleV3R3CodeCell, + data: saleData, + }; + const stateInitCell = beginCell().store(storeStateInit(stateInit)).endCell(); + + const saleBody = beginCell() + .storeUint(1, 32) // accept coins on deploy + .storeUint(0, 64) + .endCell(); + + const transferNftBody = beginCell() + .storeUint(0x5fcc3d14, 32) + .storeUint(0, 64) + .storeAddress(destinationAddress) // new_owner + .storeAddress(walletAddress) // response_destination + .storeBit(0) + .storeCoins(toNano('0.2')) // forward_amount + .storeBit(0) + .storeUint(0x0fe0ede, 31) // do_sale opcode + .storeRef(stateInitCell) + .storeRef(saleBody) + .endCell(); + + // Send transferNftBody to the NFT item contract with at least 1.08 TON +} +``` + +### Buy an NFT (GetGems) + +To buy from a [nft-fixprice-sale-v3r3](https://github.com/getgems-io/nft-contracts/blob/main/packages/contracts/sources/nft-fixprice-sale-v3r3.fc) sale contract, send a regular transfer **without** payload. Amount = NFT price + 1 TON for fees: + + + ```tsx title="React" icon="react" + import { useTonConnectUI } from '@tonconnect/ui-react'; + import { toNano } from '@ton/ton'; + + const nftPrice = 5.0; // TON + const buyAmount = nftPrice + 1.0; + + const transaction = { + validUntil: Math.floor(Date.now() / 1000) + 360, + messages: [ + { + address: '', // sale contract address + amount: toNano(buyAmount).toString(), // price + 1 TON, excess returned + }, + ], + }; + + export const BuyNft = () => { + const [tonConnectUI] = useTonConnectUI(); + + return ( + + ); + }; + ``` + + ```ts title="TypeScript" icon="globe" + import TonConnectUI from '@tonconnect/ui'; + import { toNano } from '@ton/ton'; + + const nftPrice = 5.0; + const buyAmount = nftPrice + 1.0; + + const transaction = { + validUntil: Math.floor(Date.now() / 1000) + 360, + messages: [ + { + address: '', + amount: toNano(buyAmount).toString(), + }, + ], + }; + + const result = await tonConnectUI.sendTransaction(transaction); + ``` + + +Where `` is the address of the GetGems sale contract for the desired NFT item. + +## See also + +- [dApp integration with TON Connect](/ecosystem/ton-connect/dapp) +- [Jetton transfer](/standard/tokens/jettons/transfer) +- [NFT standard overview](/standard/tokens/nft/how-it-works) From 090c93987a6c150db366899e4b8cc9f08e43e779 Mon Sep 17 00:00:00 2001 From: Coalus Date: Mon, 13 Apr 2026 08:27:12 +0300 Subject: [PATCH 2/5] fix(standard): fix formatting --- standard/tokens/nft/transfer.mdx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/standard/tokens/nft/transfer.mdx b/standard/tokens/nft/transfer.mdx index c5854273a..937a2a865 100644 --- a/standard/tokens/nft/transfer.mdx +++ b/standard/tokens/nft/transfer.mdx @@ -174,7 +174,9 @@ const transferNftBody = beginCell() .endCell(); ``` -