ETH Price: $3,821.02 (+1.61%)
Gas: 4 Gwei

Contract

0x4a14347083B80E5216cA31350a2D21702aC3650d
 

Overview

ETH Balance

0.007116781569388584 ETH

Eth Value

$27.19 (@ $3,821.02/ETH)

Token Holdings

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Value
0x60086101127924172021-07-09 9:36:221058 days ago1625823382IN
 Create: AMMWrapperWithPath
0 ETH0.0664920615

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Block From To Value
199927392024-05-31 22:17:2326 hrs ago1717193843
Tokenlon: AMM Wrapper With Path
0.148 ETH
199927392024-05-31 22:17:2326 hrs ago1717193843
Tokenlon: AMM Wrapper With Path
0.148 ETH
199927012024-05-31 22:09:4726 hrs ago1717193387
Tokenlon: AMM Wrapper With Path
0.03 ETH
199927012024-05-31 22:09:4726 hrs ago1717193387
Tokenlon: AMM Wrapper With Path
0.03 ETH
199926972024-05-31 22:08:5926 hrs ago1717193339
Tokenlon: AMM Wrapper With Path
0.01489382 ETH
199926972024-05-31 22:08:5926 hrs ago1717193339
Tokenlon: AMM Wrapper With Path
0.01489382 ETH
199926212024-05-31 21:53:2326 hrs ago1717192403
Tokenlon: AMM Wrapper With Path
0.0759 ETH
199926212024-05-31 21:53:2326 hrs ago1717192403
Tokenlon: AMM Wrapper With Path
0.0759 ETH
199925762024-05-31 21:44:2326 hrs ago1717191863
Tokenlon: AMM Wrapper With Path
0.63 ETH
199925762024-05-31 21:44:2326 hrs ago1717191863
Tokenlon: AMM Wrapper With Path
0.63 ETH
199924872024-05-31 21:26:2327 hrs ago1717190783
Tokenlon: AMM Wrapper With Path
0.435 ETH
199924872024-05-31 21:26:2327 hrs ago1717190783
Tokenlon: AMM Wrapper With Path
0.435 ETH
199924512024-05-31 21:19:1127 hrs ago1717190351
Tokenlon: AMM Wrapper With Path
0.13362566 ETH
199924512024-05-31 21:19:1127 hrs ago1717190351
Tokenlon: AMM Wrapper With Path
0.13362566 ETH
199924472024-05-31 21:18:2327 hrs ago1717190303
Tokenlon: AMM Wrapper With Path
0.2073915 ETH
199924472024-05-31 21:18:2327 hrs ago1717190303
Tokenlon: AMM Wrapper With Path
0.2073915 ETH
199924032024-05-31 21:09:2327 hrs ago1717189763
Tokenlon: AMM Wrapper With Path
0.7 ETH
199924032024-05-31 21:09:2327 hrs ago1717189763
Tokenlon: AMM Wrapper With Path
0.7 ETH
199923952024-05-31 21:07:4727 hrs ago1717189667
Tokenlon: AMM Wrapper With Path
4.5 ETH
199923952024-05-31 21:07:4727 hrs ago1717189667
Tokenlon: AMM Wrapper With Path
4.5 ETH
199923892024-05-31 21:06:3527 hrs ago1717189595
Tokenlon: AMM Wrapper With Path
1.28 ETH
199923892024-05-31 21:06:3527 hrs ago1717189595
Tokenlon: AMM Wrapper With Path
1.28 ETH
199923682024-05-31 21:02:2327 hrs ago1717189343
Tokenlon: AMM Wrapper With Path
0.27 ETH
199923682024-05-31 21:02:2327 hrs ago1717189343
Tokenlon: AMM Wrapper With Path
0.27 ETH
199923462024-05-31 20:57:5927 hrs ago1717189079
Tokenlon: AMM Wrapper With Path
0.39179847 ETH
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
AMMWrapperWithPath

Compiler Version
v0.6.12+commit.27d51765

Optimization Enabled:
Yes with 1000 runs

Other Settings:
default evmVersion
File 1 of 57 : AMMWrapperWithPath.sol
pragma solidity ^0.6.0;
pragma experimental ABIEncoderV2;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import "@openzeppelin/contracts/math/SafeMath.sol";
import "./AMMWrapper.sol";
import "./interfaces/ISpender.sol";
import "./interfaces/IUniswapRouterV2.sol";
import "./interfaces/IUniswapV3SwapRouter.sol";
import "./interfaces/IPermanentStorage.sol";
import "./utils/UniswapV3PathLib.sol";

contract AMMWrapperWithPath is AMMWrapper {
    using SafeMath for uint256;
    using SafeERC20 for IERC20;
    using Path for bytes;

    // Constants do not have storage slot.
    address public constant UNISWAP_V3_ROUTER_ADDRESS = 0xE592427A0AEce92De3Edee1F18E0157C05861564;

    event Swapped(
        TxMetaData,
        Order order
    );

    /************************************************************
    *              Constructor and init functions               *
    *************************************************************/
    constructor (
        address _operator,
        uint256 _subsidyFactor,
        address _userProxy,
        ISpender _spender,
        IPermanentStorage _permStorage,
        IWETH _weth
    ) public AMMWrapper(_operator, _subsidyFactor, _userProxy, _spender, _permStorage, _weth) {}

    /************************************************************
    *                   External functions                      *
    *************************************************************/
    function trade(
        Order memory _order,
        uint256 _feeFactor,
        bytes calldata _sig,
        bytes calldata _makerSpecificData,
        address[] calldata _path
    )
        payable
        external
        nonReentrant
        onlyUserProxy
        returns (uint256) 
    {
        require(_order.deadline >= block.timestamp, "AMMWrapper: expired order");
        TxMetaData memory txMetaData;
        InternalTxData memory internalTxData;

        // These variables are copied straight from function parameters and
        // used to bypass stack too deep error.
        txMetaData.subsidyFactor = uint16(subsidyFactor);
        txMetaData.feeFactor = uint16(_feeFactor);
        internalTxData.makerSpecificData = _makerSpecificData;
        internalTxData.path = _path;
        if (! permStorage.isRelayerValid(tx.origin)) {
            txMetaData.feeFactor = (txMetaData.subsidyFactor > txMetaData.feeFactor) ? txMetaData.subsidyFactor : txMetaData.feeFactor;
            txMetaData.subsidyFactor = 0;
        }

        // Assign trade vairables
        internalTxData.fromEth = (_order.takerAssetAddr == ZERO_ADDRESS || _order.takerAssetAddr == ETH_ADDRESS);
        internalTxData.toEth = (_order.makerAssetAddr == ZERO_ADDRESS || _order.makerAssetAddr == ETH_ADDRESS);
        if(_isCurve(_order.makerAddr)) {
            // PermanetStorage can recognize `ETH_ADDRESS` but not `ZERO_ADDRESS`.
            // Convert it to `ETH_ADDRESS` as passed in `_order.takerAssetAddr` or `_order.makerAssetAddr` might be `ZERO_ADDRESS`.
            internalTxData.takerAssetInternalAddr = internalTxData.fromEth ? ETH_ADDRESS : _order.takerAssetAddr;
            internalTxData.makerAssetInternalAddr = internalTxData.toEth ? ETH_ADDRESS : _order.makerAssetAddr;
        } else {
            internalTxData.takerAssetInternalAddr = internalTxData.fromEth ? address(weth) : _order.takerAssetAddr;
            internalTxData.makerAssetInternalAddr = internalTxData.toEth ? address(weth) : _order.makerAssetAddr;
        }

        txMetaData.transactionHash = _verify(
            _order,
            _sig
        );

        _prepare(_order, internalTxData);

        (txMetaData.source, txMetaData.receivedAmount) = _swapWithPath(
            _order,
            txMetaData,
            internalTxData
        );

        // Settle
        txMetaData.settleAmount = _settle(
            _order,
            txMetaData,
            internalTxData
        );

        emit Swapped(
            txMetaData,
            _order
        );

        return txMetaData.settleAmount;
    }

    /**
     * @dev internal function of `trade`.
     * Used to tell if maker is Curve.
     */
    function _isCurve(address _makerAddr) override internal pure returns (bool) {
        if (
            _makerAddr == UNISWAP_V2_ROUTER_02_ADDRESS ||
            _makerAddr == UNISWAP_V3_ROUTER_ADDRESS ||
            _makerAddr == SUSHISWAP_ROUTER_ADDRESS
        ) return false;
        else return true;
    }

    /**
     * @dev internal function of `trade`.
     * It executes the swap on chosen AMM.
     */
    function _swapWithPath(
        Order memory _order,
        TxMetaData memory _txMetaData,
        InternalTxData memory _internalTxData
    )
        internal
        approveTakerAsset(_internalTxData.takerAssetInternalAddr, _order.makerAddr)
        returns (string memory source, uint256 receivedAmount)
    {
        // Swap
        // minAmount = makerAssetAmount * (10000 - subsidyFactor) / 10000
        uint256 minAmount = _order.makerAssetAmount.mul((BPS_MAX.sub(_txMetaData.subsidyFactor))).div(BPS_MAX);

        if (_order.makerAddr == UNISWAP_V2_ROUTER_02_ADDRESS ||
            _order.makerAddr == SUSHISWAP_ROUTER_ADDRESS) {
            source = (_order.makerAddr == SUSHISWAP_ROUTER_ADDRESS) ? "SushiSwap" : "Uniswap V2";
            // Sushiswap shares the same interface as Uniswap's
            receivedAmount = _tradeUniswapV2TokenToToken(
                _order.makerAddr,
                _internalTxData.takerAssetInternalAddr,
                _internalTxData.makerAssetInternalAddr,
                _order.takerAssetAmount,
                minAmount,
                _order.deadline,
                _internalTxData.path
            );
        } else if (_order.makerAddr == UNISWAP_V3_ROUTER_ADDRESS) {
            source = "Uniswap V3";
            receivedAmount = _tradeUniswapV3TokenToToken(
                _order.makerAddr,
                _internalTxData.takerAssetInternalAddr,
                _internalTxData.makerAssetInternalAddr,
                _order.deadline,
                _order.takerAssetAmount,
                minAmount,
                _internalTxData.makerSpecificData
            );
        } else {
            CurveData memory curveData;
            (
                curveData.fromTokenCurveIndex,
                curveData.toTokenCurveIndex,
                curveData.swapMethod,
            ) = permStorage.getCurvePoolInfo(
                _order.makerAddr,
                _internalTxData.takerAssetInternalAddr,
                _internalTxData.makerAssetInternalAddr
            );
            require(curveData.swapMethod != 0,"AMMWrapper: swap method not registered");
            if (curveData.fromTokenCurveIndex > 0 && curveData.toTokenCurveIndex > 0) {
                source = "Curve";
                // Substract index by 1 because indices stored in `permStorage` starts from 1
                curveData.fromTokenCurveIndex = curveData.fromTokenCurveIndex - 1;
                curveData.toTokenCurveIndex = curveData.toTokenCurveIndex - 1;
                // Curve does not return amount swapped so we need to record balance change instead.
                uint256 balanceBeforeTrade = _getSelfBalance(_internalTxData.makerAssetInternalAddr);
                _tradeCurveTokenToToken(
                    _order.makerAddr,
                    curveData.fromTokenCurveIndex,
                    curveData.toTokenCurveIndex,
                    _order.takerAssetAmount,
                    minAmount,
                    curveData.swapMethod
                );
                uint256 balanceAfterTrade = _getSelfBalance(_internalTxData.makerAssetInternalAddr);
                receivedAmount = balanceAfterTrade.sub(balanceBeforeTrade);
            } else {
                revert("AMMWrapper: unsupported makerAddr");
            }
        }
    }

    function _tradeUniswapV2TokenToToken(
        address _makerAddr,
        address _takerAssetAddr,
        address _makerAssetAddr,
        uint256 _takerAssetAmount,
        uint256 _makerAssetAmount,
        uint256 _deadline,
        address[] memory _path
    )
        internal 
        returns (uint256) 
    {
        IUniswapRouterV2 router = IUniswapRouterV2(_makerAddr);
        if (_path.length == 0) {
            _path = new address[](2);
            _path[0] = _takerAssetAddr;
            _path[1] = _makerAssetAddr;
        } else {
            require(_path.length >= 2, "AMMWrapper: path length must be at least two");
            require(_path[0] == _takerAssetAddr, "AMMWrapper: first element of path must match taker asset");
            require(_path[_path.length - 1] == _makerAssetAddr, "AMMWrapper: last element of path must match maker asset");
        }
        uint256[] memory amounts = router.swapExactTokensForTokens(
            _takerAssetAmount,
            _makerAssetAmount,
            _path,
            address(this),
            _deadline
        );
        return amounts[amounts.length - 1];
    }

    function _validateUniswapV3Path(
        bytes memory _path,
        address _takerAssetAddr,
        address _makerAssetAddr
    ) internal {
        (address tokenA, address tokenB, ) = _path.decodeFirstPool();

        if (_path.hasMultiplePools()) {
            _path = _path.skipToken();
            while (_path.hasMultiplePools()) {
                _path = _path.skipToken();
            }
            (, tokenB, ) = _path.decodeFirstPool();
        }

        require(tokenA == _takerAssetAddr, "AMMWrapper: first element of path must match taker asset");
        require(tokenB == _makerAssetAddr, "AMMWrapper: last element of path must match maker asset");
    }

    function _tradeUniswapV3TokenToToken(
        address _makerAddr,
        address _takerAssetAddr,
        address _makerAssetAddr,
        uint256 _deadline,
        uint256 _takerAssetAmount,
        uint256 _makerAssetAmount,
        bytes memory _makerSpecificData
    )
        internal 
        returns (uint256 amountOut) 
    {
        ISwapRouter router = ISwapRouter(_makerAddr);
        // swapType:
        // 1: exactInputSingle, 2: exactInput
        uint8 swapType = uint8(uint256(_makerSpecificData.readBytes32(0)));

        if (swapType == 1) {
            (, uint24 poolFee) = abi.decode(_makerSpecificData, (uint8, uint24));
            ISwapRouter.ExactInputSingleParams memory exactInputSingleParams;
            exactInputSingleParams.tokenIn = _takerAssetAddr;
            exactInputSingleParams.tokenOut = _makerAssetAddr;
            exactInputSingleParams.fee = poolFee;
            exactInputSingleParams.recipient = address(this);
            exactInputSingleParams.deadline = _deadline;
            exactInputSingleParams.amountIn = _takerAssetAmount;
            exactInputSingleParams.amountOutMinimum = _makerAssetAmount;
            exactInputSingleParams.sqrtPriceLimitX96 = 0;

            amountOut = router.exactInputSingle(exactInputSingleParams);
        } else if (swapType == 2) {
            (, bytes memory path) = abi.decode(_makerSpecificData, (uint8, bytes));
            _validateUniswapV3Path(path, _takerAssetAddr, _makerAssetAddr);
            ISwapRouter.ExactInputParams memory exactInputParams;
            exactInputParams.path = path;
            exactInputParams.recipient = address(this);
            exactInputParams.deadline = _deadline;
            exactInputParams.amountIn = _takerAssetAmount;
            exactInputParams.amountOutMinimum = _makerAssetAmount;

            amountOut = router.exactInput(exactInputParams);
        } else {
            revert("AMMWrapper: unsupported UniswapV3 swap type");
        }
    }
}

File 2 of 57 : AllowanceTarget.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.6.5;

import "@openzeppelin/contracts/utils/Address.sol";
import "./interfaces/IAllowanceTarget.sol";

/**
 * @dev AllowanceTarget contract
 */
contract AllowanceTarget is IAllowanceTarget {
    using Address for address;

    uint256 constant private TIME_LOCK_DURATION = 1 days;

    address public spender;
    address public newSpender;
    uint256 public timelockExpirationTime;

    modifier onlySpender() {
        require(spender == msg.sender, "AllowanceTarget: not the spender");
        _;
    }


    constructor(address _spender) public {
        require(_spender != address(0), "AllowanceTarget: _spender should not be 0");

        // Set spender
        spender = _spender;
    }


    function setSpenderWithTimelock(address _newSpender) override external onlySpender {
        require(_newSpender.isContract(), "AllowanceTarget: new spender not a contract");
        require(newSpender == address(0) && timelockExpirationTime == 0, "AllowanceTarget: SetSpender in progress");

        timelockExpirationTime = now + TIME_LOCK_DURATION;
        newSpender = _newSpender;
    }

    function completeSetSpender() override external {
        require(timelockExpirationTime != 0, "AllowanceTarget: no pending SetSpender");
        require(now >= timelockExpirationTime, "AllowanceTarget: time lock not expired yet");

        // Set new spender
        spender = newSpender;
        // Reset
        timelockExpirationTime = 0;
        newSpender = address(0);
    }


    function teardown() override external onlySpender {
        selfdestruct(payable(spender));
    }


    /// @dev Execute an arbitrary call. Only an authority can call this.
    /// @param target The call target.
    /// @param callData The call data.
    /// @return resultData The data returned by the call.
    function executeCall(
        address payable target,
        bytes calldata callData
    )
        override
        external
        onlySpender
        returns (bytes memory resultData)
    {
        bool success;
        (success, resultData) = target.call(callData);
        if (!success) {
            // Get the error message returned
            assembly {
                let ptr := mload(0x40)
                let size := returndatasize()
                returndatacopy(ptr, 0, size)
                revert(ptr, size)
            }
        }
    }
}

File 3 of 57 : Address.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.2 <0.8.0;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize, which returns 0 for contracts in
        // construction, since the code is only stored at the end of the
        // constructor execution.

        uint256 size;
        // solhint-disable-next-line no-inline-assembly
        assembly { size := extcodesize(account) }
        return size > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
        (bool success, ) = recipient.call{ value: amount }("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain`call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
      return functionCall(target, data, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        require(isContract(target), "Address: call to non-contract");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.call{ value: value }(data);
        return _verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.staticcall(data);
        return _verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
        require(isContract(target), "Address: delegate call to non-contract");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return _verifyCallResult(success, returndata, errorMessage);
    }

    function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly

                // solhint-disable-next-line no-inline-assembly
                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

File 4 of 57 : IAllowanceTarget.sol
pragma solidity ^0.6.0;

interface IAllowanceTarget {
    function setSpenderWithTimelock(address _newSpender) external;
    function completeSetSpender() external;
    function executeCall(address payable _target, bytes calldata _callData) external returns (bytes memory resultData);
    function teardown() external;
}

File 5 of 57 : Spender.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.6.5;

import "@openzeppelin/contracts/math/SafeMath.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./interfaces/IAllowanceTarget.sol";

/**
 * @dev Spender contract
 */
contract Spender {
    using SafeMath for uint256;

    // Constants do not have storage slot.
    address private constant ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
    address private constant ZERO_ADDRESS = address(0);
    uint256 constant private TIME_LOCK_DURATION = 1 days;

    // Below are the variables which consume storage slots.
    address public operator;
    address public pendingOperator;
    address public allowanceTarget;
    mapping(address => bool) private authorized;
    mapping(address => bool) private tokenBlacklist;
    uint256 public numPendingAuthorized;
    mapping(uint256 => address) public pendingAuthorized;
    uint256 public timelockExpirationTime;
    uint256 public contractDeployedTime;
    bool public timelockActivated;
    mapping(address => bool) public consumeGasERC20Tokens;

    // System events
    event TimeLockActivated(uint256 activatedTimeStamp);
    // Operator events
    event TransferOwnership(address newOperator);
    event SetAllowanceTarget(address allowanceTarget);
    event SetNewSpender(address newSpender);
    event SetConsumeGasERC20Token(address token);
    event TearDownAllowanceTarget(uint256 tearDownTimeStamp);
    event BlackListToken(address token, bool isBlacklisted);
    event AuthorizeSpender(address spender, bool isAuthorized);


    /************************************************************
    *          Access control and ownership management          *
    *************************************************************/
    modifier onlyOperator() {
        require(operator == msg.sender, "Spender: not the operator");
        _;
    }

    modifier onlyAuthorized() {
        require(authorized[msg.sender], "Spender: not authorized");
        _;
    }

    function setNewOperator(address _newOperator) external onlyOperator {
        require(_newOperator != address(0), "Spender: operator can not be zero address");
        pendingOperator = _newOperator;
    }

    function acceptAsOperator() external {
        require(pendingOperator == msg.sender, "Spender: only nominated one can accept as new operator");
        operator = pendingOperator;
        pendingOperator = address(0);
        emit TransferOwnership(pendingOperator);
    }


    /************************************************************
    *                    Timelock management                    *
    *************************************************************/
    /// @dev Everyone can activate timelock after the contract has been deployed for more than 1 day.
    function activateTimelock() external {
        bool canActivate = block.timestamp.sub(contractDeployedTime) > 1 days;
        require(canActivate && ! timelockActivated, "Spender: can not activate timelock yet or has been activated");
        timelockActivated = true;

        emit TimeLockActivated(block.timestamp);
    }


    /************************************************************
    *              Constructor and init functions               *
    *************************************************************/
    constructor(address _operator, address[] memory _consumeGasERC20Tokens) public {
        require(_operator != address(0), "Spender: _operator should not be 0");

        // Set operator
        operator = _operator;
        timelockActivated = false;
        contractDeployedTime = block.timestamp;

        for (uint256 i = 0; i < _consumeGasERC20Tokens.length; i++) {
            consumeGasERC20Tokens[_consumeGasERC20Tokens[i]] = true;
        }
    }

    function setAllowanceTarget(address _allowanceTarget) external onlyOperator {
        require(allowanceTarget == address(0), "Spender: can not reset allowance target");

        // Set allowanceTarget
        allowanceTarget = _allowanceTarget;

        emit SetAllowanceTarget(_allowanceTarget);
    }



    /************************************************************
    *          AllowanceTarget interaction functions            *
    *************************************************************/
    function setNewSpender(address _newSpender) external onlyOperator {
        IAllowanceTarget(allowanceTarget).setSpenderWithTimelock(_newSpender);

        emit SetNewSpender(_newSpender);
    }

    function teardownAllowanceTarget() external onlyOperator {
        IAllowanceTarget(allowanceTarget).teardown();

        emit TearDownAllowanceTarget(block.timestamp);
    }



    /************************************************************
    *           Whitelist and blacklist functions               *
    *************************************************************/
    function isBlacklisted(address _tokenAddr) external view returns (bool) {
        return tokenBlacklist[_tokenAddr];
    }

    function blacklist(address[] calldata _tokenAddrs, bool[] calldata _isBlacklisted) external onlyOperator {
        require(_tokenAddrs.length == _isBlacklisted.length, "Spender: length mismatch");
        for (uint256 i = 0; i < _tokenAddrs.length; i++) {
            tokenBlacklist[_tokenAddrs[i]] = _isBlacklisted[i];

            emit BlackListToken(_tokenAddrs[i], _isBlacklisted[i]);
        }
    }
    
    function isAuthorized(address _caller) external view returns (bool) {
        return authorized[_caller];
    }

    function authorize(address[] calldata _pendingAuthorized) external onlyOperator {
        require(_pendingAuthorized.length > 0, "Spender: authorize list is empty");
        require(numPendingAuthorized == 0 && timelockExpirationTime == 0, "Spender: an authorize current in progress");

        if (timelockActivated) {
            numPendingAuthorized = _pendingAuthorized.length;
            for (uint256 i = 0; i < _pendingAuthorized.length; i++) {
                require(_pendingAuthorized[i] != address(0), "Spender: can not authorize zero address");
                pendingAuthorized[i] = _pendingAuthorized[i];
            }
            timelockExpirationTime = now + TIME_LOCK_DURATION;
        } else {
            for (uint256 i = 0; i < _pendingAuthorized.length; i++) {
                require(_pendingAuthorized[i] != address(0), "Spender: can not authorize zero address");
                authorized[_pendingAuthorized[i]] = true;

                emit AuthorizeSpender(_pendingAuthorized[i], true);
            }
        }
    }

    function completeAuthorize() external {
        require(timelockExpirationTime != 0, "Spender: no pending authorize");
        require(now >= timelockExpirationTime, "Spender: time lock not expired yet");

        for (uint256 i = 0; i < numPendingAuthorized; i++) {
            authorized[pendingAuthorized[i]] = true;
            emit AuthorizeSpender(pendingAuthorized[i], true);
            delete pendingAuthorized[i];
        }
        timelockExpirationTime = 0;
        numPendingAuthorized = 0;
    }

    function deauthorize(address[] calldata _deauthorized) external onlyOperator {
        for (uint256 i = 0; i < _deauthorized.length; i++) {
            authorized[_deauthorized[i]] = false;

            emit AuthorizeSpender(_deauthorized[i], false);
        }
    }

    function setConsumeGasERC20Tokens(address[] memory _consumeGasERC20Tokens) external onlyOperator {
        for (uint256 i = 0; i < _consumeGasERC20Tokens.length; i++) {
            consumeGasERC20Tokens[_consumeGasERC20Tokens[i]] = true;

            emit SetConsumeGasERC20Token(_consumeGasERC20Tokens[i]);
        }
    }

    /************************************************************
    *                   External functions                      *
    *************************************************************/
    /// @dev Spend tokens on user's behalf. Only an authority can call this.
    /// @param _user The user to spend token from.
    /// @param _tokenAddr The address of the token.
    /// @param _amount Amount to spend.
    function spendFromUser(address _user, address _tokenAddr, uint256 _amount) external onlyAuthorized {
        require(! tokenBlacklist[_tokenAddr], "Spender: token is blacklisted");

        // Fix gas stipend for non standard ERC20 transfer in case token contract's SafeMath violation is triggered
        // and all gas are consumed.
        uint256 gasStipend;
        if(consumeGasERC20Tokens[_tokenAddr]) gasStipend = 80000;
        else gasStipend = gasleft();

        if (_tokenAddr != ETH_ADDRESS && _tokenAddr != ZERO_ADDRESS) {

            uint256 balanceBefore = IERC20(_tokenAddr).balanceOf(msg.sender);
            (bool callSucceed, bytes memory returndata) = address(allowanceTarget).call{gas: gasStipend}(
                abi.encodeWithSelector(
                    IAllowanceTarget.executeCall.selector,
                    _tokenAddr,
                    abi.encodeWithSelector(
                        IERC20.transferFrom.selector,
                        _user,
                        msg.sender,
                        _amount
                    )
                )
            );
            require(callSucceed, "Spender: ERC20 transferFrom failed");
            bytes memory decodedReturnData = abi.decode(returndata, (bytes));
            if (decodedReturnData.length > 0) { // Return data is optional
                // Tokens like ZRX returns false on failed transfer
                require(abi.decode(decodedReturnData, (bool)), "Spender: ERC20 transferFrom failed");
            }
            // Check balance
            uint256 balanceAfter = IERC20(_tokenAddr).balanceOf(msg.sender);
            require(balanceAfter.sub(balanceBefore) == _amount, "Spender: ERC20 transferFrom amount mismatch");
        }
    }

    /// @dev Spend tokens on user's behalf. Only an authority can call this.
    /// @param _user The user to spend token from.
    /// @param _tokenAddr The address of the token.
    /// @param _receiver The receiver of the token.
    /// @param _amount Amount to spend.
    function spendFromUserTo(address _user, address _tokenAddr, address _receiver, uint256 _amount) external onlyAuthorized {
        require(! tokenBlacklist[_tokenAddr], "Spender: token is blacklisted");

        // Fix gas stipend for non standard ERC20 transfer in case token contract's SafeMath violation is triggered
        // and all gas are consumed.
        uint256 gasStipend;
        if(consumeGasERC20Tokens[_tokenAddr]) gasStipend = 80000;
        else gasStipend = gasleft();

        if (_tokenAddr != ETH_ADDRESS && _tokenAddr != ZERO_ADDRESS) {

            uint256 balanceBefore = IERC20(_tokenAddr).balanceOf(msg.sender);
            (bool callSucceed, bytes memory returndata) = address(allowanceTarget).call{gas: gasStipend}(
                abi.encodeWithSelector(
                    IAllowanceTarget.executeCall.selector,
                    _tokenAddr,
                    abi.encodeWithSelector(
                        IERC20.transferFrom.selector,
                        _user,
                        _receiver,
                        _amount
                    )
                )
            );
            require(callSucceed, "Spender: ERC20 transferFrom failed");
            bytes memory decodedReturnData = abi.decode(returndata, (bytes));
            if (decodedReturnData.length > 0) { // Return data is optional
                // Tokens like ZRX returns false on failed transfer
                require(abi.decode(decodedReturnData, (bool)), "Spender: ERC20 transferFrom failed");
            }
            // Check balance
            uint256 balanceAfter = IERC20(_tokenAddr).balanceOf(msg.sender);
            require(balanceAfter.sub(balanceBefore) == _amount, "Spender: ERC20 transferFrom amount mismatch");
        }
    }
}

File 6 of 57 : SafeMath.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

/**
 * @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, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        uint256 c = a + b;
        if (c < a) return (false, 0);
        return (true, c);
    }

    /**
     * @dev Returns the substraction of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        if (b > a) return (false, 0);
        return (true, a - b);
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, 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-contracts/pull/522
        if (a == 0) return (true, 0);
        uint256 c = a * b;
        if (c / a != b) return (false, 0);
        return (true, c);
    }

    /**
     * @dev Returns the division of two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        if (b == 0) return (false, 0);
        return (true, a / b);
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        if (b == 0) return (false, 0);
        return (true, a % b);
    }

    /**
     * @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");
        return a - b;
    }

    /**
     * @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) {
        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, reverting 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) {
        require(b > 0, "SafeMath: division by zero");
        return a / b;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting 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;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
     * overflow (when the result is negative).
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {trySub}.
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b <= a, errorMessage);
        return a - b;
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting with custom message on
     * division by zero. The result is rounded towards zero.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryDiv}.
     *
     * 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, string memory errorMessage) internal pure returns (uint256) {
        require(b > 0, errorMessage);
        return a / b;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting with custom message when dividing by zero.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryMod}.
     *
     * 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, string memory errorMessage) internal pure returns (uint256) {
        require(b > 0, errorMessage);
        return a % b;
    }
}

File 7 of 57 : IERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `recipient`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address recipient, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `sender` to `recipient` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);

    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);
}

File 8 of 57 : UserProxyStub.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.6.5;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import "../interfaces/IAMM.sol";

contract UserProxyStub {
    using SafeERC20 for IERC20;

    // Constants do not have storage slot.
    uint256 private constant MAX_UINT = 2**256 - 1;
    address private constant ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
    address private constant ZERO_ADDRESS = address(0);

    /**
     * @dev Below are the variables which consume storage slots.
     */
    address public operator;
    address public weth;
    address public ammWrapperAddr;
    address public pmmAddr;
    address public rfqAddr;

    receive() external payable {
    }

    /**
     * @dev Access control and ownership management.
     */
    modifier onlyOperator() {
        require(operator == msg.sender, "UserProxyStub: not the operator");
        _;
    }
    /* End of access control and ownership management */


    /**
     * @dev Replacing constructor and initialize the contract. This function should only be called once.
     */
    constructor(address _weth) public {
        operator = msg.sender;
        weth = _weth;
    }

    function upgradePMM(address _pmmAddr) external onlyOperator {
        pmmAddr = _pmmAddr;
    }

    function upgradeAMMWrapper(address _ammWrapperAddr) external onlyOperator {
        ammWrapperAddr = _ammWrapperAddr;
    }

    function upgradeRFQ(address _rfqAddr) external onlyOperator {
        rfqAddr = _rfqAddr;
    }

    function toAMM(bytes calldata _payload) external payable {
        (bool callSucceed,) = ammWrapperAddr.call{value: msg.value}(_payload);
        if (callSucceed == false) {
            // Get the error message returned
            assembly {
                let ptr := mload(0x40)
                let size := returndatasize()
                returndatacopy(ptr, 0, size)
                revert(ptr, size)
            }
        }
    }

    function toPMM(bytes calldata _payload) external payable {
        (bool callSucceed,) = pmmAddr.call{value: msg.value}(_payload);
        if (callSucceed == false) {
            // Get the error message returned
            assembly {
                let ptr := mload(0x40)
                let size := returndatasize()
                returndatacopy(ptr, 0, size)
                revert(ptr, size)
            }
        }
    }

    function toRFQ(bytes calldata _payload) external payable {
        (bool callSucceed,) = rfqAddr.call{value: msg.value}(_payload);
        if (callSucceed == false) {
            // Get the error message returned
            assembly {
                let ptr := mload(0x40)
                let size := returndatasize()
                returndatacopy(ptr, 0, size)
                revert(ptr, size)
            }
        }
    }
}

File 9 of 57 : SafeERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

import "./IERC20.sol";
import "../../math/SafeMath.sol";
import "../../utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    using SafeMath for uint256;
    using Address for address;

    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(IERC20 token, address spender, uint256 value) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        // solhint-disable-next-line max-line-length
        require((value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 newAllowance = token.allowance(address(this), spender).add(value);
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
        if (returndata.length > 0) { // Return data is optional
            // solhint-disable-next-line max-line-length
            require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
        }
    }
}

File 10 of 57 : IAMM.sol
pragma solidity ^0.6.0;

import "./ISetAllowance.sol";

interface IAMM is ISetAllowance {
    function trade(
        address _makerAddress,
        address _fromAssetAddress,
        address _toAssetAddress,
        uint256 _takerAssetAmount,
        uint256 _makerAssetAmount,
        uint256 _feeFactor,
        address _spender,
        address payable _receiver,
        uint256 _nonce,
        uint256 _deadline,
        bytes memory _sig
    ) payable external returns (uint256);
}

File 11 of 57 : ISetAllowance.sol
pragma solidity ^0.6.0;

interface ISetAllowance {
    function setAllowance(address[] memory tokenList, address spender) external;
    function closeAllowance(address[] memory tokenList, address spender) external;
}

File 12 of 57 : ERC1271WalletStub.sol
pragma solidity ^0.6.0;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import "../interfaces/ISetAllowance.sol";
import "../interfaces/IERC1271Wallet.sol";

contract ERC1271WalletStub is
    ISetAllowance,
    IERC1271Wallet
{
    using SafeERC20 for IERC20;
    // bytes4(keccak256("isValidSignature(bytes,bytes)"))
    bytes4 constant internal ERC1271_MAGICVALUE = 0x20c13b0b;

    // bytes4(keccak256("isValidSignature(bytes32,bytes)"))
    bytes4 constant internal ERC1271_MAGICVALUE_BYTES32 = 0x1626ba7e;
    uint256 private constant MAX_UINT = 2**256 - 1;
    address public operator;

    modifier onlyOperator() {
        require(operator == msg.sender, "Quoter: not the operator");
        _;
    }

    constructor (address _operator) public {
        operator = _operator;
    }

    function setAllowance(address[] memory _tokenList, address _spender) override external onlyOperator {
        for (uint256 i = 0 ; i < _tokenList.length; i++) {
            IERC20(_tokenList[i]).safeApprove(_spender, MAX_UINT);
        }
    }

    function closeAllowance(address[] memory _tokenList, address _spender) override external onlyOperator {
        for (uint256 i = 0 ; i < _tokenList.length; i++) {
            IERC20(_tokenList[i]).safeApprove(_spender, 0);
        }
    }

    function isValidSignature(
        bytes calldata _data,
        bytes calldata _signature)
        override
        external
        view
        returns (bytes4 magicValue)
    {
        return ERC1271_MAGICVALUE;
    }

    function isValidSignature(
        bytes32 _hash,
        bytes calldata _signature)
        override
        external
        view
        returns (bytes4 magicValue)
    {
        return ERC1271_MAGICVALUE_BYTES32;
    }
}

File 13 of 57 : IERC1271Wallet.sol
pragma solidity ^0.6.0;


interface  IERC1271Wallet {

  /**
   * @notice Verifies whether the provided signature is valid with respect to the provided data
   * @dev MUST return the correct magic value if the signature provided is valid for the provided data
   *   > The bytes4 magic value to return when signature is valid is 0x20c13b0b : bytes4(keccak256("isValidSignature(bytes,bytes)")
   *   > This function MAY modify Ethereum's state
   * @param _data       Arbitrary length data signed on the behalf of address(this)
   * @param _signature  Signature byte array associated with _data
   * @return magicValue Magic value 0x20c13b0b if the signature is valid and 0x0 otherwise
   *
   */
  function isValidSignature(
    bytes calldata _data,
    bytes calldata _signature)
    external
    view
    returns (bytes4 magicValue);

  /**
   * @notice Verifies whether the provided signature is valid with respect to the provided hash
   * @dev MUST return the correct magic value if the signature provided is valid for the provided hash
   *   > The bytes4 magic value to return when signature is valid is 0x20c13b0b : bytes4(keccak256("isValidSignature(bytes,bytes)")
   *   > This function MAY modify Ethereum's state
   * @param _hash       keccak256 hash that was signed
   * @param _signature  Signature byte array associated with _data
   * @return magicValue Magic value 0x20c13b0b if the signature is valid and 0x0 otherwise
   */
  function isValidSignature(
    bytes32 _hash,
    bytes calldata _signature)
    external
    view
    returns (bytes4 magicValue);
}

File 14 of 57 : RFQ.sol
pragma solidity ^0.6.0;
pragma experimental ABIEncoderV2;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import "@openzeppelin/contracts/math/SafeMath.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "./interfaces/ISpender.sol";
import "./interfaces/IWeth.sol";
import "./interfaces/IRFQ.sol";
import "./interfaces/IPermanentStorage.sol";
import "./interfaces/IERC1271Wallet.sol";
import "./utils/RFQLibEIP712.sol";

