false
true
0

Contract Address Details

0xDA9aBA4eACF54E0273f56dfFee6B8F1e20B23Bba

Contract Name
PulseXSwapRouter
Creator
0x30e22a–c72539 at 0x269af1–b239fc
Balance
0 PLS ( )
Tokens
Fetching tokens...
Transactions
7,605,728 Transactions
Transfers
0 Transfers
Gas Used
0
Last Balance Update
25854667
Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
Contract name:
PulseXSwapRouter




Optimization enabled
true
Compiler version
v0.7.6+commit.7338295f




Optimization runs
1000000
EVM Version
istanbul




Verified at
2024-09-20T16:27:51.147511Z

Constructor Arguments

0x0000000000000000000000001715a3e4a142d8b698131108995174f37aeba10d00000000000000000000000029ea7545def87022badc76323f373ea1e707c5230000000000000000000000001973e61a0518a3cfcbfb7a840ee9ac7ba35562a0000000000000000000000000a1077a294dde1b09bb078844df40758a5d0f9a27

Arg [0] (address) : 0x1715a3e4a142d8b698131108995174f37aeba10d
Arg [1] (address) : 0x29ea7545def87022badc76323f373ea1e707c523
Arg [2] (address) : 0x1973e61a0518a3cfcbfb7a840ee9ac7ba35562a0
Arg [3] (address) : 0xa1077a294dde1b09bb078844df40758a5d0f9a27

              

contracts/PulseXSwapRouter.sol

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity =0.7.6;
pragma abicoder v2;

import './interfaces/IPulseXSwapRouter.sol';
import './V1SwapRouter.sol';
import './V2SwapRouter.sol';
import './StableSwapRouter.sol';
import './base/MulticallExtended.sol';
import './base/SelfPermit.sol';

/// @title PulseX V1, V2 and Stable Swap Router
contract PulseXSwapRouter is IPulseXSwapRouter, V1SwapRouter, V2SwapRouter, StableSwapRouter, MulticallExtended, SelfPermit {
    constructor(
        address _factoryV1,
        address factoryV2,
        address _stableInfo,
        address _WETH9
    ) ImmutableState(_factoryV1, factoryV2, _WETH9) StableSwapRouter(_stableInfo) {}
}
        

contracts/base/SelfPermit.sol

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import '@openzeppelin/contracts/drafts/IERC20Permit.sol';

import '../interfaces/ISelfPermit.sol';
import '../interfaces/IERC20PermitAllowed.sol';

/// @title Self Permit
/// @notice Functionality to call permit on any EIP-2612-compliant token for use in the route
/// @dev These functions are expected to be embedded in multicalls to allow EOAs to approve a contract and call a function
/// that requires an approval in a single transaction.
abstract contract SelfPermit is ISelfPermit {
    /// @inheritdoc ISelfPermit
    function selfPermit(
        address token,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) public payable override {
        IERC20Permit(token).permit(msg.sender, address(this), value, deadline, v, r, s);
    }

    /// @inheritdoc ISelfPermit
    function selfPermitIfNecessary(
        address token,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external payable override {
        if (IERC20(token).allowance(msg.sender, address(this)) < value) selfPermit(token, value, deadline, v, r, s);
    }

    /// @inheritdoc ISelfPermit
    function selfPermitAllowed(
        address token,
        uint256 nonce,
        uint256 expiry,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) public payable override {
        IERC20PermitAllowed(token).permit(msg.sender, address(this), nonce, expiry, true, v, r, s);
    }

    /// @inheritdoc ISelfPermit
    function selfPermitAllowedIfNecessary(
        address token,
        uint256 nonce,
        uint256 expiry,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external payable override {
        if (IERC20(token).allowance(msg.sender, address(this)) < type(uint256).max)
            selfPermitAllowed(token, nonce, expiry, v, r, s);
    }
}
          

contracts/libraries/PulseXLibraryV2.sol

pragma solidity >=0.5.0;

import '../interfaces/IPulseXPair.sol';
import './LowGasSafeMath.sol';

library PulseXLibraryV2 {
    using LowGasSafeMath for uint;

    // returns sorted token addresses, used to handle return values from pairs sorted in this order
    function sortTokens(address tokenA, address tokenB) internal pure returns (address token0, address token1) {
        require(tokenA != tokenB);
        (token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
        require(token0 != address(0));
    }

    // calculates the CREATE2 address for a pair without making any external calls
    function pairFor(address factory, address tokenA, address tokenB) internal pure returns (address pair) {
        (address token0, address token1) = sortTokens(tokenA, tokenB);
        pair = address(uint(keccak256(abi.encodePacked(
                hex'ff',
                factory,
                keccak256(abi.encodePacked(token0, token1)),
                hex'5dff1ac2d132f5ac2841294c6e9fc0ebafae8d447fac7996ef21c21112f411f1' // init code hash
            ))));
    }

    // fetches and sorts the reserves for a pair
    function getReserves(
        address factory,
        address tokenA,
        address tokenB
    ) internal view returns (uint256 reserveA, uint256 reserveB) {
        (address token0, ) = sortTokens(tokenA, tokenB);
        (uint256 reserve0, uint256 reserve1, ) = IPulseXPair(pairFor(factory, tokenA, tokenB)).getReserves();
        (reserveA, reserveB) = tokenA == token0 ? (reserve0, reserve1) : (reserve1, reserve0);
    }

    // given an input amount of an asset and pair reserves, returns the maximum output amount of the other asset
    function getAmountOut(
        uint256 amountIn,
        uint256 reserveIn,
        uint256 reserveOut
    ) internal pure returns (uint256 amountOut) {
        require(amountIn > 0, 'INSUFFICIENT_INPUT_AMOUNT');
        require(reserveIn > 0 && reserveOut > 0);
        uint256 amountInWithFee = amountIn.mul(9971);
        uint256 numerator = amountInWithFee.mul(reserveOut);
        uint256 denominator = reserveIn.mul(10000).add(amountInWithFee);
        amountOut = numerator / denominator;
    }

    // given an output amount of an asset and pair reserves, returns a required input amount of the other asset
    function getAmountIn(
        uint256 amountOut,
        uint256 reserveIn,
        uint256 reserveOut
    ) internal pure returns (uint256 amountIn) {
        require(amountOut > 0, 'INSUFFICIENT_OUTPUT_AMOUNT');
        require(reserveIn > 0 && reserveOut > 0);
        uint256 numerator = reserveIn.mul(amountOut).mul(10000);
        uint256 denominator = reserveOut.sub(amountOut).mul(9971);
        amountIn = (numerator / denominator).add(1);
    }

    // performs chained getAmountIn calculations on any number of pairs
    function getAmountsIn(
        address factory,
        uint256 amountOut,
        address[] memory path
    ) internal view returns (uint256[] memory amounts) {
        require(path.length >= 2);
        amounts = new uint256[](path.length);
        amounts[amounts.length - 1] = amountOut;
        for (uint256 i = path.length - 1; i > 0; i--) {
            (uint256 reserveIn, uint256 reserveOut) = getReserves(factory, path[i - 1], path[i]);
            amounts[i - 1] = getAmountIn(amounts[i], reserveIn, reserveOut);
        }
    }
}
          

contracts/base/ImmutableState.sol

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity =0.7.6;

import '../interfaces/IImmutableState.sol';

/// @title Immutable state
/// @notice Immutable state used by the swap router
abstract contract ImmutableState is IImmutableState {
    /// @inheritdoc IImmutableState
    address public immutable override factoryV1;
    /// @inheritdoc IImmutableState
    address public immutable override factoryV2;
    /// @inheritdoc IImmutableState
    address public immutable override WETH9;

    constructor(address _factoryV1, address _factoryV2, address _WETH9) {
        factoryV1 = _factoryV1;
        factoryV2 = _factoryV2;
        WETH9 = _WETH9;
    }
}
          

contracts/interfaces/IPeripheryPaymentsExtended.sol

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.5;

import './IPeripheryPayments.sol';

/// @title Periphery Payments Extended
/// @notice Functions to ease deposits and withdrawals of ETH and tokens
interface IPeripheryPaymentsExtended is IPeripheryPayments {
    /// @notice Unwraps the contract's WETH9 balance and sends it to msg.sender as ETH.
    /// @dev The amountMinimum parameter prevents malicious contracts from stealing WETH9 from users.
    /// @param amountMinimum The minimum amount of WETH9 to unwrap
    function unwrapWETH9(uint256 amountMinimum) external payable;

    /// @notice Wraps the contract's ETH balance into WETH9
    /// @dev The resulting WETH9 is custodied by the router, thus will require further distribution
    /// @param value The amount of ETH to wrap
    function wrapETH(uint256 value) external payable;

    /// @notice Transfers the full amount of a token held by this contract to msg.sender
    /// @dev The amountMinimum parameter prevents malicious contracts from stealing the token from users
    /// @param token The contract address of the token which will be transferred to msg.sender
    /// @param amountMinimum The minimum amount of token required for a transfer
    function sweepToken(address token, uint256 amountMinimum) external payable;

    /// @notice Transfers the specified amount of a token from the msg.sender to address(this)
    /// @param token The token to pull
    /// @param value The amount to pay
    function pull(address token, uint256 value) external payable;
}
          

contracts/interfaces/IV2SwapRouter.sol

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.5;
pragma abicoder v2;

/// @title Router token swapping functionality
/// @notice Functions for swapping tokens via PulseX V2
interface IV2SwapRouter {
    /// @notice Swaps `amountIn` of one token for as much as possible of another token
    /// @dev Setting `amountIn` to 0 will cause the contract to look up its own balance,
    /// and swap the entire amount, enabling contracts to send tokens before calling this function.
    /// @param amountIn The amount of token to swap
    /// @param amountOutMin The minimum amount of output that must be received
    /// @param path The ordered list of tokens to swap through
    /// @param to The recipient address
    /// @return amountOut The amount of the received token
    function swapExactTokensForTokensV2(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to
    ) external payable returns (uint256 amountOut);

    /// @notice Swaps as little as possible of one token for an exact amount of another token
    /// @param amountOut The amount of token to swap for
    /// @param amountInMax The maximum amount of input that the caller will pay
    /// @param path The ordered list of tokens to swap through
    /// @param to The recipient address
    /// @return amountIn The amount of token to pay
    function swapTokensForExactTokensV2(
        uint256 amountOut,
        uint256 amountInMax,
        address[] calldata path,
        address to
    ) external payable returns (uint256 amountIn);
}
          

contracts/interfaces/IPulseXPair.sol

pragma solidity >=0.5.0;

interface IPulseXPair {
    event Approval(address indexed owner, address indexed spender, uint value);
    event Transfer(address indexed from, address indexed to, uint value);

    function name() external pure returns (string memory);
    function symbol() external pure returns (string memory);
    function decimals() external pure returns (uint8);
    function totalSupply() external view returns (uint);
    function balanceOf(address owner) external view returns (uint);
    function allowance(address owner, address spender) external view returns (uint);

    function approve(address spender, uint value) external returns (bool);
    function transfer(address to, uint value) external returns (bool);
    function transferFrom(address from, address to, uint value) external returns (bool);

    function DOMAIN_SEPARATOR() external view returns (bytes32);
    function PERMIT_TYPEHASH() external pure returns (bytes32);
    function nonces(address owner) external view returns (uint);

    function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;

    event Mint(address indexed sender, uint amount0, uint amount1, address indexed senderOrigin);
    event Burn(address indexed sender, uint amount0, uint amount1, address indexed to, address indexed senderOrigin);
    event Swap(
        address indexed sender,
        uint amount0In,
        uint amount1In,
        uint amount0Out,
        uint amount1Out,
        address indexed to
    );
    event Sync(uint112 reserve0, uint112 reserve1);

    function MINIMUM_LIQUIDITY() external pure returns (uint);
    function factory() external view returns (address);
    function token0() external view returns (address);
    function token1() external view returns (address);
    function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
    function price0CumulativeLast() external view returns (uint);
    function price1CumulativeLast() external view returns (uint);
    function kLast() external view returns (uint);

    function mint(address to, address senderOrigin) external returns (uint liquidity);
    function burn(address to, address senderOrigin) external returns (uint amount0, uint amount1);
    function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;
    function skim(address to) external;
    function sync() external;

    function initialize(address, address) external;
}
          

contracts/StableSwapRouter.sol

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity =0.7.6;
pragma abicoder v2;

import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import '@openzeppelin/contracts/utils/ReentrancyGuard.sol';

import './interfaces/IStableSwapRouter.sol';
import './interfaces/IStableSwap.sol';
import './libraries/StableSwapLibrary.sol';
import './libraries/Constants.sol';

import './base/PeripheryPaymentsWithFeeExtended.sol';

/// @title PulseX Stable Swap Router
abstract contract StableSwapRouter is IStableSwapRouter, PeripheryPaymentsWithFeeExtended, ReentrancyGuard {
    address public stableSwapInfo;

    event SetStableSwap(address indexed factory, address indexed info);

    constructor( address _stableSwapInfo) {
        stableSwapInfo = _stableSwapInfo;
    }

    /// `refundETH` should be called at very end of all swaps
    function _swap(
        address[] memory path,
        address[] memory swapContracts
    ) private {
        for (uint256 i; i < path.length - 1; i++) {
            (address input, address output) = (path[i], path[i + 1]);
            (uint256 k, uint256 j) = StableSwapLibrary.getStableInfo(swapContracts[i], input, output);
            uint256 amountIn_ = IERC20(input).balanceOf(address(this));
            TransferHelper.safeApprove(input, swapContracts[i], amountIn_);
            IStableSwap(swapContracts[i]).exchange(k, j, amountIn_, 0);
        }
    }

    function exactInputStableSwap(
        address[] calldata path,
        address[] calldata swapContracts,
        uint256 amountIn,
        uint256 amountOutMin,
        address to
    ) external payable override nonReentrant returns (uint256 amountOut) {
        IERC20 srcToken = IERC20(path[0]);
        IERC20 dstToken = IERC20(path[path.length - 1]);

        // use amountIn == Constants.CONTRACT_BALANCE as a flag to swap the entire balance of the contract
        bool hasAlreadyPaid;
        if (amountIn == Constants.CONTRACT_BALANCE) {
            hasAlreadyPaid = true;
            amountIn = srcToken.balanceOf(address(this));
        }

        if (!hasAlreadyPaid) {
            pay(address(srcToken), msg.sender, address(this), amountIn);
        }

        _swap(path, swapContracts);

        amountOut = dstToken.balanceOf(address(this));
        require(amountOut >= amountOutMin);

        // find and replace to addresses
        if (to == Constants.MSG_SENDER) to = msg.sender;
        else if (to == Constants.ADDRESS_THIS) to = address(this);

        if (to != address(this)) pay(address(dstToken), address(this), to, amountOut);
    }

    function exactOutputStableSwap(
        address[] calldata path,
        address[] calldata swapContracts,
        uint256 amountOut,
        uint256 amountInMax,
        address to
    ) external payable override nonReentrant returns (uint256 amountIn) {
        amountIn = StableSwapLibrary.getStableAmountsIn(swapContracts[0], stableSwapInfo, path, amountOut)[0];
        require(amountIn <= amountInMax);

        pay(path[0], msg.sender, address(this), amountIn);

        _swap(path, swapContracts);

        // find and replace to addresses
        if (to == Constants.MSG_SENDER) to = msg.sender;
        else if (to == Constants.ADDRESS_THIS) to = address(this);

        if (to != address(this)) pay(path[path.length - 1], address(this), to, amountOut);    
    }
}
          

contracts/base/PeripheryPaymentsExtended.sol

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.5;

import './PeripheryPayments.sol';
import '../libraries/TransferHelper.sol';
import '../interfaces/IPeripheryPaymentsExtended.sol';

abstract contract PeripheryPaymentsExtended is IPeripheryPaymentsExtended, PeripheryPayments {
    /// @inheritdoc IPeripheryPaymentsExtended
    function unwrapWETH9(uint256 amountMinimum) external payable override {
        unwrapWETH9(amountMinimum, msg.sender);
    }

    /// @inheritdoc IPeripheryPaymentsExtended
    function wrapETH(uint256 value) external payable override {
        IWETH9(WETH9).deposit{value: value}();
    }

    /// @inheritdoc IPeripheryPaymentsExtended
    function sweepToken(address token, uint256 amountMinimum) external payable override {
        sweepToken(token, amountMinimum, msg.sender);
    }

    /// @inheritdoc IPeripheryPaymentsExtended
    function pull(address token, uint256 value) external payable override {
        TransferHelper.safeTransferFrom(token, msg.sender, address(this), value);
    }
}
          

contracts/base/BlockTimestamp.sol

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity =0.7.6;

/// @title Function for getting block timestamp
/// @dev Base contract that is overridden for tests
abstract contract BlockTimestamp {
    /// @dev Method that exists purely to be overridden for tests
    /// @return The current block timestamp
    function _blockTimestamp() internal view virtual returns (uint256) {
        return block.timestamp;
    }
}
          

contracts/interfaces/IStableSwapInfo.sol

// SPDX-License-Identifier: MIT
pragma solidity =0.7.6;
pragma abicoder v2;

interface IStableSwapInfo {
    function get_dx(
        address _swap,
        uint256 i,
        uint256 j,
        uint256 dy,
        uint256 max_dx
    ) external view returns (uint256);
}
          

contracts/interfaces/IMulticallExtended.sol

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.5;
pragma abicoder v2;

import './IMulticall.sol';

/// @title MulticallExtended interface
/// @notice Enables calling multiple methods in a single call to the contract with optional validation
interface IMulticallExtended is IMulticall {
    /// @notice Call multiple functions in the current contract and return the data from all of them if they all succeed
    /// @dev The `msg.value` should not be trusted for any method callable from multicall.
    /// @param deadline The time by which this function must be called before failing
    /// @param data The encoded function data for each of the calls to make to this contract
    /// @return results The results from each of the calls passed in via data
    function multicall(uint256 deadline, bytes[] calldata data) external payable returns (bytes[] memory results);

    /// @notice Call multiple functions in the current contract and return the data from all of them if they all succeed
    /// @dev The `msg.value` should not be trusted for any method callable from multicall.
    /// @param previousBlockhash The expected parent blockHash
    /// @param data The encoded function data for each of the calls to make to this contract
    /// @return results The results from each of the calls passed in via data
    function multicall(bytes32 previousBlockhash, bytes[] calldata data)
        external
        payable
        returns (bytes[] memory results);
}
          

contracts/libraries/TransferHelper.sol

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.6.0;

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

library TransferHelper {
    /// @notice Transfers tokens from the targeted address to the given destination
    /// @notice Errors with 'STF' if transfer fails
    /// @param token The contract address of the token to be transferred
    /// @param from The originating address from which the tokens will be transferred
    /// @param to The destination address of the transfer
    /// @param value The amount to be transferred
    function safeTransferFrom(
        address token,
        address from,
        address to,
        uint256 value
    ) internal {
        (bool success, bytes memory data) =
            token.call(abi.encodeWithSelector(IERC20.transferFrom.selector, from, to, value));
        require(success && (data.length == 0 || abi.decode(data, (bool))), 'STF');
    }

    /// @notice Transfers tokens from msg.sender to a recipient
    /// @dev Errors with ST if transfer fails
    /// @param token The contract address of the token which will be transferred
    /// @param to The recipient of the transfer
    /// @param value The value of the transfer
    function safeTransfer(
        address token,
        address to,
        uint256 value
    ) internal {
        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20.transfer.selector, to, value));
        require(success && (data.length == 0 || abi.decode(data, (bool))), 'ST');
    }

    /// @notice Approves the stipulated contract to spend the given allowance in the given token
    /// @dev Errors with 'SA' if transfer fails
    /// @param token The contract address of the token to be approved
    /// @param to The target of the approval
    /// @param value The amount of the given token the target will be allowed to spend
    function safeApprove(
        address token,
        address to,
        uint256 value
    ) internal {
        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20.approve.selector, to, value));
        require(success && (data.length == 0 || abi.decode(data, (bool))), 'SA');
    }

    /// @notice Transfers ETH to the recipient address
    /// @dev Fails with `STE`
    /// @param to The destination of the transfer
    /// @param value The value to be transferred
    function safeTransferETH(address to, uint256 value) internal {
        (bool success, ) = to.call{value: value}(new bytes(0));
        require(success, 'STE');
    }
}
          

