MON Price: $0.018931 (+3.34%)

Contract

0xC3C9b856189e933a87803Dd5CF1C01DF75Da76e7

Overview

MON Balance

Monad Chain LogoMonad Chain LogoMonad Chain Logo0 MON

MON Value

$0.00

Token Holdings

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To

There are no matching entries

> 10 Token Transfers found.

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
RouterLogic

Compiler Version
v0.8.26+commit.8a97fa7a

Optimization Enabled:
Yes with 1000000 runs

Other Settings:
paris EvmVersion, MIT license
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";

import {FeeAdapter} from "./FeeAdapter.sol";
import {RouterAdapter} from "./RouterAdapter.sol";
import {IRouterLogic} from "./interfaces/IRouterLogic.sol";
import {Flags} from "./libraries/Flags.sol";
import {PackedRoute} from "./libraries/PackedRoute.sol";
import {RouterLib} from "./libraries/RouterLib.sol";
import {TokenLib} from "./libraries/TokenLib.sol";

/**
 * @title RouterLogic
 * @notice Router logic contract for swapping tokens using a route.
 * The route must follow the PackedRoute format.
 */
contract RouterLogic is FeeAdapter, RouterAdapter, IRouterLogic {
    struct InternalSwapParams {
        uint256 feePtr;
        uint256 ptr;
        uint256 nbTokens;
        uint256 nbSwaps;
        address feeToken;
        address allocatee;
        uint256 feePercent;
        address recipient;
    }

    address private immutable ROUTER;

    /**
     * @dev Constructor for the RouterLogic contract.
     *
     * Requirements:
     * - The router address must be a contract with code.
     * - The protocolFeeReceiver address must not be the zero address.
     * - The protocolFeeShare must be less than or equal to 10_000 (100%).
     */
    constructor(
        address router,
        address routerV2_0,
        address uniswapV4Manager,
        address wnative,
        address protocolFeeReceiver,
        uint96 protocolFeeShare
    ) RouterAdapter(routerV2_0, uniswapV4Manager, wnative) FeeAdapter(protocolFeeReceiver, protocolFeeShare) {
        if (router.code.length == 0) revert RouterLogic__InvalidRouter();
        ROUTER = router;
    }

    /**
     * @dev Swaps an exact amount of tokenIn for as much tokenOut as possible.
     *
     * Requirements:
     * - The caller must be the router.
     * - The route must be a valid route, following the PackedRoute format.
     * - The route must have at least two tokens.
     * - The route must have at least one swap.
     * - The tokenIn must be the first token in the route.
     * - The tokenOut must be the last token in the route.
     * - Each swap amountIn and amountOut must be greater than zero and less than 2^128.
     * - The entire balance of all tokens must have been swapped to the last token.
     * - The actual amountOut must be greater than or equal to the amountOutMin.
     * - If the route has a fee, it should be the first route and the data must use the valid format:
     *   `(allocatee, feePercent, Flags.FEE_ID, 0, 0)`
     */
    function swapExactIn(
        address tokenIn,
        address tokenOut,
        uint256 amountIn,
        uint256 amountOutMin,
        address from,
        address to,
        bytes calldata route
    ) external override returns (uint256, uint256) {
        InternalSwapParams memory params;
        (params.feePtr, params.ptr, params.nbTokens, params.nbSwaps) = _startAndVerify(route, tokenIn, tokenOut);

        (params.feeToken, params.allocatee, params.feePercent) = _getFeePercent(route, params.feePtr, params.nbTokens);

        uint256 amountInWithoutFee = amountIn;
        if (params.feeToken == tokenIn) {
            unchecked {
                uint256 feeAmount = (amountInWithoutFee * params.feePercent) / BPS;
                amountInWithoutFee -= feeAmount;
                _sendFee(tokenIn, from, params.allocatee, feeAmount);
            }
        }

        uint256 amountOut = _swapExactIn(
            amountInWithoutFee,
            params.nbTokens,
            from,
            params.feeToken == tokenOut ? address(this) : to,
            params.ptr,
            params.nbSwaps,
            route
        );

        if (params.feeToken == tokenOut) {
            unchecked {
                uint256 feeAmount = (amountOut * params.feePercent) / BPS;
                amountOut -= feeAmount;
                _sendFee(tokenOut, address(this), params.allocatee, feeAmount);
                TokenLib.transfer(tokenOut, to, amountOut);
            }
        }

        if (amountOut < amountOutMin) revert RouterLogic__InsufficientAmountOut(amountOut, amountOutMin);

        return (amountIn, amountOut);
    }

    /**
     * @dev Swaps an exact amount of tokenOut for as little tokenIn as possible.
     * Due to roundings, the actual amountOut might actually be greater than the amountOut.
     *
     * Requirements:
     * - The caller must be the router.
     * - The route must be a valid route, following the PackedRoute format.
     * - The route must have at least two tokens.
     * - The route must have at least one swap.
     * - The tokenIn must be the first token in the route.
     * - The tokenOut must be the last token in the route.
     * - Each swap amountIn and amountOut must be greater than zero and less than 2^128.
     * - The entire balance of all tokens must have been used to calculate the amountIn.
     *   (due to potential rounding, some dust might be left in the contract after the swap)
     * - The actual amountIn must be less than or equal to the amountInMax.
     * - If the route has a fee, it should be the first route and the data must use the valid format:
     *   `(feeRecipient, feePercent, Flags.FEE_ID, 0, 0)`
     */
    function swapExactOut(
        address tokenIn,
        address tokenOut,
        uint256 amountInMax,
        uint256 amountOut,
        address from,
        address to,
        bytes calldata route
    ) external override returns (uint256 totalIn, uint256 totalOut) {
        InternalSwapParams memory params;
        (params.feePtr, params.ptr, params.nbTokens, params.nbSwaps) = _startAndVerify(route, tokenIn, tokenOut);

        (params.feeToken, params.allocatee, params.feePercent) = _getFeePercent(route, params.feePtr, params.nbTokens);

        if (PackedRoute.isTransferTax(route)) revert RouterLogic__TransferTaxNotSupported();

        address recipient;
        uint256 amountOutWithFee = amountOut;
        if (params.feeToken == tokenOut) {
            recipient = address(this);
            unchecked {
                amountOutWithFee = amountOutWithFee * BPS / (BPS - params.feePercent);
            }
        } else {
            recipient = to;
        }

        (uint256 amountInWithFee, uint256[] memory amountsIn) =
            _getAmountsIn(amountOutWithFee, params.nbTokens, params.nbSwaps, params.ptr, route);

        if (params.feeToken == tokenIn) {
            unchecked {
                uint256 feeAmount = amountInWithFee * params.feePercent / (BPS - params.feePercent);
                amountInWithFee += feeAmount;
                _sendFee(params.feeToken, from, params.allocatee, feeAmount);
            }
        }

        if (amountInWithFee > amountInMax) revert RouterLogic__ExceedsMaxAmountIn(amountInWithFee, amountInMax);

        _swapExactOut(from, recipient, amountsIn, params.ptr, params.nbTokens, params.nbSwaps, route);

        if (params.feeToken == tokenOut) {
            unchecked {
                uint256 feeAmount = amountOutWithFee - amountOut;
                _sendFee(tokenOut, address(this), params.allocatee, feeAmount);
                TokenLib.transfer(tokenOut, to, amountOut);
            }
        }

        return (amountInWithFee, amountOut);
    }

    /**
     * @dev Sweeps tokens from the contract to the recipient.
     *
     * Requirements:
     * - The caller must be the router owner.
     */
    function sweep(address token, address to, uint256 amount) external override {
        _checkSender();

        token == address(0) ? TokenLib.transferNative(to, amount) : TokenLib.transfer(token, to, amount);
    }

    /**
     * @dev Checks if the sender is the router's owner.
     *
     * Requirements:
     * - The sender must be the router's owner.
     */
    function _checkSender() internal view override {
        if (msg.sender != Ownable(ROUTER).owner()) revert RouterLogic__OnlyRouterOwner();
    }

    /**
     * @dev Helper function to check if the amount is valid.
     *
     * Requirements:
     * - The amount must be greater than zero and less than 2^128.
     */
    function _checkAmount(uint256 amount) private pure {
        if (amount == 0 || amount > type(uint128).max) revert RouterLogic__InvalidAmount();
    }

    /**
     * @dev Helper function to start and verify the route.
     *
     * Requirements:
     * - The caller must be the router.
     * - The route must have at least two tokens.
     * - The route must have at least one swap.
     * - The tokenIn must be the first token in the route.
     * - The tokenOut must be the last token in the route.
     */
    function _startAndVerify(bytes calldata route, address tokenIn, address tokenOut)
        private
        view
        returns (uint256 feePtr, uint256 ptr, uint256 nbTokens, uint256 nbSwaps)
    {
        if (msg.sender != ROUTER) revert RouterLogic__OnlyRouter();

        (ptr, nbTokens, nbSwaps) = PackedRoute.start(route);

        if (nbTokens < 2) revert RouterLogic__InsufficientTokens();

        (uint256 nextPtr, bytes32 value) = PackedRoute.next(route, ptr);

        if (Flags.id(PackedRoute.flags(value)) == Flags.FEE_ID) {
            if (nbSwaps < 2) revert RouterLogic__ZeroSwap();
            unchecked {
                --nbSwaps;
            }
            feePtr = ptr;
            ptr = nextPtr;
        } else {
            if (nbSwaps == 0) revert RouterLogic__ZeroSwap();
        }

        if (PackedRoute.token(route, 0) != tokenIn) revert RouterLogic__InvalidTokenIn();
        if (PackedRoute.token(route, nbTokens - 1) != tokenOut) revert RouterLogic__InvalidTokenOut();
    }

    /**
     * @dev Returns the fee amount added on the swap.
     * The fee is calculated as follows:
     * - if `isSwapExactIn`, the fee is calculated as `(amountIn * feePercent) / BPS`
     *   else, the fee is calculated as `(amountIn * feePercent) / (BPS - feePercent)`
     *
     * Requirements:
     * - The data must use the valid format:
     *   - If the fee is in tokenIn, `(allocatee, feePercent, Flags.FEE_ID, 0, 0)`
     *   - If the fee is in tokenOut, `(allocatee, feePercent, Flags.FEE_ID, nbTokens - 1, nbTokens - 1)`
     * - The feePercent must be greater than 0 and less than BPS.
     */
    function _getFeePercent(bytes calldata route, uint256 feePtr, uint256 nbTokens)
        private
        pure
        returns (address feeToken, address allocatee, uint256 feePercent)
    {
        if (feePtr > 0) {
            (, bytes32 value) = PackedRoute.next(route, feePtr);

            // The fee route use the pair field as the allocatee
            allocatee = PackedRoute.pair(value);
            feePercent = PackedRoute.percent(value);

            uint256 feeTokenId = PackedRoute.tokenInId(value);
            feeToken = PackedRoute.token(route, feeTokenId);

            if (
                (PackedRoute.flags(value) | (feeTokenId ^ PackedRoute.tokenOutId(value))) != 0
                    || (feeTokenId != 0 && feeTokenId != nbTokens - 1)
            ) revert RouterLogic__InvalidFeeData();
            if (feePercent == 0 || feePercent >= BPS) revert RouterLogic__InvalidFeePercent();
        }
    }

    /**
     * @dev Helper function to return the amountIn for each swap in the route and the amountIn of the first token.
     * The function will most likely revert if the same pair is used twice, or if the output of a pair is changed
     * between the calculation and the actual swap (for example, before swap hooks).
     *
     * Requirements:
     * - The route must be a valid route, following the PackedRoute format.
     * - Each swap amountIn and amountOut must be greater than zero and less than 2^128.
     * - The entire balance of all tokens must have been used to calculate the amountIn.
     */
    function _getAmountsIn(uint256 amountOut, uint256 nbTokens, uint256 nbSwaps, uint256 ptr, bytes calldata route)
        private
        returns (uint256 amountIn, uint256[] memory)
    {
        ptr = PackedRoute.endPtr(ptr, nbSwaps);

        uint256[] memory amountsIn = new uint256[](nbSwaps);
        uint256[] memory balances = new uint256[](nbTokens);

        balances[nbTokens - 1] = amountOut;
        uint256 total = amountOut;

        bytes32 value;
        for (uint256 i = nbSwaps; i > 0;) {
            (ptr, value) = PackedRoute.previous(route, ptr);

            // Reversing tokenInId and tokenOutId to match the reverse iteration
            uint256 tokenInId = PackedRoute.tokenOutId(value);
            uint256 tokenOutId = PackedRoute.tokenInId(value);

            uint256 amount = balances[tokenInId] * PackedRoute.percent(value) / BPS;
            balances[tokenInId] -= amount;

            _checkAmount(amount);
            amountIn = _getAmountIn(route, value, amount);
            balances[tokenOutId] += amountIn;
            _checkAmount(amountIn);

            amountsIn[--i] = amountIn;

            unchecked {
                total += amountIn - amount;
            }
        }

        amountIn = balances[0];
        if (total != amountIn) revert RouterLogic__ExcessBalanceUnused();

        return (amountIn, amountsIn);
    }

    /**
     * @dev Helper function to swap an exact amount of tokenIn for as much tokenOut as possible.
     * The function will most likely revert if the same pair is used twice, or if the output of a pair is changed
     * between the calculation and the actual swap (for example, before swap hooks).
     *
     * Requirements:
     * - The route must be a valid route, following the PackedRoute format.
     * - Each swap amountIn and amountOut must be greater than zero and less than 2^128.
     * - The entire balance of all tokens must have been used to calculate the amountIn.
     */
    function _swapExactIn(
        uint256 amountIn,
        uint256 nbTokens,
        address from,
        address recipient,
        uint256 ptr,
        uint256 nbSwaps,
        bytes calldata route
    ) internal returns (uint256 amountOut) {
        unchecked {
            uint256[] memory balances = new uint256[](nbTokens);
            uint256 lastTokenId = nbTokens - 1;

            balances[0] = amountIn;

            bytes32 value;
            for (uint256 i; i < nbSwaps; i++) {
                (ptr, value) = PackedRoute.next(route, ptr);

                amountIn += _swapExactInSingle(lastTokenId, route, balances, from, recipient, value);
            }

            amountOut = balances[lastTokenId];
            if (amountIn != amountOut) revert RouterLogic__ExcessBalanceUnused();
        }
    }

    /**
     * @dev Helper function to swap an exact amount of tokenOut for as little tokenIn as possible.
     * The function will most likely revert if the same pair is used twice, or if the output of a pair is changed
     * between the calculation and the actual swap (for example, before swap hooks).
     *
     * Requirements:
     * - The route must be a valid route, following the PackedRoute format.
     * - Each swap amountIn and amountOut must be greater than zero and less than 2^128.
     */
    function _swapExactOut(
        address from,
        address recipient,
        uint256[] memory amountsIn,
        uint256 ptr,
        uint256 nbTokens,
        uint256 nbSwaps,
        bytes calldata route
    ) internal {
        bytes32 value;
        for (uint256 i; i < nbSwaps; i++) {
            (ptr, value) = PackedRoute.next(route, ptr);

            _swapExactOutSingle(route, nbTokens, from, recipient, value, amountsIn[i]);
        }
    }

    /**
     * @dev Helper function to swap an exact amount of tokenIn for as much tokenOut as possible.
     *
     * Requirements:
     * - The route must be a valid route, following the PackedRoute format.
     * - Each swap amountIn and amountOut must be greater than zero and less than 2^128.
     */
    function _swapExactInSingle(
        uint256 lastTokenId,
        bytes calldata route,
        uint256[] memory balances,
        address from,
        address to,
        bytes32 value
    ) private returns (uint256) {
        uint256 flags = PackedRoute.flags(value);
        uint256 tokenInId = PackedRoute.tokenInId(value);
        uint256 tokenOutId = PackedRoute.tokenOutId(value);
        address recipient = tokenOutId == lastTokenId ? to : address(this);

        uint256 amountIn = balances[tokenInId] * PackedRoute.percent(value) / BPS;
        balances[tokenInId] -= amountIn;

        (address tokenIn, uint256 actualAmountIn) = _transferFromTokenId(
            route, tokenInId, from, Flags.callback(flags) ? address(this) : PackedRoute.pair(value), amountIn
        );

        _checkAmount(actualAmountIn);
        uint256 amountOut = _swap(route, value, tokenIn, actualAmountIn, recipient, flags);
        _checkAmount(amountOut);

        balances[tokenOutId] += amountOut;

        unchecked {
            return amountOut - amountIn;
        }
    }

    /**
     * @dev Helper function to swap an exact amount of tokenOut for as little tokenIn as possible.
     *
     * Requirements:
     * - The route must be a valid route, following the PackedRoute format.
     */
    function _swapExactOutSingle(
        bytes calldata route,
        uint256 nbTokens,
        address from,
        address to,
        bytes32 value,
        uint256 amountIn
    ) private {
        address pair = PackedRoute.pair(value);
        uint256 flags = PackedRoute.flags(value);

        (address tokenIn, uint256 actualAmountIn) = _transferFromTokenId(
            route, PackedRoute.tokenInId(value), from, Flags.callback(flags) ? address(this) : pair, amountIn
        );

        address recipient = PackedRoute.tokenOutId(value) == nbTokens - 1 ? to : address(this);

        _swap(route, value, tokenIn, actualAmountIn, recipient, flags);
    }

    /**
     * @dev Helper function to transfer tokens.
     * If the token is the first token of the route, it will transfer the token from the user to the recipient using
     * the transfer function of the router. If the token is flagged as a transfer tax, it will return the actual amount
     * received by the recipient.
     * Else, it will transfer the token from this contract to the recipient, unless the recipient is this contract.
     *
     * Requirements:
     * - The route must be a valid route, following the PackedRoute format.
     */
    function _transferFromTokenId(bytes calldata route, uint256 tokenId, address from, address to, uint256 amount)
        private
        returns (address, uint256)
    {
        address token = PackedRoute.token(route, tokenId);

        if (tokenId == 0) {
            bool isTransferTax = PackedRoute.isTransferTax(route);

            uint256 balance = isTransferTax ? TokenLib.balanceOf(token, to) : 0;
            RouterLib.transfer(ROUTER, token, from, to, amount);
            amount = isTransferTax ? TokenLib.balanceOf(token, to) - balance : amount;
        } else if (to != address(this)) {
            TokenLib.transfer(token, to, amount);
        }

        return (token, amount);
    }

    /**
     * @dev Helper function to transfer the fee.
     * If from is this contract, it will transfer the fee to the recipient directly.
     * Else, it will transfer the fee from the router to the recipient.
     */
    function _transferFee(address token, address from, address to, uint256 amount) internal override {
        if (from == address(this)) TokenLib.transfer(token, to, amount);
        else RouterLib.transfer(ROUTER, token, from, to, amount);
    }
}

// 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
pragma solidity ^0.8.20;

import {IFeeAdapter} from "./interfaces/IFeeAdapter.sol";
import {TokenLib} from "./libraries/TokenLib.sol";

/**
 * @title FeeAdapter
 * @notice This contract handles the fee sharing logic for the router.
 * It allows setting the protocol fee parameters and sending fees to the protocol fee recipient.
 * When sending fees, it splits the fee between the protocol fee recipient and the fee recipient
 * based on the protocol fee share. E.g. if the protocol fee share is 10% and the fee amount is 100,
 * then 10 will be sent to the protocol fee recipient and 90 will be sent to the fee recipient.
 */