contract RFQ is
    ReentrancyGuard,
    IRFQ,
    RFQLibEIP712,
    SignatureValidator
{
    using SafeMath for uint256;
    using SafeERC20 for IERC20;
    using Address for address;

    // Constants do not have storage slot.
    string public constant version = "5.2.0";
    uint256 private constant MAX_UINT = 2**256 - 1;
    string public constant SOURCE = "RFQ v1";
    uint256 private constant BPS_MAX = 10000;
    address public immutable userProxy;
    IPermanentStorage public immutable permStorage;
    IWETH public immutable weth;

    // Below are the variables which consume storage slots.
    address public operator;
    ISpender public spender;

    struct GroupedVars {
        bytes32 orderHash;
        bytes32 transactionHash;
    }

    // Operator events
    event TransferOwnership(address newOperator);
    event UpgradeSpender(address newSpender);
    event AllowTransfer(address spender);
    event DisallowTransfer(address spender);
    event DepositETH(uint256 ethBalance);

    event FillOrder(
        string source,
        bytes32 indexed transactionHash,
        bytes32 indexed orderHash,
        address indexed userAddr,
        address takerAssetAddr,
        uint256 takerAssetAmount,
        address makerAddr,
        address makerAssetAddr,
        uint256 makerAssetAmount,
        address receiverAddr,
        uint256 settleAmount,
        uint16 feeFactor
    );


    receive() external payable {}


    /************************************************************
    *          Access control and ownership management          *
    *************************************************************/
    modifier onlyOperator {
        require(operator == msg.sender, "RFQ: not operator");
        _;
    }

    modifier onlyUserProxy() {
        require(address(userProxy) == msg.sender, "RFQ: not the UserProxy contract");
        _;
    }

    function transferOwnership(address _newOperator) external onlyOperator {
        require(_newOperator != address(0), "RFQ: operator can not be zero address");
        operator = _newOperator;

        emit TransferOwnership(_newOperator);
    }


    /************************************************************
    *              Constructor and init functions               *
    *************************************************************/
    constructor (
        address _operator, 
        address _userProxy, 
        ISpender _spender, 
        IPermanentStorage _permStorage, 
        IWETH _weth
    ) public {
        operator = _operator;
        userProxy = _userProxy;
        spender = _spender;
        permStorage = _permStorage;
        weth = _weth;
    }


    /************************************************************
    *           Management functions for Operator               *
    *************************************************************/
    /**
     * @dev set new Spender
     */
    function upgradeSpender(address _newSpender) external onlyOperator {
        require(_newSpender != address(0), "RFQ: spender can not be zero address");
        spender = ISpender(_newSpender);

        emit UpgradeSpender(_newSpender);
    }

    /**
     * @dev approve spender to transfer tokens from this contract. This is used to collect fee.
     */
    function setAllowance(address[] calldata _tokenList, address _spender) override external onlyOperator {
        for (uint256 i = 0 ; i < _tokenList.length; i++) {
            IERC20(_tokenList[i]).safeApprove(_spender, MAX_UINT);

            emit AllowTransfer(_spender);
        }
    }

    function closeAllowance(address[] calldata _tokenList, address _spender) override external onlyOperator {
        for (uint256 i = 0 ; i < _tokenList.length; i++) {
            IERC20(_tokenList[i]).safeApprove(_spender, 0);

            emit DisallowTransfer(_spender);
        }
    }

    /**
     * @dev convert collected ETH to WETH
     */
    function depositETH() external onlyOperator {
        uint256 balance = address(this).balance;
        if (balance > 0) {
            weth.deposit{value: balance}();

            emit DepositETH(balance);
        }
    }


    /************************************************************
    *                   External functions                      *
    *************************************************************/
    function fill(
        RFQLibEIP712.Order memory _order,
        bytes memory _mmSignature,
        bytes memory _userSignature
    )
        override
        payable
        external
        nonReentrant
        onlyUserProxy
        returns (uint256)
    {
        // check the order deadline and fee factor
        require(_order.deadline >= block.timestamp, "RFQ: expired order");
        require(_order.feeFactor < BPS_MAX, "RFQ: invalid fee factor");

        GroupedVars memory vars;

        // Validate signatures
        vars.orderHash = _getOrderHash(_order);
        require(
            isValidSignature(
                _order.makerAddr,
                _getOrderSignDigestFromHash(vars.orderHash),
                bytes(""),
                _mmSignature
            ),
            "RFQ: invalid MM signature"
        );
        vars.transactionHash = _getTransactionHash(_order);
        require(
            isValidSignature(
                _order.takerAddr,
                _getTransactionSignDigestFromHash(vars.transactionHash),
                bytes(""),
                _userSignature
            ),
            "RFQ: invalid user signature"
        );

        // Set transaction as seen, PermanentStorage would throw error if transaction already seen.
        permStorage.setRFQTransactionSeen(vars.transactionHash);

        // Deposit to WETH if taker asset is ETH, else transfer from user
        if (address(weth) == _order.takerAssetAddr) {
            require(
                msg.value == _order.takerAssetAmount,
                "RFQ: insufficient ETH"
            );
            weth.deposit{value: msg.value}();
        } else {
            spender.spendFromUser(_order.takerAddr, _order.takerAssetAddr, _order.takerAssetAmount);
        }
        // Transfer from maker
        spender.spendFromUser(_order.makerAddr, _order.makerAssetAddr, _order.makerAssetAmount);

        // settle token/ETH to user
        return _settle(_order, vars);
    }

    // settle
    function _settle(
        RFQLibEIP712.Order memory _order,
        GroupedVars memory _vars
    ) internal returns(uint256) {
        // Transfer taker asset to maker
        IERC20(_order.takerAssetAddr).safeTransfer(_order.makerAddr, _order.takerAssetAmount);

        // Transfer maker asset to taker, sub fee
        uint256 settleAmount = _order.makerAssetAmount;
        if (_order.feeFactor > 0) {
            // settleAmount = settleAmount * (10000 - feeFactor) / 10000
            settleAmount = settleAmount.mul((BPS_MAX).sub(_order.feeFactor)).div(BPS_MAX);
        }

        // Transfer token/Eth to receiver
        if (_order.makerAssetAddr == address(weth)){
            weth.withdraw(settleAmount);
            payable(_order.receiverAddr).transfer(settleAmount);
        } else {
            IERC20(_order.makerAssetAddr).safeTransfer(_order.receiverAddr, settleAmount);
        }

        emit FillOrder(
            SOURCE,
            _vars.transactionHash,
            _vars.orderHash,
            _order.takerAddr,
            _order.takerAssetAddr,
            _order.takerAssetAmount,
            _order.makerAddr,
            _order.makerAssetAddr,
            _order.makerAssetAmount,
            _order.receiverAddr,
            settleAmount,
            uint16(_order.feeFactor)
        );

        return settleAmount;
    }
}

File 15 of 57 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied 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.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;

    uint256 private _status;

    constructor () internal {
        _status = _NOT_ENTERED;
    }

    /**
     * @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() {
        // On the first call to nonReentrant, _notEntered will be true
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

        // Any calls to nonReentrant after this point will fail
        _status = _ENTERED;

        _;

        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }
}

File 16 of 57 : ISpender.sol
pragma solidity ^0.6.0;

interface ISpender {
    function spendFromUser(address _user, address _tokenAddr, uint256 _amount) external;
    function spendFromUserTo(address _user, address _tokenAddr, address _receiverAddr, uint256 _amount) external;
}

File 17 of 57 : IWeth.sol
pragma solidity ^0.6.0;

interface IWETH {
    function balanceOf(address account) external view returns (uint256);
    function deposit() external payable;
    function withdraw(uint256 amount) external;
    function transferFrom(address src, address dst, uint wad) external returns (bool);
}

File 18 of 57 : IRFQ.sol
pragma solidity ^0.6.0;
pragma experimental ABIEncoderV2;

import "../utils/RFQLibEIP712.sol";
import "./ISetAllowance.sol";

interface IRFQ is ISetAllowance {
    function fill(
        RFQLibEIP712.Order memory _order,
        bytes memory _mmSignature,
        bytes memory _userSignature
    ) external payable returns (uint256);
}

File 19 of 57 : IPermanentStorage.sol
pragma solidity ^0.6.0;

interface IPermanentStorage {
    function wethAddr() external view returns (address);
    function getCurvePoolInfo(address _makerAddr, address _takerAssetAddr, address _makerAssetAddr) external view returns (int128 takerAssetIndex, int128 makerAssetIndex, uint16 swapMethod, bool supportGetDx);
    function setCurvePoolInfo(address _makerAddr, address[] calldata _underlyingCoins, address[] calldata _coins, bool _supportGetDx) external;
    function isTransactionSeen(bytes32 _transactionHash) external view returns (bool);  // Kept for backward compatability. Should be removed from AMM 5.2.1 upward
    function isAMMTransactionSeen(bytes32 _transactionHash) external view returns (bool);
    function isRFQTransactionSeen(bytes32 _transactionHash) external view returns (bool);
    function isRelayerValid(address _relayer) external view returns (bool);
    function setTransactionSeen(bytes32 _transactionHash) external;  // Kept for backward compatability. Should be removed from AMM 5.2.1 upward
    function setAMMTransactionSeen(bytes32 _transactionHash) external;
    function setRFQTransactionSeen(bytes32 _transactionHash) external;
    function setRelayersValid(address[] memory _relayers, bool[] memory _isValids) external;
}

File 20 of 57 : RFQLibEIP712.sol
pragma solidity ^0.6.0;

import "./BaseLibEIP712.sol";
import "./SignatureValidator.sol";

contract RFQLibEIP712 is BaseLibEIP712 {
    /***********************************|
    |             Constants             |
    |__________________________________*/
    

    struct Order {
        address takerAddr;
        address makerAddr;
        address takerAssetAddr;
        address makerAssetAddr;
        uint256 takerAssetAmount;
        uint256 makerAssetAmount;
        address receiverAddr;
        uint256 salt;
        uint256 deadline;
        uint256 feeFactor;
    }

    bytes32 public constant ORDER_TYPEHASH = keccak256(
        abi.encodePacked(
            "Order(",
            "address takerAddr,",
            "address makerAddr,",
            "address takerAssetAddr,",
            "address makerAssetAddr,",
            "uint256 takerAssetAmount,",
            "uint256 makerAssetAmount,",
            "uint256 salt,",
            "uint256 deadline,",
            "uint256 feeFactor",
            ")"
        )
    );

    function _getOrderHash(Order memory _order) internal pure returns (bytes32 orderHash) {
        orderHash = keccak256(
            abi.encode(
                ORDER_TYPEHASH,
                _order.takerAddr,
                _order.makerAddr,
                _order.takerAssetAddr,
                _order.makerAssetAddr,
                _order.takerAssetAmount,
                _order.makerAssetAmount,
                _order.salt,
                _order.deadline,
                _order.feeFactor
            )
        );
    }

    function _getOrderSignDigest(Order memory _order) internal view returns (bytes32 orderSignDigest) {
        orderSignDigest = keccak256(
            abi.encodePacked(
                EIP191_HEADER,
                EIP712_DOMAIN_SEPARATOR,
                _getOrderHash(_order)
            )
        );
    }

    function _getOrderSignDigestFromHash(bytes32 _orderHash) internal view returns (bytes32 orderSignDigest) {
        orderSignDigest = keccak256(
            abi.encodePacked(
                EIP191_HEADER,
                EIP712_DOMAIN_SEPARATOR,
                _orderHash
            )
        );
    }

    bytes32 public constant FILL_WITH_PERMIT_TYPEHASH = keccak256(
        abi.encodePacked(
            "fillWithPermit(",
            "address makerAddr,",
            "address takerAssetAddr,",
            "address makerAssetAddr,",
            "uint256 takerAssetAmount,",
            "uint256 makerAssetAmount,",
            "address takerAddr,",
            "address receiverAddr,",
            "uint256 salt,",
            "uint256 deadline,",
            "uint256 feeFactor",
            ")"
        )
    );

    function _getTransactionHash(Order memory _order) internal pure returns(bytes32 transactionHash) {
        transactionHash = keccak256(
            abi.encode(
                FILL_WITH_PERMIT_TYPEHASH,
                _order.makerAddr,
                _order.takerAssetAddr,
                _order.makerAssetAddr,
                _order.takerAssetAmount,
                _order.makerAssetAmount,
                _order.takerAddr,
                _order.receiverAddr,
                _order.salt,
                _order.deadline,
                _order.feeFactor
            )
        );
    }

    function _getTransactionSignDigest(Order memory _order) internal view returns (bytes32 transactionSignDigest) {
        transactionSignDigest = keccak256(
            abi.encodePacked(
                EIP191_HEADER,
                EIP712_DOMAIN_SEPARATOR,
                _getTransactionHash(_order)
            )
        );
    }

    function _getTransactionSignDigestFromHash(bytes32 _txHash) internal view returns (bytes32 transactionSignDigest) {
        transactionSignDigest = keccak256(
            abi.encodePacked(
                EIP191_HEADER,
                EIP712_DOMAIN_SEPARATOR,
                _txHash
            )
        );
    }
}

File 21 of 57 : BaseLibEIP712.sol
pragma solidity ^0.6.0;

contract BaseLibEIP712 {
    /***********************************|
    |             Constants             |
    |__________________________________*/

    // EIP-191 Header
    string public constant EIP191_HEADER = "\x19\x01";

    // EIP712Domain
    string public constant EIP712_DOMAIN_NAME = "Tokenlon";
    string public constant EIP712_DOMAIN_VERSION = "v5";

    // EIP712Domain Separator
    bytes32 public immutable EIP712_DOMAIN_SEPARATOR = keccak256(
        abi.encode(
            keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
            keccak256(bytes(EIP712_DOMAIN_NAME)),
            keccak256(bytes(EIP712_DOMAIN_VERSION)),
            getChainID(),
            address(this)
        )
    );

    /**
        * @dev Return `chainId`
        */
    function getChainID() internal pure returns (uint) {
        uint chainId;
        assembly {
            chainId := chainid()
        }
        return chainId;
    }
}

File 22 of 57 : SignatureValidator.sol
pragma solidity ^0.6.0;

import "../interfaces/IERC1271Wallet.sol";
import "./LibBytes.sol";

interface IWallet {
    /// @dev Verifies that a signature is valid.
    /// @param hash Message hash that is signed.
    /// @param signature Proof of signing.
    /// @return isValid Validity of order signature.
    function isValidSignature(
        bytes32 hash,
        bytes memory signature
    )
        external
        view
        returns (bool isValid);
}

/**
 * @dev Contains logic for signature validation.
 * Signatures from wallet contracts assume ERC-1271 support (https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1271.md)
 * Notes: Methods are strongly inspired by contracts in https://github.com/0xProject/0x-monorepo/blob/development/
 */
contract SignatureValidator {
  using LibBytes for bytes;

  /***********************************|
  |             Variables             |
  |__________________________________*/

  // bytes4(keccak256("isValidSignature(bytes,bytes)"))
  bytes4 constant internal ERC1271_MAGICVALUE = 0x20c13b0b;

  // bytes4(keccak256("isValidSignature(bytes32,bytes)"))
  bytes4 constant internal ERC1271_MAGICVALUE_BYTES32 = 0x1626ba7e;

  // keccak256("isValidWalletSignature(bytes32,address,bytes)")
  bytes4 constant internal ERC1271_FALLBACK_MAGICVALUE_BYTES32 = 0xb0671381;

  // Allowed signature types.
  enum SignatureType {
    Illegal,                     // 0x00, default value
    Invalid,                     // 0x01
    EIP712,                      // 0x02
    EthSign,                     // 0x03
    WalletBytes,                 // 0x04  standard 1271 wallet type
    WalletBytes32,               // 0x05  standard 1271 wallet type
    Wallet,                      // 0x06  0x wallet type for signature compatibility
    NSignatureTypes              // 0x07, number of signature types. Always leave at end.
  }

  /***********************************|
  |        Signature Functions        |
  |__________________________________*/

  /**
   * @dev Verifies that a hash has been signed by the given signer.
   * @param _signerAddress  Address that should have signed the given hash.
   * @param _hash           Hash of the EIP-712 encoded data
   * @param _data           Full EIP-712 data structure that was hashed and signed
   * @param _sig            Proof that the hash has been signed by signer.
   *      For non wallet signatures, _sig is expected to be an array tightly encoded as
   *      (bytes32 r, bytes32 s, uint8 v, uint256 nonce, SignatureType sigType)
   * @return isValid True if the address recovered from the provided signature matches the input signer address.
   */
  function isValidSignature(
    address _signerAddress,
    bytes32 _hash,
    bytes memory _data,
    bytes memory _sig
  )
    public
    view
    returns (bool isValid)
  {
    require(
      _sig.length > 0,
      "SignatureValidator#isValidSignature: length greater than 0 required"
    );

    require(
      _signerAddress != address(0x0),
      "SignatureValidator#isValidSignature: invalid signer"
    );

    // Pop last byte off of signature byte array.
    uint8 signatureTypeRaw = uint8(_sig.popLastByte());

    // Ensure signature is supported
    require(
      signatureTypeRaw < uint8(SignatureType.NSignatureTypes),
      "SignatureValidator#isValidSignature: unsupported signature"
    );

    // Extract signature type
    SignatureType signatureType = SignatureType(signatureTypeRaw);

    // Variables are not scoped in Solidity.
    uint8 v;
    bytes32 r;
    bytes32 s;
    address recovered;

    // Always illegal signature.
    // This is always an implicit option since a signer can create a
    // signature array with invalid type or length. We may as well make
    // it an explicit option. This aids testing and analysis. It is
    // also the initialization value for the enum type.
    if (signatureType == SignatureType.Illegal) {
      revert("SignatureValidator#isValidSignature: illegal signature");


    // Signature using EIP712
    } else if (signatureType == SignatureType.EIP712) {
      require(
        _sig.length == 97,
        "SignatureValidator#isValidSignature: length 97 required"
      );
      r = _sig.readBytes32(0);
      s = _sig.readBytes32(32);
      v = uint8(_sig[64]);
      recovered = ecrecover(_hash, v, r, s);
      isValid = _signerAddress == recovered;
      return isValid;


    // Signed using web3.eth_sign() or Ethers wallet.signMessage()
    } else if (signatureType == SignatureType.EthSign) {
      require(
        _sig.length == 97,
        "SignatureValidator#isValidSignature: length 97 required"
      );
      r = _sig.readBytes32(0);
      s = _sig.readBytes32(32);
      v = uint8(_sig[64]);
      recovered = ecrecover(
        keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", _hash)),
        v,
        r,
        s
      );
      isValid = _signerAddress == recovered;
      return isValid;


    // Signature verified by wallet contract with data validation.
    } else if (signatureType == SignatureType.WalletBytes) {
      isValid = ERC1271_MAGICVALUE == IERC1271Wallet(_signerAddress).isValidSignature(_data, _sig);
      return isValid;


    // Signature verified by wallet contract without data validation.
    } else if (signatureType == SignatureType.WalletBytes32) {
      isValid = ERC1271_MAGICVALUE_BYTES32 == IERC1271Wallet(_signerAddress).isValidSignature(_hash, _sig);
      return isValid;


    } else if (signatureType == SignatureType.Wallet) {
      isValid = isValidWalletSignature(
          _hash,
          _signerAddress,
          _sig
      );
      return isValid;
    }

    // Anything else is illegal (We do not return false because
    // the signature may actually be valid, just not in a format
    // that we currently support. In this case returning false
    // may lead the caller to incorrectly believe that the
    // signature was invalid.)
    revert("SignatureValidator#isValidSignature: unsupported signature");
  }

  /// @dev Verifies signature using logic defined by Wallet contract.
  /// @param hash Any 32 byte hash.
  /// @param walletAddress Address that should have signed the given hash
  ///                      and defines its own signature verification method.
  /// @param signature Proof that the hash has been signed by signer.
  /// @return isValid True if signature is valid for given wallet..
  function isValidWalletSignature(
      bytes32 hash,
      address walletAddress,
      bytes memory signature
  )
      internal
      view
      returns (bool isValid)
  {
      bytes memory _calldata = abi.encodeWithSelector(
          IWallet(walletAddress).isValidSignature.selector,
          hash,
          signature
      );
      bytes32 magic_salt = bytes32(bytes4(keccak256("isValidWalletSignature(bytes32,address,bytes)")));
      assembly {
          if iszero(extcodesize(walletAddress)) {
              // Revert with `Error("WALLET_ERROR")`
              mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
              mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
              mstore(64, 0x0000000c57414c4c45545f4552524f5200000000000000000000000000000000)
              mstore(96, 0)
              revert(0, 100)
          }

          let cdStart := add(_calldata, 32)
          let success := staticcall(
              gas(),              // forward all gas
              walletAddress,    // address of Wallet contract
              cdStart,          // pointer to start of input
              mload(_calldata),  // length of input
              cdStart,          // write output over input
              32                // output size is 32 bytes
          )

          if iszero(eq(returndatasize(), 32)) {
              // Revert with `Error("WALLET_ERROR")`
              mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
              mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
              mstore(64, 0x0000000c57414c4c45545f4552524f5200000000000000000000000000000000)
              mstore(96, 0)
              revert(0, 100)
          }

          switch success
          case 0 {
              // Revert with `Error("WALLET_ERROR")`
              mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
              mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
              mstore(64, 0x0000000c57414c4c45545f4552524f5200000000000000000000000000000000)
              mstore(96, 0)
              revert(0, 100)
          }
          case 1 {
              // Signature is valid if call did not revert and returned true
              isValid := eq(
                  and(mload(cdStart), 0xffffffff00000000000000000000000000000000000000000000000000000000),
                  and(magic_salt, 0xffffffff00000000000000000000000000000000000000000000000000000000)
              )
          }
      }
      return isValid;
  }
}

File 23 of 57 : LibBytes.sol
/*
  Copyright 2018 ZeroEx Intl.
  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at
  http://www.apache.org/licenses/LICENSE-2.0
  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
  This is a truncated version of the original LibBytes.sol library from ZeroEx.
*/

pragma solidity ^0.6.0;


library LibBytes {
  using LibBytes for bytes;

  /***********************************|
  |        Pop Bytes Functions        |
  |__________________________________*/

  /**
   * @dev Pops the last byte off of a byte array by modifying its length.
   * @param b Byte array that will be modified.
   * @return result The byte that was popped off.
   */
  function popLastByte(bytes memory b)
    internal
    pure
    returns (bytes1 result)
  {
    require(
      b.length > 0,
      "LibBytes#popLastByte: greater than zero length required"
    );

    // Store last byte.
    result = b[b.length - 1];

    assembly {
      // Decrement length of byte array.
      let newLen := sub(mload(b), 1)
      mstore(b, newLen)
    }
    return result;
  }

  /// @dev Reads an address from a position in a byte array.
  /// @param b Byte array containing an address.
  /// @param index Index in byte array of address.
  /// @return result address from byte array.
  function readAddress(
    bytes memory b,
    uint256 index
  )
    internal
    pure
    returns (address result)
  {
    require(
      b.length >= index + 20,  // 20 is length of address
      "LibBytes#readAddress greater or equal to 20 length required"
    );

    // Add offset to index:
    // 1. Arrays are prefixed by 32-byte length parameter (add 32 to index)
    // 2. Account for size difference between address length and 32-byte storage word (subtract 12 from index)
    index += 20;

    // Read address from array memory
    assembly {
      // 1. Add index to address of bytes array
      // 2. Load 32-byte word from memory
      // 3. Apply 20-byte mask to obtain address
      result := and(mload(add(b, index)), 0xffffffffffffffffffffffffffffffffffffffff)
    }
    return result;
  }

  /***********************************|
  |        Read Bytes Functions       |
  |__________________________________*/

  /**
   * @dev Reads a bytes32 value from a position in a byte array.
   * @param b Byte array containing a bytes32 value.
   * @param index Index in byte array of bytes32 value.
   * @return result bytes32 value from byte array.
   */
  function readBytes32(
    bytes memory b,
    uint256 index
  )
    internal
    pure
    returns (bytes32 result)
  {
    require(
      b.length >= index + 32,
      "LibBytes#readBytes32 greater or equal to 32 length required"
    );

    // Arrays are prefixed by a 256 bit length parameter
    index += 32;

    // Read the bytes32 from array memory
    assembly {
      result := mload(add(b, index))
    }
    return result;
  }

  /// @dev Reads an unpadded bytes4 value from a position in a byte array.
  /// @param b Byte array containing a bytes4 value.
  /// @param index Index in byte array of bytes4 value.
  /// @return result bytes4 value from byte array.
  function readBytes4(
    bytes memory b,
    uint256 index
  )
    internal
    pure
    returns (bytes4 result)
  {
    require(
      b.length >= index + 4,
      "LibBytes#readBytes4 greater or equal to 4 length required"
    );

    // Arrays are prefixed by a 32 byte length field
    index += 32;

    // Read the bytes4 from array memory
    assembly {
      result := mload(add(b, index))
      // Solidity does not require us to clean the trailing bytes.
      // We do it anyway
      result := and(result, 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000)
    }
    return result;
  }

  function readBytes2(
    bytes memory b,
    uint256 index
  )
    internal
    pure
    returns (bytes2 result)
  {
    require(
      b.length >= index + 2,
      "LibBytes#readBytes2 greater or equal to 2 length required"
    );

    // Arrays are prefixed by a 32 byte length field
    index += 32;

    // Read the bytes4 from array memory
    assembly {
      result := mload(add(b, index))
      // Solidity does not require us to clean the trailing bytes.
      // We do it anyway
      result := and(result, 0xFFFF000000000000000000000000000000000000000000000000000000000000)
    }
    return result;
  }
}

File 24 of 57 : MultiSig.sol
pragma solidity 0.6.12;

import "../utils/LibBytes.sol";
import "./MultiSigLibEIP712.sol";

/**
 * @title MultiSig
 * @author dYdX
 *
 * Multi-Signature Wallet.
 * Allows multiple parties to agree on transactions before execution.
 * Adapted from Stefan George's MultiSigWallet contract.
 *
 * Logic Changes:
 *  - Removed the fallback function
 *  - Ensure newOwner is notNull
 *
 * Syntax Changes:
 *  - Update Solidity syntax for 0.5.X: use `emit` keyword (events), use `view` keyword (functions)
 *  - Add braces to all `if` and `for` statements
 *  - Remove named return variables
 *  - Add space before and after comparison operators
 *  - Add ADDRESS_ZERO as a constant
 *  - uint => uint256
 *  - external_call => externalCall
 */
contract MultiSig is MultiSigLibEIP712 {
    using LibBytes for bytes;

    // ============ Events ============

    event Deposit(address indexed depositer, uint256 amount);
    event Confirmation(address indexed sender, uint256 indexed transactionId);
    event Revocation(address indexed sender, uint256 indexed transactionId);
    event Submission(uint256 indexed transactionId);
    event Execution(uint256 indexed transactionId);
    event ExecutionFailure(uint256 indexed transactionId);
    event OwnerAddition(address indexed owner);
    event OwnerRemoval(address indexed owner);
    event RequirementChange(uint256 required);

    // ============ Constants ============

    uint256 constant public MAX_OWNER_COUNT = 50;
    address constant ADDRESS_ZERO = address(0x0);

    // ============ Storage ============

    mapping (uint256 => Transaction) public transactions;
    mapping (uint256 => mapping (address => bool)) public confirmations;
    mapping (address => bool) public isOwner;
    address[] public owners;
    uint256 public required;
    uint256 public transactionCount;

    // ============ Structs ============

    struct Transaction {
        address destination;
        uint256 value;
        bytes data;
        bool executed;
    }

    // ============ Modifiers ============

    modifier onlyWallet() {
        /* solium-disable-next-line error-reason */
        require(msg.sender == address(this));
        _;
    }

    modifier ownerDoesNotExist(
        address owner
    ) {
        /* solium-disable-next-line error-reason */
        require(!isOwner[owner]);
        _;
    }

    modifier ownerExists(
        address owner
    ) {
        /* solium-disable-next-line error-reason */
        require(isOwner[owner]);
        _;
    }

    modifier transactionExists(
        uint256 transactionId
    ) {
        /* solium-disable-next-line error-reason */
        require(transactions[transactionId].destination != ADDRESS_ZERO);
        _;
    }

    modifier confirmed(
        uint256 transactionId,
        address owner
    ) {
        /* solium-disable-next-line error-reason */
        require(confirmations[transactionId][owner]);
        _;
    }

    modifier notConfirmed(
        uint256 transactionId,
        address owner
    ) {
        /* solium-disable-next-line error-reason */
        require(!confirmations[transactionId][owner]);
        _;
    }

    modifier notExecuted(
        uint256 transactionId
    ) {
        /* solium-disable-next-line error-reason */
        require(!transactions[transactionId].executed);
        _;
    }

    modifier notNull(
        address _address
    ) {
        /* solium-disable-next-line error-reason */
        require(_address != ADDRESS_ZERO);
        _;
    }

    modifier validRequirement(
        uint256 ownerCount,
        uint256 _required
    ) {
        /* solium-disable-next-line error-reason */
        require(
            ownerCount <= MAX_OWNER_COUNT
            && _required <= ownerCount
            && _required != 0
            && ownerCount != 0
        );
        _;
    }

    // ========= Fallback function ==========
    receive() external payable {
        emit Deposit(msg.sender, msg.value);
    }

    // ============ Constructor ============

    /**
     * Contract constructor sets initial owners and required number of confirmations.
     *
     * @param  _owners    List of initial owners.
     * @param  _required  Number of required confirmations.
     */
    constructor(
        address[] memory _owners,
        uint256 _required
    )
        public
        validRequirement(_owners.length, _required)
        MultiSigLibEIP712()
    {
        for (uint256 i = 0; i < _owners.length; i++) {
            /* solium-disable-next-line error-reason */
            require(!isOwner[_owners[i]] && _owners[i] != ADDRESS_ZERO);
            isOwner[_owners[i]] = true;
        }
        owners = _owners;
        required = _required;
    }

    // ============ Wallet-Only Functions ============

    /**
     * Allows to add a new owner. Transaction has to be sent by wallet.
     *
     * @param  owner  Address of new owner.
     */
    function addOwner(
        address owner
    )
        public
        onlyWallet
        ownerDoesNotExist(owner)
        notNull(owner)
        validRequirement(owners.length + 1, required)
    {
        isOwner[owner] = true;
        owners.push(owner);
        emit OwnerAddition(owner);
    }

    /**
     * Allows to remove an owner. Transaction has to be sent by wallet.
     *
     * @param  owner  Address of owner.
     */
    function removeOwner(
        address owner
    )
        public
        onlyWallet
        ownerExists(owner)
    {
        isOwner[owner] = false;
        for (uint256 i = 0; i < owners.length - 1; i++) {
            if (owners[i] == owner) {
                owners[i] = owners[owners.length - 1];
                break;
            }
        }
        delete owners[owners.length - 1];
        if (required > owners.length) {
            changeRequirement(owners.length);
        }
        emit OwnerRemoval(owner);
    }

    /**
     * Allows to replace an owner with a new owner. Transaction has to be sent by wallet.
     *
     * @param  owner     Address of owner to be replaced.
     * @param  newOwner  Address of new owner.
     */
    function replaceOwner(
        address owner,
        address newOwner
    )
        public
        onlyWallet
        ownerExists(owner)
        ownerDoesNotExist(newOwner)
        notNull(newOwner)
    {
        for (uint256 i = 0; i < owners.length; i++) {
            if (owners[i] == owner) {
                owners[i] = newOwner;
                break;
            }
        }
        isOwner[owner] = false;
        isOwner[newOwner] = true;
        emit OwnerRemoval(owner);
        emit OwnerAddition(newOwner);
    }

    /**
     * Allows to change the number of required confirmations. Transaction has to be sent by wallet.
     *
     * @param  _required  Number of required confirmations.
     */
    function changeRequirement(
        uint256 _required
    )
        public
        onlyWallet
        validRequirement(owners.length, _required)
    {
        required = _required;
        emit RequirementChange(_required);
    }

    // ============ Owner Functions ============

    /**
     * Allows an owner to submit and confirm a transaction.
     *
     * @param  destination  Transaction target address.
     * @param  value        Transaction ether value.
     * @param  data         Transaction data payload.
     * @return              Transaction ID.
     */
    function submitTransaction(
        address destination,
        uint256 value,
        bytes memory data
    )
        public
        returns (uint256)
    {
        uint256 transactionId = addTransaction(destination, value, data);
        confirmTransaction(transactionId);
        return transactionId;
    }

    /**
     * Allows an owner to submit and confirm a transaction via meta transaction.
     *
     * @param  signer           Signer of the meta transaction.
     * @param  transactionId    Transaction ID of this transaction.
     * @param  destination      Transaction target address.
     * @param  value            Transaction ether value.
     * @param  data             Transaction data payload.
     * @param  sig              Signature.
     * @return                  Transaction ID.
     */
    function submitTransaction(
        address signer,
        uint256 transactionId,
        address destination,
        uint256 value,
        bytes memory data,
        bytes memory sig
    )
        public
        ownerExists(signer)
        returns (uint256)
    {
        // SUBMIT_TRANSACTION_TYPE_HASH = keccak256("submitTransaction(uint256 transactionId,address destination,uint256 value,bytes data)");
        bytes32 EIP712SignDigest = keccak256(
            abi.encodePacked(
                bytes1(0x19),
                bytes1(0x01),
                EIP712_DOMAIN_SEPARATOR,
                keccak256(
                    abi.encode(
                        SUBMIT_TRANSACTION_TYPE_HASH,
                        transactionId,
                        destination,
                        value,
                        data
                    )
                )
            )
        );
        validateSignature(signer, EIP712SignDigest, sig);

        uint256 _transactionId = addTransaction(destination, value, data);

        require(transactionId == _transactionId);

        confirmTransactionBySigner(signer, transactionId);
        return transactionId;
    }

    // confirm transaction on behalf of signer, not msg.sender
    function confirmTransactionBySigner(
        address signer,
        uint256 transactionId
    )
        internal
        transactionExists(transactionId)
        notConfirmed(transactionId, signer)
    {
        // Confirm
        confirmations[transactionId][signer] = true;
        emit Confirmation(signer, transactionId);

        // Execute
        executeTransactionBySigner(signer, transactionId);
    }

    // execute transaction on behalf of signer, not msg.sender
    function executeTransactionBySigner(
        address signer,
        uint256 transactionId
    )
        internal
        notExecuted(transactionId)
    {
        if (isConfirmed(transactionId)) {
            Transaction storage txn = transactions[transactionId];
            txn.executed = true;
            if (externalCall(
                txn.destination,
                txn.value,
                txn.data.length,
                txn.data)
            ) {
                emit Execution(transactionId);
            } else {
                emit ExecutionFailure(transactionId);
                txn.executed = false;
            }
        }
    }

    /**
     * Allows an owner to confirm a transaction.
     *
     * @param  transactionId  Transaction ID.
     */
    function confirmTransaction(
        uint256 transactionId
    )
        public
        virtual
        ownerExists(msg.sender)
        transactionExists(transactionId)
        notConfirmed(transactionId, msg.sender)
    {
        confirmations[transactionId][msg.sender] = true;
        emit Confirmation(msg.sender, transactionId);
        executeTransaction(transactionId);
    }

    /**
     * Allows an owner to confirm a transaction via meta transaction.
     *
     * @param  signer           Signer of the meta transaction.
     * @param  transactionId    Transaction ID.
     * @param  sig              Signature.
     */
    function confirmTransaction(
        address signer,
        uint256 transactionId,
        bytes memory sig
    )
        public
        virtual
        ownerExists(signer)
        transactionExists(transactionId)
        notConfirmed(transactionId, signer)
    {
        // CONFIRM_TRANSACTION_TYPE_HASH = keccak256("confirmTransaction(uint256 transactionId)");
        bytes32 EIP712SignDigest = keccak256(
            abi.encodePacked(
                bytes1(0x19),
                bytes1(0x01),
                EIP712_DOMAIN_SEPARATOR,
                keccak256(
                    abi.encode(
                        CONFIRM_TRANSACTION_TYPE_HASH,
                        transactionId
                    )
                )
            )
        );
        validateSignature(signer, EIP712SignDigest, sig);

        confirmations[transactionId][signer] = true;
        emit Confirmation(signer, transactionId);
        executeTransactionBySigner(signer, transactionId);
    }

    /**
     * Allows an owner to revoke a confirmation for a transaction.
     *
     * @param  transactionId  Transaction ID.
     */
    function revokeConfirmation(
        uint256 transactionId
    )
        public
        ownerExists(msg.sender)
        confirmed(transactionId, msg.sender)
        notExecuted(transactionId)
    {
        confirmations[transactionId][msg.sender] = false;
        emit Revocation(msg.sender, transactionId);
    }

    /**
     * Allows an owner to execute a confirmed transaction.
     *
     * @param  transactionId  Transaction ID.
     */
    function executeTransaction(
        uint256 transactionId
    )
        public
        virtual
        ownerExists(msg.sender)
        confirmed(transactionId, msg.sender)
        notExecuted(transactionId)
    {
        if (isConfirmed(transactionId)) {
            Transaction storage txn = transactions[transactionId];
            txn.executed = true;
            if (externalCall(
                txn.destination,
                txn.value,
                txn.data.length,
                txn.data)
            ) {
                emit Execution(transactionId);
            } else {
                emit ExecutionFailure(transactionId);
                txn.executed = false;
            }
        }
    }

    // ============ Getter Functions ============

    /**
     * Returns the confirmation status of a transaction.
     *
     * @param  transactionId  Transaction ID.
     * @return                Confirmation status.
     */
    function isConfirmed(
        uint256 transactionId
    )
        public
        view
        returns (bool)
    {
        uint256 count = 0;
        for (uint256 i = 0; i < owners.length; i++) {
            if (confirmations[transactionId][owners[i]]) {
                count += 1;
            }
            if (count == required) {
                return true;
            }
        }
    }

    /**
     * Returns number of confirmations of a transaction.
     *
     * @param  transactionId  Transaction ID.
     * @return                Number of confirmations.
     */
    function getConfirmationCount(
        uint256 transactionId
    )
        public
        view
        returns (uint256)
    {
        uint256 count = 0;
        for (uint256 i = 0; i < owners.length; i++) {
            if (confirmations[transactionId][owners[i]]) {
                count += 1;
            }
        }
        return count;
    }

    /**
     * Returns total number of transactions after filers are applied.
     *
     * @param  pending   Include pending transactions.
     * @param  executed  Include executed transactions.
     * @return           Total number of transactions after filters are applied.
     */
    function getTransactionCount(
        bool pending,
        bool executed
    )
        public
        view
        returns (uint256)
    {
        uint256 count = 0;
        for (uint256 i = 0; i < transactionCount; i++) {
            if (
                pending && !transactions[i].executed
                || executed && transactions[i].executed
            ) {
                count += 1;
            }
        }
        return count;
    }

    /**
     * Returns array of owners.
     *
     * @return  Array of owner addresses.
     */
    function getOwners()
        public
        view
        returns (address[] memory)
    {
        return owners;
    }

    /**
     * Returns array with owner addresses, which confirmed transaction.
     *
     * @param  transactionId  Transaction ID.
     * @return                Array of owner addresses.
     */
    function getConfirmations(
        uint256 transactionId
    )
        public
        view
        returns (address[] memory)
    {
        address[] memory confirmationsTemp = new address[](owners.length);
        uint256 count = 0;
        uint256 i;
        for (i = 0; i < owners.length; i++) {
            if (confirmations[transactionId][owners[i]]) {
                confirmationsTemp[count] = owners[i];
                count += 1;
            }
        }
        address[] memory _confirmations = new address[](count);
        for (i = 0; i < count; i++) {
            _confirmations[i] = confirmationsTemp[i];
        }
        return _confirmations;
    }

    /**
     * Returns list of transaction IDs in defined range.
     *
     * @param  from      Index start position of transaction array.
     * @param  to        Index end position of transaction array.
     * @param  pending   Include pending transactions.
     * @param  executed  Include executed transactions.
     * @return           Array of transaction IDs.
     */
    function getTransactionIds(
        uint256 from,
        uint256 to,
        bool pending,
        bool executed
    )
        public
        view
        returns (uint256[] memory)
    {
        uint256[] memory transactionIdsTemp = new uint256[](transactionCount);
        uint256 count = 0;
        uint256 i;
        for (i = 0; i < transactionCount; i++) {
            if (
                pending && !transactions[i].executed
                || executed && transactions[i].executed
            ) {
                transactionIdsTemp[count] = i;
                count += 1;
            }
        }
        uint256[] memory _transactionIds = new uint256[](to - from);
        for (i = from; i < to; i++) {
            _transactionIds[i - from] = transactionIdsTemp[i];
        }
        return _transactionIds;
    }

    // ============ Helper Functions ============

    function validateSignature(
        address signer,
        bytes32 digest,
        bytes memory sig
    )
        internal
    {
        require(sig.length == 65);
        uint8 v = uint8(sig[64]);
        bytes32 r = sig.readBytes32(0);
        bytes32 s = sig.readBytes32(32);
        address recovered = ecrecover(digest, v, r, s);
        require(signer == recovered);
    }

    // call has been separated into its own function in order to take advantage
    // of the Solidity's code generator to produce a loop that copies tx.data into memory.
    function externalCall(
        address destination,
        uint256 value,
        uint256 dataLength,
        bytes memory data
    )
        internal
        returns (bool)
    {
        bool result;
        /* solium-disable-next-line security/no-inline-assembly */
        assembly {
            let x := mload(0x40)   // "Allocate" memory for output (0x40 is where "free memory" pointer is stored by convention)
            let d := add(data, 32) // First 32 bytes are the padded length of data, so exclude that
            result := call(
                sub(gas(), 34710),   // 34710 is the value that solidity is currently emitting
                                   // It includes callGas (700) + callVeryLow (3, to pay for SUB) + callValueTransferGas (9000) +
                                   // callNewAccountGas (25000, in case the destination address does not exist and needs creating)
                destination,
                value,
                d,
                dataLength,        // Size of the input (in bytes) - this is what fixes the padding problem
                x,
                0                  // Output is ignored, therefore the output size is zero
            )
        }
        return result;
    }

    /**
     * Adds a new transaction to the transaction mapping, if transaction does not exist yet.
     *
     * @param  destination  Transaction target address.
     * @param  value        Transaction ether value.
     * @param  data         Transaction data payload.
     * @return              Transaction ID.
     */
    function addTransaction(
        address destination,
        uint256 value,
        bytes memory data
    )
        internal
        notNull(destination)
        returns (uint256)
    {
        uint256 transactionId = transactionCount;
        transactions[transactionId] = Transaction({
            destination: destination,
            value: value,
            data: data,
            executed: false
        });
        transactionCount += 1;
        emit Submission(transactionId);
        return transactionId;
    }
}

