Key Solidity Commands/Illustrations

Useful foundation solidity commands - Understanding

LLL has been deprecated in late 2019Event : How to generate event when updating a variable

Sample code here under illustrate how to generate an event when we insert an input in the adopters array. You can use https://remix.ethereum.org to see how event are generated. Go on Remix Environment section and select VM (to use the Remix Blockchain Simulator). Select the good compiler version, compile the code, then deploy it with Remix. In the deployed contract section, select the deployed contract and insert an adopter by clicking on "adopt and put 1". Check on the rigth section and click on debut to see the detail. You will see in the logs section the value of the event generated:

[ { "from": "0xd4fc541236927e2eaf8f27606bd7309c1fc2cbee", "topic": "0x9bfa8a75e6c0508ff72126d8b916a6dcc0bfa9920fa37c6ad1b5db12b3218230", "event": "Triggeradoption", "args": { "0": "0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2", "1": "1", "_adopter": "0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2", "petId": "1", "length": 2 } } ]

Run this sample code to see the event generated on https://remix.ethereum.org

Adoption.sol
pragma solidity ^0.6.1;

contract Adoption {
    
    address[16] public adopters;
    
    event Triggeradoption(address _adopter, uint petId);
    
    function adopt(uint petId) public returns (uint){
        require(petId>0 && petId<15);
        adopters[petId]=msg.sender;
        emit Triggeradoption(msg.sender, petId);
        return petId;
    }
        
    function getAdopters() public view returns(address[16] memory){
        return adopters;
    
    }
}

Inheritance

contract voter {...}
// Inherite one base contract
contract belgiumVoting is voter { ... }
// Inherite multiple base contract
contract brusselsVoting is voter, belgiumVoting {...} 
// Order of inheritence matter in definition
contract diegemVoting is belgiumVoting,voter {...} //!! Doesn't work because voter must be defined first

// Interface Contract definition
interface voting {...}

Heritance is like in Python Definition The derived contract inherite:

  • State Variable

  • Functions

  • Event

The code included in the base contract is copied in the derived contract at the moment of compilation. So the inherited byte code is copy in the derived SC.

Contract can inherite multiple base contracts. Functions in derived contract can overwrite functions defined in base contract. To overwrite a base function, the overwrite function must have the same name, input, input type and output. This apply for internal and external fucntions.

If herited contract constructor requires arguments, include them in derived contract constructor signature header.

Order of inheritance matter, define order from the most base like to to most derived.

Function, modifier and event names must be unique among contract and all base contract.

Polymorphism means that a function call (internal and external) always executes the function of the same name (and parameter types) in the most derived contract in the inheritance hierarchy.

Abstract contracts: can miss function implementation, but can be used as based contract. Contract that inherite abtract contract but doesn't implement all functions are also abstract contract.

Interface contract: similar to abtract contract but can not have any function implemented. They can not inherite from any contract. They are inherited by any contract, just as regular contract. They don't have:

  • They can not declare state variable (nor enum)

  • They can not declare a constructor

  • They cannot inherit from other contracts, but they can inherit from other interfaces

  • All declared functions must be external.

Additional URL:

Library and the Ethereum Package Manager

Library Definition: Libraries are contracts that do not have storage, they cannot hold ether and they cannot inherit or be inherited by other contracts. Libraries can be seen as implicit base contracts of the contracts that use them.

  • Exists for the purpose of code reuse

  • Allow library function to modify the states of the calling contract. Possible by using the delegatecall opcode on EVM

  • The library can define the struct data type, but it will not be saved in storage until it is actually implemented in a contract. This implemented struct can be passed to the library and modified, and the modifications will persist in the contracts’ storage

  • If the contracts deployed from the factory contract are declaring the same functions, moving the functions to a library can reduce the size of the contract created by the factory

  • Calling a library function from a contract is a bit more expensive than calling internal functions, so there is a trade-off to consider.

Package Management:

Usefull URL:

SmartContract System Design

Designing a voting system (Commit, Reveal approach)

Proof of existence SC - Sample

Based on https://blog.openzeppelin.com/the-hitchhikers-guide-to-smart-contracts-in-ethereum-848f08001f05/

Proof of Existence is a service that verifies the existence of a document or file at a specific time via timestamped transactions

Step1: Connect to blockchain: using ganache-cli

Step2: Setup the Truffle Dev Framework

// Install Ganache-CLI
$ sudo npm install -g ganache-cli

// Start Ganache-CLI
// Ganache-cli starts a development blockchain that needs to be running
// while develop the application so leave it running while we continue working 
// on the application.
$ ganache-cli

// Install Truffle:
$ sudo npm install -g truffle
$ sudo npm install -g @truffle/hdwallet-provider

// Setup Truffle Project
$ mkdir proof-of-existence
$ cd proof-of-existence/
$ truffle init

//In truffle-config.js we need to specify the network that we will be using.
// In the module.exports object, add the following information:
module.exports = {
    networks: {
        development: {
            host: 'localhost',
            port: 8545,
            network_id: '*'
        }
    }
};

//ganache-cli is running our blockchain environemnt on port 8545

//Truffle comes with a Migrations.sol contract that keeps track our migrations 
//as well as a 1_initial_migrations.js script to deploy the Migrations.sol contract.

//Run the following command in the terminal in your project directory:

$ truffle migrate
//The terminal should print information about the deployment of the Migrations 
//contract.

Step 3: Write the SC

Find more info on truffle migrate on https://www.trufflesuite.com/docs/truffle/getting-started/running-migrations

//Run the following command in the terminal in your project directory:
//Set up the boilerplate code for the contract, a contract definition and 
//a contract constructor
$ truffle create contract ProofOfExistence1
//Then copy the content inside of the SC (the functions to be added)

// Create a new migrations file in the migrations directory called 2_deploy_contracts.js.
var ProofOfExistence1 = artifacts.require('./ProofOfExistence1.sol');

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

// This script tells Truffle to get the contract information from 
// ProofOfExitence.sol and deploy it to the specified network. 
// Now we just need to tell Truffle to run the deployment.
$ truffle migrate

// Truffle remembers which contracts it has migrated to the network, 
// so if we want to run the migration again on the same network, 
// we need to use the --reset option like so:
$ truffle migrate --reset

Step 4: Interact with SC

// Launch Truffle Console to interact with Blockchain
$ truffle console
truffle(development)>
// define a variable that is an instance of teh ProofOfExistence1 deployed:
var poe = await ProofOfExistence1.at(ProofOfExistence1.address)

// find address of the SC deployed
truffle(development)> poe.address
'0x5E7E0167539e117BbD3cE3DAc41b809Cb49fA2Ff'

// Call the notarization function on that SC
truffle(development)> poe.notarize('Hello World!')
{ tx: '0x60ae...2643cbea65',
  receipt: 
}

//We can get the proof for the string with
truffle(development)> poe.proofFor('Hello World!')
‘0x7f83b...126d9069’

//And check that the contract’s state was correctly changed
truffle(development)> poe.proof()
'0x7f83b...126d9069'
The hashes match!

Step 5: Updating the code of the SC

// Exit the Truffle console:
truffle(development)> .exit

// create a new file called ProofOfExistence2.sol
$ truffle create contract ProofOfExistence2

// update content of SC with input: https://github.com/ConsenSys-Academy/proof-of-existence-exercise/blob/master/contracts/ProofOfExistence2.sol
// update the 2_deploy_contracts.js to point to the ProofOfExistence2.sol
$ truffle migrate --reset

// Interact with new version (via Javascript)
$ truffle console

// Catch deployed contract in a variable
truffle(development)> var poe = await ProofOfExistence2.at(ProofOfExistence2.address)

// Check if a proof is on registy:
truffle(development)> poe.checkDocument('Hello World!')
false // doc has not been yet notarized

// notarize the document:
truffle(development)> poe.notarize('Hello World!')

// check if notarized:
truffle(development)> poe.checkDocument('Hello World!')
true // doc has been notarized

// Looping over arrays in smart contracts can get expensive as arrays get longer. 
// Using a mapping is a better solution.

Step 6: Deploy on Testnet

  • Use MetaMask (save your param in .secret in the project) !! Do not do this on production with real value, this is for simulation lab only

  • To perform transaction on Testnet, we need some Ether. Get free Rinkeby ether by going to this website

  • To deploy contracts to the testnet using Truffle without having to sync a local node, you can use Infura. Infura allows you to access a fully synced Ethereum node via their API. We will use their API to deploy our contracts to the Rinkeby testnet. Go to the Infura website and sign up for a free account.

//Edit truffle-config.js and uncomment the following
// It will import the tool to derive a private key and address from a mnemonic.
const HDWalletProvider = require('@truffle/hdwallet-provider');
// Entering your Infura API key in line 26 will allow you to easily deploy contracts to Ethereum
const infuraKey = "fj4jll3k.....";
// import your seed phrase (from the .secret file) into the file
// Truffle will use these words to access your wallet and deploy the contract.
const fs = require('fs');
const mnemonic = fs.readFileSync(".secret").toString().trim();

// Create Infura ACCOUNT:
// https://blog.infura.io/getting-started-with-infura-28e41844cc89/
curl https://rinkeby.infura.io/v3/76002fe6f0654982aafba3dd73c4f32d -X POST -H “Content-Type: application/json” -d ‘{“jsonrpc”:”2.0",”method”:”eth_getBalance”,”params”: [“0xBf4eD7b27F1d666546E30D74d50d173d20bca754”, “latest”],”id”:1}’

