Inheritance & External Contracts - Discussion

Hey @tom88norman,

Good to see you back here in the forum :slight_smile:

Yes this is correct.

… by external contracts AND from any external “service” outside of the EVM/blockchain e.g. from Remix, or from the front-end interface of a dapp …

So, to put it another way, from Remix you can call both the public and external functions of a deployed contract.
Other than from an external “service”, external functions can only be called from external contracts. But as you know, public functions can also be called from anywhere else: from within the same contract, from within any derived contracts, and also from external contracts.

Let me know if anything is still unclear or if you have any further questions about this. I’m now going to have a look at your other question :slight_smile:

Hey again, Tom,

Yes, unfortunately it’s not magic :wink: You’ve got two things going on here…

(1)

The external address — the one showing in the Account field near the top of the Deploy & Run Transaction panel in Remix — which is calling the transfer() function, is the msg.sender of the internal transfer of funds within the Bank contract. In terms of the funds transferred between msg.sender and recipient, no actual ether enters or leaves the Bank contract. That’s why you won’t see any change to either of their external address balances in the Account field. What happens is effectively a reallocation of the contract’s total ether balance i.e. the msg.sender's share of the total contract balance (or their entitlement) decreases, and the recipient’s share (or entitlement) increases by the same amount. This is why you will only see a change in their individual user balances in the mapping, which you can check by calling getBalance() with each address.

(2)

The external function call within the transfer() function…

governmentInstance.addTransaction{value: 1 ether}(msg.sender, recipient, amount);

… is not invoked by the initial caller of the actual transfer() function (i.e. the user with an individual balance in the Bank’s mapping who is transferring funds to another user). Instead, the external function addTransaction is called by the Bank contract address itself (i.e. the address it’s deployed at). So the value of 1 ether is transferred from the Bank contract ether balance to the Government contract ether balance. You will only see the origin of the 1 ether if you add a separate getContractBalance() function to the Bank contract, so you have one in both contracts

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

Check both contract balances both before and after calling the transfer() function, to see how they change. The Bank contract balance should decrease by 1 ether, and the Government contract balance should increase by 1 ether.

Note that, even though addTransaction() is called by the Bank contract address, the msg.sender it is called with (as the 1st argument) references the address which initially called the transfer() function (and not the Bank contract address).

Without the external function call, the Bank contract balance should always be equal to the sum of all of the separate, individual user balances in the mapping; but with the external function call, the Bank contract balance will be less than the sum of these individual balances by an amount equal to the total fees — or tax, or levy, depending on how you wish to interpret the ether value transfer — “paid” by the Bank contract to the Government contract per transfer transaction.

Our particuar example code is based on a contractual relationship between a bank and the government. The idea is that the government needs to collect some kind of tax on each banking transaction that is an internal bank transfer. To keep things simple in our example, the government is collecting a fixed amount of tax on each transfer. I would suggest an amount much smaller than 1 ether would be more appropriate, for example any value from 1 wei up to 1 gwei. This amount (which is the amount of tax paid by the Bank, and not the amount transferred from one bank account holder to another) will be automatically transferred from the Bank contract balance to the Government contract balance on each successfully executed transfer transaction. For record keeping purposes, the relevant data for each transfer is also sent from Bank to Government and stored in the government’s transaction log (an array).

Instead of as a tax, another way to interpret this transfer of value between the two contracts could be as payment, or fees, for some kind of record keeping service i.e. the Bank contract pays the Government contract for storing all of its transaction data.

However we interpret this payment from one contract to the other, it demonstrates (albeit in an overly simplistic way) how we can program automatic interactions between individual contracts for the purposes of both data and value transfer. Hopefully, this example shows the potential for these sorts of programmed interactions to be applied to many different use cases.

Anyway, I hope that helps to clarify things. Do let me know if anything is still unclear, or if you have any further questions :slight_smile:

Hi Jon,

Thanks so much, as always, for taking the time to give such a detailed answer.

Yeah I do (finally) get the difference between the balances of the individual users tracked with a mapping vs the overall contract’s balance. It just never occurred to me that it was possible for there to be a discrepency between them (as in, the sum of all mapped balances vs the contract’s actual balance). But I guess it makes sense if you think about it in the context of an actual bank. And given the fact that the balance mapping is just a number that isn’t tied to any actual funds.

Anyway, your explanation really helped as always, thanks again.

I’ve been neck-deep in the multisig wallet project for the last few days so I’m about to post my first janky attempt at that :sweat_smile: wish me luck!

1 Like

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