This is a read note of Mastering Ethereum Ch07: Smart Contracts and Solidity. Contract accounts do not have private keys and so “control themselves” in the predetermined way prescribed by their smart contract code.

1 What Is a Smart Contract?

The term “smart contracts” refers to immutable computer programs that run deterministically in the context of an Ethereum Virtual Machine as part of the Ethereum network protocol—i.e., on the decentralized Ethereum world computer. Immutable means once deployed, the code of a smart contract cannot change. Unlike with traditional software, the only way to modify a smart contract is to deploy a new instance. Deterministic means the outcome of the execution of a smart contract is the same for everyone who runs it, given the context of the transaction that initiated its execution and the state of the Ethereum blockchain at the moment of execution.

Smart contracts operate with a very limited execution context call Ethereum Virutal Machine (EVM) context. They can access their own state, the context of the transaction that called them, and some information about the most recent blocks. The EVM runs as a local instance on every Ethereum node, but because all instances of the EVM operate on the same initial state and produce the same final state, the system as a whole operates as a single “world computer.”

Smart contracts are typically written in a high-level language, such as Solidity. But in order to run, they must be compiled to the low-level bytecode that runs in the EVM. Once compiled, they are deployed on the Ethereum platform using a special contract creation transaction, which is identified as such by being sent to the special contract creation address, namely 0x0. Each contract is identified by an Ethereum address, which is derived from the contract creation transaction as a function of the originating account and nonce.

Contracts only run if they are called by a transaction. A contract can call another contract that can call another contract, and so on, but the first contract in such a chain of execution will always have been called by a transaction from an EOA. Contracts never run “on their own” or “in the background.” Contracts effectively lie dormant until a transaction triggers execution, either directly or indirectly as part of a chain of contract calls. It is also worth noting that smart contracts are not executed “in parallel” in any sense—the Ethereum world computer can be considered to be a single-threaded machine.

Transactions are atomic, they are either successfully terminated or reverted. if a transaction is reverted, all of its effects (changes in state) are “rolled back” as if the transaction never ran. A failed transaction is still recorded as having been attempted, and the ether spent on gas for the execution is deducted from the originating account, but it otherwise has no other effects on contract or account state.

A contract can be “deleted,” removing the code and its internal state (storage) from its address, leaving a blank account. Any transactions sent to that account address after the contract has been deleted do not result in any code execution, because there is no longer any code there to execute. That operation costs “negative gas,” a gas refund, thereby incentivizing the release of network client resources from the deletion of stored state. It is also important to note that the SELFDESTRUCT capability will only be available if the contract author programmed the smart contract to have that functionality.

2 Solidity

Solidity compiler, solc, converts programs written in the Solidity language to EVM bytecode. The project also manages the important application binary interface (ABI) standard for Ethereum smart contracts, which we will explore in detail in this chapter. Each version of the Solidity compiler corresponds to and compiles a specific version of the Solidity language.

2.1 ABI

An application binary interface (ABI) is an interface between two program modules; often, between the operating system and user programs. An ABI defines how data structures and functions are accessed in machine code; this is not to be confused with an API, which defines this access in high-level, often human-readable formats as source code. The ABI is thus the primary way of encoding and decoding data into and out of machine code. The purpose of an Ethereum ABI is to define the functions in the contract that can be invoked and describe how each function will accept arguments and return its result.

A contract’s ABI is specified as a JSON array of function descriptions and events. A function description is a JSON object with fields type, name, inputs, outputs, constant, and payable. An event description object has fields type, name, inputs, and anonymous.

2.2 Programming with Solidity

Solidity offers the following data types: Boolean (bool), Integer (int, uint) form int8 to unint256 with 8 bit increments, Fixed point (fixed, unfixed), Address, Byte array (fixed) from bytes1 to bytes32, Byte array (dynamic), Enum, Array, Struct, and Mapping.

Solidity also offers some value literals that can be used to calculate differnt units such as time units and Ether units.

When a contract is executed in the EVM, it has access to a small set of global objects. These include the msg, block, tx and address objects. In addition, Solidity exposes a number of EVM opcodes as predefined functions such as addmod, keccak256, sha256, sha3, and this etc.

Solidity data types also include contract, interface and library.

The syntax to declare a function in Solidity is as follows: function FunctionName([parameters]) {public|private|internal|external} [pure|view|payable] [modifiers] [returns (return types)]. A function marked as a view promises not to modify any state. A pure function is one that neither reads nor writes any variables in storage. It can only operate on arguments and return data, without reference to any stored data. A payable function is one that can accept incoming payments. Functions not declared as payable will reject incoming payments.

Solidity offers a special type of function called a function modifier. You apply modifiers to functions by adding the modifier name in the function declaration. Modifiers are most often used to create conditions that apply to many functions within a contract.

Solidity’s contract object supports inheritance, which is a mechanism for extending a base contract with additional functionality.

Error handling in Solidity is handled by three functions: assert, require, and revert.

2.3 Events

When a transaction completes (successfully or not), it produces a transaction receipt that contains log entries that provide information about the actions that occurred during the execution of the transaction. Events are the Solidity high-level objects that are used to construct these logs. Events are especially useful for light clients and DApp services, which can “watch” for specific events and report them to the user interface, or make a change in the state of the application to reflect an event in an underlying contract. Event objects take arguments that are serialized and recorded in the transaction logs, in the blockchain.You can supply the keyword indexed before an argument, to make the value part of an indexed table (hash table) that can be searched or filtered by an application.

2.4 Calling Other Contracts

Calling other contracts from within your contract is a very useful but potentially dangerous operation. The risks arise from the fact that you may not know much about a contract you are calling into or that is calling into your contract. When writing smart contracts, you must keep in mind that while you may mostly expect to be dealing with EOAs, there is nothing to stop arbitrarily complex and perhaps malign contracts from calling into and being called by your code.

  • The safest way to call another contract is if you create that other contract yourself. That way, you are certain of its interfaces and behavior.
  • Another way you can call a contract is by casting the address of an existing instance of the contract. This is much riskier because we don’t know for sure whether that address actually is an expected object.
  • The third way is to construct a contract-to-contract call manually using call or delegatecall. It is the most flexible and the most dangerous mechanisms for calling other contracts. A delegatecall is different from a call in that the msg context does not change.