Inheritance Assignment

Hi @Acex13,

Thanks for alerting us to this issue. It’s seems as though the website hosting the article has shut down, or something similar has happened. We’ve now found a suitable replacement article, and modified the assignment instructions accordingly. This update should be available for you to view on the Academy website within the next 48 hours.

Apologies for the inconvenience caused by this.

1 Like

Hi @ildevirdee,

Thanks for alerting us to this issue. It’s seems as though the website hosting the article has shut down, or something similar has happened. We’ve now found a suitable replacement article, and modified the assignment instructions accordingly. This update should be available for you to view on the Academy website within the next 48 hours.

Apologies for the inconvenience caused by this.

Hi @Klem,

Take your time to rest and recover… there’s no rush!

Anyway, here’s some further feedback on what you’ve posted… for whenever you’re ready…

Your Ownable contract is correct and you have defined the owner as a payable address. This means that calling selfdestruct() with owner in Destroyable is also correct :ok_hand:


Your Destroyable contract isn’t inherited.


Where exactly are your Ownable and Destroyable contracts? From what you’ve posted, Ownable seems to be below Bank in the same file, and Destroyable seems to be below the Government contract… but that can’t be right… If that’s the case, Destroyable won’t inherit Ownable, and Bank won’t inherit Ownable (if the base and derived contract are both in the same file, the base contract must precede the derived contract). I think it will be much clearer and more appropriate if you put Ownable and Destroyable in their own separate files, and add import statements to import each of them into their respective derived contracts.


You also have a couple of issues with your code relating to the Government contract and the external function call (in the transfer function in Bank) to addTransaction(). These issues will cause the external function call to fail, and transfer() to revert. However, as the Government contract and this external function call are not part of this Inheritance Assignment, I won’t comment on these issues here. I’m also not sure whether you’re still working on them. If you need help with this next part (which comes after this assignment), then post your questions and code in the discussion topic for this section here.

Ask away, whenever you’re ready :slight_smile:

1 Like

Thanks for the detailed feedback. I have figured it out in more detail now.
I expected the contract to close and disappear in Remix. Similar to what it does when I press the trashcan icon.
So this is very good to know, that actually the user can still call the contract at cost, but nothing happens.
I guess that means I should send an event in the case of a selfdestruct().

1 Like

*The link is not available.

*My solution:

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

import “./Ownable.sol”;
import “./SelfDestruct.sol”;

contract Bank is Ownable, SelfDestruct {
(…)
}

contract SelfDestruct is Ownable{
function destroySmartContract(address payable _to) public onlyOwner {
selfdestruct(_to);
//all remaining funds on the address of the Smart Contract are transferred to _to.
}
}

1 Like

Great job @loso :ok_hand:
… especially as the link no longer worked, and you have completed this assignment based on your own research :muscle:

Apologies for the delay in giving you feedback on this assignment, and also for the inconvenience caused by the link no longer working. It appears as though the website hosting the article has shut down, or something similar has happened. We’ve now found a suitable replacement article, and modified the assignment instructions accordingly. The assignment itself is still the same, but if you would like to read the new article and more comprehensive instructions, this update is now available for you to view on the Academy website.

Feedback on your solution

Well done for realising that the address passed to selfdestruct() needs to be payable, in order for any remaining ether in the contract to be paid/transferred to this address when selfdestruct() is triggered. Your method, calling destroySmartContract() with a payable address parameter, is perfectly correct. An appropriate address to send these remaining funds to would be the contract owner address. You can do this by passing either msg.sender, or owner (inherited from Ownable), to selfdestruct(), which also means destroySmartContract() no longer needs an address parameter.

You can use msg.sender because, as a result of the onlyOwner modifier, it can only ever be the contract owner address. Also, msg.sender is already a payable address by default (only before Solidity v0.8) and so doesn’t need to be explicitly converted.

However, if you pass owner you would need to:

  • either define it as a payable address state variable in Ownable;
  • or convert it locally within the destroySmartContract() function body, either using a local variable before it is passed to selfdestruct(), or convert the actual argument e.g. selfdestruct(payable(owner));

