ETH Price: $1,599.29 (+2.79%)
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Whitelist Dex Ag...164726702023-01-23 23:04:35809 days ago1674515075IN
0x6209a862...cF49c7425
0 ETH0.0024801617.58172267
Whitelist Dex Ag...164726682023-01-23 23:04:11809 days ago1674515051IN
0x6209a862...cF49c7425
0 ETH0.0046087416.28708812
Whitelist Keeper...164726662023-01-23 23:03:47809 days ago1674515027IN
0x6209a862...cF49c7425
0 ETH0.0016242717.33391254

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

Contract Source Code Verified (Exact Match)

Contract Name:
RookSwap

Compiler Version
v0.8.16+commit.07a7930e

Optimization Enabled:
Yes with 1000000 runs

Other Settings:
default evmVersion
File 1 of 15 : swap.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.16;

import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";

import "./reentrancyGuard.sol";
import "./whitelist.sol";
import "./owner.sol";
import "./assetManagement.sol";
import "./orderUtils.sol";
import "./utils.sol";

/// @title RookSwap - A token swapping protocol that enables users to receive MEV generated from their orders.
/// A keeper executes the order on behalf of the user and extracts the value created by the order to distribute back to the user.
/// Orders are signed and submitted to an off-chain orderbook, where keepers can bid for the right to execute.
/// Users don't have to pay gas to swap tokens, except for token allowance approvals.
/// @author Joey Zacherl - <[email protected]>

/// Note: Some critical public/external functions in this contract are appended with underscores and extra characters as a gas optimization
/// Example: function() may become function_xyz() or function__abc()

// Exception codes
// RS:E0 - ETH transfer failed
// RS:E1 - Address(0) is not allowed
// RS:E2 - Cannot overfill order
// RS:E3 - Order not partially fillable, must fill order exactly full
// RS:E4 - Order already filled
// RS:E5 - Swap tokens must differ
// RS:E6 - Order not fillable
// RS:E7 - Order signature invalid
// RS:E8 - Malformed ecdsa signature
// RS:E9 - Invalid ecdsa signature
// RS:E10 - Malformed pre-signature
// RS:E11 - toUint256_outOfBounds
// RS:E12 - Array lengths must match, orders and makerAmountsToSpend
// RS:E13 - Do not use takerTokenDistribution_custom for one single order, use takerTokenDistribution_even
// RS:E14 - Array lengths must match, orders and takerTokenDistributions
// RS:E15 - Orders must not involve the same maker & same tokens
// RS:E16 - Presigner must be valid
// RS:E17 - Must be owner
// RS:E18 - ReentrancyGuard: reentrant call
// RS:E19 - Can not approve allowance for 0x0
// RS:E20 - Not permitted to cancel order
// RS:E21 - Must be whitelisted Keeper
// RS:E22 - Must be whitelisted DexAggKeeper
// RS:E23 - maker not satisfied, partiallyFillable = true
// RS:E24 - maker not satisfied, partiallyFillable = false
// RS:E25 - RookSwap contract surplusToken balances must not decrease, including threshold
// RS:E26 - RookSwap contract otherToken balances must not decrease
// RS:E27 - Begin and expiry must be valid
// RS:E28 - surplusToken must be in all orders
// RS:E29 - otherToken must be in all orders
// RS:E30 - Must be whitelisted DexAggRouter
// RS:E31 - surplusToken and otherToken must differ
// RS:E32 - approveToken must be either surplusToken or otherToken

/// @dev Keeper interface for the callback function to execute swaps.
abstract contract Keeper
{
    function rookSwapExecution_s3gN(
        address rookSwapsMsgSender,
        uint256[] calldata makerAmountsSentByRook,
        bytes calldata data
    )
        external
        virtual
        returns (bytes memory keeperReturn);
}

contract RookSwap is
    ReentrancyGuard,
    Owner,
    AssetManagement,
    Whitelist,
    OrderUtils
{
    using Address for address;
    using SafeERC20 for IERC20;
    using LibBytes for bytes;

    /// @dev Facilitate swaps using Keeper's calldata through Keeper's custom trading contract.
    /// Must be a whitelisted Keeper.
    /// @param orders The orders to fill
    /// @param makerAmountsToSpend makerAmounts which to fill, correspond with orders
    /// @param keeperTaker Keeper's taker address which will execute the swap, typically a trading contract. Must implement rookSwapExecution_s3gN.
    /// @param data Keeper's calldata to pass into their rookSwapExecution_s3gN implementation.
    /// This calldata is responsible for facilitating trade and paying the maker back in
    /// takerTokens or else the transaction will revert.
    /// @return keeperReturn Return the keeper's keeperCallData return value from rookSwapExecution_s3gN.
    /// They'll likey want this for simulations, it's up to the keeper to decide what to return.
    /// to help with tx simulations.
    function swapKeeper__oASr(
        Order[] calldata orders,
        uint256[] calldata makerAmountsToSpend,
        address keeperTaker,
        bytes calldata data
    )
        external
        nonReentrant
        returns (bytes memory keeperReturn)
    {
        // Only allow swap execution whitelisted Keepers
        require(
            getKeeperWhitelistPosition__2u3w(keeperTaker) != 0,
            "RS:E21"
        );

        LibData.MakerData[] memory makerData = _prepareSwapExecution(orders, makerAmountsToSpend, keeperTaker);

        // Call the keeper's rookSwapExecution_s3gN function to execute the swap
        // Keeper must satisfy the user based on the signed order within this callback execution
        // We are passing in msg.sender to the keeper's rookSwapExecution_s3gN function.
        // We have ensured that keeperTaker is a whitelisted Rook keeper
        // but we have not ensured that msg.sender is keeperTaker's EOA
        // Keeper is responsible for ensuring that the msg.sender we pass them is their EOA and in their personal whitelist
        // Keeper is also responsible for ensuring that only a valid RookSwap contract can call their rookSwapExecution_s3gN function
        // This RookSwap contract is NOT upgradeable, so you can trust that the msg.sender we're passing along to Keeper is safe and correct
        keeperReturn = Keeper(keeperTaker).rookSwapExecution_s3gN(msg.sender, makerAmountsToSpend, data);

        _finalizeSwapExecution(orders, makerAmountsToSpend, makerData, keeperTaker);
    }

    /// @dev Facilitate swaps using DexAgg Keeper's calldata through this contract.
    /// Must be a whitelisted DexAgg Keeper.
    /// @param orders The orders to fill.
    /// @param makerAmountsToSpend makerAmounts which to fill, correspond with orders.
    /// @param makerWeights Mathematical weight for distributing tokens to makers.
    /// moved this math off chain to save gas and simplify on chain computation.
    /// corresponds with orders.
    /// @param swap Execution calldata for facilitating a swap via DEX Aggregators right here on this contract.
    /// If this function fails to pay back the maker in takerTokens, the transaction will revert.
    /// @param takerTokenDistributions Quantities for distributing tokens to makers.
    /// moved this math off chain to save gas and simplify on chain computation.
    /// corresponds with orders.
    /// @param metaData Supplementary data for swap execution and how to handle surplusTokens.
    /// @return surplusAmount Amount of surplusTokens acquired during swap execution.
    function swapDexAggKeeper_8B77(
        Order[] calldata orders,
        uint256[] calldata makerAmountsToSpend,
        uint256[] calldata makerWeights,
        LibSwap.DexAggSwap calldata swap,
        uint256[] calldata takerTokenDistributions,
        LibSwap.MetaData calldata metaData
    )
        external
        nonReentrant
        returns (uint256 surplusAmount)
    {
        // Only allow swap execution whitelisted DexAggKeepers
        require(
            getDexAggKeeperWhitelistPosition_IkFc(msg.sender) != 0,
            "RS:E22"
        );

        // Only allow swap execution on whitelisted DexAggs
        require(
            getDexAggRouterWhitelistPosition_ZgLC(swap.router) != 0,
            "RS:E30"
        );

        // surplusToken and otherToken must differ
        require(
            metaData.surplusToken != metaData.otherToken,
            "RS:E31"
        );

        // surplusToken and otherToken must be in every order, meaning there can only be 2 unique tokens in the swap.
        // otherwise this is an unsupported type of batching, or there is a logic bug with the dexAggKeeper offchain logic.
        // This check may not be necessary, but it's good to reject types of batching that are not supported today.
        // Reverting here will help prevent bugs and undesired behaviors.
        for (uint256 i; i < orders.length;)
        {
            require(
                metaData.surplusToken == orders[i].makerToken || metaData.surplusToken == orders[i].takerToken,
                "RS:E28"
            );
            require(
                metaData.otherToken == orders[i].takerToken || metaData.otherToken == orders[i].makerToken,
                "RS:E29"
            );

            // Gas optimization
            unchecked
            {
                ++i;
            }
        }

        LibData.ContractData memory contractData = LibData.ContractData(
            IERC20(metaData.surplusToken).balanceOf(address(this)),
            0,
            IERC20(metaData.otherToken).balanceOf(address(this))
        );
        LibData.MakerData[] memory makerData = _prepareSwapExecution(orders, makerAmountsToSpend, address(this));

        // Begin the swap execution by performing swaps on DexAggs
        uint256 takerTokenAmountToDistribute = _beginDexAggSwapExecution(
            swap,
            metaData
        );

        // Complete the swap execution by distributing takerTokens properly
        if (metaData.takerTokenDistributionType == LibSwap.TakerTokenDistributionType.Even)
        {
            _completeDexAggSwapExecution_takerTokenDistribution_even(
                orders,
                makerWeights,
                takerTokenAmountToDistribute
            );
        }
        else // elif (metaData.takerTokenDistributionType == LibSwap.TakerTokenDistributionType.Custom)
        {
            _completeDexAggSwapExecution_takerTokenDistribution_custom(
                orders,
                takerTokenDistributions
            );
        }

        _finalizeSwapExecution(orders, makerAmountsToSpend, makerData, msg.sender);

        // Return the amount of surplus retained
        surplusAmount = _finalizeSwapExecution_dexAggKeeper(contractData, metaData);
    }

    /// @dev Prepare for swap execution by doing math, validating orders, and
    /// transferring makerTokens from the maker to the Keeper who will be facilitating swaps.
    function _prepareSwapExecution(
        Order[] calldata orders,
        uint256[] calldata makerAmountsToSpend,
        address makerTokenRecipient
    )
        private
        returns (LibData.MakerData[] memory makerData)
    {
        uint256 ordersLength = orders.length;
        require(
            ordersLength == makerAmountsToSpend.length,
            "RS:E12"
        );

        makerData = new LibData.MakerData[](ordersLength);
        for (uint256 i; i < ordersLength;)
        {
            // RookSwap does not currently support batching together swaps where 2 or more of the orders have the same maker & same tokens
            // This could be supported, however the gas efficiency would be horrible
            // because we'd have to use mappings and storage which costs a lot of gas
            // If you want to batch together orders in this way, it's still supported if you
            // by simply calling the RookSwap's swap function separately.
            // Example:
            //  call (RookSwap).swapKeeper([order1, order2, order3])
            //  then immediately after call (RookSwap).swapKeeper([order4, order5, order6])
            //  In this example let's assume that order1 and order4 include the same maker & tokens, so they had to be in separate function calls
            //  examples of order1 and order4
            //      Order1: Maker0x1234 swapping 900 DAI -> 0.6 WETH
            //      Order4: Maker0x1234 swapping 900 DAI -> 0.6 WETH
            // The reason for this is that _finalizeSwapExecution would be exposed to an exploit
            // if it attempted to process them in one single function call
            // And if we do process it securely in one signle function call, gas efficiency suffers beyond recovery.

            //  examples of order1 and order4
            //      Order1: Maker0x1234 swapping 0.6 WETH -> 900 DAI
            //      Order4: Maker0x1234 swapping 900 DAI -> 0.6 WETH

            //  examples of order1 and order5
            //      Order1: Maker0x1234 swapping 0.6 WETH -> 900 DAI
            //      Order5: Maker0x1234 swapping 900 DAI -> 900 USDC

            for (uint256 j; j < ordersLength;)
            {
                if (i != j && orders[i].maker == orders[j].maker &&
                    (orders[i].takerToken == orders[j].takerToken || orders[i].makerToken == orders[j].takerToken))
                {
                    revert("RS:E15");
                }

                // Gas optimization
                unchecked
                {
                    ++j;
                }
            }

            bytes32 orderHash = getOrderHash(orders[i]);
            // makerData[i] = orders[i].data._decodeData(orderHash);
            makerData[i] = _decodeData(orders[i].data, orderHash);
            // Set the balance in the makerData
            makerData[i].takerTokenBalance_before = IERC20(orders[i].takerToken).balanceOf(orders[i].maker);

            // We are calling this with doGetActualFillableMakerAmount = false as a gas optimization
            // and with doRevertOnFailure = true because we expect it to revert if the order is not fillable
            // We don't care about making that extra gas consuming calls
            // The only reason we're calling this function, is to validate the order
            _validateAndGetOrderRelevantStatus(orders[i], orderHash, makerData[i], true, false);

            // Transfer makers makerToken to the keeper to begin trade execution
            IERC20(orders[i].makerToken).safeTransferFrom(orders[i].maker, makerTokenRecipient, makerAmountsToSpend[i]);

            // Update makerAmountFilled now that the makerTokens have been spent
            // The tx will revert if the maker isn't paid back takerTokens based on the order they signed
            _updateMakerAmountFilled(
                orders[i].makerAmount,
                orderHash,
                makerAmountsToSpend[i],
                makerData[i].partiallyFillable
            );

            // Gas optimization
            unchecked
            {
                ++i;
            }
        }
    }

    /// @dev Begin executing swap for DexAgg Keeper by executing the swap's calldata on the DexAgg.
    /// Also calculate the amount of tokens we need to distribute.
    function _beginDexAggSwapExecution(
        LibSwap.DexAggSwap calldata swap,
        LibSwap.MetaData calldata metaData
    )
        private
        returns (uint256 takerTokenAmountToDistribute)
    {
        // Begin swap execution by executing the swap on the DexAgg
        takerTokenAmountToDistribute = _dexAggKeeperSwap(swap, metaData);

        if (metaData.surplusTokenIsSwapTakerToken)
        {
            // With regards to a custom takerToken distribution
            // SurplusAmount could be extracted both from the makerToken and takerToken of a batched swap
            // Example: from the makerToken of User1's swap and the takerToken of User2's swap.
            // In this case User1 and User2 are sharing the tx gas fee.
            // So in this case, surplusAmountWithheld is only a fraction of the entire tx gas fee
            // And that's okay because this logic doesn't care about the other fraction of the tx gas fee
            // that was extracted at the beginning of the tx

            // Deduct the txs gas fee from the takerTokenAmountToDistribute because it's the surplusToken
            takerTokenAmountToDistribute = takerTokenAmountToDistribute - metaData.surplusAmountWithheld;
        }
    }

    /// @dev Complete DexAgg Keeper swap execution by distributing the takerTokens evenly among all makers in the batch.
    /// This function supports on chain positive slippage by utilizing the makerWeights and some simple math.
    function _completeDexAggSwapExecution_takerTokenDistribution_even(
        Order[] calldata orders,
        uint256[] calldata makerWeights,
        uint256 takerTokenAmountToDistribute
    )
        private
    {
        uint256 ordersLength = orders.length;
        // Transfer takerToken to maker to complete trade
        // If statement here because we can save gas by not doing math if there's only 1 order in the batch
        // Otherwise we have to spend some gas on calculating the positive slippage for each user
        if (ordersLength == 1)
        {
            IERC20(orders[0].takerToken).safeTransfer(orders[0].maker, takerTokenAmountToDistribute);
        }
        else
        {
            // Determine how much to transfer to each maker in the batch
            for (uint256 i; i < ordersLength;)
            {
                IERC20(orders[i].takerToken).safeTransfer(orders[i].maker, takerTokenAmountToDistribute * makerWeights[i] / 1000000000000000000);

                // Gas optimization
                unchecked
                {
                    ++i;
                }
            }
        }
    }

    /// @dev Complete DexAgg Keeper swap execution by distributing the takerTokens customly among all makers in the batch.
    /// This function does NOT support on chain positive slippage as the math is determined off chain ahead of time.
    function _completeDexAggSwapExecution_takerTokenDistribution_custom(
        Order[] calldata orders,
        uint256[] calldata takerTokenDistributions
    )
        private
    {
        // For all of our takerTokenDistributions,
        // Transfer takerToken to maker to complete trade

        // This function should only be called with 2 or more orders
        // If only 1 order is being processed, use evenTakerTokenDistribution instead
        uint256 ordersLength = orders.length;
        require(
            ordersLength > 1,
            "RS:E13"
        );

        // for every order, we must have an takerTokenDistribution
        require(
            ordersLength == takerTokenDistributions.length,
            "RS:E14"
        );

        for (uint256 i; i < ordersLength;)
        {
            IERC20(orders[i].takerToken).safeTransfer(orders[i].maker, takerTokenDistributions[i]);

            // Gas optimization
            unchecked
            {
                ++i;
            }
        }
    }

    /// @dev Finalize swap execution by doing some math, verifying that each maker got paid, and emitting events.
    function _finalizeSwapExecution(
        Order[] calldata orders,
        uint256[] calldata makerAmountsToSpend,
        LibData.MakerData[] memory makerData,
        address taker
    )
        private
    {
        // Require that all of the maker's swaps have been satisfied based on the order they signed
        for (uint256 i; i < orders.length;)
        {
            // Measure maker's post-trade balance
            makerData[i].takerTokenBalance_after = IERC20(orders[i].takerToken).balanceOf(orders[i].maker);

            // Validate order requirements
            uint256 takerAmountFilled = makerData[i].takerTokenBalance_after - makerData[i].takerTokenBalance_before;

            // Ensure the fill meets the maker's signed requirement
            // Gas optimization
            // if takerAmountDecayRate is zero, we can save gas by not calling _calculateCurrentTakerAmountMin
            // otherwise we must perform some extra calculations to determine currentTakerAmountMin
            uint256 currentTakerAmountMin =
                orders[i].takerAmountDecayRate == 0 ?
                orders[i].takerAmountMin :
                _calculateCurrentTakerAmountMin(
                    orders[i].takerAmountMin,
                    orders[i].takerAmountDecayRate, makerData[i]
                );
            if (makerData[i].partiallyFillable)
            {
                // If the order is partiallyFillable, we have to slightly alter our math to support checking this properly
                // We must factor in the ratio of the makerAmount we're actually spending against the order's full makerAmount
                // This is because the _calculateCurrentTakerAmountMin is always in terms of the order's full amount
                // OPTIMIZATION: I could store this in a variable to make the code cleaner, but that costs more gas
                // So I'm in-lining all this math to save on gas
                require(
                    takerAmountFilled * orders[i].makerAmount >= currentTakerAmountMin * makerAmountsToSpend[i],
                    "RS:E23"
                );
            }
            else
            {
                require(
                    takerAmountFilled >= currentTakerAmountMin,
                    "RS:E24"
                );
            }

            // Log the fill event
            emit Fill(
                orders[i].maker,
                taker,
                orders[i].makerToken,
                orders[i].takerToken,
                makerAmountsToSpend[i],
                takerAmountFilled,
                makerData[i].orderHash
            );

            // Gas optimization
            unchecked
            {
                ++i;
            }
        }
    }

    /// @dev Finalize swap execution for the DexAgg Keeper by ensuring that this contract didn't lose value
    /// and that all thresholds were satisfied.
    function _finalizeSwapExecution_dexAggKeeper(
        LibData.ContractData memory contractData,
        LibSwap.MetaData calldata metaData
    )
        private
        view
        returns (uint256 surplusAmount)
    {
        // Measure post-trade balances
        contractData.surplusTokenBalance_after = IERC20(metaData.surplusToken).balanceOf(address(this));
        // Gas optimization
        // not setting a variable here since we only use it once
        // contractData.otherTokenBalance_after = IERC20(metaData.otherToken).balanceOf(address(this));

        // Require that the DexAggKeeper has been satisfied
        // Revert if the DexAggKeeper has lost value in surplusTokens or otherTokens
        // We expect to gain surplus in surplusTokens by metaData.surplusProtectionThreshold to cover the cost of gas and other fees
        // But we do not expect otherToken to increase
        // This protection is required so that we don't need to trust the DexAggs's calldata nearly as much

        // surplusToken must increase based on metaData.surplusProtectionThreshold, and should never decrease
        require(
            contractData.surplusTokenBalance_after >= (contractData.surplusTokenBalance_before + metaData.surplusProtectionThreshold),
            "RS:E25"
        );

        // otherToken must at least break even
        // Typically this balance will not increase, break even is normal
        require(
            IERC20(metaData.otherToken).balanceOf(address(this)) >= contractData.otherTokenBalance_before,
            "RS:E26"
        );

        surplusAmount = contractData.surplusTokenBalance_after - contractData.surplusTokenBalance_before;
    }

    /// @dev Calculate the order's current takerAmountMin at this point in time.
    /// The takerAmountDecayRate behaves like a dutch auction, as the takerAmount decays over time down to the takerAmountMin.
    /// Setting the takerAmountDecayRate to zero disables this decay feature and the swapping price remains static.
    /// Ideally, if takerAmountDecayRate is zero you don't even have to call this function because it just returns takerAmountMin.
    function _calculateCurrentTakerAmountMin(
        uint256 takerAmountMin,
        uint256 takerAmountDecayRate,
        LibData.MakerData memory makerData
    )
        private
        view
        returns (uint256 currentTakerAmountMin)
    {
        // Saving gas by not creating variables for these
        // Leaving commented out variables here to help with readability
        // uint256 elapsedTime = block.timestamp - makerData.begin;
        // uint256 totalTime = makerData.expiry - makerData.begin;
        // uint256 timestamp = block.timestamp >= makerData.begin ? block.timestamp : makerData.begin;
        // uint256 multiplier  = block.timestamp < makerData.expiry ? makerData.expiry - timestamp: 0;
        // currentTakerAmountMin = takerAmountMin + (takerAmountDecayRate * multiplier);

        // Gas optimization
        // Saving gas by not creating variables for any of this.
        // The code is increidbly hard to read, but it saves a lot of gas
        // The more readable version of the code is commented out above
        currentTakerAmountMin =
            takerAmountMin + (takerAmountDecayRate * (
                block.timestamp < makerData.expiry
                    ?
                    makerData.expiry - (
                        block.timestamp >= makerData.begin
                            ?
                            block.timestamp
                            :
                            makerData.begin
                        )
                    :
                    0
                )
            );
    }

    /// @dev Execute the swap calldatas on the DexAggs. Also manage allowances to the DexAggs.
    function _dexAggKeeperSwap(
        LibSwap.DexAggSwap memory swap,
        LibSwap.MetaData calldata metaData
    )
        private
        returns (uint256 swapOutput)
    {
        // Execute all requried allowance approvals before swapping
        // We will assume that the function caller knows which tokens need approved and which do not
        // We should be approving the token we're spending inside the swap.callData, if we don't this tx will likely revert
        // So it's up to the function caller to set this properly or else reverts can happen due to no allowance
        require(
            swap.approveToken != address(0),
            "RS:E19"
        );

        // approveToken must be either surplusToken or otherToken, to prevent arbitrary token allowance approvals
        require(
            swap.approveToken == metaData.surplusToken || swap.approveToken == metaData.otherToken,
            "RS:E32"
        );

        // Approve exactly how much we intend to swap on the DexAgg
        IERC20(swap.approveToken).approve(swap.router, swap.approvalAmount);

        // Execute the calldata
        (bool success, bytes memory returnData) = swap.router.call{ value: 0 }(swap.callData);
        _verifyCallResult(success, returnData, "callData execution failed");
        swapOutput = returnData.toUint256(0);

        // Revoke the DexAgg's allowance now that the swap has finished
        IERC20(swap.approveToken).approve(swap.router, 0);
    }

    /// @dev Verify the result of a call
    /// This function reverts and bubbles up a revert reason if there's a problem
    /// The return value doesn't matter, the function caller should already have the call's result
    function _verifyCallResult(
        bool success,
        bytes memory returnData,
        string memory errorMessage
    )
        private
        pure
    {
        if (success)
        {
            // Nothing needs done, just return
            return;
        }
        else
        {
            // Look for revert reason and bubble it up if present
            if (returnData.length != 0)
            {
                // The easiest way to bubble the revert reason is using memory via assembly
                assembly
                {
                    let returnData_size := mload(returnData)
                    revert(add(32, returnData), returnData_size)
                }
            }
            else
            {
                revert(errorMessage);
            }
        }
    }
}

