Building dapps on Ethereum - part 2: smart contracts
This is the second post in my series about building decentralised apps (dapps) on Ethereum. In this post we will cover installation and configuration of the development environment for coding, compiling and testing smart contracts. You’ll also learn how to set up two different Ethereum blockchains on your local computer, firstly with testrpc
for automated testing and secondly with geth
which is a full-spec client for more advanced needs.
Before we begin
If you haven’t already, I would recommend you to read the previous post in this series:
Here is what we’ll cover:
- Important concepts
- Development tools
- Basic project structure
- Writing your first smart contract
- Writing tests for smart contracts
- Running tests
In the third and next post in this series we will learn how to write a user interface for our smart contract.
The setup described below assumes macOS v10.12 and NodeJS (v7 or v8). Further, it’s helpful to have a basic understanding of what a blockchain is, what a smart contract is and the big picture of what makes a decentralised app. I described the latter in the first post of this series.
1. Important concepts
So far on this blog we’ve covered the very basic concepts of what dapps are. But to actually develop dapps there are some further concepts that are important to understand. Pay extra attention to the following things throughout this post:
- Compilation — the act of compiling Solidity code into byte code that can be stored on the blockchain for execution.
- Call vs Transaction — calls are functions in smart contracts that read data from the blockchain while transactions write or update the state of the blockchain. Calls cost nothing to execute, while transactions cost gas
- Gas — the cost of transactions on the blockchain which is paid with Ether (remember that nothing is free).
- Migration — a transaction that create (or update) a contract to the blockchain.
- Mining — the act of verifying transactions which is rewarded with Ether (that in turn can be used to pay for gas).
2. Development tools
In order to be productive I would recommend a decent IDE. I’m used to JetBrains’ PhpStorm, so I used the ItelliJ-Solidity plugin for working with smart contracts:
We need a tool to help us with compilation, migration and testing of contracts. For this we use the Truffle framework. We also need a client in order to run a blockchain locally for testing. [TestRPC] is the standard choice which works well for automated testing, so we’ll use this. Install both tools globally so that you use them across your different projects:
$ npm install -g truffle ethereumjs-testrpc
As a bonus at the very end of this post we’ll cover how to install and run geth
for more advanced needs.
3. Basic project structure
The Truffle framework project has some decent resources for creating and developing smart contracts. For example, you can read their documentation and tutorials and learn how to use the truffle init
command to scaffold an example project. But for the purpose of this post we are going to manually create a minimum viable project in order to truly learn what everything means.
We will start by creating the following files and directory structure for our dapp:
build/
chain/
contracts/
- Migrations.sol
migrations/
- 1_initial_migration.js
scripts/
src/
test/
genesis.json
truffle.json
We have not yet created any project-specific files. The above structure, and the source code of these files are project-agnostic and can be re-used for almost any project. Here’s a brief description of each directory and the source code for each file so far:
build/
is where Truffle will put our compiled contracts.
chain/
is where the data directory and all the blockchain files will live. This is only required when using geth as the blockchain client.
scripts/
is where we later will put various utility scripts.
src/
is where we later will put the Javascript source files for our dapp front-end.
test/
is where we will keep our files for automated testing.
contracts/
is where the Solidity source code will live for all our contracts. The only contract so far is the required but project-agnostic Migrations.sol
that Truffle is using to store the migration state for your project-specific contracts (we have not yet created these). Don’t worry about understanding the source code of this contract yet.
migrations/
is where we need to put deployment scripts for all contracts. Naming of these scripts are important, they are executed in numerical order and the first script must be the required but project-agnostic 1_initial_migration.js
which migrates Migrations.sol
to the blockchain for the purpose we described earlier. Your project-specific deployment scripts must be named 2_example.js
, 3_example.js
etc. The format of deployment script is easy to understand and is described in the Truffle framework documentation. You will need both of these scripts:
genesis.json
contain information required to create the very first block in the blockchain (called the coinbase). This is only required when using geth as the blockchain client. The file contains information such as the initial mining difficulty, gas limit for transactions etc.
Interesting fact: Bitcoin’s coinbase block famously contains this piece of extra data:
The Times 03/Jan/2009 Chancellor on brink of second bailout for banks
truffle.js
contain basic configuration neededs for the Truffle framework in order to connect to the blockchain which we will start later in this guide.
4. Writing your first smart contract
Alright, we’re done with the basic project structure and we have everything we need to actually write our own contracts. Throughout this blog post series we will use sample code from the Iron Doers project which is a quite simple concept briefly described in a practical example of using blockchains and the project’s whitepaper.
Smart contracts are written in Solidity and I’ll cover some basics of how it works, but for more details I recommend you reading the Solidity documentation. Our first smart contract will be a simple organisation that can do four different things:
- Add doers (a transaction)
- Get the address of the trustee (a call)
- Get the number of doers (a call)
- Check if a given address is a doer (a call)
The source code for this organisation is very straight forward. Solidity is a strictly typed language which makes it quite easy and convenient to read:
I won’t go into all the linguistic details of how Solidity works, but there are a few key things going on here:
- State variables (e.g.
uint public doerCount;
) hold the data stored on the blockchain - The global variable
msg
hold metadata about the current message sent to the contract (such as the sender’s address) - The constructor (e.g.
function IronDoers()
) is only executed once when the contract is first deployed/created on the blockchain - Constant functions promise to not change the state of the blockchain, i.e. you can call these for free without having to pay gas
- Modifiers are bits of code that are executed before the modified function. These are often used for access checks. The
_;
syntax is where the execution of the modified function continues.
5. Writing tests for smart contracts
Tests are extremely important when dealing with systems of trust and financial data. Tests can be written in either Solidity or with Javascript (using Truffle’s abstractions that call out to the bytecode on the blockchain). I’ve chosen Javascript because our front-end will later be written in Javascript, which means we can have all our tests in a single language — that’s handy!
The Truffle framework embeds support for the popular Mocha test framework for Javascript. Here’s what some basic tests look like for the above contract:
Notice the syntax difference between a call vs transaction, i.e. contract.getDoerCount.call()
vs contract.addDoer()
.
6. Running tests
Now we’ve got our tools, the project structure, the first contract and some tests in place. Let’s run it all! First we need to start the blockchain we will use to run our tests on:
$ testrpc
Then, in a new terminal window you must compile and migrate all contracts to the blockchain you just started:
$ truffle compile --all
$ truffle migrate --reset
Then finally we can run the automated tests with one simple command:
$ truffle test
Hopefully you will see something like this:
Bonus: Installing and running geth
This setup will use the geth
client to run a local blockchain. This is good for advanced testing and debugging, but can be a little bit complicated to set up. If you want to run the test suite against a blockchain on geth
instead of testrpc
then you need to manually create all required accounts and fill them with Ether accordingly (this is what testrpc
does automatically for you).
Install global dependencies:
$ brew tap ethereum/ethereum
$ brew install ethereum
To begin operating the blockchain you first need to initiate the genesis block and then jump into the console:
$ geth --datadir ./chain init ./genesis.json
$ geth --datadir ./chain --rpc --rpcapi="db,eth,net,web3,personal" console
Note that this blockchain isn’t very useful yet — there are no accounts and no one is mining, so no transactions would be verified. To fix this we need to:
- Create the first user account
- Unlock said account
- Start mining Ether
First, while inside the geth console, create a new account and give it a password by running:
> personal.newAccount()
Second, to unlock the account for 9999 seconds by copying the address that was created in the previous step and run:
> personal.unlockAccount("the address", "the password", 9999)
Third, in order to be able to do transactions you need to (a) have some Ether to pay the gas fees and (b) validate said transactions. Both are achieved via mining, which you start in the console by running (using 1 CPU thread):
> miner.start(1)
At any point you can stop mining by running the below function. But remember, no transactions will be processed while mining is stopped! Mining is what keeps the system running.
> miner.stop()
Continue reading
- Part 3: user interface
- Part 4: decentralised hosting using Swarm
- Part 5: Ethereum Name Service and Swarm
Featured image: Creative Commons Attribution 3 - https://www.ethereum.org/assets