We can also streamline the inheritance structure by having Bank explicitly inherit Destroyable only, because it will inherit Ownable implicitly via Destroyable. This will give you a multi-level inheritance structure.

@Anvar_Xadja is also right about this…

Let me know if you have any questions about any of these points :slight_smile:

Nice solution @Anvar_Xadja :ok_hand:

Apologies for the delay in giving you feedback on this assignment, and also for the inconvenience caused by the link no longer working. It appears as though the website hosting the article has shut down, or something similar has happened. We’ve now found a suitable replacement article, and modified the assignment instructions accordingly. The assignment itself is still the same, but if you would like to read the new article and more comprehensive instructions, this update is now available for you to view on the Academy website.

Feedback on your solution

Well done for realising that the address passed to selfdestruct() needs to be payable , in order for any remaining ether in the contract to be transferred to this address when selfdestruct() is triggered. Your method, calling destroyContract() with a payable address parameter, is perfectly correct, and provides the flexibilty for the owner to transfer to any address of their choosing :ok_hand:

If we knew in advance that any remaining funds should only be transferred to the contract owner’s address (which would be an appropriate and secure restriction), then alternative solutions would be to pass either msg.sender , or owner (inherited from Ownable), to selfdestruct(), which also means destroyContract() no longer needs an address parameter. I’ve gone into further detail about how to implement these alternatives in my feedback to @loso (which I’ve also tagged you in).

If the emit statement is placed after the call to selfdestruct(), the event won’t be emitted because the contract has been destroyed once selfdestruct() has executed, and so the emit statement is never reached. You could fix this by placing the emit statement before the selfdestruct() function call, but I don’t think this is wise, because we would be emitting the event before the actual event (contract destruction) has ocurred. On the other hand, if selfdestruct() failed, then the function would revert, including the emitted event, but I would still be reluctant to place the emit statement first. This needs some further investigation. What are your initial thoughts on the position of the emit statement?

Can we also see what modifications you’ve made to the start of your Bank.sol file, and Bank contract header, for the inheritance? After all, Bank is the derived contract that we are actually deploying.

Let me know if you have any questions :slight_smile:

Hi Joseph,

Apologies for the late response!

Depending on the inheritance structure you’re using, the onlyOwner modifier is either already defined within your Destroyable contract, or is inherited by Destroyable from Ownable. All modifiers in a base contract are inherited by the derived contract.

Available modifiers are applied to functions by referencing the modifier’s identifier (name) in the function header, as follows:

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

It’s as simple as that :smiley:

Just let me know if anything is still unclear, or if you have any further questions.

Hi @ildevirdee,

Just to let you know, that the new article and updated assignment instructions are now available for you to view on the Academy website.

Once again, apologies for the inconvenience caused by this.

Hi Douglas,

Just to let you know, that the new article and updated assignment instructions are now available for you to view on the Academy website.

Once again, apologies for the inconvenience caused by this.

Hi @raphbaph,

Good to hear :slight_smile:

You raise an interesting point. I’ve been discussing the issues related to emitting an event for selfdestruct() with @Anvar_Xadja …

Apart from the above points, it’s also worth bearing in mind that emitting an event doesn’t necessarily mean that users will see that infomation, and so doesn’t actually prevent them from subsequently calling the destroyed contract’s functions, incurring a gas cost, and losing any ether sent. There would still need to be some form of communication to all users informing them of the contract’s destruction, and what the consequences and implications of this are in terms of what they must and mustn’t do from now on. This is also why, in practice, other types of security mechanisms, such as proxy contracts, are implemented to deal with these kinds of emergency situations. You will learn more about the practicalities of this if you go on to take the Smart Contract Security course.

Let me know if you have any questions :slight_smile:

Ownable Contract

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

Destroyable Contract

pragma solidity 0.7.5;

import "./Ownable.sol";

