Learning Note – Smart Contract Development Workflow

### Development Workflow

### beginning of development with a private blockchain
>> geth –dev console –ipcdisable

To revisit 5.1

### node, npm and nvm

often you will encounter incompatible version of node and npm, or you need different version of npm/node for different projects. nvm is the solution.
Mac/Linux
https://github.com/nvm-sh/nvm

Nvm doesn’t support Window. Below is a alternative to nvm in window
https://github.com/coreybutler/nvm-windows

Note:. Before installing the nvm-window, remember to remove the existing node installation

Uninstall Node.js and npm

Uninstall node.js and npm installed using Windows Installer (msi)

To uninstall node.js and npm, Open Windows “Control Panel” -> “Uninstall a program”, select Node.js and click on uninstall to uninstaill both node.js and npm

Cleanup directories

After the uninstall, look for the following folders and delete them if exists
* Nodejs installed directory
* npm and npm-cache directories from %appdata% directory

* npmrc directory from user home directory ( C:Users{User} )

>> nvm
### Install other version of node
>> nvm install 10.22.1
>> nvm list
>> nvm use 10.22.1

Note: to run npm in powershell, make sure you open the powershell in “administrative mode”

### Ganache

>> npm install -g ganache-cli

>>ganache-cli

Available Accounts
==================
(0) 0xa8b4D5578e74922473D43bd9B1BA2005C1697522 (100 ETH)
(1) 0xb5367e2d47Ec97eE54dAd67aE2665245441A2383 (100 ETH)
(2) 0x651411ABdD83Cd610Cd7C44f4C2F87D85107b9b6 (100 ETH)
(3) 0xF93806c963D8A2D751672E15B306bF90Bf20e586 (100 ETH)
(4) 0x113015caA33D0c32AD160F8bC2336f33F25f69bA (100 ETH)
(5) 0x885E2EC9ae39EEE506040525320139250bb6C6ab (100 ETH)
(6) 0x50561D6728B238a64dEe47ac3E701cC546CB2a7A (100 ETH)
(7) 0x1383c4848882A4d3c7681935Aed6d3cA2CC597aC (100 ETH)
(8) 0x751bf220aCF7A0A23a1c8106d5FF7bDdc1B75097 (100 ETH)
(9) 0x33A11A50f0fCA2f46283AAf870EFEF9C2d5ddc3d (100 ETH)

Private Keys
==================
(0) 0x3b1dca2f486d5b7b1c22ac08921fc2d5596e61efaab93561310b0aa0770850b0
(1) 0x17844954e498fa51d6be9dca83f19c6538514fe75439a8ebb8559e37436eb60a
(2) 0x3cab334e964b2e66daf013e60d29d7473702141eddf553aa88afc1c9698ed5ed
(3) 0x39cdf82ca0916a4e1bb4ef21838e88579cff75b6333065d7a4e38535fb8e594e
(4) 0x07e371aac4eb57c66fa3c6d4f861678332d0eaf2aeb7fb24afa92d3480c781db
(5) 0x839525ce112385f7e421b58614d4d2af3a77e860c8c9b35d86caa1d53cd69f9e
(6) 0xa280a67e15816ef6eef8d1ec1c47347902275c6ea57f26703ff3ff5f8716b585
(7) 0xa4953932cee5cf185bf670d888c447e1ed9abd258a2076c074d892db4e8a132a
(8) 0x5856942dbd530d4779c25532b0b21226bb109a832e9018668102e2f59970ce06
(9) 0xc369545eff1c03c9d5becd318519d9ef0dbdee79863e5bbde67aa77b75a5a736

HD Wallet
==================
Mnemonic: artwork visa parrot ethics obvious awkward embody slice stomach wrong kind print
Base HD Path: m/44’/60’/0’/0/{account_index}

Gas Price
==================
20000000000

Gas Limit
==================
6721975

Call Gas Limit
==================
9007199254740991

Listening on 127.0.0.1:8545

Note: you may encounter disabled running script issue in Window Powershell

Solution: In an administrative mode powershell,
>> set-executionpolicy remotesigned

Reference: https://www.faqforge.com/windows/windows-powershell-running-scripts-is-disabled-on-this-systeadm/

### Connect localhost with Remix.ethereum.org

### import accounts to Metamask and check if all 10 accounts have a balance of 100 ETH

Note: Import accounts with private key. Make sure your network is localhost

### Truffle Suite
documentation: http://truffleframework.com/

### Installing Truffle
>>npm install -g truffle