File 2 of 15 : reentrancyGuard.sol
// SPDX-License-Identifier: MIT

// Vendored from OpenZeppelin contracts with minor modifications:
// - Modified Solidity version
// - Formatted code
// <https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.4.0/contracts/utils/ReentrancyGuard.sol>

pragma solidity 0.8.16;

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

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

    uint256 private _status;

    constructor()
    {
        _status = _NOT_ENTERED;
    }

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

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

        _;

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

File 3 of 15 : owner.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;

contract Owner
{
    /// @dev Current owner of this contract.
    address owner;

    /// @dev Pending owner of this contract. Set when an ownership transfer is initiated.
    address pendingOwner;

    /// @dev Event emitted when an ownership transfer is initiated.
    event OwnershipTransferInitiated(
        address indexed previousOwner,
        address indexed newOwner
    );

    /// @dev Event emmitted when ownership transfer has completed.
    event OwnershipTransferCompleted(
        address indexed previousOwner,
        address indexed newOwner
    );

    constructor()
    {
        owner = msg.sender;
        emit OwnershipTransferCompleted(address(0), owner);
    }

    modifier onlyOwner()
    {
        require(
            owner == msg.sender,
            "RS:E17"
        );
        _;
    }

    /// @dev Initiates ownership transfer by setting pendingOwner.
    function transferOwnership(
        address newOwner
    )
        external
        onlyOwner
    {
        require(
            newOwner != address(0),
            "RS:E1"
        );

        pendingOwner = newOwner;
        emit OwnershipTransferInitiated(owner, newOwner);
    }

    /// @dev Allows pendingOwner to claim ownership.
    function acceptOwnership(
    )
        external
    {
        require(
            pendingOwner == msg.sender,
            "RS:E17"
        );

        _transferOwnership(msg.sender);
    }

    /// @dev Completes ownership transfer.
    function _transferOwnership(
        address newOwner
    )
        internal
    {
        address oldOwner = owner;
        owner = newOwner;
        emit OwnershipTransferCompleted(oldOwner, newOwner);
        delete pendingOwner;
    }
}

File 4 of 15 : whitelist.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.16;

import "./owner.sol";

/// @dev Type of whitelist
enum WhitelistType
{
    Keeper,
    DexAggKeeper,
    DexAggRouter
}

/// @dev Data specific to a particular whitelist
struct WhitelistData
{
    // Array of whitelisted addresses
    address [] whitelistedAddressArray;
    // Keyed by whitelisted address, valued by position in whitelistedAddressArray
    mapping(address => uint256) whitelistedAddress;
}

/// @dev Whitelist library containing logic resuable for all whitelists
library LibWhitelist
{
    /// @dev Event emitting when a new address has been whitelisted
    event WhitelistEvent(
        address keeper,
        bool whitelisted,
        WhitelistType indexed whitelistType
    );

    /// @dev add addresses to the whitelist
    function _addToWhitelist(
        WhitelistData storage whitelist,
        address[] memory addresses,
        WhitelistType whitelistType
    )
        internal
    {
        uint256 size = addresses.length;
        for (uint256 i = 0; i < size; i++)
        {
            address keeper = addresses[i];
            // Get the position of the address in the whitelist
            uint256 keeperPosition = whitelist.whitelistedAddress[keeper];

            // If it's currently whitelisted
            if (keeperPosition != 0)
            {
                // Skip it and emit the event again for reassurance
                emit WhitelistEvent(keeper, true, whitelistType);
                continue;
            }
            // Otherwise, it's not currently whitelisted
            // Get the position of the last whitelisted address
            uint256 position = whitelist.whitelistedAddressArray.length;
            // Store the new whitelisted address and position + 1 in the mapping and array
            whitelist.whitelistedAddress[keeper] = position + 1;
            whitelist.whitelistedAddressArray.push(keeper);

            emit WhitelistEvent(keeper, true, whitelistType);
        }
    }

    /// @dev remove addresses from the whitelist
    function _removeFromWhitelist(
        WhitelistData storage whitelist,
        address[] memory addresses,
        WhitelistType whitelistType
    )
        internal
    {
        uint256 size = addresses.length;
        for (uint256 i = 0; i < size; i++)
        {
            address keeper = addresses[i];
            // Get the position of the address in the whitelist
            uint256 keeperPosition = whitelist.whitelistedAddress[keeper];

            // If it's not currently whitelisted
            if (keeperPosition == 0)
            {
                // Skip it and emit the event again for reassurance
                emit WhitelistEvent(keeper, false, whitelistType);
                continue;
            }
            // Otherwise, it's currently whitelisted
            // We need to remove the keeper from the array
            // We know that keeper is in the position keeperPosition
            // Get the length of the array
            uint256 lastKeeperPosition = whitelist.whitelistedAddressArray.length - 1;
            // Get the address stored in lastKeeperPosition
            address lastKeeper = whitelist.whitelistedAddressArray[lastKeeperPosition];

            // Set the new lastKeeperPosition
            // Remember that we store position increased by 1 in the mapping
            whitelist.whitelistedAddressArray[keeperPosition - 1] = whitelist.whitelistedAddressArray[lastKeeperPosition];

            // Update the mapping with the new position of the lastKeeper
            whitelist.whitelistedAddress[lastKeeper] = keeperPosition;
            // Update the mapping with zero as the new position of the removed keeper
            whitelist.whitelistedAddress[keeper] = 0;

            // Pop the last element of the array
            whitelist.whitelistedAddressArray.pop();

            emit WhitelistEvent(keeper, false, whitelistType);
        }
    }
}

/// @dev Manages all whitelists on this contract
contract Whitelist is Owner
{
    using LibWhitelist for WhitelistData;

    /// @dev Whitelist for Keepers
    WhitelistData keeperWhitelist;
    /// @dev Whitelist for DexAgg Keepers
    WhitelistData dexAggKeeperWhitelist;
    /// @dev Whitelist for DexAgg Routers
    WhitelistData dexAggRouterWhitelist;

    /// @dev The address of the next whitelist in the link.
    address public nextLinkedWhitelist;

    constructor()
    {
        // initialize as the null address, meaning this is the newest address in the chain
        nextLinkedWhitelist = address(0);
    }

    /// @dev Set the next whitelist in the link
    function setNextLinkedWhitelist(
        address newNextLinkedWhitelist
    )
        external
        onlyOwner
    {
        nextLinkedWhitelist = newNextLinkedWhitelist;
    }

    /// @dev Get current keeper whitelist
    function getKeeperWhitelist()
        public
        view
        returns (address[] memory)
    {
        return keeperWhitelist.whitelistedAddressArray;
    }

    /// @dev Get current dex agg keeper whitelist
    function getDexAggKeeperWhitelist()
        public
        view
        returns (address[] memory)
    {
        return dexAggKeeperWhitelist.whitelistedAddressArray;
    }

    /// @dev Get current dex agg router whitelist
    function getDexAggRouterWhitelist()
        public
        view
        returns (address[] memory)
    {
        return dexAggRouterWhitelist.whitelistedAddressArray;
    }

    /// @dev Get the position of this address in the whitelist.
    /// If this address is not whitelisted, it will return a zero.
    function getKeeperWhitelistPosition__2u3w(
        address keeper
    )
        public
        view
        returns (uint256 keeperWhitelistPosition)
    {
        keeperWhitelistPosition = keeperWhitelist.whitelistedAddress[keeper];
    }

    /// @dev Get the position of this address in the whitelist.
    /// If this address is not whitelisted, it will return a zero.
    function getDexAggKeeperWhitelistPosition_IkFc(
        address keeper
    )
        public
        view
        returns (uint256 dexAggKeeperWhitelistPosition)
    {
        dexAggKeeperWhitelistPosition = dexAggKeeperWhitelist.whitelistedAddress[keeper];
    }

    /// @dev Get the position of this address in the whitelist.
    /// If this address is not whitelisted, it will return a zero.
    function getDexAggRouterWhitelistPosition_ZgLC(
        address router
    )
        public
        view
        returns (uint256 dexAggRouterWhitelistPosition)
    {
        dexAggRouterWhitelistPosition = dexAggRouterWhitelist.whitelistedAddress[router];
    }

    /// @dev Update the whitelist status of these addresses
    function whitelistKeepers(
        address[] memory addresses,
        bool value
    )
        external
        onlyOwner
    {
        if (value)
        {
            keeperWhitelist._addToWhitelist(addresses, WhitelistType.Keeper);
        }
        else
        {
            keeperWhitelist._removeFromWhitelist(addresses, WhitelistType.Keeper);
        }
    }

    /// @dev Update the whitelist status of these addresses
    function whitelistDexAggKeepers(
        address[] memory addresses,
        bool value
    )
        external
        onlyOwner
    {
        if (value)
        {
            dexAggKeeperWhitelist._addToWhitelist(addresses, WhitelistType.DexAggKeeper);
        }
        else
        {
            dexAggKeeperWhitelist._removeFromWhitelist(addresses, WhitelistType.DexAggKeeper);
        }
    }

    /// @dev Update the whitelist status of these addresses
    function whitelistDexAggRouters(
        address[] memory addresses,
        bool value
    )
        external
        onlyOwner
    {
        if (value)
        {
            dexAggRouterWhitelist._addToWhitelist(addresses, WhitelistType.DexAggRouter);
        }
        else
        {
            dexAggRouterWhitelist._removeFromWhitelist(addresses, WhitelistType.DexAggRouter);
        }
    }
}

File 5 of 15 : assetManagement.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.16;

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

import "./owner.sol";

contract AssetManagement is
    Owner
{
    using Address for address;
    using SafeERC20 for IERC20;

    /// @dev Event emmitted when assets are withdrawn.
    event Withdraw(
        address asset,
        uint256 amount
    );

    /// @dev Withdraw Ether from this contract.
	function withdrawEther_wEuX(
	    uint256 amount
    )
        external
        onlyOwner
    {
        (bool success, ) = msg.sender.call{value: amount}("");

        require(
            success,
            "RS:E0"
        );

        emit Withdraw(address(0), amount);
	}

    /// @dev Withdraw tokens from this contract.
	function withdrawToken_14u2(
	    address token,
        uint256 amount
    )
        external
        onlyOwner
    {
        require(
            token != address(0),
            "RS:E1"
        );

        IERC20(token).safeTransfer(msg.sender, amount);
        emit Withdraw(token, amount);
	}

    /// @dev Manually set allowance for a given token & spender pair.
    /// Allowances for this contract are managed via DexAggSwaps,
    /// but any manual intervention is performed with this function.
    function manuallySetAllowances(
        address spender,
        IERC20[] memory tokens,
        uint256[] memory values
    )
        external
        onlyOwner
    {
        require(
            spender != address(0),
            "RS:E1"
        );
        for (uint256 i; i < tokens.length;)
        {
            require(
                address(tokens[i]) != address(0),
                "RS:E1"
            );
            tokens[i].approve(spender, values[i]);

            // Gas optimization
            unchecked
            {
                ++i;
            }
        }
    }
}

File 6 of 15 : orderUtils.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.16;

import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/math/Math.sol";

import "./signing.sol";
import "./utils.sol";

