ETH Price: $2,470.70 (+1.20%)
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Initialize Broke...103707072020-07-01 1:35:121587 days ago1593567312IN
0x8e7554d9...5eb164b53
0 ETH0.00173640
0x60806040103707062020-07-01 1:34:561587 days ago1593567296IN
 Create: UniswapDappV2
0 ETH0.0401335240

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Block From To
123415422021-04-30 11:24:001283 days ago1619781840
0x8e7554d9...5eb164b53
0.7351695 ETH
123415422021-04-30 11:24:001283 days ago1619781840
0x8e7554d9...5eb164b53
0.7351695 ETH
123364612021-04-29 16:31:271284 days ago1619713887
0x8e7554d9...5eb164b53
0.103983 ETH
123364612021-04-29 16:31:271284 days ago1619713887
0x8e7554d9...5eb164b53
0.103983 ETH
123293102021-04-28 13:46:591285 days ago1619617619
0x8e7554d9...5eb164b53
0.879925 ETH
123293102021-04-28 13:46:591285 days ago1619617619
0x8e7554d9...5eb164b53
0.879925 ETH
123293072021-04-28 13:46:271285 days ago1619617587
0x8e7554d9...5eb164b53
15.956143 ETH
123293072021-04-28 13:46:271285 days ago1619617587
0x8e7554d9...5eb164b53
15.956143 ETH
123191642021-04-27 0:27:241287 days ago1619483244
0x8e7554d9...5eb164b53
0.229878 ETH
123191642021-04-27 0:27:241287 days ago1619483244
0x8e7554d9...5eb164b53
0.229878 ETH
123117302021-04-25 21:00:451288 days ago1619384445
0x8e7554d9...5eb164b53
0.11016 ETH
123117302021-04-25 21:00:451288 days ago1619384445
0x8e7554d9...5eb164b53
0.11016 ETH
123117232021-04-25 20:59:201288 days ago1619384360
0x8e7554d9...5eb164b53
2.203092 ETH
123117232021-04-25 20:59:201288 days ago1619384360
0x8e7554d9...5eb164b53
2.203092 ETH
123117212021-04-25 20:58:421288 days ago1619384322
0x8e7554d9...5eb164b53
0.137781 ETH
123117212021-04-25 20:58:421288 days ago1619384322
0x8e7554d9...5eb164b53
0.137781 ETH
123064512021-04-25 1:30:231289 days ago1619314223
0x8e7554d9...5eb164b53
0.1533 ETH
123064512021-04-25 1:30:231289 days ago1619314223
0x8e7554d9...5eb164b53
0.1533 ETH
123064472021-04-25 1:29:421289 days ago1619314182
0x8e7554d9...5eb164b53
3.06621 ETH
123064472021-04-25 1:29:421289 days ago1619314182
0x8e7554d9...5eb164b53
3.06621 ETH
123045052021-04-24 18:18:181289 days ago1619288298
0x8e7554d9...5eb164b53
0.1829 ETH
123045052021-04-24 18:18:181289 days ago1619288298
0x8e7554d9...5eb164b53
0.1829 ETH
122933992021-04-23 1:01:551291 days ago1619139715
0x8e7554d9...5eb164b53
0.409833 ETH
122933992021-04-23 1:01:551291 days ago1619139715
0x8e7554d9...5eb164b53
0.409833 ETH
122932662021-04-23 0:30:301291 days ago1619137830
0x8e7554d9...5eb164b53
0.769311 ETH
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
UniswapDappV2

Compiler Version
v0.5.12+commit.7709ece9

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion, MIT license

Contract Source Code (Solidity)

/**
 *Submitted for verification at Etherscan.io on 2020-07-01
*/

// File: contracts/lib/math/SafeMath.sol

pragma solidity 0.5.12;

/**
 * @dev Wrappers over Solidity's arithmetic operations with added overflow
 * checks.
 *
 * Arithmetic operations in Solidity wrap on overflow. This can easily result
 * in bugs, because programmers usually assume that an overflow raises an
 * error, which is the standard behavior in high level programming languages.
 * `SafeMath` restores this intuition by reverting the transaction when an
 * operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 */
