Inheritance & External Contracts - Discussion

  1. What is the base contract?
    Base contract is a parent contract in inheritance

  2. Which functions are available for derived contracts?
    All public and internal scoped functions and state variables are available to derived contracts.

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

1 Like
  1. What is the base contract?
    A. he contract has a parent known as the derived class and the parent contract is known as a base contract

  2. Which functions are available for derived contracts?
    A Single inheritance, multi-level inheritance, hierarchical inheritance, multiple inheritance, function polymorphism, contract polymorphism.

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

1 Like
  1. What is the base contract?
    In contract composition, the base contract is the parent contract which is inherited by a child contract (derived contract).

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

  3. What is hierarchical inheritance?
    Hierarchical inheritance describes the relationship where a single base contract is inherited by more than one derived contract.

1 Like

My bank contract was working great, and worked well with the government interface too. Then I got to the section about value calls, and when I updated my code it stopped working. 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:
“transact to Bank.transfer errored: VM error: revert. 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.”
Also I get one if I try depolying contract with an amount of ether:
“creation of Bank errored: VM error: revert. 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.”

Here is my code:




interface GovernmentInterface {
    function addTransaction(address _from, address _to, uint amount) payable external;
}

contract Bank {
    
    GovernmentInterface GovernmentInstance = GovernmentInterface(0xd9145CCE52D386f254917e481eB44e9943F39138);
    
    mapping(address => uint) balance;
    address owner;
    event balanceAdded(uint amount, address indexed depositedTo);
    event transferred(uint amount, address indexed sentFrom, address indexed sentTo);
    
    modifier onlyOwner{
        require(owner==msg.sender);
        _;
    }
    
    constructor(){
        owner = msg.sender;
    }
    
    function deposit() public payable returns(uint){
        balance[msg.sender] += msg.value;
        emit balanceAdded(msg.value, msg.sender);
        return balance[msg.sender];
    }
    
    function withdraw(uint amount) public returns(uint){
        require(balance[msg.sender] >= amount, "not enough funds");
        msg.sender.transfer(amount);
        balance[msg.sender] -= amount;
        return balance[msg.sender];
    }
    
    function addBalance(uint _toAdd) public returns (uint){
        balance[msg.sender] += _toAdd;
        emit balanceAdded(_toAdd, msg.sender);
        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);
        require(msg.sender != _recipient);
        
        _transfer(msg.sender, _recipient, _amount);
        
        GovernmentInstance.addTransaction(msg.sender,_recipient,_amount);
        
        
        emit transferred(_amount, msg.sender, _recipient);
        
    }
    
    function _transfer(address _from, address _to, uint _amount) private{
        balance[_from] -= _amount;
        balance[_to] += _amount;
    }
    
    
    
    
    
}

1 Like
  1. What is the base contract?
    The base contract is the parent to the child or derived contract and is inherited.

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

  3. What is hierarchical inheritance?
    When a base contract is inherited by more than one derived contracts.

1 Like

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.