File 25 of 57 : MultiSigLibEIP712.sol
pragma solidity 0.6.12;

contract MultiSigLibEIP712 {
    /***********************************|
  |             Constants             |
  |__________________________________*/

    // EIP712Domain
    string public constant EIP712_DOMAIN_NAME = "MultiSig";
    string public constant EIP712_DOMAIN_VERSION = "v1";

    // EIP712Domain Separator
    bytes32 public EIP712_DOMAIN_SEPARATOR;

    // SUBMIT_TRANSACTION_TYPE_HASH = keccak256("submitTransaction(uint256 transactionId,address destination,uint256 value,bytes data)");
    bytes32 public constant SUBMIT_TRANSACTION_TYPE_HASH = 0x2c78e27c3bb2592e67e8d37ad1a95bfccd188e77557c22593b1af0b920a08295;

    // CONFIRM_TRANSACTION_TYPE_HASH = keccak256("confirmTransaction(uint256 transactionId)");
    bytes32 public constant CONFIRM_TRANSACTION_TYPE_HASH = 0x3e96bdc38d4133bc81813a187b2d41bc74332643ce7dbe82c7d94ead8366a65f;

    constructor() public {
        EIP712_DOMAIN_SEPARATOR = keccak256(
            abi.encode(
                keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
                keccak256(bytes(EIP712_DOMAIN_NAME)),
                keccak256(bytes(EIP712_DOMAIN_VERSION)),
                getChainID(),
                address(this)
            )
        );
    }

    /**
    * @dev Return `chainId`
    */
    function getChainID() internal pure returns (uint) {
        uint chainId;
        assembly {
            chainId := chainid()
        }
        return chainId;
    }
}

File 26 of 57 : MiningTreasury.sol
pragma solidity 0.6.12;

import "./MultiSig.sol";

contract MiningTreasury is MultiSig {
    constructor (
        address[] memory _owners,
        uint256 _required
    )
        public
        MultiSig(_owners, _required)
    {
    }
}

File 27 of 57 : DelayedMultiSig.sol
pragma solidity 0.6.12;

import "./MultiSig.sol";

// File: contracts/external/multisig/DelayedMultiSig.sol

/**
 * @title DelayedMultiSig
 * @author dYdX
 *
 * Multi-Signature Wallet with delay in execution.
 * Allows multiple parties to execute a transaction after a time lock has passed.
 * Adapted from Amir Bandeali's MultiSigWalletWithTimeLock contract.

 * Logic Changes:
 *  - Only owners can execute transactions
 *  - Require that each transaction succeeds
 *  - Added function to execute multiple transactions within the same Ethereum transaction
 */
contract DelayedMultiSig is
    MultiSig
{
    // ============ Events ============

    event ConfirmationTimeSet(uint256 indexed transactionId, uint256 confirmationTime);
    event TimeLockChange(uint32 secondsTimeLocked);

    // ============ Storage ============

    uint32 public secondsTimeLocked;
    mapping (uint256 => uint256) public confirmationTimes;

    // ============ Modifiers ============

    modifier notFullyConfirmed(
        uint256 transactionId
    ) {
        require(
            !isConfirmed(transactionId),
            "TX_FULLY_CONFIRMED"
        );
        _;
    }

    modifier fullyConfirmed(
        uint256 transactionId
    ) {
        require(
            isConfirmed(transactionId),
            "TX_NOT_FULLY_CONFIRMED"
        );
        _;
    }

    modifier pastTimeLock(
        uint256 transactionId
    ) virtual {
        require(
            block.timestamp >= confirmationTimes[transactionId] + secondsTimeLocked,
            "TIME_LOCK_INCOMPLETE"
        );
        _;
    }

    // ============ Constructor ============

    /**
     * Contract constructor sets initial owners, required number of confirmations, and time lock.
     *
     * @param  _owners             List of initial owners.
     * @param  _required           Number of required confirmations.
     * @param  _secondsTimeLocked  Duration needed after a transaction is confirmed and before it
     *                             becomes executable, in seconds.
     */
    constructor (
        address[] memory _owners,
        uint256 _required,
        uint32 _secondsTimeLocked
    )
        public
        MultiSig(_owners, _required)
    {
        secondsTimeLocked = _secondsTimeLocked;
    }

    // ============ Wallet-Only Functions ============

    /**
     * Changes the duration of the time lock for transactions.
     *
     * @param  _secondsTimeLocked  Duration needed after a transaction is confirmed and before it
     *                             becomes executable, in seconds.
     */
    function changeTimeLock(
        uint32 _secondsTimeLocked
    )
        public
        onlyWallet
    {
        secondsTimeLocked = _secondsTimeLocked;
        emit TimeLockChange(_secondsTimeLocked);
    }

    // ============ Owner Functions ============

    /**
     * Allows an owner to confirm a transaction.
     * Overrides the function in MultiSig.
     *
     * @param  transactionId  Transaction ID.
     */
    function confirmTransaction(
        uint256 transactionId
    )
        public
        override
        ownerExists(msg.sender)
        transactionExists(transactionId)
        notConfirmed(transactionId, msg.sender)
        notFullyConfirmed(transactionId)
    {
        confirmations[transactionId][msg.sender] = true;
        emit Confirmation(msg.sender, transactionId);
        if (isConfirmed(transactionId)) {
            setConfirmationTime(transactionId, block.timestamp);
        }
    }

    /**
     * Allows an owner to confirm a transaction via meta transaction.
     * Overrides the function in MultiSig.
     *
     * @param  signer           Signer of the meta transaction.
     * @param  transactionId    Transaction ID.
     * @param  sig              Signature.
     */
    function confirmTransaction(
        address signer,
        uint256 transactionId,
        bytes memory sig
    )
        public
        override
        ownerExists(signer)
        transactionExists(transactionId)
        notConfirmed(transactionId, signer)
        notFullyConfirmed(transactionId)
    {
        // CONFIRM_TRANSACTION_TYPE_HASH = keccak256("confirmTransaction(uint256 transactionId)");
        bytes32 EIP712SignDigest = keccak256(
            abi.encodePacked(
                bytes1(0x19),
                bytes1(0x01),
                EIP712_DOMAIN_SEPARATOR,
                keccak256(
                    abi.encode(
                        CONFIRM_TRANSACTION_TYPE_HASH,
                        transactionId
                    )
                )
            )
        );
        validateSignature(signer, EIP712SignDigest, sig);

        confirmations[transactionId][signer] = true;
        emit Confirmation(signer, transactionId);
        if (isConfirmed(transactionId)) {
            setConfirmationTime(transactionId, block.timestamp);
        }
    }

    /**
     * Allows an owner to execute a confirmed transaction.
     * Overrides the function in MultiSig.
     *
     * @param  transactionId  Transaction ID.
     */
    function executeTransaction(
        uint256 transactionId
    )
        public
        override
        ownerExists(msg.sender)
        notExecuted(transactionId)
        fullyConfirmed(transactionId)
        pastTimeLock(transactionId)
    {
        Transaction storage txn = transactions[transactionId];
        txn.executed = true;
        bool success = externalCall(
            txn.destination,
            txn.value,
            txn.data.length,
            txn.data
        );
        require(
            success,
            "TX_REVERTED"
        );
        emit Execution(transactionId);
    }

    /**
     * Allows an owner to execute multiple confirmed transactions.
     *
     * @param  transactionIds  List of transaction IDs.
     */
    function executeMultipleTransactions(
        uint256[] memory transactionIds
    )
        public
        ownerExists(msg.sender)
    {
        for (uint256 i = 0; i < transactionIds.length; i++) {
            executeTransaction(transactionIds[i]);
        }
    }

    // ============ Helper Functions ============

    /**
     * Sets the time of when a submission first passed.
     */
    function setConfirmationTime(
        uint256 transactionId,
        uint256 confirmationTime
    )
        internal
    {
        confirmationTimes[transactionId] = confirmationTime;
        emit ConfirmationTimeSet(transactionId, confirmationTime);
    }
}

File 28 of 57 : PartiallyDelayedMultiSig.sol
pragma solidity 0.6.12;

import "./DelayedMultiSig.sol";

// File: contracts/external/multisig/PartiallyDelayedMultiSig.sol

/**
 * @title PartiallyDelayedMultiSig
 * @author dYdX
 *
 * Multi-Signature Wallet with delay in execution except for some function selectors.
 */
contract PartiallyDelayedMultiSig is
    DelayedMultiSig
{
    // ============ Events ============

    event SelectorSet(address destination, bytes4 selector, bool approved);

    // ============ Constants ============

    bytes4 constant internal BYTES_ZERO = bytes4(0x0);

    // ============ Storage ============

    // destination => function selector => can bypass timelock
    mapping (address => mapping (bytes4 => bool)) public instantData;

    // ============ Modifiers ============

    // Overrides old modifier that requires a timelock for every transaction
    modifier pastTimeLock(
        uint256 transactionId
    ) override {
        // if the function selector is not exempt from timelock, then require timelock
        require(
            block.timestamp >= confirmationTimes[transactionId] + secondsTimeLocked
            || txCanBeExecutedInstantly(transactionId),
            "TIME_LOCK_INCOMPLETE"
        );
        _;
    }

    // ============ Constructor ============

    /**
     * Contract constructor sets initial owners, required number of confirmations, and time lock.
     *
     * @param  _owners               List of initial owners.
     * @param  _required             Number of required confirmations.
     * @param  _secondsTimeLocked    Duration needed after a transaction is confirmed and before it
     *                               becomes executable, in seconds.
     * @param  _noDelayDestinations  List of destinations that correspond with the selectors.
     *                               Zero address allows the function selector to be used with any
     *                               address.
     * @param  _noDelaySelectors     All function selectors that do not require a delay to execute.
     *                               Fallback function is 0x00000000.
     */
    constructor (
        address[] memory _owners,
        uint256 _required,
        uint32 _secondsTimeLocked,
        address[] memory _noDelayDestinations,
        bytes4[] memory _noDelaySelectors
    )
        public
        DelayedMultiSig(_owners, _required, _secondsTimeLocked)
    {
        require(
            _noDelayDestinations.length == _noDelaySelectors.length,
            "ADDRESS_AND_SELECTOR_MISMATCH"
        );

        for (uint256 i = 0; i < _noDelaySelectors.length; i++) {
            address destination = _noDelayDestinations[i];
            bytes4 selector = _noDelaySelectors[i];
            instantData[destination][selector] = true;
            emit SelectorSet(destination, selector, true);
        }
    }

    // ============ Wallet-Only Functions ============

    /**
     * Adds or removes functions that can be executed instantly. Transaction must be sent by wallet.
     *
     * @param  destination  Destination address of function. Zero address allows the function to be
     *                      sent to any address.
     * @param  selector     4-byte selector of the function. Fallback function is 0x00000000.
     * @param  approved     True if adding approval, false if removing approval.
     */
    function setSelector(
        address destination,
        bytes4 selector,
        bool approved
    )
        public
        onlyWallet
    {
        instantData[destination][selector] = approved;
        emit SelectorSet(destination, selector, approved);
    }

    // ============ Helper Functions ============

    /**
     * Returns true if transaction can be executed instantly (without timelock).
     */
    function txCanBeExecutedInstantly(
        uint256 transactionId
    )
        internal
        view
        returns (bool)
    {
        // get transaction from storage
        Transaction memory txn = transactions[transactionId];
        address dest = txn.destination;
        bytes memory data = txn.data;

        // fallback function
        if (data.length == 0) {
            return selectorCanBeExecutedInstantly(dest, BYTES_ZERO);
        }

        // invalid function selector
        if (data.length < 4) {
            return false;
        }

        // check first four bytes (function selector)
        bytes32 rawData;
        /* solium-disable-next-line security/no-inline-assembly */
        assembly {
            rawData := mload(add(data, 32))
        }
        bytes4 selector = bytes4(rawData);

        return selectorCanBeExecutedInstantly(dest, selector);
    }

    /**
     * Function selector is in instantData for address dest (or for address zero).
     */
    function selectorCanBeExecutedInstantly(
        address destination,
        bytes4 selector
    )
        internal
        view
        returns (bool)
    {
        return instantData[destination][selector]
            || instantData[ADDRESS_ZERO][selector];
    }
}

File 29 of 57 : LibDecoder.sol
pragma solidity ^0.6.0;
pragma experimental ABIEncoderV2;

import "./LibOrder.sol";
import "../../utils/LibBytes.sol";

contract LibDecoder {
    using LibBytes for bytes;

    function decodeFillOrder(bytes memory data) internal pure returns(LibOrder.Order memory order, uint256 takerFillAmount, bytes memory mmSignature) {
        require(
            data.length > 800,
            "LibDecoder: LENGTH_LESS_800"
        );

        // compare method_id
        // 0x64a3bc15 is fillOrKillOrder's method id.
        require(
            data.readBytes4(0) == 0x64a3bc15,
            "LibDecoder: WRONG_METHOD_ID"
        );
        
        bytes memory dataSlice;
        assembly {
            dataSlice := add(data, 4)
        }
        return abi.decode(dataSlice, (LibOrder.Order, uint256, bytes));

    }

    function decodeMmSignature(bytes memory signature) internal pure returns(uint8 v, bytes32 r, bytes32 s) {
        v = uint8(signature[0]);
        r = signature.readBytes32(1);
        s = signature.readBytes32(33);

        return (v, r, s);
    }

    function decodeUserSignatureWithoutSign(bytes memory signature) internal pure returns(address receiver) {
        require(
            signature.length == 85 || signature.length == 86,
            "LibDecoder: LENGTH_85_REQUIRED"
        );
        receiver = signature.readAddress(65);

        return receiver;
    }

    function decodeUserSignature(bytes memory signature) internal pure returns(uint8 v, bytes32 r, bytes32 s, address receiver) {
        receiver = decodeUserSignatureWithoutSign(signature);

        v = uint8(signature[0]);
        r = signature.readBytes32(1);
        s = signature.readBytes32(33);

        return (v, r, s, receiver);
    }

    function decodeERC20Asset(bytes memory assetData) internal pure returns(address) {
        require(
            assetData.length == 36,
            "LibDecoder: LENGTH_36_REQUIRED"
        );

        return assetData.readAddress(16);
    }
}

File 30 of 57 : LibOrder.sol
/*

  Copyright 2018 ZeroEx Intl.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.

*/

pragma solidity ^0.6.0;

import "./LibEIP712.sol";


contract LibOrder is
    LibEIP712
{
    // Hash for the EIP712 Order Schema
    bytes32 constant internal EIP712_ORDER_SCHEMA_HASH = keccak256(abi.encodePacked(
        "Order(",
        "address makerAddress,",
        "address takerAddress,",
        "address feeRecipientAddress,",
        "address senderAddress,",
        "uint256 makerAssetAmount,",
        "uint256 takerAssetAmount,",
        "uint256 makerFee,",
        "uint256 takerFee,",
        "uint256 expirationTimeSeconds,",
        "uint256 salt,",
        "bytes makerAssetData,",
        "bytes takerAssetData",
        ")"
    ));

    // A valid order remains fillable until it is expired, fully filled, or cancelled.
    // An order's state is unaffected by external factors, like account balances.
    enum OrderStatus {
        INVALID,                     // Default value
        INVALID_MAKER_ASSET_AMOUNT,  // Order does not have a valid maker asset amount
        INVALID_TAKER_ASSET_AMOUNT,  // Order does not have a valid taker asset amount
        FILLABLE,                    // Order is fillable
        EXPIRED,                     // Order has already expired
        FULLY_FILLED,                // Order is fully filled
        CANCELLED                    // Order has been cancelled
    }

    // solhint-disable max-line-length
    struct Order {
        address makerAddress;           // Address that created the order.      
        address takerAddress;           // Address that is allowed to fill the order. If set to 0, any address is allowed to fill the order.          
        address feeRecipientAddress;    // Address that will recieve fees when order is filled.      
        address senderAddress;          // Address that is allowed to call Exchange contract methods that affect this order. If set to 0, any address is allowed to call these methods.
        uint256 makerAssetAmount;       // Amount of makerAsset being offered by maker. Must be greater than 0.        
        uint256 takerAssetAmount;       // Amount of takerAsset being bid on by maker. Must be greater than 0.        
        uint256 makerFee;               // Amount of ZRX paid to feeRecipient by maker when order is filled. If set to 0, no transfer of ZRX from maker to feeRecipient will be attempted.
        uint256 takerFee;               // Amount of ZRX paid to feeRecipient by taker when order is filled. If set to 0, no transfer of ZRX from taker to feeRecipient will be attempted.
        uint256 expirationTimeSeconds;  // Timestamp in seconds at which order expires.          
        uint256 salt;                   // Arbitrary number to facilitate uniqueness of the order's hash.     
        bytes makerAssetData;           // Encoded data that can be decoded by a specified proxy contract when transferring makerAsset. The last byte references the id of this proxy.
        bytes takerAssetData;           // Encoded data that can be decoded by a specified proxy contract when transferring takerAsset. The last byte references the id of this proxy.
    }
    // solhint-enable max-line-length

    struct OrderInfo {
        uint8 orderStatus;                    // Status that describes order's validity and fillability.
        bytes32 orderHash;                    // EIP712 hash of the order (see LibOrder.getOrderHash).
        uint256 orderTakerAssetFilledAmount;  // Amount of order that has already been filled.
    }

    /// @dev Calculates Keccak-256 hash of the order.
    /// @param order The order structure.
    /// @return orderHash Keccak-256 EIP712 hash of the order.
    function getOrderHash(Order memory order)
        internal
        view
        returns (bytes32 orderHash)
    {
        orderHash = hashEIP712Message(hashOrder(order));
        return orderHash;
    }

    /// @dev Calculates EIP712 hash of the order.
    /// @param order The order structure.
    /// @return result EIP712 hash of the order.
    function hashOrder(Order memory order)
        internal
        pure
        returns (bytes32 result)
    {
        bytes32 schemaHash = EIP712_ORDER_SCHEMA_HASH;
        bytes32 makerAssetDataHash = keccak256(order.makerAssetData);
        bytes32 takerAssetDataHash = keccak256(order.takerAssetData);

        // Assembly for more efficiently computing:
        // keccak256(abi.encodePacked(
        //     EIP712_ORDER_SCHEMA_HASH,
        //     bytes32(order.makerAddress),
        //     bytes32(order.takerAddress),
        //     bytes32(order.feeRecipientAddress),
        //     bytes32(order.senderAddress),
        //     order.makerAssetAmount,
        //     order.takerAssetAmount,
        //     order.makerFee,
        //     order.takerFee,
        //     order.expirationTimeSeconds,
        //     order.salt,
        //     keccak256(order.makerAssetData),
        //     keccak256(order.takerAssetData)
        // ));

        assembly {
            // Calculate memory addresses that will be swapped out before hashing
            let pos1 := sub(order, 32)
            let pos2 := add(order, 320)
            let pos3 := add(order, 352)

            // Backup
            let temp1 := mload(pos1)
            let temp2 := mload(pos2)
            let temp3 := mload(pos3)
            
            // Hash in place
            mstore(pos1, schemaHash)
            mstore(pos2, makerAssetDataHash)
            mstore(pos3, takerAssetDataHash)
            result := keccak256(pos1, 416)
            
            // Restore
            mstore(pos1, temp1)
            mstore(pos2, temp2)
            mstore(pos3, temp3)
        }
        return result;
    }
}

File 31 of 57 : LibEIP712.sol
/*

  Copyright 2018 ZeroEx Intl.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.

*/

pragma solidity ^0.6.0;


contract LibEIP712 {

    // EIP191 header for EIP712 prefix
    string constant internal EIP191_HEADER = "\x19\x01";

    // EIP712 Domain Name value
    string constant internal EIP712_DOMAIN_NAME = "0x Protocol";

    // EIP712 Domain Version value
    string constant internal EIP712_DOMAIN_VERSION = "2";

    // Hash of the EIP712 Domain Separator Schema
    bytes32 constant internal EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH = keccak256(abi.encodePacked(
        "EIP712Domain(",
        "string name,",
        "string version,",
        "address verifyingContract",
        ")"
    ));

    // Hash of the EIP712 Domain Separator data
    // solhint-disable-next-line var-name-mixedcase
    bytes32 public EIP712_DOMAIN_HASH;

    constructor ()
        public
    {
        EIP712_DOMAIN_HASH = keccak256(abi.encodePacked(
            EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH,
            keccak256(bytes(EIP712_DOMAIN_NAME)),
            keccak256(bytes(EIP712_DOMAIN_VERSION)),
            bytes12(0),
            address(this)
        ));
    }

    /// @dev Calculates EIP712 encoding for a hash struct in this EIP712 Domain.
    /// @param hashStruct The EIP712 hash struct.
    /// @return result EIP712 hash applied to this EIP712 Domain.
    function hashEIP712Message(bytes32 hashStruct)
        internal
        view
        returns (bytes32 result)
    {
        bytes32 eip712DomainHash = EIP712_DOMAIN_HASH;

        // Assembly for more efficient computing:
        // keccak256(abi.encodePacked(
        //     EIP191_HEADER,
        //     EIP712_DOMAIN_HASH,
        //     hashStruct    
        // ));

        assembly {
            // Load free memory pointer
            let memPtr := mload(64)

            mstore(memPtr, 0x1901000000000000000000000000000000000000000000000000000000000000)  // EIP191 header
            mstore(add(memPtr, 2), eip712DomainHash)                                            // EIP712 domain hash
            mstore(add(memPtr, 34), hashStruct)                                                 // Hash of struct

            // Compute hash
            result := keccak256(memPtr, 66)
        }
        return result;
    }
}

File 32 of 57 : PMM.sol
pragma solidity ^0.6.0;
pragma experimental ABIEncoderV2;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import "@openzeppelin/contracts/math/SafeMath.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "./pmm/0xLibs/LibOrder.sol";
import "./pmm/0xLibs/LibDecoder.sol";
import "./pmm/0xLibs/LibEncoder.sol";
import "./interfaces/ISpender.sol";
import "./interfaces/IZeroExchange.sol";
import "./interfaces/IWeth.sol";
import "./interfaces/IPMM.sol";
import "./interfaces/IPermanentStorage.sol";
import "./interfaces/IERC1271Wallet.sol";

contract PMM is
    ReentrancyGuard,
    IPMM,
    LibOrder,
    LibDecoder,
    LibEncoder
{
    using SafeMath for uint256;
    using SafeERC20 for IERC20;
    using Address for address;

    // Constants do not have storage slot.
    string public constant version = "5.0.0";
    uint256 private constant MAX_UINT = 2**256 - 1;
    string public constant SOURCE = "0x v2";
    uint256 private constant BPS_MAX = 10000;
    bytes4 constant internal ERC1271_MAGICVALUE_BYTES32 = 0x1626ba7e;  // bytes4(keccak256("isValidSignature(bytes32,bytes)"))
    address public immutable userProxy;
    ISpender public immutable spender;
    IPermanentStorage public immutable permStorage;
    IZeroExchange public immutable zeroExchange;
    address public immutable zxERC20Proxy;

    // Below are the variables which consume storage slots.
    address public operator;

    struct TradeInfo {
        address user;
        address receiver;
        uint16 feeFactor;
        address makerAssetAddr;
        address takerAssetAddr;
        bytes32 transactionHash;
        bytes32 orderHash;
    }

    // events
    event FillOrder(
        string source,
        bytes32 indexed transactionHash,
        bytes32 indexed orderHash,
        address indexed userAddr,
        address takerAssetAddr,
        uint256 takerAssetAmount,
        address makerAddr,
        address makerAssetAddr,
        uint256 makerAssetAmount,
        address receiverAddr,
        uint256 settleAmount,
        uint16 feeFactor
    );


    receive() external payable {}


    /************************************************************
    *          Access control and ownership management          *
    *************************************************************/
    modifier onlyOperator {
        require(operator == msg.sender, "PMM: not operator");
        _;
    }

    modifier onlyUserProxy() {
        require(address(userProxy) == msg.sender, "PMM: not the UserProxy contract");
        _;
    }

    function transferOwnership(address _newOperator) external onlyOperator {
        require(_newOperator != address(0), "AMMWrapper: operator can not be zero address");
        operator = _newOperator;
    }


    /************************************************************
    *              Constructor and init functions               *
    *************************************************************/
    constructor (address _operator, address _userProxy, ISpender _spender, IPermanentStorage _permStorage, IZeroExchange _zeroExchange, address _zxERC20Proxy) public {
        operator = _operator;
        userProxy = _userProxy;
        spender = _spender;
        permStorage = _permStorage;
        zeroExchange = _zeroExchange;
        zxERC20Proxy = _zxERC20Proxy;
        // This constant follows ZX_EXCHANGE address
        EIP712_DOMAIN_HASH = keccak256(
            abi.encodePacked(
                EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH,
                keccak256(bytes(EIP712_DOMAIN_NAME)),
                keccak256(bytes(EIP712_DOMAIN_VERSION)),
                bytes12(0),
                address(_zeroExchange)
            )
        );
    }


    /************************************************************
    *           Management functions for Operator               *
    *************************************************************/
    /**
     * @dev approve spender to transfer tokens from this contract. This is used to collect fee.
     */
    function setAllowance(address[] calldata _tokenList, address _spender) override external onlyOperator {
        for (uint256 i = 0 ; i < _tokenList.length; i++) {
            IERC20(_tokenList[i]).safeApprove(_spender, MAX_UINT);
        }
    }

    function closeAllowance(address[] calldata _tokenList, address _spender) override external onlyOperator {
        for (uint256 i = 0 ; i < _tokenList.length; i++) {
            IERC20(_tokenList[i]).safeApprove(_spender, 0);
        }
    }


    /************************************************************
    *                   External functions                      *
    *************************************************************/
    function fill(
        uint256 userSalt,
        bytes memory data,
        bytes memory userSignature
    )
        override
        public
        payable
        onlyUserProxy
        nonReentrant
        returns (uint256)
    {
        // decode & assert
        (LibOrder.Order memory order,
        TradeInfo memory tradeInfo) = _assertTransaction(userSalt, data, userSignature);

        // Deposit to WETH if taker asset is ETH, else transfer from user
        IWETH weth = IWETH(permStorage.wethAddr());
        if (address(weth) == tradeInfo.takerAssetAddr) {
            require(
                msg.value == order.takerAssetAmount,
                "PMM: insufficient ETH"
            );
            weth.deposit{value: msg.value}();
        } else {
            spender.spendFromUser(tradeInfo.user, tradeInfo.takerAssetAddr, order.takerAssetAmount);
        }

        IERC20(tradeInfo.takerAssetAddr).safeIncreaseAllowance(zxERC20Proxy, order.takerAssetAmount);

        // send tx to 0x
        zeroExchange.executeTransaction(
            userSalt,
            address(this),
            data,
            ""
        );

        // settle token/ETH to user
        uint256 settleAmount = _settle(weth, tradeInfo.receiver, tradeInfo.makerAssetAddr, order.makerAssetAmount, tradeInfo.feeFactor);
        IERC20(tradeInfo.takerAssetAddr).safeApprove(zxERC20Proxy, 0);

        emit FillOrder(
            SOURCE,
            tradeInfo.transactionHash,
            tradeInfo.orderHash,
            tradeInfo.user,
            tradeInfo.takerAssetAddr,
            order.takerAssetAmount,
            order.makerAddress,
            tradeInfo.makerAssetAddr,
            order.makerAssetAmount,
            tradeInfo.receiver,
            settleAmount,
            tradeInfo.feeFactor
        );
        return settleAmount;
    }

    /**
     * @dev internal function of `fill`.
     * It decodes and validates transaction data.
     */
    function _assertTransaction(
        uint256 userSalt,
        bytes memory data,
        bytes memory userSignature
    )
        internal
        view
        returns(
            LibOrder.Order memory order,
            TradeInfo memory tradeInfo
        )
    {
        // decode fillOrder data
        uint256 takerFillAmount;
        bytes memory mmSignature;
        (order, takerFillAmount, mmSignature) = decodeFillOrder(data);

        require(
            order.takerAddress == address(this),
            "PMM: incorrect taker"
        );
        require(
            order.takerAssetAmount == takerFillAmount,
            "PMM: incorrect fill amount"
        );

        // generate transactionHash
        tradeInfo.transactionHash = encodeTransactionHash(
            userSalt,
            address(this),
            data
        );

        tradeInfo.orderHash = getOrderHash(order);
        tradeInfo.feeFactor = uint16(order.salt);
        tradeInfo.receiver = decodeUserSignatureWithoutSign(userSignature);
        tradeInfo.user = _ecrecoverAddress(tradeInfo.transactionHash, userSignature);

        if (tradeInfo.user != order.feeRecipientAddress) {
            require(
                order.feeRecipientAddress.isContract(),
                "PMM: invalid contract address"
            );
            // isValidSignature() should return magic value: bytes4(keccak256("isValidSignature(bytes32,bytes)"))
            require(
                ERC1271_MAGICVALUE_BYTES32 == IERC1271Wallet(order.feeRecipientAddress)
                    .isValidSignature(
                        tradeInfo.transactionHash,
                        userSignature
                    ),
                "PMM: invalid ERC1271 signer"
            );
            tradeInfo.user = order.feeRecipientAddress;
        }

        require(
            tradeInfo.feeFactor < BPS_MAX,
            "PMM: invalid fee factor"
        );

        require(
            tradeInfo.receiver != address(0),
            "PMM: invalid receiver"
        );

        // decode asset
        // just support ERC20
        tradeInfo.makerAssetAddr = decodeERC20Asset(order.makerAssetData);
        tradeInfo.takerAssetAddr = decodeERC20Asset(order.takerAssetData);
        return (
            order,
            tradeInfo
        );        
    }

    // settle
    function _settle(IWETH weth, address receiver, address makerAssetAddr, uint256 makerAssetAmount, uint16 feeFactor) internal returns(uint256) {
        uint256 settleAmount = makerAssetAmount;
        if (feeFactor > 0) {
            // settleAmount = settleAmount * (10000 - feeFactor) / 10000
            settleAmount = settleAmount.mul((BPS_MAX).sub(feeFactor)).div(BPS_MAX);
        }

        if (makerAssetAddr == address(weth)){
            weth.withdraw(settleAmount);
            payable(receiver).transfer(settleAmount);
        } else {
            IERC20(makerAssetAddr).safeTransfer(receiver, settleAmount);
        }

        return settleAmount;
    }

    function _ecrecoverAddress(bytes32 transactionHash, bytes memory signature) internal pure returns (address){
        (uint8 v, bytes32 r, bytes32 s, address receiver) = decodeUserSignature(signature);
        return ecrecover(
            keccak256(
                abi.encodePacked(
                    transactionHash,
                    receiver
                )),
            v, r, s
        );
    }
}

File 33 of 57 : LibEncoder.sol
pragma solidity ^0.6.0;
pragma experimental ABIEncoderV2;

import "./LibEIP712.sol";

contract LibEncoder is
    LibEIP712
{
    // Hash for the EIP712 ZeroEx Transaction Schema
    bytes32 constant internal EIP712_ZEROEX_TRANSACTION_SCHEMA_HASH = keccak256(
        abi.encodePacked(
        "ZeroExTransaction(",
        "uint256 salt,",
        "address signerAddress,",
        "bytes data",
        ")"
    ));

    function encodeTransactionHash(
        uint256 salt,
        address signerAddress,
        bytes memory data
    )
        internal
        view 
        returns (bytes32 result)
    {
        bytes32 schemaHash = EIP712_ZEROEX_TRANSACTION_SCHEMA_HASH;
        bytes32 dataHash = keccak256(data);

        // Assembly for more efficiently computing:
        // keccak256(abi.encodePacked(
        //     EIP712_ZEROEX_TRANSACTION_SCHEMA_HASH,
        //     salt,
        //     bytes32(signerAddress),
        //     keccak256(data)
        // ));

        assembly {
            // Load free memory pointer
            let memPtr := mload(64)

            mstore(memPtr, schemaHash)                                                               // hash of schema
            mstore(add(memPtr, 32), salt)                                                            // salt
            mstore(add(memPtr, 64), and(signerAddress, 0xffffffffffffffffffffffffffffffffffffffff))  // signerAddress
            mstore(add(memPtr, 96), dataHash)                                                        // hash of data

            // Compute hash
            result := keccak256(memPtr, 128)
        }
        result = hashEIP712Message(result);
        return result;
    }
}

File 34 of 57 : IZeroExchange.sol
/*
  Copyright 2018 ZeroEx Intl.
  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at
    http://www.apache.org/licenses/LICENSE-2.0
  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
*/

pragma solidity ^0.6.0;
pragma experimental ABIEncoderV2;

interface IZeroExchange {
    function executeTransaction(
        uint256 salt,
        address signerAddress,
        bytes calldata data,
        bytes calldata signature
    ) external;
}

File 35 of 57 : IPMM.sol
pragma solidity ^0.6.0;
pragma experimental ABIEncoderV2;

import "../pmm/0xLibs/LibOrder.sol";
import "./ISetAllowance.sol";

interface IPMM is ISetAllowance {
    function fill(
        uint256 userSalt,
        bytes memory data,
        bytes memory userSignature
    ) external payable returns (uint256);
}

File 36 of 57 : PermanentStorageStub.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.6.5;

import "../interfaces/IPermanentStorage.sol";
import "../utils/lib_storage/PSStorage.sol";

