Thursday, July 28, 2022

 Onboarding TDD Solidity Typescript Hardhat Nextjs - Environment

https://mirror.xyz/simpledoers.eth/afu_BfjFwh2hzkElDu6WYPBKtLAjBlHKzOpoJ_z0E9A

Local Environment - Step by Step - Sample Code

TDD for blockchain to be tested, like:

Solidity Event

Solidity Modifiers - AccessControl and Constraints
Solidity Custom Errors
Solidity Errors PANIC Overflow
Solidity Smart Contracts Instance Value
Solidity Smart Contracts Storage vs Memory

I am learning how to use mirror.xyz to publish content,
in this case
while I am learning TDD Typescript Solidity
from online references MIT and open source,
but referencing them! ;)

#opensource #learning #environment #references #content #tdd #solidity #typescript #hardhat #nextjs #javascript #nodejs #linux #web3 #blockchain

Onboarding TDD Solidity Typescript Hardhat Nextjs - Environment


weekly59: 20220715

Onboarding TDD Solidity Typescript Hardhat Nextjs - Environment


Web 3 - First things First - Environment is one of the keys to do any staff In this case I use environment that can help me do the site that will use the contracts in next steps


step 1 - environment

  • Environment Tools Version
    • Solidity 0.8.9
    • Nextjs 12.2.2
    • Hardhat 2.10.1

step 1.1

npx creat-next-app blog4 --typescript

Web 3 - In this Next step I have some code that I will be transforming in next weeks I hope to have the Full Stack Sample


