[box type="note" align="" class="" width=""]This post is a book extract from the title Mastering Blockchain, authored by Imran Bashir. The book begins with the technical foundations of blockchain, teaching you the fundamentals of cryptography and how it keeps data secure.[/box]
Our article aims to quickly get you up to speed with Blockchain development using the Solidity Programming language.
Solidity is a domain-specific language of choice for programming contracts in Ethereum. There are, however, other languages, such as serpent, Mutan, and LLL but solidity is the most popular at the time of writing this. Its syntax is closer to JavaScript and C. Solidity has evolved into a mature language over the last few years and is quite easy to use, but it still has a long way to go before it can become advanced and feature-rich like other well established languages. Nevertheless, this is the most widely used language available for programming contracts currently.
It is a statically typed language, which means that variable type checking in solidity is carried out at compile time. Each variable, either state or local, must be specified with a type at compile time. This is beneficial in the sense that any validation and checking is completed at compile time and certain types of bugs, such as interpretation of data types, can be caught earlier in the development cycle instead of at run time, which could be costly, especially in the case of the blockchain/smart contracts paradigm. Other features of the language include inheritance, libraries, and the ability to define composite data types.
Solidity is also a called contract-oriented language. In solidity, contracts are equivalent to the concept of classes in other object-oriented programming languages.
Solidity has two categories of data types: value types and reference types.
These are explained in detail here.
Boolean
This data type has two possible values, true or false, for example:
bool v = true;
This statement assigns the value true to v.
Integers
This data type represents integers. A table is shown here, which shows various keywords used to declare integer data types.
For example, in this code, note that uint is an alias for uint256:
uint256 x;
uint y;
int256 z;
These types can also be declared with the constant keyword, which means that no storage slot will be reserved by the compiler for these variables. In this case, each occurrence will be replaced with the actual value:
uint constant z=10+10;
State variables are declared outside the body of a function, and they remain available throughout the contract depending on the accessibility assigned to them and as long as the contract persists.
Address
This data type holds a 160-bit long (20 byte) value. This type has several members that can be used to interact with and query the contracts. These members are described here:
Balance
The balance member returns the balance of the address in Wei.
Send
This member is used to send an amount of ether to an address (Ethereum's 160-bit address) and returns true or false depending on the result of the transaction, for example, the following:
address to = 0x6414cc08d148dce9ebf5a2d0b7c220ed2d3203da;
address from = this;
if (to.balance < 10 && from.balance > 50) to.send(20);
Call functions
The call, callcode, and delegatecall are provided in order to interact with functions that do not have Application Binary Interface (ABI). These functions should be used with caution as they are not safe to use due to the impact on type safety and security of the contracts.
Array value types (fixed size and dynamically sized byte arrays)
Solidity has fixed size and dynamically sized byte arrays. Fixed size keywords range from bytes1 to bytes32, whereas dynamically sized keywords include bytes and strings. bytes are used for raw byte data and string is used for strings encoded in UTF-8. As these arrays are returned by the value, calling them will incur gas cost. length is a member of array value types and returns the length of the byte array.
An example of a static (fixed size) array is as follows:
bytes32[10] bankAccounts;
An example of a dynamically sized array is as follows:
bytes32[] trades;
Get length of trades:
trades.length;
Literals
These are used to represent a fixed value.
Integer literals
Integer literals are a sequence of decimal numbers in the range of 0-9. An example is shown as follows:
uint8 x = 2;
String literals
String literals specify a set of characters written with double or single quotes. An example is shown as follows:
'packt'
"packt”
Hexadecimal literals
Hexadecimal literals are prefixed with the keyword hex and specified within double or single quotation marks. An example is shown as follows:
(hex'AABBCC');
Enums
This allows the creation of user-defined types. An example is shown as follows: enum
Order{Filled, Placed, Expired };
Order private ord;
ord=Order.Filled;
Explicit conversion to and from all integer types is allowed with enums.
There are two function types: internal and external functions.
These can be used only within the context of the current contract.
External functions can be called via external function calls.
A function in solidity can be marked as a constant. Constant functions cannot change anything in the contract; they only return values when they are invoked and do not cost any gas. This is the practical implementation of the concept of call as discussed in the previous chapter.
The syntax to declare a function is shown as follows:
function <nameofthefunction> (<parameter types> <name of the variable>)
{internal|external} [constant] [payable] [returns (<return types> <name of
the variable>)]
As the name suggests, these types are passed by reference and are discussed in the following section.
Arrays represent a contiguous set of elements of the same size and type laid out at a memory location. The concept is the same as any other programming language. Arrays have two members named length and push:
uint[] OrderIds;
These constructs can be used to group a set of dissimilar data types under a logical group. These can be used to define new types, as shown in the following example:
Struct Trade
{
uint tradeid;
uint quantity;
uint price;
string trader;
}
Data location specifies where a particular complex data type will be stored. Depending on the default or annotation specified, the location can be storage or memory. This is applicable to arrays and structs and can be specified using the storage or memory keywords. As copying between memory and storage can be quite expensive, specifying a location can be helpful to control the gas expenditure at times. Calldata is another memory location that is used to store function arguments. Parameters of external functions use calldata memory. By default, parameters of functions are stored in memory, whereas all other local variables make use of storage. State variables, on the other hand, are required to use storage.
Mappings are used for a key to value mapping. This is a way to associate a value with a key. All values in this map are already initialized with all zeroes, for example, the following:
mapping (address => uint) offers;
This example shows that offers is declared as a mapping. Another example makes this clearer:
mapping (string => uint) bids;
bids["packt"] = 10;
This is basically a dictionary or a hash table where string values are mapped to integer values. The mapping named bids has a packt string value mapped to value 10.
Solidity provides a number of global variables that are always available in the global namespace. These variables provide information about blocks and transactions. Additionally, cryptographic functions and address-related variables are available as well.
A subset of available functions and variables is shown as follows:
keccak256(...) returns (bytes32)
This function is used to compute the keccak256 hash of the argument provided to the Function:
ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s)
returns (address)
This function returns the associated address of the public key from the elliptic curve signature:
block.number
This returns the current block number.
Control structures available in solidity are if - else, do, while, for, break, continue, return. They work in a manner similar to how they work in C-language or JavaScript.
Events
Events in solidity can be used to log certain events in EVM logs. These are quite useful when external interfaces are required to be notified of any change or event in the contract. These logs are stored on the blockchain in transaction logs. Logs cannot be accessed from the contracts but are used as a mechanism to notify change of state or the occurrence of an event (meeting a condition) in the contract.
In a simple example here, the valueEvent event will return true if the x parameter passed to function Matcher is equal to or greater than 10:
contract valueChecker {
uint8 price=10;
event valueEvent(bool returnValue);
function Matcher(uint8 x) returns (bool)
{
if (x>=price)
{
valueEvent(true);
return true;
}
}
}
Inheritance
Inheritance is supported in solidity. The is keyword is used to derive a contract from another contract. In the following example, valueChecker2 is derived from the valueChecker contract. The derived contract has access to all nonprivate members of the parent contract:
contract valueChecker
{
uint8 price=10;
event valueEvent(bool returnValue);
function Matcher(uint8 x) returns (bool)
{
if (x>=price)
{
valueEvent(true);
return true;
}
}
}
contract valueChecker2 is valueChecker
{
function Matcher2() returns (uint)
{
return price + 10;
}
}
In the preceding example, if uint8 price = 10 is changed to uint8 private price = 10, then it will not be accessible by the valuechecker2 contract. This is because now the member is declared as private, it is not allowed to be accessed by any other contract.
Libraries
Libraries are deployed only once at a specific address and their code is called via CALLCODE/DELEGATECALL Opcode of the EVM. The key idea behind libraries is code reusability. They are similar to contracts and act as base contracts to the calling contracts. A library can be declared as shown in the following example:
library Addition
{
function Add(uint x,uint y) returns (uint z)
{
return x + y;
}
}
This library can then be called in the contract, as shown here. First, it needs to be imported and it can be used anywhere in the code. A simple example is shown as follows:
Import "Addition.sol"
function Addtwovalues() returns(uint)
{
return Addition.Add(100,100);
}
There are a few limitations with libraries; for example, they cannot have state variables and cannot inherit or be inherited. Moreover, they cannot receive Ether either; this is in contrast to contracts that can receive Ether.
Functions
Functions in solidity are modules of code that are associated with a contract. Functions are declared with a name, optional parameters, access modifier, optional constant keyword, and optional return type. This is shown in the following example:
function orderMatcher(uint x) private constant returns(bool returnvalue)
In the preceding example, function is the keyword used to declare the function. orderMatcher is the function name, uint x is an optional parameter, private is the access modifier/specifier that controls access to the function from external contracts, constant is an optional keyword used to specify that this function does not change anything in the contract but is used only to retrieve values from the contract instead, and returns (bool returnvalue) is the optional return type of the function.
function <name of the function>(<parameters>) <visibility
specifier> returns (<return data type> <name of the variable>)
{
<function body>
}
In this example function, Matcher has the signature hash of d99c89cb. This information is useful in order to build interfaces.
contract myContract
{
function checkValues(uint x, uint y)
{
}
}
contract myContract
{
Function getValue() returns (uint z)
{
z=x+y;
}
}
A function can return multiple values. In the preceding example function, getValue only returns one value, but a function can return up to 14 values of different data types. The names of the unused return parameters can be omitted optionally.
function ()
{
throw;
}
In this case, if the fallback function is called according to the conditions described earlier; it will call throw, which will roll back the state to what it was before making the call. It can also be some other construct than throw; for example, it can log an event that can be used as an alert to feed back the outcome of the call to the calling application.
Layout of a solidity source code file
Version pragma
In order to address compatibility issues that may arise from future versions of the solidity compiler version, pragma can be used to specify the version of the compatible compiler as, for example, in the following:
pragma solidity ^0.5.0
This will ensure that the source file does not compile with versions smaller than 0.5.0 and versions starting from 0.6.0.
Import
Import in solidity allows the importing of symbols from the existing solidity files into the current global scope. This is similar to import statements available in JavaScript, as for example, in the following:
Import "module-name";
Comments
Comments can be added in the solidity source code file in a manner similar to C-language. Multiple line comments are enclosed in /* and */, whereas single line comments start with //.
An example solidity program is as follows, showing the use of pragma, import, and comments:
To summarize, we went through a brief introduction to the solidity language. Detailed documentation and coding guidelines are available online.
If you found this article useful, and would like to learn more about building blockchains, go ahead and grab the book Mastering Blockchain, authored by Imran Bashir.