contract PermanentStorageStub is IPermanentStorage {

    // Supported Curve pools
    address public constant CURVE_COMPOUND_POOL = 0xA2B47E3D5c44877cca798226B7B8118F9BFb7A56;
    address public constant CURVE_USDT_POOL = 0x52EA46506B9CC5Ef470C5bf89f17Dc28bB35D85C;
    address public constant CURVE_Y_POOL = 0x45F783CCE6B7FF23B2ab2D70e416cdb7D6055f51;
    address public constant CURVE_3_POOL = 0xbEbc44782C7dB0a1A60Cb6fe97d0b483032FF1C7;
    address public constant CURVE_sUSD_POOL = 0xA5407eAE9Ba41422680e2e00537571bcC53efBfD;
    address public constant CURVE_BUSD_POOL = 0x79a8C46DeA5aDa233ABaFFD40F3A0A2B1e5A4F27;
    address public constant CURVE_renBTC_POOL = 0x93054188d876f558f4a66B2EF1d97d16eDf0895B;
    address public constant CURVE_sBTC_POOL = 0x7fC77b5c7614E1533320Ea6DDc2Eb61fa00A9714;
    address public constant CURVE_hBTC_POOL = 0x4CA9b3063Ec5866A4B82E437059D2C43d1be596F;
    address public constant CURVE_sETH_POOL = 0xc5424B857f758E906013F3555Dad202e4bdB4567;

    // Curve coins
    address private constant ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
    address private constant DAI = 0x6B175474E89094C44Da98b954EedeAC495271d0F;
    address private constant USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
    address private constant cDAI = 0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643;
    address private constant cUSDC = 0x39AA39c021dfbaE8faC545936693aC917d5E7563;
    address private constant USDT = 0xdAC17F958D2ee523a2206206994597C13D831ec7;
    address private constant TUSD = 0x0000000000085d4780B73119b644AE5ecd22b376;
    address private constant Y_POOL_yDAI = 0x16de59092dAE5CcF4A1E6439D611fd0653f0Bd01;
    address private constant Y_POOL_yUSDC = 0xd6aD7a6750A7593E092a9B218d66C0A814a3436e;
    address private constant Y_POOL_yUSDT = 0x83f798e925BcD4017Eb265844FDDAbb448f1707D;
    address private constant Y_POOL_yTUSD = 0x73a052500105205d34Daf004eAb301916DA8190f;
    address private constant sUSD = 0x57Ab1ec28D129707052df4dF418D58a2D46d5f51;
    address private constant BUSD = 0x4Fabb145d64652a948d72533023f6E7A623C7C53;
    address private constant BUSD_POOL_yDAI = 0xC2cB1040220768554cf699b0d863A3cd4324ce32;
    address private constant BUSD_POOL_yUSDC = 0x26EA744E5B887E5205727f55dFBE8685e3b21951;
    address private constant BUSD_POOL_yUSDT = 0xE6354ed5bC4b393a5Aad09f21c46E101e692d447;
    address private constant BUSD_POOL_yBUSD = 0x04bC0Ab673d88aE9dbC9DA2380cB6B79C4BCa9aE;
    address private constant renBTC = 0xEB4C2781e4ebA804CE9a9803C67d0893436bB27D;
    address private constant wBTC = 0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599;
    address private constant sBTC = 0xfE18be6b3Bd88A2D2A7f928d00292E7a9963CfC6;
    address private constant hBTC = 0x0316EB71485b0Ab14103307bf65a021042c6d380;
    address private constant sETH = 0x5e74C9036fb86BD7eCdcb084a0673EFc32eA31cb;

    constructor() public {
        // register WETH address
        PSStorage.getStorage().wethAddr = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
        // register Compound pool
        // underlying_coins, exchange_underlying
        AMMWrapperStorage.getStorage().curveTokenIndexes[CURVE_COMPOUND_POOL][DAI] = 1;
        AMMWrapperStorage.getStorage().curveTokenIndexes[CURVE_COMPOUND_POOL][USDC] = 2;
        // coins, exchange
        AMMWrapperStorage.getStorage().curveWrappedTokenIndexes[CURVE_COMPOUND_POOL][cDAI] = 1;
        AMMWrapperStorage.getStorage().curveWrappedTokenIndexes[CURVE_COMPOUND_POOL][cUSDC] = 2;
        AMMWrapperStorage.getStorage().curveSupportGetDx[CURVE_COMPOUND_POOL] = true; // support get_dx or get_dx_underlying for quoting

        // register USDT pool
        // underlying_coins, exchange_underlying
        AMMWrapperStorage.getStorage().curveTokenIndexes[CURVE_USDT_POOL][DAI] = 1;
        AMMWrapperStorage.getStorage().curveTokenIndexes[CURVE_USDT_POOL][USDC] = 2;
        AMMWrapperStorage.getStorage().curveTokenIndexes[CURVE_USDT_POOL][USDT] = 3;
        // coins, exchange
        AMMWrapperStorage.getStorage().curveWrappedTokenIndexes[CURVE_USDT_POOL][cDAI] = 1;
        AMMWrapperStorage.getStorage().curveWrappedTokenIndexes[CURVE_USDT_POOL][cUSDC] = 2;
        AMMWrapperStorage.getStorage().curveWrappedTokenIndexes[CURVE_USDT_POOL][USDT] = 3;
        AMMWrapperStorage.getStorage().curveSupportGetDx[CURVE_USDT_POOL] = true;

        // register Y pool
        // underlying_coins, exchange_underlying
        AMMWrapperStorage.getStorage().curveTokenIndexes[CURVE_Y_POOL][DAI] = 1;
        AMMWrapperStorage.getStorage().curveTokenIndexes[CURVE_Y_POOL][USDC] = 2;
        AMMWrapperStorage.getStorage().curveTokenIndexes[CURVE_Y_POOL][USDT] = 3;
        AMMWrapperStorage.getStorage().curveTokenIndexes[CURVE_Y_POOL][TUSD] = 4;
        // coins, exchange
        AMMWrapperStorage.getStorage().curveWrappedTokenIndexes[CURVE_Y_POOL][Y_POOL_yDAI] = 1;
        AMMWrapperStorage.getStorage().curveWrappedTokenIndexes[CURVE_Y_POOL][Y_POOL_yUSDC] = 2;
        AMMWrapperStorage.getStorage().curveWrappedTokenIndexes[CURVE_Y_POOL][Y_POOL_yUSDT] = 3;
        AMMWrapperStorage.getStorage().curveWrappedTokenIndexes[CURVE_Y_POOL][Y_POOL_yTUSD] = 4;
        AMMWrapperStorage.getStorage().curveSupportGetDx[CURVE_Y_POOL] = true;

        // register 3 pool
        // coins, exchange
        AMMWrapperStorage.getStorage().curveWrappedTokenIndexes[CURVE_3_POOL][DAI] = 1;
        AMMWrapperStorage.getStorage().curveWrappedTokenIndexes[CURVE_3_POOL][USDC] = 2;
        AMMWrapperStorage.getStorage().curveWrappedTokenIndexes[CURVE_3_POOL][USDT] = 3;
        AMMWrapperStorage.getStorage().curveSupportGetDx[CURVE_3_POOL] = false; // only support get_dy and get_dy_underlying for exactly the same functionality

        // register sUSD pool
        // coins, exchange
        AMMWrapperStorage.getStorage().curveWrappedTokenIndexes[CURVE_sUSD_POOL][DAI] = 1;
        AMMWrapperStorage.getStorage().curveWrappedTokenIndexes[CURVE_sUSD_POOL][USDC] = 2;
        AMMWrapperStorage.getStorage().curveWrappedTokenIndexes[CURVE_sUSD_POOL][USDT] = 3;
        AMMWrapperStorage.getStorage().curveWrappedTokenIndexes[CURVE_sUSD_POOL][sUSD] = 4;
        AMMWrapperStorage.getStorage().curveSupportGetDx[CURVE_sUSD_POOL] = false;

        // register BUSD pool
        // underlying_coins, exchange_underlying
        AMMWrapperStorage.getStorage().curveTokenIndexes[CURVE_BUSD_POOL][DAI] = 1;
        AMMWrapperStorage.getStorage().curveTokenIndexes[CURVE_BUSD_POOL][USDC] = 2;
        AMMWrapperStorage.getStorage().curveTokenIndexes[CURVE_BUSD_POOL][USDT] = 3;
        AMMWrapperStorage.getStorage().curveTokenIndexes[CURVE_BUSD_POOL][BUSD] = 4;
        // coins, exchange
        AMMWrapperStorage.getStorage().curveWrappedTokenIndexes[CURVE_BUSD_POOL][BUSD_POOL_yDAI] = 1;
        AMMWrapperStorage.getStorage().curveWrappedTokenIndexes[CURVE_BUSD_POOL][BUSD_POOL_yUSDC] = 2;
        AMMWrapperStorage.getStorage().curveWrappedTokenIndexes[CURVE_BUSD_POOL][BUSD_POOL_yUSDT] = 3;
        AMMWrapperStorage.getStorage().curveWrappedTokenIndexes[CURVE_BUSD_POOL][BUSD_POOL_yBUSD] = 4;
        AMMWrapperStorage.getStorage().curveSupportGetDx[CURVE_BUSD_POOL] = true;

        // register renBTC pool
        // coins, exchange
        AMMWrapperStorage.getStorage().curveWrappedTokenIndexes[CURVE_renBTC_POOL][renBTC] = 1; // renBTC
        AMMWrapperStorage.getStorage().curveWrappedTokenIndexes[CURVE_renBTC_POOL][wBTC] = 2; // wBTC
        AMMWrapperStorage.getStorage().curveSupportGetDx[CURVE_renBTC_POOL] = false;

        // register sBTC pool
        // coins, exchange
        AMMWrapperStorage.getStorage().curveWrappedTokenIndexes[CURVE_sBTC_POOL][renBTC] = 1; // renBTC
        AMMWrapperStorage.getStorage().curveWrappedTokenIndexes[CURVE_sBTC_POOL][wBTC] = 2; // wBTC
        AMMWrapperStorage.getStorage().curveWrappedTokenIndexes[CURVE_sBTC_POOL][sBTC] = 3; // sBTC
        AMMWrapperStorage.getStorage().curveSupportGetDx[CURVE_sBTC_POOL] = false;

        // register hBTC pool
        // coins, exchange
        AMMWrapperStorage.getStorage().curveWrappedTokenIndexes[CURVE_hBTC_POOL][hBTC] = 1; // hBTC
        AMMWrapperStorage.getStorage().curveWrappedTokenIndexes[CURVE_hBTC_POOL][wBTC] = 2; // wBTC
        AMMWrapperStorage.getStorage().curveSupportGetDx[CURVE_hBTC_POOL] = false;

        // register sETH pool
        // coins, exchange
        AMMWrapperStorage.getStorage().curveWrappedTokenIndexes[CURVE_sETH_POOL][ETH] = 1; // ETH
        AMMWrapperStorage.getStorage().curveWrappedTokenIndexes[CURVE_sETH_POOL][sETH] = 2; // sETH
        AMMWrapperStorage.getStorage().curveSupportGetDx[CURVE_sETH_POOL] = false;
    }

    /************************************************************
    *                     Getter functions                      *
    *************************************************************/
    function ammWrapperAddr() public view returns (address) {
        return PSStorage.getStorage().ammWrapperAddr;
    }

    function pmmAddr() public view returns (address) {
        return PSStorage.getStorage().pmmAddr;
    }

    function rfqAddr() public view returns (address) {
        return PSStorage.getStorage().rfqAddr;
    }

    function wethAddr() override external view returns (address) {
        return PSStorage.getStorage().wethAddr;
    }

    function getCurvePoolInfo(address _makerAddr, address _takerAssetAddr, address _makerAssetAddr) override external view returns (int128 takerAssetIndex, int128 makerAssetIndex, uint16 swapMethod, bool supportGetDx) {
        // underlying_coins
        int128 i = AMMWrapperStorage.getStorage().curveTokenIndexes[_makerAddr][_takerAssetAddr];
        int128 j = AMMWrapperStorage.getStorage().curveTokenIndexes[_makerAddr][_makerAssetAddr];
        supportGetDx = AMMWrapperStorage.getStorage().curveSupportGetDx[_makerAddr];

        swapMethod = 0;
        if (i != 0 && j != 0) {
            // in underlying_coins list
            takerAssetIndex = i;
            makerAssetIndex = j;
            // exchange_underlying
            swapMethod = 2;
        } else {
            // in coins list
            int128 iWrapped = AMMWrapperStorage.getStorage().curveWrappedTokenIndexes[_makerAddr][_takerAssetAddr];
            int128 jWrapped = AMMWrapperStorage.getStorage().curveWrappedTokenIndexes[_makerAddr][_makerAssetAddr];
            if (iWrapped != 0 && jWrapped != 0) {
                takerAssetIndex = iWrapped;
                makerAssetIndex = jWrapped;
                // exchange
                swapMethod = 1;
            } else {
                revert("PermanentStorage: invalid pair");
            }
        }
        return (takerAssetIndex, makerAssetIndex, swapMethod, supportGetDx);
    }

    function isTransactionSeen(bytes32 _transactionHash) override external view returns (bool) {
        return AMMWrapperStorage.getStorage().transactionSeen[_transactionHash];
    }

    function isAMMTransactionSeen(bytes32 _transactionHash) override external view returns (bool) {
        return AMMWrapperStorage.getStorage().transactionSeen[_transactionHash];
    }

    function isRFQTransactionSeen(bytes32 _transactionHash) override external view returns (bool) {
        return RFQStorage.getStorage().transactionSeen[_transactionHash];
    }

    function isRelayerValid(address _relayer) override external view returns (bool) {
        return AMMWrapperStorage.getStorage().relayerValid[_relayer];
    }


    /************************************************************
    *           Management functions for Operator               *
    *************************************************************/
    /// @dev Update AMMWrapper contract address.
    function upgradeAMMWrapper(address _newAMMWrapper) external {
        PSStorage.getStorage().ammWrapperAddr = _newAMMWrapper;
    }

    /// @dev Update PMM contract address.
    function upgradePMM(address _newPMM) external {
        PSStorage.getStorage().pmmAddr = _newPMM;
    }

    /// @dev Update RFQ contract address.
    function upgradeRFQ(address _newRFQ) external {
        PSStorage.getStorage().rfqAddr = _newRFQ;
    }

    /// @dev Update WETH contract address.
    function upgradeWETH(address _newWETH) external {
        PSStorage.getStorage().wethAddr = _newWETH;
    }


    /************************************************************
    *                   External functions                      *
    *************************************************************/
    function setCurvePoolInfo(address _makerAddr, address[] calldata _underlyingCoins, address[] calldata _coins, bool _supportGetDx) override external {
        int128 underlyingCoinsLength = int128(_underlyingCoins.length);
        for (int128 i = 0 ; i < underlyingCoinsLength; i++) {
            address assetAddr = _underlyingCoins[uint256(i)];
            // underlying coins for original DAI, USDC, TUSD
            AMMWrapperStorage.getStorage().curveTokenIndexes[_makerAddr][assetAddr] = i + 1;
        }

        int128 coinsLength = int128(_coins.length);
        for (int128 i = 0 ; i < coinsLength; i++) {
            address assetAddr = _coins[uint256(i)];
            // wrapped coins for cDAI, cUSDC, yDAI, yUSDC, yTUSD, yBUSD
            AMMWrapperStorage.getStorage().curveWrappedTokenIndexes[_makerAddr][assetAddr] = i + 1;
        }

        AMMWrapperStorage.getStorage().curveSupportGetDx[_makerAddr] = _supportGetDx;
    }

    function setTransactionSeen(bytes32 _transactionHash) override external {
        require(!AMMWrapperStorage.getStorage().transactionSeen[_transactionHash], "PermanentStorage: transaction seen before");
        AMMWrapperStorage.getStorage().transactionSeen[_transactionHash] = true;
    }

    function setAMMTransactionSeen(bytes32 _transactionHash) override external {
        require(!AMMWrapperStorage.getStorage().transactionSeen[_transactionHash], "PermanentStorage: transaction seen before");
        AMMWrapperStorage.getStorage().transactionSeen[_transactionHash] = true;
    }

    function setRFQTransactionSeen(bytes32 _transactionHash) override external {
        require(!RFQStorage.getStorage().transactionSeen[_transactionHash], "PermanentStorage: transaction seen before");
        RFQStorage.getStorage().transactionSeen[_transactionHash] = true;
    }

    function setRelayersValid(address[] calldata _relayers, bool[] calldata _isValids) override external {
        require(_relayers.length == _isValids.length, "PermanentStorage: inputs length mismatch");
        for (uint256 i = 0; i < _relayers.length; i++) {
            AMMWrapperStorage.getStorage().relayerValid[_relayers[i]] = _isValids[i];
        }
    }
}

File 37 of 57 : PSStorage.sol
pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;

library PSStorage {
    bytes32 private constant STORAGE_SLOT = 0x92dd52b981a2dd69af37d8a3febca29ed6a974aede38ae66e4ef773173aba471;

    struct Storage {
        address ammWrapperAddr;
        address pmmAddr;
        address wethAddr;
        address rfqAddr;
    }

    /// @dev Get the storage bucket for this contract.
    function getStorage() internal pure returns (Storage storage stor) {
        assert(STORAGE_SLOT == bytes32(uint256(keccak256("permanent.storage.storage")) - 1));
        bytes32 slot = STORAGE_SLOT;

        // Dip into assembly to change the slot pointed to by the local
        // variable `stor`.
        // See https://solidity.readthedocs.io/en/v0.6.8/assembly.html?highlight=slot#access-to-external-variables-functions-and-libraries
        assembly { stor_slot := slot }
    }
}

library AMMWrapperStorage {
    bytes32 private constant STORAGE_SLOT = 0xd38d862c9fa97c2fa857a46e08022d272a3579c114ca4f335f1e5fcb692c045e;

    struct Storage {
        mapping(bytes32 => bool) transactionSeen;
        // curve pool => underlying token address => underlying token index
        mapping(address => mapping(address => int128)) curveTokenIndexes;
        mapping(address => bool) relayerValid;
        // 5.1.0 appended storage
        // curve pool => wrapped token address => wrapped token index
        mapping(address => mapping(address => int128)) curveWrappedTokenIndexes;
        mapping(address => bool) curveSupportGetDx;
    }

    /// @dev Get the storage bucket for this contract.
    function getStorage() internal pure returns (Storage storage stor) {
        assert(STORAGE_SLOT == bytes32(uint256(keccak256("permanent.ammwrapper.storage")) - 1));
        bytes32 slot = STORAGE_SLOT;

        // Dip into assembly to change the slot pointed to by the local
        // variable `stor`.
        // See https://solidity.readthedocs.io/en/v0.6.8/assembly.html?highlight=slot#access-to-external-variables-functions-and-libraries
        assembly { stor_slot := slot }
    }
}

library RFQStorage {
    bytes32 private constant STORAGE_SLOT = 0x9174e76494cfb023ddc1eb0effb6c12e107165382bbd0ecfddbc38ea108bbe52;

    struct Storage {
        mapping(bytes32 => bool) transactionSeen;
    }

    /// @dev Get the storage bucket for this contract.
    function getStorage() internal pure returns (Storage storage stor) {
        assert(STORAGE_SLOT == bytes32(uint256(keccak256("permanent.rfq.storage")) - 1));
        bytes32 slot = STORAGE_SLOT;

        // Dip into assembly to change the slot pointed to by the local
        // variable `stor`.
        // See https://solidity.readthedocs.io/en/v0.6.8/assembly.html?highlight=slot#access-to-external-variables-functions-and-libraries
        assembly { stor_slot := slot }
    }
}

File 38 of 57 : PermanentStorage.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.6.5;

import "./interfaces/IPermanentStorage.sol";
import "./utils/lib_storage/PSStorage.sol";

contract PermanentStorage is IPermanentStorage {

    // Constants do not have storage slot.
    bytes32 public constant curveTokenIndexStorageId = 0xf4c750cdce673f6c35898d215e519b86e3846b1f0532fb48b84fe9d80f6de2fc; // keccak256("curveTokenIndex")
    bytes32 public constant transactionSeenStorageId = 0x695d523b8578c6379a2121164fd8de334b9c5b6b36dff5408bd4051a6b1704d0;  // keccak256("transactionSeen")
    bytes32 public constant relayerValidStorageId = 0x2c97779b4deaf24e9d46e02ec2699240a957d92782b51165b93878b09dd66f61;  // keccak256("relayerValid")

    // New supported Curve pools
    address public constant CURVE_renBTC_POOL = 0x93054188d876f558f4a66B2EF1d97d16eDf0895B;
    address public constant CURVE_sBTC_POOL = 0x7fC77b5c7614E1533320Ea6DDc2Eb61fa00A9714;
    address public constant CURVE_hBTC_POOL = 0x4CA9b3063Ec5866A4B82E437059D2C43d1be596F;
    address public constant CURVE_sETH_POOL = 0xc5424B857f758E906013F3555Dad202e4bdB4567;

    // Curve coins
    address private constant ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
    address private constant renBTC = 0xEB4C2781e4ebA804CE9a9803C67d0893436bB27D;
    address private constant wBTC = 0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599;
    address private constant sBTC = 0xfE18be6b3Bd88A2D2A7f928d00292E7a9963CfC6;
    address private constant hBTC = 0x0316EB71485b0Ab14103307bf65a021042c6d380;
    address private constant sETH = 0x5e74C9036fb86BD7eCdcb084a0673EFc32eA31cb;

    // Below are the variables which consume storage slots.
    address public operator;
    string public version;  // Current version of the contract
    mapping(bytes32 => mapping(address => bool)) private permission;


    // Operator events
    event TransferOwnership(address newOperator);
    event SetPermission(bytes32 storageId, address role, bool enabled);
    event UpgradeAMMWrapper(address newAMMWrapper);
    event UpgradePMM(address newPMM);
    event UpgradeRFQ(address newRFQ);
    event UpgradeWETH(address newWETH);


    /************************************************************
    *          Access control and ownership management          *
    *************************************************************/
    modifier onlyOperator() {
        require(operator == msg.sender, "PermanentStorage: not the operator");
        _;
    }

    modifier validRole(bool _enabled, address _role) {
        if (_enabled) {
            require(
                (_role == operator) || (_role == ammWrapperAddr()) || (_role == pmmAddr() || (_role == rfqAddr())),
                "PermanentStorage: not a valid role"
            );
        }
        _;
    }

    modifier isPermitted(bytes32 _storageId, address _role) {
        require(permission[_storageId][_role], "PermanentStorage: has no permission");
        _;
    }


    function transferOwnership(address _newOperator) external onlyOperator {
        require(_newOperator != address(0), "PermanentStorage: operator can not be zero address");
        operator = _newOperator;

        emit TransferOwnership(_newOperator);
    }

    /// @dev Set permission for entity to write certain storage.
    function setPermission(bytes32 _storageId, address _role, bool _enabled) external onlyOperator validRole(_enabled, _role) {
        permission[_storageId][_role] = _enabled;

        emit SetPermission(_storageId, _role, _enabled);
    }


    /************************************************************
    *              Constructor and init functions               *
    *************************************************************/
    /// @dev Replacing constructor and initialize the contract. This function should only be called once.
    function initialize() external {
        require(
            keccak256(abi.encodePacked(version)) == keccak256(abi.encodePacked("5.1.0")),
            "PermanentStorage: not upgrading from 5.1.0 version"
        );
        // upgrade from 5.1.0 to 5.2.0
        version = "5.2.0";
        // register renBTC pool
        // coins, exchange
        AMMWrapperStorage.getStorage().curveWrappedTokenIndexes[CURVE_renBTC_POOL][renBTC] = 1; // renBTC
        AMMWrapperStorage.getStorage().curveWrappedTokenIndexes[CURVE_renBTC_POOL][wBTC] = 2; // wBTC
        AMMWrapperStorage.getStorage().curveSupportGetDx[CURVE_renBTC_POOL] = false;

        // register sBTC pool
        // coins, exchange
        AMMWrapperStorage.getStorage().curveWrappedTokenIndexes[CURVE_sBTC_POOL][renBTC] = 1; // renBTC
        AMMWrapperStorage.getStorage().curveWrappedTokenIndexes[CURVE_sBTC_POOL][wBTC] = 2; // wBTC
        AMMWrapperStorage.getStorage().curveWrappedTokenIndexes[CURVE_sBTC_POOL][sBTC] = 3; // sBTC
        AMMWrapperStorage.getStorage().curveSupportGetDx[CURVE_sBTC_POOL] = false;

        // register hBTC pool
        // coins, exchange
        AMMWrapperStorage.getStorage().curveWrappedTokenIndexes[CURVE_hBTC_POOL][hBTC] = 1; // hBTC
        AMMWrapperStorage.getStorage().curveWrappedTokenIndexes[CURVE_hBTC_POOL][wBTC] = 2; // wBTC
        AMMWrapperStorage.getStorage().curveSupportGetDx[CURVE_hBTC_POOL] = false;

        // register sETH pool
        // coins, exchange
        AMMWrapperStorage.getStorage().curveWrappedTokenIndexes[CURVE_sETH_POOL][ETH] = 1; // ETH
        AMMWrapperStorage.getStorage().curveWrappedTokenIndexes[CURVE_sETH_POOL][sETH] = 2; // sETH
        AMMWrapperStorage.getStorage().curveSupportGetDx[CURVE_sETH_POOL] = false;
    }


    /************************************************************
    *                     Getter functions                      *
    *************************************************************/
    function hasPermission(bytes32 _storageId, address _role) external view returns (bool) {
        return permission[_storageId][_role];
    }

    function ammWrapperAddr() public view returns (address) {
        return PSStorage.getStorage().ammWrapperAddr;
    }

    function pmmAddr() public view returns (address) {
        return PSStorage.getStorage().pmmAddr;
    }

    function rfqAddr() public view returns (address) {
        return PSStorage.getStorage().rfqAddr;
    }

    function wethAddr() override external view returns (address) {
        return PSStorage.getStorage().wethAddr;
    }

    function getCurvePoolInfo(address _makerAddr, address _takerAssetAddr, address _makerAssetAddr) override external view returns (int128 takerAssetIndex, int128 makerAssetIndex, uint16 swapMethod, bool supportGetDx) {
        // underlying_coins
        int128 i = AMMWrapperStorage.getStorage().curveTokenIndexes[_makerAddr][_takerAssetAddr];
        int128 j = AMMWrapperStorage.getStorage().curveTokenIndexes[_makerAddr][_makerAssetAddr];
        supportGetDx = AMMWrapperStorage.getStorage().curveSupportGetDx[_makerAddr];

        swapMethod = 0;
        if (i != 0 && j != 0) {
            // in underlying_coins list
            takerAssetIndex = i;
            makerAssetIndex = j;
            // exchange_underlying
            swapMethod = 2;
        } else {
            // in coins list
            int128 iWrapped = AMMWrapperStorage.getStorage().curveWrappedTokenIndexes[_makerAddr][_takerAssetAddr];
            int128 jWrapped = AMMWrapperStorage.getStorage().curveWrappedTokenIndexes[_makerAddr][_makerAssetAddr];
            if (iWrapped != 0 && jWrapped != 0) {
                takerAssetIndex = iWrapped;
                makerAssetIndex = jWrapped;
                // exchange
                swapMethod = 1;
            } else {
                revert("PermanentStorage: invalid pair");
            }
        }
        return (takerAssetIndex, makerAssetIndex, swapMethod, supportGetDx);
    }

    /* 
    NOTE: `isTransactionSeen` is replaced by `isAMMTransactionSeen`. It is kept for backward compatability.
    It should be removed from AMM 5.2.1 upward.
    */
    function isTransactionSeen(bytes32 _transactionHash) override external view returns (bool) {
        return AMMWrapperStorage.getStorage().transactionSeen[_transactionHash];
    }

    function isAMMTransactionSeen(bytes32 _transactionHash) override external view returns (bool) {
        return AMMWrapperStorage.getStorage().transactionSeen[_transactionHash];
    }

    function isRFQTransactionSeen(bytes32 _transactionHash) override external view returns (bool) {
        return RFQStorage.getStorage().transactionSeen[_transactionHash];
    }

    function isRelayerValid(address _relayer) override external view returns (bool) {
        return AMMWrapperStorage.getStorage().relayerValid[_relayer];
    }


    /************************************************************
    *           Management functions for Operator               *
    *************************************************************/
    /// @dev Update AMMWrapper contract address.
    function upgradeAMMWrapper(address _newAMMWrapper) external onlyOperator {
        PSStorage.getStorage().ammWrapperAddr = _newAMMWrapper;

        emit UpgradeAMMWrapper(_newAMMWrapper);
    }

    /// @dev Update PMM contract address.
    function upgradePMM(address _newPMM) external onlyOperator {
        PSStorage.getStorage().pmmAddr = _newPMM;

        emit UpgradePMM(_newPMM);
    }

    /// @dev Update RFQ contract address.
    function upgradeRFQ(address _newRFQ) external onlyOperator {
        PSStorage.getStorage().rfqAddr = _newRFQ;

        emit UpgradeRFQ(_newRFQ);
    }

    /// @dev Update WETH contract address.
    function upgradeWETH(address _newWETH) external onlyOperator {
        PSStorage.getStorage().wethAddr = _newWETH;

        emit UpgradeWETH(_newWETH);
    }


    /************************************************************
    *                   External functions                      *
    *************************************************************/
    function setCurvePoolInfo(address _makerAddr, address[] calldata _underlyingCoins, address[] calldata _coins, bool _supportGetDx) override external isPermitted(curveTokenIndexStorageId, msg.sender) {
        int128 underlyingCoinsLength = int128(_underlyingCoins.length);
        for (int128 i = 0 ; i < underlyingCoinsLength; i++) {
            address assetAddr = _underlyingCoins[uint256(i)];
            // underlying coins for original DAI, USDC, TUSD
            AMMWrapperStorage.getStorage().curveTokenIndexes[_makerAddr][assetAddr] = i + 1;  // Start the index from 1
        }

        int128 coinsLength = int128(_coins.length);
        for (int128 i = 0 ; i < coinsLength; i++) {
            address assetAddr = _coins[uint256(i)];
            // wrapped coins for cDAI, cUSDC, yDAI, yUSDC, yTUSD, yBUSD
            AMMWrapperStorage.getStorage().curveWrappedTokenIndexes[_makerAddr][assetAddr] = i + 1;  // Start the index from 1
        }

        AMMWrapperStorage.getStorage().curveSupportGetDx[_makerAddr] = _supportGetDx;
    }

    /* 
    NOTE: `setTransactionSeen` is replaced by `setAMMTransactionSeen`. It is kept for backward compatability.
    It should be removed from AMM 5.2.1 upward.
    */
    function setTransactionSeen(bytes32 _transactionHash) override external isPermitted(transactionSeenStorageId, msg.sender) {
        require(!AMMWrapperStorage.getStorage().transactionSeen[_transactionHash], "PermanentStorage: transaction seen before");
        AMMWrapperStorage.getStorage().transactionSeen[_transactionHash] = true;
    }

    function setAMMTransactionSeen(bytes32 _transactionHash) override external isPermitted(transactionSeenStorageId, msg.sender) {
        require(!AMMWrapperStorage.getStorage().transactionSeen[_transactionHash], "PermanentStorage: transaction seen before");
        AMMWrapperStorage.getStorage().transactionSeen[_transactionHash] = true;
    }

    function setRFQTransactionSeen(bytes32 _transactionHash) override external isPermitted(transactionSeenStorageId, msg.sender) {
        require(!RFQStorage.getStorage().transactionSeen[_transactionHash], "PermanentStorage: transaction seen before");
        RFQStorage.getStorage().transactionSeen[_transactionHash] = true;
    }

    function setRelayersValid(address[] calldata _relayers, bool[] calldata _isValids) override external isPermitted(relayerValidStorageId, msg.sender) {
        require(_relayers.length == _isValids.length, "PermanentStorage: inputs length mismatch");
        for (uint256 i = 0; i < _relayers.length; i++) {
            AMMWrapperStorage.getStorage().relayerValid[_relayers[i]] = _isValids[i];
        }
    }
}

File 40 of 57 : AMMWrapper.sol
pragma solidity ^0.6.0;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "@openzeppelin/contracts/math/SafeMath.sol";
import "./interfaces/ISpender.sol";
import "./interfaces/IUniswapExchange.sol";
import "./interfaces/IUniswapFactory.sol";
import "./interfaces/IUniswapRouterV2.sol";
import "./interfaces/ICurveFi.sol";
import "./interfaces/IAMM.sol";
import "./interfaces/IWeth.sol";
import "./interfaces/IPermanentStorage.sol";
import "./utils/AMMLibEIP712.sol";
import "./utils/SignatureValidator.sol";

contract AMMWrapper is
    IAMM,
    ReentrancyGuard,
    AMMLibEIP712,
    SignatureValidator
{
    using SafeMath for uint256;
    using SafeERC20 for IERC20;

    // Constants do not have storage slot.
    string public constant version = "5.2.0";
    uint256 internal constant MAX_UINT = 2**256 - 1;
    uint256 internal constant BPS_MAX = 10000;
    address internal constant ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
    address internal constant ZERO_ADDRESS = address(0);
    address public immutable userProxy;
    IWETH public immutable weth;
    IPermanentStorage public immutable permStorage;
    address public constant UNISWAP_V2_ROUTER_02_ADDRESS = 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D;
    address public constant SUSHISWAP_ROUTER_ADDRESS = 0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F;

    // Below are the variables which consume storage slots.
    address public operator;
    uint256 public subsidyFactor;
    ISpender public spender;

    /* Struct and event declaration */
    // Group the local variables together to prevent
    // Compiler error: Stack too deep, try removing local variables.
    struct TxMetaData {
        string source;
        bytes32 transactionHash;
        uint256 settleAmount;
        uint256 receivedAmount;
        uint16 feeFactor;
        uint16 subsidyFactor;
    }

    struct InternalTxData {
        bool fromEth;
        bool toEth;
        address takerAssetInternalAddr;
        address makerAssetInternalAddr;
        address[] path;
        bytes makerSpecificData;
    }

    struct CurveData {
        int128 fromTokenCurveIndex;
        int128 toTokenCurveIndex;
        uint16 swapMethod;
    }

    // Operator events
    event TransferOwnership(address newOperator);
    event UpgradeSpender(address newSpender);
    event SetSubsidyFactor(uint256 newSubisdyFactor);
    event AllowTransfer(address spender);
    event DisallowTransfer(address spender);
    event DepositETH(uint256 ethBalance);

    event Swapped(
        string source,
        bytes32 indexed transactionHash,
        address indexed userAddr,
        address takerAssetAddr,
        uint256 takerAssetAmount,
        address makerAddr,
        address makerAssetAddr,
        uint256 makerAssetAmount,
        address receiverAddr,
        uint256 settleAmount,
        uint256 receivedAmount,
        uint16 feeFactor,
        uint16 subsidyFactor
    );


    receive() external payable {}


    /************************************************************
    *          Access control and ownership management          *
    *************************************************************/
    modifier onlyOperator() {
        require(operator == msg.sender, "AMMWrapper: not the operator");
        _;
    }

    modifier onlyUserProxy() {
        require(address(userProxy) == msg.sender, "AMMWrapper: not the UserProxy contract");
        _;
    }

    function transferOwnership(address _newOperator) external onlyOperator {
        require(_newOperator != address(0), "AMMWrapper: operator can not be zero address");
        operator = _newOperator;

        emit TransferOwnership(_newOperator);
    }

    /************************************************************
    *                 Internal function modifier                *
    *************************************************************/
    modifier approveTakerAsset(address _takerAssetInternalAddr, address _makerAddr) {
        bool isTakerAssetETH = _isInternalAssetETH(_takerAssetInternalAddr);
        if (! isTakerAssetETH) IERC20(_takerAssetInternalAddr).safeApprove(_makerAddr, MAX_UINT);

        _;

        if (! isTakerAssetETH) IERC20(_takerAssetInternalAddr).safeApprove(_makerAddr, 0);
    }

    /************************************************************
    *              Constructor and init functions               *
    *************************************************************/
    constructor (
        address _operator,
        uint256 _subsidyFactor,
        address _userProxy,
        ISpender _spender,
        IPermanentStorage _permStorage,
        IWETH _weth
    ) public {
        operator = _operator;
        subsidyFactor = _subsidyFactor;
        userProxy = _userProxy;
        spender = _spender;
        permStorage = _permStorage;
        weth = _weth;
    }


    /************************************************************
    *           Management functions for Operator               *
    *************************************************************/
    /**
     * @dev set new Spender
     */
    function upgradeSpender(address _newSpender) external onlyOperator {
        require(_newSpender != address(0), "AMMWrapper: spender can not be zero address");
        spender = ISpender(_newSpender);

        emit UpgradeSpender(_newSpender);
    }

    function setSubsidyFactor(uint256 _subsidyFactor) external onlyOperator {
        subsidyFactor = _subsidyFactor;

        emit SetSubsidyFactor(_subsidyFactor);
    }

    /**
     * @dev approve spender to transfer tokens from this contract. This is used to collect fee.
     */
    function setAllowance(address[] calldata _tokenList, address _spender) override external onlyOperator {
        for (uint256 i = 0 ; i < _tokenList.length; i++) {
            IERC20(_tokenList[i]).safeApprove(_spender, MAX_UINT);

            emit AllowTransfer(_spender);
        }
    }

    function closeAllowance(address[] calldata _tokenList, address _spender) override external onlyOperator {
        for (uint256 i = 0 ; i < _tokenList.length; i++) {
            IERC20(_tokenList[i]).safeApprove(_spender, 0);

            emit DisallowTransfer(_spender);
        }
    }

    /**
     * @dev convert collected ETH to WETH
     */
    function depositETH() external onlyOperator {
        uint256 balance = address(this).balance;
        if (balance > 0) {
            weth.deposit{value: balance}();

            emit DepositETH(balance);
        }
    }


    /************************************************************
    *                   External functions                      *
    *************************************************************/
    function trade(
        address _makerAddr,
        address _takerAssetAddr,
        address _makerAssetAddr,
        uint256 _takerAssetAmount,
        uint256 _makerAssetAmount,
        uint256 _feeFactor,
        address _userAddr,
        address payable _receiverAddr,
        uint256 _salt,
        uint256 _deadline,
        bytes calldata _sig
    )
        override
        payable
        external
        nonReentrant
        onlyUserProxy
        returns (uint256) 
    {
        Order memory order = Order(
            _makerAddr,
            _takerAssetAddr,
            _makerAssetAddr,
            _takerAssetAmount,
            _makerAssetAmount,
            _userAddr,
            _receiverAddr,
            _salt,
            _deadline
        );
        require(order.deadline >= block.timestamp, "AMMWrapper: expired order");
        TxMetaData memory txMetaData;
        InternalTxData memory internalTxData;

        // These variables are copied straight from function parameters and
        // used to bypass stack too deep error.
        txMetaData.subsidyFactor = uint16(subsidyFactor);
        txMetaData.feeFactor = uint16(_feeFactor);
        if (! permStorage.isRelayerValid(tx.origin)) {
            txMetaData.feeFactor = (txMetaData.subsidyFactor > txMetaData.feeFactor) ? txMetaData.subsidyFactor : txMetaData.feeFactor;
            txMetaData.subsidyFactor = 0;
        }

        // Assign trade vairables
        internalTxData.fromEth = (order.takerAssetAddr == ZERO_ADDRESS || order.takerAssetAddr == ETH_ADDRESS);
        internalTxData.toEth = (order.makerAssetAddr == ZERO_ADDRESS || order.makerAssetAddr == ETH_ADDRESS);
        if(_isCurve(order.makerAddr)) {
            // PermanetStorage can recognize `ETH_ADDRESS` but not `ZERO_ADDRESS`.
            // Convert it to `ETH_ADDRESS` as passed in `order.takerAssetAddr` or `order.makerAssetAddr` might be `ZERO_ADDRESS`.
            internalTxData.takerAssetInternalAddr = internalTxData.fromEth ? ETH_ADDRESS : order.takerAssetAddr;
            internalTxData.makerAssetInternalAddr = internalTxData.toEth ? ETH_ADDRESS : order.makerAssetAddr;
        } else {
            internalTxData.takerAssetInternalAddr = internalTxData.fromEth ? address(weth) : order.takerAssetAddr;
            internalTxData.makerAssetInternalAddr = internalTxData.toEth ? address(weth) : order.makerAssetAddr;
        }

        txMetaData.transactionHash = _verify(
            order,
            _sig
        );

        _prepare(order, internalTxData);

        (txMetaData.source, txMetaData.receivedAmount) = _swap(
            order,
            txMetaData,
            internalTxData
        );

        // Settle
        txMetaData.settleAmount = _settle(
            order,
            txMetaData,
            internalTxData
        );

        emit Swapped(
            txMetaData.source,
            txMetaData.transactionHash,
            order.userAddr,
            order.takerAssetAddr,
            order.takerAssetAmount,
            order.makerAddr,
            order.makerAssetAddr,
            order.makerAssetAmount,
            order.receiverAddr,
            txMetaData.settleAmount,
            txMetaData.receivedAmount,
            txMetaData.feeFactor,
            txMetaData.subsidyFactor
        );

        return txMetaData.settleAmount;
    }

    /**
     * @dev internal function of `trade`.
     * Used to tell if maker is Curve.
     */
    function _isCurve(address _makerAddr) virtual internal pure returns (bool) {
        if (
            _makerAddr == UNISWAP_V2_ROUTER_02_ADDRESS ||
            _makerAddr == SUSHISWAP_ROUTER_ADDRESS
        ) return false;
        else return true;
    }

    /**
     * @dev internal function of `trade`.
     * Used to tell if internal asset is ETH.
     */
    function _isInternalAssetETH(address _internalAssetAddr) internal pure returns (bool) {
        if (_internalAssetAddr == ETH_ADDRESS || _internalAssetAddr == ZERO_ADDRESS) return true;
        else return false;
    }

    /**
     * @dev internal function of `trade`.
     * Get this contract's eth balance or token balance.
     */
    function _getSelfBalance(address _makerAssetInternalAddr) internal view returns (uint256) {
        if (_isInternalAssetETH(_makerAssetInternalAddr)) {
            return address(this).balance;
        } else {
            return IERC20(_makerAssetInternalAddr).balanceOf(address(this));
        }
    }

    /**
     * @dev internal function of `trade`.
     * It verifies user signature and store tx hash to prevent replay attack.
     */
    function _verify(
        Order memory _order,
        bytes calldata _sig
    ) internal returns (bytes32 transactionHash) {
        // Verify user signature
        // TRADE_WITH_PERMIT_TYPEHASH = keccak256("tradeWithPermit(address makerAddr,address takerAssetAddr,address makerAssetAddr,uint256 takerAssetAmount,uint256 makerAssetAmount,address userAddr,address receiverAddr,uint256 salt,uint256 deadline)");
        transactionHash = keccak256(
            abi.encode(
                TRADE_WITH_PERMIT_TYPEHASH,
                _order.makerAddr,
                _order.takerAssetAddr,
                _order.makerAssetAddr,
                _order.takerAssetAmount,
                _order.makerAssetAmount,
                _order.userAddr,
                _order.receiverAddr,
                _order.salt,
                _order.deadline
            )
        );
        bytes32 EIP712SignDigest = keccak256(
            abi.encodePacked(
                EIP191_HEADER,
                EIP712_DOMAIN_SEPARATOR,
                transactionHash
            )
        );
        require(isValidSignature(_order.userAddr, EIP712SignDigest, bytes(""), _sig), "AMMWrapper: invalid user signature");
        // Set transaction as seen, PermanentStorage would throw error if transaction already seen.
        permStorage.setAMMTransactionSeen(transactionHash);
    }

    /**
     * @dev internal function of `trade`.
     * It executes the swap on chosen AMM.
     */
    function _prepare(Order memory _order, InternalTxData memory _internalTxData) internal {
        // Transfer asset from user and deposit to weth if needed
        if (_internalTxData.fromEth) {
            require(msg.value > 0, "AMMWrapper: msg.value is zero");
            require(_order.takerAssetAmount == msg.value, "AMMWrapper: msg.value doesn't match");
            // Deposit ETH to WETH if internal asset is WETH instead of ETH
            if (! _isInternalAssetETH(_internalTxData.takerAssetInternalAddr)) {
                weth.deposit{value: msg.value}();
            }
        } else {
            // other ERC20 tokens
            spender.spendFromUser(_order.userAddr, _order.takerAssetAddr, _order.takerAssetAmount);
        }
    }

    /**
     * @dev internal function of `trade`.
     * It executes the swap on chosen AMM.
     */
    function _swap(
        Order memory _order,
        TxMetaData memory _txMetaData,
        InternalTxData memory _internalTxData
    )
        internal
        approveTakerAsset(_internalTxData.takerAssetInternalAddr, _order.makerAddr)
        returns (string memory source, uint256 receivedAmount)
    {
        // Swap
        // minAmount = makerAssetAmount * (10000 - subsidyFactor) / 10000
        uint256 minAmount = _order.makerAssetAmount.mul((BPS_MAX.sub(_txMetaData.subsidyFactor))).div(BPS_MAX);

        if (_order.makerAddr == UNISWAP_V2_ROUTER_02_ADDRESS ||
            _order.makerAddr == SUSHISWAP_ROUTER_ADDRESS) {
            source = (_order.makerAddr == SUSHISWAP_ROUTER_ADDRESS) ? "SushiSwap" : "Uniswap V2";
            // Sushiswap shares the same interface as Uniswap's
            receivedAmount = _tradeUniswapV2TokenToToken(
                _order.makerAddr,
                _internalTxData.takerAssetInternalAddr,
                _internalTxData.makerAssetInternalAddr,
                _order.takerAssetAmount,
                minAmount,
                _order.deadline
            );
        } else {
            CurveData memory curveData;
            (
                curveData.fromTokenCurveIndex,
                curveData.toTokenCurveIndex,
                curveData.swapMethod,
            ) = permStorage.getCurvePoolInfo(
                _order.makerAddr,
                _internalTxData.takerAssetInternalAddr,
                _internalTxData.makerAssetInternalAddr
            );
            require(curveData.swapMethod != 0, "AMMWrapper: swap method not registered");
            if (curveData.fromTokenCurveIndex > 0 && curveData.toTokenCurveIndex > 0) {
                source = "Curve";
                // Substract index by 1 because indices stored in `permStorage` starts from 1
                curveData.fromTokenCurveIndex = curveData.fromTokenCurveIndex - 1;
                curveData.toTokenCurveIndex = curveData.toTokenCurveIndex - 1;
                // Curve does not return amount swapped so we need to record balance change instead.
                uint256 balanceBeforeTrade = _getSelfBalance(_internalTxData.makerAssetInternalAddr);
                _tradeCurveTokenToToken(
                    _order.makerAddr,
                    curveData.fromTokenCurveIndex,
                    curveData.toTokenCurveIndex,
                    _order.takerAssetAmount,
                    minAmount,
                    curveData.swapMethod
                );
                uint256 balanceAfterTrade = _getSelfBalance(_internalTxData.makerAssetInternalAddr);
                receivedAmount = balanceAfterTrade.sub(balanceBeforeTrade);
            } else {
                revert("AMMWrapper: unsupported makerAddr");
            }
        }
    }

    /**
     * @dev internal function of `trade`.
     * It collects fee from the trade or compensates the trade based on the actual amount swapped.
     */
    function _settle(
        Order memory _order,
        TxMetaData memory _txMetaData,
        InternalTxData memory _internalTxData
    )
        internal
        returns (uint256 settleAmount)
    {
        // Convert var type from uint16 to uint256
        uint256 _feeFactor = _txMetaData.feeFactor;
        uint256 _subsidyFactor = _txMetaData.subsidyFactor;

        if (_txMetaData.receivedAmount == _order.makerAssetAmount) {
            settleAmount = _txMetaData.receivedAmount;
        } else if (_txMetaData.receivedAmount > _order.makerAssetAmount) {
            // shouldCollectFee = ((receivedAmount - makerAssetAmount) / receivedAmount) > (feeFactor / 10000)
            bool shouldCollectFee = _txMetaData.receivedAmount.sub(_order.makerAssetAmount).mul(BPS_MAX) > _feeFactor.mul(_txMetaData.receivedAmount);
            if (shouldCollectFee) {
                // settleAmount = receivedAmount * (1 - feeFactor) / 10000
                settleAmount = _txMetaData.receivedAmount.mul(BPS_MAX.sub(_feeFactor)).div(BPS_MAX);
            } else {
                settleAmount = _order.makerAssetAmount;
            }
        } else {
            require(_subsidyFactor > 0, "AMMWrapper: this trade will not be subsidized");

            // If fee factor is smaller than subsidy factor, choose fee factor as actual subsidy factor
            // since we should subsidize less if we charge less.
            uint256 actualSubsidyFactor = (_subsidyFactor < _feeFactor) ? _subsidyFactor : _feeFactor;

            // inSubsidyRange = ((makerAssetAmount - receivedAmount) / receivedAmount) > (actualSubsidyFactor / 10000)
            bool inSubsidyRange = _order.makerAssetAmount.sub(_txMetaData.receivedAmount).mul(BPS_MAX) <= actualSubsidyFactor.mul(_txMetaData.receivedAmount);
            require(inSubsidyRange, "AMMWrapper: amount difference larger than subsidy amount");

            uint256 selfBalance = _getSelfBalance(_internalTxData.makerAssetInternalAddr);
            bool hasEnoughToSubsidize = selfBalance >= _order.makerAssetAmount;
            if (! hasEnoughToSubsidize && _isInternalAssetETH(_internalTxData.makerAssetInternalAddr)) {
                // We treat ETH and WETH the same so we have to convert WETH to ETH if ETH balance is not enough.
                uint256 amountShort = _order.makerAssetAmount.sub(selfBalance);
                if (amountShort <= weth.balanceOf(address(this))) {
                    // Withdraw the amount short from WETH
                    weth.withdraw(amountShort);
                    // Now we have enough
                    hasEnoughToSubsidize = true;
                }
            }
            require(hasEnoughToSubsidize, "AMMWrapper: not enough savings to subsidize");

            settleAmount = _order.makerAssetAmount;
        }

        // Transfer token/ETH to receiver
        if (_internalTxData.toEth) {
            // Withdraw from WETH if internal maker asset is WETH
            if (! _isInternalAssetETH(_internalTxData.makerAssetInternalAddr)) {
                weth.withdraw(settleAmount);
            }
            _order.receiverAddr.transfer(settleAmount);
        } else {
            // other ERC20 tokens
            IERC20(_order.makerAssetAddr).safeTransfer(_order.receiverAddr, settleAmount);
        }
    }

    function _tradeCurveTokenToToken(
        address _makerAddr,
        int128 i,
        int128 j,
        uint256 _takerAssetAmount,
        uint256 _makerAssetAmount,
        uint16 swapMethod
    ) 
        internal
    {
        ICurveFi curve = ICurveFi(_makerAddr);
        if (swapMethod == 1) {
            curve.exchange{value: msg.value}(i, j, _takerAssetAmount, _makerAssetAmount);
        } else if (swapMethod == 2) {
            curve.exchange_underlying{value: msg.value}(i, j, _takerAssetAmount, _makerAssetAmount);
        }
    }

    function _tradeUniswapV2TokenToToken(
        address _makerAddr,
        address _takerAssetAddr,
        address _makerAssetAddr,
        uint256 _takerAssetAmount,
        uint256 _makerAssetAmount,
        uint256 _deadline
    )
        internal 
        returns (uint256) 
    {
        IUniswapRouterV2 router = IUniswapRouterV2(_makerAddr);
        address[] memory path = new address[](2);
        path[0] = _takerAssetAddr;
        path[1] = _makerAssetAddr;
        uint256[] memory amounts = router.swapExactTokensForTokens(
            _takerAssetAmount,
            _makerAssetAmount,
            path,
            address(this),
            _deadline
        );
        return amounts[1];
    }
}

