ETH Price: $3,377.79 (-1.28%)

Contract

0xa2F1F3a93921299f071a002B77a5F3175492Bc6A
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
0x61012060179352242023-08-17 14:46:11467 days ago1692283571IN
 Create: EthereumBridgeAdapter
0 ETH0.1744529734.97119006

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
EthereumBridgeAdapter

Compiler Version
v0.6.12+commit.27d51765

Optimization Enabled:
Yes with 1000000 runs

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

pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;

import "./AbstractBridgeAdapter.sol";
import "./BridgeProtocols.sol";
import "./mixins/MixinAaveV2.sol";
import "./mixins/MixinBalancer.sol";
import "./mixins/MixinBalancerV2Batch.sol";
import "./mixins/MixinBancorV3.sol";
import "./mixins/MixinBarter.sol";
import "./mixins/MixinCompound.sol";
import "./mixins/MixinCurve.sol";
import "./mixins/MixinCurveV2.sol";
import "./mixins/MixinCryptoCom.sol";
import "./mixins/MixinDodo.sol";
import "./mixins/MixinDodoV2.sol";
import "./mixins/MixinKyberDmm.sol";
import "./mixins/MixinKyberElastic.sol";
import "./mixins/MixinLido.sol";
import "./mixins/MixinMakerPSM.sol";
import "./mixins/MixinMaverickV1.sol";
import "./mixins/MixinNerve.sol";
import "./mixins/MixinSynthetix.sol";
import "./mixins/MixinUniswap.sol";
import "./mixins/MixinUniswapV2.sol";
import "./mixins/MixinUniswapV3.sol";
import "./mixins/MixinZeroExBridge.sol";

contract EthereumBridgeAdapter is
    AbstractBridgeAdapter(1, "Ethereum"),
    MixinAaveV2,
    MixinBalancer,
    MixinBalancerV2Batch,
    MixinBancorV3,
    MixinBarter,
    MixinCompound,
    MixinCurve,
    MixinCurveV2,
    MixinCryptoCom,
    MixinDodo,
    MixinDodoV2,
    MixinKyberDmm,
    MixinKyberElastic,
    MixinLido,
    MixinMakerPSM,
    MixinMaverickV1,
    MixinNerve,
    MixinSynthetix,
    MixinUniswap,
    MixinUniswapV2,
    MixinUniswapV3,
    MixinZeroExBridge
{
    constructor(
        IEtherToken weth
    ) public MixinBancorV3(weth) MixinCompound(weth) MixinCurve(weth) MixinLido(weth) MixinUniswap(weth) {}

    function _trade(
        BridgeOrder memory order,
        IERC20Token sellToken,
        IERC20Token buyToken,
        uint256 sellAmount,
        bool dryRun
    ) internal override returns (uint256 boughtAmount, bool supportedSource) {
        uint128 protocolId = uint128(uint256(order.source) >> 128);
        if (protocolId == BridgeProtocols.CURVE) {
            if (dryRun) {
                return (0, true);
            }
            boughtAmount = _tradeCurve(sellToken, buyToken, sellAmount, order.bridgeData);
        } else if (protocolId == BridgeProtocols.CURVEV2) {
            if (dryRun) {
                return (0, true);
            }
            boughtAmount = _tradeCurveV2(sellToken, buyToken, sellAmount, order.bridgeData);
        } else if (protocolId == BridgeProtocols.UNISWAPV3) {
            if (dryRun) {
                return (0, true);
            }
            boughtAmount = _tradeUniswapV3(sellToken, sellAmount, order.bridgeData);
        } else if (protocolId == BridgeProtocols.UNISWAPV2) {
            if (dryRun) {
                return (0, true);
            }
            boughtAmount = _tradeUniswapV2(buyToken, sellAmount, order.bridgeData);
        } else if (protocolId == BridgeProtocols.UNISWAP) {
            if (dryRun) {
                return (0, true);
            }
            boughtAmount = _tradeUniswap(sellToken, buyToken, sellAmount, order.bridgeData);
        } else if (protocolId == BridgeProtocols.BALANCER) {
            if (dryRun) {
                return (0, true);
            }
            boughtAmount = _tradeBalancer(sellToken, buyToken, sellAmount, order.bridgeData);
        } else if (protocolId == BridgeProtocols.BALANCERV2BATCH) {
            if (dryRun) {
                return (0, true);
            }
            boughtAmount = _tradeBalancerV2Batch(sellAmount, order.bridgeData);
        } else if (protocolId == BridgeProtocols.MAKERPSM) {
            if (dryRun) {
                return (0, true);
            }
            boughtAmount = _tradeMakerPsm(sellToken, buyToken, sellAmount, order.bridgeData);
        } else if (protocolId == BridgeProtocols.DODO) {
            if (dryRun) {
                return (0, true);
            }
            boughtAmount = _tradeDodo(sellToken, sellAmount, order.bridgeData);
        } else if (protocolId == BridgeProtocols.DODOV2) {
            if (dryRun) {
                return (0, true);
            }
            boughtAmount = _tradeDodoV2(sellToken, sellAmount, order.bridgeData);
        } else if (protocolId == BridgeProtocols.CRYPTOCOM) {
            if (dryRun) {
                return (0, true);
            }
            boughtAmount = _tradeCryptoCom(buyToken, sellAmount, order.bridgeData);
        } else if (protocolId == BridgeProtocols.NERVE) {
            if (dryRun) {
                return (0, true);
            }
            boughtAmount = _tradeNerve(sellToken, sellAmount, order.bridgeData);
        } else if (protocolId == BridgeProtocols.KYBERDMM) {
            if (dryRun) {
                return (0, true);
            }
            boughtAmount = _tradeKyberDmm(buyToken, sellAmount, order.bridgeData);
        } else if (protocolId == BridgeProtocols.KYBERELASTIC) {
            if (dryRun) {
                return (0, true);
            }
            boughtAmount = _tradeKyberElastic(sellToken, sellAmount, order.bridgeData);
        } else if (protocolId == BridgeProtocols.LIDO) {
            if (dryRun) {
                return (0, true);
            }
            boughtAmount = _tradeLido(sellToken, buyToken, sellAmount, order.bridgeData);
        } else if (protocolId == BridgeProtocols.AAVEV2) {
            if (dryRun) {
                return (0, true);
            }
            boughtAmount = _tradeAaveV2(sellToken, buyToken, sellAmount, order.bridgeData);
        } else if (protocolId == BridgeProtocols.COMPOUND) {
            if (dryRun) {
                return (0, true);
            }
            boughtAmount = _tradeCompound(sellToken, buyToken, sellAmount, order.bridgeData);
        } else if (protocolId == BridgeProtocols.BANCORV3) {
            if (dryRun) {
                return (0, true);
            }
            boughtAmount = _tradeBancorV3(buyToken, sellAmount, order.bridgeData);
        } else if (protocolId == BridgeProtocols.SYNTHETIX) {
            if (dryRun) {
                return (0, true);
            }
            boughtAmount = _tradeSynthetix(sellAmount, order.bridgeData);
        } else if (protocolId == BridgeProtocols.BARTER) {
            if (dryRun) {
                return (0, true);
            }
            boughtAmount = _tradeBarter(sellToken, sellAmount, order.bridgeData);
        } else if (protocolId == BridgeProtocols.MAVERICK) {
            if (dryRun) {
                return (0, true);
            }
            boughtAmount = _tradeMaverickV1(sellToken, buyToken, sellAmount, order.bridgeData);
        } else if (protocolId == BridgeProtocols.UNKNOWN) {
            if (dryRun) {
                return (0, true);
            }
            boughtAmount = _tradeZeroExBridge(sellToken, buyToken, sellAmount, order.bridgeData);
        }

        emit BridgeFill(order.source, sellToken, buyToken, sellAmount, boughtAmount);
    }
}

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

pragma solidity ^0.6;
pragma experimental ABIEncoderV2;

import "./IBridgeAdapter.sol";

abstract contract AbstractBridgeAdapter is IBridgeAdapter {
    constructor(uint256 expectedChainId, string memory expectedChainName) public {
        uint256 chainId;
        assembly {
            chainId := chainid()
        }
        // Skip chain id validation on Ganache (1337), Anvil (31337), Goerli (5), Mumbai (80001), Base Goerli (84531)
        bool skipValidation = (chainId == 1337 ||
            chainId == 31337 ||
            chainId == 5 ||
            chainId == 80001 ||
            chainId == 84531);

        if (chainId != expectedChainId && !skipValidation) {
            revert(string(abi.encodePacked(expectedChainName, "BridgeAdapter.constructor: wrong chain ID")));
        }
    }

    function isSupportedSource(bytes32 source) external override returns (bool isSupported) {
        BridgeOrder memory placeholderOrder;
        placeholderOrder.source = source;
        IERC20Token placeholderToken = IERC20Token(address(0));

        (, isSupported) = _trade(placeholderOrder, placeholderToken, placeholderToken, 0, true);
    }

    function trade(
        BridgeOrder memory order,
        IERC20Token sellToken,
        IERC20Token buyToken,
        uint256 sellAmount
    ) public override returns (uint256 boughtAmount) {
        (boughtAmount, ) = _trade(order, sellToken, buyToken, sellAmount, false);
    }

    function _trade(
        BridgeOrder memory order,
        IERC20Token sellToken,
        IERC20Token buyToken,
        uint256 sellAmount,
        bool dryRun
    ) internal virtual returns (uint256 boughtAmount, bool supportedSource);
}

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

pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;

import "@0x/contracts-erc20/src/IERC20Token.sol";

interface IBridgeAdapter {
    struct BridgeOrder {
        // Upper 16 bytes: uint128 protocol ID (right-aligned)
        // Lower 16 bytes: ASCII source name (left-aligned)
        bytes32 source;
        uint256 takerTokenAmount;
        uint256 makerTokenAmount;
        bytes bridgeData;
    }

    /// @dev Emitted when tokens are swapped with an external source.
    /// @param source A unique ID for the source, where the upper 16 bytes
    ///        encodes the (right-aligned) uint128 protocol ID and the
    ///        lower 16 bytes encodes an ASCII source name.
    /// @param inputToken The token the bridge is converting from.
    /// @param outputToken The token the bridge is converting to.
    /// @param inputTokenAmount Amount of input token sold.
    /// @param outputTokenAmount Amount of output token bought.
    event BridgeFill(
        bytes32 source,
        IERC20Token inputToken,
        IERC20Token outputToken,
        uint256 inputTokenAmount,
        uint256 outputTokenAmount
    );

    function isSupportedSource(bytes32 source) external returns (bool isSupported);

    function trade(
        BridgeOrder calldata order,
        IERC20Token sellToken,
        IERC20Token buyToken,
        uint256 sellAmount
    ) external returns (uint256 boughtAmount);
}

File 4 of 35 : IERC20Token.sol
// SPDX-License-Identifier: Apache-2.0
/*

  Copyright 2023 ZeroEx Intl.

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

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

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

*/

pragma solidity >=0.6.5 <0.9;

interface IERC20Token {
    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);

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

    /// @dev Get the allowance for `spender` to spend from `owner`.
    /// @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);

    /// @dev Get the number of decimals this token has.
    function decimals() external view returns (uint8);
}

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

pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;

import "@0x/contracts-erc20/src/IERC20Token.sol";

library BridgeProtocols {
    // A incrementally increasing, append-only list of protocol IDs.
    // We don't use an enum so solidity doesn't throw when we pass in a
    // new protocol ID that hasn't been rolled up yet.
    uint128 internal constant UNKNOWN = 0;
    uint128 internal constant CURVE = 1;
    uint128 internal constant UNISWAPV2 = 2;
    uint128 internal constant UNISWAP = 3;
    uint128 internal constant BALANCER = 4;
    uint128 internal constant KYBER = 5; // Not used: deprecated.
    uint128 internal constant MOONISWAP = 6;
    uint128 internal constant MSTABLE = 7;
    uint128 internal constant OASIS = 8; // Not used: deprecated.
    uint128 internal constant SHELL = 9;
    uint128 internal constant DODO = 10;
    uint128 internal constant DODOV2 = 11;
    uint128 internal constant CRYPTOCOM = 12;
    uint128 internal constant BANCOR = 13;
    uint128 internal constant COFIX = 14; // Not used: deprecated.
    uint128 internal constant NERVE = 15;
    uint128 internal constant MAKERPSM = 16;
    uint128 internal constant BALANCERV2 = 17;
    uint128 internal constant UNISWAPV3 = 18;
    uint128 internal constant KYBERDMM = 19;
    uint128 internal constant CURVEV2 = 20;
    uint128 internal constant LIDO = 21;
    uint128 internal constant CLIPPER = 22; // Not used: Clipper is now using PLP interface
    uint128 internal constant AAVEV2 = 23;
    uint128 internal constant COMPOUND = 24;
    uint128 internal constant BALANCERV2BATCH = 25;
    uint128 internal constant GMX = 26;
    uint128 internal constant PLATYPUS = 27;
    uint128 internal constant BANCORV3 = 28;
    uint128 internal constant SOLIDLY = 29;
    uint128 internal constant SYNTHETIX = 30;
    uint128 internal constant WOOFI = 31;
    uint128 internal constant AAVEV3 = 32;
    uint128 internal constant KYBERELASTIC = 33;
    uint128 internal constant BARTER = 34;
    uint128 internal constant TRADERJOEV2 = 35;
    uint128 internal constant VELODROMEV2 = 36;
    uint128 internal constant MAVERICK = 37;
}

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

pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;

import "@0x/contracts-erc20/src/v06/LibERC20TokenV06.sol";
import "@0x/contracts-erc20/src/IERC20Token.sol";

// Minimal Aave V2 LendingPool interface
interface ILendingPool {
    /**
     * @dev Deposits an `amount` of underlying asset into the reserve, receiving in return overlying aTokens.
     * - E.g. User deposits 100 USDC and gets in return 100 aUSDC
     * @param asset The address of the underlying asset to deposit
     * @param amount The amount to be deposited
     * @param onBehalfOf The address that will receive the aTokens, same as msg.sender if the user
     *   wants to receive them on his own wallet, or a different address if the beneficiary of aTokens
     *   is a different wallet
     * @param referralCode Code used to register the integrator originating the operation, for potential rewards.
     *   0 if the action is executed directly by the user, without any middle-man
     **/
    function deposit(address asset, uint256 amount, address onBehalfOf, uint16 referralCode) external;

    /**
     * @dev Withdraws an `amount` of underlying asset from the reserve, burning the equivalent aTokens owned
     * E.g. User has 100 aUSDC, calls withdraw() and receives 100 USDC, burning the 100 aUSDC
     * @param asset The address of the underlying asset to withdraw
     * @param amount The underlying amount to be withdrawn
     *   - Send the value type(uint256).max in order to withdraw the whole aToken balance
     * @param to Address that will receive the underlying, same as msg.sender if the user
     *   wants to receive it on his own wallet, or a different address if the beneficiary is a
     *   different wallet
     * @return The final amount withdrawn
     **/
    function withdraw(address asset, uint256 amount, address to) external returns (uint256);
}

contract MixinAaveV2 {
    using LibERC20TokenV06 for IERC20Token;

    function _tradeAaveV2(
        IERC20Token sellToken,
        IERC20Token buyToken,
        uint256 sellAmount,
        bytes memory bridgeData
    ) internal returns (uint256) {
        (ILendingPool lendingPool, address aToken) = abi.decode(bridgeData, (ILendingPool, address));

        sellToken.approveIfBelow(address(lendingPool), sellAmount);

        if (address(buyToken) == aToken) {
            lendingPool.deposit(address(sellToken), sellAmount, address(this), 0);
            // 1:1 mapping token -> aToken and have the same number of decimals as the underlying token
            return sellAmount;
        } else if (address(sellToken) == aToken) {
            return lendingPool.withdraw(address(buyToken), sellAmount, address(this));
        }

        revert("MixinAaveV2/UNSUPPORTED_TOKEN_PAIR");
    }
}

File 7 of 35 : LibERC20TokenV06.sol
// SPDX-License-Identifier: Apache-2.0
/*

  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.

*/

pragma solidity ^0.6.5;

