Coder Social home page Coder Social logo

solidity-essentials's Introduction

Solidity-Baby Steps...

The best path for earning Solidity is to approach examples.

Here are baby examples. Typing one-by-one is recommended.

References:

MyBlog explanation for using EnglishAuction and testing.

examples

videoResource

Example-1

updateBalance & getBalance

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.0 <0.9.0;

contract MyLedger {
    mapping(address => uint) public balances;
    function updateBalance(uint newBal) public {
        balances[msg.sender] = newBal;
    }
    function getBalance() public view returns(uint) {
        return balances[msg.sender];
    }
}

Example-2

Inherited from Square

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.0 <0.9.0;

import "@openzeppelin/contracts/utils/Strings.sol";

contract Shape {
    uint height;
    uint width;

    constructor(uint _height, uint _width) {
        height = _height;
        width = _width;
    }
}

contract Square is Shape {
    constructor(uint h, uint w) Shape(h, w) {}

    function getHeight() public view returns(uint) {
        return height;
    }

    function getArea() public view returns(uint) {
        return height * width;
    }
}

When using Remix, assigning height and width is needed before deployment.

Example-3

Owner & Purchase

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.0 <0.9.0;

import "@openzeppelin/contracts/utils/Strings.sol";

contract Owner {
    address owner;
    constructor() {owner = msg.sender;}

    modifier onlyOwner {
        require(msg.sender == owner);
        _;
    }
}

contract Purchase is Owner {
    mapping(address => bool) purchasers;
    uint price;
    constructor(uint _price) {
        price = _price;
    }

    function purchase() public payable {
        purchasers[msg.sender] = true;
    }

    function setPrice(uint _price) public onlyOwner {
        price = _price;
    }
    function getPrice() public view returns(uint) {
        return price;
    }
}

Example-4

Simple Storage for unsigned integer and string data type

// SPDX-License-Identifier:GPL-3.0

pragma solidity >=0.4.16 < 0.9.0;

contract SimpleStorage {
    uint storedData;

    function setData(uint x) public {
        storedData = x;
    }
    function getData() public view returns (uint) {
        return storedData;
    }
}


contract StringStorage {
    string storedString;

    function setString(string memory x) public {
        storedString = x;
    }
    function getString() public view returns (string memory) {
        return storedString;
    }
}

Example-5

Struct example

 // SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.0 <0.9.0; 

contract Storage1{
    
    struct People {
        string name;
        uint256 favoriteNumber;

    }

    People[] public people;
    mapping(string => uint256) public nameToFavoriteNumber;

    function appPerson(string memory _name, uint256 _favoriteNumber) public {
        people.push(People(_name, _favoriteNumber));
        nameToFavoriteNumber[_name] = _favoriteNumber;
    }

}

Example-6

Smart Funding...

// SPDX-License-Identifier: GPL-3.0

pragma solidity ^0.8.7;

contract CryptoKids {
    // owner DAD
    address owner;

    event LogKidFundingReceived(address addr, uint amount, uint contractBalance);

    constructor() {
        owner = msg.sender;
    }
    // define Kid
    struct Kid {
        address payable walletAddress;
        string firstName;
        string lastName;
        uint releaseTime;
        uint amount;
        bool canWithdraw;
    }

    // add kid to contract
    Kid[] public kids;

    modifier onlyOwner() {
        require(msg.sender == owner, "Only the owner can add kids");
        _;
    }

    function addKid(address payable walletAddress, string memory firstName, string memory lastName, uint releaseTime, uint amount, bool canWithdraw) public onlyOwner {
        kids.push(Kid(
            walletAddress, 
            firstName,
            lastName,
            releaseTime,
            amount,
            canWithdraw
        ));
    }

    function balanceOf() public view returns(uint) {
        return address(this).balance;
    }

    // deposit funds to contract, especially to a kid's account
    function deposit(address walletAddress) payable public {
        addToKidsBalance(walletAddress);
    }
    function addToKidsBalance(address walletAddress) private {
        for(uint i = 0; i < kids.length; i++) {
            if(kids[i].walletAddress == walletAddress) {
                kids[i].amount += msg.value;
                emit LogKidFundingReceived(walletAddress, msg.value, balanceOf());
            }
        }
    }

    function getIndex(address walletAddress) view private returns(uint) {
        for(uint i = 0; i < kids.length; i++) {
            if (kids[i].walletAddress == walletAddress) {
                return i;
            }
        }
        return 999;
    }
    // kid checks if able to withdraw
    function availableToWithdraw(address walletAddress) public returns(bool) {
        uint i = getIndex(walletAddress);
        if (block.timestamp > kids[i].releaseTime) {
            kids[i].canWithdraw = true;
            return true;
        }
        return false;
    }

    // withdraw the money
    function withdraw(address walletAddress) payable public {
        uint i = getIndex(walletAddress);
        require(msg.sender == kids[i].walletAddress, "You must be the very kid to withdraw ETH");
        require(kids[i].canWithdraw == true, "You are not be able to withdraw at this time");
        kids[i].walletAddress.transfer(kids[i].amount);
    }
}

Example-7

// SPDX-License-Identifier: GPL-3.0

pragma solidity ^0.8.7;

contract Greeter {

    address creator;
    string greeting;

    constructor(string memory _greeting) {
        creator = msg.sender;
        greeting = _greeting;
    }

    function greet() public view returns (string memory) {
        return greeting;
    }

    function getBlockNumber() public view returns (uint) {
        return block.number;
    }

    function setGreeting(string memory _newgreeting) public {
        greeting = _newgreeting;
    }
    function getCreatorBalance() public view returns (uint) {
        return creator.balance;
    }
}

Example-8

basic messages

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.0 <0.9.0;
contract basicInfoGetter {

    address creator;

    constructor()
    {
        creator = msg.sender; 								    
    }
	
	function minerAddress() public view returns (address) // get CURRENT block miner's address, 
	{														     // not necessarily the address of the miner when this block was born
		return block.coinbase;
	}
	
	function difficulty() public view returns (uint)
	{
		return block.difficulty;
	}
	
	function gaslimit() public view returns (uint)  // the most gas that can be spent on any given transaction right now
	{													  
		return block.gaslimit;
	}
	
	function blockNumber() public view returns (uint)
	{
		return block.number;
	}
    
    function timestamp() public view returns (uint) // returns current block timestamp in SECONDS (not ms) from epoch
    {													
    	return block.timestamp; 						 // also "now" == "block.timestamp", as in "return now;"
    }
    
    function msgData() public pure returns (bytes memory) 		// The data of a call to this function. Always returns "0xc8e7ca2e" for me.
    {										            // adding an input parameter would probably change it with each diff call?
    	return msg.data;
    }
    
    function msgSender() public view returns (address)  // Returns the address of whomever made this call
    {													// (i.e. not necessarily the creator of the contract)
    	return msg.sender;
    }
    
    function msgValue() public payable returns (uint)		// returns amt of wei sent with this call
    {
    	return msg.value;
    }

    // check balance of the contract
    function balanceOf() public view returns(uint) {
        return address(this).balance / 1e18;
    }

    
    /***  A note about gas and gasprice:
     
     Every transaction must specify a quantity of "gas" that it is willing to consume (called startgas), 
     and the fee that it is willing to pay per unit gas (gasprice). At the start of execution, 
     startgas * gasprice ether are removed from the transaction sender's account. 
     Whatever is not used is immediately refunded.
     
     */
    
    function msgGas() public view returns (uint)        
    {													
    	return gasleft();
    }
    
	function txGasprice() public view returns (uint) 	// "gasprice" is the amount of gas the sender was *willing* to pay. 50000000 for me. (geth default)
    {											     	
    	return tx.gasprice;
    }
    
    function txOrigin() public view returns (address) 	// returns sender of the transaction
    {											   		// What if there is a chain of calls? I think it returns the first sender, whoever provided the gas.
    	return tx.origin;
    }
    
	function contractAddress() public view returns (address) 
	{
		return creator;
	}
    
    function contractBalance() public view returns (uint) 
    {
    	return creator.balance;
    }
    
}