File 41 of 57 : IUniswapRouterV2.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.5.0 <0.8.0;

interface IUniswapRouterV2 {
    function swapExactTokensForTokens(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external returns (uint256[] memory amounts);

    function addLiquidity(
        address tokenA,
        address tokenB,
        uint256 amountADesired,
        uint256 amountBDesired,
        uint256 amountAMin,
        uint256 amountBMin,
        address to,
        uint256 deadline
    )
        external
        returns (
            uint256 amountA,
            uint256 amountB,
            uint256 liquidity
        );

    function addLiquidityETH(
        address token,
        uint256 amountTokenDesired,
        uint256 amountTokenMin,
        uint256 amountETHMin,
        address to,
        uint256 deadline
    )
        external
        payable
        returns (
            uint256 amountToken,
            uint256 amountETH,
            uint256 liquidity
        );

    function removeLiquidity(
        address tokenA,
        address tokenB,
        uint256 liquidity,
        uint256 amountAMin,
        uint256 amountBMin,
        address to,
        uint256 deadline
    ) external returns (uint256 amountA, uint256 amountB);

    function getAmountsOut(uint256 amountIn, address[] calldata path)
        external
        view
        returns (uint256[] memory amounts);

    function getAmountsIn(uint256 amountOut, address[] calldata path)
        external
        view
        returns (uint256[] memory amounts);

    function swapETHForExactTokens(
        uint256 amountOut,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external payable returns (uint256[] memory amounts);

    function swapExactETHForTokens(
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external payable returns (uint256[] memory amounts);
}

File 42 of 57 : IUniswapV3SwapRouter.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0 <0.8.0;
pragma experimental ABIEncoderV2;

import "./IUniswapV3SwapCallback.sol";

/// @title Router token swapping functionality
/// @notice Functions for swapping tokens via Uniswap V3
interface ISwapRouter is IUniswapV3SwapCallback {
    struct ExactInputSingleParams {
        address tokenIn;
        address tokenOut;
        uint24 fee;
        address recipient;
        uint256 deadline;
        uint256 amountIn;
        uint256 amountOutMinimum;
        uint160 sqrtPriceLimitX96;
    }

    /// @notice Swaps `amountIn` of one token for as much as possible of another token
    /// @param params The parameters necessary for the swap, encoded as `ExactInputSingleParams` in calldata
    /// @return amountOut The amount of the received token
    function exactInputSingle(ExactInputSingleParams calldata params) external payable returns (uint256 amountOut);

    struct ExactInputParams {
        bytes path;
        address recipient;
        uint256 deadline;
        uint256 amountIn;
        uint256 amountOutMinimum;
    }

    /// @notice Swaps `amountIn` of one token for as much as possible of another along the specified path
    /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactInputParams` in calldata
    /// @return amountOut The amount of the received token
    function exactInput(ExactInputParams calldata params) external payable returns (uint256 amountOut);

    struct ExactOutputSingleParams {
        address tokenIn;
        address tokenOut;
        uint24 fee;
        address recipient;
        uint256 deadline;
        uint256 amountOut;
        uint256 amountInMaximum;
        uint160 sqrtPriceLimitX96;
    }

    /// @notice Swaps as little as possible of one token for `amountOut` of another token
    /// @param params The parameters necessary for the swap, encoded as `ExactOutputSingleParams` in calldata
    /// @return amountIn The amount of the input token
    function exactOutputSingle(ExactOutputSingleParams calldata params) external payable returns (uint256 amountIn);

    struct ExactOutputParams {
        bytes path;
        address recipient;
        uint256 deadline;
        uint256 amountOut;
        uint256 amountInMaximum;
    }

    /// @notice Swaps as little as possible of one token for `amountOut` of another along the specified path (reversed)
    /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactOutputParams` in calldata
    /// @return amountIn The amount of the input token
    function exactOutput(ExactOutputParams calldata params) external payable returns (uint256 amountIn);
}

File 43 of 57 : UniswapV3PathLib.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.6.0;

library BytesLib {
    function slice(
        bytes memory _bytes,
        uint256 _start,
        uint256 _length
    ) internal pure returns (bytes memory) {
        require(_length + 31 >= _length, "slice_overflow");
        require(_start + _length >= _start, "slice_overflow");
        require(_bytes.length >= _start + _length, "slice_outOfBounds");

        bytes memory tempBytes;

        assembly {
            switch iszero(_length)
                case 0 {
                    // Get a location of some free memory and store it in tempBytes as
                    // Solidity does for memory variables.
                    tempBytes := mload(0x40)

                    // The first word of the slice result is potentially a partial
                    // word read from the original array. To read it, we calculate
                    // the length of that partial word and start copying that many
                    // bytes into the array. The first word we copy will start with
                    // data we don't care about, but the last `lengthmod` bytes will
                    // land at the beginning of the contents of the new array. When
                    // we're done copying, we overwrite the full first word with
                    // the actual length of the slice.
                    let lengthmod := and(_length, 31)

                    // The multiplication in the next line is necessary
                    // because when slicing multiples of 32 bytes (lengthmod == 0)
                    // the following copy loop was copying the origin's length
                    // and then ending prematurely not copying everything it should.
                    let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))
                    let end := add(mc, _length)

                    for {
                        // The multiplication in the next line has the same exact purpose
                        // as the one above.
                        let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start)
                    } lt(mc, end) {
                        mc := add(mc, 0x20)
                        cc := add(cc, 0x20)
                    } {
                        mstore(mc, mload(cc))
                    }

                    mstore(tempBytes, _length)

                    //update free-memory pointer
                    //allocating the array padded to 32 bytes like the compiler does now
                    mstore(0x40, and(add(mc, 31), not(31)))
                }
                //if we want a zero-length slice let's just return a zero-length array
                default {
                    tempBytes := mload(0x40)
                    //zero out the 32 bytes slice we are about to return
                    //we need to do it because Solidity does not garbage collect
                    mstore(tempBytes, 0)

                    mstore(0x40, add(tempBytes, 0x20))
                }
        }

        return tempBytes;
    }

    function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) {
        require(_start + 20 >= _start, "toAddress_overflow");
        require(_bytes.length >= _start + 20, "toAddress_outOfBounds");
        address tempAddress;

        assembly {
            tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000)
        }

        return tempAddress;
    }

    function toUint24(bytes memory _bytes, uint256 _start) internal pure returns (uint24) {
        require(_start + 3 >= _start, "toUint24_overflow");
        require(_bytes.length >= _start + 3, "toUint24_outOfBounds");
        uint24 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x3), _start))
        }

        return tempUint;
    }
}

/// @title Functions for manipulating path data for multihop swaps
library Path {
    using BytesLib for bytes;

    /// @dev The length of the bytes encoded address
    uint256 private constant ADDR_SIZE = 20;
    /// @dev The length of the bytes encoded fee
    uint256 private constant FEE_SIZE = 3;

    /// @dev The offset of a single token address and pool fee
    uint256 private constant NEXT_OFFSET = ADDR_SIZE + FEE_SIZE;
    /// @dev The offset of an encoded pool key
    uint256 private constant POP_OFFSET = NEXT_OFFSET + ADDR_SIZE;
    /// @dev The minimum length of an encoding that contains 2 or more pools
    uint256 private constant MULTIPLE_POOLS_MIN_LENGTH = POP_OFFSET + NEXT_OFFSET;

    /// @notice Returns true iff the path contains two or more pools
    /// @param path The encoded swap path
    /// @return True if path contains two or more pools, otherwise false
    function hasMultiplePools(bytes memory path) internal pure returns (bool) {
        return path.length >= MULTIPLE_POOLS_MIN_LENGTH;
    }

    /// @notice Decodes the first pool in path
    /// @param path The bytes encoded swap path
    /// @return tokenA The first token of the given pool
    /// @return tokenB The second token of the given pool
    /// @return fee The fee level of the pool
    function decodeFirstPool(bytes memory path)
        internal
        pure
        returns (
            address tokenA,
            address tokenB,
            uint24 fee
        )
    {
        tokenA = path.toAddress(0);
        fee = path.toUint24(ADDR_SIZE);
        tokenB = path.toAddress(NEXT_OFFSET);
    }

    /// @notice Skips a token + fee element from the buffer and returns the remainder
    /// @param path The swap path
    /// @return The remaining token + fee elements in the path
    function skipToken(bytes memory path) internal pure returns (bytes memory) {
        return path.slice(NEXT_OFFSET, path.length - NEXT_OFFSET);
    }
}

File 44 of 57 : IUniswapExchange.sol
pragma solidity >=0.5.0 <0.8.0;

interface IUniswapExchange {
    // Address of ERC20 token sold on this exchange
    function tokenAddress() external view returns (address token);
    // Address of Uniswap Factory
    function factoryAddress() external view returns (address factory);
    // Provide Liquidity
    function addLiquidity(uint256 min_liquidity, uint256 max_tokens, uint256 deadline) external payable returns (uint256);
    function removeLiquidity(uint256 amount, uint256 min_eth, uint256 min_tokens, uint256 deadline) external returns (uint256, uint256);
    // Get Prices
    function getEthToTokenInputPrice(uint256 eth_sold) external view returns (uint256 tokens_bought);
    function getEthToTokenOutputPrice(uint256 tokens_bought) external view returns (uint256 eth_sold);
    function getTokenToEthInputPrice(uint256 tokens_sold) external view returns (uint256 eth_bought);
    function getTokenToEthOutputPrice(uint256 eth_bought) external view returns (uint256 tokens_sold);
    // Trade ETH to ERC20
    function ethToTokenSwapInput(uint256 min_tokens, uint256 deadline) external payable returns (uint256 tokens_bought);
    function ethToTokenTransferInput(uint256 min_tokens, uint256 deadline, address recipient) external payable returns (uint256  tokens_bought);
    function ethToTokenSwapOutput(uint256 tokens_bought, uint256 deadline) external payable returns (uint256  eth_sold);
    function ethToTokenTransferOutput(uint256 tokens_bought, uint256 deadline, address recipient) external payable returns (uint256  eth_sold);
    // Trade ERC20 to ETH
    function tokenToEthSwapInput(uint256 tokens_sold, uint256 min_eth, uint256 deadline) external returns (uint256  eth_bought);
    function tokenToEthTransferInput(uint256 tokens_sold, uint256 min_eth, uint256 deadline, address recipient) external returns (uint256  eth_bought);
    function tokenToEthSwapOutput(uint256 eth_bought, uint256 max_tokens, uint256 deadline) external returns (uint256  tokens_sold);
    function tokenToEthTransferOutput(uint256 eth_bought, uint256 max_tokens, uint256 deadline, address recipient) external returns (uint256  tokens_sold);
    // Trade ERC20 to ERC20
    function tokenToTokenSwapInput(uint256 tokens_sold, uint256 min_tokens_bought, uint256 min_eth_bought, uint256 deadline, address token_addr) external returns (uint256  tokens_bought);
    function tokenToTokenTransferInput(uint256 tokens_sold, uint256 min_tokens_bought, uint256 min_eth_bought, uint256 deadline, address recipient, address token_addr) external returns (uint256  tokens_bought);
    function tokenToTokenSwapOutput(uint256 tokens_bought, uint256 max_tokens_sold, uint256 max_eth_sold, uint256 deadline, address token_addr) external returns (uint256  tokens_sold);
    function tokenToTokenTransferOutput(uint256 tokens_bought, uint256 max_tokens_sold, uint256 max_eth_sold, uint256 deadline, address recipient, address token_addr) external returns (uint256  tokens_sold);
    // Trade ERC20 to Custom Pool
    function tokenToExchangeSwapInput(uint256 tokens_sold, uint256 min_tokens_bought, uint256 min_eth_bought, uint256 deadline, address exchange_addr) external returns (uint256  tokens_bought);
    function tokenToExchangeTransferInput(uint256 tokens_sold, uint256 min_tokens_bought, uint256 min_eth_bought, uint256 deadline, address recipient, address exchange_addr) external returns (uint256  tokens_bought);
    function tokenToExchangeSwapOutput(uint256 tokens_bought, uint256 max_tokens_sold, uint256 max_eth_sold, uint256 deadline, address exchange_addr) external returns (uint256  tokens_sold);
    function tokenToExchangeTransferOutput(uint256 tokens_bought, uint256 max_tokens_sold, uint256 max_eth_sold, uint256 deadline, address recipient, address exchange_addr) external returns (uint256  tokens_sold);
    // ERC20 comaptibility for liquidity tokens
    function name() external view returns (bytes32);
    function symbol() external view returns (bytes32);
    function decimals() external view returns (uint256);
    function transfer(address _to, uint256 _value) external returns (bool);
    function transferFrom(address _from, address _to, uint256 value) external returns (bool);
    function approve(address _spender, uint256 _value) external returns (bool);
    function allowance(address _owner, address _spender) external view returns (uint256);
    function balanceOf(address _owner) external view returns (uint256);
    function totalSupply() external view returns (uint256);
    // Never use
    function setup(address token_addr) external;
}

File 45 of 57 : IUniswapFactory.sol
pragma solidity >=0.5.0 <0.8.0;

interface IUniswapFactory {
    event PairCreated(
        address indexed token0,
        address indexed token1,
        address pair,
        uint256
    );

    function getPair(address tokenA, address tokenB)
        external
        view
        returns (address pair);

    function allPairs(uint256) external view returns (address pair);
    function allPairsLength() external view returns (uint256);
    function feeTo() external view returns (address);
    function feeToSetter() external view returns (address);
    function createPair(address tokenA, address tokenB)
        external
        returns (address pair);

    // Create Exchange
    function createExchange(address token) external returns (address exchange);
    // Get Exchange and Token Info
    function getExchange(address token) external view returns (address exchange);
    function getToken(address exchange) external view returns (address token);
    function getTokenWithId(uint256 tokenId) external view returns (address token);
    // Never use
    function initializeFactory(address template) external;
}

File 46 of 57 : ICurveFi.sol
pragma solidity >=0.5.0 <0.8.0;

interface ICurveFi {
    function get_virtual_price() external returns (uint256 out);
    function add_liquidity(
        uint256[2] calldata amounts,
        uint256 deadline
    ) external;

    function add_liquidity(
        // sBTC pool
        uint256[3] calldata amounts,
        uint256 min_mint_amount
    ) external;

    function add_liquidity(
        // bUSD pool
        uint256[4] calldata amounts,
        uint256 min_mint_amount
    ) external;

    function get_dx(
        int128 i,
        int128 j,
        uint256 dy
    ) external view returns (uint256 out);

    function get_dx_underlying(
        int128 i,
        int128 j,
        uint256 dy
    ) external view returns (uint256 out);

    function get_dy(
        int128 i,
        int128 j,
        uint256 dx
    ) external view returns (uint256 out);

    function get_dy_underlying(
        int128 i,
        int128 j,
        uint256 dx
    ) external view returns (uint256 out);

    function exchange(
        int128 i,
        int128 j,
        uint256 dx,
        uint256 min_dy
    ) external payable;

    function exchange(
        int128 i,
        int128 j,
        uint256 dx,
        uint256 min_dy,
        uint256 deadline
    ) external payable;

    function exchange_underlying(
        int128 i,
        int128 j,
        uint256 dx,
        uint256 min_dy
    ) external payable;

    function exchange_underlying(
        int128 i,
        int128 j,
        uint256 dx,
        uint256 min_dy,
        uint256 deadline
    ) external payable;

    function remove_liquidity(
        uint256 _amount,
        uint256 deadline,
        uint256[2] calldata min_amounts
    ) external;

    function remove_liquidity_imbalance(
        uint256[2] calldata amounts,
        uint256 deadline
    ) external;

    function remove_liquidity_imbalance(
        uint256[3] calldata amounts,
        uint256 max_burn_amount
    ) external;

    function remove_liquidity(uint256 _amount, uint256[3] calldata amounts)
        external;

    function remove_liquidity_imbalance(
        uint256[4] calldata amounts,
        uint256 max_burn_amount
    ) external;

    function remove_liquidity(uint256 _amount, uint256[4] calldata amounts)
        external;

    function commit_new_parameters(
        int128 amplification,
        int128 new_fee,
        int128 new_admin_fee
    ) external;

    function apply_new_parameters() external;

    function revert_new_parameters() external;

    function commit_transfer_ownership(address _owner) external;

    function apply_transfer_ownership() external;

    function revert_transfer_ownership() external;

    function withdraw_admin_fees() external;

    function coins(int128 arg0) external returns (address out);

    function underlying_coins(int128 arg0) external returns (address out);

    function balances(int128 arg0) external returns (uint256 out);

    function A() external returns (int128 out);

    function fee() external returns (int128 out);

    function admin_fee() external returns (int128 out);

    function owner() external returns (address out);

    function admin_actions_deadline() external returns (uint256 out);

    function transfer_ownership_deadline() external returns (uint256 out);

    function future_A() external returns (int128 out);

    function future_fee() external returns (int128 out);

    function future_admin_fee() external returns (int128 out);

    function future_owner() external returns (address out);
}

File 47 of 57 : AMMLibEIP712.sol
pragma solidity ^0.6.0;

import "./BaseLibEIP712.sol";

contract AMMLibEIP712 is BaseLibEIP712 {
    /***********************************|
    |             Constants             |
    |__________________________________*/

    struct Order {
        address makerAddr;
        address takerAssetAddr;
        address makerAssetAddr;
        uint256 takerAssetAmount;
        uint256 makerAssetAmount;
        address userAddr;
        address payable receiverAddr;
        uint256 salt;
        uint256 deadline;
    }

    // keccak256("tradeWithPermit(address makerAddr,address takerAssetAddr,address makerAssetAddr,uint256 takerAssetAmount,uint256 makerAssetAmount,address userAddr,address receiverAddr,uint256 salt,uint256 deadline)");
    bytes32 public constant TRADE_WITH_PERMIT_TYPEHASH = keccak256(
        abi.encodePacked(
            "tradeWithPermit(",
            "address makerAddr,",
            "address takerAssetAddr,",
            "address makerAssetAddr,",
            "uint256 takerAssetAmount,",
            "uint256 makerAssetAmount,",
            "address userAddr,",
            "address receiverAddr,",
            "uint256 salt,",
            "uint256 deadline",
            ")"
        )
    );
}

File 48 of 57 : IUniswapV3SwapCallback.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0 <0.8.0;

/// @title Callback for IUniswapV3PoolActions#swap
/// @notice Any contract that calls IUniswapV3PoolActions#swap must implement this interface
interface IUniswapV3SwapCallback {
    /// @notice Called to `msg.sender` after executing a swap via IUniswapV3Pool#swap.
    /// @dev In the implementation you must pay the pool tokens owed for the swap.
    /// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory.
    /// amount0Delta and amount1Delta can both be 0 if no tokens were swapped.
    /// @param amount0Delta The amount of token0 that was sent (negative) or must be received (positive) by the pool by
    /// the end of the swap. If positive, the callback must send that amount of token0 to the pool.
    /// @param amount1Delta The amount of token1 that was sent (negative) or must be received (positive) by the pool by
    /// the end of the swap. If positive, the callback must send that amount of token1 to the pool.
    /// @param data Any data passed through by the caller via the IUniswapV3PoolActions#swap call
    function uniswapV3SwapCallback(
        int256 amount0Delta,
        int256 amount1Delta,
        bytes calldata data
    ) external;
}

File 49 of 57 : MarketMakerProxy.sol
pragma solidity ^0.6.0;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import "./interfaces/IWeth.sol";
import "./pmm/mmp/Ownable.sol";
import "./pmm/0xLibs/LibDecoder.sol";

interface IIMBTC {
    function burn(uint256 amount, bytes calldata data) external;
}

interface IWBTC {
    function burn(uint256 value) external;
}

contract MarketMakerProxy is 
    Ownable,
    LibDecoder
{
    using SafeERC20 for IERC20;

    string public constant version = "5.0.0";
    uint256 constant MAX_UINT = 2**256 - 1;
    address public SIGNER;

    // auto withdraw weth to eth
    address public WETH_ADDR;
    address public withdrawer;
    mapping (address => bool) public isWithdrawWhitelist;

    modifier onlyWithdrawer() {
        require(
            msg.sender == withdrawer,
            "MarketMakerProxy: only contract withdrawer"
        );
        _;
    }

    constructor () public {
        owner = msg.sender;
        operator = msg.sender;
    }

    receive() external payable {}

    // Manage
    function setSigner(address _signer) public onlyOperator {
        SIGNER = _signer;
    }

    function setConfig(address _weth) public onlyOperator {
        WETH_ADDR = _weth;
    }

    function setWithdrawer(address _withdrawer) public onlyOperator {
        withdrawer = _withdrawer;
    }

    function setAllowance(address[] memory token_addrs, address spender) public onlyOperator {
        for (uint i = 0; i < token_addrs.length; i++) {
            address token = token_addrs[i];
            IERC20(token).safeApprove(spender, MAX_UINT);
        }
    }

    function closeAllowance(address[] memory token_addrs, address spender) public onlyOperator {
        for (uint i = 0; i < token_addrs.length; i++) {
            address token = token_addrs[i];
            IERC20(token).safeApprove(spender, 0);
        }
    }

    function registerWithdrawWhitelist(address _addr, bool _add) public onlyOperator {
        isWithdrawWhitelist[_addr] = _add;
    }

    function withdraw(address token, address payable to, uint256 amount) public onlyWithdrawer {
        require(
            isWithdrawWhitelist[to],
            "MarketMakerProxy: not in withdraw whitelist"
        );
        if(token == WETH_ADDR) {
            IWETH(WETH_ADDR).withdraw(amount);
            to.transfer(amount);
        } else {
            IERC20(token).safeTransfer(to , amount);
        }
    }

    function withdrawETH(address payable to, uint256 amount) public onlyWithdrawer {
        require(
            isWithdrawWhitelist[to],
            "MarketMakerProxy: not in withdraw whitelist"
        );
        to.transfer(amount);
    }


    function isValidSignature(bytes32 orderHash, bytes memory signature) public view returns (bytes32) {
        require(
            SIGNER == _ecrecoverAddress(orderHash, signature),
            "MarketMakerProxy: invalid signature"
        );
        return keccak256("isValidWalletSignature(bytes32,address,bytes)");
    }

    function _ecrecoverAddress(bytes32 orderHash, bytes memory signature) internal pure returns (address) {
        (uint8 v, bytes32 r, bytes32 s) = decodeMmSignature(signature);
        return ecrecover(
            keccak256(
                abi.encodePacked(
                    "\x19Ethereum Signed Message:\n32",
                    orderHash
                )),
            v, r, s
        );
    }
}

File 50 of 57 : Ownable.sol
pragma solidity ^0.6.0;

contract Ownable {
  address public owner;
  address public operator;

  constructor ()
    public
  {
    owner = msg.sender;
  }

  modifier onlyOwner() {
    require(
      msg.sender == owner,
      "Ownable: only contract owner"
    );
    _;
  }

  modifier onlyOperator() {
    require(
      msg.sender == operator,
      "Ownable: only contract operator"
    );
    _;
  }

  function transferOwnership(address newOwner)
    public
    onlyOwner
  {
    if (newOwner != address(0)) {
      owner = newOwner;
    }
  }

  function setOperator(address newOperator)
    public
    onlyOwner 
  {
    operator = newOperator;
  }
}

File 51 of 57 : AMMQuoter.sol
pragma solidity ^0.6.0;
pragma experimental ABIEncoderV2;

import "@openzeppelin/contracts/math/SafeMath.sol";
import "./interfaces/IUniswapExchange.sol";
import "./interfaces/IUniswapFactory.sol";
import "./interfaces/IUniswapRouterV2.sol";
import "./interfaces/ICurveFi.sol";
import "./interfaces/IWeth.sol";
import "./interfaces/IPermanentStorage.sol";
import "./interfaces/IUniswapV3Quoter.sol";
import "./utils/LibBytes.sol";

/// This contract is designed to be called off-chain.
/// At T1, 4 requests would be made in order to get quote, which is for Uniswap v2, v3, Sushiswap and others.
/// For those source without path design, we can find best out amount in this contract.
/// For Uniswap and Sushiswap, best path would be calculated off-chain, we only verify out amount in this contract.

