ETH Price: $3,426.65 (-6.17%)
Gas: 7.95 Gwei

Contract

0xc800b0e15c40a1Ff0539218100c86F4c1BAC8D9C
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To

There are no matching entries

> 10 Internal Transactions found.

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Block
From
To
214413452024-12-20 4:28:115 mins ago1734668891
0xc800b0e1...c1BAC8D9C
1.97509757 ETH
214413452024-12-20 4:28:115 mins ago1734668891
0xc800b0e1...c1BAC8D9C
1.97509757 ETH
214411832024-12-20 3:55:4737 mins ago1734666947
0xc800b0e1...c1BAC8D9C
4.78262108 ETH
214411832024-12-20 3:55:4737 mins ago1734666947
0xc800b0e1...c1BAC8D9C
4.78262108 ETH
214405832024-12-20 1:55:112 hrs ago1734659711
0xc800b0e1...c1BAC8D9C
1.50143068 ETH
214405832024-12-20 1:55:112 hrs ago1734659711
0xc800b0e1...c1BAC8D9C
1.50143068 ETH
214405672024-12-20 1:51:592 hrs ago1734659519
0xc800b0e1...c1BAC8D9C
2 ETH
214405672024-12-20 1:51:592 hrs ago1734659519
0xc800b0e1...c1BAC8D9C
2 ETH
214394682024-12-19 22:11:356 hrs ago1734646295
0xc800b0e1...c1BAC8D9C
8.62507524 ETH
214394682024-12-19 22:11:356 hrs ago1734646295
0xc800b0e1...c1BAC8D9C
8.62507524 ETH
214394152024-12-19 22:00:596 hrs ago1734645659
0xc800b0e1...c1BAC8D9C
2.16874999 ETH
214394152024-12-19 22:00:596 hrs ago1734645659
0xc800b0e1...c1BAC8D9C
2.16874999 ETH
214392292024-12-19 21:23:477 hrs ago1734643427
0xc800b0e1...c1BAC8D9C
20.37876287 ETH
214392292024-12-19 21:23:477 hrs ago1734643427
0xc800b0e1...c1BAC8D9C
20.37876287 ETH
214392092024-12-19 21:19:477 hrs ago1734643187
0xc800b0e1...c1BAC8D9C
14.41300502 ETH
214392092024-12-19 21:19:477 hrs ago1734643187
0xc800b0e1...c1BAC8D9C
14.41300502 ETH
214389382024-12-19 20:25:118 hrs ago1734639911
0xc800b0e1...c1BAC8D9C
14.70395438 ETH
214389382024-12-19 20:25:118 hrs ago1734639911
0xc800b0e1...c1BAC8D9C
14.70395438 ETH
214383242024-12-19 18:21:5910 hrs ago1734632519
0xc800b0e1...c1BAC8D9C
7.27263671 ETH
214383242024-12-19 18:21:5910 hrs ago1734632519
0xc800b0e1...c1BAC8D9C
7.27263671 ETH
214383092024-12-19 18:18:5910 hrs ago1734632339
0xc800b0e1...c1BAC8D9C
7.24444625 ETH
214383092024-12-19 18:18:5910 hrs ago1734632339
0xc800b0e1...c1BAC8D9C
7.24444625 ETH
214382072024-12-19 17:58:2310 hrs ago1734631103
0xc800b0e1...c1BAC8D9C
2.96950897 ETH
214382072024-12-19 17:58:2310 hrs ago1734631103
0xc800b0e1...c1BAC8D9C
2.96950897 ETH
214381122024-12-19 17:38:5910 hrs ago1734629939
0xc800b0e1...c1BAC8D9C
3.00182613 ETH
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
FluidDexT1

Compiler Version
v0.8.21+commit.d9974bed

Optimization Enabled:
Yes with 10000000 runs

Other Settings:
paris EvmVersion
File 1 of 25 : main.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;

import { CoreHelpers } from "../helpers/coreHelpers.sol";
import { SafeTransfer } from "../../../../../libraries/safeTransfer.sol";
import { DexSlotsLink } from "../../../../../libraries/dexSlotsLink.sol";
import { DexCalcs } from "../../../../../libraries/dexCalcs.sol";
import { BigMathMinified } from "../../../../../libraries/bigMathMinified.sol";
import { ErrorTypes } from "../../../errorTypes.sol";
import { IFluidDexT1 } from "../../../interfaces/iDexT1.sol";

interface IDexCallback {
    function dexCallback(address token_, uint256 amount_) external;
}

/// @title FluidDexT1
/// @notice Implements core logics for Fluid Dex protocol.
/// Note Token transfers happen directly from user to Liquidity contract and vice-versa.
contract FluidDexT1 is CoreHelpers {
    using BigMathMinified for uint256;

    constructor(ConstantViews memory constantViews_) CoreHelpers(constantViews_) {
        // any implementations should not be zero
        if (
            constantViews_.implementations.shift == address(0) ||
            constantViews_.implementations.admin == address(0) ||
            constantViews_.implementations.colOperations == address(0) ||
            constantViews_.implementations.debtOperations == address(0) ||
            constantViews_.implementations.perfectOperationsAndSwapOut == address(0)
        ) {
            revert FluidDexError(ErrorTypes.DexT1__InvalidImplementation);
        }
    }

    struct SwapInExtras {
        address to;
        uint amountOutMin;
        bool isCallback;
    }

    /// @dev This function allows users to swap a specific amount of input tokens for output tokens
    /// @param swap0to1_ Direction of swap. If true, swaps token0 for token1; if false, swaps token1 for token0
    /// @param amountIn_ The exact amount of input tokens to swap
    /// @param extras_ Additional parameters for the swap:
    ///   - to: Recipient of swapped tokens. If to_ == address(0) then out tokens will be sent to msg.sender. If to_ == ADDRESS_DEAD then function will revert with amountOut_
    ///   - amountOutMin: The minimum amount of output tokens the user expects to receive
    ///   - isCallback: If true, indicates that the input tokens should be transferred via a callback
    /// @return amountOut_ The amount of output tokens received from the swap
    function _swapIn(
        bool swap0to1_,
        uint256 amountIn_,
        SwapInExtras memory extras_
    ) internal returns (uint256 amountOut_) {
        uint dexVariables_ = dexVariables;
        uint dexVariables2_ = dexVariables2;

        if ((dexVariables2_ >> 255) == 1) revert FluidDexError(ErrorTypes.DexT1__SwapAndArbitragePaused);

        _check(dexVariables_, dexVariables2_);

        if (extras_.to == address(0)) extras_.to = msg.sender;

        SwapInMemory memory s_;

        if (swap0to1_) {
            (s_.tokenIn, s_.tokenOut) = (TOKEN_0, TOKEN_1);
            unchecked {
                s_.amtInAdjusted = (amountIn_ * TOKEN_0_NUMERATOR_PRECISION) / TOKEN_0_DENOMINATOR_PRECISION;
            }
        } else {
            (s_.tokenIn, s_.tokenOut) = (TOKEN_1, TOKEN_0);
            unchecked {
                s_.amtInAdjusted = (amountIn_ * TOKEN_1_NUMERATOR_PRECISION) / TOKEN_1_DENOMINATOR_PRECISION;
            }
        }

        _verifySwapAndNonPerfectActions(s_.amtInAdjusted, amountIn_);

        PricesAndExchangePrice memory pex_ = _getPricesAndExchangePrices(dexVariables_, dexVariables2_);

        if (msg.value > 0) {
            if (msg.value != amountIn_) revert FluidDexError(ErrorTypes.DexT1__EthAndAmountInMisMatch);
            if (s_.tokenIn != NATIVE_TOKEN) revert FluidDexError(ErrorTypes.DexT1__EthSentForNonNativeSwap);
        }

        // is smart collateral pool enabled
        uint temp_ = dexVariables2_ & 1;
        // is smart debt pool enabled
        uint temp2_ = (dexVariables2_ >> 1) & 1;

        uint temp3_;
        uint temp4_;

        // extracting fee
        temp3_ = ((dexVariables2_ >> 2) & X17);
        unchecked {
            // revenueCut in 6 decimals, to have proper precision
            // if fee = 1% and revenue cut = 10% then revenueCut = 1e8 - (10000 * 10) = 99900000
            s_.revenueCut = EIGHT_DECIMALS - ((((dexVariables2_ >> 19) & X7) * temp3_));
            // fee in 4 decimals
            // 1 - fee. If fee is 1% then withoutFee will be 1e6 - 1e4
            // s_.fee => 1 - withdraw fee
            s_.fee = SIX_DECIMALS - temp3_;
        }

        CollateralReservesSwap memory cs_;
        DebtReservesSwap memory ds_;
        if (temp_ == 1) {
            // smart collateral is enabled
            {
                CollateralReserves memory c_ = _getCollateralReserves(
                    pex_.geometricMean,
                    pex_.upperRange,
                    pex_.lowerRange,
                    pex_.supplyToken0ExchangePrice,
                    pex_.supplyToken1ExchangePrice
                );
                if (swap0to1_) {
                    (
                        cs_.tokenInRealReserves,
                        cs_.tokenOutRealReserves,
                        cs_.tokenInImaginaryReserves,
                        cs_.tokenOutImaginaryReserves
                    ) = (
                        c_.token0RealReserves,
                        c_.token1RealReserves,
                        c_.token0ImaginaryReserves,
                        c_.token1ImaginaryReserves
                    );
                } else {
                    (
                        cs_.tokenInRealReserves,
                        cs_.tokenOutRealReserves,
                        cs_.tokenInImaginaryReserves,
                        cs_.tokenOutImaginaryReserves
                    ) = (
                        c_.token1RealReserves,
                        c_.token0RealReserves,
                        c_.token1ImaginaryReserves,
                        c_.token0ImaginaryReserves
                    );
                }
            }
        }

        if (temp2_ == 1) {
            // smart debt is enabled
            {
                DebtReserves memory d_ = _getDebtReserves(
                    pex_.geometricMean,
                    pex_.upperRange,
                    pex_.lowerRange,
                    pex_.borrowToken0ExchangePrice,
                    pex_.borrowToken1ExchangePrice
                );
                if (swap0to1_) {
                    (
                        ds_.tokenInDebt,
                        ds_.tokenOutDebt,
                        ds_.tokenInRealReserves,
                        ds_.tokenOutRealReserves,
                        ds_.tokenInImaginaryReserves,
                        ds_.tokenOutImaginaryReserves
                    ) = (
                        d_.token0Debt,
                        d_.token1Debt,
                        d_.token0RealReserves,
                        d_.token1RealReserves,
                        d_.token0ImaginaryReserves,
                        d_.token1ImaginaryReserves
                    );
                } else {
                    (
                        ds_.tokenInDebt,
                        ds_.tokenOutDebt,
                        ds_.tokenInRealReserves,
                        ds_.tokenOutRealReserves,
                        ds_.tokenInImaginaryReserves,
                        ds_.tokenOutImaginaryReserves
                    ) = (
                        d_.token1Debt,
                        d_.token0Debt,
                        d_.token1RealReserves,
                        d_.token0RealReserves,
                        d_.token1ImaginaryReserves,
                        d_.token0ImaginaryReserves
                    );
                }
            }
        }

        // limiting amtInAdjusted to be not more than 50% of both (collateral & debt) imaginary tokenIn reserves combined
        // basically, if this throws that means user is trying to swap 0.5x tokenIn if current tokenIn imaginary reserves is x
        // let's take x as token0 here, that means, initially the pool pricing might be:
        // token1Reserve / x and new pool pricing will become token1Reserve / 1.5x (token1Reserve will decrease after swap but for simplicity ignoring that)
        // So pool price is decreased by ~33.33% (oracle will throw error in this case as it only allows 5% price difference but better to limit it before hand)
        unchecked {
            if (s_.amtInAdjusted > ((cs_.tokenInImaginaryReserves + ds_.tokenInImaginaryReserves) / 2))
                revert FluidDexError(ErrorTypes.DexT1__SwapInLimitingAmounts);
        }

        if (temp_ == 1 && temp2_ == 1) {
            // unless both pools are enabled s_.swapRoutingAmt will be 0
            s_.swapRoutingAmt = _swapRoutingIn(
                s_.amtInAdjusted,
                cs_.tokenOutImaginaryReserves,
                cs_.tokenInImaginaryReserves,
                ds_.tokenOutImaginaryReserves,
                ds_.tokenInImaginaryReserves
            );
        }

        // In below if else statement temps are:
        // temp_ => deposit amt
        // temp2_ => withdraw amt
        // temp3_ => payback amt
        // temp4_ => borrow amt
        if (int(s_.amtInAdjusted) > s_.swapRoutingAmt && s_.swapRoutingAmt > 0) {
            // swap will route from the both pools
            // temp_ = amountInCol_
            temp_ = uint(s_.swapRoutingAmt);
            unchecked {
                // temp3_ = amountInDebt_
                temp3_ = s_.amtInAdjusted - temp_;
            }

            (temp2_, temp4_) = (0, 0);

            // debt pool price will be the same as collateral pool after the swap
            s_.withdrawTo = extras_.to;
            s_.borrowTo = extras_.to;
        } else if ((temp_ == 1 && temp2_ == 0) || (s_.swapRoutingAmt >= int(s_.amtInAdjusted))) {
            // entire swap will route through collateral pool
            (temp_, temp2_, temp3_, temp4_) = (s_.amtInAdjusted, 0, 0, 0);
            // price can slightly differ from debt pool but difference will be very small. Probably <0.01% for active DEX pools.
            s_.withdrawTo = extras_.to;
        } else if ((temp_ == 0 && temp2_ == 1) || (s_.swapRoutingAmt <= 0)) {
            // entire swap will route through debt pool
            (temp_, temp2_, temp3_, temp4_) = (0, 0, s_.amtInAdjusted, 0);
            // price can slightly differ from collateral pool but difference will be very small. Probably <0.01% for active DEX pools.
            s_.borrowTo = extras_.to;
        } else {
            // swap should never reach this point but if it does then reverting
            revert FluidDexError(ErrorTypes.DexT1__NoSwapRoute);
        }

        if (temp_ > 0) {
            // temp2_ = amountOutCol_
            temp2_ = _getAmountOut(
                ((temp_ * s_.fee) / SIX_DECIMALS),
                cs_.tokenInImaginaryReserves,
                cs_.tokenOutImaginaryReserves
            );
            swap0to1_
                ? _verifyToken1Reserves(
                    (cs_.tokenInRealReserves + temp_),
                    (cs_.tokenOutRealReserves - temp2_),
                    pex_.centerPrice,
                    MINIMUM_LIQUIDITY_SWAP
                )
                : _verifyToken0Reserves(
                    (cs_.tokenOutRealReserves - temp2_),
                    (cs_.tokenInRealReserves + temp_),
                    pex_.centerPrice,
                    MINIMUM_LIQUIDITY_SWAP
                );
        }
        if (temp3_ > 0) {
            // temp4_ = amountOutDebt_
            temp4_ = _getAmountOut(
                ((temp3_ * s_.fee) / SIX_DECIMALS),
                ds_.tokenInImaginaryReserves,
                ds_.tokenOutImaginaryReserves
            );
            swap0to1_
                ? _verifyToken1Reserves(
                    (ds_.tokenInRealReserves + temp3_),
                    (ds_.tokenOutRealReserves - temp4_),
                    pex_.centerPrice,
                    MINIMUM_LIQUIDITY_SWAP
                )
                : _verifyToken0Reserves(
                    (ds_.tokenOutRealReserves - temp4_),
                    (ds_.tokenInRealReserves + temp3_),
                    pex_.centerPrice,
                    MINIMUM_LIQUIDITY_SWAP
                );
        }

        // (temp_ + temp3_) == amountIn_ == msg.value (for native token), if there is revenue cut then this statement is not true
        temp_ = (temp_ * s_.revenueCut) / EIGHT_DECIMALS;
        temp3_ = (temp3_ * s_.revenueCut) / EIGHT_DECIMALS;

        // from whatever pool higher amount of swap is routing we are taking that as final price, does not matter much because both pools final price should be same
        if (temp_ > temp3_) {
            // new pool price from col pool
            s_.price = swap0to1_
                ? ((cs_.tokenOutImaginaryReserves - temp2_) * 1e27) / (cs_.tokenInImaginaryReserves + temp_)
                : ((cs_.tokenInImaginaryReserves + temp_) * 1e27) / (cs_.tokenOutImaginaryReserves - temp2_);
        } else {
            // new pool price from debt pool
            s_.price = swap0to1_
                ? ((ds_.tokenOutImaginaryReserves - temp4_) * 1e27) / (ds_.tokenInImaginaryReserves + temp3_)
                : ((ds_.tokenInImaginaryReserves + temp3_) * 1e27) / (ds_.tokenOutImaginaryReserves - temp4_);
        }

        // converting into normal token amounts
        if (swap0to1_) {
            temp_ = ((temp_ * TOKEN_0_DENOMINATOR_PRECISION) / TOKEN_0_NUMERATOR_PRECISION);
            temp3_ = ((temp3_ * TOKEN_0_DENOMINATOR_PRECISION) / TOKEN_0_NUMERATOR_PRECISION);
            // only adding uncheck in out amount
            unchecked {
                temp2_ = ((temp2_ * TOKEN_1_DENOMINATOR_PRECISION) / TOKEN_1_NUMERATOR_PRECISION);
                temp4_ = ((temp4_ * TOKEN_1_DENOMINATOR_PRECISION) / TOKEN_1_NUMERATOR_PRECISION);
            }
        } else {
            temp_ = ((temp_ * TOKEN_1_DENOMINATOR_PRECISION) / TOKEN_1_NUMERATOR_PRECISION);
            temp3_ = ((temp3_ * TOKEN_1_DENOMINATOR_PRECISION) / TOKEN_1_NUMERATOR_PRECISION);
            // only adding uncheck in out amount
            unchecked {
                temp2_ = ((temp2_ * TOKEN_0_DENOMINATOR_PRECISION) / TOKEN_0_NUMERATOR_PRECISION);
                temp4_ = ((temp4_ * TOKEN_0_DENOMINATOR_PRECISION) / TOKEN_0_NUMERATOR_PRECISION);
            }
        }

        unchecked {
            amountOut_ = temp2_ + temp4_;
        }

        // if address dead then reverting with amountOut
        if (extras_.to == ADDRESS_DEAD) revert FluidDexSwapResult(amountOut_);

        if (amountOut_ < extras_.amountOutMin) revert FluidDexError(ErrorTypes.DexT1__NotEnoughAmountOut);

        // allocating to avoid stack-too-deep error
        // not setting in the callbackData as last 2nd to avoid SKIP_TRANSFERS clashing
        s_.data = abi.encode(amountIn_, extras_.isCallback, msg.sender); // true/false is to decide if dex should do callback or directly transfer from user
        // deposit & payback token in at liquidity
        LIQUIDITY.operate{ value: msg.value }(s_.tokenIn, int(temp_), -int(temp3_), address(0), address(0), s_.data);
        // withdraw & borrow token out at liquidity
        LIQUIDITY.operate(s_.tokenOut, -int(temp2_), int(temp4_), s_.withdrawTo, s_.borrowTo, new bytes(0));

        // if hook exists then calling hook
        temp_ = (dexVariables2_ >> 142) & X30;
        if (temp_ > 0) {
            s_.swap0to1 = swap0to1_;
            _hookVerify(temp_, 1, s_.swap0to1, s_.price);
        }

        swap0to1_
            ? _utilizationVerify(((dexVariables2_ >> 238) & X10), EXCHANGE_PRICE_TOKEN_1_SLOT)
            : _utilizationVerify(((dexVariables2_ >> 228) & X10), EXCHANGE_PRICE_TOKEN_0_SLOT);

        dexVariables = _updateOracle(s_.price, pex_.centerPrice, dexVariables_);

        emit Swap(swap0to1_, amountIn_, amountOut_, extras_.to);
    }

    /// @dev Swap tokens with perfect amount in
    /// @param swap0to1_ Direction of swap. If true, swaps token0 for token1; if false, swaps token1 for token0
    /// @param amountIn_ The exact amount of tokens to swap in
    /// @param amountOutMin_ The minimum amount of tokens to receive after swap
    /// @param to_ Recipient of swapped tokens. If to_ == address(0) then out tokens will be sent to msg.sender. If to_ == ADDRESS_DEAD then function will revert with amountOut_
    /// @return amountOut_ The amount of output tokens received from the swap
    function swapIn(
        bool swap0to1_,
        uint256 amountIn_,
        uint256 amountOutMin_,
        address to_
    ) public payable returns (uint256 amountOut_) {
        return _swapIn(swap0to1_, amountIn_, SwapInExtras(to_, amountOutMin_, false));
    }

    /// @dev Swap tokens with perfect amount in and callback functionality
    /// @param swap0to1_ Direction of swap. If true, swaps token0 for token1; if false, swaps token1 for token0
    /// @param amountIn_ The exact amount of tokens to swap in
    /// @param amountOutMin_ The minimum amount of tokens to receive after swap
    /// @param to_ Recipient of swapped tokens. If to_ == address(0) then out tokens will be sent to msg.sender. If to_ == ADDRESS_DEAD then function will revert with amountOut_
    /// @return amountOut_ The amount of output tokens received from the swap
    function swapInWithCallback(
        bool swap0to1_,
        uint256 amountIn_,
        uint256 amountOutMin_,
        address to_
    ) public payable returns (uint256 amountOut_) {
        return _swapIn(swap0to1_, amountIn_, SwapInExtras(to_, amountOutMin_, true));
    }

    /// @dev Swap tokens with perfect amount out
    /// @param swap0to1_ Direction of swap. If true, swaps token0 for token1; if false, swaps token1 for token0
    /// @param amountOut_ The exact amount of tokens to receive after swap
    /// @param amountInMax_ Maximum amount of tokens to swap in
    /// @param to_ Recipient of swapped tokens. If to_ == address(0) then out tokens will be sent to msg.sender. If to_ == ADDRESS_DEAD then function will revert with amountIn_
    /// @return amountIn_ The amount of input tokens used for the swap
    function swapOut(
        bool swap0to1_,
        uint256 amountOut_,
        uint256 amountInMax_,
        address to_
    ) public payable returns (uint256 amountIn_) {
        return abi.decode(_spell(PERFECT_OPERATIONS_AND_SWAP_OUT_IMPLEMENTATION, msg.data), (uint256));
    }

    /// @dev Swap tokens with perfect amount out and callback functionality
    /// @param swap0to1_ Direction of swap. If true, swaps token0 for token1; if false, swaps token1 for token0
    /// @param amountOut_ The exact amount of tokens to receive after swap
    /// @param amountInMax_ Maximum amount of tokens to swap in
    /// @param to_ Recipient of swapped tokens. If to_ == address(0) then out tokens will be sent to msg.sender. If to_ == ADDRESS_DEAD then function will revert with amountIn_
    /// @return amountIn_ The amount of input tokens used for the swap
    function swapOutWithCallback(
        bool swap0to1_,
        uint256 amountOut_,
        uint256 amountInMax_,
        address to_
    ) public payable returns (uint256 amountIn_) {
        return abi.decode(_spell(PERFECT_OPERATIONS_AND_SWAP_OUT_IMPLEMENTATION, msg.data), (uint256));
    }

    /// @dev Deposit tokens in equal proportion to the current pool ratio
    /// @param shares_ The number of shares to mint
    /// @param maxToken0Deposit_ Maximum amount of token0 to deposit
    /// @param maxToken1Deposit_ Maximum amount of token1 to deposit
    /// @param estimate_ If true, function will revert with estimated deposit amounts without executing the deposit
    /// @return token0Amt_ Amount of token0 deposited
    /// @return token1Amt_ Amount of token1 deposited
    function depositPerfect(
        uint shares_,
        uint maxToken0Deposit_,
        uint maxToken1Deposit_,
        bool estimate_
    ) public payable returns (uint token0Amt_, uint token1Amt_) {
        return abi.decode(_spell(PERFECT_OPERATIONS_AND_SWAP_OUT_IMPLEMENTATION, msg.data), (uint256, uint256));
    }

    /// @dev This function allows users to withdraw a perfect amount of collateral liquidity
    /// @param shares_ The number of shares to withdraw
    /// @param minToken0Withdraw_ The minimum amount of token0 the user is willing to accept
    /// @param minToken1Withdraw_ The minimum amount of token1 the user is willing to accept
    /// @param to_ Recipient of withdrawn tokens. If to_ == address(0) then out tokens will be sent to msg.sender. If to_ == ADDRESS_DEAD then function will revert with token0Amt_ & token1Amt_
    /// @return token0Amt_ The amount of token0 withdrawn
    /// @return token1Amt_ The amount of token1 withdrawn
    function withdrawPerfect(
        uint shares_,
        uint minToken0Withdraw_,
        uint minToken1Withdraw_,
        address to_
    ) public returns (uint token0Amt_, uint token1Amt_) {
        return abi.decode(_spell(PERFECT_OPERATIONS_AND_SWAP_OUT_IMPLEMENTATION, msg.data), (uint256, uint256));
    }

    /// @dev This function allows users to borrow tokens in equal proportion to the current debt pool ratio
    /// @param shares_ The number of shares to borrow
    /// @param minToken0Borrow_ Minimum amount of token0 to borrow
    /// @param minToken1Borrow_ Minimum amount of token1 to borrow
    /// @param to_ Recipient of borrowed tokens. If to_ == address(0) then out tokens will be sent to msg.sender. If to_ == ADDRESS_DEAD then function will revert with token0Amt_ & token1Amt_
    /// @return token0Amt_ Amount of token0 borrowed
    /// @return token1Amt_ Amount of token1 borrowed
    function borrowPerfect(
        uint shares_,
        uint minToken0Borrow_,
        uint minToken1Borrow_,
        address to_
    ) public returns (uint token0Amt_, uint token1Amt_) {
        return abi.decode(_spell(PERFECT_OPERATIONS_AND_SWAP_OUT_IMPLEMENTATION, msg.data), (uint256, uint256));
    }

    /// @dev This function allows users to pay back borrowed tokens in equal proportion to the current debt pool ratio
    /// @param shares_ The number of shares to pay back
    /// @param maxToken0Payback_ Maximum amount of token0 to pay back
    /// @param maxToken1Payback_ Maximum amount of token1 to pay back
    /// @param estimate_ If true, function will revert with estimated payback amounts without executing the payback
    /// @return token0Amt_ Amount of token0 paid back
    /// @return token1Amt_ Amount of token1 paid back
    function paybackPerfect(
        uint shares_,
        uint maxToken0Payback_,
        uint maxToken1Payback_,
        bool estimate_
    ) public payable returns (uint token0Amt_, uint token1Amt_) {
        return abi.decode(_spell(PERFECT_OPERATIONS_AND_SWAP_OUT_IMPLEMENTATION, msg.data), (uint256, uint256));
    }

    /// @dev This function allows users to deposit tokens in any proportion into the col pool
    /// @param token0Amt_ The amount of token0 to deposit
    /// @param token1Amt_ The amount of token1 to deposit
    /// @param minSharesAmt_ The minimum amount of shares the user expects to receive
    /// @param estimate_ If true, function will revert with estimated shares without executing the deposit
    /// @return shares_ The amount of shares minted for the deposit
    function deposit(
        uint token0Amt_,
        uint token1Amt_,
        uint minSharesAmt_,
        bool estimate_
    ) public payable returns (uint shares_) {
        return abi.decode(_spell(COL_OPERATIONS_IMPLEMENTATION, msg.data), (uint256));
    }

    /// @dev This function allows users to withdraw tokens in any proportion from the col pool
    /// @param token0Amt_ The amount of token0 to withdraw
    /// @param token1Amt_ The amount of token1 to withdraw
    /// @param maxSharesAmt_ The maximum number of shares the user is willing to burn
    /// @param to_ Recipient of withdrawn tokens. If to_ == address(0) then out tokens will be sent to msg.sender. If to_ == ADDRESS_DEAD then function will revert with shares_
    /// @return shares_ The number of shares burned for the withdrawal
    function withdraw(
        uint token0Amt_,
        uint token1Amt_,
        uint maxSharesAmt_,
        address to_
    ) public returns (uint shares_) {
        return abi.decode(_spell(COL_OPERATIONS_IMPLEMENTATION, msg.data), (uint256));
    }

    /// @dev This function allows users to borrow tokens in any proportion from the debt pool
    /// @param token0Amt_ The amount of token0 to borrow
    /// @param token1Amt_ The amount of token1 to borrow
    /// @param maxSharesAmt_ The maximum amount of shares the user is willing to receive
    /// @param to_ Recipient of borrowed tokens. If to_ == address(0) then out tokens will be sent to msg.sender. If to_ == ADDRESS_DEAD then function will revert with shares_
    /// @return shares_ The amount of borrow shares minted to represent the borrowed amount
    function borrow(
        uint token0Amt_,
        uint token1Amt_,
        uint maxSharesAmt_,
        address to_
    ) public returns (uint shares_) {
        return abi.decode(_spell(DEBT_OPERATIONS_IMPLEMENTATION, msg.data), (uint256));
    }

    /// @dev This function allows users to payback tokens in any proportion to the debt pool
    /// @param token0Amt_ The amount of token0 to payback
    /// @param token1Amt_ The amount of token1 to payback
    /// @param minSharesAmt_ The minimum amount of shares the user expects to burn
    /// @param estimate_ If true, function will revert with estimated shares without executing the payback
    /// @return shares_ The amount of borrow shares burned for the payback
    function payback(
        uint token0Amt_,
        uint token1Amt_,
        uint minSharesAmt_,
        bool estimate_
    ) public payable returns (uint shares_) {
        return abi.decode(_spell(DEBT_OPERATIONS_IMPLEMENTATION, msg.data), (uint256));
    }

    /// @dev This function allows users to withdraw their collateral with perfect shares in one token
    /// @param shares_ The number of shares to burn for withdrawal
    /// @param minToken0_ The minimum amount of token0 the user expects to receive (set to 0 if withdrawing in token1)
    /// @param minToken1_ The minimum amount of token1 the user expects to receive (set to 0 if withdrawing in token0)
    /// @param to_ Recipient of withdrawn tokens. If to_ == address(0) then out tokens will be sent to msg.sender. If to_ == ADDRESS_DEAD then function will revert with withdrawAmt_
    /// @return withdrawAmt_ The amount of tokens withdrawn in the chosen token
    function withdrawPerfectInOneToken(
        uint shares_,
        uint minToken0_,
        uint minToken1_,
        address to_
    ) public returns (uint withdrawAmt_) {
        return abi.decode(_spell(COL_OPERATIONS_IMPLEMENTATION, msg.data), (uint256));
    }

    /// @dev This function allows users to payback their debt with perfect shares in one token
    /// @param shares_ The number of shares to burn for payback
    /// @param maxToken0_ The maximum amount of token0 the user is willing to pay (set to 0 if paying back in token1)
    /// @param maxToken1_ The maximum amount of token1 the user is willing to pay (set to 0 if paying back in token0)
    /// @param estimate_ If true, the function will revert with the estimated payback amount without executing the payback
    /// @return paybackAmt_ The amount of tokens paid back in the chosen token
    function paybackPerfectInOneToken(
        uint shares_,
        uint maxToken0_,
        uint maxToken1_,
        bool estimate_
    ) public payable returns (uint paybackAmt_) {
        return abi.decode(_spell(DEBT_OPERATIONS_IMPLEMENTATION, msg.data), (uint256));
    }

    /// @dev liquidity callback for cheaper token transfers in case of deposit or payback.
    /// only callable by Liquidity during an operation.
    function liquidityCallback(address token_, uint amount_, bytes calldata data_) external {
        if (msg.sender != address(LIQUIDITY)) revert FluidDexError(ErrorTypes.DexT1__MsgSenderNotLiquidity);
        if (dexVariables & 1 == 0) revert FluidDexError(ErrorTypes.DexT1__ReentrancyBitShouldBeOn);
        if (data_.length != 96) revert FluidDexError(ErrorTypes.DexT1__IncorrectDataLength);

        (uint amountToSend_, bool isCallback_, address from_) = abi.decode(data_, (uint, bool, address));

        if (amountToSend_ < amount_) revert FluidDexError(ErrorTypes.DexT1__AmountToSendLessThanAmount);

        if (isCallback_) {
            IDexCallback(from_).dexCallback(token_, amountToSend_);
        } else {
            SafeTransfer.safeTransferFrom(token_, from_, address(LIQUIDITY), amountToSend_);
        }
    }

    /// @dev the oracle assumes last set price of pool till the next swap happens.
    /// There's a possibility that during that time some interest is generated hence the last stored price is not the 100% correct price for the whole duration
    /// but the difference due to interest will be super low so this difference is ignored
    /// For example 2 swaps happened 10min (600 seconds) apart and 1 token has 10% higher interest than other.
    /// then that token will accrue about 10% * 600 / secondsInAYear = ~0.0002%
    /// @param secondsAgos_ array of seconds ago for which TWAP is needed. If user sends [10, 30, 60] then twaps_ will return [10-0, 30-10, 60-30]
    /// @return twaps_ twap price, lowest price (aka minima) & highest price (aka maxima) between secondsAgo checkpoints
    /// @return currentPrice_ price of pool after the most recent swap
    function oraclePrice(
        uint[] memory secondsAgos_
    ) external view returns (Oracle[] memory twaps_, uint currentPrice_) {
        OraclePriceMemory memory o_;

        uint dexVariables_ = dexVariables;

        if ((dexVariables_ >> 195) & 1 == 0) {
            revert FluidDexError(ErrorTypes.DexT1__OracleNotActive);
        }

        twaps_ = new Oracle[](secondsAgos_.length);

        uint totalTime_;
        uint time_;

        uint i;
        uint secondsAgo_ = secondsAgos_[0];

        currentPrice_ = (dexVariables_ >> 41) & X40;
        currentPrice_ = (currentPrice_ >> DEFAULT_EXPONENT_SIZE) << (currentPrice_ & DEFAULT_EXPONENT_MASK);
        uint price_ = currentPrice_;
        o_.lowestPrice1by0 = currentPrice_;
        o_.highestPrice1by0 = currentPrice_;

        uint twap1by0_;
        uint twap0by1_;

        uint j;

        o_.oracleSlot = (dexVariables_ >> 176) & X3;
        o_.oracleMap = (dexVariables_ >> 179) & X16;
        // if o_.oracleSlot == 7 then it'll enter the if statement in the below while loop
        o_.oracle = o_.oracleSlot < 7 ? _oracle[o_.oracleMap] : 0;

        uint slotData_;
        uint percentDiff_;

        if (((dexVariables_ >> 121) & X33) < block.timestamp) {
            // last swap didn't occured in this block.
            // hence last price is current price of pool & also the last price
            time_ = block.timestamp - ((dexVariables_ >> 121) & X33);
        } else {
            // last swap occured in this block, that means current price is active for 0 secs. Hence TWAP for it will be 0.
            ++j;
        }

        while (true) {
            if (j == 2) {
                if (++o_.oracleSlot == 8) {
                    o_.oracleSlot = 0;
                    if (o_.oracleMap == 0) {
                        o_.oracleMap = TOTAL_ORACLE_MAPPING;
                    }
                    o_.oracle = _oracle[--o_.oracleMap];
                }

                slotData_ = (o_.oracle >> (o_.oracleSlot * 32)) & X32;
                if (slotData_ > 0) {
                    time_ = slotData_ & X9;
                    if (time_ == 0) {
                        // time is in precision & sign bits
                        time_ = slotData_ >> 9;
                        // if o_.oracleSlot is 7 then precision & bits and stored in 1 less map
                        if (o_.oracleSlot == 7) {
                            o_.oracleSlot = 0;
                            if (o_.oracleMap == 0) {
                                o_.oracleMap = TOTAL_ORACLE_MAPPING;
                            }
                            o_.oracle = _oracle[--o_.oracleMap];
                            slotData_ = o_.oracle & X32;
                        } else {
                            ++o_.oracleSlot;
                            slotData_ = (o_.oracle >> (o_.oracleSlot * 32)) & X32;
                        }
                    }
                    percentDiff_ = slotData_ >> 10;
                    percentDiff_ = (ORACLE_LIMIT * percentDiff_) / X22;
                    if (((slotData_ >> 9) & 1 == 1)) {
                        // if positive then old price was lower than current hence subtracting
                        price_ = price_ - (price_ * percentDiff_) / ORACLE_PRECISION;
                    } else {
                        // if negative then old price was higher than current hence adding
                        price_ = price_ + (price_ * percentDiff_) / ORACLE_PRECISION;
                    }
                } else {
                    // oracle data does not exist. Probably due to pool recently got initialized and not have much swaps.
                    revert FluidDexError(ErrorTypes.DexT1__InsufficientOracleData);
                }
            } else if (j == 1) {
                // last & last to last price
                price_ = (dexVariables_ >> 1) & X40;
                price_ = (price_ >> DEFAULT_EXPONENT_SIZE) << (price_ & DEFAULT_EXPONENT_MASK);
                time_ = (dexVariables_ >> 154) & X22;
                ++j;
            } else if (j == 0) {
                ++j;
            }

            totalTime_ += time_;
            if (o_.lowestPrice1by0 > price_) o_.lowestPrice1by0 = price_;
            if (o_.highestPrice1by0 < price_) o_.highestPrice1by0 = price_;
            if (totalTime_ < secondsAgo_) {
                twap1by0_ += price_ * time_;
                twap0by1_ += (1e54 / price_) * time_;
            } else {
                time_ = time_ + secondsAgo_ - totalTime_;
                twap1by0_ += price_ * time_;
                twap0by1_ += (1e54 / price_) * time_;
                // also auto checks that secondsAgos_ should not be == 0
                twap1by0_ = twap1by0_ / secondsAgo_;
                twap0by1_ = twap0by1_ / secondsAgo_;

                twaps_[i] = Oracle(
                    twap1by0_,
                    o_.lowestPrice1by0,
                    o_.highestPrice1by0,
                    twap0by1_,
                    (1e54 / o_.highestPrice1by0),
                    (1e54 / o_.lowestPrice1by0)
                );

                // TWAP for next secondsAgo will start with price_
                o_.lowestPrice1by0 = price_;
                o_.highestPrice1by0 = price_;

                while (++i < secondsAgos_.length) {
                    // secondsAgo_ = [60, 15, 0]
                    time_ = totalTime_ - secondsAgo_;
                    // updating total time as new seconds ago started
                    totalTime_ = time_;
                    // also auto checks that secondsAgos_[i + 1] > secondsAgos_[i]
                    secondsAgo_ = secondsAgos_[i] - secondsAgos_[i - 1];
                    if (totalTime_ < secondsAgo_) {
                        twap1by0_ = price_ * time_;
                        twap0by1_ = (1e54 / price_) * time_;
                        // if time_ comes out as 0 here then lowestPrice & highestPrice should not be price_, it should be next price_ that we will calculate
                        if (time_ == 0) {
                            o_.lowestPrice1by0 = type(uint).max;
                            o_.highestPrice1by0 = 0;
                        }
                        break;
                    } else {
                        time_ = time_ + secondsAgo_ - totalTime_;
                        // twap1by0_ = price_ here
                        twap1by0_ = price_ * time_;
                        // twap0by1_ = (1e54 / price_) * time_;
                        twap0by1_ = (1e54 / price_) * time_;
                        twap1by0_ = twap1by0_ / secondsAgo_;
                        twap0by1_ = twap0by1_ / secondsAgo_;
                        twaps_[i] = Oracle(
                            twap1by0_,
                            o_.lowestPrice1by0,
                            o_.highestPrice1by0,
                            twap0by1_,
                            (1e54 / o_.highestPrice1by0),
                            (1e54 / o_.lowestPrice1by0)
                        );
                    }
                }
                if (i == secondsAgos_.length) return (twaps_, currentPrice_); // oracle fetch over
            }
        }
    }

    function getPricesAndExchangePrices() public {
        uint dexVariables_ = dexVariables;
        uint dexVariables2_ = dexVariables2;

        _check(dexVariables_, dexVariables2_);

        PricesAndExchangePrice memory pex_ = _getPricesAndExchangePrices(dexVariables, dexVariables2);

        revert FluidDexPricesAndExchangeRates(pex_);
    }

    /// @dev Internal fallback function to handle calls to non-existent functions
    /// @notice This function is called when a transaction is sent to the contract without matching any other function
    /// @notice It checks if the caller is authorized, enables re-entrancy protection, delegates the call to the admin implementation, and then disables re-entrancy protection
    /// @notice Only authorized callers (global or dex auth) can trigger this function
    /// @notice This function uses assembly to perform a delegatecall to the admin implementation to update configs related to DEX
    function _fallback() private {
        if (!(DEX_FACTORY.isGlobalAuth(msg.sender) || DEX_FACTORY.isDexAuth(address(this), msg.sender))) {
            revert FluidDexError(ErrorTypes.DexT1__NotAnAuth);
        }

        uint dexVariables_ = dexVariables;
        if (dexVariables_ & 1 == 1) revert FluidDexError(ErrorTypes.DexT1__AlreadyEntered);
        // enabling re-entrancy
        dexVariables = dexVariables_ | 1;

        // Delegate the current call to `ADMIN_IMPLEMENTATION`.
        _spell(ADMIN_IMPLEMENTATION, msg.data);

        // disabling re-entrancy
        // directly fetching from storage so updates from Admin module will get auto covered
        dexVariables = dexVariables & ~uint(1);
    }

    fallback() external payable {
        _fallback();
    }

    receive() external payable {
        if (msg.sig != 0x00000000) {
            _fallback();
        }
    }

    /// @notice returns all Vault constants
    function constantsView() external view returns (ConstantViews memory constantsView_) {
        constantsView_.dexId = DEX_ID;
        constantsView_.liquidity = address(LIQUIDITY);
        constantsView_.factory = address(DEX_FACTORY);
        constantsView_.token0 = TOKEN_0;
        constantsView_.token1 = TOKEN_1;
        constantsView_.implementations.shift = SHIFT_IMPLEMENTATION;
        constantsView_.implementations.admin = ADMIN_IMPLEMENTATION;
        constantsView_.implementations.colOperations = COL_OPERATIONS_IMPLEMENTATION;
        constantsView_.implementations.debtOperations = DEBT_OPERATIONS_IMPLEMENTATION;
        constantsView_.implementations.perfectOperationsAndSwapOut = PERFECT_OPERATIONS_AND_SWAP_OUT_IMPLEMENTATION;
        constantsView_.deployerContract = DEPLOYER_CONTRACT;
        constantsView_.supplyToken0Slot = SUPPLY_TOKEN_0_SLOT;
        constantsView_.borrowToken0Slot = BORROW_TOKEN_0_SLOT;
        constantsView_.supplyToken1Slot = SUPPLY_TOKEN_1_SLOT;
        constantsView_.borrowToken1Slot = BORROW_TOKEN_1_SLOT;
        constantsView_.exchangePriceToken0Slot = EXCHANGE_PRICE_TOKEN_0_SLOT;
        constantsView_.exchangePriceToken1Slot = EXCHANGE_PRICE_TOKEN_1_SLOT;
        constantsView_.oracleMapping = TOTAL_ORACLE_MAPPING;
    }

    /// @notice returns all Vault constants
    function constantsView2() external view returns (ConstantViews2 memory constantsView2_) {
        constantsView2_.token0NumeratorPrecision = TOKEN_0_NUMERATOR_PRECISION;
        constantsView2_.token0DenominatorPrecision = TOKEN_0_DENOMINATOR_PRECISION;
        constantsView2_.token1NumeratorPrecision = TOKEN_1_NUMERATOR_PRECISION;
        constantsView2_.token1DenominatorPrecision = TOKEN_1_DENOMINATOR_PRECISION;
    }

    /// @notice Calculates the real and imaginary reserves for collateral tokens
    /// @dev This function retrieves the supply of both tokens from the liquidity layer,
    ///      adjusts them based on exchange prices, and calculates imaginary reserves
    ///      based on the geometric mean and price range
    /// @param geometricMean_ The geometric mean of the token prices
    /// @param upperRange_ The upper price range
    /// @param lowerRange_ The lower price range
    /// @param token0SupplyExchangePrice_ The exchange price for token0 from liquidity layer
    /// @param token1SupplyExchangePrice_ The exchange price for token1 from liquidity layer
    /// @return c_ A struct containing the calculated real and imaginary reserves for both tokens:
    ///         - token0RealReserves: The real reserves of token0
    ///         - token1RealReserves: The real reserves of token1
    ///         - token0ImaginaryReserves: The imaginary reserves of token0
    ///         - token1ImaginaryReserves: The imaginary reserves of token1
    function getCollateralReserves(
        uint geometricMean_,
        uint upperRange_,
        uint lowerRange_,
        uint token0SupplyExchangePrice_,
        uint token1SupplyExchangePrice_
    ) public view returns (CollateralReserves memory c_) {
        return
            _getCollateralReserves(
                geometricMean_,
                upperRange_,
                lowerRange_,
                token0SupplyExchangePrice_,
                token1SupplyExchangePrice_
            );
    }

    /// @notice Calculates the debt reserves for both tokens
    /// @param geometricMean_ The geometric mean of the upper and lower price ranges
    /// @param upperRange_ The upper price range
    /// @param lowerRange_ The lower price range
    /// @param token0BorrowExchangePrice_ The exchange price of token0 from liquidity layer
    /// @param token1BorrowExchangePrice_ The exchange price of token1 from liquidity layer
    /// @return d_ The calculated debt reserves for both tokens, containing:
    ///         - token0Debt: The debt amount of token0
    ///         - token1Debt: The debt amount of token1
    ///         - token0RealReserves: The real reserves of token0 derived from token1 debt
    ///         - token1RealReserves: The real reserves of token1 derived from token0 debt
    ///         - token0ImaginaryReserves: The imaginary debt reserves of token0
    ///         - token1ImaginaryReserves: The imaginary debt reserves of token1
    function getDebtReserves(
        uint geometricMean_,
        uint upperRange_,
        uint lowerRange_,
        uint token0BorrowExchangePrice_,
        uint token1BorrowExchangePrice_
    ) public view returns (DebtReserves memory d_) {
        return
            _getDebtReserves(
                geometricMean_,
                upperRange_,
                lowerRange_,
                token0BorrowExchangePrice_,
                token1BorrowExchangePrice_
            );
    }
}

File 2 of 25 : iProxy.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

interface IProxy {
    function setAdmin(address newAdmin_) external;

    function setDummyImplementation(address newDummyImplementation_) external;

    function addImplementation(address implementation_, bytes4[] calldata sigs_) external;

    function removeImplementation(address implementation_) external;

    function getAdmin() external view returns (address);

    function getDummyImplementation() external view returns (address);

    function getImplementationSigs(address impl_) external view returns (bytes4[] memory);

    function getSigsImplementation(bytes4 sig_) external view returns (address);

    function readFromStorage(bytes32 slot_) external view returns (uint256 result_);
}

File 3 of 25 : addressCalcs.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;

/// @notice implements calculation of address for contracts deployed through CREATE.
/// Accepts contract deployed from which address & nonce
library AddressCalcs {

    /// @notice                         Computes the address of a contract based
    /// @param deployedFrom_            Address from which the contract was deployed
    /// @param nonce_                   Nonce at which the contract was deployed
    /// @return contract_               Address of deployed contract
    function addressCalc(address deployedFrom_, uint nonce_) internal pure returns (address contract_) {
        // @dev based on https://ethereum.stackexchange.com/a/61413

        // nonce of smart contract always starts with 1. so, with nonce 0 there won't be any deployment
        // hence, nonce of vault deployment starts with 1.
        bytes memory data;
        if (nonce_ == 0x00) {
            return address(0);
        } else if (nonce_ <= 0x7f) {
            data = abi.encodePacked(bytes1(0xd6), bytes1(0x94), deployedFrom_, uint8(nonce_));
        } else if (nonce_ <= 0xff) {
            data = abi.encodePacked(bytes1(0xd7), bytes1(0x94), deployedFrom_, bytes1(0x81), uint8(nonce_));
        } else if (nonce_ <= 0xffff) {
            data = abi.encodePacked(bytes1(0xd8), bytes1(0x94), deployedFrom_, bytes1(0x82), uint16(nonce_));
        } else if (nonce_ <= 0xffffff) {
            data = abi.encodePacked(bytes1(0xd9), bytes1(0x94), deployedFrom_, bytes1(0x83), uint24(nonce_));
        } else {
            data = abi.encodePacked(bytes1(0xda), bytes1(0x94), deployedFrom_, bytes1(0x84), uint32(nonce_));
        }

        return address(uint160(uint256(keccak256(data))));
    }

}

File 4 of 25 : bigMathMinified.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;

/// @title library that represents a number in BigNumber(coefficient and exponent) format to store in smaller bits.
/// @notice the number is divided into two parts: a coefficient and an exponent. This comes at a cost of losing some precision
/// at the end of the number because the exponent simply fills it with zeroes. This precision is oftentimes negligible and can
/// result in significant gas cost reduction due to storage space reduction.
/// Also note, a valid big number is as follows: if the exponent is > 0, then coefficient last bits should be occupied to have max precision.
/// @dev roundUp is more like a increase 1, which happens everytime for the same number.
/// roundDown simply sets trailing digits after coefficientSize to zero (floor), only once for the same number.
library BigMathMinified {
    /// @dev constants to use for `roundUp` input param to increase readability
    bool internal constant ROUND_DOWN = false;
    bool internal constant ROUND_UP = true;

    /// @dev converts `normal` number to BigNumber with `exponent` and `coefficient` (or precision).
    /// e.g.:
    /// 5035703444687813576399599 (normal) = (coefficient[32bits], exponent[8bits])[40bits]
    /// 5035703444687813576399599 (decimal) => 10000101010010110100000011111011110010100110100000000011100101001101001101011101111 (binary)
    ///                                     => 10000101010010110100000011111011000000000000000000000000000000000000000000000000000
    ///                                                                        ^-------------------- 51(exponent) -------------- ^
    /// coefficient = 1000,0101,0100,1011,0100,0000,1111,1011               (2236301563)
    /// exponent =                                            0011,0011     (51)
    /// bigNumber =   1000,0101,0100,1011,0100,0000,1111,1011,0011,0011     (572493200179)
    ///
    /// @param normal number which needs to be converted into Big Number
    /// @param coefficientSize at max how many bits of precision there should be (64 = uint64 (64 bits precision))
    /// @param exponentSize at max how many bits of exponent there should be (8 = uint8 (8 bits exponent))
    /// @param roundUp signals if result should be rounded down or up
    /// @return bigNumber converted bigNumber (coefficient << exponent)
    function toBigNumber(
        uint256 normal,
        uint256 coefficientSize,
        uint256 exponentSize,
        bool roundUp
    ) internal pure returns (uint256 bigNumber) {
        assembly {
            let lastBit_
            let number_ := normal
            if gt(number_, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) {
                number_ := shr(0x80, number_)
                lastBit_ := 0x80
            }
            if gt(number_, 0xFFFFFFFFFFFFFFFF) {
                number_ := shr(0x40, number_)
                lastBit_ := add(lastBit_, 0x40)
            }
            if gt(number_, 0xFFFFFFFF) {
                number_ := shr(0x20, number_)
                lastBit_ := add(lastBit_, 0x20)
            }
            if gt(number_, 0xFFFF) {
                number_ := shr(0x10, number_)
                lastBit_ := add(lastBit_, 0x10)
            }
            if gt(number_, 0xFF) {
                number_ := shr(0x8, number_)
                lastBit_ := add(lastBit_, 0x8)
            }
            if gt(number_, 0xF) {
                number_ := shr(0x4, number_)
                lastBit_ := add(lastBit_, 0x4)
            }
            if gt(number_, 0x3) {
                number_ := shr(0x2, number_)
                lastBit_ := add(lastBit_, 0x2)
            }
            if gt(number_, 0x1) {
                lastBit_ := add(lastBit_, 1)
            }
            if gt(number_, 0) {
                lastBit_ := add(lastBit_, 1)
            }
            if lt(lastBit_, coefficientSize) {
                // for throw exception
                lastBit_ := coefficientSize
            }
            let exponent := sub(lastBit_, coefficientSize)
            let coefficient := shr(exponent, normal)
            if and(roundUp, gt(exponent, 0)) {
                // rounding up is only needed if exponent is > 0, as otherwise the coefficient fully holds the original number
                coefficient := add(coefficient, 1)
                if eq(shl(coefficientSize, 1), coefficient) {
                    // case were coefficient was e.g. 111, with adding 1 it became 1000 (in binary) and coefficientSize 3 bits
                    // final coefficient would exceed it's size. -> reduce coefficent to 100 and increase exponent by 1.
                    coefficient := shl(sub(coefficientSize, 1), 1)
                    exponent := add(exponent, 1)
                }
            }
            if iszero(lt(exponent, shl(exponentSize, 1))) {
                // if exponent is >= exponentSize, the normal number is too big to fit within
                // BigNumber with too small sizes for coefficient and exponent
                revert(0, 0)
            }
            bigNumber := shl(exponentSize, coefficient)
            bigNumber := add(bigNumber, exponent)
        }
    }

    /// @dev get `normal` number from `bigNumber`, `exponentSize` and `exponentMask`
    function fromBigNumber(
        uint256 bigNumber,
        uint256 exponentSize,
        uint256 exponentMask
    ) internal pure returns (uint256 normal) {
        assembly {
            let coefficient := shr(exponentSize, bigNumber)
            let exponent := and(bigNumber, exponentMask)
            normal := shl(exponent, coefficient)
        }
    }

    /// @dev gets the most significant bit `lastBit` of a `normal` number (length of given number of binary format).
    /// e.g.
    /// 5035703444687813576399599 = 10000101010010110100000011111011110010100110100000000011100101001101001101011101111
    /// lastBit =                   ^---------------------------------   83   ----------------------------------------^
    function mostSignificantBit(uint256 normal) internal pure returns (uint lastBit) {
        assembly {
            let number_ := normal
            if gt(normal, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) {
                number_ := shr(0x80, number_)
                lastBit := 0x80
            }
            if gt(number_, 0xFFFFFFFFFFFFFFFF) {
                number_ := shr(0x40, number_)
                lastBit := add(lastBit, 0x40)
            }
            if gt(number_, 0xFFFFFFFF) {
                number_ := shr(0x20, number_)
                lastBit := add(lastBit, 0x20)
            }
            if gt(number_, 0xFFFF) {
                number_ := shr(0x10, number_)
                lastBit := add(lastBit, 0x10)
            }
            if gt(number_, 0xFF) {
                number_ := shr(0x8, number_)
                lastBit := add(lastBit, 0x8)
            }
            if gt(number_, 0xF) {
                number_ := shr(0x4, number_)
                lastBit := add(lastBit, 0x4)
            }
            if gt(number_, 0x3) {
                number_ := shr(0x2, number_)
                lastBit := add(lastBit, 0x2)
            }
            if gt(number_, 0x1) {
                lastBit := add(lastBit, 1)
            }
            if gt(number_, 0) {
                lastBit := add(lastBit, 1)
            }
        }
    }
}

File 5 of 25 : dexCalcs.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;

import { BigMathMinified } from "./bigMathMinified.sol";
import { DexSlotsLink } from "./dexSlotsLink.sol";

// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// @DEV ATTENTION: ON ANY CHANGES HERE, MAKE SURE THAT LOGIC IN VAULTS WILL STILL BE VALID.
// SOME CODE THERE ASSUMES DEXCALCS == LIQUIDITYCALCS.
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

/// @notice implements calculation methods used for Fluid Dex such as updated withdrawal / borrow limits.
library DexCalcs {
    // constants used for BigMath conversion from and to storage
    uint256 internal constant DEFAULT_EXPONENT_SIZE = 8;
    uint256 internal constant DEFAULT_EXPONENT_MASK = 0xFF;

    uint256 internal constant FOUR_DECIMALS = 1e4;
    uint256 internal constant X14 = 0x3fff;
    uint256 internal constant X18 = 0x3ffff;
    uint256 internal constant X24 = 0xffffff;
    uint256 internal constant X33 = 0x1ffffffff;
    uint256 internal constant X64 = 0xffffffffffffffff;

    ///////////////////////////////////////////////////////////////////////////
    //////////                      CALC LIMITS                       /////////
    ///////////////////////////////////////////////////////////////////////////

    /// @dev calculates withdrawal limit before an operate execution:
    /// amount of user supply that must stay supplied (not amount that can be withdrawn).
    /// i.e. if user has supplied 100m and can withdraw 5M, this method returns the 95M, not the withdrawable amount 5M
    /// @param userSupplyData_ user supply data packed uint256 from storage
    /// @param userSupply_ current user supply amount already extracted from `userSupplyData_` and converted from BigMath
    /// @return currentWithdrawalLimit_ current withdrawal limit updated for expansion since last interaction.
    ///         returned value is in raw for with interest mode, normal amount for interest free mode!
    function calcWithdrawalLimitBeforeOperate(
        uint256 userSupplyData_,
        uint256 userSupply_
    ) internal view returns (uint256 currentWithdrawalLimit_) {
        // @dev must support handling the case where timestamp is 0 (config is set but no interactions yet).
        // first tx where timestamp is 0 will enter `if (lastWithdrawalLimit_ == 0)` because lastWithdrawalLimit_ is not set yet.
        // returning max withdrawal allowed, which is not exactly right but doesn't matter because the first interaction must be
        // a deposit anyway. Important is that it would not revert.

        // Note the first time a deposit brings the user supply amount to above the base withdrawal limit, the active limit
        // is the fully expanded limit immediately.

        // extract last set withdrawal limit
        uint256 lastWithdrawalLimit_ = (userSupplyData_ >> DexSlotsLink.BITS_USER_SUPPLY_PREVIOUS_WITHDRAWAL_LIMIT) &
            X64;
        lastWithdrawalLimit_ =
            (lastWithdrawalLimit_ >> DEFAULT_EXPONENT_SIZE) <<
            (lastWithdrawalLimit_ & DEFAULT_EXPONENT_MASK);
        if (lastWithdrawalLimit_ == 0) {
            // withdrawal limit is not activated. Max withdrawal allowed
            return 0;
        }

        uint256 maxWithdrawableLimit_;
        uint256 temp_;
        unchecked {
            // extract max withdrawable percent of user supply and
            // calculate maximum withdrawable amount expandPercentage of user supply at full expansion duration elapsed
            // e.g.: if 10% expandPercentage, meaning 10% is withdrawable after full expandDuration has elapsed.

            // userSupply_ needs to be atleast 1e73 to overflow max limit of ~1e77 in uint256 (no token in existence where this is possible).
            maxWithdrawableLimit_ =
                (((userSupplyData_ >> DexSlotsLink.BITS_USER_SUPPLY_EXPAND_PERCENT) & X14) * userSupply_) /
                FOUR_DECIMALS;

            // time elapsed since last withdrawal limit was set (in seconds)
            // @dev last process timestamp is guaranteed to exist for withdrawal, as a supply must have happened before.
            // last timestamp can not be > current timestamp
            temp_ = block.timestamp - ((userSupplyData_ >> DexSlotsLink.BITS_USER_SUPPLY_LAST_UPDATE_TIMESTAMP) & X33);
        }
        // calculate withdrawable amount of expandPercent that is elapsed of expandDuration.
        // e.g. if 60% of expandDuration has elapsed, then user should be able to withdraw 6% of user supply, down to 94%.
        // Note: no explicit check for this needed, it is covered by setting minWithdrawalLimit_ if needed.
        temp_ =
            (maxWithdrawableLimit_ * temp_) /
            // extract expand duration: After this, decrement won't happen (user can withdraw 100% of withdraw limit)
            ((userSupplyData_ >> DexSlotsLink.BITS_USER_SUPPLY_EXPAND_DURATION) & X24); // expand duration can never be 0
        // calculate expanded withdrawal limit: last withdrawal limit - withdrawable amount.
        // Note: withdrawable amount here can grow bigger than userSupply if timeElapsed is a lot bigger than expandDuration,
        // which would cause the subtraction `lastWithdrawalLimit_ - withdrawableAmount_` to revert. In that case, set 0
        // which will cause minimum (fully expanded) withdrawal limit to be set in lines below.
        unchecked {
            // underflow explicitly checked & handled
            currentWithdrawalLimit_ = lastWithdrawalLimit_ > temp_ ? lastWithdrawalLimit_ - temp_ : 0;
            // calculate minimum withdrawal limit: minimum amount of user supply that must stay supplied at full expansion.
            // subtraction can not underflow as maxWithdrawableLimit_ is a percentage amount (<=100%) of userSupply_
            temp_ = userSupply_ - maxWithdrawableLimit_;
        }
        // if withdrawal limit is decreased below minimum then set minimum
        // (e.g. when more than expandDuration time has elapsed)
        if (temp_ > currentWithdrawalLimit_) {
            currentWithdrawalLimit_ = temp_;
        }
    }

    /// @dev calculates withdrawal limit after an operate execution:
    /// amount of user supply that must stay supplied (not amount that can be withdrawn).
    /// i.e. if user has supplied 100m and can withdraw 5M, this method returns the 95M, not the withdrawable amount 5M
    /// @param userSupplyData_ user supply data packed uint256 from storage
    /// @param userSupply_ current user supply amount already extracted from `userSupplyData_` and added / subtracted with the executed operate amount
    /// @param newWithdrawalLimit_ current withdrawal limit updated for expansion since last interaction, result from `calcWithdrawalLimitBeforeOperate`
    /// @return withdrawalLimit_ updated withdrawal limit that should be written to storage. returned value is in
    ///                          raw for with interest mode, normal amount for interest free mode!
    function calcWithdrawalLimitAfterOperate(
        uint256 userSupplyData_,
        uint256 userSupply_,
        uint256 newWithdrawalLimit_
    ) internal pure returns (uint256) {
        // temp_ => base withdrawal limit. below this, maximum withdrawals are allowed
        uint256 temp_ = (userSupplyData_ >> DexSlotsLink.BITS_USER_SUPPLY_BASE_WITHDRAWAL_LIMIT) & X18;
        temp_ = (temp_ >> DEFAULT_EXPONENT_SIZE) << (temp_ & DEFAULT_EXPONENT_MASK);

        // if user supply is below base limit then max withdrawals are allowed
        if (userSupply_ < temp_) {
            return 0;
        }
        // temp_ => withdrawal limit expandPercent (is in 1e2 decimals)
        temp_ = (userSupplyData_ >> DexSlotsLink.BITS_USER_SUPPLY_EXPAND_PERCENT) & X14;
        unchecked {
            // temp_ => minimum withdrawal limit: userSupply - max withdrawable limit (userSupply * expandPercent))
            // userSupply_ needs to be atleast 1e73 to overflow max limit of ~1e77 in uint256 (no token in existence where this is possible).
            // subtraction can not underflow as maxWithdrawableLimit_ is a percentage amount (<=100%) of userSupply_
            temp_ = userSupply_ - ((userSupply_ * temp_) / FOUR_DECIMALS);
        }
        // if new (before operation) withdrawal limit is less than minimum limit then set minimum limit.
        // e.g. can happen on new deposits. withdrawal limit is instantly fully expanded in a scenario where
        // increased deposit amount outpaces withrawals.
        if (temp_ > newWithdrawalLimit_) {
            return temp_;
        }
        return newWithdrawalLimit_;
    }

    /// @dev calculates borrow limit before an operate execution:
    /// total amount user borrow can reach (not borrowable amount in current operation).
    /// i.e. if user has borrowed 50M and can still borrow 5M, this method returns the total 55M, not the borrowable amount 5M
    /// @param userBorrowData_ user borrow data packed uint256 from storage
    /// @param userBorrow_ current user borrow amount already extracted from `userBorrowData_`
    /// @return currentBorrowLimit_ current borrow limit updated for expansion since last interaction. returned value is in
    ///                             raw for with interest mode, normal amount for interest free mode!
    function calcBorrowLimitBeforeOperate(
        uint256 userBorrowData_,
        uint256 userBorrow_
    ) internal view returns (uint256 currentBorrowLimit_) {
        // @dev must support handling the case where timestamp is 0 (config is set but no interactions yet) -> base limit.
        // first tx where timestamp is 0 will enter `if (maxExpandedBorrowLimit_ < baseBorrowLimit_)` because `userBorrow_` and thus
        // `maxExpansionLimit_` and thus `maxExpandedBorrowLimit_` is 0 and `baseBorrowLimit_` can not be 0.

        // temp_ = extract borrow expand percent (is in 1e2 decimals)
        uint256 temp_ = (userBorrowData_ >> DexSlotsLink.BITS_USER_BORROW_EXPAND_PERCENT) & X14;

        uint256 maxExpansionLimit_;
        uint256 maxExpandedBorrowLimit_;
        unchecked {
            // calculate max expansion limit: Max amount limit can expand to since last interaction
            // userBorrow_ needs to be atleast 1e73 to overflow max limit of ~1e77 in uint256 (no token in existence where this is possible).
            maxExpansionLimit_ = ((userBorrow_ * temp_) / FOUR_DECIMALS);

            // calculate max borrow limit: Max point limit can increase to since last interaction
            maxExpandedBorrowLimit_ = userBorrow_ + maxExpansionLimit_;
        }

        // currentBorrowLimit_ = extract base borrow limit
        currentBorrowLimit_ = (userBorrowData_ >> DexSlotsLink.BITS_USER_BORROW_BASE_BORROW_LIMIT) & X18;
        currentBorrowLimit_ =
            (currentBorrowLimit_ >> DEFAULT_EXPONENT_SIZE) <<
            (currentBorrowLimit_ & DEFAULT_EXPONENT_MASK);

        if (maxExpandedBorrowLimit_ < currentBorrowLimit_) {
            return currentBorrowLimit_;
        }
        // time elapsed since last borrow limit was set (in seconds)
        unchecked {
            // temp_ = timeElapsed_ (last timestamp can not be > current timestamp)
            temp_ = block.timestamp - ((userBorrowData_ >> DexSlotsLink.BITS_USER_BORROW_LAST_UPDATE_TIMESTAMP) & X33); // extract last update timestamp
        }

        // currentBorrowLimit_ = expandedBorrowableAmount + extract last set borrow limit
        currentBorrowLimit_ =
            // calculate borrow limit expansion since last interaction for `expandPercent` that is elapsed of `expandDuration`.
            // divisor is extract expand duration (after this, full expansion to expandPercentage happened).
            ((maxExpansionLimit_ * temp_) /
                ((userBorrowData_ >> DexSlotsLink.BITS_USER_BORROW_EXPAND_DURATION) & X24)) + // expand duration can never be 0
            //  extract last set borrow limit
            BigMathMinified.fromBigNumber(
                (userBorrowData_ >> DexSlotsLink.BITS_USER_BORROW_PREVIOUS_BORROW_LIMIT) & X64,
                DEFAULT_EXPONENT_SIZE,
                DEFAULT_EXPONENT_MASK
            );

        // if timeElapsed is bigger than expandDuration, new borrow limit would be > max expansion,
        // so set to `maxExpandedBorrowLimit_` in that case.
        // also covers the case where last process timestamp = 0 (timeElapsed would simply be very big)
        if (currentBorrowLimit_ > maxExpandedBorrowLimit_) {
            currentBorrowLimit_ = maxExpandedBorrowLimit_;
        }
        // temp_ = extract hard max borrow limit. Above this user can never borrow (not expandable above)
        temp_ = (userBorrowData_ >> DexSlotsLink.BITS_USER_BORROW_MAX_BORROW_LIMIT) & X18;
        temp_ = (temp_ >> DEFAULT_EXPONENT_SIZE) << (temp_ & DEFAULT_EXPONENT_MASK);

        if (currentBorrowLimit_ > temp_) {
            currentBorrowLimit_ = temp_;
        }
    }

    /// @dev calculates borrow limit after an operate execution:
    /// total amount user borrow can reach (not borrowable amount in current operation).
    /// i.e. if user has borrowed 50M and can still borrow 5M, this method returns the total 55M, not the borrowable amount 5M
    /// @param userBorrowData_ user borrow data packed uint256 from storage
    /// @param userBorrow_ current user borrow amount already extracted from `userBorrowData_` and added / subtracted with the executed operate amount
    /// @param newBorrowLimit_ current borrow limit updated for expansion since last interaction, result from `calcBorrowLimitBeforeOperate`
    /// @return borrowLimit_ updated borrow limit that should be written to storage.
    ///                      returned value is in raw for with interest mode, normal amount for interest free mode!
    function calcBorrowLimitAfterOperate(
        uint256 userBorrowData_,
        uint256 userBorrow_,
        uint256 newBorrowLimit_
    ) internal pure returns (uint256 borrowLimit_) {
        // temp_ = extract borrow expand percent
        uint256 temp_ = (userBorrowData_ >> DexSlotsLink.BITS_USER_BORROW_EXPAND_PERCENT) & X14; // (is in 1e2 decimals)

        unchecked {
            // borrowLimit_ = calculate maximum borrow limit at full expansion.
            // userBorrow_ needs to be at least 1e73 to overflow max limit of ~1e77 in uint256 (no token in existence where this is possible).
            borrowLimit_ = userBorrow_ + ((userBorrow_ * temp_) / FOUR_DECIMALS);
        }

        // temp_ = extract base borrow limit
        temp_ = (userBorrowData_ >> DexSlotsLink.BITS_USER_BORROW_BASE_BORROW_LIMIT) & X18;
        temp_ = (temp_ >> DEFAULT_EXPONENT_SIZE) << (temp_ & DEFAULT_EXPONENT_MASK);

        if (borrowLimit_ < temp_) {
            // below base limit, borrow limit is always base limit
            return temp_;
        }
        // temp_ = extract hard max borrow limit. Above this user can never borrow (not expandable above)
        temp_ = (userBorrowData_ >> DexSlotsLink.BITS_USER_BORROW_MAX_BORROW_LIMIT) & X18;
        temp_ = (temp_ >> DEFAULT_EXPONENT_SIZE) << (temp_ & DEFAULT_EXPONENT_MASK);

        // make sure fully expanded borrow limit is not above hard max borrow limit
        if (borrowLimit_ > temp_) {
            borrowLimit_ = temp_;
        }
        // if new borrow limit (from before operate) is > max borrow limit, set max borrow limit.
        // (e.g. on a repay shrinking instantly to fully expanded borrow limit from new borrow amount. shrinking is instant)
        if (newBorrowLimit_ > borrowLimit_) {
            return borrowLimit_;
        }
        return newBorrowLimit_;
    }
}

File 6 of 25 : dexSlotsLink.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;

/// @notice library that helps in reading / working with storage slot data of Fluid Dex.
/// @dev as all data for Fluid Dex is internal, any data must be fetched directly through manual
/// slot reading through this library or, if gas usage is less important, through the FluidDexResolver.
library DexSlotsLink {
    /// @dev storage slot for variables at Dex
    uint256 internal constant DEX_VARIABLES_SLOT = 0;
    /// @dev storage slot for variables2 at Dex
    uint256 internal constant DEX_VARIABLES2_SLOT = 1;
    /// @dev storage slot for total supply shares at Dex
    uint256 internal constant DEX_TOTAL_SUPPLY_SHARES_SLOT = 2;
    /// @dev storage slot for user supply mapping at Dex
    uint256 internal constant DEX_USER_SUPPLY_MAPPING_SLOT = 3;
    /// @dev storage slot for total borrow shares at Dex
    uint256 internal constant DEX_TOTAL_BORROW_SHARES_SLOT = 4;
    /// @dev storage slot for user borrow mapping at Dex
    uint256 internal constant DEX_USER_BORROW_MAPPING_SLOT = 5;
    /// @dev storage slot for oracle mapping at Dex
    uint256 internal constant DEX_ORACLE_MAPPING_SLOT = 6;
    /// @dev storage slot for range and threshold shifts at Dex
    uint256 internal constant DEX_RANGE_THRESHOLD_SHIFTS_SLOT = 7;
    /// @dev storage slot for center price shift at Dex
    uint256 internal constant DEX_CENTER_PRICE_SHIFT_SLOT = 8;

    // --------------------------------
    // @dev stacked uint256 storage slots bits position data for each:

    // UserSupplyData
    uint256 internal constant BITS_USER_SUPPLY_ALLOWED = 0;
    uint256 internal constant BITS_USER_SUPPLY_AMOUNT = 1;
    uint256 internal constant BITS_USER_SUPPLY_PREVIOUS_WITHDRAWAL_LIMIT = 65;
    uint256 internal constant BITS_USER_SUPPLY_LAST_UPDATE_TIMESTAMP = 129;
    uint256 internal constant BITS_USER_SUPPLY_EXPAND_PERCENT = 162;
    uint256 internal constant BITS_USER_SUPPLY_EXPAND_DURATION = 176;
    uint256 internal constant BITS_USER_SUPPLY_BASE_WITHDRAWAL_LIMIT = 200;

    // UserBorrowData
    uint256 internal constant BITS_USER_BORROW_ALLOWED = 0;
    uint256 internal constant BITS_USER_BORROW_AMOUNT = 1;
    uint256 internal constant BITS_USER_BORROW_PREVIOUS_BORROW_LIMIT = 65;
    uint256 internal constant BITS_USER_BORROW_LAST_UPDATE_TIMESTAMP = 129;
    uint256 internal constant BITS_USER_BORROW_EXPAND_PERCENT = 162;
    uint256 internal constant BITS_USER_BORROW_EXPAND_DURATION = 176;
    uint256 internal constant BITS_USER_BORROW_BASE_BORROW_LIMIT = 200;
    uint256 internal constant BITS_USER_BORROW_MAX_BORROW_LIMIT = 218;

    // --------------------------------

    /// @notice Calculating the slot ID for Dex contract for single mapping at `slot_` for `key_`
    function calculateMappingStorageSlot(uint256 slot_, address key_) internal pure returns (bytes32) {
        return keccak256(abi.encode(key_, slot_));
    }

    /// @notice Calculating the slot ID for Dex contract for double mapping at `slot_` for `key1_` and `key2_`
    function calculateDoubleMappingStorageSlot(
        uint256 slot_,
        address key1_,
        address key2_
    ) internal pure returns (bytes32) {
        bytes32 intermediateSlot_ = keccak256(abi.encode(key1_, slot_));
        return keccak256(abi.encode(key2_, intermediateSlot_));
    }
}

File 7 of 25 : errorTypes.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;

library LibsErrorTypes {
    /***********************************|
    |         LiquidityCalcs            | 
    |__________________________________*/

    /// @notice thrown when supply or borrow exchange price is zero at calc token data (token not configured yet)
    uint256 internal constant LiquidityCalcs__ExchangePriceZero = 70001;

    /// @notice thrown when rate data is set to a version that is not implemented
    uint256 internal constant LiquidityCalcs__UnsupportedRateVersion = 70002;

    /// @notice thrown when the calculated borrow rate turns negative. This should never happen.
    uint256 internal constant LiquidityCalcs__BorrowRateNegative = 70003;

    /***********************************|
    |           SafeTransfer            | 
    |__________________________________*/

    /// @notice thrown when safe transfer from for an ERC20 fails
    uint256 internal constant SafeTransfer__TransferFromFailed = 71001;

    /// @notice thrown when safe transfer for an ERC20 fails
    uint256 internal constant SafeTransfer__TransferFailed = 71002;
}

File 8 of 25 : liquidityCalcs.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;

import { LibsErrorTypes as ErrorTypes } from "./errorTypes.sol";
import { LiquiditySlotsLink } from "./liquiditySlotsLink.sol";
import { BigMathMinified } from "./bigMathMinified.sol";

/// @notice implements calculation methods used for Fluid liquidity such as updated exchange prices,
/// borrow rate, withdrawal / borrow limits, revenue amount.
library LiquidityCalcs {
    error FluidLiquidityCalcsError(uint256 errorId_);

    /// @notice emitted if the calculated borrow rate surpassed max borrow rate (16 bits) and was capped at maximum value 65535
    event BorrowRateMaxCap();

    /// @dev constants as from Liquidity variables.sol
    uint256 internal constant EXCHANGE_PRICES_PRECISION = 1e12;

    /// @dev Ignoring leap years
    uint256 internal constant SECONDS_PER_YEAR = 365 days;
    // constants used for BigMath conversion from and to storage
    uint256 internal constant DEFAULT_EXPONENT_SIZE = 8;
    uint256 internal constant DEFAULT_EXPONENT_MASK = 0xFF;

    uint256 internal constant FOUR_DECIMALS = 1e4;
    uint256 internal constant TWELVE_DECIMALS = 1e12;
    uint256 internal constant X14 = 0x3fff;
    uint256 internal constant X15 = 0x7fff;
    uint256 internal constant X16 = 0xffff;
    uint256 internal constant X18 = 0x3ffff;
    uint256 internal constant X24 = 0xffffff;
    uint256 internal constant X33 = 0x1ffffffff;
    uint256 internal constant X64 = 0xffffffffffffffff;

    ///////////////////////////////////////////////////////////////////////////
    //////////                  CALC EXCHANGE PRICES                  /////////
    ///////////////////////////////////////////////////////////////////////////

    /// @dev calculates interest (exchange prices) for a token given its' exchangePricesAndConfig from storage.
    /// @param exchangePricesAndConfig_ exchange prices and config packed uint256 read from storage
    /// @return supplyExchangePrice_ updated supplyExchangePrice
    /// @return borrowExchangePrice_ updated borrowExchangePrice
    function calcExchangePrices(
        uint256 exchangePricesAndConfig_
    ) internal view returns (uint256 supplyExchangePrice_, uint256 borrowExchangePrice_) {
        // Extracting exchange prices
        supplyExchangePrice_ =
            (exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_SUPPLY_EXCHANGE_PRICE) &
            X64;
        borrowExchangePrice_ =
            (exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_BORROW_EXCHANGE_PRICE) &
            X64;

        if (supplyExchangePrice_ == 0 || borrowExchangePrice_ == 0) {
            revert FluidLiquidityCalcsError(ErrorTypes.LiquidityCalcs__ExchangePriceZero);
        }

        uint256 temp_ = exchangePricesAndConfig_ & X16; // temp_ = borrowRate

        unchecked {
            // last timestamp can not be > current timestamp
            uint256 secondsSinceLastUpdate_ = block.timestamp -
                ((exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_LAST_TIMESTAMP) & X33);

            uint256 borrowRatio_ = (exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_BORROW_RATIO) &
                X15;
            if (secondsSinceLastUpdate_ == 0 || temp_ == 0 || borrowRatio_ == 1) {
                // if no time passed, borrow rate is 0, or no raw borrowings: no exchange price update needed
                // (if borrowRatio_ == 1 means there is only borrowInterestFree, as first bit is 1 and rest is 0)
                return (supplyExchangePrice_, borrowExchangePrice_);
            }

            // calculate new borrow exchange price.
            // formula borrowExchangePriceIncrease: previous price * borrow rate * secondsSinceLastUpdate_.
            // nominator is max uint112 (uint64 * uint16 * uint32). Divisor can not be 0.
            borrowExchangePrice_ +=
                (borrowExchangePrice_ * temp_ * secondsSinceLastUpdate_) /
                (SECONDS_PER_YEAR * FOUR_DECIMALS);

            // FOR SUPPLY EXCHANGE PRICE:
            // all yield paid by borrowers (in mode with interest) goes to suppliers in mode with interest.
            // formula: previous price * supply rate * secondsSinceLastUpdate_.
            // where supply rate = (borrow rate  - revenueFee%) * ratioSupplyYield. And
            // ratioSupplyYield = utilization * supplyRatio * borrowRatio
            //
            // Example:
            // supplyRawInterest is 80, supplyInterestFree is 20. totalSupply is 100. BorrowedRawInterest is 50.
            // BorrowInterestFree is 10. TotalBorrow is 60. borrow rate 40%, revenueFee 10%.
            // yield is 10 (so half a year must have passed).
            // supplyRawInterest must become worth 89. totalSupply must become 109. BorrowedRawInterest must become 60.
            // borrowInterestFree must still be 10. supplyInterestFree still 20. totalBorrow 70.
            // supplyExchangePrice would have to go from 1 to 1,125 (+ 0.125). borrowExchangePrice from 1 to 1,2 (+0.2).
            // utilization is 60%. supplyRatio = 20 / 80 = 25% (only 80% of lenders receiving yield).
            // borrowRatio = 10 / 50 = 20% (only 83,333% of borrowers paying yield):
            // x of borrowers paying yield = 100% - (20 / (100 + 20)) = 100% - 16.6666666% = 83,333%.
            // ratioSupplyYield = 60% * 83,33333% * (100% + 20%) = 62,5%
            // supplyRate = (40% * (100% - 10%)) * = 36% * 62,5% = 22.5%
            // increase in supplyExchangePrice, assuming 100 as previous price.
            // 100 * 22,5% * 1/2 (half a year) = 0,1125.
            // cross-check supplyRawInterest worth = 80 * 1.1125 = 89. totalSupply worth = 89 + 20.

            // -------------- 1. calculate ratioSupplyYield --------------------------------
            // step1: utilization * supplyRatio (or actually part of lenders receiving yield)

            // temp_ => supplyRatio (in 1e2: 100% = 10_000; 1% = 100 -> max value 16_383)
            // if first bit 0 then ratio is supplyInterestFree / supplyWithInterest (supplyWithInterest is bigger)
            // else ratio is supplyWithInterest / supplyInterestFree (supplyInterestFree is bigger)
            temp_ = (exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_SUPPLY_RATIO) & X15;

            if (temp_ == 1) {
                // if no raw supply: no exchange price update needed
                // (if supplyRatio_ == 1 means there is only supplyInterestFree, as first bit is 1 and rest is 0)
                return (supplyExchangePrice_, borrowExchangePrice_);
            }

            // ratioSupplyYield precision is 1e27 as 100% for increased precision when supplyInterestFree > supplyWithInterest
            if (temp_ & 1 == 1) {
                // ratio is supplyWithInterest / supplyInterestFree (supplyInterestFree is bigger)
                temp_ = temp_ >> 1;

                // Note: case where temp_ == 0 (only supplyInterestFree, no yield) already covered by early return
                // in the if statement a little above.

                // based on above example but supplyRawInterest is 20, supplyInterestFree is 80. no fee.
                // supplyRawInterest must become worth 30. totalSupply must become 110.
                // supplyExchangePrice would have to go from 1 to 1,5. borrowExchangePrice from 1 to 1,2.
                // so ratioSupplyYield must come out as 2.5 (250%).
                // supplyRatio would be (20 * 10_000 / 80) = 2500. but must be inverted.
                temp_ = (1e27 * FOUR_DECIMALS) / temp_; // e.g. 1e31 / 2500 = 4e27. (* 1e27 for precision)
                // e.g. 5_000 * (1e27 + 4e27) / 1e27 = 25_000 (=250%).
                temp_ =
                    // utilization * (100% + 100% / supplyRatio)
                    (((exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_UTILIZATION) & X14) *
                        (1e27 + temp_)) / // extract utilization (max 16_383 so there is no way this can overflow).
                    (FOUR_DECIMALS);
                // max possible value of temp_ here is 16383 * (1e27 + 1e31) / 1e4 = ~1.64e31
            } else {
                // ratio is supplyInterestFree / supplyWithInterest (supplyWithInterest is bigger)
                temp_ = temp_ >> 1;
                // if temp_ == 0 then only supplyWithInterest => full yield. temp_ is already 0

                // e.g. 5_000 * 10_000 + (20 * 10_000 / 80) / 10_000 = 5000 * 12500 / 10000 = 6250 (=62.5%).
                temp_ =
                    // 1e27 * utilization * (100% + supplyRatio) / 100%
                    (1e27 *
                        ((exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_UTILIZATION) & X14) * // extract utilization (max 16_383 so there is no way this can overflow).
                        (FOUR_DECIMALS + temp_)) /
                    (FOUR_DECIMALS * FOUR_DECIMALS);
                // max possible temp_ value: 1e27 * 16383 * 2e4 / 1e8 = 3.2766e27
            }
            // from here temp_ => ratioSupplyYield (utilization * supplyRatio part) scaled by 1e27. max possible value ~1.64e31

            // step2 of ratioSupplyYield: add borrowRatio (only x% of borrowers paying yield)
            if (borrowRatio_ & 1 == 1) {
                // ratio is borrowWithInterest / borrowInterestFree (borrowInterestFree is bigger)
                borrowRatio_ = borrowRatio_ >> 1;
                // borrowRatio_ => x of total bororwers paying yield. scale to 1e27.

                // Note: case where borrowRatio_ == 0 (only borrowInterestFree, no yield) already covered
                // at the beginning of the method by early return if `borrowRatio_ == 1`.

                // based on above example but borrowRawInterest is 10, borrowInterestFree is 50. no fee. borrowRatio = 20%.
                // so only 16.66% of borrowers are paying yield. so the 100% - part of the formula is not needed.
                // x of borrowers paying yield = (borrowRatio / (100 + borrowRatio)) = 16.6666666%
                // borrowRatio_ => x of total bororwers paying yield. scale to 1e27.
                borrowRatio_ = (borrowRatio_ * 1e27) / (FOUR_DECIMALS + borrowRatio_);
                // max value here for borrowRatio_ is (1e31 / (1e4 + 1e4))= 5e26 (= 50% of borrowers paying yield).
            } else {
                // ratio is borrowInterestFree / borrowWithInterest (borrowWithInterest is bigger)
                borrowRatio_ = borrowRatio_ >> 1;

                // borrowRatio_ => x of total bororwers paying yield. scale to 1e27.
                // x of borrowers paying yield = 100% - (borrowRatio / (100 + borrowRatio)) = 100% - 16.6666666% = 83,333%.
                borrowRatio_ = (1e27 - ((borrowRatio_ * 1e27) / (FOUR_DECIMALS + borrowRatio_)));
                // borrowRatio can never be > 100%. so max subtraction can be 100% - 100% / 200%.
                // or if borrowRatio_ is 0 -> 100% - 0. or if borrowRatio_ is 1 -> 100% - 1 / 101.
                // max value here for borrowRatio_ is 1e27 - 0 = 1e27 (= 100% of borrowers paying yield).
            }

            // temp_ => ratioSupplyYield. scaled down from 1e25 = 1% each to normal percent precision 1e2 = 1%.
            // max nominator value is ~1.64e31 * 1e27 = 1.64e58. max result = 1.64e8
            temp_ = (FOUR_DECIMALS * temp_ * borrowRatio_) / 1e54;

            // 2. calculate supply rate
            // temp_ => supply rate (borrow rate  - revenueFee%) * ratioSupplyYield.
            // division part is done in next step to increase precision. (divided by 2x FOUR_DECIMALS, fee + borrowRate)
            // Note that all calculation divisions for supplyExchangePrice are rounded down.
            // Note supply rate can be bigger than the borrowRate, e.g. if there are only few lenders with interest
            // but more suppliers not earning interest.
            temp_ = ((exchangePricesAndConfig_ & X16) * // borrow rate
                temp_ * // ratioSupplyYield
                (FOUR_DECIMALS - ((exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_FEE) & X14))); // revenueFee
            // fee can not be > 100%. max possible = 65535 * ~1.64e8 * 1e4 =~1.074774e17.

            // 3. calculate increase in supply exchange price
            supplyExchangePrice_ += ((supplyExchangePrice_ * temp_ * secondsSinceLastUpdate_) /
                (SECONDS_PER_YEAR * FOUR_DECIMALS * FOUR_DECIMALS * FOUR_DECIMALS));
            // max possible nominator = max uint 64 * 1.074774e17 * max uint32 = ~8.52e45. Denominator can not be 0.
        }
    }

    ///////////////////////////////////////////////////////////////////////////
    //////////                     CALC REVENUE                       /////////
    ///////////////////////////////////////////////////////////////////////////

    /// @dev gets the `revenueAmount_` for a token given its' totalAmounts and exchangePricesAndConfig from storage
    /// and the current balance of the Fluid liquidity contract for the token.
    /// @param totalAmounts_ total amounts packed uint256 read from storage
    /// @param exchangePricesAndConfig_ exchange prices and config packed uint256 read from storage
    /// @param liquidityTokenBalance_   current balance of Liquidity contract (IERC20(token_).balanceOf(address(this)))
    /// @return revenueAmount_ collectable revenue amount
    function calcRevenue(
        uint256 totalAmounts_,
        uint256 exchangePricesAndConfig_,
        uint256 liquidityTokenBalance_
    ) internal view returns (uint256 revenueAmount_) {
        // @dev no need to super-optimize this method as it is only used by admin

        // calculate the new exchange prices based on earned interest
        (uint256 supplyExchangePrice_, uint256 borrowExchangePrice_) = calcExchangePrices(exchangePricesAndConfig_);

        // total supply = interest free + with interest converted from raw
        uint256 totalSupply_ = getTotalSupply(totalAmounts_, supplyExchangePrice_);

        if (totalSupply_ > 0) {
            // available revenue: balanceOf(token) + totalBorrowings - totalLendings.
            revenueAmount_ = liquidityTokenBalance_ + getTotalBorrow(totalAmounts_, borrowExchangePrice_);
            // ensure there is no possible case because of rounding etc. where this would revert,
            // explicitly check if >
            revenueAmount_ = revenueAmount_ > totalSupply_ ? revenueAmount_ - totalSupply_ : 0;
            // Note: if utilization > 100% (totalSupply < totalBorrow), then all the amount above 100% utilization
            // can only be revenue.
        } else {
            // if supply is 0, then rest of balance can be withdrawn as revenue so that no amounts get stuck
            revenueAmount_ = liquidityTokenBalance_;
        }
    }

    ///////////////////////////////////////////////////////////////////////////
    //////////                      CALC LIMITS                       /////////
    ///////////////////////////////////////////////////////////////////////////

    /// @dev calculates withdrawal limit before an operate execution:
    /// amount of user supply that must stay supplied (not amount that can be withdrawn).
    /// i.e. if user has supplied 100m and can withdraw 5M, this method returns the 95M, not the withdrawable amount 5M
    /// @param userSupplyData_ user supply data packed uint256 from storage
    /// @param userSupply_ current user supply amount already extracted from `userSupplyData_` and converted from BigMath
    /// @return currentWithdrawalLimit_ current withdrawal limit updated for expansion since last interaction.
    ///         returned value is in raw for with interest mode, normal amount for interest free mode!
    function calcWithdrawalLimitBeforeOperate(
        uint256 userSupplyData_,
        uint256 userSupply_
    ) internal view returns (uint256 currentWithdrawalLimit_) {
        // @dev must support handling the case where timestamp is 0 (config is set but no interactions yet).
        // first tx where timestamp is 0 will enter `if (lastWithdrawalLimit_ == 0)` because lastWithdrawalLimit_ is not set yet.
        // returning max withdrawal allowed, which is not exactly right but doesn't matter because the first interaction must be
        // a deposit anyway. Important is that it would not revert.

        // Note the first time a deposit brings the user supply amount to above the base withdrawal limit, the active limit
        // is the fully expanded limit immediately.

        // extract last set withdrawal limit
        uint256 lastWithdrawalLimit_ = (userSupplyData_ >>
            LiquiditySlotsLink.BITS_USER_SUPPLY_PREVIOUS_WITHDRAWAL_LIMIT) & X64;
        lastWithdrawalLimit_ =
            (lastWithdrawalLimit_ >> DEFAULT_EXPONENT_SIZE) <<
            (lastWithdrawalLimit_ & DEFAULT_EXPONENT_MASK);
        if (lastWithdrawalLimit_ == 0) {
            // withdrawal limit is not activated. Max withdrawal allowed
            return 0;
        }

        uint256 maxWithdrawableLimit_;
        uint256 temp_;
        unchecked {
            // extract max withdrawable percent of user supply and
            // calculate maximum withdrawable amount expandPercentage of user supply at full expansion duration elapsed
            // e.g.: if 10% expandPercentage, meaning 10% is withdrawable after full expandDuration has elapsed.

            // userSupply_ needs to be atleast 1e73 to overflow max limit of ~1e77 in uint256 (no token in existence where this is possible).
            maxWithdrawableLimit_ =
                (((userSupplyData_ >> LiquiditySlotsLink.BITS_USER_SUPPLY_EXPAND_PERCENT) & X14) * userSupply_) /
                FOUR_DECIMALS;

            // time elapsed since last withdrawal limit was set (in seconds)
            // @dev last process timestamp is guaranteed to exist for withdrawal, as a supply must have happened before.
            // last timestamp can not be > current timestamp
            temp_ =
                block.timestamp -
                ((userSupplyData_ >> LiquiditySlotsLink.BITS_USER_SUPPLY_LAST_UPDATE_TIMESTAMP) & X33);
        }
        // calculate withdrawable amount of expandPercent that is elapsed of expandDuration.
        // e.g. if 60% of expandDuration has elapsed, then user should be able to withdraw 6% of user supply, down to 94%.
        // Note: no explicit check for this needed, it is covered by setting minWithdrawalLimit_ if needed.
        temp_ =
            (maxWithdrawableLimit_ * temp_) /
            // extract expand duration: After this, decrement won't happen (user can withdraw 100% of withdraw limit)
            ((userSupplyData_ >> LiquiditySlotsLink.BITS_USER_SUPPLY_EXPAND_DURATION) & X24); // expand duration can never be 0
        // calculate expanded withdrawal limit: last withdrawal limit - withdrawable amount.
        // Note: withdrawable amount here can grow bigger than userSupply if timeElapsed is a lot bigger than expandDuration,
        // which would cause the subtraction `lastWithdrawalLimit_ - withdrawableAmount_` to revert. In that case, set 0
        // which will cause minimum (fully expanded) withdrawal limit to be set in lines below.
        unchecked {
            // underflow explicitly checked & handled
            currentWithdrawalLimit_ = lastWithdrawalLimit_ > temp_ ? lastWithdrawalLimit_ - temp_ : 0;
            // calculate minimum withdrawal limit: minimum amount of user supply that must stay supplied at full expansion.
            // subtraction can not underflow as maxWithdrawableLimit_ is a percentage amount (<=100%) of userSupply_
            temp_ = userSupply_ - maxWithdrawableLimit_;
        }
        // if withdrawal limit is decreased below minimum then set minimum
        // (e.g. when more than expandDuration time has elapsed)
        if (temp_ > currentWithdrawalLimit_) {
            currentWithdrawalLimit_ = temp_;
        }
    }

    /// @dev calculates withdrawal limit after an operate execution:
    /// amount of user supply that must stay supplied (not amount that can be withdrawn).
    /// i.e. if user has supplied 100m and can withdraw 5M, this method returns the 95M, not the withdrawable amount 5M
    /// @param userSupplyData_ user supply data packed uint256 from storage
    /// @param userSupply_ current user supply amount already extracted from `userSupplyData_` and added / subtracted with the executed operate amount
    /// @param newWithdrawalLimit_ current withdrawal limit updated for expansion since last interaction, result from `calcWithdrawalLimitBeforeOperate`
    /// @return withdrawalLimit_ updated withdrawal limit that should be written to storage. returned value is in
    ///                          raw for with interest mode, normal amount for interest free mode!
    function calcWithdrawalLimitAfterOperate(
        uint256 userSupplyData_,
        uint256 userSupply_,
        uint256 newWithdrawalLimit_
    ) internal pure returns (uint256) {
        // temp_ => base withdrawal limit. below this, maximum withdrawals are allowed
        uint256 temp_ = (userSupplyData_ >> LiquiditySlotsLink.BITS_USER_SUPPLY_BASE_WITHDRAWAL_LIMIT) & X18;
        temp_ = (temp_ >> DEFAULT_EXPONENT_SIZE) << (temp_ & DEFAULT_EXPONENT_MASK);

        // if user supply is below base limit then max withdrawals are allowed
        if (userSupply_ < temp_) {
            return 0;
        }
        // temp_ => withdrawal limit expandPercent (is in 1e2 decimals)
        temp_ = (userSupplyData_ >> LiquiditySlotsLink.BITS_USER_SUPPLY_EXPAND_PERCENT) & X14;
        unchecked {
            // temp_ => minimum withdrawal limit: userSupply - max withdrawable limit (userSupply * expandPercent))
            // userSupply_ needs to be atleast 1e73 to overflow max limit of ~1e77 in uint256 (no token in existence where this is possible).
            // subtraction can not underflow as maxWithdrawableLimit_ is a percentage amount (<=100%) of userSupply_
            temp_ = userSupply_ - ((userSupply_ * temp_) / FOUR_DECIMALS);
        }
        // if new (before operation) withdrawal limit is less than minimum limit then set minimum limit.
        // e.g. can happen on new deposits. withdrawal limit is instantly fully expanded in a scenario where
        // increased deposit amount outpaces withrawals.
        if (temp_ > newWithdrawalLimit_) {
            return temp_;
        }
        return newWithdrawalLimit_;
    }

    /// @dev calculates borrow limit before an operate execution:
    /// total amount user borrow can reach (not borrowable amount in current operation).
    /// i.e. if user has borrowed 50M and can still borrow 5M, this method returns the total 55M, not the borrowable amount 5M
    /// @param userBorrowData_ user borrow data packed uint256 from storage
    /// @param userBorrow_ current user borrow amount already extracted from `userBorrowData_`
    /// @return currentBorrowLimit_ current borrow limit updated for expansion since last interaction. returned value is in
    ///                             raw for with interest mode, normal amount for interest free mode!
    function calcBorrowLimitBeforeOperate(
        uint256 userBorrowData_,
        uint256 userBorrow_
    ) internal view returns (uint256 currentBorrowLimit_) {
        // @dev must support handling the case where timestamp is 0 (config is set but no interactions yet) -> base limit.
        // first tx where timestamp is 0 will enter `if (maxExpandedBorrowLimit_ < baseBorrowLimit_)` because `userBorrow_` and thus
        // `maxExpansionLimit_` and thus `maxExpandedBorrowLimit_` is 0 and `baseBorrowLimit_` can not be 0.

        // temp_ = extract borrow expand percent (is in 1e2 decimals)
        uint256 temp_ = (userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_EXPAND_PERCENT) & X14;

        uint256 maxExpansionLimit_;
        uint256 maxExpandedBorrowLimit_;
        unchecked {
            // calculate max expansion limit: Max amount limit can expand to since last interaction
            // userBorrow_ needs to be atleast 1e73 to overflow max limit of ~1e77 in uint256 (no token in existence where this is possible).
            maxExpansionLimit_ = ((userBorrow_ * temp_) / FOUR_DECIMALS);

            // calculate max borrow limit: Max point limit can increase to since last interaction
            maxExpandedBorrowLimit_ = userBorrow_ + maxExpansionLimit_;
        }

        // currentBorrowLimit_ = extract base borrow limit
        currentBorrowLimit_ = (userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_BASE_BORROW_LIMIT) & X18;
        currentBorrowLimit_ =
            (currentBorrowLimit_ >> DEFAULT_EXPONENT_SIZE) <<
            (currentBorrowLimit_ & DEFAULT_EXPONENT_MASK);

        if (maxExpandedBorrowLimit_ < currentBorrowLimit_) {
            return currentBorrowLimit_;
        }
        // time elapsed since last borrow limit was set (in seconds)
        unchecked {
            // temp_ = timeElapsed_ (last timestamp can not be > current timestamp)
            temp_ =
                block.timestamp -
                ((userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_LAST_UPDATE_TIMESTAMP) & X33); // extract last update timestamp
        }

        // currentBorrowLimit_ = expandedBorrowableAmount + extract last set borrow limit
        currentBorrowLimit_ =
            // calculate borrow limit expansion since last interaction for `expandPercent` that is elapsed of `expandDuration`.
            // divisor is extract expand duration (after this, full expansion to expandPercentage happened).
            ((maxExpansionLimit_ * temp_) /
                ((userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_EXPAND_DURATION) & X24)) + // expand duration can never be 0
            //  extract last set borrow limit
            BigMathMinified.fromBigNumber(
                (userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_PREVIOUS_BORROW_LIMIT) & X64,
                DEFAULT_EXPONENT_SIZE,
                DEFAULT_EXPONENT_MASK
            );

        // if timeElapsed is bigger than expandDuration, new borrow limit would be > max expansion,
        // so set to `maxExpandedBorrowLimit_` in that case.
        // also covers the case where last process timestamp = 0 (timeElapsed would simply be very big)
        if (currentBorrowLimit_ > maxExpandedBorrowLimit_) {
            currentBorrowLimit_ = maxExpandedBorrowLimit_;
        }
        // temp_ = extract hard max borrow limit. Above this user can never borrow (not expandable above)
        temp_ = (userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_MAX_BORROW_LIMIT) & X18;
        temp_ = (temp_ >> DEFAULT_EXPONENT_SIZE) << (temp_ & DEFAULT_EXPONENT_MASK);

        if (currentBorrowLimit_ > temp_) {
            currentBorrowLimit_ = temp_;
        }
    }

    /// @dev calculates borrow limit after an operate execution:
    /// total amount user borrow can reach (not borrowable amount in current operation).
    /// i.e. if user has borrowed 50M and can still borrow 5M, this method returns the total 55M, not the borrowable amount 5M
    /// @param userBorrowData_ user borrow data packed uint256 from storage
    /// @param userBorrow_ current user borrow amount already extracted from `userBorrowData_` and added / subtracted with the executed operate amount
    /// @param newBorrowLimit_ current borrow limit updated for expansion since last interaction, result from `calcBorrowLimitBeforeOperate`
    /// @return borrowLimit_ updated borrow limit that should be written to storage.
    ///                      returned value is in raw for with interest mode, normal amount for interest free mode!
    function calcBorrowLimitAfterOperate(
        uint256 userBorrowData_,
        uint256 userBorrow_,
        uint256 newBorrowLimit_
    ) internal pure returns (uint256 borrowLimit_) {
        // temp_ = extract borrow expand percent
        uint256 temp_ = (userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_EXPAND_PERCENT) & X14; // (is in 1e2 decimals)

        unchecked {
            // borrowLimit_ = calculate maximum borrow limit at full expansion.
            // userBorrow_ needs to be at least 1e73 to overflow max limit of ~1e77 in uint256 (no token in existence where this is possible).
            borrowLimit_ = userBorrow_ + ((userBorrow_ * temp_) / FOUR_DECIMALS);
        }

        // temp_ = extract base borrow limit
        temp_ = (userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_BASE_BORROW_LIMIT) & X18;
        temp_ = (temp_ >> DEFAULT_EXPONENT_SIZE) << (temp_ & DEFAULT_EXPONENT_MASK);

        if (borrowLimit_ < temp_) {
            // below base limit, borrow limit is always base limit
            return temp_;
        }
        // temp_ = extract hard max borrow limit. Above this user can never borrow (not expandable above)
        temp_ = (userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_MAX_BORROW_LIMIT) & X18;
        temp_ = (temp_ >> DEFAULT_EXPONENT_SIZE) << (temp_ & DEFAULT_EXPONENT_MASK);

        // make sure fully expanded borrow limit is not above hard max borrow limit
        if (borrowLimit_ > temp_) {
            borrowLimit_ = temp_;
        }
        // if new borrow limit (from before operate) is > max borrow limit, set max borrow limit.
        // (e.g. on a repay shrinking instantly to fully expanded borrow limit from new borrow amount. shrinking is instant)
        if (newBorrowLimit_ > borrowLimit_) {
            return borrowLimit_;
        }
        return newBorrowLimit_;
    }

    ///////////////////////////////////////////////////////////////////////////
    //////////                      CALC RATES                        /////////
    ///////////////////////////////////////////////////////////////////////////

    /// @dev Calculates new borrow rate from utilization for a token
    /// @param rateData_ rate data packed uint256 from storage for the token
    /// @param utilization_ totalBorrow / totalSupply. 1e4 = 100% utilization
    /// @return rate_ rate for that particular token in 1e2 precision (e.g. 5% rate = 500)
    function calcBorrowRateFromUtilization(uint256 rateData_, uint256 utilization_) internal returns (uint256 rate_) {
        // extract rate version: 4 bits (0xF) starting from bit 0
        uint256 rateVersion_ = (rateData_ & 0xF);

        if (rateVersion_ == 1) {
            rate_ = calcRateV1(rateData_, utilization_);
        } else if (rateVersion_ == 2) {
            rate_ = calcRateV2(rateData_, utilization_);
        } else {
            revert FluidLiquidityCalcsError(ErrorTypes.LiquidityCalcs__UnsupportedRateVersion);
        }

        if (rate_ > X16) {
            // hard cap for borrow rate at maximum value 16 bits (65535) to make sure it does not overflow storage space.
            // this is unlikely to ever happen if configs stay within expected levels.
            rate_ = X16;
            // emit event to more easily become aware
            emit BorrowRateMaxCap();
        }
    }

    /// @dev calculates the borrow rate based on utilization for rate data version 1 (with one kink) in 1e2 precision
    /// @param rateData_ rate data packed uint256 from storage for the token
    /// @param utilization_  in 1e2 (100% = 1e4)
    /// @return rate_ rate in 1e2 precision
    function calcRateV1(uint256 rateData_, uint256 utilization_) internal pure returns (uint256 rate_) {
        /// For rate v1 (one kink) ------------------------------------------------------
        /// Next 16  bits =>  4 - 19 => Rate at utilization 0% (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535)
        /// Next 16  bits =>  20- 35 => Utilization at kink1 (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535)
        /// Next 16  bits =>  36- 51 => Rate at utilization kink1 (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535)
        /// Next 16  bits =>  52- 67 => Rate at utilization 100% (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535)
        /// Last 188 bits =>  68-255 => blank, might come in use in future

        // y = mx + c.
        // y is borrow rate
        // x is utilization
        // m = slope (m can also be negative for declining rates)
        // c is constant (c can be negative)

        uint256 y1_;
        uint256 y2_;
        uint256 x1_;
        uint256 x2_;

        // extract kink1: 16 bits (0xFFFF) starting from bit 20
        // kink is in 1e2, same as utilization, so no conversion needed for direct comparison of the two
        uint256 kink1_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V1_UTILIZATION_AT_KINK) & X16;
        if (utilization_ < kink1_) {
            // if utilization is less than kink
            y1_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V1_RATE_AT_UTILIZATION_ZERO) & X16;
            y2_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V1_RATE_AT_UTILIZATION_KINK) & X16;
            x1_ = 0; // 0%
            x2_ = kink1_;
        } else {
            // else utilization is greater than kink
            y1_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V1_RATE_AT_UTILIZATION_KINK) & X16;
            y2_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V1_RATE_AT_UTILIZATION_MAX) & X16;
            x1_ = kink1_;
            x2_ = FOUR_DECIMALS; // 100%
        }

        int256 constant_;
        int256 slope_;
        unchecked {
            // calculating slope with twelve decimal precision. m = (y2 - y1) / (x2 - x1).
            // utilization of x2 can not be <= utilization of x1 (so no underflow or 0 divisor)
            // y is in 1e2 so can not overflow when multiplied with TWELVE_DECIMALS
            slope_ = (int256(y2_ - y1_) * int256(TWELVE_DECIMALS)) / int256((x2_ - x1_));

            // calculating constant at 12 decimal precision. slope is already in 12 decimal hence only multiple with y1. c = y - mx.
            // maximum y1_ value is 65535. 65535 * 1e12 can not overflow int256
            // maximum slope is 65535 - 0 * TWELVE_DECIMALS / 1 = 65535 * 1e12;
            // maximum x1_ is 100% (9_999 actually) => slope_ * x1_ can not overflow int256
            // subtraction most extreme case would be  0 - max value slope_ * x1_ => can not underflow int256
            constant_ = int256(y1_ * TWELVE_DECIMALS) - (slope_ * int256(x1_));

            // calculating new borrow rate
            // - slope_ max value is 65535 * 1e12,
            // - utilization max value is let's say 500% (extreme case where borrow rate increases borrow amount without new supply)
            // - constant max value is 65535 * 1e12
            // so max values are 65535 * 1e12 * 50_000 + 65535 * 1e12 -> 3.2768*10^21, which easily fits int256
            // divisor TWELVE_DECIMALS can not be 0
            slope_ = (slope_ * int256(utilization_)) + constant_; // reusing `slope_` as variable for gas savings
            if (slope_ < 0) {
                revert FluidLiquidityCalcsError(ErrorTypes.LiquidityCalcs__BorrowRateNegative);
            }
            rate_ = uint256(slope_) / TWELVE_DECIMALS;
        }
    }

    /// @dev calculates the borrow rate based on utilization for rate data version 2 (with two kinks) in 1e4 precision
    /// @param rateData_ rate data packed uint256 from storage for the token
    /// @param utilization_  in 1e2 (100% = 1e4)
    /// @return rate_ rate in 1e4 precision
    function calcRateV2(uint256 rateData_, uint256 utilization_) internal pure returns (uint256 rate_) {
        /// For rate v2 (two kinks) -----------------------------------------------------
        /// Next 16  bits =>  4 - 19 => Rate at utilization 0% (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535)
        /// Next 16  bits =>  20- 35 => Utilization at kink1 (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535)
        /// Next 16  bits =>  36- 51 => Rate at utilization kink1 (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535)
        /// Next 16  bits =>  52- 67 => Utilization at kink2 (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535)
        /// Next 16  bits =>  68- 83 => Rate at utilization kink2 (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535)
        /// Next 16  bits =>  84- 99 => Rate at utilization 100% (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535)
        /// Last 156 bits => 100-255 => blank, might come in use in future

        // y = mx + c.
        // y is borrow rate
        // x is utilization
        // m = slope (m can also be negative for declining rates)
        // c is constant (c can be negative)

        uint256 y1_;
        uint256 y2_;
        uint256 x1_;
        uint256 x2_;

        // extract kink1: 16 bits (0xFFFF) starting from bit 20
        // kink is in 1e2, same as utilization, so no conversion needed for direct comparison of the two
        uint256 kink1_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_UTILIZATION_AT_KINK1) & X16;
        if (utilization_ < kink1_) {
            // if utilization is less than kink1
            y1_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_ZERO) & X16;
            y2_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_KINK1) & X16;
            x1_ = 0; // 0%
            x2_ = kink1_;
        } else {
            // extract kink2: 16 bits (0xFFFF) starting from bit 52
            uint256 kink2_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_UTILIZATION_AT_KINK2) & X16;
            if (utilization_ < kink2_) {
                // if utilization is less than kink2
                y1_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_KINK1) & X16;
                y2_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_KINK2) & X16;
                x1_ = kink1_;
                x2_ = kink2_;
            } else {
                // else utilization is greater than kink2
                y1_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_KINK2) & X16;
                y2_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_MAX) & X16;
                x1_ = kink2_;
                x2_ = FOUR_DECIMALS;
            }
        }

        int256 constant_;
        int256 slope_;
        unchecked {
            // calculating slope with twelve decimal precision. m = (y2 - y1) / (x2 - x1).
            // utilization of x2 can not be <= utilization of x1 (so no underflow or 0 divisor)
            // y is in 1e2 so can not overflow when multiplied with TWELVE_DECIMALS
            slope_ = (int256(y2_ - y1_) * int256(TWELVE_DECIMALS)) / int256((x2_ - x1_));

            // calculating constant at 12 decimal precision. slope is already in 12 decimal hence only multiple with y1. c = y - mx.
            // maximum y1_ value is 65535. 65535 * 1e12 can not overflow int256
            // maximum slope is 65535 - 0 * TWELVE_DECIMALS / 1 = 65535 * 1e12;
            // maximum x1_ is 100% (9_999 actually) => slope_ * x1_ can not overflow int256
            // subtraction most extreme case would be  0 - max value slope_ * x1_ => can not underflow int256
            constant_ = int256(y1_ * TWELVE_DECIMALS) - (slope_ * int256(x1_));

            // calculating new borrow rate
            // - slope_ max value is 65535 * 1e12,
            // - utilization max value is let's say 500% (extreme case where borrow rate increases borrow amount without new supply)
            // - constant max value is 65535 * 1e12
            // so max values are 65535 * 1e12 * 50_000 + 65535 * 1e12 -> 3.2768*10^21, which easily fits int256
            // divisor TWELVE_DECIMALS can not be 0
            slope_ = (slope_ * int256(utilization_)) + constant_; // reusing `slope_` as variable for gas savings
            if (slope_ < 0) {
                revert FluidLiquidityCalcsError(ErrorTypes.LiquidityCalcs__BorrowRateNegative);
            }
            rate_ = uint256(slope_) / TWELVE_DECIMALS;
        }
    }

    /// @dev reads the total supply out of Liquidity packed storage `totalAmounts_` for `supplyExchangePrice_`
    function getTotalSupply(
        uint256 totalAmounts_,
        uint256 supplyExchangePrice_
    ) internal pure returns (uint256 totalSupply_) {
        // totalSupply_ => supplyInterestFree
        totalSupply_ = (totalAmounts_ >> LiquiditySlotsLink.BITS_TOTAL_AMOUNTS_SUPPLY_INTEREST_FREE) & X64;
        totalSupply_ = (totalSupply_ >> DEFAULT_EXPONENT_SIZE) << (totalSupply_ & DEFAULT_EXPONENT_MASK);

        uint256 totalSupplyRaw_ = totalAmounts_ & X64; // no shifting as supplyRaw is first 64 bits
        totalSupplyRaw_ = (totalSupplyRaw_ >> DEFAULT_EXPONENT_SIZE) << (totalSupplyRaw_ & DEFAULT_EXPONENT_MASK);

        // totalSupply = supplyInterestFree + supplyRawInterest normalized from raw
        totalSupply_ += ((totalSupplyRaw_ * supplyExchangePrice_) / EXCHANGE_PRICES_PRECISION);
    }

    /// @dev reads the total borrow out of Liquidity packed storage `totalAmounts_` for `borrowExchangePrice_`
    function getTotalBorrow(
        uint256 totalAmounts_,
        uint256 borrowExchangePrice_
    ) internal pure returns (uint256 totalBorrow_) {
        // totalBorrow_ => borrowInterestFree
        // no & mask needed for borrow interest free as it occupies the last bits in the storage slot
        totalBorrow_ = (totalAmounts_ >> LiquiditySlotsLink.BITS_TOTAL_AMOUNTS_BORROW_INTEREST_FREE);
        totalBorrow_ = (totalBorrow_ >> DEFAULT_EXPONENT_SIZE) << (totalBorrow_ & DEFAULT_EXPONENT_MASK);

        uint256 totalBorrowRaw_ = (totalAmounts_ >> LiquiditySlotsLink.BITS_TOTAL_AMOUNTS_BORROW_WITH_INTEREST) & X64;
        totalBorrowRaw_ = (totalBorrowRaw_ >> DEFAULT_EXPONENT_SIZE) << (totalBorrowRaw_ & DEFAULT_EXPONENT_MASK);

        // totalBorrow = borrowInterestFree + borrowRawInterest normalized from raw
        totalBorrow_ += ((totalBorrowRaw_ * borrowExchangePrice_) / EXCHANGE_PRICES_PRECISION);
    }
}

File 9 of 25 : liquiditySlotsLink.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;

/// @notice library that helps in reading / working with storage slot data of Fluid Liquidity.
/// @dev as all data for Fluid Liquidity is internal, any data must be fetched directly through manual
/// slot reading through this library or, if gas usage is less important, through the FluidLiquidityResolver.
library LiquiditySlotsLink {
    /// @dev storage slot for status at Liquidity
    uint256 internal constant LIQUIDITY_STATUS_SLOT = 1;
    /// @dev storage slot for auths mapping at Liquidity
    uint256 internal constant LIQUIDITY_AUTHS_MAPPING_SLOT = 2;
    /// @dev storage slot for guardians mapping at Liquidity
    uint256 internal constant LIQUIDITY_GUARDIANS_MAPPING_SLOT = 3;
    /// @dev storage slot for user class mapping at Liquidity
    uint256 internal constant LIQUIDITY_USER_CLASS_MAPPING_SLOT = 4;
    /// @dev storage slot for exchangePricesAndConfig mapping at Liquidity
    uint256 internal constant LIQUIDITY_EXCHANGE_PRICES_MAPPING_SLOT = 5;
    /// @dev storage slot for rateData mapping at Liquidity
    uint256 internal constant LIQUIDITY_RATE_DATA_MAPPING_SLOT = 6;
    /// @dev storage slot for totalAmounts mapping at Liquidity
    uint256 internal constant LIQUIDITY_TOTAL_AMOUNTS_MAPPING_SLOT = 7;
    /// @dev storage slot for user supply double mapping at Liquidity
    uint256 internal constant LIQUIDITY_USER_SUPPLY_DOUBLE_MAPPING_SLOT = 8;
    /// @dev storage slot for user borrow double mapping at Liquidity
    uint256 internal constant LIQUIDITY_USER_BORROW_DOUBLE_MAPPING_SLOT = 9;
    /// @dev storage slot for listed tokens array at Liquidity
    uint256 internal constant LIQUIDITY_LISTED_TOKENS_ARRAY_SLOT = 10;
    /// @dev storage slot for listed tokens array at Liquidity
    uint256 internal constant LIQUIDITY_CONFIGS2_MAPPING_SLOT = 11;

    // --------------------------------
    // @dev stacked uint256 storage slots bits position data for each:

    // ExchangePricesAndConfig
    uint256 internal constant BITS_EXCHANGE_PRICES_BORROW_RATE = 0;
    uint256 internal constant BITS_EXCHANGE_PRICES_FEE = 16;
    uint256 internal constant BITS_EXCHANGE_PRICES_UTILIZATION = 30;
    uint256 internal constant BITS_EXCHANGE_PRICES_UPDATE_THRESHOLD = 44;
    uint256 internal constant BITS_EXCHANGE_PRICES_LAST_TIMESTAMP = 58;
    uint256 internal constant BITS_EXCHANGE_PRICES_SUPPLY_EXCHANGE_PRICE = 91;
    uint256 internal constant BITS_EXCHANGE_PRICES_BORROW_EXCHANGE_PRICE = 155;
    uint256 internal constant BITS_EXCHANGE_PRICES_SUPPLY_RATIO = 219;
    uint256 internal constant BITS_EXCHANGE_PRICES_BORROW_RATIO = 234;
    uint256 internal constant BITS_EXCHANGE_PRICES_USES_CONFIGS2 = 249;

    // RateData:
    uint256 internal constant BITS_RATE_DATA_VERSION = 0;
    // RateData: V1
    uint256 internal constant BITS_RATE_DATA_V1_RATE_AT_UTILIZATION_ZERO = 4;
    uint256 internal constant BITS_RATE_DATA_V1_UTILIZATION_AT_KINK = 20;
    uint256 internal constant BITS_RATE_DATA_V1_RATE_AT_UTILIZATION_KINK = 36;
    uint256 internal constant BITS_RATE_DATA_V1_RATE_AT_UTILIZATION_MAX = 52;
    // RateData: V2
    uint256 internal constant BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_ZERO = 4;
    uint256 internal constant BITS_RATE_DATA_V2_UTILIZATION_AT_KINK1 = 20;
    uint256 internal constant BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_KINK1 = 36;
    uint256 internal constant BITS_RATE_DATA_V2_UTILIZATION_AT_KINK2 = 52;
    uint256 internal constant BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_KINK2 = 68;
    uint256 internal constant BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_MAX = 84;

    // TotalAmounts
    uint256 internal constant BITS_TOTAL_AMOUNTS_SUPPLY_WITH_INTEREST = 0;
    uint256 internal constant BITS_TOTAL_AMOUNTS_SUPPLY_INTEREST_FREE = 64;
    uint256 internal constant BITS_TOTAL_AMOUNTS_BORROW_WITH_INTEREST = 128;
    uint256 internal constant BITS_TOTAL_AMOUNTS_BORROW_INTEREST_FREE = 192;

    // UserSupplyData
    uint256 internal constant BITS_USER_SUPPLY_MODE = 0;
    uint256 internal constant BITS_USER_SUPPLY_AMOUNT = 1;
    uint256 internal constant BITS_USER_SUPPLY_PREVIOUS_WITHDRAWAL_LIMIT = 65;
    uint256 internal constant BITS_USER_SUPPLY_LAST_UPDATE_TIMESTAMP = 129;
    uint256 internal constant BITS_USER_SUPPLY_EXPAND_PERCENT = 162;
    uint256 internal constant BITS_USER_SUPPLY_EXPAND_DURATION = 176;
    uint256 internal constant BITS_USER_SUPPLY_BASE_WITHDRAWAL_LIMIT = 200;
    uint256 internal constant BITS_USER_SUPPLY_IS_PAUSED = 255;

    // UserBorrowData
    uint256 internal constant BITS_USER_BORROW_MODE = 0;
    uint256 internal constant BITS_USER_BORROW_AMOUNT = 1;
    uint256 internal constant BITS_USER_BORROW_PREVIOUS_BORROW_LIMIT = 65;
    uint256 internal constant BITS_USER_BORROW_LAST_UPDATE_TIMESTAMP = 129;
    uint256 internal constant BITS_USER_BORROW_EXPAND_PERCENT = 162;
    uint256 internal constant BITS_USER_BORROW_EXPAND_DURATION = 176;
    uint256 internal constant BITS_USER_BORROW_BASE_BORROW_LIMIT = 200;
    uint256 internal constant BITS_USER_BORROW_MAX_BORROW_LIMIT = 218;
    uint256 internal constant BITS_USER_BORROW_IS_PAUSED = 255;

    // Configs2
    uint256 internal constant BITS_CONFIGS2_MAX_UTILIZATION = 0;

    // --------------------------------

    /// @notice Calculating the slot ID for Liquidity contract for single mapping at `slot_` for `key_`
    function calculateMappingStorageSlot(uint256 slot_, address key_) internal pure returns (bytes32) {
        return keccak256(abi.encode(key_, slot_));
    }

    /// @notice Calculating the slot ID for Liquidity contract for double mapping at `slot_` for `key1_` and `key2_`
    function calculateDoubleMappingStorageSlot(
        uint256 slot_,
        address key1_,
        address key2_
    ) internal pure returns (bytes32) {
        bytes32 intermediateSlot_ = keccak256(abi.encode(key1_, slot_));
        return keccak256(abi.encode(key2_, intermediateSlot_));
    }
}

File 10 of 25 : safeTransfer.sol
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity 0.8.21;

import { LibsErrorTypes as ErrorTypes } from "./errorTypes.sol";

/// @notice provides minimalistic methods for safe transfers, e.g. ERC20 safeTransferFrom
library SafeTransfer {
    uint256 internal constant MAX_NATIVE_TRANSFER_GAS = 20000; // pass max. 20k gas for native transfers

    error FluidSafeTransferError(uint256 errorId_);

    /// @dev Transfer `amount_` of `token_` from `from_` to `to_`, spending the approval given by `from_` to the
    /// calling contract. If `token_` returns no value, non-reverting calls are assumed to be successful.
    /// Minimally modified from Solmate SafeTransferLib (address as input param for token, Custom Error):
    /// https://github.com/transmissions11/solmate/blob/50e15bb566f98b7174da9b0066126a4c3e75e0fd/src/utils/SafeTransferLib.sol#L31-L63
    function safeTransferFrom(address token_, address from_, address to_, uint256 amount_) internal {
        bool success_;

        /// @solidity memory-safe-assembly
        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), and(from_, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "from_" argument.
            mstore(add(freeMemoryPointer, 36), and(to_, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to_" argument.
            mstore(add(freeMemoryPointer, 68), amount_) // Append the "amount_" argument. Masking not required as it's a full 32 byte type.

            success_ := and(
                // Set success to whether the call reverted, if not we check it either
                // returned exactly 1 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.
                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token_, 0, freeMemoryPointer, 100, 0, 32)
            )
        }

        if (!success_) {
            revert FluidSafeTransferError(ErrorTypes.SafeTransfer__TransferFromFailed);
        }
    }

    /// @dev Transfer `amount_` of `token_` to `to_`.
    /// If `token_` returns no value, non-reverting calls are assumed to be successful.
    /// Minimally modified from Solmate SafeTransferLib (address as input param for token, Custom Error):
    /// https://github.com/transmissions11/solmate/blob/50e15bb566f98b7174da9b0066126a4c3e75e0fd/src/utils/SafeTransferLib.sol#L65-L95
    function safeTransfer(address token_, address to_, uint256 amount_) internal {
        bool success_;

        /// @solidity memory-safe-assembly
        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), and(to_, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to_" argument.
            mstore(add(freeMemoryPointer, 36), amount_) // Append the "amount_" argument. Masking not required as it's a full 32 byte type.

            success_ := and(
                // Set success to whether the call reverted, if not we check it either
                // returned exactly 1 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token_, 0, freeMemoryPointer, 68, 0, 32)
            )
        }

        if (!success_) {
            revert FluidSafeTransferError(ErrorTypes.SafeTransfer__TransferFailed);
        }
    }

    /// @dev Transfer `amount_` of ` native token to `to_`.
    /// Minimally modified from Solmate SafeTransferLib (Custom Error):
    /// https://github.com/transmissions11/solmate/blob/50e15bb566f98b7174da9b0066126a4c3e75e0fd/src/utils/SafeTransferLib.sol#L15-L25
    function safeTransferNative(address to_, uint256 amount_) internal {
        bool success_;

        /// @solidity memory-safe-assembly
        assembly {
            // Transfer the ETH and store if it succeeded or not. Pass limited gas
            success_ := call(MAX_NATIVE_TRANSFER_GAS, to_, amount_, 0, 0, 0, 0)
        }

        if (!success_) {
            revert FluidSafeTransferError(ErrorTypes.SafeTransfer__TransferFailed);
        }
    }
}

File 11 of 25 : storageRead.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;

/// @notice implements a method to read uint256 data from storage at a bytes32 storage slot key.
contract StorageRead {
    function readFromStorage(bytes32 slot_) public view returns (uint256 result_) {
        assembly {
            result_ := sload(slot_) // read value from the storage slot
        }
    }
}

File 12 of 25 : structs.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;

abstract contract Structs {
    struct AddressBool {
        address addr;
        bool value;
    }

    struct AddressUint256 {
        address addr;
        uint256 value;
    }

    /// @notice struct to set borrow rate data for version 1
    struct RateDataV1Params {
        ///
        /// @param token for rate data
        address token;
        ///
        /// @param kink in borrow rate. in 1e2: 100% = 10_000; 1% = 100
        /// utilization below kink usually means slow increase in rate, once utilization is above kink borrow rate increases fast
        uint256 kink;
        ///
        /// @param rateAtUtilizationZero desired borrow rate when utilization is zero. in 1e2: 100% = 10_000; 1% = 100
        /// i.e. constant minimum borrow rate
        /// e.g. at utilization = 0.01% rate could still be at least 4% (rateAtUtilizationZero would be 400 then)
        uint256 rateAtUtilizationZero;
        ///
        /// @param rateAtUtilizationKink borrow rate when utilization is at kink. in 1e2: 100% = 10_000; 1% = 100
        /// e.g. when rate should be 7% at kink then rateAtUtilizationKink would be 700
        uint256 rateAtUtilizationKink;
        ///
        /// @param rateAtUtilizationMax borrow rate when utilization is maximum at 100%. in 1e2: 100% = 10_000; 1% = 100
        /// e.g. when rate should be 125% at 100% then rateAtUtilizationMax would be 12_500
        uint256 rateAtUtilizationMax;
    }

    /// @notice struct to set borrow rate data for version 2
    struct RateDataV2Params {
        ///
        /// @param token for rate data
        address token;
        ///
        /// @param kink1 first kink in borrow rate. in 1e2: 100% = 10_000; 1% = 100
        /// utilization below kink 1 usually means slow increase in rate, once utilization is above kink 1 borrow rate increases faster
        uint256 kink1;
        ///
        /// @param kink2 second kink in borrow rate. in 1e2: 100% = 10_000; 1% = 100
        /// utilization below kink 2 usually means slow / medium increase in rate, once utilization is above kink 2 borrow rate increases fast
        uint256 kink2;
        ///
        /// @param rateAtUtilizationZero desired borrow rate when utilization is zero. in 1e2: 100% = 10_000; 1% = 100
        /// i.e. constant minimum borrow rate
        /// e.g. at utilization = 0.01% rate could still be at least 4% (rateAtUtilizationZero would be 400 then)
        uint256 rateAtUtilizationZero;
        ///
        /// @param rateAtUtilizationKink1 desired borrow rate when utilization is at first kink. in 1e2: 100% = 10_000; 1% = 100
        /// e.g. when rate should be 7% at first kink then rateAtUtilizationKink would be 700
        uint256 rateAtUtilizationKink1;
        ///
        /// @param rateAtUtilizationKink2 desired borrow rate when utilization is at second kink. in 1e2: 100% = 10_000; 1% = 100
        /// e.g. when rate should be 7% at second kink then rateAtUtilizationKink would be 1_200
        uint256 rateAtUtilizationKink2;
        ///
        /// @param rateAtUtilizationMax desired borrow rate when utilization is maximum at 100%. in 1e2: 100% = 10_000; 1% = 100
        /// e.g. when rate should be 125% at 100% then rateAtUtilizationMax would be 12_500
        uint256 rateAtUtilizationMax;
    }

    /// @notice struct to set token config
    struct TokenConfig {
        ///
        /// @param token address
        address token;
        ///
        /// @param fee charges on borrower's interest. in 1e2: 100% = 10_000; 1% = 100
        uint256 fee;
        ///
        /// @param threshold on when to update the storage slot. in 1e2: 100% = 10_000; 1% = 100
        uint256 threshold;
        ///
        /// @param maxUtilization maximum allowed utilization. in 1e2: 100% = 10_000; 1% = 100
        ///                       set to 100% to disable and have default limit of 100% (avoiding SLOAD).
        uint256 maxUtilization;
    }

    /// @notice struct to set user supply & withdrawal config
    struct UserSupplyConfig {
        ///
        /// @param user address
        address user;
        ///
        /// @param token address
        address token;
        ///
        /// @param mode: 0 = without interest. 1 = with interest
        uint8 mode;
        ///
        /// @param expandPercent withdrawal limit expand percent. in 1e2: 100% = 10_000; 1% = 100
        /// Also used to calculate rate at which withdrawal limit should decrease (instant).
        uint256 expandPercent;
        ///
        /// @param expandDuration withdrawal limit expand duration in seconds.
        /// used to calculate rate together with expandPercent
        uint256 expandDuration;
        ///
        /// @param baseWithdrawalLimit base limit, below this, user can withdraw the entire amount.
        /// amount in raw (to be multiplied with exchange price) or normal depends on configured mode in user config for the token:
        /// with interest -> raw, without interest -> normal
        uint256 baseWithdrawalLimit;
    }

    /// @notice struct to set user borrow & payback config
    struct UserBorrowConfig {
        ///
        /// @param user address
        address user;
        ///
        /// @param token address
        address token;
        ///
        /// @param mode: 0 = without interest. 1 = with interest
        uint8 mode;
        ///
        /// @param expandPercent debt limit expand percent. in 1e2: 100% = 10_000; 1% = 100
        /// Also used to calculate rate at which debt limit should decrease (instant).
        uint256 expandPercent;
        ///
        /// @param expandDuration debt limit expand duration in seconds.
        /// used to calculate rate together with expandPercent
        uint256 expandDuration;
        ///
        /// @param baseDebtCeiling base borrow limit. until here, borrow limit remains as baseDebtCeiling
        /// (user can borrow until this point at once without stepped expansion). Above this, automated limit comes in place.
        /// amount in raw (to be multiplied with exchange price) or normal depends on configured mode in user config for the token:
        /// with interest -> raw, without interest -> normal
        uint256 baseDebtCeiling;
        ///
        /// @param maxDebtCeiling max borrow ceiling, maximum amount the user can borrow.
        /// amount in raw (to be multiplied with exchange price) or normal depends on configured mode in user config for the token:
        /// with interest -> raw, without interest -> normal
        uint256 maxDebtCeiling;
    }
}

File 13 of 25 : iLiquidity.sol
//SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

import { IProxy } from "../../infiniteProxy/interfaces/iProxy.sol";
import { Structs as AdminModuleStructs } from "../adminModule/structs.sol";

interface IFluidLiquidityAdmin {
    /// @notice adds/removes auths. Auths generally could be contracts which can have restricted actions defined on contract.
    ///         auths can be helpful in reducing governance overhead where it's not needed.
    /// @param authsStatus_ array of structs setting allowed status for an address.
    ///                     status true => add auth, false => remove auth
    function updateAuths(AdminModuleStructs.AddressBool[] calldata authsStatus_) external;

    /// @notice adds/removes guardians. Only callable by Governance.
    /// @param guardiansStatus_ array of structs setting allowed status for an address.
    ///                         status true => add guardian, false => remove guardian
    function updateGuardians(AdminModuleStructs.AddressBool[] calldata guardiansStatus_) external;

    /// @notice changes the revenue collector address (contract that is sent revenue). Only callable by Governance.
    /// @param revenueCollector_  new revenue collector address
    function updateRevenueCollector(address revenueCollector_) external;

    /// @notice changes current status, e.g. for pausing or unpausing all user operations. Only callable by Auths.
    /// @param newStatus_ new status
    ///        status = 2 -> pause, status = 1 -> resume.
    function changeStatus(uint256 newStatus_) external;

    /// @notice                  update tokens rate data version 1. Only callable by Auths.
    /// @param tokensRateData_   array of RateDataV1Params with rate data to set for each token
    function updateRateDataV1s(AdminModuleStructs.RateDataV1Params[] calldata tokensRateData_) external;

    /// @notice                  update tokens rate data version 2. Only callable by Auths.
    /// @param tokensRateData_   array of RateDataV2Params with rate data to set for each token
    function updateRateDataV2s(AdminModuleStructs.RateDataV2Params[] calldata tokensRateData_) external;

    /// @notice updates token configs: fee charge on borrowers interest & storage update utilization threshold.
    ///         Only callable by Auths.
    /// @param tokenConfigs_ contains token address, fee & utilization threshold
    function updateTokenConfigs(AdminModuleStructs.TokenConfig[] calldata tokenConfigs_) external;

    /// @notice updates user classes: 0 is for new protocols, 1 is for established protocols.
    ///         Only callable by Auths.
    /// @param userClasses_ struct array of uint256 value to assign for each user address
    function updateUserClasses(AdminModuleStructs.AddressUint256[] calldata userClasses_) external;

    /// @notice sets user supply configs per token basis. Eg: with interest or interest-free and automated limits.
    ///         Only callable by Auths.
    /// @param userSupplyConfigs_ struct array containing user supply config, see `UserSupplyConfig` struct for more info
    function updateUserSupplyConfigs(AdminModuleStructs.UserSupplyConfig[] memory userSupplyConfigs_) external;

    /// @notice sets a new withdrawal limit as the current limit for a certain user
    /// @param user_ user address for which to update the withdrawal limit
    /// @param token_ token address for which to update the withdrawal limit
    /// @param newLimit_ new limit until which user supply can decrease to.
    ///                  Important: input in raw. Must account for exchange price in input param calculation.
    ///                  Note any limit that is < max expansion or > current user supply will set max expansion limit or
    ///                  current user supply as limit respectively.
    ///                  - set 0 to make maximum possible withdrawable: instant full expansion, and if that goes
    ///                  below base limit then fully down to 0.
    ///                  - set type(uint256).max to make current withdrawable 0 (sets current user supply as limit).
    function updateUserWithdrawalLimit(address user_, address token_, uint256 newLimit_) external;

    /// @notice setting user borrow configs per token basis. Eg: with interest or interest-free and automated limits.
    ///         Only callable by Auths.
    /// @param userBorrowConfigs_ struct array containing user borrow config, see `UserBorrowConfig` struct for more info
    function updateUserBorrowConfigs(AdminModuleStructs.UserBorrowConfig[] memory userBorrowConfigs_) external;

    /// @notice pause operations for a particular user in class 0 (class 1 users can't be paused by guardians).
    /// Only callable by Guardians.
    /// @param user_          address of user to pause operations for
    /// @param supplyTokens_  token addresses to pause withdrawals for
    /// @param borrowTokens_  token addresses to pause borrowings for
    function pauseUser(address user_, address[] calldata supplyTokens_, address[] calldata borrowTokens_) external;

    /// @notice unpause operations for a particular user in class 0 (class 1 users can't be paused by guardians).
    /// Only callable by Guardians.
    /// @param user_          address of user to unpause operations for
    /// @param supplyTokens_  token addresses to unpause withdrawals for
    /// @param borrowTokens_  token addresses to unpause borrowings for
    function unpauseUser(address user_, address[] calldata supplyTokens_, address[] calldata borrowTokens_) external;

    /// @notice         collects revenue for tokens to configured revenueCollector address.
    /// @param tokens_  array of tokens to collect revenue for
    /// @dev            Note that this can revert if token balance is < revenueAmount (utilization > 100%)
    function collectRevenue(address[] calldata tokens_) external;

    /// @notice gets the current updated exchange prices for n tokens and updates all prices, rates related data in storage.
    /// @param tokens_ tokens to update exchange prices for
    /// @return supplyExchangePrices_ new supply rates of overall system for each token
    /// @return borrowExchangePrices_ new borrow rates of overall system for each token
    function updateExchangePrices(
        address[] calldata tokens_
    ) external returns (uint256[] memory supplyExchangePrices_, uint256[] memory borrowExchangePrices_);
}

interface IFluidLiquidityLogic is IFluidLiquidityAdmin {
    /// @notice Single function which handles supply, withdraw, borrow & payback
    /// @param token_ address of token (0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE for native)
    /// @param supplyAmount_ if +ve then supply, if -ve then withdraw, if 0 then nothing
    /// @param borrowAmount_ if +ve then borrow, if -ve then payback, if 0 then nothing
    /// @param withdrawTo_ if withdrawal then to which address
    /// @param borrowTo_ if borrow then to which address
    /// @param callbackData_ callback data passed to `liquidityCallback` method of protocol
    /// @return memVar3_ updated supplyExchangePrice
    /// @return memVar4_ updated borrowExchangePrice
    /// @dev to trigger skipping in / out transfers (gas optimization):
    /// -  ` callbackData_` MUST be encoded so that "from" address is the last 20 bytes in the last 32 bytes slot,
    ///     also for native token operations where liquidityCallback is not triggered!
    ///     from address must come at last position if there is more data. I.e. encode like:
    ///     abi.encode(otherVar1, otherVar2, FROM_ADDRESS). Note dynamic types used with abi.encode come at the end
    ///     so if dynamic types are needed, you must use abi.encodePacked to ensure the from address is at the end.
    /// -   this "from" address must match withdrawTo_ or borrowTo_ and must be == `msg.sender`
    /// -   `callbackData_` must in addition to the from address as described above include bytes32 SKIP_TRANSFERS
    ///     in the slot before (bytes 32 to 63)
    /// -   `msg.value` must be 0.
    /// -   Amounts must be either:
    ///     -  supply(+) == borrow(+), withdraw(-) == payback(-).
    ///     -  Liquidity must be on the winning side (deposit < borrow OR payback < withdraw).
    function operate(
        address token_,
        int256 supplyAmount_,
        int256 borrowAmount_,
        address withdrawTo_,
        address borrowTo_,
        bytes calldata callbackData_
    ) external payable returns (uint256 memVar3_, uint256 memVar4_);
}

interface IFluidLiquidity is IProxy, IFluidLiquidityLogic {}

File 14 of 25 : error.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;

import { Structs } from "./poolT1/coreModule/structs.sol";

abstract contract Error {
    error FluidDexError(uint256 errorId_);

    error FluidDexFactoryError(uint256 errorId);

    /// @notice used to simulate swap to find the output amount
    error FluidDexSwapResult(uint256 amountOut);

    error FluidDexPerfectLiquidityOutput(uint256 token0Amt, uint token1Amt);

    error FluidDexSingleTokenOutput(uint256 tokenAmt);

    error FluidDexLiquidityOutput(uint256 shares_);

    error FluidDexPricesAndExchangeRates(Structs.PricesAndExchangePrice pex_);
}

File 15 of 25 : errorTypes.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;

library ErrorTypes {
    /***********************************|
    |             DexT1                 | 
    |__________________________________*/

    /// @notice thrown at reentrancy
    uint256 internal constant DexT1__AlreadyEntered = 51001;

    uint256 internal constant DexT1__NotAnAuth = 51002;

    uint256 internal constant DexT1__SmartColNotEnabled = 51003;

    uint256 internal constant DexT1__SmartDebtNotEnabled = 51004;

    uint256 internal constant DexT1__PoolNotInitialized = 51005;

    uint256 internal constant DexT1__TokenReservesTooLow = 51006;

    uint256 internal constant DexT1__EthAndAmountInMisMatch = 51007;

    uint256 internal constant DexT1__EthSentForNonNativeSwap = 51008;

    uint256 internal constant DexT1__NoSwapRoute = 51009;

    uint256 internal constant DexT1__NotEnoughAmountOut = 51010;

    uint256 internal constant DexT1__LiquidityLayerTokenUtilizationCapReached = 51011;

    uint256 internal constant DexT1__HookReturnedFalse = 51012;

    // Either user's config are not set or user is paused
    uint256 internal constant DexT1__UserSupplyInNotOn = 51013;

    // Either user's config are not set or user is paused
    uint256 internal constant DexT1__UserDebtInNotOn = 51014;

    // Thrown when contract asks for more token0 or token1 than what user's wants to give on deposit
    uint256 internal constant DexT1__AboveDepositMax = 51015;

    uint256 internal constant DexT1__MsgValueLowOnDepositOrPayback = 51016;

    uint256 internal constant DexT1__WithdrawLimitReached = 51017;

    // Thrown when contract gives less token0 or token1 than what user's wants on withdraw
    uint256 internal constant DexT1__BelowWithdrawMin = 51018;

    uint256 internal constant DexT1__DebtLimitReached = 51019;

    // Thrown when contract gives less token0 or token1 than what user's wants on borrow
    uint256 internal constant DexT1__BelowBorrowMin = 51020;

    // Thrown when contract asks for more token0 or token1 than what user's wants on payback
    uint256 internal constant DexT1__AbovePaybackMax = 51021;

    uint256 internal constant DexT1__InvalidDepositAmts = 51022;

    uint256 internal constant DexT1__DepositAmtsZero = 51023;

    uint256 internal constant DexT1__SharesMintedLess = 51024;

    uint256 internal constant DexT1__WithdrawalNotEnough = 51025;

    uint256 internal constant DexT1__InvalidWithdrawAmts = 51026;

    uint256 internal constant DexT1__WithdrawAmtsZero = 51027;

    uint256 internal constant DexT1__WithdrawExcessSharesBurn = 51028;

    uint256 internal constant DexT1__InvalidBorrowAmts = 51029;

    uint256 internal constant DexT1__BorrowAmtsZero = 51030;

    uint256 internal constant DexT1__BorrowExcessSharesMinted = 51031;

    uint256 internal constant DexT1__PaybackAmtTooHigh = 51032;

    uint256 internal constant DexT1__InvalidPaybackAmts = 51033;

    uint256 internal constant DexT1__PaybackAmtsZero = 51034;

    uint256 internal constant DexT1__PaybackSharedBurnedLess = 51035;

    uint256 internal constant DexT1__NothingToArbitrage = 51036;

    uint256 internal constant DexT1__MsgSenderNotLiquidity = 51037;

    // On liquidity callback reentrancy bit should be on
    uint256 internal constant DexT1__ReentrancyBitShouldBeOn = 51038;

    // Thrown is reentrancy is already on and someone tries to fetch oracle price. Should not be possible to this
    uint256 internal constant DexT1__OraclePriceFetchAlreadyEntered = 51039;

    // Thrown when swap changes the current price by more than 5%
    uint256 internal constant DexT1__OracleUpdateHugeSwapDiff = 51040;

    uint256 internal constant DexT1__Token0ShouldBeSmallerThanToken1 = 51041;

    uint256 internal constant DexT1__OracleMappingOverflow = 51042;

    /// @notice thrown if governance has paused the swapping & arbitrage so only perfect functions are usable
    uint256 internal constant DexT1__SwapAndArbitragePaused = 51043;

    uint256 internal constant DexT1__ExceedsAmountInMax = 51044;

    /// @notice thrown if amount in is too high or too low
    uint256 internal constant DexT1__SwapInLimitingAmounts = 51045;

    /// @notice thrown if amount out is too high or too low
    uint256 internal constant DexT1__SwapOutLimitingAmounts = 51046;

    uint256 internal constant DexT1__MintAmtOverflow = 51047;

    uint256 internal constant DexT1__BurnAmtOverflow = 51048;

    uint256 internal constant DexT1__LimitingAmountsSwapAndNonPerfectActions = 51049;

    uint256 internal constant DexT1__InsufficientOracleData = 51050;

    uint256 internal constant DexT1__SharesAmountInsufficient = 51051;

    uint256 internal constant DexT1__CenterPriceOutOfRange = 51052;

    uint256 internal constant DexT1__DebtReservesTooLow = 51053;

    uint256 internal constant DexT1__SwapAndDepositTooLowOrTooHigh = 51054;

    uint256 internal constant DexT1__WithdrawAndSwapTooLowOrTooHigh = 51055;

    uint256 internal constant DexT1__BorrowAndSwapTooLowOrTooHigh = 51056;

    uint256 internal constant DexT1__SwapAndPaybackTooLowOrTooHigh = 51057;

    uint256 internal constant DexT1__InvalidImplementation = 51058;

    uint256 internal constant DexT1__OnlyDelegateCallAllowed = 51059;

    uint256 internal constant DexT1__IncorrectDataLength = 51060;

    uint256 internal constant DexT1__AmountToSendLessThanAmount = 51061;

    uint256 internal constant DexT1__InvalidCollateralReserves = 51062;

    uint256 internal constant DexT1__InvalidDebtReserves = 51063;

    uint256 internal constant DexT1__SupplySharesOverflow = 51064;

    uint256 internal constant DexT1__BorrowSharesOverflow = 51065;

    uint256 internal constant DexT1__OracleNotActive = 51066;

    /***********************************|
    |            DEX Admin              | 
    |__________________________________*/

    /// @notice thrown when pool is not initialized
    uint256 internal constant DexT1Admin__PoolNotInitialized = 52001;

    uint256 internal constant DexT1Admin__SmartColIsAlreadyOn = 52002;

    uint256 internal constant DexT1Admin__SmartDebtIsAlreadyOn = 52003;

    /// @notice thrown when any of the configs value overflow the maximum limit
    uint256 internal constant DexT1Admin__ConfigOverflow = 52004;

    uint256 internal constant DexT1Admin__AddressNotAContract = 52005;

    uint256 internal constant DexT1Admin__InvalidParams = 52006;

    uint256 internal constant DexT1Admin__UserNotDefined = 52007;

    uint256 internal constant DexT1Admin__OnlyDelegateCallAllowed = 52008;

    uint256 internal constant DexT1Admin__UnexpectedPoolState = 52009;

    /// @notice thrown when trying to pause or unpause but user is already in the target pause state
    uint256 internal constant DexT1Admin__InvalidPauseToggle = 52009;

    /***********************************|
    |            DEX Factory            | 
    |__________________________________*/

    uint256 internal constant DexFactory__InvalidOperation = 53001;
    uint256 internal constant DexFactory__Unauthorized = 53002;
    uint256 internal constant DexFactory__SameTokenNotAllowed = 53003;
    uint256 internal constant DexFactory__TokenConfigNotProper = 53004;
    uint256 internal constant DexFactory__InvalidParams = 53005;
    uint256 internal constant DexFactory__OnlyDelegateCallAllowed = 53006;
    uint256 internal constant DexFactory__InvalidDexAddress = 53007;
}

File 16 of 25 : iDexFactory.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

interface IFluidDexFactory {
    /// @notice Global auth is auth for all dexes
    function isGlobalAuth(address auth_) external view returns (bool);

    /// @notice Dex auth is auth for a specific dex
    function isDexAuth(address vault_, address auth_) external view returns (bool);

    /// @notice Total dexes deployed.
    function totalDexes() external view returns (uint256);

    /// @notice Compute dexAddress
    function getDexAddress(uint256 dexId_) external view returns (address);

    /// @notice read uint256 `result_` for a storage `slot_` key
    function readFromStorage(bytes32 slot_) external view returns (uint256 result_);
}

File 17 of 25 : iDexT1.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

interface IFluidDexT1 {
    error FluidDexError(uint256 errorId);

    /// @notice used to simulate swap to find the output amount
    error FluidDexSwapResult(uint256 amountOut);

    error FluidDexPerfectLiquidityOutput(uint256 token0Amt, uint token1Amt);

    error FluidDexSingleTokenOutput(uint256 tokenAmt);

    error FluidDexLiquidityOutput(uint256 shares);

    error FluidDexPricesAndExchangeRates(PricesAndExchangePrice pex_);

    /// @notice returns the dex id
    function DEX_ID() external view returns (uint256);

    /// @notice reads uint256 data `result_` from storage at a bytes32 storage `slot_` key.
    function readFromStorage(bytes32 slot_) external view returns (uint256 result_);

    struct Implementations {
        address shift;
        address admin;
        address colOperations;
        address debtOperations;
        address perfectOperationsAndOracle;
    }

    struct ConstantViews {
        uint256 dexId;
        address liquidity;
        address factory;
        Implementations implementations;
        address deployerContract;
        address token0;
        address token1;
        bytes32 supplyToken0Slot;
        bytes32 borrowToken0Slot;
        bytes32 supplyToken1Slot;
        bytes32 borrowToken1Slot;
        bytes32 exchangePriceToken0Slot;
        bytes32 exchangePriceToken1Slot;
        uint256 oracleMapping;
    }

    struct ConstantViews2 {
        uint token0NumeratorPrecision;
        uint token0DenominatorPrecision;
        uint token1NumeratorPrecision;
        uint token1DenominatorPrecision;
    }

    struct PricesAndExchangePrice {
        uint lastStoredPrice; // last stored price in 1e27 decimals
        uint centerPrice; // last stored price in 1e27 decimals
        uint upperRange; // price at upper range in 1e27 decimals
        uint lowerRange; // price at lower range in 1e27 decimals
        uint geometricMean; // geometric mean of upper range & lower range in 1e27 decimals
        uint supplyToken0ExchangePrice;
        uint borrowToken0ExchangePrice;
        uint supplyToken1ExchangePrice;
        uint borrowToken1ExchangePrice;
    }

    struct CollateralReserves {
        uint token0RealReserves;
        uint token1RealReserves;
        uint token0ImaginaryReserves;
        uint token1ImaginaryReserves;
    }

    struct DebtReserves {
        uint token0Debt;
        uint token1Debt;
        uint token0RealReserves;
        uint token1RealReserves;
        uint token0ImaginaryReserves;
        uint token1ImaginaryReserves;
    }

    function getCollateralReserves(
        uint geometricMean_,
        uint upperRange_,
        uint lowerRange_,
        uint token0SupplyExchangePrice_,
        uint token1SupplyExchangePrice_
    ) external view returns (CollateralReserves memory c_);

    function getDebtReserves(
        uint geometricMean_,
        uint upperRange_,
        uint lowerRange_,
        uint token0BorrowExchangePrice_,
        uint token1BorrowExchangePrice_
    ) external view returns (DebtReserves memory d_);

    // reverts with FluidDexPricesAndExchangeRates(pex_);
    function getPricesAndExchangePrices() external;

    function constantsView() external view returns (ConstantViews memory constantsView_);

    function constantsView2() external view returns (ConstantViews2 memory constantsView2_);

    struct Oracle {
        uint twap1by0; // TWAP price
        uint lowestPrice1by0; // lowest price point
        uint highestPrice1by0; // highest price point
        uint twap0by1; // TWAP price
        uint lowestPrice0by1; // lowest price point
        uint highestPrice0by1; // highest price point
    }

    /// @dev This function allows users to swap a specific amount of input tokens for output tokens
    /// @param swap0to1_ Direction of swap. If true, swaps token0 for token1; if false, swaps token1 for token0
    /// @param amountIn_ The exact amount of input tokens to swap
    /// @param amountOutMin_ The minimum amount of output tokens the user is willing to accept
    /// @param to_ Recipient of swapped tokens. If to_ == address(0) then out tokens will be sent to msg.sender. If to_ == ADDRESS_DEAD then function will revert with amountOut_
    /// @return amountOut_ The amount of output tokens received from the swap
    function swapIn(
        bool swap0to1_,
        uint256 amountIn_,
        uint256 amountOutMin_,
        address to_
    ) external payable returns (uint256 amountOut_);

    /// @dev Swap tokens with perfect amount out
    /// @param swap0to1_ Direction of swap. If true, swaps token0 for token1; if false, swaps token1 for token0
    /// @param amountOut_ The exact amount of tokens to receive after swap
    /// @param amountInMax_ Maximum amount of tokens to swap in
    /// @param to_ Recipient of swapped tokens. If to_ == address(0) then out tokens will be sent to msg.sender. If to_ == ADDRESS_DEAD then function will revert with amountIn_
    /// @return amountIn_ The amount of input tokens used for the swap
    function swapOut(
        bool swap0to1_,
        uint256 amountOut_,
        uint256 amountInMax_,
        address to_
    ) external payable returns (uint256 amountIn_);

    /// @dev Deposit tokens in equal proportion to the current pool ratio
    /// @param shares_ The number of shares to mint
    /// @param maxToken0Deposit_ Maximum amount of token0 to deposit
    /// @param maxToken1Deposit_ Maximum amount of token1 to deposit
    /// @param estimate_ If true, function will revert with estimated deposit amounts without executing the deposit
    /// @return token0Amt_ Amount of token0 deposited
    /// @return token1Amt_ Amount of token1 deposited
    function depositPerfect(
        uint shares_,
        uint maxToken0Deposit_,
        uint maxToken1Deposit_,
        bool estimate_
    ) external payable returns (uint token0Amt_, uint token1Amt_);

    /// @dev This function allows users to withdraw a perfect amount of collateral liquidity
    /// @param shares_ The number of shares to withdraw
    /// @param minToken0Withdraw_ The minimum amount of token0 the user is willing to accept
    /// @param minToken1Withdraw_ The minimum amount of token1 the user is willing to accept
    /// @param to_ Recipient of swapped tokens. If to_ == address(0) then out tokens will be sent to msg.sender. If to_ == ADDRESS_DEAD then function will revert with token0Amt_ & token1Amt_
    /// @return token0Amt_ The amount of token0 withdrawn
    /// @return token1Amt_ The amount of token1 withdrawn
    function withdrawPerfect(
        uint shares_,
        uint minToken0Withdraw_,
        uint minToken1Withdraw_,
        address to_
    ) external returns (uint token0Amt_, uint token1Amt_);

    /// @dev This function allows users to borrow tokens in equal proportion to the current debt pool ratio
    /// @param shares_ The number of shares to borrow
    /// @param minToken0Borrow_ Minimum amount of token0 to borrow
    /// @param minToken1Borrow_ Minimum amount of token1 to borrow
    /// @param to_ Recipient of swapped tokens. If to_ == address(0) then out tokens will be sent to msg.sender. If to_ == ADDRESS_DEAD then function will revert with token0Amt_ & token1Amt_
    /// @return token0Amt_ Amount of token0 borrowed
    /// @return token1Amt_ Amount of token1 borrowed
    function borrowPerfect(
        uint shares_,
        uint minToken0Borrow_,
        uint minToken1Borrow_,
        address to_
    ) external returns (uint token0Amt_, uint token1Amt_);

    /// @dev This function allows users to pay back borrowed tokens in equal proportion to the current debt pool ratio
    /// @param shares_ The number of shares to pay back
    /// @param maxToken0Payback_ Maximum amount of token0 to pay back
    /// @param maxToken1Payback_ Maximum amount of token1 to pay back
    /// @param estimate_ If true, function will revert with estimated payback amounts without executing the payback
    /// @return token0Amt_ Amount of token0 paid back
    /// @return token1Amt_ Amount of token1 paid back
    function paybackPerfect(
        uint shares_,
        uint maxToken0Payback_,
        uint maxToken1Payback_,
        bool estimate_
    ) external payable returns (uint token0Amt_, uint token1Amt_);

    /// @dev This function allows users to deposit tokens in any proportion into the col pool
    /// @param token0Amt_ The amount of token0 to deposit
    /// @param token1Amt_ The amount of token1 to deposit
    /// @param minSharesAmt_ The minimum amount of shares the user expects to receive
    /// @param estimate_ If true, function will revert with estimated shares without executing the deposit
    /// @return shares_ The amount of shares minted for the deposit
    function deposit(
        uint token0Amt_,
        uint token1Amt_,
        uint minSharesAmt_,
        bool estimate_
    ) external payable returns (uint shares_);

    /// @dev This function allows users to withdraw tokens in any proportion from the col pool
    /// @param token0Amt_ The amount of token0 to withdraw
    /// @param token1Amt_ The amount of token1 to withdraw
    /// @param maxSharesAmt_ The maximum number of shares the user is willing to burn
    /// @param to_ Recipient of swapped tokens. If to_ == address(0) then out tokens will be sent to msg.sender. If to_ == ADDRESS_DEAD then function will revert with shares_
    /// @return shares_ The number of shares burned for the withdrawal
    function withdraw(
        uint token0Amt_,
        uint token1Amt_,
        uint maxSharesAmt_,
        address to_
    ) external returns (uint shares_);

    /// @dev This function allows users to borrow tokens in any proportion from the debt pool
    /// @param token0Amt_ The amount of token0 to borrow
    /// @param token1Amt_ The amount of token1 to borrow
    /// @param maxSharesAmt_ The maximum amount of shares the user is willing to receive
    /// @param to_ Recipient of swapped tokens. If to_ == address(0) then out tokens will be sent to msg.sender. If to_ == ADDRESS_DEAD then function will revert with shares_
    /// @return shares_ The amount of borrow shares minted to represent the borrowed amount
    function borrow(
        uint token0Amt_,
        uint token1Amt_,
        uint maxSharesAmt_,
        address to_
    ) external returns (uint shares_);

    /// @dev This function allows users to payback tokens in any proportion to the debt pool
    /// @param token0Amt_ The amount of token0 to payback
    /// @param token1Amt_ The amount of token1 to payback
    /// @param minSharesAmt_ The minimum amount of shares the user expects to burn
    /// @param estimate_ If true, function will revert with estimated shares without executing the payback
    /// @return shares_ The amount of borrow shares burned for the payback
    function payback(
        uint token0Amt_,
        uint token1Amt_,
        uint minSharesAmt_,
        bool estimate_
    ) external payable returns (uint shares_);

    /// @dev This function allows users to withdraw their collateral with perfect shares in one token
    /// @param shares_ The number of shares to burn for withdrawal
    /// @param minToken0_ The minimum amount of token0 the user expects to receive (set to 0 if withdrawing in token1)
    /// @param minToken1_ The minimum amount of token1 the user expects to receive (set to 0 if withdrawing in token0)
    /// @param to_ Recipient of swapped tokens. If to_ == address(0) then out tokens will be sent to msg.sender. If to_ == ADDRESS_DEAD then function will revert with withdrawAmt_
    /// @return withdrawAmt_ The amount of tokens withdrawn in the chosen token
    function withdrawPerfectInOneToken(
        uint shares_,
        uint minToken0_,
        uint minToken1_,
        address to_
    ) external returns (
        uint withdrawAmt_
    );

    /// @dev This function allows users to payback their debt with perfect shares in one token
    /// @param shares_ The number of shares to burn for payback
    /// @param maxToken0_ The maximum amount of token0 the user is willing to pay (set to 0 if paying back in token1)
    /// @param maxToken1_ The maximum amount of token1 the user is willing to pay (set to 0 if paying back in token0)
    /// @param estimate_ If true, the function will revert with the estimated payback amount without executing the payback
    /// @return paybackAmt_ The amount of tokens paid back in the chosen token
    function paybackPerfectInOneToken(
        uint shares_,
        uint maxToken0_,
        uint maxToken1_,
        bool estimate_
    ) external payable returns (
        uint paybackAmt_
    );

    /// @dev the oracle assumes last set price of pool till the next swap happens.
    /// There's a possibility that during that time some interest is generated hence the last stored price is not the 100% correct price for the whole duration
    /// but the difference due to interest will be super low so this difference is ignored
    /// For example 2 swaps happened 10min (600 seconds) apart and 1 token has 10% higher interest than other.
    /// then that token will accrue about 10% * 600 / secondsInAYear = ~0.0002%
    /// @param secondsAgos_ array of seconds ago for which TWAP is needed. If user sends [10, 30, 60] then twaps_ will return [10-0, 30-10, 60-30]
    /// @return twaps_ twap price, lowest price (aka minima) & highest price (aka maxima) between secondsAgo checkpoints
    /// @return currentPrice_ price of pool after the most recent swap
    function oraclePrice(
        uint[] memory secondsAgos_
    ) external view returns (
        Oracle[] memory twaps_,
        uint currentPrice_
    );
}

File 18 of 25 : constantVariables.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;

import { StorageRead } from "../../../../libraries/storageRead.sol";

interface ITokenDecimals {
    function decimals() external view returns (uint8);
}

abstract contract ConstantVariables is StorageRead {
    /*//////////////////////////////////////////////////////////////
                          CONSTANTS / IMMUTABLES
    //////////////////////////////////////////////////////////////*/

    address internal constant TEAM_MULTISIG = 0x4F6F977aCDD1177DCD81aB83074855EcB9C2D49e;

    address internal constant NATIVE_TOKEN = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
    uint256 internal constant NATIVE_TOKEN_DECIMALS = 18;
    address internal constant ADDRESS_DEAD = 0x000000000000000000000000000000000000dEaD;
    uint256 internal constant TOKENS_DECIMALS_PRECISION = 12;
    uint256 internal constant TOKENS_DECIMALS = 1e12;

    uint256 internal constant SMALL_COEFFICIENT_SIZE = 10;
    uint256 internal constant DEFAULT_COEFFICIENT_SIZE = 56;
    uint256 internal constant DEFAULT_EXPONENT_SIZE = 8;
    uint256 internal constant DEFAULT_EXPONENT_MASK = 0xFF;

    uint256 internal constant X2 = 0x3;
    uint256 internal constant X3 = 0x7;
    uint256 internal constant X5 = 0x1f;
    uint256 internal constant X7 = 0x7f;
    uint256 internal constant X8 = 0xff;
    uint256 internal constant X9 = 0x1ff;
    uint256 internal constant X10 = 0x3ff;
    uint256 internal constant X11 = 0x7ff;
    uint256 internal constant X14 = 0x3fff;
    uint256 internal constant X16 = 0xffff;
    uint256 internal constant X17 = 0x1ffff;
    uint256 internal constant X18 = 0x3ffff;
    uint256 internal constant X20 = 0xfffff;
    uint256 internal constant X22 = 0x3fffff;
    uint256 internal constant X23 = 0x7fffff;
    uint256 internal constant X24 = 0xffffff;
    uint256 internal constant X28 = 0xfffffff;
    uint256 internal constant X30 = 0x3fffffff;
    uint256 internal constant X32 = 0xffffffff;
    uint256 internal constant X33 = 0x1ffffffff;
    uint256 internal constant X40 = 0xffffffffff;
    uint256 internal constant X64 = 0xffffffffffffffff;
    uint256 internal constant X96 = 0xffffffffffffffffffffffff;
    uint256 internal constant X128 = 0xffffffffffffffffffffffffffffffff;

    uint256 internal constant TWO_DECIMALS = 1e2;
    uint256 internal constant THREE_DECIMALS = 1e3;
    uint256 internal constant FOUR_DECIMALS = 1e4;
    uint256 internal constant FIVE_DECIMALS = 1e5;
    uint256 internal constant SIX_DECIMALS = 1e6;
    uint256 internal constant EIGHT_DECIMALS = 1e8;
    uint256 internal constant NINE_DECIMALS = 1e9;

    uint256 internal constant PRICE_PRECISION = 1e27;

    uint256 internal constant ORACLE_PRECISION = 1e18; // 100%
    uint256 internal constant ORACLE_LIMIT = 5 * 1e16; // 5%

    /// after swap token0 reserves should not be less than token1InToken0 / MINIMUM_LIQUIDITY_SWAP
    /// after swap token1 reserves should not be less than token0InToken1 / MINIMUM_LIQUIDITY_SWAP
    uint256 internal constant MINIMUM_LIQUIDITY_SWAP = 1e4;

    /// after user operations (deposit, withdraw, borrow, payback) token0 reserves should not be less than token1InToken0 / MINIMUM_LIQUIDITY_USER_OPERATIONS
    /// after user operations (deposit, withdraw, borrow, payback) token1 reserves should not be less than token0InToken0 / MINIMUM_LIQUIDITY_USER_OPERATIONS
    uint256 internal constant MINIMUM_LIQUIDITY_USER_OPERATIONS = 1e6;

    /// To skip transfers in liquidity layer if token in & out is same and liquidity layer is on the winning side
    bytes32 internal constant SKIP_TRANSFERS = keccak256(bytes("SKIP_TRANSFERS"));

    function _decimals(address token_) internal view returns (uint256) {
        return (token_ == NATIVE_TOKEN) ? NATIVE_TOKEN_DECIMALS : ITokenDecimals(token_).decimals();
    }
}

File 19 of 25 : variables.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;

abstract contract Variables {
    /*//////////////////////////////////////////////////////////////
                          STORAGE VARIABLES
    //////////////////////////////////////////////////////////////*/

    /// First 1 bit  => 0 => re-entrancy. If 0 then allow transaction to go, else throw.
    /// Next 40 bits => 1-40 => last to last stored price. BigNumber (32 bits precision, 8 bits exponent)
    /// Next 40 bits => 41-80 => last stored price of pool. BigNumber (32 bits precision, 8 bits exponent)
    /// Next 40 bits => 81-120 => center price. Center price from where the ranges will be calculated. BigNumber (32 bits precision, 8 bits exponent)
    /// Next 33 bits => 121-153 => last interaction time stamp
    /// Next 22 bits => 154-175 => max 4194303 seconds (~1165 hrs, ~48.5 days), time difference between last to last and last price stored
    /// Next 3 bits  => 176-178 => oracle checkpoint, if 0 then first slot, if 7 then last slot
    /// Next 16 bits => 179-194 => current mapping or oracle, after every 8 transaction it will increase by 1. Max capacity is 65535 but it can be lower than that check dexVariables2
    /// Next 1 bit  => 195 => is oracle active?
    uint internal dexVariables;

    /// Next  1 bit  => 0 => is smart collateral enabled?
    /// Next  1 bit  => 1 => is smart debt enabled?
    /// Next 17 bits => 2-18 => fee (1% = 10000, max value: 100000 = 10%, fee should not be more than 10%)
    /// Next  7 bits => 19-25 => revenue cut from fee (1 = 1%, 100 = 100%). If fee is 1000 = 0.1% and revenue cut is 10 = 10% then governance get 0.01% of every swap
    /// Next  1 bit  => 26 => percent active change going on or not, 0 = false, 1 = true, if true than that means governance has updated the below percents and the update should happen with a specified time.
    /// Next 20 bits => 27-46 => upperPercent (1% = 10000, max value: 104.8575%) upperRange - upperRange * upperPercent = centerPrice. Hence, upperRange = centerPrice / (1 - upperPercent)
    /// Next 20 bits => 47-66 => lowerPercent. lowerRange = centerPrice - centerPrice * lowerPercent.
    /// Next  1 bit  => 67 => threshold percent active change going on or not, 0 = false, 1 = true, if true than that means governance has updated the below percents and the update should happen with a specified time.
    /// Next 10 bits => 68-77 => upper shift threshold percent, 1 = 0.1%. 1000 = 100%. if currentPrice > (centerPrice + (upperRange - centerPrice) * (1000 - upperShiftThresholdPercent) / 1000) then trigger shift
    /// Next 10 bits => 78-87 => lower shift threshold percent, 1 = 0.1%. 1000 = 100%. if currentPrice < (centerPrice - (centerPrice - lowerRange) * (1000 - lowerShiftThresholdPercent) / 1000) then trigger shift
    /// Next 24 bits => 88-111 => Shifting time (~194 days) (rate = (% up + % down) / time ?)
    /// Next 30 bits => 112-131 => Address of center price if center price should be fetched externally, for example, for wstETH <> ETH pool, fetch wstETH exchange rate into stETH from wstETH contract.
    /// Why fetch it externally? Because let's say pool width is 0.1% and wstETH temporarily got depeg of 0.5% then pool will start to shift to newer pricing
    /// but we don't want pool to shift to 0.5% because we know the depeg will recover so to avoid the loss for users.
    /// Next 30 bits => 142-171 => Hooks bits, calculate hook address by storing deployment nonce from factory.
    /// Next 28 bits => 172-199 => max center price. BigNumber (20 bits precision, 8 bits exponent)
    /// Next 28 bits => 200-227 => min center price. BigNumber (20 bits precision, 8 bits exponent)
    /// Next 10 bits => 228-237 => utilization limit of token0. Max value 1000 = 100%, if 100% then no need to check the utilization.
    /// Next 10 bits => 238-247 => utilization limit of token1. Max value 1000 = 100%, if 100% then no need to check the utilization.
    /// Next 1  bit  => 248     => is center price shift active
    /// Last 1  bit  => 255     => Pause swap & arbitrage (only perfect functions will be usable), if we need to pause entire DEX then that can be done through pausing DEX on Liquidity Layer
    uint internal dexVariables2;

    /// first 128 bits => 0-127 => total supply shares
    /// last 128 bits => 128-255 => max supply shares
    uint internal _totalSupplyShares;

    /// @dev user supply data: user -> data
    /// Aside from 1st bit, entire bits here are same as liquidity layer _userSupplyData. Hence exact same supply & borrow limit library can be used
    /// First  1 bit  =>       0 => is user allowed to supply? 0 = not allowed, 1 = allowed
    /// Next  64 bits =>   1- 64 => user supply amount/shares; BigMath: 56 | 8
    /// Next  64 bits =>  65-128 => previous user withdrawal limit; BigMath: 56 | 8
    /// Next  33 bits => 129-161 => last triggered process timestamp (enough until 16 March 2242 -> max value 8589934591)
    /// Next  14 bits => 162-175 => expand withdrawal limit percentage (in 1e2: 100% = 10_000; 1% = 100 -> max value 16_383).
    ///                             @dev shrinking is instant
    /// Next  24 bits => 176-199 => withdrawal limit expand duration in seconds.(Max value 16_777_215; ~4_660 hours, ~194 days)
    /// Next  18 bits => 200-217 => base withdrawal limit: below this, 100% withdrawals can be done (aka shares can be burned); BigMath: 10 | 8
    /// Next  38 bits => 218-255 => empty for future use
    mapping(address => uint) internal _userSupplyData;

    /// first 128 bits => 0-127 => total borrow shares
    /// last 128 bits => 128-255 => max borrow shares
    uint internal _totalBorrowShares;

    /// @dev user borrow data: user -> data
    /// Aside from 1st bit, entire bits here are same as liquidity layer _userBorrowData. Hence exact same supply & borrow limit library function can be used
    /// First  1 bit  =>       0 => is user allowed to borrow? 0 = not allowed, 1 = allowed
    /// Next  64 bits =>   1- 64 => user debt amount/shares; BigMath: 56 | 8
    /// Next  64 bits =>  65-128 => previous user debt ceiling; BigMath: 56 | 8
    /// Next  33 bits => 129-161 => last triggered process timestamp (enough until 16 March 2242 -> max value 8589934591)
    /// Next  14 bits => 162-175 => expand debt ceiling percentage (in 1e2: 100% = 10_000; 1% = 100 -> max value 16_383)
    ///                             @dev shrinking is instant
    /// Next  24 bits => 176-199 => debt ceiling expand duration in seconds (Max value 16_777_215; ~4_660 hours, ~194 days)
    /// Next  18 bits => 200-217 => base debt ceiling: below this, there's no debt ceiling limits; BigMath: 10 | 8
    /// Next  18 bits => 218-235 => max debt ceiling: absolute maximum debt ceiling can expand to; BigMath: 10 | 8
    /// Next  20 bits => 236-255 => empty for future use
    mapping(address => uint) internal _userBorrowData;

    /// Price difference between last swap of last block & last swap of new block
    /// If last swap happened at Block B - 4 and next swap happened after 4 blocks at Block B then it will store that difference
    /// considering time difference between these 4 blocks is 48 seconds, hence time will be stored as 48
    /// New oracle update:
    /// time to 9 bits and precision to 22 bits
    /// if time exceeds 9 bits which is 511 sec or ~8.5 min then we will use 2 oracle slot to store the data
    /// we will leave the both time slot as 0 and on first sign + precision slot we will store time and
    /// on second sign + precision slot we will store sign & precision
    /// First 9 bits =>   0-  8 => time, 511 seconds
    /// Next   1 bit  =>  9     => sign of percent in change, if 1 then 0 or positive, else negative
    /// Next  22 bits =>  10- 31 => 4194303, change in price, max change is capped to 5%, so 4194303 = 5%, 1 = 0.0000011920931797249746%
    /// Next  9 bits =>  32- 40 => time, 511 seconds
    /// Next   1 bit  =>  41     => sign of percent in change, if 1 then 0 or positive, else negative
    /// Next  22 bits =>  42- 63 => 4194303, change in price, max change is capped to 5%, so 4194303 = 5%, 1 = 0.0000011920931797249746%
    /// Next  9 bits =>  64- 72 => time, 511 seconds
    /// Next   1 bit  =>  73     => sign of percent in change, if 1 then 0 or positive, else negative
    /// Next  22 bits =>  74- 95 => 4194303, change in price, max change is capped to 5%, so 4194303 = 5%, 1 = 0.0000011920931797249746%
    /// Next  9 bits =>  96-104 => time, 511 seconds
    /// Next   1 bit  => 105     => sign of percent in change, if 1 then 0 or positive, else negative
    /// Next  22 bits => 106-127 => 4194303, change in price, max change is capped to 5%, so 4194303 = 5%, 1 = 0.0000011920931797249746%
    /// Next  9 bits => 128-136 => time, 511 seconds
    /// Next   1 bit  => 137     => sign of percent in change, if 1 then 0 or positive, else negative
    /// Next  22 bits => 138-159 => 4194303, change in price, max change is capped to 5%, so 4194303 = 5%, 1 = 0.0000011920931797249746%
    /// Next  9 bits => 160-168 => time, 511 seconds
    /// Next   1 bit  => 169     => sign of percent in change, if 1 then 0 or positive, else negative
    /// Next  22 bits => 170-191 => 4194303, change in price, max change is capped to 5%, so 4194303 = 5%, 1 = 0.0000011920931797249746%
    /// Next  9 bits => 192-200 => time, 511 seconds
    /// Next   1 bit  => 201     => sign of percent in change, if 1 then 0 or positive, else negative
    /// Next  22 bits => 202-223 => 4194303, change in price, max change is capped to 5%, so 4194303 = 5%, 1 = 0.0000011920931797249746%
    /// Next  9 bits => 224-232 => time, 511 seconds
    /// Next   1 bit  => 233     => sign of percent in change, if 1 then 0 or positive, else negative
    /// Next  22 bits => 234-255 => 4194303, change in price, max change is capped to 5%, so 4194303 = 5%, 1 = 0.0000011920931797249746%
    mapping(uint => uint) internal _oracle;

    /// First 20 bits =>  0-19 => old upper shift
    /// Next  20 bits => 20-39 => old lower shift
    /// Next  20 bits => 40-59 => in seconds, ~12 days max, shift can last for max ~12 days
    /// Next  33 bits => 60-92 => timestamp of when the shift has started.
    uint128 internal _rangeShift;

    /// First 10 bits =>  0- 9 => old upper shift
    /// Next  10 bits => 10-19 => empty so we can use same helper function
    /// Next  10 bits => 20-29 => old lower shift
    /// Next  10 bits => 30-39 => empty so we can use same helper function
    /// Next  20 bits => 40-59 => in seconds, ~12 days max, shift can last for max ~12 days
    /// Next  33 bits => 60-92 => timestamp of when the shift has started.
    /// Next  24 bits => 93-116 => old threshold time
    uint128 internal _thresholdShift;

    /// Shifting is fuzzy and with time it'll keep on getting closer and then eventually get over
    /// First 33 bits => 0 -32 => starting timestamp
    /// Next  20 bits => 33-52 => % shift
    /// Next  20 bits => 53-72 => time to shift that percent
    uint256 internal _centerPriceShift;
}

File 20 of 25 : events.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;

abstract contract Events {
    /// @notice Emitted on token swaps
    /// @param swap0to1 Indicates whether the swap is from token0 to token1 or vice-versa.
    /// @param amountIn The amount of tokens to be sent to the vault to swap.
    /// @param amountOut The amount of tokens user got from the swap.
    /// @param to Recepient of swapped tokens.
    event Swap(bool swap0to1, uint256 amountIn, uint256 amountOut, address to);

    /// @notice Emitted when liquidity is added with shares specified.
    /// @param shares Expected exact shares to be received.
    /// @param token0Amt Amount of token0 deposited.
    /// @param token0Amt Amount of token1 deposited.
    event LogDepositPerfectColLiquidity(uint shares, uint token0Amt, uint token1Amt);

    /// @notice Emitted when liquidity is withdrawn with shares specified.
    /// @param shares shares burned
    /// @param token0Amt Amount of token0 withdrawn.
    /// @param token1Amt Amount of token1 withdrawn.
    event LogWithdrawPerfectColLiquidity(uint shares, uint token0Amt, uint token1Amt);

    /// @notice Emitted when liquidity is borrowed with shares specified.
    /// @param shares shares minted
    /// @param token0Amt Amount of token0 borrowed.
    /// @param token1Amt Amount of token1 borrowed.
    event LogBorrowPerfectDebtLiquidity(uint shares, uint token0Amt, uint token1Amt);

    /// @notice Emitted when liquidity is paid back with shares specified.
    /// @param shares shares burned
    /// @param token0Amt Amount of token0 paid back.
    /// @param token1Amt Amount of token1 paid back.
    event LogPaybackPerfectDebtLiquidity(uint shares, uint token0Amt, uint token1Amt);

    /// @notice Emitted when liquidity is deposited with specified token0 & token1 amount
    /// @param amount0 Amount of token0 deposited.
    /// @param amount1 Amount of token1 deposited.
    /// @param shares Amount of shares minted.
    event LogDepositColLiquidity(uint amount0, uint amount1, uint shares);

    /// @notice Emitted when liquidity is withdrawn with specified token0 & token1 amount
    /// @param amount0 Amount of token0 withdrawn.
    /// @param amount1 Amount of token1 withdrawn.
    /// @param shares Amount of shares burned.
    event LogWithdrawColLiquidity(uint amount0, uint amount1, uint shares);

    /// @notice Emitted when liquidity is borrowed with specified token0 & token1 amount
    /// @param amount0 Amount of token0 borrowed.
    /// @param amount1 Amount of token1 borrowed.
    /// @param shares Amount of shares minted.
    event LogBorrowDebtLiquidity(uint amount0, uint amount1, uint shares);

    /// @notice Emitted when liquidity is paid back with specified token0 & token1 amount
    /// @param amount0 Amount of token0 paid back.
    /// @param amount1 Amount of token1 paid back.
    /// @param shares Amount of shares burned.
    event LogPaybackDebtLiquidity(uint amount0, uint amount1, uint shares);

    /// @notice Emitted when liquidity is withdrawn with shares specified into one token only.
    /// @param shares shares burned
    /// @param token0Amt Amount of token0 withdrawn.
    /// @param token1Amt Amount of token1 withdrawn.
    event LogWithdrawColInOneToken(uint shares, uint token0Amt, uint token1Amt);

    /// @notice Emitted when liquidity is paid back with shares specified from one token only.
    /// @param shares shares burned
    /// @param token0Amt Amount of token0 paid back.
    /// @param token1Amt Amount of token1 paid back.
    event LogPaybackDebtInOneToken(uint shares, uint token0Amt, uint token1Amt);

    /// @notice Emitted when internal arbitrage between 2 pools happen
    /// @param routing if positive then routing is amtIn of token0 in deposit & borrow else token0 withdraw & payback
    /// @param amtOut if routing is positive then token1 withdraw & payback amount else token1 deposit & borrow
    event LogArbitrage(int routing, uint amtOut);
}

File 21 of 25 : coreHelpers.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;

import { FixedPointMathLib } from "solmate/src/utils/FixedPointMathLib.sol";

import { Variables } from "../../common/variables.sol";
import { ImmutableVariables } from "../immutableVariables.sol";
import { Events } from "../events.sol";
import { ErrorTypes } from "../../../errorTypes.sol";
import { IHook, ICenterPrice } from "../interfaces.sol";
import { LiquiditySlotsLink } from "../../../../../libraries/liquiditySlotsLink.sol";
import { LiquidityCalcs } from "../../../../../libraries/liquidityCalcs.sol";
import { DexSlotsLink } from "../../../../../libraries/dexSlotsLink.sol";
import { DexCalcs } from "../../../../../libraries/dexCalcs.sol";
import { BigMathMinified } from "../../../../../libraries/bigMathMinified.sol";
import { AddressCalcs } from "../../../../../libraries/addressCalcs.sol";

interface IShifting {
    /// @dev Calculates the new upper and lower range values during an active range shift
    /// @param upperRange_ The target upper range value
    /// @param lowerRange_ The target lower range value
    /// @param dexVariables2_ needed in case shift is ended and we need to update dexVariables2
    /// @return The updated upper range, lower range, and dexVariables2
    function _calcRangeShifting(
        uint upperRange_,
        uint lowerRange_,
        uint dexVariables2_
    ) external payable returns (uint, uint, uint);

    /// @dev Calculates the new threshold values during an active threshold shift
    /// @param upperThreshold_ The target upper threshold value
    /// @param lowerThreshold_ The target lower threshold value
    /// @param dexVariables2_ needed in case shift is ended and we need to update dexVariables2
    /// @return The updated upper threshold, lower threshold, and dexVariables2
    function _calcThresholdShifting(
        uint upperThreshold_,
        uint lowerThreshold_,
        uint dexVariables2_
    ) external payable returns (uint, uint, uint);

    /// @dev Calculates the new center price during an active center price shift
    /// @param dexVariables_ The current state of dex variables
    /// @param dexVariables2_ Additional dex variables
    /// @return The updated center price
    function _calcCenterPrice(
        uint dexVariables_,
        uint dexVariables2_
    ) external payable returns (uint);
}

abstract contract CoreHelpers is Variables, ImmutableVariables, Events {
    using BigMathMinified for uint256;

    /// @dev            do any arbitrary call
    /// @param target_  Address to which the call needs to be delegated
    /// @param data_    Data to execute at the delegated address
    function _spell(address target_, bytes memory data_) internal returns (bytes memory response_) {
        assembly {
            let succeeded := delegatecall(gas(), target_, add(data_, 0x20), mload(data_), 0, 0)
            let size := returndatasize()

            response_ := mload(0x40)
            mstore(0x40, add(response_, and(add(add(size, 0x20), 0x1f), not(0x1f))))
            mstore(response_, size)
            returndatacopy(add(response_, 0x20), 0, size)

            if iszero(succeeded) {
                // throw if delegatecall failed
                returndatacopy(0x00, 0x00, size)
                revert(0x00, size)
            }
        }
    }

    /// @dev Given an input amount of asset and pair reserves, returns the maximum output amount of the other asset
    /// @param amountIn_ The amount of input asset.
    /// @param iReserveIn_ Imaginary token reserve with input amount.
    /// @param iReserveOut_ Imaginary token reserve of output amount.
    function _getAmountOut(
        uint256 amountIn_,
        uint iReserveIn_,
        uint iReserveOut_
    ) internal pure returns (uint256 amountOut_) {
        unchecked {
            // Both numerator and denominator are scaled to 1e6 to factor in fee scaling.
            uint256 numerator_ = amountIn_ * iReserveOut_;
            uint256 denominator_ = iReserveIn_ + amountIn_;

            // Using the swap formula: (AmountIn * iReserveY) / (iReserveX + AmountIn)
            amountOut_ = numerator_ / denominator_;
        }
    }

    /// @dev Given an output amount of asset and pair reserves, returns the input amount of the other asset
    /// @param amountOut_ Desired output amount of the asset.
    /// @param iReserveIn_ Imaginary token reserve of input amount.
    /// @param iReserveOut_ Imaginary token reserve of output amount.
    function _getAmountIn(
        uint256 amountOut_,
        uint iReserveIn_,
        uint iReserveOut_
    ) internal pure returns (uint256 amountIn_) {
        // Both numerator and denominator are scaled to 1e6 to factor in fee scaling.
        uint256 numerator_ = amountOut_ * iReserveIn_;
        uint256 denominator_ = iReserveOut_ - amountOut_;

        // Using the swap formula: (AmountOut * iReserveX) / (iReserveY - AmountOut)
        amountIn_ = numerator_ / denominator_;
    }

    /// @param t total amount in
    /// @param x imaginary reserves of token out of collateral
    /// @param y imaginary reserves of token in of collateral
    /// @param x2 imaginary reserves of token out of debt
    /// @param y2 imaginary reserves of token in of debt
    /// @return a_ how much swap should go through collateral pool. Remaining will go from debt
    /// note if a < 0 then entire trade route through debt pool and debt pool arbitrage with col pool
    /// note if a > t then entire trade route through col pool and col pool arbitrage with debt pool
    /// note if a > 0 & a < t then swap will route through both pools
    function _swapRoutingIn(uint t, uint x, uint y, uint x2, uint y2) internal pure returns (int a_) {
        // Main equations:
        // 1. out = x * a / (y + a)
        // 2. out2 = x2 * (t - a) / (y2 + (t - a))
        // final price should be same
        // 3. (y + a) / (x - out) = (y2 + (t - a)) / (x2 - out2)
        // derivation: https://chatgpt.com/share/dce6f381-ee5f-4d5f-b6ea-5996e84d5b57

        // adding 1e18 precision
        uint xyRoot_ = FixedPointMathLib.sqrt(x * y * 1e18);
        uint x2y2Root_ = FixedPointMathLib.sqrt(x2 * y2 * 1e18);

        a_ = (int(y2 * xyRoot_ + t * xyRoot_) - int(y * x2y2Root_)) / int(xyRoot_ + x2y2Root_);
    }

    /// @param t total amount out
    /// @param x imaginary reserves of token in of collateral
    /// @param y imaginary reserves of token out of collateral
    /// @param x2 imaginary reserves of token in of debt
    /// @param y2 imaginary reserves of token out of debt
    /// @return a_ how much swap should go through collateral pool. Remaining will go from debt
    /// note if a < 0 then entire trade route through debt pool and debt pool arbitrage with col pool
    /// note if a > t then entire trade route through col pool and col pool arbitrage with debt pool
    /// note if a > 0 & a < t then swap will route through both pools
    function _swapRoutingOut(uint t, uint x, uint y, uint x2, uint y2) internal pure returns (int a_) {
        // Main equations:
        // 1. in = (x * a) / (y - a)
        // 2. in2 = (x2 * (t - a)) / (y2 - (t - a))
        // final price should be same
        // 3. (y - a) / (x + in) = (y2 - (t - a)) / (x2 + in2)
        // derivation: https://chatgpt.com/share/6585bc28-841f-49ec-aea2-1e5c5b7f4fa9

        // adding 1e18 precision
        uint xyRoot_ = FixedPointMathLib.sqrt(x * y * 1e18);
        uint x2y2Root_ = FixedPointMathLib.sqrt(x2 * y2 * 1e18);

        // 1e18 precision gets cancelled out in division
        a_ = (int(t * xyRoot_ + y * x2y2Root_) - int(y2 * xyRoot_)) / int(xyRoot_ + x2y2Root_);
    }

    function _utilizationVerify(uint utilizationLimit_, bytes32 exchangePriceSlot_) internal view {
        if (utilizationLimit_ < THREE_DECIMALS) {
            utilizationLimit_ = utilizationLimit_ * 10;
            // extracting utilization of token from liquidity layer
            uint liquidityLayerUtilization_ = LIQUIDITY.readFromStorage(exchangePriceSlot_);
            liquidityLayerUtilization_ =
                (liquidityLayerUtilization_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_UTILIZATION) &
                X14;
            // Note: this can go slightly above the utilization limit if no update is written to storage at liquidity layer
            // if swap was not big enough to go far enough above or any other storage update threshold write cause there
            // so just to keep in mind when configuring the actual limit reachable can be utilizationLimit_ + storageUpdateThreshold at Liquidity
            if (liquidityLayerUtilization_ > utilizationLimit_)
                revert FluidDexError(ErrorTypes.DexT1__LiquidityLayerTokenUtilizationCapReached);
        }
    }

    function _check(uint dexVariables_, uint dexVariables2_) internal {
        if (dexVariables_ & 1 == 1) revert FluidDexError(ErrorTypes.DexT1__AlreadyEntered);
        if (dexVariables2_ & 3 == 0) revert FluidDexError(ErrorTypes.DexT1__PoolNotInitialized);
        // enabling re-entrancy
        dexVariables = dexVariables_ | 1;
    }

    /// @dev if token0 reserves are too low w.r.t token1 then revert, this is to avoid edge case scenario and making sure that precision on calculations should be high enough
    function _verifyToken0Reserves(
        uint token0Reserves_,
        uint token1Reserves_,
        uint centerPrice_,
        uint minLiquidity_
    ) internal pure {
        if (((token0Reserves_) < ((token1Reserves_ * 1e27) / (centerPrice_ * minLiquidity_)))) {
            revert FluidDexError(ErrorTypes.DexT1__TokenReservesTooLow);
        }
    }

    /// @dev if token1 reserves are too low w.r.t token0 then revert, this is to avoid edge case scenario and making sure that precision on calculations should be high enough
    function _verifyToken1Reserves(
        uint token0Reserves_,
        uint token1Reserves_,
        uint centerPrice_,
        uint minLiquidity_
    ) internal pure {
        if (((token1Reserves_) < ((token0Reserves_ * centerPrice_) / (1e27 * minLiquidity_)))) {
            revert FluidDexError(ErrorTypes.DexT1__TokenReservesTooLow);
        }
    }

    function _verifySwapAndNonPerfectActions(uint amountAdjusted_, uint amount_) internal pure {
        // after shifting amount should not become 0
        // limiting to six decimals which means in case of USDC, USDT it's 1 wei, for WBTC 100 wei, for ETH 1000 gwei
        if (amountAdjusted_ < SIX_DECIMALS || amountAdjusted_ > X96 || amount_ < TWO_DECIMALS || amount_ > X128)
            revert FluidDexError(ErrorTypes.DexT1__LimitingAmountsSwapAndNonPerfectActions);
    }

    /// @dev Calculates the new upper and lower range values during an active range shift
    /// @param upperRange_ The target upper range value
    /// @param lowerRange_ The target lower range value
    /// @param dexVariables2_ needed in case shift is ended and we need to update dexVariables2
    /// @return The updated upper range, lower range, and dexVariables2
    /// @notice This function handles the gradual shifting of range values over time
    /// @notice If the shift is complete, it updates the state and clears the shift data
    function _calcRangeShifting(
        uint upperRange_,
        uint lowerRange_,
        uint dexVariables2_
    ) internal returns (uint, uint, uint) {
        return
            abi.decode(
                _spell(
                    SHIFT_IMPLEMENTATION,
                    abi.encodeWithSelector(
                        IShifting._calcRangeShifting.selector,
                        upperRange_,
                        lowerRange_,
                        dexVariables2_
                    )
                ),
                (uint, uint, uint)
            );
    }

    /// @dev Calculates the new upper and lower threshold values during an active threshold shift
    /// @param upperThreshold_ The target upper threshold value
    /// @param lowerThreshold_ The target lower threshold value
    /// @param thresholdTime_ The time passed since shifting started
    /// @return The updated upper threshold, lower threshold, and threshold time
    /// @notice This function handles the gradual shifting of threshold values over time
    /// @notice If the shift is complete, it updates the state and clears the shift data
    function _calcThresholdShifting(
        uint upperThreshold_,
        uint lowerThreshold_,
        uint thresholdTime_
    ) internal returns (uint, uint, uint) {
        return
            abi.decode(
                _spell(
                    SHIFT_IMPLEMENTATION,
                    abi.encodeWithSelector(
                        IShifting._calcThresholdShifting.selector,
                        upperThreshold_,
                        lowerThreshold_,
                        thresholdTime_
                    )
                ),
                (uint, uint, uint)
            );
    }

    /// @dev Calculates the new center price during an active price shift
    /// @param dexVariables_ The current state of dex variables
    /// @param dexVariables2_ Additional dex variables
    /// @return newCenterPrice_ The updated center price
    /// @notice This function gradually shifts the center price towards a new target price over time
    /// @notice It uses an external price source (via ICenterPrice) to determine the target price
    /// @notice The shift continues until the current price reaches the target, or the shift duration ends
    /// @notice Once the shift is complete, it updates the state and clears the shift data
    /// @notice The shift rate is dynamic and depends on:
    /// @notice - Time remaining in the shift duration
    /// @notice - The new center price (fetched externally, which may change)
    /// @notice - The current (old) center price
    /// @notice This results in a fuzzy shifting mechanism where the rate can change as these parameters evolve
    /// @notice The externally fetched new center price is expected to not differ significantly from the last externally fetched center price
    function _calcCenterPrice(uint dexVariables_, uint dexVariables2_) internal returns (uint newCenterPrice_) {
        return
            abi.decode(
                _spell(
                    SHIFT_IMPLEMENTATION,
                    abi.encodeWithSelector(IShifting._calcCenterPrice.selector, dexVariables_, dexVariables2_)
                ),
                (uint)
            );
    }

    /// @notice Calculates and returns the current prices and exchange prices for the pool
    /// @param dexVariables_ The first set of DEX variables containing various pool parameters
    /// @param dexVariables2_ The second set of DEX variables containing additional pool parameters
    /// @return pex_ A struct containing the calculated prices and exchange prices:
    ///         - pex_.lastStoredPrice: The last stored price in 1e27 decimals
    ///         - pex_.centerPrice: The calculated or fetched center price in 1e27 decimals
    ///         - pex_.upperRange: The upper range price limit in 1e27 decimals
    ///         - pex_.lowerRange: The lower range price limit in 1e27 decimals
    ///         - pex_.geometricMean: The geometric mean of upper range & lower range in 1e27 decimals
    ///         - pex_.supplyToken0ExchangePrice: The current exchange price for supplying token0
    ///         - pex_.borrowToken0ExchangePrice: The current exchange price for borrowing token0
    ///         - pex_.supplyToken1ExchangePrice: The current exchange price for supplying token1
    ///         - pex_.borrowToken1ExchangePrice: The current exchange price for borrowing token1
    /// @dev This function performs the following operations:
    ///      1. Determines the center price (either from storage, external source, or calculated)
    ///      2. Retrieves the last stored price from dexVariables_
    ///      3. Calculates the upper and lower range prices based on the center price and range percentages
    ///      4. Checks if rebalancing is needed based on threshold settings
    ///      5. Adjusts prices if necessary based on the time elapsed and threshold conditions
    ///      6. Update the dexVariables2_ if changes were made
    ///      7. Returns the calculated prices and exchange prices in the PricesAndExchangePrice struct
    function _getPricesAndExchangePrices(
        uint dexVariables_,
        uint dexVariables2_
    ) internal returns (PricesAndExchangePrice memory pex_) {
        uint centerPrice_;

        if (((dexVariables2_ >> 248) & 1) == 0) {
            // centerPrice_ => center price hook
            centerPrice_ = (dexVariables2_ >> 112) & X30;
            if (centerPrice_ == 0) {
                centerPrice_ = (dexVariables_ >> 81) & X40;
                centerPrice_ = (centerPrice_ >> DEFAULT_EXPONENT_SIZE) << (centerPrice_ & DEFAULT_EXPONENT_MASK);
            } else {
                // center price should be fetched from external source. For exmaple, in case of wstETH <> ETH pool,
                // we would want the center price to be pegged to wstETH exchange rate into ETH
                centerPrice_ = ICenterPrice(AddressCalcs.addressCalc(DEPLOYER_CONTRACT, centerPrice_)).centerPrice();
            }
        } else {
            // an active centerPrice_ shift is going on
            centerPrice_ = _calcCenterPrice(dexVariables_, dexVariables2_);
        }

        uint lastStoredPrice_ = (dexVariables_ >> 41) & X40;
        lastStoredPrice_ = (lastStoredPrice_ >> DEFAULT_EXPONENT_SIZE) << (lastStoredPrice_ & DEFAULT_EXPONENT_MASK);

        uint upperRange_ = ((dexVariables2_ >> 27) & X20);
        uint lowerRange_ = ((dexVariables2_ >> 47) & X20);
        if (((dexVariables2_ >> 26) & 1) == 1) {
            // an active range shift is going on
            (upperRange_, lowerRange_, dexVariables2_) = _calcRangeShifting(upperRange_, lowerRange_, dexVariables2_);
        }

        unchecked {
            // adding into unchecked because upperRange_ & lowerRange_ can only be > 0 & < SIX_DECIMALS
            // 1% = 1e4, 100% = 1e6
            upperRange_ = (centerPrice_ * SIX_DECIMALS) / (SIX_DECIMALS - upperRange_);
            // 1% = 1e4, 100% = 1e6
            lowerRange_ = (centerPrice_ * (SIX_DECIMALS - lowerRange_)) / SIX_DECIMALS;
        }

        bool changed_;
        {
            // goal will be to keep threshold percents 0 if center price is fetched from external source
            // checking if threshold are set non 0 then only rebalancing is on
            if (((dexVariables2_ >> 68) & X20) > 0) {
                uint upperThreshold_ = (dexVariables2_ >> 68) & X10;
                uint lowerThreshold_ = (dexVariables2_ >> 78) & X10;
                uint shiftingTime_ = (dexVariables2_ >> 88) & X24;
                if (((dexVariables2_ >> 67) & 1) == 1) {
                    // if active shift is going on for threshold then calculate threshold real time
                    (upperThreshold_, lowerThreshold_, shiftingTime_) = _calcThresholdShifting(
                        upperThreshold_,
                        lowerThreshold_,
                        shiftingTime_
                    );
                }

                unchecked {
                    if (
                        lastStoredPrice_ >
                        (centerPrice_ +
                            ((upperRange_ - centerPrice_) * (THREE_DECIMALS - upperThreshold_)) /
                            THREE_DECIMALS)
                    ) {
                        uint timeElapsed_ = block.timestamp - ((dexVariables_ >> 121) & X33);
                        // price shifting towards upper range
                        if (timeElapsed_ < shiftingTime_) {
                            centerPrice_ = centerPrice_ + ((upperRange_ - centerPrice_) * timeElapsed_) / shiftingTime_;
                        } else {
                            // 100% price shifted
                            centerPrice_ = upperRange_;
                        }
                        changed_ = true;
                    } else if (
                        lastStoredPrice_ <
                        (centerPrice_ -
                            ((centerPrice_ - lowerRange_) * (THREE_DECIMALS - lowerThreshold_)) /
                            THREE_DECIMALS)
                    ) {
                        uint timeElapsed_ = block.timestamp - ((dexVariables_ >> 121) & X33);
                        // price shifting towards lower range
                        if (timeElapsed_ < shiftingTime_) {
                            centerPrice_ = centerPrice_ - ((centerPrice_ - lowerRange_) * timeElapsed_) / shiftingTime_;
                        } else {
                            // 100% price shifted
                            centerPrice_ = lowerRange_;
                        }
                        changed_ = true;
                    }
                }
            }
        }

        // temp_ => max center price
        uint temp_ = (dexVariables2_ >> 172) & X28;
        temp_ = (temp_ >> DEFAULT_EXPONENT_SIZE) << (temp_ & DEFAULT_EXPONENT_MASK);
        if (centerPrice_ > temp_) {
            // if center price is greater than max center price
            centerPrice_ = temp_;
            changed_ = true;
        } else {
            // check if center price is less than min center price
            // temp_ => min center price
            temp_ = (dexVariables2_ >> 200) & X28;
            temp_ = (temp_ >> DEFAULT_EXPONENT_SIZE) << (temp_ & DEFAULT_EXPONENT_MASK);
            if (centerPrice_ < temp_) {
                centerPrice_ = temp_;
                changed_ = true;
            }
        }

        // if centerPrice_ is changed then calculating upper and lower range again
        if (changed_) {
            upperRange_ = ((dexVariables2_ >> 27) & X20);
            lowerRange_ = ((dexVariables2_ >> 47) & X20);
            if (((dexVariables2_ >> 26) & 1) == 1) {
                (upperRange_, lowerRange_, dexVariables2_) = _calcRangeShifting(
                    upperRange_,
                    lowerRange_,
                    dexVariables2_
                );
            }

            unchecked {
                // adding into unchecked because upperRange_ & lowerRange_ can only be > 0 & < SIX_DECIMALS
                // 1% = 1e4, 100% = 1e6
                upperRange_ = (centerPrice_ * SIX_DECIMALS) / (SIX_DECIMALS - upperRange_);
                // 1% = 1e4, 100% = 1e6
                lowerRange_ = (centerPrice_ * (SIX_DECIMALS - lowerRange_)) / SIX_DECIMALS;
            }
        }

        pex_.lastStoredPrice = lastStoredPrice_;
        pex_.centerPrice = centerPrice_;
        pex_.upperRange = upperRange_;
        pex_.lowerRange = lowerRange_;

        unchecked {
            if (upperRange_ < 1e38) {
                // 1e38 * 1e38 = 1e76 which is less than max uint limit
                pex_.geometricMean = FixedPointMathLib.sqrt(upperRange_ * lowerRange_);
            } else {
                // upperRange_ price is pretty large hence lowerRange_ will also be pretty large
                pex_.geometricMean = FixedPointMathLib.sqrt((upperRange_ / 1e18) * (lowerRange_ / 1e18)) * 1e18;
            }
        }

        // Exchange price will remain same as Liquidity Layer
        (pex_.supplyToken0ExchangePrice, pex_.borrowToken0ExchangePrice) = LiquidityCalcs.calcExchangePrices(
            LIQUIDITY.readFromStorage(EXCHANGE_PRICE_TOKEN_0_SLOT)
        );

        (pex_.supplyToken1ExchangePrice, pex_.borrowToken1ExchangePrice) = LiquidityCalcs.calcExchangePrices(
            LIQUIDITY.readFromStorage(EXCHANGE_PRICE_TOKEN_1_SLOT)
        );
    }

    /// @dev getting reserves outside range.
    /// @param gp_ is geometric mean pricing of upper percent & lower percent
    /// @param pa_ price of upper range or lower range
    /// @param rx_ real reserves of token0 or token1
    /// @param ry_ whatever is rx_ the other will be ry_
    function _calculateReservesOutsideRange(
        uint gp_,
        uint pa_,
        uint rx_,
        uint ry_
    ) internal pure returns (uint xa_, uint yb_) {
        // equations we have:
        // 1. x*y = k
        // 2. xa*ya = k
        // 3. xb*yb = k
        // 4. Pa = ya / xa = upperRange_ (known)
        // 5. Pb = yb / xb = lowerRange_ (known)
        // 6. x - xa = rx = real reserve of x (known)
        // 7. y - yb = ry = real reserve of y (known)
        // With solving we get:
        // ((Pa*Pb)^(1/2) - Pa)*xa^2 + (rx * (Pa*Pb)^(1/2) + ry)*xa + rx*ry = 0
        // yb = yb = xa * (Pa * Pb)^(1/2)

        // xa = (GP⋅rx + ry + (-rx⋅ry⋅4⋅(GP - Pa) + (GP⋅rx + ry)^2)^0.5) / (2Pa - 2GP)
        // multiply entire equation by 1e27 to remove the price decimals precision of 1e27
        // xa = (GP⋅rx + ry⋅1e27 + (rx⋅ry⋅4⋅(Pa - GP)⋅1e27 + (GP⋅rx + ry⋅1e27)^2)^0.5) / 2*(Pa - GP)
        // dividing the equation with 2*(Pa - GP). Pa is always > GP so answer will be positive.
        // xa = (((GP⋅rx + ry⋅1e27) / 2*(Pa - GP)) + (((rx⋅ry⋅4⋅(Pa - GP)⋅1e27) / 4*(Pa - GP)^2) + ((GP⋅rx + ry⋅1e27) / 2*(Pa - GP))^2)^0.5)
        // xa = (((GP⋅rx + ry⋅1e27) / 2*(Pa - GP)) + (((rx⋅ry⋅1e27) / (Pa - GP)) + ((GP⋅rx + ry⋅1e27) / 2*(Pa - GP))^2)^0.5)

        // dividing in 3 parts for simplification:
        // part1 = (Pa - GP)
        // part2 = (GP⋅rx + ry⋅1e27) / (2*part1)
        // part3 = rx⋅ry
        // note: part1 will almost always be < 1e28 but in case it goes above 1e27 then it's extremely unlikely it'll go above > 1e29
        uint p1_ = pa_ - gp_;
        uint p2_ = ((gp_ * rx_) + (ry_ * 1e27)) / (2 * p1_);
        uint p3_ = rx_ * ry_;
        // to avoid overflowing
        p3_ = (p3_ < 1e50) ? ((p3_ * 1e27) / p1_) : (p3_ / p1_) * 1e27;

        // xa = part2 + (part3 + (part2 * part2))^(1/2)
        // yb = xa_ * gp_
        xa_ = p2_ + FixedPointMathLib.sqrt((p3_ + (p2_ * p2_)));
        yb_ = (xa_ * gp_) / 1e27;
    }

    /// @dev Retrieves collateral amount from liquidity layer for a given token
    /// @param supplyTokenSlot_ The storage slot for the supply token data
    /// @param tokenExchangePrice_ The exchange price of the token
    /// @param isToken0_ Boolean indicating if the token is token0 (true) or token1 (false)
    /// @return tokenSupply_ The calculated liquidity collateral amount
    function _getLiquidityCollateral(
        bytes32 supplyTokenSlot_,
        uint tokenExchangePrice_,
        bool isToken0_
    ) internal view returns (uint tokenSupply_) {
        uint tokenSupplyData_ = LIQUIDITY.readFromStorage(supplyTokenSlot_);
        tokenSupply_ = (tokenSupplyData_ >> LiquiditySlotsLink.BITS_USER_SUPPLY_AMOUNT) & X64;
        tokenSupply_ = (tokenSupply_ >> DEFAULT_EXPONENT_SIZE) << (tokenSupply_ & DEFAULT_EXPONENT_MASK);

        if (tokenSupplyData_ & 1 == 1) {
            // supply with interest is on
            unchecked {
                tokenSupply_ = (tokenSupply_ * tokenExchangePrice_) / LiquidityCalcs.EXCHANGE_PRICES_PRECISION;
            }
        }

        unchecked {
            tokenSupply_ = isToken0_
                ? ((tokenSupply_ * TOKEN_0_NUMERATOR_PRECISION) / TOKEN_0_DENOMINATOR_PRECISION)
                : ((tokenSupply_ * TOKEN_1_NUMERATOR_PRECISION) / TOKEN_1_DENOMINATOR_PRECISION);
        }
    }

    /// @notice Calculates the real and imaginary reserves for collateral tokens
    /// @dev This function retrieves the supply of both tokens from the liquidity layer,
    ///      adjusts them based on exchange prices, and calculates imaginary reserves
    ///      based on the geometric mean and price range
    /// @param geometricMean_ The geometric mean of the token prices
    /// @param upperRange_ The upper price range
    /// @param lowerRange_ The lower price range
    /// @param token0SupplyExchangePrice_ The exchange price for token0 from liquidity layer
    /// @param token1SupplyExchangePrice_ The exchange price for token1 from liquidity layer
    /// @return c_ A struct containing the calculated real and imaginary reserves for both tokens:
    ///         - token0RealReserves: The real reserves of token0
    ///         - token1RealReserves: The real reserves of token1
    ///         - token0ImaginaryReserves: The imaginary reserves of token0
    ///         - token1ImaginaryReserves: The imaginary reserves of token1
    function _getCollateralReserves(
        uint geometricMean_,
        uint upperRange_,
        uint lowerRange_,
        uint token0SupplyExchangePrice_,
        uint token1SupplyExchangePrice_
    ) internal view returns (CollateralReserves memory c_) {
        uint token0Supply_ = _getLiquidityCollateral(SUPPLY_TOKEN_0_SLOT, token0SupplyExchangePrice_, true);
        uint token1Supply_ = _getLiquidityCollateral(SUPPLY_TOKEN_1_SLOT, token1SupplyExchangePrice_, false);

        if (geometricMean_ < 1e27) {
            (c_.token0ImaginaryReserves, c_.token1ImaginaryReserves) = _calculateReservesOutsideRange(
                geometricMean_,
                upperRange_,
                token0Supply_,
                token1Supply_
            );
        } else {
            // inversing, something like `xy = k` so for calculation we are making everything related to x into y & y into x
            // 1 / geometricMean for new geometricMean
            // 1 / lowerRange will become upper range
            // 1 / upperRange will become lower range
            (c_.token1ImaginaryReserves, c_.token0ImaginaryReserves) = _calculateReservesOutsideRange(
                (1e54 / geometricMean_),
                (1e54 / lowerRange_),
                token1Supply_,
                token0Supply_
            );
        }

        c_.token0RealReserves = token0Supply_;
        c_.token1RealReserves = token1Supply_;
        unchecked {
            c_.token0ImaginaryReserves += token0Supply_;
            c_.token1ImaginaryReserves += token1Supply_;
        }
    }

    /// @notice Calculates the real and imaginary debt reserves for both tokens
    /// @dev This function uses a quadratic equation to determine the debt reserves
    ///      based on the geometric mean price and the current debt amounts
    /// @param gp_ The geometric mean price of upper range & lower range
    /// @param pb_ The price of lower range
    /// @param dx_ The debt amount of one token
    /// @param dy_ The debt amount of the other token
    /// @return rx_ The real debt reserve of the first token
    /// @return ry_ The real debt reserve of the second token
    /// @return irx_ The imaginary debt reserve of the first token
    /// @return iry_ The imaginary debt reserve of the second token
    function _calculateDebtReserves(
        uint gp_,
        uint pb_,
        uint dx_,
        uint dy_
    ) internal pure returns (uint rx_, uint ry_, uint irx_, uint iry_) {
        // Assigning letter to knowns:
        // c = debtA
        // d = debtB
        // e = upperPrice
        // f = lowerPrice
        // g = upperPrice^1/2
        // h = lowerPrice^1/2

        // c = 1
        // d = 2000
        // e = 2222.222222
        // f = 1800
        // g = 2222.222222^1/2
        // h = 1800^1/2

        // Assigning letter to unknowns:
        // w = realDebtReserveA
        // x = realDebtReserveB
        // y = imaginaryDebtReserveA
        // z = imaginaryDebtReserveB
        // k = k

        // below quadratic will give answer of realDebtReserveB
        // A, B, C of quadratic equation:
        // A = h
        // B = dh - cfg
        // C = -cfdh

        // A = lowerPrice^1/2
        // B = debtB⋅lowerPrice^1/2 - debtA⋅lowerPrice⋅upperPrice^1/2
        // C = -(debtA⋅lowerPrice⋅debtB⋅lowerPrice^1/2)

        // x = (cfg − dh + (4cdf(h^2)+(cfg−dh)^2))^(1/2)) / 2h
        // simplifying dividing by h, note h = f^1/2
        // x = ((c⋅g⋅(f^1/2) − d) / 2 + ((4⋅c⋅d⋅f⋅f) / (4h^2) + ((c⋅f⋅g) / 2h − (d⋅h) / 2h)^2))^(1/2))
        // x = ((c⋅g⋅(f^1/2) − d) / 2 + ((c⋅d⋅f) + ((c⋅g⋅(f^1/2) − d) / 2)^2))^(1/2))

        // dividing in 3 parts for simplification:
        // part1 = (c⋅g⋅(f^1/2) − d) / 2
        // part2 = (c⋅d⋅f)
        // x = (part1 + (part2 + part1^2)^(1/2))
        // note: part1 will almost always be < 1e27 but in case it goes above 1e27 then it's extremely unlikely it'll go above > 1e28

        // part1 = ((debtA * upperPrice^1/2 * lowerPrice^1/2) - debtB) / 2
        // note: upperPrice^1/2 * lowerPrice^1/2 = geometric mean
        // part1 = ((debtA * geometricMean) - debtB) / 2
        // part2 = debtA * debtB * lowerPrice

        // converting decimals properly as price is in 1e27 decimals
        // part1 = ((debtA * geometricMean) - (debtB * 1e27)) / (2 * 1e27)
        // part2 = (debtA * debtB * lowerPrice) / 1e27
        // final x equals:
        // x = (part1 + (part2 + part1^2)^(1/2))
        int p1_ = (int(dx_ * gp_) - int(dy_ * 1e27)) / (2 * 1e27);
        uint p2_ = (dx_ * dy_);
        p2_ = p2_ < 1e50 ? (p2_ * pb_) / 1e27 : (p2_ / 1e27) * pb_;
        ry_ = uint(p1_ + int(FixedPointMathLib.sqrt((p2_ + uint(p1_ * p1_)))));

        // finding z:
        // x^2 - zx + cfz = 0
        // z*(x - cf) = x^2
        // z = x^2 / (x - cf)
        // z = x^2 / (x - debtA * lowerPrice)
        // converting decimals properly as price is in 1e27 decimals
        // z = (x^2 * 1e27) / ((x * 1e27) - (debtA * lowerPrice))

        iry_ = ((ry_ * 1e27) - (dx_ * pb_));
        if (iry_ < SIX_DECIMALS) {
            // almost impossible situation to ever get here
            revert FluidDexError(ErrorTypes.DexT1__DebtReservesTooLow);
        }
        if (ry_ < 1e25) {
            iry_ = (ry_ * ry_ * 1e27) / iry_;
        } else {
            // note: it can never result in negative as final result will always be in positive
            iry_ = (ry_ * ry_) / (iry_ / 1e27);
        }

        // finding y
        // x = z * c / (y + c)
        // y + c = z * c / x
        // y = (z * c / x) - c
        // y = (z * debtA / x) - debtA
        irx_ = ((iry_ * dx_) / ry_) - dx_;

        // finding w
        // w = y * d / (z + d)
        // w = (y * debtB) / (z + debtB)
        rx_ = (irx_ * dy_) / (iry_ + dy_);
    }

    /// @notice Calculates the debt amount for a given token from liquidity layer
    /// @param borrowTokenSlot_ The storage slot for the token's borrow data
    /// @param tokenExchangePrice_ The current exchange price of the token
    /// @param isToken0_ Boolean indicating if this is for token0 (true) or token1 (false)
    /// @return tokenDebt_ The calculated debt amount for the token
    function _getLiquidityDebt(
        bytes32 borrowTokenSlot_,
        uint tokenExchangePrice_,
        bool isToken0_
    ) internal view returns (uint tokenDebt_) {
        uint tokenBorrowData_ = LIQUIDITY.readFromStorage(borrowTokenSlot_);

        tokenDebt_ = (tokenBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_AMOUNT) & X64;
        tokenDebt_ = (tokenDebt_ >> 8) << (tokenDebt_ & X8);

        if (tokenBorrowData_ & 1 == 1) {
            // borrow with interest is on
            unchecked {
                tokenDebt_ = (tokenDebt_ * tokenExchangePrice_) / LiquidityCalcs.EXCHANGE_PRICES_PRECISION;
            }
        }

        unchecked {
            tokenDebt_ = isToken0_
                ? ((tokenDebt_ * TOKEN_0_NUMERATOR_PRECISION) / TOKEN_0_DENOMINATOR_PRECISION)
                : ((tokenDebt_ * TOKEN_1_NUMERATOR_PRECISION) / TOKEN_1_DENOMINATOR_PRECISION);
        }
    }

    /// @notice Calculates the debt reserves for both tokens
    /// @param geometricMean_ The geometric mean of the upper and lower price ranges
    /// @param upperRange_ The upper price range
    /// @param lowerRange_ The lower price range
    /// @param token0BorrowExchangePrice_ The exchange price of token0 from liquidity layer
    /// @param token1BorrowExchangePrice_ The exchange price of token1 from liquidity layer
    /// @return d_ The calculated debt reserves for both tokens, containing:
    ///         - token0Debt: The debt amount of token0
    ///         - token1Debt: The debt amount of token1
    ///         - token0RealReserves: The real reserves of token0 derived from token1 debt
    ///         - token1RealReserves: The real reserves of token1 derived from token0 debt
    ///         - token0ImaginaryReserves: The imaginary debt reserves of token0
    ///         - token1ImaginaryReserves: The imaginary debt reserves of token1
    function _getDebtReserves(
        uint geometricMean_,
        uint upperRange_,
        uint lowerRange_,
        uint token0BorrowExchangePrice_,
        uint token1BorrowExchangePrice_
    ) internal view returns (DebtReserves memory d_) {
        uint token0Debt_ = _getLiquidityDebt(BORROW_TOKEN_0_SLOT, token0BorrowExchangePrice_, true);
        uint token1Debt_ = _getLiquidityDebt(BORROW_TOKEN_1_SLOT, token1BorrowExchangePrice_, false);

        d_.token0Debt = token0Debt_;
        d_.token1Debt = token1Debt_;

        if (geometricMean_ < 1e27) {
            (
                d_.token0RealReserves,
                d_.token1RealReserves,
                d_.token0ImaginaryReserves,
                d_.token1ImaginaryReserves
            ) = _calculateDebtReserves(geometricMean_, lowerRange_, token0Debt_, token1Debt_);
        } else {
            // inversing, something like `xy = k` so for calculation we are making everything related to x into y & y into x
            // 1 / geometricMean for new geometricMean
            // 1 / lowerRange will become upper range
            // 1 / upperRange will become lower range
            (
                d_.token1RealReserves,
                d_.token0RealReserves,
                d_.token1ImaginaryReserves,
                d_.token0ImaginaryReserves
            ) = _calculateDebtReserves((1e54 / geometricMean_), (1e54 / upperRange_), token1Debt_, token0Debt_);
        }
    }

    function _priceDiffCheck(uint oldPrice_, uint newPrice_) internal pure returns (int priceDiff_) {
        // check newPrice_ & oldPrice_ difference should not be more than 5%
        // old price w.r.t new price
        priceDiff_ = int(ORACLE_PRECISION) - int((oldPrice_ * ORACLE_PRECISION) / newPrice_);

        unchecked {
            if ((priceDiff_ > int(ORACLE_LIMIT)) || (priceDiff_ < -int(ORACLE_LIMIT))) {
                // if oracle price difference is more than 5% then revert
                // in 1 swap price should only change by <= 5%
                // if a total fall by let's say 8% then in current block price can only fall by 5% and
                // in next block it'll fall the remaining 3%
                revert FluidDexError(ErrorTypes.DexT1__OracleUpdateHugeSwapDiff);
            }
        }
    }

    function _updateOracle(uint newPrice_, uint centerPrice_, uint dexVariables_) internal returns (uint) {
        // time difference between last & current swap
        uint timeDiff_ = block.timestamp - ((dexVariables_ >> 121) & X33);
        uint temp_;
        uint temp2_;
        uint temp3_;

        if (timeDiff_ == 0) {
            // doesn't matter if oracle is on or off when timediff = 0 code for both is same

            // temp_ => oldCenterPrice
            temp_ = (dexVariables_ >> 81) & X40;
            temp_ = (temp_ >> DEFAULT_EXPONENT_SIZE) << (temp_ & DEFAULT_EXPONENT_MASK);

            // Ensure that the center price is within the acceptable range of the old center price if it's not the first swap in the same block
            unchecked {
                if (
                    (centerPrice_ < (((EIGHT_DECIMALS - 1) * temp_) / EIGHT_DECIMALS)) ||
                    (centerPrice_ > (((EIGHT_DECIMALS + 1) * temp_) / EIGHT_DECIMALS))
                ) {
                    revert FluidDexError(ErrorTypes.DexT1__CenterPriceOutOfRange);
                }
            }

            // olderPrice_ => temp_
            temp_ = (dexVariables_ >> 1) & X40;
            temp_ = (temp_ >> DEFAULT_EXPONENT_SIZE) << (temp_ & DEFAULT_EXPONENT_MASK);

            _priceDiffCheck(temp_, newPrice_);

            // 2nd swap in same block no need to update anything around oracle, only need to update last swap price in dexVariables
            return ((dexVariables_ & 0xfffffffffffffffffffffffffffffffffffffffffffe0000000001ffffffffff) |
                (newPrice_.toBigNumber(32, 8, BigMathMinified.ROUND_DOWN) << 41));
        }

        if (((dexVariables_ >> 195) & 1) == 0) {
            // if oracle is not active then just returning updated DEX variable
            temp_ = ((dexVariables_ >> 41) & X40);
            temp_ = (temp_ >> DEFAULT_EXPONENT_SIZE) << (temp_ & DEFAULT_EXPONENT_MASK);

            _priceDiffCheck(temp_, newPrice_);
            
            return ((dexVariables_ & 0xfffffffffffffffffffffffffc00000000000000000000000000000000000001) |
                (((dexVariables_ >> 41) & X40) << 1) |
                (newPrice_.toBigNumber(32, 8, BigMathMinified.ROUND_DOWN) << 41) |
                (centerPrice_.toBigNumber(32, 8, BigMathMinified.ROUND_DOWN) << 81) |
                (block.timestamp << 121));
        } else {
            // oracle is active hence update oracle

            // olderPrice_ => temp_
            temp_ = (dexVariables_ >> 1) & X40;
            temp_ = (temp_ >> DEFAULT_EXPONENT_SIZE) << (temp_ & DEFAULT_EXPONENT_MASK);

            // oldPrice_ => temp2_
            temp2_ = (dexVariables_ >> 41) & X40;
            temp2_ = (temp2_ >> DEFAULT_EXPONENT_SIZE) << (temp2_ & DEFAULT_EXPONENT_MASK);

            int priceDiff_ = _priceDiffCheck(temp2_, newPrice_);

            unchecked {
                // older price w.r.t old price
                priceDiff_ = int(ORACLE_PRECISION) - int((temp_ * ORACLE_PRECISION) / temp2_);
            }

            // priceDiffInPercentAndSign_ => temp3_
            // priceDiff_ will always be lower than ORACLE_LIMIT due to above check
            unchecked {
                if (priceDiff_ < 0) {
                    temp3_ = ((uint(-priceDiff_) * X22) / ORACLE_LIMIT) << 1;
                } else {
                    // if greater than or equal to 0 then make sign flag 1
                    temp3_ = (((uint(priceDiff_) * X22) / ORACLE_LIMIT) << 1) | 1;
                }
            }

            if (timeDiff_ > X22) {
                // if time difference is this then that means DEX has been inactive ~45 days
                // that means oracle price of this DEX should not be used.
                timeDiff_ = X22;
            }

            // temp_ => lastTimeDiff_
            temp_ = (dexVariables_ >> 154) & X22;
            uint nextOracleSlot_ = ((dexVariables_ >> 176) & X3);
            uint oracleMap_ = (dexVariables_ >> 179) & X16;
            if (temp_ > X9) {
                if (nextOracleSlot_ > 0) {
                    // if greater than 0 then current slot has 2 or more oracle slot empty
                    // First 9 bits are of time, so not using that
                    temp3_ = (temp3_ << 41) | (temp_ << 9);
                    _oracle[oracleMap_] = _oracle[oracleMap_] | (temp3_ << (--nextOracleSlot_ * 32));
                    if (nextOracleSlot_ > 0) {
                        --nextOracleSlot_;
                    } else {
                        // if == 0 that means the oracle slots will get filled and shift to next oracle map
                        nextOracleSlot_ = 7;
                        unchecked {
                            oracleMap_ = (oracleMap_ + 1) % TOTAL_ORACLE_MAPPING;
                        }
                        _oracle[oracleMap_] = 0;
                    }
                } else {
                    // if == 0
                    // then seconds will be in last map
                    // precision will be in last map + 1
                    // Storing precision & sign slot in first precision & sign slot and leaving time slot empty
                    temp3_ = temp3_ << 9;
                    _oracle[oracleMap_] = _oracle[oracleMap_] | temp3_;
                    nextOracleSlot_ = 6; // storing 6 here as 7 is going to occupied right now
                    unchecked {
                        oracleMap_ = (oracleMap_ + 1) % TOTAL_ORACLE_MAPPING;
                    }
                    // Storing time in 2nd precision & sign and leaving time slot empty
                    _oracle[oracleMap_] = temp_ << ((7 * 32) + 9);
                }
            } else {
                temp3_ = (temp3_ << 9) | temp_;
                unchecked {
                    if (nextOracleSlot_ < 7) {
                        _oracle[oracleMap_] = _oracle[oracleMap_] | (temp3_ << (nextOracleSlot_ * 32));
                    } else {
                        _oracle[oracleMap_] = temp3_ << ((7 * 32));
                    }
                }
                if (nextOracleSlot_ > 0) {
                    --nextOracleSlot_;
                } else {
                    nextOracleSlot_ = 7;
                    unchecked {
                        oracleMap_ = (oracleMap_ + 1) % TOTAL_ORACLE_MAPPING;
                    }
                    _oracle[oracleMap_] = 0;
                }
            }

            // doing this due to stack too deep error when using params memory variables
            temp_ = newPrice_;
            temp2_ = centerPrice_;
            temp3_ = dexVariables_;

            // then update last price
            return ((temp3_ & 0xfffffffffffffff8000000000000000000000000000000000000000000000001) |
                (((temp3_ >> 41) & X40) << 1) |
                (temp_.toBigNumber(32, 8, BigMathMinified.ROUND_DOWN) << 41) |
                (temp2_.toBigNumber(32, 8, BigMathMinified.ROUND_DOWN) << 81) |
                (block.timestamp << 121) |
                (timeDiff_ << 154) |
                (nextOracleSlot_ << 176) |
                (oracleMap_ << 179));
        }
    }

    function _hookVerify(uint hookAddress_, uint mode_, bool swap0to1_, uint price_) internal {
        try
            IHook(AddressCalcs.addressCalc(DEPLOYER_CONTRACT, hookAddress_)).dexPrice(
                mode_,
                swap0to1_,
                TOKEN_0,
                TOKEN_1,
                price_
            )
        returns (bool isOk_) {
            if (!isOk_) revert FluidDexError(ErrorTypes.DexT1__HookReturnedFalse);
        } catch (bytes memory /*lowLevelData*/) {
            // skip checking hook nothing
        }
    }

    function _updateSupplyShares(uint newTotalShares_) internal {
        uint totalSupplyShares_ = _totalSupplyShares;

        // new total shares are greater than old total shares && new total shares are greater than max supply shares
        if (
            (newTotalShares_ > (totalSupplyShares_ & X128)) && 
            newTotalShares_ > (totalSupplyShares_ >> 128)
        ) {
            revert FluidDexError(ErrorTypes.DexT1__SupplySharesOverflow);
        }

        // keeping max supply shares intact
        _totalSupplyShares = ((totalSupplyShares_ >> 128) << 128) | newTotalShares_;
    }

    function _updateBorrowShares(uint newTotalShares_) internal {
        uint totalBorrowShares_ = _totalBorrowShares;

        // new total shares are greater than old total shares && new total shares are greater than max borrow shares
        if (
            (newTotalShares_ > (totalBorrowShares_ & X128)) && 
            newTotalShares_ > (totalBorrowShares_ >> 128)
        ) {
            revert FluidDexError(ErrorTypes.DexT1__BorrowSharesOverflow);
        }

        // keeping max borrow shares intact
        _totalBorrowShares = ((totalBorrowShares_ >> 128) << 128) | newTotalShares_;
    }

    constructor(ConstantViews memory constantViews_) ImmutableVariables(constantViews_) {}
}

File 22 of 25 : immutableVariables.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;

import { IFluidLiquidity } from "../../../../liquidity/interfaces/iLiquidity.sol";
import { Structs } from "./structs.sol";
import { ConstantVariables } from "../common/constantVariables.sol";
import { IFluidDexFactory } from "../../interfaces/iDexFactory.sol";
import { Error } from "../../error.sol";
import { ErrorTypes } from "../../errorTypes.sol";

abstract contract ImmutableVariables is ConstantVariables, Structs, Error {
    /*//////////////////////////////////////////////////////////////
                          CONSTANTS / IMMUTABLES
    //////////////////////////////////////////////////////////////*/

    uint256 public immutable DEX_ID;

    /// @dev Address of token 0
    address internal immutable TOKEN_0;

    /// @dev Address of token 1
    address internal immutable TOKEN_1;

    address internal immutable THIS_CONTRACT;

    uint256 internal immutable TOKEN_0_NUMERATOR_PRECISION;
    uint256 internal immutable TOKEN_0_DENOMINATOR_PRECISION;
    uint256 internal immutable TOKEN_1_NUMERATOR_PRECISION;
    uint256 internal immutable TOKEN_1_DENOMINATOR_PRECISION;

    /// @dev Address of liquidity contract
    IFluidLiquidity internal immutable LIQUIDITY;

    /// @dev Address of DEX factory contract
    IFluidDexFactory internal immutable DEX_FACTORY;

    /// @dev Address of Shift implementation
    address internal immutable SHIFT_IMPLEMENTATION;

    /// @dev Address of Admin implementation
    address internal immutable ADMIN_IMPLEMENTATION;

    /// @dev Address of Col Operations implementation
    address internal immutable COL_OPERATIONS_IMPLEMENTATION;

    /// @dev Address of Debt Operations implementation
    address internal immutable DEBT_OPERATIONS_IMPLEMENTATION;

    /// @dev Address of Perfect Operations and Swap Out implementation
    address internal immutable PERFECT_OPERATIONS_AND_SWAP_OUT_IMPLEMENTATION;

    /// @dev Address of contract used for deploying center price & hook related contract
    address internal immutable DEPLOYER_CONTRACT;

    /// @dev Liquidity layer slots
    bytes32 internal immutable SUPPLY_TOKEN_0_SLOT;
    bytes32 internal immutable BORROW_TOKEN_0_SLOT;
    bytes32 internal immutable SUPPLY_TOKEN_1_SLOT;
    bytes32 internal immutable BORROW_TOKEN_1_SLOT;
    bytes32 internal immutable EXCHANGE_PRICE_TOKEN_0_SLOT;
    bytes32 internal immutable EXCHANGE_PRICE_TOKEN_1_SLOT;
    uint256 internal immutable TOTAL_ORACLE_MAPPING;

    function _calcNumeratorAndDenominator(
        address token_
    ) private view returns (uint256 numerator_, uint256 denominator_) {
        uint256 decimals_ = _decimals(token_);
        if (decimals_ > TOKENS_DECIMALS_PRECISION) {
            numerator_ = 1;
            denominator_ = 10 ** (decimals_ - TOKENS_DECIMALS_PRECISION);
        } else {
            numerator_ = 10 ** (TOKENS_DECIMALS_PRECISION - decimals_);
            denominator_ = 1;
        }
    }

    constructor(ConstantViews memory constants_) {
        THIS_CONTRACT = address(this);

        DEX_ID = constants_.dexId;
        LIQUIDITY = IFluidLiquidity(constants_.liquidity);
        DEX_FACTORY = IFluidDexFactory(constants_.factory);

        TOKEN_0 = constants_.token0;
        TOKEN_1 = constants_.token1;

        if (TOKEN_0 >= TOKEN_1) revert FluidDexError(ErrorTypes.DexT1__Token0ShouldBeSmallerThanToken1);

        (TOKEN_0_NUMERATOR_PRECISION, TOKEN_0_DENOMINATOR_PRECISION) = _calcNumeratorAndDenominator(TOKEN_0);
        (TOKEN_1_NUMERATOR_PRECISION, TOKEN_1_DENOMINATOR_PRECISION) = _calcNumeratorAndDenominator(TOKEN_1);

        if (constants_.implementations.shift != address(0)) {
            SHIFT_IMPLEMENTATION = constants_.implementations.shift;
        } else {
            SHIFT_IMPLEMENTATION = address(this);
        }
        if (constants_.implementations.admin != address(0)) {
            ADMIN_IMPLEMENTATION = constants_.implementations.admin;
        } else {
            ADMIN_IMPLEMENTATION = address(this);
        }
        if (constants_.implementations.colOperations != address(0)) {
            COL_OPERATIONS_IMPLEMENTATION = constants_.implementations.colOperations;
        } else {
            COL_OPERATIONS_IMPLEMENTATION = address(this);
        }
        if (constants_.implementations.debtOperations != address(0)) {
            DEBT_OPERATIONS_IMPLEMENTATION = constants_.implementations.debtOperations;
        } else {
            DEBT_OPERATIONS_IMPLEMENTATION = address(this);
        }
        if (constants_.implementations.perfectOperationsAndSwapOut != address(0)) {
            PERFECT_OPERATIONS_AND_SWAP_OUT_IMPLEMENTATION = constants_.implementations.perfectOperationsAndSwapOut;
        } else {
            PERFECT_OPERATIONS_AND_SWAP_OUT_IMPLEMENTATION = address(this);
        }

        DEPLOYER_CONTRACT = constants_.deployerContract;

        SUPPLY_TOKEN_0_SLOT = constants_.supplyToken0Slot;
        BORROW_TOKEN_0_SLOT = constants_.borrowToken0Slot;
        SUPPLY_TOKEN_1_SLOT = constants_.supplyToken1Slot;
        BORROW_TOKEN_1_SLOT = constants_.borrowToken1Slot;
        EXCHANGE_PRICE_TOKEN_0_SLOT = constants_.exchangePriceToken0Slot;
        EXCHANGE_PRICE_TOKEN_1_SLOT = constants_.exchangePriceToken1Slot;

        if (constants_.oracleMapping > X16) revert FluidDexError(ErrorTypes.DexT1__OracleMappingOverflow);

        TOTAL_ORACLE_MAPPING = constants_.oracleMapping;
    }
}

File 23 of 25 : interfaces.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;

interface IHook {
    /// @notice Hook function to check for liquidation opportunities before external swaps
    /// @dev The primary use of this hook is to check if a particular pair vault has liquidation available.
    ///      If liquidation is available, it gives priority to the liquidation process before allowing external swaps.
    ///      In most cases, this hook will not be set.
    /// @param id_ Identifier for the operation type: 1 for swap, 2 for internal arbitrage
    /// @param swap0to1_ Direction of the swap: true if swapping token0 for token1, false otherwise
    /// @param token0_ Address of the first token in the pair
    /// @param token1_ Address of the second token in the pair
    /// @param price_ The price ratio of token1 to token0, expressed with 27 decimal places
    /// @return isOk_ Boolean indicating whether the operation should proceed
    function dexPrice(
        uint id_,
        bool swap0to1_,
        address token0_,
        address token1_,
        uint price_
    ) external returns (bool isOk_);
}

interface ICenterPrice {
    /// @notice Retrieves the center price for the pool
    /// @dev This function is marked as non-constant (potentially state-changing) to allow flexibility in price fetching mechanisms.
    ///      While typically used as a read-only operation, this design permits write operations if needed for certain token pairs
    ///      (e.g., fetching up-to-date exchange rates that may require state changes).
    /// @return price The current price ratio of token1 to token0, expressed with 27 decimal places
    function centerPrice() external returns (uint price);
}

File 24 of 25 : structs.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;

abstract contract Structs {
    struct PricesAndExchangePrice {
        uint lastStoredPrice; // last stored price in 1e27 decimals
        uint centerPrice; // last stored price in 1e27 decimals
        uint upperRange; // price at upper range in 1e27 decimals
        uint lowerRange; // price at lower range in 1e27 decimals
        uint geometricMean; // geometric mean of upper range & lower range in 1e27 decimals
        uint supplyToken0ExchangePrice;
        uint borrowToken0ExchangePrice;
        uint supplyToken1ExchangePrice;
        uint borrowToken1ExchangePrice;
    }

    struct ExchangePrices {
        uint supplyToken0ExchangePrice;
        uint borrowToken0ExchangePrice;
        uint supplyToken1ExchangePrice;
        uint borrowToken1ExchangePrice;
    }

    struct CollateralReserves {
        uint token0RealReserves;
        uint token1RealReserves;
        uint token0ImaginaryReserves;
        uint token1ImaginaryReserves;
    }

    struct CollateralReservesSwap {
        uint tokenInRealReserves;
        uint tokenOutRealReserves;
        uint tokenInImaginaryReserves;
        uint tokenOutImaginaryReserves;
    }

    struct DebtReserves {
        uint token0Debt;
        uint token1Debt;
        uint token0RealReserves;
        uint token1RealReserves;
        uint token0ImaginaryReserves;
        uint token1ImaginaryReserves;
    }

    struct DebtReservesSwap {
        uint tokenInDebt;
        uint tokenOutDebt;
        uint tokenInRealReserves;
        uint tokenOutRealReserves;
        uint tokenInImaginaryReserves;
        uint tokenOutImaginaryReserves;
    }

    struct SwapInMemory {
        address tokenIn;
        address tokenOut;
        uint256 amtInAdjusted;
        address withdrawTo;
        address borrowTo;
        uint price; // price of pool after swap
        uint fee; // fee of pool
        uint revenueCut; // revenue cut of pool
        bool swap0to1;
        int swapRoutingAmt;
        bytes data; // just added to avoid stack-too-deep error
    }

    struct SwapOutMemory {
        address tokenIn;
        address tokenOut;
        uint256 amtOutAdjusted;
        address withdrawTo;
        address borrowTo;
        uint price; // price of pool after swap
        uint fee;
        uint revenueCut; // revenue cut of pool
        bool swap0to1;
        int swapRoutingAmt;
        bytes data; // just added to avoid stack-too-deep error
        uint msgValue;
    }

    struct DepositColMemory {
        uint256 token0AmtAdjusted;
        uint256 token1AmtAdjusted;
        uint256 token0ReservesInitial;
        uint256 token1ReservesInitial;
    }

    struct WithdrawColMemory {
        uint256 token0AmtAdjusted;
        uint256 token1AmtAdjusted;
        uint256 token0ReservesInitial;
        uint256 token1ReservesInitial;
        address to;
    }

    struct BorrowDebtMemory {
        uint256 token0AmtAdjusted;
        uint256 token1AmtAdjusted;
        uint256 token0DebtInitial;
        uint256 token1DebtInitial;
        address to;
    }

    struct PaybackDebtMemory {
        uint256 token0AmtAdjusted;
        uint256 token1AmtAdjusted;
        uint256 token0DebtInitial;
        uint256 token1DebtInitial;
    }

    struct OraclePriceMemory {
        uint lowestPrice1by0;
        uint highestPrice1by0;
        uint oracleSlot;
        uint oracleMap;
        uint oracle;
    }

    struct Oracle {
        uint twap1by0; // TWAP price
        uint lowestPrice1by0; // lowest price point
        uint highestPrice1by0; // highest price point
        uint twap0by1; // TWAP price
        uint lowestPrice0by1; // lowest price point
        uint highestPrice0by1; // highest price point
    }

    struct Implementations {
        address shift;
        address admin;
        address colOperations;
        address debtOperations;
        address perfectOperationsAndSwapOut;
    }

    struct ConstantViews {
        uint256 dexId;
        address liquidity;
        address factory;
        Implementations implementations;
        address deployerContract;
        address token0;
        address token1;
        bytes32 supplyToken0Slot;
        bytes32 borrowToken0Slot;
        bytes32 supplyToken1Slot;
        bytes32 borrowToken1Slot;
        bytes32 exchangePriceToken0Slot;
        bytes32 exchangePriceToken1Slot;
        uint256 oracleMapping;
    }

    struct ConstantViews2 {
        uint token0NumeratorPrecision;
        uint token0DenominatorPrecision;
        uint token1NumeratorPrecision;
        uint token1DenominatorPrecision;
    }
}

File 25 of 25 : FixedPointMathLib.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Arithmetic library with operations for fixed-point numbers.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol)
/// @author Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol)
library FixedPointMathLib {
    /*//////////////////////////////////////////////////////////////
                    SIMPLIFIED FIXED POINT OPERATIONS
    //////////////////////////////////////////////////////////////*/

    uint256 internal constant MAX_UINT256 = 2**256 - 1;

    uint256 internal constant WAD = 1e18; // The scalar of ETH and most ERC20s.

    function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down.
    }

    function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up.
    }

    function divWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down.
    }

    function divWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up.
    }

    /*//////////////////////////////////////////////////////////////
                    LOW LEVEL FIXED POINT OPERATIONS
    //////////////////////////////////////////////////////////////*/

    function mulDivDown(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
            if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
                revert(0, 0)
            }

            // Divide x * y by the denominator.
            z := div(mul(x, y), denominator)
        }
    }

    function mulDivUp(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
            if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
                revert(0, 0)
            }

            // If x * y modulo the denominator is strictly greater than 0,
            // 1 is added to round up the division of x * y by the denominator.
            z := add(gt(mod(mul(x, y), denominator), 0), div(mul(x, y), denominator))
        }
    }

    function rpow(
        uint256 x,
        uint256 n,
        uint256 scalar
    ) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            switch x
            case 0 {
                switch n
                case 0 {
                    // 0 ** 0 = 1
                    z := scalar
                }
                default {
                    // 0 ** n = 0
                    z := 0
                }
            }
            default {
                switch mod(n, 2)
                case 0 {
                    // If n is even, store scalar in z for now.
                    z := scalar
                }
                default {
                    // If n is odd, store x in z for now.
                    z := x
                }

                // Shifting right by 1 is like dividing by 2.
                let half := shr(1, scalar)

                for {
                    // Shift n right by 1 before looping to halve it.
                    n := shr(1, n)
                } n {
                    // Shift n right by 1 each iteration to halve it.
                    n := shr(1, n)
                } {
                    // Revert immediately if x ** 2 would overflow.
                    // Equivalent to iszero(eq(div(xx, x), x)) here.
                    if shr(128, x) {
                        revert(0, 0)
                    }

                    // Store x squared.
                    let xx := mul(x, x)

                    // Round to the nearest number.
                    let xxRound := add(xx, half)

                    // Revert if xx + half overflowed.
                    if lt(xxRound, xx) {
                        revert(0, 0)
                    }

                    // Set x to scaled xxRound.
                    x := div(xxRound, scalar)

                    // If n is even:
                    if mod(n, 2) {
                        // Compute z * x.
                        let zx := mul(z, x)

                        // If z * x overflowed:
                        if iszero(eq(div(zx, x), z)) {
                            // Revert if x is non-zero.
                            if iszero(iszero(x)) {
                                revert(0, 0)
                            }
                        }

                        // Round to the nearest number.
                        let zxRound := add(zx, half)

                        // Revert if zx + half overflowed.
                        if lt(zxRound, zx) {
                            revert(0, 0)
                        }

                        // Return properly scaled zxRound.
                        z := div(zxRound, scalar)
                    }
                }
            }
        }
    }

    /*//////////////////////////////////////////////////////////////
                        GENERAL NUMBER UTILITIES
    //////////////////////////////////////////////////////////////*/

    function sqrt(uint256 x) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            let y := x // We start y at x, which will help us make our initial estimate.

            z := 181 // The "correct" value is 1, but this saves a multiplication later.

            // This segment is to get a reasonable initial estimate for the Babylonian method. With a bad
            // start, the correct # of bits increases ~linearly each iteration instead of ~quadratically.

            // We check y >= 2^(k + 8) but shift right by k bits
            // each branch to ensure that if x >= 256, then y >= 256.
            if iszero(lt(y, 0x10000000000000000000000000000000000)) {
                y := shr(128, y)
                z := shl(64, z)
            }
            if iszero(lt(y, 0x1000000000000000000)) {
                y := shr(64, y)
                z := shl(32, z)
            }
            if iszero(lt(y, 0x10000000000)) {
                y := shr(32, y)
                z := shl(16, z)
            }
            if iszero(lt(y, 0x1000000)) {
                y := shr(16, y)
                z := shl(8, z)
            }

            // Goal was to get z*z*y within a small factor of x. More iterations could
            // get y in a tighter range. Currently, we will have y in [256, 256*2^16).
            // We ensured y >= 256 so that the relative difference between y and y+1 is small.
            // That's not possible if x < 256 but we can just verify those cases exhaustively.

            // Now, z*z*y <= x < z*z*(y+1), and y <= 2^(16+8), and either y >= 256, or x < 256.
            // Correctness can be checked exhaustively for x < 256, so we assume y >= 256.
            // Then z*sqrt(y) is within sqrt(257)/sqrt(256) of sqrt(x), or about 20bps.

            // For s in the range [1/256, 256], the estimate f(s) = (181/1024) * (s+1) is in the range
            // (1/2.84 * sqrt(s), 2.84 * sqrt(s)), with largest error when s = 1 and when s = 256 or 1/256.

            // Since y is in [256, 256*2^16), let a = y/65536, so that a is in [1/256, 256). Then we can estimate
            // sqrt(y) using sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2^18.

            // There is no overflow risk here since y < 2^136 after the first branch above.
            z := shr(18, mul(z, add(y, 65536))) // A mul() is saved from starting z at 181.

            // Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough.
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))

            // If x+1 is a perfect square, the Babylonian method cycles between
            // floor(sqrt(x)) and ceil(sqrt(x)). This statement ensures we return floor.
            // See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division
            // Since the ceil is rare, we save gas on the assignment and repeat division in the rare case.
            // If you don't care whether the floor or ceil square root is returned, you can remove this statement.
            z := sub(z, lt(div(x, z), z))
        }
    }

    function unsafeMod(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Mod x by y. Note this will return
            // 0 instead of reverting if y is zero.
            z := mod(x, y)
        }
    }

    function unsafeDiv(uint256 x, uint256 y) internal pure returns (uint256 r) {
        /// @solidity memory-safe-assembly
        assembly {
            // Divide x by y. Note this will return
            // 0 instead of reverting if y is zero.
            r := div(x, y)
        }
    }

    function unsafeDivUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Add 1 to x * y if x % y > 0. Note this will
            // return 0 instead of reverting if y is zero.
            z := add(gt(mod(x, y), 0), div(x, y))
        }
    }
}

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

Contract Security Audit

Contract ABI

[{"inputs":[{"components":[{"internalType":"uint256","name":"dexId","type":"uint256"},{"internalType":"address","name":"liquidity","type":"address"},{"internalType":"address","name":"factory","type":"address"},{"components":[{"internalType":"address","name":"shift","type":"address"},{"internalType":"address","name":"admin","type":"address"},{"internalType":"address","name":"colOperations","type":"address"},{"internalType":"address","name":"debtOperations","type":"address"},{"internalType":"address","name":"perfectOperationsAndSwapOut","type":"address"}],"internalType":"struct Structs.Implementations","name":"implementations","type":"tuple"},{"internalType":"address","name":"deployerContract","type":"address"},{"internalType":"address","name":"token0","type":"address"},{"internalType":"address","name":"token1","type":"address"},{"internalType":"bytes32","name":"supplyToken0Slot","type":"bytes32"},{"internalType":"bytes32","name":"borrowToken0Slot","type":"bytes32"},{"internalType":"bytes32","name":"supplyToken1Slot","type":"bytes32"},{"internalType":"bytes32","name":"borrowToken1Slot","type":"bytes32"},{"internalType":"bytes32","name":"exchangePriceToken0Slot","type":"bytes32"},{"internalType":"bytes32","name":"exchangePriceToken1Slot","type":"bytes32"},{"internalType":"uint256","name":"oracleMapping","type":"uint256"}],"internalType":"struct Structs.ConstantViews","name":"constantViews_","type":"tuple"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"errorId_","type":"uint256"}],"name":"FluidDexError","type":"error"},{"inputs":[{"internalType":"uint256","name":"errorId","type":"uint256"}],"name":"FluidDexFactoryError","type":"error"},{"inputs":[{"internalType":"uint256","name":"shares_","type":"uint256"}],"name":"FluidDexLiquidityOutput","type":"error"},{"inputs":[{"internalType":"uint256","name":"token0Amt","type":"uint256"},{"internalType":"uint256","name":"token1Amt","type":"uint256"}],"name":"FluidDexPerfectLiquidityOutput","type":"error"},{"inputs":[{"components":[{"internalType":"uint256","name":"lastStoredPrice","type":"uint256"},{"internalType":"uint256","name":"centerPrice","type":"uint256"},{"internalType":"uint256","name":"upperRange","type":"uint256"},{"internalType":"uint256","name":"lowerRange","type":"uint256"},{"internalType":"uint256","name":"geometricMean","type":"uint256"},{"internalType":"uint256","name":"supplyToken0ExchangePrice","type":"uint256"},{"internalType":"uint256","name":"borrowToken0ExchangePrice","type":"uint256"},{"internalType":"uint256","name":"supplyToken1ExchangePrice","type":"uint256"},{"internalType":"uint256","name":"borrowToken1ExchangePrice","type":"uint256"}],"internalType":"struct Structs.PricesAndExchangePrice","name":"pex_","type":"tuple"}],"name":"FluidDexPricesAndExchangeRates","type":"error"},{"inputs":[{"internalType":"uint256","name":"tokenAmt","type":"uint256"}],"name":"FluidDexSingleTokenOutput","type":"error"},{"inputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}],"name":"FluidDexSwapResult","type":"error"},{"inputs":[{"internalType":"uint256","name":"errorId_","type":"uint256"}],"name":"FluidLiquidityCalcsError","type":"error"},{"inputs":[{"internalType":"uint256","name":"errorId_","type":"uint256"}],"name":"FluidSafeTransferError","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"int256","name":"routing","type":"int256"},{"indexed":false,"internalType":"uint256","name":"amtOut","type":"uint256"}],"name":"LogArbitrage","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount0","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount1","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"LogBorrowDebtLiquidity","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"token0Amt","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"token1Amt","type":"uint256"}],"name":"LogBorrowPerfectDebtLiquidity","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount0","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount1","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"LogDepositColLiquidity","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"token0Amt","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"token1Amt","type":"uint256"}],"name":"LogDepositPerfectColLiquidity","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"token0Amt","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"token1Amt","type":"uint256"}],"name":"LogPaybackDebtInOneToken","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount0","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount1","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"LogPaybackDebtLiquidity","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"token0Amt","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"token1Amt","type":"uint256"}],"name":"LogPaybackPerfectDebtLiquidity","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"token0Amt","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"token1Amt","type":"uint256"}],"name":"LogWithdrawColInOneToken","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount0","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount1","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"LogWithdrawColLiquidity","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"token0Amt","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"token1Amt","type":"uint256"}],"name":"LogWithdrawPerfectColLiquidity","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"swap0to1","type":"bool"},{"indexed":false,"internalType":"uint256","name":"amountIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountOut","type":"uint256"},{"indexed":false,"internalType":"address","name":"to","type":"address"}],"name":"Swap","type":"event"},{"stateMutability":"payable","type":"fallback"},{"inputs":[],"name":"DEX_ID","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"token0Amt_","type":"uint256"},{"internalType":"uint256","name":"token1Amt_","type":"uint256"},{"internalType":"uint256","name":"maxSharesAmt_","type":"uint256"},{"internalType":"address","name":"to_","type":"address"}],"name":"borrow","outputs":[{"internalType":"uint256","name":"shares_","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares_","type":"uint256"},{"internalType":"uint256","name":"minToken0Borrow_","type":"uint256"},{"internalType":"uint256","name":"minToken1Borrow_","type":"uint256"},{"internalType":"address","name":"to_","type":"address"}],"name":"borrowPerfect","outputs":[{"internalType":"uint256","name":"token0Amt_","type":"uint256"},{"internalType":"uint256","name":"token1Amt_","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"constantsView","outputs":[{"components":[{"internalType":"uint256","name":"dexId","type":"uint256"},{"internalType":"address","name":"liquidity","type":"address"},{"internalType":"address","name":"factory","type":"address"},{"components":[{"internalType":"address","name":"shift","type":"address"},{"internalType":"address","name":"admin","type":"address"},{"internalType":"address","name":"colOperations","type":"address"},{"internalType":"address","name":"debtOperations","type":"address"},{"internalType":"address","name":"perfectOperationsAndSwapOut","type":"address"}],"internalType":"struct Structs.Implementations","name":"implementations","type":"tuple"},{"internalType":"address","name":"deployerContract","type":"address"},{"internalType":"address","name":"token0","type":"address"},{"internalType":"address","name":"token1","type":"address"},{"internalType":"bytes32","name":"supplyToken0Slot","type":"bytes32"},{"internalType":"bytes32","name":"borrowToken0Slot","type":"bytes32"},{"internalType":"bytes32","name":"supplyToken1Slot","type":"bytes32"},{"internalType":"bytes32","name":"borrowToken1Slot","type":"bytes32"},{"internalType":"bytes32","name":"exchangePriceToken0Slot","type":"bytes32"},{"internalType":"bytes32","name":"exchangePriceToken1Slot","type":"bytes32"},{"internalType":"uint256","name":"oracleMapping","type":"uint256"}],"internalType":"struct Structs.ConstantViews","name":"constantsView_","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"constantsView2","outputs":[{"components":[{"internalType":"uint256","name":"token0NumeratorPrecision","type":"uint256"},{"internalType":"uint256","name":"token0DenominatorPrecision","type":"uint256"},{"internalType":"uint256","name":"token1NumeratorPrecision","type":"uint256"},{"internalType":"uint256","name":"token1DenominatorPrecision","type":"uint256"}],"internalType":"struct Structs.ConstantViews2","name":"constantsView2_","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"token0Amt_","type":"uint256"},{"internalType":"uint256","name":"token1Amt_","type":"uint256"},{"internalType":"uint256","name":"minSharesAmt_","type":"uint256"},{"internalType":"bool","name":"estimate_","type":"bool"}],"name":"deposit","outputs":[{"internalType":"uint256","name":"shares_","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares_","type":"uint256"},{"internalType":"uint256","name":"maxToken0Deposit_","type":"uint256"},{"internalType":"uint256","name":"maxToken1Deposit_","type":"uint256"},{"internalType":"bool","name":"estimate_","type":"bool"}],"name":"depositPerfect","outputs":[{"internalType":"uint256","name":"token0Amt_","type":"uint256"},{"internalType":"uint256","name":"token1Amt_","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"geometricMean_","type":"uint256"},{"internalType":"uint256","name":"upperRange_","type":"uint256"},{"internalType":"uint256","name":"lowerRange_","type":"uint256"},{"internalType":"uint256","name":"token0SupplyExchangePrice_","type":"uint256"},{"internalType":"uint256","name":"token1SupplyExchangePrice_","type":"uint256"}],"name":"getCollateralReserves","outputs":[{"components":[{"internalType":"uint256","name":"token0RealReserves","type":"uint256"},{"internalType":"uint256","name":"token1RealReserves","type":"uint256"},{"internalType":"uint256","name":"token0ImaginaryReserves","type":"uint256"},{"internalType":"uint256","name":"token1ImaginaryReserves","type":"uint256"}],"internalType":"struct Structs.CollateralReserves","name":"c_","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"geometricMean_","type":"uint256"},{"internalType":"uint256","name":"upperRange_","type":"uint256"},{"internalType":"uint256","name":"lowerRange_","type":"uint256"},{"internalType":"uint256","name":"token0BorrowExchangePrice_","type":"uint256"},{"internalType":"uint256","name":"token1BorrowExchangePrice_","type":"uint256"}],"name":"getDebtReserves","outputs":[{"components":[{"internalType":"uint256","name":"token0Debt","type":"uint256"},{"internalType":"uint256","name":"token1Debt","type":"uint256"},{"internalType":"uint256","name":"token0RealReserves","type":"uint256"},{"internalType":"uint256","name":"token1RealReserves","type":"uint256"},{"internalType":"uint256","name":"token0ImaginaryReserves","type":"uint256"},{"internalType":"uint256","name":"token1ImaginaryReserves","type":"uint256"}],"internalType":"struct Structs.DebtReserves","name":"d_","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPricesAndExchangePrices","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token_","type":"address"},{"internalType":"uint256","name":"amount_","type":"uint256"},{"internalType":"bytes","name":"data_","type":"bytes"}],"name":"liquidityCallback","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"secondsAgos_","type":"uint256[]"}],"name":"oraclePrice","outputs":[{"components":[{"internalType":"uint256","name":"twap1by0","type":"uint256"},{"internalType":"uint256","name":"lowestPrice1by0","type":"uint256"},{"internalType":"uint256","name":"highestPrice1by0","type":"uint256"},{"internalType":"uint256","name":"twap0by1","type":"uint256"},{"internalType":"uint256","name":"lowestPrice0by1","type":"uint256"},{"internalType":"uint256","name":"highestPrice0by1","type":"uint256"}],"internalType":"struct Structs.Oracle[]","name":"twaps_","type":"tuple[]"},{"internalType":"uint256","name":"currentPrice_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"token0Amt_","type":"uint256"},{"internalType":"uint256","name":"token1Amt_","type":"uint256"},{"internalType":"uint256","name":"minSharesAmt_","type":"uint256"},{"internalType":"bool","name":"estimate_","type":"bool"}],"name":"payback","outputs":[{"internalType":"uint256","name":"shares_","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares_","type":"uint256"},{"internalType":"uint256","name":"maxToken0Payback_","type":"uint256"},{"internalType":"uint256","name":"maxToken1Payback_","type":"uint256"},{"internalType":"bool","name":"estimate_","type":"bool"}],"name":"paybackPerfect","outputs":[{"internalType":"uint256","name":"token0Amt_","type":"uint256"},{"internalType":"uint256","name":"token1Amt_","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares_","type":"uint256"},{"internalType":"uint256","name":"maxToken0_","type":"uint256"},{"internalType":"uint256","name":"maxToken1_","type":"uint256"},{"internalType":"bool","name":"estimate_","type":"bool"}],"name":"paybackPerfectInOneToken","outputs":[{"internalType":"uint256","name":"paybackAmt_","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"slot_","type":"bytes32"}],"name":"readFromStorage","outputs":[{"internalType":"uint256","name":"result_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"swap0to1_","type":"bool"},{"internalType":"uint256","name":"amountIn_","type":"uint256"},{"internalType":"uint256","name":"amountOutMin_","type":"uint256"},{"internalType":"address","name":"to_","type":"address"}],"name":"swapIn","outputs":[{"internalType":"uint256","name":"amountOut_","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bool","name":"swap0to1_","type":"bool"},{"internalType":"uint256","name":"amountIn_","type":"uint256"},{"internalType":"uint256","name":"amountOutMin_","type":"uint256"},{"internalType":"address","name":"to_","type":"address"}],"name":"swapInWithCallback","outputs":[{"internalType":"uint256","name":"amountOut_","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bool","name":"swap0to1_","type":"bool"},{"internalType":"uint256","name":"amountOut_","type":"uint256"},{"internalType":"uint256","name":"amountInMax_","type":"uint256"},{"internalType":"address","name":"to_","type":"address"}],"name":"swapOut","outputs":[{"internalType":"uint256","name":"amountIn_","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bool","name":"swap0to1_","type":"bool"},{"internalType":"uint256","name":"amountOut_","type":"uint256"},{"internalType":"uint256","name":"amountInMax_","type":"uint256"},{"internalType":"address","name":"to_","type":"address"}],"name":"swapOutWithCallback","outputs":[{"internalType":"uint256","name":"amountIn_","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"token0Amt_","type":"uint256"},{"internalType":"uint256","name":"token1Amt_","type":"uint256"},{"internalType":"uint256","name":"maxSharesAmt_","type":"uint256"},{"internalType":"address","name":"to_","type":"address"}],"name":"withdraw","outputs":[{"internalType":"uint256","name":"shares_","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares_","type":"uint256"},{"internalType":"uint256","name":"minToken0Withdraw_","type":"uint256"},{"internalType":"uint256","name":"minToken1Withdraw_","type":"uint256"},{"internalType":"address","name":"to_","type":"address"}],"name":"withdrawPerfect","outputs":[{"internalType":"uint256","name":"token0Amt_","type":"uint256"},{"internalType":"uint256","name":"token1Amt_","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares_","type":"uint256"},{"internalType":"uint256","name":"minToken0_","type":"uint256"},{"internalType":"uint256","name":"minToken1_","type":"uint256"},{"internalType":"address","name":"to_","type":"address"}],"name":"withdrawPerfectInOneToken","outputs":[{"internalType":"uint256","name":"withdrawAmt_","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]

6103606040523480156200001257600080fd5b5060405162005f4938038062005f49833981016040819052620000359162000518565b3060e052805160805260208101516001600160a01b0390811661018052604082015181166101a05260a08083015182169081905260c080840151909216918290528291829110620000a2576040516317f71f0760e11b815261c76160048201526024015b60405180910390fd5b60a051620000b09062000313565b610120526101005260c051620000c69062000313565b61016052610140526060810151516001600160a01b031615620000fc576060810151516001600160a01b03166101c05262000102565b306101c0525b6060810151602001516001600160a01b03161562000136576060810151602001516001600160a01b03166101e0526200013c565b306101e0525b6060810151604001516001600160a01b03161562000170576060810151604001516001600160a01b03166102005262000176565b30610200525b60608082015101516001600160a01b031615620001a85760608082015101516001600160a01b031661022052620001ae565b30610220525b6060810151608001516001600160a01b031615620001e2576060810151608001516001600160a01b031661024052620001e8565b30610240525b60808101516001600160a01b03166102605260e0810151610280526101008101516102a0526101208101516102c0526101408101516102e05261016081015161030052610180810151610320526101a081015161ffff101562000263576040516317f71f0760e11b815261c762600482015260240162000099565b6101a0015161034052506060810151516001600160a01b031615806200029857506060810151602001516001600160a01b0316155b80620002b357506060810151604001516001600160a01b0316155b80620002cd575060608082015101516001600160a01b0316155b80620002e857506060810151608001516001600160a01b0316155b156200030c576040516317f71f0760e11b815261c772600482015260240162000099565b5062000771565b6000808062000322846200037b565b9050600c8111156200035457600192506200033f600c8262000624565b6200034c90600a62000737565b915062000375565b6200036181600c62000624565b6200036e90600a62000737565b9250600191505b50915091565b60006001600160a01b03821673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee146200041057816001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015620003e1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200040791906200074c565b60ff1662000413565b60125b92915050565b6040516101c081016001600160401b03811182821017156200044b57634e487b7160e01b600052604160045260246000fd5b60405290565b80516001600160a01b03811681146200046957600080fd5b919050565b600060a082840312156200048157600080fd5b60405160a081016001600160401b0381118282101715620004b257634e487b7160e01b600052604160045260246000fd5b604052905080620004c38362000451565b8152620004d36020840162000451565b6020820152620004e66040840162000451565b6040820152620004f96060840162000451565b60608201526200050c6080840162000451565b60808201525092915050565b600061024082840312156200052c57600080fd5b6200053662000419565b82518152620005486020840162000451565b60208201526200055b6040840162000451565b60408201526200056f84606085016200046e565b60608201526101006200058481850162000451565b60808301526101206200059981860162000451565b60a0840152610140620005ae81870162000451565b60c08501526101608087015160e086015261018080880151858701526101a0945084880151848701526101c0880151838701526101e088015182870152610200880151818701525050505061022084015181830152508091505092915050565b634e487b7160e01b600052601160045260246000fd5b818103818111156200041357620004136200060e565b600181815b808511156200067b5781600019048211156200065f576200065f6200060e565b808516156200066d57918102915b93841c93908002906200063f565b509250929050565b600082620006945750600162000413565b81620006a35750600062000413565b8160018114620006bc5760028114620006c757620006e7565b600191505062000413565b60ff841115620006db57620006db6200060e565b50506001821b62000413565b5060208310610133831016604e8410600b84101617156200070c575081810a62000413565b6200071883836200063a565b80600019048211156200072f576200072f6200060e565b029392505050565b600062000745838362000683565b9392505050565b6000602082840312156200075f57600080fd5b815160ff811681146200074557600080fd5b60805160a05160c05160e05161010051610120516101405161016051610180516101a0516101c0516101e05161020051610220516102405161026051610280516102a0516102c0516102e05161030051610320516103405161551062000a3960003960008181610ff0015281816112690152818161131a01528181613dba01528181613e230152613ed0015260008181610fc9015281816129c5015261311f015260008181610fa20152818161298e015261303d015260008181610f7b0152611936015260008181610f540152612ae4015260008181610f2d0152611906015260008181610f070152612ab4015260008181610edf01528181612cdc0152613823015260008181610856015281816108ba0152610eb60152600081816107970152610e8f01526000818161093d0152610e640152600081816105f50152610e39015260008181610e12015281816145330152818161463301526146c4015260008181610429015281816104e30152610d8f015260008181610a9401528181610c5e01528181610d63015281816127c2015281816128650152818161306701528181613149015281816132b50152613a2401526000818161076901528181611c5f0152818161249a015281816124ee0152818161254a015281816125a1015261335f01526000818161074301528181611c8001528181612479015281816124cd015281816125260152818161257d015261338001526000818161071d01528181611ba2015281816123ef01528181612446015281816125f50152818161264901526133b60152600081816106fa01528181611bc3015281816123cb01528181612422015281816125d40152818161262801526133d701526000505060008181610de301528181611b5601528181611c3b01526138bc015260008181610dbb01528181611b7e01528181611c1301526138940152600081816103d90152610d2b01526155106000f3fe6080604052600436106101845760003560e01c80636560abaa116100d6578063be17c79c1161007f578063e27203cd11610059578063e27203cd1461027f578063e980e1eb146103b9578063f4b9a3fb146103c7576101bd565b8063be17c79c14610378578063d331bef7146102b4578063d811b2ce1461038b576101bd565b8063ad207501116100b0578063ad20750114610317578063b5c736e414610337578063b7791bf214610356576101bd565b80636560abaa146102e25780636876698114610271578063916cef4e14610302576101bd565b806330acd6fd116101385780634d9036de116101125780634d9036de146102d45780635b3d38d7146102d4578063653295aa1461025e576101bd565b806330acd6fd1461027157806335f0df981461027f5780634c89bfd4146102b4576101bd565b8063242011d511610169578063242011d51461021d5780632668dfaa1461024b578063286f0e611461025e576101bd565b806305d455a9146101c55780631595cbd3146101fb576101bd565b366101bd577fffffffff0000000000000000000000000000000000000000000000000000000060003516156101bb576101bb6103fb565b005b6101bb6103fb565b3480156101d157600080fd5b506101e56101e0366004614b99565b61067d565b6040516101f29190614bd4565b60405180910390f35b34801561020757600080fd5b506102106106cd565b6040516101f29190614c17565b34801561022957600080fd5b5061023d610238366004614c67565b610790565b6040519081526020016101f2565b61023d610259366004614cb6565b61080f565b61023d61026c366004614cb6565b61084f565b61023d610238366004614cf5565b34801561028b57600080fd5b5061029f61029a366004614c67565b6108b2565b604080519283526020830191909152016101f2565b3480156102c057600080fd5b5061023d6102cf366004614c67565b610936565b61029f61029a366004614cf5565b3480156102ee57600080fd5b506102106102fd366004614b99565b610999565b34801561030e57600080fd5b506101bb6109d1565b34801561032357600080fd5b506101bb610332366004614d2b565b610a7c565b34801561034357600080fd5b5061023d610352366004614db4565b5490565b34801561036257600080fd5b5061036b610c8c565b6040516101f29190614dcd565b61023d610386366004614cb6565b611018565b34801561039757600080fd5b506103ab6103a6366004614f8e565b611058565b6040516101f292919061505e565b61023d6102cf366004614cf5565b3480156103d357600080fd5b5061023d7f000000000000000000000000000000000000000000000000000000000000000081565b6040517f4502d0630000000000000000000000000000000000000000000000000000000081523360048201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690634502d06390602401602060405180830381865afa158015610485573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104a991906150e5565b8061056357506040517ffbeeca2c0000000000000000000000000000000000000000000000000000000081523060048201523360248201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063fbeeca2c90604401602060405180830381865afa15801561053f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061056391906150e5565b6105a2576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c73a60048201526024015b60405180910390fd5b600054600180821690036105e6576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c7396004820152602401610599565b806001176000819055506106517f00000000000000000000000000000000000000000000000000000000000000006000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061188092505050565b5050600080547ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe169055565b6106b66040518060c001604052806000815260200160008152602001600081526020016000815260200160008152602001600081525090565b6106c386868686866118c6565b9695505050505050565b6106f86040518060800160405280600081526020016000815260200160008152602001600081525090565b7f000000000000000000000000000000000000000000000000000000000000000081527f000000000000000000000000000000000000000000000000000000000000000060208201527f000000000000000000000000000000000000000000000000000000000000000060408201527f0000000000000000000000000000000000000000000000000000000000000000606082015290565b60006107f37f00000000000000000000000000000000000000000000000000000000000000006000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061188092505050565b8060200190518101906108069190615102565b95945050505050565b6000610806858560405180606001604052808673ffffffffffffffffffffffffffffffffffffffff16815260200187815260200160001515815250611a0e565b60006107f37f00000000000000000000000000000000000000000000000000000000000000006000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061188092505050565b6000806109167f00000000000000000000000000000000000000000000000000000000000000006000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061188092505050565b806020019051810190610929919061511b565b9150915094509492505050565b60006107f37f00000000000000000000000000000000000000000000000000000000000000006000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061188092505050565b6109c46040518060800160405280600081526020016000815260200160008152602001600081525090565b6106c38686868686612a82565b6000546001546109e18282612bbd565b60006109f1600054600154612c48565b604080517f23bfdd3b0000000000000000000000000000000000000000000000000000000081528251600482015260208301516024820152908201516044820152606082015160648201526080820151608482015260a082015160a482015260c082015160c482015260e082015160e482015261010082015161010482015290915061012401610599565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610aef576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c75d6004820152602401610599565b600054600116600003610b32576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c75e6004820152602401610599565b60608114610b70576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c7746004820152602401610599565b60008080610b808486018661513f565b92509250925085831015610bc4576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c7756004820152602401610599565b8115610c57576040517f9410ae8800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff888116600483015260248201859052821690639410ae8890604401600060405180830381600087803b158015610c3a57600080fd5b505af1158015610c4e573d6000803e3d6000fd5b50505050610c83565b610c8387827f0000000000000000000000000000000000000000000000000000000000000000866131a5565b50505050505050565b610d29604080516101c08101825260008082526020808301829052828401829052835160a08101855282815290810182905292830181905260608381018290526080840191909152909190820190815260006020820181905260408201819052606082018190526080820181905260a0820181905260c0820181905260e08201819052610100820181905261012082018190526101409091015290565b7f0000000000000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000081166020808401919091527f000000000000000000000000000000000000000000000000000000000000000082166040808501919091527f0000000000000000000000000000000000000000000000000000000000000000831660a08501527f0000000000000000000000000000000000000000000000000000000000000000831660c0850152606080850180517f00000000000000000000000000000000000000000000000000000000000000008616905280517f0000000000000000000000000000000000000000000000000000000000000000861694019390935282517f0000000000000000000000000000000000000000000000000000000000000000851692019190915281517f00000000000000000000000000000000000000000000000000000000000000008416910152517f000000000000000000000000000000000000000000000000000000000000000082166080918201527f0000000000000000000000000000000000000000000000000000000000000000909116908201527f000000000000000000000000000000000000000000000000000000000000000060e08201527f00000000000000000000000000000000000000000000000000000000000000006101008201527f00000000000000000000000000000000000000000000000000000000000000006101208201527f00000000000000000000000000000000000000000000000000000000000000006101408201527f00000000000000000000000000000000000000000000000000000000000000006101608201527f00000000000000000000000000000000000000000000000000000000000000006101808201527f00000000000000000000000000000000000000000000000000000000000000006101a082015290565b6000610806858560405180606001604052808673ffffffffffffffffffffffffffffffffffffffff16815260200187815260200160011515815250611a0e565b6060600061108e6040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b6000805490600160c383901c1690036110d7576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c77a6004820152602401610599565b845167ffffffffffffffff8111156110f1576110f1614f5f565b60405190808252806020026020018201604052801561115b57816020015b6111486040518060c001604052806000815260200160008152602001600081526020016000815260200160008152602001600081525090565b81526020019060019003908161110f5790505b5093506000806000808860008151811061117757611177615181565b602090810291909101810151603187901c63ffffffff16602988901c60ff161b808952918801829052600760b088901c811660408a0181905261ffff60b38a901c1660608b0152929950909250889160009182918291116111d95760006111ee565b60608a01516000908152600660205260409020545b60808b01526000804260798c901c6401ffffffff1610156112255761121e60798c901c6401ffffffff16426151df565b9850611231565b61122e836151f2565b92505b8260020361146e578b6040018051611248906151f2565b908190526008036112bc57600060408d0181905260608d0151900361128e577f000000000000000000000000000000000000000000000000000000000000000060608d01525b600660008d60600180516112a19061522a565b90819052815260208101919091526040016000205460808d01525b63ffffffff8c6040015160206112d2919061525f565b60808e0151901c1691508115611438576101ff82169850886000036113ad57600982901c98508b6040015160070361137b57600060408d0181905260608d0151900361133f577f000000000000000000000000000000000000000000000000000000000000000060608d01525b600660008d60600180516113529061522a565b90819052815260208101919091526040016000205460808d0181905263ffffffff1691506113ad565b8b604001805161138a906151f2565b905260408c015163ffffffff906113a290602061525f565b8d60800151901c1691505b50600a81901c623fffff6113c88266b1a2bc2ec5000061525f565b6113d291906152a5565b9050600982901c60011660010361141157670de0b6b3a76400006113f6828861525f565b61140091906152a5565b61140a90876151df565b95506114bc565b670de0b6b3a7640000611424828861525f565b61142e91906152a5565b61140a90876152b9565b6040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c76a6004820152602401610599565b826001036114a857609a8b901c623fffff16985063ffffffff60098c901c1660ff60018d901c161b95506114a1836151f2565b92506114bc565b826000036114bc576114b9836151f2565b92505b6114c6898b6152b9565b9950858c6000015111156114d857858c525b858c6020015110156114ec5760208c018690525b868a1015611547576114fe898761525f565b61150890866152b9565b94508861152c87760a70c3c40a64e6c51999090b65f67d92400000000000006152a5565b611536919061525f565b61154090856152b9565b9350611231565b89611552888b6152b9565b61155c91906151df565b9850611568898761525f565b61157290866152b9565b94508861159687760a70c3c40a64e6c51999090b65f67d92400000000000006152a5565b6115a0919061525f565b6115aa90856152b9565b93506115b687866152a5565b94506115c287856152a5565b93506040518060c001604052808681526020018d6000015181526020018d6020015181526020018581526020018d60200151760a70c3c40a64e6c51999090b65f67d924000000000000061161691906152a5565b81526020018d60000151760a70c3c40a64e6c51999090b65f67d924000000000000061164291906152a5565b8152508e898151811061165757611657615181565b602090810291909101810191909152868d528c018690525b8e5161167a896151f2565b98508810156118625761168d878b6151df565b99508998508e61169e60018a6151df565b815181106116ae576116ae615181565b60200260200101518f89815181106116c8576116c8615181565b60200260200101516116da91906151df565b9650868a1015611756576116ee898761525f565b94508861171287760a70c3c40a64e6c51999090b65f67d92400000000000006152a5565b61171c919061525f565b935088600003611751577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8c52600060208d01525b611862565b89611761888b6152b9565b61176b91906151df565b9850611777898761525f565b94508861179b87760a70c3c40a64e6c51999090b65f67d92400000000000006152a5565b6117a5919061525f565b93506117b187866152a5565b94506117bd87856152a5565b93506040518060c001604052808681526020018d6000015181526020018d6020015181526020018581526020018d60200151760a70c3c40a64e6c51999090b65f67d924000000000000061181191906152a5565b81526020018d60000151760a70c3c40a64e6c51999090b65f67d924000000000000061183d91906152a5565b8152508e898151811061185257611852615181565b602002602001018190525061166f565b8e51880361187b57505050505050505050505050915091565b611231565b6060600080835160208501865af43d6040519250601f19601f6020830101168301604052808352806000602085013e816118be57806000803e806000fd5b505092915050565b6118ff6040518060c001604052806000815260200160008152602001600081526020016000815260200160008152602001600081525090565b600061192d7f000000000000000000000000000000000000000000000000000000000000000085600161326c565b9050600061195d7f000000000000000000000000000000000000000000000000000000000000000085600061326c565b8284526020840181905290506b033b2e3c9fd0803ce80000008810156119a2576119898887848461340f565b60a0870152608086015260608501526040840152611a03565b6119ee6119c689760a70c3c40a64e6c51999090b65f67d92400000000000006152a5565b6119e789760a70c3c40a64e6c51999090b65f67d92400000000000006152a5565b838561340f565b608087015260a0860152604085015260608401525b505095945050505050565b60008054600180549060ff82901c9003611a58576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c7636004820152602401610599565b611a628282612bbd565b835173ffffffffffffffffffffffffffffffffffffffff16611a82573384525b611b39604051806101600160405280600073ffffffffffffffffffffffffffffffffffffffff168152602001600073ffffffffffffffffffffffffffffffffffffffff16815260200160008152602001600073ffffffffffffffffffffffffffffffffffffffff168152602001600073ffffffffffffffffffffffffffffffffffffffff16815260200160008152602001600081526020016000815260200160001515815260200160008152602001606081525090565b8615611bfc5773ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000811660208301527f00000000000000000000000000000000000000000000000000000000000000001681527f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000870281611bf157611bf1615276565b046040820152611cb5565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000811660208301527f00000000000000000000000000000000000000000000000000000000000000001681527f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000870281611cae57611cae615276565b0460408201525b611cc3816040015187613621565b6000611ccf8484612c48565b90503415611d7c57863414611d14576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c73f6004820152602401610599565b815173ffffffffffffffffffffffffffffffffffffffff1673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee14611d7c576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c7406004820152602401610599565b6201ffff600284901c16607f601385901c1681026305f5e1000360e0840152620f424081900360c0840152604080516080810182526000808252602082018190529181018290526060810182905260018087169387821c909116929091611e126040518060c001604052806000815260200160008152602001600081526020016000815260200160008152602001600081525090565b85600103611e97576000611e3d886080015189604001518a606001518b60a001518c60e00151612a82565b90508e15611e6c5780516020808301516040808501516060808701519089015290870152908501528352611e95565b602080820151825160608085015160408087015192890192909252908701529185019190915283525b505b84600103611f48576000611ec3886080015189604001518a606001518b60c001518c61010001516118c6565b90508e15611f0657805160208083015160408085015160608087015160808089015160a0808b0151908c0152908a01529088015290860152908401528252611f46565b602080820151825160608085015160408087015160a0808901516080808b0151928c0192909252908a015292880192909252908601529184019190915282525b505b6002816080015183604001510181611f6257611f62615276565b0488604001511115611fa4576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c7656004820152602401610599565b856001148015611fb45750846001145b15611fe157611fda8860400151836060015184604001518460a0015185608001516136a1565b6101208901525b8761012001518860400151138015611ffe57506000886101200151135b156120495761012088015160408901518d5173ffffffffffffffffffffffffffffffffffffffff90811660608c01528e511660808b0152909650600095508690039350849250612130565b856001148015612057575084155b8061206b5750876040015188610120015112155b156120a35760408801518c5173ffffffffffffffffffffffffffffffffffffffff1660608a0152955060009450849350839250612130565b851580156120b15750846001145b806120c25750600088610120015113155b156120fa5760408801518c5173ffffffffffffffffffffffffffffffffffffffff1660808a0152600096508695509350849250612130565b6040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c7416004820152602401610599565b85156121cd57612166620f42408960c001518861214d919061525f565b61215791906152a5565b83604001518460600151613728565b94508d61219f5761219a85836020015161218091906151df565b835161218d9089906152b9565b896020015161271061374a565b6121cd565b81516121cd906121b09088906152b9565b8684602001516121c091906151df565b89602001516127106137b7565b831561225557612203620f42408960c00151866121ea919061525f565b6121f491906152a5565b82608001518360a00151613728565b92508d6122325761222d83826060015161221d91906151df565b85836040015161218d91906152b9565b612255565b61225584826040015161224591906152b9565b8483606001516121c091906151df565b6305f5e1008860e001518761226a919061525f565b61227491906152a5565b95506305f5e1008860e001518561228b919061525f565b61229591906152a5565b935083861115612333578d6122e9578482606001516122b491906151df565b8683604001516122c491906152b9565b6122da906b033b2e3c9fd0803ce800000061525f565b6122e491906152a5565b612329565b8582604001516122f991906152b9565b85836060015161230991906151df565b61231f906b033b2e3c9fd0803ce800000061525f565b61232991906152a5565b60a08901526123c3565b8d61237d57828160a0015161234891906151df565b84826080015161235891906152b9565b61236e906b033b2e3c9fd0803ce800000061525f565b61237891906152a5565b6123bd565b83816080015161238d91906152b9565b838260a0015161239d91906151df565b6123b3906b033b2e3c9fd0803ce800000061525f565b6123bd91906152a5565b60a08901525b8d15612524577f00000000000000000000000000000000000000000000000000000000000000006124147f00000000000000000000000000000000000000000000000000000000000000008861525f565b61241e91906152a5565b95507f000000000000000000000000000000000000000000000000000000000000000061246b7f00000000000000000000000000000000000000000000000000000000000000008661525f565b61247591906152a5565b93507f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000008602816124c8576124c8615276565b0494507f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000084028161251c5761251c615276565b04925061267b565b7f000000000000000000000000000000000000000000000000000000000000000061256f7f00000000000000000000000000000000000000000000000000000000000000008861525f565b61257991906152a5565b95507f00000000000000000000000000000000000000000000000000000000000000006125c67f00000000000000000000000000000000000000000000000000000000000000008661525f565b6125d091906152a5565b93507f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000086028161262357612623615276565b0494507f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000084028161267757612677615276565b0492505b8b518386019b5073ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff2153016126f3576040517fb3bfda99000000000000000000000000000000000000000000000000000000008152600481018c9052602401610599565b8b602001518b1015612735576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c7426004820152602401610599565b8c8c604001513360405160200161277593929190928352901515602083015273ffffffffffffffffffffffffffffffffffffffff16604082015260600190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152919052610140890152875173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169063ad967e15903490896127f5896152cc565b6000808f61014001516040518863ffffffff1660e01b815260040161281f96959493929190615304565b604080518083038185885af115801561283c573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190612861919061511b565b50507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663ad967e158960200151876128ae906152cc565b60608c015160808d0151604080516000815260208101918290527fffffffff0000000000000000000000000000000000000000000000000000000060e088901b169091526129059493928a92909160248101615304565b60408051808303816000875af1158015612923573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612947919061511b565b5050608e89901c633fffffff169550851561297b578d1515610100890181905260a089015161297b9188916001919061381e565b8d6129b7576129b26103ff60e48b901c167f00000000000000000000000000000000000000000000000000000000000000006139c5565b6129e9565b6129e96103ff60ee8b901c167f00000000000000000000000000000000000000000000000000000000000000006139c5565b6129fc8860a0015188602001518c613adb565b6000819055507fdc004dbca4ef9c966218431ee5d9133d337ad018dd5b5c5493722803f75c64f78e8e8d8f60000151604051612a69949392919093151584526020840192909252604083015273ffffffffffffffffffffffffffffffffffffffff16606082015260800190565b60405180910390a1505050505050505050509392505050565b612aad6040518060800160405280600081526020016000815260200160008152602001600081525090565b6000612adb7f000000000000000000000000000000000000000000000000000000000000000085600161326c565b90506000612b0b7f000000000000000000000000000000000000000000000000000000000000000085600061326c565b90506b033b2e3c9fd0803ce8000000881015612b3c57612b2d88888484613fa0565b60608501526040840152612b93565b612b88612b6089760a70c3c40a64e6c51999090b65f67d92400000000000006152a5565b612b8188760a70c3c40a64e6c51999090b65f67d92400000000000006152a5565b8385613fa0565b604085015260608401525b81835260208301819052604083018051909201909152606082018051909101905295945050505050565b81600116600103612bfe576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c7396004820152602401610599565b80600316600003612c3f576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c73d6004820152602401610599565b50600117600055565b612c976040518061012001604052806000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b600060f883901c600116600003612d7857633fffffff607084901c16905080600003612cd7575063ffffffff605984901c1660ff605185901c161b612d85565b612d017f0000000000000000000000000000000000000000000000000000000000000000826140ad565b73ffffffffffffffffffffffffffffffffffffffff1663f763f7a66040518163ffffffff1660e01b81526004016020604051808303816000875af1158015612d4d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d719190615102565b9050612d85565b612d828484614519565b90505b63ffffffff603185901c1660ff602986901c161b601b84901c620fffff90811690602f86901c166001601a87901c81169003612dce57612dc682828861460e565b975090925090505b81620f424003620f4240850281612de757612de7615276565b049150620f42409081038402046000620fffff604488901c1615612eed57604487901c6103ff90811690604e89901c16605889901c62ffffff16600160438b901c81169003612e4357612e3b83838361469f565b919450925090505b6103e888870384820302048801871115612e985760798b901c6401ffffffff16420381811015612e8a5781818a89030281612e8057612e80615276565b0489019850612e8e565b8698505b6001945050612ee9565b6103e885890383820302048803871015612ee95760798b901c6401ffffffff16420381811015612edf578181878b030281612ed557612ed5615276565b0489039850612ee3565b8598505b60019450505b5050505b620fffff60b488901c1660ff60ac89901c161b80861115612f145780955060019150612f38565b50620fffff60d088901c1660ff60c889901c161b80861015612f3857809550600191505b8115612fa157620fffff601b89901c169350620fffff602f89901c169250601a88901c600116600103612f7857612f7084848a61460e565b995090945092505b83620f424003620f4240870281612f9157612f91615276565b049350620f424083810387020492505b8487526020870186905260408701849052606087018390526f4b3b4ca85a86c47a098a224000000000841015612fe657612fdc83850261470e565b6080880152613015565b613005670de0b6b3a76400008404670de0b6b3a764000086040261470e565b670de0b6b3a76400000260808801525b6040517fb5c736e40000000000000000000000000000000000000000000000000000000081527f000000000000000000000000000000000000000000000000000000000000000060048201526130ed907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063b5c736e4906024015b602060405180830381865afa1580156130c4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130e89190615102565b6147c0565b60c089015260a08801526040517fb5c736e40000000000000000000000000000000000000000000000000000000081527f0000000000000000000000000000000000000000000000000000000000000000600482015261318d907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063b5c736e4906024016130a7565b61010089015260e08801525094979650505050505050565b60006040517f23b872dd00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8516600482015273ffffffffffffffffffffffffffffffffffffffff841660248201528260448201526020600060648360008a5af13d15601f3d1160016000511416171691505080613265576040517fdee51a8a000000000000000000000000000000000000000000000000000000008152620115596004820152602401610599565b5050505050565b6040517fb5c736e400000000000000000000000000000000000000000000000000000000815260048101849052600090819073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169063b5c736e490602401602060405180830381865afa1580156132fc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133209190615102565b905067ffffffffffffffff600182901c16915060ff8216600883901c901b9150806001166001036133585764e8d4a510008285020491505b826133b4577f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000008302816133ae576133ae615276565b04610806565b7f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000083028161340557613405615276565b0495945050505050565b6000808080806b06765c793fa10079d0000000613438876b033b2e3c9fd0803ce800000061525f565b6134428b8a61525f565b61344c91906153b1565b61345691906153d8565b90506000613464878961525f565b905074446c3b15f9926687d2c40534fdb56400000000000081106134a857886134996b033b2e3c9fd0803ce8000000836152a5565b6134a3919061525f565b6134c9565b6b033b2e3c9fd0803ce80000006134bf8a8361525f565b6134c991906152a5565b90506134e76134d88380615440565b6134e290836152b9565b61470e565b6134f1908361548c565b94506134fd898961525f565b613513866b033b2e3c9fd0803ce800000061525f565b61351d91906151df565b9250620f4240831015613560576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c76d6004820152602401610599565b6a084595161401484a0000008510156135a5578261357e868061525f565b613594906b033b2e3c9fd0803ce800000061525f565b61359e91906152a5565b92506135d2565b6135bb6b033b2e3c9fd0803ce8000000846152a5565b6135c5868061525f565b6135cf91906152a5565b92505b87856135de828661525f565b6135e891906152a5565b6135f291906151df565b93506135fe87846152b9565b613608888661525f565b61361291906152a5565b95505050945094509450949050565b620f424082108061363d57506bffffffffffffffffffffffff82115b806136485750606481105b8061366257506fffffffffffffffffffffffffffffffff81115b1561369d576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c7696004820152602401610599565b5050565b6000806136c36136b1868861525f565b6134e290670de0b6b3a764000061525f565b905060006136d46136b1858761525f565b90506136e081836152b9565b6136ea828861525f565b6136f4848b61525f565b6136fe858861525f565b61370891906152b9565b61371291906153b1565b61371c91906153d8565b98975050505050505050565b600083820283850180828161373f5761373f615276565b049695505050505050565b613754818361525f565b61376a846b033b2e3c9fd0803ce800000061525f565b61377491906152a5565b8410156137b1576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c73e6004820152602401610599565b50505050565b6137cd816b033b2e3c9fd0803ce800000061525f565b6137d7838661525f565b6137e191906152a5565b8310156137b1576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c73e6004820152602401610599565b6138487f0000000000000000000000000000000000000000000000000000000000000000856140ad565b6040517fd1f9be4e00000000000000000000000000000000000000000000000000000000815260048101859052831515602482015273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000811660448301527f00000000000000000000000000000000000000000000000000000000000000008116606483015260848201849052919091169063d1f9be4e9060a4016020604051808303816000875af1925050508015613950575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820190925261394d918101906150e5565b60015b61398a573d80801561397e576040519150601f19603f3d011682016040523d82523d6000602084013e613983565b606091505b50506137b1565b80613265576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c7446004820152602401610599565b6103e882101561369d576139da82600a61525f565b6040517fb5c736e40000000000000000000000000000000000000000000000000000000081526004810183905290925060009073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169063b5c736e490602401602060405180830381865afa158015613a6b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a8f9190615102565b601e1c613fff16905082811115613ad6576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c7436004820152602401610599565b505050565b600080613af3607984901c6401ffffffff16426151df565b9050600080600083600003613bdb5763ffffffff605987901c1660ff605188901c161b92506305f5e1006305f5e0ff840204871080613b3d57506305f5e1006305f5e10184020487115b15613b78576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c76c6004820152602401610599565b63ffffffff600987901c1660ff600188901c161b9250613b9883896149ce565b506029613baa89602060086000614a70565b901b867ffffffffffffffffffffffffffffffffffffffffffffe0000000001ffffffffff1617945050505050613f99565b60c386901c600116600003613c755763ffffffff603187901c1660ff602988901c161b9250613c0a83896149ce565b504260791b6051613c2089602060086000614a70565b901b6029613c338b602060086000614a70565b7ffffffffffffffffffffffffffc000000000000000000000000000000000000018a166501fffffffffe60288c901c1617911b1717179450613f999350505050565b63ffffffff600987901c811660ff600189901c81169190911b9450603188901c909116602988901c9091161b91506000613caf838a6149ce565b905082670de0b6b3a7640000850281613cca57613cca615276565b04670de0b6b3a76400000390506000811215613d0a57600166b1a2bc2ec50000623fffff836000030281613d0057613d00615276565b04901b9150613d23565b600166b1a2bc2ec50000623fffff830204901b60011791505b623fffff851115613d3557623fffff94505b609a87901c623fffff16935060b087901c60071660b388901c61ffff166101ff861115613e70578115613e015760299390931b600986901b1792613d788261522a565b9150613d8582602061525f565b600082815260066020526040902080549186901b90911790558115613db457613dad8261522a565b9150613f13565b600791507f00000000000000000000000000000000000000000000000000000000000000008160010181613dea57613dea615276565b066000818152600660205260408120559050613f13565b6000818152600660208190526040909120805460099690961b958617905591507f00000000000000000000000000000000000000000000000000000000000000006001820181613e5357613e53615276565b06600081815260066020526040902060e988901b90559050613f13565b85600985901b1793506007821015613ea5576000818152600660209081526040909120805491840286901b9091179055613ebb565b600081815260066020526040902060e085901b90555b8115613eca57613dad8261522a565b600791507f00000000000000000000000000000000000000000000000000000000000000008160010181613f0057613f00615276565b0660008181526006602052604081205590505b8a955089945088935060b381901b60b083901b609a89901b4260791b6051613f408a602060086000614a70565b901b6029613f538c602060086000614a70565b901b600164ffffffffff60298c901c16901b8a7ffffffffffffffff800000000000000000000000000000000000000000000000116171717171717179750505050505050505b9392505050565b60008080613fae87876151df565b90506000613fbd82600261525f565b613fd3866b033b2e3c9fd0803ce800000061525f565b613fdd888b61525f565b613fe791906152b9565b613ff191906152a5565b90506000613fff868861525f565b905074446c3b15f9926687d2c40534fdb56400000000000081106140425761402783826152a5565b61403d906b033b2e3c9fd0803ce800000061525f565b614063565b82614059826b033b2e3c9fd0803ce800000061525f565b61406391906152a5565b90506140726134d8838061525f565b61407c90836152b9565b94506b033b2e3c9fd0803ce80000006140958a8761525f565b61409f91906152a5565b935050505094509492505050565b60006060826000036140c3576000915050614513565b607f831161418c576040517fd60000000000000000000000000000000000000000000000000000000000000060208201527f940000000000000000000000000000000000000000000000000000000000000060218201527fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606086901b16602282015260f884901b7fff000000000000000000000000000000000000000000000000000000000000001660368201526037015b6040516020818303038152906040529050614508565b60ff8311614269576040517fd70000000000000000000000000000000000000000000000000000000000000060208201527f940000000000000000000000000000000000000000000000000000000000000060218201527fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606086901b1660228201527f8100000000000000000000000000000000000000000000000000000000000000603682015260f884901b7fff00000000000000000000000000000000000000000000000000000000000000166037820152603801614176565b61ffff8311614347576040517fd80000000000000000000000000000000000000000000000000000000000000060208201527f940000000000000000000000000000000000000000000000000000000000000060218201527fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606086901b1660228201527f820000000000000000000000000000000000000000000000000000000000000060368201527fffff00000000000000000000000000000000000000000000000000000000000060f085901b166037820152603901614176565b62ffffff8311614426576040517fd90000000000000000000000000000000000000000000000000000000000000060208201527f940000000000000000000000000000000000000000000000000000000000000060218201527fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606086901b1660228201527f830000000000000000000000000000000000000000000000000000000000000060368201527fffffff000000000000000000000000000000000000000000000000000000000060e885901b166037820152603a01614176565b6040517fda0000000000000000000000000000000000000000000000000000000000000060208201527f940000000000000000000000000000000000000000000000000000000000000060218201527fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606086901b1660228201527f840000000000000000000000000000000000000000000000000000000000000060368201527fffffffff0000000000000000000000000000000000000000000000000000000060e085901b166037820152603b0160405160208183030381529060405290505b805160209091012090505b92915050565b60405160248101839052604481018290526000906145fb907f0000000000000000000000000000000000000000000000000000000000000000907f2f850fb300000000000000000000000000000000000000000000000000000000906064015b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152611880565b806020019051810190613f999190615102565b6040516024810184905260448101839052606481018290526000908190819061467d907f0000000000000000000000000000000000000000000000000000000000000000907f0d7cf0ab0000000000000000000000000000000000000000000000000000000090608401614579565b80602001905181019061469091906154ac565b92509250925093509350939050565b6040516024810184905260448101839052606481018290526000908190819061467d907f0000000000000000000000000000000000000000000000000000000000000000907fe8518a660000000000000000000000000000000000000000000000000000000090608401614579565b60b5817101000000000000000000000000000000000081106147355760409190911b9060801c5b690100000000000000000081106147515760209190911b9060401c5b6501000000000081106147695760109190911b9060201c5b6301000000811061477f5760089190911b9060101c5b62010000010260121c80820401600190811c80830401811c80830401811c80830401811c80830401811c80830401811c80830401901c908190048111900390565b67ffffffffffffffff605b82901c811690609b83901c168115806147e2575080155b1561481e576040517fd50d7512000000000000000000000000000000000000000000000000000000008152620111716004820152602401610599565b61ffff8316603a84901c6401ffffffff16428181039160ea87901c617fff16911480614848575082155b806148535750806001145b1561486057505050915091565b64496cebb80084840283020484019350617fff60db87901c1692508260010361488b57505050915091565b826001166001036148e05760019290921c91826c7e37be2022c0914b2680000000816148b9576148b9615276565b049250612710601e87901c613fff166b033b2e3c9fd0803ce800000085010204925061490d565b60019290921c916305f5e100601e87901c613fff166127108501026b033b2e3c9fd0803ce8000000020492505b806001166001036149445760011c61271081016b033b2e3c9fd0803ce800000082028161493c5761493c615276565b04905061497a565b60011c61271081016b033b2e3c9fd0803ce800000082028161496857614968615276565b046b033b2e3c9fd0803ce80000000390505b760a70c3c40a64e6c51999090b65f67d92400000000000008382026127100261ffff881691900402601087901c613fff16612710030292506801b5a660ea44b8000085840283020485019450505050915091565b6000816149e3670de0b6b3a76400008561525f565b6149ed91906152a5565b6149ff90670de0b6b3a76400006153b1565b905066b1a2bc2ec50000811380614a3557507fffffffffffffffffffffffffffffffffffffffffffffffffff4e5d43d13b000081125b15614513576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c7606004820152602401610599565b600080856fffffffffffffffffffffffffffffffff811115614a935760809150811c5b67ffffffffffffffff811115614aab576040918201911c5b63ffffffff811115614abf576020918201911c5b61ffff811115614ad1576010918201911c5b60ff811115614ae2576008918201911c5b600f811115614af3576004918201911c5b6003811115614b04576002918201911c5b6001811115614b14576001820191505b8015614b21576001820191505b5084811015614b2d5750835b848103905085811c60008211841615614b7c57600181019050806001871b03614b7c57506001908101907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff86011b5b6001851b8210614b8b57600080fd5b90931b909201949350505050565b600080600080600060a08688031215614bb157600080fd5b505083359560208501359550604085013594606081013594506080013592509050565b60c081016145138284805182526020810151602083015260408101516040830152606081015160608301526080810151608083015260a081015160a08301525050565b8151815260208083015190820152604080830151908201526060808301519082015260808101614513565b73ffffffffffffffffffffffffffffffffffffffff81168114614c6457600080fd5b50565b60008060008060808587031215614c7d57600080fd5b8435935060208501359250604085013591506060850135614c9d81614c42565b939692955090935050565b8015158114614c6457600080fd5b60008060008060808587031215614ccc57600080fd5b8435614cd781614ca8565b935060208501359250604085013591506060850135614c9d81614c42565b60008060008060808587031215614d0b57600080fd5b8435935060208501359250604085013591506060850135614c9d81614ca8565b60008060008060608587031215614d4157600080fd5b8435614d4c81614c42565b935060208501359250604085013567ffffffffffffffff80821115614d7057600080fd5b818701915087601f830112614d8457600080fd5b813581811115614d9357600080fd5b886020828501011115614da557600080fd5b95989497505060200194505050565b600060208284031215614dc657600080fd5b5035919050565b81518152602080830151610240830191614dfe9084018273ffffffffffffffffffffffffffffffffffffffff169052565b506040830151614e26604084018273ffffffffffffffffffffffffffffffffffffffff169052565b506060830151614e84606084018273ffffffffffffffffffffffffffffffffffffffff808251168352806020830151166020840152806040830151166040840152806060830151166060840152806080830151166080840152505050565b506080830151610100614eae8185018373ffffffffffffffffffffffffffffffffffffffff169052565b60a08501519150610120614ed98186018473ffffffffffffffffffffffffffffffffffffffff169052565b60c08601519250610140614f048187018573ffffffffffffffffffffffffffffffffffffffff169052565b60e0870151610160878101919091529287015161018080880191909152918701516101a080880191909152908701516101c0870152918601516101e08601528501516102008501529093015161022090920191909152919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60006020808385031215614fa157600080fd5b823567ffffffffffffffff80821115614fb957600080fd5b818501915085601f830112614fcd57600080fd5b813581811115614fdf57614fdf614f5f565b8060051b6040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0603f8301168101818110858211171561502257615022614f5f565b60405291825284820192508381018501918883111561504057600080fd5b938501935b8285101561371c57843584529385019392850192615045565b604080825283519082018190526000906020906060840190828701845b828110156150d6576150c3848351805182526020810151602083015260408101516040830152606081015160608301526080810151608083015260a081015160a08301525050565b60c093909301929084019060010161507b565b50505092019290925292915050565b6000602082840312156150f757600080fd5b8151613f9981614ca8565b60006020828403121561511457600080fd5b5051919050565b6000806040838503121561512e57600080fd5b505080516020909101519092909150565b60008060006060848603121561515457600080fd5b83359250602084013561516681614ca8565b9150604084013561517681614c42565b809150509250925092565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b81810381811115614513576145136151b0565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203615223576152236151b0565b5060010190565b600081615239576152396151b0565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b8082028115828204841417614513576145136151b0565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000826152b4576152b4615276565b500490565b80820180821115614513576145136151b0565b60007f800000000000000000000000000000000000000000000000000000000000000082036152fd576152fd6151b0565b5060000390565b600073ffffffffffffffffffffffffffffffffffffffff8089168352602088818501528760408501528187166060850152818616608085015260c060a0850152845191508160c085015260005b8281101561536d5785810182015185820160e001528101615351565b5050600060e0828501015260e07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f830116840101915050979650505050505050565b81810360008312801583831316838312821617156153d1576153d16151b0565b5092915050565b6000826153e7576153e7615276565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83147f80000000000000000000000000000000000000000000000000000000000000008314161561543b5761543b6151b0565b500590565b808202600082127f800000000000000000000000000000000000000000000000000000000000000084141615615478576154786151b0565b8181058314821517614513576145136151b0565b80820182811260008312801582168215821617156118be576118be6151b0565b6000806000606084860312156154c157600080fd5b835192506020840151915060408401519050925092509256fea2646970667358221220030a7fecc110b38b34765d7248652e1fcb76dfaaf261eed5e38b5d920a127a9a64736f6c63430008150033000000000000000000000000000000000000000000000000000000000000000b00000000000000000000000052aa899454998be5b000ad077a46bbe360f4e49700000000000000000000000091716c4eda1fb55e84bf8b4c7085f84285c190850000000000000000000000004ecb836267b8b6473b34e244c82a6aa0749ffb9b000000000000000000000000780d3c70d583dd8bd894abb53952b868916f69d80000000000000000000000007eb692a3fa34eb8e724c4809cdc4293b0a455e8c000000000000000000000000bfa9093d4ee238adb15ea40bfa90a088c1d629d30000000000000000000000005be9c322a0b38458b8d36f9a91eb7aca5df255430000000000000000000000004ec7b668baf70d4a4b0fc7941a7708a07b6d45be0000000000000000000000006f40d4a6237c257fff2db00fa0510deeecd303eb000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee76eefde27cef0839f6721813b8a946733d2dc6c96617e06f48ce36e5ebf61a727fc1b849f2080d40c18fc3be2117724aa74d4d24fd5a693f8ad10a8bb0d39853c6aa0a1ebc72fe4114f79538b07fe13247dd00fa4cd45b15bdf0806962814311ec31c709fa552a64d5bf7b00e0effa57c1f425f5596528967d700b0acd6ed68b7e6352010c06d4d4e841dfdca419ce23a0173412563e7ab6bebf6cce56e1202aa1829a9003092132f585b6ccdd167c19fe9774dbdea4260287e8a8e8ca8185d70000000000000000000000000000000000000000000000000000000000000400

Deployed Bytecode

0x6080604052600436106101845760003560e01c80636560abaa116100d6578063be17c79c1161007f578063e27203cd11610059578063e27203cd1461027f578063e980e1eb146103b9578063f4b9a3fb146103c7576101bd565b8063be17c79c14610378578063d331bef7146102b4578063d811b2ce1461038b576101bd565b8063ad207501116100b0578063ad20750114610317578063b5c736e414610337578063b7791bf214610356576101bd565b80636560abaa146102e25780636876698114610271578063916cef4e14610302576101bd565b806330acd6fd116101385780634d9036de116101125780634d9036de146102d45780635b3d38d7146102d4578063653295aa1461025e576101bd565b806330acd6fd1461027157806335f0df981461027f5780634c89bfd4146102b4576101bd565b8063242011d511610169578063242011d51461021d5780632668dfaa1461024b578063286f0e611461025e576101bd565b806305d455a9146101c55780631595cbd3146101fb576101bd565b366101bd577fffffffff0000000000000000000000000000000000000000000000000000000060003516156101bb576101bb6103fb565b005b6101bb6103fb565b3480156101d157600080fd5b506101e56101e0366004614b99565b61067d565b6040516101f29190614bd4565b60405180910390f35b34801561020757600080fd5b506102106106cd565b6040516101f29190614c17565b34801561022957600080fd5b5061023d610238366004614c67565b610790565b6040519081526020016101f2565b61023d610259366004614cb6565b61080f565b61023d61026c366004614cb6565b61084f565b61023d610238366004614cf5565b34801561028b57600080fd5b5061029f61029a366004614c67565b6108b2565b604080519283526020830191909152016101f2565b3480156102c057600080fd5b5061023d6102cf366004614c67565b610936565b61029f61029a366004614cf5565b3480156102ee57600080fd5b506102106102fd366004614b99565b610999565b34801561030e57600080fd5b506101bb6109d1565b34801561032357600080fd5b506101bb610332366004614d2b565b610a7c565b34801561034357600080fd5b5061023d610352366004614db4565b5490565b34801561036257600080fd5b5061036b610c8c565b6040516101f29190614dcd565b61023d610386366004614cb6565b611018565b34801561039757600080fd5b506103ab6103a6366004614f8e565b611058565b6040516101f292919061505e565b61023d6102cf366004614cf5565b3480156103d357600080fd5b5061023d7f000000000000000000000000000000000000000000000000000000000000000b81565b6040517f4502d0630000000000000000000000000000000000000000000000000000000081523360048201527f00000000000000000000000091716c4eda1fb55e84bf8b4c7085f84285c1908573ffffffffffffffffffffffffffffffffffffffff1690634502d06390602401602060405180830381865afa158015610485573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104a991906150e5565b8061056357506040517ffbeeca2c0000000000000000000000000000000000000000000000000000000081523060048201523360248201527f00000000000000000000000091716c4eda1fb55e84bf8b4c7085f84285c1908573ffffffffffffffffffffffffffffffffffffffff169063fbeeca2c90604401602060405180830381865afa15801561053f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061056391906150e5565b6105a2576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c73a60048201526024015b60405180910390fd5b600054600180821690036105e6576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c7396004820152602401610599565b806001176000819055506106517f000000000000000000000000780d3c70d583dd8bd894abb53952b868916f69d86000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061188092505050565b5050600080547ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe169055565b6106b66040518060c001604052806000815260200160008152602001600081526020016000815260200160008152602001600081525090565b6106c386868686866118c6565b9695505050505050565b6106f86040518060800160405280600081526020016000815260200160008152602001600081525090565b7f000000000000000000000000000000000000000000000000000000000000000181527f00000000000000000000000000000000000000000000000000000000000f424060208201527f000000000000000000000000000000000000000000000000000000000000000160408201527f00000000000000000000000000000000000000000000000000000000000f4240606082015290565b60006107f37f000000000000000000000000bfa9093d4ee238adb15ea40bfa90a088c1d629d36000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061188092505050565b8060200190518101906108069190615102565b95945050505050565b6000610806858560405180606001604052808673ffffffffffffffffffffffffffffffffffffffff16815260200187815260200160001515815250611a0e565b60006107f37f0000000000000000000000005be9c322a0b38458b8d36f9a91eb7aca5df255436000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061188092505050565b6000806109167f0000000000000000000000005be9c322a0b38458b8d36f9a91eb7aca5df255436000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061188092505050565b806020019051810190610929919061511b565b9150915094509492505050565b60006107f37f0000000000000000000000007eb692a3fa34eb8e724c4809cdc4293b0a455e8c6000368080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061188092505050565b6109c46040518060800160405280600081526020016000815260200160008152602001600081525090565b6106c38686868686612a82565b6000546001546109e18282612bbd565b60006109f1600054600154612c48565b604080517f23bfdd3b0000000000000000000000000000000000000000000000000000000081528251600482015260208301516024820152908201516044820152606082015160648201526080820151608482015260a082015160a482015260c082015160c482015260e082015160e482015261010082015161010482015290915061012401610599565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000052aa899454998be5b000ad077a46bbe360f4e4971614610aef576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c75d6004820152602401610599565b600054600116600003610b32576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c75e6004820152602401610599565b60608114610b70576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c7746004820152602401610599565b60008080610b808486018661513f565b92509250925085831015610bc4576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c7756004820152602401610599565b8115610c57576040517f9410ae8800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff888116600483015260248201859052821690639410ae8890604401600060405180830381600087803b158015610c3a57600080fd5b505af1158015610c4e573d6000803e3d6000fd5b50505050610c83565b610c8387827f00000000000000000000000052aa899454998be5b000ad077a46bbe360f4e497866131a5565b50505050505050565b610d29604080516101c08101825260008082526020808301829052828401829052835160a08101855282815290810182905292830181905260608381018290526080840191909152909190820190815260006020820181905260408201819052606082018190526080820181905260a0820181905260c0820181905260e08201819052610100820181905261012082018190526101409091015290565b7f000000000000000000000000000000000000000000000000000000000000000b815273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000052aa899454998be5b000ad077a46bbe360f4e49781166020808401919091527f00000000000000000000000091716c4eda1fb55e84bf8b4c7085f84285c1908582166040808501919091527f0000000000000000000000006f40d4a6237c257fff2db00fa0510deeecd303eb831660a08501527f000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee831660c0850152606080850180517f0000000000000000000000004ecb836267b8b6473b34e244c82a6aa0749ffb9b8616905280517f000000000000000000000000780d3c70d583dd8bd894abb53952b868916f69d8861694019390935282517f0000000000000000000000007eb692a3fa34eb8e724c4809cdc4293b0a455e8c851692019190915281517f000000000000000000000000bfa9093d4ee238adb15ea40bfa90a088c1d629d38416910152517f0000000000000000000000005be9c322a0b38458b8d36f9a91eb7aca5df2554382166080918201527f0000000000000000000000004ec7b668baf70d4a4b0fc7941a7708a07b6d45be909116908201527f76eefde27cef0839f6721813b8a946733d2dc6c96617e06f48ce36e5ebf61a7260e08201527f7fc1b849f2080d40c18fc3be2117724aa74d4d24fd5a693f8ad10a8bb0d398536101008201527fc6aa0a1ebc72fe4114f79538b07fe13247dd00fa4cd45b15bdf08069628143116101208201527fec31c709fa552a64d5bf7b00e0effa57c1f425f5596528967d700b0acd6ed68b6101408201527f7e6352010c06d4d4e841dfdca419ce23a0173412563e7ab6bebf6cce56e1202a6101608201527fa1829a9003092132f585b6ccdd167c19fe9774dbdea4260287e8a8e8ca8185d76101808201527f00000000000000000000000000000000000000000000000000000000000004006101a082015290565b6000610806858560405180606001604052808673ffffffffffffffffffffffffffffffffffffffff16815260200187815260200160011515815250611a0e565b6060600061108e6040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b6000805490600160c383901c1690036110d7576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c77a6004820152602401610599565b845167ffffffffffffffff8111156110f1576110f1614f5f565b60405190808252806020026020018201604052801561115b57816020015b6111486040518060c001604052806000815260200160008152602001600081526020016000815260200160008152602001600081525090565b81526020019060019003908161110f5790505b5093506000806000808860008151811061117757611177615181565b602090810291909101810151603187901c63ffffffff16602988901c60ff161b808952918801829052600760b088901c811660408a0181905261ffff60b38a901c1660608b0152929950909250889160009182918291116111d95760006111ee565b60608a01516000908152600660205260409020545b60808b01526000804260798c901c6401ffffffff1610156112255761121e60798c901c6401ffffffff16426151df565b9850611231565b61122e836151f2565b92505b8260020361146e578b6040018051611248906151f2565b908190526008036112bc57600060408d0181905260608d0151900361128e577f000000000000000000000000000000000000000000000000000000000000040060608d01525b600660008d60600180516112a19061522a565b90819052815260208101919091526040016000205460808d01525b63ffffffff8c6040015160206112d2919061525f565b60808e0151901c1691508115611438576101ff82169850886000036113ad57600982901c98508b6040015160070361137b57600060408d0181905260608d0151900361133f577f000000000000000000000000000000000000000000000000000000000000040060608d01525b600660008d60600180516113529061522a565b90819052815260208101919091526040016000205460808d0181905263ffffffff1691506113ad565b8b604001805161138a906151f2565b905260408c015163ffffffff906113a290602061525f565b8d60800151901c1691505b50600a81901c623fffff6113c88266b1a2bc2ec5000061525f565b6113d291906152a5565b9050600982901c60011660010361141157670de0b6b3a76400006113f6828861525f565b61140091906152a5565b61140a90876151df565b95506114bc565b670de0b6b3a7640000611424828861525f565b61142e91906152a5565b61140a90876152b9565b6040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c76a6004820152602401610599565b826001036114a857609a8b901c623fffff16985063ffffffff60098c901c1660ff60018d901c161b95506114a1836151f2565b92506114bc565b826000036114bc576114b9836151f2565b92505b6114c6898b6152b9565b9950858c6000015111156114d857858c525b858c6020015110156114ec5760208c018690525b868a1015611547576114fe898761525f565b61150890866152b9565b94508861152c87760a70c3c40a64e6c51999090b65f67d92400000000000006152a5565b611536919061525f565b61154090856152b9565b9350611231565b89611552888b6152b9565b61155c91906151df565b9850611568898761525f565b61157290866152b9565b94508861159687760a70c3c40a64e6c51999090b65f67d92400000000000006152a5565b6115a0919061525f565b6115aa90856152b9565b93506115b687866152a5565b94506115c287856152a5565b93506040518060c001604052808681526020018d6000015181526020018d6020015181526020018581526020018d60200151760a70c3c40a64e6c51999090b65f67d924000000000000061161691906152a5565b81526020018d60000151760a70c3c40a64e6c51999090b65f67d924000000000000061164291906152a5565b8152508e898151811061165757611657615181565b602090810291909101810191909152868d528c018690525b8e5161167a896151f2565b98508810156118625761168d878b6151df565b99508998508e61169e60018a6151df565b815181106116ae576116ae615181565b60200260200101518f89815181106116c8576116c8615181565b60200260200101516116da91906151df565b9650868a1015611756576116ee898761525f565b94508861171287760a70c3c40a64e6c51999090b65f67d92400000000000006152a5565b61171c919061525f565b935088600003611751577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8c52600060208d01525b611862565b89611761888b6152b9565b61176b91906151df565b9850611777898761525f565b94508861179b87760a70c3c40a64e6c51999090b65f67d92400000000000006152a5565b6117a5919061525f565b93506117b187866152a5565b94506117bd87856152a5565b93506040518060c001604052808681526020018d6000015181526020018d6020015181526020018581526020018d60200151760a70c3c40a64e6c51999090b65f67d924000000000000061181191906152a5565b81526020018d60000151760a70c3c40a64e6c51999090b65f67d924000000000000061183d91906152a5565b8152508e898151811061185257611852615181565b602002602001018190525061166f565b8e51880361187b57505050505050505050505050915091565b611231565b6060600080835160208501865af43d6040519250601f19601f6020830101168301604052808352806000602085013e816118be57806000803e806000fd5b505092915050565b6118ff6040518060c001604052806000815260200160008152602001600081526020016000815260200160008152602001600081525090565b600061192d7f7fc1b849f2080d40c18fc3be2117724aa74d4d24fd5a693f8ad10a8bb0d3985385600161326c565b9050600061195d7fec31c709fa552a64d5bf7b00e0effa57c1f425f5596528967d700b0acd6ed68b85600061326c565b8284526020840181905290506b033b2e3c9fd0803ce80000008810156119a2576119898887848461340f565b60a0870152608086015260608501526040840152611a03565b6119ee6119c689760a70c3c40a64e6c51999090b65f67d92400000000000006152a5565b6119e789760a70c3c40a64e6c51999090b65f67d92400000000000006152a5565b838561340f565b608087015260a0860152604085015260608401525b505095945050505050565b60008054600180549060ff82901c9003611a58576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c7636004820152602401610599565b611a628282612bbd565b835173ffffffffffffffffffffffffffffffffffffffff16611a82573384525b611b39604051806101600160405280600073ffffffffffffffffffffffffffffffffffffffff168152602001600073ffffffffffffffffffffffffffffffffffffffff16815260200160008152602001600073ffffffffffffffffffffffffffffffffffffffff168152602001600073ffffffffffffffffffffffffffffffffffffffff16815260200160008152602001600081526020016000815260200160001515815260200160008152602001606081525090565b8615611bfc5773ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee811660208301527f0000000000000000000000006f40d4a6237c257fff2db00fa0510deeecd303eb1681527f00000000000000000000000000000000000000000000000000000000000f42407f0000000000000000000000000000000000000000000000000000000000000001870281611bf157611bf1615276565b046040820152611cb5565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000006f40d4a6237c257fff2db00fa0510deeecd303eb811660208301527f000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1681527f00000000000000000000000000000000000000000000000000000000000f42407f0000000000000000000000000000000000000000000000000000000000000001870281611cae57611cae615276565b0460408201525b611cc3816040015187613621565b6000611ccf8484612c48565b90503415611d7c57863414611d14576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c73f6004820152602401610599565b815173ffffffffffffffffffffffffffffffffffffffff1673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee14611d7c576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c7406004820152602401610599565b6201ffff600284901c16607f601385901c1681026305f5e1000360e0840152620f424081900360c0840152604080516080810182526000808252602082018190529181018290526060810182905260018087169387821c909116929091611e126040518060c001604052806000815260200160008152602001600081526020016000815260200160008152602001600081525090565b85600103611e97576000611e3d886080015189604001518a606001518b60a001518c60e00151612a82565b90508e15611e6c5780516020808301516040808501516060808701519089015290870152908501528352611e95565b602080820151825160608085015160408087015192890192909252908701529185019190915283525b505b84600103611f48576000611ec3886080015189604001518a606001518b60c001518c61010001516118c6565b90508e15611f0657805160208083015160408085015160608087015160808089015160a0808b0151908c0152908a01529088015290860152908401528252611f46565b602080820151825160608085015160408087015160a0808901516080808b0151928c0192909252908a015292880192909252908601529184019190915282525b505b6002816080015183604001510181611f6257611f62615276565b0488604001511115611fa4576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c7656004820152602401610599565b856001148015611fb45750846001145b15611fe157611fda8860400151836060015184604001518460a0015185608001516136a1565b6101208901525b8761012001518860400151138015611ffe57506000886101200151135b156120495761012088015160408901518d5173ffffffffffffffffffffffffffffffffffffffff90811660608c01528e511660808b0152909650600095508690039350849250612130565b856001148015612057575084155b8061206b5750876040015188610120015112155b156120a35760408801518c5173ffffffffffffffffffffffffffffffffffffffff1660608a0152955060009450849350839250612130565b851580156120b15750846001145b806120c25750600088610120015113155b156120fa5760408801518c5173ffffffffffffffffffffffffffffffffffffffff1660808a0152600096508695509350849250612130565b6040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c7416004820152602401610599565b85156121cd57612166620f42408960c001518861214d919061525f565b61215791906152a5565b83604001518460600151613728565b94508d61219f5761219a85836020015161218091906151df565b835161218d9089906152b9565b896020015161271061374a565b6121cd565b81516121cd906121b09088906152b9565b8684602001516121c091906151df565b89602001516127106137b7565b831561225557612203620f42408960c00151866121ea919061525f565b6121f491906152a5565b82608001518360a00151613728565b92508d6122325761222d83826060015161221d91906151df565b85836040015161218d91906152b9565b612255565b61225584826040015161224591906152b9565b8483606001516121c091906151df565b6305f5e1008860e001518761226a919061525f565b61227491906152a5565b95506305f5e1008860e001518561228b919061525f565b61229591906152a5565b935083861115612333578d6122e9578482606001516122b491906151df565b8683604001516122c491906152b9565b6122da906b033b2e3c9fd0803ce800000061525f565b6122e491906152a5565b612329565b8582604001516122f991906152b9565b85836060015161230991906151df565b61231f906b033b2e3c9fd0803ce800000061525f565b61232991906152a5565b60a08901526123c3565b8d61237d57828160a0015161234891906151df565b84826080015161235891906152b9565b61236e906b033b2e3c9fd0803ce800000061525f565b61237891906152a5565b6123bd565b83816080015161238d91906152b9565b838260a0015161239d91906151df565b6123b3906b033b2e3c9fd0803ce800000061525f565b6123bd91906152a5565b60a08901525b8d15612524577f00000000000000000000000000000000000000000000000000000000000000016124147f00000000000000000000000000000000000000000000000000000000000f42408861525f565b61241e91906152a5565b95507f000000000000000000000000000000000000000000000000000000000000000161246b7f00000000000000000000000000000000000000000000000000000000000f42408661525f565b61247591906152a5565b93507f00000000000000000000000000000000000000000000000000000000000000017f00000000000000000000000000000000000000000000000000000000000f42408602816124c8576124c8615276565b0494507f00000000000000000000000000000000000000000000000000000000000000017f00000000000000000000000000000000000000000000000000000000000f424084028161251c5761251c615276565b04925061267b565b7f000000000000000000000000000000000000000000000000000000000000000161256f7f00000000000000000000000000000000000000000000000000000000000f42408861525f565b61257991906152a5565b95507f00000000000000000000000000000000000000000000000000000000000000016125c67f00000000000000000000000000000000000000000000000000000000000f42408661525f565b6125d091906152a5565b93507f00000000000000000000000000000000000000000000000000000000000000017f00000000000000000000000000000000000000000000000000000000000f424086028161262357612623615276565b0494507f00000000000000000000000000000000000000000000000000000000000000017f00000000000000000000000000000000000000000000000000000000000f424084028161267757612677615276565b0492505b8b518386019b5073ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff2153016126f3576040517fb3bfda99000000000000000000000000000000000000000000000000000000008152600481018c9052602401610599565b8b602001518b1015612735576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c7426004820152602401610599565b8c8c604001513360405160200161277593929190928352901515602083015273ffffffffffffffffffffffffffffffffffffffff16604082015260600190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152919052610140890152875173ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000052aa899454998be5b000ad077a46bbe360f4e497169063ad967e15903490896127f5896152cc565b6000808f61014001516040518863ffffffff1660e01b815260040161281f96959493929190615304565b604080518083038185885af115801561283c573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190612861919061511b565b50507f00000000000000000000000052aa899454998be5b000ad077a46bbe360f4e49773ffffffffffffffffffffffffffffffffffffffff1663ad967e158960200151876128ae906152cc565b60608c015160808d0151604080516000815260208101918290527fffffffff0000000000000000000000000000000000000000000000000000000060e088901b169091526129059493928a92909160248101615304565b60408051808303816000875af1158015612923573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612947919061511b565b5050608e89901c633fffffff169550851561297b578d1515610100890181905260a089015161297b9188916001919061381e565b8d6129b7576129b26103ff60e48b901c167f7e6352010c06d4d4e841dfdca419ce23a0173412563e7ab6bebf6cce56e1202a6139c5565b6129e9565b6129e96103ff60ee8b901c167fa1829a9003092132f585b6ccdd167c19fe9774dbdea4260287e8a8e8ca8185d76139c5565b6129fc8860a0015188602001518c613adb565b6000819055507fdc004dbca4ef9c966218431ee5d9133d337ad018dd5b5c5493722803f75c64f78e8e8d8f60000151604051612a69949392919093151584526020840192909252604083015273ffffffffffffffffffffffffffffffffffffffff16606082015260800190565b60405180910390a1505050505050505050509392505050565b612aad6040518060800160405280600081526020016000815260200160008152602001600081525090565b6000612adb7f76eefde27cef0839f6721813b8a946733d2dc6c96617e06f48ce36e5ebf61a7285600161326c565b90506000612b0b7fc6aa0a1ebc72fe4114f79538b07fe13247dd00fa4cd45b15bdf080696281431185600061326c565b90506b033b2e3c9fd0803ce8000000881015612b3c57612b2d88888484613fa0565b60608501526040840152612b93565b612b88612b6089760a70c3c40a64e6c51999090b65f67d92400000000000006152a5565b612b8188760a70c3c40a64e6c51999090b65f67d92400000000000006152a5565b8385613fa0565b604085015260608401525b81835260208301819052604083018051909201909152606082018051909101905295945050505050565b81600116600103612bfe576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c7396004820152602401610599565b80600316600003612c3f576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c73d6004820152602401610599565b50600117600055565b612c976040518061012001604052806000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b600060f883901c600116600003612d7857633fffffff607084901c16905080600003612cd7575063ffffffff605984901c1660ff605185901c161b612d85565b612d017f0000000000000000000000004ec7b668baf70d4a4b0fc7941a7708a07b6d45be826140ad565b73ffffffffffffffffffffffffffffffffffffffff1663f763f7a66040518163ffffffff1660e01b81526004016020604051808303816000875af1158015612d4d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d719190615102565b9050612d85565b612d828484614519565b90505b63ffffffff603185901c1660ff602986901c161b601b84901c620fffff90811690602f86901c166001601a87901c81169003612dce57612dc682828861460e565b975090925090505b81620f424003620f4240850281612de757612de7615276565b049150620f42409081038402046000620fffff604488901c1615612eed57604487901c6103ff90811690604e89901c16605889901c62ffffff16600160438b901c81169003612e4357612e3b83838361469f565b919450925090505b6103e888870384820302048801871115612e985760798b901c6401ffffffff16420381811015612e8a5781818a89030281612e8057612e80615276565b0489019850612e8e565b8698505b6001945050612ee9565b6103e885890383820302048803871015612ee95760798b901c6401ffffffff16420381811015612edf578181878b030281612ed557612ed5615276565b0489039850612ee3565b8598505b60019450505b5050505b620fffff60b488901c1660ff60ac89901c161b80861115612f145780955060019150612f38565b50620fffff60d088901c1660ff60c889901c161b80861015612f3857809550600191505b8115612fa157620fffff601b89901c169350620fffff602f89901c169250601a88901c600116600103612f7857612f7084848a61460e565b995090945092505b83620f424003620f4240870281612f9157612f91615276565b049350620f424083810387020492505b8487526020870186905260408701849052606087018390526f4b3b4ca85a86c47a098a224000000000841015612fe657612fdc83850261470e565b6080880152613015565b613005670de0b6b3a76400008404670de0b6b3a764000086040261470e565b670de0b6b3a76400000260808801525b6040517fb5c736e40000000000000000000000000000000000000000000000000000000081527f7e6352010c06d4d4e841dfdca419ce23a0173412563e7ab6bebf6cce56e1202a60048201526130ed907f00000000000000000000000052aa899454998be5b000ad077a46bbe360f4e49773ffffffffffffffffffffffffffffffffffffffff169063b5c736e4906024015b602060405180830381865afa1580156130c4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130e89190615102565b6147c0565b60c089015260a08801526040517fb5c736e40000000000000000000000000000000000000000000000000000000081527fa1829a9003092132f585b6ccdd167c19fe9774dbdea4260287e8a8e8ca8185d7600482015261318d907f00000000000000000000000052aa899454998be5b000ad077a46bbe360f4e49773ffffffffffffffffffffffffffffffffffffffff169063b5c736e4906024016130a7565b61010089015260e08801525094979650505050505050565b60006040517f23b872dd00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8516600482015273ffffffffffffffffffffffffffffffffffffffff841660248201528260448201526020600060648360008a5af13d15601f3d1160016000511416171691505080613265576040517fdee51a8a000000000000000000000000000000000000000000000000000000008152620115596004820152602401610599565b5050505050565b6040517fb5c736e400000000000000000000000000000000000000000000000000000000815260048101849052600090819073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000052aa899454998be5b000ad077a46bbe360f4e497169063b5c736e490602401602060405180830381865afa1580156132fc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133209190615102565b905067ffffffffffffffff600182901c16915060ff8216600883901c901b9150806001166001036133585764e8d4a510008285020491505b826133b4577f00000000000000000000000000000000000000000000000000000000000f42407f00000000000000000000000000000000000000000000000000000000000000018302816133ae576133ae615276565b04610806565b7f00000000000000000000000000000000000000000000000000000000000f42407f000000000000000000000000000000000000000000000000000000000000000183028161340557613405615276565b0495945050505050565b6000808080806b06765c793fa10079d0000000613438876b033b2e3c9fd0803ce800000061525f565b6134428b8a61525f565b61344c91906153b1565b61345691906153d8565b90506000613464878961525f565b905074446c3b15f9926687d2c40534fdb56400000000000081106134a857886134996b033b2e3c9fd0803ce8000000836152a5565b6134a3919061525f565b6134c9565b6b033b2e3c9fd0803ce80000006134bf8a8361525f565b6134c991906152a5565b90506134e76134d88380615440565b6134e290836152b9565b61470e565b6134f1908361548c565b94506134fd898961525f565b613513866b033b2e3c9fd0803ce800000061525f565b61351d91906151df565b9250620f4240831015613560576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c76d6004820152602401610599565b6a084595161401484a0000008510156135a5578261357e868061525f565b613594906b033b2e3c9fd0803ce800000061525f565b61359e91906152a5565b92506135d2565b6135bb6b033b2e3c9fd0803ce8000000846152a5565b6135c5868061525f565b6135cf91906152a5565b92505b87856135de828661525f565b6135e891906152a5565b6135f291906151df565b93506135fe87846152b9565b613608888661525f565b61361291906152a5565b95505050945094509450949050565b620f424082108061363d57506bffffffffffffffffffffffff82115b806136485750606481105b8061366257506fffffffffffffffffffffffffffffffff81115b1561369d576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c7696004820152602401610599565b5050565b6000806136c36136b1868861525f565b6134e290670de0b6b3a764000061525f565b905060006136d46136b1858761525f565b90506136e081836152b9565b6136ea828861525f565b6136f4848b61525f565b6136fe858861525f565b61370891906152b9565b61371291906153b1565b61371c91906153d8565b98975050505050505050565b600083820283850180828161373f5761373f615276565b049695505050505050565b613754818361525f565b61376a846b033b2e3c9fd0803ce800000061525f565b61377491906152a5565b8410156137b1576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c73e6004820152602401610599565b50505050565b6137cd816b033b2e3c9fd0803ce800000061525f565b6137d7838661525f565b6137e191906152a5565b8310156137b1576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c73e6004820152602401610599565b6138487f0000000000000000000000004ec7b668baf70d4a4b0fc7941a7708a07b6d45be856140ad565b6040517fd1f9be4e00000000000000000000000000000000000000000000000000000000815260048101859052831515602482015273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000006f40d4a6237c257fff2db00fa0510deeecd303eb811660448301527f000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee8116606483015260848201849052919091169063d1f9be4e9060a4016020604051808303816000875af1925050508015613950575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820190925261394d918101906150e5565b60015b61398a573d80801561397e576040519150601f19603f3d011682016040523d82523d6000602084013e613983565b606091505b50506137b1565b80613265576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c7446004820152602401610599565b6103e882101561369d576139da82600a61525f565b6040517fb5c736e40000000000000000000000000000000000000000000000000000000081526004810183905290925060009073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000052aa899454998be5b000ad077a46bbe360f4e497169063b5c736e490602401602060405180830381865afa158015613a6b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a8f9190615102565b601e1c613fff16905082811115613ad6576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c7436004820152602401610599565b505050565b600080613af3607984901c6401ffffffff16426151df565b9050600080600083600003613bdb5763ffffffff605987901c1660ff605188901c161b92506305f5e1006305f5e0ff840204871080613b3d57506305f5e1006305f5e10184020487115b15613b78576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c76c6004820152602401610599565b63ffffffff600987901c1660ff600188901c161b9250613b9883896149ce565b506029613baa89602060086000614a70565b901b867ffffffffffffffffffffffffffffffffffffffffffffe0000000001ffffffffff1617945050505050613f99565b60c386901c600116600003613c755763ffffffff603187901c1660ff602988901c161b9250613c0a83896149ce565b504260791b6051613c2089602060086000614a70565b901b6029613c338b602060086000614a70565b7ffffffffffffffffffffffffffc000000000000000000000000000000000000018a166501fffffffffe60288c901c1617911b1717179450613f999350505050565b63ffffffff600987901c811660ff600189901c81169190911b9450603188901c909116602988901c9091161b91506000613caf838a6149ce565b905082670de0b6b3a7640000850281613cca57613cca615276565b04670de0b6b3a76400000390506000811215613d0a57600166b1a2bc2ec50000623fffff836000030281613d0057613d00615276565b04901b9150613d23565b600166b1a2bc2ec50000623fffff830204901b60011791505b623fffff851115613d3557623fffff94505b609a87901c623fffff16935060b087901c60071660b388901c61ffff166101ff861115613e70578115613e015760299390931b600986901b1792613d788261522a565b9150613d8582602061525f565b600082815260066020526040902080549186901b90911790558115613db457613dad8261522a565b9150613f13565b600791507f00000000000000000000000000000000000000000000000000000000000004008160010181613dea57613dea615276565b066000818152600660205260408120559050613f13565b6000818152600660208190526040909120805460099690961b958617905591507f00000000000000000000000000000000000000000000000000000000000004006001820181613e5357613e53615276565b06600081815260066020526040902060e988901b90559050613f13565b85600985901b1793506007821015613ea5576000818152600660209081526040909120805491840286901b9091179055613ebb565b600081815260066020526040902060e085901b90555b8115613eca57613dad8261522a565b600791507f00000000000000000000000000000000000000000000000000000000000004008160010181613f0057613f00615276565b0660008181526006602052604081205590505b8a955089945088935060b381901b60b083901b609a89901b4260791b6051613f408a602060086000614a70565b901b6029613f538c602060086000614a70565b901b600164ffffffffff60298c901c16901b8a7ffffffffffffffff800000000000000000000000000000000000000000000000116171717171717179750505050505050505b9392505050565b60008080613fae87876151df565b90506000613fbd82600261525f565b613fd3866b033b2e3c9fd0803ce800000061525f565b613fdd888b61525f565b613fe791906152b9565b613ff191906152a5565b90506000613fff868861525f565b905074446c3b15f9926687d2c40534fdb56400000000000081106140425761402783826152a5565b61403d906b033b2e3c9fd0803ce800000061525f565b614063565b82614059826b033b2e3c9fd0803ce800000061525f565b61406391906152a5565b90506140726134d8838061525f565b61407c90836152b9565b94506b033b2e3c9fd0803ce80000006140958a8761525f565b61409f91906152a5565b935050505094509492505050565b60006060826000036140c3576000915050614513565b607f831161418c576040517fd60000000000000000000000000000000000000000000000000000000000000060208201527f940000000000000000000000000000000000000000000000000000000000000060218201527fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606086901b16602282015260f884901b7fff000000000000000000000000000000000000000000000000000000000000001660368201526037015b6040516020818303038152906040529050614508565b60ff8311614269576040517fd70000000000000000000000000000000000000000000000000000000000000060208201527f940000000000000000000000000000000000000000000000000000000000000060218201527fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606086901b1660228201527f8100000000000000000000000000000000000000000000000000000000000000603682015260f884901b7fff00000000000000000000000000000000000000000000000000000000000000166037820152603801614176565b61ffff8311614347576040517fd80000000000000000000000000000000000000000000000000000000000000060208201527f940000000000000000000000000000000000000000000000000000000000000060218201527fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606086901b1660228201527f820000000000000000000000000000000000000000000000000000000000000060368201527fffff00000000000000000000000000000000000000000000000000000000000060f085901b166037820152603901614176565b62ffffff8311614426576040517fd90000000000000000000000000000000000000000000000000000000000000060208201527f940000000000000000000000000000000000000000000000000000000000000060218201527fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606086901b1660228201527f830000000000000000000000000000000000000000000000000000000000000060368201527fffffff000000000000000000000000000000000000000000000000000000000060e885901b166037820152603a01614176565b6040517fda0000000000000000000000000000000000000000000000000000000000000060208201527f940000000000000000000000000000000000000000000000000000000000000060218201527fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606086901b1660228201527f840000000000000000000000000000000000000000000000000000000000000060368201527fffffffff0000000000000000000000000000000000000000000000000000000060e085901b166037820152603b0160405160208183030381529060405290505b805160209091012090505b92915050565b60405160248101839052604481018290526000906145fb907f0000000000000000000000004ecb836267b8b6473b34e244c82a6aa0749ffb9b907f2f850fb300000000000000000000000000000000000000000000000000000000906064015b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152611880565b806020019051810190613f999190615102565b6040516024810184905260448101839052606481018290526000908190819061467d907f0000000000000000000000004ecb836267b8b6473b34e244c82a6aa0749ffb9b907f0d7cf0ab0000000000000000000000000000000000000000000000000000000090608401614579565b80602001905181019061469091906154ac565b92509250925093509350939050565b6040516024810184905260448101839052606481018290526000908190819061467d907f0000000000000000000000004ecb836267b8b6473b34e244c82a6aa0749ffb9b907fe8518a660000000000000000000000000000000000000000000000000000000090608401614579565b60b5817101000000000000000000000000000000000081106147355760409190911b9060801c5b690100000000000000000081106147515760209190911b9060401c5b6501000000000081106147695760109190911b9060201c5b6301000000811061477f5760089190911b9060101c5b62010000010260121c80820401600190811c80830401811c80830401811c80830401811c80830401811c80830401811c80830401901c908190048111900390565b67ffffffffffffffff605b82901c811690609b83901c168115806147e2575080155b1561481e576040517fd50d7512000000000000000000000000000000000000000000000000000000008152620111716004820152602401610599565b61ffff8316603a84901c6401ffffffff16428181039160ea87901c617fff16911480614848575082155b806148535750806001145b1561486057505050915091565b64496cebb80084840283020484019350617fff60db87901c1692508260010361488b57505050915091565b826001166001036148e05760019290921c91826c7e37be2022c0914b2680000000816148b9576148b9615276565b049250612710601e87901c613fff166b033b2e3c9fd0803ce800000085010204925061490d565b60019290921c916305f5e100601e87901c613fff166127108501026b033b2e3c9fd0803ce8000000020492505b806001166001036149445760011c61271081016b033b2e3c9fd0803ce800000082028161493c5761493c615276565b04905061497a565b60011c61271081016b033b2e3c9fd0803ce800000082028161496857614968615276565b046b033b2e3c9fd0803ce80000000390505b760a70c3c40a64e6c51999090b65f67d92400000000000008382026127100261ffff881691900402601087901c613fff16612710030292506801b5a660ea44b8000085840283020485019450505050915091565b6000816149e3670de0b6b3a76400008561525f565b6149ed91906152a5565b6149ff90670de0b6b3a76400006153b1565b905066b1a2bc2ec50000811380614a3557507fffffffffffffffffffffffffffffffffffffffffffffffffff4e5d43d13b000081125b15614513576040517f2fee3e0e00000000000000000000000000000000000000000000000000000000815261c7606004820152602401610599565b600080856fffffffffffffffffffffffffffffffff811115614a935760809150811c5b67ffffffffffffffff811115614aab576040918201911c5b63ffffffff811115614abf576020918201911c5b61ffff811115614ad1576010918201911c5b60ff811115614ae2576008918201911c5b600f811115614af3576004918201911c5b6003811115614b04576002918201911c5b6001811115614b14576001820191505b8015614b21576001820191505b5084811015614b2d5750835b848103905085811c60008211841615614b7c57600181019050806001871b03614b7c57506001908101907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff86011b5b6001851b8210614b8b57600080fd5b90931b909201949350505050565b600080600080600060a08688031215614bb157600080fd5b505083359560208501359550604085013594606081013594506080013592509050565b60c081016145138284805182526020810151602083015260408101516040830152606081015160608301526080810151608083015260a081015160a08301525050565b8151815260208083015190820152604080830151908201526060808301519082015260808101614513565b73ffffffffffffffffffffffffffffffffffffffff81168114614c6457600080fd5b50565b60008060008060808587031215614c7d57600080fd5b8435935060208501359250604085013591506060850135614c9d81614c42565b939692955090935050565b8015158114614c6457600080fd5b60008060008060808587031215614ccc57600080fd5b8435614cd781614ca8565b935060208501359250604085013591506060850135614c9d81614c42565b60008060008060808587031215614d0b57600080fd5b8435935060208501359250604085013591506060850135614c9d81614ca8565b60008060008060608587031215614d4157600080fd5b8435614d4c81614c42565b935060208501359250604085013567ffffffffffffffff80821115614d7057600080fd5b818701915087601f830112614d8457600080fd5b813581811115614d9357600080fd5b886020828501011115614da557600080fd5b95989497505060200194505050565b600060208284031215614dc657600080fd5b5035919050565b81518152602080830151610240830191614dfe9084018273ffffffffffffffffffffffffffffffffffffffff169052565b506040830151614e26604084018273ffffffffffffffffffffffffffffffffffffffff169052565b506060830151614e84606084018273ffffffffffffffffffffffffffffffffffffffff808251168352806020830151166020840152806040830151166040840152806060830151166060840152806080830151166080840152505050565b506080830151610100614eae8185018373ffffffffffffffffffffffffffffffffffffffff169052565b60a08501519150610120614ed98186018473ffffffffffffffffffffffffffffffffffffffff169052565b60c08601519250610140614f048187018573ffffffffffffffffffffffffffffffffffffffff169052565b60e0870151610160878101919091529287015161018080880191909152918701516101a080880191909152908701516101c0870152918601516101e08601528501516102008501529093015161022090920191909152919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60006020808385031215614fa157600080fd5b823567ffffffffffffffff80821115614fb957600080fd5b818501915085601f830112614fcd57600080fd5b813581811115614fdf57614fdf614f5f565b8060051b6040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0603f8301168101818110858211171561502257615022614f5f565b60405291825284820192508381018501918883111561504057600080fd5b938501935b8285101561371c57843584529385019392850192615045565b604080825283519082018190526000906020906060840190828701845b828110156150d6576150c3848351805182526020810151602083015260408101516040830152606081015160608301526080810151608083015260a081015160a08301525050565b60c093909301929084019060010161507b565b50505092019290925292915050565b6000602082840312156150f757600080fd5b8151613f9981614ca8565b60006020828403121561511457600080fd5b5051919050565b6000806040838503121561512e57600080fd5b505080516020909101519092909150565b60008060006060848603121561515457600080fd5b83359250602084013561516681614ca8565b9150604084013561517681614c42565b809150509250925092565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b81810381811115614513576145136151b0565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203615223576152236151b0565b5060010190565b600081615239576152396151b0565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b8082028115828204841417614513576145136151b0565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000826152b4576152b4615276565b500490565b80820180821115614513576145136151b0565b60007f800000000000000000000000000000000000000000000000000000000000000082036152fd576152fd6151b0565b5060000390565b600073ffffffffffffffffffffffffffffffffffffffff8089168352602088818501528760408501528187166060850152818616608085015260c060a0850152845191508160c085015260005b8281101561536d5785810182015185820160e001528101615351565b5050600060e0828501015260e07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f830116840101915050979650505050505050565b81810360008312801583831316838312821617156153d1576153d16151b0565b5092915050565b6000826153e7576153e7615276565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83147f80000000000000000000000000000000000000000000000000000000000000008314161561543b5761543b6151b0565b500590565b808202600082127f800000000000000000000000000000000000000000000000000000000000000084141615615478576154786151b0565b8181058314821517614513576145136151b0565b80820182811260008312801582168215821617156118be576118be6151b0565b6000806000606084860312156154c157600080fd5b835192506020840151915060408401519050925092509256fea2646970667358221220030a7fecc110b38b34765d7248652e1fcb76dfaaf261eed5e38b5d920a127a9a64736f6c63430008150033

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

000000000000000000000000000000000000000000000000000000000000000b00000000000000000000000052aa899454998be5b000ad077a46bbe360f4e49700000000000000000000000091716c4eda1fb55e84bf8b4c7085f84285c190850000000000000000000000004ecb836267b8b6473b34e244c82a6aa0749ffb9b000000000000000000000000780d3c70d583dd8bd894abb53952b868916f69d80000000000000000000000007eb692a3fa34eb8e724c4809cdc4293b0a455e8c000000000000000000000000bfa9093d4ee238adb15ea40bfa90a088c1d629d30000000000000000000000005be9c322a0b38458b8d36f9a91eb7aca5df255430000000000000000000000004ec7b668baf70d4a4b0fc7941a7708a07b6d45be0000000000000000000000006f40d4a6237c257fff2db00fa0510deeecd303eb000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee76eefde27cef0839f6721813b8a946733d2dc6c96617e06f48ce36e5ebf61a727fc1b849f2080d40c18fc3be2117724aa74d4d24fd5a693f8ad10a8bb0d39853c6aa0a1ebc72fe4114f79538b07fe13247dd00fa4cd45b15bdf0806962814311ec31c709fa552a64d5bf7b00e0effa57c1f425f5596528967d700b0acd6ed68b7e6352010c06d4d4e841dfdca419ce23a0173412563e7ab6bebf6cce56e1202aa1829a9003092132f585b6ccdd167c19fe9774dbdea4260287e8a8e8ca8185d70000000000000000000000000000000000000000000000000000000000000400

-----Decoded View---------------
Arg [0] : constantViews_ (tuple): System.Collections.Generic.List`1[Nethereum.ABI.FunctionEncoding.ParameterOutput]

-----Encoded View---------------
18 Constructor Arguments found :
Arg [0] : 000000000000000000000000000000000000000000000000000000000000000b
Arg [1] : 00000000000000000000000052aa899454998be5b000ad077a46bbe360f4e497
Arg [2] : 00000000000000000000000091716c4eda1fb55e84bf8b4c7085f84285c19085
Arg [3] : 0000000000000000000000004ecb836267b8b6473b34e244c82a6aa0749ffb9b
Arg [4] : 000000000000000000000000780d3c70d583dd8bd894abb53952b868916f69d8
Arg [5] : 0000000000000000000000007eb692a3fa34eb8e724c4809cdc4293b0a455e8c
Arg [6] : 000000000000000000000000bfa9093d4ee238adb15ea40bfa90a088c1d629d3
Arg [7] : 0000000000000000000000005be9c322a0b38458b8d36f9a91eb7aca5df25543
Arg [8] : 0000000000000000000000004ec7b668baf70d4a4b0fc7941a7708a07b6d45be
Arg [9] : 0000000000000000000000006f40d4a6237c257fff2db00fa0510deeecd303eb
Arg [10] : 000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
Arg [11] : 76eefde27cef0839f6721813b8a946733d2dc6c96617e06f48ce36e5ebf61a72
Arg [12] : 7fc1b849f2080d40c18fc3be2117724aa74d4d24fd5a693f8ad10a8bb0d39853
Arg [13] : c6aa0a1ebc72fe4114f79538b07fe13247dd00fa4cd45b15bdf0806962814311
Arg [14] : ec31c709fa552a64d5bf7b00e0effa57c1f425f5596528967d700b0acd6ed68b
Arg [15] : 7e6352010c06d4d4e841dfdca419ce23a0173412563e7ab6bebf6cce56e1202a
Arg [16] : a1829a9003092132f585b6ccdd167c19fe9774dbdea4260287e8a8e8ca8185d7
Arg [17] : 0000000000000000000000000000000000000000000000000000000000000400


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

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

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