Sending Messages
TON Connect 2.0 has more powerful options than just authenticating users in the dApp: it's possible to send outgoing messages via connected wallets!
You will understand:
- how to send messages from the DAppto 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
1) Understanding a task
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).
2) 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: "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. Please, accept it.
3) 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 of how is data 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 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.
Sending complex transactions
Serialization of cells
Before we proceed, let's talk about the format of messages we are going to send.
- payload (string base64, optional): raw one-cell BoC encoded in Base64.
- we will use it to store text comment 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 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
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, 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"
}
]
}));
Get more examples in 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 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.