Inheritance & External Contracts - Discussion

When I interact with a contract, I pay a certain transaction fee.
If that contract has calls to an external one, does also that transaction incur into fees?

In our example: I pay fees for the bank contract. Does the bank contract pay fees when interacting with the interface of government contract?

Thanks in advance.

Hey @LukeAppleby

When you call the selfdistruct method it automatically sends all the ethers in the contract to address specified as parameter to the method.
In your case selfdistruct(owner) will send the ether to the address bound to the variable owner.
Because you are sending ether to that address, the address must be casted as payable.
In solidity > 0.7 you can cast an address to payable by just doing payable(owner).

Give it a try.

Cheers,
Dani

2 Likes

@dan-i That was the fix, thanks for the help :smile:

1 Like

In need of help please!

I can’t figure out where I went wrong. I’m trying to complete a transfer, but it keeps giving me an error ā€œ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. Debug the transaction to get more information.ā€

This is my code. Does anyone know where I went wrong?

pragma solidity 0.7.5;

import "./Ownable.sol";

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

contract Bank is Ownable {
    
    GovernmentInterface GovernmentInstance = GovernmentInterface(0xcCbE7717e986CCb546E50d16143757Aff9CEd4e4);
    
    mapping(address => uint) balance;
    
    event depositDone(uint amount, address indexed depositedTo);
    event withdrawDone(uint amount, address indexed withdrawFrom);
    
    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");
        msg.sender.transfer(amount);
        emit withdrawDone(amount, 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, "Insufficient Balance");
        require(msg.sender != recipient, "Cannot send to Self");
        
        uint previousSenderBalance = balance[msg.sender];
    
        _transfer(msg.sender, recipient, amount);
        
        GovernmentInstance.addTransaction(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;        
    }
}

Any help would be greatly appreciated. Thanks!

1 Like

Nevernind, figured it out. Didn’t update Government Address after deploying

1 Like

Just for experiment, delete this code line:

GovernmentInstance.addTransaction(msg.sender, recipient, amount);

and test the transfer function again.

Why is failing? because you have not deployed the government contract yet, so just to test if the transfer function works properly I just delete that codeline and boom.

Carlos Z

Some questions about contract inheritance:

  1. As I understand it, once a contract is written into the blockchain, it is immutable and therefore any modification to the contract code will be a new contract ID when it is deployed. Is this also an example of inheritance?
  2. Also as I understand it, the parent contract code is copied into any derived contract bytecode, so that if you actually read the bytecode of the contract in the EVM you’d see all parents of the contract at the moment of its deployment included.
  3. If both of those items are correct, it seems a little hazardous to use much by way of inheritance as new functionality on the part of a parent contract would necessitate recompiling and redeploying of children. If there’s a deep hierarchy of inheritance, wouldn’t this be an all-consuming task to keep up with?
  4. Also of interest is inheritance of the parent contract states. As I understand it, the state variable data (IE balances of everyone in the contract) is stored in ā€œstorageā€, which is maintained in the EVM and is in a constant state of change as people use the contract. However, when a derived contract is deployed inheriting those states, it doesn’t refer back to the live states themselves, it will copy the states at the time of deployment. Is that correct, or are the live states of a parent contract available to the children of that contract?
1 Like
  1. It is the Parent contract

  2. All public and internal functions

  3. Similar to simple inheritance, but a single contract acts as a base contract for multiple derived contracts.

1 Like

Hi @stevesirag

  1. As I understand it, once a contract is written into the blockchain, it is immutable and therefore any modification to the contract code will be a new contract ID when it is deployed. Is this also an example of inheritance?

Not sure what you mean with your question, but that’s not an example of inheritance.

  1. Also as I understand it, the parent contract code is copied into any derived contract bytecode, so that if you actually read the bytecode of the contract in the EVM you’d see all parents of the contract at the moment of its deployment included.

When contract A inherits contract B, what happens is that the code of contract B is copy-pasted in contract A them contract A is deployed, you have plenty of this examples if you look at etherscan (tether for example): https://etherscan.io/address/0xdac17f958d2ee523a2206206994597c13d831ec7#code

From solidity docs

When a contract inherits from other contracts, only a single contract is created on the blockchain, and the code from all the base contracts is compiled into the created contract.

Reagards,
Dani

pragma solidity 0.7.5;
contract destroyable{
address owner;

modifier onlyowner {
require(msg.sender==owner);
_;
}

constructor(){
owner =msg.sender;

}
}

contract number is destroyable {

mapping (address => uint)balance;

event confirmdeposit(uint amount,address depositedto);

function close() public onlyowner{
selfdestruct(msg.sender);
}
function DEPOSIT() public payable returns(uint){
balance[msg.sender]+=msg.value;
emit confirmdeposit(msg.value,msg.sender);
return balance[msg.sender];

}
function withdraw(uint amount)public 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 reciepent,uint amount) public {
require(balance[msg.sender]>=amount);
require(msg.sender !=reciepent);

uint pervioussenderbalance=balance[msg.sender];

 transferexecutive(msg.sender,reciepent,amount);
 
 assert(balance[msg.sender]==pervioussenderbalance - amount);
}
function transferexecutive(address originating,address reaching,uint amount)private {
 balance[originating]-= amount;
 balance[reaching] += amount;
}

}