# should revisit 5.1 and 5.5

### Solidity Fundamentals
### Data Types and Variables
Statically typed

Elementary Types

  • Boolean
     
  • Integer
     
  • Address
     
  • Byte Arrays
     
  • Enums
     

Complex Types

  • Arrays

Structs

Mappings

### Functions

### The difference between storage and memory
### Contract Structure
### How to read smart contract
### Smart Contract Application Binary Interfaces (ABIs)
### Events and logs in Ethereum
Reference: https://solidity.readthedocs.io/en/latest/contracts.html#events

The common uses for events can be broken down into three main use cases:

  1. Events can provide smart contract return values for the User Interface
  2. They can act as asynchronous triggers with data and
  3. They can act a cheaper form of storage.

Events as Data Storage

Logs, which are essentially the same as events (the context dictates which term is more appropriate) can also be used as a cheaper form of storage. Logs cost 8 gas per byte whereas contract storage costs 20,000 per 32 bytes, or 625 gas per byte. Logs are cheaper, but also cannot be accessed from any contracts so their use case as storage objects is much more limited. Even still, logs can be useful for aggregating historical reference data.

### Factory Contracts
### A deep dive into the Remix IDE

Should revisit the entire chapter 6

### Truffle Pet Shop Tutorial
This tutorial will take you through the process of building your first dapp—an adoption tracking system for a pet shop!

ensure you have the development environment setup
>> truffle -v

### use truffle box (boiler plates) to create the project structure with user interface code
>> truffle unbox pet-shop

Note: understand more about Truffle Box boiler plates https://www.trufflesuite.com/boxes

Directory structure
The default Truffle directory structure contains the following:

  • contracts/: Contains the Solidity source files for our smart contracts. There is an important contract in here called Migrations.sol, which we’ll talk about later.
  • migrations/: Truffle uses a migration system to handle smart contract deployments. A migration is an additional special smart contract that keeps track of changes.
  • test/: Contains both JavaScript and Solidity tests for our smart contracts
  • truffle-config.js: Truffle configuration file

### Create a new contract name “Adoption.sol”

pragma solidity ^0.5.0;

contract Adoption {
address[16] public adopter;

// adopting a pet
function adopt(uint petId) public returns (uint) {
require(petId >= 0 && petId <= 15);

adopters[petId] = msg.sender;

return petId;
}

// retrieving the adopters
function getAdopters() public view returns (address[16] memory){
return adopters;
}

}

### compile contract
>> truffle compile

### Migrate the contract to blockchain
A migration is a deployment script meant to alter the state of your application’s contracts, moving it from one state to the next. For the first migration, you might just be deploying new code, but over time, other migrations might move data around or replace a contract with a new one.

### Create a new file name “2_deploy_contracts.js” under the “migration” folder
var Adoption = artifacts.require(“Adoption”);

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

### Launch ganache and migrate the contract to ganache private blockchain

>> truffle migrate

### Testing the smart contract using solidity
We start the contract off with 3 imports:

  • Assert.sol: Gives us various assertions to use in our tests. In testing, an assertion checks for things like equality, inequality or emptiness to return a pass/fail from our test. Here’s a full list of the assertions included with Truffle.
  • DeployedAddresses.sol: When running tests, Truffle will deploy a fresh instance of the contract being tested to the blockchain. This smart contract gets the address of the deployed contract.
  • Adoption: The smart contract we want to test.

Then we define three contract-wide variables:

  • First, one containing the smart contract to be tested, calling the DeployedAddresses smart contract to get its address.
  • Second, the id of the pet that will be used to test the adoption functions.
  • Third, since the TestAdoption contract will be sending the transaction, we set the expected adopter address to this, a contract-wide variable that gets the current contract’s address.

### Create a new file name “TestAdoption.sol” under “test” folder
pragma solidity ^0.5.0;

import “truffle/Assert.sol”;
import “truffle/DeployedAddresses.sol”;
import “../contracts/Adoption.sol”;

