Modifiers & Inheritance & Import

Modifiers

are a great way to check pre-conditions.

//SPDX-License-Identifier: MIT

pragma solidity 0.8.3;

contract ModifierExample {

    mapping(address => uint) public tokenBalance;
    address owner;
    uint tokenPrice = 1 ether;

    constructor() {
        owner = msg.sender;
        tokenBalance[owner] = 100;
    }

    function createNewToken() public {
        require(msg.sender == owner, "You are not the owner");
        tokenBalance[owner]++;
    }

    function burnToken() public {
        require(msg.sender == owner, "you are not allowed");
        tokenBalance[owner]--;
    }
}

Both functions burnToken() and createNewToken() have the same require statement. We can centralize this by using a modifier instead.

    modifier onlyOwner() {
        require(msg.sender == owner, "You are not the owner");
        _;
    }
// needs to end with _;
//SPDX-License-Identifier: MIT

pragma solidity 0.8.3;

contract ModifierExample {

    mapping(address => uint) public tokenBalance;
    address owner;
    uint tokenPrice = 1 ether;

    constructor() {
        owner = msg.sender;
        tokenBalance[owner] = 100;
    }

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

    function createNewToken() public onlyOwner {
        tokenBalance[owner]++;
    }

    function burnToken() public onlyOwner {
        tokenBalance[owner]--;
    }
}

We can take this one step further by abstracting it with Inheritance.

Inheritance

Inherited contracts are deployed as a single contract, not seperate ones.

We shall move the owner variable and modifier into a separate contract called Owned. Our earlier contract will now inherit as per ModifierExample is Owned

//SPDX-License-Identifier: MIT
pragma solidity 0.8.3;

contract Owned {
    address owner;

    constructor() {
        owner = msg.sender;
    }

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

contract ModifierExample is Owned {

    mapping(address => uint) public tokenBalance;
    uint tokenPrice = 1 ether;

    constructor() {
        tokenBalance[owner] = 100;
    }

    function createNewToken() public onlyOwner {
        tokenBalance[owner]++;
    }

    function burnToken() public onlyOwner {
        tokenBalance[owner]--;
    }
}

The derived contract is able to utilize the modifier onlyOwner defined in the parent as well as the owner variable which was set by the parent.

When contract A inherits from contract B, only contract A is actually deployed to the blockchain. All code from contract B is copied into contract A. So there is only one smart contract address.

Solidity can do multiple inheritance (C3 linearization, Polymorphism)

Import & Inherit

//SPDX-License-Identifier: MIT
pragma solidity 0.8.3;

contract Owned {
    address owner;

    constructor() {
        owner = msg.sender;
    }

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

https://ethereum.stackexchange.com/questions/92265/solidity-proper-way-to-import-contracts

  • both functions and state variables will be available

  • With the is case, the contract inherits all the functionality of the base contract.

  • It means that when you instantiate the contract, a single object code (i.e., a single contract instance) containing all of that functionality is deployed on the network. Any call to a function of the base contract yields a non-external function call.

Import only (no inheritance)

Factory pattern -> used to deploy instances of a contract via new keyword

The new keyword in Solidity deploys and creates a new contract instance. It initializes the contract instance by deploying the contract, initializing the state variables, running its constructor, setting the nonce value to one, and, eventually, returns the address of the instance to the caller.

  • importing a contract into another so that you can use its instances in the contract you are importing it in

  • With the has case, the contract only holds a pointer to the other contract. They are essentially two different instances deployed on two different address on the network. Any call to a function of the other contract yields an external function call.

Other import stuff

Last updated