Inheritance & External Contracts - Discussion

Hi @Jason_Purcell,

Have you managed to resolve this now?

If your code has compiled successfully, then if when you try to deploy Bank it gets “stuck” on  creation of Helloworld pending...  but you never actually get the transaction receipt with the green tick confirming successful deployment, then you may just need to close Remix in your browser and then reopen it (perhaps also completely closing your browser and reopening that as well). Sometimes Remix just gets tired and sluggish, and needs fresh start :wink:

I’m assuming this contract you’ve posted is before you’ve actually done the Inheritance assignment, right? I’m only asking because you’ll need a third contract in your inheritance structure for the assignment solution… but I imagine you’re just posting this here because you got stuck with this issue before even starting the assignment …

Anyway, just let us know if you’re still experiencing any problems and need some more help.


By the way, you also need to remove the onlyOwner modifier from your withdraw() function header. Adding this means that only the contract owner will be able to withdraw funds while the Bank contract is deployed and operating normally. The contract allows multiple addresses to deposit funds (we are simulating a bank with bank account holders) and so, while the contract is still deployed, it only seems fair that all users should be able to withdraw their funds as well, don’t you think? :sweat_smile: Earlier on in the course, our withdraw() function header didn’t include the onlyOwner modifier, but it looks like you’ve missed out the assignment where we focus on this function in particular, so maybe you haven’t realised this. I think during the course we’ve been adding and removing onlyOwner from various functions to demonstrate different scenarios, and I think it might have ended up being left in the withdraw() function in the code for this later part of the course by mistake!

I think you may have also missed out the Events Assignment, because you are missing a Transfer event, and a corresponding emit statement, in your contract. This is the assignment from the Events video lecture, which is near the end of the Additional Solidity Concepts section of the course. I would encourage you to have a go and post your solution code for all the course assignments, because this is a good opportunity to practise, and to get some helpful feedback on your progress and some suggestions :slight_smile:

Hi @markzachary,

… and welcome to the forum! … I hope you’ve been enjoying the course! :slight_smile:

The solution you’ve posted inherits 2 other contracts, so can we see those as well, so that we can fully understand how it works and deploy and test it if necessary? Unless we can see the parent contracts, it’s also difficult to make comments or provide you with feedback with any degree of certainty.

Having said that, I assume that ethBank4 must be what we’ve been calling Ownable, and ethBank2 what we’ve been calling Bank, right?

Have you tried testing your contract? … If you have, and assuming I’m right in my assumptions about the parent contracts, you should have found a few problems that need solving…

(1)  The contract owner will never be able to destroy the contract, because the line of code with the selfdestruct method is unreachable. On compiling your code you should get an orange warning that notifies you about this. It’s unreachable, because a function will finish executing after a return statement.

Have you completed all of the other course assignments? If you haven’t, I would strongly suggest that you do, and that you post your solutions, because that will help you practice and become aware of these sorts of concepts. It will also give you a chance to get some useful feedback about your progress before moving on to more advanced topics.


(2) During testing, after several different transactions have been performed — involving the deposit, withdraw and transfer functions, and resulting in several users holding shares of the total contract address balance — you will notice that when the contract owner calls the destroy() function, they only receive their share of the remaining contract balance, and not all of the remaining funds. This is because the withdraw() function (which I assume is inherited) is being called with a withdrawal amount equivalent to the contract owner’s individual balance in the mapping, and not the total contract address balance.

In fact, you don’t even need this line of code …

As well as destroying the contract, selfdestruct also automatically transfers the remaining contract balance to the payable address argument it is called with (in our case, the contract owner’s address). This is all part of the pre-defined selfdestruct functionality, and is why there is no need to include an extra line of code to perform this transfer of funds.


(3) Before making any amendments for the above issues, the return statement is currently returning the contract owner’s total external address balance (showing in the Account field near the top of the Deploy & Run Transactions panel in Remix) after the withdrawal of their individual share of the total Ether held in the contract, but before the close transaction’s gas cost has also been deducted.

However, after making the necessary modifications for (1) and (2) above, even if this return statement is corrected to return the total remaining contract address balance, it should still be removed, because …

  • If it’s placed before selfdestruct, it will prevent selfdestruct from executing (as already discussed above);

  • If it’s placed after selfdestruct, the return statement itself cannot execute, because once selfdestruct has been executed, the contract will have been destroyed and its code removed from the blockchain, and so the return statement will actually no longer exist, anyway.