Example-9

increment: transactions will be made when clicking 'increment'; result will be shown when clicking 'getIterration'.

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.0 <0.9.0;
contract Increment {
    address creator;
    uint iteration;

    constructor() {
        creator = msg.sender;
        iteration = 0;
    }

    function increment() public {
        iteration += 1;
    }

    function getIteration() public view returns (uint) {
        return iteration;
    }

    function ContractBal() public view returns (uint) {
        return address(this).balance / 1e18;
    }

    function getBalance() public view returns (uint) {
        return creator.balance / 1e18;
    }

    function valueToContract() public payable returns (uint) {
        return msg.value;
    }
}

Example-10

increment2: increment (integer) number can be defined by user

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.0 <0.9.0;

contract Increment2 {
    address owner;
    int iteration;
    string whatHappened;

    constructor() {
        owner = msg.sender;
        iteration = 0;
        whatHappened = "constructor executed";
    }

    function increment(int howmuch) public {
        if (howmuch == 0) {
            iteration += 1;
            whatHappened = "howmuch was 0. Incremented by 1.";
        }
        else {
            iteration += howmuch;
            whatHappened = "howmuch was not 0. Incremented by its value";
        }
        return;
    }

    function getWhatHappened() public view returns (string memory) {
        return whatHappened;
    }

    function getIteration() public view returns (int) {
        return iteration;
    }
}

Example-11

Increment3

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.0 <0.9.0;

contract Increment3 {
    address creator;
    int iteration;
    string whatHappened;
    int customValue;

    constructor () {
        creator = msg.sender;
        iteration = 0;
        whatHappened = "constructor initialized";
    }

    function increment(int incre, int _customValue) public {
        customValue = _customValue;
        if (incre == 0) {
            iteration += 1;
            whatHappened = "Increment was 0, Incremented by 1. Custom Value also set";
        }
        else {
            iteration += incre;
            whatHappened = "Increment was not 0, Incremented by its value. Custom Value also set";
        }
        return;
    }

    function getInfo() public view returns (string memory) {
        return whatHappened;
    }
    function getTotalIter() public view returns (int) {
        return iteration;
    }
}

Example-12

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.0 <0.9.0;

contract msgExaminer {
    address  owner;
    address  miner;

    constructor() {
         owner = msg.sender;
         miner = 0x8945A1288dc78A6D8952a92C77aEe6730B414778;
    }

    function getMsgData() public pure returns (bytes calldata) {
        return msg.data;
    }
    function getMsgGas() public view returns (uint) {
        return gasleft();
    }
    function getMsgVal() public payable returns (uint) {
        return msg.value;
    }

    	function txGasprice() public view returns (uint) 	// "gasprice" is the amount of gas the sender was *willing* to pay. 50000000 for me. (geth default)
    {											     	
    	return tx.gasprice;
    }
    
    function txOrigin() public view returns (address) 	// returns sender of the transaction
    {											   		// What if there is a chain of calls? I think it returns the first sender, whoever provided the gas.
    	return tx.origin;
    }

    function minerAddress() public view returns (address) // get CURRENT block miner's address, 
	{														     // not necessarily the address of the miner when this block was born
		return block.coinbase;
	}

}

Example-13

Add, update and delete student struct type

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

contract StudentList {
    struct Student{
        string name;
        uint age;
    }
    // 
    Student[] public students;

    function create(string memory _name, uint _age) public {
        students.push(Student({name:_name, age:_age}));
    }

    function updateAge(uint _index, uint _age) public {
        Student storage studentUpdate = students[_index];
        studentUpdate.age = _age;
    }

    function updateName(uint _index, string memory _name) public {
        Student storage studentUpdate = students[_index];
        studentUpdate.name = _name;
    }

    function getLength() public view returns (uint) {
        return students.length;
    }


    function getStudent(uint _index) public view returns (string memory name, uint age)  {
            Student storage studentEvr = students[_index];
            return (studentEvr.name, studentEvr.age);
    }


    // delete the last student
    function delLast() public {
        students.pop();
    }

    // delete student using index
    function remove(uint _index) public {
        require(_index < students.length, "index out of bound");
        for (uint i = _index; i < students.length - 1; i++) {
            students[i] = students[i + 1];
        }
        students.pop();
    }
}

Example-14

Balance

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

contract Account {
    uint public balance;
    uint public constant MAX_UINT = 2**256 - 1;

    function deposit(uint _amount) public {
        uint oldBalance = balance;
        uint newBalance = balance + _amount;

        // balance + _amount does not overflow if balance + _amount >= balance
        require(newBalance >= oldBalance, "Overflow");

        balance = newBalance;

        assert(balance >= oldBalance);
    }

    function withdraw(uint _amount) public {
        uint oldBalance = balance;

        // balance - _amount does not underflow if balance >= _amount
        require(balance >= _amount, "Underflow");

        if (balance < _amount) {
            revert("Underflow");
        }

        balance -= _amount;

        assert(balance <= oldBalance);
    }
}

Example-15

ETH transfer example

Reference : HERE

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

contract Payable {
    address payable public owner;

    constructor() payable {
        owner = payable(msg.sender);
    }
    // Function to deposit Ether into this contract.
    // Call this function along with some Ether.
    // The balance of this contract will be automatically updated.
    function deposit() public payable {}

    // check the balance of current contract
    function balContract() public view returns (uint) {
        return address(this).balance / 1e18;
    }

    function balOwner() public view returns (uint) {
        return address(owner).balance / 1e18;
    }

    function withdraw() public {
        // get the amount of Ether stored in this contract
        uint amount = address(this).balance;

        // send all Ether to owner
        // Owner can receive Ether since the address of owner is payable
        (bool success, ) = owner.call{value: amount}("");
        require(success, "Failed to send Ether");
    }

    // Function to transfer Ether from this contract to address from input
    function transfer(address payable _to, uint _amount) public {
        // Note that "to" is declared as payable
        (bool success, ) = _to.call{value: _amount * 1e18}("");
        require(success, "Failed to send Ether");
    }
}

Example-16

Version2. slight difference...

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

contract Payable {
    address payable public owner;

    constructor() payable {
        owner = payable(msg.sender);
    }
    // Function to deposit Ether into this contract.
    // Call this function along with some Ether.
    // The balance of this contract will be automatically updated.
    function deposit() public payable {

    }

    receive() external payable {}
    fallback() external payable {}

    function deposit2(uint _amount) public payable {
        // Call returns a boolean value indicating success or failure.
        // This is the current recommended method to use.
        (bool sent, ) = (address(this)).call{value: _amount *1 * 1e18}("");
        require(sent, "Failed to send Ether");
    }

    // check the balance of current contract
    function balContract() public view returns (uint) {
        return address(this).balance / 1e18;
    }

    function balOwner() public view returns (uint) {
        return address(owner).balance / 1e18;
    }

    function withdraw() public {
        // get the amount of Ether stored in this contract
        uint amount = address(this).balance;

        // send all Ether to owner
        // Owner can receive Ether since the address of owner is payable
        (bool success, ) = owner.call{value: amount}("");
        require(success, "Failed to send Ether");
    }

    // Function to transfer Ether from this contract to address from input
    function transfer(address payable _to, uint _amount) public {
        // Note that "to" is declared as payable
        (bool success, ) = _to.call{value: _amount * 1e18}("");
        require(success, "Failed to send Ether");
    }

    function getConAddr() public view returns (address) {
        return address(this);
    }
}

