ETH Price: $2,159.93 (+1.14%)

Contract

0xDbC81cfa7f5dF8CfFC5014F4c9540C7bb983Df3D
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To

There are no matching entries

Please try again later

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

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
UniversalRouter

Compiler Version
v0.8.30+commit.73712a01

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion, MIT license
File 1 of 10 : UniversalRouter.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";

/**
 * @title  DSF UniversalRouter
 * @author Andrei Averin — CTO dsf.finance
 * @notice A universal router that combines DEX modules (Curve, UniswapV2, SushiSwap, UniswapV3, etc.)
 *         and performs single and split swaps with automatic commission withholding.
 * @dev    Router:
 * - Requests the best quotes (1-hop and 2-hop) from all modules;
 * - Selects the optimal route or split between the two best;
 * - Pulls tokens from the user, approves modules, and executes the swap;
 * - Charges a fee from the swap (feeBpsSwap) and from positive slippage (feeBpsPositive);
 * - Supports ETH↔WETH, secure calls, and module list management.
 *
 * Uses low-level staticcall to IDexModule.getBestRoute(address,address,uint256)
 * and a unified payload format: 
 * abi.encode(module,index,quotedOut,tokenIn,tokenOut,amountIn,bytes[] route).
 */

/* ─────────────────────────────── Interfaces / Types ─────────────────────────────── */

struct DexRoute { bytes[] data; }

struct Quote {
    address pool;
    int128  i;
    int128  j;
    bool    useUnderlying;
    uint256 amountOut;
}

struct BestAgg {
    bytes payload;
    uint256 amount;
    address module;
    uint256 idx;
}

struct RouteInfo {
    address module;
    uint256 index;
    bytes   payload;  // the same format as in getBestRoute/decodeRoute
    uint256 amount;   // quotedOut
}

struct QuoteArgs {
    address tokenIn;
    address tokenOut;
    uint256 amountIn;
}

struct LegDecoded {
    address module;
    uint256 index;
    uint256 quoted;   // quotedOut from payload
    address tokenIn;
    address tokenOut;
    uint256 amountIn;
    bytes[] route;
}

struct SplitResult {
    address moduleA;
    address moduleB;
    address tokenIn;
    address tokenOut;
    uint256 totalIn;
    uint256 amountInA;
    uint256 amountInB;
    uint256 outA;
    uint256 outB;
    uint256 totalOut;
}

struct TrackedRoute {
    bytes payload;
    uint256 amountOut;
    address module;
    uint256 moduleIndex;
}

struct BestQuotes {
    TrackedRoute top1Hop;
    TrackedRoute second1Hop;
    TrackedRoute top2Hop;
    TrackedRoute second2Hop;
}

struct ModuleQuotes {
    address module;
    uint256 moduleIndex;
    bytes payload1Hop;
    uint256 amountOut1Hop;
    bytes payload2Hop;
    uint256 amountOut2Hop;
}

interface IFeedRegistry {
    function latestRoundData(address base, address quote)
        external
        view
        returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);

    function decimals(address base, address quote) external view returns (uint8);
}

interface IDexModule {
    /**
     * @notice  Compute the best 1-hop and 2-hop routes.
     * @param   tokenIn       Input token
     * @param   tokenOut      Output token
     * @param   amountIn      Input amount
     * @return  best1HopRoute Serialized 1-hop route
     * @return  amountOut1Hop Quoted 1-hop output
     * @return  best2HopRoute Serialized 2-hop route
     * @return  amountOut2Hop Quoted 2-hop output
     */
    function getBestRoute(
        address tokenIn,
        address tokenOut,
        uint256 amountIn
    ) external view returns (
        DexRoute memory best1HopRoute,
        uint256 amountOut1Hop,
        DexRoute memory best2HopRoute,
        uint256 amountOut2Hop
    );

    /**
     * @notice  Execute a previously returned route with a slippage check based on a percentage.
     * @param   route     Serialized route
     * @param   to        Recipient of the final tokens
     * @param   percent   Percentage (0-100) of amountIn from the route to be swapped. 100 = 100%.
     * @return  amountOut Actual output received
     */
    function swapRoute(
        DexRoute calldata route,
        address to,
        uint256 percent
    ) external returns (uint256 amountOut);

    /**
     * @notice  Simulate a route (1–2 hops) encoded as {DexRoute}.
     * @param   route Serialized route
     * @param   percent   Percentage (0-100)
     * @return  amountOut Quoted total output amount
     */
    function simulateRoute(
        DexRoute calldata route,
        uint256 percent
    ) external view returns (uint256 amountOut);
}

interface IWETH {
    function deposit() external payable;
    function withdraw(uint256 amount) external;
    function transfer(address to, uint256 amount) external returns (bool);
    function balanceOf(address) external view returns (uint256);
}