I would also suggest that the contract we were calling Bank should be the most derived contract in your inheritance structure, the contract that we actually deploy, and not Destroyable. Once you’ve corrected your close() function for the above issues, even though your exisiting inheritance structure will now produce the desired result when Destroyable is deployed, one important reason for using inheritance is to create re-usable modules of code. And by having Destroyable inherit Bank, we are considerably reducing the re-usability of Destroyable, because instead of other contracts just being able to implement its “off-the-shelf” contract destruction functionality, together with the contract ownership functionality which Destroyable also inherits from Ownable, these other contracts will also have to inherit all the additional “baggage” from Bank, most of which they probably won’t need.

So, see if you can also come up with an alternative inheritance structure, which increases overall modularity and re-usability.

Let me know if anything is unclear, if you need any further help adapting your solution, or if you have any questions :slight_smile:

2 questions on addresses… the owner address is the address that deploys the contract? how is the contract address generated?

1 Like

What is the base contract?
The parent contract.
Which functions are available for derived contracts?
Can access all non private members including state variables and
internal methods.
What is hierarchical inheritance?
Similar to simple inheritance except a single contract acts as
a base contract for multiple derived contracts.

2 Likes

1.) The base contract, also known as the parent contract, is a contract containing reusable Solidity code that can compose a multitude of other contracts on top of itself.
2.) The functions available for derived contracts are: single inheritance, multi-level inheritance, hierarchical inheritance, and multiple inheritance.
3.) Hierarchical inheritance is an event in Solidity where a single base contract has the ability to derive multiple contracts based on its own reusable code.

1 Like
  1. The base contract is the parent contract whose byte code is copied by the Solidity compiler into derived contract byte code. This generates a single address that can be shared through inheritance between parent-child contracts.
  2. Functions available for derived contracts include all public and internal scoped functions and state variables.
  3. Hierarchical inheritance is where a single contract acts as a base contract for multiple derived contracts.
1 Like

Welcome to the forum @BD1726! … I hope you’re enjoying the course :slight_smile:

In our example contract it is, but that’s only because we have included a constructor which assigns the address that deploys the contract to the owner state variable. This is, however, a common feature in Solidity smart contracts.

The address that deploys the contract (our owner address) is the one showing in the Account field (near the top of the Deploy & Run Transactions panel in Remix) when the orange Deploy button is clicked.

The following link explains quite well the mechanics behind this …

https://ethereum.stackexchange.com/q/760

In Remix, you will find a shortened version of the Ethereum address your contract has been deployed at, after the contract name in the Deployed Contracts section at the bottom of the Deploy & Run Transactions panel. You can copy-and-paste the full address by clicking on the icon on the right.

Let me know if you have any further questions.

Hi @Baz,

Q1 & Q3 :ok_hand:

Just to clarify …

… it’s single not simple inheritance. There is an error in the article, here. This type of inheritance is correctly termed single inheritance in all the other places it’s mentioned in the article.

Q2

You’re halfway there …

This is correct for state variables in the parent contract, because state variables have either public, internal, or private visibility, and it’s those that are either public or internal which can be accessed from within the derived contract.

But what do you mean by members? This is a programming term, but it seems to be used to mean different things by different people and in the context of different programming languages.

The only functions which aren’t inherited are those with private visibility. In other words, public, internal and external functions are all inherited.

However, only the parent contract functions with public or internal visibility are available to be called from within the derived contract. Functions with external visibility are only inherited to the extent that they will be available to call externally e.g. from Remix, a web3 client (such as the front-end interface of a dapp), or from an external (i.e. non-derived) contract.

This is correct, if by method you mean function . However, the important point to make for functions is what I’ve already mentioned above, that it’s the public and internal functions in the parent contract which are available to be called from within the derived contract.

All modifers and event declarations in the parent contract are also inherited and available for derived contracts.

Just let me know if anything is unclear, or if you have any questions.

Hi @gunnarseay,

Q1 & Q3 :ok_hand:

Just to confirm …

Yes … if by “can compose other contracts on top of itself” you mean “can be inherited by other contracts”. When a derived contract is compiled, the code it inherits from the base contract is incorporated into the same, single set of bytecode, which can then be deployed at a single Ethereum address (contract address).

This is correct, except that I wouldn’t describe hierarchical inheritance as an event, as this is the name of a very different, specific type of functionality in Solidity. Instead, hierarchical inheritance is a specific type of inheritance structure. You’ve correctly described the relationship between the base and derived contracts in this particular type of inheritance structure.

However, I wouldn’t say that it represents an ability of the base contract to do something, because a base contract is potentially able to form part of any type of inheritance structure. Instead, hierarchical inheritance is a term used for a specific type of inheritance relationship between contracts which has already been defined in code.


Q2

No … single, multi-level, hierarchical and multiple inheritance are different types of inheritance structure (inheritance relationships between base and derived contracts).

