Coding Smart Contracts – Tutorial Part I

How to Write, Deploy and Test a Smart Contract

Natallia Martchouk, co-founder of trimplement, the fintech enabler
Natallia Martchouk, co-founder of trimplement, explains how to develop Ethereum smart contracts.

In this article, I will give you a smart contract tutorial. It will tell you how to quickly write, test and deploy Ethereum smart contracts. My motivation is to help people to make the first steps. There are several good tutorials which helped me to get started. But I missed kind of a “cookbook recipe” for the entire journey, starting with the installation of tools and frameworks and ending with deployment to Ethereum and usage out of an application.

And so, I decided to write down all the steps involved and hope that you will find it helpful!

I’m working on a Mac, but I’ll provide links to the documentation of all tools and frameworks so that you’ll be able to find fitting instructions for your personal environment.

Today we will: 

  • Setup an environment that  allows you to write production-ready smart contracts
  • Write a simple smart contract
  • Test security and style guide issues with solhint
  • Write unit tests with a Truffle framework
  • Deploy the contract on the Rinkeby testnet using MetaMask and Remix
  • Execute calls on the deployed smart contract

1. Prepare

Before you start you will need the following tools, frameworks, and resources.  Some of them need to be installed, some of them are available online. Once you have the full setup, you are good to go. This setup is also the starting point for my second tutorial, where you will learn to use the smart contract from a java application.

npm install -g truffle

Verify installation:

truffle version
brew update
brew upgrade
brew tap Ethereum/Ethereum
brew install solidity
  • Web3j for generation of Solidity smart contract function wrappers and interaction with Ethereum clients like parity or geth (we will use parity) from our java application
brew tap web3j/web3j
brew install web3j
  • Parity, an Ethereum client that maintains a local copy of the Ethereum testnet, alternatively you can use geth or even infura – a centralized Ethereum client
  • MetaMask Browser Extension for easy management of your Ethereum accounts and deployment of smart contracts on a non-local environment. When developing locally you can use Remix + your local parity instance (as Web3J provider), but for non-local deployment, I’d recommend to use Remix + MetaMask (select Environment: Injected Web3 in Remix) so that you don’t need to make parity available to the outside world while allowing access for Remix. In this case, we simply separate contract deployment (Remix, MetaMask) and contract usage (java, web3j, parity).
  • Remix for developing, deploying and testing your smart contract.
  • Solhint is helpful to quickly verify you smart contract code and check security and style guide issues.
npm install -g solhint

Verify installation:

solhint -V

2. Write the Smart Contract Code

Let’s start with writing a simple smart contract code. For testing purposes we will just deploy a very simple smart contract Ownable which has knowledge of its owner address and defines a couple of functions that we can use for testing:

pragma solidity ^0.5.0;

/**
* @title Ownable
* @dev A basic contract for all contracts that need to be administered by the contract owner.
* It provides a function modifier 'onlyOwner' that can be used in
* the derived contracts for functions executable only by the
* contract owner.
*/
contract Ownable {

    // Keeps the owner of the contract
    address private contractOwner;

    // C'tor
    constructor() public {
        contractOwner = msg.sender;
    }

    modifier onlyOwner() {
        require(msg.sender == contractOwner);
        _;
    }

    /**
     * @dev determine if this address is the owner of this contract
     * @return bool
     */
    function isContractOwner(address _address) view public returns (bool) {
        return _address == contractOwner;
    }

    /**
     * @dev returns the address of the owner of this contract
     * @return address
     */
    function getContractOwner() view public returns (address){
      return contractOwner;
    }
}

You can copy and paste this code into a file and name it Ownable.sol. Please create a directory myproject/ (which we will use as the main directory) and a subdirectory contracts/ to store the file (myproject/contracts/). Now you can quickly check with solhint whether the contract has some issues. Solhint expects to find a config .json file in the directory. 

$ cd myproject/contracts/
$ vi .solhint.json

Insert:

{
"extends": "solhint:default"
}

And save the .solhint.json file.

Check the contract:

$ solhint Ownable.sol

Ideally the output of solhint should be empty. Here is an example of how the solhint would report a formatting issue:


$ solhint Ownable.sol 

Ownable.sol
37:7  error Expected indentation of 8 spaces but found 6  indent

✖ 1 problem (1 error, 0 warnings)

If the output is empty, there is nothing more to do in this step. If not,  you will need to fix the issues pointed out by solhint and then continue.

Let’s develop some tests with Truffle.

3. Write the Truffle Tests

With the Truffle framework you can easily develop and run tests for your smart contract locally. Actually, Truffle allows you to compile and run tests on different blockchains (testnets or the livenet) using configuration in truffle-config.js. See http://truffleframework.com/docs/advanced/configuration for more detailed documentation. 
I’m going to show you how to use the truffle’s built-in development blockchain. For this, you won´t need to configure anything, the truffle-config.js shall exist, but can remain empty:

module.exports = {

  // See <http://truffleframework.com/docs/advanced/configuration> for more information
  // We can use truffle to compile and deploy contracts to different networks and chains
  // To be configured

};

Create a truffle-config.js and store it in your main directory myproject/.

Now let’s write a simple test for our contract Ownable.sol.

Create a subdirectory test in myproject/, create a file called OwnableTest.js there.

Add some tests:

// The contract to test
const Ownable = artifacts.require("Ownable");

