Inheritance Assignment

import “./Ownable.sol”;
pragma solidity 0.7.5;
contract Destroyable is Ownable
{
function destroy() public onlyOwner
{
address payable receiver = msg.sender;
selfdestruct(receiver);
}
}

1 Like

Hello;

hopefully this is correct:

contract destroyable is ownable {
    function close() public onlyOwner { 
  selfdestruct(owner);  
    }
}

Take care

An excellent assignment solution, and overall three very well and accurately coded contracts @Deini-Atakan :muscle:

Your implementation of 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.

And well done for realising that you needed to make the owner address payable , so that selfdestruct() is called with a payable address and, therefore, any remaining ether in the contract can be paid/transferred to the owner when selfdestruct() is triggered.

Your fundsTransfered event and corresponding emit statement are also well coded. The emit statement is in the correct position within the transfer function, and it logs relevant information when a call to the transfer function has executed successfully.

The only code that seems to be missing are the 2 lines for the assert statement in the transfer() function… but maybe you had this in earlier versions of your Bank contract and it just got lost somewhere along the way :relaxed:

You’re making great progress, Atakan!

1 Like

Hi @Kippie,

Your code doesn’t compile because of the missing semi colon at the end of your import statement…

You should have got a red compiler error for this, and the error message explains what the problem is. Apart from this, your Destroyable contract is well-coded, but it does rely on you having made an additional change in Ownable. When the selfdestruct() function is called it should automatically transfer the ether balance remaining in the contract to the address it is called with. For this to happen, the address (in your case, owner ) needs to be a payable address. As you haven’t converted owner to a payable address locally, within the boom() function body, we do also need to see your Ownable contract to confirm whether owner is payable or not when it is passed to selfdestruct().

We also need to see what modifications you’ve made to the start of your Bank.sol file, and Bank contract header, for the inheritance. This is a key part of your solution, because the aim of this assignment is for our Bank contract to inherit both the contract destruction and ownership functionalities, so that both are available when we just deploy Bank i.e. we should be able to deploy Bank, perform some transactions, then destroy the Bank contract, transferring any remaining ether balance to the caller of the selfdestruct function (here, the contract owner).

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

Thanks for these kind words, it really encourages me. I had the assert function in my code but as you said, I removed it for some reason that I don’t remember right now, should I add it back? I understood the importance of that function but is it more like a safety precaution or is it only just to be sure?

1 Like

So I first created an ownable contract to specify that only the owner of the contract can call the call the destroy() function.

pragma solidity 0.7.5;

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

Then created the destroyable contract which inherits from the ownable contract in order to take owner as a parameter in the selfdestruct() function

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

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

And lastly created a sample contract to destroy that inherits from the destroyable contact and therefore also inherits from the ownable contract.

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

contract myContract is Destroyable{
    
    function hello(string memory input) public pure returns(string memory){
        return input;
    }
    
}

My main doubt on this assignment was the payable functionality added to the owner address. I understand that the selfDestruct() function should take a payable address as an argument because the error messages on Remix alerted me about this, but otherwise no idea.

Also I was reading on one of the posts from @Guy and found it quite interesting (if I understood correctly)that you could also simply pass msg.sender as an argument to the selfdestruct() function, an skip the ownable contract all together (at least for the destroyable contract).

2 Likes

thanks jon i’m going to do it again and impliment those changes. btw sometimes i read the error message and understand other times i’m confused on what it is suggesting

1 Like

import “./ownable.sol”;
pragma solidity 0.5.12;

contract Bank is Ownable {

 function Deposite2() public onlyOwner{
   person (msg.sender);
   }

}

Hi Atakan,

It’s not mandatory… it was more of an example of how to code an assert statement. Whether you include one or not depends on you own risk assessment, in other words… what are the consequences of the “impossible” happening?

That’s the main thing… as long as you have it somewhere as an example of how to implement an assert statement.

I think I’ve answered that above :slight_smile:

Do let me know if you have any further questions about assert(). Again, the details surrounding when exactly you need to use it can be quite complex, and requires more experience to really have a good feel for it. What we are aiming for by the end of this 101 course is just to understand what the syntax is, and how to code an assert() statement correctly within the context of a particular function. Like the secrets of inheritance, the secrets of assert will gradually be revealed in time :mage:

1 Like

Nice Destroyable contract @Alex9230,

Can we also see what modifications you’ve made to the start of your Bank.sol file, and Bank contract header, for the inheritance? This is an important part of the solution, because the aim of this assignment is for our Bank contract to inherit both the selfdestruct() and contract ownership functionalities, so that both are available when we just deploy Bank i.e. we should be able to deploy Bank, perform some transactions, then destroy the Bank contract, transferring any remaining ether balance to the caller of the selfdestruct function (here, the contract owner).

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

