Our journey into smart contracts

Jacques Nel
Trixta
Published in
14 min readJun 12, 2019

Introduction

At Trixta, one of the applications we are developing assists in analyzing smart contracts and working with statistics. Initially, some people in our team had very little understanding of smart contracts. We set aside three days to upskill ourselves on smart contracts.

My name is Jacques Nel, I have been a .NET developer for many years and over the last two have been more focussed on React and node.js applications. I’m used to writing code (not articles) but we thought it was valuable to share our journey for other developers wanting to get into smart contract development.

In this article, I give you an overview of what happened in these three days and how far we got on our smart contract learning journey. Before we could begin, we needed some sort of guide or map and decided to do training together in our meeting room following a course on Pluralsight by Ivan Mushketyk. I highly recommend this course as it gives you enough insight into:

  • How the blockchain works
  • Ethereum
  • Smart Contracts
  • Solidity
  • Truffle
  • Ganache
  • Creating a react web application interacting with smart contracts

HINT: You can use Pluralsight’s 30 day trial to attend the course for free.

What is Ethereum?

Ethereum is the open-source platform that anyone can have access to from anywhere in the world. A platform to do what, you might be asking yourself. Well, Ethereum is a decentralized platform where the code is paid for by the person who runs it. No, it’s not your everyday money you find in banks to pay but a crypto-currency known as Wei. Some people mention Ether, which is not wrong, 1 Ether is equivalent 1,000,000,000,000,000,000 Wei. However, it’s a little more complicated than that. It would not make sense that it is the same fixed cost, otherwise over a long period of time running smart contracts would not be viable. Instead, it costs a certain amount of gas which is converted to Wei.

Here is an article I found that explains it very well and here is a very useful sheet I found to summarize all the costs.

What are Smart Contracts?

So remember when I mentioned writing code on the Ethereum platform (Ethereum blockchain). That’s what a smart contract is, it’s the piece of code that is going to be executed or run on the Ethereum platform. There are a few things you need to know about a smart contract:

  • A smart contract has a unique address (aka Public key) in the Ethereum blockchain.
  • A smart contract can send or transfer Wei to another smart contract address.
  • The gas cost is usually paid by the account that calls the smart contract, not the contract itself.
  • A smart contract has rules that the developer can code such as a minimum amount of Wei to pay.

If an address on the Ethereum blockchain has code associated with it then it is a smart contract. If not then it’s just an Ethereum wallet address which anyone in the world can obtain and transfer Wei into to pay for using existing smart contracts or exchange for the currency in your country. Here is a nice article explaining smart contracts in plain English.

What is Solidity?

Solidity is the high-level language for writing smart contracts on Ethereum. How difficult is it to write in this language? To be brutally honest, it’s not that difficult to write a contract in this language especially if you have a Javascript coding background. What makes it difficult is writing the code in the most efficient manner. Remember all code that runs on the Ethereum blockchain costs Wei.

Clunky code = expensive to run.

What is Truffle or Ganache?

Truffle is a framework to help make writing Ethereum smart contracts easier, and it does exactly that. I honestly feel bad for developers that started without this tool. Not only does this tool give you a direction on writing smart contracts but also makes testing your smart contracts easy. It assists in deploying your smart contracts to the Ethereum blockchain. Before this tool, you had to write your own code to manually deploy your smart contracts to the Ethereum blockchain.

Ganache is a tool that allows you to create your own local personal Ethereum blockchain which you can use to run tests. Your own personal Ethereum blockchain has unlimited funds to test and play around with before actually deploying your smart contract on the main Ethereum blockchain out to the public.

Our journey

The first two days involved us working through the course and discussing as a team anything we were not sure about. This was a great decision because not only was no one left behind, but we were all learning together at the same pace. The course took us through the detailed process of writing a voting contract in Solidity.

On the third day, we were tasked with a mission to create our first smart contract as a team. At first, this looked daunting and very complicated compared to our voting example, but most things in life are like that. Take it one step at a time and turn those baby steps into a final solution.

The mission

We came up with a use case to test our new knowledge. The task was to build the Smart Contracts for a wool supply chain. The following diagram shows the various parties (1–9) involved in selling and deliver an order of wool.
The three smart contracts (A-C) act as immutable public records for various aspects of the order.

Contract Interaction Scenario