contract AMMQuoter {
    using SafeMath for uint256;
    using LibBytes for bytes;

    /* Constants */
    string public constant version = "5.2.0";
    address private constant ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
    address private constant ZERO_ADDRESS = address(0);
    address public constant UNISWAP_V2_ROUTER_02_ADDRESS = 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D;
    address public constant UNISWAP_V3_ROUTER_ADDRESS = 0xE592427A0AEce92De3Edee1F18E0157C05861564;
    address public constant UNISWAP_V3_QUOTER_ADDRESS = 0xb27308f9F90D607463bb33eA1BeBb41C27CE5AB6;
    address public constant SUSHISWAP_ROUTER_ADDRESS = 0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F;
    address public immutable weth;
    IPermanentStorage public immutable permStorage;

    struct GroupedVars {
        address makerAddr;
        address takerAssetAddr;
        address makerAssetAddr;
        uint256 takerAssetAmount;
        uint256 makerAssetAmount;
        address[] path;
    }

    event CurveTokenAdded(
        address indexed makerAddress,
        address indexed assetAddress,
        int128 index
    );

    constructor (IPermanentStorage _permStorage, address _weth) public {
        permStorage = _permStorage;
        weth = _weth;
    }

    function isETH(address assetAddress) public pure returns (bool) {
        return (assetAddress == ZERO_ADDRESS || assetAddress == ETH_ADDRESS);
    }

    function getMakerOutAmountWithPath(
        address _makerAddr,
        address _takerAssetAddr,
        address _makerAssetAddr,
        uint256 _takerAssetAmount,
        address[] calldata _path,
        bytes memory _makerSpecificData
    )
        public
        returns (uint256 makerAssetAmount)
    {
        GroupedVars memory vars;
        vars.makerAddr = _makerAddr;
        vars.takerAssetAddr = _takerAssetAddr;
        vars.makerAssetAddr = _makerAssetAddr;
        vars.takerAssetAmount = _takerAssetAmount;
        vars.path = _path;
        if (vars.makerAddr == UNISWAP_V2_ROUTER_02_ADDRESS ||
            vars.makerAddr == SUSHISWAP_ROUTER_ADDRESS) {
            IUniswapRouterV2 router = IUniswapRouterV2(vars.makerAddr);
            uint256[] memory amounts = router.getAmountsOut(vars.takerAssetAmount, vars.path);
            makerAssetAmount = amounts[amounts.length-1];
        } else if (vars.makerAddr == UNISWAP_V3_ROUTER_ADDRESS) {
            IUniswapV3Quoter quoter = IUniswapV3Quoter(UNISWAP_V3_QUOTER_ADDRESS);
            // swapType:
            // 1: exactInputSingle, 2: exactInput, 3: exactOuputSingle, 4: exactOutput
            uint8 swapType = uint8(uint256(_makerSpecificData.readBytes32(0)));
            if (swapType == 1) {
                address v3TakerInternalAsset = isETH(vars.takerAssetAddr) ? weth : vars.takerAssetAddr;
                address v3MakerInternalAsset = isETH(vars.makerAssetAddr) ? weth : vars.makerAssetAddr;
                (, uint24 poolFee) = abi.decode(_makerSpecificData, (uint8, uint24));
                makerAssetAmount = quoter.quoteExactInputSingle(v3TakerInternalAsset, v3MakerInternalAsset, poolFee, vars.takerAssetAmount, 0);
            } else if (swapType == 2) {
                (, bytes memory path) = abi.decode(_makerSpecificData, (uint8, bytes));
                makerAssetAmount = quoter.quoteExactInput(path, vars.takerAssetAmount);
            } else {
                revert("AMMQuoter: Invalid UniswapV3 swap type");
            }
        } else {
            address curveTakerIntenalAsset = isETH(vars.takerAssetAddr) ? ETH_ADDRESS : vars.takerAssetAddr;
            address curveMakerIntenalAsset = isETH(vars.makerAssetAddr) ? ETH_ADDRESS : vars.makerAssetAddr;
            (int128 fromTokenCurveIndex, int128 toTokenCurveIndex, uint16 swapMethod,) = permStorage.getCurvePoolInfo(vars.makerAddr, curveTakerIntenalAsset, curveMakerIntenalAsset);
            if (fromTokenCurveIndex > 0 && toTokenCurveIndex > 0) {
                require(swapMethod != 0, "AMMQuoter: swap method not registered");
                // Substract index by 1 because indices stored in `permStorage` starts from 1
                fromTokenCurveIndex = fromTokenCurveIndex - 1;
                toTokenCurveIndex = toTokenCurveIndex - 1;
                ICurveFi curve = ICurveFi(vars.makerAddr);
                if (swapMethod == 1) {
                    makerAssetAmount = curve.get_dy(fromTokenCurveIndex, toTokenCurveIndex, vars.takerAssetAmount).sub(1);
                } else if (swapMethod == 2) {
                    makerAssetAmount = curve.get_dy_underlying(fromTokenCurveIndex, toTokenCurveIndex, vars.takerAssetAmount).sub(1);
                }
            } else {
                revert("AMMQuoter: Unsupported makerAddr");
            }
        }
        return makerAssetAmount;
    }

    function getMakerOutAmount(
        address _makerAddr,
        address _takerAssetAddr,
        address _makerAssetAddr,
        uint256 _takerAssetAmount
    )
        public
        view
        returns (uint256)
    {
        uint256 makerAssetAmount;
        if (_makerAddr == UNISWAP_V2_ROUTER_02_ADDRESS ||
            _makerAddr == SUSHISWAP_ROUTER_ADDRESS) {
            IUniswapRouterV2 router = IUniswapRouterV2(_makerAddr);
            address[] memory path = new address[](2);
            if (isETH(_takerAssetAddr)) {
                path[0] = weth;
                path[1] = _makerAssetAddr;
            } else if (isETH(_makerAssetAddr)) {
                path[0] = _takerAssetAddr;
                path[1] = weth;
            } else {
                path[0] = _takerAssetAddr;
                path[1] = _makerAssetAddr;
            }
            uint256[] memory amounts = router.getAmountsOut(_takerAssetAmount, path);
            makerAssetAmount = amounts[1];
        } else {
            address curveTakerIntenalAsset = isETH(_takerAssetAddr) ? ETH_ADDRESS : _takerAssetAddr;
            address curveMakerIntenalAsset = isETH(_makerAssetAddr) ? ETH_ADDRESS : _makerAssetAddr;
            (int128 fromTokenCurveIndex, int128 toTokenCurveIndex, uint16 swapMethod,) = permStorage.getCurvePoolInfo(_makerAddr, curveTakerIntenalAsset, curveMakerIntenalAsset);
            if (fromTokenCurveIndex > 0 && toTokenCurveIndex > 0) {
                require(swapMethod != 0, "AMMQuoter: swap method not registered");
                // Substract index by 1 because indices stored in `permStorage` starts from 1
                fromTokenCurveIndex = fromTokenCurveIndex - 1;
                toTokenCurveIndex = toTokenCurveIndex - 1;
                ICurveFi curve = ICurveFi(_makerAddr);
                if (swapMethod == 1) {
                    makerAssetAmount = curve.get_dy(fromTokenCurveIndex, toTokenCurveIndex, _takerAssetAmount).sub(1);
                } else if (swapMethod == 2) {
                    makerAssetAmount = curve.get_dy_underlying(fromTokenCurveIndex, toTokenCurveIndex, _takerAssetAmount).sub(1);
                }
            } else {
                revert("AMMQuoter: Unsupported makerAddr");
            }
        }
        return makerAssetAmount;
    }

    /// @dev This function is designed for finding best out amount among AMM makers other than Uniswap and Sushiswap
    function getBestOutAmount(
        address[] calldata _makerAddresses,
        address _takerAssetAddr,
        address _makerAssetAddr,
        uint256 _takerAssetAmount
    )
        external
        view
        returns (address bestMaker, uint256 bestAmount)
    {
        bestAmount = 0;
        uint256 poolLength = _makerAddresses.length;
        for (uint256 i = 0; i < poolLength; i++) {
            address makerAddress = _makerAddresses[i];
            uint256 makerAssetAmount = getMakerOutAmount(makerAddress, _takerAssetAddr, _makerAssetAddr, _takerAssetAmount);
            if (makerAssetAmount > bestAmount) {
                bestAmount = makerAssetAmount;
                bestMaker = makerAddress;
            }
        }
        return (bestMaker, bestAmount);
    }

    function getTakerInAmountWithPath(
        address _makerAddr,
        address _takerAssetAddr,
        address _makerAssetAddr,
        uint256 _makerAssetAmount,
        address[] calldata _path,
        bytes memory _makerSpecificData
    )
        public
        returns (uint256 takerAssetAmount)
    {
        GroupedVars memory vars;
        vars.makerAddr = _makerAddr;
        vars.takerAssetAddr = _takerAssetAddr;
        vars.makerAssetAddr = _makerAssetAddr;
        vars.makerAssetAmount = _makerAssetAmount;
        vars.path = _path;
        if (vars.makerAddr == UNISWAP_V2_ROUTER_02_ADDRESS ||
            vars.makerAddr == SUSHISWAP_ROUTER_ADDRESS) {
            IUniswapRouterV2 router = IUniswapRouterV2(vars.makerAddr);
            uint256[] memory amounts = router.getAmountsIn(vars.makerAssetAmount, _path);
            takerAssetAmount = amounts[0];
        } else if (vars.makerAddr == UNISWAP_V3_ROUTER_ADDRESS) {
            IUniswapV3Quoter quoter = IUniswapV3Quoter(UNISWAP_V3_QUOTER_ADDRESS);
            // swapType:
            // 1: exactInputSingle, 2: exactInput, 3: exactOuputSingle, 4: exactOutput
            uint8 swapType = uint8(uint256(_makerSpecificData.readBytes32(0)));
            if (swapType == 3) {
                address v3TakerInternalAsset = isETH(vars.takerAssetAddr) ? weth : vars.takerAssetAddr;
                address v3MakerInternalAsset = isETH(vars.makerAssetAddr) ? weth : vars.makerAssetAddr;
                (, uint24 poolFee) = abi.decode(_makerSpecificData, (uint8, uint24));
                takerAssetAmount = quoter.quoteExactOutputSingle(v3TakerInternalAsset, v3MakerInternalAsset, poolFee, vars.makerAssetAmount, 0);
            } else if (swapType == 4) {
                (, bytes memory path) = abi.decode(_makerSpecificData, (uint8, bytes));
                takerAssetAmount = quoter.quoteExactOutput(path, vars.makerAssetAmount);
            } else {
                revert("AMMQuoter: Invalid UniswapV3 swap type");
            }
        } else {
            address curveTakerIntenalAsset = isETH(vars.takerAssetAddr) ? ETH_ADDRESS : vars.takerAssetAddr;
            address curveMakerIntenalAsset = isETH(vars.makerAssetAddr) ? ETH_ADDRESS : vars.makerAssetAddr;
            (int128 fromTokenCurveIndex, int128 toTokenCurveIndex, uint16 swapMethod, bool supportGetDx) = permStorage.getCurvePoolInfo(vars.makerAddr, curveTakerIntenalAsset, curveMakerIntenalAsset);
            if (fromTokenCurveIndex > 0 && toTokenCurveIndex > 0) {
                require(swapMethod != 0, "AMMQuoter: swap method not registered");
                // Substract index by 1 because indices stored in `permStorage` starts from 1
                fromTokenCurveIndex = fromTokenCurveIndex - 1;
                toTokenCurveIndex = toTokenCurveIndex - 1;
                ICurveFi curve = ICurveFi(vars.makerAddr);
                if (supportGetDx) {
                    if (swapMethod == 1) {
                        takerAssetAmount = curve.get_dx(fromTokenCurveIndex, toTokenCurveIndex, vars.makerAssetAmount);
                    } else if (swapMethod == 2) {
                        takerAssetAmount = curve.get_dx_underlying(fromTokenCurveIndex, toTokenCurveIndex, vars.makerAssetAmount);
                    }
                } else {
                    if (swapMethod == 1) {
                        // does not support get_dx_underlying, try to get an estimated rate here
                        takerAssetAmount = curve.get_dy(toTokenCurveIndex, fromTokenCurveIndex, vars.makerAssetAmount);
                    } else if (swapMethod == 2) {
                        takerAssetAmount = curve.get_dy_underlying(toTokenCurveIndex, fromTokenCurveIndex, vars.makerAssetAmount);
                    }
                }
            } else {
                revert("AMMQuoter: Unsupported makerAddr");
            }
        }
        return takerAssetAmount;
    }

    function getTakerInAmount(
        address _makerAddr,
        address _takerAssetAddr,
        address _makerAssetAddr,
        uint256 _makerAssetAmount
    )
        public
        view
        returns (uint256)
    {
        uint256 takerAssetAmount;
        if (_makerAddr == UNISWAP_V2_ROUTER_02_ADDRESS ||
            _makerAddr == SUSHISWAP_ROUTER_ADDRESS) {
            IUniswapRouterV2 router = IUniswapRouterV2(_makerAddr);
            address[] memory path = new address[](2);
            if (isETH(_takerAssetAddr)) {
                path[0] = weth;
                path[1] = _makerAssetAddr;
            } else if (isETH(_makerAssetAddr)) {
                path[0] = _takerAssetAddr;
                path[1] = weth;
            } else {
                path[0] = _takerAssetAddr;
                path[1] = _makerAssetAddr;
            }
            uint256[] memory amounts = router.getAmountsIn(_makerAssetAmount, path);
            takerAssetAmount = amounts[0];
        } else {
            address curveTakerIntenalAsset = isETH(_takerAssetAddr) ? ETH_ADDRESS : _takerAssetAddr;
            address curveMakerIntenalAsset = isETH(_makerAssetAddr) ? ETH_ADDRESS : _makerAssetAddr;
            (int128 fromTokenCurveIndex, int128 toTokenCurveIndex, uint16 swapMethod, bool supportGetDx) = permStorage.getCurvePoolInfo(_makerAddr, curveTakerIntenalAsset, curveMakerIntenalAsset);
            if (fromTokenCurveIndex > 0 && toTokenCurveIndex > 0) {
                require(swapMethod != 0, "AMMQuoter: swap method not registered");
                // Substract index by 1 because indices stored in `permStorage` starts from 1
                fromTokenCurveIndex = fromTokenCurveIndex - 1;
                toTokenCurveIndex = toTokenCurveIndex - 1;
                ICurveFi curve = ICurveFi(_makerAddr);
                if (supportGetDx) {
                    if (swapMethod == 1) {
                        takerAssetAmount = curve.get_dx(fromTokenCurveIndex, toTokenCurveIndex, _makerAssetAmount);
                    } else if (swapMethod == 2) {
                        takerAssetAmount = curve.get_dx_underlying(fromTokenCurveIndex, toTokenCurveIndex, _makerAssetAmount);
                    }
                } else {
                    if (swapMethod == 1) {
                        // does not support get_dx_underlying, try to get an estimated rate here
                        takerAssetAmount = curve.get_dy(toTokenCurveIndex, fromTokenCurveIndex, _makerAssetAmount);
                    } else if (swapMethod == 2) {
                        takerAssetAmount = curve.get_dy_underlying(toTokenCurveIndex, fromTokenCurveIndex, _makerAssetAmount);
                    }
                }
            } else {
                revert("AMMQuoter: Unsupported makerAddr");
            }
        }
        return takerAssetAmount;
    }

    /// @dev This function is designed for finding best in amount among AMM makers other than Uniswap and Sushiswap
    function getBestInAmount(
        address[] calldata _makerAddresses,
        address _takerAssetAddr,
        address _makerAssetAddr,
        uint256 _makerAssetAmount
    )
        external
        view
        returns (address bestMaker, uint256 bestAmount)
    {
        bestAmount = 2**256 - 1;
        uint256 poolLength = _makerAddresses.length;
        for (uint256 i = 0; i < poolLength; i++) {
            address makerAddress = _makerAddresses[i];
            uint256 takerAssetAmount = getTakerInAmount(makerAddress, _takerAssetAddr, _makerAssetAddr, _makerAssetAmount);
            if (takerAssetAmount < bestAmount) {
                bestAmount = takerAssetAmount;
                bestMaker = makerAddress;
            }
        }
        return (bestMaker, bestAmount);
    }
}

File 52 of 57 : IUniswapV3Quoter.sol
pragma solidity ^0.6.0;
pragma experimental ABIEncoderV2;

/// @title Quoter Interface
/// @notice Supports quoting the calculated amounts from exact input or exact output swaps
/// @dev These functions are not marked view because they rely on calling non-view functions and reverting
/// to compute the result. They are also not gas efficient and should not be called on-chain.
interface IUniswapV3Quoter {
    /// @notice Returns the amount out received for a given exact input swap without executing the swap
    /// @param path The path of the swap, i.e. each token pair and the pool fee
    /// @param amountIn The amount of the first token to swap
    /// @return amountOut The amount of the last token that would be received
    function quoteExactInput(bytes memory path, uint256 amountIn) external returns (uint256 amountOut);

    /// @notice Returns the amount out received for a given exact input but for a swap of a single pool
    /// @param tokenIn The token being swapped in
    /// @param tokenOut The token being swapped out
    /// @param fee The fee of the token pool to consider for the pair
    /// @param amountIn The desired input amount
    /// @param sqrtPriceLimitX96 The price limit of the pool that cannot be exceeded by the swap
    /// @return amountOut The amount of `tokenOut` that would be received
    function quoteExactInputSingle(
        address tokenIn,
        address tokenOut,
        uint24 fee,
        uint256 amountIn,
        uint160 sqrtPriceLimitX96
    ) external returns (uint256 amountOut);

    /// @notice Returns the amount in required for a given exact output swap without executing the swap
    /// @param path The path of the swap, i.e. each token pair and the pool fee. Path must be provided in reverse order
    /// @param amountOut The amount of the last token to receive
    /// @return amountIn The amount of first token required to be paid
    function quoteExactOutput(bytes memory path, uint256 amountOut) external returns (uint256 amountIn);

    /// @notice Returns the amount in required to receive the given exact output amount but for a swap of a single pool
    /// @param tokenIn The token being swapped in
    /// @param tokenOut The token being swapped out
    /// @param fee The fee of the token pool to consider for the pair
    /// @param amountOut The desired output amount
    /// @param sqrtPriceLimitX96 The price limit of the pool that cannot be exceeded by the swap
    /// @return amountIn The amount required as the input for the swap in order to receive `amountOut`
    function quoteExactOutputSingle(
        address tokenIn,
        address tokenOut,
        uint24 fee,
        uint256 amountOut,
        uint160 sqrtPriceLimitX96
    ) external returns (uint256 amountIn);
}

File 53 of 57 : SpenderSimulation.sol
pragma solidity ^0.6.0;

import "./interfaces/IHasBlackListERC20Token.sol";
import "./interfaces/ISpender.sol";

contract SpenderSimulation {
    ISpender public immutable spender;

    mapping(address => bool) public hasBlackListERC20Tokens;

    modifier checkBlackList(address _tokenAddr, address _user) {
        if (hasBlackListERC20Tokens[_tokenAddr]) {
            IHasBlackListERC20Token hasBlackListERC20Token = IHasBlackListERC20Token(_tokenAddr);
            require(!hasBlackListERC20Token.isBlackListed(_user), "SpenderSimulation: user in token's blacklist");
        }
        _;
    }

    /************************************************************
    *                       Constructor                         *
    *************************************************************/
    constructor (ISpender _spender, address[] memory _hasBlackListERC20Tokens) public {
        spender = _spender;

        for (uint256 i = 0; i < _hasBlackListERC20Tokens.length; i++) {
            hasBlackListERC20Tokens[_hasBlackListERC20Tokens[i]] = true;
        }
    }

    /************************************************************
    *                    Helper functions                       *
    *************************************************************/
    /// @dev Spend tokens on user's behalf but reverts if succeed.
    /// This is only intended to be run off-chain to check if the transfer will succeed.
    /// @param _user The user to spend token from.
    /// @param _tokenAddr The address of the token.
    /// @param _amount Amount to spend.
    function simulate(address _user, address _tokenAddr, uint256 _amount) external checkBlackList(_tokenAddr, _user) {
        spender.spendFromUser(_user, _tokenAddr, _amount);

        // All checks passed: revert with success reason string
        revert("SpenderSimulation: transfer simulation success");
    }
}

File 54 of 57 : IHasBlackListERC20Token.sol
pragma solidity ^0.6.0;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

interface IHasBlackListERC20Token is IERC20 {
    function isBlackListed(address user) external returns (bool);
    function addBlackList(address user) external;
    function removeBlackList(address user) external;
}

File 55 of 57 : Tokenlon.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.6.0;

import "./upgrade_proxy/TransparentUpgradeableProxy.sol";

contract Tokenlon is TransparentUpgradeableProxy {
    constructor(address _logic, address _admin, bytes memory _data) public payable TransparentUpgradeableProxy(_logic, _admin, _data) {}
}

File 56 of 57 : TransparentUpgradeableProxy.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

import "./UpgradeableProxy.sol";

/**
 * @dev This contract implements a proxy that is upgradeable by an admin.
 * 
 * To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector
 * clashing], which can potentially be used in an attack, this contract uses the
 * https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two
 * things that go hand in hand:
 * 
 * 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if
 * that call matches one of the admin functions exposed by the proxy itself.
 * 2. If the admin calls the proxy, it can access the admin functions, but its calls will never be forwarded to the
 * implementation. If the admin tries to call a function on the implementation it will fail with an error that says
 * "admin cannot fallback to proxy target".
 * 
 * These properties mean that the admin account can only be used for admin actions like upgrading the proxy or changing
 * the admin, so it's best if it's a dedicated account that is not used for anything else. This will avoid headaches due
 * to sudden errors when trying to call a function from the proxy implementation.
 * 
 * Our recommendation is for the dedicated account to be an instance of the {ProxyAdmin} contract. If set up this way,
 * you should think of the `ProxyAdmin` instance as the real administrative interface of your proxy.
 */
contract TransparentUpgradeableProxy is UpgradeableProxy {
    /**
     * @dev Initializes an upgradeable proxy managed by `_admin`, backed by the implementation at `_logic`, and
     * optionally initialized with `_data` as explained in {UpgradeableProxy-constructor}.
     */
    constructor(address _logic, address _admin, bytes memory _data) public payable UpgradeableProxy(_logic, _data) {
        assert(_ADMIN_SLOT == bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1));
        _setAdmin(_admin);
    }

    /**
     * @dev Emitted when the admin account has changed.
     */
    event AdminChanged(address previousAdmin, address newAdmin);

    /**
     * @dev Storage slot with the admin of the contract.
     * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
     * validated in the constructor.
     */
    bytes32 private constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;

    /**
     * @dev Modifier used internally that will delegate the call to the implementation unless the sender is the admin.
     */
    modifier ifAdmin() {
        if (msg.sender == _admin()) {
            _;
        } else {
            _fallback();
        }
    }

    /**
     * @dev Returns the current admin.
     * 
     * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyAdmin}.
     * 
     * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
     * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
     * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
     */
    function admin() external ifAdmin returns (address) {
        return _admin();
    }

    /**
     * @dev Returns the current implementation.
     * 
     * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyImplementation}.
     * 
     * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
     * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
     * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`
     */
    function implementation() external ifAdmin returns (address) {
        return _implementation();
    }

    /**
     * @dev Changes the admin of the proxy.
     * 
     * Emits an {AdminChanged} event.
     * 
     * NOTE: Only the admin can call this function. See {ProxyAdmin-changeProxyAdmin}.
     */
    function changeAdmin(address newAdmin) external ifAdmin {
        require(newAdmin != address(0), "TransparentUpgradeableProxy: new admin is the zero address");
        emit AdminChanged(_admin(), newAdmin);
        _setAdmin(newAdmin);
    }

    /**
     * @dev Upgrade the implementation of the proxy.
     * 
     * NOTE: Only the admin can call this function. See {ProxyAdmin-upgrade}.
     */
    function upgradeTo(address newImplementation) external ifAdmin {
        _upgradeTo(newImplementation);
    }

    /**
     * @dev Upgrade the implementation of the proxy, and then call a function from the new implementation as specified
     * by `data`, which should be an encoded function call. This is useful to initialize new storage variables in the
     * proxied contract.
     * 
     * NOTE: Only the admin can call this function. See {ProxyAdmin-upgradeAndCall}.
     */
    function upgradeToAndCall(address newImplementation, bytes calldata data) external payable ifAdmin {
        _upgradeTo(newImplementation);
        // solhint-disable-next-line avoid-low-level-calls
        (bool success,) = newImplementation.delegatecall(data);
        require(success);
    }

    /**
     * @dev Returns the current admin.
     */
    function _admin() internal view returns (address adm) {
        bytes32 slot = _ADMIN_SLOT;
        // solhint-disable-next-line no-inline-assembly
        assembly {
            adm := sload(slot)
        }
    }

    /**
     * @dev Stores a new address in the EIP1967 admin slot.
     */
    function _setAdmin(address newAdmin) private {
        bytes32 slot = _ADMIN_SLOT;

        // solhint-disable-next-line no-inline-assembly
        assembly {
            sstore(slot, newAdmin)
        }
    }

    /**
     * @dev Makes sure the admin cannot access the fallback function. See {Proxy-_beforeFallback}.
     */
    function _beforeFallback() internal override virtual {
        require(msg.sender != _admin(), "TransparentUpgradeableProxy: admin cannot fallback to proxy target");
        super._beforeFallback();
    }
}

File 57 of 57 : UpgradeableProxy.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

import "@openzeppelin/contracts/utils/Address.sol";

import "./Proxy.sol";

/**
 * @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an
 * implementation address that can be changed. This address is stored in storage in the location specified by
 * https://eips.ethereum.org/EIPS/eip-1967[EIP1967], so that it doesn't conflict with the storage layout of the
 * implementation behind the proxy.
 * 
 * Upgradeability is only provided internally through {_upgradeTo}. For an externally upgradeable proxy see
 * {TransparentUpgradeableProxy}.
 */
contract UpgradeableProxy is Proxy {
    /**
     * @dev Initializes the upgradeable proxy with an initial implementation specified by `_logic`.
     * 
     * If `_data` is nonempty, it's used as data in a delegate call to `_logic`. This will typically be an encoded
     * function call, and allows initializating the storage of the proxy like a Solidity constructor.
     */
    constructor(address _logic, bytes memory _data) public payable {
        assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1));
        _setImplementation(_logic);
        if(_data.length > 0) {
            // solhint-disable-next-line avoid-low-level-calls
            (bool success,) = _logic.delegatecall(_data);
            require(success);
        }
    }

    /**
     * @dev Emitted when the implementation is upgraded.
     */
    event Upgraded(address indexed implementation);

    /**
     * @dev Storage slot with the address of the current implementation.
     * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
     * validated in the constructor.
     */
    bytes32 private constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;

    /**
     * @dev Returns the current implementation address.
     */
    function _implementation() internal override view returns (address impl) {
        bytes32 slot = _IMPLEMENTATION_SLOT;
        // solhint-disable-next-line no-inline-assembly
        assembly {
            impl := sload(slot)
        }
    }

    /**
     * @dev Upgrades the proxy to a new implementation.
     * 
     * Emits an {Upgraded} event.
     */
    function _upgradeTo(address newImplementation) internal {
        _setImplementation(newImplementation);
        emit Upgraded(newImplementation);
    }

    /**
     * @dev Stores a new address in the EIP1967 implementation slot.
     */
    function _setImplementation(address newImplementation) private {
        require(Address.isContract(newImplementation), "UpgradeableProxy: new implementation is not a contract");

        bytes32 slot = _IMPLEMENTATION_SLOT;

        // solhint-disable-next-line no-inline-assembly
        assembly {
            sstore(slot, newImplementation)
        }
    }
}

File 58 of 57 : Proxy.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

/**
 * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM
 * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to
 * be specified by overriding the virtual {_implementation} function.
 * 
 * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a
 * different contract through the {_delegate} function.
 * 
 * The success and return data of the delegated call will be returned back to the caller of the proxy.
 */
abstract contract Proxy {
    /**
     * @dev Delegates the current call to `implementation`.
     * 
     * This function does not return to its internall call site, it will return directly to the external caller.
     */
    function _delegate(address implementation) internal {
        // solhint-disable-next-line no-inline-assembly
        assembly {
            // Copy msg.data. We take full control of memory in this inline assembly
            // block because it will not return to Solidity code. We overwrite the
            // Solidity scratch pad at memory position 0.
            calldatacopy(0, 0, calldatasize())

            // Call the implementation.
            // out and outsize are 0 because we don't know the size yet.
            let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)

            // Copy the returned data.
            returndatacopy(0, 0, returndatasize())

            switch result
            // delegatecall returns 0 on error.
            case 0 { revert(0, returndatasize()) }
            default { return(0, returndatasize()) }
        }
    }

    /**
     * @dev This is a virtual function that should be overriden so it returns the address to which the fallback function
     * and {_fallback} should delegate.
     */
    function _implementation() internal virtual view returns (address);

    /**
     * @dev Delegates the current call to the address returned by `_implementation()`.
     * 
     * This function does not return to its internall call site, it will return directly to the external caller.
     */
    function _fallback() internal {
        _beforeFallback();
        _delegate(_implementation());
    }

    /**
     * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other
     * function in the contract matches the call data.
     */
    fallback () payable external {
        _fallback();
    }

    /**
     * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data
     * is empty.
     */
    receive () payable external {
        _fallback();
    }

    /**
     * @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback`
     * call, or as part of the Solidity `fallback` or `receive` functions.
     * 
     * If overriden should call `super._beforeFallback()`.
     */
    function _beforeFallback() internal virtual {
    }
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 1000
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "abi"
      ]
    }
  },
  "metadata": {
    "useLiteralContent": true
  },
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"_operator","type":"address"},{"internalType":"uint256","name":"_subsidyFactor","type":"uint256"},{"internalType":"address","name":"_userProxy","type":"address"},{"internalType":"contract ISpender","name":"_spender","type":"address"},{"internalType":"contract IPermanentStorage","name":"_permStorage","type":"address"},{"internalType":"contract IWETH","name":"_weth","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"spender","type":"address"}],"name":"AllowTransfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"ethBalance","type":"uint256"}],"name":"DepositETH","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"spender","type":"address"}],"name":"DisallowTransfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newSubisdyFactor","type":"uint256"}],"name":"SetSubsidyFactor","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"string","name":"source","type":"string"},{"internalType":"bytes32","name":"transactionHash","type":"bytes32"},{"internalType":"uint256","name":"settleAmount","type":"uint256"},{"internalType":"uint256","name":"receivedAmount","type":"uint256"},{"internalType":"uint16","name":"feeFactor","type":"uint16"},{"internalType":"uint16","name":"subsidyFactor","type":"uint16"}],"indexed":false,"internalType":"struct AMMWrapper.TxMetaData","name":"","type":"tuple"},{"components":[{"internalType":"address","name":"makerAddr","type":"address"},{"internalType":"address","name":"takerAssetAddr","type":"address"},{"internalType":"address","name":"makerAssetAddr","type":"address"},{"internalType":"uint256","name":"takerAssetAmount","type":"uint256"},{"internalType":"uint256","name":"makerAssetAmount","type":"uint256"},{"internalType":"address","name":"userAddr","type":"address"},{"internalType":"address payable","name":"receiverAddr","type":"address"},{"internalType":"uint256","name":"salt","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"indexed":false,"internalType":"struct AMMLibEIP712.Order","name":"order","type":"tuple"}],"name":"Swapped","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"source","type":"string"},{"indexed":true,"internalType":"bytes32","name":"transactionHash","type":"bytes32"},{"indexed":true,"internalType":"address","name":"userAddr","type":"address"},{"indexed":false,"internalType":"address","name":"takerAssetAddr","type":"address"},{"indexed":false,"internalType":"uint256","name":"takerAssetAmount","type":"uint256"},{"indexed":false,"internalType":"address","name":"makerAddr","type":"address"},{"indexed":false,"internalType":"address","name":"makerAssetAddr","type":"address"},{"indexed":false,"internalType":"uint256","name":"makerAssetAmount","type":"uint256"},{"indexed":false,"internalType":"address","name":"receiverAddr","type":"address"},{"indexed":false,"internalType":"uint256","name":"settleAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"receivedAmount","type":"uint256"},{"indexed":false,"internalType":"uint16","name":"feeFactor","type":"uint16"},{"indexed":false,"internalType":"uint16","name":"subsidyFactor","type":"uint16"}],"name":"Swapped","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newOperator","type":"address"}],"name":"TransferOwnership","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newSpender","type":"address"}],"name":"UpgradeSpender","type":"event"},{"inputs":[],"name":"EIP191_HEADER","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"EIP712_DOMAIN_NAME","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"EIP712_DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"EIP712_DOMAIN_VERSION","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SUSHISWAP_ROUTER_ADDRESS","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"TRADE_WITH_PERMIT_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"UNISWAP_V2_ROUTER_02_ADDRESS","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"UNISWAP_V3_ROUTER_ADDRESS","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_tokenList","type":"address[]"},{"internalType":"address","name":"_spender","type":"address"}],"name":"closeAllowance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"depositETH","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_signerAddress","type":"address"},{"internalType":"bytes32","name":"_hash","type":"bytes32"},{"internalType":"bytes","name":"_data","type":"bytes"},{"internalType":"bytes","name":"_sig","type":"bytes"}],"name":"isValidSignature","outputs":[{"internalType":"bool","name":"isValid","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"operator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"permStorage","outputs":[{"internalType":"contract IPermanentStorage","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_tokenList","type":"address[]"},{"internalType":"address","name":"_spender","type":"address"}],"name":"setAllowance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_subsidyFactor","type":"uint256"}],"name":"setSubsidyFactor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"spender","outputs":[{"internalType":"contract ISpender","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"subsidyFactor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_makerAddr","type":"address"},{"internalType":"address","name":"_takerAssetAddr","type":"address"},{"internalType":"address","name":"_makerAssetAddr","type":"address"},{"internalType":"uint256","name":"_takerAssetAmount","type":"uint256"},{"internalType":"uint256","name":"_makerAssetAmount","type":"uint256"},{"internalType":"uint256","name":"_feeFactor","type":"uint256"},{"internalType":"address","name":"_userAddr","type":"address"},{"internalType":"address payable","name":"_receiverAddr","type":"address"},{"internalType":"uint256","name":"_salt","type":"uint256"},{"internalType":"uint256","name":"_deadline","type":"uint256"},{"internalType":"bytes","name":"_sig","type":"bytes"}],"name":"trade","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"makerAddr","type":"address"},{"internalType":"address","name":"takerAssetAddr","type":"address"},{"internalType":"address","name":"makerAssetAddr","type":"address"},{"internalType":"uint256","name":"takerAssetAmount","type":"uint256"},{"internalType":"uint256","name":"makerAssetAmount","type":"uint256"},{"internalType":"address","name":"userAddr","type":"address"},{"internalType":"address payable","name":"receiverAddr","type":"address"},{"internalType":"uint256","name":"salt","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"internalType":"struct AMMLibEIP712.Order","name":"_order","type":"tuple"},{"internalType":"uint256","name":"_feeFactor","type":"uint256"},{"internalType":"bytes","name":"_sig","type":"bytes"},{"internalType":"bytes","name":"_makerSpecificData","type":"bytes"},{"internalType":"address[]","name":"_path","type":"address[]"}],"name":"trade","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_newOperator","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newSpender","type":"address"}],"name":"upgradeSpender","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"userProxy","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"version","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"weth","outputs":[{"internalType":"contract IWETH","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]

