Working With Wallet Smart Contracts
๐ Introductionโ
Learning how wallets and transactions work on TON before beginning smart contracts development is essential. This knowledge will help developers understand the interaction between wallets, messages, and smart contracts to implement specific development tasks.
It's recommended to get acquainted with Types of Wallet Contracts article before reading this tutorial.
In this section weโll learn to create operations without using pre-configured functions to understand development workflows. All references necessary for the analysis of this tutorial are located in the references chapter.
๐ก Prerequisitesโ
This tutorial requires basic knowledge of JavaScript and TypeScript or Golang. It is also necessary to hold at least 3 TON (which can be stored in an exchange account, a non-custodial wallet, or by using the Telegram bot wallet). It is necessary to have a basic understanding of cell, addresses in TON, blockchain of blockchains to understand this tutorial.
Working with the TON Testnet often leads to deployment errors, difficulty tracking transactions, and unstable network functionality. Therefore, it could be beneficial to complete most development on the TON Mainnet to potentially avoid these issues, which might be necessary to reduce the number of transactions and thereby possibly minimize fees.
๐ฟ Source Codeโ
All code examples used in this tutorial can be found in the following GitHub repository.
โ๏ธ What You Need To Get Startedโ
- Ensure NodeJS is installed.
- Specific Ton libraries are required and include: @ton/ton 13.5.1+, @ton/core 0.49.2+ and @ton/crypto 3.2.0+.
OPTIONAL: If you prefer to use Go instead of JS, it is necessary to install the tonutils-go library and the GoLand IDE to conduct development on TON. This library will be used in this tutorial for the GO version.
- JavaScript
- Golang
npm i --save @ton/ton @ton/core @ton/crypto
go get github.com/xssnick/tonutils-go
go get github.com/xssnick/tonutils-go/adnl
go get github.com/xssnick/tonutils-go/address
โ Set Your Environmentโ
In order to create a TypeScript project it's necessary to conduct the following steps in order:
- Create an empty folder (which weโll name WalletsTutorial).
- Open the project folder using the CLI.
- Use the following commands to set up your project:
npm init -y
npm install typescript @types/node ts-node nodemon --save-dev
npx tsc --init --rootDir src --outDir build \ --esModuleInterop --target es2020 --resolveJsonModule --lib es6 \ --module commonjs --allowJs true --noImplicitAny false --allowSyntheticDefaultImports true --strict false
To help us carry out the next process a ts-node
is used to execute TypeScript code directly without precompiling. nodemon
is used to restart the node application automatically when file changes in the directory are detected.
- Next, remove these lines from
tsconfig.json
:
"files": [
"\\",
"\\"
]
- Then, create a
nodemon.json
config in your project root with the following content:
{
"watch": ["src"],
"ext": ".ts,.js",
"ignore": [],
"exec": "npx ts-node ./src/index.ts"
}
- Add this script to
package.json
instead of "test", which is added when the project is created:
"start:dev": "npx nodemon"
- Create
src
folder in the project root andindex.ts
file in this folder. - Next, the following code should be added:
async function main() {
console.log("Hello, TON!");
}
main().finally(() => console.log("Exiting..."));
- Run the code using terminal:
npm run start:dev
- Finally, the console output will appear.
The TON Community created an excellent tool for automating all development processes (deployment, contract writing, testing) called Blueprint. However, we will not be needing such a powerful tool, so it is suggested that the instructions above are followed.
**OPTIONAL: ** When using Golang, follow these instructions::
- Install the GoLand IDE.
- Create a project folder and
go.mod
file using the following content (the version of Go may need to be changed to conduct this process if the current version being used is outdated):
module main
go 1.20
- Type the following command into the terminal:
go get github.com/xssnick/tonutils-go
- Create the
main.go
file in the root of your project with following content:
package main
import (
"log"
)
func main() {
log.Println("Hello, TON!")
}
- Change the name of the module in the
go.mod
tomain
. - Run the code above until the output in the terminal is displayed.
It is also possible to use another IDE since GoLand isnโt free, but it is preferred.
All coding components should be added to the main
function that was created in the โ Set Your Environment section.
Additionally, only the imports required for a specific code section will be specified in each new section and new imports will need to be added and combined with old ones.
๐ Let's Get Started!โ
In this tutorial weโll learn which wallets (versionโs 3 and 4) are most often used on TON Blockchain and get acquainted with how their smart contracts work. This will allow developers to better understand the different messages types on the TON platform to make it simpler to create messages, send them to the blockchain, deploy wallets, and eventually, be able to work with high-load wallets.
Our main task is to build messages using various objects and functions for @ton/ton, @ton/core, @ton/crypto (ExternalMessage, InternalMessage, Signing etc.) to understand what messages look like on a bigger scale. To carry out this process we'll make use of two main wallet versions (v3 and v4) because of the fact that exchanges, non-custodial wallets, and most users only used these specific versions.
There may be occasions in this tutorial when there is no explanation for particular details. In these cases, more details will be provided in later stages of this tutorial.
IMPORTANT: Throughout this tutorial wallet v3 code is used to better understand the wallet development process. It should be noted that version v3 has two sub-versions: r1 and r2. Currently, only the second version is being used, this means that when we refer to v3 in this document it means v3r2.
๐ TON Blockchain Walletsโ
All wallets that operate and run on TON Blockchain are actually smart contracts, in the same way, everything operating on TON is a smart contract. Like most blockchains, it is possible to deploy smart contracts on the network and customize them for different uses. Thanks to this feature, full wallet customization is possible. On TON wallet smart contracts help the platform communicate with other smart contract types. However, it is important to consider how wallet communication takes place.
Wallet Communicationโ
Generally, there are two message types on TON Blockchain: internal
and external
. External messages allow for the ability to send messages to the blockchain from the outside world, thus allowing for the communication with smart contracts that accept such messages. The function responsible for carrying out this process is as follows:
() recv_external(slice in_msg) impure {
;; some code
}
Before we dive into more details concerning wallets, letโs look at how wallets accept external messages. On TON, all wallets hold the ownerโs public key
, seqno
, and subwallet_id
. When receiving an external message, the wallet uses the get_data()
method to retrieve data from the storage portion of the wallet. It then conducts several verification procedures and decides whether to accept the message or not. This process is conducted as follows:
() recv_external(slice in_msg) impure {
var signature = in_msg~load_bits(512); ;; get signature from the message body
var cs = in_msg;
var (subwallet_id, valid_until, msg_seqno) = (cs~load_uint(32), cs~load_uint(32), cs~load_uint(32)); ;; get rest values from the message body
throw_if(35, valid_until <= now()); ;; check the relevance of the message
var ds = get_data().begin_parse(); ;; get data from storage and convert it into a slice to be able to read values
var (stored_seqno, stored_subwallet, public_key) = (ds~load_uint(32), ds~load_uint(32), ds~load_uint(256)); ;; read values from storage
ds.end_parse(); ;; make sure we do not have anything in ds variable
throw_unless(33, msg_seqno == stored_seqno);
throw_unless(34, subwallet_id == stored_subwallet);
throw_unless(35, check_signature(slice_hash(in_msg), signature, public_key));
accept_message();
๐ก Useful links:
Now letโs take a closer look.