Developing Ethereum smart contracts with the Truffle Suite including Web3

Michael Whittle
Sep 24 · 10 min read

In this tutorial, I installed VirtualBox on my Mac, then went to the Ubuntu site to download the latest Ubuntu Desktop. I then spun up a virtual machine in bridged network mode so it has its own un-NATed address.

You can also do this with AWS EC2 as it is quick and easy to spin up an instance. Whichever way you decide to install Ubuntu just make sure the following ports are open UDP 30301, TCP 30303, UDP 30303, and TCP 8545.

Please note that if you use an Amazon EC2 Ubuntu instance the free tier t2.micro will cause you problems. You will find Geth will never mine a single block. You should use at least a t2.large for this tutorial for it to work properly.

The steps to install Geth on Ubuntu as root…

# sudo apt-get install -y software-properties-common 
# sudo add-apt-repository -y ppa:ethereum/ethereum
# sudo apt-get update
# sudo apt-get install -y ethereum

If everything goes to plan you should be able to confirm Geth is installed like this.

# geth version
Geth
Version: 1.9.21-stable
Git Commit: 0287d54847d3297f3ced62cd83a4c95ccbe0045b
Architecture: amd64
Protocol Versions: [65 64 63]
Go Version: go1.15
Operating System: linux
GOPATH=
GOROOT=go

You will also want to install GIT and the Node Package Manager (npm).

# apt-get install git npm -y

The next step is your want to install truffle globally using npm.

# npm install truffle --global# truffle version
Truffle v5.1.45 (core: 5.1.45)
Solidity v0.5.16 (solc-js)
Node v10.19.0
Web3.js v1.2.1

We are going to “unbox” one of the example Truffle suite projects.

# mkdir metacoin
# cd metacoin
~/metacoin# truffle unbox metacoinStarting unbox...
=================
✔ Preparing to download box
✔ Downloading
✔ Cleaning up temporary files
✔ Setting up box
Unbox successful, sweet!Commands:Compile contracts: truffle compile
Migrate contracts: truffle migrate
Test contracts: truffle test

In order to show you the tree structure of what is created I installed “tree”.

# apt-get install tree -y~/metacoin# tree
.
├── build
│ └── contracts
│ ├── ConvertLib.json
│ ├── MetaCoin.json
│ └── Migrations.json
├── contracts
│ ├── ConvertLib.sol
│ ├── MetaCoin.sol
│ └── Migrations.sol
├── LICENSE
├── migrations
│ ├── 1_initial_migration.js
│ └── 2_deploy_contracts.js
├── test
│ ├── metacoin.js
│ └── TestMetaCoin.sol
└── truffle-config.js
5 directories, 12 files

Let’s try and compile the Metacoin contract…

~/metacoin# truffle compileCompiling your contracts...
===========================
> Compiling ./contracts/ConvertLib.sol
> Compiling ./contracts/MetaCoin.sol
> Compiling ./contracts/Migrations.sol
> Artifacts written to /root/metacoin/build/contracts
> Compiled successfully using:
- solc: 0.5.16+commit.9c3226ce.Emscripten.clang

That all looks good. Now let’s try run the unit tests from ./test

Just as a note, unit tests in Truffle can use Solidity or Mocha/Chai.

~/metacoin# truffle test
Using network 'test'.
Compiling your contracts...===========================> Compiling ./test/TestMetaCoin.sol
> Artifacts written to /tmp/test--46542-HF0pmqE0JWA1
> Compiled successfully using:
- solc: 0.5.16+commit.9c3226ce.Emscripten.clang
TestMetaCoin
✓ testInitialBalanceUsingDeployedContract (91ms)
✓ testInitialBalanceWithNewMetaCoin (61ms)
Contract: MetaCoin
✓ should put 10000 MetaCoin in the first account (64ms)
✓ should call a function that depends on a linked library (93ms)
✓ should send coin correctly (163ms)
5 passing (8s)

That all looks good as well.

In order to deploy your Metacoin contract using Truffle you first need to fire up your Geth node. If you followed by other article “https://medium.com/coinmonks/creating-and-exploring-a-private-ethereum-blockchain-using-geth-d71afc5cdcf9”, you can run the ./2020-start-alone.sh script to start your private Ethereum network with ID 2020.

The default ./truffle-config.js file looks like this…

module.exports = {
// Uncommenting the defaults below
// provides for an easier quick-start with Ganache.
// You can also follow this format for other networks;
// see <http://truffleframework.com/docs/advanced/configuration>
// for more details on how to specify configuration options!
//
// networks: {
// development: {
// host: "127.0.0.1",
// port: 7545,
// network_id: "*"
// },
// test: {
// host: "127.0.0.1",
// port: 7545,
// network_id: "*"
// }
// }
//
};

