External Contracts & Interfaces in Ethereum


#21
  1. Yes, otherwise we would just have a payable function in the root contract and send ether to that function.

  2. Yes, that is correct. You can do that. But it might not make sense from a design perspective. The contract might be an external one, that you haven’t built. So you might not want to inherit from it necessarily.

3 and 4. Not really, I think you are missing one thing. The deposit function doesn’t only need the information about how much was transferred, it needs the actual ether. It needs to receive the cryptocurrency. It’s not enough just increasing the balance variable, that is only created to help us see the balance. The contract needs to get the currency, the ether, and that can only be done with the value function. So you can’t send it as a parameter, because it’s not like an integer, it’s actual money. If we had it as a paramter, I could input whatever I want regardless of how much money I have in my account. The value function actually sends money, cryptocurrency.

  1. I’m not 100% sure of what you are asking. But we need the payable modifier if the function in question is going to be receiving cryptocurrency. It doesn’t matter what that function then do with the currency. If it is receiving currency, then we need the payable modifier. Otherwise the function cannot receive it.

  2. msg.value is related to the transaction itself. So here we are talking about two different transactions. One between the user and the kennel contract and one between the kennel contract and the bank contract. So each of those transactions will have it’s own msg.value. They are not connected in any way. You could for example choose to only send 50% of the funds received in the kennel contract to the bank contract. So the msg.value does not have to be the same.


#22

Filip

Thank you. Once I realized that we need the value function to transfer the cryptocurrency from the Kennel contract to the BankContract and that we are talking about 2 different transacitons: a transaction between the user Ethereum account and the Kennel contract and a second separate transaction between the Kennel contract and the BankContract, it made sense to me.

Thanks again


#23

To everyone, please feel free to suggest corrections to any of my comments. I am using them as a future study guide if need be. Cheers.

pragma solidity 0.5.1;

// this is the interface that interfaces ExternalContract with DogContract within dog.solidity
// interface will have the function headers of the corresponding contract functions it is aiming for
// interface will also have the same name as the contract it is aiming for
contract DogContract {
    function addDog(string memory _name, uint _age) public payable returns (uint);
    
    function getBalance() public view returns (uint);
}

contract ExternalContract {
    // this contract will interact with the dog contract via the DogContract interface above
    // dcInstance is of DogContract interface (contains function heads/parameters for interfacing)
    // Then it is assigned the DogContract address from dog.sol, essentially aiming the interface
    // had to get the capitalized version of the contract address from the compile tab... glitch
    DogContract dcInstance = DogContract(0x2b47F0D926a28AdFC90a69939502DCb687aC3686);
     
    /*
        this function uses dcInstace, which contains the two interface function headers targeted at the DogContract address, and
        then calls the addDog function header of the interface and supplies the two arguments of _name and _age to be passed to the primary
        DogContract function within dog.sol
    */
    function addExternalDog(string memory _name, uint _age) public payable returns (uint){
        // the value(msg.value) passes remaining wei to the next function, which is the addDog function within dog.sol, passing the wei value through the interface to the primary contract we are aiming for
        return dcInstance.addDog.value(msg.value)(_name, _age);
    }
        // returning dcInstance balance, which utilizes the interface to use the dog.sol balance function rather than getting the value of this external contract. The value of the internal contract is then returned to the external contract as output.
        function getBalance() public view returns (uint){
        return dcInstance.getBalance();
    }
} 

#24

Hi @filip. I have a question that forms more of a discussion here:

This is my DogInterface.sol codebase:

pragma solidity 0.5.1;

contract DogContract {
function addDog(string memory _name, uint _age) public payable returns (uint);

    function getBalance() public view returns (uint);
}

contract ExternalContract {
DogContract dcInstance = DogContract(0xfE06481cBd1Ebbe8332ecF88902b55D5C8A3c11B);

function addExternalDog(string memory _name, uint _age) public payable returns (uint) {
    return dcInstance.addDog.value(msg.value)(_name, _age);
}

function getExternalBalance() public view returns (uint) {
    return dcInstance.getBalance();
}

}

It pretty much mirrors the code that you have created.

  1. Is the getExternalBalance function correct?
  2. When I run the addExternalDog function, it seems to operate fine, but then when I go back to the actual DogContract and getAnimal, at the index that it should be, there is no animal there. i.e. the array is not being updated.
  3. The balance of the account in DogContract does not reflect any change in value.
  4. The getExternalBalance has a balance of 200000000000000000X000 (The “X” keeps going up)

Any idea why this is happening? Everything up until the DogInterface addition to this ‘project’ is working fine.


