Inheritance Assignment

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:

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

contract Destroyable is Ownable {

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

contract Bank is Destroyable {

1 Like

Contract for destroying

contract Destroyable{


    function Destroy() public    {
        selfdestruct(msg.sender);
    }


}

Ownable Contract

contract Ownable{

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

}

Modified bank contract (check the last few lines of code)

// SPDX-License-Identifier: MIT
pragma solidity 0.7.5;
import "./Destroyable.sol" ;
import "./Ownable.sol";

contract Bank2 is Destroyable, Ownable{

  mapping(address => uint)Balance;
  
  event DepositDone(uint Amount, address DepositedTo);
  event TransactionDetails(address sender, address receiver, uint amount);
     
  

    function Deposit() public payable returns(uint){

        Balance[msg.sender] = Balance[msg.sender] + msg.value; //Adds ether to the contract
        emit DepositDone(msg.value, msg.sender);
        return Balance[msg.sender];
    }

    function Withdraw(uint amount) public OnlyOwner returns(uint){
        require(amount <= Balance[msg.sender]);

        msg.sender.transfer(amount); //transfers the Ether in the smart contract to the selected address

        Balance[msg.sender] = Balance[msg.sender] - amount;

        return Balance[msg.sender];

        
    }

    function transfer(address recipient, uint amount) public{

        require(Balance[msg.sender] >= amount, "Balance not enough");
        require(msg.sender != recipient, "You can't send funds to yourself!");

        uint BalanceBefore = Balance[msg.sender];

        _transfer(msg.sender, recipient, amount);

        assert( Balance[msg.sender] == BalanceBefore - amount);

         emit TransactionDetails(msg.sender , recipient, amount);

        //calls the transfer function, and takes the parameters of msg.sender(owner of an address), recipient and an amount
    }

    function _transfer(address from, address to, uint _amount) private{

        Balance[from] -= _amount;
        Balance[to] += _amount;

        //Subtracts balance from the sender and adds it to the recipient

    }
    
    function getBalance() public view returns(uint) {

        return Balance[msg.sender];

    }


    function DestroyContract() public OnlyOwner {
        Destroy();
    }
    
}

Hi @Giupi,

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

However, you haven’t taken full advantage of your inheritance structure, and you’ve included a lot of code which isn’t necessary. The same objectives can be met in a much more concise way …

(1) By giving your destroy() function in Destroyable internal visibility, it is inherited and available within Bank, but cannot be called externally by the contract owner. To resolve this you’ve added an additional public function to Bank which in turn calls destroy(). However, an important reason for using inheritance is to reduce code duplication, and if you change the visibility of the destroy() function from internal to public, it will be inherited and available to be called from both within Bank and externally from Remix. You could also give it external visibility, which would enable it to be called externally from Remix, but not from within Bank, which for the purposes of this assignment would be enough. This removes the need for the additional destroyContract() function in Bank.

destroy() will now appear as a function-call button in the Remix panel, instead of destroyContract()

Additionally, by keeping all of the code and functionality related to contract destruction within a single, separate contract (Destroyable), you will improve the modularity and re-usability of your code (other important reasons for using inheritance) and keep your code more organised.

(2)

These lines of code in your destroyContract() function achieve the desired result, and from a technical point of view they are well coded. However …

  • Firstly, as the total contract balance is being transferred to the owner’s external address, and the contract then destroyed, there is no point in updating the mapping for this, in order to then be able to use the withdraw() function to transfer the funds. The following refactored code achieves the same result much more concisely …
     msg.sender.transfer(address(this).balance);
  • Secondly, and more importantly, 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 perform the extra steps that you describe here …

So, in summary, and as a result of all of the above points, instead of the two functions you currently have (destroy and destroyContract) all you need is the following one in Destroyable…

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

A couple of additional observations …

(3)

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). Your destroyContract() function doesn’t receive Ether: by calling withdraw() it deducts Ether from the contract address balance and sends it to an external address. Your destroy() function will also do the same when you change its visibility.

The method selfdestruct in your destroy() function does require a payable address argument. This is so it can transfer the remaining Ether in the contract to this receiving address when the contract is destroyed. You have already ensured that this address argument (owner, in your case) is payable, by defining the owner state variable in Ownable as a payable address.

(4) In your withdraw() function, it’s important to modify the contract state by reducing the individual user’s balance in the mapping by the withdrawal amount …

balance[msg.sender] -= amount;

before actually transferring the funds out of the contract to the external address…

msg.sender.transfer(amount);

This is to prevent what is known as a re-entrancy attack from occuring after the transfer, but before the user’s individual balance (effectively, the share of the total contract balance they are entitled to withdraw) is reduced to reflect this operation. You’ll learn more about this type of attack, and how the above order of operations helps to prevent it, in the courses which follow this one.

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

Hi @CryptoUsic,

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 :ok_hand:

Your solution works if your Destroyable contract is in the same file as Bank. However, better practice is to place each contract in their own separate file, so that Destroyable, as well as Ownable, can be re-used as an independent module. That would also mean adding an import statement to Bank.sol to import Destroyable.sol.

Also, calling selfdestruct() with msg.sender directly …

selfdestruct(msg.sender);

… is a more concise alternative to using the additional receiver variable …

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.

Just let me know if you have any questions.

1 Like

Hi @Adrian1,

Your solution is almost there, except for one very serious flaw. If you deploy your Bank contract, you will notice that there are 2 functions that can be called from Remix to destroy the contract and transfer the Ether remaining in the contract to the calling address …

  1. DestroyContract() in Bank, which only allows the contract owner to destroy the contract and receive all of the remaining funds :ok_hand:

  2. Destroy() in Destroyable can also be called directly from Remix because it has public visibility, and so is inherited by Bank and can also be called by an external address. Not only is this unnecessary code duplication, but also, more seriously, any address can call this function, destroying the contract and transferring all of the remaining funds to their own external address (not the contract owner’s), because calling this function directly bypasses the onlyOwner restriction which is only applied to DestroyContract() :warning:

One easy fix would be to change the visibility of the Destroy() function in Destroyable from public to internal. This would still allow it to be inherited, but only available to call from within Bank. This means that only the contract owner would be able to call DestroyContract(), and DestroyContract() could then still call Destroy(), destroying the contract and transferring the remaining funds to msg.sender (the caller of the DestroyContract function, which can only be the owner).

However, this would still leave you with unnecessary code duplication, and reducing code duplication is an important reason for using inheritance. So, if you keep Destroy() public and add the onlyOwner modifier to this function instead, you can remove DestroyContract() from Bank altogether. You’ll also need to change your inheritance structure, so that onlyOwner is available in Destroyable and not just Bank. But this will mean that Destroy() is now the only function available to call from Remix which can trigger selfdestruct, but this time only the contract owner will be able to perform this operation, destroy the contract and receive the remaining contract balance.

Additionally, by keeping all of the code and functionality related to contract destruction within a single, separate contract (Destroyable), you will improve the modularity and re-usability of your code (other important reasons for using inheritance) and keep your code more organised.


A couple of additional comments …

  • Always include the pragma statements for each file in your posted solutions. Some students choose to use different Solidity versions, and so it’s always best to confirm which version you’re using by including the pragma statements.

  • You’ve 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!

Just let me know if anything is unclear, if you have any questions, or if you need any help modifying your code for these points.

Hey Filip,

ownable.sol

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

destroyable.sol

import "./ownable.sol";

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

bank.sol

import "./destroyable.sol";

contract Bank is Destroyable {
//...

Initially I worked out sending by creating close() function in bank.sol:

function close() public onlyOwner {
        payable(owner).transfer(address(this).balance);
        super.destroy();
}

But then I read a bit more and realized selfdestruct(owner) actually sends all funds to owner, so that is simpler and works good far as I tested.

I have question…since there is payable “deposit” function in Bank contract. After contract is destroyed any sending there will not “revert” and will take/burn funds sent…that’s sort of understandable but…
…would it be good to make some kind of checks/fallbacks on payables that so we know if there’s active contract and maybe revert transaction? Or that is not possible, since contract won’t do any code when functions are called.
I understand from our frontend we can disable calls - but people still may interact with contract directly. If you can clarify that part and maybe how that’s done.

Thanks,
Peg

1 Like

Hi Jon, thanks for your response, I found it really helpful and I modified the contract according to your suggestions. Everything is a lot clearer now, I’m looking forward to more feedback on the following assignments. :smiley:
Thanks again,
Giupi.

1 Like

Hi @peg … welcome to the forum! I hope you’ve been enjoying this course :slight_smile:

First of all … your final version is a good solution :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.

Yes … 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 the extra line of code …

Including your additional close() function in Bank does also achieve the desired result, and from a technical point of view it’s well coded. However, an important reason for using inheritance is to reduce code duplication. By giving the destroy() function in Destroyable public visibility, it will be inherited and available to be called from both within Bank and externally from Remix. You could also give it external visibility, which would enable it to be called externally from Remix, but not from within Bank, which for the purposes of this assignment would be enough. This removes the need for the additional close() function in Bank. If you deploy your Bank contract with the additional close() function included, you will notice that both close() and destroy() can be called by the contract owner from Remix. Both of these functions will produce exactly the same result, and so this also demonstrates why close() is unnecessary code duplication.

Additionally, by keeping all of the code and functionality related to contract destruction within a single, separate contract (Destroyable), you will improve the modularity and re-usability of your code (other important reasons for using inheritance). It also keeps your code more organised, and makes it more readable and easier to manage.

By the way, if we did need to call a function inherited from Destroyable from the derived contract Bank, we wouldn’t need to use super (or the Destroyable contract name) as you have …

You can call the function using just its name:   destroy();

We only need to prepend the function name with super. or the contract name, e.g. Destroyable.destroy() , if there are functions with the same name in different contracts within the inheritance structure (polymorphism) and we need to call an overriden function; and super is only needed when an overriden function cannot be called using the contract name, because its contract is on a different “branch” of the same overall inheritance structure. For more detail on this, and a coded example, see the following section of the Solidity documentation (specifically, the bottom part with the 2nd and 3rd code examples) …

https://docs.soliditylang.org/en/latest/contracts.html#inheritance


Well observed! :+1:

This is a serious disadvantage of using selfdestruct, the potential repercussions of which need to be fully understood before choosing to implement it. As a result of this, if a smart contract deployed on the mainnet is ever destroyed, all users need to be notified and informed that they must not send any more funds to that contract address, otherwise (as you have discovered) those funds will be lost.

Obviously, this is hardly an ideal situation!

You are right that this isn’t possible, because once selfdestruct() has been triggered, even though function calls can still be made from the web3 client (i.e. the frontend), the smart contract itself has been destroyed and its code removed from the blockchain; from this point the contract state ceases to exist. The zeros that are returned and, more seriously, any Ether that is sent and lost, are the unwanted side-effects of making external calls to a destroyed (and now non-existant) contract.

But, you are thinking along the right lines :ok_hand: In practice, other types of security mechanisms, such as pausable or proxy contracts, can be implemented to deal with these kinds of emergency situations.

Instead of destroying the contract, which is permanent and irreversible, pausable contracts are able to provide a range of more nuanced solutions, where…

  • an authorised address can both pause and unpause a contract; and
  • by using two different modifiers, specific functions can be activated or deactivated depending on whether the contract is currently paused or unpaused.

The following blog post sets out and explains the reasons why a pausable contract has many advantages over the more “primitive” selfdestruct

https://blog.b9lab.com/selfdestruct-is-a-bug-9c312d1bb2a5

OpenZeppelin Contracts (see their GitHub repository, and README file) provides a smart contract library which includes a “ready-made” Pausable contract:

https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.3.0/contracts/security/Pausable.sol

The functionality provided by OpenZeppelin in this library can be implemented by inheriting the relevant contracts.

Even though using selfdestruct() has clear disadvantages, for the purposes of this introductory course it provides a simple and straightforward way to practise implementing inheritance, which is our main objective in this assignment. Pausable contracts involve more advanced techniques and are therefore out of the scope of this introductory course. So, don’t worry if you don’t fully understand everything discussed in the blog post, or all of the information and code in the other links to OpenZeppelin Contracts. You will learn about pausable contracts and other smart-contract emergency and security features, such as proxy contracts, if you go on to take the Ethereum Smart Contract Security course. The 201 course will also introduce more advanced concepts and techniques which will help you to more fully understand and work effectively with this material.

Just let me know if you have any further questions … and keep up the research :muscle:

1 Like

Wow Jon thanks a lot for this detailed walkthrough and explanation - especially that “paused” openzeppelin contract I’m gonna definitely look into it - much appreciated… :beer:

Cheers!

1 Like

You’re very welcome @peg! … you’re asking some great questions! When you’ve got the time, I think you’ll find the info in the links really interesting and helpful.

1 Like
contract Ownable{
  address payable public owner;

  modifier onlyOwner(){
    require(msg.sender == owner, "Not the owner of the contract!");
    _; // Run the function
  }
  constructor(){
    owner = msg.sender;
  }
}

contract Destroyable is Ownable{

  function killMyself() public onlyOwner{ 
    selfdestruct(owner); 
  }

}

contract Bank is Destroyable{
1 Like

Hi @tbem,

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 :ok_hand:

I’m assuming you must have included a pragma statement at the top of your file, otherwise it won’t compile and Bank can’t be deployed. If so, then your solution works, but only if your 3 contracts are all in the same file, because you haven’t included any import statements. However, better practice is to place each contract in their own separate file, so that Ownable and Destroyable can be re-used as independent modules to incorporate their specialised contract ownership and contract destruction functionalities. That would also mean adding an import statement to Destroyable.sol to import Ownable.sol, and to Bank.sol to import Destroyable.sol.

Let me know if you have any questions :slightly_smiling_face:

here is my scram function :slight_smile:

pragma solidity ^0.8.0;

import './Owneable.sol';

contract Destroyable is Owneable {

    function scram() public onlyOwner {
        selfdestruct(payable(owner));
    }

}
1 Like

contract Destroyable is Ownable{

function hakai() public onlyOwner{
selfdestruct(payable(owner));
}

}