Introduction To Smart Contracts - Solidity 0.7.6 Documentation
Introduction To Smart Contracts - Solidity 0.7.6 Documentation
Storage Example
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.8.0;
contract SimpleStorage {
uint storedData;
The first line tells you that the source code is licensed under the GPL version 3.0. Machine-
readable license specifiers are important in a se ng where publishing the source code is the
default.
The next line specifies that the source code is wri en for Solidity version 0.4.16, or a newer
version of the language up to, but not including version 0.8.0. This is to ensure that the contract
is not compilable with a new (breaking) compiler version, where it could behave differently.
Pragmas are common instruc ons for compilers about how to treat the source code (e.g. pragma
once).
A contract in the sense of Solidity is a collec on of code (its func ons) and data (its state) that
resides at a specific address on the Ethereum blockchain. The line uint storedData; declares a
state variable called storedData of type uint (unsigned integer of 256 bits). You can think of it
as a single slot in a database that you can query and alter by calling func ons of the code that
manages the database. In this example, the contract defines the func ons set and get that
can be used to modify or retrieve the value of the variable.
To access a state variable, you do not need the prefix this. as is common in other languages.
This contract does not do much yet apart from (due to the infrastructure built by Ethereum)
allowing anyone to store a single number that is accessible by anyone in the world without a
(feasible) way to prevent you from publishing this number. Anyone could call set again with a
different value and overwrite your number, but the number is s ll stored in the history of the
blockchain. Later, you will see how you can impose access restric ons so that only you can alter
the number.
Warning
Be careful with using Unicode text, as similar looking (or even iden cal) characters can have
different code points and as such are encoded as a different byte array.
Note
All iden fiers (contract names, func on names and variable names) are restricted to the
ASCII character set. It is possible to store UTF-8 encoded data in string variables.
Subcurrency Example
The following contract implements the simplest form of a cryptocurrency. The contract allows
only its creator to create new coins (different issuance schemes are possible). Anyone can send
coins to each other without a need for registering with a username and password, all you need is
an Ethereum keypair.
// SPDX-License-Identifier: GPL-3.0
pragma solidity >0.5.99 <0.8.0;
contract Coin {
// The keyword "public" makes variables
// accessible from other contracts
address public minter;
mapping (address => uint) public balances;
This contract introduces some new concepts, let us go through them one by one.
The line address public minter; declares a state variable of type address. The address type is
a 160-bit value that does not allow any arithme c opera ons. It is suitable for storing addresses
of contracts, or a hash of the public half of a keypair belonging to external accounts.
The keyword public automa cally generates a func on that allows you to access the current
value of the state variable from outside of the contract. Without this keyword, other contracts
have no way to access the variable. The code of the func on generated by the compiler is
equivalent to the following (ignore external and view for now):
The next line, mapping (address => uint) public balances; also creates a public state variable,
but it is a more complex datatype. The mapping type maps addresses to unsigned integers.
Mappings can be seen as hash tables which are virtually ini alised such that every possible key
exists from the start and is mapped to a value whose byte-representa on is all zeros. However, it
is neither possible to obtain a list of all keys of a mapping, nor a list of all values. Record what
you added to the mapping, or use it in a context where this is not needed. Or even be er, keep a
list, or use a more suitable data type.
The ge er func on created by the public keyword is more complex in the case of a mapping. It
looks like the following:
You can use this func on to query the balance of a single account.
The line event Sent(address from, address to, uint amount); declares an “event”, which is
emi ed in the last line of the func on send . Ethereum clients such as web applica ons can
listen for these events emi ed on the blockchain without much cost. As soon as it is emi ed, the
listener receives the arguments from , to and amount , which makes it possible to track
transac ons.
To listen for this event, you could use the following JavaScript code, which uses web3.js to
create the Coin contract object, and any user interface calls the automa cally generated
balances func on from above:
The func ons that make up the contract, and that users and contracts can call are mint and
send .
The mint func on sends an amount of newly created coins to another address. The require
func on call defines condi ons that reverts all changes if not met. In this example,
require(msg.sender == minter); ensures that only the creator of the contract can call mint ,
and require(amount < 1e60); ensures a maximum amount of tokens. This ensures that there are
no overflow errors in the future.
The send func on can be used by anyone (who already has some of these coins) to send coins
to anyone else. If the sender does not have enough coins to send, the require call fails and
provides the sender with an appropriate error message string.
Note
If you use this contract to send coins to an address, you will not see anything when you look
at that address on a blockchain explorer, because the record that you sent coins and the
changed balances are only stored in the data storage of this par cular coin contract. By using
events, you can create a “blockchain explorer” that tracks transac ons and balances of your
new coin, but you have to inspect the coin contract address and not the addresses of the
coin owners.
Blockchain Basics
Blockchains as a concept are not too hard to understand for programmers. The reason is that
most of the complica ons (mining, hashing, ellip c-curve cryptography, peer-to-peer networks,
etc.) are just there to provide a certain set of features and promises for the pla orm. Once you
accept these features as given, you do not have to worry about the underlying technology - or
do you have to know how Amazon’s AWS works internally in order to use it?
Transactions
A blockchain is a globally shared, transac onal database. This means that everyone can read
entries in the database just by par cipa ng in the network. If you want to change something in
the database, you have to create a so-called transac on which has to be accepted by all others.
The word transac on implies that the change you want to make (assume you want to change
two values at the same me) is either not done at all or completely applied. Furthermore, while
your transac on is being applied to the database, no other transac on can alter it.
As an example, imagine a table that lists the balances of all accounts in an electronic currency. If
a transfer from one account to another is requested, the transac onal nature of the database
ensures that if the amount is subtracted from one account, it is always added to the other
account. If due to whatever reason, adding the amount to the target account is not possible, the
source account is also not modified.
Furthermore, a transac on is always cryptographically signed by the sender (creator). This makes
it straigh orward to guard access to specific modifica ons of the database. In the example of
the electronic currency, a simple check ensures that only the person holding the keys to the
account can transfer money from it.
Blocks
One major obstacle to overcome is what (in Bitcoin terms) is called a “double-spend a ack”:
What happens if two transac ons exist in the network that both want to empty an account?
Only one of the transac ons can be valid, typically the one that is accepted first. The problem is
that “first” is not an objec ve term in a peer-to-peer network.
The abstract answer to this is that you do not have to care. A globally accepted order of the
transac ons will be selected for you, solving the conflict. The transac ons will be bundled into
what is called a “block” and then they will be executed and distributed among all par cipa ng
nodes. If two transac ons contradict each other, the one that ends up being second will be
rejected and not become part of the block.
These blocks form a linear sequence in me and that is where the word “blockchain” derives
from. Blocks are added to the chain in rather regular intervals - for Ethereum this is roughly
every 17 seconds.
As part of the “order selec on mechanism” (which is called “mining”) it may happen that blocks
are reverted from me to me, but only at the “ p” of the chain. The more blocks are added on
top of a par cular block, the less likely this block will be reverted. So it might be that your
transac ons are reverted and even removed from the blockchain, but the longer you wait, the
less likely it will be.
Note
Transac ons are not guaranteed to be included in the next block or any specific future block,
since it is not up to the submi er of a transac on, but up to the miners to determine in
which block the transac on is included.
If you want to schedule future calls of your contract, you can use the alarm clock or a similar
oracle service.
Overview
The Ethereum Virtual Machine or EVM is the run me environment for smart contracts in
Ethereum. It is not only sandboxed but actually completely isolated, which means that code
running inside the EVM has no access to network, filesystem or other processes. Smart contracts
even have limited access to other smart contracts.
Accounts
There are two kinds of accounts in Ethereum which share the same address space: 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.
The address of an external account is determined from the public key while the address of a
contract is determined at the me the contract is created (it is derived from the creator address
and the number of transac ons sent from that address, the so-called “nonce”).
Regardless of whether or not the account stores code, the two types are treated equally by the
EVM.
Every account has a persistent key-value store mapping 256-bit words to 256-bit words called
storage.
Furthermore, every account has a balance in Ether (in “Wei” to be exact, 1 ether is
10**18 wei ) which can be modified by sending transac ons that include Ether.
Transactions
A transac on 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 (the transac on does not have a recipient or the recipient is set to
null ), the transac on creates a new contract. As already men oned, the address of that
contract is not the zero address but an address derived from the sender and its number of
transac ons sent (the “nonce”). The payload of such a contract crea on transac on is taken to
be EVM bytecode and executed. The output data of this execu on is permanently stored as the
code of the contract. This means that in order to create a contract, you do not send the actual
code of the contract, but in fact code that returns that code when executed.
Note
While a contract is being created, its code is s ll empty. Because of that, you should not call
back into the contract under construc on un l its constructor has finished execu ng.
Gas
Upon crea on, each transac on is charged with a certain amount of gas, whose purpose is to
limit the amount of work that is needed to execute the transac on and to pay for this execu on
at the same me. While the EVM executes the transac on, the gas is gradually depleted
according to specific rules.
The gas price is a value set by the creator of the transac on, who has to pay gas_price * gas
up front from the sending account. If some gas is le a er the execu on, it is refunded to the
creator in the same way.
If the gas is used up at any point (i.e. it would be nega ve), an out-of-gas excep on is triggered,
which reverts all modifica ons made to the state in the current call frame.
The Ethereum Virtual Machine has three areas where it can store data- storage, memory and the
stack, which are explained in the following paragraphs.
Each account has a data area called storage, which is persistent between func on calls and
transac ons. Storage is a key-value store that maps 256-bit words to 256-bit words. It is not
possible to enumerate storage from within a contract, it is compara vely costly to read, and
even more to ini alise and modify storage. Because of this cost, you should minimize what you
store in persistent storage to what the contract needs to run. Store data like derived
calcula ons, caching, and aggregates outside of the contract. A contract can neither read nor
write to any storage apart from its own.
The second data area is called memory, of which a contract obtains a freshly cleared instance for
each message call. Memory is linear and can be addressed at byte level, but reads are limited to a
width of 256 bits, while writes can be either 8 bits or 256 bits wide. Memory is expanded by a
word (256-bit), when accessing (either reading or wri ng) a previously untouched memory word
(i.e. any offset within a word). At the me of expansion, the cost in gas must be paid. Memory is
more costly the larger it grows (it scales quadra cally).
The EVM is not a register machine but a stack machine, so all computa ons are performed on a
data area called the stack. It has a maximum size of 1024 elements and contains words of 256
bits. Access to the stack is limited to the top end in the following way: It is possible to copy one
of the topmost 16 elements to the top of the stack or swap the topmost element with one of
the 16 elements below it. All other opera ons take the topmost two (or one, or more, depending
on the opera on) elements from the stack and push the result onto the stack. Of course it is
possible to move stack elements to storage or memory in order to get deeper access to the
stack, but it is not possible to just access arbitrary elements deeper in the stack without first
removing the top of the stack.
Instruction Set
The instruc on set of the EVM is kept minimal in order to avoid incorrect or inconsistent
implementa ons which could cause consensus problems. All instruc ons operate on the basic
data type, 256-bit words or on slices of memory (or other byte arrays). The usual arithme c, bit,
logical and comparison opera ons are present. Condi onal and uncondi onal jumps are
possible. Furthermore, contracts can access relevant proper es of the current block like its
number and mestamp.
For a complete list, please see the list of opcodes as part of the inline assembly documenta on.
Message Calls
Contracts can call other contracts or send Ether to non-contract accounts by the means of
message calls. Message calls are similar to transac ons, in that they have a source, a target, data
payload, Ether, gas and return data. In fact, every transac on consists of a top-level message call
which in turn can create further message calls.
A contract can decide how much of its remaining gas should be sent with the inner message call
and how much it wants to retain. If an out-of-gas excep on happens in the inner call (or any
other excep on), this will be signaled by an error value put onto the stack. In this case, only the
gas sent together with the call is used up. In Solidity, the calling contract causes a manual
excep on by default in such situa ons, so that excep ons “bubble up” the call stack.
As already said, 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. A er it has finished execu on, it can return data which will be
stored at a loca on in the caller’s memory preallocated by the caller. All such calls are fully
synchronous.
Calls are limited to a depth of 1024, which means that for more complex opera ons, loops
should be preferred over recursive calls. Furthermore, only 63/64th of the gas can be forwarded
in a message call, which causes a depth limit of a li le less than 1000 in prac ce.
There exists a special variant of a message call, named delegatecall which is iden cal 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.
This means that a contract can dynamically load code from a different address at run me.
Storage, current address and balance s ll refer to the calling contract, only the code is taken
from the called address.
This makes it possible to implement the “library” feature in Solidity: Reusable library code that
can be applied to a contract’s storage, e.g. in order to implement a complex data structure.
Logs
It is possible to store data in a specially indexed data structure that maps all the way up to the
block level. This feature called logs is used by Solidity in order to implement events. Contracts
cannot access log data a er it has been created, but they can be efficiently accessed from
outside the blockchain. Since some part of the log data is stored in bloom filters, it is possible to
search for this data in an efficient and cryptographically secure way, so network peers that do
not download the whole blockchain (so-called “light clients”) can s ll find these logs.
Create
Contracts can even create other contracts using a special opcode (i.e. they do not simply call the
zero address as a transac on would). The only difference between these create calls and normal
message calls is that the payload data is executed and the result stored as code and the caller /
creator receives the address of the new contract on the stack.
Warning
Note
Even if a contract’s code does not contain a call to selfdestruct , it can s ll perform that
opera on using delegatecall or callcode .
If you want to deac vate your contracts, you should instead disable them by changing some
internal state which causes all func ons to revert. This makes it impossible to use the contract,
as it returns Ether immediately.