Example-17

Default values

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

contract DefaultVals {
    bool public b;  // default is false;
    uint public u;  // default is 0;
    int  public i;  // default is 0;
    address public a;   // default : 0x0000000000000000000000000000000000000000
    bytes32 public b32; // default : 0x0000000000000000000000000000000000000000000000000000000000000000
}

Example-18

Constant state variables with called gas value

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

// spend less gas when deploy the contract using constant state variables
// 21442 gas
contract Constants {
    // define a constant state variable
    address public constant MY_ADDRESS = 0x0000000000000000000000000000000000000100;
    uint public constant MY_UINT = 124;
}
// 23575 gas
contract Variables {
    address public MY_ADDRESS = 0x0000000000000000000000000000000000000100;
    uint public MY_UINT = 124;
}

Example-19

How Modifier works in the contract.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

contract FunctionModifier {
    bool public paused;
    uint public counter;

    constructor () {
        paused = true;
    }

    function setPause() external {
        paused = !paused;
    }

    modifier whenNotPaused() {
        require(paused == false, "Paused!");
        _;
    }

    modifier cap(uint _x) {
        require(_x < 100, "x >= 100");
        _;
    } 

    function inc() external whenNotPaused {
        counter += 1;
    }

    function dec() external whenNotPaused {
        counter -= 1;
    }

    // Two modifiers are added, and pay attention to the second one with parameter in side
    function incBy(uint _x) external whenNotPaused cap(_x){
        counter += _x;
    }
}

Example-20

Simple Access control management / ownership management

// SPDX-License-Identifier:MIT

pragma solidity ^0.8.13;

// In this example we will use state variables, global variables, function modifier, function and error handling
// we will claim the ownership of the contract, so that the access control of functions have been made.

contract ownable {
    address public owner;

    constructor () {
        owner = msg.sender;
    }

    // access control 
    modifier onlyOwner() {
        require(msg.sender == owner, "not owner");
        _;
    }

    // set ownership of the contract
    function setOwner(address _newOwner) external onlyOwner {
        require(_newOwner != address(0), "invalid address");
        owner = _newOwner;
    }

    // only the current owner of the contract can call this function
    function onlyOwnerCanCall() external onlyOwner {
    }

    // every one can have access to this function
    function anyOneCanCall() external {
    }
}

Example-21

Function outputs

// SPDX-License-Identifier:MIT

pragma solidity ^0.8.13;

contract FunctionOutputs {
    function returnMany() public pure returns (uint, bool) {
        return (1, true);
    }
    // 0: uint256:  1
    // 1: bool:  true

    function named() public pure returns (uint x, bool b) {
        return (1, true);
    }
    // 0: uint256: x 1
    // 1: bool: b true

    function assigned() public pure returns (uint x, bool b) {
        x = 1;
        b = false;
    }
    // 0: uint256: x 1
    // 1: bool: b true

    function destructingAssignments() public pure returns (uint m, bool n, bool q) {
        (uint x, bool b) = returnMany();
        (, bool _b) = assigned();
        return (x, b, _b);
    }
}

Example-22

Array basics

// SPDX-License-Identifier:MIT

pragma solidity ^0.8.13;

// Array - dynamic or fixed size
// Initialization
// Insert (push) , get, update, delete, pop, length
// creating array in memory
// returning array from function

contract Array {
    uint[] public nums = [1,3,5];
    uint[3] public numsFixed = [4,5,6]; // you cannot push() to a fixed sized array.

    function examples(uint _x) external {
        nums.push(_x);
        uint x = nums[1];
        nums[2] = x;
        delete nums[0];  // replace the number that indicated with 0, 
        nums.pop(); // the last element of the array will be deleted.

        // create an array in memory
        // Array in memory has to be fixed sized
        // so a.pop() or a.push() cannot be used.
        uint[] memory a = new uint[] (5);

        // assign value to element
        a[1] = 23;
    }

    // not recommended because a lot of gas will be charged.
    function returnArray() external view returns (uint[] memory) {
        return nums;
    }
}

Example-23

// SPDX-License-Identifier:MIT

pragma solidity ^0.8.14;

// Mapping 
// How to declare a mapping (simple and nested)
// set, get, and delete

// ["alice", "bob", "charlie"]
// {"alice": true, "bob":true, "charlie":true }

contract Mapping {
    mapping(address => uint) public balances;
    mapping(address => mapping(address => bool)) public isFriend;

    function examples() external {
        balances[msg.sender] = 123;
        // uint bal = balances[msg.sender];
        // uint bal2 = balances[address(1)]; // 0
        balances[msg.sender] += 456; // 123 +456 
        //delete balances[msg.seder]; // 0
        isFriend[msg.sender][address(this)] = true;
    }
}

contract IterableMapping {
    mapping (address => uint) public balances;
    mapping (address => bool) public inserted;

    address[] public keys;

    // assign value to address and push into array.
    // one address can only be pushed once;
    function set(address _key, uint _val) external {
        require(!inserted[_key], "Value has been assigned to this address!");
        inserted[_key] =true;
        balances[_key] = _val;
        keys.push(_key);
    }
    function getSize() external view returns (uint) {
        return keys.length;
    }
    function first() external view returns (uint) {
        return balances[keys[0]];
    }
    function last() external view returns (uint) {
        return balances[keys[keys.length-1]];
    }
    function get(uint _i) external view returns (uint) {
        return balances[keys[_i]];
    }
}

Example-24

Learning struct

// SPDX-License-Identifier:MIT

pragma solidity ^0.8.14;

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", 1990, msg.sender);
        Car memory lambo  = Car({year: 1980, model: "Lamborghini", owner: msg.sender});
        Car memory tesla;
        tesla.model = "Tesla";
        tesla.year  = 2000;
        tesla.owner = msg.sender;

        cars.push(toyota);
        cars.push(lambo);
        cars.push(tesla);
        cars.push(Car("Ferrari", 2022, msg.sender));

        // Car memory _car = cars[0];
        // _car.model;
        // _car.year;
        // _car.owner;

        // modify the data cars[0] that is stored in the memory. 
        // that is why we use 'storage' rather than 'memory';
        Car storage _car = cars[0]; //modify the attributes of cars[0];
        _car.year = 1999;
        delete _car.owner;  // set to the default value;
        delete cars[1];     // reset to default value;

    }
}

Example-25

Learning enum

// SPDX-License-Identifier:MIT

pragma solidity ^0.8.14;

contract Enum {
    enum Status {
        None,       //0
        Pending,    //1
        Shipped,    //2
        Rejected,   //3
        Canceled    //4
    }

    Status public status;
    string strtusToString;

    struct Order {
        address buyer;
        Status status;
    }

    Order[] public orders;

    function get()  external view returns (Status) {
        return status; 
    }

    function set(Status _status) external {
        status = _status;
    }

    function ship() external {
        status = Status.Shipped;
    }

    function reset() external {
        delete status;
    }
}

Example-26

Insert, update, read form array of struct

// SPDX-License-Identifier:MIT

