2

I understand that for good organization and to preserve flexibility, it is good practice to separate code by functionality. For example: separate your datastorage from app logic. To do this, AFAIK, we have several tools:

  1. Inheritance via import (essentially copying code at compilation).
  2. Deploying separate contracts and then calling back-and-forth (intuitively seems very expensive). For example, putting data storage in one contract and continually making calls to it from the app logic contract.
  3. Libraries / Interface : I understand this allows us to consolidate blocks of oft-repeated code, but I don't understand how this is inherently different or less-expensive than a simple inheritance / import.

I need help understanding the differences between these. For example, what are some use-cases for each, what are their respective pros/cons, how do they differ in terms of resource consumption, and what is happening under-the hood with each.

Very much thanks.

1 Answer 1

2

Hope this clears it up for you:

  1. Inheritance

This you use when you want your contract to bahave like another, meaning whatever methods and storage variable will belong to your new contract as if you declare it there. This can be donde by writing the contract in the same file or importing the contract from a library or the same project, for this example Im going to place them in the same file for better visibility

Example:

contract Fruit{
  function isFruit() returns(uint256){
     return 20;
  }
}
contract Apple is Fruit{
  function fruitNumber() returns(uint256){
    return isFruit() + 1;
  }
}
  1. Libraries They have a set of methods you can rehuse but doesnt mean your new contract will behave like that library it will just borrow some methods here and there for a specific type.

Example:

  library MathLib {
    
    function multiply(uint a, uint b) public view returns (uint, address) {
        return (a * b, address(this));
    }
}
contract Example {
    
    using MathLib for uint;
    address owner = address(this);
    
    function multiplyExample(uint _a, uint _b) public view returns (uint, address) {
        return _a.mult(_b);
    }
}
  1. Interface These are abstract contracts that just include the signature of the methods, normally you use them when trying to consume a deployed contract on this example we pass the address where the contract is deployed

Example:

 // Uniswap example
interface UniswapV2Factory {
    function getPair(address tokenA, address tokenB)
        external
        view
        returns (address pair);
}

interface UniswapV2Pair {
    function getReserves()
        external
        view
        returns (
            uint112 reserve0,
            uint112 reserve1,
            uint32 blockTimestampLast
        );
}

contract UniswapExample {
    address private factory = 0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f;
    address private dai = 0x6B175474E89094C44Da98b954EedeAC495271d0F;
    address private weth = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;

    function getTokenReserves() external view returns (uint, uint) {
        address pair = UniswapV2Factory(factory).getPair(dai, weth);
        (uint reserve0, uint reserve1, ) = UniswapV2Pair(pair).getReserves();
        return (reserve0, reserve1);
    }
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.