Based on my other article we will want to deploy our contract to Network ID 2020 on RPC port TCP 8545.

module.exports = {  
networks: {
development: {
host: 'localhost',
port: 8545,
network_id: '*' // Match any network id
},
'net2020': {
host: 'localhost',
port: 8545,
network_id: 2020
},

'ropsten': {
host: 'localhost',
port: 8545,
network_id: 3
}
}
}

Now let’s try migrate (deploy) the Metacoin contract.

Although technically not a requirement, I always clear the ./build directory first. You can sometimes experience some weird behavior if you don’t.

# rm -rf ./build.

The steps to deploy/migrate to the Blockchain “network 2020” is as follows…

# truffle migrate net2020Compiling your contracts...
===========================
> Everything is up to date, there is nothing to compile.
Starting migrations...
======================
> Network name: 'development'
> Network id: 2020
> Block gas limit: 8000000 (0x7a1200)
1_initial_migration.js
======================
Deploying 'Migrations'
----------------------
Error: *** Deployment Failed ***"Migrations" -- Returned error: authentication needed: password or unlock.at /usr/local/lib/node_modules/truffle/build/webpack:/packages/deployer/src/deployment.js:364:1
at process._tickCallback (internal/process/next_tick.js:68:7)
Truffle v5.1.45 (core: 5.1.45)
Node v10.19.0

It looks like it mostly worked but we need to unlock our account first.

> personal.unlockAccount(eth.accounts[0])
Unlock account 0x6be8a6cad397746efc5033e27e82477e27c9afec
Passphrase:
true

And we try again…

# rm -rf ./build# truffle migrate net2020Compiling your contracts...
===========================
> Compiling ./contracts/ConvertLib.sol
> Compiling ./contracts/MetaCoin.sol
> Compiling ./contracts/Migrations.sol
> Artifacts written to /root/metacoin/build/contracts
> Compiled successfully using:
- solc: 0.5.16+commit.9c3226ce.Emscripten.clang
Starting migrations...
======================
> Network name: 'development'
> Network id: 2020
> Block gas limit: 8000000 (0x7a1200)
1_initial_migration.js
======================
Deploying 'Migrations'
----------------------
> transaction hash: 0xeeeabfd52398d198917191a1c985ec435b0e841f56cbca1ed0ee2b50fd0634eb> Blocks: 0 Seconds: 0
> contract address: 0x3A01FDf6E168347AcA1A87eAae0cAEF83beaC74A
> block number: 9451
> block timestamp: 1600959637
> account: 0x6BE8A6cAD397746EfC5033e27e82477E27c9aFec
> balance: 47255
> gas used: 188483 (0x2e043)
> gas price: 20 gwei
> value sent: 0 ETH
> total cost: 0.00376966 ETH
> Saving migration to chain.
> Saving artifacts
-------------------------------------
> Total cost: 0.00376966 ETH
2_deploy_contracts.js
=====================
Deploying 'ConvertLib'
----------------------
> transaction hash: 0x12f7a0024e7f35ec9565acf48187ba9889c45aef98e28032f97906f3c6c87168> Blocks: 2 Seconds: 8
> contract address: 0x4492b3E4Ff5fAB49807753AaA8A51Fb8cD0a595f
> block number: 9440
> block timestamp: 1600959431
> account: 0x6BE8A6cAD397746EfC5033e27e82477E27c9aFec
> balance: 47205
> gas used: 105974 (0x19df6)
> gas price: 20 gwei
> value sent: 0 ETH
> total cost: 0.00211948 ETH
Linking
-------
* Contract: MetaCoin <--> Library: ConvertLib (at address: 0x4492b3E4Ff5fAB49807753AaA8A51Fb8cD0a595f)Deploying 'MetaCoin'
--------------------
> transaction hash: 0xb6fb5de6e5e6b6b48d76fed9cb7c44e0530826504819bd8469a93e50414f49a4> Blocks: 0 Seconds: 4
> contract address: 0xC2EDEBAE99938775301394E95188599bE84aB695
> block number: 9442
> block timestamp: 1600959438
> account: 0x6BE8A6cAD397746EfC5033e27e82477E27c9aFec
> balance: 47210
> gas used: 340697 (0x532d9)
> gas price: 20 gwei
> value sent: 0 ETH
> total cost: 0.00681394 ETH
> Saving migration to chain.
> Saving artifacts
-------------------------------------
> Total cost: 0.00893342 ETH
Summary
=======
> Total deployments: 3
> Final cost: 0.01270308 ETH

And that is it… Metacoin compiled, tested, and deployed using Truffle Suite.

I also recommend you take a look at “truffle unbox react”. That provides a nice example of how to use the web3 library which works in React.js, Node.js, and Angular.js.

