Inheritance Assignment

…also I hope you don’t mind but I’ve removed the interface, and the other lines associated with the government contract, from your post — as they are part of the next section, I thought it might confuse other students who may be reading these posts for this assignment :wink:

1 Like

I updated the contract header to include inheritance from Destructable.

contract Bank is Ownable, Destructable{
    
    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, "Insufficient Balance");
        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, "Insufficient Balance");
        require(msg.sender != recipient, "Cannot Send To Self");
        
        uint prevBalance = balance[msg.sender];
        
        _transfer(msg.sender, recipient, amount);
        
        assert(balance[msg.sender] == prevBalance - amount);
    }
    
    function _transfer(address from, address to, uint amount) private {
        balance[from] -= amount;
        balance[to] += amount;
    }
}
1 Like

Bank (Child Contract):

pragma solidity 0.7.5;

import "./Ownable.sol";

import "./Destroyable.sol";

contract Bank is Ownable, Destroyable{
    
   mapping(address => uint) balance;
   
   event deposited(uint amount, address indexed depositedTo);
   
   event transferProcess(address sender, address reciever, uint amount);
   
   modifier costs(uint price){
       require(msg.value >= price);
       _; // run the function
   }
   
   
   function deposit() public payable returns (uint) {
       
       balance[msg.sender] += msg.value;
       emit deposited(msg.value, msg.sender);
       return balance[msg.sender];
       
   }
   function getBalance() public view returns (uint){
       return balance[msg.sender];
   }
   
   function withdraw(uint amount) public onlyOwner returns (uint) {
       require(balance[msg.sender] >= amount, "You cannot overdraw.");
       
       balance[msg.sender] -= amount;
       
       msg.sender.transfer(amount);
       return balance[msg.sender];
   }
   
   function transfer(address recipient, uint amount) public {
       require(balance[msg.sender] >= amount, "Insufficient balance!");
       require(msg.sender != recipient, "Don't transfer money to yourself");
       
       uint previousSenderBalance = balance[msg.sender];
       
       _transfer(msg.sender, recipient, amount);
       emit transferProcess(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;
   }
}

Destroyable (Base Contract):

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

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

Hi @jon_m, I must have misunderstood the assignment. I thought it was enough to create a new contract by its own that could potentially be inherited by other contracts. I made the implementation with my bank contract and took your advice to make the code more modular by separating ownable and destroyable into two separate contracts. Thanks for some good advice!

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

contract Bank is Destroyable {
    
    mapping(address => uint) balance;
    
    constructor(address _address) Destroyable(_address) {}
    
    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 returns (uint){
        require(balance[msg.sender] >= amount);
        msg.sender.transfer(amount);
        balance[msg.sender] -= amount;
        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);
                
        assert(balance[msg.sender] == previousSenderBalance - amount);
    }
    
    function _transfer(address from, address to, uint amount) private {
        balance[from] -= amount;
        balance[to] += amount;
    }
    
}
pragma solidity 0.7.5;
import "./Ownable.sol";

contract Destroyable is Ownable {
    
    constructor(address _owner) Ownable(_owner) {}
    
    function destroy() public onlyOwner {
        selfdestruct(msg.sender);
    }
    
}
pragma solidity 0.7.5;

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

contract Destroyable{
    
    address payable owner;
    
    constructor(){
        owner = msg.sender;
    }
    
    modifier onlyOwner(){
        require(msg.sender == owner);
        _;
    }
    
    function destroy() public onlyOwner{
        selfdestruct(owner);
    }
}
1 Like
import "./Ownable.sol";
pragma solidity 0.7.5;

// create a new contract called Destroyable, which will allow any contract inheriting from it to call a function to self destruct. 
// Obviously, this action should only be available for the contract owner.