library SafeMath {
    /**
     * @dev Returns the addition of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     * - Addition cannot overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        require(c >= a, "SafeMath: addition overflow");

        return c;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b <= a, "SafeMath: subtraction overflow");
        uint256 c = a - b;

        return c;
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     * - Multiplication cannot overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
        // benefit is lost if 'b' is also tested.
        // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
        if (a == 0) {
            return 0;
        }

        uint256 c = a * b;
        require(c / a == b, "SafeMath: multiplication overflow");

        return c;
    }

    /**
     * @dev Returns the integer division of two unsigned integers. Reverts on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        // Solidity only automatically asserts when dividing by 0
        require(b > 0, "SafeMath: division by zero");
        uint256 c = a / b;
        // assert(a == b * c + a % b); // There is no case in which this doesn't hold

        return c;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * Reverts when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b != 0, "SafeMath: modulo by zero");
        return a % b;
    }
}

// File: contracts/lib/utils/ReentrancyGuard.sol

pragma solidity ^0.5.0;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the `nonReentrant` modifier
 * available, which can be aplied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 */
contract ReentrancyGuard {
    /// @dev counter to allow mutex lock with only one SSTORE operation
    uint256 private _guardCounter;

    constructor () internal {
        // The counter starts at one to prevent changing it from zero to a non-zero
        // value, which is a more expensive operation.
        _guardCounter = 1;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and make it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _guardCounter += 1;
        uint256 localCounter = _guardCounter;
        _;
        require(localCounter == _guardCounter, "ReentrancyGuard: reentrant call");
    }
}

// File: contracts/extensions/BrokerExtension.sol

pragma solidity 0.5.12;


interface Broker {
    function owner() external returns (address);
    function isAdmin(address _user) external returns(bool);
    function markNonce(uint256 _nonce) external;
}

contract BrokerExtension is ReentrancyGuard {
    Broker public broker;

    modifier onlyAdmin() {
        require(broker.isAdmin(msg.sender), "Invalid msg.sender");
        _;
    }

    modifier onlyOwner() {
        require(broker.owner() == msg.sender, "Invalid msg.sender");
        _;
    }

    function initializeBroker(address _brokerAddress) external {
        require(_brokerAddress != address(0), "Invalid _brokerAddress");
        require(address(broker) == address(0), "Broker already set");
        broker = Broker(_brokerAddress);
    }
}

// File: contracts/Utils.sol

pragma solidity 0.5.12;


interface ERC20 {
    function balanceOf(address account) external view returns (uint256);
}

interface MarketDapp {
    // Returns the address to approve tokens for
    function tokenReceiver(address[] calldata assetIds, uint256[] calldata dataValues, address[] calldata addresses) external view returns(address);
    function trade(address[] calldata assetIds, uint256[] calldata dataValues, address[] calldata addresses, address payable recipient) external payable;
}

/// @title Util functions for the BrokerV2 contract for Switcheo Exchange
/// @author Switcheo Network
/// @notice Functions were moved from the BrokerV2 contract into this contract
/// so that the BrokerV2 contract would not exceed the maximum contract size of
/// 24 KB.
library Utils {
    using SafeMath for uint256;

    // The constants for EIP-712 are precompiled to reduce contract size,
    // the original values are left here for reference and verification.
    //
    // bytes32 public constant EIP712_DOMAIN_TYPEHASH = keccak256(abi.encodePacked(
    //     "EIP712Domain(",
    //         "string name,",
    //         "string version,",
    //         "uint256 chainId,",
    //         "address verifyingContract,",
    //         "bytes32 salt",
    //     ")"
    // ));
    // bytes32 public constant EIP712_DOMAIN_TYPEHASH = 0xd87cd6ef79d4e2b95e15ce8abf732db51ec771f1ca2edccf22a46c729ac56472;
    //
    // bytes32 public constant CONTRACT_NAME = keccak256("Switcheo Exchange");
    // bytes32 public constant CONTRACT_VERSION = keccak256("2");
    // uint256 public constant CHAIN_ID = 3; // TODO: Update this before deploying
    // address public constant VERIFYING_CONTRACT = address(1); // TODO: Update this before deploying
    // bytes32 public constant SALT = keccak256("switcheo-eth-salt");

    // bytes32 public constant DOMAIN_SEPARATOR = keccak256(abi.encode(
    //     EIP712_DOMAIN_TYPEHASH,
    //     CONTRACT_NAME,
    //     CONTRACT_VERSION,
    //     CHAIN_ID,
    //     VERIFYING_CONTRACT,
    //     SALT
    // ));
    bytes32 public constant DOMAIN_SEPARATOR = 0x14f697e312cdba1c10a1eb5c87d96fa22b63aef9dc39592568387471319ea630;

    // bytes32 public constant OFFER_TYPEHASH = keccak256(abi.encodePacked(
    //     "Offer(",
    //         "address maker,",
    //         "address offerAssetId,",
    //         "uint256 offerAmount,",
    //         "address wantAssetId,",
    //         "uint256 wantAmount,",
    //         "address feeAssetId,",
    //         "uint256 feeAmount,",
    //         "uint256 nonce",
    //     ")"
    // ));
    bytes32 public constant OFFER_TYPEHASH = 0xf845c83a8f7964bc8dd1a092d28b83573b35be97630a5b8a3b8ae2ae79cd9260;

    // bytes32 public constant CANCEL_TYPEHASH = keccak256(abi.encodePacked(
    //     "Cancel(",
    //         "bytes32 offerHash,",
    //         "address feeAssetId,",
    //         "uint256 feeAmount,",
    //     ")"
    // ));
    bytes32 public constant CANCEL_TYPEHASH = 0x46f6d088b1f0ff5a05c3f232c4567f2df96958e05457e6c0e1221dcee7d69c18;

    // bytes32 public constant FILL_TYPEHASH = keccak256(abi.encodePacked(
    //     "Fill(",
    //         "address filler,",
    //         "address offerAssetId,",
    //         "uint256 offerAmount,",
    //         "address wantAssetId,",
    //         "uint256 wantAmount,",
    //         "address feeAssetId,",
    //         "uint256 feeAmount,",
    //         "uint256 nonce",
    //     ")"
    // ));
    bytes32 public constant FILL_TYPEHASH = 0x5f59dbc3412a4575afed909d028055a91a4250ce92235f6790c155a4b2669e99;

    // The Ether token address is set as the constant 0x00 for backwards
    // compatibility
    address private constant ETHER_ADDR = address(0);

    uint256 private constant mask8 = ~(~uint256(0) << 8);
    uint256 private constant mask16 = ~(~uint256(0) << 16);
    uint256 private constant mask24 = ~(~uint256(0) << 24);
    uint256 private constant mask32 = ~(~uint256(0) << 32);
    uint256 private constant mask40 = ~(~uint256(0) << 40);
    uint256 private constant mask48 = ~(~uint256(0) << 48);
    uint256 private constant mask56 = ~(~uint256(0) << 56);
    uint256 private constant mask120 = ~(~uint256(0) << 120);
    uint256 private constant mask128 = ~(~uint256(0) << 128);
    uint256 private constant mask136 = ~(~uint256(0) << 136);
    uint256 private constant mask144 = ~(~uint256(0) << 144);

    event Trade(
        address maker,
        address taker,
        address makerGiveAsset,
        uint256 makerGiveAmount,
        address fillerGiveAsset,
        uint256 fillerGiveAmount
    );

    /// @dev Calculates the balance increments for a set of trades
    /// @param _values The _values param from the trade method
    /// @param _incrementsLength Should match the value of _addresses.length / 2
    /// from the trade method
    /// @return An array of increments
    function calculateTradeIncrements(
        uint256[] memory _values,
        uint256 _incrementsLength
    )
        public
        pure
        returns (uint256[] memory)
    {
        uint256[] memory increments = new uint256[](_incrementsLength);
        _creditFillBalances(increments, _values);
        _creditMakerBalances(increments, _values);
        _creditMakerFeeBalances(increments, _values);
        return increments;
    }

    /// @dev Calculates the balance decrements for a set of trades
    /// @param _values The _values param from the trade method
    /// @param _decrementsLength Should match the value of _addresses.length / 2
    /// from the trade method
    /// @return An array of decrements
    function calculateTradeDecrements(
        uint256[] memory _values,
        uint256 _decrementsLength
    )
        public
        pure
        returns (uint256[] memory)
    {
        uint256[] memory decrements = new uint256[](_decrementsLength);
        _deductFillBalances(decrements, _values);
        _deductMakerBalances(decrements, _values);
        return decrements;
    }

    /// @dev Calculates the balance increments for a set of network trades
    /// @param _values The _values param from the networkTrade method
    /// @param _incrementsLength Should match the value of _addresses.length / 2
    /// from the networkTrade method
    /// @return An array of increments
    function calculateNetworkTradeIncrements(
        uint256[] memory _values,
        uint256 _incrementsLength
    )
        public
        pure
        returns (uint256[] memory)
    {
        uint256[] memory increments = new uint256[](_incrementsLength);
        _creditMakerBalances(increments, _values);
        _creditMakerFeeBalances(increments, _values);
        return increments;
    }

    /// @dev Calculates the balance decrements for a set of network trades
    /// @param _values The _values param from the trade method
    /// @param _decrementsLength Should match the value of _addresses.length / 2
    /// from the networkTrade method
    /// @return An array of decrements
    function calculateNetworkTradeDecrements(
        uint256[] memory _values,
        uint256 _decrementsLength
    )
        public
        pure
        returns (uint256[] memory)
    {
        uint256[] memory decrements = new uint256[](_decrementsLength);
        _deductMakerBalances(decrements, _values);
        return decrements;
    }

    /// @dev Validates `BrokerV2.trade` parameters to ensure trade fairness,
    /// see `BrokerV2.trade` for param details.
    /// @param _values Values from `trade`
    /// @param _hashes Hashes from `trade`
    /// @param _addresses Addresses from `trade`
    function validateTrades(
        uint256[] memory _values,
        bytes32[] memory _hashes,
        address[] memory _addresses,
        address _operator
    )
        public
        returns (bytes32[] memory)
    {
        _validateTradeInputLengths(_values, _hashes);
        _validateUniqueOffers(_values);
        _validateMatches(_values, _addresses);
        _validateFillAmounts(_values);
        _validateTradeData(_values, _addresses, _operator);

        // validate signatures of all offers
        _validateTradeSignatures(
            _values,
            _hashes,
            _addresses,
            OFFER_TYPEHASH,
            0,
            _values[0] & mask8 // numOffers
        );

        // validate signatures of all fills
        _validateTradeSignatures(
            _values,
            _hashes,
            _addresses,
            FILL_TYPEHASH,
            _values[0] & mask8, // numOffers
            (_values[0] & mask8) + ((_values[0] & mask16) >> 8) // numOffers + numFills
        );

        _emitTradeEvents(_values, _addresses, new address[](0), false);

        return _hashes;
    }

    /// @dev Validates `BrokerV2.networkTrade` parameters to ensure trade fairness,
    /// see `BrokerV2.networkTrade` for param details.
    /// @param _values Values from `networkTrade`
    /// @param _hashes Hashes from `networkTrade`
    /// @param _addresses Addresses from `networkTrade`
    /// @param _operator Address of the `BrokerV2.operator`
    function validateNetworkTrades(
        uint256[] memory _values,
        bytes32[] memory _hashes,
        address[] memory _addresses,
        address _operator
    )
        public
        pure
        returns (bytes32[] memory)
    {
        _validateNetworkTradeInputLengths(_values, _hashes);
        _validateUniqueOffers(_values);
        _validateNetworkMatches(_values, _addresses, _operator);
        _validateTradeData(_values, _addresses, _operator);

        // validate signatures of all offers
        _validateTradeSignatures(
            _values,
            _hashes,
            _addresses,
            OFFER_TYPEHASH,
            0,
            _values[0] & mask8 // numOffers
        );

        return _hashes;
    }

    /// @dev Executes trades against external markets,
    /// see `BrokerV2.networkTrade` for param details.
    /// @param _values Values from `networkTrade`
    /// @param _addresses Addresses from `networkTrade`
    /// @param _marketDapps See `BrokerV2.marketDapps`
    function performNetworkTrades(
        uint256[] memory _values,
        address[] memory _addresses,
        address[] memory _marketDapps
    )
        public
        returns (uint256[] memory)
    {
        uint256[] memory increments = new uint256[](_addresses.length / 2);
        // i = 1 + numOffers * 2
        uint256 i = 1 + (_values[0] & mask8) * 2;
        uint256 end = _values.length;

        // loop matches
        for(i; i < end; i++) {
            uint256[] memory data = new uint256[](9);
            data[0] = _values[i]; // match data
            data[1] = data[0] & mask8; // offerIndex
            data[2] = (data[0] & mask24) >> 16; // operator.surplusAssetIndex
            data[3] = _values[data[1] * 2 + 1]; // offer.dataA
            data[4] = _values[data[1] * 2 + 2]; // offer.dataB
            data[5] = ((data[3] & mask16) >> 8); // maker.offerAssetIndex
            data[6] = ((data[3] & mask24) >> 16); // maker.wantAssetIndex
            // amount of offerAssetId to take from the offer is equal to the match.takeAmount
            data[7] = data[0] >> 128;
            // expected amount to receive is: matchData.takeAmount * offer.wantAmount / offer.offerAmount
            data[8] = data[7].mul(data[4] >> 128).div(data[4] & mask128);

            address[] memory assetIds = new address[](3);
            assetIds[0] = _addresses[data[5] * 2 + 1]; // offer.offerAssetId
            assetIds[1] = _addresses[data[6] * 2 + 1]; // offer.wantAssetId
            assetIds[2] = _addresses[data[2] * 2 + 1]; // surplusAssetId

            uint256[] memory dataValues = new uint256[](3);
            dataValues[0] = data[7]; // the proportion of offerAmount to offer
            dataValues[1] = data[8]; // the proportion of wantAmount to receive for the offer
            dataValues[2] = data[0]; // match data

            increments[data[2]] = _performNetworkTrade(
                assetIds,
                dataValues,
                _marketDapps,
                _addresses
            );
        }

        _emitTradeEvents(_values, _addresses, _marketDapps, true);

        return increments;
    }

    /// @dev Validates the signature of a cancel invocation
    /// @param _values The _values param from the cancel method
    /// @param _hashes The _hashes param from the cancel method
    /// @param _addresses The _addresses param from the cancel method
    function validateCancel(
        uint256[] memory _values,
        bytes32[] memory _hashes,
        address[] memory _addresses
    )
        public
        pure
    {
        bytes32 offerHash = hashOffer(_values, _addresses);

        bytes32 cancelHash = keccak256(abi.encode(
            CANCEL_TYPEHASH,
            offerHash,
            _addresses[4],
            _values[1] >> 128
        ));

        validateSignature(
            cancelHash,
            _addresses[0], // maker
            uint8((_values[2] & mask144) >> 136), // v
            _hashes[0], // r
            _hashes[1], // s
            ((_values[2] & mask136) >> 128) != 0 // prefixedSignature
        );
    }

    /// @dev Hashes an offer for the cancel method
    /// @param _values The _values param from the cancel method
    /// @param _addresses THe _addresses param from the cancel method
    /// @return The hash of the offer
    function hashOffer(
        uint256[] memory _values,
        address[] memory _addresses
    )
        public
        pure
        returns (bytes32)
    {
        return keccak256(abi.encode(
            OFFER_TYPEHASH,
            _addresses[0], // maker
            _addresses[1], // offerAssetId
            _values[0] & mask128, // offerAmount
            _addresses[2], // wantAssetId
            _values[0] >> 128, // wantAmount
            _addresses[3], // feeAssetId
            _values[1] & mask128, // feeAmount
            _values[2] >> 144 // offerNonce
        ));
    }

    /// @notice Approves a token transfer
    /// @param _assetId The address of the token to approve
    /// @param _spender The address of the spender to approve
    /// @param _amount The number of tokens to approve
    function approveTokenTransfer(
        address _assetId,
        address _spender,
        uint256 _amount
    )
        public
    {
        _validateContractAddress(_assetId);

        // Some tokens have an `approve` which returns a boolean and some do not.
        // The ERC20 interface cannot be used here because it requires specifying
        // an explicit return value, and an EVM exception would be raised when calling
        // a token with the mismatched return value.
        bytes memory payload = abi.encodeWithSignature(
            "approve(address,uint256)",
            _spender,
            _amount
        );
        bytes memory returnData = _callContract(_assetId, payload);
        // Ensure that the asset transfer succeeded
        _validateContractCallResult(returnData);
    }

    /// @notice Transfers tokens into the contract
    /// @param _user The address to transfer the tokens from
    /// @param _assetId The address of the token to transfer
    /// @param _amount The number of tokens to transfer
    /// @param _expectedAmount The number of tokens expected to be received,
    /// this may not match `_amount`, for example, tokens which have a
    /// proportion burnt on transfer will have a different amount received.
    function transferTokensIn(
        address _user,
        address _assetId,
        uint256 _amount,
        uint256 _expectedAmount
    )
        public
    {
        _validateContractAddress(_assetId);

        uint256 initialBalance = tokenBalance(_assetId);

        // Some tokens have a `transferFrom` which returns a boolean and some do not.
        // The ERC20 interface cannot be used here because it requires specifying
        // an explicit return value, and an EVM exception would be raised when calling
        // a token with the mismatched return value.
        bytes memory payload = abi.encodeWithSignature(
            "transferFrom(address,address,uint256)",
            _user,
            address(this),
            _amount
        );
        bytes memory returnData = _callContract(_assetId, payload);
        // Ensure that the asset transfer succeeded
        _validateContractCallResult(returnData);

        uint256 finalBalance = tokenBalance(_assetId);
        uint256 transferredAmount = finalBalance.sub(initialBalance);

        require(transferredAmount == _expectedAmount, "Invalid transfer");
    }

    /// @notice Transfers tokens from the contract to a user
    /// @param _receivingAddress The address to transfer the tokens to
    /// @param _assetId The address of the token to transfer
    /// @param _amount The number of tokens to transfer
    function transferTokensOut(
        address _receivingAddress,
        address _assetId,
        uint256 _amount
    )
        public
    {
        _validateContractAddress(_assetId);

        // Some tokens have a `transfer` which returns a boolean and some do not.
        // The ERC20 interface cannot be used here because it requires specifying
        // an explicit return value, and an EVM exception would be raised when calling
        // a token with the mismatched return value.
        bytes memory payload = abi.encodeWithSignature(
                                   "transfer(address,uint256)",
                                   _receivingAddress,
                                   _amount
                               );
        bytes memory returnData = _callContract(_assetId, payload);

        // Ensure that the asset transfer succeeded
        _validateContractCallResult(returnData);
    }

    /// @notice Returns the number of tokens owned by this contract
    /// @param _assetId The address of the token to query
    function externalBalance(address _assetId) public view returns (uint256) {
        if (_assetId == ETHER_ADDR) {
            return address(this).balance;
        }
        return tokenBalance(_assetId);
    }

    /// @notice Returns the number of tokens owned by this contract.
    /// @dev This will not work for Ether tokens, use `externalBalance` for
    /// Ether tokens.
    /// @param _assetId The address of the token to query
    function tokenBalance(address _assetId) public view returns (uint256) {
        return ERC20(_assetId).balanceOf(address(this));
    }

    /// @dev Validates that the specified `_hash` was signed by the specified `_user`.
    /// This method supports the EIP712 specification, the older Ethereum
    /// signed message specification is also supported for backwards compatibility.
    /// @param _hash The original hash that was signed by the user
    /// @param _user The user who signed the hash
    /// @param _v The `v` component of the `_user`'s signature
    /// @param _r The `r` component of the `_user`'s signature
    /// @param _s The `s` component of the `_user`'s signature
    /// @param _prefixed If true, the signature will be verified
    /// against the Ethereum signed message specification instead of the
    /// EIP712 specification
    function validateSignature(
        bytes32 _hash,
        address _user,
        uint8 _v,
        bytes32 _r,
        bytes32 _s,
        bool _prefixed
    )
        public
        pure
    {
        bytes32 eip712Hash = keccak256(abi.encodePacked(
            "\x19\x01",
            DOMAIN_SEPARATOR,
            _hash
        ));

        if (_prefixed) {
            bytes32 prefixedHash = keccak256(abi.encodePacked(
                "\x19Ethereum Signed Message:\n32",
                eip712Hash
            ));
            require(_user == ecrecover(prefixedHash, _v, _r, _s), "Invalid signature");
        } else {
            require(_user == ecrecover(eip712Hash, _v, _r, _s), "Invalid signature");
        }
    }

    /// @dev Ensures that `_address` is not the zero address
    /// @param _address The address to check
    function validateAddress(address _address) public pure {
        require(_address != address(0), "Invalid address");
    }

    /// @dev Credit fillers for each fill.wantAmount,and credit the operator
    /// for each fill.feeAmount. See the `trade` method for param details.
    /// @param _values Values from `trade`
    function _creditFillBalances(
        uint256[] memory _increments,
        uint256[] memory _values
    )
        private
        pure
    {
        // 1 + numOffers * 2
        uint256 i = 1 + (_values[0] & mask8) * 2;
        // i + numFills * 2
        uint256 end = i + ((_values[0] & mask16) >> 8) * 2;

        // loop fills
        for(i; i < end; i += 2) {
            uint256 fillerWantAssetIndex = (_values[i] & mask24) >> 16;
            uint256 wantAmount = _values[i + 1] >> 128;

            // credit fill.wantAmount to filler
            _increments[fillerWantAssetIndex] = _increments[fillerWantAssetIndex].add(wantAmount);

            uint256 feeAmount = _values[i] >> 128;
            if (feeAmount == 0) { continue; }

            uint256 operatorFeeAssetIndex = ((_values[i] & mask40) >> 32);
            // credit fill.feeAmount to operator
            _increments[operatorFeeAssetIndex] = _increments[operatorFeeAssetIndex].add(feeAmount);
        }
    }

    /// @dev Credit makers for each amount received through a matched fill.
    /// See the `trade` method for param details.
    /// @param _values Values from `trade`
    function _creditMakerBalances(
        uint256[] memory _increments,
        uint256[] memory _values
    )
        private
        pure
    {
        uint256 i = 1;
        // i += numOffers * 2
        i += (_values[0] & mask8) * 2;
        // i += numFills * 2
        i += ((_values[0] & mask16) >> 8) * 2;

        uint256 end = _values.length;

        // loop matches
        for(i; i < end; i++) {
            // match.offerIndex
            uint256 offerIndex = _values[i] & mask8;
            // maker.wantAssetIndex
            uint256 makerWantAssetIndex = (_values[1 + offerIndex * 2] & mask24) >> 16;

            // match.takeAmount
            uint256 amount = _values[i] >> 128;
            // receiveAmount = match.takeAmount * offer.wantAmount / offer.offerAmount
            amount = amount.mul(_values[2 + offerIndex * 2] >> 128)
                           .div(_values[2 + offerIndex * 2] & mask128);

            // credit maker for the amount received from the match
            _increments[makerWantAssetIndex] = _increments[makerWantAssetIndex].add(amount);
        }
    }

    /// @dev Credit the operator for each offer.feeAmount if the offer has not
    /// been recorded through a previous `trade` call.
    /// See the `trade` method for param details.
    /// @param _values Values from `trade`
    function _creditMakerFeeBalances(
        uint256[] memory _increments,
        uint256[] memory _values
    )
        private
        pure
    {
        uint256 i = 1;
        // i + numOffers * 2
        uint256 end = i + (_values[0] & mask8) * 2;

        // loop offers
        for(i; i < end; i += 2) {
            bool nonceTaken = ((_values[i] & mask128) >> 120) == 1;
            if (nonceTaken) { continue; }

            uint256 feeAmount = _values[i] >> 128;
            if (feeAmount == 0) { continue; }

            uint256 operatorFeeAssetIndex = (_values[i] & mask40) >> 32;

            // credit make.feeAmount to operator
            _increments[operatorFeeAssetIndex] = _increments[operatorFeeAssetIndex].add(feeAmount);
        }
    }

    /// @dev Deduct tokens from fillers for each fill.offerAmount
    /// and each fill.feeAmount.
    /// See the `trade` method for param details.
    /// @param _values Values from `trade`
    function _deductFillBalances(
        uint256[] memory _decrements,
        uint256[] memory _values
    )
        private
        pure
    {
        // 1 + numOffers * 2
        uint256 i = 1 + (_values[0] & mask8) * 2;
        // i + numFills * 2
        uint256 end = i + ((_values[0] & mask16) >> 8) * 2;

        // loop fills
        for(i; i < end; i += 2) {
            uint256 fillerOfferAssetIndex = (_values[i] & mask16) >> 8;
            uint256 offerAmount = _values[i + 1] & mask128;

            // deduct fill.offerAmount from filler
            _decrements[fillerOfferAssetIndex] = _decrements[fillerOfferAssetIndex].add(offerAmount);

            uint256 feeAmount = _values[i] >> 128;
            if (feeAmount == 0) { continue; }

            // deduct fill.feeAmount from filler
            uint256 fillerFeeAssetIndex = (_values[i] & mask32) >> 24;
            _decrements[fillerFeeAssetIndex] = _decrements[fillerFeeAssetIndex].add(feeAmount);
        }
    }

    /// @dev Deduct tokens from makers for each offer.offerAmount
    /// and each offer.feeAmount if the offer has not been recorded
    /// through a previous `trade` call.
    /// See the `trade` method for param details.
    /// @param _values Values from `trade`
    function _deductMakerBalances(
        uint256[] memory _decrements,
        uint256[] memory _values
    )
        private
        pure
    {
        uint256 i = 1;
        // i + numOffers * 2
        uint256 end = i + (_values[0] & mask8) * 2;

        // loop offers
        for(i; i < end; i += 2) {
            bool nonceTaken = ((_values[i] & mask128) >> 120) == 1;
            if (nonceTaken) { continue; }

            uint256 makerOfferAssetIndex = (_values[i] & mask16) >> 8;
            uint256 offerAmount = _values[i + 1] & mask128;

            // deduct make.offerAmount from maker
            _decrements[makerOfferAssetIndex] = _decrements[makerOfferAssetIndex].add(offerAmount);

            uint256 feeAmount = _values[i] >> 128;
            if (feeAmount == 0) { continue; }

            // deduct make.feeAmount from maker
            uint256 makerFeeAssetIndex = (_values[i] & mask32) >> 24;
            _decrements[makerFeeAssetIndex] = _decrements[makerFeeAssetIndex].add(feeAmount);
        }
    }

    /// @dev Emits trade events for easier tracking
    /// @param _values The _values param from the trade / networkTrade method
    /// @param _addresses The _addresses param from the trade / networkTrade method
    /// @param _marketDapps The _marketDapps from BrokerV2
    /// @param _forNetworkTrade Whether this is called from the networkTrade method
    function _emitTradeEvents(
        uint256[] memory _values,
        address[] memory _addresses,
        address[] memory _marketDapps,
        bool _forNetworkTrade
    )
        private
    {
        uint256 i = 1;
        // i += numOffers * 2
        i += (_values[0] & mask8) * 2;
        // i += numFills * 2
        i += ((_values[0] & mask16) >> 8) * 2;

        uint256 end = _values.length;

        // loop matches
        for(i; i < end; i++) {
            uint256[] memory data = new uint256[](7);
            data[0] = _values[i] & mask8; // match.offerIndex
            data[1] = _values[1 + data[0] * 2] & mask8; // makerIndex
            data[2] = (_values[1 + data[0] * 2] & mask16) >> 8; // makerOfferAssetIndex
            data[3] = (_values[1 + data[0] * 2] & mask24) >> 16; // makerWantAssetIndex
            data[4] = _values[i] >> 128; // match.takeAmount
            // receiveAmount = match.takeAmount * offer.wantAmount / offer.offerAmount
            data[5] = data[4].mul(_values[2 + data[0] * 2] >> 128)
                             .div(_values[2 + data[0] * 2] & mask128);
            // match.fillIndex for `trade`, marketDappIndex for `networkTrade`
            data[6] = (_values[i] & mask16) >> 8;

            address filler;
            if (_forNetworkTrade) {
                filler = _marketDapps[data[6]];
            } else {
                uint256 fillerIndex = (_values[1 + data[6] * 2] & mask8);
                filler = _addresses[fillerIndex * 2];
            }

            emit Trade(
                _addresses[data[1] * 2], // maker
                filler,
                _addresses[data[2] * 2 + 1], // makerGiveAsset
                data[4], // makerGiveAmount
                _addresses[data[3] * 2 + 1], // fillerGiveAsset
                data[5] // fillerGiveAmount
            );
        }
    }


    /// @notice Executes a trade against an external market.
    /// @dev The initial Ether or token balance is compared with the
    /// balance after the trade to ensure that the appropriate amounts of
    /// tokens were taken and an appropriate amount received.
    /// The trade will fail if the number of tokens received is less than
    /// expected. If the number of tokens received is more than expected than
    /// the excess tokens are transferred to the `BrokerV2.operator`.
    /// @param _assetIds[0] The offerAssetId of the offer
    /// @param _assetIds[1] The wantAssetId of the offer
    /// @param _assetIds[2] The surplusAssetId
    /// @param _dataValues[0] The number of tokens offerred
    /// @param _dataValues[1] The number of tokens expected to be received
    /// @param _dataValues[2] Match data
    /// @param _marketDapps See `BrokerV2.marketDapps`
    /// @param _addresses Addresses from `networkTrade`
    function _performNetworkTrade(
        address[] memory _assetIds,
        uint256[] memory _dataValues,
        address[] memory _marketDapps,
        address[] memory _addresses
    )
        private
        returns (uint256)
    {
        uint256 dappIndex = (_dataValues[2] & mask16) >> 8;
        validateAddress(_marketDapps[dappIndex]);
        MarketDapp marketDapp = MarketDapp(_marketDapps[dappIndex]);

        uint256[] memory funds = new uint256[](6);
        funds[0] = externalBalance(_assetIds[0]); // initialOfferTokenBalance
        funds[1] = externalBalance(_assetIds[1]); // initialWantTokenBalance
        if (_assetIds[2] != _assetIds[0] && _assetIds[2] != _assetIds[1]) {
            funds[2] = externalBalance(_assetIds[2]); // initialSurplusTokenBalance
        }

        uint256 ethValue = 0;
        address tokenReceiver;

        if (_assetIds[0] == ETHER_ADDR) {
            ethValue = _dataValues[0]; // offerAmount
        } else {
            tokenReceiver = marketDapp.tokenReceiver(_assetIds, _dataValues, _addresses);
            approveTokenTransfer(
                _assetIds[0], // offerAssetId
                tokenReceiver,
                _dataValues[0] // offerAmount
            );
        }

        marketDapp.trade.value(ethValue)(
            _assetIds,
            _dataValues,
            _addresses,
            // use uint160 to cast `address` to `address payable`
            address(uint160(address(this))) // destAddress
        );

        funds[3] = externalBalance(_assetIds[0]); // finalOfferTokenBalance
        funds[4] = externalBalance(_assetIds[1]); // finalWantTokenBalance
        if (_assetIds[2] != _assetIds[0] && _assetIds[2] != _assetIds[1]) {
            funds[5] = externalBalance(_assetIds[2]); // finalSurplusTokenBalance
        }

        uint256 surplusAmount = 0;

        // validate that the appropriate offerAmount was deducted
        // surplusAssetId == offerAssetId
        if (_assetIds[2] == _assetIds[0]) {
            // surplusAmount = finalOfferTokenBalance - (initialOfferTokenBalance - offerAmount)
            surplusAmount = funds[3].sub(funds[0].sub(_dataValues[0]));
        } else {
            // finalOfferTokenBalance == initialOfferTokenBalance - offerAmount
            require(funds[3] == funds[0].sub(_dataValues[0]), "Invalid offer asset balance");
        }

        // validate that the appropriate wantAmount was credited
        // surplusAssetId == wantAssetId
        if (_assetIds[2] == _assetIds[1]) {
            // surplusAmount = finalWantTokenBalance - (initialWantTokenBalance + wantAmount)
            surplusAmount = funds[4].sub(funds[1].add(_dataValues[1]));
        } else {
            // finalWantTokenBalance == initialWantTokenBalance + wantAmount
            require(funds[4] == funds[1].add(_dataValues[1]), "Invalid want asset balance");
        }

        // surplusAssetId != offerAssetId && surplusAssetId != wantAssetId
        if (_assetIds[2] != _assetIds[0] && _assetIds[2] != _assetIds[1]) {
            // surplusAmount = finalSurplusTokenBalance - initialSurplusTokenBalance
            surplusAmount = funds[5].sub(funds[2]);
        }

        // set the approved token amount back to zero
        if (_assetIds[0] != ETHER_ADDR) {
            approveTokenTransfer(
                _assetIds[0],
                tokenReceiver,
                0
            );
        }

        return surplusAmount;
    }

    /// @dev Validates input lengths based on the expected format
    /// detailed in the `trade` method.
    /// @param _values Values from `trade`
    /// @param _hashes Hashes from `trade`
    function _validateTradeInputLengths(
        uint256[] memory _values,
        bytes32[] memory _hashes
    )
        private
        pure
    {
        uint256 numOffers = _values[0] & mask8;
        uint256 numFills = (_values[0] & mask16) >> 8;
        uint256 numMatches = (_values[0] & mask24) >> 16;

        // Validate that bits(24..256) are zero
        require(_values[0] >> 24 == 0, "Invalid trade input");

        // It is enforced by other checks that if a fill is present
        // then it must be completely filled so there must be at least one offer
        // and at least one match in this case.
        // It is possible to have one offer with no matches and no fills
        // but that is blocked by this check as there is no foreseeable use
        // case for it.
        require(
            numOffers > 0 && numFills > 0 && numMatches > 0,
            "Invalid trade input"
        );

        require(
            _values.length == 1 + numOffers * 2 + numFills * 2 + numMatches,
            "Invalid _values.length"
        );

        require(
            _hashes.length == (numOffers + numFills) * 2,
            "Invalid _hashes.length"
        );
    }

    /// @dev Validates input lengths based on the expected format
    /// detailed in the `networkTrade` method.
    /// @param _values Values from `networkTrade`
    /// @param _hashes Hashes from `networkTrade`
    function _validateNetworkTradeInputLengths(
        uint256[] memory _values,
        bytes32[] memory _hashes
    )
        private
        pure
    {
        uint256 numOffers = _values[0] & mask8;
        uint256 numFills = (_values[0] & mask16) >> 8;
        uint256 numMatches = (_values[0] & mask24) >> 16;

        // Validate that bits(24..256) are zero
        require(_values[0] >> 24 == 0, "Invalid networkTrade input");

        // Validate that numFills is zero because the offers
        // should be filled against external orders
        require(
            numOffers > 0 && numMatches > 0 && numFills == 0,
            "Invalid networkTrade input"
        );

        require(
            _values.length == 1 + numOffers * 2 + numMatches,
            "Invalid _values.length"
        );

        require(
            _hashes.length == numOffers * 2,
            "Invalid _hashes.length"
        );
    }

    /// @dev See the `BrokerV2.trade` method for an explanation of why offer
    /// uniquness is required.
    /// The set of offers in `_values` must be sorted such that offer nonces'
    /// are arranged in a strictly ascending order.
    /// This allows the validation of offer uniqueness to be done in O(N) time,
    /// with N being the number of offers.
    /// @param _values Values from `trade`
    function _validateUniqueOffers(uint256[] memory _values) private pure {
        uint256 numOffers = _values[0] & mask8;

        uint256 prevNonce;

        for(uint256 i = 0; i < numOffers; i++) {
            uint256 nonce = (_values[i * 2 + 1] & mask120) >> 56;

            if (i == 0) {
                // Set the value of the first nonce
                prevNonce = nonce;
                continue;
            }

            require(nonce > prevNonce, "Invalid offer nonces");
            prevNonce = nonce;
        }
    }

    /// @dev Validate that for every match:
    /// 1. offerIndexes fall within the range of offers
    /// 2. fillIndexes falls within the range of fills
    /// 3. offer.offerAssetId == fill.wantAssetId
    /// 4. offer.wantAssetId == fill.offerAssetId
    /// 5. takeAmount > 0
    /// 6. (offer.wantAmount * takeAmount) % offer.offerAmount == 0
    /// @param _values Values from `trade`
    /// @param _addresses Addresses from `trade`
    function _validateMatches(
        uint256[] memory _values,
        address[] memory _addresses
    )
        private
        pure
    {
        uint256 numOffers = _values[0] & mask8;
        uint256 numFills = (_values[0] & mask16) >> 8;

        uint256 i = 1 + numOffers * 2 + numFills * 2;
        uint256 end = _values.length;

        // loop matches
        for (i; i < end; i++) {
            uint256 offerIndex = _values[i] & mask8;
            uint256 fillIndex = (_values[i] & mask16) >> 8;

            require(offerIndex < numOffers, "Invalid match.offerIndex");

            require(fillIndex >= numOffers && fillIndex < numOffers + numFills, "Invalid match.fillIndex");

            require(
                _addresses[(_values[1 + offerIndex * 2] & mask8)] !=
                _addresses[(_values[1 + fillIndex * 2] & mask8)],
                "offer.maker cannot be the same as fill.filler"
            );

            uint256 makerOfferAssetIndex = (_values[1 + offerIndex * 2] & mask16) >> 8;
            uint256 makerWantAssetIndex = (_values[1 + offerIndex * 2] & mask24) >> 16;
            uint256 fillerOfferAssetIndex = (_values[1 + fillIndex * 2] & mask16) >> 8;
            uint256 fillerWantAssetIndex = (_values[1 + fillIndex * 2] & mask24) >> 16;

            require(
                _addresses[makerOfferAssetIndex * 2 + 1] ==
                _addresses[fillerWantAssetIndex * 2 + 1],
                "offer.offerAssetId does not match fill.wantAssetId"
            );

            require(
                _addresses[makerWantAssetIndex * 2 + 1] ==
                _addresses[fillerOfferAssetIndex * 2 + 1],
                "offer.wantAssetId does not match fill.offerAssetId"
            );

            // require that bits(16..128) are all zero for every match
            require((_values[i] & mask128) >> 16 == uint256(0), "Invalid match data");

            uint256 takeAmount = _values[i] >> 128;
            require(takeAmount > 0, "Invalid match.takeAmount");

            uint256 offerDataB = _values[2 + offerIndex * 2];
            // (offer.wantAmount * takeAmount) % offer.offerAmount == 0
            require(
                (offerDataB >> 128).mul(takeAmount).mod(offerDataB & mask128) == 0,
                "Invalid amounts"
            );
        }
    }

    /// @dev Validate that for every match:
    /// 1. offerIndexes fall within the range of offers
    /// 2. _addresses[surplusAssetIndexes * 2] matches the operator address
    /// 3. takeAmount > 0
    /// 4. (offer.wantAmount * takeAmount) % offer.offerAmount == 0
    /// @param _values Values from `trade`
    /// @param _addresses Addresses from `trade`
    /// @param _operator Address of the `BrokerV2.operator`
    function _validateNetworkMatches(
        uint256[] memory _values,
        address[] memory _addresses,
        address _operator
    )
        private
        pure
    {
        uint256 numOffers = _values[0] & mask8;

        // 1 + numOffers * 2
        uint256 i = 1 + (_values[0] & mask8) * 2;
        uint256 end = _values.length;

        // loop matches
        for (i; i < end; i++) {
            uint256 offerIndex = _values[i] & mask8;
            uint256 surplusAssetIndex = (_values[i] & mask24) >> 16;

            require(offerIndex < numOffers, "Invalid match.offerIndex");
            require(_addresses[surplusAssetIndex * 2] == _operator, "Invalid operator address");

            uint256 takeAmount = _values[i] >> 128;
            require(takeAmount > 0, "Invalid match.takeAmount");

            uint256 offerDataB = _values[2 + offerIndex * 2];
            // (offer.wantAmount * takeAmount) % offer.offerAmount == 0
            require(
                (offerDataB >> 128).mul(takeAmount).mod(offerDataB & mask128) == 0,
                "Invalid amounts"
            );
        }
    }

    /// @dev Validate that all fills will be completely filled by the specified
    /// matches. See the `BrokerV2.trade` method for an explanation of why
    /// fills must be completely filled.
    /// @param _values Values from `trade`
    function _validateFillAmounts(uint256[] memory _values) private pure {
        // "filled" is used to store the sum of `takeAmount`s and `giveAmount`s.
        // While a fill's `offerAmount` and `wantAmount` are combined to share
        // a single uint256 value, each sum of `takeAmount`s and `giveAmount`s
        // for a fill is tracked with an individual uint256 value.
        // This is to prevent the verification from being vulnerable to overflow
        // issues.
        uint256[] memory filled = new uint256[](_values.length);

        uint256 i = 1;
        // i += numOffers * 2
        i += (_values[0] & mask8) * 2;
        // i += numFills * 2
        i += ((_values[0] & mask16) >> 8) * 2;

        uint256 end = _values.length;

        // loop matches
        for (i; i < end; i++) {
            uint256 offerIndex = _values[i] & mask8;
            uint256 fillIndex = (_values[i] & mask16) >> 8;
            uint256 takeAmount = _values[i] >> 128;
            uint256 wantAmount = _values[2 + offerIndex * 2] >> 128;
            uint256 offerAmount = _values[2 + offerIndex * 2] & mask128;
            // giveAmount = takeAmount * wantAmount / offerAmount
            uint256 giveAmount = takeAmount.mul(wantAmount).div(offerAmount);

            // (1 + fillIndex * 2) would give the index of the first part
            // of the data for the fill at fillIndex within `_values`,
            // and (2 + fillIndex * 2) would give the index of the second part
            filled[1 + fillIndex * 2] = filled[1 + fillIndex * 2].add(giveAmount);
            filled[2 + fillIndex * 2] = filled[2 + fillIndex * 2].add(takeAmount);
        }

        // numOffers
        i = _values[0] & mask8;
        // i + numFills
        end = i + ((_values[0] & mask16) >> 8);

        // loop fills
        for(i; i < end; i++) {
            require(
                // fill.offerAmount == (sum of given amounts for fill)
                _values[i * 2 + 2] & mask128 == filled[i * 2 + 1] &&
                // fill.wantAmount == (sum of taken amounts for fill)
                _values[i * 2 + 2] >> 128 == filled[i * 2 + 2],
                "Invalid fills"
            );
        }
    }

    /// @dev Validates that for every offer / fill
    /// 1. user address matches address referenced by user.offerAssetIndex
    /// 2. user address matches address referenced by user.wantAssetIndex
    /// 3. user address matches address referenced by user.feeAssetIndex
    /// 4. offerAssetId != wantAssetId
    /// 5. offerAmount > 0 && wantAmount > 0
    /// 6. Specified `operator` address matches the expected `operator` address,
    /// 7. Specified `operator.feeAssetId` matches the offer's feeAssetId
    /// @param _values Values from `trade`
    /// @param _addresses Addresses from `trade`
    function _validateTradeData(
        uint256[] memory _values,
        address[] memory _addresses,
        address _operator
    )
        private
        pure
    {
        // numOffers + numFills
        uint256 end = (_values[0] & mask8) +
                      ((_values[0] & mask16) >> 8);

        for (uint256 i = 0; i < end; i++) {
            uint256 dataA = _values[i * 2 + 1];
            uint256 dataB = _values[i * 2 + 2];

            require(
                // user address == user in user.offerAssetIndex pair
                _addresses[(dataA & mask8) * 2] ==
                _addresses[((dataA & mask16) >> 8) * 2],
                "Invalid user in user.offerAssetIndex"
            );

            require(
                // user address == user in user.wantAssetIndex pair
                _addresses[(dataA & mask8) * 2] ==
                _addresses[((dataA & mask24) >> 16) * 2],
                "Invalid user in user.wantAssetIndex"
            );

            require(
                // user address == user in user.feeAssetIndex pair
                _addresses[(dataA & mask8) * 2] ==
                _addresses[((dataA & mask32) >> 24) * 2],
                "Invalid user in user.feeAssetIndex"
            );

            require(
                // offerAssetId != wantAssetId
                _addresses[((dataA & mask16) >> 8) * 2 + 1] !=
                _addresses[((dataA & mask24) >> 16) * 2 + 1],
                "Invalid trade assets"
            );

            require(
                // offerAmount > 0 && wantAmount > 0
                (dataB & mask128) > 0 && (dataB >> 128) > 0,
                "Invalid trade amounts"
            );

            uint256 operatorFeeAssetIndex = ((dataA & mask40) >> 32) * 2;

             require(
                _addresses[operatorFeeAssetIndex] == _operator,
                "Invalid operator address"
            );

             require(
                _addresses[operatorFeeAssetIndex + 1] ==
                _addresses[((dataA & mask32) >> 24) * 2 + 1],
                "Invalid operator fee asset ID"
            );
        }
    }

    /// @dev Validates signatures for a set of offers or fills
    /// Note that the r value of the offer / fill in _hashes will be
    /// overwritten by the hash of that offer / fill
    /// @param _values Values from `trade`
    /// @param _hashes Hashes from `trade`
    /// @param _addresses Addresses from `trade`
    /// @param _typehash The typehash used to construct the signed hash
    /// @param _i The starting index to verify
    /// @param _end The ending index to verify
    /// @return An array of hash keys if _i started as 0, because only
    /// the hash keys of offers are needed
    function _validateTradeSignatures(
        uint256[] memory _values,
        bytes32[] memory _hashes,
        address[] memory _addresses,
        bytes32 _typehash,
        uint256 _i,
        uint256 _end
    )
        private
        pure
    {
        for (_i; _i < _end; _i++) {
            uint256 dataA = _values[_i * 2 + 1];
            uint256 dataB = _values[_i * 2 + 2];

            bytes32 hashKey = keccak256(abi.encode(
                _typehash,
                _addresses[(dataA & mask8) * 2], // user
                _addresses[((dataA & mask16) >> 8) * 2 + 1], // offerAssetId
                dataB & mask128, // offerAmount
                _addresses[((dataA & mask24) >> 16) * 2 + 1], // wantAssetId
                dataB >> 128, // wantAmount
                _addresses[((dataA & mask32) >> 24) * 2 + 1], // feeAssetId
                dataA >> 128, // feeAmount
                (dataA & mask120) >> 56 // nonce
            ));

            bool prefixedSignature = ((dataA & mask56) >> 48) != 0;

            validateSignature(
                hashKey,
                _addresses[(dataA & mask8) * 2], // user
                uint8((dataA & mask48) >> 40), // The `v` component of the user's signature
                _hashes[_i * 2], // The `r` component of the user's signature
                _hashes[_i * 2 + 1], // The `s` component of the user's signature
                prefixedSignature
            );

            _hashes[_i * 2] = hashKey;
        }
    }

    /// @dev Ensure that the address is a deployed contract
    /// @param _contract The address to check
    function _validateContractAddress(address _contract) private view {
        assembly {
            if iszero(extcodesize(_contract)) { revert(0, 0) }
        }
    }

    /// @dev A thin wrapper around the native `call` function, to
    /// validate that the contract `call` must be successful.
    /// See https://solidity.readthedocs.io/en/v0.5.1/050-breaking-changes.html
    /// for details on constructing the `_payload`
    /// @param _contract Address of the contract to call
    /// @param _payload The data to call the contract with
    /// @return The data returned from the contract call
    function _callContract(
        address _contract,
        bytes memory _payload
    )
        private
        returns (bytes memory)
    {
        bool success;
        bytes memory returnData;

        (success, returnData) = _contract.call(_payload);
        require(success, "Contract call failed");

        return returnData;
    }

    /// @dev Fix for ERC-20 tokens that do not have proper return type
    /// See: https://github.com/ethereum/solidity/issues/4116
    /// https://medium.com/loopring-protocol/an-incompatibility-in-smart-contract-threatening-dapp-ecosystem-72b8ca5db4da
    /// https://github.com/sec-bit/badERC20Fix/blob/master/badERC20Fix.sol
    /// @param _data The data returned from a transfer call
    function _validateContractCallResult(bytes memory _data) private pure {
        require(
            _data.length == 0 ||
            (_data.length == 32 && _getUint256FromBytes(_data) != 0),
            "Invalid contract call result"
        );
    }

    /// @dev Converts data of type `bytes` into its corresponding `uint256` value
    /// @param _data The data in bytes
    /// @return The corresponding `uint256` value
    function _getUint256FromBytes(
        bytes memory _data
    )
        private
        pure
        returns (uint256)
    {
        uint256 parsed;
        assembly { parsed := mload(add(_data, 32)) }
        return parsed;
    }
}

// File: contracts/extensions/UniswapDappV2.sol

pragma solidity 0.5.12;




interface UniswapRouterV2 {
    function WETH() external pure returns (address);
    function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline)
        external
        payable
        returns (uint[] memory amounts);
    function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline)
        external
        returns (uint[] memory amounts);
    function swapExactTokensForTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external returns (uint[] memory amounts);
}


