Soldity Crash Course

This is a very concise introduction into Solidity, the programming language of the Ethereum blockchain.

We want to build a decentralized application and to make our application tamperproof, we want to store information on the blockchain.

To achieve this specific task, we need to provide the blockchain with a specific set of rules. This set of rules will be defined in our smart contract. It will define functions we will later call by sending transaction to the blockchain. It regulates the way users can access and manipulate the sensitive data we want to store.

Ethereum supports two types of accounts. The ones controlled by a user holding a private key and the ones controlled by code, the smart contract. It essentially becomes an autonomous agent in the network that will be able to receive and send funds and manipulate its own storage.

Because smart contracts serve a unique purpose, it makes sense that they are written in their own unique language.
Solidity is a high-level language for writing smart contracts. Its syntax is very similar to JavaScript, but it is statically typed. This means that data types must be set and are enforced at compile-time. Like other object oriented languages, it supports inheritance, libraries and user-defined objects.

Please keep in mind that the information covered in this chapter is not a complete representation of everything that is possible in Solidity. Rather, we will look at the most important parts, moving from intuitive to the more complicated ones.

General Structure

Files written in Solidity are identified by the filename extension “.sol”. The first line always contains the version of Solidity used in the file.

1.  pragma solidity ^ 0.4.6;  

pragma stands for “pragmatic information” and indicates that the text that follows is meaningful to the compiler.
After that, a sol-file can contain a number of contracts, but most of the time it only contains one, just like most files when using object-oriented programming languages will only contain one class.
Contracts are defined as follows:

1.  contract MyContract {

2.   

3.  }  

Our new contract “MyContract” is still very empty so let’s fill it up with some functions.

Solidity makes a very important distinction between two types of functions. The ones that alter the state of the blockchain (i.e. by manipulating the contract’s storage) and the ones that don’t.
The latter are marked with the Identifier
constant and do not cost any Ether to execute since we are only reading from the existing state of the blockchain from our local node.
Both types follow the same principles and are defined as follows. The red parts are optional.

function name(type1 variable1, type2 variable2) identifiers returns(type1, type2) {}

1.  contract  MyContract  {     

2.      function  put(int  x)  { 

3.          //do something  

4.      }  

5.  }    

We just created a function “put” that expects an Integer “x” as an input variable.
In order to save x, we are going to create a variable to store it in. Following the principles of encapsulation, this variable will be defined outside the function but inside the contract. We will be able to access it from everywhere inside the contract. 

1.  contract MyContract {  

2.      int save;  

3.    

4.      function put(int x) {  

5.          save = x;  

6.      }  

7.  }  

Calling this function will now put the value “x” into our variable “save”. It is now saved in the storage associated with our smart contract and is therefore engraved in the blockchain. The only way to manipulate it, is through our very own contract. But first, we want to be able to read the value we just saved. Again, similar to other object-oriented languages, we can either make our Integer public (int public save;) or write a “get”-function. We will do the latter.

1.  contract MyContract {  

2.      int save;  

3.    

4.      function put(int x) {  

5.          save = x;  

6.      }  

7.    

8.      function get() constant returns(int) {  

9.          return save;  

10.     }  

11. }  

Our function “get” does not take any input variables. It is marked with the Identifier constant, which indicates that we are dealing with a function that does not alter the contract’s storage and thereby the state of the blockchain. As mentioned before, calling this function will not cost any Ether.

Of course, integers aren’t the only type of data we can use in Solidity.

Simple Types

What types of variables can we use in Solidity and how are they defined?

Booleans are defined as bool and can be either true orfalse. Operators are same as in JavaScript (!, &&, ||, ==, != )
Integers are defined as int for signed and uint for unsigned integers. int distributes its range around 0, while uint only holds positive values. If your variable does not need negative values, using uint will offer double the amount of range. The operators are also the same
(
i<, <=, ==, !=, >=, > ).

Strings can be used in Solidity as well. It is important to understand that a string is just a dynamically-sized byte array, storing UTF-8-encoded characters. Because handling dynamically-sized arrays is expensive and will consume a lot of gas, it is always worth a thought if a string is really necessary. Maybe a fixed-size byte representation will also do the trick. These range from bytes1 to bytes32.

