Smart Contract is similar to a class in object oriented
programming. A smart contract can contain the following parts:
- Version Pragma
- Comments
- State Variables
- Functions
- Function Modifiers
- Events
- Structs Types
- Enums Types
Below is the brief definition of these components. In the
subsequent blogs I will go in depth into each of these components:
Version Pragma
This pragma directive tells the compiler to use the correction
version to compile the contract and to reject compiling with an incompatible
compiler. This annotation ensures that our code is always compiled correctly as
we intended.
The version pragma is used as follows:
pragma solidity ^0.4.14;
This directive is optional, but is highly recommended to
annotate every source file with this version pragma. The version pragma uses semantic
versioning and is denoted by [major, minor, patch] tuple. As shown above
0.4.14.
In the above version directive we used caret range to
specify the supported compilers. The caret range allows compiler greater than
the specified version and does not allow compiler greater than the left most
non-zero digit. In our definition the left most non-zero digit is 4 (0.4). So
the caret range allows compiler greater than the specified version (0.4.14) and
does not allow greater than the left most non-zero digit (0.5.0). In other
words, only compiler with version >= 0.4.14 and < 0.5.0 be allowed to
compile to compile our contract code. This version pragma directive can also
include prerelease tags such as alpha, beta. Below are examples of caret
ranges:
- ^0.4.14 := >=0.4.14 and <0.5.0
- ^1.2.3 := >=1.2.3 and <2.0.0
- ^0.2.3 := >=0.2.3 <0.3.0
- ^0.0.3 := >=0.0.3 <0.0.4
The solidity compiler can use complex rules for identifying
the correct compiler version. But this may be rarely used. Unless you want to
target a specific version range, you don’t need to go that complex.
State Variables
State Variables are values which are permanently stored in
contract storage. These are similar to the class variables.
pragma solidity ^0.4.14;
contract Service {
address client; // State variable to
store client who requests service
address contractor; // State
variable to store contractor for the service
address platform; // State
variable to store 0x address
}
In the above contract we defined three state variables
called client, contractor and platform which are of type address. We use these
variable to store the address of the client who requested service, contractor
who has hired to perform the service and 0x platform who facilitates the interaction
between client and contractor. I will revisit State Variables once we cover
more details.
Functions
Functions are the executable units of code within a contract.
Again these are similar to the functions in the object oriented world.
A contract can have the following types of functions
- Member functions
- Constructor
- Fallback function
- Constant functions
Member functions
These are the functions which modify the state variables of
the contract and performs transactions that are stored in the block chain.
These functions typically don’t have return values.
// hiring the
contractor to perform the work
function hire (address _contractor) {
contractor = _contractor;
}
In the above code the client of the service requestor
finalized the contractor and is hiring the contractor to perform the service. As
you see this function is updating the contractor state variable.
Constructor
A constructor is a special type of member function. The
constructor is called only once for the life of the contract and during the initialization
of the contract. Hence you can have all your initialization logic in this
constructor. As show below I am storing the client address, one who creates the
contract (msg.sender is the account which initializes the contract) and the 0x
platform address which can be used to arbitrage between client and contractor.
//
creates the service for the client
//
0x will be platform provider
function
Service
(address
_platform) {
client = msg.sender;
platform = _platform;
}
Fallback function
Fallback function is a function which does not have a
function name. This function is invoked whenever a contact is called with a
function name that does not exist. For example if user calls a function called “close”
and this close function does not exist in the contract, then the fallback function will be called. In our code we don’t want users to call functions that
don’t exist, hence reverting the call.
//default function
function() {
revert();
}
A
contract can have exactly one unnamed function. This function cannot have
arguments and cannot return anything.
Constant function
A constant function is used in scenarios where you want to read
the state variables of the contract and don’t want to update their state.
Constant functions prevent updating the state variables of the contract. In our
0x contract we will have a validate function which checks the terms and
conditions of our smart contract. For this POC the actual validation is out of
scope. So let’s assume that the validation is always successful and return true
as shown below:
// validate the terms
of the contract
function validate() constant returns (bool) {
//
for POC we are doing additional validations
return true;
}
Function Modifiers
Function Modifiers are used to amend the semantics of a
function in a declarative way. These can be used to automatically check a
condition prior and/or after the execution of a function. Here is an example on
how Function Modifiers are used in our 0x service contract
// modifier to ensure
client can only execute a function
modifier onlyClient() {
require(msg.sender == client);
_;
}
function hire (address _contractor)
onlyClient { // using function
modifier
contractor = _contractor;
}
In the above code we defined a function modifier,
onlyClient, in which we are checking whether the caller of the contract
function is a client. msg.sender contains the address of the caller. Require
checks and validates the condition is true before proceeding. If the condition
fails, requires throws an exception and exits the function.
The special symbol underscore “_” is very important in the
modifier. The body of the actual function will be inserted and executed at this
point. This enables us to use function modifiers before and/or after the
execution of the function.
A function can have multiple function modifiers. These
modifiers should be specified in a whitespace separated list and these
modifiers are evaluated in the order present.
Structs Type
Structs are the custom defined types in Solidity. These are
used to group several variables. In our 0x service we will be defining a custom
type, struct, Bidder as shown below:
// struct type to hold
bidder information
struct Bidder {
address contractor;
uint bidAmount;
}
mapping(address => Bidder) bids;
Struct types are typically used inside mappings and arrays.
Mapping type in the above code is similar to the dictionary in C#. Structs can
contain other structs and other complex types.
Enum Types
Enums are also user defined types in solidity (similar to
structs). Enum Types will have a finite set of values. In our 0x Service
contract we defined an enum to hold the current state of the contract as shown
below:
enum State { Bid, Award, Work,
Paid }
State contractState;
In the above code the State enum contains the possible
states of the contract, i.e., open for bid, contract awarded, work started and
finally payment for the service. contractState variable is used to store the
current state of the contract.