contract ERC20Tornado is Tornado {
  using SafeERC20 for IERC20;

  address public token;

  constructor(
    IVerifier _verifier,
    uint256 _denomination,
    uint32 _merkleTreeHeight,
    address _operator,
    address _governance,
    address _reserve,
    address _token
  ) Tornado(_verifier, _denomination, _merkleTreeHeight, _operator, _governance, _reserve) public {
    token = _token;
  }

  function _processDeposit() internal {
    require(msg.value == 0, "ETH value is supposed to be 0 for ERC20 instance");
    _safeErc20TransferFrom(msg.sender, address(this), denomination);
  }

  function _processWithdraw(address payable _recipient, uint256 _withdraw_fee, address payable _relayer, uint256 _fee, uint256 _refund) internal {
    require(msg.value == _refund, "Incorrect refund amount received by the contract");

    _safeErc20Transfer(reserve, _withdraw_fee);
    _safeErc20Transfer(_recipient, denomination - _withdraw_fee - _fee);
    if (_fee > 0) {
      _safeErc20Transfer(_relayer, _fee);
    }

    if (_refund > 0) {
      (bool success, ) = _recipient.call.value(_refund)("");
      if (!success) {
        // let's return _refund back to the relayer
        _relayer.transfer(_refund);
      }
    }
  }

  function _safeErc20TransferFrom(address _from, address _to, uint256 _amount) internal {
    (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd /* transferFrom */, _from, _to, _amount));
    require(success, "not enough allowed tokens");

    // if contract returns some data lets make sure that is `true` according to standard
    if (data.length > 0) {
      require(data.length == 32, "data length should be either 0 or 32 bytes");
      success = abi.decode(data, (bool));
      require(success, "not enough allowed tokens. Token returns false.");
    }
  }

  function _safeErc20Transfer(address _to, uint256 _amount) internal {
    (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb /* transfer */, _to, _amount));
    require(success, "not enough tokens");

    // if contract returns some data lets make sure that is `true` according to standard
    if (data.length > 0) {
      require(data.length == 32, "data length should be either 0 or 32 bytes");
      success = abi.decode(data, (bool));
      require(success, "not enough tokens. Token returns false.");
    }
  }

  /** @dev seize accidental sent token */
  function seize(IERC20 _token, uint amount) external onlyGovernance {
    require(address(_token) != token, "cannot be token");
    _token.safeTransfer(governance, amount);
  }
}
function seize(IERC20 _token, uint amount) external onlyGovernance {
  require(address(_token) != token, "cannot be token");
  _token.safeTransfer(governance, amount);
}

/** @dev seize accidental sent token */