Download as pdf or txt
Download as pdf or txt
You are on page 1of 21

Prerequisites

Welcome
In order to continue to the next lecture, you need to have the following software installed on your computer. 

• NPM (Node Package Manager). Can be downloaded from here. 


• Metamask (https://metamask.io/)

You should also have taken Ethereum Smart Contract Programming 101 before you take this course. 

Setup Environment
Truffle Introduction
Install Truffle: npm install -g truffle@5.0.42

Download Ganache: https://github.com/trufflesuite/ganache/releases/tag/v2.1.1

Forum Discussion: https://forum.ivanontech.com/t/truffle-introduction/10049

Hello World
Truffle Introduction
The complete code can be found by clicking here.

Forum Discussion: https://forum.ivanontech.com/t/truffle-introduction/10049

mkdir Helloworld;
cd Helloworld;
truffle init

Edit contracts/Helloworld.sol and insert:

// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.5.12;

contract Helloworld {
   string message = "Hello World";

   function getMessage() public view returns(string memory) {


      return(message);
   }
}

Deploying our First Contract


Truffle Introduction
The complete code can be found by clicking here.

Forum Discussion: https://forum.ivanontech.com/t/truffle-introduction/10049

Edit 2_Helloworld_deploy.js like this:

// Helloworld in require is the name of the contract


const Helloworld = artifacts.require("Helloworld");

module.exports = function(deployer) {
   deployer.deploy(Helloworld);
};

truffle compile --list --all # To have a list of all installed compiler versions

truffle compile

In ganache in Contracts tab add truffle-config.js so that ganache can monitor the contracts deployed

Deploy the contracts:

truffle migrate

See the results in Ganache


Now you can interact with the deployd contract with truffle console:

truffle console

let instance = await Helloworld.deployed()


instance.getMessage()

Setter Function
Truffle Introduction
The complete code can be found by clicking here.

Forum Discussion: https://forum.ivanontech.com/t/truffle-introduction/10049

In truffle console:
migrate 

This will compile and upgrade the contract on ganache but do add --reset to re-deploy because sometimes
truffle doesn't "see" all the changes (for example when you change the migrate files)

migrate --reset 

With truffle you can modify the Helloworld_deploy.js to add interaction with the contract (like set some init
state, transfer eth):

// Helloworld in require is the name of the contract


const Helloworld = artifacts.require("Helloworld");

module.exports = function(deployer) {
   deployer.deploy(Helloworld).then( instance => {
      instance.setMessage("Mars!").then( receipt => {
         //console.log("receipt = " + receipt);
         instance.getMessage().then( message => {
            console.log("message = " + message);
         })
      });
   });
};

Error Handling
Truffle Introduction
The complete code can be found by clicking here.

Forum Discussion: https://forum.ivanontech.com/t/truffle-introduction/10049

// Helloworld in require is the name of the contract


const Helloworld = artifacts.require("Helloworld");

module.exports = function(deployer, network, accounts) {


   deployer.deploy(Helloworld).then( instance => {
      instance.setMessage("Mars!", {value: 10000000000, from: accounts[1]}).then( receipt => {
         //console.log("receipt = " + receipt);
         console.log("Success!");
         instance.getMessage().then( message => {
            console.log("message = " + message);
         })
      }).catch( err => {
         console.log("Error: " + err);
      });
   }).catch(err => {
      console.log("Deploy Error: " + err);
   });
};

Introduction to Unit Testing

Divide your code in unit and run test for each unit

Yes - I commented out the following line (line #868) in the MOCHA library file runner.js ::

//process.on(‘uncaughtException’, uncaught);

Thanks …

/usr/lib/node_modules/truffle/node_modules/mocha/lib/runner.js

In test directory write a file called Helloworld_test.js:

const Helloworld = artifacts.require("Helloworld");

contract("Helloworld", async function() {


   it("should initialize correctly", async function() {
      let instance = await Helloworld.deployed();
      let message = await instance.getMessage();
      assert(message === "Hi there!", "message is '" + message + "' but it should be 'Hi there!'");
   })
  

   it("should set the message correctly", async function() {


      let instance = await Helloworld.deployed();
      await instance.setMessage("Working?");
      let message = await instance.getMessage();
      assert(message === "Working?", "message is '" + message + "' but it should be 'Working?'");
   });

})
Importing the people contract
Unit Testing
Link to the contract: https://github.com/filipmartinsson/solidity-0.5.12-course/blob/master/Inheritance/
HelloWorld.sol

Forum Discussion: https://forum.ivanontech.com/t/unit-testing-in-truffle/10053

Testing for Errors


Unit Testing
The final code is available by clicking here.

Forum Discussion: https://forum.ivanontech.com/t/unit-testing-in-truffle/10053

For verify require and asserts you need and additional truffle module to install:

npm init
npm install truffle-assertions

Write a test unit:

const People = artifacts.require("People");


const truffleAssert = require("truffle-assertions");

contract("People", async function() {


  it("shouldn't create a person with age over 150 years", async function() {
    let instance = await People.deployed();
    // truffleAssert.ErrorType.REVERT is the type of error to check. The .fails means the test is
successful if createPerson function calls fails (because age 200 is > 150)
    await truffleAssert.fails(instance.createPerson("Bob", 200, 190, {value: web3.utils.toWei("1",
"ether")}), truffleAssert.ErrorType.REVERT);
  });
  

it("should set senior status correctly", async function() {


   let instance = await People.deployed();
   await instance.createPerson("Bob", 20, 190, {value: web3.utils.toWei("1", "ether")});
   let person = await instance.getPerson();
   assert(person.senior === false, "age= 20 so senior is false but is: ", person.senior);
   await instance.createPerson("Bob", 65, 190, {value: web3.utils.toWei("1", "ether")});
   person = await instance.getPerson();
   assert(person.senior === true, "age=65 so senior is true but is: " + person.senior);
})   
});

truffleAssert with message: 

await truffleAssert.fails(instance.deletePerson(hacker, {from: hacker}),


truffleAssert.ErrorType.REVERT, null, "User can't delete person");

Other test unit with less code:

const People = artifacts.require("People");


const truffleAssert = require("truffle-assertions");
const AssertionError = require("assertion-error");

contract("People", accounts => {

let instance;

before(async function(){
   instance = await People.deployed()
});

const owner = accounts[0];


const hacker = accounts[1];

it("User can't delete a person", async () => {

   await instance.createPerson("Bob", 20, 190, {value: web3.utils.toWei("1", "ether"), from:


hacker});

   await truffleAssert.fails(instance.deletePerson(hacker, {from: hacker}),


truffleAssert.ErrorType.REVERT, null, "User can't delete person");

});

it("Owner can delete a person", async () => {

   await instance.createPerson("Bob", 20, 190, {value: web3.utils.toWei("1", "ether"), from:


hacker});
   await truffleAssert.passes(instance.deletePerson(hacker, {from: owner}), null, "Owner was not
able to delete person!");

});

});

Truffle Instances
Unit Testing
The final code is available by clicking here.

Forum Discussion: https://forum.ivanontech.com/t/unit-testing-in-truffle/10053

You can use in truffle istances the

before that executes at the begin of the unit test

before(async function(){
   instance = await People.deployed()
});

and 

beforeEach that executes before each it()

We have also after and afterEach that executes at the end of the unit and at the end of each it() respectively

You can add a new istance of the contract doing: 

let instance = await People.new();

Value Assignment
Unit Testing
The final code is available by clicking here.
Forum Discussion: https://forum.ivanontech.com/t/unit-testing-in-truffle/10053

// Get contract address


const contractAddress = instance.address; 

// Get contract balance


web3.eth.getBalance(address);

// Get wei from ether


web3.utils.toWei("0", "ether");

Template Walkthrough
Dapp Intro
Forum Discussion: https://forum.ivanontech.com/t/dapp-introduction/10054

Project Setup
Dapp Intro
The final code is available by clicking here.

Forum Discussion: https://forum.ivanontech.com/t/dapp-introduction/10054

Use python to launch a simple http server

python -m http.server 

Connect Metamask to ganache. Import an address from ganache to Metamask using the address private key

Creating a Contract Instance


Dapp Intro
The final code is available by clicking here.
Ethereum JavaScript Libraries: web3.js vs.
ethers.js
https://blog.infura.io/ethereum-javascript-libraries-web3-js-vs-ethers-js-part-i/

https://web3js.readthedocs.io/en/v1.2.11/

https://web3js.readthedocs.io/en/v1.2.7/web3-utils.html

var web3 = new Web3(Web3.givenProvider);


var contractInstance;

$(document).ready(function() {
   console.log("In ready function!");
   window.ethereum.enable().then( accounts => {
      //contractInstance = new web3.eth.Contract(abi, address, addressinjson)
      contractInstance = new web3.eth.Contract(abi,
"0x581101d0622E110CB89f86364C00EE0277f48604", {from: accounts[0]});
      console.log("contractInstance=", contractInstance);
   })
})

Setting Data
Dapp Intro
The final code is available by clicking here.

$("#add_data_button").click(inputData)

function inputData() {

let name = $("#name_input").val()

let age = $("#age_input").val()

let height = $("#height_input").val()


let config = {

value: web3.utils.toWei("1", "ether")

contractInstance.methods.createPerson(name, age, height).send(config)

.on("transactionHash", hash => {

console.log("Hash=", hash)

})

.on("confirmation", confirmationNr => {

// 12 confirmation is the raccomanded confirmation number

//if(confirmationNr > 12)

console.log("confirmationNr=", confirmationNr)

})

.on("receipt", receipt => {

console.log("Receipt=", receipt)

alert("Dones")

})

Getting Data & Displaying It


Dapp Intro
The final code is available by clicking here.

function showData() {

contractInstance.methods.getPerson().call().then( res => {


console.log("res=", res)

$("#name_output").text(res.name)

$("#age_output").text(res.age)

$("#height_output").text(res.height)

})

Project Introduction
Programming Project - Phase 1
Forum Discussion: https://forum.ivanontech.com/t/programming-project-phase-1/10055

Flip Project: You bet some ether and you can loose all or double the stake.
You are going to use:
- HTML/JS
- Web3.js 
For the interface (web page)
and:
- Smart contract
- Truffle

Project Description
Programming Project - Phase 1

Pseudo Randomness
Programming Project - Phase 1
function random() private returns(uint) {
   return now % 2;
}
Hand In
Programming Project - Phase 1
When you are done with your code, please take some screenshots or screen recordings of you dapp in action
and post it here. 

For this first phase of the project, you don't have to post your code. 
solidity contract

// SPDX-License-Identifier: MIT

import "./Ownable.sol";

pragma solidity 0.5.12;

contract Flip is Ownable {

event betResult(address player, uint value);

modifier costs(uint cost){

require(msg.value >= cost);

_;

// Pseudo random function

// Returns 0 or 1

function random() public view returns(uint val) {

return block.timestamp % 2;

function bet(uint userBet) public payable costs(0.01 ether) {


uint balance = address(this).balance;

require(msg.value <= balance / 2, "Bet stake is too high");

uint win = 0;

if (random() == userBet) {

//if (1 == 1) {

win = msg.value * 2;

msg.sender.transfer(win);

emit betResult(msg.sender, win);

function fund() public payable {

function withdrawAll() public onlyOwner {

//address payable payAddress = address(owner);

msg.sender.transfer(address(this).balance);

//fallback() external payable {}

//fallback() public payable {}

main.js

var web3 = new Web3(Web3.givenProvider)


var contractInstance

// Minimum bet is 0.01 ether

const minimumBet = 0.01

$(document).ready(function() {

console.log("In ready function!")

const contractAddress = "0xa4490a0F3859F9199C73F863F222C2094E3A15f1"

window.ethereum.enable().then( accounts => {

//contractInstance = new web3.eth.Contract(abi, address, addressinjson)

contractInstance = new web3.eth.Contract(abi, contractAddress, {from: accounts[0]})

console.log("contractInstance=", contractInstance)

})

// update contract balance view

updateBalance()

// make the update happens in background

backgroundBalance()

function backgroundBalance() {

window.setTimeout(function() {

updateBalance()

backgroundBalance()

}, 5000);

}
// hook bet-btn to bet function

$("#bet-btn").click(bet)

// bet function

// handle the bet

function bet() {

let betAmount = $("#bet").val()

if(betAmount < minimumBet) {

myAlert("Your bet has to be at least 0.01 ether", "alert-danger")

return false

web3.eth.getBalance(contractAddress).then( balance => {

let betAmountWei = web3.utils.toWei(betAmount, "ether")

if(betAmountWei > balance/2) {

betAmount = web3.utils.fromWei(parseInt(balance/2).toString())

$("#bet").val(betAmount)

myAlert(`Your bet has been lowered to ${betAmount} ether`, "alert-danger")

return false

//alert(betAmount)

let config = {

value: web3.utils.toWei(betAmount, "ether")

const coinSide = $("#betForm input[name='inlineCoinRadioOptions']:checked").val()

console.log("radio= ", coinSide)

contractInstance.methods.bet(coinSide).send(config).then( res => {


console.log("res=", res)

// Don't know why but res.events did not give event betResult by name but only [0]

// This was when res.events[0].raw.data =


"0x000000000000000000000000f7572e1f98b1fb1d788e3144314b4c9a0e6d09e80000000000000
0000000000000000000000000000000000000470de4df820000"

// and win = 20000000000000000

//const win = parseInt(res.events[0].raw.data.substr(2+64), 16)

const win = res.events.betResult.returnValues.value

console.log("win=", win)

if (win > 0) {

myAlert("CONGRATULATION YOU WON " + web3.utils.fromWei(win.toString()) + " ether",


undefined, 5000)

} else {

myAlert(`SORRY YOU LOST ${betAmount} ether`, "alert-danger", 5000)

updateBalance()

})

/*

contractInstance.methods.bet(coinSide).send(config)

.on("receipt", receipt => {

console.log("Receipt=", receipt.events[0].raw.data)

const win = parseInt(res.events[0].raw.datasubstr(2+64), 16)

})

.on("error", error => {

console.log("Error=", error)

})
*/

// Dani

//let res = await contractInstance.methods.bet(coinSide).send(config)

//console.log("Dani res=", res)

// Dani2

//contractInstance.methods.bet(coinSide).send(config).then(async function(res){

// console.log("Dani2 res=", res);

//})

// Dani3

//contractInstance.bet.call(coinSide,{value:betAmountWei}).then( res => {

// console.log("Dani3 res=", res)

//})

})

function updateBalance() {

web3.eth.getBalance(contractAddress).then( balance => {

$("#pot").text(balance/10**18 + " ether")

console.log("Contract balance=",balance)

})

/*

function inputData() {
let name = $("#name_input").val()

let age = $("#age_input").val()

let height = $("#height_input").val()

let config = {

value: web3.utils.toWei("1", "ether")

contractInstance.methods.createPerson(name, age, height).send(config)

.on("transactionHash", hash => {

console.log("Hash=", hash)

})

.on("confirmation", confirmationNr => {

// 12 confirmation is the raccomanded confirmation number

//if(confirmationNr > 12)

console.log("confirmationNr=", confirmationNr)

})

.on("receipt", receipt => {

console.log("Receipt=", receipt)

alert("Done")

})

function showData() {

contractInstance.methods.getPerson().call().then( res => {

console.log("res=", res)
$("#name_output").text(res.name)

$("#age_output").text(res.age)

$("#height_output").text(res.height)

})

*/

})

Oracle Intro
Programming Project - Phase 2

How Oracles Work


Oracles are a service than inject off-chain data into the blockchain
From you smart cotract you use a library to call a smart contract Oracle that emits en event that a off-chain
server (linked to the Oracle) get and find the information and feed it to your smart contract via a callback
Because different transactions and internet query are involved it can take quite some time.

Oracle Code
Programming Project - Phase 2
Link to Provable Ethereum API: https://github.com/provable-things/ethereum-api

Oraclize random datasource Solidity example


Testnet Truffle Setup
Programming Project - Phase 2
Infura: infura.io

Do truffle init to create a new project


Then edit truffle-config.js and uncomment:

const HDWalletProvider = require('@truffle/hdwallet-provider');


const infuraKey = "fj4jll3k.....";
//
const fs = require('fs');
const mnemonic = fs.readFileSync(".secret").toString().trim();

HDWalletProvider is a tool that helps with transactions sign


infuriaKey is a key you get from infuria. Infuria is an API that let us access to blockchain info without having
to mantain a node.
fs is a library for dealing with files
mnemonic is the wallet seed that we put in .secret file

Than we uncomment the configuration for ropstien (test) network:

ropsten: {
  provider: () => new HDWalletProvider(mnemonic, `https://ropsten.infura.io/v3/YOUR-
PROJECT-ID`),
  network_id: 3, // Ropsten's id
  gas: 5500000, // Ropsten has a lower block limit than mainnet
  confirmations: 2, // # of confs to wait between deployments. (default: 0)
  timeoutBlocks: 200, // # of blocks before a deployment times out (minimum/default: 50)
  skipDryRun: true // Skip dry run before migrations? (default: false for public nets )
},

Register on https://infura.io/ and copy the key (project secret) in infuriaKey


than copy the api end point (select ropsten) https://ropsten.infura.io/v3/3efe8dd47d954521a23abca8abda4076
in ropsten provider
Then put the metamask (wallet) seed frase in .secret file in the root of your project(don't share this file put
in .gitignore) 

Than npm init to make a node project then npm install truffle-hdwallet-provider ( It worked instead:  npm
install --save-dev @truffle/hdwallet-provider )

To deploy the project do in the project folder:


truffle deploy --network ropsten
But you need to have (free) ropsten ether: go here https://faucet.metamask.io/
Using Provable in Truffle
In your contract import the oracle library:

import "provableApi.sol"

contract Flip is usingProvable;

You might also like