0

I found this fantastic code used to generate a smart contract which allows you to send your ERC20 tokens to another address.

A simple overview / explanation of how that works is as following:

We initiate a mapping of all the erc20 coins that the smart contract accepts. Let's say you want to accept only BNB, your contract can do that as there is also a function, allowing the smart contract owner to establish which ERC20 to be accepted. Part of the code:

    /**
     * @dev list of all supported tokens for transfer
     * @param string token symbol
     * @param address contract address of token
     */
    mapping(bytes32 => address) public tokens;

    /**
     * @dev add address of token to list of supported tokens using
     * token symbol as identifier in mapping
     */
    function addNewToken(bytes32 symbol_, address address_) public onlyOwner returns (bool) {
        tokens[symbol_] = address_;

        return true;
    }

    /**
     * @dev remove address of token we no more support
     */
    function removeToken(bytes32 symbol_) public onlyOwner returns (bool) {
        require(tokens[symbol_] != 0x0);

        delete(tokens[symbol_]);

        return true;
    }

There are two parts of this smart contract that I don't get. First, what does this code on line 43 do?

ERC20 public ERC20Interface;

From what I found, ERC20 is declared in the openzepelin project which is imported at the beggining. I thought that contracts can be used only with the word 'is' such as (Contract myContract is ERC20).

Assuming that in this circumstance ERC20 comes from the openzepelin project, what does public EERC20Interface do?

I found ERC20 Interface here but I thought that all smart contracts would have this standard 'interface', so why specifically declaring it?

Second part that I don't get is more complex so I will add /** comments

function transferTokens(bytes32 symbol_, address to_, uint256 amount_) public whenNotPaused {
    require(tokens[symbol_] != 0x0);
    require(amount_ > 0);

    address contract_ = tokens[symbol_];
    address from_ = msg.sender;

    ERC20Interface = ERC20(contract_); /** what does this line do? Is it initiating a new instance of ERC20? **/

    uint256 transactionId = transactions.push(
        Transfer({
            contract_:  contract_,
            to_: to_,
            amount_: amount_,
            failed_: true
        })
    );

    transactionIndexesToSender[from_].push(transactionId - 1);

    if(amount_ > ERC20Interface.allowance(from_, address(this))) { 
        /** what does ERC20Interface.allowance indicate here? is this line checking that the ERC20 sender has enough balance? **/
        emit TransferFailed(from_, to_, amount_);
        revert();
    }

    ERC20Interface.transferFrom(from_, to_, amount_);

    transactions[transactionId - 1].failed_ = false;

    emit TransferSuccessful(from_, to_, amount_);
}
  • 1
    ERC20 comes from the openzepelin project, what does public ERC20Interface do? I think that's just a public variable for the ERC20 token (which is practically just the interface to ERC20). – GrandFleet Jan 9 at 21:03
  • Interfaces are contracts (that are not deployed) that define what the signature (definitions might be better wording?) of a contract that implements the interface should look like. So ERC20.sol should implement everything that is in IERC20.sol We can also use the interface to instantiate contracts at an address on the client side. – Nico Vergauwen Apr 1 at 15:07
1

At line 3:

import "openzeppelin-solidity/contracts/token/ERC20/ERC20.sol";

In there:

contract ERC20 ...

So ERC20 is a contract.

At line 43 in the linked contract:

ERC20 public ERC20Interface;

So ERC20 is the type (which is the contract defined in ERC20.sol), the visibility is public and the instance is called ERC20Interface.

Later at line 93:

ERC20Interface = ERC20(contract_);

This is instantiating an instance, this time with the address of a deployed specimen. That attaches the function signatures (from ERC20) to the address. That means the function can then interact with the ERC20 contract at the address that was passed into the function.

In my opinion, this usage of the ERC20 contract is sub-optimal. It's rolling up all the code from ERC20.sol even though TokenZendR will never us it. It will never use the function implementations that will be compiled because all it ever does is communicate with other (ERC20) contracts. This is accomplished more efficiently by using an interface.

Indeed, ERC20.sol itself points to the interface contract.

At line 3:

import "./IERC20.sol";

This contains sufficient information for TokenZendR to send messages and is much smaller, so it would reduce complexity, size (and cost).

It's also wasteful to reserve and assign storage space for what amounts to an interface specification.

import "./IERC20.sol";
contract TokenZendR ...

  function transferTokens(address addr,...

    IERC20 i = IERC20(addr);

From the perspective of this contract it's enough to know that there are functions like this (but it's not important to know the implementation) ...

function transfer(address to, uint256 value) external returns (bool);

... so you can do things like:

bool successfulTransfer = i.transfer(receiver, amount);

or

require(i.transfer(receiver, amount);

Hope it helps.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.