Inheritance Assignment

Ownable.sol

contract Ownable {

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

     constructor(){
        owner = msg.sender;
    }

}```

Destroyable.sol

import "./Ownable.sol";

contract Destroyable is Ownable {

    function destroy() public onlyOwner {

        selfdestruct(msg.sender);
    }
}

Bank.sol

pragma solidity 0.7.5;

import "./Destroyable.sol";

contract Bank is Destroyable {
1 Like

pragma solidity 0.7.5;

contract Ownable{

address public owner;

constructor(){

    owner=msg.sender;

}

modifier onlyOwner{

    require(msg.sender== owner);

    _;

}

}

contract Destroyable is Ownable{

function destroyContract() public payable{

 require ( owner == msg.sender, "You are not the owner");

 selfdestruct(msg.sender);

}

}

contract Bank is Destroyable{

mapping(address => uint)balance;

// This function will deposit money to the contract

function deposit() public payable returns(uint){

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

    return balance[msg.sender];

   

    }

//this function will withdraw the amount from the contract to the address calling this function    

function withdraw(uint amount)  public  {

    require(amount <= balance[msg.sender],"Balance is not sufficient");

    msg.sender.transfer(amount);

    balance[msg.sender]-=amount;

   

}

//this function will get the balance of address executing this function

function getyourBalance() public view returns(uint){

    return balance[msg.sender];

}

//this function will get you the total balance of ether in your contract

function CheckBalanceofContract() public view returns(uint){

    return address(this).balance;

}

}

1 Like

Bank contract

pragma solidity 0.7.5;
import "./Ownable.sol";
import "./Destroyable.sol";

contract Bank is Ownable, Destroyable {
    
    mapping(address => uint) balance;
    
    event depositDone(uint amount, address indexed depositedTo);
    
    function deposit() public payable returns (uint)  {
        balance[msg.sender] = balance[msg.sender] + msg.value;
        emit depositDone(msg.value, msg.sender);
        return balance[msg.sender];
    }
    
    function withdraw(uint amount) public onlyOwner returns (uint){
        require(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, "You do not have enough ETH");
        require(msg.sender != recipient, "Error");
        
        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;
    }
}

Ownable

pragma solidity 0.7.5;

contract Ownable {

    address owner;

   

    modifier onlyOwner {

        require(msg.sender == owner);

        _;

    }

   

    constructor(){

        owner = msg.sender;

    }

}

Destroyable

pragma solidity 0.7.5;

import "./Ownable.sol";

contract Destroyable is Ownable {

  function destroy() public onlyOwner {

    selfdestruct(msg.sender);

  }

}
1 Like

Another excellent solution, @LELUK911 :muscle:

Your implementation of a multi-level inheritance structure is the best approach. Bank_Eth only needs to explicitly inherit Self_destroyed, because it will implicitly inherit Owner via Self_destroyed.

You have converted msg.sender to a payable address where the Solidity v0.8 syntax you are using requires this …

The setOwner() function you’ve added to the Owner contract is well coded and appropriate. You have correctly commented that the owner variable cannot be marked immutable if the owner address can be changed with the setOwner() function after deployment. Otherwise, you could definitely define owner as immutable, as your alternative code suggests, although it would also still need to be marked payable…

address payable public immutable owner;

… unless you convert owner to a payable address only when it needs to be, within the destroyed() function itself e.g.

function destroyed() public OnlyOwner {
   selfdestruct(payable(owner));
}

Let me know if you have any questions … and keep up the great coding! :slight_smile:

1 Like

An excellent solution @Techman120 :muscle:

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

Nice solution @JaimeAS :ok_hand:

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

Don’t forget to specify the required compiler version for each separate file, by including a pragma statement in each one. This may just be a copy-and-paste error when posting your code, but if it isn’t, the compiler should have generated orange warnings for omitting these.

It is not good practice to omit the pragma statement from any Solidity file, because we should always specify the Solidity compiler version (or range of versions) which our code is written to be compatible with.
Because the Bank contract inherits functionality from Destroyable and Ownable, even if the pragma statements are omitted from either or even both of their files, Remix will still attempt to compile Bank as a single contract together with the code it inherits. Bank will compile and deploy successfully as long as the code in both of the inherited contracts is compatible with the Solidity version declared in the pragma statement at the top of the file containing Bank. However, the compiler still generates orange warnings, highlighting that it’s best practice to include pragma statements in each file.
In the absence of a pragma statement, it seems that Remix will always try to find a suitable compiler version, although this doesn’t seem to be 100% reliable.
So, in conslusion, always include a pragma statement at the top of every .sol file.

Let me know if you have any questions :slight_smile:

Hi @imran82,

Your solution works and meets all of the assignment objectives :ok_hand: Your implementation of a multi-level inheritance structure is also the best approach: Bank only needs to explicitly inherit Destroyable, because it will implicitly inherit Ownable via Destroyable.

A few observations:

(1) Your destroyContract() function should not be marked payable, because it doesn’t need to receive Ether from an external address to add to the contract address balance.
It might help to think of payable 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.
In contrast, the destroyContract() function triggers the selfdestruct method which, in addition to destroying the contract, also deducts the total amount of Ether from the contract balance and sends it to the payable address argument (msg.sender) which selfdestruct is called with, and which in our case can only ever be the contract owner’s address.

(2) You don’t need to include the require() statement in your destroyContract() function …

… because the inherited onlyOwner modifier is already available within Destroyable to add to the destroyContract() function header, and thereby prevent any address other than the contract owner from executing this function, destroying the contract, and receiving any Ether still remaining in the contract.

You could, however, add the error message "You are not the owner" to the require() statement in your onlyOwner modifier.

// Suggested improvements to address the points made in (1) and (2) above
function destroyContract() public onlyOwner {
    selfdestruct(msg.sender);
}

(3) By including all three contracts within the same file, your solution works with only one pragma statement, and without having to add any import statements. However, best practice is to place each contract in a separate file — no matter how small any of them are — each with its own pragma statement, and to import the parent contract file(s) where this is necessary for the successful implementation of the inheritance structure. Doing this makes your code more modular and, therefore, also more re-usable and easier to develop further. Improved modularity and code re-usability are both key reasons for choosing to implement inheritance.

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

Hi @Arch.xyz,

Your code meets the assignment objectives. Your inheritance structure is well coded, but Bank only actually needs to explicitly inherit Destroyable, because it will inherit Ownable implicitly via Destroyable. This will give you a multi-level inheritance structure.

However, your withdraw() function is no longer adjusting the user’s individual balance in the mapping, because it no longer includes   balance[msg.sender] -= amount;
This is obviously a serious bug, and needs to be corrected.

You’ve also modified the withdraw() function with the onlyOwner modifier, but 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: Your withdraw() function header didn’t include the onlyOwner modifier before. I think during the course we were 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 solution code for this assignment by mistake!

Let me know if you have any questions :slight_smile:

Destroyable.sol

pragma solidity 0.8.7;

import "./Ownable.sol";

contract Destroyable is Ownable {

    function destroy() public onlyOwner{

        selfdestruct(payable(msg.sender));

    }

}

bank contract.sol

pragma solidity 0.8.7;

import "./Ownable.sol";

import "./Destroyable.sol";

contract Bank is Ownable, Destroyable {
1 Like

Hi @Wyse_Sousa,

Your code meets the assignment objectives. Your inheritance structure is well coded, but Bank only actually needs to explicitly inherit Destroyable, because it will inherit Ownable implicitly via Destroyable. This will give you a multi-level inheritance structure.

Because you’re using Solidity v0.8, you have correctly converted msg.sender to a payable address when passing it as an argument to selfdestruct …

As you may already be aware, from Solidity v.0.8 msg.sender is no longer payable by default, which means it has to be explicity converted to a payable address when necessary e.g. when passed to selfdestruct as its payable address argument.
The transfer method also needs to be called on a payable address. So, if you haven’t done so already, you also need to change the following line of code in your withdraw function in Bank …

// from
msg.sender.transfer(amount);

// to Solidity v0.8 syntax
payable(msg.sender).transfer(amount);

Just let me know if you have any questions.

1 Like

Destroyable.sol

pragma solidity 0.7.5;

import "./ownable.sol";

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

Ownable.sol

pragma solidity 0.7.5;

contract Ownable {
    
    address public owner;

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

Bank.sol

pragma solidity 0.7.5;

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

contract Bank is Ownable, Destroyable 

My bank contract does not appear in the contract section above deploy, so I am not able to deploy the bank contract.

Ownable.sol

pragma solidity 0.7.5;

contract Ownable{
    
    address public owner; 
    modifier onlyOwner{ 
           require(msg.sender == owner);
           _;
    }

    constructor(){
        owner = msg.sender; 
    }

}

Destroyable.sol

pragma solidity 0.7.5;

import "./Ownable.sol";

contract Destroyable is Ownable{

    function close () public onlyOwner{
        selfdestruct(msg.sender);
    }
}

Bank.sol(header)

pragma solidity 0.7.5;

import "./Destroyable.sol";

contract bank is Destroyable, Ownable{

So I played around with this for a few hours, I got it to the way I liked it, but I kept getting an error at the self destruct line. So I went online trying to see if something was off with my syntax. I ended up adding some lines from the blog to see if that changed things, but then I started getting new errors on the constructor and the address payable private line. Can you see what my problem is in here? My guess is it’s redundant, but when I tried taking certain lines out I still got error messages. Here is the code below:

Ownable Contract Code:

pragma solidity 0.7.5; //SPDX-License-Identifier: SPDX-License-Identifier; unlicensed//

contract Ownable {

address owner;

modifier onlyOwner{

    require (msg.sender == owner);

    _; //run the function

}

}

Destroyable Contract Code:

pragma solidity 0.7.5; //SPDX-License-Identifier: SPDX-License-Identifier; unlicensed//

import “Ownable.sol”;

contract Destroyable is Ownable {

 address payable private owner;

 uint256 number

constructor(){

   owner = msg.sender;

}

function store(uint256 num) public {

number = num;

}

function retrieve() public view returns (uint256){

return number;

}

function close() public payable {

    selfdestruct(owner);

}

}

Bank Contract Code:

pragma solidity 0.7.5; //SPDX-License-Identifier: SPDX-License-Identifier; unlicensed//

import “Destroyable.sol”;

contract Bank is Destroyable{

Thanks so much for all the awesome feedback and insights @jon_m!! Reading through all of this now and updating my contracts :blush: The level of detail is truly really helpful for me, I really appreciate it.

1 Like

Hey @jon_m — thanks again for all the help above! Sorry for the delay, been heads deep in work the past month but now getting my Academy studies back on track :sweat_smile:

Made updates to my code, would you be able to check these over to let me know if I’m on the right track on this? I totally saw what you mean know about how the onlyOwner modifier was on the withdraw function…I was wondering why I could only withdraw as the contract deployer but that’s why :blush: I’ve updated everything below, so looking forward to hearing what you think.

Bank.sol

// SPDX-License-Identifier: None

pragma solidity 0.7.5;

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

contract Bank is Ownable, Destroyable {

    mapping(address => uint) balance;
    
    // define event
    event depositDone(uint amount, address indexed depositedTo);

    event balanceTransfered(uint amount, address indexed transferedFrom, address transferredTo);

    event balanceWithdrawn(address indexed withdrawnFrom);

    
    // for anyone to deposit
    function deposit() public payable returns (uint) {
        balance[msg.sender] += msg.value; // to internally track who deposited money
        // add event 
        emit depositDone(msg.value, msg.sender);
        return balance[msg.sender]; 
    }

    // function for folks to withdraw eth to their address
    function withdraw(uint amount) public returns (uint) {
        require(balance[msg.sender] >= amount); // prevents withdraw of others' money
        msg.sender.transfer(amount); // run the transfer function
        balance[msg.sender] -= amount; // remove amount from balance before transfer occurs
        emit balanceWithdrawn(msg.sender);
        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);

       // governmentInstance.addTransaction(msg.sender, recipient, amount); 

       // add event 
       emit balanceTransfered(amount, msg.sender, recipient);

       assert(balance[msg.sender] == previousSenderBalance - amount);
    }

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

}

Destroyable.sol

// SPDX-License-Identifier: None

pragma solidity 0.7.5;

import "./Ownable.sol";

contract Destroyable is Ownable {
 
    function destroy() public onlyOwner { 
        address payable receiver = msg.sender;
        selfdestruct(receiver); 
    }
}

Ownable.sol

// SPDX-License-Identifier: None

pragma solidity 0.7.5;

// way to inhert from contract ownable for owner functionality
contract Ownable {
    
    address internal owner;

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

    // sets the owner as owner of contract (msg.sender)
    constructor() {
        owner = msg.sender;
    }

}
1 Like

Hi @MichaelAudire,

That’s because your Destroyable contract isn’t compiling, and so your Bank contract isn’t compiling either. If Bank isn’t compiling you can’t deploy it, so that’s why it’s not appearing in the Contract field.

You are probably getting a compiler error that says either contract ownable.sol, or contract Destroyable.sol , has not been found. This is because your file names in the import statements are inconsistent in terms of whether they start with an upper- or lower-case letter. You need to make sure that they are exactly the same as your actual file names …

but

and

Once you have corrected this, Bank still won’t compile because of another error in Destroyable, in your close() function …

If you read the compiler error for this line, you will see that your owner argument in the selfdestruct() function call needs to be explicitly converted to a payable address. This is because the owner state variable in your Ownable contract is defined as a non-payable address; but the method selfdestruct requires a payable address argument to be able to transfer the remaining Ether in the contract to this address when the contract is destroyed.

You can easily fix this is several different ways. Which would you choose? If you’re not sure, have a look at some of the other students’ solutions, and the feedback and comments they’ve received, posted here in this discussion topic. You will find a lot of useful information here.

If you post your corrected code, I’ll take another look at it for you. But just let me know if you have any questions, or if you need any more help :slight_smile:

Hi @Abel,

The code within the body of each contract (Ownable and Destroyable) is correct, but your inheritance structure is incorrectly coded in Bank. This means that your Bank contract won’t compile and so can’t be deployed.

What the compiler error is saying for this line of code is that the order of the inherited contracts is incorrect based on the parent-child inheritance relationship you’ve already defined between Ownable (parent) and Destroyable (child). So, if you change the order to …

contract Bank is Ownable, Destroyable { ... }

… then Bank will compile. However, including both contracts in the Bank contract header is unnecessary. Bank only actually needs to explicitly inherit Destroyable, because it will inherit Ownable implicitly via Destroyable. This will give you a multi-level inheritance structure…

contract Ownable { ... }
contract Destroyable is Ownable { ... }
contract Bank is Destroyable { ... } 

Once you’ve corrected this, you’ll be able to deploy Bank and perform the different transactions mentioned in the assignment instructions, finishing with the contract owner destroying the Bank contract and transferring the remaining contract balance to their own external address.

Just let me know if you have any questions :slight_smile:

Hi @Liquidbaker89,

Your implementation of a multi-level inheritance structure is well coded and is the best approach: Bank only needs to explicitly inherit Destroyable, because it will implicitly inherit Ownable via Destroyable :ok_hand:

Before you added all the additional code to Destroyable, this was probably because your owner state variable in your Ownable contract is defined as a non-payable address …

The method selfdestruct in your close() function requires a payable address argument. This is so it can transfer the remaining Ether in the contract to this address when the contract is destroyed. But you were calling selfdestruct with owner , which referenced a non-payable address. The compiler error for this line would have told you that your owner argument in the selfdestruct() function needed to be explicitly converted to a payable address.

Marking the close() function payable doesn’t make the owner argument payable. Only functions that receive Ether from an external address (in order to add it to the contract address balance) should be marked payable (e.g. the deposit function in Bank). The close() function doesn’t receive Ether, it deducts Ether from the contract address balance and sends it to an external address.

Instead, you can make the argument passed to selfdestruct a payable address in one of three ways:

(1) If we only need the contract owner’s address to be payable for the purposes of executing selfdestruct , we can leave the owner state variable in Ownable as a non-payable address type, and explicitly convert the address to payable locally, within the function where we actually need to use it as payable. We can even do this directly within the selfdestruct() function call itself, as follows:

 selfdestruct(payable(owner));

(2) Or you can define the owner address state variable as a payable address type by adding the payable keyword …

address payable owner;

You’ve done this by adding a duplicate owner state variable to Destroyable. Once you’ve added a semi colon to the end of …

… in order to correct the compiler error you’re getting on the following line (where the constructor is), you’ll then get a compiler error for …

This line throws an error, because Destroyable already inherits an owner state variable from Ownable. The error message tells you that owner has already been declared. So, you need to remove the owner state variable declaration from Destroyable and either make the one in Ownable payable, or explicity convert owner locally instead (as described in option 1 above). But if you define the owner state variable in Ownable as payable (option 2) you can’t also define it with private visibility, because then it won’t be inherited. The owner state variable should be declared within Ownable and not Destroyable, because it is a fundamental part of the Contract Ownership functionality and we want to aim for code which is modular and therefore re-usable. The constructor also needs to be within Ownable, otherwise the contract owner’s address will not be assigned to the owner state variable on deployment, and the onlyOwner modifier will also be useless.

(3) For security, we want to ensure that only the contract owner can call close() and trigger selfdestruct. Currently, your close() function has no such restriction. By applying the onlyOwner modifier, only the owner address will be able to call this function. This will also mean that msg.sender in this function will only be able to reference the contract owner’s address, giving us a third option of passing msg.sender to selfdestruct as its argument instead of owner…

selfdestruct(msg.sender);

If you post your corrected code, I’ll take another look at it for you. But just let me know if anything is still unclear, if you have any further questions, or if you need any more help :slight_smile:

Hi everyone,
a quick comment on the destroyContract() function, please feel free to share your thoughts.

I thought that after changing the owner’s balance, the function should have the owner withdraw() all the contract funds before calling destroy() and therefore destroying the contract, otherwise the funds will just be stuck in the contract and nobody would be able to access them.

Here is my solution:

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

contract Ownable {

    address payable public owner;

    constructor() {
        owner = msg.sender;
    }

    modifier onlyOwner {
        require(msg.sender == owner, " Operation allowed only to contract owner");
        _;
    }

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

import "./ownable.sol";

contract Destroyable is Ownable {

    function destroy() internal onlyOwner {
        selfdestruct(owner);
    }
}

And just the beginning of the Bank contract. Everything else is the same.

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

import "./destroyable.sol";

contract Bank is Destroyable {

    mapping(address => uint) balance;

    event withdrawal(address indexed client, uint amountWithdrawn, uint newBalance);

    function destroyContract() public payable onlyOwner {
        balance[owner] += (address(this).balance - balance[owner]);
        withdraw(balance[owner]);
        destroy();
    }

    function withdraw(uint amount) public returns (uint) {
        require(balance[msg.sender] >= amount, "Insufficient funds to withdraw");
        msg.sender.transfer(amount);
        balance[msg.sender] -= amount;
        emit withdrawal(msg.sender, amount, balance[msg.sender]);
        return balance[msg.sender];
    }
}
1 Like

Hey @InterstellarX,

You’re very welcome! I’m glad you found the feedback really helpful :muscle:

The modifications you’ve made mean that your Bank contract will now work as it should, and it meets all of the assignment objectives :ok_hand: Your inheritance structure is correctly coded, but now consider this comment …

i.e.

contract Ownable { ... }

import "./Ownable.sol";
contract Destroyable is Ownable { ... }

import "./Destroyable.sol";
contract Bank is Destroyable { ... }

You’ve correctly removed your duplicate close() function from Bank, but you didn’t need modify your original close() function in Destroyable …

Calling selfdestruct() with msg.sender directly, is a concise alternative to using the additional receiver variable which you’ve added to this function in your latest version …

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


withdraw() function in Bank

Anyone with funds in the contract can now withdraw their Ether :ok_hand:
But you’ve changed the order of the statements in the function body, which now makes the contract vulnerable to re-entrancy attacks…

Your comments are correct, but the second one now doesn’t make sense because you’ve changed the order…

Remember this?

Here is the original order of your statements in the withdraw() function body, which was correct in terms of reducing security risk …

You should also consider improving your balanceWithdrawn event …

And finally, regarding your emit statement for the transfer event … Generally speaking, it’s probably better to place an emit statement after an assert statement if there is one.


I hope you find these final comments useful as well :slightly_smiling_face: