Common concept
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. Read more about differences. Like your own custom smart contract, or any other one, wallets can receive external and internal messages, send internal messages and logs, and provideget methods.
So the question is: what functionality do they provide, and how do they differ 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 contract hashes
Here, you can find the current hashes of the wallet contract code versions. For detailed specifications of each wallet contract, please refer to the sections further down this page.| Contract version | Hash |
|---|---|
| V1 R1 | a0cfc2c48aee16a271f2cfc0b7382d81756cecb1017d077faaab3bb602f6868c |
| V1 R2 | d4902fcc9fad74698fa8e353220a68da0dcf72e32bcb2eb9ee04217c17d3062c |
| V1 R3 | 587cc789eff1c84f46ec3797e45fc809a14ff5ae24f1e0c7a6a99cc9dc9061ff |
| V2 R1 | 5c9a5e68c108e18721a07c42f9956bfb39ad77ec6d624b60c576ec88eee65329 |
| V2 R2 | fe9530d3243853083ef2ef0b4c2908c0abf6fa1c31ea243aacaa5bf8c7d753f1 |
| V3 R1 | b61041a58a7980b946e8fb9e198e3c904d24799ffa36574ea4251c41a566f581 |
| V3 R2 | 84dafa449f98a6987789ba232358072bc0f76dc4524002a5d0918b9a75d2d599 |
| V4 R1 | 64dd54805522c5be8a9db59cea0105ccf0d08786ca79beb8cb79e880a8d7322d |
| V4 R2 | feb5ff6820e2ff0d9483e7e0d62c817d846789fb4ae580c878866d959dabd5c0 |
| V5 R1 | 20834b7b72b112147e1b2fb457b84e74d1a30f04f737d4f62a668e9552d2b72f |
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_untilcheck, so you can’t be sure that the transaction won’t be confirmed too late.
V1R2 and V1R3. The R stands for revision. Usually, revisions are just minor 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.
Official code hashes
| Contract version | Hash |
|---|---|
| V1 R1 | a0cfc2c48aee16a271f2cfc0b7382d81756cecb1017d077faaab3bb602f6868c |
| V1 R2 | d4902fcc9fad74698fa8e353220a68da0dcf72e32bcb2eb9ee04217c17d3062c |
| V1 R3 | 587cc789eff1c84f46ec3797e45fc809a14ff5ae24f1e0c7a6a99cc9dc9061ff |
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 integers defining sending mode for each message.
- Up to 4 references to cells containing messages.
seqno mechanism protects against replay attacks, and the Ed25519 signature provides authorized access to wallet functionality. 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 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 |
|---|---|
| 33 | seqno check failed, replay protection occurred |
| 34 | Ed25519 signature check failed |
| 0 | Standard successful execution exit code. |
Get methods
int seqno()returns current stored seqno.int get_public_key()returns the current stored public key.
Wallet V2
Wallet source code:Originally in FunC; compiled to Fift assembly; later optimized
Final Fift source:
get methods. Final Fift source:
ton/crypto/smartcont/new-wallet-v2.fif.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 a 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: 35, 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.
Official code hashes
| Contract version | Hash |
|---|---|
| V2 R1 | 5c9a5e68c108e18721a07c42f9956bfb39ad77ec6d624b60c576ec88eee65329 |
| V2 R2 | fe9530d3243853083ef2ef0b4c2908c0abf6fa1c31ea243aacaa5bf8c7d753f1 |
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 integers defining the sending mode for each message.
- Up to 4 references to cells containing messages.
Wallet V3
This version introduces thesubwallet_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:
Originally in FunC; compiled to Fift assembly; later optimized
Final Fift source:
get methods. Final Fift source:
ton/crypto/smartcont/wallet-v3-code.fif.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.
Official code hashes
| Contract version | Hash |
|---|---|
| V3 R1 | b61041a58a7980b946e8fb9e198e3c904d24799ffa36574ea4251c41a566f581 |
| V3 R2 | 84dafa449f98a6987789ba232358072bc0f76dc4524002a5d0918b9a75d2d599 |
Persistent memory layout
seqno: 32-bit sequence number.subwallet_id: 32-bit subwallet ID.public_key: 256-bit public key.
External message body 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.
Exit codes
| Exit code | Description |
|---|---|
| 33 | seqno check failed; replay protection triggered |
| 34 | subwallet_id does not match the stored one |
| 35 | valid_until check failed; transaction confirmation attempted too late |
| 35 | Ed25519 signature check failed |
| 0 | Standard successful execution exit code. |
In V3, exit code
35 is intentionally re-used for two checks. This is not a typo. In V4 they are split: 35 = signature check failed; 36 = valid_until check failed.Wallet V4
Why V4 was created: V4 was developed to introduce plugin functionality, allowing third-party smart contracts to interact with wallets in a controlled manner. This enabled features like automated payments, subscriptions, and complex DApp integrations while maintaining security through an allowlist system. Read about this standard iteration on its dedicated page: V4 wallet standard.Wallet V5
Why V5 was created: V5 was developed by the Tonkeeper team to address the growing need for gasless transactions, improved extensibility, and better user experience. It adds support for owner-signed internal messages, enabling gasless transactions where users can pay fees in tokens like USDt instead of Toncoin. It is the most modern wallet version at the moment, aimed at replacing V4 and allowing arbitrary extensions. Read more:Special wallets
Sometimes the functionality of basic wallets isn’t enough. That’s why there are several types of specialized wallets:high-load, lockup, and restricted.
Let’s have a look at them.
Highload wallets
When working with many messages in a short period, there is a need for a special wallet called a Highload Wallet. Read the article for more information.Lockup wallet
If you, for some reason, need to lock coins in a wallet for some time without the possibility of withdrawing them before that time passes, have a look at the lockup wallet. It allows you to set the time until which you won’t be able to withdraw anything from the wallet. You can also customize it by setting unlock periods so that you will be able to spend some coins during these set periods. For example, you can create a wallet that will hold 1 million coins with a total vesting time of 10 years. Set the cliff duration to one year, so the funds will be locked for the first year after the wallet is created. Then, you can set the unlock period to one month, so1'000'000 Toncoin / 120 months = ~8333 Toncoin will unlock every month.
Wallet source code:
Restricted wallet
This wallet’s function is to act like a regular wallet, but restrict transfers to only one pre-defined destination address. You can set the destination when you create this wallet, and then you’ll only be able to transfer funds from it to that address. But note that you can still transfer funds to validation contracts, so you can run a validator with this wallet. Wallet source code:Conclusion
As you can see, there are many different versions of wallets in TON. For new deployments, preferV5. Use V3R2 or V4R2 only for legacy compatibility or specific constraints. You can also use one of the special wallets if you want additional functionality like periodic unlocking of funds.