contract OrderUtils is
    Signing
{
    /// @dev Highest bit of a uint256, used to flag cancelled orders.
    uint256 private constant HIGH_BIT = 1 << 255;

    /// @dev Keyed by orderHash, valued by order's filled amount in makerTokens paid by the maker.
    mapping(bytes32 => uint256) public makerAmountFilled;

    /// @dev Order data containing a signed commitment a user made to swap tokens.
    struct Order
    {
        // items contained within TYPEHASH_ORDER
        address maker;
        address makerToken;
        address takerToken;
        uint256 makerAmount;
        uint256 takerAmountMin;
        uint256 takerAmountDecayRate;
        uint256 data;
        // items NOT contained within TYPEHASH_ORDER
        bytes signature;
    }

    /// @dev Status of an order depending on various events that play out.
    enum OrderStatus
    {
        Invalid,
        Fillable,
        Filled,
        Canceled,
        Expired
    }

    /// @dev Info about an order's status and general fillability.
    struct OrderInfo
    {
        bytes32 orderHash;
        OrderStatus status;
        uint256 makerFilledAmount;
    }

    /// @dev Event emitted when an order is filled.
    event Fill(
        address maker,
        address taker,
        address makerToken,
        address takerToken,
        uint256 makerAmountFilled,
        uint256 takerAmountFilled,
        bytes32 orderHash
    );

    /// @dev Event emitted when an order is canceled.
    event OrderCancelled(
        bytes32 orderHash,
        address maker
    );

    constructor()
    {
    }

    /// @dev Update the makerAmountFilled for the order being processed in a swap.
    function _updateMakerAmountFilled(
        uint256 makerAmount,
        bytes32 orderHash,
        uint256 makerAmountToSpend,
        bool partiallyFillable
    )
        internal
    {
        // Update the fillAmount to prevent replay attacks
        // differentiate between partial fills and not allowing partial fills
        if (partiallyFillable)
        {
            uint256 newMakerAmountFilled = makerAmountFilled[orderHash] + makerAmountToSpend;
            // newMakerAmountFilled must be valid
            require(
                newMakerAmountFilled <= makerAmount,
                "RS:E2"
            );
            makerAmountFilled[orderHash] = newMakerAmountFilled;
        }
        else
        {
            // makerAmount must be valid
            require(
                makerAmountToSpend == makerAmount,
                "RS:E3"
            );
            // order must not already be filled
            require(
                makerAmountFilled[orderHash] == 0,
                "RS:E4"
            );
            // Since partial fills are not allowed, we must set this to the order's full amount
            makerAmountFilled[orderHash] = makerAmount;
        }
    }

    /// @dev Get relevant order information to determine fillability of many orders.
    function getOrderRelevantStatuses(
        Order[] calldata orders
    )
        external
        view
        returns (
            OrderInfo[] memory orderInfos,
            uint256[] memory makerAmountsFillable,
            bool[] memory isSignatureValids
        )
    {
        uint256 ordersLength = orders.length;
        orderInfos = new OrderInfo[](ordersLength);
        makerAmountsFillable = new uint256[](ordersLength);
        isSignatureValids = new bool[](ordersLength);
        for (uint256 i; i < ordersLength;)
        {
            // try/catches can only be used for external funciton calls
            try
                this.getOrderRelevantStatus(orders[i])
                    returns (
                        OrderInfo memory orderInfo,
                        uint256 makerAmountFillable,
                        bool isSignatureValid
                    )
            {
                orderInfos[i] = orderInfo;
                makerAmountsFillable[i] = makerAmountFillable;
                isSignatureValids[i] = isSignatureValid;
            }
            catch {}

            // Gas optimization
            unchecked
            {
                ++i;
            }
        }
    }

    /// @dev Get relevant order information to determine fillability of an order.
    // This function must be public because it's being called in a try catch above.
    function getOrderRelevantStatus(
        Order calldata order
    )
        external
        view
        returns (
            OrderInfo memory orderInfo,
            uint256 makerAmountFillable,
            bool isSignatureValid
        )
    {
        bytes32 orderHash = getOrderHash(order);
        LibData.MakerData memory makerData = _decodeData(order.data, orderHash);
        return _validateAndGetOrderRelevantStatus(order, orderHash, makerData, false, true);
    }

    /// @dev Validate an order's signature and get relevant order information to determine fillability of an order.
    /// Depending on what's calling this function, we may want to revert on a failure.
    /// For example, if we are swapping and something bad happens
    ///     we absolutely want to revert
    /// But, if we are simply making an off chain call to check the order's status and we see a bad status
    ///     we do NOT want to revert because this is critical information we want to return to the off chain function caller
    /// Or we may want to provide additional data (or not to save on gas cost).
    function _validateAndGetOrderRelevantStatus(
        Order calldata order,
        bytes32 orderHash,
        LibData.MakerData memory makerData,
        bool doRevertOnFailure,
        bool doGetActualFillableMakerAmount
    )
        internal
        view
        returns (
            OrderInfo memory orderInfo,
            uint256 makerAmountFillable,
            bool isSignatureValid
        )
    {
        // Tokens must be different
        require(
            order.makerToken != order.takerToken,
            "RS:E5"
        );

        // Set the various parts of orderInfo
        orderInfo.orderHash = orderHash;
        orderInfo.makerFilledAmount = makerAmountFilled[orderInfo.orderHash];
        // Determine orderInfo.status
        // The high bit will be set if the order was cancelled
        if (orderInfo.makerFilledAmount & HIGH_BIT != 0)
        {
            orderInfo.status = OrderStatus.Canceled;
        }
        // If the order has already been filled to or over the max
        else if (orderInfo.makerFilledAmount >= order.makerAmount)
        {
            orderInfo.status = OrderStatus.Filled;
        }
        // Check for expiration
        else if (makerData.expiry <= block.timestamp)
        {
            orderInfo.status = OrderStatus.Expired;
        }
        else
        {
            // If we've made it this far, the order is fillable
            orderInfo.status = OrderStatus.Fillable;
        }

        // Validate order status
        // So I have this here that will revert if it's not fillable, but i don't think i'm verifying that it's filled properly.
        // For example, right now you can doulbe fill an order. I dont think there's anything stopping that
        require(
            !doRevertOnFailure || orderInfo.status == OrderStatus.Fillable,
            "RS:E6"
        );

        // Do not calculate makerAmountFillable internally when swapping,
        // only calculate it when making external calls checking the status of orders
        // This is critical because external parties care about this information
        // but when swapping tokens we do not, and not calling this saves a lot of gas
        // If when swapping tokens, the transaction were to fail because someone doesn't have an allowance,
        // we just let it fail and bubble up an exception elsewhere, this is a great gas optimization
        if (doGetActualFillableMakerAmount)
        {
            makerAmountFillable = _getMakerAmountFillable(order, orderInfo);
        }

        // Validate order signature against the signer
        address signer = _recoverOrderSignerFromOrderHash(orderInfo.orderHash, makerData.signingScheme, order.signature);

        // Order signer must be either the order's maker or the maker's valid signer
        // Gas optimization
        // We fist compare the order.maker and signer, before considering calling isValidOrderSigner()
        // isValidOrderSigner will read from storage which incurs a large gas cost
        isSignatureValid =
            signer != address(0) &&
            (
                (order.maker == signer) ||
                isValidOrderSigner(order.maker, signer)
            );

        require(
            !doRevertOnFailure || isSignatureValid,
            "RS:E7"
        );
    }

    /// @dev Calculate the actual order fillability based on maker allowance, balances, etc
    function _getMakerAmountFillable(
        Order calldata order,
        OrderInfo memory orderInfo
    )
        private
        view
        returns (uint256 makerAmountFillable)
    {
        if (orderInfo.status != OrderStatus.Fillable)
        {
            // Not fillable
            return 0;
        }
        if (order.makerAmount == 0)
        {
            // Empty order
            return 0;
        }

        // It is critical to have already returned above if the order is NOT fillable
        // because certain statuses like the canceled status modifies the makerFilledAmount value
        // which would mess up the below logic.
        // So we must not proceed with the below logic if any bits in makerFilledAmount
        // have been set by order cancels or something similiar

        // Get the fillable maker amount based on the order quantities and previously filled amount
        makerAmountFillable = order.makerAmount - orderInfo.makerFilledAmount;

        // Clamp it to the amount of maker tokens we can spend on behalf of the maker
        makerAmountFillable = Math.min(
            makerAmountFillable,
            _getSpendableERC20BalanceOf(IERC20(order.makerToken), order.maker)
        );
    }

    /// @dev Get spendable balance considering allowance.
    function _getSpendableERC20BalanceOf(
        IERC20 token,
        address owner
    )
        internal
        view
        returns (uint256 spendableERC20BalanceOf)
    {
        spendableERC20BalanceOf = Math.min(
            token.allowance(owner, address(this)),
            token.balanceOf(owner)
        );
    }

    /// @dev Decode order data into its individual components.
    function _decodeData(
        uint256 data,
        bytes32 orderHash
    )
        internal
        pure
        returns (LibData.MakerData memory makerData)
    {
        // Bits
        // 0 -> 63    = begin
        // 64 -> 127  = expiry
        // 128        = partiallyFillable
        // 129 -> 130 = signingScheme
        // 131 -> ... = reserved, must be zero

        uint256 begin = uint256(uint64(data));
        uint256 expiry = uint256(uint64(data >> 64));
        bool partiallyFillable = data & 0x100000000000000000000000000000000 != 0;
        // NOTE: Take advantage of the fact that Solidity will revert if the
        // following expression does not produce a valid enum value. This means
        // we check here that the leading reserved bits must be 0.
        LibSignatures.Scheme signingScheme = LibSignatures.Scheme(data >> 129);

        // Do not allow orders where begin comes after expiry
        // This doesn't make sense on a UI/UX level and leads to exceptions with our logic
        require(
            expiry >= begin,
            "RS:E27"
        );

        // Measure maker's pre-trade balance
        makerData = LibData.MakerData(
            orderHash,
            0,
            0,
            begin,
            expiry,
            partiallyFillable,
            signingScheme
        );
    }

    /// @dev Cancel multiple orders. The caller must be the maker or a valid order signer.
    /// Silently succeeds if the order has already been cancelled.
    function cancelOrders__tYNw(
        Order[] calldata orders
    )
        external
    {
        for (uint256 i; i < orders.length;)
        {
            // Must be either the order's maker or the maker's valid signer
            if (orders[i].maker != msg.sender &&
                !isValidOrderSigner(orders[i].maker, msg.sender))
            {
                revert("RS:E20");
            }

            bytes32 orderHash = getOrderHash(orders[i]);
            // Set the high bit on the makerAmountFilled to indicate a cancel.
            // It's okay to cancel twice.
            makerAmountFilled[orderHash] |= HIGH_BIT;
            emit OrderCancelled(orderHash, orders[i].maker);

            // Gas optimization
            unchecked
            {
                ++i;
            }
        }
    }
}

File 7 of 15 : utils.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.16;

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

library LibSignatures
{
    /// @dev Signing scheme used for recovering signers from signatures
    enum Scheme
    {
        Eip712,
        EthSign,
        Eip1271,
        PreSign
    }
}

library LibData
{
    /// @dev Data specific to this contract.
    struct ContractData
    {
        uint256 surplusTokenBalance_before;
        uint256 surplusTokenBalance_after;
        uint256 otherTokenBalance_before;
    }

    /// @dev Data specific to a maker.
    struct MakerData
    {
        // Params we calculate
        bytes32 orderHash;
        uint256 takerTokenBalance_before;
        uint256 takerTokenBalance_after;
        // Params extracted from order.data
        uint256 begin;
        uint256 expiry;
        bool partiallyFillable;
        LibSignatures.Scheme signingScheme;
    }
}

library LibSwap
{
    /// @dev DexAgg swap calldata.
    struct DexAggSwap
    {
        address router;
        bytes callData;
        address approveToken;
        uint256 approvalAmount;
    }

    /// @dev Metadata regarding the swap and how to handle surplus
    struct MetaData
    {
        address surplusToken;
        uint256 surplusAmountWithheld;
        address otherToken;
        bool surplusTokenIsSwapTakerToken;
        TakerTokenDistributionType takerTokenDistributionType;
        uint256 surplusProtectionThreshold;
    }

    /// @dev How to handle swap takerToken distribution
    enum TakerTokenDistributionType
    {
        Even,
        Custom
    }
}

library LibBytes
{
    /// @dev Convert bytes to uint256
    function toUint256(
        bytes memory bytesToConvert,
        uint256 start
    )
        internal
        pure
        returns (uint256 convertedInt)
    {
        require(
            bytesToConvert.length >= start + 32,
            "RS:E11"
        );
        assembly
        {
            convertedInt := mload(add(add(bytesToConvert, 0x20), start))
        }
    }
}

File 8 of 15 : ECDSA.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (utils/cryptography/ECDSA.sol)

pragma solidity ^0.8.0;

import "../Strings.sol";

/**
 * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
 *
 * These functions can be used to verify that a message was signed by the holder
 * of the private keys of a given address.
 */
library ECDSA {
    enum RecoverError {
        NoError,
        InvalidSignature,
        InvalidSignatureLength,
        InvalidSignatureS,
        InvalidSignatureV
    }

    function _throwError(RecoverError error) private pure {
        if (error == RecoverError.NoError) {
            return; // no error: do nothing
        } else if (error == RecoverError.InvalidSignature) {
            revert("ECDSA: invalid signature");
        } else if (error == RecoverError.InvalidSignatureLength) {
            revert("ECDSA: invalid signature length");
        } else if (error == RecoverError.InvalidSignatureS) {
            revert("ECDSA: invalid signature 's' value");
        } else if (error == RecoverError.InvalidSignatureV) {
            revert("ECDSA: invalid signature 'v' value");
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature` or error string. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     *
     * Documentation for signature generation:
     * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
     * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
     *
     * _Available since v4.3._
     */
    function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
        // Check the signature length
        // - case 65: r,s,v signature (standard)
        // - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098) _Available since v4.1._
        if (signature.length == 65) {
            bytes32 r;
            bytes32 s;
            uint8 v;
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            assembly {
                r := mload(add(signature, 0x20))
                s := mload(add(signature, 0x40))
                v := byte(0, mload(add(signature, 0x60)))
            }
            return tryRecover(hash, v, r, s);
        } else if (signature.length == 64) {
            bytes32 r;
            bytes32 vs;
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            assembly {
                r := mload(add(signature, 0x20))
                vs := mload(add(signature, 0x40))
            }
            return tryRecover(hash, r, vs);
        } else {
            return (address(0), RecoverError.InvalidSignatureLength);
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature`. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     */
    function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, signature);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
     *
     * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
     *
     * _Available since v4.3._
     */
    function tryRecover(
        bytes32 hash,
        bytes32 r,
        bytes32 vs
    ) internal pure returns (address, RecoverError) {
        bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
        uint8 v = uint8((uint256(vs) >> 255) + 27);
        return tryRecover(hash, v, r, s);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
     *
     * _Available since v4.2._
     */
    function recover(
        bytes32 hash,
        bytes32 r,
        bytes32 vs
    ) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, r, vs);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
     * `r` and `s` signature fields separately.
     *
     * _Available since v4.3._
     */
    function tryRecover(
        bytes32 hash,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal pure returns (address, RecoverError) {
        // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
        // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
        // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
        // signatures from current libraries generate a unique signature with an s-value in the lower half order.
        //
        // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
        // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
        // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
        // these malleable signatures as well.
        if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
            return (address(0), RecoverError.InvalidSignatureS);
        }
        if (v != 27 && v != 28) {
            return (address(0), RecoverError.InvalidSignatureV);
        }

        // If the signature is valid (and not malleable), return the signer address
        address signer = ecrecover(hash, v, r, s);
        if (signer == address(0)) {
            return (address(0), RecoverError.InvalidSignature);
        }

        return (signer, RecoverError.NoError);
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `v`,
     * `r` and `s` signature fields separately.
     */
    function recover(
        bytes32 hash,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal pure returns (address) {
        (address recovered, RecoverError error) = tryRecover(hash, v, r, s);
        _throwError(error);
        return recovered;
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from a `hash`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
        // 32 is the length in bytes of hash,
        // enforced by the type signature above
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from `s`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s));
    }

    /**
     * @dev Returns an Ethereum Signed Typed Data, created from a
     * `domainSeparator` and a `structHash`. This produces hash corresponding
     * to the one signed with the
     * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
     * JSON-RPC method as part of EIP-712.
     *
     * See {recover}.
     */
    function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
    }
}