contracts/base/Multicall.sol

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity =0.7.6;
pragma abicoder v2;

import '../interfaces/IMulticall.sol';

/// @title Multicall
/// @notice Enables calling multiple methods in a single call to the contract
abstract contract Multicall is IMulticall {
    /// @inheritdoc IMulticall
    function multicall(bytes[] calldata data) public payable override returns (bytes[] memory results) {
        results = new bytes[](data.length);
        for (uint256 i = 0; i < data.length; i++) {
            (bool success, bytes memory result) = address(this).delegatecall(data[i]);

            if (!success) {
                // Next 5 lines from https://ethereum.stackexchange.com/a/83577
                if (result.length < 68) revert();
                assembly {
                    result := add(result, 0x04)
                }
                revert(abi.decode(result, (string)));
            }

            results[i] = result;
        }
    }
}
          

contracts/interfaces/ISelfPermit.sol

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.5;

/// @title Self Permit
/// @notice Functionality to call permit on any EIP-2612-compliant token for use in the route
interface ISelfPermit {
    /// @notice Permits this contract to spend a given token from `msg.sender`
    /// @dev The `owner` is always msg.sender and the `spender` is always address(this).
    /// @param token The address of the token spent
    /// @param value The amount that can be spent of token
    /// @param deadline A timestamp, the current blocktime must be less than or equal to this timestamp
    /// @param v Must produce valid secp256k1 signature from the holder along with `r` and `s`
    /// @param r Must produce valid secp256k1 signature from the holder along with `v` and `s`
    /// @param s Must produce valid secp256k1 signature from the holder along with `r` and `v`
    function selfPermit(
        address token,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external payable;

    /// @notice Permits this contract to spend a given token from `msg.sender`
    /// @dev The `owner` is always msg.sender and the `spender` is always address(this).
    /// Can be used instead of #selfPermit to prevent calls from failing due to a frontrun of a call to #selfPermit
    /// @param token The address of the token spent
    /// @param value The amount that can be spent of token
    /// @param deadline A timestamp, the current blocktime must be less than or equal to this timestamp
    /// @param v Must produce valid secp256k1 signature from the holder along with `r` and `s`
    /// @param r Must produce valid secp256k1 signature from the holder along with `v` and `s`
    /// @param s Must produce valid secp256k1 signature from the holder along with `r` and `v`
    function selfPermitIfNecessary(
        address token,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external payable;

    /// @notice Permits this contract to spend the sender's tokens for permit signatures that have the `allowed` parameter
    /// @dev The `owner` is always msg.sender and the `spender` is always address(this)
    /// @param token The address of the token spent
    /// @param nonce The current nonce of the owner
    /// @param expiry The timestamp at which the permit is no longer valid
    /// @param v Must produce valid secp256k1 signature from the holder along with `r` and `s`
    /// @param r Must produce valid secp256k1 signature from the holder along with `v` and `s`
    /// @param s Must produce valid secp256k1 signature from the holder along with `r` and `v`
    function selfPermitAllowed(
        address token,
        uint256 nonce,
        uint256 expiry,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external payable;

    /// @notice Permits this contract to spend the sender's tokens for permit signatures that have the `allowed` parameter
    /// @dev The `owner` is always msg.sender and the `spender` is always address(this)
    /// Can be used instead of #selfPermitAllowed to prevent calls from failing due to a frontrun of a call to #selfPermitAllowed.
    /// @param token The address of the token spent
    /// @param nonce The current nonce of the owner
    /// @param expiry The timestamp at which the permit is no longer valid
    /// @param v Must produce valid secp256k1 signature from the holder along with `r` and `s`
    /// @param r Must produce valid secp256k1 signature from the holder along with `v` and `s`
    /// @param s Must produce valid secp256k1 signature from the holder along with `r` and `v`
    function selfPermitAllowedIfNecessary(
        address token,
        uint256 nonce,
        uint256 expiry,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external payable;
}
          

contracts/interfaces/IPeripheryPaymentsWithFeeExtended.sol

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.5;

import './IPeripheryPaymentsWithFee.sol';
import './IPeripheryPaymentsExtended.sol';

/// @title Periphery Payments With Fee Extended
/// @notice Functions to ease deposits and withdrawals of ETH
interface IPeripheryPaymentsWithFeeExtended is IPeripheryPaymentsExtended, IPeripheryPaymentsWithFee {
    /// @notice Unwraps the contract's WETH9 balance and sends it to msg.sender as ETH, with a percentage between
    /// 0 (exclusive), and 1 (inclusive) going to feeRecipient
    /// @dev The amountMinimum parameter prevents malicious contracts from stealing WETH9 from users.
    function unwrapWETH9WithFee(
        uint256 amountMinimum,
        uint256 feeBips,
        address feeRecipient
    ) external payable;

    /// @notice Transfers the full amount of a token held by this contract to msg.sender, with a percentage between
    /// 0 (exclusive) and 1 (inclusive) going to feeRecipient
    /// @dev The amountMinimum parameter prevents malicious contracts from stealing the token from users
    function sweepTokenWithFee(
        address token,
        uint256 amountMinimum,
        uint256 feeBips,
        address feeRecipient
    ) external payable;
}
          

contracts/interfaces/IPeripheryPaymentsWithFee.sol

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.5;

import './IPeripheryPayments.sol';

/// @title Periphery Payments
/// @notice Functions to ease deposits and withdrawals of ETH
interface IPeripheryPaymentsWithFee is IPeripheryPayments {
    /// @notice Unwraps the contract's WETH9 balance and sends it to recipient as ETH, with a percentage between
    /// 0 (exclusive), and 1 (inclusive) going to feeRecipient
    /// @dev The amountMinimum parameter prevents malicious contracts from stealing WETH9 from users.
    function unwrapWETH9WithFee(
        uint256 amountMinimum,
        address recipient,
        uint256 feeBips,
        address feeRecipient
    ) external payable;

    /// @notice Transfers the full amount of a token held by this contract to recipient, with a percentage between
    /// 0 (exclusive) and 1 (inclusive) going to feeRecipient
    /// @dev The amountMinimum parameter prevents malicious contracts from stealing the token from users
    function sweepTokenWithFee(
        address token,
        uint256 amountMinimum,
        address recipient,
        uint256 feeBips,
        address feeRecipient
    ) external payable;
}
          

contracts/libraries/PulseXLibraryV1.sol

pragma solidity >=0.5.0;

import '../interfaces/IPulseXPair.sol';
import './LowGasSafeMath.sol';

library PulseXLibraryV1 {
    using LowGasSafeMath for uint;

    // returns sorted token addresses, used to handle return values from pairs sorted in this order
    function sortTokens(address tokenA, address tokenB) internal pure returns (address token0, address token1) {
        require(tokenA != tokenB);
        (token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
        require(token0 != address(0));
    }

    // calculates the CREATE2 address for a pair without making any external calls
    function pairFor(address factory, address tokenA, address tokenB) internal pure returns (address pair) {
        (address token0, address token1) = sortTokens(tokenA, tokenB);
        pair = address(uint(keccak256(abi.encodePacked(
                hex'ff',
                factory,
                keccak256(abi.encodePacked(token0, token1)),
                hex'59fffffddd756cba9095128e53f3291a6ba38b21e3df744936e7289326555d62' // init code hash
            ))));
    }

    // fetches and sorts the reserves for a pair
    function getReserves(
        address factory,
        address tokenA,
        address tokenB
    ) internal view returns (uint256 reserveA, uint256 reserveB) {
        (address token0, ) = sortTokens(tokenA, tokenB);
        (uint256 reserve0, uint256 reserve1, ) = IPulseXPair(pairFor(factory, tokenA, tokenB)).getReserves();
        (reserveA, reserveB) = tokenA == token0 ? (reserve0, reserve1) : (reserve1, reserve0);
    }

    // given an input amount of an asset and pair reserves, returns the maximum output amount of the other asset
    function getAmountOut(
        uint256 amountIn,
        uint256 reserveIn,
        uint256 reserveOut
    ) internal pure returns (uint256 amountOut) {
        require(amountIn > 0, 'INSUFFICIENT_INPUT_AMOUNT');
        require(reserveIn > 0 && reserveOut > 0);
        uint256 amountInWithFee = amountIn.mul(9971);
        uint256 numerator = amountInWithFee.mul(reserveOut);
        uint256 denominator = reserveIn.mul(10000).add(amountInWithFee);
        amountOut = numerator / denominator;
    }

    // given an output amount of an asset and pair reserves, returns a required input amount of the other asset
    function getAmountIn(
        uint256 amountOut,
        uint256 reserveIn,
        uint256 reserveOut
    ) internal pure returns (uint256 amountIn) {
        require(amountOut > 0, 'INSUFFICIENT_OUTPUT_AMOUNT');
        require(reserveIn > 0 && reserveOut > 0);
        uint256 numerator = reserveIn.mul(amountOut).mul(10000);
        uint256 denominator = reserveOut.sub(amountOut).mul(9971);
        amountIn = (numerator / denominator).add(1);
    }

    // performs chained getAmountIn calculations on any number of pairs
    function getAmountsIn(
        address factory,
        uint256 amountOut,
        address[] memory path
    ) internal view returns (uint256[] memory amounts) {
        require(path.length >= 2);
        amounts = new uint256[](path.length);
        amounts[amounts.length - 1] = amountOut;
        for (uint256 i = path.length - 1; i > 0; i--) {
            (uint256 reserveIn, uint256 reserveOut) = getReserves(factory, path[i - 1], path[i]);
            amounts[i - 1] = getAmountIn(amounts[i], reserveIn, reserveOut);
        }
    }
}
          

contracts/V2SwapRouter.sol

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity =0.7.6;
pragma abicoder v2;

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

import './libraries/LowGasSafeMath.sol';
import './interfaces/IV2SwapRouter.sol';
import './base/ImmutableState.sol';
import './base/PeripheryPaymentsWithFeeExtended.sol';
import './libraries/Constants.sol';
import './libraries/PulseXLibraryV2.sol';

/// @title PulseX V2 Swap Router
/// @notice Router for stateless execution of swaps against PulseX V2
abstract contract V2SwapRouter is IV2SwapRouter, ImmutableState, PeripheryPaymentsWithFeeExtended {
    using LowGasSafeMath for uint256;

    // supports fee-on-transfer tokens
    // requires the initial amount to have already been sent to the first pair
    function _swapV2(address[] memory path, address _to) private {
        for (uint256 i; i < path.length - 1; i++) {
            (address input, address output) = (path[i], path[i + 1]);
            (address token0, ) = PulseXLibraryV2.sortTokens(input, output);
            IPulseXPair pair = IPulseXPair(PulseXLibraryV2.pairFor(factoryV2, input, output));
            uint256 amountInput;
            uint256 amountOutput;
            // scope to avoid stack too deep errors
            {
                (uint256 reserve0, uint256 reserve1, ) = pair.getReserves();
                (uint256 reserveInput, uint256 reserveOutput) =
                    input == token0 ? (reserve0, reserve1) : (reserve1, reserve0);
                amountInput = IERC20(input).balanceOf(address(pair)).sub(reserveInput);
                amountOutput = PulseXLibraryV2.getAmountOut(amountInput, reserveInput, reserveOutput);
            }
            (uint256 amount0Out, uint256 amount1Out) =
                input == token0 ? (uint256(0), amountOutput) : (amountOutput, uint256(0));
            address to = i < path.length - 2 ? PulseXLibraryV2.pairFor(factoryV2, output, path[i + 2]) : _to;
            pair.swap(amount0Out, amount1Out, to, new bytes(0));
        }
    }

    /// @inheritdoc IV2SwapRouter
    function swapExactTokensForTokensV2(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to
    ) external payable override returns (uint256 amountOut) {
        // use amountIn == Constants.CONTRACT_BALANCE as a flag to swap the entire balance of the contract
        bool hasAlreadyPaid;
        if (amountIn == Constants.CONTRACT_BALANCE) {
            hasAlreadyPaid = true;
            amountIn = IERC20(path[0]).balanceOf(address(this));
        }

        pay(
            path[0],
            hasAlreadyPaid ? address(this) : msg.sender,
            PulseXLibraryV2.pairFor(factoryV2, path[0], path[1]),
            amountIn
        );

        // find and replace to addresses
        if (to == Constants.MSG_SENDER) to = msg.sender;
        else if (to == Constants.ADDRESS_THIS) to = address(this);

        uint256 balanceBefore = IERC20(path[path.length - 1]).balanceOf(to);

        _swapV2(path, to);

        amountOut = IERC20(path[path.length - 1]).balanceOf(to).sub(balanceBefore);
        require(amountOut >= amountOutMin, 'Too little received');
    }

    /// @inheritdoc IV2SwapRouter
    function swapTokensForExactTokensV2(
        uint256 amountOut,
        uint256 amountInMax,
        address[] calldata path,
        address to
    ) external payable override returns (uint256 amountIn) {
        amountIn = PulseXLibraryV2.getAmountsIn(factoryV2, amountOut, path)[0];
        require(amountIn <= amountInMax, 'Too much requested');

        pay(path[0], msg.sender, PulseXLibraryV2.pairFor(factoryV2, path[0], path[1]), amountIn);

        // find and replace to addresses
        if (to == Constants.MSG_SENDER) to = msg.sender;
        else if (to == Constants.ADDRESS_THIS) to = address(this);

        _swapV2(path, to);
    }
}
          

contracts/interfaces/IMulticall.sol

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.5;
pragma abicoder v2;

/// @title Multicall interface
/// @notice Enables calling multiple methods in a single call to the contract
interface IMulticall {
    /// @notice Call multiple functions in the current contract and return the data from all of them if they all succeed
    /// @dev The `msg.value` should not be trusted for any method callable from multicall.
    /// @param data The encoded function data for each of the calls to make to this contract
    /// @return results The results from each of the calls passed in via data
    function multicall(bytes[] calldata data) external payable returns (bytes[] memory results);
}
          

@openzeppelin/contracts/utils/ReentrancyGuard.sol

// SPDX-License-Identifier: MIT

pragma solidity ^0.7.0;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

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

    uint256 private _status;

    constructor () {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and make it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        // On the first call to nonReentrant, _notEntered will be true
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

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

        _;

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

contracts/libraries/StableSwapLibrary.sol

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity =0.7.6;
pragma abicoder v2;

import '../interfaces/IStableSwap.sol';
import '../interfaces/IStableSwapInfo.sol';
import './LowGasSafeMath.sol';

library StableSwapLibrary {
    using LowGasSafeMath for uint256;

    // get the pool info in stable swap
    function getStableInfo(
        address swapContract,
        address input,
        address output
    ) internal view returns (uint256 i, uint256 j) {
        require(swapContract != address(0), "getStableInfo: invalid pool address");
        IStableSwap swap = IStableSwap(swapContract);
        uint256 coins = swap.N_COINS();
        if (coins == 2) {
            address t0 = swap.coins(0);
            i = input == t0 ? 0 : 1;
            j = (i == 0) ? 1 : 0;
        } else if (coins == 3) {
            address t0 = swap.coins(0);
            address t1 = swap.coins(1);
            address t2 = swap.coins(2);

            if (input == t0) i = 0;
            else if (input == t1) i = 1;
            else if (input == t2) i = 2;

            if (output == t0) j = 0;
            else if (output == t1) j = 1;
            else if (output == t2) j = 2;
        }
    }

    function getStableAmountsIn(
        address swapContract,
        address stableSwapInfo,
        address[] memory path,
        uint256 amountOut
    ) internal view returns (uint256[] memory amounts) {
        uint256 length = path.length;
        require(length >= 2, "getStableAmountsIn: incorrect length");

        amounts = new uint256[](length);
        amounts[length - 1] = amountOut;

        for (uint256 i = length - 1; i > 0; i--) {
            uint256 last = i - 1;
            (uint256 k, uint256 j) = getStableInfo(swapContract, path[last], path[i]);
            amounts[last] = IStableSwapInfo(stableSwapInfo).get_dx(swapContract, k, j, amounts[i], type(uint256).max);
        }
    }
}
          

contracts/interfaces/IV1SwapRouter.sol

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.5;
pragma abicoder v2;

/// @title Router token swapping functionality
/// @notice Functions for swapping tokens via PulseX V1
interface IV1SwapRouter {
    /// @notice Swaps `amountIn` of one token for as much as possible of another token
    /// @dev Setting `amountIn` to 0 will cause the contract to look up its own balance,
    /// and swap the entire amount, enabling contracts to send tokens before calling this function.
    /// @param amountIn The amount of token to swap
    /// @param amountOutMin The minimum amount of output that must be received
    /// @param path The ordered list of tokens to swap through
    /// @param to The recipient address
    /// @return amountOut The amount of the received token
    function swapExactTokensForTokensV1(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to
    ) external payable returns (uint256 amountOut);

    /// @notice Swaps as little as possible of one token for an exact amount of another token
    /// @param amountOut The amount of token to swap for
    /// @param amountInMax The maximum amount of input that the caller will pay
    /// @param path The ordered list of tokens to swap through
    /// @param to The recipient address
    /// @return amountIn The amount of token to pay
    function swapTokensForExactTokensV1(
        uint256 amountOut,
        uint256 amountInMax,
        address[] calldata path,
        address to
    ) external payable returns (uint256 amountIn);
}
          

contracts/interfaces/IWETH9.sol

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

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

/// @title Interface for WETH9
interface IWETH9 is IERC20 {
    /// @notice Deposit ether to get wrapped ether
    function deposit() external payable;

    /// @notice Withdraw wrapped ether to get ether
    function withdraw(uint256) external;
}
          

contracts/base/PeripheryValidationExtended.sol

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity =0.7.6;

import './PeripheryValidation.sol';

abstract contract PeripheryValidationExtended is PeripheryValidation {
    modifier checkPreviousBlockhash(bytes32 previousBlockhash) {
        require(blockhash(block.number - 1) == previousBlockhash, 'Blockhash');
        _;
    }
}
          

contracts/base/MulticallExtended.sol

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity =0.7.6;
pragma abicoder v2;

import './Multicall.sol';
import '../interfaces/IMulticallExtended.sol';
import '../base/PeripheryValidationExtended.sol';

/// @title Multicall
/// @notice Enables calling multiple methods in a single call to the contract
abstract contract MulticallExtended is IMulticallExtended, Multicall, PeripheryValidationExtended {
    /// @inheritdoc IMulticallExtended
    function multicall(uint256 deadline, bytes[] calldata data)
        external
        payable
        override
        checkDeadline(deadline)
        returns (bytes[] memory)
    {
        return multicall(data);
    }

    /// @inheritdoc IMulticallExtended
    function multicall(bytes32 previousBlockhash, bytes[] calldata data)
        external
        payable
        override
        checkPreviousBlockhash(previousBlockhash)
        returns (bytes[] memory)
    {
        return multicall(data);
    }
}
          

contracts/interfaces/IPeripheryPayments.sol

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.5;

/// @title Periphery Payments
/// @notice Functions to ease deposits and withdrawals of ETH
interface IPeripheryPayments {
    /// @notice Unwraps the contract's WETH9 balance and sends it to recipient as ETH.
    /// @dev The amountMinimum parameter prevents malicious contracts from stealing WETH9 from users.
    /// @param amountMinimum The minimum amount of WETH9 to unwrap
    /// @param recipient The address receiving ETH
    function unwrapWETH9(uint256 amountMinimum, address recipient) external payable;

    /// @notice Refunds any ETH balance held by this contract to the `msg.sender`
    /// @dev Useful for bundling with mint or increase liquidity that uses ether, or exact output swaps
    /// that use ether for the input amount
    function refundETH() external payable;

    /// @notice Transfers the full amount of a token held by this contract to recipient
    /// @dev The amountMinimum parameter prevents malicious contracts from stealing the token from users
    /// @param token The contract address of the token which will be transferred to `recipient`
    /// @param amountMinimum The minimum amount of token required for a transfer
    /// @param recipient The destination address of the token
    function sweepToken(
        address token,
        uint256 amountMinimum,
        address recipient
    ) external payable;
}
          

contracts/libraries/LowGasSafeMath.sol

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.0;

/// @title Optimized overflow and underflow safe math operations
/// @notice Contains methods for doing math operations that revert on overflow or underflow for minimal gas cost
library LowGasSafeMath {
    /// @notice Returns x + y, reverts if sum overflows uint256
    /// @param x The augend
    /// @param y The addend
    /// @return z The sum of x and y
    function add(uint256 x, uint256 y) internal pure returns (uint256 z) {
        require((z = x + y) >= x);
    }

    /// @notice Returns x - y, reverts if underflows
    /// @param x The minuend
    /// @param y The subtrahend
    /// @return z The difference of x and y
    function sub(uint256 x, uint256 y) internal pure returns (uint256 z) {
        require((z = x - y) <= x);
    }

    /// @notice Returns x * y, reverts if overflows
    /// @param x The multiplicand
    /// @param y The multiplier
    /// @return z The product of x and y
    function mul(uint256 x, uint256 y) internal pure returns (uint256 z) {
        require(x == 0 || (z = x * y) / x == y);
    }

    /// @notice Returns x + y, reverts if overflows or underflows
    /// @param x The augend
    /// @param y The addend
    /// @return z The sum of x and y
    function add(int256 x, int256 y) internal pure returns (int256 z) {
        require((z = x + y) >= x == (y >= 0));
    }

    /// @notice Returns x - y, reverts if overflows or underflows
    /// @param x The minuend
    /// @param y The subtrahend
    /// @return z The difference of x and y
    function sub(int256 x, int256 y) internal pure returns (int256 z) {
        require((z = x - y) <= x == (y >= 0));
    }
}
          

contracts/interfaces/IImmutableState.sol

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title Immutable state
/// @notice Functions that return immutable state of the router
interface IImmutableState {
    /// @return Returns the address of the PulseX V1 factory
    function factoryV1() external view returns (address);

    /// @return Returns the address of the PulseX V2 factory
    function factoryV2() external view returns (address);

    /// @return Returns the address of WETH9
    function WETH9() external view returns (address);
}
          

contracts/V1SwapRouter.sol

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity =0.7.6;
pragma abicoder v2;

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

import './libraries/LowGasSafeMath.sol';
import './interfaces/IV1SwapRouter.sol';
import './base/ImmutableState.sol';
import './base/PeripheryPaymentsWithFeeExtended.sol';
import './libraries/Constants.sol';
import './libraries/PulseXLibraryV1.sol';

/// @title PulseX V1 Swap Router
/// @notice Router for stateless execution of swaps against PulseX V1
abstract contract V1SwapRouter is IV1SwapRouter, ImmutableState, PeripheryPaymentsWithFeeExtended {
    using LowGasSafeMath for uint256;

    // supports fee-on-transfer tokens
    // requires the initial amount to have already been sent to the first pair
    function _swapV1(address[] memory path, address _to) private {
        for (uint256 i; i < path.length - 1; i++) {
            (address input, address output) = (path[i], path[i + 1]);
            (address token0, ) = PulseXLibraryV1.sortTokens(input, output);
            IPulseXPair pair = IPulseXPair(PulseXLibraryV1.pairFor(factoryV1, input, output));
            uint256 amountInput;
            uint256 amountOutput;
            // scope to avoid stack too deep errors
            {
                (uint256 reserve0, uint256 reserve1, ) = pair.getReserves();
                (uint256 reserveInput, uint256 reserveOutput) =
                    input == token0 ? (reserve0, reserve1) : (reserve1, reserve0);
                amountInput = IERC20(input).balanceOf(address(pair)).sub(reserveInput);
                amountOutput = PulseXLibraryV1.getAmountOut(amountInput, reserveInput, reserveOutput);
            }
            (uint256 amount0Out, uint256 amount1Out) =
                input == token0 ? (uint256(0), amountOutput) : (amountOutput, uint256(0));
            address to = i < path.length - 2 ? PulseXLibraryV1.pairFor(factoryV1, output, path[i + 2]) : _to;
            pair.swap(amount0Out, amount1Out, to, new bytes(0));
        }
    }

    /// @inheritdoc IV1SwapRouter
    function swapExactTokensForTokensV1(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to
    ) external payable override returns (uint256 amountOut) {
        // use amountIn == Constants.CONTRACT_BALANCE as a flag to swap the entire balance of the contract
        bool hasAlreadyPaid;
        if (amountIn == Constants.CONTRACT_BALANCE) {
            hasAlreadyPaid = true;
            amountIn = IERC20(path[0]).balanceOf(address(this));
        }

        pay(
            path[0],
            hasAlreadyPaid ? address(this) : msg.sender,
            PulseXLibraryV1.pairFor(factoryV1, path[0], path[1]),
            amountIn
        );

        // find and replace to addresses
        if (to == Constants.MSG_SENDER) to = msg.sender;
        else if (to == Constants.ADDRESS_THIS) to = address(this);

        uint256 balanceBefore = IERC20(path[path.length - 1]).balanceOf(to);

        _swapV1(path, to);

        amountOut = IERC20(path[path.length - 1]).balanceOf(to).sub(balanceBefore);
        require(amountOut >= amountOutMin, 'Too little received');
    }

    /// @inheritdoc IV1SwapRouter
    function swapTokensForExactTokensV1(
        uint256 amountOut,
        uint256 amountInMax,
        address[] calldata path,
        address to
    ) external payable override returns (uint256 amountIn) {
        amountIn = PulseXLibraryV1.getAmountsIn(factoryV1, amountOut, path)[0];
        require(amountIn <= amountInMax, 'Too much requested');

        pay(path[0], msg.sender, PulseXLibraryV1.pairFor(factoryV1, path[0], path[1]), amountIn);

        // find and replace to addresses
        if (to == Constants.MSG_SENDER) to = msg.sender;
        else if (to == Constants.ADDRESS_THIS) to = address(this);

        _swapV1(path, to);
    }
}
          

contracts/base/PeripheryPayments.sol

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.5;

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

import '../interfaces/IPeripheryPayments.sol';
import '../interfaces/IWETH9.sol';

import '../libraries/TransferHelper.sol';

import './ImmutableState.sol';

abstract contract PeripheryPayments is IPeripheryPayments, ImmutableState {
    receive() external payable {
        require(msg.sender == WETH9, 'Not WETH9');
    }

    /// @inheritdoc IPeripheryPayments
    function unwrapWETH9(uint256 amountMinimum, address recipient) public payable override {
        uint256 balanceWETH9 = IWETH9(WETH9).balanceOf(address(this));
        require(balanceWETH9 >= amountMinimum, 'Insufficient WETH9');

        if (balanceWETH9 > 0) {
            IWETH9(WETH9).withdraw(balanceWETH9);
            TransferHelper.safeTransferETH(recipient, balanceWETH9);
        }
    }

    /// @inheritdoc IPeripheryPayments
    function sweepToken(
        address token,
        uint256 amountMinimum,
        address recipient
    ) public payable override {
        uint256 balanceToken = IERC20(token).balanceOf(address(this));
        require(balanceToken >= amountMinimum, 'Insufficient token');

        if (balanceToken > 0) {
            TransferHelper.safeTransfer(token, recipient, balanceToken);
        }
    }

    /// @inheritdoc IPeripheryPayments
    function refundETH() external payable override {
        if (address(this).balance > 0) TransferHelper.safeTransferETH(msg.sender, address(this).balance);
    }

    /// @param token The token to pay
    /// @param payer The entity that must pay
    /// @param recipient The entity that will receive payment
    /// @param value The amount to pay
    function pay(
        address token,
        address payer,
        address recipient,
        uint256 value
    ) internal {
        if (token == WETH9 && address(this).balance >= value) {
            // pay with WETH9
            IWETH9(WETH9).deposit{value: value}(); // wrap only what is needed to pay
            IWETH9(WETH9).transfer(recipient, value);
        } else if (payer == address(this)) {
            // pay with tokens already in the contract (for the exact input multihop case)
            TransferHelper.safeTransfer(token, recipient, value);
        } else {
            // pull payment
            TransferHelper.safeTransferFrom(token, payer, recipient, value);
        }
    }
}
          

contracts/interfaces/IStableSwapRouter.sol

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity =0.7.6;
pragma abicoder v2;

/// @title Router token swapping functionality
/// @notice Functions for swapping tokens via PulseX Stable Swap
interface IStableSwapRouter {
    function exactInputStableSwap(
        address[] calldata path,
        address[] calldata swapContracts,
        uint256 amountIn,
        uint256 amountOutMin,
        address to
    ) external payable returns (uint256 amountOut);

    function exactOutputStableSwap(
        address[] calldata path,
        address[] calldata swapContracts,
        uint256 amountOut,
        uint256 amountInMax,
        address to
    ) external payable returns (uint256 amountIn);
}
          

contracts/interfaces/IStableSwap.sol

// SPDX-License-Identifier: MIT
pragma solidity =0.7.6;
pragma abicoder v2;

interface IStableSwap {
    // solium-disable-next-line mixedcase
    function get_dy(
        uint256 i,
        uint256 j,
        uint256 dx
    ) external view returns (uint256 dy);

    // solium-disable-next-line mixedcase
    function exchange(
        uint256 i,
        uint256 j,
        uint256 dx,
        uint256 minDy
    ) external payable;

    // solium-disable-next-line mixedcase
    function N_COINS() external view returns (uint256);

    // solium-disable-next-line mixedcase
    function coins(uint256 i) external view returns (address);

    // solium-disable-next-line mixedcase
    function balances(uint256 i) external view returns (uint256);

    // solium-disable-next-line mixedcase
    function A() external view returns (uint256);

    // solium-disable-next-line mixedcase
    function fee() external view returns (uint256);
}
          

@openzeppelin/contracts/drafts/IERC20Permit.sol

// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on `{IERC20-approve}`, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over `owner`'s tokens,
     * given `owner`'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     */
    function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external;

    /**
     * @dev Returns the current nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address owner) external view returns (uint256);

    /**
     * @dev Returns the domain separator used in the encoding of the signature for `permit`, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}
          

contracts/interfaces/IPulseXSwapRouter.sol

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.5;
pragma abicoder v2;

import './ISelfPermit.sol';
import './IV1SwapRouter.sol';
import './IV2SwapRouter.sol';
import './IStableSwapRouter.sol';
import './IMulticallExtended.sol';

/// @title Router token swapping functionality
interface IPulseXSwapRouter is IV1SwapRouter, IV2SwapRouter, IStableSwapRouter, IMulticallExtended, ISelfPermit {

}
          

contracts/base/PeripheryPaymentsWithFeeExtended.sol

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.5;

import './PeripheryPaymentsWithFee.sol';
import '../interfaces/IPeripheryPaymentsWithFeeExtended.sol';
import './PeripheryPaymentsExtended.sol';

abstract contract PeripheryPaymentsWithFeeExtended is
    IPeripheryPaymentsWithFeeExtended,
    PeripheryPaymentsExtended,
    PeripheryPaymentsWithFee
{
    /// @inheritdoc IPeripheryPaymentsWithFeeExtended
    function unwrapWETH9WithFee(
        uint256 amountMinimum,
        uint256 feeBips,
        address feeRecipient
    ) external payable override {
        unwrapWETH9WithFee(amountMinimum, msg.sender, feeBips, feeRecipient);
    }

    /// @inheritdoc IPeripheryPaymentsWithFeeExtended
    function sweepTokenWithFee(
        address token,
        uint256 amountMinimum,
        uint256 feeBips,
        address feeRecipient
    ) external payable override {
        sweepTokenWithFee(token, amountMinimum, msg.sender, feeBips, feeRecipient);
    }
}
          

contracts/interfaces/IERC20PermitAllowed.sol

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title Interface for permit
/// @notice Interface used by DAI/CHAI for permit
interface IERC20PermitAllowed {
    /// @notice Approve the spender to spend some tokens via the holder signature
    /// @dev This is the permit interface used by DAI and CHAI
    /// @param holder The address of the token holder, the token owner
    /// @param spender The address of the token spender
    /// @param nonce The holder's nonce, increases at each call to permit
    /// @param expiry The timestamp at which the permit is no longer valid
    /// @param allowed Boolean that sets approval amount, true for type(uint256).max and false for 0
    /// @param v Must produce valid secp256k1 signature from the holder along with `r` and `s`
    /// @param r Must produce valid secp256k1 signature from the holder along with `v` and `s`
    /// @param s Must produce valid secp256k1 signature from the holder along with `r` and `v`
    function permit(
        address holder,
        address spender,
        uint256 nonce,
        uint256 expiry,
        bool allowed,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;
}
          

contracts/base/PeripheryValidation.sol

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity =0.7.6;

import './BlockTimestamp.sol';

abstract contract PeripheryValidation is BlockTimestamp {
    modifier checkDeadline(uint256 deadline) {
        require(_blockTimestamp() <= deadline, 'Transaction too old');
        _;
    }
}
          

contracts/libraries/Constants.sol

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity =0.7.6;

/// @title Constant state
/// @notice Constant state used by the swap router
library Constants {
    /// @dev Used for identifying cases when this contract's balance of a token is to be used
    uint256 internal constant CONTRACT_BALANCE = 0;

    /// @dev Used as a flag for identifying msg.sender, saves gas by sending more 0 bytes
    address internal constant MSG_SENDER = address(1);

    /// @dev Used as a flag for identifying address(this), saves gas by sending more 0 bytes
    address internal constant ADDRESS_THIS = address(2);
}
          

@openzeppelin/contracts/token/ERC20/IERC20.sol

// SPDX-License-Identifier: MIT

pragma solidity ^0.7.0;

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

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

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

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

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

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

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

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

contracts/base/PeripheryPaymentsWithFee.sol

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.5;

import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import '../libraries/LowGasSafeMath.sol';

import './PeripheryPayments.sol';
import '../interfaces/IPeripheryPaymentsWithFee.sol';

import '../interfaces/IWETH9.sol';
import '../libraries/TransferHelper.sol';

abstract contract PeripheryPaymentsWithFee is PeripheryPayments, IPeripheryPaymentsWithFee {
    using LowGasSafeMath for uint256;

    /// @inheritdoc IPeripheryPaymentsWithFee
    function unwrapWETH9WithFee(
        uint256 amountMinimum,
        address recipient,
        uint256 feeBips,
        address feeRecipient
    ) public payable override {
        require(feeBips > 0 && feeBips <= 100);

        uint256 balanceWETH9 = IWETH9(WETH9).balanceOf(address(this));
        require(balanceWETH9 >= amountMinimum, 'Insufficient WETH9');

        if (balanceWETH9 > 0) {
            IWETH9(WETH9).withdraw(balanceWETH9);
            uint256 feeAmount = balanceWETH9.mul(feeBips) / 10_000;
            if (feeAmount > 0) TransferHelper.safeTransferETH(feeRecipient, feeAmount);
            TransferHelper.safeTransferETH(recipient, balanceWETH9 - feeAmount);
        }
    }

    /// @inheritdoc IPeripheryPaymentsWithFee
    function sweepTokenWithFee(
        address token,
        uint256 amountMinimum,
        address recipient,
        uint256 feeBips,
        address feeRecipient
    ) public payable override {
        require(feeBips > 0 && feeBips <= 100);

        uint256 balanceToken = IERC20(token).balanceOf(address(this));
        require(balanceToken >= amountMinimum, 'Insufficient token');

        if (balanceToken > 0) {
            uint256 feeAmount = balanceToken.mul(feeBips) / 10_000;
            if (feeAmount > 0) TransferHelper.safeTransfer(token, feeRecipient, feeAmount);
            TransferHelper.safeTransfer(token, recipient, balanceToken - feeAmount);
        }
    }
}
          

Compiler Settings

{"outputSelection":{"*":{"*":["abi","evm.bytecode","evm.deployedBytecode","evm.methodIdentifiers","metadata"],"":["ast"]}},"optimizer":{"runs":1000000,"enabled":true},"metadata":{"bytecodeHash":"none"},"libraries":{},"evmVersion":"istanbul"}
              

Contract ABI

[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"address","name":"_factoryV1","internalType":"address"},{"type":"address","name":"factoryV2","internalType":"address"},{"type":"address","name":"_stableInfo","internalType":"address"},{"type":"address","name":"_WETH9","internalType":"address"}]},{"type":"event","name":"SetStableSwap","inputs":[{"type":"address","name":"factory","internalType":"address","indexed":true},{"type":"address","name":"info","internalType":"address","indexed":true}],"anonymous":false},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"WETH9","inputs":[]},{"type":"function","stateMutability":"payable","outputs":[{"type":"uint256","name":"amountOut","internalType":"uint256"}],"name":"exactInputStableSwap","inputs":[{"type":"address[]","name":"path","internalType":"address[]"},{"type":"address[]","name":"swapContracts","internalType":"address[]"},{"type":"uint256","name":"amountIn","internalType":"uint256"},{"type":"uint256","name":"amountOutMin","internalType":"uint256"},{"type":"address","name":"to","internalType":"address"}]},{"type":"function","stateMutability":"payable","outputs":[{"type":"uint256","name":"amountIn","internalType":"uint256"}],"name":"exactOutputStableSwap","inputs":[{"type":"address[]","name":"path","internalType":"address[]"},{"type":"address[]","name":"swapContracts","internalType":"address[]"},{"type":"uint256","name":"amountOut","internalType":"uint256"},{"type":"uint256","name":"amountInMax","internalType":"uint256"},{"type":"address","name":"to","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"factoryV1","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"factoryV2","inputs":[]},{"type":"function","stateMutability":"payable","outputs":[{"type":"bytes[]","name":"","internalType":"bytes[]"}],"name":"multicall","inputs":[{"type":"bytes32","name":"previousBlockhash","internalType":"bytes32"},{"type":"bytes[]","name":"data","internalType":"bytes[]"}]},{"type":"function","stateMutability":"payable","outputs":[{"type":"bytes[]","name":"","internalType":"bytes[]"}],"name":"multicall","inputs":[{"type":"uint256","name":"deadline","internalType":"uint256"},{"type":"bytes[]","name":"data","internalType":"bytes[]"}]},{"type":"function","stateMutability":"payable","outputs":[{"type":"bytes[]","name":"results","internalType":"bytes[]"}],"name":"multicall","inputs":[{"type":"bytes[]","name":"data","internalType":"bytes[]"}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"pull","inputs":[{"type":"address","name":"token","internalType":"address"},{"type":"uint256","name":"value","internalType":"uint256"}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"refundETH","inputs":[]},{"type":"function","stateMutability":"payable","outputs":[],"name":"selfPermit","inputs":[{"type":"address","name":"token","internalType":"address"},{"type":"uint256","name":"value","internalType":"uint256"},{"type":"uint256","name":"deadline","internalType":"uint256"},{"type":"uint8","name":"v","internalType":"uint8"},{"type":"bytes32","name":"r","internalType":"bytes32"},{"type":"bytes32","name":"s","internalType":"bytes32"}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"selfPermitAllowed","inputs":[{"type":"address","name":"token","internalType":"address"},{"type":"uint256","name":"nonce","internalType":"uint256"},{"type":"uint256","name":"expiry","internalType":"uint256"},{"type":"uint8","name":"v","internalType":"uint8"},{"type":"bytes32","name":"r","internalType":"bytes32"},{"type":"bytes32","name":"s","internalType":"bytes32"}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"selfPermitAllowedIfNecessary","inputs":[{"type":"address","name":"token","internalType":"address"},{"type":"uint256","name":"nonce","internalType":"uint256"},{"type":"uint256","name":"expiry","internalType":"uint256"},{"type":"uint8","name":"v","internalType":"uint8"},{"type":"bytes32","name":"r","internalType":"bytes32"},{"type":"bytes32","name":"s","internalType":"bytes32"}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"selfPermitIfNecessary","inputs":[{"type":"address","name":"token","internalType":"address"},{"type":"uint256","name":"value","internalType":"uint256"},{"type":"uint256","name":"deadline","internalType":"uint256"},{"type":"uint8","name":"v","internalType":"uint8"},{"type":"bytes32","name":"r","internalType":"bytes32"},{"type":"bytes32","name":"s","internalType":"bytes32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"stableSwapInfo","inputs":[]},{"type":"function","stateMutability":"payable","outputs":[{"type":"uint256","name":"amountOut","internalType":"uint256"}],"name":"swapExactTokensForTokensV1","inputs":[{"type":"uint256","name":"amountIn","internalType":"uint256"},{"type":"uint256","name":"amountOutMin","internalType":"uint256"},{"type":"address[]","name":"path","internalType":"address[]"},{"type":"address","name":"to","internalType":"address"}]},{"type":"function","stateMutability":"payable","outputs":[{"type":"uint256","name":"amountOut","internalType":"uint256"}],"name":"swapExactTokensForTokensV2","inputs":[{"type":"uint256","name":"amountIn","internalType":"uint256"},{"type":"uint256","name":"amountOutMin","internalType":"uint256"},{"type":"address[]","name":"path","internalType":"address[]"},{"type":"address","name":"to","internalType":"address"}]},{"type":"function","stateMutability":"payable","outputs":[{"type":"uint256","name":"amountIn","internalType":"uint256"}],"name":"swapTokensForExactTokensV1","inputs":[{"type":"uint256","name":"amountOut","internalType":"uint256"},{"type":"uint256","name":"amountInMax","internalType":"uint256"},{"type":"address[]","name":"path","internalType":"address[]"},{"type":"address","name":"to","internalType":"address"}]},{"type":"function","stateMutability":"payable","outputs":[{"type":"uint256","name":"amountIn","internalType":"uint256"}],"name":"swapTokensForExactTokensV2","inputs":[{"type":"uint256","name":"amountOut","internalType":"uint256"},{"type":"uint256","name":"amountInMax","internalType":"uint256"},{"type":"address[]","name":"path","internalType":"address[]"},{"type":"address","name":"to","internalType":"address"}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"sweepToken","inputs":[{"type":"address","name":"token","internalType":"address"},{"type":"uint256","name":"amountMinimum","internalType":"uint256"},{"type":"address","name":"recipient","internalType":"address"}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"sweepToken","inputs":[{"type":"address","name":"token","internalType":"address"},{"type":"uint256","name":"amountMinimum","internalType":"uint256"}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"sweepTokenWithFee","inputs":[{"type":"address","name":"token","internalType":"address"},{"type":"uint256","name":"amountMinimum","internalType":"uint256"},{"type":"uint256","name":"feeBips","internalType":"uint256"},{"type":"address","name":"feeRecipient","internalType":"address"}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"sweepTokenWithFee","inputs":[{"type":"address","name":"token","internalType":"address"},{"type":"uint256","name":"amountMinimum","internalType":"uint256"},{"type":"address","name":"recipient","internalType":"address"},{"type":"uint256","name":"feeBips","internalType":"uint256"},{"type":"address","name":"feeRecipient","internalType":"address"}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"unwrapWETH9","inputs":[{"type":"uint256","name":"amountMinimum","internalType":"uint256"},{"type":"address","name":"recipient","internalType":"address"}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"unwrapWETH9","inputs":[{"type":"uint256","name":"amountMinimum","internalType":"uint256"}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"unwrapWETH9WithFee","inputs":[{"type":"uint256","name":"amountMinimum","internalType":"uint256"},{"type":"address","name":"recipient","internalType":"address"},{"type":"uint256","name":"feeBips","internalType":"uint256"},{"type":"address","name":"feeRecipient","internalType":"address"}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"unwrapWETH9WithFee","inputs":[{"type":"uint256","name":"amountMinimum","internalType":"uint256"},{"type":"uint256","name":"feeBips","internalType":"uint256"},{"type":"address","name":"feeRecipient","internalType":"address"}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"wrapETH","inputs":[{"type":"uint256","name":"value","internalType":"uint256"}]},{"type":"receive","stateMutability":"payable"}]
              

Contract Creation Code

0x60e06040523480156200001157600080fd5b5060405162004b4038038062004b4083398101604081905262000034916200009e565b606093841b6001600160601b031990811660805292841b831660a05290921b1660c0526001600081905580546001600160a01b0319166001600160a01b03909216919091179055620000fa565b80516001600160a01b03811681146200009957600080fd5b919050565b60008060008060808587031215620000b4578384fd5b620000bf8562000081565b9350620000cf6020860162000081565b9250620000df6040860162000081565b9150620000ef6060860162000081565b905092959194509250565b60805160601c60a05160601c60c05160601c6149b16200018f600039806101d852806104c052806108aa52806109d45280610a7b5280610ea05280610fca52806124d1528061253152806125b2525080610e60528061160e5280611c775280611d4b5280612cd95280612e9c525080610549528061062652806107c45280611185528061270452806128c752506149b16000f3fe6080604052600436106101bb5760003560e01c80639b5c7a81116100ec578063cafe95a01161008a578063e0e189a011610064578063e0e189a014610460578063e90a182f14610473578063f2d5d56b14610486578063f3995c671461049957610266565b8063cafe95a014610427578063d4ef38de1461043a578063df2ab5bb1461044d57610266565b8063ac9650d8116100c6578063ac9650d8146103d9578063b85aa7af146103ec578063c2e3140a14610401578063c8bb18561461041457610266565b80639b5c7a81146103a0578063a4a78f0c146103b3578063ab0acea4146103c657610266565b806349404b7c116101595780634aa94288116101335780634aa94288146103525780635ae401dc1461036557806368e0d4e1146103785780639b2c0a371461038d57610266565b806349404b7c14610317578063496169971461032a5780634aa4a4fc1461033d57610266565b80631f0464d1116101955780631f0464d1146102af5780633068c554146102cf5780633957f453146102e25780634659a4941461030457610266565b806312210e8a1461026b5780631c58db4f146102735780631d4389f51461028657610266565b36610266573373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000161461026457604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600960248201527f4e6f742057455448390000000000000000000000000000000000000000000000604482015290519081900360640190fd5b005b600080fd5b6102646104ac565b610264610281366004614500565b6104be565b6102996102943660046145ca565b610542565b6040516102a69190614759565b60405180910390f35b6102c26102bd3660046143c8565b610725565b6040516102a691906146db565b6102646102dd36600461424b565b6107af565b3480156102ee57600080fd5b506102f76107c2565b6040516102a6919061467f565b610264610312366004614294565b6107e6565b610264610325366004614530565b6108a6565b610264610338366004614500565b610a6c565b34801561034957600080fd5b506102f7610a79565b6102996103603660046142f4565b610a9d565b6102c26103733660046143c8565b610de5565b34801561038457600080fd5b506102f7610e5e565b61026461039b36600461455f565b610e82565b6102996103ae3660046145ca565b611098565b6102646103c1366004614294565b61144c565b6102996103d43660046145ca565b611521565b6102c26103e7366004614388565b6117cd565b3480156103f857600080fd5b506102f7611927565b61026461040f366004614294565b611943565b6102996104223660046142f4565b6119f8565b6102996104353660046145ca565b611c70565b61026461044836600461459d565b611e21565b61026461045b3660046141ac565b611e2d565b61026461046e3660046141ed565b611f44565b610264610481366004614181565b6120aa565b610264610494366004614181565b6120b9565b6102646104a7366004614294565b6120c5565b47156104bc576104bc334761215d565b565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b15801561052657600080fd5b505af115801561053a573d6000803e3d6000fd5b505050505050565b60006105a27f0000000000000000000000000000000000000000000000000000000000000000878686808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152506122ab92505050565b6000815181106105ae57fe5b60200260200101519050848111156105fb576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105f29061482f565b60405180910390fd5b6106948484600081811061060b57fe5b90506020020160208101906106209190614142565b3361068e7f00000000000000000000000000000000000000000000000000000000000000008888600081811061065257fe5b90506020020160208101906106679190614142565b8989600181811061067457fe5b90506020020160208101906106899190614142565b6123e4565b846124cf565b73ffffffffffffffffffffffffffffffffffffffff8216600114156106bb573391506106de565b73ffffffffffffffffffffffffffffffffffffffff8216600214156106de573091505b61071c8484808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152508692506126ad915050565b95945050505050565b6060838060014303401461079a57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600960248201527f426c6f636b686173680000000000000000000000000000000000000000000000604482015290519081900360640190fd5b6107a484846117cd565b91505b509392505050565b6107bc8484338585611f44565b50505050565b7f000000000000000000000000000000000000000000000000000000000000000081565b604080517f8fcbaf0c00000000000000000000000000000000000000000000000000000000815233600482015230602482015260448101879052606481018690526001608482015260ff851660a482015260c4810184905260e48101839052905173ffffffffffffffffffffffffffffffffffffffff881691638fcbaf0c9161010480830192600092919082900301818387803b15801561088657600080fd5b505af115801561089a573d6000803e3d6000fd5b50505050505050505050565b60007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b15801561092f57600080fd5b505afa158015610943573d6000803e3d6000fd5b505050506040513d602081101561095957600080fd5b50519050828110156109cc57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f496e73756666696369656e742057455448390000000000000000000000000000604482015290519081900360640190fd5b8015610a67577f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16632e1a7d4d826040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b158015610a4557600080fd5b505af1158015610a59573d6000803e3d6000fd5b50505050610a67828261215d565b505050565b610a7681336108a6565b50565b7f000000000000000000000000000000000000000000000000000000000000000081565b600060026000541415610b1157604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b6002600090815588888281610b2257fe5b9050602002016020810190610b379190614142565b9050600089897fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8101818110610b6957fe5b9050602002016020810190610b7e9190614142565b9050600086610c3057506040517f70a0823100000000000000000000000000000000000000000000000000000000815260019073ffffffffffffffffffffffffffffffffffffffff8416906370a0823190610bdd90309060040161467f565b60206040518083038186803b158015610bf557600080fd5b505afa158015610c09573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c2d9190614518565b96505b80610c4157610c418333308a6124cf565b610cae8b8b8080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525050604080516020808f0282810182019093528e82529093508e92508d9182918501908490808284376000920191909152506129b292505050565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8316906370a0823190610d0090309060040161467f565b60206040518083038186803b158015610d1857600080fd5b505afa158015610d2c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d509190614518565b935085841015610d5f57600080fd5b73ffffffffffffffffffffffffffffffffffffffff851660011415610d8657339450610da9565b73ffffffffffffffffffffffffffffffffffffffff851660021415610da9573094505b73ffffffffffffffffffffffffffffffffffffffff85163014610dd257610dd2823087876124cf565b5050600160005550979650505050505050565b60608380610df1612b59565b111561079a57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f5472616e73616374696f6e20746f6f206f6c6400000000000000000000000000604482015290519081900360640190fd5b7f000000000000000000000000000000000000000000000000000000000000000081565b600082118015610e93575060648211155b610e9c57600080fd5b60007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015610f2557600080fd5b505afa158015610f39573d6000803e3d6000fd5b505050506040513d6020811015610f4f57600080fd5b5051905084811015610fc257604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f496e73756666696369656e742057455448390000000000000000000000000000604482015290519081900360640190fd5b8015611091577f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16632e1a7d4d826040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b15801561103b57600080fd5b505af115801561104f573d6000803e3d6000fd5b50505050600061271061106b8584612b5d90919063ffffffff16565b8161107257fe5b049050801561108557611085838261215d565b61053a8582840361215d565b5050505050565b6000808661114e5750600184846000816110ae57fe5b90506020020160208101906110c39190614142565b73ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b81526004016110fb919061467f565b60206040518083038186803b15801561111357600080fd5b505afa158015611127573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061114b9190614518565b96505b6111d98585600081811061115e57fe5b90506020020160208101906111739190614142565b8261117e5733611180565b305b6111d37f0000000000000000000000000000000000000000000000000000000000000000898960008181106111b157fe5b90506020020160208101906111c69190614142565b8a8a600181811061067457fe5b8a6124cf565b73ffffffffffffffffffffffffffffffffffffffff83166001141561120057339250611223565b73ffffffffffffffffffffffffffffffffffffffff831660021415611223573092505b600085857fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff810181811061125357fe5b90506020020160208101906112689190614142565b73ffffffffffffffffffffffffffffffffffffffff166370a08231856040518263ffffffff1660e01b81526004016112a0919061467f565b60206040518083038186803b1580156112b857600080fd5b505afa1580156112cc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112f09190614518565b90506113308686808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152508892506126ad915050565b6114058187877fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff810181811061136257fe5b90506020020160208101906113779190614142565b73ffffffffffffffffffffffffffffffffffffffff166370a08231876040518263ffffffff1660e01b81526004016113af919061467f565b60206040518083038186803b1580156113c757600080fd5b505afa1580156113db573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113ff9190614518565b90612b87565b925086831015611441576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105f290614866565b505095945050505050565b604080517fdd62ed3e00000000000000000000000000000000000000000000000000000000815233600482015230602482015290517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9173ffffffffffffffffffffffffffffffffffffffff89169163dd62ed3e91604480820192602092909190829003018186803b1580156114e157600080fd5b505afa1580156114f5573d6000803e3d6000fd5b505050506040513d602081101561150b57600080fd5b5051101561053a5761053a8686868686866107e6565b600080866115d757506001848460008161153757fe5b905060200201602081019061154c9190614142565b73ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401611584919061467f565b60206040518083038186803b15801561159c57600080fd5b505afa1580156115b0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115d49190614518565b96505b611676858560008181106115e757fe5b90506020020160208101906115fc9190614142565b826116075733611609565b305b6111d37f00000000000000000000000000000000000000000000000000000000000000008989600081811061163a57fe5b905060200201602081019061164f9190614142565b8a8a600181811061165c57fe5b90506020020160208101906116719190614142565b612b97565b73ffffffffffffffffffffffffffffffffffffffff83166001141561169d573392506116c0565b73ffffffffffffffffffffffffffffffffffffffff8316600214156116c0573092505b600085857fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81018181106116f057fe5b90506020020160208101906117059190614142565b73ffffffffffffffffffffffffffffffffffffffff166370a08231856040518263ffffffff1660e01b815260040161173d919061467f565b60206040518083038186803b15801561175557600080fd5b505afa158015611769573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061178d9190614518565b9050611330868680806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250889250612c82915050565b60608167ffffffffffffffff811180156117e657600080fd5b5060405190808252806020026020018201604052801561181a57816020015b60608152602001906001900390816118055790505b50905060005b82811015611920576000803086868581811061183857fe5b905060200281019061184a91906148f3565b60405161185892919061466f565b600060405180830381855af49150503d8060008114611893576040519150601f19603f3d011682016040523d82523d6000602084013e611898565b606091505b5091509150816118fe576044815110156118b157600080fd5b600481019050808060200190518101906118cb9190614412565b6040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105f29190614762565b8084848151811061190b57fe5b60209081029190910101525050600101611820565b5092915050565b60015473ffffffffffffffffffffffffffffffffffffffff1681565b604080517fdd62ed3e0000000000000000000000000000000000000000000000000000000081523360048201523060248201529051869173ffffffffffffffffffffffffffffffffffffffff89169163dd62ed3e91604480820192602092909190829003018186803b1580156119b857600080fd5b505afa1580156119cc573d6000803e3d6000fd5b505050506040513d60208110156119e257600080fd5b5051101561053a5761053a8686868686866120c5565b600060026000541415611a6c57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b6002600081905550611aee86866000818110611a8457fe5b9050602002016020810190611a999190614142565b600154604080516020808d0282810182019093528c825273ffffffffffffffffffffffffffffffffffffffff9093169290918d918d9182918501908490808284376000920191909152508a9250612f87915050565b600081518110611afa57fe5b6020026020010151905082811115611b1157600080fd5b611b3e88886000818110611b2157fe5b9050602002016020810190611b369190614142565b3330846124cf565b611bab88888080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525050604080516020808c0282810182019093528b82529093508b92508a9182918501908490808284376000920191909152506129b292505050565b73ffffffffffffffffffffffffffffffffffffffff821660011415611bd257339150611bf5565b73ffffffffffffffffffffffffffffffffffffffff821660021415611bf5573091505b73ffffffffffffffffffffffffffffffffffffffff82163014611c6057611c6088887fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8101818110611c4357fe5b9050602002016020810190611c589190614142565b3084876124cf565b6001600055979650505050505050565b6000611cd07f00000000000000000000000000000000000000000000000000000000000000008786868080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525061319f92505050565b600081518110611cdc57fe5b6020026020010151905084811115611d20576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105f29061482f565b611d9984846000818110611d3057fe5b9050602002016020810190611d459190614142565b3361068e7f000000000000000000000000000000000000000000000000000000000000000088886000818110611d7757fe5b9050602002016020810190611d8c9190614142565b8989600181811061165c57fe5b73ffffffffffffffffffffffffffffffffffffffff821660011415611dc057339150611de3565b73ffffffffffffffffffffffffffffffffffffffff821660021415611de3573091505b61071c848480806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250869250612c82915050565b610a6783338484610e82565b60008373ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015611e9657600080fd5b505afa158015611eaa573d6000803e3d6000fd5b505050506040513d6020811015611ec057600080fd5b5051905082811015611f3357604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f496e73756666696369656e7420746f6b656e0000000000000000000000000000604482015290519081900360640190fd5b80156107bc576107bc8483836132c9565b600082118015611f55575060648211155b611f5e57600080fd5b60008573ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015611fc757600080fd5b505afa158015611fdb573d6000803e3d6000fd5b505050506040513d6020811015611ff157600080fd5b505190508481101561206457604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f496e73756666696369656e7420746f6b656e0000000000000000000000000000604482015290519081900360640190fd5b801561053a5760006127106120798386612b5d565b8161208057fe5b0490508015612094576120948784836132c9565b6120a187868385036132c9565b50505050505050565b6120b5828233611e2d565b5050565b6120b58233308461349e565b604080517fd505accf000000000000000000000000000000000000000000000000000000008152336004820152306024820152604481018790526064810186905260ff8516608482015260a4810184905260c48101839052905173ffffffffffffffffffffffffffffffffffffffff88169163d505accf9160e480830192600092919082900301818387803b15801561088657600080fd5b6040805160008082526020820190925273ffffffffffffffffffffffffffffffffffffffff84169083906040518082805190602001908083835b602083106121d457805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101612197565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d8060008114612236576040519150601f19603f3d011682016040523d82523d6000602084013e61223b565b606091505b5050905080610a6757604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f5354450000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b60606002825110156122bc57600080fd5b815167ffffffffffffffff811180156122d457600080fd5b506040519080825280602002602001820160405280156122fe578160200160208202803683370190505b509050828160018351038151811061231257fe5b602090810291909101015281517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff015b80156107a75760008061237f8786600186038151811061235e57fe5b602002602001015187868151811061237257fe5b602002602001015161367b565b915091506123a184848151811061239257fe5b60200260200101518383613763565b8460018503815181106123b057fe5b602090810291909101015250507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01612342565b60008060006123f38585613839565b604080517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606094851b811660208084019190915293851b81166034830152825160288184030181526048830184528051908501207fff0000000000000000000000000000000000000000000000000000000000000060688401529a90941b9093166069840152607d8301989098527f59fffffddd756cba9095128e53f3291a6ba38b21e3df744936e7289326555d62609d808401919091528851808403909101815260bd909201909752805196019590952095945050505050565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff1614801561252a5750804710155b15612673577f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b15801561259757600080fd5b505af11580156125ab573d6000803e3d6000fd5b50505050507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663a9059cbb83836040518363ffffffff1660e01b8152600401808373ffffffffffffffffffffffffffffffffffffffff16815260200182815260200192505050602060405180830381600087803b15801561264157600080fd5b505af1158015612655573d6000803e3d6000fd5b505050506040513d602081101561266b57600080fd5b506107bc9050565b73ffffffffffffffffffffffffffffffffffffffff83163014156126a15761269c8483836132c9565b6107bc565b6107bc8484848461349e565b60005b6001835103811015610a67576000808483815181106126cb57fe5b60200260200101518584600101815181106126e257fe5b60200260200101519150915060006126fa8383613839565b509050600061272a7f000000000000000000000000000000000000000000000000000000000000000085856123e4565b90506000806000808473ffffffffffffffffffffffffffffffffffffffff16630902f1ac6040518163ffffffff1660e01b815260040160606040518083038186803b15801561277857600080fd5b505afa15801561278c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127b091906144bd565b506dffffffffffffffffffffffffffff1691506dffffffffffffffffffffffffffff1691506000808773ffffffffffffffffffffffffffffffffffffffff168a73ffffffffffffffffffffffffffffffffffffffff1614612812578284612815565b83835b91509150612856828b73ffffffffffffffffffffffffffffffffffffffff166370a082318a6040518263ffffffff1660e01b81526004016113af919061467f565b95506128638683836138de565b9450505050506000808573ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff16146128a7578260006128ab565b6000835b91509150600060028c51038a106128c2578a612903565b6129037f0000000000000000000000000000000000000000000000000000000000000000898e8d600201815181106128f657fe5b60200260200101516123e4565b604080516000815260208101918290527f022c0d9f0000000000000000000000000000000000000000000000000000000090915290915073ffffffffffffffffffffffffffffffffffffffff87169063022c0d9f9061296b908690869086906024810161489d565b600060405180830381600087803b15801561298557600080fd5b505af1158015612999573d6000803e3d6000fd5b50506001909b019a506126b09950505050505050505050565b60005b6001835103811015610a67576000808483815181106129d057fe5b60200260200101518584600101815181106129e757fe5b602002602001015191509150600080612a14868681518110612a0557fe5b602002602001015185856139b4565b9150915060008473ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401612a53919061467f565b60206040518083038186803b158015612a6b57600080fd5b505afa158015612a7f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612aa39190614518565b9050612ac385888881518110612ab557fe5b602002602001015183613ed8565b868681518110612acf57fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff16635b41b90884848460006040518563ffffffff1660e01b8152600401612b1694939291906148d8565b600060405180830381600087803b158015612b3057600080fd5b505af1158015612b44573d6000803e3d6000fd5b5050600190970196506129b595505050505050565b4290565b6000821580612b7857505081810281838281612b7557fe5b04145b612b8157600080fd5b92915050565b80820382811115612b8157600080fd5b6000806000612ba68585613839565b604080517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606094851b811660208084019190915293851b81166034830152825160288184030181526048830184528051908501207fff0000000000000000000000000000000000000000000000000000000000000060688401529a90941b9093166069840152607d8301989098527f5dff1ac2d132f5ac2841294c6e9fc0ebafae8d447fac7996ef21c21112f411f1609d808401919091528851808403909101815260bd909201909752805196019590952095945050505050565b60005b6001835103811015610a6757600080848381518110612ca057fe5b6020026020010151858460010181518110612cb757fe5b6020026020010151915091506000612ccf8383613839565b5090506000612cff7f00000000000000000000000000000000000000000000000000000000000000008585612b97565b90506000806000808473ffffffffffffffffffffffffffffffffffffffff16630902f1ac6040518163ffffffff1660e01b815260040160606040518083038186803b158015612d4d57600080fd5b505afa158015612d61573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d8591906144bd565b506dffffffffffffffffffffffffffff1691506dffffffffffffffffffffffffffff1691506000808773ffffffffffffffffffffffffffffffffffffffff168a73ffffffffffffffffffffffffffffffffffffffff1614612de7578284612dea565b83835b91509150612e2b828b73ffffffffffffffffffffffffffffffffffffffff166370a082318a6040518263ffffffff1660e01b81526004016113af919061467f565b9550612e388683836138de565b9450505050506000808573ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff1614612e7c57826000612e80565b6000835b91509150600060028c51038a10612e97578a612ed8565b612ed87f0000000000000000000000000000000000000000000000000000000000000000898e8d60020181518110612ecb57fe5b6020026020010151612b97565b604080516000815260208101918290527f022c0d9f0000000000000000000000000000000000000000000000000000000090915290915073ffffffffffffffffffffffffffffffffffffffff87169063022c0d9f90612f40908690869086906024810161489d565b600060405180830381600087803b158015612f5a57600080fd5b505af1158015612f6e573d6000803e3d6000fd5b50506001909b019a50612c859950505050505050505050565b81516060906002811015612fc7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105f290614775565b8067ffffffffffffffff81118015612fde57600080fd5b50604051908082528060200260200182016040528015613008578160200160208202803683370190505b5091508282600183038151811061301b57fe5b60209081029190910101527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81015b801561319557600060018203905060008061308c8a89858151811061306b57fe5b60200260200101518a878151811061307f57fe5b60200260200101516139b4565b915091508873ffffffffffffffffffffffffffffffffffffffff1663ca4bc7148b84848a89815181106130bb57fe5b60200260200101517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6040518663ffffffff1660e01b81526004016131049594939291906146a0565b60206040518083038186803b15801561311c57600080fd5b505afa158015613130573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131549190614518565b86848151811061316057fe5b60209081029190910101525050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0161304a565b5050949350505050565b60606002825110156131b057600080fd5b815167ffffffffffffffff811180156131c857600080fd5b506040519080825280602002602001820160405280156131f2578160200160208202803683370190505b509050828160018351038151811061320657fe5b602090810291909101015281517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff015b80156107a7576000806132738786600186038151811061325257fe5b602002602001015187868151811061326657fe5b60200260200101516140ad565b9150915061328684848151811061239257fe5b84600185038151811061329557fe5b602090810291909101015250507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01613236565b6040805173ffffffffffffffffffffffffffffffffffffffff8481166024830152604480830185905283518084039091018152606490920183526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb000000000000000000000000000000000000000000000000000000001781529251825160009485949389169392918291908083835b6020831061339e57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101613361565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114613400576040519150601f19603f3d011682016040523d82523d6000602084013e613405565b606091505b5091509150818015613433575080511580613433575080806020019051602081101561343057600080fd5b50515b61109157604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600260248201527f5354000000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b6040805173ffffffffffffffffffffffffffffffffffffffff85811660248301528481166044830152606480830185905283518084039091018152608490920183526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f23b872dd00000000000000000000000000000000000000000000000000000000178152925182516000948594938a169392918291908083835b6020831061357b57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161353e565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d80600081146135dd576040519150601f19603f3d011682016040523d82523d6000602084013e6135e2565b606091505b5091509150818015613610575080511580613610575080806020019051602081101561360d57600080fd5b50515b61053a57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f5354460000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b600080600061368a8585613839565b50905060008061369b8888886123e4565b73ffffffffffffffffffffffffffffffffffffffff16630902f1ac6040518163ffffffff1660e01b815260040160606040518083038186803b1580156136e057600080fd5b505afa1580156136f4573d6000803e3d6000fd5b505050506040513d606081101561370a57600080fd5b5080516020909101516dffffffffffffffffffffffffffff918216935016905073ffffffffffffffffffffffffffffffffffffffff87811690841614613751578082613754565b81815b90999098509650505050505050565b60008084116137d357604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f494e53554646494349454e545f4f55545055545f414d4f554e54000000000000604482015290519081900360640190fd5b6000831180156137e35750600082115b6137ec57600080fd5b60006138046127106137fe8688612b5d565b90612b5d565b905060006138186126f36137fe8689612b87565b905061382f600182848161382857fe5b04906140cd565b9695505050505050565b6000808273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff16141561387557600080fd5b8273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff16106138af5782846138b2565b83835b909250905073ffffffffffffffffffffffffffffffffffffffff82166138d757600080fd5b9250929050565b600080841161394e57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f494e53554646494349454e545f494e5055545f414d4f554e5400000000000000604482015290519081900360640190fd5b60008311801561395e5750600082115b61396757600080fd5b6000613975856126f3612b5d565b905060006139838285612b5d565b9050600061399d8361399788612710612b5d565b906140cd565b90508082816139a857fe5b04979650505050505050565b60008073ffffffffffffffffffffffffffffffffffffffff8516613a04576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105f2906147d2565b600085905060008173ffffffffffffffffffffffffffffffffffffffff1663293577506040518163ffffffff1660e01b815260040160206040518083038186803b158015613a5157600080fd5b505afa158015613a65573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a899190614518565b90508060021415613b98576040517fc661065700000000000000000000000000000000000000000000000000000000815260009073ffffffffffffffffffffffffffffffffffffffff84169063c661065790613ae9908490600401614759565b60206040518083038186803b158015613b0157600080fd5b505afa158015613b15573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b399190614165565b90508073ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff1614613b75576001613b78565b60005b60ff1694508415613b8a576000613b8d565b60015b60ff16935050613ece565b8060031415613ece576040517fc661065700000000000000000000000000000000000000000000000000000000815260009073ffffffffffffffffffffffffffffffffffffffff84169063c661065790613bf6908490600401614759565b60206040518083038186803b158015613c0e57600080fd5b505afa158015613c22573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c469190614165565b905060008373ffffffffffffffffffffffffffffffffffffffff1663c661065760016040518263ffffffff1660e01b8152600401613c849190614759565b60206040518083038186803b158015613c9c57600080fd5b505afa158015613cb0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613cd49190614165565b905060008473ffffffffffffffffffffffffffffffffffffffff1663c661065760026040518263ffffffff1660e01b8152600401613d129190614759565b60206040518083038186803b158015613d2a57600080fd5b505afa158015613d3e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613d629190614165565b90508273ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff161415613da15760009650613e17565b8173ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff161415613dde5760019650613e17565b8073ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff161415613e1757600296505b8273ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff161415613e545760009550613eca565b8173ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff161415613e915760019550613eca565b8073ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff161415613eca57600295505b5050505b5050935093915050565b6040805173ffffffffffffffffffffffffffffffffffffffff8481166024830152604480830185905283518084039091018152606490920183526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f095ea7b3000000000000000000000000000000000000000000000000000000001781529251825160009485949389169392918291908083835b60208310613fad57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101613f70565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d806000811461400f576040519150601f19603f3d011682016040523d82523d6000602084013e614014565b606091505b5091509150818015614042575080511580614042575080806020019051602081101561403f57600080fd5b50515b61109157604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600260248201527f5341000000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b60008060006140bc8585613839565b50905060008061369b888888612b97565b80820182811015612b8157600080fd5b60008083601f8401126140ee578081fd5b50813567ffffffffffffffff811115614105578182fd5b60208301915083602080830285010111156138d757600080fd5b80516dffffffffffffffffffffffffffff8116811461413d57600080fd5b919050565b600060208284031215614153578081fd5b813561415e81614982565b9392505050565b600060208284031215614176578081fd5b815161415e81614982565b60008060408385031215614193578081fd5b823561419e81614982565b946020939093013593505050565b6000806000606084860312156141c0578081fd5b83356141cb81614982565b92506020840135915060408401356141e281614982565b809150509250925092565b600080600080600060a08688031215614204578081fd5b853561420f81614982565b945060208601359350604086013561422681614982565b925060608601359150608086013561423d81614982565b809150509295509295909350565b60008060008060808587031215614260578384fd5b843561426b81614982565b93506020850135925060408501359150606085013561428981614982565b939692955090935050565b60008060008060008060c087890312156142ac578081fd5b86356142b781614982565b95506020870135945060408701359350606087013560ff811681146142da578182fd5b9598949750929560808101359460a0909101359350915050565b600080600080600080600060a0888a03121561430e578081fd5b873567ffffffffffffffff80821115614325578283fd5b6143318b838c016140dd565b909950975060208a0135915080821115614349578283fd5b506143568a828b016140dd565b9096509450506040880135925060608801359150608088013561437881614982565b8091505092959891949750929550565b6000806020838503121561439a578081fd5b823567ffffffffffffffff8111156143b0578182fd5b6143bc858286016140dd565b90969095509350505050565b6000806000604084860312156143dc578081fd5b83359250602084013567ffffffffffffffff8111156143f9578182fd5b614405868287016140dd565b9497909650939450505050565b600060208284031215614423578081fd5b815167ffffffffffffffff8082111561443a578283fd5b818401915084601f83011261444d578283fd5b81518181111561445957fe5b60405160207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116820101818110848211171561449557fe5b6040528181528382016020018710156144ac578485fd5b61382f826020830160208701614956565b6000806000606084860312156144d1578081fd5b6144da8461411f565b92506144e86020850161411f565b9150604084015163ffffffff811681146141e2578182fd5b600060208284031215614511578081fd5b5035919050565b600060208284031215614529578081fd5b5051919050565b60008060408385031215614542578182fd5b82359150602083013561455481614982565b809150509250929050565b60008060008060808587031215614574578182fd5b84359350602085013561458681614982565b925060408501359150606085013561428981614982565b6000806000606084860312156145b1578081fd5b833592506020840135915060408401356141e281614982565b6000806000806000608086880312156145e1578283fd5b8535945060208601359350604086013567ffffffffffffffff811115614605578384fd5b614611888289016140dd565b909450925050606086013561423d81614982565b6000815180845261463d816020860160208601614956565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6000828483379101908152919050565b73ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b73ffffffffffffffffffffffffffffffffffffffff959095168552602085019390935260408401919091526060830152608082015260a00190565b6000602080830181845280855180835260408601915060408482028701019250838701855b8281101561474c577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc088860301845261473a858351614625565b94509285019290850190600101614700565b5092979650505050505050565b90815260200190565b60006020825261415e6020830184614625565b60208082526024908201527f676574537461626c65416d6f756e7473496e3a20696e636f7272656374206c6560408201527f6e67746800000000000000000000000000000000000000000000000000000000606082015260800190565b60208082526023908201527f676574537461626c65496e666f3a20696e76616c696420706f6f6c206164647260408201527f6573730000000000000000000000000000000000000000000000000000000000606082015260800190565b60208082526012908201527f546f6f206d756368207265717565737465640000000000000000000000000000604082015260600190565b60208082526013908201527f546f6f206c6974746c6520726563656976656400000000000000000000000000604082015260600190565b600085825284602083015273ffffffffffffffffffffffffffffffffffffffff841660408301526080606083015261382f6080830184614625565b93845260208401929092526040830152606082015260800190565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112614927578283fd5b83018035915067ffffffffffffffff821115614941578283fd5b6020019150368190038213156138d757600080fd5b60005b83811015614971578181015183820152602001614959565b838111156107bc5750506000910152565b73ffffffffffffffffffffffffffffffffffffffff81168114610a7657600080fdfea164736f6c6343000706000a0000000000000000000000001715a3e4a142d8b698131108995174f37aeba10d00000000000000000000000029ea7545def87022badc76323f373ea1e707c5230000000000000000000000001973e61a0518a3cfcbfb7a840ee9ac7ba35562a0000000000000000000000000a1077a294dde1b09bb078844df40758a5d0f9a27

Deployed ByteCode

0x6080604052600436106101bb5760003560e01c80639b5c7a81116100ec578063cafe95a01161008a578063e0e189a011610064578063e0e189a014610460578063e90a182f14610473578063f2d5d56b14610486578063f3995c671461049957610266565b8063cafe95a014610427578063d4ef38de1461043a578063df2ab5bb1461044d57610266565b8063ac9650d8116100c6578063ac9650d8146103d9578063b85aa7af146103ec578063c2e3140a14610401578063c8bb18561461041457610266565b80639b5c7a81146103a0578063a4a78f0c146103b3578063ab0acea4146103c657610266565b806349404b7c116101595780634aa94288116101335780634aa94288146103525780635ae401dc1461036557806368e0d4e1146103785780639b2c0a371461038d57610266565b806349404b7c14610317578063496169971461032a5780634aa4a4fc1461033d57610266565b80631f0464d1116101955780631f0464d1146102af5780633068c554146102cf5780633957f453146102e25780634659a4941461030457610266565b806312210e8a1461026b5780631c58db4f146102735780631d4389f51461028657610266565b36610266573373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000a1077a294dde1b09bb078844df40758a5d0f9a27161461026457604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600960248201527f4e6f742057455448390000000000000000000000000000000000000000000000604482015290519081900360640190fd5b005b600080fd5b6102646104ac565b610264610281366004614500565b6104be565b6102996102943660046145ca565b610542565b6040516102a69190614759565b60405180910390f35b6102c26102bd3660046143c8565b610725565b6040516102a691906146db565b6102646102dd36600461424b565b6107af565b3480156102ee57600080fd5b506102f76107c2565b6040516102a6919061467f565b610264610312366004614294565b6107e6565b610264610325366004614530565b6108a6565b610264610338366004614500565b610a6c565b34801561034957600080fd5b506102f7610a79565b6102996103603660046142f4565b610a9d565b6102c26103733660046143c8565b610de5565b34801561038457600080fd5b506102f7610e5e565b61026461039b36600461455f565b610e82565b6102996103ae3660046145ca565b611098565b6102646103c1366004614294565b61144c565b6102996103d43660046145ca565b611521565b6102c26103e7366004614388565b6117cd565b3480156103f857600080fd5b506102f7611927565b61026461040f366004614294565b611943565b6102996104223660046142f4565b6119f8565b6102996104353660046145ca565b611c70565b61026461044836600461459d565b611e21565b61026461045b3660046141ac565b611e2d565b61026461046e3660046141ed565b611f44565b610264610481366004614181565b6120aa565b610264610494366004614181565b6120b9565b6102646104a7366004614294565b6120c5565b47156104bc576104bc334761215d565b565b7f000000000000000000000000a1077a294dde1b09bb078844df40758a5d0f9a2773ffffffffffffffffffffffffffffffffffffffff1663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b15801561052657600080fd5b505af115801561053a573d6000803e3d6000fd5b505050505050565b60006105a27f0000000000000000000000001715a3e4a142d8b698131108995174f37aeba10d878686808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152506122ab92505050565b6000815181106105ae57fe5b60200260200101519050848111156105fb576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105f29061482f565b60405180910390fd5b6106948484600081811061060b57fe5b90506020020160208101906106209190614142565b3361068e7f0000000000000000000000001715a3e4a142d8b698131108995174f37aeba10d8888600081811061065257fe5b90506020020160208101906106679190614142565b8989600181811061067457fe5b90506020020160208101906106899190614142565b6123e4565b846124cf565b73ffffffffffffffffffffffffffffffffffffffff8216600114156106bb573391506106de565b73ffffffffffffffffffffffffffffffffffffffff8216600214156106de573091505b61071c8484808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152508692506126ad915050565b95945050505050565b6060838060014303401461079a57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600960248201527f426c6f636b686173680000000000000000000000000000000000000000000000604482015290519081900360640190fd5b6107a484846117cd565b91505b509392505050565b6107bc8484338585611f44565b50505050565b7f0000000000000000000000001715a3e4a142d8b698131108995174f37aeba10d81565b604080517f8fcbaf0c00000000000000000000000000000000000000000000000000000000815233600482015230602482015260448101879052606481018690526001608482015260ff851660a482015260c4810184905260e48101839052905173ffffffffffffffffffffffffffffffffffffffff881691638fcbaf0c9161010480830192600092919082900301818387803b15801561088657600080fd5b505af115801561089a573d6000803e3d6000fd5b50505050505050505050565b60007f000000000000000000000000a1077a294dde1b09bb078844df40758a5d0f9a2773ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b15801561092f57600080fd5b505afa158015610943573d6000803e3d6000fd5b505050506040513d602081101561095957600080fd5b50519050828110156109cc57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f496e73756666696369656e742057455448390000000000000000000000000000604482015290519081900360640190fd5b8015610a67577f000000000000000000000000a1077a294dde1b09bb078844df40758a5d0f9a2773ffffffffffffffffffffffffffffffffffffffff16632e1a7d4d826040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b158015610a4557600080fd5b505af1158015610a59573d6000803e3d6000fd5b50505050610a67828261215d565b505050565b610a7681336108a6565b50565b7f000000000000000000000000a1077a294dde1b09bb078844df40758a5d0f9a2781565b600060026000541415610b1157604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b6002600090815588888281610b2257fe5b9050602002016020810190610b379190614142565b9050600089897fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8101818110610b6957fe5b9050602002016020810190610b7e9190614142565b9050600086610c3057506040517f70a0823100000000000000000000000000000000000000000000000000000000815260019073ffffffffffffffffffffffffffffffffffffffff8416906370a0823190610bdd90309060040161467f565b60206040518083038186803b158015610bf557600080fd5b505afa158015610c09573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c2d9190614518565b96505b80610c4157610c418333308a6124cf565b610cae8b8b8080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525050604080516020808f0282810182019093528e82529093508e92508d9182918501908490808284376000920191909152506129b292505050565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8316906370a0823190610d0090309060040161467f565b60206040518083038186803b158015610d1857600080fd5b505afa158015610d2c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d509190614518565b935085841015610d5f57600080fd5b73ffffffffffffffffffffffffffffffffffffffff851660011415610d8657339450610da9565b73ffffffffffffffffffffffffffffffffffffffff851660021415610da9573094505b73ffffffffffffffffffffffffffffffffffffffff85163014610dd257610dd2823087876124cf565b5050600160005550979650505050505050565b60608380610df1612b59565b111561079a57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f5472616e73616374696f6e20746f6f206f6c6400000000000000000000000000604482015290519081900360640190fd5b7f00000000000000000000000029ea7545def87022badc76323f373ea1e707c52381565b600082118015610e93575060648211155b610e9c57600080fd5b60007f000000000000000000000000a1077a294dde1b09bb078844df40758a5d0f9a2773ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015610f2557600080fd5b505afa158015610f39573d6000803e3d6000fd5b505050506040513d6020811015610f4f57600080fd5b5051905084811015610fc257604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f496e73756666696369656e742057455448390000000000000000000000000000604482015290519081900360640190fd5b8015611091577f000000000000000000000000a1077a294dde1b09bb078844df40758a5d0f9a2773ffffffffffffffffffffffffffffffffffffffff16632e1a7d4d826040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b15801561103b57600080fd5b505af115801561104f573d6000803e3d6000fd5b50505050600061271061106b8584612b5d90919063ffffffff16565b8161107257fe5b049050801561108557611085838261215d565b61053a8582840361215d565b5050505050565b6000808661114e5750600184846000816110ae57fe5b90506020020160208101906110c39190614142565b73ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b81526004016110fb919061467f565b60206040518083038186803b15801561111357600080fd5b505afa158015611127573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061114b9190614518565b96505b6111d98585600081811061115e57fe5b90506020020160208101906111739190614142565b8261117e5733611180565b305b6111d37f0000000000000000000000001715a3e4a142d8b698131108995174f37aeba10d898960008181106111b157fe5b90506020020160208101906111c69190614142565b8a8a600181811061067457fe5b8a6124cf565b73ffffffffffffffffffffffffffffffffffffffff83166001141561120057339250611223565b73ffffffffffffffffffffffffffffffffffffffff831660021415611223573092505b600085857fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff810181811061125357fe5b90506020020160208101906112689190614142565b73ffffffffffffffffffffffffffffffffffffffff166370a08231856040518263ffffffff1660e01b81526004016112a0919061467f565b60206040518083038186803b1580156112b857600080fd5b505afa1580156112cc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112f09190614518565b90506113308686808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152508892506126ad915050565b6114058187877fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff810181811061136257fe5b90506020020160208101906113779190614142565b73ffffffffffffffffffffffffffffffffffffffff166370a08231876040518263ffffffff1660e01b81526004016113af919061467f565b60206040518083038186803b1580156113c757600080fd5b505afa1580156113db573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113ff9190614518565b90612b87565b925086831015611441576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105f290614866565b505095945050505050565b604080517fdd62ed3e00000000000000000000000000000000000000000000000000000000815233600482015230602482015290517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9173ffffffffffffffffffffffffffffffffffffffff89169163dd62ed3e91604480820192602092909190829003018186803b1580156114e157600080fd5b505afa1580156114f5573d6000803e3d6000fd5b505050506040513d602081101561150b57600080fd5b5051101561053a5761053a8686868686866107e6565b600080866115d757506001848460008161153757fe5b905060200201602081019061154c9190614142565b73ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401611584919061467f565b60206040518083038186803b15801561159c57600080fd5b505afa1580156115b0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115d49190614518565b96505b611676858560008181106115e757fe5b90506020020160208101906115fc9190614142565b826116075733611609565b305b6111d37f00000000000000000000000029ea7545def87022badc76323f373ea1e707c5238989600081811061163a57fe5b905060200201602081019061164f9190614142565b8a8a600181811061165c57fe5b90506020020160208101906116719190614142565b612b97565b73ffffffffffffffffffffffffffffffffffffffff83166001141561169d573392506116c0565b73ffffffffffffffffffffffffffffffffffffffff8316600214156116c0573092505b600085857fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81018181106116f057fe5b90506020020160208101906117059190614142565b73ffffffffffffffffffffffffffffffffffffffff166370a08231856040518263ffffffff1660e01b815260040161173d919061467f565b60206040518083038186803b15801561175557600080fd5b505afa158015611769573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061178d9190614518565b9050611330868680806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250889250612c82915050565b60608167ffffffffffffffff811180156117e657600080fd5b5060405190808252806020026020018201604052801561181a57816020015b60608152602001906001900390816118055790505b50905060005b82811015611920576000803086868581811061183857fe5b905060200281019061184a91906148f3565b60405161185892919061466f565b600060405180830381855af49150503d8060008114611893576040519150601f19603f3d011682016040523d82523d6000602084013e611898565b606091505b5091509150816118fe576044815110156118b157600080fd5b600481019050808060200190518101906118cb9190614412565b6040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105f29190614762565b8084848151811061190b57fe5b60209081029190910101525050600101611820565b5092915050565b60015473ffffffffffffffffffffffffffffffffffffffff1681565b604080517fdd62ed3e0000000000000000000000000000000000000000000000000000000081523360048201523060248201529051869173ffffffffffffffffffffffffffffffffffffffff89169163dd62ed3e91604480820192602092909190829003018186803b1580156119b857600080fd5b505afa1580156119cc573d6000803e3d6000fd5b505050506040513d60208110156119e257600080fd5b5051101561053a5761053a8686868686866120c5565b600060026000541415611a6c57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015290519081900360640190fd5b6002600081905550611aee86866000818110611a8457fe5b9050602002016020810190611a999190614142565b600154604080516020808d0282810182019093528c825273ffffffffffffffffffffffffffffffffffffffff9093169290918d918d9182918501908490808284376000920191909152508a9250612f87915050565b600081518110611afa57fe5b6020026020010151905082811115611b1157600080fd5b611b3e88886000818110611b2157fe5b9050602002016020810190611b369190614142565b3330846124cf565b611bab88888080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525050604080516020808c0282810182019093528b82529093508b92508a9182918501908490808284376000920191909152506129b292505050565b73ffffffffffffffffffffffffffffffffffffffff821660011415611bd257339150611bf5565b73ffffffffffffffffffffffffffffffffffffffff821660021415611bf5573091505b73ffffffffffffffffffffffffffffffffffffffff82163014611c6057611c6088887fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8101818110611c4357fe5b9050602002016020810190611c589190614142565b3084876124cf565b6001600055979650505050505050565b6000611cd07f00000000000000000000000029ea7545def87022badc76323f373ea1e707c5238786868080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525061319f92505050565b600081518110611cdc57fe5b6020026020010151905084811115611d20576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105f29061482f565b611d9984846000818110611d3057fe5b9050602002016020810190611d459190614142565b3361068e7f00000000000000000000000029ea7545def87022badc76323f373ea1e707c52388886000818110611d7757fe5b9050602002016020810190611d8c9190614142565b8989600181811061165c57fe5b73ffffffffffffffffffffffffffffffffffffffff821660011415611dc057339150611de3565b73ffffffffffffffffffffffffffffffffffffffff821660021415611de3573091505b61071c848480806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250869250612c82915050565b610a6783338484610e82565b60008373ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015611e9657600080fd5b505afa158015611eaa573d6000803e3d6000fd5b505050506040513d6020811015611ec057600080fd5b5051905082811015611f3357604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f496e73756666696369656e7420746f6b656e0000000000000000000000000000604482015290519081900360640190fd5b80156107bc576107bc8483836132c9565b600082118015611f55575060648211155b611f5e57600080fd5b60008573ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060206040518083038186803b158015611fc757600080fd5b505afa158015611fdb573d6000803e3d6000fd5b505050506040513d6020811015611ff157600080fd5b505190508481101561206457604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f496e73756666696369656e7420746f6b656e0000000000000000000000000000604482015290519081900360640190fd5b801561053a5760006127106120798386612b5d565b8161208057fe5b0490508015612094576120948784836132c9565b6120a187868385036132c9565b50505050505050565b6120b5828233611e2d565b5050565b6120b58233308461349e565b604080517fd505accf000000000000000000000000000000000000000000000000000000008152336004820152306024820152604481018790526064810186905260ff8516608482015260a4810184905260c48101839052905173ffffffffffffffffffffffffffffffffffffffff88169163d505accf9160e480830192600092919082900301818387803b15801561088657600080fd5b6040805160008082526020820190925273ffffffffffffffffffffffffffffffffffffffff84169083906040518082805190602001908083835b602083106121d457805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101612197565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d8060008114612236576040519150601f19603f3d011682016040523d82523d6000602084013e61223b565b606091505b5050905080610a6757604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f5354450000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b60606002825110156122bc57600080fd5b815167ffffffffffffffff811180156122d457600080fd5b506040519080825280602002602001820160405280156122fe578160200160208202803683370190505b509050828160018351038151811061231257fe5b602090810291909101015281517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff015b80156107a75760008061237f8786600186038151811061235e57fe5b602002602001015187868151811061237257fe5b602002602001015161367b565b915091506123a184848151811061239257fe5b60200260200101518383613763565b8460018503815181106123b057fe5b602090810291909101015250507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01612342565b60008060006123f38585613839565b604080517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606094851b811660208084019190915293851b81166034830152825160288184030181526048830184528051908501207fff0000000000000000000000000000000000000000000000000000000000000060688401529a90941b9093166069840152607d8301989098527f59fffffddd756cba9095128e53f3291a6ba38b21e3df744936e7289326555d62609d808401919091528851808403909101815260bd909201909752805196019590952095945050505050565b7f000000000000000000000000a1077a294dde1b09bb078844df40758a5d0f9a2773ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff1614801561252a5750804710155b15612673577f000000000000000000000000a1077a294dde1b09bb078844df40758a5d0f9a2773ffffffffffffffffffffffffffffffffffffffff1663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b15801561259757600080fd5b505af11580156125ab573d6000803e3d6000fd5b50505050507f000000000000000000000000a1077a294dde1b09bb078844df40758a5d0f9a2773ffffffffffffffffffffffffffffffffffffffff1663a9059cbb83836040518363ffffffff1660e01b8152600401808373ffffffffffffffffffffffffffffffffffffffff16815260200182815260200192505050602060405180830381600087803b15801561264157600080fd5b505af1158015612655573d6000803e3d6000fd5b505050506040513d602081101561266b57600080fd5b506107bc9050565b73ffffffffffffffffffffffffffffffffffffffff83163014156126a15761269c8483836132c9565b6107bc565b6107bc8484848461349e565b60005b6001835103811015610a67576000808483815181106126cb57fe5b60200260200101518584600101815181106126e257fe5b60200260200101519150915060006126fa8383613839565b509050600061272a7f0000000000000000000000001715a3e4a142d8b698131108995174f37aeba10d85856123e4565b90506000806000808473ffffffffffffffffffffffffffffffffffffffff16630902f1ac6040518163ffffffff1660e01b815260040160606040518083038186803b15801561277857600080fd5b505afa15801561278c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127b091906144bd565b506dffffffffffffffffffffffffffff1691506dffffffffffffffffffffffffffff1691506000808773ffffffffffffffffffffffffffffffffffffffff168a73ffffffffffffffffffffffffffffffffffffffff1614612812578284612815565b83835b91509150612856828b73ffffffffffffffffffffffffffffffffffffffff166370a082318a6040518263ffffffff1660e01b81526004016113af919061467f565b95506128638683836138de565b9450505050506000808573ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff16146128a7578260006128ab565b6000835b91509150600060028c51038a106128c2578a612903565b6129037f0000000000000000000000001715a3e4a142d8b698131108995174f37aeba10d898e8d600201815181106128f657fe5b60200260200101516123e4565b604080516000815260208101918290527f022c0d9f0000000000000000000000000000000000000000000000000000000090915290915073ffffffffffffffffffffffffffffffffffffffff87169063022c0d9f9061296b908690869086906024810161489d565b600060405180830381600087803b15801561298557600080fd5b505af1158015612999573d6000803e3d6000fd5b50506001909b019a506126b09950505050505050505050565b60005b6001835103811015610a67576000808483815181106129d057fe5b60200260200101518584600101815181106129e757fe5b602002602001015191509150600080612a14868681518110612a0557fe5b602002602001015185856139b4565b9150915060008473ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff1660e01b8152600401612a53919061467f565b60206040518083038186803b158015612a6b57600080fd5b505afa158015612a7f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612aa39190614518565b9050612ac385888881518110612ab557fe5b602002602001015183613ed8565b868681518110612acf57fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff16635b41b90884848460006040518563ffffffff1660e01b8152600401612b1694939291906148d8565b600060405180830381600087803b158015612b3057600080fd5b505af1158015612b44573d6000803e3d6000fd5b5050600190970196506129b595505050505050565b4290565b6000821580612b7857505081810281838281612b7557fe5b04145b612b8157600080fd5b92915050565b80820382811115612b8157600080fd5b6000806000612ba68585613839565b604080517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606094851b811660208084019190915293851b81166034830152825160288184030181526048830184528051908501207fff0000000000000000000000000000000000000000000000000000000000000060688401529a90941b9093166069840152607d8301989098527f5dff1ac2d132f5ac2841294c6e9fc0ebafae8d447fac7996ef21c21112f411f1609d808401919091528851808403909101815260bd909201909752805196019590952095945050505050565b60005b6001835103811015610a6757600080848381518110612ca057fe5b6020026020010151858460010181518110612cb757fe5b6020026020010151915091506000612ccf8383613839565b5090506000612cff7f00000000000000000000000029ea7545def87022badc76323f373ea1e707c5238585612b97565b90506000806000808473ffffffffffffffffffffffffffffffffffffffff16630902f1ac6040518163ffffffff1660e01b815260040160606040518083038186803b158015612d4d57600080fd5b505afa158015612d61573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d8591906144bd565b506dffffffffffffffffffffffffffff1691506dffffffffffffffffffffffffffff1691506000808773ffffffffffffffffffffffffffffffffffffffff168a73ffffffffffffffffffffffffffffffffffffffff1614612de7578284612dea565b83835b91509150612e2b828b73ffffffffffffffffffffffffffffffffffffffff166370a082318a6040518263ffffffff1660e01b81526004016113af919061467f565b9550612e388683836138de565b9450505050506000808573ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff1614612e7c57826000612e80565b6000835b91509150600060028c51038a10612e97578a612ed8565b612ed87f00000000000000000000000029ea7545def87022badc76323f373ea1e707c523898e8d60020181518110612ecb57fe5b6020026020010151612b97565b604080516000815260208101918290527f022c0d9f0000000000000000000000000000000000000000000000000000000090915290915073ffffffffffffffffffffffffffffffffffffffff87169063022c0d9f90612f40908690869086906024810161489d565b600060405180830381600087803b158015612f5a57600080fd5b505af1158015612f6e573d6000803e3d6000fd5b50506001909b019a50612c859950505050505050505050565b81516060906002811015612fc7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105f290614775565b8067ffffffffffffffff81118015612fde57600080fd5b50604051908082528060200260200182016040528015613008578160200160208202803683370190505b5091508282600183038151811061301b57fe5b60209081029190910101527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81015b801561319557600060018203905060008061308c8a89858151811061306b57fe5b60200260200101518a878151811061307f57fe5b60200260200101516139b4565b915091508873ffffffffffffffffffffffffffffffffffffffff1663ca4bc7148b84848a89815181106130bb57fe5b60200260200101517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6040518663ffffffff1660e01b81526004016131049594939291906146a0565b60206040518083038186803b15801561311c57600080fd5b505afa158015613130573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131549190614518565b86848151811061316057fe5b60209081029190910101525050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0161304a565b5050949350505050565b60606002825110156131b057600080fd5b815167ffffffffffffffff811180156131c857600080fd5b506040519080825280602002602001820160405280156131f2578160200160208202803683370190505b509050828160018351038151811061320657fe5b602090810291909101015281517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff015b80156107a7576000806132738786600186038151811061325257fe5b602002602001015187868151811061326657fe5b60200260200101516140ad565b9150915061328684848151811061239257fe5b84600185038151811061329557fe5b602090810291909101015250507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01613236565b6040805173ffffffffffffffffffffffffffffffffffffffff8481166024830152604480830185905283518084039091018152606490920183526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb000000000000000000000000000000000000000000000000000000001781529251825160009485949389169392918291908083835b6020831061339e57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101613361565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114613400576040519150601f19603f3d011682016040523d82523d6000602084013e613405565b606091505b5091509150818015613433575080511580613433575080806020019051602081101561343057600080fd5b50515b61109157604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600260248201527f5354000000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b6040805173ffffffffffffffffffffffffffffffffffffffff85811660248301528481166044830152606480830185905283518084039091018152608490920183526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f23b872dd00000000000000000000000000000000000000000000000000000000178152925182516000948594938a169392918291908083835b6020831061357b57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161353e565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d80600081146135dd576040519150601f19603f3d011682016040523d82523d6000602084013e6135e2565b606091505b5091509150818015613610575080511580613610575080806020019051602081101561360d57600080fd5b50515b61053a57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600360248201527f5354460000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b600080600061368a8585613839565b50905060008061369b8888886123e4565b73ffffffffffffffffffffffffffffffffffffffff16630902f1ac6040518163ffffffff1660e01b815260040160606040518083038186803b1580156136e057600080fd5b505afa1580156136f4573d6000803e3d6000fd5b505050506040513d606081101561370a57600080fd5b5080516020909101516dffffffffffffffffffffffffffff918216935016905073ffffffffffffffffffffffffffffffffffffffff87811690841614613751578082613754565b81815b90999098509650505050505050565b60008084116137d357604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f494e53554646494349454e545f4f55545055545f414d4f554e54000000000000604482015290519081900360640190fd5b6000831180156137e35750600082115b6137ec57600080fd5b60006138046127106137fe8688612b5d565b90612b5d565b905060006138186126f36137fe8689612b87565b905061382f600182848161382857fe5b04906140cd565b9695505050505050565b6000808273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff16141561387557600080fd5b8273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff16106138af5782846138b2565b83835b909250905073ffffffffffffffffffffffffffffffffffffffff82166138d757600080fd5b9250929050565b600080841161394e57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f494e53554646494349454e545f494e5055545f414d4f554e5400000000000000604482015290519081900360640190fd5b60008311801561395e5750600082115b61396757600080fd5b6000613975856126f3612b5d565b905060006139838285612b5d565b9050600061399d8361399788612710612b5d565b906140cd565b90508082816139a857fe5b04979650505050505050565b60008073ffffffffffffffffffffffffffffffffffffffff8516613a04576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105f2906147d2565b600085905060008173ffffffffffffffffffffffffffffffffffffffff1663293577506040518163ffffffff1660e01b815260040160206040518083038186803b158015613a5157600080fd5b505afa158015613a65573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a899190614518565b90508060021415613b98576040517fc661065700000000000000000000000000000000000000000000000000000000815260009073ffffffffffffffffffffffffffffffffffffffff84169063c661065790613ae9908490600401614759565b60206040518083038186803b158015613b0157600080fd5b505afa158015613b15573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b399190614165565b90508073ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff1614613b75576001613b78565b60005b60ff1694508415613b8a576000613b8d565b60015b60ff16935050613ece565b8060031415613ece576040517fc661065700000000000000000000000000000000000000000000000000000000815260009073ffffffffffffffffffffffffffffffffffffffff84169063c661065790613bf6908490600401614759565b60206040518083038186803b158015613c0e57600080fd5b505afa158015613c22573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c469190614165565b905060008373ffffffffffffffffffffffffffffffffffffffff1663c661065760016040518263ffffffff1660e01b8152600401613c849190614759565b60206040518083038186803b158015613c9c57600080fd5b505afa158015613cb0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613cd49190614165565b905060008473ffffffffffffffffffffffffffffffffffffffff1663c661065760026040518263ffffffff1660e01b8152600401613d129190614759565b60206040518083038186803b158015613d2a57600080fd5b505afa158015613d3e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613d629190614165565b90508273ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff161415613da15760009650613e17565b8173ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff161415613dde5760019650613e17565b8073ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff161415613e1757600296505b8273ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff161415613e545760009550613eca565b8173ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff161415613e915760019550613eca565b8073ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff161415613eca57600295505b5050505b5050935093915050565b6040805173ffffffffffffffffffffffffffffffffffffffff8481166024830152604480830185905283518084039091018152606490920183526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f095ea7b3000000000000000000000000000000000000000000000000000000001781529251825160009485949389169392918291908083835b60208310613fad57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101613f70565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d806000811461400f576040519150601f19603f3d011682016040523d82523d6000602084013e614014565b606091505b5091509150818015614042575080511580614042575080806020019051602081101561403f57600080fd5b50515b61109157604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600260248201527f5341000000000000000000000000000000000000000000000000000000000000604482015290519081900360640190fd5b60008060006140bc8585613839565b50905060008061369b888888612b97565b80820182811015612b8157600080fd5b60008083601f8401126140ee578081fd5b50813567ffffffffffffffff811115614105578182fd5b60208301915083602080830285010111156138d757600080fd5b80516dffffffffffffffffffffffffffff8116811461413d57600080fd5b919050565b600060208284031215614153578081fd5b813561415e81614982565b9392505050565b600060208284031215614176578081fd5b815161415e81614982565b60008060408385031215614193578081fd5b823561419e81614982565b946020939093013593505050565b6000806000606084860312156141c0578081fd5b83356141cb81614982565b92506020840135915060408401356141e281614982565b809150509250925092565b600080600080600060a08688031215614204578081fd5b853561420f81614982565b945060208601359350604086013561422681614982565b925060608601359150608086013561423d81614982565b809150509295509295909350565b60008060008060808587031215614260578384fd5b843561426b81614982565b93506020850135925060408501359150606085013561428981614982565b939692955090935050565b60008060008060008060c087890312156142ac578081fd5b86356142b781614982565b95506020870135945060408701359350606087013560ff811681146142da578182fd5b9598949750929560808101359460a0909101359350915050565b600080600080600080600060a0888a03121561430e578081fd5b873567ffffffffffffffff80821115614325578283fd5b6143318b838c016140dd565b909950975060208a0135915080821115614349578283fd5b506143568a828b016140dd565b9096509450506040880135925060608801359150608088013561437881614982565b8091505092959891949750929550565b6000806020838503121561439a578081fd5b823567ffffffffffffffff8111156143b0578182fd5b6143bc858286016140dd565b90969095509350505050565b6000806000604084860312156143dc578081fd5b83359250602084013567ffffffffffffffff8111156143f9578182fd5b614405868287016140dd565b9497909650939450505050565b600060208284031215614423578081fd5b815167ffffffffffffffff8082111561443a578283fd5b818401915084601f83011261444d578283fd5b81518181111561445957fe5b60405160207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116820101818110848211171561449557fe5b6040528181528382016020018710156144ac578485fd5b61382f826020830160208701614956565b6000806000606084860312156144d1578081fd5b6144da8461411f565b92506144e86020850161411f565b9150604084015163ffffffff811681146141e2578182fd5b600060208284031215614511578081fd5b5035919050565b600060208284031215614529578081fd5b5051919050565b60008060408385031215614542578182fd5b82359150602083013561455481614982565b809150509250929050565b60008060008060808587031215614574578182fd5b84359350602085013561458681614982565b925060408501359150606085013561428981614982565b6000806000606084860312156145b1578081fd5b833592506020840135915060408401356141e281614982565b6000806000806000608086880312156145e1578283fd5b8535945060208601359350604086013567ffffffffffffffff811115614605578384fd5b614611888289016140dd565b909450925050606086013561423d81614982565b6000815180845261463d816020860160208601614956565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6000828483379101908152919050565b73ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b73ffffffffffffffffffffffffffffffffffffffff959095168552602085019390935260408401919091526060830152608082015260a00190565b6000602080830181845280855180835260408601915060408482028701019250838701855b8281101561474c577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc088860301845261473a858351614625565b94509285019290850190600101614700565b5092979650505050505050565b90815260200190565b60006020825261415e6020830184614625565b60208082526024908201527f676574537461626c65416d6f756e7473496e3a20696e636f7272656374206c6560408201527f6e67746800000000000000000000000000000000000000000000000000000000606082015260800190565b60208082526023908201527f676574537461626c65496e666f3a20696e76616c696420706f6f6c206164647260408201527f6573730000000000000000000000000000000000000000000000000000000000606082015260800190565b60208082526012908201527f546f6f206d756368207265717565737465640000000000000000000000000000604082015260600190565b60208082526013908201527f546f6f206c6974746c6520726563656976656400000000000000000000000000604082015260600190565b600085825284602083015273ffffffffffffffffffffffffffffffffffffffff841660408301526080606083015261382f6080830184614625565b93845260208401929092526040830152606082015260800190565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112614927578283fd5b83018035915067ffffffffffffffff821115614941578283fd5b6020019150368190038213156138d757600080fd5b60005b83811015614971578181015183820152602001614959565b838111156107bc5750506000910152565b73ffffffffffffffffffffffffffffffffffffffff81168114610a7657600080fdfea164736f6c6343000706000a