ETH Price: $3,893.03 (+0.86%)
Gas: 0.59 GWei

Contract

0xf29E31a7fd95FEa4BedAE8b61a12Fe5a61286430

Overview

ETH Balance

Scroll LogoScroll LogoScroll Logo0 ETH

ETH Value

$0.00

Token Holdings

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
0xdfd8aab8b09201765b533689b95296ff1d79a744a8fbe68a46e3e40f62b13049 -(pending)2024-10-22 9:35:3044 days ago1729589730IN
0xf29E31a7...a61286430
0 ETH(Pending)(Pending)
Swap104981712024-10-24 21:09:3342 days ago1729804173IN
0xf29E31a7...a61286430
0.0007 ETH0.000017470.05
Swap104981712024-10-24 21:09:3342 days ago1729804173IN
0xf29E31a7...a61286430
0.0004 ETH0.00001450.05
Swap104346802024-10-22 18:38:1044 days ago1729622290IN
0xf29E31a7...a61286430
0 ETH0.00003750.05004734
Swap102842832024-10-18 13:18:3448 days ago1729257514IN
0xf29E31a7...a61286430
0 ETH0.000035220.06
Swap101843102024-10-15 11:40:3151 days ago1728992431IN
0xf29E31a7...a61286430
0.015 ETH0.000015770.04870218
Swap98236492024-10-03 4:39:2264 days ago1727930362IN
0xf29E31a7...a61286430
0 ETH0.000003320.04611321
Swap98234332024-10-03 4:28:3564 days ago1727929715IN
0xf29E31a7...a61286430
0 ETH0.000002970.04713744
Swap98229742024-10-03 4:05:3964 days ago1727928339IN
0xf29E31a7...a61286430
0 ETH0.000010130.04694757
Swap96830082024-09-28 8:34:4268 days ago1727512482IN
0xf29E31a7...a61286430
0 ETH0.000029650.08
Swap94963652024-09-21 21:57:0475 days ago1726955824IN
0xf29E31a7...a61286430
0 ETH0.000010530.05214285
Swap94467602024-09-20 4:44:4177 days ago1726807481IN
0xf29E31a7...a61286430
0 ETH0.000017270.08058733
Swap94465572024-09-20 4:35:0977 days ago1726806909IN
0xf29E31a7...a61286430
0 ETH0.000020480.09567405
Swap92446302024-09-13 5:16:3984 days ago1726204599IN
0xf29E31a7...a61286430
0.1 ETH0.000009520.06
Swap92340922024-09-12 20:31:3684 days ago1726173096IN
0xf29E31a7...a61286430
0.003 ETH0.000011610.0478019
Swap92191502024-09-12 8:11:4284 days ago1726128702IN
0xf29E31a7...a61286430
0.01611978 ETH0.000010660.04320169
Swap92150662024-09-12 4:49:5285 days ago1726116592IN
0xf29E31a7...a61286430
0.103 ETH0.000009650.06
Swap92027692024-09-11 18:38:3785 days ago1726079917IN
0xf29E31a7...a61286430
0.01429391 ETH0.000011520.0470892
Swap91920952024-09-11 9:57:5485 days ago1726048674IN
0xf29E31a7...a61286430
0.01630898 ETH0.000008110.04116864
Swap91918702024-09-11 9:46:4985 days ago1726048009IN
0xf29E31a7...a61286430
0.01394788 ETH0.000011070.04154936
Swap91886862024-09-11 7:09:4385 days ago1726038583IN
0xf29E31a7...a61286430
0.01552971 ETH0.000009130.04096853
Swap91872712024-09-11 5:59:5585 days ago1726034395IN
0xf29E31a7...a61286430
0.11408584 ETH0.000022910.06
Swap91872662024-09-11 5:59:4085 days ago1726034380IN
0xf29E31a7...a61286430
0.01733161 ETH0.000010.04000712
Swap91871552024-09-11 5:54:0885 days ago1726034048IN
0xf29E31a7...a61286430
0.01489725 ETH0.000010050.04004442
Swap91784892024-09-10 22:43:4086 days ago1726008220IN
0xf29E31a7...a61286430
0.003 ETH0.000018440.06374435
View all transactions

Latest 25 internal transactions (View All)

Parent Transaction Hash Block From To
104981712024-10-24 21:09:3342 days ago1729804173
0xf29E31a7...a61286430
0.0007 ETH
104981712024-10-24 21:09:3342 days ago1729804173
0xf29E31a7...a61286430
0.0004 ETH
101843102024-10-15 11:40:3151 days ago1728992431
0xf29E31a7...a61286430
0.015 ETH
98229742024-10-03 4:05:3964 days ago1727928339
0xf29E31a7...a61286430
0.00007111 ETH
98229742024-10-03 4:05:3964 days ago1727928339
0xf29E31a7...a61286430
0.00000015 ETH
98229742024-10-03 4:05:3964 days ago1727928339
0xf29E31a7...a61286430
0.00007127 ETH
94467602024-09-20 4:44:4177 days ago1726807481
0xf29E31a7...a61286430
0.00013735 ETH
94467602024-09-20 4:44:4177 days ago1726807481
0xf29E31a7...a61286430
0.00000138 ETH
94467602024-09-20 4:44:4177 days ago1726807481
0xf29E31a7...a61286430
0.00013874 ETH
92446302024-09-13 5:16:3984 days ago1726204599
0xf29E31a7...a61286430
0.1 ETH
92340922024-09-12 20:31:3684 days ago1726173096
0xf29E31a7...a61286430
0.003 ETH
92191502024-09-12 8:11:4284 days ago1726128702
0xf29E31a7...a61286430
0.01611978 ETH
92150662024-09-12 4:49:5285 days ago1726116592
0xf29E31a7...a61286430
0.103 ETH
92027692024-09-11 18:38:3785 days ago1726079917
0xf29E31a7...a61286430
0.01429391 ETH
91920952024-09-11 9:57:5485 days ago1726048674
0xf29E31a7...a61286430
0.01630898 ETH
91918702024-09-11 9:46:4985 days ago1726048009
0xf29E31a7...a61286430
0.01394788 ETH
91886862024-09-11 7:09:4385 days ago1726038583
0xf29E31a7...a61286430
0.01552971 ETH
91872712024-09-11 5:59:5585 days ago1726034395
0xf29E31a7...a61286430
0.11408584 ETH
91872662024-09-11 5:59:4085 days ago1726034380
0xf29E31a7...a61286430
0.01733161 ETH
91871552024-09-11 5:54:0885 days ago1726034048
0xf29E31a7...a61286430
0.01489725 ETH
91784892024-09-10 22:43:4086 days ago1726008220
0xf29E31a7...a61286430
0.003 ETH
91776262024-09-10 22:00:3386 days ago1726005633
0xf29E31a7...a61286430
0.003 ETH
91776262024-09-10 22:00:3386 days ago1726005633
0xf29E31a7...a61286430
0.003 ETH
91776262024-09-10 22:00:3386 days ago1726005633
0xf29E31a7...a61286430
0.003 ETH
91744482024-09-10 19:22:0986 days ago1725996129
0xf29E31a7...a61286430
0.077 ETH
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
WowmaxRouter

Compiler Version
v0.8.7+commit.e28d00a7

Optimization Enabled:
Yes with 800 runs

Other Settings:
default evmVersion
File 1 of 36 : WowmaxRouter.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity =0.8.7;

import "./interfaces/IWETH.sol";
import "./interfaces/IWowmaxRouter.sol";

import "./libraries/UniswapV2.sol";
import "./libraries/UniswapV3.sol";
import "./libraries/Curve.sol";
import "./libraries/PancakeSwapStable.sol";
import "./libraries/DODOV2.sol";
import "./libraries/DODOV1.sol";
import "./libraries/DODOV3.sol";
import "./libraries/Hashflow.sol";
import "./libraries/Saddle.sol";
import "./libraries/Wombat.sol";
import "./libraries/Level.sol";
import "./libraries/Fulcrom.sol";
import "./libraries/WooFi.sol";
import "./libraries/Elastic.sol";
import "./libraries/AlgebraV1.sol";
import "./libraries/SyncSwap.sol";
import "./libraries/Vooi.sol";
import "./libraries/VelocoreV2.sol";
import "./libraries/Iziswap.sol";
import "./libraries/Velodrome.sol";
import "./libraries/BalancerV2.sol";
import "./libraries/MaverickV1.sol";
import "./libraries/MaverickV2.sol";
import "./libraries/WrappedNative.sol";
import "./libraries/LiquiditybookV2_1.sol";

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "./WowmaxSwapReentrancyGuard.sol";

/**
 * @title WOWMAX Router
 * @notice Router for stateless execution of swaps against multiple DEX protocols.
 *
 * The WowmaxRouter contract encompasses three primary responsibilities:
 * 1. Facilitating the exchange of user tokens based on a provided exchange route.
 * 2. Ensuring validation of the received output amounts for users, guaranteeing their alignment
 * within the designated slippage range.
 * 3. Transferring any surplus amounts to the treasury, thereby functioning as a service fee.
 *
 * The WowmaxRouter contract does not hold any tokens between swap operations. Tokens should not be transferred directly
 * to the contract. If, by any chance, tokens are transferred directly to the contract, they are most likely to be lost.
 */
contract WowmaxRouter is IWowmaxRouter, Ownable, WowmaxSwapReentrancyGuard {
    /**
     * @dev WETH contract
     */
    IWETH public WETH;

    /**
     * @dev Treasury address
     */
    address public treasury;

    /**
     * @dev Max fee percentage. All contract percentage values have two extra digits for precision. Default value is 1%
     */
    uint256 public maxFeePercentage = 100;

    /**
     * @dev Max allowed slippage percentage, default value is 20%
     */
    uint256 public maxSlippage = 2000;

    // Mapping of protocol names
    bytes32 internal constant UNISWAP_V2 = "UNISWAP_V2";
    bytes32 internal constant UNISWAP_V3 = "UNISWAP_V3";
    bytes32 internal constant UNISWAP_V2_ROUTER = "UNISWAP_V2_ROUTER";
    bytes32 internal constant CURVE = "CURVE";
    bytes32 internal constant DODO_V1 = "DODO_V1";
    bytes32 internal constant DODO_V2 = "DODO_V2";
    bytes32 internal constant DODO_V3 = "DODO_V3";
    bytes32 internal constant HASHFLOW = "HASHFLOW";
    bytes32 internal constant PANCAKESWAP_STABLE = "PANCAKESWAP_STABLE";
    bytes32 internal constant SADDLE = "SADDLE";
    bytes32 internal constant WOMBAT = "WOMBAT";
    bytes32 internal constant LEVEL = "LEVEL";
    bytes32 internal constant FULCROM = "FULCROM";
    bytes32 internal constant WOOFI = "WOOFI";
    bytes32 internal constant ELASTIC = "ELASTIC";
    bytes32 internal constant ALGEBRA_V1 = "ALGEBRA_V1";
    bytes32 internal constant ALGEBRA_V1_9 = "ALGEBRA_V1_9";
    bytes32 internal constant SYNCSWAP = "SYNCSWAP";
    bytes32 internal constant VOOI = "VOOI";
    bytes32 internal constant VELOCORE_V2 = "VELOCORE_V2";
    bytes32 internal constant IZISWAP = "IZISWAP";
    bytes32 internal constant VELODROME = "VELODROME";
    bytes32 internal constant BALANCER_V2 = "BALANCER_V2";
    bytes32 internal constant MAVERICK_V1 = "MAVERICK_V1";
    bytes32 internal constant WRAPPED_NATIVE = "WRAPPED_NATIVE";
    bytes32 internal constant LIQUIDITY_BOOK_V2_1 = "LIQUIDITY_BOOK_V2_1";
    bytes32 internal constant MAVERICK_V2 = "MAVERICK_V2";

    using SafeERC20 for IERC20;

    /**
     * @dev sets the WETH and treasury addresses
     */
    constructor(address _weth, address _treasury) {
        require(_weth != address(0), "WOWMAX: Wrong WETH address");
        require(_treasury != address(0), "WOWMAX: Wrong treasury address");

        WETH = IWETH(_weth);
        treasury = _treasury;
    }

    /**
     * @dev fallback function to receive native tokens
     */
    receive() external payable {}

    /**
     * @dev fallback function to process various protocols callback functions
     */
    fallback() external onlyDuringSwap {
        (bool success, int256 amount0Delta, int256 amount1Delta, bytes calldata data) = UniswapV3.decodeCallback({
            dataWithSelector: msg.data
        });
        require(success, "WOWMAX: unsupported callback");
        UniswapV3.invokeCallback(amount0Delta, amount1Delta, data);
    }

    // Admin functions

    /**
     * @dev withdraws tokens from a contract, in case of leftovers after a swap, invalid swap requests,
     * or direct transfers. Only callable by the owner.
     * @param token Token to be withdrawn
     * @param amount Amount to be withdrawn
     */
    function withdraw(address token, uint256 amount) external onlyOwner {
        IERC20(token).safeTransfer(treasury, amount);
    }

    /**
     * @dev withdraws native tokens from a contract, in case of leftovers after a swap or invalid swap requests.
     * Only callable by the owner.
     * @param amount Amount to be withdrawn
     */
    function withdrawETH(uint256 amount) external onlyOwner {
        (bool sent, ) = payable(treasury).call{ value: amount }("");
        require(sent, "Wowmax: Failed to send native tokens");
    }

    /**
     * @dev sets the max fee percentage. Only callable by the owner.
     * @param _maxFeePercentage Max fee percentage
     */
    function setMaxFeePercentage(uint256 _maxFeePercentage) external onlyOwner {
        maxFeePercentage = _maxFeePercentage;
    }

    /**
     * @dev sets the max allowed slippage. Only callable by the owner.
     * @param _maxSlippage Max allowed slippage percentage
     */
    function setMaxSlippage(uint256 _maxSlippage) external onlyOwner {
        maxSlippage = _maxSlippage;
    }

    // Callbacks

    /**
     * @dev callback for Maverick V1 pools. Not allowed to be executed outside of a swap operation
     * @param amountToPay Amount to be paid
     * @param amountOut Amount to be received
     * @param data Additional data to be passed to the callback function
     */
    function swapCallback(uint256 amountToPay, uint256 amountOut, bytes calldata data) external onlyDuringSwap {
        MaverickV1.invokeCallback(amountToPay, amountOut, data);
    }

    /**
     * @dev callback for Maverick V2 pools. Not allowed to be executed outside of a swap operation
     * @param tokenIn Token to be transferred to the caller
     * @param amountIn Amount of tokens to be transferred to the caller
     * @param amountOut Amount of tokens to be transferred to the caller
     * @param data Additional data to be passed to the callback function
     */
    function maverickV2SwapCallback(address tokenIn, uint256 amountIn, uint256 amountOut, bytes calldata data) external onlyDuringSwap {
        MaverickV2.invokeCallback(tokenIn, amountIn);
    }

    /**
     * @dev callback for Algebra V1 pairs. Not allowed to be executed outside of a swap operation
     * @param amount0Delta Amount of token0 to be transferred to the caller
     * @param amount1Delta Amount of token1 to be transferred to the caller
     * @param data Additional data to be passed to the callback function
     */
    function algebraSwapCallback(
        int256 amount0Delta,
        int256 amount1Delta,
        bytes calldata data
    ) external onlyDuringSwap {
        AlgebraV1.invokeCallback(amount0Delta, amount1Delta, data);
    }

    /**
     * @notice Called to msg.sender in iZiSwapPool#swapX2Y(DesireY) call
     * @param x Amount of tokenX trader will pay
     * @param data Any dadta passed though by the msg.sender via the iZiSwapPool#swapX2Y(DesireY) call
     */
    function swapX2YCallback(uint256 x, uint256 /*y*/, bytes calldata data) external onlyDuringSwap {
        Iziswap.transferTokens(x, data);
    }

    /**
     * @notice Called to msg.sender in iZiSwapPool#swapY2X(DesireX) call
     * @param y Amount of tokenY trader will pay
     * @param data Any dadta passed though by the msg.sender via the iZiSwapPool#swapY2X(DesireX) call
     */
    function swapY2XCallback(uint256 /*x*/, uint256 y, bytes calldata data) external onlyDuringSwap {
        Iziswap.transferTokens(y, data);
    }

    /**
     * @notice Callback for DODO v3 Pools
     * @param token Token to be transferred to the caller
     * @param value Amount of tokens to be transferred to the caller
     * @param data Additional data to be passed to the callback function
     */
    function d3MMSwapCallBack(address token, uint256 value, bytes calldata data) external {
        DODOV3.invokeCallback(token, value, data);
    }

    // Swap functions

    /**
     * @inheritdoc IWowmaxRouter
     */
    function swap(
        ExchangeRequest calldata request
    ) external payable virtual override reentrancyProtectedSwap returns (uint256[] memory amountsOut) {
        amountsOut = _swap(request);
    }

    /**
     * @dev swap inner logic
     */
    function _swap(ExchangeRequest calldata request) internal returns (uint256[] memory amountsOut) {
        checkRequest(request);
        uint256 amountIn = receiveTokens(request);
        for (uint256 i = 0; i < request.exchangeRoutes.length; i++) {
            exchange(request.exchangeRoutes[i]);
        }
        amountsOut = sendTokens(request);

        emit SwapExecuted(
            msg.sender,
            request.from == address(0) ? address(WETH) : request.from,
            amountIn,
            request.to,
            amountsOut
        );
    }

    /**
     * @dev receives tokens from the caller
     * @param request Exchange request that contains the token to be received parameters.
     */
    function receiveTokens(ExchangeRequest calldata request) private returns (uint256) {
        uint256 amountIn;
        if (msg.value > 0 && request.from == address(0) && request.amountIn == 0) {
            amountIn = msg.value;
            WETH.deposit{ value: amountIn }();
        } else {
            if (request.amountIn > 0) {
                amountIn = request.amountIn;
                IERC20(request.from).safeTransferFrom(msg.sender, address(this), amountIn);
            }
        }
        return amountIn;
    }

    /**
     * @dev sends swapped received tokens to the caller and treasury
     * @param request Exchange request that contains output tokens parameters
     */
    function sendTokens(ExchangeRequest calldata request) private returns (uint256[] memory amountsOut) {
        amountsOut = new uint256[](request.to.length);
        uint256 amountOut;
        IERC20 token;
        for (uint256 i = 0; i < request.to.length; i++) {
            token = IERC20(request.to[i]);
            amountOut = token.balanceOf(address(this));

            uint256 feeAmount;
            if (amountOut > request.amountOutExpected[i]) {
                feeAmount = amountOut - request.amountOutExpected[i];
                uint256 maxFeeAmount = (amountOut * maxFeePercentage) / 10000;
                if (feeAmount > maxFeeAmount) {
                    feeAmount = maxFeeAmount;
                    amountsOut[i] = amountOut - feeAmount;
                } else {
                    amountsOut[i] = request.amountOutExpected[i];
                }
            } else {
                require(
                    amountOut >= (request.amountOutExpected[i] * (10000 - request.slippage[i])) / 10000,
                    "WOWMAX: Insufficient output amount"
                );
                amountsOut[i] = amountOut;
            }

            if (address(token) == address(WETH)) {
                WETH.withdraw(amountOut);
            }

            transfer(token, treasury, feeAmount);
            transfer(token, msg.sender, amountsOut[i]);
        }
    }

    /**
     * @dev transfers token to the recipient
     * @param token Token to be transferred
     * @param to Recipient address
     * @param amount Amount to be transferred
     */
    function transfer(IERC20 token, address to, uint256 amount) private {
        //slither-disable-next-line incorrect-equality
        if (amount == 0) {
            return;
        }
        if (address(token) == address(WETH)) {
            //slither-disable-next-line arbitrary-send-eth //recipient is either a msg.sender or a treasury
            (bool sent, ) = payable(to).call{ value: amount }("");
            require(sent, "Wowmax: Failed to send native tokens");
        } else {
            token.safeTransfer(to, amount);
        }
    }

    /**
     * @dev executes an exchange operation according to the provided route
     * @param exchangeRoute Route to be executed
     */
    function exchange(ExchangeRoute calldata exchangeRoute) private returns (uint256) {
        uint256 amountIn = IERC20(exchangeRoute.from).balanceOf(address(this));
        uint256 amountOut;
        for (uint256 i = 0; i < exchangeRoute.swaps.length; i++) {
            amountOut += executeSwap(
                exchangeRoute.from,
                (amountIn * exchangeRoute.swaps[i].part) / exchangeRoute.parts,
                exchangeRoute.swaps[i]
            );
        }
        return amountOut;
    }

    /**
     * @dev executes a swap operation according to the provided parameters
     * @param from Token to be swapped
     * @param amountIn Amount to be swapped
     * @param swapData Swap data that contains the swap parameters
     */
    function executeSwap(address from, uint256 amountIn, Swap calldata swapData) private returns (uint256) {
        if (swapData.family == UNISWAP_V3) {
            return UniswapV3.swap(amountIn, swapData);
        } else if (swapData.family == HASHFLOW) {
            return Hashflow.swap(from, amountIn, swapData);
        } else if (swapData.family == WOMBAT) {
            return Wombat.swap(from, amountIn, swapData);
        } else if (swapData.family == LEVEL) {
            return Level.swap(from, amountIn, swapData);
        } else if (swapData.family == DODO_V2) {
            return DODOV2.swap(from, amountIn, swapData);
        } else if (swapData.family == DODO_V3) {
            return DODOV3.swap(from, amountIn, swapData);
        } else if (swapData.family == WOOFI) {
            return WooFi.swap(from, amountIn, swapData);
        } else if (swapData.family == UNISWAP_V2) {
            return UniswapV2.swap(from, amountIn, swapData);
        } else if (swapData.family == CURVE) {
            return Curve.swap(from, amountIn, swapData);
        } else if (swapData.family == PANCAKESWAP_STABLE) {
            return PancakeSwapStable.swap(from, amountIn, swapData);
        } else if (swapData.family == DODO_V1) {
            return DODOV1.swap(from, amountIn, swapData);
        } else if (swapData.family == BALANCER_V2) {
            return BalancerV2.swap(from, amountIn, swapData);
        } else if (swapData.family == MAVERICK_V1) {
            return MaverickV1.swap(amountIn, swapData);
        } else if (swapData.family == SADDLE) {
            return Saddle.swap(from, amountIn, swapData);
        } else if (swapData.family == FULCROM) {
            return Fulcrom.swap(from, amountIn, swapData);
        } else if (swapData.family == UNISWAP_V2_ROUTER) {
            return UniswapV2.routerSwap(from, amountIn, swapData);
        } else if (swapData.family == ELASTIC) {
            return Elastic.swap(from, amountIn, swapData);
        } else if (swapData.family == ALGEBRA_V1) {
            return AlgebraV1.swap(from, amountIn, swapData);
        } else if (swapData.family == ALGEBRA_V1_9) {
            return AlgebraV1.swap(from, amountIn, swapData);
        } else if (swapData.family == SYNCSWAP) {
            return SyncSwap.swap(from, amountIn, swapData);
        } else if (swapData.family == VOOI) {
            return Vooi.swap(from, amountIn, swapData);
        } else if (swapData.family == VELOCORE_V2) {
            return VelocoreV2.swap(address(WETH), from, amountIn, swapData);
        } else if (swapData.family == IZISWAP) {
            return Iziswap.swap(from, amountIn, swapData);
        } else if (swapData.family == VELODROME) {
            return Velodrome.swap(from, amountIn, swapData);
        } else if (swapData.family == WRAPPED_NATIVE) {
            return WrappedNative.swap(from, amountIn, swapData);
        } else if (swapData.family == LIQUIDITY_BOOK_V2_1) {
            return LiquiditybookV2_1.swap(from, amountIn, swapData);
        } else if (swapData.family == MAVERICK_V2) {
            return MaverickV2.swap(amountIn, swapData);
        } else {
            revert("WOWMAX: Unknown DEX family");
        }
    }

    // Checks and verifications

    /**
     * @dev checks the swap request parameters
     * @param request Exchange request to be checked
     */
    function checkRequest(ExchangeRequest calldata request) private view {
        require(request.to.length > 0, "WOWMAX: No output tokens specified");
        require(request.to.length == request.amountOutExpected.length, "WOWMAX: Wrong amountOutExpected length");
        require(request.to.length == request.slippage.length, "WOWMAX: Wrong slippage length");
        for (uint256 i = 0; i < request.to.length; i++) {
            require(request.to[i] != address(0), "WOWMAX: Wrong output token address");
            require(request.amountOutExpected[i] > 0, "WOWMAX: Wrong amountOutExpected value");
            require(request.slippage[i] <= maxSlippage, "WOWMAX: Slippage is too high");
        }
    }
}