Hi @Klem,

Your Destroyable contract is looking good, but it does rely on you having made an additional change in Ownable. When the selfdestruct() function is called it should automatically transfer the ether balance remaining in the contract to the address it is called with. For this to happen, the address (in your case, owner ) needs to be a payable address. As you haven’t converted owner to a payable address locally, within the close() function body, we also need to see your Ownable contract to check whether owner is payable when it’s passed to selfdestruct().

A couple of other points about your Destroyable contract:

  • In Solidity, it’s standard practice to start the names we give to contracts with a capital letter. This also applies to structs and events. Following conventions such as these consistently, makes it easier to read and manage code.

  • Is Destroyable in a separate file? If so, did you include the import statement?

We also need to see what modifications you’ve made to the start of your Bank.sol file, and Bank contract header, for the inheritance. This is a key part of your solution, because the aim of this assignment is for our Bank contract to inherit both the contract destruction and ownership functionalities, so that both are available when we just deploy Bank i.e. we should be able to deploy Bank, perform some transactions, then destroy the Bank contract, transferring any remaining ether balance to the caller of the selfdestruct function (here, the contract owner).

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

1 Like

Hi Joseph,

Apologies for my delayed response regarding your Inheritance Assignment. Your post was in the Transfer Assignment discussion topic, but I’m replying here so that we can continue in the appropriate place :slight_smile:

Your Bank contract correctly inherits Destroyable :ok_hand:
And both of your contracts compile :+1:

If you now deploy Bank in Remix, you will see that your destroy_contract function is available to call, so we know that it has been successfully inherited by the derived contract Bank :ok_hand: All of this is correct… but if you call destroy_contract() with an address that is not the contract owner, you will notice that they are able to destroy the contract. Any address can do this, because you have not restricted access to destroy_contract() to the owner address. Hopefully, you can see that, even though the remaining funds can only be transferred to the contract owner, we still don’t want anyone to be able to initiate the destruction of the contract in the first place.

This is correct, except that we are deploying a derived contract (Bank), not an inherited (base) contract (Destroyable). In terms of the part I’ve highlighted, bear in mind that selfdestruct() is triggered by calling destroy_contract(), so this access restriction needs to be applied to destroy_contract().

You already have the onlyOwner modifier, so you just need to apply it to destroy_contract(). You will then be able to see that if an address other than the owner’s tries to call this function and trigger selfdestruct(), the require() statement in the modifier will fail and the transaction will revert.


What you have done is absolutely fine, Joseph. Sorry, if I’ve confused you. The only thing that needs to be corrected in your current solution is what I’ve just mentioned above.

In terms of the alternative… before this assignment, you had 2 files: Bank and Ownable. Bank was the derived contract and it inherited Ownable (the base contract). It is always good practice to separate out code with a distinct functionality, especially when this functionality has the potential to be reused, because this will avoid code duplication. This is the reason behind why we placed all of the contract ownership functionality within a separate base contract. You can probably see that the code in Ownable could easily be inherited by other derived contracts, which are very different to Bank, but which still need to implement the same contract ownership functionality.

For this assignment, we can leave the code related to contract ownership (owner address state variable, constructor, and onlyOwner modifier) in Ownable, and just create a 3rd contract, Destroyable, which only contains the selfdestruct() functionality. Again, the reasoning behind this is that contract destruction is a separate type of functionality, which we may not want to always implement together with the contract ownership functionality. With your current solution, this flexibility isn’t an option for us — we either have to inherit both together, or neither.

The inheritance relationship between the 3 contracts can be structured as multi-level inheritance, as follows:

contract Ownable {...}

contract Destroyable is Ownable {...}
// needs to inherit: owner address state variable, and onlyOwner modifier

contract Bank is Destroyable {....}
// This doesn't need to change from what you have now

If you pass owner to selfdestruct() then, with this 3-contract solution, you will still need to make it a payable address.

As always, just let me know if anything is unclear, or if you have any further questions :slight_smile:

function close() public onlyOwner { //onlyOwner is custom modifier
  selfdestruct(owner);  // `owner` is the owners address
}

Your Ownable and Destroyable contracts are excellently coded @Ernest_SG :muscle:

Your implementation of a multi-level inheritance structure is the best approach, because it is more streamlined and, as you say, myContract only needs to explicitly inherit Destroyable, because it will implicitly inherit Ownable via Destroyable.

Yes, you are correct that selfdestruct() must be called with a payable address, and you have ensured this by defining the owner address state variable in Ownable as payable. The reason for this is because, as well as destroying the contract, selfdestruct() automatically transfers any ether remaining in the contract to the address it is called with. This ensures that any funds remaining in the contract are not lost and are transferred to an address that is considered secure e.g. to the contract owner. You cannot see this play out with your derived contract, because it doesn’t handle any ether.