/// @title The DApp adapter contract for Uniswap
/// @author Switcheo Network
/// @notice This contract allows BrokerV2 offers to be filled by Uniswap
contract UniswapDappV2 is BrokerExtension {
    using SafeMath for uint256;

    UniswapRouterV2 public router;
    address private constant ETHER_ADDR = address(0);

    constructor(address _routerAddress) public {
        router = UniswapRouterV2(_routerAddress);
    }

    function setRouter(address _routerAddress) external onlyOwner nonReentrant {
        router = UniswapRouterV2(_routerAddress);
    }

    /// @notice See Utils._performNetworkTrade for method details
    function tokenReceiver(
        address[] memory /* _assetIds */,
        uint256[] memory /* _dataValues */,
        address[] memory /* _addresses */
    )
        public
        view
        returns(address)
    {
        return address(this);
    }

    /// @notice See Utils._performNetworkTrade for method details
    function trade(
        address[] memory _assetIds,
        uint256[] memory _dataValues,
        address[] memory /* _addresses */,
        address payable _recipient
    )
        public
        payable
        nonReentrant
    {
        // _dataValues[2] bits(24..56): delay
        uint256 deadline = now.add((_dataValues[2] & ~(~uint256(0) << 56)) >> 24);

        // give exact ETH and expect min tokens back
        if (_assetIds[0] == ETHER_ADDR) {
            address[] memory path = new address[](2);
            path[0] = router.WETH(); // token in
            path[1] = _assetIds[1]; // token out

            router.swapExactETHForTokens.value(_dataValues[0])(
                _dataValues[1],
                path,
                _recipient,
                deadline
            );
            return;
        }

        Utils.transferTokensIn(msg.sender, _assetIds[0], _dataValues[0], _dataValues[0]);
        Utils.approveTokenTransfer(
            _assetIds[0],
            address(router),
            _dataValues[0]
        );

        if (_assetIds[1] == ETHER_ADDR) {
            address[] memory path = new address[](2);
            path[0] = _assetIds[0]; // token in
            path[1] = router.WETH(); // token out

            router.swapExactTokensForETH(
                _dataValues[0],
                _dataValues[1],
                path,
                _recipient,
                deadline
            );
            return;
        }

        address[] memory path = new address[](2);
        path[0] = _assetIds[0]; // token in
        path[1] = _assetIds[1]; // token out

        router.swapExactTokensForTokens(
            _dataValues[0],
            _dataValues[1],
            path,
            _recipient,
            deadline
        );
    }
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"_routerAddress","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"constant":true,"inputs":[],"name":"broker","outputs":[{"internalType":"contract Broker","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_brokerAddress","type":"address"}],"name":"initializeBroker","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"router","outputs":[{"internalType":"contract UniswapRouterV2","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_routerAddress","type":"address"}],"name":"setRouter","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address[]","name":"","type":"address[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"address[]","name":"","type":"address[]"}],"name":"tokenReceiver","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address[]","name":"_assetIds","type":"address[]"},{"internalType":"uint256[]","name":"_dataValues","type":"uint256[]"},{"internalType":"address[]","name":"","type":"address[]"},{"internalType":"address payable","name":"_recipient","type":"address"}],"name":"trade","outputs":[],"payable":true,"stateMutability":"payable","type":"function"}]

608060405234801561001057600080fd5b506040516110d63803806110d68339818101604052602081101561003357600080fd5b50516001600055600280546001600160a01b039092166001600160a01b031990921691909117905561106c8061006a6000396000f3fe6080604052600436106100555760003560e01c806324c33e4f1461005a578063422f16da1461022857806367969383146103da578063abff01101461040d578063c0d7865514610422578063f887ea4014610455575b600080fd5b34801561006657600080fd5b5061020c6004803603606081101561007d57600080fd5b810190602081018135600160201b81111561009757600080fd5b8201836020820111156100a957600080fd5b803590602001918460208302840111600160201b831117156100ca57600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b81111561011957600080fd5b82018360208201111561012b57600080fd5b803590602001918460208302840111600160201b8311171561014c57600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b81111561019b57600080fd5b8201836020820111156101ad57600080fd5b803590602001918460208302840111600160201b831117156101ce57600080fd5b91908080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525092955061046a945050505050565b604080516001600160a01b039092168252519081900360200190f35b6103d86004803603608081101561023e57600080fd5b810190602081018135600160201b81111561025857600080fd5b82018360208201111561026a57600080fd5b803590602001918460208302840111600160201b8311171561028b57600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b8111156102da57600080fd5b8201836020820111156102ec57600080fd5b803590602001918460208302840111600160201b8311171561030d57600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b81111561035c57600080fd5b82018360208201111561036e57600080fd5b803590602001918460208302840111600160201b8311171561038f57600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550505090356001600160a01b031691506104729050565b005b3480156103e657600080fd5b506103d8600480360360208110156103fd57600080fd5b50356001600160a01b0316610e03565b34801561041957600080fd5b5061020c610ecc565b34801561042e57600080fd5b506103d86004803603602081101561044557600080fd5b50356001600160a01b0316610edb565b34801561046157600080fd5b5061020c610fc7565b309392505050565b6000805460010180825584519091906104b89060189066ffffffffffffff908890600290811061049e57fe5b602002602001015116901c42610fd690919063ffffffff16565b905060006001600160a01b0316866000815181106104d257fe5b60200260200101516001600160a01b031614156107815760408051600280825260608083018452926020830190803883395050600254604080516315ab88c960e31b815290519394506001600160a01b039091169263ad5c464892506004808301926020929190829003018186803b15801561054d57600080fd5b505afa158015610561573d6000803e3d6000fd5b505050506040513d602081101561057757600080fd5b50518151829060009061058657fe5b60200260200101906001600160a01b031690816001600160a01b031681525050866001815181106105b357fe5b6020026020010151816001815181106105c857fe5b6001600160a01b0392831660209182029290920101526002548751911690637ff36ab59088906000906105f757fe5b60200260200101518860018151811061060c57fe5b60200260200101518488876040518663ffffffff1660e01b81526004018085815260200180602001846001600160a01b03166001600160a01b03168152602001838152602001828103825285818151815260200191508051906020019060200280838360005b8381101561068a578181015183820152602001610672565b50505050905001955050505050506000604051808303818588803b1580156106b157600080fd5b505af11580156106c5573d6000803e3d6000fd5b50505050506040513d6000823e601f3d908101601f1916820160405260208110156106ef57600080fd5b8101908080516040519392919084600160201b82111561070e57600080fd5b90830190602082018581111561072357600080fd5b82518660208202830111600160201b8211171561073f57600080fd5b82525081516020918201928201910280838360005b8381101561076c578181015183820152602001610754565b50505050905001604052505050505050610da6565b73d6e266d0221a2e7909fb7f9fd45a84d217e909e763ac1ecbfc33886000815181106107a957fe5b6020026020010151886000815181106107be57fe5b6020026020010151896000815181106107d357fe5b60200260200101516040518563ffffffff1660e01b815260040180856001600160a01b03166001600160a01b03168152602001846001600160a01b03166001600160a01b0316815260200183815260200182815260200194505050505060006040518083038186803b15801561084857600080fd5b505af415801561085c573d6000803e3d6000fd5b5050505073d6e266d0221a2e7909fb7f9fd45a84d217e909e76352a23bbb8760008151811061088757fe5b6020026020010151600260009054906101000a90046001600160a01b0316886000815181106108b257fe5b60200260200101516040518463ffffffff1660e01b815260040180846001600160a01b03166001600160a01b03168152602001836001600160a01b03166001600160a01b03168152602001828152602001935050505060006040518083038186803b15801561092057600080fd5b505af4158015610934573d6000803e3d6000fd5b5050505060006001600160a01b03168660018151811061095057fe5b60200260200101516001600160a01b03161415610b665760408051600280825260608083018452926020830190803883390190505090508660008151811061099457fe5b6020026020010151816000815181106109a957fe5b6001600160a01b03928316602091820292909201810191909152600254604080516315ab88c960e31b81529051919093169263ad5c4648926004808301939192829003018186803b1580156109fd57600080fd5b505afa158015610a11573d6000803e3d6000fd5b505050506040513d6020811015610a2757600080fd5b5051815182906001908110610a3857fe5b6001600160a01b03928316602091820292909201015260025487519116906318cbafe5908890600090610a6757fe5b602002602001015188600181518110610a7c57fe5b60200260200101518488876040518663ffffffff1660e01b81526004018086815260200185815260200180602001846001600160a01b03166001600160a01b03168152602001838152602001828103825285818151815260200191508051906020019060200280838360005b83811015610b00578181015183820152602001610ae8565b505050509050019650505050505050600060405180830381600087803b158015610b2957600080fd5b505af1158015610b3d573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405260208110156106ef57600080fd5b604080516002808252606080830184529260208301908038833901905050905086600081518110610b9357fe5b602002602001015181600081518110610ba857fe5b60200260200101906001600160a01b031690816001600160a01b03168152505086600181518110610bd557fe5b602002602001015181600181518110610bea57fe5b6001600160a01b03928316602091820292909201015260025487519116906338ed1739908890600090610c1957fe5b602002602001015188600181518110610c2e57fe5b60200260200101518488876040518663ffffffff1660e01b81526004018086815260200185815260200180602001846001600160a01b03166001600160a01b03168152602001838152602001828103825285818151815260200191508051906020019060200280838360005b83811015610cb2578181015183820152602001610c9a565b505050509050019650505050505050600060405180830381600087803b158015610cdb57600080fd5b505af1158015610cef573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526020811015610d1857600080fd5b8101908080516040519392919084600160201b821115610d3757600080fd5b908301906020820185811115610d4c57600080fd5b82518660208202830111600160201b82111715610d6857600080fd5b82525081516020918201928201910280838360005b83811015610d95578181015183820152602001610d7d565b505050509050016040525050505050505b6000548114610dfc576040805162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b5050505050565b6001600160a01b038116610e57576040805162461bcd60e51b8152602060048201526016602482015275496e76616c6964205f62726f6b65724164647265737360501b604482015290519081900360640190fd5b6001546001600160a01b031615610eaa576040805162461bcd60e51b8152602060048201526012602482015271109c9bdad95c88185b1c9958591e481cd95d60721b604482015290519081900360640190fd5b600180546001600160a01b0319166001600160a01b0392909216919091179055565b6001546001600160a01b031681565b60015460408051638da5cb5b60e01b8152905133926001600160a01b031691638da5cb5b9160048083019260209291908290030181600087803b158015610f2157600080fd5b505af1158015610f35573d6000803e3d6000fd5b505050506040513d6020811015610f4b57600080fd5b50516001600160a01b031614610f9d576040805162461bcd60e51b815260206004820152601260248201527124b73b30b634b21036b9b39739b2b73232b960711b604482015290519081900360640190fd5b6000805460010190819055600280546001600160a01b0319166001600160a01b0384161790555050565b6002546001600160a01b031681565b600082820183811015611030576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b939250505056fea265627a7a723158206c72b72fd0e7faef38e669d24296f2fc052e24b5e9a60cc376c8009b6ef5e30364736f6c634300050c00320000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488d

Deployed Bytecode

0x6080604052600436106100555760003560e01c806324c33e4f1461005a578063422f16da1461022857806367969383146103da578063abff01101461040d578063c0d7865514610422578063f887ea4014610455575b600080fd5b34801561006657600080fd5b5061020c6004803603606081101561007d57600080fd5b810190602081018135600160201b81111561009757600080fd5b8201836020820111156100a957600080fd5b803590602001918460208302840111600160201b831117156100ca57600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b81111561011957600080fd5b82018360208201111561012b57600080fd5b803590602001918460208302840111600160201b8311171561014c57600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b81111561019b57600080fd5b8201836020820111156101ad57600080fd5b803590602001918460208302840111600160201b831117156101ce57600080fd5b91908080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525092955061046a945050505050565b604080516001600160a01b039092168252519081900360200190f35b6103d86004803603608081101561023e57600080fd5b810190602081018135600160201b81111561025857600080fd5b82018360208201111561026a57600080fd5b803590602001918460208302840111600160201b8311171561028b57600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b8111156102da57600080fd5b8201836020820111156102ec57600080fd5b803590602001918460208302840111600160201b8311171561030d57600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295949360208101935035915050600160201b81111561035c57600080fd5b82018360208201111561036e57600080fd5b803590602001918460208302840111600160201b8311171561038f57600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550505090356001600160a01b031691506104729050565b005b3480156103e657600080fd5b506103d8600480360360208110156103fd57600080fd5b50356001600160a01b0316610e03565b34801561041957600080fd5b5061020c610ecc565b34801561042e57600080fd5b506103d86004803603602081101561044557600080fd5b50356001600160a01b0316610edb565b34801561046157600080fd5b5061020c610fc7565b309392505050565b6000805460010180825584519091906104b89060189066ffffffffffffff908890600290811061049e57fe5b602002602001015116901c42610fd690919063ffffffff16565b905060006001600160a01b0316866000815181106104d257fe5b60200260200101516001600160a01b031614156107815760408051600280825260608083018452926020830190803883395050600254604080516315ab88c960e31b815290519394506001600160a01b039091169263ad5c464892506004808301926020929190829003018186803b15801561054d57600080fd5b505afa158015610561573d6000803e3d6000fd5b505050506040513d602081101561057757600080fd5b50518151829060009061058657fe5b60200260200101906001600160a01b031690816001600160a01b031681525050866001815181106105b357fe5b6020026020010151816001815181106105c857fe5b6001600160a01b0392831660209182029290920101526002548751911690637ff36ab59088906000906105f757fe5b60200260200101518860018151811061060c57fe5b60200260200101518488876040518663ffffffff1660e01b81526004018085815260200180602001846001600160a01b03166001600160a01b03168152602001838152602001828103825285818151815260200191508051906020019060200280838360005b8381101561068a578181015183820152602001610672565b50505050905001955050505050506000604051808303818588803b1580156106b157600080fd5b505af11580156106c5573d6000803e3d6000fd5b50505050506040513d6000823e601f3d908101601f1916820160405260208110156106ef57600080fd5b8101908080516040519392919084600160201b82111561070e57600080fd5b90830190602082018581111561072357600080fd5b82518660208202830111600160201b8211171561073f57600080fd5b82525081516020918201928201910280838360005b8381101561076c578181015183820152602001610754565b50505050905001604052505050505050610da6565b73d6e266d0221a2e7909fb7f9fd45a84d217e909e763ac1ecbfc33886000815181106107a957fe5b6020026020010151886000815181106107be57fe5b6020026020010151896000815181106107d357fe5b60200260200101516040518563ffffffff1660e01b815260040180856001600160a01b03166001600160a01b03168152602001846001600160a01b03166001600160a01b0316815260200183815260200182815260200194505050505060006040518083038186803b15801561084857600080fd5b505af415801561085c573d6000803e3d6000fd5b5050505073d6e266d0221a2e7909fb7f9fd45a84d217e909e76352a23bbb8760008151811061088757fe5b6020026020010151600260009054906101000a90046001600160a01b0316886000815181106108b257fe5b60200260200101516040518463ffffffff1660e01b815260040180846001600160a01b03166001600160a01b03168152602001836001600160a01b03166001600160a01b03168152602001828152602001935050505060006040518083038186803b15801561092057600080fd5b505af4158015610934573d6000803e3d6000fd5b5050505060006001600160a01b03168660018151811061095057fe5b60200260200101516001600160a01b03161415610b665760408051600280825260608083018452926020830190803883390190505090508660008151811061099457fe5b6020026020010151816000815181106109a957fe5b6001600160a01b03928316602091820292909201810191909152600254604080516315ab88c960e31b81529051919093169263ad5c4648926004808301939192829003018186803b1580156109fd57600080fd5b505afa158015610a11573d6000803e3d6000fd5b505050506040513d6020811015610a2757600080fd5b5051815182906001908110610a3857fe5b6001600160a01b03928316602091820292909201015260025487519116906318cbafe5908890600090610a6757fe5b602002602001015188600181518110610a7c57fe5b60200260200101518488876040518663ffffffff1660e01b81526004018086815260200185815260200180602001846001600160a01b03166001600160a01b03168152602001838152602001828103825285818151815260200191508051906020019060200280838360005b83811015610b00578181015183820152602001610ae8565b505050509050019650505050505050600060405180830381600087803b158015610b2957600080fd5b505af1158015610b3d573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405260208110156106ef57600080fd5b604080516002808252606080830184529260208301908038833901905050905086600081518110610b9357fe5b602002602001015181600081518110610ba857fe5b60200260200101906001600160a01b031690816001600160a01b03168152505086600181518110610bd557fe5b602002602001015181600181518110610bea57fe5b6001600160a01b03928316602091820292909201015260025487519116906338ed1739908890600090610c1957fe5b602002602001015188600181518110610c2e57fe5b60200260200101518488876040518663ffffffff1660e01b81526004018086815260200185815260200180602001846001600160a01b03166001600160a01b03168152602001838152602001828103825285818151815260200191508051906020019060200280838360005b83811015610cb2578181015183820152602001610c9a565b505050509050019650505050505050600060405180830381600087803b158015610cdb57600080fd5b505af1158015610cef573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526020811015610d1857600080fd5b8101908080516040519392919084600160201b821115610d3757600080fd5b908301906020820185811115610d4c57600080fd5b82518660208202830111600160201b82111715610d6857600080fd5b82525081516020918201928201910280838360005b83811015610d95578181015183820152602001610d7d565b505050509050016040525050505050505b6000548114610dfc576040805162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b5050505050565b6001600160a01b038116610e57576040805162461bcd60e51b8152602060048201526016602482015275496e76616c6964205f62726f6b65724164647265737360501b604482015290519081900360640190fd5b6001546001600160a01b031615610eaa576040805162461bcd60e51b8152602060048201526012602482015271109c9bdad95c88185b1c9958591e481cd95d60721b604482015290519081900360640190fd5b600180546001600160a01b0319166001600160a01b0392909216919091179055565b6001546001600160a01b031681565b60015460408051638da5cb5b60e01b8152905133926001600160a01b031691638da5cb5b9160048083019260209291908290030181600087803b158015610f2157600080fd5b505af1158015610f35573d6000803e3d6000fd5b505050506040513d6020811015610f4b57600080fd5b50516001600160a01b031614610f9d576040805162461bcd60e51b815260206004820152601260248201527124b73b30b634b21036b9b39739b2b73232b960711b604482015290519081900360640190fd5b6000805460010190819055600280546001600160a01b0319166001600160a01b0384161790555050565b6002546001600160a01b031681565b600082820183811015611030576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b939250505056fea265627a7a723158206c72b72fd0e7faef38e669d24296f2fc052e24b5e9a60cc376c8009b6ef5e30364736f6c634300050c0032

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

0000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488d

-----Decoded View---------------
Arg [0] : _routerAddress (address): 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D

-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 0000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488d


Deployed Bytecode Sourcemap

58205:2682:0:-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;58701:262;;8:9:-1;5:2;;;30:1;27;20:12;5:2;58701:262:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;58701:262:0;;;;;;;;-1:-1:-1;;;5:28;;2:2;;;46:1;43;36:12;2:2;58701:262:0;;35:9:-1;28:4;12:14;8:25;5:40;2:2;;;58:1;55;48:12;2:2;58701:262:0;;;;;;101:9:-1;95:2;81:12;77:21;67:8;63:36;60:51;-1:-1;;;25:12;22:29;11:108;8:2;;;132:1;129;122:12;8:2;58701:262:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;30:3:-1;22:6;14;1:33;99:1;81:16;;74:27;;;;-1:-1;58701:262:0;;;;;;;;-1:-1:-1;58701:262:0;;-1:-1:-1;;;;;5:28;;2:2;;;46:1;43;36:12;2:2;58701:262:0;;35:9:-1;28:4;12:14;8:25;5:40;2:2;;;58:1;55;48:12;2:2;58701:262:0;;;;;;101:9:-1;95:2;81:12;77:21;67:8;63:36;60:51;-1:-1;;;25:12;22:29;11:108;8:2;;;132:1;129;122:12;8:2;58701:262:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;30:3:-1;22:6;14;1:33;99:1;81:16;;74:27;;;;-1:-1;58701:262:0;;;;;;;;-1:-1:-1;58701:262:0;;-1:-1:-1;;;;;5:28;;2:2;;;46:1;43;36:12;2:2;58701:262:0;;35:9:-1;28:4;12:14;8:25;5:40;2:2;;;58:1;55;48:12;2:2;58701:262:0;;;;;;101:9:-1;95:2;81:12;77:21;67:8;63:36;60:51;-1:-1;;;25:12;22:29;11:108;8:2;;;132:1;129;122:12;8:2;58701:262:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;30:3:-1;22:6;14;1:33;99:1;81:16;;74:27;;;;-1:-1;58701:262:0;;-1:-1:-1;58701:262:0;;-1:-1:-1;;;;;58701:262:0:i;:::-;;;;-1:-1:-1;;;;;58701:262:0;;;;;;;;;;;;;;59038:1846;;;;;;13:3:-1;8;5:12;2:2;;;30:1;27;20:12;2:2;59038:1846:0;;;;;;;;-1:-1:-1;;;5:28;;2:2;;;46:1;43;36:12;2:2;59038:1846:0;;35:9:-1;28:4;12:14;8:25;5:40;2:2;;;58:1;55;48:12;2:2;59038:1846:0;;;;;;101:9:-1;95:2;81:12;77:21;67:8;63:36;60:51;-1:-1;;;25:12;22:29;11:108;8:2;;;132:1;129;122:12;8:2;59038:1846:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;30:3:-1;22:6;14;1:33;99:1;81:16;;74:27;;;;-1:-1;59038:1846:0;;;;;;;;-1:-1:-1;59038:1846:0;;-1:-1:-1;;;;;5:28;;2:2;;;46:1;43;36:12;2:2;59038:1846:0;;35:9:-1;28:4;12:14;8:25;5:40;2:2;;;58:1;55;48:12;2:2;59038:1846:0;;;;;;101:9:-1;95:2;81:12;77:21;67:8;63:36;60:51;-1:-1;;;25:12;22:29;11:108;8:2;;;132:1;129;122:12;8:2;59038:1846:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;30:3:-1;22:6;14;1:33;99:1;81:16;;74:27;;;;-1:-1;59038:1846:0;;;;;;;;-1:-1:-1;59038:1846:0;;-1:-1:-1;;;;;5:28;;2:2;;;46:1;43;36:12;2:2;59038:1846:0;;35:9:-1;28:4;12:14;8:25;5:40;2:2;;;58:1;55;48:12;2:2;59038:1846:0;;;;;;101:9:-1;95:2;81:12;77:21;67:8;63:36;60:51;-1:-1;;;25:12;22:29;11:108;8:2;;;132:1;129;122:12;8:2;59038:1846:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;30:3:-1;22:6;14;1:33;99:1;81:16;;74:27;;;;-1:-1;59038:1846:0;;-1:-1:-1;;;59038:1846:0;;-1:-1:-1;;;;;59038:1846:0;;-1:-1:-1;59038:1846:0;;-1:-1:-1;59038:1846:0:i;:::-;;5826:254;;8:9:-1;5:2;;;30:1;27;20:12;5:2;5826:254:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;5826:254:0;-1:-1:-1;;;;;5826:254:0;;:::i;5561:20::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;5561:20:0;;;:::i;58492:134::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;58492:134:0;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;58492:134:0;-1:-1:-1;;;;;58492:134:0;;:::i;58289:29::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;58289:29:0;;;:::i;58701:262::-;58950:4;58701:262;;;;;:::o;59038:1846::-;5066:13;:18;;5083:1;5066:18;;;;59363:14;;5066:18;;:13;59354:54;;59405:2;;59380:20;;59363:11;;59375:1;;59363:14;;;;;;;;;;;;:37;59362:45;;59354:3;:7;;:54;;;;:::i;:::-;59335:73;;58371:1;-1:-1:-1;;;;;59479:26:0;:9;59489:1;59479:12;;;;;;;;;;;;;;-1:-1:-1;;;;;59479:26:0;;59475:414;;;59546:16;;;59560:1;59546:16;;;59522:21;59546:16;;;;;59522:21;59546:16;;;;;105:10:-1;59546:16:0;88:34:-1;-1:-1;;59587:6:0;;:13;;;-1:-1:-1;;;59587:13:0;;;;59522:40;;-1:-1:-1;;;;;;59587:6:0;;;;:11;;-1:-1:-1;59587:13:0;;;;;;;;;;;;;;:6;:13;;;5:2:-1;;;;30:1;27;20:12;5:2;59587:13:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;59587:13:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;59587:13:0;59577:7;;:4;;59582:1;;59577:7;;;;;;;;;:23;-1:-1:-1;;;;;59577:23:0;;;-1:-1:-1;;;;;59577:23:0;;;;;59637:9;59647:1;59637:12;;;;;;;;;;;;;;59627:4;59632:1;59627:7;;;;;;;;-1:-1:-1;;;;;59627:22:0;;;:7;;;;;;;;;:22;59679:6;;59714:14;;59679:6;;;:28;;59714:11;;59679:6;;59714:14;;;;;;;;;;59748:11;59760:1;59748:14;;;;;;;;;;;;;;59781:4;59804:10;59833:8;59679:177;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;59679:177:0;-1:-1:-1;;;;;59679:177:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;23:1:-1;8:100;33:3;30:1;27:10;8:100;;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;;12:14;59679:177:0;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;59679:177:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;59679:177:0;;;;;;;39:16:-1;36:1;17:17;2:54;101:4;59679:177:0;80:15:-1;;;-1:-1;;76:31;65:43;;120:4;113:20;13:2;5:11;;2:2;;;29:1;26;19:12;2:2;59679:177:0;;;;;;;;;;;;;-1:-1:-1;;;14:3;11:20;8:2;;;44:1;41;34:12;8:2;62:21;;;;123:4;114:14;;138:31;;;135:2;;;182:1;179;172:12;135:2;219:3;213:10;331:9;325:2;311:12;307:21;289:16;285:44;282:59;-1:-1;;;247:12;244:29;233:116;230:2;;;362:1;359;352:12;230:2;373:25;;-1:-1;59679:177:0;;421:4:-1;412:14;;;;59679:177:0;;;;;412:14:-1;59679:177:0;23:1:-1;8:100;33:3;30:1;27:10;8:100;;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;;12:14;59679:177:0;;;;;;;;;;;;59871:7;;;;59475:414;59901:5;:22;59924:10;59936:9;59946:1;59936:12;;;;;;;;;;;;;;59950:11;59962:1;59950:14;;;;;;;;;;;;;;59966:11;59978:1;59966:14;;;;;;;;;;;;;;59901:80;;;;;;;;;;;;;-1:-1:-1;;;;;59901:80:0;-1:-1:-1;;;;;59901:80:0;;;;;;-1:-1:-1;;;;;59901:80:0;-1:-1:-1;;;;;59901:80:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;59901:80:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;59901:80:0;;;;59992:5;:26;60033:9;60043:1;60033:12;;;;;;;;;;;;;;60068:6;;;;;;;;;-1:-1:-1;;;;;60068:6:0;60090:11;60102:1;60090:14;;;;;;;;;;;;;;59992:123;;;;;;;;;;;;;-1:-1:-1;;;;;59992:123:0;-1:-1:-1;;;;;59992:123:0;;;;;;-1:-1:-1;;;;;59992:123:0;-1:-1:-1;;;;;59992:123:0;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;59992:123:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;59992:123:0;;;;58371:1;-1:-1:-1;;;;;60132:26:0;:9;60142:1;60132:12;;;;;;;;;;;;;;-1:-1:-1;;;;;60132:26:0;;60128:425;;;60199:16;;;60213:1;60199:16;;;60175:21;60199:16;;;;;60175:21;60199:16;;;;;105:10:-1;60199:16:0;88:34:-1;136:17;;-1:-1;60199:16:0;60175:40;;60240:9;60250:1;60240:12;;;;;;;;;;;;;;60230:4;60235:1;60230:7;;;;;;;;-1:-1:-1;;;;;60230:22:0;;;:7;;;;;;;;;;:22;;;;60289:6;;:13;;;-1:-1:-1;;;60289:13:0;;;;:6;;;;;:11;;:13;;;;;60230:7;;60289:13;;;;;:6;:13;;;5:2:-1;;;;30:1;27;20:12;5:2;60289:13:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;60289:13:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;60289:13:0;60279:7;;:4;;60284:1;;60279:7;;;;;;-1:-1:-1;;;;;60279:23:0;;;:7;;;;;;;;;:23;60332:6;;60379:14;;60332:6;;;:28;;60379:11;;60332:6;;60379:14;;;;;;;;;;60412:11;60424:1;60412:14;;;;;;;;;;;;;;60445:4;60468:10;60497:8;60332:188;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;60332:188:0;-1:-1:-1;;;;;60332:188:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;23:1:-1;8:100;33:3;30:1;27:10;8:100;;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;;12:14;60332:188:0;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;60332:188:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;60332:188:0;;;;;;39:16:-1;36:1;17:17;2:54;101:4;60332:188:0;80:15:-1;;;-1:-1;;76:31;65:43;;120:4;113:20;13:2;5:11;;2:2;;;29:1;26;19:12;60128:425:0;60589:16;;;60603:1;60589:16;;;60565:21;60589:16;;;;;60565:21;60589:16;;;;;105:10:-1;60589:16:0;88:34:-1;136:17;;-1:-1;60589:16:0;60565:40;;60626:9;60636:1;60626:12;;;;;;;;;;;;;;60616:4;60621:1;60616:7;;;;;;;;;;;;;:22;-1:-1:-1;;;;;60616:22:0;;;-1:-1:-1;;;;;60616:22:0;;;;;60671:9;60681:1;60671:12;;;;;;;;;;;;;;60661:4;60666:1;60661:7;;;;;;;;-1:-1:-1;;;;;60661:22:0;;;:7;;;;;;;;;:22;60709:6;;60755:14;;60709:6;;;:31;;60755:11;;60709:6;;60755:14;;;;;;;;;;60784:11;60796:1;60784:14;;;;;;;;;;;;;;60813:4;60832:10;60857:8;60709:167;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;60709:167:0;-1:-1:-1;;;;;60709:167:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;23:1:-1;8:100;33:3;30:1;27:10;8:100;;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;;12:14;60709:167:0;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;60709:167:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;60709:167:0;;;;;;39:16:-1;36:1;17:17;2:54;101:4;60709:167:0;80:15:-1;;;-1:-1;;76:31;65:43;;120:4;113:20;13:2;5:11;;2:2;;;29:1;26;19:12;2:2;60709:167:0;;;;;;;;;;;;;-1:-1:-1;;;14:3;11:20;8:2;;;44:1;41;34:12;8:2;62:21;;;;123:4;114:14;;138:31;;;135:2;;;182:1;179;172:12;135:2;219:3;213:10;331:9;325:2;311:12;307:21;289:16;285:44;282:59;-1:-1;;;247:12;244:29;233:116;230:2;;;362:1;359;352:12;230:2;373:25;;-1:-1;60709:167:0;;421:4:-1;412:14;;;;60709:167:0;;;;;412:14:-1;60709:167:0;23:1:-1;8:100;33:3;30:1;27:10;8:100;;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;;12:14;60709:167:0;;;;;;;;;;;;5142:1;;;5178:13;;5162:12;:29;5154:73;;;;;-1:-1:-1;;;5154:73:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;59038:1846;;;;;:::o;5826:254::-;-1:-1:-1;;;;;5904:28:0;;5896:63;;;;;-1:-1:-1;;;5896:63:0;;;;;;;;;;;;-1:-1:-1;;;5896:63:0;;;;;;;;;;;;;;;5986:6;;-1:-1:-1;;;;;5986:6:0;5978:29;5970:60;;;;;-1:-1:-1;;;5970:60:0;;;;;;;;;;;;-1:-1:-1;;;5970:60:0;;;;;;;;;;;;;;;6041:6;:31;;-1:-1:-1;;;;;;6041:31:0;-1:-1:-1;;;;;6041:31:0;;;;;;;;;;5826:254::o;5561:20::-;;;-1:-1:-1;;;;;5561:20:0;;:::o;58492:134::-;5747:6;;:14;;;-1:-1:-1;;;5747:14:0;;;;5765:10;;-1:-1:-1;;;;;5747:6:0;;:12;;:14;;;;;;;;;;;;;;:6;;:14;;;5:2:-1;;;;30:1;27;20:12;5:2;5747:14:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;5747:14:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;5747:14:0;-1:-1:-1;;;;;5747:28:0;;5739:59;;;;;-1:-1:-1;;;5739:59:0;;;;;;;;;;;;-1:-1:-1;;;5739:59:0;;;;;;;;;;;;;;;5066:13;:18;;5083:1;5066:18;;;;;58578:6;:40;;-1:-1:-1;;;;;;58578:40:0;-1:-1:-1;;;;;58578:40:0;;;;;5809:1;58492:134;:::o;58289:29::-;;;-1:-1:-1;;;;;58289:29:0;;:::o;903:181::-;961:7;993:5;;;1017:6;;;;1009:46;;;;;-1:-1:-1;;;1009:46:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;1075:1;903:181;-1:-1:-1;;;903:181:0:o

Swarm Source

bzzr://6c72b72fd0e7faef38e669d24296f2fc052e24b5e9a60cc376c8009b6ef5e303

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.