600861010052672a37b5b2b73637b760c11b6101205261018060405260026101405261763560f01b610160527f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f7f4428669f887e7f6a6e361e218ee42308201bdbacbf601211fc38b8b2ec6961817f7d6f66f923317ceee1bd9447053fb8a64c76979d436a723b87aff2899feaa6c3620000986200015d565b30604051602001620000af959493929190620001e8565b60408051601f198184030181529190528051602090910120608052348015620000d757600080fd5b506040516200507538038062005075833981016040819052620000fa9162000161565b6001600081905580546001600160a01b03199081166001600160a01b0398891617909155600295909555606093841b6001600160601b031990811660a05260038054909616939096169290921790935591811b831660e0521b1660c0526200022d565b4690565b60008060008060008060c087890312156200017a578182fd5b8651620001878162000214565b602088015160408901519197509550620001a18162000214565b6060880151909450620001b48162000214565b6080880151909350620001c78162000214565b60a0880151909250620001da8162000214565b809150509295509295509295565b9485526020850193909352604084019190915260608301526001600160a01b0316608082015260a00190565b6001600160a01b03811681146200022a57600080fd5b50565b60805160a05160601c60c05160601c60e05160601c614db9620002bc600039806105635280610b67528061102852806118695280611bde5250806107355280610778528061097c5280610d395280610d7c528061117c52806119355280611f5e528061200052806120c952508061044552806109585280610a5152508061104c528061177e5250614db96000f3fe60806040526004361061018f5760003560e01c8063982f0ef3116100d6578063e0c05c241161007f578063f6326fb311610059578063f6326fb3146103b9578063fa4e12d7146103ce578063fd070296146103fb57610196565b8063e0c05c241461036f578063e8edc81614610384578063f2fde38b1461039957610196565b8063c49e4fd9116100b0578063c49e4fd914610330578063dab400f314610345578063deb536451461035a57610196565b8063982f0ef3146102f1578063a94c12bc14610306578063bb8a43b91461031b57610196565b806348093018116101385780635cc33321116101125780635cc333211461029c5780638225500c146102b157806382fdaf58146102d157610196565b8063480930181461025257806354fd4d5014610265578063570ca7351461028757610196565b80633ec63216116101695780633ec63216146102085780633fc8cef31461021d57806346920bad1461023257610196565b806303ad2aa01461019b578063192f0c04146101c457806330db4580146101e657610196565b3661019657005b600080fd5b6101ae6101a93660046134fc565b610410565b6040516101bb9190613d04565b60405180910390f35b3480156101d057600080fd5b506101d961088d565b6040516101bb9190613c6b565b3480156101f257600080fd5b50610206610201366004613651565b6108a5565b005b34801561021457600080fd5b506101d9610956565b34801561022957600080fd5b506101d961097a565b34801561023e57600080fd5b5061020661024d366004613651565b61099e565b6101ae6102603660046137de565b610a25565b34801561027157600080fd5b5061027a610e3b565b6040516101bb9190613ded565b34801561029357600080fd5b506101d9610e74565b3480156102a857600080fd5b5061027a610e83565b3480156102bd57600080fd5b506102066102cc366004613920565b610ebc565b3480156102dd57600080fd5b506102066102ec3660046134e0565b610f26565b3480156102fd57600080fd5b506101ae610fce565b34801561031257600080fd5b506101d9610ff6565b34801561032757600080fd5b506101d961100e565b34801561033c57600080fd5b506101d9611026565b34801561035157600080fd5b506101ae61104a565b34801561036657600080fd5b506101ae61106e565b34801561037b57600080fd5b5061027a611074565b34801561039057600080fd5b506101d9611092565b3480156103a557600080fd5b506102066103b43660046134e0565b6110a1565b3480156103c557600080fd5b50610206611149565b3480156103da57600080fd5b506103ee6103e93660046135d4565b611220565b6040516101bb9190613cf9565b34801561040757600080fd5b5061027a61161a565b60006002600054141561043e5760405162461bcd60e51b8152600401610435906149b9565b60405180910390fd5b60026000557f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316331461048b5760405162461bcd60e51b815260040161043590614177565b610493613290565b6040518061012001604052808f6001600160a01b031681526020018e6001600160a01b031681526020018d6001600160a01b031681526020018c81526020018b8152602001896001600160a01b03168152602001886001600160a01b031681526020018781526020018681525090504281610100015110156105275760405162461bcd60e51b815260040161043590614769565b61052f6132dc565b610537613311565b60025461ffff90811660a08401528b1660808301526040516317f7751d60e11b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690632feeea3a90610598903290600401613c6b565b60206040518083038186803b1580156105b057600080fd5b505afa1580156105c4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105e89190613736565b61062457816080015161ffff168260a0015161ffff161161060d578160800151610613565b8160a001515b61ffff166080830152600060a08301525b60208301516001600160a01b0316158061065e575060208301516001600160a01b031673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee145b1515815260408301516001600160a01b0316158061069c575060408301516001600160a01b031673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee145b1515602082015282516106ae90611653565b156107235780516106c35782602001516106d9565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b6001600160a01b0316604082015260208101516106fa578260400151610710565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b6001600160a01b031660608201526107a7565b8051610733578260200151610755565b7f00000000000000000000000000000000000000000000000000000000000000005b6001600160a01b031660408201526020810151610776578260400151610798565b7f00000000000000000000000000000000000000000000000000000000000000005b6001600160a01b031660608201525b6107b28387876116d9565b60208301526107c183826118d8565b6107cc838383611a3e565b606084015282526107de838383611db2565b8260400181815250508260a001516001600160a01b031682602001517f751c65d3b4f3d8a604acd9f9de3800b28af395263a2c0a096f50a5d479d8bf4f846000015186602001518760600151886000015189604001518a608001518b60c001518b604001518c606001518d608001518e60a001516040516108699b9a99989796959493929190613e00565b60405180910390a3506040015160016000559e9d5050505050505050505050505050565b73e592427a0aece92de3edee1f18e0157c0586156481565b6001546001600160a01b031633146108cf5760405162461bcd60e51b81526004016104359061461b565b60005b82811015610950576109118260008686858181106108ec57fe5b905060200201602081019061090191906134e0565b6001600160a01b031691906121a3565b7f7c22b5f0390808135dc69153cbe5633a868bb389d20d7e2071500f3c8e49017e826040516109409190613c6b565b60405180910390a16001016108d2565b50505050565b7f000000000000000000000000000000000000000000000000000000000000000081565b7f000000000000000000000000000000000000000000000000000000000000000081565b6001546001600160a01b031633146109c85760405162461bcd60e51b81526004016104359061461b565b60005b82811015610950576109e6826000198686858181106108ec57fe5b7fcc25b8a957df0a0b6c4413850c122a29ee10048018cd63f00e453e1bba64943a82604051610a159190613c6b565b60405180910390a16001016109cb565b600060026000541415610a4a5760405162461bcd60e51b8152600401610435906149b9565b60026000557f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03163314610a975760405162461bcd60e51b815260040161043590614177565b428961010001511015610abc5760405162461bcd60e51b815260040161043590614769565b610ac46132dc565b610acc613311565b60025461ffff90811660a08401528a166080830152604080516020601f890181900481028201810190925287815290889088908190840183828082843760009201919091525050505060a08201526040805160208087028281018201909352868252909187918791829185019084908082843760009201919091525050505060808201526040516317f7751d60e11b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690632feeea3a90610b9c903290600401613c6b565b60206040518083038186803b158015610bb457600080fd5b505afa158015610bc8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bec9190613736565b610c2857816080015161ffff168260a0015161ffff1611610c11578160800151610c17565b8160a001515b61ffff166080830152600060a08301525b60208b01516001600160a01b03161580610c62575060208b01516001600160a01b031673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee145b1515815260408b01516001600160a01b03161580610ca0575060408b01516001600160a01b031673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee145b151560208201528a51610cb290611653565b15610d27578051610cc7578a60200151610cdd565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b6001600160a01b031660408201526020810151610cfe578a60400151610d14565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b6001600160a01b03166060820152610dab565b8051610d37578a60200151610d59565b7f00000000000000000000000000000000000000000000000000000000000000005b6001600160a01b031660408201526020810151610d7a578a60400151610d9c565b7f00000000000000000000000000000000000000000000000000000000000000005b6001600160a01b031660608201525b610db68b8a8a6116d9565b6020830152610dc58b826118d8565b610dd08b83836122d0565b60608401528252610de28b8383611db2565b8260400181815250507fc36ae6e11a161c28ae95fc0f8c0f56d3d0fb7f3a3524499c53fb6733ed86764d828c604051610e1c929190614b4c565b60405180910390a1506040015160016000559998505050505050505050565b6040518060400160405280600581526020017f352e322e3000000000000000000000000000000000000000000000000000000081525081565b6001546001600160a01b031681565b6040518060400160405280600281526020017f763500000000000000000000000000000000000000000000000000000000000081525081565b6001546001600160a01b03163314610ee65760405162461bcd60e51b81526004016104359061461b565b60028190556040517f944e6cfc55d615def1246239dc39ee5d2490dc67f9f0088edf3142a9cfa4445190610f1b908390613d04565b60405180910390a150565b6001546001600160a01b03163314610f505760405162461bcd60e51b81526004016104359061461b565b6001600160a01b038116610f765760405162461bcd60e51b815260040161043590614359565b6003805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0383161790556040517fbd4e916c3e5390ed2ffaf01ea6c14195c3e174811b8ad55bca06034e89bbd0bb90610f1b908390613c6b565b604051602001610fdd90613a95565b6040516020818303038152906040528051906020012081565b737a250d5630b4cf539739df2c5dacb4c659f2488d81565b73d9e1ce17f2641f24ae83637ab66a2cca9c378b9f81565b7f000000000000000000000000000000000000000000000000000000000000000081565b7f000000000000000000000000000000000000000000000000000000000000000081565b60025481565b60405180604001604052806002815260200161190160f01b81525081565b6003546001600160a01b031681565b6001546001600160a01b031633146110cb5760405162461bcd60e51b81526004016104359061461b565b6001600160a01b0381166110f15760405162461bcd60e51b815260040161043590613ff2565b6001805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0383161790556040517fcfaaa26691e16e66e73290fc725eee1a6b4e0e693a1640484937aac25ffb55a490610f1b908390613c6b565b6001546001600160a01b031633146111735760405162461bcd60e51b81526004016104359061461b565b47801561121d577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b1580156111d557600080fd5b505af11580156111e9573d6000803e3d6000fd5b50505050507ff21b64ad26683e79854b8f088d254ef4e123df84bdb91d1f7f4356d772716a3981604051610f1b9190613d04565b50565b6000808251116112425760405162461bcd60e51b8152600401610435906144cd565b6001600160a01b0385166112685760405162461bcd60e51b81526004016104359061404f565b60006112738361249f565b60f81c9050600781106112985760405162461bcd60e51b8152600401610435906145be565b60008160ff1660078111156112a957fe5b90506000808080808560078111156112bd57fe5b14156112db5760405162461bcd60e51b8152600401610435906146af565b60028560078111156112e957fe5b14156113b35787516061146113105760405162461bcd60e51b815260040161043590613e7e565b61131b886000612505565b9250611328886020612505565b91508760408151811061133757fe5b602001015160f81c60f81b60f81c935060018a8585856040516000815260200160405260405161136a9493929190613d7e565b6020604051602081039080840390855afa15801561138c573d6000803e3d6000fd5b5050604051601f1901516001600160a01b038d811691161497506116129650505050505050565b60038560078111156113c157fe5b14156114685787516061146113e85760405162461bcd60e51b815260040161043590613e7e565b6113f3886000612505565b9250611400886020612505565b91508760408151811061140f57fe5b602001015160f81c60f81b60f81c935060018a6040516020016114329190613c3a565b604051602081830303815290604052805190602001208585856040516000815260200160405260405161136a9493929190613d7e565b600485600781111561147657fe5b141561151b576040516320c13b0b60e01b81526001600160a01b038c16906320c13b0b906114aa908c908c90600401613d9c565b60206040518083038186803b1580156114c257600080fd5b505afa1580156114d6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114fa9190613752565b6001600160e01b0319166320c13b0b60e01b14965061161295505050505050565b600585600781111561152957fe5b14156115ce57604051630b135d3f60e11b81526001600160a01b038c1690631626ba7e9061155d908d908c90600401613d65565b60206040518083038186803b15801561157557600080fd5b505afa158015611589573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115ad9190613752565b6001600160e01b031916630b135d3f60e11b14965061161295505050505050565b60068560078111156115dc57fe5b14156115fa576115ed8a8c8a61253e565b9650505050505050611612565b60405162461bcd60e51b8152600401610435906145be565b949350505050565b6040518060400160405280600881526020017f546f6b656e6c6f6e00000000000000000000000000000000000000000000000081525081565b60006001600160a01b038216737a250d5630b4cf539739df2c5dacb4c659f2488d148061169c57506001600160a01b03821673e592427a0aece92de3edee1f18e0157c05861564145b806116c357506001600160a01b03821673d9e1ce17f2641f24ae83637ab66a2cca9c378b9f145b156116d0575060006116d4565b5060015b919050565b60006040516020016116ea90613a95565b60405160208183030381529060405280519060200120846000015185602001518660400151876060015188608001518960a001518a60c001518b60e001518c61010001516040516020016117479a99989796959493929190613d0d565b604051602081830303815290604052805190602001209050600060405180604001604052806002815260200161190160f01b8152507f0000000000000000000000000000000000000000000000000000000000000000836040516020016117b093929190613a6e565b60405160208183030381529060405280519060200120905061181d8560a00151826040518060200160405280600081525087878080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061122092505050565b6118395760405162461bcd60e51b815260040161043590614413565b6040517f36ef42510000000000000000000000000000000000000000000000000000000081526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906336ef42519061189e908590600401613d04565b600060405180830381600087803b1580156118b857600080fd5b505af11580156118cc573d6000803e3d6000fd5b50505050509392505050565b8051156119ad57600034116118ff5760405162461bcd60e51b815260040161043590614982565b348260600151146119225760405162461bcd60e51b8152600401610435906143b6565b61192f81604001516126a6565b6119a8577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d0e30db0346040518263ffffffff1660e01b81526004016000604051808303818588803b15801561198e57600080fd5b505af11580156119a2573d6000803e3d6000fd5b50505050505b611a3a565b60035460a0830151602084015160608501516040517f3474ad1a0000000000000000000000000000000000000000000000000000000081526001600160a01b0390941693633474ad1a93611a079390929091600401613cbc565b600060405180830381600087803b158015611a2157600080fd5b505af1158015611a35573d6000803e3d6000fd5b505050505b5050565b60606000826040015185600001516000611a57836126a6565b905080611a7457611a746001600160a01b038416836000196121a3565b6000611aad612710611aa7611a9c8b60a0015161ffff166127106126ef90919063ffffffff16565b60808d015190612717565b90612751565b89519091506001600160a01b0316737a250d5630b4cf539739df2c5dacb4c659f2488d1480611af9575088516001600160a01b031673d9e1ce17f2641f24ae83637ab66a2cca9c378b9f145b15611b9a5788516001600160a01b031673d9e1ce17f2641f24ae83637ab66a2cca9c378b9f14611b4b576040518060400160405280600a8152602001692ab734b9bbb0b8102b1960b11b815250611b6e565b6040518060400160405280600981526020016805375736869537761760bc1b8152505b9550611b938960000151886040015189606001518c60600151858e6101000151612783565b9450611d8c565b611ba261335d565b89516040808a015160608b015191517f8ab4a8cc0000000000000000000000000000000000000000000000000000000081526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001693638ab4a8cc93611c13939192600401613c99565b60806040518083038186803b158015611c2b57600080fd5b505afa158015611c3f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c63919061377a565b5061ffff1660408401819052600f91820b820b602085015291810b900b8252611c9e5760405162461bcd60e51b8152600401610435906140ac565b60008160000151600f0b138015611cbc575060008160200151600f0b135b15611d725760408051808201909152600581527f43757276650000000000000000000000000000000000000000000000000000006020808301919091528251600019908101600f90810b810b85529184018051909101820b90910b90526060890151909750600090611d2d906128a7565b8b518351602085015160608f01516040870151949550611d4e948890612941565b6000611d5d8a606001516128a7565b9050611d6981836126ef565b97505050611d8a565b60405162461bcd60e51b81526004016104359061486b565b505b5080611da757611da76001600160a01b0384168360006121a3565b505050935093915050565b60808083015160a084015191850151606085015160009361ffff9384169316911415611de45784606001519250612097565b856080015185606001511115611e70576000611e0d86606001518461271790919063ffffffff16565b611e34612710611e2e8a608001518a606001516126ef90919063ffffffff16565b90612717565b1190508015611e6257611e5b612710611aa7611e5082876126ef565b60608a015190612717565b9350611e6a565b866080015193505b50612097565b60008111611e905760405162461bcd60e51b815260040161043590613edb565b6000828210611e9f5782611ea1565b815b90506000611ebc87606001518361271790919063ffffffff16565b611edd612710611e2e8a606001518c608001516126ef90919063ffffffff16565b1115905080611efe5760405162461bcd60e51b815260040161043590613f95565b6000611f0d87606001516128a7565b60808a0151909150811080159081611f2d5750611f2d88606001516126a6565b1561206e5760808a0151600090611f4490846126ef565b6040516370a0823160e01b81529091506001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190611f93903090600401613c6b565b60206040518083038186803b158015611fab57600080fd5b505afa158015611fbf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611fe39190613938565b811161206c57604051632e1a7d4d60e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690632e1a7d4d90612035908490600401613d04565b600060405180830381600087803b15801561204f57600080fd5b505af1158015612063573d6000803e3d6000fd5b50505050600191505b505b8061208b5760405162461bcd60e51b815260040161043590613f38565b89608001519650505050505b836020015115612174576120ae84606001516126a6565b61213157604051632e1a7d4d60e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690632e1a7d4d906120fe908690600401613d04565b600060405180830381600087803b15801561211857600080fd5b505af115801561212c573d6000803e3d6000fd5b505050505b8560c001516001600160a01b03166108fc849081150290604051600060405180830381858888f1935050505015801561216e573d6000803e3d6000fd5b5061219a565b61219a8660c001518488604001516001600160a01b0316612a699092919063ffffffff16565b50509392505050565b80158061224457506040517fdd62ed3e0000000000000000000000000000000000000000000000000000000081526001600160a01b0384169063dd62ed3e906121f29030908690600401613c7f565b60206040518083038186803b15801561220a57600080fd5b505afa15801561221e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122429190613938565b155b6122605760405162461bcd60e51b8152600401610435906149f0565b6122cb8363095ea7b360e01b848460405160240161227f929190613ce0565b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff166001600160e01b031990931692909217909152612a88565b505050565b606060008260400151856000015160006122e9836126a6565b905080612306576123066001600160a01b038416836000196121a3565b600061232e612710611aa7611a9c8b60a0015161ffff166127106126ef90919063ffffffff16565b89519091506001600160a01b0316737a250d5630b4cf539739df2c5dacb4c659f2488d148061237a575088516001600160a01b031673d9e1ce17f2641f24ae83637ab66a2cca9c378b9f145b156124195788516001600160a01b031673d9e1ce17f2641f24ae83637ab66a2cca9c378b9f146123cc576040518060400160405280600a8152602001692ab734b9bbb0b8102b1960b11b8152506123ef565b6040518060400160405280600981526020016805375736869537761760bc1b8152505b9550611b938960000151886040015189606001518c60600151858e61010001518d60800151612b17565b88516001600160a01b031673e592427a0aece92de3edee1f18e0157c058615641415611b9a576040518060400160405280600a81526020017f556e6973776170205633000000000000000000000000000000000000000000008152509550611b938960000151886040015189606001518c61010001518d60600151868d60a00151612d08565b6000808251116124c15760405162461bcd60e51b81526004016104359061470c565b816001835103815181106124d157fe5b0160200151825160001901909252507fff000000000000000000000000000000000000000000000000000000000000001690565b6000816020018351101561252b5760405162461bcd60e51b815260040161043590614652565b6020820191508183015190505b92915050565b60006060631626ba7e60e01b858460405160240161255d929190613d65565b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff166001600160e01b03199093169290921790915290507fb067138100000000000000000000000000000000000000000000000000000000843b6125fe5762461bcd60e51b600052600160e51b6020526c062ba0a62622aa2fa2a92927a960811b604052600060605260646000fd5b60208201602081845183895afa60203d146126445762461bcd60e51b600052600160e51b6020526c062ba0a62622aa2fa2a92927a960811b604052600060605260646000fd5b8080156126585760018114612689576118cc565b62461bcd60e51b600052600160e51b6020526c062ba0a62622aa2fa2a92927a960811b604052600060605260646000fd5b5050516001600160e01b03199081169116149150505b9392505050565b60006001600160a01b03821673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee14806126da57506001600160a01b038216155b156126e7575060016116d4565b5060006116d4565b6000828211156127115760405162461bcd60e51b815260040161043590614140565b50900390565b60008261272657506000612538565b8282028284828161273357fe5b041461269f5760405162461bcd60e51b815260040161043590614470565b60008082116127725760405162461bcd60e51b815260040161043590614231565b81838161277b57fe5b049392505050565b604080516002808252606080830184526000938a9391929060208301908036833701905050905087816000815181106127b857fe5b60200260200101906001600160a01b031690816001600160a01b03168152505086816001815181106127e657fe5b6001600160a01b0392831660209182029290920101526040516338ed173960e01b81526060918416906338ed17399061282b908a908a90879030908c90600401614c3b565b600060405180830381600087803b15801561284557600080fd5b505af1158015612859573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261288191908101906136a6565b90508060018151811061289057fe5b602002602001015193505050509695505050505050565b60006128b2826126a6565b156128be5750476116d4565b6040516370a0823160e01b81526001600160a01b038316906370a08231906128ea903090600401613c6b565b60206040518083038186803b15801561290257600080fd5b505afa158015612916573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061293a9190613938565b90506116d4565b85600161ffff831614156129d3576040517f3df021240000000000000000000000000000000000000000000000000000000081526001600160a01b03821690633df0212490349061299c908a908a908a908a90600401613dca565b6000604051808303818588803b1580156129b557600080fd5b505af11580156129c9573d6000803e3d6000fd5b5050505050612a60565b8161ffff1660021415612a60576040517fa6417ed60000000000000000000000000000000000000000000000000000000081526001600160a01b0382169063a6417ed6903490612a2d908a908a908a908a90600401613dca565b6000604051808303818588803b158015612a4657600080fd5b505af1158015612a5a573d6000803e3d6000fd5b50505050505b50505050505050565b6122cb8363a9059cbb60e01b848460405160240161227f929190613ce0565b6060612add826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316612ee19092919063ffffffff16565b8051909150156122cb5780806020019051810190612afb9190613736565b6122cb5760405162461bcd60e51b815260040161043590614925565b80516000908890612ba35760408051600280825260608201835290916020830190803683370190505092508783600081518110612b5057fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508683600181518110612b7e57fe5b60200260200101906001600160a01b031690816001600160a01b031681525050612c52565b600283511015612bc55760405162461bcd60e51b8152600401610435906148c8565b876001600160a01b031683600081518110612bdc57fe5b60200260200101516001600160a01b031614612c0a5760405162461bcd60e51b8152600401610435906142fc565b866001600160a01b031683600185510381518110612c2457fe5b60200260200101516001600160a01b031614612c525760405162461bcd60e51b81526004016104359061429f565b6040516338ed173960e01b81526060906001600160a01b038316906338ed173990612c89908a908a90899030908c90600401614c3b565b600060405180830381600087803b158015612ca357600080fd5b505af1158015612cb7573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052612cdf91908101906136a6565b905080600182510381518110612cf157fe5b602002602001015192505050979650505050505050565b60008781612d168482612505565b9050600160ff82161415612e2257600084806020019051810190612d3a91906139d9565b915050612d4561337d565b6001600160a01b03808c1682528a8116602083015262ffffff8316604080840191909152306060840152608083018b905260a083018a905260c08301899052600060e0840152517f414bf3890000000000000000000000000000000000000000000000000000000081529085169063414bf38990612dc7908490600401614add565b602060405180830381600087803b158015612de157600080fd5b505af1158015612df5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e199190613938565b94505050612ed4565b8060ff1660021415612ebc57606084806020019051810190612e449190613950565b915050612e52818b8b612ef0565b612e5a6133c1565b81815230602082015260408082018a90526060820189905260808201889052517fc04b8d590000000000000000000000000000000000000000000000000000000081526001600160a01b0385169063c04b8d5990612dc7908490600401614a84565b60405162461bcd60e51b81526004016104359061480e565b5050979650505050505050565b60606116128484600085612fb0565b600080612efc85613071565b5091509150612f0a856130a2565b15612f4757612f18856130aa565b94505b612f24856130a2565b15612f3957612f32856130aa565b9450612f1b565b612f4285613071565b509150505b836001600160a01b0316826001600160a01b031614612f785760405162461bcd60e51b8152600401610435906142fc565b826001600160a01b0316816001600160a01b031614612fa95760405162461bcd60e51b81526004016104359061429f565b5050505050565b606082471015612fd25760405162461bcd60e51b8152600401610435906141d4565b612fdb856130c1565b612ff75760405162461bcd60e51b8152600401610435906147a0565b60006060866001600160a01b031685876040516130149190613a52565b60006040518083038185875af1925050503d8060008114613051576040519150601f19603f3d011682016040523d82523d6000602084013e613056565b606091505b50915091506130668282866130c7565b979650505050505050565b6000808061307f8482613100565b925061308c846014613162565b9050613099846017613100565b91509193909250565b516042111590565b8051606090612538908390601790601619016131b4565b3b151590565b606083156130d657508161269f565b8251156130e65782518084602001fd5b8160405162461bcd60e51b81526004016104359190613ded565b6000818260140110156131255760405162461bcd60e51b815260040161043590614a4d565b81601401835110156131495760405162461bcd60e51b815260040161043590614587565b5001602001516c01000000000000000000000000900490565b6000818260030110156131875760405162461bcd60e51b815260040161043590614109565b81600301835110156131ab5760405162461bcd60e51b815260040161043590614550565b50016003015190565b60608182601f0110156131d95760405162461bcd60e51b815260040161043590614268565b8282840110156131fb5760405162461bcd60e51b815260040161043590614268565b8183018451101561321e5760405162461bcd60e51b8152600401610435906147d7565b60608215801561323d5760405191506000825260208201604052613287565b6040519150601f8416801560200281840101858101878315602002848b0101015b8183101561327657805183526020928301920161325e565b5050858452601f01601f1916604052505b50949350505050565b6040805161012081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e0810182905261010081019190915290565b6040805160c081018252606080825260006020830181905292820183905281018290526080810182905260a081019190915290565b6040518060c0016040528060001515815260200160001515815260200160006001600160a01b0316815260200160006001600160a01b0316815260200160608152602001606081525090565b604080516060810182526000808252602082018190529181019190915290565b6040805161010081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e081019190915290565b6040518060a001604052806060815260200160006001600160a01b031681526020016000815260200160008152602001600081525090565b803561253881614d42565b60008083601f840112613415578182fd5b50813567ffffffffffffffff81111561342c578182fd5b602083019150836020808302850101111561344657600080fd5b9250929050565b60008083601f84011261345e578182fd5b50813567ffffffffffffffff811115613475578182fd5b60208301915083602082850101111561344657600080fd5b600082601f83011261349d578081fd5b81356134b06134ab82614cf2565b614cab565b91508082528360208285010111156134c757600080fd5b8060208401602084013760009082016020015292915050565b6000602082840312156134f1578081fd5b813561269f81614d42565b6000806000806000806000806000806000806101608d8f03121561351e578788fd5b6135288d35614d42565b8c359b5061353960208e0135614d42565b60208d01359a5061354d8e60408f016133f9565b995060608d0135985060808d0135975060a08d013596506135718e60c08f016133f9565b95506135808e60e08f016133f9565b94506101008d013593506101208d0135925067ffffffffffffffff6101408e013511156135ab578081fd5b6135bc8e6101408f01358f0161344d565b81935080925050509295989b509295989b509295989b565b600080600080608085870312156135e9578384fd5b84356135f481614d42565b935060208501359250604085013567ffffffffffffffff80821115613617578384fd5b6136238883890161348d565b93506060870135915080821115613638578283fd5b506136458782880161348d565b91505092959194509250565b600080600060408486031215613665578283fd5b833567ffffffffffffffff81111561367b578384fd5b61368786828701613404565b909450925050602084013561369b81614d42565b809150509250925092565b600060208083850312156136b8578182fd5b825167ffffffffffffffff8111156136ce578283fd5b8301601f810185136136de578283fd5b80516136ec6134ab82614cd2565b8181528381019083850185840285018601891015613708578687fd5b8694505b8385101561372a57805183526001949094019391850191850161370c565b50979650505050505050565b600060208284031215613747578081fd5b815161269f81614d57565b600060208284031215613763578081fd5b81516001600160e01b03198116811461269f578182fd5b6000806000806080858703121561378f578182fd5b845161379a81614d65565b60208601519094506137ab81614d65565b604086015190935061ffff811681146137c2578283fd5b60608601519092506137d381614d57565b939692955090935050565b600080600080600080600080888a036101a08112156137fb578283fd5b6101208082121561380a578384fd5b61381381614cab565b915061381f8c8c6133f9565b825261382e8c60208d016133f9565b60208301526138408c60408d016133f9565b604083015260608b0135606083015260808b013560808301526138668c60a08d016133f9565b60a08301526138788c60c08d016133f9565b60c083015260e08b81013590830152610100808c013590830152909850890135965061014089013567ffffffffffffffff808211156138b5578384fd5b6138c18c838d0161344d565b90985096506101608b01359150808211156138da578384fd5b6138e68c838d0161344d565b90965094506101808b01359150808211156138ff578384fd5b5061390c8b828c01613404565b999c989b5096995094979396929594505050565b600060208284031215613931578081fd5b5035919050565b600060208284031215613949578081fd5b5051919050565b60008060408385031215613962578182fd5b825161396d81614d74565b602084015190925067ffffffffffffffff811115613989578182fd5b8301601f81018513613999578182fd5b80516139a76134ab82614cf2565b8181528660208385010111156139bb578384fd5b6139cc826020830160208601614d16565b8093505050509250929050565b600080604083850312156139eb578182fd5b82516139f681614d74565b602084015190925062ffffff81168114613a0e578182fd5b809150509250929050565b6001600160a01b03169052565b60008151808452613a3e816020860160208601614d16565b601f01601f19169290920160200192915050565b60008251613a64818460208701614d16565b9190910192915050565b60008451613a80818460208901614d16565b91909101928352506020820152604001919050565b7f7472616465576974685065726d6974280000000000000000000000000000000081527f61646472657373206d616b6572416464722c000000000000000000000000000060108201527f616464726573732074616b65724173736574416464722c00000000000000000060228201527f61646472657373206d616b65724173736574416464722c00000000000000000060398201527f75696e743235362074616b65724173736574416d6f756e742c0000000000000060508201527f75696e74323536206d616b65724173736574416d6f756e742c0000000000000060698201527f616464726573732075736572416464722c00000000000000000000000000000060828201527f61646472657373207265636569766572416464722c000000000000000000000060938201527f75696e743235362073616c742c0000000000000000000000000000000000000060a88201527f75696e7432353620646561646c696e650000000000000000000000000000000060b58201527f290000000000000000000000000000000000000000000000000000000000000060c582015260c60190565b7f19457468657265756d205369676e6564204d6573736167653a0a3332000000008152601c810191909152603c0190565b6001600160a01b0391909116815260200190565b6001600160a01b0392831681529116602082015260400190565b6001600160a01b0393841681529183166020830152909116604082015260600190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b6001600160a01b03929092168252602082015260400190565b901515815260200190565b90815260200190565b998a526001600160a01b0398891660208b015296881660408a01529487166060890152608088019390935260a0870191909152841660c086015290921660e08401526101008301919091526101208201526101400190565b6000838252604060208301526116126040830184613a26565b93845260ff9290921660208401526040830152606082015260800190565b600060408252613daf6040830185613a26565b8281036020840152613dc18185613a26565b95945050505050565b600f94850b81529290930b60208301526040820152606081019190915260800190565b60006020825261269f6020830184613a26565b6000610160808352613e148184018f613a26565b6001600160a01b039d8e166020850152604084019c909c525050978a166060890152958916608088015260a08701949094529190961660c085015260e084019590955261010083019490945261ffff93841661012083015290921661014090920191909152919050565b60208082526037908201527f5369676e617475726556616c696461746f7223697356616c69645369676e617460408201527f7572653a206c656e677468203937207265717569726564000000000000000000606082015260800190565b6020808252602d908201527f414d4d577261707065723a20746869732074726164652077696c6c206e6f742060408201527f626520737562736964697a656400000000000000000000000000000000000000606082015260800190565b6020808252602b908201527f414d4d577261707065723a206e6f7420656e6f75676820736176696e6773207460408201527f6f20737562736964697a65000000000000000000000000000000000000000000606082015260800190565b60208082526038908201527f414d4d577261707065723a20616d6f756e7420646966666572656e6365206c6160408201527f72676572207468616e207375627369647920616d6f756e740000000000000000606082015260800190565b6020808252602c908201527f414d4d577261707065723a206f70657261746f722063616e206e6f742062652060408201527f7a65726f20616464726573730000000000000000000000000000000000000000606082015260800190565b60208082526033908201527f5369676e617475726556616c696461746f7223697356616c69645369676e617460408201527f7572653a20696e76616c6964207369676e657200000000000000000000000000606082015260800190565b60208082526026908201527f414d4d577261707065723a2073776170206d6574686f64206e6f74207265676960408201527f7374657265640000000000000000000000000000000000000000000000000000606082015260800190565b60208082526011908201527f746f55696e7432345f6f766572666c6f77000000000000000000000000000000604082015260600190565b6020808252601e908201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604082015260600190565b60208082526026908201527f414d4d577261707065723a206e6f7420746865205573657250726f787920636f60408201527f6e74726163740000000000000000000000000000000000000000000000000000606082015260800190565b60208082526026908201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60408201527f722063616c6c0000000000000000000000000000000000000000000000000000606082015260800190565b6020808252601a908201527f536166654d6174683a206469766973696f6e206279207a65726f000000000000604082015260600190565b6020808252600e908201527f736c6963655f6f766572666c6f77000000000000000000000000000000000000604082015260600190565b60208082526037908201527f414d4d577261707065723a206c61737420656c656d656e74206f66207061746860408201527f206d757374206d61746368206d616b6572206173736574000000000000000000606082015260800190565b60208082526038908201527f414d4d577261707065723a20666972737420656c656d656e74206f662070617460408201527f68206d757374206d617463682074616b65722061737365740000000000000000606082015260800190565b6020808252602b908201527f414d4d577261707065723a207370656e6465722063616e206e6f74206265207a60408201527f65726f2061646472657373000000000000000000000000000000000000000000606082015260800190565b60208082526023908201527f414d4d577261707065723a206d73672e76616c756520646f65736e2774206d6160408201527f7463680000000000000000000000000000000000000000000000000000000000606082015260800190565b60208082526022908201527f414d4d577261707065723a20696e76616c69642075736572207369676e61747560408201527f7265000000000000000000000000000000000000000000000000000000000000606082015260800190565b60208082526021908201527f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f60408201527f7700000000000000000000000000000000000000000000000000000000000000606082015260800190565b60208082526043908201527f5369676e617475726556616c696461746f7223697356616c69645369676e617460408201527f7572653a206c656e6774682067726561746572207468616e203020726571756960608201527f7265640000000000000000000000000000000000000000000000000000000000608082015260a00190565b60208082526014908201527f746f55696e7432345f6f75744f66426f756e6473000000000000000000000000604082015260600190565b60208082526015908201527f746f416464726573735f6f75744f66426f756e64730000000000000000000000604082015260600190565b6020808252603a908201527f5369676e617475726556616c696461746f7223697356616c69645369676e617460408201527f7572653a20756e737570706f72746564207369676e6174757265000000000000606082015260800190565b6020808252601c908201527f414d4d577261707065723a206e6f7420746865206f70657261746f7200000000604082015260600190565b6020808252603b908201527f4c696242797465732372656164427974657333322067726561746572206f722060408201527f657175616c20746f203332206c656e6774682072657175697265640000000000606082015260800190565b60208082526036908201527f5369676e617475726556616c696461746f7223697356616c69645369676e617460408201527f7572653a20696c6c6567616c207369676e617475726500000000000000000000606082015260800190565b60208082526037908201527f4c6962427974657323706f704c617374427974653a206772656174657220746860408201527f616e207a65726f206c656e677468207265717569726564000000000000000000606082015260800190565b60208082526019908201527f414d4d577261707065723a2065787069726564206f7264657200000000000000604082015260600190565b6020808252601d908201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604082015260600190565b60208082526011908201527f736c6963655f6f75744f66426f756e6473000000000000000000000000000000604082015260600190565b6020808252602b908201527f414d4d577261707065723a20756e737570706f7274656420556e69737761705660408201527f3320737761702074797065000000000000000000000000000000000000000000606082015260800190565b60208082526021908201527f414d4d577261707065723a20756e737570706f72746564206d616b657241646460408201527f7200000000000000000000000000000000000000000000000000000000000000606082015260800190565b6020808252602c908201527f414d4d577261707065723a2070617468206c656e677468206d7573742062652060408201527f6174206c656173742074776f0000000000000000000000000000000000000000606082015260800190565b6020808252602a908201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60408201527f6f74207375636365656400000000000000000000000000000000000000000000606082015260800190565b6020808252601d908201527f414d4d577261707065723a206d73672e76616c7565206973207a65726f000000604082015260600190565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b60208082526036908201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60408201527f20746f206e6f6e2d7a65726f20616c6c6f77616e636500000000000000000000606082015260800190565b60208082526012908201527f746f416464726573735f6f766572666c6f770000000000000000000000000000604082015260600190565b600060208252825160a06020840152614aa060c0840182613a26565b90506001600160a01b0360208501511660408401526040840151606084015260608401516080840152608084015160a08401528091505092915050565b6000610100820190506001600160a01b0380845116835280602085015116602084015262ffffff60408501511660408401528060608501511660608401526080840151608084015260a084015160a084015260c084015160c08401528060e08501511660e08401525092915050565b6000610140808352845160c082850152614b6a610200850182613a26565b602087810151610160870152604088015161018087015260608801516101a0870152608088015161ffff9081166101c088015260a0890151166101e08701528651919450614bbc935085019150613a19565b6020830151614bce6040840182613a19565b506040830151614be16060840182613a19565b5060608301516080830152608083015160a083015260a0830151614c0860c0840182613a19565b5060c0830151614c1b60e0840182613a19565b5060e0830151610100838101919091529092015161012090910152919050565b600060a082018783526020878185015260a0604085015281875180845260c0860191508289019350845b81811015614c8a5784516001600160a01b031683529383019391830191600101614c65565b50506001600160a01b03969096166060850152505050608001529392505050565b60405181810167ffffffffffffffff81118282101715614cca57600080fd5b604052919050565b600067ffffffffffffffff821115614ce8578081fd5b5060209081020190565b600067ffffffffffffffff821115614d08578081fd5b50601f01601f191660200190565b60005b83811015614d31578181015183820152602001614d19565b838111156109505750506000910152565b6001600160a01b038116811461121d57600080fd5b801515811461121d57600080fd5b80600f0b811461121d57600080fd5b60ff8116811461121d57600080fdfea26469706673582212209934b9ec08ae96b9c9cc92d8e4c59e9edc724d056d67457584e4359d24a8a0d064736f6c634300060c00330000000000000000000000009afc226dc049b99342ad6774eeb08bfa2f874465000000000000000000000000000000000000000000000000000000000000001400000000000000000000000003f34be1bf910116595db1b11e9d1b2ca5d596590000000000000000000000003c68dfc45dc92c9c605d92b49858073e10b857a60000000000000000000000006d9cc14a1d36e6ff13fc6efa9e9326fcd12e7903000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2

Deployed Bytecode