However, if you make your Bank contract a derived contract of Destroyable, and then deploy Bank, perform some transactions so that Bank’s contract address has an ether balance, and finally have the contract owner call your destroy() function … you will see the owner’s wallet address balance increase by an amount equal to the ether held in the contract at the moment it was destroyed.

Yes… selfdestruct() is triggered when the destroy() function is called, and the onlyOwner modifier restricts access to this function to the contract owner. Therefore, msg.sender can only be the owner address, and so passing msg.sender is equivalent to passing owner. Prior to Solidity v.0.8 msg.sender is payable by default, and as we are using v.0.7.5 in this course, that’s why we can pass msg.sender to selfdestruct() without having to explicity convert it to a payable address…

selfdestruct(msg.sender);

However, from v.0.8, msg.sender is non-payable by default and so you’d have to explicitly convert it to payable e.g.

selfdestruct(payable(msg.sender));

This isn’t correct though. We still need to ensure that only the owner address can call the destroy() function and trigger selfdestruct(); and that means Destroyable still needs to inherit the onlyOwner modifier from Ownable. In addition, without the onlyOwner modifier being available to restrict access to destroy() to the contract owner address, msg.sender would no longer only be able to reference the owner address. And if you look again at @Guy’s solution, even though he calls selfdestruct() with msg.sender instead of owner, his equivalent of our Destroyable contract (which he calls SelfDestruct) does still inherit Ownable:

Just let me know if you have any further questions about any of these points.

You’re making great progress, Ernest, and I’m sure you’ll really enjoy and get a lot out of the 201 course which follows this one :smiley:

Yes, I do understand what you mean @jahh  But that will gradually change as you gain more experience. That’s why it’s important to always look at the error messages and try to work out what they mean, even if there are some you don’t understand yet — that’s also why I gave you some additional information :wink:
By the way, you can also try doing occasional searches for some of the error messages you don’t understand…

Waaa! Many doubts solved in one go mate, thanks a lot for addressing all of them. I am eager to start with the next part of this course, but first gottta do the multisig wallet assignment. Looks like promising evolution, and all thanks to your help and the great courses. Cheers :fist_right:

2 Likes
pragma solidity 0.7.5;

import "./Ownable.sol";

contract Destroyer is Ownable{
    
    function killswitch() public onlyOwner {
        selfdestruct(msg.sender);
    }
}

Then Ownable contains:

pragma solidity 0.7.5; 

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

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

And Bank.sol has:

pragma solidity 0.7.5;

import "./Destroyer.sol";

contract Bank is Destroyer {

I have the killswitch function in the Bank contract when deployed, however it executes even when called from another account, and it doesn’t kill the contract.

Making the selfdestruct address payable fixes the problem of allowing other accounts to call the function killswitch() but still doesn’t kill the contract.

Also, the error message I get when calling killswitch() from another contract is puzzling:

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

Can you tell me why?

1 Like

that’s a great suggestion thank

1 Like

Hi @cryptoforyou,

I’m not really sure what you’re trying to do here…

Have you accidently called this contract Bank instead of Destroyable?

What is this? Where is the function person() than you are calling, and where is selfdestruct()?

The aim of this assignment is for our Bank contract (the one we’ve been developing during this course) to inherit both the contract destruction and contract ownership functionalities, so that both are available when we just deploy Bank i.e. we should be able to deploy Bank, perform some transactions, then destroy the Bank contract, transferring any remaining ether balance to the caller of the selfdestruct function (here, the contract owner).

When you’ve made the necessary changes, please post all of the contracts in your inheritance structure, because that makes it easier to review.

Let me know if anything is unclear, or if you have any questions about what you need to do for this assignment :slight_smile:

Hi @michasuen,

Can we see your complete code for the file which contains this function? Otherwise we can’t see how you’ve coded the inheritance relationship?

Your close() function looks good, but it does rely on you having made an additional change in Ownable. When the selfdestruct() function is called it should automatically transfer the ether balance remaining in the contract to the address it is called with. For this to happen, the address (in your case, owner ) needs to be a payable address. As you haven’t converted owner to a payable address locally, within the close() function body, we also need to see your Ownable contract to check whether owner is payable when it’s passed to selfdestruct().

We also need to see what modifications you’ve made to the start of your Bank.sol file, and Bank contract header, for the inheritance. This is a key part of your solution, because the aim of this assignment is for our Bank contract to inherit both the contract destruction and ownership functionalities, so that both are available when we just deploy Bank i.e. we should be able to deploy Bank, perform some transactions, then destroy the Bank contract, transferring any remaining ether balance to the caller of the selfdestruct function (here, the contract owner).

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