TON is a very modern blockchain that brings some radical new ideas to smart contract development. It was designed quite some time after Ethereum was launched and had the luxury to learn what works well in the EVM model and what can be improved.
If you have some prior smart contract experience, you're probably familiar with Ethereum's Solidity language and its EVM. When learning TON development you should be aware of certain design differences that make things on TON behave quite differently from what you expect. The purpose of this post is to highlight some of these differences and give you some general ideas as to why they came to be.
Moving from data to big data
The main thing to understand about TON is that it was designed to bring blockchain to the hands of every human on earth. This means massive scale - billions of users sending billions of transactions per day.Think of this as the shift from data to big data. When you need to store the menu of a restaurant, an SQL database is a great choice - it can run powerful and flexible queries because all the data is readily available. When you need to store the Facebook posts of every human on earth, an SQL database is probably not the route to take. These amounts of "big data" must be sharded aggressively - limiting the flexibility of the queries you can run. Different tradeoffs for different purposes.
Here are six unique aspects of TON blockchain that will probably surprise most Solidity developers:
1. Your smart contract needs to pay rent and charge its users
The blockchain as an immutable and eternal store of data, is a great concept on paper, but as we'll soon see, is quite impractical to scale. The fee model of Ethereum is inspired from that of a bank. You want to send some money, you pay the bank a transaction fee. Who is responsible for paying the fee? The user who initiated the transfer.What about storing data on the blockchain - for example deploying the bytecode for a new smart contract? The Ethereum model dictates that the person sending the deploy transaction will pay the fee. But this fee is paid only once, yet if data on the chain is eternal, miners will have to keep paying infrastructure costs to retain this data for years to come. These fee economics don't add up, and if you try to scale them to billions of users, they will eventually collapse.
Moving from a bank to an instant messenger
2. Calls between smart contracts are asynchronous and not atomic
Moving from a single server to a cluster of microservices
If you imagine Ethereum as a monolith on a single server, TON is more similar to a cluster of microservices. Think that every smart contract may be running on a different machine. If two smart contracts want to call each other, just like two microservices communicating, they can send a message over the network. This message takes some time to travel, so communication is suddenly asynchronous! This means that when your smart contract calls a method of a different smart contract, the call will be processed after the transaction terminates, on some different future block.This is much more difficult to reason about. What happens if conditions change from when the message was sent and until it was received? For example, the calling contract balance had one value, but by the time the second contract processes the call, the balance has changed. Maintaining consistency is more difficult and bugs can creep up. What about atomicity? What happens if you chain three calls and only the last one fails? If you need to roll back all the changes, you will have to do so manually.
3. Your smart contract cannot run getter methods on other contracts
Moving from a single server to a cluster of microservices
4. Smart contract code is not immutable and can easily be modified
Moving from a lawyers to a software engineers
5. You should not have unbounded data structures in your contract state
This is a tricky one that takes a while to understand, but will explain why some smart contracts on TON are architected the way they are.Unbounded data structures are state variables in smart contracts that can grow indefinitely. Consider the ERC20 contract implementing the USDC token. This contract needs to maintain a map of balances per user address. The number of different USDC holders can grow indefinitely since USDC can be minted in large amounts and broken down to tiny pieces. In other words, the number of keys in the map can be as large as you want.What happens if an attacker tries to spam the contract by adding more and more entries? Would they be able to cause some DoS attack and prevent other honest users from using this contract? Ethereum solves this problem quite elegantly for smart contract developers. The Ethereum fee model dictates that the user who writes new state data pays the fee for this data. This means our attacker will have to pay a high cost for their spam. In addition, the gas cost for writes to a map on Ethereum is constant and does not depend on how much data this map contains, which means other users will not suffer from the spam. The bottom line is that spamming maps on Ethereum is not economical and the protection is provided by the system.
Moving from unbounded maps to unbounded contracts
6. Wallets are contracts and one public key can have mutliple wallets deployed
On Ethereum, a user's wallet is synonymous to their address, and an address is derived directly from a public key (and its corresponding private key). This is a 1:1 relationship, there's one address per public key and one public key per address. As long as the user knows their private key, they could never lose their wallet.In addition, a user on Ethereum doesn't have to do anything special to have a wallet. The Ethereum address is the wallet. The address can hold the native currency ETH, the address can hold ERC20 tokens and NFTs and the address can send and sign transactions to other smart contracts directly.
Moving from address to contract
On TON, wallets are not implied, they are independent smart contracts that must be deployed like any other smart contract. When a new user wants to start using TON blockchain, their first step would be to deploy a wallet on-chain. The address of this wallet is derived from the wallet contract code and various init parameters like the user's public key.This means that a user can have more than one wallet deployed, each with its own address. The wallets can differ on their code (different official code versions are published by the foundation from time to time) or on their init parameters (one of these parameters is normally a sequence number). This also means that a user that knows their private key must still make a conscious effort to remember their wallet address (or the init parameters used in its construction).Sending a transaction to some dapp on TON involves signing a message using the user's private key. Unlike Ethereum, this transaction is not sent to the dapp smart contract, but to the user's wallet contract, which will in turn forward the message to the dapp smart contract.This design approach opens up a new dimension of flexibility on TON. New wallet contracts can be invented by the community over time, for example consider a wallet without a nonce (transaction sequence number) that allows multiple transactions to be sent in parallel from different clients without prior synchronization. In addition, special wallets like multi-sig wallets (that also on Ethereum require a special smart contract to be deployed), behave just like their regular counterparts.