#25
  1. Yes
  2. That’s normal in this case. As I think I said in the video, the owner of the dog is now the External Contract, not your personal address. It’s only the address that created the dog that can “get” the dog. So the only address that can access the dog in the DogContract is the address of the contract that created it.
  3. I can’t replicate this when I tried. The balance updates fine for me.
  4. I can’t replicate this either. Seems to work fine for me. Can you post your complete code here, both contracts. Please use the code function here in the forum so that your code is within the grey boxes completely. Makes is so much easier for me to read.

#26
pragma solidity 0.5.1;

import './Animal.sol';

contract DogContract is AnimalContract {
    
    modifier costs(uint amount){
        require(msg.value >= amount);
        if(msg.value > amount) {
            msg.sender.send(msg.value - amount);
        }
        _;
    }
    
    function addDog(string memory _name, uint _age) public payable costs(1000) returns (uint) {
        return _addAnimal(_name, _age, AnimalType.DOG);
    }
    
    function getBalance() public view returns (uint){
        return address(this).balance;
    }
}
    

#27
pragma solidity 0.5.1;

import './Ownable.sol';

contract AnimalContract is Ownable{
    
    event animalAdded(address owner, string animalName);
    
    enum AnimalType {DOG, CAT}

    struct Animal {
        string name;
        uint age;
        AnimalType animalType;
    }

    mapping(address => Animal[]) ownerToAnimals;
    
    function _addAnimal(string memory _name, uint _age, AnimalType _animalType) internal returns (uint) {
        emit animalAdded(msg.sender, _name);
        return ownerToAnimals[msg.sender].push(Animal(_name, _age, _animalType)) - 1;
    }
    
    function getAnimal(uint _id) public view returns (string memory name) {
        return ownerToAnimals[msg.sender][_id].name;
    }
    
}

#28
pragma solidity 0.5.1;

contract Ownable {
    
    address public owner;
    
    constructor() public {
        owner = msg.sender;
    }

    modifier onlyOwner() {
        require (msg.sender == owner);
        _;
    }
    
    function newOwner (address _newOwner) public onlyOwner {
        owner = _newOwner;
    }
}








pragma solidity 0.5.1;

contract DogContract {
        function addDog(string memory _name, uint _age) public payable returns (uint);
        
        function getBalance() public view returns (uint);
    }
    

contract ExternalContract {
    DogContract dcInstance = DogContract(0xfE06481cBd1Ebbe8332ecF88902b55D5C8A3c11B);
    
    function addExternalDog(string memory _name, uint _age) public payable returns (uint) {
        return dcInstance.addDog.value(msg.value)(_name, _age);
    }
    
    function getExternalBalance() public view returns (uint) {
        return dcInstance.getBalance();
    }
}

#29

Still works for me, even when I copy your code. How did you look up the balance?


#30

I deploy DogContract in Dog.sol and ExternalContract in DogInterface.sol on the JS VM environment.

I then use ExternalContract.addExternalDog to add a string and uint, with 1000 wei, and the transaction is successful. I then go back to DogContract.getBalance to check the balance and it returns 0.

When I check ExternalContract.getExternalBalance, it returns 2000000000000000005000 which keeps increasing even after deleting and redeploying contracts.


#31

Everything seems to work fine when deploying to Rinkeby though.


#32

Ok glad it’s working now.


#33

Here is my solution for the very last exercise in interfaces.
But I have one question: in my code, if you don’t send the exact amount
of eth (in my example 100 wei), the transaction does not pass i got the error
“The transaction has been reverted to the initial state”. But if you send exact
values, then it is ok.

I put a require to fix this but I was wondering… why the DogContract (original)
does not send a refund, even if the DogContract has a refund function implemented
inside cost modifier?

Best

pragma solidity 0.5.1;

contract DogPayableMappingContract{
     
    function addDog(string memory _name, uint _age) public payable returns(uint); 
    
    function getBalance() public view returns(uint);
    
}

contract ExtralContract{
    // Interact with dog contract//
    
    DogPayableMappingContract dcInstance=DogPayableMappingContract(0x53C705A9C8E2012094CB61c186BacD7A630617C0);
    
    function addExternalDog(string memory _name, uint _age) public payable returns(uint){
       require(msg.value==100, "Must send exact value, 100 wei");
       // this line fixes the issue that if exact values is not provided, dog is not added 
        return dcInstance.addDog.value(msg.value)(_name, _age);
    // .value(msg.value) passes eth sent from address to externalcontract to DogPayableMappingContract
    // but if it is NOT THE EXACT value is not sent, transaction does not pass 
    }
    
    function getExternalBalance( ) public view returns(uint){
        return dcInstance.getBalance();
    }
    
}

#34

When the external contract is the contract calling the addDog function, that contract becomes the sender and will therefore receive the refund.