pragma solidity ^0.8.14;

// Insert, update, read form array of struct
contract TodoList {
    struct Todo {
        string text;
        bool completed;
    }
    Todo[] public todos;
    function create(string calldata _text) external {
        todos.push(Todo({text:_text, completed:false}));
    }
    function updateText(uint _index, string calldata _text) external {
        // 35138 gas
        // every time first access the index of array, then get the struct and then assign the new value;
        todos[_index].text = _text;
        // todos[_index].text = _text;
        // todos[_index].text = _text;
        // todos[_index].text = _text;

        //34578 gas
        // access the index of array only once, then get the struct and then assign the new value four times
        // Todo storage todo = todos[_index];
        // todo.text = _text;
        // todo.text = _text;
        // todo.text = _text;
        // todo.text = _text;
    }

    // the least gas willbe used with this way using 'storage'
    function get(uint _index) external view returns (string memory, bool) {
        Todo storage todo = todos[_index];              //'storage': 29395 gas; while 'memory': 29452
        return (todo.text, todo.completed);
    }

    // gas used: 29547
    // function get(uint _index) external view returns (string memory, bool) {
    //   return (todos[_index].text, todos[_index].completed);  
    // }

    function toggleCompleted(uint _index) external {

        //53013 gas
        // Todo storage todo = todos[_index];
        // todo.completed = !todo.completed;

        //53218 gas
        todos[_index].completed = !todos[_index].completed;
    }

}

Example-26

How to use event and emit built-in function

// SPDX-License-Identifier:MIT

pragma solidity ^0.8.14;

// 
contract Event {
    event Log(string message, uint val);
    event IndexedLog(address indexed sender, uint val);

    // this is a transactional function rather than a read only 
    // not 'pure' or 'view'
    function example() external {
        emit Log("foo", 1244); // we are storing new data on blockchain
        emit IndexedLog(msg.sender, 7890);
    }

    event Message(address indexed _from, address indexed _to, string message);

    function sendMessage(address _to, string calldata message) external {
        emit Message(msg.sender, _to, message);
    }
}

Example-27

About inheritance

// SPDX-License-Identifier:MIT

pragma solidity ^0.8.14;

// Inheritance
// use 'virtual' keyword to declear that the function can be inherited
contract A {
    function foo() public pure virtual returns (string memory) {
        return "A";
    }
    function bar() public pure virtual returns (string memory) {
        return "A";
    }
    function baz() public pure virtual returns (string memory) {
        return "Haven't appeared in B";
    }
    // more code here

}

// use'override' to declear that this function is inherited from father and is overrided.
contract B is A {
    function foo() public pure override returns (string memory) {
        return "B";
    }
    // this function can be inherited by sons
    function bar() public pure virtual override returns (string memory) {
        return "B";
    }
    // more code here
    // Here function baz() is inherited automatically...
}

contract C is B {
    function bar() public pure override returns (string memory) {
        return "C";
    }
}

Example-28

Call parent contructors and the execution order

// SPDX-License-Identifier:MIT
pragma solidity ^0.8.14;

// 2 ways to call parent constructors
// Order of initialization
contract S {
    string public name;
    constructor(string memory _name) {
        name = _name;
    }
}
contract T {
    string public text;
    constructor(string memory _text) {
        text = _text;
    }
}
// way 1 initialize using static input 
contract U is S("s"), T("t") {}

// way 2 initialize dynamically
// execution order will be S T V
// determined by the order that written behind 'is'
contract V is S, T {
    constructor(string memory _name, string memory _text) S(_name) T(_text){
        // name = _name;
        // text = _text;
    }
}
// one is static input and another is dynamic input
contract W is S("s"), T {
    constructor(string memory _text) T(_text) {
    }
}

Example-29

function and state variable's visibility

// SPDX-License-Identifier:MIT
pragma solidity ^0.8.14;

// function and state variable's visibility
// private - only inside contract
// internal - only inside contract and child contracts
// public  - inside and outside contract
// external - only from outside contract

Example-30

Using receive() and fallback()

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.14;

/*
Fallback executed when 
- function doesn't exist
- directly send ETH 
*/

/*
Difference between fallback() and receive()
Ether is sent to contract
            |
    Is msg.data empty?
            / \
        yes     no
        /        \
receive() exist?   fallback()
        / \
      yes  no
      /     \
receive()   fallback()
*/

// this contract can receive ETH
// You can send ETH directly to this contract
// Or have another contract sending ETH to this contract
// If the receive() funciton does not exist, even though msg.data is empty, fallback() function will executed.
contract Fallback {
    event Log(string func, address sender, uint value, bytes data);

    fallback() external payable {
        emit Log("fallback", msg.sender, msg.value, msg.data);
    }
    receive() external payable {
        emit Log("receive", msg.sender, msg.value, "");

    }
}

Example-30

Sending ETH from a contract to another contract

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.14;

// 3 ways to send ETH
// transfer - 2300 gas, reverts
// send - 2300 gas, returns bool
// call - all gas, returns bool and data;

contract SendEther {
    // enable to receive Ether
    constructor () payable {}
    receive() external payable {}
    //send 2300 gas to the contract to execute some code		"event": "Log",
    // "args": {
    // 	"0": "123",
    // 	"1": "2260",
    // 	"amount": "123",
    // 	"gas": "2260"
    function sendViaTransfer(address payable _to) external payable {
        _to.transfer(msg.value);
    }
    // "args": {
	// 		"0": "123",
	// 		"1": "2260",
	// 		"amount": "123",
	// 		"gas": "2260"
	// 	}
    function sendViaSend(address payable _to) external payable {
        bool sent = _to.send(msg.value);
        require(sent, "send failed!");
    }
    // "args": {
	// 		"0": "123",
	// 		"1": "6521",
	// 		"amount": "123",
	// 		"gas": "6521"       // receiving more gas than the other two
	// 	}
    function sendViaCall(address payable _to) external payable {
        (bool success, ) = _to.call{value:msg.value}("");
        require(success, "call failed");
    }
}

// from the contract above, we will be able to send ETH to the contract below
contract EthReceiver {
    event Log(uint amount, uint gas);
    receive() external payable {
        emit Log(msg.value, gasleft());
    }
}

Example-31

ETH wallet Everyone can send ETH to this contract Only the owner of the contract can withdraw ETH from this contract

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.14;

// every one can send ETH to this contract
// only the owner can withdraw ETH from this contract

contract EtherWallet {
    address payable public owner;
    modifier onlyOwner {
        require(msg.sender == owner, "caller is not owner");
        _;
    }
    constructor() {
        owner = payable(msg.sender);
    }
    receive() external payable {}

    function withdraw(uint _amount) external onlyOwner{
        payable(msg.sender).transfer(_amount);
    }
    function getBalance() external view returns (uint) {
        return address(this).balance;
    }
}

Example-31

call another contract

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.14;
// call other contracts
contract CallTestContract {
    function setX (TestContract _test, uint _x) external {
        _test.setX(_x);
    }
    function getX (TestContract _test) external view returns(uint) {
        return _test.getX();
    }
    function setXandReceiveEther(address _test, uint _x) external payable {
        TestContract(_test).setXandReceiveEther{value: msg.value}(_x);
    }
    function getXandValue(TestContract _test) external view returns (uint x, uint value) {
        //return _test.getXandValue();  // alternative way to return
        (x, value) = _test.getXandValue();
    }
}
// base contract which is used for being called by other contract
contract TestContract {
    uint public x;
    uint public value = 12;

    function setX(uint _x) external {
        x = _x;
    }
    function getX() external view returns (uint) {
        return x;
    }
    function setXandReceiveEther(uint _x) external payable {
        x = _x;
        value = msg.value;
    }
    function getXandValue() external view returns (uint, uint) {
        return (x, value);
    }
}

