Message body
When working with TON Connect you’ll need to craft a message body — a payload encoded in cells — for any on-chain action other than a simple TON transfer. This page explains the cell–building basics; concrete payload recipes are given on the following Cookbook pages.
Cells and message serialization
Before diving into building messages, let's introduce the concept of cells, which message bodies are made from.
What is a cell?
A cell is a basic data structure in the TON Blockchain. It can store up to 1023
bits and hold up to 4
references to other cells, which allows you to store more complex data structures.
Libraries like @ton/core
and @ton-community/assets-sdk
provide efficient cell handling.
You can read more about cells here.
Creating a cell
To build a cell, you use the beginCell()
function. While the cell is open, you can store various data types with store...()
functions.
When you're done, you close the cell with the endCell()
function.
import { Address, beginCell } from "@ton/ton";
const cell = beginCell()
.storeUint(99, 64) // Stores uint 99 in 64 bits
.storeAddress(Address.parse("[SOME_ADDR]")) // Stores an address
.storeCoins(123) // Stores 123 as coins
.endCell(); // Closes the cell
These examples use the @ton/ton
library only.
However, every TON SDK offers equivalent helper functions.
Parsing a cell
The beginParse()
function is called to read or parse data from a cell. You read data in the same order it was stored using similar load...()
functions:
const slice = cell.beginParse();
const uint = slice.loadUint(64);
const address = slice.loadAddress();
const coins = slice.loadCoins();
Larger amounts of data
Each cell has a 1023-bit limit. If you exceed this, an error occurs:
// This will fail due to overflow
const cell = beginCell()
.storeUint(1, 256)
.storeUint(2, 256)
.storeUint(3, 256)
.storeUint(4, 256) // Exceeds 1023-bit limit (256 + 256 + 256 + 256 = 1024)
.endCell();
To store more data, cells can reference up to four other cells. You can use the storeRef()
function to create nested cells:
const cell = beginCell()
.storeUint(1, 256)
.storeUint(2, 256)
.storeRef(beginCell().storeUint(3, 256).storeUint(4, 256).endCell())
.endCell();
To load a referenced (nested) cell, use loadRef()
:
const slice = cell.beginParse();
const uint1 = slice.loadUint(256);
const uint2 = slice.loadUint(256);
const innerSlice = slice.loadRef().beginParse(); // Load and parse nested cell
const uint3 = innerSlice.loadUint(256);
const uint4 = innerSlice.loadUint(256);
Optional references and values
You can store optional (nullable) values in cells by using the storeMaybe...()
helpers:
const cell = beginCell()
.storeMaybeInt(null, 64) // Optionally stores an int
.storeMaybeInt(1, 64)
.storeMaybeRef(null) // Optionally stores a reference
.storeMaybeRef(beginCell().storeCoins(123).endCell());
You can parse optional values using the corresponding loadMaybe...()
functions. Returned values are nullable so do not forget to check them for null!
const slice = cell.beginParse();
const maybeInt = slice.loadMaybeUint(64);
const maybeInt1 = slice.loadMaybeUint(64);
const maybeRef = slice.loadMaybeRef();
const maybeRef1 = slice.loadMaybeRef();
if (maybeRef1) {
const coins = maybeRef1.beginParse().loadCoins();
}
Using assets SDK
Manually handling cells can be tedious, so the @ton-community/assets-sdk
provides convenient methods for serializing and deserializing messages.
Using @ton-community/assets-sdk
is more readable and less error-prone.
- @ton-community/assets-sdk
- @ton/ton
import {Address, beginCell} from "@ton/core";
import {storeJettonTransferMessage, loadJettonTransferMessage} from "@ton-community/assets-sdk";
// serialization
const cell = beginCell()
.store(storeJettonTransferMessage({
queryId: 42n,
amount: 100n,
destination: Address.parse('[DESTINATION]'),
responseDestination: Address.parse('[RESPONSE_DESTINATION]'),
customPayload: null,
forwardAmount: 1n,
forwardPayload: null,
}))
.endCell()
// deserialization
const transferMessage = loadJettonTransferMessage(cell.beginParse());
import {Address, beginCell} from "@ton/core";
// serialization
const cell = beginCell()
.storeUint(260734629, 32)
.storeUint(42, 64)
.storeCoins(100)
.storeAddress(Address.parse('[DESTINATION]'))
.storeAddress(Address.parse('[RESPONSE_DESTINATION]'))
.storeMaybeRef(null)
.storeCoins(1)
.storeMaybeRef(null)
.endCell();
// deserialization
const slice = cell.beginParse();
const op = slice.loadUint(32);
const queryId = slice.loadUint(64);
const amount = slice.loadCoins();
const destination = slice.loadAddress();
const responseDestination = slice.loadAddress();
const customPayload = slice.loadMaybeRef();
const fwdAmount = slice.loadCoins();
const fwdPayload = slice.loadMaybeRef();
const transferMessage = { op, queryId, amount, destination, responseDestination, customPayload, fwdAmount, fwdPayload };
Next steps
Check the TON transfers examples and craft custom transfers.