Inheritance & External Contracts - Discussion

Hey Tom,

Good luck! :sweat_smile:

From what you’ve said in your post it sounds like lots of things have now clicked into place :slightly_smiling_face:

I understand what you mean here. I like to think of the mapping balances as accounting entries, and the additions and deductions we make to these balances in the various functions as accounting adjustments. These balances are “tied” to funds, because they reflect each individual user’s share of the total contract balance, effectively their entitlement to transfer ether out of the contract to an external address. If it wasn’t for these individual balances, we would just have a pool of funds in the contract without any way to record or control the allocation of ownership of these funds to individual external addresses, and how this changes over time.

Ownable Contract

pragma solidity 0.8.10;

contract Ownable {

address public owner;

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

}

Destroyable Contract

import “.\Ownable.sol”;
pragma solidity 0.8.10;

contract Destroyable is Ownable {

function terminate() public OnlyOwner {
    address payable receiver = msg.sender;
        selfdestruct(receiver);
}

}

Bank Contract

import “.\Destroyable.sol”;
pragma solidity 0.8.10;

contract Bank is Destroyable {

mapping(address => uint) balance;

event depositDone(uint amount, address indexed depositedTo);

function DestroyThis() public onlyOwner {
    Destroyable.close();
}

function deposit() public payable returns (uint)  {
    balance[msg.sender] += msg.value;
    emit depositDone(msg.value, msg.sender);
    return balance[msg.sender];
}

function withdraw(uint amount) public returns (uint){
    require(balance[msg.sender] >= amount);
    balance[msg.sender] -= amount;
    msg.sender.transfer(amount);
    return balance[msg.sender];
}

function getBalance() public view returns (uint){
    return balance[msg.sender];
}

function transfer(address recipient, uint amount) public {
    require(balance[msg.sender] >= amount, "Balance not sufficient");
    require(msg.sender != recipient, "Don't transfer money to yourself"); 
    uint previousSenderBalance = balance[msg.sender];
    _transfer(msg.sender, recipient, amount);
    assert(balance[msg.sender] == previousSenderBalance - amount);
}

function _transfer(address from, address to, uint amount) private {
    balance[from] -= amount;
    balance[to] += amount;
}

}

Hi @PabloAAAJ,

Your attempt to implement a multi-level inheritance structure is the best approach, because it is more streamlined. Bank only needs to explicitly inherit Destroyable, because it will implicitly inherit Ownable via Destroyable.

Unfortunately, your Destroyable and Bank contracts don’t compile for various reasons, and so your Bank contract can’t be deployed…

(1) For the current version of your code, you should be getting compiler errors for both of your import statements…

This is because each file path should have a forward slash instead of a back slash.


(2) Once you’ve corrected the import statements, you should now be getting a compiler error for this line …

The error here is an upper/lower case inconsistency with one of the letters in your modifier name. Let the compiler, and the error messages it generates, help you find these types of spelling-related errors.


(3) The next compiler errors that are generated are for the following two lines of code:

In the terminate() funtion in Destroyable

In the withdraw() function in Bank

