1

How would I go about creating follow:
Escrow/Controller contract A which is capable of accepting ERC20tokens from contract B, and ETH from party C and do a swap when certain conditions are met.

This same contract A should also be able to receive ERC20tokens from contract D and ETH from contract E and do a swap when certain conditions are met.

The catch/relevancy of the question is, how to design contract A to be able to handle ERC20token inputs of different kinds e.g. Walton(WTC) & FUSION(FSN) ERC20 tokens?

2

This is where interfaces come in. That's great, because ERC-20 is actually an interface! I'll give you an example below. As you can see, the TokenSwapper doesn't even need to know about TokenA and TokenB, just that they implement the ERC20 interface.

Oh and before you start developing, you should first make yourself familiar with the basics of the ERC20 interface. Read this wiki

pragma solidity ^0.4.23;

/* Declaring the ERC20 interface. This tells
   other contracts how to handle ERC-20 tokens */
interface ERC20 {
    function allowance(address owner, address spender)
    external view returns (uint256);

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

    function approve(address spender, uint256 value) external returns (bool);

    function totalSupply() external view returns (uint256);
    function balanceOf(address who) external view returns (uint256);
    function transfer(address to, uint256 value) external returns (bool);
}


/* a simple base ERC token contract for testing */
/* DO NOT USE FOR PRODUCTION AS IT IS NOT SAFE */
contract ERC20Token is ERC20 {
    uint256 totalSupply_;

    mapping (address => mapping (address => uint256)) internal allowed;
    mapping(address => uint256) balances;

    function totalSupply() public view returns (uint256) {
        return totalSupply_;
    }

    function transfer(address _to, uint256 _value) external returns (bool) {
        require(_to != address(0));
        require(_value <= balances[msg.sender]);


        balances[msg.sender] -= _value;
        balances[_to] += _value;

        return true;
    }

    function balanceOf(address _owner) public view returns (uint256) {
        return balances[_owner];
    }

      function transferFrom(
        address _from,
        address _to,
        uint256 _value
    )
        public
        returns (bool)
    {
        require(_to != address(0));
        require(_value <= balances[_from]);
        require(_value <= allowed[_from][msg.sender]);

        balances[_from] -= _value;
        balances[_to] += _value;
        allowed[_from][msg.sender] -= _value;

        return true;
    }

    function approve(address _spender, uint256 _value) public returns (bool) {
        allowed[msg.sender][_spender] = _value;
        return true;
    }

    function allowance(
        address _owner,
        address _spender
    )
    public
    view
    returns (uint256)
    {
        return allowed[_owner][_spender];
    }
}

/* The tokens inherit their functionality from ERC20Token */
contract TokenA is ERC20Token {
}

contract TokenB is ERC20Token {
}

contract TokenC is ERC20Token {
}
contract TokenD is ERC20Token {
}

/* DO NOT USE IN PRODUCTION AS IT DOESN'T CHECK FOR UNDERFLOW/OVERFLOW */
contract TokenSwapper {
    // Keeps track of the tokens users have
    mapping(address => mapping(address => uint256)) tokenBalances_;

    function tokenBalances(address _owner, ERC20 _token) public view returns (uint256 balance) {
        return balance = tokenBalances_[_owner][_token];
    }

    function deposit(ERC20 _token, uint256 _amount) public {
        _token.transferFrom(msg.sender, address(this), _amount);
        tokenBalances_[msg.sender][_token] += _amount;
    }

    function swap(ERC20 _fromToken, ERC20 _toToken, uint256 _amount) public {
        tokenBalances_[msg.sender][_fromToken] -= _amount;
        tokenBalances_[msg.sender][_toToken] += _amount;
    }

    function withdraw(ERC20 _token, uint256 _amount) public {
        require(tokenBalances_[msg.sender][_token] >= _amount);

        _token.transfer(msg.sender, _amount);
    }
}
| improve this answer | |
  • Henk, so 'TokenSwapper' can hold any type of erc20token in tokenBalances, along as address is specified? Is there no need to generate a object from the interface initially like in crowd sales? e.g. ERC20 public token; thanks for answering, interfaces has been bothering me for a while.. isn't it possible to transfer ERC20 instead of transferFrom? – NowsyMe May 16 '18 at 16:23
  • 1
    Yes, that's correct. Using transfer is not secure, as the smart contract cannot detect when it received tokens – Henk May 16 '18 at 16:27
  • maybe this is a stupid question, but what if the interface is named 'Token' or something else, would you still be able to use ERC20 as the input argument type for _token? in the TokenSwapper contract? – NowsyMe May 16 '18 at 16:37
  • 1
    Yep, the name doesn't matter. The names of the functions, however declared in the interface, do matter. – Henk May 16 '18 at 16:39
  • How does TokenSwapper know that it has to use an interface if it's not inherited by the contract or specified within the contract? thanks so much for clearing my bottlenecks! – NowsyMe May 16 '18 at 16:43

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.