Example-32

Using interface to call pre-defined functions

Interface definition and caller contract

// SPDX-License-Identifier:MIT
pragma solidity ^0.8.14;
// Need to pay attention to the first function
// which can be called and return the value of 
//state variable count in the original contract 
//which may be not accessible
interface ICounter {
    function count() external view returns (uint); // need to pay more attention
    function inc() external;
}

contract CallInterface {
    uint public count;
    function examples(address _counter) external {
        ICounter(_counter).inc();
        count = ICounter(_counter).count();
    }
}

original contract which may be not accessible

// SPDX-License-Identifier:MIT

pragma solidity ^0.8.14;

contract Counter {
    uint public count;

    function inc() external {
        count += 1;
    }
    function dec() external {
        count -= 1;
    }
}

Example-33

How to create a contract with another contract using Solidity

// SPDX-License-Identifier:MIT
pragma solidity ^0.8.14;

contract Account {
    address public bank;
    address public owner;
    constructor (address _owner) payable {
        bank = msg.sender;
        owner = _owner;
    }
}
// deploying another contract using solidity
// usually when you see a contract is deploying another contract, such contract is named XxxFactory.
// since this contract deploys Account contract, that is why we name this contract as AccountFactory.
// a factory is creating new staff, likewise this factory is creating new contact and in this case, it created new account contract.

// where is this new contract deployed?
// Since we stored the newly created contract account into array Account, so I can get the address by visit accounts[0].
// and this is the address of the contract that is newly deployed.
contract AccountFactory {
    Account[] public accounts;
    function createAccount(address _owner) external payable {
        Account account = new Account{value: 111}(_owner);
        accounts.push(account);
    }
}

// video resource:
// https://www.youtube.com/watch?v=J2Wp2SHq1Qo&list=PLO5VPQH6OWdVQwpQfw9rZ67O6Pjfo6q-p&index=45

Example-34

How to define a library and use it in other contract.

// SPDX-License-Identifier:MIT
pragma solidity ^0.8.14;
// define a library
library Math {
    function max(uint x, uint y) internal pure returns (uint) {
        return x >= y ? x : y;
    }
}
// call a library 
contract Test {
    function testMax(uint x, uint y) external pure returns (uint) {
        return Math.max(x, y);
    }
}
// ---------Another example--------

library FindElement{
    function findElement(uint[] storage _arr, uint _elem) internal view returns (uint) {
        for(uint i = 0; i < _arr.length; i++) {
            if (_arr[i] == _elem) {
                return i;
            }
        }
        revert("Not Found!");
    }
}

contract TestArray {
    // uint[] datatype can use this library more convenient by using .findElement. 
    // See testFindRivsed
    using FindElement for uint[];
    uint[] public arr = [1,2,3,4];
    function testFind(uint _elem) external view returns (uint index) {
        index = FindElement.findElement(arr, _elem);
    }
    function testFindRivsed(uint _elem) external view returns (uint index) {
        index = arr.findElement(_elem);
    }
}

Example-35

Access control with ADMIN Role & USER Role

// SPDX-License-Identifier:MIT

pragma solidity ^0.8.14;

contract AccessControl {
    event GrantRole(bytes32 indexed role, address indexed account);
    event RevokeRole(bytes32 indexed role, address indexed account);

    // role => account = > bool
    mapping(bytes32 => mapping(address => bool)) public roles;

    // should make ADMIN and USER as private 
    bytes32 public constant ADMIN = keccak256(abi.encodePacked("ADMIN"));
    bytes32 public constant  USER = keccak256(abi.encodePacked("USER"));

    modifier onlyRole(bytes32 _role) {
        require(roles[_role][msg.sender], "Not authorized");
        _;
    }

    constructor() {
        _grantRole(ADMIN, msg.sender);
    }
    function _grantRole(bytes32 _role, address _account) internal {
        roles[_role][_account] = true;
        emit GrantRole(_role, _account);
    }
    function grantRole(bytes32 _role, address _account) external onlyRole(ADMIN){
        _grantRole(_role, _account);
    }
    function revokeRole(bytes32 _role, address _account) external onlyRole(ADMIN){
        roles[_role][_account] = false;
        emit RevokeRole(_role, _account);
    }
}

Example-36

// [DEV Blog Link](https://dev.to/yongchanghe/tutorial-delete-a-contract-using-kill-5a69-temp-slug-7306883?preview=4072e945a0132cdac0b36390bfc825449e2b57fe278cf08ed2d1ef6a052c4d430a989783a23735967ba8c8c46cb09cc80aab564f28c67d14c635c7a0)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
/* 
There is a funciton called selfdestruct in solidity. 
When you call this funciton, you will delete a contract from blockchain, 
and the function also force send ETH to any address specified. Even if 
the contract doesn't have a fallback function.
*/
// e.g. there is 1 ETH logged in this contract. 
// When we call the function kill(), it will execute selfdesctruct(), and this will delete the contract from blockchain.
// Since the contract saves 1 ETH, the contract will send 1 ETH to msg.sender.
// msg.sender is a contract without a fallback function.
/*
This contract will be deleted when we call the function kill().
When the contract is deleted, we will not be able to call the function testCall().
*/
contract Kill {
    constructor () payable {}

    fallback() external payable {}

    receive() external payable {}

    function kill () external {
        selfdestruct(payable(msg.sender));
    }
    function testCall() external pure returns (string memory) {
        return "Tst called";
    }
    function contractBal() external view returns (uint) {
        return address(this).balance;
    }
    function ownerBal() external view returns (uint) {
        return msg.sender.balance;
    }
}

contract Helper {
    function getBalance() external view returns (uint) {
        return address(this).balance;
    }
    function kill(Kill _kill) external {
        _kill.kill();
    }
}

Example-37

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
/*
This contract demostrates that every accounts can deposit ETH to this contract, but only the owner of 
the contract can withdraw ETH from this contact and delete this contract.
*/

contract PiggyBank {
    event Deposit(uint amount);
    event Withdraw(uint amount);
    address public owner = msg.sender;

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

    receive() external payable {
        emit Deposit(msg.value);
    }
    fallback() external payable{}

    function withdraw() external onlyOwner{
        require(msg.sender == owner, "not owner");
        emit Withdraw(address(this).balance);
        selfdestruct(payable(msg.sender));
    }
    function contractBalance() external view returns (uint) {
        return address(this).balance;
    }

}

Example-38

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