File 2 of 36 : Ownable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)

pragma solidity ^0.8.0;

import "../utils/Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        _transferOwnership(_msgSender());
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

File 3 of 36 : draft-IERC20Permit.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol)

pragma solidity ^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);
}

File 4 of 36 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @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);

    /**
     * @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 `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, 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 `from` to `to` 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 from,
        address to,
        uint256 amount
    ) external returns (bool);
}

File 5 of 36 : SafeERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";
import "../extensions/draft-IERC20Permit.sol";
import "../../../utils/Address.sol";

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

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

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

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

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

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

    function safePermit(
        IERC20Permit token,
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        uint256 nonceBefore = token.nonces(owner);
        token.permit(owner, spender, value, deadline, v, r, s);
        uint256 nonceAfter = token.nonces(owner);
        require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
    }

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

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

File 6 of 36 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

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

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

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

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

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

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

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    function _revert(bytes memory returndata, string memory errorMessage) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert(errorMessage);
        }
    }
}

File 7 of 36 : Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}

File 8 of 36 : IWETH.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity =0.8.7;

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

/**
 * @title Wrapped Ether contract interface
 */
interface IWETH is IERC20 {
    /**
     * @dev `msg.value` of ETH sent to this contract grants caller account a matching increase in WETH token balance.
     */
    function deposit() external payable;

    /**
     * @dev Burn WETH token from caller account and withdraw matching ETH to the same.
     * @param wad Amount of WETH token to burn.
     */
    function withdraw(uint wad) external;
}

File 9 of 36 : IWowmaxRouter.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity =0.8.7;
import "./IWowmaxRouterBase.sol";

/**
 * @title IWowmaxRouter
 * @notice Interface for the Wowmax Router, describes the functions that can be called from the router
 * and corresponding data structures
 */
interface IWowmaxRouter is IWowmaxRouterBase {

    /**
     * @notice Executes a token swap
     * @dev if from token is address(0) and amountIn is 0,
     * then chain native token is used as a source token, and value is used as an input amount
     * @param request Exchange request to be executed
     * @return amountsOut Array of output amounts that were received for each target token
     */
    function swap(ExchangeRequest calldata request) external payable returns (uint256[] memory amountsOut);
}

File 10 of 36 : IWowmaxRouterBase.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity =0.8.7;

/**
 * @title IWowmaxRouterBase
 * @notice Interface for the Wowmax Router Base, describes data structures
 */
interface IWowmaxRouterBase {

    /**
     * @notice Swap operation details structure
     * @param to Target token address
     * @param part Part of the currently owned tokens to be swapped. Total number of parts
     * is defined in the ExchangeRoute structure
     * @param addr Contract address that performs the swap
     * @param family Contract DEX family
     * @param data Additional data that is required for a specific DEX protocol
     */
    struct Swap {
        address to;
        uint256 part;
        address addr;
        bytes32 family;
        bytes data;
    }

    /**
     * @notice Exchange route details structure
     * @param from Source token address
     * @param parts Total number of parts of the currently owned tokens
     * @param swaps Array of swaps for a specified token
     */
    struct ExchangeRoute {
        address from;
        uint256 parts;
        Swap[] swaps;
    }

    /**
     * @notice Exchange request details structure
     * @param from Source token address
     * @param amountIn Source token amount to swap
     * @param to Array of target token addresses
     * @param exchangeRoutes Array of exchange routes
     * @param slippage Array of slippage tolerance values for each target token
     * @param amountOutExpected Array fo expected output amounts for each target token
     */
    struct ExchangeRequest {
        address from;
        uint256 amountIn;
        address[] to;
        ExchangeRoute[] exchangeRoutes;
        uint256[] slippage;
        uint256[] amountOutExpected;
    }

    /**
     * @notice Emitted when a swap is executed
     * @param account Account that initiated the swap
     * @param from Source token address
     * @param amountIn Source token amount that was swapped
     * @param to Array of target token addresses
     * @param amountOut Array of amounts that were received for each target token
     */
    event SwapExecuted(
        address indexed account,
        address indexed from,
        uint256 amountIn,
        address[] to,
        uint256[] amountOut
    );
}

File 11 of 36 : AlgebraV1.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity =0.8.7;

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

/**
 * @title Algebra V1 pool interface
 */
interface IAlgebraV1Pool {
    /**
     * @notice Swap token0 for token1, or token1 for token0
     * @dev The caller of this method receives a callback in the form of IAlgebraSwapCallback# AlgebraSwapCallback
     * @param recipient The address to receive the output of the swap
     * @param zeroToOne The direction of the swap, true for token0 to token1, false for token1 to token0
     * @param amountSpecified The amount of the swap, which implicitly configures the swap as exact input (positive), or exact output (negative)
     * @param limitSqrtPrice The Q64.96 sqrt price limit. If zero for one, the price cannot be less than this
     * value after the swap. If one for zero, the price cannot be greater than this value after the swap
     * @param data Any data to be passed through to the callback. If using the Router it should contain
     * SwapRouter#SwapCallbackData
     * @return amount0 The delta of the balance of token0 of the pool, exact when negative, minimum when positive
     * @return amount1 The delta of the balance of token1 of the pool, exact when negative, minimum when positive
     */
    function swap(
        address recipient,
        bool zeroToOne,
        int256 amountSpecified,
        uint160 limitSqrtPrice,
        bytes calldata data
    ) external returns (int256 amount0, int256 amount1);

    /**
     * @dev Returns the address of the first token of the Uniswap v3 based pair
     * @return Address of the first token of the pair
     */
    function token0() external view returns (address);

    /**
     * @dev Returns the address of the second token of the Uniswap v3 based pair
     * @return Address of the second token of the pair
     */
    function token1() external view returns (address);
}

/**
 * @title Algebra V1 library
 * @notice Functions to swap tokens on Algebra V1 based protocols
 */
library AlgebraV1 {
    using SafeERC20 for IERC20;

    /**
     * @dev Swaps tokens on a Algebra V1 based protocol contract
     * @param from Address of token to swap from
     * @param amountIn Amount of token to swap
     * @param swapData Swap operation details. The `data` field should contain two int128 values,
     * which are the indices of the tokens to swap from and to
     * @return amountOut Amount of tokens received
     */
    function swap(
        address from,
        uint256 amountIn,
        IWowmaxRouter.Swap memory swapData
    ) internal returns (uint256 amountOut) {
        bool zeroToOne = from == IAlgebraV1Pool(swapData.addr).token0();
        uint160 sqrtPriceLimitX96 = zeroToOne ? 4295128740 : 1461446703485210103287273052203988822378723970341;
        (int256 amount0, int256 amount1) = IAlgebraV1Pool(swapData.addr).swap(
            address(this),
            zeroToOne,
            int256(amountIn),
            sqrtPriceLimitX96,
            new bytes(0)
        );
        amountOut = uint(zeroToOne ? -amount1 : -amount0);
    }

    /**
     * @dev Performs Algebra V1 callback, sends required amounts of tokens to the pair
     * @param amount0Delta Amount of the first token to send
     * @param amount1Delta Amount of the second token to send
     */
    function invokeCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata /*_data*/) internal {
        if (amount0Delta > 0 && amount1Delta < 0) {
            IERC20(IAlgebraV1Pool(msg.sender).token0()).safeTransfer(msg.sender, uint256(amount0Delta));
        } else if (amount0Delta < 0 && amount1Delta > 0) {
            IERC20(IAlgebraV1Pool(msg.sender).token1()).safeTransfer(msg.sender, uint256(amount1Delta));
        } else {
            revert("WOWMAX: Algebra V1 invariant violation");
        }
    }
}

File 12 of 36 : BalancerV2.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity =0.8.7;

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

/**
 * @title Balancer Asset interface
 */
interface IBalancerAsset {
    // solhint-disable-previous-line no-empty-blocks
}

/**
 * @title Balancer vault interface
 */
interface IBalancerVault {
    enum SwapKind {
        GIVEN_IN,
        GIVEN_OUT
    }

    /**
     * @dev Data for a single swap executed by `swap`. `amount` is either `amountIn` or `amountOut` depending on
     * the `kind` value.
     *
     * `assetIn` and `assetOut` are either token addresses, or the IAsset sentinel value for ETH (the zero address).
     * Note that Pools never interact with ETH directly: it will be wrapped to or unwrapped from WETH by the Vault.
     *
     * The `userData` field is ignored by the Vault, but forwarded to the Pool in the `onSwap` hook, and may be
     * used to extend swap behavior.
     */
    struct SingleSwap {
        bytes32 poolId;
        SwapKind kind;
        IBalancerAsset assetIn;
        IBalancerAsset assetOut;
        uint256 amount;
        bytes userData;
    }

    /**
     * @dev All tokens in a swap are either sent from the `sender` account to the Vault, or from the Vault to the
     * `recipient` account.
     *
     * If the caller is not `sender`, it must be an authorized relayer for them.
     *
     * If `fromInternalBalance` is true, the `sender`'s Internal Balance will be preferred, performing an ERC20
     * transfer for the difference between the requested amount and the User's Internal Balance (if any). The `sender`
     * must have allowed the Vault to use their tokens via `IERC20.approve()`. This matches the behavior of
     * `joinPool`.
     *
     * If `toInternalBalance` is true, tokens will be deposited to `recipient`'s internal balance instead of
     * transferred. This matches the behavior of `exitPool`.
     *
     * Note that ETH cannot be deposited to or withdrawn from Internal Balance: attempting to do so will trigger a
     * revert.
     */
    struct FundManagement {
        address sender;
        bool fromInternalBalance;
        address payable recipient;
        bool toInternalBalance;
    }

    /**
     * @dev Performs a swap with a single Pool.
     *
     * If the swap is 'given in' (the number of tokens to send to the Pool is known), it returns the amount of tokens
     * taken from the Pool, which must be greater than or equal to `limit`.
     *
     * If the swap is 'given out' (the number of tokens to take from the Pool is known), it returns the amount of tokens
     * sent to the Pool, which must be less than or equal to `limit`.
     *
     * Internal Balance usage and the recipient are determined by the `funds` struct.
     *
     * Emits a `Swap` event.
     */
    function swap(
        SingleSwap memory singleSwap,
        FundManagement memory funds,
        uint256 limit,
        uint256 deadline
    ) external payable returns (uint256);
}

/**
 * @title Balancer library
 * @notice Functions to swap tokens on Balancer and compatible protocols
 */
library BalancerV2 {
    using SafeERC20 for IERC20;

    /**
     * @dev Performs a swap through a Balancer based vault
     * @param from Address of a token to swap from
     * @param amountIn Amount of tokens to swap
     * @param swapData Swap operation details. The `data` field should contain poolId and vault address
     * @return amountOut Amount of tokens received
     */
    function swap(
        address from,
        uint256 amountIn,
        IWowmaxRouter.Swap memory swapData
    ) internal returns (uint256 amountOut) {
        (bytes32 poolId, address vaultAddress) = abi.decode(swapData.data, (bytes32, address));

        IBalancerVault.SingleSwap memory singleSwap = IBalancerVault.SingleSwap({
            poolId: poolId,
            kind: IBalancerVault.SwapKind.GIVEN_IN,
            assetIn: IBalancerAsset(from),
            assetOut: IBalancerAsset(swapData.to),
            amount: amountIn,
            userData: new bytes(0)
        });

        IBalancerVault.FundManagement memory funds = IBalancerVault.FundManagement({
            sender: address(this),
            fromInternalBalance: false,
            recipient: payable(address(this)),
            toInternalBalance: false
        });

        IERC20(from).safeIncreaseAllowance(vaultAddress, amountIn);

        amountOut = IBalancerVault(vaultAddress).swap(singleSwap, funds, 0, type(uint256).max);
    }
}

File 13 of 36 : Curve.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity =0.8.7;

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

/**
 * @title Curve pool interface
 */
interface ICurvePool {
    /**
     * @dev Swaps tokens on a Curve based protocol contract
     * @param i Index of token to swap from
     * @param j Index of token to swap to
     * @param dx Amount of token to swap
     * @param min_dy Minimum amount of tokens to receive
     */
    function exchange(int128 i, int128 j, uint256 dx, uint256 min_dy) external;
}

/**
 * @title Curve library
 * @notice Functions to swap tokens on Curve based protocols
 */
library Curve {
    using SafeERC20 for IERC20;

    /**
     * @dev Swaps tokens on a Curve based protocol contract
     * @param from Address of token to swap from
     * @param amountIn Amount of token to swap
     * @param swapData Swap operation details. The `data` field should contain two int128 values,
     * which are the indices of the tokens to swap from and to
     * @return amountOut Amount of tokens received
     */
    function swap(
        address from,
        uint256 amountIn,
        IWowmaxRouter.Swap memory swapData
    ) internal returns (uint256 amountOut) {
        (int128 i, int128 j) = abi.decode(swapData.data, (int128, int128));
        uint256 balanceBefore = IERC20(swapData.to).balanceOf(address(this));
        //slither-disable-next-line unused-return //it's safe to ignore
        IERC20(from).safeIncreaseAllowance(swapData.addr, amountIn);
        ICurvePool(swapData.addr).exchange(i, j, amountIn, 0);
        amountOut = IERC20(swapData.to).balanceOf(address(this)) - balanceBefore;
    }
}

File 14 of 36 : DODOV1.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity =0.8.7;

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

/**
 * @title DODO v1 pool interface
 */
interface IDODOV1Pool {
    /**
     * @dev Sells base token for quote token
     * @param amount Amount of base token to sell
     * @param minReceiveQuote Minimum amount of quote token to receive
     * @param data Additional data to be used in callback hook
     * @return amountOut Amount of quote token received
     */
    function sellBaseToken(uint256 amount, uint256 minReceiveQuote, bytes calldata data) external returns (uint256);

    /**
     * @dev Buys base token with quote token
     * @param amount Amount of base token to buy
     * @param maxPayQuote Maximum amount of quote token to pay
     * @param data Additional data to be used in callback hook
     * @return amountOut Amount of base token received
     */
    function buyBaseToken(uint256 amount, uint256 maxPayQuote, bytes calldata data) external returns (uint256);
}

/**
 * @title DODO v1 library
 * @notice Functions to swap tokens on DODO v1 protocol
 */
library DODOV1 {
    using SafeERC20 for IERC20;

    /**
     * @dev Swaps tokens on a Curve based protocol contract
     * @param from Address of token to swap from
     * @param amountIn Amount of token to swap
     * @param swapData Swap operation details. The `data` field should contain two int128 values, i and j,
     * which are the indexes of the tokens to swap
     * @return amountOut Amount of tokens received
     */
    function swap(
        address from,
        uint256 amountIn,
        IWowmaxRouter.Swap memory swapData
    ) internal returns (uint256 amountOut) {
        //slither-disable-next-line unused-return //it's safe to ignore
        IERC20(from).safeIncreaseAllowance(swapData.addr, amountIn);
        amountOut = IDODOV1Pool(swapData.addr).sellBaseToken(amountIn, 0, "");
    }
}

File 15 of 36 : DODOV2.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity =0.8.7;

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

/**
 * @title DODO v2 pool interface
 */
interface IDODOV2Pool {
    /**
     * @dev Swaps tokens on a DODO v2 based protocol contract
     * @param to Address of token to swap to
     * @return amountOut Amount of tokens received
     */
    function sellBase(address to) external returns (uint256);

    /**
     * @dev Swaps tokens on a DODO v2 based protocol contract
     * @param to Address of token to swap to
     * @return amountOut Amount of tokens received
     */
    function sellQuote(address to) external returns (uint256);
}

/**
 * @title DODO v2 library
 * @notice Functions to swap tokens on DODO v2 protocol
 */
library DODOV2 {
    uint8 internal constant BASE_TO_QUOTE = 0;

    using SafeERC20 for IERC20;

    /**
     * @dev Swaps tokens on a DODO v2 pool contract
     * @param from Address of token to swap from
     * @param amountIn Amount of tokens to swap
     * @param swapData Swap operation details. The `data` field should contain one uint8 value,
     * which is the direction of the swap
     * @return amountOut Amount of tokens received
     */
    function swap(
        address from,
        uint256 amountIn,
        IWowmaxRouter.Swap memory swapData
    ) internal returns (uint256 amountOut) {
        IERC20(from).safeTransfer(swapData.addr, amountIn);
        uint8 direction = abi.decode(swapData.data, (uint8));

        if (direction == BASE_TO_QUOTE) {
            amountOut = IDODOV2Pool(swapData.addr).sellBase(address(this));
        } else {
            amountOut = IDODOV2Pool(swapData.addr).sellQuote(address(this));
        }
    }
}

File 16 of 36 : DODOV3.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity =0.8.7;

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

/**
 * @title DODO v3 pool interface
 */
interface IDODOV3Pool {
    /**
     * @dev Swaps tokens on a DODO v3 based protocol contract
     * @param to Address to send swapped tokens to
     * @param fromToken Address of a token to swap from
     * @param toToken Address of a token to swap to
     * @param fromAmount Amount of tokens to swap
     * @param minReceiveAmount Minimal amount of tokens to receive
     * @param data Data to be passed to the Dodo V3 Swap Callback contract
     * @return Amount of tokens received
     */
    function sellToken(
        address to,
        address fromToken,
        address toToken,
        uint256 fromAmount,
        uint256 minReceiveAmount,
        bytes calldata data
    ) external returns (uint256);
}

/**
 * @title DODO v3 library
 * @notice Functions to swap tokens on DODO v3 protocol
 */
library DODOV3 {
    uint8 internal constant BASE_TO_QUOTE = 0;

    using SafeERC20 for IERC20;

    /**
     * @dev Swaps tokens on a DODO v3 pool contract
     * @param from Address of token to swap from
     * @param amountIn Amount of tokens to swap
     * @param swapData Swap operation details. The `data` field should be empty
     * @return amountOut Amount of tokens received
     */
    function swap(
        address from,
        uint256 amountIn,
        IWowmaxRouter.Swap memory swapData
    ) internal returns (uint256 amountOut) {
        return IDODOV3Pool(swapData.addr).sellToken(address(this), from, swapData.to, amountIn, 0, "");
    }

    /**
     * @dev Callback function to receive tokens from DODO v3 pool contract
     * @param token Address of token to receive
     * @param amount Amount of tokens to receive
     */
    function invokeCallback(address token, uint256 amount, bytes calldata /*data*/) internal {
        IERC20(token).safeTransfer(msg.sender, amount);
    }
}

File 17 of 36 : Elastic.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity =0.8.7;

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

/**
 * @title Elastic pool interface
 */
interface IElasticPool {
    /**
     * @notice Swap token0 -> token1, or vice versa
     * @dev This method's caller receives a callback in the form of ISwapCallback#swapCallback
     * @dev swaps will execute up to limitSqrtP or swapQty is fully used
     * @param recipient The address to receive the swap output
     * @param swapQty The swap quantity, which implicitly configures the swap as exact input (>0), or exact output (<0)
     * @param isToken0 Whether the swapQty is specified in token0 (true) or token1 (false)
     * @param limitSqrtP the limit of sqrt price after swapping
     * could be MAX_SQRT_RATIO-1 when swapping 1 -> 0 and MIN_SQRT_RATIO+1 when swapping 0 -> 1 for no limit swap
     * @param data Any data to be passed through to the callback
     * @return qty0 Exact token0 qty sent to recipient if < 0. Minimally received quantity if > 0.
     * @return qty1 Exact token1 qty sent to recipient if < 0. Minimally received quantity if > 0.
     */
    function swap(
        address recipient,
        int256 swapQty,
        bool isToken0,
        uint160 limitSqrtP,
        bytes calldata data
    ) external returns (int256 qty0, int256 qty1);