Addresses are a type that is unique to Solidity. An address is a 20 byte value that is fitted to hold an Ethereum address. The special thing about the address type are its two member functions.
With
address.balance we can retrieve the addresse’s Ether balance in Wei. Because Ether can be split into very small fractions, units exist to simplify its handling. For now we only need to know about the smallest unit, Wei.
 
1 000 000 000 000 000 000 wei == 1 ether  
These units can be used directly in Solidity as we will see later on. Most functions that deal with Ether will take arguments in Wei, just like address.send().
address.send() can be used to let the contract send Ether to that address.

1.  address a = 0xB494124a56878E68d10cc72c7f0d5aB98bbF5409;  

2.  bool success = a.send(10);  

We just sent 10 Wei to the address we defined.

There is one very important thing to remember here. send() returns a bool indicating if the transfer was successful. We should always check for that, before we move on with our function. This is why it is often used inside and if-statement like this:

1.  if (!x.send(10)) return;  

This way, we can assure that our process will interrupt and roll back in case the transfer fails. A fail can actually very easily be achieved by a malevolent party by artificially overloading the call stack of the function call, because the call stack depth is fixed at 1024. A transaction can also fail if it runs out of gas.

It is recommended to use the address.transfer() function instead. It will make the contract call stop with an exception if the transfer fails.

Or even better: we avoid actively sending money completely by implementing patterns where the recipient withdraws the money.

A more advanced example

Contracts are not only used to handle the storing and manipulation of data, but also funds. Let’s say we want to write a contract that stores the address of every user that contributes 1 Ether to it. In order to do that, we will need to create an array of addresses. We will make it public so we can read it directly when testing.

1.  address[] public contributors;  

Now we need a function that will create the entry. Some new elements are used in this function.

1. payable – Not every function in your contract is able to receive Ether. Remember that functions are called by sending a transaction to the contract’s address. The data field of the transaction defines which function will be called and also holds the arguments. The value field, just like in a transaction from person to person, indicates the amount of Ether that is sent with the transaction. Now, sending Ether to a contract can be dangerous because, it being an autonomous agent, there may be no way of retrieving it afterwards. In order to prevent Ether accidently being sent to our contract, we have to specifically mark those functions that accept Ether as payable.

2. msg.value – Solidity features some blockchain-specific variables, which are globally available. block provides us with information about the current block like block.number,block.timestamp or block.coinbase.
now will also return the timestamp of the current block.
tx.gasprice and tx.origin deliver information about the transaction that initiated the call.
msg is the most interesting for our purposes, as it can give us the address of the sender of the message (msg.sender), as well as well as the amount of Wei that was sent with it (msg.value).

3. array.push() – When we want to add a new entry to a dynamically-sized array like “contributors”, the push()-function comes in handy. As an argument, we will provide the object when want to push on top of the exisiting array.
Deleting an entry from an array can be achieved using
delete array[index]. This will delete the entry corresponding to that index, but will leave it empty. To rearrange the array manually, we would need to write our own loop.

This is our contract, using all the new features we just learned.

1.  contract MyContract {  

2.      address[] public contributors;  

3.    

4.      function contribute() payable {  

5.          if (msg.value == 1 ether) {  

6.              contributors.push(msg.sender);  

7.          }

8.      }  

9.  }  

If we call the function “contribute” and send exactly 1 Ether with the transaction, our address will be pushed into the “contributors” array to record the contribution we made. Using indices, we could read addresses from the array (contr[index]) and even call it directly from our application with the web3.js API (MyContract.contr(index)) because it is public. We will learn more on how to do that later.

Structs and Mappings

Say we want our contract to remember more than just the contributors address. Maybe one can now contribute more than 1 Ether and we want to record the amount and also the real name of the contributor. To achieve that, we can create a struct.

1.  struct Contributor {  

2.      address a;  

3.      uint amount;  

4.      string name;  

5.  }  

We can then simply use the newly created structure and make an array out of it.

1.  Contributor[] contributors;  

Filling this array would look like this.