/*
interface definition:
totalSupply:    total amount of this ERC20 token.
balanceOf:      returns the amount of token that the current account has.
transfer:       the holder of ERC20 token can transfer his/her token over to a recipient.
approve:        if a holder of ERC20 token want another user account to send token on his behalf.
allowance:      how much token is a spender allowed to spend from a holder? Find out by calling this function.
transferFrom:   spender can call this. Transfer token from holder to another account with a spcified amount.
*/
interface IERC20 {
    function totalSupply() external view returns (uint);
    function balanceOf(address account) external view returns (uint);
    function transfer(address recipient, uint amount) external returns (bool);
    function allowance(address owner, address spender) external view returns (uint);
    function approve(address spender, uint amount) external returns (bool);
    function transferFrom(address sender, address recipient,uint amount) external returns (bool);
    event Transfer(address indexed from, address indexed to, uint amount);
    event Approval(address indexed owner, address indexed spender, uint amount);
}
contract ERC20 is IERC20 {
    uint public totalSupply;
    mapping(address => uint) public balanceOf;
    mapping(address => mapping(address => uint)) public allowance;
    string public name = "Test";
    string public symbol = "TEST";
    uint8 public decimals = 18;

    function transfer(address recipient, uint amount) external returns (bool) {
        balanceOf[msg.sender] -= amount;
        balanceOf[recipient] += amount;
        emit Transfer(msg.sender, recipient, amount);
        return true;
    }
    function approve(address spender, uint amount) external returns (bool) {
        allowance[msg.sender][spender] = amount;
        emit Approval(msg.sender, spender, amount);
        return true;
    }
    function transferFrom(address sender, address recipient, uint amount) external returns (bool) {
        allowance[sender][msg.sender] -= amount;
        balanceOf[sender] -= amount;
        balanceOf[recipient] += amount;
        emit Transfer(sender, recipient, amount);
        return true;
    }
    function mint(uint amount) external {
        balanceOf[msg.sender] += amount;
        totalSupply += amount;
        emit Transfer(address(0), msg.sender, amount);
    }
    function burn(uint amount) external {
        balanceOf[msg.sender] -= amount;
        totalSupply -= amount;
        emit Transfer(msg.sender, address(0), amount);
    }
}

Example-39

For now I don't know how to test it in Remix. Could any body help? Multi signature wallet model

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.14;

contract MultiSigWallet {
    event Deposit(address indexed sender, uint amount);         // triggered when deposit
    event Submit(uint indexed txId);                            // emitted when a transaction is submitted and waiting for other users to approve.
    event Approve(address indexed owner, uint indexed txId);    // emitted when other users approve the transaction
    event Revoke(address indexed owner, uint indexed txId);     // emitted when other users want to revoke their approvals
    event Execute(uint indexed txId);                           // once the approvals are sufficient the contract will be executed

    struct Transaction {
        address to;
        uint value;
        bytes data;
        bool executed;
    }

    address[] public owners;
    mapping (address => bool) public isOwner;
    uint public required;
    Transaction[] public transactions;
    mapping(uint => mapping(address => bool)) public approved;

    modifier onlyOwner() {
        require(isOwner[msg.sender], "not owner");
        _;
    }
    modifier txExists(uint _txId) {
        require(_txId < transactions.length, "tx does not exist");
        _;
    }
    modifier notApproved(uint _txId) {
        require(!approved[_txId][msg.sender], "tx already approved");
        _;
    }
    modifier notExecuted(uint _txId) {
        require(!transactions[_txId].executed, "tx already executed");
        _;
    }

    constructor(address[] memory _owners, uint _required) {
        require(_owners.length > 0, "owners required");
        require(_required > 0 && _required <= _owners.length, "Invalid required number of owners");
        for ( uint i = 0; i < _owners.length; i ++) {
            address owner = _owners[i];
            require(owner != address(0), "Invalid owner");
            require(!isOwner[owner], "Owner is not unique");
            isOwner[owner] = true;
            owners.push(owner);
        }
    }

    receive() external payable {
        emit Deposit(msg.sender, msg.value);
    }

    function submit(address _to, uint _value, bytes calldata _data) external onlyOwner {
        transactions.push(Transaction({
            to: _to,
            value: _value,
            data: _data,
            executed: false
        }));
        emit Submit(transactions.length - 1);
    }
    function approve(uint _txId) external onlyOwner txExists(_txId) notApproved(_txId) notExecuted(_txId) {
        approved[_txId][msg.sender] = true;
        emit Approve(msg.sender, _txId);
    }
    function _getApprovalCount(uint _txId) private view returns (uint count) {
        for (uint i; i < owners.length; i ++) {
            if (approved[_txId][owners[i]]) {
                count += 1;
            }
        }
    }
    function execute(uint _txId) external txExists(_txId) notExecuted(_txId) {
        require(_getApprovalCount(_txId) >= required, "approvals < required");
        Transaction storage transaction = transactions[_txId];
        transaction.executed = true;
        (bool success, ) = transaction.to.call{value: transaction.value}(transaction.data);
        require(success, "tx failed");
        emit Execute(_txId);
    }
    function revoke(uint _txId) external onlyOwner txExists(_txId) notExecuted(_txId) {
        require(approved[_txId][msg.sender], "tx not approved");
        approved[_txId][msg.sender] = false;
        emit Revoke(msg.sender, _txId);
    }
}

Example-40

Explain passing message

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.14;

// What is the message that is being passed when we use a contract calls another contract?

contract FunctionSelector {
    function getSelector(string calldata _func) external pure returns (bytes4) {
        return bytes4(keccak256(bytes(_func)));
    }
}
// input the following parameter in function getSelector() when deploy and test FunctionSelector:
//  "transfer(address,uint256)"

contract Receiver {
    // we can see the data that has been sent by call this event using emit
    event Log(bytes data);

    function transfer(address _to, uint _amount) external {
        emit Log(msg.data); 
    }
}
// will get the following data for "args": "data":
        // 0xa9059cbb
        // 0000000000000000000000005b38da6a701c568545dcfcb03fcb875f56beddc4 is correspondent to the address _to that is address(0)
        // 000000000000000000000000000000000000000000000000000000000000000b is correspondent to the uint _amount 11
        // https://www.rapidtables.com/convert/number/hex-to-decimal.html hex to decimal converter
	

Example-41

This example need to be tested using ERC721, which is example-42. How to test this contract? HERE

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

interface IERC721 {
    function transferFrom (
        address _from,
        address _to,
        uint _nftId
    )external;
}

contract DutchAuction {
    uint private constant DURATION = 7 days;

    // this variable can not be changed after the contract being deployed
    IERC721 public immutable nft;
    uint public immutable nftId;
    address payable public immutable seller;    // here'payable' keyword is needed or will have parse error at line 58 selfdestruct(); 
    uint public immutable stratingPrice;
    uint public immutable startAt;
    uint public immutable expiresAt;
    uint public immutable discountRate;

    constructor(
        uint _strartingPrice,
        uint _discountRate,
        address _nft,
        uint _nftId
    ) {
        seller = payable(msg.sender);
        stratingPrice = _strartingPrice;
        discountRate = _discountRate;
        startAt = block.timestamp;
        expiresAt = block.timestamp + DURATION;

        require(_strartingPrice >= _discountRate * DURATION, "starting price < discount");
        nft = IERC721(_nft);
        nftId = _nftId;
    }

    function getPrice() public view returns (uint) {
        uint timeElapsed = block.timestamp - startAt;
        uint discount = discountRate * timeElapsed;
        return stratingPrice - discount;
    }

    function buy() external payable {
        require(block.timestamp < expiresAt, " Auction expired");
        uint price = getPrice();
        require(msg.value >= price, "ETH < price");

        nft.transferFrom(seller, msg.sender, nftId);
        uint refund = msg.value - price;

        if(refund > 0) {
            payable(msg.sender).transfer(refund);
        }
        selfdestruct(seller);
    }
}

Example-42

ERC721 contract for NFT.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.6;

interface IERC165 {
    function supportsInterface(bytes4 interfaceID) external view returns (bool);
}

interface IERC721 is IERC165 {
    function balanceOf(address owner) external view returns (uint balance);

    function ownerOf(uint tokenId) external view returns (address owner);

    function safeTransferFrom(
        address from,
        address to,
        uint tokenId
    ) external;

