This is a study note of Solidity. Solidity is a statically typed OO language for writing Ethereum smart contracts executed in Ethereum Virtual Machine(EVM).

1 Basics

A Solidity contract is a collection of code and data (its state) that resides at a specific address on the Ethereum blockchain.

All identifiers are restricted to the ASCII character set. It is possible to store UTF-8 encoded data in string variables.

In line address public minter;, the address type is a 160-bit value used to stroe contract address or the address of an external account. The keyword public tells compiler to generate a function function minter() external view returns (address) { return minter; }.

The line mapping (address => uint) public balances; creates a mapping type that maps an address to uint. Mappings can be seen as hash tables that are initialized with every possible key mapped to zero. It is neither possible to obtain a list of all keys, nor a list of all values of a mapping. You need different data type to keep the list of keys and values. The compiler generates a default getter but not setter.

You can emit an Event that can only be listened by off-chain program. A contract cannot listen to events of another contract. A common off-chain program is called *blockchain explorer that tracks transaction events and states.

You can revert when an error happens.

The constructor is a special function that is executed during the creation of the contract and cannot be called afterwards.

The msg (together with tx and block), is a special global variable that contains properties which allows access to the blockchain. msg.address is the address where the current function call come from.

The require function call defines conditions that reverts all changes if not met.

2 EVM

The Ethereum Virtual Machine or EVM is the isolated runtime environment for smart contracts in Ethereum. Smart contracts only have limited access to other smart contracts.

Ethereum has two types of accounts: External Accounts that are controlled by public-private key pairs (i.e. humans) and Contract Accounts which are controlled by the code stored together with the account. For external account, the storage and code are empty. Every account has a balance in Wei which can be modified by sending transactions that include Ether.

A transaction is a message that is sent from one account to another account (which might be the same or empty, see below). It can include binary data (which is called “payload”) and Ether. If the target account contains code, that code is executed and the payload is provided as input data.

If the target account is not set (null or no recipient), the transaction creates a new contract. The address of the contract is derived from the sender and its number of transactions sent (the “nonce”). The payload of a contract is executed and the output is the code of the new contract.

The Ethereum Virtual Machine has three areas to store data:

  • Storage: a key-value store that maps 256-bit words to 256-bit words.
  • Memory: A contract obtains a freshly cleared instance of memory for each message call. Memory is linear of 256-bit words and addressed at byte level.
  • Stack: A stack contains words of 256bits. All computations are preformed on stack that has a maximum size of 1024 elements. The top 16 elements can be copied ot be swaped with 16 elements below them.

The EVM instruction set includes arithmetic, bit, logical and comparison operations and conditional/jump controls. It can access current block properties such as its number and timestamp.

Contracts can call other contracts or send Ether to non-contract accounts by the means of message calls – so-called message calls. Every transaction consists of a top-level message call which in turn can create further message calls. The called contract (which can be the same as the caller) will receive a freshly cleared instance of memory and has access to the call payload - which will be provided in a separate area called the calldata. After it has finished execution, it can return data which will be stored at a location in the caller’s memory preallocated by the caller. All such calls are fully synchronous.

There exists a special variant of a message call, named delegatecall or callcode which is identical to a message call apart from the fact that the code at the target address is executed in the context of the calling contract and msg.sender and msg.value do not change their values. A contract uses this feature to call library code.

EVM uses a specially indexed data structure called log to implement events. It can only accessed from outside the blockchain.

Contracts even create other contracts using a special create call opcode and receives the address of the new contract on the stack.

The only way to remove code from the blockchain is when a contract at that address performs the selfdestruct operation, directly or via delegatecall or callcode. The remaining Ether stored at that address is sent to a designated target and then the storage and code is removed from the state. The removed contracts are still part of the blockchain history. It is dangerous because all Ethers sent to removed contracts are forever lost. The best practice is that you should instead disable them by changing some internal state which causes all functions to revert. This makes it impossible to use the contract, as it returns Ether immediately.

There is a small set of precompiled contracts in the addresses ranging from 1 to 0xffff.