contract UniversalRouter is Ownable, ReentrancyGuard {
    using SafeERC20 for IERC20;

    /* ─────────────────────────────── Storage ─────────────────────────────── */

    address constant DENOM_ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
    address constant DENOM_USD = 0x0000000000000000000000000000000000000348;

    // Chainlink Feed Registry (mainnet)
    address public feedRegistry = 0x47Fb2585D2C56Fe188D0E6ec628a38b74fCeeeDf;

    address public constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
    address[] public modules;                                // list of modules (Curve, UniV2, Sushi, UniV3)
    mapping(address => bool)     public isModule;
    mapping(address => uint256)  private moduleIndexPlusOne; // 1-based for O(1) remove

    /* ───────────────────────────── Fees config ───────────────────────────── */

    address public feeRecipient;                             // commission recipient address
    uint16  public feeBpsSwap;                               // commission in bps (max 10000 = 100%)
    uint16  public feeBpsPositive;                           // commission with positive slippage, bps (max 10000 = 100%)

    /* ────────────────────────────── Fees caps ────────────────────────────── */

    uint16 public constant MAX_FEE_SWAP_BPS      = 100;      // 1%
    uint16 public constant MAX_FEE_POSITIVE_BPS  = 10_000;   // 100%

    /* ─────────────────────────────── Events ──────────────────────────────── */

    event ModuleAdded(address indexed module);
    event ModuleRemoved(address indexed module);
    event ModulesReset(uint256 newCount);
    event FeeConfigUpdated(address indexed recipient, uint16 bpsSwap, uint16 bpsPositive);
    event ERC20Recovered(address indexed token, address indexed to, uint256 amount);
    event ETHSwept(address indexed to, uint256 amount);

    /**
     * @notice Execution of a single swap.
     * @param  module    Module that executed the route.
     * @param  user      Initiator (msg.sender).
     * @param  to        Recipient of the final funds.
     * @param  tokenIn   Input token.
     * @param  tokenOut  Output token.
     * @param  amountIn  Input amount (withdrawn from the user).
     * @param  amountOut Final amount after fees (net).
     * @param  quotedOut Expected output (quota from payload).
     */
    event SwapExecuted(
        address indexed module,
        address indexed user,
        address indexed to,
        address tokenIn,
        address tokenOut,
        uint256 amountIn,
        uint256 amountOut,
        uint256 quotedOut
    );

    /**
     * @notice Execution of a split swap via two routes.
     * @param  user      Initiator (msg.sender).
     * @param  to        Recipient of the final funds.
     * @param  moduleA   Module A.
     * @param  moduleB   Module B.
     * @param  tokenIn   Input token (or WETH for ETH route).
     * @param  tokenOut  Output token.
     * @param  totalIn   Total input.
     * @param  totalOut  Total output (after fees — if the event is emitted after distribution).
     * @param  bpsA      Share A in percent (0–100).
     */
    event SwapSplitExecuted(
        address indexed user,
        address indexed to,
        address moduleA,
        address moduleB,
        address tokenIn,
        address tokenOut,
        uint256 totalIn,
        uint256 totalOut,
        uint16  bpsA
    );

    /* ─────────────────────────────── Errors ─────────────────────────────── */

    error ZeroAddress();
    error DuplicateModule();
    error NotAModule();
    error NoRouteFound();

    /* ─────────────────────────────── Modifiers ─────────────────────────────── */

    modifier onlyERC20(address token) {
        require(token != address(0) && token.code.length > 0, "not ERC20");
        _;
    }

    /* ──────────────────────────────── receive ───────────────────────────────── */

    /// @notice Needed to get native ETH (e.g., with IWETH.withdraw()).
    receive() external payable {}

    /* ─────────────────────────────── Constructor ─────────────────────────────── */

    /**
     * @notice Deploys the router and configures modules and commission parameters.
     * @param  _modules        List of module addresses (Curve/UniV2/UniV3/…).
     * @param  _feeRecipient   Address of the commission recipient.
     * @param  _feeBpsSwap     Swap fee, bps (max. limited by require inside).
     * @param  _feeBpsPositive Positive slippage fee, bps (max. limited by require inside).
     */
    constructor(
        address[] memory _modules, 
        address _feeRecipient, 
        uint16 _feeBpsSwap,
        uint16 _feeBpsPositive
    ) Ownable(msg.sender) {
        _setModules(_modules);
        require(_feeBpsSwap     <= MAX_FEE_SWAP_BPS,     "UR: swap fee too high");
        require(_feeBpsPositive <= MAX_FEE_POSITIVE_BPS, "UR: pos fee too high");
        feeRecipient   = _feeRecipient;
        feeBpsSwap     = _feeBpsSwap;
        feeBpsPositive = _feeBpsPositive;

        emit FeeConfigUpdated(feeRecipient, feeBpsSwap, feeBpsPositive);
    }

    /* ────────────────────────────── Admin: Registry ──────────────────────────── */

    function setFeedRegistry(address reg) external onlyOwner { 
        require(reg != address(0), "UR: bad registry");
        require(reg.code.length > 0, "UR: registry not a contract");
        feedRegistry = reg; 
    }

    /* ──────────────────────────── Admin: modules mgmt ────────────────────────── */

    /**
     * @notice Complete reset of the module list.
     * @dev    Clears old ones, adds new ones, emits ModulesReset.
     * @param  _modules New list of modules.
     */
    function setModules(address[] calldata _modules) external onlyOwner {
        _clearModules();
        _addModules(_modules);
        emit ModulesReset(_modules.length);
    }

    /**
     * @notice Add module to allowlist.
     * @param  module Address of IDexModule module.
     */
    function addModule(address module) external onlyOwner {
        _addModule(module);
    }

    /**
     * @notice Remove module from allowlist.
     * @param  module Address of IDexModule module.
     */
    function removeModule(address module) external onlyOwner {
        _removeModule(module);
    }

    /**
     * @notice Returns the number of connected modules.
     * @return The length of the modules array.
     */
    function modulesLength() external view returns (uint256) {
        return modules.length;
    }

    /**
     * @notice Returns the current list of modules.
     * @dev    The array is returned in memory (a copy of the state).
     * @return An  array of module addresses.
     */
    function getModules() external view returns (address[] memory) {
        return modules;
    }

    /* ────────────────────────────────── Admin ────────────────────────────────── */
    
    /**
     * @notice Rescue all stuck ERC-20 tokens to `to` or owner.
     * @dev    Owner-only. Uses SafeERC20. Always sends the entire token balance
     *         held by this contract. If `to` is zero, defaults to owner().
     * @param  token ERC-20 token address to rescue (must be non-zero).
     * @param  to    Recipient; if zero address, defaults to owner().
     */
    function recoverERC20(address token, address to)
        external
        onlyOwner
        nonReentrant
    {
        require(token != address(0), "UR: token=0");
        address recipient = (to == address(0)) ? owner() : to;

        uint256 amt = IERC20(token).balanceOf(address(this));
        if (amt == 0) return; // nothing to do

        IERC20(token).safeTransfer(recipient, amt);
        emit ERC20Recovered(token, recipient, amt);
    }

    /**
     * @notice Transfers all remaining ETH from the contract to the owner or specified address.
     * @dev    Only for the owner (onlyOwner). Added nonReentrant to protect against repeated calls.
     *         If the `to` parameter is equal to a zero address, the funds are sent to the contract owner.
     * @param  to The address of the ETH recipient (if 0x0 — send to the owner).
     */
    function sweepETH(address to)
        external
        onlyOwner
        nonReentrant
    {
        address recipient = (to == address(0)) ? owner() : to;
        uint256 bal = address(this).balance;
        (bool ok,) = recipient.call{value: bal}("");
        require(ok, "ETH sweep failed");
        emit ETHSwept(recipient, bal);
    }

    /* ───────────────────────────────── Admin: fee ────────────────────────────── */

    /**
     * @notice Update the address of the commission recipient.
     * @param  _recipient New address of the fee recipient.
     */
    function setFeeRecipient(address _recipient) external onlyOwner {
        feeRecipient = _recipient;
        emit FeeConfigUpdated(feeRecipient, feeBpsSwap, feeBpsPositive);
    }

    /**
     * @notice Update commission percentages.
     * @dev    Upper limit checked via require; emits FeeConfigUpdated.
     * @param  _feeBpsSwap Swap commission, bps.
     * @param  _feeBpsPositive Positive slippage commission, bps.
     */
    function setFeePercents(uint16 _feeBpsSwap, uint16 _feeBpsPositive) external onlyOwner {
        require(_feeBpsSwap     <= MAX_FEE_SWAP_BPS,     "UR: swap fee too high");
        require(_feeBpsPositive <= MAX_FEE_POSITIVE_BPS, "UR: pos fee too high");
        feeBpsSwap     = _feeBpsSwap;
        feeBpsPositive = _feeBpsPositive;
        emit FeeConfigUpdated(feeRecipient, feeBpsSwap, feeBpsPositive);
    }

    /**
     * @notice Completely reinstalls the list of modules.
     * @dev    Clears the current modules, then adds new ones. Emits ModulesReset.
     * @param  _modules New list of modules.
     */
    function _setModules(address[] memory _modules) internal {
        _clearModules();
        uint256 n = _modules.length;
        for (uint256 i; i < n; ) {
            _addModule(_modules[i]);
            unchecked { ++i; }
        }
        emit ModulesReset(n);
    }

    /**
     * @notice Resets (clears) all modules.
     * @dev    Resets isModule and indexes; clears the modules array.
     */
    function _clearModules() internal {
        uint256 n = modules.length;
        for (uint256 i; i < n; ) {
            address m = modules[i];
            isModule[m] = false;
            moduleIndexPlusOne[m] = 0;
            unchecked { ++i; }
        }
        delete modules;
    }

    /**
     * @notice Adds modules in bulk.
     * @dev    Calls _addModule for each address.
     * @param  _modules List of module addresses.
     */
    function _addModules(address[] calldata _modules) internal {
        uint256 n = _modules.length;
        for (uint256 i; i < n; ) {
            _addModule(_modules[i]);
            unchecked { ++i; }
        }
    }

    /**
     * @notice Adds one module to the allowlist.
     * @dev    Checks for a non-zero address, the presence of code, and the absence of duplicates.
     *         Updates isModule, modules, and moduleIndexPlusOne. Emits ModuleAdded.
     * @param  module The module contract address.
     */
    function _addModule(address module) internal {
        if (module == address(0)) revert ZeroAddress();
        if (isModule[module]) revert DuplicateModule();

        // (опционально) минимальная проверка кода
        if (module.code.length == 0) revert ZeroAddress();

        isModule[module] = true;
        modules.push(module);
        moduleIndexPlusOne[module] = modules.length; // 1-based
        emit ModuleAdded(module);
    }

    /**
     * @notice Removes a module from the allowlist.
     * @dev    Performs O(1) removal via swap-pop, supporting 1-based indexing.
     *         Emit ModuleRemoved.
     * @param  module Address of the module to be removed.
     */
    function _removeModule(address module) internal {
        uint256 idxPlusOne = moduleIndexPlusOne[module];
        if (idxPlusOne == 0) revert NotAModule();

        uint256 idx = idxPlusOne - 1;
        uint256 lastIdx = modules.length - 1;

        if (idx != lastIdx) {
            address last = modules[lastIdx];
            modules[idx] = last;
            moduleIndexPlusOne[last] = idx + 1;
        }
        modules.pop();

        isModule[module] = false;
        moduleIndexPlusOne[module] = 0;
        emit ModuleRemoved(module);
    }

    /* ─────────────────────────────── WETH Helpers ────────────────────────────── */
    
    /**
     * @dev    Wraps incoming native ETH into WETH.
     * @param  amount Amount of ETH to wrap (msg.value).
     */
    function _wrapETH(uint256 amount) internal {
        IWETH(WETH).deposit{value: amount}();
    }

    /**
     * @dev    Converts WETH back to ETH and sends it to the recipient.
     * @param  amount Amount of WETH to convert.
     * @param  to Recipient's native ETH address.
     */
    function _unwrapWETHAndSend(uint256 amount, address to) internal {
        require(IWETH(WETH).balanceOf(address(this)) >= amount, "UR: insufficient WETH");
        IWETH(WETH).withdraw(amount);
        // Send native ETH
        (bool success,) = to.call{value: amount}("");
        require(success, "UR: ETH transfer failed");
    }

    /* ───────────────────────────── ETH payout/guards ─────────────────────────── */

    /**
     * @notice Ensures that tokenIn == WETH in the input payload.
     * @dev    Reads the address from slot 3 of the payload ABI header (see _loadAddressFromPayload).
     * @param  payload ABI-encoded route: (module,index,quotedOut,tokenIn,tokenOut,amountIn,bytes[]).
     */
    function _requireWethIn(bytes calldata payload) internal pure {
        address tokenIn = _loadAddressFromPayload(payload, 3);
        require(tokenIn == WETH, "UR: payload tokenIn != WETH");
    }

    /**
     * @notice Ensures that tokenOut == WETH in the output payload.
     * @dev    Reads the address from the ABI header payload in slot 4 (see _loadAddressFromPayload).
     * @param  payload ABI-encoded route: (module,index,quotedOut,tokenIn,tokenOut,amountIn,bytes[]).
     */
    function _requireWethOut(bytes calldata payload) internal pure {
        address tokenOut = _loadAddressFromPayload(payload, 4);
        require(tokenOut == WETH, "UR: payload tokenOut != WETH");
    }

    /**
     * @notice Quick reading of the address from the ABI header payload.
     * @dev    The slot corresponds to the position of a 32-byte word in abi.encode(...).
     *         0: module, 1: index, 2: quotedOut, 3: tokenIn, 4: tokenOut, 5: amountIn, 6: offset(bytes[]).
     * @param  payload Full ABI payload.
     * @param  slot Slot number (0-based).
     * @return a Address read from the specified slot.
     */
    function _loadAddressFromPayload(bytes calldata payload, uint256 slot) internal pure returns (address a) {
        assembly ("memory-safe") {
            a := shr(96, calldataload(add(payload.offset, mul(slot, 32))))
        }
    }

    /* ────────────────────────────────── Helpers ──────────────────────────────── */

    /**
     * @notice Updates the best quotes in 1-hop and 2-hop segments.
     * @dev    Supports “top-1” and “top-2” for each category.
     * @param  currentBest Current best routes.
     * @param  newRoute Candidate for inclusion.
     * @param  is1Hop 1-hop (true) or 2-hop (false) flag.
     */
    function _updateBestQuotes(BestQuotes memory currentBest, TrackedRoute memory newRoute, bool is1Hop) private pure {
        if (is1Hop) {
            if (newRoute.amountOut > currentBest.top1Hop.amountOut) {
                currentBest.second1Hop = currentBest.top1Hop;
                currentBest.top1Hop = newRoute;
            } else if (newRoute.amountOut > currentBest.second1Hop.amountOut) {
                currentBest.second1Hop = newRoute;
            }
        } else { // 2-hop
            if (newRoute.amountOut > currentBest.top2Hop.amountOut) {
                currentBest.second2Hop = currentBest.top2Hop;
                currentBest.top2Hop = newRoute;
            } else if (newRoute.amountOut > currentBest.second2Hop.amountOut) {
                currentBest.second2Hop = newRoute;
            }
        }
    }

    /**
     * @notice Updates the two absolute best routes found so far (overall Top-1 and Top-2).
     * @dev    If the new route beats Top-1, it becomes Top-1 and the old Top-1 shifts to Top-2.
     *         Otherwise, if it only beats Top-2, it replaces Top-2.
     * @param  top1     Current absolute best route (Top-1).
     * @param  top2     Current second absolute best route (Top-2).
     * @param  newRoute Newly observed candidate route to compare against the tops.
     * @return Updated  Top-1 and Top-2 routes (in this order).
     */
    function _updateTopOverall(
        TrackedRoute memory top1,
        TrackedRoute memory top2,
        TrackedRoute memory newRoute
    ) private pure returns (TrackedRoute memory, TrackedRoute memory) {
        if (newRoute.amountOut > top1.amountOut) {
            top2 = top1;
            top1 = newRoute;
        } else if (newRoute.amountOut > top2.amountOut) {
            top2 = newRoute;
        }
        return (top1, top2);
    }

    /**
     * @notice Queries a module for the best 1-hop and 2-hop quotes and packages them as payloads.
     * @dev    Calls IDexModule.getBestRoute via staticcall and, if non-zero quotes are returned,
     *         encodes payloads as abi.encode(module, index, quotedOut, tokenIn, tokenOut, amountIn, route.data).
     *         If the module is not registered or the call fails/returns empty, the struct remains zeroed.
     * @param  m   Module address being queried.
     * @param  idx Module index (stored for payload bookkeeping).
     * @param  a   Quote arguments (tokenIn, tokenOut, amountIn).
     * @return quotes Struct holding module info, 1-hop/2-hop amounts and payloads (if any).
     */
    function _getModuleQuotes(
        address m,
        uint256 idx,
        QuoteArgs memory a
    ) internal view returns (ModuleQuotes memory quotes) {
        quotes.module = m;
        quotes.moduleIndex = idx;
        
        if (!isModule[m]) return quotes;

        bytes memory cd = abi.encodeWithSelector(
            IDexModule.getBestRoute.selector,
            a.tokenIn,
            a.tokenOut,
            a.amountIn
        );

        (bool success, bytes memory ret) = m.staticcall(cd);
        if (!success || ret.length == 0) return quotes;

        (
            DexRoute memory route1, uint256 out1,
            DexRoute memory route2, uint256 out2
        ) = abi.decode(ret, (DexRoute, uint256, DexRoute, uint256));

        // Build payloads only for non-zero, non-empty routes.
        if (out1 > 0 && route1.data.length > 0) {
            quotes.amountOut1Hop = out1;
            quotes.payload1Hop = abi.encode(
                m, idx, out1, a.tokenIn, a.tokenOut, a.amountIn, route1.data
            );
        }
        
        if (out2 > 0 && route2.data.length > 0) {
            quotes.amountOut2Hop = out2;
            quotes.payload2Hop = abi.encode(
                m, idx, out2, a.tokenIn, a.tokenOut, a.amountIn, route2.data
            );
        }
    }

    /**
     * @dev    Private helper function for calculating the total output amount.
     * @param  percentA Percentage of amountIn for Route A (0-100).
     */
    function _calculateTotalOut(
        address moduleA,
        bytes[] memory routeA,
        address moduleB,
        bytes[] memory routeB,
        uint16 percentA // 0-100
    ) internal view returns (uint256 totalOut) {
        uint16 percentB = 100 - percentA;
        
        // simulateRoute for A (percent 0–100)
        uint256 outA = IDexModule(moduleA).simulateRoute(DexRoute({ data: routeA }), percentA);
        
        // simulateRoute for B (percent 0–100)
        uint256 outB = IDexModule(moduleB).simulateRoute(DexRoute({ data: routeB }), percentB);
        
        return outA + outB;
    }

    /**
     * @notice Safely sets the allowance to the required minimum.
     * @dev    If the current allowance < amount, first set it to zero (if >0), then set it to type(uint256).max.
     *         Uses    SafeERC20.forceApprove for maximum compatibility.
     * @param  token   ERC20 token address.
     * @param  spender Contract address to which we issue the allowance.
     * @param  amount  Minimum required limit.
     */
    function _smartApprove(address token, address spender, uint256 amount) internal {
        uint256 cur = IERC20(token).allowance(address(this), spender);
        if (cur < amount) {
            if (cur > 0) IERC20(token).forceApprove(spender, 0);
            IERC20(token).forceApprove(spender, type(uint256).max);
        }
    }

    /**
     * @notice Emits the consolidated split-swap execution event.
     * @dev    Packs the essential split data into a single event for off-chain indexing/analytics.
     * @param  r       Split result struct (modules, tokens, totals).
     * @param  user    Original caller (initiator).
     * @param  to      Final receiver of the swapped tokens/ETH.
     * @param  bpsA    Portion routed through module A, in percent (0–100).
     */
    function _emitSwapSplit(
        SplitResult memory r,
        address user,
        address to,
        uint16 bpsA
    ) internal {
        emit SwapSplitExecuted(
            user,
            to,
            r.moduleA,
            r.moduleB,
            r.tokenIn,
            r.tokenOut,
            r.totalIn,
            r.totalOut,
            bpsA
        );
    }

    /**
     * @notice Decodes a route payload (in memory) into a typed struct used by the router.
     * @dev    Expects payload encoded as:
     *         (address module, uint256 index, uint256 quoted, address tokenIn, address tokenOut, uint256 amountIn, bytes[] route)
     * @param  payload ABI-encoded payload stored in memory.
     * @return d       Decoded LegDecoded struct.
     */
    function _decodeRouteStruct(bytes memory payload)
        internal
        pure
        returns (LegDecoded memory d)
    {
        (d.module, d.index, d.quoted, d.tokenIn, d.tokenOut, d.amountIn, d.route) =
            abi.decode(payload, (address, uint256, uint256, address, address, uint256, bytes[]));
    }

    /**
     * @notice Decodes a route payload (in calldata) into a typed struct used by the router.
     * @dev    Same layout as the memory version, but reads directly from calldata to save gas.
     * @param  payload ABI-encoded payload residing in calldata.
     * @return d       Decoded LegDecoded struct.
     */
    function _decodeRouteStructCallData(bytes calldata payload)
        internal
        pure
        returns (LegDecoded memory d)
    {
        (d.module, d.index, d.quoted, d.tokenIn, d.tokenOut, d.amountIn, d.route) =
            abi.decode(payload, (address, uint256, uint256, address, address, uint256, bytes[]));
    }

    /**
     * @notice Distribution of commissions and ERC20 transfer.
     * @dev    Retains fix-fee (feeBpsSwap) and % of positive slippage (feeBpsPositive).
     * @param  token        ERC20 address.
     * @param  to           Recipient.
     * @param  grossOut     Actual output after swap(s).
     * @param  quotedOut    Quote (expectation).
     * @param  minOut       Minimum acceptable output.
     * @return netOut       Amount after commissions.
     */
    function _distributeTokenWithFees(
        address token,
        address to,
        uint256 grossOut,         // actual output after swap(s)
        uint256 quotedOut,        // quoted (expected) output
        uint256 minOut
    ) internal returns (uint256 netOut) {
        if (grossOut == 0) return 0;

        uint256 baseline = quotedOut > minOut ? quotedOut : minOut;

        uint256 feeSwap = 0;
        uint256 feePos  = 0;

        // take fees only if recipient is set and bps > 0
        if (feeRecipient != address(0)) {
            if (feeBpsSwap > 0) {
                unchecked { feeSwap = (grossOut * feeBpsSwap) / 10_000; }
            }
            if (feeBpsPositive > 0 && grossOut > baseline) {
                unchecked { feePos = ((grossOut - baseline) * feeBpsPositive) / 10_000; }
            }
        }

        uint256 totalFee = feeSwap + feePos;
        // safety guard against overflow/rounding:
        if (totalFee > grossOut) totalFee = grossOut;

        netOut = grossOut - totalFee;

        // Payouts: send fee to feeRecipient first, then net to user
        if (totalFee > 0) {
            IERC20(token).safeTransfer(feeRecipient, totalFee);
        }
        IERC20(token).safeTransfer(to, netOut);
    }

    /**
     * @notice Distribution of fees and transfer of ETH.
     * @dev    Similar      to _distributeTokenWithFees, but for ETH.
     * @param  to           Recipient.
     * @param  grossEth     Actual ETH output.
     * @param  quotedOutEth Expected output.
     * @param  minOutEth    Minimum allowable output.
     * @return netOut       Amount after fees.
     */
    function _distributeETHWithFees(
        address to,
        uint256 grossEth,         // actual ETH output
        uint256 quotedOutEth,     // expected output (WETH==ETH)
        uint256 minOutEth
    ) internal returns (uint256 netOut) {
        if (grossEth == 0) return 0;

        uint256 baseline = quotedOutEth > minOutEth ? quotedOutEth : minOutEth;

        uint256 feeSwap = 0;
        uint256 feePos  = 0;

        if (feeRecipient != address(0)) {
            if (feeBpsSwap > 0) {
                unchecked { feeSwap = (grossEth * feeBpsSwap) / 10_000; }
            }
            if (feeBpsPositive > 0 && grossEth > baseline) {
                unchecked { feePos = ((grossEth - baseline) * feeBpsPositive) / 10_000; }
            }
        }

        uint256 totalFee = feeSwap + feePos;
        if (totalFee > grossEth) totalFee = grossEth;

        netOut = grossEth - totalFee;

        if (totalFee > 0) {
            (bool fs, ) = feeRecipient.call{value: totalFee}("");
            require(fs, "fee ETH xfer failed");
        }
        (bool ok, ) = to.call{value: netOut}("");
        require(ok, "ETH xfer failed");
    }

    /**
     * @notice Safely reads balance, allowance, and decimals for a (token, wallet, spender).
     * @dev    For ETH (token==address(0)): returns (wallet.balance, 0, 18).
     *         Uses low-level staticcall to tolerate non-standard ERC-20s (e.g., USDT).
     *         If {decimals()} cannot be read, returns 0 as a sentinel value.
     * @param  token   ERC-20 token address (or address(0) for ETH).
     * @param  wallet  Address whose balance is queried.
     * @param  spender Address whose allowance is checked.
     * @return bal     Token balance of {wallet} (or ETH balance if token==address(0)).
     * @return allow_  Current allowance from {wallet} to {spender} (0 for ETH).
     * @return decs    Token decimals (18 for ETH; 0 if unreadable).
     */
    function _safeBalanceAndAllowance(
        address token,
        address wallet,
        address spender
    ) internal view returns (uint256 bal, uint256 allow_, uint8 decs) {
        if (token == address(0)) {
            // ETH: allowance not applicable
            return (wallet.balance, 0, 18);
        }

        // balanceOf(wallet)
        (bool ok1, bytes memory data1) =
            token.staticcall(abi.encodeWithSelector(IERC20.balanceOf.selector, wallet));
        if (ok1 && data1.length >= 32) {
            bal = abi.decode(data1, (uint256));
        } else {
            bal = 0;
        }

        // allowance(wallet, spender)
        (bool ok2, bytes memory data2) =
            token.staticcall(abi.encodeWithSelector(IERC20.allowance.selector, wallet, spender));
        if (ok2 && data2.length >= 32) {
            allow_ = abi.decode(data2, (uint256));
        } else {
            allow_ = 0;
        }

        // decimals() selector = 0x313ce567
        (bool ok3, bytes memory data3) =
            token.staticcall(abi.encodeWithSelector(bytes4(0x313ce567)));
        if (ok3 && data3.length >= 32) {
            // Some tokens return uint256, some return uint8. Read as uint256 and cast safely.            
            uint256 d = abi.decode(data3, (uint256));
            decs = d > 255 ? uint8(255) : uint8(d);
        } else {
            // if reading failed — 0, so that the caller understands that it is undefined
            decs = 0;
        }
    }

    /**
     * @notice Reads the base/quote price from the Chainlink Feed Registry and normalizes it to 1e18.
     * @dev    Never reverts. Returns (price1e18=0, updatedAt=0) if the feed is missing or invalid.
     *         No freshness check is performed here; callers may validate staleness separately.
     * @param  base       Address of the base asset (token address or Chainlink denomination, e.g. DENOM_ETH).
     * @param  quote      Address of the quote asset (typically DENOM_USD).
     * @return price1e18  Base/quote price scaled to 1e18 (0 if unavailable).
     * @return updatedAt  Timestamp of the last feed update (0 if unavailable).
     */
    function _pairPrice1e18(address base, address quote)
        private
        view
        returns (uint256 price1e18, uint256 updatedAt)
    {
        address reg = feedRegistry;
        if (reg == address(0)) return (0, 0);

        try IFeedRegistry(reg).latestRoundData(base, quote) returns (
            uint80, int256 answer, uint256, uint256 upd, uint80
        ) {
            if (answer <= 0) return (0, upd);
            uint8 dec = IFeedRegistry(reg).decimals(base, quote);
            uint256 u = uint256(answer);
            if (dec < 18) price1e18 = u * 10 ** (18 - dec);
            else if (dec > 18) price1e18 = u / 10 ** (dec - 18);
            else price1e18 = u;
            return (price1e18, upd);
        } catch {
            return (0, 0);
        }
    }

    /**
     * @notice Returns the price of 1 token in USD (1e18) via Chainlink Feed Registry.
     * @dev    Never reverts. **Does not** check data freshness; returns whatever the registry holds.
     *         Resolution order:
     *           - If `asEth == true` or `token == address(0)`: use ETH/USD;
     *           - Else try direct TOKEN/USD;
     *           - If `token == WETH`: use ETH/USD;
     *           - Else try TOKEN/ETH × ETH/USD.
     * @param  token      Token address (or address(0) for ETH).
     * @param  asEth      If true, force using the ETH/USD price (e.g., for WETH, stETH, etc.).
     * @return price1e18  The price of 1 token in USD (1e18), or 0 if unavailable.
     * @return updatedAt  The timestamp of the last feed update used for the computation.
     */
    function _tryTokenUsdPrice1e18(address token, bool asEth)
        internal
        view
        returns (uint256 price1e18, uint256 updatedAt)
    {
        // ETH or forcibly as ETH
        if (asEth || token == address(0)) {
            (price1e18, updatedAt) = _pairPrice1e18(DENOM_ETH, DENOM_USD);
            if (price1e18 != 0) return (price1e18, updatedAt);
        }

        // Direct TOKEN/USD
        (price1e18, updatedAt) = _pairPrice1e18(token, DENOM_USD);
        if (price1e18 != 0) return (price1e18, updatedAt);

        // WETH → ETH/USD
        if (token == WETH) {
            (price1e18, updatedAt) = _pairPrice1e18(DENOM_ETH, DENOM_USD);
            return (price1e18, updatedAt);
        }

        // Attempt via TOKEN/ETH × ETH/USD (relevant for LST, etc.)
        (uint256 tEth, uint256 updA) = _pairPrice1e18(token, DENOM_ETH);
        if (tEth == 0) return (0, updA);
        (uint256 ethUsd, uint256 updB) = _pairPrice1e18(DENOM_ETH, DENOM_USD);
        if (ethUsd == 0) return (0, updB);

        uint256 minUpd = updA < updB ? updA : updB;

        return ((tEth * ethUsd) / 1e18, minUpd);
    }

    /* ──────────────────────────────────── Read ───────────────────────────────── */

    /**
     * @notice Reads token→USD price (1e18) via Chainlink Feed Registry without checking “freshness”.
     * @dev    Returns 0 if the feed is missing or `answer <= 0`. ETH is passed as address(0).
     * @param  token           Token address (or address(0) for ETH).
     * @param  asEth           If true, use ETH/USD price (for WETH, stETH, etc.).
     * @return usdPerToken1e18 Token price in USD (1e18) or 0.
     * @return updatedAt       Time of the last feed update.
     */
    function tryTokenUsdPrice1e18(address token, bool asEth)
        external
        view
        returns (uint256 usdPerToken1e18, uint256 updatedAt)
    {
        (usdPerToken1e18, updatedAt) = _tryTokenUsdPrice1e18(token, asEth);
        return (usdPerToken1e18, updatedAt);
    }

    /**
     * @notice Returns balance, allowance, and decimals for a (token, wallet, spender).
     * @dev    For ETH (token==address(0)): returns (wallet.balance, 0, 18).
     *         Decimals is 0 if the token's {decimals()} cannot be read.
     * @param  token      ERC-20 token address (or address(0) for ETH).
     * @param  wallet     Address whose balance is queried.
     * @param  spender    Address whose allowance is checked.
     * @return balance    Token balance of {wallet} (or ETH balance if token==address(0)).
     * @return allowance_ Current allowance from {wallet} to {spender} (0 for ETH).
     * @return decimals_  Token decimals (18 for ETH; 0 if unreadable).
     */
    function balanceAndAllowanceOf(
        address token,
        address wallet,
        address spender
    ) external view returns (uint256 balance, uint256 allowance_, uint8   decimals_) {
        (balance, allowance_, decimals_) = _safeBalanceAndAllowance(token, wallet, spender);
    }

    /**
     * @notice Returns balance, allowance, decimals, and USD price for a single token.
     * @dev    For ETH (token==address(0)): returns (wallet.balance, 0, 18) and uses ETH/USD price when {asEth} is true.
     *         Decimals is 0 if the token's {decimals()} cannot be read.
     * @param  token           ERC-20 token address (or address(0) for ETH).
     * @param  wallet          Address whose balance is queried.
     * @param  spender         Address whose allowance is checked.
     * @param  asEth           If true, forces the ETH/USD feed for this token.
     * @return balance         Token balance of {wallet} (or ETH balance if token==address(0)).
     * @return allowance_      Current allowance from {wallet} to {spender} (0 for ETH).
     * @return usdPerToken1e18 Price of 1 token in USD (scaled to 1e18), 0 if no feed/<=0.
     * @return updatedAt       Timestamp of the last price update.
     * @return decimals_       Token decimals (18 for ETH; 0 if unreadable).
     */
    function balanceAllowanceAndUsd(
        address token,
        address wallet,
        address spender, 
        bool    asEth
    )
        external
        view
        returns (
            uint256 balance,
            uint256 allowance_,
            uint256 usdPerToken1e18,
            uint256 updatedAt,
            uint8   decimals_
        )
    {
        // 1) safely read balance/allowance (do not revert to non-standard ERC-20)
        (balance, allowance_, decimals_) = _safeBalanceAndAllowance(token, wallet, spender);

        // 2) Let's try to get the price in USD (gently, without revert)
        (usdPerToken1e18, updatedAt) = _tryTokenUsdPrice1e18(token, asEth);
    }

    /**
     * @notice Returns balance, allowance, decimals, and USD price for two tokens at once.
     * @dev    For ETH (token==address(0)): returns (wallet.balance, 0, 18) and uses ETH/USD price if requested via {asEthIn}/{asEthOut}.
     *         Decimals is 0 if a token's {decimals()} cannot be read.
     * @param  wallet             Address whose balances are queried.
     * @param  spender            Address whose allowances are checked.
     * @param  tokenIn            First token address (or address(0) for ETH).
     * @param  asEthIn            If true, forces ETH/USD feed for tokenIn.
     * @param  tokenOut           Second token address (or address(0) for ETH).
     * @param  asEthOut           If true, forces ETH/USD feed for tokenOut.
     * @return balanceIn          {wallet} balance of tokenIn (ETH if tokenIn==address(0)).
     * @return allowance_In       Allowance of tokenIn to {spender} (0 for ETH).
     * @return usdPerToken1e18In  TokenIn USD price normalized to 1e18 (0 if no feed/<=0).
     * @return updatedAtIn        Timestamp of the last price update for tokenIn.
     * @return decimals_In        Decimals of tokenIn (18 for ETH; 0 if unreadable).
     * @return balanceOut         {wallet} balance of tokenOut (ETH if tokenOut==address(0)).
     * @return allowance_Out      Allowance of tokenOut to {spender} (0 for ETH).
     * @return usdPerToken1e18Out TokenOut USD price normalized to 1e18 (0 if no feed/<=0).
     * @return updatedAtOut       Timestamp of the last price update for tokenOut.
     * @return decimals_Out       Decimals of tokenOut (18 for ETH; 0 if unreadable).
     */
    function balanceAllowanceAndUsdDouble(
        address wallet,
        address spender, 
        address tokenIn,
        bool    asEthIn,
        address tokenOut, 
        bool    asEthOut
    )
        external
        view
        returns (
            uint256 balanceIn,
            uint256 allowance_In,
            uint256 usdPerToken1e18In,
            uint256 updatedAtIn,
            uint8   decimals_In,
            uint256 balanceOut,
            uint256 allowance_Out,
            uint256 usdPerToken1e18Out,
            uint256 updatedAtOut,
            uint8   decimals_Out
        )
    {
        // 1) safely read balance/allowance (do not revert to non-standard ERC-20)
        (balanceIn, allowance_In, decimals_In) = _safeBalanceAndAllowance(tokenIn, wallet, spender);
        (balanceOut, allowance_Out, decimals_Out) = _safeBalanceAndAllowance(tokenOut, wallet, spender);

        // 2) Let's try to get the price in USD (gently, without revert)
        (usdPerToken1e18In, updatedAtIn) = _tryTokenUsdPrice1e18(tokenIn, asEthIn);
        (usdPerToken1e18Out, updatedAtOut) = _tryTokenUsdPrice1e18(tokenOut, asEthOut);
    }

    /**
     * @notice Return the 4 best routes (Top-1/Top-2 for 1-hop and 2-hop) and (optionally) the optimal split of the two absolute leaders.
     * @param  tokenIn            Input token.
     * @param  tokenOut           Output token.
     * @param  amountIn           Input amount.
     * @return best1HopRouteTop1  Payload of the best 1-hop.
     * @return amountOut1HopTop1  Quote of the best 1-hop.
     * @return best2HopRouteTop1  Payload of the best 2-hop.
     * @return amountOut2HopTop1  Quote for the best 2-hop.
     * @return best1HopRouteTop2  Payload of the second 1-hop.
     * @return amountOut1HopTop2  Quote for the second 1-hop.
     * @return best2HopRouteTop2  Payload of the second 2-hop.
     * @return amountOut2HopTop2  Quote for the second 2-hop.
     * @return splitAmountOut     Best split quote between two absolute tops (0 if split does not improve).
     * @return splitPercentA      Share for route A (in percent, 0–100) for split (0 if split is not applicable).
     */
    function getBestRoute(
        address tokenIn,
        address tokenOut,
        uint256 amountIn
    ) external view returns (
        bytes memory best1HopRouteTop1, uint256 amountOut1HopTop1,
        bytes memory best2HopRouteTop1, uint256 amountOut2HopTop1,
        bytes memory best1HopRouteTop2, uint256 amountOut1HopTop2,
        bytes memory best2HopRouteTop2, uint256 amountOut2HopTop2,
        uint256 splitAmountOut, uint16 splitPercentA
    ) {
        QuoteArgs memory qa = QuoteArgs({
            tokenIn: tokenIn,
            tokenOut: tokenOut,
            amountIn: amountIn
        });

        BestQuotes memory best; 
        TrackedRoute memory top1Overall; // Absolute best route
        TrackedRoute memory top2Overall; // Second best route

        for (uint256 i = 0; i < modules.length; ) {
            ModuleQuotes memory quotes = _getModuleQuotes(modules[i], i, qa);

            if (quotes.amountOut1Hop > 0) {
                TrackedRoute memory r1 = TrackedRoute({
                    payload: quotes.payload1Hop,
                    amountOut: quotes.amountOut1Hop,
                    module: quotes.module,
                    moduleIndex: quotes.moduleIndex
                });
                _updateBestQuotes(best, r1, true); 
                (top1Overall, top2Overall) = _updateTopOverall(top1Overall, top2Overall, r1);
            }

            if (quotes.amountOut2Hop > 0) {
                TrackedRoute memory r2 = TrackedRoute({
                    payload: quotes.payload2Hop,
                    amountOut: quotes.amountOut2Hop,
                    module: quotes.module,
                    moduleIndex: quotes.moduleIndex
                });
                _updateBestQuotes(best, r2, false); 
                (top1Overall, top2Overall) = _updateTopOverall(top1Overall, top2Overall, r2);
            }

            unchecked { ++i; }
        }

        if (top1Overall.amountOut == 0) revert NoRouteFound();

        // Return the standard 8 fields
        best1HopRouteTop1 = best.top1Hop.payload; amountOut1HopTop1 = best.top1Hop.amountOut;
        best2HopRouteTop1 = best.top2Hop.payload; amountOut2HopTop1 = best.top2Hop.amountOut;
        best1HopRouteTop2 = best.second1Hop.payload; amountOut1HopTop2 = best.second1Hop.amountOut;
        best2HopRouteTop2 = best.second2Hop.payload; amountOut2HopTop2 = best.second2Hop.amountOut;

        // Compute split between the two overall best routes (T1 and T2)
        if (top2Overall.amountOut > 0 && keccak256(top1Overall.payload) != keccak256(top2Overall.payload)) {
            (splitAmountOut, splitPercentA) = findBestSplit(
                top1Overall.payload, 
                top2Overall.payload
            );
            
            // If split provides no improvement, do not return it,
            // since the best will be either T1 or T2 (T1.amountOut >= T2.amountOut).
            if (splitAmountOut <= top1Overall.amountOut) {
                 splitAmountOut = 0;
                 splitPercentA = 0;
            }
        } else {
            // If only one route found, or T1 == T2, split is not applicable
            splitAmountOut = 0;
            splitPercentA = 0;
        }
    }

    /**
     * @notice Returns top quotes (1-hop & 2-hop), an optional optimal split of the two best routes,
     *         and wallet allowances/balances/decimals plus Chainlink USD prices.
     * @dev    Scans all registered modules and tracks Top-1/Top-2 for both 1-hop and 2-hop.
     *         Also tracks the two overall best routes and probes a split between them.
     *         If no route is found, reverts with {NoRouteFound}.
     *         For ETH inputs/outputs, balances/allowances refer to ETH and decimals=18.
     * @param  tokenIn            Input token.
     * @param  tokenOut           Output token.
     * @param  amountIn           Input amount.
     * @param  wallet             Wallet to read balances/allowances from.
     * @param  spender            Spender to check allowances against.
     * @param  asEthIn            If true, forces ETH/USD feed for tokenIn (WETH, stETH, etc.).
     * @param  asEthOut           If true, forces ETH/USD feed for tokenOut (WETH, stETH, etc.).
     * @return best1HopRouteTop1  Serialized payload of best 1-hop route.
     * @return amountOut1HopTop1  Quoted output of best 1-hop route.
     * @return best2HopRouteTop1  Serialized payload of best 2-hop route.
     * @return amountOut2HopTop1  Quoted output of best 2-hop route.
     * @return best1HopRouteTop2  Serialized payload of second-best 1-hop route.
     * @return amountOut1HopTop2  Quoted output of second-best 1-hop route.
     * @return best2HopRouteTop2  Serialized payload of second-best 2-hop route.
     * @return amountOut2HopTop2  Quoted output of second-best 2-hop route.
     * @return splitAmountOut     Quote for the best split of the two overall leaders (0 if not improving).
     * @return splitPercentA      Percent for route A (0–100) in the split (0 if split not applicable).
     *
     * @return balanceIn          {wallet} balance of tokenIn (ETH if tokenIn==address(0)).
     * @return allowance_In       Allowance of tokenIn to {spender} (0 for ETH).
     * @return decimals_In        Decimals of tokenIn (18 for ETH; 0 if unreadable).
     * @return balanceOut         {wallet} balance of tokenOut (ETH if tokenOut==address(0)).
     * @return allowance_Out      Allowance of tokenOut to {spender} (0 for ETH).
     * @return decimals_Out       Decimals of tokenOut (18 for ETH; 0 if unreadable).
     *
     * @return usdPerToken1e18In  TokenIn USD price normalized to 1e18 (0 if no feed/<=0).
     * @return updatedAtIn        Timestamp of the last price update for tokenIn.
     * @return usdPerToken1e18Out TokenOut USD price normalized to 1e18 (0 if no feed/<=0).
     * @return updatedAtOut       Timestamp of the last price update for tokenOut.
     */
    function getBestRouteSuper(
        address tokenIn,
        address tokenOut,
        uint256 amountIn,
        address wallet,
        address spender,
        bool    asEthIn,
        bool    asEthOut
    ) external view returns (
        bytes memory best1HopRouteTop1, uint256 amountOut1HopTop1,
        bytes memory best2HopRouteTop1, uint256 amountOut2HopTop1,
        bytes memory best1HopRouteTop2, uint256 amountOut1HopTop2,
        bytes memory best2HopRouteTop2, uint256 amountOut2HopTop2,
        uint256 splitAmountOut, uint16 splitPercentA,

        uint256 balanceIn, uint256 allowance_In, uint8  decimals_In,
        uint256 balanceOut, uint256 allowance_Out, uint8  decimals_Out,

        uint256 usdPerToken1e18In, uint256 updatedAtIn,
        uint256 usdPerToken1e18Out, uint256 updatedAtOut
    ) {
        QuoteArgs memory qa = QuoteArgs({
            tokenIn: tokenIn,
            tokenOut: tokenOut,
            amountIn: amountIn
        });

        BestQuotes memory best; 
        TrackedRoute memory top1Overall; // Absolute best route
        TrackedRoute memory top2Overall; // Second best route

        for (uint256 i = 0; i < modules.length; ) {
            ModuleQuotes memory quotes = _getModuleQuotes(modules[i], i, qa);

            if (quotes.amountOut1Hop > 0) {
                TrackedRoute memory r1 = TrackedRoute({
                    payload: quotes.payload1Hop,
                    amountOut: quotes.amountOut1Hop,
                    module: quotes.module,
                    moduleIndex: quotes.moduleIndex
                });
                _updateBestQuotes(best, r1, true); 
                (top1Overall, top2Overall) = _updateTopOverall(top1Overall, top2Overall, r1);
            }

            if (quotes.amountOut2Hop > 0) {
                TrackedRoute memory r2 = TrackedRoute({
                    payload: quotes.payload2Hop,
                    amountOut: quotes.amountOut2Hop,
                    module: quotes.module,
                    moduleIndex: quotes.moduleIndex
                });
                _updateBestQuotes(best, r2, false); 
                (top1Overall, top2Overall) = _updateTopOverall(top1Overall, top2Overall, r2);
            }

            unchecked { ++i; }
        }

        if (top1Overall.amountOut == 0) revert NoRouteFound();

        // Return the standard 8 fields
        best1HopRouteTop1 = best.top1Hop.payload; amountOut1HopTop1 = best.top1Hop.amountOut;
        best2HopRouteTop1 = best.top2Hop.payload; amountOut2HopTop1 = best.top2Hop.amountOut;
        best1HopRouteTop2 = best.second1Hop.payload; amountOut1HopTop2 = best.second1Hop.amountOut;
        best2HopRouteTop2 = best.second2Hop.payload; amountOut2HopTop2 = best.second2Hop.amountOut;

        // Compute split between the two overall best routes (T1 and T2)
        if (top2Overall.amountOut > 0 && keccak256(top1Overall.payload) != keccak256(top2Overall.payload)) {
            (splitAmountOut, splitPercentA) = findBestSplit(
                top1Overall.payload, 
                top2Overall.payload
            );
            
            // If split provides no improvement, do not return it,
            // since the best will be either T1 or T2 (T1.amountOut >= T2.amountOut).
            if (splitAmountOut <= top1Overall.amountOut) {
                 splitAmountOut = 0;
                 splitPercentA = 0;
            }
        } else {
            // If only one route found, or T1 == T2, split is not applicable
            splitAmountOut = 0;
            splitPercentA = 0;
        }

        // safely read balance/allowance (do not revert on non-standard ERC-20)
        (balanceIn, allowance_In, decimals_In) = _safeBalanceAndAllowance(tokenIn, wallet, spender);
        (balanceOut, allowance_Out, decimals_Out) = _safeBalanceAndAllowance(tokenOut, wallet, spender);

        // Let's try to get the price in USD (gently, without revert)
        (usdPerToken1e18In, updatedAtIn)   = _tryTokenUsdPrice1e18(tokenIn, asEthIn);
        (usdPerToken1e18Out, updatedAtOut) = _tryTokenUsdPrice1e18(tokenOut, asEthOut);
    }

    /**
     * @notice Find the best split ratio between two route payloads.
     * @dev    Discrete search by simulateRoute + local fine-tuning.
     * @param  payloadA          Route A.
     * @param  payloadB          Route B.
     * @return bestAmountOut     Best total quote.
     * @return bestPercentA      Share of A (0–100) giving the maximum.
     */
    function findBestSplit(
        bytes memory payloadA, // ИЗМЕНЕНИЕ: bytes memory
        bytes memory payloadB
    )
        internal 
        view
        returns (
            uint256 bestAmountOut,
            uint16 bestPercentA
        )
    {
        // Decode and verify
        LegDecoded memory A = _decodeRouteStruct(payloadA);
        LegDecoded memory B = _decodeRouteStruct(payloadB);

        require(A.amountIn > 0 && B.amountIn > 0, "UR: zero amounts");
        require(A.tokenIn == B.tokenIn, "UR: in mismatch");
        require(A.tokenOut == B.tokenOut, "UR: out mismatch");
        require(A.amountIn == B.amountIn, "UR: totalIn mismatch"); 

        address moduleA = A.module;
        address moduleB = B.module;
        
        // --- Step 1: Initialization (50%) ---
        uint16 initialPercent = 50; // 50%
        
        uint256 currentMaxOut = _calculateTotalOut(
            moduleA, A.route, moduleB, B.route, initialPercent
        );
        uint16 currentBestPercent = initialPercent;

        // --- Step 2: Main sparse search: 10% to 90% in 10% increments ---
        // Check 10, 20, 30, 40, 60, 70, 80, 90. (50% already checked).
        for (uint16 percent = 10; percent <= 90; percent += 10) {
            if (percent == 50) continue; 

            uint256 totalOut = _calculateTotalOut(
                moduleA, A.route, moduleB, B.route, percent
            );

            if (totalOut > currentMaxOut) {
                currentMaxOut = totalOut;
                currentBestPercent = percent;
            }
        }
        
        // --- Step 3: Refinement (Local search, +/- 5% step) ---
        uint16 bestPercentFound = currentBestPercent;
        
        // Array of offsets for refinement: [-5, +5] Percent
        int16[] memory offsets = new int16[](2);
        offsets[0] = -5; // Checking -5% from the best point
        offsets[1] = 5;  // Checking +5% from the best point

        for (uint256 i = 0; i < offsets.length; ) {
            int16 offset = offsets[i];
            
            // Protection against values exceeding the limits (e.g., below 1% or above 99%)
            // Condition: bestPercentFound <= 5 (for -5) or bestPercentFound >= 95 (for +5)
            if (
                (offset < 0 && bestPercentFound <= uint16(-offset)) || 
                (offset > 0 && bestPercentFound >= 100 - uint16(offset))
            ) {
                 unchecked { ++i; }
                 continue;
            }
            
            uint16 checkPercent;
            if (offset < 0) {
                checkPercent = bestPercentFound - uint16(-offset);
            } else {
                checkPercent = bestPercentFound + uint16(offset);
            }
            
            // Check that the point is within a reasonable range for swap [1, 99]
            if (checkPercent >= 1 && checkPercent <= 99) { 
                uint256 totalOut = _calculateTotalOut(
                    moduleA, A.route, moduleB, B.route, checkPercent
                );

                if (totalOut > currentMaxOut) {
                    currentMaxOut = totalOut;
                    currentBestPercent = checkPercent;
                }
            }
            unchecked { ++i; }
        }
        
        // 4. Return the result
        bestAmountOut = currentMaxOut;
        bestPercentA = currentBestPercent;
    }

    /**
     * @notice Decodes the route payload.
     * @param  payload ABI-encoded packet.
     * @return module            Module address.
     * @return moduleIndex       Module index.
     * @return quotedOut         Output quote.
     * @return tokenIn           Input token.
     * @return tokenOut          Output token.
     * @return amountIn          Input amount.
     * @return routeData         Route byte hops.
     */
    function decodeRoute(bytes calldata payload)
        public
        pure
        returns (
            address module,
            uint256 moduleIndex,
            uint256 quotedOut,
            address tokenIn,
            address tokenOut,
            uint256 amountIn,
            bytes[] memory routeData
        )
    {
        (module, moduleIndex, quotedOut, tokenIn, tokenOut, amountIn, routeData) =
            abi.decode(payload, (address, uint256, uint256, address, address, uint256, bytes[]));
    }   

    /* ──────────────────────────────────── Swap ───────────────────────────────── */

    /* ─────────────── ROUTE: Token → Token ───────────── */

    /**
     * @notice Execute a swap based on a pre-prepared payload.
     * @dev    Takes a commission from the swap and positive slippage; checks minAmountOut; transfers the net amount to `to`.
     * @param  payload           ABI-encoded route (see decodeRoute).
     * @param  to                Recipient of the final tokens.
     * @param  minAmountOut      Minimum allowable output.
     * @return netOut            Net amount after commissions are deducted.
     */
    function swapRoute(bytes calldata payload, address to, uint256 minAmountOut)
        external
        nonReentrant
        returns (uint256 netOut)
    {
        require(to != address(0), "UR: bad to");

        (
            address module, , uint256 quotedOut,
            address tokenIn, address tokenOut, uint256 amountIn,
            bytes[] memory routeData
        ) = decodeRoute(payload);

        require(isModule[module], "UR: unknown module");
        require(amountIn > 0, "UR: zero amountIn");
        require(routeData.length > 0, "UR: empty route");

        IERC20(tokenIn).safeTransferFrom(msg.sender, address(this), amountIn);
        _smartApprove(tokenIn, module, amountIn);

        uint256 amountOut = IDexModule(module).swapRoute(DexRoute({ data: routeData }), address(this), 100);
        require(amountOut >= minAmountOut, "UR: slippage");

        // Pay the user minus the fee — and return immediately
        netOut = _distributeTokenWithFees(tokenOut, to, amountOut, quotedOut, minAmountOut);

        emit SwapExecuted(module, msg.sender, to, tokenIn, tokenOut, amountIn, netOut, quotedOut);
    }

    /* ─────────────── ROUTE: ETH → Token ─────────────── */

    /**
     * @notice Swap ETH→Token by payload with WETH as tokenIn.
     * @dev    Wraps ETH in WETH, calls the module, holds commissions, sends net amount `to`.
     * @param  payload           Route packet (tokenIn=WETH, amountIn=msg.value).
     * @param  to                Recipient.
     * @param  minAmountOut      Minimum output.
     * @return netOut            Net amount (ERC20).
     */
    function swapRouteExactETHForTokens(
        bytes calldata payload,      // payload with tokenIn == WETH and amountIn == msg.value
        address to,
        uint256 minAmountOut
    ) external payable nonReentrant returns (uint256 netOut) {
        require(to != address(0), "UR: bad to");
        require(msg.value > 0, "UR: no ETH");
        _requireWethIn(payload);

        (
            address module, , uint256 quotedOut,
            , address tokenOut, uint256 amountIn,
            bytes[] memory routeData
        ) = decodeRoute(payload);

        require(isModule[module], "UR: unknown module");
        require(routeData.length > 0, "UR: empty route");
        require(amountIn == msg.value, "UR: value != amountIn");

        _wrapETH(msg.value);                       // ETH -> WETH
        _smartApprove(WETH, module, msg.value);    // approve

        // Send to router → calculate commission → pay customer
        uint256 amountOut = IDexModule(module).swapRoute(DexRoute({data: routeData}), address(this), 100);
        require(amountOut >= minAmountOut, "UR: slippage");

        netOut = _distributeTokenWithFees(tokenOut, to, amountOut, quotedOut, minAmountOut);

        emit SwapExecuted(module, msg.sender, to, WETH, tokenOut, amountIn, netOut, quotedOut);
    }

    /* ─────────────── ROUTE: Token → ETH ─────────────── */

    /**
     * @notice Swap Token→ETH by payload with WETH as tokenOut.
     * @dev    Calls the module before WETH, converts to ETH, holds commissions, sends net amount `to`.
     * @param  payload           Route package (tokenOut=WETH).
     * @param  to                ETH recipient.
     * @param  minAmountOut      Minimum output.
     * @return netEthOut         Net amount (ETH).
     */
    function swapRouteExactTokensForETH(
        bytes calldata payload,      // payload: tokenOut == WETH
        address to,
        uint256 minAmountOut
    ) external nonReentrant returns (uint256 netEthOut) {
        require(to != address(0), "UR: bad to");
        _requireWethOut(payload);

        (
            address module, , uint256 quotedOut,
            address tokenIn, , uint256 amountIn,
            bytes[] memory routeData
        ) = decodeRoute(payload);

        require(isModule[module], "UR: unknown module");
        require(amountIn > 0, "UR: zero in");
        require(routeData.length > 0, "UR: empty route");

        IERC20(tokenIn).safeTransferFrom(msg.sender, address(this), amountIn);
        _smartApprove(tokenIn, module, amountIn);

        uint256 outWeth = IDexModule(module).swapRoute(DexRoute({data: routeData}), address(this), 100);
        require(outWeth >= minAmountOut, "UR: slippage");

        // Unwrap and distribute with fees
        _unwrapWETHAndSend(outWeth, address(this));

        netEthOut = _distributeETHWithFees(to, outWeth, quotedOut, minAmountOut);

        emit SwapExecuted(module, msg.sender, to, tokenIn, WETH, amountIn, netEthOut, quotedOut);
    }

    /* ─────────────── SPLIT: Token → Token ───────────── */

    /**
     * @notice Perform a split swap with two token→token routes.
     * @dev    Splits the input by `percentA`/`100-percentA`; checks minAmountOut; holds commissions; forwards the net amount.
     * @param  payloadA          Route A package.
     * @param  payloadB          Route B package.
     * @param  percentA          Share A (1–99).
     * @param  minAmountOut      Minimum total output.
     * @param  to                Recipient.
     * @return netOut            Net amount after fees.
     */
    function swapSplit(
        bytes calldata payloadA,
        bytes calldata payloadB,
        uint16 percentA,
        uint256 minAmountOut,
        address to
    ) external nonReentrant returns (uint256 netOut) {       
        // Decode and verify
        LegDecoded memory A = _decodeRouteStructCallData(payloadA);
        LegDecoded memory B = _decodeRouteStructCallData(payloadB);
        
        require(A.amountIn > 0 && B.amountIn > 0, "UR: zero amounts");
        require(A.tokenIn == B.tokenIn, "UR: in mismatch");
        require(A.tokenOut == B.tokenOut, "UR: out mismatch");
        require(A.amountIn == B.amountIn, "UR: totalIn mismatch");
        require(A.route.length > 0 && B.route.length > 0, "UR: empty route");
        require(percentA >= 1 && percentA <= 99, "UR: percent out of bounds");
        require(isModule[A.module], "UR: unknown module");
        require(isModule[B.module], "UR: unknown module");

        IERC20(A.tokenIn).safeTransferFrom(msg.sender, address(this), A.amountIn); // if A.amountIn equals B.amountIn
        
        _smartApprove(A.tokenIn, A.module, A.amountIn);
        _smartApprove(A.tokenIn, B.module, B.amountIn);
        
        // Perform swaps (call modules)

        // Route A (percentA)
        // IDexModule.swapRoute passes a percentage (0-100), and the module
        // must internally calculate the exact amountIn for this part of the swap.
        uint256 outA = IDexModule(A.module).swapRoute(
            DexRoute({ data: A.route }), 
            address(this), 
            percentA
        );

        // Route B (100 - percentA)
        uint256 outB = IDexModule(B.module).swapRoute(
            DexRoute({ data: B.route }), 
            address(this), 
            uint16(100 - percentA)
        );

        // Slip check and return
        require((outA + outB) >= minAmountOut, "UR: slippage");

        uint256 quotedTotal = (A.quoted * percentA) / 100 + (B.quoted * (uint16(100 - percentA))) / 100;

        // Commission + payment to user
        netOut = _distributeTokenWithFees(A.tokenOut, to, outA + outB, quotedTotal, minAmountOut);

        SplitResult memory r = SplitResult({
            moduleA: A.module,
            moduleB: B.module,
            tokenIn: A.tokenIn,
            tokenOut: A.tokenOut,
            totalIn: A.amountIn,
            amountInA: (A.amountIn * percentA) / 100,
            amountInB: (B.amountIn * (uint16(100 - percentA))) / 100,
            outA: outA,
            outB: outB,
            totalOut: outA + outB
        });
        _emitSwapSplit(r, msg.sender, to, percentA);
    }

    /* ─────────────── SPLIT: ETH → Token ─────────────── */

    /**
     * @notice Split-swap ETH→Token via two routes (both via WETH).
     * @dev    Converts ETH to WETH; splits input by percentage; holds fees; transfers net amount `to`.
     * @param  payloadA          Package A (tokenIn=WETH, amountIn=msg.value).
     * @param  payloadB          Package B.
     * @param  percentA          Share A (1–99).
     * @param  minTotalOut       Minimum total output.
     * @param  to                Recipient.
     * @return netOut            Net result (ERC20).
     */
    function swapSplitExactETHForTokens(
        bytes calldata payloadA,     // both: tokenIn == WETH, amountIn == msg.value
        bytes calldata payloadB,
        uint16 percentA,             // 1..99
        uint256 minTotalOut,
        address to
    ) external payable nonReentrant returns (uint256 netOut) {
        require(to != address(0), "UR: bad to");
        require(msg.value > 0, "UR: no ETH");
        require(percentA >= 1 && percentA <= 99, "UR: percent out of bounds");

        _requireWethIn(payloadA);
        _requireWethIn(payloadB);

        LegDecoded memory A = _decodeRouteStructCallData(payloadA);
        LegDecoded memory B = _decodeRouteStructCallData(payloadB);
        require(A.amountIn == B.amountIn, "UR: split amount mismatch");
        require(A.amountIn == msg.value, "UR: value != amountIn");
        require(A.tokenOut == B.tokenOut, "UR: out mismatch");
        require(A.route.length > 0 && B.route.length > 0, "UR: empty route");
        require(isModule[A.module], "UR: unknown module");
        require(isModule[B.module], "UR: unknown module");

        _wrapETH(msg.value);
        _smartApprove(WETH, A.module, msg.value);
        _smartApprove(WETH, B.module, msg.value);

        uint16 percentB = uint16(100 - percentA);

        // Route execution → fees → recipient
        uint256 outA = IDexModule(A.module).swapRoute(DexRoute({data: A.route}), address(this), percentA);
        uint256 outB = IDexModule(B.module).swapRoute(DexRoute({data: B.route}), address(this), percentB);

        uint256 grossOut = outA + outB;
        require(grossOut >= minTotalOut, "UR: slippage");

        uint256 quotedTotal = (A.quoted * percentA) / 100 + (B.quoted * percentB) / 100;

        netOut = _distributeTokenWithFees(A.tokenOut, to, grossOut, quotedTotal, minTotalOut);

        SplitResult memory r = SplitResult({
            moduleA: A.module,
            moduleB: B.module,
            tokenIn: WETH,
            tokenOut: A.tokenOut,
            totalIn: msg.value,
            amountInA: (uint256(msg.value) * percentA) / 100,
            amountInB: (uint256(msg.value) * percentB) / 100,
            outA: outA,
            outB: outB,
            totalOut: grossOut
        });
        _emitSwapSplit(r, msg.sender, to, percentA);
    }

    /* ─────────────── SPLIT: Token → ETH ─────────────── */

    /**
     * @notice Split-swap Token→ETH via two routes (both ending in WETH).
     * @dev    Splits input by percentage; converts WETH→ETH; holds fees; transfers net amount `to`.
     * @param  payloadA          Package A (tokenOut=WETH).
     * @param  payloadB          Package B.
     * @param  percentA          Share A (1–99).
     * @param  minTotalEthOut    Minimum total output in ETH.
     * @param  to                ETH recipient.
     * @return netEthOut         Net result (ETH).
     */
    function swapSplitExactTokensForETH(
        bytes calldata payloadA,     // both: tokenOut == WETH, same amountIn
        bytes calldata payloadB,
        uint16 percentA,             // 1..99
        uint256 minTotalEthOut,
        address to
    ) external nonReentrant returns (uint256 netEthOut) {
        require(to != address(0), "UR: bad to");
        require(percentA >= 1 && percentA <= 99, "UR: percent out of bounds");

        _requireWethOut(payloadA);
        _requireWethOut(payloadB);

        LegDecoded memory A = _decodeRouteStructCallData(payloadA);
        LegDecoded memory B = _decodeRouteStructCallData(payloadB);
        require(A.amountIn > 0 && B.amountIn > 0, "UR: zero in");
        require(A.amountIn == B.amountIn, "UR: split amount mismatch");
        require(A.tokenIn == B.tokenIn, "UR: in mismatch");
        require(A.route.length > 0 && B.route.length > 0, "UR: empty route");
        require(isModule[A.module], "UR: unknown module");
        require(isModule[B.module], "UR: unknown module");

        IERC20(A.tokenIn).safeTransferFrom(msg.sender, address(this), A.amountIn);
        _smartApprove(A.tokenIn, A.module, A.amountIn);
        _smartApprove(A.tokenIn, B.module, B.amountIn);

        uint16 percentB = uint16(100 - percentA);

        uint256 outA = IDexModule(A.module).swapRoute(DexRoute({data: A.route}), address(this), percentA);
        uint256 outB = IDexModule(B.module).swapRoute(DexRoute({data: B.route}), address(this), percentB);

        uint256 totalWeth = outA + outB;
        require(totalWeth >= minTotalEthOut, "UR: slippage");

        uint256 quotedTotal = (A.quoted * percentA) / 100 + (B.quoted * percentB) / 100;

        _unwrapWETHAndSend(totalWeth, address(this));
        netEthOut = _distributeETHWithFees(to, totalWeth, quotedTotal, minTotalEthOut);

        SplitResult memory r = SplitResult({
            moduleA: A.module,
            moduleB: B.module,
            tokenIn: A.tokenIn,
            tokenOut: WETH,
            totalIn: A.amountIn,
            amountInA: (A.amountIn * percentA) / 100,
            amountInB: (B.amountIn * percentB) / 100,
            outA: outA,
            outB: outB,
            totalOut: totalWeth
        });
        _emitSwapSplit(r, msg.sender, to, percentA);
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)

pragma solidity ^0.8.0;

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

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

    uint256 private _status;

    constructor() {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be _NOT_ENTERED
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

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

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

    /**
     * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
     * `nonReentrant` function in the call stack.
     */
    function _reentrancyGuardEntered() internal view returns (bool) {
        return _status == _ENTERED;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)

pragma solidity ^0.8.20;

import {Context} from "../utils/Context.sol";

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

    /**
     * @dev The caller account is not authorized to perform an operation.
     */
    error OwnableUnauthorizedAccount(address account);

    /**
     * @dev The owner is not a valid owner account. (eg. `address(0)`)
     */
    error OwnableInvalidOwner(address owner);

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

    /**
     * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
     */
    constructor(address initialOwner) {
        if (initialOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(initialOwner);
    }

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

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

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        if (owner() != _msgSender()) {
            revert OwnableUnauthorizedAccount(_msgSender());
        }
    }

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

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        if (newOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(newOwner);
    }

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";
import {IERC1363} from "../../../interfaces/IERC1363.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC-20 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 {
    /**
     * @dev An operation with an ERC-20 token failed.
     */
    error SafeERC20FailedOperation(address token);

    /**
     * @dev Indicates a failed `decreaseAllowance` request.
     */
    error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);

    /**
     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
    }

    /**
     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
     */
    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
    }

    /**
     * @dev Variant of {safeTransfer} that returns a bool instead of reverting if the operation is not successful.
     */
    function trySafeTransfer(IERC20 token, address to, uint256 value) internal returns (bool) {
        return _callOptionalReturnBool(token, abi.encodeCall(token.transfer, (to, value)));
    }

    /**
     * @dev Variant of {safeTransferFrom} that returns a bool instead of reverting if the operation is not successful.
     */
    function trySafeTransferFrom(IERC20 token, address from, address to, uint256 value) internal returns (bool) {
        return _callOptionalReturnBool(token, abi.encodeCall(token.transferFrom, (from, to, value)));
    }

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     *
     * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
     * smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
     * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
     * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        forceApprove(token, spender, oldAllowance + value);
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
     * value, non-reverting calls are assumed to be successful.
     *
     * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
     * smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
     * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
     * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
        unchecked {
            uint256 currentAllowance = token.allowance(address(this), spender);
            if (currentAllowance < requestedDecrease) {
                revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
            }
            forceApprove(token, spender, currentAllowance - requestedDecrease);
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
     * to be set to zero before setting it to a non-zero value, such as USDT.
     *
     * NOTE: If the token implements ERC-7674, this function will not modify any temporary allowance. This function
     * only sets the "standard" allowance. Any temporary allowance will remain active, in addition to the value being
     * set here.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));

        if (!_callOptionalReturnBool(token, approvalCall)) {
            _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
            _callOptionalReturn(token, approvalCall);
        }
    }

    /**
     * @dev Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no
     * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * Reverts if the returned value is other than `true`.
     */
    function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
        if (to.code.length == 0) {
            safeTransfer(token, to, value);
        } else if (!token.transferAndCall(to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target
     * has no code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * Reverts if the returned value is other than `true`.
     */
    function transferFromAndCallRelaxed(
        IERC1363 token,
        address from,
        address to,
        uint256 value,
        bytes memory data
    ) internal {
        if (to.code.length == 0) {
            safeTransferFrom(token, from, to, value);
        } else if (!token.transferFromAndCall(from, to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no
     * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}.
     * Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall}
     * once without retrying, and relies on the returned value to be true.
     *
     * Reverts if the returned value is other than `true`.
     */
    function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
        if (to.code.length == 0) {
            forceApprove(token, to, value);
        } else if (!token.approveAndCall(to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @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).
     *
     * This is a variant of {_callOptionalReturnBool} that reverts if call fails to meet the requirements.
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        uint256 returnSize;
        uint256 returnValue;
        assembly ("memory-safe") {
            let success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
            // bubble errors
            if iszero(success) {
                let ptr := mload(0x40)
                returndatacopy(ptr, 0, returndatasize())
                revert(ptr, returndatasize())
            }
            returnSize := returndatasize()
            returnValue := mload(0)
        }

        if (returnSize == 0 ? address(token).code.length == 0 : returnValue != 1) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @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).
     *
     * This is a variant of {_callOptionalReturn} that silently catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        bool success;
        uint256 returnSize;
        uint256 returnValue;
        assembly ("memory-safe") {
            success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
            returnSize := returndatasize()
            returnValue := mload(0)
        }
        return success && (returnSize == 0 ? address(token).code.length > 0 : returnValue == 1);
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/IERC20.sol)

pragma solidity >=0.4.16;

/**
 * @dev Interface of the ERC-20 standard as defined in the ERC.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

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

    /**
     * @dev Returns the value of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

    /**
     * @dev Moves a `value` amount of 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 value) 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 a `value` amount of tokens 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 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the
     * allowance mechanism. `value` 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 value) external returns (bool);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC1363.sol)

pragma solidity >=0.6.2;

import {IERC20} from "./IERC20.sol";
import {IERC165} from "./IERC165.sol";

/**
 * @title IERC1363
 * @dev Interface of the ERC-1363 standard as defined in the https://eips.ethereum.org/EIPS/eip-1363[ERC-1363].
 *
 * Defines an extension interface for ERC-20 tokens that supports executing code on a recipient contract
 * after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction.
 */
interface IERC1363 is IERC20, IERC165 {
    /*
     * Note: the ERC-165 identifier for this interface is 0xb0202a11.
     * 0xb0202a11 ===
     *   bytes4(keccak256('transferAndCall(address,uint256)')) ^
     *   bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^
     *   bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^
     *   bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^
     *   bytes4(keccak256('approveAndCall(address,uint256)')) ^
     *   bytes4(keccak256('approveAndCall(address,uint256,bytes)'))
     */

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferAndCall(address to, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @param data Additional data with no specified format, sent in call to `to`.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param from The address which you want to send tokens from.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferFromAndCall(address from, address to, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param from The address which you want to send tokens from.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @param data Additional data with no specified format, sent in call to `to`.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
     * @param spender The address which will spend the funds.
     * @param value The amount of tokens to be spent.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function approveAndCall(address spender, uint256 value) external returns (bool);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
     * @param spender The address which will spend the funds.
     * @param value The amount of tokens to be spent.
     * @param data Additional data with no specified format, sent in call to `spender`.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)

pragma solidity ^0.8.20;

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

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

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}

File 8 of 10 : IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC165.sol)

pragma solidity >=0.4.16;

import {IERC165} from "../utils/introspection/IERC165.sol";

File 9 of 10 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC20.sol)

pragma solidity >=0.4.16;

import {IERC20} from "../token/ERC20/IERC20.sol";

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (utils/introspection/IERC165.sol)

pragma solidity >=0.4.16;

/**
 * @dev Interface of the ERC-165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[ERC].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "viaIR": true,
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "abi"
      ]
    }
  },
  "remappings": []
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address[]","name":"_modules","type":"address[]"},{"internalType":"address","name":"_feeRecipient","type":"address"},{"internalType":"uint16","name":"_feeBpsSwap","type":"uint16"},{"internalType":"uint16","name":"_feeBpsPositive","type":"uint16"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"DuplicateModule","type":"error"},{"inputs":[],"name":"NoRouteFound","type":"error"},{"inputs":[],"name":"NotAModule","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ERC20Recovered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ETHSwept","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint16","name":"bpsSwap","type":"uint16"},{"indexed":false,"internalType":"uint16","name":"bpsPositive","type":"uint16"}],"name":"FeeConfigUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"module","type":"address"}],"name":"ModuleAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"module","type":"address"}],"name":"ModuleRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newCount","type":"uint256"}],"name":"ModulesReset","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"module","type":"address"},{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"address","name":"tokenIn","type":"address"},{"indexed":false,"internalType":"address","name":"tokenOut","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountOut","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"quotedOut","type":"uint256"}],"name":"SwapExecuted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"address","name":"moduleA","type":"address"},{"indexed":false,"internalType":"address","name":"moduleB","type":"address"},{"indexed":false,"internalType":"address","name":"tokenIn","type":"address"},{"indexed":false,"internalType":"address","name":"tokenOut","type":"address"},{"indexed":false,"internalType":"uint256","name":"totalIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalOut","type":"uint256"},{"indexed":false,"internalType":"uint16","name":"bpsA","type":"uint16"}],"name":"SwapSplitExecuted","type":"event"},{"inputs":[],"name":"MAX_FEE_POSITIVE_BPS","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_FEE_SWAP_BPS","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WETH","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"module","type":"address"}],"name":"addModule","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"wallet","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"bool","name":"asEth","type":"bool"}],"name":"balanceAllowanceAndUsd","outputs":[{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"uint256","name":"allowance_","type":"uint256"},{"internalType":"uint256","name":"usdPerToken1e18","type":"uint256"},{"internalType":"uint256","name":"updatedAt","type":"uint256"},{"internalType":"uint8","name":"decimals_","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"wallet","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"bool","name":"asEthIn","type":"bool"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"bool","name":"asEthOut","type":"bool"}],"name":"balanceAllowanceAndUsdDouble","outputs":[{"internalType":"uint256","name":"balanceIn","type":"uint256"},{"internalType":"uint256","name":"allowance_In","type":"uint256"},{"internalType":"uint256","name":"usdPerToken1e18In","type":"uint256"},{"internalType":"uint256","name":"updatedAtIn","type":"uint256"},{"internalType":"uint8","name":"decimals_In","type":"uint8"},{"internalType":"uint256","name":"balanceOut","type":"uint256"},{"internalType":"uint256","name":"allowance_Out","type":"uint256"},{"internalType":"uint256","name":"usdPerToken1e18Out","type":"uint256"},{"internalType":"uint256","name":"updatedAtOut","type":"uint256"},{"internalType":"uint8","name":"decimals_Out","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"wallet","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"balanceAndAllowanceOf","outputs":[{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"uint256","name":"allowance_","type":"uint256"},{"internalType":"uint8","name":"decimals_","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"payload","type":"bytes"}],"name":"decodeRoute","outputs":[{"internalType":"address","name":"module","type":"address"},{"internalType":"uint256","name":"moduleIndex","type":"uint256"},{"internalType":"uint256","name":"quotedOut","type":"uint256"},{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"bytes[]","name":"routeData","type":"bytes[]"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"feeBpsPositive","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feeBpsSwap","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feeRecipient","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feedRegistry","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"}],"name":"getBestRoute","outputs":[{"internalType":"bytes","name":"best1HopRouteTop1","type":"bytes"},{"internalType":"uint256","name":"amountOut1HopTop1","type":"uint256"},{"internalType":"bytes","name":"best2HopRouteTop1","type":"bytes"},{"internalType":"uint256","name":"amountOut2HopTop1","type":"uint256"},{"internalType":"bytes","name":"best1HopRouteTop2","type":"bytes"},{"internalType":"uint256","name":"amountOut1HopTop2","type":"uint256"},{"internalType":"bytes","name":"best2HopRouteTop2","type":"bytes"},{"internalType":"uint256","name":"amountOut2HopTop2","type":"uint256"},{"internalType":"uint256","name":"splitAmountOut","type":"uint256"},{"internalType":"uint16","name":"splitPercentA","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"address","name":"wallet","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"bool","name":"asEthIn","type":"bool"},{"internalType":"bool","name":"asEthOut","type":"bool"}],"name":"getBestRouteSuper","outputs":[{"internalType":"bytes","name":"best1HopRouteTop1","type":"bytes"},{"internalType":"uint256","name":"amountOut1HopTop1","type":"uint256"},{"internalType":"bytes","name":"best2HopRouteTop1","type":"bytes"},{"internalType":"uint256","name":"amountOut2HopTop1","type":"uint256"},{"internalType":"bytes","name":"best1HopRouteTop2","type":"bytes"},{"internalType":"uint256","name":"amountOut1HopTop2","type":"uint256"},{"internalType":"bytes","name":"best2HopRouteTop2","type":"bytes"},{"internalType":"uint256","name":"amountOut2HopTop2","type":"uint256"},{"internalType":"uint256","name":"splitAmountOut","type":"uint256"},{"internalType":"uint16","name":"splitPercentA","type":"uint16"},{"internalType":"uint256","name":"balanceIn","type":"uint256"},{"internalType":"uint256","name":"allowance_In","type":"uint256"},{"internalType":"uint8","name":"decimals_In","type":"uint8"},{"internalType":"uint256","name":"balanceOut","type":"uint256"},{"internalType":"uint256","name":"allowance_Out","type":"uint256"},{"internalType":"uint8","name":"decimals_Out","type":"uint8"},{"internalType":"uint256","name":"usdPerToken1e18In","type":"uint256"},{"internalType":"uint256","name":"updatedAtIn","type":"uint256"},{"internalType":"uint256","name":"usdPerToken1e18Out","type":"uint256"},{"internalType":"uint256","name":"updatedAtOut","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getModules","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isModule","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"modules","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"modulesLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"to","type":"address"}],"name":"recoverERC20","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"module","type":"address"}],"name":"removeModule","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"_feeBpsSwap","type":"uint16"},{"internalType":"uint16","name":"_feeBpsPositive","type":"uint16"}],"name":"setFeePercents","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_recipient","type":"address"}],"name":"setFeeRecipient","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"reg","type":"address"}],"name":"setFeedRegistry","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_modules","type":"address[]"}],"name":"setModules","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"payload","type":"bytes"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"minAmountOut","type":"uint256"}],"name":"swapRoute","outputs":[{"internalType":"uint256","name":"netOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"payload","type":"bytes"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"minAmountOut","type":"uint256"}],"name":"swapRouteExactETHForTokens","outputs":[{"internalType":"uint256","name":"netOut","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes","name":"payload","type":"bytes"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"minAmountOut","type":"uint256"}],"name":"swapRouteExactTokensForETH","outputs":[{"internalType":"uint256","name":"netEthOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"payloadA","type":"bytes"},{"internalType":"bytes","name":"payloadB","type":"bytes"},{"internalType":"uint16","name":"percentA","type":"uint16"},{"internalType":"uint256","name":"minAmountOut","type":"uint256"},{"internalType":"address","name":"to","type":"address"}],"name":"swapSplit","outputs":[{"internalType":"uint256","name":"netOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"payloadA","type":"bytes"},{"internalType":"bytes","name":"payloadB","type":"bytes"},{"internalType":"uint16","name":"percentA","type":"uint16"},{"internalType":"uint256","name":"minTotalOut","type":"uint256"},{"internalType":"address","name":"to","type":"address"}],"name":"swapSplitExactETHForTokens","outputs":[{"internalType":"uint256","name":"netOut","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes","name":"payloadA","type":"bytes"},{"internalType":"bytes","name":"payloadB","type":"bytes"},{"internalType":"uint16","name":"percentA","type":"uint16"},{"internalType":"uint256","name":"minTotalEthOut","type":"uint256"},{"internalType":"address","name":"to","type":"address"}],"name":"swapSplitExactTokensForETH","outputs":[{"internalType":"uint256","name":"netEthOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"sweepETH","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"bool","name":"asEth","type":"bool"}],"name":"tryTokenUsdPrice1e18","outputs":[{"internalType":"uint256","name":"usdPerToken1e18","type":"uint256"},{"internalType":"uint256","name":"updatedAt","type":"uint256"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]

60806040523461046657614d45803803806100198161046a565b9283398101906080818303126104665780516001600160401b0381116104665781019180601f84011215610466578251926001600160401b038411610372578360051b9060208061006b81850161046a565b80978152019282010192831161046657602001905b82821061044e575050506100966020820161048f565b6100ae60606100a7604085016104a3565b93016104a3565b92331561043b575f8054336001600160a01b0319821681178355916001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a360018055600280546001600160a01b0319167347fb2585d2c56fe188d0e6ec628a38b74fceeedf1790556003545f5b8181106103fa5750506003545f600355806103b8575b508051905f5b8281106102a457505060207f24ec4b702193c608d667733c4e53752fd751d1f33023f9852db029c22a34ddf991604051908152a1606461ffff83161161025f5761271061ffff84161161021a576006805460a093841b61ffff60a01b166001600160c01b03199091166001600160a01b039093169283171760b094851b61ffff60b01b161790819055604080519382901c61ffff90811685529190941c166020830152917ffc218f700eca8d3f8834187c973fff1cd7abf19039dbd4751c024df57d7385f991a260405161487a90816104cb8239f35b60405162461bcd60e51b815260206004820152601460248201527f55523a20706f732066656520746f6f20686967680000000000000000000000006044820152606490fd5b60405162461bcd60e51b815260206004820152601560248201527f55523a20737761702066656520746f6f206869676800000000000000000000006044820152606490fd5b81518110156103a457600581901b8201602001516001600160a01b031690811561038657815f52600460205260ff60405f20541661039557813b1561038657815f52600460205260405f20600160ff198254161790556003549168010000000000000000831015610372576103208360018095016003556104b2565b81549060031b9083821b91868060a01b03901b1916179055600354815f52600560205260405f20557fead6a006345da1073a106d5f32372d2d2204f46cb0b4bca8f5ebafcbbed12b8a5f80a201610144565b634e487b7160e01b5f52604160045260245ffd5b63d92e233d60e01b5f5260045ffd5b633b3a461d60e21b5f5260045ffd5b634e487b7160e01b5f52603260045260245ffd5b60035f527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b908101905b8181106103ef575061013e565b5f81556001016103e2565b806104066001926104b2565b838060a01b0391549060031b1c16805f52600460205260405f2060ff1981541690555f5260056020525f604081205501610128565b631e4fbdf760e01b5f525f60045260245ffd5b6020809161045b8461048f565b815201910190610080565b5f80fd5b6040519190601f01601f191682016001600160401b0381118382101761037257604052565b51906001600160a01b038216820361046657565b519061ffff8216820361046657565b6003548110156103a45760035f5260205f2001905f9056fe610160604052600436101561001b575b3615610019575f80fd5b005b5f3560e01c80630197ea7c146123125780631163b2b01461223d5780631ed86f1914612214578063200f03fd146121f7578063204d406c146121aa57806323d8dbf7146120f257806324735bdf1461209f5780632fe9c11414611d2b57806335e8248f14611ced57806342f6e38914611cb05780634690484014611c885780635748108d14611b13578063715018a614611abc578063793f74e11461191257806381b2248a146118d0578063886f039a1461189057806388a77dc1146116c357806388ff8db8146116a85780638da5cb5b1461168157806390238c3914611659578063a0632461146114eb578063aaaf1c14146114cf578063ad5c4648146114a8578063b2494df3146113e0578063c7c73ee714611087578063c95cadd714610cf4578063caa4f16514610cd0578063cc654a6714610b8e578063e10f143114610a15578063e74b981b14610987578063eb2de53514610963578063efb0f22314610892578063f2fde38b1461080d578063f3f7f15b146104225763faa55b6d0361000f573461041e57606036600319011261041e576101b961237d565b6101c1612393565b604051916101ce8361262b565b6001600160a01b03908116835216602082015260443560408201526101f1612cb5565b6101f9612c91565b610201612c91565b60035490935f5b82811061033457505050602081019182511561032557805192602084519401519460408301519260208451940151602082015190606060208351930151930151936020855195015197602082015115158061030b575b156102fe5751905161026f91613d28565b969080965110156102f3575b604051998a996101408b526101408b0161029491612434565b9060208b015289810360408b01526102ab91612434565b90606089015287810360808901526102c291612434565b9060a087015285810360c08701526102d991612434565b9260e085015261010084015261ffff166101208301520390f35b5f965086955061027b565b505093505f935f9561027b565b50805160208151910120825160208151910120141561025e565b6305fcedf360e01b5f5260045ffd5b6103598282610342816125b4565b905460039190911b1c6001600160a01b0316613a48565b6060810151806103cf575b5060a08101518061037a575b5050600101610208565b966103c5929560019860808401519360208b8060a01b0382511691015191604051956103a587612646565b86526020860152604085015260608401526103c08389613c99565b613cd2565b959093905f610370565b909496610415929760408701519260018060a01b03885116602089015191604051956103fa87612646565b86526020860152604085015260608401526103c08389613c64565b9590935f610364565b5f80fd5b3461041e5761044a610450610436366124f7565b94909693979195610445613053565b613211565b95613211565b926105da60a08601918251151580610800575b61046c90612d34565b6060878101805191880151909491610491916001600160a01b03908116911614612cf6565b60808881018051918901519092916104b6916001600160a01b03908116911614612967565b878151916104ca60a0830193845114612d73565b6020898c61057e60c08201918251511515806107f2575b6104ea906129a6565b61ffff8416966001881015806107e7575b6105049061288b565b81516001600160a01b03165f9081526004875260409020546105289060ff166129e4565b516001600160a01b03165f90815260048652604090205461054b9060ff166129e4565b8b51865161056591309033906001600160a01b03166135f9565b8b5190518651916001600160a01b039182169116613370565b89518d51875161059c9290916001600160a01b039081169116613370565b8d5190516040516001600160a01b0390921691906105b9826125e0565b81525f604051809b81958294637215562960e01b8452309060048501612a9b565b03925af19485156107a6575f956107b1575b61063b9650602060018060a01b038c511660c08d01519060405191610610836125e0565b825261061b8d612a25565b915f604051809c81958294637215562960e01b8452309060048501612a9b565b03925af19687156107a6575f97610772575b50806106598888612acc565b101561066490612ad9565b8260408d01519061067491612b14565b6064900460408c01516106868c612a25565b61ffff1661069391612b14565b606490046106a091612acc565b85516001600160a01b031691908a6106b88a8a612acc565b906106c294613414565b9a5199519651935190516001600160a01b039a8b169a978816979485169490929116906106ef9083612b14565b6064900492516106fe8a612a25565b61ffff1661070b91612b14565b606490049361071a8787612acc565b976040519b6107288d61260f565b8c5260208c015260408b015260608a0152608089015260a088015260c087015260e08601526101008501526101208401523361076393613503565b60018055604051908152602090f35b9096506020813d60201161079e575b8161078e60209383612661565b8101031261041e5751958c61064d565b3d9150610781565b6040513d5f823e3d90fd5b94506020863d6020116107df575b816107cc60209383612661565b8101031261041e5761063b9551946105ec565b3d91506107bf565b5060638811156104fb565b5060c08701515115156104e1565b5060a08601511515610463565b3461041e57602036600319011261041e5761082661237d565b61082e61302d565b6001600160a01b0316801561087f575f80546001600160a01b03198116831782556001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a3005b631e4fbdf760e01b5f525f60045260245ffd5b3461041e57602036600319011261041e576108ab61237d565b6108b361302d565b6001600160a01b03811690811561092b573b156108e6576bffffffffffffffffffffffff60a01b60025416176002555f80f35b60405162461bcd60e51b815260206004820152601b60248201527f55523a207265676973747279206e6f74206120636f6e747261637400000000006044820152606490fd5b60405162461bcd60e51b815260206004820152601060248201526f55523a2062616420726567697374727960801b6044820152606490fd5b3461041e575f36600319011261041e57602061ffff60065460a01c16604051908152f35b3461041e57602036600319011261041e576109a061237d565b6109a861302d565b600680546001600160a01b0319166001600160a01b0392909216918217908190556040805160a083901c61ffff908116825260b09390931c90921660208301527ffc218f700eca8d3f8834187c973fff1cd7abf19039dbd4751c024df57d7385f99190819081015b0390a2005b3461041e57602036600319011261041e576004356001600160401b03811161041e573660238201121561041e578060040135906001600160401b03821161041e573660248360051b8301011161041e57610a6d61302d565b6003545f5b818110610b4d5750506003545f60035580610aed575b505f5b82811015610ac057600581901b820160240135906001600160a01b038216820361041e57610aba6001926130a9565b01610a8b565b7f24ec4b702193c608d667733c4e53752fd751d1f33023f9852db029c22a34ddf9602084604051908152a1005b60035f527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b017fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b5b818110610b425750610a88565b5f8155600101610b35565b80610b596001926125b4565b838060a01b0391549060031b1c16805f52600460205260405f2060ff1981541690555f5260056020525f604081205501610a72565b3461041e57604036600319011261041e5760043561ffff81169081810361041e576024359161ffff83169081840361041e57606490610bcb61302d565b11610c935761271010610c57576006805463ffffffff60a01b19811660a093841b61ffff60a01b161760b094851b61ffff60b01b161791829055604080519383901c61ffff90811685529290941c90911660208301526001600160a01b0316917ffc218f700eca8d3f8834187c973fff1cd7abf19039dbd4751c024df57d7385f9919081908101610a10565b60405162461bcd60e51b81526020600482015260146024820152730aaa47440e0dee640cccaca40e8dede40d0d2ced60631b6044820152606490fd5b60405162461bcd60e51b81526020600482015260156024820152740aaa47440e6eec2e040cccaca40e8dede40d0d2ced605b1b6044820152606490fd5b3461041e575f36600319011261041e57602061ffff60065460b01c16604051908152f35b3461041e57610d64610e96610d5e610d0b366124f7565b959793989291969094610d1c613053565b610d306001600160a01b0388161515612819565b61ffff88169860018a10158061107c575b610d4a9061288b565b610d548282613596565b610445848c613596565b97613211565b9460a0870190815115158061106f575b610d7d90612b45565b8151610d8f60a08901918251146128d7565b60608981018051918a0151909391610db4916001600160a01b03908116911614612cf6565b60c08a0193845151151580611061575b610dcd906129a6565b8a516001600160a01b03165f90815260046020526040902054610df29060ff166129e4565b89516001600160a01b03165f90815260046020526040902054610e179060ff166129e4565b83518151610e3191309033906001600160a01b03166135f9565b83518b518251610e4f9290916001600160a01b039081169116613370565b83518a518451610e6d9290916001600160a01b039081169116613370565b602089610e7981612a25565b8d51975160405191986001600160a01b031691906105b9826125e0565b03925af19485156107a6575f9561102b575b610eee96506020818c60c060018060a01b0382511691015160405190610ecd826125e0565b81525f604051809c81958294637215562960e01b8452309060048501612a9b565b03925af19687156107a6575f97610ff7575b50610f0b8787612acc565b97838d610f1a838c1015612ad9565b6040015190610f2891612b14565b6064900460408d01519261ffff169283610f4191612b14565b60649004610f4e91612acc565b90610f59308b613642565b610f64918a8c6137b1565b9b519a51945191516001600160a01b039b8c169b958616959093921691610f8b9084612b14565b60649004935190610f9b91612b14565b60649004936040519a610fad8c61260f565b8b5260208b015260408a0152606089015f5160206148255f395f51905f529052608089015260a088015260c087015260e08601526101008501526101208401523361076393613503565b9096506020813d602011611023575b8161101360209383612661565b8101031261041e5751958c610f00565b3d9150611006565b94506020863d602011611059575b8161104660209383612661565b8101031261041e57610eee955194610ea8565b3d9150611039565b5060c08a0151511515610dc4565b5060a08701511515610d74565b5060638a1115610d41565b3461041e5760e036600319011261041e576110a061237d565b6110a8612393565b606435916001600160a01b038316830361041e576110c46123bf565b916110cd6123f8565b9160c43593841515850361041e57604051956110e88761262b565b6001600160a01b03838116885284166020880152604435604088015261110c612cb5565b90611115612c91565b9761111e612c91565b60035490915f5b8281106113125750505060208901988951156103255783519360208551950151610120526020604082015180516101405201519260208201519060606020835193015193015193602085519501516101005260208201511515806112f8575b156112eb5751905161119591613d28565b9490809d5110156112e0575b6111ac88878b612db6565b996111ba919992988d612db6565b9d6111c7919d929c612f5d565b6080529d6111d491612f5d565b60e05260a05260405160c05260c051610280905260c051610280016111f891612434565b6101205160c0516020015260c051810360c05160400152610140519061121d91612434565b9060c0516060015260c051810360c0516080015261123a91612434565b9060c05160a0015260c051810360c05160c0015261125791612434565b986101005160c05160e0015260c051610100015261ffff1660c051610120015260c051610140015260c051610160015260ff1660c051610180015260c0516101a0015260c0516101c0015260ff1660c0516101e0015260c051610200015260805160c051610220015260a05160c051610240015260e05160c051610260015260c051900360c051f35b5f9c508c94506111a1565b50509a505f9a5f936111a1565b508051602081519101208251602081519101201415611184565b6113208282610342816125b4565b606081015180611391575b5060a081015180611341575b5050600101611125565b94611387929d6001966080840151936020898060a01b03825116910151916040519561136c87612646565b86526020860152604085015260608401526103c0838a613c99565b93909b908d611337565b94819d6113d79396604084015193602060018060a01b0382511691015191604051956113bc87612646565b86526020860152604085015260608401526103c0838a613c64565b93909b8d61132b565b3461041e575f36600319011261041e576040518060206003549283815201809260035f527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b905f5b818110611489575050508161143e910382612661565b604051918291602083019060208452518091526040830191905f5b818110611467575050500390f35b82516001600160a01b0316845285945060209384019390920191600101611459565b82546001600160a01b0316845260209093019260019283019201611428565b3461041e575f36600319011261041e5760206040515f5160206148255f395f51905f528152f35b3461041e575f36600319011261041e5760206040516127108152f35b3461041e57602036600319011261041e5761150461237d565b61150c61302d565b6001600160a01b03165f81815260056020526040902054801561164a575f198101818111611636576003545f198101908111611636578082036115d4575b50505060035480156115c0575f1901611562816125b4565b81546001600160a01b03600392831b1b1916909155555f818152600460209081526040808320805460ff19169055600590915281208190557f0a1ee69f55c33d8467c69ca59ce2007a737a88603d75392972520bf67cb513b89080a2005b634e487b7160e01b5f52603160045260245ffd5b6115fe916115e4611622926125b4565b905460039190911b1c6001600160a01b03169283916125b4565b81546001600160a01b0393841660039290921b91821b9390911b1916919091179055565b5f52600560205260405f205581808061154a565b634e487b7160e01b5f52601160045260245ffd5b632cd2a9d360e21b5f5260045ffd5b3461041e575f36600319011261041e576002546040516001600160a01b039091168152602090f35b3461041e575f36600319011261041e575f546040516001600160a01b039091168152602090f35b3461041e575f36600319011261041e57602060405160648152f35b3461041e576116fb6116d43661256e565b939092916116e0613053565b6001600160a01b038416926116f6841515612819565b6127f0565b989197909295945060018060a01b03851694855f52600460205261172560ff60405f2054166129e4565b8315611857578961175e8560209361174261178f9e5115156129a6565b6001600160a01b038b169a6117598330338f6135f9565b613370565b6040519061176b826125e0565b8152604051809b8192637215562960e01b8352606060048401526064830190612a38565b3060248301526064604483015203815f895af19889156107a6575f99611821575b509083896117cc936117c68460209d1015612ad9565b8a613414565b9560405194855260018060a01b031687850152604084015284606084015260808301527fb19d709f69027ded94fa264e8317793c05de52cf8f379f7655d49a585ce7b1f060a03393a460018055604051908152f35b9850906020893d60201161184f575b8161183d60209383612661565b8101031261041e5797519790836117b0565b3d9150611830565b60405162461bcd60e51b81526020600482015260116024820152702aa91d103d32b9379030b6b7bab73a24b760791b6044820152606490fd5b3461041e57604036600319011261041e576118ca6118ac61237d565b6118b4612393565b906118bd61302d565b6118c5613053565b612b7f565b60018055005b3461041e57602036600319011261041e5760043560035481101561041e576118f96020916125b4565b905460405160039290921b1c6001600160a01b03168152f35b3461041e576119e560206119566119283661256e565b949161193694919394613053565b6001600160a01b0384169461194c861515612819565b6116f68282613596565b91509893506119b48960018060a09a969899959a1b03881697885f526004855261198660ff60405f2054166129e4565b611991821515612b45565b61199d845115156129a6565b6001600160a01b038a16996117598330338e6135f9565b604051906119c1826125e0565b8152604051809a8192637215562960e01b8352606060048401526064830190612a38565b3060248301526064604483015203815f885af19788156107a6575f98611a86575b50908288611a2b93611a1c8460209c1015612ad9565b611a263083613642565b6137b1565b946040519384525f5160206148255f395f51905f5287850152604084015284606084015260808301527fb19d709f69027ded94fa264e8317793c05de52cf8f379f7655d49a585ce7b1f060a03393a460018055604051908152f35b9750906020883d602011611ab4575b81611aa260209383612661565b8101031261041e579651969082611a06565b3d9150611a95565b3461041e575f36600319011261041e57611ad461302d565b5f80546001600160a01b0319811682556001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b611bb2611b5d6020611b243661256e565b929391611b32959195613053565b6001600160a01b03861694611b48861515612819565b611b53341515612852565b6116f68282613168565b919794509892949591506119b460018060a01b03871696875f5260048452611b8b60ff60405f2054166129e4565b611b97835115156129a6565b611ba2348c14612923565b611bab34613260565b34906132b2565b3060248301526064604483015203815f885af19788156107a6575f98611c52575b50908288611bef93611be98460209c1015612ad9565b87613414565b94604051935f5160206148255f395f51905f52855260018060a01b031687850152604084015284606084015260808301527fb19d709f69027ded94fa264e8317793c05de52cf8f379f7655d49a585ce7b1f060a03393a460018055604051908152f35b9750906020883d602011611c80575b81611c6e60209383612661565b8101031261041e579651969082611bd3565b3d9150611c61565b3461041e575f36600319011261041e576006546040516001600160a01b039091168152602090f35b3461041e57602036600319011261041e576001600160a01b03611cd161237d565b165f526004602052602060ff60405f2054166040519015158152f35b3461041e57604036600319011261041e57611d0661237d565b60243590811515820361041e57604091611d1f91612f5d565b82519182526020820152f35b611d9b611ec0610d5e611d3d366124f7565b959793989291969094611d4e613053565b611d626001600160a01b0388161515612819565b611d6d341515612852565b61ffff88169860018a101580612094575b611d879061288b565b611d918282613168565b610445848c613168565b94611dbb60a08801611db3815160a08a0151146128d7565b513414612923565b60808781018051918801519091611ddf916001600160a01b03908116911614612967565b60c08801805151151580612086575b611df7906129a6565b88516001600160a01b03165f90815260046020526040902054611e1c9060ff166129e4565b87516001600160a01b03165f90815260046020526040902054611e419060ff166129e4565b611e4a34613260565b8851611e609034906001600160a01b03166132b2565b8751611e769034906001600160a01b03166132b2565b602087611e8281612a25565b8b51935160405191946001600160a01b03169190611e9f826125e0565b81525f604051809981958294637215562960e01b8452309060048501612a9b565b03925af19283156107a6575f93612050575b611f17945060208260018060a01b038b511660c08c015160405190611ef6826125e0565b81525f604051809a81958294637215562960e01b8452309060048501612a9b565b03925af19485156107a6575f9561201c575b5086611f358686612acc565b96611f4281891015612ad9565b8260408d015190611f5291612b14565b6064900460408c01519461ffff169485611f6b91612b14565b60649004611f7891612acc565b8551611f8e938a916001600160a01b0316613414565b9951985192516001600160a01b03998a1699938416931690611fb09034612b14565b6064900491611fbf9034612b14565b606490049260405199611fd18b61260f565b8a5260208a0152604089015f5160206148255f395f51905f529052606089015234608089015260a088015260c087015260e08601526101008501526101208401523361076393613503565b9094506020813d602011612048575b8161203860209383612661565b8101031261041e5751938a611f29565b3d915061202b565b92506020843d60201161207e575b8161206b60209383612661565b8101031261041e57611f17935192611ed2565b3d915061205e565b5060c0880151511515611dee565b5060638a1115611d7e565b3461041e57602036600319011261041e576004356001600160401b03811161041e576120db6120d56120ee923690600401612407565b906127f0565b9360409795979391935197889788612458565b0390f35b3461041e5760c036600319011261041e5761014061210e61237d565b60ff61216661211b612393565b926121246123a9565b9061212d6123e9565b916121366123bf565b928561215c6121436123f8565b61216e6121518b8888612db6565b94919c90988a612db6565b9a91969097612f5d565b929098612f5d565b9790966040519b8c5260208c015260408b015260608a015216608088015260a087015260c086015260e085015261010084015216610120820152f35b3461041e57606036600319011261041e57606060ff6121e06121ca61237d565b6121d2612393565b6121da6123a9565b91612db6565b906040939293519384526020840152166040820152f35b3461041e575f36600319011261041e576020600354604051908152f35b3461041e57602036600319011261041e5761001961223061237d565b61223861302d565b6130a9565b3461041e57602036600319011261041e5761225661237d565b61225e61302d565b612266613053565b6001600160a01b03811661230d57505f546001600160a01b03165b47905f80808085855af161229361269d565b50156122d5576040519182526001600160a01b0316907fab31ddb82a9ca2de51c9befe9da7c6e815e12eac49ae58b8d9cb6b7a181cbc7190602090a260018055005b60405162461bcd60e51b815260206004820152601060248201526f115512081cddd9595c0819985a5b195960821b6044820152606490fd5b612281565b3461041e57608036600319011261041e5760a061232d61237d565b60ff61235f61233a612393565b926123556123466123a9565b61234e6123e9565b9583612db6565b9391959092612f5d565b91604051958652602086015260408501526060840152166080820152f35b600435906001600160a01b038216820361041e57565b602435906001600160a01b038216820361041e57565b604435906001600160a01b038216820361041e57565b608435906001600160a01b038216820361041e57565b35906001600160a01b038216820361041e57565b60643590811515820361041e57565b60a43590811515820361041e57565b9181601f8401121561041e578235916001600160401b03831161041e576020838186019501011161041e57565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b9593919492909460e087019560018060a01b031687526020870152604086015260018060a01b0316606085015260018060a01b0316608084015260a083015260e060c0830152825180915261010082019160206101008360051b8301019401925f915b8383106124ca57505050505090565b90919293946020806124e860019360ff198682030187528951612434565b970193019301919392906124bb565b60a060031982011261041e576004356001600160401b03811161041e578161252191600401612407565b92909291602435906001600160401b03821161041e5761254391600401612407565b909160443561ffff8116810361041e5790606435906084356001600160a01b038116810361041e5790565b606060031982011261041e57600435906001600160401b03821161041e5761259891600401612407565b90916024356001600160a01b038116810361041e579060443590565b6003548110156125cc5760035f5260205f2001905f90565b634e487b7160e01b5f52603260045260245ffd5b602081019081106001600160401b038211176125fb57604052565b634e487b7160e01b5f52604160045260245ffd5b61014081019081106001600160401b038211176125fb57604052565b606081019081106001600160401b038211176125fb57604052565b608081019081106001600160401b038211176125fb57604052565b90601f801991011681019081106001600160401b038211176125fb57604052565b6001600160401b0381116125fb57601f01601f191660200190565b3d156126c7573d906126ae82612682565b916126bc6040519384612661565b82523d5f602084013e565b606090565b6001600160401b0381116125fb5760051b60200190565b60e08183031261041e576126f6816123d5565b9260208201359260408301359261270f606082016123d5565b9261271c608083016123d5565b9260a08301359260c0810135906001600160401b03821161041e57019080601f8301121561041e57813591612750836126cc565b9261275e6040519485612661565b80845260208085019160051b8301019183831161041e5760208101915b83831061278a57505050505090565b82356001600160401b03811161041e57820185603f8201121561041e576020810135916127b683612682565b6127c36040519182612661565b838152604083850101881061041e575f60208581966040839701838601378301015281520192019161277b565b6127fc918101906126e3565b6001600160a01b0396871697959694959385169490921692909190565b1561282057565b60405162461bcd60e51b815260206004820152600a60248201526955523a2062616420746f60b01b6044820152606490fd5b1561285957565b60405162461bcd60e51b815260206004820152600a6024820152690aaa47440dcde408aa8960b31b6044820152606490fd5b1561289257565b60405162461bcd60e51b815260206004820152601960248201527f55523a2070657263656e74206f7574206f6620626f756e6473000000000000006044820152606490fd5b156128de57565b60405162461bcd60e51b815260206004820152601960248201527f55523a2073706c697420616d6f756e74206d69736d61746368000000000000006044820152606490fd5b1561292a57565b60405162461bcd60e51b81526020600482015260156024820152742aa91d103b30b63ab290109e9030b6b7bab73a24b760591b6044820152606490fd5b1561296e57565b60405162461bcd60e51b815260206004820152601060248201526f0aaa47440deeae840dad2e6dac2e8c6d60831b6044820152606490fd5b156129ad57565b60405162461bcd60e51b815260206004820152600f60248201526e55523a20656d70747920726f75746560881b6044820152606490fd5b156129eb57565b60405162461bcd60e51b815260206004820152601260248201527155523a20756e6b6e6f776e206d6f64756c6560701b6044820152606490fd5b61ffff166064039061ffff821161163657565b90602081019151916020825282518091526040820191602060408360051b8301019401925f915b838310612a6e57505050505090565b9091929394602080612a8c600193603f198682030187528951612434565b97019301930191939290612a5f565b91939261ffff90612ab6604093606086526060860190612a38565b6001600160a01b03909616602085015216910152565b9190820180921161163657565b15612ae057565b60405162461bcd60e51b815260206004820152600c60248201526b55523a20736c69707061676560a01b6044820152606490fd5b8181029291811591840414171561163657565b8115612b31570490565b634e487b7160e01b5f52601260045260245ffd5b15612b4c57565b60405162461bcd60e51b815260206004820152600b60248201526a2aa91d103d32b9379034b760a91b6044820152606490fd5b6001600160a01b0316908115612c5e576001600160a01b038116612c5857505f546001600160a01b0316905b6040516370a0823160e01b815230600482015291602083602481855afa9283156107a6575f93612c24575b508215612c1f57602081612c0c857faca8fb252cde442184e5f10e0f2e6e4029e8cd7717cae63559079610702436aa948661391b565b6040519485526001600160a01b031693a3565b505050565b9092506020813d602011612c50575b81612c4060209383612661565b8101031261041e5751915f612bd6565b3d9150612c33565b90612bab565b60405162461bcd60e51b815260206004820152600b60248201526a055523a20746f6b656e3d360ac1b6044820152606490fd5b60405190612c9e82612646565b5f6060838181528260208201528260408201520152565b60405190612cc282612646565b81612ccb612c91565b8152612cd5612c91565b6020820152612ce2612c91565b60408201526060612cf1612c91565b910152565b15612cfd57565b60405162461bcd60e51b815260206004820152600f60248201526e0aaa47440d2dc40dad2e6dac2e8c6d608b1b6044820152606490fd5b15612d3b57565b60405162461bcd60e51b815260206004820152601060248201526f55523a207a65726f20616d6f756e747360801b6044820152606490fd5b15612d7a57565b60405162461bcd60e51b81526020600482015260146024820152730aaa47440e8dee8c2d892dc40dad2e6dac2e8c6d60631b6044820152606490fd5b9192906001600160a01b03831615612f52575f8060405160208101906370a0823160e01b825260018060a01b038516602482015260248152612df9604482612661565b5190865afa612e0661269d565b9080612f46575b15612f35576020815191818082019384920101031261041e575f91612e68612e76849351975b604051636eb1769f60e11b602082019081526001600160a01b0395861660248301529490911660448201529182906064820190565b03601f198101835282612661565b5190855afa612e8361269d565b9080612f29575b15612f1f576020815191818082019384920101031261041e575f809151935b60405163313ce56760e01b602082019081526004825290612ecb602482612661565b51915afa612ed761269d565b9080612f13575b15612f0e576020815191818082019384920101031261041e575160ff811115612f08575060ff5b90565b60ff1690565b505f90565b50602081511015612ede565b505f808093612ea9565b50602081511015612e8a565b505f8091612e68612e768397612e33565b50602081511015612e0d565b3192505f9150601290565b90801561301c575b613007575b612f73816142c0565b90806130015750506001600160a01b0381165f5160206148255f395f51905f5214612ff457612fa1906143df565b81929115612fee57612fb161411a565b81929115612fe55791612fd891670de0b6b3a7640000938082105f14612fdd575094612b14565b049190565b905094612b14565b935050505f9190565b5f925090565b50612ffd61411a565b9091565b92909150565b61300f61411a565b9080613001575050612f6a565b506001600160a01b03811615612f65565b5f546001600160a01b0316330361304057565b63118cdaa760e01b5f523360045260245ffd5b600260015414613064576002600155565b60405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606490fd5b6001600160a01b03811690811561314a57815f52600460205260ff60405f20541661315957803b1561314a57815f52600460205260405f20600160ff1982541617905560035490680100000000000000008210156125fb576115fe82600161311494016003556125b4565b600354815f52600560205260405f20557fead6a006345da1073a106d5f32372d2d2204f46cb0b4bca8f5ebafcbbed12b8a5f80a2565b63d92e233d60e01b5f5260045ffd5b633b3a461d60e21b5f5260045ffd5b5f5160206148255f395f51905f5291506060013560601c0361318657565b60405162461bcd60e51b815260206004820152601b60248201527f55523a207061796c6f616420746f6b656e496e20213d205745544800000000006044820152606490fd5b6040519060e082018281106001600160401b038211176125fb57604052606060c0835f81525f60208201525f60408201525f838201525f60808201525f60a08201520152565b613229909291926132206131cb565b938101906126e3565b60c089015260a08801526001600160a01b039081166080880152908116606087015260408601919091526020850191909152168252565b5f5160206148255f395f51905f523b1561041e575f60049160405192838092630d0e30db60e41b82525f5160206148255f395f51905f525af180156107a6576132a65750565b5f6132b091612661565b565b604051636eb1769f60e11b81523060048201526001600160a01b0382166024820152916020836044815f5160206148255f395f51905f525afa9283156107a6575f9361333c575b508210613304575050565b6132b091613320575b5f5160206148255f395f51905f526145c4565b613337815f5160206148255f395f51905f52614512565b61330d565b9092506020813d602011613368575b8161335860209383612661565b8101031261041e5751915f6132f9565b3d915061334b565b604051636eb1769f60e11b81523060048201526001600160a01b0383811660248301529093911690602084604481855afa9384156107a6575f946133d3575b5083106133bb57505050565b6132b092156145c4576133ce8282614512565b6145c4565b9093506020813d6020116133ff575b816133ef60209383612661565b8101031261041e5751925f6133af565b3d91506133e2565b9190820391821161163657565b93949282156134f9576132b09394958082115f146134f15750945b6006546001600160a01b03811696613452915f9182918a6134a0575b5050612acc565b92808411613498575b8361346591613407565b9583879461347e575b50506001600160a01b031661391b565b613491916001600160a01b03841661391b565b5f8061346e565b92508261345b565b61ffff8160a01c16806134e0575b5060b01c61ffff1690811515806134d7575b1561344b5761271092935087030204905f8061344b565b508088116134c0565b61271090890204925061ffff6134ae565b90509461342f565b5050509150505f90565b80516020808301516040808501516060808701516080808901516101209099015185516001600160a01b03998a16815296891697870197909752928716938501939093529185169183019190915281019390935260a083015261ffff9490941660c08201529183169216907fefb23ebb23587ddae20adda5913cf9b74002499a5f9a8ba79ed13193238a694c9060e090a3565b5f5160206148255f395f51905f5291506080013560601c036135b457565b60405162461bcd60e51b815260206004820152601c60248201527f55523a207061796c6f616420746f6b656e4f757420213d2057455448000000006044820152606490fd5b6040516323b872dd60e01b60208201526001600160a01b0392831660248201529290911660448301526064808301939093529181526132b09161363d608483612661565b6145fe565b6040516370a0823160e01b815230600482015290915f916020816024815f5160206148255f395f51905f525afa80156107a65784915f9161377c575b501061373f575f5160206148255f395f51905f523b1561041e57604051632e1a7d4d60e01b8152600481018490525f81602481835f5160206148255f395f51905f525af180156107a65761372a575b5081809381925af16136dd61269d565b50156136e557565b60405162461bcd60e51b815260206004820152601760248201527f55523a20455448207472616e73666572206661696c65640000000000000000006044820152606490fd5b6137379192505f90612661565b5f905f6136cd565b60405162461bcd60e51b81526020600482015260156024820152740aaa47440d2dce6eaccccd2c6d2cadce840ae8aa89605b1b6044820152606490fd5b9150506020813d6020116137a9575b8161379860209383612661565b8101031261041e578390515f61367e565b3d915061378b565b919093928415613913578082111561390b5750905b6006546001600160a01b038116926137e8915f918291866138ba575050612acc565b938085116138b2575b846137fb91613407565b93849281613855575b50505f80809381935af161381661269d565b501561381e57565b60405162461bcd60e51b815260206004820152600f60248201526e115512081e19995c8819985a5b1959608a1b6044820152606490fd5b5f80939450809281925af161386861269d565b50156138775782905f80613804565b60405162461bcd60e51b815260206004820152601360248201527219995948115512081e19995c8819985a5b1959606a1b6044820152606490fd5b9350836137f1565b61ffff8160a01c16806138fa575b5060b01c61ffff1690811515806138f1575b1561344b5761271092935088030204905f8061344b565b508089116138da565b612710908a0204925061ffff6138c8565b9050906137c6565b505f93505050565b60405163a9059cbb60e01b60208201526001600160a01b0390921660248301526044808301939093529181526132b09161363d606483612661565b9080601f8301121561041e5781519161396e836126cc565b9261397c6040519485612661565b80845260208085019160051b8301019183831161041e5760208101915b8383106139a857505050505090565b82516001600160401b03811161041e57820185603f8201121561041e576020810151916139d483612682565b6139e16040519182612661565b838152604083850101881061041e575f602085819660408397018386015e83010152815201920191613999565b91909160208184031261041e5760405190613a28826125e0565b819381516001600160401b03811161041e57613a449201613956565b9052565b92916040519360c085018581106001600160401b038211176125fb57604052606060408601525f6060860152606060808601525f60a08601528460018060a01b0382168082528460208301525f52600460205260ff60405f20541615613c5d575081516020808401805160408087018051915163faa55b6d60e01b9581019586526001600160a01b0396871660248201529290951660448301526064820152909492915f91829190613afd8160848101612e68565b5190865afa613b0a61269d565b90158015613c54575b613c4c5780518101916080826020850194031261041e5760208201516001600160401b03811161041e57836020613b4c92850101613a0e565b926040830151936060840151916001600160401b03831161041e578b6080613b7b8a9360208e978a0101613a0e565b9601519680151580613c41575b613bfc575b505050505082151580613bf1575b613ba9575b50505050505050565b613be095612e68948460a08c015260018060a01b039051169060018060a01b03905116915192519360405198899760208901612458565b60808301525f808080808080613ba0565b508151511515613b9b565b613c3293816060612e6894015260018060a01b038a511660018060a01b038d511690885192519360405198899760208901612458565b60408a0152865f868b82613b8d565b508351511515613b88565b505050505050565b50805115613b13565b9450505050565b602082015160208251015181115f14613c8257508051602082015252565b906020019060208251015110613c96575050565b52565b602082015190604081019160208351015181115f14613cbd57506060825191015252565b91506060019060208251015110613c96575050565b90929192613cde612c91565b50613ce7612c91565b506020840151602083015181115f14613cff57505090565b6020829592015110613d1057509190565b92509190565b60010b617fff198114611636575f0390565b613d37613d3d9193929361466a565b9261466a565b613da560a0840180511515806140c1575b613d5790612d34565b60608581015190840151613d78916001600160a01b03918216911614612cf6565b60808581015190840151613d99916001600160a01b03918216911614612967565b5160a083015114612d73565b8251815160c094850180519590930180516040516001600160a01b039485169792959490931692603292613e049291602091613de0826125e0565b8152604051809481926313c458e160e31b8352604060048401526044830190612a38565b6032602483015203818b5afa9182156107a6575f9261408b575b50613e509260209160405190613e33826125e0565b81526040516313c458e160e31b81529485928392600484016146e1565b0381865afa9081156107a6575f91614055575b613e6d9250612acc565b926032600a5b61ffff8116605a8111613edf57603214613ed457613e97818651868651918c614703565b868111613ec7575b5061ffff600a915b160161ffff811115613e7357634e487b7160e01b5f52601160045260245ffd5b955090508061ffff613e9f565b61ffff600a91613ea7565b5050604051929493919281613ef5606083612661565b600282526020820160403682378251156125cc5760041990528151600110156125cc57600560408301525f5b82518110156140485782518110156125cc5760208160051b840101518060010b5f811280809161402e575b8015614008575b613ffc5715613fd357613f669150613d16565b61ffff1661ffff85160361ffff8111611636575b61ffff811660018110159081613fc7575b50613f9d575b50600101975b97613f21565b613fad818c8b89518b5192614703565b888111613fbb575b50613f91565b9750915060015f613fb5565b6063915011155f613f8b565b5061ffff1661ffff85160161ffff811115613f7a57634e487b7160e01b5f52601160045260245ffd5b50505060010197613f97565b505f82138015613f53575061ffff614021848216612a25565b1661ffff88161015613f53565b5061ffff61403b83613d16565b1661ffff88161115613f4c565b5097505050505090509190565b90506020823d602011614083575b8161407060209383612661565b8101031261041e57613e6d915190613e63565b3d9150614063565b9091506020813d6020116140b9575b816140a760209383612661565b8101031261041e575190613e50613e1e565b3d915061409a565b5060a08301511515613d4e565b519069ffffffffffffffffffff8216820361041e57565b60ff6011199116019060ff821161163657565b60ff166012039060ff821161163657565b60ff16604d811161163657600a0a90565b6002546001600160a01b03169081156142b95760405163bcfd032d60e01b815273eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee6004820152610348602482015260a081604481865afa92835f925f95614263575b5061417f57505090505f905f90565b5f82131561425c57602060449160405192838092630b1c5a7560e31b825273eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee600483015261034860248301525afa9081156107a6575f91614220575b5060ff811660128110156141fd5750906141f36141ee6141f9936140f8565b614109565b90612b14565b9190565b6012101561421b57906142156141ee6141f9936140e5565b90612b27565b509190565b90506020813d602011614254575b8161423b60209383612661565b8101031261041e575160ff8116810361041e575f6141cf565b3d915061422e565b50505f9190565b9250935060a0823d60a0116142b1575b8161428060a09383612661565b8101031261041e57614291826140ce565b5060208201516142a86080606085015194016140ce565b5091935f614170565b3d9150614273565b5f91508190565b6002546001600160a01b0316919082156143d75760405163bcfd032d60e01b81526001600160a01b039091166004820181905261034860248301529260a082604481845afa93845f935f96614381575b506143205750505090505f905f90565b5f83131561437957602090604460405180948193630b1c5a7560e31b8352600483015261034860248301525afa9081156107a6575f91614220575060ff811660128110156141fd5750906141f36141ee6141f9936140f8565b5050505f9190565b9350945060a0833d60a0116143cf575b8161439e60a09383612661565b8101031261041e576143af836140ce565b5060208301516143c66080606086015195016140ce565b5092945f614310565b3d9150614391565b505f91508190565b6002546001600160a01b0316919082156143d75760405163bcfd032d60e01b81526001600160a01b039091166004820181905273eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee60248301529260a082604481845afa93845f935f966144bc575b506144515750505090505f905f90565b5f83131561437957602090604460405180948193630b1c5a7560e31b8352600483015273eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee60248301525afa9081156107a6575f91614220575060ff811660128110156141fd5750906141f36141ee6141f9936140f8565b9350945060a0833d60a01161450a575b816144d960a09383612661565b8101031261041e576144ea836140ce565b5060208301516145016080606086015195016140ce565b5092945f614441565b3d91506144cc565b6040519060205f8184019463095ea7b360e01b865260018060a01b0316948560248601528160448601526044855261454b606486612661565b84519082855af15f513d8261459f575b50501561456757505050565b61363d6132b0936040519063095ea7b360e01b602083015260248201525f604482015260448152614599606482612661565b826145fe565b9091506145bc57506001600160a01b0381163b15155b5f8061455b565b6001146145b5565b6040519060205f8184019463095ea7b360e01b865260018060a01b031694856024860152811960448601526044855261454b606486612661565b905f602091828151910182855af1156107a6575f513d61464d57506001600160a01b0381163b155b61462d5750565b635274afe760e01b5f9081526001600160a01b0391909116600452602490fd5b60011415614626565b51906001600160a01b038216820361041e57565b906146736131cb565b91805181019060e08183031261041e5761468f60208201614656565b916040820151916060810151916146a860808301614656565b916146b560a08201614656565b9160c08201519160e0810151916001600160401b03831161041e57613229926020809201920101613956565b9061ffff6146fc602092959495604085526040850190612a38565b9416910152565b6147429391929460209161471682612a25565b9460405190614724826125e0565b81526040518097819482936313c458e160e31b8452600484016146e1565b03916001600160a01b03165afa9283156107a6575f936147ec575b50906147919360209260405190614773826125e0565b81526040518096819482936313c458e160e31b8452600484016146e1565b03916001600160a01b03165afa9081156107a6575f916147b6575b612f059250612acc565b90506020823d6020116147e4575b816147d160209383612661565b8101031261041e57612f059151906147ac565b3d91506147c4565b919092506020823d60201161481c575b8161480960209383612661565b8101031261041e5790519161479161475d565b3d91506147fc56fe000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a2646970667358221220ae4883b2f6d001d10dab0eb4cafb86cfab7dc8d9d456146282072e794eb0d27564736f6c634300081e00330000000000000000000000000000000000000000000000000000000000000080000000000000000000000000a68be02a2b7ebecac84a6f33f2dda63b05911b95000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000027100000000000000000000000000000000000000000000000000000000000000004000000000000000000000000f3d3748f9093d41a380f9b94a056b05fed980d9100000000000000000000000002673b2a2f32e3f4ca1ac1cebccb6aa2c2114a2a000000000000000000000000afd80e1a3d3cf36e828a270120b12ff97006e7df000000000000000000000000bdcdabd2019f01da14620c2f95b6f4907e66eb55

Deployed Bytecode

0x610160604052600436101561001b575b3615610019575f80fd5b005b5f3560e01c80630197ea7c146123125780631163b2b01461223d5780631ed86f1914612214578063200f03fd146121f7578063204d406c146121aa57806323d8dbf7146120f257806324735bdf1461209f5780632fe9c11414611d2b57806335e8248f14611ced57806342f6e38914611cb05780634690484014611c885780635748108d14611b13578063715018a614611abc578063793f74e11461191257806381b2248a146118d0578063886f039a1461189057806388a77dc1146116c357806388ff8db8146116a85780638da5cb5b1461168157806390238c3914611659578063a0632461146114eb578063aaaf1c14146114cf578063ad5c4648146114a8578063b2494df3146113e0578063c7c73ee714611087578063c95cadd714610cf4578063caa4f16514610cd0578063cc654a6714610b8e578063e10f143114610a15578063e74b981b14610987578063eb2de53514610963578063efb0f22314610892578063f2fde38b1461080d578063f3f7f15b146104225763faa55b6d0361000f573461041e57606036600319011261041e576101b961237d565b6101c1612393565b604051916101ce8361262b565b6001600160a01b03908116835216602082015260443560408201526101f1612cb5565b6101f9612c91565b610201612c91565b60035490935f5b82811061033457505050602081019182511561032557805192602084519401519460408301519260208451940151602082015190606060208351930151930151936020855195015197602082015115158061030b575b156102fe5751905161026f91613d28565b969080965110156102f3575b604051998a996101408b526101408b0161029491612434565b9060208b015289810360408b01526102ab91612434565b90606089015287810360808901526102c291612434565b9060a087015285810360c08701526102d991612434565b9260e085015261010084015261ffff166101208301520390f35b5f965086955061027b565b505093505f935f9561027b565b50805160208151910120825160208151910120141561025e565b6305fcedf360e01b5f5260045ffd5b6103598282610342816125b4565b905460039190911b1c6001600160a01b0316613a48565b6060810151806103cf575b5060a08101518061037a575b5050600101610208565b966103c5929560019860808401519360208b8060a01b0382511691015191604051956103a587612646565b86526020860152604085015260608401526103c08389613c99565b613cd2565b959093905f610370565b909496610415929760408701519260018060a01b03885116602089015191604051956103fa87612646565b86526020860152604085015260608401526103c08389613c64565b9590935f610364565b5f80fd5b3461041e5761044a610450610436366124f7565b94909693979195610445613053565b613211565b95613211565b926105da60a08601918251151580610800575b61046c90612d34565b6060878101805191880151909491610491916001600160a01b03908116911614612cf6565b60808881018051918901519092916104b6916001600160a01b03908116911614612967565b878151916104ca60a0830193845114612d73565b6020898c61057e60c08201918251511515806107f2575b6104ea906129a6565b61ffff8416966001881015806107e7575b6105049061288b565b81516001600160a01b03165f9081526004875260409020546105289060ff166129e4565b516001600160a01b03165f90815260048652604090205461054b9060ff166129e4565b8b51865161056591309033906001600160a01b03166135f9565b8b5190518651916001600160a01b039182169116613370565b89518d51875161059c9290916001600160a01b039081169116613370565b8d5190516040516001600160a01b0390921691906105b9826125e0565b81525f604051809b81958294637215562960e01b8452309060048501612a9b565b03925af19485156107a6575f956107b1575b61063b9650602060018060a01b038c511660c08d01519060405191610610836125e0565b825261061b8d612a25565b915f604051809c81958294637215562960e01b8452309060048501612a9b565b03925af19687156107a6575f97610772575b50806106598888612acc565b101561066490612ad9565b8260408d01519061067491612b14565b6064900460408c01516106868c612a25565b61ffff1661069391612b14565b606490046106a091612acc565b85516001600160a01b031691908a6106b88a8a612acc565b906106c294613414565b9a5199519651935190516001600160a01b039a8b169a978816979485169490929116906106ef9083612b14565b6064900492516106fe8a612a25565b61ffff1661070b91612b14565b606490049361071a8787612acc565b976040519b6107288d61260f565b8c5260208c015260408b015260608a0152608089015260a088015260c087015260e08601526101008501526101208401523361076393613503565b60018055604051908152602090f35b9096506020813d60201161079e575b8161078e60209383612661565b8101031261041e5751958c61064d565b3d9150610781565b6040513d5f823e3d90fd5b94506020863d6020116107df575b816107cc60209383612661565b8101031261041e5761063b9551946105ec565b3d91506107bf565b5060638811156104fb565b5060c08701515115156104e1565b5060a08601511515610463565b3461041e57602036600319011261041e5761082661237d565b61082e61302d565b6001600160a01b0316801561087f575f80546001600160a01b03198116831782556001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a3005b631e4fbdf760e01b5f525f60045260245ffd5b3461041e57602036600319011261041e576108ab61237d565b6108b361302d565b6001600160a01b03811690811561092b573b156108e6576bffffffffffffffffffffffff60a01b60025416176002555f80f35b60405162461bcd60e51b815260206004820152601b60248201527f55523a207265676973747279206e6f74206120636f6e747261637400000000006044820152606490fd5b60405162461bcd60e51b815260206004820152601060248201526f55523a2062616420726567697374727960801b6044820152606490fd5b3461041e575f36600319011261041e57602061ffff60065460a01c16604051908152f35b3461041e57602036600319011261041e576109a061237d565b6109a861302d565b600680546001600160a01b0319166001600160a01b0392909216918217908190556040805160a083901c61ffff908116825260b09390931c90921660208301527ffc218f700eca8d3f8834187c973fff1cd7abf19039dbd4751c024df57d7385f99190819081015b0390a2005b3461041e57602036600319011261041e576004356001600160401b03811161041e573660238201121561041e578060040135906001600160401b03821161041e573660248360051b8301011161041e57610a6d61302d565b6003545f5b818110610b4d5750506003545f60035580610aed575b505f5b82811015610ac057600581901b820160240135906001600160a01b038216820361041e57610aba6001926130a9565b01610a8b565b7f24ec4b702193c608d667733c4e53752fd751d1f33023f9852db029c22a34ddf9602084604051908152a1005b60035f527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b017fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b5b818110610b425750610a88565b5f8155600101610b35565b80610b596001926125b4565b838060a01b0391549060031b1c16805f52600460205260405f2060ff1981541690555f5260056020525f604081205501610a72565b3461041e57604036600319011261041e5760043561ffff81169081810361041e576024359161ffff83169081840361041e57606490610bcb61302d565b11610c935761271010610c57576006805463ffffffff60a01b19811660a093841b61ffff60a01b161760b094851b61ffff60b01b161791829055604080519383901c61ffff90811685529290941c90911660208301526001600160a01b0316917ffc218f700eca8d3f8834187c973fff1cd7abf19039dbd4751c024df57d7385f9919081908101610a10565b60405162461bcd60e51b81526020600482015260146024820152730aaa47440e0dee640cccaca40e8dede40d0d2ced60631b6044820152606490fd5b60405162461bcd60e51b81526020600482015260156024820152740aaa47440e6eec2e040cccaca40e8dede40d0d2ced605b1b6044820152606490fd5b3461041e575f36600319011261041e57602061ffff60065460b01c16604051908152f35b3461041e57610d64610e96610d5e610d0b366124f7565b959793989291969094610d1c613053565b610d306001600160a01b0388161515612819565b61ffff88169860018a10158061107c575b610d4a9061288b565b610d548282613596565b610445848c613596565b97613211565b9460a0870190815115158061106f575b610d7d90612b45565b8151610d8f60a08901918251146128d7565b60608981018051918a0151909391610db4916001600160a01b03908116911614612cf6565b60c08a0193845151151580611061575b610dcd906129a6565b8a516001600160a01b03165f90815260046020526040902054610df29060ff166129e4565b89516001600160a01b03165f90815260046020526040902054610e179060ff166129e4565b83518151610e3191309033906001600160a01b03166135f9565b83518b518251610e4f9290916001600160a01b039081169116613370565b83518a518451610e6d9290916001600160a01b039081169116613370565b602089610e7981612a25565b8d51975160405191986001600160a01b031691906105b9826125e0565b03925af19485156107a6575f9561102b575b610eee96506020818c60c060018060a01b0382511691015160405190610ecd826125e0565b81525f604051809c81958294637215562960e01b8452309060048501612a9b565b03925af19687156107a6575f97610ff7575b50610f0b8787612acc565b97838d610f1a838c1015612ad9565b6040015190610f2891612b14565b6064900460408d01519261ffff169283610f4191612b14565b60649004610f4e91612acc565b90610f59308b613642565b610f64918a8c6137b1565b9b519a51945191516001600160a01b039b8c169b958616959093921691610f8b9084612b14565b60649004935190610f9b91612b14565b60649004936040519a610fad8c61260f565b8b5260208b015260408a0152606089015f5160206148255f395f51905f529052608089015260a088015260c087015260e08601526101008501526101208401523361076393613503565b9096506020813d602011611023575b8161101360209383612661565b8101031261041e5751958c610f00565b3d9150611006565b94506020863d602011611059575b8161104660209383612661565b8101031261041e57610eee955194610ea8565b3d9150611039565b5060c08a0151511515610dc4565b5060a08701511515610d74565b5060638a1115610d41565b3461041e5760e036600319011261041e576110a061237d565b6110a8612393565b606435916001600160a01b038316830361041e576110c46123bf565b916110cd6123f8565b9160c43593841515850361041e57604051956110e88761262b565b6001600160a01b03838116885284166020880152604435604088015261110c612cb5565b90611115612c91565b9761111e612c91565b60035490915f5b8281106113125750505060208901988951156103255783519360208551950151610120526020604082015180516101405201519260208201519060606020835193015193015193602085519501516101005260208201511515806112f8575b156112eb5751905161119591613d28565b9490809d5110156112e0575b6111ac88878b612db6565b996111ba919992988d612db6565b9d6111c7919d929c612f5d565b6080529d6111d491612f5d565b60e05260a05260405160c05260c051610280905260c051610280016111f891612434565b6101205160c0516020015260c051810360c05160400152610140519061121d91612434565b9060c0516060015260c051810360c0516080015261123a91612434565b9060c05160a0015260c051810360c05160c0015261125791612434565b986101005160c05160e0015260c051610100015261ffff1660c051610120015260c051610140015260c051610160015260ff1660c051610180015260c0516101a0015260c0516101c0015260ff1660c0516101e0015260c051610200015260805160c051610220015260a05160c051610240015260e05160c051610260015260c051900360c051f35b5f9c508c94506111a1565b50509a505f9a5f936111a1565b508051602081519101208251602081519101201415611184565b6113208282610342816125b4565b606081015180611391575b5060a081015180611341575b5050600101611125565b94611387929d6001966080840151936020898060a01b03825116910151916040519561136c87612646565b86526020860152604085015260608401526103c0838a613c99565b93909b908d611337565b94819d6113d79396604084015193602060018060a01b0382511691015191604051956113bc87612646565b86526020860152604085015260608401526103c0838a613c64565b93909b8d61132b565b3461041e575f36600319011261041e576040518060206003549283815201809260035f527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b905f5b818110611489575050508161143e910382612661565b604051918291602083019060208452518091526040830191905f5b818110611467575050500390f35b82516001600160a01b0316845285945060209384019390920191600101611459565b82546001600160a01b0316845260209093019260019283019201611428565b3461041e575f36600319011261041e5760206040515f5160206148255f395f51905f528152f35b3461041e575f36600319011261041e5760206040516127108152f35b3461041e57602036600319011261041e5761150461237d565b61150c61302d565b6001600160a01b03165f81815260056020526040902054801561164a575f198101818111611636576003545f198101908111611636578082036115d4575b50505060035480156115c0575f1901611562816125b4565b81546001600160a01b03600392831b1b1916909155555f818152600460209081526040808320805460ff19169055600590915281208190557f0a1ee69f55c33d8467c69ca59ce2007a737a88603d75392972520bf67cb513b89080a2005b634e487b7160e01b5f52603160045260245ffd5b6115fe916115e4611622926125b4565b905460039190911b1c6001600160a01b03169283916125b4565b81546001600160a01b0393841660039290921b91821b9390911b1916919091179055565b5f52600560205260405f205581808061154a565b634e487b7160e01b5f52601160045260245ffd5b632cd2a9d360e21b5f5260045ffd5b3461041e575f36600319011261041e576002546040516001600160a01b039091168152602090f35b3461041e575f36600319011261041e575f546040516001600160a01b039091168152602090f35b3461041e575f36600319011261041e57602060405160648152f35b3461041e576116fb6116d43661256e565b939092916116e0613053565b6001600160a01b038416926116f6841515612819565b6127f0565b989197909295945060018060a01b03851694855f52600460205261172560ff60405f2054166129e4565b8315611857578961175e8560209361174261178f9e5115156129a6565b6001600160a01b038b169a6117598330338f6135f9565b613370565b6040519061176b826125e0565b8152604051809b8192637215562960e01b8352606060048401526064830190612a38565b3060248301526064604483015203815f895af19889156107a6575f99611821575b509083896117cc936117c68460209d1015612ad9565b8a613414565b9560405194855260018060a01b031687850152604084015284606084015260808301527fb19d709f69027ded94fa264e8317793c05de52cf8f379f7655d49a585ce7b1f060a03393a460018055604051908152f35b9850906020893d60201161184f575b8161183d60209383612661565b8101031261041e5797519790836117b0565b3d9150611830565b60405162461bcd60e51b81526020600482015260116024820152702aa91d103d32b9379030b6b7bab73a24b760791b6044820152606490fd5b3461041e57604036600319011261041e576118ca6118ac61237d565b6118b4612393565b906118bd61302d565b6118c5613053565b612b7f565b60018055005b3461041e57602036600319011261041e5760043560035481101561041e576118f96020916125b4565b905460405160039290921b1c6001600160a01b03168152f35b3461041e576119e560206119566119283661256e565b949161193694919394613053565b6001600160a01b0384169461194c861515612819565b6116f68282613596565b91509893506119b48960018060a09a969899959a1b03881697885f526004855261198660ff60405f2054166129e4565b611991821515612b45565b61199d845115156129a6565b6001600160a01b038a16996117598330338e6135f9565b604051906119c1826125e0565b8152604051809a8192637215562960e01b8352606060048401526064830190612a38565b3060248301526064604483015203815f885af19788156107a6575f98611a86575b50908288611a2b93611a1c8460209c1015612ad9565b611a263083613642565b6137b1565b946040519384525f5160206148255f395f51905f5287850152604084015284606084015260808301527fb19d709f69027ded94fa264e8317793c05de52cf8f379f7655d49a585ce7b1f060a03393a460018055604051908152f35b9750906020883d602011611ab4575b81611aa260209383612661565b8101031261041e579651969082611a06565b3d9150611a95565b3461041e575f36600319011261041e57611ad461302d565b5f80546001600160a01b0319811682556001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b611bb2611b5d6020611b243661256e565b929391611b32959195613053565b6001600160a01b03861694611b48861515612819565b611b53341515612852565b6116f68282613168565b919794509892949591506119b460018060a01b03871696875f5260048452611b8b60ff60405f2054166129e4565b611b97835115156129a6565b611ba2348c14612923565b611bab34613260565b34906132b2565b3060248301526064604483015203815f885af19788156107a6575f98611c52575b50908288611bef93611be98460209c1015612ad9565b87613414565b94604051935f5160206148255f395f51905f52855260018060a01b031687850152604084015284606084015260808301527fb19d709f69027ded94fa264e8317793c05de52cf8f379f7655d49a585ce7b1f060a03393a460018055604051908152f35b9750906020883d602011611c80575b81611c6e60209383612661565b8101031261041e579651969082611bd3565b3d9150611c61565b3461041e575f36600319011261041e576006546040516001600160a01b039091168152602090f35b3461041e57602036600319011261041e576001600160a01b03611cd161237d565b165f526004602052602060ff60405f2054166040519015158152f35b3461041e57604036600319011261041e57611d0661237d565b60243590811515820361041e57604091611d1f91612f5d565b82519182526020820152f35b611d9b611ec0610d5e611d3d366124f7565b959793989291969094611d4e613053565b611d626001600160a01b0388161515612819565b611d6d341515612852565b61ffff88169860018a101580612094575b611d879061288b565b611d918282613168565b610445848c613168565b94611dbb60a08801611db3815160a08a0151146128d7565b513414612923565b60808781018051918801519091611ddf916001600160a01b03908116911614612967565b60c08801805151151580612086575b611df7906129a6565b88516001600160a01b03165f90815260046020526040902054611e1c9060ff166129e4565b87516001600160a01b03165f90815260046020526040902054611e419060ff166129e4565b611e4a34613260565b8851611e609034906001600160a01b03166132b2565b8751611e769034906001600160a01b03166132b2565b602087611e8281612a25565b8b51935160405191946001600160a01b03169190611e9f826125e0565b81525f604051809981958294637215562960e01b8452309060048501612a9b565b03925af19283156107a6575f93612050575b611f17945060208260018060a01b038b511660c08c015160405190611ef6826125e0565b81525f604051809a81958294637215562960e01b8452309060048501612a9b565b03925af19485156107a6575f9561201c575b5086611f358686612acc565b96611f4281891015612ad9565b8260408d015190611f5291612b14565b6064900460408c01519461ffff169485611f6b91612b14565b60649004611f7891612acc565b8551611f8e938a916001600160a01b0316613414565b9951985192516001600160a01b03998a1699938416931690611fb09034612b14565b6064900491611fbf9034612b14565b606490049260405199611fd18b61260f565b8a5260208a0152604089015f5160206148255f395f51905f529052606089015234608089015260a088015260c087015260e08601526101008501526101208401523361076393613503565b9094506020813d602011612048575b8161203860209383612661565b8101031261041e5751938a611f29565b3d915061202b565b92506020843d60201161207e575b8161206b60209383612661565b8101031261041e57611f17935192611ed2565b3d915061205e565b5060c0880151511515611dee565b5060638a1115611d7e565b3461041e57602036600319011261041e576004356001600160401b03811161041e576120db6120d56120ee923690600401612407565b906127f0565b9360409795979391935197889788612458565b0390f35b3461041e5760c036600319011261041e5761014061210e61237d565b60ff61216661211b612393565b926121246123a9565b9061212d6123e9565b916121366123bf565b928561215c6121436123f8565b61216e6121518b8888612db6565b94919c90988a612db6565b9a91969097612f5d565b929098612f5d565b9790966040519b8c5260208c015260408b015260608a015216608088015260a087015260c086015260e085015261010084015216610120820152f35b3461041e57606036600319011261041e57606060ff6121e06121ca61237d565b6121d2612393565b6121da6123a9565b91612db6565b906040939293519384526020840152166040820152f35b3461041e575f36600319011261041e576020600354604051908152f35b3461041e57602036600319011261041e5761001961223061237d565b61223861302d565b6130a9565b3461041e57602036600319011261041e5761225661237d565b61225e61302d565b612266613053565b6001600160a01b03811661230d57505f546001600160a01b03165b47905f80808085855af161229361269d565b50156122d5576040519182526001600160a01b0316907fab31ddb82a9ca2de51c9befe9da7c6e815e12eac49ae58b8d9cb6b7a181cbc7190602090a260018055005b60405162461bcd60e51b815260206004820152601060248201526f115512081cddd9595c0819985a5b195960821b6044820152606490fd5b612281565b3461041e57608036600319011261041e5760a061232d61237d565b60ff61235f61233a612393565b926123556123466123a9565b61234e6123e9565b9583612db6565b9391959092612f5d565b91604051958652602086015260408501526060840152166080820152f35b600435906001600160a01b038216820361041e57565b602435906001600160a01b038216820361041e57565b604435906001600160a01b038216820361041e57565b608435906001600160a01b038216820361041e57565b35906001600160a01b038216820361041e57565b60643590811515820361041e57565b60a43590811515820361041e57565b9181601f8401121561041e578235916001600160401b03831161041e576020838186019501011161041e57565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b9593919492909460e087019560018060a01b031687526020870152604086015260018060a01b0316606085015260018060a01b0316608084015260a083015260e060c0830152825180915261010082019160206101008360051b8301019401925f915b8383106124ca57505050505090565b90919293946020806124e860019360ff198682030187528951612434565b970193019301919392906124bb565b60a060031982011261041e576004356001600160401b03811161041e578161252191600401612407565b92909291602435906001600160401b03821161041e5761254391600401612407565b909160443561ffff8116810361041e5790606435906084356001600160a01b038116810361041e5790565b606060031982011261041e57600435906001600160401b03821161041e5761259891600401612407565b90916024356001600160a01b038116810361041e579060443590565b6003548110156125cc5760035f5260205f2001905f90565b634e487b7160e01b5f52603260045260245ffd5b602081019081106001600160401b038211176125fb57604052565b634e487b7160e01b5f52604160045260245ffd5b61014081019081106001600160401b038211176125fb57604052565b606081019081106001600160401b038211176125fb57604052565b608081019081106001600160401b038211176125fb57604052565b90601f801991011681019081106001600160401b038211176125fb57604052565b6001600160401b0381116125fb57601f01601f191660200190565b3d156126c7573d906126ae82612682565b916126bc6040519384612661565b82523d5f602084013e565b606090565b6001600160401b0381116125fb5760051b60200190565b60e08183031261041e576126f6816123d5565b9260208201359260408301359261270f606082016123d5565b9261271c608083016123d5565b9260a08301359260c0810135906001600160401b03821161041e57019080601f8301121561041e57813591612750836126cc565b9261275e6040519485612661565b80845260208085019160051b8301019183831161041e5760208101915b83831061278a57505050505090565b82356001600160401b03811161041e57820185603f8201121561041e576020810135916127b683612682565b6127c36040519182612661565b838152604083850101881061041e575f60208581966040839701838601378301015281520192019161277b565b6127fc918101906126e3565b6001600160a01b0396871697959694959385169490921692909190565b1561282057565b60405162461bcd60e51b815260206004820152600a60248201526955523a2062616420746f60b01b6044820152606490fd5b1561285957565b60405162461bcd60e51b815260206004820152600a6024820152690aaa47440dcde408aa8960b31b6044820152606490fd5b1561289257565b60405162461bcd60e51b815260206004820152601960248201527f55523a2070657263656e74206f7574206f6620626f756e6473000000000000006044820152606490fd5b156128de57565b60405162461bcd60e51b815260206004820152601960248201527f55523a2073706c697420616d6f756e74206d69736d61746368000000000000006044820152606490fd5b1561292a57565b60405162461bcd60e51b81526020600482015260156024820152742aa91d103b30b63ab290109e9030b6b7bab73a24b760591b6044820152606490fd5b1561296e57565b60405162461bcd60e51b815260206004820152601060248201526f0aaa47440deeae840dad2e6dac2e8c6d60831b6044820152606490fd5b156129ad57565b60405162461bcd60e51b815260206004820152600f60248201526e55523a20656d70747920726f75746560881b6044820152606490fd5b156129eb57565b60405162461bcd60e51b815260206004820152601260248201527155523a20756e6b6e6f776e206d6f64756c6560701b6044820152606490fd5b61ffff166064039061ffff821161163657565b90602081019151916020825282518091526040820191602060408360051b8301019401925f915b838310612a6e57505050505090565b9091929394602080612a8c600193603f198682030187528951612434565b97019301930191939290612a5f565b91939261ffff90612ab6604093606086526060860190612a38565b6001600160a01b03909616602085015216910152565b9190820180921161163657565b15612ae057565b60405162461bcd60e51b815260206004820152600c60248201526b55523a20736c69707061676560a01b6044820152606490fd5b8181029291811591840414171561163657565b8115612b31570490565b634e487b7160e01b5f52601260045260245ffd5b15612b4c57565b60405162461bcd60e51b815260206004820152600b60248201526a2aa91d103d32b9379034b760a91b6044820152606490fd5b6001600160a01b0316908115612c5e576001600160a01b038116612c5857505f546001600160a01b0316905b6040516370a0823160e01b815230600482015291602083602481855afa9283156107a6575f93612c24575b508215612c1f57602081612c0c857faca8fb252cde442184e5f10e0f2e6e4029e8cd7717cae63559079610702436aa948661391b565b6040519485526001600160a01b031693a3565b505050565b9092506020813d602011612c50575b81612c4060209383612661565b8101031261041e5751915f612bd6565b3d9150612c33565b90612bab565b60405162461bcd60e51b815260206004820152600b60248201526a055523a20746f6b656e3d360ac1b6044820152606490fd5b60405190612c9e82612646565b5f6060838181528260208201528260408201520152565b60405190612cc282612646565b81612ccb612c91565b8152612cd5612c91565b6020820152612ce2612c91565b60408201526060612cf1612c91565b910152565b15612cfd57565b60405162461bcd60e51b815260206004820152600f60248201526e0aaa47440d2dc40dad2e6dac2e8c6d608b1b6044820152606490fd5b15612d3b57565b60405162461bcd60e51b815260206004820152601060248201526f55523a207a65726f20616d6f756e747360801b6044820152606490fd5b15612d7a57565b60405162461bcd60e51b81526020600482015260146024820152730aaa47440e8dee8c2d892dc40dad2e6dac2e8c6d60631b6044820152606490fd5b9192906001600160a01b03831615612f52575f8060405160208101906370a0823160e01b825260018060a01b038516602482015260248152612df9604482612661565b5190865afa612e0661269d565b9080612f46575b15612f35576020815191818082019384920101031261041e575f91612e68612e76849351975b604051636eb1769f60e11b602082019081526001600160a01b0395861660248301529490911660448201529182906064820190565b03601f198101835282612661565b5190855afa612e8361269d565b9080612f29575b15612f1f576020815191818082019384920101031261041e575f809151935b60405163313ce56760e01b602082019081526004825290612ecb602482612661565b51915afa612ed761269d565b9080612f13575b15612f0e576020815191818082019384920101031261041e575160ff811115612f08575060ff5b90565b60ff1690565b505f90565b50602081511015612ede565b505f808093612ea9565b50602081511015612e8a565b505f8091612e68612e768397612e33565b50602081511015612e0d565b3192505f9150601290565b90801561301c575b613007575b612f73816142c0565b90806130015750506001600160a01b0381165f5160206148255f395f51905f5214612ff457612fa1906143df565b81929115612fee57612fb161411a565b81929115612fe55791612fd891670de0b6b3a7640000938082105f14612fdd575094612b14565b049190565b905094612b14565b935050505f9190565b5f925090565b50612ffd61411a565b9091565b92909150565b61300f61411a565b9080613001575050612f6a565b506001600160a01b03811615612f65565b5f546001600160a01b0316330361304057565b63118cdaa760e01b5f523360045260245ffd5b600260015414613064576002600155565b60405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606490fd5b6001600160a01b03811690811561314a57815f52600460205260ff60405f20541661315957803b1561314a57815f52600460205260405f20600160ff1982541617905560035490680100000000000000008210156125fb576115fe82600161311494016003556125b4565b600354815f52600560205260405f20557fead6a006345da1073a106d5f32372d2d2204f46cb0b4bca8f5ebafcbbed12b8a5f80a2565b63d92e233d60e01b5f5260045ffd5b633b3a461d60e21b5f5260045ffd5b5f5160206148255f395f51905f5291506060013560601c0361318657565b60405162461bcd60e51b815260206004820152601b60248201527f55523a207061796c6f616420746f6b656e496e20213d205745544800000000006044820152606490fd5b6040519060e082018281106001600160401b038211176125fb57604052606060c0835f81525f60208201525f60408201525f838201525f60808201525f60a08201520152565b613229909291926132206131cb565b938101906126e3565b60c089015260a08801526001600160a01b039081166080880152908116606087015260408601919091526020850191909152168252565b5f5160206148255f395f51905f523b1561041e575f60049160405192838092630d0e30db60e41b82525f5160206148255f395f51905f525af180156107a6576132a65750565b5f6132b091612661565b565b604051636eb1769f60e11b81523060048201526001600160a01b0382166024820152916020836044815f5160206148255f395f51905f525afa9283156107a6575f9361333c575b508210613304575050565b6132b091613320575b5f5160206148255f395f51905f526145c4565b613337815f5160206148255f395f51905f52614512565b61330d565b9092506020813d602011613368575b8161335860209383612661565b8101031261041e5751915f6132f9565b3d915061334b565b604051636eb1769f60e11b81523060048201526001600160a01b0383811660248301529093911690602084604481855afa9384156107a6575f946133d3575b5083106133bb57505050565b6132b092156145c4576133ce8282614512565b6145c4565b9093506020813d6020116133ff575b816133ef60209383612661565b8101031261041e5751925f6133af565b3d91506133e2565b9190820391821161163657565b93949282156134f9576132b09394958082115f146134f15750945b6006546001600160a01b03811696613452915f9182918a6134a0575b5050612acc565b92808411613498575b8361346591613407565b9583879461347e575b50506001600160a01b031661391b565b613491916001600160a01b03841661391b565b5f8061346e565b92508261345b565b61ffff8160a01c16806134e0575b5060b01c61ffff1690811515806134d7575b1561344b5761271092935087030204905f8061344b565b508088116134c0565b61271090890204925061ffff6134ae565b90509461342f565b5050509150505f90565b80516020808301516040808501516060808701516080808901516101209099015185516001600160a01b03998a16815296891697870197909752928716938501939093529185169183019190915281019390935260a083015261ffff9490941660c08201529183169216907fefb23ebb23587ddae20adda5913cf9b74002499a5f9a8ba79ed13193238a694c9060e090a3565b5f5160206148255f395f51905f5291506080013560601c036135b457565b60405162461bcd60e51b815260206004820152601c60248201527f55523a207061796c6f616420746f6b656e4f757420213d2057455448000000006044820152606490fd5b6040516323b872dd60e01b60208201526001600160a01b0392831660248201529290911660448301526064808301939093529181526132b09161363d608483612661565b6145fe565b6040516370a0823160e01b815230600482015290915f916020816024815f5160206148255f395f51905f525afa80156107a65784915f9161377c575b501061373f575f5160206148255f395f51905f523b1561041e57604051632e1a7d4d60e01b8152600481018490525f81602481835f5160206148255f395f51905f525af180156107a65761372a575b5081809381925af16136dd61269d565b50156136e557565b60405162461bcd60e51b815260206004820152601760248201527f55523a20455448207472616e73666572206661696c65640000000000000000006044820152606490fd5b6137379192505f90612661565b5f905f6136cd565b60405162461bcd60e51b81526020600482015260156024820152740aaa47440d2dce6eaccccd2c6d2cadce840ae8aa89605b1b6044820152606490fd5b9150506020813d6020116137a9575b8161379860209383612661565b8101031261041e578390515f61367e565b3d915061378b565b919093928415613913578082111561390b5750905b6006546001600160a01b038116926137e8915f918291866138ba575050612acc565b938085116138b2575b846137fb91613407565b93849281613855575b50505f80809381935af161381661269d565b501561381e57565b60405162461bcd60e51b815260206004820152600f60248201526e115512081e19995c8819985a5b1959608a1b6044820152606490fd5b5f80939450809281925af161386861269d565b50156138775782905f80613804565b60405162461bcd60e51b815260206004820152601360248201527219995948115512081e19995c8819985a5b1959606a1b6044820152606490fd5b9350836137f1565b61ffff8160a01c16806138fa575b5060b01c61ffff1690811515806138f1575b1561344b5761271092935088030204905f8061344b565b508089116138da565b612710908a0204925061ffff6138c8565b9050906137c6565b505f93505050565b60405163a9059cbb60e01b60208201526001600160a01b0390921660248301526044808301939093529181526132b09161363d606483612661565b9080601f8301121561041e5781519161396e836126cc565b9261397c6040519485612661565b80845260208085019160051b8301019183831161041e5760208101915b8383106139a857505050505090565b82516001600160401b03811161041e57820185603f8201121561041e576020810151916139d483612682565b6139e16040519182612661565b838152604083850101881061041e575f602085819660408397018386015e83010152815201920191613999565b91909160208184031261041e5760405190613a28826125e0565b819381516001600160401b03811161041e57613a449201613956565b9052565b92916040519360c085018581106001600160401b038211176125fb57604052606060408601525f6060860152606060808601525f60a08601528460018060a01b0382168082528460208301525f52600460205260ff60405f20541615613c5d575081516020808401805160408087018051915163faa55b6d60e01b9581019586526001600160a01b0396871660248201529290951660448301526064820152909492915f91829190613afd8160848101612e68565b5190865afa613b0a61269d565b90158015613c54575b613c4c5780518101916080826020850194031261041e5760208201516001600160401b03811161041e57836020613b4c92850101613a0e565b926040830151936060840151916001600160401b03831161041e578b6080613b7b8a9360208e978a0101613a0e565b9601519680151580613c41575b613bfc575b505050505082151580613bf1575b613ba9575b50505050505050565b613be095612e68948460a08c015260018060a01b039051169060018060a01b03905116915192519360405198899760208901612458565b60808301525f808080808080613ba0565b508151511515613b9b565b613c3293816060612e6894015260018060a01b038a511660018060a01b038d511690885192519360405198899760208901612458565b60408a0152865f868b82613b8d565b508351511515613b88565b505050505050565b50805115613b13565b9450505050565b602082015160208251015181115f14613c8257508051602082015252565b906020019060208251015110613c96575050565b52565b602082015190604081019160208351015181115f14613cbd57506060825191015252565b91506060019060208251015110613c96575050565b90929192613cde612c91565b50613ce7612c91565b506020840151602083015181115f14613cff57505090565b6020829592015110613d1057509190565b92509190565b60010b617fff198114611636575f0390565b613d37613d3d9193929361466a565b9261466a565b613da560a0840180511515806140c1575b613d5790612d34565b60608581015190840151613d78916001600160a01b03918216911614612cf6565b60808581015190840151613d99916001600160a01b03918216911614612967565b5160a083015114612d73565b8251815160c094850180519590930180516040516001600160a01b039485169792959490931692603292613e049291602091613de0826125e0565b8152604051809481926313c458e160e31b8352604060048401526044830190612a38565b6032602483015203818b5afa9182156107a6575f9261408b575b50613e509260209160405190613e33826125e0565b81526040516313c458e160e31b81529485928392600484016146e1565b0381865afa9081156107a6575f91614055575b613e6d9250612acc565b926032600a5b61ffff8116605a8111613edf57603214613ed457613e97818651868651918c614703565b868111613ec7575b5061ffff600a915b160161ffff811115613e7357634e487b7160e01b5f52601160045260245ffd5b955090508061ffff613e9f565b61ffff600a91613ea7565b5050604051929493919281613ef5606083612661565b600282526020820160403682378251156125cc5760041990528151600110156125cc57600560408301525f5b82518110156140485782518110156125cc5760208160051b840101518060010b5f811280809161402e575b8015614008575b613ffc5715613fd357613f669150613d16565b61ffff1661ffff85160361ffff8111611636575b61ffff811660018110159081613fc7575b50613f9d575b50600101975b97613f21565b613fad818c8b89518b5192614703565b888111613fbb575b50613f91565b9750915060015f613fb5565b6063915011155f613f8b565b5061ffff1661ffff85160161ffff811115613f7a57634e487b7160e01b5f52601160045260245ffd5b50505060010197613f97565b505f82138015613f53575061ffff614021848216612a25565b1661ffff88161015613f53565b5061ffff61403b83613d16565b1661ffff88161115613f4c565b5097505050505090509190565b90506020823d602011614083575b8161407060209383612661565b8101031261041e57613e6d915190613e63565b3d9150614063565b9091506020813d6020116140b9575b816140a760209383612661565b8101031261041e575190613e50613e1e565b3d915061409a565b5060a08301511515613d4e565b519069ffffffffffffffffffff8216820361041e57565b60ff6011199116019060ff821161163657565b60ff166012039060ff821161163657565b60ff16604d811161163657600a0a90565b6002546001600160a01b03169081156142b95760405163bcfd032d60e01b815273eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee6004820152610348602482015260a081604481865afa92835f925f95614263575b5061417f57505090505f905f90565b5f82131561425c57602060449160405192838092630b1c5a7560e31b825273eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee600483015261034860248301525afa9081156107a6575f91614220575b5060ff811660128110156141fd5750906141f36141ee6141f9936140f8565b614109565b90612b14565b9190565b6012101561421b57906142156141ee6141f9936140e5565b90612b27565b509190565b90506020813d602011614254575b8161423b60209383612661565b8101031261041e575160ff8116810361041e575f6141cf565b3d915061422e565b50505f9190565b9250935060a0823d60a0116142b1575b8161428060a09383612661565b8101031261041e57614291826140ce565b5060208201516142a86080606085015194016140ce565b5091935f614170565b3d9150614273565b5f91508190565b6002546001600160a01b0316919082156143d75760405163bcfd032d60e01b81526001600160a01b039091166004820181905261034860248301529260a082604481845afa93845f935f96614381575b506143205750505090505f905f90565b5f83131561437957602090604460405180948193630b1c5a7560e31b8352600483015261034860248301525afa9081156107a6575f91614220575060ff811660128110156141fd5750906141f36141ee6141f9936140f8565b5050505f9190565b9350945060a0833d60a0116143cf575b8161439e60a09383612661565b8101031261041e576143af836140ce565b5060208301516143c66080606086015195016140ce565b5092945f614310565b3d9150614391565b505f91508190565b6002546001600160a01b0316919082156143d75760405163bcfd032d60e01b81526001600160a01b039091166004820181905273eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee60248301529260a082604481845afa93845f935f966144bc575b506144515750505090505f905f90565b5f83131561437957602090604460405180948193630b1c5a7560e31b8352600483015273eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee60248301525afa9081156107a6575f91614220575060ff811660128110156141fd5750906141f36141ee6141f9936140f8565b9350945060a0833d60a01161450a575b816144d960a09383612661565b8101031261041e576144ea836140ce565b5060208301516145016080606086015195016140ce565b5092945f614441565b3d91506144cc565b6040519060205f8184019463095ea7b360e01b865260018060a01b0316948560248601528160448601526044855261454b606486612661565b84519082855af15f513d8261459f575b50501561456757505050565b61363d6132b0936040519063095ea7b360e01b602083015260248201525f604482015260448152614599606482612661565b826145fe565b9091506145bc57506001600160a01b0381163b15155b5f8061455b565b6001146145b5565b6040519060205f8184019463095ea7b360e01b865260018060a01b031694856024860152811960448601526044855261454b606486612661565b905f602091828151910182855af1156107a6575f513d61464d57506001600160a01b0381163b155b61462d5750565b635274afe760e01b5f9081526001600160a01b0391909116600452602490fd5b60011415614626565b51906001600160a01b038216820361041e57565b906146736131cb565b91805181019060e08183031261041e5761468f60208201614656565b916040820151916060810151916146a860808301614656565b916146b560a08201614656565b9160c08201519160e0810151916001600160401b03831161041e57613229926020809201920101613956565b9061ffff6146fc602092959495604085526040850190612a38565b9416910152565b6147429391929460209161471682612a25565b9460405190614724826125e0565b81526040518097819482936313c458e160e31b8452600484016146e1565b03916001600160a01b03165afa9283156107a6575f936147ec575b50906147919360209260405190614773826125e0565b81526040518096819482936313c458e160e31b8452600484016146e1565b03916001600160a01b03165afa9081156107a6575f916147b6575b612f059250612acc565b90506020823d6020116147e4575b816147d160209383612661565b8101031261041e57612f059151906147ac565b3d91506147c4565b919092506020823d60201161481c575b8161480960209383612661565b8101031261041e5790519161479161475d565b3d91506147fc56fe000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2a2646970667358221220ae4883b2f6d001d10dab0eb4cafb86cfab7dc8d9d456146282072e794eb0d27564736f6c634300081e0033

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

0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000a68be02a2b7ebecac84a6f33f2dda63b05911b95000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000027100000000000000000000000000000000000000000000000000000000000000004000000000000000000000000f3d3748f9093d41a380f9b94a056b05fed980d9100000000000000000000000002673b2a2f32e3f4ca1ac1cebccb6aa2c2114a2a000000000000000000000000afd80e1a3d3cf36e828a270120b12ff97006e7df000000000000000000000000bdcdabd2019f01da14620c2f95b6f4907e66eb55

-----Decoded View---------------
Arg [0] : _modules (address[]): 0xf3D3748f9093d41a380f9b94a056B05FED980D91,0x02673b2a2F32e3f4ca1ac1ceBCcB6aA2c2114A2a,0xAFD80E1a3d3cf36E828a270120b12FF97006E7DF,0xbdcdaBD2019f01da14620c2f95b6f4907E66EB55
Arg [1] : _feeRecipient (address): 0xa68be02a2B7EbeCac84a6F33F2DDa63B05911b95
Arg [2] : _feeBpsSwap (uint16): 5
Arg [3] : _feeBpsPositive (uint16): 10000

-----Encoded View---------------
9 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000000000000000000000000000000000080
Arg [1] : 000000000000000000000000a68be02a2b7ebecac84a6f33f2dda63b05911b95
Arg [2] : 0000000000000000000000000000000000000000000000000000000000000005
Arg [3] : 0000000000000000000000000000000000000000000000000000000000002710
Arg [4] : 0000000000000000000000000000000000000000000000000000000000000004
Arg [5] : 000000000000000000000000f3d3748f9093d41a380f9b94a056b05fed980d91
Arg [6] : 00000000000000000000000002673b2a2f32e3f4ca1ac1cebccb6aa2c2114a2a
Arg [7] : 000000000000000000000000afd80e1a3d3cf36e828a270120b12ff97006e7df
Arg [8] : 000000000000000000000000bdcdabd2019f01da14620c2f95b6f4907e66eb55


Deployed Bytecode Sourcemap

5079:72119:9:-:0;;;;;;;;;-1:-1:-1;5079:72119:9;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;5079:72119:9;;;;;;:::i;:::-;;;:::i;:::-;;;;;;;:::i;:::-;-1:-1:-1;;;;;5079:72119:9;;;;;;;47656:119;;5079:72119;;;;47656:119;;5079:72119;;;:::i;:::-;;;:::i;:::-;;;:::i;:::-;47976:7;5079:72119;47957:13;;5079:72119;47972:18;;;;;;49114:21;;;5079:72119;49114:21;;5079:72119;;;49114:26;49110:53;;49237:12;;:20;5079:72119;49237:20;;49279:22;;5079:72119;49332:12;5079:72119;49332:12;;;:20;5079:72119;49332:20;;49374:22;;5079:72119;;49427:15;;;:23;5079:72119;;49427:23;;49472:25;;5079:72119;49528:15;;;:23;5079:72119;49528:23;;49573:25;;5079:72119;49689:21;5079:72119;49689:21;;5079:72119;49689:25;;:93;;;47952:1146;49685:745;;;49865:19;49904;;49833:105;;;:::i;:::-;49799:139;;;5079:72119;;-1:-1:-1;50126:39:9;50122:136;;49685:745;5079:72119;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;:::i;:::-;;48583:20;5079:72119;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;50122:136;5079:72119;;-1:-1:-1;5079:72119:9;;-1:-1:-1;50122:136:9;;49685:745;50368:18;;;;5079:72119;50401:17;5079:72119;49685:745;;;49689:93;49728:19;;;5079:72119;;;;;49718:30;49762:19;;5079:72119;;;;;49752:30;49718:64;;49689:93;;49110:53;55559:14;;;5079:72119;49149:14;5079:72119;;49149:14;47957:13;48038:35;48055:10;;;;;:::i;:::-;5079:72119;;47976:7;5079:72119;;;;;-1:-1:-1;;;;;5079:72119:9;48038:35;:::i;:::-;5079:72119;48094:20;;5079:72119;48094:24;48090:473;;47957:13;48583:20;;;;5079:72119;48583:24;48579:474;;47957:13;5079:72119;;;;47957:13;;48579:474;48698:18;48990:47;48698:18;;5079:72119;48698:18;;;;;5079:72119;;;;;;;;;;48850:18;;5079:72119;;;;;;;;:::i;:::-;;;;48653:235;;5079:72119;;48653:235;;5079:72119;;48653:235;;5079:72119;48935:5;;;;:::i;:::-;48990:47;:::i;:::-;48961:76;;48579:474;;;;;48090:473;48209:18;;;48500:47;48209:18;;5079:72119;48209:18;;;5079:72119;;;;;;;;;;48361:18;;5079:72119;;;;;;;;:::i;:::-;;;;48164:235;;5079:72119;;48164:235;;5079:72119;;48164:235;;5079:72119;48446:4;;;;:::i;48500:47::-;48471:76;;48090:473;;;;5079:72119;;;;;;;;68811:36;68880;5079:72119;;;:::i;:::-;2227:103:4;;;;;;;;;:::i;:::-;68811:36:9;:::i;:::-;68880;;:::i;:::-;68945:10;69986:136;68945:10;;;5079:72119;;;68945:14;;:32;;;5079:72119;68937:61;;;:::i;:::-;69017:9;;;;5079:72119;;69030:9;;;5079:72119;69017:9;;5079:72119;69009:50;;-1:-1:-1;;;;;5079:72119:9;;;;;69017:22;69009:50;:::i;:::-;69078:10;;;;5079:72119;;69092:10;;;5079:72119;69078:10;;5079:72119;69070:53;;-1:-1:-1;;;;;5079:72119:9;;;;;69078:24;69070:53;:::i;:::-;5079:72119;;;69156:10;69134:57;68945:10;69156;;5079:72119;;;69142:24;69134:57;:::i;:::-;5079:72119;69210:7;;69647:10;69210:7;;;;;;5079:72119;69210:18;;:40;;;5079:72119;69202:68;;;:::i;:::-;5079:72119;;;69289:13;69301:1;69289:13;;;:31;;;5079:72119;69281:69;;;:::i;:::-;5079:72119;;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;;;;69361:49;;5079:72119;;69361:49;:::i;:::-;5079:72119;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;;;;69421:49;;5079:72119;;69421:49;:::i;:::-;5079:72119;;;;69545:10;;69538:4;;69518:10;;-1:-1:-1;;;;;5079:72119:9;69545:10;:::i;:::-;5079:72119;;;;;;;-1:-1:-1;;;;;5079:72119:9;;;;;69647:10;:::i;:::-;5079:72119;;;;;;69704:10;;5079:72119;;-1:-1:-1;;;;;5079:72119:9;;;;;69704:10;:::i;:::-;5079:72119;;70048:7;;5079:72119;;-1:-1:-1;;;;;5079:72119:9;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;69986:136;;69538:4;69986:136;5079:72119;69986:136;;;:::i;:::-;;;;;;;;;;5079:72119;69986:136;;;5079:72119;70187:150;5079:72119;;;;;;;;;;;69210:7;70249;;;5079:72119;;;;;;;:::i;:::-;;;70311:14;;;:::i;:::-;5079:72119;;;;;;;;;;;;;70187:150;;69538:4;70187:150;5079:72119;70187:150;;;:::i;:::-;;;;;;;;;;5079:72119;70187:150;;;5079:72119;70393:11;;;;;;:::i;:::-;70392:29;;70384:54;;;:::i;:::-;70474:8;5079:72119;70474:8;;5079:72119;70474:19;;;;:::i;:::-;70311:3;5079:72119;;;70504:8;;5079:72119;70523:14;;;:::i;:::-;5079:72119;;70504:35;;;:::i;:::-;70311:3;5079:72119;;70473:73;;;:::i;:::-;5079:72119;;-1:-1:-1;;;;;5079:72119:9;;;70650:11;;;;;:::i;:::-;70609:80;;;;:::i;:::-;5079:72119;;;;;;;;;;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;;;;;;;;;70930:21;;5079:72119;70930:21;:::i;:::-;70311:3;5079:72119;;;;71006:14;;;:::i;:::-;5079:72119;;70985:37;;;:::i;:::-;70311:3;5079:72119;;71104:11;;;;;:::i;:::-;5079:72119;;;;;;;:::i;:::-;;;;70725:402;;5079:72119;;70725:402;;5079:72119;69017:9;70725:402;;5079:72119;69078:10;70725:402;;5079:72119;68945:10;70725:402;;5079:72119;69210:7;70725:402;;5079:72119;;70725:402;;5079:72119;70725:402;;;5079:72119;70725:402;;;5079:72119;69518:10;71172:8;;;:::i;:::-;69301:1;1759::4;;5079:72119:9;;;;;;;;70187:150;;;;5079:72119;70187:150;;5079:72119;70187:150;;;;;;5079:72119;70187:150;;;:::i;:::-;;;5079:72119;;;;;70187:150;;;;;;;-1:-1:-1;70187:150:9;;;5079:72119;;;;;;;;;69986:136;;;5079:72119;69986:136;;5079:72119;69986:136;;;;;;5079:72119;69986:136;;;:::i;:::-;;;5079:72119;;;;70187:150;5079:72119;;69986:136;;;;;;-1:-1:-1;69986:136:9;;69289:31;-1:-1:-1;69318:2:9;69306:14;;;69289:31;;69210:40;-1:-1:-1;69210:7:9;69232;;;5079:72119;69232:18;;69210:40;;68945:32;-1:-1:-1;68945:10:9;68963;;5079:72119;68963:14;;68945:32;;5079:72119;;;;;;-1:-1:-1;;5079:72119:9;;;;;;:::i;:::-;1500:62:0;;:::i;:::-;-1:-1:-1;;;;;5079:72119:9;2627:22:0;;2623:91;;5079:72119:9;;;-1:-1:-1;;;;;;5079:72119:9;;;;;;-1:-1:-1;;;;;5079:72119:9;;3052:40:0;;5079:72119:9;3052:40:0;5079:72119:9;2623:91:0;2672:31;;;5079:72119:9;2672:31:0;5079:72119:9;;;;;2672:31:0;5079:72119:9;;;;;;-1:-1:-1;;5079:72119:9;;;;;;:::i;:::-;1500:62:0;;:::i;:::-;-1:-1:-1;;;;;5079:72119:9;;;11542:17;;5079:72119;;11599:15;:19;5079:72119;;;;;11661:18;5079:72119;;;11661:18;5079:72119;;;;;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;;;-1:-1:-1;;5079:72119:9;;;;;;6291:25;5079:72119;;;;;;;;;;;;;;;;-1:-1:-1;;5079:72119:9;;;;;;:::i;:::-;1500:62:0;;:::i;:::-;15499:25:9;5079:72119;;-1:-1:-1;;;;;;5079:72119:9;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;15540:58;;5079:72119;;;;;15540:58;;;;5079:72119;;;;;;;-1:-1:-1;;5079:72119:9;;;;;;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;;;;;;;1500:62:0;;:::i;:::-;16978:7:9;5079:72119;;17019:5;;;;;;5079:72119;;16978:7;5079:72119;;16978:7;5079:72119;;;;17003:180;17494:9;5079:72119;17494:9;17505:5;;;;;;5079:72119;;;;;;;;;;-1:-1:-1;;;;;5079:72119:9;;;;;;17540:11;5079:72119;17540:11;;:::i;:::-;5079:72119;17494:9;;17505:5;12218:29;5079:72119;17505:5;5079:72119;;;;;12218:29;5079:72119;;16978:7;5079:72119;;;;;;;;;;;;;;;;;;;;;;17008:9;17055:10;;5079:72119;17055:10;;:::i;:::-;5079:72119;;;;;;;;16978:7;5079:72119;;;;;;;;;;;;;;;;;;;;;;;;;;;;1759:1:4;5079:72119:9;17008:9;;5079:72119;;;;;;-1:-1:-1;;5079:72119:9;;;;;;;;;;;;;;;;;;;;;;;;;;;6770:3;1500:62:0;;;:::i;:::-;15975:35:9;5079:72119;;6838:6;-1:-1:-1;5079:72119:9;;16134:28;5079:72119;;-1:-1:-1;;;;5079:72119:9;;;;;;-1:-1:-1;;;5079:72119:9;;;;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;5079:72119:9;;16221:58;;5079:72119;;;;;16221:58;5079:72119;;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;;-1:-1:-1;;;5079:72119:9;;;;6770:3;;5079:72119;;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;;-1:-1:-1;;;5079:72119:9;;;;6770:3;;5079:72119;;;;;;;-1:-1:-1;;5079:72119:9;;;;;;6393:29;5079:72119;;;;;;;;;;;;;;75467:36;76184:82;75398:36;5079:72119;;;:::i;:::-;2227:103:4;;;;;;;;;;;:::i;:::-;75170:39:9;-1:-1:-1;;;;;5079:72119:9;;75178:16;;75170:39;:::i;:::-;5079:72119;;;75228:13;75240:1;75228:13;;;:31;;;5079:72119;75220:69;;;:::i;:::-;75318:8;;;;:::i;:::-;75354;;;;:::i;75398:36::-;75467;;:::i;:::-;75522:10;;;;5079:72119;;;75522:14;;:32;;;5079:72119;75514:56;;;:::i;:::-;5079:72119;;75581:62;75522:10;75603;;5079:72119;;;75589:24;75581:62;:::i;:::-;75662:9;;;;5079:72119;;75675:9;;;5079:72119;75662:9;;5079:72119;75654:50;;-1:-1:-1;;;;;5079:72119:9;;;;;75662:22;75654:50;:::i;:::-;75723:7;;;;;;5079:72119;75723:18;;:40;;;5079:72119;75715:68;;;:::i;:::-;5079:72119;;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;;;;75794:49;;5079:72119;;75794:49;:::i;:::-;5079:72119;;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;;;;75854:49;;5079:72119;;75854:49;:::i;:::-;5079:72119;;;;75978:10;;75971:4;;75951:10;;-1:-1:-1;;;;;5079:72119:9;75978:10;:::i;:::-;5079:72119;;;;;;76035:10;;5079:72119;;-1:-1:-1;;;;;5079:72119:9;;;;;76035:10;:::i;:::-;5079:72119;;;;;;76092:10;;5079:72119;;-1:-1:-1;;;;;5079:72119:9;;;;;76092:10;:::i;:::-;5079:72119;76141:14;;;;:::i;:::-;5079:72119;;76231:7;;5079:72119;;;;-1:-1:-1;;;;;5079:72119:9;;;;;;:::i;76184:82::-;;;;;;;;;;5079:72119;76184:82;;;5079:72119;76292:82;5079:72119;;;;;75723:7;5079:72119;;;;;;;;76339:7;;;5079:72119;;;;;;:::i;:::-;;;;;;;;;;;;;;;76292:82;;75971:4;76292:82;5079:72119;76292:82;;;:::i;:::-;;;;;;;;;;5079:72119;76292:82;;;5079:72119;76407:11;;;;;:::i;:::-;76437:27;;;76429:52;76437:27;;;;76429:52;:::i;:::-;5079:72119;76517:8;5079:72119;76517:19;;;;:::i;:::-;76141:3;5079:72119;;;76547:8;;5079:72119;;;;76547:19;;;;;:::i;:::-;76141:3;5079:72119;;76516:57;;;:::i;:::-;75971:4;76616:13;75971:4;76616:13;;:::i;:::-;76653:66;;;;;:::i;:::-;5079:72119;;;;;;;;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;;;;;76954:21;;5079:72119;76954:21;:::i;:::-;76141:3;5079:72119;;;;77009:21;;;;:::i;:::-;76141:3;5079:72119;;;;;;;;;:::i;:::-;;;;76755:378;;5079:72119;;76755:378;;5079:72119;75662:9;76755:378;;-1:-1:-1;;;;;;;;;;;5079:72119:9;;76755:378;;;5079:72119;75522:10;76755:378;;5079:72119;75723:7;76755:378;;5079:72119;;76755:378;;5079:72119;76755:378;;;5079:72119;76755:378;;;5079:72119;75951:10;77178:8;;;:::i;76292:82::-;;;;5079:72119;76292:82;;5079:72119;76292:82;;;;;;5079:72119;76292:82;;;:::i;:::-;;;5079:72119;;;;;76292:82;;;;;;;-1:-1:-1;76292:82:9;;76184;;;5079:72119;76184:82;;5079:72119;76184:82;;;;;;5079:72119;76184:82;;;:::i;:::-;;;5079:72119;;;;76292:82;5079:72119;;76184:82;;;;;;-1:-1:-1;76184:82:9;;75723:40;-1:-1:-1;75723:7:9;75745;;;5079:72119;75745:18;;75723:40;;75522:32;-1:-1:-1;75522:10:9;75540;;5079:72119;75540:14;;75522:32;;75228:31;-1:-1:-1;75257:2:9;75245:14;;;75228:31;;5079:72119;;;;;;-1:-1:-1;;5079:72119:9;;;;;;:::i;:::-;;;:::i;:::-;;;;-1:-1:-1;;;;;5079:72119:9;;;;;;;;:::i;:::-;;;;:::i;:::-;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;5079:72119:9;;;;;;;;54066:119;;5079:72119;;;;54066:119;;5079:72119;;;:::i;:::-;54232:31;5079:72119;;:::i;:::-;54297:31;5079:72119;;:::i;:::-;54386:7;5079:72119;54367:13;;5079:72119;54382:18;;;;;;55524:21;;;5079:72119;55524:21;;5079:72119;;;55524:26;55520:53;;55647:12;;:20;5079:72119;55647:20;;55689:22;;5079:72119;;;;;55742:12;;;:20;;55722:40;;55784:22;5079:72119;55837:15;5079:72119;55837:15;;;:23;5079:72119;;55837:23;;55882:25;;5079:72119;55938:15;;;:23;5079:72119;55938:23;;55983:25;;5079:72119;;;;56099:21;;5079:72119;56099:25;;:93;;;54362:1146;56095:745;;;56275:19;56314;;56243:105;;;:::i;:::-;56209:139;;;5079:72119;;-1:-1:-1;56536:39:9;56532:136;;56095:745;56974:50;;;;;:::i;:::-;57079:51;;;;;;;;:::i;:::-;57251:39;;;;;;;:::i;:::-;;;57338:41;;;;:::i;:::-;;;;;5079:72119;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;56532:136;5079:72119;;-1:-1:-1;5079:72119:9;;-1:-1:-1;56532:136:9;;56095:745;56778:18;;;;5079:72119;56811:17;5079:72119;56095:745;;;56099:93;56138:19;;;5079:72119;;;;;56128:30;56172:19;;5079:72119;;;;;56162:30;56128:64;;56099:93;;54367:13;54448:35;54465:10;;;;;:::i;54448:35::-;5079:72119;54504:20;;5079:72119;54504:24;54500:473;;54367:13;54993:20;5079:72119;54993:20;;5079:72119;54993:24;54989:474;;54367:13;5079:72119;;;;54367:13;;54989:474;55108:18;55400:47;55108:18;;5079:72119;55108:18;5079:72119;55108:18;;;5079:72119;;;;;;;;;;55260:18;;5079:72119;;;;;;;;:::i;:::-;;;;55063:235;;5079:72119;;55063:235;;5079:72119;;55063:235;;5079:72119;55345:5;;;;:::i;55400:47::-;55371:76;;54989:474;;;;;54500:473;54619:18;;;54910:47;54619:18;;5079:72119;54619:18;;;5079:72119;;;;;;;;;;54771:18;;5079:72119;;;;;;;;:::i;:::-;;;;54574:235;;5079:72119;;54574:235;;5079:72119;;54574:235;;5079:72119;54856:4;;;;:::i;54910:47::-;54881:76;;54500:473;;;;5079:72119;;;;;;-1:-1:-1;;5079:72119:9;;;;;;;;13195:7;5079:72119;;;;;;;;13195:7;5079:72119;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;5079:72119:9;;;;;-1:-1:-1;5079:72119:9;;;;;;;;;;;;;;;;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;5079:72119:9;;;;;;;-1:-1:-1;;;;;;;;;;;5079:72119:9;;;;;;;;;-1:-1:-1;;5079:72119:9;;;;;;;6838:6;5079:72119;;;;;;;;;-1:-1:-1;;5079:72119:9;;;;;;:::i;:::-;1500:62:0;;:::i;:::-;-1:-1:-1;;;;;5079:72119:9;;;;;18735:18;5079:72119;;;;;;18776:15;;18772:40;;-1:-1:-1;;5079:72119:9;;;;;;;18882:7;5079:72119;-1:-1:-1;;5079:72119:9;;;;;;;18917:14;;;18913:161;;5079:72119;;;;18882:7;5079:72119;;;;;-1:-1:-1;;5079:72119:9;;;;:::i;:::-;;;-1:-1:-1;;;;;18882:7:9;5079:72119;;;;;;;;;;-1:-1:-1;5079:72119:9;;;;;;;;;;;;;;-1:-1:-1;;5079:72119:9;;;18735:18;5079:72119;;;;;1759:1:4;;;19191:21:9;;-1:-1:-1;19191:21:9;5079:72119;;;;;;;;;;;;;18913:161;18994:12;18963:16;;18994:19;18963:16;;:::i;:::-;5079:72119;;18882:7;5079:72119;;;;;-1:-1:-1;;;;;5079:72119:9;;;;18994:12;:::i;:::-;5079:72119;;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;;;;;;;;;;;;;;;;18994:19;5079:72119;;18735:18;5079:72119;;;;;1759:1:4;18913:161:9;;;;;5079:72119;;;;;;;;;;;;18772:40;18800:12;;;5079:72119;18800:12;5079:72119;;18800:12;5079:72119;;;;;;-1:-1:-1;;5079:72119:9;;;;5583:72;5079:72119;;;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;;;;;;-1:-1:-1;;5079:72119:9;;;;;;;;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;;;;;;-1:-1:-1;;5079:72119:9;;;;;;;6770:3;5079:72119;;;;;;;63445:20;5079:72119;;;:::i;:::-;2227:103:4;;;;;;:::i;:::-;-1:-1:-1;;;;;5079:72119:9;;;63224:39;63232:16;;;63224:39;:::i;:::-;63445:20;:::i;:::-;5079:72119;;;;;;;;;;;;;;;;;;;;;;63478:47;5079:72119;;;;;;63478:47;:::i;:::-;63544:12;;5079:72119;;;63761:8;5079:72119;;;63589:48;5079:72119;;;63597:20;;63589:48;:::i;:::-;-1:-1:-1;;;;;5079:72119:9;;;63710:8;63703:4;;63683:10;5079:72119;63710:8;:::i;:::-;63761;:::i;:::-;5079:72119;;;;;;:::i;:::-;;;;;;;;;;;;63803:79;;5079:72119;;63803:79;;5079:72119;63878:3;5079:72119;;;;:::i;:::-;63703:4;5079:72119;;;;63878:3;5079:72119;;;;63803:79;;5079:72119;63803:79;;;;;;;;5079:72119;63803:79;;;5079:72119;63901:25;;;;64031:74;63901:25;63893:50;63901:25;5079:72119;63901:25;;;63893:50;:::i;:::-;64031:74;;:::i;:::-;5079:72119;;;64123:84;5079:72119;;;;;;;;;;;;;;;;;;;;;;;;;64123:84;5079:72119;63683:10;64123:84;;1716:1:4;1759;;5079:72119:9;;;;;;63803:79;;;;5079:72119;63803:79;;5079:72119;63803:79;;;;;;5079:72119;63803:79;;;:::i;:::-;;;5079:72119;;;;;;;63803:79;5079:72119;63803:79;;;;;-1:-1:-1;63803:79:9;;5079:72119;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;;;-1:-1:-1;;5079:72119:9;;;;2292:1:4;5079:72119:9;;:::i;:::-;;;:::i;:::-;1500:62:0;;;:::i;:::-;2227:103:4;;:::i;:::-;2292:1;:::i;:::-;1716;1759;;5079:72119:9;;;;;;;-1:-1:-1;;5079:72119:9;;;;;;5744:24;5079:72119;5744:24;;;;;;5079:72119;5744:24;;:::i;:::-;5079:72119;;;;5744:24;5079:72119;;;;;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;;67087:20;5079:72119;;;:::i;:::-;2227:103:4;;;;;;;;:::i;:::-;-1:-1:-1;;;;;5079:72119:9;;;66847:39;66855:16;;;66847:39;:::i;:::-;66913:7;;;;:::i;67087:20::-;5079:72119;;;;;67397:8;5079:72119;;;;;;;;;;;;;;;;;;;;;67120:47;5079:72119;;;;;;67120:47;:::i;:::-;67178:36;67186:12;;;67178:36;:::i;:::-;67225:48;5079:72119;;67233:20;;67225:48;:::i;:::-;-1:-1:-1;;;;;5079:72119:9;;;67346:8;67339:4;;67319:10;5079:72119;67346:8;:::i;67397:::-;5079:72119;;;;;;:::i;:::-;;;;;;;;;;;;67437:77;;5079:72119;;67437:77;;5079:72119;67510:3;5079:72119;;;;:::i;:::-;67339:4;5079:72119;;;;67510:3;5079:72119;;;;67437:77;;5079:72119;67437:77;;;;;;;;5079:72119;67437:77;;;5079:72119;67533:23;;;;67697:60;67533:23;67525:48;67533:23;5079:72119;67533:23;;;67525:48;:::i;:::-;67658:13;67339:4;67658:13;;:::i;:::-;67697:60;:::i;:::-;5079:72119;;;67775:83;5079:72119;;-1:-1:-1;;;;;;;;;;;5079:72119:9;;;;;;;;;;;;;;;;;67775:83;5079:72119;67319:10;67775:83;;1716:1:4;1759;;5079:72119:9;;;;;;67437:77;;;;5079:72119;67437:77;;5079:72119;67437:77;;;;;;5079:72119;67437:77;;;:::i;:::-;;;5079:72119;;;;;;;67437:77;5079:72119;67437:77;;;;;-1:-1:-1;67437:77:9;;5079:72119;;;;;;-1:-1:-1;;5079:72119:9;;;;1500:62:0;;:::i;:::-;5079:72119:9;;;-1:-1:-1;;;;;;5079:72119:9;;;;-1:-1:-1;;;;;5079:72119:9;3052:40:0;5079:72119:9;;3052:40:0;5079:72119:9;;;65304:20;5079:72119;;;;:::i;:::-;2227:103:4;;;;;;;;:::i;:::-;-1:-1:-1;;;;;5079:72119:9;;;65017:39;65025:16;;;65017:39;:::i;:::-;65067:36;65075:9;:13;;65067:36;:::i;:::-;65129:7;;;;:::i;65304:20::-;5079:72119;;;;;;;;;;65617:9;5079:72119;;;;;;;;;;;;;;65337:47;5079:72119;;;;;;65337:47;:::i;:::-;65395:48;5079:72119;;65403:20;;65395:48;:::i;:::-;65454:55;65075:9;65462:21;;65454:55;:::i;:::-;65531:9;65075;65531;:::i;:::-;65075;65617;;:::i;5079:72119::-;65809:4;5079:72119;;;;65816:3;5079:72119;;;;65743:77;;5079:72119;65743:77;;;;;;;;5079:72119;65743:77;;;5079:72119;65839:25;;;;65903:74;65839:25;65831:50;65839:25;5079:72119;65839:25;;;65831:50;:::i;:::-;65903:74;;:::i;:::-;5079:72119;;;65995:81;-1:-1:-1;;;;;;;;;;;5079:72119:9;;;;;;;;;;;;;;;;;;;;;;;;;65995:81;5079:72119;66016:10;65995:81;;1716:1:4;1759;;5079:72119:9;;;;;;65743:77;;;;5079:72119;65743:77;;5079:72119;65743:77;;;;;;5079:72119;65743:77;;;:::i;:::-;;;5079:72119;;;;;;;65743:77;5079:72119;65743:77;;;;;-1:-1:-1;65743:77:9;;5079:72119;;;;;;-1:-1:-1;;5079:72119:9;;;;6197:27;5079:72119;;;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;;;;;;-1:-1:-1;;5079:72119:9;;;;-1:-1:-1;;;;;5079:72119:9;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;5079:72119:9;;;;;;:::i;:::-;;;;;;;;;;;;40445:35;;;;:::i;:::-;5079:72119;;;;;;;;;;;72518:36;73224:82;72449:36;5079:72119;;;:::i;:::-;2227:103:4;;;;;;;;;;;:::i;:::-;72176:39:9;-1:-1:-1;;;;;5079:72119:9;;72184:16;;72176:39;:::i;:::-;72226:36;72234:9;:13;;72226:36;:::i;:::-;5079:72119;;;72281:13;72293:1;72281:13;;;:31;;;5079:72119;72273:69;;;:::i;:::-;72370:8;;;;:::i;:::-;72405;;;;:::i;72518:36::-;72573:10;72638:57;72573:10;;;72565:62;5079:72119;;72573:10;72587;;5079:72119;72573:24;72565:62;:::i;:::-;5079:72119;72234:9;72646:23;72638:57;:::i;:::-;72714:10;;;;5079:72119;;72728:10;;;5079:72119;72714:10;;72706:53;;-1:-1:-1;;;;;5079:72119:9;;;;;72714:24;72706:53;:::i;:::-;72778:7;;;;;5079:72119;72778:18;;:40;;;5079:72119;72770:68;;;:::i;:::-;5079:72119;;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;;;;72849:49;;5079:72119;;72849:49;:::i;:::-;5079:72119;;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;;;;72909:49;;5079:72119;;72909:49;:::i;:::-;72980:9;72234;72980;:::i;:::-;5079:72119;;73031:9;;72234;;-1:-1:-1;;;;;5079:72119:9;73031:9;:::i;:::-;5079:72119;;73082:9;;72234;;-1:-1:-1;;;;;5079:72119:9;73082:9;:::i;:::-;5079:72119;73130:14;;;;:::i;:::-;5079:72119;;73271:7;;5079:72119;;;;-1:-1:-1;;;;;5079:72119:9;;;;;;:::i;:::-;;;;;;;;;;;;;;;73224:82;;73290:4;73224:82;5079:72119;73224:82;;;:::i;:::-;;;;;;;;;;5079:72119;73224:82;;;5079:72119;73332:82;5079:72119;;;;;;;;;;;;72778:7;73379;;;5079:72119;;;;;;:::i;:::-;;;;;;;;;;;;;;;73332:82;;73290:4;73332:82;5079:72119;73332:82;;;:::i;:::-;;;;;;;;;;5079:72119;73332:82;;;5079:72119;73446:11;;;;;;:::i;:::-;73476:23;73468:48;73476:23;;;;73468:48;:::i;:::-;73552:8;5079:72119;73552:8;;5079:72119;73552:19;;;;:::i;:::-;73130:3;5079:72119;;;73582:8;;5079:72119;;;;73582:19;;;;;:::i;:::-;73130:3;5079:72119;;73551:57;;;:::i;:::-;5079:72119;;73630:76;;5079:72119;;-1:-1:-1;;;;;5079:72119:9;73630:76;:::i;:::-;5079:72119;;;;;;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;;73941:29;;72234:9;73941:29;:::i;:::-;73130:3;5079:72119;;;74004:29;;72234:9;74004:29;:::i;:::-;73130:3;5079:72119;;;;;;;;;:::i;:::-;;;;73742:393;;5079:72119;;73742:393;;-1:-1:-1;;;;;;;;;;;5079:72119:9;;73742:393;;;5079:72119;72234:9;72714:10;73742:393;;5079:72119;72573:10;73742:393;;5079:72119;72778:7;73742:393;;5079:72119;;73742:393;;5079:72119;73742:393;;;5079:72119;73742:393;;;5079:72119;74164:10;74180:8;;;:::i;73332:82::-;;;;5079:72119;73332:82;;5079:72119;73332:82;;;;;;5079:72119;73332:82;;;:::i;:::-;;;5079:72119;;;;;73332:82;;;;;;;-1:-1:-1;73332:82:9;;73224;;;5079:72119;73224:82;;5079:72119;73224:82;;;;;;5079:72119;73224:82;;;:::i;:::-;;;5079:72119;;;;73332:82;5079:72119;;73224:82;;;;;;-1:-1:-1;73224:82:9;;72778:40;-1:-1:-1;72778:7:9;72800;;;5079:72119;72800:18;;72778:40;;72281:31;-1:-1:-1;72310:2:9;72298:14;;;72281:31;;5079:72119;;;;;;-1:-1:-1;;5079:72119:9;;;;;;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;;;;;:::i;:::-;;;:::i;:::-;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;-1:-1:-1;;5079:72119:9;;;;;;;:::i;:::-;;45985:39;5079:72119;;:::i;:::-;;;;:::i;:::-;;;;:::i;:::-;;;;:::i;:::-;;;45812:51;5079:72119;;:::i;:::-;46072:41;45707:50;;;;;:::i;:::-;45812:51;;;;;;;:::i;:::-;45985:39;;;;;;:::i;:::-;46072:41;;;;:::i;:::-;5079:72119;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;5079:72119:9;;;;;;41486:48;5079:72119;;:::i;:::-;;;:::i;:::-;;;:::i;:::-;41486:48;;:::i;:::-;5079:72119;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;5079:72119:9;;;;;12895:7;5079:72119;;;;;;;;;;;;;-1:-1:-1;;5079:72119:9;;;;12451:6;5079:72119;;:::i;:::-;1500:62:0;;:::i;:::-;12451:6:9;:::i;5079:72119::-;;;;;;-1:-1:-1;;5079:72119:9;;;;;;:::i;:::-;1500:62:0;;:::i;:::-;2227:103:4;;:::i;:::-;-1:-1:-1;;;;;5079:72119:9;;;;-1:-1:-1;5079:72119:9;;-1:-1:-1;;;;;5079:72119:9;14835:33;14893:21;14938:30;5079:72119;14938:30;;;;;;;;;:::i;:::-;;5079:72119;;;;;;;;-1:-1:-1;;;;;5079:72119:9;;15026:24;;5079:72119;;15026:24;1716:1:4;1759;;5079:72119:9;;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;;-1:-1:-1;;;5079:72119:9;;;;;;;14835:33;;;5079:72119;;;;;;-1:-1:-1;;5079:72119:9;;;;;;;:::i;:::-;;43236:35;5079:72119;;:::i;:::-;;43070:48;5079:72119;;:::i;:::-;;;:::i;:::-;43070:48;;;:::i;:::-;43236:35;;;;;;:::i;:::-;5079:72119;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;5079:72119:9;;;;;;:::o;:::-;;;;-1:-1:-1;;;;;5079:72119:9;;;;;;:::o;:::-;;;;-1:-1:-1;;;;;5079:72119:9;;;;;;:::o;:::-;;;;-1:-1:-1;;;;;5079:72119:9;;;;;;:::o;:::-;;;-1:-1:-1;;;;;5079:72119:9;;;;;;:::o;:::-;;;;;;;;;;;:::o;:::-;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;-1:-1:-1;5079:72119:9;;;;;;;;-1:-1:-1;;5079:72119:9;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;-1:-1:-1;;5079:72119:9;;;;;;;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;;:::i;:::-;;;;;;;;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;5079:72119:9;;;;;;;:::o;:::-;;-1:-1:-1;;5079:72119:9;;;;;;;;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;:::i;:::-;;;;;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;;:::o;:::-;5744:24;5079:72119;;;;;;5744:24;-1:-1:-1;5079:72119:9;;-1:-1:-1;5079:72119:9;;;-1:-1:-1;5079:72119:9;:::o;:::-;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;5079:72119:9;;;;;;;:::o;:::-;;;;-1:-1:-1;5079:72119:9;;;;;-1:-1:-1;5079:72119:9;;;;;;;;-1:-1:-1;;;;;5079:72119:9;;;;;;;:::o;:::-;;;;;;;-1:-1:-1;;;;;5079:72119:9;;;;;;;:::o;:::-;;;;;;;-1:-1:-1;;;;;5079:72119:9;;;;;;;:::o;:::-;;;;;;;;;;;;;-1:-1:-1;;;;;5079:72119:9;;;;;;;:::o;:::-;-1:-1:-1;;;;;5079:72119:9;;;;;;-1:-1:-1;;5079:72119:9;;;;:::o;:::-;;;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;-1:-1:-1;5079:72119:9;;;;:::o;:::-;;;:::o;:::-;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;:::o;:::-;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;;;;;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;;;-1:-1:-1;5079:72119:9;;-1:-1:-1;5079:72119:9;;;;;;;;;;;;;;;;;;;;;;;;61689:526;62123:84;61689:526;62123:84;;;;:::i;:::-;-1:-1:-1;;;;;5079:72119:9;;;;62035:172;;;;5079:72119;;;;;;;;62035:172;;;61689:526::o;5079:72119::-;;;;:::o;:::-;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;:::o;:::-;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;:::o;:::-;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;:::o;:::-;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;:::o;:::-;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;:::o;:::-;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;;;:::o;:::-;;;;;;;;;;:::o;:::-;;;;:::o;:::-;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;:::o;:::-;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;;-1:-1:-1;;;5079:72119:9;;;;;;;13833:460;-1:-1:-1;;;;;5079:72119:9;;13965:19;;5079:72119;;-1:-1:-1;;;;;5079:72119:9;;;;-1:-1:-1;13982:1:9;5079:72119;-1:-1:-1;;;;;5079:72119:9;;14031:33;5079:72119;;-1:-1:-1;;;14091:38:9;;14123:4;14091:38;;;5079:72119;;;;14091:38;5079:72119;14091:38;;;;;;;;13982:1;14091:38;;;14031:33;14144:8;;;14140:21;;5079:72119;14228:3;;;14248:37;14228:3;;;:::i;:::-;5079:72119;;;;;-1:-1:-1;;;;;5079:72119:9;;14248:37;13833:460::o;14140:21::-;14154:7;;;:::o;14091:38::-;;;;5079:72119;14091:38;;5079:72119;14091:38;;;;;;5079:72119;14091:38;;;:::i;:::-;;;5079:72119;;;;;14091:38;;;;;;;-1:-1:-1;14091:38:9;;14031:33;;;;5079:72119;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;5079:72119:9;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;:::i;:::-;;;;:::i;:::-;;;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;:::o;:::-;;;;:::o;:::-;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;:::o;:::-;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;:::o;:::-;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;;-1:-1:-1;;;5079:72119:9;;;;;;;34525:1529;;;;-1:-1:-1;;;;;5079:72119:9;;34720:19;34716:128;;34737:1;5079:72119;;;34949:57;;;5079:72119;;;;34949:57;;5079:72119;;;;;;;34949:57;;;5079:72119;34949:57;;;;;;;:::i;:::-;34932:75;;;;;;;:::i;:::-;35022:25;;;;34525:1529;35018:132;;;5079:72119;;;35070:28;;;;;;;;;;5079:72119;;;;34737:1;5079:72119;35264:66;;5079:72119;;;35018:132;;5079:72119;;-1:-1:-1;;;34949:57:9;35264:66;;;;;-1:-1:-1;;;;;5079:72119:9;;;34949:57;35264:66;;5079:72119;;;;;;;;;;;;;;;;;35264:66;;5079:72119;;35264:66;;;;;;:::i;:::-;35247:84;;;;;;;:::i;:::-;35346:25;;;;35018:132;35342:138;;;5079:72119;;;35397:28;;;;;;;;;;5079:72119;;;;34737:1;5079:72119;;;35342:138;;5079:72119;;-1:-1:-1;;;34949:57:9;35600:42;;;;;;;;;;34949:57;5079:72119;35600:42;:::i;:::-;35583:60;;;;;;:::i;:::-;35658:25;;;;35342:138;35654:393;;;5079:72119;;;35820:28;;;;;;;;;;5079:72119;;;;;35874:3;35870:7;;35874:3;;;35870:31;35874:3;35870:31;35654:393;34525:1529::o;35870:31::-;35874:3;5079:72119;35654:393;34525:1529::o;35654:393::-;36027:8;34737:1;35654:393;34525:1529::o;35658:25::-;5079:72119;34949:57;5079:72119;;35665:18;;35658:25;;35342:138;35458:10;34737:1;35458:10;;35342:138;;;35346:25;5079:72119;34949:57;5079:72119;;35353:18;;35346:25;;35018:132;35131:7;34737:1;35131:7;;35264:66;;35131:7;35018:132;;;35022:25;5079:72119;34949:57;5079:72119;;35029:18;;35022:25;;34716:128;34810:14;;-1:-1:-1;34737:1:9;;-1:-1:-1;34829:2:9;;34802:30::o;38348:1160::-;;38549:28;;;;38348:1160;38545:186;;38348:1160;38797:32;;;:::i;:::-;38844:14;;38840:49;;-1:-1:-1;;;;;;;5079:72119:9;;-1:-1:-1;;;;;;;;;;;38935:13:9;38931:151;;39195:32;;;:::i;:::-;39242:9;;;;39238:31;;39313:36;;:::i;:::-;39364:11;;;;39360:33;;39423:25;39470:13;39423:25;39487:4;39423:25;:11;;;:25;:11;;;:25;;39470:13;:::i;:::-;5079:72119;39461:39;38348:1160;:::o;39423:25::-;;;;39470:13;:::i;39360:33::-;39377:16;;;;38857:1;39377:16;;:::o;39238:31::-;38857:1;;-1:-1:-1;39253:16:9;:::o;38931:151::-;38990:36;;;:::i;:::-;39041:29;;:::o;38840:49::-;38860:29;;;-1:-1:-1;38860:29:9:o;38545:186::-;38619:36;;:::i;:::-;38674:14;;38670:49;;38545:186;;;;38549:28;-1:-1:-1;;;;;;5079:72119:9;;38558:19;38549:28;;1796:162:0;1710:6;5079:72119:9;-1:-1:-1;;;;;5079:72119:9;735:10:7;1855:23:0;1851:101;;1796:162::o;1851:101::-;1901:40;;;1710:6;1901:40;735:10:7;1901:40:0;5079:72119:9;;1710:6:0;1901:40;2336:287:4;1759:1;2468:7;5079:72119:9;2468:19:4;1759:1;;;2468:7;1759:1;2336:287::o;1759:1::-;5079:72119:9;;-1:-1:-1;;;1759:1:4;;;;;;;;;;;5079:72119:9;1759:1:4;5079:72119:9;;;1759:1:4;;;;17917:482:9;-1:-1:-1;;;;;5079:72119:9;;;17977:20;;17973:46;;5079:72119;17995:1;5079:72119;18034:8;5079:72119;;;;17995:1;5079:72119;;;18030:46;;18179:18;;:23;18175:49;;5079:72119;17995:1;5079:72119;18034:8;5079:72119;;;17995:1;5079:72119;18256:4;5079:72119;;;;;;;;18271:7;5079:72119;;;;;;;;;;18256:4;5079:72119;;;18271:7;5079:72119;;:::i;:::-;18271:7;5079:72119;;17995:1;5079:72119;18302:18;5079:72119;;;17995:1;5079:72119;1759:1:4;18372:19:9;17995:1;18372:19;;17917:482::o;18175:49::-;18006:13;;;17995:1;18211:13;18034:8;17995:1;18211:13;18030:46;18059:17;;;17995:1;18059:17;18034:8;17995:1;18059:17;20710:200;-1:-1:-1;;;;;;;;;;;20710:200:9;;21973:113;;;;;20855:15;5079:72119;;20710:200::o;5079:72119::-;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;;-1:-1:-1;5079:72119:9;;-1:-1:-1;5079:72119:9;;;;-1:-1:-1;5079:72119:9;;;;-1:-1:-1;5079:72119:9;;;;-1:-1:-1;5079:72119:9;;;;-1:-1:-1;5079:72119:9;;;;;;:::o;30066:328::-;30302:84;30066:328;;;;5079:72119;;:::i;:::-;30302:84;;;;;:::i;:::-;30278:7;;;30213:173;30266:10;;;5079:72119;-1:-1:-1;;;;;5079:72119:9;;;30254:10;;;5079:72119;;;;30243:9;;;5079:72119;-1:-1:-1;30233:8:9;;5079:72119;;;;-1:-1:-1;30224:7:9;;5079:72119;;;;;;;30066:328::o;19572:98::-;-1:-1:-1;;;;;;;;;;;19626:36:9;;;;;;5079:72119;;;;;;;;;;19626:36;;-1:-1:-1;;;;;;;;;;;19626:36:9;;;;;;;;19572:98;:::o;19626:36::-;;;;;:::i;:::-;19572:98::o;27819:335::-;5079:72119;;-1:-1:-1;;;27924:47:9;;27956:4;27924:47;;;5079:72119;-1:-1:-1;;;;;5079:72119:9;;;;;;;27924:47;5079:72119;;;-1:-1:-1;;;;;;;;;;;27924:47:9;;;;;;;-1:-1:-1;27924:47:9;;;27819:335;27986:12;;;27982:165;;27819:335;;:::o;27982:165::-;28117:17;28019:7;28015:51;;27982:165;-1:-1:-1;;;;;;;;;;;28117:17:9;:::i;28015:51::-;28028:38;;-1:-1:-1;;;;;;;;;;;28028:38:9;:::i;:::-;28015:51;;27924:47;;;;;;;;;;;;;;5079:72119;27924:47;;;:::i;:::-;;;5079:72119;;;;;27924:47;;;;;;;-1:-1:-1;27924:47:9;;27819:335;5079:72119;;-1:-1:-1;;;27924:47:9;;27956:4;27924:47;;;5079:72119;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;;;;27924:47;5079:72119;;;;27924:47;;;;;;;-1:-1:-1;27924:47:9;;;27819:335;27986:12;;;27982:165;;27819:335;;;:::o;27982:165::-;28117:17;28019:7;28015:51;28117:17;28015:51;28028:38;;;;:::i;:::-;28117:17;:::i;27924:47::-;;;;;;;;;;;;;;5079:72119;27924:47;;;:::i;:::-;;;5079:72119;;;;;27924:47;;;;;;;-1:-1:-1;27924:47:9;;5079:72119;;;;;;;;;;:::o;30877:1283::-;;;;31166:13;;31162:27;;32145:6;31221:39;;;:18;;;:39;:18;;;:39;;;31398:12;5079:72119;-1:-1:-1;;;;;5079:72119:9;;;31762:16;;5079:72119;;;;;31394:337;;31221:39;31762:16;;;:::i;:::-;31845:19;;;;31841:44;;31221:39;31907:19;;;;:::i;:::-;32013:12;;;;32009:95;;31221:39;-1:-1:-1;;;;;;;5079:72119:9;32145:6;:::i;32009:95::-;32083:8;;-1:-1:-1;;;;;5079:72119:9;;32083:8;:::i;:::-;32009:95;;;;31841:44;31866:19;-1:-1:-1;31866:19:9;31841:44;;31394:337;5079:72119;;;;;31445:14;31441:111;;31394:337;-1:-1:-1;5079:72119:9;;;;;31570:18;;;;:41;;31394:337;31566:154;31394:337;31566:154;31696:6;5079:72119;;;;;;;31566:154;;;31394:337;;31570:41;31592:19;;;;31570:41;;31441:111;31528:6;5079:72119;;;;;-1:-1:-1;5079:72119:9;31441:111;;31221:39;;;;;;31162:27;31181:8;;;;;;5079:72119;31181:8;:::o;28617:390::-;5079:72119;;28862:9;;;;5079:72119;28886:9;;;;5079:72119;28910:10;;;;5079:72119;28935:9;;;;5079:72119;28959:10;;;;5079:72119;;;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;28770:229;;5079:72119;;28770:229;28617:390::o;21211:204::-;-1:-1:-1;;;;;;;;;;;21211:204:9;;21973:113;;;;;21358:16;5079:72119;;21211:204::o;5079:72119::-;;;-1:-1:-1;;;5079:72119:9;;;21337:1;5079:72119;;;;;;;;;;;;;;;;1618:188:6;5079:72119:9;;-1:-1:-1;;;1745:53:6;;;;-1:-1:-1;;;;;5079:72119:9;;;1745:53:6;;;5079:72119:9;;;;;;;;;;;;;;;;;1745:53:6;;;;;;5079:72119:9;;1745:53:6;:::i;:::-;;:::i;19870:340:9:-;5079:72119;;-1:-1:-1;;;19954:36:9;;19984:4;19954:36;;;5079:72119;19870:340;;-1:-1:-1;;5079:72119:9;;19954:36;5079:72119;-1:-1:-1;;;;;;;;;;;19954:36:9;;;;;;;;-1:-1:-1;19954:36:9;;;19870:340;19954:46;;5079:72119;;-1:-1:-1;;;;;;;;;;;20037:28:9;;;;5079:72119;;-1:-1:-1;;;20037:28:9;;19954:36;20037:28;;5079:72119;;;-1:-1:-1;5079:72119:9;19954:36;5079:72119;-1:-1:-1;;;;;;;;;;;;20037:28:9;;;;;;;;19870:340;20122:26;;;;;;;;;;:::i;:::-;;5079:72119;;;19870:340::o;5079:72119::-;;;-1:-1:-1;;;5079:72119:9;;;19954:36;5079:72119;;;;19954:36;5079:72119;;;;;;;;;;;20037:28;;;;;-1:-1:-1;20037:28:9;;:::i;:::-;-1:-1:-1;20037:28:9;;;;5079:72119;;;-1:-1:-1;;;5079:72119:9;;;19954:36;5079:72119;;;;19954:36;5079:72119;;;-1:-1:-1;;;5079:72119:9;;;;;;;19954:36;;;;5079:72119;19954:36;;5079:72119;19954:36;;;;;;5079:72119;19954:36;;;:::i;:::-;;;5079:72119;;;;;;;19954:36;;;;;;-1:-1:-1;19954:36:9;;32552:1178;;;;;32811:13;;32807:27;;32866:24;;;;;;:51;;;32996:12;5079:72119;-1:-1:-1;;;;;5079:72119:9;;;33360:16;;5079:72119;;;;;32992:337;;33360:16;;;:::i;:::-;33391:19;;;;33387:44;;32866:51;33453:19;;;;:::i;:::-;33489:12;;;;33485:146;;32866:51;33655:26;;5079:72119;33655:26;;;;;;;;;:::i;:::-;;5079:72119;;;32552:1178::o;5079:72119::-;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;;-1:-1:-1;;;5079:72119:9;;;;;;;33485:146;5079:72119;33532:38;;;;;;;;;;;;:::i;:::-;;5079:72119;;;33485:146;;;;;;5079:72119;;;-1:-1:-1;;;5079:72119:9;;;;;;;;;;;;-1:-1:-1;;;5079:72119:9;;;;;;;33387:44;33412:19;-1:-1:-1;33412:19:9;33387:44;;32992:337;5079:72119;;;;;33043:14;33039:111;;32992:337;-1:-1:-1;5079:72119:9;;;;;33168:18;;;;:41;;32992:337;33164:154;32992:337;33164:154;33294:6;5079:72119;;;;;;;33164:154;;;32992:337;;33168:41;33190:19;;;;33168:41;;33039:111;33126:6;5079:72119;;;;;-1:-1:-1;5079:72119:9;33039:111;;32866:51;;;;;;32807:27;-1:-1:-1;5079:72119:9;;-1:-1:-1;;;32826:8:9:o;1219:160:6:-;5079:72119:9;;-1:-1:-1;;;1328:43:6;;;;-1:-1:-1;;;;;5079:72119:9;;;1328:43:6;;;5079:72119:9;;;;;;;;;1328:43:6;;;;;;5079:72119:9;;1328:43:6;:::i;5079:72119:9:-;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;;;-1:-1:-1;5079:72119:9;;-1:-1:-1;5079:72119:9;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;-1:-1:-1;;;;;5079:72119:9;;;;;;;;:::i;:::-;;;:::o;25231:1332::-;;;5079:72119;;;;;;;;;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;;;;-1:-1:-1;5079:72119:9;;;;;;;;;-1:-1:-1;5079:72119:9;;;;25358:26;5079:72119;;;;;;;;;;;;;;;-1:-1:-1;5079:72119:9;25475:8;5079:72119;;;;-1:-1:-1;5079:72119:9;;;25474:12;25470:31;;-1:-1:-1;5079:72119:9;;;25640:10;;;5079:72119;;;25665:10;;;5079:72119;;;;-1:-1:-1;;;25532:154:9;;;;;;-1:-1:-1;;;;;5079:72119:9;;;25532:154;;;5079:72119;;;;;;;;;;;;;25640:10;;5079:72119;25665:10;-1:-1:-1;;;;25532:154:9;;5079:72119;;;;25532:154;5079:72119;25532:154;25734:16;;;;;;;:::i;:::-;25765:8;;5079:72119;;25765:27;;25231:1332;25761:46;;5079:72119;;25936:55;;;5079:72119;25936:55;5079:72119;25936:55;;5079:72119;;;;;;25936:55;;5079:72119;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;-1:-1:-1;;;;;5079:72119:9;;;;;;;;;;;;;;;;:::i;:::-;;;;26072:8;;;;:34;;;25231:1332;26068:234;;25231:1332;26326:8;;;;;;;;:34;;;25231:1332;26322:234;;25231:1332;;;;;;;;:::o;26322:234::-;26440:104;26377:20;26440:104;26377:20;;5079:72119;26377:20;;5079:72119;;;;;;;;;;;;;;;;;;;;26518:11;;5079:72119;;;26440:104;;;5079:72119;26440:104;;;:::i;:::-;5079:72119;26419:18;;:125;26322:234;;;;;;;;;26326:34;26338:11;;;5079:72119;26338:22;;26326:34;;26068:234;26186:104;26123:20;;5079:72119;26186:104;26123:20;;5079:72119;;;;;;;;;;;;;;;;;;;;26264:11;;5079:72119;;;26186:104;;;5079:72119;26186:104;;;:::i;:::-;5079:72119;26165:18;;:125;26068:234;;;;;;;26072:34;26084:11;;;5079:72119;26084:22;;26072:34;;25761:46;25794:13;;;;;;:::o;25765:27::-;5079:72119;;;25777:15;25765:27;;25470:31;25488:13;-1:-1:-1;;;;25488:13:9:o;22639:845::-;22795:18;;;5079:72119;22795:18;22816:19;;:29;5079:72119;22795:50;;22791:317;22795:18;;;22891:19;;;22795:18;22866:22;;:44;22929:30;22639:845::o;22791:317::-;23006:22;22795:18;23006:22;;22795:18;23006:22;;:32;5079:72119;-1:-1:-1;22981:127:9;;22791:317;;22639:845::o;22981:127::-;23059:33;22639:845::o;:::-;23153:18;;;5079:72119;23174:19;;;;;23153:18;23174:19;;:29;5079:72119;23153:50;;23149:317;23153:18;;;23249:19;23224:22;23249:19;;23224:22;;:44;23287:30;22639:845::o;23149:317::-;23364:22;;;;;23153:18;23364:22;;:32;5079:72119;-1:-1:-1;23339:127:9;;23149:317;;22639:845::o;24052:453::-;;;;;5079:72119;;:::i;:::-;;;;:::i;:::-;;24274:18;;;5079:72119;24274:18;24295:14;;5079:72119;24274:35;;24270:198;24274:18;;;24326:11;;24052:453;:::o;24270:198::-;24274:18;24410:14;;;;5079:72119;-1:-1:-1;24385:83:9;;24270:198;24478:19;24052:453;:::o;24385:83::-;24441:15;-1:-1:-1;24478:19:9;24052:453;:::o;5079:72119::-;;;-1:-1:-1;;5079:72119:9;;;;;;;:::o;57766:3470::-;58096:28;58157;57766:3470;;;;58096:28;:::i;:::-;58157;;:::i;:::-;58395:57;58206:10;;;5079:72119;;58206:14;;:32;;;57766:3470;58198:61;;;:::i;:::-;58278:9;;;;5079:72119;58291:9;;;5079:72119;58270:50;;-1:-1:-1;;;;;5079:72119:9;;;;;58278:22;58270:50;:::i;:::-;58339:10;;;;5079:72119;58353:10;;;5079:72119;58331:53;;-1:-1:-1;;;;;5079:72119:9;;;;;58339:24;58331:53;:::i;:::-;5079:72119;58206:10;58417;;5079:72119;58403:24;58395:57;:::i;:::-;5079:72119;;;;58719:7;;;;;;58737;;;;;;5079:72119;;-1:-1:-1;;;;;5079:72119:9;;;;58737:7;;58719;5079:72119;;;;58623:2;;5079:72119;;58737:7;5079:72119;;;;;:::i;:::-;;;;;;;;;;;;27095:71;;5079:72119;27095:71;;;5079:72119;;;;;;:::i;:::-;58623:2;5079:72119;;;;27095:71;;;;;;;;;;-1:-1:-1;27095:71:9;;;57766:3470;5079:72119;27252:71;5079:72119;;;;;;;;;:::i;:::-;;;;;-1:-1:-1;;;27252:71:9;;5079:72119;;;;;27095:71;27252;;;:::i;:::-;;;;;;;;;;;-1:-1:-1;27252:71:9;;;57766:3470;27351:11;;;;:::i;:::-;58782:42;58623:2;59009;59028:13;5079:72119;;;59024:2;59013:13;;;;58623:2;59062:13;59058:27;;59122:95;59168:7;;;59186;;;59122:95;;;:::i;:::-;59238:24;;;59234:136;;59028:13;;5079:72119;59009:2;59028:13;58992:19;5079:72119;;;;;;58992:19;5079:72119;;;;-1:-1:-1;5079:72119:9;;27095:71;5079:72119;;-1:-1:-1;5079:72119:9;59234:136;59283:24;-1:-1:-1;59283:24:9;-1:-1:-1;59283:24:9;5079:72119;59234:136;;59058:27;5079:72119;59009:2;59077:8;;;59013:13;-1:-1:-1;;5079:72119:9;;59013:13;;;;;;5079:72119;;;;:::i;:::-;59632:1;5079:72119;;;;;;;;;;;;;;-1:-1:-1;;5079:72119:9;;;;;;;;;;;;;;-1:-1:-1;59776:13:9;5079:72119;;59791:18;;;;;5079:72119;;;;;;;;;;;;;;;;;;-1:-1:-1;60091:10:9;;;;:49;;;59776:13;60090:129;;;;59776:13;60068:248;;60378:189;;;60454:7;;;;:::i;:::-;5079:72119;;;;;;;;;;;60378:189;5079:72119;;;;60682:17;;;:39;;;;60378:189;60678:381;;;60378:189;5079:72119;;;59776:13;;;;;60678:381;60762:108;60812:7;;;;;60830;;60762:108;;:::i;:::-;60895:24;;;60891:153;;60678:381;;;;60891:153;60944:24;-1:-1:-1;60944:24:9;-1:-1:-1;5079:72119:9;60891:153;;;60682:39;60719:2;60703:18;;;;60682:39;;;60378:189;60518:33;5079:72119;;;;;;;;;;60378:189;5079:72119;;;;-1:-1:-1;5079:72119:9;;27095:71;5079:72119;;-1:-1:-1;5079:72119:9;60068:248;5079:72119;;;;;60292:8;;;60090:129;60164:10;-1:-1:-1;60164:10:9;;:54;;60090:129;60164:54;5079:72119;;60198:20;5079:72119;;;60198:20;:::i;:::-;5079:72119;;;;60178:40;;60090:129;;60091:49;60132:7;5079:72119;60132:7;;;:::i;:::-;5079:72119;;;;60105:35;;60091:49;;59791:18;;;;;;;;;;61195:33;57766:3470;:::o;27252:71::-;;;5079:72119;27252:71;;5079:72119;27252:71;;;;;;5079:72119;27252:71;;;:::i;:::-;;;5079:72119;;;;27351:11;5079:72119;;27252:71;;;;;;-1:-1:-1;27252:71:9;;27095;;;;5079:72119;27095:71;;5079:72119;27095:71;;;;;;5079:72119;27095:71;;;:::i;:::-;;;5079:72119;;;;;;27252:71;27095;;;;;-1:-1:-1;27095:71:9;;58206:32;-1:-1:-1;58206:10:9;58224;;5079:72119;58224:14;;58206:32;;5079:72119;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;:::o;:::-;;;;;;;;;;;:::o;36730:800::-;36900:12;5079:72119;-1:-1:-1;;;;;5079:72119:9;;36927:17;;36923:36;;5079:72119;;-1:-1:-1;;;36976:47:9;;5412:42;36976:47;;;5079:72119;5490:42;5079:72119;;;;36976:47;5079:72119;36976:47;5079:72119;36976:47;;;;;-1:-1:-1;;;36976:47:9;;;36730:800;-1:-1:-1;36972:551:9;;37498:13;;;;-1:-1:-1;37498:13:9;-1:-1:-1;37498:13:9;:::o;36972:551::-;-1:-1:-1;37129:11:9;;;37125:32;;5079:72119;36976:47;5079:72119;;;;;;;;;;37184:40;;5412:42;36976:47;37184:40;;5079:72119;5490:42;5079:72119;;;;37184:40;;;;;;;-1:-1:-1;37184:40:9;;;36972:551;-1:-1:-1;5079:72119:9;;;37291:2;37285:8;;37291:2;;;37318:8;;37311:16;37318:8;37307:20;37318:8;;:::i;:::-;37311:16;:::i;:::-;37307:20;;:::i;:::-;37441:23;;:::o;37281:145::-;37291:2;-1:-1:-1;37291:2:9;;;37384:8;37377:16;37384:8;37373:20;37384:8;;:::i;37377:16::-;37373:20;;:::i;37347:79::-;37413:13;37441:23;;:::o;37184:40::-;;;5079:72119;37184:40;;5079:72119;37184:40;;;;;;5079:72119;37184:40;;;:::i;:::-;;;5079:72119;;;;;;;;;;;;37184:40;;;;;;-1:-1:-1;37184:40:9;;37125:32;37142:15;;-1:-1:-1;37142:15:9;;:::o;36976:47::-;;;;;;;;;;;;;;;;;;;:::i;:::-;;;5079:72119;;;;;;;:::i;:::-;;;;;;;;;;;;;;;:::i;:::-;;36976:47;;;;;;;;-1:-1:-1;36976:47:9;;36923:36;-1:-1:-1;;;;;36946:13:9:o;36730:800::-;36900:12;5079:72119;-1:-1:-1;;;;;5079:72119:9;;36730:800;36927:17;;36923:36;;5079:72119;;-1:-1:-1;;;36976:47:9;;-1:-1:-1;;;;;5079:72119:9;;;36976:47;;;5079:72119;;;5490:42;5079:72119;;;;;;;36976:47;5079:72119;36976:47;;;;;-1:-1:-1;;;36976:47:9;;;36730:800;-1:-1:-1;36972:551:9;;37498:13;;;;;-1:-1:-1;37498:13:9;-1:-1:-1;37498:13:9;:::o;36972:551::-;-1:-1:-1;37129:11:9;;;37125:32;;5079:72119;;36976:47;5079:72119;;;;;;;;;37184:40;;36976:47;37184:40;;5079:72119;5490:42;5079:72119;;;;37184:40;;;;;;;-1:-1:-1;37184:40:9;;;-1:-1:-1;5079:72119:9;;;37291:2;37285:8;;37291:2;;;37318:8;;37311:16;37318:8;37307:20;37318:8;;:::i;37125:32::-;37142:15;;;-1:-1:-1;37142:15:9;;:::o;36976:47::-;;;;;;;;;;;;;;;;;;;:::i;:::-;;;5079:72119;;;;;;;:::i;:::-;;;;;;;;;;;;;;;:::i;:::-;;36976:47;;;;;;;;-1:-1:-1;36976:47:9;;36923:36;-1:-1:-1;;;;;;36946:13:9:o;36730:800::-;36900:12;5079:72119;-1:-1:-1;;;;;5079:72119:9;;36730:800;36927:17;;36923:36;;5079:72119;;-1:-1:-1;;;36976:47:9;;-1:-1:-1;;;;;5079:72119:9;;;36976:47;;;5079:72119;;;5412:42;5079:72119;;;;;;;36976:47;5079:72119;36976:47;;;;;-1:-1:-1;;;36976:47:9;;;36730:800;-1:-1:-1;36972:551:9;;37498:13;;;;;-1:-1:-1;37498:13:9;-1:-1:-1;37498:13:9;:::o;36972:551::-;-1:-1:-1;37129:11:9;;;37125:32;;5079:72119;;36976:47;5079:72119;;;;;;;;;37184:40;;36976:47;37184:40;;5079:72119;5412:42;5079:72119;;;;37184:40;;;;;;;-1:-1:-1;37184:40:9;;;-1:-1:-1;5079:72119:9;;;37291:2;37285:8;;37291:2;;;37318:8;;37311:16;37318:8;37307:20;37318:8;;:::i;36976:47::-;;;;;;;;;;;;;;;;;;;:::i;:::-;;;5079:72119;;;;;;;:::i;:::-;;;;;;;;;;;;;;;:::i;:::-;;36976:47;;;;;;;;-1:-1:-1;36976:47:9;;5084:380:6;5079:72119:9;;5199:47:6;;-1:-1:-1;5199:47:6;;;5079:72119:9;;;;5199:47:6;;5079:72119:9;;;;;;5199:47:6;;;;;5079:72119:9;;;;;;5199:47:6;;;;;;;:::i;:::-;9770:199;;;;;;;-1:-1:-1;9770:199:6;;9985:80;;;5084:380;5261:45;;;5257:201;;5084:380;;;:::o;5257:201::-;5349:43;5434:12;5079:72119:9;;;;;;;5199:47:6;5349:43;;;5199:47;5349:43;;5079:72119:9;-1:-1:-1;5199:47:6;5079:72119:9;;;5199:47:6;5349:43;;;5199:47;5349:43;;:::i;:::-;;;:::i;9985:80::-;9997:67;;-1:-1:-1;9997:15:6;;-1:-1:-1;;;;;;5079:72119:9;;10015:26:6;:30;;9997:67;9985:80;;;;9997:67;10063:1;10048:16;9997:67;;5084:380;5079:72119:9;;5199:47:6;;-1:-1:-1;5199:47:6;;;5079:72119:9;;;;5199:47:6;;5079:72119:9;;;;;;5199:47:6;;;;;5079:72119:9;;;;;;;5199:47:6;;;;;;;:::i;8370:720::-;;-1:-1:-1;8507:421:6;8370:720;8507:421;;;;;;;;;;;;-1:-1:-1;8507:421:6;;8942:15;;-1:-1:-1;;;;;;5079:72119:9;;8960:26:6;:31;8942:68;8938:146;;8370:720;:::o;8938:146::-;-1:-1:-1;;;;9033:40:6;;;-1:-1:-1;;;;;5079:72119:9;;;;9033:40:6;5079:72119:9;;;9033:40:6;8942:68;9009:1;8994:16;;8942:68;;5079:72119:9;;;-1:-1:-1;;;;;5079:72119:9;;;;;;:::o;29414:318::-;;5079:72119;;:::i;:::-;;;;29640:84;;5079:72119;;;;;;;;;29640:84;;;5079:72119;:::i;:::-;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;;;;;-1:-1:-1;;;;;5079:72119:9;;;;;29640:84;;;;;5079:72119;;;;:::i;:::-;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;:::o;26739:631::-;27095:71;26739:631;;;;5079:72119;26739:631;26995:14;;;:::i;:::-;5079:72119;;;;;;;:::i;:::-;;;;;;;;;;;;;;27095:71;;;;;;:::i;:::-;;;-1:-1:-1;;;;;5079:72119:9;27095:71;;;;;;;-1:-1:-1;27095:71:9;;;26739:631;5079:72119;;27252:71;5079:72119;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;27252:71;;27095;27252;;;:::i;:::-;;;-1:-1:-1;;;;;5079:72119:9;27252:71;;;;;;;-1:-1:-1;27252:71:9;;;26739:631;27351:11;;;;:::i;27252:71::-;;;5079:72119;27252:71;;5079:72119;27252:71;;;;;;5079:72119;27252:71;;;:::i;:::-;;;5079:72119;;;;27351:11;5079:72119;;27252:71;;;;;;-1:-1:-1;27252:71:9;;27095;;;;;5079:72119;27095:71;;5079:72119;27095:71;;;;;;5079:72119;27095:71;;;:::i;:::-;;;5079:72119;;;;;;;27252:71;27095;;;;;-1:-1:-1;27095:71:9;

Swarm Source

ipfs://ae4883b2f6d001d10dab0eb4cafb86cfab7dc8d9d456146282072e794eb0d275

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

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.