    /**
     * @dev Returns the address of the first token of the Uniswap v3 based pair
     * @return Address of the first token of the pair
     */
    function token0() external view returns (address);

    /**
     * @dev Returns the address of the second token of the Uniswap v3 based pair
     * @return Address of the second token of the pair
     */
    function token1() external view returns (address);
}

/**
 * @title Elastic library
 * @notice Functions to swap tokens on Elastic based protocols
 */
library Elastic {
    using SafeERC20 for IERC20;

    /**
     * @dev Swaps tokens on Elastic based protocol contract
     * @param from Address of token to swap from
     * @param amountIn Amount of token to swap
     * @param swapData Swap operation details. The `data` field should contain two int128 values,
     * which are the indices of the tokens to swap from and to
     * @return amountOut Amount of tokens received
     */
    function swap(
        address from,
        uint256 amountIn,
        IWowmaxRouter.Swap memory swapData
    ) internal returns (uint256 amountOut) {
        bool isToken0 = from == IElasticPool(swapData.addr).token0();
        uint160 sqrtPriceLimitX96 = isToken0 ? 4295128740 : 1461446703485210103287273052203988822378723970341;
        (int256 amount0, int256 amount1) = IElasticPool(swapData.addr).swap(
            address(this),
            int256(amountIn),
            isToken0,
            sqrtPriceLimitX96,
            new bytes(0)
        );
        amountOut = uint(isToken0 ? -amount1 : -amount0);
    }

    /**
     * @dev Performs Elastic callback, sends required amounts of tokens to the pair
     * @param deltaQty0 Amount of the first token to send
     * @param deltaQty1 Amount of the second token to send
     */
    function invokeCallback(int256 deltaQty0, int256 deltaQty1, bytes calldata /*_data*/) internal {
        if (deltaQty0 > 0 && deltaQty1 < 0) {
            IERC20(IElasticPool(msg.sender).token0()).safeTransfer(msg.sender, uint256(deltaQty0));
        } else if (deltaQty0 < 0 && deltaQty1 > 0) {
            IERC20(IElasticPool(msg.sender).token1()).safeTransfer(msg.sender, uint256(deltaQty1));
        } else {
            revert("WOWMAX: Elastic invariant violation");
        }
    }
}

File 18 of 36 : Fulcrom.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity =0.8.7;

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

/**
 * @title Fulcrom pool interface
 */
interface IFulcromPool {
    /**
     * @dev Swaps tokens on a Fulcrom based protocol contract
     * @param _tokenIn Address of a token to swap from
     * @param _tokenOut Address of a token to swap to
     * @param _receiver Address to send swapped tokens to
     * @return amountOut Amount of tokens received
     */
    function swap(address _tokenIn, address _tokenOut, address _receiver) external returns (uint256);
}

/**
 * @title Fulcrom library
 * @notice Functions to swap tokens on Fulcrom protocol
 */
library Fulcrom {
    using SafeERC20 for IERC20;

    /**
     * @dev Swaps tokens on a Fulcrom pool contract
     * @param from Address of a token to swap from
     * @param amountIn Amount of tokens to swap
     * @param swapData Swap operation details. The `data` field should be empty
     * @return amountOut Amount of tokens received
     */
    function swap(
        address from,
        uint256 amountIn,
        IWowmaxRouter.Swap memory swapData
    ) internal returns (uint256 amountOut) {
        IERC20(from).safeTransfer(swapData.addr, amountIn);
        amountOut = IFulcromPool(swapData.addr).swap(from, swapData.to, address(this));
    }
}

File 19 of 36 : Hashflow.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity =0.8.7;

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

/**
 * @title Hashflow Router interface
 */
interface IHashflowRouter {
    struct RFQTQuote {
        /// @notice The address of the HashflowPool to trade against.
        address pool;
        /**
         * @notice The external account linked to the HashflowPool.
         * If the HashflowPool holds funds, this should be address(0).
         */
        address externalAccount;
        /// @notice The recipient of the quoteToken at the end of the trade.
        address trader;
        /**
         * @notice The account "effectively" making the trade (ultimately receiving the funds).
         * This is commonly used by aggregators, where a proxy contract (the 'trader')
         * receives the quoteToken, and the effective trader is the user initiating the call.
         *
         * This field DOES NOT influence movement of funds. However, it is used to check against
         * quote replay.
         */
        address effectiveTrader;
        /// @notice The token that the trader sells.
        address baseToken;
        /// @notice The token that the trader buys.
        address quoteToken;
        /**
         * @notice The amount of baseToken sold in this trade. The exchange rate
         * is going to be preserved as the quoteTokenAmount / baseTokenAmount ratio.
         *
         * Most commonly, effectiveBaseTokenAmount will == baseTokenAmount.
         */
        uint256 effectiveBaseTokenAmount;
        /// @notice The max amount of baseToken sold.
        uint256 baseTokenAmount;
        /// @notice The amount of quoteToken bought when baseTokenAmount is sold.
        uint256 quoteTokenAmount;
        /// @notice The Unix timestamp (in seconds) when the quote expires.
        /// @dev This gets checked against block.timestamp.
        uint256 quoteExpiry;
        /// @notice The nonce used by this effectiveTrader. Nonces are used to protect against replay.
        uint256 nonce;
        /// @notice Unique identifier for the quote.
        /// @dev Generated off-chain via a distributed UUID generator.
        bytes32 txid;
        /// @notice Signature provided by the market maker (EIP-191).
        bytes signature;
    }

    /**
     * @notice Executes an intra-chain RFQ-T trade.
     * @param quote The quote data to be executed.
     */
    function tradeRFQT(RFQTQuote memory quote) external payable;
}

// @title Hashflow library
// @notice Functions to swap tokens on Hashflow protocol
library Hashflow {
    using SafeERC20 for IERC20;

    /**
     * @dev Swaps tokens on a Hashflow router contract
     * @param from Address of a token to swap from
     * @param amountIn Amount of tokens to swap
     * @param swapData Swap operation details. The `data` field should contain encoded RFQTQuote structure
     * @return amountOut Amount of tokens received
     */
    function swap(
        address from,
        uint256 amountIn,
        IWowmaxRouter.Swap memory swapData
    ) internal returns (uint256 amountOut) {
        IHashflowRouter.RFQTQuote memory quote = abi.decode(swapData.data, (IHashflowRouter.RFQTQuote));
        //slither-disable-next-line unused-return //it's safe to ignore
        IERC20(from).safeIncreaseAllowance(swapData.addr, amountIn);
        if (amountIn < quote.baseTokenAmount) {
            quote.effectiveBaseTokenAmount = amountIn;
        }
        uint256 balanceBefore = IERC20(swapData.to).balanceOf(address(this));
        IHashflowRouter(swapData.addr).tradeRFQT(quote);
        amountOut = IERC20(swapData.to).balanceOf(address(this)) - balanceBefore;
    }
}

File 20 of 36 : Iziswap.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity =0.8.7;

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

/**
 * @title Iziswap pool interface
 */
interface IIziswapPool {
    /**
     * @notice Swap tokenX for tokenY, given max amount of tokenX user willing to pay.
     * @param recipient the address to receive tokenY
     * @param amount the max amount of tokenX user willing to pay
     * @param lowPt the lowest point(price) of x/y during swap
     * @param data any data to be passed through to the callback
     * @return amountX amount of tokenX acquired
     * @return amountY amount of tokenY payed
     */
    function swapX2Y(
        address recipient,
        uint128 amount,
        int24 lowPt,
        bytes calldata data
    ) external returns (uint256 amountX, uint256 amountY);

    /**
     * @notice Swap tokenY for tokenX, given max amount of tokenY user willing to pay.
     * @param recipient the address to receive tokenX
     * @param amount the max amount of tokenY user willing to pay
     * @param highPt the highest point(price) of x/y during swap
     * @param data any data to be passed through to the callback
     * @return amountX amount of tokenX payed
     * @return amountY amount of tokenY acquired
     */
    function swapY2X(
        address recipient,
        uint128 amount,
        int24 highPt,
        bytes calldata data
    ) external returns (uint256 amountX, uint256 amountY);
}

/**
 * @title Iziswap library
 * @notice Functions to swap tokens on Iziswap based protocols
 */
library Iziswap {
    uint256 constant X2Y = 0;
    using SafeERC20 for IERC20;

    /**
     * @dev Swaps tokens on a Iziswap based protocol contract
     * @param from Address of token to swap from
     * @param amountIn Amount of token to swap
     * @param swapData Swap operation details. The `data` field should contain two int128 values,
     * which are the indices of the tokens to swap from and to
     * @return amountOut Amount of tokens received
     */
    function swap(
        address from,
        uint256 amountIn,
        IWowmaxRouter.Swap memory swapData
    ) internal returns (uint256 amountOut) {
        int24 pt = abi.decode(swapData.data, (int24));
        //slither-disable-next-line unused-return //it's safe to ignore
        bytes memory data = abi.encode(from);

        if (from < swapData.to) {
            (, amountOut) = IIziswapPool(swapData.addr).swapX2Y(address(this), uint128(amountIn), pt, data);
        } else {
            (amountOut, ) = IIziswapPool(swapData.addr).swapY2X(address(this), uint128(amountIn), pt, data);
        }
    }

    function transferTokens(uint256 amount, bytes calldata data) internal {
        address token = abi.decode(data, (address));
        IERC20(token).safeTransfer(msg.sender, amount);
    }
}

File 21 of 36 : Level.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity =0.8.7;

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

/**
 * @title Level pool interface
 */
interface ILevelPool {
    /**
     * @dev Swaps tokens on a Level based protocol contract
     * @param _tokenIn Address of a token to swap from
     * @param _tokenOut Address of a token to swap to
     * @param _minOut Minimal amount of tokens to receive
     * @param _to Address to send swapped tokens to
     * @param extradata Data to be used in callback
     */
    function swap(address _tokenIn, address _tokenOut, uint256 _minOut, address _to, bytes calldata extradata) external;
}

/**
 * @title Level library
 * @notice Functions to swap tokens on Level protocol
 */
library Level {
    using SafeERC20 for IERC20;

    /**
     * @dev Swaps tokens on a Level pool contract
     * @param from Address of a token to swap from
     * @param amountIn Amount of tokens to swap
     * @param swapData Swap operation details. The `data` field should be empty
     * @return amountOut Amount of tokens received
     */
    function swap(
        address from,
        uint256 amountIn,
        IWowmaxRouter.Swap memory swapData
    ) internal returns (uint256 amountOut) {
        IERC20(from).safeTransfer(swapData.addr, amountIn);
        uint256 balanceBefore = IERC20(swapData.to).balanceOf(address(this));
        ILevelPool(swapData.addr).swap(from, swapData.to, 0, address(this), new bytes(0));
        amountOut = IERC20(swapData.to).balanceOf(address(this)) - balanceBefore;
    }
}

File 22 of 36 : LiquiditybookV2_1.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity =0.8.7;

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

/**
 * @title Liquiditybook V2.1 pool interface
 */
interface ILiquiditybookV2_1Pool {
    /**
     * @notice Swap tokens iterating over the bins until the entire amount is swapped.
     * Token X will be swapped for token Y if `swapForY` is true, and token Y for token X if `swapForY` is false.
     * This function will not transfer the tokens from the caller, it is expected that the tokens have already been
     * transferred to this contract through another contract, most likely the router.
     * That is why this function shouldn't be called directly, but only through one of the swap functions of a router
     * that will also perform safety checks, such as minimum amounts and slippage.
     * The variable fee is updated throughout the swap, it increases with the number of bins crossed.
     * The oracle is updated at the end of the swap.
     * @param swapForY Whether you're swapping token X for token Y (true) or token Y for token X (false)
     * @param to The address to send the tokens to
     * @return amountsOut The encoded amounts of token X and token Y sent to `to`
     */
    function swap(bool swapForY, address to) external returns (bytes32 amountsOut);
}

/**
 * @title Liquiditybook V2.1 library
 * @notice Functions to swap tokens on Liquiditybook V2.1 based protocols
 */
library LiquiditybookV2_1 {
    using SafeERC20 for IERC20;

    /**
     * @dev Swaps tokens on a Liquiditybook V2.1 based protocol contract
     * @param from Address of token to swap from
     * @param amountIn Amount of token to swap
     * @param swapData Swap operation details. The `data` field should contain swapForY flag
     * @return amountOut Amount of tokens received
     */
    function swap(
        address from,
        uint256 amountIn,
        IWowmaxRouter.Swap memory swapData
    ) internal returns (uint256 amountOut) {
        bool swapForY = abi.decode(swapData.data, (uint256)) == 1;
        uint256 balanceBefore = IERC20(swapData.to).balanceOf(address(this));
        IERC20(from).safeTransfer(swapData.addr, amountIn);
        ILiquiditybookV2_1Pool(swapData.addr).swap(
            swapForY,
            address(this)
        );
        amountOut = IERC20(swapData.to).balanceOf(address(this)) - balanceBefore;
    }
}

File 23 of 36 : MaverickV1.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity =0.8.7;

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

/**
 * @title Maverick V1 pool interface
 */
interface IMaverickV1Pool {
    /**
     * @dev Swaps tokens on a Maverick v1 based pool
     * @param recipient Address to send swapped tokens to
     * @param amount Amount of tokens to swap
     * @param tokenAIn Flag that indicates token direction. True for tokenA to tokenB direction,
     * false for tokenB to tokenA
     * @param exactOutput Flag that indicates whether the amount is exact output or input
     * @param sqrtPriceLimit Price limit within which swap is processed
     * @param data Additional data to be send in callback function
     * @return amountIn Amount of the input token to be paid
     * @return amountOut Amount of the output token to be received
     */
    function swap(
        address recipient,
        uint256 amount,
        bool tokenAIn,
        bool exactOutput,
        uint256 sqrtPriceLimit,
        bytes calldata data
    ) external returns (uint256 amountIn, uint256 amountOut);

    /**
     * @dev Returns the address of the first token of the Maverick v1 based pair
     * @return Address of the first token of the pair
     */
    function tokenA() external view returns (address);

    /**
     * @dev Returns the address of the second token of the Maverick v1 based pair
     * @return Address of the second token of the pair
     */
    function tokenB() external view returns (address);
}

/**
 * @title Maverick v1 library
 * @notice Functions to swap tokens on Maverick v1 and compatible protocol
 */
library MaverickV1 {
    using SafeERC20 for IERC20;

    /**
     * @dev Swaps tokens on a Maverick v1 based pair
     * @param amountIn Amount of tokens to swap
     * @param swapData Swap data. The `data` field should contain zeroForOne flag
     * @return amountOut Amount of tokens received
     */
    function swap(uint256 amountIn, IWowmaxRouter.Swap memory swapData) internal returns (uint256 amountOut) {
        bool tokenAIn = abi.decode(swapData.data, (bool));
        (, amountOut) = IMaverickV1Pool(swapData.addr).swap(address(this), amountIn, tokenAIn, false, 0, swapData.data);
    }

    /**
     * @dev Performs Maverick v1 callback, sends required amounts of tokens to the pool
     * @param amountToPay amount to pay
     * @param _data encoded swap direction
     */
    function invokeCallback(uint256 amountToPay, uint256 /*amountOut*/, bytes calldata _data) internal {
        bool tokenAIn = abi.decode(_data, (bool));
        if (tokenAIn) {
            IERC20(IMaverickV1Pool(msg.sender).tokenA()).safeTransfer(msg.sender, amountToPay);
        } else {
            IERC20(IMaverickV1Pool(msg.sender).tokenB()).safeTransfer(msg.sender, amountToPay);
        }
    }
}

File 24 of 36 : MaverickV2.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity =0.8.7;

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

/**
 * @title Maverick V2 pool interface
 */
interface IMaverickV2Pool {
    /**
     * @notice Parameters for swap.
     * @param amount Amount of the token that is either the input if exactOutput is false
     * or the output if exactOutput is true.
     * @param tokenAIn Boolean indicating whether tokenA is the input.
     * @param exactOutput Boolean indicating whether the amount specified is
     * the exact output amount (true).
     * @param tickLimit The furthest tick a swap will execute in. If no limit
     * is desired, value should be set to type(int32).max for a tokenAIn swap
     * and type(int32).min for a swap where tokenB is the input.
     */
    struct SwapParams {
        uint256 amount;
        bool tokenAIn;
        bool exactOutput;
        int32 tickLimit;
    }

    /**
     * @notice Swap tokenA/tokenB assets in the pool.  The swap user has two
     * options for funding their swap.
     * - The user can push the input token amount to the pool before calling
     * the swap function. In order to avoid having the pool call the callback,
     * the user should pass a zero-length `data` bytes object with the swap
     * call.
     * - The user can send the input token amount to the pool when the pool
     * calls the `maverickV2SwapCallback` function on the calling contract.
     * That callback has input parameters that specify the token address of the
     * input token, the input and output amounts, and the bytes data sent to
     * the swap function.
     * @dev  If the users elects to do a callback-based swap, the output
     * assets will be sent before the callback is called, allowing the user to
     * execute flash swaps.  However, the pool does have reentrancy protection,
     * so a swapper will not be able to interact with the same pool again
     * while they are in the callback function.
     * @param recipient The address to receive the output tokens.
     * @param params Parameters containing the details of the swap
     * @param data Bytes information that gets passed to the callback.
     */
    function swap(
        address recipient,
        SwapParams memory params,
        bytes calldata data
    ) external returns (uint256 amountIn, uint256 amountOut);

    /**
     * @dev Returns the address of the first token of the Maverick V2 based pair
     * @return Address of the first token of the pair
     */
    function tokenA() external view returns (address);

    /**
     * @dev Returns the address of the second token of the Maverick V2 based pair
     * @return Address of the second token of the pair
     */
    function tokenB() external view returns (address);
}

/**
 * @title Maverick V2 library
 * @notice Functions to swap tokens on Maverick V2 and compatible protocol
 */
library MaverickV2 {
    using SafeERC20 for IERC20;

    /**
     * @dev Swaps tokens on a Maverick V2 based pair
     * @param amountIn Amount of tokens to swap
     * @param swapData Swap data. The `data` field should contain tokenAIn flag
     * @return amountOut Amount of tokens received
     */
    function swap(uint256 amountIn, IWowmaxRouter.Swap memory swapData) internal returns (uint256 amountOut) {
        bool tokenAIn = abi.decode(swapData.data, (bool));
        int32 tickLimit = tokenAIn ? type(int32).max : type(int32).min;
        IMaverickV2Pool.SwapParams memory params = IMaverickV2Pool.SwapParams({
            amount: amountIn,
            tokenAIn: tokenAIn,
            exactOutput: false,
            tickLimit: tickLimit
        });
        (, amountOut) = IMaverickV2Pool(swapData.addr).swap(address(this), params, swapData.data);
    }

    /**
     * @dev Performs Maverick V2 callback, sends required amounts of tokens to the pool
     * @param tokenIn token to pay
     * @param amountIn amount to pay
     */
    function invokeCallback(address tokenIn, uint256 amountIn) internal {
        IERC20(tokenIn).safeTransfer(msg.sender, amountIn);
    }
}

File 25 of 36 : PancakeSwapStable.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity =0.8.7;

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

/**
 * @title PancakeSwap pool interface
 */
interface IPancakeStablePool {
    /**
     * @notice Exchange `dx` amount of `i` token to at least `min_dy` amount of `j` token
     * @dev Same as Curve but uses uint256 instead of int128
     * @param i Index of token to swap from
     * @param j Index of token to swap to
     * @param dx Amount of `i` token to swap from
     * @param min_dy Minimum amount of `j` token to receive
     */
    function exchange(uint256 i, uint256 j, uint256 dx, uint256 min_dy) external;
}

/**
 * @title PancakeSwap library
 * @notice Functions to swap tokens on PancakeSwap like protocols
 */
library PancakeSwapStable {
    using SafeERC20 for IERC20;

    /**
     * @notice Swaps tokens on a PancakeSwap Stable like protocol contract
     * @param from Address of token to swap from
     * @param amountIn Amount of token to swap
     * @param swapData Swap operation details. The `data` field should contain two int128 values,
     * which are the indices of the tokens to swap from and to
     * @return amountOut Amount of tokens received
     */
    function swap(
        address from,
        uint256 amountIn,
        IWowmaxRouter.Swap memory swapData
    ) internal returns (uint256 amountOut) {
        (int128 i, int128 j) = abi.decode(swapData.data, (int128, int128));
        uint256 balanceBefore = IERC20(swapData.to).balanceOf(address(this));
        //slither-disable-next-line unused-return //it's safe to ignore
        IERC20(from).safeIncreaseAllowance(swapData.addr, amountIn);
        IPancakeStablePool(swapData.addr).exchange(uint256(uint128(i)), uint256(uint128(j)), amountIn, 0);
        amountOut = IERC20(swapData.to).balanceOf(address(this)) - balanceBefore;
    }
}

File 26 of 36 : Saddle.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity =0.8.7;

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

/**
 * @title Saddle pool interface
 */
interface ISaddlePool {
    /**
     * @dev Swaps tokens on a Saddle based protocol contract
     * @param tokenIndexFrom Index of a token to swap from
     * @param tokenIndexTo Index of a token to swap to
     * @param dx Amount of tokens to swap
     * @param minDy Minimum amount of tokens to receive
     * @param deadline Timestamp after which transaction will revert
     */
    function swap(uint8 tokenIndexFrom, uint8 tokenIndexTo, uint256 dx, uint256 minDy, uint256 deadline) external;
}

/**
 * @title Saddle library
 * @notice Functions to swap tokens on Saddle protocol
 */
