I am having a difficulty of understanding interfaces in Solidity. What am I missing?
Asked Answered
D

2

6

I came from Java OOP background and understand interfaces.

Currently working on a simple budgeting app (https://github.com/compound-developers/compound-supply-examples) that takes ETH or Stablecoin and put in in Compound and earn interest.

My confusion is how Solidity Interfaces are used. I came from OOP (Java) background and very familiar with interfaces.

So in this code(MyContracts.sol) you can see that there is a mint() function in the interface. However, there is no implementation for it but you can see that it's used here uint mintResult = cToken.mint(_numTokensToSupply); without any implementation.

Can anyone shade some lights on how interface functions are used without implementations ? When you call mint in this case, which code is actually being executed ?

Doss answered 8/11, 2020 at 1:15 Comment(0)
D
20

I believe I got the main issue that was confusing to me.

So if you come from OOP background this what we know about interfaces:

interface IERC20 { 
   function totalSupply() external view returns (uint256);
}

contract XYZ is IERC20 {
// then implement totalSupply here 
function totalSupply() external view returns (uint256) {
// implementiation goes here. 
address public add='0x123...4'
}

So at this point you can call XYZ's totalSupply() and you should be fine.

However, there is another way of using interfaces in Solidity. I will take this code from compound protocol as an example (https://github.com/compound-developers/compound-supply-examples)

If you see MyContracts.sol, it has the following interface:

interface CEth {
    function mint() external payable;

    function exchangeRateCurrent() external returns (uint256);

    function supplyRatePerBlock() external returns (uint256);

    function redeem(uint) external returns (uint);

    function redeemUnderlying(uint) external returns (uint);
}

However, there is no place in our contract that uses the keyword IS and implements any of the methods. So you might ask how is our interface being used ?

Now let's go back to MyContract contract in MyContracts.sol file and see this code under supplyEthToCompound function:

CEth cToken = CEth(_cEtherContract);

Here we are providing CEth interface with a contract address of Compound (i.e _cEtherContract and the contract at that address has a mint() function.)

When you call cToken.exchangeRateCurrent(); on the next line, what happens is we are basically calling a function exchangeRateCurrent on Compound Contract.

At first it seems like exchangeRateCurrent has no implementation in the file we are calling it but the implementation resides at _cEtherContract address.

I hope this clears the confusion especially if you come from traditional OOP background.

Feel free to point out anything that is misleading in my answer.

Doss answered 8/11, 2020 at 3:30 Comment(2)
Hey, thank you very much for your explanation. It is very helpful. I would like to ask you (and I am sorry in advance for a silly question, but I am really new to OOP): In the code, you defined the implementation of the totalSupply() function in the contract. My question is. Why would I define the totalSupply() without implementation in interface if I subsequently define the function again in the contract? What for is the interface then? PS if you have a great source to study in order to understand, please, I would love to dive into it. Thanks in advance!Feria
I still don't get it, where is the Compound contract defined and the implementation for the exchangeRateCurrent() function?Mennonite
F
0

interface is used for type casting. from this contract

interface IReceiver {
    function receiveTokens(address tokenAddress, uint256 amount) external;
}

inside contract

contract UnstoppableLender is ReentrancyGuard {
    .....
    function flashLoan(uint256 borrowAmount) external nonReentrant {
        require(borrowAmount > 0, "Must borrow at least one token");
        uint256 balanceBefore = damnValuableToken.balanceOf(address(this));
        require(balanceBefore >= borrowAmount, "Not enough tokens in pool");
        assert(poolBalance == balanceBefore);
        damnValuableToken.transfer(msg.sender, borrowAmount);

        // that means msg.sender has receiveTokens functionality
        // we are making sure that who ever is calling this function, has this method implemented
        // we can conclude that ms.sender is a contract address
        IReceiver(msg.sender).receiveTokens(
            address(damnValuableToken),
            borrowAmount
        );
      .......
}

msg.sender is the address that calling flashLoan function. By casting with the IReceiver interface we are saying that whichever contract address is calling flashLoan function must have it is own receiveTokens function implemented.

If you look at the contract that calling the flashLoan function, in fact has receiveTokens

function receiveTokens(address tokenAddress, uint256 amount) external {
        require(msg.sender == address(pool), "Sender must be pool");
        // Return all tokens to the pool
        require(IERC20(tokenAddress).transfer(msg.sender, amount), "Transfer of tokens failed");
    }
Foetid answered 4/12, 2022 at 23:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.