0x60806040526004361061018f5760003560e01c8063982f0ef3116100d6578063e0c05c241161007f578063f6326fb311610059578063f6326fb3146103b9578063fa4e12d7146103ce578063fd070296146103fb57610196565b8063e0c05c241461036f578063e8edc81614610384578063f2fde38b1461039957610196565b8063c49e4fd9116100b0578063c49e4fd914610330578063dab400f314610345578063deb536451461035a57610196565b8063982f0ef3146102f1578063a94c12bc14610306578063bb8a43b91461031b57610196565b806348093018116101385780635cc33321116101125780635cc333211461029c5780638225500c146102b157806382fdaf58146102d157610196565b8063480930181461025257806354fd4d5014610265578063570ca7351461028757610196565b80633ec63216116101695780633ec63216146102085780633fc8cef31461021d57806346920bad1461023257610196565b806303ad2aa01461019b578063192f0c04146101c457806330db4580146101e657610196565b3661019657005b600080fd5b6101ae6101a93660046134fc565b610410565b6040516101bb9190613d04565b60405180910390f35b3480156101d057600080fd5b506101d961088d565b6040516101bb9190613c6b565b3480156101f257600080fd5b50610206610201366004613651565b6108a5565b005b34801561021457600080fd5b506101d9610956565b34801561022957600080fd5b506101d961097a565b34801561023e57600080fd5b5061020661024d366004613651565b61099e565b6101ae6102603660046137de565b610a25565b34801561027157600080fd5b5061027a610e3b565b6040516101bb9190613ded565b34801561029357600080fd5b506101d9610e74565b3480156102a857600080fd5b5061027a610e83565b3480156102bd57600080fd5b506102066102cc366004613920565b610ebc565b3480156102dd57600080fd5b506102066102ec3660046134e0565b610f26565b3480156102fd57600080fd5b506101ae610fce565b34801561031257600080fd5b506101d9610ff6565b34801561032757600080fd5b506101d961100e565b34801561033c57600080fd5b506101d9611026565b34801561035157600080fd5b506101ae61104a565b34801561036657600080fd5b506101ae61106e565b34801561037b57600080fd5b5061027a611074565b34801561039057600080fd5b506101d9611092565b3480156103a557600080fd5b506102066103b43660046134e0565b6110a1565b3480156103c557600080fd5b50610206611149565b3480156103da57600080fd5b506103ee6103e93660046135d4565b611220565b6040516101bb9190613cf9565b34801561040757600080fd5b5061027a61161a565b60006002600054141561043e5760405162461bcd60e51b8152600401610435906149b9565b60405180910390fd5b60026000557f00000000000000000000000003f34be1bf910116595db1b11e9d1b2ca5d596596001600160a01b0316331461048b5760405162461bcd60e51b815260040161043590614177565b610493613290565b6040518061012001604052808f6001600160a01b031681526020018e6001600160a01b031681526020018d6001600160a01b031681526020018c81526020018b8152602001896001600160a01b03168152602001886001600160a01b031681526020018781526020018681525090504281610100015110156105275760405162461bcd60e51b815260040161043590614769565b61052f6132dc565b610537613311565b60025461ffff90811660a08401528b1660808301526040516317f7751d60e11b81526001600160a01b037f0000000000000000000000006d9cc14a1d36e6ff13fc6efa9e9326fcd12e79031690632feeea3a90610598903290600401613c6b565b60206040518083038186803b1580156105b057600080fd5b505afa1580156105c4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105e89190613736565b61062457816080015161ffff168260a0015161ffff161161060d578160800151610613565b8160a001515b61ffff166080830152600060a08301525b60208301516001600160a01b0316158061065e575060208301516001600160a01b031673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee145b1515815260408301516001600160a01b0316158061069c575060408301516001600160a01b031673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee145b1515602082015282516106ae90611653565b156107235780516106c35782602001516106d9565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b6001600160a01b0316604082015260208101516106fa578260400151610710565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b6001600160a01b031660608201526107a7565b8051610733578260200151610755565b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc25b6001600160a01b031660408201526020810151610776578260400151610798565b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc25b6001600160a01b031660608201525b6107b28387876116d9565b60208301526107c183826118d8565b6107cc838383611a3e565b606084015282526107de838383611db2565b8260400181815250508260a001516001600160a01b031682602001517f751c65d3b4f3d8a604acd9f9de3800b28af395263a2c0a096f50a5d479d8bf4f846000015186602001518760600151886000015189604001518a608001518b60c001518b604001518c606001518d608001518e60a001516040516108699b9a99989796959493929190613e00565b60405180910390a3506040015160016000559e9d5050505050505050505050505050565b73e592427a0aece92de3edee1f18e0157c0586156481565b6001546001600160a01b031633146108cf5760405162461bcd60e51b81526004016104359061461b565b60005b82811015610950576109118260008686858181106108ec57fe5b905060200201602081019061090191906134e0565b6001600160a01b031691906121a3565b7f7c22b5f0390808135dc69153cbe5633a868bb389d20d7e2071500f3c8e49017e826040516109409190613c6b565b60405180910390a16001016108d2565b50505050565b7f00000000000000000000000003f34be1bf910116595db1b11e9d1b2ca5d5965981565b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc281565b6001546001600160a01b031633146109c85760405162461bcd60e51b81526004016104359061461b565b60005b82811015610950576109e6826000198686858181106108ec57fe5b7fcc25b8a957df0a0b6c4413850c122a29ee10048018cd63f00e453e1bba64943a82604051610a159190613c6b565b60405180910390a16001016109cb565b600060026000541415610a4a5760405162461bcd60e51b8152600401610435906149b9565b60026000557f00000000000000000000000003f34be1bf910116595db1b11e9d1b2ca5d596596001600160a01b03163314610a975760405162461bcd60e51b815260040161043590614177565b428961010001511015610abc5760405162461bcd60e51b815260040161043590614769565b610ac46132dc565b610acc613311565b60025461ffff90811660a08401528a166080830152604080516020601f890181900481028201810190925287815290889088908190840183828082843760009201919091525050505060a08201526040805160208087028281018201909352868252909187918791829185019084908082843760009201919091525050505060808201526040516317f7751d60e11b81526001600160a01b037f0000000000000000000000006d9cc14a1d36e6ff13fc6efa9e9326fcd12e79031690632feeea3a90610b9c903290600401613c6b565b60206040518083038186803b158015610bb457600080fd5b505afa158015610bc8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bec9190613736565b610c2857816080015161ffff168260a0015161ffff1611610c11578160800151610c17565b8160a001515b61ffff166080830152600060a08301525b60208b01516001600160a01b03161580610c62575060208b01516001600160a01b031673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee145b1515815260408b01516001600160a01b03161580610ca0575060408b01516001600160a01b031673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee145b151560208201528a51610cb290611653565b15610d27578051610cc7578a60200151610cdd565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b6001600160a01b031660408201526020810151610cfe578a60400151610d14565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b6001600160a01b03166060820152610dab565b8051610d37578a60200151610d59565b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc25b6001600160a01b031660408201526020810151610d7a578a60400151610d9c565b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc25b6001600160a01b031660608201525b610db68b8a8a6116d9565b6020830152610dc58b826118d8565b610dd08b83836122d0565b60608401528252610de28b8383611db2565b8260400181815250507fc36ae6e11a161c28ae95fc0f8c0f56d3d0fb7f3a3524499c53fb6733ed86764d828c604051610e1c929190614b4c565b60405180910390a1506040015160016000559998505050505050505050565b6040518060400160405280600581526020017f352e322e3000000000000000000000000000000000000000000000000000000081525081565b6001546001600160a01b031681565b6040518060400160405280600281526020017f763500000000000000000000000000000000000000000000000000000000000081525081565b6001546001600160a01b03163314610ee65760405162461bcd60e51b81526004016104359061461b565b60028190556040517f944e6cfc55d615def1246239dc39ee5d2490dc67f9f0088edf3142a9cfa4445190610f1b908390613d04565b60405180910390a150565b6001546001600160a01b03163314610f505760405162461bcd60e51b81526004016104359061461b565b6001600160a01b038116610f765760405162461bcd60e51b815260040161043590614359565b6003805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0383161790556040517fbd4e916c3e5390ed2ffaf01ea6c14195c3e174811b8ad55bca06034e89bbd0bb90610f1b908390613c6b565b604051602001610fdd90613a95565b6040516020818303038152906040528051906020012081565b737a250d5630b4cf539739df2c5dacb4c659f2488d81565b73d9e1ce17f2641f24ae83637ab66a2cca9c378b9f81565b7f0000000000000000000000006d9cc14a1d36e6ff13fc6efa9e9326fcd12e790381565b7f2398e52ffc413ba4f582150da8278d5b2386c55f513d50e900e11bff0ea59c7c81565b60025481565b60405180604001604052806002815260200161190160f01b81525081565b6003546001600160a01b031681565b6001546001600160a01b031633146110cb5760405162461bcd60e51b81526004016104359061461b565b6001600160a01b0381166110f15760405162461bcd60e51b815260040161043590613ff2565b6001805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0383161790556040517fcfaaa26691e16e66e73290fc725eee1a6b4e0e693a1640484937aac25ffb55a490610f1b908390613c6b565b6001546001600160a01b031633146111735760405162461bcd60e51b81526004016104359061461b565b47801561121d577f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b1580156111d557600080fd5b505af11580156111e9573d6000803e3d6000fd5b50505050507ff21b64ad26683e79854b8f088d254ef4e123df84bdb91d1f7f4356d772716a3981604051610f1b9190613d04565b50565b6000808251116112425760405162461bcd60e51b8152600401610435906144cd565b6001600160a01b0385166112685760405162461bcd60e51b81526004016104359061404f565b60006112738361249f565b60f81c9050600781106112985760405162461bcd60e51b8152600401610435906145be565b60008160ff1660078111156112a957fe5b90506000808080808560078111156112bd57fe5b14156112db5760405162461bcd60e51b8152600401610435906146af565b60028560078111156112e957fe5b14156113b35787516061146113105760405162461bcd60e51b815260040161043590613e7e565b61131b886000612505565b9250611328886020612505565b91508760408151811061133757fe5b602001015160f81c60f81b60f81c935060018a8585856040516000815260200160405260405161136a9493929190613d7e565b6020604051602081039080840390855afa15801561138c573d6000803e3d6000fd5b5050604051601f1901516001600160a01b038d811691161497506116129650505050505050565b60038560078111156113c157fe5b14156114685787516061146113e85760405162461bcd60e51b815260040161043590613e7e565b6113f3886000612505565b9250611400886020612505565b91508760408151811061140f57fe5b602001015160f81c60f81b60f81c935060018a6040516020016114329190613c3a565b604051602081830303815290604052805190602001208585856040516000815260200160405260405161136a9493929190613d7e565b600485600781111561147657fe5b141561151b576040516320c13b0b60e01b81526001600160a01b038c16906320c13b0b906114aa908c908c90600401613d9c565b60206040518083038186803b1580156114c257600080fd5b505afa1580156114d6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114fa9190613752565b6001600160e01b0319166320c13b0b60e01b14965061161295505050505050565b600585600781111561152957fe5b14156115ce57604051630b135d3f60e11b81526001600160a01b038c1690631626ba7e9061155d908d908c90600401613d65565b60206040518083038186803b15801561157557600080fd5b505afa158015611589573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115ad9190613752565b6001600160e01b031916630b135d3f60e11b14965061161295505050505050565b60068560078111156115dc57fe5b14156115fa576115ed8a8c8a61253e565b9650505050505050611612565b60405162461bcd60e51b8152600401610435906145be565b949350505050565b6040518060400160405280600881526020017f546f6b656e6c6f6e00000000000000000000000000000000000000000000000081525081565b60006001600160a01b038216737a250d5630b4cf539739df2c5dacb4c659f2488d148061169c57506001600160a01b03821673e592427a0aece92de3edee1f18e0157c05861564145b806116c357506001600160a01b03821673d9e1ce17f2641f24ae83637ab66a2cca9c378b9f145b156116d0575060006116d4565b5060015b919050565b60006040516020016116ea90613a95565b60405160208183030381529060405280519060200120846000015185602001518660400151876060015188608001518960a001518a60c001518b60e001518c61010001516040516020016117479a99989796959493929190613d0d565b604051602081830303815290604052805190602001209050600060405180604001604052806002815260200161190160f01b8152507f2398e52ffc413ba4f582150da8278d5b2386c55f513d50e900e11bff0ea59c7c836040516020016117b093929190613a6e565b60405160208183030381529060405280519060200120905061181d8560a00151826040518060200160405280600081525087878080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061122092505050565b6118395760405162461bcd60e51b815260040161043590614413565b6040517f36ef42510000000000000000000000000000000000000000000000000000000081526001600160a01b037f0000000000000000000000006d9cc14a1d36e6ff13fc6efa9e9326fcd12e790316906336ef42519061189e908590600401613d04565b600060405180830381600087803b1580156118b857600080fd5b505af11580156118cc573d6000803e3d6000fd5b50505050509392505050565b8051156119ad57600034116118ff5760405162461bcd60e51b815260040161043590614982565b348260600151146119225760405162461bcd60e51b8152600401610435906143b6565b61192f81604001516126a6565b6119a8577f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031663d0e30db0346040518263ffffffff1660e01b81526004016000604051808303818588803b15801561198e57600080fd5b505af11580156119a2573d6000803e3d6000fd5b50505050505b611a3a565b60035460a0830151602084015160608501516040517f3474ad1a0000000000000000000000000000000000000000000000000000000081526001600160a01b0390941693633474ad1a93611a079390929091600401613cbc565b600060405180830381600087803b158015611a2157600080fd5b505af1158015611a35573d6000803e3d6000fd5b505050505b5050565b60606000826040015185600001516000611a57836126a6565b905080611a7457611a746001600160a01b038416836000196121a3565b6000611aad612710611aa7611a9c8b60a0015161ffff166127106126ef90919063ffffffff16565b60808d015190612717565b90612751565b89519091506001600160a01b0316737a250d5630b4cf539739df2c5dacb4c659f2488d1480611af9575088516001600160a01b031673d9e1ce17f2641f24ae83637ab66a2cca9c378b9f145b15611b9a5788516001600160a01b031673d9e1ce17f2641f24ae83637ab66a2cca9c378b9f14611b4b576040518060400160405280600a8152602001692ab734b9bbb0b8102b1960b11b815250611b6e565b6040518060400160405280600981526020016805375736869537761760bc1b8152505b9550611b938960000151886040015189606001518c60600151858e6101000151612783565b9450611d8c565b611ba261335d565b89516040808a015160608b015191517f8ab4a8cc0000000000000000000000000000000000000000000000000000000081526001600160a01b037f0000000000000000000000006d9cc14a1d36e6ff13fc6efa9e9326fcd12e79031693638ab4a8cc93611c13939192600401613c99565b60806040518083038186803b158015611c2b57600080fd5b505afa158015611c3f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c63919061377a565b5061ffff1660408401819052600f91820b820b602085015291810b900b8252611c9e5760405162461bcd60e51b8152600401610435906140ac565b60008160000151600f0b138015611cbc575060008160200151600f0b135b15611d725760408051808201909152600581527f43757276650000000000000000000000000000000000000000000000000000006020808301919091528251600019908101600f90810b810b85529184018051909101820b90910b90526060890151909750600090611d2d906128a7565b8b518351602085015160608f01516040870151949550611d4e948890612941565b6000611d5d8a606001516128a7565b9050611d6981836126ef565b97505050611d8a565b60405162461bcd60e51b81526004016104359061486b565b505b5080611da757611da76001600160a01b0384168360006121a3565b505050935093915050565b60808083015160a084015191850151606085015160009361ffff9384169316911415611de45784606001519250612097565b856080015185606001511115611e70576000611e0d86606001518461271790919063ffffffff16565b611e34612710611e2e8a608001518a606001516126ef90919063ffffffff16565b90612717565b1190508015611e6257611e5b612710611aa7611e5082876126ef565b60608a015190612717565b9350611e6a565b866080015193505b50612097565b60008111611e905760405162461bcd60e51b815260040161043590613edb565b6000828210611e9f5782611ea1565b815b90506000611ebc87606001518361271790919063ffffffff16565b611edd612710611e2e8a606001518c608001516126ef90919063ffffffff16565b1115905080611efe5760405162461bcd60e51b815260040161043590613f95565b6000611f0d87606001516128a7565b60808a0151909150811080159081611f2d5750611f2d88606001516126a6565b1561206e5760808a0151600090611f4490846126ef565b6040516370a0823160e01b81529091506001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc216906370a0823190611f93903090600401613c6b565b60206040518083038186803b158015611fab57600080fd5b505afa158015611fbf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611fe39190613938565b811161206c57604051632e1a7d4d60e01b81526001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc21690632e1a7d4d90612035908490600401613d04565b600060405180830381600087803b15801561204f57600080fd5b505af1158015612063573d6000803e3d6000fd5b50505050600191505b505b8061208b5760405162461bcd60e51b815260040161043590613f38565b89608001519650505050505b836020015115612174576120ae84606001516126a6565b61213157604051632e1a7d4d60e01b81526001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc21690632e1a7d4d906120fe908690600401613d04565b600060405180830381600087803b15801561211857600080fd5b505af115801561212c573d6000803e3d6000fd5b505050505b8560c001516001600160a01b03166108fc849081150290604051600060405180830381858888f1935050505015801561216e573d6000803e3d6000fd5b5061219a565b61219a8660c001518488604001516001600160a01b0316612a699092919063ffffffff16565b50509392505050565b80158061224457506040517fdd62ed3e0000000000000000000000000000000000000000000000000000000081526001600160a01b0384169063dd62ed3e906121f29030908690600401613c7f565b60206040518083038186803b15801561220a57600080fd5b505afa15801561221e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122429190613938565b155b6122605760405162461bcd60e51b8152600401610435906149f0565b6122cb8363095ea7b360e01b848460405160240161227f929190613ce0565b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff166001600160e01b031990931692909217909152612a88565b505050565b606060008260400151856000015160006122e9836126a6565b905080612306576123066001600160a01b038416836000196121a3565b600061232e612710611aa7611a9c8b60a0015161ffff166127106126ef90919063ffffffff16565b89519091506001600160a01b0316737a250d5630b4cf539739df2c5dacb4c659f2488d148061237a575088516001600160a01b031673d9e1ce17f2641f24ae83637ab66a2cca9c378b9f145b156124195788516001600160a01b031673d9e1ce17f2641f24ae83637ab66a2cca9c378b9f146123cc576040518060400160405280600a8152602001692ab734b9bbb0b8102b1960b11b8152506123ef565b6040518060400160405280600981526020016805375736869537761760bc1b8152505b9550611b938960000151886040015189606001518c60600151858e61010001518d60800151612b17565b88516001600160a01b031673e592427a0aece92de3edee1f18e0157c058615641415611b9a576040518060400160405280600a81526020017f556e6973776170205633000000000000000000000000000000000000000000008152509550611b938960000151886040015189606001518c61010001518d60600151868d60a00151612d08565b6000808251116124c15760405162461bcd60e51b81526004016104359061470c565b816001835103815181106124d157fe5b0160200151825160001901909252507fff000000000000000000000000000000000000000000000000000000000000001690565b6000816020018351101561252b5760405162461bcd60e51b815260040161043590614652565b6020820191508183015190505b92915050565b60006060631626ba7e60e01b858460405160240161255d929190613d65565b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff166001600160e01b03199093169290921790915290507fb067138100000000000000000000000000000000000000000000000000000000843b6125fe5762461bcd60e51b600052600160e51b6020526c062ba0a62622aa2fa2a92927a960811b604052600060605260646000fd5b60208201602081845183895afa60203d146126445762461bcd60e51b600052600160e51b6020526c062ba0a62622aa2fa2a92927a960811b604052600060605260646000fd5b8080156126585760018114612689576118cc565b62461bcd60e51b600052600160e51b6020526c062ba0a62622aa2fa2a92927a960811b604052600060605260646000fd5b5050516001600160e01b03199081169116149150505b9392505050565b60006001600160a01b03821673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee14806126da57506001600160a01b038216155b156126e7575060016116d4565b5060006116d4565b6000828211156127115760405162461bcd60e51b815260040161043590614140565b50900390565b60008261272657506000612538565b8282028284828161273357fe5b041461269f5760405162461bcd60e51b815260040161043590614470565b60008082116127725760405162461bcd60e51b815260040161043590614231565b81838161277b57fe5b049392505050565b604080516002808252606080830184526000938a9391929060208301908036833701905050905087816000815181106127b857fe5b60200260200101906001600160a01b031690816001600160a01b03168152505086816001815181106127e657fe5b6001600160a01b0392831660209182029290920101526040516338ed173960e01b81526060918416906338ed17399061282b908a908a90879030908c90600401614c3b565b600060405180830381600087803b15801561284557600080fd5b505af1158015612859573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261288191908101906136a6565b90508060018151811061289057fe5b602002602001015193505050509695505050505050565b60006128b2826126a6565b156128be5750476116d4565b6040516370a0823160e01b81526001600160a01b038316906370a08231906128ea903090600401613c6b565b60206040518083038186803b15801561290257600080fd5b505afa158015612916573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061293a9190613938565b90506116d4565b85600161ffff831614156129d3576040517f3df021240000000000000000000000000000000000000000000000000000000081526001600160a01b03821690633df0212490349061299c908a908a908a908a90600401613dca565b6000604051808303818588803b1580156129b557600080fd5b505af11580156129c9573d6000803e3d6000fd5b5050505050612a60565b8161ffff1660021415612a60576040517fa6417ed60000000000000000000000000000000000000000000000000000000081526001600160a01b0382169063a6417ed6903490612a2d908a908a908a908a90600401613dca565b6000604051808303818588803b158015612a4657600080fd5b505af1158015612a5a573d6000803e3d6000fd5b50505050505b50505050505050565b6122cb8363a9059cbb60e01b848460405160240161227f929190613ce0565b6060612add826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316612ee19092919063ffffffff16565b8051909150156122cb5780806020019051810190612afb9190613736565b6122cb5760405162461bcd60e51b815260040161043590614925565b80516000908890612ba35760408051600280825260608201835290916020830190803683370190505092508783600081518110612b5057fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508683600181518110612b7e57fe5b60200260200101906001600160a01b031690816001600160a01b031681525050612c52565b600283511015612bc55760405162461bcd60e51b8152600401610435906148c8565b876001600160a01b031683600081518110612bdc57fe5b60200260200101516001600160a01b031614612c0a5760405162461bcd60e51b8152600401610435906142fc565b866001600160a01b031683600185510381518110612c2457fe5b60200260200101516001600160a01b031614612c525760405162461bcd60e51b81526004016104359061429f565b6040516338ed173960e01b81526060906001600160a01b038316906338ed173990612c89908a908a90899030908c90600401614c3b565b600060405180830381600087803b158015612ca357600080fd5b505af1158015612cb7573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052612cdf91908101906136a6565b905080600182510381518110612cf157fe5b602002602001015192505050979650505050505050565b60008781612d168482612505565b9050600160ff82161415612e2257600084806020019051810190612d3a91906139d9565b915050612d4561337d565b6001600160a01b03808c1682528a8116602083015262ffffff8316604080840191909152306060840152608083018b905260a083018a905260c08301899052600060e0840152517f414bf3890000000000000000000000000000000000000000000000000000000081529085169063414bf38990612dc7908490600401614add565b602060405180830381600087803b158015612de157600080fd5b505af1158015612df5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e199190613938565b94505050612ed4565b8060ff1660021415612ebc57606084806020019051810190612e449190613950565b915050612e52818b8b612ef0565b612e5a6133c1565b81815230602082015260408082018a90526060820189905260808201889052517fc04b8d590000000000000000000000000000000000000000000000000000000081526001600160a01b0385169063c04b8d5990612dc7908490600401614a84565b60405162461bcd60e51b81526004016104359061480e565b5050979650505050505050565b60606116128484600085612fb0565b600080612efc85613071565b5091509150612f0a856130a2565b15612f4757612f18856130aa565b94505b612f24856130a2565b15612f3957612f32856130aa565b9450612f1b565b612f4285613071565b509150505b836001600160a01b0316826001600160a01b031614612f785760405162461bcd60e51b8152600401610435906142fc565b826001600160a01b0316816001600160a01b031614612fa95760405162461bcd60e51b81526004016104359061429f565b5050505050565b606082471015612fd25760405162461bcd60e51b8152600401610435906141d4565b612fdb856130c1565b612ff75760405162461bcd60e51b8152600401610435906147a0565b60006060866001600160a01b031685876040516130149190613a52565b60006040518083038185875af1925050503d8060008114613051576040519150601f19603f3d011682016040523d82523d6000602084013e613056565b606091505b50915091506130668282866130c7565b979650505050505050565b6000808061307f8482613100565b925061308c846014613162565b9050613099846017613100565b91509193909250565b516042111590565b8051606090612538908390601790601619016131b4565b3b151590565b606083156130d657508161269f565b8251156130e65782518084602001fd5b8160405162461bcd60e51b81526004016104359190613ded565b6000818260140110156131255760405162461bcd60e51b815260040161043590614a4d565b81601401835110156131495760405162461bcd60e51b815260040161043590614587565b5001602001516c01000000000000000000000000900490565b6000818260030110156131875760405162461bcd60e51b815260040161043590614109565b81600301835110156131ab5760405162461bcd60e51b815260040161043590614550565b50016003015190565b60608182601f0110156131d95760405162461bcd60e51b815260040161043590614268565b8282840110156131fb5760405162461bcd60e51b815260040161043590614268565b8183018451101561321e5760405162461bcd60e51b8152600401610435906147d7565b60608215801561323d5760405191506000825260208201604052613287565b6040519150601f8416801560200281840101858101878315602002848b0101015b8183101561327657805183526020928301920161325e565b5050858452601f01601f1916604052505b50949350505050565b6040805161012081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e0810182905261010081019190915290565b6040805160c081018252606080825260006020830181905292820183905281018290526080810182905260a081019190915290565b6040518060c0016040528060001515815260200160001515815260200160006001600160a01b0316815260200160006001600160a01b0316815260200160608152602001606081525090565b604080516060810182526000808252602082018190529181019190915290565b6040805161010081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e081019190915290565b6040518060a001604052806060815260200160006001600160a01b031681526020016000815260200160008152602001600081525090565b803561253881614d42565b60008083601f840112613415578182fd5b50813567ffffffffffffffff81111561342c578182fd5b602083019150836020808302850101111561344657600080fd5b9250929050565b60008083601f84011261345e578182fd5b50813567ffffffffffffffff811115613475578182fd5b60208301915083602082850101111561344657600080fd5b600082601f83011261349d578081fd5b81356134b06134ab82614cf2565b614cab565b91508082528360208285010111156134c757600080fd5b8060208401602084013760009082016020015292915050565b6000602082840312156134f1578081fd5b813561269f81614d42565b6000806000806000806000806000806000806101608d8f03121561351e578788fd5b6135288d35614d42565b8c359b5061353960208e0135614d42565b60208d01359a5061354d8e60408f016133f9565b995060608d0135985060808d0135975060a08d013596506135718e60c08f016133f9565b95506135808e60e08f016133f9565b94506101008d013593506101208d0135925067ffffffffffffffff6101408e013511156135ab578081fd5b6135bc8e6101408f01358f0161344d565b81935080925050509295989b509295989b509295989b565b600080600080608085870312156135e9578384fd5b84356135f481614d42565b935060208501359250604085013567ffffffffffffffff80821115613617578384fd5b6136238883890161348d565b93506060870135915080821115613638578283fd5b506136458782880161348d565b91505092959194509250565b600080600060408486031215613665578283fd5b833567ffffffffffffffff81111561367b578384fd5b61368786828701613404565b909450925050602084013561369b81614d42565b809150509250925092565b600060208083850312156136b8578182fd5b825167ffffffffffffffff8111156136ce578283fd5b8301601f810185136136de578283fd5b80516136ec6134ab82614cd2565b8181528381019083850185840285018601891015613708578687fd5b8694505b8385101561372a57805183526001949094019391850191850161370c565b50979650505050505050565b600060208284031215613747578081fd5b815161269f81614d57565b600060208284031215613763578081fd5b81516001600160e01b03198116811461269f578182fd5b6000806000806080858703121561378f578182fd5b845161379a81614d65565b60208601519094506137ab81614d65565b604086015190935061ffff811681146137c2578283fd5b60608601519092506137d381614d57565b939692955090935050565b600080600080600080600080888a036101a08112156137fb578283fd5b6101208082121561380a578384fd5b61381381614cab565b915061381f8c8c6133f9565b825261382e8c60208d016133f9565b60208301526138408c60408d016133f9565b604083015260608b0135606083015260808b013560808301526138668c60a08d016133f9565b60a08301526138788c60c08d016133f9565b60c083015260e08b81013590830152610100808c013590830152909850890135965061014089013567ffffffffffffffff808211156138b5578384fd5b6138c18c838d0161344d565b90985096506101608b01359150808211156138da578384fd5b6138e68c838d0161344d565b90965094506101808b01359150808211156138ff578384fd5b5061390c8b828c01613404565b999c989b5096995094979396929594505050565b600060208284031215613931578081fd5b5035919050565b600060208284031215613949578081fd5b5051919050565b60008060408385031215613962578182fd5b825161396d81614d74565b602084015190925067ffffffffffffffff811115613989578182fd5b8301601f81018513613999578182fd5b80516139a76134ab82614cf2565b8181528660208385010111156139bb578384fd5b6139cc826020830160208601614d16565b8093505050509250929050565b600080604083850312156139eb578182fd5b82516139f681614d74565b602084015190925062ffffff81168114613a0e578182fd5b809150509250929050565b6001600160a01b03169052565b60008151808452613a3e816020860160208601614d16565b601f01601f19169290920160200192915050565b60008251613a64818460208701614d16565b9190910192915050565b60008451613a80818460208901614d16565b91909101928352506020820152604001919050565b7f7472616465576974685065726d6974280000000000000000000000000000000081527f61646472657373206d616b6572416464722c000000000000000000000000000060108201527f616464726573732074616b65724173736574416464722c00000000000000000060228201527f61646472657373206d616b65724173736574416464722c00000000000000000060398201527f75696e743235362074616b65724173736574416d6f756e742c0000000000000060508201527f75696e74323536206d616b65724173736574416d6f756e742c0000000000000060698201527f616464726573732075736572416464722c00000000000000000000000000000060828201527f61646472657373207265636569766572416464722c000000000000000000000060938201527f75696e743235362073616c742c0000000000000000000000000000000000000060a88201527f75696e7432353620646561646c696e650000000000000000000000000000000060b58201527f290000000000000000000000000000000000000000000000000000000000000060c582015260c60190565b7f19457468657265756d205369676e6564204d6573736167653a0a3332000000008152601c810191909152603c0190565b6001600160a01b0391909116815260200190565b6001600160a01b0392831681529116602082015260400190565b6001600160a01b0393841681529183166020830152909116604082015260600190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b6001600160a01b03929092168252602082015260400190565b901515815260200190565b90815260200190565b998a526001600160a01b0398891660208b015296881660408a01529487166060890152608088019390935260a0870191909152841660c086015290921660e08401526101008301919091526101208201526101400190565b6000838252604060208301526116126040830184613a26565b93845260ff9290921660208401526040830152606082015260800190565b600060408252613daf6040830185613a26565b8281036020840152613dc18185613a26565b95945050505050565b600f94850b81529290930b60208301526040820152606081019190915260800190565b60006020825261269f6020830184613a26565b6000610160808352613e148184018f613a26565b6001600160a01b039d8e166020850152604084019c909c525050978a166060890152958916608088015260a08701949094529190961660c085015260e084019590955261010083019490945261ffff93841661012083015290921661014090920191909152919050565b60208082526037908201527f5369676e617475726556616c696461746f7223697356616c69645369676e617460408201527f7572653a206c656e677468203937207265717569726564000000000000000000606082015260800190565b6020808252602d908201527f414d4d577261707065723a20746869732074726164652077696c6c206e6f742060408201527f626520737562736964697a656400000000000000000000000000000000000000606082015260800190565b6020808252602b908201527f414d4d577261707065723a206e6f7420656e6f75676820736176696e6773207460408201527f6f20737562736964697a65000000000000000000000000000000000000000000606082015260800190565b60208082526038908201527f414d4d577261707065723a20616d6f756e7420646966666572656e6365206c6160408201527f72676572207468616e207375627369647920616d6f756e740000000000000000606082015260800190565b6020808252602c908201527f414d4d577261707065723a206f70657261746f722063616e206e6f742062652060408201527f7a65726f20616464726573730000000000000000000000000000000000000000606082015260800190565b60208082526033908201527f5369676e617475726556616c696461746f7223697356616c69645369676e617460408201527f7572653a20696e76616c6964207369676e657200000000000000000000000000606082015260800190565b60208082526026908201527f414d4d577261707065723a2073776170206d6574686f64206e6f74207265676960408201527f7374657265640000000000000000000000000000000000000000000000000000606082015260800190565b60208082526011908201527f746f55696e7432345f6f766572666c6f77000000000000000000000000000000604082015260600190565b6020808252601e908201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604082015260600190565b60208082526026908201527f414d4d577261707065723a206e6f7420746865205573657250726f787920636f60408201527f6e74726163740000000000000000000000000000000000000000000000000000606082015260800190565b60208082526026908201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60408201527f722063616c6c0000000000000000000000000000000000000000000000000000606082015260800190565b6020808252601a908201527f536166654d6174683a206469766973696f6e206279207a65726f000000000000604082015260600190565b6020808252600e908201527f736c6963655f6f766572666c6f77000000000000000000000000000000000000604082015260600190565b60208082526037908201527f414d4d577261707065723a206c61737420656c656d656e74206f66207061746860408201527f206d757374206d61746368206d616b6572206173736574000000000000000000606082015260800190565b60208082526038908201527f414d4d577261707065723a20666972737420656c656d656e74206f662070617460408201527f68206d757374206d617463682074616b65722061737365740000000000000000606082015260800190565b6020808252602b908201527f414d4d577261707065723a207370656e6465722063616e206e6f74206265207a60408201527f65726f2061646472657373000000000000000000000000000000000000000000606082015260800190565b60208082526023908201527f414d4d577261707065723a206d73672e76616c756520646f65736e2774206d6160408201527f7463680000000000000000000000000000000000000000000000000000000000606082015260800190565b60208082526022908201527f414d4d577261707065723a20696e76616c69642075736572207369676e61747560408201527f7265000000000000000000000000000000000000000000000000000000000000606082015260800190565b60208082526021908201527f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f60408201527f7700000000000000000000000000000000000000000000000000000000000000606082015260800190565b60208082526043908201527f5369676e617475726556616c696461746f7223697356616c69645369676e617460408201527f7572653a206c656e6774682067726561746572207468616e203020726571756960608201527f7265640000000000000000000000000000000000000000000000000000000000608082015260a00190565b60208082526014908201527f746f55696e7432345f6f75744f66426f756e6473000000000000000000000000604082015260600190565b60208082526015908201527f746f416464726573735f6f75744f66426f756e64730000000000000000000000604082015260600190565b6020808252603a908201527f5369676e617475726556616c696461746f7223697356616c69645369676e617460408201527f7572653a20756e737570706f72746564207369676e6174757265000000000000606082015260800190565b6020808252601c908201527f414d4d577261707065723a206e6f7420746865206f70657261746f7200000000604082015260600190565b6020808252603b908201527f4c696242797465732372656164427974657333322067726561746572206f722060408201527f657175616c20746f203332206c656e6774682072657175697265640000000000606082015260800190565b60208082526036908201527f5369676e617475726556616c696461746f7223697356616c69645369676e617460408201527f7572653a20696c6c6567616c207369676e617475726500000000000000000000606082015260800190565b60208082526037908201527f4c6962427974657323706f704c617374427974653a206772656174657220746860408201527f616e207a65726f206c656e677468207265717569726564000000000000000000606082015260800190565b60208082526019908201527f414d4d577261707065723a2065787069726564206f7264657200000000000000604082015260600190565b6020808252601d908201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604082015260600190565b60208082526011908201527f736c6963655f6f75744f66426f756e6473000000000000000000000000000000604082015260600190565b6020808252602b908201527f414d4d577261707065723a20756e737570706f7274656420556e69737761705660408201527f3320737761702074797065000000000000000000000000000000000000000000606082015260800190565b60208082526021908201527f414d4d577261707065723a20756e737570706f72746564206d616b657241646460408201527f7200000000000000000000000000000000000000000000000000000000000000606082015260800190565b6020808252602c908201527f414d4d577261707065723a2070617468206c656e677468206d7573742062652060408201527f6174206c656173742074776f0000000000000000000000000000000000000000606082015260800190565b6020808252602a908201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60408201527f6f74207375636365656400000000000000000000000000000000000000000000606082015260800190565b6020808252601d908201527f414d4d577261707065723a206d73672e76616c7565206973207a65726f000000604082015260600190565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b60208082526036908201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60408201527f20746f206e6f6e2d7a65726f20616c6c6f77616e636500000000000000000000606082015260800190565b60208082526012908201527f746f416464726573735f6f766572666c6f770000000000000000000000000000604082015260600190565b600060208252825160a06020840152614aa060c0840182613a26565b90506001600160a01b0360208501511660408401526040840151606084015260608401516080840152608084015160a08401528091505092915050565b6000610100820190506001600160a01b0380845116835280602085015116602084015262ffffff60408501511660408401528060608501511660608401526080840151608084015260a084015160a084015260c084015160c08401528060e08501511660e08401525092915050565b6000610140808352845160c082850152614b6a610200850182613a26565b602087810151610160870152604088015161018087015260608801516101a0870152608088015161ffff9081166101c088015260a0890151166101e08701528651919450614bbc935085019150613a19565b6020830151614bce6040840182613a19565b506040830151614be16060840182613a19565b5060608301516080830152608083015160a083015260a0830151614c0860c0840182613a19565b5060c0830151614c1b60e0840182613a19565b5060e0830151610100838101919091529092015161012090910152919050565b600060a082018783526020878185015260a0604085015281875180845260c0860191508289019350845b81811015614c8a5784516001600160a01b031683529383019391830191600101614c65565b50506001600160a01b03969096166060850152505050608001529392505050565b60405181810167ffffffffffffffff81118282101715614cca57600080fd5b604052919050565b600067ffffffffffffffff821115614ce8578081fd5b5060209081020190565b600067ffffffffffffffff821115614d08578081fd5b50601f01601f191660200190565b60005b83811015614d31578181015183820152602001614d19565b838111156109505750506000910152565b6001600160a01b038116811461121d57600080fd5b801515811461121d57600080fd5b80600f0b811461121d57600080fd5b60ff8116811461121d57600080fdfea26469706673582212209934b9ec08ae96b9c9cc92d8e4c59e9edc724d056d67457584e4359d24a8a0d064736f6c634300060c0033

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

0000000000000000000000009afc226dc049b99342ad6774eeb08bfa2f874465000000000000000000000000000000000000000000000000000000000000001400000000000000000000000003f34be1bf910116595db1b11e9d1b2ca5d596590000000000000000000000003c68dfc45dc92c9c605d92b49858073e10b857a60000000000000000000000006d9cc14a1d36e6ff13fc6efa9e9326fcd12e7903000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2

-----Decoded View---------------
Arg [0] : _operator (address): 0x9aFc226Dc049B99342Ad6774Eeb08BfA2F874465
Arg [1] : _subsidyFactor (uint256): 20
Arg [2] : _userProxy (address): 0x03f34bE1BF910116595dB1b11E9d1B2cA5D59659
Arg [3] : _spender (address): 0x3c68dfc45dc92C9c605d92B49858073e10b857A6
Arg [4] : _permStorage (address): 0x6D9Cc14a1d36E6fF13fc6efA9e9326FcD12E7903
Arg [5] : _weth (address): 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2

-----Encoded View---------------
6 Constructor Arguments found :
Arg [0] : 0000000000000000000000009afc226dc049b99342ad6774eeb08bfa2f874465
Arg [1] : 0000000000000000000000000000000000000000000000000000000000000014
Arg [2] : 00000000000000000000000003f34be1bf910116595db1b11e9d1b2ca5d59659
Arg [3] : 0000000000000000000000003c68dfc45dc92c9c605d92b49858073e10b857a6
Arg [4] : 0000000000000000000000006d9cc14a1d36e6ff13fc6efa9e9326fcd12e7903
Arg [5] : 000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2


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
Chain Token Portfolio % Price Amount Value
ETH49.62%$3,821.0270.8725$270,805.39
ETH26.97%$0.999714147,216.882$147,174.78
ETH4.28%$0.99933423,362.6875$23,347.13
ETH3.39%$0.0000151,241,430,802.3539$18,509.73
ETH3.10%$9.891,707.8511$16,890.65
ETH2.50%$0.99853213,686.6319$13,666.54
ETH1.16%$4.811,311.7908$6,309.71
ETH0.97%$18.39288.193$5,299.87
ETH0.84%$0.7860925,858.8795$4,605.62
ETH0.71%$1.033,757.4734$3,858.93
ETH0.68%$2.121,753.418$3,717.25
ETH0.64%$0.6990574,986.7176$3,486
ETH0.61%$6.3528.1663$3,327.45
ETH0.41%$44.8349.9882$2,240.97
ETH0.39%$0.4130375,093.0181$2,103.6
ETH0.34%$0.15131712,203.5562$1,846.61
ETH0.24%$1.31,027.7778$1,336.11
ETH0.23%$2,687.780.4613$1,239.98
ETH0.19%$67,4120.0157$1,056.1
ETH0.19%$0.8944141,145.1$1,024.19
ETH0.18%$67,6890.0149$1,005.57
ETH0.18%$4.79200.5089$960.44
ETH0.16%$0.4719211,862.4833$878.94
ETH0.14%$1.17675.0607$789.82
ETH0.14%$104.487.4014$773.26
ETH0.12%$10.0662.9236$633.01
ETH0.11%$0.897368654.8262$587.62
ETH0.11%$28.520.5694$586.23
ETH0.10%$1556.3909$556.39
ETH0.09%$0.999686490.07$489.92
ETH0.09%$0.0687786,971.1272$479.46
ETH0.07%$3.34112.9155$377.14
ETH0.06%$0.448447725.2265$325.23
ETH0.06%$0.998189319.3208$318.74
ETH0.06%$0.493053621.131$306.25
ETH0.05%$0.0825333,629.2399$299.53
ETH0.05%$23.1212.6557$292.6
ETH0.05%$6,905.690.0403$278.27
ETH0.05%$2.8288.0803$248.04
ETH0.05%$2.22111.4505$247.42
ETH0.04%$0.1100012,103.8109$231.42
ETH0.03%$0.348919531.7034$185.52
ETH0.03%$0.520411340.3518$177.12
ETH0.03%$2.0574.5985$152.93
ETH0.03%$0.999015145.0811$144.94
ETH0.03%$58.652.4453$143.41
ETH0.02%$0.242031486.8043$117.82
ETH0.02%$1.6167.6962$108.99
ETH0.02%$0.0931781,163.3717$108.4
ETH0.02%$0.00152270,578.9283$107.43
ETH0.02%$6.1117.0954$104.45
ETH0.02%$9.610.254$98.44
ETH0.02%$0.43842219.6135$96.28
ETH0.02%$4,453.370.0212$94.32
ETH0.02%$0.99361587.4606$86.9
ETH0.01%$0.554868143.3786$79.56
ETH0.01%$0.318745230.6368$73.51
ETH0.01%$0.484994133.7408$64.86
ETH0.01%$2.4626.1958$64.44
ETH0.01%$0.156871368.9567$57.88
ETH<0.01%$24.582.1676$53.28
ETH<0.01%$3.4514.7154$50.77
ETH<0.01%$0.99850750.6613$50.59
ETH<0.01%$0.00114843,735.6532$50.21
ETH<0.01%$91.490.5269$48.2
ETH<0.01%$69.540.6868$47.76
ETH<0.01%$4,224.420.011$46.33
ETH<0.01%$84.320.517$43.59
ETH<0.01%$0.0069675,825.5764$40.59
ETH<0.01%$0.097451404.1111$39.38
ETH<0.01%$0.96675540.6784$39.33
ETH<0.01%$0.299867127.3546$38.19
ETH<0.01%$33.761.0959$37
ETH<0.01%$0.04307854.6642$36.81
ETH<0.01%$0.13826247.0677$34.16
ETH<0.01%$3.48.9039$30.27
ETH<0.01%$0.00000125,332,871.5667$29.46
ETH<0.01%$0.166353173.0387$28.79
ETH
Ether (ETH)
<0.01%$3,821.020.00711678$27.19
ETH<0.01%$2.998.4325$25.21
ETH<0.01%$121.8264$21.89
ETH<0.01%$0.95393921.6146$20.62
ETH<0.01%$7.92.3587$18.63
ETH<0.01%$0.115303158.8306$18.31
ETH<0.01%$0.99315917.0268$16.91
ETH<0.01%$0.0134791,122.2508$15.13
ETH<0.01%$1.877.7953$14.58
ETH<0.01%$0.13191495.913$12.65
ETH<0.01%$0.094918127.4945$12.1
ETH<0.01%$0.9957810.922$10.88
ETH<0.01%$1.775.5171$9.77
ETH<0.01%$1.188.2616$9.75
ETH<0.01%$0.0296325.7993$9.64
ETH<0.01%$0.024307387.9257$9.43
ETH<0.01%$3.082.8173$8.68
ETH<0.01%$0.21469138.6275$8.29
ETH<0.01%$5.961.3613$8.11
ETH<0.01%$1.087.07$7.66
ETH<0.01%$0.9979057.3484$7.33
ETH<0.01%$0.030605210.1068$6.43
ETH<0.01%$0.6989269.1827$6.42
ETH<0.01%$1.793.5284$6.32
ETH<0.01%$0.14913740.5562$6.05
ETH<0.01%$33.460.161$5.39
ETH<0.01%$12.330.4291$5.29
ETH<0.01%$0.31231516.1801$5.05
ETH<0.01%$0.11912938.646$4.6
ETH<0.01%<$0.00000119,571,396.1686$3.78
ETH<0.01%$24.750.1511$3.74
ETH<0.01%$1.132.9922$3.37
ETH<0.01%$3,783.560.00085611$3.24
ETH<0.01%$4,087.970.00073654$3.01
ETH<0.01%$0.0024681,199.1678$2.96
ETH<0.01%$0.018404150.0253$2.76
ETH<0.01%$3.110.7755$2.41
ETH<0.01%$3.320.6361$2.11
ETH<0.01%$2.041$2.04
ETH<0.01%$0.03695655$2.03
ETH<0.01%$1.011.8838$1.9
ETH<0.01%$0.7009572.3741$1.66
ETH<0.01%$1.241.1424$1.41
ETH<0.01%$752,566.270.00000185$1.39
ETH<0.01%$0.12851810.4545$1.34
ETH<0.01%$3,963.60.00031657$1.25
ETH<0.01%$20.720.0539$1.12
ETH<0.01%$5.190.1904$0.9881
ETH<0.01%$0.1996384.2528$0.849
ETH<0.01%$0.5259291.4177$0.7455
ETH<0.01%$0.522511.3612$0.7112
ETH<0.01%$339.730.00178085$0.605
ETH<0.01%$67,6740.00000886$0.5995
ETH<0.01%$77.670.0076818$0.5966
ETH<0.01%$0.00508390.2384$0.4587
ETH<0.01%$0.0350111.5529$0.4044
ETH<0.01%$1.560.2297$0.3583
ETH<0.01%$0.2009361.7712$0.3559
ETH<0.01%$0.0665085.2869$0.3516
ETH<0.01%$0.9994120.3492$0.3489
ETH<0.01%$2.40.1428$0.3426
ETH<0.01%$84.210.00332167$0.2797
ETH<0.01%$0.2089671.3301$0.2779
ETH<0.01%$0.2958950.8522$0.2521
ETH<0.01%$7.090.0341$0.2419
ETH<0.01%$4.270.0505$0.2154
ETH<0.01%$2.10.0945$0.1984
ETH<0.01%$0.001549105.849$0.164
ETH<0.01%$0.00791620.7101$0.1639
ETH<0.01%$65.20.0022912$0.1493
ETH<0.01%$0.0658432.0526$0.1351
ETH<0.01%$16.650.00778808$0.1296
ETH<0.01%$72.910.00175214$0.1277
ETH<0.01%$0.00392532$0.1255
ETH<0.01%$3,961.910.00003084$0.1221
ETH<0.01%$3,810.070.00003158$0.1203
ETH<0.01%$0.0791251.5052$0.1191
ETH<0.01%$0.00126590.2708$0.1141
ETH<0.01%$474.130.00021549$0.1021
MATIC<0.01%$0.6985930.0033$0.002305
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.