Дизайн смарт-контракта: Контракт условного депонирования/контролера

Как я могу создать следующее:
Контракт условного депонирования/контроллера A, который способен принимать токены ERC20 из контракта B и ETH от стороны C и выполнять обмен при соблюдении определенных условий.

Этот же контракт A также должен иметь возможность получать токены ERC20 из контракта D и ETH из контракта E и выполнять обмен при выполнении определенных условий.

Уловка/актуальность вопроса заключается в том, как спроектировать контракт A, чтобы иметь возможность обрабатывать вводы токенов ERC20 разных типов, например токены Walton (WTC) и FUSION (FSN) ERC20?

Ответы (1)

Вот тут-то и появляются интерфейсы. Это здорово, потому что ERC-20 на самом деле является интерфейсом! Ниже я приведу пример. Как видите, им TokenSwapperдаже не нужно знать о TokenAи TokenB, просто они реализуют ERC20интерфейс.

О, и прежде чем приступить к разработке, вы должны сначала ознакомиться с основами интерфейса ERC20. Прочтите эту вики

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);
    }
}
Хенк, значит, «TokenSwapper» может содержать любой тип erc20token в tokenBalances вместе с указанным адресом? Нет необходимости изначально генерировать объект из интерфейса, как в краудсейлах? например, общедоступный токен ERC20; спасибо за ответ, интерфейсы меня давно беспокоят.. нельзя ли передать ERC20 вместо TransferFrom?
Да, это правильно. Использование transferнебезопасно, так как смарт-контракт не может определить, когда он получил токены.
может быть, это глупый вопрос, но что, если интерфейс называется «Токен» или что-то еще, сможете ли вы по-прежнему использовать ERC20 в качестве типа входного аргумента для _token? в контракте TokenSwapper?
Да, имя не имеет значения. Имена функций, как бы они ни были объявлены в интерфейсе, имеют значение.
Как TokenSwapper узнает, что он должен использовать интерфейс, если он не унаследован контрактом или не указан в контракте? Большое спасибо за устранение моих узких мест!