# mkdir react
# cd react
# truffle unbox reactThis directory is non-empty...? Proceed anyway? (Y/n)
Starting unbox...
=================
? Proceed anyway? Yes
✔ Preparing to download box
✔ Downloading
.gitattributes already exists in this directory...
? Overwrite .gitattributes? Yes
*** INSTALLATION PROCESS ***✔ Cleaning up temporary files
✔ Setting up box
Unbox successful, sweet!Commands:Compile: truffle compile
Migrate: truffle migrate
Test contracts: truffle test
Test dapp: cd client && npm test
Run dev server: cd client && npm run start
Build for production: cd client && npm run build

Then the same steps as before. Log into your Geth node and unlock the eth.account[0] account. Then you can do the following…

# truffle test# truffle compile# rm -rf ./build
# truffle migrate net2020

Now you probably noticed this at the end of the unboxing.

Test dapp:            cd client && npm test
Run dev server: cd client && npm run start
Build for production: cd client && npm run build

That is for the React part of this.

client# npm testPASS  src/App.test.js
✓ renders without crashing (25ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 3.079s
Ran all test suites.
Watch Usage
› Press f to run only failed tests.
› Press o to only run tests related to changed files.
› Press q to quit watch mode.
› Press p to filter by a filename regex pattern.
› Press t to filter by a test name regex pattern.
› Press Enter to trigger a test run.

npm run start” will fire up your React.js site.

Compiled successfully!You can now view client in the browser.Local:            http://localhost:3000/
On Your Network: http://192.168.1.2:3000/
Note that the development build is not optimized.
To create a production build, use yarn build.

And you can build this…

# npm run build> client@0.1.0 build /root/react/client
> react-scripts build
Creating an optimized production build...
Browserslist: caniuse-lite is outdated. Please run next command `yarn upgrade`
Compiled successfully.File sizes after gzip:343.55 KB build/static/js/2.c7ba9215.chunk.js
3.56 KB build/static/js/main.aaf0ca31.chunk.js
772 B build/static/js/runtime-main.b240f91f.js
417 B build/static/css/main.b100e6da.chunk.css
The project was built assuming it is hosted at the server root.
You can control this with the homepage field in your package.json.
For example, add this to build it for GitHub Pages:
"homepage" : "http://myname.github.io/myapp",The build folder is ready to be deployed.
You may serve it with a static server:
yarn global add serve
serve -s build
Find out more about deployment here:https://bit.ly/CRA-deploy

And “serve” it like this…

# npm install serve --global
/usr/local/bin/serve -> /usr/local/lib/node_modules/serve/bin/serve.js
+ serve@11.3.2
added 78 packages from 39 contributors in 4.46s
serve -s build

The web3 library and React code will try and connect to your Blockchain using the Metamask browser plugin. This is the preferred secure way to connect web based applications to Blockchains. Once installed you can open Metamask, click on the drop-down menu at the top and select “Custom RPC”. Give it a “Network Name” and “New RPC URL” of http://SERVER_IP:8545. If you are trying to connect from the same host as the Geth node you can select “Localhost 8545” from the menu instead.

So the last part I wanted to show you is how to deploy Owner.sol from my other article using Truffle with some useful Mocha/Chai tests to start you off.

Create a new directory called “mediumtutorial”, go into the directory, and run “truffle init”.

# mkdir mediumtutorial# cd mediumtutorial# mediumtutorial# truffle initStarting init...
================
> Copying project files to /root/mediumtutorialInit successful, sweet!

Create ./contracts/Owner.sol

pragma solidity >=0.4.22 <0.7.0;contract Owner {
address private owner;

event OwnerSet(address indexed oldOwner, address indexed newOwner);

modifier isOwner() {
require(msg.sender == owner, "Caller is not owner");
_;
}

constructor() public {
owner = msg.sender;
emit OwnerSet(address(0), owner);
}
function changeOwner(address newOwner) public isOwner {
emit OwnerSet(owner, newOwner);
owner = newOwner;
}
function getOwner() external view returns (address) {
return owner;
}
}

Replace ./truffle-config.js with this.

module.exports = {  
networks: {
'net2020': {
host: 'localhost',
port: 8545,
network_id: 2020
}

}
}

Create ./migrations/2_deploy_contracts.js

var Owner = artifacts.require("./Owner.sol");let owner;
let ownerOwner;
module.exports = function(deployer, network, accounts) {
deployer.then(function() {
ownerOwner = accounts[0];
console.log('Owner owner:', ownerOwner);
return Owner.new({ from: ownerOwner });
}).then(instance => {
owner = instance;
console.log('Owner address:', owner.address);
});
};

Let’s try compiling it…

# truffle compileCompiling your contracts...
===========================
> Compiling ./contracts/Migrations.sol
> Compiling ./contracts/Owner.sol
> Artifacts written to /root/mediumtutorial/build/contracts
> Compiled successfully using:
- solc: 0.5.16+commit.9c3226ce.Emscripten.clang

All good so far, let’s create a basic test: ./tests/owner.js

var Owner = artifacts.require("./Owner.sol");contract('Contract', function(accounts) {
var myContract;
const owner = accounts[0];beforeEach( function() {
return Owner.new( { from : owner } )
.then( function( instance ) {
myContract = instance;
});
});
it("should be owned by the owner", function() {
return myContract.getOwner( { from:owner } )
.then( receivedValue => {
assert.strictEqual( receivedValue, owner, "Contract is not owned by owner");
});
});
});

And the test runs perfectly :)

