Inheritance Assignment

import "./ownable.sol"

contract destroyable is ownable {

	function SelfDestruct() public OnlyOwner {
		selfdestruct(owner);
	}
}
1 Like

Thanks Jon, yes, this makes sense. I will try to revise this exercise or maybe just do what was intended with the bank contract but it is very good feedback. I appreciate it.

1 Like

Ownable.sol code //Parent of Destroyable.sol

pragma solidity 0.7.5;

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

Destroyable.sol code //Parent of Bank.sol

pragma solidity 0.7.5;

import "./Ownable.sol";  // an important code to access Ownable.sol

contract Destroyable is Ownable { // an important code to access Ownable.sol

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

Bank.sol code //inherits both Ownable.sol and Destroyable.sol

pragma solidity 0.7.5;

import "./Destroyable.sol"; // an important code to access both Ownable.sol and Destroyable.sol

contract Bank is Destroyable {   // an important code to access both Ownable.sol and Destroyable.sol
...    
}

Short explanation of my answer:

This inheritance assignment is an example of a multi-level inheritance wherein Ownable.sol is the parent of Destroyable.sol and Destroyable.sol is the parent of Bank.sol. By virtue of multi-level inheritance, Bank.sol can access both Ownable and Destroyable contract.

P.S. You can check the diagram above made by @evasilev to understand further.

3 Likes

This is a good diagram man!

2 Likes

I believe the solution could be,

pragma solidity 0.7.5;

import “./Ownable.sol”;

contract Destroyable is Ownable {

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

}

Where Ownable.sol includes the code from the bank code example,

contract Ownable {

address public owner;

modifier onlyOwner{
    require(msg.sender == owner);
    _; //needed, it means run the function
}

constructor(){
    owner = msg.sender;
}

}

1 Like

Assuming that all the contracts located in the same folder, the code might be like that:

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

pragma solidity 0.7.5;
import "./Ownable.sol";
contract Destroyable is Ownable{
    function close() public onlyOwner {
// I should have specified the owner parameter:
//  address payable owner = msg.sender; 
selfdestruct(owner);}
}

pragma solidity 0.7.5;
import "./Destroyable.sol";
contract HelloWorld is Destroyable{....}
2 Likes

Hi @makinitnow,

The onlyOwner modifier (inherited from Ownable) restricts who can call a function (with onlyOwner in the header) to the owner of the contract. In our example, the owner of the contract is set using the constructor (also inherited from Ownable) to the address which initially deploys the contract. By placing the onlyOwner restriction on the selfdestruct function (inherited from Destroyable) only the contract owner (its deployer) can destroy it. This isn’t an unusual feature to find in smart contracts, because if we wanted to include the option for the contract to be destroyed (for risk management reasons, for example) then it makes sense that the ability to initiate this would be restricted to just one authorised individual or group of individuals.

1 Like

pragma solidity 0.7.5;

import “./Ownable”;

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

1 Like

just as quick question, lets say we wanted to use the functionality in the parent destroyable contract within the child bank contract, how would we do that? the modifier onlyOwner is inherited from Ownable and used in the function headers. But how would we trigger the function in destroyable? would we create a public function in Bank that has the onlyOwner modifier and uses a function instance of Destroy(). Can you give an example please?

Or is the destroy function available for the owner to click/invoke once the contract is deployed therefore unnecessary to reference it in Bank?

Hi @Bdos87

In order to access the close function(or selfdestruct) from bank contract, Back contract has to inherits to destroyable contract. it will be like this Back contract -> Destroyable contract -> Ownable contract and other way we can just created the close function inside the Bank contract we dont need to inherits the destroyable function, Because selfdestruct is available to any contract is build-in function.

Happy learning :grinning:
Abel

Destroyable.sol:

pragma solidity 0.7.5;

import "./Ownable.sol";

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

Also, in Bank.sol make sure it inherits from Destroyable, i.e:

contract Bank is Destroyable { ...
1 Like
Ownable.sol:
pragma solidity 0.7.5;

contract Ownable {
    address payable 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{

    // delete the deployed contract
    function close() public onlyOwner {
        selfdestruct(owner);
    }
}

3 Likes

selfDestrucr.sol

pragma solidity 0.7.5;

import "./ownable.sol";

contract Destroyable is Ownable {
    
    function close() public onlyOwner { //onlyOwner is custom modifier
        selfdestruct(msg.sender);  // `owner` is the owners address
    }
}

and in Helloworld.sol

import "./selfDestruct.sol";

contract BankOfNico is Ownable, Destroyable
2 Likes

pragma solidity 0.7.5;
import “Ownable.sol”;

contract Destroyable is Ownable {

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

}

/* to use
import “Destroyable.sol”;
contract HelloWorld is Destroyable{
}
*/

2 Likes

Here is an example of Destroyable base contract that allows the Bank contract to be destroyed, provided the caller is the owner.

It seems to work but I am a bit confused because Remix allows me to call “destroy” more than once. The second call does not fail but has a transaction cost of zero.

pragma solidity 0.7.5;



contract Destroyable{
    
    address payable owner;
    
    constructor(){
        owner = msg.sender;
    }
    
     function destroy() public {
        require( msg.sender == owner, "Only the owner can destroy this contract");
        selfdestruct(owner);
    }
   
}

contract Bank is Destroyable{
    
    
    mapping(address => uint) balance;
    
    
    event depositDone(uint amount, address indexed depositedTo);
    
    modifier onlyOwner {
        require(msg.sender == owner);
        _;
    }

    
    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, "balance not sufficient");
        msg.sender.transfer(amount);
    
        balance[msg.sender] -= amount;
        
        return balance[msg.sender];
    }
    
    function getBalance() public view returns (uint){
        return balance[msg.sender];
    }
}
1 Like

This is my solution to the problem. I changed the main smart contract to “is Destroyable”. I noticed that I got an error at the input of selfdestruct(owner)… I thought the variable owner is accessible, since it is public, but I noticed from the way others solved this, that I have to put msg.sender. Could you explain why is this? Since I’ve made the Destroyable contract “is Ownable”, shouldn’t it by rule of inheritance know what the variable “owner” is? Thank you very much, for the answer.

contract Ownable {
    
    address public owner;

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

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

contract Destroyable is Ownable {
    
    function destroyContract() public onlyOwner {
        selfdestruct(owner);
    }
}
1 Like

Hi @codinginlondon,

Good question! Your contract does work, but you are correct that even after the contract has been destroyed, the destroy function can still be called. In fact any of the other functions can also still be called, and gas will be consumed. However, the smart contract is inoperable and even though the functions can still be invoked they won’t have any effect (other than consuming gas). But the fact that users are not made aware that the smart contract has been destroyed, and can still make function calls that consume gas, is obviously a disadvantage with using selfdestruct. This is why selfdestruct is only meant to be invoked in extreme circumstances e.g. to retrieve funds held in the contract in the event of an attack, as a form of damage limitation. One way in which this disadvantage with selfdestruct() can be avoided is by adding code that will divert control of the contract in the event of selfdestruct being invoked.

One way in which your contract can be improved is to move the onlyOwner modifier from the derived contract to the parent contract. That way it can be used to restrict access to selfdestruct() in the parent contract, while still being available via inheritance in the derived contract, if needed. This avoids code duplication and the need to repeat the same require statement in both the modifier and the destroy function.

Also, have a look at this post which explains an important security modification that should be made to the order of the statements within your withdraw function body.

1 Like

Hi @ZigaP,

Good question! You are correct that as the state variable owner has public visibility it is inherited and accessible in the derived contract Destroyable. However the reason you are getting the error is not because of this.

The address argument passed to selfdestruct() is where the contract’s ETH balance is transferred to on destruction (so these funds aren’t lost). Because this address is used to receive a payment, it needs to be a payable address.

Addresses can be stored in variables as either payable, or non-payable. Unless we specifically mark them as payable, then they are non-payable by default. However, when msg.sender is used directly as a value (and not first assigned to a variable), it is treated as a payable address by default in Solidity, so that’s why  selfdestruct(msg.sender)  works without having to add anything further or make any additional modifications. If you do the same with owner the compiler will give you an error, because you currently have it defined as a non-payable address in contract Ownable:

address public owner;

You can still use owner but you will need to also define it as a payable address by explicity adding the payable keyword:

address payable public owner;

If you make this modification to the owner state variable definition in Ownable, then selfdestruct(owner); will work.

Another alternative is to leave the owner state variable definition as non-payable, and convert it to a payable address directly within the selfdestruct function call in contract Destroyable:

selfdestruct(payable(owner));

I hope that makes sense now, but do let us know if you have any further questions :slight_smile:

1 Like

I got it to work!..
3 files, 1 The contract to be destroy, 2 The Ownable, 3 The Destroyable

  1. Code for the Contract to be Destroyed…
    pragma solidity 0.7.5;
    import “./Ownable.sol”;
    import “./Destroyable.sol”;

contract Bank is Ownable, Destroyable {
mapping(address => uint) Balance;

function addBalance(uint _ToAdd) public onlyowner returns(uint){…}

}

  1. Ownable file…
    pragma solidity 0.7.5;
    contract Ownable {
    address owner;
    modifier onlyowner {
    require(msg.sender == owner, “only owner can run this function…”);
    _;
    }
    //We use constructor to initialize variables…
    constructor(){
    owner = msg.sender;
    }
    }

  2. The Destroyable contract…

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

contract Destroyable is Ownable {

function close() public onlyowner{
    
    selfdestruct(msg.sender) 
}   

}

1 Like