This is the order of events in the process:

  • Buyer (8) places an order (A) for $115
    - Contract (A) creates a $10 record in (B)
  • Delivery van (2) collects goods
  • Warehouse (3) receives goods
    - Delivery van (2) gets paid $5 from (A)
  • QA (4) attests quality (B)
    - QA (4) gets paid $10 from (B)
  • Shipping (5) collects goods
    - Warehouse (3) gets paid $18 from (A)
    - Contract (A) creates $17 record in (C)
  • Warehouse (6) receives goods
  • Inspector (7) Attests to quality & quantity (C) from (B)
    - Inspector (7) gets paid $5 from (C)
    - Shipping gets paid $12 from (C)
  • Goods delivered to the buyer (8), buyer checks against (C) before accepting
  • The buyer tells Contract (A) that order has been accepted
    - Verify that the buyer is the same person who initiated the order
    - Warehouse (6) gets paid $10 from (A)
    - Developer (9) gets paid $5 from (A)
    - Farmer gets paid balance ( $50 ) from (A)
This is how the placements and money flows

Looking at the given scenario we decided as a team to peer program and work on the most complicated contract together. Based on the diagram above we have three contracts to code: Purchase Order, Quality Record, and Inspector Record. Let’s start with the Purchase Order contract.

Getting started

The IDE we were using in the team was either Visual Studio Code or IntelliJ IDEA. I would pick either as they both have Solidity plugins to assist in coding the smart contracts. For this article, I will be using IntelliJ IDEA, just my personal preference.

The tools that we will be using for this project are:

  • Ganache for testing our smart contract
  • Truffle for developing and deploying our smart contract
  • Metamask for creating test Ethereum wallets
  • Nodejs for installing packages
  • Npm for downloading packages

Truffle has helpful boilerplates for creating dapps (decentralized applications) called boxes. For this project, we will be using the react box. We may later have a front-end in React to interact with our smart contracts but for now, we just want the truffle framework to create a foundation for us to work in.

npx truffle unbox react command will install truffle and create the foundation we need to get started.

Once this operation is completed, you’ll now have a project structure with the following items:

Next, let’s install Ganache. After installing Ganache and running the application you should see something similar in the diagram below.

For this project, we created a workspace called Delivery Pipeline

After creating our workspace we are presented with a list of addresses that have a balance of 100.00 Ether. These are going to be our test accounts we are going to use to interact with our smart contracts. Be aware of the NETWORK ID and the RPC SERVER address. We will be using this for our truffle.config file.

const path = require("path");module.exports = {
// See <http://truffleframework.com/docs/advanced/configuration>
// to customize your Truffle configuration!
networks: {
development: {
host: "localhost",
port: 8545,
network_id: "*", // Match any network id
gas: 4712388,
gasPrice: 2000,
},
ganache: {
host: "localhost",
port: 7545, // rpc server address of delivery pipeline workspace
gas: 4712388,
gasPrice: 2000,
network_id: "5777" // network id of delivery pipeline workspace
}
}
};

To compile your smart contracts truffle compile command will compile the solidity contracts in your contracts/ directory. Your compiled contracts can be found in your build/contracts/ directory. For further info on compiling contracts see here.

To deploy your smart contracts to the main Ethereum network or your local test network, you need to run the command truffle migrate . This will deploy your compiled smart contracts to the default develop network mentioned in your truffle.config file. But we want to deploy to the test network we created earlier, truffle migrate --network ganache. But hang on, how does the migration know what contracts to deploy and with what addresses and data. The migrations/ directory contains deployment script files. The first being our migrations contract that keeps track of what has been deployed thus far. 1_initial_migration.js

var Migrations = artifacts.require("./Migrations.sol");module.exports = function(deployer) {
deployer.deploy(Migrations);
};

Here is our second deployment script for our purchase order contract. 2_deploy_purchase_order_contract.js

var QualityRecord = artifacts.require("./QualityRecord.sol");
var InspectionRecord = artifacts.require("./InspectionRecord.sol");
var PurchaseOrder = artifacts.require("./PurchaseOrder.sol");
module.exports = async function(deployer, network, accounts) {
console.log("Migration accounts:");
console.log(accounts);
const developerAddress = accounts[0];
const buyerAddress = accounts[1];

await deployer.deploy(QualityRecord);
await deployer.deploy(InspectionRecord, QualityRecord.address);
await deployer.deploy(PurchaseOrder, developerAddress, QualityRecord.address, InspectionRecord.address, "Wool", 100, {from:buyerAddress, value:1000000});
};

For further info on migration see here.

As you can see my contracts are deployed.
Transactions for deploying the contracts

To test our contracts we create tests in our test/ directory. Truffle uses the Mocha testing framework and Chai for assertions to provide you with a solid framework from which to write your JavaScript tests. The truffle test --network ganache will run all test files found in the test/ directory against our test Ethereum network.

For further info on testing contracts see here.

For smart contracts, there are a few special variables and functions you need to be aware of. Here is a link about them but for this project, I am just going to mention the ones we need.

Special variables:

msg.sender (address): sender of the message (current call)

msg.value (uint): number of wei sent with the message

Special functions:

<address>.balance (uint256):balance of the Address in Wei

<address>.transfer(uint256 amount):send given amount of Wei to Address, throws on failure, forwards 2300 gas stipend, not adjustable

<address>.send(uint256 amount) returns (bool):send given amount of Wei to Address, returns false on failure, forwards 2300 gas stipend, not adjustable

selfdestruct(address recipient):destroy the current contract, sending its funds to the given Address

Purchase Order Contract (A)

For this contract, we are going to need to store:

  • Order details( quantity and name)
  • Developer’s address, Developer (9) gets paid $5 from (A)
  • Buyer’s address, Verify that the buyer is the same person who initiated the order
  • Farmer’s (Producer address), Farmer gets paid balance ($50) from (A)
  • Deliverer’s address, Delivery van (2) gets paid $5 from (A)
  • Warehouse 1 address, Warehouse (3) gets paid $18 from (A)
  • Warehouse 2 address, Warehouse (6) gets paid $10 from (A)
  • Quality Attester address, QA (4) gets paid $10 from (B)
  • Inspector’s address, Inspector (7) gets paid $5 from (C)
  • Shipper’s address, Shipping gets paid $12 from (C)
  • Order phase, to make sure no one attempts to call a function out of order
  • Contract costs, to reference the associated cost for each phase
  • Inspection Record Contract address, Contract (A) creates $17 record in (C)
  • Quality Record Contract address, Contract (A) creates a $10 record in (B)

To store the addresses and the phase that the contract is in we will use a mapping with an enum for the phase.

enum OrderPhase {
Buying,
Production,
Delivery,
Warehouse1,
Shipping,
Warehouse2,
QA,
Inspection,
Complete
}
mapping(uint => address payable) private providerForPhase;

The providerForPhase will store the phase with the corresponding address that made the call. To store the address for the buyer would look like this
providerForPhase[uint(OrderPhase.Buying)] = msg.sender

To return our address for the buyer would look like this
address buyerAddress = providerForPhase[uint(OrderPhase.Buying)];

For the order details, we will use a struct

struct Order {
uint quantity;
string name;
}
Order public order;

To keep track of the various costs to each participant, we will use a struct

struct ContractCosts {
uint purchaseOrder;
uint qualityRecord;
uint inspectionRecord;
uint delivery;
uint warehouse1;
uint warehouse2;
uint shipping;
uint developer;
uint producer;
uint inspector;
uint serviceCosts;
}

When you create a smart contract without passing an address, a new one will be deployed to the Ethereum chain, and there are costs involved as well to deploy a smart contract. However, what we want is that once a Quality Record Contract and Inspection Record Contract is deployed, for this Purchase Order Contract, we can call those same contracts again. To do this we need to store the address of the Quality Record Contract and Inspection Record Contract.

InspectionRecord private inspectionRecord;
QualityRecord private qualityRecord;

Meaning Quality Record Contract and Inspection Record Contract need to be deployed first and the addresses saved to the Purchase Order Contract.

qualityRecord = QualityRecord(_qualityRecordAddress);
inspectionRecord = InspectionRecord(_inspectionRecordAddress);

Now that we know what to store, we needed to come up with methods that can be called on the smart contract. We stubbed out each method and created todos for later implementation by the team.

Methods for contract:
In solidity, you can create your own modifiers, below is one modifier we created.

require is an error handling function in solidity to check a condition and if not met will revert the contract and output the message.

_; is to indicate that function code can continue

The modifier below is checking that the orderPhase passed in is the same as the order phase (providerForPhase) of the contract. We needed this modifier to keep a check that phases are in order when each of the relevant public methods on the contract is called.

modifier validOrderState(OrderPhase orderPhase){
require(providerForPhase[uint(orderPhase)] != address(0), “Order phase invalid”);
_;
}
function startProduction() public validOrderState(OrderPhase.Production) {
// TODO save msg.sender address as producer
// TODO update phase to OrderPhase.Production
}
function startDelivery()
public validOrderState(OrderPhase.Delivery) {
// TODO save msg.sender address as delivery;
// TODO update phase to OrderPhase.Delivery
}
function warehouse1Received() public validOrderState(OrderPhase.Warehouse1) {
// TODO save msg.sender address for warehouse1
// TODO update phase to OrderPhase.Warehouse1
// TODO pay Delivery address in ProviderForPhase from contract balance
}function qualityCheckDone(address payable qaAddress) public validOrderState(OrderPhase.QA) {
// TODO save qaAddress;
// TODO update phase to OrderPhase.QA
}
function startShipping(address payable shippingAddress) public validOrderState(OrderPhase.Shipping) {
// TODO save shipping address
// TODO update phase to OrderPhase.Shipping
// TODO pay Warehouse1 address in ProviderForPhase from contract balance
// TODO pay inspectionRecord contract from contract balance

}
function warehouse2Received() public validOrderState(OrderPhase.Warehouse2) {
// TODO save msg.sender address for warehouse2
// TODO update phase to OrderPhase.Warehouse2
}
function inspectionComplete(address inspectorAddress) public validOrderState(OrderPhase.Inspection) {
// TODO save inspectorAddress and phase
// TODO update phase to OrderPhase.Inspection
}

