Skip to content

Fungible Tokens (Jettons)

This content is not available in your language yet.

This page lists common examples of working with jettons.

Accepting jetton transfer

Transfer notification message have the following structure.

message(0x7362d09c) JettonTransferNotification {
queryId: Int as uint64;
amount: Int as coins;
sender: Address;
forwardPayload: Slice as remaining;
}

Use receiver function to accept token notification message.

Validation can be done using jetton wallet state init and calculating jetton address. Note, that notifications are coming from YOUR contract’s jetton wallet, so myAddress() should be used in owner address field. Wallet initial data layout is shown below, but sometimes it can differ. Note that myJettonWalletAddress may also be stored in contract storage to use less gas in every transaction.

struct JettonWalletData {
balance: Int as coins;
ownerAddress: Address;
jettonMasterAddress: Address;
jettonWalletCode: Cell;
}
fun calculateJettonWalletAddress(ownerAddress: Address, jettonMasterAddress: Address, jettonWalletCode: Cell): Address {
let initData = JettonWalletData{
balance: 0,
ownerAddress,
jettonMasterAddress,
jettonWalletCode,
};
return contractAddress(StateInit{code: jettonWalletCode, data: initData.toCell()});
}
contract Sample {
jettonWalletCode: Cell;
jettonMasterAddress: Address;
init(jettonWalletCode: Cell, jettonMasterAddress: Address) {
self.jettonWalletCode = jettonWalletCode;
self.jettonMasterAddress = jettonMasterAddress;
}
receive(msg: JettonTransferNotification) {
let myJettonWalletAddress = calculateJettonWalletAddress(myAddress(), self.jettonMasterAddress, self.jettonWalletCode);
require(sender() == myJettonWalletAddress, "Notification not from your jetton wallet!");
// your logic of processing token notification
}
}

Sending jetton transfer

To send jetton transfer use send() function. Note that myJettonWalletAddress may also be stored in contract storage to use less gas in every transaction.

message(0xf8a7ea5) JettonTransfer {
queryId: Int as uint64;
amount: Int as coins;
destination: Address;
responseDestination: Address?;
customPayload: Cell? = null;
forwardTonAmount: Int as coins;
forwardPayload: Slice as remaining;
}
receive("send") {
let myJettonWalletAddress = calculateJettonWalletAddress(myAddress(), self.jettonMasterAddress, self.jettonWalletCode);
send(SendParameters{
to: myJettonWalletAddress,
value: ton("0.05"),
body: JettonTransfer{
queryId: 42,
amount: jettonAmount, // jetton amount you want to transfer
destination: msg.userAddress, // address you want to transfer jettons. Note that this is address of jetton wallet owner, not jetton wallet itself
responseDestination: msg.userAddress, // address where to send a response with confirmation of a successful transfer and the rest of the incoming message Toncoins
customPayload: null, // in most cases will be null and can be omitted. Needed for custom logic on Jetton Wallets itself
forwardTonAmount: 1, // amount that will be transferred with JettonTransferNotification. Needed for custom logic execution like in example below. If the amount is 0 notification won't be sent
forwardPayload: rawSlice("F") // precomputed beginCell().storeUint(0xF, 4).endCell().beginParse(). This works for simple transfer, if needed any struct can be used as `forwardPayload`
}.toCell(),
});
}

Burning jetton

message(0x595f07bc) JettonBurn {
queryId: Int as uint64;
amount: Int as coins;
responseDestination: Address?;
customPayload: Cell? = null;
}
receive("burn") {
let myJettonWalletAddress = calculateJettonWalletAddress(myAddress(), self.jettonMasterAddress, self.jettonWalletCode);
send(SendParameters{
to: myJettonWalletAddress,
body: JettonBurn{
queryId: 42,
amount: jettonAmount, // jetton amount you want to burn
responseDestination: someAddress, // address where to send a response with confirmation of a successful burn and the rest of the incoming message coins
customPayload: null, // in most cases will be null and can be omitted. Needed for custom logic on jettons itself
}.toCell(),
});
}

USDT jetton operations

Operations with USDT (on TON) remain the same, except that the JettonWalletData will have the following structure:

struct JettonWalletData {
status: Ins as uint4;
balance: Int as coins;
ownerAddress: Address;
jettonMasterAddress: Address;
}

Function to calculate wallet address will look like this:

fun calculateJettonWalletAddress(ownerAddress: Address, jettonMasterAddress: Address, jettonWalletCode: Cell): Address {
let initData = JettonWalletData{
status: 0,
balance: 0,
ownerAddress,
jettonMasterAddress,
};
return contractAddress(StateInit{code: jettonWalletCode, data: initData.toCell()});
}