Coding Smart Contracts — Tutorial Part I

How to Write, Deploy and Test a Smart Contract

  • 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 truffletruffle versionbrew update
brew upgrade
brew tap Ethereum/Ethereum
brew install solidity
brew tap web3j/web3j
brew install web3j
npm install -g solhint
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;
}
}
$ cd myproject/contracts/
$ vi .solhint.json
{
"extends": "solhint:default"
}
$ solhint Ownable.sol
$ solhint Ownable.sol Ownable.sol
37:7 error Expected indentation of 8 spaces but found 6 indent
✖ 1 problem (1 error, 0 warnings)

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
};
// 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);
});
});
});
1_initial_migration.js
2_deploy_ownable.js
var Migrations = artifacts.require("./Migrations.sol");
module.exports = function(deployer) {
deployer.deploy(Migrations);
};
// Define contracts to be deployed
var Ownable = artifacts.require("Ownable");
// Deploy contracts
module.exports = function(deployer) {
deployer.deploy(Ownable);
};
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);
}
}
myproject
├── contracts
│ ├── Migrations.sol
│ └── Ownable.sol
├── migrations
│ ├── 1_initial_migration.js
│ └── 2_deploy_ownable.js
├── test
│ └── OwnableTest.js
└── truffle-config.js
$ truffle develop
truffle(develop)> compiletruffle(develop)> migratetruffle(develop)> test
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)

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 , but you are free to choose any other testnet.

creation of Ownable pending...
https://rinkeby.etherscan.io/tx/0x936c531caaa06a97c4416f4daa91d44287d1c30f6316c7807eff33d28a9a3832
[block:4622611 txIndex:3] from:0x4cc...ed751 to:Ownable.(constructor) value:0 wei data:0x608...e0029 logs:0 hash:0x936...a3832
block:4622611 txIndex:3

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
The trimplement Team

We are the fintech enabler. Explore our scalable and secure emoney, cryptocurrency and stored value solutions and follow our journey: https://trimplement.com/