Inheritance & External Contracts - Discussion

Hey @Mitchell_Giddens

The errors are related to conditions not satisfied, more specifically:

So I went back and undid the changes but its still not working and I dont know what I’m missing, the error message isnt helping much but it says: […]

What function are you calling, what parameters are you sending?
Can you tell how to replicate it step by step?

Also I get one if I try depolying contract with an amount of ether:

If you are trying to send eth to your contract during the deployment you should make sure your constructor is payable otherwise the transaction will revert.

Cheers,
Dani

1 Like
  1. What is the base contract?

Parent contract that you are deriving from.

  1. Which functions are available for derived contracts?

External, public, private, internal

  1. What is hierarchical inheritance?

A single contract acts as a base for multiple derived contracts.

1 Like

Hey @Mitchell_Giddens, hope you are great.

As @dan-i mention you about the conditions not being satisfied, i would like to add:

you have 2 require statements in your transfer function, you should add an error msg on them and try again, maybe one of those are being trigger. thats why you have a revert error probably.

Let us know :face_with_monocle:

Carlos Z

I rewrote the contracts and theyre working fine now… I think it was because I wasnt updating the address of the government contract in code every time I deployed a new instance of it.

1 Like

Hi @bfleming98,

Q1 & 3 :ok_hand:

Functions with public or internal visibility are available for derived contracts. But functions with private or external visibility are not available.

Hi @Mitchell_Giddens,

Yes, the Government contract address is different on each separate employment. Forgetting not to replace the previous address in your Bank contract for the new one is easily done. It catches most of us out the first time we make that mistake :sweat_smile:
This will have caused the transaction to revert (when you called the transfer function, right?) 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 wasn’t anything to do with the function not being marked payable, or the value exceeding the current balance.

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

1 Like

What are the use cases for the value property?

govermentInstance.addTransaction{value: 50 ether}(msg.sender, recipient, amount);

Is there any examples, when would a contract move ether from itself to another?

1 Like
pragma solidity 0.7.4;

import "./Ownable.sol";

interface GovermentInterface{
    function addTransaction(address from, address to, uint amount) external payable;
}



