ETH Price: $3,169.33 (+1.31%)
Gas: 2 Gwei

Contract

0xD8C38704c9937eA3312De29F824b4AD3450a5e61
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
0x60806040102518302020-06-12 15:58:441492 days ago1591977524IN
 Create: ERC20BridgeSampler
0 ETH0.1121472537

Advanced mode:
Parent Transaction Hash Block From To
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
ERC20BridgeSampler

Compiler Version
v0.5.17+commit.d19bba13

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion, None license

Contract Source Code (Solidity)

/**
 *Submitted for verification at Etherscan.io on 2020-06-12
*/

pragma solidity ^0.5.9;
pragma experimental ABIEncoderV2;

/*

  Copyright 2019 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.

*/

/*

  Copyright 2019 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.

*/

/*

  Copyright 2019 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.

*/

interface IUniswapExchange {

    /// @dev Buys at least `minTokensBought` tokens with ETH and transfer them
    ///      to `recipient`.
    /// @param minTokensBought The minimum number of tokens to buy.
    /// @param deadline Time when this order expires.
    /// @param recipient Who to transfer the tokens to.
    /// @return tokensBought Amount of tokens bought.
    function ethToTokenTransferInput(
        uint256 minTokensBought,
        uint256 deadline,
        address recipient
    )
        external
        payable
        returns (uint256 tokensBought);

    /// @dev Buys at least `minEthBought` ETH with tokens.
    /// @param tokensSold Amount of tokens to sell.
    /// @param minEthBought The minimum amount of ETH to buy.
    /// @param deadline Time when this order expires.
    /// @return ethBought Amount of tokens bought.
    function tokenToEthSwapInput(
        uint256 tokensSold,
        uint256 minEthBought,
        uint256 deadline
    )
        external
        returns (uint256 ethBought);

    /// @dev Buys at least `minTokensBought` tokens with the exchange token
    ///      and transfer them to `recipient`.
    /// @param minTokensBought The minimum number of tokens to buy.
    /// @param minEthBought The minimum amount of intermediate ETH to buy.
    /// @param deadline Time when this order expires.
    /// @param recipient Who to transfer the tokens to.
    /// @param toTokenAddress The token being bought.
    /// @return tokensBought Amount of tokens bought.
    function tokenToTokenTransferInput(
        uint256 tokensSold,
        uint256 minTokensBought,
        uint256 minEthBought,
        uint256 deadline,
        address recipient,
        address toTokenAddress
    )
        external
        returns (uint256 tokensBought);
}

interface IUniswapExchangeFactory {

    /// @dev Get the exchange for a token.
    /// @param tokenAddress The address of the token contract.
    function getExchange(address tokenAddress)
        external
        view
        returns (address);
}

/*

  Copyright 2019 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.

*/

/*

  Copyright 2019 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.

*/

library LibRichErrors {

    // bytes4(keccak256("Error(string)"))
    bytes4 internal constant STANDARD_ERROR_SELECTOR =
        0x08c379a0;

    // solhint-disable func-name-mixedcase
    /// @dev ABI encode a standard, string revert error payload.
    ///      This is the same payload that would be included by a `revert(string)`
    ///      solidity statement. It has the function signature `Error(string)`.
    /// @param message The error string.
    /// @return The ABI encoded error.
    function StandardError(
        string memory message
    )
        internal
        pure
        returns (bytes memory)
    {
        return abi.encodeWithSelector(
            STANDARD_ERROR_SELECTOR,
            bytes(message)
        );
    }
    // solhint-enable func-name-mixedcase

    /// @dev Reverts an encoded rich revert reason `errorData`.
    /// @param errorData ABI encoded error data.
    function rrevert(bytes memory errorData)
        internal
        pure
    {
        assembly {
            revert(add(errorData, 0x20), mload(errorData))
        }
    }
}

/*

  Copyright 2019 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.

*/

/*

  Copyright 2019 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.

*/

library LibBytesRichErrors {

    enum InvalidByteOperationErrorCodes {
        FromLessThanOrEqualsToRequired,
        ToLessThanOrEqualsLengthRequired,
        LengthGreaterThanZeroRequired,
        LengthGreaterThanOrEqualsFourRequired,
        LengthGreaterThanOrEqualsTwentyRequired,
        LengthGreaterThanOrEqualsThirtyTwoRequired,
        LengthGreaterThanOrEqualsNestedBytesLengthRequired,
        DestinationLengthGreaterThanOrEqualSourceLengthRequired
    }

    // bytes4(keccak256("InvalidByteOperationError(uint8,uint256,uint256)"))
    bytes4 internal constant INVALID_BYTE_OPERATION_ERROR_SELECTOR =
        0x28006595;

    // solhint-disable func-name-mixedcase
    function InvalidByteOperationError(
        InvalidByteOperationErrorCodes errorCode,
        uint256 offset,
        uint256 required
    )
        internal
        pure
        returns (bytes memory)
    {
        return abi.encodeWithSelector(
            INVALID_BYTE_OPERATION_ERROR_SELECTOR,
            errorCode,
            offset,
            required
        );
    }
}