# rm -rf ./build; truffle testCompiling your contracts...
===========================
> Compiling ./contracts/Migrations.sol
> Compiling ./contracts/Owner.sol
> Artifacts written to /tmp/test--55143-VRCwuiLfjzo1
> Compiled successfully using:
- solc: 0.5.16+commit.9c3226ce.Emscripten.clang
Owner owner: 0x627306090abaB3A6e1400e9345bC60c78a8BEf57
Owner address: 0x345cA3e014Aaf5dcA488057592ee47305D9B3e10
Contract: Contract
✓ should be owned by the owner
1 passing (211ms)

You may or may not have had luck getting the “truffle unbox react” to work. I’m going to provide you with a very minimal “create-react-app” to demonstrate how to interface with a smart contract from React.

# npm install create-react-app --global# create-react-app mediumtutorial*** INSTALLATION PROCESS ***# cd mediumtutorial
# yarn add web3 @truffle/contract
*** INSTALLATION PROCESS ***# yarn start

Copy your Owner.json into your React ./src directory and replace the App.js with this.

import React, { Component } from 'react';
import Web3 from 'web3';
import OwnerContract from './Owner.json';
import './App.css';
let getWeb3 = new Promise(function(resolve, reject) {
window.addEventListener('load', function() {
let results;
let web3 = window.web3;
if (typeof web3 !== 'undefined') {
web3 = new Web3(web3.currentProvider);
results = { web3: web3 };
console.log('Injected web3 detected.');
resolve(results);
} else {
const provider = new
Web3.providers.HttpProvider('http://127.0.0.1:8545');
web3 = new Web3(provider);
results = { web3: web3 };
console.log('No web3 instance injected, using Local web3.');
resolve(results);
}
})
})
class App extends Component {
state = {
web3: null
}
componentWillMount() {
getWeb3
.then(results => {
const { web3 } = results;
this.setState({ web3 });

web3.eth.getAccounts((error, accounts) => {
console.log('Metamask accounts: ', accounts);
});
const contract = require('@truffle/contract');
const contractOwner = contract(OwnerContract);
contractOwner.setProvider(web3.currentProvider);
let ownerInstance;this.state.web3.eth.getAccounts((error, accounts) => {
contractOwner.deployed().then((instance) => {
ownerInstance = instance;
console.log(ownerInstance);
});
});
})
.catch(() => {
console.log('Error finding web3.')
});
}
render() {
return (
<div className="App">
<header className="App-header">
web3 Demo
</header>
</div>
);
}
}
export default App;

This is just an example but you should be able to see this working in the Browser console.

If you enjoyed reading this article and would like me to write on any other topics please let me know in the comments or email me directly.

I’m the Head of the Networks Practice at Net Reply. My team specialises in networks, security, and process automation including self-service dashboards. If you would like more information on this please contact me on m.whittle@reply.com. Alternatively, you can learn more about us on LinkedIn and Twitter.

Also Read: The Best Ethereum Hardrawe wallets

Get Best Software Deals Directly In Your Inbox

Image for post

Coinmonks

Coinmonks is a non-profit Crypto educational publication.

Sign up for Coinmonks

By Coinmonks

A newsletter that brings you week's best crypto and blockchain stories and trending news directly in your inbox, by CoinCodeCap.com Take a look

By signing up, you will create a Medium account if you don’t already have one. Review our Privacy Policy for more information about our privacy practices.

Check your inbox
Medium sent you an email at to complete your subscription.

Michael Whittle

Written by

CCIE R&S #24223 | Network Architect | Security Specialist | Software Developer | Blockchain Developer | Studying Machine Learning & AI

Coinmonks

Coinmonks

Coinmonks is a non-profit Crypto educational publication. Follow us on Twitter @coinmonks Our other project — https://coincodecap.com

Michael Whittle

Written by

CCIE R&S #24223 | Network Architect | Security Specialist | Software Developer | Blockchain Developer | Studying Machine Learning & AI

Coinmonks

Coinmonks

Coinmonks is a non-profit Crypto educational publication. Follow us on Twitter @coinmonks Our other project — https://coincodecap.com

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

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