The Hitchhiker’s Guide to Smart Contracts in Ethereum

I’ve been working with smart contracts for 4 years, mainly in the Bitcoin blockchain. Some projects I participated in are Proof of Existence, bitcore, and Streamium. In the past months, I’ve been exploring and working with the Ethereum platform.

I’ve decided to compile a short guide to ease the way of future programmers learning Ethereum smart contract development. I’ve divided the guide in two sections: how to get started building smart contracts in Ethereum, and a quick note on smart contract security.

Getting started with Smart Contracts on Ethereum

0. Basic Concepts

This guide assumes you have a basic level of technical understanding on how cryptocurrencies and blockchains work. If you don’t, I recommend skimming over Andreas Antonopoulos’ Mastering Bitcoin book, Consensys’ “Just Enough Bitcoin for Ethereum” guide, or at least watching this short video by Scott Driscoll. To continue ahead you should know what a public and private key are, why a blockchain needs miners, how decentralized consensus is reached, what a transaction is, and the concepts of transaction scripting and smart contracts.

Two other important and related concepts you’ll need to understand before working with Ethereum are the Ethereum Virtual Machine and gas.

Ethereum was designed as a smart contract platform. Its origin is actually linked to a critique made by Vitalik Buterin on bitcoin as a very limited smart contract platform. The Ethereum Virtual Machine (EVM) is where smart contracts run in Ethereum. It provides a more expressive and complete language than bitcoin for scripting. In fact, it is a Turing Complete programming language. A good metaphor is that the EVM is a distributed global computer where all smart contracts are executed.

Given that smart contracts run in the EVM, there must be a mechanism to limit the resources used by each contract. Every single operation that is executed inside the EVM is actually simultaneously executed by every node in the network. This is why gas exists. An Ethereum transaction contract code can trigger data reads and writes, do expensive computations like using cryptographic primitives, make calls (send messages) to other contracts, etc. Each of these operations have a cost measured in gas, and each gas unit consumed by a transaction must be paid for in Ether, based on a gas/Ether price which changes dynamically. This price is deducted from the Ethereum account sending the transaction. Transactions also have a gas limit parameter that is an upper bound on how much gas the transaction can consume, and is used as a safe-guard against programming errors that could deplete an account’s funds. You can read more about gas here.

1. Setting up your environment

So, you know the basics, let’s set everything up to code! To start developing Ethereum apps (or DApps, for decentralized applications, as many people like to call them), you’ll need a client to connect to the network. It will act as your window to the distributed network, and provide a view of the blockchain, where all the EVM state is represented.

There are various compatible clients for the protocol, the most popular being geth, a Go language implementation. However, it’s not the most developer-friendly. The best option I’ve found is the testrpc node (yes, the name sucks). Trust me, it will save you a lot of time. Install it and run it:

$ sudo npm install -g ethereumjs-testrpc
$ testrpc

You should run `testrpc` in a new terminal and leave it running while you develop. Each time you run testrpc, it will generate 10 new addresses with simulated test funds for you to use. This is not real money and you’re safe to try anything with no risk of losing funds.

The most popular language for writing smart contracts in Ethereum is Solidity, so we’ll be using that. We’re also using the Truffle development framework, which helps with smart contract creation, compiling, deployment and testing. Let’s start:

# First, let's install truffle
$ sudo npm install -g truffle
# let's setup our project
$ mkdir solidity-experiments
$ cd solidity-experiments/
$ truffle init

Truffle will create all the files for an example project, including contracts for MetaCoin, a sample token contract. You should be able to compile the example contracts by running `truffle compile`. Then, to deploy the contracts to the simulated network using the testrpc node we have running, you need to run `truffle migrate`:

$ truffle compile
Compiling ConvertLib.sol...
Compiling MetaCoin.sol...
Compiling Migrations.sol...
Writing artifacts to ./build/contracts
$ truffle migrate
Running migration: 1_initial_migration.js
Deploying Migrations...
Migrations: 0x78102b69114dbb846200a6a55c2fce8b16f61a5d
Saving successful migration to network...
Saving artifacts...
Running migration: 2_deploy_contracts.js
Deploying ConvertLib...
ConvertLib: 0xaa708272521f972b9ceced7e4b0dae92c77a49ad
Linking ConvertLib to MetaCoin
Deploying MetaCoin...
MetaCoin: 0xdd14d0691ca607d9a38f303501c5b0cf6c843fa1
Saving successful migration to network...
Saving artifacts...
Note to Mac OS X users: Truffle is sometimes confused by .DS_Store files. If you get an error mentioning one of those files, just delete it.