library Saddle {
    using SafeERC20 for IERC20;

    /**
     * @dev Swaps tokens on a Saddle pool contract
     * @param from Address of a token to swap from
     * @param amountIn Amount of tokens to swap
     * @param swapData Swap operation details. The `data` field should contain two uint8 values,
     * which are tokenIndexFrom and tokenIndexTo
     * @return amountOut Amount of tokens received
     */
    function swap(
        address from,
        uint256 amountIn,
        IWowmaxRouter.Swap memory swapData
    ) internal returns (uint256 amountOut) {
        (uint8 tokenIndexFrom, uint8 tokenIndexTo) = abi.decode(swapData.data, (uint8, uint8));
        uint256 balanceBefore = IERC20(swapData.to).balanceOf(address(this));
        //slither-disable-next-line unused-return //it's safe to ignore
        IERC20(from).safeIncreaseAllowance(swapData.addr, amountIn);
        ISaddlePool(swapData.addr).swap(tokenIndexFrom, tokenIndexTo, amountIn, 0, type(uint256).max);
        amountOut = IERC20(swapData.to).balanceOf(address(this)) - balanceBefore;
    }
}

File 27 of 36 : SyncSwap.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity =0.8.7;

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

/**
 * @title SyncSwap pool interface
 */
interface ISyncSwapPool {
    /**
     * @dev Token amount structure
     * @param token Address of a token
     * @param amount Amount of tokens
     */
    struct TokenAmount {
        address token;
        uint amount;
    }

    /**
     * @dev Swaps between tokens.
     * @param data Swap operation details
     * @param sender Address of the sender
     * @param callback Callback address
     * @param callbackData Callback data
     * @return tokenAmount Amount of tokens received
     */
    function swap(
        bytes calldata data,
        address sender,
        address callback,
        bytes calldata callbackData
    ) external returns (TokenAmount memory tokenAmount);

    /**
     * @dev Returns the address of the SyncSwap pool vault
     * @return Vault address
     */
    function vault() external view returns (address);
}

/**
 * @title SyncSwap vault interface
 */
interface ISyncSwapVault {
    /**
     * @dev Deposits tokens to the SyncSwap vault
     * @param token Address of a token to deposit
     * @param to SyncSwap pool address
     * @return amount Amount of tokens deposited
     */
    function deposit(address token, address to) external payable returns (uint amount);
}

/**
 * @title SyncSwap library
 * @notice Functions to swap tokens on SyncSwap protocol
 */
library SyncSwap {
    using SafeERC20 for IERC20;

    /**
     * @dev Swaps tokens on a SyncSwap pool contract
     * @param from Address of a token to swap from
     * @param amountIn Amount of tokens to swap
     * @param swapData Swap operation details. The `data` field should be empty
     * @return amountOut Amount of tokens received
     */
    function swap(
        address from,
        uint256 amountIn,
        IWowmaxRouter.Swap memory swapData
    ) internal returns (uint256 amountOut) {
        ISyncSwapVault vault = ISyncSwapVault(ISyncSwapPool(swapData.addr).vault());
        IERC20(from).safeTransfer(address(vault), amountIn);
        vault.deposit(from, swapData.addr);
        bytes memory data = abi.encode(from, address(this), uint8(2)); // from, to, withdrawMode (0 - default, 1 - unwrapped, 2 - wrapped)
        ISyncSwapPool.TokenAmount memory out = ISyncSwapPool(swapData.addr).swap(
            data,
            address(0x0),
            address(0x0),
            new bytes(0)
        );
        return out.amount;
    }
}

File 28 of 36 : UniswapV2.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity =0.8.7;

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

/**
 * @title Uniswap v2 pair interface
 */
interface IUniswapV2Pair {
    /**
     * @dev Returns the address of the first token of the Uniswap v2 based pair
     * @return Address of the first token of the pair
     */
    function token0() external view returns (address);

    /**
     * @dev Returns the address of the second token of the Uniswap v2 based pair
     * @return Address of the second token of the pair
     */
    function token1() external view returns (address);

    /**
     * @dev Returns the Uniswap v2 based pair reserves
     * @return _reserve0 Reserve of the first token in the pair
     * @return _reserve1 Reserve of the second token in the pair
     * @return _blockTimestampLast Block timestamp of the last reserve update
     */
    function getReserves() external view returns (uint112 _reserve0, uint112 _reserve1, uint32 _blockTimestampLast);

    /**
     * @dev Swaps tokens on a Uniswap v2 based pair
     * @param amount0Out Amount of the first token to receive
     * @param amount1Out Amount of the second token to receive
     * @param to Address to send tokens to
     * @param data Data to be send in callback function, if any
     */
    function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;

    /**
     * @dev Returns the address of the Uniswap v2 based pair router
     * @return Router address
     */
    function router() external returns (address);
}

/**
 * @title Uniswap v2 router interface
 */
interface IUniswapV2Router {
    /**
     * @dev Swaps tokens on a Uniswap v2 based router
     * @param amountIn Amount of tokens to swap
     * @param amountOutMin Minimal amount of tokens to receive
     * @param path Sequence of tokens to perform swap through
     * @param to Address to send swapped tokens to
     * @param deadline Timestamp after which the transaction will revert,
     * 0 if it is not defined
     * @return amounts Amounts of tokens received for each swap step
     */
    function swapExactTokensForTokens(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external returns (uint256[] memory amounts);
}

/**
 * @title Uniswap v2 library
 * @notice Functions to swap tokens on Uniswap v2 and compatible protocols
 */
library UniswapV2 {
    /**
     * @dev Common fee denominator
     */
    uint256 private constant FEE_DENOMINATOR = 10000;

    using SafeERC20 for IERC20;

    /**
     * @dev Performs a direct swap through a Uniswap v2 based pair
     * @param from Address of a token to swap from
     * @param amountIn Amount of tokens to swap
     * @param swapData Swap operation details. The `data` field should contain the fee value specific for the pair
     * @return amountOut Amount of tokens received
     */
    function swap(
        address from,
        uint256 amountIn,
        IWowmaxRouter.Swap memory swapData
    ) internal returns (uint256 amountOut) {
        IERC20(from).safeTransfer(swapData.addr, amountIn);
        uint256 fee = abi.decode(swapData.data, (uint256));
        bool directSwap = IUniswapV2Pair(swapData.addr).token0() == from;
        (uint112 reserveIn, uint112 reserveOut) = getReserves(swapData.addr, directSwap);
        amountOut = getAmountOut(amountIn, reserveIn, reserveOut, fee);
        if (amountOut > 0) {
            IUniswapV2Pair(swapData.addr).swap(
                directSwap ? 0 : amountOut,
                directSwap ? amountOut : 0,
                address(this),
                new bytes(0)
            );
        }
    }

    /**
     * @dev Performs a swap through a Uniswap v2 based protocol using a router contract
     * @param from Address of a token to swap from
     * @param amountIn Amount of tokens to swap
     * @param swapData Swap operation details
     * data field should be empty
     * @return amountOut Amount of tokens received
     */
    function routerSwap(
        address from,
        uint256 amountIn,
        IWowmaxRouter.Swap memory swapData
    ) internal returns (uint256 amountOut) {
        IUniswapV2Router router = IUniswapV2Router(IUniswapV2Pair(swapData.addr).router());
        IERC20(from).safeIncreaseAllowance(address(router), amountIn);
        address[] memory path = new address[](2);
        path[0] = from;
        path[1] = swapData.to;
        return router.swapExactTokensForTokens(amountIn, 0, path, address(this), type(uint256).max)[1];
    }

    /**
     * @dev Returns the reserves of a Uniswap v2 based pair ordered according to swap direction
     * @param pair Address of a Uniswap v2 based pair
     * @param directSwap True if the first token of the pair is the token to swap from, false otherwise
     * @return reserveIn Reserve of the token to swap from
     * @return reserveOut Reserve of the token to swap to
     */
    function getReserves(address pair, bool directSwap) private view returns (uint112 reserveIn, uint112 reserveOut) {
        (uint112 reserve0, uint112 reserve1, ) = IUniswapV2Pair(pair).getReserves();
        return directSwap ? (reserve0, reserve1) : (reserve1, reserve0);
    }

    /**
     * @dev Returns the amount of tokens to be received for a given input amount and pair reserves
     * @param amountIn Amount of tokens to swap
     * @param reserveIn Reserve of the token to swap from
     * @param reserveOut Reserve of the token to swap to
     * @param fee Fee of the Uniswap v2 based pair. This value is specific for the pair
     * and/or it's DEX, and should be provided externally
     * @return amountOut Amount of tokens received
     */
    function getAmountOut(
        uint256 amountIn,
        uint112 reserveIn,
        uint112 reserveOut,
        uint256 fee
    ) private pure returns (uint256 amountOut) {
        uint256 amountInWithFee = amountIn * (FEE_DENOMINATOR - fee);
        uint256 numerator = amountInWithFee * reserveOut;
        uint256 denominator = reserveIn * FEE_DENOMINATOR + amountInWithFee;
        amountOut = numerator / denominator;
    }
}

File 29 of 36 : UniswapV3.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity =0.8.7;

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

/**
 * @title Uniswap v2 pair interface
 */
interface IUniswapV3Pool {
    /**
     * @dev Swaps tokens on a Uniswap v3 based pair
     * @param recipient Address to send swapped tokens to
     * @param zeroForOne Flag that indicates token direction. True for token0 to token1 direction,
     * false for token1 to token0
     * @param amountSpecified Amount of tokens to swap
     * @param sqrtPriceLimitX96 Price limit within which swap is processed
     * @param data Additional data to be send in callback function
     * @return amount0 Amount of the first token received
     * @return amount1 Amount of the second token received
     */
    function swap(
        address recipient,
        bool zeroForOne,
        int256 amountSpecified,
        uint160 sqrtPriceLimitX96,
        bytes calldata data
    ) external returns (int256 amount0, int256 amount1);

    /**
     * @dev Returns the address of the first token of the Uniswap v3 based pair
     * @return Address of the first token of the pair
     */
    function token0() external view returns (address);

    /**
     * @dev Returns the address of the second token of the Uniswap v3 based pair
     * @return Address of the second token of the pair
     */
    function token1() external view returns (address);
}

/**
 * @title Uniswap v3 library
 * @notice Functions to swap tokens on Uniswap v3 and compatible protocol
 */
library UniswapV3 {
    using SafeERC20 for IERC20;

    /**
     * @dev Swaps tokens on a Uniswap v3 based pair
     * @param amountIn Amount of tokens to swap
     * @param swapData Swap data. The `data` field should contain zeroForOne flag
     * @return amountOut Amount of tokens received
     */
    function swap(uint256 amountIn, IWowmaxRouter.Swap memory swapData) internal returns (uint256 amountOut) {
        bool zeroForOne = abi.decode(swapData.data, (bool));
        uint160 sqrtPriceLimitX96 = zeroForOne ? 4295128740 : 1461446703485210103287273052203988822378723970341;
        (int256 amount0, int256 amount1) = IUniswapV3Pool(swapData.addr).swap(
            address(this),
            zeroForOne,
            int256(amountIn),
            sqrtPriceLimitX96,
            new bytes(0)
        );
        amountOut = uint(zeroForOne ? -amount1 : -amount0);
    }

    /**
     * @dev Performs Uniswap v3 callback, sends required amounts of tokens to the pair
     * @param amount0Delta Amount of the first token to send
     * @param amount1Delta Amount of the second token to send
     */
    function invokeCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata /*_data*/) internal {
        if (amount0Delta > 0 && amount1Delta < 0) {
            IERC20(IUniswapV3Pool(msg.sender).token0()).safeTransfer(msg.sender, uint256(amount0Delta));
        } else if (amount0Delta < 0 && amount1Delta > 0) {
            IERC20(IUniswapV3Pool(msg.sender).token1()).safeTransfer(msg.sender, uint256(amount1Delta));
        } else {
            revert("WOWMAX: Uniswap v3 invariant violation");
        }
    }

    /**
     * @notice Try to decode provided "dataWithSelector" as Uniswap V3 callback.
     * @return Flag indicating whether decoding succeeded, followed by decoded amount0Delta, amount1Delta and data.
     */
    function decodeCallback(
        bytes calldata dataWithSelector
    ) internal pure returns (bool, int256, int256, bytes calldata) {
        int256 amount0Delta;
        int256 amount1Delta;
        bytes calldata data;

        assembly {
            amount0Delta := calldataload(add(dataWithSelector.offset, 4))
            amount1Delta := calldataload(add(dataWithSelector.offset, 36))

            // get offset of bytes length: selector + (amount0Delta | amount1Delta | data length offset | data length | data).
            // "length offset" is relative to start of first parameter in data.
            let dataLenOffset := add(add(dataWithSelector.offset, 4), calldataload(add(dataWithSelector.offset, 68)))
            data.length := calldataload(dataLenOffset)
            data.offset := add(dataLenOffset, 32)
        }

        // validate that what we got matches what we expect
        unchecked {
            // account for padding in 32-byte word unaligned data
            uint256 paddedDataLen = data.length;
            uint256 remainder = data.length % 32;
            if (remainder > 0) {
                paddedDataLen = paddedDataLen + (32 - remainder);
            }
            // 132 = 4 (selector) + 64 ("sender", "amount0Delta", "amount1Delta" offsets) + 64 ("length offset" + "length" offsets)
            if (dataWithSelector.length != (paddedDataLen + 132)) {
                return (false, 0, 0, emptyBytesCalldata());
            }
        }

        return (true, amount0Delta, amount1Delta, data);
    }

    function emptyBytesCalldata() private pure returns (bytes calldata) {
        bytes calldata empty;
        assembly {
            empty.length := 0
            empty.offset := 0
        }
        return empty;
    }
}

File 30 of 36 : VelocoreV2.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity =0.8.7;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "../interfaces/IWowmaxRouter.sol";
import "../interfaces/IWETH.sol";

/**
 * @title Velocore vault interface
 */
interface IVelocoreVault {
    /**
     * @dev Swaps tokens on a Velocore based protocol
     * @param pool Address of a Velocore pool contract
     * @param method Swap method
     * @param t1 Address of a token to swap from
     * @param m1 Swap method for the first token
     * @param a1 Amount of the first token to swap
     * @param t2 Address of a token to swap to
     * @param m2 Swap method for the second token
     * @param a2 Amount of the second token to swap
     * @param data Additional data to be used in swap
     * @return amounts Amounts of tokens received
     */
    function execute2(
        address pool,
        uint8 method,
        address t1,
        uint8 m1,
        int128 a1,
        address t2,
        uint8 m2,
        int128 a2,
        bytes memory data
    ) external payable returns (int128[] memory);

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

/**
 * @title Velocore library
 * @notice Functions to swap tokens on Velocore protocol
 */
library VelocoreV2 {
    uint8 constant SWAP = 0;
    uint8 constant EXACTLY = 0;
    uint8 constant AT_MOST = 1;
    address constant NATIVE_TOKEN = address(0x0);

    using SafeERC20 for IERC20;

    /**
     * @dev Swaps tokens on a Velocore pool contract
     * @param wrappedNativeToken Address of a wrapped native token
     * @param from Address of a token to swap from
     * @param amountIn Amount of tokens to swap
     * @param swapData Swap operation details. The `data` field should contain vault address
     * @return amountOut Amount of tokens received
     */
    function swap(
        address wrappedNativeToken,
        address from,
        uint256 amountIn,
        IWowmaxRouter.Swap memory swapData
    ) internal returns (uint256 amountOut) {
        address vaultAddress = abi.decode(swapData.data, (address));
        IVelocoreVault vault = IVelocoreVault(vaultAddress);

        address fromToken = from == wrappedNativeToken ? NATIVE_TOKEN : from;
        address toToken = swapData.to == wrappedNativeToken ? NATIVE_TOKEN : swapData.to;
        int128[] memory result;

        if (fromToken == NATIVE_TOKEN) {
            IWETH(wrappedNativeToken).withdraw(amountIn);
            result = vault.execute2{ value: amountIn }(
                swapData.addr,
                SWAP,
                fromToken,
                EXACTLY,
                int128(int256(amountIn)),
                toToken,
                AT_MOST,
                0,
                ""
            );
        } else {
            IERC20(fromToken).safeIncreaseAllowance(address(vault), amountIn);
            result = vault.execute2(
                swapData.addr,
                SWAP,
                fromToken,
                EXACTLY,
                int128(int256(amountIn)),
                toToken,
                AT_MOST,
                0,
                ""
            );
        }

        amountOut = uint256(int256(result[1]));

        if (toToken == NATIVE_TOKEN) {
            IWETH(wrappedNativeToken).deposit{ value: amountOut }();
        }
    }
}

File 31 of 36 : Velodrome.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity =0.8.7;

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

/**
 * @title Velodrome pair interface
 */
interface IVelodromePair {
    /**
     * @dev Returns the Velodrome based pair amount out
     * @param amountIn Amount to swap
     * @param from Address of the token to swap from
     * @return Amount of tokens to receive
     */
    function getAmountOut(uint256 amountIn, address from) external view returns (uint256);

