Recently, I have been looking for tutorials on how to create a simple Ethereum Smart Contract. As it turned out, most of blog posts and articles I found are quite outdated and have mistakes or issues related to the newer versions of the Ethereum tooling. At some point, I found myself constantly jumping between the stackexchange website, GitHub Issues and random tutorials to simply move forward and complete the originally picked up tutorial for voting.
This shows that the area of Blockchain is evolving very fast, so that existing tooling is constantly getting broken by newer versions.
In this blog post, we will implement a simple Ethereum contract using Truffle framework and Solidity. Then, we will deploy it to the test network Rinkeby. Hopefully, this tutorial will be much cleaner and last longer.
Smart Contract Subject
From our goal perspective, the main subject of the smart contract does not really matter. However, let’s choose something self-explanatory like a Bookmarks app. In our example contract, we will maintain a state with arrays of URLs and their user-defined names.
Create Dev Project
Let’s create a project directory and change into it:
We are going to use Truffle framework and its template to set up a project structure.
First, we need NPM installed on our local environment. It can be installed through a package manager.
For example, we can use brew
on macOS:
Now we need to install the Truffle framework:
In my case, I installed Truffle version 5.0.3
. Be aware that newer versions may have breaking changes, so that
this tutorial may not work in the future.
The two commands above are not necessary to be executed inside the Bookmarks
directory.
Now, let’s use Truffle to create a bare project with all required folders included. Run this command:
If the command is executed successfully, then we should have the following file structure in Bookmarks
directory:
Congratulations, the first checkpoint is done!
Compiling the Contract
Our contract code looks like this:
Save it to the contract folder with the name Bookmarks.sol
.
We are going to use Truffle to compile above contract using the command:
It should print something like this as output:
One more checkpoint achieved.
Deploying the Contract
The deployment process in Truffle is called migration. It consists of a folder
with scripted tasks in JavaScript. As we ran truffle init
earlier, we got a folder, called migrations
.
It contains a default script to deploy Migrations.sol
, which is an auxiliary
contract to maintain the state of the latest migration number on the Blockchain. If you’ve ever been
working with a database, this might be familiar. Migration allows to
evolve existing applications and deploy new version of a contract.
It keeps track, which scripts are not yet applied. We will get back to it again later.
The Truffle init
command has already prepared a script for a migration contract.
However, we need to add our own contract. This can be done as a separate script.
Let’s create a file at migrations/2_deploy_contracts.js
:
In order to deploy our contract, run:
Oops … we got an error!
You might have noticed that we didn’t mention ‘where’ we are going to deploy the contract. The Ethereum blockchain has several networks to choose from in order to deploy a smart contract. Truffle’s output above also gives a tip on what needs to be specified to make the migration/deployment successful.
As part of this tutorial, we want to deploy our Bookmarks contract to
the Ethereum test network Rinkeby
, using Truffle framework.
To make this happen, we have several options to choose from:
Ganache
Ganache is one more tool from the Truffle’s suite. It is a personal blockchain for Ethereum development you can use to deploy contracts, develop applications, and run tests. It comes with test accounts and fakeEther
. Since our goal is to deploy a contract to the real test network, we won’t go with this option.Local Ethereum client: Geth or other
Geth (Go Ethereum) is one of the solutions to connect to the main and test Ethereum network. Geth can be run as an Ethereumfull
node, which allows tomine
new blocks.Infura
Infura is an API platform, which exposes JSON-RPC API to connect to the Ethereum network. Basically, Infura, or a similar platform, is providing an infrastructure, so that one does not need to maintain a local node on its own.
We will go with Geth Local node to be connected to the Ethereum network directly. However, this would require us to download the whole blockchain from the very first block up to the current block.
Setting up Geth
Install Geth using the installation instructions based on your OS.
Start Geth with the HTTP RPC server enabled; this will be used by Truffle.
Once it is started, you should see block synchronization messages.
Above command starts Geth in fast
mode. This mode makes
synchronization much faster than running a full synchronization. However, this will still
take some time. In my case (2,2 GHz Intel Core i7, SSD disk),
it took less than an hour to sync with entire blockchain.
Connecting Truffle to Geth
Add below configuration to the Bookmarks/truffle-config.js
file inside the networks
section.
Above configuration points to localhost and port 8545; the same port we used to run Geth earlier.
An important configuration is network, which in our case points
to 4
Rinkeby, the public Geth PoA testnet
The property from needs to be set to your account address, which will be
used to make transaction in the Ethereum network.
That means this account needs to have some amount of Ether to deploy the contract
and later interact with it. In case you already have a test Rinkeby account,
just put it in truffle-config.js networks.my_local.from
instead of “…”.
Otherwise, follow the two sections below to create an account and request to test Ether.
Creating an Ethereum account (Optional)
In case we do not have an account yet, we can use the Geth console to create one.
Let the already existing Geth process run. Start a new terminal window and attach to existing Geth process via:
In the Geth console, we can use the JavaScript API to create and unlock a new account:
It will output the new account address, in my case it was: “0x560ae6e63f0e79c9027a590900291399cc954f00”. In your case, it will be a different account.
Now the account needs to be unlocked before it can be used to make a transaction. In the same Geth console where we created an account, run below command:
Do not forget to put the correct account number, which was given by the first command. Also, replace the pass phrase, which you have used to create an account.
600
is number of seconds for how long this account needs to be unlocked.
It means, that after 600 seconds an account will be locked again.
Make sure to run the next steps, which requires an unlocked account, within this duration.
Otherwise unlock it again, when needed.
Request free Ether (Optional)
Since we have created new account, we need to have some amount of Ether to deploy a contract and interact with it. Ethereum network provides, so called faucets to ask for fake Ether for testing purposes. Go to https://faucet.rinkeby.io and request an Ether using the short instruction given there. Fake Ether will be transferred to your account within several seconds, once you post the required HTTP link to a social media account.
Deploy with Truffle
Finally, we can run the migration scripts to deploy our Bookmarks contract:
Truffle will print a detailed output of what is actually being executed. For each deployment script from the migrations folder, we should see the following:
- transaction hash
- contract address
- amount of gas used and its price
- total cost in ETH
We can also see contract creation transactions on https://rinkeby.etherscan.io
More on the Migration
The last file index will be saved on the chain in the Migrations contract.
We can see its address in the Truffle logs above and then call a getter
function for the last_completed_migration
property.
Right after the initial deployment, this function should reply 2
,
because the last file in a sequence of the migrations folder is 2_deploy_contracts.js
.
This approach prevents us to run the same scripts again,
if we launch the above migration command. In case we need to deploy
a new contract version, we need to create a new script with a file
prefix = last_completed_migration + 1
.
For example, a file with name migrations/3_deploy_contracts.js
would be the next version of our application. Of course, we can copy paste
the previous file.
The Truffle migration API
has a lot of features to control the deployment process, so that we can
script our process in any way we need.
If you have trouble understanding which contract address is the Migrations contract, and which one is the main application contract after running several migration per day, you can decompile contract byte code, even online.
Congratulations, the hardest part is completed!
Call with Truffle
There are a couple of options we can choose from to call the deployed contract. In this tutorial, we will do that programmatically using Truffle console. One could also work with a contract via IDE like Remix.
Let’s run the Truffle console and call the deployed contract in the Rinkeby test network:
We get 0
back as expected, when we call getBookmarksCount
on empty an
Bookmarks contract. Let’s add one bookmark and try to read it.
If the addBookmark
transaction is completed successfully, then we should
get 1
back.
Summary
We have developed a new contract from scratch and deployed it to the test network Rinkeby. Deployment to the main network will be similar, but we will be using real Ether (i.e. real money) there to create a contract and make transactions. Our example contract can be improved a lot in terms of Gas usage efficiency, so that the contract creation and state modification function call would be cheaper. One of the ideas is to simply make our internal array a dynamic one, so that we do not need to create a fixed size array in advance. We could also tackle struct types to be more space efficient.
Using Truffle framework, we simplified our project creation process, compilation and deployment. Hopefully, we will see more useful and stable development tools for Ethereum blockchain in future.