Inheritance & External Contracts - Discussion

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

  1. What is the base contract?
    In Solidity, each contract has a parent class, known as the derived class, and a parent contract, known as the base contract.

  2. Which functions are available for derived contracts?
    Derived contracts have access to all public and internal scoped functions of the base contract.

  3. What is hierarchical inheritance?
    hHierarchical inheritance refers to a single contract which serves as a base contract for multiple derived contracts.

2 Likes

Hey @KennethJP,

There aren’t any additional assignments after the Value Calls video for the Inheritance & External Contracts section. But I’ve just checked the Value Calls video, and you are right that it is confusing, because Filip does mention that there are.

You aren’t missing a video. The Value Calls video is the last one in that section, and after this comes the Multisig Wallet Project.

Thanks for pointing this out. I will ask for this to made clear at the end of the video.

1 Like

Hi @Samm,

I’ve never used “At Address” in Remix before. To be honest, I’m not entirely sure what it is you were trying to do using "At Address, but having done a quick bit of research it seems as though you would only use it to access and interact in Remix with a contract you already have deployed on a testnet i.e. not just within the JavaScript VM environment which uses a sandbox blockchain in the browser.

Anyway, here are the links to the information I’ve found. Hopefully this will go some way towards answering your question…

https://remix-ide.readthedocs.io/en/latest/run.html#environment
https://remix-ide.readthedocs.io/en/latest/run.html#deploy-ataddress
https://ethereum.stackexchange.com/questions/49376/how-to-load-contract-from-address-remix/49377

Hi @FerfiC,

Q1 & Q2  :ok_hand:

Q3

No… if a contract inherits another, the functionality available to the parent contract, which the child contract has access to, will always be the public and internal functions, the public and internal state variables, all events, and all modifiers.

Hierarchical inheritance is the term for a specific type of inheritance structure, where the same, single base/parent contract is inherited by more than one (i.e. multiple) derived/child contracts.

Just let me know if you have any questions.

Hi @Nathan_Burkiewicz,

Q2 & Q3  :ok_hand:

A parent contract is known as the base contract — yes, that’s correct.
But in Solidity the term class simply means contract i.e.

  • child contract = derived contract = derived class (all alternative terms for the same thing)
  • parent contract = base contract = parent class  (all alternative terms for the same thing)

It’s not true that each contract has a parent class (a base/parent contract). Only a contract which inherits another contract (i.e. a derived contract) has a parent class (the inherited contract).

Just let me know if anything is unclear, or if you have any questions :slight_smile:

1 Like

Hi @apmfree,

Your solution meets the objectives of the assignment :ok_hand: but it is wasteful on code…

You realised that you needed to make the owner address payable , so that selfdestruct() is called with a payable address. But the reason for this is so that selfdestruct() itself can transfer any remaining ether in the contract to the contract owner, when it is triggered. This operation is already built-in to the pre-defined functionality of selfdestruct(). Therefore, you don’t need the additional code you’ve added to perform this transfer of any remaining ether in the contract when it is destroyed:

Your coding of the inheritance structure is good, and your implementation of a multi-level inheritance structure is also the best approach, because it is more streamlined: Bank only needs to explicitly inherit Destroyable, because it will implicitly inherit Ownable via Destroyable. But don’t forget that in Bank.sol you also need an import statement to import Destroyable.sol (assuming you have Bank and Destroyable in separate files).

Just let me know if you have any questions :slight_smile:

Hey @2tijan7,

I hope you’ve managed to resolve this issue now. But if not, then it’s probably caused by the following…

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:

Let me know if you’re still experiencing any difficulties with this, or if you have any further questions :slight_smile:

Hi Filip, I really enjoyed this course, but I’m having trouble understanding the very last topic that was shows quickly “Value Calls”.

I’m having trouble wrapping my head around why do you need “{value: 1 ether}” when you’re specifying the amount as an uint input. This is the full line of code in the video:

governmentInstance.addTransaction{value: 1 ether}(msg.sender, recipient, amount);

Thanks!

1 Like

Hi @atlwarrior,

Our particular 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 of tax on each transfer. I would suggest an amount much smaller than 1 ether would be more appropriate, for example any value from 1 wei up to 1 gwei. This amount (which is the amount of tax paid by the Bank, and not the amount transferred from one bank account holder to another) will be automatically transferred from the Bank contract balance to the Government contract balance on each successfully executed transfer transaction.

When an amount of ether (always in units of wei) is sent from one contract’s address balance to an external contract’s address balance, the ether itself is not passed to the external function executing this transaction in the same way as other arguments are. For example, if our Bank contract was only sending 1 gwei to the Government contract, but no other data was being sent as arguments to be processed within the external function receiving the value transfer, our external function call, and the external function itself, could look something like this…

// within one of the Bank contract's functions
governmentInstance.collectTax{value: 1 gwei}();

// within the Government contract
function collectTax() external payable { }

Now, let’s return to our example of the government collecting tax on each banking transaction that is a transfer. 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). The data sent is passed to the external function as 3 arguments. The 3rd argument (amount) references the amount being transferred between two Bank contract account holders. The other two arguments reference the addresses of the two participants in the transfer — the sender and the recipient.