    /**
     * @dev Swaps tokens on a Velodrome based pair
     * @param amount0Out Amount of the first token to receive
     * @param amount1Out Amount of the second token to receive
     * @param to Address to send tokens to
     * @param data Data to be send in callback function, if any
     */
    function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;
}

/**
 * @title Velodrome library
 * @notice Functions to swap tokens on Velodrome and compatible protocols
 */
library Velodrome {
    using SafeERC20 for IERC20;

    /**
     * @dev Performs a swap through a Velodrome based pair
     * @param from Address of a token to swap from
     * @param amountIn Amount of tokens to swap
     * @param swapData Swap operation details. The `data` field should contain from token index
     * @return amountOut Amount of tokens received
     */
    function swap(
        address from,
        uint256 amountIn,
        IWowmaxRouter.Swap memory swapData
    ) internal returns (uint256 amountOut) {
        uint256 fromIndex = abi.decode(swapData.data, (uint256));
        amountOut = IVelodromePair(swapData.addr).getAmountOut(amountIn, from);
        if (amountOut > 0) {
            IERC20(from).safeTransfer(swapData.addr, amountIn);
            IVelodromePair(swapData.addr).swap(
                fromIndex == 0 ? 0 : amountOut,
                fromIndex == 0 ? amountOut : 0,
                address(this),
                new bytes(0)
            );
        }
    }
}

File 32 of 36 : Vooi.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity =0.8.7;

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

/**
 * @title Vooi pool interface
 */
interface IVooiPool {
    /**
     * @notice Swap fromToken for toToken, ensures deadline and minimumToAmount and sends quoted amount to `to` address
     * @dev This function assumes tax free token.
     * @param _fromID The token being inserted into Pool by user for swap
     * @param _toID The token wanted by user, leaving the Pool
     * @param _fromAmount The amount of from token inserted
     * @param _minToAmount The minimum amount that will be accepted by user as result
     * @param _to The user receiving the result of swap
     * @param _deadline The deadline to be respected
     */
    function swap(
        uint256 _fromID,
        uint256 _toID,
        uint256 _fromAmount,
        uint256 _minToAmount,
        address _to,
        uint256 _deadline
    ) external returns (uint256 actualToAmount, uint256 lpFeeAmount);
}

/**
 * @title Vooi library
 * @notice Functions to swap tokens on Vooi protocol
 */
library Vooi {
    using SafeERC20 for IERC20;

    /**
     * @dev Swaps tokens on a Vooi pool contract
     * @param from Address of a token to swap from
     * @param amountIn Amount of tokens to swap
     * @param swapData Swap operation details. The `data` field should be empty
     * @return amountOut Amount of tokens received
     */
    function swap(
        address from,
        uint256 amountIn,
        IWowmaxRouter.Swap memory swapData
    ) internal returns (uint256 amountOut) {
        (uint8 fromID, uint8 toID) = abi.decode(swapData.data, (uint8, uint8));
        IERC20(from).safeIncreaseAllowance(swapData.addr, amountIn);
        (amountOut, ) = IVooiPool(swapData.addr).swap(fromID, toID, amountIn, 0, address(this), type(uint256).max);
    }
}

File 33 of 36 : Wombat.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity =0.8.7;

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

/**
 * @title Wombat pool interface
 */
interface IWombatPool {
    /**
     * @dev Swaps tokens on a Wombat based protocol contract
     * @param fromToken Address of a token to swap from
     * @param toToken Address of a token to swap to
     * @param fromAmount Amount of tokens to swap
     * @param minimumToAmount Minimal amount of tokens to receive
     * @param to Address to send swapped tokens to
     * @param deadline Timestamp after which the transaction will revert
     * @return actualToAmount Actual amount of tokens received
     * @return haircut Amount of tokens taken as fee
     */
    function swap(
        address fromToken,
        address toToken,
        uint256 fromAmount,
        uint256 minimumToAmount,
        address to,
        uint256 deadline
    ) external returns (uint256 actualToAmount, uint256 haircut);
}

/**
 * @title Wombat library
 * @notice Functions to swap tokens on Wombat protocol
 */
library Wombat {
    using SafeERC20 for IERC20;

    /**
     * @dev Swaps tokens on a Wombat pool contract
     * @param from Address of a token to swap from
     * @param amountIn Amount of tokens to swap
     * @param swapData Swap operation details. The `data` field should be empty
     * @return amountOut Amount of tokens received
     */
    function swap(
        address from,
        uint256 amountIn,
        IWowmaxRouter.Swap memory swapData
    ) internal returns (uint256 amountOut) {
        IERC20(from).safeIncreaseAllowance(swapData.addr, amountIn);
        (amountOut, ) = IWombatPool(swapData.addr).swap(
            from,
            swapData.to,
            amountIn,
            0,
            address(this),
            type(uint256).max
        );
    }
}

File 34 of 36 : WooFi.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity =0.8.7;

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

/**
 * @title WooFi pool interface
 */
interface IWooFiPool {
    /**
     * @dev Swaps tokens on a WooFi based protocol contract
     * @param fromToken Address of a token to swap from
     * @param toToken Address of a token to swap to
     * @param fromAmount Amount of tokens to swap
     * @param minToAmount Minimal amount of tokens to receive
     * @param to Address to send swapped tokens to
     * @param rebateTo The rebate address (optional, can be address ZERO)
     * @return realToAmount Amount of tokens received
     */
    function swap(
        address fromToken,
        address toToken,
        uint256 fromAmount,
        uint256 minToAmount,
        address to,
        address rebateTo
    ) external returns (uint256 realToAmount);
}

/**
 * @title WooFi library
 * @notice Functions to swap tokens on WooFi protocol
 */
library WooFi {
    using SafeERC20 for IERC20;

    /**
     * @dev Swaps tokens on a WooFi pool contract
     * @param from Address of a token to swap from
     * @param amountIn Amount of tokens to swap
     * @param swapData Swap operation details. The `data` field should be empty
     * @return amountOut Amount of tokens received
     */
    function swap(
        address from,
        uint256 amountIn,
        IWowmaxRouter.Swap memory swapData
    ) internal returns (uint256 amountOut) {
        IERC20(from).safeTransfer(swapData.addr, amountIn);
        amountOut = IWooFiPool(swapData.addr).swap(from, swapData.to, amountIn, 0, address(this), address(0x0));
    }
}

File 35 of 36 : WrappedNative.sol
// SPDX-License-Identifier: BUSL-1.1

pragma solidity =0.8.7;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "../interfaces/IWowmaxRouter.sol";
import "../interfaces/IWETH.sol";

/**
 * @title WETH library
 * @notice Functions to swap tokens on WETH contract
 */
library WrappedNative {
    using SafeERC20 for IERC20;

    /**
     * @dev Swaps tokens on a WETH contract
     * @param from Address of a token to swap from
     * @param amountIn Amount of tokens to swap
     * @param swapData Swap operation details. The `data` field should be empty
     * @return amountOut Amount of tokens received
     */
    function swap(
        address from,
        uint256 amountIn,
        IWowmaxRouter.Swap memory swapData
    ) internal returns (uint256 amountOut) {
        if (from == swapData.addr) {
            IWETH(swapData.addr).withdraw(amountIn);
        } else {
            IWETH(swapData.addr).deposit{ value: amountIn }();
        }
        amountOut = amountIn;
    }
}

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

pragma solidity =0.8.7;

/**
 * @dev Contract module that helps prevent reentrant swaps. Based on OpenZeppelin Contracts (last updated v4.8.0)
 * (security/ReentrancyGuard.sol)
 */
abstract contract WowmaxSwapReentrancyGuard {
    uint256 private constant _SWAP_IN_PROGRESS = 1;
    uint256 private constant _SWAP_NOT_IN_PROGRESS = 2;

    uint256 private _swapStatus;

    /**
     * @dev Prevents a contract from calling swap function again from within swap
     */
    modifier reentrancyProtectedSwap() {
        _beforeSwap();
        _;
        _afterSwap();
    }

    /**
     * @dev Prevents operation from being called outside of swap
     */
    modifier onlyDuringSwap() {
        require(_swapStatus == _SWAP_IN_PROGRESS, "WOWMAX: not allowed outside of swap");
        _;
    }

    constructor() {
        _swapStatus = _SWAP_NOT_IN_PROGRESS;
    }

    /**
     * @dev checks if swap is in progress and prevents reentrant calls
     */
    function _beforeSwap() private {
        require(_swapStatus != _SWAP_IN_PROGRESS, "WOWMAX: reentrant swap not allowed");
        _swapStatus = _SWAP_IN_PROGRESS;
    }

    /**
     * @dev sets swap status to not in progress
     */
    function _afterSwap() private {
        _swapStatus = _SWAP_NOT_IN_PROGRESS;
    }
}

Settings
{
  "metadata": {
    "bytecodeHash": "none"
  },
  "optimizer": {
    "enabled": true,
    "runs": 800
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"_weth","type":"address"},{"internalType":"address","name":"_treasury","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountIn","type":"uint256"},{"indexed":false,"internalType":"address[]","name":"to","type":"address[]"},{"indexed":false,"internalType":"uint256[]","name":"amountOut","type":"uint256[]"}],"name":"SwapExecuted","type":"event"},{"stateMutability":"nonpayable","type":"fallback"},{"inputs":[],"name":"WETH","outputs":[{"internalType":"contract IWETH","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"int256","name":"amount0Delta","type":"int256"},{"internalType":"int256","name":"amount1Delta","type":"int256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"algebraSwapCallback","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"d3MMSwapCallBack","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"maverickV2SwapCallback","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"maxFeePercentage","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxSlippage","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_maxFeePercentage","type":"uint256"}],"name":"setMaxFeePercentage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_maxSlippage","type":"uint256"}],"name":"setMaxSlippage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"address[]","name":"to","type":"address[]"},{"components":[{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256","name":"parts","type":"uint256"},{"components":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"part","type":"uint256"},{"internalType":"address","name":"addr","type":"address"},{"internalType":"bytes32","name":"family","type":"bytes32"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct IWowmaxRouterBase.Swap[]","name":"swaps","type":"tuple[]"}],"internalType":"struct IWowmaxRouterBase.ExchangeRoute[]","name":"exchangeRoutes","type":"tuple[]"},{"internalType":"uint256[]","name":"slippage","type":"uint256[]"},{"internalType":"uint256[]","name":"amountOutExpected","type":"uint256[]"}],"internalType":"struct IWowmaxRouterBase.ExchangeRequest","name":"request","type":"tuple"}],"name":"swap","outputs":[{"internalType":"uint256[]","name":"amountsOut","type":"uint256[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountToPay","type":"uint256"},{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"swapCallback","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"x","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"swapX2YCallback","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"y","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"swapY2XCallback","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"treasury","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawETH","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]

608060405260646004556107d06005553480156200001c57600080fd5b506040516200558f3803806200558f8339810160408190526200003f91620001a2565b6200004a3362000135565b60026001556001600160a01b038216620000ab5760405162461bcd60e51b815260206004820152601a60248201527f574f574d41583a2057726f6e672057455448206164647265737300000000000060448201526064015b60405180910390fd5b6001600160a01b038116620001035760405162461bcd60e51b815260206004820152601e60248201527f574f574d41583a2057726f6e67207472656173757279206164647265737300006044820152606401620000a2565b600280546001600160a01b039384166001600160a01b03199182161790915560038054929093169116179055620001da565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b80516001600160a01b03811681146200019d57600080fd5b919050565b60008060408385031215620001b657600080fd5b620001c18362000185565b9150620001d16020840162000185565b90509250929050565b6153a580620001ea6000396000f3fe60806040526004361061012c5760003560e01c80638da5cb5b116100a5578063bf654aac11610074578063f14210a611610059578063f14210a61461040f578063f2fde38b1461042f578063f3fef3a31461044f57610133565b8063bf654aac146103d9578063d3e1c284146103ef57610133565b80638da5cb5b1461035b578063923b8a2a14610379578063a66cd09414610399578063ad5c4648146103b957610133565b806343f68a49116100fc57806367ca7c91116100e157806367ca7c9114610302578063715018a6146103225780638c04166f1461033757610133565b806343f68a49146102aa57806361d027b3146102ca57610133565b8062d5a9e81461021f57806318780684146102485780632c8958f61461026a5780633e88c8ab1461028a57610133565b3661013357005b34801561013f57600080fd5b5060018054146101a25760405162461bcd60e51b815260206004820152602360248201527f574f574d41583a206e6f7420616c6c6f776564206f757473696465206f66207360448201526207761760ec1b60648201526084015b60405180910390fd5b60008060003660006101b560003661046f565b945094509450945094508461020c5760405162461bcd60e51b815260206004820152601c60248201527f574f574d41583a20756e737570706f727465642063616c6c6261636b000000006044820152606401610199565b610218848484846104e4565b5050505050005b61023261022d3660046148f3565b610634565b60405161023f9190614d4c565b60405180910390f35b34801561025457600080fd5b506102686102633660046148b8565b610658565b005b34801561027657600080fd5b506102686102853660046148b8565b6106c0565b34801561029657600080fd5b506102686102a53660046145ca565b610729565b3480156102b657600080fd5b506102686102c5366004614aee565b610735565b3480156102d657600080fd5b506003546102ea906001600160a01b031681565b6040516001600160a01b03909116815260200161023f565b34801561030e57600080fd5b5061026861031d366004614626565b610742565b34801561032e57600080fd5b506102686107b0565b34801561034357600080fd5b5061034d60055481565b60405190815260200161023f565b34801561036757600080fd5b506000546001600160a01b03166102ea565b34801561038557600080fd5b506102686103943660046148b8565b6107c4565b3480156103a557600080fd5b506102686103b4366004614aee565b61082d565b3480156103c557600080fd5b506002546102ea906001600160a01b031681565b3480156103e557600080fd5b5061034d60045481565b3480156103fb57600080fd5b5061026861040a3660046148b8565b61083a565b34801561041b57600080fd5b5061026861042a366004614aee565b6108a2565b34801561043b57600080fd5b5061026861044a366004614564565b61095d565b34801561045b57600080fd5b5061026861046a36600461459e565b6109ed565b600080803681600487810135906024808a01359160448b01358b0191820191013580601f811680156104a45780602003820191505b608482018c146104c9576000808080809a509a509a509a509a505050505050506104da565b506001995093975091955093509150505b9295509295909350565b6000841380156104f45750600083125b156105845761057f3385336001600160a01b0316630dfe16816040518163ffffffff1660e01b815260040160206040518083038186803b15801561053757600080fd5b505afa15801561054b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061056f9190614581565b6001600160a01b03169190610a0b565b61062e565b6000841280156105945750600083135b156105d75761057f3384336001600160a01b031663d21220a76040518163ffffffff1660e01b815260040160206040518083038186803b15801561053757600080fd5b60405162461bcd60e51b815260206004820152602660248201527f574f574d41583a20556e697377617020763320696e76617269616e742076696f6044820152653630ba34b7b760d11b6064820152608401610199565b50505050565b606061063e610a88565b61064782610aeb565b90506106536002600155565b919050565b60018054146106b55760405162461bcd60e51b815260206004820152602360248201527f574f574d41583a206e6f7420616c6c6f776564206f757473696465206f66207360448201526207761760ec1b6064820152608401610199565b61062e848383610c06565b600180541461071d5760405162461bcd60e51b815260206004820152602360248201527f574f574d41583a206e6f7420616c6c6f776564206f757473696465206f66207360448201526207761760ec1b6064820152608401610199565b61062e84848484610c2a565b61062e84848484610d27565b61073d610d3b565b600555565b600180541461079f5760405162461bcd60e51b815260206004820152602360248201527f574f574d41583a206e6f7420616c6c6f776564206f757473696465206f66207360448201526207761760ec1b6064820152608401610199565b6107a98585610d95565b5050505050565b6107b8610d3b565b6107c26000610da9565b565b60018054146108215760405162461bcd60e51b815260206004820152602360248201527f574f574d41583a206e6f7420616c6c6f776564206f757473696465206f66207360448201526207761760ec1b6064820152608401610199565b61062e84848484610e11565b610835610d3b565b600455565b60018054146108975760405162461bcd60e51b815260206004820152602360248201527f574f574d41583a206e6f7420616c6c6f776564206f757473696465206f66207360448201526207761760ec1b6064820152608401610199565b61062e838383610c06565b6108aa610d3b565b6003546040516000916001600160a01b03169083908381818185875af1925050503d80600081146108f7576040519150601f19603f3d011682016040523d82523d6000602084013e6108fc565b606091505b50509050806109595760405162461bcd60e51b8152602060048201526024808201527f576f776d61783a204661696c656420746f2073656e64206e617469766520746f6044820152636b656e7360e01b6064820152608401610199565b5050565b610965610d3b565b6001600160a01b0381166109e15760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f64647265737300000000000000000000000000000000000000000000000000006064820152608401610199565b6109ea81610da9565b50565b6109f5610d3b565b600354610959906001600160a01b038481169116835b6040516001600160a01b038316602482015260448101829052610a8390849063a9059cbb60e01b906064015b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff166001600160e01b031990931692909217909152610ea8565b505050565b600180541415610ae55760405162461bcd60e51b815260206004820152602260248201527f574f574d41583a207265656e7472616e742073776170206e6f7420616c6c6f77604482015261195960f21b6064820152608401610199565b60018055565b6060610af682610f8d565b6000610b01836112c1565b905060005b610b13606085018561508b565b9050811015610b6657610b53610b2c606086018661508b565b83818110610b3c57610b3c615349565b9050602002810190610b4e91906150d5565b6113a0565b5080610b5e816152e5565b915050610b06565b50610b70836114fb565b91506000610b816020850185614564565b6001600160a01b031614610ba157610b9c6020840184614564565b610bae565b6002546001600160a01b03165b6001600160a01b0316337f25a3ab87780ac0e2eeeb645d408cbc89d66c72590ae0e1f81bda8f3d3a8d121683610be7604088018861508b565b87604051610bf89493929190614f80565b60405180910390a350919050565b6000610c1482840184614564565b905061062e6001600160a01b0382163386610a0b565b600084138015610c3a5750600083125b15610c7d5761057f3385336001600160a01b0316630dfe16816040518163ffffffff1660e01b815260040160206040518083038186803b15801561053757600080fd5b600084128015610c8d5750600083135b15610cd05761057f3384336001600160a01b031663d21220a76040518163ffffffff1660e01b815260040160206040518083038186803b15801561053757600080fd5b60405162461bcd60e51b815260206004820152602660248201527f574f574d41583a20416c676562726120563120696e76617269616e742076696f6044820152653630ba34b7b760d11b6064820152608401610199565b61062e6001600160a01b0385163385610a0b565b6000546001600160a01b031633146107c25760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610199565b6109596001600160a01b0383163383610a0b565b600080546001600160a01b038381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6000610e1f828401846147bb565b90508015610e6a57610e653386336001600160a01b0316630fc63d106040518163ffffffff1660e01b815260040160206040518083038186803b15801561053757600080fd5b6107a9565b6107a93386336001600160a01b0316635f64b55b6040518163ffffffff1660e01b815260040160206040518083038186803b15801561053757600080fd5b6000610efd826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166118ca9092919063ffffffff16565b805190915015610a835780806020019051810190610f1b91906147d8565b610a835760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401610199565b6000610f9c604083018361508b565b905011610ff65760405162461bcd60e51b815260206004820152602260248201527f574f574d41583a204e6f206f757470757420746f6b656e732073706563696669604482015261195960f21b6064820152608401610199565b61100360a082018261508b565b9050611012604083018361508b565b9050146110875760405162461bcd60e51b815260206004820152602660248201527f574f574d41583a2057726f6e6720616d6f756e744f757445787065637465642060448201527f6c656e67746800000000000000000000000000000000000000000000000000006064820152608401610199565b611094608082018261508b565b90506110a3604083018361508b565b9050146110f25760405162461bcd60e51b815260206004820152601d60248201527f574f574d41583a2057726f6e6720736c697070616765206c656e6774680000006044820152606401610199565b60005b611102604083018361508b565b905081101561095957600061111a604084018461508b565b8381811061112a5761112a615349565b905060200201602081019061113f9190614564565b6001600160a01b031614156111a15760405162461bcd60e51b815260206004820152602260248201527f574f574d41583a2057726f6e67206f757470757420746f6b656e206164647265604482015261737360f01b6064820152608401610199565b60006111b060a084018461508b565b838181106111c0576111c0615349565b905060200201351161123a5760405162461bcd60e51b815260206004820152602560248201527f574f574d41583a2057726f6e6720616d6f756e744f757445787065637465642060448201527f76616c75650000000000000000000000000000000000000000000000000000006064820152608401610199565b60055461124a608084018461508b565b8381811061125a5761125a615349565b9050602002013511156112af5760405162461bcd60e51b815260206004820152601c60248201527f574f574d41583a20536c69707061676520697320746f6f2068696768000000006044820152606401610199565b806112b9816152e5565b9150506110f5565b6000806000341180156112e9575060006112de6020850185614564565b6001600160a01b0316145b80156112f757506020830135155b15611363575060025460408051630d0e30db60e41b8152905134926001600160a01b03169163d0e30db091849160048082019260009290919082900301818588803b15801561134557600080fd5b505af1158015611359573d6000803e3d6000fd5b505050505061139a565b60208301351561139a57506020820180359061139a903390309084906113899088614564565b6001600160a01b03169291906118e3565b92915050565b6000806113b06020840184614564565b6040516370a0823160e01b81523060048201526001600160a01b0391909116906370a082319060240160206040518083038186803b1580156113f157600080fd5b505afa158015611405573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061142991906147f5565b90506000805b61143c604086018661508b565b90508110156114f3576114d56114556020870187614564565b6020870135611467604089018961508b565b8581811061147757611477615349565b905060200281019061148991906150eb565b6114979060200135876151e2565b6114a191906151c0565b6114ae604089018961508b565b858181106114be576114be615349565b90506020028101906114d091906150eb565b61191b565b6114df90836151a8565b9150806114eb816152e5565b91505061142f565b509392505050565b606061150a604083018361508b565b905067ffffffffffffffff8111156115245761152461535f565b60405190808252806020026020018201604052801561154d578160200160208202803683370190505b50905060008060005b611563604086018661508b565b90508110156118c257611579604086018661508b565b8281811061158957611589615349565b905060200201602081019061159e9190614564565b6040516370a0823160e01b81523060048201529092506001600160a01b038316906370a082319060240160206040518083038186803b1580156115e057600080fd5b505afa1580156115f4573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061161891906147f5565b9250600061162960a087018761508b565b8381811061163957611639615349565b905060200201358411156117185761165460a087018761508b565b8381811061166457611664615349565b90506020020135846116769190615201565b905060006127106004548661168b91906151e2565b61169591906151c0565b9050808211156116cf579050806116ac8186615201565b8684815181106116be576116be615349565b602002602001018181525050611712565b6116dc60a088018861508b565b848181106116ec576116ec615349565b9050602002013586848151811061170557611705615349565b6020026020010181815250505b506117fe565b612710611728608088018861508b565b8481811061173857611738615349565b9050602002013561271061174c9190615201565b61175960a089018961508b565b8581811061176957611769615349565b9050602002013561177a91906151e2565b61178491906151c0565b8410156117de5760405162461bcd60e51b815260206004820152602260248201527f574f574d41583a20496e73756666696369656e74206f757470757420616d6f756044820152611b9d60f21b6064820152608401610199565b838583815181106117f1576117f1615349565b6020026020010181815250505b6002546001600160a01b038481169116141561187357600254604051632e1a7d4d60e01b8152600481018690526001600160a01b0390911690632e1a7d4d90602401600060405180830381600087803b15801561185a57600080fd5b505af115801561186e573d6000803e3d6000fd5b505050505b60035461188b9084906001600160a01b031683611ffd565b6118af83338785815181106118a2576118a2615349565b6020026020010151611ffd565b50806118ba816152e5565b915050611556565b505050919050565b60606118d984846000856120e0565b90505b9392505050565b6040516001600160a01b038085166024830152831660448201526064810182905261062e9085906323b872dd60e01b90608401610a37565b60007f554e49535741505f563300000000000000000000000000000000000000000000826060013514156119625761195b8361195684615218565b6121d4565b90506118dc565b7f48415348464c4f57000000000000000000000000000000000000000000000000826060013514156119a15761195b848461199c85615218565b6122e1565b7f574f4d4241540000000000000000000000000000000000000000000000000000826060013514156119e05761195b84846119db85615218565b61249d565b7f4c4556454c00000000000000000000000000000000000000000000000000000082606001351415611a1f5761195b8484611a1a85615218565b612568565b7f444f444f5f56320000000000000000000000000000000000000000000000000082606001351415611a5e5761195b8484611a5985615218565b61270c565b7f444f444f5f56330000000000000000000000000000000000000000000000000082606001351415611a9d5761195b8484611a9885615218565b612852565b7f574f4f464900000000000000000000000000000000000000000000000000000082606001351415611adc5761195b8484611ad785615218565b612903565b7f554e49535741505f56320000000000000000000000000000000000000000000082606001351415611b1b5761195b8484611b1685615218565b612978565b7f435552564500000000000000000000000000000000000000000000000000000082606001351415611b5a5761195b8484611b5585615218565b612b07565b7f50414e43414b45535741505f535441424c45000000000000000000000000000082606001351415611b995761195b8484611b9485615218565b612cc4565b7f444f444f5f56310000000000000000000000000000000000000000000000000082606001351415611bd85761195b8484611bd385615218565b612ddb565b7f42414c414e4345525f563200000000000000000000000000000000000000000082606001351415611c175761195b8484611c1285615218565b612e40565b7f4d4156455249434b5f563100000000000000000000000000000000000000000082606001351415611c555761195b83611c5084615218565b612fa7565b7f534144444c45000000000000000000000000000000000000000000000000000082606001351415611c945761195b8484611c8f85615218565b613054565b7f46554c43524f4d0000000000000000000000000000000000000000000000000082606001351415611cd35761195b8484611cce85615218565b613164565b7f554e49535741505f56325f524f5554455200000000000000000000000000000082606001351415611d125761195b8484611d0d85615218565b6131c4565b7f454c41535449430000000000000000000000000000000000000000000000000082606001351415611d515761195b8484611d4c85615218565b613382565b7f414c47454252415f56310000000000000000000000000000000000000000000082606001351415611d905761195b8484611d8b85615218565b6134f5565b7f414c47454252415f56315f39000000000000000000000000000000000000000082606001351415611dca5761195b8484611d8b85615218565b7f53594e435357415000000000000000000000000000000000000000000000000082606001351415611e095761195b8484611e0485615218565b6135f7565b63564f4f4960e01b82606001351415611e2f5761195b8484611e2a85615218565b6137e0565b7f56454c4f434f52455f563200000000000000000000000000000000000000000082606001351415611e7b5760025461195b906001600160a01b03168585611e7686615218565b6138cd565b7f495a49535741500000000000000000000000000000000000000000000000000082606001351415611eba5761195b8484611eb585615218565b613bf7565b7f56454c4f44524f4d45000000000000000000000000000000000000000000000082606001351415611ef95761195b8484611ef485615218565b613d2a565b7f575241505045445f4e415449564500000000000000000000000000000000000082606001351415611f385761195b8484611f3385615218565b613e8d565b7f4c49515549444954595f424f4f4b5f56325f310000000000000000000000000082606001351415611f775761195b8484611f7285615218565b613f77565b7f4d4156455249434b5f563200000000000000000000000000000000000000000082606001351415611fb55761195b83611fb084615218565b6140eb565b60405162461bcd60e51b815260206004820152601a60248201527f574f574d41583a20556e6b6e6f776e204445582066616d696c790000000000006044820152606401610199565b8061200757505050565b6002546001600160a01b03848116911614156120cc576000826001600160a01b03168260405160006040518083038185875af1925050503d806000811461206a576040519150601f19603f3d011682016040523d82523d6000602084013e61206f565b606091505b505090508061062e5760405162461bcd60e51b8152602060048201526024808201527f576f776d61783a204661696c656420746f2073656e64206e617469766520746f6044820152636b656e7360e01b6064820152608401610199565b610a836001600160a01b0384168383610a0b565b6060824710156121585760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c00000000000000000000000000000000000000000000000000006064820152608401610199565b600080866001600160a01b031685876040516121749190614bb3565b60006040518083038185875af1925050503d80600081146121b1576040519150601f19603f3d011682016040523d82523d6000602084013e6121b6565b606091505b50915091506121c7878383876141d3565b925050505b949350505050565b60008082608001518060200190518101906121ef91906147d8565b90506000816122125773fffd8963efd1fc6a506488495d951d5263988d25612219565b6401000276a45b604085810151815160008082526020820193849052630251596160e31b909352929350909182916001600160a01b03169063128acb089061226590309088908c90899060248101614c09565b6040805180830381600087803b15801561227e57600080fd5b505af1158015612292573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122b69190614894565b91509150836122cd576122c882615300565b6122d6565b6122d681615300565b979650505050505050565b60008082608001518060200190518101906122fc919061492e565b6040840151909150612319906001600160a01b0387169086614249565b8060e0015184101561232d5760c081018490525b82516040516370a0823160e01b81523060048201526000916001600160a01b0316906370a082319060240160206040518083038186803b15801561237057600080fd5b505afa158015612384573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123a891906147f5565b905083604001516001600160a01b031663c52ac720836040518263ffffffff1660e01b81526004016123da9190614dae565b600060405180830381600087803b1580156123f457600080fd5b505af1158015612408573d6000803e3d6000fd5b505085516040516370a0823160e01b81523060048201528493506001600160a01b0390911691506370a08231906024015b60206040518083038186803b15801561245157600080fd5b505afa158015612465573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061248991906147f5565b6124939190615201565b9695505050505050565b60408101516000906124ba906001600160a01b0386169085614249565b60408281015183519151639908fc8b60e01b81526001600160a01b0387811660048301529283166024820152604481018690526000606482015230608482015260001960a4820152911690639908fc8b9060c4016040805180830381600087803b15801561252757600080fd5b505af115801561253b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061255f9190614894565b50949350505050565b6040810151600090612585906001600160a01b0386169085610a0b565b81516040516370a0823160e01b81523060048201526000916001600160a01b0316906370a082319060240160206040518083038186803b1580156125c857600080fd5b505afa1580156125dc573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061260091906147f5565b6040848101518551825160008082526020820194859052631fa196a960e21b9094529394506001600160a01b0390911692637e865aa49261264b928a92909190309060248101614bcf565b600060405180830381600087803b15801561266557600080fd5b505af1158015612679573d6000803e3d6000fd5b505084516040516370a0823160e01b81523060048201528493506001600160a01b0390911691506370a082319060240160206040518083038186803b1580156126c157600080fd5b505afa1580156126d5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126f991906147f5565b6127039190615201565b95945050505050565b6040810151600090612729906001600160a01b0386169085610a0b565b600082608001518060200190518101906127439190614b07565b905060ff81166127d3576040808401519051632f58056d60e21b81523060048201526001600160a01b039091169063bd6015b490602401602060405180830381600087803b15801561279457600080fd5b505af11580156127a8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127cc91906147f5565b91506114f3565b6040808401519051636ec9facd60e11b81523060048201526001600160a01b039091169063dd93f59a90602401602060405180830381600087803b15801561281a57600080fd5b505af115801561282e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061270391906147f5565b60408082015182519151637597a8d360e11b81523060048201526001600160a01b03868116602483015292831660448201526064810185905260006084820181905260c060a483015260c48201819052929091169063eb2f51a69060e4015b602060405180830381600087803b1580156128cb57600080fd5b505af11580156128df573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118d991906147f5565b6040810151600090612920906001600160a01b0386169085610a0b565b60408281015183519151633ee101c160e11b81526001600160a01b03878116600483015292831660248201526044810186905260006064820181905230608483015260a4820152911690637dc203829060c4016128b1565b6040810151600090612995906001600160a01b0386169085610a0b565b600082608001518060200190518101906129af91906147f5565b90506000856001600160a01b031684604001516001600160a01b0316630dfe16816040518163ffffffff1660e01b815260040160206040518083038186803b1580156129fa57600080fd5b505afa158015612a0e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a329190614581565b6001600160a01b0316149050600080612a4f86604001518461430a565b91509150612a5f878383876143a3565b94508415612afc5785604001516001600160a01b031663022c0d9f84612a855786612a88565b60005b85612a94576000612a96565b875b604080516000815260208101918290526001600160e01b031960e086901b16909152612ac992919030906024810161505d565b600060405180830381600087803b158015612ae357600080fd5b505af1158015612af7573d6000803e3d6000fd5b505050505b505050509392505050565b60008060008360800151806020019051810190612b24919061483e565b85516040516370a0823160e01b81523060048201529294509092506000916001600160a01b03909116906370a082319060240160206040518083038186803b158015612b6f57600080fd5b505afa158015612b83573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ba791906147f5565b6040860151909150612bc4906001600160a01b0389169088614249565b6040808601519051630f7c084960e21b8152600f85810b600483015284900b602482015260448101889052600060648201526001600160a01b0390911690633df02124906084015b600060405180830381600087803b158015612c2657600080fd5b505af1158015612c3a573d6000803e3d6000fd5b505086516040516370a0823160e01b81523060048201528493506001600160a01b0390911691506370a082319060240160206040518083038186803b158015612c8257600080fd5b505afa158015612c96573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612cba91906147f5565b6122d69190615201565b60008060008360800151806020019051810190612ce1919061483e565b85516040516370a0823160e01b81523060048201529294509092506000916001600160a01b03909116906370a082319060240160206040518083038186803b158015612d2c57600080fd5b505afa158015612d40573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d6491906147f5565b6040860151909150612d81906001600160a01b0389169088614249565b6040808601519051630b68372160e31b81526fffffffffffffffffffffffffffffffff80861660048301528416602482015260448101889052600060648201526001600160a01b0390911690635b41b90890608401612c0c565b6040810151600090612df8906001600160a01b0386169085614249565b6040808301519051638dae733360e01b8152600481018590526000602482018190526060604483015260648201526001600160a01b0390911690638dae7333906084016128b1565b60008060008360800151806020019051810190612e5d919061480e565b9150915060006040518060c0016040528084815260200160006001811115612e8757612e87615333565b81526001600160a01b03808a16602083015287511660408201526060810188905260800160006040519080825280601f01601f191660200182016040528015612ed7576020820181803683370190505b5090526040805160808101825230808252600060208301819052928201526060810191909152909150612f146001600160a01b0389168489614249565b6040516352bbbe2960e01b81526001600160a01b038416906352bbbe2990612f49908590859060009060001990600401614ea0565b602060405180830381600087803b158015612f6357600080fd5b505af1158015612f77573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f9b91906147f5565b98975050505050505050565b6000808260800151806020019051810190612fc291906147d8565b905082604001516001600160a01b031663c51c902930868460008089608001516040518763ffffffff1660e01b815260040161300396959493929190614d0e565b6040805180830381600087803b15801561301c57600080fd5b505af1158015613030573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127039190614894565b600080600083608001518060200190518101906130719190614b22565b85516040516370a0823160e01b81523060048201529294509092506000916001600160a01b03909116906370a082319060240160206040518083038186803b1580156130bc57600080fd5b505afa1580156130d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130f491906147f5565b6040860151909150613111906001600160a01b0389169088614249565b60408086015190516348b4aac360e11b815260ff808616600483015284166024820152604481018890526000606482015260001960848201526001600160a01b039091169063916955869060a401612c0c565b6040810151600090613181906001600160a01b0386169085610a0b565b60408281015183519151634998b10960e11b81526001600160a01b03878116600483015292831660248201523060448201529116906393316212906064016128b1565b60008082604001516001600160a01b031663f887ea406040518163ffffffff1660e01b8152600401602060405180830381600087803b15801561320657600080fd5b505af115801561321a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061323e9190614581565b90506132546001600160a01b0386168286614249565b604080516002808252606082018352600092602083019080368337019050509050858160008151811061328957613289615349565b60200260200101906001600160a01b031690816001600160a01b0316815250508360000151816001815181106132c1576132c1615349565b6001600160a01b0392831660209182029290920101526040516338ed173960e01b8152908316906338ed1739906133079088906000908690309060001990600401614fec565b600060405180830381600087803b15801561332157600080fd5b505af1158015613335573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261335d919081019061472f565b60018151811061336f5761336f615349565b6020026020010151925050509392505050565b60008082604001516001600160a01b0316630dfe16816040518163ffffffff1660e01b815260040160206040518083038186803b1580156133c257600080fd5b505afa1580156133d6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133fa9190614581565b6001600160a01b0316856001600160a01b03161490506000816134315773fffd8963efd1fc6a506488495d951d5263988d25613438565b6401000276a45b60408581015181516000808252602082019384905263092cc68360e21b909352929350909182916001600160a01b0316906324b31a0c906134849030908b908990899060248101614c43565b6040805180830381600087803b15801561349d57600080fd5b505af11580156134b1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134d59190614894565b91509150836134ec576134e782615300565b612f9b565b612f9b81615300565b60008082604001516001600160a01b0316630dfe16816040518163ffffffff1660e01b815260040160206040518083038186803b15801561353557600080fd5b505afa158015613549573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061356d9190614581565b6001600160a01b0316856001600160a01b03161490506000816135a45773fffd8963efd1fc6a506488495d951d5263988d256135ab565b6401000276a45b604085810151815160008082526020820193849052630251596160e31b909352929350909182916001600160a01b03169063128acb089061348490309088908c90899060248101614c09565b60008082604001516001600160a01b031663fbfa77cf6040518163ffffffff1660e01b815260040160206040518083038186803b15801561363757600080fd5b505afa15801561364b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061366f9190614581565b90506136856001600160a01b0386168286610a0b565b6040838101519051631f2c13e160e31b81526001600160a01b03878116600483015291821660248201529082169063f9609f0890604401602060405180830381600087803b1580156136d657600080fd5b505af11580156136ea573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061370e91906147f5565b50604080516001600160a01b03878116602083015230828401526002606080840191909152835180840390910181526080830180855287850151600080835260a0860196879052637132bb7f60e01b9096529194939190921691637132bb7f916137809186918691829160a401614d5f565b6040805180830381600087803b15801561379957600080fd5b505af11580156137ad573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137d19190614a46565b60200151979650505050505050565b600080600083608001518060200190518101906137fd9190614b22565b6040860151919350915061381c906001600160a01b0388169087614249565b60408085015190516347b5ef5560e11b815260ff808516600483015283166024820152604481018790526000606482015230608482015260001960a48201526001600160a01b0390911690638f6bdeaa9060c4015b6040805180830381600087803b15801561388a57600080fd5b505af115801561389e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138c29190614894565b509695505050505050565b60008082608001518060200190518101906138e89190614581565b90508060006001600160a01b03878116908916146139065786613909565b60005b90506000886001600160a01b031686600001516001600160a01b031614613931578551613934565b60005b905060606001600160a01b038316613a7657604051632e1a7d4d60e01b8152600481018990526001600160a01b038b1690632e1a7d4d90602401600060405180830381600087803b15801561398857600080fd5b505af115801561399c573d6000803e3d6000fd5b50505060408089015190516342f3185360e11b81526001600160a01b039182166004820152600060248201819052868316604483015260648201819052600f8c900b608483015285831660a4830152600160c483015260e4820181905261012061010483015261012482015290861691506385e630a6908a90610144016000604051808303818588803b158015613a3257600080fd5b505af1158015613a46573d6000803e3d6000fd5b50505050506040513d6000823e601f3d908101601f19168201604052613a6f9190810190614690565b9050613b5a565b613a8a6001600160a01b038416858a614249565b60408781015190516342f3185360e11b81526001600160a01b039182166004820152600060248201819052858316604483015260648201819052600f8b900b608483015284831660a4830152600160c483015260e48201819052610120610104830152610124820152908516906385e630a69061014401600060405180830381600087803b158015613b1b57600080fd5b505af1158015613b2f573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052613b579190810190614690565b90505b80600181518110613b6d57613b6d615349565b6020026020010151600f0b955060006001600160a01b0316826001600160a01b03161415613bea57896001600160a01b031663d0e30db0876040518263ffffffff1660e01b81526004016000604051808303818588803b158015613bd057600080fd5b505af1158015613be4573d6000803e3d6000fd5b50505050505b5050505050949350505050565b6000808260800151806020019051810190613c129190614871565b604080516001600160a01b038816602082015291925060009101604051602081830303815290604052905083600001516001600160a01b0316866001600160a01b03161015613ceb5783604001516001600160a01b031663857f812f308785856040518563ffffffff1660e01b8152600401613c919493929190614ccb565b6040805180830381600087803b158015613caa57600080fd5b505af1158015613cbe573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ce29190614894565b9350613d219050565b83604001516001600160a01b0316632c481252308785856040518563ffffffff1660e01b81526004016138719493929190614ccb565b50509392505050565b6000808260800151806020019051810190613d4591906147f5565b60408085015190516378a051ad60e11b8152600481018790526001600160a01b03888116602483015292935091169063f140a35a9060440160206040518083038186803b158015613d9557600080fd5b505afa158015613da9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613dcd91906147f5565b915081156114f3576040830151613def906001600160a01b0387169086610a0b565b60408301516001600160a01b031663022c0d9f8215613e0e5783613e11565b60005b8315613e1e576000613e20565b845b604080516000815260208101918290526001600160e01b031960e086901b16909152613e5392919030906024810161505d565b600060405180830381600087803b158015613e6d57600080fd5b505af1158015613e81573d6000803e3d6000fd5b50505050509392505050565b600081604001516001600160a01b0316846001600160a01b03161415613f165781604001516001600160a01b0316632e1a7d4d846040518263ffffffff1660e01b8152600401613edf91815260200190565b600060405180830381600087803b158015613ef957600080fd5b505af1158015613f0d573d6000803e3d6000fd5b50505050613f6f565b81604001516001600160a01b031663d0e30db0846040518263ffffffff1660e01b81526004016000604051808303818588803b158015613f5557600080fd5b505af1158015613f69573d6000803e3d6000fd5b50505050505b509092915050565b6000808260800151806020019051810190613f9291906147f5565b83516040516370a0823160e01b815230600482015260019290921492506000916001600160a01b03909116906370a082319060240160206040518083038186803b158015613fdf57600080fd5b505afa158015613ff3573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061401791906147f5565b6040850151909150614034906001600160a01b0388169087610a0b565b604080850151905163029e02cd60e51b815283151560048201523060248201526001600160a01b03909116906353c059a090604401602060405180830381600087803b15801561408357600080fd5b505af1158015614097573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906140bb91906147f5565b5083516040516370a0823160e01b815230600482015282916001600160a01b0316906370a0823190602401612439565b600080826080015180602001905181019061410691906147d8565b905060008161411a57637fffffff19614120565b637fffffff5b60408051608080820183528882528515156020830152600082840152600384900b606083015287830151908801519251633eece7db60e01b815293945090926001600160a01b0390911691633eece7db91614182913091869190600401614c7d565b6040805180830381600087803b15801561419b57600080fd5b505af11580156141af573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122d69190614894565b6060831561423f578251614238576001600160a01b0385163b6142385760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610199565b50816121cc565b6121cc8383614411565b604051636eb1769f60e11b81523060048201526001600160a01b038381166024830152600091839186169063dd62ed3e9060440160206040518083038186803b15801561429557600080fd5b505afa1580156142a9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906142cd91906147f5565b6142d791906151a8565b6040516001600160a01b03851660248201526044810182905290915061062e90859063095ea7b360e01b90606401610a37565b600080600080856001600160a01b0316630902f1ac6040518163ffffffff1660e01b815260040160606040518083038186803b15801561434957600080fd5b505afa15801561435d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906143819190614a9e565b509150915084614392578082614395565b81815b9350935050505b9250929050565b6000806143b283612710615201565b6143bc90876151e2565b905060006143da6dffffffffffffffffffffffffffff8616836151e2565b90506000826143fb6127106dffffffffffffffffffffffffffff8a166151e2565b61440591906151a8565b9050612f9b81836151c0565b8151156144215781518083602001fd5b8060405162461bcd60e51b81526004016101999190614d9b565b805161065381615375565b60008083601f84011261445857600080fd5b50813567ffffffffffffffff81111561447057600080fd5b60208301915083602082850101111561439c57600080fd5b600082601f83011261449957600080fd5b81356144ac6144a782615180565b61512b565b8181528460208386010111156144c157600080fd5b816020850160208301376000918101602001919091529392505050565b600082601f8301126144ef57600080fd5b81516144fd6144a782615180565b81815284602083860101111561451257600080fd5b6121cc8260208301602087016152b9565b8051600f81900b811461065357600080fd5b80516dffffffffffffffffffffffffffff8116811461065357600080fd5b805160ff8116811461065357600080fd5b60006020828403121561457657600080fd5b81356118dc81615375565b60006020828403121561459357600080fd5b81516118dc81615375565b600080604083850312156145b157600080fd5b82356145bc81615375565b946020939093013593505050565b600080600080606085870312156145e057600080fd5b84356145eb81615375565b935060208501359250604085013567ffffffffffffffff81111561460e57600080fd5b61461a87828801614446565b95989497509550505050565b60008060008060006080868803121561463e57600080fd5b853561464981615375565b94506020860135935060408601359250606086013567ffffffffffffffff81111561467357600080fd5b61467f88828901614446565b969995985093965092949392505050565b600060208083850312156146a357600080fd5b825167ffffffffffffffff8111156146ba57600080fd5b8301601f810185136146cb57600080fd5b80516146d96144a78261515c565b80828252848201915084840188868560051b87010111156146f957600080fd5b600094505b838510156147235761470f81614523565b8352600194909401939185019185016146fe565b50979650505050505050565b6000602080838503121561474257600080fd5b825167ffffffffffffffff81111561475957600080fd5b8301601f8101851361476a57600080fd5b80516147786144a78261515c565b80828252848201915084840188868560051b870101111561479857600080fd5b600094505b8385101561472357805183526001949094019391850191850161479d565b6000602082840312156147cd57600080fd5b81356118dc8161538a565b6000602082840312156147ea57600080fd5b81516118dc8161538a565b60006020828403121561480757600080fd5b5051919050565b6000806040838503121561482157600080fd5b82519150602083015161483381615375565b809150509250929050565b6000806040838503121561485157600080fd5b61485a83614523565b915061486860208401614523565b90509250929050565b60006020828403121561488357600080fd5b81518060020b81146118dc57600080fd5b600080604083850312156148a757600080fd5b505080516020909101519092909150565b600080600080606085870312156148ce57600080fd5b8435935060208501359250604085013567ffffffffffffffff81111561460e57600080fd5b60006020828403121561490557600080fd5b813567ffffffffffffffff81111561491c57600080fd5b820160c081850312156118dc57600080fd5b60006020828403121561494057600080fd5b815167ffffffffffffffff8082111561495857600080fd5b908301906101a0828603121561496d57600080fd5b614975615101565b61497e8361443b565b815261498c6020840161443b565b602082015261499d6040840161443b565b60408201526149ae6060840161443b565b60608201526149bf6080840161443b565b60808201526149d060a0840161443b565b60a082015260c0838101519082015260e0808401519082015261010080840151908201526101208084015190820152610140808401519082015261016080840151908201526101808084015183811115614a2957600080fd5b614a35888287016144de565b918301919091525095945050505050565b600060408284031215614a5857600080fd5b6040516040810181811067ffffffffffffffff82111715614a7b57614a7b61535f565b6040528251614a8981615375565b81526020928301519281019290925250919050565b600080600060608486031215614ab357600080fd5b614abc84614535565b9250614aca60208501614535565b9150604084015163ffffffff81168114614ae357600080fd5b809150509250925092565b600060208284031215614b0057600080fd5b5035919050565b600060208284031215614b1957600080fd5b6118dc82614553565b60008060408385031215614b3557600080fd5b614b3e83614553565b915061486860208401614553565b600081518084526020808501945080840160005b83811015614b7c57815187529582019590820190600101614b60565b509495945050505050565b60008151808452614b9f8160208601602086016152b9565b601f01601f19169290920160200192915050565b60008251614bc58184602087016152b9565b9190910192915050565b60006001600160a01b038088168352808716602084015285604084015280851660608401525060a060808301526122d660a0830184614b87565b60006001600160a01b038088168352861515602084015285604084015280851660608401525060a060808301526122d660a0830184614b87565b60006001600160a01b038088168352866020840152851515604084015280851660608401525060a060808301526122d660a0830184614b87565b6001600160a01b038416815282516020820152602083015115156040820152604083015115156060820152606083015160030b608082015260c060a0820152600061270360c0830184614b87565b6001600160a01b03851681526fffffffffffffffffffffffffffffffff841660208201528260020b60408201526080606082015260006124936080830184614b87565b6001600160a01b03871681528560208201528415156040820152831515606082015282608082015260c060a08201526000612f9b60c0830184614b87565b6020815260006118dc6020830184614b4c565b608081526000614d726080830187614b87565b6001600160a01b0386811660208501528516604084015282810360608401526122d68185614b87565b6020815260006118dc6020830184614b87565b60208152614dc86020820183516001600160a01b03169052565b60006020830151614de460408401826001600160a01b03169052565b5060408301516001600160a01b03811660608401525060608301516001600160a01b03811660808401525060808301516001600160a01b03811660a08401525060a08301516001600160a01b03811660c08401525060c083015160e08381019190915283015161010080840191909152830151610120808401919091528301516101408084019190915283015161016080840191909152830151610180808401919091528301516101a0808401526121cc6101c0840182614b87565b60e08152845160e08201526000602086015160028110614ed057634e487b7160e01b600052602160045260246000fd5b61010083015260408601516001600160a01b03166101208301526060860151614f056101408401826001600160a01b03169052565b50608086015161016083015260a086015160c0610180840152614f2c6101a0840182614b87565b915050614f6e60208301866001600160a01b03808251168352602082015115156020840152806040830151166040840152506060810151151560608301525050565b60a082019390935260c0015292915050565b84815260606020808301829052908201849052600090859060808401835b87811015614fcc578335614fb181615375565b6001600160a01b031682529282019290820190600101614f9e565b508481036040860152614fdf8187614b4c565b9998505050505050505050565b600060a082018783526020878185015260a0604085015281875180845260c086019150828901935060005b8181101561503c5784516001600160a01b031683529383019391830191600101615017565b50506001600160a01b03969096166060850152505050608001529392505050565b8481528360208201526001600160a01b03831660408201526080606082015260006124936080830184614b87565b6000808335601e198436030181126150a257600080fd5b83018035915067ffffffffffffffff8211156150bd57600080fd5b6020019150600581901b360382131561439c57600080fd5b60008235605e19833603018112614bc557600080fd5b60008235609e19833603018112614bc557600080fd5b6040516101a0810167ffffffffffffffff811182821017156151255761512561535f565b60405290565b604051601f8201601f1916810167ffffffffffffffff811182821017156151545761515461535f565b604052919050565b600067ffffffffffffffff8211156151765761517661535f565b5060051b60200190565b600067ffffffffffffffff82111561519a5761519a61535f565b50601f01601f191660200190565b600082198211156151bb576151bb61531d565b500190565b6000826151dd57634e487b7160e01b600052601260045260246000fd5b500490565b60008160001904831182151516156151fc576151fc61531d565b500290565b6000828210156152135761521361531d565b500390565b600060a0823603121561522a57600080fd5b60405160a0810167ffffffffffffffff828210818311171561524e5761524e61535f565b816040528435915061525f82615375565b818352602085013560208401526040850135915061527c82615375565b8160408401526060850135606084015260808501359150808211156152a057600080fd5b506152ad36828601614488565b60808301525092915050565b60005b838110156152d45781810151838201526020016152bc565b8381111561062e5750506000910152565b60006000198214156152f9576152f961531d565b5060010190565b6000600160ff1b8214156153165761531661531d565b5060000390565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052602160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b03811681146109ea57600080fd5b80151581146109ea57600080fdfea164736f6c6343000807000a0000000000000000000000005300000000000000000000000000000000000004000000000000000000000000bd4a0f12293c54e4e8ad221271cf0d395dd60a71

Deployed Bytecode

0x60806040526004361061012c5760003560e01c80638da5cb5b116100a5578063bf654aac11610074578063f14210a611610059578063f14210a61461040f578063f2fde38b1461042f578063f3fef3a31461044f57610133565b8063bf654aac146103d9578063d3e1c284146103ef57610133565b80638da5cb5b1461035b578063923b8a2a14610379578063a66cd09414610399578063ad5c4648146103b957610133565b806343f68a49116100fc57806367ca7c91116100e157806367ca7c9114610302578063715018a6146103225780638c04166f1461033757610133565b806343f68a49146102aa57806361d027b3146102ca57610133565b8062d5a9e81461021f57806318780684146102485780632c8958f61461026a5780633e88c8ab1461028a57610133565b3661013357005b34801561013f57600080fd5b5060018054146101a25760405162461bcd60e51b815260206004820152602360248201527f574f574d41583a206e6f7420616c6c6f776564206f757473696465206f66207360448201526207761760ec1b60648201526084015b60405180910390fd5b60008060003660006101b560003661046f565b945094509450945094508461020c5760405162461bcd60e51b815260206004820152601c60248201527f574f574d41583a20756e737570706f727465642063616c6c6261636b000000006044820152606401610199565b610218848484846104e4565b5050505050005b61023261022d3660046148f3565b610634565b60405161023f9190614d4c565b60405180910390f35b34801561025457600080fd5b506102686102633660046148b8565b610658565b005b34801561027657600080fd5b506102686102853660046148b8565b6106c0565b34801561029657600080fd5b506102686102a53660046145ca565b610729565b3480156102b657600080fd5b506102686102c5366004614aee565b610735565b3480156102d657600080fd5b506003546102ea906001600160a01b031681565b6040516001600160a01b03909116815260200161023f565b34801561030e57600080fd5b5061026861031d366004614626565b610742565b34801561032e57600080fd5b506102686107b0565b34801561034357600080fd5b5061034d60055481565b60405190815260200161023f565b34801561036757600080fd5b506000546001600160a01b03166102ea565b34801561038557600080fd5b506102686103943660046148b8565b6107c4565b3480156103a557600080fd5b506102686103b4366004614aee565b61082d565b3480156103c557600080fd5b506002546102ea906001600160a01b031681565b3480156103e557600080fd5b5061034d60045481565b3480156103fb57600080fd5b5061026861040a3660046148b8565b61083a565b34801561041b57600080fd5b5061026861042a366004614aee565b6108a2565b34801561043b57600080fd5b5061026861044a366004614564565b61095d565b34801561045b57600080fd5b5061026861046a36600461459e565b6109ed565b600080803681600487810135906024808a01359160448b01358b0191820191013580601f811680156104a45780602003820191505b608482018c146104c9576000808080809a509a509a509a509a505050505050506104da565b506001995093975091955093509150505b9295509295909350565b6000841380156104f45750600083125b156105845761057f3385336001600160a01b0316630dfe16816040518163ffffffff1660e01b815260040160206040518083038186803b15801561053757600080fd5b505afa15801561054b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061056f9190614581565b6001600160a01b03169190610a0b565b61062e565b6000841280156105945750600083135b156105d75761057f3384336001600160a01b031663d21220a76040518163ffffffff1660e01b815260040160206040518083038186803b15801561053757600080fd5b60405162461bcd60e51b815260206004820152602660248201527f574f574d41583a20556e697377617020763320696e76617269616e742076696f6044820152653630ba34b7b760d11b6064820152608401610199565b50505050565b606061063e610a88565b61064782610aeb565b90506106536002600155565b919050565b60018054146106b55760405162461bcd60e51b815260206004820152602360248201527f574f574d41583a206e6f7420616c6c6f776564206f757473696465206f66207360448201526207761760ec1b6064820152608401610199565b61062e848383610c06565b600180541461071d5760405162461bcd60e51b815260206004820152602360248201527f574f574d41583a206e6f7420616c6c6f776564206f757473696465206f66207360448201526207761760ec1b6064820152608401610199565b61062e84848484610c2a565b61062e84848484610d27565b61073d610d3b565b600555565b600180541461079f5760405162461bcd60e51b815260206004820152602360248201527f574f574d41583a206e6f7420616c6c6f776564206f757473696465206f66207360448201526207761760ec1b6064820152608401610199565b6107a98585610d95565b5050505050565b6107b8610d3b565b6107c26000610da9565b565b60018054146108215760405162461bcd60e51b815260206004820152602360248201527f574f574d41583a206e6f7420616c6c6f776564206f757473696465206f66207360448201526207761760ec1b6064820152608401610199565b61062e84848484610e11565b610835610d3b565b600455565b60018054146108975760405162461bcd60e51b815260206004820152602360248201527f574f574d41583a206e6f7420616c6c6f776564206f757473696465206f66207360448201526207761760ec1b6064820152608401610199565b61062e838383610c06565b6108aa610d3b565b6003546040516000916001600160a01b03169083908381818185875af1925050503d80600081146108f7576040519150601f19603f3d011682016040523d82523d6000602084013e6108fc565b606091505b50509050806109595760405162461bcd60e51b8152602060048201526024808201527f576f776d61783a204661696c656420746f2073656e64206e617469766520746f6044820152636b656e7360e01b6064820152608401610199565b5050565b610965610d3b565b6001600160a01b0381166109e15760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f64647265737300000000000000000000000000000000000000000000000000006064820152608401610199565b6109ea81610da9565b50565b6109f5610d3b565b600354610959906001600160a01b038481169116835b6040516001600160a01b038316602482015260448101829052610a8390849063a9059cbb60e01b906064015b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff166001600160e01b031990931692909217909152610ea8565b505050565b600180541415610ae55760405162461bcd60e51b815260206004820152602260248201527f574f574d41583a207265656e7472616e742073776170206e6f7420616c6c6f77604482015261195960f21b6064820152608401610199565b60018055565b6060610af682610f8d565b6000610b01836112c1565b905060005b610b13606085018561508b565b9050811015610b6657610b53610b2c606086018661508b565b83818110610b3c57610b3c615349565b9050602002810190610b4e91906150d5565b6113a0565b5080610b5e816152e5565b915050610b06565b50610b70836114fb565b91506000610b816020850185614564565b6001600160a01b031614610ba157610b9c6020840184614564565b610bae565b6002546001600160a01b03165b6001600160a01b0316337f25a3ab87780ac0e2eeeb645d408cbc89d66c72590ae0e1f81bda8f3d3a8d121683610be7604088018861508b565b87604051610bf89493929190614f80565b60405180910390a350919050565b6000610c1482840184614564565b905061062e6001600160a01b0382163386610a0b565b600084138015610c3a5750600083125b15610c7d5761057f3385336001600160a01b0316630dfe16816040518163ffffffff1660e01b815260040160206040518083038186803b15801561053757600080fd5b600084128015610c8d5750600083135b15610cd05761057f3384336001600160a01b031663d21220a76040518163ffffffff1660e01b815260040160206040518083038186803b15801561053757600080fd5b60405162461bcd60e51b815260206004820152602660248201527f574f574d41583a20416c676562726120563120696e76617269616e742076696f6044820152653630ba34b7b760d11b6064820152608401610199565b61062e6001600160a01b0385163385610a0b565b6000546001600160a01b031633146107c25760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610199565b6109596001600160a01b0383163383610a0b565b600080546001600160a01b038381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6000610e1f828401846147bb565b90508015610e6a57610e653386336001600160a01b0316630fc63d106040518163ffffffff1660e01b815260040160206040518083038186803b15801561053757600080fd5b6107a9565b6107a93386336001600160a01b0316635f64b55b6040518163ffffffff1660e01b815260040160206040518083038186803b15801561053757600080fd5b6000610efd826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166118ca9092919063ffffffff16565b805190915015610a835780806020019051810190610f1b91906147d8565b610a835760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401610199565b6000610f9c604083018361508b565b905011610ff65760405162461bcd60e51b815260206004820152602260248201527f574f574d41583a204e6f206f757470757420746f6b656e732073706563696669604482015261195960f21b6064820152608401610199565b61100360a082018261508b565b9050611012604083018361508b565b9050146110875760405162461bcd60e51b815260206004820152602660248201527f574f574d41583a2057726f6e6720616d6f756e744f757445787065637465642060448201527f6c656e67746800000000000000000000000000000000000000000000000000006064820152608401610199565b611094608082018261508b565b90506110a3604083018361508b565b9050146110f25760405162461bcd60e51b815260206004820152601d60248201527f574f574d41583a2057726f6e6720736c697070616765206c656e6774680000006044820152606401610199565b60005b611102604083018361508b565b905081101561095957600061111a604084018461508b565b8381811061112a5761112a615349565b905060200201602081019061113f9190614564565b6001600160a01b031614156111a15760405162461bcd60e51b815260206004820152602260248201527f574f574d41583a2057726f6e67206f757470757420746f6b656e206164647265604482015261737360f01b6064820152608401610199565b60006111b060a084018461508b565b838181106111c0576111c0615349565b905060200201351161123a5760405162461bcd60e51b815260206004820152602560248201527f574f574d41583a2057726f6e6720616d6f756e744f757445787065637465642060448201527f76616c75650000000000000000000000000000000000000000000000000000006064820152608401610199565b60055461124a608084018461508b565b8381811061125a5761125a615349565b9050602002013511156112af5760405162461bcd60e51b815260206004820152601c60248201527f574f574d41583a20536c69707061676520697320746f6f2068696768000000006044820152606401610199565b806112b9816152e5565b9150506110f5565b6000806000341180156112e9575060006112de6020850185614564565b6001600160a01b0316145b80156112f757506020830135155b15611363575060025460408051630d0e30db60e41b8152905134926001600160a01b03169163d0e30db091849160048082019260009290919082900301818588803b15801561134557600080fd5b505af1158015611359573d6000803e3d6000fd5b505050505061139a565b60208301351561139a57506020820180359061139a903390309084906113899088614564565b6001600160a01b03169291906118e3565b92915050565b6000806113b06020840184614564565b6040516370a0823160e01b81523060048201526001600160a01b0391909116906370a082319060240160206040518083038186803b1580156113f157600080fd5b505afa158015611405573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061142991906147f5565b90506000805b61143c604086018661508b565b90508110156114f3576114d56114556020870187614564565b6020870135611467604089018961508b565b8581811061147757611477615349565b905060200281019061148991906150eb565b6114979060200135876151e2565b6114a191906151c0565b6114ae604089018961508b565b858181106114be576114be615349565b90506020028101906114d091906150eb565b61191b565b6114df90836151a8565b9150806114eb816152e5565b91505061142f565b509392505050565b606061150a604083018361508b565b905067ffffffffffffffff8111156115245761152461535f565b60405190808252806020026020018201604052801561154d578160200160208202803683370190505b50905060008060005b611563604086018661508b565b90508110156118c257611579604086018661508b565b8281811061158957611589615349565b905060200201602081019061159e9190614564565b6040516370a0823160e01b81523060048201529092506001600160a01b038316906370a082319060240160206040518083038186803b1580156115e057600080fd5b505afa1580156115f4573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061161891906147f5565b9250600061162960a087018761508b565b8381811061163957611639615349565b905060200201358411156117185761165460a087018761508b565b8381811061166457611664615349565b90506020020135846116769190615201565b905060006127106004548661168b91906151e2565b61169591906151c0565b9050808211156116cf579050806116ac8186615201565b8684815181106116be576116be615349565b602002602001018181525050611712565b6116dc60a088018861508b565b848181106116ec576116ec615349565b9050602002013586848151811061170557611705615349565b6020026020010181815250505b506117fe565b612710611728608088018861508b565b8481811061173857611738615349565b9050602002013561271061174c9190615201565b61175960a089018961508b565b8581811061176957611769615349565b9050602002013561177a91906151e2565b61178491906151c0565b8410156117de5760405162461bcd60e51b815260206004820152602260248201527f574f574d41583a20496e73756666696369656e74206f757470757420616d6f756044820152611b9d60f21b6064820152608401610199565b838583815181106117f1576117f1615349565b6020026020010181815250505b6002546001600160a01b038481169116141561187357600254604051632e1a7d4d60e01b8152600481018690526001600160a01b0390911690632e1a7d4d90602401600060405180830381600087803b15801561185a57600080fd5b505af115801561186e573d6000803e3d6000fd5b505050505b60035461188b9084906001600160a01b031683611ffd565b6118af83338785815181106118a2576118a2615349565b6020026020010151611ffd565b50806118ba816152e5565b915050611556565b505050919050565b60606118d984846000856120e0565b90505b9392505050565b6040516001600160a01b038085166024830152831660448201526064810182905261062e9085906323b872dd60e01b90608401610a37565b60007f554e49535741505f563300000000000000000000000000000000000000000000826060013514156119625761195b8361195684615218565b6121d4565b90506118dc565b7f48415348464c4f57000000000000000000000000000000000000000000000000826060013514156119a15761195b848461199c85615218565b6122e1565b7f574f4d4241540000000000000000000000000000000000000000000000000000826060013514156119e05761195b84846119db85615218565b61249d565b7f4c4556454c00000000000000000000000000000000000000000000000000000082606001351415611a1f5761195b8484611a1a85615218565b612568565b7f444f444f5f56320000000000000000000000000000000000000000000000000082606001351415611a5e5761195b8484611a5985615218565b61270c565b7f444f444f5f56330000000000000000000000000000000000000000000000000082606001351415611a9d5761195b8484611a9885615218565b612852565b7f574f4f464900000000000000000000000000000000000000000000000000000082606001351415611adc5761195b8484611ad785615218565b612903565b7f554e49535741505f56320000000000000000000000000000000000000000000082606001351415611b1b5761195b8484611b1685615218565b612978565b7f435552564500000000000000000000000000000000000000000000000000000082606001351415611b5a5761195b8484611b5585615218565b612b07565b7f50414e43414b45535741505f535441424c45000000000000000000000000000082606001351415611b995761195b8484611b9485615218565b612cc4565b7f444f444f5f56310000000000000000000000000000000000000000000000000082606001351415611bd85761195b8484611bd385615218565b612ddb565b7f42414c414e4345525f563200000000000000000000000000000000000000000082606001351415611c175761195b8484611c1285615218565b612e40565b7f4d4156455249434b5f563100000000000000000000000000000000000000000082606001351415611c555761195b83611c5084615218565b612fa7565b7f534144444c45000000000000000000000000000000000000000000000000000082606001351415611c945761195b8484611c8f85615218565b613054565b7f46554c43524f4d0000000000000000000000000000000000000000000000000082606001351415611cd35761195b8484611cce85615218565b613164565b7f554e49535741505f56325f524f5554455200000000000000000000000000000082606001351415611d125761195b8484611d0d85615218565b6131c4565b7f454c41535449430000000000000000000000000000000000000000000000000082606001351415611d515761195b8484611d4c85615218565b613382565b7f414c47454252415f56310000000000000000000000000000000000000000000082606001351415611d905761195b8484611d8b85615218565b6134f5565b7f414c47454252415f56315f39000000000000000000000000000000000000000082606001351415611dca5761195b8484611d8b85615218565b7f53594e435357415000000000000000000000000000000000000000000000000082606001351415611e095761195b8484611e0485615218565b6135f7565b63564f4f4960e01b82606001351415611e2f5761195b8484611e2a85615218565b6137e0565b7f56454c4f434f52455f563200000000000000000000000000000000000000000082606001351415611e7b5760025461195b906001600160a01b03168585611e7686615218565b6138cd565b7f495a49535741500000000000000000000000000000000000000000000000000082606001351415611eba5761195b8484611eb585615218565b613bf7565b7f56454c4f44524f4d45000000000000000000000000000000000000000000000082606001351415611ef95761195b8484611ef485615218565b613d2a565b7f575241505045445f4e415449564500000000000000000000000000000000000082606001351415611f385761195b8484611f3385615218565b613e8d565b7f4c49515549444954595f424f4f4b5f56325f310000000000000000000000000082606001351415611f775761195b8484611f7285615218565b613f77565b7f4d4156455249434b5f563200000000000000000000000000000000000000000082606001351415611fb55761195b83611fb084615218565b6140eb565b60405162461bcd60e51b815260206004820152601a60248201527f574f574d41583a20556e6b6e6f776e204445582066616d696c790000000000006044820152606401610199565b8061200757505050565b6002546001600160a01b03848116911614156120cc576000826001600160a01b03168260405160006040518083038185875af1925050503d806000811461206a576040519150601f19603f3d011682016040523d82523d6000602084013e61206f565b606091505b505090508061062e5760405162461bcd60e51b8152602060048201526024808201527f576f776d61783a204661696c656420746f2073656e64206e617469766520746f6044820152636b656e7360e01b6064820152608401610199565b610a836001600160a01b0384168383610a0b565b6060824710156121585760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c00000000000000000000000000000000000000000000000000006064820152608401610199565b600080866001600160a01b031685876040516121749190614bb3565b60006040518083038185875af1925050503d80600081146121b1576040519150601f19603f3d011682016040523d82523d6000602084013e6121b6565b606091505b50915091506121c7878383876141d3565b925050505b949350505050565b60008082608001518060200190518101906121ef91906147d8565b90506000816122125773fffd8963efd1fc6a506488495d951d5263988d25612219565b6401000276a45b604085810151815160008082526020820193849052630251596160e31b909352929350909182916001600160a01b03169063128acb089061226590309088908c90899060248101614c09565b6040805180830381600087803b15801561227e57600080fd5b505af1158015612292573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122b69190614894565b91509150836122cd576122c882615300565b6122d6565b6122d681615300565b979650505050505050565b60008082608001518060200190518101906122fc919061492e565b6040840151909150612319906001600160a01b0387169086614249565b8060e0015184101561232d5760c081018490525b82516040516370a0823160e01b81523060048201526000916001600160a01b0316906370a082319060240160206040518083038186803b15801561237057600080fd5b505afa158015612384573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123a891906147f5565b905083604001516001600160a01b031663c52ac720836040518263ffffffff1660e01b81526004016123da9190614dae565b600060405180830381600087803b1580156123f457600080fd5b505af1158015612408573d6000803e3d6000fd5b505085516040516370a0823160e01b81523060048201528493506001600160a01b0390911691506370a08231906024015b60206040518083038186803b15801561245157600080fd5b505afa158015612465573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061248991906147f5565b6124939190615201565b9695505050505050565b60408101516000906124ba906001600160a01b0386169085614249565b60408281015183519151639908fc8b60e01b81526001600160a01b0387811660048301529283166024820152604481018690526000606482015230608482015260001960a4820152911690639908fc8b9060c4016040805180830381600087803b15801561252757600080fd5b505af115801561253b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061255f9190614894565b50949350505050565b6040810151600090612585906001600160a01b0386169085610a0b565b81516040516370a0823160e01b81523060048201526000916001600160a01b0316906370a082319060240160206040518083038186803b1580156125c857600080fd5b505afa1580156125dc573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061260091906147f5565b6040848101518551825160008082526020820194859052631fa196a960e21b9094529394506001600160a01b0390911692637e865aa49261264b928a92909190309060248101614bcf565b600060405180830381600087803b15801561266557600080fd5b505af1158015612679573d6000803e3d6000fd5b505084516040516370a0823160e01b81523060048201528493506001600160a01b0390911691506370a082319060240160206040518083038186803b1580156126c157600080fd5b505afa1580156126d5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126f991906147f5565b6127039190615201565b95945050505050565b6040810151600090612729906001600160a01b0386169085610a0b565b600082608001518060200190518101906127439190614b07565b905060ff81166127d3576040808401519051632f58056d60e21b81523060048201526001600160a01b039091169063bd6015b490602401602060405180830381600087803b15801561279457600080fd5b505af11580156127a8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127cc91906147f5565b91506114f3565b6040808401519051636ec9facd60e11b81523060048201526001600160a01b039091169063dd93f59a90602401602060405180830381600087803b15801561281a57600080fd5b505af115801561282e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061270391906147f5565b60408082015182519151637597a8d360e11b81523060048201526001600160a01b03868116602483015292831660448201526064810185905260006084820181905260c060a483015260c48201819052929091169063eb2f51a69060e4015b602060405180830381600087803b1580156128cb57600080fd5b505af11580156128df573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118d991906147f5565b6040810151600090612920906001600160a01b0386169085610a0b565b60408281015183519151633ee101c160e11b81526001600160a01b03878116600483015292831660248201526044810186905260006064820181905230608483015260a4820152911690637dc203829060c4016128b1565b6040810151600090612995906001600160a01b0386169085610a0b565b600082608001518060200190518101906129af91906147f5565b90506000856001600160a01b031684604001516001600160a01b0316630dfe16816040518163ffffffff1660e01b815260040160206040518083038186803b1580156129fa57600080fd5b505afa158015612a0e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a329190614581565b6001600160a01b0316149050600080612a4f86604001518461430a565b91509150612a5f878383876143a3565b94508415612afc5785604001516001600160a01b031663022c0d9f84612a855786612a88565b60005b85612a94576000612a96565b875b604080516000815260208101918290526001600160e01b031960e086901b16909152612ac992919030906024810161505d565b600060405180830381600087803b158015612ae357600080fd5b505af1158015612af7573d6000803e3d6000fd5b505050505b505050509392505050565b60008060008360800151806020019051810190612b24919061483e565b85516040516370a0823160e01b81523060048201529294509092506000916001600160a01b03909116906370a082319060240160206040518083038186803b158015612b6f57600080fd5b505afa158015612b83573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ba791906147f5565b6040860151909150612bc4906001600160a01b0389169088614249565b6040808601519051630f7c084960e21b8152600f85810b600483015284900b602482015260448101889052600060648201526001600160a01b0390911690633df02124906084015b600060405180830381600087803b158015612c2657600080fd5b505af1158015612c3a573d6000803e3d6000fd5b505086516040516370a0823160e01b81523060048201528493506001600160a01b0390911691506370a082319060240160206040518083038186803b158015612c8257600080fd5b505afa158015612c96573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612cba91906147f5565b6122d69190615201565b60008060008360800151806020019051810190612ce1919061483e565b85516040516370a0823160e01b81523060048201529294509092506000916001600160a01b03909116906370a082319060240160206040518083038186803b158015612d2c57600080fd5b505afa158015612d40573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d6491906147f5565b6040860151909150612d81906001600160a01b0389169088614249565b6040808601519051630b68372160e31b81526fffffffffffffffffffffffffffffffff80861660048301528416602482015260448101889052600060648201526001600160a01b0390911690635b41b90890608401612c0c565b6040810151600090612df8906001600160a01b0386169085614249565b6040808301519051638dae733360e01b8152600481018590526000602482018190526060604483015260648201526001600160a01b0390911690638dae7333906084016128b1565b60008060008360800151806020019051810190612e5d919061480e565b9150915060006040518060c0016040528084815260200160006001811115612e8757612e87615333565b81526001600160a01b03808a16602083015287511660408201526060810188905260800160006040519080825280601f01601f191660200182016040528015612ed7576020820181803683370190505b5090526040805160808101825230808252600060208301819052928201526060810191909152909150612f146001600160a01b0389168489614249565b6040516352bbbe2960e01b81526001600160a01b038416906352bbbe2990612f49908590859060009060001990600401614ea0565b602060405180830381600087803b158015612f6357600080fd5b505af1158015612f77573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f9b91906147f5565b98975050505050505050565b6000808260800151806020019051810190612fc291906147d8565b905082604001516001600160a01b031663c51c902930868460008089608001516040518763ffffffff1660e01b815260040161300396959493929190614d0e565b6040805180830381600087803b15801561301c57600080fd5b505af1158015613030573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127039190614894565b600080600083608001518060200190518101906130719190614b22565b85516040516370a0823160e01b81523060048201529294509092506000916001600160a01b03909116906370a082319060240160206040518083038186803b1580156130bc57600080fd5b505afa1580156130d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130f491906147f5565b6040860151909150613111906001600160a01b0389169088614249565b60408086015190516348b4aac360e11b815260ff808616600483015284166024820152604481018890526000606482015260001960848201526001600160a01b039091169063916955869060a401612c0c565b6040810151600090613181906001600160a01b0386169085610a0b565b60408281015183519151634998b10960e11b81526001600160a01b03878116600483015292831660248201523060448201529116906393316212906064016128b1565b60008082604001516001600160a01b031663f887ea406040518163ffffffff1660e01b8152600401602060405180830381600087803b15801561320657600080fd5b505af115801561321a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061323e9190614581565b90506132546001600160a01b0386168286614249565b604080516002808252606082018352600092602083019080368337019050509050858160008151811061328957613289615349565b60200260200101906001600160a01b031690816001600160a01b0316815250508360000151816001815181106132c1576132c1615349565b6001600160a01b0392831660209182029290920101526040516338ed173960e01b8152908316906338ed1739906133079088906000908690309060001990600401614fec565b600060405180830381600087803b15801561332157600080fd5b505af1158015613335573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261335d919081019061472f565b60018151811061336f5761336f615349565b6020026020010151925050509392505050565b60008082604001516001600160a01b0316630dfe16816040518163ffffffff1660e01b815260040160206040518083038186803b1580156133c257600080fd5b505afa1580156133d6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133fa9190614581565b6001600160a01b0316856001600160a01b03161490506000816134315773fffd8963efd1fc6a506488495d951d5263988d25613438565b6401000276a45b60408581015181516000808252602082019384905263092cc68360e21b909352929350909182916001600160a01b0316906324b31a0c906134849030908b908990899060248101614c43565b6040805180830381600087803b15801561349d57600080fd5b505af11580156134b1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134d59190614894565b91509150836134ec576134e782615300565b612f9b565b612f9b81615300565b60008082604001516001600160a01b0316630dfe16816040518163ffffffff1660e01b815260040160206040518083038186803b15801561353557600080fd5b505afa158015613549573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061356d9190614581565b6001600160a01b0316856001600160a01b03161490506000816135a45773fffd8963efd1fc6a506488495d951d5263988d256135ab565b6401000276a45b604085810151815160008082526020820193849052630251596160e31b909352929350909182916001600160a01b03169063128acb089061348490309088908c90899060248101614c09565b60008082604001516001600160a01b031663fbfa77cf6040518163ffffffff1660e01b815260040160206040518083038186803b15801561363757600080fd5b505afa15801561364b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061366f9190614581565b90506136856001600160a01b0386168286610a0b565b6040838101519051631f2c13e160e31b81526001600160a01b03878116600483015291821660248201529082169063f9609f0890604401602060405180830381600087803b1580156136d657600080fd5b505af11580156136ea573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061370e91906147f5565b50604080516001600160a01b03878116602083015230828401526002606080840191909152835180840390910181526080830180855287850151600080835260a0860196879052637132bb7f60e01b9096529194939190921691637132bb7f916137809186918691829160a401614d5f565b6040805180830381600087803b15801561379957600080fd5b505af11580156137ad573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137d19190614a46565b60200151979650505050505050565b600080600083608001518060200190518101906137fd9190614b22565b6040860151919350915061381c906001600160a01b0388169087614249565b60408085015190516347b5ef5560e11b815260ff808516600483015283166024820152604481018790526000606482015230608482015260001960a48201526001600160a01b0390911690638f6bdeaa9060c4015b6040805180830381600087803b15801561388a57600080fd5b505af115801561389e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906138c29190614894565b509695505050505050565b60008082608001518060200190518101906138e89190614581565b90508060006001600160a01b03878116908916146139065786613909565b60005b90506000886001600160a01b031686600001516001600160a01b031614613931578551613934565b60005b905060606001600160a01b038316613a7657604051632e1a7d4d60e01b8152600481018990526001600160a01b038b1690632e1a7d4d90602401600060405180830381600087803b15801561398857600080fd5b505af115801561399c573d6000803e3d6000fd5b50505060408089015190516342f3185360e11b81526001600160a01b039182166004820152600060248201819052868316604483015260648201819052600f8c900b608483015285831660a4830152600160c483015260e4820181905261012061010483015261012482015290861691506385e630a6908a90610144016000604051808303818588803b158015613a3257600080fd5b505af1158015613a46573d6000803e3d6000fd5b50505050506040513d6000823e601f3d908101601f19168201604052613a6f9190810190614690565b9050613b5a565b613a8a6001600160a01b038416858a614249565b60408781015190516342f3185360e11b81526001600160a01b039182166004820152600060248201819052858316604483015260648201819052600f8b900b608483015284831660a4830152600160c483015260e48201819052610120610104830152610124820152908516906385e630a69061014401600060405180830381600087803b158015613b1b57600080fd5b505af1158015613b2f573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052613b579190810190614690565b90505b80600181518110613b6d57613b6d615349565b6020026020010151600f0b955060006001600160a01b0316826001600160a01b03161415613bea57896001600160a01b031663d0e30db0876040518263ffffffff1660e01b81526004016000604051808303818588803b158015613bd057600080fd5b505af1158015613be4573d6000803e3d6000fd5b50505050505b5050505050949350505050565b6000808260800151806020019051810190613c129190614871565b604080516001600160a01b038816602082015291925060009101604051602081830303815290604052905083600001516001600160a01b0316866001600160a01b03161015613ceb5783604001516001600160a01b031663857f812f308785856040518563ffffffff1660e01b8152600401613c919493929190614ccb565b6040805180830381600087803b158015613caa57600080fd5b505af1158015613cbe573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ce29190614894565b9350613d219050565b83604001516001600160a01b0316632c481252308785856040518563ffffffff1660e01b81526004016138719493929190614ccb565b50509392505050565b6000808260800151806020019051810190613d4591906147f5565b60408085015190516378a051ad60e11b8152600481018790526001600160a01b03888116602483015292935091169063f140a35a9060440160206040518083038186803b158015613d9557600080fd5b505afa158015613da9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613dcd91906147f5565b915081156114f3576040830151613def906001600160a01b0387169086610a0b565b60408301516001600160a01b031663022c0d9f8215613e0e5783613e11565b60005b8315613e1e576000613e20565b845b604080516000815260208101918290526001600160e01b031960e086901b16909152613e5392919030906024810161505d565b600060405180830381600087803b158015613e6d57600080fd5b505af1158015613e81573d6000803e3d6000fd5b50505050509392505050565b600081604001516001600160a01b0316846001600160a01b03161415613f165781604001516001600160a01b0316632e1a7d4d846040518263ffffffff1660e01b8152600401613edf91815260200190565b600060405180830381600087803b158015613ef957600080fd5b505af1158015613f0d573d6000803e3d6000fd5b50505050613f6f565b81604001516001600160a01b031663d0e30db0846040518263ffffffff1660e01b81526004016000604051808303818588803b158015613f5557600080fd5b505af1158015613f69573d6000803e3d6000fd5b50505050505b509092915050565b6000808260800151806020019051810190613f9291906147f5565b83516040516370a0823160e01b815230600482015260019290921492506000916001600160a01b03909116906370a082319060240160206040518083038186803b158015613fdf57600080fd5b505afa158015613ff3573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061401791906147f5565b6040850151909150614034906001600160a01b0388169087610a0b565b604080850151905163029e02cd60e51b815283151560048201523060248201526001600160a01b03909116906353c059a090604401602060405180830381600087803b15801561408357600080fd5b505af1158015614097573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906140bb91906147f5565b5083516040516370a0823160e01b815230600482015282916001600160a01b0316906370a0823190602401612439565b600080826080015180602001905181019061410691906147d8565b905060008161411a57637fffffff19614120565b637fffffff5b60408051608080820183528882528515156020830152600082840152600384900b606083015287830151908801519251633eece7db60e01b815293945090926001600160a01b0390911691633eece7db91614182913091869190600401614c7d565b6040805180830381600087803b15801561419b57600080fd5b505af11580156141af573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122d69190614894565b6060831561423f578251614238576001600160a01b0385163b6142385760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610199565b50816121cc565b6121cc8383614411565b604051636eb1769f60e11b81523060048201526001600160a01b038381166024830152600091839186169063dd62ed3e9060440160206040518083038186803b15801561429557600080fd5b505afa1580156142a9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906142cd91906147f5565b6142d791906151a8565b6040516001600160a01b03851660248201526044810182905290915061062e90859063095ea7b360e01b90606401610a37565b600080600080856001600160a01b0316630902f1ac6040518163ffffffff1660e01b815260040160606040518083038186803b15801561434957600080fd5b505afa15801561435d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906143819190614a9e565b509150915084614392578082614395565b81815b9350935050505b9250929050565b6000806143b283612710615201565b6143bc90876151e2565b905060006143da6dffffffffffffffffffffffffffff8616836151e2565b90506000826143fb6127106dffffffffffffffffffffffffffff8a166151e2565b61440591906151a8565b9050612f9b81836151c0565b8151156144215781518083602001fd5b8060405162461bcd60e51b81526004016101999190614d9b565b805161065381615375565b60008083601f84011261445857600080fd5b50813567ffffffffffffffff81111561447057600080fd5b60208301915083602082850101111561439c57600080fd5b600082601f83011261449957600080fd5b81356144ac6144a782615180565b61512b565b8181528460208386010111156144c157600080fd5b816020850160208301376000918101602001919091529392505050565b600082601f8301126144ef57600080fd5b81516144fd6144a782615180565b81815284602083860101111561451257600080fd5b6121cc8260208301602087016152b9565b8051600f81900b811461065357600080fd5b80516dffffffffffffffffffffffffffff8116811461065357600080fd5b805160ff8116811461065357600080fd5b60006020828403121561457657600080fd5b81356118dc81615375565b60006020828403121561459357600080fd5b81516118dc81615375565b600080604083850312156145b157600080fd5b82356145bc81615375565b946020939093013593505050565b600080600080606085870312156145e057600080fd5b84356145eb81615375565b935060208501359250604085013567ffffffffffffffff81111561460e57600080fd5b61461a87828801614446565b95989497509550505050565b60008060008060006080868803121561463e57600080fd5b853561464981615375565b94506020860135935060408601359250606086013567ffffffffffffffff81111561467357600080fd5b61467f88828901614446565b969995985093965092949392505050565b600060208083850312156146a357600080fd5b825167ffffffffffffffff8111156146ba57600080fd5b8301601f810185136146cb57600080fd5b80516146d96144a78261515c565b80828252848201915084840188868560051b87010111156146f957600080fd5b600094505b838510156147235761470f81614523565b8352600194909401939185019185016146fe565b50979650505050505050565b6000602080838503121561474257600080fd5b825167ffffffffffffffff81111561475957600080fd5b8301601f8101851361476a57600080fd5b80516147786144a78261515c565b80828252848201915084840188868560051b870101111561479857600080fd5b600094505b8385101561472357805183526001949094019391850191850161479d565b6000602082840312156147cd57600080fd5b81356118dc8161538a565b6000602082840312156147ea57600080fd5b81516118dc8161538a565b60006020828403121561480757600080fd5b5051919050565b6000806040838503121561482157600080fd5b82519150602083015161483381615375565b809150509250929050565b6000806040838503121561485157600080fd5b61485a83614523565b915061486860208401614523565b90509250929050565b60006020828403121561488357600080fd5b81518060020b81146118dc57600080fd5b600080604083850312156148a757600080fd5b505080516020909101519092909150565b600080600080606085870312156148ce57600080fd5b8435935060208501359250604085013567ffffffffffffffff81111561460e57600080fd5b60006020828403121561490557600080fd5b813567ffffffffffffffff81111561491c57600080fd5b820160c081850312156118dc57600080fd5b60006020828403121561494057600080fd5b815167ffffffffffffffff8082111561495857600080fd5b908301906101a0828603121561496d57600080fd5b614975615101565b61497e8361443b565b815261498c6020840161443b565b602082015261499d6040840161443b565b60408201526149ae6060840161443b565b60608201526149bf6080840161443b565b60808201526149d060a0840161443b565b60a082015260c0838101519082015260e0808401519082015261010080840151908201526101208084015190820152610140808401519082015261016080840151908201526101808084015183811115614a2957600080fd5b614a35888287016144de565b918301919091525095945050505050565b600060408284031215614a5857600080fd5b6040516040810181811067ffffffffffffffff82111715614a7b57614a7b61535f565b6040528251614a8981615375565b81526020928301519281019290925250919050565b600080600060608486031215614ab357600080fd5b614abc84614535565b9250614aca60208501614535565b9150604084015163ffffffff81168114614ae357600080fd5b809150509250925092565b600060208284031215614b0057600080fd5b5035919050565b600060208284031215614b1957600080fd5b6118dc82614553565b60008060408385031215614b3557600080fd5b614b3e83614553565b915061486860208401614553565b600081518084526020808501945080840160005b83811015614b7c57815187529582019590820190600101614b60565b509495945050505050565b60008151808452614b9f8160208601602086016152b9565b601f01601f19169290920160200192915050565b60008251614bc58184602087016152b9565b9190910192915050565b60006001600160a01b038088168352808716602084015285604084015280851660608401525060a060808301526122d660a0830184614b87565b60006001600160a01b038088168352861515602084015285604084015280851660608401525060a060808301526122d660a0830184614b87565b60006001600160a01b038088168352866020840152851515604084015280851660608401525060a060808301526122d660a0830184614b87565b6001600160a01b038416815282516020820152602083015115156040820152604083015115156060820152606083015160030b608082015260c060a0820152600061270360c0830184614b87565b6001600160a01b03851681526fffffffffffffffffffffffffffffffff841660208201528260020b60408201526080606082015260006124936080830184614b87565b6001600160a01b03871681528560208201528415156040820152831515606082015282608082015260c060a08201526000612f9b60c0830184614b87565b6020815260006118dc6020830184614b4c565b608081526000614d726080830187614b87565b6001600160a01b0386811660208501528516604084015282810360608401526122d68185614b87565b6020815260006118dc6020830184614b87565b60208152614dc86020820183516001600160a01b03169052565b60006020830151614de460408401826001600160a01b03169052565b5060408301516001600160a01b03811660608401525060608301516001600160a01b03811660808401525060808301516001600160a01b03811660a08401525060a08301516001600160a01b03811660c08401525060c083015160e08381019190915283015161010080840191909152830151610120808401919091528301516101408084019190915283015161016080840191909152830151610180808401919091528301516101a0808401526121cc6101c0840182614b87565b60e08152845160e08201526000602086015160028110614ed057634e487b7160e01b600052602160045260246000fd5b61010083015260408601516001600160a01b03166101208301526060860151614f056101408401826001600160a01b03169052565b50608086015161016083015260a086015160c0610180840152614f2c6101a0840182614b87565b915050614f6e60208301866001600160a01b03808251168352602082015115156020840152806040830151166040840152506060810151151560608301525050565b60a082019390935260c0015292915050565b84815260606020808301829052908201849052600090859060808401835b87811015614fcc578335614fb181615375565b6001600160a01b031682529282019290820190600101614f9e565b508481036040860152614fdf8187614b4c565b9998505050505050505050565b600060a082018783526020878185015260a0604085015281875180845260c086019150828901935060005b8181101561503c5784516001600160a01b031683529383019391830191600101615017565b50506001600160a01b03969096166060850152505050608001529392505050565b8481528360208201526001600160a01b03831660408201526080606082015260006124936080830184614b87565b6000808335601e198436030181126150a257600080fd5b83018035915067ffffffffffffffff8211156150bd57600080fd5b6020019150600581901b360382131561439c57600080fd5b60008235605e19833603018112614bc557600080fd5b60008235609e19833603018112614bc557600080fd5b6040516101a0810167ffffffffffffffff811182821017156151255761512561535f565b60405290565b604051601f8201601f1916810167ffffffffffffffff811182821017156151545761515461535f565b604052919050565b600067ffffffffffffffff8211156151765761517661535f565b5060051b60200190565b600067ffffffffffffffff82111561519a5761519a61535f565b50601f01601f191660200190565b600082198211156151bb576151bb61531d565b500190565b6000826151dd57634e487b7160e01b600052601260045260246000fd5b500490565b60008160001904831182151516156151fc576151fc61531d565b500290565b6000828210156152135761521361531d565b500390565b600060a0823603121561522a57600080fd5b60405160a0810167ffffffffffffffff828210818311171561524e5761524e61535f565b816040528435915061525f82615375565b818352602085013560208401526040850135915061527c82615375565b8160408401526060850135606084015260808501359150808211156152a057600080fd5b506152ad36828601614488565b60808301525092915050565b60005b838110156152d45781810151838201526020016152bc565b8381111561062e5750506000910152565b60006000198214156152f9576152f961531d565b5060010190565b6000600160ff1b8214156153165761531661531d565b5060000390565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052602160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b03811681146109ea57600080fd5b80151581146109ea57600080fdfea164736f6c6343000807000a

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

0000000000000000000000005300000000000000000000000000000000000004000000000000000000000000bd4a0f12293c54e4e8ad221271cf0d395dd60a71

-----Decoded View---------------
Arg [0] : _weth (address): 0x5300000000000000000000000000000000000004
Arg [1] : _treasury (address): 0xBD4a0F12293C54e4E8ad221271CF0d395dd60a71

-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 0000000000000000000000005300000000000000000000000000000000000004
Arg [1] : 000000000000000000000000bd4a0f12293c54e4e8ad221271cf0d395dd60a71


Block Transaction Gas Used Reward
view all blocks sequenced

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

Validator Index Block Amount
View All Withdrawals

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

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