    function safeTransferFrom(
        address from,
        address to,
        uint tokenId,
        bytes calldata data
    ) external;

    function transferFrom(
        address from,
        address to,
        uint tokenId
    ) external;

    function approve(address to, uint tokenId) external;

    function getApproved(uint tokenId) external view returns (address operator);

    function setApprovalForAll(address operator, bool _approved) external;

    function isApprovedForAll(address owner, address operator)
        external
        view
        returns (bool);
}

interface IERC721Receiver {
    function onERC721Received(
        address operator,
        address from,
        uint tokenId,
        bytes calldata data
    ) external returns (bytes4);
}

contract ERC721 is IERC721 {
    using Address for address;

    event Transfer(address indexed from, address indexed to, uint indexed tokenId);
    event Approval(
        address indexed owner,
        address indexed approved,
        uint indexed tokenId
    );
    event ApprovalForAll(
        address indexed owner,
        address indexed operator,
        bool approved
    );

    // Mapping from token ID to owner address
    mapping(uint => address) private _owners;

    // Mapping owner address to token count
    mapping(address => uint) private _balances;

    // Mapping from token ID to approved address
    mapping(uint => address) private _tokenApprovals;

    // Mapping from owner to operator approvals
    mapping(address => mapping(address => bool)) private _operatorApprovals;

    function supportsInterface(bytes4 interfaceId)
        external
        pure
        override
        returns (bool)
    {
        return
            interfaceId == type(IERC721).interfaceId ||
            interfaceId == type(IERC165).interfaceId;
    }

    function balanceOf(address owner) external view override returns (uint) {
        require(owner != address(0), "owner = zero address");
        return _balances[owner];
    }

    function ownerOf(uint tokenId) public view override returns (address owner) {
        owner = _owners[tokenId];
        require(owner != address(0), "token doesn't exist");
    }

    function isApprovedForAll(address owner, address operator)
        external
        view
        override
        returns (bool)
    {
        return _operatorApprovals[owner][operator];
    }

    function setApprovalForAll(address operator, bool approved) external override {
        _operatorApprovals[msg.sender][operator] = approved;
        emit ApprovalForAll(msg.sender, operator, approved);
    }

    function getApproved(uint tokenId) external view override returns (address) {
        require(_owners[tokenId] != address(0), "token doesn't exist");
        return _tokenApprovals[tokenId];
    }

    function _approve(
        address owner,
        address to,
        uint tokenId
    ) private {
        _tokenApprovals[tokenId] = to;
        emit Approval(owner, to, tokenId);
    }

    function approve(address to, uint tokenId) external override {
        address owner = _owners[tokenId];
        require(
            msg.sender == owner || _operatorApprovals[owner][msg.sender],
            "not owner nor approved for all"
        );
        _approve(owner, to, tokenId);
    }

    function _isApprovedOrOwner(
        address owner,
        address spender,
        uint tokenId
    ) private view returns (bool) {
        return (spender == owner ||
            _tokenApprovals[tokenId] == spender ||
            _operatorApprovals[owner][spender]);
    }

    function _transfer(
        address owner,
        address from,
        address to,
        uint tokenId
    ) private {
        require(from == owner, "not owner");
        require(to != address(0), "transfer to the zero address");

        _approve(owner, address(0), tokenId);

        _balances[from] -= 1;
        _balances[to] += 1;
        _owners[tokenId] = to;

        emit Transfer(from, to, tokenId);
    }

    function transferFrom(
        address from,
        address to,
        uint tokenId
    ) external override {
        address owner = ownerOf(tokenId);
        require(
            _isApprovedOrOwner(owner, msg.sender, tokenId),
            "not owner nor approved"
        );
        _transfer(owner, from, to, tokenId);
    }

    function _checkOnERC721Received(
        address from,
        address to,
        uint tokenId,
        bytes memory _data
    ) private returns (bool) {
        if (to.isContract()) {
            return
                IERC721Receiver(to).onERC721Received(
                    msg.sender,
                    from,
                    tokenId,
                    _data
                ) == IERC721Receiver.onERC721Received.selector;
        } else {
            return true;
        }
    }

    function _safeTransfer(
        address owner,
        address from,
        address to,
        uint tokenId,
        bytes memory _data
    ) private {
        _transfer(owner, from, to, tokenId);
        require(_checkOnERC721Received(from, to, tokenId, _data), "not ERC721Receiver");
    }

    function safeTransferFrom(
        address from,
        address to,
        uint tokenId,
        bytes memory _data
    ) public override {
        address owner = ownerOf(tokenId);
        require(
            _isApprovedOrOwner(owner, msg.sender, tokenId),
            "not owner nor approved"
        );
        _safeTransfer(owner, from, to, tokenId, _data);
    }

    function safeTransferFrom(
        address from,
        address to,
        uint tokenId
    ) external override {
        safeTransferFrom(from, to, tokenId, "");
    }

    function mint(address to, uint tokenId) external {
        require(to != address(0), "mint to zero address");
        require(_owners[tokenId] == address(0), "token already minted");

        _balances[to] += 1;
        _owners[tokenId] = to;

        emit Transfer(address(0), to, tokenId);
    }

    function burn(uint tokenId) external {
        address owner = ownerOf(tokenId);

        _approve(owner, address(0), tokenId);

        _balances[owner] -= 1;
        delete _owners[tokenId];

        emit Transfer(owner, address(0), tokenId);
    }
}

library Address {
    function isContract(address account) internal view returns (bool) {
        uint size;
        assembly {
            size := extcodesize(account)
        }
        return size > 0;
    }
}

Example-43

English Auction example.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.14;

interface IERC721 {
    function transferFrom (
        address from,
        address to,
        uint nftId
    ) external;
}

contract EnglishAuction {
    event Start();
    event Bid(address indexed sender, uint amount);
    event Withdraw(address indexed bidder, uint amount);
    event End(address highestBidder, uint amount);
    
    IERC721 public immutable nft;
    uint public immutable nftId;
    address payable public immutable seller;
    uint32 public endAt;
    bool public started;
    bool public ended;
    address public highestBidder;
    uint public highestBid;
    mapping(address => uint) public bids;

    constructor(address _nft, uint _nftId, uint _startingBid) {
        nft = IERC721(_nft);
        nftId = _nftId;
        seller = payable(msg.sender);
        highestBid = _startingBid;
    }
    function start() external {
        require(msg.sender == seller, "not seller");
        require(!started, "started");
        started = true;
        endAt = uint32(block.timestamp + 60); // 60 seconds should be long enough for the Demo and test.
        nft.transferFrom(seller, address(this), nftId);
        emit Start();
    }
    function bid() external payable {
        require(started, "not started");
        require(block.timestamp < endAt, "ended");
        require(msg.value > highestBid, " value < highest bid");
        if (highestBidder != address(0)) {
            bids[highestBidder] += highestBid;
        }
        highestBid = msg.value;
        highestBidder = msg.sender;
        emit Bid(msg.sender, msg.value);
    }
    function withdraw() external {
        uint bal = bids[msg.sender];
        bids[msg.sender] = 0;
        payable(msg.sender).transfer(bal);
        emit Withdraw(msg.sender, bal);
    }
    function end() external {
        require(started, "not started");
        require(!ended, "ended!");
        require(block.timestamp >= endAt, "not ended");
        ended = true;
        if (highestBidder != address(0)) {
            nft.transferFrom(address(this), highestBidder, nftId);
            seller.transfer(highestBid);
        } else {
            nft.transferFrom(address(this), seller, nftId);
        }
        emit End(highestBidder, highestBid);
    }
}