contract Bank is Ownable {
    
    GovermentInterface govermentInstance = GovermentInterface(0x5B38Da6a701c568545dCfcB03FcB875f56beddC4)
    
    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 transfer(address recipent, uint amount) public {
        require(balance[msg.sender] >= amount, "Balance not sufficient");
        require(msg.sender != recipent, "Dont't transfer money to yourself");
        
        uint previousSenderBalance = balance[msg.sender];
        
        _transer(msg.sender, recipent, amount);
        
        govermentInstance.addTransaction{value: 1 ether}(msg.sender, recipent, amount);
        
        assert(balance[msg.sender] == previousSenderBalance - amount);
    }
    
    function _transer(address from, address to, uint amount) private {
        balance[from] -= amount;
        balance[to] += amount;
    }
    
```I am not sure what is wrong with the mapping???? very confused..

Hi Mickey,

Thanks for posting this here :+1:

Which line are you getting the error on and what does it say? There is nothing wrong with your mapping. But if you are using v0.8 instead of v0.7.5 then you need to make sure msg.sender is converted to a payable address in this line:

Before v0.8 msg.sender was payable by default, but unpayable by default from v0.8

I mention this, because your code throws an error for me on this line only.

1 Like

Another possible problem could be what is discussed in this post

1 Like
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 returns(address, address, uint) {
        return(transactionLog[_index].from, transactionLog[_index].to, transactionLog[_index].amount);
    }
    
    
    
    
    
} 

Would this be the problem? do I need to make these payable?
i fixed the last “mapping” issue due to a missed ; so… big facepalm haha

Hi @Mickey_McClimon,

OK, this is the 3rd attempt posting this reply to your “Government contract / payable” issue LOL. I got myself in a muddle about one of the points I was explaining to you, but I’ve cleared it up now, and so rest assured you can rely on this post :sweat_smile:

If you saw either of the previous 2 posts (posted about 1.5 hours ago, then please ignore them. But I’ve deleted them now, so if this is the first reply you’re reading then great — you won’t have noticed anything strange :wink:

Easily done! :joy:

Whether you’re using v0.7 or v.0.8 any function which needs to receive ether needs to be payable

So here is the problem:

Your addTransaction function isn’t payable in Government:

But you made this function payable in the interface in Bank:

The function is called with an ether value:

In the interface the function is payable, but the transaction will revert because the actual function being called in the external contract (Government) isn’t payable.

You also need to make sure that all of your contracts related by inheritance are using the same Solidity version. Otherwise you will run into compatibility issues. You can still call the external Government contract, via the interface, if its v0.7, but it may be safer to use the same version there too. I have them all in the same version.

What I was saying earlier is that v0.8 requires msg.sender to be explicitly converted to a payable address if it is receiving ether. So in the Bank withdraw function…

… would need to be either  payable(msg.sender).transfer(amount);
or  owner.transfer(amount) if you have already made owner payable in Ownable (it will then still be payable in Bank due to inheritance). Wherever you have made a function onlyOwner, msg.sender can only be the owner address, so in these functions you can use owner or msg.sender interchangeably. You just need to be careful about when this address needs to be payable, and how you ensure it is payable.

If you use v0.7 then you won’t have this payable issue with msg.sender, because it is payable by default. On the other hand, owner would still need to be converted to a payable address if used to receive ether.

1 Like

Also, this function won’t compile if the parameters don’t maintain consistent names! They are without underscore in the header, but then with underscore when referenced in the function body.

1 Like

that is exactly what this problem was thank you so much!

I did need to take an extra look at my headers after I do the video and complete the code. I usually catch those mistakes but did not in this instance so thank you!

1 Like

Hi @hihaiu,

value  used in  {value: 50 ether}  is not a property. This is syntax required by Solidity if you want to specifiy the amount of ether to be sent with an external function call. Here, the external function being called is addTransaction(). In this example, addTransaction() is called with the values input for the arguments: msg.sender (an address value), recipient (another address value), and amount (an unsigned integer value). Here, I’m using the word value in a general sense. Note that the msg.sender as the 1st argument is not the sender of this function call. Instead it references the caller of the transfer() function (i.e. the sender of the funds being transferred). The addTransaction function is being called by the Bank contract, and so the value specified of 50 ether is effectively a payment made by the Bank contract to the Government contract. In order for the function call to be successful, the Bank contract must have a balance of at least 50 ether. If the function call is successful, the Bank contract’s ether balance will be reduced by 50 ether, and the Government contract’s balance increased by 50 ether. You can check this by adding a function to both contracts which gets the respective contract balances using:
address(this).balance.
You can probably already see that a payment value of much less than 50 ether would probably be more appropriate!

So, having understood what {value: x ether/gwei/wei} does, we can now consider actual use cases.
In our Bank/Government contract example, the idea is that some kind of government levy or tax etc. is required to be paid by the bank (either itself, or on behalf of its account holders) for each transfer of funds between account holders. So, you can probably also see, now, why 1 gwei (= 1,000,000,000 wei, or 0.000000001 ether) would be a much more realistic value, considering the amounts of ether that are most probably being transferred.

The 3 arguments passed to the addTransaction function enable the Government contract to record each transaction (for which a payment/tax is due/paid) in its transaction log (the transactionLog array).

So that’s the use case we are simulating in our example contracts. You can probably now appreciate that this type of value transfer between contracts could therefore be implemented whenever one contract needs to charge another contract for some kind of service or cost etc., or when it needs to receive an income from the other contract for a specific purpose, project etc. Our Government contract enables the government to generate income (raise taxes) from the economic activity of other contracts (here, banking activity). It could either be used by the bank to account for the payments it needs to make at a later date to the government (e.g. annually), or by the government itself to record and account for its income from bank transaction charges etc.

Hi Micky,

I’ve cut the following Government contract from your post in the Inheritance Assignment topic, and pasted it here. I know they are all one project, but I just want to avoid code and discussion involving the external Government contract appearing in a thread related to an earlier part of the course, where external contract instances, external function calls, and interfaces haven’t been introduced yet.

My feedback on this particular contract is below your code.

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 payable {
        transactionLog.push( Transaction(_from, _to, _amount, transactionLog.length) );
    }
    
    function getTransaction(uint _index) public returns(address, address, uint) {
        return(transactionLog[_index].from, transactionLog[_index].to, transactionLog[_index].amount);
    }
      
}

Just a couple of other minor points to mention here:

  • You should be getting an orange warning in the compiler, indicating that you should restrict the getTransaction function to view. If a function doesn’t need to change the contract state, it’s always safer to restrict it to view (so it can read the contract state, but not change it). Similarly if a function doesn’t need to read or change the contract state, it’s safer to restrict it to pure.
  • Also in the getTransaction function, I would add names to the 2 address values returned, so that it’s clear which is the sender’s address and which is the recipient’s address.
1 Like

When running the government contract I get the following error:

call to Government.getTransaction errored: VM error: invalid opcode. invalid opcode The execution might have thrown. Debug the transaction to get more information.

When I debug I it points to the getTansaction line

function getTransaction(uint _index) public view returns(address, address, uint) {

I have reviewed the video a few times and have the exact same code. I am not certain how to fix this error.

  1. The base contracts is the contract that a child/ derived contract inherits.

  2. The functions defined in the parent contract.

  3. Hierarchal inheritance is where a parent class is inherited by multiple different child contracts.

1 Like

Hi @Michael_Gerst,

Q1 :ok_hand:

Q2

The functions in the parent contract with internal and public visibility are available for derived contracts, but not those with external or private visibility.

Q3 :ok_hand:

Yes… “…where a single parent contract is inherited by multiple different child contracts…”

1 Like

Hi @CMar10,

I assume you are trying to call getTransaction() from Remix, and not as an external function call from Bank, is that right? If I remember rightly, in the video only addTransaction() is included in the GovernmentInterface in Bank and then called externally from the Bank contract.

If that’s the case, then one reason why you would get this error, is because you are calling getTransaction before having added a transaction i.e. if the transactionLog array is still empty.

transactionLog may be empty either because (i) addTransaction() hasn’t been called from Remix, (ii) transfer() in Bank hasn’t been called yet, meaning that addTransaction hasn’t been called externally from within the transfer() function (which is the intended method of calling addTransaction and adding data to transactionLog). Or (iii) it could be due to an error related to the addTransaction function somewhere else in your Bank or Government code, meaning that calling addTransaction() itself fails.


** EDIT **
Before reading the rest of this post, jump to my next post, which I didn’t realise until afterwards is more likely to be the issue. But, if not, then come back to the rest of this post…


If your compiler is indicating an error with the line…

… then this could also be triggered by something in the function body, or even in the syntax at the end of your addTransaction function above. So, if the suggestions above (and below) don’t help solve the problem, we would need to see your full code (both the Government and the Bank contract) to be able to identify the issue for you.

Other things that may be causing a problem (although I doubt it, considering the error you have described above):