Inheritance Assignment

Destruction derby contract:

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

contract Destroyable is Ownable {

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

}
What if someone else would deploy a Ownable.sol contract with exact same functionality :
pragma solidity 0.5.12;

contract Ownable{

address public owner;

constructor () public {
    // is only called when contract is created
    owner = msg.sender;
}

modifier onlyOwner(){
    require(msg.sender == owner);
    _; // tells solidity to continue execution
}

}

Could he/she then be the owner of the parent contract in the eyes of the EVM? and thus be allowed to call the destroyContract function or withdrawAll function and get all the ethers stored in the contract?

1 Like

Actually if I use the destroyContract function, the transaction is successful but the contract still seems partly working, for instance withdrawAll function still works?

Also do I need to deploy the ownbale and destroyable contracts or they just need to be imported in the base contract? When compiling the parent contract, it compiles all of them but it seems ownable and destroyable contracts do not need to be deployed?

1 Like

Each “ownable” contract can be equal (in code) for each smart contract app, but the deployed address is different, meaning that even when someone deploy the same Ownable contract that you code, it can’t manage your contract, because your contract have his own “ownable” contract with an specific address.

You contract should be completely destroyed after you use the “destroyContract” function, meaning any other function of it should not work anymore.

By deplying the parent contract, it will automatically deploy all the inherited contracts for your.

For example:

Contract A is B { }

Contract B is C {}

Contract C {}

If you deploy Contract A, it will automatically inherit and deploy B and C, because they are part of Contract A logic.

If you have any more questions, please let us know so we can help you! :slight_smile:

Carlos Z.

// SPDX-License-Identifier: GPL-3.0
import "./Ownable.sol";
pragma solidity >=0.7.0 <0.8.0;

contract Destroyable is Ownable {
    function destroy(address payable receiver) public onlyOwner {
        selfdestruct(receiver);
    }
}
1 Like

import “./ownable.sol”;
pragma solidity 0.5.12;
contract Destroyable is Ownable {

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

}

1 Like
//  4_hello_world_file.sol
import "./7_my_ownable.sol";
import "./8_my_destroyable.sol";
pragma solidity 0.5.12;

contract HelloWorldSmartContract is MyOwnable, MyDestroyable {
...
}

// 7_my_ownable.sol
pragma solidity 0.5.12;

contract MyOwnable{
    address public owner;
    uint public balance;

    modifier onlyOwner(){
        require(msg.sender == owner, "Only the owner may do this!");
        _; //Continue execution
    }    
    
    constructor() public{
        owner = msg.sender;
    }    
}
//  8_my_destroyable.sol
import "./7_my_ownable.sol";
pragma solidity 0.5.12;

contract MyDestroyable is MyOwnable {

    function close() public onlyOwner {
      require(balance == 0, 
         "You cannot destroy this contract when there is a remaining balance. Please call withdrawAll() before!");
      address payable payableOwner = address(uint160(owner));
      selfdestruct(payableOwner);    }

}
1 Like

Hi @Su.kal.Crypto,

Your Destroyable contract with selfdestruct() function is correct :ok_hand:
But if you pass owner as the argument to selfdestruct() instead of msg.sender , then you also need to either make an additional modification to contract Ownable, or add some additional code to contract Destroyable.

Do you know what this is, and why?

And what changes did you also have to make to HelloWorld in terms of inheritance?

Nice work, again @Laurenzius :ok_hand:

Just a couple of observations:

When selfdestruct is called by the owner, any remaining contract balance will be transferred to the address argument it is called with — in this case the same owner. You have added a constraint requiring that the balance has already been reduced to zero, by the owner calling withdrawAll() beforehand. Because only the owner can call withdrawAll, this is exactly what the selfdestruct function will do anyway. So the require statement is redundant and can be removed.

Also, have a look at this post — it explains how you can streamline the inheritance by having HelloWorld just inherit Destroyable. I think you’ll find it interesting :slight_smile:

1 Like
pragma solidity 0.5.12;

contract Ownable {
    
    address payable owner;
    
    modifier ownerOnly() {
        require(msg.sender == owner, "Operation requires owner of the contract");
        _;
    }
    
    constructor() public {
        owner = msg.sender;
    }
    
    
}
import "./Ownable.sol";

pragma solidity 0.5.12;

