Skip to main content

Sending messages

TON Connect has more powerful options than just authenticating users in the dApp; it also allows sending outgoing messages via connected wallets.

You will understand:

  • how to send messages from the DApp to the blockchain
  • how to send multiple messages in one transaction
  • how to deploy a contract using TON Connect

Playground page

We will use the low level TON Connect SDK for JavaScript. We'll experiment in the browser console on a page where the wallet is already connected. Here is the sample page:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="https://unpkg.com/@tonconnect/sdk@latest/dist/tonconnect-sdk.min.js"></script>
<script src="https://unpkg.com/[email protected]/dist/tonweb.js"></script>
</head>
<body>
<script>
window.onload = async () => {
window.connector = new TonConnectSDK.TonConnect({
manifestUrl: 'https://ratingers.pythonanywhere.com/ratelance/tonconnect-manifest.json'
});
connector.restoreConnection();
}
</script>
</body>
</html>

Feel free to copy-paste it into your browser console and run it.

Sending multiple messages

Understanding a task

We will send two messages in one transaction: one to your address, carrying 0.2 TON, and one to the other wallet address, carrying 0.1 TON.

By the way, there is a limit to the number of messages sent in one transaction:

  • standard (v3/v4) wallets: 4 outgoing messages;
  • highload wallets: 255 outgoing messages (close to blockchain limitations).

Sending the messages

Run the following code:

console.log(await connector.sendTransaction({
validUntil: Math.floor(new Date() / 1000) + 360,
messages: [
{
address: connector.wallet.account.address,
amount: "200000000"
},
{
address: "EQCyoez1VF4HbNNq5Rbqfr3zKuoAjKorhK-YZr7LIIiVrSD7",
amount: "100000000"
}
]
}));

You'll notice that this command does not print anything into the console, null or undefined, as functions returning nothing do. This means that connector.sendTransaction does not exit immediately.

Open your wallet application, and you'll see why. There is a request showing what you are sending and where the coins would go. Please, accept it.

Getting the result

The function will exit, and the output from the blockchain will be printed:

{
boc: "te6cckEBAwEA4QAC44gBZUPZ6qi8Dtmm1cot1P175lXUARlUVwlfMM19lkERK1oCUB3RqDxAFnPpeo191X/jiimn9Bwnq3zwcU/MMjHRNN5sC5tyymBV3SJ1rjyyscAjrDDFAIV/iE+WBySEPP9wCU1NGLsfcvVgAAACSAAYHAECAGhCAFlQ9nqqLwO2abVyi3U/XvmVdQBGVRXCV8wzX2WQRErWoAmJaAAAAAAAAAAAAAAAAAAAAGZCAFlQ9nqqLwO2abVyi3U/XvmVdQBGVRXCV8wzX2WQRErWnMS0AAAAAAAAAAAAAAAAAAADkk4U"
}

BoC is bag of cells, the way data is stored in TON. Now, we can decode it.

Decode this BoC in the tool of your choice, and you'll get the following tree of cells:

x{88016543D9EAA8BC0ED9A6D5CA2DD4FD7BE655D401195457095F30CD7D9641112B5A02501DD1A83C401673E97A8D7DD57FE38A29A7F41C27AB7CF0714FCC3231D134DE6C0B9B72CA6055DD2275AE3CB2B1C023AC30C500857F884F960724843CFF70094D4D18BB1F72F5600000024800181C_}
x{42005950F67AAA2F03B669B5728B753F5EF9957500465515C257CC335F6590444AD6A00989680000000000000000000000000000}
x{42005950F67AAA2F03B669B5728B753F5EF9957500465515C257CC335F6590444AD69CC4B40000000000000000000000000000}

This is a serialized external message, and two references are outgoing messages representations.