Quality Record Contract (B)

For this contract, we are going to need to store:

  • Purchase Order addresses, QA ( 4 ) attests quality ( B )

To store the purchase orders that have been tested for the quality we will use a map

mapping(address => bool) public purchaseOrderQualityRecords;

Methods for contract:

function qualityPassed(address purchaseOrderAddress) public {    PurchaseOrder purchaseOrder = PurchaseOrder(purchaseOrderAddress);//TODO update purchaseOrderQualityRecords map with purchaseOrderAddress//TODO call purchaseOrder.qualityCheckDone method
purchaseOrder.qualityCheckDone(msg.sender);
//TODO save the msg.sender as the qaAttesterAddress
//TODO transfer funds from purchaseOrder to qaAttesterAddress
}

For this function, you will notice the modifier view , this is to indicate that the function does not alter any state and therefore just returns data and will not cost any Wei to call.

function hasPassedQualityCheck(address purchaseOrderAddress) public view returns (bool) {
// TODO return purchaseOrderQualityRecords mapping with purchaseOrderAddress
}

Inspection Record Contract (C)

For this contract, we are going to need to store:

  • Quality Record Contract address, Inspector ( 7 ) Attests to quality & quantity ( C ) from ( B )
  • Passed Inspections, keep track of purchase order addresses that have gone under inspection

To store the address of the Quality Record Contract, we will use the same approach as we did previously in the Purchase Order Contract.

QualityRecord qualityRecordContract;qualityRecordContract = QualityRecord(_qualityRecordAddress);

For the passed inspections we will use a map containing a boolean with the address of the purchase order contract.

mapping(address => bool) passedInspections;

Methods for contract:

function hasInspectedPurchaseOrder(address purchaseOrderAddress) public view returns (bool){
// TODO return passedInspections mapping with purchaseOrderAddress
}
function passedInspection (address purchaseOrderAddress) public {
PurchaseOrder purchaseOrder = PurchaseOrder(purchaseOrderAddress);
//TODO use qualityRecordContract.hasPassedQualityCheck
//TODO if passed update passedInspections with purchaseOrderAddress
//TODO call purchaseOrder.inspectionComplete(msg.sender)
//TODO transfer funds from this contract to msg.sender(inspector address)
//TODO get shipper address from purchaseOrder and pay funds
}

Troubles along our journey

  • The scenario above introduced the use of more than one smart contract and interaction between them. This was something new that we had to investigate and took quite a bit of time to understand.
  • Testing smart contracts are a must but can be tricky if you don’t understand mocha or chai syntax.
  • Setting up your solidity compiler and environment for a windows user is quite a struggle.
  • We did not get a chance to fully test and understand the transferring of Wei from one smart contract to another.

Lessons learned from our journey

testing your smart contracts is a must — not only does it give an indication of how much Wei is required to run your smart contract but allows you to optimize the code doing more thorough tests. Knowing you have tested the functions of your smart contract and are executing their purpose correctly.

truffle doesn’t enable optimization by default — after reading the truffle config documentation we realized that truffle compile doesn’t enable contract optimization by default. Tweaking just a few settings saved us another ~20% gas in our tests.

how you store your state in smart contracts is important —Try to avoid arrays as it is more costly to save to an array than it is to a mapping . Remember the SSTORE operation is the most expensive operation in smart contracts.

Conclusion

Thank you for reading my first article, I hope this gave you some clarity on smart contract development. It’s not that intimidating to write on Ethereum and test them thanks to Truffle. What is intimidating, is to create a UI to interact with Ethereum.

It is a new way of thinking, to code smart contracts regarding the use of maps , interacting with other smart contracts and that everything costs Wei. Being aware that all your public functions are exposed, which could lead to hacks or abuse of your smart contract if you're not writing strict checks and rules. Such as ourvalidOrderState modifier we created earlier. Other aspects of smart contracts to think about is, firstly, how to make a profit from your smart contract, and, secondly, how to benefit the users of your smart contract. If there is no benefit from using your smart contract, then why would users want to use them.

How do you get feedback from your smart contracts? Where did they go wrong? Why did ‘x’ not happen? Where did the Wei go that I paid? These questions are somewhat of a mystery, and how Trixta can assist. By making it easy to know how much Wei your smart contracts are using, errors that occurred, statistics on the data from your smart contract, you gain invaluable information from Trixta that is currently not easy to find out or measure. If you found this useful, give it a clap! Good luck building.

--

--