Prior to Solidity v0.8 msg.sender is already a payable address by default, and so doesn’t need to be explicitly converted whenever the syntax requires it to reference a payable address (such as in the above two lines of code. However, you are using Solidity v0.8.10, and from v.0.8 msg.sender is non-payable by default, and so this means that in the above two lines of code you need to explicity convert msg.sender to a payable address using the syntax payable() …

// In the terminate() function in Destroyable
// selfdestruct() must be called with a payable address argument
address payable receiver = payable(msg.sender);
selfdestruct(receiver);

// In the withdraw() function in Bank
// the transfer method must be called on a payable address
payable(msg.sender).transfer(amount);

In the terminate() function, a concise alternative to using the additional receiver variable is to call selfdestruct() with msg.sender directly, and convert it to a payable address within the function call itself…

selfdestruct(payable(msg.sender));

Have a look at this post for further details about the use of the additional local variable in the model solution.


(4)  The next compiler error is generated for the function call within the DestroyThis() function in Bank…

Again, the error message will make it clear what the problem is. You are trying to call a function in the Destroyable contract that doesn’t exist!

The inherited terminate() function has public visibility and so it is available to call within Bank. This means that you can just call it as follows …

function DestroyThis() public onlyOwner {
    terminate();
}

However, one of the key reasons for using inheritance is to avoid code duplication. The terminate() function has public visibility and so, when Bank is deployed, terminate() is also available to be called directly by the contract owner’s external address whenever they want to destroy the contract, instead of having to call it via the additional DestroyThis() function you’ve added to Bank. Therefore, you can remove the whole of the DestroyThis() function from Bank.


By the way I think you missed out the Events Assignment, because you are missing a Transfer event in your Bank contract, and a corresponding emit statement in its transfer() function.

Let me know if anything is unclear, if you have any questions, or if you need any help correcting your contracts, deploying Bank, and getting it to do what it should :slight_smile:

1 Like

Thanks a million for your feedback!
I’ve corrected the typos and I’m working on those events!

1 Like

Hey guys hoping someone can help me with a couple questions i have regarding the Inheritance Assignment with the 3 contracts… Bank.sol, Destroyable.sol. and Ownable. sol. With bank.sol I noticed I could not withdraw or transfer money in Remix without adding “payable” to those functions. Is it correct that i would have to add “payable” in the header of the withdraw function and transfer functions in order to withdraw and transfer money?

One other question as welll… I noticed the “withdraw” function in bank.sol does not adjust the balance after the withdrawal. Shouldn’t there be a line to do this so we can accurately reflect the balance, like balance[msg.sender] -= amount. Something like this?

Hi @Bitborn,

No… neither of these functions receive ether, so they don’t need to be marked payable. A function should only be marked payable when it needs to receive ether from an external address to add to the contract address balance e.g. the deposit() function. It might help to think of payable in a function header as the code that enables msg.value to be added to the contract address balance, without us having to add any further code for this. In the deposit() function, this happens automatically because we have marked the function as payable . The line …

balance[msg.sender] += msg.value;

… then adds the same value to the individual user’s balance in the mapping, in order to keep track of their share of the total funds held in the contract.

However, we should not mark the withdraw() function as payable , because it doesn’t need to receive any ether from an external address and add it to the contract balance. If we make it payable, then, if the caller sends an ether value to the withdraw() function by mistake (as well as calling it with a withdrawal amount) then this ether would be added to the contract balance and potentially lost to the user: this is because the withdraw() function does not make an equivalent accounting adjustment to the mapping for such a deposit (only for withdrawals). However, the function is much securer if we leave it as non-payable, because then, if the caller makes the same mistake, the function will revert, and the ether won’t be sent.

Effectively, the withdraw() function does the opposite of what the deposit() function does: it deducts ether from the contract balance and sends it to an external address. It does this using the address member transfer (which is like a method called on a payable address)…

<address payable>.transfer(uint amount);

The separate transfer() function (not the special transfer method in the withdraw function) shouldn’t be marked payable either, for the same reasons I’ve already outlined for the withdraw() function. In fact, calling the transfer() function doesn’t involve any ether entering or leaving the Bank contract at all. Instead, it performs an internal transfer of funds (effectively, a reallocation of funds) between two individual users of the Bank contract. The net effect to the contract balance is zero, and the only change is to the individual user balances in the mapping, in order to adjust the amount each of the parties involved in the internal transfer is entitled to withdraw from the contract (their share of the total pooled funds) i.e. the recipient’s entitlement (balance) is increased by the same amount the sender’s entitlement is reduced.


Yes, you are right that we need this line of code. I’ve checked the solution code for the Inheritance Assignment in GitHub, and this line of code is missing in the withdraw() function in the Bank contract. This is an error. Well spotted!

msg.sender.transfer(amount)  transfers ether from the contract address balance to the caller’s external address, but it doesn’t adjust the individual user balances in the mapping. As I’ve explained above, these balances perform an internal accounting role, and record each user’s share of the contract’s total ether balance. So, just as we increase a user’s individual balance when they deposit ether in the contract, we need to do the opposite whenever they make a withdrawal.


If you still have difficulties depositing, withdrawing and internally transferring funds with your deployed Bank contract, you may find this post helpful. It lays out step-by-step instructions of how to properly check that all of your functions are working correctly, and it details what you should see happening and where, if it is.

You can check the Bank contract’s ether balance if you add the following function to your Bank contract (if you don’t already have it) …

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

Let me know if anything is still unclear, or if you have any further questions :slight_smile:

  • (1) Your full code from the 2 files containing your Ownable and Destroyable contracts;

  • (2) Only include your full code from the file containing your Bank contract if you have made modifications within the contract itself. Otherwise, just include the first few lines of code up to and including the contract header.

Although I inherited from Ownable as well as Destroyable, it would not be necessary as Destroyable alreads inherits from Ownable. I did it in order to have it explicit in this assignment.

// Ownable contract
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.7.5;

contract Ownable{

    address payable owner;

    modifier onlyOwner {
        require(msg.sender == owner);
        _; //run function
    }
    constructor(){
        owner = msg.sender;
    }
}

// Destroyable contract
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.7.5;

import "./ownable.sol";

contract Destroyable is Ownable {

    function selfDestroy() public {
        selfdestruct(owner);
    }
} 

// Bank contract 
// The first few lines of code up to and including the contract header.
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.7.5;import "./ownable.sol";

import "./destroyable.sol";

contract Bank is Ownable, Destroyable {
1 Like

Hi @mcs.olive,

Your solution is almost there… Everything compiles, your inheritance structure works, the contract can be destroyed by calling the selfDestroy() function in Destroyable, and on destruction any ether still remaining in the contract will be transferred to the contract owner’s external address :ok_hand: You just have one problem to resolve…

At the moment anyone can call selfDestroy() and destroy the contract. The funds are safe, because only the contract owner can receive them. But the fact that any address can actually trigger selfdestruct is obviously not ideal :sweat_smile:

The owner address argument, which selfdestruct() is called with, determines which external address the remaining contract balance will be transferred to. It doesn’t place any restriction on which address can trigger selfdestruct() and destroy the contract.

What functionality, inherited from Ownable, can you implement in Destroyable, to ensure that only the contract owner can call selfDestroy() and trigger selfdestruct ?

Correct :+1:
Bank only needs to explicitly inherit Destroyable, because it will inherit Ownable implicitly via Destroyable. This will give you a multi-level inheritance structure.

Just let me know if you have any questions.

@jon_m
Thanks a lot for your comments.
I changed function selfDestroy in Destroyable contract so only the owner can execute it.

    function selfDestroy() public onlyOwner{
        selfdestruct(owner);
    }
1 Like

Hey Filip and Moralis Academy,

Great video on inheritance! Great to visually see what this means. Am I correct in understanding the following structure?

Base Contract (parent): HelloWorld.sol (with the bank contract)
Derived Contract (child): Ownable.sol (with the Ownable contract)

Also, am I correct in making a comparison between Base/Derived Contract as parent-child components in React? For example, you have a parent item that passes down props to the child within it for example? Is this a similar-enough comparison for example purposes?

Yes and No, the Base contract will have access to Devired Contracts, but it will not pass any props, because its an inheritance, Base contract will be able to access all functionalities of Derived contract, but this does not mean that props or arguments can be sent to the Derived Contract.

Its more like a extra module for the Base contract than a parent-child components.

Hope it helps.

Carlos Z

Hi @InterstellarX,

I can’t comment on any comparison with React, but I can confirm the following about Solidity …

Child/derived contracts are not “within” parent/base contracts. If anything, it’s the opposite (but only figuratively speaking)… When a derived contract is compiled, all of the functionality that it inherits from a parent contract is automatically included, and the result is a single set of bytecode, which is then deployed as a single smart contract at a single Ethereum address on the Ethereum blockchain.

The functionality that a derived contract inherits from its base contract(s) is the following:

  • All functions, except for those with private visibility (although only public and internal functions will be accessible to call from within the derived contract itself).
  • State variables with public or internal visibility.
  • All modifiers.
  • All events.
  • Constructors

Inherited state variables will include any public or internal mappings or arrays. If these store struct instances, then the properties (or attributes, as they can also be called) of these struct instances will be available to be referenced using dot notation within the derived contract.

You’ve got this the wrong way round…
Ownable is the base/parent contract, which is inherited by the derived/child contract Bank. In fact, in the Inheritance Assignment we should ideally end up with the following multi-level inheritance structure:

// Ownable.sol
contract Ownable {...}  // Base/parent contract of Destroyable

// Destroyable.sol
import "./Ownable.sol"
contract Destroyable is Ownable {...}  /* Derived/child contract of Ownable
                                          AND base/parent contract of Bank */
// HelloWorld.sol
import "./Destroyable.sol"
contract Bank is Destroyable {...}  // Derived/child contract of Destroyable

And just to be clear in terms of what @thecil is explaining here …

With inheritance in Solidity, it’s the derived contract(s) that can access (and have available) certain functionality in the base contract. For example, the derived contract can reference a public or internal state variable, or call a public or internal function, in the base contract; but the base contract can’t reference any of the state variables, or call any of the functions, in the derived contract.

Let me know if anything is still unclear, or if you have any further questions :slight_smile:

1 Like
  1. What is the base contract?
    Also known as parent contract, it’s the contract that is inherited or derived into a base contract either through single inheritance or multiple inheritance.

  2. Which functions are available for derived contracts?
    Any of the public, internal functions of the base contracts that are inherited.

  3. What is hierarchical inheritance?
    It’s when a single base contract is derived in multiple contracts.

1 Like

1. What is the base contract?
It’s a contract which is being inherited by children contracts, acting as a base for developing the subclasses.

2. Which functions are available for derived contracts?
Public and internal scoped functions (and state variables).

3. What is hierarchical inheritance?
It’s when a contract is used as a base contract by many children contracts, forming a kind of taxonomy.

1 Like

Hi there is my solution for lessons/inheritance-assignment-2

./abc/Ownable.sol

pragma solidity 0.7.5;

contract Ownable {
  address public owner;
  constructor(){
    owner = msg.sender;
  }
  modifier onlyOwner {
    require(msg.sender == owner, "Owner only");
    _;
  }
}

./abc/DestroyableByOwner.sol

pragma solidity 0.7.5;

import "./Ownable.sol";

contract DestroyableByOwner is Ownable {
  function destroy() public onlyOwner {
    selfdestruct(payable(owner));
  }
}

./SimpleBank.sol

pragma solidity 0.7.5;

import "./abc/Ownable.sol";
import "./abc/DestroyableByOwner.sol";

contract SimpleBank is Ownable, DestroyableByOwner {
...
}
1 Like
  1. What is the base contract?
    A base contract is the parent contract (the contract that is inherited).

  2. Which functions are available for derived contracts?
    All public and internal scoped functions.

  3. What is hierarchical inheritance?
    A single contract acts as a base contract for multiple derived contracts.

1 Like

Nice answers @JayM700 :ok_hand:

Just a couple of clarifications …

Correct

No … the base (or parent) contract is inherited by the child (or derived) contract. We can also say that the child contract is derived from the base (or parent) contract.

… through any type of inheritance relationship: single, multi-level, hierarchical or multiple inheritance.

Just let me know if you have any questions.

1 Like

An excellent, and very well-presented solution @fedev :muscle:

We can also streamline the inheritance structure by having Bank explicitly inherit Destroyable only, because it will inherit Ownable implicitly via Destroyable. This will give you a multi-level inheritance structure.

Let me know if you have any questions about this point.

1 Like

Hello. Could someone help me, please? I have a issue when I try to get the information from “getTransaction” function in Government contract. It says “call to Government.getTransaction errored: VM error: invalid opcode.”

I am adding below the two contracts. Maybe is something I did wrong and I can’t seem to notice what.

Government.sol

pragma solidity 0.7.5;

contract Government{

    struct Transaction{
        address from;
        address to;
        uint amount;
        uint txId;
    }

    Transaction[] transactionlog;

    function addTransaction(address _from, address _to, uint _amount) external{
        transactionlog.push(Transaction (_from, _to, _amount, transactionlog.length));
    }

    function getTransaction(uint _index) public view returns (address, address, uint){
        return(transactionlog[_index].from, transactionlog[_index].to, transactionlog[_index].amount);
    }
}

Etherbank.sol

pragma solidity 0.7.5;

import "./Destroyable.sol";
import "./Ownable.sol";

interface GovernmentInterface{
    function addTransaction(address _from, address _to, uint _amount) external;
}

contract EtherBank is Ownable,Destroyable{

    GovernmentInterface GovernmentInstance = GovernmentInterface(0x9ecEA68DE55F316B702f27eE389D10C2EE0dde84);

    //storage - means permanent storage of data (persistant data)
    //memory - temporare data storage (is stored temporarely as long as the function executes)
    //calldata - similar to memory, but READ-ONLY
    
    mapping (address => uint)balance;
    
    event depositDone(uint amount, address depositedTo);
    event balanceTransfered(uint amount, address fromAddress, address toAddress);

    function deposit() public payable returns (uint){
        balance[msg.sender] += msg.value; //this line is not necessarly because the balance mapping has nothing to do with the value of the contract, it just keeps track of our internal balances (which address deposited which money)
        emit depositDone(msg.value, msg.sender);
        return balance[msg.sender];
    }

    function withdraw(uint amount) public returns (uint){
        require(balance[msg.sender] >= amount);
        balance[msg.sender] -= amount;
        msg.sender.transfer(amount);
        return(balance[msg.sender]);
    }

    function getBalance() public view returns (uint){
    return balance[msg.sender];
    }

    function transfer(address recipient, uint amount) public{
    require(balance[msg.sender] >= amount);
    require(msg.sender != recipient);
    uint previousSenderBalance = balance[msg.sender];
    _transfer(msg.sender, recipient, amount);
    
    GovernmentInstance.addTransaction(msg.sender, recipient, amount);

    assert(balance[msg.sender] == previousSenderBalance - amount);
    emit balanceTransfered(amount, msg.sender, recipient);

    }
    function _transfer(address from, address to, uint amount) private{
        balance[from] -= amount;
        balance[to] += amount;

    }
}

Hey Cosmin,

There’s nothing wrong with your code.

Whenever you redeploy Government, then it will have a different contract address, and so you also need to change the Government address assigned to your governmentInstance in this line of your code in Bank:

You need to change this address before you deploy Bank, so that it’s the same as the current address that your Government contract is deployed at. If you forget to replace the previous address of your external contract for the new one, then when you call Bank’s transfer() function either of the following can happen …

(1) The transaction completes successfully, but the addTransaction() function isn’t called on the Government contract that you currently have deployed and showing in the Deploy & Run Transactions panel. So, when you then call getTransaction() on your Government contract, you get the error message that you’ve been getting:

call to Government.getTransaction errored: VM error: invalid opcode.

This is because the transfer data from the transaction wasn’t passed to the addTransaction() function that you are expecting, and so wasn’t added to the transactionlog array that you are now trying to retrieve data from. You get this error message because there is no data at the array index you are trying to look up.

OR

(2) The transaction reverts, because the external function call fails, and you get the following default error message for revert, which doesn’t actually explain what the error is:

transact to Bank.transfer errored: VM error: revert.

The transaction has been reverted to the initial state.
Note: The called function should be payable if you send value
and the value you send should be less than your current balance.
Debug the transaction to get more information.

Forgetting to update the Government contract address is easily done, and catches most of us out the first time we make this mistake :sweat_smile:

I hope that resolves your issue, but let me know if you still experience any problems.

1 Like