Example-44

Use Create2 to know contract address before the contract is deployed.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.14;
//Use Create2 to know contract address before it is deployed.
contract DeployWithCreate2 {
    address public owner;
    constructor(address _owner) {
        owner = _owner;
    }
}
contract Create2Factory {
    event Deploy(address addr);
    // to deploy another contract using owner address and salt specified
    function deploy(uint _salt) external {
        DeployWithCreate2 _contract = new DeployWithCreate2{
            salt: bytes32(_salt)    // the number of salt determines the address of the contract that will be deployed
        }(msg.sender);
        emit Deploy(address(_contract));
    }

    // get the computed address before the contract DeployWithCreate2 deployed using Bytecode of contract DeployWithCreate2 and salt specified by the sender
    function getAddress(bytes memory bytecode, uint _salt) public view returns (address) {
        bytes32 hash = keccak256(
            abi.encodePacked(
                bytes1(0xff), address(this), _salt, keccak256(bytecode)
            )
        );
        return address (uint160(uint(hash)));
    }
    // get the ByteCode of the contract DeployWithCreate2
    function getBytecode(address _owner) public pure returns (bytes memory) {
        bytes memory bytecode = type(DeployWithCreate2).creationCode;
        return abi.encodePacked(bytecode, abi.encode(_owner));
    }
}

Example-45

Aggregate many queries to contracts with a single function cal

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.14;

// Aggregate many queries to contracts with a single function cal
contract TestMultiCall {
    function func1() external view returns (uint, uint) {
        return (1, block.timestamp);
    }
    function func2() external view returns (uint, uint) {
        return (2, block.timestamp);
    }
    function getData1() external pure returns (bytes memory) {
        return abi.encodeWithSelector(this.func1.selector);
    }
    function getData2() external pure returns (bytes memory) {
        return abi.encodeWithSelector(this.func2.selector);
    }
}
contract MultiCall {
    function multiCall(address[] calldata targets, bytes[] calldata data) external view returns (bytes[] memory) {
        require(targets.length == data.length, "target length != data length");
        bytes[] memory results = new bytes[](data.length);
        for (uint i; i < targets.length; i++){
            (bool success, bytes memory result) = targets[i].staticcall(data[i]);
            require(success, "call failed");
            results[i] = result;
        }
        return results;
    }
}
contract MultiCall2 {
    function multiCall(string[] calldata targets) external pure returns (uint length) {
        length = targets.length;
    }
}

Example-46

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;

contract AbiDecode {
    struct MyStruct {
        string name;
        uint[2] nums;
    }
    function encode (uint _x, address _addr, uint[] calldata _arr, MyStruct calldata myStruct) external pure returns(bytes memory) {
        return abi.encode(_x, _addr, _arr, myStruct);
    }
    /*
    _x : 1
    _addr : 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4
    _arr : [2,3,4]
    myStruct : ["yongchang", [3,4]]
    encode output: 0x00000000000000000000000000000000000000000000000000000000000000010000000000000000000000005b38da6a701c568545dcfcb03fcb875f56beddc40000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000796f6e676368616e670000000000000000000000000000000000000000000000
    */
    function decode(bytes calldata data) external pure returns(uint x, address addr, uint[] memory arr, MyStruct memory myStruct) {
        (x, addr, arr, myStruct) = abi.decode(data, (uint, address, uint[], MyStruct));
    }
    /*
    0:
    uint256: x 1
    1:
    address: addr 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4
    2:
    uint256[]: arr 2,3,4
    3:
    tuple(string,uint256[2]): myStruct ,3,4
    */
}

Example-47

How to reduce the gas use ?

original source code link: HERE

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;

contract GasGolf {
    uint public total;
    function sumIfEvenAndLessThan99(uint[] calldata _arr) external {
        uint _total = total;
        uint len = _arr.length;
        for (uint i; i < len; ++i) {
            uint num = _arr[i];
            if (num % 2 == 0 && num < 99) {
                _total += num;
            }
        }
        total = _total;
    }
    // input [1,2,3,4,5,100]
    // output 
}
// start 50530 gas
// use calldata 48785 gas
// load state variables to memory 48574 gas
// use short circuit 48256 gas
// loop increments change from i++ to ++i 48226 gas
// cache array length by adding local variable len 48191 gas
// load array elements to memory 48029 gas 

Example-48

Wrapped Ether

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
import "@rari-capital/solmate/src/tokens/ERC20.sol";
contract WETH is ERC20 {
    event Deposit(address indexed account, uint amount);
    event Withdraw(address indexed account, uint amount);
    constructor() ERC20 ("Wrapped Ether", "WETH", 18) {}

    fallback() external payable{
        deposit();
    }
    receive() external payable{}
    function deposit() public payable {
        _mint(msg.sender, msg.value);
        emit Deposit(msg.sender, msg.value);
    }
    function withdraw(uint _amount) external {
        _burn(msg.sender, _amount);
        payable(msg.sender).transfer(_amount);
        emit Withdraw(msg.sender, _amount);
    }
}

Example - 49

PiggyBank.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

contract Payable {

    // set a savings goal
    uint public savingsGoal = 20;

    address payable public owner;

    // define a struct for saving the depositors and how much they have deposited.
    struct Depositor {
        address payable walletAddress;
        uint depoist;
    }     

    // save depositors information in a struct
    Depositor[] private depositors;       

    // access control 
    modifier onlyOwner() {
        require(msg.sender == owner, "not owner");
        _;
    }

    receive() external payable {}
    fallback() external payable {}
    constructor() payable {
        owner = payable(msg.sender);
    }

    // create an event to emit once you reach the savings goal
    event ReachedSavingGoal( address indexed sender, uint amount);


    // create an event to nofity who sent the ETH and the value
    event Deposit(address indexed sender, uint amount);     
    

    // Function to receive ETH
    function depositToBank() public payable {
        addDepositor(payable(msg.sender), msg.value);
        // Log who send the ETH and the value
        emit Deposit(msg.sender, msg.value);
        // check the balance to know 
        if (getBalance() > savingsGoal) {
            emit ReachedSavingGoal(msg.sender, msg.value);
        }
    }

    // Function to return the balance of the contract
    function getBalance() public view returns (uint) {
        return address(this).balance / 1e18;
    }

    // only the owner can withdraw the ETH
    function emptyTheBank() public onlyOwner{
        uint amount = address(this).balance;
        (bool success, ) = owner.call{value: amount}("");
        require(success, "Failed to send Ether");
    }

    // add depositors in to the struct
    function addDepositor(address payable walletAddress, uint depoist) private {
        depositors.push(Depositor(walletAddress, depoist));
    }

    // calculate the total deposit for a specified depositor
    function getDepositsValue(address payable walletAddress) view public returns (uint) {
        uint totalDeposit = 0;
        for (uint i = 0; i < depositors.length; i++) {
             if(depositors[i].walletAddress == walletAddress) {
                totalDeposit += depositors[i].depoist;
            }
        }
        return totalDeposit / 1e18;
    }
}

solidity-essentials's People

Contributors

hyc0812 avatar

Stargazers

Joseph Clark avatar Asher avatar  avatar Julian Castrence avatar Alejandro Gramajo avatar Fangyan Shi avatar Sabri Makni avatar Roman avatar TonicBoomerKewl avatar Huowuge avatar Zile avatar  avatar samuele coppede avatar 0xono avatar Tykok avatar

Watchers

 avatar  avatar

Forkers

tpatel27

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.