// Update the truffle-config.js
// In order to connect your metamask client to the Infura API gateway to Rinkeby,
// to avoid having to install a local client syncying with Rinkeby network
const infuraURL = 'https://rinkeby.infura.io/v3/<Project_ID>' <-- project id here

// For Truffle to derive our ethereum address from the mnemonic, we need to 
// install the Truffle HD wallet provider. In the terminal located in the 
// proof-of-existence project root run:
$ npm install @truffle/hdwallet-provider

// import the HD wallet provider into your truffle-config.js file.
var HDWallet = require('@truffle/hdwallet-provider')

// Add the rinkeby network configuration to the networks object in 
// truffle-config.js module.exports.
 networks: {
      development: {
          host: 'localhost',
          port: 8545,
          network_id: '*'
      },

      rinkeby: {
        provider: () => new HDWalletProvider(mnemonic, infuraURL),
        network_id: 4,          // Rinkeby's network id
        gas: 5500000,        
      },
  }

//  run “truffle migrate” for the correct network and your contract will be deployed!
$ truffle migrate --network rinkeby
// ! Error message with Node14: 
/usr/local/lib/node_modules/truffle/build/webpack:/node_modules/merkle-patricia-tree/node_modules/async/lib/async.js:358
                callback(err);
^
Error: Callback was already called.
// Workaround: 
// Truffle is not currently compatible with Node 14. Part of the problem is that Ganache is not yet compatible with Node 14 and Truffle uses that under the hood
$ truffle migrate --network rinkeby --skipDryRun

Step 7: Test the SC deployed on Rinkeby SmartContract

  • Connect to https://remix.ethereum.org with Chrome to support Metamask

  • Select injected Web3 Metamask, select Rinkebey 4 account

  • Select address where we publish the SC/ 0x1C908c985a0036637E04bdb6E9EeaA9966E7fb4d

  • Then interact and select "notarize"

Multi-Signature Wallet Sample

Sample Multi Signature Wallet

Reference on https://github.com/ConsenSys-Academy/multisig-wallet-exercise/blob/master/Multisig_wallet_info.md and https://github.com/ConsenSysMesh/MultiSigWallet

Follow steps on https://github.com/ConsenSys-Academy/multisig-wallet-exercise

A multisignature wallet is an account that requires some m-of-n quorum of approved private keys to approve a transaction before it is executed. In Ethereum, multisignature wallets are implemented as a smart contract, that each of the approved external accounts sends a transaction to in order to "sign" a group transaction.

SmartContract Best practice Security Guidlines: https://consensys.github.io/smart-contract-best-practices/

Use Remix:

Reminder of messages on Remix:

  • Yellow triangles are warning messages

  • Red circles are error messages and will prevent your contract from compiling.

Personal Nota: Code will be on: /Users/lemaima/Documents/devTest/multisig-wallet-exercise/contracts/MultiSignatureWallet.sol

// The indexed keyword in the event declaration makes the event easily // searchable and is useful when building user interfaces that need to parse // lots of events.

// When we are modifying the state of a function, it is a good practice to log an event

Another multisig wallet: https://github.com/ConsenSysMesh/MultiSigWallet/blob/master/MultiSigWalletWithDailyLimit.sol

Other useful URL on MultisigWallet:

// Start with the MultiSignatureWallet.sol
// Insert the constructor
 constructor(address[] memory _owners, uint _required)
 
 // We also added a mapping of owner addresses to booleans so that we can quickly
 // reference (without having to loop over the owners array) whether a specific 
 // address is an owner or not.
 
 // After having done the code here above, we can interact with it:
 // We will use the SimpleStorage.sol contract as target transaction to multisign
 // This is the contract that we are going to be calling from the Multisig contract.
 // look in the migrations directory you will see the deployment script 
 // that truffle will use to deploy the SimpleStorage contract as well as the 
 // MultiSig Wallet
 
// If you have made update of node, sometimes, it is needed to update also truffle
 $ npm install truffle -g
 
// Then Launch Truffle dev environment
 $ truffle develop
 
// Deploy the 3 contracts (Migration, MultiSig, SimpleStorage)
 $ truffle(develop)> migrate
 
// get the deployed instances of the SimpleStorage.sol and 
// MultiSignatureWallet.sol contracts
$ truffle(develop)> var ss = await SimpleStorage.at(SimpleStorage.address)
$ truffle(develop)> var ms = await MultiSignatureWallet.at(MultiSignatureWallet.address)
// Check the state of the SimpleStorage contract
$ truffle(develop)> ss.storedData.call()
// return -->> BN { negative: 0, words: [ 0, <1 empty item> ], length: 1, red: null }

// You can verify by waiting for the promise to resolve and converting 
//the answer to a string.
$ truffle(develop)> ss.storedData.call().then(res => { console.log( res.toString(10) )} )
// return -->> 0

// If we want to update the SimpleStorage contract data to be 5, the encoded 
// function signature and input parameters would look like this:
truffle(develop)> var encoded = '0x60fe47b10000000000000000000000000000000000000000000000000000000000000005'
// So we need to encode the functional call with Param with tool first

// Let's get the available accounts and then make a call to the MultiSig contract
truffle(develop)> var accounts = await web3.eth.getAccounts()
truffle(develop)> ms.submitTransaction(ss.address, 0, encoded, {from: accounts[0]})
// The current state of the MultiSig has one transaction that has not been 
// executed and has one confirmation (from the address that submitted it)
// One more confirmation should cause the transaction to execute
$ truffle(develop)> ms.confirmTransaction(0, {from: accounts[1]})
// You should see two log events this time as well. A “Confirmation” event 
as well as an “Execution” event. This indicates that the call to SimpleStorage 
executed successfully.
// We should see in event on console: "....    event: Execution,".

// We can verify that the state of the contract was updated by running
$ truffle(develop)> ss.storedData.call()
// return --> 5

// check that the address that updated the SimpleStorage contract was the 
// MultiSig Wallet.

$ truffle(develop)> ss.caller.call()
‘0x8...xxx’
$ truffle(develop)> ms.address
‘0x8...xxx’

Debugging Truffle Testing

Source is on: https://github.com/ConsenSys-Academy/truffle-test-debugging

Truffle is really useful for writing and running automated tests on your smart contracts. We will use https://www.trufflesuite.com/docs/truffle/getting-started/debugging-your-contracts

// First clone repo on your PC from Github: https://github.com/ConsenSys-Academy/truffle-test-debugging

// Run following js test:simpleStorageTest.js file
contract('SimpleStorage', async (accounts) => {
    it('Calling set(x) should set storedData in storage to x', async() => {
        let newValue = 2;
        let instance = await SimpleStorage.deployed()

        instance.set(newValue, {from: accounts[0]})
        let returnedValue = await instance.storedData.call()

        assert.equal(newValue, returnedValue, "The returned value should equal the new value.")
    })
})
// Launch Blockchain Simulator (use Ganache-CLI here for test)
$ ganache-cli

// Run the test (go in the cloned repo to test this -> truffle-debug/project-dir)
$ truffle test
// you will see that the test report a failure, so let's debut

//If you don see any activity on GANACHE-CLI, it is possible that not closed properly
// from previous test. then ps -al ->> then kill -9 PID

// Check on Ganache-cli interface to see call function and hash. Take the hash and
// use it for the Truffle-debug
$ truffle debug 0xc599abab3c0d47dbfc91c3570dec887928126b36fdf173201abfd9cd33f3694c

// insert "o" to go step by step on the contract exectution and enter "v" within
// each step to see how state variable evoluate.
// here error was that we add +1 to state variable, remote +1 in the SimpleStorage.sol
// then run again test
$ truffle test

Sample Exercice One - Illustration Purpose Only

Sample Exercice 2 - Illustration Purpose only

This second exercice here under is for illustration purpose only.

https://github.com/ConsenSys-Academy/supply-chain-exercise

https://github.com/CA-bootcamp-s19/supply-chain-exercise-IPConvergence

https://github.com/CA-bootcamp-s19/supply-chain-exercise-IPConvergence/settings/access

https://travis-ci.com/github/CA-bootcamp-s19/supply-chain-exercise-IPConvergence

https://pocketsense.com/purpose-escrow-account-4344.html

As ref:

https://github.com/CA-bootcamp-s19/supply-chain-exercise-arnoudcommandeur/blob/master/contracts/ProxySupplyChain.sol

https://github.com/CA-bootcamp-s19

In the field of inventory management, a stock keeping unit (SKU) is a distinct type of item for sale, such as a product or service, and all attributes associated with the item type that distinguish it from other item types. For a product, these attributes could include manufacturer, description, material, size, color, packaging, and warranty terms. When a business takes inventory of its stock, it counts the quantity it has of each SKU.

A stock-keeping unit (SKU) is a scannable bar code to help vendors automatically track the movement of inventory.

SKU are retailers code that tracks product, manufactor and price (so this is more than simple product serial number bare code).

// Solidity code statement address(0) which is the initial value of a variable of type address

Ethereum and End-User

Introduction to Web3

The problem with Internet today is your identity and your data that you don't control the access and sharing. The cost of the web is subsidized by the value of your data. This model needs to change. We need an ethical internet to foster innovation and collaboration.

It is time to decentralize the internet (go outside the power of Google, Facebook, Amazon...) and move to version 3:

  • A web of ethical collaboration, rewarding the contributor

  • A web where data is not censored

  • A web where data can be autenticated to their originator

  • A web where users own their data

Other interesting URL on the topic:

Web3.js Javascript Libraries

There are a variety of common JavaScript libraries that you can use to connect to Ethereum.

Truffle: Truffle will connect to a running blockchain specified in the truffle-config.js file, manage deployments via migration scripts and information stored in the truffle artifacts. Truffle provides contract abstractions for interacting with your contracts. Following URL are the basis API explanation, very important: https://www.trufflesuite.com/docs/truffle/reference/contract-abstractions

Web3.js

Web3.js is one of the most popular JavaScript libraries in Ethereum dApp development. There are 2 versions:

Install Ganache-GUI: https://www.trufflesuite.com/ganache

Connect your Metamask (via Chrome) to Ganache-GUI

  • Launch Ganache-GUI

  • In Metamask, select Network, new custom RPC

  • Enter http://localhost:7545

  • Then go on Ganache and copy the private key of the first account

  • Go back to Metamask and select import account and past the private key

  • Then on the chrome browser, browse: https://remix.ethereum.org, go in the section on the left "Deploy and Run" with the Ethereum logo, select environment and select Web3Provider. Then change the local host to localhost:7545 (so change port)

  • Then Remix should be connected to Metamask

  • Publish a SmartContract and see if you see a block created on ganache-GUI

  • Then go back to Chrome browser, go in menu: view -> developer -> javascript console.

  • MetaMask has injected the web3.js library in the browser.

  • So in the Chrome javascript console, type web3 and you will see the object

  • To connect the account

Side Node on Metamask:

If you are following along in your browser, you will also see that in the "currentProvider" property, the "selectedAddress" is undefined or null. Metamask does not provide access to the account address by default. If you type ethereum.enable() in the console, Metamask will pop open asking you if you'd like to connect. Connecting will make this account information accessible to the current page.

// Run on Chrome Javascript Console (when MetaMask is well connected to Ganache-GUU
// you can send transactions on the Ganache GUI network through the injected 
// web3 object in the browser console javascript
// !!! Metamask support only Web3.js 0.20 today and has injected that version here
// but the exercice here under is done with Web3.js version 1.X
// To do this you need to browse a Web page that has injected web3.js 1.X version
// in my case this is the Consensys page : https://courses.consensys.net/courses/take/blockchain-developer-bootcamp-registration-2020/texts/14509938-8-1-1-web3-js-javascript-library

// Then create a new Web3 instance that will use that web3.js 1.X injected version by
// the consensys page here above
> web3 = new Web3(web3.currentProvider)

// Return you current Metamask account address (connected to Ganache)
> web3.currentProvider.selectedAddress
// It returns your Account address

// Send a transaction via js console of Chrome:
// Use the following code snippet as your transaction information.
// Work only for web3.js v1.0 beta (not for other version, please check the API)
var transaction = {
from: web3.currentProvider.selectedAddress,
to: "0xf875cE190d4B1f8f9507b28c12a850DD9f3150e7",
value: web3.utils.toWei("1", "ether")
}

// Now send the transaction to ledger:
web3.eth.sendTransaction(transaction)

// if you get an error you may need to reset the web3 provider:
web3.setProvider(web3.currentProvider)

// Metamask will pop-up for signature, if you hav a JSON-RPC error, this is because
// you are using Metamask on different ledger and the nonce value could be the one
// buffered from the previous Metamask connection to the other ledger.
// So you simply need to reset the nonce value in Metamask
// If this happens, it is a simple fix. Open Metamask and click the account icon 
// on the upper right and select "Settings". In the "Advanced" area, 
// select "Reset Account". 

// check balance of account (valid with Web3.JS version 1.x)
> await web3.eth.getBalance("0x42e071ADfF5eeAA8Aa1B28daEd347d71fF6C59CA")

// or also
> await web3.eth.getBalance(web3.currentProvider.selectedAddress)

// Issue with the MetaMask version that is injecting web3 0.20 that doesn t
// Support those calls. You need a call back function to make it work:

web3.eth.getBalance('0x42e071ADfF5eeAA8Aa1B28daEd347d71fF6C59CA',function(error,result){

    if(error){
       console.log(error)
    }
    else{
       console.log(result)
    }
 })
 
 // Previous function is working without metamask injected web3 lib v0.20!!!

You can look at how to connect to contract with web3.js with https://web3js.readthedocs.io/en/v1.2.0/web3-eth-contract.html#web3-eth-contract

Ether.js

Ether.js library is on https://docs.ethers.io/v5/

We will continue to use Metamask for signing transaction and connecting to the local Ganache-GUI Blockchain

We will use Chrome with Javascript console open and inject command there, and use the Ether.js library that will be injected by a Web page (I use the consensys page here above)

In the browser console, you can connect ethers.js to the current network by accessing the provider given by Metamask, then set it is as the "signer". Metamask injects the provider as "web3.currentProvider", but since we changed the web3 object to use web3.js v1.0, the provider is accessible at "web3.givenProvider".

Doc:

As a developer, you can use different JavaScript libraries to interact with Ethereum blockchains. Many of the JavaScript libraries do many of the same things, but they may handle transaction signers or providers differently or have different ways of connecting to contracts and listening to events. The library you choose is primarily a matter of personal preference.

// Connect to Web3 Provider: (in the Chrome Javascript console, while beeing
// on the Consensys page here above that inject ether.js lib

/ MetaMask injects a Web3 Provider as "web3.currentProvider", so
// we can wrap it up in the ethers.js Web3Provider, which wraps a
// Web3 Provider and exposes the ethers.js Provider API.
> const provider = new ethers.providers.Web3Provider(web3.currentProvider);

// There is only ever up to one account in MetaMask exposed
const signer = provider.getSigner();

// Create the transactin:

var transaction = {
    to: "0xf875cE190d4B1f8f9507b28c12a850DD9f3150e7",
    value: ethers.utils.parseEther("1")
}
//S end it, enter  
> signer.sendTransaction(transaction) 

Connecting web3.js to contract

For this exercice, we are going to connect to the following contract for the test https://gist.github.com/ConsenSys-Academy/6d93a805ce0e90d8a793a4eb6e69b4c5

Go in view a Chrome -> view -> developer -> Javascript console, so you can interact via javascript to web3