contract Destroyable is Ownable {
    
    function destroy() public ownerOnly {
        selfdestruct(owner);
    }
    
}
import "./Destroyable.sol";

pragma solidity 0.5.12;

contract HelloWorld is Destroyable {

    // there is no need to have Ownable and Destroyable both in the list of parent contracts

}
1 Like

Excellent solution @ricoto :muscle:

Just one comment…
In contract Ownable, you are absolutely right to make the owner state variable payable. However, it is good practice to still indicate the visibility (either public or internal) e.g.

address payable internal owner;

My idea was to utilize default visibility for state variables (internal for 0.5.12 ? ). I agree that it’s better to set explicit visibility.

1 Like

contract Destroyable is Ownable {

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

}

contract HelloWorld is Destroyable {

}

1 Like

Hi @bdoherty,

Your Destroyable contract with selfdestruct() function is correct :ok_hand:
But if you pass owner as the argument to selfdestruct() instead of msg.sender , then you also need to either make an additional modification to contract Ownable, or add some additional code to contract Destroyable.

Do you know what this is, and why?

Also, don’t forget that you need to import the file containing contract Ownable into Destroyable, and the file containing contract Destroyable into Hello World.

1 Like

Hi @ricoto,

You make a valid point about the default.

This got me doing a bit of research, and default visibility for state variables (internal) and functions (public) appears to be last documented for Solidity v0.4.x.
https://docs.soliditylang.org/en/v0.4.26/contracts.html#visibility-and-getters
However, from v0.5.x this same section of the documentation no longer mentions a default visibility…
https://docs.soliditylang.org/en/v0.5.0/contracts.html#visibility-and-getters
… and under Breaking Changes for v0.5.0 it only states that explicit function visibility was introduced then…
https://docs.soliditylang.org/en/v0.7.5/050-breaking-changes.html#explicitness-requirements
… whereas I can’t find any equivalent guidance in the documentation for v.0.5.x, v.0.6.x or v.0.7.x for state variable visibility in terms of whether internal visibility should now be explicity stated for state variables as well as for functions. And the Remix compiler does appear to support this, by defaulting to internal visibility for state variables when using any of these 3 versions.
However, as you suggest…

… being explicit, rather than relying on defaults, is almost certainly better practice, and makes for clearer code.

1 Like

created new contract Destroyable:

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

contract Destroyable is Ownable{
    
    function destroyContract() public onlyOwner {
        selfdestruct(msg.sender);
    }
}

added to main contract:

import "./Destroyable.sol";
pragma solidity 0.5.12;

contract HelloWorld is Destroyable{

...
1 Like

Created a new contract called Destroyable and had Hello-World inherit from it:

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

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

1 Like

Hi @BlockchainAnarchist

Your Destroyable contract with selfdestruct() function is correct :ok_hand:
But if you pass owner as the argument to selfdestruct() instead of msg.sender , then you also need to either make an additional modification to contract Ownable, or add some additional code to contract Destroyable.

Do you know what this is, and why?

Hey @jon_m,

Yes! I realized there was an issue after I submitted this answer. If I were to leave owner in the destroy() function as it is, it would not work because selfdestruct requires a payable address as a parameter. Owner is not a payable address as it stands in my submitted solution.

To fix this, I could either create the owner variable in the Ownable contract as a payable address:

address payable internal owner;

Or I could edit the destroy function in the Destroyable contract to convert owner to a payable address before using it as a parameter in selfdestruct:

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

Please let me know if this is what you were looking for!

1 Like
// "SPDX-License-Identifier: AGPL-3.0"
pragma solidity > 0.6.0 <= 0.7.0;

import "./IvanOwnable.sol";

contract Destroyable is IvanOwnable {
        
    function lightTheFuse() internal onlyOwner {
        selfdestruct(msg.sender);
    }
}
// "SPDX-License-Identifier: AGPL-3.0"
pragma solidity > 0.6.0 <= 0.7.0;
// !! NOT PRODUCTION READY !! pragma experimental ABIEncoderV2; // required to allow a struct to be returned

import "./IvanOwnable.sol";
import "./IvanDestroyable.sol";

contract HelloWorld is IvanOwnable, Destroyable {
...

    function destroy() public onlyOwner {
        withdrawAll();
        lightTheFuse();
    }
1 Like

Perfect @BlockchainAnarchist :muscle:
… and you’ve explained the alternatives really well :slight_smile: