Skip to main content

Sending messages

info

There is no description of connecting a wallet on this page. We suppose you have already connected the wallet to your dApp. If not, you can refer to integration manual.

TON Connect 2 has more powerful options than just authenticating users in the dApp: it's possible to send outgoing messages via connected wallets!

Playground page

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/tonweb@0.0.41/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>

Sending multiple messages

Let's start with something interesting! We will send two separate messages in one transaction: one to your own address, carrying 0.2 TON, and one to the other wallet address carrying 0.1 TON.

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

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

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: "0:b2a1ecf5545e076cd36ae516ea7ebdf32aea008caa2b84af9866becb208895ad",
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 coins would go.

Accept the request. The function will exit, and something will be printed.

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

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

The purpose of returning BOC of the sent transaction is to track it.

Transfer with comment, contract deployment

info

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

Text comment on transfer is 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 2 tutorial!");
let payload = TonWeb.utils.bytesToBase64(await a.toBoc());

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

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, so that we receive our very own instance that has not been deployed by someone other. 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: "0:1c7c35ed634e8fa796e02bbbe8a2605df0e2ab59d7ccb24ca42b1d5205c735ca",
amount: "69000000",
payload: "te6ccsEBAQEAHQAAADYAAAAAVE9OIENvbm5lY3QgMiB0dXRvcmlhbCFdy+mw",
stateInit: "te6ccsEBBAEAUwAABRJJAgE0AQMBFP8A9KQT9LzyyAsCAGrTMAGCCGlJILmRMODQ0wMx+kAwi0ZG9nZYcCCAGMjLBVAEzxaARfoCE8tqEssfAc8WyXP7AAAQAAABhltsPJ+MirEd"
}
]
}));

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 when you're developing some project it's better to know what would happen in advance.

When a user clicks "Cancel" in the popup in the wallet application, an exception is thrown: Error: [TON_CONNECT_SDK_ERROR] 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.