It is the visibility of a function in the base contract, which determines if it is inherited by the derived contract or not. Functions with public, internal and external visibility are all inherited. However, only those with public or internal visibility are available to be called from within the derived contract. Functions with external visibility are only inherited to the extent that they will be available to call externally e.g. from Remix, a web3 client (such as the front-end interface of a dapp), or from an external (i.e. non-derived) contract.

Let me know if anything is unclear, or if you have any questions about any of these points.

Ownable.sol

// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.13;

contract Ownable {
    address payable public owner;
    constructor() {
        owner = payable(msg.sender);
    }
    modifier onlyOwner() {
        require(owner == msg.sender, "This functionality is limtied to the contract owner");
        _;
    }
}

Destroyable.sol

// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.13;

contract Destroyable {
    modifier destroy() {
        selfdestruct(payable(msg.sender));
        _;
    }
}

Bank.sol


// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.13;

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

contract Bank is Ownable, Destroyable {

    event BankClosed(string closingReason);
    
    mapping(address => uint) balance;
    
    event depositDone(uint amount, address indexed depositedTo);
    
    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);
        uint previousDepositorBalance = balance[msg.sender];
        balance[msg.sender] -= amount;
        payable(msg.sender).transfer(amount);
        assert(balance[msg.sender] == previousDepositorBalance - amount);
        return balance[msg.sender];
    }
    
    function getBalance() public view returns (uint){
        return balance[msg.sender];
    }

    function closeBank() external onlyOwner destroy {
        emit BankClosed("The bank is now closd to prevent hacks");
    }
}
1 Like
  1. What is the base contract?
    The root contract from which children contracts will derive
  2. Which functions are available for derived contracts?
    functions declared as internal will be available for derived contract
  3. What is hierarchical inheritance?
    the children contracts can access the functions declared as internal in the base
    contract
1 Like
  1. What is the base contract?

It is a parent contract (derived class).

  1. Which functions are available for derived contracts?

Public and internal functions are available for derived contracts (child contract).

  1. What is hierarchical inheritance?

Multiple child contracts inherit one parent contract.

1 Like
  1. What is the base contract?
    The parent contract is known as a base contract

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

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

1 Like
  1. What is the base contract?
    A: Its the parent contract.

  2. Which functions are available for derived contracts?
    A: All functions are available for the derived contracts

  3. What is hierarchical inheritance?
    A: Its where the parent contact has multiple children contract.

2 Likes
  1. A parent contract
  2. All the functions
    3.like parents having a children
1 Like

Hi Filip,

I have a question, it’s probably really straight forward.

Assume I launch my own smart contract, a token for example. I then inherit code from openzeppelin on github. What would happen to my smartcontract if openzeppelin deletes it’s account or changes all the files.

Or does the blockchain inherits once all the code?

thanks!

1 Like
  1. What is the base contract?
    the contract has a parent known as the derived class and the parent contract is known as a base contract .

  2. Which functions are available for derived contracts?
    There is a is a relationship between base and derived contracts and all public and internal scoped functions and state variables are available to derived contracts .

  3. What is hierarchical inheritance?
    Hierarchical inheritance is again similar to simple inheritance. Here, however, a single contract acts as a base contract for multiple derived contracts.

2 Likes

Ownable.sol

pragma solidity 0.8.7;

contract Ownable {
    address payable public owner;

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

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

}

Destroyable.sol

pragma solidity 0.8.7;

import "./Ownable.sol";

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

Bank.sol


import "./Destroyable.sol";

contract Bank is Destroyable {
...
2 Likes

Hello,

So I have a question. For some reason, when I add the code into the transfer function to send a copy of the transaction to the government contract, it makes the transfer function no longer work properly. No matter what I do it keeps reverting and showing an error message. I have uploaded a screenshot of the error message and pasted my full code below. Any help would be greatly appreciated, thanks!

Bank Contract

pragma solidity 0.7.5;

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

contract Bank {

   GovernmentInterface governmentInstance = GovernmentInterface(0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2);

    mapping(address => uint) balance; 

    event depositDone(uint toAdd, address depositedTo); 

    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(amount <= balance[msg.sender], "You can't withdraw more than your posted balance");
       msg.sender.transfer(amount); 
       balance[msg.sender] -= 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, "Insuficient Balance"); 
       require(msg.sender != recipient, "Cannot Transfer to Yourself"); 

       uint previousSenderBalance = balance[msg.sender];    

       _transfer(msg.sender, recipient, amount);

       governmentInstance.addTransaction(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; 
    } 

}

Government Contract

pragma solidity 0.7.5;

contract Government {

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

    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);
    }
}

1 Like

Im not sure why its failing for you, i have just copy/paste your contracts, deploy the government contract first, copy the address, paste it on your bank contract and deployed it.

I was successfully able to transfer without any error, and I just copy/paste your contracts, maybe try to restart the process (delete all deployments, deploy again and test again).

Carlos Z

1 Like