We just deployed the sample contracts to our test node. Wohoo! That was easy, right? Time to create our own contract now!

2. Writing your first Ethereum smart contract

In this guide we’ll be writing a simple Proof of Existence smart contract. The idea is to create a digital notary that stores hashes of documents as proofs of their existence. Use `truffle create:contract` to get started:

$ truffle create:contract ProofOfExistence1

Now open contracts/ProofOfExistence1.sol in your favorite text editor (I use vim with Solidity syntax highlighting), and paste this initial version of the code:

pragma solidity ^0.4.2;
// Proof of Existence contract, version 1
contract ProofOfExistence1 {
// state
bytes32 public proof;
  // calculate and store the proof for a document
// *transactional function*
function notarize(string document) {
proof = calculateProof(document);
}
// helper function to get a document's sha256
// *read-only function*
function calculateProof(string document) constant returns (bytes32) {
return sha256(document);
}
}

We’ll start with something simple but incorrect and move towards a better solution. This is a definition of a Solidity contract, which is like a class in other programming languages. Contracts have state and functions. It is important to distinguish two kinds of functions that can appear in a contract:

  • Read-only (constant) functions: functions that don’t perform any state changes. They only read state, perform computations, and return values. As these functions can be resolved locally by each node, they cost no gas. Marked with the keyword `constant`.
  • Transactional functions: functions that perform a state change in the contract or move funds. As these changes need to be reflected in the blockchain, transactional function execution requires sending a transaction to the network and spending gas.

Our contract above has one of each kind, marked in the documentation. We’ll see how the kind of function we’re using with modifies how we interact with the smart contract in the next section.

This simple version only stores one proof at a time, using the data type bytes32, or 32 bytes, which is the size of a sha256 hash. The transactional function `notarize` allows one to store the hash of a document in our smart contract’s state variable `proof`. Said variable is public, and is the only way a user of our contract has to verify if a document has been notarized. We’ll do that ourselves shortly, but first…

Let’s deploy ProofOfExistence1 to the network! This time, you’ll have to edit the migration file (migrations/2_deploy_contracts.js) to make Truffle deploy our new contract. Replace the contents with the following:

/*
* migrations/2_deploy_contracts.js:
*/
module.exports = function(deployer) {
deployer.deploy(ConvertLib);
deployer.autolink();
deployer.deploy(MetaCoin);
// add this line
deployer.deploy(ProofOfExistence1);
};

You can also optionally remove the lines about ConvertLib and MetaCoin, which we’ll no longer be using (If you do this, remove the test folder too, or build will fail. Thanks Vu Tien Khang for pointing this out). To run this migration again, you’ll need to use the reset flag, to make sure it’s run again.

truffle migrate --reset

More on how Truffle migrations work can be found here.

3. Interacting with your smart contract

Now that our contract is deployed, let’s play with it! We can send messages to it via function calls and read its public state. We’ll use the Truffle console for that:

$ truffle console
// get the deployed version of our contract
truffle(default)> var poe = ProofOfExistence1.deployed()
// and print its address 
truffle(default)> console.log(poe.address)
0x3d3bce79cccc331e9e095e8985def13651a86004
// let's register our first "document"
truffle(default)>
poe.notarize('An amazing idea')
Promise { <pending> }
// let's now get the proof for that document
truffle(default)> poe.calculateProof('An amazing idea').then(console.log)
Promise { <pending> }
0xa3287ff8d1abde95498962c4e1dd2f50a9f75bd8810bd591a64a387b93580ee7
// To check if the contract's state was correctly changed:
truffle(default)> poe.proof().then(console.log)
0xa3287ff8d1abde95498962c4e1dd2f50a9f75bd8810bd591a64a387b93580ee7
// The hash matches the one we previously calculated

Note that all function calls return a Promise, and we use `.then(console.log)` to print the result once the Promise resolves if we want to check it.

First thing we do is obtain a representation of our deployed contract and store it in a variable called `poe`.

