A 'Hello world' Ethereum smart contract using Truffle
Continuing from the previous article, I will cover how to use the Truffle framework to facilitate development, testing and deployment of Ethereum smart contracts.
A box of Truffles
Revisiting the previous article on creating a smart contract, we can immediately see a number of things that we would need to have an effective production deployment pipeline.
-
Scaffolding
Solidity is a relatively new language, which moves pretty fast. Solidity contracts have the additional constraint of complexity being expensive. This makes Solidity programming effectively an optimisation problem. 1
Therefore creating new contracts needs an easy way to quickly “scaffold” together optimal coding patterns and libraries. -
Testing
Although a smart contract is inspired by object-oriented programming (classes encapsulating code and state), initializing one involves deployment to a blockchain. The same applies to resetting the internal state as part of a CI pipeline.
This calls for a dedicated testing framework. -
Tracking of smart contracts
Each deployment of a smart contract (i.e. instantiation) results in a new persistent blockchain address to keep track of.
In the highly volatile pre-production environments (during active bug fixing and feature addition), tracking many different instances of a smart contract by hand is impossible. -
Promotion through environments
This is a corollary of the above. As a contract’s logic evolves and new versions are created, we need a way to keep track what is deployed where. I.e. “our DEV environment has been upgraded, but our UAT not yet”. In addition, most non-trivial applications will require the “co-ordinated” deployment of more than one smart contracts. This will be a familiar concept to people performing database migrations.
Truffle is the answer to all the above; a development framework and testing environment facilitating the SDLC of Ethereum smart contracts.
-
Scaffolding
Truffle introduces the concept of boxes; a way to quickly initialize a project with Solidity contracts, libraries and front-end code. -
Testing
It offers a framework to auto-deploy smart contracts before each test execution to isolate side-effects. Unit testing itself is based on Mocha and Chai. -
Tracking of smart contracts
Truffle keeps track of deployed contract addresses across environments. It has the option of a simple file-based mechanism or a more flexible queryable database. -
Promotion through environments
Truffle defines a smart contract-based mechanism to follow what has been deployed in which environment (migrations, in Truffle parlance). The mechanism is very similar to what database migration tools use internally. Here Truffle is using the blockchain as a migration state storage mechanism. 2
After this “sales pitch”, let’s install Truffle (e.g. by brew install truffle
).
The project
In a new folder, run truffle unbox sgerogia/hello_truffle_box
.
This command uses Git to fetch the box project, reads the box descriptor and executes the commands inside it.
This project is a repetition of the ‘Hello world’ contract, built in the previous article. I created a new
Truffle project (truffle init
), added the Hello contract, along with unit tests and, for ease of use, I packaged it as
a box.
Let’s quickly examine the contents of the new project.
- It has a
contracts
folder, where we can seeHello.sol
(our contract), alongsideMigrations.sol
(Truffle’s migration supporting contract). - The
migrations
folder contains the commands executed in sequence each time the contract(s) in our project are deployed to an environment. - The
test
folder has 2 examples of Javascript-based unit tests of contracts. - Finally, the
truffle-config.js
contains theganache
network definition pointing to a local running Ganache node.
Compiling
Compiling, testing and deploying are straight-forward command-line operations.
truffle compile
is the first thing to do. You will notice that the command creates a build
subfolder; this is where
Truffle keeps its metadata about the project.
Let’s try running the tests (truffle test
).
You will notice that, by default, Truffle is using the test
network. This is an in-memory blockchain emulator, using
the same engine as the Ganache app.
Changing the assertion in one of the tests, will yield an error like the following
Deploying
Let’s deploy the contracts to our Ganache app and see what happens behind the scenes.
First we need to create a clean Ganache workspace (or reset an existing one).
In a terminal running in our project’s folder, let’s migrate our contract.
truffle migrate --network ganache
We can see the results of the deployment of the 2 contracts: first Migrations
, then Hello
.
By default, Truffle is using the first account it finds in Ganache to perform the deployments (and charge for the cost
in ether).
Going into the Transactions tab of Ganache, we can inspect what has happened on the blockchain.
The transactions appear newer first.
We can see that we have
- 2 “contract creation” transactions, for the 2 deployed contracts, and
- 2 “contract call” transactions. We will see what these are in the next section, where we are…
Interacting with the contracts
In the same project folder, let’s open a new terminal with a Truffle console
truffle console --network ganache
This loads an “enhanced” Node.js console, with some convenient abstractions over the Web3 library.
First let’s see what those 2 “contract call” transactions where. Let’s type
1
2
Migrations.deployed().then(function(instance) {migrationsApp = instance;})
migrationsApp.last_completed_migration().then(function(result){return result.toNumber();})
We can see that the value of the last_completed_migration
field is 2
. This corresponds to the filename prefix
of the migrations
files. It tells Truffle “next time you migrate for this project and blockchain environment,
continue from this file”. In other words the numeric part of the filename is for Truffle, the rest is for readability.
Now let’s call our own contract, by typing
1
2
Hello.deployed().then(function(instance) {helloApp = instance;})
helloApp.getMessage()
Getting the value is much more straight-forward when in the context of Truffle.
Finally, let’s update the contract’s value.
helloApp.setMessage("Hello from Truffle!", {from: accounts[1]}).then(function() {return helloApp.getMessage();})
We are calling the contract from the 2nd account in Ganache (accounts[1]
). Because updating the contract’s state
involves mining a block (i.e. some delay), we verify the updated value (helloApp.getMessage()
) inside the return of
the promise.
Parting thought
And that’s it!
In this post we got a taste of the Truffle framework and how it facilitates the SDLC of smart contract programming.
Happy coding!
Footnotes