Inheritance & External Contracts - Discussion

I hope I am in the right place and that I have the right idea on Ownable/Destroyable inheritance. Here is what I came up with:

================= Ownable.sol =================
pragma solidity 0.8.4;

contract Ownable {
address public owner;

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

constructor() {
    owner = msg.sender;
}

}
================ Destroyable.sol ======================
pragma solidity 0.8.4;

import “./Ownable.sol”;

contract Destroyable is Ownable {

event contractClosed(uint closeBalance, address indexed closeAddress);

function destroyContract() onlyOwner public { 
    uint contractBalance = address (this).balance;
    selfdestruct(payable (owner)); 
    assert(address (this).balance == 0);
    emit contractClosed(contractBalance, owner);
}

}================ helloworld.sol ===============
pragma solidity 0.8.4;

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

contract Bank is Ownable, Destroyable {

}

I can now call destroyContract() from Remix, but only from the original
deployer of the contract, not from any other address, so that appears correct,
and I DO, in fact get my balance back, plus what was in the other address.

I am wondering, though, where do I see the logs again?
I am hoping to see the contract balance that gets sent to the
contract owner when it is destroyed via the event called in Destroyable.sol.

Thanks!

1 Like

Hi, FrankB,
I believe that modifiers are similar to compiler directives in that the modifier code is actually inserted into your function at compile time, therefor it is not really “referenced” publicly or privately.

Then again, I may be wrong…

Well, my Bank contract was working well until I added the Government Interface and broke it!

First question: Why would the addTransaction function be payable? Does it need to be or was that an example?

Second question: Why am I getting this error when I call addTransaction()?
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.

In Bank contract:

interface IGovernment{
function addTransaction(address _from, address _to, uint _amount) external payable;
}

in transfer() function:
governmentInstance.addTransaction{value: 1 wei}(msg.sender, recipient, amount);

In Government contract:
function addTransaction(address _from, address _to, uint _amount) external payable {
transactionLog.push( Transaction(_from, _to, _amount, transactionLog.length) );
}

Thanks.

Oops. Forgot this line in Bank Contract:

IGovernment governmentInstance = IGovernment(0xdD870f…7392148);

2 Likes

I have the same error after i made the function in government payable and added value to the instance.

Somehow I got mine to work. Did you?
I just kept testing the Government contract stand-alone until it worked, then tried it again from the Bank contract. Not sure why it wasn’t working in the first place!

1 Like

Hi @Samm,

Firstly, apologies for the delay in giving you some feedback on this, and in answering your specific question about the event logs.

Yes… your solution achieves this objective nicely :ok_hand:

Yes… your solution also handles this appropriately. 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 transferred to the owner when selfdestruct() is triggered :ok_hand: The remaining contract balance is transferred, and so it’s also correct that the contract owner will receive the sum of all of the individual users’ remaining account balances (as well as their own).

Your coding of the inheritance structure is sound. Note, that 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.


Below are some excerpts from discussions I’ve had with a couple of other students about the issues related to emitting an event for selfdestruct() …

So in your destroyContract function, both the emit statement and the assert statement are unreachable and will not be executed.

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

Thanks so much for the explanation. Especially “…inherit Destroyable only, because it will inherit Ownable implicitly via Destroyable.” and “… the event won’t be emitted because the contract has been destroyed once selfdestruct() has executed, and so the emit statement is never reached…”

These were news to me and very informative. I’m anxious to learn more about proxy contracts!

1 Like

Glad it was informative :slight_smile:
I think you’ll definitely enjoy the Smart Contract Security course…

Hey @Samm,

Once again, apologies for the delay in giving you some answers on this.

It doesn’t have to be. Our example is based on a contractual relationship between a government and a bank. The idea is that the government needs to collect some kind of tax on each banking transaction that is a transfer. To keep things simple in our example, the government is collecting a fixed amount (1 wei) on each transfer. This amount will be automatically transferred from the Bank contract balance to the Government contract balance on each successfully executed transfer transaction. For record keeping purposes, the relevant data for each transfer is also sent from Bank to Government and stored in the government’s transaction log (an array).

Instead of being a tax, another way to interpret this payment could be as payment for some kind of record keeping service i.e. the Bank contract pays the Government contract for storing all of its transaction data.

However we interpret this payment from one contract to the other, it demonstrates (albeit in an overly simplistic way) how we can program automatic interactions between individual contracts for the purposes of both data and value transfer. Hopefully, this example shows the potential for these sorts of programmed interactions to be applied to many different use cases.

But returning to your original question… we could equally exclude the transfer of value from the addTransaction function, if we want it to provide a purely record keeping role at no extra charge (except of course the cost of gas consumption, which in any case is paid to the miners and not to the external contract).

If no value is transferred, then the addTransaction function should not be marked payable, in both Government and the Government interface in Bank.


You should be deploying your Government contract before deploying Bank. If you redeploy Government at any stage, then it will have a different contract address, and so you also need to change the Government address assigned to your governmentInstance in this line of your code in Bank:

If you forgot to replace the previous address of your external contract for the new one, then this will have caused the transaction to revert when you called the transfer function, and will have given you the error message you got. The problem is that the error message you got was just a default one, and so that’s why it wasn’t helpful in pinpointing the error. Your error most probably wasn’t anything to do with the function not being marked payable, or the value exceeding the current balance.

This is easily done, and catches most of us out the first time we make that mistake :sweat_smile:

Adding an error message to your require() statements, is always a good idea, because if the revert is caused by one of them, the message you added will appear as part of the error message you get in the Remix console. If one of your own error messages doesn’t appear, you know that the transaction reverted because of something else.

Let me know if you think that was the cause of the problem or not. But I’m glad to see that you got it to work in the end :slight_smile:

Hey @Joey,

I’ve replied to @Samm regarding this issue. Have a look to see if this solves the problem:

Hopefully that will solve the problem. If not, then post a copy of your code for both contracts and I’ll take a look and investigate further :slight_smile:

Hey guys, is the assignments mentioned in the value calls video the Multi Sig project? or am i missing a video ? :smiley:

1 Like

I wonder if this was caused by re-deploying the Government contract over and over to the same address using “At Address” in Remix? I may have redeployed it using a different address in my list when it finally started working. Can you verify if this could be the case?

Thanks!

1-The base contract is the parent contract. The base contract is referenced by the child like so:
(defining the child contract) contract CHILD is PARENT {…}
2-All public and internal scoped functions are available to derived contracts
3-Heirarchical inheritance is when a single contract acts as a base or parent, for multiple derived or child contracts.

1 Like

contract Ownable {

address public owner;

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

constructor() {
    owner = msg.sender;
}

}

pragma solidity 0.7.5;

import “./Ownable.sol”;

contract Destroyable is Ownable {

function destroy() public onlyOwner {
    
    //generate payable owner address
    address payable _owner = payable(owner);
    
    //get total eth in contract
    uint _total = address(this).balance;
    
    //send all ether to owner
    (bool success,) = _owner.call{value:_total}("");
    require(success,"Failed to send Ether");
    
    //self-desctruct contract
    
    selfdestruct(_owner);
}

}

Here is header of Bank contract…

contract Bank is Destroyable {

2 Likes

1. What is the base contract?
The base contract is the name used to refers the parent contract.

2. Which functions are available for derived contracts?
Public and Internal functions

3. What is hierarchical inheritance?
Is where a single contract behaves as a base contract for multiple derived contracts that are related to each other through parent-child relationship.

2 Likes

1. What is the base contract?
It is a “Parent contract”. It is a type of contract, that has a contract derived from it called “derived class”

2. Which functions are available for derived contracts?
All Public and internal functions.

3. What is hierarchical inheritance?
Is where a single contract acts as a base contract for multiple derived contracts.

2 Likes

Hi,
I am stuck at the end of External Contracts video. I keep getting this error when I do the transfer function and I don’t know why. But if comment govermentInstance.addTransaction(msg.sender, recipient, amount); transfer function works and I get no errors:
image

Here is the code of Bank contract:

pragma solidity 0.7.5;

import "./Ownable.sol";
import "./Destroyable.sol";

interface GovermentInterface {
    function addTransaction(address _from, address _to, uint _amount) external;
}


contract EtherBank is Ownable, Destroyable {    
    GovermentInterface govermentInstance = GovermentInterface(0x5B38Da6a701c568545dCfcB03FcB875f56beddC4);
    
    mapping(address => uint) balance;
    
    event depositDone(uint amount, address depositedTo);
    event amountTrasferred(uint amount, address toAccount, address fromAccount);
    
    
    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(amount <= balance[msg.sender], "Not enough funds");
        balance[msg.sender] -= amount;
        msg.sender.transfer(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, "Can't send money to yourself");
        
        uint previusSenderBalance = balance[msg.sender];
        
        _transfer(msg.sender, recipient, amount);
        //emit amountTrasferred(amount, recipient, msg.sender);
        
        govermentInstance.addTransaction(msg.sender, recipient, amount);
        
        assert(balance[msg.sender] == previusSenderBalance - amount);
    }
    
    
    function _transfer(address from, address to, uint amount) private {
        balance[from] -= amount;
        balance[to] +=amount;
    }
    
}

And here is the code of Government contract:

pragma solidity 0.7.5;

contract Goverment {

    struct Transaction{
        address from;
        address to;
        uint amount;
        uint txId;
    }
    
    Transaction[] transactionLog;
    
    function addTransaction(address _from, address _to, uint _amount) external {
        transactionLog.push(Transaction(_from, _to, _amount, transactionLog.length));     
    }
    
    
    function getTransaction(uint _index) public view returns(address, address, uint) {
        return (transactionLog[_index].from, transactionLog[_index].to, transactionLog[_index].amount);
    }
}

I am messing with this over 90 min now and I hope I didn’t make one stupid typo.

@jon_m, @filip, @Mauro, @ivan

What is the base contract?
The base contract is another way to refer to the parent contract, and the contract that inherits the parent is called child contract.

Which functions are available for derived contracts?
Derived contracts can use all public and internal functions of the base contract.

What is hierarchical inheritance?
Hierarchical inheritance is the way we define the relation between child and parent contracts. Depending on how a contract is defined in the order of inheritance it may have access to more or less shared functionalities.

1 Like
  1. What is the base contract?
    The base contract is the parent contract.

  2. Which functions are available for derived contracts?
    public and internal scoped functions.

  3. What is hierarchical inheritance?
    hierarchical inheritance is when a single contract acts as base contract for multiple derived contracts.

1 Like

Hey @2tijan7, hope you are ok.

Sorry for the delay, the error only appears when interacting with the government contract, you could try changing the ‘addTransaction’ function to public, the interface keep it has external.

Let us know if that solve the issue :nerd_face:

Carlos Z