contract Destroyable is Ownable {

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

Bank Contract’s beginning

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

contract Bank is Ownable, Destroyable {
1 Like

Definitely would emit before self destruct. But interesting point about not being able to verify if contract then actually self destructed…

1 Like

This is the Ownable contract with the file name of InheritancePart2Ownable.sol

pragma solidity 0.7.5;

contract Ownable{
     mapping(address => uint)balance;
    
    address owner;
    
    event depositCompleted(uint amount, address indexed depositedTo);
    event transferCompleted(address indexed sentBy, uint amount, address indexed receivedBy);
    event withdrawalCompleted(address indexed withdrawTo, uint amount);
    event selfDestruction(address indexed balancePostDestroyedSentTo);
    event selfDestructionv2(address indexed balancePostDestroyedSentTo);
    
    modifier onlyOwner{
        require(msg.sender == owner);
        _; // run the function
    }
    
    constructor() {
        owner = msg.sender;
    }
    
}

This is the Destroyable contract with the file name of InheritancePart3SelfDestruct.sol that inherits Ownable

pragma solidity 0.7.5;

import "./InheritancePart2Ownable.sol";

contract Destroyable is Ownable {
    function selfDestruct() public onlyOwner payable {
    emit selfDestruction(msg.sender);
    selfdestruct(msg.sender);
    
    //test if emit will still run after selfdestruct invoked (it doesn't)
    emit selfDestructionv2(msg.sender);
    //Hypothesis: anything after selfdestruct doesn't run as the smart contract is effectively destroyed
    //But the showBalance still work as stated in https://betterprogramming.pub/solidity-what-happens-with-selfdestruct-f337fcaa58a7
}
}

For the Bank contract (just for the import and contract header mainly)

import "./InheritancePart3SelfDestruct.sol";

contract Bank is Destroyable{
   
    function deposit() public payable{
        balance[msg.sender]+=msg.value;
        emit depositCompleted(msg.value, msg.sender);
        
    }

Ive structured the inheritance whereby Ownable contract is the parent to Destroyable and Bank inherits from Destroyable somewhat making the relationship of the contracts to be Ownable>Destroyable>Bank which probably defines the relationship as a multi-level inheritance?

*Another question would be:

contract Bank is Destroyable, Ownable 

results in this error:
“InheritancePart1Bank.sol:7:1: TypeError: Linearization of inheritance graph impossible contract Bank is Destroyable, Ownable{ ^ (Relevant source part starts here and spans across multiple lines).”

whereas if:

contract Bank is Ownable, Destroyable

There isn’t a warning. Does that mean that the sequence of contract inheritance in the contract header is important to how the contract runs?

1 Like

@jon_m, sorry for not getting back to you sooner. I have been struggling to gain insight into other courses before my subscription is up. I will be taking a few days off from the course and in the meantime, I will be making progress elsewhere but I aim to return in a short while. Thank you very much for your time.

1 Like

No problem @JosephGarza, I totally understand — no need to apologise. Just pick things up with this course again when you’re ready. A short break, and focussing on something else can be just as beneficial as ploughing on when you get to that saturation point, which can happen at different stages with different courses. The last thing you need is to feel overly pressurised. Take care, and see you soon :slight_smile:

I don’t think the problem is that a user can’t verify if the contract has been destroyed or not, but rather that they would need to do this pro-actively. They wouldn’t be automatically prevented from calling one of the destroyed contract’s functions. The contract owner could notify users that the contract has been destroyed, via an alert in the frontend interface. This could perhaps be triggered by an event listener which listens for an emitted event, if it is decided to emit an event before selfdestruct() is actually called.

1 Like

Hi @jon_m how are you ? Hope you’re doing well.

I think I’ve figured it out, works in Remix so far:

Bank.sol

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] += 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);
        balance[msg.sender] -= amount;
        msg.sender.transfer(amount);
        return balance[msg.sender];
    }
    
    function getBalance() public view returns (uint){
        return balance[msg.sender];
    }
    
    function getOwner() public view returns (address) {
        return owner;
    }
    
    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);
        
        assert(balance[msg.sender] == previousSenderBalance - amount);
    }
    
    function _transfer(address from, address to, uint amount) private {
        balance[from] -= amount;
        balance[to] += amount;
    }
    
}

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 {
    function close() public onlyOwner { 
  selfdestruct(owner);  
    }
}