import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
import "@0x/contracts-utils/contracts/src/v06/LibBytesV06.sol";
import "../IERC20Token.sol";

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

    /// @dev Calls `IERC20Token(token).approve()`.
    ///      Reverts if the return data is invalid or the call reverts.
    /// @param token The address of the token contract.
    /// @param spender The address that receives an allowance.
    /// @param allowance The allowance to set.
    function compatApprove(IERC20Token token, address spender, uint256 allowance) internal {
        bytes memory callData = abi.encodeWithSelector(token.approve.selector, spender, allowance);
        _callWithOptionalBooleanResult(address(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 the return data is invalid or the call reverts.
    /// @param token The address of the token contract.
    /// @param spender The address that receives an allowance.
    /// @param amount The minimum allowance needed.
    function approveIfBelow(IERC20Token token, address spender, uint256 amount) internal {
        if (token.allowance(address(this), spender) < amount) {
            compatApprove(token, spender, uint256(-1));
        }
    }

    /// @dev Calls `IERC20Token(token).transfer()`.
    ///      Reverts if the return data is invalid or the call reverts.
    /// @param token The address of the token contract.
    /// @param to The address that receives the tokens
    /// @param amount Number of tokens to transfer.
    function compatTransfer(IERC20Token token, address to, uint256 amount) internal {
        bytes memory callData = abi.encodeWithSelector(token.transfer.selector, to, amount);
        _callWithOptionalBooleanResult(address(token), callData);
    }

    /// @dev Calls `IERC20Token(token).transferFrom()`.
    ///      Reverts if the return data is invalid or the call reverts.
    /// @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 compatTransferFrom(IERC20Token token, address from, address to, uint256 amount) internal {
        bytes memory callData = abi.encodeWithSelector(token.transferFrom.selector, from, to, amount);
        _callWithOptionalBooleanResult(address(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 compatDecimals(IERC20Token token) internal view returns (uint8 tokenDecimals) {
        tokenDecimals = 18;
        (bool didSucceed, bytes memory resultData) = address(token).staticcall(DECIMALS_CALL_DATA);
        if (didSucceed && resultData.length >= 32) {
            tokenDecimals = uint8(LibBytesV06.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 compatAllowance(
        IERC20Token token,
        address owner,
        address spender
    ) internal view returns (uint256 allowance_) {
        (bool didSucceed, bytes memory resultData) = address(token).staticcall(
            abi.encodeWithSelector(token.allowance.selector, owner, spender)
        );
        if (didSucceed && resultData.length >= 32) {
            allowance_ = LibBytesV06.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 compatBalanceOf(IERC20Token token, address owner) internal view returns (uint256 balance) {
        (bool didSucceed, bytes memory resultData) = address(token).staticcall(
            abi.encodeWithSelector(token.balanceOf.selector, owner)
        );
        if (didSucceed && resultData.length >= 32) {
            balance = LibBytesV06.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);
        // Revert if the call reverted.
        if (!didSucceed) {
            LibRichErrorsV06.rrevert(resultData);
        }
        // If we get back 0 returndata, this may be a non-standard ERC-20 that
        // does not return a boolean. Check that it at least contains code.
        if (resultData.length == 0) {
            uint256 size;
            assembly {
                size := extcodesize(target)
            }
            require(size > 0, "invalid token address, contains no code");
            return;
        }
        // If we get back at least 32 bytes, we know the target address
        // contains code, and we assume it is a token that returned a boolean
        // success value, which must be true.
        if (resultData.length >= 32) {
            uint256 result = LibBytesV06.readUint256(resultData, 0);
            if (result == 1) {
                return;
            } else {
                LibRichErrorsV06.rrevert(resultData);
            }
        }
        // If 0 < returndatasize < 32, the target is a contract, but not a
        // valid token.
        LibRichErrorsV06.rrevert(resultData);
    }
}

File 8 of 35 : LibRichErrorsV06.sol
// SPDX-License-Identifier: Apache-2.0
/*

  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.

*/

pragma solidity ^0.6.5;

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

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

    /// @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))
        }
    }
}

File 9 of 35 : LibBytesV06.sol
// SPDX-License-Identifier: Apache-2.0
/*

  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.

*/

pragma solidity ^0.6.5;

import "./errors/LibBytesRichErrorsV06.sol";
import "./errors/LibRichErrorsV06.sol";

library LibBytesV06 {
    using LibBytesV06 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.
                    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.
                    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) {
            LibRichErrorsV06.rrevert(
                LibBytesRichErrorsV06.InvalidByteOperationError(
                    LibBytesRichErrorsV06.InvalidByteOperationErrorCodes.FromLessThanOrEqualsToRequired,
                    from,
                    to
                )
            );
        }
        if (to > b.length) {
            LibRichErrorsV06.rrevert(
                LibBytesRichErrorsV06.InvalidByteOperationError(
                    LibBytesRichErrorsV06.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.
    ///      When `from == 0`, the original array will match the slice.
    ///      In other cases its state will be corrupted.
    /// @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)
    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) {
            LibRichErrorsV06.rrevert(
                LibBytesRichErrorsV06.InvalidByteOperationError(
                    LibBytesRichErrorsV06.InvalidByteOperationErrorCodes.FromLessThanOrEqualsToRequired,
                    from,
                    to
                )
            );
        }
        if (to > b.length) {
            LibRichErrorsV06.rrevert(
                LibBytesRichErrorsV06.InvalidByteOperationError(
                    LibBytesRichErrorsV06.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 result The byte that was popped off.
    function popLastByte(bytes memory b) internal pure returns (bytes1 result) {
        if (b.length == 0) {
            LibRichErrorsV06.rrevert(
                LibBytesRichErrorsV06.InvalidByteOperationError(
                    LibBytesRichErrorsV06.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 equal 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 result address from byte array.
    function readAddress(bytes memory b, uint256 index) internal pure returns (address result) {
        if (b.length < index + 20) {
            LibRichErrorsV06.rrevert(
                LibBytesRichErrorsV06.InvalidByteOperationError(
                    LibBytesRichErrorsV06.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) {
            LibRichErrorsV06.rrevert(
                LibBytesRichErrorsV06.InvalidByteOperationError(
                    LibBytesRichErrorsV06.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 result bytes32 value from byte array.
    function readBytes32(bytes memory b, uint256 index) internal pure returns (bytes32 result) {
        if (b.length < index + 32) {
            LibRichErrorsV06.rrevert(
                LibBytesRichErrorsV06.InvalidByteOperationError(
                    LibBytesRichErrorsV06.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) {
            LibRichErrorsV06.rrevert(
                LibBytesRichErrorsV06.InvalidByteOperationError(
                    LibBytesRichErrorsV06.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 result 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 result bytes4 value from byte array.
    function readBytes4(bytes memory b, uint256 index) internal pure returns (bytes4 result) {
        if (b.length < index + 4) {
            LibRichErrorsV06.rrevert(
                LibBytesRichErrorsV06.InvalidByteOperationError(
                    LibBytesRichErrorsV06.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)
        }
    }
}

File 10 of 35 : LibBytesRichErrorsV06.sol
// SPDX-License-Identifier: Apache-2.0
/*

  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.

*/

pragma solidity ^0.6.5;

library LibBytesRichErrorsV06 {
    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;

    function InvalidByteOperationError(
        InvalidByteOperationErrorCodes errorCode,
        uint256 offset,
        uint256 required
    ) internal pure returns (bytes memory) {
        return abi.encodeWithSelector(INVALID_BYTE_OPERATION_ERROR_SELECTOR, errorCode, offset, required);
    }
}

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

pragma solidity ^0.6.5;

import "@0x/contracts-erc20/src/v06/LibERC20TokenV06.sol";
import "@0x/contracts-erc20/src/IERC20Token.sol";

interface IBalancerPool {
    /// @dev Sell `tokenAmountIn` of `tokenIn` and receive `tokenOut`.
    /// @param tokenIn The token being sold
    /// @param tokenAmountIn The amount of `tokenIn` to sell.
    /// @param tokenOut The token being bought.
    /// @param minAmountOut The minimum amount of `tokenOut` to buy.
    /// @param maxPrice The maximum value for `spotPriceAfter`.
    /// @return tokenAmountOut The amount of `tokenOut` bought.
    /// @return spotPriceAfter The new marginal spot price of the given
    ///         token pair for this pool.
    function swapExactAmountIn(
        IERC20Token tokenIn,
        uint256 tokenAmountIn,
        IERC20Token tokenOut,
        uint256 minAmountOut,
        uint256 maxPrice
    ) external returns (uint256 tokenAmountOut, uint256 spotPriceAfter);
}

contract MixinBalancer {
    using LibERC20TokenV06 for IERC20Token;

    function _tradeBalancer(
        IERC20Token sellToken,
        IERC20Token buyToken,
        uint256 sellAmount,
        bytes memory bridgeData
    ) internal returns (uint256 boughtAmount) {
        // Decode the bridge data.
        IBalancerPool pool = abi.decode(bridgeData, (IBalancerPool));
        sellToken.approveIfBelow(address(pool), sellAmount);
        // Sell all of this contract's `sellToken` token balance.
        (boughtAmount, ) = pool.swapExactAmountIn(
            sellToken, // tokenIn
            sellAmount, // tokenAmountIn
            buyToken, // tokenOut
            1, // minAmountOut
            uint256(-1) // maxPrice
        );
        return boughtAmount;
    }
}

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

pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;

import "@0x/contracts-erc20/src/v06/LibERC20TokenV06.sol";
import "@0x/contracts-erc20/src/IERC20Token.sol";

interface IBalancerV2BatchSwapVault {
    enum SwapKind {
        GIVEN_IN,
        GIVEN_OUT
    }

    struct BatchSwapStep {
        bytes32 poolId;
        uint256 assetInIndex;
        uint256 assetOutIndex;
        uint256 amount;
        bytes userData;
    }

    struct FundManagement {
        address sender;
        bool fromInternalBalance;
        address payable recipient;
        bool toInternalBalance;
    }

    function batchSwap(
        SwapKind kind,
        BatchSwapStep[] calldata swaps,
        IERC20Token[] calldata assets,
        FundManagement calldata funds,
        int256[] calldata limits,
        uint256 deadline
    ) external returns (int256[] memory amounts);
}

contract MixinBalancerV2Batch {
    using LibERC20TokenV06 for IERC20Token;

    struct BalancerV2BatchBridgeData {
        IBalancerV2BatchSwapVault vault;
        IBalancerV2BatchSwapVault.BatchSwapStep[] swapSteps;
        IERC20Token[] assets;
    }

    function _tradeBalancerV2Batch(
        uint256 sellAmount,
        bytes memory bridgeData
    ) internal returns (uint256 boughtAmount) {
        // Decode the bridge data.
        (
            IBalancerV2BatchSwapVault vault,
            IBalancerV2BatchSwapVault.BatchSwapStep[] memory swapSteps,
            address[] memory assets_
        ) = abi.decode(bridgeData, (IBalancerV2BatchSwapVault, IBalancerV2BatchSwapVault.BatchSwapStep[], address[]));
        IERC20Token[] memory assets;
        assembly {
            assets := assets_
        }

        // Grant an allowance to the exchange to spend `fromTokenAddress` token.
        assets[0].approveIfBelow(address(vault), sellAmount);

        swapSteps[0].amount = sellAmount;
        int256[] memory limits = new int256[](assets.length);
        for (uint256 i = 0; i < limits.length; ++i) {
            limits[i] = type(int256).max;
        }

        int256[] memory amounts = vault.batchSwap(
            IBalancerV2BatchSwapVault.SwapKind.GIVEN_IN,
            swapSteps,
            assets,
            IBalancerV2BatchSwapVault.FundManagement({
                sender: address(this),
                fromInternalBalance: false,
                recipient: payable(address(this)),
                toInternalBalance: false
            }),
            limits,
            block.timestamp + 1
        );
        require(amounts[amounts.length - 1] <= 0, "Unexpected BalancerV2Batch output");
        return uint256(amounts[amounts.length - 1] * -1);
    }
}

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

pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;

import "@0x/contracts-erc20/src/v06/LibERC20TokenV06.sol";
import "@0x/contracts-erc20/src/IERC20Token.sol";
import "@0x/contracts-erc20/src/IEtherToken.sol";

/*
    BancorV3
*/
interface IBancorV3 {
    /**
     * @dev performs a trade by providing the source amount and returns the target amount and the associated fee
     *
     * requirements:
     *
     * - the caller must be the network contract
     */
    function tradeBySourceAmount(
        address sourceToken,
        address targetToken,
        uint256 sourceAmount,
        uint256 minReturnAmount,
        uint256 deadline,
        address beneficiary
    ) external payable returns (uint256 amount);
}

contract MixinBancorV3 {
    using LibERC20TokenV06 for IERC20Token;

    IERC20Token public constant BANCORV3_ETH_ADDRESS = IERC20Token(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);
    IEtherToken private immutable WETH;

    constructor(IEtherToken weth) public {
        WETH = weth;
    }

    function _tradeBancorV3(
        IERC20Token buyToken,
        uint256 sellAmount,
        bytes memory bridgeData
    ) internal returns (uint256 amountOut) {
        IBancorV3 router;
        IERC20Token[] memory path;
        address[] memory _path;
        uint256 payableAmount = 0;

        {
            (router, _path) = abi.decode(bridgeData, (IBancorV3, address[]));
            // To get around `abi.decode()` not supporting interface array types.
            assembly {
                path := _path
            }
        }

        require(path.length >= 2, "MixinBancorV3/PATH_LENGTH_MUST_BE_AT_LEAST_TWO");
        require(path[path.length - 1] == buyToken, "MixinBancorV3/LAST_ELEMENT_OF_PATH_MUST_MATCH_OUTPUT_TOKEN");

        //swap WETH->ETH as Bancor only deals in ETH
        if (_path[0] == address(WETH)) {
            //withdraw the sell amount of WETH for ETH
            WETH.withdraw(sellAmount);
            payableAmount = sellAmount;
            // set _path[0] to the ETH address if WETH is our buy token
            _path[0] = address(BANCORV3_ETH_ADDRESS);
        } else {
            // Grant the BancorV3 router an allowance to sell the first token.
            path[0].approveIfBelow(address(router), sellAmount);
        }

        // if we are buying WETH we need to swap to ETH and deposit into WETH after the swap
        if (_path[1] == address(WETH)) {
            _path[1] = address(BANCORV3_ETH_ADDRESS);
        }

        uint256 amountOut = router.tradeBySourceAmount{value: payableAmount}(
            _path[0],
            _path[1],
            // Sell all tokens we hold.
            sellAmount,
            // Minimum buy amount.
            1,
            //deadline
            block.timestamp + 1,
            // address of the mixin
            address(this)
        );

        // if we want to return WETH deposit the ETH amount we sold
        if (buyToken == WETH) {
            WETH.deposit{value: amountOut}();
        }

        return amountOut;
    }
}

File 14 of 35 : IEtherToken.sol
// SPDX-License-Identifier: Apache-2.0
/*

  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.

*/

pragma solidity ^0.6.5;

import "./IERC20Token.sol";

interface IEtherToken is IERC20Token {
    /// @dev Wrap ether.
    function deposit() external payable;

    /// @dev Unwrap ether.
    function withdraw(uint256 amount) external;
}

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

pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;

import "@0x/contracts-erc20/src/v06/LibERC20TokenV06.sol";
import "@0x/contracts-erc20/src/IERC20Token.sol";
import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";

contract MixinBarter {
    using LibERC20TokenV06 for IERC20Token;
    using LibRichErrorsV06 for bytes;

    function _tradeBarter(
        IERC20Token sellToken,
        uint256 sellAmount,
        bytes memory bridgeData
    ) internal returns (uint256 boughtAmount) {
        (address barterRouter, bytes memory data) = abi.decode(bridgeData, (address, bytes));
        sellToken.approveIfBelow(barterRouter, sellAmount);

        (bool success, bytes memory resultData) = barterRouter.call(data);
        if (!success) {
            resultData.rrevert();
        }

        return abi.decode(resultData, (uint256));
    }
}

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

pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;

import "@0x/contracts-erc20/src/v06/LibERC20TokenV06.sol";
import "@0x/contracts-erc20/src/IERC20Token.sol";
import "@0x/contracts-erc20/src/IEtherToken.sol";
import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";

/// @dev Minimal CToken interface
interface ICToken {
    /// @dev deposits specified amount underlying tokens and mints cToken for the sender
    /// @param mintAmountInUnderlying amount of underlying tokens to deposit to mint cTokens
    /// @return status code of whether the mint was successful or not
    function mint(uint256 mintAmountInUnderlying) external returns (uint256);

    /// @dev redeems specified amount of cTokens and returns the underlying token to the sender
    /// @param redeemTokensInCtokens amount of cTokens to redeem for underlying collateral
    /// @return status code of whether the redemption was successful or not
    function redeem(uint256 redeemTokensInCtokens) external returns (uint256);
}

/// @dev Minimal CEther interface
interface ICEther {
    /// @dev deposits the amount of Ether sent as value and return mints cEther for the sender
    function mint() external payable;

    /// @dev redeems specified amount of cETH and returns the underlying ether to the sender
    /// @dev redeemTokensInCEther amount of cETH to redeem for underlying ether
    /// @return status code of whether the redemption was successful or not
    function redeem(uint256 redeemTokensInCEther) external returns (uint256);
}

contract MixinCompound {
    using LibERC20TokenV06 for IERC20Token;
    using LibSafeMathV06 for uint256;

    IEtherToken private immutable WETH;

    constructor(IEtherToken weth) public {
        WETH = weth;
    }

    uint256 private constant COMPOUND_SUCCESS_CODE = 0;

    function _tradeCompound(
        IERC20Token sellToken,
        IERC20Token buyToken,
        uint256 sellAmount,
        bytes memory bridgeData
    ) internal returns (uint256) {
        address cTokenAddress = abi.decode(bridgeData, (address));
        uint256 beforeBalance = buyToken.balanceOf(address(this));

        if (address(buyToken) == cTokenAddress) {
            if (address(sellToken) == address(WETH)) {
                // ETH/WETH -> cETH
                ICEther cETH = ICEther(cTokenAddress);
                // Compound expects ETH to be sent with mint call
                WETH.withdraw(sellAmount);
                // NOTE: cETH mint will revert on failure instead of returning a status code
                cETH.mint{value: sellAmount}();
            } else {
                sellToken.approveIfBelow(cTokenAddress, sellAmount);
                // Token -> cToken
                ICToken cToken = ICToken(cTokenAddress);
                require(cToken.mint(sellAmount) == COMPOUND_SUCCESS_CODE, "MixinCompound/FAILED_TO_MINT_CTOKEN");
            }
        } else if (address(sellToken) == cTokenAddress) {
            if (address(buyToken) == address(WETH)) {
                // cETH -> ETH/WETH
                uint256 etherBalanceBefore = address(this).balance;
                ICEther cETH = ICEther(cTokenAddress);
                require(cETH.redeem(sellAmount) == COMPOUND_SUCCESS_CODE, "MixinCompound/FAILED_TO_REDEEM_CETHER");
                uint256 etherBalanceAfter = address(this).balance;
                uint256 receivedEtherBalance = etherBalanceAfter.safeSub(etherBalanceBefore);
                WETH.deposit{value: receivedEtherBalance}();
            } else {
                ICToken cToken = ICToken(cTokenAddress);
                require(cToken.redeem(sellAmount) == COMPOUND_SUCCESS_CODE, "MixinCompound/FAILED_TO_REDEEM_CTOKEN");
            }
        }

        return buyToken.balanceOf(address(this)).safeSub(beforeBalance);
    }
}

File 17 of 35 : LibSafeMathV06.sol
// SPDX-License-Identifier: Apache-2.0
/*

  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.

*/

pragma solidity ^0.6.5;

import "./errors/LibRichErrorsV06.sol";
import "./errors/LibSafeMathRichErrorsV06.sol";

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

    function safeDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        if (b == 0) {
            LibRichErrorsV06.rrevert(
                LibSafeMathRichErrorsV06.Uint256BinOpError(
                    LibSafeMathRichErrorsV06.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) {
            LibRichErrorsV06.rrevert(
                LibSafeMathRichErrorsV06.Uint256BinOpError(
                    LibSafeMathRichErrorsV06.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) {
            LibRichErrorsV06.rrevert(
                LibSafeMathRichErrorsV06.Uint256BinOpError(
                    LibSafeMathRichErrorsV06.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;
    }

    function safeMul128(uint128 a, uint128 b) internal pure returns (uint128) {
        if (a == 0) {
            return 0;
        }
        uint128 c = a * b;
        if (c / a != b) {
            LibRichErrorsV06.rrevert(
                LibSafeMathRichErrorsV06.Uint256BinOpError(
                    LibSafeMathRichErrorsV06.BinOpErrorCodes.MULTIPLICATION_OVERFLOW,
                    a,
                    b
                )
            );
        }
        return c;
    }

    function safeDiv128(uint128 a, uint128 b) internal pure returns (uint128) {
        if (b == 0) {
            LibRichErrorsV06.rrevert(
                LibSafeMathRichErrorsV06.Uint256BinOpError(
                    LibSafeMathRichErrorsV06.BinOpErrorCodes.DIVISION_BY_ZERO,
                    a,
                    b
                )
            );
        }
        uint128 c = a / b;
        return c;
    }

    function safeSub128(uint128 a, uint128 b) internal pure returns (uint128) {
        if (b > a) {
            LibRichErrorsV06.rrevert(
                LibSafeMathRichErrorsV06.Uint256BinOpError(
                    LibSafeMathRichErrorsV06.BinOpErrorCodes.SUBTRACTION_UNDERFLOW,
                    a,
                    b
                )
            );
        }
        return a - b;
    }

    function safeAdd128(uint128 a, uint128 b) internal pure returns (uint128) {
        uint128 c = a + b;
        if (c < a) {
            LibRichErrorsV06.rrevert(
                LibSafeMathRichErrorsV06.Uint256BinOpError(
                    LibSafeMathRichErrorsV06.BinOpErrorCodes.ADDITION_OVERFLOW,
                    a,
                    b
                )
            );
        }
        return c;
    }

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

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

    function safeDowncastToUint128(uint256 a) internal pure returns (uint128) {
        if (a > type(uint128).max) {
            LibRichErrorsV06.rrevert(
                LibSafeMathRichErrorsV06.Uint256DowncastError(
                    LibSafeMathRichErrorsV06.DowncastErrorCodes.VALUE_TOO_LARGE_TO_DOWNCAST_TO_UINT128,
                    a
                )
            );
        }
        return uint128(a);
    }
}

File 18 of 35 : LibSafeMathRichErrorsV06.sol
// SPDX-License-Identifier: Apache-2.0
/*

  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.

*/

pragma solidity ^0.6.5;

library LibSafeMathRichErrorsV06 {
    // 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,
        VALUE_TOO_LARGE_TO_DOWNCAST_TO_UINT128
    }

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

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

pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;

import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
import "@0x/contracts-erc20/src/IEtherToken.sol";
import "@0x/contracts-erc20/src/v06/LibERC20TokenV06.sol";
import "@0x/contracts-erc20/src/IERC20Token.sol";
import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";

contract MixinCurve {
    using LibERC20TokenV06 for IERC20Token;
    using LibSafeMathV06 for uint256;
    using LibRichErrorsV06 for bytes;

    /// @dev Mainnet address of the WETH contract.
    IEtherToken private immutable WETH;

    constructor(IEtherToken weth) public {
        WETH = weth;
    }

    struct CurveBridgeData {
        address curveAddress;
        bytes4 exchangeFunctionSelector;
        int128 fromCoinIdx;
        int128 toCoinIdx;
    }

    function _tradeCurve(
        IERC20Token sellToken,
        IERC20Token buyToken,
        uint256 sellAmount,
        bytes memory bridgeData
    ) internal returns (uint256 boughtAmount) {
        // Decode the bridge data to get the Curve metadata.
        CurveBridgeData memory data = abi.decode(bridgeData, (CurveBridgeData));
        uint256 payableAmount;
        if (sellToken == WETH) {
            payableAmount = sellAmount;
            WETH.withdraw(sellAmount);
        } else {
            sellToken.approveIfBelow(data.curveAddress, sellAmount);
        }

        uint256 beforeBalance = buyToken.balanceOf(address(this));
        (bool success, bytes memory resultData) = data.curveAddress.call{value: payableAmount}(
            abi.encodeWithSelector(
                data.exchangeFunctionSelector,
                data.fromCoinIdx,
                data.toCoinIdx,
                // dx
                sellAmount,
                // min dy
                1
            )
        );
        if (!success) {
            resultData.rrevert();
        }

        if (buyToken == WETH) {
            boughtAmount = address(this).balance;
            WETH.deposit{value: boughtAmount}();
        }

        return buyToken.balanceOf(address(this)).safeSub(beforeBalance);
    }
}

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

pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;

import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
import "@0x/contracts-erc20/src/v06/LibERC20TokenV06.sol";
import "@0x/contracts-erc20/src/IERC20Token.sol";
import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";

contract MixinCurveV2 {
    using LibERC20TokenV06 for IERC20Token;
    using LibSafeMathV06 for uint256;
    using LibRichErrorsV06 for bytes;

    struct CurveBridgeDataV2 {
        address curveAddress;
        bytes4 exchangeFunctionSelector;
        int128 fromCoinIdx;
        int128 toCoinIdx;
    }

    function _tradeCurveV2(
        IERC20Token sellToken,
        IERC20Token buyToken,
        uint256 sellAmount,
        bytes memory bridgeData
    ) internal returns (uint256 boughtAmount) {
        // Decode the bridge data to get the Curve metadata.
        CurveBridgeDataV2 memory data = abi.decode(bridgeData, (CurveBridgeDataV2));
        sellToken.approveIfBelow(data.curveAddress, sellAmount);

        uint256 beforeBalance = buyToken.balanceOf(address(this));
        (bool success, bytes memory resultData) = data.curveAddress.call(
            abi.encodeWithSelector(
                data.exchangeFunctionSelector,
                data.fromCoinIdx,
                data.toCoinIdx,
                // dx
                sellAmount,
                // min dy
                1
            )
        );
        if (!success) {
            resultData.rrevert();
        }

        return buyToken.balanceOf(address(this)).safeSub(beforeBalance);
    }
}

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

pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;

import "@0x/contracts-erc20/src/v06/LibERC20TokenV06.sol";
import "@0x/contracts-erc20/src/IERC20Token.sol";
import "./MixinUniswapV2.sol";

contract MixinCryptoCom {
    using LibERC20TokenV06 for IERC20Token;

    function _tradeCryptoCom(
        IERC20Token buyToken,
        uint256 sellAmount,
        bytes memory bridgeData
    ) internal returns (uint256 boughtAmount) {
        IUniswapV2Router02 router;
        IERC20Token[] memory path;
        {
            address[] memory _path;
            (router, _path) = abi.decode(bridgeData, (IUniswapV2Router02, address[]));
            // To get around `abi.decode()` not supporting interface array types.
            assembly {
                path := _path
            }
        }

        require(path.length >= 2, "MixinCryptoCom/PATH_LENGTH_MUST_BE_AT_LEAST_TWO");
        require(path[path.length - 1] == buyToken, "MixinCryptoCom/LAST_ELEMENT_OF_PATH_MUST_MATCH_OUTPUT_TOKEN");
        // Grant the CryptoCom router an allowance to sell the first token.
        path[0].approveIfBelow(address(router), sellAmount);

        uint256[] memory amounts = router.swapExactTokensForTokens(
            // Sell all tokens we hold.
            sellAmount,
            // Minimum buy amount.
            1,
            // Convert to `buyToken` along this path.
            path,
            // Recipient is `this`.
            address(this),
            // Expires after this block.
            block.timestamp
        );
        return amounts[amounts.length - 1];
    }
}

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

pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;

import "@0x/contracts-erc20/src/v06/LibERC20TokenV06.sol";
import "@0x/contracts-erc20/src/IERC20Token.sol";
import "../IBridgeAdapter.sol";

/*
    UniswapV2
*/
interface IUniswapV2Router02 {
    /// @dev Swaps an exact amount of input tokens for as many output tokens as possible, along the route determined by
    /// the path. The first element of path is the input token, the last is the output token, and any intermediate
    /// elements represent intermediate pairs to trade through (if, for example, a direct pair does not exist).
    /// @param amountIn The amount of input tokens to send.
    /// @param amountOutMin The minimum amount of output tokens that must be received for the transaction not to revert.
    /// @param path An array of token addresses. path.length must be >= 2. Pools for each consecutive pair of addresses
    /// must exist and have liquidity.
    /// @param to Recipient of the output tokens.
    /// @param deadline Unix timestamp after which the transaction will revert.
    /// @return amounts The input token amount and all subsequent output token amounts.
    function swapExactTokensForTokens(
        uint256 amountIn,
        uint256 amountOutMin,
        IERC20Token[] calldata path,
        address to,
        uint256 deadline
    ) external returns (uint256[] memory amounts);
}

contract MixinUniswapV2 {
    using LibERC20TokenV06 for IERC20Token;

    function _tradeUniswapV2(
        IERC20Token buyToken,
        uint256 sellAmount,
        bytes memory bridgeData
    ) internal returns (uint256 boughtAmount) {
        IUniswapV2Router02 router;
        IERC20Token[] memory path;
        {
            address[] memory _path;
            (router, _path) = abi.decode(bridgeData, (IUniswapV2Router02, address[]));
            // To get around `abi.decode()` not supporting interface array types.
            assembly {
                path := _path
            }
        }

        require(path.length >= 2, "MixinUniswapV2/PATH_LENGTH_MUST_BE_AT_LEAST_TWO");
        require(path[path.length - 1] == buyToken, "MixinUniswapV2/LAST_ELEMENT_OF_PATH_MUST_MATCH_OUTPUT_TOKEN");
        // Grant the Uniswap router an allowance to sell the first token.
        path[0].approveIfBelow(address(router), sellAmount);

        uint256[] memory amounts = router.swapExactTokensForTokens(
            // Sell all tokens we hold.
            sellAmount,
            // Minimum buy amount.
            1,
            // Convert to `buyToken` along this path.
            path,
            // Recipient is `this`.
            address(this),
            // Expires after this block.
            block.timestamp
        );
        return amounts[amounts.length - 1];
    }
}

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

pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;

import "@0x/contracts-erc20/src/v06/LibERC20TokenV06.sol";
import "@0x/contracts-erc20/src/IERC20Token.sol";
import "../IBridgeAdapter.sol";

interface IDODO {
    function sellBaseToken(uint256 amount, uint256 minReceiveQuote, bytes calldata data) external returns (uint256);

    function buyBaseToken(uint256 amount, uint256 maxPayQuote, bytes calldata data) external returns (uint256);
}

interface IDODOHelper {
    function querySellQuoteToken(IDODO dodo, uint256 amount) external view returns (uint256);
}

contract MixinDodo {
    using LibERC20TokenV06 for IERC20Token;

    function _tradeDodo(
        IERC20Token sellToken,
        uint256 sellAmount,
        bytes memory bridgeData
    ) internal returns (uint256 boughtAmount) {
        (IDODOHelper helper, IDODO pool, bool isSellBase) = abi.decode(bridgeData, (IDODOHelper, IDODO, bool));

        // Grant the Dodo pool contract an allowance to sell the first token.
        sellToken.approveIfBelow(address(pool), sellAmount);

        if (isSellBase) {
            // Sell the Base token directly against the contract
            boughtAmount = pool.sellBaseToken(
                // amount to sell
                sellAmount,
                // min receive amount
                1,
                new bytes(0)
            );
        } else {
            // Need to re-calculate the sell quote amount into buyBase
            boughtAmount = helper.querySellQuoteToken(pool, sellAmount);
            pool.buyBaseToken(
                // amount to buy
                boughtAmount,
                // max pay amount
                sellAmount,
                new bytes(0)
            );
        }

        return boughtAmount;
    }
}

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

pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;

import "@0x/contracts-erc20/src/v06/LibERC20TokenV06.sol";
import "@0x/contracts-erc20/src/IERC20Token.sol";
import "../IBridgeAdapter.sol";

interface IDODOV2 {
    function sellBase(address recipient) external returns (uint256);

    function sellQuote(address recipient) external returns (uint256);
}

contract MixinDodoV2 {
    using LibERC20TokenV06 for IERC20Token;

    function _tradeDodoV2(
        IERC20Token sellToken,
        uint256 sellAmount,
        bytes memory bridgeData
    ) internal returns (uint256 boughtAmount) {
        (IDODOV2 pool, bool isSellBase) = abi.decode(bridgeData, (IDODOV2, bool));

        // Transfer the tokens into the pool
        sellToken.compatTransfer(address(pool), sellAmount);

        boughtAmount = isSellBase ? pool.sellBase(address(this)) : pool.sellQuote(address(this));
    }
}

File 25 of 35 : MixinKyberDmm.sol
// SPDX-License-Identifier: Apache-2.0
/*

  Copyright 2021 ZeroEx Intl.

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

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

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

*/

pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;

import "@0x/contracts-erc20/src/v06/LibERC20TokenV06.sol";
import "@0x/contracts-erc20/src/IERC20Token.sol";
import "../IBridgeAdapter.sol";

/*
    KyberDmm Router
*/
interface IKyberDmmRouter {
    /// @dev Swaps an exact amount of input tokens for as many output tokens as possible, along the route determined by
    /// the path. The first element of path is the input token, the last is the output token, and any intermediate
    /// elements represent intermediate pairs to trade through (if, for example, a direct pair does not exist).
    /// @param amountIn The amount of input tokens to send.
    /// @param amountOutMin The minimum amount of output tokens that must be received for the transaction not to revert.
    /// @param pools An array of pool addresses. pools.length must be >= 1.
    /// @param path An array of token addresses. path.length must be >= 2. Pools for each consecutive pair of addresses
    /// must exist and have liquidity.
    /// @param to Recipient of the output tokens.
    /// @param deadline Unix timestamp after which the transaction will revert.
    /// @return amounts The input token amount and all subsequent output token amounts.
    function swapExactTokensForTokens(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata pools,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external returns (uint256[] memory amounts);
}

contract MixinKyberDmm {
    using LibERC20TokenV06 for IERC20Token;

    function _tradeKyberDmm(
        IERC20Token buyToken,
        uint256 sellAmount,
        bytes memory bridgeData
    ) internal returns (uint256 boughtAmount) {
        address router;
        address[] memory pools;
        address[] memory path;
        (router, pools, path) = abi.decode(bridgeData, (address, address[], address[]));

        require(pools.length >= 1, "MixinKyberDmm/POOLS_LENGTH_MUST_BE_AT_LEAST_ONE");
        require(path.length == pools.length + 1, "MixinKyberDmm/ARRAY_LENGTH_MISMATCH");
        require(
            path[path.length - 1] == address(buyToken),
            "MixinKyberDmm/LAST_ELEMENT_OF_PATH_MUST_MATCH_OUTPUT_TOKEN"
        );
        // Grant the KyberDmm router an allowance to sell the first token.
        IERC20Token(path[0]).approveIfBelow(address(router), sellAmount);

        uint256[] memory amounts = IKyberDmmRouter(router).swapExactTokensForTokens(
            // Sell all tokens we hold.
            sellAmount,
            // Minimum buy amount.
            1,
            pools,
            // Convert to `buyToken` along this path.
            path,
            // Recipient is `this`.
            address(this),
            // Expires after this block.
            block.timestamp
        );
        return amounts[amounts.length - 1];
    }
}

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

pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;

import "@0x/contracts-erc20/src/v06/LibERC20TokenV06.sol";
import "@0x/contracts-erc20/src/IERC20Token.sol";

interface IKyberElasticRouter {
    struct ExactInputParams {
        bytes path;
        address recipient;
        uint256 deadline;
        uint256 amountIn;
        uint256 minAmountOut;
    }

    function swapExactInput(ExactInputParams calldata params) external payable returns (uint256 amountOut);
}

contract MixinKyberElastic {
    using LibERC20TokenV06 for IERC20Token;

    function _tradeKyberElastic(
        IERC20Token sellToken,
        uint256 sellAmount,
        bytes memory bridgeData
    ) internal returns (uint256 boughtAmount) {
        (IKyberElasticRouter router, bytes memory path) = abi.decode(bridgeData, (IKyberElasticRouter, bytes));

        // Grant the Kyber router an allowance to sell the sell token.
        sellToken.approveIfBelow(address(router), sellAmount);

        boughtAmount = router.swapExactInput(
            IKyberElasticRouter.ExactInputParams({
                path: path,
                recipient: address(this),
                deadline: block.timestamp,
                amountIn: sellAmount,
                minAmountOut: 1
            })
        );
    }
}

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

pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;

import "@0x/contracts-erc20/src/v06/LibERC20TokenV06.sol";
import "@0x/contracts-erc20/src/IERC20Token.sol";
import "@0x/contracts-erc20/src/IEtherToken.sol";

/// @dev Minimal interface for minting StETH
interface IStETH {
    /// @dev Adds eth to the pool
    /// @param _referral optional address for referrals
    /// @return StETH Amount of shares generated
    function submit(address _referral) external payable returns (uint256 StETH);

    /// @dev Retrieve the current pooled ETH representation of the shares amount
    /// @param _sharesAmount amount of shares
    /// @return amount of pooled ETH represented by the shares amount
    function getPooledEthByShares(uint256 _sharesAmount) external view returns (uint256);
}

/// @dev Minimal interface for wrapping/unwrapping stETH.
interface IWstETH {
    /**
     * @notice Exchanges stETH to wstETH
     * @param _stETHAmount amount of stETH to wrap in exchange for wstETH
     * @dev Requirements:
     *  - `_stETHAmount` must be non-zero
     *  - msg.sender must approve at least `_stETHAmount` stETH to this
     *    contract.
     *  - msg.sender must have at least `_stETHAmount` of stETH.
     * User should first approve _stETHAmount to the WstETH contract
     * @return Amount of wstETH user receives after wrap
     */
    function wrap(uint256 _stETHAmount) external returns (uint256);

    /**
     * @notice Exchanges wstETH to stETH
     * @param _wstETHAmount amount of wstETH to uwrap in exchange for stETH
     * @dev Requirements:
     *  - `_wstETHAmount` must be non-zero
     *  - msg.sender must have at least `_wstETHAmount` wstETH.
     * @return Amount of stETH user receives after unwrap
     */
    function unwrap(uint256 _wstETHAmount) external returns (uint256);
}

contract MixinLido {
    using LibERC20TokenV06 for IERC20Token;
    using LibERC20TokenV06 for IEtherToken;

    IEtherToken private immutable WETH;

    constructor(IEtherToken weth) public {
        WETH = weth;
    }

    function _tradeLido(
        IERC20Token sellToken,
        IERC20Token buyToken,
        uint256 sellAmount,
        bytes memory bridgeData
    ) internal returns (uint256 boughtAmount) {
        if (address(sellToken) == address(WETH)) {
            return _tradeStETH(buyToken, sellAmount, bridgeData);
        }

        return _tradeWstETH(sellToken, buyToken, sellAmount, bridgeData);
    }

    function _tradeStETH(
        IERC20Token buyToken,
        uint256 sellAmount,
        bytes memory bridgeData
    ) private returns (uint256 boughtAmount) {
        IStETH stETH = abi.decode(bridgeData, (IStETH));
        if (address(buyToken) == address(stETH)) {
            WETH.withdraw(sellAmount);
            return stETH.getPooledEthByShares(stETH.submit{value: sellAmount}(address(0)));
        }

        revert("MixinLido/UNSUPPORTED_TOKEN_PAIR");
    }

    function _tradeWstETH(
        IERC20Token sellToken,
        IERC20Token buyToken,
        uint256 sellAmount,
        bytes memory bridgeData
    ) private returns (uint256 boughtAmount) {
        (IEtherToken stETH, IWstETH wstETH) = abi.decode(bridgeData, (IEtherToken, IWstETH));
        if (address(sellToken) == address(stETH) && address(buyToken) == address(wstETH)) {
            sellToken.approveIfBelow(address(wstETH), sellAmount);
            return wstETH.wrap(sellAmount);
        }
        if (address(sellToken) == address(wstETH) && address(buyToken) == address(stETH)) {
            return wstETH.unwrap(sellAmount);
        }

        revert("MixinLido/UNSUPPORTED_TOKEN_PAIR");
    }
}

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

pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;

import "@0x/contracts-erc20/src/v06/LibERC20TokenV06.sol";
import "@0x/contracts-erc20/src/IERC20Token.sol";
import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";

interface IPSM {
    // @dev Get the fee for selling USDC to DAI in PSM
    // @return tin toll in [wad]
    function tin() external view returns (uint256);

    // @dev Get the fee for selling DAI to USDC in PSM
    // @return tout toll out [wad]
    function tout() external view returns (uint256);

    // @dev Get the address of the PSM state Vat
    // @return address of the Vat
    function vat() external view returns (address);

    // @dev Get the address of the underlying vault powering PSM
    // @return address of gemJoin contract
    function gemJoin() external view returns (address);

    // @dev Sell USDC for DAI
    // @param usr The address of the account trading USDC for DAI.
    // @param gemAmt The amount of USDC to sell in USDC base units
    function sellGem(address usr, uint256 gemAmt) external;

    // @dev Buy USDC for DAI
    // @param usr The address of the account trading DAI for USDC
    // @param gemAmt The amount of USDC to buy in USDC base units
    function buyGem(address usr, uint256 gemAmt) external;
}

contract MixinMakerPSM {
    using LibERC20TokenV06 for IERC20Token;
    using LibSafeMathV06 for uint256;

    struct MakerPsmBridgeData {
        address psmAddress;
        address gemTokenAddres;
    }

    // Maker units
    // wad: fixed point decimal with 18 decimals (for basic quantities, e.g. balances)
    uint256 private constant WAD = 10 ** 18;
    // ray: fixed point decimal with 27 decimals (for precise quantites, e.g. ratios)
    uint256 private constant RAY = 10 ** 27;
    // rad: fixed point decimal with 45 decimals (result of integer multiplication with a wad and a ray)
    uint256 private constant RAD = 10 ** 45;

    // See https://github.com/makerdao/dss/blob/master/DEVELOPING.md

    function _tradeMakerPsm(
        IERC20Token sellToken,
        IERC20Token buyToken,
        uint256 sellAmount,
        bytes memory bridgeData
    ) internal returns (uint256 boughtAmount) {
        // Decode the bridge data.
        MakerPsmBridgeData memory data = abi.decode(bridgeData, (MakerPsmBridgeData));
        uint256 beforeBalance = buyToken.balanceOf(address(this));

        IPSM psm = IPSM(data.psmAddress);

        if (address(sellToken) == data.gemTokenAddres) {
            sellToken.approveIfBelow(psm.gemJoin(), sellAmount);

            psm.sellGem(address(this), sellAmount);
        } else if (address(buyToken) == data.gemTokenAddres) {
            uint256 feeDivisor = WAD.safeAdd(psm.tout()); // eg. 1.001 * 10 ** 18 with 0.1% fee [tout is in wad];
            uint256 buyTokenBaseUnit = uint256(10) ** uint256(buyToken.decimals());
            uint256 gemAmount = sellAmount.safeMul(buyTokenBaseUnit).safeDiv(feeDivisor);

            sellToken.approveIfBelow(data.psmAddress, sellAmount);
            psm.buyGem(address(this), gemAmount);
        }

        return buyToken.balanceOf(address(this)).safeSub(beforeBalance);
    }
}

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

pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;

import "@0x/contracts-erc20/src/v06/LibERC20TokenV06.sol";
import "@0x/contracts-erc20/src/IERC20Token.sol";

interface IMaverickV1Router {
    struct ExactInputSingleParams {
        address tokenIn;
        address tokenOut;
        address pool;
        address recipient;
        uint256 deadline;
        uint256 amountIn;
        uint256 amountOutMinimum;
        uint256 sqrtPriceLimitD18;
    }

    function exactInputSingle(ExactInputSingleParams calldata params) external payable returns (uint256 amountOut);
}

contract MixinMaverickV1 {
    using LibERC20TokenV06 for IERC20Token;

    function _tradeMaverickV1(
        IERC20Token sellToken,
        IERC20Token buyToken,
        uint256 sellAmount,
        bytes memory bridgeData
    ) internal returns (uint256 boughtAmount) {
        (IMaverickV1Router router, address pool) = abi.decode(bridgeData, (IMaverickV1Router, address));

        // Grant the MaverickV1 router an allowance to sell the sellToken
        sellToken.approveIfBelow(address(router), sellAmount);

        boughtAmount = router.exactInputSingle(
            IMaverickV1Router.ExactInputSingleParams({
                tokenIn: address(sellToken),
                tokenOut: address(buyToken),
                pool: pool,
                recipient: address(this),
                deadline: block.timestamp,
                amountIn: sellAmount,
                amountOutMinimum: 1,
                sqrtPriceLimitD18: 0
            })
        );
    }
}

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

pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;

import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
import "@0x/contracts-erc20/src/v06/LibERC20TokenV06.sol";
import "@0x/contracts-erc20/src/IERC20Token.sol";
import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";

contract MixinNerve {
    using LibERC20TokenV06 for IERC20Token;
    using LibSafeMathV06 for uint256;
    using LibRichErrorsV06 for bytes;

    struct NerveBridgeData {
        address pool;
        bytes4 exchangeFunctionSelector;
        int128 fromCoinIdx;
        int128 toCoinIdx;
    }

    function _tradeNerve(
        IERC20Token sellToken,
        uint256 sellAmount,
        bytes memory bridgeData
    ) internal returns (uint256 boughtAmount) {
        // Basically a Curve fork but the swap option has a deadline

        // Decode the bridge data to get the Curve metadata.
        NerveBridgeData memory data = abi.decode(bridgeData, (NerveBridgeData));
        sellToken.approveIfBelow(data.pool, sellAmount);
        (bool success, bytes memory resultData) = data.pool.call(
            abi.encodeWithSelector(
                data.exchangeFunctionSelector,
                data.fromCoinIdx,
                data.toCoinIdx,
                // dx
                sellAmount,
                // min dy
                1,
                // deadline
                block.timestamp
            )
        );
        if (!success) {
            resultData.rrevert();
        }
        return abi.decode(resultData, (uint256));
    }
}

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

pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;

interface ISynthetix {
    // Ethereum Mainnet
    function exchangeAtomically(
        bytes32 sourceCurrencyKey,
        uint256 sourceAmount,
        bytes32 destinationCurrencyKey,
        bytes32 trackingCode,
        uint256 minAmount
    ) external returns (uint256 amountReceived);

    // Optimism
    function exchangeWithTracking(
        bytes32 sourceCurrencyKey,
        uint256 sourceAmount,
        bytes32 destinationCurrencyKey,
        address rewardAddress,
        bytes32 trackingCode
    ) external returns (uint256 amountReceived);
}

contract MixinSynthetix {
    // solhint-disable-next-line const-name-snakecase
    address private constant rewardAddress = 0x5C80239D97E1eB216b5c3D8fBa5DE5Be5d38e4C9;
    // solhint-disable-next-line const-name-snakecase
    bytes32 constant trackingCode = 0x3058000000000000000000000000000000000000000000000000000000000000;

    function _tradeSynthetix(uint256 sellAmount, bytes memory bridgeData) public returns (uint256 boughtAmount) {
        (ISynthetix synthetix, bytes32 sourceCurrencyKey, bytes32 destinationCurrencyKey) = abi.decode(
            bridgeData,
            (ISynthetix, bytes32, bytes32)
        );

        boughtAmount = exchange(synthetix, sourceCurrencyKey, destinationCurrencyKey, sellAmount);
    }

    function exchange(
        ISynthetix synthetix,
        bytes32 sourceCurrencyKey,
        bytes32 destinationCurrencyKey,
        uint256 sellAmount
    ) internal returns (uint256 boughtAmount) {
        uint256 chainId;
        assembly {
            chainId := chainid()
        }

        if (chainId == 1) {
            boughtAmount = synthetix.exchangeAtomically(
                sourceCurrencyKey,
                sellAmount,
                destinationCurrencyKey,
                trackingCode,
                0
            );
        } else {
            boughtAmount = synthetix.exchangeWithTracking(
                sourceCurrencyKey,
                sellAmount,
                destinationCurrencyKey,
                rewardAddress,
                trackingCode
            );
        }
    }
}

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

pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;

import "@0x/contracts-erc20/src/v06/LibERC20TokenV06.sol";
import "@0x/contracts-erc20/src/IERC20Token.sol";
import "@0x/contracts-erc20/src/IEtherToken.sol";
import "../IBridgeAdapter.sol";

interface IUniswapExchangeFactory {
    /// @dev Get the exchange for a token.
    /// @param token The token contract.
    function getExchange(IERC20Token token) external view returns (IUniswapExchange exchange);
}

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 tokensSold Amount of tokens to sell.
    /// @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 buyToken The token being bought.
    /// @return tokensBought Amount of tokens bought.
    function tokenToTokenTransferInput(
        uint256 tokensSold,
        uint256 minTokensBought,
        uint256 minEthBought,
        uint256 deadline,
        address recipient,
        IERC20Token buyToken
    ) external returns (uint256 tokensBought);

    /// @dev Buys at least `minTokensBought` tokens with the exchange token.
    /// @param tokensSold Amount of tokens to sell.
    /// @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 buyToken The token being bought.
    /// @return tokensBought Amount of tokens bought.
    function tokenToTokenSwapInput(
        uint256 tokensSold,
        uint256 minTokensBought,
        uint256 minEthBought,
        uint256 deadline,
        IERC20Token buyToken
    ) external returns (uint256 tokensBought);
}

contract MixinUniswap {
    using LibERC20TokenV06 for IERC20Token;

    /// @dev Mainnet address of the WETH contract.
    IEtherToken private immutable WETH;

    constructor(IEtherToken weth) public {
        WETH = weth;
    }

    //B7solhint-disable-next-lineB7function-max-lines
    function _tradeUniswap(
        IERC20Token sellToken,
        IERC20Token buyToken,
        uint256 sellAmount,
        bytes memory bridgeData
    ) internal returns (uint256 boughtAmount) {
        IUniswapExchangeFactory exchangeFactory = abi.decode(bridgeData, (IUniswapExchangeFactory));

        // Get the exchange for the token pair.
        IUniswapExchange exchange = _getUniswapExchangeForTokenPair(exchangeFactory, sellToken, buyToken);

        // Convert from WETH to a token.
        if (sellToken == WETH) {
            // Unwrap the WETH.
            WETH.withdraw(sellAmount);
            // Buy as much of `buyToken` token with ETH as possible
            boughtAmount = exchange.ethToTokenTransferInput{value: sellAmount}(
                // Minimum buy amount.
                1,
                // Expires after this block.
                block.timestamp,
                // Recipient is `this`.
                address(this)
            );

            // Convert from a token to WETH.
        } else if (buyToken == WETH) {
            // Grant the exchange an allowance.
            sellToken.approveIfBelow(address(exchange), sellAmount);
            // Buy as much ETH with `sellToken` token as possible.
            boughtAmount = exchange.tokenToEthSwapInput(
                // Sell all tokens we hold.
                sellAmount,
                // Minimum buy amount.
                1,
                // Expires after this block.
                block.timestamp
            );
            // Wrap the ETH.
            WETH.deposit{value: boughtAmount}();
            // Convert from one token to another.
        } else {
            // Grant the exchange an allowance.
            sellToken.approveIfBelow(address(exchange), sellAmount);
            // Buy as much `buyToken` token with `sellToken` token
            boughtAmount = exchange.tokenToTokenSwapInput(
                // Sell all tokens we hold.
                sellAmount,
                // Minimum buy amount.
                1,
                // Must buy at least 1 intermediate wei of ETH.
                1,
                // Expires after this block.
                block.timestamp,
                // Convert to `buyToken`.
                buyToken
            );
        }

        return boughtAmount;
    }

    /// @dev Retrieves the uniswap exchange for a given token pair.
    ///      In the case of a WETH-token exchange, this will be the non-WETH token.
    ///      In th ecase of a token-token exchange, this will be the first token.
    /// @param exchangeFactory The exchange factory.
    /// @param sellToken The address of the token we are converting from.
    /// @param buyToken The address of the token we are converting to.
    /// @return exchange The uniswap exchange.
    function _getUniswapExchangeForTokenPair(
        IUniswapExchangeFactory exchangeFactory,
        IERC20Token sellToken,
        IERC20Token buyToken
    ) private view returns (IUniswapExchange exchange) {
        // Whichever isn't WETH is the exchange token.
        exchange = sellToken == WETH ? exchangeFactory.getExchange(buyToken) : exchangeFactory.getExchange(sellToken);
        require(address(exchange) != address(0), "MixinUniswap/NO_EXCHANGE");
    }
}

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

pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;

import "@0x/contracts-erc20/src/v06/LibERC20TokenV06.sol";
import "@0x/contracts-erc20/src/IERC20Token.sol";
import "../IBridgeAdapter.sol";

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

    function exactInput(ExactInputParams memory params) external payable returns (uint256 amountOut);
}

// https://github.com/Uniswap/swap-router-contracts/blob/main/contracts/interfaces/IV3SwapRouter.sol
interface IUniswapV3Router2 {
    struct ExactInputParams {
        bytes path;
        address recipient;
        uint256 amountIn;
        uint256 amountOutMinimum;
    }

    function exactInput(ExactInputParams memory params) external payable returns (uint256 amountOut);
}

contract MixinUniswapV3 {
    using LibERC20TokenV06 for IERC20Token;

    function _tradeUniswapV3(
        IERC20Token sellToken,
        uint256 sellAmount,
        bytes memory bridgeData
    ) internal returns (uint256 boughtAmount) {
        (address router, bytes memory path, uint256 routerVersion) = abi.decode(bridgeData, (address, bytes, uint256));

        // Grant the Uniswap router an allowance to sell the sell token.
        sellToken.approveIfBelow(router, sellAmount);

        if (routerVersion != 2) {
            boughtAmount = IUniswapV3Router(router).exactInput(
                IUniswapV3Router.ExactInputParams({
                    path: path,
                    recipient: address(this),
                    deadline: block.timestamp,
                    amountIn: sellAmount,
                    amountOutMinimum: 1
                })
            );
        } else {
            boughtAmount = IUniswapV3Router2(router).exactInput(
                IUniswapV3Router2.ExactInputParams({
                    path: path,
                    recipient: address(this),
                    amountIn: sellAmount,
                    amountOutMinimum: 1
                })
            );
        }
    }
}

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

pragma solidity ^0.6.5;

import "@0x/contracts-erc20/src/v06/LibERC20TokenV06.sol";
import "@0x/contracts-erc20/src/IERC20Token.sol";
import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";
import "../../../vendor/ILiquidityProvider.sol";

contract MixinZeroExBridge {
    using LibERC20TokenV06 for IERC20Token;
    using LibSafeMathV06 for uint256;

    function _tradeZeroExBridge(
        IERC20Token sellToken,
        IERC20Token buyToken,
        uint256 sellAmount,
        bytes memory bridgeData
    ) internal returns (uint256 boughtAmount) {
        (ILiquidityProvider provider, bytes memory lpData) = abi.decode(bridgeData, (ILiquidityProvider, bytes));
        // Trade the good old fashioned way
        sellToken.compatTransfer(address(provider), sellAmount);
        boughtAmount = provider.sellTokenForToken(
            sellToken,
            buyToken,
            address(this), // recipient
            1, // minBuyAmount
            lpData
        );
    }
}

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

pragma solidity ^0.6.5;

import "@0x/contracts-erc20/src/IERC20Token.sol";

interface ILiquidityProvider {
    /// @dev An optional event an LP can emit for each fill against a source.
    /// @param inputToken The input token.
    /// @param outputToken The output token.
    /// @param inputTokenAmount How much input token was sold.
    /// @param outputTokenAmount How much output token was bought.
    /// @param sourceId A bytes32 encoded ascii source ID. E.g., `bytes32('Curve')`/
    /// @param sourceAddress An optional address associated with the source (e.g, a curve pool).
    /// @param sourceId A bytes32 encoded ascii source ID. E.g., `bytes32('Curve')`/
    /// @param sourceAddress An optional address associated with the source (e.g, a curve pool).
    /// @param sender The caller of the LP.
    /// @param recipient The recipient of the output tokens.
    event LiquidityProviderFill(
        IERC20Token inputToken,
        IERC20Token outputToken,
        uint256 inputTokenAmount,
        uint256 outputTokenAmount,
        bytes32 sourceId,
        address sourceAddress,
        address sender,
        address recipient
    );

    /// @dev Trades `inputToken` for `outputToken`. The amount of `inputToken`
    ///      to sell must be transferred to the contract prior to calling this
    ///      function to trigger the trade.
    /// @param inputToken The token being sold.
    /// @param outputToken The token being bought.
    /// @param recipient The recipient of the bought tokens.
    /// @param minBuyAmount The minimum acceptable amount of `outputToken` to buy.
    /// @param auxiliaryData Arbitrary auxiliary data supplied to the contract.
    /// @return boughtAmount The amount of `outputToken` bought.
    function sellTokenForToken(
        IERC20Token inputToken,
        IERC20Token outputToken,
        address recipient,
        uint256 minBuyAmount,
        bytes calldata auxiliaryData
    ) external returns (uint256 boughtAmount);

    /// @dev Trades ETH for token. ETH must either be attached to this function
    ///      call or sent to the contract prior to calling this function to
    ///      trigger the trade.
    /// @param outputToken The token being bought.
    /// @param recipient The recipient of the bought tokens.
    /// @param minBuyAmount The minimum acceptable amount of `outputToken` to buy.
    /// @param auxiliaryData Arbitrary auxiliary data supplied to the contract.
    /// @return boughtAmount The amount of `outputToken` bought.
    function sellEthForToken(
        IERC20Token outputToken,
        address recipient,
        uint256 minBuyAmount,
        bytes calldata auxiliaryData
    ) external payable returns (uint256 boughtAmount);

    /// @dev Trades token for ETH. The token must be sent to the contract prior
    ///      to calling this function to trigger the trade.
    /// @param inputToken The token being sold.
    /// @param recipient The recipient of the bought tokens.
    /// @param minBuyAmount The minimum acceptable amount of ETH to buy.
    /// @param auxiliaryData Arbitrary auxiliary data supplied to the contract.
    /// @return boughtAmount The amount of ETH bought.
    function sellTokenForEth(
        IERC20Token inputToken,
        address payable recipient,
        uint256 minBuyAmount,
        bytes calldata auxiliaryData
    ) external returns (uint256 boughtAmount);

    /// @dev Quotes the amount of `outputToken` that would be obtained by
    ///      selling `sellAmount` of `inputToken`.
    /// @param inputToken Address of the taker token (what to sell). Use
    ///        the wETH address if selling ETH.
    /// @param outputToken Address of the maker token (what to buy). Use
    ///        the wETH address if buying ETH.
    /// @param sellAmount Amount of `inputToken` to sell.
    /// @return outputTokenAmount Amount of `outputToken` that would be obtained.
    function getSellQuote(
        IERC20Token inputToken,
        IERC20Token outputToken,
        uint256 sellAmount
    ) external view returns (uint256 outputTokenAmount);
}

Settings
{
  "remappings": [
    "@0x/contracts-erc20=/Users/sav/Repos/protocol/node_modules/@0x/contracts-erc20",
    "@0x/contracts-utils=/Users/sav/Repos/protocol/node_modules/@0x/contracts-utils"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 1000000,
    "details": {
      "yul": true,
      "deduplicate": true,
      "cse": true,
      "constantOptimizer": true
    }
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "istanbul"
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"contract IEtherToken","name":"weth","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"source","type":"bytes32"},{"indexed":false,"internalType":"contract IERC20Token","name":"inputToken","type":"address"},{"indexed":false,"internalType":"contract IERC20Token","name":"outputToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"inputTokenAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"outputTokenAmount","type":"uint256"}],"name":"BridgeFill","type":"event"},{"inputs":[],"name":"BANCORV3_ETH_ADDRESS","outputs":[{"internalType":"contract IERC20Token","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"sellAmount","type":"uint256"},{"internalType":"bytes","name":"bridgeData","type":"bytes"}],"name":"_tradeSynthetix","outputs":[{"internalType":"uint256","name":"boughtAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"source","type":"bytes32"}],"name":"isSupportedSource","outputs":[{"internalType":"bool","name":"isSupported","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"source","type":"bytes32"},{"internalType":"uint256","name":"takerTokenAmount","type":"uint256"},{"internalType":"uint256","name":"makerTokenAmount","type":"uint256"},{"internalType":"bytes","name":"bridgeData","type":"bytes"}],"internalType":"struct IBridgeAdapter.BridgeOrder","name":"order","type":"tuple"},{"internalType":"contract IERC20Token","name":"sellToken","type":"address"},{"internalType":"contract IERC20Token","name":"buyToken","type":"address"},{"internalType":"uint256","name":"sellAmount","type":"uint256"}],"name":"trade","outputs":[{"internalType":"uint256","name":"boughtAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"}]

6101206040523480156200001257600080fd5b5060405162005c5938038062005c59833981016040819052620000359162000138565b8081828384600160405180604001604052806008815260200167457468657265756d60c01b8152506000469050600081610539148062000076575081617a69145b80620000825750816005145b806200009057508162013881145b806200009e57508162014a33145b9050838214158015620000af575080155b15620000f95782604051602001620000c8919062000168565b60408051601f198184030181529082905262461bcd60e51b8252620000f091600401620001bd565b60405180910390fd5b5050506001600160601b0319606092831b811660805292821b831660a0525091821b811660c05291811b821660e0529190911b16610100525062000225565b6000602082840312156200014a578081fd5b81516001600160a01b038116811462000161578182fd5b9392505050565b600082516200017c818460208701620001f2565b7f427269646765416461707465722e636f6e7374727563746f723a2077726f6e67920191825250680818da185a5b88125160ba1b6020820152602901919050565b6000602082528251806020840152620001de816040850160208701620001f2565b601f01601f19169190910160400192915050565b60005b838110156200020f578181015183820152602001620001f5565b838111156200021f576000848401525b50505050565b60805160601c60a05160601c60c05160601c60e05160601c6101005160601c61599a620002bf600039806113d35280611462528061157d528061169d52806139ca5250806128c65280613d605250806108e652806109785280610bd45280610c2b525080612c315280612cc25280612ece5280613013525080613298528061333b528061341a52806135a552806135f9525061599a6000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c806302d2734f1461005157806365f53ee61461007a57806399a4d2d01461008f578063f712a148146100af575b600080fd5b61006461005f366004614738565b6100c2565b6040516100719190614dc6565b60405180910390f35b6100826100e8565b6040516100719190614ca9565b6100a261009d366004614a65565b610100565b6040516100719190615740565b6100a26100bd36600461493a565b610136565b60006100cc6142ec565b82815260006100df828280806001610150565b95945050505050565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee81565b6000806000808480602001905181019061011a9190614904565b92509250925061012c838383896106f6565b9695505050505050565b6000610146858585856000610150565b5095945050505050565b8451600090819060801c600181141561018e578315610177576000600192509250506106ec565b6101878787878b606001516108c2565b92506106aa565b6fffffffffffffffffffffffffffffffff8116601414156101cd5783156101bd576000600192509250506106ec565b6101878787878b60600151610d4e565b6fffffffffffffffffffffffffffffffff81166012141561020b5783156101fc576000600192509250506106ec565b61018787868a60600151610fa6565b6fffffffffffffffffffffffffffffffff81166002141561024957831561023a576000600192509250506106ec565b61018786868a6060015161118f565b6fffffffffffffffffffffffffffffffff811660031415610288578315610278576000600192509250506106ec565b6101878787878b606001516113a9565b6fffffffffffffffffffffffffffffffff8116600414156102c75783156102b7576000600192509250506106ec565b6101878787878b606001516117fc565b6fffffffffffffffffffffffffffffffff8116601914156103045783156102f6576000600192509250506106ec565b61018785896060015161190d565b6fffffffffffffffffffffffffffffffff811660101415610343578315610333576000600192509250506106ec565b6101878787878b60600151611b8a565b6fffffffffffffffffffffffffffffffff8116600a1415610381578315610372576000600192509250506106ec565b61018787868a60600151611fee565b6fffffffffffffffffffffffffffffffff8116600b14156103bf5783156103b0576000600192509250506106ec565b61018787868a60600151612201565b6fffffffffffffffffffffffffffffffff8116600c14156103fd5783156103ee576000600192509250506106ec565b61018786868a60600151612392565b6fffffffffffffffffffffffffffffffff8116600f141561043b57831561042c576000600192509250506106ec565b61018787868a6060015161246b565b6fffffffffffffffffffffffffffffffff81166013141561047957831561046a576000600192509250506106ec565b61018786868a606001516125e8565b6fffffffffffffffffffffffffffffffff8116602114156104b75783156104a8576000600192509250506106ec565b61018787868a6060015161280d565b6fffffffffffffffffffffffffffffffff8116601514156104f65783156104e6576000600192509250506106ec565b6101878787878b606001516128c2565b6fffffffffffffffffffffffffffffffff811660171415610535578315610525576000600192509250506106ec565b6101878787878b60600151612936565b6fffffffffffffffffffffffffffffffff811660181415610574578315610564576000600192509250506106ec565b6101878787878b60600151612b55565b6fffffffffffffffffffffffffffffffff8116601c14156105b25783156105a3576000600192509250506106ec565b61018786868a606001516131ba565b6fffffffffffffffffffffffffffffffff8116601e14156105ef5783156105e1576000600192509250506106ec565b610187858960600151610100565b6fffffffffffffffffffffffffffffffff81166022141561062d57831561061e576000600192509250506106ec565b61018787868a60600151613684565b6fffffffffffffffffffffffffffffffff81166025141561066c57831561065c576000600192509250506106ec565b6101878787878b60600151613755565b6fffffffffffffffffffffffffffffffff81166106aa578315610697576000600192509250506106ec565b6106a78787878b60600151613824565b92505b87516040517fe59e71a14fe90157eedc866c4f8c767d3943d6b6b2e8cd64dddcc92ab4c55af8916106e2918a908a908a908990614dd1565b60405180910390a1505b9550959350505050565b60004660018114156107d6576040517f44b3e92300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8716906344b3e9239061077d908890879089907f305800000000000000000000000000000000000000000000000000000000000090600090600401614e46565b602060405180830381600087803b15801561079757600080fd5b505af11580156107ab573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107cf9190614a4d565b91506108b9565b6040517f30ead76000000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8716906330ead7609061086490889087908990735c80239d97e1eb216b5c3d8fba5de5be5d38e4c9907f305800000000000000000000000000000000000000000000000000000000000090600401614e0d565b602060405180830381600087803b15801561087e57600080fd5b505af1158015610892573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108b69190614a4d565b91505b50949350505050565b60006108cc614317565b828060200190518101906108e091906149f0565b905060007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff1614156109e457506040517f2e1a7d4d000000000000000000000000000000000000000000000000000000008152849073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001690632e1a7d4d906109ad908490600401615740565b600060405180830381600087803b1580156109c757600080fd5b505af11580156109db573d6000803e3d6000fd5b50505050610a08565b8151610a089073ffffffffffffffffffffffffffffffffffffffff891690876138be565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815260009073ffffffffffffffffffffffffffffffffffffffff8816906370a0823190610a5d903090600401614ca9565b60206040518083038186803b158015610a7557600080fd5b505afa158015610a89573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610aad9190614a4d565b905060006060846000015173ffffffffffffffffffffffffffffffffffffffff16848660200151876040015188606001518c6001604051602401610af49493929190614ffc565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051610b7d9190614c8d565b60006040518083038185875af1925050503d8060008114610bba576040519150601f19603f3d011682016040523d82523d6000602084013e610bbf565b606091505b509150915081610bd257610bd28161399a565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff161415610cab574795507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663d0e30db0876040518263ffffffff1660e01b81526004016000604051808303818588803b158015610c9157600080fd5b505af1158015610ca5573d6000803e3d6000fd5b50505050505b610d3e838a73ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401610ce89190614ca9565b60206040518083038186803b158015610d0057600080fd5b505afa158015610d14573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d389190614a4d565b906139a2565b955050505050505b949350505050565b6000610d58614317565b82806020019051810190610d6c91906149f0565b8051909150610d939073ffffffffffffffffffffffffffffffffffffffff881690866138be565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815260009073ffffffffffffffffffffffffffffffffffffffff8716906370a0823190610de8903090600401614ca9565b60206040518083038186803b158015610e0057600080fd5b505afa158015610e14573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e389190614a4d565b905060006060836000015173ffffffffffffffffffffffffffffffffffffffff168460200151856040015186606001518a6001604051602401610e7e9493929190614ffc565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051610f079190614c8d565b6000604051808303816000865af19150503d8060008114610f44576040519150601f19603f3d011682016040523d82523d6000602084013e610f49565b606091505b509150915081610f5c57610f5c8161399a565b610f99838973ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401610ce89190614ca9565b9998505050505050505050565b6000806060600084806020019051810190610fc19190614650565b91945092509050610fe973ffffffffffffffffffffffffffffffffffffffff881684886138be565b806002146110c0576040805160a0810182528381523060208201524281830152606081018890526001608082015290517fc04b8d5900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85169163c04b8d5991611067919060040161565a565b602060405180830381600087803b15801561108157600080fd5b505af1158015611095573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110b99190614a4d565b9350611185565b604080516080810182528381523060208201528082018890526001606082015290517fb858183f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85169163b858183f91611130919060040161566d565b602060405180830381600087803b15801561114a57600080fd5b505af115801561115e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111829190614a4d565b93505b5050509392505050565b600080606080848060200190518101906111a9919061480c565b805191945092506002111590506111f5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016111ec90615283565b60405180910390fd5b8573ffffffffffffffffffffffffffffffffffffffff168160018351038151811061121c57fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff1614611271576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016111ec906155a0565b6112b082868360008151811061128357fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff166138be9092919063ffffffff16565b6040517f38ed173900000000000000000000000000000000000000000000000000000000815260609073ffffffffffffffffffffffffffffffffffffffff8416906338ed17399061130e9089906001908790309042906004016157a6565b600060405180830381600087803b15801561132857600080fd5b505af115801561133c573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820160405261138291908101906146a8565b90508060018251038151811061139457fe5b602002602001015193505050505b9392505050565b600080828060200190518101906113c09190614571565b905060006113cf8288886139c6565b90507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff16141561157b576040517f2e1a7d4d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001690632e1a7d4d90611497908890600401615740565b600060405180830381600087803b1580156114b157600080fd5b505af11580156114c5573d6000803e3d6000fd5b50506040517fad65d76d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8416925063ad65d76d91508790611522906001904290309060040161504f565b6020604051808303818588803b15801561153b57600080fd5b505af115801561154f573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906115749190614a4d565b92506117f2565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff161415611721576115f073ffffffffffffffffffffffffffffffffffffffff881682876138be565b6040517f95e3c50b00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8216906395e3c50b906116479088906001904290600401615847565b602060405180830381600087803b15801561166157600080fd5b505af1158015611675573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116999190614a4d565b92507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663d0e30db0846040518263ffffffff1660e01b81526004016000604051808303818588803b15801561170357600080fd5b505af1158015611717573d6000803e3d6000fd5b50505050506117f2565b61174273ffffffffffffffffffffffffffffffffffffffff881682876138be565b6040517fddf7e1a700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82169063ddf7e1a79061179d908890600190819042908d9060040161580e565b602060405180830381600087803b1580156117b757600080fd5b505af11580156117cb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117ef9190614a4d565b92505b5050949350505050565b600080828060200190518101906118139190614571565b905061183673ffffffffffffffffffffffffffffffffffffffff871682866138be565b6040517f8201aa3f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff821690638201aa3f906118b190899088908a906001907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90600401614ebb565b6040805180830381600087803b1580156118ca57600080fd5b505af11580156118de573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119029190614aa0565b509695505050505050565b600080606080848060200190518101906119279190614750565b925092509250606081905061194484888360008151811061128357fe5b868360008151811061195257fe5b602002602001015160600181815250506060815167ffffffffffffffff8111801561197c57600080fd5b506040519080825280602002602001820160405280156119a6578160200160208202803683370190505b50905060005b81518110156119f5577f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8282815181106119e257fe5b60209081029190910101526001016119ac565b50604080516080810182523080825260006020830181905282840191909152606082810182905292517f945bcec900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff89169263945bcec992611a799290918a9189919089904260010190600401614f2c565b600060405180830381600087803b158015611a9357600080fd5b505af1158015611aa7573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052611aed91908101906146a8565b9050600081600183510381518110611b0157fe5b60200260200101511315611b41576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016111ec906151c9565b80600182510381518110611b5157fe5b60200260200101517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0296505050505050505b92915050565b6000611b9461433e565b82806020019051810190611ba89190614a0b565b905060008573ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401611be59190614ca9565b60206040518083038186803b158015611bfd57600080fd5b505afa158015611c11573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c359190614a4d565b825160208401519192509073ffffffffffffffffffffffffffffffffffffffff89811691161415611d8a57611cff8173ffffffffffffffffffffffffffffffffffffffff166301664f666040518163ffffffff1660e01b815260040160206040518083038186803b158015611ca957600080fd5b505afa158015611cbd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ce19190614571565b73ffffffffffffffffffffffffffffffffffffffff8a1690886138be565b6040517f9599127600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff821690639599127690611d539030908a90600401614d36565b600060405180830381600087803b158015611d6d57600080fd5b505af1158015611d81573d6000803e3d6000fd5b50505050611fa5565b826020015173ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff161415611fa5576000611e548273ffffffffffffffffffffffffffffffffffffffff1663fae036d56040518163ffffffff1660e01b815260040160206040518083038186803b158015611e0d57600080fd5b505afa158015611e21573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e459190614a4d565b670de0b6b3a764000090613bb3565b905060008873ffffffffffffffffffffffffffffffffffffffff1663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b158015611e9e57600080fd5b505afa158015611eb2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ed69190614ac3565b60ff16600a0a90506000611ef483611eee8b85613bcf565b90613c00565b8651909150611f1b9073ffffffffffffffffffffffffffffffffffffffff8d16908b6138be565b6040517f8d7ef9bb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff851690638d7ef9bb90611f6f9030908590600401614d36565b600060405180830381600087803b158015611f8957600080fd5b505af1158015611f9d573d6000803e3d6000fd5b505050505050505b611fe2828873ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401610ce89190614ca9565b98975050505050505050565b600080600080848060200190518101906120089190614851565b9194509250905061203073ffffffffffffffffffffffffffffffffffffffff881683886138be565b801561209a57604080516000815260208101918290527f8dae73330000000000000000000000000000000000000000000000000000000090915273ffffffffffffffffffffffffffffffffffffffff831690638dae733390611067908990600190602481016157ef565b6040517fca19ebd900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84169063ca19ebd9906120ee9085908a90600401614d36565b60206040518083038186803b15801561210657600080fd5b505afa15801561211a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061213e9190614a4d565b604080516000815260208101918290527fe67ce7060000000000000000000000000000000000000000000000000000000090915290945073ffffffffffffffffffffffffffffffffffffffff83169063e67ce706906121a49087908a90602481016157ef565b602060405180830381600087803b1580156121be57600080fd5b505af11580156121d2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121f69190614a4d565b505050509392505050565b60008060008380602001905181019061221a919061489d565b909250905061224073ffffffffffffffffffffffffffffffffffffffff87168387613c2a565b806122ee576040517fdd93f59a00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff83169063dd93f59a90612297903090600401614ca9565b602060405180830381600087803b1580156122b157600080fd5b505af11580156122c5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122e99190614a4d565b61012c565b6040517fbd6015b400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff83169063bd6015b490612340903090600401614ca9565b602060405180830381600087803b15801561235a57600080fd5b505af115801561236e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061012c9190614a4d565b600080606080848060200190518101906123ac919061480c565b805191945092506002111590506123ef576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016111ec90615226565b8573ffffffffffffffffffffffffffffffffffffffff168160018351038151811061241657fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff1614611271576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016111ec906155fd565b6000612475614317565b8280602001905181019061248991906149f0565b80519091506124b09073ffffffffffffffffffffffffffffffffffffffff871690866138be565b60006060826000015173ffffffffffffffffffffffffffffffffffffffff16836020015184604001518560600151896001426040516024016124f6959493929190615021565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090941693909317909252905161257f9190614c8d565b6000604051808303816000865af19150503d80600081146125bc576040519150601f19603f3d011682016040523d82523d6000602084013e6125c1565b606091505b5091509150816125d4576125d48161399a565b808060200190518101906111829190614a4d565b60008060608084806020019051810190612602919061458d565b8151929550909350915060011115612646576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016111ec90615489565b8151600101815114612684576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016111ec906152e0565b8673ffffffffffffffffffffffffffffffffffffffff16816001835103815181106126ab57fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff1614612700576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016111ec9061516c565b61271283878360008151811061128357fe5b6040517fceb757d500000000000000000000000000000000000000000000000000000000815260609073ffffffffffffffffffffffffffffffffffffffff85169063ceb757d590612772908a906001908890889030904290600401615749565b600060405180830381600087803b15801561278c57600080fd5b505af11580156127a0573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526127e691908101906146a8565b9050806001825103815181106127f857fe5b60200260200101519450505050509392505050565b6000806060838060200190518101906128269190614601565b909250905061284c73ffffffffffffffffffffffffffffffffffffffff871683876138be565b6040805160a0810182528281523060208201524281830152606081018790526001608082015290517f5d946c2500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff841691635d946c2591612340919060040161565a565b60007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff16141561292a57612923848484613cd6565b9050610d46565b6100df85858585613f1c565b60008060008380602001905181019061294f91906148d6565b909250905061297573ffffffffffffffffffffffffffffffffffffffff881683876138be565b8073ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff161415612a3e576040517fe8eda9df00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff83169063e8eda9df90612a02908a9089903090600090600401614d8c565b600060405180830381600087803b158015612a1c57600080fd5b505af1158015612a30573d6000803e3d6000fd5b505050508492505050610d46565b8073ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff161415612b23576040517f69328dec00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8316906369328dec90612ac890899089903090600401614d5c565b602060405180830381600087803b158015612ae257600080fd5b505af1158015612af6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b1a9190614a4d565b92505050610d46565b6040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016111ec90615543565b60008082806020019051810190612b6c9190614571565b905060008573ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401612ba99190614ca9565b60206040518083038186803b158015612bc157600080fd5b505afa158015612bd5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612bf99190614a4d565b90508173ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff161415612e98577f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff161415612d90576040517f2e1a7d4d000000000000000000000000000000000000000000000000000000008152829073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001690632e1a7d4d90612cf7908990600401615740565b600060405180830381600087803b158015612d1157600080fd5b505af1158015612d25573d6000803e3d6000fd5b505050508073ffffffffffffffffffffffffffffffffffffffff16631249c58b876040518263ffffffff1660e01b81526004016000604051808303818588803b158015612d7157600080fd5b505af1158015612d85573d6000803e3d6000fd5b505050505050612e93565b612db173ffffffffffffffffffffffffffffffffffffffff881683876138be565b6040517fa0712d68000000000000000000000000000000000000000000000000000000008152829060009073ffffffffffffffffffffffffffffffffffffffff83169063a0712d6890612e08908a90600401615740565b602060405180830381600087803b158015612e2257600080fd5b505af1158015612e36573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e5a9190614a4d565b14612e91576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016111ec90615372565b505b61317d565b8173ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff16141561317d577f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff16141561309b576040517fdb006a750000000000000000000000000000000000000000000000000000000081524790839060009073ffffffffffffffffffffffffffffffffffffffff83169063db006a7590612f79908b90600401615740565b602060405180830381600087803b158015612f9357600080fd5b505af1158015612fa7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612fcb9190614a4d565b14613002576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016111ec9061542c565b47600061300f82856139a2565b90507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b15801561307957600080fd5b505af115801561308d573d6000803e3d6000fd5b50505050505050505061317d565b6040517fdb006a75000000000000000000000000000000000000000000000000000000008152829060009073ffffffffffffffffffffffffffffffffffffffff83169063db006a75906130f2908a90600401615740565b602060405180830381600087803b15801561310c57600080fd5b505af1158015613120573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131449190614a4d565b1461317b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016111ec906154e6565b505b6117ef818773ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401610ce89190614ca9565b6000806060806000858060200190518101906131d6919061480c565b805191955093508392506002111561321a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016111ec9061507b565b8773ffffffffffffffffffffffffffffffffffffffff168360018551038151811061324157fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff1614613296576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016111ec9061510f565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16826000815181106132da57fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff161415613406576040517f2e1a7d4d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001690632e1a7d4d90613370908a90600401615740565b600060405180830381600087803b15801561338a57600080fd5b505af115801561339e573d6000803e3d6000fd5b5050505086905073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee826000815181106133c757fe5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050613418565b61341884888560008151811061128357fe5b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff168260018151811061345c57fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff1614156134dd5773eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee826001815181106134a257fe5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250505b60008473ffffffffffffffffffffffffffffffffffffffff1663d3a4acd3838560008151811061350957fe5b60200260200101518660018151811061351e57fe5b60200260200101518c600142600101306040518863ffffffff1660e01b815260040161354f96959493929190614cf1565b6020604051808303818588803b15801561356857600080fd5b505af115801561357c573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906135a19190614a4d565b90507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff161415611fe2577f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b15801561365f57600080fd5b505af1158015613673573d6000803e3d6000fd5b505050505098975050505050505050565b60008060608380602001905181019061369d9190614601565b90925090506136c373ffffffffffffffffffffffffffffffffffffffff871683876138be565b600060608373ffffffffffffffffffffffffffffffffffffffff16836040516136ec9190614c8d565b6000604051808303816000865af19150503d8060008114613729576040519150601f19603f3d011682016040523d82523d6000602084013e61372e565b606091505b509150915081613741576137418161399a565b80806020019051810190611fe29190614a4d565b60008060008380602001905181019061376e91906148d6565b909250905061379473ffffffffffffffffffffffffffffffffffffffff881683876138be565b604080516101008101825273ffffffffffffffffffffffffffffffffffffffff898116825288811660208301528381168284015230606083015242608083015260a08201889052600160c0830152600060e083015291517fa5dcbcdf0000000000000000000000000000000000000000000000000000000081529184169163a5dcbcdf9161179d916004016156c9565b60008060608380602001905181019061383d9190614601565b909250905061386373ffffffffffffffffffffffffffffffffffffffff88168387613c2a565b6040517f65d02b0400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8316906365d02b049061179d908a908a9030906001908890600401614e69565b6040517fdd62ed3e000000000000000000000000000000000000000000000000000000008152819073ffffffffffffffffffffffffffffffffffffffff85169063dd62ed3e906139149030908790600401614cca565b60206040518083038186803b15801561392c57600080fd5b505afa158015613940573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139649190614a4d565b10156139955761399583837fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6140d4565b505050565b805160208201fd5b6000828211156139c0576139c06139bb600285856140f1565b61399a565b50900390565b60007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1614613ac2576040517f06f2bf6200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8516906306f2bf6290613a6d908690600401614ca9565b60206040518083038186803b158015613a8557600080fd5b505afa158015613a99573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613abd9190614571565b613b64565b6040517f06f2bf6200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8516906306f2bf6290613b14908590600401614ca9565b60206040518083038186803b158015613b2c57600080fd5b505afa158015613b40573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b649190614571565b905073ffffffffffffffffffffffffffffffffffffffff81166113a2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016111ec906150d8565b6000828201838110156113a2576113a26139bb600086866140f1565b600082613bde57506000611b84565b82820282848281613beb57fe5b04146113a2576113a26139bb600186866140f1565b600081613c1657613c166139bb600385856140f1565b6000828481613c2157fe5b04949350505050565b606063a9059cbb60e01b8383604051602401613c47929190614d36565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909316929092179091529050613cd08482614196565b50505050565b60008082806020019051810190613ced9190614571565b90508073ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff161415613eea576040517f2e1a7d4d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001690632e1a7d4d90613d95908790600401615740565b600060405180830381600087803b158015613daf57600080fd5b505af1158015613dc3573d6000803e3d6000fd5b50506040517fa1903eab00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84169250637a28fb889150829063a1903eab908890613e2490600090600401614ca9565b6020604051808303818588803b158015613e3d57600080fd5b505af1158015613e51573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190613e769190614a4d565b6040518263ffffffff1660e01b8152600401613e929190615740565b60206040518083038186803b158015613eaa57600080fd5b505afa158015613ebe573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ee29190614a4d565b9150506113a2565b6040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016111ec9061533d565b600080600083806020019051810190613f3591906148d6565b915091508173ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff16148015613f9f57508073ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff16145b1561401757613fc573ffffffffffffffffffffffffffffffffffffffff881682876138be565b6040517fea598cb000000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82169063ea598cb090612ac8908890600401615740565b8073ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff1614801561407d57508173ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff16145b15613eea576040517fde0e9a3e00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82169063de0e9a3e90612ac8908890600401615740565b606063095ea7b360e01b8383604051602401613c47929190614d36565b606063e946c1bb60e01b84848460405160240161411093929190614efc565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915290509392505050565b600060608373ffffffffffffffffffffffffffffffffffffffff16836040516141bf9190614c8d565b6000604051808303816000865af19150503d80600081146141fc576040519150601f19603f3d011682016040523d82523d6000602084013e614201565b606091505b509150915081614214576142148161399a565b805161425b57833b80614253576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016111ec906153cf565b505050614298565b602081511061428f57600061427182600061429c565b9050806001141561428457505050614298565b61428d8261399a565b505b613cd08161399a565b5050565b60006113a28383600081602001835110156142c4576142c46139bb60058551856020016142cd565b50016020015190565b6060632800659560e01b84848460405160240161411093929190614f1e565b6040518060800160405280600080191681526020016000815260200160008152602001606081525090565b60408051608081018252600080825260208201819052918101829052606081019190915290565b604080518082019091526000808252602082015290565b600082601f830112614365578081fd5b815161437861437382615884565b61585d565b81815291506020808301908481018184028601820187101561439957600080fd5b60005b848110156143c15781516143af81615922565b8452928201929082019060010161439c565b505050505092915050565b600082601f8301126143dc578081fd5b81356143ea614373826158a4565b915080825283602082850101111561440157600080fd5b8060208401602084013760009082016020015292915050565b600082601f83011261442a578081fd5b8151614438614373826158a4565b915080825283602082850101111561444f57600080fd5b6144608160208401602086016158f6565b5092915050565b8035611b8481615922565b600060a08284031215614483578081fd5b61448d60a061585d565b905081518152602082015160208201526040820151604082015260608201516060820152608082015167ffffffffffffffff8111156144cb57600080fd5b6144d78482850161441a565b60808301525092915050565b6000608082840312156144f4578081fd5b6144fe608061585d565b9050815161450b81615922565b815260208201517fffffffff000000000000000000000000000000000000000000000000000000008116811461454057600080fd5b6020820152604082015161455381615955565b6040820152606082015161456681615955565b606082015292915050565b600060208284031215614582578081fd5b81516113a281615922565b6000806000606084860312156145a1578182fd5b83516145ac81615922565b602085015190935067ffffffffffffffff808211156145c9578384fd5b6145d587838801614355565b935060408601519150808211156145ea578283fd5b506145f786828701614355565b9150509250925092565b60008060408385031215614613578182fd5b825161461e81615922565b602084015190925067ffffffffffffffff81111561463a578182fd5b6146468582860161441a565b9150509250929050565b600080600060608486031215614664578081fd5b835161466f81615922565b602085015190935067ffffffffffffffff81111561468b578182fd5b6146978682870161441a565b925050604084015190509250925092565b600060208083850312156146ba578182fd5b825167ffffffffffffffff8111156146d0578283fd5b8301601f810185136146e0578283fd5b80516146ee61437382615884565b818152838101908385018584028501860189101561470a578687fd5b8694505b8385101561472c57805183526001949094019391850191850161470e565b50979650505050505050565b600060208284031215614749578081fd5b5035919050565b600080600060608486031215614764578081fd5b835161476f81615922565b8093505060208085015167ffffffffffffffff8082111561478e578384fd5b818701915087601f8301126147a1578384fd5b81516147af61437382615884565b81815284810190848601875b848110156147e4576147d28d8984518a0101614472565b845292870192908701906001016147bb565b505060408a015190975094505050808311156147fe578384fd5b50506145f786828701614355565b6000806040838503121561481e578182fd5b825161482981615922565b602084015190925067ffffffffffffffff811115614845578182fd5b61464685828601614355565b600080600060608486031215614865578081fd5b835161487081615922565b602085015190935061488181615922565b604085015190925061489281615947565b809150509250925092565b600080604083850312156148af578182fd5b82516148ba81615922565b60208401519092506148cb81615947565b809150509250929050565b600080604083850312156148e8578182fd5b82516148f381615922565b60208401519092506148cb81615922565b600080600060608486031215614918578081fd5b835161492381615922565b602085015160409095015190969495509392505050565b6000806000806080858703121561494f578182fd5b843567ffffffffffffffff80821115614966578384fd5b9086019060808289031215614979578384fd5b614983608061585d565b8235815260208301356020820152604083013560408201526060830135828111156149ac578586fd5b6149b88a8286016143cc565b6060830152508096505050506149d18660208701614467565b92506149e08660408701614467565b9396929550929360600135925050565b600060808284031215614a01578081fd5b6113a283836144e3565b600060408284031215614a1c578081fd5b614a26604061585d565b8251614a3181615922565b81526020830151614a4181615922565b60208201529392505050565b600060208284031215614a5e578081fd5b5051919050565b60008060408385031215614a77578182fd5b82359150602083013567ffffffffffffffff811115614a94578182fd5b614646858286016143cc565b60008060408385031215614ab2578182fd5b505080516020909101519092909150565b600060208284031215614ad4578081fd5b815160ff811681146113a2578182fd5b6000815180845260208085019450808401835b83811015614b2957815173ffffffffffffffffffffffffffffffffffffffff1687529582019590820190600101614af7565b509495945050505050565b6000815180845260208085019450808401835b83811015614b2957815187529582019590820190600101614b47565b60008151808452614b7b8160208601602086016158f6565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b600081518352602082015160208401526040820151604084015260608201516060840152608082015160a06080850152610d4660a0850182614b63565b6000815160a08452614bff60a0850182614b63565b905073ffffffffffffffffffffffffffffffffffffffff60208401511660208501526040830151604085015260608301516060850152608083015160808501528091505092915050565b73ffffffffffffffffffffffffffffffffffffffff808251168352602082015115156020840152806040830151166040840152506060810151151560608301525050565b60008251614c9f8184602087016158f6565b9190910192915050565b73ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b73ffffffffffffffffffffffffffffffffffffffff92831681529116602082015260400190565b73ffffffffffffffffffffffffffffffffffffffff9687168152948616602086015260408501939093526060840191909152608083015290911660a082015260c00190565b73ffffffffffffffffffffffffffffffffffffffff929092168252602082015260400190565b73ffffffffffffffffffffffffffffffffffffffff93841681526020810192909252909116604082015260600190565b73ffffffffffffffffffffffffffffffffffffffff948516815260208101939093529216604082015261ffff909116606082015260800190565b901515815260200190565b94855273ffffffffffffffffffffffffffffffffffffffff93841660208601529190921660408401526060830191909152608082015260a00190565b9485526020850193909352604084019190915273ffffffffffffffffffffffffffffffffffffffff166060830152608082015260a00190565b948552602085019390935260408401919091526060830152608082015260a00190565b600073ffffffffffffffffffffffffffffffffffffffff8088168352808716602084015280861660408401525083606083015260a06080830152614eb060a0830184614b63565b979650505050505050565b73ffffffffffffffffffffffffffffffffffffffff958616815260208101949094529190931660408301526060820192909252608081019190915260a00190565b6060810160048510614f0a57fe5b938152602081019290925260409091015290565b6060810160088510614f0a57fe5b6000610120808301614f3d8a6158e6565b8452602080850192909252885190819052610140808501928281028601909101918a8201855b82811015614faf577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec0888603018652614f9d858351614bad565b95840195945090830190600101614f63565b505050508381036040850152614fc58189614ae4565b915050614fd56060840187614c49565b82810360e0840152614fe78186614b34565b91505082610100830152979650505050505050565b600f94850b81529290930b6020830152604082015260ff909116606082015260800190565b600f95860b81529390940b6020840152604083019190915260ff166060820152608081019190915260a00190565b928352602083019190915273ffffffffffffffffffffffffffffffffffffffff16604082015260600190565b6020808252602e908201527f4d6978696e42616e636f7256332f504154485f4c454e4754485f4d5553545f4260408201527f455f41545f4c454153545f54574f000000000000000000000000000000000000606082015260800190565b60208082526018908201527f4d6978696e556e69737761702f4e4f5f45584348414e47450000000000000000604082015260600190565b6020808252603a908201527f4d6978696e42616e636f7256332f4c4153545f454c454d454e545f4f465f504160408201527f54485f4d5553545f4d415443485f4f55545055545f544f4b454e000000000000606082015260800190565b6020808252603a908201527f4d6978696e4b79626572446d6d2f4c4153545f454c454d454e545f4f465f504160408201527f54485f4d5553545f4d415443485f4f55545055545f544f4b454e000000000000606082015260800190565b60208082526021908201527f556e65787065637465642042616c616e63657256324261746368206f7574707560408201527f7400000000000000000000000000000000000000000000000000000000000000606082015260800190565b6020808252602f908201527f4d6978696e43727970746f436f6d2f504154485f4c454e4754485f4d5553545f60408201527f42455f41545f4c454153545f54574f0000000000000000000000000000000000606082015260800190565b6020808252602f908201527f4d6978696e556e697377617056322f504154485f4c454e4754485f4d5553545f60408201527f42455f41545f4c454153545f54574f0000000000000000000000000000000000606082015260800190565b60208082526023908201527f4d6978696e4b79626572446d6d2f41525241595f4c454e4754485f4d49534d4160408201527f5443480000000000000000000000000000000000000000000000000000000000606082015260800190565b6020808252818101527f4d6978696e4c69646f2f554e535550504f525445445f544f4b454e5f50414952604082015260600190565b60208082526023908201527f4d6978696e436f6d706f756e642f4641494c45445f544f5f4d494e545f43544f60408201527f4b454e0000000000000000000000000000000000000000000000000000000000606082015260800190565b60208082526027908201527f696e76616c696420746f6b656e20616464726573732c20636f6e7461696e732060408201527f6e6f20636f646500000000000000000000000000000000000000000000000000606082015260800190565b60208082526025908201527f4d6978696e436f6d706f756e642f4641494c45445f544f5f52454445454d5f4360408201527f4554484552000000000000000000000000000000000000000000000000000000606082015260800190565b6020808252602f908201527f4d6978696e4b79626572446d6d2f504f4f4c535f4c454e4754485f4d5553545f60408201527f42455f41545f4c454153545f4f4e450000000000000000000000000000000000606082015260800190565b60208082526025908201527f4d6978696e436f6d706f756e642f4641494c45445f544f5f52454445454d5f4360408201527f544f4b454e000000000000000000000000000000000000000000000000000000606082015260800190565b60208082526022908201527f4d6978696e4161766556322f554e535550504f525445445f544f4b454e5f504160408201527f4952000000000000000000000000000000000000000000000000000000000000606082015260800190565b6020808252603b908201527f4d6978696e556e697377617056322f4c4153545f454c454d454e545f4f465f5060408201527f4154485f4d5553545f4d415443485f4f55545055545f544f4b454e0000000000606082015260800190565b6020808252603b908201527f4d6978696e43727970746f436f6d2f4c4153545f454c454d454e545f4f465f5060408201527f4154485f4d5553545f4d415443485f4f55545055545f544f4b454e0000000000606082015260800190565b6000602082526113a26020830184614bea565b60006020825282516080602084015261568960a0840182614b63565b905073ffffffffffffffffffffffffffffffffffffffff602085015116604084015260408401516060840152606084015160808401528091505092915050565b60006101008201905073ffffffffffffffffffffffffffffffffffffffff808451168352806020850151166020840152806040850151166040840152806060850151166060840152506080830151608083015260a083015160a083015260c083015160c083015260e083015160e083015292915050565b90815260200190565b600087825286602083015260c0604083015261576860c0830187614ae4565b828103606084015261577a8187614ae4565b73ffffffffffffffffffffffffffffffffffffffff959095166080840152505060a00152949350505050565b600086825285602083015260a060408301526157c560a0830186614ae4565b73ffffffffffffffffffffffffffffffffffffffff94909416606083015250608001529392505050565b6000848252836020830152606060408301526100df6060830184614b63565b94855260208501939093526040840191909152606083015273ffffffffffffffffffffffffffffffffffffffff16608082015260a00190565b9283526020830191909152604082015260600190565b60405181810167ffffffffffffffff8111828210171561587c57600080fd5b604052919050565b600067ffffffffffffffff82111561589a578081fd5b5060209081020190565b600067ffffffffffffffff8211156158ba578081fd5b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b80600281106158f157fe5b919050565b60005b838110156159115781810151838201526020016158f9565b83811115613cd05750506000910152565b73ffffffffffffffffffffffffffffffffffffffff8116811461594457600080fd5b50565b801515811461594457600080fd5b80600f0b811461594457600080fdfea26469706673582212208fa636d6f56d0c6e65470285120a4eef938a8aae07a48694062402d894437fed64736f6c634300060c0033000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2

Deployed Bytecode

0x608060405234801561001057600080fd5b506004361061004c5760003560e01c806302d2734f1461005157806365f53ee61461007a57806399a4d2d01461008f578063f712a148146100af575b600080fd5b61006461005f366004614738565b6100c2565b6040516100719190614dc6565b60405180910390f35b6100826100e8565b6040516100719190614ca9565b6100a261009d366004614a65565b610100565b6040516100719190615740565b6100a26100bd36600461493a565b610136565b60006100cc6142ec565b82815260006100df828280806001610150565b95945050505050565b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee81565b6000806000808480602001905181019061011a9190614904565b92509250925061012c838383896106f6565b9695505050505050565b6000610146858585856000610150565b5095945050505050565b8451600090819060801c600181141561018e578315610177576000600192509250506106ec565b6101878787878b606001516108c2565b92506106aa565b6fffffffffffffffffffffffffffffffff8116601414156101cd5783156101bd576000600192509250506106ec565b6101878787878b60600151610d4e565b6fffffffffffffffffffffffffffffffff81166012141561020b5783156101fc576000600192509250506106ec565b61018787868a60600151610fa6565b6fffffffffffffffffffffffffffffffff81166002141561024957831561023a576000600192509250506106ec565b61018786868a6060015161118f565b6fffffffffffffffffffffffffffffffff811660031415610288578315610278576000600192509250506106ec565b6101878787878b606001516113a9565b6fffffffffffffffffffffffffffffffff8116600414156102c75783156102b7576000600192509250506106ec565b6101878787878b606001516117fc565b6fffffffffffffffffffffffffffffffff8116601914156103045783156102f6576000600192509250506106ec565b61018785896060015161190d565b6fffffffffffffffffffffffffffffffff811660101415610343578315610333576000600192509250506106ec565b6101878787878b60600151611b8a565b6fffffffffffffffffffffffffffffffff8116600a1415610381578315610372576000600192509250506106ec565b61018787868a60600151611fee565b6fffffffffffffffffffffffffffffffff8116600b14156103bf5783156103b0576000600192509250506106ec565b61018787868a60600151612201565b6fffffffffffffffffffffffffffffffff8116600c14156103fd5783156103ee576000600192509250506106ec565b61018786868a60600151612392565b6fffffffffffffffffffffffffffffffff8116600f141561043b57831561042c576000600192509250506106ec565b61018787868a6060015161246b565b6fffffffffffffffffffffffffffffffff81166013141561047957831561046a576000600192509250506106ec565b61018786868a606001516125e8565b6fffffffffffffffffffffffffffffffff8116602114156104b75783156104a8576000600192509250506106ec565b61018787868a6060015161280d565b6fffffffffffffffffffffffffffffffff8116601514156104f65783156104e6576000600192509250506106ec565b6101878787878b606001516128c2565b6fffffffffffffffffffffffffffffffff811660171415610535578315610525576000600192509250506106ec565b6101878787878b60600151612936565b6fffffffffffffffffffffffffffffffff811660181415610574578315610564576000600192509250506106ec565b6101878787878b60600151612b55565b6fffffffffffffffffffffffffffffffff8116601c14156105b25783156105a3576000600192509250506106ec565b61018786868a606001516131ba565b6fffffffffffffffffffffffffffffffff8116601e14156105ef5783156105e1576000600192509250506106ec565b610187858960600151610100565b6fffffffffffffffffffffffffffffffff81166022141561062d57831561061e576000600192509250506106ec565b61018787868a60600151613684565b6fffffffffffffffffffffffffffffffff81166025141561066c57831561065c576000600192509250506106ec565b6101878787878b60600151613755565b6fffffffffffffffffffffffffffffffff81166106aa578315610697576000600192509250506106ec565b6106a78787878b60600151613824565b92505b87516040517fe59e71a14fe90157eedc866c4f8c767d3943d6b6b2e8cd64dddcc92ab4c55af8916106e2918a908a908a908990614dd1565b60405180910390a1505b9550959350505050565b60004660018114156107d6576040517f44b3e92300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8716906344b3e9239061077d908890879089907f305800000000000000000000000000000000000000000000000000000000000090600090600401614e46565b602060405180830381600087803b15801561079757600080fd5b505af11580156107ab573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107cf9190614a4d565b91506108b9565b6040517f30ead76000000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8716906330ead7609061086490889087908990735c80239d97e1eb216b5c3d8fba5de5be5d38e4c9907f305800000000000000000000000000000000000000000000000000000000000090600401614e0d565b602060405180830381600087803b15801561087e57600080fd5b505af1158015610892573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108b69190614a4d565b91505b50949350505050565b60006108cc614317565b828060200190518101906108e091906149f0565b905060007f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc273ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff1614156109e457506040517f2e1a7d4d000000000000000000000000000000000000000000000000000000008152849073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc21690632e1a7d4d906109ad908490600401615740565b600060405180830381600087803b1580156109c757600080fd5b505af11580156109db573d6000803e3d6000fd5b50505050610a08565b8151610a089073ffffffffffffffffffffffffffffffffffffffff891690876138be565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815260009073ffffffffffffffffffffffffffffffffffffffff8816906370a0823190610a5d903090600401614ca9565b60206040518083038186803b158015610a7557600080fd5b505afa158015610a89573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610aad9190614a4d565b905060006060846000015173ffffffffffffffffffffffffffffffffffffffff16848660200151876040015188606001518c6001604051602401610af49493929190614ffc565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051610b7d9190614c8d565b60006040518083038185875af1925050503d8060008114610bba576040519150601f19603f3d011682016040523d82523d6000602084013e610bbf565b606091505b509150915081610bd257610bd28161399a565b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc273ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff161415610cab574795507f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc273ffffffffffffffffffffffffffffffffffffffff1663d0e30db0876040518263ffffffff1660e01b81526004016000604051808303818588803b158015610c9157600080fd5b505af1158015610ca5573d6000803e3d6000fd5b50505050505b610d3e838a73ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401610ce89190614ca9565b60206040518083038186803b158015610d0057600080fd5b505afa158015610d14573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d389190614a4d565b906139a2565b955050505050505b949350505050565b6000610d58614317565b82806020019051810190610d6c91906149f0565b8051909150610d939073ffffffffffffffffffffffffffffffffffffffff881690866138be565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815260009073ffffffffffffffffffffffffffffffffffffffff8716906370a0823190610de8903090600401614ca9565b60206040518083038186803b158015610e0057600080fd5b505afa158015610e14573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e389190614a4d565b905060006060836000015173ffffffffffffffffffffffffffffffffffffffff168460200151856040015186606001518a6001604051602401610e7e9493929190614ffc565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909416939093179092529051610f079190614c8d565b6000604051808303816000865af19150503d8060008114610f44576040519150601f19603f3d011682016040523d82523d6000602084013e610f49565b606091505b509150915081610f5c57610f5c8161399a565b610f99838973ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401610ce89190614ca9565b9998505050505050505050565b6000806060600084806020019051810190610fc19190614650565b91945092509050610fe973ffffffffffffffffffffffffffffffffffffffff881684886138be565b806002146110c0576040805160a0810182528381523060208201524281830152606081018890526001608082015290517fc04b8d5900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85169163c04b8d5991611067919060040161565a565b602060405180830381600087803b15801561108157600080fd5b505af1158015611095573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110b99190614a4d565b9350611185565b604080516080810182528381523060208201528082018890526001606082015290517fb858183f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85169163b858183f91611130919060040161566d565b602060405180830381600087803b15801561114a57600080fd5b505af115801561115e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111829190614a4d565b93505b5050509392505050565b600080606080848060200190518101906111a9919061480c565b805191945092506002111590506111f5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016111ec90615283565b60405180910390fd5b8573ffffffffffffffffffffffffffffffffffffffff168160018351038151811061121c57fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff1614611271576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016111ec906155a0565b6112b082868360008151811061128357fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff166138be9092919063ffffffff16565b6040517f38ed173900000000000000000000000000000000000000000000000000000000815260609073ffffffffffffffffffffffffffffffffffffffff8416906338ed17399061130e9089906001908790309042906004016157a6565b600060405180830381600087803b15801561132857600080fd5b505af115801561133c573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820160405261138291908101906146a8565b90508060018251038151811061139457fe5b602002602001015193505050505b9392505050565b600080828060200190518101906113c09190614571565b905060006113cf8288886139c6565b90507f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc273ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff16141561157b576040517f2e1a7d4d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc21690632e1a7d4d90611497908890600401615740565b600060405180830381600087803b1580156114b157600080fd5b505af11580156114c5573d6000803e3d6000fd5b50506040517fad65d76d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8416925063ad65d76d91508790611522906001904290309060040161504f565b6020604051808303818588803b15801561153b57600080fd5b505af115801561154f573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906115749190614a4d565b92506117f2565b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc273ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff161415611721576115f073ffffffffffffffffffffffffffffffffffffffff881682876138be565b6040517f95e3c50b00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8216906395e3c50b906116479088906001904290600401615847565b602060405180830381600087803b15801561166157600080fd5b505af1158015611675573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116999190614a4d565b92507f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc273ffffffffffffffffffffffffffffffffffffffff1663d0e30db0846040518263ffffffff1660e01b81526004016000604051808303818588803b15801561170357600080fd5b505af1158015611717573d6000803e3d6000fd5b50505050506117f2565b61174273ffffffffffffffffffffffffffffffffffffffff881682876138be565b6040517fddf7e1a700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82169063ddf7e1a79061179d908890600190819042908d9060040161580e565b602060405180830381600087803b1580156117b757600080fd5b505af11580156117cb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117ef9190614a4d565b92505b5050949350505050565b600080828060200190518101906118139190614571565b905061183673ffffffffffffffffffffffffffffffffffffffff871682866138be565b6040517f8201aa3f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff821690638201aa3f906118b190899088908a906001907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90600401614ebb565b6040805180830381600087803b1580156118ca57600080fd5b505af11580156118de573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119029190614aa0565b509695505050505050565b600080606080848060200190518101906119279190614750565b925092509250606081905061194484888360008151811061128357fe5b868360008151811061195257fe5b602002602001015160600181815250506060815167ffffffffffffffff8111801561197c57600080fd5b506040519080825280602002602001820160405280156119a6578160200160208202803683370190505b50905060005b81518110156119f5577f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8282815181106119e257fe5b60209081029190910101526001016119ac565b50604080516080810182523080825260006020830181905282840191909152606082810182905292517f945bcec900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff89169263945bcec992611a799290918a9189919089904260010190600401614f2c565b600060405180830381600087803b158015611a9357600080fd5b505af1158015611aa7573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052611aed91908101906146a8565b9050600081600183510381518110611b0157fe5b60200260200101511315611b41576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016111ec906151c9565b80600182510381518110611b5157fe5b60200260200101517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0296505050505050505b92915050565b6000611b9461433e565b82806020019051810190611ba89190614a0b565b905060008573ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401611be59190614ca9565b60206040518083038186803b158015611bfd57600080fd5b505afa158015611c11573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c359190614a4d565b825160208401519192509073ffffffffffffffffffffffffffffffffffffffff89811691161415611d8a57611cff8173ffffffffffffffffffffffffffffffffffffffff166301664f666040518163ffffffff1660e01b815260040160206040518083038186803b158015611ca957600080fd5b505afa158015611cbd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ce19190614571565b73ffffffffffffffffffffffffffffffffffffffff8a1690886138be565b6040517f9599127600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff821690639599127690611d539030908a90600401614d36565b600060405180830381600087803b158015611d6d57600080fd5b505af1158015611d81573d6000803e3d6000fd5b50505050611fa5565b826020015173ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff161415611fa5576000611e548273ffffffffffffffffffffffffffffffffffffffff1663fae036d56040518163ffffffff1660e01b815260040160206040518083038186803b158015611e0d57600080fd5b505afa158015611e21573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e459190614a4d565b670de0b6b3a764000090613bb3565b905060008873ffffffffffffffffffffffffffffffffffffffff1663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b158015611e9e57600080fd5b505afa158015611eb2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ed69190614ac3565b60ff16600a0a90506000611ef483611eee8b85613bcf565b90613c00565b8651909150611f1b9073ffffffffffffffffffffffffffffffffffffffff8d16908b6138be565b6040517f8d7ef9bb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff851690638d7ef9bb90611f6f9030908590600401614d36565b600060405180830381600087803b158015611f8957600080fd5b505af1158015611f9d573d6000803e3d6000fd5b505050505050505b611fe2828873ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401610ce89190614ca9565b98975050505050505050565b600080600080848060200190518101906120089190614851565b9194509250905061203073ffffffffffffffffffffffffffffffffffffffff881683886138be565b801561209a57604080516000815260208101918290527f8dae73330000000000000000000000000000000000000000000000000000000090915273ffffffffffffffffffffffffffffffffffffffff831690638dae733390611067908990600190602481016157ef565b6040517fca19ebd900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84169063ca19ebd9906120ee9085908a90600401614d36565b60206040518083038186803b15801561210657600080fd5b505afa15801561211a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061213e9190614a4d565b604080516000815260208101918290527fe67ce7060000000000000000000000000000000000000000000000000000000090915290945073ffffffffffffffffffffffffffffffffffffffff83169063e67ce706906121a49087908a90602481016157ef565b602060405180830381600087803b1580156121be57600080fd5b505af11580156121d2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121f69190614a4d565b505050509392505050565b60008060008380602001905181019061221a919061489d565b909250905061224073ffffffffffffffffffffffffffffffffffffffff87168387613c2a565b806122ee576040517fdd93f59a00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff83169063dd93f59a90612297903090600401614ca9565b602060405180830381600087803b1580156122b157600080fd5b505af11580156122c5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122e99190614a4d565b61012c565b6040517fbd6015b400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff83169063bd6015b490612340903090600401614ca9565b602060405180830381600087803b15801561235a57600080fd5b505af115801561236e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061012c9190614a4d565b600080606080848060200190518101906123ac919061480c565b805191945092506002111590506123ef576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016111ec90615226565b8573ffffffffffffffffffffffffffffffffffffffff168160018351038151811061241657fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff1614611271576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016111ec906155fd565b6000612475614317565b8280602001905181019061248991906149f0565b80519091506124b09073ffffffffffffffffffffffffffffffffffffffff871690866138be565b60006060826000015173ffffffffffffffffffffffffffffffffffffffff16836020015184604001518560600151896001426040516024016124f6959493929190615021565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090941693909317909252905161257f9190614c8d565b6000604051808303816000865af19150503d80600081146125bc576040519150601f19603f3d011682016040523d82523d6000602084013e6125c1565b606091505b5091509150816125d4576125d48161399a565b808060200190518101906111829190614a4d565b60008060608084806020019051810190612602919061458d565b8151929550909350915060011115612646576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016111ec90615489565b8151600101815114612684576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016111ec906152e0565b8673ffffffffffffffffffffffffffffffffffffffff16816001835103815181106126ab57fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff1614612700576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016111ec9061516c565b61271283878360008151811061128357fe5b6040517fceb757d500000000000000000000000000000000000000000000000000000000815260609073ffffffffffffffffffffffffffffffffffffffff85169063ceb757d590612772908a906001908890889030904290600401615749565b600060405180830381600087803b15801561278c57600080fd5b505af11580156127a0573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526127e691908101906146a8565b9050806001825103815181106127f857fe5b60200260200101519450505050509392505050565b6000806060838060200190518101906128269190614601565b909250905061284c73ffffffffffffffffffffffffffffffffffffffff871683876138be565b6040805160a0810182528281523060208201524281830152606081018790526001608082015290517f5d946c2500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff841691635d946c2591612340919060040161565a565b60007f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc273ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff16141561292a57612923848484613cd6565b9050610d46565b6100df85858585613f1c565b60008060008380602001905181019061294f91906148d6565b909250905061297573ffffffffffffffffffffffffffffffffffffffff881683876138be565b8073ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff161415612a3e576040517fe8eda9df00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff83169063e8eda9df90612a02908a9089903090600090600401614d8c565b600060405180830381600087803b158015612a1c57600080fd5b505af1158015612a30573d6000803e3d6000fd5b505050508492505050610d46565b8073ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff161415612b23576040517f69328dec00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8316906369328dec90612ac890899089903090600401614d5c565b602060405180830381600087803b158015612ae257600080fd5b505af1158015612af6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b1a9190614a4d565b92505050610d46565b6040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016111ec90615543565b60008082806020019051810190612b6c9190614571565b905060008573ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401612ba99190614ca9565b60206040518083038186803b158015612bc157600080fd5b505afa158015612bd5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612bf99190614a4d565b90508173ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff161415612e98577f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc273ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff161415612d90576040517f2e1a7d4d000000000000000000000000000000000000000000000000000000008152829073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc21690632e1a7d4d90612cf7908990600401615740565b600060405180830381600087803b158015612d1157600080fd5b505af1158015612d25573d6000803e3d6000fd5b505050508073ffffffffffffffffffffffffffffffffffffffff16631249c58b876040518263ffffffff1660e01b81526004016000604051808303818588803b158015612d7157600080fd5b505af1158015612d85573d6000803e3d6000fd5b505050505050612e93565b612db173ffffffffffffffffffffffffffffffffffffffff881683876138be565b6040517fa0712d68000000000000000000000000000000000000000000000000000000008152829060009073ffffffffffffffffffffffffffffffffffffffff83169063a0712d6890612e08908a90600401615740565b602060405180830381600087803b158015612e2257600080fd5b505af1158015612e36573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e5a9190614a4d565b14612e91576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016111ec90615372565b505b61317d565b8173ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff16141561317d577f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc273ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff16141561309b576040517fdb006a750000000000000000000000000000000000000000000000000000000081524790839060009073ffffffffffffffffffffffffffffffffffffffff83169063db006a7590612f79908b90600401615740565b602060405180830381600087803b158015612f9357600080fd5b505af1158015612fa7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612fcb9190614a4d565b14613002576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016111ec9061542c565b47600061300f82856139a2565b90507f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc273ffffffffffffffffffffffffffffffffffffffff1663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b15801561307957600080fd5b505af115801561308d573d6000803e3d6000fd5b50505050505050505061317d565b6040517fdb006a75000000000000000000000000000000000000000000000000000000008152829060009073ffffffffffffffffffffffffffffffffffffffff83169063db006a75906130f2908a90600401615740565b602060405180830381600087803b15801561310c57600080fd5b505af1158015613120573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131449190614a4d565b1461317b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016111ec906154e6565b505b6117ef818773ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401610ce89190614ca9565b6000806060806000858060200190518101906131d6919061480c565b805191955093508392506002111561321a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016111ec9061507b565b8773ffffffffffffffffffffffffffffffffffffffff168360018551038151811061324157fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff1614613296576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016111ec9061510f565b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc273ffffffffffffffffffffffffffffffffffffffff16826000815181106132da57fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff161415613406576040517f2e1a7d4d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc21690632e1a7d4d90613370908a90600401615740565b600060405180830381600087803b15801561338a57600080fd5b505af115801561339e573d6000803e3d6000fd5b5050505086905073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee826000815181106133c757fe5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff1681525050613418565b61341884888560008151811061128357fe5b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc273ffffffffffffffffffffffffffffffffffffffff168260018151811061345c57fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff1614156134dd5773eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee826001815181106134a257fe5b602002602001019073ffffffffffffffffffffffffffffffffffffffff16908173ffffffffffffffffffffffffffffffffffffffff16815250505b60008473ffffffffffffffffffffffffffffffffffffffff1663d3a4acd3838560008151811061350957fe5b60200260200101518660018151811061351e57fe5b60200260200101518c600142600101306040518863ffffffff1660e01b815260040161354f96959493929190614cf1565b6020604051808303818588803b15801561356857600080fd5b505af115801561357c573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906135a19190614a4d565b90507f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc273ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff161415611fe2577f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc273ffffffffffffffffffffffffffffffffffffffff1663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b15801561365f57600080fd5b505af1158015613673573d6000803e3d6000fd5b505050505098975050505050505050565b60008060608380602001905181019061369d9190614601565b90925090506136c373ffffffffffffffffffffffffffffffffffffffff871683876138be565b600060608373ffffffffffffffffffffffffffffffffffffffff16836040516136ec9190614c8d565b6000604051808303816000865af19150503d8060008114613729576040519150601f19603f3d011682016040523d82523d6000602084013e61372e565b606091505b509150915081613741576137418161399a565b80806020019051810190611fe29190614a4d565b60008060008380602001905181019061376e91906148d6565b909250905061379473ffffffffffffffffffffffffffffffffffffffff881683876138be565b604080516101008101825273ffffffffffffffffffffffffffffffffffffffff898116825288811660208301528381168284015230606083015242608083015260a08201889052600160c0830152600060e083015291517fa5dcbcdf0000000000000000000000000000000000000000000000000000000081529184169163a5dcbcdf9161179d916004016156c9565b60008060608380602001905181019061383d9190614601565b909250905061386373ffffffffffffffffffffffffffffffffffffffff88168387613c2a565b6040517f65d02b0400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8316906365d02b049061179d908a908a9030906001908890600401614e69565b6040517fdd62ed3e000000000000000000000000000000000000000000000000000000008152819073ffffffffffffffffffffffffffffffffffffffff85169063dd62ed3e906139149030908790600401614cca565b60206040518083038186803b15801561392c57600080fd5b505afa158015613940573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139649190614a4d565b10156139955761399583837fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6140d4565b505050565b805160208201fd5b6000828211156139c0576139c06139bb600285856140f1565b61399a565b50900390565b60007f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc273ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1614613ac2576040517f06f2bf6200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8516906306f2bf6290613a6d908690600401614ca9565b60206040518083038186803b158015613a8557600080fd5b505afa158015613a99573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613abd9190614571565b613b64565b6040517f06f2bf6200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8516906306f2bf6290613b14908590600401614ca9565b60206040518083038186803b158015613b2c57600080fd5b505afa158015613b40573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b649190614571565b905073ffffffffffffffffffffffffffffffffffffffff81166113a2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016111ec906150d8565b6000828201838110156113a2576113a26139bb600086866140f1565b600082613bde57506000611b84565b82820282848281613beb57fe5b04146113a2576113a26139bb600186866140f1565b600081613c1657613c166139bb600385856140f1565b6000828481613c2157fe5b04949350505050565b606063a9059cbb60e01b8383604051602401613c47929190614d36565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909316929092179091529050613cd08482614196565b50505050565b60008082806020019051810190613ced9190614571565b90508073ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff161415613eea576040517f2e1a7d4d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc21690632e1a7d4d90613d95908790600401615740565b600060405180830381600087803b158015613daf57600080fd5b505af1158015613dc3573d6000803e3d6000fd5b50506040517fa1903eab00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84169250637a28fb889150829063a1903eab908890613e2490600090600401614ca9565b6020604051808303818588803b158015613e3d57600080fd5b505af1158015613e51573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190613e769190614a4d565b6040518263ffffffff1660e01b8152600401613e929190615740565b60206040518083038186803b158015613eaa57600080fd5b505afa158015613ebe573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ee29190614a4d565b9150506113a2565b6040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016111ec9061533d565b600080600083806020019051810190613f3591906148d6565b915091508173ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff16148015613f9f57508073ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff16145b1561401757613fc573ffffffffffffffffffffffffffffffffffffffff881682876138be565b6040517fea598cb000000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82169063ea598cb090612ac8908890600401615740565b8073ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff1614801561407d57508173ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff16145b15613eea576040517fde0e9a3e00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82169063de0e9a3e90612ac8908890600401615740565b606063095ea7b360e01b8383604051602401613c47929190614d36565b606063e946c1bb60e01b84848460405160240161411093929190614efc565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915290509392505050565b600060608373ffffffffffffffffffffffffffffffffffffffff16836040516141bf9190614c8d565b6000604051808303816000865af19150503d80600081146141fc576040519150601f19603f3d011682016040523d82523d6000602084013e614201565b606091505b509150915081614214576142148161399a565b805161425b57833b80614253576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016111ec906153cf565b505050614298565b602081511061428f57600061427182600061429c565b9050806001141561428457505050614298565b61428d8261399a565b505b613cd08161399a565b5050565b60006113a28383600081602001835110156142c4576142c46139bb60058551856020016142cd565b50016020015190565b6060632800659560e01b84848460405160240161411093929190614f1e565b6040518060800160405280600080191681526020016000815260200160008152602001606081525090565b60408051608081018252600080825260208201819052918101829052606081019190915290565b604080518082019091526000808252602082015290565b600082601f830112614365578081fd5b815161437861437382615884565b61585d565b81815291506020808301908481018184028601820187101561439957600080fd5b60005b848110156143c15781516143af81615922565b8452928201929082019060010161439c565b505050505092915050565b600082601f8301126143dc578081fd5b81356143ea614373826158a4565b915080825283602082850101111561440157600080fd5b8060208401602084013760009082016020015292915050565b600082601f83011261442a578081fd5b8151614438614373826158a4565b915080825283602082850101111561444f57600080fd5b6144608160208401602086016158f6565b5092915050565b8035611b8481615922565b600060a08284031215614483578081fd5b61448d60a061585d565b905081518152602082015160208201526040820151604082015260608201516060820152608082015167ffffffffffffffff8111156144cb57600080fd5b6144d78482850161441a565b60808301525092915050565b6000608082840312156144f4578081fd5b6144fe608061585d565b9050815161450b81615922565b815260208201517fffffffff000000000000000000000000000000000000000000000000000000008116811461454057600080fd5b6020820152604082015161455381615955565b6040820152606082015161456681615955565b606082015292915050565b600060208284031215614582578081fd5b81516113a281615922565b6000806000606084860312156145a1578182fd5b83516145ac81615922565b602085015190935067ffffffffffffffff808211156145c9578384fd5b6145d587838801614355565b935060408601519150808211156145ea578283fd5b506145f786828701614355565b9150509250925092565b60008060408385031215614613578182fd5b825161461e81615922565b602084015190925067ffffffffffffffff81111561463a578182fd5b6146468582860161441a565b9150509250929050565b600080600060608486031215614664578081fd5b835161466f81615922565b602085015190935067ffffffffffffffff81111561468b578182fd5b6146978682870161441a565b925050604084015190509250925092565b600060208083850312156146ba578182fd5b825167ffffffffffffffff8111156146d0578283fd5b8301601f810185136146e0578283fd5b80516146ee61437382615884565b818152838101908385018584028501860189101561470a578687fd5b8694505b8385101561472c57805183526001949094019391850191850161470e565b50979650505050505050565b600060208284031215614749578081fd5b5035919050565b600080600060608486031215614764578081fd5b835161476f81615922565b8093505060208085015167ffffffffffffffff8082111561478e578384fd5b818701915087601f8301126147a1578384fd5b81516147af61437382615884565b81815284810190848601875b848110156147e4576147d28d8984518a0101614472565b845292870192908701906001016147bb565b505060408a015190975094505050808311156147fe578384fd5b50506145f786828701614355565b6000806040838503121561481e578182fd5b825161482981615922565b602084015190925067ffffffffffffffff811115614845578182fd5b61464685828601614355565b600080600060608486031215614865578081fd5b835161487081615922565b602085015190935061488181615922565b604085015190925061489281615947565b809150509250925092565b600080604083850312156148af578182fd5b82516148ba81615922565b60208401519092506148cb81615947565b809150509250929050565b600080604083850312156148e8578182fd5b82516148f381615922565b60208401519092506148cb81615922565b600080600060608486031215614918578081fd5b835161492381615922565b602085015160409095015190969495509392505050565b6000806000806080858703121561494f578182fd5b843567ffffffffffffffff80821115614966578384fd5b9086019060808289031215614979578384fd5b614983608061585d565b8235815260208301356020820152604083013560408201526060830135828111156149ac578586fd5b6149b88a8286016143cc565b6060830152508096505050506149d18660208701614467565b92506149e08660408701614467565b9396929550929360600135925050565b600060808284031215614a01578081fd5b6113a283836144e3565b600060408284031215614a1c578081fd5b614a26604061585d565b8251614a3181615922565b81526020830151614a4181615922565b60208201529392505050565b600060208284031215614a5e578081fd5b5051919050565b60008060408385031215614a77578182fd5b82359150602083013567ffffffffffffffff811115614a94578182fd5b614646858286016143cc565b60008060408385031215614ab2578182fd5b505080516020909101519092909150565b600060208284031215614ad4578081fd5b815160ff811681146113a2578182fd5b6000815180845260208085019450808401835b83811015614b2957815173ffffffffffffffffffffffffffffffffffffffff1687529582019590820190600101614af7565b509495945050505050565b6000815180845260208085019450808401835b83811015614b2957815187529582019590820190600101614b47565b60008151808452614b7b8160208601602086016158f6565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b600081518352602082015160208401526040820151604084015260608201516060840152608082015160a06080850152610d4660a0850182614b63565b6000815160a08452614bff60a0850182614b63565b905073ffffffffffffffffffffffffffffffffffffffff60208401511660208501526040830151604085015260608301516060850152608083015160808501528091505092915050565b73ffffffffffffffffffffffffffffffffffffffff808251168352602082015115156020840152806040830151166040840152506060810151151560608301525050565b60008251614c9f8184602087016158f6565b9190910192915050565b73ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b73ffffffffffffffffffffffffffffffffffffffff92831681529116602082015260400190565b73ffffffffffffffffffffffffffffffffffffffff9687168152948616602086015260408501939093526060840191909152608083015290911660a082015260c00190565b73ffffffffffffffffffffffffffffffffffffffff929092168252602082015260400190565b73ffffffffffffffffffffffffffffffffffffffff93841681526020810192909252909116604082015260600190565b73ffffffffffffffffffffffffffffffffffffffff948516815260208101939093529216604082015261ffff909116606082015260800190565b901515815260200190565b94855273ffffffffffffffffffffffffffffffffffffffff93841660208601529190921660408401526060830191909152608082015260a00190565b9485526020850193909352604084019190915273ffffffffffffffffffffffffffffffffffffffff166060830152608082015260a00190565b948552602085019390935260408401919091526060830152608082015260a00190565b600073ffffffffffffffffffffffffffffffffffffffff8088168352808716602084015280861660408401525083606083015260a06080830152614eb060a0830184614b63565b979650505050505050565b73ffffffffffffffffffffffffffffffffffffffff958616815260208101949094529190931660408301526060820192909252608081019190915260a00190565b6060810160048510614f0a57fe5b938152602081019290925260409091015290565b6060810160088510614f0a57fe5b6000610120808301614f3d8a6158e6565b8452602080850192909252885190819052610140808501928281028601909101918a8201855b82811015614faf577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec0888603018652614f9d858351614bad565b95840195945090830190600101614f63565b505050508381036040850152614fc58189614ae4565b915050614fd56060840187614c49565b82810360e0840152614fe78186614b34565b91505082610100830152979650505050505050565b600f94850b81529290930b6020830152604082015260ff909116606082015260800190565b600f95860b81529390940b6020840152604083019190915260ff166060820152608081019190915260a00190565b928352602083019190915273ffffffffffffffffffffffffffffffffffffffff16604082015260600190565b6020808252602e908201527f4d6978696e42616e636f7256332f504154485f4c454e4754485f4d5553545f4260408201527f455f41545f4c454153545f54574f000000000000000000000000000000000000606082015260800190565b60208082526018908201527f4d6978696e556e69737761702f4e4f5f45584348414e47450000000000000000604082015260600190565b6020808252603a908201527f4d6978696e42616e636f7256332f4c4153545f454c454d454e545f4f465f504160408201527f54485f4d5553545f4d415443485f4f55545055545f544f4b454e000000000000606082015260800190565b6020808252603a908201527f4d6978696e4b79626572446d6d2f4c4153545f454c454d454e545f4f465f504160408201527f54485f4d5553545f4d415443485f4f55545055545f544f4b454e000000000000606082015260800190565b60208082526021908201527f556e65787065637465642042616c616e63657256324261746368206f7574707560408201527f7400000000000000000000000000000000000000000000000000000000000000606082015260800190565b6020808252602f908201527f4d6978696e43727970746f436f6d2f504154485f4c454e4754485f4d5553545f60408201527f42455f41545f4c454153545f54574f0000000000000000000000000000000000606082015260800190565b6020808252602f908201527f4d6978696e556e697377617056322f504154485f4c454e4754485f4d5553545f60408201527f42455f41545f4c454153545f54574f0000000000000000000000000000000000606082015260800190565b60208082526023908201527f4d6978696e4b79626572446d6d2f41525241595f4c454e4754485f4d49534d4160408201527f5443480000000000000000000000000000000000000000000000000000000000606082015260800190565b6020808252818101527f4d6978696e4c69646f2f554e535550504f525445445f544f4b454e5f50414952604082015260600190565b60208082526023908201527f4d6978696e436f6d706f756e642f4641494c45445f544f5f4d494e545f43544f60408201527f4b454e0000000000000000000000000000000000000000000000000000000000606082015260800190565b60208082526027908201527f696e76616c696420746f6b656e20616464726573732c20636f6e7461696e732060408201527f6e6f20636f646500000000000000000000000000000000000000000000000000606082015260800190565b60208082526025908201527f4d6978696e436f6d706f756e642f4641494c45445f544f5f52454445454d5f4360408201527f4554484552000000000000000000000000000000000000000000000000000000606082015260800190565b6020808252602f908201527f4d6978696e4b79626572446d6d2f504f4f4c535f4c454e4754485f4d5553545f60408201527f42455f41545f4c454153545f4f4e450000000000000000000000000000000000606082015260800190565b60208082526025908201527f4d6978696e436f6d706f756e642f4641494c45445f544f5f52454445454d5f4360408201527f544f4b454e000000000000000000000000000000000000000000000000000000606082015260800190565b60208082526022908201527f4d6978696e4161766556322f554e535550504f525445445f544f4b454e5f504160408201527f4952000000000000000000000000000000000000000000000000000000000000606082015260800190565b6020808252603b908201527f4d6978696e556e697377617056322f4c4153545f454c454d454e545f4f465f5060408201527f4154485f4d5553545f4d415443485f4f55545055545f544f4b454e0000000000606082015260800190565b6020808252603b908201527f4d6978696e43727970746f436f6d2f4c4153545f454c454d454e545f4f465f5060408201527f4154485f4d5553545f4d415443485f4f55545055545f544f4b454e0000000000606082015260800190565b6000602082526113a26020830184614bea565b60006020825282516080602084015261568960a0840182614b63565b905073ffffffffffffffffffffffffffffffffffffffff602085015116604084015260408401516060840152606084015160808401528091505092915050565b60006101008201905073ffffffffffffffffffffffffffffffffffffffff808451168352806020850151166020840152806040850151166040840152806060850151166060840152506080830151608083015260a083015160a083015260c083015160c083015260e083015160e083015292915050565b90815260200190565b600087825286602083015260c0604083015261576860c0830187614ae4565b828103606084015261577a8187614ae4565b73ffffffffffffffffffffffffffffffffffffffff959095166080840152505060a00152949350505050565b600086825285602083015260a060408301526157c560a0830186614ae4565b73ffffffffffffffffffffffffffffffffffffffff94909416606083015250608001529392505050565b6000848252836020830152606060408301526100df6060830184614b63565b94855260208501939093526040840191909152606083015273ffffffffffffffffffffffffffffffffffffffff16608082015260a00190565b9283526020830191909152604082015260600190565b60405181810167ffffffffffffffff8111828210171561587c57600080fd5b604052919050565b600067ffffffffffffffff82111561589a578081fd5b5060209081020190565b600067ffffffffffffffff8211156158ba578081fd5b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b80600281106158f157fe5b919050565b60005b838110156159115781810151838201526020016158f9565b83811115613cd05750506000910152565b73ffffffffffffffffffffffffffffffffffffffff8116811461594457600080fd5b50565b801515811461594457600080fd5b80600f0b811461594457600080fdfea26469706673582212208fa636d6f56d0c6e65470285120a4eef938a8aae07a48694062402d894437fed64736f6c634300060c0033

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

000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2

-----Decoded View---------------
Arg [0] : weth (address): 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2

-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
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.