Smart contract addresses
This section describes the specifics of smart contract addresses on the TON Blockchain. It also explains how actors are synonymous with smart contracts on TON.
Everything is a smart contract
On TON, smart contracts are built using the Actor model. In fact, actors on TON are technically represented as smart contracts. This means that even your wallet is a simple actor (and a smart contract).
Typically, actors process incoming messages, change their internal states, and generate outbound messages as a result. That's why every actor (i.e., smart contract) on TON Blockchain must have an address, so it can receive messages from other actors.
On the Ethereum Virtual Machine (EVM), addresses are completely separate from smart contracts. Feel free to learn more about the differences by reading our article "Six unique aspects of TON Blockchain that will surprise Solidity developers" - Tal Kol.
Address of smart contract
Conceptually, smart contract addresses on TON consist of two main components:
-
(workchain_id): denotes the WorkChain ID (a signed 32-bit integer)
-
(account_id): denotes the address of the account (64-512 bits, depending on the WorkChain)
In the raw address overview section of this documentation, we'll discuss how (workchain_id, account_id) pairs are presented.
WorkChain ID and Account ID
WorkChain ID
As we've seen before, it is possible to create as many as 2^32
WorkChains operating on TON Blockchain. We also noted how 32-bit prefix smart contract addresses identify and are linked to smart contract addresses within different WorkChains. This allows smart contracts to send and receive messages to and from different WorkChains on TON Blockchain.
Nowadays, only the MasterChain (workchain_id=-1) and the basic WorkChain (workchain_id=0) are running in TON Blockchain.
Both of them have 256-bit addresses, therefore, we assume that the workchain_id is either 0 or -1, and the address within the WorkChain is precisely 256 bits.
Account ID
All account IDs on TON use 256-bit addresses on the MasterChain and Basechain (also referred to as the basic WorkChain).
In fact, an Account ID (account_id) is defined as the result of applying a hash function (specifically SHA-256) to an initial state of the contract. Initial state of a contract consists of two main parts:
- Initial code. The logic of the smart contract, compiled into bytecode.
- Initial data. The contract's values at the moment it is deployed on-chain.
To derive the contract's address, you calculate the hash of the (Initial code, Initial data) pair. We won’t explore how the TVM works at this time, but it is important to understand that account IDs on TON follow this formula:
account_id = hash(initial code, initial data)
Later in this documentation, we will dive deeper into the technical specifications of the TVM and TL-B scheme. Now that we are familiar with how the account_id is generated and how it interacts with smart contract addresses on TON, let’s discuss Raw and User-Friendly addresses.
Code and data that contract holds may change through the execution of messages. However, address of the contract remains the same.
Addresses state
Each address can be in one of possible states:
nonexist
- contract doesn't have any data (or the contract was deleted) and the balance of an account is 0. We can say that initially all 2256 addresses are in this state.uninit
- address has some data, which contains balance and meta-info. At this state address doesn't have any smart contract code/persistent data yet. An address enters this state, for example, when it was in a nonexist state, and another address sent Toncoins to it.active
- address has smart contract code, persistent data and balance. At this state it can perform some logic during the transaction and change its code or data. An address enters this state when it wasuninit
ornonexist
and there was an incoming message with state_init param (note, that to be able to deploy this address, hash ofstate_init
must be equal to address).frozen
- address cannot perform any operations, this state contains only the hash of the previous state (https://github.com/ton-blockchain/ton/blob/master/crypto/block/block.tlb#L268). When an address's storage charge exceeds its balance, it goes into this state. To unfreeze it, you can send an internal message withstate_init
which hash equals to the hash of the previous state described earlier and some Toncoin. It can be difficult to recover it, so you should not allow this situation. There is a project to unfreeze the address, which you can find here.
Raw and user-friendly addresses
After providing a brief overview of how smart contract addresses on TON leverage workchains and account IDs (for the MasterChain and BaseChain specifically), it is important to understand that these addresses are expressed in two main formats:
- Raw addresses: original full representation of smart contract addresses.
- User-friendly addresses: user-friendly addresses are an enhanced format of raw address that employ better security and ease of use.
These types of addresses exist only in the off-chain representation. Addresses in the on-chain representation are Slices of specific format.
Below, we’ll explain more about the differences between these two address types and dive deeper into why user-friendly addresses are used on TON.
Raw address
Raw smart contract addresses consist of a WorkChain ID and account ID (workchain_id, account_id) and are displayed in the following format:
- [decimal workchain_id]:[hexadecimal digits with account_id]
Provided below, is an example of a raw smart contract address using a WorkChain ID and account ID together (expressed as workchain_id and account_id):
-1:fcb91a3a3816d0f7b8c2c76108b8a9bc5a6b7a55bd79f8ab101c52db29232260
Notice the -1
at the start of the address string, which denotes a workchain_id that belongs to the MasterChain.
Uppercase letters (such as 'A', 'B', 'C', 'D' etc.) may be used in address strings instead of their lowercase counterparts (such as 'a', 'b', 'c', 'd' etc.).
Issues with raw addresses
Using the raw address form presents two main issues:
- When using the raw address format, it's not possible to verify addresses to eliminate errors prior to sending a transaction. This means that if you accidentally replace characters in the address string prior to sending the transaction, your transaction will be sent to the wrong destination, resulting in loss of funds.
- When using the raw address format, it's impossible to add special flags like those used when sending transactions that employ user-friendly addresses. To help you better understand this concept, we’ll explain which flags can be used below.
User-friendly address
User-friendly addresses were developed to secure and simplify the experience for TON users who share addresses on the internet (for example, on public messaging platforms or via their email service providers), as well as in the real world.
User-friendly address structure
User-friendly addresses are made up of 36 bytes in total and are obtained by generating the following components in order:
-
[flags - 1 byte] — Flags that are pinned to addresses change the way smart contracts react to the received message. Flags types that employ the user-friendly address format include:
- isBounceable. Denotes a bounceable or non-bounceable address type. (0x11 for "bounceable", 0x51 for "non-bounceable")
- isTestnetOnly. Denotes an address type used for testnet purposes only. Addresses beginning with 0x80 should not be accepted by software running on the production network
- isUrlSafe. Denotes a deprecated flag that is defined as URL-safe for an address. All addresses are then considered URL-safe.
-
[workchain_id - 1 byte] — The WorkChain ID (workchain_id) is defined by a signed 8-bit integer workchain_id.
(0x00 for the BaseChain, 0xff for the MasterChain)
-
[account_id - 32 byte] — The account ID is made up of a (big-endian) 256-bit address in the workchain.
-
[address verification - 2 bytes] — In user-friendly addresses, address verification is composed of a CRC16-CCITT signature from the previous 34 bytes. (Example) In fact, the idea pertaining to verification for user-friendly addresses is quite similar to the Luhn algorithm, which is used on all credit cards to prevent users from entering non-existing card numbers by mistake.
The addition of these 4 main components means that: 1 + 1 + 32 + 2 = 36
bytes in total (per user-friendly address).
To generate a user-friendly address, the developer must encode all 36 bytes using either:
- base64 (using
+
and/
characters) - base64url (using
-
and_
characters for URL safety)
Both formats are valid and must be accepted by applications. The base64url
format is preferred when addresses are used in URLs to avoid encoding issues.
After this process is complete, the generation of a user-friendly address with a length of 48 non-spaced characters is finalized.
On TON, DNS addresses such as mywallet.ton are sometimes used instead of raw and user-friendly addresses. DNS addresses are made up of user-friendly addresses and include all the required flags that allow developers to access all the flags from the DNS record within the TON domain.
User-friendly address encoding examples
For example, the "test giver" smart contract (a special smart contract residing in the testnet MasterChain that sends 2 test tokens to anyone who requests them) makes use of the following raw address:
-1:fcb91a3a3816d0f7b8c2c76108b8a9bc5a6b7a55bd79f8ab101c52db29232260
The above "test giver" raw address must be converted into the user-friendly address form. This is obtained using either the base64 or base64url forms (that we introduced previously) as follows:
kf/8uRo6OBbQ97jCx2EIuKm8Wmt6Vb15+KsQHFLbKSMiYIny
(base64)kf_8uRo6OBbQ97jCx2EIuKm8Wmt6Vb15-KsQHFLbKSMiYIny
(base64url)
Notice that both forms (base64 and base64url) are valid and must be accepted!
Bounceable vs non-bounceable addresses
The core idea behind the bounceable address flag is sender's funds security.
For example, if the destination smart contract does not exist, or if an issue happens during the transaction, the message will be "bounced" back to the sender and constitute the remainder of the original value of the transaction (minus all transfer and gas fees). In relation to bounceable addresses specifically:
- The bounceable=false flag generally means the receiver is a wallet.
- The bounceable=true flag typically denotes a custom smart contract with its own application logic (for example, a DEX). In this example, non-bounceable messages should not be sent because of security reasons.
Feel free to read more on this topic in our documentation to gain a better understanding of non-bounceable messages.
Armored base64 representations
Additional binary data related to TON Blockchain employs similar "armored" base64 user-friendly address representations. These differentiate from one another depending on the first 4 characters of their byte tag. For example, 256-bit Ed25519 public keys are represented by first creating a 36-byte sequence using the below process in order:
- A single byte tag using the 0x3E format denotes a public key
- A single byte tag using the 0xE6 format denotes a Ed25519 public key
- 32 bytes containing the standard binary representation of the Ed25519 public key
- 2 bytes containing the big-endian representation of CRC16-CCITT of the previous 34 bytes
The resulting 36-byte sequence is converted into a 48-character base64 or base64url string in the standard fashion. For example, the Ed25519 public key E39ECDA0A7B0C60A7107EC43967829DBE8BC356A49B9DFC6186B3EAC74B5477D
(usually represented by a sequence of 32 bytes such as: 0xE3, 0x9E, ..., 0x7D
) presents itself through the "armored" representation as follows:
Pubjns2gp7DGCnEH7EOWeCnb6Lw1akm538YYaz6sdLVHfRB2
Converting user-friendly addresses and raw addresses
The simplest way to convert user-friendly and raw addresses is to use one of several TON APIs and other tools, including:
- ton.org/address
- dton.io API method
- toncenter API methods in mainnet
- toncenter API methods in testnet
Additionally, there are two ways to convert user-friendly and raw addresses for wallets using JavaScript:
- Convert address from/to user-friendly or raw form using ton.js
- Convert address from/to user-friendly or raw form using tonweb
It's also possible to make use of similar mechanisms using SDKs.
Address examples
Learn more examples on TON Addresses in the TON cookbook.
Possible problems
When interacting with the TON blockchain, it's crucial to understand the implications of transferring TON coins to uninit
wallet addresses. This section outlines the various scenarios and their outcomes to provide clarity on how such transactions are handled.
What happens when you transfer Toncoin to an uninit address?
Transaction with state_init
included
When a transaction includes the state_init
(containing the smart contract's code and data), the smart contract is deployed first. After deployment, the incoming message is processed, just as it would be for an already initialized account.
Transaction without state_init
and bounce
flag set
If a message is sent to an uninit
address without a state_init
and with the bounce
flag set, it cannot be processed. The message is bounced back to the sender, and the transaction amount is returned after deducting gas fees.
Transaction without state_init
and bounce
flag unset
If the bounce
flag is not set, the message still cannot be processed, but it's value won't be returned to the sender. Instead, the funds are credited to the uninit
account. These funds will be accessible once the one deploys the smart contract on that address.
Recommended deployment practice
The recommended method for deploying a wallet is to first send some TON to its uninit
address with the bounce
flag unset. One can then use this balance to pay for the deployment and initialization of the wallet, which typically happens during the first transaction from that wallet.
Protecting against erroneous transactions
To prevent accidental fund loss, the TON ecosystem uses bounceable and non-bounceable address formats, which are described in detail here. Standard wallets and applications handle these automatically.
It is common practice for wallets to use the non-bounceable format when sending funds to an uninitialized address to ensure the funds are delivered for future contract deployment.
If you need to quickly convert an address to its bounceable or non-bounceable form, you can use a tool like ton.org/address.
Responsibility for custom products
If you are developing a custom product on TON, it is crucial to implement similar logic:
If the user provides the address to send TON to, you should use its format to determine the bounce flag. Otherwise, send unbounceable messages if you are sure that the address belongs to a user wallet; send bounceable messages otherwise.