// Start with a clean test state
contract('Ownable', accounts => {

// Keep Ownable instance
let OwnableContract;
// Keep the account of the contract owner
let Owner;
// Keep the address of the first account that was created by truffle for you
const Account1 = accounts[1];

//******************************************************************
// Test cases for the isContractOwner method
//******************************************************************

// Test for the isContractOwner method - true
it("IsContractOwner should return true", () => {
return Ownable.deployed()
.then(function(instance) {
OwnableContract = instance;
// Get contract owner
return OwnableContract.getContractOwner.call();
}).then(function(resultOwner) {
Owner = resultOwner;
// Call isContractOwner for the returned owner address
  return OwnableContract.isContractOwner.call(Owner);
}).then(function(resultIsOwner) {
// Check that the result is true
  assert.equal(resultIsOwner.valueOf(), true, "Expected the result to be true");
  return true;
}).catch(function(e) {
// We don't expect any error here
console.log(e);
});
});

// Test for the isContractOwner method - false
it("IsContractOwner should return false", () => {
return Ownable.deployed()
.then(function(instance) {
OwnableContract = instance;
// Call isContractOwner for the non-owner address
  return (OwnableContract.isContractOwner.call(Account1));
  }).then(function(resultIsOwner) {
// Check that the result is true
  assert.equal(resultIsOwner.valueOf(), false, "Expected the result to be true");
  return true;
}).catch(function(e) {
// We don't expect any error here
console.log(e);
});
});

});

Now, we need to deploy our contract to the chain where we are going to perform the testing. For this, Truffle needs to know what contracts have to be deployed. Create a subdirectory migrations/ in myproject/and place 2 files in there:

1_initial_migration.js
2_deploy_ownable.js

Content of the 1_initial_migration.js

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

Content of the 2_deploy_ownable.js

// Define contracts to be deployed
var Ownable = artifacts.require("Ownable");

// Deploy contracts
module.exports = function(deployer) {
  deployer.deploy(Ownable);
};

These files will be picked up by truffle when you run the migrate command and executed in the order how they are prefixed: 1_, 2_…

Now we need to add a Migrations smart contract for truffle, that is referenced in the 1_initial_migration.js. The Migrations contract keeps track of what migrations (as listed in the migrations/ directory) were done on the current network.

Go to contracts/, create and store a Migrations.sol file there:


pragma solidity ^0.5.0;

contract Migrations {

  address public owner;
  uint public last_completed_migration;

  modifier restricted() {
    if (msg.sender == owner) _;
  }

  constructor() public {
    owner = msg.sender;
  }

  function setCompleted(uint completed) public restricted {
    last_completed_migration = completed;
  }

  function upgrade(address new_address) public restricted {
    Migrations upgraded = Migrations(new_address);
    upgraded.setCompleted(last_completed_migration);
  }
}

At the end you should have the following structure in your main directory myproject/:

myproject
├── contracts
│   ├── Migrations.sol
│   └── Ownable.sol
├── migrations
│   ├── 1_initial_migration.js
│   └── 2_deploy_ownable.js
├── test
│   └── OwnableTest.js
└── truffle-config.js

To run the tests with the built-in blockchain, do the following in your main directory myproject/:

  • Start truffle built in development blockchain:
$ truffle develop

This will start a test environment and create a couple of accounts for you which you can easily reference in your tests (see the test example above).

  • Compile your contracts
truffle(develop)> compile
  • Run migrations if your contracts have changed: migrate
truffle(develop)> migrate
  • Run the tests:
truffle(develop)> test

You should see the following output:

truffle(develop)> test
Using network 'develop'.

Compiling your contracts...
===========================
> Everything is up to date, there is nothing to compile.

  Contract: Ownable
    ✓ IsContractOwner should return true (54ms)
    ✓ IsContractOwner should return false (50ms)

  2 passing (137ms)

Hooray!

4. Deploy to Testnet (Rinkeby)

Now you have written and tested your smart contract locally and you are ready to deploy it on the testnet. For this, I will be using Rinkeby, but you are free to choose any other testnet.

First of all you will need to create an Ethereum account on rinkeby and get some test coins to be able to deploy your smart contract to the testnet.

We are going to use MetaMask to create and manage your accounts, and Remix to deploy and test the contract.

Use MetaMask to create an account. You will be provided with an Ethereum address and you will also be allowed to switch between different networks (test networks or mainnet). To get some test ETH go to the rinkeby faucet and follow the instructions.

The following instructions only apply to the old Remix interface.

Compile your contract in Remix. Add your file with the Solidity code (Ownable.sol). Go to the Compile tab of Remix, select the appropriate compiler version (in your case it will be 0.5.0). Push Start to compile button.

Now you are ready to deploy the contract. Select Rinkeby as the network in MetaMask, go to Remix Run tab, and choose Injected Web3J as environment option. Remix will pick up your MetaMask account automatically and show your balance in ETH. Now you can choose your compiled contract and deploy it to the Rinkeby testnet. You will need to confirm the deployment transaction in MetaMask.

You will see a similar output in the remix console:

creation of Ownable pending...
https://rinkeby.etherscan.io/tx/0x936c531caaa06a97c4416f4daa91d44287d1c30f6316c7807eff33d28a9a3832

Once the transaction was successfully executed and recorded on the blockchain, you will get something like:

[block:4622611 txIndex:3] from:0x4cc...ed751 to:Ownable.(constructor) value:0 wei data:0x608...e0029 logs:0 hash:0x936...a3832

A click on

block:4622611 txIndex:3

will provide detailed information about the transaction.

Your contract can now be seen under Deployed contracts and can be tested by calling getContractOwner and isContractOwner.

Voilà!

In Part II of this smart contract tutorial, I will show how to use a deployed contract from a java application.

Natallia Martchouk

Co-Founder trimplement GmbH. Interested in: new technologies, creativity, entrepreneurship, art, photography.

Leave a Reply

Your email address will not be published. Required fields are marked *