Jetton transfer
The body
for jetton transfers is based on the (TEP-74) standard. Please note that the number of decimals can vary between different tokens: for example, USDT uses 6 decimals (1 USDT = 1 × 106), while typically jettons and Toncoin uses 9 decimals (1 TON = 1 × 109).
The assets-sdk
library works out of the box with ton-connect
.
- @ton/ton
- assets/sdk
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(0xf8a7ea5, 32) // jetton transfer op code
.storeUint(0, 64) // query_id:uint64
.storeCoins(toNano("0.001")) // amount:(VarUInteger 16) - Jetton amount for transfer (decimals = 6 - USDT, 9 - default). Function toNano use decimals = 9 (remember it)
.storeAddress(Address.parse(Wallet_DST)) // destination:MsgAddress
.storeAddress(Address.parse(Wallet_SRC)) // response_destination:MsgAddress
.storeUint(0, 1) // custom_payload:(Maybe ^Cell)
.storeCoins(toNano("0.05")) // forward_ton_amount:(VarUInteger 16) - if >0, will send notification message
.storeUint(0,1) // forward_payload:(Either Cell ^Cell)
.endCell();
Next, sending the transaction with this body to sender's jettonWalletContract
executed:
- @tonconnect/react-ui
- @tonconnect/ui
import { useTonConnectUI } from '@tonconnect/ui-react';
import { toNano } from '@ton/ton'
const myTransaction = {
validUntil: Math.floor(Date.now() / 1000) + 360,
messages: [
{
address: jettonWalletContract, // sender jetton wallet
amount: toNano("0.05").toString(), // for commission fees, excess will be returned
payload: body.toBoc().toString("base64") // payload with jetton transfer body
}
]
}
export const Settings = () => {
const [tonConnectUI, setOptions] = useTonConnectUI();
return (
<div>
<button onClick={() => tonConnectUI.sendTransaction(myTransaction)}>
Send transaction
</button>
</div>
);
};
import TonConnectUI from '@tonconnect/ui'
import { toNano } from '@ton/ton'
const transaction = {
validUntil: Math.floor(Date.now() / 1000) + 360,
messages: [
{
address: jettonWalletContract, // sender jetton wallet
amount: toNano("0.05").toString(), // for commission fees, excess will be returned
payload: body.toBoc().toString("base64") // payload with jetton transfer body
}
]
}
const result = await tonConnectUI.sendTransaction(transaction)
validUntil
- UNIX-time until message validjettonWalletAddress
- Address, JettonWallet address, that defined based on JettonMaser and Wallet contractsbalance
- Integer, the amount of Toncoin used for gas payments in nanotons.body
- payload for thejettonContract
Jetton wallet state init and address preparation example
import { Address, TonClient, beginCell, StateInit, storeStateInit } from '@ton/ton'
async function main() {
const client = new TonClient({
endpoint: 'https://toncenter.com/api/v2/jsonRPC',
apiKey: 'put your api key'
})
const jettonWalletAddress = Address.parse('Sender_Jetton_Wallet');
let jettonWalletDataResult = await client.runMethod(jettonWalletAddress, 'get_wallet_data');
jettonWalletDataResult.stack.readNumber();
const ownerAddress = jettonWalletDataResult.stack.readAddress();
const jettonMasterAddress = jettonWalletDataResult.stack.readAddress();
const jettonCode = jettonWalletDataResult.stack.readCell();
const jettonData = beginCell()
.storeCoins(0)
.storeAddress(ownerAddress)
.storeAddress(jettonMasterAddress)
.storeRef(jettonCode)
.endCell();
const stateInit: StateInit = {
code: jettonCode,
data: jettonData
}
const stateInitCell = beginCell()
.store(storeStateInit(stateInit))
.endCell();
console.log(new Address(0, stateInitCell.hash()));
}
Note: For the browser, you must set a polyfill for Buffer
.
For more examples, check documentation
const NETWORK = "testnet";
const api = await createApi(NETWORK);
const provider = new TonConnectUI(); // OR you can use tonConnectUI as a provider from @tonconnect/ui-react
// https://github.com/ton-community/assets-sdk/blob/main/examples/use-tonconnect.ts
const sender = new TonConnectSender(provider);
const storage: PinataStorageParams = {
pinataApiKey: process.env.PINATA_API_KEY!,
pinataSecretKey: process.env.PINATA_SECRET!,
};
const sdk = AssetsSDK.create({
api,
storage,
sender,
});
const jetton = sdk.openJettonWallet(Address.parse("JETTON_ADDRESS"));
const RECEIVER_ADDRESS = Address.parse("RECIEVER_ADDRESS");
jetton.send(sender, RECEIVER_ADDRESS, toNano(10));
OR you can use Jetton Contract with built-in methods:
const provider = tonConnectUi;
// https://github.com/ton-community/assets-sdk/blob/main/examples/use-tonconnect.ts
const sender = new TonConnectSender(provider);
const client = new TonClient({
endpoint: "https://testnet.toncenter.com/api/v2/jsonRPC",
});
const jettonMaster = client.open(
JettonMinter.createFromAddress(
Address.parse("[JETTON_WALLET]"),
new DefaultContentResolver()
)
);
const jettonWalletAddress = await jettonMaster.getWalletAddress(
sender.address!
);
const jettonContent = await jettonMaster.getContent();
const jettonDecimals = jettonContent.decimals ?? 9;
const jetton = client.open(JettonWallet.createFromAddress(jettonWalletAddress));
await jetton.send(
sender,
Address.parse("[SENDER_WALLET]"),
BigInt(1 * 10 ** jettonDecimals)
);
Jetton transfer with comment
- @ton/ton
- assets/sdk
The messageBody
for jetton transfer(TEP-74) with comment we should additionally to the regular transfer body
serialize comment and pack this in the forwardPayload
. Please note that the number of decimals can vary between different tokens:
- 9 decimals: 1 TON = 1 × 109 typically for various jettons and always for Toncoin
- 6 decimals: 1 USDT = 1 × 106 specific jettons, like USDT
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 destinationAddress = Address.parse('put destination wallet address');
const forwardPayload = beginCell()
.storeUint(0, 32) // 0 opcode means we have a comment
.storeStringTail('Hello, TON!')
.endCell();
const body = beginCell()
.storeUint(0xf8a7ea5, 32) // opcode for jetton transfer
.storeUint(0, 64) // query id
.storeCoins(toNano("5")) // Jetton amount for transfer (decimals = 6 - USDT, 9 - default). Function toNano use decimals = 9 (remember it)
.storeAddress(destinationAddress) // TON wallet destination address
.storeAddress(destinationAddress) // response excess destination
.storeBit(0) // no custom payload
.storeCoins(toNano("0.02")) // forward amount (if >0, will send notification message)
.storeBit(1) // we store forwardPayload as a reference
.storeRef(forwardPayload)
.endCell();
Next, send the transaction with this body to the sender's jettonWalletContract
executed:
- @tonconnect/react-ui
- @tonconnect/ui
import { useTonConnectUI } from '@tonconnect/ui-react';
import { toNano } from '@ton/ton'
const jettonWalletContract = Address.parse('put your jetton wallet address');
const myTransaction = {
validUntil: Math.floor(Date.now() / 1000) + 360,
messages: [
{
address: jettonWalletContract, // sender jetton wallet
amount: toNano("0.05").toString(), // for commission fees, excess will be returned
payload: body.toBoc().toString("base64") // payload with jetton transfer and comment body
}
]
}
export const Settings = () => {
const [tonConnectUI, setOptions] = useTonConnectUI();
return (
<div>
<button onClick={() => tonConnectUI.sendTransaction(myTransaction)}>
Send transaction
</button>
</div>
);
};
import TonConnectUI from '@tonconnect/ui'
import { toNano } from '@ton/ton'
const transaction = {
validUntil: Math.floor(Date.now() / 1000) + 360,
messages: [
{
address: jettonWalletContract, // sender jetton wallet
amount: toNano("0.05").toString(), // for commission fees, excess will be returned
payload: body.toBoc().toString("base64") // payload with jetton transfer and comment body
}
]
}
const result = await tonConnectUI.sendTransaction(transaction)
validUntil
- UNIX-time until message validjettonWalletAddress
- Address, JettonWallet address, that defined based on JettonMaser and Wallet contractsbalance
- Integer, the amount of Toncoin used for gas payments in nanotons.body
- payload for thejettonContract
Jetton wallet state init and address preparation example
import { Address, TonClient, beginCell, StateInit, storeStateInit } from '@ton/ton'
async function main() {
const client = new TonClient({
endpoint: 'https://toncenter.com/api/v2/jsonRPC',
apiKey: 'put your api key'
})
const jettonWalletAddress = Address.parse('Sender_Jetton_Wallet');
let jettonWalletDataResult = await client.runMethod(jettonWalletAddress, 'get_wallet_data');
jettonWalletDataResult.stack.readNumber();
const ownerAddress = jettonWalletDataResult.stack.readAddress();
const jettonMasterAddress = jettonWalletDataResult.stack.readAddress();
const jettonCode = jettonWalletDataResult.stack.readCell();
const jettonData = beginCell()
.storeCoins(0)
.storeAddress(ownerAddress)
.storeAddress(jettonMasterAddress)
.storeRef(jettonCode)
.endCell();
const stateInit: StateInit = {
code: jettonCode,
data: jettonData
}
const stateInitCell = beginCell()
.store(storeStateInit(stateInit))
.endCell();
console.log(new Address(0, stateInitCell.hash()));
}
Note: For the browser, you must set a polyfill for Buffer
.
For more examples, check documentation
const NETWORK = "testnet";
const api = await createApi(NETWORK);
const provider = new TonConnectUI(); // OR you can use tonConnectUI as a provider from @tonconnect/ui-react
// https://github.com/ton-community/assets-sdk/blob/main/examples/use-tonconnect.ts
const sender = new TonConnectSender(provider);
const storage: PinataStorageParams = {
pinataApiKey: process.env.PINATA_API_KEY!,
pinataSecretKey: process.env.PINATA_SECRET!,
};
const sdk = AssetsSDK.create({
api,
storage,
sender,
});
const jetton = sdk.openJettonWallet(Address.parse("JETTON_ADDRESS"));
const forwardPayload = beginCell()
.storeUint(0, 32) // 0 opcode means we have a comment
.storeStringTail('Hello, TON!')
.endCell();
jetton.send(sender, RECEIVER_ADDRESS, toNano(10), { notify: { payload: forwardPayload } });
Jetton burn
- @ton/ton
- assets/sdk
The body
for jetton burn is based on the (TEP-74) standard. Please note that the number of decimals can vary between different tokens:
- 9 decimals: 1 TON = 1 × 109 typically for various jettons and always for Toncoin
- 6 decimals: 1 USDT = 1 × 106 specific jettons, like USDT
import { beginCell, Address } from '@ton/ton'
// burn#595f07bc query_id:uint64 amount:(VarUInteger 16)
// response_destination:MsgAddress custom_payload:(Maybe ^Cell)
// = InternalMsgBody;
const body = beginCell()
.storeUint(0x595f07bc, 32) // jetton burn op code
.storeUint(0, 64) // query_id:uint64
.storeCoins(toNano("0.001")) // amount:(VarUInteger 16) - Jetton amount in decimal (decimals = 6 - USDT, 9 - default). Function toNano use decimals = 9 (remember it)
.storeAddress(Address.parse(Wallet_SRC)) // response_destination:MsgAddress - owner's wallet
.storeUint(0, 1) // custom_payload:(Maybe ^Cell) - w/o payload typically
.endCell();
Message places into the following request:
- @tonconnect/react-ui
- @tonconnect/ui
import { useTonConnectUI } from '@tonconnect/ui-react';
import { toNano } from '@ton/ton'
const myTransaction = {
validUntil: Math.floor(Date.now() / 1000) + 360,
messages: [
{
address: jettonWalletContract, // owner's jetton wallet
amount: toNano("0.05").toString(), // for commission fees, excess will be returned
payload: body.toBoc().toString("base64") // payload with a jetton burn body
}
]
}
export const Settings = () => {
const [tonConnectUI, setOptions] = useTonConnectUI();
return (
<div>
<button onClick={() => tonConnectUI.sendTransaction(myTransaction)}>
Send transaction
</button>
</div>
);
};
import TonConnectUI from '@tonconnect/ui'
import { toNano } from '@ton/ton'
const transaction = {
validUntil: Math.floor(Date.now() / 1000) + 360,
messages: [
{
address: jettonWalletContract, // owner's jetton wallet
amount: toNano("0.05").toString(), // for commission fees, excess will be returned
payload: body.toBoc().toString("base64") // payload with a jetton burn body
}
]
}
const result = await tonConnectUI.sendTransaction(transaction)
jettonWalletAddress
- Jetton Wallet contract address, that defined based on JettonMaser and Wallet contractsamount
- Integer, amount of Toncoin for gas payments in nanotons.body
- payload for the jetton wallet with theburn#595f07bc
op code
Note: For the browser, you must set a polyfill for Buffer
.
For more examples, check documentation
const NETWORK = "testnet";
const api = await createApi(NETWORK);
const provider = new TonConnectUI(); // OR you can use tonConnectUI as a provider from @tonconnect/ui-react
// https://github.com/ton-community/assets-sdk/blob/main/examples/use-tonconnect.ts
const sender = new TonConnectSender(provider);
const storage: PinataStorageParams = {
pinataApiKey: process.env.PINATA_API_KEY!,
pinataSecretKey: process.env.PINATA_SECRET!,
};
const sdk = AssetsSDK.create({
api,
storage,
sender,
});
const jetton = sdk.openJettonWallet(Address.parse("JETTON_ADDRESS"));
jetton.sendBurn(sender, toNano(10));
OR you can use Jetton Contract with built-in methods:
const provider = tonConnectUi;
// https://github.com/ton-community/assets-sdk/blob/main/examples/use-tonconnect.ts
const sender = new TonConnectSender(provider);
const client = new TonClient({
endpoint: "https://testnet.toncenter.com/api/v2/jsonRPC",
});
const jettonMaster = client.open(
JettonMinter.createFromAddress(
Address.parse("[JETTON_WALLET]"),
new DefaultContentResolver()
)
);
const jettonWalletAddress = await jettonMaster.getWalletAddress(
sender.address!
);
const jettonContent = await jettonMaster.getContent();
const jettonDecimals = jettonContent.decimals ?? 9;
const jetton = client.open(JettonWallet.createFromAddress(jettonWalletAddress));
await jetton.sendBurn(
sender,
BigInt(1 * 10 ** jettonDecimals)
);
Next steps
- Work with NFTs → NFT transfer
- Search your transaction → Transaction lookup