contract TestAdoption{
// The address of the adoption contract to be tested
Adoption adoption = Adoption(DeployedAddresses.Adoption());

// The id of the pet that will be used for testing
uint expectedPetId = 8;

//The expected owner of adopted pet is this contract
address expectedAdopter = address(this);

// Testing the adopt() function
function testUserCanAdoptPet() public {
uint returnedId = adoption.adopt(expectedPetId);

Assert.equal(returnedId, expectedPetId, “Adoption of the expected pet should match what is returned.”);
}

// Testing retrieval of a single pet’s owner
function testGetAdopterAddressByPetId() public {
address adopter = adoption.adopters(expectedPetId);

Assert.equal(adopter, expectedAdopter, “Owner of the expected pet should be this contract”);
}

// Testing retrieval of all pet owners
function testGetAdopterAddressByPetIdInArray() public {
// Store adopters in memory rather than contract’s storage
address[16] memory adopters = adoption.getAdopters();

Assert.equal(adopters[expectedPetId], expectedAdopter, “Owner of the expected pet should be this contract”);
}
}

### Run the test
>> truffle test

### Creating UI with smart contract
Modify the “app.js” file in the “/src/js” folder as below:
App = {
web3Provider: null,
contracts: {},

init: async function() {
// Load pets.
$.getJSON(‘../pets.json’, function(data) {
var petsRow = $(‘#petsRow’);
var petTemplate = $(‘#petTemplate’);

for (i = 0; i < data.length; i ++) {
petTemplate.find(‘.panel-title’).text(data[i].name);
petTemplate.find(‘img’).attr(‘src’, data[i].picture);
petTemplate.find(‘.pet-breed’).text(data[i].breed);
petTemplate.find(‘.pet-age’).text(data[i].age);
petTemplate.find(‘.pet-location’).text(data[i].location);
petTemplate.find(‘.btn-adopt’).attr(‘data-id’, data[i].id);

petsRow.append(petTemplate.html());
}
});

return await App.initWeb3();
},

initWeb3: async function() {
// Modern dapp browsers…
if (window.ethereum) {
App.web3Provider = window.ethereum;
try {
// Request account access
await window.ethereum.enable();
} catch (error) {
// User denied account access…
console.error(“User denied account access”)
}
}
// Legacy dapp browsers…
else if (window.web3) {
App.web3Provider = window.web3.currentProvider;
}
// If no injected web3 instance is detected, fall back to Ganache
else {
App.web3Provider = new Web3.providers.HttpProvider(‘http://localhost:7545‘);
}
web3 = new Web3(App.web3Provider);

return App.initContract();
},

initContract: function() {
$.getJSON(‘Adoption.json’, function(data) {
// Get the necessary contract artifact file and instantiate it with @truffle/contract
var AdoptionArtifact = data;
App.contracts.Adoption = TruffleContract(AdoptionArtifact);

// Set the provider for our contract
App.contracts.Adoption.setProvider(App.web3Provider);

// Use our contract to retrieve and mark the adopted pets
return App.markAdopted();
});

return App.bindEvents();
},

bindEvents: function() {
$(document).on(‘click’, ‘.btn-adopt’, App.handleAdopt);
},

markAdopted: function() {
var adoptionInstance;

App.contracts.Adoption.deployed().then(function(instance) {
adoptionInstance = instance;

return adoptionInstance.getAdopters.call();
}).then(function(adopters) {
for (i = 0; i < adopters.length; i++) {
if (adopters[i] !== ‘0x0000000000000000000000000000000000000000’) {
$(‘.panel-pet’).eq(i).find(‘button’).text(‘Success’).attr(‘disabled’, true);
}
}
}).catch(function(err) {
console.log(err.message);
});
},

handleAdopt: function(event) {
event.preventDefault();

var petId = parseInt($(event.target).data(‘id’));

var adoptionInstance;

web3.eth.getAccounts(function(error, accounts) {
if (error) {
console.log(error);
}

var account = accounts[0];

App.contracts.Adoption.deployed().then(function(instance) {
adoptionInstance = instance;

// Execute adopt as a transaction by sending account
return adoptionInstance.adopt(petId, {from: account});
}).then(function(result) {
return App.markAdopted();
}).catch(function(err) {
console.log(err.message);
});
});
}

};

$(function() {
$(window).load(function() {
App.init();
});
});

Note: Initiate Web3, Instantiating contract, getting the adopted pets and updating the UI, handling the adopt function,

### Interact with the dapp in browser

  1. Get the private key from Ganache
     
  2. Import ganache test account to MetaMask
  1. Add LocalHost 7545 Network to MetaMask

### Installing and configuring lite-server
Examine “bs-config.json” and “package.json” files
{
“server”: {
“baseDir”: [“./src”, “./build/contracts”]
}}

“scripts”: {
“dev”: “lite-server”,
“test”: “echo “Error: no test specified” && exit 1″},

### Starting the local web server
>> npm run dev

Pet Shop Dapp launched:


Related Posts