Skip to main content

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).

info

The assets-sdk library works out of the box with ton-connect.

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:

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>
);
};

  • validUntil - UNIX-time until message valid
  • jettonWalletAddress - Address, JettonWallet address, that defined based on JettonMaser and Wallet contracts
  • balance - Integer, the amount of Toncoin used for gas payments in nanotons.
  • body - payload for the jettonContract
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()));
}

Jetton transfer with comment

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:

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>
);
};
  • validUntil - UNIX-time until message valid
  • jettonWalletAddress - Address, JettonWallet address, that defined based on JettonMaser and Wallet contracts
  • balance - Integer, the amount of Toncoin used for gas payments in nanotons.
  • body - payload for the jettonContract
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()));
}

Jetton burn

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:

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>
);
};
  • jettonWalletAddress - Jetton Wallet contract address, that defined based on JettonMaser and Wallet contracts
  • amount - Integer, amount of Toncoin for gas payments in nanotons.
  • body - payload for the jetton wallet with the burn#595f07bc op code

Next steps

  1. Work with NFTs → NFT transfer
  2. Search your transaction → Transaction lookup
Was this article useful?