We then call the transactional function `notarize`, which involves a state change. When we call a transactional function, we get a Promise that resolves to a transaction id, not what the actual function returns. Remember that to change the EVM state we need to spend gas and send a transaction to the network. That’s why we get a transaction id as the result of the Promise, from the transaction that did this state change. In this case, we are not interested in the transaction id, so we just discard the Promise. When writing a real app, we’ll want to save it to check the resulting transaction and catch errors.

Next, we call the read-only (constant) function `calculateProof`. Remember to mark your read-only functions with the keyword `constant`, or else Truffle will try to craft a transaction to execute the function. This is a way to tell Truffle that we’re not interacting with the blockchain but just reading from it. By using this read-only function, we obtain the sha256 of the ‘An amazing idea’ “document”.

We now need to contrast this with the state of our smart contract. To check if the state changed correctly, we need to read the `proof` public state variable. To get the value of a public state variable, we can call a function of the same name, which returns a Promise. In our case, the output hash is the same, so everything worked as expected :)

For more info on how to interact with contracts, read this section of the Truffle documentation.

As you can see from the snippet above, our first version of the Proof of Existence smart contract seems to be working! Good work! It’s only good for registering one document at a time, though. Let’s create a better version.

4. Iterating the contract code

Let’s change the contract to support multiple document proofs. Copy the original file with the name contracts/ProofOfExistence2.sol and apply these changes. The main changes are: we change the proof variable into a bytes32 array and call it proofs, we make it private, and we add a function to check if a document has already been notarized by iterating that array.

pragma solidity ^0.4.2;
// Proof of Existence contract, version 2
contract ProofOfExistence2 {
// state
bytes32[] private proofs;
  // store a proof of existence in the contract state
// *transactional function*
function storeProof(bytes32 proof) {
proofs.push(proof);
}
  // calculate and store the proof for a document
// *transactional function*
function notarize(string document) {
var proof = calculateProof(document);
storeProof(proof);
}
  // helper function to get a document's sha256
// *read-only function*
function calculateProof(string document) constant returns (bytes32) {
return sha256(document);
}
  // check if a document has been notarized
// *read-only function*
function checkDocument(string document) constant returns (bool) {
var proof = calculateProof(document);
return hasProof(proof);
}
  // returns true if proof is stored
// *read-only function*
function hasProof(bytes32 proof) constant returns (bool) {
for (var i = 0; i < proofs.length; i++) {
if (proofs[i] == proof) {
return true;
}
}
return false;
}
}

Let’s interact with the new functions: (remember to update migrations/2_deploy_contracts.js to include the new contract and run `truffle migrate --reset`)

// deploy contracts
truffle(default)> migrate --reset
// Get the new version of the contract
truffle(default)> var poe = ProofOfExistence2.deployed()
// let's check for some new document, and it shouldn't be there.
truffle(default)> poe.checkDocument('hello').then(console.log)
Promise { <pending> }
false
// let's now add that document to the proof store
truffle(default)> poe.notarize('hello')
Promise { <pending> }
// let's now check again if the document has been notarized!
truffle(default)> poe.checkDocument('hello').then(console.log)
Promise { <pending> }
true
// success!
// we can also store other documents and they are recorded too
truffle(default)> poe.notarize('some other document');
truffle(default)> poe.checkDocument('some other document').then(console.log)
Promise { <pending> }
true

This version is better than the first, but still has some problems. Note that every time we want to check if a document was notarized, we need to iterate through all existing proofs. A better structure to store proofs is a map. Luckily, Solidity supports maps, and calls them mappings. Another thing we’ll improve in this version is removing all that extra comments marking read-only or transactional functions. I think you get it by now :)

Here’s the final version, which should be pretty easy to understand because you followed along previous versions:

pragma solidity ^0.4.2;
// Proof of Existence contract, version 3
contract ProofOfExistence3 {
  mapping (bytes32 => bool) private proofs;
  // store a proof of existence in the contract state
function storeProof(bytes32 proof) {
proofs[proof] = true;
}
  // calculate and store the proof for a document
function notarize(string document) {
var proof = calculateProof(document);
storeProof(proof);
}
  // helper function to get a document's sha256
function calculateProof(string document) constant returns (bytes32) {
return sha256(document);
}
  // check if a document has been notarized
function checkDocument(string document) constant returns (bool) {
var proof = calculateProof(document);
return hasProof(proof);
}
  // returns true if proof is stored
function hasProof(bytes32 proof) constant returns(bool) {
return proofs[proof];
}
}