File 9 of 15 : SafeERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "../IERC20.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));
        }
    }

    /**
     * @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 10 of 15 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.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 functionCall(target, data, "Address: low-level call failed");
    }

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

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

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

        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

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

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

        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

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

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

        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason 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 {
            // 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

                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

File 11 of 15 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

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

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

    /**
     * @dev Moves `amount` tokens from the caller's account to `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);

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

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

File 12 of 15 : signing.sol
// SPDX-License-Identifier: LGPL-3.0-or-later
pragma solidity 0.8.16;

import "./orderUtils.sol";
import "./eip1271.sol";
import "./utils.sol";

abstract contract Signing
{
    /// @dev Name of contract.
    string private constant CONTRACT_NAME = "Rook Swap";

    /// @dev Version of contract.
    string private constant CONTRACT_VERSION = "0.1.0";

    /// @dev The EIP-712 typehash for the contract's domain.
    bytes32 private constant TYPEHASH_DOMAIN = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");

    /// @dev The EIP-712 typehash for the Order struct.
    bytes32 private constant TYPEHASH_ORDER = keccak256("Order(address maker,address makerToken,address takerToken,uint256 makerAmount,uint256 takerAmountMin,uint256 takerAmountDecayRate,uint256 data)");

    /// @dev Storage indicating whether or not an orderHash has been pre signed
    mapping(bytes32 => bool) public preSign;

    /// @dev Event that is emitted when an account either pre-signs an order or revokes an existing pre-signature.
    event PreSign(
        bytes32 orderHash,
        bool signed
    );

    /// @dev The length of any signature from an externally owned account.
    uint256 private constant ECDSA_SIGNATURE_LENGTH = 65;

    /// @dev Domain separator.
    bytes32 immutable domainSeparator;

    /// @dev Contains a mapping of EOA signers who are registered to sign on behalf of a maker address.
    // Maker can be a contract, this enables any contract to be a maker even if they don't have a private key.
    // Keyed by maker and valued by mapping which is keyed by signer and valued by bool representing registration status.
    mapping(address => mapping(address => bool)) orderSignerRegistry;


    /// @dev Event emitted when a new registration is made to orderSignerRegistry.
    event OrderSignerRegistered(
        address maker,
        address signer,
        bool allowed
    );

    constructor()
    {
        domainSeparator = keccak256(
            abi.encode(
                TYPEHASH_DOMAIN,
                keccak256(bytes(CONTRACT_NAME)),
                keccak256(bytes(CONTRACT_VERSION)),
                _getChainId(),
                address(this)
            )
        );
    }

    /// @dev Gets the chainId.
    function _getChainId(
    )
        private
        view
        returns (uint256 chainId)
    {
        assembly
        {
            chainId := chainid()
        }
    }

    /// @dev Calculates an order's hash.
    function getOrderHash(
        OrderUtils.Order calldata order
    )
        public
        view
        returns (bytes32 orderHash)
    {
        orderHash = keccak256(
            abi.encodePacked(
                "\x19\x01",
                domainSeparator,
                keccak256(abi.encode(
                    TYPEHASH_ORDER,
                    order.maker,
                    order.makerToken,
                    order.takerToken,
                    order.makerAmount,
                    order.takerAmountMin,
                    order.takerAmountDecayRate,
                    order.data)
                )
            )
        );
    }

    /// @dev Recovers an order's signer from the specified order and signature.
    /// @param orderHash The computed order hash for recovering a signature.
    /// @param signingScheme The signing scheme.
    /// @param encodedSignature The signature bytes.
    /// @return signer The recovered address from the specified signature, or address(0) if signature is invalid (for 1271 and presign only).
    /// We are not reverting if signer == address(0) in this function, that responsibility is on the function caller
    function _recoverOrderSignerFromOrderHash(
        bytes32 orderHash,
        LibSignatures.Scheme signingScheme,
        bytes calldata encodedSignature
    )
        internal
        view
        returns (address signer)
    {
        if (signingScheme == LibSignatures.Scheme.Eip712)
        {
            signer = _ecdsaRecover(orderHash, encodedSignature);
        }
        else if (signingScheme == LibSignatures.Scheme.EthSign)
        {
            // The signed message is encoded as:
            // `"\x19Ethereum Signed Message:\n" || length || data`, where
            // the length is a constant (32 bytes) and the data is defined as:
            // `orderHash`.
            signer = _ecdsaRecover(
                keccak256(
                    abi.encodePacked(
                        "\x19Ethereum Signed Message:\n32",
                        orderHash
                    )
                ),
                encodedSignature);
        }
        else if (signingScheme == LibSignatures.Scheme.Eip1271)
        {
            // Use assembly to read the verifier address from the encoded
            // signature bytes.
            // solhint-disable-next-line no-inline-assembly
            assembly
            {
                // signer = address(encodedSignature[0:20])
                signer := shr(96, calldataload(encodedSignature.offset))
            }

            bytes calldata _signature = encodedSignature[20:];

            // Set signer to address(0) instead of reverting if isValidSignature fails.
            // We have to use a try/catch here in case the verifier's implementation of isValidSignature reverts when false
            // But we cannot rely only on that, because it may return a with a non 1271 magic number instead of reverting.
            try EIP1271Verifier(signer).isValidSignature(orderHash, _signature) returns (bytes4 magicValue)
            {
                // Check if isValidSignature return matches the 1271 magic value spec
                bool isValid = (magicValue == LibERC1271.MAGICVALUE);

                // If not, set signer to address(0)
                assembly
                {
                    let mask := mul(isValid, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
                    signer := and(signer, mask)
                }
            }
            catch
            {
                signer = address(0);
            }
        }
        else // signingScheme == Scheme.PreSign
        {
            assembly
            {
                // signer = address(encodedSignature[0:20])
                signer := shr(96, calldataload(encodedSignature.offset))
            }

            bool isValid = preSign[orderHash];

            assembly
            {
                let mask := mul(isValid, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
                signer := and(signer, mask)
            }
        }
        return signer;
    }

    /// @dev Perform an ECDSA recover for the specified message and calldata
    /// signature.
    ///
    /// The signature is encoded by tighyly packing the following struct:
    /// ```
    /// struct EncodedSignature {
    ///     bytes32 r;
    ///     bytes32 s;
    ///     uint8 v;
    /// }
    /// ```
    /// @param message The signed message.
    /// @param encodedSignature The encoded signature.
    /// @return signer The recovered address from the specified signature.
    function _ecdsaRecover(
        bytes32 message,
        bytes calldata encodedSignature
    )
        internal
        pure
        returns (address signer)
    {
        require(
            encodedSignature.length == ECDSA_SIGNATURE_LENGTH,
            "RS:E8"
        );

        bytes32 r;
        bytes32 s;
        uint8 v;

        // NOTE: Use assembly to efficiently decode signature data.
        // solhint-disable-next-line no-inline-assembly
        assembly
        {
            // r = uint256(encodedSignature[0:32])
            r := calldataload(encodedSignature.offset)
            // s = uint256(encodedSignature[32:64])
            s := calldataload(add(encodedSignature.offset, 32))
            // v = uint8(encodedSignature[64])
            v := shr(248, calldataload(add(encodedSignature.offset, 64)))
        }

        signer = ecrecover(message, v, r, s);
    }

    /// @dev Sets presign signatures for a batch of specified orders.
    ///
    /// @param orders The order data of the orders to pre-sign.
    /// @param signed Boolean indicating whether to pre-sign or cancel pre-signature.
    function setPreSigns_weQh(
        OrderUtils.Order[] calldata orders,
        bool signed
    )
        external
    {
        for (uint256 i; i < orders.length;)
        {
            // Must be either the order's maker or the maker's valid signer
            require(
                (orders[i].maker == msg.sender) || isValidOrderSigner(orders[i].maker, msg.sender),
                "RS:E16"
            );

            bytes32 orderHash = getOrderHash(orders[i]);

            preSign[orderHash] = signed;
            emit PreSign(orderHash, signed);

            // Gas optimization
            unchecked
            {
                ++i;
            }
        }
    }

    /// @dev Checks if a given address is registered to sign on behalf of a maker address.
    /// @param maker The maker address encoded in an order (can be a contract).
    /// @param signer The address that is providing a signature.
    function isValidOrderSigner(
        address maker,
        address signer
    )
        public
        view
        returns (bool isValid)
    {
        isValid = orderSignerRegistry[maker][signer];
    }

    /// @dev Register a signer who can sign on behalf of msg.sender (msg.sender can be a contract).
    /// @param signer The address from which you plan to generate signatures.
    /// @param allowed True to register, false to unregister.
    function registerAllowedOrderSigner(
        address signer,
        bool allowed
    )
        external
    {
        require(
            signer != address(0),
            "RS:E1"
        );

        orderSignerRegistry[msg.sender][signer] = allowed;

        emit OrderSignerRegistered(msg.sender, signer, allowed);
    }
}

File 13 of 15 : Math.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (utils/math/Math.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a >= b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds up instead
     * of rounding down.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a / b + (a % b == 0 ? 0 : 1);
    }
}

File 14 of 15 : eip1271.sol
// SPDX-License-Identifier: LGPL-3.0-or-later
pragma solidity 0.8.16;

library LibERC1271
{
    /// @dev Value returned by a call to `isValidSignature` if the signature
    /// was verified successfully. The value is defined in EIP-1271 as:
    /// bytes4(keccak256("isValidSignature(bytes32,bytes)"))
    bytes4 internal constant MAGICVALUE = 0x1626ba7e;
}

/// @title EIP1271 Interface
/// @dev Standardized interface for an implementation of smart contract
/// signatures as described in EIP-1271. The code that follows is identical to
/// the code in the standard with the exception of formatting and syntax
/// changes to adapt the code to our Solidity version.
interface EIP1271Verifier
{
    /// @dev Should return whether the signature provided is valid for the
    /// provided data
    /// @param _hash      Hash of the data to be signed
    /// @param _signature Signature byte array associated with _data
    ///
    /// MUST return the bytes4 magic value 0x1626ba7e when function passes.
    /// MUST NOT modify state (using STATICCALL for solc < 0.5, view modifier for
    /// solc > 0.5)
    /// MUST allow external calls
    ///
    function isValidSignature(
        bytes32 _hash,
        bytes memory _signature
    )
        external
        view
        returns (bytes4 magicValue);
}

File 15 of 15 : Strings.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Strings.sol)

pragma solidity ^0.8.0;

/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        // Inspired by OraclizeAPI's implementation - MIT licence
        // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol

        if (value == 0) {
            return "0";
        }
        uint256 temp = value;
        uint256 digits;
        while (temp != 0) {
            digits++;
            temp /= 10;
        }
        bytes memory buffer = new bytes(digits);
        while (value != 0) {
            digits -= 1;
            buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
            value /= 10;
        }
        return string(buffer);
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        if (value == 0) {
            return "0x00";
        }
        uint256 temp = value;
        uint256 length = 0;
        while (temp != 0) {
            length++;
            temp >>= 8;
        }
        return toHexString(value, length);
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = _HEX_SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }
}

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

Contract Security Audit

Contract ABI

API
[{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"maker","type":"address"},{"indexed":false,"internalType":"address","name":"taker","type":"address"},{"indexed":false,"internalType":"address","name":"makerToken","type":"address"},{"indexed":false,"internalType":"address","name":"takerToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"makerAmountFilled","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"takerAmountFilled","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"orderHash","type":"bytes32"}],"name":"Fill","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"orderHash","type":"bytes32"},{"indexed":false,"internalType":"address","name":"maker","type":"address"}],"name":"OrderCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"maker","type":"address"},{"indexed":false,"internalType":"address","name":"signer","type":"address"},{"indexed":false,"internalType":"bool","name":"allowed","type":"bool"}],"name":"OrderSignerRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferCompleted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferInitiated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"orderHash","type":"bytes32"},{"indexed":false,"internalType":"bool","name":"signed","type":"bool"}],"name":"PreSign","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Withdraw","type":"event"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"maker","type":"address"},{"internalType":"address","name":"makerToken","type":"address"},{"internalType":"address","name":"takerToken","type":"address"},{"internalType":"uint256","name":"makerAmount","type":"uint256"},{"internalType":"uint256","name":"takerAmountMin","type":"uint256"},{"internalType":"uint256","name":"takerAmountDecayRate","type":"uint256"},{"internalType":"uint256","name":"data","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct OrderUtils.Order[]","name":"orders","type":"tuple[]"}],"name":"cancelOrders__tYNw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getDexAggKeeperWhitelist","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"keeper","type":"address"}],"name":"getDexAggKeeperWhitelistPosition_IkFc","outputs":[{"internalType":"uint256","name":"dexAggKeeperWhitelistPosition","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getDexAggRouterWhitelist","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"router","type":"address"}],"name":"getDexAggRouterWhitelistPosition_ZgLC","outputs":[{"internalType":"uint256","name":"dexAggRouterWhitelistPosition","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getKeeperWhitelist","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"keeper","type":"address"}],"name":"getKeeperWhitelistPosition__2u3w","outputs":[{"internalType":"uint256","name":"keeperWhitelistPosition","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"maker","type":"address"},{"internalType":"address","name":"makerToken","type":"address"},{"internalType":"address","name":"takerToken","type":"address"},{"internalType":"uint256","name":"makerAmount","type":"uint256"},{"internalType":"uint256","name":"takerAmountMin","type":"uint256"},{"internalType":"uint256","name":"takerAmountDecayRate","type":"uint256"},{"internalType":"uint256","name":"data","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct OrderUtils.Order","name":"order","type":"tuple"}],"name":"getOrderHash","outputs":[{"internalType":"bytes32","name":"orderHash","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"maker","type":"address"},{"internalType":"address","name":"makerToken","type":"address"},{"internalType":"address","name":"takerToken","type":"address"},{"internalType":"uint256","name":"makerAmount","type":"uint256"},{"internalType":"uint256","name":"takerAmountMin","type":"uint256"},{"internalType":"uint256","name":"takerAmountDecayRate","type":"uint256"},{"internalType":"uint256","name":"data","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct OrderUtils.Order","name":"order","type":"tuple"}],"name":"getOrderRelevantStatus","outputs":[{"components":[{"internalType":"bytes32","name":"orderHash","type":"bytes32"},{"internalType":"enum OrderUtils.OrderStatus","name":"status","type":"uint8"},{"internalType":"uint256","name":"makerFilledAmount","type":"uint256"}],"internalType":"struct OrderUtils.OrderInfo","name":"orderInfo","type":"tuple"},{"internalType":"uint256","name":"makerAmountFillable","type":"uint256"},{"internalType":"bool","name":"isSignatureValid","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"maker","type":"address"},{"internalType":"address","name":"makerToken","type":"address"},{"internalType":"address","name":"takerToken","type":"address"},{"internalType":"uint256","name":"makerAmount","type":"uint256"},{"internalType":"uint256","name":"takerAmountMin","type":"uint256"},{"internalType":"uint256","name":"takerAmountDecayRate","type":"uint256"},{"internalType":"uint256","name":"data","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct OrderUtils.Order[]","name":"orders","type":"tuple[]"}],"name":"getOrderRelevantStatuses","outputs":[{"components":[{"internalType":"bytes32","name":"orderHash","type":"bytes32"},{"internalType":"enum OrderUtils.OrderStatus","name":"status","type":"uint8"},{"internalType":"uint256","name":"makerFilledAmount","type":"uint256"}],"internalType":"struct OrderUtils.OrderInfo[]","name":"orderInfos","type":"tuple[]"},{"internalType":"uint256[]","name":"makerAmountsFillable","type":"uint256[]"},{"internalType":"bool[]","name":"isSignatureValids","type":"bool[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"maker","type":"address"},{"internalType":"address","name":"signer","type":"address"}],"name":"isValidOrderSigner","outputs":[{"internalType":"bool","name":"isValid","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"makerAmountFilled","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"contract IERC20[]","name":"tokens","type":"address[]"},{"internalType":"uint256[]","name":"values","type":"uint256[]"}],"name":"manuallySetAllowances","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"nextLinkedWhitelist","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"preSign","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"signer","type":"address"},{"internalType":"bool","name":"allowed","type":"bool"}],"name":"registerAllowedOrderSigner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newNextLinkedWhitelist","type":"address"}],"name":"setNextLinkedWhitelist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"maker","type":"address"},{"internalType":"address","name":"makerToken","type":"address"},{"internalType":"address","name":"takerToken","type":"address"},{"internalType":"uint256","name":"makerAmount","type":"uint256"},{"internalType":"uint256","name":"takerAmountMin","type":"uint256"},{"internalType":"uint256","name":"takerAmountDecayRate","type":"uint256"},{"internalType":"uint256","name":"data","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct OrderUtils.Order[]","name":"orders","type":"tuple[]"},{"internalType":"bool","name":"signed","type":"bool"}],"name":"setPreSigns_weQh","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"maker","type":"address"},{"internalType":"address","name":"makerToken","type":"address"},{"internalType":"address","name":"takerToken","type":"address"},{"internalType":"uint256","name":"makerAmount","type":"uint256"},{"internalType":"uint256","name":"takerAmountMin","type":"uint256"},{"internalType":"uint256","name":"takerAmountDecayRate","type":"uint256"},{"internalType":"uint256","name":"data","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct OrderUtils.Order[]","name":"orders","type":"tuple[]"},{"internalType":"uint256[]","name":"makerAmountsToSpend","type":"uint256[]"},{"internalType":"uint256[]","name":"makerWeights","type":"uint256[]"},{"components":[{"internalType":"address","name":"router","type":"address"},{"internalType":"bytes","name":"callData","type":"bytes"},{"internalType":"address","name":"approveToken","type":"address"},{"internalType":"uint256","name":"approvalAmount","type":"uint256"}],"internalType":"struct LibSwap.DexAggSwap","name":"swap","type":"tuple"},{"internalType":"uint256[]","name":"takerTokenDistributions","type":"uint256[]"},{"components":[{"internalType":"address","name":"surplusToken","type":"address"},{"internalType":"uint256","name":"surplusAmountWithheld","type":"uint256"},{"internalType":"address","name":"otherToken","type":"address"},{"internalType":"bool","name":"surplusTokenIsSwapTakerToken","type":"bool"},{"internalType":"enum LibSwap.TakerTokenDistributionType","name":"takerTokenDistributionType","type":"uint8"},{"internalType":"uint256","name":"surplusProtectionThreshold","type":"uint256"}],"internalType":"struct LibSwap.MetaData","name":"metaData","type":"tuple"}],"name":"swapDexAggKeeper_8B77","outputs":[{"internalType":"uint256","name":"surplusAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"maker","type":"address"},{"internalType":"address","name":"makerToken","type":"address"},{"internalType":"address","name":"takerToken","type":"address"},{"internalType":"uint256","name":"makerAmount","type":"uint256"},{"internalType":"uint256","name":"takerAmountMin","type":"uint256"},{"internalType":"uint256","name":"takerAmountDecayRate","type":"uint256"},{"internalType":"uint256","name":"data","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct OrderUtils.Order[]","name":"orders","type":"tuple[]"},{"internalType":"uint256[]","name":"makerAmountsToSpend","type":"uint256[]"},{"internalType":"address","name":"keeperTaker","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"swapKeeper__oASr","outputs":[{"internalType":"bytes","name":"keeperReturn","type":"bytes"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"addresses","type":"address[]"},{"internalType":"bool","name":"value","type":"bool"}],"name":"whitelistDexAggKeepers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"addresses","type":"address[]"},{"internalType":"bool","name":"value","type":"bool"}],"name":"whitelistDexAggRouters","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"addresses","type":"address[]"},{"internalType":"bool","name":"value","type":"bool"}],"name":"whitelistKeepers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawEther_wEuX","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawToken_14u2","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60a060405234801561001057600080fd5b506001600081815581546001600160a01b031916339081179092556040517fe9a5158ac7353c7c7322ececc080bc8e89334efa5795b6e21e40eb266b0003d6908290a3600980546001600160a01b0319168155604080518082018252918252680526f6f6b20537761760bc1b602092830152805180820190915260058152640302e312e360dc1b9101527f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f7f76d2af50352d880308e542db11640dafde4ddfa14a4f853259c40706d845b9227faa7cdbe2cce2ec7b606b0e199ddd9b264a6e645e767fb8479a7917dcd1b8693f46604080516020810195909552840192909252606083015260808201523060a082015260c00160408051601f198184030181529190528051602090910120608052608051615b606200015a6000396000611d880152615b606000f3fe608060405234801561001057600080fd5b50600436106101985760003560e01c806361502535116100f9578063982b012311610097578063d0a46b9b11610071578063d0a46b9b14610462578063e567b86914610484578063ea7faa6114610497578063f2fde38b146104aa57600080fd5b8063982b0123146103ea5780639bb6cbd4146103fd578063aaf4f89d1461044257600080fd5b80636e4e952d116100d35780636e4e952d146103bf57806379ba5097146103d257806380c45f1e146103da578063873d0203146103e257600080fd5b8063615025351461035057806361e47ccf146103635780636b52a4a81461037657600080fd5b8060781161015a578063381e360c1161013d578063381e360c146102d357806346c02d7a146102f55780634c93f4ec146103285780635a73dfe31461033b57600080fd5b80607814610254578060c81461028a578060fa1461029d57600080fd5b80604a1161017f5780604a146101eb578060601461020b578060751461024157600080fd5b80600b1461019d5780601c146101b257806036146101c5575b600080fd5b6101b06101ab366004614b4c565b6104bd565b005b6101b06101c0366004614ba3565b61069c565b6101d86101d3366004614be6565b61080e565b6040519081526020015b60405180910390f35b6101fe6101f9366004614d0e565b610ec5565b6040516101e29190614e58565b6101d8610219366004614e6b565b73ffffffffffffffffffffffffffffffffffffffff1660009081526004602052604090205490565b6101b061024f366004614e88565b6110b2565b6101d8610262366004614e6b565b73ffffffffffffffffffffffffffffffffffffffff1660009081526008602052604090205490565b6101b0610298366004614eca565b611269565b6101d86102ab366004614e6b565b73ffffffffffffffffffffffffffffffffffffffff1660009081526006602052604090205490565b6102e66102e1366004614e88565b6113d4565b6040516101e293929190614fb2565b610318610303366004614ba3565b600a6020526000908152604090205460ff1681565b60405190151581526020016101e2565b6101b0610336366004615115565b61160a565b6103436116af565b6040516101e291906151c6565b6101b061035e366004615286565b611721565b6101b0610371366004614e6b565b6119b4565b61031861038436600461535c565b73ffffffffffffffffffffffffffffffffffffffff9182166000908152600b6020908152604080832093909416825291909152205460ff1690565b6101b06103cd366004615115565b611a7c565b6101b0611b1d565b610343611ba9565b610343611c19565b6101b06103f8366004615115565b611c89565b60095461041d9073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101e2565b6101d8610450366004614ba3565b600c6020526000908152604090205481565b610475610470366004615395565b611d2a565b6040516101e2939291906153d1565b6101d8610492366004615395565b611d84565b6101b06104a53660046153f5565b611ee2565b6101b06104b8366004614e6b565b611ff9565b60005b8281101561069657338484838181106104db576104db615423565b90506020028101906104ed9190615452565b6104fb906020810190614e6b565b73ffffffffffffffffffffffffffffffffffffffff161480610583575061058384848381811061052d5761052d615423565b905060200281019061053f9190615452565b61054d906020810190614e6b565b73ffffffffffffffffffffffffffffffffffffffff166000908152600b6020908152604080832033845290915290205460ff1690565b6105ee576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453136000000000000000000000000000000000000000000000000000060448201526064015b60405180910390fd5b600061061785858481811061060557610605615423565b90506020028101906104929190615452565b6000818152600a602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00168715159081179091558251848152918201529192507fe5132260ede7f3ca4aee317ef8dad1d6ddbd4169e74ea367b7e6964883916587910160405180910390a1506001016104c0565b50505050565b60015473ffffffffffffffffffffffffffffffffffffffff16331461071d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453137000000000000000000000000000000000000000000000000000060448201526064016105e5565b604051600090339083908381818185875af1925050503d806000811461075f576040519150601f19603f3d011682016040523d82523d6000602084013e610764565b606091505b50509050806107cf576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600560248201527f52533a453000000000000000000000000000000000000000000000000000000060448201526064016105e5565b6040805160008152602081018490527f884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a942436491015b60405180910390a15050565b600060026000540361087c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453138000000000000000000000000000000000000000000000000000060448201526064016105e5565b60026000908155338152600660205260409020546000036108f9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453232000000000000000000000000000000000000000000000000000060448201526064016105e5565b6109096102626020870187614e6b565b600003610972576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453330000000000000000000000000000000000000000000000000000060448201526064016105e5565b6109826060830160408401614e6b565b73ffffffffffffffffffffffffffffffffffffffff166109a56020840184614e6b565b73ffffffffffffffffffffffffffffffffffffffff1603610a22576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453331000000000000000000000000000000000000000000000000000060448201526064016105e5565b60005b8a811015610cd1578b8b82818110610a3f57610a3f615423565b9050602002810190610a519190615452565b610a62906040810190602001614e6b565b73ffffffffffffffffffffffffffffffffffffffff16610a856020850185614e6b565b73ffffffffffffffffffffffffffffffffffffffff161480610b1257508b8b82818110610ab457610ab4615423565b9050602002810190610ac69190615452565b610ad7906060810190604001614e6b565b73ffffffffffffffffffffffffffffffffffffffff16610afa6020850185614e6b565b73ffffffffffffffffffffffffffffffffffffffff16145b610b78576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453238000000000000000000000000000000000000000000000000000060448201526064016105e5565b8b8b82818110610b8a57610b8a615423565b9050602002810190610b9c9190615452565b610bad906060810190604001614e6b565b73ffffffffffffffffffffffffffffffffffffffff16610bd36060850160408601614e6b565b73ffffffffffffffffffffffffffffffffffffffff161480610c6357508b8b82818110610c0257610c02615423565b9050602002810190610c149190615452565b610c25906040810190602001614e6b565b73ffffffffffffffffffffffffffffffffffffffff16610c4b6060850160408601614e6b565b73ffffffffffffffffffffffffffffffffffffffff16145b610cc9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453239000000000000000000000000000000000000000000000000000060448201526064016105e5565b600101610a25565b50604080516060810190915260009080610cee6020860186614e6b565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff91909116906370a0823190602401602060405180830381865afa158015610d5a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d7e9190615490565b815260200160008152602001846040016020810190610d9d9190614e6b565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff91909116906370a0823190602401602060405180830381865afa158015610e09573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e2d9190615490565b905290506000610e408d8d8d8d3061216e565b90506000610e4e88866127c7565b90506000610e6260a08701608088016154a9565b6001811115610e7357610e73614ef6565b03610e8a57610e858e8e8c8c85612809565b610e96565b610e968e8e898961293d565b610ea48e8e8e8e8633612a84565b610eae8386612f81565b60016000559e9d5050505050505050505050505050565b6060600260005403610f33576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453138000000000000000000000000000000000000000000000000000060448201526064016105e5565b6002600090815573ffffffffffffffffffffffffffffffffffffffff8516815260046020526040902054600003610fc6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453231000000000000000000000000000000000000000000000000000060448201526064016105e5565b6000610fd5898989898961216e565b6040517c5700000000000000000000000000000000000000000000000000000000815290915073ffffffffffffffffffffffffffffffffffffffff86169060579061102c9033908b908b908a908a90600401615513565b6000604051808303816000875af115801561104b573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820160405261109191908101906155e1565b91506110a189898989858a612a84565b506001600055979650505050505050565b60005b8181101561126457338383838181106110d0576110d0615423565b90506020028101906110e29190615452565b6110f0906020810190614e6b565b73ffffffffffffffffffffffffffffffffffffffff1614158015611126575061112483838381811061052d5761052d615423565b155b1561118d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453230000000000000000000000000000000000000000000000000000060448201526064016105e5565b60006111a484848481811061060557610605615423565b6000818152600c6020526040902080547f800000000000000000000000000000000000000000000000000000000000000017905590507fa6eb7cdc219e1518ced964e9a34e61d68a94e4f1569db3e84256ba981ba527538185858581811061120e5761120e615423565b90506020028101906112209190615452565b61122e906020810190614e6b565b6040805192835273ffffffffffffffffffffffffffffffffffffffff90911660208301520160405180910390a1506001016110b5565b505050565b60015473ffffffffffffffffffffffffffffffffffffffff1633146112ea576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453137000000000000000000000000000000000000000000000000000060448201526064016105e5565b73ffffffffffffffffffffffffffffffffffffffff8216611367576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600560248201527f52533a453100000000000000000000000000000000000000000000000000000060448201526064016105e5565b61138873ffffffffffffffffffffffffffffffffffffffff831633836131c4565b6040805173ffffffffffffffffffffffffffffffffffffffff84168152602081018390527f884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a94243649101610802565b60608080838067ffffffffffffffff8111156113f2576113f261504a565b60405190808252806020026020018201604052801561145b57816020015b60408051606081018252600080825260208083018290529282015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9092019101816114105790505b5093508067ffffffffffffffff8111156114775761147761504a565b6040519080825280602002602001820160405280156114a0578160200160208202803683370190505b5092508067ffffffffffffffff8111156114bc576114bc61504a565b6040519080825280602002602001820160405280156114e5578160200160208202803683370190505b50915060005b81811015611601573063d0a46b9b88888481811061150b5761150b615423565b905060200281019061151d9190615452565b6040518263ffffffff1660e01b815260040161153991906156bc565b60a060405180830381865afa925050508015611590575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820190925261158d91810190615787565b60015b156115f957828885815181106115a8576115a8615423565b6020026020010181905250818785815181106115c6576115c6615423565b602002602001018181525050808685815181106115e5576115e5615423565b911515602092830291909101909101525050505b6001016114eb565b50509250925092565b60015473ffffffffffffffffffffffffffffffffffffffff16331461168b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453137000000000000000000000000000000000000000000000000000060448201526064016105e5565b80156116a25761169e6007836002613298565b5050565b61169e6007836002613451565b6060600760000180548060200260200160405190810160405280929190818152602001828054801561171757602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff1681526001909101906020018083116116ec575b5050505050905090565b60015473ffffffffffffffffffffffffffffffffffffffff1633146117a2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453137000000000000000000000000000000000000000000000000000060448201526064016105e5565b73ffffffffffffffffffffffffffffffffffffffff831661181f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600560248201527f52533a453100000000000000000000000000000000000000000000000000000060448201526064016105e5565b60005b825181101561069657600073ffffffffffffffffffffffffffffffffffffffff1683828151811061185557611855615423565b602002602001015173ffffffffffffffffffffffffffffffffffffffff16036118da576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600560248201527f52533a453100000000000000000000000000000000000000000000000000000060448201526064016105e5565b8281815181106118ec576118ec615423565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1663095ea7b38584848151811061192257611922615423565b60200260200101516040518363ffffffff1660e01b815260040161196892919073ffffffffffffffffffffffffffffffffffffffff929092168252602082015260400190565b6020604051808303816000875af1158015611987573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119ab919061580f565b50600101611822565b60015473ffffffffffffffffffffffffffffffffffffffff163314611a35576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453137000000000000000000000000000000000000000000000000000060448201526064016105e5565b600980547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b60015473ffffffffffffffffffffffffffffffffffffffff163314611afd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453137000000000000000000000000000000000000000000000000000060448201526064016105e5565b8015611b105761169e6005836001613298565b61169e6005836001613451565b60025473ffffffffffffffffffffffffffffffffffffffff163314611b9e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453137000000000000000000000000000000000000000000000000000060448201526064016105e5565b611ba73361371e565b565b606060056000018054806020026020016040519081016040528092919081815260200182805480156117175760200282019190600052602060002090815473ffffffffffffffffffffffffffffffffffffffff1681526001909101906020018083116116ec575050505050905090565b606060036000018054806020026020016040519081016040528092919081815260200182805480156117175760200282019190600052602060002090815473ffffffffffffffffffffffffffffffffffffffff1681526001909101906020018083116116ec575050505050905090565b60015473ffffffffffffffffffffffffffffffffffffffff163314611d0a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453137000000000000000000000000000000000000000000000000000060448201526064016105e5565b8015611d1d5761169e6003836000613298565b61169e6003836000613451565b60408051606081018252600080825260208201819052918101829052908080611d5285611d84565b90506000611d648660c00135836137bd565b9050611d75868383600060016138f9565b94509450945050509193909250565b60007f00000000000000000000000000000000000000000000000000000000000000007f4319db3766093257e119019721ad33761927ac79912abb48d42c37a7fe85fdfd611dd56020850185614e6b565b611de56040860160208701614e6b565b611df56060870160408801614e6b565b866060013587608001358860a001358960c00135604051602001611e6d98979695949392919097885273ffffffffffffffffffffffffffffffffffffffff96871660208901529486166040880152929094166060860152608085015260a084019290925260c083019190915260e08201526101000190565b60405160208183030381529060405280519060200120604051602001611ec59291907f190100000000000000000000000000000000000000000000000000000000000081526002810192909252602282015260420190565b604051602081830303815290604052805190602001209050919050565b73ffffffffffffffffffffffffffffffffffffffff8216611f5f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600560248201527f52533a453100000000000000000000000000000000000000000000000000000060448201526064016105e5565b336000818152600b6020908152604080832073ffffffffffffffffffffffffffffffffffffffff87168085529083529281902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016861515908117909155815194855291840192909252908201527f6ea9dbe8b2cc119348716a9220a0742ad62b7884ecb0ff4b32cd508121fd937990606001610802565b60015473ffffffffffffffffffffffffffffffffffffffff16331461207a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453137000000000000000000000000000000000000000000000000000060448201526064016105e5565b73ffffffffffffffffffffffffffffffffffffffff81166120f7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600560248201527f52533a453100000000000000000000000000000000000000000000000000000060448201526064016105e5565b600280547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff838116918217909255600154604051919216907fb150023a879fd806e3599b6ca8ee3b60f0e360ab3846d128d67ebce1a391639a90600090a350565b6060848381146121da576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453132000000000000000000000000000000000000000000000000000060448201526064016105e5565b8067ffffffffffffffff8111156121f3576121f361504a565b60405190808252806020026020018201604052801561226157816020015b61224e6040805160e081018252600080825260208201819052918101829052606081018290526080810182905260a081018290529060c082015290565b8152602001906001900390816122115790505b50915060005b818110156127bc5760005b828110156124c257808214158015612317575088888281811061229757612297615423565b90506020028101906122a99190615452565b6122b7906020810190614e6b565b73ffffffffffffffffffffffffffffffffffffffff168989848181106122df576122df615423565b90506020028101906122f19190615452565b6122ff906020810190614e6b565b73ffffffffffffffffffffffffffffffffffffffff16145b8015612453575088888281811061233057612330615423565b90506020028101906123429190615452565b612353906060810190604001614e6b565b73ffffffffffffffffffffffffffffffffffffffff1689898481811061237b5761237b615423565b905060200281019061238d9190615452565b61239e906060810190604001614e6b565b73ffffffffffffffffffffffffffffffffffffffff16148061245357508888828181106123cd576123cd615423565b90506020028101906123df9190615452565b6123f0906060810190604001614e6b565b73ffffffffffffffffffffffffffffffffffffffff1689898481811061241857612418615423565b905060200281019061242a9190615452565b61243b906040810190602001614e6b565b73ffffffffffffffffffffffffffffffffffffffff16145b156124ba576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453135000000000000000000000000000000000000000000000000000060448201526064016105e5565b600101612272565b5060006124da89898481811061060557610605615423565b905061250d8989848181106124f1576124f1615423565b90506020028101906125039190615452565b60c00135826137bd565b84838151811061251f5761251f615423565b602002602001018190525088888381811061253c5761253c615423565b905060200281019061254e9190615452565b61255f906060810190604001614e6b565b73ffffffffffffffffffffffffffffffffffffffff166370a082318a8a8581811061258c5761258c615423565b905060200281019061259e9190615452565b6125ac906020810190614e6b565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b16815273ffffffffffffffffffffffffffffffffffffffff9091166004820152602401602060405180830381865afa158015612615573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126399190615490565b84838151811061264b5761264b615423565b602002602001015160200181815250506126a689898481811061267057612670615423565b90506020028101906126829190615452565b8286858151811061269557612695615423565b6020026020010151600160006138f9565b50505061274b8989848181106126be576126be615423565b90506020028101906126d09190615452565b6126de906020810190614e6b565b868989868181106126f1576126f1615423565b905060200201358c8c8781811061270a5761270a615423565b905060200281019061271c9190615452565b61272d906040810190602001614e6b565b73ffffffffffffffffffffffffffffffffffffffff16929190613c4c565b6127b389898481811061276057612760615423565b90506020028101906127729190615452565b606001358289898681811061278957612789615423565b905060200201358786815181106127a2576127a2615423565b602002602001015160a00151613caa565b50600101612267565b505095945050505050565b60006127db6127d58461582c565b83613e3c565b90506127ed60808301606084016158dc565b1561280357612800602083013582615928565b90505b92915050565b8360018190036128a25761289d8686600081811061282957612829615423565b905060200281019061283b9190615452565b612849906020810190614e6b565b838888600081811061285d5761285d615423565b905060200281019061286f9190615452565b612880906060810190604001614e6b565b73ffffffffffffffffffffffffffffffffffffffff1691906131c4565b612935565b60005b818110156129335761292b8787838181106128c2576128c2615423565b90506020028101906128d49190615452565b6128e2906020810190614e6b565b670de0b6b3a76400008787858181106128fd576128fd615423565b905060200201358661290f919061593b565b6129199190615978565b89898581811061285d5761285d615423565b6001016128a5565b505b505050505050565b82600181116129a8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453133000000000000000000000000000000000000000000000000000060448201526064016105e5565b808214612a11576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453134000000000000000000000000000000000000000000000000000060448201526064016105e5565b60005b8181101561293557612a7c868683818110612a3157612a31615423565b9050602002810190612a439190615452565b612a51906020810190614e6b565b858584818110612a6357612a63615423565b9050602002013588888581811061285d5761285d615423565b600101612a14565b60005b8581101561293357868682818110612aa157612aa1615423565b9050602002810190612ab39190615452565b612ac4906060810190604001614e6b565b73ffffffffffffffffffffffffffffffffffffffff166370a08231888884818110612af157612af1615423565b9050602002810190612b039190615452565b612b11906020810190614e6b565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b16815273ffffffffffffffffffffffffffffffffffffffff9091166004820152602401602060405180830381865afa158015612b7a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b9e9190615490565b838281518110612bb057612bb0615423565b602002602001015160400181815250506000838281518110612bd457612bd4615423565b602002602001015160200151848381518110612bf257612bf2615423565b602002602001015160400151612c089190615928565b90506000888884818110612c1e57612c1e615423565b9050602002810190612c309190615452565b60a0013515612cb057612cab898985818110612c4e57612c4e615423565b9050602002810190612c609190615452565b608001358a8a86818110612c7657612c76615423565b9050602002810190612c889190615452565b60a00135878681518110612c9e57612c9e615423565b60200260200101516141c3565b612cd9565b888884818110612cc257612cc2615423565b9050602002810190612cd49190615452565b608001355b9050848381518110612ced57612ced615423565b602002602001015160a0015115612dc157868684818110612d1057612d10615423565b9050602002013581612d22919061593b565b898985818110612d3457612d34615423565b9050602002810190612d469190615452565b612d5490606001358461593b565b1015612dbc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453233000000000000000000000000000000000000000000000000000060448201526064016105e5565b612e2b565b80821015612e2b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453234000000000000000000000000000000000000000000000000000060448201526064016105e5565b7fcabd156033bc5efebccd321136638073b3e452c01a38d36cbfc9bdec2ffd0f9d898985818110612e5e57612e5e615423565b9050602002810190612e709190615452565b612e7e906020810190614e6b565b858b8b87818110612e9157612e91615423565b9050602002810190612ea39190615452565b612eb4906040810190602001614e6b565b8c8c88818110612ec657612ec6615423565b9050602002810190612ed89190615452565b612ee9906060810190604001614e6b565b8b8b89818110612efb57612efb615423565b90506020020135878b8a81518110612f1557612f15615423565b602090810291909101810151516040805173ffffffffffffffffffffffffffffffffffffffff998a1681529789169288019290925294871686820152929095166060850152608084015260a083019390935260c082015290519081900360e00190a15050600101612a87565b6000612f906020830183614e6b565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff91909116906370a0823190602401602060405180830381865afa158015612ffc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130209190615490565b602084015282516130369060a0840135906159b3565b836020015110156130a3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453235000000000000000000000000000000000000000000000000000060448201526064016105e5565b82604001518260400160208101906130bb9190614e6b565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff91909116906370a0823190602401602060405180830381865afa158015613127573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061314b9190615490565b10156131b3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453236000000000000000000000000000000000000000000000000000060448201526064016105e5565b825160208401516128009190615928565b60405173ffffffffffffffffffffffffffffffffffffffff83166024820152604481018290526112649084907fa9059cbb00000000000000000000000000000000000000000000000000000000906064015b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915261421a565b815160005b8181101561344a5760008482815181106132b9576132b9615423565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff8116600090815260018901909252604090912054909150801561335f5784600281111561330957613309614ef6565b6040805173ffffffffffffffffffffffffffffffffffffffff85168152600160208201527f2fceaccc046b9071f648e6e6eadf8f0b7686fd0c3de699cbf926b8617ebc7faf910160405180910390a25050613438565b865461336c8160016159b3565b73ffffffffffffffffffffffffffffffffffffffff841660008181526001808c01602090815260408320949094558b549081018c558b825292902090910180547fffffffffffffffffffffffff00000000000000000000000000000000000000001690911790558560028111156133e5576133e5614ef6565b6040805173ffffffffffffffffffffffffffffffffffffffff86168152600160208201527f2fceaccc046b9071f648e6e6eadf8f0b7686fd0c3de699cbf926b8617ebc7faf910160405180910390a25050505b80613442816159c6565b91505061329d565b5050505050565b815160005b8181101561344a57600084828151811061347257613472615423565b6020026020010151905060008660010160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905080600003613532578460028111156134dc576134dc614ef6565b6040805173ffffffffffffffffffffffffffffffffffffffff85168152600060208201527f2fceaccc046b9071f648e6e6eadf8f0b7686fd0c3de699cbf926b8617ebc7faf910160405180910390a2505061370c565b865460009061354390600190615928565b9050600088600001828154811061355c5761355c615423565b600091825260209091200154895473ffffffffffffffffffffffffffffffffffffffff909116915089908390811061359657613596615423565b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff16896135c4600186615928565b815481106135d4576135d4615423565b600091825260208083209190910180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff948516179055838316825260018c0190526040808220869055918616815290812055885489908061364e5761364e6159fe565b60008281526020902081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90810180547fffffffffffffffffffffffff00000000000000000000000000000000000000001690550190558660028111156136b8576136b8614ef6565b6040805173ffffffffffffffffffffffffffffffffffffffff87168152600060208201527f2fceaccc046b9071f648e6e6eadf8f0b7686fd0c3de699cbf926b8617ebc7faf910160405180910390a2505050505b80613716816159c6565b915050613456565b6001805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681179093556040519116919082907fe9a5158ac7353c7c7322ececc080bc8e89334efa5795b6e21e40eb266b0003d690600090a35050600280547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055565b6137fa6040805160e081018252600080825260208201819052918101829052606081018290526080810182905260a081018290529060c082015290565b67ffffffffffffffff80841690604085901c16700100000000000000000000000000000000851615156000608187901c600381111561383b5761383b614ef6565b9050838310156138a7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453237000000000000000000000000000000000000000000000000000060448201526064016105e5565b6040518060e00160405280878152602001600081526020016000815260200185815260200184815260200183151581526020018260038111156138ec576138ec614ef6565b9052979650505050505050565b60408051606081018252600080825260208201819052918101919091526000806139296060890160408a01614e6b565b73ffffffffffffffffffffffffffffffffffffffff1661394f60408a0160208b01614e6b565b73ffffffffffffffffffffffffffffffffffffffff16036139cc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600560248201527f52533a453500000000000000000000000000000000000000000000000000000060448201526064016105e5565b8683526000878152600c602052604090819020549084018190527f80000000000000000000000000000000000000000000000000000000000000001615613a2f576020830160035b90816004811115613a2757613a27614ef6565b905250613a67565b8760600135836040015110613a4957602083016002613a14565b42866080015111613a5f57602083016004613a14565b600160208401525b841580613a895750600183602001516004811115613a8757613a87614ef6565b145b613aef576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600560248201527f52533a453600000000000000000000000000000000000000000000000000000060448201526064016105e5565b8315613b0257613aff8884614326565b91505b825160c0870151600091613b2291613b1d60e08d018d615a2d565b6143a4565b905073ffffffffffffffffffffffffffffffffffffffff811615801590613bce575073ffffffffffffffffffffffffffffffffffffffff8116613b6860208b018b614e6b565b73ffffffffffffffffffffffffffffffffffffffff161480613bce5750613bce613b9560208b018b614e6b565b73ffffffffffffffffffffffffffffffffffffffff9081166000908152600b602090815260408083209386168352929052205460ff1690565b9150851580613bda5750815b613c40576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600560248201527f52533a453700000000000000000000000000000000000000000000000000000060448201526064016105e5565b50955095509592505050565b60405173ffffffffffffffffffffffffffffffffffffffff808516602483015283166044820152606481018290526106969085907f23b872dd0000000000000000000000000000000000000000000000000000000090608401613216565b8015613d4a576000838152600c6020526040812054613cca9084906159b3565b905084811115613d36576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600560248201527f52533a453200000000000000000000000000000000000000000000000000000060448201526064016105e5565b6000848152600c6020526040902055610696565b838214613db3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600560248201527f52533a453300000000000000000000000000000000000000000000000000000060448201526064016105e5565b6000838152600c602052604090205415613e29576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600560248201527f52533a453400000000000000000000000000000000000000000000000000000060448201526064016105e5565b50506000908152600c6020526040902055565b604082015160009073ffffffffffffffffffffffffffffffffffffffff16613ec0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453139000000000000000000000000000000000000000000000000000060448201526064016105e5565b613ecd6020830183614e6b565b73ffffffffffffffffffffffffffffffffffffffff16836040015173ffffffffffffffffffffffffffffffffffffffff161480613f485750613f156060830160408401614e6b565b73ffffffffffffffffffffffffffffffffffffffff16836040015173ffffffffffffffffffffffffffffffffffffffff16145b613fae576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453332000000000000000000000000000000000000000000000000000060448201526064016105e5565b6040838101518451606086015192517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff91821660048201526024810193909352169063095ea7b3906044016020604051808303816000875af115801561402f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614053919061580f565b50600080846000015173ffffffffffffffffffffffffffffffffffffffff16600086602001516040516140869190615a92565b60006040518083038185875af1925050503d80600081146140c3576040519150601f19603f3d011682016040523d82523d6000602084013e6140c8565b606091505b509150915061410d82826040518060400160405280601981526020017f63616c6c4461746120657865637574696f6e206661696c6564000000000000008152506145ce565b61411881600061461d565b604086810151875191517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff928316600482015260006024820152929550169063095ea7b3906044016020604051808303816000875af1158015614196573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906141ba919061580f565b50505092915050565b6000816080015142106141d75760006141fe565b81606001514210156141ed5781606001516141ef565b425b82608001516141fe9190615928565b614208908461593b565b61421290856159b3565b949350505050565b600061427c826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff1661469d9092919063ffffffff16565b805190915015611264578080602001905181019061429a919061580f565b611264576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f7420737563636565640000000000000000000000000000000000000000000060648201526084016105e5565b600060018260200151600481111561434057614340614ef6565b1461434d57506000612803565b826060013560000361436157506000612803565b6040820151614374906060850135615928565b90506128008161439f61438d6040870160208801614e6b565b61439a6020880188614e6b565b6146b3565b6147e0565b6000808460038111156143b9576143b9614ef6565b036143d0576143c98584846147f6565b9050614212565b60018460038111156143e4576143e4614ef6565b0361443d576040517f19457468657265756d205369676e6564204d6573736167653a0a3332000000006020820152603c81018690526143c990605c016040516020818303038152906040528051906020012084846147f6565b600284600381111561445157614451614ef6565b036145955750813560601c36600061446c8460148188615aa4565b6040517f1626ba7e000000000000000000000000000000000000000000000000000000008152919350915073ffffffffffffffffffffffffffffffffffffffff841690631626ba7e906144c7908a9086908690600401615ace565b602060405180830381865afa92505050801561451e575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820190925261451b91810190615ae8565b60015b61452b576000925061458e565b73ffffffffffffffffffffffffffffffffffffffff7fffffffff00000000000000000000000000000000000000000000000000000000919091167f1626ba7e00000000000000000000000000000000000000000000000000000000140292909216915b5050614212565b506000848152600a602052604090205473ffffffffffffffffffffffffffffffffffffffff60ff90911602823560601c16949350505050565b82156145d957505050565b8151156145e95781518083602001fd5b806040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105e59190614e58565b600061462a8260206159b3565b83511015614694576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453131000000000000000000000000000000000000000000000000000060448201526064016105e5565b50016020015190565b606061421284846000856148f6565b9392505050565b6040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82811660048301523060248301526000916128009185169063dd62ed3e90604401602060405180830381865afa15801561472c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906147509190615490565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85811660048301528616906370a0823190602401602060405180830381865afa1580156147bc573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061439f9190615490565b60008183106147ef5781612800565b5090919050565b600060418214614862576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600560248201527f52533a453800000000000000000000000000000000000000000000000000000060448201526064016105e5565b604080516000815260208082018084528790528583013560f81c92820183905285356060830181905290860135608083018190529092909160019060a0016020604051602081039080840390855afa1580156148c2573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015198975050505050505050565b606082471015614988576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c000000000000000000000000000000000000000000000000000060648201526084016105e5565b73ffffffffffffffffffffffffffffffffffffffff85163b614a06576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016105e5565b6000808673ffffffffffffffffffffffffffffffffffffffff168587604051614a2f9190615a92565b60006040518083038185875af1925050503d8060008114614a6c576040519150601f19603f3d011682016040523d82523d6000602084013e614a71565b606091505b5091509150614a81828286614a8c565b979650505050505050565b60608315614a9b5750816146ac565b825115614aab5782518084602001fd5b816040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105e59190614e58565b60008083601f840112614af157600080fd5b50813567ffffffffffffffff811115614b0957600080fd5b6020830191508360208260051b8501011115614b2457600080fd5b9250929050565b8015158114614b3957600080fd5b50565b8035614b4781614b2b565b919050565b600080600060408486031215614b6157600080fd5b833567ffffffffffffffff811115614b7857600080fd5b614b8486828701614adf565b9094509250506020840135614b9881614b2b565b809150509250925092565b600060208284031215614bb557600080fd5b5035919050565b600060808284031215614bce57600080fd5b50919050565b600060c08284031215614bce57600080fd5b6000806000806000806000806000806101608b8d031215614c0657600080fd5b8a3567ffffffffffffffff80821115614c1e57600080fd5b614c2a8e838f01614adf565b909c509a5060208d0135915080821115614c4357600080fd5b614c4f8e838f01614adf565b909a50985060408d0135915080821115614c6857600080fd5b614c748e838f01614adf565b909850965060608d0135915080821115614c8d57600080fd5b614c998e838f01614bbc565b955060808d0135915080821115614caf57600080fd5b50614cbc8d828e01614adf565b9094509250614cd090508c60a08d01614bd4565b90509295989b9194979a5092959850565b73ffffffffffffffffffffffffffffffffffffffff81168114614b3957600080fd5b8035614b4781614ce1565b60008060008060008060006080888a031215614d2957600080fd5b873567ffffffffffffffff80821115614d4157600080fd5b614d4d8b838c01614adf565b909950975060208a0135915080821115614d6657600080fd5b614d728b838c01614adf565b909750955060408a01359150614d8782614ce1565b90935060608901359080821115614d9d57600080fd5b818a0191508a601f830112614db157600080fd5b813581811115614dc057600080fd5b8b6020828501011115614dd257600080fd5b60208301945080935050505092959891949750929550565b60005b83811015614e05578181015183820152602001614ded565b50506000910152565b60008151808452614e26816020860160208601614dea565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6020815260006128006020830184614e0e565b600060208284031215614e7d57600080fd5b81356146ac81614ce1565b60008060208385031215614e9b57600080fd5b823567ffffffffffffffff811115614eb257600080fd5b614ebe85828601614adf565b90969095509350505050565b60008060408385031215614edd57600080fd5b8235614ee881614ce1565b946020939093013593505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b80518252602081015160058110614f65577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b6020830152604090810151910152565b600081518084526020808501945080840160005b83811015614fa7578151151587529582019590820190600101614f89565b509495945050505050565b60608082528451828201819052600091906020906080850190828901855b82811015614ff357614fe3848351614f25565b9285019290840190600101614fd0565b50505084810382860152865180825290820192508682019060005b8181101561502a5782518552938301939183019160010161500e565b5050505082810360408401526150408185614f75565b9695505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040516080810167ffffffffffffffff8111828210171561509c5761509c61504a565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff811182821017156150e9576150e961504a565b604052919050565b600067ffffffffffffffff82111561510b5761510b61504a565b5060051b60200190565b6000806040838503121561512857600080fd5b823567ffffffffffffffff81111561513f57600080fd5b8301601f8101851361515057600080fd5b80356020615165615160836150f1565b6150a2565b82815260059290921b8301810191818101908884111561518457600080fd5b938201935b838510156151ab57843561519c81614ce1565b82529382019390820190615189565b95506151ba9050868201614b3c565b93505050509250929050565b6020808252825182820181905260009190848201906040850190845b8181101561521457835173ffffffffffffffffffffffffffffffffffffffff16835292840192918401916001016151e2565b50909695505050505050565b600082601f83011261523157600080fd5b81356020615241615160836150f1565b82815260059290921b8401810191818101908684111561526057600080fd5b8286015b8481101561527b5780358352918301918301615264565b509695505050505050565b60008060006060848603121561529b57600080fd5b83356152a681614ce1565b925060208481013567ffffffffffffffff808211156152c457600080fd5b818701915087601f8301126152d857600080fd5b81356152e6615160826150f1565b81815260059190911b8301840190848101908a83111561530557600080fd5b938501935b8285101561532c57843561531d81614ce1565b8252938501939085019061530a565b96505050604087013592508083111561534457600080fd5b505061535286828701615220565b9150509250925092565b6000806040838503121561536f57600080fd5b823561537a81614ce1565b9150602083013561538a81614ce1565b809150509250929050565b6000602082840312156153a757600080fd5b813567ffffffffffffffff8111156153be57600080fd5b820161010081850312156146ac57600080fd5b60a081016153df8286614f25565b8360608301528215156080830152949350505050565b6000806040838503121561540857600080fd5b823561541381614ce1565b9150602083013561538a81614b2b565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0183360301811261548657600080fd5b9190910192915050565b6000602082840312156154a257600080fd5b5051919050565b6000602082840312156154bb57600080fd5b8135600281106146ac57600080fd5b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b73ffffffffffffffffffffffffffffffffffffffff861681526060602082015283606082015260007f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff85111561556857600080fd5b8460051b8087608085013782018281036080908101604085015261558f90820185876154ca565b98975050505050505050565b600067ffffffffffffffff8211156155b5576155b561504a565b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b6000602082840312156155f357600080fd5b815167ffffffffffffffff81111561560a57600080fd5b8201601f8101841361561b57600080fd5b80516156296151608261559b565b81815285602083850101111561563e57600080fd5b61564f826020830160208601614dea565b95945050505050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261568d57600080fd5b830160208101925035905067ffffffffffffffff8111156156ad57600080fd5b803603821315614b2457600080fd5b60208152600082356156cd81614ce1565b73ffffffffffffffffffffffffffffffffffffffff81166020840152506156f660208401614d03565b73ffffffffffffffffffffffffffffffffffffffff811660408401525061571f60408401614d03565b73ffffffffffffffffffffffffffffffffffffffff811660608401525060608301356080830152608083013560a083015260a083013560c083015260c083013560e083015261577160e0840184615658565b6101008481015261564f610120850182846154ca565b600080600083850360a081121561579d57600080fd5b60608112156157ab57600080fd5b506040516060810181811067ffffffffffffffff821117156157cf576157cf61504a565b604052845181526020850151600581106157e857600080fd5b602082015260408581015190820152606085015160808601519194509250614b9881614b2b565b60006020828403121561582157600080fd5b81516146ac81614b2b565b60006080823603121561583e57600080fd5b615846615079565b823561585181614ce1565b815260208381013567ffffffffffffffff81111561586e57600080fd5b840136601f82011261587f57600080fd5b803561588d6151608261559b565b81815236848385010111156158a157600080fd5b818484018583013760008483830101528084860152505050506158c660408401614d03565b6040820152606092830135928101929092525090565b6000602082840312156158ee57600080fd5b81356146ac81614b2b565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b81810381811115612803576128036158f9565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615615973576159736158f9565b500290565b6000826159ae577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b80820180821115612803576128036158f9565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036159f7576159f76158f9565b5060010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112615a6257600080fd5b83018035915067ffffffffffffffff821115615a7d57600080fd5b602001915036819003821315614b2457600080fd5b60008251615486818460208701614dea565b60008085851115615ab457600080fd5b83861115615ac157600080fd5b5050820193919092039150565b83815260406020820152600061564f6040830184866154ca565b600060208284031215615afa57600080fd5b81517fffffffff00000000000000000000000000000000000000000000000000000000811681146146ac57600080fdfea2646970667358221220cedcf2d3206d2775a915263f0bbcc407cafccdf4dea9439be2437f1bcb0e330064736f6c63430008100033

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106101985760003560e01c806361502535116100f9578063982b012311610097578063d0a46b9b11610071578063d0a46b9b14610462578063e567b86914610484578063ea7faa6114610497578063f2fde38b146104aa57600080fd5b8063982b0123146103ea5780639bb6cbd4146103fd578063aaf4f89d1461044257600080fd5b80636e4e952d116100d35780636e4e952d146103bf57806379ba5097146103d257806380c45f1e146103da578063873d0203146103e257600080fd5b8063615025351461035057806361e47ccf146103635780636b52a4a81461037657600080fd5b8060781161015a578063381e360c1161013d578063381e360c146102d357806346c02d7a146102f55780634c93f4ec146103285780635a73dfe31461033b57600080fd5b80607814610254578060c81461028a578060fa1461029d57600080fd5b80604a1161017f5780604a146101eb578060601461020b578060751461024157600080fd5b80600b1461019d5780601c146101b257806036146101c5575b600080fd5b6101b06101ab366004614b4c565b6104bd565b005b6101b06101c0366004614ba3565b61069c565b6101d86101d3366004614be6565b61080e565b6040519081526020015b60405180910390f35b6101fe6101f9366004614d0e565b610ec5565b6040516101e29190614e58565b6101d8610219366004614e6b565b73ffffffffffffffffffffffffffffffffffffffff1660009081526004602052604090205490565b6101b061024f366004614e88565b6110b2565b6101d8610262366004614e6b565b73ffffffffffffffffffffffffffffffffffffffff1660009081526008602052604090205490565b6101b0610298366004614eca565b611269565b6101d86102ab366004614e6b565b73ffffffffffffffffffffffffffffffffffffffff1660009081526006602052604090205490565b6102e66102e1366004614e88565b6113d4565b6040516101e293929190614fb2565b610318610303366004614ba3565b600a6020526000908152604090205460ff1681565b60405190151581526020016101e2565b6101b0610336366004615115565b61160a565b6103436116af565b6040516101e291906151c6565b6101b061035e366004615286565b611721565b6101b0610371366004614e6b565b6119b4565b61031861038436600461535c565b73ffffffffffffffffffffffffffffffffffffffff9182166000908152600b6020908152604080832093909416825291909152205460ff1690565b6101b06103cd366004615115565b611a7c565b6101b0611b1d565b610343611ba9565b610343611c19565b6101b06103f8366004615115565b611c89565b60095461041d9073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101e2565b6101d8610450366004614ba3565b600c6020526000908152604090205481565b610475610470366004615395565b611d2a565b6040516101e2939291906153d1565b6101d8610492366004615395565b611d84565b6101b06104a53660046153f5565b611ee2565b6101b06104b8366004614e6b565b611ff9565b60005b8281101561069657338484838181106104db576104db615423565b90506020028101906104ed9190615452565b6104fb906020810190614e6b565b73ffffffffffffffffffffffffffffffffffffffff161480610583575061058384848381811061052d5761052d615423565b905060200281019061053f9190615452565b61054d906020810190614e6b565b73ffffffffffffffffffffffffffffffffffffffff166000908152600b6020908152604080832033845290915290205460ff1690565b6105ee576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453136000000000000000000000000000000000000000000000000000060448201526064015b60405180910390fd5b600061061785858481811061060557610605615423565b90506020028101906104929190615452565b6000818152600a602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00168715159081179091558251848152918201529192507fe5132260ede7f3ca4aee317ef8dad1d6ddbd4169e74ea367b7e6964883916587910160405180910390a1506001016104c0565b50505050565b60015473ffffffffffffffffffffffffffffffffffffffff16331461071d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453137000000000000000000000000000000000000000000000000000060448201526064016105e5565b604051600090339083908381818185875af1925050503d806000811461075f576040519150601f19603f3d011682016040523d82523d6000602084013e610764565b606091505b50509050806107cf576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600560248201527f52533a453000000000000000000000000000000000000000000000000000000060448201526064016105e5565b6040805160008152602081018490527f884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a942436491015b60405180910390a15050565b600060026000540361087c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453138000000000000000000000000000000000000000000000000000060448201526064016105e5565b60026000908155338152600660205260409020546000036108f9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453232000000000000000000000000000000000000000000000000000060448201526064016105e5565b6109096102626020870187614e6b565b600003610972576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453330000000000000000000000000000000000000000000000000000060448201526064016105e5565b6109826060830160408401614e6b565b73ffffffffffffffffffffffffffffffffffffffff166109a56020840184614e6b565b73ffffffffffffffffffffffffffffffffffffffff1603610a22576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453331000000000000000000000000000000000000000000000000000060448201526064016105e5565b60005b8a811015610cd1578b8b82818110610a3f57610a3f615423565b9050602002810190610a519190615452565b610a62906040810190602001614e6b565b73ffffffffffffffffffffffffffffffffffffffff16610a856020850185614e6b565b73ffffffffffffffffffffffffffffffffffffffff161480610b1257508b8b82818110610ab457610ab4615423565b9050602002810190610ac69190615452565b610ad7906060810190604001614e6b565b73ffffffffffffffffffffffffffffffffffffffff16610afa6020850185614e6b565b73ffffffffffffffffffffffffffffffffffffffff16145b610b78576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453238000000000000000000000000000000000000000000000000000060448201526064016105e5565b8b8b82818110610b8a57610b8a615423565b9050602002810190610b9c9190615452565b610bad906060810190604001614e6b565b73ffffffffffffffffffffffffffffffffffffffff16610bd36060850160408601614e6b565b73ffffffffffffffffffffffffffffffffffffffff161480610c6357508b8b82818110610c0257610c02615423565b9050602002810190610c149190615452565b610c25906040810190602001614e6b565b73ffffffffffffffffffffffffffffffffffffffff16610c4b6060850160408601614e6b565b73ffffffffffffffffffffffffffffffffffffffff16145b610cc9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453239000000000000000000000000000000000000000000000000000060448201526064016105e5565b600101610a25565b50604080516060810190915260009080610cee6020860186614e6b565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff91909116906370a0823190602401602060405180830381865afa158015610d5a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d7e9190615490565b815260200160008152602001846040016020810190610d9d9190614e6b565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff91909116906370a0823190602401602060405180830381865afa158015610e09573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e2d9190615490565b905290506000610e408d8d8d8d3061216e565b90506000610e4e88866127c7565b90506000610e6260a08701608088016154a9565b6001811115610e7357610e73614ef6565b03610e8a57610e858e8e8c8c85612809565b610e96565b610e968e8e898961293d565b610ea48e8e8e8e8633612a84565b610eae8386612f81565b60016000559e9d5050505050505050505050505050565b6060600260005403610f33576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453138000000000000000000000000000000000000000000000000000060448201526064016105e5565b6002600090815573ffffffffffffffffffffffffffffffffffffffff8516815260046020526040902054600003610fc6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453231000000000000000000000000000000000000000000000000000060448201526064016105e5565b6000610fd5898989898961216e565b6040517c5700000000000000000000000000000000000000000000000000000000815290915073ffffffffffffffffffffffffffffffffffffffff86169060579061102c9033908b908b908a908a90600401615513565b6000604051808303816000875af115801561104b573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820160405261109191908101906155e1565b91506110a189898989858a612a84565b506001600055979650505050505050565b60005b8181101561126457338383838181106110d0576110d0615423565b90506020028101906110e29190615452565b6110f0906020810190614e6b565b73ffffffffffffffffffffffffffffffffffffffff1614158015611126575061112483838381811061052d5761052d615423565b155b1561118d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453230000000000000000000000000000000000000000000000000000060448201526064016105e5565b60006111a484848481811061060557610605615423565b6000818152600c6020526040902080547f800000000000000000000000000000000000000000000000000000000000000017905590507fa6eb7cdc219e1518ced964e9a34e61d68a94e4f1569db3e84256ba981ba527538185858581811061120e5761120e615423565b90506020028101906112209190615452565b61122e906020810190614e6b565b6040805192835273ffffffffffffffffffffffffffffffffffffffff90911660208301520160405180910390a1506001016110b5565b505050565b60015473ffffffffffffffffffffffffffffffffffffffff1633146112ea576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453137000000000000000000000000000000000000000000000000000060448201526064016105e5565b73ffffffffffffffffffffffffffffffffffffffff8216611367576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600560248201527f52533a453100000000000000000000000000000000000000000000000000000060448201526064016105e5565b61138873ffffffffffffffffffffffffffffffffffffffff831633836131c4565b6040805173ffffffffffffffffffffffffffffffffffffffff84168152602081018390527f884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a94243649101610802565b60608080838067ffffffffffffffff8111156113f2576113f261504a565b60405190808252806020026020018201604052801561145b57816020015b60408051606081018252600080825260208083018290529282015282527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9092019101816114105790505b5093508067ffffffffffffffff8111156114775761147761504a565b6040519080825280602002602001820160405280156114a0578160200160208202803683370190505b5092508067ffffffffffffffff8111156114bc576114bc61504a565b6040519080825280602002602001820160405280156114e5578160200160208202803683370190505b50915060005b81811015611601573063d0a46b9b88888481811061150b5761150b615423565b905060200281019061151d9190615452565b6040518263ffffffff1660e01b815260040161153991906156bc565b60a060405180830381865afa925050508015611590575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820190925261158d91810190615787565b60015b156115f957828885815181106115a8576115a8615423565b6020026020010181905250818785815181106115c6576115c6615423565b602002602001018181525050808685815181106115e5576115e5615423565b911515602092830291909101909101525050505b6001016114eb565b50509250925092565b60015473ffffffffffffffffffffffffffffffffffffffff16331461168b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453137000000000000000000000000000000000000000000000000000060448201526064016105e5565b80156116a25761169e6007836002613298565b5050565b61169e6007836002613451565b6060600760000180548060200260200160405190810160405280929190818152602001828054801561171757602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff1681526001909101906020018083116116ec575b5050505050905090565b60015473ffffffffffffffffffffffffffffffffffffffff1633146117a2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453137000000000000000000000000000000000000000000000000000060448201526064016105e5565b73ffffffffffffffffffffffffffffffffffffffff831661181f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600560248201527f52533a453100000000000000000000000000000000000000000000000000000060448201526064016105e5565b60005b825181101561069657600073ffffffffffffffffffffffffffffffffffffffff1683828151811061185557611855615423565b602002602001015173ffffffffffffffffffffffffffffffffffffffff16036118da576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600560248201527f52533a453100000000000000000000000000000000000000000000000000000060448201526064016105e5565b8281815181106118ec576118ec615423565b602002602001015173ffffffffffffffffffffffffffffffffffffffff1663095ea7b38584848151811061192257611922615423565b60200260200101516040518363ffffffff1660e01b815260040161196892919073ffffffffffffffffffffffffffffffffffffffff929092168252602082015260400190565b6020604051808303816000875af1158015611987573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119ab919061580f565b50600101611822565b60015473ffffffffffffffffffffffffffffffffffffffff163314611a35576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453137000000000000000000000000000000000000000000000000000060448201526064016105e5565b600980547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b60015473ffffffffffffffffffffffffffffffffffffffff163314611afd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453137000000000000000000000000000000000000000000000000000060448201526064016105e5565b8015611b105761169e6005836001613298565b61169e6005836001613451565b60025473ffffffffffffffffffffffffffffffffffffffff163314611b9e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453137000000000000000000000000000000000000000000000000000060448201526064016105e5565b611ba73361371e565b565b606060056000018054806020026020016040519081016040528092919081815260200182805480156117175760200282019190600052602060002090815473ffffffffffffffffffffffffffffffffffffffff1681526001909101906020018083116116ec575050505050905090565b606060036000018054806020026020016040519081016040528092919081815260200182805480156117175760200282019190600052602060002090815473ffffffffffffffffffffffffffffffffffffffff1681526001909101906020018083116116ec575050505050905090565b60015473ffffffffffffffffffffffffffffffffffffffff163314611d0a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453137000000000000000000000000000000000000000000000000000060448201526064016105e5565b8015611d1d5761169e6003836000613298565b61169e6003836000613451565b60408051606081018252600080825260208201819052918101829052908080611d5285611d84565b90506000611d648660c00135836137bd565b9050611d75868383600060016138f9565b94509450945050509193909250565b60007f235bf3c2a237ed301bc29f4edaf2b675dcd12f0dea94469169802cafaaee3f307f4319db3766093257e119019721ad33761927ac79912abb48d42c37a7fe85fdfd611dd56020850185614e6b565b611de56040860160208701614e6b565b611df56060870160408801614e6b565b866060013587608001358860a001358960c00135604051602001611e6d98979695949392919097885273ffffffffffffffffffffffffffffffffffffffff96871660208901529486166040880152929094166060860152608085015260a084019290925260c083019190915260e08201526101000190565b60405160208183030381529060405280519060200120604051602001611ec59291907f190100000000000000000000000000000000000000000000000000000000000081526002810192909252602282015260420190565b604051602081830303815290604052805190602001209050919050565b73ffffffffffffffffffffffffffffffffffffffff8216611f5f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600560248201527f52533a453100000000000000000000000000000000000000000000000000000060448201526064016105e5565b336000818152600b6020908152604080832073ffffffffffffffffffffffffffffffffffffffff87168085529083529281902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016861515908117909155815194855291840192909252908201527f6ea9dbe8b2cc119348716a9220a0742ad62b7884ecb0ff4b32cd508121fd937990606001610802565b60015473ffffffffffffffffffffffffffffffffffffffff16331461207a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453137000000000000000000000000000000000000000000000000000060448201526064016105e5565b73ffffffffffffffffffffffffffffffffffffffff81166120f7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600560248201527f52533a453100000000000000000000000000000000000000000000000000000060448201526064016105e5565b600280547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff838116918217909255600154604051919216907fb150023a879fd806e3599b6ca8ee3b60f0e360ab3846d128d67ebce1a391639a90600090a350565b6060848381146121da576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453132000000000000000000000000000000000000000000000000000060448201526064016105e5565b8067ffffffffffffffff8111156121f3576121f361504a565b60405190808252806020026020018201604052801561226157816020015b61224e6040805160e081018252600080825260208201819052918101829052606081018290526080810182905260a081018290529060c082015290565b8152602001906001900390816122115790505b50915060005b818110156127bc5760005b828110156124c257808214158015612317575088888281811061229757612297615423565b90506020028101906122a99190615452565b6122b7906020810190614e6b565b73ffffffffffffffffffffffffffffffffffffffff168989848181106122df576122df615423565b90506020028101906122f19190615452565b6122ff906020810190614e6b565b73ffffffffffffffffffffffffffffffffffffffff16145b8015612453575088888281811061233057612330615423565b90506020028101906123429190615452565b612353906060810190604001614e6b565b73ffffffffffffffffffffffffffffffffffffffff1689898481811061237b5761237b615423565b905060200281019061238d9190615452565b61239e906060810190604001614e6b565b73ffffffffffffffffffffffffffffffffffffffff16148061245357508888828181106123cd576123cd615423565b90506020028101906123df9190615452565b6123f0906060810190604001614e6b565b73ffffffffffffffffffffffffffffffffffffffff1689898481811061241857612418615423565b905060200281019061242a9190615452565b61243b906040810190602001614e6b565b73ffffffffffffffffffffffffffffffffffffffff16145b156124ba576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453135000000000000000000000000000000000000000000000000000060448201526064016105e5565b600101612272565b5060006124da89898481811061060557610605615423565b905061250d8989848181106124f1576124f1615423565b90506020028101906125039190615452565b60c00135826137bd565b84838151811061251f5761251f615423565b602002602001018190525088888381811061253c5761253c615423565b905060200281019061254e9190615452565b61255f906060810190604001614e6b565b73ffffffffffffffffffffffffffffffffffffffff166370a082318a8a8581811061258c5761258c615423565b905060200281019061259e9190615452565b6125ac906020810190614e6b565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b16815273ffffffffffffffffffffffffffffffffffffffff9091166004820152602401602060405180830381865afa158015612615573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126399190615490565b84838151811061264b5761264b615423565b602002602001015160200181815250506126a689898481811061267057612670615423565b90506020028101906126829190615452565b8286858151811061269557612695615423565b6020026020010151600160006138f9565b50505061274b8989848181106126be576126be615423565b90506020028101906126d09190615452565b6126de906020810190614e6b565b868989868181106126f1576126f1615423565b905060200201358c8c8781811061270a5761270a615423565b905060200281019061271c9190615452565b61272d906040810190602001614e6b565b73ffffffffffffffffffffffffffffffffffffffff16929190613c4c565b6127b389898481811061276057612760615423565b90506020028101906127729190615452565b606001358289898681811061278957612789615423565b905060200201358786815181106127a2576127a2615423565b602002602001015160a00151613caa565b50600101612267565b505095945050505050565b60006127db6127d58461582c565b83613e3c565b90506127ed60808301606084016158dc565b1561280357612800602083013582615928565b90505b92915050565b8360018190036128a25761289d8686600081811061282957612829615423565b905060200281019061283b9190615452565b612849906020810190614e6b565b838888600081811061285d5761285d615423565b905060200281019061286f9190615452565b612880906060810190604001614e6b565b73ffffffffffffffffffffffffffffffffffffffff1691906131c4565b612935565b60005b818110156129335761292b8787838181106128c2576128c2615423565b90506020028101906128d49190615452565b6128e2906020810190614e6b565b670de0b6b3a76400008787858181106128fd576128fd615423565b905060200201358661290f919061593b565b6129199190615978565b89898581811061285d5761285d615423565b6001016128a5565b505b505050505050565b82600181116129a8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453133000000000000000000000000000000000000000000000000000060448201526064016105e5565b808214612a11576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453134000000000000000000000000000000000000000000000000000060448201526064016105e5565b60005b8181101561293557612a7c868683818110612a3157612a31615423565b9050602002810190612a439190615452565b612a51906020810190614e6b565b858584818110612a6357612a63615423565b9050602002013588888581811061285d5761285d615423565b600101612a14565b60005b8581101561293357868682818110612aa157612aa1615423565b9050602002810190612ab39190615452565b612ac4906060810190604001614e6b565b73ffffffffffffffffffffffffffffffffffffffff166370a08231888884818110612af157612af1615423565b9050602002810190612b039190615452565b612b11906020810190614e6b565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b16815273ffffffffffffffffffffffffffffffffffffffff9091166004820152602401602060405180830381865afa158015612b7a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b9e9190615490565b838281518110612bb057612bb0615423565b602002602001015160400181815250506000838281518110612bd457612bd4615423565b602002602001015160200151848381518110612bf257612bf2615423565b602002602001015160400151612c089190615928565b90506000888884818110612c1e57612c1e615423565b9050602002810190612c309190615452565b60a0013515612cb057612cab898985818110612c4e57612c4e615423565b9050602002810190612c609190615452565b608001358a8a86818110612c7657612c76615423565b9050602002810190612c889190615452565b60a00135878681518110612c9e57612c9e615423565b60200260200101516141c3565b612cd9565b888884818110612cc257612cc2615423565b9050602002810190612cd49190615452565b608001355b9050848381518110612ced57612ced615423565b602002602001015160a0015115612dc157868684818110612d1057612d10615423565b9050602002013581612d22919061593b565b898985818110612d3457612d34615423565b9050602002810190612d469190615452565b612d5490606001358461593b565b1015612dbc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453233000000000000000000000000000000000000000000000000000060448201526064016105e5565b612e2b565b80821015612e2b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453234000000000000000000000000000000000000000000000000000060448201526064016105e5565b7fcabd156033bc5efebccd321136638073b3e452c01a38d36cbfc9bdec2ffd0f9d898985818110612e5e57612e5e615423565b9050602002810190612e709190615452565b612e7e906020810190614e6b565b858b8b87818110612e9157612e91615423565b9050602002810190612ea39190615452565b612eb4906040810190602001614e6b565b8c8c88818110612ec657612ec6615423565b9050602002810190612ed89190615452565b612ee9906060810190604001614e6b565b8b8b89818110612efb57612efb615423565b90506020020135878b8a81518110612f1557612f15615423565b602090810291909101810151516040805173ffffffffffffffffffffffffffffffffffffffff998a1681529789169288019290925294871686820152929095166060850152608084015260a083019390935260c082015290519081900360e00190a15050600101612a87565b6000612f906020830183614e6b565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff91909116906370a0823190602401602060405180830381865afa158015612ffc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130209190615490565b602084015282516130369060a0840135906159b3565b836020015110156130a3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453235000000000000000000000000000000000000000000000000000060448201526064016105e5565b82604001518260400160208101906130bb9190614e6b565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff91909116906370a0823190602401602060405180830381865afa158015613127573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061314b9190615490565b10156131b3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453236000000000000000000000000000000000000000000000000000060448201526064016105e5565b825160208401516128009190615928565b60405173ffffffffffffffffffffffffffffffffffffffff83166024820152604481018290526112649084907fa9059cbb00000000000000000000000000000000000000000000000000000000906064015b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915261421a565b815160005b8181101561344a5760008482815181106132b9576132b9615423565b60209081029190910181015173ffffffffffffffffffffffffffffffffffffffff8116600090815260018901909252604090912054909150801561335f5784600281111561330957613309614ef6565b6040805173ffffffffffffffffffffffffffffffffffffffff85168152600160208201527f2fceaccc046b9071f648e6e6eadf8f0b7686fd0c3de699cbf926b8617ebc7faf910160405180910390a25050613438565b865461336c8160016159b3565b73ffffffffffffffffffffffffffffffffffffffff841660008181526001808c01602090815260408320949094558b549081018c558b825292902090910180547fffffffffffffffffffffffff00000000000000000000000000000000000000001690911790558560028111156133e5576133e5614ef6565b6040805173ffffffffffffffffffffffffffffffffffffffff86168152600160208201527f2fceaccc046b9071f648e6e6eadf8f0b7686fd0c3de699cbf926b8617ebc7faf910160405180910390a25050505b80613442816159c6565b91505061329d565b5050505050565b815160005b8181101561344a57600084828151811061347257613472615423565b6020026020010151905060008660010160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905080600003613532578460028111156134dc576134dc614ef6565b6040805173ffffffffffffffffffffffffffffffffffffffff85168152600060208201527f2fceaccc046b9071f648e6e6eadf8f0b7686fd0c3de699cbf926b8617ebc7faf910160405180910390a2505061370c565b865460009061354390600190615928565b9050600088600001828154811061355c5761355c615423565b600091825260209091200154895473ffffffffffffffffffffffffffffffffffffffff909116915089908390811061359657613596615423565b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff16896135c4600186615928565b815481106135d4576135d4615423565b600091825260208083209190910180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff948516179055838316825260018c0190526040808220869055918616815290812055885489908061364e5761364e6159fe565b60008281526020902081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90810180547fffffffffffffffffffffffff00000000000000000000000000000000000000001690550190558660028111156136b8576136b8614ef6565b6040805173ffffffffffffffffffffffffffffffffffffffff87168152600060208201527f2fceaccc046b9071f648e6e6eadf8f0b7686fd0c3de699cbf926b8617ebc7faf910160405180910390a2505050505b80613716816159c6565b915050613456565b6001805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681179093556040519116919082907fe9a5158ac7353c7c7322ececc080bc8e89334efa5795b6e21e40eb266b0003d690600090a35050600280547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055565b6137fa6040805160e081018252600080825260208201819052918101829052606081018290526080810182905260a081018290529060c082015290565b67ffffffffffffffff80841690604085901c16700100000000000000000000000000000000851615156000608187901c600381111561383b5761383b614ef6565b9050838310156138a7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453237000000000000000000000000000000000000000000000000000060448201526064016105e5565b6040518060e00160405280878152602001600081526020016000815260200185815260200184815260200183151581526020018260038111156138ec576138ec614ef6565b9052979650505050505050565b60408051606081018252600080825260208201819052918101919091526000806139296060890160408a01614e6b565b73ffffffffffffffffffffffffffffffffffffffff1661394f60408a0160208b01614e6b565b73ffffffffffffffffffffffffffffffffffffffff16036139cc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600560248201527f52533a453500000000000000000000000000000000000000000000000000000060448201526064016105e5565b8683526000878152600c602052604090819020549084018190527f80000000000000000000000000000000000000000000000000000000000000001615613a2f576020830160035b90816004811115613a2757613a27614ef6565b905250613a67565b8760600135836040015110613a4957602083016002613a14565b42866080015111613a5f57602083016004613a14565b600160208401525b841580613a895750600183602001516004811115613a8757613a87614ef6565b145b613aef576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600560248201527f52533a453600000000000000000000000000000000000000000000000000000060448201526064016105e5565b8315613b0257613aff8884614326565b91505b825160c0870151600091613b2291613b1d60e08d018d615a2d565b6143a4565b905073ffffffffffffffffffffffffffffffffffffffff811615801590613bce575073ffffffffffffffffffffffffffffffffffffffff8116613b6860208b018b614e6b565b73ffffffffffffffffffffffffffffffffffffffff161480613bce5750613bce613b9560208b018b614e6b565b73ffffffffffffffffffffffffffffffffffffffff9081166000908152600b602090815260408083209386168352929052205460ff1690565b9150851580613bda5750815b613c40576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600560248201527f52533a453700000000000000000000000000000000000000000000000000000060448201526064016105e5565b50955095509592505050565b60405173ffffffffffffffffffffffffffffffffffffffff808516602483015283166044820152606481018290526106969085907f23b872dd0000000000000000000000000000000000000000000000000000000090608401613216565b8015613d4a576000838152600c6020526040812054613cca9084906159b3565b905084811115613d36576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600560248201527f52533a453200000000000000000000000000000000000000000000000000000060448201526064016105e5565b6000848152600c6020526040902055610696565b838214613db3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600560248201527f52533a453300000000000000000000000000000000000000000000000000000060448201526064016105e5565b6000838152600c602052604090205415613e29576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600560248201527f52533a453400000000000000000000000000000000000000000000000000000060448201526064016105e5565b50506000908152600c6020526040902055565b604082015160009073ffffffffffffffffffffffffffffffffffffffff16613ec0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453139000000000000000000000000000000000000000000000000000060448201526064016105e5565b613ecd6020830183614e6b565b73ffffffffffffffffffffffffffffffffffffffff16836040015173ffffffffffffffffffffffffffffffffffffffff161480613f485750613f156060830160408401614e6b565b73ffffffffffffffffffffffffffffffffffffffff16836040015173ffffffffffffffffffffffffffffffffffffffff16145b613fae576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453332000000000000000000000000000000000000000000000000000060448201526064016105e5565b6040838101518451606086015192517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff91821660048201526024810193909352169063095ea7b3906044016020604051808303816000875af115801561402f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614053919061580f565b50600080846000015173ffffffffffffffffffffffffffffffffffffffff16600086602001516040516140869190615a92565b60006040518083038185875af1925050503d80600081146140c3576040519150601f19603f3d011682016040523d82523d6000602084013e6140c8565b606091505b509150915061410d82826040518060400160405280601981526020017f63616c6c4461746120657865637574696f6e206661696c6564000000000000008152506145ce565b61411881600061461d565b604086810151875191517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff928316600482015260006024820152929550169063095ea7b3906044016020604051808303816000875af1158015614196573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906141ba919061580f565b50505092915050565b6000816080015142106141d75760006141fe565b81606001514210156141ed5781606001516141ef565b425b82608001516141fe9190615928565b614208908461593b565b61421290856159b3565b949350505050565b600061427c826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff1661469d9092919063ffffffff16565b805190915015611264578080602001905181019061429a919061580f565b611264576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f7420737563636565640000000000000000000000000000000000000000000060648201526084016105e5565b600060018260200151600481111561434057614340614ef6565b1461434d57506000612803565b826060013560000361436157506000612803565b6040820151614374906060850135615928565b90506128008161439f61438d6040870160208801614e6b565b61439a6020880188614e6b565b6146b3565b6147e0565b6000808460038111156143b9576143b9614ef6565b036143d0576143c98584846147f6565b9050614212565b60018460038111156143e4576143e4614ef6565b0361443d576040517f19457468657265756d205369676e6564204d6573736167653a0a3332000000006020820152603c81018690526143c990605c016040516020818303038152906040528051906020012084846147f6565b600284600381111561445157614451614ef6565b036145955750813560601c36600061446c8460148188615aa4565b6040517f1626ba7e000000000000000000000000000000000000000000000000000000008152919350915073ffffffffffffffffffffffffffffffffffffffff841690631626ba7e906144c7908a9086908690600401615ace565b602060405180830381865afa92505050801561451e575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820190925261451b91810190615ae8565b60015b61452b576000925061458e565b73ffffffffffffffffffffffffffffffffffffffff7fffffffff00000000000000000000000000000000000000000000000000000000919091167f1626ba7e00000000000000000000000000000000000000000000000000000000140292909216915b5050614212565b506000848152600a602052604090205473ffffffffffffffffffffffffffffffffffffffff60ff90911602823560601c16949350505050565b82156145d957505050565b8151156145e95781518083602001fd5b806040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105e59190614e58565b600061462a8260206159b3565b83511015614694576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600660248201527f52533a453131000000000000000000000000000000000000000000000000000060448201526064016105e5565b50016020015190565b606061421284846000856148f6565b9392505050565b6040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82811660048301523060248301526000916128009185169063dd62ed3e90604401602060405180830381865afa15801561472c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906147509190615490565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85811660048301528616906370a0823190602401602060405180830381865afa1580156147bc573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061439f9190615490565b60008183106147ef5781612800565b5090919050565b600060418214614862576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600560248201527f52533a453800000000000000000000000000000000000000000000000000000060448201526064016105e5565b604080516000815260208082018084528790528583013560f81c92820183905285356060830181905290860135608083018190529092909160019060a0016020604051602081039080840390855afa1580156148c2573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015198975050505050505050565b606082471015614988576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c000000000000000000000000000000000000000000000000000060648201526084016105e5565b73ffffffffffffffffffffffffffffffffffffffff85163b614a06576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016105e5565b6000808673ffffffffffffffffffffffffffffffffffffffff168587604051614a2f9190615a92565b60006040518083038185875af1925050503d8060008114614a6c576040519150601f19603f3d011682016040523d82523d6000602084013e614a71565b606091505b5091509150614a81828286614a8c565b979650505050505050565b60608315614a9b5750816146ac565b825115614aab5782518084602001fd5b816040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105e59190614e58565b60008083601f840112614af157600080fd5b50813567ffffffffffffffff811115614b0957600080fd5b6020830191508360208260051b8501011115614b2457600080fd5b9250929050565b8015158114614b3957600080fd5b50565b8035614b4781614b2b565b919050565b600080600060408486031215614b6157600080fd5b833567ffffffffffffffff811115614b7857600080fd5b614b8486828701614adf565b9094509250506020840135614b9881614b2b565b809150509250925092565b600060208284031215614bb557600080fd5b5035919050565b600060808284031215614bce57600080fd5b50919050565b600060c08284031215614bce57600080fd5b6000806000806000806000806000806101608b8d031215614c0657600080fd5b8a3567ffffffffffffffff80821115614c1e57600080fd5b614c2a8e838f01614adf565b909c509a5060208d0135915080821115614c4357600080fd5b614c4f8e838f01614adf565b909a50985060408d0135915080821115614c6857600080fd5b614c748e838f01614adf565b909850965060608d0135915080821115614c8d57600080fd5b614c998e838f01614bbc565b955060808d0135915080821115614caf57600080fd5b50614cbc8d828e01614adf565b9094509250614cd090508c60a08d01614bd4565b90509295989b9194979a5092959850565b73ffffffffffffffffffffffffffffffffffffffff81168114614b3957600080fd5b8035614b4781614ce1565b60008060008060008060006080888a031215614d2957600080fd5b873567ffffffffffffffff80821115614d4157600080fd5b614d4d8b838c01614adf565b909950975060208a0135915080821115614d6657600080fd5b614d728b838c01614adf565b909750955060408a01359150614d8782614ce1565b90935060608901359080821115614d9d57600080fd5b818a0191508a601f830112614db157600080fd5b813581811115614dc057600080fd5b8b6020828501011115614dd257600080fd5b60208301945080935050505092959891949750929550565b60005b83811015614e05578181015183820152602001614ded565b50506000910152565b60008151808452614e26816020860160208601614dea565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6020815260006128006020830184614e0e565b600060208284031215614e7d57600080fd5b81356146ac81614ce1565b60008060208385031215614e9b57600080fd5b823567ffffffffffffffff811115614eb257600080fd5b614ebe85828601614adf565b90969095509350505050565b60008060408385031215614edd57600080fd5b8235614ee881614ce1565b946020939093013593505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b80518252602081015160058110614f65577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b6020830152604090810151910152565b600081518084526020808501945080840160005b83811015614fa7578151151587529582019590820190600101614f89565b509495945050505050565b60608082528451828201819052600091906020906080850190828901855b82811015614ff357614fe3848351614f25565b9285019290840190600101614fd0565b50505084810382860152865180825290820192508682019060005b8181101561502a5782518552938301939183019160010161500e565b5050505082810360408401526150408185614f75565b9695505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040516080810167ffffffffffffffff8111828210171561509c5761509c61504a565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff811182821017156150e9576150e961504a565b604052919050565b600067ffffffffffffffff82111561510b5761510b61504a565b5060051b60200190565b6000806040838503121561512857600080fd5b823567ffffffffffffffff81111561513f57600080fd5b8301601f8101851361515057600080fd5b80356020615165615160836150f1565b6150a2565b82815260059290921b8301810191818101908884111561518457600080fd5b938201935b838510156151ab57843561519c81614ce1565b82529382019390820190615189565b95506151ba9050868201614b3c565b93505050509250929050565b6020808252825182820181905260009190848201906040850190845b8181101561521457835173ffffffffffffffffffffffffffffffffffffffff16835292840192918401916001016151e2565b50909695505050505050565b600082601f83011261523157600080fd5b81356020615241615160836150f1565b82815260059290921b8401810191818101908684111561526057600080fd5b8286015b8481101561527b5780358352918301918301615264565b509695505050505050565b60008060006060848603121561529b57600080fd5b83356152a681614ce1565b925060208481013567ffffffffffffffff808211156152c457600080fd5b818701915087601f8301126152d857600080fd5b81356152e6615160826150f1565b81815260059190911b8301840190848101908a83111561530557600080fd5b938501935b8285101561532c57843561531d81614ce1565b8252938501939085019061530a565b96505050604087013592508083111561534457600080fd5b505061535286828701615220565b9150509250925092565b6000806040838503121561536f57600080fd5b823561537a81614ce1565b9150602083013561538a81614ce1565b809150509250929050565b6000602082840312156153a757600080fd5b813567ffffffffffffffff8111156153be57600080fd5b820161010081850312156146ac57600080fd5b60a081016153df8286614f25565b8360608301528215156080830152949350505050565b6000806040838503121561540857600080fd5b823561541381614ce1565b9150602083013561538a81614b2b565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0183360301811261548657600080fd5b9190910192915050565b6000602082840312156154a257600080fd5b5051919050565b6000602082840312156154bb57600080fd5b8135600281106146ac57600080fd5b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b73ffffffffffffffffffffffffffffffffffffffff861681526060602082015283606082015260007f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff85111561556857600080fd5b8460051b8087608085013782018281036080908101604085015261558f90820185876154ca565b98975050505050505050565b600067ffffffffffffffff8211156155b5576155b561504a565b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b6000602082840312156155f357600080fd5b815167ffffffffffffffff81111561560a57600080fd5b8201601f8101841361561b57600080fd5b80516156296151608261559b565b81815285602083850101111561563e57600080fd5b61564f826020830160208601614dea565b95945050505050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261568d57600080fd5b830160208101925035905067ffffffffffffffff8111156156ad57600080fd5b803603821315614b2457600080fd5b60208152600082356156cd81614ce1565b73ffffffffffffffffffffffffffffffffffffffff81166020840152506156f660208401614d03565b73ffffffffffffffffffffffffffffffffffffffff811660408401525061571f60408401614d03565b73ffffffffffffffffffffffffffffffffffffffff811660608401525060608301356080830152608083013560a083015260a083013560c083015260c083013560e083015261577160e0840184615658565b6101008481015261564f610120850182846154ca565b600080600083850360a081121561579d57600080fd5b60608112156157ab57600080fd5b506040516060810181811067ffffffffffffffff821117156157cf576157cf61504a565b604052845181526020850151600581106157e857600080fd5b602082015260408581015190820152606085015160808601519194509250614b9881614b2b565b60006020828403121561582157600080fd5b81516146ac81614b2b565b60006080823603121561583e57600080fd5b615846615079565b823561585181614ce1565b815260208381013567ffffffffffffffff81111561586e57600080fd5b840136601f82011261587f57600080fd5b803561588d6151608261559b565b81815236848385010111156158a157600080fd5b818484018583013760008483830101528084860152505050506158c660408401614d03565b6040820152606092830135928101929092525090565b6000602082840312156158ee57600080fd5b81356146ac81614b2b565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b81810381811115612803576128036158f9565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615615973576159736158f9565b500290565b6000826159ae577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b80820180821115612803576128036158f9565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036159f7576159f76158f9565b5060010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112615a6257600080fd5b83018035915067ffffffffffffffff821115615a7d57600080fd5b602001915036819003821315614b2457600080fd5b60008251615486818460208701614dea565b60008085851115615ab457600080fd5b83861115615ac157600080fd5b5050820193919092039150565b83815260406020820152600061564f6040830184866154ca565b600060208284031215615afa57600080fd5b81517fffffffff00000000000000000000000000000000000000000000000000000000811681146146ac57600080fdfea2646970667358221220cedcf2d3206d2775a915263f0bbcc407cafccdf4dea9439be2437f1bcb0e330064736f6c63430008100033

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

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

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