Connect your Metamask in the same chrome to Rinkeby and swap to your Rinkeby account that has test ether (often people forget to swap manually account and they use accound of the local Ganache-GUI that is not defined on Rinkeby with Ether received via faucet

Open you Chrome browser and browse the consensys page :https://courses.consensys.net/courses/take/blockchain-developer-bootcamp-registration-2020/texts/14509940-8-1-2-connecting-web3-js-to-a-contract

Check that the web3 object instance has well been injected in the browser javascript console by the Metamask app and check that the provider is well the address of your account on Rinkeby

web3.currentProvider.selectedAddress -> you should see the address of your Rinkeby account selected in your metamask client

// Type to following commands in your javascript console in Chrome

// Initialize the web3.js lib in your javascript browser console to use it
> web3 = new Web3(web3.currentProvider)

// To use the SimpleStorage Contract deployed on Rinkeby, you need to initialise 
// an object in javascript consol, using web3 lib

// We provide to our web3 object with the SC API and address
> const SSaddress = "0x49Bb098E781eD5C50D85E82d85cbA1a6F03FD3e6"

// Set the ABI in the javascript console

> const ABI = [
        {
                "constant": false,
                "inputs": [
                        {
                                "internalType": "uint256",
                                "name": "x",
                                "type": "uint256"
                        }
                ],
                "name": "set",
                "outputs": [],
                "payable": false,
                "stateMutability": "nonpayable",
                "type": "function"
        },
        {
                "anonymous": false,
                "inputs": [
                        {
                                "indexed": false,
                                "internalType": "uint256",
                                "name": "newValue",
                                "type": "uint256"
                        },
                        {
                                "indexed": false,
                                "internalType": "address",
                                "name": "updatedBy",
                                "type": "address"
                        }
                ],
                "name": "storageUpdate",
                "type": "event"
        },
        {
                "constant": true,
                "inputs": [],
                "name": "get",
                "outputs": [
                        {
                                "internalType": "uint256",
                                "name": "",
                                "type": "uint256"
                        }
                ],
                "payable": false,
                "stateMutability": "view",
                "type": "function"
        }
]
// Then create the Rinkeby SimpleStorage Running SC, instance on Javascript
// console, via web3 lib
> const simpleStorage = new web3.eth.Contract(ABI, SSaddress)

// if you don't have any more Ether on Rinkeby, use faucet:  https://www.rinkeby.io/#faucet

// Read the Contract State, so the value of storeData state variable
> simpleStorage.methods.get().call().then(console.log)

// Insert a value in the Storage SmartContrat
> simpleStorage.methods.set(9).send({from: web3.givenProvider.selectedAddress})
// Running this code should trigger Metamask to ask you to sign a transaction
// This open the Wallet and you just need to validate the transaction

// Validate the transaction writing
> simpleStorage.methods.get().call().then(console.log) 

// Subscribe to events with simpleStorage
> simpleStorage.events.storageUpdate(function(error, event){console.log(event)})

// https://web3js.readthedocs.io/en/v1.2.0/web3-eth-contract.html#contract-events
// To trigger this event, you will have to call the "set()" function on the contract again


{address: "0x49Bb098E781eD5C50D85E82d85cbA1a6F03FD3e6", blockHash: "0x06611e65952cab4abe3d4b5379b6fa3470c0def67dc75899c6c100a686e3baf1", blockNumber: 7415667, logIndex: 23, removed: false, …}
address: "0x49Bb098E781eD5C50D85E82d85cbA1a6F03FD3e6"
blockHash: "0x06611e65952cab4abe3d4b5379b6fa3470c0def67dc75899c6c100a686e3baf1"
blockNumber: 7415667
event: "storageUpdate"
id: "log_cb9c26a9"
logIndex: 23
raw: {data: "0x000000000000000000000000000000000000000000000000…0000000007c584e710fe6afa5369a8bffee47fc98d76535a5", topics: Array(1)}
removed: false
returnValues: p {0: "9", 1: "0x7C584e710fE6afA5369A8Bffee47Fc98D76535a5", newValue: "9", updatedBy: "0x7C584e710fE6afA5369A8Bffee47Fc98D76535a5"}
signature: "0x8920a650e770f0ad1e6f22b8db2e2aebfad8ce417eddbb244a5499495a727c15"
transactionHash: "0x31a59ff98a318ebe471c23803a30606975ee82c8ff6671fc3ced9ff9c953859f"
transactionIndex: 20
__proto__: Object

Building Truffle for the Web

As reference for inspiration: https://www.trufflesuite.com/tutorials/pet-shop#creating-a-ui-to-interact-with-our-smart-contract

We are going to build a Browser App interface to interact with SC.

// GO to your petshop directory
$ truffle compile
// It creates artifact in build/constrats section

// Create the 2_deploy.contracts.js (to deploy the SC on ledger)

// Edit truffle-config.js to point to port 8545 of your local ganache-cli
// Launch Ganache-cli on your machine
$ ganache-cli

// Then deploy the SC on ganache-cli ledger (run this command in your truffle project directory)
$ truffle migrate

// You should see succesfull deployed

// Go on you Visual Studo Code editor on folder /src/js/app.js (we are going to build the web interface)

// Connect your Metamask client to the local Ganache-CLI (on port 8545)
// Select localhost:8545, import account 1 of ganache by taking private key
// Then click on account in metamask to select connect. You should the Ether value of the account on the account on Metamask
// If not, client connect account on metamask

// Then launch the  Web server hosting the app.js
$ npm run dev
// This will launch the lite server

// Then go to your Chrome Browser and run localhost:3000 to see the app and interact

Update to Metamask

Metamask automatically injected the web3 object into the browser window object, making it available for any application to use it. Metamask now provides the option to no longer automatically inject the web3 object, something called "privacy mode". Which requires that dapps ask permission to view users’ accounts. Dapps should update their code to support this feature.

dapps must now request access to user accounts by calling a new method on the provider: ethereum.enable(). This method returns a Promise that’s either resolved with user accounts after user approval, or rejected with an Error after user rejection.

Integrating with React

The following sample exercice is usefull to understand how to integrate with React framework: https://www.trufflesuite.com/boxes/react

Other usefull pages:

// Use Truffle React box
$ mkdir truffle_react
$ cd truffle_react
// Install the React truffle box
$ truffle unbox react

// Go to truffle-config.js and add the local ganache Ledger
networks: {
    development: {
      host: "127.0.0.1",
      port: 8545,
      network_id: "*" // Match any network id
    }
  }
  
// Go to Truffle_reac/client/src/getWeb3.js
// add the local httpProvider 127.0.0.0.1:8545
// port 8545 for ganache-cli and 7545 for ganache-ui

// Launch ganache-cli
$ ganache-cli

// Copy mnemonic or private key and import on Metamask in Chrome browser

// Migrate contract to ganache
$ truffle migrate // from inside the truffle_react directory

// Start the React web app
$ cd /client
$npm run start
// This will start the Web client app (app.js)
// you will have a pop-up to validate transaction by Metamask

// change the value in the simple storage via the app.js file

Rimble UI

Rimble is a React component library that makes creating Web3 applications in react much easier. The library is under active development by the ConsenSys Design team.

Other usefull URL on the subject:

// Install Rimble UI
$ npm install --save rimble-ui styled-components

// To include a Button, this is all you need:

import React, { Component } from 'react' 
import { Button } from 'rimble-ui'
class Example extends Component {
    render () {
        return (
            <Button size={'medium'}>
                Click me!
            </Button>
        )
    }
}

Smart Contracts Pitfalls, Testing, and Debugging

Covered on this section:

  • Writing tests for Solidity smart contracts in Javascript and Solidity using the Truffle framework

  • Smart contract best practices

  • Smart contract exploits and dangers

  • Optimizing Gas

  • Go over a safety checklist of things to consider before deploying your contracts

Writing Test

Test Driven Development: Developers write tests first, then implement functions to make the tests pass. We write test for:

  • Verify correct Behaviour

  • Consider Edge Cases

  • Consider range of possible user input

  • No necessarily for finding bugs only:

    • It is about contract behaviour

    • TDD is about behaviour defined first through unit test

  • Test in Truffle can be writen in Solidity or Javascript

  • Testing contract should not extend any existing contract

  • With Truffle, we import:

    • Assert.sol library (from Truffle, planned for test)

    • DeployedAddresses.sol (from Truffle, planned for test): The addresses of your deployed contracts (i.e., contracts that were deployed as part of your migrations) are available through the truffle/DeployedAddresses.sol library.

    • + the contract we want to test

  • Remark regarding the test contract:

    • Contract name should start with T

    • Function test must beging with t (test...)

// We import the DeployedAddresses.sol truffle made SC for accessing the targeted SC
// to bet tested
import "truffle/DeployedAddresses.sol";
contract TestAdoption {
 // The address of the adoption contract to be tested
 Adoption adoption = Adoption(DeployedAddresses.Adoption());
 
 // We import the Assert.sol SC from Truffle to compare value
 // We all the time compare the value returned by the SC function (returnedId)
 // to an expected value expectedPetId
 Assert.equal(returnedId, expectedPetId, "Adoption of the expected pet should match what is returned.");
 
 // use memory keyword when you need just tempor variable in SC for check
 address[16] memory adopters = adoption.getAdopters();
 Assert.equal(adopters[expectedPetId], expectedAdopter, "Owner of the expected pet should be this contract");

Kooks:

  • There exists also provided hooks like in Mocha Javascript Testing Framework

  • Use to setup and teardown before and after each test

  • It is possible for the setup function to exceed the gaz limit, then play with beforeEachAgain() hook to solve that problem

Test with Javascript: (see example pet-shop-tutorial/test/testAdoption.test.js)

  • Truffle use the Mocha Framework and chai for assertion

  • use contract() instead of describe()

  • before each contract(), the contract are redeply, meaning state variable rest also (clean state)

  • you can still use describe() when contract() is not necessary

// To import SC in javascript test file, use require
const Adoption = artifacts.require("Adoption");

// contract() provides a list of account
// web3 is provided in the testing environment (to be put there)
// A web3 instance is available in each test file, configured to the correct provider. So calling web3.eth.getBalance just works!

// 2 ways to call test: to be called from withing the project truffle repo
// Exectue the SC.test + the javascript test
$ fruffle test
// Exectue only the javascrip test
$ cd test/
$ truffle test testAdoption.test.js

Other good references for testing writing:

Catching Javascript error

Try catch

// When a VM Exception is encountered by Truffle, it throws an Error. 
// We can catch this error in a try...catch block.

async function tryCatch(promise) {
    try {
        await promise;
        throw null;
    }
    catch (error) {
        assert(error, "Expected a VM exception but did not get one");
        assert(error.message.search(errorString) >= 0, "Expected an error starting with '" + errorString + "' but got '" + error.message + "' instead");
    }
};//

This tryCatch function will first execute the try block that will wait for the promise that it received to resolve. If it resolves without throwing an error, then the last line of the try block will throw an empty error. So no matter what, whether the promise succeeds or fails, the catch block will be executed, receiving whatever error information was thrown in the try block.

Special error handling

We can make this error handling function even more specific so that it can tell us why a specific error is being thrown. The VM exception messages provide details as to why an exception was thrown. For example, whether the exception was due to a revert, if the transaction ran out of gas, if there was an invalid JUMP or another invalid OPCODE, if there was a stack overflow or underflow or if there was a static state change. We can add these details to our errorString to catch specific cases.

// save this file called something like "exceptionHelpers.js" in 
// your Truffle test directory

const errorString = "VM exception while processing transaction: ";

async function tryCatch(promise, reason) {    
    try {
        await promise;
        throw null;
    }
    catch (error) {
        assert(error, "Expected a VM exception but did not get one");
        assert(error.message.search(errorString + reason) >= 0, "Expected an error starting with '" + errorString + reason + "' but got '" + error.message + "' instead");
    }
};

module.exports = {
    catchRevert            : async function(promise) {await tryCatch(promise, "revert"             );},
    catchOutOfGas          : async function(promise) {await tryCatch(promise, "out of gas"         );},
    catchInvalidJump       : async function(promise) {await tryCatch(promise, "invalid JUMP"       );},
    catchInvalidOpcode     : async function(promise) {await tryCatch(promise, "invalid opcode"     );},
    catchStackOverflow     : async function(promise) {await tryCatch(promise, "stack overflow"     );},
    catchStackUnderflow    : async function(promise) {await tryCatch(promise, "stack underflow"    );},
    catchStaticStateChange : async function(promise) {await tryCatch(promise, "static state change");},
};

In the catch block, the first assertion statement checks whether the error message contains any information. If the promise successfully executed, this first assertion statement will fail and will let you know that it "Expected a VM exception but did not get one." So "error!=0" -> it executes the texte after. If the first assertion statement passes, and the error message does contain information, then the second assertion statement will be executed. The statement will search the error message for a predefined string.

How to include in our .js test?

Now to include this in your Javascript tests, you can save this file called something like "exceptionHelpers.js" in your Truffle test directory. You can import specific functions and include them in your tests! This test will pass as long as the buyTickets() function throws a revert!

// Add this in you testing.js function

var EventTickets = artifacts.require('EventTickets')
let catchRevert = require("./exceptionsHelpers.js").catchRevert

contract('EventTicket', function(accounts) {

  // ...

    it("tickets should only be able to be purchased when the msg.value 
        is greater than or equal to the ticket cost", async() => {
        const instance = await EventTickets.new(description, url, ticketNumber)

        await catchRevert(instance.buyTickets(1, {from: secondAccount, value: web3.utils.toWei("0.5", "ether")}))
    })


  // ...

}

Other References on the subject:

Smart Contract Best Practices

Different than other programing language:

  • Cost of failure can be high

  • Change can be difficult

  • Move Slow and be carefull

Prepare for failure:

  • Code will have bug

  • Setup a circuit breaker design pattern

  • Manage amount of money at risk with rate limiting or caps

  • Setup upgrade path for bug fixes and improvement

Roll-out very carefully

  • Catch Bug before release

    • Automated testing

    • Use Testnets

    • Time locking Alpha and beta release

    • 3th party security audit

  • But bounties (Prime)

Keep contract simple

  • Complexity => more bugs

  • Moduralize the code, with simple block

  • Use Audited and battle tested code when possible

  • Prefer clarity than performance

  • Only use blockchain when necessary

Unique Environment

  • Know the env

  • Be very carefull when calling external contract

  • Function are public

    • malicious actors will try calling your function in a way you didn't intend or foresee

  • contract data is public

  • Limits proper to this sytem (contrains)

    • gaz price

    • block gaz limit

Fundamental trade-offs

  • trade-offs between engineering and security

    • Ideal system is modular, reuse code, and support upgradable component

  • Rigidity versus upgradability

    • on SW dev perspective, upgradability support is good but on SC it highers the maleability, complexity and surface attack

    • For simple function, simplicity is focus before upgradability support

  • Monolithic versus Modular

    • A single Monolithic SC keep all data local, identifiable and controlable, can make code review easier

    • Modular can be easier to maintain on SW point of view

  • Code duplication versus reuse

    • Use proved, deployed, audited code when possible

Other usefull reference:

Exploits and Dangers

There is a SmarContract Weakness Classification Registry (SWC) under: https://swcregistry.io

The Smart Contract Weakness Classification Registry (SWC Registry) is an implementation of the weakness classification scheme proposed in EIP-1470. It is loosely aligned to the terminologies and structure used in the Common Weakness Enumeration (CWE) while overlaying a wide range of weakness variants that are specific to smart contracts.

The goals of this project are as follows:

  • Provide a straightforward way to classify security issues in smart contract systems.

  • Define a common language for describing security issues in smart contract systems' architecture, design, or code.

  • Serve as a way to train and increase performance for smart contract security analysis tools.

Re-entracy Attack (SWC 107) https://swcregistry.io/docs/SWC-107

Problematic because calling external contracts passes control flow to them. The called contract may take over the control flow and end up calling the smart contract function again in a recursive manner (you could have then an un-ending loop or abuse loop). If you can’t remove the external call, the next simplest way to prevent this attack is to do the internal work before making the external function call.

// Insecure Target SC that call function execution from the calling SC
//Example of a user calling this function that will implement in his calling
// SC amountToWrithdraw() that call transfer on an INSECURE way
mapping (address => uint) private userBalances;

function transfer(address to, uint amount) {
    if (userBalances[msg.sender] >= amount) {
       userBalances[to] += amount;
       userBalances[msg.sender] -= amount;
    }
}

function withdrawBalance() public {
    uint amountToWithdraw = userBalances[msg.sender];
    require(msg.sender.call.value(amountToWithdraw)()); // At this point, the caller's code is executed, and can call transfer()
    userBalances[msg.sender] = 0;
}
// In this case, the attacker can call transfer() when their code is executed on the external call in withdrawBalance. Since their balance has not yet been set to 0, they are able to transfer the tokens even though they already received the withdrawal. This vulnerability was also used in the DAO attack.
// Ask yourself, if multiple contract functions are called, what happens?

How to mitigate:

  • Good idea to handle your internal contract state changes before calling external contracts

  • A more complex solution could implement mutual exclusion, or a mutex. This allows you to lock a state and only allow changes by the owner of the lock.

Additionnal complementary info on: https://consensys.github.io/smart-contract-best-practices/known_attacks/#race-conditions42

Transaction Ordering and Timestamp Dependence (SWC-114) https://swcregistry.io/docs/SWC-114

Here we focus on how transactions are included in the blockchain and considerations around the process.

Transactions that are broadcast to the network but have not yet been included in a block are in the mempool. Miners choose the order in which to include transactions from the mempool into a block that they are mining. Also, since transactions are in the mempool before they make it into a block, anyone can know what transactions are about to occur on the network. This can be problematic for things like decentralized markets. Protecting against this is difficult and you will likely need to devise contract specific solutions. Decentralized markets can mitigate concerns by implementing batch auctions (enchère) or using a pre-commit scheme, where the details are submitted after the transaction is committed.

Integer Overflow and Underflow (SWC-101) https://swcregistry.io/docs/SWC-101

Integers can underflow or overflow in the EVM (SWC-101). This happens when an arithmetic value oversteps the minimum or maximum size of a type. Reference

An overflow/underflow happens when an arithmetic operation reaches the maximum or minimum size of a type. For instance if a number is stored in the uint8 type, it means that the number is stored in a 8 bits unsigned number ranging from 0 to 2^8-1. In computer programming, an integer overflow occurs when an arithmetic operation attempts to create a numeric value that is outside of the range that can be represented with a given number of bits – either larger than the maximum or lower than the minimum representable value.

Underflow is a similar situation, but when a uint goes below its minimum value it will be set to its maximum value.

Denial of Service with Failed Call (SWC-113) https://swcregistry.io/docs/SWC-113

External calls can fail accidentally or deliberately, which can cause a DoS condition in the contract. To minimize the damage caused by such failures, it is better to isolate each external call into its own transaction that can be initiated by the recipient of the call. This is especially relevant for payments, where it is better to let users withdraw funds rather than push funds to them automatically (this also reduces the chance of problems with the gas limit).

Remediation: it is recommended to follow call best practices:

  • Avoid combining multiple calls in a single transaction, especially when calls are executed as part of a loop

  • Always assume that external calls can fail

  • Implement the contract logic to handle failed calls

Denial of Service by Block Gas Limit or startGas (SWC-128) https://swcregistry.io/docs/SWC-128

When smart contracts are deployed or functions inside them are called, the execution of these actions always requires a certain amount of gas, based of how much computation is needed to complete them. The Ethereum network specifies a block gas limit and the sum of all transactions included in a block can not exceed the threshold.

Programming patterns that are harmless in centralized applications can lead to Denial of Service conditions in smart contracts when the cost of executing a function exceeds the block gas limit. Modifying an array of unknown size, that increases in size over time, can lead to such a Denial of Service condition.

There is a limit to how much computation can be included in a single Ethereum block, currently 10,000,000 gas worth

How transaction are generally ordered in block (Miner can decide), but this is a trend: https://ethereum.stackexchange.com/questions/6107/what-is-the-default-ordering-of-transactions-during-mining/6111#6111

Remediation:

Caution is advised when you expect to have large arrays that grow over time. Actions that require looping across the entire data structure should be avoided.

If you absolutely must loop over an array of unknown size, then you should plan for it to potentially take multiple blocks, and therefore require multiple transactions.

Force Sending Ether

Another danger is using logic that depends on the contract balance. Be aware that it is possible to send ether to a contract without triggering its fallback function. Using the selfdestruct function on another contract and using the target contract as the recipient will force the destroyed contract’s funds to be sent to the target.

It is also possible to pre-compute a contracts address and send ether to the address before the contract is deployed.

The contract’s balance will be greater than 0 when it is finally deployed.

Complementary interesting links:

Optimizing Gaz

Reducing the gas consumed by a contract is important in two situations:

  • Cost of deploying a contract

  • Cost to call the contract functions

The Solidity optimizer tries to improve the efficiency of your contract as much as possible during compile time

Gaz cost per opcode: https://docs.google.com/spreadsheets/d/1n6mRqkBz3iWcOlRem_mO09GtSKEKrAsfO7Frgx18pNU/edit#gid=0

Short Circuit Rules

The operators || and && apply the common short-circuiting rules. This means that in the expression f(x) || g(y), if f(x) evaluates to true, g(y) will not be evaluated even if it may have side-effects.

// How can these functions in Unoptimized.sol be modified to reduce gas usage?
// https://gist.github.com/ConsenSys-Academy/a61670fd8796d73d8b4b7d5935f9e714
// Not optimal way
function shortCircuit() public view returns(bool){
    if (oftenFalse || oftenTrue) {
        return true;
    }
}

// Better way
function shortCircuit2() public view returns(bool){
    if(oftenTrue && oftenFalse) {
        return false;
    } else {
        return true;
    }
}

Expensive operations in a loop

Modifying storage variables in a loop can be very expensive and should be avoided unless absolutely necessary. How can this function be improved, given that loop is a storage variable? Here is the source file.

function looping (uint x) public returns (bool) {
    for(uint i; i < x; i++){
        loops += 1;
    }
    return true;
}

Reduce the number of loops

Zero loops is ideal, but sometimes you just have to loop. Since loops are expensive, can you reduce the number of loops in your functions?

function looping2 (uint x) public pure returns(bool){
    uint m = 0;
    uint v = 0;
    for(uint i = 0; i < x; i++){
        m += i;
    }
    for(uint j = 0; j < x; j++){
        v -= j;
    }
    return true;
}

Fixed size byte arrays

The type byte[] is an array of bytes, but due to padding rules, it wastes 31 bytes of space for each element (except in storage). It is better to use the bytes type instead. see https://solidity.readthedocs.io/en/latest/types.html#fixed-size-byte-arrays

Other good document references:

Safety Checklist

SWC Registry

For a comprehensive, maintained list of smart contract weaknesses, along with test cases and examples of vulnerable smart contracts, check out the SWC Registry. This is a community maintained project and includes most, if not all of the well known vulnerabilities and hacks, with contract samples and suggestions for how to address weaknesses.

Additional Resources:

Logic Bugs :

  • run unit test to avoid logic bug

  • following standard coding and best practices

  • avoid complex rule and implementation

Recursive call

  • Can be dangerous, re-entrancy attack with external SC call (this was exploited in the DAO attack)

Integer arithmetic overflow

  • integers will automatically and silently wrap if too small or too large

  • max for uint = 2^256-1 (which is a quite huge number)

  • Usint uint8 -> can be dangerous if user can insert data without control of his value

Poison Data

Like a user changing a state variable so that it oveflows.

  • Assume user will input data we don't expect

  • validate and sanatize user input data

What contract expose to the world

  • Be aware

  • ! Soliditiy functions default to public

  • Any code that go to production should be audited

  • All code and data (state variable) are public on blockchain (all storage variable are public)

    • Making them private only make them not accessible to SC directly

    • !!! Private storage variable can still be observed by external blockchain user

Miner Vulnerabilities

Miners can manipulate block timestamp and which transaction can be in a block, during a limited windows time frame. Sometimes better to include a timestamp inside the SC state variable

Malicious Admin

Dangerous to allocat to specific address power on SC. Better to work with multisig contract.

Off-chain safety

Use tradditional Web security best practices:

  • HTTPS

  • 2 factors authentication

  • encryption

Cross-chain Replay attack

When hardforks are happening on a blockchain, any transaction on one is valid on the other one. So one transaction posted on one blockchain could be run on the other one, without our consent, because valid by default

When hardforks are happening on a blockchain, any transaction on one is valid on the other one. So one transaction posted on one blockchain could be run on the other one, without our consent, because valid by default

TX Origin and Gas limit

!!! Use msg.sender in place of tx.origin

Looping over a undetermined range of a array could be dangerous. We could exeed block gas max limit

  • Run Test for gas usage in our SC tests

  • Limit the lenght of user supplied data

Security Analysis Tools

A quick and easy way to check your Solidity code for common bugs is to use security analysis tools. Static analysis tools will parse your code and highlight potential security vulnerabilities.

  • Code online check tool: https://tool.smartdec.net

  • The more popular security tool: https://mythx.io

    • MythX is the premier security analysis API for Ethereum smart contracts. It detects many common Solidity and EVM bytecode vulnerabilities. This makes it attractive for developers and auditors alike. It can be used right away with integrations for Truffle, Remix, Brownie, or from the system CLI. There are also libraries (https://docs.mythx.io/en/latest/building-security-tools/index.html#language-bindings) available as a starting point for building custom security tools such as IDE extensions and CI scripts.

    • MythX is a security analysis API that allows anyone to create purpose-built security tools for smart contract developers. Tools built on MythX integrate seamlessly into the development environments and continuous integration pipelines used throughout the Ethereum ecosystem.

Exercice to do:

// Go to https://github.com/ConsenSys/truffle-security
// Install the NPM Pluggin (Mythx)
// ! It is going to be deprecated
// They recommend to use another interface https://github.com/dmuhs/mythx-cli
$ npm install -g truffle-security

Generate the Mythx API key: Generate your API key in the tools section of the MythX dashboard.

The key can be passed to Truffle either via the MYTHX_API_KEY environment variable or the --apiKey command line argument. For security reasons it is recommended to always pass the token through an environment variable, e.g. defined in the settings of a Continuous Integration (CI) server or a shell script that can be sourced from.

Set the following enviromment variables to your API key (add this to your .bashrc or .bash_profile for added convenience):

$ export MYTHX_API_KEY='Put your API key here!'

// Or more simple on CLI the security check to see if Mythx discover security issue
$ truffle run verify --apiKey ddddd

// You can see the report on MythX interface: https://dashboard.mythx.io/#/console/analyses/e82cb004-5918-4b4b-a3a5-57f7a35bb635

To test Mythx, we will use the following Github project: https://github.com/ConsenSys-Academy/simple-coin In the Visual Studio, go to source control on the left, '...' source, clone, github "consensys-Academy/simple-coin

MythX can also be used on Remix!

Additional Resources

tx.origin Attack Demo

Ref Material:

tx.origin is a global variable in Solidity which returns the address of the account that sent the transaction. Using the variable for authorization could make a contract vulnerable if an authorized account calls into a malicious contract. A call could be made to the vulnerable contract that passes the authorization check since tx.origin returns the original sender of the transaction which in this case is the authorized account.

The global variable tx.origin in Solidity always references the address of the original sender of the transaction, of the full call chain. See the reference in the docs here. This is different than message.sender in that message.sender references the address of the sender of the current call.

You should never use tx.origin in Solidity for authorization

Additional References:

Denial of service attack example

Shows how a contract may be susceptible to a denial of service attack by an unexpected revert (SWC-113)

External calls can fail accidentally or deliberately, which can cause a DoS condition in the contract. To minimize the damage caused by such failures, it is better to isolate each external call into its own transaction that can be initiated by the recipient of the call. This is especially relevant for payments, where it is better to let users withdraw funds rather than push funds to them automatically (this also reduces the chance of problems with the gas limit).

Re-entrancy attack example

A reentrancy attack -> (https://swcregistry.io/docs/SWC-107) l

Integer over/under flow example

Shows what an integer overflow vulnerability (https://swcregistry.io/docs/SWC-101) looks like. This type of attack is easily avoidable by using a SafeMath library such as this, that provides safety checks and will revert on error.

MythX

MythX is a smart contract security service for Ethereum.

Advanced Ethereum Topics

We will cover the following points:

  • Common Smart contract design patterns

  • Designing upgradable smart contract systems

  • What are Oracles, and how do you use one?

  • Using the Ethereum Name Service

  • An introduction to IPFS

  • Formal Verification and smart contracts

SmartContract Design Pattern

We will go over some of the most common design patterns that developers use when writing smart contracts.

Good Principle: Fail early and fail loud

Restricting Access

You cannot prevent people or computer programs from reading your contracts’ state. The state is publicly available information for anyone with access to the blockchain. However, you can restrict other contracts’ access to the state by making state variables private.

contract C1 {
        uint private internalNum;
}

You can restrict function access so that only specific addresses are permitted to execute functions. This is useful for allowing only designated users, or other contracts to access administrative methods, such as changing ownership of a contract, implementing an upgrade or stopping the contract.

contract Admin {

        mapping(address => bool) admins;

        modifier onlyAdmin {
                require(admins[msg.sender] == true);
                _;
        }

        function addAdmin(address _a)
                public
                onlyAdmin
                returns(bool)
        {
                admins[_a] = true;
                return true;
        }

}

More on access control with Zepplin: https://docs.openzeppelin.com/contracts/2.x/access-control

Auto-Deprecation

The auto deprecation design pattern is a useful strategy for closing contracts that should expire after a certain amount of time. This can be useful when running alpha or beta testing for your smart contracts.

!!! Remember that using timestamps such as the now keyword are subject to manipulation by the block miners in a 30-second window.

pragma solidity ^0.4.25;

contract Autodeprecate {
        uint contract_expired;
        
        modifier is_active {
                if(!expired()) _;
        }

        modifier when_deprecated {
                if(expired()) _;
        }

        constructor(uint duration) {
                contract_expired = now + duration;
        }

        function expired()
                view
                public
                returns(bool)
        {
                return now > contract_expired ? true : false;
        }
}

Mortal

Function to kill a SC. Implementing the mortal design pattern means including the ability to destroy the contract and remove it from the blockchain. You can destroy a contract using the selfdestruct keyword. The function to do it is often called kill. It takes one parameter which is the address that will receive all of the funds that the contract currently holds. As an irreversible action, restricting access to this function is important.

As compl info sample: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/56de324afea13c4649b00ca8c3a3e3535d532bd4/contracts/access/Ownable.sol

pragma solidity ^0.5.0;

import "./Ownable.sol";

contract Mortal is Ownable {
    
    function kill()
    {
           if(msg.sender == owner()) selfdestruct(address(uint160(owner()))); // cast owner to address payable
    }

}

Withdrawal pattern (pull over push payment)

See also: https://gist.github.com/critesjosh/80d41928db2310684bc7660aa45873da, there is a separation of function logic. The split() function handles the accounting and divides the msg.value sent with the transaction. Another function, withdraw(), allows accounts to transfer their balance from the contract to their account.

This pattern is also called the withdrawal pattern. It protects against re-entrancy and denial of service attacks that we will cover in the next lesson.

Circuit Breaker

Circuit Breakers are design patterns that allow contract functionality to be stopped. This would be desirable in situations where there is a live contract where a bug has been detected. Freezing the contract would be beneficial for reducing harm before a fix can be implemented.

contract CircuitBreaker {

    bool public stopped = false;

    modifier stopInEmergency { require(!stopped); _; }
    modifier onlyInEmergency { require(stopped); _; }

    function deposit() stopInEmergency public { … }
    function withdraw() onlyInEmergency public { … } 
}

In a situation such as this, you would also want to restrict access to the accounts that can modify the stopped state variable, maybe to the contract owner (such as multisig wallet) or a set of admins.

State Machine

Contracts often act as a state machine, where the contract has certain states in which it behaves differently and different functions can and should be called. A function call often ends a stage and moves the contract to the next stage (especially if the contract models interaction). It is also common that some stages are automatically reached at a certain point in time.

Speed Bump

Speed bumps slow down actions so that if malicious actions occur, there is time to recover.

pragma solidity ^0.4.25;

contract SpeedBump {

        uint allowedTime;

        modifier allowed_every(uint _time){
                if(now > allowedTime){
                        _;
                        allowedTime = now + _time;
                }
        }

        constructor(uint duration){
                allowedTime = 0;
        }

        function setAllowedTime(uint _time)
                public
                returns(bool)
        {
                allowedTime = now + _time;
                return true;
        }

}

Used to limit issue of DAO: https://github.com/blockchainsllc/DAO

The Ethereum Name System

Resolve Human readable name to machine readable identifiers:

  • Name to Ethereum Address

  • Name to IPFS Hash

  • Name to Swarm Hash

It aims at solving the same problem as DNS for Internet (Name -> IP Address)

ENS offer Ethereum address resolution but the system could be extensed, enabling further resources to be resolved in the future.

ENS Architecture:

  • Registry SmartContract:

    • Single Contract

    • List domain, subdomain and data

      • The Data include the owner of the domain that can be an external account or contract

    • Store the address of the Resolver SC for the domain and the Time to live

    • Domain Owner can

      • set the resolver they want to use

      • can transfer the ownership of the domain

      • can change also ownership of subdomain

  • Resolver SmartContract:

    • Translate name to machine readable identifier

    • Any contract that implement the standard resolver can be setup as resolver

    • Each record type (Ethereum, ipfs) defines a method the resolver must implement

    • New Record type can be submitted via EIP (Ethereum improvement proposal) to community

Additional Resources:

IPFS

  • IPFS stand for the Inter-planetary file system

  • Hashes in IPFS start with "Qm..."

  • The js-ipfs library allows you to use IPFS without having to run a local IPFS node

  • It aims to replace HTTP by addressing file content (with hashes) rather than file locations

  • It uses p2p protocols, making it a decentralized network

  • It complements blockchain nicely, as it allows immutable, off-chain data storage

  • Other benefits include bandwidth saving for servers, potential content availability improvements, censorship and deletion resistance and data deduplication

IPFS is a distributed Peer to Peer file system:

  • no servers

  • only node

  • nodes store files and cryptographic hashes of files

  • Files are adressed by hashes

  • A single network sharing git objects

Good complement to Ethereum for decentralized applications:

  • Maintain Data Integrity of the resources that it references (change it content data -> change in hash so change in address)

  • Peer to peer and decentralized, so no SPOC and resilient

  • Cheaper Data Storage than blockchain

    • Store Data on IPFS

    • Store IPFS hash on SC

Additional Resources:

Upgradable contract

  • Bugs are inevitable -> upgradability strategy needed for SC

  • Modular design -> Enable to upgrade some component and not all

    • Plan for upgrade contract is important, but lots of research on it

2 approaches for managing upgradable SC:

  • Maintain a registry SC storing the address of the latest version of SC

  • Or use a contract (Proxy) that forward Data and call to the correct SC

Upgrade take time:

  • Incorporate security measure, like circuit breaker

Registry SC:

  • Store the latest contract address -> this requires user make a query for every time they want to use a SC to know the latest address

    • Disadvantage: if user not use it, it is pushing data on old SC version + how to manage data migration

Forward Data and Call SC:

  • Avoid problem of registry contract

  • User only interact with User relay contract

  • Attention on storage point:

    • Must be the same between upgrade

Additional Resources:

  1. https://eips.ethereum.org/EIPS/eip-2535 -> The diamond standard a new way to upgrade SC

Oracle with Rhombus

  • Oracle provides off-chain data into SC

  • Oracle are like an API for Blockchain

  • You could build your own oracle, but some cons:

    • How can a user be sure he can trust your oracle?

  • Some 3th party Oracle Service provider:

    • Oraclize (a leading one for Ethereum, bitcoin...)

    • Smartcontract.com

  • Oracle logic is "it this, then I give you that info"

    • Recursive queries are possible

  • An Oracle queries require:

    • Data Source Type

      • URL

      • Wolfram Alpha (https://www.wolframalpha.com) (Retrieve an answer to question )

      • IPFS (retrive a file from an IPFS location)

      • Random byte

      • Computation (provide the result of arbitrary computation)

    • Query (to the Data Source type)

    • Authenticity Proof type (optional)

      • This proof enables to reduce the trust user must have in Oracle

From withing a SC, we can make queries to Oracle SC, passing data source and query to it. The Oracle SC then answer to the _fallback() function of the calling SC that can catch in variable the answer and perform update of its state variable. In the calling SC definition, we use contract Co2Level is usingOraclize {...} We can add proof of authenticity check

Additional Resources:

  1. Provable Things Documentation -> Very usefull to read!

Rhombus network

In the context of blockchain, oracles are an industry term for an information source that delivers real world data to smart contracts on a blockchain. Oracle providers must ensure truthfulness of the data provided:

  • First off, the real world data must be obtained from a trusted place. There is no blockchain system to guarantee data accuracy, so this data is often obtained from a trusted and verified source.

  • Second, the oracle should also provide proof that the data has not been tampered with, either by external parties, or the oracle service provider itself.

We will sample how this works thanks to a step by step sample code on how to incorporate real world data into your smart contracts with the Rhombus Lighthouse Delivery method -> https://github.com/RhombusNetwork/tutorial

Rhombus provides oracle data through a mechanism called a lighthouse. In simple terms, a lighthouse is an address where a single integer value that contains off-chain data is stored. Rhombus ensures the value is updated periodically, and can provide guarantees that the value is correct. -> https://rhombus.network

In this case, there is a random number generator lighthouse already deployed on Rinkeby, and you must access this lighthouse from your casino project smart contract (which will also be deployed on Rinkeby)

// include the Lighthouse Interface file ILighthouse.sol in the smart contract 
// file where you want to read data into, Gamble.sol.
import "../contracts/Ilighthouse.sol";

// Ilighthouse file contains the interface for a Lighthouse Object. 
// Now you can declare a global ILighthouse object in your Gamble.sol file
ILighthouse  public myLighthouse;

// Look at the https://github.com/RhombusNetwork/tutorial, tuto is well explained

Formal Verification

The goal is to make writing secure smart contracts as easy and accessible as possible. One route to this goal is via formal verification. From Wikipedia, formal verification is the act of proving or disproving the correctness of intended algorithms underlying a system with respect to a certain formal specification or property, using formal methods of mathematics. Said another way, it is a way to prove that a program is correct for all inputs. This can ensure that a hacker cannot modify the contract to an unintended state.

Formal verification is a method to prove a program correct for all inputs

MythX is also able to formally verify smart contracts. This guide gives more details on how to formally verify a smart contract in Remix using the MythX analysis API.

In Remix, go to the section plugin and search for Mythx, add it. Then in settings, add API and login /Password of your Mythx Account. Then you can run basic test.

Other usefull links:

Zero Knowledge Proof

First production on ZKP is from the 1980, some initial publications can be found on https://amturing.acm.org/award_winners/goldwasser_8627889.cfm

The goal of zero-knowledge proofs (ZKPs) is for a verifier to be able to convince themselves that a prover possesses knowledge of a secret parameter, called a witness, satisfying some relation, without revealing the witness to the verifier or anyone else.

Zero-knowledge proofs are currently being researched as solutions to two main issues in public blockchains:

  • Privacy: All essential information within a blockchain network must be public. ZKPs allow users to verify / prove certain elements of information (such as the answer to a crossword puzzle) while not revealing the information itself (to not give the answer to someone else). Certain tools, such as AZTEC, promise to use ZKPs to provide channels which shield the entire nature of certain transactions from the public blockchain while still using the public blockchain's security promises

  • Scalability: As blockchain networks grow, the amount of data they require to be maintained grows as well. ZKPs compress large amounts of data into compact, verifiable proofs, a characteristic scalability researchers hope to use to dramatically reduce the data required to maintain security on a public blockchain.From ZCash:“Succinct” zero-knowledge proofs (SNARKs, STARKs) can be verified within a few milliseconds, with a proof length of only a few hundred bytes even for statements about programs that are very large.

A common way Zero-Knowledge Proofs are used in blockchain is as follows. A Verifier will take the piece of data to be verified and put it through a private process (ZK-SNARK or STARK, for example) which produces a program called a Proof Circuit. This Proof Circuit can then be posted openly (such as in a Smart Contract) as it reveals no meaningful information about the nature of the data it represents. A Prover can then use the Proof Circuit to irrefutably prove they know the piece of data by publicly "solving" the Proof Circuit, a process that also reveals nothing about the nature of the data.

zk-SNARKs

Succinct Non-Interactive ARgument of Knowledge, or SNARKs, were first widely implemented on a blockchain by ZCash, a fork of Bitcoin. Ethereum introduced SNARK capacity with the Byzantium fork by including precompiled contracts that allowed efficient SNARK verification on-chain. SNARKs are being discussed in terms of privacy (as in ZCash) and scalability (as in SNARK roll-ups)

Other links:

zk-STARKs

Scalable and Transparent ARgument of Knowledge, or STARKs, share a similar fundamental ideas with SNARKs but differ in execution. Their development and advocacy is done chiefly by STARKware, a private company led by Eli Ben Sasson, with assistance from the Ethereum Foundation.

No trust setup and faster than SNARK

Other links:

AZTEC

Anonymous Zero-Knowledge Transactions with Efficient Communication, or AZTEC, is a way of executing confidential transactions on a public blockchain like Ethereum. The AZTEC Protocol is a series of op-codes (based on range proofs) embedded in smart contract libraries deployed by the AZTEC Protocol team (a private company acquired by ConsenSys) on Ethereum. Using a combination of these op-codes along with an off-chain Javascript library, the AZTEC Protocol team has developed a design pattern they call the "AZTEC Note," which allows for individuals to generate and transfer their own version of these notes on-chain without revealing internal transactions to external parties.

Other references:

ZKP Tutorials

More Resources

Etherum 2.0

Launch of Ethereum in 2015, First roadmpa https://blog.ethereum.org/2015/12/24/understanding-serenity-part-i-abstraction/

If we were to think of the Ethereum network as a house, launch of Ethereum mainnet was the building of the house and everyone moving in. Network forks are the patches, repairs and additional features to address challenges that arise from more people moving into the house. The upgrade to Ethereum 2.0 ("Serenity" on the roadmap) will be akin to building an entirely new foundation and house to accommodate the next generation of network growth.

Imagine the Ethereum 2.0 house being beside the Ethereum 1.x home. At first, there will be a path connecting the two. Ultimately, Ethereum 2.0 will expand to include Ethereum 1.x within it.

Ethereum 2.0 has three phases:

  • Phase 0 - Beacon Chain (Proof-of-Stake)

Phases 0 is the implementation of a Proof-of-Stake consensus mechanism for Ethereum. The blockchain secured by Phase 0's PoS mechanism is called the Beacon Chain. Miners for the Beacon Chain are called validators.

Phase 0 is the phase most public one now, as it's currently undergoing public testing and there are multiple companies developing software for it. Here is a list of the main validator clients:

  • Teku — Java-based client developed by ConsenSys Software

  • Prysm — Go-based client developed by Prysmatic Labs

  • Lighthouse — Rust-based client developed by Sigma Prime

  • Lodestar — Javascript-based client

  • Nimbus — Nim-based client developed by Status

The Medalla testnet was the first, public, multi-client testnet available. You can check out its progress here and join the testnet by following these steps.

  • Phase 1 - Shards (Data Availability and Coordination)

Database sharding is used in conventional computer programming to increases scalability of large systems. From this article. Ethereum 2.0 leverages traditional database sharding to decrease the amount of memory needed to maintain the full state of the network. Originally meant to be 1024 shards, the current spec will produce 64 database shards. Each of these shards will have their own validators. They will periodically check into the beacon chain using crosslinks, which is a summary of the state of that shard and the only representation of the shard on the Beacon Chain.

  • Phase 2 - Execution Environments (Computation)

The final phase of Ethereum 2.0 deals with the execution environments present on each shard. in Ethereum 1.x, the execution environment is the Ethereum Virtual Machine, a Turing-complete programming language that provides a universal computation environment for all in the network to use.

However, this universality comes at an efficiency cost. The EVM is slow compared to modern processing languages. Phase 2 addresses this processing cost by using a version of WebAssembly, a new type of code developed by Mozilla. It allows code written in C, C++ or Rust but executed in the browser to run at near-native speeds. (For more information about the implications of WebAssembly for the broader web, please see this video: "Rust, WebAssembly and the Future of Serverless")

In Phase 2, each shard will be allowed a unique execution environment. While at least one will be running the EVM (for sake of continuity), it's possible others will be running execution environments (EEs) for Libra, Bitcoin, or any other blockchain network.

Research is still developing in this area and the specs have not been frozen. If you'd like to try an experimental execution environment prototyping tool for Ethereum 2.0, please see Scout.

The Beacon Chain (that connect the shard chains):

In August 2020, the Ethereum 2.0 developers launched the Medalla testnet. It is the first multi-client public testnet for Ethereum 2.0 Beacon Chain. It's exciting because you can get involved to run a Beacon Chain client and stake test-Ether to mimic the actions of a validator. All of this is to test the clients and find major issues before the launch of the actual Beacon Chain.

Here's a tutorial walking through staking on the Medalla testnet using Ubuntu 20.04 on a Raspberry Pi

Here's a website created by Ethereum Fondation and ConsenSys to help people become Validators on the Medalla Beacon Chain.

Near the end of the course, we'll be doing a walkthrough of setting up and staking on the Medalla testnet using multiple clients.

Relevant URL:

Additional Topics

LLL (Low-level Lisp-like Language)

LLL has been deprecated in late 2019. Lisp Like Language (LLL) is a low level language similar to Assembly. It is meant to be very simple and minimalistic; essentially just a tiny wrapper over coding in EVM directly. LLL has direct access to storage and memory of the EVM so you can determine exactly where your data sits. When you use an EVM opcode in LLL, it translates directly to the bytecode representation of that opcode. In fact, all EVM opcodes are available to LLL.

Generally LLL binaries are about 30 to 40% smaller than the equivalent in Solidity. LLL also uses prefix notation, where operators are placed to the left of their operands.

Example implemenation of SC in LLL: https://github.com/benjaminion/LLL_erc20#erc20lll-an-implementation-of-ethereum-erc20-tokens-in-lll

The registry contract of the Ethereum Name service (ENS) was written in LLL mainly because of the gas savings due to its size and the brevity of its code: https://github.com/ensdomains/ens

Introduction to Vyper

Vyper is an experimental, contract-oriented, pythonic programming language that targets the Ethereum Virtual Machine. Beware, Vyper is not ready for production use cases.Vyper is not trying to be a replacement for Solidity. It is meant to be a more security focused smart contract programming language and will likely not be able to do everything that Solidity can.

Other references:

Writing SC in Vyper

Sample banking code in VIper: https://github.com/ConsenSys-Academy/simple-bank-vyper

You need to install the Vyper compiler yourself for Truffle to be able to compile Vyper contracts.

Installing Vyper on Linux is as simple as

sudo snap install vyper --edge --devmode

Otherwise, consult the docs.

Ethereum Improvement proposal (EIP)

Ethereum is an open source project with no one company or organization that controls the direction of the project. There is an open governance model where everyone is free to propose and discuss changes to the system. The EIP system is the forum to do this. You can view the EIP website here.

The explanation of the EIP Process: https://eips.ethereum.org/EIPS/eip-1

There are several different stages that an EIP can be in. Draft EIPs are works in progress, are open for consideration and discussed on GitHub. Accepted EIPs can be expected to be included in the next hard fork upgrade. You can see a list of the accepted EIPs for the Byzantium upgrade in EIP 609. Final EIPs are proposals that have already been adopted and deferred EIPs are not being considered for immediate adoption, but may be considered again in the future.

An EIP includes the following sections:

  • Preamble (metadata)

  • Simple summary

  • Abstract - A short description of the issue

  • Motivation - Why is the existing protocol inadequate

  • Specification - Describes the syntax and semantics of the new feature

  • Rationale - Why did you make these design decisions?

  • Backwards compatibility - Explain any backward incompatibilities and how they will be addressed

  • Test Cases - These are mandatory for EIPs proposing consensus changes

  • Implementation - What does an implementation of the EIP look like? This section must be completed before the EIP is given the status “Final”.

ERC= Ethereum request for Comment

The development of tokens on Ethereum is under active development. Explore these EIPs to follow the development of token standards.

Additional Resources:

Additional Tips and Tricks

A transaction hash on Ethereum is calculated as followed:

txhash = keccak256(signedTransaction)

So when you call a smartcontract method, you can not within that method access the transaction hash, because you need first to sign that transaction, this is the chicken and eggs problem. You can via a second transaction after access that info.

Last updated