library LibBytes {

    using LibBytes for bytes;

    /// @dev Gets the memory address for a byte array.
    /// @param input Byte array to lookup.
    /// @return memoryAddress Memory address of byte array. This
    ///         points to the header of the byte array which contains
    ///         the length.
    function rawAddress(bytes memory input)
        internal
        pure
        returns (uint256 memoryAddress)
    {
        assembly {
            memoryAddress := input
        }
        return memoryAddress;
    }

    /// @dev Gets the memory address for the contents of a byte array.
    /// @param input Byte array to lookup.
    /// @return memoryAddress Memory address of the contents of the byte array.
    function contentAddress(bytes memory input)
        internal
        pure
        returns (uint256 memoryAddress)
    {
        assembly {
            memoryAddress := add(input, 32)
        }
        return memoryAddress;
    }

    /// @dev Copies `length` bytes from memory location `source` to `dest`.
    /// @param dest memory address to copy bytes to.
    /// @param source memory address to copy bytes from.
    /// @param length number of bytes to copy.
    function memCopy(
        uint256 dest,
        uint256 source,
        uint256 length
    )
        internal
        pure
    {
        if (length < 32) {
            // Handle a partial word by reading destination and masking
            // off the bits we are interested in.
            // This correctly handles overlap, zero lengths and source == dest
            assembly {
                let mask := sub(exp(256, sub(32, length)), 1)
                let s := and(mload(source), not(mask))
                let d := and(mload(dest), mask)
                mstore(dest, or(s, d))
            }
        } else {
            // Skip the O(length) loop when source == dest.
            if (source == dest) {
                return;
            }

            // For large copies we copy whole words at a time. The final
            // word is aligned to the end of the range (instead of after the
            // previous) to handle partial words. So a copy will look like this:
            //
            //  ####
            //      ####
            //          ####
            //            ####
            //
            // We handle overlap in the source and destination range by
            // changing the copying direction. This prevents us from
            // overwriting parts of source that we still need to copy.
            //
            // This correctly handles source == dest
            //
            if (source > dest) {
                assembly {
                    // We subtract 32 from `sEnd` and `dEnd` because it
                    // is easier to compare with in the loop, and these
                    // are also the addresses we need for copying the
                    // last bytes.
                    length := sub(length, 32)
                    let sEnd := add(source, length)
                    let dEnd := add(dest, length)

                    // Remember the last 32 bytes of source
                    // This needs to be done here and not after the loop
                    // because we may have overwritten the last bytes in
                    // source already due to overlap.
                    let last := mload(sEnd)

                    // Copy whole words front to back
                    // Note: the first check is always true,
                    // this could have been a do-while loop.
                    // solhint-disable-next-line no-empty-blocks
                    for {} lt(source, sEnd) {} {
                        mstore(dest, mload(source))
                        source := add(source, 32)
                        dest := add(dest, 32)
                    }

                    // Write the last 32 bytes
                    mstore(dEnd, last)
                }
            } else {
                assembly {
                    // We subtract 32 from `sEnd` and `dEnd` because those
                    // are the starting points when copying a word at the end.
                    length := sub(length, 32)
                    let sEnd := add(source, length)
                    let dEnd := add(dest, length)

                    // Remember the first 32 bytes of source
                    // This needs to be done here and not after the loop
                    // because we may have overwritten the first bytes in
                    // source already due to overlap.
                    let first := mload(source)

                    // Copy whole words back to front
                    // We use a signed comparisson here to allow dEnd to become
                    // negative (happens when source and dest < 32). Valid
                    // addresses in local memory will never be larger than
                    // 2**255, so they can be safely re-interpreted as signed.
                    // Note: the first check is always true,
                    // this could have been a do-while loop.
                    // solhint-disable-next-line no-empty-blocks
                    for {} slt(dest, dEnd) {} {
                        mstore(dEnd, mload(sEnd))
                        sEnd := sub(sEnd, 32)
                        dEnd := sub(dEnd, 32)
                    }

                    // Write the first 32 bytes
                    mstore(dest, first)
                }
            }
        }
    }

    /// @dev Returns a slices from a byte array.
    /// @param b The byte array to take a slice from.
    /// @param from The starting index for the slice (inclusive).
    /// @param to The final index for the slice (exclusive).
    /// @return result The slice containing bytes at indices [from, to)
    function slice(
        bytes memory b,
        uint256 from,
        uint256 to
    )
        internal
        pure
        returns (bytes memory result)
    {
        // Ensure that the from and to positions are valid positions for a slice within
        // the byte array that is being used.
        if (from > to) {
            LibRichErrors.rrevert(LibBytesRichErrors.InvalidByteOperationError(
                LibBytesRichErrors.InvalidByteOperationErrorCodes.FromLessThanOrEqualsToRequired,
                from,
                to
            ));
        }
        if (to > b.length) {
            LibRichErrors.rrevert(LibBytesRichErrors.InvalidByteOperationError(
                LibBytesRichErrors.InvalidByteOperationErrorCodes.ToLessThanOrEqualsLengthRequired,
                to,
                b.length
            ));
        }

        // Create a new bytes structure and copy contents
        result = new bytes(to - from);
        memCopy(
            result.contentAddress(),
            b.contentAddress() + from,
            result.length
        );
        return result;
    }

    /// @dev Returns a slice from a byte array without preserving the input.
    /// @param b The byte array to take a slice from. Will be destroyed in the process.
    /// @param from The starting index for the slice (inclusive).
    /// @param to The final index for the slice (exclusive).
    /// @return result The slice containing bytes at indices [from, to)
    /// @dev When `from == 0`, the original array will match the slice. In other cases its state will be corrupted.
    function sliceDestructive(
        bytes memory b,
        uint256 from,
        uint256 to
    )
        internal
        pure
        returns (bytes memory result)
    {
        // Ensure that the from and to positions are valid positions for a slice within
        // the byte array that is being used.
        if (from > to) {
            LibRichErrors.rrevert(LibBytesRichErrors.InvalidByteOperationError(
                LibBytesRichErrors.InvalidByteOperationErrorCodes.FromLessThanOrEqualsToRequired,
                from,
                to
            ));
        }
        if (to > b.length) {
            LibRichErrors.rrevert(LibBytesRichErrors.InvalidByteOperationError(
                LibBytesRichErrors.InvalidByteOperationErrorCodes.ToLessThanOrEqualsLengthRequired,
                to,
                b.length
            ));
        }

        // Create a new bytes structure around [from, to) in-place.
        assembly {
            result := add(b, from)
            mstore(result, sub(to, from))
        }
        return result;
    }

    /// @dev Pops the last byte off of a byte array by modifying its length.
    /// @param b Byte array that will be modified.
    /// @return The byte that was popped off.
    function popLastByte(bytes memory b)
        internal
        pure
        returns (bytes1 result)
    {
        if (b.length == 0) {
            LibRichErrors.rrevert(LibBytesRichErrors.InvalidByteOperationError(
                LibBytesRichErrors.InvalidByteOperationErrorCodes.LengthGreaterThanZeroRequired,
                b.length,
                0
            ));
        }

        // 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 Tests equality of two byte arrays.
    /// @param lhs First byte array to compare.
    /// @param rhs Second byte array to compare.
    /// @return True if arrays are the same. False otherwise.
    function equals(
        bytes memory lhs,
        bytes memory rhs
    )
        internal
        pure
        returns (bool equal)
    {
        // Keccak gas cost is 30 + numWords * 6. This is a cheap way to compare.
        // We early exit on unequal lengths, but keccak would also correctly
        // handle this.
        return lhs.length == rhs.length && keccak256(lhs) == keccak256(rhs);
    }

    /// @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 address from byte array.
    function readAddress(
        bytes memory b,
        uint256 index
    )
        internal
        pure
        returns (address result)
    {
        if (b.length < index + 20) {
            LibRichErrors.rrevert(LibBytesRichErrors.InvalidByteOperationError(
                LibBytesRichErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsTwentyRequired,
                b.length,
                index + 20 // 20 is length of address
            ));
        }

        // 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;
    }

    /// @dev Writes an address into a specific position in a byte array.
    /// @param b Byte array to insert address into.
    /// @param index Index in byte array of address.
    /// @param input Address to put into byte array.
    function writeAddress(
        bytes memory b,
        uint256 index,
        address input
    )
        internal
        pure
    {
        if (b.length < index + 20) {
            LibRichErrors.rrevert(LibBytesRichErrors.InvalidByteOperationError(
                LibBytesRichErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsTwentyRequired,
                b.length,
                index + 20 // 20 is length of address
            ));
        }

        // 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;

        // Store address into array memory
        assembly {
            // The address occupies 20 bytes and mstore stores 32 bytes.
            // First fetch the 32-byte word where we'll be storing the address, then
            // apply a mask so we have only the bytes in the word that the address will not occupy.
            // Then combine these bytes with the address and store the 32 bytes back to memory with mstore.

            // 1. Add index to address of bytes array
            // 2. Load 32-byte word from memory
            // 3. Apply 12-byte mask to obtain extra bytes occupying word of memory where we'll store the address
            let neighbors := and(
                mload(add(b, index)),
                0xffffffffffffffffffffffff0000000000000000000000000000000000000000
            )

            // Make sure input address is clean.
            // (Solidity does not guarantee this)
            input := and(input, 0xffffffffffffffffffffffffffffffffffffffff)

            // Store the neighbors and address into memory
            mstore(add(b, index), xor(input, neighbors))
        }
    }

    /// @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 bytes32 value from byte array.
    function readBytes32(
        bytes memory b,
        uint256 index
    )
        internal
        pure
        returns (bytes32 result)
    {
        if (b.length < index + 32) {
            LibRichErrors.rrevert(LibBytesRichErrors.InvalidByteOperationError(
                LibBytesRichErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsThirtyTwoRequired,
                b.length,
                index + 32
            ));
        }

        // 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 Writes a bytes32 into a specific position in a byte array.
    /// @param b Byte array to insert <input> into.
    /// @param index Index in byte array of <input>.
    /// @param input bytes32 to put into byte array.
    function writeBytes32(
        bytes memory b,
        uint256 index,
        bytes32 input
    )
        internal
        pure
    {
        if (b.length < index + 32) {
            LibRichErrors.rrevert(LibBytesRichErrors.InvalidByteOperationError(
                LibBytesRichErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsThirtyTwoRequired,
                b.length,
                index + 32
            ));
        }

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

        // Read the bytes32 from array memory
        assembly {
            mstore(add(b, index), input)
        }
    }

    /// @dev Reads a uint256 value from a position in a byte array.
    /// @param b Byte array containing a uint256 value.
    /// @param index Index in byte array of uint256 value.
    /// @return uint256 value from byte array.
    function readUint256(
        bytes memory b,
        uint256 index
    )
        internal
        pure
        returns (uint256 result)
    {
        result = uint256(readBytes32(b, index));
        return result;
    }

    /// @dev Writes a uint256 into a specific position in a byte array.
    /// @param b Byte array to insert <input> into.
    /// @param index Index in byte array of <input>.
    /// @param input uint256 to put into byte array.
    function writeUint256(
        bytes memory b,
        uint256 index,
        uint256 input
    )
        internal
        pure
    {
        writeBytes32(b, index, bytes32(input));
    }

    /// @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 bytes4 value from byte array.
    function readBytes4(
        bytes memory b,
        uint256 index
    )
        internal
        pure
        returns (bytes4 result)
    {
        if (b.length < index + 4) {
            LibRichErrors.rrevert(LibBytesRichErrors.InvalidByteOperationError(
                LibBytesRichErrors.InvalidByteOperationErrorCodes.LengthGreaterThanOrEqualsFourRequired,
                b.length,
                index + 4
            ));
        }

        // 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;
    }

    /// @dev Writes a new length to a byte array.
    ///      Decreasing length will lead to removing the corresponding lower order bytes from the byte array.
    ///      Increasing length may lead to appending adjacent in-memory bytes to the end of the byte array.
    /// @param b Bytes array to write new length to.
    /// @param length New length of byte array.
    function writeLength(bytes memory b, uint256 length)
        internal
        pure
    {
        assembly {
            mstore(b, length)
        }
    }
}

/*

  Copyright 2019 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.

*/

contract IERC20Token {

    // solhint-disable no-simple-event-func-name
    event Transfer(
        address indexed _from,
        address indexed _to,
        uint256 _value
    );

    event Approval(
        address indexed _owner,
        address indexed _spender,
        uint256 _value
    );

    /// @dev send `value` token to `to` from `msg.sender`
    /// @param _to The address of the recipient
    /// @param _value The amount of token to be transferred
    /// @return True if transfer was successful
    function transfer(address _to, uint256 _value)
        external
        returns (bool);

    /// @dev send `value` token to `to` from `from` on the condition it is approved by `from`
    /// @param _from The address of the sender
    /// @param _to The address of the recipient
    /// @param _value The amount of token to be transferred
    /// @return True if transfer was successful
    function transferFrom(
        address _from,
        address _to,
        uint256 _value
    )
        external
        returns (bool);

    /// @dev `msg.sender` approves `_spender` to spend `_value` tokens
    /// @param _spender The address of the account able to transfer the tokens
    /// @param _value The amount of wei to be approved for transfer
    /// @return Always true if the call has enough gas to complete execution
    function approve(address _spender, uint256 _value)
        external
        returns (bool);

    /// @dev Query total supply of token
    /// @return Total supply of token
    function totalSupply()
        external
        view
        returns (uint256);

    /// @param _owner The address from which the balance will be retrieved
    /// @return Balance of owner
    function balanceOf(address _owner)
        external
        view
        returns (uint256);

    /// @param _owner The address of the account owning tokens
    /// @param _spender The address of the account able to transfer the tokens
    /// @return Amount of remaining tokens allowed to spent
    function allowance(address _owner, address _spender)
        external
        view
        returns (uint256);
}

library LibERC20Token {
    bytes constant private DECIMALS_CALL_DATA = hex"313ce567";

    /// @dev Calls `IERC20Token(token).approve()`.
    ///      Reverts if `false` is returned or if the return
    ///      data length is nonzero and not 32 bytes.
    /// @param token The address of the token contract.
    /// @param spender The address that receives an allowance.
    /// @param allowance The allowance to set.
    function approve(
        address token,
        address spender,
        uint256 allowance
    )
        internal
    {
        bytes memory callData = abi.encodeWithSelector(
            IERC20Token(0).approve.selector,
            spender,
            allowance
        );
        _callWithOptionalBooleanResult(token, callData);
    }

    /// @dev Calls `IERC20Token(token).approve()` and sets the allowance to the
    ///      maximum if the current approval is not already >= an amount.
    ///      Reverts if `false` is returned or if the return
    ///      data length is nonzero and not 32 bytes.
    /// @param token The address of the token contract.
    /// @param spender The address that receives an allowance.
    /// @param amount The minimum allowance needed.
    function approveIfBelow(
        address token,
        address spender,
        uint256 amount
    )
        internal
    {
        if (IERC20Token(token).allowance(address(this), spender) < amount) {
            approve(token, spender, uint256(-1));
        }
    }

    /// @dev Calls `IERC20Token(token).transfer()`.
    ///      Reverts if `false` is returned or if the return
    ///      data length is nonzero and not 32 bytes.
    /// @param token The address of the token contract.
    /// @param to The address that receives the tokens
    /// @param amount Number of tokens to transfer.
    function transfer(
        address token,
        address to,
        uint256 amount
    )
        internal
    {
        bytes memory callData = abi.encodeWithSelector(
            IERC20Token(0).transfer.selector,
            to,
            amount
        );
        _callWithOptionalBooleanResult(token, callData);
    }

    /// @dev Calls `IERC20Token(token).transferFrom()`.
    ///      Reverts if `false` is returned or if the return
    ///      data length is nonzero and not 32 bytes.
    /// @param token The address of the token contract.
    /// @param from The owner of the tokens.
    /// @param to The address that receives the tokens
    /// @param amount Number of tokens to transfer.
    function transferFrom(
        address token,
        address from,
        address to,
        uint256 amount
    )
        internal
    {
        bytes memory callData = abi.encodeWithSelector(
            IERC20Token(0).transferFrom.selector,
            from,
            to,
            amount
        );
        _callWithOptionalBooleanResult(token, callData);
    }

    /// @dev Retrieves the number of decimals for a token.
    ///      Returns `18` if the call reverts.
    /// @param token The address of the token contract.
    /// @return tokenDecimals The number of decimals places for the token.
    function decimals(address token)
        internal
        view
        returns (uint8 tokenDecimals)
    {
        tokenDecimals = 18;
        (bool didSucceed, bytes memory resultData) = token.staticcall(DECIMALS_CALL_DATA);
        if (didSucceed && resultData.length == 32) {
            tokenDecimals = uint8(LibBytes.readUint256(resultData, 0));
        }
    }

    /// @dev Retrieves the allowance for a token, owner, and spender.
    ///      Returns `0` if the call reverts.
    /// @param token The address of the token contract.
    /// @param owner The owner of the tokens.
    /// @param spender The address the spender.
    /// @return allowance The allowance for a token, owner, and spender.
    function allowance(address token, address owner, address spender)
        internal
        view
        returns (uint256 allowance_)
    {
        (bool didSucceed, bytes memory resultData) = token.staticcall(
            abi.encodeWithSelector(
                IERC20Token(0).allowance.selector,
                owner,
                spender
            )
        );
        if (didSucceed && resultData.length == 32) {
            allowance_ = LibBytes.readUint256(resultData, 0);
        }
    }

    /// @dev Retrieves the balance for a token owner.
    ///      Returns `0` if the call reverts.
    /// @param token The address of the token contract.
    /// @param owner The owner of the tokens.
    /// @return balance The token balance of an owner.
    function balanceOf(address token, address owner)
        internal
        view
        returns (uint256 balance)
    {
        (bool didSucceed, bytes memory resultData) = token.staticcall(
            abi.encodeWithSelector(
                IERC20Token(0).balanceOf.selector,
                owner
            )
        );
        if (didSucceed && resultData.length == 32) {
            balance = LibBytes.readUint256(resultData, 0);
        }
    }

    /// @dev Executes a call on address `target` with calldata `callData`
    ///      and asserts that either nothing was returned or a single boolean
    ///      was returned equal to `true`.
    /// @param target The call target.
    /// @param callData The abi-encoded call data.
    function _callWithOptionalBooleanResult(
        address target,
        bytes memory callData
    )
        private
    {
        (bool didSucceed, bytes memory resultData) = target.call(callData);
        if (didSucceed) {
            if (resultData.length == 0) {
                return;
            }
            if (resultData.length == 32) {
                uint256 result = LibBytes.readUint256(resultData, 0);
                if (result == 1) {
                    return;
                }
            }
        }
        LibRichErrors.rrevert(resultData);
    }
}

/*

  Copyright 2019 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.

*/

/*

  Copyright 2019 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.

*/

library LibEIP712 {

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

    /// @dev Calculates a EIP712 domain separator.
    /// @param name The EIP712 domain name.
    /// @param version The EIP712 domain version.
    /// @param verifyingContract The EIP712 verifying contract.
    /// @return EIP712 domain separator.
    function hashEIP712Domain(
        string memory name,
        string memory version,
        uint256 chainId,
        address verifyingContract
    )
        internal
        pure
        returns (bytes32 result)
    {
        bytes32 schemaHash = _EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH;

        // Assembly for more efficient computing:
        // keccak256(abi.encodePacked(
        //     _EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH,
        //     keccak256(bytes(name)),
        //     keccak256(bytes(version)),
        //     chainId,
        //     uint256(verifyingContract)
        // ))

        assembly {
            // Calculate hashes of dynamic data
            let nameHash := keccak256(add(name, 32), mload(name))
            let versionHash := keccak256(add(version, 32), mload(version))

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

            // Store params in memory
            mstore(memPtr, schemaHash)
            mstore(add(memPtr, 32), nameHash)
            mstore(add(memPtr, 64), versionHash)
            mstore(add(memPtr, 96), chainId)
            mstore(add(memPtr, 128), verifyingContract)

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

    /// @dev Calculates EIP712 encoding for a hash struct with a given domain hash.
    /// @param eip712DomainHash Hash of the domain domain separator data, computed
    ///                         with getDomainHash().
    /// @param hashStruct The EIP712 hash struct.
    /// @return EIP712 hash applied to the given EIP712 Domain.
    function hashEIP712Message(bytes32 eip712DomainHash, bytes32 hashStruct)
        internal
        pure
        returns (bytes32 result)
    {
        // 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;
    }
}

library LibOrder {

    using LibOrder for Order;

    // Hash for the EIP712 Order Schema:
    // 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,",
    //     "bytes makerFeeAssetData,",
    //     "bytes takerFeeAssetData",
    //     ")"
    // ))
    bytes32 constant internal _EIP712_ORDER_SCHEMA_HASH =
        0xf80322eb8376aafb64eadf8f0d7623f22130fd9491a221e902b713cb984a7534;

    // A valid order remains fillable until it is expired, fully filled, or cancelled.
    // An order's status 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
    /// @dev Canonical order structure.
    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;               // Fee paid to feeRecipient by maker when order is filled.
        uint256 takerFee;               // Fee paid to feeRecipient by taker when order is filled.
        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 leading bytes4 references the id of the asset proxy.
        bytes takerAssetData;           // Encoded data that can be decoded by a specified proxy contract when transferring takerAsset. The leading bytes4 references the id of the asset proxy.
        bytes makerFeeAssetData;        // Encoded data that can be decoded by a specified proxy contract when transferring makerFeeAsset. The leading bytes4 references the id of the asset proxy.
        bytes takerFeeAssetData;        // Encoded data that can be decoded by a specified proxy contract when transferring takerFeeAsset. The leading bytes4 references the id of the asset proxy.
    }
    // solhint-enable max-line-length

    /// @dev Order information returned by `getOrderInfo()`.
    struct OrderInfo {
        OrderStatus orderStatus;                    // Status that describes order's validity and fillability.
        bytes32 orderHash;                    // EIP712 typed data hash of the order (see LibOrder.getTypedDataHash).
        uint256 orderTakerAssetFilledAmount;  // Amount of order that has already been filled.
    }

    /// @dev Calculates the EIP712 typed data hash of an order with a given domain separator.
    /// @param order The order structure.
    /// @return EIP712 typed data hash of the order.
    function getTypedDataHash(Order memory order, bytes32 eip712ExchangeDomainHash)
        internal
        pure
        returns (bytes32 orderHash)
    {
        orderHash = LibEIP712.hashEIP712Message(
            eip712ExchangeDomainHash,
            order.getStructHash()
        );
        return orderHash;
    }

    /// @dev Calculates EIP712 hash of the order struct.
    /// @param order The order structure.
    /// @return EIP712 hash of the order struct.
    function getStructHash(Order memory order)
        internal
        pure
        returns (bytes32 result)
    {
        bytes32 schemaHash = _EIP712_ORDER_SCHEMA_HASH;
        bytes memory makerAssetData = order.makerAssetData;
        bytes memory takerAssetData = order.takerAssetData;
        bytes memory makerFeeAssetData = order.makerFeeAssetData;
        bytes memory takerFeeAssetData = order.takerFeeAssetData;

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

        assembly {
            // Assert order offset (this is an internal error that should never be triggered)
            if lt(order, 32) {
                invalid()
            }

            // 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)
            let pos4 := add(order, 384)
            let pos5 := add(order, 416)

            // Backup
            let temp1 := mload(pos1)
            let temp2 := mload(pos2)
            let temp3 := mload(pos3)
            let temp4 := mload(pos4)
            let temp5 := mload(pos5)

            // Hash in place
            mstore(pos1, schemaHash)
            mstore(pos2, keccak256(add(makerAssetData, 32), mload(makerAssetData)))        // store hash of makerAssetData
            mstore(pos3, keccak256(add(takerAssetData, 32), mload(takerAssetData)))        // store hash of takerAssetData
            mstore(pos4, keccak256(add(makerFeeAssetData, 32), mload(makerFeeAssetData)))  // store hash of makerFeeAssetData
            mstore(pos5, keccak256(add(takerFeeAssetData, 32), mload(takerFeeAssetData)))  // store hash of takerFeeAssetData
            result := keccak256(pos1, 480)

            // Restore
            mstore(pos1, temp1)
            mstore(pos2, temp2)
            mstore(pos3, temp3)
            mstore(pos4, temp4)
            mstore(pos5, temp5)
        }
        return result;
    }
}

/*

  Copyright 2019 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.

*/

library LibSafeMathRichErrors {

    // bytes4(keccak256("Uint256BinOpError(uint8,uint256,uint256)"))
    bytes4 internal constant UINT256_BINOP_ERROR_SELECTOR =
        0xe946c1bb;

    // bytes4(keccak256("Uint256DowncastError(uint8,uint256)"))
    bytes4 internal constant UINT256_DOWNCAST_ERROR_SELECTOR =
        0xc996af7b;

    enum BinOpErrorCodes {
        ADDITION_OVERFLOW,
        MULTIPLICATION_OVERFLOW,
        SUBTRACTION_UNDERFLOW,
        DIVISION_BY_ZERO
    }

    enum DowncastErrorCodes {
        VALUE_TOO_LARGE_TO_DOWNCAST_TO_UINT32,
        VALUE_TOO_LARGE_TO_DOWNCAST_TO_UINT64,
        VALUE_TOO_LARGE_TO_DOWNCAST_TO_UINT96
    }

    // solhint-disable func-name-mixedcase
    function Uint256BinOpError(
        BinOpErrorCodes errorCode,
        uint256 a,
        uint256 b
    )
        internal
        pure
        returns (bytes memory)
    {
        return abi.encodeWithSelector(
            UINT256_BINOP_ERROR_SELECTOR,
            errorCode,
            a,
            b
        );
    }

    function Uint256DowncastError(
        DowncastErrorCodes errorCode,
        uint256 a
    )
        internal
        pure
        returns (bytes memory)
    {
        return abi.encodeWithSelector(
            UINT256_DOWNCAST_ERROR_SELECTOR,
            errorCode,
            a
        );
    }
}

library LibSafeMath {

    function safeMul(uint256 a, uint256 b)
        internal
        pure
        returns (uint256)
    {
        if (a == 0) {
            return 0;
        }
        uint256 c = a * b;
        if (c / a != b) {
            LibRichErrors.rrevert(LibSafeMathRichErrors.Uint256BinOpError(
                LibSafeMathRichErrors.BinOpErrorCodes.MULTIPLICATION_OVERFLOW,
                a,
                b
            ));
        }
        return c;
    }

    function safeDiv(uint256 a, uint256 b)
        internal
        pure
        returns (uint256)
    {
        if (b == 0) {
            LibRichErrors.rrevert(LibSafeMathRichErrors.Uint256BinOpError(
                LibSafeMathRichErrors.BinOpErrorCodes.DIVISION_BY_ZERO,
                a,
                b
            ));
        }
        uint256 c = a / b;
        return c;
    }

    function safeSub(uint256 a, uint256 b)
        internal
        pure
        returns (uint256)
    {
        if (b > a) {
            LibRichErrors.rrevert(LibSafeMathRichErrors.Uint256BinOpError(
                LibSafeMathRichErrors.BinOpErrorCodes.SUBTRACTION_UNDERFLOW,
                a,
                b
            ));
        }
        return a - b;
    }

    function safeAdd(uint256 a, uint256 b)
        internal
        pure
        returns (uint256)
    {
        uint256 c = a + b;
        if (c < a) {
            LibRichErrors.rrevert(LibSafeMathRichErrors.Uint256BinOpError(
                LibSafeMathRichErrors.BinOpErrorCodes.ADDITION_OVERFLOW,
                a,
                b
            ));
        }
        return c;
    }

    function max256(uint256 a, uint256 b)
        internal
        pure
        returns (uint256)
    {
        return a >= b ? a : b;
    }

    function min256(uint256 a, uint256 b)
        internal
        pure
        returns (uint256)
    {
        return a < b ? a : b;
    }
}

library LibMathRichErrors {

    // bytes4(keccak256("DivisionByZeroError()"))
    bytes internal constant DIVISION_BY_ZERO_ERROR =
        hex"a791837c";

    // bytes4(keccak256("RoundingError(uint256,uint256,uint256)"))
    bytes4 internal constant ROUNDING_ERROR_SELECTOR =
        0x339f3de2;

    // solhint-disable func-name-mixedcase
    function DivisionByZeroError()
        internal
        pure
        returns (bytes memory)
    {
        return DIVISION_BY_ZERO_ERROR;
    }

    function RoundingError(
        uint256 numerator,
        uint256 denominator,
        uint256 target
    )
        internal
        pure
        returns (bytes memory)
    {
        return abi.encodeWithSelector(
            ROUNDING_ERROR_SELECTOR,
            numerator,
            denominator,
            target
        );
    }
}

library LibMath {

    using LibSafeMath for uint256;

    /// @dev Calculates partial value given a numerator and denominator rounded down.
    ///      Reverts if rounding error is >= 0.1%
    /// @param numerator Numerator.
    /// @param denominator Denominator.
    /// @param target Value to calculate partial of.
    /// @return Partial value of target rounded down.
    function safeGetPartialAmountFloor(
        uint256 numerator,
        uint256 denominator,
        uint256 target
    )
        internal
        pure
        returns (uint256 partialAmount)
    {
        if (isRoundingErrorFloor(
                numerator,
                denominator,
                target
        )) {
            LibRichErrors.rrevert(LibMathRichErrors.RoundingError(
                numerator,
                denominator,
                target
            ));
        }

        partialAmount = numerator.safeMul(target).safeDiv(denominator);
        return partialAmount;
    }

    /// @dev Calculates partial value given a numerator and denominator rounded down.
    ///      Reverts if rounding error is >= 0.1%
    /// @param numerator Numerator.
    /// @param denominator Denominator.
    /// @param target Value to calculate partial of.
    /// @return Partial value of target rounded up.
    function safeGetPartialAmountCeil(
        uint256 numerator,
        uint256 denominator,
        uint256 target
    )
        internal
        pure
        returns (uint256 partialAmount)
    {
        if (isRoundingErrorCeil(
                numerator,
                denominator,
                target
        )) {
            LibRichErrors.rrevert(LibMathRichErrors.RoundingError(
                numerator,
                denominator,
                target
            ));
        }

        // safeDiv computes `floor(a / b)`. We use the identity (a, b integer):
        //       ceil(a / b) = floor((a + b - 1) / b)
        // To implement `ceil(a / b)` using safeDiv.
        partialAmount = numerator.safeMul(target)
            .safeAdd(denominator.safeSub(1))
            .safeDiv(denominator);

        return partialAmount;
    }

    /// @dev Calculates partial value given a numerator and denominator rounded down.
    /// @param numerator Numerator.
    /// @param denominator Denominator.
    /// @param target Value to calculate partial of.
    /// @return Partial value of target rounded down.
    function getPartialAmountFloor(
        uint256 numerator,
        uint256 denominator,
        uint256 target
    )
        internal
        pure
        returns (uint256 partialAmount)
    {
        partialAmount = numerator.safeMul(target).safeDiv(denominator);
        return partialAmount;
    }

    /// @dev Calculates partial value given a numerator and denominator rounded down.
    /// @param numerator Numerator.
    /// @param denominator Denominator.
    /// @param target Value to calculate partial of.
    /// @return Partial value of target rounded up.
    function getPartialAmountCeil(
        uint256 numerator,
        uint256 denominator,
        uint256 target
    )
        internal
        pure
        returns (uint256 partialAmount)
    {
        // safeDiv computes `floor(a / b)`. We use the identity (a, b integer):
        //       ceil(a / b) = floor((a + b - 1) / b)
        // To implement `ceil(a / b)` using safeDiv.
        partialAmount = numerator.safeMul(target)
            .safeAdd(denominator.safeSub(1))
            .safeDiv(denominator);

        return partialAmount;
    }

    /// @dev Checks if rounding error >= 0.1% when rounding down.
    /// @param numerator Numerator.
    /// @param denominator Denominator.
    /// @param target Value to multiply with numerator/denominator.
    /// @return Rounding error is present.
    function isRoundingErrorFloor(
        uint256 numerator,
        uint256 denominator,
        uint256 target
    )
        internal
        pure
        returns (bool isError)
    {
        if (denominator == 0) {
            LibRichErrors.rrevert(LibMathRichErrors.DivisionByZeroError());
        }

        // The absolute rounding error is the difference between the rounded
        // value and the ideal value. The relative rounding error is the
        // absolute rounding error divided by the absolute value of the
        // ideal value. This is undefined when the ideal value is zero.
        //
        // The ideal value is `numerator * target / denominator`.
        // Let's call `numerator * target % denominator` the remainder.
        // The absolute error is `remainder / denominator`.
        //
        // When the ideal value is zero, we require the absolute error to
        // be zero. Fortunately, this is always the case. The ideal value is
        // zero iff `numerator == 0` and/or `target == 0`. In this case the
        // remainder and absolute error are also zero.
        if (target == 0 || numerator == 0) {
            return false;
        }

        // Otherwise, we want the relative rounding error to be strictly
        // less than 0.1%.
        // The relative error is `remainder / (numerator * target)`.
        // We want the relative error less than 1 / 1000:
        //        remainder / (numerator * denominator)  <  1 / 1000
        // or equivalently:
        //        1000 * remainder  <  numerator * target
        // so we have a rounding error iff:
        //        1000 * remainder  >=  numerator * target
        uint256 remainder = mulmod(
            target,
            numerator,
            denominator
        );
        isError = remainder.safeMul(1000) >= numerator.safeMul(target);
        return isError;
    }

    /// @dev Checks if rounding error >= 0.1% when rounding up.
    /// @param numerator Numerator.
    /// @param denominator Denominator.
    /// @param target Value to multiply with numerator/denominator.
    /// @return Rounding error is present.
    function isRoundingErrorCeil(
        uint256 numerator,
        uint256 denominator,
        uint256 target
    )
        internal
        pure
        returns (bool isError)
    {
        if (denominator == 0) {
            LibRichErrors.rrevert(LibMathRichErrors.DivisionByZeroError());
        }

        // See the comments in `isRoundingError`.
        if (target == 0 || numerator == 0) {
            // When either is zero, the ideal value and rounded value are zero
            // and there is no rounding error. (Although the relative error
            // is undefined.)
            return false;
        }
        // Compute remainder as before
        uint256 remainder = mulmod(
            target,
            numerator,
            denominator
        );
        remainder = denominator.safeSub(remainder) % denominator;
        isError = remainder.safeMul(1000) >= numerator.safeMul(target);
        return isError;
    }
}

/*

  Copyright 2019 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.

*/

contract DeploymentConstants {
    /// @dev Mainnet address of the WETH contract.
    address constant private WETH_ADDRESS = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
    // /// @dev Kovan address of the WETH contract.
    // address constant private WETH_ADDRESS = 0xd0A1E359811322d97991E03f863a0C30C2cF029C;
    /// @dev Mainnet address of the KyberNetworkProxy contract.
    address constant private KYBER_NETWORK_PROXY_ADDRESS = 0x818E6FECD516Ecc3849DAf6845e3EC868087B755;
    // /// @dev Kovan address of the KyberNetworkProxy contract.
    // address constant private KYBER_NETWORK_PROXY_ADDRESS = 0x692f391bCc85cefCe8C237C01e1f636BbD70EA4D;
    /// @dev Mainnet address of the `UniswapExchangeFactory` contract.
    address constant private UNISWAP_EXCHANGE_FACTORY_ADDRESS = 0xc0a47dFe034B400B47bDaD5FecDa2621de6c4d95;
    // /// @dev Kovan address of the `UniswapExchangeFactory` contract.
    // address constant private UNISWAP_EXCHANGE_FACTORY_ADDRESS = 0xD3E51Ef092B2845f10401a0159B2B96e8B6c3D30;
    /// @dev Mainnet address of the `UniswapV2Router01` contract.
    address constant private UNISWAP_V2_ROUTER_01_ADDRESS = 0xf164fC0Ec4E93095b804a4795bBe1e041497b92a;
    // /// @dev Kovan address of the `UniswapV2Router01` contract.
    // address constant private UNISWAP_V2_ROUTER_01_ADDRESS = 0xf164fC0Ec4E93095b804a4795bBe1e041497b92a;
    /// @dev Mainnet address of the Eth2Dai `MatchingMarket` contract.
    address constant private ETH2DAI_ADDRESS = 0x794e6e91555438aFc3ccF1c5076A74F42133d08D;
    // /// @dev Kovan address of the Eth2Dai `MatchingMarket` contract.
    // address constant private ETH2DAI_ADDRESS = 0xe325acB9765b02b8b418199bf9650972299235F4;
    /// @dev Mainnet address of the `ERC20BridgeProxy` contract
    address constant private ERC20_BRIDGE_PROXY_ADDRESS = 0x8ED95d1746bf1E4dAb58d8ED4724f1Ef95B20Db0;
    // /// @dev Kovan address of the `ERC20BridgeProxy` contract
    // address constant private ERC20_BRIDGE_PROXY_ADDRESS = 0xFb2DD2A1366dE37f7241C83d47DA58fd503E2C64;
    ///@dev Mainnet address of the `Dai` (multi-collateral) contract
    address constant private DAI_ADDRESS = 0x6B175474E89094C44Da98b954EedeAC495271d0F;
    // ///@dev Kovan address of the `Dai` (multi-collateral) contract
    // address constant private DAI_ADDRESS = 0x4F96Fe3b7A6Cf9725f59d353F723c1bDb64CA6Aa;
    /// @dev Mainnet address of the `Chai` contract
    address constant private CHAI_ADDRESS = 0x06AF07097C9Eeb7fD685c692751D5C66dB49c215;
    /// @dev Mainnet address of the 0x DevUtils contract.
    address constant private DEV_UTILS_ADDRESS = 0x74134CF88b21383713E096a5ecF59e297dc7f547;
    // /// @dev Kovan address of the 0x DevUtils contract.
    // address constant private DEV_UTILS_ADDRESS = 0x9402639A828BdF4E9e4103ac3B69E1a6E522eB59;
    /// @dev Kyber ETH pseudo-address.
    address constant internal KYBER_ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
    /// @dev Mainnet address of the dYdX contract.
    address constant private DYDX_ADDRESS = 0x1E0447b19BB6EcFdAe1e4AE1694b0C3659614e4e;
    /// @dev Mainnet address of the GST2 contract
    address constant private GST_ADDRESS = 0x0000000000b3F879cb30FE243b4Dfee438691c04;
    /// @dev Mainnet address of the GST Collector
    address constant private GST_COLLECTOR_ADDRESS = 0x000000D3b08566BE75A6DB803C03C85C0c1c5B96;
    // /// @dev Kovan address of the GST2 contract
    // address constant private GST_ADDRESS = address(0);
    // /// @dev Kovan address of the GST Collector
    // address constant private GST_COLLECTOR_ADDRESS = address(0);

    /// @dev Overridable way to get the `KyberNetworkProxy` address.
    /// @return kyberAddress The `IKyberNetworkProxy` address.
    function _getKyberNetworkProxyAddress()
        internal
        view
        returns (address kyberAddress)
    {
        return KYBER_NETWORK_PROXY_ADDRESS;
    }

    /// @dev Overridable way to get the WETH address.
    /// @return wethAddress The WETH address.
    function _getWethAddress()
        internal
        view
        returns (address wethAddress)
    {
        return WETH_ADDRESS;
    }

    /// @dev Overridable way to get the `UniswapExchangeFactory` address.
    /// @return uniswapAddress The `UniswapExchangeFactory` address.
    function _getUniswapExchangeFactoryAddress()
        internal
        view
        returns (address uniswapAddress)
    {
        return UNISWAP_EXCHANGE_FACTORY_ADDRESS;
    }

    /// @dev Overridable way to get the `UniswapV2Router01` address.
    /// @return uniswapRouterAddress The `UniswapV2Router01` address.
    function _getUniswapV2Router01Address()
        internal
        view
        returns (address uniswapRouterAddress)
    {
        return UNISWAP_V2_ROUTER_01_ADDRESS;
    }

    /// @dev An overridable way to retrieve the Eth2Dai `MatchingMarket` contract.
    /// @return eth2daiAddress The Eth2Dai `MatchingMarket` contract.
    function _getEth2DaiAddress()
        internal
        view
        returns (address eth2daiAddress)
    {
        return ETH2DAI_ADDRESS;
    }

    /// @dev An overridable way to retrieve the `ERC20BridgeProxy` contract.
    /// @return erc20BridgeProxyAddress The `ERC20BridgeProxy` contract.
    function _getERC20BridgeProxyAddress()
        internal
        view
        returns (address erc20BridgeProxyAddress)
    {
        return ERC20_BRIDGE_PROXY_ADDRESS;
    }

    /// @dev An overridable way to retrieve the `Dai` contract.
    /// @return daiAddress The `Dai` contract.
    function _getDaiAddress()
        internal
        view
        returns (address daiAddress)
    {
        return DAI_ADDRESS;
    }

    /// @dev An overridable way to retrieve the `Chai` contract.
    /// @return chaiAddress The `Chai` contract.
    function _getChaiAddress()
        internal
        view
        returns (address chaiAddress)
    {
        return CHAI_ADDRESS;
    }

    /// @dev An overridable way to retrieve the 0x `DevUtils` contract address.
    /// @return devUtils The 0x `DevUtils` contract address.
    function _getDevUtilsAddress()
        internal
        view
        returns (address devUtils)
    {
        return DEV_UTILS_ADDRESS;
    }

    /// @dev Overridable way to get the DyDx contract.
    /// @return exchange The DyDx exchange contract.
    function _getDydxAddress()
        internal
        view
        returns (address dydxAddress)
    {
        return DYDX_ADDRESS;
    }

    /// @dev An overridable way to retrieve the GST2 contract address.
    /// @return gst The GST contract.
    function _getGstAddress()
        internal
        view
        returns (address gst)
    {
        return GST_ADDRESS;
    }

    /// @dev An overridable way to retrieve the GST Collector address.
    /// @return collector The GST collector address.
    function _getGstCollectorAddress()
        internal
        view
        returns (address collector)
    {
        return GST_COLLECTOR_ADDRESS;
    }
}

/*

  Copyright 2019 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.

*/

interface IDevUtils {

    /// @dev Fetches all order-relevant information needed to validate if the supplied order is fillable.
    /// @param order The order structure.
    /// @param signature Signature provided by maker that proves the order's authenticity.
    /// `0x01` can always be provided if the signature does not need to be validated.
    /// @return The orderInfo (hash, status, and `takerAssetAmount` already filled for the given order),
    /// fillableTakerAssetAmount (amount of the order's `takerAssetAmount` that is fillable given all on-chain state),
    /// and isValidSignature (validity of the provided signature).
    /// NOTE: If the `takerAssetData` encodes data for multiple assets, `fillableTakerAssetAmount` will represent a "scaled"
    /// amount, meaning it must be multiplied by all the individual asset amounts within the `takerAssetData` to get the final
    /// amount of each asset that can be filled.
    function getOrderRelevantState(LibOrder.Order calldata order, bytes calldata signature)
        external
        view
        returns (
            LibOrder.OrderInfo memory orderInfo,
            uint256 fillableTakerAssetAmount,
            bool isValidSignature
        );
}

/*

  Copyright 2019 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.

*/

interface IERC20BridgeSampler {

    struct FakeBuyOptions {
        uint256 targetSlippageBps;
        uint256 maxIterations;
    }

    /// @dev Call multiple public functions on this contract in a single transaction.
    /// @param callDatas ABI-encoded call data for each function call.
    /// @return callResults ABI-encoded results data for each call.
    function batchCall(bytes[] calldata callDatas)
        external
        view
        returns (bytes[] memory callResults);

    /// @dev Queries the fillable taker asset amounts of native orders.
    /// @param orders Native orders to query.
    /// @param orderSignatures Signatures for each respective order in `orders`.
    /// @return orderFillableTakerAssetAmounts How much taker asset can be filled
    ///         by each order in `orders`.
    function getOrderFillableTakerAssetAmounts(
        LibOrder.Order[] calldata orders,
        bytes[] calldata orderSignatures
    )
        external
        view
        returns (uint256[] memory orderFillableTakerAssetAmounts);

    /// @dev Queries the fillable maker asset amounts of native orders.
    /// @param orders Native orders to query.
    /// @param orderSignatures Signatures for each respective order in `orders`.
    /// @return orderFillableMakerAssetAmounts How much maker asset can be filled
    ///         by each order in `orders`.
    function getOrderFillableMakerAssetAmounts(
        LibOrder.Order[] calldata orders,
        bytes[] calldata orderSignatures
    )
        external
        view
        returns (uint256[] memory orderFillableMakerAssetAmounts);

    /// @dev Sample sell quotes from Kyber.
    /// @param takerToken Address of the taker token (what to sell).
    /// @param makerToken Address of the maker token (what to buy).
    /// @param takerTokenAmounts Taker token sell amount for each sample.
    /// @return makerTokenAmounts Maker amounts bought at each taker token
    ///         amount.
    function sampleSellsFromKyberNetwork(
        address takerToken,
        address makerToken,
        uint256[] calldata takerTokenAmounts
    )
        external
        view
        returns (uint256[] memory makerTokenAmounts);

    /// @dev Sample buy quotes from Kyber.
    /// @param takerToken Address of the taker token (what to sell).
    /// @param makerToken Address of the maker token (what to buy).
    /// @param makerTokenAmounts Maker token buy amount for each sample.
    /// @param opts `FakeBuyOptions` specifying target slippage and max iterations.
    /// @return takerTokenAmounts Taker amounts sold at each maker token
    ///         amount.
    function sampleBuysFromKyberNetwork(
        address takerToken,
        address makerToken,
        uint256[] calldata makerTokenAmounts,
        FakeBuyOptions calldata opts
    )
        external
        view
        returns (uint256[] memory takerTokenAmounts);

    /// @dev Sample sell quotes from Eth2Dai/Oasis.
    /// @param takerToken Address of the taker token (what to sell).
    /// @param makerToken Address of the maker token (what to buy).
    /// @param takerTokenAmounts Taker token sell amount for each sample.
    /// @return makerTokenAmounts Maker amounts bought at each taker token
    ///         amount.
    function sampleSellsFromEth2Dai(
        address takerToken,
        address makerToken,
        uint256[] calldata takerTokenAmounts
    )
        external
        view
        returns (uint256[] memory makerTokenAmounts);

    /// @dev Sample sell quotes from Uniswap.
    /// @param takerToken Address of the taker token (what to sell).
    /// @param makerToken Address of the maker token (what to buy).
    /// @param takerTokenAmounts Taker token sell amount for each sample.
    /// @return makerTokenAmounts Maker amounts bought at each taker token
    ///         amount.
    function sampleSellsFromUniswap(
        address takerToken,
        address makerToken,
        uint256[] calldata takerTokenAmounts
    )
        external
        view
        returns (uint256[] memory makerTokenAmounts);

    /// @dev Sample buy quotes from Uniswap.
    /// @param takerToken Address of the taker token (what to sell).
    /// @param makerToken Address of the maker token (what to buy).
    /// @param makerTokenAmounts Maker token buy amount for each sample.
    /// @return takerTokenAmounts Taker amounts sold at each maker token
    ///         amount.
    function sampleBuysFromUniswap(
        address takerToken,
        address makerToken,
        uint256[] calldata makerTokenAmounts
    )
        external
        view
        returns (uint256[] memory takerTokenAmounts);

    /// @dev Sample buy quotes from Eth2Dai/Oasis.
    /// @param takerToken Address of the taker token (what to sell).
    /// @param makerToken Address of the maker token (what to buy).
    /// @param makerTokenAmounts Maker token buy amount for each sample.
    /// @return takerTokenAmounts Taker amounts sold at each maker token
    ///         amount.
    function sampleBuysFromEth2Dai(
        address takerToken,
        address makerToken,
        uint256[] calldata makerTokenAmounts
    )
        external
        view
        returns (uint256[] memory takerTokenAmounts);

    /// @dev Sample sell quotes from Curve.
    /// @param curveAddress Address of the Curve contract.
    /// @param fromTokenIdx Index of the taker token (what to sell).
    /// @param toTokenIdx Index of the maker token (what to buy).
    /// @param takerTokenAmounts Taker token sell amount for each sample.
    /// @return makerTokenAmounts Maker amounts bought at each taker token
    ///         amount.
    function sampleSellsFromCurve(
        address curveAddress,
        int128 fromTokenIdx,
        int128 toTokenIdx,
        uint256[] calldata takerTokenAmounts
    )
        external
        view
        returns (uint256[] memory makerTokenAmounts);

    /// @dev Sample buy quotes from Curve.
    /// @param curveAddress Address of the Curve contract.
    /// @param fromTokenIdx Index of the taker token (what to sell).
    /// @param toTokenIdx Index of the maker token (what to buy).
    /// @param makerTokenAmounts Maker token buy amount for each sample.
    /// @return takerTokenAmounts Taker amounts sold at each maker token
    ///         amount.
    function sampleBuysFromCurve(
        address curveAddress,
        int128 fromTokenIdx,
        int128 toTokenIdx,
        uint256[] calldata makerTokenAmounts
    )
        external
        view
        returns (uint256[] memory takerTokenAmounts);

    /// @dev Sample sell quotes from an arbitrary on-chain liquidity provider.
    /// @param registryAddress Address of the liquidity provider registry contract.
    /// @param takerToken Address of the taker token (what to sell).
    /// @param makerToken Address of the maker token (what to buy).
    /// @param takerTokenAmounts Taker token sell amount for each sample.
    /// @return makerTokenAmounts Maker amounts bought at each taker token
    ///         amount.
    function sampleSellsFromLiquidityProviderRegistry(
        address registryAddress,
        address takerToken,
        address makerToken,
        uint256[] calldata takerTokenAmounts
    )
        external
        view
        returns (uint256[] memory makerTokenAmounts);

    /// @dev Sample sell quotes from MultiBridge.
    /// @param multibridge Address of the MultiBridge contract.
    /// @param takerToken Address of the taker token (what to sell).
    /// @param intermediateToken The address of the intermediate token to
    ///        use in an indirect route.
    /// @param makerToken Address of the maker token (what to buy).
    /// @param takerTokenAmounts Taker token sell amount for each sample.
    /// @return makerTokenAmounts Maker amounts bought at each taker token
    ///         amount.
    function sampleSellsFromMultiBridge(
        address multibridge,
        address takerToken,
        address intermediateToken,
        address makerToken,
        uint256[] calldata takerTokenAmounts
    )
        external
        view
        returns (uint256[] memory makerTokenAmounts);

    /// @dev Sample buy quotes from an arbitrary on-chain liquidity provider.
    /// @param registryAddress Address of the liquidity provider registry contract.
    /// @param takerToken Address of the taker token (what to sell).
    /// @param makerToken Address of the maker token (what to buy).
    /// @param makerTokenAmounts Maker token buy amount for each sample.
    /// @param opts `FakeBuyOptions` specifying target slippage and max iterations.
    /// @return takerTokenAmounts Taker amounts sold at each maker token
    ///         amount.
    function sampleBuysFromLiquidityProviderRegistry(
        address registryAddress,
        address takerToken,
        address makerToken,
        uint256[] calldata makerTokenAmounts,
        FakeBuyOptions calldata opts

    )
        external
        view
        returns (uint256[] memory takerTokenAmounts);

    /// @dev Returns the address of a liquidity provider for the given market
    ///      (takerToken, makerToken), from a registry of liquidity providers.
    ///      Returns address(0) if no such provider exists in the registry.
    /// @param takerToken Taker asset managed by liquidity provider.
    /// @param makerToken Maker asset managed by liquidity provider.
    /// @return providerAddress Address of the liquidity provider.
    function getLiquidityProviderFromRegistry(
        address registryAddress,
        address takerToken,
        address makerToken
    )
        external
        view
        returns (address providerAddress);

    /// @dev Sample sell quotes from UniswapV2.
    /// @param path Token route.
    /// @param takerTokenAmounts Taker token sell amount for each sample.
    /// @return makerTokenAmounts Maker amounts bought at each taker token
    ///         amount.
    function sampleSellsFromUniswapV2(
        address[] calldata path,
        uint256[] calldata takerTokenAmounts
    )
        external
        view
        returns (uint256[] memory makerTokenAmounts);

    /// @dev Sample buy quotes from UniswapV2.
    /// @param path Token route.
    /// @param makerTokenAmounts Maker token buy amount for each sample.
    /// @return takerTokenAmounts Taker amounts sold at each maker token
    ///         amount.
    function sampleBuysFromUniswapV2(
        address[] calldata path,
        uint256[] calldata makerTokenAmounts
    )
        external
        view
        returns (uint256[] memory takerTokenAmounts);
}

/*

  Copyright 2019 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.

*/

interface IEth2Dai {

    function getBuyAmount(
        address buyToken,
        address payToken,
        uint256 payAmount
    )
        external
        view
        returns (uint256 buyAmount);

    function getPayAmount(
        address payToken,
        address buyToken,
        uint256 buyAmount
    )
        external
        view
        returns (uint256 payAmount);
}

/*

  Copyright 2019 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.

*/

interface IKyberNetwork {

    function searchBestRate(
        address fromToken,
        address toToken,
        uint256 fromAmount,
        bool usePermissionless
    )
        external
        view
        returns (address reserve, uint256 expectedRate);
}

/*

  Copyright 2019 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.

*/

interface IKyberNetworkProxy {

    function kyberNetworkContract() external view returns (address);

    function getExpectedRate(
        address fromToken,
        address toToken,
        uint256 fromAmount
    )
        external
        view
        returns (uint256 expectedRate, uint256 slippageRate);
}

/*

  Copyright 2019 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.

*/

interface IUniswapExchangeQuotes {

    function getEthToTokenInputPrice(
        uint256 ethSold
    )
        external
        view
        returns (uint256 tokensBought);

    function getEthToTokenOutputPrice(
        uint256 tokensBought
    )
        external
        view
        returns (uint256 ethSold);

    function getTokenToEthInputPrice(
        uint256 tokensSold
    )
        external
        view
        returns (uint256 ethBought);

    function getTokenToEthOutputPrice(
        uint256 ethBought
    )
        external
        view
        returns (uint256 tokensSold);
}

/*

  Copyright 2019 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.

*/

// solhint-disable func-name-mixedcase
interface ICurve {

    /// @dev Sell `sellAmount` of `fromToken` token and receive `toToken` token.
    ///      This function exists on early versions of Curve (USDC/DAI)
    /// @param i The token index being sold.
    /// @param j The token index being bought.
    /// @param sellAmount The amount of token being bought.
    /// @param minBuyAmount The minimum buy amount of the token being bought.
    /// @param deadline The time in seconds when this operation should expire.
    function exchange_underlying(
        int128 i,
        int128 j,
        uint256 sellAmount,
        uint256 minBuyAmount,
        uint256 deadline
    )
        external;

    /// @dev Sell `sellAmount` of `fromToken` token and receive `toToken` token.
    ///      This function exists on later versions of Curve (USDC/DAI/USDT)
    /// @param i The token index being sold.
    /// @param j The token index being bought.
    /// @param sellAmount The amount of token being bought.
    /// @param minBuyAmount The minimum buy amount of the token being bought.
    function exchange_underlying(
        int128 i,
        int128 j,
        uint256 sellAmount,
        uint256 minBuyAmount
    )
        external;

    /// @dev Get the amount of `toToken` by selling `sellAmount` of `fromToken`
    /// @param i The token index being sold.
    /// @param j The token index being bought.
    /// @param sellAmount The amount of token being bought.
    function get_dy_underlying(
        int128 i,
        int128 j,
        uint256 sellAmount
    )
        external
        returns (uint256 dy);

    /// @dev Get the amount of `fromToken` by buying `buyAmount` of `toToken`
    ///      This function exists on later versions of Curve (USDC/DAI/USDT)
    /// @param i The token index being sold.
    /// @param j The token index being bought.
    /// @param buyAmount The amount of token being bought.
    function get_dx_underlying(
        int128 i,
        int128 j,
        uint256 buyAmount
    )
        external
        returns (uint256 dx);

    /// @dev Get the underlying token address from the token index
    /// @param i The token index.
    function underlying_coins(
        int128 i
    )
        external
        returns (address tokenAddress);
}

/*

  Copyright 2019 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.

*/

interface ILiquidityProvider {

    /// @dev Transfers `amount` of the ERC20 `tokenAddress` from `from` to `to`.
    /// @param tokenAddress The address of the ERC20 token to transfer.
    /// @param from Address to transfer asset from.
    /// @param to Address to transfer asset to.
    /// @param amount Amount of asset to transfer.
    /// @param bridgeData Arbitrary asset data needed by the bridge contract.
    /// @return success The magic bytes `0xdc1600f3` if successful.
    function bridgeTransferFrom(
        address tokenAddress,
        address from,
        address to,
        uint256 amount,
        bytes calldata bridgeData
    )
        external
        returns (bytes4 success);

    /// @dev Quotes the amount of `makerToken` that would be obtained by
    ///      selling `sellAmount` of `takerToken`.
    /// @param takerToken Address of the taker token (what to sell).
    /// @param makerToken Address of the maker token (what to buy).
    /// @param sellAmount Amount of `takerToken` to sell.
    /// @return makerTokenAmount Amount of `makerToken` that would be obtained.
    function getSellQuote(
        address takerToken,
        address makerToken,
        uint256 sellAmount
    )
        external
        view
        returns (uint256 makerTokenAmount);

    /// @dev Quotes the amount of `takerToken` that would need to be sold in
    ///      order to obtain `buyAmount` of `makerToken`.
    /// @param takerToken Address of the taker token (what to sell).
    /// @param makerToken Address of the maker token (what to buy).
    /// @param buyAmount Amount of `makerToken` to buy.
    /// @return takerTokenAmount Amount of `takerToken` that would need to be sold.
    function getBuyQuote(
        address takerToken,
        address makerToken,
        uint256 buyAmount
    )
        external
        view
        returns (uint256 takerTokenAmount);
}

/*

  Copyright 2019 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.

*/

interface ILiquidityProviderRegistry {

    /// @dev Returns the address of a liquidity provider for the given market
    ///      (takerToken, makerToken), reverting if the pool does not exist.
    /// @param takerToken Taker asset managed by liquidity provider.
    /// @param makerToken Maker asset managed by liquidity provider.
    /// @return Address of the liquidity provider.
    function getLiquidityProviderForMarket(
        address takerToken,
        address makerToken
    )
        external
        view
        returns (address providerAddress);
}

/*

  Copyright 2019 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.

*/

interface IUniswapV2Router01 {

    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);
}

/*

  Copyright 2020 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.

*/

interface IMultiBridge {

    /// @dev Transfers `amount` of the ERC20 `tokenAddress` from `from` to `to`.
    /// @param tokenAddress The address of the ERC20 token to transfer.
    /// @param from Address to transfer asset from.
    /// @param to Address to transfer asset to.
    /// @param amount Amount of asset to transfer.
    /// @param bridgeData Arbitrary asset data needed by the bridge contract.
    /// @return success The magic bytes `0xdc1600f3` if successful.
    function bridgeTransferFrom(
        address tokenAddress,
        address from,
        address to,
        uint256 amount,
        bytes calldata bridgeData
    )
        external
        returns (bytes4 success);

    /// @dev Quotes the amount of `makerToken` that would be obtained by
    ///      selling `sellAmount` of `takerToken`.
    /// @param takerToken Address of the taker token (what to sell).
    /// @param intermediateToken The address of the intermediate token to
    ///        use in an indirect route.
    /// @param makerToken Address of the maker token (what to buy).
    /// @param sellAmount Amount of `takerToken` to sell.
    /// @return makerTokenAmount Amount of `makerToken` that would be obtained.
    function getSellQuote(
        address takerToken,
        address intermediateToken,
        address makerToken,
        uint256 sellAmount
    )
        external
        view
        returns (uint256 makerTokenAmount);
}

contract ERC20BridgeSampler is
    IERC20BridgeSampler,
    DeploymentConstants
{
    /// @dev Gas limit for DevUtils calls.
    uint256 constant internal DEV_UTILS_CALL_GAS = 500e3; // 500k
    /// @dev Gas limit for Kyber calls.
    uint256 constant internal KYBER_CALL_GAS = 1500e3; // 1.5m
    /// @dev Gas limit for Uniswap calls.
    uint256 constant internal UNISWAP_CALL_GAS = 150e3; // 150k
    /// @dev Gas limit for UniswapV2 calls.
    uint256 constant internal UNISWAPV2_CALL_GAS = 150e3; // 150k
    /// @dev Base gas limit for Eth2Dai calls.
    uint256 constant internal ETH2DAI_CALL_GAS = 1000e3; // 1m
    /// @dev Base gas limit for Curve calls. Some Curves have multiple tokens
    ///      So a reasonable ceil is 150k per token. Biggest Curve has 4 tokens.
    uint256 constant internal CURVE_CALL_GAS = 600e3; // 600k
    /// @dev Default gas limit for liquidity provider calls.
    uint256 constant internal DEFAULT_CALL_GAS = 400e3; // 400k
    /// @dev The Kyber Uniswap Reserve address
    address constant internal KYBER_UNIWAP_RESERVE = 0x31E085Afd48a1d6e51Cc193153d625e8f0514C7F;
    /// @dev The Kyber Eth2Dai Reserve address
    address constant internal KYBER_ETH2DAI_RESERVE = 0x1E158c0e93c30d24e918Ef83d1e0bE23595C3c0f;

    address private _devUtilsAddress;

    constructor(address devUtilsAddress) public {
        _devUtilsAddress = devUtilsAddress;
    }

    /// @dev Call multiple public functions on this contract in a single transaction.
    /// @param callDatas ABI-encoded call data for each function call.
    /// @return callResults ABI-encoded results data for each call.
    function batchCall(bytes[] calldata callDatas)
        external
        view
        returns (bytes[] memory callResults)
    {
        callResults = new bytes[](callDatas.length);
        for (uint256 i = 0; i != callDatas.length; ++i) {
            (bool didSucceed, bytes memory resultData) = address(this).staticcall(callDatas[i]);
            if (!didSucceed) {
                assembly { revert(add(resultData, 0x20), mload(resultData)) }
            }
            callResults[i] = resultData;
        }
    }

    /// @dev Queries the fillable taker asset amounts of native orders.
    ///      Effectively ignores orders that have empty signatures or
    ///      maker/taker asset amounts (returning 0).
    /// @param orders Native orders to query.
    /// @param orderSignatures Signatures for each respective order in `orders`.
    /// @return orderFillableTakerAssetAmounts How much taker asset can be filled
    ///         by each order in `orders`.
    function getOrderFillableTakerAssetAmounts(
        LibOrder.Order[] memory orders,
        bytes[] memory orderSignatures
    )
        public
        view
        returns (uint256[] memory orderFillableTakerAssetAmounts)
    {
        orderFillableTakerAssetAmounts = new uint256[](orders.length);
        address devUtilsAddress = _devUtilsAddress;
        for (uint256 i = 0; i != orders.length; i++) {
            // Ignore orders with no signature or empty maker/taker amounts.
            if (orderSignatures[i].length == 0 ||
                orders[i].makerAssetAmount == 0 ||
                orders[i].takerAssetAmount == 0) {
                orderFillableTakerAssetAmounts[i] = 0;
                continue;
            }
            // solhint-disable indent
            (bool didSucceed, bytes memory resultData) =
                devUtilsAddress
                    .staticcall
                    .gas(DEV_UTILS_CALL_GAS)
                    (abi.encodeWithSelector(
                       IDevUtils(devUtilsAddress).getOrderRelevantState.selector,
                       orders[i],
                       orderSignatures[i]
                    ));
            // solhint-enable indent
            if (!didSucceed) {
                orderFillableTakerAssetAmounts[i] = 0;
                continue;
            }
            (
                LibOrder.OrderInfo memory orderInfo,
                uint256 fillableTakerAssetAmount,
                bool isValidSignature
            ) = abi.decode(
                resultData,
                (LibOrder.OrderInfo, uint256, bool)
            );
            // The fillable amount is zero if the order is not fillable or if the
            // signature is invalid.
            if (orderInfo.orderStatus != LibOrder.OrderStatus.FILLABLE ||
                !isValidSignature) {
                orderFillableTakerAssetAmounts[i] = 0;
            } else {
                orderFillableTakerAssetAmounts[i] = fillableTakerAssetAmount;
            }
        }
    }

    /// @dev Queries the fillable taker asset amounts of native orders.
    ///      Effectively ignores orders that have empty signatures or
    /// @param orders Native orders to query.
    /// @param orderSignatures Signatures for each respective order in `orders`.
    /// @return orderFillableMakerAssetAmounts How much maker asset can be filled
    ///         by each order in `orders`.
    function getOrderFillableMakerAssetAmounts(
        LibOrder.Order[] memory orders,
        bytes[] memory orderSignatures
    )
        public
        view
        returns (uint256[] memory orderFillableMakerAssetAmounts)
    {
        orderFillableMakerAssetAmounts = getOrderFillableTakerAssetAmounts(
            orders,
            orderSignatures
        );
        // `orderFillableMakerAssetAmounts` now holds taker asset amounts, so
        // convert them to maker asset amounts.
        for (uint256 i = 0; i < orders.length; ++i) {
            if (orderFillableMakerAssetAmounts[i] != 0) {
                orderFillableMakerAssetAmounts[i] = LibMath.getPartialAmountCeil(
                    orderFillableMakerAssetAmounts[i],
                    orders[i].takerAssetAmount,
                    orders[i].makerAssetAmount
                );
            }
        }
    }

    /// @dev Sample sell quotes from Kyber.
    /// @param takerToken Address of the taker token (what to sell).
    /// @param makerToken Address of the maker token (what to buy).
    /// @param takerTokenAmounts Taker token sell amount for each sample.
    /// @return makerTokenAmounts Maker amounts bought at each taker token
    ///         amount.
    function sampleSellsFromKyberNetwork(
        address takerToken,
        address makerToken,
        uint256[] memory takerTokenAmounts
    )
        public
        view
        returns (uint256[] memory makerTokenAmounts)
    {
        _assertValidPair(makerToken, takerToken);
        uint256 numSamples = takerTokenAmounts.length;
        makerTokenAmounts = new uint256[](numSamples);
        address wethAddress = _getWethAddress();
        uint256 value;
        address reserve;
        for (uint256 i = 0; i < numSamples; i++) {
            if (takerToken == wethAddress || makerToken == wethAddress) {
                // Direct ETH based trade
                (value, reserve) = _sampleSellFromKyberNetwork(takerToken, makerToken, takerTokenAmounts[i]);
                // If this fills on an on-chain reserve we remove it as that can introduce collisions
                if (reserve == KYBER_UNIWAP_RESERVE || reserve == KYBER_ETH2DAI_RESERVE) {
                    value = 0;
                }
            } else {
                // Hop to ETH
                (value, reserve) = _sampleSellFromKyberNetwork(takerToken, wethAddress, takerTokenAmounts[i]);
                if (value != 0) {
                    address otherReserve;
                    (value, otherReserve) = _sampleSellFromKyberNetwork(wethAddress, makerToken, value);
                    // If this fills on Eth2Dai it is ok as we queried a different market
                    // If this fills on Uniswap on both legs then this is a hard collision
                    if (reserve == KYBER_UNIWAP_RESERVE && reserve == otherReserve) {
                        value = 0;
                    }
                }
            }
            makerTokenAmounts[i] = value;
        }
    }

    /// @dev Sample buy quotes from Kyber.
    /// @param takerToken Address of the taker token (what to sell).
    /// @param makerToken Address of the maker token (what to buy).
    /// @param makerTokenAmounts Maker token buy amount for each sample.
    /// @param opts `FakeBuyOptions` specifying target slippage and max iterations.
    /// @return takerTokenAmounts Taker amounts sold at each maker token
    ///         amount.
    function sampleBuysFromKyberNetwork(
        address takerToken,
        address makerToken,
        uint256[] memory makerTokenAmounts,
        FakeBuyOptions memory opts
    )
        public
        view
        returns (uint256[] memory takerTokenAmounts)
    {
        return _sampleApproximateBuysFromSource(
            takerToken,
            makerToken,
            makerTokenAmounts,
            opts,
            this.sampleSellsFromKyberNetwork.selector,
            address(0) // PLP registry address
        );
    }

    /// @dev Sample sell quotes from Eth2Dai/Oasis.
    /// @param takerToken Address of the taker token (what to sell).
    /// @param makerToken Address of the maker token (what to buy).
    /// @param takerTokenAmounts Taker token sell amount for each sample.
    /// @return makerTokenAmounts Maker amounts bought at each taker token
    ///         amount.
    function sampleSellsFromEth2Dai(
        address takerToken,
        address makerToken,
        uint256[] memory takerTokenAmounts
    )
        public
        view
        returns (uint256[] memory makerTokenAmounts)
    {
        _assertValidPair(makerToken, takerToken);
        uint256 numSamples = takerTokenAmounts.length;
        makerTokenAmounts = new uint256[](numSamples);
        for (uint256 i = 0; i < numSamples; i++) {
            (bool didSucceed, bytes memory resultData) =
                _getEth2DaiAddress().staticcall.gas(ETH2DAI_CALL_GAS)(
                    abi.encodeWithSelector(
                        IEth2Dai(0).getBuyAmount.selector,
                        makerToken,
                        takerToken,
                        takerTokenAmounts[i]
                    ));
            uint256 buyAmount = 0;
            if (didSucceed) {
                buyAmount = abi.decode(resultData, (uint256));
            } else{
                break;
            }
            makerTokenAmounts[i] = buyAmount;
        }
    }

    /// @dev Sample sell quotes from Eth2Dai/Oasis using a hop to an intermediate token.
    ///      I.e WBTC/DAI via ETH or WBTC/ETH via DAI
    /// @param takerToken Address of the taker token (what to sell).
    /// @param makerToken Address of the maker token (what to buy).
    /// @param intermediateToken Address of the token to hop to.
    /// @param takerTokenAmounts Taker token sell amount for each sample.
    /// @return makerTokenAmounts Maker amounts bought at each taker token
    ///         amount.
    function sampleSellsFromEth2DaiHop(
        address takerToken,
        address makerToken,
        address intermediateToken,
        uint256[] memory takerTokenAmounts
    )
        public
        view
        returns (uint256[] memory makerTokenAmounts)
    {
        if (makerToken == intermediateToken || takerToken == intermediateToken) {
            return makerTokenAmounts;
        }
        uint256[] memory intermediateAmounts = sampleSellsFromEth2Dai(takerToken, intermediateToken, takerTokenAmounts);
        makerTokenAmounts = sampleSellsFromEth2Dai(intermediateToken, makerToken, intermediateAmounts);
    }

    /// @dev Sample buy quotes from Eth2Dai/Oasis.
    /// @param takerToken Address of the taker token (what to sell).
    /// @param makerToken Address of the maker token (what to buy).
    /// @param takerTokenAmounts Maker token sell amount for each sample.
    /// @return takerTokenAmounts Taker amounts sold at each maker token
    ///         amount.
    function sampleBuysFromEth2Dai(
        address takerToken,
        address makerToken,
        uint256[] memory makerTokenAmounts
    )
        public
        view
        returns (uint256[] memory takerTokenAmounts)
    {
        _assertValidPair(makerToken, takerToken);
        uint256 numSamples = makerTokenAmounts.length;
        takerTokenAmounts = new uint256[](numSamples);
        for (uint256 i = 0; i < numSamples; i++) {
            (bool didSucceed, bytes memory resultData) =
                _getEth2DaiAddress().staticcall.gas(ETH2DAI_CALL_GAS)(
                    abi.encodeWithSelector(
                        IEth2Dai(0).getPayAmount.selector,
                        takerToken,
                        makerToken,
                        makerTokenAmounts[i]
                    ));
            uint256 sellAmount = 0;
            if (didSucceed) {
                sellAmount = abi.decode(resultData, (uint256));
            } else {
                break;
            }
            takerTokenAmounts[i] = sellAmount;
        }
    }

    /// @dev Sample sell quotes from Uniswap.
    /// @param takerToken Address of the taker token (what to sell).
    /// @param makerToken Address of the maker token (what to buy).
    /// @param takerTokenAmounts Taker token sell amount for each sample.
    /// @return makerTokenAmounts Maker amounts bought at each taker token
    ///         amount.
    function sampleSellsFromUniswap(
        address takerToken,
        address makerToken,
        uint256[] memory takerTokenAmounts
    )
        public
        view
        returns (uint256[] memory makerTokenAmounts)
    {
        _assertValidPair(makerToken, takerToken);
        uint256 numSamples = takerTokenAmounts.length;
        makerTokenAmounts = new uint256[](numSamples);
        IUniswapExchangeQuotes takerTokenExchange = takerToken == _getWethAddress() ?
            IUniswapExchangeQuotes(0) : _getUniswapExchange(takerToken);
        IUniswapExchangeQuotes makerTokenExchange = makerToken == _getWethAddress() ?
            IUniswapExchangeQuotes(0) : _getUniswapExchange(makerToken);
        for (uint256 i = 0; i < numSamples; i++) {
            bool didSucceed = true;
            if (makerToken == _getWethAddress()) {
                (makerTokenAmounts[i], didSucceed) = _callUniswapExchangePriceFunction(
                    address(takerTokenExchange),
                    takerTokenExchange.getTokenToEthInputPrice.selector,
                    takerTokenAmounts[i]
                );
            } else if (takerToken == _getWethAddress()) {
                (makerTokenAmounts[i], didSucceed) = _callUniswapExchangePriceFunction(
                    address(makerTokenExchange),
                    makerTokenExchange.getEthToTokenInputPrice.selector,
                    takerTokenAmounts[i]
                );
            } else {
                uint256 ethBought;
                (ethBought, didSucceed) = _callUniswapExchangePriceFunction(
                    address(takerTokenExchange),
                    takerTokenExchange.getTokenToEthInputPrice.selector,
                    takerTokenAmounts[i]
                );
                if (ethBought != 0) {
                    (makerTokenAmounts[i], didSucceed) = _callUniswapExchangePriceFunction(
                        address(makerTokenExchange),
                        makerTokenExchange.getEthToTokenInputPrice.selector,
                        ethBought
                    );
                } else {
                    makerTokenAmounts[i] = 0;
                }
            }
            if (!didSucceed) {
                break;
            }
        }
    }

    /// @dev Sample buy quotes from Uniswap.
    /// @param takerToken Address of the taker token (what to sell).
    /// @param makerToken Address of the maker token (what to buy).
    /// @param makerTokenAmounts Maker token sell amount for each sample.
    /// @return takerTokenAmounts Taker amounts sold at each maker token
    ///         amount.
    function sampleBuysFromUniswap(
        address takerToken,
        address makerToken,
        uint256[] memory makerTokenAmounts
    )
        public
        view
        returns (uint256[] memory takerTokenAmounts)
    {
        _assertValidPair(makerToken, takerToken);
        uint256 numSamples = makerTokenAmounts.length;
        takerTokenAmounts = new uint256[](numSamples);
        IUniswapExchangeQuotes takerTokenExchange = takerToken == _getWethAddress() ?
            IUniswapExchangeQuotes(0) : _getUniswapExchange(takerToken);
        IUniswapExchangeQuotes makerTokenExchange = makerToken == _getWethAddress() ?
            IUniswapExchangeQuotes(0) : _getUniswapExchange(makerToken);
        for (uint256 i = 0; i < numSamples; i++) {
            bool didSucceed = true;
            if (makerToken == _getWethAddress()) {
                (takerTokenAmounts[i], didSucceed) = _callUniswapExchangePriceFunction(
                    address(takerTokenExchange),
                    takerTokenExchange.getTokenToEthOutputPrice.selector,
                    makerTokenAmounts[i]
                );
            } else if (takerToken == _getWethAddress()) {
                (takerTokenAmounts[i], didSucceed) = _callUniswapExchangePriceFunction(
                    address(makerTokenExchange),
                    makerTokenExchange.getEthToTokenOutputPrice.selector,
                    makerTokenAmounts[i]
                );
            } else {
                uint256 ethSold;
                (ethSold, didSucceed) = _callUniswapExchangePriceFunction(
                    address(makerTokenExchange),
                    makerTokenExchange.getEthToTokenOutputPrice.selector,
                    makerTokenAmounts[i]
                );
                if (ethSold != 0) {
                    (takerTokenAmounts[i], didSucceed) = _callUniswapExchangePriceFunction(
                        address(takerTokenExchange),
                        takerTokenExchange.getTokenToEthOutputPrice.selector,
                        ethSold
                    );
                } else {
                    takerTokenAmounts[i] = 0;
                }
            }
            if (!didSucceed) {
                break;
            }
        }
    }

    /// @dev Sample sell quotes from Curve.
    /// @param curveAddress Address of the Curve contract.
    /// @param fromTokenIdx Index of the taker token (what to sell).
    /// @param toTokenIdx Index of the maker token (what to buy).
    /// @param takerTokenAmounts Taker token sell amount for each sample.
    /// @return makerTokenAmounts Maker amounts bought at each taker token
    ///         amount.
    function sampleSellsFromCurve(
        address curveAddress,
        int128 fromTokenIdx,
        int128 toTokenIdx,
        uint256[] memory takerTokenAmounts
    )
        public
        view
        returns (uint256[] memory makerTokenAmounts)
    {
        uint256 numSamples = takerTokenAmounts.length;
        makerTokenAmounts = new uint256[](numSamples);
        for (uint256 i = 0; i < numSamples; i++) {
            (bool didSucceed, bytes memory resultData) =
                curveAddress.staticcall.gas(CURVE_CALL_GAS)(
                    abi.encodeWithSelector(
                        ICurve(0).get_dy_underlying.selector,
                        fromTokenIdx,
                        toTokenIdx,
                        takerTokenAmounts[i]
                    ));
            uint256 buyAmount = 0;
            if (didSucceed) {
                buyAmount = abi.decode(resultData, (uint256));
            } else {
                break;
            }
            makerTokenAmounts[i] = buyAmount;
        }
    }

    /// @dev Sample buy quotes from Curve.
    /// @param curveAddress Address of the Curve contract.
    /// @param fromTokenIdx Index of the taker token (what to sell).
    /// @param toTokenIdx Index of the maker token (what to buy).
    /// @param makerTokenAmounts Maker token buy amount for each sample.
    /// @return takerTokenAmounts Taker amounts sold at each maker token
    ///         amount.
    function sampleBuysFromCurve(
        address curveAddress,
        int128 fromTokenIdx,
        int128 toTokenIdx,
        uint256[] memory makerTokenAmounts
    )
        public
        view
        returns (uint256[] memory takerTokenAmounts)
    {
        uint256 numSamples = makerTokenAmounts.length;
        takerTokenAmounts = new uint256[](numSamples);
        for (uint256 i = 0; i < numSamples; i++) {
            (bool didSucceed, bytes memory resultData) =
                curveAddress.staticcall.gas(CURVE_CALL_GAS)(
                    abi.encodeWithSelector(
                        ICurve(0).get_dx_underlying.selector,
                        fromTokenIdx,
                        toTokenIdx,
                        makerTokenAmounts[i]
                    ));
            uint256 sellAmount = 0;
            if (didSucceed) {
                sellAmount = abi.decode(resultData, (uint256));
            } else {
                break;
            }
            takerTokenAmounts[i] = sellAmount;
        }
    }

    /// @dev Sample sell quotes from an arbitrary on-chain liquidity provider.
    /// @param registryAddress Address of the liquidity provider registry contract.
    /// @param takerToken Address of the taker token (what to sell).
    /// @param makerToken Address of the maker token (what to buy).
    /// @param takerTokenAmounts Taker token sell amount for each sample.
    /// @return makerTokenAmounts Maker amounts bought at each taker token
    ///         amount.
    function sampleSellsFromLiquidityProviderRegistry(
        address registryAddress,
        address takerToken,
        address makerToken,
        uint256[] memory takerTokenAmounts
    )
        public
        view
        returns (uint256[] memory makerTokenAmounts)
    {
        // Initialize array of maker token amounts.
        uint256 numSamples = takerTokenAmounts.length;
        makerTokenAmounts = new uint256[](numSamples);

        // Query registry for provider address.
        address providerAddress = getLiquidityProviderFromRegistry(
            registryAddress,
            takerToken,
            makerToken
        );
        // If provider doesn't exist, return all zeros.
        if (providerAddress == address(0)) {
            return makerTokenAmounts;
        }

        for (uint256 i = 0; i < numSamples; i++) {
            (bool didSucceed, bytes memory resultData) =
                providerAddress.staticcall.gas(DEFAULT_CALL_GAS)(
                    abi.encodeWithSelector(
                        ILiquidityProvider(0).getSellQuote.selector,
                        takerToken,
                        makerToken,
                        takerTokenAmounts[i]
                    ));
            uint256 buyAmount = 0;
            if (didSucceed) {
                buyAmount = abi.decode(resultData, (uint256));
            } else {
                // Exit early if the amount is too high for the liquidity provider to serve
                break;
            }
            makerTokenAmounts[i] = buyAmount;
        }
    }

    /// @dev Sample sell quotes from MultiBridge.
    /// @param multibridge Address of the MultiBridge contract.
    /// @param takerToken Address of the taker token (what to sell).
    /// @param intermediateToken The address of the intermediate token to
    ///        use in an indirect route.
    /// @param makerToken Address of the maker token (what to buy).
    /// @param takerTokenAmounts Taker token sell amount for each sample.
    /// @return makerTokenAmounts Maker amounts bought at each taker token
    ///         amount.
    function sampleSellsFromMultiBridge(
        address multibridge,
        address takerToken,
        address intermediateToken,
        address makerToken,
        uint256[] memory takerTokenAmounts
    )
        public
        view
        returns (uint256[] memory makerTokenAmounts)
    {
        // Initialize array of maker token amounts.
        uint256 numSamples = takerTokenAmounts.length;
        makerTokenAmounts = new uint256[](numSamples);

        // If no address provided, return all zeros.
        if (multibridge == address(0)) {
            return makerTokenAmounts;
        }

        for (uint256 i = 0; i < numSamples; i++) {
            (bool didSucceed, bytes memory resultData) =
                multibridge.staticcall.gas(DEFAULT_CALL_GAS)(
                    abi.encodeWithSelector(
                        IMultiBridge(0).getSellQuote.selector,
                        takerToken,
                        intermediateToken,
                        makerToken,
                        takerTokenAmounts[i]
                    ));
            uint256 buyAmount = 0;
            if (didSucceed) {
                buyAmount = abi.decode(resultData, (uint256));
            } else {
                // Exit early if the amount is too high for the liquidity provider to serve
                break;
            }
            makerTokenAmounts[i] = buyAmount;
        }
    }

    /// @dev Sample buy quotes from an arbitrary on-chain liquidity provider.
    /// @param registryAddress Address of the liquidity provider registry contract.
    /// @param takerToken Address of the taker token (what to sell).
    /// @param makerToken Address of the maker token (what to buy).
    /// @param makerTokenAmounts Maker token buy amount for each sample.
    /// @param opts `FakeBuyOptions` specifying target slippage and max iterations.
    /// @return takerTokenAmounts Taker amounts sold at each maker token
    ///         amount.
    function sampleBuysFromLiquidityProviderRegistry(
        address registryAddress,
        address takerToken,
        address makerToken,
        uint256[] memory makerTokenAmounts,
        FakeBuyOptions memory opts
    )
        public
        view
        returns (uint256[] memory takerTokenAmounts)
    {
        return _sampleApproximateBuysFromSource(
            takerToken,
            makerToken,
            makerTokenAmounts,
            opts,
            this.sampleSellsFromLiquidityProviderRegistry.selector,
            registryAddress
        );
    }

    /// @dev Returns the address of a liquidity provider for the given market
    ///      (takerToken, makerToken), from a registry of liquidity providers.
    ///      Returns address(0) if no such provider exists in the registry.
    /// @param takerToken Taker asset managed by liquidity provider.
    /// @param makerToken Maker asset managed by liquidity provider.
    /// @return providerAddress Address of the liquidity provider.
    function getLiquidityProviderFromRegistry(
        address registryAddress,
        address takerToken,
        address makerToken
    )
        public
        view
        returns (address providerAddress)
    {
        bytes memory callData = abi.encodeWithSelector(
            ILiquidityProviderRegistry(0).getLiquidityProviderForMarket.selector,
            takerToken,
            makerToken
        );
        (bool didSucceed, bytes memory returnData) = registryAddress.staticcall(callData);
        if (didSucceed && returnData.length == 32) {
            return LibBytes.readAddress(returnData, 12);
        }
    }

    /// @dev Sample sell quotes from UniswapV2.
    /// @param path Token route. Should be takerToken -> makerToken
    /// @param takerTokenAmounts Taker token sell amount for each sample.
    /// @return makerTokenAmounts Maker amounts bought at each taker token
    ///         amount.
    function sampleSellsFromUniswapV2(
        address[] memory path,
        uint256[] memory takerTokenAmounts
    )
        public
        view
        returns (uint256[] memory makerTokenAmounts)
    {
        uint256 numSamples = takerTokenAmounts.length;
        makerTokenAmounts = new uint256[](numSamples);
        for (uint256 i = 0; i < numSamples; i++) {
            (bool didSucceed, bytes memory resultData) =
                _getUniswapV2Router01Address().staticcall.gas(UNISWAPV2_CALL_GAS)(
                    abi.encodeWithSelector(
                        IUniswapV2Router01(0).getAmountsOut.selector,
                        takerTokenAmounts[i],
                        path
                    ));
            uint256 buyAmount = 0;
            if (didSucceed) {
                // solhint-disable-next-line indent
                buyAmount = abi.decode(resultData, (uint256[]))[path.length - 1];
            } else {
                break;
            }
            makerTokenAmounts[i] = buyAmount;
        }
    }

    /// @dev Sample buy quotes from UniswapV2.
    /// @param path Token route. Should be takerToken -> makerToken.
    /// @param makerTokenAmounts Maker token buy amount for each sample.
    /// @return takerTokenAmounts Taker amounts sold at each maker token
    ///         amount.
    function sampleBuysFromUniswapV2(
        address[] memory path,
        uint256[] memory makerTokenAmounts
    )
        public
        view
        returns (uint256[] memory takerTokenAmounts)
    {
        uint256 numSamples = makerTokenAmounts.length;
        takerTokenAmounts = new uint256[](numSamples);
        for (uint256 i = 0; i < numSamples; i++) {
            (bool didSucceed, bytes memory resultData) =
                _getUniswapV2Router01Address().staticcall.gas(UNISWAPV2_CALL_GAS)(
                    abi.encodeWithSelector(
                        IUniswapV2Router01(0).getAmountsIn.selector,
                        makerTokenAmounts[i],
                        path
                    ));
            uint256 sellAmount = 0;
            if (didSucceed) {
                // solhint-disable-next-line indent
                sellAmount = abi.decode(resultData, (uint256[]))[0];
            } else {
                break;
            }
            takerTokenAmounts[i] = sellAmount;
        }
    }

    /// @dev Overridable way to get token decimals.
    /// @param tokenAddress Address of the token.
    /// @return decimals The decimal places for the token.
    function _getTokenDecimals(address tokenAddress)
        internal
        view
        returns (uint8 decimals)
    {
        return LibERC20Token.decimals(tokenAddress);
    }

    /// @dev Gracefully calls a Uniswap pricing function.
    /// @param uniswapExchangeAddress Address of an `IUniswapExchangeQuotes` exchange.
    /// @param functionSelector Selector of the target function.
    /// @param inputAmount Quantity parameter particular to the pricing function.
    /// @return outputAmount The returned amount from the function call. Will be
    ///         zero if the call fails or if `uniswapExchangeAddress` is zero.
    function _callUniswapExchangePriceFunction(
        address uniswapExchangeAddress,
        bytes4 functionSelector,
        uint256 inputAmount
    )
        private
        view
        returns (uint256 outputAmount, bool didSucceed)
    {
        if (uniswapExchangeAddress == address(0)) {
            return (outputAmount, didSucceed);
        }
        bytes memory resultData;
        (didSucceed, resultData) =
            uniswapExchangeAddress.staticcall.gas(UNISWAP_CALL_GAS)(
                abi.encodeWithSelector(
                    functionSelector,
                    inputAmount
                ));
        if (didSucceed) {
            outputAmount = abi.decode(resultData, (uint256));
        }
    }

    /// @dev Retrive an existing Uniswap exchange contract.
    ///      Throws if the exchange does not exist.
    /// @param tokenAddress Address of the token contract.
    /// @return exchange `IUniswapExchangeQuotes` for the token.
    function _getUniswapExchange(address tokenAddress)
        private
        view
        returns (IUniswapExchangeQuotes exchange)
    {
        exchange = IUniswapExchangeQuotes(
            address(IUniswapExchangeFactory(_getUniswapExchangeFactoryAddress())
            .getExchange(tokenAddress))
        );
    }

    /// @dev Assert that the tokens in a trade pair are valid.
    /// @param makerToken Address of the maker token.
    /// @param takerToken Address of the taker token.
    function _assertValidPair(address makerToken, address takerToken)
        private
        pure
    {
        require(makerToken != takerToken, "ERC20BridgeSampler/INVALID_TOKEN_PAIR");
    }

    function _sampleSellForApproximateBuy(
        address takerToken,
        address makerToken,
        uint256 takerTokenAmount,
        bytes4 selector,
        address plpRegistryAddress
    )
        private
        view
        returns (uint256 makerTokenAmount)
    {
        bytes memory callData;
        uint256[] memory tmpTakerAmounts = new uint256[](1);
        tmpTakerAmounts[0] = takerTokenAmount;
        if (selector == this.sampleSellsFromKyberNetwork.selector) {
            callData = abi.encodeWithSelector(
                this.sampleSellsFromKyberNetwork.selector,
                takerToken,
                makerToken,
                tmpTakerAmounts
            );
        } else {
            callData = abi.encodeWithSelector(
                this.sampleSellsFromLiquidityProviderRegistry.selector,
                plpRegistryAddress,
                takerToken,
                makerToken,
                tmpTakerAmounts
            );
        }
        (bool success, bytes memory resultData) = address(this).staticcall(callData);
        if (!success) {
            return 0;
        }
        // solhint-disable indent
        makerTokenAmount = abi.decode(resultData, (uint256[]))[0];
    }

    function _sampleApproximateBuysFromSource(
        address takerToken,
        address makerToken,
        uint256[] memory makerTokenAmounts,
        FakeBuyOptions memory opts,
        bytes4 selector,
        address plpRegistryAddress
    )
        private
        view
        returns (uint256[] memory takerTokenAmounts)
    {
        _assertValidPair(makerToken, takerToken);
        if (makerTokenAmounts.length == 0) {
            return takerTokenAmounts;
        }
        uint256 sellAmount;
        uint256 buyAmount;
        uint256 slippageFromTarget;
        takerTokenAmounts = new uint256[](makerTokenAmounts.length);
        sellAmount = _sampleSellForApproximateBuy(
            makerToken,
            takerToken,
            makerTokenAmounts[0],
            selector,
            plpRegistryAddress
        );

        if (sellAmount == 0) {
            return takerTokenAmounts;
        }

        buyAmount = _sampleSellForApproximateBuy(
            takerToken,
            makerToken,
            sellAmount,
            selector,
            plpRegistryAddress
        );
        if (buyAmount == 0) {
            return takerTokenAmounts;
        }

        for (uint256 i = 0; i < makerTokenAmounts.length; i++) {
            for (uint256 iter = 0; iter < opts.maxIterations; iter++) {
                // adjustedSellAmount = previousSellAmount * (target/actual) * JUMP_MULTIPLIER
                sellAmount = LibMath.getPartialAmountCeil(
                    makerTokenAmounts[i],
                    buyAmount,
                    sellAmount
                );
                sellAmount = LibMath.getPartialAmountCeil(
                    (10000 + opts.targetSlippageBps),
                    10000,
                    sellAmount
                );
                uint256 _buyAmount = _sampleSellForApproximateBuy(
                    takerToken,
                    makerToken,
                    sellAmount,
                    selector,
                    plpRegistryAddress
                );
                if (_buyAmount == 0) {
                    break;
                }
                // We re-use buyAmount next iteration, only assign if it is
                // non zero
                buyAmount = _buyAmount;
                // If we've reached our goal, exit early
                if (buyAmount >= makerTokenAmounts[i]) {
                    uint256 slippageFromTarget = (buyAmount - makerTokenAmounts[i]) * 10000 /
                                                makerTokenAmounts[i];
                    if (slippageFromTarget <= opts.targetSlippageBps) {
                        break;
                    }
                }
            }
            // We do our best to close in on the requested amount, but we can either over buy or under buy and exit
            // if we hit a max iteration limit
            // We scale the sell amount to get the approximate target
            takerTokenAmounts[i] = LibMath.getPartialAmountCeil(
                makerTokenAmounts[i],
                buyAmount,
                sellAmount
            );
        }
    }

    function _sampleSellFromKyberNetwork(
        address takerToken,
        address makerToken,
        uint256 takerTokenAmount
    )
        private
        view
        returns (uint256 makerTokenAmount, address reserve)
    {
        address _takerToken = takerToken == _getWethAddress() ? KYBER_ETH_ADDRESS : takerToken;
        address _makerToken = makerToken == _getWethAddress() ? KYBER_ETH_ADDRESS : makerToken;
        uint256 takerTokenDecimals = _getTokenDecimals(takerToken);
        uint256 makerTokenDecimals = _getTokenDecimals(makerToken);
        (bool didSucceed, bytes memory resultData) = _getKyberNetworkProxyAddress().staticcall.gas(DEFAULT_CALL_GAS)(
            abi.encodeWithSelector(
                IKyberNetworkProxy(0).kyberNetworkContract.selector
            ));
        if (!didSucceed) {
            return (0, address(0));
        }
        address kyberNetworkContract = abi.decode(resultData, (address));
        (didSucceed, resultData) =
            kyberNetworkContract.staticcall.gas(KYBER_CALL_GAS)(
                abi.encodeWithSelector(
                    IKyberNetwork(0).searchBestRate.selector,
                    _takerToken,
                    _makerToken,
                    takerTokenAmount,
                    false // usePermissionless
                ));
        uint256 rate = 0;
        address reserve;
        if (didSucceed) {
            (reserve, rate) = abi.decode(resultData, (address, uint256));
        } else {
            return (0, address(0));
        }
        makerTokenAmount =
            rate *
            takerTokenAmount *
            10 ** makerTokenDecimals /
            10 ** takerTokenDecimals /
            10 ** 18;

        return (makerTokenAmount, reserve);
    }
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"devUtilsAddress","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"constant":true,"inputs":[{"internalType":"bytes[]","name":"callDatas","type":"bytes[]"}],"name":"batchCall","outputs":[{"internalType":"bytes[]","name":"callResults","type":"bytes[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"registryAddress","type":"address"},{"internalType":"address","name":"takerToken","type":"address"},{"internalType":"address","name":"makerToken","type":"address"}],"name":"getLiquidityProviderFromRegistry","outputs":[{"internalType":"address","name":"providerAddress","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"components":[{"internalType":"address","name":"makerAddress","type":"address"},{"internalType":"address","name":"takerAddress","type":"address"},{"internalType":"address","name":"feeRecipientAddress","type":"address"},{"internalType":"address","name":"senderAddress","type":"address"},{"internalType":"uint256","name":"makerAssetAmount","type":"uint256"},{"internalType":"uint256","name":"takerAssetAmount","type":"uint256"},{"internalType":"uint256","name":"makerFee","type":"uint256"},{"internalType":"uint256","name":"takerFee","type":"uint256"},{"internalType":"uint256","name":"expirationTimeSeconds","type":"uint256"},{"internalType":"uint256","name":"salt","type":"uint256"},{"internalType":"bytes","name":"makerAssetData","type":"bytes"},{"internalType":"bytes","name":"takerAssetData","type":"bytes"},{"internalType":"bytes","name":"makerFeeAssetData","type":"bytes"},{"internalType":"bytes","name":"takerFeeAssetData","type":"bytes"}],"internalType":"struct LibOrder.Order[]","name":"orders","type":"tuple[]"},{"internalType":"bytes[]","name":"orderSignatures","type":"bytes[]"}],"name":"getOrderFillableMakerAssetAmounts","outputs":[{"internalType":"uint256[]","name":"orderFillableMakerAssetAmounts","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"components":[{"internalType":"address","name":"makerAddress","type":"address"},{"internalType":"address","name":"takerAddress","type":"address"},{"internalType":"address","name":"feeRecipientAddress","type":"address"},{"internalType":"address","name":"senderAddress","type":"address"},{"internalType":"uint256","name":"makerAssetAmount","type":"uint256"},{"internalType":"uint256","name":"takerAssetAmount","type":"uint256"},{"internalType":"uint256","name":"makerFee","type":"uint256"},{"internalType":"uint256","name":"takerFee","type":"uint256"},{"internalType":"uint256","name":"expirationTimeSeconds","type":"uint256"},{"internalType":"uint256","name":"salt","type":"uint256"},{"internalType":"bytes","name":"makerAssetData","type":"bytes"},{"internalType":"bytes","name":"takerAssetData","type":"bytes"},{"internalType":"bytes","name":"makerFeeAssetData","type":"bytes"},{"internalType":"bytes","name":"takerFeeAssetData","type":"bytes"}],"internalType":"struct LibOrder.Order[]","name":"orders","type":"tuple[]"},{"internalType":"bytes[]","name":"orderSignatures","type":"bytes[]"}],"name":"getOrderFillableTakerAssetAmounts","outputs":[{"internalType":"uint256[]","name":"orderFillableTakerAssetAmounts","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"curveAddress","type":"address"},{"internalType":"int128","name":"fromTokenIdx","type":"int128"},{"internalType":"int128","name":"toTokenIdx","type":"int128"},{"internalType":"uint256[]","name":"makerTokenAmounts","type":"uint256[]"}],"name":"sampleBuysFromCurve","outputs":[{"internalType":"uint256[]","name":"takerTokenAmounts","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"takerToken","type":"address"},{"internalType":"address","name":"makerToken","type":"address"},{"internalType":"uint256[]","name":"makerTokenAmounts","type":"uint256[]"}],"name":"sampleBuysFromEth2Dai","outputs":[{"internalType":"uint256[]","name":"takerTokenAmounts","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"takerToken","type":"address"},{"internalType":"address","name":"makerToken","type":"address"},{"internalType":"uint256[]","name":"makerTokenAmounts","type":"uint256[]"},{"components":[{"internalType":"uint256","name":"targetSlippageBps","type":"uint256"},{"internalType":"uint256","name":"maxIterations","type":"uint256"}],"internalType":"struct IERC20BridgeSampler.FakeBuyOptions","name":"opts","type":"tuple"}],"name":"sampleBuysFromKyberNetwork","outputs":[{"internalType":"uint256[]","name":"takerTokenAmounts","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"registryAddress","type":"address"},{"internalType":"address","name":"takerToken","type":"address"},{"internalType":"address","name":"makerToken","type":"address"},{"internalType":"uint256[]","name":"makerTokenAmounts","type":"uint256[]"},{"components":[{"internalType":"uint256","name":"targetSlippageBps","type":"uint256"},{"internalType":"uint256","name":"maxIterations","type":"uint256"}],"internalType":"struct IERC20BridgeSampler.FakeBuyOptions","name":"opts","type":"tuple"}],"name":"sampleBuysFromLiquidityProviderRegistry","outputs":[{"internalType":"uint256[]","name":"takerTokenAmounts","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"takerToken","type":"address"},{"internalType":"address","name":"makerToken","type":"address"},{"internalType":"uint256[]","name":"makerTokenAmounts","type":"uint256[]"}],"name":"sampleBuysFromUniswap","outputs":[{"internalType":"uint256[]","name":"takerTokenAmounts","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"uint256[]","name":"makerTokenAmounts","type":"uint256[]"}],"name":"sampleBuysFromUniswapV2","outputs":[{"internalType":"uint256[]","name":"takerTokenAmounts","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"curveAddress","type":"address"},{"internalType":"int128","name":"fromTokenIdx","type":"int128"},{"internalType":"int128","name":"toTokenIdx","type":"int128"},{"internalType":"uint256[]","name":"takerTokenAmounts","type":"uint256[]"}],"name":"sampleSellsFromCurve","outputs":[{"internalType":"uint256[]","name":"makerTokenAmounts","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"takerToken","type":"address"},{"internalType":"address","name":"makerToken","type":"address"},{"internalType":"uint256[]","name":"takerTokenAmounts","type":"uint256[]"}],"name":"sampleSellsFromEth2Dai","outputs":[{"internalType":"uint256[]","name":"makerTokenAmounts","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"takerToken","type":"address"},{"internalType":"address","name":"makerToken","type":"address"},{"internalType":"address","name":"intermediateToken","type":"address"},{"internalType":"uint256[]","name":"takerTokenAmounts","type":"uint256[]"}],"name":"sampleSellsFromEth2DaiHop","outputs":[{"internalType":"uint256[]","name":"makerTokenAmounts","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"takerToken","type":"address"},{"internalType":"address","name":"makerToken","type":"address"},{"internalType":"uint256[]","name":"takerTokenAmounts","type":"uint256[]"}],"name":"sampleSellsFromKyberNetwork","outputs":[{"internalType":"uint256[]","name":"makerTokenAmounts","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"registryAddress","type":"address"},{"internalType":"address","name":"takerToken","type":"address"},{"internalType":"address","name":"makerToken","type":"address"},{"internalType":"uint256[]","name":"takerTokenAmounts","type":"uint256[]"}],"name":"sampleSellsFromLiquidityProviderRegistry","outputs":[{"internalType":"uint256[]","name":"makerTokenAmounts","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"multibridge","type":"address"},{"internalType":"address","name":"takerToken","type":"address"},{"internalType":"address","name":"intermediateToken","type":"address"},{"internalType":"address","name":"makerToken","type":"address"},{"internalType":"uint256[]","name":"takerTokenAmounts","type":"uint256[]"}],"name":"sampleSellsFromMultiBridge","outputs":[{"internalType":"uint256[]","name":"makerTokenAmounts","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"takerToken","type":"address"},{"internalType":"address","name":"makerToken","type":"address"},{"internalType":"uint256[]","name":"takerTokenAmounts","type":"uint256[]"}],"name":"sampleSellsFromUniswap","outputs":[{"internalType":"uint256[]","name":"makerTokenAmounts","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"uint256[]","name":"takerTokenAmounts","type":"uint256[]"}],"name":"sampleSellsFromUniswapV2","outputs":[{"internalType":"uint256[]","name":"makerTokenAmounts","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"}]

60806040523480156200001157600080fd5b50604051620036403803806200364083398101604081905262000034916200006d565b600080546001600160a01b0319166001600160a01b0392909216919091179055620000c2565b80516200006781620000a8565b92915050565b6000602082840312156200008057600080fd5b60006200008e84846200005a565b949350505050565b60006001600160a01b03821662000067565b620000b38162000096565b8114620000bf57600080fd5b50565b61356e80620000d26000396000f3fe608060405234801561001057600080fd5b50600436106101165760003560e01c806368be3cf2116100a25780639f76ad35116100715780639f76ad3514610235578063abffc76114610248578063c7f7142e1461025b578063d0eea06d1461027b578063e68248f71461028e57610116565b806368be3cf2146101dc5780636dd6b78d146101fc5780638b123a021461020f57806398cdafba1461022257610116565b80634cb8e253116100e95780634cb8e2531461017d57806358306ba01461019057806359f515d0146101a357806360ee052a146101b657806364ee6ade146101c957610116565b80631796fb871461011b5780632d753aa414610144578063354152a3146101575780634703a7e61461016a575b600080fd5b61012e610129366004612c6f565b6102a1565b60405161013b919061330b565b60405180910390f35b61012e610152366004612a26565b61040d565b61012e610165366004612c6f565b610591565b61012e610178366004612ba9565b6106e6565b61012e61018b366004612ba9565b61085d565b61012e61019e366004612ab2565b610a02565b61012e6101b1366004612d51565b610a65565b61012e6101c4366004612ba9565b610b0e565b61012e6101d7366004612ba9565b610d02565b6101ef6101ea366004612d10565b610e63565b60405161013b91906132fa565b61012e61020a366004612ba9565b610f83565b61012e61021d366004612d51565b61115f565b61012e610230366004612b2a565b6113c6565b61012e610243366004612ab2565b6113e7565b61012e610256366004612cb3565b611579565b61026e6102693660046129d9565b6116f7565b60405161013b91906131d8565b61012e610289366004612c03565b6117e2565b61012e61029c366004612cb3565b6117fa565b6060600082519050806040519080825280602002602001820160405280156102d3578160200160208202803883390190505b50915060005b818110156104035760006060886001600160a01b0316620927c060006001600160a01b0316630e71d1b9905060e01b8a8a8a888151811061031657fe5b602002602001015160405160240161033093929190613345565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b031990941693909317909252905161036e91906131cc565b6000604051808303818686fa925050503d80600081146103aa576040519150601f19603f3d011682016040523d82523d6000602084013e6103af565b606091505b509092509050600082156103d857818060200190516103d19190810190612e25565b90506103e0565b505050610403565b808685815181106103ed57fe5b60209081029190910101525050506001016102d9565b5050949350505050565b60606000825190508060405190808252806020026020018201604052801561043f578160200160208202803883390190505b5091506001600160a01b0387166104565750610588565b60005b818110156105855760006060896001600160a01b031662061a8060006001600160a01b0316636e79e133905060e01b8b8b8b8b898151811061049757fe5b60200260200101516040516024016104b2949392919061323b565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b03199094169390931790925290516104f091906131cc565b6000604051808303818686fa925050503d806000811461052c576040519150601f19603f3d011682016040523d82523d6000602084013e610531565b606091505b5090925090506000821561055a57818060200190516105539190810190612e25565b9050610562565b505050610585565b8086858151811061056f57fe5b6020908102919091010152505050600101610459565b50505b95945050505050565b6060600082519050806040519080825280602002602001820160405280156105c3578160200160208202803883390190505b50915060005b818110156104035760006060886001600160a01b0316620927c060006001600160a01b03166307211ef7905060e01b8a8a8a888151811061060657fe5b602002602001015160405160240161062093929190613345565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b031990941693909317909252905161065e91906131cc565b6000604051808303818686fa925050503d806000811461069a576040519150601f19603f3d011682016040523d82523d6000602084013e61069f565b606091505b509092509050600082156103d857818060200190516106c19190810190612e25565b9050808685815181106106d057fe5b60209081029190910101525050506001016105c9565b60606106f28385611968565b815160408051828152602080840282010190915281801561071d578160200160208202803883390190505b50915060005b8181101561085457600060606107376119a7565b6001600160a01b0316620f424060006001600160a01b031663ff1fd974905060e01b8a8a8a888151811061076757fe5b60200260200101516040516024016107819392919061329d565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b03199094169390931790925290516107bf91906131cc565b6000604051808303818686fa925050503d80600081146107fb576040519150601f19603f3d011682016040523d82523d6000602084013e610800565b606091505b5090925090506000821561082957818060200190516108229190810190612e25565b9050610831565b505050610854565b8086858151811061083e57fe5b6020908102919091010152505050600101610723565b50509392505050565b60606108698385611968565b8151604080518281526020808402820101909152818015610894578160200160208202803883390190505b50915060006108a16119bf565b9050600080805b848110156109f657836001600160a01b0316896001600160a01b031614806108e15750836001600160a01b0316886001600160a01b0316145b1561095f5761090489898984815181106108f757fe5b60200260200101516119d7565b90935091506001600160a01b0382167331e085afd48a1d6e51cc193153d625e8f0514c7f148061095057506001600160a01b038216731e158c0e93c30d24e918ef83d1e0be23595c3c0f145b1561095a57600092505b6109d6565b61097089858984815181106108f757fe5b909350915082156109d6576000610988858a866119d7565b90945090506001600160a01b0383167331e085afd48a1d6e51cc193153d625e8f0514c7f1480156109ca5750806001600160a01b0316836001600160a01b0316145b156109d457600093505b505b828682815181106109e357fe5b60209081029190910101526001016108a8565b50505050509392505050565b6060826001600160a01b0316846001600160a01b03161480610a355750826001600160a01b0316856001600160a01b0316145b15610a3f57610a5d565b6060610a4c868585610d02565b9050610a59848683610d02565b9150505b949350505050565b6060610a71838361115f565b905060005b8351811015610b0757818181518110610a8b57fe5b6020026020010151600014610aff57610ae6828281518110610aa957fe5b6020026020010151858381518110610abd57fe5b602002602001015160a00151868481518110610ad557fe5b602002602001015160800151611c77565b828281518110610af257fe5b6020026020010181815250505b600101610a76565b5092915050565b6060610b1a8385611968565b8151604080518281526020808402820101909152818015610b45578160200160208202803883390190505b5091506000610b526119bf565b6001600160a01b0316866001600160a01b031614610b7857610b7386611cb9565b610b7b565b60005b90506000610b876119bf565b6001600160a01b0316866001600160a01b031614610bad57610ba886611cb9565b610bb0565b60005b905060005b83811015610cf7576001610bc76119bf565b6001600160a01b0316886001600160a01b03161415610c2b578651610c0a9085906309903d8b60e21b908a9086908110610bfd57fe5b6020026020010151611d44565b878481518110610c1657fe5b60200260200101819350828152505050610ce3565b610c336119bf565b6001600160a01b0316896001600160a01b03161415610c69578651610c0a908490632cf4a43160e11b908a9086908110610bfd57fe5b8651600090610c89908590632cf4a43160e11b908b9087908110610bfd57fe5b925090508015610cc657610ca5856309903d8b60e21b83611d44565b888581518110610cb157fe5b60200260200101819450828152505050610ce1565b6000878481518110610cd457fe5b6020026020010181815250505b505b80610cee5750610cf7565b50600101610bb5565b505050509392505050565b6060610d0e8385611968565b8151604080518281526020808402820101909152818015610d39578160200160208202803883390190505b50915060005b818110156108545760006060610d536119a7565b6001600160a01b0316620f424060006001600160a01b031663144a2752905060e01b898b8a8881518110610d8357fe5b6020026020010151604051602401610d9d9392919061329d565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b0319909416939093179092529051610ddb91906131cc565b6000604051808303818686fa925050503d8060008114610e17576040519150601f19603f3d011682016040523d82523d6000602084013e610e1c565b606091505b509092509050600082156108295781806020019051610e3e9190810190612e25565b905080868581518110610e4d57fe5b6020908102919091010152505050600101610d3f565b604080518281526020808402820101909152606090828015610e9957816020015b6060815260200190600190039081610e845790505b50905060005b808314610b07576000606030868685818110610eb757fe5b602002820190508035601e1936849003018112610ed357600080fd5b909101602081019150356001600160401b03811115610ef157600080fd5b36819003821315610f0157600080fd5b604051610f0f9291906131bf565b600060405180830381855afa9150503d8060008114610f4a576040519150601f19603f3d011682016040523d82523d6000602084013e610f4f565b606091505b509150915081610f6157805160208201fd5b80848481518110610f6e57fe5b60209081029190910101525050600101610e9f565b6060610f8f8385611968565b8151604080518281526020808402820101909152818015610fba578160200160208202803883390190505b5091506000610fc76119bf565b6001600160a01b0316866001600160a01b031614610fed57610fe886611cb9565b610ff0565b60005b90506000610ffc6119bf565b6001600160a01b0316866001600160a01b0316146110225761101d86611cb9565b611025565b60005b905060005b83811015610cf757600161103c6119bf565b6001600160a01b0316886001600160a01b031614156110935786516110729085906395b68fe760e01b908a9086908110610bfd57fe5b87848151811061107e57fe5b6020026020010181935082815250505061114b565b61109b6119bf565b6001600160a01b0316896001600160a01b031614156110d157865161107290849063cd7724c360e01b908a9086908110610bfd57fe5b86516000906110f19086906395b68fe760e01b908b9087908110610bfd57fe5b92509050801561112e5761110d8463cd7724c360e01b83611d44565b88858151811061111957fe5b60200260200101819450828152505050611149565b600087848151811061113c57fe5b6020026020010181815250505b505b806111565750610cf7565b5060010161102a565b6060825160405190808252806020026020018201604052801561118c578160200160208202803883390190505b50600080549192506001600160a01b03909116905b845181146113be578381815181106111b557fe5b602002602001015151600014806111e357508481815181106111d357fe5b6020026020010151608001516000145b8061120557508481815181106111f557fe5b602002602001015160a001516000145b1561122957600083828151811061121857fe5b6020026020010181815250506113b6565b60006060836001600160a01b03166207a120856001600160a01b031663e77286eb905060e01b89868151811061125b57fe5b602002602001015189878151811061126f57fe5b6020026020010151604051602401611288929190613370565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b03199094169390931790925290516112c691906131cc565b6000604051808303818686fa925050503d8060008114611302576040519150601f19603f3d011682016040523d82523d6000602084013e611307565b606091505b50915091508161133257600085848151811061131f57fe5b60200260200101818152505050506113b6565b61133a61241f565b600080838060200190516113519190810190612de2565b9194509250905060038351600681111561136757fe5b141580611372575080155b1561139657600088878151811061138557fe5b6020026020010181815250506113b0565b818887815181106113a357fe5b6020026020010181815250505b50505050505b6001016111a1565b505092915050565b60606113dd85858585639f76ad3560e01b8b611e27565b9695505050505050565b606060008251905080604051908082528060200260200182016040528015611419578160200160208202803883390190505b50915060006114298787876116f7565b90506001600160a01b0381166114415750610a5d9050565b60005b8281101561156e5760006060836001600160a01b031662061a8060006001600160a01b031663343fbcdd905060e01b8b8b8b888151811061148157fe5b602002602001015160405160240161149b9392919061329d565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b03199094169390931790925290516114d991906131cc565b6000604051808303818686fa925050503d8060008114611515576040519150601f19603f3d011682016040523d82523d6000602084013e61151a565b606091505b50909250905060008215611543578180602001905161153c9190810190612e25565b905061154b565b50505061156e565b8087858151811061155857fe5b6020908102919091010152505050600101611444565b505050949350505050565b6060600082519050806040519080825280602002602001820160405280156115ab578160200160208202803883390190505b50915060005b818110156113be57600060606115c5611fed565b6001600160a01b0316620249f060006001600160a01b031663d06ca61f905060e01b8886815181106115f357fe5b60200260200101518a60405160240161160d9291906133a3565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b031990941693909317909252905161164b91906131cc565b6000604051808303818686fa925050503d8060008114611687576040519150601f19603f3d011682016040523d82523d6000602084013e61168c565b606091505b509092509050600082156116cc57818060200190516116ae9190810190612dae565b6001895103815181106116bd57fe5b602002602001015190506116d4565b5050506113be565b808685815181106116e157fe5b60209081029190910101525050506001016115b1565b60405160009060609063153f599760e01b9061171990869086906024016131e6565b604051602081830303815290604052906001600160e01b0319166020820180516001600160e01b038381831617835250505050905060006060866001600160a01b03168360405161176a91906131cc565b600060405180830381855afa9150503d80600081146117a5576040519150601f19603f3d011682016040523d82523d6000602084013e6117aa565b606091505b50915091508180156117bd575080516020145b156117d7576117cd81600c612005565b93505050506117db565b5050505b9392505050565b606061058885858585634cb8e25360e01b6000611e27565b60606000825190508060405190808252806020026020018201604052801561182c578160200160208202803883390190505b50915060005b818110156113be5760006060611846611fed565b6001600160a01b0316620249f060006001600160a01b0316631f00ca74905060e01b88868151811061187457fe5b60200260200101518a60405160240161188e9291906133a3565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b03199094169390931790925290516118cc91906131cc565b6000604051808303818686fa925050503d8060008114611908576040519150601f19603f3d011682016040523d82523d6000602084013e61190d565b606091505b509092509050600082156116cc578180602001905161192f9190810190612dae565b60008151811061193b57fe5b602002602001015190508086858151811061195257fe5b6020908102919091010152505050600101611832565b806001600160a01b0316826001600160a01b031614156119a35760405162461bcd60e51b815260040161199a90613360565b60405180910390fd5b5050565b73794e6e91555438afc3ccf1c5076a74f42133d08d90565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc290565b60008060006119e46119bf565b6001600160a01b0316866001600160a01b031614611a025785611a18565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b90506000611a246119bf565b6001600160a01b0316866001600160a01b031614611a425785611a58565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b90506000611a658861203d565b60ff1690506000611a758861203d565b60ff16905060006060611a8661204d565b60408051600481526024810182526020810180516001600160e01b0316634f61ff8b60e01b17905290516001600160a01b03929092169162061a8091611acb916131cc565b6000604051808303818686fa925050503d8060008114611b07576040519150601f19603f3d011682016040523d82523d6000602084013e611b0c565b606091505b509150915081611b29575060009650869550611c6f945050505050565b600081806020019051611b3f9190810190612981565b9050806001600160a01b03166216e36060006001600160a01b0316630c235d96905060e01b89898e6000604051602401611b7c94939291906132c5565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b0319909416939093179092529051611bba91906131cc565b6000604051808303818686fa925050503d8060008114611bf6576040519150601f19603f3d011682016040523d82523d6000602084013e611bfb565b606091505b5090935091506000808415611c275783806020019051611c1e919081019061299f565b92509050611c3d565b5060009950899850611c6f975050505050505050565b670de0b6b3a764000087600a0a87600a0a8e85020281611c5957fe5b0481611c6157fe5b049a50985050505050505050505b935093915050565b6000610a5d83611cad611c9182600163ffffffff61206516565b611ca1888763ffffffff61208416565b9063ffffffff6120b516565b9063ffffffff6120d116565b6000611cc36120fb565b6001600160a01b03166306f2bf62836040518263ffffffff1660e01b8152600401611cee91906131d8565b60206040518083038186803b158015611d0657600080fd5b505afa158015611d1a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611d3e9190810190612981565b92915050565b6000806001600160a01b038516611d5a57611c6f565b6060856001600160a01b0316620249f08686604051602401611d7c9190613395565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b0319909416939093179092529051611dba91906131cc565b6000604051808303818686fa925050503d8060008114611df6576040519150601f19603f3d011682016040523d82523d6000602084013e611dfb565b606091505b5090925090508115611e1e5780806020019051611e1b9190810190612e25565b92505b50935093915050565b6060611e338688611968565b8451611e3e576113dd565b60008060008751604051908082528060200260200182016040528015611e6e578160200160208202803883390190505b509350611e92898b8a600081518110611e8357fe5b60200260200101518989612113565b925082611ea257506113dd915050565b611eaf8a8a858989612113565b915081611ebf57506113dd915050565b60005b8851811015611fdf5760005b8860200151811015611fa157611ef88a8381518110611ee957fe5b60200260200101518587611c77565b9450611f0f89600001516127100161271087611c77565b94506000611f208d8d888c8c612113565b905080611f2d5750611fa1565b8094508a8381518110611f3c57fe5b60200260200101518510611f985760008b8481518110611f5857fe5b60200260200101518c8581518110611f6c57fe5b602002602001015187036127100281611f8157fe5b0490508a600001518111611f96575050611fa1565b505b50600101611ece565b50611fc0898281518110611fb157fe5b60200260200101518486611c77565b858281518110611fcc57fe5b6020908102919091010152600101611ec2565b505050509695505050505050565b73f164fc0ec4e93095b804a4795bbe1e041497b92a90565b6000816014018351101561202b5761202b61202660048551856014016122c2565b61231c565b5001601401516001600160a01b031690565b6000611d3e82612324565b919050565b73818e6fecd516ecc3849daf6845e3ec868087b75590565b60008282111561207e5761207e612026600285856123cf565b50900390565b60008261209357506000611d3e565b828202828482816120a057fe5b04146117db576117db612026600186866123cf565b6000828201838110156117db576117db612026600086866123cf565b6000816120e7576120e7612026600385856123cf565b60008284816120f257fe5b04949350505050565b73c0a47dfe034b400b47bdad5fecda2621de6c4d9590565b604080516001808252818301909252600091606091829160208083019080388339019050509050858160008151811061214857fe5b60209081029190910101526001600160e01b03198516634cb8e25360e01b14156121c357604051634cb8e25360e01b9061218a908a908a908590602401613270565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b0319909316929092179091529150612218565b604051639f76ad3560e01b906121e39086908b908b908690602401613201565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b03199093169290921790915291505b60006060306001600160a01b03168460405161223491906131cc565b600060405180830381855afa9150503d806000811461226f576040519150601f19603f3d011682016040523d82523d6000602084013e612274565b606091505b50915091508161228b576000945050505050610588565b8080602001905161229f9190810190612dae565b6000815181106122ab57fe5b602002602001015194505050505095945050505050565b6060632800659560e01b8484846040516024016122e193929190613337565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b03199093169290921790915290509392505050565b805160208201fd5b60006012905060006060836001600160a01b031660405180604001604052806004815260200163313ce56760e01b81525060405161236291906131cc565b600060405180830381855afa9150503d806000811461239d576040519150601f19603f3d011682016040523d82523d6000602084013e6123a2565b606091505b50915091508180156123b5575080516020145b156123c8576123c58160006123ee565b92505b5050919050565b606063e946c1bb60e01b8484846040516024016122e19392919061331c565b60006117db8383600081602001835110156124165761241661202660058551856020016122c2565b50016020015190565b6040805160608101909152806000815260006020820181905260409091015290565b8035611d3e816134ef565b8051611d3e816134ef565b600082601f83011261246857600080fd5b813561247b612476826133e9565b6133c3565b915081818352602084019350602081019050838560208402820111156124a057600080fd5b60005b838110156124cc57816124b68882612441565b84525060209283019291909101906001016124a3565b5050505092915050565b60008083601f8401126124e857600080fd5b5081356001600160401b038111156124ff57600080fd5b60208301915083602082028301111561251757600080fd5b9250929050565b600082601f83011261252f57600080fd5b813561253d612476826133e9565b81815260209384019390925082018360005b838110156124cc578135860161256588826126ce565b845250602092830192919091019060010161254f565b600082601f83011261258c57600080fd5b813561259a612476826133e9565b81815260209384019390925082018360005b838110156124cc57813586016125c288826127d5565b84525060209283019291909101906001016125ac565b600082601f8301126125e957600080fd5b81516125f7612476826133e9565b9150818183526020840193506020810190508385602084028201111561261c57600080fd5b60005b838110156124cc578161263288826126c3565b845250602092830192919091019060010161261f565b600082601f83011261265957600080fd5b8135612667612476826133e9565b9150818183526020840193506020810190508385602084028201111561268c57600080fd5b60005b838110156124cc57816126a28882612976565b845250602092830192919091019060010161268f565b8051611d3e81613503565b8051611d3e8161350c565b600082601f8301126126df57600080fd5b81356126ed61247682613409565b9150808252602083016020830185838301111561270957600080fd5b612714838284613492565b50505092915050565b8051611d3e81613515565b8035611d3e81613522565b60006040828403121561274557600080fd5b61274f60406133c3565b9050600061275d8484612976565b825250602061276e84848301612976565b60208301525092915050565b60006060828403121561278c57600080fd5b61279660606133c3565b905060006127a4848461271d565b82525060206127b5848483016126c3565b60208301525060406127c9848285016126c3565b60408301525092915050565b60006101c082840312156127e857600080fd5b6127f36101c06133c3565b905060006128018484612441565b825250602061281284848301612441565b602083015250604061282684828501612441565b604083015250606061283a84828501612441565b606083015250608061284e84828501612976565b60808301525060a061286284828501612976565b60a08301525060c061287684828501612976565b60c08301525060e061288a84828501612976565b60e08301525061010061289f84828501612976565b610100830152506101206128b584828501612976565b610120830152506101408201356001600160401b038111156128d657600080fd5b6128e2848285016126ce565b610140830152506101608201356001600160401b0381111561290357600080fd5b61290f848285016126ce565b610160830152506101808201356001600160401b0381111561293057600080fd5b61293c848285016126ce565b610180830152506101a08201356001600160401b0381111561295d57600080fd5b612969848285016126ce565b6101a08301525092915050565b8035611d3e8161350c565b60006020828403121561299357600080fd5b6000610a5d848461244c565b600080604083850312156129b257600080fd5b60006129be858561244c565b92505060206129cf858286016126c3565b9150509250929050565b6000806000606084860312156129ee57600080fd5b60006129fa8686612441565b9350506020612a0b86828701612441565b9250506040612a1c86828701612441565b9150509250925092565b600080600080600060a08688031215612a3e57600080fd5b6000612a4a8888612441565b9550506020612a5b88828901612441565b9450506040612a6c88828901612441565b9350506060612a7d88828901612441565b92505060808601356001600160401b03811115612a9957600080fd5b612aa588828901612648565b9150509295509295909350565b60008060008060808587031215612ac857600080fd5b6000612ad48787612441565b9450506020612ae587828801612441565b9350506040612af687828801612441565b92505060608501356001600160401b03811115612b1257600080fd5b612b1e87828801612648565b91505092959194509250565b600080600080600060c08688031215612b4257600080fd5b6000612b4e8888612441565b9550506020612b5f88828901612441565b9450506040612b7088828901612441565b93505060608601356001600160401b03811115612b8c57600080fd5b612b9888828901612648565b9250506080612aa588828901612733565b600080600060608486031215612bbe57600080fd5b6000612bca8686612441565b9350506020612bdb86828701612441565b92505060408401356001600160401b03811115612bf757600080fd5b612a1c86828701612648565b60008060008060a08587031215612c1957600080fd5b6000612c258787612441565b9450506020612c3687828801612441565b93505060408501356001600160401b03811115612c5257600080fd5b612c5e87828801612648565b9250506060612b1e87828801612733565b60008060008060808587031215612c8557600080fd5b6000612c918787612441565b9450506020612ca287828801612728565b9350506040612af687828801612728565b60008060408385031215612cc657600080fd5b82356001600160401b03811115612cdc57600080fd5b612ce885828601612457565b92505060208301356001600160401b03811115612d0457600080fd5b6129cf85828601612648565b60008060208385031215612d2357600080fd5b82356001600160401b03811115612d3957600080fd5b612d45858286016124d6565b92509250509250929050565b60008060408385031215612d6457600080fd5b82356001600160401b03811115612d7a57600080fd5b612d868582860161257b565b92505060208301356001600160401b03811115612da257600080fd5b6129cf8582860161251e565b600060208284031215612dc057600080fd5b81516001600160401b03811115612dd657600080fd5b610a5d848285016125d8565b600080600060a08486031215612df757600080fd5b6000612e03868661277a565b9350506060612e14868287016126c3565b9250506080612a1c868287016126b8565b600060208284031215612e3757600080fd5b6000610a5d84846126c3565b6000612e4f8383612e6f565b505060200190565b60006117db8383612fea565b6000612e4f83836131b6565b612e7881613443565b82525050565b6000612e8982613436565b612e93818561343a565b9350612e9e83613430565b8060005b83811015612ecc578151612eb68882612e43565b9750612ec183613430565b925050600101612ea2565b509495945050505050565b6000612ee282613436565b612eec818561343a565b935083602082028501612efe85613430565b8060005b85811015612f385784840389528151612f1b8582612e57565b9450612f2683613430565b60209a909a0199925050600101612f02565b5091979650505050505050565b6000612f5082613436565b612f5a818561343a565b9350612f6583613430565b8060005b83811015612ecc578151612f7d8882612e63565b9750612f8883613430565b925050600101612f69565b612e788161344e565b6000612fa88385612048565b9350612fb5838584613492565b50500190565b6000612fc682613436565b612fd08185612048565b9350612fe081856020860161349e565b9290920192915050565b6000612ff582613436565b612fff818561343a565b935061300f81856020860161349e565b613018816134ce565b9093019392505050565b612e788161347c565b612e7881613487565b612e788161346a565b600061304a60258361343a565b7f455243323042726964676553616d706c65722f494e56414c49445f544f4b454e8152642fa820a4a960d91b602082015260400192915050565b80516000906101c08401906130998582612e6f565b5060208301516130ac6020860182612e6f565b5060408301516130bf6040860182612e6f565b5060608301516130d26060860182612e6f565b5060808301516130e560808601826131b6565b5060a08301516130f860a08601826131b6565b5060c083015161310b60c08601826131b6565b5060e083015161311e60e08601826131b6565b506101008301516131336101008601826131b6565b506101208301516131486101208601826131b6565b506101408301518482036101408601526131628282612fea565b91505061016083015184820361016086015261317e8282612fea565b91505061018083015184820361018086015261319a8282612fea565b9150506101a08301518482036101a08601526105888282612fea565b612e7881613453565b6000610a5d828486612f9c565b60006117db8284612fbb565b60208101611d3e8284612e6f565b604081016131f48285612e6f565b6117db6020830184612e6f565b6080810161320f8287612e6f565b61321c6020830186612e6f565b6132296040830185612e6f565b81810360608301526113dd8184612f45565b608081016132498287612e6f565b6132566020830186612e6f565b6132636040830185612e6f565b61058860608301846131b6565b6060810161327e8286612e6f565b61328b6020830185612e6f565b81810360408301526105888184612f45565b606081016132ab8286612e6f565b6132b86020830185612e6f565b610a5d60408301846131b6565b608081016132d38287612e6f565b6132e06020830186612e6f565b6132ed60408301856131b6565b6105886060830184612f93565b602080825281016117db8184612ed7565b602080825281016117db8184612f45565b6060810161332a8286613022565b6132b860208301856131b6565b6060810161332a828661302b565b606081016133538286613034565b6132b86020830185613034565b60208082528101611d3e8161303d565b604080825281016133818185613084565b90508181036020830152610a5d8184612fea565b60208101611d3e82846131b6565b604081016133b182856131b6565b8181036020830152610a5d8184612e7e565b6040518181016001600160401b03811182821017156133e157600080fd5b604052919050565b60006001600160401b038211156133ff57600080fd5b5060209081020190565b60006001600160401b0382111561341f57600080fd5b506020601f91909101601f19160190565b60200190565b5190565b90815260200190565b6000611d3e82613470565b151590565b90565b80612048816134d8565b80612048816134e5565b600f0b90565b6001600160a01b031690565b6000611d3e82613456565b6000611d3e82613460565b82818337506000910152565b60005b838110156134b95781810151838201526020016134a1565b838111156134c8576000848401525b50505050565b601f01601f191690565b600481106134e257fe5b50565b600881106134e257fe5b6134f881613443565b81146134e257600080fd5b6134f88161344e565b6134f881613453565b600781106134e257600080fd5b6134f88161346a56fea365627a7a72315820ddb003984abfa8a85ae40b8965430411b3a89ecf35a231473731767cd99143826c6578706572696d656e74616cf564736f6c6343000511004000000000000000000000000074134cf88b21383713e096a5ecf59e297dc7f547

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106101165760003560e01c806368be3cf2116100a25780639f76ad35116100715780639f76ad3514610235578063abffc76114610248578063c7f7142e1461025b578063d0eea06d1461027b578063e68248f71461028e57610116565b806368be3cf2146101dc5780636dd6b78d146101fc5780638b123a021461020f57806398cdafba1461022257610116565b80634cb8e253116100e95780634cb8e2531461017d57806358306ba01461019057806359f515d0146101a357806360ee052a146101b657806364ee6ade146101c957610116565b80631796fb871461011b5780632d753aa414610144578063354152a3146101575780634703a7e61461016a575b600080fd5b61012e610129366004612c6f565b6102a1565b60405161013b919061330b565b60405180910390f35b61012e610152366004612a26565b61040d565b61012e610165366004612c6f565b610591565b61012e610178366004612ba9565b6106e6565b61012e61018b366004612ba9565b61085d565b61012e61019e366004612ab2565b610a02565b61012e6101b1366004612d51565b610a65565b61012e6101c4366004612ba9565b610b0e565b61012e6101d7366004612ba9565b610d02565b6101ef6101ea366004612d10565b610e63565b60405161013b91906132fa565b61012e61020a366004612ba9565b610f83565b61012e61021d366004612d51565b61115f565b61012e610230366004612b2a565b6113c6565b61012e610243366004612ab2565b6113e7565b61012e610256366004612cb3565b611579565b61026e6102693660046129d9565b6116f7565b60405161013b91906131d8565b61012e610289366004612c03565b6117e2565b61012e61029c366004612cb3565b6117fa565b6060600082519050806040519080825280602002602001820160405280156102d3578160200160208202803883390190505b50915060005b818110156104035760006060886001600160a01b0316620927c060006001600160a01b0316630e71d1b9905060e01b8a8a8a888151811061031657fe5b602002602001015160405160240161033093929190613345565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b031990941693909317909252905161036e91906131cc565b6000604051808303818686fa925050503d80600081146103aa576040519150601f19603f3d011682016040523d82523d6000602084013e6103af565b606091505b509092509050600082156103d857818060200190516103d19190810190612e25565b90506103e0565b505050610403565b808685815181106103ed57fe5b60209081029190910101525050506001016102d9565b5050949350505050565b60606000825190508060405190808252806020026020018201604052801561043f578160200160208202803883390190505b5091506001600160a01b0387166104565750610588565b60005b818110156105855760006060896001600160a01b031662061a8060006001600160a01b0316636e79e133905060e01b8b8b8b8b898151811061049757fe5b60200260200101516040516024016104b2949392919061323b565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b03199094169390931790925290516104f091906131cc565b6000604051808303818686fa925050503d806000811461052c576040519150601f19603f3d011682016040523d82523d6000602084013e610531565b606091505b5090925090506000821561055a57818060200190516105539190810190612e25565b9050610562565b505050610585565b8086858151811061056f57fe5b6020908102919091010152505050600101610459565b50505b95945050505050565b6060600082519050806040519080825280602002602001820160405280156105c3578160200160208202803883390190505b50915060005b818110156104035760006060886001600160a01b0316620927c060006001600160a01b03166307211ef7905060e01b8a8a8a888151811061060657fe5b602002602001015160405160240161062093929190613345565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b031990941693909317909252905161065e91906131cc565b6000604051808303818686fa925050503d806000811461069a576040519150601f19603f3d011682016040523d82523d6000602084013e61069f565b606091505b509092509050600082156103d857818060200190516106c19190810190612e25565b9050808685815181106106d057fe5b60209081029190910101525050506001016105c9565b60606106f28385611968565b815160408051828152602080840282010190915281801561071d578160200160208202803883390190505b50915060005b8181101561085457600060606107376119a7565b6001600160a01b0316620f424060006001600160a01b031663ff1fd974905060e01b8a8a8a888151811061076757fe5b60200260200101516040516024016107819392919061329d565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b03199094169390931790925290516107bf91906131cc565b6000604051808303818686fa925050503d80600081146107fb576040519150601f19603f3d011682016040523d82523d6000602084013e610800565b606091505b5090925090506000821561082957818060200190516108229190810190612e25565b9050610831565b505050610854565b8086858151811061083e57fe5b6020908102919091010152505050600101610723565b50509392505050565b60606108698385611968565b8151604080518281526020808402820101909152818015610894578160200160208202803883390190505b50915060006108a16119bf565b9050600080805b848110156109f657836001600160a01b0316896001600160a01b031614806108e15750836001600160a01b0316886001600160a01b0316145b1561095f5761090489898984815181106108f757fe5b60200260200101516119d7565b90935091506001600160a01b0382167331e085afd48a1d6e51cc193153d625e8f0514c7f148061095057506001600160a01b038216731e158c0e93c30d24e918ef83d1e0be23595c3c0f145b1561095a57600092505b6109d6565b61097089858984815181106108f757fe5b909350915082156109d6576000610988858a866119d7565b90945090506001600160a01b0383167331e085afd48a1d6e51cc193153d625e8f0514c7f1480156109ca5750806001600160a01b0316836001600160a01b0316145b156109d457600093505b505b828682815181106109e357fe5b60209081029190910101526001016108a8565b50505050509392505050565b6060826001600160a01b0316846001600160a01b03161480610a355750826001600160a01b0316856001600160a01b0316145b15610a3f57610a5d565b6060610a4c868585610d02565b9050610a59848683610d02565b9150505b949350505050565b6060610a71838361115f565b905060005b8351811015610b0757818181518110610a8b57fe5b6020026020010151600014610aff57610ae6828281518110610aa957fe5b6020026020010151858381518110610abd57fe5b602002602001015160a00151868481518110610ad557fe5b602002602001015160800151611c77565b828281518110610af257fe5b6020026020010181815250505b600101610a76565b5092915050565b6060610b1a8385611968565b8151604080518281526020808402820101909152818015610b45578160200160208202803883390190505b5091506000610b526119bf565b6001600160a01b0316866001600160a01b031614610b7857610b7386611cb9565b610b7b565b60005b90506000610b876119bf565b6001600160a01b0316866001600160a01b031614610bad57610ba886611cb9565b610bb0565b60005b905060005b83811015610cf7576001610bc76119bf565b6001600160a01b0316886001600160a01b03161415610c2b578651610c0a9085906309903d8b60e21b908a9086908110610bfd57fe5b6020026020010151611d44565b878481518110610c1657fe5b60200260200101819350828152505050610ce3565b610c336119bf565b6001600160a01b0316896001600160a01b03161415610c69578651610c0a908490632cf4a43160e11b908a9086908110610bfd57fe5b8651600090610c89908590632cf4a43160e11b908b9087908110610bfd57fe5b925090508015610cc657610ca5856309903d8b60e21b83611d44565b888581518110610cb157fe5b60200260200101819450828152505050610ce1565b6000878481518110610cd457fe5b6020026020010181815250505b505b80610cee5750610cf7565b50600101610bb5565b505050509392505050565b6060610d0e8385611968565b8151604080518281526020808402820101909152818015610d39578160200160208202803883390190505b50915060005b818110156108545760006060610d536119a7565b6001600160a01b0316620f424060006001600160a01b031663144a2752905060e01b898b8a8881518110610d8357fe5b6020026020010151604051602401610d9d9392919061329d565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b0319909416939093179092529051610ddb91906131cc565b6000604051808303818686fa925050503d8060008114610e17576040519150601f19603f3d011682016040523d82523d6000602084013e610e1c565b606091505b509092509050600082156108295781806020019051610e3e9190810190612e25565b905080868581518110610e4d57fe5b6020908102919091010152505050600101610d3f565b604080518281526020808402820101909152606090828015610e9957816020015b6060815260200190600190039081610e845790505b50905060005b808314610b07576000606030868685818110610eb757fe5b602002820190508035601e1936849003018112610ed357600080fd5b909101602081019150356001600160401b03811115610ef157600080fd5b36819003821315610f0157600080fd5b604051610f0f9291906131bf565b600060405180830381855afa9150503d8060008114610f4a576040519150601f19603f3d011682016040523d82523d6000602084013e610f4f565b606091505b509150915081610f6157805160208201fd5b80848481518110610f6e57fe5b60209081029190910101525050600101610e9f565b6060610f8f8385611968565b8151604080518281526020808402820101909152818015610fba578160200160208202803883390190505b5091506000610fc76119bf565b6001600160a01b0316866001600160a01b031614610fed57610fe886611cb9565b610ff0565b60005b90506000610ffc6119bf565b6001600160a01b0316866001600160a01b0316146110225761101d86611cb9565b611025565b60005b905060005b83811015610cf757600161103c6119bf565b6001600160a01b0316886001600160a01b031614156110935786516110729085906395b68fe760e01b908a9086908110610bfd57fe5b87848151811061107e57fe5b6020026020010181935082815250505061114b565b61109b6119bf565b6001600160a01b0316896001600160a01b031614156110d157865161107290849063cd7724c360e01b908a9086908110610bfd57fe5b86516000906110f19086906395b68fe760e01b908b9087908110610bfd57fe5b92509050801561112e5761110d8463cd7724c360e01b83611d44565b88858151811061111957fe5b60200260200101819450828152505050611149565b600087848151811061113c57fe5b6020026020010181815250505b505b806111565750610cf7565b5060010161102a565b6060825160405190808252806020026020018201604052801561118c578160200160208202803883390190505b50600080549192506001600160a01b03909116905b845181146113be578381815181106111b557fe5b602002602001015151600014806111e357508481815181106111d357fe5b6020026020010151608001516000145b8061120557508481815181106111f557fe5b602002602001015160a001516000145b1561122957600083828151811061121857fe5b6020026020010181815250506113b6565b60006060836001600160a01b03166207a120856001600160a01b031663e77286eb905060e01b89868151811061125b57fe5b602002602001015189878151811061126f57fe5b6020026020010151604051602401611288929190613370565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b03199094169390931790925290516112c691906131cc565b6000604051808303818686fa925050503d8060008114611302576040519150601f19603f3d011682016040523d82523d6000602084013e611307565b606091505b50915091508161133257600085848151811061131f57fe5b60200260200101818152505050506113b6565b61133a61241f565b600080838060200190516113519190810190612de2565b9194509250905060038351600681111561136757fe5b141580611372575080155b1561139657600088878151811061138557fe5b6020026020010181815250506113b0565b818887815181106113a357fe5b6020026020010181815250505b50505050505b6001016111a1565b505092915050565b60606113dd85858585639f76ad3560e01b8b611e27565b9695505050505050565b606060008251905080604051908082528060200260200182016040528015611419578160200160208202803883390190505b50915060006114298787876116f7565b90506001600160a01b0381166114415750610a5d9050565b60005b8281101561156e5760006060836001600160a01b031662061a8060006001600160a01b031663343fbcdd905060e01b8b8b8b888151811061148157fe5b602002602001015160405160240161149b9392919061329d565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b03199094169390931790925290516114d991906131cc565b6000604051808303818686fa925050503d8060008114611515576040519150601f19603f3d011682016040523d82523d6000602084013e61151a565b606091505b50909250905060008215611543578180602001905161153c9190810190612e25565b905061154b565b50505061156e565b8087858151811061155857fe5b6020908102919091010152505050600101611444565b505050949350505050565b6060600082519050806040519080825280602002602001820160405280156115ab578160200160208202803883390190505b50915060005b818110156113be57600060606115c5611fed565b6001600160a01b0316620249f060006001600160a01b031663d06ca61f905060e01b8886815181106115f357fe5b60200260200101518a60405160240161160d9291906133a3565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b031990941693909317909252905161164b91906131cc565b6000604051808303818686fa925050503d8060008114611687576040519150601f19603f3d011682016040523d82523d6000602084013e61168c565b606091505b509092509050600082156116cc57818060200190516116ae9190810190612dae565b6001895103815181106116bd57fe5b602002602001015190506116d4565b5050506113be565b808685815181106116e157fe5b60209081029190910101525050506001016115b1565b60405160009060609063153f599760e01b9061171990869086906024016131e6565b604051602081830303815290604052906001600160e01b0319166020820180516001600160e01b038381831617835250505050905060006060866001600160a01b03168360405161176a91906131cc565b600060405180830381855afa9150503d80600081146117a5576040519150601f19603f3d011682016040523d82523d6000602084013e6117aa565b606091505b50915091508180156117bd575080516020145b156117d7576117cd81600c612005565b93505050506117db565b5050505b9392505050565b606061058885858585634cb8e25360e01b6000611e27565b60606000825190508060405190808252806020026020018201604052801561182c578160200160208202803883390190505b50915060005b818110156113be5760006060611846611fed565b6001600160a01b0316620249f060006001600160a01b0316631f00ca74905060e01b88868151811061187457fe5b60200260200101518a60405160240161188e9291906133a3565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b03199094169390931790925290516118cc91906131cc565b6000604051808303818686fa925050503d8060008114611908576040519150601f19603f3d011682016040523d82523d6000602084013e61190d565b606091505b509092509050600082156116cc578180602001905161192f9190810190612dae565b60008151811061193b57fe5b602002602001015190508086858151811061195257fe5b6020908102919091010152505050600101611832565b806001600160a01b0316826001600160a01b031614156119a35760405162461bcd60e51b815260040161199a90613360565b60405180910390fd5b5050565b73794e6e91555438afc3ccf1c5076a74f42133d08d90565b73c02aaa39b223fe8d0a0e5c4f27ead9083c756cc290565b60008060006119e46119bf565b6001600160a01b0316866001600160a01b031614611a025785611a18565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b90506000611a246119bf565b6001600160a01b0316866001600160a01b031614611a425785611a58565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5b90506000611a658861203d565b60ff1690506000611a758861203d565b60ff16905060006060611a8661204d565b60408051600481526024810182526020810180516001600160e01b0316634f61ff8b60e01b17905290516001600160a01b03929092169162061a8091611acb916131cc565b6000604051808303818686fa925050503d8060008114611b07576040519150601f19603f3d011682016040523d82523d6000602084013e611b0c565b606091505b509150915081611b29575060009650869550611c6f945050505050565b600081806020019051611b3f9190810190612981565b9050806001600160a01b03166216e36060006001600160a01b0316630c235d96905060e01b89898e6000604051602401611b7c94939291906132c5565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b0319909416939093179092529051611bba91906131cc565b6000604051808303818686fa925050503d8060008114611bf6576040519150601f19603f3d011682016040523d82523d6000602084013e611bfb565b606091505b5090935091506000808415611c275783806020019051611c1e919081019061299f565b92509050611c3d565b5060009950899850611c6f975050505050505050565b670de0b6b3a764000087600a0a87600a0a8e85020281611c5957fe5b0481611c6157fe5b049a50985050505050505050505b935093915050565b6000610a5d83611cad611c9182600163ffffffff61206516565b611ca1888763ffffffff61208416565b9063ffffffff6120b516565b9063ffffffff6120d116565b6000611cc36120fb565b6001600160a01b03166306f2bf62836040518263ffffffff1660e01b8152600401611cee91906131d8565b60206040518083038186803b158015611d0657600080fd5b505afa158015611d1a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611d3e9190810190612981565b92915050565b6000806001600160a01b038516611d5a57611c6f565b6060856001600160a01b0316620249f08686604051602401611d7c9190613395565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b0319909416939093179092529051611dba91906131cc565b6000604051808303818686fa925050503d8060008114611df6576040519150601f19603f3d011682016040523d82523d6000602084013e611dfb565b606091505b5090925090508115611e1e5780806020019051611e1b9190810190612e25565b92505b50935093915050565b6060611e338688611968565b8451611e3e576113dd565b60008060008751604051908082528060200260200182016040528015611e6e578160200160208202803883390190505b509350611e92898b8a600081518110611e8357fe5b60200260200101518989612113565b925082611ea257506113dd915050565b611eaf8a8a858989612113565b915081611ebf57506113dd915050565b60005b8851811015611fdf5760005b8860200151811015611fa157611ef88a8381518110611ee957fe5b60200260200101518587611c77565b9450611f0f89600001516127100161271087611c77565b94506000611f208d8d888c8c612113565b905080611f2d5750611fa1565b8094508a8381518110611f3c57fe5b60200260200101518510611f985760008b8481518110611f5857fe5b60200260200101518c8581518110611f6c57fe5b602002602001015187036127100281611f8157fe5b0490508a600001518111611f96575050611fa1565b505b50600101611ece565b50611fc0898281518110611fb157fe5b60200260200101518486611c77565b858281518110611fcc57fe5b6020908102919091010152600101611ec2565b505050509695505050505050565b73f164fc0ec4e93095b804a4795bbe1e041497b92a90565b6000816014018351101561202b5761202b61202660048551856014016122c2565b61231c565b5001601401516001600160a01b031690565b6000611d3e82612324565b919050565b73818e6fecd516ecc3849daf6845e3ec868087b75590565b60008282111561207e5761207e612026600285856123cf565b50900390565b60008261209357506000611d3e565b828202828482816120a057fe5b04146117db576117db612026600186866123cf565b6000828201838110156117db576117db612026600086866123cf565b6000816120e7576120e7612026600385856123cf565b60008284816120f257fe5b04949350505050565b73c0a47dfe034b400b47bdad5fecda2621de6c4d9590565b604080516001808252818301909252600091606091829160208083019080388339019050509050858160008151811061214857fe5b60209081029190910101526001600160e01b03198516634cb8e25360e01b14156121c357604051634cb8e25360e01b9061218a908a908a908590602401613270565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b0319909316929092179091529150612218565b604051639f76ad3560e01b906121e39086908b908b908690602401613201565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b03199093169290921790915291505b60006060306001600160a01b03168460405161223491906131cc565b600060405180830381855afa9150503d806000811461226f576040519150601f19603f3d011682016040523d82523d6000602084013e612274565b606091505b50915091508161228b576000945050505050610588565b8080602001905161229f9190810190612dae565b6000815181106122ab57fe5b602002602001015194505050505095945050505050565b6060632800659560e01b8484846040516024016122e193929190613337565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b03199093169290921790915290509392505050565b805160208201fd5b60006012905060006060836001600160a01b031660405180604001604052806004815260200163313ce56760e01b81525060405161236291906131cc565b600060405180830381855afa9150503d806000811461239d576040519150601f19603f3d011682016040523d82523d6000602084013e6123a2565b606091505b50915091508180156123b5575080516020145b156123c8576123c58160006123ee565b92505b5050919050565b606063e946c1bb60e01b8484846040516024016122e19392919061331c565b60006117db8383600081602001835110156124165761241661202660058551856020016122c2565b50016020015190565b6040805160608101909152806000815260006020820181905260409091015290565b8035611d3e816134ef565b8051611d3e816134ef565b600082601f83011261246857600080fd5b813561247b612476826133e9565b6133c3565b915081818352602084019350602081019050838560208402820111156124a057600080fd5b60005b838110156124cc57816124b68882612441565b84525060209283019291909101906001016124a3565b5050505092915050565b60008083601f8401126124e857600080fd5b5081356001600160401b038111156124ff57600080fd5b60208301915083602082028301111561251757600080fd5b9250929050565b600082601f83011261252f57600080fd5b813561253d612476826133e9565b81815260209384019390925082018360005b838110156124cc578135860161256588826126ce565b845250602092830192919091019060010161254f565b600082601f83011261258c57600080fd5b813561259a612476826133e9565b81815260209384019390925082018360005b838110156124cc57813586016125c288826127d5565b84525060209283019291909101906001016125ac565b600082601f8301126125e957600080fd5b81516125f7612476826133e9565b9150818183526020840193506020810190508385602084028201111561261c57600080fd5b60005b838110156124cc578161263288826126c3565b845250602092830192919091019060010161261f565b600082601f83011261265957600080fd5b8135612667612476826133e9565b9150818183526020840193506020810190508385602084028201111561268c57600080fd5b60005b838110156124cc57816126a28882612976565b845250602092830192919091019060010161268f565b8051611d3e81613503565b8051611d3e8161350c565b600082601f8301126126df57600080fd5b81356126ed61247682613409565b9150808252602083016020830185838301111561270957600080fd5b612714838284613492565b50505092915050565b8051611d3e81613515565b8035611d3e81613522565b60006040828403121561274557600080fd5b61274f60406133c3565b9050600061275d8484612976565b825250602061276e84848301612976565b60208301525092915050565b60006060828403121561278c57600080fd5b61279660606133c3565b905060006127a4848461271d565b82525060206127b5848483016126c3565b60208301525060406127c9848285016126c3565b60408301525092915050565b60006101c082840312156127e857600080fd5b6127f36101c06133c3565b905060006128018484612441565b825250602061281284848301612441565b602083015250604061282684828501612441565b604083015250606061283a84828501612441565b606083015250608061284e84828501612976565b60808301525060a061286284828501612976565b60a08301525060c061287684828501612976565b60c08301525060e061288a84828501612976565b60e08301525061010061289f84828501612976565b610100830152506101206128b584828501612976565b610120830152506101408201356001600160401b038111156128d657600080fd5b6128e2848285016126ce565b610140830152506101608201356001600160401b0381111561290357600080fd5b61290f848285016126ce565b610160830152506101808201356001600160401b0381111561293057600080fd5b61293c848285016126ce565b610180830152506101a08201356001600160401b0381111561295d57600080fd5b612969848285016126ce565b6101a08301525092915050565b8035611d3e8161350c565b60006020828403121561299357600080fd5b6000610a5d848461244c565b600080604083850312156129b257600080fd5b60006129be858561244c565b92505060206129cf858286016126c3565b9150509250929050565b6000806000606084860312156129ee57600080fd5b60006129fa8686612441565b9350506020612a0b86828701612441565b9250506040612a1c86828701612441565b9150509250925092565b600080600080600060a08688031215612a3e57600080fd5b6000612a4a8888612441565b9550506020612a5b88828901612441565b9450506040612a6c88828901612441565b9350506060612a7d88828901612441565b92505060808601356001600160401b03811115612a9957600080fd5b612aa588828901612648565b9150509295509295909350565b60008060008060808587031215612ac857600080fd5b6000612ad48787612441565b9450506020612ae587828801612441565b9350506040612af687828801612441565b92505060608501356001600160401b03811115612b1257600080fd5b612b1e87828801612648565b91505092959194509250565b600080600080600060c08688031215612b4257600080fd5b6000612b4e8888612441565b9550506020612b5f88828901612441565b9450506040612b7088828901612441565b93505060608601356001600160401b03811115612b8c57600080fd5b612b9888828901612648565b9250506080612aa588828901612733565b600080600060608486031215612bbe57600080fd5b6000612bca8686612441565b9350506020612bdb86828701612441565b92505060408401356001600160401b03811115612bf757600080fd5b612a1c86828701612648565b60008060008060a08587031215612c1957600080fd5b6000612c258787612441565b9450506020612c3687828801612441565b93505060408501356001600160401b03811115612c5257600080fd5b612c5e87828801612648565b9250506060612b1e87828801612733565b60008060008060808587031215612c8557600080fd5b6000612c918787612441565b9450506020612ca287828801612728565b9350506040612af687828801612728565b60008060408385031215612cc657600080fd5b82356001600160401b03811115612cdc57600080fd5b612ce885828601612457565b92505060208301356001600160401b03811115612d0457600080fd5b6129cf85828601612648565b60008060208385031215612d2357600080fd5b82356001600160401b03811115612d3957600080fd5b612d45858286016124d6565b92509250509250929050565b60008060408385031215612d6457600080fd5b82356001600160401b03811115612d7a57600080fd5b612d868582860161257b565b92505060208301356001600160401b03811115612da257600080fd5b6129cf8582860161251e565b600060208284031215612dc057600080fd5b81516001600160401b03811115612dd657600080fd5b610a5d848285016125d8565b600080600060a08486031215612df757600080fd5b6000612e03868661277a565b9350506060612e14868287016126c3565b9250506080612a1c868287016126b8565b600060208284031215612e3757600080fd5b6000610a5d84846126c3565b6000612e4f8383612e6f565b505060200190565b60006117db8383612fea565b6000612e4f83836131b6565b612e7881613443565b82525050565b6000612e8982613436565b612e93818561343a565b9350612e9e83613430565b8060005b83811015612ecc578151612eb68882612e43565b9750612ec183613430565b925050600101612ea2565b509495945050505050565b6000612ee282613436565b612eec818561343a565b935083602082028501612efe85613430565b8060005b85811015612f385784840389528151612f1b8582612e57565b9450612f2683613430565b60209a909a0199925050600101612f02565b5091979650505050505050565b6000612f5082613436565b612f5a818561343a565b9350612f6583613430565b8060005b83811015612ecc578151612f7d8882612e63565b9750612f8883613430565b925050600101612f69565b612e788161344e565b6000612fa88385612048565b9350612fb5838584613492565b50500190565b6000612fc682613436565b612fd08185612048565b9350612fe081856020860161349e565b9290920192915050565b6000612ff582613436565b612fff818561343a565b935061300f81856020860161349e565b613018816134ce565b9093019392505050565b612e788161347c565b612e7881613487565b612e788161346a565b600061304a60258361343a565b7f455243323042726964676553616d706c65722f494e56414c49445f544f4b454e8152642fa820a4a960d91b602082015260400192915050565b80516000906101c08401906130998582612e6f565b5060208301516130ac6020860182612e6f565b5060408301516130bf6040860182612e6f565b5060608301516130d26060860182612e6f565b5060808301516130e560808601826131b6565b5060a08301516130f860a08601826131b6565b5060c083015161310b60c08601826131b6565b5060e083015161311e60e08601826131b6565b506101008301516131336101008601826131b6565b506101208301516131486101208601826131b6565b506101408301518482036101408601526131628282612fea565b91505061016083015184820361016086015261317e8282612fea565b91505061018083015184820361018086015261319a8282612fea565b9150506101a08301518482036101a08601526105888282612fea565b612e7881613453565b6000610a5d828486612f9c565b60006117db8284612fbb565b60208101611d3e8284612e6f565b604081016131f48285612e6f565b6117db6020830184612e6f565b6080810161320f8287612e6f565b61321c6020830186612e6f565b6132296040830185612e6f565b81810360608301526113dd8184612f45565b608081016132498287612e6f565b6132566020830186612e6f565b6132636040830185612e6f565b61058860608301846131b6565b6060810161327e8286612e6f565b61328b6020830185612e6f565b81810360408301526105888184612f45565b606081016132ab8286612e6f565b6132b86020830185612e6f565b610a5d60408301846131b6565b608081016132d38287612e6f565b6132e06020830186612e6f565b6132ed60408301856131b6565b6105886060830184612f93565b602080825281016117db8184612ed7565b602080825281016117db8184612f45565b6060810161332a8286613022565b6132b860208301856131b6565b6060810161332a828661302b565b606081016133538286613034565b6132b86020830185613034565b60208082528101611d3e8161303d565b604080825281016133818185613084565b90508181036020830152610a5d8184612fea565b60208101611d3e82846131b6565b604081016133b182856131b6565b8181036020830152610a5d8184612e7e565b6040518181016001600160401b03811182821017156133e157600080fd5b604052919050565b60006001600160401b038211156133ff57600080fd5b5060209081020190565b60006001600160401b0382111561341f57600080fd5b506020601f91909101601f19160190565b60200190565b5190565b90815260200190565b6000611d3e82613470565b151590565b90565b80612048816134d8565b80612048816134e5565b600f0b90565b6001600160a01b031690565b6000611d3e82613456565b6000611d3e82613460565b82818337506000910152565b60005b838110156134b95781810151838201526020016134a1565b838111156134c8576000848401525b50505050565b601f01601f191690565b600481106134e257fe5b50565b600881106134e257fe5b6134f881613443565b81146134e257600080fd5b6134f88161344e565b6134f881613453565b600781106134e257600080fd5b6134f88161346a56fea365627a7a72315820ddb003984abfa8a85ae40b8965430411b3a89ecf35a231473731767cd99143826c6578706572696d656e74616cf564736f6c63430005110040

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

00000000000000000000000074134cf88b21383713e096a5ecf59e297dc7f547

-----Decoded View---------------
Arg [0] : devUtilsAddress (address): 0x74134CF88b21383713E096a5ecF59e297dc7f547

-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 00000000000000000000000074134cf88b21383713e096a5ecf59e297dc7f547


Deployed Bytecode Sourcemap

94077:39443:0:-;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;94077:39443:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;114612:1059;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;118317:1438;;;;;;;;;:::i;113133:1057::-;;;;;;;;;:::i;106273:1086::-;;;;;;;;;:::i;100473:1800::-;;;;;;;;;:::i;105262:638::-;;;;;;;;;:::i;99200:905::-;;;;;;;;;:::i;110404:2303::-;;;;;;;;;:::i;103645:1083::-;;;;;;;;;:::i;95735:528::-;;;;;;;;;:::i;:::-;;;;;;;;107729:2308;;;;;;;;;:::i;96726:2066::-;;;;;;;;;:::i;120324:588::-;;;;;;;;;:::i;116159:1602::-;;;;;;;;;:::i;122309:1061::-;;;;;;;;;:::i;121364:643::-;;;;;;;;;:::i;:::-;;;;;;;;102722:547;;;;;;;;;:::i;123669:1048::-;;;;;;;;;:::i;114612:1059::-;114830:34;114882:18;114903:17;:24;114882:45;;114972:10;114958:25;;;;;;;;;;;;;;;;;;;;;;29:2:-1;21:6;17:15;117:4;105:10;97:6;88:34;136:17;;-1:-1;114958:25:0;-1:-1:-1;114938:45:0;-1:-1:-1;114999:9:0;114994:670;115018:10;115014:1;:14;114994:670;;;115051:15;115068:23;115112:12;-1:-1:-1;;;;;115112:23:0;94919:5;115234:1;-1:-1:-1;;;;;115227:27:0;;:36;;;;115290:12;115329:10;115366:17;115384:1;115366:20;;;;;;;;;;;;;;115178:231;;;;;;;;;;;;;;;-1:-1:-1;;26:21;;;22:32;6:49;;115178:231:0;;;49:4:-1;25:18;;61:17;;-1:-1;;;;;182:15;-1:-1;;;;;;115178:231:0;;;179:29:-1;;;;160:49;;;115112:298:0;;;;115178:231;115112:298;;;;;;;;;;;;;;;;;;14:1:-1;21;16:31;;;;75:4;69:11;64:16;;144:4;140:9;133:4;115:16;111:27;107:43;104:1;100:51;94:4;87:65;169:16;166:1;159:27;225:16;222:1;215:4;212:1;208:12;193:49;7:242;;16:31;36:4;31:9;;7:242;-1:-1;115050:360:0;;-1:-1:-1;115050:360:0;-1:-1:-1;115425:18:0;115462:143;;;;115521:10;115510:33;;;;;;;;;;;;;;115497:46;;115462:143;;;115584:5;;;;;115462:143;115642:10;115619:17;115637:1;115619:20;;;;;;;;;;;;;;;;;:33;-1:-1:-1;;;115030:3:0;;114994:670;;;;114612:1059;;;;;;;:::o;118317:1438::-;118577:34;118682:18;118703:17;:24;118682:45;;118772:10;118758:25;;;;;;;;;;;;;;;;;;;;;;29:2:-1;21:6;17:15;117:4;105:10;97:6;88:34;136:17;;-1:-1;118758:25:0;-1:-1:-1;118738:45:0;-1:-1:-1;;;;;;118854:25:0;;118850:82;;-1:-1:-1;118896:24:0;;118850:82;118949:9;118944:804;118968:10;118964:1;:14;118944:804;;;119001:15;119018:23;119062:11;-1:-1:-1;;;;;119062:22:0;95046:5;119191:1;-1:-1:-1;;;;;119178:28:0;;:37;;;;119242:10;119279:17;119323:10;119360:17;119378:1;119360:20;;;;;;;;;;;;;;119129:274;;;;;;;;;;;;;;;;-1:-1:-1;;26:21;;;22:32;6:49;;119129:274:0;;;49:4:-1;25:18;;61:17;;-1:-1;;;;;182:15;-1:-1;;;;;;119129:274:0;;;179:29:-1;;;;160:49;;;119062:342:0;;;;119129:274;119062:342;;;;;;;;;;;;;;;;;;14:1:-1;21;16:31;;;;75:4;69:11;64:16;;144:4;140:9;133:4;115:16;111:27;107:43;104:1;100:51;94:4;87:65;169:16;166:1;159:27;225:16;222:1;215:4;212:1;208:12;193:49;7:242;;16:31;36:4;31:9;;7:242;-1:-1;119000:404:0;;-1:-1:-1;119000:404:0;-1:-1:-1;119419:17:0;119455:235;;;;119513:10;119502:33;;;;;;;;;;;;;;119490:45;;119455:235;;;119669:5;;;;;119455:235;119727:9;119704:17;119722:1;119704:20;;;;;;;;;;;;;;;;;:32;-1:-1:-1;;;118980:3:0;;118944:804;;;;118317:1438;;;;;;;;;:::o;113133:1057::-;113352:34;113404:18;113425:17;:24;113404:45;;113494:10;113480:25;;;;;;;;;;;;;;;;;;;;;;29:2:-1;21:6;17:15;117:4;105:10;97:6;88:34;136:17;;-1:-1;113480:25:0;-1:-1:-1;113460:45:0;-1:-1:-1;113521:9:0;113516:667;113540:10;113536:1;:14;113516:667;;;113573:15;113590:23;113634:12;-1:-1:-1;;;;;113634:23:0;94919:5;113756:1;-1:-1:-1;;;;;113749:27:0;;:36;;;;113812:12;113851:10;113888:17;113906:1;113888:20;;;;;;;;;;;;;;113700:231;;;;;;;;;;;;;;;-1:-1:-1;;26:21;;;22:32;6:49;;113700:231:0;;;49:4:-1;25:18;;61:17;;-1:-1;;;;;182:15;-1:-1;;;;;;113700:231:0;;;179:29:-1;;;;160:49;;;113634:298:0;;;;113700:231;113634:298;;;;;;;;;;;;;;;;;;14:1:-1;21;16:31;;;;75:4;69:11;64:16;;144:4;140:9;133:4;115:16;111:27;107:43;104:1;100:51;94:4;87:65;169:16;166:1;159:27;225:16;222:1;215:4;212:1;208:12;193:49;7:242;;16:31;36:4;31:9;;7:242;-1:-1;113572:360:0;;-1:-1:-1;113572:360:0;-1:-1:-1;113947:17:0;113983:142;;;;114041:10;114030:33;;;;;;;;;;;;;;114018:45;;114162:9;114139:17;114157:1;114139:20;;;;;;;;;;;;;;;;;:32;-1:-1:-1;;;113552:3:0;;113516:667;;106273:1086;106462:34;106514:40;106531:10;106543;106514:16;:40::i;:::-;106586:24;;106641:25;;;;;;;;;;;;;;;;106586:24;106641:25;;;;;;;29:2:-1;21:6;17:15;117:4;105:10;97:6;88:34;136:17;;-1:-1;106641:25:0;-1:-1:-1;106621:45:0;-1:-1:-1;106682:9:0;106677:675;106701:10;106697:1;:14;106677:675;;;106734:15;106751:23;106795:20;:18;:20::i;:::-;-1:-1:-1;;;;;106795:31:0;94696:6;106929:1;-1:-1:-1;;;;;106920:24:0;;:33;;;;106980:10;107017;107054:17;107072:1;107054:20;;;;;;;;;;;;;;106871:226;;;;;;;;;;;;;;;-1:-1:-1;;26:21;;;22:32;6:49;;106871:226:0;;;49:4:-1;25:18;;61:17;;-1:-1;;;;;182:15;-1:-1;;;;;;106871:226:0;;;179:29:-1;;;;160:49;;;106795:303:0;;;;106871:226;106795:303;;;;;;;;;;;;;;;;;;14:1:-1;21;16:31;;;;75:4;69:11;64:16;;144:4;140:9;133:4;115:16;111:27;107:43;104:1;100:51;94:4;87:65;169:16;166:1;159:27;225:16;222:1;215:4;212:1;208:12;193:49;7:242;;16:31;36:4;31:9;;7:242;-1:-1;106733:365:0;;-1:-1:-1;106733:365:0;-1:-1:-1;107113:18:0;107150:143;;;;107209:10;107198:33;;;;;;;;;;;;;;107185:46;;107150:143;;;107272:5;;;;;107150:143;107330:10;107307:17;107325:1;107307:20;;;;;;;;;;;;;;;;;:33;-1:-1:-1;;;106713:3:0;;106677:675;;;;106273:1086;;;;;;:::o;100473:1800::-;100668:34;100720:40;100737:10;100749;100720:16;:40::i;:::-;100792:24;;100847:25;;;;;;;;;;;;;;;;100792:24;100847:25;;;;;;;29:2:-1;21:6;17:15;117:4;105:10;97:6;88:34;136:17;;-1:-1;100847:25:0;;100827:45;;100883:19;100905:17;:15;:17::i;:::-;100883:39;-1:-1:-1;100933:13:0;;;100983:1283;101007:10;101003:1;:14;100983:1283;;;101057:11;-1:-1:-1;;;;;101043:25:0;:10;-1:-1:-1;;;;;101043:25:0;;:54;;;;101086:11;-1:-1:-1;;;;;101072:25:0;:10;-1:-1:-1;;;;;101072:25:0;;101043:54;101039:1173;;;101180:73;101208:10;101220;101232:17;101250:1;101232:20;;;;;;;;;;;;;;101180:27;:73::i;:::-;101161:92;;-1:-1:-1;101161:92:0;-1:-1:-1;;;;;;101379:31:0;;95163:42;101379:31;;:67;;-1:-1:-1;;;;;;101414:32:0;;95310:42;101414:32;101379:67;101375:125;;;101479:1;101471:9;;101375:125;101039:1173;;;101590:74;101618:10;101630:11;101643:17;101661:1;101643:20;;;;;;;101590:74;101571:93;;-1:-1:-1;101571:93:0;-1:-1:-1;101687:10:0;;101683:514;;101722:20;101789:59;101817:11;101830:10;101842:5;101789:27;:59::i;:::-;101765:83;;-1:-1:-1;101765:83:0;-1:-1:-1;;;;;;102058:31:0;;95163:42;102058:31;:58;;;;;102104:12;-1:-1:-1;;;;;102093:23:0;:7;-1:-1:-1;;;;;102093:23:0;;102058:58;102054:124;;;102153:1;102145:9;;102054:124;101683:514;;102249:5;102226:17;102244:1;102226:20;;;;;;;;;;;;;;;;;:28;101019:3;;100983:1283;;;;100473:1800;;;;;;;;;:::o;105262:638::-;105491:34;105561:17;-1:-1:-1;;;;;105547:31:0;:10;-1:-1:-1;;;;;105547:31:0;;:66;;;;105596:17;-1:-1:-1;;;;;105582:31:0;:10;-1:-1:-1;;;;;105582:31:0;;105547:66;105543:123;;;105630:24;;105543:123;105676:36;105715:72;105738:10;105750:17;105769;105715:22;:72::i;:::-;105676:111;;105818:74;105841:17;105860:10;105872:19;105818:22;:74::i;:::-;105798:94;;105262:638;;;;;;;;:::o;99200:905::-;99380:47;99478:95;99526:6;99547:15;99478:33;:95::i;:::-;99445:128;-1:-1:-1;99717:9:0;99712:386;99736:6;:13;99732:1;:17;99712:386;;;99775:30;99806:1;99775:33;;;;;;;;;;;;;;99812:1;99775:38;99771:316;;99870:201;99921:30;99952:1;99921:33;;;;;;;;;;;;;;99977:6;99984:1;99977:9;;;;;;;;;;;;;;:26;;;100026:6;100033:1;100026:9;;;;;;;;;;;;;;:26;;;99870:28;:201::i;:::-;99834:30;99865:1;99834:33;;;;;;;;;;;;;:237;;;;;99771:316;99751:3;;99712:386;;;;99200:905;;;;:::o;110404:2303::-;110593:34;110645:40;110662:10;110674;110645:16;:40::i;:::-;110717:24;;110772:25;;;;;;;;;;;;;;;;110717:24;110772:25;;;;;;;29:2:-1;21:6;17:15;117:4;105:10;97:6;88:34;136:17;;-1:-1;110772:25:0;;110752:45;;110808:41;110866:17;:15;:17::i;:::-;-1:-1:-1;;;;;110852:31:0;:10;-1:-1:-1;;;;;110852:31:0;;:106;;110927:31;110947:10;110927:19;:31::i;:::-;110852:106;;;110922:1;110852:106;110808:150;;110969:41;111027:17;:15;:17::i;:::-;-1:-1:-1;;;;;111013:31:0;:10;-1:-1:-1;;;;;111013:31:0;;:106;;111088:31;111108:10;111088:19;:31::i;:::-;111013:106;;;111083:1;111013:106;110969:150;-1:-1:-1;111135:9:0;111130:1570;111154:10;111150:1;:14;111130:1570;;;111204:4;111241:17;:15;:17::i;:::-;-1:-1:-1;;;;;111227:31:0;:10;-1:-1:-1;;;;;111227:31:0;;111223:1395;;;111497:20;;111316:220;;111380:18;;-1:-1:-1;;;111422:52:0;111497:17;;111515:1;;111497:20;;;;;;;;;;;;111316:33;:220::i;:::-;111280:17;111298:1;111280:20;;;;;;;;;;;;;111279:257;;;;;;;;;111223:1395;;;111576:17;:15;:17::i;:::-;-1:-1:-1;;;;;111562:31:0;:10;-1:-1:-1;;;;;111562:31:0;;111558:1060;;;111832:20;;111651:220;;111715:18;;-1:-1:-1;;;111757:52:0;111832:17;;111850:1;;111832:20;;;;;111558:1060;112151:20;;111912:15;;111970:220;;112034:18;;-1:-1:-1;;;112076:52:0;112151:17;;112169:1;;112151:20;;;;;111970:220;111946:244;-1:-1:-1;111946:244:0;-1:-1:-1;112213:12:0;;112209:394;;112287:223;112355:18;-1:-1:-1;;;112480:7:0;112287:33;:223::i;:::-;112251:17;112269:1;112251:20;;;;;;;;;;;;;112250:260;;;;;;;;;112209:394;;;112582:1;112559:17;112577:1;112559:20;;;;;;;;;;;;;:24;;;;;112209:394;111558:1060;;112637:10;112632:57;;112668:5;;;112632:57;-1:-1:-1;111166:3:0;;111130:1570;;;;110404:2303;;;;;;;;:::o;103645:1083::-;103835:34;103887:40;103904:10;103916;103887:16;:40::i;:::-;103959:24;;104014:25;;;;;;;;;;;;;;;;103959:24;104014:25;;;;;;;29:2:-1;21:6;17:15;117:4;105:10;97:6;88:34;136:17;;-1:-1;104014:25:0;-1:-1:-1;103994:45:0;-1:-1:-1;104055:9:0;104050:671;104074:10;104070:1;:14;104050:671;;;104107:15;104124:23;104168:20;:18;:20::i;:::-;-1:-1:-1;;;;;104168:31:0;94696:6;104302:1;-1:-1:-1;;;;;104293:24:0;;:33;;;;104353:10;104390;104427:17;104445:1;104427:20;;;;;;;;;;;;;;104244:226;;;;;;;;;;;;;;;-1:-1:-1;;26:21;;;22:32;6:49;;104244:226:0;;;49:4:-1;25:18;;61:17;;-1:-1;;;;;182:15;-1:-1;;;;;;104244:226:0;;;179:29:-1;;;;160:49;;;104168:303:0;;;;104244:226;104168:303;;;;;;;;;;;;;;;;;;14:1:-1;21;16:31;;;;75:4;69:11;64:16;;144:4;140:9;133:4;115:16;111:27;107:43;104:1;100:51;94:4;87:65;169:16;166:1;159:27;225:16;222:1;215:4;212:1;208:12;193:49;7:242;;16:31;36:4;31:9;;7:242;-1:-1;104106:365:0;;-1:-1:-1;104106:365:0;-1:-1:-1;104486:17:0;104522:141;;;;104580:10;104569:33;;;;;;;;;;;;;;104557:45;;104700:9;104677:17;104695:1;104677:20;;;;;;;;;;;;;;;;;:32;-1:-1:-1;;;104086:3:0;;104050:671;;95735:528;95890:29;;;;;;;;;;;;;;;;95832:26;;95902:9;95890:29;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;95876:43:0;-1:-1:-1;95935:9:0;95930:326;95950:21;;;95930:326;;95994:15;96011:23;96046:4;96063:9;;96073:1;96063:12;;;;;;;;;;;;-1:-1:-1;30:25;;-1:-1;;100:14;96:29;;;92:48;68:73;;58:2;;155:1;152;145:12;58:2;174:33;;;69:4;55:19;;;-1:-1;16:22;-1:-1;;;;;82:30;;79:2;;;125:1;122;115:12;79:2;155:14;151:38;;;137:53;;134:2;;;203:1;200;193:12;134:2;96038:38:0;;;;;;;;;;;;;;;;;;;;;;;14:1:-1;21;16:31;;;;75:4;69:11;64:16;;144:4;140:9;133:4;115:16;111:27;107:43;104:1;100:51;94:4;87:65;169:16;166:1;159:27;225:16;222:1;215:4;212:1;208:12;193:49;7:242;;16:31;36:4;31:9;;7:242;;95993:83:0;;;;96096:10;96091:112;;96174:10;96168:17;96161:4;96149:10;96145:21;96138:48;96136:52;96234:10;96217:11;96229:1;96217:14;;;;;;;;;;;;;;;;;:27;-1:-1:-1;;95973:3:0;;95930:326;;107729:2308;107919:34;107971:40;107988:10;108000;107971:16;:40::i;:::-;108043:24;;108098:25;;;;;;;;;;;;;;;;108043:24;108098:25;;;;;;;29:2:-1;21:6;17:15;117:4;105:10;97:6;88:34;136:17;;-1:-1;108098:25:0;;108078:45;;108134:41;108192:17;:15;:17::i;:::-;-1:-1:-1;;;;;108178:31:0;:10;-1:-1:-1;;;;;108178:31:0;;:106;;108253:31;108273:10;108253:19;:31::i;:::-;108178:106;;;108248:1;108178:106;108134:150;;108295:41;108353:17;:15;:17::i;:::-;-1:-1:-1;;;;;108339:31:0;:10;-1:-1:-1;;;;;108339:31:0;;:106;;108414:31;108434:10;108414:19;:31::i;:::-;108339:106;;;108409:1;108339:106;108295:150;-1:-1:-1;108461:9:0;108456:1574;108480:10;108476:1;:14;108456:1574;;;108530:4;108567:17;:15;:17::i;:::-;-1:-1:-1;;;;;108553:31:0;:10;-1:-1:-1;;;;;108553:31:0;;108549:1399;;;108822:20;;108642:219;;108706:18;;-1:-1:-1;;;108748:51:0;108822:17;;108840:1;;108822:20;;;;;108642:219;108606:17;108624:1;108606:20;;;;;;;;;;;;;108605:256;;;;;;;;;108549:1399;;;108901:17;:15;:17::i;:::-;-1:-1:-1;;;;;108887:31:0;:10;-1:-1:-1;;;;;108887:31:0;;108883:1065;;;109156:20;;108976:219;;109040:18;;-1:-1:-1;;;109082:51:0;109156:17;;109174:1;;109156:20;;;;;108883:1065;109478:20;;109236:17;;109298:219;;109362:18;;-1:-1:-1;;;109404:51:0;109478:17;;109496:1;;109478:20;;;;;109298:219;109272:245;-1:-1:-1;109272:245:0;-1:-1:-1;109540:14:0;;109536:397;;109616:224;109684:18;-1:-1:-1;;;109808:9:0;109616:33;:224::i;:::-;109580:17;109598:1;109580:20;;;;;;;;;;;;;109579:261;;;;;;;;;109536:397;;;109912:1;109889:17;109907:1;109889:20;;;;;;;;;;;;;:24;;;;;109536:397;108883:1065;;109967:10;109962:57;;109998:5;;;109962:57;-1:-1:-1;108492:3:0;;108456:1574;;96726:2066;96906:47;97018:6;:13;97004:28;;;;;;;;;;;;;;;;;;;;;;29:2:-1;21:6;17:15;117:4;105:10;97:6;88:34;136:17;;-1:-1;97004:28:0;-1:-1:-1;97043:23:0;97069:16;;96971:61;;-1:-1:-1;;;;;;97069:16:0;;;;97096:1689;97121:6;:13;97116:1;:18;97096:1689;;97238:15;97254:1;97238:18;;;;;;;;;;;;;;:25;97267:1;97238:30;:82;;;;97289:6;97296:1;97289:9;;;;;;;;;;;;;;:26;;;97319:1;97289:31;97238:82;:134;;;;97341:6;97348:1;97341:9;;;;;;;;;;;;;;:26;;;97371:1;97341:31;97238:134;97234:239;;;97429:1;97393:30;97424:1;97393:33;;;;;;;;;;;;;:37;;;;;97449:8;;97234:239;97527:15;97544:23;97588:15;-1:-1:-1;;;;;97588:48:0;94258:5;97763:15;-1:-1:-1;;;;;97753:48:0;;:57;;;;97836:6;97843:1;97836:9;;;;;;;;;;;;;;97871:15;97887:1;97871:18;;;;;;;;;;;;;;97705:207;;;;;;;;;;;;;;-1:-1:-1;;26:21;;;22:32;6:49;;97705:207:0;;;49:4:-1;25:18;;61:17;;-1:-1;;;;;182:15;-1:-1;;;;;;97705:207:0;;;179:29:-1;;;;160:49;;;97588:325:0;;;;97705:207;97588:325;;;;;;;;;;;;;;;;;;14:1:-1;21;16:31;;;;75:4;69:11;64:16;;144:4;140:9;133:4;115:16;111:27;107:43;104:1;100:51;94:4;87:65;169:16;166:1;159:27;225:16;222:1;215:4;212:1;208:12;193:49;7:242;;16:31;36:4;31:9;;7:242;;97526:387:0;;;;97971:10;97966:116;;98038:1;98002:30;98033:1;98002:33;;;;;;;;;;;;;:37;;;;;98058:8;;;;97966:116;98115:35;;:::i;:::-;98169:32;98220:21;98288:10;98259:108;;;;;;;;;;;;;;98096:271;;-1:-1:-1;98096:271:0;-1:-1:-1;98096:271:0;-1:-1:-1;98532:29:0;98507:21;;:54;;;;;;;;;;:92;;;;98583:16;98582:17;98507:92;98503:271;;;98656:1;98620:30;98651:1;98620:33;;;;;;;;;;;;;:37;;;;;98503:271;;;98734:24;98698:30;98729:1;98698:33;;;;;;;;;;;;;:60;;;;;98503:271;97096:1689;;;;;;97136:3;;97096:1689;;;;96726:2066;;;;;:::o;120324:588::-;120602:34;120661:243;120708:10;120733;120758:17;120790:4;-1:-1:-1;;;120878:15:0;120661:32;:243::i;:::-;120654:250;120324:588;-1:-1:-1;;;;;;120324:588:0:o;116159:1602::-;116401:34;116506:18;116527:17;:24;116506:45;;116596:10;116582:25;;;;;;;;;;;;;;;;;;;;;;29:2:-1;21:6;17:15;117:4;105:10;97:6;88:34;136:17;;-1:-1;116582:25:0;;116562:45;;116669:23;116695:123;116742:15;116772:10;116797;116695:32;:123::i;:::-;116669:149;-1:-1:-1;;;;;;116890:29:0;;116886:86;;-1:-1:-1;116936:24:0;;-1:-1:-1;116936:24:0;116886:86;116989:9;116984:770;117008:10;117004:1;:14;116984:770;;;117041:15;117058:23;117102:15;-1:-1:-1;;;;;117102:26:0;95046:5;117241:1;-1:-1:-1;;;;;117222:34:0;;:43;;;;117292:10;117329;117366:17;117384:1;117366:20;;;;;;;;;;;;;;117173:236;;;;;;;;;;;;;;;-1:-1:-1;;26:21;;;22:32;6:49;;117173:236:0;;;49:4:-1;25:18;;61:17;;-1:-1;;;;;182:15;-1:-1;;;;;;117173:236:0;;;179:29:-1;;;;160:49;;;117102:308:0;;;;117173:236;117102:308;;;;;;;;;;;;;;;;;;14:1:-1;21;16:31;;;;75:4;69:11;64:16;;144:4;140:9;133:4;115:16;111:27;107:43;104:1;100:51;94:4;87:65;169:16;166:1;159:27;225:16;222:1;215:4;212:1;208:12;193:49;7:242;;16:31;36:4;31:9;;7:242;-1:-1;117040:370:0;;-1:-1:-1;117040:370:0;-1:-1:-1;117425:17:0;117461:235;;;;117519:10;117508:33;;;;;;;;;;;;;;117496:45;;117461:235;;;117675:5;;;;;117461:235;117733:9;117710:17;117728:1;117710:20;;;;;;;;;;;;;;;;;:32;-1:-1:-1;;;117020:3:0;;116984:770;;;;116159:1602;;;;;;;;:::o;122309:1061::-;122475:34;122527:18;122548:17;:24;122527:45;;122617:10;122603:25;;;;;;;;;;;;;;;;;;;;;;29:2:-1;21:6;17:15;117:4;105:10;97:6;88:34;136:17;;-1:-1;122603:25:0;-1:-1:-1;122583:45:0;-1:-1:-1;122644:9:0;122639:724;122663:10;122659:1;:14;122639:724;;;122696:15;122713:23;122757:30;:28;:30::i;:::-;-1:-1:-1;;;;;122757:41:0;94583:5;122913:1;-1:-1:-1;;;;;122894:35:0;;:44;;;;122965:17;122983:1;122965:20;;;;;;;;;;;;;;123012:4;122845:194;;;;;;;;;;;;;;-1:-1:-1;;26:21;;;22:32;6:49;;122845:194:0;;;49:4:-1;25:18;;61:17;;-1:-1;;;;;182:15;-1:-1;;;;;;122845:194:0;;;179:29:-1;;;;160:49;;;122757:283:0;;;;122845:194;122757:283;;;;;;;;;;;;;;;;;;14:1:-1;21;16:31;;;;75:4;69:11;64:16;;144:4;140:9;133:4;115:16;111:27;107:43;104:1;100:51;94:4;87:65;169:16;166:1;159:27;225:16;222:1;215:4;212:1;208:12;193:49;7:242;;16:31;36:4;31:9;;7:242;-1:-1;122695:345:0;;-1:-1:-1;122695:345:0;-1:-1:-1;123055:17:0;123091:214;;;;123202:10;123191:35;;;;;;;;;;;;;;123241:1;123227:4;:11;:15;123191:52;;;;;;;;;;;;;;123179:64;;123091:214;;;123284:5;;;;;123091:214;123342:9;123319:17;123337:1;123319:20;;;;;;;;;;;;;;;;;:32;-1:-1:-1;;;122675:3:0;;122639:724;;121364:643;121618:166;;121553:23;;121594:21;;-1:-1:-1;;;121655:68:0;121618:166;;121738:10;;121763;;121618:166;;;;;;;49:4:-1;39:7;30;26:21;22:32;13:7;6:49;121618:166:0;;;;-1:-1:-1;;;;;121618:166:0;;38:4:-1;29:7;25:18;67:10;61:17;-1:-1;;;;;199:8;192:4;186;182:15;179:29;167:10;160:49;0:215;;;121618:166:0;121594:190;;121796:15;121813:23;121840:15;-1:-1:-1;;;;;121840:26:0;121867:8;121840:36;;;;;;;;;;;;;;;;;;;;;;14:1:-1;21;16:31;;;;75:4;69:11;64:16;;144:4;140:9;133:4;115:16;111:27;107:43;104:1;100:51;94:4;87:65;169:16;166:1;159:27;225:16;222:1;215:4;212:1;208:12;193:49;7:242;;16:31;36:4;31:9;;7:242;;121795:81:0;;;;121891:10;:37;;;;;121905:10;:17;121926:2;121905:23;121891:37;121887:113;;;121952:36;121973:10;121985:2;121952:20;:36::i;:::-;121945:43;;;;;;;121887:113;121364:643;;;;;;;;;:::o;102722:547::-;102953:34;103012:249;103059:10;103084;103109:17;103141:4;-1:-1:-1;;;103224:1:0;103012:32;:249::i;123669:1048::-;123834:34;123886:18;123907:17;:24;123886:45;;123976:10;123962:25;;;;;;;;;;;;;;;;;;;;;;29:2:-1;21:6;17:15;117:4;105:10;97:6;88:34;136:17;;-1:-1;123962:25:0;-1:-1:-1;123942:45:0;-1:-1:-1;124003:9:0;123998:712;124022:10;124018:1;:14;123998:712;;;124055:15;124072:23;124116:30;:28;:30::i;:::-;-1:-1:-1;;;;;124116:41:0;94583:5;124272:1;-1:-1:-1;;;;;124253:34:0;;:43;;;;124323:17;124341:1;124323:20;;;;;;;;;;;;;;124370:4;124204:193;;;;;;;;;;;;;;-1:-1:-1;;26:21;;;22:32;6:49;;124204:193:0;;;49:4:-1;25:18;;61:17;;-1:-1;;;;;182:15;-1:-1;;;;;;124204:193:0;;;179:29:-1;;;;160:49;;;124116:282:0;;;;124204:193;124116:282;;;;;;;;;;;;;;;;;;14:1:-1;21;16:31;;;;75:4;69:11;64:16;;144:4;140:9;133:4;115:16;111:27;107:43;104:1;100:51;94:4;87:65;169:16;166:1;159:27;225:16;222:1;215:4;212:1;208:12;193:49;7:242;;16:31;36:4;31:9;;7:242;-1:-1;124054:344:0;;-1:-1:-1;124054:344:0;-1:-1:-1;124413:18:0;124450:201;;;;124562:10;124551:35;;;;;;;;;;;;;;124587:1;124551:38;;;;;;;;;;;;;;124538:51;;124688:10;124665:17;124683:1;124665:20;;;;;;;;;;;;;;;;;:33;-1:-1:-1;;;124034:3:0;;123998:712;;127035:195;127170:10;-1:-1:-1;;;;;127156:24:0;:10;-1:-1:-1;;;;;127156:24:0;;;127148:74;;;;-1:-1:-1;;;127148:74:0;;;;;;;;;;;;;;;;;127035:195;;:::o;64937:150::-;61429:42;64937:150;:::o;63970:141::-;60064:42;63970:141;:::o;131717:1800::-;131903:24;131929:15;131962:19;131998:17;:15;:17::i;:::-;-1:-1:-1;;;;;131984:31:0;:10;-1:-1:-1;;;;;131984:31:0;;:64;;132038:10;131984:64;;;62844:42;131984:64;131962:86;;132059:19;132095:17;:15;:17::i;:::-;-1:-1:-1;;;;;132081:31:0;:10;-1:-1:-1;;;;;132081:31:0;;:64;;132135:10;132081:64;;;62844:42;132081:64;132059:86;;132156:26;132185:29;132203:10;132185:17;:29::i;:::-;132156:58;;;;132225:26;132254:29;132272:10;132254:17;:29::i;:::-;132225:58;;;;132295:15;132312:23;132339:30;:28;:30::i;:::-;132417:107;;;22:32:-1;6:49;;132417:107:0;;;;;49:4:-1;25:18;;61:17;;-1:-1;;;;;182:15;-1:-1;;;179:29;160:49;;132339:186:0;;-1:-1:-1;;;;;132339:41:0;;;;;95046:5;;132339:186;;;;;;;;;;;;;;;;;;;;14:1:-1;21;16:31;;;;75:4;69:11;64:16;;144:4;140:9;133:4;115:16;111:27;107:43;104:1;100:51;94:4;87:65;169:16;166:1;159:27;225:16;222:1;215:4;212:1;208:12;193:49;7:242;;16:31;36:4;31:9;;7:242;;132294:231:0;;;;132541:10;132536:66;;-1:-1:-1;132576:1:0;;-1:-1:-1;132576:1:0;;-1:-1:-1;132568:22:0;;-1:-1:-1;;;;;132568:22:0;132536:66;132612:28;132654:10;132643:33;;;;;;;;;;;;;;132612:64;;132727:20;-1:-1:-1;;;;;132727:31:0;94362:6;132856:1;-1:-1:-1;;;;;132842:31:0;;:40;;;;132905:11;132939;132973:16;133012:5;132797:260;;;;;;;;;;;;;;;;-1:-1:-1;;26:21;;;22:32;6:49;;132797:260:0;;;49:4:-1;25:18;;61:17;;-1:-1;;;;;182:15;-1:-1;;;;;;132797:260:0;;;179:29:-1;;;;160:49;;;132727:331:0;;;;132797:260;132727:331;;;;;;;;;;;;;;;;;;14:1:-1;21;16:31;;;;75:4;69:11;64:16;;144:4;140:9;133:4;115:16;111:27;107:43;104:1;100:51;94:4;87:65;169:16;166:1;159:27;225:16;222:1;215:4;212:1;208:12;193:49;7:242;;16:31;36:4;31:9;;7:242;-1:-1;132687:371:0;;-1:-1:-1;132687:371:0;-1:-1:-1;133069:12:0;;133122:158;;;;133182:10;133171:42;;;;;;;;;;;;;;133153:60;-1:-1:-1;133153:60:0;-1:-1:-1;133122:158:0;;;-1:-1:-1;133254:1:0;;-1:-1:-1;133254:1:0;;-1:-1:-1;133246:22:0;;-1:-1:-1;;;;;;;;133246:22:0;133122:158;133454:8;133420:18;133414:2;:24;133380:18;133374:2;:24;133342:16;133322:4;:36;:76;:116;;;;;;:140;;;;;;;-1:-1:-1;133501:7:0;-1:-1:-1;;;;;;;;;131717:1800:0;;;;;;;:::o;55347:562::-;55517:21;55762:106;55856:11;55762:71;55810:22;55856:11;55830:1;55810:22;:19;:22;:::i;:::-;55762:25;:9;55780:6;55762:25;:17;:25;:::i;:::-;:47;:71;:47;:71;:::i;:::-;:93;:106;:93;:106;:::i;126528:325::-;126628:31;126757:35;:33;:35::i;:::-;-1:-1:-1;;;;;126733:86:0;;126820:12;126733:100;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;126733:100:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;126733:100:0;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;126733:100:0;;;;;;;;;126677:168;126528:325;-1:-1:-1;;126528:325:0:o;125537:743::-;125741:20;;-1:-1:-1;;;;;125800:36:0;;125796:102;;125853:33;;125796:102;125908:23;125982:22;-1:-1:-1;;;;;125982:33:0;94471:5;126101:16;126140:11;126056:114;;;;;;;;;;;;;-1:-1:-1;;26:21;;;22:32;6:49;;126056:114:0;;;49:4:-1;25:18;;61:17;;-1:-1;;;;;182:15;-1:-1;;;;;;126056:114:0;;;179:29:-1;;;;160:49;;;125982:189:0;;;;126056:114;125982:189;;;;;;;;;;;;;;;;;;14:1:-1;21;16:31;;;;75:4;69:11;64:16;;144:4;140:9;133:4;115:16;111:27;107:43;104:1;100:51;94:4;87:65;169:16;166:1;159:27;225:16;222:1;215:4;212:1;208:12;193:49;7:242;;16:31;36:4;31:9;;7:242;-1:-1;125942:229:0;;-1:-1:-1;125942:229:0;-1:-1:-1;126182:91:0;;;;126239:10;126228:33;;;;;;;;;;;;;;126213:48;;126182:91;125537:743;;;;;;;:::o;128505:3204::-;128806:34;128858:40;128875:10;128887;128858:16;:40::i;:::-;128913:24;;128909:86;;128959:24;;128909:86;129005:18;129034:17;129062:26;129133:17;:24;129119:39;;;;;;;;;;;;;;;;;;;;;;29:2:-1;21:6;17:15;117:4;105:10;97:6;88:34;136:17;;-1:-1;129119:39:0;;129099:59;;129182:180;129225:10;129250;129275:17;129293:1;129275:20;;;;;;;;;;;;;;129310:8;129333:18;129182:28;:180::i;:::-;129169:193;-1:-1:-1;129379:15:0;129375:72;;-1:-1:-1;129411:24:0;;-1:-1:-1;;129411:24:0;129375:72;129471:170;129514:10;129539;129564;129589:8;129612:18;129471:28;:170::i;:::-;129459:182;-1:-1:-1;129656:14:0;129652:71;;-1:-1:-1;129687:24:0;;-1:-1:-1;;129687:24:0;129652:71;129740:9;129735:1967;129759:17;:24;129755:1;:28;129735:1967;;;129810:12;129805:1473;129835:4;:18;;;129828:4;:25;129805:1473;;;129991:155;130042:17;130060:1;130042:20;;;;;;;;;;;;;;130085:9;130117:10;129991:28;:155::i;:::-;129978:168;;130178:163;130238:4;:22;;;130230:5;:30;130284:5;130312:10;130178:28;:163::i;:::-;130165:176;;130360:18;130381:218;130432:10;130465;130498;130531:8;130562:18;130381:28;:218::i;:::-;130360:239;-1:-1:-1;130622:15:0;130618:69;;130662:5;;;130618:69;130823:10;130811:22;;130927:17;130945:1;130927:20;;;;;;;;;;;;;;130914:9;:33;130910:353;;130972:26;131095:17;131113:1;131095:20;;;;;;;;;;;;;;131014:17;131032:1;131014:20;;;;;;;;;;;;;;131002:9;:32;131038:5;131001:42;:114;;;;;;130972:143;;131164:4;:22;;;131142:18;:44;131138:106;;131215:5;;;;131138:106;130910:353;;-1:-1:-1;129855:6:0;;129805:1473;;;;131551:139;131598:17;131616:1;131598:20;;;;;;;;;;;;;;131637:9;131665:10;131551:28;:139::i;:::-;131528:17;131546:1;131528:20;;;;;;;;;;;;;;;;;:162;129785:3;;129735:1967;;;;128505:3204;;;;;;;;;;;:::o;64595:179::-;61089:42;64595:179;:::o;18925:1100::-;19052:14;19099:5;19107:2;19099:10;19088:1;:8;:21;19084:327;;;19126:273;19148:250;19211:89;19319:1;:8;19346:5;19354:2;19346:10;19148:44;:250::i;:::-;19126:21;:273::i;:::-;-1:-1:-1;19924:13:0;19662:2;19924:13;19918:20;-1:-1:-1;;;;;19914:69:0;;18925:1100::o;124889:182::-;124988:14;125027:36;125050:12;125027:22;:36::i;124889:182::-;;;;:::o;63690:170::-;60378:42;63690:170;:::o;50295:377::-;50384:7;50417:1;50413;:5;50409:233;;;50435:195;50457:172;50515:59;50593:1;50613;50457:39;:172::i;50435:195::-;-1:-1:-1;50659:5:0;;;50295:377::o;49417:465::-;49506:7;49535:6;49531:47;;-1:-1:-1;49565:1:0;49558:8;;49531:47;49600:5;;;49604:1;49600;:5;:1;49620:5;;;;;:10;49616:240;;49647:197;49669:174;49727:61;49807:1;49827;49669:39;:174::i;50680:397::-;50769:7;50806:5;;;50826;;;50822:229;;;50848:191;50870:168;50928:55;51002:1;51022;50870:39;:168::i;49890:397::-;49979:7;50008:6;50004:229;;50031:190;50053:167;50111:54;50184:1;50204;50053:39;:167::i;50031:190::-;50243:9;50259:1;50255;:5;;;;;;;49890:397;-1:-1:-1;;;;49890:397:0:o;64264:182::-;60732:42;64264:182;:::o;127238:1259::-;127597:16;;;127611:1;127597:16;;;;;;;;;127488:24;;127530:21;;;;127597:16;;;;;;105:10:-1;127597:16:0;88:34:-1;136:17;;-1:-1;127597:16:0;127562:51;;127645:16;127624:15;127640:1;127624:18;;;;;;;;;;;;;;;;;:37;-1:-1:-1;;;;;;127676:53:0;;-1:-1:-1;;;127676:53:0;127672:569;;;127757:189;;-1:-1:-1;;;127798:41:0;127757:189;;127858:10;;127887;;127916:15;;127757:189;;;;;;;;-1:-1:-1;;26:21;;;22:32;6:49;;127757:189:0;;;49:4:-1;25:18;;61:17;;-1:-1;;;;;182:15;-1:-1;;;;;;127757:189:0;;;179:29:-1;;;;160:49;;;127757:189:0;-1:-1:-1;127672:569:0;;;127990:239;;-1:-1:-1;;;128031:54:0;127990:239;;128104:18;;128141:10;;128170;;128199:15;;127990:239;;;;;;;;-1:-1:-1;;26:21;;;22:32;6:49;;127990:239:0;;;49:4:-1;25:18;;61:17;;-1:-1;;;;;182:15;-1:-1;;;;;;127990:239:0;;;179:29:-1;;;;160:49;;;127990:239:0;-1:-1:-1;127672:569:0;128252:12;128266:23;128301:4;-1:-1:-1;;;;;128293:24:0;128318:8;128293:34;;;;;;;;;;;;;;;;;;;;;;14:1:-1;21;16:31;;;;75:4;69:11;64:16;;144:4;140:9;133:4;115:16;111:27;107:43;104:1;100:51;94:4;87:65;169:16;166:1;159:27;225:16;222:1;215:4;212:1;208:12;193:49;7:242;;16:31;36:4;31:9;;7:242;;128251:76:0;;;;128343:7;128338:49;;128374:1;128367:8;;;;;;;;128338:49;128462:10;128451:35;;;;;;;;;;;;;;128487:1;128451:38;;;;;;;;;;;;;;128432:57;;127238:1259;;;;;;;;;;;:::o;8173:393::-;8368:12;8110:10;8442:37;;8494:9;8518:6;8539:8;8405:153;;;;;;;;;;;;;;;-1:-1:-1;;26:21;;;22:32;6:49;;8405:153:0;;;49:4:-1;25:18;;61:17;;-1:-1;;;;;182:15;-1:-1;;;;;;8405:153:0;;;179:29:-1;;;;160:49;;;8405:153:0;-1:-1:-1;8173:393:0;;;;;:::o;6089:177::-;6237:9;6231:16;6224:4;6213:9;6209:20;6202:46;32626:376;32709:19;32762:2;32746:18;;32776:15;32793:23;32820:5;-1:-1:-1;;;;;32820:16:0;32837:18;;;;;;;;;;;;;-1:-1:-1;;;32837:18:0;;;32820:36;;;;;;;;;;;;;;;;;;;;;;14:1:-1;21;16:31;;;;75:4;69:11;64:16;;144:4;140:9;133:4;115:16;111:27;107:43;104:1;100:51;94:4;87:65;169:16;166:1;159:27;225:16;222:1;215:4;212:1;208:12;193:49;7:242;;16:31;36:4;31:9;;7:242;;32775:81:0;;;;32871:10;:37;;;;;32885:10;:17;32906:2;32885:23;32871:37;32867:128;;;32947:35;32968:10;32980:1;32947:20;:35::i;:::-;32925:58;;32867:128;32626:376;;;;;:::o;48726:337::-;48886:12;48171:10;48960:28;;49003:9;49027:1;49043;48923:132;;;;;;;;;;;24244:230;24371:14;24420:21;24432:1;24435:5;22516:14;22563:5;22571:2;22563:10;22552:1;:8;:21;22548:303;;;22590:249;22612:226;22675:92;22786:1;:8;22813:5;22821:2;22813:10;22612:44;:226::i;22590:249::-;-1:-1:-1;23036:13:0;22934:2;23036:13;23030:20;;22389:703::o;94077:39443::-;;;;;;;;;;;-1:-1:-1;94077:39443:0;;;;;;;;;;;;;;;:::o;5:130:-1:-;72:20;;97:33;72:20;97:33;;142:134;220:13;;238:33;220:13;238:33;;458:707;;575:3;568:4;560:6;556:17;552:27;542:2;;593:1;590;583:12;542:2;630:6;617:20;652:80;667:64;724:6;667:64;;;652:80;;;643:89;;749:5;774:6;767:5;760:21;804:4;796:6;792:17;782:27;;826:4;821:3;817:14;810:21;;879:6;926:3;918:4;910:6;906:17;901:3;897:27;894:36;891:2;;;943:1;940;933:12;891:2;968:1;953:206;978:6;975:1;972:13;953:206;;;1036:3;1058:37;1091:3;1079:10;1058:37;;;1046:50;;-1:-1;1119:4;1110:14;;;;1138;;;;;1000:1;993:9;953:206;;;957:14;535:630;;;;;;;;1189:359;;;1326:3;1319:4;1311:6;1307:17;1303:27;1293:2;;1344:1;1341;1334:12;1293:2;-1:-1;1364:20;;-1:-1;;;;;1393:30;;1390:2;;;1436:1;1433;1426:12;1390:2;1470:4;1462:6;1458:17;1446:29;;1521:3;1513:4;1505:6;1501:17;1491:8;1487:32;1484:41;1481:2;;;1538:1;1535;1528:12;1481:2;1286:262;;;;;;1572:693;;1694:3;1687:4;1679:6;1675:17;1671:27;1661:2;;1712:1;1709;1702:12;1661:2;1749:6;1736:20;1771:85;1786:69;1848:6;1786:69;;1771:85;1884:21;;;1928:4;1916:17;;;;1762:94;;-1:-1;1941:14;;1916:17;2036:1;2021:238;2046:6;2043:1;2040:13;2021:238;;;2129:3;2116:17;2108:6;2104:30;2153:42;2191:3;2179:10;2153:42;;;2141:55;;-1:-1;2219:4;2210:14;;;;2238;;;;;2068:1;2061:9;2021:238;;2305:735;;2441:3;2434:4;2426:6;2422:17;2418:27;2408:2;;2459:1;2456;2449:12;2408:2;2496:6;2483:20;2518:99;2533:83;2609:6;2533:83;;2518:99;2645:21;;;2689:4;2677:17;;;;2509:108;;-1:-1;2702:14;;2677:17;2797:1;2782:252;2807:6;2804:1;2801:13;2782:252;;;2890:3;2877:17;2869:6;2865:30;2914:56;2966:3;2954:10;2914:56;;;2902:69;;-1:-1;2994:4;2985:14;;;;3013;;;;;2829:1;2822:9;2782:252;;3066:714;;3190:3;3183:4;3175:6;3171:17;3167:27;3157:2;;3208:1;3205;3198:12;3157:2;3238:6;3232:13;3260:76;3275:60;3328:6;3275:60;;3260:76;3251:85;;3353:5;3378:6;3371:5;3364:21;3408:4;3400:6;3396:17;3386:27;;3430:4;3425:3;3421:14;3414:21;;3483:6;3530:3;3522:4;3514:6;3510:17;3505:3;3501:27;3498:36;3495:2;;;3547:1;3544;3537:12;3495:2;3572:1;3557:217;3582:6;3579:1;3576:13;3557:217;;;3640:3;3662:48;3706:3;3694:10;3662:48;;;3650:61;;-1:-1;3734:4;3725:14;;;;3753;;;;;3604:1;3597:9;3557:217;;3806:707;;3923:3;3916:4;3908:6;3904:17;3900:27;3890:2;;3941:1;3938;3931:12;3890:2;3978:6;3965:20;4000:80;4015:64;4072:6;4015:64;;4000:80;3991:89;;4097:5;4122:6;4115:5;4108:21;4152:4;4144:6;4140:17;4130:27;;4174:4;4169:3;4165:14;4158:21;;4227:6;4274:3;4266:4;4258:6;4254:17;4249:3;4245:27;4242:36;4239:2;;;4291:1;4288;4281:12;4239:2;4316:1;4301:206;4326:6;4323:1;4320:13;4301:206;;;4384:3;4406:37;4439:3;4427:10;4406:37;;;4394:50;;-1:-1;4467:4;4458:14;;;;4486;;;;;4348:1;4341:9;4301:206;;4521:128;4596:13;;4614:30;4596:13;4614:30;;4656:134;4734:13;;4752:33;4734:13;4752:33;;4798:432;;4895:3;4888:4;4880:6;4876:17;4872:27;4862:2;;4913:1;4910;4903:12;4862:2;4950:6;4937:20;4972:60;4987:44;5024:6;4987:44;;4972:60;4963:69;;5052:6;5045:5;5038:21;5088:4;5080:6;5076:17;5121:4;5114:5;5110:16;5156:3;5147:6;5142:3;5138:16;5135:25;5132:2;;;5173:1;5170;5163:12;5132:2;5183:41;5217:6;5212:3;5207;5183:41;;;4855:375;;;;;;;;5238:166;5332:13;;5350:49;5332:13;5350:49;;5411:128;5477:20;;5502:32;5477:20;5502:32;;5594:496;;5715:4;5703:9;5698:3;5694:19;5690:30;5687:2;;;5733:1;5730;5723:12;5687:2;5751:20;5766:4;5751:20;;;5742:29;-1:-1;5834:1;5866:49;5911:3;5891:9;5866:49;;;5841:75;;-1:-1;5986:2;6019:49;6064:3;6040:22;;;6019:49;;;6012:4;6005:5;6001:16;5994:75;5937:143;5681:409;;;;;6129:704;;6252:4;6240:9;6235:3;6231:19;6227:30;6224:2;;;6270:1;6267;6260:12;6224:2;6288:20;6303:4;6288:20;;;6279:29;-1:-1;6365:1;6397:76;6469:3;6449:9;6397:76;;;6372:102;;-1:-1;6540:2;6573:60;6629:3;6605:22;;;6573:60;;;6566:4;6559:5;6555:16;6548:86;6495:150;6718:2;6751:60;6807:3;6798:6;6787:9;6783:22;6751:60;;;6744:4;6737:5;6733:16;6726:86;6655:168;6218:615;;;;;6868:2714;;6976:6;6964:9;6959:3;6955:19;6951:32;6948:2;;;6996:1;6993;6986:12;6948:2;7014:22;7029:6;7014:22;;;7005:31;-1:-1;7094:1;7126:49;7171:3;7151:9;7126:49;;;7101:75;;-1:-1;7245:2;7278:49;7323:3;7299:22;;;7278:49;;;7271:4;7264:5;7260:16;7253:75;7197:142;7404:2;7437:49;7482:3;7473:6;7462:9;7458:22;7437:49;;;7430:4;7423:5;7419:16;7412:75;7349:149;7557:2;7590:49;7635:3;7626:6;7615:9;7611:22;7590:49;;;7583:4;7576:5;7572:16;7565:75;7508:143;7713:3;7747:49;7792:3;7783:6;7772:9;7768:22;7747:49;;;7740:4;7733:5;7729:16;7722:75;7661:147;7870:3;7904:49;7949:3;7940:6;7929:9;7925:22;7904:49;;;7897:4;7890:5;7886:16;7879:75;7818:147;8019:3;8053:49;8098:3;8089:6;8078:9;8074:22;8053:49;;;8046:4;8039:5;8035:16;8028:75;7975:139;8168:3;8202:49;8247:3;8238:6;8227:9;8223:22;8202:49;;;8195:4;8188:5;8184:16;8177:75;8124:139;8330:3;8366:49;8411:3;8402:6;8391:9;8387:22;8366:49;;;8357:6;8350:5;8346:18;8339:77;8273:154;8477:3;8513:49;8558:3;8549:6;8538:9;8534:22;8513:49;;;8504:6;8497:5;8493:18;8486:77;8437:137;8662:3;8651:9;8647:19;8634:33;-1:-1;;;;;8679:6;8676:30;8673:2;;;8719:1;8716;8709:12;8673:2;8756:54;8806:3;8797:6;8786:9;8782:22;8756:54;;;8747:6;8740:5;8736:18;8729:82;8584:238;8910:3;8899:9;8895:19;8882:33;-1:-1;;;;;8927:6;8924:30;8921:2;;;8967:1;8964;8957:12;8921:2;9004:54;9054:3;9045:6;9034:9;9030:22;9004:54;;;8995:6;8988:5;8984:18;8977:82;8832:238;9161:3;9150:9;9146:19;9133:33;-1:-1;;;;;9178:6;9175:30;9172:2;;;9218:1;9215;9208:12;9172:2;9255:54;9305:3;9296:6;9285:9;9281:22;9255:54;;;9246:6;9239:5;9235:18;9228:82;9080:241;9412:3;9401:9;9397:19;9384:33;-1:-1;;;;;9429:6;9426:30;9423:2;;;9469:1;9466;9459:12;9423:2;9506:54;9556:3;9547:6;9536:9;9532:22;9506:54;;;9497:6;9490:5;9486:18;9479:82;9331:241;6942:2640;;;;;9589:130;9656:20;;9681:33;9656:20;9681:33;;9867:263;;9982:2;9970:9;9961:7;9957:23;9953:32;9950:2;;;9998:1;9995;9988:12;9950:2;10033:1;10050:64;10106:7;10086:9;10050:64;;10423:415;;;10563:2;10551:9;10542:7;10538:23;10534:32;10531:2;;;10579:1;10576;10569:12;10531:2;10614:1;10631:72;10695:7;10675:9;10631:72;;;10621:82;;10593:116;10740:2;10758:64;10814:7;10805:6;10794:9;10790:22;10758:64;;;10748:74;;10719:109;10525:313;;;;;;10845:491;;;;10983:2;10971:9;10962:7;10958:23;10954:32;10951:2;;;10999:1;10996;10989:12;10951:2;11034:1;11051:53;11096:7;11076:9;11051:53;;;11041:63;;11013:97;11141:2;11159:53;11204:7;11195:6;11184:9;11180:22;11159:53;;;11149:63;;11120:98;11249:2;11267:53;11312:7;11303:6;11292:9;11288:22;11267:53;;;11257:63;;11228:98;10945:391;;;;;;11343:879;;;;;;11540:3;11528:9;11519:7;11515:23;11511:33;11508:2;;;11557:1;11554;11547:12;11508:2;11592:1;11609:53;11654:7;11634:9;11609:53;;;11599:63;;11571:97;11699:2;11717:53;11762:7;11753:6;11742:9;11738:22;11717:53;;;11707:63;;11678:98;11807:2;11825:53;11870:7;11861:6;11850:9;11846:22;11825:53;;;11815:63;;11786:98;11915:2;11933:53;11978:7;11969:6;11958:9;11954:22;11933:53;;;11923:63;;11894:98;12051:3;12040:9;12036:19;12023:33;-1:-1;;;;;12068:6;12065:30;12062:2;;;12108:1;12105;12098:12;12062:2;12128:78;12198:7;12189:6;12178:9;12174:22;12128:78;;;12118:88;;12002:210;11502:720;;;;;;;;;12229:753;;;;;12409:3;12397:9;12388:7;12384:23;12380:33;12377:2;;;12426:1;12423;12416:12;12377:2;12461:1;12478:53;12523:7;12503:9;12478:53;;;12468:63;;12440:97;12568:2;12586:53;12631:7;12622:6;12611:9;12607:22;12586:53;;;12576:63;;12547:98;12676:2;12694:53;12739:7;12730:6;12719:9;12715:22;12694:53;;;12684:63;;12655:98;12812:2;12801:9;12797:18;12784:32;-1:-1;;;;;12828:6;12825:30;12822:2;;;12868:1;12865;12858:12;12822:2;12888:78;12958:7;12949:6;12938:9;12934:22;12888:78;;;12878:88;;12763:209;12371:611;;;;;;;;12989:943;;;;;;13218:3;13206:9;13197:7;13193:23;13189:33;13186:2;;;13235:1;13232;13225:12;13186:2;13270:1;13287:53;13332:7;13312:9;13287:53;;;13277:63;;13249:97;13377:2;13395:53;13440:7;13431:6;13420:9;13416:22;13395:53;;;13385:63;;13356:98;13485:2;13503:53;13548:7;13539:6;13528:9;13524:22;13503:53;;;13493:63;;13464:98;13621:2;13610:9;13606:18;13593:32;-1:-1;;;;;13637:6;13634:30;13631:2;;;13677:1;13674;13667:12;13631:2;13697:78;13767:7;13758:6;13747:9;13743:22;13697:78;;;13687:88;;13572:209;13812:3;13831:85;13908:7;13899:6;13888:9;13884:22;13831:85;;13939:627;;;;14102:2;14090:9;14081:7;14077:23;14073:32;14070:2;;;14118:1;14115;14108:12;14070:2;14153:1;14170:53;14215:7;14195:9;14170:53;;;14160:63;;14132:97;14260:2;14278:53;14323:7;14314:6;14303:9;14299:22;14278:53;;;14268:63;;14239:98;14396:2;14385:9;14381:18;14368:32;-1:-1;;;;;14412:6;14409:30;14406:2;;;14452:1;14449;14442:12;14406:2;14472:78;14542:7;14533:6;14522:9;14518:22;14472:78;;14573:817;;;;;14785:3;14773:9;14764:7;14760:23;14756:33;14753:2;;;14802:1;14799;14792:12;14753:2;14837:1;14854:53;14899:7;14879:9;14854:53;;;14844:63;;14816:97;14944:2;14962:53;15007:7;14998:6;14987:9;14983:22;14962:53;;;14952:63;;14923:98;15080:2;15069:9;15065:18;15052:32;-1:-1;;;;;15096:6;15093:30;15090:2;;;15136:1;15133;15126:12;15090:2;15156:78;15226:7;15217:6;15206:9;15202:22;15156:78;;;15146:88;;15031:209;15271:2;15289:85;15366:7;15357:6;15346:9;15342:22;15289:85;;15397:749;;;;;15575:3;15563:9;15554:7;15550:23;15546:33;15543:2;;;15592:1;15589;15582:12;15543:2;15627:1;15644:53;15689:7;15669:9;15644:53;;;15634:63;;15606:97;15734:2;15752:52;15796:7;15787:6;15776:9;15772:22;15752:52;;;15742:62;;15713:97;15841:2;15859:52;15903:7;15894:6;15883:9;15879:22;15859:52;;16153:638;;;16324:2;16312:9;16303:7;16299:23;16295:32;16292:2;;;16340:1;16337;16330:12;16292:2;16375:31;;-1:-1;;;;;16415:30;;16412:2;;;16458:1;16455;16448:12;16412:2;16478:78;16548:7;16539:6;16528:9;16524:22;16478:78;;;16468:88;;16354:208;16621:2;16610:9;16606:18;16593:32;-1:-1;;;;;16637:6;16634:30;16631:2;;;16677:1;16674;16667:12;16631:2;16697:78;16767:7;16758:6;16747:9;16743:22;16697:78;;16798:411;;;16944:2;16932:9;16923:7;16919:23;16915:32;16912:2;;;16960:1;16957;16950:12;16912:2;16995:31;;-1:-1;;;;;17035:30;;17032:2;;;17078:1;17075;17068:12;17032:2;17106:87;17185:7;17176:6;17165:9;17161:22;17106:87;;;17096:97;;;;16974:225;16906:303;;;;;;17216:686;;;17411:2;17399:9;17390:7;17386:23;17382:32;17379:2;;;17427:1;17424;17417:12;17379:2;17462:31;;-1:-1;;;;;17502:30;;17499:2;;;17545:1;17542;17535:12;17499:2;17565:97;17654:7;17645:6;17634:9;17630:22;17565:97;;;17555:107;;17441:227;17727:2;17716:9;17712:18;17699:32;-1:-1;;;;;17743:6;17740:30;17737:2;;;17783:1;17780;17773:12;17737:2;17803:83;17878:7;17869:6;17858:9;17854:22;17803:83;;17909:384;;18045:2;18033:9;18024:7;18020:23;18016:32;18013:2;;;18061:1;18058;18051:12;18013:2;18096:24;;-1:-1;;;;;18129:30;;18126:2;;;18172:1;18169;18162:12;18126:2;18192:85;18269:7;18260:6;18249:9;18245:22;18192:85;;18300:577;;;;18469:3;18457:9;18448:7;18444:23;18440:33;18437:2;;;18486:1;18483;18476:12;18437:2;18521:1;18538:87;18617:7;18597:9;18538:87;;;18528:97;;18500:131;18662:2;18680:64;18736:7;18727:6;18716:9;18712:22;18680:64;;;18670:74;;18641:109;18781:3;18800:61;18853:7;18844:6;18833:9;18829:22;18800:61;;18884:263;;18999:2;18987:9;18978:7;18974:23;18970:32;18967:2;;;19015:1;19012;19005:12;18967:2;19050:1;19067:64;19123:7;19103:9;19067:64;;19155:173;;19242:46;19284:3;19276:6;19242:46;;;-1:-1;;19317:4;19308:14;;19235:93;19337:177;;19448:60;19504:3;19496:6;19448:60;;19523:173;;19610:46;19652:3;19644:6;19610:46;;19704:103;19777:24;19795:5;19777:24;;;19772:3;19765:37;19759:48;;;19965:690;;20110:54;20158:5;20110:54;;;20177:86;20256:6;20251:3;20177:86;;;20170:93;;20284:56;20334:5;20284:56;;;20360:7;20388:1;20373:260;20398:6;20395:1;20392:13;20373:260;;;20465:6;20459:13;20486:63;20545:3;20530:13;20486:63;;;20479:70;;20566:60;20619:6;20566:60;;;20556:70;-1:-1;;20420:1;20413:9;20373:260;;;-1:-1;20646:3;;20089:566;-1:-1;;;;;20089:566;20690:888;;20845:59;20898:5;20845:59;;;20917:91;21001:6;20996:3;20917:91;;;20910:98;;21031:3;21073:4;21065:6;21061:17;21056:3;21052:27;21100:61;21155:5;21100:61;;;21181:7;21209:1;21194:345;21219:6;21216:1;21213:13;21194:345;;;21281:9;21275:4;21271:20;21266:3;21259:33;21326:6;21320:13;21348:74;21417:4;21402:13;21348:74;;;21340:82;;21439:65;21497:6;21439:65;;;21527:4;21518:14;;;;;21429:75;-1:-1;;21241:1;21234:9;21194:345;;;-1:-1;21552:4;;20824:754;-1:-1;;;;;;;20824:754;21617:690;;21762:54;21810:5;21762:54;;;21829:86;21908:6;21903:3;21829:86;;;21822:93;;21936:56;21986:5;21936:56;;;22012:7;22040:1;22025:260;22050:6;22047:1;22044:13;22025:260;;;22117:6;22111:13;22138:63;22197:3;22182:13;22138:63;;;22131:70;;22218:60;22271:6;22218:60;;;22208:70;-1:-1;;22072:1;22065:9;22025:260;;22315:104;22392:21;22407:5;22392:21;;22449:306;;22577:88;22658:6;22653:3;22577:88;;;22570:95;;22677:43;22713:6;22708:3;22701:5;22677:43;;;-1:-1;;22733:16;;22563:192;22763:356;;22891:38;22923:5;22891:38;;;22941:88;23022:6;23017:3;22941:88;;;22934:95;;23034:52;23079:6;23074:3;23067:4;23060:5;23056:16;23034:52;;;23098:16;;;;;22871:248;-1:-1;;22871:248;23126:315;;23222:34;23250:5;23222:34;;;23268:60;23321:6;23316:3;23268:60;;;23261:67;;23333:52;23378:6;23373:3;23366:4;23359:5;23355:16;23333:52;;;23406:29;23428:6;23406:29;;;23397:39;;;;23202:239;-1:-1;;;23202:239;24145:162;24246:55;24295:5;24246:55;;24314:188;24428:68;24490:5;24428:68;;24509:110;24590:23;24607:5;24590:23;;24627:374;;24787:67;24851:2;24846:3;24787:67;;;24887:34;24867:55;;-1:-1;;;24951:2;24942:12;;24935:29;24992:2;24983:12;;24773:228;-1:-1;;24773:228;25062:2776;25281:23;;25062:2776;;25205:6;25196:16;;;25310:63;25200:3;25281:23;25310:63;;;25227:152;25460:4;25453:5;25449:16;25443:23;25472:63;25529:4;25524:3;25520:14;25506:12;25472:63;;;25389:152;25629:4;25622:5;25618:16;25612:23;25641:63;25698:4;25693:3;25689:14;25675:12;25641:63;;;25551:159;25792:4;25785:5;25781:16;25775:23;25804:63;25861:4;25856:3;25852:14;25838:12;25804:63;;;25720:153;25958:4;25951:5;25947:16;25941:23;25970:63;26027:4;26022:3;26018:14;26004:12;25970:63;;;25883:156;26124:4;26117:5;26113:16;26107:23;26136:63;26193:4;26188:3;26184:14;26170:12;26136:63;;;26049:156;26282:4;26275:5;26271:16;26265:23;26294:63;26351:4;26346:3;26342:14;26328:12;26294:63;;;26215:148;26440:4;26433:5;26429:16;26423:23;26452:63;26509:4;26504:3;26500:14;26486:12;26452:63;;;26373:148;26611:6;26604:5;26600:18;26594:25;26625:65;26682:6;26677:3;26673:16;26659:12;26625:65;;;26531:165;26769:6;26762:5;26758:18;26752:25;26783:65;26840:6;26835:3;26831:16;26817:12;26783:65;;;26706:148;26937:6;26930:5;26926:18;26920:25;26993:3;26987:4;26983:14;26974:6;26969:3;26965:16;26958:40;27013:67;27075:4;27061:12;27013:67;;;27005:75;;26864:228;27175:6;27168:5;27164:18;27158:25;27231:3;27225:4;27221:14;27212:6;27207:3;27203:16;27196:40;27251:67;27313:4;27299:12;27251:67;;;27243:75;;27102:228;27416:6;27409:5;27405:18;27399:25;27472:3;27466:4;27462:14;27453:6;27448:3;27444:16;27437:40;27492:67;27554:4;27540:12;27492:67;;;27484:75;;27340:231;27657:6;27650:5;27646:18;27640:25;27713:3;27707:4;27703:14;27694:6;27689:3;27685:16;27678:40;27733:67;27795:4;27781:12;27733:67;;27845:103;27918:24;27936:5;27918:24;;28075:274;;28225:99;28320:3;28311:6;28303;28225:99;;28356:254;;28496:89;28581:3;28572:6;28496:89;;28886:213;29004:2;28989:18;;29018:71;28993:9;29062:6;29018:71;;29106:324;29252:2;29237:18;;29266:71;29241:9;29310:6;29266:71;;;29348:72;29416:2;29405:9;29401:18;29392:6;29348:72;;29437:695;29689:3;29674:19;;29704:71;29678:9;29748:6;29704:71;;;29786:72;29854:2;29843:9;29839:18;29830:6;29786:72;;;29869;29937:2;29926:9;29922:18;29913:6;29869:72;;;29989:9;29983:4;29979:20;29974:2;29963:9;29959:18;29952:48;30014:108;30117:4;30108:6;30014:108;;30139:547;30341:3;30326:19;;30356:71;30330:9;30400:6;30356:71;;;30438:72;30506:2;30495:9;30491:18;30482:6;30438:72;;;30521;30589:2;30578:9;30574:18;30565:6;30521:72;;;30604;30672:2;30661:9;30657:18;30648:6;30604:72;;30693:583;30917:2;30902:18;;30931:71;30906:9;30975:6;30931:71;;;31013:72;31081:2;31070:9;31066:18;31057:6;31013:72;;;31133:9;31127:4;31123:20;31118:2;31107:9;31103:18;31096:48;31158:108;31261:4;31252:6;31158:108;;31283:435;31457:2;31442:18;;31471:71;31446:9;31515:6;31471:71;;;31553:72;31621:2;31610:9;31606:18;31597:6;31553:72;;;31636;31704:2;31693:9;31689:18;31680:6;31636:72;;31725:535;31921:3;31906:19;;31936:71;31910:9;31980:6;31936:71;;;32018:72;32086:2;32075:9;32071:18;32062:6;32018:72;;;32101;32169:2;32158:9;32154:18;32145:6;32101:72;;;32184:66;32246:2;32235:9;32231:18;32222:6;32184:66;;32267:381;32445:2;32459:47;;;32430:18;;32520:118;32430:18;32624:6;32520:118;;32655:361;32823:2;32837:47;;;32808:18;;32898:108;32808:18;32992:6;32898:108;;33023:471;33215:2;33200:18;;33229:89;33204:9;33291:6;33229:89;;;33329:72;33397:2;33386:9;33382:18;33373:6;33329:72;;33501:497;33706:2;33691:18;;33720:102;33695:9;33795:6;33720:102;;34005:427;34175:2;34160:18;;34189:69;34164:9;34231:6;34189:69;;;34269:70;34335:2;34324:9;34320:18;34311:6;34269:70;;34439:407;34630:2;34644:47;;;34615:18;;34705:131;34615:18;34705:131;;34853:532;35055:2;35069:47;;;35040:18;;35130:100;35040:18;35216:6;35130:100;;;35122:108;;35278:9;35272:4;35268:20;35263:2;35252:9;35248:18;35241:48;35303:72;35370:4;35361:6;35303:72;;35392:213;35510:2;35495:18;;35524:71;35499:9;35568:6;35524:71;;35612:472;35808:2;35793:18;;35822:71;35797:9;35866:6;35822:71;;;35941:9;35935:4;35931:20;35926:2;35915:9;35911:18;35904:48;35966:108;36069:4;36060:6;35966:108;;36091:256;36153:2;36147:9;36179:17;;;-1:-1;;;;;36239:34;;36275:22;;;36236:62;36233:2;;;36311:1;36308;36301:12;36233:2;36327;36320:22;36131:216;;-1:-1;36131:216;36354:304;;-1:-1;;;;;36505:6;36502:30;36499:2;;;36545:1;36542;36535:12;36499:2;-1:-1;36580:4;36568:17;;;36633:15;;36436:222;37929:317;;-1:-1;;;;;38060:6;38057:30;38054:2;;;38100:1;38097;38090:12;38054:2;-1:-1;38231:4;38167;38144:17;;;;-1:-1;;38140:33;38221:15;;37991:255;38253:151;38377:4;38368:14;;38325:79;38732:137;38835:12;;38806:63;39772:178;39890:19;;;39939:4;39930:14;;39883:67;40994:91;;41056:24;41074:5;41056:24;;41198:85;41264:13;41257:21;;41240:43;41290:72;41352:5;41335:27;41369:146;41451:5;41457:53;41451:5;41457:53;;41522:172;41617:5;41623:66;41617:5;41623:66;;41701:87;41773:2;41762:21;;41745:43;41795:121;-1:-1;;;;;41857:54;;41840:76;42002:146;;42099:44;42137:5;42099:44;;42155:172;;42265:57;42316:5;42265:57;;42335:145;42416:6;42411:3;42406;42393:30;-1:-1;42472:1;42454:16;;42447:27;42386:94;42489:268;42554:1;42561:101;42575:6;42572:1;42569:13;42561:101;;;42642:11;;;42636:18;42623:11;;;42616:39;42597:2;42590:10;42561:101;;;42677:6;42674:1;42671:13;42668:2;;;42742:1;42733:6;42728:3;42724:16;42717:27;42668:2;42538:219;;;;;42765:97;42853:2;42833:14;-1:-1;;42829:28;;42813:49;42870:111;42959:1;42952:5;42949:12;42939:2;;42965:9;42939:2;42933:48;;42988:124;43090:1;43083:5;43080:12;43070:2;;43096:9;43119:117;43188:24;43206:5;43188:24;;;43181:5;43178:35;43168:2;;43227:1;43224;43217:12;43383:111;43449:21;43464:5;43449:21;;43501:117;43570:24;43588:5;43570:24;;43625:110;43710:1;43703:5;43700:12;43690:2;;43726:1;43723;43716:12;43742:115;43810:23;43827:5;43810:23;

Swarm Source

bzzr://ddb003984abfa8a85ae40b8965430411b3a89ecf35a231473731767cd9914382

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

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

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.