contract Destroyable is Ownable { //permanently deleted from the blockchain

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

Could you please help explain what is the difference if we change public into internal in “address public owner” as of below Ownable contract. Thanks

Edit: this will be answered in video about Internal Visibility coming right after this
Basically, public create automatically function get_owner _address. If we change it into internal and create a new function get_owner _address, it still works fine

pragma solidity 0.7.5;

// memory - temporary storage used in function execution
// calldata - ssave arguments/ input to our functions
// string, array, mapping, struct

contract Ownable {
    //state variables (contract scope)
    
    address public owner;  // if we change to internal????

    
    modifier onlyOwner{
        require(msg.sender == owner); //used to validate that the caller is the owner or validate inputs
        _;                           //_ means VM will run the function
    }
    
    constructor(){
        owner = msg.sender ;
    }
}    
1 Like

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

contract Destroyable is Ownable {

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

}

1 Like

pragma solidity 0.7.5;

import “./Ownable.sol”;

contract Destroyable is Ownable {

function close() public OnlyOwner {
selfdestruct(msg.sender);
}

}

type or paste code here
1 Like

pragma solidity 0.7.0;

//==========here I miss-read the assignment but eventually, modified it, for it to work======================

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

contract selfDestruct {

mapping(uint => Book) private books;

event bookCreation(address bookAdded, string title, uint book_id);



struct Book {
    string title;
    string author;
    uint book_id;
}


Book book;

function setBook() public returns (string memory) {
    require (msg.sender == owner);
    book = Book('Learn Solidity', "Philip, Ivan on Tech", 1000);
    emit bookCreation(msg.sender, book.title, book.book_id);
}

function getBookId() public view returns (uint) {
    return book.book_id;
}

function getBookTitle() public view returns (string memory) {
    return book.title;
}

function getBookAuthor() public view returns (string memory) {
    return book.author;
}

}

//==================================
/*
contract Ownable {
address public owner;

modifier onlyOwner { // break out function: modifier for require function; to check for owner == owner status
require(msg.sender == owner);
_;
}

constructor() {
    owner = msg.sender;
}

}
*/

//===================================
/*
pragma solidity 0.7.5;

import “./Ownable.sol”;

contract Destroyable is Ownable {

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

1 Like

Hi @Haysoose,

We can also streamline the inheritance by having Bank just inherit Destructable, because it will then indirectly inherit Ownable via Destructable. This will give you a multi-level inheritance structure. Remember that in Bank.sol you also need to import the file Destructable.sol, and if you implement the more streamlined approach, there is no longer any need to import Ownable.sol, which can be removed.

That makes sense. So since deductable is ownable, bank-- by inheriting destructable – is inherently ownable. Do I have that right?

1 Like

Exactly right :slight_smile:
We then get the following multi-level inheritance structure:

contract Ownable {...}
contract Destructable is Ownable {...}
contract Bank is Destructable {...}

When just Bank is deployed, it will have access to both Ownable and Destructable via inheritance.

contract Bank is Ownable, Destructable {...}
// This isn't wrong, and still works fine, but is unnecessary.

Nice solution @makinitnow,

Can we also see how you’ve coded the start of your Bank.sol file and Bank contract to include the inheritance?

That makes total sense. Thank you for the clarification.

1 Like

Hi @bjamRez,

First of all, well done for making this assignment more of a challenge by implementing your own child contract :muscle:

Your Ownable and Destroyable files and contracts are well coded :ok_hand:

As well as importing these files into Book.sol, you also need to inherit the contracts in the contract header:

contract Book is Ownable, Destroyable {...}
/* Notice that I've renamed the contract Book.
   Naming it selfDestruct could be confusing because this is the name of
   the selfdestruct function that is inherited from Destroyable. Also
   the purpose and principle functionality of this contract is not to
   self-destruct, but to manage book information. */

Other comments about your Book contract:

  • It won’t compile unless you use the same Solidity version as the inherited contracts: 0.7.5
  • You have a mapping but you aren’t using it to store books. Instead of the book state variable, which can only store one Book instance, and the single, fixed book data that setBook() creates, you could include input parameters in setBook() for title, author and ID, and then assign each new book to the mapping using the ID as the key.

  • You don’t need to return anything in the setBook function header, because your function body doesn’t have a return statement. The purpose of this function is not to return any data but to set data (i.e. update the contract’s state by assigning book data to storage).

  • As well as destroying a contract, the selfdestruct function is also able to return any remaining ether held by the contract address to the address passed to selfdestruct() as the argument. This happens when the contract self-destructs. Because you haven’t included any payable functionality in your Book contract, you won’t see this happening when you call selfdestruct to destroy your contract. This is why the idea was to use our pre-exisiting Bank contract within this assignment’s inheritance structure.

  • We can also streamline the inheritance by having Book just inherit Destroyable, because it will then indirectly inherit Ownable via Destroyable. This will give you a multi-level inheritance structure.

I hope you find this helpful. Let us know if you have any questions :slight_smile:

1 Like

Hi guys,

Here’s my solution to the assignment, but could you also look at the diagrams bellow?

Ownable.sol

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

Destroy.sol

import "./ownable.sol";
contract Destroy is Ownable {
    function destroy() public onlyOwner { 
        selfdestruct(msg.sender); 
    }
}

Bank.sol

import "./destroy.sol";
contract Bank is Destroy {...}

For optimisation purposes out of the assignment, is it ok to structure the code like in the diagram 3 (with destroy function within the Ownable contract rather than Destroy contract)?

2 Likes

Great solution @evasilev :ok_hand:
… and nice diagrams clearly illustrating alternative inheritance structures.

While inheritance structure 2 is correct and works just like inheritance structure 1, there is no need for Bank to directly inherit Ownable as well as Destroy, because it indirectly inherits Ownable via Destroy.

While providing a workable solution, combining the functionality of Ownable and Destroy within one single contract would not be considered as optimal or useful as separating it. Keeping the code which manages ownership separate from the code which manages contract destruction makes it more modular, and each individual contract more reusable.

1 Like

Thanks a lot. This is a really wonderful course. Very excellent teaching and assignments.

1 Like

Bank.sol

pragma solidity 0.7.5;

import “./Destroyable.sol”

contract Bank is Destroyable {

.

.

So this means we allowed the onlyOwner to destroy our contract?

1 Like