It is important to realise that the amount argument does not represent an amount of ether entering or leaving the Bank contract when transfer() is called. The amount is just an internal accounting adjustment to the balances in the mapping of the two individual account holders participating in the transfer (the sender and the recipient). The adjustments to their respective balances in the mapping reflect the change in each individual’s share of the total contract address balance and, therefore, the change to what each individual is entitled to withdraw from the contract. The total amount of ether held in the Bank contract does not actually change as a result of the amount transferred between the sender and recipient addresses (the amount argument). The only change in the Bank contract’s address balance is a reduction for the tax payment transferred between the two contract addresses.

In the Government contract, the amount parameter together with the sender’s address and the recipient’s address are pushed to the transaction log as part of a transaction instance created within the addTransaction() function from the Transaction struct. The amount of wei received as the tax payment is automatically added to the Government contract address balance, and this could be checked by adding the following function to your Government contract, and then calling it after a transfer has been executed:

function getContractBalance() external view returns(uint) {
    return address(this).balance;
}

The same function could also be added to the Bank contract, so that you can also check that the same amount of wei (sent as the tax payment) has been deducted from the Bank contract address balance.

Instead of as a tax, another way to interpret this transfer of value between the two contracts 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.

And hopefully this explanation has helped to clarify the difference between a transfer of value in wei using the curly brackets, and a transfer of data as arguments/parameters within the parentheses. But do let me know if anything is still unclear, or if you have any further questions about value calls :slight_smile:

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

contract Destroyable is Ownable{

function DestroyContract() public onlyOwner { //onlyOwner is custom modifier
    selfdestruct(msg.sender);  // 'owner' is the owners address
}

}

pragma solidity 0.7.5;

contract Ownable{
address internal owner;

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

constructor(){
    owner = msg.sender;
}

}

pragma solidity 0.7.5;

import “./DestroyableAssign.sol”;

contract Bank is Destroyable{

I hope I have done this right… Once I hit the destroy button it looks like I can still add more funds

1 Like

An excellent solution @CryptoDiddy :muscle:

Your implementation of a multi-level inheritance structure is the best approach, because it is more streamlined. Bank only needs to explicitly inherit Destroyable, because it will implicitly inherit Ownable via Destroyable.

This is unfortunately one of disadvantages of using selfdestruct(). As you have pointed out, after selfdestruct() has executed, successful calls can still be made to the destroyed contract’s functions! This is a serious problem if a user isn’t informed that the contract has been destroyed and unwittingly sends ether to the contract (e.g by calling the deposit function) because it will be lost. It isn’t deposited or locked in the contract, because the contract has been removed from the blockchain and no longer exists. We know that the contract has been destroyed, because getBalance() will return 0 for any user who previously had a positive individual account balance as a result of ether they had deposited in the contract.

As a result of this issue with selfdestruct(), if a smart contract deployed on the mainnet is ever destroyed, all users need to be notified that they must not send any more funds to that contract address, otherwise those funds will be lost. There are more secure ways to manage this kind of situation (e.g. by using proxy contracts or pausable contracts) but this is out of the scope of this introductory course. If you are interested, then I would highly recommend that you take the Ethereum Smart Contract Security Course, either after this course, or after you’ve also done Ethereum Smart Contract Programming 201.


One final observation…

You haven’t used the owner variable here, but you could have done. The key, here, is that the address argument passed to selfdestruct() needs to be a payable address, in order for the contract’s remaining ether balance to be transferred to that address when the contract is destroyed. To receive ether an address must be payable. There are several ways we can code this…

(1) Use msg.sender (as you have done) to reference the contract owner’s address, because this is the only address that it can ever represent (as a result of the onlyOwner modifier restricting who can call the destroyContract function in the first place). Prior to Solidity v.0.8, msg.sender is payable by default, and so you wouldn’t need to explicitly convert it.
However, from v.0.8, msg.sender is non-payable by default, and so whenever you are using v.0.8 and need to use msg.sender as a payable address, this requires an explicit conversion e.g.

selfdestruct(payable(msg.sender));

(2) Use owner to reference the contract owner’s address. To be able to do this, you also need to do either of the following:

  1. Declare the owner state variable in Ownable with a payable address type, instead of with the default, non-payable address type;

  2. If you only need the contract owner’s address to be payable for the purposes of executing selfdestruct(), you can leave the owner state variable as non-payable, and explicitly convert the address to payable locally, within the function where you actually need to use it as payable. You can even do this directly within the selfdestruct() function call itself, as follows:

      function destroyContract() public onlyOwner {
          selfdestruct(payable(owner));
      }

Just let me know if you have any questions :slight_smile:

  1. What is the base contract?
    parent contract

  2. Which functions are available for derived contracts?
    all functions on parents contracts

  3. What is hierarchical inheritance?
    Where single contract acts as a parent contract for more than one contracts

  1. What is the base contract?

The contract that is inherited is called the parent contract, the parent contract is known as the base contract.

There is a relationship between base and derived contracts.

  1. Which functions are available for derived contracts?

A child contract is derived from a base / parent contract.

All public and internal scoped functions and state variables are available to derived contracts to an extent.

Also, it depends on the type of inheritance from single, to multi-level, to hierarchical, and multiple from the base function on what functions the derived contract will have.

  1. What is hierarchical inheritance?

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

1 Like

Hi @suhan_kim,

Q1 & Q3 :ok_hand:

No… not all functions in the parent contract are inherited. Those with public or internal visibility are inherited, but private and external functions are not available for derived contracts.