cd blog4
mkdir components
cp ../../weekly57/blog3/components/* components/
cp ../../weekly57/blog3/styles/* styles/
cp ../../weekly57/blog3/public/* public/

  • Nodejs version 18
    • @latest
    • Always start with the @latest
    • Or testing or beta, but not LTS ( is my personal opinion, break and fix or inform ;)
nvm install 18
nvm use 18
node --version
v18.3.0

Web 3, Solidity - In this step add HardHat the simplest way to local development in web 3 and Solidity


step 1.2

npm install -D hardhat
rm README.md tsconfig.json
  • tsconfig.json
    • take care module: esnext
{
  "compilerOptions": {
    "target": "es2020",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "skipLibCheck": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "noEmit": true,
    "esModuleInterop": true,
    "module": "esnext", 
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "jsx": "preserve",
    "incremental": true
  },
  "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
  "exclude": ["node_modules"]
}
  • [error] try to hardhat compile at once ... :o
    • But I mix create-next-app and hardhat creation
    • Step by step, baby steps
npx hardhat compile
Error HH13: Your Hardhat project uses typescript, but ts-node is not installed.
      
Please run: npm install --save-dev ts-node
  • [ok] fix install ts-node ;)
npm install -D ts-node
  • [error] try to hardhat compile ... :o
    • I am missing something, lets read the error and docs
 npx hardhat compile
An unexpected error occurred:

/home/debian/projects/weekly59/blog4/hardhat.config.ts:1
import "@nomicfoundation/hardhat-toolbox";
^^^^^^
  • [ok] install hardhat-toolbox dependencies ;)
npm install -D @nomicfoundation/hardhat-toolbox
  • [error]
    • HO, I have to read the error :()
npx hardhat compile
An unexpected error occurred:

/home/debian/projects/weekly59/blog4/hardhat.config.ts:1
import "@nomicfoundation/hardhat-toolbox";
^^^^^^

SyntaxError: Cannot use import statement outside a module
  • [ok]
    • Again! Thanks Nextjs Team and Hardhat Team
    • module: CommonJS
{
  "compilerOptions": {
    "target": "es2020",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "skipLibCheck": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "noEmit": true,
    "esModuleInterop": true,
    "module": "CommonJS",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "jsx": "preserve",
    "incremental": true
  },
  "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
  "exclude": ["node_modules"]
}
  • [ok] now hardhat compile ;)
npx hardhat compile
Generating typings for: 2 artifacts in dir: typechain-types for target: ethers-v5
Successfully generated 6 typings!
Compiled 2 Solidity files successfully

web 3, Solidity - Compile Environment Complete, lets continue with TDD


  • [ok] run hardhat test
    • Just before touching any solidity code
    • Hardhat team exelent work ;)
    • With Complete Sample :()
npx hardhat test
  Lock
    Deployment
      ✔ Should set the right unlockTime (1637ms)
      ✔ Should set the right owner
      ✔ Should receive and store the funds to lock
      ✔ Should fail if the unlockTime is not in the future (92ms)
    Withdrawals
      Validations
Unlock time is '1689365381' and block timestamp is '1657829383'
        ✔ Should revert with the right error if called too soon (81ms)
Unlock time is '1689365381' and block timestamp is '1689365382'
        ✔ Should revert with the right error if called from another account (99ms)
Unlock time is '1689365381' and block timestamp is '1689365382'
        ✔ Shouldn't fail if the unlockTime has arrived and the owner calls it (95ms)
      Events
Unlock time is '1689365381' and block timestamp is '1689365382'
        ✔ Should emit an event on withdrawals (81ms)
      Transfers
Unlock time is '1689365381' and block timestamp is '1689365382'
        ✔ Should transfer the funds to the owner (73ms)
  9 passing (2s)

web 3, Solidity, TDD - Test Environment Complete, lets write down TDD with Typescript Nextjs Solidity


step 2 - Start Solidity by TDD Typescript Hardhat


// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
import "hardhat/console.sol";
contract SafeMath {
  function testUnderflow() public pure returns (uint) {
    uint x = 0;
    x--; // check safe underflow ;)
    return x;
  }
  function testUncheckedUndeflow() public pure returns (int){
    int x = 0;
    unchecked { // out of safe check underflow :o
      x--; 
    }
    return x;
  }
}
  • test/SafeMath.ts solidity 0.8 overflow test PANIC ARITHMETIC OVERFLOW
    • I need to write code
    • Step by step
import { loadFixture } from "@nomicfoundation/hardhat-network-helpers";
import { PANIC_CODES } from "@nomicfoundation/hardhat-chai-matchers/panic";
import { expect } from "chai";
import { ethers } from "hardhat";
describe(`SafeMath`, () => {
  async function deployFixture() {
    // Contracts are deployed using the first signer/account by default
    const [owner, otherAccount] = await ethers.getSigners();
    const SafeMath = await ethers.getContractFactory("SafeMath");
    const safeMath = await SafeMath.deploy();
    return { safeMath, owner, otherAccount };
  }
  describe(`Deployment Safe Math`, () => {
    it(`Should check Underflow OK`, async () => {
      const { safeMath } = await loadFixture(deployFixture);
      // <https://hardhat.org/hardhat-chai-matchers/docs/reference#.revertedwithpanic>
      await expect(safeMath.testUnderflow()).to.be
      .revertedWithPanic( PANIC_CODES.ARITHMETIC_UNDER_OR_OVERFLOW ) ;
    });
    it(`Should NOT check or uncheck Underflow calculus error prone.`, async () => {
      const { safeMath } = await loadFixture(deployFixture);
      await expect(safeMath.testUncheckedUndeflow()).to.be.not.null;
    });
  });
});
  • [ok] test result
npx hardhat test
  SafeMath
    Deployment Safe Math
      ✔ Should check Underflow OK (101ms)
      ✔ Should NOT check or uncheck Underflow mmmm calculus error prone.

web 3, Solidity, TDD catch Custom Error with Typescript Nextjs from Solidity


step 2.2


  • contracts/VendingMachine.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
error VENDING_MACHINE__UNAUTORIZED(); // custom error
// <https://docs.soliditylang.org/en/v0.8.14/contracts.html#errors-and-the-revert-statement>
contract VendingMachine {
  address payable owner = payable(msg.sender);
  function withdraw() public {
    if(msg.sender == owner) // == instead of != only for testing purposes
      revert VENDING_MACHINE__UNAUTORIZED(); 
    owner.transfer( address(this).balance );
  }
}
  • tests/VendingMachine.ts
import { loadFixture } from "@nomicfoundation/hardhat-network-helpers";
import { expect } from "chai";
import { ethers } from "hardhat";
describe(`VendingMachine`, () => {
  async function deployFixture() {
    // Contracts are deployed using the first signer/account by default
    const [owner, otherAccount] = await ethers.getSigners();
    const VendingMachine = await ethers.getContractFactory("VendingMachine");
    const vendingMachine = await VendingMachine.deploy();
    return { vendingMachine, owner, otherAccount };
  }
  describe(`Deployment VendingMachine`, () => {
    it(`Should Custom Error OK`, async () => {
      const { vendingMachine, otherAccount } = await loadFixture(deployFixture);
      await expect(vendingMachine.withdraw()).to.be
         .revertedWithCustomError( vendingMachine,"VENDING_MACHINE__UNAUTORIZED" ) ;
      //<https://hardhat.org/hardhat-chai-matchers/docs/reference#.revertedwithcustomerror>
    });
  });
});
  • [ok]
    • Custom Error Code ( Naming Error Code ) :()
    • TDD Catch rejection ;)
  VendingMachine
    Deployment VendingMachine
      ✔ Should Custom Error OK (132ms)

web 3, Solidity, TDD passing parameter values - call functions with parameter values from Typescript Nextjs run Solidity and Catch Error when this parameter values make them


step 2.3

TDD [ok, rejected] function SafeMath solidity 0.8 default


  • contracts/FunctionIntro.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.9;
// import "hardhat/console.sol";
// function log( uint msg ){ // So we can have a function outside of Contract Instance, like.. tolling purpose reusable piece of soft ;)
//   console.log( msg );
// }
contract FunctionIntro {
  function add(uint x, uint y) external pure returns (uint) {
    return x + y;
  }
  function sub(uint x, uint y) external pure returns (uint) {
    return x - y;
  }
}
  • test/FunctionIntro.ts
import { loadFixture } from "@nomicfoundation/hardhat-network-helpers";
import { PANIC_CODES } from "@nomicfoundation/hardhat-chai-matchers/panic";
import { expect } from "chai";
import { ethers } from "hardhat";

describe(`FunctionIntro`, () => {
  async function deployFixture() {
    // Contracts are deployed using the first signer/account by default
    const [owner, otherAccount] = await ethers.getSigners();
    const FunctionIntro = await ethers.getContractFactory("FunctionIntro");
    const functionIntro = await FunctionIntro.deploy();
    return { functionIntro, owner, otherAccount };
  }
  describe(`Deployment FunctionIntro`, () => {
    it(`Should add a + b = c`, async () => {
      const { functionIntro } = await loadFixture(deployFixture);
      console.log( await functionIntro.add( 3, 7));
      expect( await functionIntro.add( 3, 7) ).to.be.equal(10); // look where the await is ;)
    });
    it(`Should sub Reject Arithmetic a - b = d`, async () => {
      const { functionIntro } = await loadFixture(deployFixture);
      await expect( functionIntro.sub( 3, 7) ).to.be
      .revertedWithPanic( PANIC_CODES.ARITHMETIC_UNDER_OR_OVERFLOW ); // look where the await is ;)
    });
    it(`Should sub a - b = d [ok]`, async () => {
      const { functionIntro } = await loadFixture(deployFixture);
      expect( await functionIntro.sub( 7, 5) ).to.be.equal(2); // look where the await is ;)
    });
  });
});
  • [ok] Verify tdd functions
    • change state
    • rejection status
  FunctionIntro
    Deployment FunctionIntro
BigNumber { value: "10" }
      ✔ Should add a + b = c (167ms)
      ✔ Should sub Reject Arithmetic a - b = d (38ms)
      ✔ Should sub a - b = d [ok]


web 3, Solidity, Instance State VariablesInstance of a Contract TDD change state variables - call functions that change instance state values from Typescript Nextjs run Solidity and catch Error when this parameter values make them


STEP 3 - Solidity Contract looking for storage memory instance Typescript TDD Hardhat


  • contracts/Counter.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.9;
contract Counter {
  uint public count;
  function inc() external {
    count += 1;
  }
  function dec() external {
    count -= 1;
  }
}
  • test/Counter.ts
import { loadFixture } from "@nomicfoundation/hardhat-network-helpers";
import { PANIC_CODES } from "@nomicfoundation/hardhat-chai-matchers/panic";
import { expect } from "chai";
import { ethers } from "hardhat";
describe(`Counter`, () => {
  async function deployFixture() {
    // Contracts are deployed using the first signer/account by default
    const [owner, otherAccount] = await ethers.getSigners();
    const Counter = await ethers.getContractFactory("Counter");
    const counter = await Counter.deploy();
    return { counter, owner, otherAccount };
  }
  describe(`Deployment Counter`, () => {
    it(`Should increment storage data in contract Counter`, async () => {
      const { counter } = await loadFixture(deployFixture); // Give an instance 1 of the contract counter
      expect( await counter.count() ).to.be.equal(0);
      expect( await counter.inc() ).to.be.not.reverted; // look where the await is ;)
      expect( await counter.count() ).to.be.equal(1);
    });
    it(`Should decrement storage data in counter to 0 [ok]`, async () => {
      const { counter } = await loadFixture(deployFixture); // Give an instance 2 of the contract counter
      expect( await counter.count() ).to.be.equal(0);
      expect( await counter.inc() ).to.be.not.reverted; // look where the await is ;)
      expect( await counter.count() ).to.be.equal(1);
      expect( await counter.dec() ).to.be.not.reverted; // look where the await is ;)
      expect( await counter.count() ).to.be.equal(0);
    });
    it(`Should decrement Reject Arithmetic Underflow `, async () => {
      const { counter } = await loadFixture(deployFixture); // Give an instance 3 of the contract counter
      await expect( counter.dec() ).to.be
      .revertedWithPanic( PANIC_CODES.ARITHMETIC_UNDER_OR_OVERFLOW ); // look where the await is ;)
    });
  });
});
  • [ok] Result
    • Instance variable in blockchain contract
    • Change state variable in blockchain
    • Catch rejection of SafeMath Underflow
npx hardhat test

  Counter
    Deployment Counter
       Should increment storage data in contract Counter (1424ms)
       Should decrement storage data in counter to 0 [ok] (95ms)
       Should decrement Reject Arithmetic Underflow  (47ms)

  FunctionIntro
    Deployment FunctionIntro
BigNumber { value: "10" }
       Should add a + b = c (167ms)
       Should sub Reject Arithmetic a - b = d (38ms)
       Should sub a - b = d [ok]

  Lock
    Deployment
       Should set the right unlockTime (166ms)
       Should set the right owner (48ms)
       Should receive and store the funds to lock
       Should fail if the unlockTime is not in the future (85ms)
    Withdrawals
      Validations
Unlock time is '1689450825' and block timestamp is '1657914827'
         Should revert with the right error if called too soon (59ms)
Unlock time is '1689450825' and block timestamp is '1689450826'
         Should revert with the right error if called from another account (75ms)
Unlock time is '1689450825' and block timestamp is '1689450826'
         Shouldn't fail if the unlockTime has arrived and the owner calls it (80ms)
      Events
Unlock time is '1689450825' and block timestamp is '1689450826'
        ✔ Should emit an event on withdrawals (57ms)
      Transfers
Unlock time is '1689450825' and block timestamp is '1689450826'
        ✔ Should transfer the funds to the owner (70ms)

  SafeMath
    Deployment Safe Math
      ✔ Should check Underflow OK (140ms)
      ✔ Should NOT check or uncheck Underflow calculus error prone.

  VendingMachine
    Deployment VendingMachine
      ✔ Should Custom Error OK (132ms)

  18 passing (3s)


web 3, Solidity, Modifiers TDD add/extract/refactor restrictions/guards/validations - call functions that change instance state values from Typescript Nextjs run Solidity and catch Error when this conditions din't pass the guard/validation/restriction/constraint


STEP 4 - Solidity Modifier TDD Typescript Hardhat


  • Solidity Modifiers

    • Always check isolated code
    • When there is an error or problem, isolate the problem
  • contracts/FunctionModifier.sol

// SPDX-License-Identifier: MIT
pragma solidity 0.8.9;
contract FunctionModifier {
  bool public paused;
  uint public count;
  function setPaused(bool _paused) external {
    paused = _paused;
  }
  modifier whenNotPaused(){
    require(!paused, "Paused");
    _;
  }
  function inc() external whenNotPaused {
    count += 1;
  }
  function dec() external whenNotPaused {
    count -= 1;
  }
  modifier cap(uint _x){
    require(_x < 100, "x >= 100");
    // execute some code Before the main thread
    _; // execute back the main thread
    // execute some code After the main thread ( sandwich )
  }
  function incBy(uint _x) external whenNotPaused cap(_x) {
    count += _x;
  }
}
  • test/FunctionModifier.ts
import { loadFixture } from "@nomicfoundation/hardhat-network-helpers";
import { PANIC_CODES } from "@nomicfoundation/hardhat-chai-matchers/panic";
import { expect } from "chai";
import { ethers } from "hardhat";
describe(`FunctionModifier`, () => {
  async function deployFixture() {
    // Contracts are deployed using the first signer/account by default
    const [owner, otherAccount] = await ethers.getSigners();
    const FunctionModifier = await ethers.getContractFactory("FunctionModifier");
    const functionModifier = await FunctionModifier.deploy();
    return { functionModifier, owner, otherAccount };
  }
  describe(`Deployment FunctionModifier`, () => {
    it(`Should increment`, async () => {
      const { functionModifier } = await loadFixture(deployFixture); // Give an instance 1 of the contract counter
      expect( await functionModifier.count() ).to.be.equal(0);
      expect( await functionModifier.inc() ).to.be.not.reverted; // look where the await is ;)
      expect( await functionModifier.count() ).to.be.equal(1);
    });
    it(`Should decrement`, async () => {
      const { functionModifier } = await loadFixture(deployFixture); // Give an instance 1 of the contract counter
      expect( await functionModifier.count() ).to.be.equal(0);
      expect( await functionModifier.inc() ).to.be.not.reverted; // look where the await is ;)
      expect( await functionModifier.count() ).to.be.equal(1);
      expect( await functionModifier.dec() ).to.be.not.reverted; // look where the await is ;)
      expect( await functionModifier.count() ).to.be.equal(0);
    });
    it(`Should Revert "Paused" - modifier `, async () => {
      const { functionModifier } = await loadFixture(deployFixture); // Give an instance 3 of the contract counter
      await functionModifier.setPaused(true);
      await expect( functionModifier.dec() ).to.be
      .revertedWith( "Paused" ); // look where the await is ;)
    });
    it(`Should Revert "x >= 100" - modifier `, async () => {
      const { functionModifier } = await loadFixture(deployFixture); // Give an instance 3 of the contract counter
      await expect( functionModifier.incBy(200) ).to.be
      .revertedWith( "x >= 100" ); // look where the await is ;)
    });
  });
});
  • [ok] Result - TDD modifier catch rejection revert assert
npx hardhat test test/FunctionModifier.ts

  FunctionModifier
    Deployment FunctionModifier
      ✔ Should increment (1515ms)
      ✔ Should decrement (164ms)
      ✔ Should Revert "Paused" - modifier  (97ms)
      ✔ Should Revert "x >= 100" - modifier  (48ms)

  4 passing (2s)

STEP 5 - Solidity constructor() TDD Typescript Hardhat


Block and Transaction Properties
    blockhash(uint blockNumber) returns (bytes32): hash of the given block when blocknumber is one of the 256 most recent blocks; otherwise returns zero
    block.basefee (uint): current block’s base fee (EIP-3198 and EIP-1559)
    block.chainid (uint): current chain id
    block.coinbase (address payable): current block miner’s address
    block.difficulty (uint): current block difficulty
    block.gaslimit (uint): current block gaslimit
    block.number (uint): current block number
    block.timestamp (uint): current block timestamp as seconds since unix epoch
    gasleft() returns (uint256): remaining gas
    msg.data (bytes calldata): complete calldata
    msg.sender (address): sender of the message (current call)
    msg.sig (bytes4): first four bytes of the calldata (i.e. function identifier)
    msg.value (uint): number of wei sent with the message
    tx.gasprice (uint): gas price of the transaction
    tx.origin (address): sender of the transaction (full call chain)
  • [error] ok I forget to add a parameter value to call the constructor( _ )
npx hardhat test test/Constructor
An unexpected error occurred:

test/Constructor.ts:12:43 - error TS2554: Expected 1-2 arguments, but got 0.

12     const constructor = await Constructor.deploy();
                                             ~~~~~~~~
  typechain-types/factories/Constructor__factory.ts:77:5
    77     _x: PromiseOrValue<BigNumberish>,
           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    An argument for '_x' was not provided.
  • [ok] first test Constructor solidity contract, hardhat tdd typescript console log
    • contracts/Constructor.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.9;
// Import this file to use console.log
import "hardhat/console.sol";
contract Constructor { // this name Constuctor can be contract Banana but methods **constructor()** is reserved for any contractName as **constructor**
  address public owner;
  uint public x;
  constructor(uint _x){
    owner = msg.sender;
    x = _x;
    console.log("msg.sender:  %s", msg.sender);
    //console.log("msg.value: %o", msg.value); // only on payable constructor
    //console.log("msg.sig:   %o", msg.sig); 
    //console.log("msg.data:  %o", msg.data);
  }
}
  • test/Constructor.ts
import { loadFixture } from "@nomicfoundation/hardhat-network-helpers";
import { expect } from "chai";
import { ethers } from "hardhat";
const TEST_UINT_OK = 123;
describe(`Constructor`, () => {
  async function deployFixture() {
    // Contracts are deployed using the first signer/account by default
    const [owner, otherAccount] = await ethers.getSigners();
    const Constructor = await ethers.getContractFactory("Constructor");
    const constructor = await Constructor.deploy(TEST_UINT_OK);
    return { constructor, owner, otherAccount };
  }
  describe(`Deployment Constructor`, () => {
    it(`Should call Constructor, and show some debug info as tutorial weekly59`, async () => {
      const { constructor } = await loadFixture(deployFixture); // Give one **Constructor** instance.
      expect( await constructor.x() ).to.be.equal(TEST_UINT_OK);
    });
  });
});
  • [ok] Result, First call test Constructor console log msg sender
npx hardhat test test/Constructor
  Constructor
    Deployment Constructor
msg.sender:  0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266
      ✔ Should call Constructor, and show some debug info as tutorial weekly59 (1650ms)
  1 passing (2s)
  • contracts/Constructor.sol more log tx block msg
// SPDX-License-Identifier: MIT
pragma solidity 0.8.9;
// Import this file to use console.log
import "hardhat/console.sol";
contract Constructor { // this name Constuctor can be contract Banana but methods **constructor()** is reserved for any contractName as **constructor**
  address public owner;
  uint public x;
  constructor(uint _x){
    owner = msg.sender;
    x = _x;
    console.log("msg.sender:  %s", msg.sender);
    console.log("tx.origin:  %s", tx.origin);
    console.log("tx.gasprice:  %s", tx.gasprice);
    console.log("block.timestamp:  %s", block.timestamp);
    console.log("block.number:  %s", block.number);
    console.log("block.chainid:  %s", block.chainid);
  }
}
  • [ok] Result more Constructor.sol more log tx block msg
npx hardhat test test/Constructor
Compiled 1 Solidity file successfully
  Constructor
    Deployment Constructor
msg.sender:  0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266
tx.origin:  0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266
tx.gasprice:  1875000000
block.timestamp:  1658154619
block.number:  1
block.chainid:  31337
      ✔ Should call Constructor, and show some debug info as tutorial weekly59 (1839ms)
  1 passing (2s)

STEP 5 - Solidity state global error TDD Typescript Hardhat


  • contracts/Ownable.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.9;
error OWNABLE__NOT_OWNER();
error OWNABLE__INVALID_ADDRESS();
contract Ownable {
  address public owner;
  constructor(){
    owner = msg.sender;
  }
  modifier onlyOwner() {
    if ( msg.sender != owner ) {
      revert OWNABLE__NOT_OWNER();
    }
    _; // continue execution of main thread
  }
  function setOwner(address _newOwner) external onlyOwner {
    if( _newOwner ==  owner ){ //address(0) ){
      revert OWNABLE__INVALID_ADDRESS();
    }
    owner = _newOwner;
  }
  function onlyOwnerCanCallThisFunction() external onlyOwner {    
  }
  function anyOneCanCallThisFunction() external {
  }
}
  • test/Ownable.ts
import { loadFixture } from "@nomicfoundation/hardhat-network-helpers";
import { expect, assert } from "chai";
import { ethers } from "hardhat";
describe(`Ownable`, () => {
  async function deployFixture() {
    // Contracts are deployed using the first signer/account by default
    const [owner, otherAccount] = await ethers.getSigners();
    const Ownable = await ethers.getContractFactory("Ownable");
    const ownable = await Ownable.deploy();
    return { ownable, owner, otherAccount };
  }
  describe(`Deployment Ownable`, () => {
    it(`Should owner call ok`, async () => {
      const { ownable } = await loadFixture(deployFixture); // Give an instance 1 of the contract counter
      expect( await ownable.onlyOwnerCanCallThisFunction() ).ok;
    });
    it(`Should not owner call rejected`, async () => {
      const { ownable, otherAccount } = await loadFixture(deployFixture); // Give an instance 1 of the contract counter
      expect( await ownable.setOwner(otherAccount.address) ).to.be.ok;      
      try{
        await ownable.onlyOwnerCanCallThisFunction();
        assert.fail();        
      }catch(err){
        assert.isOk;
      }
    });
    it(`Should not owner call ok`, async () => {
      const { ownable, otherAccount } = await loadFixture(deployFixture); // Give an instance 1 of the contract counter
      expect( await ownable.setOwner(otherAccount.address) ).to.be.ok;
      await expect( ownable.anyOneCanCallThisFunction() ).to.be.ok;
    });
    it(`Should reject same owner not change ownership rejected`, async () => {
      const { ownable, owner ,otherAccount } = await loadFixture(deployFixture); // Give an instance 1 of the contract counter
      try{
        await ownable.setOwner(owner.getAddress());
        assert.fail();        
      }catch(err){
        assert.isOk;
      }
    });
  });
});
  • [ok] Result Ownable
npx hardhat test test/Ownable
  Ownable
    Deployment Ownable
      ✔ Should owner call ok (1536ms)
      ✔ Should not owner call rejected (128ms)
      ✔ Should not owner call ok (48ms)
      ✔ Should reject same owner not change ownership rejected (80ms)
  4 passing (2s)

  • Struct - Object - Array - no execution code
// SPDX-License-Identifier: MIT
pragma solidity 0.8.9;
contract Structs {
  struct Car {
    string model;
    uint year;
    address owner;
  }
  Car public car;
  Car[] public cars;
  mapping( address => Car[] ) public carsByOwner;
  function examples() external  {
    Car memory toyota = Car("Toyota", 2020, msg.sender); // memory execution of the funcion
    Car memory lambo =  Car({ model: "Lamborghini", year: 2020, owner: msg.sender});
    // memory - ReadOnly data
    // storage - Write data in storage/blockchain state
    cars.push(toyota);
    cars.push(lambo);
    carsByOwner[msg.sender] = cars;
  }
  function exampleCallable(uint[] calldata y) external pure { // calldata by reference, memory by value
    _internal(y);
  }
  function _internal(uint[] memory _y) private pure {
    uint x = _y[0];
    x;
  }
}

web 3, Solidity, Events Emit TDD emit Soliditi event, and Receive in Typescript the Event


STEP 6 - Solidity event emit TDD Typescript Hardhat


  • Events
    • contracts/Events.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.9;
contract Events {
  event Log(string message, uint val);
  function example() external { // is a transactional function, not readonly or view because of the emit data
    emit Log("Thing the best of Others", 123456);
  }
}
  • test/Events.ts
import { loadFixture } from "@nomicfoundation/hardhat-network-helpers";
import { expect, assert } from "chai";
import { ethers } from "hardhat";
describe(`Events`, () => {
  async function deployFixture() {
    const [owner, otherAccount] = await ethers.getSigners();
    const Events = await ethers.getContractFactory("Events");
    const events = await Events.deploy();
    return { events, owner, otherAccount };
  }
  describe(`Deployment Events`, () => {
    it(`Should check Event emit Log`, async () => {
      const { events } = await loadFixture(deployFixture);
      await expect( events.example() ).to.be.emit(events, "Log"); // Goooood ;)
      assert.isOk;
    });
  });
});
  • [ok] Result
npx hardhat test test/Events
  Events
    Deployment Events
      ✔ Should  (1428ms)
  1 passing (1s)

  • constract/Inheritance.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.9;
contract Dad {
  function speak() public pure virtual returns (string memory){
    return "Sempre Avanti!";
  }
}
contract Son is Dad {
  function speak() public pure virtual override returns (string memory){
    return "Always Moving Forward!";
  }
}
contract Nipote is Son {
  function speak() public pure override returns (string memory){
    return "Siempre Hacia Adelante!";
  }
}


{

name: "Maximiliano Usich",

email: "maximilianou@gmail.com",

role: ["Husband", "Dad", "Software Developer", "Mentor", "Full Stack nodejs Javascript Typescript"],

github: https://github.com/maximilianou,

wallet: <simpledoers.eth>,

eth: ipns://simpledoers.eth,

eth-https: https://simpledoers.eth.limo,

codersrank: https://profile.codersrank.io/user/maximilianou,

aCryptoCoffe: https://www.buymeacryptocoffee.xyz/simpledoers.eth,

aCoffe: https://www.buymeacoffee.com/simpledoers,

mirror: https://mirror.xyz/simpledoers.eth/subscribe,

twitter: https://twitter.com/maximilianou,

indeed: https://my.indeed.com/p/maximilianou-s1rmnyx,

linkedin: https://www.linkedin.com/in/maximilianou/

}