Types of Wallet Contracts
You may have heard about different versions of wallets on the TON Blockchain. But what do these versions actually mean, and how do they differ?
In this article, we’ll explore the various versions and modifications of TON wallets.
Before we start, there are some terms and concepts that you should be familiar with to fully understand the article:
- Message management, because this is the main functionality of the wallets.
- FunC language, because we will heavily rely on implementations made using it.
Common concept
To break the tension, we should first understand that wallets are not a specific entity in the TON ecosystem. They are still just smart contracts consisting of code and data, and, in that sense, are equal to any other actor (i.e., smart contract) in TON.
Like your own custom smart contract, or any other one, wallets can receive external and internal messages, send internal messages and logs, and provide "get" methods. So the question is: what functionality do they provide and how it differs between versions?
You can consider each wallet version as a smart-contract implementation providing a standard external interface, allowing different external clients to interact with the wallets in the same way. You can find these implementations in FunC and Fift languages in the main TON monorepo:
Basic wallets
Wallet V1
This is the simplest one. It only allows you to send four transactions at a time and doesn't check anything besides your signature and seqno.
Wallet source code:
This version isn’t even used in regular apps because it has some major issues:
- No easy way to retrieve the seqno and public key from the contract.
- No
valid_until
check, so you can't be sure that the transaction won't be confirmed too late.
The first issue was fixed in V1R2
and V1R3
. The R
stands for "revision". Usually, revisions are just small updates that only add get methods; you can find all of those in the changes history of new-wallet.fif
. Hereinafter, we will consider only the latest revisions.
Nevertheless, because each subsequent version inherits the functionality of the previous one, we should still stick to it, as this will help us with later versions.
Persistent memory layout
- seqno: 32-bit long sequence number.
- public-key: 256-bit long public key.
External message body layout
- Data:
- signature: 512-bit long ed25519 signature.
- msg-seqno: 32-bit long sequence number.
- (0-4)mode: up to four 8-bit long integer's defining sending mode for each message.
- Up to 4 references to cells containing messages.
As you can see, the main functionality of the wallet is to provide a safe way to communicate with the TON blockchain from the outside world. The seqno
mechanism protects against replay attacks, and the Ed25519 signature
provides authorized access to wallet functionality. We will not dwell in detail on each of these mechanisms, as they are described in detail in the external message documentation page and are quite common among smart contracts receiving external messages. The payload data consists of up to 4 references to cells and the corresponding number of modes, which will be directly transferred to the send_raw_message(cell msg, int mode) method.
Note that the wallet doesn't provide any validation for internal messages you send through it. It is the programmer's (i.e., the external client’s) responsibility to serialize the data according to the internal message layout.
Exit codes
Exit code | Description |
---|---|
0x21 | seqno check failed, reply protection accured |
0x22 | Ed25519 signature check failed |
0x0 | Standard successful execution exit code. |
Note that TVM has standart exit codes (0x0
- is one of them), so you can get one of them too, if you run out of gas, for example, you will get 0xD
code.
Get methods
- int seqno() returns current stored seqno.
- int get_public_key returns current stored public key.
Wallet V2
Wallet source code:
This version introduces the valid_until
parameter, which is used to set a time limit for a transaction in case you don't want it to be confirmed too late. This version also does not have the get-method for the public key, which was added in V2R2
.
All differences compared to the previous version are a consequence of adding the valid_until
functionality. A new exit code was added: 0x23
, marking the failure of the valid_until check. Additionally, a new UNIX-time field has been added to the external message body layout, setting the time limit for the transaction. All get methods remain the same.
External message body layout
- Data:
- signature: 512-bit long ed25519 signature.
- msg-seqno: 32-bit long sequence number.
- valid-until: 32-bit long Unix-time integer.
- (0-4)mode: up to four 8-bit long integer's defining sending mode for each message.
- Up to 4 references to cells containing messages.
Wallet V3
This version introduces the subwallet_id
parameter, which allows you to create multiple wallets using the same public key (so you can have only one seed phrase and multiple wallets). As before, V3R2
only adds the get-method for the public key.
Wallet source code:
Essentially, subwallet_id
is just a number added to the contract state when it’s deployed. Since the contract address in TON is a hash of its state and code, the wallet address will change with a different subwallet_id
. This version is the most widely used right now. It covers most use cases and remains clean, simple, and mostly the same as previous versions. All get methods remain the same.
Persistent Memory Layout
- seqno: 32-bit sequence number.
- subwallet: 32-bit subwallet ID.
- public-key: 256-bit public key.
External Message Layout
- Data:
- signature: 512-bit ed25519 signature.
- subwallet-id: 32-bit subwallet ID.
- msg-seqno: 32-bit sequence number.
- valid-until: 32-bit UNIX time integer.
- (0-4)mode: Up to four 8-bit integers defining the sending mode for each message.
- Up to 4 references to cells containing messages.