1.  function contribute(string name) payable {  

2.      if (msg.value >= 1 ether) {  

3.          contributors.push(Contributor(msg.sender, msg.value, name));  

4.      }  

5.  }  

Solidity makes it easy to create new instances of our struct. We have to follow the right order of attributes and use the syntax struct{type1 attribute1,type2 attribute2, …} to create it. We then push it directly into our array of structs created earlier.

It would be very convenient to be able to receive this whole array from the smart contract. Unfortunately, this is not possible yet!
If we think of possible use cases like querying for a specific address, we would have to get every single entry in separate calls from the blockchain. We could also implement the search algorithm in the smart contract. This could look like this:

 

1.  function getContrOf(address _a) constant returns(uint) {  

2.      for (uint i = 0; i < contributors.lengthi++) {  

3.          if (contributors[i].a == _a) {  

4.              return contributors[i].amount;  

5.          }  

6.      }  

7.      return 0;  

8.  }  

Notice that this function is also constant. The for-loop is familiar from jacaScript and we see that we can use the .length attribute of our array. We are addressing the different parts of it using the array[index].attribute syntax known from other object-oriented languages. If we find an address corresponding to the one given to us in the function, contributors[i].amount is returned. If we don’t find anything, we return 0.

Another elegant way to solve our problem would be to use a mapping.
It is essentially a key-value store. Here, we would be able to make the address point directly to the rest of our data. Creating a
mapping is very simple.

1.  mapping (address => Contributor) contributors;  

We have created a mapping where an address will act as a key and point to our self-created struct object as a value. We can now directly look for an entry corresponding to any address.

Filling this mapping would be done as follows.

1.  function contribute(string name) {  

2.      if (msg.value >= 1 ether) {  

3.          contrs[msg.sender] = Contributor(msg.sender,  msg.value,  name);    

4.      }  

5.  }  

In this case, we assign the “Contributor” instance to the field in our mapping with the key being the address. We actually could have gotten rid of the address attribute in the struct, but left it in for simplicity. Retreiving a specific contribution is now fairly easy.

1.  function getContrOf(address a) constant returns(uint) {  

2.      return contrs[a].amount;  

3.  }  

The advantage of the mapping is that we do not need to look through our data manually. The downside is we have no way of knowing how many entries there are and can therefore not retrieve all data, even with repetitive calls as mentioned before when talking about arrays.

A way around this trade-off would be to store all the keys of our mapping in an extra array.

Events and Oracles

Having learned about the most important capabilities of Solidity, it is also necessary to understand some fundamental limitations of smart contracts before developing a ÐApp.

One of the first ideas that come to mind when learning about blockchain is an application where the user can bet on the weather the next day or the result of a soccer game. The problem with these kind of applications is that they require information from outside the blockchain. Say, a contract would consult a standardized web service about the soccer result and use this information to make payouts. Every node would process the transaction, execute the code and call the web service. The web service can only answer to one call at a time. This is fine as long as the response for every call will be the same. But what if for some reason the web service shuts down halfway through the requests. Half the nodes would get an invalid result, while the other half executed the contract using the received results. This sort of un-deterministic behaviour would endanger the blockchain and is therefore not possible. Instead, so called oracles can be used, which would reliably publish information into the blockchain for it to be used by smart contracts.

Another problem would be calling an external API from a smart contract. Naturally, the API should not be called individually by every node as that would cause the called function to be executed multiple times and nodes would get different results depending on whether the call was successful or not. Here, the solution would be using EventLogs. Smart contracts can throw Events which applications can listen for. This means, instead of calling the API, a service would monitor the blockchain for a specific Event and then execute the call itself. Both these solutions require an outside party that needs to be trusted, so the trust created by the blockchain does not apply here. In the end, interactions with the blockchain are limited to simple write (oracle) and read (EventLogs) operations.

 

The End

I hope this tutorial was helpful to get you started with Solidity and give you a better and deeper understanding of Ethereum in general.

As we only covered some parts of Solidity, I encourage you to have a look at the full documentation at:  https://solidity.readthedocs.io/en/develop/solidity-in-depth.html

Thanks for reading


SIMON DOS

Comments