I’ve been playing around in Remix a little which answered my questions regarding the selfdestruct function.

For a while I was trying to have Destroyable.sol below Ownable.sol, but it didn’t work because of the onlyOwner modifier. I looked up to comma (contract Bank is Ownable, Destroyable), which was what was giving me issues, but it makes sense. If you have a complex inheritance, do you put a lot of commas ?

After some time when you go through the material over and over again and it becomes clearer and everything comes together it’s really cool !

Thank you for your help !

Best wishes, Klemen

1 Like

Hi Jon, i was hoping you could help me correct my understandin of a struct. i wrote out the code and tried to write my understanding for each line. Please let me know if my understanding is correct and if not i would love to get ur insights on it thank you

Structs - similar to object in java ,you can define what properties a person should have

pragma solidity 0.7.5;
contract Classes {
struct Person{ // (Ur defining what properties a person should have in this area)
uint age;
string name;
}
Person[]people; // This is a person array of people this will allow you to keep adding more than one person

function addPerson(uint _age, string memory _name) public {
Person memory newPerson = Person(_age, _name); // ur using the defination of a person here
people.push(newPerson); // Then you push the person into the array here
}
function getPerson( uint _index) public view returns (uint, string memory){ //we return the properties of the person

Person memory personToReturn = people[_index]; // here is were we get the person of the people array/ This is where we link the person to there index number eg: Tom will link to the [0] index, Juile will to the [1] index etc…

return (personToReturn.age, personToReturn.name); // This is how you access the properties of a struct
}
}

1 Like

Hi @jahh,

You’ve explained what your code is doing very well, and you appear to have a good understanding of how structs work in Solidity. Below is your original code and comments, with my own comments added…

pragma solidity 0.7.5;

contract Classes {

    struct Person { // (Ur defining what properties a person should have in this area)
        uint age;
        string name;
    }
    /* CORRECT -- we are effectively defining a new data type of type Person.
       The struct is like a template which, as you say,
       defines the identifier (name) and the data type of each property. */ 
    
    Person[] people; // This is a person array of people this will allow you to keep adding more than one person
    /* CORRECT -- again, the important thing here is that the array is defined with a Person data type.
       So, this array must hold struct instances based on the template defined in the Person struct. */

    function addPerson(uint _age, string memory _name) public {
        
        Person memory newPerson = Person(_age, _name); // ur using the defination of a person here
        // CORRECT -- we are creating a new Person struct instance, based on the template we have defined with the Person struct.
        // If we call addPerson() with the arguments: 25, "Bob" ...
        // ... the new Person struct instance created could be visually represented as {age: 25, name: "Bob"}
        // This new Person struct instance is stored temporarily in a local variable (newPerson) ...
        //  ... so we need to define this local variable with the Person data type.
        
        people.push(newPerson); // Then you push the person into the array here
                                // CORRECT
    }
    
    function getPerson(uint _index) public view returns (uint, string memory){ // we return the properties of the person
                                                                               // CORRECT

        Person memory personToReturn = people[_index]; // here is were we get the person of the people array
        // This is where we link the person to there index number eg: Tom will link to the [0] index, Juile will to the [1] index etc…
        // CORRECT -- we reference a specific Person instance in the people array ...
        // ... using the index number corresponding to their position within the array.
        // The specific Person instance we have referenced is assigned to, and stored temporarily in, a local variable (personToReturn) ...
        // ... so we need to define this local variable with the Person data type.
                   
        return (personToReturn.age, personToReturn.name); // This is how you access the properties of a struct
        // CORRECT -- we access (and then return) the individual property values of the Person instance using dot notation. 
    }
}