That looks good enough. And it works exactly as the 2nd version. To try it out, remember to update the migration file and run `truffle migrate --reset` again. All the code in this tutorial can be found in this GitHub repo.

5. Deploying to the real testnet network

Once you’ve tested your contract extensively using testrpc in the simulated network, you’re ready to try it in the real network! To do that, we need a real testnet/livenet Ethereum client. Follow these instructions to install geth.

During development, you should run nodes in testnet mode, so that you can test everything out without risking real money. Testnet mode (also known as Morden in Ethereum) is basically identical to the real Ethereum, but the Ether token there has no monetary value. Don’t be lazy and remember to always develop in testnet mode, you will regret it if you lose real Ether due to a programming error.

Run geth in testnet mode, with RPC server enabled:

geth --testnet --rpc console 2>> geth.log

This will open a console where you can type in basic commands to control your node/client. Your node will begin downloading the testnet blockchain, and you can check progress by checking eth.blockNumber. While the blockchain is downloading, you can still run commands. For example, let’s create an account: (remember the password!)

> personal.newAccount()
Passphrase:
Repeat passphrase:
"0xa88614166227d83c93f4c50be37150b9500d51fc"

Let’s send some coins there and check the balance. You can get free testnet Ether here: https://zerogox.com/ethereum/wei_faucet. Just copy-paste the address you just generated and this faucet will send you 1 testnet Ether. To check you balance, run:

> eth.getBalance(eth.accounts[0])
0

It will show no balance because your node hasn’t synced with the rest of the network yet. While you wait for that, check your balance in a testnet block explorer. There, you can also see the current testnet highest block number (#1355293 at the time of writing), which you can use in combination with eth.blockNumber to know when your node has synced completely.

Once your node has synced, you’re ready to deploy the contracts to the testnet using Truffle. First, unlock your main geth account, so that Truffle can use it. And be sure that it holds some balance, or you won’t be able to push a new contract to the network.

> personal.unlockAccount(eth.accounts[0], "mypassword", 24*3600)
true
> eth.getBalance(eth.accounts[0])
1000000000000000000

Ready to go! If some of these two are not working for you, check the steps above and make sure you’ve completed them correctly. Now run:

$ truffle migrate --reset

Note that this time, it will take longer to complete, as we’re connecting to the actual network and not one simulated by testrpc. Once it completes, you can interact with the contract using the same approach as before.

The testnet-deployed version of ProofOfExistence3 can be found at the address 0xcaf216d1975f75ab3fed520e1e3325dac3e79e05.

I’ll leave the details on how to deploy to the live network to the reader. You should only do this once you’ve extensively tested your contracts in simulated and test networks. Remember any programming error can result in monetary loss in the livenet!

Smart Contract security in Ethereum is hard

“Smart contracts are pretty difficult to get right.” Emin Gün Sirer

Given that by nature, smart contracts are computer code that define how money moves, I couldn’t end this guide without even a small note on security. I’ll be talking about smart contract security in much more depth at future posts (edit: like this one), but here are some quick notes to get you started.

Some problems you should be aware of (and avoid):

  • Reentrancy: Do not perform external calls in contracts. If you do, ensure that they are the very last thing you do.
  • Send can fail: When sending money, your code should always be prepared for the send function to fail.
  • Loops can trigger gas limit: Be careful when looping over state variables, which can grow in size and make gas consumption hit the limits.
  • Call stack depth limit: Don’t use recursion, and be aware that any call can fail if stack depth limit is reached.
  • Timestamp dependency: Do not use timestamps in critical parts of the code, because miners can manipulate them.

These are provided just as examples of unexpected behaviors that can lead for theft or destruction of funds in your smart contract. The moral is: if you’re writing smart contracts, you’re writing code that handles real money. You should be very careful! Write tests, do code reviews, and audit your code.

The best way to avoid obvious security problems is to have a solid understanding of the language. I recommend you read the Solidity documentation if you have the time. We still need better tools for acceptable smart contract security.


That’s it! I hope you enjoyed reading this guide and learning your first steps in programming smart contracts in Ethereum! This is still a very new industry and there’s lot of room for new apps and tools. Feel free to contact me with ideas or prototypes.

Follow us on Medium or Twitter to keep updated on our smart contract development publications.