x{88016543D9EAA8BC0ED9A6D5CA2DD4FD7BE655D401195457095F30CD7D964111...
$10 ext_in_msg_info
$00 src:MsgAddressExt (null address)
"EQ..."a dest:MsgAddressInt (your wallet)
0 import_fee:Grams
$0 (no state_init)
$0 (body starts in this cell)
...

Returning the BoC of the sent transaction is to track it.

Processing transactions initiated with TON Connect

To find a transaction by extInMsg, you need to do the following:

  1. Parse the received extInMsg as a cell.
  2. Calculate the hash() of the obtained cell.
info

The received hash is what the sendBocReturnHash methods of TON Center API are already returning to you.

  1. Search for the required transaction using this hash through an indexer:

It's important to note that extInMsg may not be unique, which means collisions can occur. However, all transactions are unique. If you are using this for an informative display, this method should be sufficient. With standard wallet contracts, collisions can occur only in exceptional situations.

Sending complex transactions

Serialization of cells

Before we proceed, let's talk about the format of the messages we will send.

  • payload (string base64, optional): raw one-cell BoC encoded in Base64.
    • We will use it to store text comments on transfer
  • stateInit (string base64, optional): raw one-cell BoC encoded in Base64.
    • We will use it to deploy a smart contract

After building a message, you can serialize it into BoC.

TonWeb.utils.bytesToBase64(await payloadCell.toBoc())

Transfer with comment

You can use toncenter/tonweb JS SDK or your favourite tool to serialize cells to BoC.

Text comments on transfer are encoded as opcode 0 (32 zero bits) + UTF-8 bytes of comment. Here's an example of how to convert it into a bag of cells.

let a = new TonWeb.boc.Cell();
a.bits.writeUint(0, 32);
a.bits.writeString("TON Connect tutorial!");
let payload = TonWeb.utils.bytesToBase64(await a.toBoc());

console.log(payload);
// te6ccsEBAQEAHQAAADYAAAAAVE9OIENvbm5lY3QgMiB0dXRvcmlhbCFdy+mw

Smart contract deployment

And we'll deploy an instance of super simple chatbot Doge, mentioned as one of smart contract examples. First of all, we load its code and store something unique in data to receive our very own instance that someone else has not deployed. Then, we combine code and data into stateInit.

let code = TonWeb.boc.Cell.oneFromBoc(TonWeb.utils.base64ToBytes('te6cckEBAgEARAABFP8A9KQT9LzyyAsBAGrTMAGCCGlJILmRMODQ0wMx+kAwi0ZG9nZYcCCAGMjLBVAEzxaARfoCE8tqEssfAc8WyXP7AN4uuM8='));
let data = new TonWeb.boc.Cell();
data.bits.writeUint(Math.floor(new Date()), 64);

let state_init = new TonWeb.boc.Cell();
state_init.bits.writeUint(6, 5);
state_init.refs.push(code);
state_init.refs.push(data);

let state_init_boc = TonWeb.utils.bytesToBase64(await state_init.toBoc());
console.log(state_init_boc);
// te6ccsEBBAEAUwAABRJJAgE0AQMBFP8A9KQT9LzyyAsCAGrTMAGCCGlJILmRMODQ0wMx+kAwi0ZG9nZYcCCAGMjLBVAEzxaARfoCE8tqEssfAc8WyXP7AAAQAAABhltsPJ+MirEd

let doge_address = '0:' + TonWeb.utils.bytesToHex(await state_init.hash());
console.log(doge_address);
// 0:1c7c35ed634e8fa796e02bbbe8a2605df0e2ab59d7ccb24ca42b1d5205c735ca

And it's time to send our transaction:

console.log(await connector.sendTransaction({
validUntil: Math.floor(new Date() / 1000) + 360,
messages: [
{
address: "EQAcfDXtY06Pp5bgK7voomBd8OKrWdfMskykKx1SBcc1yh5O",
amount: "69000000",
payload: "te6ccsEBAQEAHQAAADYAAAAAVE9OIENvbm5lY3QgMiB0dXRvcmlhbCFdy+mw",
stateInit: "te6ccsEBBAEAUwAABRJJAgE0AQMBFP8A9KQT9LzyyAsCAGrTMAGCCGlJILmRMODQ0wMx+kAwi0ZG9nZYcCCAGMjLBVAEzxaARfoCE8tqEssfAc8WyXP7AAAQAAABhltsPJ+MirEd"
}
]
}));
info

Get more examples on the Preparing Messages page for Transfer NFT and Jettons.

After confirmation, we may see our transaction complete at tonscan.org.

What happens if the user rejects a transaction request?

It's pretty easy to handle request rejection, but it's better to know what would happen in advance when you're developing some project.

When a user clicks Cancel in the popup in the wallet application, an exception is thrown:

Error: [TON_CONNECT_SDK_ERROR] The Wallet declined the request 

This error can be considered final (unlike connection cancellation) - if it has been raised, then the requested transaction will definitely not happen until the next request is sent.

See also

Was this article useful?