i did selfdestruct(msg.sender);
cos owner = msg.sender is it correct or wrong

I’m having some issues. Literally just trying to run and deploy your code but I keep getting this:

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. Debug the transaction to get more information.

But I deposit Ether into an account using the deposit function and I also use withdraw and getbalance to check to make sure there’s value in the account. I don’t know what’s wrong. Please help!

Hey @beloved

Can you please elaborate your question?

Regards,
Dani

Hello @mervxxgotti

VM error: revert. revert The transaction has been reverted to the initial state.

Whenever you got this error message, it means that a revert statement is hit therefore your transaction is reverted.

In order to fix it, you should check your require statements and make sure their conditions are satisfied.

If you struggle, post your code here and I will take a look.

Regards,
Dani

1 Like

It seems to be something wrong with the

governmentInstance.addTransaction(msg.sender, recipient, amount);

line as when i comment it out, it runs and transfers fine. But the government contract to track the transfer is not working.

pragma solidity 0.7.5;

//goverment contract to track transactions
contract Government {
    
    struct Transaction {
        address from;
        address to;
        uint amount;
        uint txId;
    }
    
    Transaction[] transactionLog;
    
    //use external - only other contracts can use this function but not from within contract, remix, or other applications
    function addTransaction(address _from, address _to, uint _amount) external payable{
        
        //make new temporary transaction from inputs then push to TxLog
        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);
    }
}

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

//to address: 0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2

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

contract Bank { //making an Ether bank
    
    //MAPPING
    mapping(address => uint) balance; //mapping type works like a dictionary, address (key) points to balance (value),
    //so balance[address] = value, or balance[0xa4e...] = 100
    
    GovernmentInterface governmentInstance = GovernmentInterface(msg.sender);
    
    address owner; //only admin can add balance
    
    event depositDone(uint amount, address indexed depositedTo);

    modifier onlyOwner { //make modifier to only allow owner to use addBalance()
        require(msg.sender == owner, "You're not the admin!"); //require sender to be owner
        _; //run the addBalance function, theoretically this means the code from that function gets placed here
    }
    
    modifier costs(uint price) {
        require(msg.value >= price);
        _;
    }

    constructor() {
        owner = msg.sender;
    }
    
    //"payable" keyword allows function to recieve money when called
    function deposit() public payable returns (uint){
        
        balance[msg.sender] += msg.value; //add toAdd to balance of this ETH address [msg.sender], sender is ETH address and value is ETH value
        emit depositDone(msg.value, msg.sender);    
        return balance[msg.sender];
    }
    
    function withdraw(uint amount) public returns (uint){
        
        require(balance[msg.sender] >= amount, "Not enough balance!"); //require existing balance is more than withdraw amount
        msg.sender.transfer(amount); //amount transferred in wei
        balance[msg.sender] -= amount; //update balance
        return balance[msg.sender]; //show current balance after withdrawal
    }
    
    function getBalance() public view returns (uint){
        return balance[msg.sender];
    }
    
    function transfer(address recipient, uint amount) public {
        require(balance[msg.sender] >= amount, "Balance empty!"); //if amount is not in account balance, do not proceed, reverse transaction (revert()), show error
        require(msg.sender != recipient, "Illegal!"); //check sender isn't sending to self, can't send money to self
        
        uint previousSenderBalance = balance[msg.sender]; //save previous balance of sender
        
        _transfer(msg.sender, recipient, amount);
        //governmentInstance.addTransaction(msg.sender, recipient, amount);
        
        assert(balance[msg.sender] == previousSenderBalance - amount); //assert that the sender balance is previous balance - sent amount
    }
    
    function _transfer(address from, address to, uint amount) private { //internal or private functions denoted by _
        balance[from] -= amount;
        balance[to] += amount;
    }
    
}

Bank inherits from destroyable which is ownable.

pragma solidity 0.7.5;

import "./Destroyable.sol";

contract Bank is Destroyable {
    
   mapping(address => uint) balance;
import "./Ownable.sol";
contract Destroyable is Ownable {
    
    function close() public  onlyOwner {
        selfdestruct(msg.sender);
    }
    
}
contract Ownable {
     address public owner;
     
     
        modifier onlyOwner{
       require(msg.sender == owner);
       _; // Run the function
   }
   
   
      constructor(){
       owner = msg.sender;
   }
}

This gave me some problems later on, where I had to inherit directly from Ownable instead of Destroyable. Otherwise the Transfer function did not work properly. Could you please clarify why that is?

Hi @mervxxgotti

In your bank contract you are setting the GovernmentInterfact giving msg.sender as address, but you should use the Government contract address instead.
You are basically telling Solidity that you want to call a function x in a contract having address msg.sender but msg.sender is not the address of the contract.

Check it out and try again.

Screenshot 2021-03-03 at 12.52.34

Cheers,
Dani

1 Like

Hi @ElCrypto

In order to help you I need to see all your contracts.
Please post them here.

Cheers,
Dani

Hi dan-i

Thank you, but I haven’t saved the previous code. I will next time :slight_smile: I was able to fix it doing it differently.