abstract contract FeeAdapter is IFeeAdapter {
    uint256 internal constant BPS = 10_000;

    address private _protocolFeeRecipient;
    uint96 private _protocolFeeShare;

    constructor(address feeReceiver, uint96 feeShare) {
        _setProtocolFeeParameters(feeReceiver, feeShare);
    }

    /**
     * @dev Returns the protocol fee recipient address.
     */
    function getProtocolFeeRecipient() external view override returns (address) {
        return _protocolFeeRecipient;
    }

    /**
     * @dev Returns the protocol fee share.
     */
    function getProtocolFeeShare() external view override returns (uint256) {
        return _protocolFeeShare;
    }

    /**
     * @dev Sets the protocol fee parameters.
     *
     * Requirements:
     * - The caller must be authorized.
     * - The fee receiver address must not be zero.
     * - The fee share must be less than or equal to 10_000 (100%).
     */
    function setProtocolFeeParameters(address feeReceiver, uint96 feeShare) external override {
        _checkSender();
        _setProtocolFeeParameters(feeReceiver, feeShare);
    }

    /**
     * @dev Internal function to set the protocol fee parameters.
     *
     * Requirements:
     * - The fee receiver address must not be zero.
     * - The fee share must be less than or equal to 10_000 (100%).
     */
    function _setProtocolFeeParameters(address protocolFeeReceiver, uint96 protocolFeeShare) internal {
        if (protocolFeeReceiver == address(0)) revert FeeAdapter__InvalidProtocolFeeReceiver();
        if (protocolFeeShare > BPS) revert FeeAdapter__InvalidProtocolFeeShare();

        _protocolFeeRecipient = protocolFeeReceiver;
        _protocolFeeShare = protocolFeeShare;

        emit ProtocolFeeParametersSet(msg.sender, protocolFeeReceiver, protocolFeeShare);
    }

    /**
     * @dev Internal function to send the fee to the fee recipient.
     * The user parameter is only used for event logging.
     *
     * Requirements:
     * - The fee recipient address must not be zero.
     * - The fee amount must be greater than zero.
     */
    function _sendFee(address token, address payer, address allocatee, uint256 feeAmount) internal {
        if (feeAmount > 0) {
            if (allocatee == address(0)) revert FeeAdapter__InvalidFeeReceiver();

            uint256 protocolFeeAmount = (feeAmount * _protocolFeeShare) / BPS;
            uint256 remainingFeeAmount = feeAmount - protocolFeeAmount;

            if (remainingFeeAmount > 0) _transferFee(token, payer, allocatee, remainingFeeAmount);
            if (protocolFeeAmount > 0) _transferFee(token, payer, _protocolFeeRecipient, protocolFeeAmount);

            emit FeeSent(token, allocatee, feeAmount, protocolFeeAmount);
        }
    }

    /**
     * @dev Internal function to transfer tokens from the contract to the recipient.
     *
     * Requirements:
     * - The sender must be the contract itself.
     */
    function _transferFee(address token, address from, address to, uint256 amount) internal virtual {
        if (from != address(this)) revert FeeAdapter__InvalidFrom();
        TokenLib.transfer(token, to, amount);
    }

    /**
     * @dev Internal function to check if the sender is authorized.
     * Must be implemented in the derived contract.
     */
    function _checkSender() internal view virtual;
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {Flags} from "./libraries/Flags.sol";
import {PackedRoute} from "./libraries/PackedRoute.sol";
import {PairInteraction} from "./libraries/PairInteraction.sol";
import {TokenLib} from "./libraries/TokenLib.sol";

/**
 * @title RouterAdapter
 * @notice Router adapter contract for interacting with different types of pairs.
 * Currently supports Uniswap V2, LFJ Legacy LB, LFJ LB, Uniswap V3 pairs and LFJ Token Mill.
 */
abstract contract RouterAdapter {
    error RouterAdapter__InvalidId();
    error RouterAdapter__InsufficientLBLiquidity();
    error RouterAdapter__InsufficientTMLiquidity();
    error RouterAdapter__InsufficientTMV2Liquidity();
    error RouterAdapter__UnexpectedCallback();
    error RouterAdapter__UnexpectedAmountIn();
    error RouterAdapter__OnlyWnative();

    address private immutable ROUTER_V2_0;
    address private immutable UNISWAP_V4_MANAGER;
    address private immutable WNATIVE;

    uint256 private _callbackData = 0xdead;

    /**
     * @dev Constructor for the RouterAdapter contract.
     */
    constructor(address routerV2_0, address uniswapV4Manager, address wnative) {
        ROUTER_V2_0 = routerV2_0;
        WNATIVE = wnative;
        UNISWAP_V4_MANAGER = uniswapV4Manager;
    }

    /**
     * @dev Allows the contract to receive native tokens and wrap them, unless it's from the wrapped native
     * token contract itself, then it just accepts the native tokens.
     */
    receive() external payable {
        if (msg.sender != WNATIVE) TokenLib.wrap(WNATIVE, msg.value);
    }

    /**
     * @dev Fallback function to handle callbacks from pairs.
     *
     * Requirements:
     * - The callback data must have been set to `pair << 96 | PAIR_ID` before the callback, otherwise revert with
     * `RouterAdapter__UnexpectedCallback()`.
     */
    fallback(bytes calldata data) external returns (bytes memory) {
        uint256 callbackData = _callbackData;
        uint256 id = Flags.id(callbackData);
        // forge-lint: disable-next-line(unsafe-typecast)
        address account = address(uint160(callbackData >> 96));

        if (id == Flags.UNISWAP_V3_ID && msg.sender == account) {
            return _uniswapV3SwapCallback(data);
        } else if (id == Flags.UNISWAP_V4_ID && msg.sender == UNISWAP_V4_MANAGER) {
            return _uniswapV4UnlockCallback(data, account);
        } else if (id == Flags.POE_ID && msg.sender == account) {
            return _poeSwapCallback(data);
        }

        assembly ("memory-safe") {
            calldatacopy(0, 0, calldatasize())
            revert(0, calldatasize())
        }
    }

    /**
     * @dev Returns the amount of tokenIn needed to get amountOut from the pair.
     *
     * Requirements:
     * - The id of the flags must be valid and not the FEE_ID.
     */
    function _getAmountIn(bytes calldata, bytes32 value, uint256 amountOut) internal returns (uint256 amountIn) {
        address pair = PackedRoute.pair(value);
        uint256 flags = PackedRoute.flags(value);

        uint256 id = Flags.id(flags);

        if (id == Flags.UNISWAP_V2_ID) amountIn = _getAmountInUV2(pair, flags, amountOut);
        else if (id == Flags.LFJ_LEGACY_LIQUIDITY_BOOK_ID) amountIn = _getAmountInLegacyLB(pair, flags, amountOut);
        else if (id == Flags.LFJ_LIQUIDITY_BOOK_ID) amountIn = _getAmountInLB(pair, flags, amountOut);
        else if (id == Flags.UNISWAP_V3_ID) amountIn = _getAmountInUV3(pair, flags, amountOut);
        else if (id == Flags.LFJ_TOKEN_MILL_ID) amountIn = _getAmountInTM(pair, flags, amountOut);
        else if (id == Flags.LFJ_TOKEN_MILL_V2_ID) amountIn = _getAmountInTMV2(pair, flags, amountOut);
        // else if (id == Flags.UNISWAP_V4_ID) amountIn = _getAmountInUV4(route, value, pair, flags, amountOut);
        // Not supported yet because of a rounding issue in the getAmountIn vs getAmountOut functions
        // else if (id == Flags.BYREAL_ID) amountIn = _getSwapInByReal(route, pair, amountOut, value);
        else revert RouterAdapter__InvalidId();
    }

    /**
     * @dev Swaps tokens from the sender to the recipient.
     *
     * Requirements:
     * - The id of the flags must be valid and not the FEE_ID.
     */
    function _swap(bytes calldata, bytes32 value, address tokenIn, uint256 amountIn, address recipient, uint256 flags)
        internal
        returns (uint256 amountOut)
    {
        address pair = PackedRoute.pair(value);
        uint256 id = Flags.id(flags);

        if (id == Flags.UNISWAP_V2_ID) amountOut = _swapUV2(pair, flags, amountIn, recipient);
        else if (id == Flags.LFJ_LEGACY_LIQUIDITY_BOOK_ID) amountOut = _swapLegacyLB(pair, flags, recipient);
        else if (id == Flags.LFJ_LIQUIDITY_BOOK_ID) amountOut = _swapLB(pair, flags, recipient);
        else if (id == Flags.UNISWAP_V3_ID) amountOut = _swapUV3(pair, flags, recipient, amountIn, tokenIn);
        else if (id == Flags.LFJ_TOKEN_MILL_ID) amountOut = _swapTM(pair, flags, recipient, amountIn);
        else if (id == Flags.LFJ_TOKEN_MILL_V2_ID) amountOut = _swapTMV2(pair, flags, recipient, amountIn);
        // else if (id == Flags.UNISWAP_V4_ID) amountOut = _swapUV4(route, value, pair, flags, recipient, amountIn);
        else if (id == Flags.BYREAL_ID) amountOut = _swapByReal(pair, recipient, amountIn, tokenIn);
        else if (id == Flags.POE_ID) amountOut = _swapPoe(pair, flags, recipient, amountIn, tokenIn);
        else revert RouterAdapter__InvalidId();
    }

    /* Uniswap V2 */

    /**
     * @dev Returns the amount of tokenIn needed to get amountOut from the Uniswap V2 pair.
     */
    function _getAmountInUV2(address pair, uint256 flags, uint256 amountOut) internal view returns (uint256) {
        (uint256 reserveIn, uint256 reserveOut) = PairInteraction.getReservesUV2(pair, Flags.zeroForOne(flags));
        return (reserveIn * amountOut * 1000 - 1) / ((reserveOut - amountOut) * 997) + 1;
    }

    /**
     * @dev Swaps tokens from the sender to the recipient using the Uniswap V2 pair.
     */
    function _swapUV2(address pair, uint256 flags, uint256 amountIn, address recipient)
        internal
        returns (uint256 amountOut)
    {
        bool ordered = Flags.zeroForOne(flags);
        (uint256 reserveIn, uint256 reserveOut) = PairInteraction.getReservesUV2(pair, ordered);

        uint256 amountInWithFee = amountIn * 997;
        amountOut = (amountInWithFee * reserveOut) / (reserveIn * 1000 + amountInWithFee);

        (uint256 amount0, uint256 amount1) = ordered ? (uint256(0), amountOut) : (amountOut, uint256(0));
        PairInteraction.swapUV2(pair, amount0, amount1, recipient);
    }

    /* Legacy LB v2.0 */

    /**
     * @dev Returns the amount of tokenIn needed to get amountOut from the LFJ Legacy LB pair.
     */
    function _getAmountInLegacyLB(address pair, uint256 flags, uint256 amountOut)
        internal
        view
        returns (uint256 amountIn)
    {
        return PairInteraction.getSwapInLegacyLB(ROUTER_V2_0, pair, amountOut, Flags.zeroForOne(flags));
    }

    /**
     * @dev Swaps tokens from the sender to the recipient using the LFJ Legacy LB pair.
     */
    function _swapLegacyLB(address pair, uint256 flags, address recipient) internal returns (uint256 amountOut) {
        return PairInteraction.swapLegacyLB(pair, Flags.zeroForOne(flags), recipient);
    }

    /* LB v2.1 and v2.2 */

    /**
     * @dev Returns the amount of tokenIn needed to get amountOut from the LFJ LB pair.
     */
    function _getAmountInLB(address pair, uint256 flags, uint256 amountOut) internal view returns (uint256) {
        (uint256 amountIn, uint256 amountLeft) = PairInteraction.getSwapInLB(pair, amountOut, Flags.zeroForOne(flags));
        if (amountLeft != 0) revert RouterAdapter__InsufficientLBLiquidity();
        return amountIn;
    }

    /**
     * @dev Swaps tokens from the sender to the recipient using the LFJ LB pair.
     */
    function _swapLB(address pair, uint256 flags, address recipient) internal returns (uint256 amountOut) {
        return PairInteraction.swapLB(pair, Flags.zeroForOne(flags), recipient);
    }

    /* Uniswap V3 */

    /**
     * @dev Returns the amount of tokenIn needed to get amountOut from the Uniswap V3 pair.
     */
    function _getAmountInUV3(address pair, uint256 flags, uint256 amountOut) internal returns (uint256 amountIn) {
        return PairInteraction.getSwapInUV3(pair, Flags.zeroForOne(flags), amountOut);
    }

    /**
     * @dev Swaps tokens from the sender to the recipient using the Uniswap V3 pair.
     * Will set the callback address to the pair.
     */
    function _swapUV3(address pair, uint256 flags, address recipient, uint256 amountIn, address tokenIn)
        internal
        returns (uint256)
    {
        _callbackData = (uint256(uint160(pair)) << 96) | Flags.UNISWAP_V3_ID;

        (uint256 amountOut, uint256 actualAmountIn, uint256 hash) =
            PairInteraction.swapUV3(pair, recipient, Flags.zeroForOne(flags), amountIn, tokenIn);

        if (_callbackData != hash) revert RouterAdapter__UnexpectedCallback();
        if (actualAmountIn != amountIn) revert RouterAdapter__UnexpectedAmountIn();

        _callbackData = 0xdead;

        return amountOut;
    }

    /**
     * @dev Callback function for Uniswap V3 swaps.
     *
     * Requirements:
     * - The caller must be the callback address.
     */
    function _uniswapV3SwapCallback(bytes calldata data) internal returns (bytes memory) {
        (int256 amount0Delta, int256 amount1Delta, address token) = PairInteraction.decodeUV3CallbackData(data);

        _callbackData = PairInteraction.hashUV3(amount0Delta, amount1Delta, token);

        TokenLib.transfer(token, msg.sender, uint256(amount0Delta > 0 ? amount0Delta : amount1Delta));
        return new bytes(0);
    }

    /* Token Mill */

    /**
     * @dev Returns the amount of tokenIn needed to get amountOut from the LFJ Token Mill pair.
     */
    function _getAmountInTM(address pair, uint256 flags, uint256 amountOut) internal view returns (uint256) {
        (uint256 amountIn, uint256 actualAmountOut) =
            PairInteraction.getSwapInTM(pair, amountOut, Flags.zeroForOne(flags));
        if (actualAmountOut != amountOut) revert RouterAdapter__InsufficientTMLiquidity();
        return amountIn;
    }

    /**
     * @dev Swaps tokens from the sender to the recipient using the LFJ Token Mill pair.
     */
    function _swapTM(address pair, uint256 flags, address recipient, uint256 amountIn) internal returns (uint256) {
        (uint256 amountOut, uint256 actualAmountIn) =
            PairInteraction.swapTM(pair, recipient, amountIn, Flags.zeroForOne(flags));

        if (actualAmountIn != amountIn) revert RouterAdapter__InsufficientTMLiquidity();
        return amountOut;
    }

    /* Token Mill V2 */

    /**
     * @dev Returns the amount of tokenIn needed to get amountOut from the LFJ Token Mill V2 pair.
     */
    function _getAmountInTMV2(address pair, uint256 flags, uint256 amountOut) internal view returns (uint256) {
        (uint256 amountIn, uint256 actualAmountOut) =
            PairInteraction.getSwapInTMV2(pair, amountOut, Flags.zeroForOne(flags));
        if (actualAmountOut != amountOut) revert RouterAdapter__InsufficientTMV2Liquidity();
        return amountIn;
    }

    /**
     * @dev Swaps tokens from the sender to the recipient using the LFJ Token Mill V2 pair.
     */
    function _swapTMV2(address pair, uint256 flags, address recipient, uint256 amountIn) internal returns (uint256) {
        (uint256 amountOut, uint256 actualAmountIn) =
            PairInteraction.swapTMV2(pair, recipient, amountIn, Flags.zeroForOne(flags));

        if (actualAmountIn != amountIn) revert RouterAdapter__InsufficientTMV2Liquidity();
        return amountOut;
    }

    /* Uniswap V4 */

    /**
     * @dev Returns the amount of tokenIn needed to get amountOut from the Uniswap V4 pair.
     * Will set the callback address to the Uniswap V4 manager.
     */
    function _getAmountInUV4(bytes calldata route, bytes32 value, address pair, uint256 flags, uint256 amountOut)
        internal
        returns (uint256)
    {
        _callbackData = Flags.UNISWAP_V4_ID;

        // Use pair as the dataOffset
        return PairInteraction.getSwapInUV4(route, value, UNISWAP_V4_MANAGER, pair, Flags.zeroForOne(flags), amountOut);
    }

    /**
     * @dev Swaps tokens from the sender to the recipient using the Uniswap V4 pair.
     * Will set the callback address to the recipient.
     */
    function _swapUV4(
        bytes calldata route,
        bytes32 value,
        address pair,
        uint256 flags,
        address recipient,
        uint256 amountIn
    ) internal returns (uint256) {
        _callbackData = (uint256(uint160(recipient)) << 96) | Flags.UNISWAP_V4_ID;

        (uint256 amountOut, uint256 actualAmountIn) =
            PairInteraction.swapUV4(route, value, UNISWAP_V4_MANAGER, pair, Flags.zeroForOne(flags), amountIn);

        _callbackData = 0xdead;

        if (actualAmountIn != amountIn) revert RouterAdapter__UnexpectedAmountIn();

        return amountOut;
    }

    /**
     * @dev Callback function for Uniswap V4 unlocks.
     *
     * Requirements:
     * - The caller must be the callback address.
     */
    function _uniswapV4UnlockCallback(bytes calldata data, address recipient) internal returns (bytes memory) {
        (int256 delta0, int256 delta1) = PairInteraction.swapUV4Callback(data, recipient, WNATIVE);
        return abi.encode(0x20, 0x40, delta0, delta1);
    }

    /* ByReal */

    /**
     * @dev Returns the amount of tokenIn needed to get amountOut from the ByReal pair.
     */
    function _getSwapInByReal(bytes calldata route, address pair, uint256 amountOut, bytes32 value)
        internal
        view
        returns (uint256)
    {
        address tokenOut = PackedRoute.token(route, PackedRoute.tokenOutId(value));
        return PairInteraction.getSwapInByReal(pair, amountOut, tokenOut);
    }

    /**
     * @dev Swaps tokens from the sender to the recipient using the ByReal pair.
     */
    function _swapByReal(address pair, address recipient, uint256 amountIn, address tokenIn)
        internal
        returns (uint256 amountOut)
    {
        TokenLib.forceApprove(tokenIn, pair, amountIn);
        return PairInteraction.swapByReal(pair, recipient, amountIn, tokenIn);
    }

    /* Poe */

    /**
     * @dev Swaps tokens from the sender to the recipient using the Poe pair.
     */
    function _swapPoe(address pair, uint256 flags, address recipient, uint256 amountIn, address tokenIn)
        internal
        returns (uint256)
    {
        _callbackData = (uint256(uint160(pair)) << 96) | Flags.POE_ID;

        (uint256 amountOut, uint256 actualAmountIn, uint256 hash) =
            PairInteraction.swapPOE(pair, recipient, amountIn, Flags.zeroForOne(flags), tokenIn);

        if (_callbackData != hash) revert RouterAdapter__UnexpectedCallback();
        if (actualAmountIn != amountIn) revert RouterAdapter__UnexpectedAmountIn();

        _callbackData = 0xdead;

        return amountOut;
    }

    /**
     * @dev Callback function for Poe swaps.
     *
     * Requirements:
     * - The caller must be the callback address.
     */
    function _poeSwapCallback(bytes calldata data) internal returns (bytes memory) {
        (int256 amount0Delta, int256 amount1Delta, address token) = PairInteraction.decodeUV3CallbackData(data);

        _callbackData = PairInteraction.hashUV3(amount0Delta, amount1Delta, token);

        TokenLib.transfer(token, msg.sender, uint256(amount0Delta > 0 ? amount0Delta : amount1Delta));

        return abi.encode(PairInteraction.POE_SWAP_CALLBACK_SELECTOR);
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {IFeeAdapter} from "./IFeeAdapter.sol";

interface IRouterLogic is IFeeAdapter {
    error RouterLogic__OnlyRouter();
    error RouterLogic__InvalidTokenIn();
    error RouterLogic__InvalidTokenOut();
    error RouterLogic__InvalidRouter();
    error RouterLogic__ExcessBalanceUnused();
    error RouterLogic__InvalidAmount();
    error RouterLogic__ZeroSwap();
    error RouterLogic__InsufficientTokens();
    error RouterLogic__ExceedsMaxAmountIn(uint256 amountIn, uint256 amountInMax);
    error RouterLogic__InsufficientAmountOut(uint256 amountOut, uint256 amountOutMin);
    error RouterLogic__TransferTaxNotSupported();
    error RouterLogic__OnlyRouterOwner();
    error RouterLogic__InvalidFeeData();
    error RouterLogic__InvalidFeePercent();

    function swapExactIn(
        address tokenIn,
        address tokenOut,
        uint256 amountIn,
        uint256 amountOutMin,
        address from,
        address to,
        bytes calldata route
    ) external returns (uint256 totalIn, uint256 totalOut);

    function swapExactOut(
        address tokenIn,
        address tokenOut,
        uint256 amountInMax,
        uint256 amountOut,
        address from,
        address to,
        bytes calldata route
    ) external returns (uint256 totalIn, uint256 totalOut);

    function sweep(address token, address to, uint256 amount) external;
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

/**
 * @title Flags
 * @dev Helper library for parsing flags received from a packed route
 * The flags are a uint16 variable that contains the following information:
 * - zeroForOne: The first bit of the flags variable (0: false, 1: true)
 * - callback: The second bit of the flags variable (0: false, 1: true)
 * - id: The last 8 bits of the flags variable (1-255)
 * Note that the bits 2-7 are unused for now, and might be used in the future
 * The following ids must set the callback flag in order to work properly:
 * - Uniswap V3 (UNISWAP_V3_ID)
 * - Uniswap V4 (UNISWAP_V4_ID)
 * - ByReal (BYREAL_ID)
 */
library Flags {
    uint256 internal constant ONE_FOR_ZERO = 0;
    uint256 internal constant ZERO_FOR_ONE = 1;
    uint256 internal constant CALLBACK = 2;

    uint256 internal constant ID_OFFSET = 8;
    uint256 internal constant ID_MASK = 0xff00;

    /// forge-lint: disable-start(incorrect-shift)
    uint256 internal constant FEE_ID = 0 << ID_OFFSET; // 0 is reserved for the fee id
    uint256 internal constant UNISWAP_V2_ID = 1 << ID_OFFSET;
    uint256 internal constant LFJ_LEGACY_LIQUIDITY_BOOK_ID = 2 << ID_OFFSET;
    uint256 internal constant LFJ_LIQUIDITY_BOOK_ID = 3 << ID_OFFSET; // v2.1 and v2.2 have the same ABI for swaps
    uint256 internal constant UNISWAP_V3_ID = 4 << ID_OFFSET;
    uint256 internal constant LFJ_TOKEN_MILL_ID = 5 << ID_OFFSET;
    uint256 internal constant LFJ_TOKEN_MILL_V2_ID = 6 << ID_OFFSET;
    uint256 internal constant UNISWAP_V4_ID = 7 << ID_OFFSET;
    uint256 internal constant BYREAL_ID = 8 << ID_OFFSET;
    uint256 internal constant POE_ID = 9 << ID_OFFSET;

    /// forge-lint: disable-end(incorrect-shift)

    /**
     * @dev Returns the id of the flags variable
     */
    function id(uint256 flags) internal pure returns (uint256 idx) {
        return flags & ID_MASK;
    }

    /**
     * @dev Returns whether the zeroForOne flag is set
     */
    function zeroForOne(uint256 flags) internal pure returns (bool) {
        return flags & ZERO_FOR_ONE == ZERO_FOR_ONE;
    }

    /**
     * @dev Returns whether the callback flag is set
     */
    function callback(uint256 flags) internal pure returns (bool) {
        return flags & CALLBACK == CALLBACK;
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

/**
 * @title PackedRoute
 * @dev Helper library to decode packed route data
 * Route data is a byte array following the format:
 * [nb tokens]
 * [isTransferTax, token0, token1, token2, ..., tokenN-1, tokenN]
 * [{pairAB, percentAB, flagsAB, tokenA_id, tokenB_id}, {pairBC, percentBC, flagsBC, tokenB_id, tokenC_id}, ...]
 * [extraData][extraDataLength] (optional)
 *
 * The number of tokens is encoded on the first byte, it must be less or equal to 255.
 * The isTransferTax is a boolean flag that indicates if the token0 is a transfer tax token.
 * The tokens are encoded as 20 bytes addresses.
 *     The token0 must be the tokenIn and the tokenN must be the tokenOut.
 * The pairs are encoded as 20 bytes addresses.
 * The percent is a 16 bits unsigned integer, it must be less or equal to 10 000 (100%).
 *     It represents the percentage of the remaining amount use for the swap. Therefore, the last swap must be 100%.
 *     Thus, the sum of all percents will exceed 100% if there are more than 1 swap.
 * The flags are encoded as 16 bits unsigned integer. They contain the dex id and information for the swap. See the
 *     Flags library for more information.
 * The token ids are encoded as 8 bits unsigned integer. They must match the id of the token in the token list.
 * All the values are packed in a bytes array each time using the least amount of bytes possible (in solidity,
 *     use abi.encodePacked).
 * The extraData is an optional field that can be used to store additional data for the route. It must be at the end of
 *     the route and **MUST** have an even length (multiple of 2 bytes).
 *     This is to avoid the extra data to be taken as a route.
 * Currently, only Uniswap V4 requires extra data for its swaps, they must be encoded as follows:
 *     [fee: 3][tickSpacing: 3][nativeFlag: 1][hooks: 20][hookData length: 3][hookData: variable]
 *     The hookData length **MUST** be even (multiple of 2 bytes).
 * The extraDataLength is a 3 bytes unsigned integer that indicates the length of the extra data. It is appended at the
 *     end of the route. It is used to know where the extra data starts and ends. If there is no extra data,
 *     this field should be omitted.
 * Example 1, swapExactIn:
 * User wants to swap X WETH to USDT using the following route:
 *
 *                          WETH
 *                  0.8       |      0.2
 *                   ------------------
 *                   |                |
 *         UNIV3-WETH/WAVAX      LB2.1-WETH/USDC
 *                   |                |
 *                 WAVAX              |
 *            0.3    |      0.7       |
 *            -----------------       |
 *            |               |       |
 *            |    UNIV2-WAVAX/USDC   |
 *            |               |       |
 *            |               ---------
 *            |                    |
 *            |                  USDC
 * LB2.0-WAVAX/USDT         0.4    |     0.6
 *            |              --------------
 *            |              |            |
 *            |              |   UNIV3-BTC/USDC
 *            |              |            |
 *            |              |           BTC
 *            |    LB2.2-USDC/USDT        |
 *            |              |   UNIV2-BTC/USDT
 *            |              |            |
 *            -----------------------------
 *                           |
 *                          USDT
 *
 * Encoding:
 *              0     1     2     3     4
 * [5][false, WETH, WAVAX, USDC, BTC, USDT] // 5 tokens, WETH is not a transfer tax token
 * {UNIV3-WETH/WAVAX,  8000, UNIV3_ID | CALLBACK | ZERO_FOR_ONE, 0, 1}
 * {LB2.1-WETH/USDC,   2000,            LB2_1_ID | ZERO_FOR_ONE, 0, 2}
 * {UNIV2-WAVAX/USDC,  7000,            UNIV2_ID | ZERO_FOR_ONE, 1, 2}
 * {UNIV3-BTC/USDC,    6000, UNIV3_ID | CALLBACK | ONE_FOR_ZERO, 2, 3}
 * {UNIV2-BTC/USDT,   10000,            UNIV2_ID | ZERO_FOR_ONE, 3, 4}
 * {LB2.0-WAVAX/USDT,  3000,            LB2_0_ID | ZERO_FOR_ONE, 1, 4}
 * {LB2.2-USDC/USDT,   4000,            LB2_2_ID | ZERO_FOR_ONE, 2, 4}
 *
 * Now we have to recalculate the percents, as the amountIn is calculated using the percent of the remaining token
 * balance.
 *
 * UNIV3-WETH/WAVAX = 0.8
 * LB2.1-WETH/USDC = 0.2 / (1 - 0.8) = 1.0 (we force it to 1 as it's the last swap from WETH)
 * UNIV2-WAVAX/USDC = 0.7
 * UNIV3-BTC/USDC = 0.6
 * UNIV2-BTC/USDT = 1.0
 * LB2.0-WAVAX/USDT = 0.3 / (1 - 0.7) = 1.0 (we force it to 1 as it's the last swap to USDT)
 * LB2.2-USDC/USDT = 0.4 / (1 - 0.6) = 1.0 (we force it to 1 as it's the last swap to USDT)
 *
 * Final encoding:
 *
 *              0     1     2     3     4
 * [5][false, WETH, WAVAX, USDC, BTC, USDT] // 5 tokens, WETH is not a transfer tax token
 * {UNIV3-WETH/WAVAX,  8000, UNIV3_ID | CALLBACK | ZERO_FOR_ONE, 0, 1}
 * {LB2.1-WETH/USDC,  10000,            LB2_1_ID | ZERO_FOR_ONE, 0, 2}
 * {UNIV2-WAVAX/USDC,  7000,            UNIV2_ID | ZERO_FOR_ONE, 1, 2}
 * {UNIV3-BTC/USDC,    6000, UNIV3_ID | CALLBACK | ONE_FOR_ZERO, 2, 3}
 * {UNIV2-BTC/USDT,   10000,            UNIV2_ID | ZERO_FOR_ONE, 3, 4}
 * {LB2.0-WAVAX/USDT, 10000,            LB2_0_ID | ZERO_FOR_ONE, 1, 4}
 * {LB2.2-USDC/USDT,  10000,            LB2_2_ID | ZERO_FOR_ONE, 2, 4}
 *
 * Example 2, swapExactOut:
 * User wants to swap WETH to X USDT using the same route, we need to calculate the weights in the opposite direction:
 *
 * UNIV2-BTC/USDT = (0.2 + 0.8 * 0.7) * 0.6 ~= 0.46
 * LB2.2-USDC/USDT = (0.2 + 0.8 * 0.7) * 0.4 ~= 0.3
 * LB2.0-WAVAX/USDT = 0.8 * 0.3 ~= 0.24
 * UNIV3-BTC/USDC = 1.0 (single route)
 * UNIV2-WAVAX/USDC = 0.8 * 0.7 ~= 0.56
 * LB2.1-WETH/USDC = 0.2
 * UNIV3-WETH/WAVAX = 1.0 (single route)
 *
 * Then we have to normalize the nodes where the sum of its edges doesn't equal 1:
 *
 * UNIV2-WAVAX/USDC = 0.56 / 0.76 ~= 0.74
 * LB2.1-WETH/USDC = 0.2 / 0.76 ~= 0.26
 *
 *                          WETH
 *                            |
 *                   ------------------
 *                   |                |
 *         UNIV3-WETH/WAVAX      LB2.1-WETH/USDC
 *                   |                |
 *                 WAVAX              |
 *                   |                |
 *            -----------------       |
 *            |               |       |
 *            |    UNIV2-WAVAX/USDC   |
 *            |               |       |
 *            |               ---------
 *            |              0.74  |  0.26
 *            |                  USDC
 * LB2.0-WAVAX/USDT                |
 *            |              --------------
 *            |              |            |
 *            |              |   UNIV3-BTC/USDC
 *            |              |            |
 *            |              |           BTC
 *            |    LB2.2-USDC/USDT        |
 *            |              |   UNIV2-BTC/USDT
 *            |              |            |
 *            -----------------------------
 *           0.24           0.30        0.46
 *                           |
 *                          USDT
 *
 * Encoding:
 * Note that even if the route is now inverted, the tokens are still in the same order, as the user still wants to
 * swap WETH to USDT, just instead of swapping a specific number of WETH and receiving the maximum amount of USDT,
 * the user wants to swap the minimum amount of WETH to receive a specific number of USDT.
 *
 *              0     1     2     3     4
 * [5][false, WETH, WAVAX, USDC, BTC, USDT] // 5 tokens, WETH is not a transfer tax token
 * {UNIV3-WETH/WAVAX, 10000,  UNIV3_ID | CALLBACK | ZERO_FOR_ONE, 0, 1}
 * {LB2.1-WETH/USDC,   2600,             LB2_1_ID | ZERO_FOR_ONE, 0, 2}
 * {UNIV2-WAVAX/USDC,  7400,             UNIV2_ID | ZERO_FOR_ONE, 1, 2}
 * {UNIV3-BTC/USDC,   10000,  UNIV3_ID | CALLBACK | ONE_FOR_ZERO, 2, 3}
 * {UNIV2-BTC/USDT,    4600,             UNIV2_ID | ZERO_FOR_ONE, 3, 4}
 * {LB2.2-USDC/USDT,   3000,             LB2_2_ID | ZERO_FOR_ONE, 2, 4}
 * {LB2.0-WAVAX/USDT,  2400,             LB2_0_ID | ZERO_FOR_ONE, 1, 4}
 *
 * Now we have to recalculate the percents, as the amountIn is calculated using the percent of the remaining token
 * balance.
 * We start from the last token, USDT, and up to the first token, WETH.
 *
 * LB2.0-WAVAX/USDT = 0.24
 * LB2.2-USDC/USDT = 0.3 / (1 - 0.24) ~= 0.3947
 * UNIV2-BTC/USDT = 0.46 / ((1 - 0.24) * (1 - 0.3947)) = 1.0 (we force it to 1 as it's the last swap from USDT)
 * UNIV3-BTC/USDC = 1.0 (single route)
 * UNIV2-WAVAX/USDC = 0.74
 * LB2.1-WETH/USDC = 0.26 / (1 - 0.74) = 1.0 (we force it to 1 as it's the last swap from USDC)
 * UNIV3-WETH/WAVAX = 1.0 (single route)
 *
 * Final encoding:
 *
 *              0     1     2     3     4
 * [5][false, WETH, WAVAX, USDC, BTC, USDT]
 * {UNIV3-WETH/WAVAX, 10000,  UNIV3_ID | CALLBACK | ZERO_FOR_ONE, 0, 1}
 * {LB2.1-WETH/USDC,  10000,             LB2_1_ID | ZERO_FOR_ONE, 0, 2}
 * {UNIV2-WAVAX/USDC,  7400,             UNIV2_ID | ZERO_FOR_ONE, 1, 2}
 * {UNIV3-BTC/USDC,   10000,  UNIV3_ID | CALLBACK | ONE_FOR_ZERO, 2, 3}
 * {UNIV2-BTC/USDT,   10000,             UNIV2_ID | ZERO_FOR_ONE, 3, 4}
 * {LB2.2-USDC/USDT,   3947,             LB2_2_ID | ZERO_FOR_ONE, 2, 4}
 * {LB2.0-WAVAX/USDT,  2400,             LB2_0_ID | ZERO_FOR_ONE, 1, 4}
 */
library PackedRoute {
    error PackedRoute__InvalidLength();
    error PackedRoute__InvalidExtraDataLength();

    uint256 internal constant IS_TRANSFER_TAX_OFFSET = 1;
    uint256 internal constant TOKENS_OFFSET = 2;
    uint256 internal constant ROUTE_SIZE = 26;
    uint256 internal constant ADDRESS_SIZE = 20;

    // Extra data must be at the end of the route and have an even length.
    uint256 internal constant EXTRA_DATA_LENGTH_SIZE = 3; // Up to 16777215 bytes of extra data.
    uint256 internal constant EXTRA_DATA_LENGTH_SHIFT = 232; // 256 - EXTRA_DATA_LENGTH_SIZE * 8

    uint256 internal constant IS_TRANSFER_TAX_SHIFT = 248;
    uint256 internal constant ADDRESS_SHIFT = 96;
    uint256 internal constant PERCENT_SHIFT = 80;
    uint256 internal constant FLAGS_SHIFT = 64;
    uint256 internal constant TOKEN_IN_SHIFT = 56;
    uint256 internal constant TOKEN_OUT_SHIFT = 48;
    uint256 internal constant UINT16_MASK = 0xffff;
    uint256 internal constant UINT8_MASK = 0xff;

    /**
     * @dev Returns whether the tokenIn is a transfer tax token.
     * If route is not of the correct length, the function won't revert and might return an incorrect value.
     * Always use `start` to validate the route length before calling this function.
     */
    function isTransferTax(bytes calldata route) internal pure returns (bool b) {
        assembly ("memory-safe") {
            b := iszero(iszero(shr(IS_TRANSFER_TAX_SHIFT, calldataload(add(route.offset, IS_TRANSFER_TAX_OFFSET)))))
        }
    }

    /**
     * @dev Returns the token address at the given id.
     * If route is not of the correct length, the function won't revert and might return an incorrect value.
     * Always use `start` to validate the route length before calling this function.
     */
    function token(bytes calldata route, uint256 id) internal pure returns (address t) {
        assembly ("memory-safe") {
            t := shr(ADDRESS_SHIFT, calldataload(add(route.offset, add(TOKENS_OFFSET, mul(id, ADDRESS_SIZE)))))
        }
    }

    /**
     * @dev Returns the number of tokens, the number of swaps, and the offset to the first swap.
     * If the route is not of the correct length, the function will revert with `PackedRoute__InvalidLength`.
     * For the route to be valid, its length must equal
     * `TOKENS_OFFSET + nbTokens * ADDRESS_SIZE + nbSwaps * ROUTE_SIZE`.
     */
    function start(bytes calldata route) internal pure returns (uint256 ptr, uint256 nbTokens, uint256 nbSwaps) {
        assembly ("memory-safe") {
            nbTokens := shr(248, calldataload(route.offset))
        }

        unchecked {
            ptr = TOKENS_OFFSET + nbTokens * ADDRESS_SIZE;
            uint256 swapLength = route.length - ptr;
            if (route.length < ptr) revert PackedRoute__InvalidLength();

            if (swapLength % ROUTE_SIZE != 0) {
                // Might overflow if swapLength < 3, but `type(uint256).max - {0, 1, 2}` is never multiple of ROUTE_SIZE
                // so it will be caught by the modulo check.
                swapLength -= extraDataLength(route, swapLength) + EXTRA_DATA_LENGTH_SIZE;
                if (swapLength % ROUTE_SIZE != 0) revert PackedRoute__InvalidLength();
            }

            nbSwaps = swapLength / ROUTE_SIZE;
        }
    }

    /**
     * @dev Returns the end pointer of the swaps section of the route.
     * If the route is not of the correct length, the function will revert with `PackedRoute__InvalidLength`.
     * Always use `start` to validate the route length before calling this function.
     */
    function endPtr(uint256 startPtr, uint256 nbSwaps) internal pure returns (uint256 endPtr_) {
        unchecked {
            endPtr_ = startPtr + nbSwaps * ROUTE_SIZE;
        }
    }

    /**
     * @dev Returns the next swap pointer and the swap value.
     * If the route is not of the correct length, the function won't revert and might return an incorrect value.
     * Always use `start` to validate the route length before calling this function.
     */
    function next(bytes calldata route, uint256 ptr) internal pure returns (uint256 nextPtr, bytes32 value) {
        assembly ("memory-safe") {
            value := calldataload(add(route.offset, ptr))
            nextPtr := add(ptr, ROUTE_SIZE)
        }
    }

    /**
     * @dev Returns the previous swap pointer and the swap value.
     * If the route is not of the correct length, the function won't revert and might return an incorrect value.
     * Always use `start` to validate the route length before calling this function.
     */
    function previous(bytes calldata route, uint256 ptr) internal pure returns (uint256 previousPtr, bytes32 value) {
        assembly ("memory-safe") {
            previousPtr := sub(ptr, ROUTE_SIZE)
            value := calldataload(add(route.offset, previousPtr))
        }
    }

    /**
     * @dev Returns the pair address from the swap value.
     */
    function pair(bytes32 value) internal pure returns (address pair_) {
        assembly ("memory-safe") {
            pair_ := shr(ADDRESS_SHIFT, value)
        }
    }

    /**
     * @dev Returns the percent value from the swap value.
     */
    function percent(bytes32 value) internal pure returns (uint256 percent_) {
        assembly ("memory-safe") {
            percent_ := and(shr(PERCENT_SHIFT, value), UINT16_MASK)
        }
    }

    /**
     * @dev Returns the flags value from the swap value.
     */
    function flags(bytes32 value) internal pure returns (uint256 flags_) {
        assembly ("memory-safe") {
            flags_ := and(shr(FLAGS_SHIFT, value), UINT16_MASK)
        }
    }

    /**
     * @dev Returns the tokenInId from the swap value.
     */
    function tokenInId(bytes32 value) internal pure returns (uint256 tokenInId_) {
        assembly ("memory-safe") {
            tokenInId_ := and(shr(TOKEN_IN_SHIFT, value), UINT8_MASK)
        }
    }

    /**
     * @dev Returns the tokenOutId from the swap value.
     */
    function tokenOutId(bytes32 value) internal pure returns (uint256 tokenOutId_) {
        assembly ("memory-safe") {
            tokenOutId_ := and(shr(TOKEN_OUT_SHIFT, value), UINT8_MASK)
        }
    }

    /**
     * @dev Returns the length of the extra data at the end of the route.
     * If there is no extra data or if the swapLength is less than or equal to EXTRA_DATA_LENGTH_SIZE, returns 0.
     * If the route is not of the correct length, the function won't revert and might return an incorrect value.
     * Always use `start` to validate the route length before calling this function.
     * Reverts if the extra data length is not even.
     */
    function extraDataLength(bytes calldata route, uint256 swapLength) internal pure returns (uint256 length) {
        assembly ("memory-safe") {
            length := mul(
                gt(swapLength, EXTRA_DATA_LENGTH_SIZE),
                shr(EXTRA_DATA_LENGTH_SHIFT, calldataload(sub(add(route.offset, route.length), EXTRA_DATA_LENGTH_SIZE)))
            )
        }
        if (length & 1 == 1) revert PackedRoute__InvalidExtraDataLength();
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {TokenLib} from "./TokenLib.sol";

/**
 * @title RouterLib
 * @dev Helper library for router operations, such as validateAndTransfer, transfer, and swap.
 * The router must implement a fallback function that uses `validateAndTransfer` to validate the allowance
 * and transfer the tokens and functions that uses `swap` to call the router logic to swap tokens.
 * The router logic must implement the `swapExactIn` and `swapExactOut` functions to swap tokens and
 * use the `transfer` function to transfer tokens from the router according to the route selected.
 */
library RouterLib {
    error RouterLib__ZeroAmount();
    error RouterLib__InsufficientAllowance(uint256 allowance, uint256 amount);

    /**
     * @dev Returns the slot for the allowance of a token for a sender from an address.
     */
    function getAllowanceSlot(
        mapping(bytes32 key => uint256) storage allowances,
        address token,
        address sender,
        address from
    ) internal pure returns (bytes32 s) {
        assembly ("memory-safe") {
            mstore(0, shl(96, token))
            mstore(20, shl(96, sender))

            // Overwrite the last 8 bytes of the free memory pointer with zero,
            //which should always be zeros
            mstore(40, shl(96, from))

            let key := keccak256(0, 60)

            mstore(0, key)
            mstore(32, allowances.slot)

            s := keccak256(0, 64)
        }
    }

    /**
     * @dev Validates the allowance of a token for a sender from an address, and transfers the token.
     *
     * Requirements:
     * - The allowance must be greater than or equal to the amount.
     * - The amount must be greater than zero.
     * - If from is not the router, the token must have been approved for the router.
     */
    function validateAndTransfer(mapping(bytes32 key => uint256) storage allowances) internal {
        address token;
        address from;
        address to;
        uint256 amount;
        uint256 allowance;

        uint256 success;
        assembly ("memory-safe") {
            token := shr(96, calldataload(4))
            from := shr(96, calldataload(24))
            to := shr(96, calldataload(44))
            amount := calldataload(64)
        }

        bytes32 allowanceSlot = getAllowanceSlot(allowances, token, msg.sender, from);

        assembly ("memory-safe") {
            allowance := sload(allowanceSlot)

            if iszero(lt(allowance, amount)) {
                success := 1

                sstore(allowanceSlot, sub(allowance, amount))
            }
        }

        if (amount == 0) revert RouterLib__ZeroAmount(); // Also prevent calldata <= 64
        if (success == 0) revert RouterLib__InsufficientAllowance(allowance, amount);

        from == address(this) ? TokenLib.transfer(token, to, amount) : TokenLib.transferFrom(token, from, to, amount);
    }

    /**
     * @dev Calls the router to transfer tokens from an account to another account.
     *
     * Requirements:
     * - The call must succeed.
     * - The target contract must use `validateAndTransfer` inside its fallback function to validate the allowance
     *   and transfer the tokens accordingly.
     */
    function transfer(address router, address token, address from, address to, uint256 amount) internal {
        assembly ("memory-safe") {
            let m0x40 := mload(0x40)

            mstore(0, shr(32, shl(96, token)))
            mstore(24, shl(96, from))
            mstore(44, shl(96, to))
            mstore(64, amount)

            if iszero(call(gas(), router, 0, 0, 96, 0, 0)) {
                returndatacopy(0, 0, returndatasize())
                revert(0, returndatasize())
            }

            mstore(0x40, m0x40)
        }
    }

    /**
     * @dev Swaps tokens using the router logic.
     * It will also set the allowance for the logic contract to spend the token from the sender and reset it
     * after the swap is done.
     *
     * Requirements:
     * - The logic contract must not be the zero address.
     * - The call must succeed.
     * - The logic contract must call this contract's fallback function to validate the allowance and transfer the
     *   tokens.
     */
    function swap(
        mapping(bytes32 key => uint256) storage allowances,
        address tokenIn,
        address tokenOut,
        uint256 amountIn,
        uint256 amountOut,
        address from,
        address to,
        bytes calldata route,
        bool exactIn,
        address logic
    ) internal returns (uint256 totalIn, uint256 totalOut) {
        bytes32 allowanceSlot = getAllowanceSlot(allowances, tokenIn, logic, from);

        uint256 length = 256 + route.length; // 32 * 6 + 32 + 32 + route.length
        bytes memory data = new bytes(length);

        assembly ("memory-safe") {
            sstore(allowanceSlot, amountIn)

            switch exactIn
            // swapExactIn(tokenIn, tokenOut, amountIn, amountOut, from, to, route)
            // swapExactOut(tokenIn, tokenOut, amountOut, amountIn, from, to, route)
            case 1 { mstore(data, 0xbd084435) }
            default { mstore(data, 0xcb7e0007) }

            mstore(add(data, 32), tokenIn)
            mstore(add(data, 64), tokenOut)
            mstore(add(data, 96), amountIn)
            mstore(add(data, 128), amountOut)
            mstore(add(data, 160), from)
            mstore(add(data, 192), to)
            mstore(add(data, 224), 224) // 32 * 6 + 32
            mstore(add(data, 256), route.length)
            calldatacopy(add(data, 288), route.offset, route.length)

            if iszero(call(gas(), logic, 0, add(data, 28), add(length, 4), 0, 64)) {
                returndatacopy(0, 0, returndatasize())
                revert(0, returndatasize())
            }

            totalIn := mload(0)
            totalOut := mload(32)

            sstore(allowanceSlot, 0)
        }
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

/**
 * @title TokenLib
 * @dev Helper library for token operations, such as balanceOf, transfer, transferFrom, wrap, and unwrap.
 */
library TokenLib {
    error TokenLib__BalanceOfFailed();
    error TokenLib__WrapFailed();
    error TokenLib__UnwrapFailed();
    error TokenLib__NativeTransferFailed();
    error TokenLib__TransferFromFailed();
    error TokenLib__TransferFailed();
    error TokenLib__ApproveFailed();

    /**
     * @dev Returns the balance of a token for an account.
     *
     * Requirements:
     * - The call must succeed.
     * - The target contract must return at least 32 bytes.
     */
    function balanceOf(address token, address account) internal view returns (uint256 amount) {
        uint256 success;
        uint256 returnDataSize;

        assembly ("memory-safe") {
            mstore(0, 0x70a08231) // balanceOf(address)
            mstore(32, account)

            success := staticcall(gas(), token, 28, 36, 0, 32)

            returnDataSize := returndatasize()

            amount := mload(0)
        }

        if (success == 0) _tryRevertWithReason();

        // If call failed, and it didn't already bubble up the revert reason, then the return data size must be 0,
        // which will revert here with a generic error message
        if (returnDataSize < 32) revert TokenLib__BalanceOfFailed();
    }

    /**
     * @dev Returns the balance of a token for an account, or the native balance of the account if the token is the
     * native token.
     *
     * Requirements:
     * - The call must succeed (if the token is not the native token).
     * - The target contract must return at least 32 bytes (if the token is not the native token).
     */
    function universalBalanceOf(address token, address account) internal view returns (uint256 amount) {
        return token == address(0) ? account.balance : balanceOf(token, account);
    }

    /**
     * @dev Transfers native tokens to an account.
     *
     * Requirements:
     * - The call must succeed.
     */
    function transferNative(address to, uint256 amount) internal {
        uint256 success;

        assembly ("memory-safe") {
            success := call(gas(), to, amount, 0, 0, 0, 0)
        }

        if (success == 0) {
            _tryRevertWithReason();
            revert TokenLib__NativeTransferFailed();
        }
    }

    /**
     * @dev Transfers tokens from an account to another account.
     * This function does not check if the target contract has code, this should be done before calling this function
     *
     * Requirements:
     * - The call must succeed.
     */
    function wrap(address wnative, uint256 amount) internal {
        uint256 success;

        assembly ("memory-safe") {
            mstore(0, 0xd0e30db0) // deposit()

            success := call(gas(), wnative, amount, 28, 4, 0, 0)
        }

        if (success == 0) {
            _tryRevertWithReason();
            revert TokenLib__WrapFailed();
        }
    }

    /**
     * @dev Transfers tokens from an account to another account.
     * This function does not check if the target contract has code, this should be done before calling this function
     *
     * Requirements:
     * - The call must succeed.
     */
    function unwrap(address wnative, uint256 amount) internal {
        uint256 success;

        assembly ("memory-safe") {
            mstore(0, 0x2e1a7d4d) // withdraw(uint256)
            mstore(32, amount)

            success := call(gas(), wnative, 0, 28, 36, 0, 0)
        }

        if (success == 0) {
            _tryRevertWithReason();
            revert TokenLib__UnwrapFailed();
        }
    }

    /**
     * @dev Transfers tokens from an account to another account.
     *
     * Requirements:
     * - The call must succeed
     * - The target contract must either return true or no value.
     * - The target contract must have code.
     */
    function transfer(address token, address to, uint256 amount) internal {
        uint256 success;
        uint256 returnSize;
        uint256 returnValue;

        assembly ("memory-safe") {
            let m0x40 := mload(0x40)

            mstore(0, 0xa9059cbb) // transfer(address,uint256)
            mstore(32, to)
            mstore(64, amount)

            success := call(gas(), token, 0, 28, 68, 0, 32)

            returnSize := returndatasize()
            returnValue := mload(0)

            mstore(0x40, m0x40)
        }

        if (success == 0) {
            _tryRevertWithReason();
            revert TokenLib__TransferFailed();
        }

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

    /**
     * @dev Transfers tokens from an account to another account.
     *
     * Requirements:
     * - The call must succeed.
     * - The target contract must either return true or no value.
     * - The target contract must have code.
     */
    function transferFrom(address token, address from, address to, uint256 amount) internal {
        uint256 success;
        uint256 returnSize;
        uint256 returnValue;

        assembly ("memory-safe") {
            let m0x40 := mload(0x40)
            let m0x60 := mload(0x60)

            mstore(0, 0x23b872dd) // transferFrom(address,address,uint256)
            mstore(32, from)
            mstore(64, to)
            mstore(96, amount)

            success := call(gas(), token, 0, 28, 100, 0, 32)

            returnSize := returndatasize()
            returnValue := mload(0)

            mstore(0x40, m0x40)
            mstore(0x60, m0x60)
        }

        if (success == 0) {
            _tryRevertWithReason();
            revert TokenLib__TransferFromFailed();
        }

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

    /**
     * @dev Approves an account to spend tokens on behalf of the caller.
     *
     * Requirements:
     * - The call must succeed.
     * - The target contract must either return true or no value.
     * - The target contract must have code.
     */
    function forceApprove(address token, address spender, uint256 amount) internal {
        uint256 success;
        uint256 returnSize;
        uint256 returnValue;

        assembly ("memory-safe") {
            function approve(token_, amount_) -> success_ {
                mstore(64, amount_)
                success_ := call(gas(), token_, 0, 28, 68, 64, 32)
            }

            let m0x40 := mload(0x40)

            mstore(0, 0x095ea7b3) // approve(address,uint256)
            mstore(32, spender)

            success := approve(token, amount)
            if iszero(and(eq(mload(64), 1), success)) {
                if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
                    pop(approve(token, 0))
                    success := approve(token, amount)
                }
            }

            returnSize := returndatasize()
            returnValue := mload(64)

            mstore(0x40, m0x40)
        }

        if (success == 0) {
            _tryRevertWithReason();
            revert TokenLib__ApproveFailed();
        }

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

    /**
     * @dev Tries to bubble up the revert reason.
     * This function needs to be called only if the call has failed, and will revert if there is a revert reason.
     * This function might no revert if there is no revert reason, always use it in conjunction with a revert.
     */
    function _tryRevertWithReason() private pure {
        assembly ("memory-safe") {
            if returndatasize() {
                returndatacopy(0, 0, returndatasize())
                revert(0, returndatasize())
            }
        }
    }
}

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

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

interface IFeeAdapter {
    error FeeAdapter__InvalidProtocolFeeReceiver();
    error FeeAdapter__InvalidProtocolFeeShare();
    error FeeAdapter__InvalidFeeReceiver();
    error FeeAdapter__InvalidFrom();

    event ProtocolFeeParametersSet(
        address indexed sender, address indexed protocolFeeReceiver, uint96 protocolFeeShare
    );
    event FeeSent(address indexed token, address indexed allocatee, uint256 feeAmount, uint256 protocolFeeAmount);

    function getProtocolFeeRecipient() external view returns (address);

    function getProtocolFeeShare() external view returns (uint256);

    function setProtocolFeeParameters(address feeReceiver, uint96 feeShare) external;
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {PackedRoute} from "./PackedRoute.sol";
import {SafeCast} from "./SafeCast.sol";
import {TokenLib} from "./TokenLib.sol";

/**
 * @title PairInteraction
 * @dev Library for interacting with Uniswap V2, LFJ, and Uniswap V3 pairs.
 */
library PairInteraction {
    using SafeCast for uint256;
    using SafeCast for int256;

    error PairInteraction__InvalidReturnData();
    error PairInteraction__CallFailed();
    error PairInteraction__InvalidState();

    uint256 internal constant MASK_UINT112 = 0xffffffffffffffffffffffffffff;
    uint256 internal constant MIN_SWAP_SQRT_RATIO = 4295128739 + 1;
    uint256 internal constant MAX_SWAP_SQRT_RATIO = 1461446703485210103287273052203988822378723970342 - 1;

    bytes4 internal constant POE_SWAP_CALLBACK_SELECTOR = 0xfa483e72;

    /**
     * @dev Returns the ordered reserves of a Uniswap V2 pair.
     * If ordered is true, the reserves are returned as (reserve0, reserve1), otherwise as (reserve1, reserve0).
     *
     * Requirements:
     * - The call must succeed.
     * - The pair must have code.
     * - The return data must be at least 64 bytes.
     */
    function getReservesUV2(address pair, bool ordered) internal view returns (uint256 reserveIn, uint256 reserveOut) {
        uint256 returnDataSize;
        assembly ("memory-safe") {
            mstore(0, 0x0902f1ac) // getReserves()

            if staticcall(gas(), pair, 28, 4, 0, 64) { returnDataSize := returndatasize() }

            switch ordered
            case 0 {
                reserveIn := and(mload(32), MASK_UINT112)
                reserveOut := and(mload(0), MASK_UINT112)
            }
            default {
                reserveIn := and(mload(0), MASK_UINT112)
                reserveOut := and(mload(32), MASK_UINT112)
            }
        }

        if (returnDataSize < 64) revert PairInteraction__InvalidReturnData();
    }

    /**
     * @dev Returns the amount of tokenIn required to get amountOut from a Uniswap V2 pair.
     * The function doesn't check that the pair has any code, `getReservesUV2` should be called first to ensure that.
     *
     * Requirements:
     * - The call must succeed.
     */
    function swapUV2(address pair, uint256 amount0, uint256 amount1, address recipient) internal {
        uint256 success;
        assembly ("memory-safe") {
            let ptr := mload(0x40)

            mstore(ptr, 0x022c0d9f) // swap(uint256,uint256,address,bytes)
            mstore(add(ptr, 32), amount0)
            mstore(add(ptr, 64), amount1)
            mstore(add(ptr, 96), recipient)
            mstore(add(ptr, 128), 128)
            mstore(add(ptr, 160), 0)

            mstore(0x40, add(ptr, 160)) // update free memory pointer to 160 because 160:192 is 0

            success := call(gas(), pair, 0, add(ptr, 28), 164, 0, 0)
        }
        _bubbleRevert(success);
    }

    /**
     * @dev Returns the amount of tokenIn required to get amountOut from a LFJ Legacy LB pair.
     * It uses the router v2.0 helper function `getSwapIn` to get the amount required.
     *
     * Requirements:
     * - The call must succeed.
     * - The router must have code.
     * - The return data must be at least 32 bytes.
     */
    function getSwapInLegacyLB(address router, address pair, uint256 amountOut, bool swapForY)
        internal
        view
        returns (uint256 amountIn)
    {
        uint256 returnDataSize;
        uint256 success;
        assembly ("memory-safe") {
            let ptr := mload(0x40)

            mstore(ptr, 0x5bdd4b7c) // getSwapIn(address,uint256,bool)
            mstore(add(ptr, 32), pair)
            mstore(add(ptr, 64), amountOut)
            mstore(add(ptr, 96), swapForY)

            mstore(0x40, add(ptr, 128))

            success := staticcall(gas(), router, add(ptr, 28), 100, 0, 32)

            returnDataSize := returndatasize()

            amountIn := mload(0)
        }
        _bubbleRevert(success);

        if (returnDataSize < 32) revert PairInteraction__InvalidReturnData();
    }

    /**
     * @dev Swaps tokenIn for tokenOut in a LFJ Legacy LB pair.
     *
     * Requirements:
     * - The call must succeed.
     * - The pair must have code.
     * - The return data must be at least 64 bytes.
     */
    function swapLegacyLB(address pair, bool swapForY, address recipient) internal returns (uint256 amountOut) {
        uint256 returnDataSize;
        uint256 success;

        assembly ("memory-safe") {
            let m0x40 := mload(0x40)

            mstore(0, 0x53c059a0) // swap(bool,address)
            mstore(32, swapForY)
            mstore(64, recipient)

            success := call(gas(), pair, 0, 28, 68, 0, 64)

            returnDataSize := returndatasize()

            switch swapForY
            case 0 { amountOut := mload(0) }
            default { amountOut := mload(32) }

            mstore(0x40, m0x40)
        }
        _bubbleRevert(success);

        if (returnDataSize < 64) revert PairInteraction__InvalidReturnData();
    }

    /**
     * @dev Returns the amount of tokenIn required to get amountOut from a LFJ LB pair (v2.0 and v2.1).
     *
     * Requirements:
     * - The call must succeed.
     * - The pair must have code.
     * - The return data must be at least 64 bytes.
     */
    function getSwapInLB(address pair, uint256 amountOut, bool swapForY)
        internal
        view
        returns (uint256 amountIn, uint256 amountLeft)
    {
        uint256 returnDataSize;
        uint256 success;
        assembly ("memory-safe") {
            let m0x40 := mload(0x40)

            mstore(0, 0xabcd7830) // getSwapIn(uint128,bool)
            mstore(32, amountOut)
            mstore(64, swapForY)

            success := staticcall(gas(), pair, 28, 68, 0, 64)

            returnDataSize := returndatasize()

            amountIn := mload(0)
            amountLeft := mload(32)

            mstore(0x40, m0x40)
        }
        _bubbleRevert(success);

        if (returnDataSize < 64) revert PairInteraction__InvalidReturnData();
    }

    /**
     * @dev Swaps tokenIn for tokenOut in a LFJ LB pair (v2.0 and v2.1).
     *
     * Requirements:
     * - The call must succeed.
     * - The pair must have code.
     * - The return data must be at least 32 bytes.
     */
    function swapLB(address pair, bool swapForY, address recipient) internal returns (uint256 amountOut) {
        uint256 returnDataSize;
        uint256 success;

        assembly ("memory-safe") {
            let m0x40 := mload(0x40)

            mstore(0, 0x53c059a0) // swap(bool,address)
            mstore(32, swapForY)
            mstore(64, recipient)

            success := call(gas(), pair, 0, 28, 68, 0, 32)

            returnDataSize := returndatasize()

            switch swapForY
            case 0 { amountOut := shr(128, mload(16)) }
            default { amountOut := shr(128, mload(0)) }

            mstore(0x40, m0x40)
        }
        _bubbleRevert(success);

        if (returnDataSize < 32) revert PairInteraction__InvalidReturnData();
    }

    /**
     * @dev Returns the amount of tokenIn required to get amountOut from a Uniswap V3 pair.
     * The function actually tries to swap token but revert before having to send any token.
     *
     * Requirements:
     * - The call must succeed.
     * - The pair must have code.
     * - The return data must match the expected format, which is to revert with a
     *   `UniswapV3SwapCallback(int256 amount0Delta, int256 amount1Delta)` error.
     */
    function getSwapInUV3(address pair, bool zeroForOne, uint256 amountOut) internal returns (uint256 amountIn) {
        (uint256 success, uint256 ptr) = callSwapUV3(pair, address(this), zeroForOne, -amountOut.toInt256(), address(0));

        assembly ("memory-safe") {
            // data = [selector: 4][amount0: 32][amount1: 32][data_ptr: 32][data_length: 32][data_value: 32]
            switch and(iszero(success), eq(returndatasize(), 164))
            case 0 {
                returndatacopy(0, 0, returndatasize())
                revert(0, returndatasize())
            }
            default {
                switch zeroForOne
                case 0 { amountIn := mload(add(ptr, 36)) }
                default { amountIn := mload(add(ptr, 4)) }
            }
        }
    }

    /**
     * @dev Swaps tokenIn for tokenOut in a Uniswap V3 pair.
     *
     * Requirements:
     * - The call must succeed.
     * - The pair must have code.
     * - The return data must be at least 64 bytes.
     */
    function swapUV3(address pair, address recipient, bool zeroForOne, uint256 amountIn, address tokenIn)
        internal
        returns (uint256 actualAmountOut, uint256 actualAmountIn, uint256 expectedHash)
    {
        (uint256 success, uint256 ptr) = callSwapUV3(pair, recipient, zeroForOne, amountIn.toInt256(), tokenIn);
        _bubbleRevert(success);

        uint256 returnDataSize;

        assembly ("memory-safe") {
            returnDataSize := returndatasize()

            mstore(add(ptr, 64), tokenIn)
            expectedHash := keccak256(ptr, 96)

            switch zeroForOne
            case 0 {
                actualAmountOut := mload(ptr)
                actualAmountIn := mload(add(ptr, 32))
            }
            default {
                actualAmountIn := mload(ptr)
                actualAmountOut := mload(add(ptr, 32))
            }

            actualAmountOut := sub(0, actualAmountOut) // Invert the sign
        }

        if (returnDataSize < 64) revert PairInteraction__InvalidReturnData();
    }

    /**
     * @dev Returns the hash of the amount deltas and token address for a Uniswap V3 pair.
     * The hash is used to check that the callback contains the expected data.
     */
    function hashUV3(int256 amount0Delta, int256 amount1Delta, address token) internal pure returns (uint256 hash) {
        assembly ("memory-safe") {
            let ptr := mload(0x40)

            mstore(0, amount0Delta)
            mstore(32, amount1Delta)
            mstore(64, token)

            hash := keccak256(0, 96)

            mstore(0x40, ptr)
        }
    }

    /**
     * @dev Decodes the amount deltas and token address from the callback data of a Uniswap V3 pair.
     *
     * Requirements:
     * - The data must be exactly 164 bytes.
     */
    function decodeUV3CallbackData(bytes calldata data)
        internal
        pure
        returns (int256 amount0Delta, int256 amount1Delta, address token)
    {
        // data = [selector: 4][amount0: 32][amount1: 32][data_ptr: 32][data_length: 32][data_value: 32]
        if (data.length != 164) revert PairInteraction__InvalidReturnData();

        assembly ("memory-safe") {
            amount0Delta := calldataload(4)
            amount1Delta := calldataload(36)
            token := calldataload(132)
        }
    }

    /**
     * @dev Calls the `swap` function of a Uniswap V3 pair.
     * This function doesn't revert on failure, it returns a success flag instead.
     * It also returns the pointer to the return data.
     *
     * Requirements:
     * - The call must succeed.
     */
    function callSwapUV3(address pair, address recipient, bool zeroForOne, int256 deltaAmount, address tokenIn)
        internal
        returns (uint256 success, uint256 ptr)
    {
        uint256 priceLimit = zeroForOne ? MIN_SWAP_SQRT_RATIO : MAX_SWAP_SQRT_RATIO;

        assembly ("memory-safe") {
            ptr := mload(0x40)

            mstore(ptr, 0x128acb08) // swap(address,bool,int256,uint160,bytes)
            mstore(add(ptr, 32), recipient)
            mstore(add(ptr, 64), zeroForOne)
            mstore(add(ptr, 96), deltaAmount)
            mstore(add(ptr, 128), priceLimit)
            mstore(add(ptr, 160), 160)
            mstore(add(ptr, 192), 32)
            mstore(add(ptr, 224), tokenIn)

            mstore(0x40, add(ptr, 256))

            success := call(gas(), pair, 0, add(ptr, 28), 228, ptr, 68)
        }
    }

    /**
     * @dev Returns the amount of tokenIn required to get amountOut from a LFJ Token Mill pair.
     *
     * Requirements:
     * - The call must succeed.
     * - The pair must have code.
     * - The return data must be at least 32 bytes.
     */
    function getSwapInTM(address pair, uint256 amountOut, bool swapForY)
        internal
        view
        returns (uint256 amountIn, uint256 actualAmountOut)
    {
        uint256 returnDataSize;
        uint256 success;
        assembly ("memory-safe") {
            let m0x40 := mload(0x40)

            mstore(0, 0xcd56aadc) // getDeltaAmounts(int256,bool)
            mstore(32, sub(0, amountOut))
            mstore(64, swapForY)

            success := staticcall(gas(), pair, 28, 68, 0, 64)

            returnDataSize := returndatasize()

            switch swapForY
            case 0 {
                amountIn := mload(32)
                actualAmountOut := mload(0)
            }
            default {
                amountIn := mload(0)
                actualAmountOut := mload(32)
            }

            amountIn := add(amountIn, 1) // Add 1 wei to account for rounding errors
            actualAmountOut := sub(0, actualAmountOut) // Invert the sign

            mstore(0x40, m0x40)
        }
        _bubbleRevert(success);

        if (returnDataSize < 64) revert PairInteraction__InvalidReturnData();
    }

    /**
     * @dev Swaps tokenIn for tokenOut in a LFJ Token Mill pair.
     *
     * Requirements:
     * - The call must succeed.
     * - The pair must have code.
     * - The return data must be at least 64 bytes.
     */
    function swapTM(address pair, address recipient, uint256 amountIn, bool swapForY)
        internal
        returns (uint256 amountOut, uint256 actualAmountIn)
    {
        uint256 returnDataSize;
        uint256 success;

        assembly ("memory-safe") {
            let ptr := mload(0x40)

            mstore(ptr, 0xdc35ff77) // swap(address,int256,bool,bytes,address)
            mstore(add(ptr, 32), recipient)
            mstore(add(ptr, 64), amountIn)
            mstore(add(ptr, 96), swapForY)
            mstore(add(ptr, 128), 160)
            mstore(add(ptr, 160), 0)
            mstore(add(ptr, 192), 0)

            mstore(0x40, add(ptr, 160)) // update free memory pointer to 160 because 160:224 is 0

            success := call(gas(), pair, 0, add(ptr, 28), 196, 0, 64)
            returnDataSize := returndatasize()

            switch swapForY
            case 0 {
                actualAmountIn := mload(32)
                amountOut := mload(0)
            }
            default {
                actualAmountIn := mload(0)
                amountOut := mload(32)
            }

            amountOut := sub(0, amountOut) // Invert the sign
        }
        _bubbleRevert(success);

        if (returnDataSize < 64) revert PairInteraction__InvalidReturnData();
    }

    /**
     * @dev Returns the limit price bounds of a LFJ Token Mill V2 pair depending on the swap direction.
     *
     * Requirements:
     * - The call must succeed.
     * - The pair must have code.
     * - The return data must be at least 96 bytes.
     */
    function getSqrtLimitPriceInTMV2(address pair, bool swapForY) internal view returns (uint256 sqrtLimitPriceX96) {
        uint256 returnDataSize;
        assembly ("memory-safe") {
            let ptr := mload(0x40)

            mstore(0, 0xa0b6ea01) // getSqrtRatiosBounds()

            if staticcall(gas(), pair, 28, 4, 0, 96) { returnDataSize := returndatasize() }

            switch swapForY
            case 0 { sqrtLimitPriceX96 := mload(64) }
            default { sqrtLimitPriceX96 := mload(0) }

            mstore(0x40, ptr)
        }

        if (returnDataSize < 96) revert PairInteraction__InvalidReturnData();
    }

    /**
     * @dev Returns the amount of tokenIn required to get amountOut from a LFJ Token Mill V2 pair.
     *
     * Requirements:
     * - The call must succeed.
     * - The pair must have code.
     * - The return data must be at least 64 bytes.
     */
    function getSwapInTMV2(address pair, uint256 amountOut, bool swapForY)
        internal
        view
        returns (uint256 amountIn, uint256 actualAmountOut)
    {
        uint256 sqrtLimitPriceX96 = getSqrtLimitPriceInTMV2(pair, swapForY);

        uint256 returnDataSize;
        uint256 success;
        assembly ("memory-safe") {
            let ptr := mload(0x40)

            mstore(ptr, 0x3419341c) // getDeltaAmounts(bool,int256,uint256)
            mstore(add(ptr, 32), swapForY)
            mstore(add(ptr, 64), sub(0, amountOut))
            mstore(add(ptr, 96), sqrtLimitPriceX96)

            mstore(0x40, add(ptr, 128))

            success := staticcall(gas(), pair, add(ptr, 28), 100, 0, 64)
            returnDataSize := returndatasize()

            switch swapForY
            case 0 {
                amountIn := mload(32)
                actualAmountOut := mload(0)
            }
            default {
                amountIn := mload(0)
                actualAmountOut := mload(32)
            }

            actualAmountOut := sub(0, actualAmountOut) // Invert the sign
        }
        _bubbleRevert(success);

        if (returnDataSize < 64) revert PairInteraction__InvalidReturnData();
    }

    /**
     * @dev Swaps tokenIn for tokenOut in a LFJ Token Mill V2 pair.
     *
     * Requirements:
     * - The call must succeed.
     * - The pair must have code.
     * - The return data must be at least 64 bytes.
     */
    function swapTMV2(address pair, address recipient, uint256 amountIn, bool swapForY)
        internal
        returns (uint256 amountOut, uint256 actualAmountIn)
    {
        uint256 sqrtLimitPriceX96 = getSqrtLimitPriceInTMV2(pair, swapForY);

        uint256 returnDataSize;
        uint256 success;
        assembly ("memory-safe") {
            let ptr := mload(0x40)

            mstore(ptr, 0xabb1db2a) // swap(address,bool,int256,uint256)
            mstore(add(ptr, 32), recipient)
            mstore(add(ptr, 64), swapForY)
            mstore(add(ptr, 96), amountIn)
            mstore(add(ptr, 128), sqrtLimitPriceX96)

            mstore(0x40, add(ptr, 160))

            success := call(gas(), pair, 0, add(ptr, 28), 132, 0, 64)

            returnDataSize := returndatasize()

            switch swapForY
            case 0 {
                actualAmountIn := mload(32)
                amountOut := mload(0)
            }
            default {
                actualAmountIn := mload(0)
                amountOut := mload(32)
            }

            amountOut := sub(0, amountOut) // Invert the sign
        }
        _bubbleRevert(success);

        if (returnDataSize < 64) revert PairInteraction__InvalidReturnData();
    }

    /**
     * @dev Decodes the extra data for a Uniswap V4 pool from the route.
     * The dataOffset is the offset in the route where the extra data starts in the route.
     * The extraData must have an even length.
     * Requirements:
     * - The extra data must be formatted as follows:
     *   [fee: 3][tickSpacing: 3][nativeFlag: 1][hooks: 20][hookData length: 3][hookData: variable]
     *   (30 bytes + hookData length)
     *   - The fee is the fee of the pool in hundredths of a bip, i.e. 1e-6. If the highest bit is 1,
     *     the pool has a dynamic fee and must be exactly equal to 0x800000
     *   - The tickSpacing is the pool tick spacing.
     *   - The nativeFlag indicates if one of the tokens is native
     *     (1: tokenIn is native, 2: tokenOut is native, otherwise both are ERC20).
     *   - The hooks is the address of the hooks contract.
     *   - The hookData length is the length of the hookData in bytes (must be an even number for safety)
     *   - The hookData is the data to be passed to the hooks contract (can be empty).
     */
    function prepareDataUV4(
        bytes calldata route,
        bytes32 value,
        uint256 dataOffset,
        bool zeroForOne,
        int256 deltaAmount
    ) internal pure returns (bytes memory data) {
        unchecked {
            uint256 nativeFlag;
            uint256 extraDataOffset;
            assembly ("memory-safe") {
                extraDataOffset := add(route.offset, dataOffset)
                nativeFlag := shr(248, calldataload(add(extraDataOffset, 6)))
            }

            // 1 -> tokenIn is native, tokenOut is ERC20
            // 2 -> tokenIn is ERC20, tokenOut is native
            // any other value -> both tokens are ERC20
            address token0 = nativeFlag == 1 ? address(0) : PackedRoute.token(route, PackedRoute.tokenInId(value));
            address token1 = nativeFlag == 2 ? address(0) : PackedRoute.token(route, PackedRoute.tokenOutId(value));
            (token0, token1) = token0 < token1 ? (token0, token1) : (token1, token0);

            uint256 priceLimit = zeroForOne ? MIN_SWAP_SQRT_RATIO : MAX_SWAP_SQRT_RATIO;

            // data = [offset: 32][length: 32]
            //        {PoolKey: [token0: 32][token1: 32][fee: 32][tickSpacing: 32][hooks: 32]}
            //        {SwapParams: [zeroForOne: 32][amountSpecified: 32][priceLimit: 32]}
            //        {HookData: [offset: 32][length: 32][data: variable]}
            // Total length = 32 * 2 + 32 * 5 + 32 * 3 + 32 * 2 + hookData length = 64 + 320 + hookData length
            assembly ("memory-safe") {
                let hookDataLength := shr(232, calldataload(add(extraDataOffset, 27)))
                // Round up to the next complete word (320 + 31 = 351)
                let dataLength := shl(5, shr(5, add(hookDataLength, 351)))

                data := mload(0x40)
                mstore(0x40, add(data, add(96, dataLength))) // update free memory pointer

                mstore(data, add(64, dataLength)) // length
                mstore(add(data, 32), 0x20) // offset
                mstore(add(data, 64), dataLength) // length

                mstore(add(data, 96), token0) // PoolKey.token0
                mstore(add(data, 128), token1) // PoolKey.token1
                mstore(add(data, 160), shr(232, calldataload(extraDataOffset))) // PoolKey.fee
                mstore(add(data, 192), sar(232, calldataload(add(extraDataOffset, 3)))) // PoolKey.tickSpacing
                mstore(add(data, 224), shr(96, calldataload(add(extraDataOffset, 7)))) // PoolKey.hooks

                mstore(add(data, 256), zeroForOne) // SwapParams.zeroForOne
                mstore(add(data, 288), deltaAmount) // SwapParams.amountSpecified
                mstore(add(data, 320), priceLimit) // SwapParams.priceLimit

                mstore(add(data, 352), 288) // HookData.offset
                mstore(add(data, 384), hookDataLength) // HookData.length
                calldatacopy(add(data, 416), add(extraDataOffset, 30), hookDataLength) // HookData.data
            }
        }
    }

    /**
     * @dev Returns the amount of tokenIn required to get amountOut from a Uniswap V4 pool.
     * The function actually tries to swap token but revert before having to send any token.
     *
     * Requirements:
     * - The call must revert with `(int256 amount0Delta, int256 amount1Delta)` data.
     */
    function getSwapInUV4(
        bytes calldata route,
        bytes32 value,
        address manager,
        address dataOffset,
        bool zeroForOne,
        uint256 amountOut
    ) internal returns (uint256 amountIn) {
        (uint256 success, int256 deltaIn,) =
            callSwapUV4(route, value, manager, dataOffset, zeroForOne, amountOut.toInt256());
        if (success != 0) revert PairInteraction__InvalidState(); // Revert if the call succeeded (invalid state)
        unchecked {
            return (-deltaIn).toUint256(); // Invert the sign
        }
    }

    /**
     * @dev Swaps tokenIn for tokenOut in a Uniswap V4 pool.
     *
     * Requirements:
     * - The call must succeed.
     * - The pair must have code.
     */
    function swapUV4(
        bytes calldata route,
        bytes32 value,
        address manager,
        address dataOffset,
        bool zeroForOne,
        uint256 amountIn
    ) internal returns (uint256 amountOut, uint256 actualAmountIn) {
        (uint256 success, int256 deltaIn, int256 deltaOut) =
            callSwapUV4(route, value, manager, dataOffset, zeroForOne, -amountIn.toInt256());
        _bubbleRevert(success);
        unchecked {
            return (deltaOut.toUint256(), (-deltaIn).toUint256()); // Invert the sign of deltaIn
        }
    }

    /**
     * @dev Calls the `unlock` function of a Uniswap V4 pool.
     * This function doesn't revert on failure, it returns a success flag instead.
     * It also returns the actual amount in and out of the swap.
     * Requirements:
     * - The call must return exactly 64 bytes of data.
     */
    function callSwapUV4(
        bytes calldata route,
        bytes32 value,
        address manager,
        address dataOffset,
        bool zeroForOne,
        int256 deltaAmount
    ) private returns (uint256 success, int256 deltaIn, int256 deltaOut) {
        bytes memory data = prepareDataUV4(route, value, uint256(uint160(dataOffset)), zeroForOne, deltaAmount);

        uint256 returnDataSize;

        assembly ("memory-safe") {
            let length := mload(data)
            mstore(data, 0x48c89491) // unlock(bytes)

            success := call(gas(), manager, 0, add(data, 28), add(length, 4), data, 128)

            returnDataSize := returndatasize()

            switch zeroForOne
            case 0 {
                deltaIn := mload(add(data, 96))
                deltaOut := mload(add(data, 64))
            }
            default {
                deltaIn := mload(add(data, 64))
                deltaOut := mload(add(data, 96))
            }
        }

        if (returnDataSize != 128) revert PairInteraction__InvalidReturnData();
    }

    /**
     * @dev Callback function for Uniswap V4 swaps.
     * This function should be called after the `unlock` callback from the Uniswap V4 manager.
     * This function will then call the swap function and settle the amounts.
     *
     * Requirements:
     * - The call must succeed.
     * - The caller must be the Uniswap V4 manager.
     */
    function swapUV4Callback(bytes calldata data, address recipient, address wnative)
        internal
        returns (int256 amount0, int256 amount1)
    {
        address token0;
        address token1;

        uint256 success;
        uint256 returnDataSize;
        assembly ("memory-safe") {
            let ptr := mload(0x40)

            mstore(ptr, 0xf3cd914c) // swap((address,address,uint24,int24,address),(bool,int256,uint160),bytes)
            calldatacopy(add(ptr, 32), add(data.offset, 68), sub(data.length, 68))

            mstore(0x40, shl(5, shr(5, sub(add(ptr, data.length), 37)))) // Round up to the next complete word

            success := call(gas(), caller(), 0, add(ptr, 28), sub(data.length, 64), 0, 32)
            returnDataSize := returndatasize()

            let amounts := mload(0)
            amount0 := sar(128, amounts)
            amount1 := signextend(15, amounts)

            // Revert with amounts to decode them within getSwapInUV4
            if iszero(recipient) {
                mstore(0, 0x20)
                mstore(32, 0x40)
                mstore(64, amount0)
                mstore(96, amount1)
                revert(0, 128)
            }

            token0 := mload(add(ptr, 32))
            token1 := mload(add(ptr, 64))
        }
        _bubbleRevert(success);
        if (returnDataSize != 32) revert PairInteraction__InvalidReturnData();

        settleOrTakeUV4(token0, recipient, amount0, wnative);
        settleOrTakeUV4(token1, recipient, amount1, wnative);
    }

    /**
     * @dev Settles or takes the amount for a token in a Uniswap V4 swap.
     * If delta is negative, it means we need to settle the amount (send it to the pair).
     * If delta is positive, it means we need to take the amount (receive it from the pair).
     *
     * Requirements:
     * - The call must succeed.
     * - The caller must be the Uniswap V4 manager.
     */
    function settleOrTakeUV4(address token, address recipient, int256 delta, address wnative) internal {
        if (delta < 0) {
            uint256 success;
            // Settle the amount by calling sync, then transferring the tokens and finally calling settle
            assembly ("memory-safe") {
                delta := sub(0, delta)

                mstore(0, 0xa5841194) // sync(address)
                mstore(32, token)
                success := call(gas(), caller(), 0, 28, 64, 0, 0)
            }
            _bubbleRevert(success);

            uint256 nativeValue;
            if (token == address(0)) {
                // The token is native, unwrap wnative and send it
                TokenLib.unwrap(wnative, (nativeValue = delta.toUint256()));
            } else {
                TokenLib.transfer(token, msg.sender, delta.toUint256());
            }

            uint256 amount;
            uint256 returnDataSize;
            assembly ("memory-safe") {
                mstore(0, 0x11da60b4) // settle()
                success := call(gas(), caller(), nativeValue, 28, 4, 0, 32)
                returnDataSize := returndatasize()
                amount := mload(0)
            }
            _bubbleRevert(success);
            if (returnDataSize != 32) revert PairInteraction__InvalidReturnData();
        } else if (delta > 0) {
            // Take the amount by calling take, if token is native it will be wrapped after being received
            // (within the receive function)
            address to = token == address(0) ? address(this) : recipient;
            uint256 success;
            assembly ("memory-safe") {
                let ptr := mload(0x40)

                mstore(ptr, 0x0b0d9c09) // take(address,address,uint256)
                mstore(add(ptr, 32), token)
                mstore(add(ptr, 64), to)
                mstore(add(ptr, 96), delta)

                success := call(gas(), caller(), 0, add(ptr, 28), 100, 0, 0)

                mstore(0x40, add(ptr, 128))
            }
            _bubbleRevert(success);
            if (to != recipient) {
                // If the token is native, use this as a temporary address to receive the native token
                // then wrap it, and send it to the actual recipient
                TokenLib.transfer(wnative, recipient, delta.toUint256());
            }
        }
    }

    /**
     * @dev Returns the amount of tokenIn required to get amountOut from a ByReal pair.
     *
     * Requirements:
     * - The call must succeed.
     * - The pair must have code.
     * - The return data must be exactly 32 bytes.
     */
    function getSwapInByReal(address pair, uint256 amountIn, address tokenIn)
        internal
        view
        returns (uint256 amountOut)
    {
        uint256 returnDataSize;
        uint256 success;
        assembly ("memory-safe") {
            let m0x40 := mload(0x40)

            mstore(0, 0x1125f13f) // getAmountIn(uint256,address)
            mstore(32, amountIn)
            mstore(64, tokenIn)

            success := staticcall(gas(), pair, 28, 68, 0, 32)

            returnDataSize := returndatasize()

            amountOut := mload(0)

            mstore(0x40, m0x40)
        }
        _bubbleRevert(success);

        if (returnDataSize != 32) revert PairInteraction__InvalidReturnData();
    }

    /**
     * @dev Swaps tokenIn for tokenOut in a ByReal pair.
     * The function doesn't check that the pair has any code, `getSwapInOrOutByReal` should be called first to ensure
     * that.
     *
     * Requirements:
     * - The call must succeed.
     */
    function swapByReal(address pair, address recipient, uint256 amountIn, address tokenIn)
        internal
        returns (uint256 amountOut)
    {
        uint256 returnDataSize;
        uint256 success;
        assembly ("memory-safe") {
            let ptr := mload(0x40)

            mstore(ptr, 0xf946c2a2) // swap(address,bool,uint256,address)
            mstore(add(ptr, 32), tokenIn)
            mstore(add(ptr, 64), 1) // always givenIn = true
            mstore(add(ptr, 96), amountIn)
            mstore(add(ptr, 128), recipient)

            success := call(gas(), pair, 0, add(ptr, 28), 132, 0, 32)

            returnDataSize := returndatasize()

            amountOut := mload(0)

            mstore(0x40, add(ptr, 160))
        }
        _bubbleRevert(success);

        if (returnDataSize != 32) revert PairInteraction__InvalidReturnData();
    }

    /**
     * @dev Swaps tokenIn for tokenOut in a POE pair.
     *
     * Requirements:
     * - The call must succeed.
     * - The pair must have code.
     * - The return data must be at least 64 bytes.
     */
    function swapPOE(address pair, address recipient, uint256 amountIn, bool swapForY, address tokenIn)
        internal
        returns (uint256 actualAmountOut, uint256 actualAmountIn, uint256 expectedHash)
    {
        uint256 returnDataSize;
        uint256 success;
        assembly ("memory-safe") {
            let ptr := mload(0x40)

            mstore(ptr, 0x80c2c6ef) // swap(address,bool,uint256,bytes)
            mstore(add(ptr, 32), recipient)
            mstore(add(ptr, 64), swapForY)
            mstore(add(ptr, 96), amountIn)
            mstore(add(ptr, 128), 128)
            mstore(add(ptr, 160), 32)
            mstore(add(ptr, 192), tokenIn)

            success := call(gas(), pair, 0, add(ptr, 28), 196, ptr, 64)

            returnDataSize := returndatasize()

            mstore(add(ptr, 64), tokenIn)
            expectedHash := keccak256(ptr, 96)

            switch swapForY
            case 0 {
                actualAmountOut := mload(ptr)
                actualAmountIn := mload(add(ptr, 32))
            }
            case 1 {
                actualAmountIn := mload(ptr)
                actualAmountOut := mload(add(ptr, 32))
            }

            actualAmountOut := sub(0, actualAmountOut) // Invert the sign
        }
        _bubbleRevert(success);

        if (returnDataSize < 64) revert PairInteraction__InvalidReturnData();
    }

    /**
     * @dev Bubbles up a revert if the success flag is false.
     * It copies the return data to memory and reverts with it.
     * If there is no return data, it reverts with a `PairInteraction__CallFailed` error.
     */
    function _bubbleRevert(uint256 success) private pure {
        assembly ("memory-safe") {
            if iszero(success) {
                if returndatasize() {
                    returndatacopy(0, 0, returndatasize())
                    revert(0, returndatasize())
                }

                mstore(0, 0x824d2235) // PairInteraction__CallFailed()
                revert(0x1c, 4)
            }
        }
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

/**
 * @title SafeCast
 * @dev Library for safely casting between different integer types.
 */
library SafeCast {
    error SafeCast__Overflow();

    /**
     * @dev Converts a uint256 to int256, reverting on overflow.
     * Requirements:
     * - The input must be less than or equal to type(int256).max.
     */
    function toInt256(uint256 x) internal pure returns (int256) {
        // forge-lint: disable-next-line(unsafe-typecast)
        if (int256(x) >= 0) return int256(x);
        revert SafeCast__Overflow();
    }

    /**
     * @dev Converts an int256 to uint256, reverting on underflow.
     * Requirements:
     * - The input must be greater than or equal to 0.
     */
    function toUint256(int256 x) internal pure returns (uint256) {
        // forge-lint: disable-next-line(unsafe-typecast)
        if (x >= 0) return uint256(x);
        revert SafeCast__Overflow();
    }
}

Settings
{
  "remappings": [
    "@forge-std/contracts/=lib/forge-std/src/",
    "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
    "@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
    "ds-test/=lib/openzeppelin-contracts-upgradeable/lib/forge-std/lib/ds-test/src/",
    "erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/",
    "forge-std/=lib/forge-std/src/",
    "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 1000000
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "abi"
      ]
    }
  },
  "evmVersion": "paris",
  "viaIR": false
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"router","type":"address"},{"internalType":"address","name":"routerV2_0","type":"address"},{"internalType":"address","name":"uniswapV4Manager","type":"address"},{"internalType":"address","name":"wnative","type":"address"},{"internalType":"address","name":"protocolFeeReceiver","type":"address"},{"internalType":"uint96","name":"protocolFeeShare","type":"uint96"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"FeeAdapter__InvalidFeeReceiver","type":"error"},{"inputs":[],"name":"FeeAdapter__InvalidFrom","type":"error"},{"inputs":[],"name":"FeeAdapter__InvalidProtocolFeeReceiver","type":"error"},{"inputs":[],"name":"FeeAdapter__InvalidProtocolFeeShare","type":"error"},{"inputs":[],"name":"PackedRoute__InvalidExtraDataLength","type":"error"},{"inputs":[],"name":"PackedRoute__InvalidLength","type":"error"},{"inputs":[],"name":"PairInteraction__InvalidReturnData","type":"error"},{"inputs":[],"name":"RouterAdapter__InsufficientLBLiquidity","type":"error"},{"inputs":[],"name":"RouterAdapter__InsufficientTMLiquidity","type":"error"},{"inputs":[],"name":"RouterAdapter__InsufficientTMV2Liquidity","type":"error"},{"inputs":[],"name":"RouterAdapter__InvalidId","type":"error"},{"inputs":[],"name":"RouterAdapter__OnlyWnative","type":"error"},{"inputs":[],"name":"RouterAdapter__UnexpectedAmountIn","type":"error"},{"inputs":[],"name":"RouterAdapter__UnexpectedCallback","type":"error"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountInMax","type":"uint256"}],"name":"RouterLogic__ExceedsMaxAmountIn","type":"error"},{"inputs":[],"name":"RouterLogic__ExcessBalanceUnused","type":"error"},{"inputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"}],"name":"RouterLogic__InsufficientAmountOut","type":"error"},{"inputs":[],"name":"RouterLogic__InsufficientTokens","type":"error"},{"inputs":[],"name":"RouterLogic__InvalidAmount","type":"error"},{"inputs":[],"name":"RouterLogic__InvalidFeeData","type":"error"},{"inputs":[],"name":"RouterLogic__InvalidFeePercent","type":"error"},{"inputs":[],"name":"RouterLogic__InvalidRouter","type":"error"},{"inputs":[],"name":"RouterLogic__InvalidTokenIn","type":"error"},{"inputs":[],"name":"RouterLogic__InvalidTokenOut","type":"error"},{"inputs":[],"name":"RouterLogic__OnlyRouter","type":"error"},{"inputs":[],"name":"RouterLogic__OnlyRouterOwner","type":"error"},{"inputs":[],"name":"RouterLogic__TransferTaxNotSupported","type":"error"},{"inputs":[],"name":"RouterLogic__ZeroSwap","type":"error"},{"inputs":[],"name":"SafeCast__Overflow","type":"error"},{"inputs":[],"name":"TokenLib__ApproveFailed","type":"error"},{"inputs":[],"name":"TokenLib__BalanceOfFailed","type":"error"},{"inputs":[],"name":"TokenLib__NativeTransferFailed","type":"error"},{"inputs":[],"name":"TokenLib__TransferFailed","type":"error"},{"inputs":[],"name":"TokenLib__UnwrapFailed","type":"error"},{"inputs":[],"name":"TokenLib__WrapFailed","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"address","name":"allocatee","type":"address"},{"indexed":false,"internalType":"uint256","name":"feeAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"protocolFeeAmount","type":"uint256"}],"name":"FeeSent","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"protocolFeeReceiver","type":"address"},{"indexed":false,"internalType":"uint96","name":"protocolFeeShare","type":"uint96"}],"name":"ProtocolFeeParametersSet","type":"event"},{"stateMutability":"nonpayable","type":"fallback"},{"inputs":[],"name":"getProtocolFeeRecipient","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getProtocolFeeShare","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"feeReceiver","type":"address"},{"internalType":"uint96","name":"feeShare","type":"uint96"}],"name":"setProtocolFeeParameters","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"bytes","name":"route","type":"bytes"}],"name":"swapExactIn","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint256","name":"amountInMax","type":"uint256"},{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"bytes","name":"route","type":"bytes"}],"name":"swapExactOut","outputs":[{"internalType":"uint256","name":"totalIn","type":"uint256"},{"internalType":"uint256","name":"totalOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"sweep","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]

61010060405261dead60015534801561001757600080fd5b5060405161378038038061378083398101604081905261003691610164565b8484848484610045828261009b565b50506001600160a01b03928316608052821660c052811660a05286163b600003610082576040516320ee910f60e21b815260040160405180910390fd5b5050506001600160a01b0390921660e052506101e99050565b6001600160a01b0382166100c25760405163ec049e3760e01b815260040160405180910390fd5b612710816001600160601b031611156100ee576040516318fd95d160e11b815260040160405180910390fd5b6001600160a01b038216600160a01b6001600160601b038316908102821760005560405190815233907f6d94271a74c346e0cb5cd2db3fde24fe596fe70043c7b0b32ee206a3d01378a49060200160405180910390a35050565b80516001600160a01b038116811461015f57600080fd5b919050565b60008060008060008060c0878903121561017d57600080fd5b61018687610148565b955061019460208801610148565b94506101a260408801610148565b93506101b060608801610148565b92506101be60808801610148565b60a08801519092506001600160601b03811681146101db57600080fd5b809150509295509295509295565b60805160a05160c05160e05161353d61024360003960008181610cc801528181610e2601528181611a780152611ff601526000818160860152818160b001526103bc0152600061015a015260006122b3015261353d6000f3fe6080604052600436106100695760003560e01c8063bd08443511610043578063bd08443514610274578063c60bc203146102a9578063cb7e0007146102c9576100d7565b8063192bcad3146101d657806362c067671461021f57806372c8fc0e1461023f576100d7565b366100d7573373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016146100d5576100d57f0000000000000000000000000000000000000000000000000000000000000000346102e9565b005b3480156100e357600080fd5b50600154600090369060609061ff00811681831c6104008214801561011d57503373ffffffffffffffffffffffffffffffffffffffff8216145b156101365761012c8686610347565b93505050506101cb565b6107008214801561017c57503373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016145b1561018c5761012c8686836103af565b610900821480156101b257503373ffffffffffffffffffffffffffffffffffffffff8216145b156101c15761012c8686610422565b3660008037366000fd5b915050805190602001f35b3480156101e257600080fd5b506000547401000000000000000000000000000000000000000090046bffffffffffffffffffffffff166040519081526020015b60405180910390f35b34801561022b57600080fd5b506100d561023a3660046131ed565b6104ad565b34801561024b57600080fd5b5060005460405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610216565b34801561028057600080fd5b5061029461028f36600461322e565b6104e6565b60408051928352602083019190915201610216565b3480156102b557600080fd5b506100d56102c43660046132ff565b61073c565b3480156102d557600080fd5b506102946102e436600461322e565b610752565b600063d0e30db06000526000806004601c85875af190508060000361034257610310610a42565b6040517fdc0c8cd100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050565b606060008060006103588686610a54565b6040805160008581526020859052838352606090209152929550909350915060015561039481336000861361038d5784610aa6565b855b610aa6565b50506040805160008152602081019091529150505b92915050565b60606000806103e08686867f0000000000000000000000000000000000000000000000000000000000000000610b85565b60408051602081810152808201919091526060810183905260808101829052919350915060a001604051602081830303815290604052925050505b9392505050565b606060008060006104338686610a54565b6040805160008581526020859052838352606090209152929550909350915060015561046881336000861361038d5784610aa6565b604080517ffa483e7200000000000000000000000000000000000000000000000000000000602082015201604051602081830303815290604052935050505092915050565b6104b5610cc6565b73ffffffffffffffffffffffffffffffffffffffff8316156104dc57610342838383610aa6565b6103428282610db9565b60008061057360405180610100016040528060008152602001600081526020016000815260200160008152602001600073ffffffffffffffffffffffffffffffffffffffff168152602001600073ffffffffffffffffffffffffffffffffffffffff16815260200160008152602001600073ffffffffffffffffffffffffffffffffffffffff1681525090565b61057f85858d8d610e09565b60608501526040840181905260208401919091528183526105a491879187919061106d565b60c084015273ffffffffffffffffffffffffffffffffffffffff90811660a0840152908116608083018190528a918d16900361060d5760006127108360c001518302816105f3576105f3613378565b049050808203915061060b8d8a8560a001518461117b565b505b60006106698284604001518b8f73ffffffffffffffffffffffffffffffffffffffff16876080015173ffffffffffffffffffffffffffffffffffffffff1614610656578b610658565b305b876020015188606001518d8d6112d8565b90508b73ffffffffffffffffffffffffffffffffffffffff16836080015173ffffffffffffffffffffffffffffffffffffffff16036106e05760006127108460c001518302816106bb576106bb613378565b04905080820391506106d38d308660a001518461117b565b6106de8d8a84610aa6565b505b89811015610729576040517fc18a3e5800000000000000000000000000000000000000000000000000000000815260048101829052602481018b90526044015b60405180910390fd5b999c999b50989950505050505050505050565b610744610cc6565b61074e82826113de565b5050565b6000806107df60405180610100016040528060008152602001600081526020016000815260200160008152602001600073ffffffffffffffffffffffffffffffffffffffff168152602001600073ffffffffffffffffffffffffffffffffffffffff16815260200160008152602001600073ffffffffffffffffffffffffffffffffffffffff1681525090565b6107eb85858d8d610e09565b606085015260408401819052602084019190915281835261081091879187919061106d565b60c084015273ffffffffffffffffffffffffffffffffffffffff90811660a0840152166080820152600185013560f81c15610877576040517fe4c5b0d300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000808990508b73ffffffffffffffffffffffffffffffffffffffff16836080015173ffffffffffffffffffffffffffffffffffffffff16036108da573091508260c00151612710036127108202816108d2576108d2613378565b0490506108de565b8791505b6000806108fb838660400151876060015188602001518d8d6114f2565b915091508e73ffffffffffffffffffffffffffffffffffffffff16856080015173ffffffffffffffffffffffffffffffffffffffff16036109735760008560c00151612710038660c0015184028161095557610955613378565b049050808301925061097186608001518d8860a001518461117b565b505b8c8211156109b7576040517f20cd355100000000000000000000000000000000000000000000000000000000815260048101839052602481018e9052604401610720565b6109d38b8583886020015189604001518a606001518f8f61175f565b8d73ffffffffffffffffffffffffffffffffffffffff16856080015173ffffffffffffffffffffffffffffffffffffffff1603610a2e5760008c84039050610a218f308860a001518461117b565b610a2c8f8c8f610aa6565b505b509d999c50989a5050505050505050505050565b3d15610a52573d6000803e3d6000fd5b565b6000808060a48414610a92576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505060043593602435935060843592509050565b600080600060405163a9059cbb6000528560205284604052602060006044601c60008b5af193503d92506000519150806040525082600003610b1c57610aea610a42565b6040517f87c6ec7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8115610b2c578060011415610b46565b73ffffffffffffffffffffffffffffffffffffffff86163b155b15610b7d576040517f87c6ec7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050505050565b60008060008060008060405163f3cd914c815260448a0360448c0160208301377fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdb828c010116604052602060007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08c01601c840182335af1600051608081901d9850600f0b965092503d915088610c4f5760206000526040602052866040528560605260806000fd5b602081015194506040810151935050610c67826117b7565b80602014610ca1576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610cad8489888a6117dd565b610cb98389878a6117dd565b5050505094509492505050565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d31573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d5591906133a7565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610a52576040517f303693ad00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080600080600085875af190508060000361034257610dd7610a42565b6040517fa01b460600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008080803373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610e7d576040517f4b2fd34d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610e878888611974565b919450925090506002821015610ec9576040517f7c741b7500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601a830188840135604081901c61ff0016610f49576002831015610f19576040517f07263eb600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b939450927fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff909101908390610f83565b82600003610f83576040517f07263eb600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60028a013560601c73ffffffffffffffffffffffffffffffffffffffff891614610fd9576040517f80e4580000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff87166110138b8b6110006001896133f3565b601402919091016002013560601c919050565b73ffffffffffffffffffffffffffffffffffffffff1614611060576040517ffc37688f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050945094509450949050565b6000808084156111715786850135606081901c92506110908160501c61ffff1690565b915060006110a18260381c60ff1690565b90506002601482028a01013560601c94506110bf8260301c60ff1690565b81186110cf8360401c61ffff1690565b171515806110f1575080158015906110f157506110ed6001876133f3565b8114155b15611128576040517fee393ce900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b82158061113757506127108310155b1561116e576040517fa4c2399e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50505b9450945094915050565b80156112d25773ffffffffffffffffffffffffffffffffffffffff82166111ce576040517fae0edfe900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000805461271090611206907401000000000000000000000000000000000000000090046bffffffffffffffffffffffff1684613406565b611210919061341d565b9050600061121e82846133f3565b905080156112325761123286868684611a46565b811561125f5760005461125f908790879073ffffffffffffffffffffffffffffffffffffffff1685611a46565b8373ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff167f77c197a1ae17318af19ae49d654709f9fe13e90a7fb70e1bdd5f665f6b9bce8285856040516112c7929190918252602082015260400190565b60405180910390a350505b50505050565b6000808867ffffffffffffffff8111156112f4576112f4613349565b60405190808252806020026020018201604052801561131d578160200160208202803683370190505b509050600060018a0390508a8260008151811061133c5761133c613458565b6020026020010181815250506000805b8781101561137957601a890198870135915061136d838888878f8f88611aa0565b909c019b60010161134c565b5082828151811061138c5761138c613458565b60200260200101519350838c146113cf576040517fc810180a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50505098975050505050505050565b73ffffffffffffffffffffffffffffffffffffffff821661142b576040517fec049e3700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612710816bffffffffffffffffffffffff161115611475576040517f31fb2ba200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8216740100000000000000000000000000000000000000006bffffffffffffffffffffffff8316908102821760005560405190815233907f6d94271a74c346e0cb5cd2db3fde24fe596fe70043c7b0b32ee206a3d01378a49060200160405180910390a35050565b60006060601a86028501945060008667ffffffffffffffff81111561151957611519613349565b604051908082528060200260200182016040528015611542578160200160208202803683370190505b50905060008867ffffffffffffffff81111561156057611560613349565b604051908082528060200260200182016040528015611589578160200160208202803683370190505b509050898161159960018c6133f3565b815181106115a9576115a9613458565b6020908102919091010152896000895b80156116f7577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe68a81019a8a0101359150600060ff603084901c16905060006116058460381c60ff1690565b9050600061271061161a8660501c61ffff1690565b88858151811061162c5761162c613458565b602002602001015161163e9190613406565b611648919061341d565b90508087848151811061165d5761165d613458565b6020026020010181815161167191906133f3565b90525061167d81611be9565b6116898c8c8784611c3c565b99508987838151811061169e5761169e613458565b602002602001018181516116b29190613487565b9052506116be8a611be9565b89886116c98661349a565b955085815181106116dc576116dc613458565b602002602001018181525050808a03860195505050506115b9565b508260008151811061170b5761170b613458565b6020026020010151955085821461174e576040517fc810180a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b509192505050965096945050505050565b6000805b848110156117ab57601a87018785013580935081985050506117a38484888d8d878e888151811061179657611796613458565b6020026020010151611dd2565b600101611763565b50505050505050505050565b806117da573d156117cc573d6000803e3d6000fd5b63824d22356000526004601cfd5b50565b60008212156118c357600082600003925063a5841194600052846020526000806040601c6000335af19050611811816117b7565b600073ffffffffffffffffffffffffffffffffffffffff8616611848576118438361183b86611e6c565b925082611eab565b611856565b611856863361038f87611e6c565b6000806311da60b4600052602060006004601c86335af193503d90506000519150611880846117b7565b806020146118ba576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050506112d2565b60008213156112d257600073ffffffffffffffffffffffffffffffffffffffff8516156118f057836118f2565b305b90506000604051630b0d9c0981528660208201528260408201528460608201526000806064601c84016000335af191506080810160405250611933816117b7565b8473ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614610b7d57610b7d838661038f87611e6c565b813560f81c6014810260028101919060009084037ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01838510156119e4576040517f5d22b31500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601a810615611a385760036119fa878784611f09565b019003601a810615611a38576040517f5d22b31500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601a81049150509250925092565b3073ffffffffffffffffffffffffffffffffffffffff841603611a7357611a6e848383610aa6565b6112d2565b6112d27f000000000000000000000000000000000000000000000000000000000000000085858585611f74565b600080611ab18360401c61ffff1690565b90506000611ac28460381c60ff1690565b90506000611ad38560301c60ff1690565b905060008b8214611ae45730611ae6565b865b90506000612710611afb8860501c61ffff1690565b8b8681518110611b0d57611b0d613458565b6020026020010151611b1f9190613406565b611b29919061341d565b9050808a8581518110611b3e57611b3e613458565b60200260200101818151611b5291906133f3565b905250600080611b808e8e888e6002808d1614611b7857611b738e60601c90565b611b7a565b305b88611fb6565b91509150611b8d81611be9565b6000611b9e8f8f8c86868a8e61207d565b9050611ba981611be9565b808d8781518110611bbc57611bbc613458565b60200260200101818151611bd09190613487565b905250929092039e9d5050505050505050505050505050565b801580611c0557506fffffffffffffffffffffffffffffffff81115b156117da576040517f5e2ab1b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080611c498460601c90565b90506000611c5b8560401c61ffff1690565b905061ff0081167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008101611c9b57611c9483838761223d565b9350611dc7565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe008101611ccd57611c948383876122ac565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd008101611cff57611c948383876122e7565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc008101611d3157611c94838387612342565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb008101611d6357611c94838387612354565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa008101611d9557611c948383876123a5565b6040517f0886b18e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050949350505050565b6000611dde8360601c90565b90506000611df08460401c61ffff1690565b9050600080611e208b8b611e078960381c60ff1690565b8b600280891614611e185788611e1a565b305b8a611fb6565b90925090506000611e3260018b6133f3565b611e3f8860301c60ff1690565b14611e4a5730611e4c565b875b9050611e5d8c8c898686868a61207d565b50505050505050505050505050565b6000808212611e79575090565b6040517f20e4c52f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000632e1a7d4d600052816020526000806024601c6000875af190508060000361034257611ed7610a42565b6040517fa39163c000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600381117ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd848401013560e81c026001808216900361041b576040517faecb172600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040518460601b60201c6000528360601b6018528260601b602c528160405260008060606000808a5af1611fac573d6000803e3d6000fd5b6040525050505050565b6000806014860288016002013560601c86820361204757600189013560f81c1515600081611fe5576000611fef565b611fef83886123f6565b905061201e7f0000000000000000000000000000000000000000000000000000000000000000848a8a8a611f74565b81612029578561203e565b8061203484896123f6565b61203e91906133f3565b9550505061206f565b73ffffffffffffffffffffffffffffffffffffffff8516301461206f5761206f818686610aa6565b989297509195505050505050565b60008061208a8760601c90565b905061ff0083167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0081016120cb576120c48285888861246f565b9250612230565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0081016120fd576120c48285876124f7565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd00810161212f576120c4828587612509565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc008101612163576120c4828587898b61251b565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb008101612196576120c4828587896125f3565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa0081016121c9576120c48285878961264f565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80081016121fc576120c48286888a6126a1565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7008101611d95576120c4828587898b6126c3565b5050979650505050505050565b600080806122508660018088161461270f565b909250905061225f84826133f3565b61226b906103e5613406565b60016122778685613406565b612283906103e8613406565b61228d91906133f3565b612297919061341d565b6122a2906001613487565b9695505050505050565b60006122df7f000000000000000000000000000000000000000000000000000000000000000085846001808816146127d4565b949350505050565b600080806122fb8685600180891614612862565b9150915080600014612339576040517f48614bfa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50949350505050565b60006122df84600180861614846128e8565b600080806123688685600180891614612954565b91509150838114612339576040517f4fe1f8bd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080806123b986856001808916146129bc565b91509150838114612339576040517f049f37b000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008060006370a0823160005283602052602060006024601c885afa91503d905060005192508160000361242c5761242c610a42565b6020811015612467576040517f07e05a0c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505092915050565b60006001848116148180612483888461270f565b90925090506000612496876103e5613406565b9050806124a5846103e8613406565b6124af9190613487565b6124b98383613406565b6124c3919061341d565b9450600080856124d5578660006124d9565b6000875b915091506124e98b83838b612a82565b505050505050949350505050565b60006122df8460018086161484612ad5565b60006122df8460018086161484612b67565b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606086901b1661040017600155600080808061256789886125608b60019081161490565b8989612bf6565b92509250925080600154146125a8576040517f8430856b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8582146125e1576040517f594caabc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505061dead6001559695505050505050565b600080806126088786866001808b1614612ca1565b91509150838114612645576040517f4fe1f8bd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5095945050505050565b600080806126648786866001808b1614612d6d565b91509150838114612645576040517f049f37b000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006126ae828685612e39565b6126ba85858585612f58565b95945050505050565b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606086901b166109001760015560008080806125678988886127098c60019081161490565b89612fe4565b6000806000630902f1ac600052604060006004601c885afa1561272f57503d5b838015612765576dffffffffffffffffffffffffffff6000511693506dffffffffffffffffffffffffffff602051169250612790565b6dffffffffffffffffffffffffffff6020511693506dffffffffffffffffffffffffffff6000511692505b5060408110156127cc576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b509250929050565b6000806000604051635bdd4b7c815286602082015285604082015284606082015260808101604052602060006064601c84018b5afa9150503d9150600051925061281d816117b7565b6020821015612858576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050949350505050565b60008060008060405163abcd78306000528660205285604052604060006044601c8b5afa91503d92506000519450602051935080604052506128a3816117b7565b60408210156128de576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050935093915050565b600080600061290c8630876128fc88611e6c565b612905906134cf565b60006130c5565b9150915060a43d148215166000811461294057858015612932576004830151945061293a565b602483015194505b5061294a565b3d6000803e3d6000fd5b5050509392505050565b60008060008060405163cd56aadc6000528660000360205285604052604060006044601c8b5afa3d9350915085801561299657600051955060205194506129a1565b602051955060005194505b5060018501945083600003935080604052506128a3816117b7565b60008060006129cb868561314c565b9050600080604051633419341c815286602082015287600003604082015283606082015260808101604052604060006064601c84018c5afa9150503d91508560008114612a215760005195506020519450612a2c565b602051955060005194505b50836000039350612a3c816117b7565b6040821015612a77576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050935093915050565b600060405163022c0d9f8152846020820152836040820152826060820152608080820152600060a082015260a0810160405260008060a4601c840160008a5af1915050612ace816117b7565b5050505050565b60008060006040516353c059a06000528560205284604052604060006044601c60008b5af13d93509150858015612b10576020519450612b16565b60005194505b50604052612b23816117b7565b6040821015612b5e576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50509392505050565b60008060006040516353c059a06000528560205284604052602060006044601c60008b5af13d93509150858015612ba55760005160801c9450612bae565b60105160801c94505b50604052612bbb816117b7565b6020821015612b5e576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000806000806000612c138a8a8a612c0d8b611e6c565b8a6130c5565b91509150612c20826117b7565b604081018690526060812092503d888015612c45578251955060208301519650612c51565b82519650602083015195505b508560000395506040811015612c93576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050955095509592505050565b60008060008060405163dc35ff77815287602082015286604082015285606082015260a06080820152600060a0820152600060c082015260a081016040526040600060c4601c840160008d5af19150503d91508460008114612d0c5760005193506020519450612d17565b602051935060005194505b50836000039350612d27816117b7565b6040821015612d62576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505094509492505050565b6000806000612d7c878561314c565b905060008060405163abb1db2a815288602082015286604082015287606082015283608082015260a08101604052604060006084601c840160008e5af19150503d91508560008114612dd75760005194506020519550612de2565b602051945060005195505b50846000039450612df2816117b7565b6040821015612e2d576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50505094509492505050565b6000806000612e5d565b816040526000602060406044601c6000865af19392505050565b60405163095ea7b360005285602052612e768588612e43565b93508360016040511416612ea857833d883b151710612ea857612e9a600088612e43565b50612ea58588612e43565b93505b3d92506040519150806040525082600003612ef757612ec5610a42565b6040517f2cd1784000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8115612f07578060011415612f21565b73ffffffffffffffffffffffffffffffffffffffff86163b155b15610b7d576040517f2cd1784000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080600060405163f946c2a2815284602082015260016040820152856060820152866080820152602060006084601c840160008c5af191503d9250600051935060a0810160405250612faa816117b7565b81602014612858576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008060008060006040516380c2c6ef8152896020820152876040820152886060820152608080820152602060a08201528660c082015260408160c4601c840160008f5af191503d9250866040820152606081209350876000811461305057600181146130605761306c565b815196506020820151955061306c565b81519550602082015196505b505084600003945061307d816117b7565b60408210156130b8576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050955095509592505050565b6000806000856130e95773fffd8963efd1fc6a506488495d951d5263988d256130f0565b6401000276a45b9050604051915063128acb08825286602083015285604083015284606083015280608083015260a080830152602060c08301528360e0830152610100820160405260448260e4601c850160008c5af19250509550959350505050565b60008060405163a0b6ea01600052606060006004601c885afa1561316e573d91505b83801561317f576000519350613185565b60405193505b5060405260608110156131c4576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5092915050565b73ffffffffffffffffffffffffffffffffffffffff811681146117da57600080fd5b60008060006060848603121561320257600080fd5b833561320d816131cb565b9250602084013561321d816131cb565b929592945050506040919091013590565b60008060008060008060008060e0898b03121561324a57600080fd5b8835613255816131cb565b97506020890135613265816131cb565b965060408901359550606089013594506080890135613283816131cb565b935060a0890135613293816131cb565b925060c089013567ffffffffffffffff8111156132af57600080fd5b8901601f81018b136132c057600080fd5b803567ffffffffffffffff8111156132d757600080fd5b8b60208284010111156132e957600080fd5b989b979a50959850939692959194602001935050565b6000806040838503121561331257600080fd5b823561331d816131cb565b915060208301356bffffffffffffffffffffffff8116811461333e57600080fd5b809150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000602082840312156133b957600080fd5b815161041b816131cb565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b818103818111156103a9576103a96133c4565b80820281158282048414176103a9576103a96133c4565b600082613453577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b808201808211156103a9576103a96133c4565b6000816134a9576134a96133c4565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b60007f80000000000000000000000000000000000000000000000000000000000000008203613500576135006133c4565b506000039056fea26469706673582212208b73127399dde8946da9be162faea1fd4faab80441131109f005eacf4d92804964736f6c634300081a003300000000000000000000000045a62b090df48243f12a21897e7ed91863e2c86b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000188d586ddcf52439676ca21a244753fa19f9ea8e0000000000000000000000003bd359c1119da7da1d913d1c4d2b7c461115433a000000000000000000000000da7305883a97a97f4d4851f7f0e6fb041da65e5400000000000000000000000000000000000000000000000000000000000005dc

Deployed Bytecode

0x6080604052600436106100695760003560e01c8063bd08443511610043578063bd08443514610274578063c60bc203146102a9578063cb7e0007146102c9576100d7565b8063192bcad3146101d657806362c067671461021f57806372c8fc0e1461023f576100d7565b366100d7573373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000003bd359c1119da7da1d913d1c4d2b7c461115433a16146100d5576100d57f0000000000000000000000003bd359c1119da7da1d913d1c4d2b7c461115433a346102e9565b005b3480156100e357600080fd5b50600154600090369060609061ff00811681831c6104008214801561011d57503373ffffffffffffffffffffffffffffffffffffffff8216145b156101365761012c8686610347565b93505050506101cb565b6107008214801561017c57503373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000188d586ddcf52439676ca21a244753fa19f9ea8e16145b1561018c5761012c8686836103af565b610900821480156101b257503373ffffffffffffffffffffffffffffffffffffffff8216145b156101c15761012c8686610422565b3660008037366000fd5b915050805190602001f35b3480156101e257600080fd5b506000547401000000000000000000000000000000000000000090046bffffffffffffffffffffffff166040519081526020015b60405180910390f35b34801561022b57600080fd5b506100d561023a3660046131ed565b6104ad565b34801561024b57600080fd5b5060005460405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610216565b34801561028057600080fd5b5061029461028f36600461322e565b6104e6565b60408051928352602083019190915201610216565b3480156102b557600080fd5b506100d56102c43660046132ff565b61073c565b3480156102d557600080fd5b506102946102e436600461322e565b610752565b600063d0e30db06000526000806004601c85875af190508060000361034257610310610a42565b6040517fdc0c8cd100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050565b606060008060006103588686610a54565b6040805160008581526020859052838352606090209152929550909350915060015561039481336000861361038d5784610aa6565b855b610aa6565b50506040805160008152602081019091529150505b92915050565b60606000806103e08686867f0000000000000000000000003bd359c1119da7da1d913d1c4d2b7c461115433a610b85565b60408051602081810152808201919091526060810183905260808101829052919350915060a001604051602081830303815290604052925050505b9392505050565b606060008060006104338686610a54565b6040805160008581526020859052838352606090209152929550909350915060015561046881336000861361038d5784610aa6565b604080517ffa483e7200000000000000000000000000000000000000000000000000000000602082015201604051602081830303815290604052935050505092915050565b6104b5610cc6565b73ffffffffffffffffffffffffffffffffffffffff8316156104dc57610342838383610aa6565b6103428282610db9565b60008061057360405180610100016040528060008152602001600081526020016000815260200160008152602001600073ffffffffffffffffffffffffffffffffffffffff168152602001600073ffffffffffffffffffffffffffffffffffffffff16815260200160008152602001600073ffffffffffffffffffffffffffffffffffffffff1681525090565b61057f85858d8d610e09565b60608501526040840181905260208401919091528183526105a491879187919061106d565b60c084015273ffffffffffffffffffffffffffffffffffffffff90811660a0840152908116608083018190528a918d16900361060d5760006127108360c001518302816105f3576105f3613378565b049050808203915061060b8d8a8560a001518461117b565b505b60006106698284604001518b8f73ffffffffffffffffffffffffffffffffffffffff16876080015173ffffffffffffffffffffffffffffffffffffffff1614610656578b610658565b305b876020015188606001518d8d6112d8565b90508b73ffffffffffffffffffffffffffffffffffffffff16836080015173ffffffffffffffffffffffffffffffffffffffff16036106e05760006127108460c001518302816106bb576106bb613378565b04905080820391506106d38d308660a001518461117b565b6106de8d8a84610aa6565b505b89811015610729576040517fc18a3e5800000000000000000000000000000000000000000000000000000000815260048101829052602481018b90526044015b60405180910390fd5b999c999b50989950505050505050505050565b610744610cc6565b61074e82826113de565b5050565b6000806107df60405180610100016040528060008152602001600081526020016000815260200160008152602001600073ffffffffffffffffffffffffffffffffffffffff168152602001600073ffffffffffffffffffffffffffffffffffffffff16815260200160008152602001600073ffffffffffffffffffffffffffffffffffffffff1681525090565b6107eb85858d8d610e09565b606085015260408401819052602084019190915281835261081091879187919061106d565b60c084015273ffffffffffffffffffffffffffffffffffffffff90811660a0840152166080820152600185013560f81c15610877576040517fe4c5b0d300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000808990508b73ffffffffffffffffffffffffffffffffffffffff16836080015173ffffffffffffffffffffffffffffffffffffffff16036108da573091508260c00151612710036127108202816108d2576108d2613378565b0490506108de565b8791505b6000806108fb838660400151876060015188602001518d8d6114f2565b915091508e73ffffffffffffffffffffffffffffffffffffffff16856080015173ffffffffffffffffffffffffffffffffffffffff16036109735760008560c00151612710038660c0015184028161095557610955613378565b049050808301925061097186608001518d8860a001518461117b565b505b8c8211156109b7576040517f20cd355100000000000000000000000000000000000000000000000000000000815260048101839052602481018e9052604401610720565b6109d38b8583886020015189604001518a606001518f8f61175f565b8d73ffffffffffffffffffffffffffffffffffffffff16856080015173ffffffffffffffffffffffffffffffffffffffff1603610a2e5760008c84039050610a218f308860a001518461117b565b610a2c8f8c8f610aa6565b505b509d999c50989a5050505050505050505050565b3d15610a52573d6000803e3d6000fd5b565b6000808060a48414610a92576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505060043593602435935060843592509050565b600080600060405163a9059cbb6000528560205284604052602060006044601c60008b5af193503d92506000519150806040525082600003610b1c57610aea610a42565b6040517f87c6ec7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8115610b2c578060011415610b46565b73ffffffffffffffffffffffffffffffffffffffff86163b155b15610b7d576040517f87c6ec7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050505050565b60008060008060008060405163f3cd914c815260448a0360448c0160208301377fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdb828c010116604052602060007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08c01601c840182335af1600051608081901d9850600f0b965092503d915088610c4f5760206000526040602052866040528560605260806000fd5b602081015194506040810151935050610c67826117b7565b80602014610ca1576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610cad8489888a6117dd565b610cb98389878a6117dd565b5050505094509492505050565b7f00000000000000000000000045a62b090df48243f12a21897e7ed91863e2c86b73ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d31573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d5591906133a7565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610a52576040517f303693ad00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080600080600085875af190508060000361034257610dd7610a42565b6040517fa01b460600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008080803373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000045a62b090df48243f12a21897e7ed91863e2c86b1614610e7d576040517f4b2fd34d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610e878888611974565b919450925090506002821015610ec9576040517f7c741b7500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601a830188840135604081901c61ff0016610f49576002831015610f19576040517f07263eb600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b939450927fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff909101908390610f83565b82600003610f83576040517f07263eb600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60028a013560601c73ffffffffffffffffffffffffffffffffffffffff891614610fd9576040517f80e4580000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff87166110138b8b6110006001896133f3565b601402919091016002013560601c919050565b73ffffffffffffffffffffffffffffffffffffffff1614611060576040517ffc37688f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050945094509450949050565b6000808084156111715786850135606081901c92506110908160501c61ffff1690565b915060006110a18260381c60ff1690565b90506002601482028a01013560601c94506110bf8260301c60ff1690565b81186110cf8360401c61ffff1690565b171515806110f1575080158015906110f157506110ed6001876133f3565b8114155b15611128576040517fee393ce900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b82158061113757506127108310155b1561116e576040517fa4c2399e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50505b9450945094915050565b80156112d25773ffffffffffffffffffffffffffffffffffffffff82166111ce576040517fae0edfe900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000805461271090611206907401000000000000000000000000000000000000000090046bffffffffffffffffffffffff1684613406565b611210919061341d565b9050600061121e82846133f3565b905080156112325761123286868684611a46565b811561125f5760005461125f908790879073ffffffffffffffffffffffffffffffffffffffff1685611a46565b8373ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff167f77c197a1ae17318af19ae49d654709f9fe13e90a7fb70e1bdd5f665f6b9bce8285856040516112c7929190918252602082015260400190565b60405180910390a350505b50505050565b6000808867ffffffffffffffff8111156112f4576112f4613349565b60405190808252806020026020018201604052801561131d578160200160208202803683370190505b509050600060018a0390508a8260008151811061133c5761133c613458565b6020026020010181815250506000805b8781101561137957601a890198870135915061136d838888878f8f88611aa0565b909c019b60010161134c565b5082828151811061138c5761138c613458565b60200260200101519350838c146113cf576040517fc810180a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50505098975050505050505050565b73ffffffffffffffffffffffffffffffffffffffff821661142b576040517fec049e3700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612710816bffffffffffffffffffffffff161115611475576040517f31fb2ba200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8216740100000000000000000000000000000000000000006bffffffffffffffffffffffff8316908102821760005560405190815233907f6d94271a74c346e0cb5cd2db3fde24fe596fe70043c7b0b32ee206a3d01378a49060200160405180910390a35050565b60006060601a86028501945060008667ffffffffffffffff81111561151957611519613349565b604051908082528060200260200182016040528015611542578160200160208202803683370190505b50905060008867ffffffffffffffff81111561156057611560613349565b604051908082528060200260200182016040528015611589578160200160208202803683370190505b509050898161159960018c6133f3565b815181106115a9576115a9613458565b6020908102919091010152896000895b80156116f7577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe68a81019a8a0101359150600060ff603084901c16905060006116058460381c60ff1690565b9050600061271061161a8660501c61ffff1690565b88858151811061162c5761162c613458565b602002602001015161163e9190613406565b611648919061341d565b90508087848151811061165d5761165d613458565b6020026020010181815161167191906133f3565b90525061167d81611be9565b6116898c8c8784611c3c565b99508987838151811061169e5761169e613458565b602002602001018181516116b29190613487565b9052506116be8a611be9565b89886116c98661349a565b955085815181106116dc576116dc613458565b602002602001018181525050808a03860195505050506115b9565b508260008151811061170b5761170b613458565b6020026020010151955085821461174e576040517fc810180a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b509192505050965096945050505050565b6000805b848110156117ab57601a87018785013580935081985050506117a38484888d8d878e888151811061179657611796613458565b6020026020010151611dd2565b600101611763565b50505050505050505050565b806117da573d156117cc573d6000803e3d6000fd5b63824d22356000526004601cfd5b50565b60008212156118c357600082600003925063a5841194600052846020526000806040601c6000335af19050611811816117b7565b600073ffffffffffffffffffffffffffffffffffffffff8616611848576118438361183b86611e6c565b925082611eab565b611856565b611856863361038f87611e6c565b6000806311da60b4600052602060006004601c86335af193503d90506000519150611880846117b7565b806020146118ba576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050506112d2565b60008213156112d257600073ffffffffffffffffffffffffffffffffffffffff8516156118f057836118f2565b305b90506000604051630b0d9c0981528660208201528260408201528460608201526000806064601c84016000335af191506080810160405250611933816117b7565b8473ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614610b7d57610b7d838661038f87611e6c565b813560f81c6014810260028101919060009084037ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01838510156119e4576040517f5d22b31500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601a810615611a385760036119fa878784611f09565b019003601a810615611a38576040517f5d22b31500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601a81049150509250925092565b3073ffffffffffffffffffffffffffffffffffffffff841603611a7357611a6e848383610aa6565b6112d2565b6112d27f00000000000000000000000045a62b090df48243f12a21897e7ed91863e2c86b85858585611f74565b600080611ab18360401c61ffff1690565b90506000611ac28460381c60ff1690565b90506000611ad38560301c60ff1690565b905060008b8214611ae45730611ae6565b865b90506000612710611afb8860501c61ffff1690565b8b8681518110611b0d57611b0d613458565b6020026020010151611b1f9190613406565b611b29919061341d565b9050808a8581518110611b3e57611b3e613458565b60200260200101818151611b5291906133f3565b905250600080611b808e8e888e6002808d1614611b7857611b738e60601c90565b611b7a565b305b88611fb6565b91509150611b8d81611be9565b6000611b9e8f8f8c86868a8e61207d565b9050611ba981611be9565b808d8781518110611bbc57611bbc613458565b60200260200101818151611bd09190613487565b905250929092039e9d5050505050505050505050505050565b801580611c0557506fffffffffffffffffffffffffffffffff81115b156117da576040517f5e2ab1b600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080611c498460601c90565b90506000611c5b8560401c61ffff1690565b905061ff0081167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008101611c9b57611c9483838761223d565b9350611dc7565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe008101611ccd57611c948383876122ac565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd008101611cff57611c948383876122e7565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc008101611d3157611c94838387612342565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb008101611d6357611c94838387612354565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa008101611d9557611c948383876123a5565b6040517f0886b18e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050949350505050565b6000611dde8360601c90565b90506000611df08460401c61ffff1690565b9050600080611e208b8b611e078960381c60ff1690565b8b600280891614611e185788611e1a565b305b8a611fb6565b90925090506000611e3260018b6133f3565b611e3f8860301c60ff1690565b14611e4a5730611e4c565b875b9050611e5d8c8c898686868a61207d565b50505050505050505050505050565b6000808212611e79575090565b6040517f20e4c52f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000632e1a7d4d600052816020526000806024601c6000875af190508060000361034257611ed7610a42565b6040517fa39163c000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600381117ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd848401013560e81c026001808216900361041b576040517faecb172600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040518460601b60201c6000528360601b6018528260601b602c528160405260008060606000808a5af1611fac573d6000803e3d6000fd5b6040525050505050565b6000806014860288016002013560601c86820361204757600189013560f81c1515600081611fe5576000611fef565b611fef83886123f6565b905061201e7f00000000000000000000000045a62b090df48243f12a21897e7ed91863e2c86b848a8a8a611f74565b81612029578561203e565b8061203484896123f6565b61203e91906133f3565b9550505061206f565b73ffffffffffffffffffffffffffffffffffffffff8516301461206f5761206f818686610aa6565b989297509195505050505050565b60008061208a8760601c90565b905061ff0083167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0081016120cb576120c48285888861246f565b9250612230565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0081016120fd576120c48285876124f7565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd00810161212f576120c4828587612509565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc008101612163576120c4828587898b61251b565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb008101612196576120c4828587896125f3565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa0081016121c9576120c48285878961264f565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80081016121fc576120c48286888a6126a1565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7008101611d95576120c4828587898b6126c3565b5050979650505050505050565b600080806122508660018088161461270f565b909250905061225f84826133f3565b61226b906103e5613406565b60016122778685613406565b612283906103e8613406565b61228d91906133f3565b612297919061341d565b6122a2906001613487565b9695505050505050565b60006122df7f000000000000000000000000000000000000000000000000000000000000000085846001808816146127d4565b949350505050565b600080806122fb8685600180891614612862565b9150915080600014612339576040517f48614bfa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50949350505050565b60006122df84600180861614846128e8565b600080806123688685600180891614612954565b91509150838114612339576040517f4fe1f8bd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080806123b986856001808916146129bc565b91509150838114612339576040517f049f37b000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008060006370a0823160005283602052602060006024601c885afa91503d905060005192508160000361242c5761242c610a42565b6020811015612467576040517f07e05a0c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505092915050565b60006001848116148180612483888461270f565b90925090506000612496876103e5613406565b9050806124a5846103e8613406565b6124af9190613487565b6124b98383613406565b6124c3919061341d565b9450600080856124d5578660006124d9565b6000875b915091506124e98b83838b612a82565b505050505050949350505050565b60006122df8460018086161484612ad5565b60006122df8460018086161484612b67565b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606086901b1661040017600155600080808061256789886125608b60019081161490565b8989612bf6565b92509250925080600154146125a8576040517f8430856b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8582146125e1576040517f594caabc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505061dead6001559695505050505050565b600080806126088786866001808b1614612ca1565b91509150838114612645576040517f4fe1f8bd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5095945050505050565b600080806126648786866001808b1614612d6d565b91509150838114612645576040517f049f37b000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006126ae828685612e39565b6126ba85858585612f58565b95945050505050565b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606086901b166109001760015560008080806125678988886127098c60019081161490565b89612fe4565b6000806000630902f1ac600052604060006004601c885afa1561272f57503d5b838015612765576dffffffffffffffffffffffffffff6000511693506dffffffffffffffffffffffffffff602051169250612790565b6dffffffffffffffffffffffffffff6020511693506dffffffffffffffffffffffffffff6000511692505b5060408110156127cc576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b509250929050565b6000806000604051635bdd4b7c815286602082015285604082015284606082015260808101604052602060006064601c84018b5afa9150503d9150600051925061281d816117b7565b6020821015612858576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050949350505050565b60008060008060405163abcd78306000528660205285604052604060006044601c8b5afa91503d92506000519450602051935080604052506128a3816117b7565b60408210156128de576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050935093915050565b600080600061290c8630876128fc88611e6c565b612905906134cf565b60006130c5565b9150915060a43d148215166000811461294057858015612932576004830151945061293a565b602483015194505b5061294a565b3d6000803e3d6000fd5b5050509392505050565b60008060008060405163cd56aadc6000528660000360205285604052604060006044601c8b5afa3d9350915085801561299657600051955060205194506129a1565b602051955060005194505b5060018501945083600003935080604052506128a3816117b7565b60008060006129cb868561314c565b9050600080604051633419341c815286602082015287600003604082015283606082015260808101604052604060006064601c84018c5afa9150503d91508560008114612a215760005195506020519450612a2c565b602051955060005194505b50836000039350612a3c816117b7565b6040821015612a77576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050935093915050565b600060405163022c0d9f8152846020820152836040820152826060820152608080820152600060a082015260a0810160405260008060a4601c840160008a5af1915050612ace816117b7565b5050505050565b60008060006040516353c059a06000528560205284604052604060006044601c60008b5af13d93509150858015612b10576020519450612b16565b60005194505b50604052612b23816117b7565b6040821015612b5e576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50509392505050565b60008060006040516353c059a06000528560205284604052602060006044601c60008b5af13d93509150858015612ba55760005160801c9450612bae565b60105160801c94505b50604052612bbb816117b7565b6020821015612b5e576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000806000806000612c138a8a8a612c0d8b611e6c565b8a6130c5565b91509150612c20826117b7565b604081018690526060812092503d888015612c45578251955060208301519650612c51565b82519650602083015195505b508560000395506040811015612c93576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050955095509592505050565b60008060008060405163dc35ff77815287602082015286604082015285606082015260a06080820152600060a0820152600060c082015260a081016040526040600060c4601c840160008d5af19150503d91508460008114612d0c5760005193506020519450612d17565b602051935060005194505b50836000039350612d27816117b7565b6040821015612d62576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505094509492505050565b6000806000612d7c878561314c565b905060008060405163abb1db2a815288602082015286604082015287606082015283608082015260a08101604052604060006084601c840160008e5af19150503d91508560008114612dd75760005194506020519550612de2565b602051945060005195505b50846000039450612df2816117b7565b6040821015612e2d576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50505094509492505050565b6000806000612e5d565b816040526000602060406044601c6000865af19392505050565b60405163095ea7b360005285602052612e768588612e43565b93508360016040511416612ea857833d883b151710612ea857612e9a600088612e43565b50612ea58588612e43565b93505b3d92506040519150806040525082600003612ef757612ec5610a42565b6040517f2cd1784000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8115612f07578060011415612f21565b73ffffffffffffffffffffffffffffffffffffffff86163b155b15610b7d576040517f2cd1784000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080600060405163f946c2a2815284602082015260016040820152856060820152866080820152602060006084601c840160008c5af191503d9250600051935060a0810160405250612faa816117b7565b81602014612858576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008060008060006040516380c2c6ef8152896020820152876040820152886060820152608080820152602060a08201528660c082015260408160c4601c840160008f5af191503d9250866040820152606081209350876000811461305057600181146130605761306c565b815196506020820151955061306c565b81519550602082015196505b505084600003945061307d816117b7565b60408210156130b8576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050955095509592505050565b6000806000856130e95773fffd8963efd1fc6a506488495d951d5263988d256130f0565b6401000276a45b9050604051915063128acb08825286602083015285604083015284606083015280608083015260a080830152602060c08301528360e0830152610100820160405260448260e4601c850160008c5af19250509550959350505050565b60008060405163a0b6ea01600052606060006004601c885afa1561316e573d91505b83801561317f576000519350613185565b60405193505b5060405260608110156131c4576040517fc0412a7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5092915050565b73ffffffffffffffffffffffffffffffffffffffff811681146117da57600080fd5b60008060006060848603121561320257600080fd5b833561320d816131cb565b9250602084013561321d816131cb565b929592945050506040919091013590565b60008060008060008060008060e0898b03121561324a57600080fd5b8835613255816131cb565b97506020890135613265816131cb565b965060408901359550606089013594506080890135613283816131cb565b935060a0890135613293816131cb565b925060c089013567ffffffffffffffff8111156132af57600080fd5b8901601f81018b136132c057600080fd5b803567ffffffffffffffff8111156132d757600080fd5b8b60208284010111156132e957600080fd5b989b979a50959850939692959194602001935050565b6000806040838503121561331257600080fd5b823561331d816131cb565b915060208301356bffffffffffffffffffffffff8116811461333e57600080fd5b809150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000602082840312156133b957600080fd5b815161041b816131cb565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b818103818111156103a9576103a96133c4565b80820281158282048414176103a9576103a96133c4565b600082613453577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b808201808211156103a9576103a96133c4565b6000816134a9576134a96133c4565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b60007f80000000000000000000000000000000000000000000000000000000000000008203613500576135006133c4565b506000039056fea26469706673582212208b73127399dde8946da9be162faea1fd4faab80441131109f005eacf4d92804964736f6c634300081a0033

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

00000000000000000000000045a62b090df48243f12a21897e7ed91863e2c86b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000188d586ddcf52439676ca21a244753fa19f9ea8e0000000000000000000000003bd359c1119da7da1d913d1c4d2b7c461115433a000000000000000000000000da7305883a97a97f4d4851f7f0e6fb041da65e5400000000000000000000000000000000000000000000000000000000000005dc

-----Decoded View---------------
Arg [0] : router (address): 0x45A62B090DF48243F12A21897e7ed91863E2c86b
Arg [1] : routerV2_0 (address): 0x0000000000000000000000000000000000000000
Arg [2] : uniswapV4Manager (address): 0x188d586Ddcf52439676Ca21A244753fA19F9Ea8e
Arg [3] : wnative (address): 0x3bd359C1119dA7Da1D913D1C4D2B7c461115433A
Arg [4] : protocolFeeReceiver (address): 0xDA7305883a97a97F4d4851f7F0e6fB041da65e54
Arg [5] : protocolFeeShare (uint96): 1500

-----Encoded View---------------
6 Constructor Arguments found :
Arg [0] : 00000000000000000000000045a62b090df48243f12a21897e7ed91863e2c86b
Arg [1] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [2] : 000000000000000000000000188d586ddcf52439676ca21a244753fa19f9ea8e
Arg [3] : 0000000000000000000000003bd359c1119da7da1d913d1c4d2b7c461115433a
Arg [4] : 000000000000000000000000da7305883a97a97f4d4851f7f0e6fb041da65e54
Arg [5] : 00000000000000000000000000000000000000000000000000000000000005dc


Deployed Bytecode Sourcemap

637:18921:4:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1535:10:3;:21;1549:7;1535:21;;1531:60;;1558:33;1572:7;1581:9;1558:13;:33::i;:::-;637:18921:4;;;;;;;;;;;-1:-1:-1;1966:13:3;;637:18921:4;;;;1919:12:3;;911:6:7;1884:15;;2126:18:3;;;1358:14:7;2161:25:3;;:50;;;;-1:-1:-1;2190:10:3;:21;;;;2161:50;2157:369;;;2234:28;2257:4;;2234:22;:28::i;:::-;2227:35;;;;;;;2157:369;1555:14:7;2283:25:3;;:61;;;;-1:-1:-1;2312:10:3;:32;2326:18;2312:32;;2283:61;2279:247;;;2367:39;2392:4;;2398:7;2367:24;:39::i;2279:247::-;1668:14:7;2427:18:3;;:43;;;;-1:-1:-1;2449:10:3;:21;;;;2427:43;2423:103;;;2493:22;2510:4;;2493:16;:22::i;2423:103::-;2594:14;2591:1;;2575:34;2594:14;2591:1;2622:25;1871:792;;;;637:18921:4;;;;;;1216:113:2;;;;;;;;;;-1:-1:-1;1279:7:2;1305:17;;;;;;1216:113;;160:25:13;;;148:2;133:18;1216:113:2;;;;;;;;7448:214:4;;;;;;;;;;-1:-1:-1;7448:214:4;;;;;:::i;:::-;;:::i;1029:121:2:-;;;;;;;;;;-1:-1:-1;1096:7:2;1122:21;1029:121;;1122:21;;;;1014:74:13;;1002:2;987:18;1029:121:2;868:226:13;2571:1634:4;;;;;;;;;;-1:-1:-1;2571:1634:4;;;;;:::i;:::-;;:::i;:::-;;;;2666:25:13;;;2722:2;2707:18;;2700:34;;;;2639:18;2571:1634:4;2492:248:13;1585:179:2;;;;;;;;;;-1:-1:-1;1585:179:2;;;;;:::i;:::-;;:::i;5267:2023:4:-;;;;;;;;;;-1:-1:-1;5267:2023:4;;;;;:::i;:::-;;:::i;2680:365:12:-;2746:15;2821:10;2818:1;2811:21;2909:1;2906;2903;2899:2;2891:6;2882:7;2875:5;2870:41;2859:52;;2935:7;2946:1;2935:12;2931:108;;2963:22;:20;:22::i;:::-;3006;;;;;;;;;;;;;;2931:108;2736:309;2680:365;;:::o;9432:423:3:-;9503:12;9528:19;9549;9570:13;9587:43;9625:4;;9587:37;:43::i;:::-;9980:4:9;9974:11;;9900:12;9999:23;;;10042:2;10035:24;;;10072:17;;;10124:2;10111:16;;10141:17;;9527:103:3;;-1:-1:-1;9527:103:3;;-1:-1:-1;9527:103:3;-1:-1:-1;9641:13:3;:74;9726:93;9744:5;9751:10;9786:1;9771:16;;:46;;9805:12;9726:17;:93::i;9771:46::-;9790:12;9771:46;9726:17;:93::i;:::-;-1:-1:-1;;9836:12:3;;;9846:1;9836:12;;;;;;;;;-1:-1:-1;;9432:423:3;;;;;:::o;13345:268::-;13437:12;13462:13;13477;13494:57;13526:4;;13532:9;13543:7;13494:31;:57::i;:::-;13585:4;13568:38;;13579:4;13568:38;;;3615:36:13;3667:18;;;3660:45;;;;3721:18;;;3714:34;;;3764:18;;;3757:34;;;13461:90:3;;-1:-1:-1;13461:90:3;-1:-1:-1;3587:19:13;;13568:38:3;;;;;;;;;;;;13561:45;;;;13345:268;;;;;;:::o;15331:460::-;15396:12;15421:19;15442;15463:13;15480:43;15518:4;;15480:37;:43::i;:::-;9980:4:9;9974:11;;9900:12;9999:23;;;10042:2;10035:24;;;10072:17;;;10124:2;10111:16;;10141:17;;15420:103:3;;-1:-1:-1;15420:103:3;;-1:-1:-1;15420:103:3;-1:-1:-1;15534:13:3;:74;15619:93;15637:5;15644:10;15679:1;15664:16;;:46;;15698:12;9726:17;:93::i;15619:::-;15730:54;;;15741:42;15730:54;;;3946:98:13;3919:18;15730:54:3;;;;;;;;;;;;15723:61;;;;;15331:460;;;;:::o;7448:214:4:-;7534:14;:12;:14::i;:::-;7559:19;;;;:96;;7619:36;7637:5;7644:2;7648:6;7619:17;:36::i;7559:96::-;7581:35;7605:2;7609:6;7581:23;:35::i;2571:1634::-;2804:7;2813;2832:32;-1:-1:-1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2832:32:4;2937:41;2953:5;;2960:7;2969:8;2937:15;:41::i;:::-;2919:14;;;2874:104;2902:15;;;2874:104;;;2890:10;;;2874:104;;;;;;;3046:53;;3061:5;;;;2874:104;3046:14;:53::i;:::-;3025:17;;;2989:110;;;;;3007:16;;;2989:110;;;;2990:15;;;2989:110;;;3139:8;;3161:26;;;;3157:284;;3231:17;741:6:2;3273::4;:17;;;3252:18;:38;3251:46;;;;;:::i;:::-;;3231:66;;3337:9;3315:31;;;;3364:52;3373:7;3382:4;3388:6;:16;;;3406:9;3364:8;:52::i;:::-;3203:228;3157:284;3451:17;3471:234;3497:18;3529:6;:15;;;3558:4;3595:8;3576:27;;:6;:15;;;:27;;;:48;;3622:2;3576:48;;;3614:4;3576:48;3638:6;:10;;;3662:6;:14;;;3690:5;;3471:12;:234::i;:::-;3451:254;;3739:8;3720:27;;:6;:15;;;:27;;;3716:337;;3791:17;741:6:2;3824::4;:17;;;3812:9;:29;3811:37;;;;;:::i;:::-;;3791:57;;3879:9;3866:22;;;;3906:62;3915:8;3933:4;3940:6;:16;;;3958:9;3906:8;:62::i;:::-;3986:42;4004:8;4014:2;4018:9;3986:17;:42::i;:::-;3763:280;3716:337;4079:12;4067:9;:24;4063:96;;;4100:59;;;;;;;;2666:25:13;;;2707:18;;;2700:34;;;2639:18;;4100:59:4;;;;;;;;4063:96;4178:8;;4188:9;;-1:-1:-1;2571:1634:4;;-1:-1:-1;;;;;;;;;;2571:1634:4:o;1585:179:2:-;1685:14;:12;:14::i;:::-;1709:48;1735:11;1748:8;1709:25;:48::i;:::-;1585:179;;:::o;5267:2023:4:-;5501:15;5518:16;5546:32;-1:-1:-1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;5546:32:4;5651:41;5667:5;;5674:7;5683:8;5651:15;:41::i;:::-;5633:14;;;5588:104;5616:15;;;5588:104;;;5604:10;;;5588:104;;;;;;;5760:53;;5775:5;;;;5588:104;5760:14;:53::i;:::-;5739:17;;;5703:110;;;;;5721:16;;;5703:110;;5704:15;;;5703:110;10715:22:8;10697:41;;10684:55;10661:21;10657:83;10650:91;5824:83:4;;5869:38;;;;;;;;;;;;;;5824:83;5918:17;5945:24;5972:9;5945:36;;6014:8;5995:27;;:6;:15;;;:27;;;5991:253;;6058:4;6038:25;;6156:6;:17;;;741:6:2;6150:23:4;741:6:2;6124:16:4;:22;:50;;;;;:::i;:::-;;6105:69;;5991:253;;;6231:2;6219:14;;5991:253;6255:23;6280:26;6322:83;6336:16;6354:6;:15;;;6371:6;:14;;;6387:6;:10;;;6399:5;;6322:13;:83::i;:::-;6254:151;;;;6439:7;6420:26;;:6;:15;;;:26;;;6416:306;;6490:17;6555:6;:17;;;741:6:2;6549:23:4;6528:6;:17;;;6510:15;:35;:63;;;;;:::i;:::-;;6490:83;;6610:9;6591:28;;;;6637:60;6646:6;:15;;;6663:4;6669:6;:16;;;6687:9;6637:8;:60::i;:::-;6462:250;6416:306;6754:11;6736:15;:29;6732:103;;;6774:61;;;;;;;;2666:25:13;;;2707:18;;;2700:34;;;2639:18;;6774:61:4;2492:248:13;6732:103:4;6846:93;6860:4;6866:9;6877;6888:6;:10;;;6900:6;:15;;;6917:6;:14;;;6933:5;;6846:13;:93::i;:::-;6973:8;6954:27;;:6;:15;;;:27;;;6950:288;;7025:17;7064:9;7045:16;:28;7025:48;;7091:62;7100:8;7118:4;7125:6;:16;;;7143:9;7091:8;:62::i;:::-;7171:42;7189:8;7199:2;7203:9;7171:17;:42::i;:::-;6997:231;6950:288;-1:-1:-1;7256:15:4;7273:9;;-1:-1:-1;5267:2023:4;;-1:-1:-1;;;;;;;;;;;5267:2023:4:o;7657:244:12:-;7754:16;7751:134;;;7810:16;7807:1;;7789:38;7854:16;7807:1;7844:27;7751:134;7657:244::o;10370:523:9:-;10469:19;;;10664:3;10649:18;;10645:67;;10676:36;;;;;;;;;;;;;;10645:67;-1:-1:-1;;10791:1:9;10778:15;;10835:2;10822:16;;-1:-1:-1;10873:3:9;10860:17;;-1:-1:-1;10370:523:9;-1:-1:-1;10370:523:9:o;3971:769:12:-;4051:15;4076:18;4104:19;4192:4;4186:11;4221:10;4218:1;4211:21;4285:2;4281;4274:14;4312:6;4308:2;4301:18;4377:2;4374:1;4370:2;4366;4363:1;4356:5;4349;4344:36;4333:47;;4408:16;4394:30;;4458:1;4452:8;4437:23;;4487:5;4481:4;4474:19;;4517:7;4528:1;4517:12;4513:112;;4545:22;:20;:22::i;:::-;4588:26;;;;;;;;;;;;;;4513:112;4639:15;;:59;;4682:11;4697:1;4682:16;;4639:59;;;4657:17;;;;:22;4639:59;4635:98;;;4707:26;;;;;;;;;;;;;;4635:98;4041:699;;;3971:769;;;:::o;26251:1526:9:-;26367:14;26383;26413;26437;26462:15;26487:22;26575:4;26569:11;26606:10;26601:3;26594:23;26772:2;26759:11;26755:20;26750:2;26737:11;26733:20;26728:2;26723:3;26719:12;26706:70;26803:46;26817:30;26821:21;;;26817:30;26803:46;26797:4;26790:60;26977:2;26974:1;26952:20;;;26947:2;26938:12;;26974:1;26925:8;26918:5;26913:67;27062:1;27056:8;27092:3;27088:17;;;;-1:-1:-1;27140:2:9;27129:23;;-1:-1:-1;26902:78:9;-1:-1:-1;27011:16:9;;-1:-1:-1;27246:9:9;27236:204;;27285:4;27282:1;27275:15;27318:4;27314:2;27307:16;27351:7;27347:2;27340:19;27387:7;27383:2;27376:19;27422:3;27419:1;27412:14;27236:204;27479:2;27474:3;27470:12;27464:19;27454:29;;27521:2;27516:3;27512:12;27506:19;27496:29;;;27544:22;27558:7;27544:13;:22::i;:::-;27580:14;27598:2;27580:20;27576:69;;27609:36;;;;;;;;;;;;;;27576:69;27656:52;27672:6;27680:9;27691:7;27700;27656:15;:52::i;:::-;27718;27734:6;27742:9;27753:7;27762;27718:15;:52::i;:::-;26403:1374;;;;26251:1526;;;;;;;:::o;7816:144:4:-;7899:6;7891:21;;;:23;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;7877:37;;:10;:37;;;7873:80;;7923:30;;;;;;;;;;;;;;2089:326:12;2160:15;2269:1;2266;2263;2260;2252:6;2248:2;2241:5;2236:35;2225:46;;2295:7;2306:1;2295:12;2291:118;;2323:22;:20;:22::i;:::-;2366:32;;;;;;;;;;;;;;8653:1005:4;8781:14;;;;8863:10;:20;8877:6;8863:20;;8859:58;;8892:25;;;;;;;;;;;;;;8859:58;8955:24;8973:5;;8955:17;:24::i;:::-;8928:51;;-1:-1:-1;8928:51:4;-1:-1:-1;8928:51:4;-1:-1:-1;9005:1:4;8994:12;;8990:58;;;9015:33;;;;;;;;;;;;;;8990:58;13526:10:8;13517:20;;13470:22;;;13457:36;14852:11;14848:23;;;911:6:7;1884:15;9133:325:4;;9217:1;9207:7;:11;9203:47;;;9227:23;;;;;;;;;;;;;;9203:47;9338:3;;-1:-1:-1;9361:7:4;9292:9;;;;;9361:7;;9133:325;;;9403:7;9414:1;9403:12;9399:48;;9424:23;;;;;;;;;;;;;;9399:48;11220:13:8;11198:60;;11185:74;11170:13;11166:94;9472:38:4;;;;9468:80;;9519:29;;;;;;;;;;;;;;9468:80;9562:50;;;:38;9580:5;;9587:12;9598:1;9587:8;:12;:::i;:::-;11243::8;11235:21;11198:60;;;;11220:13;11198:60;11185:74;11170:13;11166:94;;11029:247;-1:-1:-1;11029:247:8;9562:38:4;:50;;;9558:93;;9621:30;;;;;;;;;;;;;;9558:93;8849:809;;8653:1005;;;;;;;;;:::o;10276:919::-;10402:16;;;10477:10;;10473:716;;13470:22:8;;;13457:36;14326:13;14322:25;;;10634:35:4;;10696:26;10716:5;14586:13:8;14582:25;14609:11;14578:43;;14444:193;10696:26:4;10683:39;;10737:18;10758:28;10780:5;15126:14:8;15122:26;15150:10;15118:43;;14978:199;10758:28:4;10737:49;-1:-1:-1;11220:13:8;11243:12;11235:21;;11198:60;;;11185:74;11170:13;11166:94;10800:47:4;;10925:29;10948:5;15406:15:8;15402:27;15431:10;15398:44;;15255:203;10925:29:4;10912:10;:42;10884:24;10902:5;14852:11:8;14848:23;14873:11;14844:41;;14716:185;10884:24:4;:71;10883:78;;;:149;;-1:-1:-1;10986:15:4;;;;;:45;;-1:-1:-1;11019:12:4;11030:1;11019:8;:12;:::i;:::-;11005:10;:26;;10986:45;10862:221;;;11054:29;;;;;;;;;;;;;;10862:221;11101:15;;;:36;;;741:6:2;11120:10:4;:17;;11101:36;11097:81;;;11146:32;;;;;;;;;;;;;;11097:81;10489:700;;10473:716;10276:919;;;;;;;;:::o;2754:659:2:-;2863:13;;2859:548;;2896:23;;;2892:68;;2928:32;;;;;;;;;;;;;;2892:68;2975:25;3016:17;;741:6;;3004:29;;3016:17;;;;;3004:9;:29;:::i;:::-;3003:37;;;;:::i;:::-;2975:65;-1:-1:-1;3054:26:2;3083:29;2975:65;3083:9;:29;:::i;:::-;3054:58;-1:-1:-1;3131:22:2;;3127:85;;3155:57;3168:5;3175;3182:9;3193:18;3155:12;:57::i;:::-;3230:21;;3226:95;;3280:21;;3253:68;;3266:5;;3273;;3280:21;;3303:17;3253:12;:68::i;:::-;3356:9;3341:55;;3349:5;3341:55;;;3367:9;3378:17;3341:55;;;;;;2666:25:13;;;2722:2;2707:18;;2700:34;2654:2;2639:18;;2492:248;3341:55:2;;;;;;;;2878:529;;2859:548;2754:659;;;;:::o;13777:813:4:-;14000:17;14053:25;14095:8;14081:23;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;14081:23:4;;14053:51;;14118:19;14151:1;14140:8;:12;14118:34;;14181:8;14167;14176:1;14167:11;;;;;;;;:::i;:::-;;;;;;:22;;;;;14204:13;14236:9;14231:213;14251:7;14247:1;:11;14231:213;;;13526:10:8;13517:20;;;13470:22;;13457:36;;-1:-1:-1;14357:72:4;14376:11;13470:22:8;14389:5:4;14396:8;14406:4;14412:9;13457:36:8;14357:18:4;:72::i;:::-;14345:84;;;;14260:3;;14231:213;;;;14470:8;14479:11;14470:21;;;;;;;;:::i;:::-;;;;;;;14458:33;;14521:9;14509:8;:21;14505:68;;14539:34;;;;;;;;;;;;;;14505:68;14029:555;;;13777:813;;;;;;;;;;:::o;2000:474:2:-;2112:33;;;2108:86;;2154:40;;;;;;;;;;;;;;2108:86;741:6;2208:16;:22;;;2204:72;;;2239:37;;;;;;;;;;;;;;2204:72;2287:43;;;2340:36;;;;;;;;;2287:21;2340:36;2392:75;;5607:58:13;;;2417:10:2;;2392:75;;5595:2:13;5580:18;2392:75:2;;;;;;;2000:474;;:::o;11813:1364:4:-;11958:16;11976;9498:2:8;12976:20;;12965:31;;12008:38:4;;12057:26;12100:7;12086:22;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;12086:22:4;;12057:51;;12118:25;12160:8;12146:23;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;12146:23:4;-1:-1:-1;12118:51:4;-1:-1:-1;12205:9:4;12118:51;12189:12;12200:1;12189:8;:12;:::i;:::-;12180:22;;;;;;;;:::i;:::-;;;;;;;;;;:34;12240:9;12224:13;12300:7;12283:742;12309:5;;12283:742;;14015:20:8;;;;;14070:30;;;14057:44;;-1:-1:-1;12474:17:4;15431:10:8;15406:15;15402:27;;;15398:44;12474:49:4;;12537:18;12558:28;12580:5;15126:14:8;15122:26;15150:10;15118:43;;14978:199;12558:28:4;12537:49;;12601:14;741:6:2;12640:26:4;12660:5;14586:13:8;14582:25;14609:11;14578:43;;14444:193;12640:26:4;12618:8;12627:9;12618:19;;;;;;;;:::i;:::-;;;;;;;:48;;;;:::i;:::-;:54;;;;:::i;:::-;12601:71;;12709:6;12686:8;12695:9;12686:19;;;;;;;;:::i;:::-;;;;;;:29;;;;;;;:::i;:::-;;;-1:-1:-1;12730:20:4;12743:6;12730:12;:20::i;:::-;12775:34;12788:5;;12795;12802:6;12775:12;:34::i;:::-;12764:45;;12847:8;12823;12832:10;12823:20;;;;;;;;:::i;:::-;;;;;;:32;;;;;;;:::i;:::-;;;-1:-1:-1;12869:22:4;12882:8;12869:12;:22::i;:::-;12923:8;12906:9;12916:3;;;:::i;:::-;;;;12906:14;;;;;;;;:::i;:::-;;;;;;:25;;;;;12994:6;12983:8;:17;12974:26;;;;12317:708;;;12283:742;;;;13046:8;13055:1;13046:11;;;;;;;;:::i;:::-;;;;;;;13035:22;;13080:8;13071:5;:17;13067:64;;13097:34;;;;;;;;;;;;;;13067:64;-1:-1:-1;13160:9:4;;-1:-1:-1;;;11813:1364:4;;;;;;;;;:::o;15103:455::-;15338:13;15366:9;15361:191;15381:7;15377:1;:11;15361:191;;;13526:10:8;13517:20;;13470:22;;;13457:36;15409:43:4;;;;;;;;15467:74;15487:5;;15494:8;15504:4;15510:9;15521:5;15528:9;15538:1;15528:12;;;;;;;;:::i;:::-;;;;;;;15467:19;:74::i;:::-;15390:3;;15361:191;;;;15328:230;15103:455;;;;;;;;:::o;34470:419:9:-;34582:7;34572:301;;34612:16;34609:146;;;34672:16;34669:1;34666;34651:38;34720:16;34717:1;34710:27;34609:146;34783:10;34780:1;34773:21;34857:1;34851:4;34844:15;34572:301;34470:419;:::o;28168:2370::-;28289:1;28281:5;:9;28277:2255;;;28306:15;28500:5;28497:1;28493:13;28484:22;;28534:10;28531:1;28524:21;28590:5;28586:2;28579:17;28660:1;28657;28653:2;28649;28646:1;28636:8;28629:5;28624:38;28613:49;;28689:22;28703:7;28689:13;:22::i;:::-;28726:19;28763;;;28759:278;;28869:59;28885:7;28909:17;:5;:15;:17::i;:::-;28895:31;;;28869:15;:59::i;:::-;28759:278;;;28967:55;28985:5;28992:10;29004:17;:5;:15;:17::i;28967:55::-;29051:14;29079:22;29168:10;29165:1;29158:21;29264:2;29261:1;29258;29254:2;29241:11;29231:8;29224:5;29219:48;29208:59;;29302:16;29284:34;;29351:1;29345:8;29335:18;;29380:22;29394:7;29380:13;:22::i;:::-;29420:14;29438:2;29420:20;29416:69;;29449:36;;;;;;;;;;;;;;29416:69;28292:1204;;;;28277:2255;;;29514:1;29506:5;:9;29502:1030;;;29683:10;29696:19;;;;:47;;29734:9;29696:47;;;29726:4;29696:47;29683:60;;29757:15;29846:4;29840:11;29881:10;29876:3;29869:23;29963:5;29958:2;29953:3;29949:12;29942:27;30007:2;30002;29997:3;29993:12;29986:24;30048:5;30043:2;30038:3;30034:12;30027:27;30130:1;30127;30122:3;30117:2;30112:3;30108:12;30105:1;30095:8;30088:5;30083:49;30072:60;;30172:3;30167;30163:13;30157:4;30150:27;;30204:22;30218:7;30204:13;:22::i;:::-;30250:9;30244:15;;:2;:15;;;30240:282;;30451:56;30469:7;30478:9;30489:17;:5;:15;:17::i;11634:908:8:-;11812:26;;11807:3;11803:36;9547:2;11905:23;;9452:1;11889:39;;;11803:36;11694:11;;11963:18;;;;11999;;;11995:59;;;12026:28;;;;;;;;;;;;;;11995:59;9498:2;12073:10;:23;:28;12069:409;;9682:1;12317:34;12333:5;;12340:10;12317:15;:34::i;:::-;:59;12303:73;;9498:2;12303:73;12398:23;:28;12394:69;;12435:28;;;;;;;;;;;;;;12394:69;9498:2;12502:10;:23;12492:33;;11859:677;11634:908;;;;;:::o;19313:243:4:-;19440:4;19424:21;;;;19420:129;;19447:36;19465:5;19472:2;19476:6;19447:17;:36::i;:::-;19420:129;;;19498:51;19517:6;19525:5;19532:4;19538:2;19542:6;19498:18;:51::i;15870:1071::-;16080:7;16099:13;16115:24;16133:5;14852:11:8;14848:23;14873:11;14844:41;;14716:185;16115:24:4;16099:40;;16149:17;16169:28;16191:5;15126:14:8;15122:26;15150:10;15118:43;;14978:199;16169:28:4;16149:48;;16207:18;16228:29;16251:5;15406:15:8;15402:27;15431:10;15398:44;;15255:203;16228:29:4;16207:50;;16267:17;16301:11;16287:10;:25;:46;;16328:4;16287:46;;;16315:2;16287:46;16267:66;;16344:16;741:6:2;16385:26:4;16405:5;14586:13:8;14582:25;14609:11;14578:43;;14444:193;16385:26:4;16363:8;16372:9;16363:19;;;;;;;;:::i;:::-;;;;;;;:48;;;;:::i;:::-;:54;;;;:::i;:::-;16344:73;;16450:8;16427;16436:9;16427:19;;;;;;;;:::i;:::-;;;;;;:31;;;;;;;:::i;:::-;;;-1:-1:-1;16470:15:4;;16513:141;16547:5;;16554:9;16565:4;822:1:7;2261:16;;;:28;16571:63:4;;16611:23;16628:5;14326:13:8;14322:25;;14197:166;16611:23:4;16571:63;;;16603:4;16571:63;16636:8;16513:20;:141::i;:::-;16469:185;;;;16665:28;16678:14;16665:12;:28::i;:::-;16703:17;16723:62;16729:5;;16736;16743:7;16752:14;16768:9;16779:5;16723;:62::i;:::-;16703:82;;16795:23;16808:9;16795:12;:23::i;:::-;16853:9;16829:8;16838:10;16829:20;;;;;;;;:::i;:::-;;;;;;:33;;;;;;;:::i;:::-;;;-1:-1:-1;16904:20:4;;;;;15870:1071;-1:-1:-1;;;;;;;;;;;;;;15870:1071:4:o;8138:150::-;8203:11;;;:41;;-1:-1:-1;8227:17:4;8218:26;;8203:41;8199:82;;;8253:28;;;;;;;;;;;;;;2857:1228:3;2947:16;2975:12;2990:23;3007:5;14326:13:8;14322:25;;14197:166;2990:23:3;2975:38;;3023:13;3039:24;3057:5;14852:11:8;14848:23;14873:11;14844:41;;14716:185;3039:24:3;3023:40;-1:-1:-1;911:6:7;1884:15;;3117:25:3;;;3113:965;;3155:39;3171:4;3177:5;3184:9;3155:15;:39::i;:::-;3144:50;;3113:965;;;3213:40;;;3209:869;;3266:44;3287:4;3293:5;3300:9;3266:20;:44::i;3209:869::-;3329:33;;;3325:753;;3375:38;3390:4;3396:5;3403:9;3375:14;:38::i;3325:753::-;3432:25;;;3428:650;;3470:39;3486:4;3492:5;3499:9;3470:15;:39::i;3428:650::-;3528:29;;;3524:554;;3570:38;3585:4;3591:5;3598:9;3570:14;:38::i;3524:554::-;3627:32;;;3623:455;;3672:40;3689:4;3695:5;3702:9;3672:16;:40::i;3623:455::-;4052:26;;;;;;;;;;;;;;3623:455;2965:1120;;;2857:1228;;;;;;:::o;17166:661:4:-;17366:12;17381:23;17398:5;14326:13:8;14322:25;;14197:166;17381:23:4;17366:38;;17414:13;17430:24;17448:5;14852:11:8;14848:23;14873:11;14844:41;;14716:185;17430:24:4;17414:40;;17466:15;17483:22;17509:141;17543:5;;17550:28;17572:5;15126:14:8;15122:26;15150:10;15118:43;;14978:199;17550:28:4;17580:4;822:1:7;2261:16;;;:28;17586:44:4;;17626:4;17586:44;;;17618:4;17586:44;17632:8;17509:20;:141::i;:::-;17465:185;;-1:-1:-1;17465:185:4;-1:-1:-1;17661:17:4;17714:12;17725:1;17714:8;:12;:::i;:::-;17681:29;17704:5;15406:15:8;15402:27;15431:10;15398:44;;15255:203;17681:29:4;:45;:66;;17742:4;17681:66;;;17729:2;17681:66;17661:86;;17758:62;17764:5;;17771;17778:7;17787:14;17803:9;17814:5;17758;:62::i;:::-;;17356:471;;;;;17166:661;;;;;;;:::o;751:202:11:-;803:7;889:1;884;:6;880:29;;-1:-1:-1;907:1:11;751:202::o;880:29::-;926:20;;;;;;;;;;;;;;3310:404:12;3378:15;3453:10;3450:1;3443:21;3509:6;3505:2;3498:18;3576:1;3573;3569:2;3565;3562:1;3553:7;3546:5;3541:37;3530:48;;3602:7;3613:1;3602:12;3598:110;;3630:22;:20;:22::i;:::-;3673:24;;;;;;;;;;;;;;15919:451:8;16240:22;16105:38;;16203:60;16207:31;;;16203:60;16190:74;16165:23;16161:104;16084:195;16311:1;16302:10;;;:15;;16298:65;;16326:37;;;;;;;;;;;;;;3271:550:10;3439:4;3433:11;3484:5;3480:2;3476:14;3472:2;3468:23;3465:1;3458:34;3524:4;3520:2;3516:13;3512:2;3505:25;3562:2;3558;3554:11;3550:2;3543:23;3590:6;3586:2;3579:18;3654:1;3651;3647:2;3644:1;3641;3633:6;3626:5;3621:35;3611:161;;3697:16;3694:1;3691;3676:38;3741:16;3738:1;3731:27;3611:161;3793:4;3786:19;-1:-1:-1;;;;;3271:550:10:o;18388:697:4:-;18532:7;;11243:12:8;11235:21;;11198:60;;11220:13;11198:60;11185:74;11170:13;11166:94;18628:12:4;;;18624:422;;10715:22:8;10697:41;;10684:55;10661:21;10657:83;10650:91;10643:99;18656:18:4;10643:99:8;18742:49:4;;18790:1;18742:49;;;18758:29;18777:5;18784:2;18758:18;:29::i;:::-;18724:67;;18805:51;18824:6;18832:5;18839:4;18845:2;18849:6;18805:18;:51::i;:::-;18879:13;:64;;18937:6;18879:64;;;18927:7;18895:29;18914:5;18921:2;18895:18;:29::i;:::-;:39;;;;:::i;:::-;18870:73;;18642:312;;18624:422;;;18964:19;;;18978:4;18964:19;18960:86;;18999:36;19017:5;19024:2;19028:6;18999:17;:36::i;:::-;19064:5;19071:6;;-1:-1:-1;18388:697:4;;-1:-1:-1;;;;;;18388:697:4:o;4257:1256:3:-;4406:17;4439:12;4454:23;4471:5;14326:13:8;14322:25;;14197:166;4454:23:3;4439:38;-1:-1:-1;911:6:7;1884:15;;4530:25:3;;;4526:980;;4569:42;4578:4;4584:5;4591:8;4601:9;4569:8;:42::i;:::-;4557:54;;4526:980;;;4630:40;;;4626:880;;4684:37;4698:4;4704:5;4711:9;4684:13;:37::i;4626:880::-;4740:33;;;4736:770;;4787:31;4795:4;4801:5;4808:9;4787:7;:31::i;4736:770::-;4837:25;;;4833:673;;4876:51;4885:4;4891:5;4898:9;4909:8;4919:7;4876:8;:51::i;4833:673::-;4946:29;;;4942:564;;4989:41;4997:4;5003:5;5010:9;5021:8;4989:7;:41::i;4942:564::-;5049:32;;;5045:461;;5095:43;5105:4;5111:5;5118:9;5129:8;5095:9;:43::i;5045:461::-;5274:21;;;5270:236;;5309:47;5321:4;5327:9;5338:8;5348:7;5309:11;:47::i;5270:236::-;5375:18;;;5371:135;;5407:51;5416:4;5422:5;5429:9;5440:8;5450:7;5407:8;:51::i;5371:135::-;4429:1084;;4257:1256;;;;;;;;;:::o;5649:315::-;5745:7;;;5806:61;5837:4;778:1:7;2064:20;;;:36;5806:30:3;:61::i;:::-;5764:103;;-1:-1:-1;5764:103:3;-1:-1:-1;5923:22:3;5936:9;5764:103;5923:22;:::i;:::-;5922:30;;5949:3;5922:30;:::i;:::-;5916:1;5885:21;5897:9;5885;:21;:::i;:::-;:28;;5909:4;5885:28;:::i;:::-;:32;;;;:::i;:::-;5884:69;;;;:::i;:::-;:73;;5956:1;5884:73;:::i;:::-;5877:80;5649:315;-1:-1:-1;;;;;;5649:315:3:o;6824:259::-;6949:16;6988:88;7022:11;7035:4;7041:9;778:1:7;2064:20;;;:36;6988:33:3;:88::i;:::-;6981:95;6824:259;-1:-1:-1;;;;6824:259:3:o;7533:334::-;7628:7;;;7688:69;7716:4;7722:9;778:1:7;2064:20;;;:36;7688:27:3;:69::i;:::-;7647:110;;;;7771:10;7785:1;7771:15;7767:68;;7795:40;;;;;;;;;;;;;;7767:68;-1:-1:-1;7852:8:3;7533:334;-1:-1:-1;;;;7533:334:3:o;8296:203::-;8387:16;8422:70;8451:4;778:1:7;2064:20;;;:36;8482:9:3;8422:28;:70::i;9995:364::-;10090:7;;;10167:69;10195:4;10201:9;778:1:7;2064:20;;;:36;10167:27:3;:69::i;:::-;10109:127;;;;10269:9;10250:15;:28;10246:81;;10287:40;;;;;;;;;;;;;;10991:370;11088:7;;;11165:71;11195:4;11201:9;778:1:7;2064:20;;;:36;11165:29:3;:71::i;:::-;11107:129;;;;11269:9;11250:15;:28;11246:83;;11287:42;;;;;;;;;;;;;;680:731:12;754:14;780:15;805:22;887:10;884:1;877:21;944:7;940:2;933:19;1013:2;1010:1;1006:2;1002;995:5;988;977:39;966:50;;1048:16;1030:34;;1094:1;1088:8;1078:18;;1120:7;1131:1;1120:12;1116:40;;1134:22;:20;:22::i;:::-;1366:2;1349:14;:19;1345:59;;;1377:27;;;;;;;;;;;;;;1345:59;770:641;;680:731;;;;:::o;6071:610:3:-;6189:17;778:1:7;2064:20;;;:36;6189:17:3;;6312:45;6343:4;2064:36:7;6312:30:3;:45::i;:::-;6270:87;;-1:-1:-1;6270:87:3;-1:-1:-1;6368:23:3;6394:14;:8;6405:3;6394:14;:::i;:::-;6368:40;-1:-1:-1;6368:40:3;6464:16;:9;6476:4;6464:16;:::i;:::-;:34;;;;:::i;:::-;6431:28;6449:10;6431:15;:28;:::i;:::-;6430:69;;;;:::i;:::-;6418:81;;6511:15;6528;6547:7;:59;;6584:9;6603:1;6547:59;;;6566:1;6570:9;6547:59;6510:96;;;;6616:58;6640:4;6646:7;6655;6664:9;6616:23;:58::i;:::-;6212:469;;;;;;6071:610;;;;;;:::o;7193:202::-;7282:17;7318:70;7347:4;778:1:7;2064:20;;;:36;7378:9:3;7318:28;:70::i;7970:190::-;8053:17;8089:64;8112:4;778:1:7;2064:20;;;:36;8143:9:3;8089:22;:64::i;8656:624::-;8831:28;8857:2;8831:28;;;;1358:14:7;8830:52:3;8814:13;:68;8791:7;;;;8965:84;8847:4;8995:9;9006:23;9023:5;778:1:7;2064:20;;;:36;;1983:124;9006:23:3;9031:8;9041:7;8965:23;:84::i;:::-;8893:156;;;;;;9081:4;9064:13;;:21;9060:69;;9094:35;;;;;;;;;;;;;;9060:69;9161:8;9143:14;:26;9139:74;;9178:35;;;;;;;;;;;;;;9139:74;-1:-1:-1;;9240:6:3;9224:13;:22;9264:9;8656:624;-1:-1:-1;;;;;;8656:624:3:o;10470:375::-;10571:7;;;10648:74;10671:4;10677:9;10688:8;778:1:7;2064:20;;;:36;10648:22:3;:74::i;:::-;10590:132;;;;10755:8;10737:14;:26;10733:79;;10772:40;;;;;;;;;;;;;;10733:79;-1:-1:-1;10829:9:3;10470:375;-1:-1:-1;;;;;10470:375:3:o;11475:381::-;11578:7;;;11655:76;11680:4;11686:9;11697:8;778:1:7;2064:20;;;:36;11655:24:3;:76::i;:::-;11597:134;;;;11764:8;11746:14;:26;11742:81;;11781:42;;;;;;;;;;;;;;14166:288;14289:17;14322:46;14344:7;14353:4;14359:8;14322:21;:46::i;:::-;14385:62;14412:4;14418:9;14429:8;14439:7;14385:26;:62::i;:::-;14378:69;14166:288;-1:-1:-1;;;;;14166:288:3:o;14569:617::-;14744:28;14770:2;14744:28;;;;1668:14:7;14743:45:3;14727:13;:61;14704:7;;;;14871:84;14760:4;14901:9;14912:8;14922:23;14939:5;778:1:7;2064:20;;;:36;;1983:124;14922:23:3;14947:7;14871:23;:84::i;1187:752:9:-;1262:17;1281:18;1311:22;1392:10;1389:1;1382:21;1471:2;1468:1;1465;1461:2;1455:4;1448:5;1437:37;1434:79;;;-1:-1:-1;1495:16:9;1434:79;1534:7;1554:138;;;;1758:12;1754:1;1748:8;1744:27;1731:40;;1817:12;1812:2;1806:9;1802:28;1788:42;;1527:317;;1554:138;1607:12;1602:2;1596:9;1592:28;1579:41;;1665:12;1661:1;1655:8;1651:27;1637:41;;1527:317;;1885:2;1868:14;:19;1864:68;;;1896:36;;;;;;;;;;;;;;1864:68;1301:638;1187:752;;;;;:::o;3269:812::-;3407:16;3439:22;3471:15;3552:4;3546:11;3583:10;3578:3;3571:23;3663:4;3658:2;3653:3;3649:12;3642:26;3702:9;3697:2;3692:3;3688:12;3681:31;3746:8;3741:2;3736:3;3732:12;3725:30;3791:3;3786;3782:13;3776:4;3769:27;3869:2;3866:1;3861:3;3856:2;3851:3;3847:12;3839:6;3832:5;3821:51;3810:62;;;3904:16;3886:34;;3952:1;3946:8;3934:20;;3973:22;3987:7;3973:13;:22::i;:::-;4027:2;4010:14;:19;4006:68;;;4038:36;;;;;;;;;;;;;;4006:68;3429:652;;3269:812;;;;;;:::o;5336:758::-;5452:16;5470:18;5504:22;5536:15;5619:4;5613:11;5648:10;5645:1;5638:21;5710:9;5706:2;5699:21;5744:8;5740:2;5733:20;5813:2;5810:1;5806:2;5802;5796:4;5789:5;5778:38;5767:49;;5848:16;5830:34;;5896:1;5890:8;5878:20;;5931:2;5925:9;5911:23;;5961:5;5955:4;5948:19;;5986:22;6000:7;5986:13;:22::i;:::-;6040:2;6023:14;:19;6019:68;;;6051:36;;;;;;;;;;;;;;6019:68;5494:600;;5336:758;;;;;;:::o;7566:781::-;7656:16;7685:15;7702:11;7717:79;7729:4;7743;7750:10;7763:20;:9;:18;:20::i;:::-;7762:21;;;:::i;:::-;7793:1;7717:11;:79::i;:::-;7684:112;;;;8004:3;7986:16;7983:25;7973:7;7966:15;7962:47;8027:1;8022:121;;;;8189:10;8216:42;;;;8312:1;8307:3;8303:11;8297:18;8285:30;;8182:135;;8216:42;8252:2;8247:3;8243:12;8237:19;8225:31;;8182:135;;7955:376;;8022:121;8068:16;8065:1;8062;8047:38;8112:16;8109:1;8102:27;7955:376;;7832:509;;7566:781;;;;;:::o;12277:1130::-;12393:16;12411:23;12450:22;12482:15;12565:4;12559:11;12594:10;12591:1;12584:21;12668:9;12665:1;12661:17;12657:2;12650:29;12703:8;12699:2;12692:20;12772:2;12769:1;12765:2;12761;12755:4;12748:5;12737:38;12807:16;;-1:-1:-1;12726:49:9;-1:-1:-1;12844:8:9;12865:104;;;;13026:1;13020:8;13008:20;;13070:2;13064:9;13045:28;;12837:250;;12865:104;12908:2;12902:9;12890:21;;12953:1;12947:8;12928:27;;12837:250;;13127:1;13117:8;13113:16;13101:28;;13212:15;13209:1;13205:23;13186:42;;13274:5;13268:4;13261:19;;13299:22;13313:7;13299:13;:22::i;16096:1223::-;16214:16;16232:23;16271:25;16299:39;16323:4;16329:8;16299:23;:39::i;:::-;16271:67;;16349:22;16381:15;16462:4;16456:11;16493:10;16488:3;16481:23;16578:8;16573:2;16568:3;16564:12;16557:30;16628:9;16625:1;16621:17;16616:2;16611:3;16607:12;16600:39;16673:17;16668:2;16663:3;16659:12;16652:39;16727:3;16722;16718:13;16712:4;16705:27;16803:2;16800:1;16795:3;16790:2;16785:3;16781:12;16775:4;16768:5;16757:49;16746:60;;;16837:16;16819:34;;16874:8;16900:1;16895:104;;;;17056:1;17050:8;17038:20;;17100:2;17094:9;17075:28;;16867:250;;16895:104;16938:2;16932:9;16920:21;;16983:1;16977:8;16958:27;;16867:250;;17157:15;17154:1;17150:23;17131:42;;17211:22;17225:7;17211:13;:22::i;:::-;17265:2;17248:14;:19;17244:68;;;17276:36;;;;;;;;;;;;;;17244:68;16261:1058;;;16096:1223;;;;;;:::o;2231:686::-;2334:15;2415:4;2409:11;2446:10;2441:3;2434:23;2530:7;2525:2;2520:3;2516:12;2509:29;2572:7;2567:2;2562:3;2558:12;2551:29;2614:9;2609:2;2604:3;2600:12;2593:31;2659:3;2653;2648;2644:13;2637:26;2698:1;2692:3;2687;2683:13;2676:24;2736:3;2731;2727:13;2721:4;2714:27;2867:1;2864;2859:3;2854:2;2849:3;2845:12;2842:1;2836:4;2829:5;2824:45;2813:56;;;2888:22;2902:7;2888:13;:22::i;:::-;2324:593;2231:686;;;;:::o;4313:751::-;4401:17;4430:22;4462:15;4546:4;4540:11;4575:10;4572:1;4565:21;4632:8;4628:2;4621:20;4665:9;4661:2;4654:21;4732:2;4729:1;4725:2;4721;4718:1;4712:4;4705:5;4700:35;4767:16;;-1:-1:-1;4689:46:9;-1:-1:-1;4804:8:9;4825:32;;;;4899:2;4893:9;4880:22;;4797:107;;4825:32;4853:1;4847:8;4834:21;;4797:107;-1:-1:-1;4925:4:9;4918:19;4956:22;4970:7;4956:13;:22::i;:::-;5010:2;4993:14;:19;4989:68;;;5021:36;;;;;;;;;;;;;;4989:68;4420:644;;4313:751;;;;;:::o;6335:765::-;6417:17;6446:22;6478:15;6562:4;6556:11;6591:10;6588:1;6581:21;6648:8;6644:2;6637:20;6681:9;6677:2;6670:21;6748:2;6745:1;6741:2;6737;6734:1;6728:4;6721:5;6716:35;6783:16;;-1:-1:-1;6705:46:9;-1:-1:-1;6820:8:9;6841:43;;;;6935:1;6929:8;6924:3;6920:18;6907:31;;6813:127;;6841:43;6878:2;6872:9;6867:3;6863:19;6850:32;;6813:127;-1:-1:-1;6961:4:9;6954:19;6992:22;7006:7;6992:13;:22::i;:::-;7046:2;7029:14;:19;7025:68;;;7057:36;;;;;;;;;;;;;;8576:1035;8712:23;8737:22;8761:20;8798:15;8815:11;8830:70;8842:4;8848:9;8859:10;8871:19;:8;:17;:19::i;:::-;8892:7;8830:11;:70::i;:::-;8797:103;;;;8910:22;8924:7;8910:13;:22::i;:::-;9079:2;9070:12;;9063:29;;;9136:2;9121:18;;;-1:-1:-1;9033:16:9;9160:10;9183:122;;;;9368:3;9362:10;9344:28;;9423:2;9418:3;9414:12;9408:19;9389:38;;9153:288;;9183:122;9233:3;9227:10;9208:29;;9287:2;9282:3;9278:12;9272:19;9254:37;;9153:288;;9481:15;9478:1;9474:23;9455:42;;9557:2;9540:14;:19;9536:68;;;9568:36;;;;;;;;;;;;;;9536:68;8787:824;;;8576:1035;;;;;;;;;:::o;13640:1286::-;13756:17;13775:22;13813;13845:15;13927:4;13921:11;13958:10;13953:3;13946:23;14046:9;14041:2;14036:3;14032:12;14025:31;14090:8;14085:2;14080:3;14076:12;14069:30;14133:8;14128:2;14123:3;14119:12;14112:30;14177:3;14171;14166;14162:13;14155:26;14216:1;14210:3;14205;14201:13;14194:24;14253:1;14247:3;14242;14238:13;14231:24;14291:3;14286;14282:13;14276:4;14269:27;14422:2;14419:1;14414:3;14409:2;14404:3;14400:12;14397:1;14391:4;14384:5;14379:46;14368:57;;;14456:16;14438:34;;14493:8;14519:1;14514:104;;;;14681:1;14675:8;14657:26;;14719:2;14713:9;14700:22;;14486:250;;14514:104;14563:2;14557:9;14539:27;;14602:1;14596:8;14583:21;;14486:250;;14770:9;14767:1;14763:17;14750:30;;14818:22;14832:7;14818:13;:22::i;:::-;14872:2;14855:14;:19;14851:68;;;14883:36;;;;;;;;;;;;;;14851:68;13803:1123;;13640:1286;;;;;;;:::o;17555:1242::-;17673:17;17692:22;17730:25;17758:39;17782:4;17788:8;17758:23;:39::i;:::-;17730:67;;17808:22;17840:15;17921:4;17915:11;17952:10;17947:3;17940:23;18034:9;18029:2;18024:3;18020:12;18013:31;18078:8;18073:2;18068:3;18064:12;18057:30;18121:8;18116:2;18111:3;18107:12;18100:30;18165:17;18159:3;18154;18150:13;18143:40;18219:3;18214;18210:13;18204:4;18197:27;18292:2;18289:1;18284:3;18279:2;18274:3;18270:12;18267:1;18261:4;18254:5;18249:46;18238:57;;;18327:16;18309:34;;18364:8;18390:1;18385:104;;;;18552:1;18546:8;18528:26;;18590:2;18584:9;18571:22;;18357:250;;18385:104;18434:2;18428:9;18410:27;;18473:1;18467:8;18454:21;;18357:250;;18641:9;18638:1;18634:17;18621:30;;18689:22;18703:7;18689:13;:22::i;:::-;18743:2;18726:14;:19;18722:68;;;18754:36;;;;;;;;;;;;;;18722:68;17720:1077;;;17555:1242;;;;;;;:::o;6170:1190:12:-;6259:15;6284:18;6312:19;6381:164;;;6456:7;6452:2;6445:19;6418:8;6528:2;6524;6520;6516;6513:1;6505:6;6498:5;6493:38;6481:50;6381:164;-1:-1:-1;;;6381:164:12:o;:::-;6578:4;6572:11;6607:10;6604:1;6597:21;6670:7;6666:2;6659:19;6703:22;6718:6;6711:5;6703:22;:::i;:::-;6692:33;;6770:7;6766:1;6761:2;6755:9;6752:16;6748:30;6738:263;;6861:7;6842:16;6833:5;6821:18;6814:26;6811:48;6808:61;6798:189;;6897:17;6912:1;6905:5;6897:17;:::i;:::-;6893:22;6947;6962:6;6955:5;6947:22;:::i;:::-;6936:33;;6798:189;7029:16;7015:30;;7079:2;7073:9;7058:24;;7109:5;7103:4;7096:19;;7139:7;7150:1;7139:12;7135:111;;7167:22;:20;:22::i;:::-;7210:25;;;;;;;;;;;;;;7135:111;7260:15;;:59;;7303:11;7318:1;7303:16;;7260:59;;;7278:17;;;;:22;7260:59;7256:97;;;7328:25;;;;;;;;;;;;;;31776:862:9;31898:17;31931:22;31963:15;32044:4;32038:11;32075:10;32070:3;32063:23;32158:7;32153:2;32148:3;32144:12;32137:29;32200:1;32195:2;32190:3;32186:12;32179:23;32261:8;32256:2;32251:3;32247:12;32240:30;32305:9;32299:3;32294;32290:13;32283:32;32383:2;32380:1;32375:3;32370:2;32365:3;32361:12;32358:1;32352:4;32345:5;32340:46;32329:57;;32418:16;32400:34;;32467:1;32461:8;32448:21;;32505:3;32500;32496:13;32490:4;32483:27;;32529:22;32543:7;32529:13;:22::i;:::-;32566:14;32584:2;32566:20;32562:69;;32595:36;;;;;;;;;;;;;;32860:1372;32994:23;33019:22;33043:20;33079:22;33111:15;33192:4;33186:11;33223:10;33218:3;33211:23;33304:9;33299:2;33294:3;33290:12;33283:31;33348:8;33343:2;33338:3;33334:12;33327:30;33391:8;33386:2;33381:3;33377:12;33370:30;33435:3;33429;33424;33420:13;33413:26;33474:2;33468:3;33463;33459:13;33452:25;33512:7;33506:3;33501;33497:13;33490:30;33590:2;33585:3;33580;33575:2;33570:3;33566:12;33563:1;33557:4;33550:5;33545:48;33534:59;;33625:16;33607:34;;33676:7;33671:2;33666:3;33662:12;33655:29;33728:2;33723:3;33713:18;33697:34;;33752:8;33778:1;33773:122;;;;33913:1;33908:122;;;;33745:285;;33773:122;33823:3;33817:10;33798:29;;33877:2;33872:3;33868:12;33862:19;33844:37;;33773:122;;33908;33957:3;33951:10;33933:28;;34012:2;34007:3;34003:12;33997:19;33978:38;;33745:285;;;34070:15;34067:1;34063:23;34044:42;;34124:22;34138:7;34124:13;:22::i;:::-;34178:2;34161:14;:19;34157:68;;;34189:36;;;;;;;;;;;;;;34157:68;33069:1163;;32860:1372;;;;;;;;;:::o;11173:840::-;11315:15;11332:11;11359:18;11380:10;:54;;718:53;11380:54;;;650:14;11380:54;11359:75;;11497:4;11491:11;11484:18;;11528:10;11523:3;11516:23;11616:9;11611:2;11606:3;11602:12;11595:31;11660:10;11655:2;11650:3;11646:12;11639:32;11705:11;11700:2;11695:3;11691:12;11684:33;11752:10;11746:3;11741;11737:13;11730:33;11798:3;11792;11787;11783:13;11776:26;11837:2;11831:3;11826;11822:13;11815:25;11875:7;11869:3;11864;11860:13;11853:30;11919:3;11914;11910:13;11904:4;11897:27;11994:2;11989:3;11984;11979:2;11974:3;11970:12;11967:1;11961:4;11954:5;11949:48;11938:59;;11470:537;11173:840;;;;;;;;:::o;15198:631::-;15283:25;15320:22;15408:4;15402:11;15437:10;15434:1;15427:21;15524:2;15521:1;15518;15514:2;15508:4;15501:5;15490:37;15487:79;;;15548:16;15530:34;;15487:79;15587:8;15608:41;;;;15699:1;15693:8;15672:29;;15580:123;;15608:41;15644:2;15638:9;15617:30;;15580:123;-1:-1:-1;15724:4:9;15717:17;15775:2;15758:19;;15754:68;;;15786:36;;;;;;;;;;;;;;15754:68;15310:519;15198:631;;;;:::o;196:154:13:-;282:42;275:5;271:54;264:5;261:65;251:93;;340:1;337;330:12;355:508;432:6;440;448;501:2;489:9;480:7;476:23;472:32;469:52;;;517:1;514;507:12;469:52;556:9;543:23;575:31;600:5;575:31;:::i;:::-;625:5;-1:-1:-1;682:2:13;667:18;;654:32;695:33;654:32;695:33;:::i;:::-;355:508;;747:7;;-1:-1:-1;;;827:2:13;812:18;;;;799:32;;355:508::o;1099:1388::-;1223:6;1231;1239;1247;1255;1263;1271;1279;1332:3;1320:9;1311:7;1307:23;1303:33;1300:53;;;1349:1;1346;1339:12;1300:53;1388:9;1375:23;1407:31;1432:5;1407:31;:::i;:::-;1457:5;-1:-1:-1;1514:2:13;1499:18;;1486:32;1527:33;1486:32;1527:33;:::i;:::-;1579:7;-1:-1:-1;1659:2:13;1644:18;;1631:32;;-1:-1:-1;1762:2:13;1747:18;;1734:32;;-1:-1:-1;1844:3:13;1829:19;;1816:33;1858;1816;1858;:::i;:::-;1910:7;-1:-1:-1;1969:3:13;1954:19;;1941:33;1983;1941;1983;:::i;:::-;2035:7;-1:-1:-1;2093:3:13;2078:19;;2065:33;2121:18;2110:30;;2107:50;;;2153:1;2150;2143:12;2107:50;2176:22;;2229:4;2221:13;;2217:27;-1:-1:-1;2207:55:13;;2258:1;2255;2248:12;2207:55;2298:2;2285:16;2324:18;2316:6;2313:30;2310:50;;;2356:1;2353;2346:12;2310:50;2401:7;2396:2;2387:6;2383:2;2379:15;2375:24;2372:37;2369:57;;;2422:1;2419;2412:12;2369:57;1099:1388;;;;-1:-1:-1;1099:1388:13;;-1:-1:-1;1099:1388:13;;;;;;2453:2;2445:11;;-1:-1:-1;;1099:1388:13:o;2745:435::-;2812:6;2820;2873:2;2861:9;2852:7;2848:23;2844:32;2841:52;;;2889:1;2886;2879:12;2841:52;2928:9;2915:23;2947:31;2972:5;2947:31;:::i;:::-;2997:5;-1:-1:-1;3054:2:13;3039:18;;3026:32;3102:26;3089:40;;3077:53;;3067:81;;3144:1;3141;3134:12;3067:81;3167:7;3157:17;;;2745:435;;;;;:::o;3185:184::-;3237:77;3234:1;3227:88;3334:4;3331:1;3324:15;3358:4;3355:1;3348:15;4055:184;4107:77;4104:1;4097:88;4204:4;4201:1;4194:15;4228:4;4225:1;4218:15;4244:251;4314:6;4367:2;4355:9;4346:7;4342:23;4338:32;4335:52;;;4383:1;4380;4373:12;4335:52;4415:9;4409:16;4434:31;4459:5;4434:31;:::i;4500:184::-;4552:77;4549:1;4542:88;4649:4;4646:1;4639:15;4673:4;4670:1;4663:15;4689:128;4756:9;;;4777:11;;;4774:37;;;4791:18;;:::i;4822:168::-;4895:9;;;4926;;4943:15;;;4937:22;;4923:37;4913:71;;4964:18;;:::i;4995:274::-;5035:1;5061;5051:189;;5096:77;5093:1;5086:88;5197:4;5194:1;5187:15;5225:4;5222:1;5215:15;5051:189;-1:-1:-1;5254:9:13;;4995:274::o;5274:184::-;5326:77;5323:1;5316:88;5423:4;5420:1;5413:15;5447:4;5444:1;5437:15;5676:125;5741:9;;;5762:10;;;5759:36;;;5775:18;;:::i;5806:196::-;5845:3;5873:5;5863:39;;5882:18;;:::i;:::-;-1:-1:-1;5929:66:13;5918:78;;5806:196::o;6007:191::-;6042:3;6073:66;6066:5;6063:77;6060:103;;6143:18;;:::i;:::-;-1:-1:-1;6183:1:13;6179:13;;6007:191::o

Swarm Source

ipfs://8b73127399dde8946da9be162faea1fd4faab80441131109f005eacf4d928049

Block Transaction Gas Used Reward
view all blocks produced

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

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits

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.