ETH Price: $2,939.31 (-1.36%)
 

More Info

Private Name Tags

Multichain Info

1 address found via
Transaction Hash
Method
Block
From
To

There are no matching entries

> 10 Internal Transactions and > 10 Token Transfers found.

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Method Block
From
To
Transfer242935012026-01-22 23:06:471 min ago1769123207
Ekubo Protocol: Core
0.02009166 ETH
Transfer242935012026-01-22 23:06:471 min ago1769123207
Ekubo Protocol: Core
0.02672817 ETH
Transfer242935012026-01-22 23:06:471 min ago1769123207
Ekubo Protocol: Core
0.05706225 ETH
0x00000000242935012026-01-22 23:06:471 min ago1769123207
Ekubo Protocol: Core
0.24981083 ETH
Transfer242935002026-01-22 23:06:351 min ago1769123195
Ekubo Protocol: Core
0.01671755 ETH
Transfer242934992026-01-22 23:06:231 min ago1769123183
Ekubo Protocol: Core
0.02537785 ETH
Transfer242934972026-01-22 23:05:591 min ago1769123159
Ekubo Protocol: Core
0.01745034 ETH
0x00000000242934972026-01-22 23:05:591 min ago1769123159
Ekubo Protocol: Core
0.05205719 ETH
0x00000000242934942026-01-22 23:05:232 mins ago1769123123
Ekubo Protocol: Core
0.002 ETH
Transfer242934932026-01-22 23:05:112 mins ago1769123111
Ekubo Protocol: Core
0.02245321 ETH
Transfer242934932026-01-22 23:05:112 mins ago1769123111
Ekubo Protocol: Core
0.01817856 ETH
Transfer242934922026-01-22 23:04:592 mins ago1769123099
Ekubo Protocol: Core
0.20281802 ETH
0x00000000242934912026-01-22 23:04:473 mins ago1769123087
Ekubo Protocol: Core
0.03401253 ETH
Transfer242934892026-01-22 23:04:233 mins ago1769123063
Ekubo Protocol: Core
0.02054305 ETH
Transfer242934892026-01-22 23:04:233 mins ago1769123063
Ekubo Protocol: Core
0.03128008 ETH
Transfer242934852026-01-22 23:03:354 mins ago1769123015
Ekubo Protocol: Core
0.04031289 ETH
Transfer242934852026-01-22 23:03:354 mins ago1769123015
Ekubo Protocol: Core
0.0193464 ETH
Transfer242934842026-01-22 23:03:234 mins ago1769123003
Ekubo Protocol: Core
0.0327304 ETH
Transfer242934832026-01-22 23:03:114 mins ago1769122991
Ekubo Protocol: Core
0.01316895 ETH
Transfer242934812026-01-22 23:02:475 mins ago1769122967
Ekubo Protocol: Core
0.03009945 ETH
Transfer242934802026-01-22 23:02:355 mins ago1769122955
Ekubo Protocol: Core
0.0105 ETH
0x00000000242934802026-01-22 23:02:355 mins ago1769122955
Ekubo Protocol: Core
0.27090931 ETH
Transfer242934782026-01-22 23:02:115 mins ago1769122931
Ekubo Protocol: Core
0.0286296 ETH
Transfer242934782026-01-22 23:02:115 mins ago1769122931
Ekubo Protocol: Core
0.0504 ETH
Transfer242934772026-01-22 23:01:595 mins ago1769122919
Ekubo Protocol: Core
0.11848748 ETH
View All Internal Transactions
Loading...
Loading
Cross-Chain Transactions

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Validator Index Block Amount
View All Withdrawals

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

Contract Source Code Verified (Exact Match)

Contract Name:
Core

Compiler Version
v0.8.33+commit.64118f21

Optimization Enabled:
Yes with 9999999 runs

Other Settings:
osaka EvmVersion
// SPDX-License-Identifier: ekubo-license-v1.eth
pragma solidity =0.8.33;

import {CallPoints, addressToCallPoints} from "./types/callPoints.sol";
import {PoolKey} from "./types/poolKey.sol";
import {PoolConfig} from "./types/poolConfig.sol";
import {PositionId} from "./types/positionId.sol";
import {FeesPerLiquidity, feesPerLiquidityFromAmounts} from "./types/feesPerLiquidity.sol";
import {Position} from "./types/position.sol";
import {tickToSqrtRatio, sqrtRatioToTick} from "./math/ticks.sol";
import {CoreStorageLayout} from "./libraries/CoreStorageLayout.sol";
import {ExtensionCallPointsLib} from "./libraries/ExtensionCallPointsLib.sol";
import {FixedPointMathLib} from "solady/utils/FixedPointMathLib.sol";
import {SafeCastLib} from "solady/utils/SafeCastLib.sol";
import {ExposedStorage} from "./base/ExposedStorage.sol";
import {liquidityDeltaToAmountDelta, addLiquidityDelta} from "./math/liquidity.sol";
import {findNextInitializedTick, findPrevInitializedTick, flipTick} from "./math/tickBitmap.sol";
import {ICore, IExtension} from "./interfaces/ICore.sol";
import {FlashAccountant} from "./base/FlashAccountant.sol";
import {MIN_TICK, MAX_TICK, NATIVE_TOKEN_ADDRESS} from "./math/constants.sol";
import {MIN_SQRT_RATIO, MAX_SQRT_RATIO, SqrtRatio} from "./types/sqrtRatio.sol";
import {PoolState, createPoolState} from "./types/poolState.sol";
import {SwapParameters} from "./types/swapParameters.sol";
import {TickInfo, createTickInfo} from "./types/tickInfo.sol";
import {PoolBalanceUpdate, createPoolBalanceUpdate} from "./types/poolBalanceUpdate.sol";
import {PoolId} from "./types/poolId.sol";
import {Locker} from "./types/locker.sol";
import {computeFee, amountBeforeFee} from "./math/fee.sol";
import {nextSqrtRatioFromAmount0, nextSqrtRatioFromAmount1} from "./math/sqrtRatio.sol";
import {
    amount0Delta,
    amount1Delta,
    amount0DeltaSorted,
    amount1DeltaSorted,
    sortAndConvertToFixedSqrtRatios
} from "./math/delta.sol";
import {StorageSlot} from "./types/storageSlot.sol";
import {LibBit} from "solady/utils/LibBit.sol";

/// @title Ekubo Protocol Core
/// @author Moody Salem <[email protected]>
/// @notice Singleton contract holding all tokens and containing all possible operations in Ekubo Protocol
/// @dev Implements the core AMM functionality including pools, positions, swaps, and fee collection
/// @dev Note this code is under the terms of the Ekubo DAO Shared Revenue License 1.0.
/// @dev The full terms of the license can be found at the contenthash specified at ekubo-license-v1.eth.
contract Core is ICore, FlashAccountant, ExposedStorage {
    using ExtensionCallPointsLib for *;

    /// @inheritdoc ICore
    function registerExtension(CallPoints memory expectedCallPoints) external {
        CallPoints memory computed = addressToCallPoints(msg.sender);
        if (!computed.eq(expectedCallPoints) || !computed.isValid()) {
            revert FailedRegisterInvalidCallPoints();
        }
        StorageSlot isExtensionRegisteredSlot = CoreStorageLayout.isExtensionRegisteredSlot(msg.sender);
        if (isExtensionRegisteredSlot.load() != bytes32(0)) revert ExtensionAlreadyRegistered();

        isExtensionRegisteredSlot.store(bytes32(LibBit.rawToUint(true)));

        emit ExtensionRegistered(msg.sender);
    }

    function readPoolState(PoolId poolId) internal view returns (PoolState state) {
        state = PoolState.wrap(CoreStorageLayout.poolStateSlot(poolId).load());
    }

    function writePoolState(PoolId poolId, PoolState state) internal {
        CoreStorageLayout.poolStateSlot(poolId).store(PoolState.unwrap(state));
    }

    /// @inheritdoc ICore
    function initializePool(PoolKey memory poolKey, int32 tick) external returns (SqrtRatio sqrtRatio) {
        poolKey.validate();

        address extension = poolKey.config.extension();
        if (extension != address(0)) {
            StorageSlot isExtensionRegisteredSlot = CoreStorageLayout.isExtensionRegisteredSlot(extension);

            if (isExtensionRegisteredSlot.load() == bytes32(0)) {
                revert ExtensionNotRegistered();
            }

            IExtension(extension).maybeCallBeforeInitializePool(msg.sender, poolKey, tick);
        }

        PoolId poolId = poolKey.toPoolId();
        PoolState state = readPoolState(poolId);
        if (state.isInitialized()) revert PoolAlreadyInitialized();

        sqrtRatio = tickToSqrtRatio(tick);
        writePoolState(poolId, createPoolState({_sqrtRatio: sqrtRatio, _tick: tick, _liquidity: 0}));

        // initialize these slots so the first swap or deposit on the pool is the same cost as any other swap
        StorageSlot fplSlot0 = CoreStorageLayout.poolFeesPerLiquiditySlot(poolId);
        fplSlot0.store(bytes32(uint256(1)));
        fplSlot0.next().store(bytes32(uint256(1)));

        emit PoolInitialized(poolId, poolKey, tick, sqrtRatio);

        IExtension(extension).maybeCallAfterInitializePool(msg.sender, poolKey, tick, sqrtRatio);
    }

    /// @inheritdoc ICore
    function prevInitializedTick(PoolId poolId, int32 fromTick, uint32 tickSpacing, uint256 skipAhead)
        external
        view
        returns (int32 tick, bool isInitialized)
    {
        (tick, isInitialized) =
            findPrevInitializedTick(CoreStorageLayout.tickBitmapsSlot(poolId), fromTick, tickSpacing, skipAhead);
    }

    /// @inheritdoc ICore
    function nextInitializedTick(PoolId poolId, int32 fromTick, uint32 tickSpacing, uint256 skipAhead)
        external
        view
        returns (int32 tick, bool isInitialized)
    {
        (tick, isInitialized) =
            findNextInitializedTick(CoreStorageLayout.tickBitmapsSlot(poolId), fromTick, tickSpacing, skipAhead);
    }

    /// @inheritdoc ICore
    function updateSavedBalances(
        address token0,
        address token1,
        bytes32,
        // positive is saving, negative is loading
        int256 delta0,
        int256 delta1
    )
        external
        payable
    {
        if (token0 >= token1) revert SavedBalanceTokensNotSorted();

        (uint256 id, address lockerAddr) = _requireLocker().parse();

        assembly ("memory-safe") {
            function addDelta(u, i) -> result {
                // full‐width sum mod 2^256
                let sum := add(u, i)
                // 1 if i<0 else 0
                let sign := shr(255, i)
                // if sum > type(uint128).max || (i>=0 && sum<u) || (i<0 && sum>u) ⇒ 256-bit wrap or underflow
                if or(shr(128, sum), or(and(iszero(sign), lt(sum, u)), and(sign, gt(sum, u)))) {
                    mstore(0x00, 0x1293d6fa) // `SavedBalanceOverflow()`
                    revert(0x1c, 0x04)
                }
                result := sum
            }

            // we can cheaply calldatacopy the arguments into memory, hence no call to CoreStorageLayout#savedBalancesSlot
            let free := mload(0x40)
            mstore(free, lockerAddr)
            // copy the first 3 arguments in the same order
            calldatacopy(add(free, 0x20), 4, 96)
            let slot := keccak256(free, 128)
            let balances := sload(slot)

            let b0 := shr(128, balances)
            let b1 := shr(128, shl(128, balances))

            let b0Next := addDelta(b0, delta0)
            let b1Next := addDelta(b1, delta1)

            sstore(slot, add(shl(128, b0Next), b1Next))
        }

        _updatePairDebtWithNative(id, token0, token1, delta0, delta1);
    }

    /// @notice Returns the pool fees per liquidity inside the given bounds
    /// @dev Internal function that calculates fees per liquidity within position bounds
    /// @param poolId Unique identifier for the pool
    /// @param tick Current tick of the pool
    /// @param tickLower Lower tick of the price range to get the snapshot of
    /// @param tickLower Upper tick of the price range to get the snapshot of
    /// @return feesPerLiquidityInside Accumulated fees per liquidity snapshot inside the bounds. Note this is a relative value.
    function _getPoolFeesPerLiquidityInside(PoolId poolId, int32 tick, int32 tickLower, int32 tickUpper)
        internal
        view
        returns (FeesPerLiquidity memory feesPerLiquidityInside)
    {
        uint256 lower0;
        uint256 lower1;
        uint256 upper0;
        uint256 upper1;
        {
            (StorageSlot l0, StorageSlot l1) = CoreStorageLayout.poolTickFeesPerLiquidityOutsideSlot(poolId, tickLower);
            (lower0, lower1) = (uint256(l0.load()), uint256(l1.load()));

            (StorageSlot u0, StorageSlot u1) = CoreStorageLayout.poolTickFeesPerLiquidityOutsideSlot(poolId, tickUpper);
            (upper0, upper1) = (uint256(u0.load()), uint256(u1.load()));
        }

        unchecked {
            if (tick < tickLower) {
                feesPerLiquidityInside.value0 = lower0 - upper0;
                feesPerLiquidityInside.value1 = lower1 - upper1;
            } else if (tick < tickUpper) {
                uint256 global0;
                uint256 global1;
                {
                    (bytes32 g0, bytes32 g1) = CoreStorageLayout.poolFeesPerLiquiditySlot(poolId).loadTwo();
                    (global0, global1) = (uint256(g0), uint256(g1));
                }

                feesPerLiquidityInside.value0 = global0 - upper0 - lower0;
                feesPerLiquidityInside.value1 = global1 - upper1 - lower1;
            } else {
                feesPerLiquidityInside.value0 = upper0 - lower0;
                feesPerLiquidityInside.value1 = upper1 - lower1;
            }
        }
    }

    /// @inheritdoc ICore
    function getPoolFeesPerLiquidityInside(PoolId poolId, int32 tickLower, int32 tickUpper)
        external
        view
        returns (FeesPerLiquidity memory)
    {
        return _getPoolFeesPerLiquidityInside(poolId, readPoolState(poolId).tick(), tickLower, tickUpper);
    }

    /// @inheritdoc ICore
    function accumulateAsFees(PoolKey memory poolKey, uint128 _amount0, uint128 _amount1) external payable {
        (uint256 id, address lockerAddr) = _requireLocker().parse();
        require(lockerAddr == poolKey.config.extension());

        PoolId poolId = poolKey.toPoolId();

        uint256 amount0;
        uint256 amount1;
        assembly ("memory-safe") {
            amount0 := _amount0
            amount1 := _amount1
        }

        // Note we do not check pool is initialized. If the extension calls this for a pool that does not exist,
        //  the fees are simply burned since liquidity is 0.

        if (amount0 != 0 || amount1 != 0) {
            uint256 liquidity;
            {
                uint128 _liquidity = readPoolState(poolId).liquidity();
                assembly ("memory-safe") {
                    liquidity := _liquidity
                }
            }

            unchecked {
                if (liquidity != 0) {
                    StorageSlot slot0 = CoreStorageLayout.poolFeesPerLiquiditySlot(poolId);

                    if (amount0 != 0) {
                        slot0.store(
                            bytes32(uint256(slot0.load()) + FixedPointMathLib.rawDiv(amount0 << 128, liquidity))
                        );
                    }
                    if (amount1 != 0) {
                        StorageSlot slot1 = slot0.next();
                        slot1.store(
                            bytes32(uint256(slot1.load()) + FixedPointMathLib.rawDiv(amount1 << 128, liquidity))
                        );
                    }
                }
            }
        }

        // whether the fees are actually accounted to any position, the caller owes the debt
        _updatePairDebtWithNative(id, poolKey.token0, poolKey.token1, int256(amount0), int256(amount1));

        emit FeesAccumulated(poolId, _amount0, _amount1);
    }

    /// @notice Updates tick information when liquidity is added or removed
    /// @dev Private function that handles tick initialization and liquidity tracking
    /// @param poolId Unique identifier for the pool
    /// @param tick Tick to update
    /// @param poolConfig Pool configuration containing tick spacing
    /// @param liquidityDelta Change in liquidity
    /// @param isUpper Whether this is the upper bound of a position
    function _updateTick(PoolId poolId, int32 tick, PoolConfig poolConfig, int128 liquidityDelta, bool isUpper)
        private
    {
        StorageSlot tickInfoSlot = CoreStorageLayout.poolTicksSlot(poolId, tick);

        (int128 currentLiquidityDelta, uint128 currentLiquidityNet) = TickInfo.wrap(tickInfoSlot.load()).parse();
        uint128 liquidityNetNext = addLiquidityDelta(currentLiquidityNet, liquidityDelta);
        // this is checked math
        int128 liquidityDeltaNext =
            isUpper ? currentLiquidityDelta - liquidityDelta : currentLiquidityDelta + liquidityDelta;

        // Check that liquidityNet doesn't exceed max liquidity per tick
        uint128 maxLiquidity = poolConfig.concentratedMaxLiquidityPerTick();
        if (liquidityNetNext > maxLiquidity) {
            revert MaxLiquidityPerTickExceeded(tick, liquidityNetNext, maxLiquidity);
        }

        if ((currentLiquidityNet == 0) != (liquidityNetNext == 0)) {
            flipTick(CoreStorageLayout.tickBitmapsSlot(poolId), tick, poolConfig.concentratedTickSpacing());

            (StorageSlot fplSlot0, StorageSlot fplSlot1) =
                CoreStorageLayout.poolTickFeesPerLiquidityOutsideSlot(poolId, tick);

            bytes32 v;
            assembly ("memory-safe") {
                v := gt(liquidityNetNext, 0)
            }

            // initialize the storage slots for the fees per liquidity outside to non-zero so tick crossing is cheaper
            fplSlot0.store(v);
            fplSlot1.store(v);
        }

        tickInfoSlot.store(TickInfo.unwrap(createTickInfo(liquidityDeltaNext, liquidityNetNext)));
    }

    /// @notice Updates debt for a token pair, handling native token payments for token0
    /// @dev Optimized version that updates both tokens' debts in a single operation when possible.
    ///      Assumes token0 < token1 (tokens are sorted).
    /// @param id Lock ID for debt tracking
    /// @param token0 Address of token0 (must be < token1)
    /// @param token1 Address of token1 (must be > token0)
    /// @param debtChange0 Change in debt amount for token0
    /// @param debtChange1 Change in debt amount for token1
    function _updatePairDebtWithNative(
        uint256 id,
        address token0,
        address token1,
        int256 debtChange0,
        int256 debtChange1
    ) private {
        if (msg.value == 0) {
            // No native token payment included in the call, so use optimized pair update
            _updatePairDebt(id, token0, token1, debtChange0, debtChange1);
        } else {
            if (token0 == NATIVE_TOKEN_ADDRESS) {
                unchecked {
                    // token0 is native, so we can still use pair update with adjusted debtChange0
                    // Subtraction is safe because debtChange0 and msg.value are both bounded by int128/uint128
                    _updatePairDebt(id, token0, token1, debtChange0 - int256(msg.value), debtChange1);
                }
            } else {
                // token0 is not native, and since token0 < token1, token1 cannot be native either
                // Update the token0, token1 debt and then update native token debt separately
                unchecked {
                    _updatePairDebt(id, token0, token1, debtChange0, debtChange1);
                    _accountDebt(id, NATIVE_TOKEN_ADDRESS, -int256(msg.value));
                }
            }
        }
    }

    /// @inheritdoc ICore
    function updatePosition(PoolKey memory poolKey, PositionId positionId, int128 liquidityDelta)
        external
        payable
        returns (PoolBalanceUpdate balanceUpdate)
    {
        positionId.validate(poolKey.config);

        Locker locker = _requireLocker();

        IExtension(poolKey.config.extension())
            .maybeCallBeforeUpdatePosition(locker, poolKey, positionId, liquidityDelta);

        PoolId poolId = poolKey.toPoolId();
        PoolState state = readPoolState(poolId);
        if (!state.isInitialized()) revert PoolNotInitialized();

        if (liquidityDelta != 0) {
            (SqrtRatio sqrtRatioLower, SqrtRatio sqrtRatioUpper) =
                (tickToSqrtRatio(positionId.tickLower()), tickToSqrtRatio(positionId.tickUpper()));

            (int128 delta0, int128 delta1) =
                liquidityDeltaToAmountDelta(state.sqrtRatio(), liquidityDelta, sqrtRatioLower, sqrtRatioUpper);

            StorageSlot positionSlot = CoreStorageLayout.poolPositionsSlot(poolId, locker.addr(), positionId);
            Position storage position;
            assembly ("memory-safe") {
                position.slot := positionSlot
            }

            uint128 liquidityNext = addLiquidityDelta(position.liquidity, liquidityDelta);

            FeesPerLiquidity memory feesPerLiquidityInside;

            if (poolKey.config.isConcentrated()) {
                // the position is fully withdrawn
                if (liquidityNext == 0) {
                    // we need to fetch it before the tick fees per liquidity outside is deleted
                    feesPerLiquidityInside = _getPoolFeesPerLiquidityInside(
                        poolId, state.tick(), positionId.tickLower(), positionId.tickUpper()
                    );
                }

                _updateTick(poolId, positionId.tickLower(), poolKey.config, liquidityDelta, false);
                _updateTick(poolId, positionId.tickUpper(), poolKey.config, liquidityDelta, true);

                if (liquidityNext != 0) {
                    feesPerLiquidityInside = _getPoolFeesPerLiquidityInside(
                        poolId, state.tick(), positionId.tickLower(), positionId.tickUpper()
                    );
                }

                if (state.tick() >= positionId.tickLower() && state.tick() < positionId.tickUpper()) {
                    state = createPoolState({
                        _sqrtRatio: state.sqrtRatio(),
                        _tick: state.tick(),
                        _liquidity: addLiquidityDelta(state.liquidity(), liquidityDelta)
                    });
                    writePoolState(poolId, state);
                }
            } else {
                // we store the active liquidity in the liquidity slot for stableswap pools
                state = createPoolState({
                    _sqrtRatio: state.sqrtRatio(),
                    _tick: state.tick(),
                    _liquidity: addLiquidityDelta(state.liquidity(), liquidityDelta)
                });
                writePoolState(poolId, state);
                StorageSlot fplFirstSlot = CoreStorageLayout.poolFeesPerLiquiditySlot(poolId);
                feesPerLiquidityInside.value0 = uint256(fplFirstSlot.load());
                feesPerLiquidityInside.value1 = uint256(fplFirstSlot.next().load());
            }

            if (liquidityNext == 0) {
                position.liquidity = 0;
                position.feesPerLiquidityInsideLast = FeesPerLiquidity(0, 0);
            } else {
                (uint128 fees0, uint128 fees1) = position.fees(feesPerLiquidityInside);
                position.liquidity = liquidityNext;
                position.feesPerLiquidityInsideLast =
                    feesPerLiquidityInside.sub(feesPerLiquidityFromAmounts(fees0, fees1, liquidityNext));
            }

            _updatePairDebtWithNative(locker.id(), poolKey.token0, poolKey.token1, delta0, delta1);

            balanceUpdate = createPoolBalanceUpdate(delta0, delta1);
            emit PositionUpdated(locker.addr(), poolId, positionId, liquidityDelta, balanceUpdate, state);
        } else {
            if (msg.value != 0) {
                _accountDebt(locker.id(), NATIVE_TOKEN_ADDRESS, -int256(msg.value));
            }
        }

        IExtension(poolKey.config.extension())
            .maybeCallAfterUpdatePosition(locker, poolKey, positionId, liquidityDelta, balanceUpdate, state);
    }

    /// @inheritdoc ICore
    function setExtraData(PoolId poolId, PositionId positionId, bytes16 _extraData) external {
        StorageSlot firstSlot = CoreStorageLayout.poolPositionsSlot(poolId, msg.sender, positionId);

        bytes32 extraData;
        assembly ("memory-safe") {
            extraData := _extraData
        }

        firstSlot.store(((firstSlot.load() >> 128) << 128) | (extraData >> 128));
    }

    /// @inheritdoc ICore
    function collectFees(PoolKey memory poolKey, PositionId positionId)
        external
        returns (uint128 amount0, uint128 amount1)
    {
        Locker locker = _requireLocker();

        IExtension(poolKey.config.extension()).maybeCallBeforeCollectFees(locker, poolKey, positionId);

        PoolId poolId = poolKey.toPoolId();

        Position storage position;
        StorageSlot positionSlot = CoreStorageLayout.poolPositionsSlot(poolId, locker.addr(), positionId);
        assembly ("memory-safe") {
            position.slot := positionSlot
        }

        FeesPerLiquidity memory feesPerLiquidityInside;
        if (poolKey.config.isStableswap()) {
            // Stableswap pools: use global fees per liquidity
            StorageSlot fplFirstSlot = CoreStorageLayout.poolFeesPerLiquiditySlot(poolId);
            feesPerLiquidityInside.value0 = uint256(fplFirstSlot.load());
            feesPerLiquidityInside.value1 = uint256(fplFirstSlot.next().load());
        } else {
            // Concentrated pools: calculate fees per liquidity inside the position bounds
            feesPerLiquidityInside = _getPoolFeesPerLiquidityInside(
                poolId, readPoolState(poolId).tick(), positionId.tickLower(), positionId.tickUpper()
            );
        }

        (amount0, amount1) = position.fees(feesPerLiquidityInside);

        position.feesPerLiquidityInsideLast = feesPerLiquidityInside;

        _updatePairDebt(
            locker.id(), poolKey.token0, poolKey.token1, -int256(uint256(amount0)), -int256(uint256(amount1))
        );

        emit PositionFeesCollected(locker.addr(), poolId, positionId, amount0, amount1);

        IExtension(poolKey.config.extension()).maybeCallAfterCollectFees(locker, poolKey, positionId, amount0, amount1);
    }

    /// @inheritdoc ICore
    function swap_6269342730() external payable {
        unchecked {
            PoolKey memory poolKey;
            address token0;
            address token1;
            PoolConfig config;

            SwapParameters params;

            assembly ("memory-safe") {
                token0 := calldataload(4)
                token1 := calldataload(36)
                config := calldataload(68)
                params := calldataload(100)
                calldatacopy(poolKey, 4, 96)
            }

            SqrtRatio sqrtRatioLimit = params.sqrtRatioLimit();
            if (!sqrtRatioLimit.isValid()) revert InvalidSqrtRatioLimit();

            Locker locker = _requireLocker();

            IExtension(config.extension()).maybeCallBeforeSwap(locker, poolKey, params);

            PoolId poolId = poolKey.toPoolId();

            PoolState stateAfter = readPoolState(poolId);

            if (!stateAfter.isInitialized()) revert PoolNotInitialized();

            int256 amountRemaining = params.amount();

            PoolBalanceUpdate balanceUpdate;

            // 0 swap amount or sqrt ratio limit == sqrt ratio is no-op
            if (amountRemaining != 0 && stateAfter.sqrtRatio() != sqrtRatioLimit) {
                (SqrtRatio sqrtRatio, int32 tick, uint128 liquidity) = stateAfter.parse();

                bool isToken1 = params.isToken1();

                bool isExactOut = amountRemaining < 0;
                bool increasing;
                assembly ("memory-safe") {
                    increasing := xor(isToken1, isExactOut)
                }

                if ((sqrtRatioLimit < sqrtRatio) == increasing) {
                    revert SqrtRatioLimitWrongDirection();
                }

                int256 calculatedAmount;

                // fees per liquidity only for the input token
                uint256 inputTokenFeesPerLiquidity;

                // 0 = not loaded & not updated, 1 = loaded & not updated, 2 = loaded & updated
                uint256 feesAccessed;

                while (true) {
                    int32 nextTick;
                    bool isInitialized;
                    SqrtRatio nextTickSqrtRatio;

                    // For stableswap pools, determine active liquidity for this step
                    uint128 stepLiquidity = liquidity;

                    if (config.isStableswap()) {
                        if (config.isFullRange()) {
                            // special case since we don't need to compute min/max tick sqrt ratio
                            (nextTick, nextTickSqrtRatio) =
                                increasing ? (MAX_TICK, MAX_SQRT_RATIO) : (MIN_TICK, MIN_SQRT_RATIO);
                        } else {
                            (int32 lower, int32 upper) = config.stableswapActiveLiquidityTickRange();

                            bool inRange;
                            assembly ("memory-safe") {
                                inRange := and(slt(tick, upper), iszero(slt(tick, lower)))
                            }
                            if (inRange) {
                                nextTick = increasing ? upper : lower;
                                nextTickSqrtRatio = tickToSqrtRatio(nextTick);
                            } else {
                                if (tick < lower) {
                                    (nextTick, nextTickSqrtRatio) =
                                        increasing ? (lower, tickToSqrtRatio(lower)) : (MIN_TICK, MIN_SQRT_RATIO);
                                } else {
                                    // tick >= upper implied
                                    (nextTick, nextTickSqrtRatio) =
                                        increasing ? (MAX_TICK, MAX_SQRT_RATIO) : (upper, tickToSqrtRatio(upper));
                                }
                                stepLiquidity = 0;
                            }
                        }
                    } else {
                        // concentrated liquidity pools use the tick bitmaps
                        (nextTick, isInitialized) = increasing
                            ? findNextInitializedTick(
                                CoreStorageLayout.tickBitmapsSlot(poolId),
                                tick,
                                config.concentratedTickSpacing(),
                                params.skipAhead()
                            )
                            : findPrevInitializedTick(
                                CoreStorageLayout.tickBitmapsSlot(poolId),
                                tick,
                                config.concentratedTickSpacing(),
                                params.skipAhead()
                            );

                        nextTickSqrtRatio = tickToSqrtRatio(nextTick);
                    }

                    SqrtRatio limitedNextSqrtRatio =
                        increasing ? nextTickSqrtRatio.min(sqrtRatioLimit) : nextTickSqrtRatio.max(sqrtRatioLimit);

                    SqrtRatio sqrtRatioNext;

                    if (stepLiquidity == 0) {
                        // if the pool is empty, the swap will always move all the way to the limit price
                        sqrtRatioNext = limitedNextSqrtRatio;
                    } else {
                        // this amount is what moves the price
                        int128 priceImpactAmount;
                        if (isExactOut) {
                            assembly ("memory-safe") {
                                priceImpactAmount := amountRemaining
                            }
                        } else {
                            uint128 amountU128;
                            assembly ("memory-safe") {
                                // cast is safe because amountRemaining is g.t. 0 and fits in int128
                                amountU128 := amountRemaining
                            }
                            uint128 feeAmount = computeFee(amountU128, config.fee());
                            assembly ("memory-safe") {
                                // feeAmount will never exceed amountRemaining since fee is < 100%
                                priceImpactAmount := sub(amountRemaining, feeAmount)
                            }
                        }

                        SqrtRatio sqrtRatioNextFromAmount = isToken1
                            ? nextSqrtRatioFromAmount1(sqrtRatio, stepLiquidity, priceImpactAmount)
                            : nextSqrtRatioFromAmount0(sqrtRatio, stepLiquidity, priceImpactAmount);

                        bool hitLimit;
                        assembly ("memory-safe") {
                            // Branchless limit check: (increasing && next > limit) || (!increasing && next < limit)
                            let exceedsUp := and(increasing, gt(sqrtRatioNextFromAmount, limitedNextSqrtRatio))
                            let exceedsDown :=
                                and(iszero(increasing), lt(sqrtRatioNextFromAmount, limitedNextSqrtRatio))
                            hitLimit := or(exceedsUp, exceedsDown)
                        }

                        // the change in fees per liquidity for this step of the iteration
                        uint256 stepFeesPerLiquidity;

                        if (hitLimit) {
                            (uint256 sqrtRatioLower, uint256 sqrtRatioUpper) =
                                sortAndConvertToFixedSqrtRatios(limitedNextSqrtRatio, sqrtRatio);
                            (uint128 limitSpecifiedAmountDelta, uint128 limitCalculatedAmountDelta) = isToken1
                                ? (
                                    amount1DeltaSorted(sqrtRatioLower, sqrtRatioUpper, stepLiquidity, !isExactOut),
                                    amount0DeltaSorted(sqrtRatioLower, sqrtRatioUpper, stepLiquidity, isExactOut)
                                )
                                : (
                                    amount0DeltaSorted(sqrtRatioLower, sqrtRatioUpper, stepLiquidity, !isExactOut),
                                    amount1DeltaSorted(sqrtRatioLower, sqrtRatioUpper, stepLiquidity, isExactOut)
                                );

                            if (isExactOut) {
                                uint128 beforeFee = amountBeforeFee(limitCalculatedAmountDelta, config.fee());
                                assembly ("memory-safe") {
                                    calculatedAmount := add(calculatedAmount, beforeFee)
                                    amountRemaining := add(amountRemaining, limitSpecifiedAmountDelta)
                                    stepFeesPerLiquidity := div(
                                        shl(128, sub(beforeFee, limitCalculatedAmountDelta)),
                                        stepLiquidity
                                    )
                                }
                            } else {
                                uint128 beforeFee = amountBeforeFee(limitSpecifiedAmountDelta, config.fee());
                                assembly ("memory-safe") {
                                    calculatedAmount := sub(calculatedAmount, limitCalculatedAmountDelta)
                                    amountRemaining := sub(amountRemaining, beforeFee)
                                    stepFeesPerLiquidity := div(
                                        shl(128, sub(beforeFee, limitSpecifiedAmountDelta)),
                                        stepLiquidity
                                    )
                                }
                            }

                            sqrtRatioNext = limitedNextSqrtRatio;
                        } else if (sqrtRatioNextFromAmount != sqrtRatio) {
                            uint128 calculatedAmountWithoutFee = isToken1
                                ? amount0Delta(sqrtRatioNextFromAmount, sqrtRatio, stepLiquidity, isExactOut)
                                : amount1Delta(sqrtRatioNextFromAmount, sqrtRatio, stepLiquidity, isExactOut);

                            if (isExactOut) {
                                uint128 includingFee = amountBeforeFee(calculatedAmountWithoutFee, config.fee());
                                assembly ("memory-safe") {
                                    calculatedAmount := add(calculatedAmount, includingFee)
                                    stepFeesPerLiquidity := div(
                                        shl(128, sub(includingFee, calculatedAmountWithoutFee)),
                                        stepLiquidity
                                    )
                                }
                            } else {
                                assembly ("memory-safe") {
                                    calculatedAmount := sub(calculatedAmount, calculatedAmountWithoutFee)
                                    stepFeesPerLiquidity := div(
                                        shl(128, sub(amountRemaining, priceImpactAmount)),
                                        stepLiquidity
                                    )
                                }
                            }

                            amountRemaining = 0;
                            sqrtRatioNext = sqrtRatioNextFromAmount;
                        } else {
                            // for an exact output swap, the price should always move since we have to round away from the current price
                            assert(!isExactOut);

                            // consume the entire input amount as fees since the price did not move
                            assembly ("memory-safe") {
                                stepFeesPerLiquidity := div(shl(128, amountRemaining), stepLiquidity)
                            }
                            amountRemaining = 0;
                            sqrtRatioNext = sqrtRatio;
                        }

                        // only if fees per liquidity was updated in this swap iteration
                        if (stepFeesPerLiquidity != 0) {
                            if (feesAccessed == 0) {
                                // this loads only the input token fees per liquidity
                                inputTokenFeesPerLiquidity = uint256(
                                    CoreStorageLayout.poolFeesPerLiquiditySlot(poolId).add(LibBit.rawToUint(increasing))
                                        .load()
                                ) + stepFeesPerLiquidity;
                            } else {
                                inputTokenFeesPerLiquidity += stepFeesPerLiquidity;
                            }

                            feesAccessed = 2;
                        }
                    }

                    if (sqrtRatioNext == nextTickSqrtRatio) {
                        sqrtRatio = sqrtRatioNext;
                        assembly ("memory-safe") {
                            // no overflow danger because nextTick is always inside the valid tick bounds
                            tick := sub(nextTick, iszero(increasing))
                        }

                        if (isInitialized) {
                            bytes32 tickValue = CoreStorageLayout.poolTicksSlot(poolId, nextTick).load();
                            assembly ("memory-safe") {
                                // if increasing, we add the liquidity delta, otherwise we subtract it
                                let liquidityDelta :=
                                    mul(signextend(15, tickValue), sub(increasing, iszero(increasing)))
                                liquidity := add(liquidity, liquidityDelta)
                            }

                            (StorageSlot tickFplFirstSlot, StorageSlot tickFplSecondSlot) =
                                CoreStorageLayout.poolTickFeesPerLiquidityOutsideSlot(poolId, nextTick);

                            if (feesAccessed == 0) {
                                inputTokenFeesPerLiquidity = uint256(
                                    CoreStorageLayout.poolFeesPerLiquiditySlot(poolId).add(LibBit.rawToUint(increasing))
                                        .load()
                                );
                                feesAccessed = 1;
                            }

                            uint256 globalFeesPerLiquidityOther = uint256(
                                CoreStorageLayout.poolFeesPerLiquiditySlot(poolId).add(LibBit.rawToUint(!increasing))
                                    .load()
                            );

                            // if increasing, it means the pool is receiving token1 so the input fees per liquidity is token1
                            if (increasing) {
                                tickFplFirstSlot.store(
                                    bytes32(globalFeesPerLiquidityOther - uint256(tickFplFirstSlot.load()))
                                );
                                tickFplSecondSlot.store(
                                    bytes32(inputTokenFeesPerLiquidity - uint256(tickFplSecondSlot.load()))
                                );
                            } else {
                                tickFplFirstSlot.store(
                                    bytes32(inputTokenFeesPerLiquidity - uint256(tickFplFirstSlot.load()))
                                );
                                tickFplSecondSlot.store(
                                    bytes32(globalFeesPerLiquidityOther - uint256(tickFplSecondSlot.load()))
                                );
                            }
                        }
                    } else if (sqrtRatio != sqrtRatioNext) {
                        sqrtRatio = sqrtRatioNext;
                        tick = sqrtRatioToTick(sqrtRatio);
                    }

                    if (amountRemaining == 0 || sqrtRatio == sqrtRatioLimit) {
                        break;
                    }
                }

                int128 calculatedAmountDelta =
                    SafeCastLib.toInt128(FixedPointMathLib.max(type(int128).min, calculatedAmount));

                int128 specifiedAmountDelta;
                int128 specifiedAmount = params.amount();
                assembly ("memory-safe") {
                    specifiedAmountDelta := sub(specifiedAmount, amountRemaining)
                }

                balanceUpdate = isToken1
                    ? createPoolBalanceUpdate(calculatedAmountDelta, specifiedAmountDelta)
                    : createPoolBalanceUpdate(specifiedAmountDelta, calculatedAmountDelta);

                stateAfter = createPoolState({_sqrtRatio: sqrtRatio, _tick: tick, _liquidity: liquidity});

                writePoolState(poolId, stateAfter);

                if (feesAccessed == 2) {
                    // this stores only the input token fees per liquidity
                    CoreStorageLayout.poolFeesPerLiquiditySlot(poolId).add(LibBit.rawToUint(increasing))
                        .store(bytes32(inputTokenFeesPerLiquidity));
                }

                _updatePairDebtWithNative(locker.id(), token0, token1, balanceUpdate.delta0(), balanceUpdate.delta1());

                assembly ("memory-safe") {
                    let o := mload(0x40)
                    mstore(o, shl(96, locker))
                    mstore(add(o, 20), poolId)
                    mstore(add(o, 52), balanceUpdate)
                    mstore(add(o, 84), stateAfter)
                    log0(o, 116)
                }
            }

            IExtension(config.extension()).maybeCallAfterSwap(locker, poolKey, params, balanceUpdate, stateAfter);

            assembly ("memory-safe") {
                mstore(0, balanceUpdate)
                mstore(32, stateAfter)
                return(0, 64)
            }
        }
    }
}

// SPDX-License-Identifier: ekubo-license-v1.eth
pragma solidity =0.8.33;

struct CallPoints {
    bool beforeInitializePool;
    bool afterInitializePool;
    bool beforeSwap;
    bool afterSwap;
    bool beforeUpdatePosition;
    bool afterUpdatePosition;
    bool beforeCollectFees;
    bool afterCollectFees;
}

using {eq, isValid, toUint8} for CallPoints global;

function eq(CallPoints memory a, CallPoints memory b) pure returns (bool) {
    return (a.beforeInitializePool == b.beforeInitializePool && a.afterInitializePool == b.afterInitializePool
            && a.beforeSwap == b.beforeSwap && a.afterSwap == b.afterSwap
            && a.beforeUpdatePosition == b.beforeUpdatePosition && a.afterUpdatePosition == b.afterUpdatePosition
            && a.beforeCollectFees == b.beforeCollectFees && a.afterCollectFees == b.afterCollectFees);
}

function isValid(CallPoints memory a) pure returns (bool) {
    return (a.beforeInitializePool || a.afterInitializePool || a.beforeSwap || a.afterSwap || a.beforeUpdatePosition
            || a.afterUpdatePosition || a.beforeCollectFees || a.afterCollectFees);
}

function toUint8(CallPoints memory callPoints) pure returns (uint8 b) {
    assembly ("memory-safe") {
        b := add(
            add(
                add(
                    add(
                        add(
                            add(
                                add(mload(callPoints), mul(128, mload(add(callPoints, 32)))),
                                mul(64, mload(add(callPoints, 64)))
                            ),
                            mul(32, mload(add(callPoints, 96)))
                        ),
                        mul(16, mload(add(callPoints, 128)))
                    ),
                    mul(8, mload(add(callPoints, 160)))
                ),
                mul(4, mload(add(callPoints, 192)))
            ),
            mul(2, mload(add(callPoints, 224)))
        )
    }
}

function addressToCallPoints(address a) pure returns (CallPoints memory result) {
    result = byteToCallPoints(uint8(uint160(a) >> 152));
}

function byteToCallPoints(uint8 b) pure returns (CallPoints memory result) {
    // note the order of bytes does not match the struct order of elements because we are matching the cairo implementation
    // which for legacy reasons has the fields in this order
    result = CallPoints({
        beforeInitializePool: (b & 1) != 0,
        afterInitializePool: (b & 128) != 0,
        beforeSwap: (b & 64) != 0,
        afterSwap: (b & 32) != 0,
        beforeUpdatePosition: (b & 16) != 0,
        afterUpdatePosition: (b & 8) != 0,
        beforeCollectFees: (b & 4) != 0,
        afterCollectFees: (b & 2) != 0
    });
}

File 3 of 33 : poolKey.sol
// SPDX-License-Identifier: ekubo-license-v1.eth
pragma solidity =0.8.33;

import {PoolId} from "./poolId.sol";
import {PoolConfig} from "./poolConfig.sol";

using {toPoolId, validate} for PoolKey global;

/// @notice Unique identifier for a pool containing token addresses and configuration
/// @dev Each pool has its own state associated with this key
struct PoolKey {
    /// @notice Address of token0 (must be < token1)
    address token0;
    /// @notice Address of token1 (must be > token0)
    address token1;
    /// @notice Packed configuration containing extension, fee, and tick spacing
    PoolConfig config;
}

/// @notice Thrown when tokens are not properly sorted (token0 >= token1)
error TokensMustBeSorted();

/// @notice Validates that a pool key is valid
/// @dev Checks that tokens are sorted and the config is valid
/// @param key The pool key to validate
function validate(PoolKey memory key) pure {
    if (key.token0 >= key.token1) revert TokensMustBeSorted();
    key.config.validate();
}

/// @notice Converts a pool key to a unique pool ID
/// @param key The pool key
/// @return result The unique pool ID (hash of the pool key)
function toPoolId(PoolKey memory key) pure returns (PoolId result) {
    assembly ("memory-safe") {
        // it's already copied into memory
        result := keccak256(key, 96)
    }
}

File 4 of 33 : poolConfig.sol
// SPDX-License-Identifier: ekubo-license-v1.eth
pragma solidity =0.8.33;

import {MIN_TICK, MAX_TICK, MAX_TICK_SPACING} from "../math/constants.sol";

/// @notice Pool configuration packed into a single bytes32
/// @dev Contains extension address (20 bytes), fee (8 bytes), and pool type config (4 bytes)
/// Pool type config (32 bits):
///   - Bit 31: discriminator (1 = concentrated, 0 = stableswap)
///   - For concentrated (bit 31 = 1): bits 30-0 are tick spacing
///   - For stableswap (bit 31 = 0): bits 30-24 are amplification factor, bits 23-0 are center tick
type PoolConfig is bytes32;

using {
    fee,
    extension,
    isConcentrated,
    isStableswap,
    isFullRange,
    concentratedTickSpacing,
    stableswapAmplification,
    stableswapCenterTick,
    stableswapActiveLiquidityTickRange,
    stableswapLiquidityWidth,
    validate,
    concentratedMaxLiquidityPerTick
} for PoolConfig global;

/// @notice Extracts the fee from a pool config
/// @param config The pool config
/// @return r The fee
function fee(PoolConfig config) pure returns (uint64 r) {
    assembly ("memory-safe") {
        r := and(shr(32, config), 0xffffffffffffffff)
    }
}

/// @notice Extracts the extension address from a pool config
/// @param config The pool config
/// @return r The extension address
function extension(PoolConfig config) pure returns (address r) {
    assembly ("memory-safe") {
        r := shr(96, config)
    }
}

/// @notice Checks if this is a concentrated liquidity pool
/// @param config The pool config
/// @return r True if the pool is concentrated liquidity
function isConcentrated(PoolConfig config) pure returns (bool r) {
    r = !config.isStableswap();
}

/// @notice Extracts the tick spacing from a concentrated liquidity pool config
/// @dev Only valid for concentrated liquidity pools (isConcentrated() == true)
/// @param config The pool config
/// @return r The tick spacing
function concentratedTickSpacing(PoolConfig config) pure returns (uint32 r) {
    assembly ("memory-safe") {
        // Extract lower 31 bits (bits 30-0)
        r := and(config, 0x7fffffff)
    }
}

/// @notice Checks if this is a stableswap pool
/// @param config The pool config
/// @return r True if the pool is stableswap
function isStableswap(PoolConfig config) pure returns (bool r) {
    assembly ("memory-safe") {
        // = iff bit 31 is not set
        r := iszero(and(0x80000000, config))
    }
}

/// @notice Determines if this pool is full range (special case of stableswap with amplification=0 and center=0)
/// @dev Full range can be slightly optimized in that we don't need to compute the sqrt ratio at the tick boundaries
/// @param config The pool config
/// @return r True if the pool is full range
function isFullRange(PoolConfig config) pure returns (bool r) {
    assembly ("memory-safe") {
        // Full range when all 32 bits are 0 (discriminator=0, amplification=0, center=0)
        r := iszero(and(config, 0xffffffff))
    }
}

/// @notice Extracts the amplification factor from a stableswap pool config
/// @dev Only valid for stableswap pools (isStableswap() == true)
/// @param config The pool config
/// @return r The amplification factor (0-127)
function stableswapAmplification(PoolConfig config) pure returns (uint8 r) {
    assembly ("memory-safe") {
        // Extract bits 30-24
        r := and(shr(24, config), 0x7f)
    }
}

/// @notice Extracts the center tick from a stableswap pool config
/// @dev Only valid for stableswap pools (isStableswap() == true)
/// @dev The 24-bit center tick is scaled by 16 to get the actual tick
/// @param config The pool config
/// @return r The center tick
function stableswapCenterTick(PoolConfig config) pure returns (int32 r) {
    assembly ("memory-safe") {
        // Extract bits 23-0 and sign extend to 32 bits (24-bit signed integer)
        // Then multiply by 16, since the value does not have full precision
        r := mul(signextend(2, and(config, 0xffffff)), 16)
    }
}

/// @notice Returns the width of the liquidity range
function stableswapLiquidityWidth(PoolConfig config) pure returns (uint256 width) {
    uint8 amp = config.stableswapAmplification();

    assembly ("memory-safe") {
        width := shr(amp, MAX_TICK)
    }
}

/// @notice Computes the tick range where liquidity is active for stableswap pools
/// @param config The pool config
/// @return lower The lower tick of the bounds
/// @return upper The upper tick of the bounds
function stableswapActiveLiquidityTickRange(PoolConfig config) pure returns (int32 lower, int32 upper) {
    int32 center = config.stableswapCenterTick();
    uint256 width = config.stableswapLiquidityWidth();

    assembly ("memory-safe") {
        lower := sub(center, width)
        lower := add(lower, mul(sgt(MIN_TICK, lower), sub(MIN_TICK, lower)))

        upper := add(center, width)
        upper := sub(upper, mul(sgt(upper, MAX_TICK), sub(upper, MAX_TICK)))
    }
}

/// @notice Creates a PoolConfig for a concentrated liquidity pool
/// @param _fee The fee for the pool
/// @param _tickSpacing The tick spacing for the pool
/// @param _extension The extension address for the pool
/// @return c The packed configuration
function createConcentratedPoolConfig(uint64 _fee, uint32 _tickSpacing, address _extension)
    pure
    returns (PoolConfig c)
{
    assembly ("memory-safe") {
        // Set bit 31 to 1 for concentrated liquidity, then OR with tick spacing (bits 30-0)
        let typeConfig := or(0x80000000, and(_tickSpacing, 0x7fffffff))
        c := or(or(shl(96, _extension), shl(32, and(_fee, 0xffffffffffffffff))), typeConfig)
    }
}

/// @notice Creates a PoolConfig for a stableswap pool
/// @param _fee The fee for the pool
/// @param _amplification The amplification factor (0-127)
/// @param _centerTick The center tick (will be divided by 16 and stored as 24-bit value)
/// @param _extension The extension address for the pool
/// @return c The packed configuration
function createStableswapPoolConfig(uint64 _fee, uint8 _amplification, int32 _centerTick, address _extension)
    pure
    returns (PoolConfig c)
{
    assembly ("memory-safe") {
        // Divide center tick by 16 to get 24-bit representation
        let stableswapCenterTick24 := sdiv(_centerTick, 16)
        // Pack: bit 31 = 0 (stableswap), bits 30-24 = amplification, bits 23-0 = center tick
        let typeConfig := or(shl(24, and(_amplification, 0x7f)), and(stableswapCenterTick24, 0xffffff))
        c := or(or(shl(96, _extension), shl(32, and(_fee, 0xffffffffffffffff))), typeConfig)
    }
}

/// @notice Creates a PoolConfig for a full range pool (stableswap with amplification=0, center=0)
/// @param _fee The fee for the pool
/// @param _extension The extension address for the pool
/// @return c The packed configuration
function createFullRangePoolConfig(uint64 _fee, address _extension) pure returns (PoolConfig c) {
    assembly ("memory-safe") {
        // All 32 bits of type config are 0 (discriminator=0, amplification=0, center=0)
        c := or(shl(96, _extension), shl(32, and(_fee, 0xffffffffffffffff)))
    }
}

/// @notice Computes the maximum liquidity per tick for a given concentrated liquidity pool configuration.
/// @dev Only valid for concentrated liquidity pools. Stableswap pools don't use ticks.
/// @dev Calculated as type(uint128).max / (1 + (MAX_TICK_MAGNITUDE / tickSpacing) * 2)
/// @param config The concentrated liquidity pool configuration
/// @return maxLiquidity The maximum liquidity allowed to reference each tick
function concentratedMaxLiquidityPerTick(PoolConfig config) pure returns (uint128 maxLiquidity) {
    uint32 _tickSpacing = config.concentratedTickSpacing();

    assembly ("memory-safe") {
        // Calculate total number of usable ticks: 1 + (MAX_TICK_MAGNITUDE / tickSpacing) * 2
        // This represents all ticks from -MAX_TICK_MAGNITUDE to +MAX_TICK_MAGNITUDE, and tick 0
        let numTicks := add(1, mul(div(MAX_TICK, _tickSpacing), 2))

        maxLiquidity := div(sub(shl(128, 1), 1), numTicks)
    }
}

/// @notice Thrown when tick spacing exceeds the maximum allowed value
error InvalidTickSpacing();

/// @notice Thrown when amplification factor exceeds the maximum allowed value
error InvalidStableswapAmplification();

/// @notice Thrown when center tick is not between min and max tick
error InvalidCenterTick();

/// @notice Validates that a pool config is properly formatted
/// @param config The config to validate
function validate(PoolConfig config) pure {
    if (config.isConcentrated()) {
        if (config.concentratedTickSpacing() > MAX_TICK_SPACING || config.concentratedTickSpacing() == 0) {
            revert InvalidTickSpacing();
        }
    } else {
        // Stableswap pool: validate amplification factor <= 26
        if (config.stableswapAmplification() > 26) {
            revert InvalidStableswapAmplification();
        }
        int32 centerTick = config.stableswapCenterTick();
        if (centerTick < MIN_TICK || centerTick > MAX_TICK) {
            revert InvalidCenterTick();
        }
    }
}

// SPDX-License-Identifier: ekubo-license-v1.eth
pragma solidity =0.8.33;

import {MIN_TICK, MAX_TICK} from "../math/constants.sol";
import {PoolConfig} from "./poolConfig.sol";

type PositionId is bytes32;

using {validate, salt, tickLower, tickUpper} for PositionId global;

function salt(PositionId positionId) pure returns (bytes24 v) {
    assembly ("memory-safe") {
        v := shl(64, shr(64, positionId))
    }
}

function tickLower(PositionId positionId) pure returns (int32 v) {
    assembly ("memory-safe") {
        // shift down, then signextend to 32 bits
        v := signextend(3, shr(32, positionId))
    }
}

function tickUpper(PositionId positionId) pure returns (int32 v) {
    assembly ("memory-safe") {
        // lowest 4 bytes, then signextend to 32 bits
        v := signextend(3, positionId)
    }
}

function createPositionId(bytes24 _salt, int32 _tickLower, int32 _tickUpper) pure returns (PositionId v) {
    assembly ("memory-safe") {
        // v = salt | (tickLower << 32) | tickUpper
        v := or(shl(64, shr(64, _salt)), or(shl(32, and(_tickLower, 0xFFFFFFFF)), and(_tickUpper, 0xFFFFFFFF)))
    }
}

/// @notice Thrown when the order of the position bounds is invalid, i.e. tickLower >= tickUpper
error BoundsOrder();
/// @notice Thrown when the bounds of the position are outside the pool's min/max tick range
error MinMaxBounds();
/// @notice Thrown when the ticks of the bounds do not align with tick spacing for concentrated pools
error BoundsTickSpacing();
/// @notice Thrown when stableswap pool positions are not at the min/max tick for the config
error StableswapMustBeFullRange();

function validate(PositionId positionId, PoolConfig config) pure {
    if (config.isConcentrated()) {
        if (positionId.tickLower() >= positionId.tickUpper()) revert BoundsOrder();
        if (positionId.tickLower() < MIN_TICK || positionId.tickUpper() > MAX_TICK) revert MinMaxBounds();
        int32 spacing = int32(config.concentratedTickSpacing());
        if (positionId.tickLower() % spacing != 0 || positionId.tickUpper() % spacing != 0) revert BoundsTickSpacing();
    } else {
        (int32 lower, int32 upper) = config.stableswapActiveLiquidityTickRange();
        // For stableswap pools, positions must be exactly min/max tick
        if (positionId.tickLower() != lower || positionId.tickUpper() != upper) revert StableswapMustBeFullRange();
    }
}

// SPDX-License-Identifier: ekubo-license-v1.eth
pragma solidity =0.8.33;

// The total fees per liquidity for each token.
// Since these are always read together we put them in a struct, even though they cannot be packed.
struct FeesPerLiquidity {
    uint256 value0;
    uint256 value1;
}

using {sub} for FeesPerLiquidity global;

function sub(FeesPerLiquidity memory a, FeesPerLiquidity memory b) pure returns (FeesPerLiquidity memory result) {
    assembly ("memory-safe") {
        mstore(result, sub(mload(a), mload(b)))
        mstore(add(result, 32), sub(mload(add(a, 32)), mload(add(b, 32))))
    }
}

function feesPerLiquidityFromAmounts(uint128 amount0, uint128 amount1, uint128 liquidity)
    pure
    returns (FeesPerLiquidity memory result)
{
    assembly ("memory-safe") {
        mstore(result, div(shl(128, amount0), liquidity))
        mstore(add(result, 32), div(shl(128, amount1), liquidity))
    }
}

File 7 of 33 : position.sol
// SPDX-License-Identifier: ekubo-license-v1.eth
pragma solidity =0.8.33;

import {FeesPerLiquidity} from "./feesPerLiquidity.sol";
import {FixedPointMathLib} from "solady/utils/FixedPointMathLib.sol";

// Position Type
// Represents a liquidity position in a pool
// Contains the position's liquidity amount and fee tracking information

/// @notice A liquidity position in a pool
/// @dev Tracks both the liquidity amount and the last known fees per liquidity for fee calculation
struct Position {
    /// @notice Extra data that can be set by the owner of a position
    bytes16 extraData;
    /// @notice Amount of liquidity in the position
    uint128 liquidity;
    /// @notice Snapshot of fees per liquidity when the position was last updated
    /// @dev Used to calculate fees owed to the position holder
    FeesPerLiquidity feesPerLiquidityInsideLast;
}

using {fees} for Position global;

/// @notice Calculates the fees owed to a position
/// @dev Returns the fee amounts of token0 and token1 owed to a position based on the given fees per liquidity inside snapshot
///      Note: if the computed fees overflow the uint128 type, it will return only the lower 128 bits. It is assumed that accumulated
///      fees will never exceed type(uint128).max.
/// @param position The position to calculate fees for
/// @param feesPerLiquidityInside Current fees per liquidity inside the position's bounds
/// @return Amount of token0 fees owed
/// @return Amount of token1 fees owed
function fees(Position memory position, FeesPerLiquidity memory feesPerLiquidityInside)
    pure
    returns (uint128, uint128)
{
    uint128 liquidity;
    uint256 difference0;
    uint256 difference1;
    assembly ("memory-safe") {
        liquidity := mload(add(position, 0x20))
        // feesPerLiquidityInsideLast is now at offset 0x40 due to extraData field
        let positionFpl := mload(add(position, 0x40))
        difference0 := sub(mload(feesPerLiquidityInside), mload(positionFpl))
        difference1 := sub(mload(add(feesPerLiquidityInside, 0x20)), mload(add(positionFpl, 0x20)))
    }

    return (
        uint128(FixedPointMathLib.fullMulDivN(difference0, liquidity, 128)),
        uint128(FixedPointMathLib.fullMulDivN(difference1, liquidity, 128))
    );
}

File 8 of 33 : ticks.sol
// SPDX-License-Identifier: ekubo-license-v1.eth
pragma solidity =0.8.33;

import {MAX_TICK_MAGNITUDE} from "./constants.sol";
import {FixedPointMathLib} from "solady/utils/FixedPointMathLib.sol";
import {LibBit} from "solady/utils/LibBit.sol";
import {SqrtRatio, toSqrtRatio} from "../types/sqrtRatio.sol";

// Tick Math Library
// Contains functions for converting between ticks and sqrt price ratios
// Ticks represent discrete price points, while sqrt ratios represent the actual prices
// The relationship is: sqrtRatio = sqrt(1.000001^tick)

/// @notice Thrown when a tick value is outside the valid range
/// @param tick The invalid tick value
error InvalidTick(int32 tick);

/// @notice Converts a tick to its corresponding sqrt price ratio
/// @dev Uses bit manipulation and precomputed constants for gas efficiency
/// @param tick The tick to convert (must be within MIN_TICK and MAX_TICK)
/// @return r The sqrt price ratio corresponding to the tick
function tickToSqrtRatio(int32 tick) pure returns (SqrtRatio r) {
    unchecked {
        uint256 t = FixedPointMathLib.abs(tick);
        if (t > MAX_TICK_MAGNITUDE) revert InvalidTick(tick);

        uint256 ratio;
        assembly ("memory-safe") {
            // bit 0 is handled with a single conditional subtract from 2^128
            ratio := sub(0x100000000000000000000000000000000, mul(and(t, 0x1), 0x8637b66cd638344daef276cd7c5))

            // -------- Gate 1: bits 1..7 (mask 0xFE) --------
            if and(t, 0xFE) {
                if and(t, 0x2) { ratio := shr(128, mul(ratio, 0xffffef390978c398134b4ff3764fe410)) }
                if and(t, 0x4) { ratio := shr(128, mul(ratio, 0xffffde72140b00a354bd3dc828e976c9)) }
                if and(t, 0x8) { ratio := shr(128, mul(ratio, 0xffffbce42c7be6c998ad6318193c0b18)) }
                if and(t, 0x10) { ratio := shr(128, mul(ratio, 0xffff79c86a8f6150a32d9778eceef97c)) }
                if and(t, 0x20) { ratio := shr(128, mul(ratio, 0xfffef3911b7cff24ba1b3dbb5f8f5974)) }
                if and(t, 0x40) { ratio := shr(128, mul(ratio, 0xfffde72350725cc4ea8feece3b5f13c8)) }
                if and(t, 0x80) { ratio := shr(128, mul(ratio, 0xfffbce4b06c196e9247ac87695d53c60)) }
            }

            // -------- Gate 2: bits 8..14 (mask 0x7F00) --------
            if and(t, 0x7F00) {
                if and(t, 0x100) { ratio := shr(128, mul(ratio, 0xfff79ca7a4d1bf1ee8556cea23cdbaa5)) }
                if and(t, 0x200) { ratio := shr(128, mul(ratio, 0xffef3995a5b6a6267530f207142a5764)) }
                if and(t, 0x400) { ratio := shr(128, mul(ratio, 0xffde7444b28145508125d10077ba83b8)) }
                if and(t, 0x800) { ratio := shr(128, mul(ratio, 0xffbceceeb791747f10df216f2e53ec57)) }
                if and(t, 0x1000) { ratio := shr(128, mul(ratio, 0xff79eb706b9a64c6431d76e63531e929)) }
                if and(t, 0x2000) { ratio := shr(128, mul(ratio, 0xfef41d1a5f2ae3a20676bec6f7f9459a)) }
                if and(t, 0x4000) { ratio := shr(128, mul(ratio, 0xfde95287d26d81bea159c37073122c73)) }
            }

            // -------- Gate 3: bits 15..20 (mask 0x1F8000) --------
            if and(t, 0x1F8000) {
                if and(t, 0x8000) { ratio := shr(128, mul(ratio, 0xfbd701c7cbc4c8a6bb81efd232d1e4e7)) }
                if and(t, 0x10000) { ratio := shr(128, mul(ratio, 0xf7bf5211c72f5185f372aeb1d48f937e)) }
                if and(t, 0x20000) { ratio := shr(128, mul(ratio, 0xefc2bf59df33ecc28125cf78ec4f167f)) }
                if and(t, 0x40000) { ratio := shr(128, mul(ratio, 0xe08d35706200796273f0b3a981d90cfd)) }
                if and(t, 0x80000) { ratio := shr(128, mul(ratio, 0xc4f76b68947482dc198a48a54348c4ed)) }
                if and(t, 0x100000) { ratio := shr(128, mul(ratio, 0x978bcb9894317807e5fa4498eee7c0fa)) }
            }

            // -------- Gate 4: bits 21..26 (mask 0x7E00000) --------
            if and(t, 0x7E00000) {
                if and(t, 0x200000) { ratio := shr(128, mul(ratio, 0x59b63684b86e9f486ec54727371ba6ca)) }
                if and(t, 0x400000) { ratio := shr(128, mul(ratio, 0x1f703399d88f6aa83a28b22d4a1f56e3)) }
                if and(t, 0x800000) { ratio := shr(128, mul(ratio, 0x3dc5dac7376e20fc8679758d1bcdcfc)) }
                if and(t, 0x1000000) { ratio := shr(128, mul(ratio, 0xee7e32d61fdb0a5e622b820f681d0)) }
                if and(t, 0x2000000) { ratio := shr(128, mul(ratio, 0xde2ee4bc381afa7089aa84bb66)) }
                if and(t, 0x4000000) { ratio := shr(128, mul(ratio, 0xc0d55d4d7152c25fb139)) }
            }

            // If original tick > 0, invert: ratio = maxUint / ratio
            if sgt(tick, 0) { ratio := div(not(0), ratio) }
        }

        r = toSqrtRatio(ratio, false);
    }
}

uint256 constant ONE_Q127 = 1 << 127;

// Convert ln(m) series to log2(m):  log2(m) = (2 / ln 2) * s.
// Precompute K = round((2 / ln 2) * 2^64) as a uint (Q64 scalar).
// K = 53226052391377289966  (≈ 0x2e2a8eca5705fc2ee)
uint256 constant K_2_OVER_LN2_X64 = 53226052391377289966;

// 2^64 / log2(sqrt(1.000001)) for converting from log base 2 in X64 to log base tick
int256 constant INV_LB_X64 = 25572630076711825471857579;

// Error bounds of the tick computation based on the number of iterations ~= +-0.002 ticks
int256 constant ERROR_BOUNDS_X128 = int256((uint256(1) << 128) / 485);

/// @notice Converts a sqrt price ratio to its corresponding tick
/// @dev Computes log2 via one normalization + atanh series (no per-bit squaring loop)
/// @param sqrtRatio The valid sqrt price ratio to convert
/// @return tick The tick corresponding to the sqrt ratio
function sqrtRatioToTick(SqrtRatio sqrtRatio) pure returns (int32 tick) {
    unchecked {
        uint256 sqrtRatioFixed = sqrtRatio.toFixed();

        // Normalize sign via reciprocal if < 1. Keep this branch-free.
        bool negative;
        uint256 x;
        uint256 hi;
        assembly ("memory-safe") {
            negative := iszero(shr(128, sqrtRatioFixed))
            // x = negative ? (type(uint256).max / R) : R
            x := add(div(sub(0, negative), sqrtRatioFixed), mul(iszero(negative), sqrtRatioFixed))
            // We know (x >> 128) != 0 because we reciprocated sqrtRatioFixed
            hi := shr(128, x)
        }

        // Integer part of log2 via CLZ: floor(log2(hi)) = 255 - clz(hi)
        uint256 msbHigh;
        assembly ("memory-safe") {
            msbHigh := sub(255, clz(hi))
        }

        // Reduce once so X ∈ [2^127, 2^128)  (Q1.127 mantissa)
        x = x >> (msbHigh + 1);

        // Fractional log2 using atanh on y = (m-1)/(m+1), m = X/2^127 ∈ [1,2)
        uint256 a = x - ONE_Q127; // (m - 1) * 2^127
        uint256 b = x + ONE_Q127; // (m + 1) * 2^127
        uint256 yQ = FixedPointMathLib.rawDiv(a << 127, b); // y in Q1.127

        // Build odd powers via y^2 ladder
        uint256 y2 = (yQ * yQ) >> 127; // y^2
        uint256 y3 = (yQ * y2) >> 127; // y^3
        uint256 y5 = (y3 * y2) >> 127; // y^5
        uint256 y7 = (y5 * y2) >> 127; // y^7
        uint256 y9 = (y7 * y2) >> 127; // y^9
        uint256 y11 = (y9 * y2) >> 127; // y^11
        uint256 y13 = (y11 * y2) >> 127; // y^13
        uint256 y15 = (y13 * y2) >> 127; // y^15

        // s = y + y^3/3 + y^5/5 + ... + y^15/15  (Q1.127)
        uint256 s = yQ + (y3 / 3) + (y5 / 5) + (y7 / 7) + (y9 / 9) + (y11 / 11) + (y13 / 13) + (y15 / 15);

        // fracX64 = ((2/ln2) * s) in Q64.64  =>  (s * K) >> 127
        uint256 fracX64 = (s * K_2_OVER_LN2_X64) >> 127;

        // Unsigned log2 in Q64.64
        uint256 log2Unsigned = (msbHigh << 64) + fracX64;

        // Map log2 to tick-space X128
        int256 base = negative ? -int256(log2Unsigned) : int256(log2Unsigned);

        int256 logBaseTickSizeX128 = base * INV_LB_X64;

        // Add error bounds to the computed logarithm
        int32 tickLow = int32((logBaseTickSizeX128 - ERROR_BOUNDS_X128) >> 128);
        tick = int32((logBaseTickSizeX128 + ERROR_BOUNDS_X128) >> 128);

        if (tick != tickLow) {
            // tickHigh overshoots
            if (tickToSqrtRatio(tick) > sqrtRatio) {
                tick = tickLow;
            }
        }
    }
}

// SPDX-License-Identifier: ekubo-license-v1.eth
pragma solidity =0.8.33;

import {PoolId} from "../types/poolId.sol";
import {PositionId} from "../types/positionId.sol";
import {StorageSlot} from "../types/storageSlot.sol";

/// @title Core Storage Layout
/// @notice Library providing functions to compute the storage locations for the Core contract
/// @dev Core uses a custom storage layout to avoid keccak's where possible.
///      For certain storage values, the pool id is used as a base offset and
///      we allocate the following relative offsets (starting from the pool id) as:
///        0: pool state
///        [FPL_OFFSET, FPL_OFFSET + 1]: fees per liquidity
///        [TICKS_OFFSET + MIN_TICK, TICKS_OFFSET + MAX_TICK]: tick info
///        [FPL_OUTSIDE_OFFSET_VALUE0 + MIN_TICK, FPL_OUTSIDE_OFFSET_VALUE0 + MAX_TICK]: fees per liquidity outside (value0)
///        [FPL_OUTSIDE_OFFSET_VALUE0 + FPL_OUTSIDE_OFFSET_VALUE1 + MIN_TICK, FPL_OUTSIDE_OFFSET_VALUE0 + FPL_OUTSIDE_OFFSET_VALUE1 + MAX_TICK]: fees per liquidity outside (value1)
///        [BITMAPS_OFFSET + FIRST_BITMAP_WORD, BITMAPS_OFFSET + LAST_BITMAP_WORD]: tick bitmaps
library CoreStorageLayout {
    /// @dev Generated using: cast keccak "CoreStorageLayout#FPL_OFFSET"
    uint256 internal constant FPL_OFFSET = 0xb09b03866d96933565a9435bfb511c8ac5b2be454285ca331201452704799f72;
    /// @dev Generated using: cast keccak "CoreStorageLayout#TICKS_OFFSET"
    uint256 internal constant TICKS_OFFSET = 0x435a5eb89a296820174331cf5a3902d9fca683928d56726d8e7acd6efb28c568;
    /// @dev Generated using: cast keccak "CoreStorageLayout#FPL_OUTSIDE_OFFSET_VALUE0"
    uint256 internal constant FPL_OUTSIDE_OFFSET_VALUE0 =
        0x5695060fdb9cfea656f872ae4887221aff7dbfefc45eaf753e4e70cdfb5cd19c;
    /// @dev Generated using: cast keccak "CoreStorageLayout#FPL_OUTSIDE_OFFSET_VALUE1"
    uint256 internal constant FPL_OUTSIDE_OFFSET_VALUE1 =
        0x7a2a03fc08af3dae7869678617dc8abe8f15a3b719b37ba108dba879571f8b02;
    /// @dev Generated using: cast keccak "CoreStorageLayout#BITMAPS_OFFSET"
    uint256 internal constant BITMAPS_OFFSET = 0x3def450d0010a2fef515ce5eba4b363b5a0f42fdd4c53e1c737975db05a2e3a5;

    /// @notice Computes the storage slot containing information on whether an extension is registered
    /// @param extension The extension address to check
    /// @return slot The storage slot in the Core contract
    function isExtensionRegisteredSlot(address extension) internal pure returns (StorageSlot slot) {
        assembly ("memory-safe") {
            mstore(0, extension)
            mstore(32, 0)
            slot := keccak256(0, 64)
        }
    }

    /// @notice Computes the storage slot of the current state of a pool
    /// @param poolId The unique identifier for the pool
    /// @return slot The storage slot in the Core contract
    function poolStateSlot(PoolId poolId) internal pure returns (StorageSlot slot) {
        slot = StorageSlot.wrap(PoolId.unwrap(poolId));
    }

    /// @notice Computes the storage slots of the current global fees per liquidity of a pool
    /// @param poolId The unique identifier for the pool
    /// @return firstSlot The first of two consecutive storage slots in the Core contract
    function poolFeesPerLiquiditySlot(PoolId poolId) internal pure returns (StorageSlot firstSlot) {
        assembly ("memory-safe") {
            firstSlot := add(poolId, FPL_OFFSET)
        }
    }

    /// @notice Computes the storage slot of tick information for a specific tick in a pool
    /// @param poolId The unique identifier for the pool
    /// @param tick The tick to query
    /// @return slot The storage slot in the Core contract
    function poolTicksSlot(PoolId poolId, int32 tick) internal pure returns (StorageSlot slot) {
        assembly ("memory-safe") {
            slot := add(poolId, add(tick, TICKS_OFFSET))
        }
    }

    /// @notice Computes the storage slots of the outside fees of a pool for a given tick
    /// @param poolId The unique identifier for the pool
    /// @param tick The tick to query
    /// @return firstSlot The first storage slot in the Core contract
    /// @return secondSlot The second storage slot in the Core contract
    function poolTickFeesPerLiquidityOutsideSlot(PoolId poolId, int32 tick)
        internal
        pure
        returns (StorageSlot firstSlot, StorageSlot secondSlot)
    {
        assembly ("memory-safe") {
            firstSlot := add(poolId, add(FPL_OUTSIDE_OFFSET_VALUE0, tick))
            secondSlot := add(firstSlot, FPL_OUTSIDE_OFFSET_VALUE1)
        }
    }

    /// @notice Computes the first storage slot of the tick bitmaps for a specific pool
    /// @param poolId The unique identifier for the pool
    /// @return firstSlot The first storage slot in the Core contract
    function tickBitmapsSlot(PoolId poolId) internal pure returns (StorageSlot firstSlot) {
        assembly ("memory-safe") {
            firstSlot := add(poolId, BITMAPS_OFFSET)
        }
    }

    /// @notice Computes the first storage slot of the position data for a specific position in a pool
    /// @param poolId The unique identifier for the pool
    /// @param owner The position owner
    /// @param positionId The unique identifier for the position
    /// @return firstSlot The first of three consecutive storage slots in the Core contract
    function poolPositionsSlot(PoolId poolId, address owner, PositionId positionId)
        internal
        pure
        returns (StorageSlot firstSlot)
    {
        assembly ("memory-safe") {
            let free := mload(0x40)
            mstore(free, positionId)
            mstore(add(free, 0x20), poolId)
            mstore(add(free, 0x40), owner)
            mstore(0, keccak256(free, 0x60))
            mstore(32, 1)
            firstSlot := keccak256(0, 64)
        }
    }

    /// @notice Computes the storage slot for saved balances
    /// @param owner The owner of the saved balances
    /// @param token0 The first token address
    /// @param token1 The second token address
    /// @param salt The salt used for the saved balance key
    /// @return slot The storage slot in the Core contract
    function savedBalancesSlot(address owner, address token0, address token1, bytes32 salt)
        internal
        pure
        returns (StorageSlot slot)
    {
        assembly ("memory-safe") {
            let free := mload(0x40)
            mstore(free, owner)
            mstore(add(free, 0x20), token0)
            mstore(add(free, 0x40), token1)
            mstore(add(free, 0x60), salt)
            slot := keccak256(free, 128)
        }
    }
}

// SPDX-License-Identifier: ekubo-license-v1.eth
pragma solidity =0.8.33;

import {IExtension} from "../interfaces/ICore.sol";
import {PoolKey} from "../types/poolKey.sol";
import {PositionId} from "../types/positionId.sol";
import {SqrtRatio} from "../types/sqrtRatio.sol";
import {PoolState} from "../types/poolState.sol";
import {SwapParameters} from "../types/swapParameters.sol";
import {Locker} from "../types/locker.sol";
import {PoolBalanceUpdate} from "../types/poolBalanceUpdate.sol";

/// @dev Contains methods for determining whether an extension should be called
library ExtensionCallPointsLib {
    function shouldCallBeforeInitializePool(IExtension extension, address initializer)
        internal
        pure
        returns (bool yes)
    {
        assembly ("memory-safe") {
            yes := and(shr(152, extension), iszero(eq(initializer, extension)))
        }
    }

    function maybeCallBeforeInitializePool(
        IExtension extension,
        address initializer,
        PoolKey memory poolKey,
        int32 tick
    ) internal {
        bool needCall = shouldCallBeforeInitializePool(extension, initializer);
        assembly ("memory-safe") {
            if needCall {
                let freeMem := mload(0x40)
                // cast sig "beforeInitializePool(address, (address, address, bytes32), int32)"
                mstore(freeMem, shl(224, 0x1fbbb462))
                mstore(add(freeMem, 4), initializer)
                mcopy(add(freeMem, 36), poolKey, 96)
                mstore(add(freeMem, 132), tick)
                // bubbles up the revert
                if iszero(call(gas(), extension, 0, freeMem, 164, 0, 0)) {
                    returndatacopy(freeMem, 0, returndatasize())
                    revert(freeMem, returndatasize())
                }
            }
        }
    }

    function shouldCallAfterInitializePool(IExtension extension, address initializer) internal pure returns (bool yes) {
        assembly ("memory-safe") {
            yes := and(shr(159, extension), iszero(eq(initializer, extension)))
        }
    }

    function maybeCallAfterInitializePool(
        IExtension extension,
        address initializer,
        PoolKey memory poolKey,
        int32 tick,
        SqrtRatio sqrtRatio
    ) internal {
        bool needCall = shouldCallAfterInitializePool(extension, initializer);
        assembly ("memory-safe") {
            if needCall {
                let freeMem := mload(0x40)
                // cast sig "afterInitializePool(address, (address, address, bytes32), int32, uint96)"
                mstore(freeMem, shl(224, 0x948374ff))
                mstore(add(freeMem, 4), initializer)
                mcopy(add(freeMem, 36), poolKey, 96)
                mstore(add(freeMem, 132), tick)
                mstore(add(freeMem, 164), sqrtRatio)
                // bubbles up the revert
                if iszero(call(gas(), extension, 0, freeMem, 196, 0, 0)) {
                    returndatacopy(freeMem, 0, returndatasize())
                    revert(freeMem, returndatasize())
                }
            }
        }
    }

    function shouldCallBeforeSwap(IExtension extension, Locker locker) internal pure returns (bool yes) {
        assembly ("memory-safe") {
            yes := and(shr(158, extension), iszero(eq(shl(96, locker), shl(96, extension))))
        }
    }

    function maybeCallBeforeSwap(IExtension extension, Locker locker, PoolKey memory poolKey, SwapParameters params)
        internal
    {
        bool needCall = shouldCallBeforeSwap(extension, locker);
        assembly ("memory-safe") {
            if needCall {
                let freeMem := mload(0x40)
                // cast sig "beforeSwap(bytes32,(address,address,bytes32),bytes32)"
                mstore(freeMem, shl(224, 0xca11dba7))
                mstore(add(freeMem, 4), locker)
                mcopy(add(freeMem, 36), poolKey, 96)
                mstore(add(freeMem, 132), params)
                // bubbles up the revert
                if iszero(call(gas(), extension, 0, freeMem, 164, 0, 0)) {
                    returndatacopy(freeMem, 0, returndatasize())
                    revert(freeMem, returndatasize())
                }
            }
        }
    }

    function shouldCallAfterSwap(IExtension extension, Locker locker) internal pure returns (bool yes) {
        assembly ("memory-safe") {
            yes := and(shr(157, extension), iszero(eq(shl(96, locker), shl(96, extension))))
        }
    }

    function maybeCallAfterSwap(
        IExtension extension,
        Locker locker,
        PoolKey memory poolKey,
        SwapParameters params,
        PoolBalanceUpdate balanceUpdate,
        PoolState stateAfter
    ) internal {
        bool needCall = shouldCallAfterSwap(extension, locker);
        assembly ("memory-safe") {
            if needCall {
                let freeMem := mload(0x40)
                // cast sig "afterSwap(bytes32,(address,address,bytes32),bytes32,bytes32,bytes32)"
                mstore(freeMem, shl(224, 0xa4e8f288))
                mstore(add(freeMem, 4), locker)
                mcopy(add(freeMem, 36), poolKey, 96)
                mstore(add(freeMem, 132), params)
                mstore(add(freeMem, 164), balanceUpdate)
                mstore(add(freeMem, 196), stateAfter)
                // bubbles up the revert
                if iszero(call(gas(), extension, 0, freeMem, 228, 0, 0)) {
                    returndatacopy(freeMem, 0, returndatasize())
                    revert(freeMem, returndatasize())
                }
            }
        }
    }

    function shouldCallBeforeUpdatePosition(IExtension extension, Locker locker) internal pure returns (bool yes) {
        assembly ("memory-safe") {
            yes := and(shr(156, extension), iszero(eq(shl(96, locker), shl(96, extension))))
        }
    }

    function maybeCallBeforeUpdatePosition(
        IExtension extension,
        Locker locker,
        PoolKey memory poolKey,
        PositionId positionId,
        int128 liquidityDelta
    ) internal {
        bool needCall = shouldCallBeforeUpdatePosition(extension, locker);
        assembly ("memory-safe") {
            if needCall {
                let freeMem := mload(0x40)
                // cast sig "beforeUpdatePosition(bytes32, (address,address,bytes32), bytes32, int128)"
                mstore(freeMem, shl(224, 0x0035c723))
                mstore(add(freeMem, 4), locker)
                mcopy(add(freeMem, 36), poolKey, 96)
                mstore(add(freeMem, 132), positionId)
                mstore(add(freeMem, 164), liquidityDelta)
                // bubbles up the revert
                if iszero(call(gas(), extension, 0, freeMem, 196, 0, 0)) {
                    returndatacopy(freeMem, 0, returndatasize())
                    revert(freeMem, returndatasize())
                }
            }
        }
    }

    function shouldCallAfterUpdatePosition(IExtension extension, Locker locker) internal pure returns (bool yes) {
        assembly ("memory-safe") {
            yes := and(shr(155, extension), iszero(eq(shl(96, locker), shl(96, extension))))
        }
    }

    function maybeCallAfterUpdatePosition(
        IExtension extension,
        Locker locker,
        PoolKey memory poolKey,
        PositionId positionId,
        int128 liquidityDelta,
        PoolBalanceUpdate balanceUpdate,
        PoolState stateAfter
    ) internal {
        bool needCall = shouldCallAfterUpdatePosition(extension, locker);
        assembly ("memory-safe") {
            if needCall {
                let freeMem := mload(0x40)
                // cast sig "afterUpdatePosition(bytes32,(address,address,bytes32),bytes32,int128,bytes32,bytes32)"
                mstore(freeMem, shl(224, 0x25fa4e69))
                mstore(add(freeMem, 4), locker)
                mcopy(add(freeMem, 36), poolKey, 96)
                mstore(add(freeMem, 132), positionId)
                mstore(add(freeMem, 164), liquidityDelta)
                mstore(add(freeMem, 196), balanceUpdate)
                mstore(add(freeMem, 228), stateAfter)
                // bubbles up the revert
                if iszero(call(gas(), extension, 0, freeMem, 260, 0, 0)) {
                    returndatacopy(freeMem, 0, returndatasize())
                    revert(freeMem, returndatasize())
                }
            }
        }
    }

    function shouldCallBeforeCollectFees(IExtension extension, Locker locker) internal pure returns (bool yes) {
        assembly ("memory-safe") {
            yes := and(shr(154, extension), iszero(eq(shl(96, locker), shl(96, extension))))
        }
    }

    function maybeCallBeforeCollectFees(
        IExtension extension,
        Locker locker,
        PoolKey memory poolKey,
        PositionId positionId
    ) internal {
        bool needCall = shouldCallBeforeCollectFees(extension, locker);
        assembly ("memory-safe") {
            if needCall {
                let freeMem := mload(0x40)
                // cast sig "beforeCollectFees(bytes32, (address,address,bytes32), bytes32)"
                mstore(freeMem, shl(224, 0xdf65d8d1))
                mstore(add(freeMem, 4), locker)
                mcopy(add(freeMem, 36), poolKey, 96)
                mstore(add(freeMem, 132), positionId)
                // bubbles up the revert
                if iszero(call(gas(), extension, 0, freeMem, 164, 0, 0)) {
                    returndatacopy(freeMem, 0, returndatasize())
                    revert(freeMem, returndatasize())
                }
            }
        }
    }

    function shouldCallAfterCollectFees(IExtension extension, Locker locker) internal pure returns (bool yes) {
        assembly ("memory-safe") {
            yes := and(shr(153, extension), iszero(eq(shl(96, locker), shl(96, extension))))
        }
    }

    function maybeCallAfterCollectFees(
        IExtension extension,
        Locker locker,
        PoolKey memory poolKey,
        PositionId positionId,
        uint128 amount0,
        uint128 amount1
    ) internal {
        bool needCall = shouldCallAfterCollectFees(extension, locker);
        assembly ("memory-safe") {
            if needCall {
                let freeMem := mload(0x40)
                // cast sig "afterCollectFees(bytes32, (address,address,bytes32), bytes32, uint128, uint128)"
                mstore(freeMem, shl(224, 0x751fd5df))
                mstore(add(freeMem, 4), locker)
                mcopy(add(freeMem, 36), poolKey, 96)
                mstore(add(freeMem, 132), positionId)
                mstore(add(freeMem, 164), amount0)
                mstore(add(freeMem, 196), amount1)
                // bubbles up the revert
                if iszero(call(gas(), extension, 0, freeMem, 228, 0, 0)) {
                    returndatacopy(freeMem, 0, returndatasize())
                    revert(freeMem, returndatasize())
                }
            }
        }
    }
}

File 11 of 33 : FixedPointMathLib.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Arithmetic library with operations for fixed-point numbers.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/FixedPointMathLib.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol)
library FixedPointMathLib {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The operation failed, as the output exceeds the maximum value of uint256.
    error ExpOverflow();

    /// @dev The operation failed, as the output exceeds the maximum value of uint256.
    error FactorialOverflow();

    /// @dev The operation failed, due to an overflow.
    error RPowOverflow();

    /// @dev The mantissa is too big to fit.
    error MantissaOverflow();

    /// @dev The operation failed, due to an multiplication overflow.
    error MulWadFailed();

    /// @dev The operation failed, due to an multiplication overflow.
    error SMulWadFailed();

    /// @dev The operation failed, either due to a multiplication overflow, or a division by a zero.
    error DivWadFailed();

    /// @dev The operation failed, either due to a multiplication overflow, or a division by a zero.
    error SDivWadFailed();

    /// @dev The operation failed, either due to a multiplication overflow, or a division by a zero.
    error MulDivFailed();

    /// @dev The division failed, as the denominator is zero.
    error DivFailed();

    /// @dev The full precision multiply-divide operation failed, either due
    /// to the result being larger than 256 bits, or a division by a zero.
    error FullMulDivFailed();

    /// @dev The output is undefined, as the input is less-than-or-equal to zero.
    error LnWadUndefined();

    /// @dev The input outside the acceptable domain.
    error OutOfDomain();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         CONSTANTS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

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

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*              SIMPLIFIED FIXED POINT OPERATIONS             */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Equivalent to `(x * y) / WAD` rounded down.
    function mulWad(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to `require(y == 0 || x <= type(uint256).max / y)`.
            if gt(x, div(not(0), y)) {
                if y {
                    mstore(0x00, 0xbac65e5b) // `MulWadFailed()`.
                    revert(0x1c, 0x04)
                }
            }
            z := div(mul(x, y), WAD)
        }
    }

    /// @dev Equivalent to `(x * y) / WAD` rounded down.
    function sMulWad(int256 x, int256 y) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mul(x, y)
            // Equivalent to `require((x == 0 || z / x == y) && !(x == -1 && y == type(int256).min))`.
            if iszero(gt(or(iszero(x), eq(sdiv(z, x), y)), lt(not(x), eq(y, shl(255, 1))))) {
                mstore(0x00, 0xedcd4dd4) // `SMulWadFailed()`.
                revert(0x1c, 0x04)
            }
            z := sdiv(z, WAD)
        }
    }

    /// @dev Equivalent to `(x * y) / WAD` rounded down, but without overflow checks.
    function rawMulWad(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := div(mul(x, y), WAD)
        }
    }

    /// @dev Equivalent to `(x * y) / WAD` rounded down, but without overflow checks.
    function rawSMulWad(int256 x, int256 y) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := sdiv(mul(x, y), WAD)
        }
    }

    /// @dev Equivalent to `(x * y) / WAD` rounded up.
    function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mul(x, y)
            // Equivalent to `require(y == 0 || x <= type(uint256).max / y)`.
            if iszero(eq(div(z, y), x)) {
                if y {
                    mstore(0x00, 0xbac65e5b) // `MulWadFailed()`.
                    revert(0x1c, 0x04)
                }
            }
            z := add(iszero(iszero(mod(z, WAD))), div(z, WAD))
        }
    }

    /// @dev Equivalent to `(x * y) / WAD` rounded up, but without overflow checks.
    function rawMulWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := add(iszero(iszero(mod(mul(x, y), WAD))), div(mul(x, y), WAD))
        }
    }

    /// @dev Equivalent to `(x * WAD) / y` rounded down.
    function divWad(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to `require(y != 0 && x <= type(uint256).max / WAD)`.
            if iszero(mul(y, lt(x, add(1, div(not(0), WAD))))) {
                mstore(0x00, 0x7c5f487d) // `DivWadFailed()`.
                revert(0x1c, 0x04)
            }
            z := div(mul(x, WAD), y)
        }
    }

    /// @dev Equivalent to `(x * WAD) / y` rounded down.
    function sDivWad(int256 x, int256 y) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mul(x, WAD)
            // Equivalent to `require(y != 0 && ((x * WAD) / WAD == x))`.
            if iszero(mul(y, eq(sdiv(z, WAD), x))) {
                mstore(0x00, 0x5c43740d) // `SDivWadFailed()`.
                revert(0x1c, 0x04)
            }
            z := sdiv(z, y)
        }
    }

    /// @dev Equivalent to `(x * WAD) / y` rounded down, but without overflow and divide by zero checks.
    function rawDivWad(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := div(mul(x, WAD), y)
        }
    }

    /// @dev Equivalent to `(x * WAD) / y` rounded down, but without overflow and divide by zero checks.
    function rawSDivWad(int256 x, int256 y) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := sdiv(mul(x, WAD), y)
        }
    }

    /// @dev Equivalent to `(x * WAD) / y` rounded up.
    function divWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to `require(y != 0 && x <= type(uint256).max / WAD)`.
            if iszero(mul(y, lt(x, add(1, div(not(0), WAD))))) {
                mstore(0x00, 0x7c5f487d) // `DivWadFailed()`.
                revert(0x1c, 0x04)
            }
            z := add(iszero(iszero(mod(mul(x, WAD), y))), div(mul(x, WAD), y))
        }
    }

    /// @dev Equivalent to `(x * WAD) / y` rounded up, but without overflow and divide by zero checks.
    function rawDivWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := add(iszero(iszero(mod(mul(x, WAD), y))), div(mul(x, WAD), y))
        }
    }

    /// @dev Equivalent to `x` to the power of `y`.
    /// because `x ** y = (e ** ln(x)) ** y = e ** (ln(x) * y)`.
    /// Note: This function is an approximation.
    function powWad(int256 x, int256 y) internal pure returns (int256) {
        // Using `ln(x)` means `x` must be greater than 0.
        return expWad((lnWad(x) * y) / int256(WAD));
    }

    /// @dev Returns `exp(x)`, denominated in `WAD`.
    /// Credit to Remco Bloemen under MIT license: https://2π.com/22/exp-ln
    /// Note: This function is an approximation. Monotonically increasing.
    function expWad(int256 x) internal pure returns (int256 r) {
        unchecked {
            // When the result is less than 0.5 we return zero.
            // This happens when `x <= (log(1e-18) * 1e18) ~ -4.15e19`.
            if (x <= -41446531673892822313) return r;

            /// @solidity memory-safe-assembly
            assembly {
                // When the result is greater than `(2**255 - 1) / 1e18` we can not represent it as
                // an int. This happens when `x >= floor(log((2**255 - 1) / 1e18) * 1e18) ≈ 135`.
                if iszero(slt(x, 135305999368893231589)) {
                    mstore(0x00, 0xa37bfec9) // `ExpOverflow()`.
                    revert(0x1c, 0x04)
                }
            }

            // `x` is now in the range `(-42, 136) * 1e18`. Convert to `(-42, 136) * 2**96`
            // for more intermediate precision and a binary basis. This base conversion
            // is a multiplication by 1e18 / 2**96 = 5**18 / 2**78.
            x = (x << 78) / 5 ** 18;

            // Reduce range of x to (-½ ln 2, ½ ln 2) * 2**96 by factoring out powers
            // of two such that exp(x) = exp(x') * 2**k, where k is an integer.
            // Solving this gives k = round(x / log(2)) and x' = x - k * log(2).
            int256 k = ((x << 96) / 54916777467707473351141471128 + 2 ** 95) >> 96;
            x = x - k * 54916777467707473351141471128;

            // `k` is in the range `[-61, 195]`.

            // Evaluate using a (6, 7)-term rational approximation.
            // `p` is made monic, we'll multiply by a scale factor later.
            int256 y = x + 1346386616545796478920950773328;
            y = ((y * x) >> 96) + 57155421227552351082224309758442;
            int256 p = y + x - 94201549194550492254356042504812;
            p = ((p * y) >> 96) + 28719021644029726153956944680412240;
            p = p * x + (4385272521454847904659076985693276 << 96);

            // We leave `p` in `2**192` basis so we don't need to scale it back up for the division.
            int256 q = x - 2855989394907223263936484059900;
            q = ((q * x) >> 96) + 50020603652535783019961831881945;
            q = ((q * x) >> 96) - 533845033583426703283633433725380;
            q = ((q * x) >> 96) + 3604857256930695427073651918091429;
            q = ((q * x) >> 96) - 14423608567350463180887372962807573;
            q = ((q * x) >> 96) + 26449188498355588339934803723976023;

            /// @solidity memory-safe-assembly
            assembly {
                // Div in assembly because solidity adds a zero check despite the unchecked.
                // The q polynomial won't have zeros in the domain as all its roots are complex.
                // No scaling is necessary because p is already `2**96` too large.
                r := sdiv(p, q)
            }

            // r should be in the range `(0.09, 0.25) * 2**96`.

            // We now need to multiply r by:
            // - The scale factor `s ≈ 6.031367120`.
            // - The `2**k` factor from the range reduction.
            // - The `1e18 / 2**96` factor for base conversion.
            // We do this all at once, with an intermediate result in `2**213`
            // basis, so the final right shift is always by a positive amount.
            r = int256(
                (uint256(r) * 3822833074963236453042738258902158003155416615667) >> uint256(195 - k)
            );
        }
    }

    /// @dev Returns `ln(x)`, denominated in `WAD`.
    /// Credit to Remco Bloemen under MIT license: https://2π.com/22/exp-ln
    /// Note: This function is an approximation. Monotonically increasing.
    function lnWad(int256 x) internal pure returns (int256 r) {
        /// @solidity memory-safe-assembly
        assembly {
            // We want to convert `x` from `10**18` fixed point to `2**96` fixed point.
            // We do this by multiplying by `2**96 / 10**18`. But since
            // `ln(x * C) = ln(x) + ln(C)`, we can simply do nothing here
            // and add `ln(2**96 / 10**18)` at the end.

            // Compute `k = log2(x) - 96`, `r = 159 - k = 255 - log2(x) = 255 ^ log2(x)`.
            r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
            r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
            r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
            r := or(r, shl(4, lt(0xffff, shr(r, x))))
            r := or(r, shl(3, lt(0xff, shr(r, x))))
            // We place the check here for more optimal stack operations.
            if iszero(sgt(x, 0)) {
                mstore(0x00, 0x1615e638) // `LnWadUndefined()`.
                revert(0x1c, 0x04)
            }
            // forgefmt: disable-next-item
            r := xor(r, byte(and(0x1f, shr(shr(r, x), 0x8421084210842108cc6318c6db6d54be)),
                0xf8f9f9faf9fdfafbf9fdfcfdfafbfcfef9fafdfafcfcfbfefafafcfbffffffff))

            // Reduce range of x to (1, 2) * 2**96
            // ln(2^k * x) = k * ln(2) + ln(x)
            x := shr(159, shl(r, x))

            // Evaluate using a (8, 8)-term rational approximation.
            // `p` is made monic, we will multiply by a scale factor later.
            // forgefmt: disable-next-item
            let p := sub( // This heavily nested expression is to avoid stack-too-deep for via-ir.
                sar(96, mul(add(43456485725739037958740375743393,
                sar(96, mul(add(24828157081833163892658089445524,
                sar(96, mul(add(3273285459638523848632254066296,
                    x), x))), x))), x)), 11111509109440967052023855526967)
            p := sub(sar(96, mul(p, x)), 45023709667254063763336534515857)
            p := sub(sar(96, mul(p, x)), 14706773417378608786704636184526)
            p := sub(mul(p, x), shl(96, 795164235651350426258249787498))
            // We leave `p` in `2**192` basis so we don't need to scale it back up for the division.

            // `q` is monic by convention.
            let q := add(5573035233440673466300451813936, x)
            q := add(71694874799317883764090561454958, sar(96, mul(x, q)))
            q := add(283447036172924575727196451306956, sar(96, mul(x, q)))
            q := add(401686690394027663651624208769553, sar(96, mul(x, q)))
            q := add(204048457590392012362485061816622, sar(96, mul(x, q)))
            q := add(31853899698501571402653359427138, sar(96, mul(x, q)))
            q := add(909429971244387300277376558375, sar(96, mul(x, q)))

            // `p / q` is in the range `(0, 0.125) * 2**96`.

            // Finalization, we need to:
            // - Multiply by the scale factor `s = 5.549…`.
            // - Add `ln(2**96 / 10**18)`.
            // - Add `k * ln(2)`.
            // - Multiply by `10**18 / 2**96 = 5**18 >> 78`.

            // The q polynomial is known not to have zeros in the domain.
            // No scaling required because p is already `2**96` too large.
            p := sdiv(p, q)
            // Multiply by the scaling factor: `s * 5**18 * 2**96`, base is now `5**18 * 2**192`.
            p := mul(1677202110996718588342820967067443963516166, p)
            // Add `ln(2) * k * 5**18 * 2**192`.
            // forgefmt: disable-next-item
            p := add(mul(16597577552685614221487285958193947469193820559219878177908093499208371, sub(159, r)), p)
            // Add `ln(2**96 / 10**18) * 5**18 * 2**192`.
            p := add(600920179829731861736702779321621459595472258049074101567377883020018308, p)
            // Base conversion: mul `2**18 / 2**192`.
            r := sar(174, p)
        }
    }

    /// @dev Returns `W_0(x)`, denominated in `WAD`.
    /// See: https://en.wikipedia.org/wiki/Lambert_W_function
    /// a.k.a. Product log function. This is an approximation of the principal branch.
    /// Note: This function is an approximation. Monotonically increasing.
    function lambertW0Wad(int256 x) internal pure returns (int256 w) {
        // forgefmt: disable-next-item
        unchecked {
            if ((w = x) <= -367879441171442322) revert OutOfDomain(); // `x` less than `-1/e`.
            (int256 wad, int256 p) = (int256(WAD), x);
            uint256 c; // Whether we need to avoid catastrophic cancellation.
            uint256 i = 4; // Number of iterations.
            if (w <= 0x1ffffffffffff) {
                if (-0x4000000000000 <= w) {
                    i = 1; // Inputs near zero only take one step to converge.
                } else if (w <= -0x3ffffffffffffff) {
                    i = 32; // Inputs near `-1/e` take very long to converge.
                }
            } else if (uint256(w >> 63) == uint256(0)) {
                /// @solidity memory-safe-assembly
                assembly {
                    // Inline log2 for more performance, since the range is small.
                    let v := shr(49, w)
                    let l := shl(3, lt(0xff, v))
                    l := add(or(l, byte(and(0x1f, shr(shr(l, v), 0x8421084210842108cc6318c6db6d54be)),
                        0x0706060506020504060203020504030106050205030304010505030400000000)), 49)
                    w := sdiv(shl(l, 7), byte(sub(l, 31), 0x0303030303030303040506080c13))
                    c := gt(l, 60)
                    i := add(2, add(gt(l, 53), c))
                }
            } else {
                int256 ll = lnWad(w = lnWad(w));
                /// @solidity memory-safe-assembly
                assembly {
                    // `w = ln(x) - ln(ln(x)) + b * ln(ln(x)) / ln(x)`.
                    w := add(sdiv(mul(ll, 1023715080943847266), w), sub(w, ll))
                    i := add(3, iszero(shr(68, x)))
                    c := iszero(shr(143, x))
                }
                if (c == uint256(0)) {
                    do { // If `x` is big, use Newton's so that intermediate values won't overflow.
                        int256 e = expWad(w);
                        /// @solidity memory-safe-assembly
                        assembly {
                            let t := mul(w, div(e, wad))
                            w := sub(w, sdiv(sub(t, x), div(add(e, t), wad)))
                        }
                        if (p <= w) break;
                        p = w;
                    } while (--i != uint256(0));
                    /// @solidity memory-safe-assembly
                    assembly {
                        w := sub(w, sgt(w, 2))
                    }
                    return w;
                }
            }
            do { // Otherwise, use Halley's for faster convergence.
                int256 e = expWad(w);
                /// @solidity memory-safe-assembly
                assembly {
                    let t := add(w, wad)
                    let s := sub(mul(w, e), mul(x, wad))
                    w := sub(w, sdiv(mul(s, wad), sub(mul(e, t), sdiv(mul(add(t, wad), s), add(t, t)))))
                }
                if (p <= w) break;
                p = w;
            } while (--i != c);
            /// @solidity memory-safe-assembly
            assembly {
                w := sub(w, sgt(w, 2))
            }
            // For certain ranges of `x`, we'll use the quadratic-rate recursive formula of
            // R. Iacono and J.P. Boyd for the last iteration, to avoid catastrophic cancellation.
            if (c == uint256(0)) return w;
            int256 t = w | 1;
            /// @solidity memory-safe-assembly
            assembly {
                x := sdiv(mul(x, wad), t)
            }
            x = (t * (wad + lnWad(x)));
            /// @solidity memory-safe-assembly
            assembly {
                w := sdiv(x, add(wad, t))
            }
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  GENERAL NUMBER UTILITIES                  */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns `a * b == x * y`, with full precision.
    function fullMulEq(uint256 a, uint256 b, uint256 x, uint256 y)
        internal
        pure
        returns (bool result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            result := and(eq(mul(a, b), mul(x, y)), eq(mulmod(x, y, not(0)), mulmod(a, b, not(0))))
        }
    }

    /// @dev Calculates `floor(x * y / d)` with full precision.
    /// Throws if result overflows a uint256 or when `d` is zero.
    /// Credit to Remco Bloemen under MIT license: https://2π.com/21/muldiv
    function fullMulDiv(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // 512-bit multiply `[p1 p0] = x * y`.
            // Compute the product mod `2**256` and mod `2**256 - 1`
            // then use the Chinese Remainder Theorem to reconstruct
            // the 512 bit result. The result is stored in two 256
            // variables such that `product = p1 * 2**256 + p0`.

            // Temporarily use `z` as `p0` to save gas.
            z := mul(x, y) // Lower 256 bits of `x * y`.
            for {} 1 {} {
                // If overflows.
                if iszero(mul(or(iszero(x), eq(div(z, x), y)), d)) {
                    let mm := mulmod(x, y, not(0))
                    let p1 := sub(mm, add(z, lt(mm, z))) // Upper 256 bits of `x * y`.

                    /*------------------- 512 by 256 division --------------------*/

                    // Make division exact by subtracting the remainder from `[p1 p0]`.
                    let r := mulmod(x, y, d) // Compute remainder using mulmod.
                    let t := and(d, sub(0, d)) // The least significant bit of `d`. `t >= 1`.
                    // Make sure `z` is less than `2**256`. Also prevents `d == 0`.
                    // Placing the check here seems to give more optimal stack operations.
                    if iszero(gt(d, p1)) {
                        mstore(0x00, 0xae47f702) // `FullMulDivFailed()`.
                        revert(0x1c, 0x04)
                    }
                    d := div(d, t) // Divide `d` by `t`, which is a power of two.
                    // Invert `d mod 2**256`
                    // Now that `d` is an odd number, it has an inverse
                    // modulo `2**256` such that `d * inv = 1 mod 2**256`.
                    // Compute the inverse by starting with a seed that is correct
                    // correct for four bits. That is, `d * inv = 1 mod 2**4`.
                    let inv := xor(2, mul(3, d))
                    // Now use Newton-Raphson iteration to improve the precision.
                    // Thanks to Hensel's lifting lemma, this also works in modular
                    // arithmetic, doubling the correct bits in each step.
                    inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**8
                    inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**16
                    inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**32
                    inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**64
                    inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**128
                    z :=
                        mul(
                            // Divide [p1 p0] by the factors of two.
                            // Shift in bits from `p1` into `p0`. For this we need
                            // to flip `t` such that it is `2**256 / t`.
                            or(mul(sub(p1, gt(r, z)), add(div(sub(0, t), t), 1)), div(sub(z, r), t)),
                            mul(sub(2, mul(d, inv)), inv) // inverse mod 2**256
                        )
                    break
                }
                z := div(z, d)
                break
            }
        }
    }

    /// @dev Calculates `floor(x * y / d)` with full precision.
    /// Behavior is undefined if `d` is zero or the final result cannot fit in 256 bits.
    /// Performs the full 512 bit calculation regardless.
    function fullMulDivUnchecked(uint256 x, uint256 y, uint256 d)
        internal
        pure
        returns (uint256 z)
    {
        /// @solidity memory-safe-assembly
        assembly {
            z := mul(x, y)
            let mm := mulmod(x, y, not(0))
            let p1 := sub(mm, add(z, lt(mm, z)))
            let t := and(d, sub(0, d))
            let r := mulmod(x, y, d)
            d := div(d, t)
            let inv := xor(2, mul(3, d))
            inv := mul(inv, sub(2, mul(d, inv)))
            inv := mul(inv, sub(2, mul(d, inv)))
            inv := mul(inv, sub(2, mul(d, inv)))
            inv := mul(inv, sub(2, mul(d, inv)))
            inv := mul(inv, sub(2, mul(d, inv)))
            z :=
                mul(
                    or(mul(sub(p1, gt(r, z)), add(div(sub(0, t), t), 1)), div(sub(z, r), t)),
                    mul(sub(2, mul(d, inv)), inv)
                )
        }
    }

    /// @dev Calculates `floor(x * y / d)` with full precision, rounded up.
    /// Throws if result overflows a uint256 or when `d` is zero.
    /// Credit to Uniswap-v3-core under MIT license:
    /// https://github.com/Uniswap/v3-core/blob/main/contracts/libraries/FullMath.sol
    function fullMulDivUp(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
        z = fullMulDiv(x, y, d);
        /// @solidity memory-safe-assembly
        assembly {
            if mulmod(x, y, d) {
                z := add(z, 1)
                if iszero(z) {
                    mstore(0x00, 0xae47f702) // `FullMulDivFailed()`.
                    revert(0x1c, 0x04)
                }
            }
        }
    }

    /// @dev Calculates `floor(x * y / 2 ** n)` with full precision.
    /// Throws if result overflows a uint256.
    /// Credit to Philogy under MIT license:
    /// https://github.com/SorellaLabs/angstrom/blob/main/contracts/src/libraries/X128MathLib.sol
    function fullMulDivN(uint256 x, uint256 y, uint8 n) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Temporarily use `z` as `p0` to save gas.
            z := mul(x, y) // Lower 256 bits of `x * y`. We'll call this `z`.
            for {} 1 {} {
                if iszero(or(iszero(x), eq(div(z, x), y))) {
                    let k := and(n, 0xff) // `n`, cleaned.
                    let mm := mulmod(x, y, not(0))
                    let p1 := sub(mm, add(z, lt(mm, z))) // Upper 256 bits of `x * y`.
                    //         |      p1     |      z     |
                    // Before: | p1_0 ¦ p1_1 | z_0  ¦ z_1 |
                    // Final:  |   0  ¦ p1_0 | p1_1 ¦ z_0 |
                    // Check that final `z` doesn't overflow by checking that p1_0 = 0.
                    if iszero(shr(k, p1)) {
                        z := add(shl(sub(256, k), p1), shr(k, z))
                        break
                    }
                    mstore(0x00, 0xae47f702) // `FullMulDivFailed()`.
                    revert(0x1c, 0x04)
                }
                z := shr(and(n, 0xff), z)
                break
            }
        }
    }

    /// @dev Returns `floor(x * y / d)`.
    /// Reverts if `x * y` overflows, or `d` is zero.
    function mulDiv(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mul(x, y)
            // Equivalent to `require(d != 0 && (y == 0 || x <= type(uint256).max / y))`.
            if iszero(mul(or(iszero(x), eq(div(z, x), y)), d)) {
                mstore(0x00, 0xad251c27) // `MulDivFailed()`.
                revert(0x1c, 0x04)
            }
            z := div(z, d)
        }
    }

    /// @dev Returns `ceil(x * y / d)`.
    /// Reverts if `x * y` overflows, or `d` is zero.
    function mulDivUp(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mul(x, y)
            // Equivalent to `require(d != 0 && (y == 0 || x <= type(uint256).max / y))`.
            if iszero(mul(or(iszero(x), eq(div(z, x), y)), d)) {
                mstore(0x00, 0xad251c27) // `MulDivFailed()`.
                revert(0x1c, 0x04)
            }
            z := add(iszero(iszero(mod(z, d))), div(z, d))
        }
    }

    /// @dev Returns `x`, the modular multiplicative inverse of `a`, such that `(a * x) % n == 1`.
    function invMod(uint256 a, uint256 n) internal pure returns (uint256 x) {
        /// @solidity memory-safe-assembly
        assembly {
            let g := n
            let r := mod(a, n)
            for { let y := 1 } 1 {} {
                let q := div(g, r)
                let t := g
                g := r
                r := sub(t, mul(r, q))
                let u := x
                x := y
                y := sub(u, mul(y, q))
                if iszero(r) { break }
            }
            x := mul(eq(g, 1), add(x, mul(slt(x, 0), n)))
        }
    }

    /// @dev Returns `ceil(x / d)`.
    /// Reverts if `d` is zero.
    function divUp(uint256 x, uint256 d) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(d) {
                mstore(0x00, 0x65244e4e) // `DivFailed()`.
                revert(0x1c, 0x04)
            }
            z := add(iszero(iszero(mod(x, d))), div(x, d))
        }
    }

    /// @dev Returns `max(0, x - y)`. Alias for `saturatingSub`.
    function zeroFloorSub(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mul(gt(x, y), sub(x, y))
        }
    }

    /// @dev Returns `max(0, x - y)`.
    function saturatingSub(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mul(gt(x, y), sub(x, y))
        }
    }

    /// @dev Returns `min(2 ** 256 - 1, x + y)`.
    function saturatingAdd(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := or(sub(0, lt(add(x, y), x)), add(x, y))
        }
    }

    /// @dev Returns `min(2 ** 256 - 1, x * y)`.
    function saturatingMul(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := or(sub(or(iszero(x), eq(div(mul(x, y), x), y)), 1), mul(x, y))
        }
    }

    /// @dev Returns `condition ? x : y`, without branching.
    function ternary(bool condition, uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, y), iszero(condition)))
        }
    }

    /// @dev Returns `condition ? x : y`, without branching.
    function ternary(bool condition, bytes32 x, bytes32 y) internal pure returns (bytes32 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, y), iszero(condition)))
        }
    }

    /// @dev Returns `condition ? x : y`, without branching.
    function ternary(bool condition, address x, address y) internal pure returns (address z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, y), iszero(condition)))
        }
    }

    /// @dev Returns `x != 0 ? x : y`, without branching.
    function coalesce(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := or(x, mul(y, iszero(x)))
        }
    }

    /// @dev Returns `x != bytes32(0) ? x : y`, without branching.
    function coalesce(bytes32 x, bytes32 y) internal pure returns (bytes32 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := or(x, mul(y, iszero(x)))
        }
    }

    /// @dev Returns `x != address(0) ? x : y`, without branching.
    function coalesce(address x, address y) internal pure returns (address z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := or(x, mul(y, iszero(shl(96, x))))
        }
    }

    /// @dev Exponentiate `x` to `y` by squaring, denominated in base `b`.
    /// Reverts if the computation overflows.
    function rpow(uint256 x, uint256 y, uint256 b) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mul(b, iszero(y)) // `0 ** 0 = 1`. Otherwise, `0 ** n = 0`.
            if x {
                z := xor(b, mul(xor(b, x), and(y, 1))) // `z = isEven(y) ? scale : x`
                let half := shr(1, b) // Divide `b` by 2.
                // Divide `y` by 2 every iteration.
                for { y := shr(1, y) } y { y := shr(1, y) } {
                    let xx := mul(x, x) // Store x squared.
                    let xxRound := add(xx, half) // Round to the nearest number.
                    // Revert if `xx + half` overflowed, or if `x ** 2` overflows.
                    if or(lt(xxRound, xx), shr(128, x)) {
                        mstore(0x00, 0x49f7642b) // `RPowOverflow()`.
                        revert(0x1c, 0x04)
                    }
                    x := div(xxRound, b) // Set `x` to scaled `xxRound`.
                    // If `y` is odd:
                    if and(y, 1) {
                        let zx := mul(z, x) // Compute `z * x`.
                        let zxRound := add(zx, half) // Round to the nearest number.
                        // If `z * x` overflowed or `zx + half` overflowed:
                        if or(xor(div(zx, x), z), lt(zxRound, zx)) {
                            // Revert if `x` is non-zero.
                            if x {
                                mstore(0x00, 0x49f7642b) // `RPowOverflow()`.
                                revert(0x1c, 0x04)
                            }
                        }
                        z := div(zxRound, b) // Return properly scaled `zxRound`.
                    }
                }
            }
        }
    }

    /// @dev Returns the square root of `x`, rounded down.
    function sqrt(uint256 x) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // `floor(sqrt(2**15)) = 181`. `sqrt(2**15) - 181 = 2.84`.
            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.

            // Let `y = x / 2**r`. We check `y >= 2**(k + 8)`
            // but shift right by `k` bits to ensure that if `x >= 256`, then `y >= 256`.
            let r := shl(7, lt(0xffffffffffffffffffffffffffffffffff, x))
            r := or(r, shl(6, lt(0xffffffffffffffffff, shr(r, x))))
            r := or(r, shl(5, lt(0xffffffffff, shr(r, x))))
            r := or(r, shl(4, lt(0xffffff, shr(r, x))))
            z := shl(shr(1, r), 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(shr(r, x), 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
            z := sub(z, lt(div(x, z), z))
        }
    }

    /// @dev Returns the cube root of `x`, rounded down.
    /// Credit to bout3fiddy and pcaversaccio under AGPLv3 license:
    /// https://github.com/pcaversaccio/snekmate/blob/main/src/snekmate/utils/math.vy
    /// Formally verified by xuwinnie:
    /// https://github.com/vectorized/solady/blob/main/audits/xuwinnie-solady-cbrt-proof.pdf
    function cbrt(uint256 x) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            let r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
            r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
            r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
            r := or(r, shl(4, lt(0xffff, shr(r, x))))
            r := or(r, shl(3, lt(0xff, shr(r, x))))
            // Makeshift lookup table to nudge the approximate log2 result.
            z := div(shl(div(r, 3), shl(lt(0xf, shr(r, x)), 0xf)), xor(7, mod(r, 3)))
            // Newton-Raphson's.
            z := div(add(add(div(x, mul(z, z)), z), z), 3)
            z := div(add(add(div(x, mul(z, z)), z), z), 3)
            z := div(add(add(div(x, mul(z, z)), z), z), 3)
            z := div(add(add(div(x, mul(z, z)), z), z), 3)
            z := div(add(add(div(x, mul(z, z)), z), z), 3)
            z := div(add(add(div(x, mul(z, z)), z), z), 3)
            z := div(add(add(div(x, mul(z, z)), z), z), 3)
            // Round down.
            z := sub(z, lt(div(x, mul(z, z)), z))
        }
    }

    /// @dev Returns the square root of `x`, denominated in `WAD`, rounded down.
    function sqrtWad(uint256 x) internal pure returns (uint256 z) {
        unchecked {
            if (x <= type(uint256).max / 10 ** 18) return sqrt(x * 10 ** 18);
            z = (1 + sqrt(x)) * 10 ** 9;
            z = (fullMulDivUnchecked(x, 10 ** 18, z) + z) >> 1;
        }
        /// @solidity memory-safe-assembly
        assembly {
            z := sub(z, gt(999999999999999999, sub(mulmod(z, z, x), 1))) // Round down.
        }
    }

    /// @dev Returns the cube root of `x`, denominated in `WAD`, rounded down.
    /// Formally verified by xuwinnie:
    /// https://github.com/vectorized/solady/blob/main/audits/xuwinnie-solady-cbrt-proof.pdf
    function cbrtWad(uint256 x) internal pure returns (uint256 z) {
        unchecked {
            if (x <= type(uint256).max / 10 ** 36) return cbrt(x * 10 ** 36);
            z = (1 + cbrt(x)) * 10 ** 12;
            z = (fullMulDivUnchecked(x, 10 ** 36, z * z) + z + z) / 3;
        }
        /// @solidity memory-safe-assembly
        assembly {
            let p := x
            for {} 1 {} {
                if iszero(shr(229, p)) {
                    if iszero(shr(199, p)) {
                        p := mul(p, 100000000000000000) // 10 ** 17.
                        break
                    }
                    p := mul(p, 100000000) // 10 ** 8.
                    break
                }
                if iszero(shr(249, p)) { p := mul(p, 100) }
                break
            }
            let t := mulmod(mul(z, z), z, p)
            z := sub(z, gt(lt(t, shr(1, p)), iszero(t))) // Round down.
        }
    }

    /// @dev Returns the factorial of `x`.
    function factorial(uint256 x) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := 1
            if iszero(lt(x, 58)) {
                mstore(0x00, 0xaba0f2a2) // `FactorialOverflow()`.
                revert(0x1c, 0x04)
            }
            for {} x { x := sub(x, 1) } { z := mul(z, x) }
        }
    }

    /// @dev Returns the log2 of `x`.
    /// Equivalent to computing the index of the most significant bit (MSB) of `x`.
    /// Returns 0 if `x` is zero.
    function log2(uint256 x) internal pure returns (uint256 r) {
        /// @solidity memory-safe-assembly
        assembly {
            r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
            r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
            r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
            r := or(r, shl(4, lt(0xffff, shr(r, x))))
            r := or(r, shl(3, lt(0xff, shr(r, x))))
            // forgefmt: disable-next-item
            r := or(r, byte(and(0x1f, shr(shr(r, x), 0x8421084210842108cc6318c6db6d54be)),
                0x0706060506020504060203020504030106050205030304010505030400000000))
        }
    }

    /// @dev Returns the log2 of `x`, rounded up.
    /// Returns 0 if `x` is zero.
    function log2Up(uint256 x) internal pure returns (uint256 r) {
        r = log2(x);
        /// @solidity memory-safe-assembly
        assembly {
            r := add(r, lt(shl(r, 1), x))
        }
    }

    /// @dev Returns the log10 of `x`.
    /// Returns 0 if `x` is zero.
    function log10(uint256 x) internal pure returns (uint256 r) {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(lt(x, 100000000000000000000000000000000000000)) {
                x := div(x, 100000000000000000000000000000000000000)
                r := 38
            }
            if iszero(lt(x, 100000000000000000000)) {
                x := div(x, 100000000000000000000)
                r := add(r, 20)
            }
            if iszero(lt(x, 10000000000)) {
                x := div(x, 10000000000)
                r := add(r, 10)
            }
            if iszero(lt(x, 100000)) {
                x := div(x, 100000)
                r := add(r, 5)
            }
            r := add(r, add(gt(x, 9), add(gt(x, 99), add(gt(x, 999), gt(x, 9999)))))
        }
    }

    /// @dev Returns the log10 of `x`, rounded up.
    /// Returns 0 if `x` is zero.
    function log10Up(uint256 x) internal pure returns (uint256 r) {
        r = log10(x);
        /// @solidity memory-safe-assembly
        assembly {
            r := add(r, lt(exp(10, r), x))
        }
    }

    /// @dev Returns the log256 of `x`.
    /// Returns 0 if `x` is zero.
    function log256(uint256 x) internal pure returns (uint256 r) {
        /// @solidity memory-safe-assembly
        assembly {
            r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
            r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
            r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
            r := or(r, shl(4, lt(0xffff, shr(r, x))))
            r := or(shr(3, r), lt(0xff, shr(r, x)))
        }
    }

    /// @dev Returns the log256 of `x`, rounded up.
    /// Returns 0 if `x` is zero.
    function log256Up(uint256 x) internal pure returns (uint256 r) {
        r = log256(x);
        /// @solidity memory-safe-assembly
        assembly {
            r := add(r, lt(shl(shl(3, r), 1), x))
        }
    }

    /// @dev Returns the scientific notation format `mantissa * 10 ** exponent` of `x`.
    /// Useful for compressing prices (e.g. using 25 bit mantissa and 7 bit exponent).
    function sci(uint256 x) internal pure returns (uint256 mantissa, uint256 exponent) {
        /// @solidity memory-safe-assembly
        assembly {
            mantissa := x
            if mantissa {
                if iszero(mod(mantissa, 1000000000000000000000000000000000)) {
                    mantissa := div(mantissa, 1000000000000000000000000000000000)
                    exponent := 33
                }
                if iszero(mod(mantissa, 10000000000000000000)) {
                    mantissa := div(mantissa, 10000000000000000000)
                    exponent := add(exponent, 19)
                }
                if iszero(mod(mantissa, 1000000000000)) {
                    mantissa := div(mantissa, 1000000000000)
                    exponent := add(exponent, 12)
                }
                if iszero(mod(mantissa, 1000000)) {
                    mantissa := div(mantissa, 1000000)
                    exponent := add(exponent, 6)
                }
                if iszero(mod(mantissa, 10000)) {
                    mantissa := div(mantissa, 10000)
                    exponent := add(exponent, 4)
                }
                if iszero(mod(mantissa, 100)) {
                    mantissa := div(mantissa, 100)
                    exponent := add(exponent, 2)
                }
                if iszero(mod(mantissa, 10)) {
                    mantissa := div(mantissa, 10)
                    exponent := add(exponent, 1)
                }
            }
        }
    }

    /// @dev Convenience function for packing `x` into a smaller number using `sci`.
    /// The `mantissa` will be in bits [7..255] (the upper 249 bits).
    /// The `exponent` will be in bits [0..6] (the lower 7 bits).
    /// Use `SafeCastLib` to safely ensure that the `packed` number is small
    /// enough to fit in the desired unsigned integer type:
    /// ```
    ///     uint32 packed = SafeCastLib.toUint32(FixedPointMathLib.packSci(777 ether));
    /// ```
    function packSci(uint256 x) internal pure returns (uint256 packed) {
        (x, packed) = sci(x); // Reuse for `mantissa` and `exponent`.
        /// @solidity memory-safe-assembly
        assembly {
            if shr(249, x) {
                mstore(0x00, 0xce30380c) // `MantissaOverflow()`.
                revert(0x1c, 0x04)
            }
            packed := or(shl(7, x), packed)
        }
    }

    /// @dev Convenience function for unpacking a packed number from `packSci`.
    function unpackSci(uint256 packed) internal pure returns (uint256 unpacked) {
        unchecked {
            unpacked = (packed >> 7) * 10 ** (packed & 0x7f);
        }
    }

    /// @dev Returns the average of `x` and `y`. Rounds towards zero.
    function avg(uint256 x, uint256 y) internal pure returns (uint256 z) {
        unchecked {
            z = (x & y) + ((x ^ y) >> 1);
        }
    }

    /// @dev Returns the average of `x` and `y`. Rounds towards negative infinity.
    function avg(int256 x, int256 y) internal pure returns (int256 z) {
        unchecked {
            z = (x >> 1) + (y >> 1) + (x & y & 1);
        }
    }

    /// @dev Returns the absolute value of `x`.
    function abs(int256 x) internal pure returns (uint256 z) {
        unchecked {
            z = (uint256(x) + uint256(x >> 255)) ^ uint256(x >> 255);
        }
    }

    /// @dev Returns the absolute distance between `x` and `y`.
    function dist(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := add(xor(sub(0, gt(x, y)), sub(y, x)), gt(x, y))
        }
    }

    /// @dev Returns the absolute distance between `x` and `y`.
    function dist(int256 x, int256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := add(xor(sub(0, sgt(x, y)), sub(y, x)), sgt(x, y))
        }
    }

    /// @dev Returns the minimum of `x` and `y`.
    function min(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, y), lt(y, x)))
        }
    }

    /// @dev Returns the minimum of `x` and `y`.
    function min(int256 x, int256 y) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, y), slt(y, x)))
        }
    }

    /// @dev Returns the maximum of `x` and `y`.
    function max(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, y), gt(y, x)))
        }
    }

    /// @dev Returns the maximum of `x` and `y`.
    function max(int256 x, int256 y) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, y), sgt(y, x)))
        }
    }

    /// @dev Returns `x`, bounded to `minValue` and `maxValue`.
    function clamp(uint256 x, uint256 minValue, uint256 maxValue)
        internal
        pure
        returns (uint256 z)
    {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, minValue), gt(minValue, x)))
            z := xor(z, mul(xor(z, maxValue), lt(maxValue, z)))
        }
    }

    /// @dev Returns `x`, bounded to `minValue` and `maxValue`.
    function clamp(int256 x, int256 minValue, int256 maxValue) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, minValue), sgt(minValue, x)))
            z := xor(z, mul(xor(z, maxValue), slt(maxValue, z)))
        }
    }

    /// @dev Returns greatest common divisor of `x` and `y`.
    function gcd(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            for { z := x } y {} {
                let t := y
                y := mod(z, y)
                z := t
            }
        }
    }

    /// @dev Returns `a + (b - a) * (t - begin) / (end - begin)`,
    /// with `t` clamped between `begin` and `end` (inclusive).
    /// Agnostic to the order of (`a`, `b`) and (`end`, `begin`).
    /// If `begins == end`, returns `t <= begin ? a : b`.
    function lerp(uint256 a, uint256 b, uint256 t, uint256 begin, uint256 end)
        internal
        pure
        returns (uint256)
    {
        if (begin > end) (t, begin, end) = (~t, ~begin, ~end);
        if (t <= begin) return a;
        if (t >= end) return b;
        unchecked {
            if (b >= a) return a + fullMulDiv(b - a, t - begin, end - begin);
            return a - fullMulDiv(a - b, t - begin, end - begin);
        }
    }

    /// @dev Returns `a + (b - a) * (t - begin) / (end - begin)`.
    /// with `t` clamped between `begin` and `end` (inclusive).
    /// Agnostic to the order of (`a`, `b`) and (`end`, `begin`).
    /// If `begins == end`, returns `t <= begin ? a : b`.
    function lerp(int256 a, int256 b, int256 t, int256 begin, int256 end)
        internal
        pure
        returns (int256)
    {
        if (begin > end) (t, begin, end) = (~t, ~begin, ~end);
        if (t <= begin) return a;
        if (t >= end) return b;
        // forgefmt: disable-next-item
        unchecked {
            if (b >= a) return int256(uint256(a) + fullMulDiv(uint256(b - a),
                uint256(t - begin), uint256(end - begin)));
            return int256(uint256(a) - fullMulDiv(uint256(a - b),
                uint256(t - begin), uint256(end - begin)));
        }
    }

    /// @dev Returns if `x` is an even number. Some people may need this.
    function isEven(uint256 x) internal pure returns (bool) {
        return x & uint256(1) == uint256(0);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   RAW NUMBER OPERATIONS                    */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns `x + y`, without checking for overflow.
    function rawAdd(uint256 x, uint256 y) internal pure returns (uint256 z) {
        unchecked {
            z = x + y;
        }
    }

    /// @dev Returns `x + y`, without checking for overflow.
    function rawAdd(int256 x, int256 y) internal pure returns (int256 z) {
        unchecked {
            z = x + y;
        }
    }

    /// @dev Returns `x - y`, without checking for underflow.
    function rawSub(uint256 x, uint256 y) internal pure returns (uint256 z) {
        unchecked {
            z = x - y;
        }
    }

    /// @dev Returns `x - y`, without checking for underflow.
    function rawSub(int256 x, int256 y) internal pure returns (int256 z) {
        unchecked {
            z = x - y;
        }
    }

    /// @dev Returns `x * y`, without checking for overflow.
    function rawMul(uint256 x, uint256 y) internal pure returns (uint256 z) {
        unchecked {
            z = x * y;
        }
    }

    /// @dev Returns `x * y`, without checking for overflow.
    function rawMul(int256 x, int256 y) internal pure returns (int256 z) {
        unchecked {
            z = x * y;
        }
    }

    /// @dev Returns `x / y`, returning 0 if `y` is zero.
    function rawDiv(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := div(x, y)
        }
    }

    /// @dev Returns `x / y`, returning 0 if `y` is zero.
    function rawSDiv(int256 x, int256 y) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := sdiv(x, y)
        }
    }

    /// @dev Returns `x % y`, returning 0 if `y` is zero.
    function rawMod(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mod(x, y)
        }
    }

    /// @dev Returns `x % y`, returning 0 if `y` is zero.
    function rawSMod(int256 x, int256 y) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := smod(x, y)
        }
    }

    /// @dev Returns `(x + y) % d`, return 0 if `d` if zero.
    function rawAddMod(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := addmod(x, y, d)
        }
    }

    /// @dev Returns `(x * y) % d`, return 0 if `d` if zero.
    function rawMulMod(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mulmod(x, y, d)
        }
    }
}

File 12 of 33 : SafeCastLib.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Safe integer casting library that reverts on overflow.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeCastLib.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/math/SafeCast.sol)
/// @dev Optimized for runtime gas for very high number of optimizer runs (i.e. >= 1000000).
library SafeCastLib {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Unable to cast to the target type due to overflow.
    error Overflow();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*          UNSIGNED INTEGER SAFE CASTING OPERATIONS          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Casts `x` to a uint8. Reverts on overflow.
    function toUint8(uint256 x) internal pure returns (uint8) {
        if (x >= 1 << 8) _revertOverflow();
        return uint8(x);
    }

    /// @dev Casts `x` to a uint16. Reverts on overflow.
    function toUint16(uint256 x) internal pure returns (uint16) {
        if (x >= 1 << 16) _revertOverflow();
        return uint16(x);
    }

    /// @dev Casts `x` to a uint24. Reverts on overflow.
    function toUint24(uint256 x) internal pure returns (uint24) {
        if (x >= 1 << 24) _revertOverflow();
        return uint24(x);
    }

    /// @dev Casts `x` to a uint32. Reverts on overflow.
    function toUint32(uint256 x) internal pure returns (uint32) {
        if (x >= 1 << 32) _revertOverflow();
        return uint32(x);
    }

    /// @dev Casts `x` to a uint40. Reverts on overflow.
    function toUint40(uint256 x) internal pure returns (uint40) {
        if (x >= 1 << 40) _revertOverflow();
        return uint40(x);
    }

    /// @dev Casts `x` to a uint48. Reverts on overflow.
    function toUint48(uint256 x) internal pure returns (uint48) {
        if (x >= 1 << 48) _revertOverflow();
        return uint48(x);
    }

    /// @dev Casts `x` to a uint56. Reverts on overflow.
    function toUint56(uint256 x) internal pure returns (uint56) {
        if (x >= 1 << 56) _revertOverflow();
        return uint56(x);
    }

    /// @dev Casts `x` to a uint64. Reverts on overflow.
    function toUint64(uint256 x) internal pure returns (uint64) {
        if (x >= 1 << 64) _revertOverflow();
        return uint64(x);
    }

    /// @dev Casts `x` to a uint72. Reverts on overflow.
    function toUint72(uint256 x) internal pure returns (uint72) {
        if (x >= 1 << 72) _revertOverflow();
        return uint72(x);
    }

    /// @dev Casts `x` to a uint80. Reverts on overflow.
    function toUint80(uint256 x) internal pure returns (uint80) {
        if (x >= 1 << 80) _revertOverflow();
        return uint80(x);
    }

    /// @dev Casts `x` to a uint88. Reverts on overflow.
    function toUint88(uint256 x) internal pure returns (uint88) {
        if (x >= 1 << 88) _revertOverflow();
        return uint88(x);
    }

    /// @dev Casts `x` to a uint96. Reverts on overflow.
    function toUint96(uint256 x) internal pure returns (uint96) {
        if (x >= 1 << 96) _revertOverflow();
        return uint96(x);
    }

    /// @dev Casts `x` to a uint104. Reverts on overflow.
    function toUint104(uint256 x) internal pure returns (uint104) {
        if (x >= 1 << 104) _revertOverflow();
        return uint104(x);
    }

    /// @dev Casts `x` to a uint112. Reverts on overflow.
    function toUint112(uint256 x) internal pure returns (uint112) {
        if (x >= 1 << 112) _revertOverflow();
        return uint112(x);
    }

    /// @dev Casts `x` to a uint120. Reverts on overflow.
    function toUint120(uint256 x) internal pure returns (uint120) {
        if (x >= 1 << 120) _revertOverflow();
        return uint120(x);
    }

    /// @dev Casts `x` to a uint128. Reverts on overflow.
    function toUint128(uint256 x) internal pure returns (uint128) {
        if (x >= 1 << 128) _revertOverflow();
        return uint128(x);
    }

    /// @dev Casts `x` to a uint136. Reverts on overflow.
    function toUint136(uint256 x) internal pure returns (uint136) {
        if (x >= 1 << 136) _revertOverflow();
        return uint136(x);
    }

    /// @dev Casts `x` to a uint144. Reverts on overflow.
    function toUint144(uint256 x) internal pure returns (uint144) {
        if (x >= 1 << 144) _revertOverflow();
        return uint144(x);
    }

    /// @dev Casts `x` to a uint152. Reverts on overflow.
    function toUint152(uint256 x) internal pure returns (uint152) {
        if (x >= 1 << 152) _revertOverflow();
        return uint152(x);
    }

    /// @dev Casts `x` to a uint160. Reverts on overflow.
    function toUint160(uint256 x) internal pure returns (uint160) {
        if (x >= 1 << 160) _revertOverflow();
        return uint160(x);
    }

    /// @dev Casts `x` to a uint168. Reverts on overflow.
    function toUint168(uint256 x) internal pure returns (uint168) {
        if (x >= 1 << 168) _revertOverflow();
        return uint168(x);
    }

    /// @dev Casts `x` to a uint176. Reverts on overflow.
    function toUint176(uint256 x) internal pure returns (uint176) {
        if (x >= 1 << 176) _revertOverflow();
        return uint176(x);
    }

    /// @dev Casts `x` to a uint184. Reverts on overflow.
    function toUint184(uint256 x) internal pure returns (uint184) {
        if (x >= 1 << 184) _revertOverflow();
        return uint184(x);
    }

    /// @dev Casts `x` to a uint192. Reverts on overflow.
    function toUint192(uint256 x) internal pure returns (uint192) {
        if (x >= 1 << 192) _revertOverflow();
        return uint192(x);
    }

    /// @dev Casts `x` to a uint200. Reverts on overflow.
    function toUint200(uint256 x) internal pure returns (uint200) {
        if (x >= 1 << 200) _revertOverflow();
        return uint200(x);
    }

    /// @dev Casts `x` to a uint208. Reverts on overflow.
    function toUint208(uint256 x) internal pure returns (uint208) {
        if (x >= 1 << 208) _revertOverflow();
        return uint208(x);
    }

    /// @dev Casts `x` to a uint216. Reverts on overflow.
    function toUint216(uint256 x) internal pure returns (uint216) {
        if (x >= 1 << 216) _revertOverflow();
        return uint216(x);
    }

    /// @dev Casts `x` to a uint224. Reverts on overflow.
    function toUint224(uint256 x) internal pure returns (uint224) {
        if (x >= 1 << 224) _revertOverflow();
        return uint224(x);
    }

    /// @dev Casts `x` to a uint232. Reverts on overflow.
    function toUint232(uint256 x) internal pure returns (uint232) {
        if (x >= 1 << 232) _revertOverflow();
        return uint232(x);
    }

    /// @dev Casts `x` to a uint240. Reverts on overflow.
    function toUint240(uint256 x) internal pure returns (uint240) {
        if (x >= 1 << 240) _revertOverflow();
        return uint240(x);
    }

    /// @dev Casts `x` to a uint248. Reverts on overflow.
    function toUint248(uint256 x) internal pure returns (uint248) {
        if (x >= 1 << 248) _revertOverflow();
        return uint248(x);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*           SIGNED INTEGER SAFE CASTING OPERATIONS           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Casts `x` to a int8. Reverts on overflow.
    function toInt8(int256 x) internal pure returns (int8) {
        unchecked {
            if (((1 << 7) + uint256(x)) >> 8 == uint256(0)) return int8(x);
            _revertOverflow();
        }
    }

    /// @dev Casts `x` to a int16. Reverts on overflow.
    function toInt16(int256 x) internal pure returns (int16) {
        unchecked {
            if (((1 << 15) + uint256(x)) >> 16 == uint256(0)) return int16(x);
            _revertOverflow();
        }
    }

    /// @dev Casts `x` to a int24. Reverts on overflow.
    function toInt24(int256 x) internal pure returns (int24) {
        unchecked {
            if (((1 << 23) + uint256(x)) >> 24 == uint256(0)) return int24(x);
            _revertOverflow();
        }
    }

    /// @dev Casts `x` to a int32. Reverts on overflow.
    function toInt32(int256 x) internal pure returns (int32) {
        unchecked {
            if (((1 << 31) + uint256(x)) >> 32 == uint256(0)) return int32(x);
            _revertOverflow();
        }
    }

    /// @dev Casts `x` to a int40. Reverts on overflow.
    function toInt40(int256 x) internal pure returns (int40) {
        unchecked {
            if (((1 << 39) + uint256(x)) >> 40 == uint256(0)) return int40(x);
            _revertOverflow();
        }
    }

    /// @dev Casts `x` to a int48. Reverts on overflow.
    function toInt48(int256 x) internal pure returns (int48) {
        unchecked {
            if (((1 << 47) + uint256(x)) >> 48 == uint256(0)) return int48(x);
            _revertOverflow();
        }
    }

    /// @dev Casts `x` to a int56. Reverts on overflow.
    function toInt56(int256 x) internal pure returns (int56) {
        unchecked {
            if (((1 << 55) + uint256(x)) >> 56 == uint256(0)) return int56(x);
            _revertOverflow();
        }
    }

    /// @dev Casts `x` to a int64. Reverts on overflow.
    function toInt64(int256 x) internal pure returns (int64) {
        unchecked {
            if (((1 << 63) + uint256(x)) >> 64 == uint256(0)) return int64(x);
            _revertOverflow();
        }
    }

    /// @dev Casts `x` to a int72. Reverts on overflow.
    function toInt72(int256 x) internal pure returns (int72) {
        unchecked {
            if (((1 << 71) + uint256(x)) >> 72 == uint256(0)) return int72(x);
            _revertOverflow();
        }
    }

    /// @dev Casts `x` to a int80. Reverts on overflow.
    function toInt80(int256 x) internal pure returns (int80) {
        unchecked {
            if (((1 << 79) + uint256(x)) >> 80 == uint256(0)) return int80(x);
            _revertOverflow();
        }
    }

    /// @dev Casts `x` to a int88. Reverts on overflow.
    function toInt88(int256 x) internal pure returns (int88) {
        unchecked {
            if (((1 << 87) + uint256(x)) >> 88 == uint256(0)) return int88(x);
            _revertOverflow();
        }
    }

    /// @dev Casts `x` to a int96. Reverts on overflow.
    function toInt96(int256 x) internal pure returns (int96) {
        unchecked {
            if (((1 << 95) + uint256(x)) >> 96 == uint256(0)) return int96(x);
            _revertOverflow();
        }
    }

    /// @dev Casts `x` to a int104. Reverts on overflow.
    function toInt104(int256 x) internal pure returns (int104) {
        unchecked {
            if (((1 << 103) + uint256(x)) >> 104 == uint256(0)) return int104(x);
            _revertOverflow();
        }
    }

    /// @dev Casts `x` to a int112. Reverts on overflow.
    function toInt112(int256 x) internal pure returns (int112) {
        unchecked {
            if (((1 << 111) + uint256(x)) >> 112 == uint256(0)) return int112(x);
            _revertOverflow();
        }
    }

    /// @dev Casts `x` to a int120. Reverts on overflow.
    function toInt120(int256 x) internal pure returns (int120) {
        unchecked {
            if (((1 << 119) + uint256(x)) >> 120 == uint256(0)) return int120(x);
            _revertOverflow();
        }
    }

    /// @dev Casts `x` to a int128. Reverts on overflow.
    function toInt128(int256 x) internal pure returns (int128) {
        unchecked {
            if (((1 << 127) + uint256(x)) >> 128 == uint256(0)) return int128(x);
            _revertOverflow();
        }
    }

    /// @dev Casts `x` to a int136. Reverts on overflow.
    function toInt136(int256 x) internal pure returns (int136) {
        unchecked {
            if (((1 << 135) + uint256(x)) >> 136 == uint256(0)) return int136(x);
            _revertOverflow();
        }
    }

    /// @dev Casts `x` to a int144. Reverts on overflow.
    function toInt144(int256 x) internal pure returns (int144) {
        unchecked {
            if (((1 << 143) + uint256(x)) >> 144 == uint256(0)) return int144(x);
            _revertOverflow();
        }
    }

    /// @dev Casts `x` to a int152. Reverts on overflow.
    function toInt152(int256 x) internal pure returns (int152) {
        unchecked {
            if (((1 << 151) + uint256(x)) >> 152 == uint256(0)) return int152(x);
            _revertOverflow();
        }
    }

    /// @dev Casts `x` to a int160. Reverts on overflow.
    function toInt160(int256 x) internal pure returns (int160) {
        unchecked {
            if (((1 << 159) + uint256(x)) >> 160 == uint256(0)) return int160(x);
            _revertOverflow();
        }
    }

    /// @dev Casts `x` to a int168. Reverts on overflow.
    function toInt168(int256 x) internal pure returns (int168) {
        unchecked {
            if (((1 << 167) + uint256(x)) >> 168 == uint256(0)) return int168(x);
            _revertOverflow();
        }
    }

    /// @dev Casts `x` to a int176. Reverts on overflow.
    function toInt176(int256 x) internal pure returns (int176) {
        unchecked {
            if (((1 << 175) + uint256(x)) >> 176 == uint256(0)) return int176(x);
            _revertOverflow();
        }
    }

    /// @dev Casts `x` to a int184. Reverts on overflow.
    function toInt184(int256 x) internal pure returns (int184) {
        unchecked {
            if (((1 << 183) + uint256(x)) >> 184 == uint256(0)) return int184(x);
            _revertOverflow();
        }
    }

    /// @dev Casts `x` to a int192. Reverts on overflow.
    function toInt192(int256 x) internal pure returns (int192) {
        unchecked {
            if (((1 << 191) + uint256(x)) >> 192 == uint256(0)) return int192(x);
            _revertOverflow();
        }
    }

    /// @dev Casts `x` to a int200. Reverts on overflow.
    function toInt200(int256 x) internal pure returns (int200) {
        unchecked {
            if (((1 << 199) + uint256(x)) >> 200 == uint256(0)) return int200(x);
            _revertOverflow();
        }
    }

    /// @dev Casts `x` to a int208. Reverts on overflow.
    function toInt208(int256 x) internal pure returns (int208) {
        unchecked {
            if (((1 << 207) + uint256(x)) >> 208 == uint256(0)) return int208(x);
            _revertOverflow();
        }
    }

    /// @dev Casts `x` to a int216. Reverts on overflow.
    function toInt216(int256 x) internal pure returns (int216) {
        unchecked {
            if (((1 << 215) + uint256(x)) >> 216 == uint256(0)) return int216(x);
            _revertOverflow();
        }
    }

    /// @dev Casts `x` to a int224. Reverts on overflow.
    function toInt224(int256 x) internal pure returns (int224) {
        unchecked {
            if (((1 << 223) + uint256(x)) >> 224 == uint256(0)) return int224(x);
            _revertOverflow();
        }
    }

    /// @dev Casts `x` to a int232. Reverts on overflow.
    function toInt232(int256 x) internal pure returns (int232) {
        unchecked {
            if (((1 << 231) + uint256(x)) >> 232 == uint256(0)) return int232(x);
            _revertOverflow();
        }
    }

    /// @dev Casts `x` to a int240. Reverts on overflow.
    function toInt240(int256 x) internal pure returns (int240) {
        unchecked {
            if (((1 << 239) + uint256(x)) >> 240 == uint256(0)) return int240(x);
            _revertOverflow();
        }
    }

    /// @dev Casts `x` to a int248. Reverts on overflow.
    function toInt248(int256 x) internal pure returns (int248) {
        unchecked {
            if (((1 << 247) + uint256(x)) >> 248 == uint256(0)) return int248(x);
            _revertOverflow();
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*               OTHER SAFE CASTING OPERATIONS                */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Casts `x` to a int8. Reverts on overflow.
    function toInt8(uint256 x) internal pure returns (int8) {
        if (x >= 1 << 7) _revertOverflow();
        return int8(int256(x));
    }

    /// @dev Casts `x` to a int16. Reverts on overflow.
    function toInt16(uint256 x) internal pure returns (int16) {
        if (x >= 1 << 15) _revertOverflow();
        return int16(int256(x));
    }

    /// @dev Casts `x` to a int24. Reverts on overflow.
    function toInt24(uint256 x) internal pure returns (int24) {
        if (x >= 1 << 23) _revertOverflow();
        return int24(int256(x));
    }

    /// @dev Casts `x` to a int32. Reverts on overflow.
    function toInt32(uint256 x) internal pure returns (int32) {
        if (x >= 1 << 31) _revertOverflow();
        return int32(int256(x));
    }

    /// @dev Casts `x` to a int40. Reverts on overflow.
    function toInt40(uint256 x) internal pure returns (int40) {
        if (x >= 1 << 39) _revertOverflow();
        return int40(int256(x));
    }

    /// @dev Casts `x` to a int48. Reverts on overflow.
    function toInt48(uint256 x) internal pure returns (int48) {
        if (x >= 1 << 47) _revertOverflow();
        return int48(int256(x));
    }

    /// @dev Casts `x` to a int56. Reverts on overflow.
    function toInt56(uint256 x) internal pure returns (int56) {
        if (x >= 1 << 55) _revertOverflow();
        return int56(int256(x));
    }

    /// @dev Casts `x` to a int64. Reverts on overflow.
    function toInt64(uint256 x) internal pure returns (int64) {
        if (x >= 1 << 63) _revertOverflow();
        return int64(int256(x));
    }

    /// @dev Casts `x` to a int72. Reverts on overflow.
    function toInt72(uint256 x) internal pure returns (int72) {
        if (x >= 1 << 71) _revertOverflow();
        return int72(int256(x));
    }

    /// @dev Casts `x` to a int80. Reverts on overflow.
    function toInt80(uint256 x) internal pure returns (int80) {
        if (x >= 1 << 79) _revertOverflow();
        return int80(int256(x));
    }

    /// @dev Casts `x` to a int88. Reverts on overflow.
    function toInt88(uint256 x) internal pure returns (int88) {
        if (x >= 1 << 87) _revertOverflow();
        return int88(int256(x));
    }

    /// @dev Casts `x` to a int96. Reverts on overflow.
    function toInt96(uint256 x) internal pure returns (int96) {
        if (x >= 1 << 95) _revertOverflow();
        return int96(int256(x));
    }

    /// @dev Casts `x` to a int104. Reverts on overflow.
    function toInt104(uint256 x) internal pure returns (int104) {
        if (x >= 1 << 103) _revertOverflow();
        return int104(int256(x));
    }

    /// @dev Casts `x` to a int112. Reverts on overflow.
    function toInt112(uint256 x) internal pure returns (int112) {
        if (x >= 1 << 111) _revertOverflow();
        return int112(int256(x));
    }

    /// @dev Casts `x` to a int120. Reverts on overflow.
    function toInt120(uint256 x) internal pure returns (int120) {
        if (x >= 1 << 119) _revertOverflow();
        return int120(int256(x));
    }

    /// @dev Casts `x` to a int128. Reverts on overflow.
    function toInt128(uint256 x) internal pure returns (int128) {
        if (x >= 1 << 127) _revertOverflow();
        return int128(int256(x));
    }

    /// @dev Casts `x` to a int136. Reverts on overflow.
    function toInt136(uint256 x) internal pure returns (int136) {
        if (x >= 1 << 135) _revertOverflow();
        return int136(int256(x));
    }

    /// @dev Casts `x` to a int144. Reverts on overflow.
    function toInt144(uint256 x) internal pure returns (int144) {
        if (x >= 1 << 143) _revertOverflow();
        return int144(int256(x));
    }

    /// @dev Casts `x` to a int152. Reverts on overflow.
    function toInt152(uint256 x) internal pure returns (int152) {
        if (x >= 1 << 151) _revertOverflow();
        return int152(int256(x));
    }

    /// @dev Casts `x` to a int160. Reverts on overflow.
    function toInt160(uint256 x) internal pure returns (int160) {
        if (x >= 1 << 159) _revertOverflow();
        return int160(int256(x));
    }

    /// @dev Casts `x` to a int168. Reverts on overflow.
    function toInt168(uint256 x) internal pure returns (int168) {
        if (x >= 1 << 167) _revertOverflow();
        return int168(int256(x));
    }

    /// @dev Casts `x` to a int176. Reverts on overflow.
    function toInt176(uint256 x) internal pure returns (int176) {
        if (x >= 1 << 175) _revertOverflow();
        return int176(int256(x));
    }

    /// @dev Casts `x` to a int184. Reverts on overflow.
    function toInt184(uint256 x) internal pure returns (int184) {
        if (x >= 1 << 183) _revertOverflow();
        return int184(int256(x));
    }

    /// @dev Casts `x` to a int192. Reverts on overflow.
    function toInt192(uint256 x) internal pure returns (int192) {
        if (x >= 1 << 191) _revertOverflow();
        return int192(int256(x));
    }

    /// @dev Casts `x` to a int200. Reverts on overflow.
    function toInt200(uint256 x) internal pure returns (int200) {
        if (x >= 1 << 199) _revertOverflow();
        return int200(int256(x));
    }

    /// @dev Casts `x` to a int208. Reverts on overflow.
    function toInt208(uint256 x) internal pure returns (int208) {
        if (x >= 1 << 207) _revertOverflow();
        return int208(int256(x));
    }

    /// @dev Casts `x` to a int216. Reverts on overflow.
    function toInt216(uint256 x) internal pure returns (int216) {
        if (x >= 1 << 215) _revertOverflow();
        return int216(int256(x));
    }

    /// @dev Casts `x` to a int224. Reverts on overflow.
    function toInt224(uint256 x) internal pure returns (int224) {
        if (x >= 1 << 223) _revertOverflow();
        return int224(int256(x));
    }

    /// @dev Casts `x` to a int232. Reverts on overflow.
    function toInt232(uint256 x) internal pure returns (int232) {
        if (x >= 1 << 231) _revertOverflow();
        return int232(int256(x));
    }

    /// @dev Casts `x` to a int240. Reverts on overflow.
    function toInt240(uint256 x) internal pure returns (int240) {
        if (x >= 1 << 239) _revertOverflow();
        return int240(int256(x));
    }

    /// @dev Casts `x` to a int248. Reverts on overflow.
    function toInt248(uint256 x) internal pure returns (int248) {
        if (x >= 1 << 247) _revertOverflow();
        return int248(int256(x));
    }

    /// @dev Casts `x` to a int256. Reverts on overflow.
    function toInt256(uint256 x) internal pure returns (int256) {
        if (int256(x) >= 0) return int256(x);
        _revertOverflow();
    }

    /// @dev Casts `x` to a uint256. Reverts on overflow.
    function toUint256(int256 x) internal pure returns (uint256) {
        if (x >= 0) return uint256(x);
        _revertOverflow();
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                      PRIVATE HELPERS                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    function _revertOverflow() private pure {
        /// @solidity memory-safe-assembly
        assembly {
            // Store the function selector of `Overflow()`.
            mstore(0x00, 0x35278d12)
            // Revert with (offset, size).
            revert(0x1c, 0x04)
        }
    }
}

File 13 of 33 : ExposedStorage.sol
// SPDX-License-Identifier: ekubo-license-v1.eth
pragma solidity =0.8.33;

import {IExposedStorage} from "../interfaces/IExposedStorage.sol";

/// @title ExposedStorage
/// @notice Abstract contract that implements storage exposure functionality
/// @dev This contract provides the implementation for the IExposedStorage interface,
///      allowing inheriting contracts to expose their storage slots via view functions.
///      Uses inline assembly for efficient storage access.
abstract contract ExposedStorage is IExposedStorage {
    /// @inheritdoc IExposedStorage
    /// @dev Uses inline assembly to efficiently read multiple storage slots specified in calldata.
    ///      Each slot value is loaded using the SLOAD opcode and stored in memory.
    function sload() external view {
        assembly ("memory-safe") {
            for { let i := 4 } lt(i, calldatasize()) { i := add(i, 32) } { mstore(sub(i, 4), sload(calldataload(i))) }
            return(0, sub(calldatasize(), 4))
        }
    }

    /// @inheritdoc IExposedStorage
    /// @dev Uses inline assembly to efficiently read multiple transient storage slots specified in calldata.
    ///      Each slot value is loaded using the TLOAD opcode and stored in memory.
    function tload() external view {
        assembly ("memory-safe") {
            for { let i := 4 } lt(i, calldatasize()) { i := add(i, 32) } { mstore(sub(i, 4), tload(calldataload(i))) }
            return(0, sub(calldatasize(), 4))
        }
    }
}

File 14 of 33 : liquidity.sol
// SPDX-License-Identifier: ekubo-license-v1.eth
pragma solidity =0.8.33;

import {FixedPointMathLib} from "solady/utils/FixedPointMathLib.sol";
import {LibBit} from "solady/utils/LibBit.sol";
import {SafeCastLib} from "solady/utils/SafeCastLib.sol";
import {amount0Delta, amount1Delta, sortAndConvertToFixedSqrtRatios} from "./delta.sol";
import {SqrtRatio} from "../types/sqrtRatio.sol";

// Liquidity Math Library
// Contains functions for calculating liquidity-related amounts and conversions
// Provides utilities for converting between liquidity changes and token amounts

/// @notice Returns the token0 and token1 delta owed for a given change in liquidity
/// @dev Calculates the token amounts required or returned when liquidity is added or removed from a position
/// @param sqrtRatio Current price (as a valid sqrt ratio)
/// @param liquidityDelta Signed liquidity change; positive = added, negative = removed
/// @param sqrtRatioLower The lower bound of the price range (as a valid sqrt ratio)
/// @param sqrtRatioUpper The upper bound of the price range (as a valid sqrt ratio)
/// @return delta0 The change in token0 amount
/// @return delta1 The change in token1 amount
function liquidityDeltaToAmountDelta(
    SqrtRatio sqrtRatio,
    int128 liquidityDelta,
    SqrtRatio sqrtRatioLower,
    SqrtRatio sqrtRatioUpper
) pure returns (int128 delta0, int128 delta1) {
    unchecked {
        if (liquidityDelta == 0) {
            return (0, 0);
        }
        bool isPositive = (liquidityDelta > 0);
        int256 sign = -1 + 2 * int256(LibBit.rawToUint(isPositive));
        // absolute value of a int128 always fits in a uint128
        uint128 magnitude = uint128(FixedPointMathLib.abs(liquidityDelta));

        if (sqrtRatio <= sqrtRatioLower) {
            delta0 = SafeCastLib.toInt128(
                sign * int256(uint256(amount0Delta(sqrtRatioLower, sqrtRatioUpper, magnitude, isPositive)))
            );
        } else if (sqrtRatio < sqrtRatioUpper) {
            delta0 = SafeCastLib.toInt128(
                sign * int256(uint256(amount0Delta(sqrtRatio, sqrtRatioUpper, magnitude, isPositive)))
            );
            delta1 = SafeCastLib.toInt128(
                sign * int256(uint256(amount1Delta(sqrtRatioLower, sqrtRatio, magnitude, isPositive)))
            );
        } else {
            delta1 = SafeCastLib.toInt128(
                sign * int256(uint256(amount1Delta(sqrtRatioLower, sqrtRatioUpper, magnitude, isPositive)))
            );
        }
    }
}

/// @notice Calculates the maximum liquidity that can be provided with a given amount of token0
/// @dev Used when the current price is below the position's range (only token0 is needed)
/// @param sqrtRatioLower The lower sqrt price ratio of the position
/// @param sqrtRatioUpper The upper sqrt price ratio of the position
/// @param amount The amount of token0 available
/// @return The maximum liquidity that can be provided
function maxLiquidityForToken0(uint256 sqrtRatioLower, uint256 sqrtRatioUpper, uint128 amount) pure returns (uint256) {
    unchecked {
        uint256 numerator1 = FixedPointMathLib.fullMulDivN(sqrtRatioLower, sqrtRatioUpper, 128);

        return FixedPointMathLib.fullMulDiv(amount, numerator1, (sqrtRatioUpper - sqrtRatioLower));
    }
}

/// @notice Calculates the maximum liquidity that can be provided with a given amount of token1
/// @dev Used when the current price is above the position's range (only token1 is needed)
/// @param sqrtRatioLower The lower sqrt price ratio of the position
/// @param sqrtRatioUpper The upper sqrt price ratio of the position
/// @param amount The amount of token1 available
/// @return The maximum liquidity that can be provided
function maxLiquidityForToken1(uint256 sqrtRatioLower, uint256 sqrtRatioUpper, uint128 amount) pure returns (uint256) {
    unchecked {
        return (uint256(amount) << 128) / (sqrtRatioUpper - sqrtRatioLower);
    }
}

/// @notice Calculates the maximum liquidity that can be provided given amounts of both tokens
/// @dev Determines the limiting factor between token0 and token1 based on current price and position bounds
/// @param _sqrtRatio Current sqrt price ratio
/// @param sqrtRatioA One bound of the position (will be sorted with sqrtRatioB)
/// @param sqrtRatioB Other bound of the position (will be sorted with sqrtRatioA)
/// @param amount0 Available amount of token0
/// @param amount1 Available amount of token1
/// @return The maximum liquidity that can be provided with the given token amounts
function maxLiquidity(
    SqrtRatio _sqrtRatio,
    SqrtRatio sqrtRatioA,
    SqrtRatio sqrtRatioB,
    uint128 amount0,
    uint128 amount1
) pure returns (uint128) {
    uint256 sqrtRatio = _sqrtRatio.toFixed();
    (uint256 sqrtRatioLower, uint256 sqrtRatioUpper) = sortAndConvertToFixedSqrtRatios(sqrtRatioA, sqrtRatioB);

    if (sqrtRatio <= sqrtRatioLower) {
        return uint128(
            FixedPointMathLib.min(type(uint128).max, maxLiquidityForToken0(sqrtRatioLower, sqrtRatioUpper, amount0))
        );
    } else if (sqrtRatio < sqrtRatioUpper) {
        return uint128(
            FixedPointMathLib.min(
                type(uint128).max,
                FixedPointMathLib.min(
                    maxLiquidityForToken0(sqrtRatio, sqrtRatioUpper, amount0),
                    maxLiquidityForToken1(sqrtRatioLower, sqrtRatio, amount1)
                )
            )
        );
    } else {
        return uint128(
            FixedPointMathLib.min(type(uint128).max, maxLiquidityForToken1(sqrtRatioLower, sqrtRatioUpper, amount1))
        );
    }
}

/// @notice Thrown when a liquidity delta operation would cause overflow
error LiquidityDeltaOverflow();

/// @notice Safely adds a liquidity delta to a liquidity amount
/// @dev Reverts if the operation would cause overflow or underflow
/// @param liquidity The current liquidity amount
/// @param liquidityDelta The change in liquidity (can be positive or negative)
/// @return result The new liquidity amount after applying the delta
function addLiquidityDelta(uint128 liquidity, int128 liquidityDelta) pure returns (uint128 result) {
    assembly ("memory-safe") {
        result := add(liquidity, liquidityDelta)
        if and(result, shl(128, 0xffffffffffffffffffffffffffffffff)) {
            mstore(0, shl(224, 0x6d862c50))
            revert(0, 4)
        }
    }
}

// SPDX-License-Identifier: ekubo-license-v1.eth
pragma solidity =0.8.33;

import {Bitmap} from "../types/bitmap.sol";
import {MIN_TICK, MAX_TICK} from "../math/constants.sol";
import {StorageSlot} from "../types/storageSlot.sol";

// Addition of this offset does two things--it centers the 0 tick within a single bitmap regardless of tick spacing,
// and gives us a contiguous range of unsigned integers for all ticks
uint256 constant TICK_BITMAP_STORAGE_OFFSET = 89421695;

// Returns the index of the word and the index _in_ that word which contains the bit representing whether the tick is initialized
// Always rounds the tick down to the nearest multiple of tickSpacing
function tickToBitmapWordAndIndex(int32 tick, uint32 tickSpacing) pure returns (uint256 word, uint256 index) {
    assembly ("memory-safe") {
        let rawIndex := add(sub(sdiv(tick, tickSpacing), slt(smod(tick, tickSpacing), 0)), TICK_BITMAP_STORAGE_OFFSET)
        word := shr(8, rawIndex)
        index := and(rawIndex, 0xff)
    }
}

// Returns the index of the word and the index _in_ that word which contains the bit representing whether the tick is initialized
/// @dev This function is only safe if tickSpacing is between 1 and MAX_TICK_SPACING, and word/index correspond to the results of tickToBitmapWordAndIndex for a tick between MIN_TICK and MAX_TICK
function bitmapWordAndIndexToTick(uint256 word, uint256 index, uint32 tickSpacing) pure returns (int32 tick) {
    assembly ("memory-safe") {
        let rawIndex := add(shl(8, word), index)
        tick := mul(sub(rawIndex, TICK_BITMAP_STORAGE_OFFSET), tickSpacing)
    }
}

function loadBitmap(StorageSlot slot, uint256 word) view returns (Bitmap bitmap) {
    bitmap = Bitmap.wrap(uint256(slot.add(word).load()));
}

// Flips the tick in the bitmap from true to false or vice versa
function flipTick(StorageSlot slot, int32 tick, uint32 tickSpacing) {
    (uint256 word, uint256 index) = tickToBitmapWordAndIndex(tick, tickSpacing);
    StorageSlot wordSlot = slot.add(word);
    wordSlot.store(wordSlot.load() ^ bytes32(1 << index));
}

function findNextInitializedTick(StorageSlot slot, int32 fromTick, uint32 tickSpacing, uint256 skipAhead)
    view
    returns (int32 nextTick, bool isInitialized)
{
    unchecked {
        nextTick = fromTick;

        while (true) {
            // convert the given tick to the bitmap position of the next nearest potential initialized tick
            (uint256 word, uint256 index) = tickToBitmapWordAndIndex(nextTick + int32(tickSpacing), tickSpacing);

            Bitmap bitmap = loadBitmap(slot, word);

            // find the index of the previous tick in that word
            uint256 nextIndex = bitmap.geSetBit(uint8(index));

            // if we found one, return it
            if (nextIndex != 0) {
                (nextTick, isInitialized) = (bitmapWordAndIndexToTick(word, nextIndex - 1, tickSpacing), true);
                break;
            }

            // otherwise, return the tick of the most significant bit in the word
            nextTick = bitmapWordAndIndexToTick(word, 255, tickSpacing);

            if (nextTick >= MAX_TICK) {
                nextTick = MAX_TICK;
                break;
            }

            // if we are done searching, stop here
            if (skipAhead == 0) {
                break;
            }

            skipAhead--;
        }
    }
}

function findPrevInitializedTick(StorageSlot slot, int32 fromTick, uint32 tickSpacing, uint256 skipAhead)
    view
    returns (int32 prevTick, bool isInitialized)
{
    unchecked {
        prevTick = fromTick;

        while (true) {
            // convert the given tick to its bitmap position
            (uint256 word, uint256 index) = tickToBitmapWordAndIndex(prevTick, tickSpacing);

            Bitmap bitmap = loadBitmap(slot, word);

            // find the index of the previous tick in that word
            uint256 prevIndex = bitmap.leSetBit(uint8(index));

            if (prevIndex != 0) {
                (prevTick, isInitialized) = (bitmapWordAndIndexToTick(word, prevIndex - 1, tickSpacing), true);
                break;
            }

            prevTick = bitmapWordAndIndexToTick(word, 0, tickSpacing);

            if (prevTick <= MIN_TICK) {
                prevTick = MIN_TICK;
                break;
            }

            if (skipAhead == 0) {
                break;
            }

            skipAhead--;
            prevTick--;
        }
    }
}

// SPDX-License-Identifier: ekubo-license-v1.eth
pragma solidity ^0.8.0;

import {CallPoints} from "../types/callPoints.sol";
import {PoolKey} from "../types/poolKey.sol";
import {PositionId} from "../types/positionId.sol";
import {FeesPerLiquidity} from "../types/feesPerLiquidity.sol";
import {IExposedStorage} from "../interfaces/IExposedStorage.sol";
import {IFlashAccountant} from "../interfaces/IFlashAccountant.sol";
import {SqrtRatio} from "../types/sqrtRatio.sol";
import {PoolState} from "../types/poolState.sol";
import {SwapParameters} from "../types/swapParameters.sol";
import {PoolId} from "../types/poolId.sol";
import {Locker} from "../types/locker.sol";
import {PoolBalanceUpdate} from "../types/poolBalanceUpdate.sol";

/// @title Extension Interface
/// @notice Interface for pool extensions that can hook into core operations
/// @dev Extensions must register with the core contract and implement these hooks
interface IExtension {
    /// @notice Called before a pool is initialized
    /// @param caller Address that initiated the pool initialization
    /// @param key Pool key identifying the pool
    /// @param tick Initial tick for the pool
    function beforeInitializePool(address caller, PoolKey calldata key, int32 tick) external;

    /// @notice Called after a pool is initialized
    /// @param caller Address that initiated the pool initialization
    /// @param key Pool key identifying the pool
    /// @param tick Initial tick for the pool
    /// @param sqrtRatio Initial sqrt price ratio for the pool
    function afterInitializePool(address caller, PoolKey calldata key, int32 tick, SqrtRatio sqrtRatio) external;

    /// @notice Called before a position is updated
    /// @param locker The current holder of the lock performing the position update
    /// @param poolKey Pool key identifying the pool
    /// @param positionId The key of the position that is being updated
    /// @param liquidityDelta The change in liquidity that is being requested for the position
    function beforeUpdatePosition(Locker locker, PoolKey memory poolKey, PositionId positionId, int128 liquidityDelta)
        external;

    /// @notice Called after a position is updated
    /// @param locker The current holder of the lock performing the position update
    /// @param poolKey Pool key identifying the pool
    /// @param positionId The key of the position that was updated
    /// @param liquidityDelta Change in liquidity of the specified position key range
    /// @param balanceUpdate Change in token balances of the pool (delta0 and delta1)
    function afterUpdatePosition(
        Locker locker,
        PoolKey memory poolKey,
        PositionId positionId,
        int128 liquidityDelta,
        PoolBalanceUpdate balanceUpdate,
        PoolState stateAfter
    ) external;

    /// @notice Called before a swap is executed
    /// @param locker The current holder of the lock performing the swap
    /// @param poolKey Pool key identifying the pool
    /// @param params Swap parameters containing amount, isToken1, sqrtRatioLimit, and skipAhead
    function beforeSwap(Locker locker, PoolKey memory poolKey, SwapParameters params) external;

    /// @notice Called after a swap is executed
    /// @param locker The current holder of the lock performing the swap
    /// @param poolKey Pool key identifying the pool
    /// @param params Swap parameters containing amount, isToken1, sqrtRatioLimit, and skipAhead
    /// @param balanceUpdate Change in token balances (delta0 and delta1)
    /// @param stateAfter The pool state after the swap
    function afterSwap(
        Locker locker,
        PoolKey memory poolKey,
        SwapParameters params,
        PoolBalanceUpdate balanceUpdate,
        PoolState stateAfter
    ) external;

    /// @notice Called before fees are collected from a position
    /// @param locker The current holder of the lock collecting fees
    /// @param poolKey Pool key identifying the pool
    /// @param positionId The key of the position for which fees will be collected
    function beforeCollectFees(Locker locker, PoolKey memory poolKey, PositionId positionId) external;

    /// @notice Called after fees are collected from a position
    /// @param locker The current holder of the lock collecting fees
    /// @param poolKey Pool key identifying the pool
    /// @param positionId The key of the position for which fees were collected
    /// @param amount0 Amount of token0 fees collected
    /// @param amount1 Amount of token1 fees collected
    function afterCollectFees(
        Locker locker,
        PoolKey memory poolKey,
        PositionId positionId,
        uint128 amount0,
        uint128 amount1
    ) external;
}

/// @title Core Interface
/// @notice Main interface for the Ekubo Protocol core contract
/// @dev Inherits from IFlashAccountant and IExposedStorage for additional functionality
interface ICore is IFlashAccountant, IExposedStorage {
    /// @notice Emitted when an extension is registered
    /// @param extension Address of the registered extension
    event ExtensionRegistered(address extension);

    /// @notice Emitted when a pool is initialized
    /// @param poolId Unique identifier for the pool
    /// @param poolKey Pool key containing token addresses and configuration
    /// @param tick Initial tick for the pool
    /// @param sqrtRatio Initial sqrt price ratio for the pool
    event PoolInitialized(PoolId poolId, PoolKey poolKey, int32 tick, SqrtRatio sqrtRatio);

    /// @notice Emitted when a position is updated
    /// @param locker The locker that is updating the position
    /// @param poolId Unique identifier for the pool
    /// @param positionId Identifier of the position specifying a salt and the bounds
    /// @param liquidityDelta The change in liquidity for the specified pool and position keys
    /// @param balanceUpdate Change in token balances (delta0 and delta1)
    event PositionUpdated(
        address locker,
        PoolId poolId,
        PositionId positionId,
        int128 liquidityDelta,
        PoolBalanceUpdate balanceUpdate,
        PoolState stateAfter
    );

    /// @notice Emitted when fees are collected from a position
    /// @param locker The locker that is collecting fees
    /// @param poolId Unique identifier for the pool
    /// @param positionId Identifier of the position specifying a salt and the bounds
    /// @param amount0 Amount of token0 fees collected
    /// @param amount1 Amount of token1 fees collected
    event PositionFeesCollected(address locker, PoolId poolId, PositionId positionId, uint128 amount0, uint128 amount1);

    /// @notice Emitted when fees are accumulated to a pool
    /// @param poolId Unique identifier for the pool
    /// @param amount0 Amount of token0 fees accumulated
    /// @param amount1 Amount of token1 fees accumulated
    /// @dev Note locker is ommitted because it's always the extension of the pool associated with poolId
    event FeesAccumulated(PoolId poolId, uint128 amount0, uint128 amount1);

    /// @notice Thrown when extension registration fails due to invalid call points
    error FailedRegisterInvalidCallPoints();

    /// @notice Thrown when trying to register an already registered extension
    error ExtensionAlreadyRegistered();

    /// @notice Thrown when saved balance operations would cause overflow
    error SavedBalanceOverflow();

    /// @notice Thrown when trying to initialize an already initialized pool
    error PoolAlreadyInitialized();

    /// @notice Thrown when trying to use an unregistered extension
    error ExtensionNotRegistered();

    /// @notice Thrown when trying to operate on an uninitialized pool
    error PoolNotInitialized();

    /// @notice Thrown when sqrt ratio limit is out of valid range
    error SqrtRatioLimitOutOfRange();

    /// @notice Thrown when sqrt ratio limit parameter given to swap is not a valid sqrt ratio
    error InvalidSqrtRatioLimit();

    /// @notice Thrown when the sqrt ratio limit is in the wrong direction of the current price
    error SqrtRatioLimitWrongDirection();

    /// @notice Thrown when saved balance tokens are not properly sorted
    error SavedBalanceTokensNotSorted();

    /// @notice Thrown when a position update would cause liquidityNet on a tick to exceed the maximum allowed
    /// @param tick The tick that would exceed the limit
    /// @param liquidityNet The resulting liquidityNet that exceeds the limit
    /// @param maxLiquidityPerTick The maximum allowed liquidity per tick
    error MaxLiquidityPerTickExceeded(int32 tick, uint128 liquidityNet, uint128 maxLiquidityPerTick);

    /// @notice Registers an extension with the core contract
    /// @dev Extensions must call this function to become registered. The call points are validated against the caller address
    /// @param expectedCallPoints Call points configuration for the extension
    function registerExtension(CallPoints memory expectedCallPoints) external;

    /// @notice Initializes a new pool with the given tick
    /// @dev Sets the initial price for a new pool in terms of tick
    /// @param poolKey Pool key identifying the pool to initialize
    /// @param tick Initial tick for the pool
    /// @return sqrtRatio Initial sqrt price ratio for the pool
    function initializePool(PoolKey memory poolKey, int32 tick) external returns (SqrtRatio sqrtRatio);

    /// @notice Finds the previous initialized tick
    /// @param poolId Unique identifier for the pool
    /// @param fromTick Starting tick to search from
    /// @param tickSpacing Tick spacing for the pool
    /// @param skipAhead Number of ticks to skip for gas optimization
    /// @return tick The previous initialized tick
    /// @return isInitialized Whether the tick is initialized
    function prevInitializedTick(PoolId poolId, int32 fromTick, uint32 tickSpacing, uint256 skipAhead)
        external
        view
        returns (int32 tick, bool isInitialized);

    /// @notice Finds the next initialized tick
    /// @param poolId Unique identifier for the pool
    /// @param fromTick Starting tick to search from
    /// @param tickSpacing Tick spacing for the pool
    /// @param skipAhead Number of ticks to skip for gas optimization
    /// @return tick The next initialized tick
    /// @return isInitialized Whether the tick is initialized
    function nextInitializedTick(PoolId poolId, int32 fromTick, uint32 tickSpacing, uint256 skipAhead)
        external
        view
        returns (int32 tick, bool isInitialized);

    /// @notice Updates saved balances for later use
    /// @dev The saved balances are stored in a single slot. The resulting saved balance must fit within a uint128 container
    /// @param token0 Address of the first token (must be < token1)
    /// @param token1 Address of the second token (must be > token0)
    /// @param salt Unique identifier for the saved balance
    /// @param delta0 Change in token0 balance (positive for saving, negative for loading)
    /// @param delta1 Change in token1 balance (positive for saving, negative for loading)
    function updateSavedBalances(address token0, address token1, bytes32 salt, int256 delta0, int256 delta1)
        external
        payable;

    /// @notice Returns the accumulated fees per liquidity inside the given bounds
    /// @dev The reason this getter is exposed is that it requires conditional SLOADs for maximum efficiency
    /// @param poolId The ID of the pool to fetch the fees per liquidity inside
    /// @param tickLower Lower bound of the price range to get the snapshot
    /// @param tickLower Upper bound of the price range to get the snapshot
    /// @return feesPerLiquidity Accumulated fees per liquidity inside the bounds
    function getPoolFeesPerLiquidityInside(PoolId poolId, int32 tickLower, int32 tickUpper)
        external
        view
        returns (FeesPerLiquidity memory feesPerLiquidity);

    /// @notice Accumulates tokens as fees for a pool
    /// @dev Only callable by the extension of the specified pool key. The current locker must be the extension.
    /// The extension must call this function within a lock callback
    /// @param poolKey Pool key identifying the pool
    /// @param amount0 Amount of token0 to accumulate as fees
    /// @param amount1 Amount of token1 to accumulate as fees
    function accumulateAsFees(PoolKey memory poolKey, uint128 amount0, uint128 amount1) external payable;

    /// @notice Updates a liquidity position and sets extra data
    /// @param poolKey Pool key identifying the pool
    /// @param positionId The key of the position to update
    /// @param liquidityDelta The change in liquidity
    /// @return balanceUpdate Change in token balances (delta0 and delta1)
    function updatePosition(PoolKey memory poolKey, PositionId positionId, int128 liquidityDelta)
        external
        payable
        returns (PoolBalanceUpdate balanceUpdate);

    /// @notice Sets extra data for the position
    /// @param poolId ID of the pool for which the position exists
    /// @param positionId The key of the position to set extra data at
    /// @param extraData The data to set on the position
    function setExtraData(PoolId poolId, PositionId positionId, bytes16 extraData) external;

    /// @notice Collects accumulated fees from a position
    /// @param poolKey Pool key identifying the pool
    /// @param positionId The key of the position for which to collect fees
    /// @return amount0 Amount of token0 fees collected
    /// @return amount1 Amount of token1 fees collected
    function collectFees(PoolKey memory poolKey, PositionId positionId)
        external
        returns (uint128 amount0, uint128 amount1);

    /// @notice Executes a swap against a pool
    /// @dev Function name is mined to have a zero function selector for gas efficiency
    function swap_6269342730() external payable;
}

// SPDX-License-Identifier: ekubo-license-v1.eth
pragma solidity =0.8.33;

import {NATIVE_TOKEN_ADDRESS} from "../math/constants.sol";
import {IFlashAccountant} from "../interfaces/IFlashAccountant.sol";
import {Locker} from "../types/locker.sol";

/// @title FlashAccountant
/// @notice Abstract contract that provides flash loan accounting functionality using transient storage
/// @dev This contract manages debt tracking for flash loans, allowing users to borrow tokens temporarily
///      and ensuring all debts are settled before the transaction completes. Uses transient storage
///      for gas-efficient temporary state management within a single transaction.
abstract contract FlashAccountant is IFlashAccountant {
    // These offsets are selected so that they do not accidentally overlap with any other base contract's use of transient storage

    /// @dev Transient storage slot for tracking the current locker ID and address
    /// @dev The stored ID is kept as id + 1 to facilitate the NotLocked check (zero means unlocked)
    /// @dev Generated using: cast keccak "FlashAccountant#CURRENT_LOCKER_SLOT"
    uint256 private constant _CURRENT_LOCKER_SLOT = 0x07cc7f5195d862f505d6b095c82f92e00cfc1766f5bca4383c28dc5fca1555fd;

    /// @dev Transient storage offset for tracking token debts for each locker
    /// @dev Generated using: cast keccak "FlashAccountant#_DEBT_LOCKER_TOKEN_ADDRESS_OFFSET"
    uint256 private constant _DEBT_LOCKER_TOKEN_ADDRESS_OFFSET =
        0x753dfe4b4dfb3ff6c11bbf6a97f3c094e91c003ce904a55cc5662fbad220f599;

    /// @dev Transient storage offset for tracking the count of tokens with non-zero debt for each locker
    /// @dev Generated using: cast keccak "FlashAccountant#NONZERO_DEBT_COUNT_OFFSET"
    uint256 private constant _NONZERO_DEBT_COUNT_OFFSET =
        0x7772acfd7e0f66ebb20a058830296c3dc1301b111d23348e1c961d324223190d;

    /// @dev Transient storage offset for tracking token balances during payment operations
    /// @dev Generated using: cast keccak "FlashAccountant#_PAYMENT_TOKEN_ADDRESS_OFFSET"
    uint256 private constant _PAYMENT_TOKEN_ADDRESS_OFFSET =
        0x6747da56dbd05b26a7ecd2a0106781585141cf07098ad54c0e049e4e86dccb8c;

    /// @notice Gets the current locker information from transient storage
    /// @dev Reverts with NotLocked() if no lock is currently active
    /// @return locker The current locker containing both id and address
    function _getLocker() internal view returns (Locker locker) {
        assembly ("memory-safe") {
            locker := tload(_CURRENT_LOCKER_SLOT)

            if iszero(locker) {
                // cast sig "NotLocked()"
                mstore(0, shl(224, 0x1834e265))
                revert(0, 4)
            }
        }
    }

    /// @notice Gets the current locker information and ensures the caller is the locker
    /// @dev Reverts with LockerOnly() if the caller is not the current locker
    /// @return locker The current locker containing both id and address (address must be msg.sender)
    function _requireLocker() internal view returns (Locker locker) {
        locker = _getLocker();
        if (locker.addr() != msg.sender) revert LockerOnly();
    }

    /// @notice Updates the debt tracking for a specific locker and token
    /// @dev We assume debtChange cannot exceed a 128 bits value, even though it uses a int256 container.
    ///      This must be enforced at the places it is called for this contract's safety.
    ///      Negative values erase debt, positive values add debt.
    ///      Updates the non-zero debt count when debt transitions between zero and non-zero states.
    /// @param id The locker ID to update debt for
    /// @param token The token address to update debt for
    /// @param debtChange The change in debt (negative to reduce, positive to increase)
    function _accountDebt(uint256 id, address token, int256 debtChange) internal {
        assembly ("memory-safe") {
            let deltaSlot := add(_DEBT_LOCKER_TOKEN_ADDRESS_OFFSET, add(shl(160, id), token))
            let current := tload(deltaSlot)

            // we know this never overflows because debtChange is only ever derived from 128 bit values in inheriting contracts
            let next := add(current, debtChange)

            let countChange := sub(iszero(current), iszero(next))

            if countChange {
                let nzdCountSlot := add(id, _NONZERO_DEBT_COUNT_OFFSET)
                tstore(nzdCountSlot, add(tload(nzdCountSlot), countChange))
            }

            tstore(deltaSlot, next)
        }
    }

    /// @notice Updates the debt tracking for a specific locker and pair of tokens in a single operation
    /// @dev Optimized version that updates both tokens' debts and performs a single tload/tstore on the non-zero debt count.
    ///      Individual token debt slots are still updated separately, but the non-zero debt count is only loaded/stored once.
    ///      We assume debtChange values cannot exceed 128 bits. This must be enforced at the places it is called for this contract's safety.
    ///      Negative values erase debt, positive values add debt.
    /// @param id The locker ID to update debt for
    /// @param tokenA The first token address
    /// @param tokenB The second token address
    /// @param debtChangeA The change in debt for tokenA (negative to reduce, positive to increase)
    /// @param debtChangeB The change in debt for tokenB (negative to reduce, positive to increase)
    function _updatePairDebt(uint256 id, address tokenA, address tokenB, int256 debtChangeA, int256 debtChangeB)
        internal
    {
        assembly ("memory-safe") {
            let nzdCountChange := 0

            // Update token0 debt if there's a change
            if debtChangeA {
                let deltaSlotA := add(_DEBT_LOCKER_TOKEN_ADDRESS_OFFSET, add(shl(160, id), tokenA))
                let currentA := tload(deltaSlotA)
                let nextA := add(currentA, debtChangeA)

                nzdCountChange := sub(iszero(currentA), iszero(nextA))

                tstore(deltaSlotA, nextA)
            }

            if debtChangeB {
                let deltaSlotB := add(_DEBT_LOCKER_TOKEN_ADDRESS_OFFSET, add(shl(160, id), tokenB))
                let currentB := tload(deltaSlotB)
                let nextB := add(currentB, debtChangeB)

                nzdCountChange := add(nzdCountChange, sub(iszero(currentB), iszero(nextB)))

                tstore(deltaSlotB, nextB)
            }

            // Update non-zero debt count only if it changed
            if nzdCountChange {
                let nzdCountSlot := add(id, _NONZERO_DEBT_COUNT_OFFSET)
                tstore(nzdCountSlot, add(tload(nzdCountSlot), nzdCountChange))
            }
        }
    }

    /// @inheritdoc IFlashAccountant
    function updateDebt() external {
        if (msg.data.length != 20) {
            revert UpdateDebtMessageLength();
        }

        uint256 id = _getLocker().id();
        int256 delta;
        assembly ("memory-safe") {
            delta := signextend(15, shr(128, calldataload(4)))
        }
        _accountDebt(id, msg.sender, delta);
    }

    /// @inheritdoc IFlashAccountant
    function lock() external {
        assembly ("memory-safe") {
            let current := tload(_CURRENT_LOCKER_SLOT)

            let id := shr(160, current)

            // store the count
            tstore(_CURRENT_LOCKER_SLOT, or(shl(160, add(id, 1)), caller()))

            let free := mload(0x40)
            // Prepare call to locked_(uint256) -> selector 0
            mstore(free, 0)
            mstore(add(free, 4), id) // ID argument

            calldatacopy(add(free, 36), 4, sub(calldatasize(), 4))

            // Call the original caller with the packed data
            let success := call(gas(), caller(), 0, free, add(calldatasize(), 32), 0, 0)

            // Pass through the error on failure
            if iszero(success) {
                returndatacopy(free, 0, returndatasize())
                revert(free, returndatasize())
            }

            // Undo the "locker" state changes
            tstore(_CURRENT_LOCKER_SLOT, current)

            // Check if something is nonzero
            let nonzeroDebtCount := tload(add(_NONZERO_DEBT_COUNT_OFFSET, id))
            if nonzeroDebtCount {
                // cast sig "DebtsNotZeroed(uint256)"
                mstore(0x00, 0x9731ba37)
                mstore(0x20, id)
                revert(0x1c, 0x24)
            }

            // Directly return whatever the subcall returned
            returndatacopy(free, 0, returndatasize())
            return(free, returndatasize())
        }
    }

    /// @inheritdoc IFlashAccountant
    function forward(address to) external {
        Locker locker = _requireLocker();

        // update this lock's locker to the forwarded address for the duration of the forwarded
        // call, meaning only the forwarded address can update state
        assembly ("memory-safe") {
            tstore(_CURRENT_LOCKER_SLOT, or(shl(160, shr(160, locker)), to))

            let free := mload(0x40)

            // Prepare call to forwarded_2374103877(bytes32) -> selector 0x01
            mstore(free, shl(224, 1))
            mstore(add(free, 4), locker)

            calldatacopy(add(free, 36), 36, sub(calldatasize(), 36))

            // Call the forwardee with the packed data
            let success := call(gas(), to, 0, free, calldatasize(), 0, 0)

            // Pass through the error on failure
            if iszero(success) {
                returndatacopy(free, 0, returndatasize())
                revert(free, returndatasize())
            }

            tstore(_CURRENT_LOCKER_SLOT, locker)

            // Directly return whatever the subcall returned
            returndatacopy(free, 0, returndatasize())
            return(free, returndatasize())
        }
    }

    /// @inheritdoc IFlashAccountant
    function startPayments() external {
        assembly ("memory-safe") {
            // 0-52 are used for the balanceOf calldata
            mstore(20, address()) // Store the `account` argument.
            mstore(0, 0x70a08231000000000000000000000000) // `balanceOf(address)`.

            let free := mload(0x40)

            for { let i := 4 } lt(i, calldatasize()) { i := add(i, 32) } {
                // clean upper 96 bits of the token argument at i
                let token := shr(96, shl(96, calldataload(i)))

                let returnLocation := add(free, sub(i, 4))

                let success := staticcall(gas(), token, 0x10, 0x24, returnLocation, 0x20)

                let tokenBalance :=
                    mul(
                        mload(returnLocation),
                        and(
                            gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                            success
                        )
                    )

                tstore(add(_PAYMENT_TOKEN_ADDRESS_OFFSET, token), add(tokenBalance, success))
            }

            return(free, sub(calldatasize(), 4))
        }
    }

    /// @inheritdoc IFlashAccountant
    function completePayments() external {
        uint256 id = _getLocker().id();

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

            for { let i := 4 } lt(i, calldatasize()) { i := add(i, 32) } {
                let token := shr(96, shl(96, calldataload(i)))

                let offset := add(_PAYMENT_TOKEN_ADDRESS_OFFSET, token)
                let lastBalance := tload(offset)
                tstore(offset, 0)

                mstore(20, address()) // Store the `account` argument.
                mstore(0, 0x70a08231000000000000000000000000) // `balanceOf(address)`.

                let currentBalance :=
                    mul( // The arguments of `mul` are evaluated from right to left.
                        mload(0),
                        and( // The arguments of `and` are evaluated from right to left.
                            gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                            staticcall(gas(), token, 0x10, 0x24, 0, 0x20)
                        )
                    )

                let payment :=
                    mul(
                        and(gt(lastBalance, 0), not(lt(currentBalance, lastBalance))),
                        sub(currentBalance, sub(lastBalance, 1))
                    )

                // We never expect tokens to have this much total supply
                if shr(128, payment) {
                    // cast sig "PaymentOverflow()"
                    mstore(0x00, 0x9cac58ca)
                    revert(0x1c, 4)
                }

                mstore(add(paymentAmounts, mul(16, div(i, 32))), shl(128, payment))

                if payment {
                    let deltaSlot := add(_DEBT_LOCKER_TOKEN_ADDRESS_OFFSET, add(shl(160, id), token))
                    let current := tload(deltaSlot)

                    // never overflows because of the payment overflow check that bounds payment to 128 bits
                    let next := sub(current, payment)

                    nzdCountChange := add(nzdCountChange, sub(iszero(current), iszero(next)))

                    tstore(deltaSlot, next)
                }
            }

            // Update nzdCountSlot only once if there were any changes
            if nzdCountChange {
                let nzdCountSlot := add(id, _NONZERO_DEBT_COUNT_OFFSET)
                tstore(nzdCountSlot, add(tload(nzdCountSlot), nzdCountChange))
            }

            return(paymentAmounts, mul(16, div(calldatasize(), 32)))
        }
    }

    /// @inheritdoc IFlashAccountant
    function withdraw() external {
        uint256 id = _requireLocker().id();

        assembly ("memory-safe") {
            let nzdCountChange := 0

            // Process each withdrawal entry
            for { let i := 4 } lt(i, calldatasize()) { i := add(i, 56) } {
                let token := shr(96, calldataload(i))
                let recipient := shr(96, calldataload(add(i, 20)))
                let amount := shr(128, calldataload(add(i, 40)))

                if amount {
                    // Update debt tracking without updating nzdCountSlot yet
                    let deltaSlot := add(_DEBT_LOCKER_TOKEN_ADDRESS_OFFSET, add(shl(160, id), token))
                    let current := tload(deltaSlot)
                    let next := add(current, amount)

                    nzdCountChange := add(nzdCountChange, sub(iszero(current), iszero(next)))

                    tstore(deltaSlot, next)

                    // Perform the transfer of the withdrawn asset
                    // Note that these calls can re-enter and even relock with the same ID
                    // However the nzdCountChange is always applied as a delta at the end, meaning we load the latest value before updating it,
                    // so it's safe from re-entry
                    switch token
                    case 0 {
                        let success := call(gas(), recipient, amount, 0, 0, 0, 0)
                        if iszero(success) {
                            // cast sig "ETHTransferFailed()"
                            mstore(0x00, 0xb12d13eb)
                            revert(0x1c, 4)
                        }
                    }
                    default {
                        mstore(0x14, recipient)
                        mstore(0x34, amount)
                        mstore(0x00, 0xa9059cbb000000000000000000000000)
                        let success := call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                        if iszero(and(eq(mload(0x00), 1), success)) {
                            if iszero(lt(or(iszero(extcodesize(token)), returndatasize()), success)) {
                                mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
                                revert(0x1c, 0x04)
                            }
                        }
                    }
                }
            }

            // Update nzdCountSlot only once if there were any changes
            if nzdCountChange {
                let nzdCountSlot := add(id, _NONZERO_DEBT_COUNT_OFFSET)
                tstore(nzdCountSlot, add(tload(nzdCountSlot), nzdCountChange))
            }

            // we return from assembly so as to prevent solidity from accessing the free memory pointer after we have written into it
            return(0, 0)
        }
    }

    /// @inheritdoc IFlashAccountant
    receive() external payable {
        uint256 id = _getLocker().id();

        // Note because we use msg.value here, this contract can never be multicallable, i.e. it should never expose the ability
        //      to delegatecall itself more than once in a single call
        unchecked {
            // We assume msg.value will never exceed type(uint128).max, so this should never cause an overflow/underflow of debt
            _accountDebt(id, NATIVE_TOKEN_ADDRESS, -int256(msg.value));
        }
    }
}

File 18 of 33 : constants.sol
// SPDX-License-Identifier: ekubo-license-v1.eth
pragma solidity =0.8.33;

// Protocol Constants
// Contains all constant values used throughout the Ekubo Protocol
// These constants define the boundaries and special values for the protocol's operation

// The minimum tick value supported by the protocol
// Corresponds to the minimum possible price ratio in the protocol
int32 constant MIN_TICK = -88722835;

// The maximum tick value supported by the protocol
// Corresponds to the maximum possible price ratio in the protocol
int32 constant MAX_TICK = 88722835;

// The maximum tick magnitude (absolute value of MAX_TICK)
// Used for validation and bounds checking in tick-related calculations
uint32 constant MAX_TICK_MAGNITUDE = uint32(MAX_TICK);

// The maximum allowed tick spacing for pools
// Defines the upper limit for tick spacing configuration in pool creation
uint32 constant MAX_TICK_SPACING = 698605;

// Address used to represent the native token (ETH) within the protocol
// Using address(0) allows the protocol to handle native ETH alongside ERC20 tokens
address constant NATIVE_TOKEN_ADDRESS = address(0);

// SPDX-License-Identifier: ekubo-license-v1.eth
pragma solidity =0.8.33;

// A dynamic fixed point number (a la floating point) that stores a shifting 94 bit view of the underlying fixed point value,
//  based on the most significant bits (mantissa)
// If the most significant 2 bits are 11, it represents a 64.30
// If the most significant 2 bits are 10, it represents a 32.62 number
// If the most significant 2 bits are 01, it represents a 0.94 number
// If the most significant 2 bits are 00, it represents a 0.126 number that is always less than 2**-32

type SqrtRatio is uint96;

uint96 constant MIN_SQRT_RATIO_RAW = 4611797791050542631;
SqrtRatio constant MIN_SQRT_RATIO = SqrtRatio.wrap(MIN_SQRT_RATIO_RAW);
uint96 constant MAX_SQRT_RATIO_RAW = 79227682466138141934206691491;
SqrtRatio constant MAX_SQRT_RATIO = SqrtRatio.wrap(MAX_SQRT_RATIO_RAW);

uint96 constant TWO_POW_95 = 0x800000000000000000000000;
uint96 constant TWO_POW_94 = 0x400000000000000000000000;
uint96 constant TWO_POW_62 = 0x4000000000000000;
uint96 constant TWO_POW_62_MINUS_ONE = 0x3fffffffffffffff;
uint96 constant BIT_MASK = 0xc00000000000000000000000; // TWO_POW_95 | TWO_POW_94

SqrtRatio constant ONE = SqrtRatio.wrap((TWO_POW_95) + (1 << 62));

using {
    toFixed,
    isValid,
    ge as >=,
    le as <=,
    lt as <,
    gt as >,
    eq as ==,
    neq as !=,
    isZero,
    min,
    max
} for SqrtRatio global;

function isValid(SqrtRatio sqrtRatio) pure returns (bool r) {
    assembly ("memory-safe") {
        r := and(
            // greater than or equal to TWO_POW_62, i.e. the whole number portion is nonzero
            gt(and(sqrtRatio, not(BIT_MASK)), TWO_POW_62_MINUS_ONE),
            // and between min/max sqrt ratio
            and(iszero(lt(sqrtRatio, MIN_SQRT_RATIO_RAW)), iszero(gt(sqrtRatio, MAX_SQRT_RATIO_RAW)))
        )
    }
}

error ValueOverflowsSqrtRatioContainer();

// If passing a value greater than this constant with roundUp = true, toSqrtRatio will overflow
// For roundUp = false, the constant is type(uint192).max
uint256 constant MAX_FIXED_VALUE_ROUND_UP =
    0x1000000000000000000000000000000000000000000000000 - 0x4000000000000000000000000;

// Converts a 64.128 value into the compact SqrtRatio representation
function toSqrtRatio(uint256 sqrtRatio, bool roundUp) pure returns (SqrtRatio r) {
    assembly ("memory-safe") {
        function compute(sr, ru) -> v {
            // rup = 0x00...00 when false, 0xff...ff when true
            let rup := sub(0, ru)

            // Region: < 2**96  (shift = 2)
            let addmask := and(0x3, rup) // (1<<s)-1 if ru
            if lt(add(sr, addmask), shl(96, 1)) {
                v := shr(2, add(sr, addmask))
                leave
            }

            // Region: < 2**128 (shift = 34)  + set bit 94
            addmask := and(0x3ffffffff, rup)
            if lt(add(sr, addmask), shl(128, 1)) {
                v := or(shl(94, 1), shr(34, add(sr, addmask)))
                leave
            }

            // Region: < 2**160 (shift = 66)  + set bit 95
            addmask := and(0x3ffffffffffffffff, rup)
            if lt(add(sr, addmask), shl(160, 1)) {
                v := or(shl(95, 1), shr(66, add(sr, addmask)))
                leave
            }

            // Region: < 2**192 (shift = 98)  + set bits 95|94
            addmask := and(0x3ffffffffffffffffffffffff, rup)
            if lt(add(sr, addmask), shl(192, 1)) {
                v := or(shl(94, 3), shr(98, add(sr, addmask))) // 3<<94 == bit95|bit94
                leave
            }

            // cast sig "ValueOverflowsSqrtRatioContainer()"
            mstore(0, shl(224, 0xa10459f4))
            revert(0, 4)
        }
        r := compute(sqrtRatio, roundUp)
    }
}

// Returns the 64.128 representation of the given sqrt ratio
function toFixed(SqrtRatio sqrtRatio) pure returns (uint256 r) {
    assembly ("memory-safe") {
        r := shl(add(2, shr(89, and(sqrtRatio, BIT_MASK))), and(sqrtRatio, not(BIT_MASK)))
    }
}

// The below operators assume that the SqrtRatio is valid, i.e. SqrtRatio#isValid returns true

function lt(SqrtRatio a, SqrtRatio b) pure returns (bool r) {
    r = SqrtRatio.unwrap(a) < SqrtRatio.unwrap(b);
}

function gt(SqrtRatio a, SqrtRatio b) pure returns (bool r) {
    r = SqrtRatio.unwrap(a) > SqrtRatio.unwrap(b);
}

function le(SqrtRatio a, SqrtRatio b) pure returns (bool r) {
    r = SqrtRatio.unwrap(a) <= SqrtRatio.unwrap(b);
}

function ge(SqrtRatio a, SqrtRatio b) pure returns (bool r) {
    r = SqrtRatio.unwrap(a) >= SqrtRatio.unwrap(b);
}

function eq(SqrtRatio a, SqrtRatio b) pure returns (bool r) {
    r = SqrtRatio.unwrap(a) == SqrtRatio.unwrap(b);
}

function neq(SqrtRatio a, SqrtRatio b) pure returns (bool r) {
    r = SqrtRatio.unwrap(a) != SqrtRatio.unwrap(b);
}

function isZero(SqrtRatio a) pure returns (bool r) {
    assembly ("memory-safe") {
        r := iszero(a)
    }
}

function max(SqrtRatio a, SqrtRatio b) pure returns (SqrtRatio r) {
    assembly ("memory-safe") {
        r := xor(a, mul(xor(a, b), gt(b, a)))
    }
}

function min(SqrtRatio a, SqrtRatio b) pure returns (SqrtRatio r) {
    assembly ("memory-safe") {
        r := xor(a, mul(xor(a, b), lt(b, a)))
    }
}

// SPDX-License-Identifier: ekubo-license-v1.eth
pragma solidity =0.8.33;

import {SqrtRatio} from "./sqrtRatio.sol";

type PoolState is bytes32;

using {sqrtRatio, tick, liquidity, isInitialized, parse} for PoolState global;

function sqrtRatio(PoolState state) pure returns (SqrtRatio r) {
    assembly ("memory-safe") {
        r := shr(160, state)
    }
}

function tick(PoolState state) pure returns (int32 t) {
    assembly ("memory-safe") {
        t := signextend(3, shr(128, state))
    }
}

function liquidity(PoolState state) pure returns (uint128 l) {
    assembly ("memory-safe") {
        l := shr(128, shl(128, state))
    }
}

function isInitialized(PoolState state) pure returns (bool yes) {
    assembly ("memory-safe") {
        yes := iszero(iszero(state))
    }
}

function parse(PoolState state) pure returns (SqrtRatio r, int32 t, uint128 l) {
    assembly ("memory-safe") {
        r := shr(160, state)
        t := signextend(3, shr(128, state))
        l := shr(128, shl(128, state))
    }
}

function createPoolState(SqrtRatio _sqrtRatio, int32 _tick, uint128 _liquidity) pure returns (PoolState s) {
    assembly ("memory-safe") {
        // s = (sqrtRatio << 160) | (_tick << 128) | liquidity
        s := or(shl(160, _sqrtRatio), or(shl(128, and(_tick, 0xFFFFFFFF)), shr(128, shl(128, _liquidity))))
    }
}

// SPDX-License-Identifier: ekubo-license-v1.eth
pragma solidity =0.8.33;

import {SqrtRatio, MIN_SQRT_RATIO_RAW, MAX_SQRT_RATIO_RAW} from "./sqrtRatio.sol";

type SwapParameters is bytes32;

using {
    sqrtRatioLimit,
    amount,
    isToken1,
    skipAhead,
    isExactOut,
    isPriceIncreasing,
    withDefaultSqrtRatioLimit
} for SwapParameters global;

function sqrtRatioLimit(SwapParameters params) pure returns (SqrtRatio r) {
    assembly ("memory-safe") {
        r := shr(160, params)
    }
}

function amount(SwapParameters params) pure returns (int128 a) {
    assembly ("memory-safe") {
        a := signextend(15, shr(32, params))
    }
}

function isToken1(SwapParameters params) pure returns (bool t) {
    assembly ("memory-safe") {
        t := and(shr(31, params), 1)
    }
}

function skipAhead(SwapParameters params) pure returns (uint256 s) {
    assembly ("memory-safe") {
        s := and(params, 0x7fffffff)
    }
}

function createSwapParameters(SqrtRatio _sqrtRatioLimit, int128 _amount, bool _isToken1, uint256 _skipAhead)
    pure
    returns (SwapParameters p)
{
    assembly ("memory-safe") {
        // p = (sqrtRatioLimit << 160) | (amount << 32) | (isToken1 << 31) | skipAhead
        // Mask each field to ensure dirty bits don't interfere
        // For isToken1, use iszero(iszero()) to convert any non-zero value to 1
        p := or(
            shl(160, _sqrtRatioLimit),
            or(
                shl(32, and(_amount, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)),
                or(shl(31, iszero(iszero(_isToken1))), and(_skipAhead, 0x7fffffff))
            )
        )
    }
}

function isExactOut(SwapParameters params) pure returns (bool yes) {
    assembly ("memory-safe") {
        yes := and(shr(159, params), 1)
    }
}

function isPriceIncreasing(SwapParameters params) pure returns (bool yes) {
    bool _isExactOut = params.isExactOut();
    bool _isToken1 = params.isToken1();
    assembly ("memory-safe") {
        yes := xor(_isExactOut, _isToken1)
    }
}

function withDefaultSqrtRatioLimit(SwapParameters params) pure returns (SwapParameters updated) {
    bool increasing = params.isPriceIncreasing();
    assembly ("memory-safe") {
        let replace := iszero(shr(160, params))
        let orMask :=
            shl(160, mul(replace, or(mul(increasing, MAX_SQRT_RATIO_RAW), mul(iszero(increasing), MIN_SQRT_RATIO_RAW))))
        updated := or(orMask, params)
    }
}

// SPDX-License-Identifier: ekubo-license-v1.eth
pragma solidity =0.8.33;

type TickInfo is bytes32;

using {liquidityDelta, liquidityNet, parse} for TickInfo global;

function liquidityDelta(TickInfo info) pure returns (int128 delta) {
    assembly ("memory-safe") {
        delta := signextend(15, info)
    }
}

function liquidityNet(TickInfo info) pure returns (uint128 net) {
    assembly ("memory-safe") {
        net := shr(128, info)
    }
}

function parse(TickInfo info) pure returns (int128 delta, uint128 net) {
    assembly ("memory-safe") {
        delta := signextend(15, info)
        net := shr(128, info)
    }
}

function createTickInfo(int128 _liquidityDelta, uint128 _liquidityNet) pure returns (TickInfo info) {
    assembly ("memory-safe") {
        // info = (liquidityNet << 128) | liquidityDelta
        info := or(shl(128, _liquidityNet), shr(128, shl(128, _liquidityDelta)))
    }
}

// SPDX-License-Identifier: ekubo-license-v1.eth
pragma solidity =0.8.33;

type PoolBalanceUpdate is bytes32;

using {delta0, delta1} for PoolBalanceUpdate global;

function delta0(PoolBalanceUpdate update) pure returns (int128 v) {
    assembly ("memory-safe") {
        v := signextend(15, shr(128, update))
    }
}

function delta1(PoolBalanceUpdate update) pure returns (int128 v) {
    assembly ("memory-safe") {
        v := signextend(15, update)
    }
}

function createPoolBalanceUpdate(int128 _delta0, int128 _delta1) pure returns (PoolBalanceUpdate update) {
    assembly ("memory-safe") {
        // update = (delta0 << 128) | delta1
        update := or(shl(128, _delta0), and(_delta1, 0xffffffffffffffffffffffffffffffff))
    }
}

File 24 of 33 : poolId.sol
// SPDX-License-Identifier: ekubo-license-v1.eth
pragma solidity =0.8.33;

/// @notice Unique identifier for a pool
/// @dev Wraps bytes32 to provide type safety for pool identifiers
type PoolId is bytes32;

// SPDX-License-Identifier: ekubo-license-v1.eth
pragma solidity =0.8.33;

type Locker is bytes32;

using {id, addr, parse} for Locker global;

function id(Locker locker) pure returns (uint256 v) {
    assembly ("memory-safe") {
        v := sub(shr(160, locker), 1)
    }
}

function addr(Locker locker) pure returns (address v) {
    assembly ("memory-safe") {
        v := shr(96, shl(96, locker))
    }
}

function parse(Locker locker) pure returns (uint256 lockerId, address lockerAddr) {
    assembly ("memory-safe") {
        lockerId := sub(shr(160, locker), 1)
        lockerAddr := shr(96, shl(96, locker))
    }
}

File 26 of 33 : fee.sol
// SPDX-License-Identifier: ekubo-license-v1.eth
pragma solidity =0.8.33;

// Returns the fee to charge based on the amount, which is the fee (a 0.64 number) times the
// amount, rounded up
function computeFee(uint128 amount, uint64 fee) pure returns (uint128 result) {
    assembly ("memory-safe") {
        result := shr(64, add(mul(amount, fee), 0xffffffffffffffff))
    }
}

error AmountBeforeFeeOverflow();

// Returns the amount before the fee is applied, which is the amount minus the fee, rounded up
function amountBeforeFee(uint128 afterFee, uint64 fee) pure returns (uint128 result) {
    assembly ("memory-safe") {
        let v := shl(64, afterFee)
        let d := sub(0x10000000000000000, fee)
        result := add(iszero(iszero(mod(v, d))), div(v, d))
        if shr(128, result) {
            mstore(0, 0x0d88f526)
            revert(0x1c, 0x04)
        }
    }
}

File 27 of 33 : sqrtRatio.sol
// SPDX-License-Identifier: ekubo-license-v1.eth
pragma solidity =0.8.33;

import {FixedPointMathLib} from "solady/utils/FixedPointMathLib.sol";
import {SqrtRatio, toSqrtRatio, MAX_FIXED_VALUE_ROUND_UP} from "../types/sqrtRatio.sol";

// Compute the next ratio from a delta amount0, always rounded towards starting price for input, and
// away from starting price for output
/// @dev Assumes sqrt ratio and liquidity are non-zero
function nextSqrtRatioFromAmount0(SqrtRatio _sqrtRatio, uint128 liquidity, int128 amount)
    pure
    returns (SqrtRatio sqrtRatioNext)
{
    if (amount == 0) {
        return _sqrtRatio;
    }

    uint256 sqrtRatio = _sqrtRatio.toFixed();

    uint256 liquidityX128;
    assembly ("memory-safe") {
        liquidityX128 := shl(128, liquidity)
    }

    if (amount < 0) {
        uint256 amountAbs;
        assembly ("memory-safe") {
            amountAbs := sub(0, amount)
        }
        unchecked {
            // multiplication will revert on overflow, so we return the maximum value for the type
            if (amountAbs > FixedPointMathLib.rawDiv(type(uint256).max, sqrtRatio)) {
                return SqrtRatio.wrap(type(uint96).max);
            }

            uint256 product = sqrtRatio * amountAbs;

            // again it will overflow if this is the case, so return the max value
            if (product >= liquidityX128) {
                return SqrtRatio.wrap(type(uint96).max);
            }

            uint256 denominator = liquidityX128 - product;

            uint256 resultFixed = FixedPointMathLib.fullMulDivUp(liquidityX128, sqrtRatio, denominator);

            if (resultFixed > MAX_FIXED_VALUE_ROUND_UP) {
                return SqrtRatio.wrap(type(uint96).max);
            }

            sqrtRatioNext = toSqrtRatio(resultFixed, true);
        }
    } else {
        uint256 sqrtRatioRaw;
        assembly ("memory-safe") {
            // this can never overflow, amountAbs is limited to 2**128-1 and liquidityX128 / sqrtRatio is limited to (2**128-1 << 128)
            // adding the 2 values can at most equal type(uint256).max
            let denominator := add(div(liquidityX128, sqrtRatio), amount)
            sqrtRatioRaw := add(div(liquidityX128, denominator), iszero(iszero(mod(liquidityX128, denominator))))
        }

        sqrtRatioNext = toSqrtRatio(sqrtRatioRaw, true);
    }
}

/// @dev Assumes liquidity is non-zero
function nextSqrtRatioFromAmount1(SqrtRatio _sqrtRatio, uint128 liquidity, int128 amount)
    pure
    returns (SqrtRatio sqrtRatioNext)
{
    uint256 sqrtRatio = _sqrtRatio.toFixed();

    unchecked {
        uint256 liquidityU256;
        assembly ("memory-safe") {
            liquidityU256 := liquidity
        }

        if (amount < 0) {
            uint256 quotient;
            assembly ("memory-safe") {
                let numerator := shl(128, sub(0, amount))
                quotient := add(div(numerator, liquidityU256), iszero(iszero(mod(numerator, liquidityU256))))
            }

            uint256 sqrtRatioNextFixed = FixedPointMathLib.zeroFloorSub(sqrtRatio, quotient);

            sqrtRatioNext = toSqrtRatio(sqrtRatioNextFixed, false);
        } else {
            uint256 quotient;
            assembly ("memory-safe") {
                quotient := div(shl(128, amount), liquidityU256)
            }
            uint256 sum = sqrtRatio + quotient;
            if (sum < sqrtRatio || sum > type(uint192).max) {
                return SqrtRatio.wrap(type(uint96).max);
            }
            sqrtRatioNext = toSqrtRatio(sum, false);
        }
    }
}

// SPDX-License-Identifier: ekubo-license-v1.eth
pragma solidity =0.8.33;

import {FixedPointMathLib} from "solady/utils/FixedPointMathLib.sol";
import {SqrtRatio} from "../types/sqrtRatio.sol";

error Amount0DeltaOverflow();
error Amount1DeltaOverflow();

function sortAndConvertToFixedSqrtRatios(SqrtRatio sqrtRatioA, SqrtRatio sqrtRatioB)
    pure
    returns (uint256 sqrtRatioLower, uint256 sqrtRatioUpper)
{
    sqrtRatioLower = sqrtRatioA.toFixed();
    sqrtRatioUpper = sqrtRatioB.toFixed();
    assembly ("memory-safe") {
        let diff := mul(sub(sqrtRatioLower, sqrtRatioUpper), gt(sqrtRatioLower, sqrtRatioUpper))

        sqrtRatioLower := sub(sqrtRatioLower, diff)
        sqrtRatioUpper := add(sqrtRatioUpper, diff)
    }
}

/// @dev Assumes that the sqrt ratios are valid
function amount0Delta(SqrtRatio sqrtRatioA, SqrtRatio sqrtRatioB, uint128 liquidity, bool roundUp)
    pure
    returns (uint128 amount0)
{
    (uint256 sqrtRatioLower, uint256 sqrtRatioUpper) = sortAndConvertToFixedSqrtRatios(sqrtRatioA, sqrtRatioB);
    amount0 = amount0DeltaSorted(sqrtRatioLower, sqrtRatioUpper, liquidity, roundUp);
}

/// @dev Assumes that the sqrt ratios are non-zero and sorted
function amount0DeltaSorted(uint256 sqrtRatioLower, uint256 sqrtRatioUpper, uint128 liquidity, bool roundUp)
    pure
    returns (uint128 amount0)
{
    unchecked {
        uint256 liquidityX128;
        assembly ("memory-safe") {
            liquidityX128 := shl(128, liquidity)
        }
        if (roundUp) {
            uint256 result0 =
                FixedPointMathLib.fullMulDivUp(liquidityX128, (sqrtRatioUpper - sqrtRatioLower), sqrtRatioUpper);
            assembly ("memory-safe") {
                let result := add(div(result0, sqrtRatioLower), iszero(iszero(mod(result0, sqrtRatioLower))))
                if shr(128, result) {
                    // cast sig "Amount0DeltaOverflow()"
                    mstore(0, 0xb4ef2546)
                    revert(0x1c, 0x04)
                }
                amount0 := result
            }
        } else {
            uint256 result0 =
                FixedPointMathLib.fullMulDivUnchecked(liquidityX128, (sqrtRatioUpper - sqrtRatioLower), sqrtRatioUpper);
            uint256 result = FixedPointMathLib.rawDiv(result0, sqrtRatioLower);
            assembly ("memory-safe") {
                if shr(128, result) {
                    // cast sig "Amount0DeltaOverflow()"
                    mstore(0, 0xb4ef2546)
                    revert(0x1c, 0x04)
                }
                amount0 := result
            }
        }
    }
}

/// @dev Assumes that the sqrt ratios are valid
function amount1Delta(SqrtRatio sqrtRatioA, SqrtRatio sqrtRatioB, uint128 liquidity, bool roundUp)
    pure
    returns (uint128 amount1)
{
    (uint256 sqrtRatioLower, uint256 sqrtRatioUpper) = sortAndConvertToFixedSqrtRatios(sqrtRatioA, sqrtRatioB);
    amount1 = amount1DeltaSorted(sqrtRatioLower, sqrtRatioUpper, liquidity, roundUp);
}

function amount1DeltaSorted(uint256 sqrtRatioLower, uint256 sqrtRatioUpper, uint128 liquidity, bool roundUp)
    pure
    returns (uint128 amount1)
{
    unchecked {
        uint256 difference = sqrtRatioUpper - sqrtRatioLower;
        uint256 liquidityU256;
        assembly ("memory-safe") {
            liquidityU256 := liquidity
        }

        if (roundUp) {
            uint256 result = FixedPointMathLib.fullMulDivN(difference, liquidityU256, 128);
            assembly ("memory-safe") {
                // addition is safe from overflow because the result of fullMulDivN will never equal type(uint256).max
                result := add(
                    result,
                    iszero(iszero(mulmod(difference, liquidityU256, 0x100000000000000000000000000000000)))
                )
                if shr(128, result) {
                    // cast sig "Amount1DeltaOverflow()"
                    mstore(0, 0x59d2b24a)
                    revert(0x1c, 0x04)
                }
                amount1 := result
            }
        } else {
            uint256 result = FixedPointMathLib.fullMulDivN(difference, liquidityU256, 128);
            assembly ("memory-safe") {
                if shr(128, result) {
                    // cast sig "Amount1DeltaOverflow()"
                    mstore(0, 0x59d2b24a)
                    revert(0x1c, 0x04)
                }
                amount1 := result
            }
        }
    }
}

// SPDX-License-Identifier: ekubo-license-v1.eth
pragma solidity =0.8.33;

type StorageSlot is bytes32;

using {load, loadTwo, store, storeTwo, next, add, sub} for StorageSlot global;

function load(StorageSlot slot) view returns (bytes32 value) {
    assembly ("memory-safe") {
        value := sload(slot)
    }
}

function loadTwo(StorageSlot slot) view returns (bytes32 value0, bytes32 value1) {
    value0 = slot.load();
    value1 = slot.next().load();
}

function store(StorageSlot slot, bytes32 value) {
    assembly ("memory-safe") {
        sstore(slot, value)
    }
}

function storeTwo(StorageSlot slot, bytes32 value0, bytes32 value1) {
    slot.store(value0);
    slot.next().store(value1);
}

function next(StorageSlot slot) pure returns (StorageSlot nextSlot) {
    assembly ("memory-safe") {
        nextSlot := add(slot, 1)
    }
}

function add(StorageSlot slot, uint256 addend) pure returns (StorageSlot summedSlot) {
    assembly ("memory-safe") {
        summedSlot := add(slot, addend)
    }
}

function sub(StorageSlot slot, uint256 subtrahend) pure returns (StorageSlot differenceSlot) {
    assembly ("memory-safe") {
        differenceSlot := sub(slot, subtrahend)
    }
}

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

/// @notice Library for bit twiddling and boolean operations.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibBit.sol)
/// @author Inspired by (https://graphics.stanford.edu/~seander/bithacks.html)
library LibBit {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  BIT TWIDDLING OPERATIONS                  */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Find last set.
    /// Returns the index of the most significant bit of `x`,
    /// counting from the least significant bit position.
    /// If `x` is zero, returns 256.
    function fls(uint256 x) internal pure returns (uint256 r) {
        /// @solidity memory-safe-assembly
        assembly {
            r := or(shl(8, iszero(x)), shl(7, lt(0xffffffffffffffffffffffffffffffff, x)))
            r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
            r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
            r := or(r, shl(4, lt(0xffff, shr(r, x))))
            r := or(r, shl(3, lt(0xff, shr(r, x))))
            // forgefmt: disable-next-item
            r := or(r, byte(and(0x1f, shr(shr(r, x), 0x8421084210842108cc6318c6db6d54be)),
                0x0706060506020504060203020504030106050205030304010505030400000000))
        }
    }

    /// @dev Count leading zeros.
    /// Returns the number of zeros preceding the most significant one bit.
    /// If `x` is zero, returns 256.
    function clz(uint256 x) internal pure returns (uint256 r) {
        /// @solidity memory-safe-assembly
        assembly {
            r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
            r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
            r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
            r := or(r, shl(4, lt(0xffff, shr(r, x))))
            r := or(r, shl(3, lt(0xff, shr(r, x))))
            // forgefmt: disable-next-item
            r := add(xor(r, byte(and(0x1f, shr(shr(r, x), 0x8421084210842108cc6318c6db6d54be)),
                0xf8f9f9faf9fdfafbf9fdfcfdfafbfcfef9fafdfafcfcfbfefafafcfbffffffff)), iszero(x))
        }
    }

    /// @dev Find first set.
    /// Returns the index of the least significant bit of `x`,
    /// counting from the least significant bit position.
    /// If `x` is zero, returns 256.
    /// Equivalent to `ctz` (count trailing zeros), which gives
    /// the number of zeros following the least significant one bit.
    function ffs(uint256 x) internal pure returns (uint256 r) {
        /// @solidity memory-safe-assembly
        assembly {
            // Isolate the least significant bit.
            x := and(x, add(not(x), 1))
            // For the upper 3 bits of the result, use a De Bruijn-like lookup.
            // Credit to adhusson: https://blog.adhusson.com/cheap-find-first-set-evm/
            // forgefmt: disable-next-item
            r := shl(5, shr(252, shl(shl(2, shr(250, mul(x,
                0xb6db6db6ddddddddd34d34d349249249210842108c6318c639ce739cffffffff))),
                0x8040405543005266443200005020610674053026020000107506200176117077)))
            // For the lower 5 bits of the result, use a De Bruijn lookup.
            // forgefmt: disable-next-item
            r := or(r, byte(and(div(0xd76453e0, shr(r, x)), 0x1f),
                0x001f0d1e100c1d070f090b19131c1706010e11080a1a141802121b1503160405))
        }
    }

    /// @dev Returns the number of set bits in `x`.
    function popCount(uint256 x) internal pure returns (uint256 c) {
        /// @solidity memory-safe-assembly
        assembly {
            let max := not(0)
            let isMax := eq(x, max)
            x := sub(x, and(shr(1, x), div(max, 3)))
            x := add(and(x, div(max, 5)), and(shr(2, x), div(max, 5)))
            x := and(add(x, shr(4, x)), div(max, 17))
            c := or(shl(8, isMax), shr(248, mul(x, div(max, 255))))
        }
    }

    /// @dev Returns the number of zero bytes in `x`.
    /// To get the number of non-zero bytes, simply do `32 - countZeroBytes(x)`.
    function countZeroBytes(uint256 x) internal pure returns (uint256 c) {
        /// @solidity memory-safe-assembly
        assembly {
            let m := 0x7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f
            c := byte(0, mul(shr(7, not(m)), shr(7, not(or(or(add(and(x, m), m), x), m)))))
        }
    }

    /// @dev Returns the number of zero bytes in `s`.
    /// To get the number of non-zero bytes, simply do `s.length - countZeroBytes(s)`.
    function countZeroBytes(bytes memory s) internal pure returns (uint256 c) {
        /// @solidity memory-safe-assembly
        assembly {
            function czb(x_) -> _c {
                let _m := 0x7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f
                _c := shr(7, not(or(or(add(and(x_, _m), _m), x_), _m)))
                _c := byte(0, mul(shr(7, not(_m)), _c))
            }
            let n := mload(s)
            let l := shl(5, shr(5, n))
            s := add(s, 0x20)
            for { let i } xor(i, l) { i := add(i, 0x20) } { c := add(czb(mload(add(s, i))), c) }
            if lt(l, n) { c := add(czb(or(shr(shl(3, sub(n, l)), not(0)), mload(add(s, l)))), c) }
        }
    }

    /// @dev Returns the number of zero bytes in `s`.
    /// To get the number of non-zero bytes, simply do `s.length - countZeroBytes(s)`.
    function countZeroBytesCalldata(bytes calldata s) internal pure returns (uint256 c) {
        /// @solidity memory-safe-assembly
        assembly {
            function czb(x_) -> _c {
                let _m := 0x7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f
                _c := shr(7, not(or(or(add(and(x_, _m), _m), x_), _m)))
                _c := byte(0, mul(shr(7, not(_m)), _c))
            }
            let l := shl(5, shr(5, s.length))
            for { let i } xor(i, l) { i := add(i, 0x20) } {
                c := add(czb(calldataload(add(s.offset, i))), c)
            }
            if lt(l, s.length) {
                let m := shr(shl(3, sub(s.length, l)), not(0))
                c := add(czb(or(m, calldataload(add(s.offset, l)))), c)
            }
        }
    }

    /// @dev Returns whether `x` is a power of 2.
    function isPo2(uint256 x) internal pure returns (bool result) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to `x && !(x & (x - 1))`.
            result := iszero(add(and(x, sub(x, 1)), iszero(x)))
        }
    }

    /// @dev Returns `x` reversed at the bit level.
    function reverseBits(uint256 x) internal pure returns (uint256 r) {
        uint256 m0 = 0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f;
        uint256 m1 = m0 ^ (m0 << 2);
        uint256 m2 = m1 ^ (m1 << 1);
        r = reverseBytes(x);
        r = (m2 & (r >> 1)) | ((m2 & r) << 1);
        r = (m1 & (r >> 2)) | ((m1 & r) << 2);
        r = (m0 & (r >> 4)) | ((m0 & r) << 4);
    }

    /// @dev Returns `x` reversed at the byte level.
    function reverseBytes(uint256 x) internal pure returns (uint256 r) {
        unchecked {
            // Computing masks on-the-fly reduces bytecode size by about 200 bytes.
            uint256 m0 = 0x100000000000000000000000000000001 * (~toUint(x == uint256(0)) >> 192);
            uint256 m1 = m0 ^ (m0 << 32);
            uint256 m2 = m1 ^ (m1 << 16);
            uint256 m3 = m2 ^ (m2 << 8);
            r = (m3 & (x >> 8)) | ((m3 & x) << 8);
            r = (m2 & (r >> 16)) | ((m2 & r) << 16);
            r = (m1 & (r >> 32)) | ((m1 & r) << 32);
            r = (m0 & (r >> 64)) | ((m0 & r) << 64);
            r = (r >> 128) | (r << 128);
        }
    }

    /// @dev Returns the common prefix of `x` and `y` at the bit level.
    function commonBitPrefix(uint256 x, uint256 y) internal pure returns (uint256) {
        unchecked {
            uint256 s = 256 - clz(x ^ y);
            return (x >> s) << s;
        }
    }

    /// @dev Returns the common prefix of `x` and `y` at the nibble level.
    function commonNibblePrefix(uint256 x, uint256 y) internal pure returns (uint256) {
        unchecked {
            uint256 s = (64 - (clz(x ^ y) >> 2)) << 2;
            return (x >> s) << s;
        }
    }

    /// @dev Returns the common prefix of `x` and `y` at the byte level.
    function commonBytePrefix(uint256 x, uint256 y) internal pure returns (uint256) {
        unchecked {
            uint256 s = (32 - (clz(x ^ y) >> 3)) << 3;
            return (x >> s) << s;
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                     BOOLEAN OPERATIONS                     */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    // A Solidity bool on the stack or memory is represented as a 256-bit word.
    // Non-zero values are true, zero is false.
    // A clean bool is either 0 (false) or 1 (true) under the hood.
    // Usually, if not always, the bool result of a regular Solidity expression,
    // or the argument of a public/external function will be a clean bool.
    // You can usually use the raw variants for more performance.
    // If uncertain, test (best with exact compiler settings).
    // Or use the non-raw variants (compiler can sometimes optimize out the double `iszero`s).

    /// @dev Returns `x & y`. Inputs must be clean.
    function rawAnd(bool x, bool y) internal pure returns (bool z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := and(x, y)
        }
    }

    /// @dev Returns `x & y`.
    function and(bool x, bool y) internal pure returns (bool z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := and(iszero(iszero(x)), iszero(iszero(y)))
        }
    }

    /// @dev Returns `w & x & y`.
    function and(bool w, bool x, bool y) internal pure returns (bool z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := iszero(or(iszero(w), or(iszero(x), iszero(y))))
        }
    }

    /// @dev Returns `v & w & x & y`.
    function and(bool v, bool w, bool x, bool y) internal pure returns (bool z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := iszero(or(or(iszero(v), iszero(w)), or(iszero(x), iszero(y))))
        }
    }

    /// @dev Returns `x | y`. Inputs must be clean.
    function rawOr(bool x, bool y) internal pure returns (bool z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := or(x, y)
        }
    }

    /// @dev Returns `x | y`.
    function or(bool x, bool y) internal pure returns (bool z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := iszero(iszero(or(x, y)))
        }
    }

    /// @dev Returns `w | x | y`.
    function or(bool w, bool x, bool y) internal pure returns (bool z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := iszero(iszero(or(w, or(x, y))))
        }
    }

    /// @dev Returns `v | w | x | y`.
    function or(bool v, bool w, bool x, bool y) internal pure returns (bool z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := iszero(iszero(or(v, or(w, or(x, y)))))
        }
    }

    /// @dev Returns 1 if `b` is true, else 0. Input must be clean.
    function rawToUint(bool b) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := b
        }
    }

    /// @dev Returns 1 if `b` is true, else 0.
    function toUint(bool b) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := iszero(iszero(b))
        }
    }
}

// SPDX-License-Identifier: ekubo-license-v1.eth
pragma solidity ^0.8.0;

/// @title IExposedStorage
/// @notice Interface for exposing contract storage via view methods
/// @dev This interface provides a way to access specific pieces of state in the inheriting contract.
///      It serves as a workaround in the absence of EIP-2330 (https://eips.ethereum.org/EIPS/eip-2330)
///      which would provide native support for exposing contract storage.
interface IExposedStorage {
    /// @notice Loads storage slots from the contract's persistent storage
    /// @dev Reads each 32-byte slot specified in the calldata (after the function selector) from storage
    ///      and returns all the loaded values concatenated together.
    function sload() external view;

    /// @notice Loads storage slots from the contract's transient storage
    /// @dev Reads each 32-byte slot specified in the calldata (after the function selector) from transient storage
    ///      and returns all the loaded values concatenated together. Transient storage is cleared at the end
    ///      of each transaction.
    function tload() external view;
}

File 32 of 33 : bitmap.sol
// SPDX-License-Identifier: ekubo-license-v1.eth
pragma solidity =0.8.33;

/**
 * @title Bitmap (256-bit)
 * @notice Lightweight helpers for treating a `uint256` as a 256-bit bitmap.
 * @dev
 * - Bit indices are in the range [0, 255].
 * - All operations are O(1) and implemented with memory-safe assembly.
 * - For search helpers `leSetBit` and `geSetBit`, the return value is
 *   one-based: it returns `index + 1` of the matching set bit, or `0` if none.
 *   This convention avoids the need for sentinels outside the 0..255 range.
 */
type Bitmap is uint256;

using {toggle, isSet, leSetBit, geSetBit} for Bitmap global;

/**
 * @notice Toggle (flip) the bit at `index` in `bitmap`.
 * @param bitmap The current bitmap value.
 * @param index  Bit position to toggle, in [0, 255].
 * @return result A new bitmap with the bit at `index` flipped.
 */
function toggle(Bitmap bitmap, uint8 index) pure returns (Bitmap result) {
    assembly ("memory-safe") {
        result := xor(bitmap, shl(index, 1))
    }
}

/**
 * @notice Check whether the bit at `index` is set.
 * @param bitmap The bitmap to read.
 * @param index  Bit position to test, in [0, 255].
 * @return yes True if the bit is 1, false otherwise.
 */
function isSet(Bitmap bitmap, uint8 index) pure returns (bool yes) {
    assembly ("memory-safe") {
        yes := and(shr(index, bitmap), 1)
    }
}

/**
 * @notice Find the most significant set bit at or below `index`.
 * @dev Returns one-based position: `pos = bitIndex + 1`, or `0` if none.
 *      Example: if bit 9 is set and `index >= 9`, returns `10`.
 *      For `index == 255`, the mask spans all bits (wraparound yields `2^256-1`).
 * @param bitmap The bitmap to search.
 * @param index  Upper bound (inclusive) for the search, in [0, 255].
 * @return v One-based position of MSB found (bitIndex + 1), or 0 if none.
 */
function leSetBit(Bitmap bitmap, uint8 index) pure returns (uint256 v) {
    unchecked {
        assembly ("memory-safe") {
            let masked := and(bitmap, sub(shl(add(index, 1), 1), 1))
            v := sub(256, clz(masked))
        }
    }
}

/**
 * @notice Find the least significant set bit at or above `index`.
 * @dev Returns one-based position: `pos = bitIndex + 1`, or `0` if none.
 *      Example: if bit 9 is set and `index <= 9`, returns `10`.
 * @param bitmap The bitmap to search.
 * @param index  Lower bound (inclusive) for the search, in [0, 255].
 * @return v One-based position of LSB found (bitIndex + 1), or 0 if none.
 */
function geSetBit(Bitmap bitmap, uint8 index) pure returns (uint256 v) {
    assembly ("memory-safe") {
        let masked := and(bitmap, not(sub(shl(index, 1), 1)))
        v := sub(256, clz(and(masked, sub(0, masked))))
    }
}

// SPDX-License-Identifier: ekubo-license-v1.eth
pragma solidity ^0.8.0;

import {Locker} from "../types/locker.sol";

interface ILocker {
    function locked_6416899205(uint256 id) external;
}

interface IForwardee {
    function forwarded_2374103877(Locker original) external;
}

/// @title IFlashAccountant
/// @notice Interface for flash loan accounting functionality using transient storage
/// @dev This interface manages debt tracking for flash loans, allowing users to borrow tokens temporarily
///      and ensuring all debts are settled before the transaction completes. Uses transient storage
///      for gas-efficient temporary state management within a single transaction.
interface IFlashAccountant {
    /// @notice Thrown when an operation is made that affects debts without an active lock
    error NotLocked();
    /// @notice Thrown when a method is called by an address other than the current locker
    error LockerOnly();
    /// @notice Thrown when the lock callback returns without clearing all the debts either via withdrawing from or paying to the accountant
    error DebtsNotZeroed(uint256 id);
    /// @notice Thrown if the contract receives a payment that exceeds type(uint128).max
    error PaymentOverflow();
    /// @notice Thrown if the contract receives a call to updateDebt that has a data length != 20
    error UpdateDebtMessageLength();

    /// @notice Creates a lock context and calls back to the caller's locked function
    /// @dev The entrypoint for all operations on the core contract. Any data passed after the
    ///      function signature is passed through back to the caller after the locked function
    ///      signature and data, with no additional encoding. Any data returned from ILocker#locked
    ///      is also returned from this function exactly as is. Reverts are bubbled up.
    ///      Ensures all debts are zeroed before completing the lock.
    function lock() external;

    /// @notice Forwards the lock context to another actor, allowing them to act on the original locker's debt
    /// @dev Temporarily changes the locker to the forwarded address for the duration of the forwarded call.
    ///      Any additional calldata is passed through to the forwardee with no additional encoding.
    ///      Any data returned from IForwardee#forwarded is returned exactly as is. Reverts are bubbled up.
    /// @param to The address to forward the lock context to
    function forward(address to) external;

    /// @notice Initiates a payment operation by recording current token balances
    /// @dev To make a payment to core, you must first call startPayments with all the tokens you'd like to send.
    ///      All the tokens that will be paid must be ABI-encoded immediately after the 4 byte function selector.
    ///      This function stores the current balance + 1 for each token to distinguish between zero balance
    ///      and uninitialized state. Returns the current balances of all specified tokens as ABI-encoded
    ///      raw bytes via assembly (no explicit Solidity return type).
    function startPayments() external;

    /// @notice Completes a payment operation by calculating and crediting token payments
    /// @dev After tokens have been transferred, call completePayments to be credited for the tokens
    ///      that have been paid to core. The credit goes to the current locker. Compares current
    ///      balances with those recorded in startPayments to determine payment amounts.
    ///      The computed payments are applied to the current locker's debt.
    ///      Returns packed uint128 payment amounts (16 bytes each) in the same order as the tokens.
    function completePayments() external;

    /// @notice Withdraws tokens from the accountant to recipients using packed calldata
    /// @dev The contract must be locked, as it tracks withdrawn amounts against the current locker's debt.
    ///      Calldata format: each withdrawal is 56 bytes: token (20) + recipient (20) + amount (16)
    ///      For native tokens, uses the NATIVE_TOKEN_ADDRESS constant and transfers ETH directly.
    function withdraw() external;

    /// @notice Updates debt for the current locker and for the token at the calling address
    /// @dev This is for deeply-integrated tokens that allow flash operations via the accountant.
    ///      The calling address is treated as the token address.
    /// @dev The debt change argument is an int128 encoded immediately after the selector.
    /// @dev The calldata length must be exactly 20 bytes in order to avoid this being called unintentionally.
    function updateDebt() external;

    /// @notice Receives ETH payments and credits them against the current locker's native token debt
    /// @dev This contract can receive ETH as a payment. The received amount is credited as a negative
    ///      debt change for the native token. Note: because we use msg.value here, this contract can
    ///      never be multicallable, i.e. it should never expose the ability to delegatecall itself
    ///      more than once in a single call.
    receive() external payable;
}

Settings
{
  "remappings": [
    "forge-std/=lib/forge-std/src/",
    "solady/=lib/solady/src/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 9999999
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "osaka",
  "viaIR": true
}

Contract Security Audit

Contract ABI

API
[{"inputs":[],"name":"BoundsOrder","type":"error"},{"inputs":[],"name":"BoundsTickSpacing","type":"error"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"DebtsNotZeroed","type":"error"},{"inputs":[],"name":"ExtensionAlreadyRegistered","type":"error"},{"inputs":[],"name":"ExtensionNotRegistered","type":"error"},{"inputs":[],"name":"FailedRegisterInvalidCallPoints","type":"error"},{"inputs":[],"name":"InvalidCenterTick","type":"error"},{"inputs":[],"name":"InvalidSqrtRatioLimit","type":"error"},{"inputs":[],"name":"InvalidStableswapAmplification","type":"error"},{"inputs":[{"internalType":"int32","name":"tick","type":"int32"}],"name":"InvalidTick","type":"error"},{"inputs":[],"name":"InvalidTickSpacing","type":"error"},{"inputs":[],"name":"LockerOnly","type":"error"},{"inputs":[{"internalType":"int32","name":"tick","type":"int32"},{"internalType":"uint128","name":"liquidityNet","type":"uint128"},{"internalType":"uint128","name":"maxLiquidityPerTick","type":"uint128"}],"name":"MaxLiquidityPerTickExceeded","type":"error"},{"inputs":[],"name":"MinMaxBounds","type":"error"},{"inputs":[],"name":"NotLocked","type":"error"},{"inputs":[],"name":"PaymentOverflow","type":"error"},{"inputs":[],"name":"PoolAlreadyInitialized","type":"error"},{"inputs":[],"name":"PoolNotInitialized","type":"error"},{"inputs":[],"name":"SavedBalanceOverflow","type":"error"},{"inputs":[],"name":"SavedBalanceTokensNotSorted","type":"error"},{"inputs":[],"name":"SqrtRatioLimitOutOfRange","type":"error"},{"inputs":[],"name":"SqrtRatioLimitWrongDirection","type":"error"},{"inputs":[],"name":"StableswapMustBeFullRange","type":"error"},{"inputs":[],"name":"TokensMustBeSorted","type":"error"},{"inputs":[],"name":"UpdateDebtMessageLength","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"extension","type":"address"}],"name":"ExtensionRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"PoolId","name":"poolId","type":"bytes32"},{"indexed":false,"internalType":"uint128","name":"amount0","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"amount1","type":"uint128"}],"name":"FeesAccumulated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"PoolId","name":"poolId","type":"bytes32"},{"components":[{"internalType":"address","name":"token0","type":"address"},{"internalType":"address","name":"token1","type":"address"},{"internalType":"PoolConfig","name":"config","type":"bytes32"}],"indexed":false,"internalType":"struct PoolKey","name":"poolKey","type":"tuple"},{"indexed":false,"internalType":"int32","name":"tick","type":"int32"},{"indexed":false,"internalType":"SqrtRatio","name":"sqrtRatio","type":"uint96"}],"name":"PoolInitialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"locker","type":"address"},{"indexed":false,"internalType":"PoolId","name":"poolId","type":"bytes32"},{"indexed":false,"internalType":"PositionId","name":"positionId","type":"bytes32"},{"indexed":false,"internalType":"uint128","name":"amount0","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"amount1","type":"uint128"}],"name":"PositionFeesCollected","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"locker","type":"address"},{"indexed":false,"internalType":"PoolId","name":"poolId","type":"bytes32"},{"indexed":false,"internalType":"PositionId","name":"positionId","type":"bytes32"},{"indexed":false,"internalType":"int128","name":"liquidityDelta","type":"int128"},{"indexed":false,"internalType":"PoolBalanceUpdate","name":"balanceUpdate","type":"bytes32"},{"indexed":false,"internalType":"PoolState","name":"stateAfter","type":"bytes32"}],"name":"PositionUpdated","type":"event"},{"inputs":[{"components":[{"internalType":"address","name":"token0","type":"address"},{"internalType":"address","name":"token1","type":"address"},{"internalType":"PoolConfig","name":"config","type":"bytes32"}],"internalType":"struct PoolKey","name":"poolKey","type":"tuple"},{"internalType":"uint128","name":"_amount0","type":"uint128"},{"internalType":"uint128","name":"_amount1","type":"uint128"}],"name":"accumulateAsFees","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"token0","type":"address"},{"internalType":"address","name":"token1","type":"address"},{"internalType":"PoolConfig","name":"config","type":"bytes32"}],"internalType":"struct PoolKey","name":"poolKey","type":"tuple"},{"internalType":"PositionId","name":"positionId","type":"bytes32"}],"name":"collectFees","outputs":[{"internalType":"uint128","name":"amount0","type":"uint128"},{"internalType":"uint128","name":"amount1","type":"uint128"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"completePayments","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"forward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"bytes32"},{"internalType":"int32","name":"tickLower","type":"int32"},{"internalType":"int32","name":"tickUpper","type":"int32"}],"name":"getPoolFeesPerLiquidityInside","outputs":[{"components":[{"internalType":"uint256","name":"value0","type":"uint256"},{"internalType":"uint256","name":"value1","type":"uint256"}],"internalType":"struct FeesPerLiquidity","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"token0","type":"address"},{"internalType":"address","name":"token1","type":"address"},{"internalType":"PoolConfig","name":"config","type":"bytes32"}],"internalType":"struct PoolKey","name":"poolKey","type":"tuple"},{"internalType":"int32","name":"tick","type":"int32"}],"name":"initializePool","outputs":[{"internalType":"SqrtRatio","name":"sqrtRatio","type":"uint96"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"lock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"bytes32"},{"internalType":"int32","name":"fromTick","type":"int32"},{"internalType":"uint32","name":"tickSpacing","type":"uint32"},{"internalType":"uint256","name":"skipAhead","type":"uint256"}],"name":"nextInitializedTick","outputs":[{"internalType":"int32","name":"tick","type":"int32"},{"internalType":"bool","name":"isInitialized","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"bytes32"},{"internalType":"int32","name":"fromTick","type":"int32"},{"internalType":"uint32","name":"tickSpacing","type":"uint32"},{"internalType":"uint256","name":"skipAhead","type":"uint256"}],"name":"prevInitializedTick","outputs":[{"internalType":"int32","name":"tick","type":"int32"},{"internalType":"bool","name":"isInitialized","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"bool","name":"beforeInitializePool","type":"bool"},{"internalType":"bool","name":"afterInitializePool","type":"bool"},{"internalType":"bool","name":"beforeSwap","type":"bool"},{"internalType":"bool","name":"afterSwap","type":"bool"},{"internalType":"bool","name":"beforeUpdatePosition","type":"bool"},{"internalType":"bool","name":"afterUpdatePosition","type":"bool"},{"internalType":"bool","name":"beforeCollectFees","type":"bool"},{"internalType":"bool","name":"afterCollectFees","type":"bool"}],"internalType":"struct CallPoints","name":"expectedCallPoints","type":"tuple"}],"name":"registerExtension","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"PoolId","name":"poolId","type":"bytes32"},{"internalType":"PositionId","name":"positionId","type":"bytes32"},{"internalType":"bytes16","name":"_extraData","type":"bytes16"}],"name":"setExtraData","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"sload","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[],"name":"startPayments","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"swap_6269342730","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"tload","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[],"name":"updateDebt","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"token0","type":"address"},{"internalType":"address","name":"token1","type":"address"},{"internalType":"PoolConfig","name":"config","type":"bytes32"}],"internalType":"struct PoolKey","name":"poolKey","type":"tuple"},{"internalType":"PositionId","name":"positionId","type":"bytes32"},{"internalType":"int128","name":"liquidityDelta","type":"int128"}],"name":"updatePosition","outputs":[{"internalType":"PoolBalanceUpdate","name":"balanceUpdate","type":"bytes32"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token0","type":"address"},{"internalType":"address","name":"token1","type":"address"},{"internalType":"bytes32","name":"","type":"bytes32"},{"internalType":"int256","name":"delta0","type":"int256"},{"internalType":"int256","name":"delta1","type":"int256"}],"name":"updateSavedBalances","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]

6080806040523460155761496d908161001a8239f35b5f80fdfe60806040526004361015610053575b3615610018575f80fd5b6100517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff610044614279565b60a01c01345f03906142cc565b005b5f3560e01c80156125c75780626c95a9146125435780630e7f263914612508578063101e8952146123f857806312e103f11461221c57806317c5da6a1461211d578063187437a114611fec578063380eb4e014611f805780633ccfd60b14611df85780635ebb090914611d3f57806366e064a814611ced57806379b45a071461198b578063c0530244146115ac578063cb48dadf146108af578063de6f935f14610553578063e96404f814610376578063ed8328301461030a578063f83d08ba146101f95763f9b6a7960361000e57346101f5577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36015f81126101f557306014526f70a082310000000000000000000000005f5260405160045b3681106101785750f35b8073ffffffffffffffffffffffffffffffffffffffff60209235167ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc82850101838160246010855afa9051601f3d1182160201907f6747da56dbd05b26a7ecd2a0106781585141cf07098ad54c0e049e4e86dccb8c015d0161016e565b5f80fd5b346101f5577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36015f81126101f5577f07cc7f5195d862f505d6b095c82f92e00cfc1766f5bca4383c28dc5fca1555fd5c8060a01c90336001830160a01b177f07cc7f5195d862f505d6b095c82f92e00cfc1766f5bca4383c28dc5fca1555fd5d604051925f8452826004850152600460248501375f80602036018582335af115610301577f07cc7f5195d862f505d6b095c82f92e00cfc1766f5bca4383c28dc5fca1555fd5d807f7772acfd7e0f66ebb20a058830296c3dc1301b111d23348e1c961d324223190d015c6102f157503d5f823e3d90f35b639731ba375f526020526024601cfd5b823d5f823e3d90fd5b346101f5577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36015f81126101f55760045b36811061034757505ff35b80355c7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc82015260200161033c565b60a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f5576103a93661347f565b6064356fffffffffffffffffffffffffffffffff8116918282036101f557608435926fffffffffffffffffffffffffffffffff8416908185036101f5576103ee6135b7565b604084015160601c73ffffffffffffffffffffffffffffffffffffffff8216036101f5577ff7e050d866774820d81a86ca676f3afe7bc72603ee893f82e99c08fbde39af6c9560609561049a928787209682158015809161054a575b6104ad575b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff73ffffffffffffffffffffffffffffffffffffffff602081845116930151169260a01c016140ee565b60405192835260208301526040820152a1005b6fffffffffffffffffffffffffffffffff89541690816104ce575b5061044f565b7fb09b03866d96933565a9435bfb511c8ac5b2be454285ca331201452704799f728a0190610539575b5084610504575b806104c8565b7fb09b03866d96933565a9435bfb511c8ac5b2be454285ca331201452704799f738901908154908660801b040190558a6104fe565b8054828660801b040190558b6104f7565b5084151561044a565b346101f5576101007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f55761058b61345e565b60043580151581036101f557815260243580151581036101f557602082015260443580151581036101f557604082015260643580151581036101f557606082015260843580151581036101f557608082015260a43580151581036101f55760a082015260c43580151581036101f55760c082015260e43580151581036101f55760e082015261061861460f565b5061062161460f565b5060013360981c16159061063361345e565b82158082523360981c6080818116151560208086019190915260408084161515908601528216151560608501526010821615159084015260088116151560a084015260048116151560c0840152600216151560e09092019190915281511515149081610896575b8161087d575b81610864575b8161084b575b81610832575b81610819575b81610801575b501590811561075e575b5061073657335f525f60205260405f20805461070e57600190557fec1256266e470abb868620c851f6bde2a3ff602549dcad318ab9ccfcb2977f146020604051338152a1005b7f8b20f687000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f7865ebfd000000000000000000000000000000000000000000000000000000005f5260045ffd5b801591506107f2575b80156107e3575b80156107d4575b80156107c5575b80156107b6575b80156107a7575b8015610798575b15816106c8565b5060023360981c161515610791565b5060043360981c16151561078a565b5060083360981c161515610783565b5060103360981c16151561077c565b5060203360981c161515610775565b5060403360981c16151561076e565b5060803360981c161515610767565b60e091500151151560023360981c16151514826106be565b905060c0810151151560043360981c16151514906106b8565b905060a0810151151560083360981c16151514906106b2565b90506080810151151560103360981c16151514906106ac565b90506060810151151560203360981c16151514906106a6565b90506040810151151560403360981c16151514906106a0565b90506020810151151560803360981c161515149061069a565b60a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f5576108e23661347f565b608435600f0b608435036101f55760408101515f9063800000008116156114bb5760643560201c60030b60643560030b9182821215611493577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffab6326d82128015611486575b61145e57637fffffff61095d911660030b809261444c565b60030b1591821592611448575b5050611420575b6109796135b7565b60408301517fffffffffffffffffffffffffffffffffffffffff00000000000000000000000081168260601b14158160fc1c166113c2575b506060832091825490811561139a57608435600f0b1561135657506109dd60643560201c60030b613856565b6109fc6109ee60643560030b613856565b8360a01c926084358461448b565b9091610a4060643573ffffffffffffffffffffffffffffffffffffffff871688906060929160405192835260208301526040820152205f52600160205260405f2090565b90610a51608435835460801c6145bc565b610a5961343e565b5f8152602081015f8152819360408c015163800000001615155f1461127c575050506fffffffffffffffffffffffffffffffff811615611255575b60408901517f435a5eb89a296820174331cf5a3902d9fca683928d56726d8e7acd6efb28c56860643560201c60030b8a0101908154610ad86084358260801c6145bc565b916f7fffffffffffffffffffffffffffffff600f83810b60843590910b019081137fffffffffffffffffffffffffffffffff8000000000000000000000000000000090911217611131576fffffffffffffffffffffffffffffffff637fffffff8216630549cd930460011b6001018104166fffffffffffffffffffffffffffffffff84169080821161121a57506fffffffffffffffffffffffffffffffff92918d91158360801c150361115e575b5050608435600f0b90600f0b01169060801b17905560408901517f435a5eb89a296820174331cf5a3902d9fca683928d56726d8e7acd6efb28c56860643560030b8a010154610bda6084358260801c6145bc565b917fffffffffffffffffffffffffffffffff80000000000000000000000000000000608435600f90810b9084900b039081126f7fffffffffffffffffffffffffffffff90911317611131576fffffffffffffffffffffffffffffffff637fffffff8216630549cd930460011b600101810416806fffffffffffffffffffffffffffffffff8516116110e75750906fffffffffffffffffffffffffffffffff91828416158260801c150361102f575b50608435600f0b90600f0b03169060801b177f435a5eb89a296820174331cf5a3902d9fca683928d56726d8e7acd6efb28c56860643560030b8a0101556fffffffffffffffffffffffffffffffff8116611008575b8560801c60030b60643560201c60030b81121580610ffa575b610f63575b50927f704b3ab4a76158ad4d66625a2a43be81edbffd24630e8fde5174e97035370a0794926fffffffffffffffffffffffffffffffff9260c0955b808516610ed0575050600281845f9354168155826020610d5461343e565b828152015282600182015501555b610dce73ffffffffffffffffffffffffffffffffffffffff8a511673ffffffffffffffffffffffffffffffffffffffff60208c0151169083600f0b9186600f0b917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8c60a01c016140ee565b169060801b17946040519073ffffffffffffffffffffffffffffffffffffffff8616825260208201526064356040820152608435600f0b60608201528560808201528360a0820152a15b6040840151907fffffffffffffffffffffffffffffffffffffffff00000000000000000000000082168360601b14158260fb1c16610e5c575b602084604051908152f35b5f92838093610104936060604051998a957f25fa4e690000000000000000000000000000000000000000000000000000000087526004870152602486015e606435608485015260843560a48501528760c485015260e484015260601c5af115610ec757818080610e51565b503d5f823e3d90fd5b918192610ee7600293610ee28461352f565b614339565b9490918354887fffffffffffffffffffffffffffffffff000000000000000000000000000000008360801b169116178455610f2061343e565b9581602088019460801b04875260801b0482526020610f3d61343e565b925f8452818401965f885251835103809452519101510380945260018201550155610d62565b926fffffffffffffffffffffffffffffffff92610fea60c09693987f704b3ab4a76158ad4d66625a2a43be81edbffd24630e8fde5174e97035370a079896610faf6084358884166145bc565b9160a01c916fffffffffffffffffffffffffffffffff73ffffffff0000000000000000000000000000000091169160801b16179060a01b1790565b97888b5592955092509294610cfb565b5060643560030b8112610cf6565b905061102960643560030b60643560201c60030b8760801c60030b8a614147565b90610cdd565b61107b637fffffff7f3def450d0010a2fef515ce5eba4b363b5a0f42fdd4c53e1c737975db05a2e3a5921660643560030b630554777f915f81830712910503019060ff8260081c921690565b908d019091018054600190921b909118905582151560643560030b8c017f5695060fdb9cfea656f872ae4887221aff7dbfefc45eaf753e4e70cdfb5cd19c81018290557fd0bf0a0be44c3c54cf61da346063acd98e9363a6de122b16472a1947527c5c9e01558c610c88565b6fffffffffffffffffffffffffffffffff847fe51274fb000000000000000000000000000000000000000000000000000000005f5260643560030b6004521660245260445260645ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b6111ad637fffffff7f3def450d0010a2fef515ce5eba4b363b5a0f42fdd4c53e1c737975db05a2e3a5921660643560201c60030b630554777f915f81830712910503019060ff8260081c921690565b9201018054600190921b909118905582151560643560201c60030b8d017f5695060fdb9cfea656f872ae4887221aff7dbfefc45eaf753e4e70cdfb5cd19c81018290557fd0bf0a0be44c3c54cf61da346063acd98e9363a6de122b16472a1947527c5c9e01558b8e610b86565b907fe51274fb000000000000000000000000000000000000000000000000000000005f5260643560201c60030b60045260245260445260645ffd5b905061127660643560030b60643560201c60030b8760801c60030b8a614147565b90610a94565b926fffffffffffffffffffffffffffffffff94916113017f704b3ab4a76158ad4d66625a2a43be81edbffd24630e8fde5174e97035370a079997949a60c099966112ca6084358a84166145bc565b60a09190911b73ffffffff000000000000000000000000000000009092166fffffffffffffffffffffffffffffffff909116171790565b998a8d557fb09b03866d96933565a9435bfb511c8ac5b2be454285ca331201452704799f728d015490527fb09b03866d96933565a9435bfb511c8ac5b2be454285ca331201452704799f738c01549052610d36565b92503415610e185761139561136a3461358b565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8460a01c016142cc565b610e18565b7f486aa307000000000000000000000000000000000000000000000000000000005f5260045ffd5b5f8060c481936040519485917e35c72300000000000000000000000000000000000000000000000000000000835287600484015260608a602485015e606435608484015260843560a484015260601c5af16109b1575b3d5f823e3d90fd5b7f89fd41b1000000000000000000000000000000000000000000000000000000005f5260045ffd5b611452925061444c565b60030b1515838061096a565b7fabfa4a31000000000000000000000000000000000000000000000000000000005f5260045ffd5b50630549cd938313610945565b7f68651c5c000000000000000000000000000000000000000000000000000000005f5260045ffd5b611554630549cd93607f62ffffff841660020b60041b9360181c161c808303807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffab6326d03817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffab6326d13020192017ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffab6326d8101630549cd93821302900390565b9060030b60643560201c60030b1490811591611599575b5015610971577f72b1019d000000000000000000000000000000000000000000000000000000005f5260045ffd5b905060030b60643560030b14158361156b565b346101f55760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f5576115e43661347f565b6064358060030b8082036101f55773ffffffffffffffffffffffffffffffffffffffff835116602084019073ffffffffffffffffffffffffffffffffffffffff82511611156119635760408401805190939063800000008116156118b857637fffffff16620aa8ed81119081156118af575b50611887575b83518060601c93846117f2575b606087209586546117ca5773ffffffffffffffffffffffffffffffffffffffff61169285613856565b9573ffffffff000000000000000000000000000000008660801b168760a01b17895560017fb09b03866d96933565a9435bfb511c8ac5b2be454285ca331201452704799f728a015560017fb09b03866d96933565a9435bfb511c8ac5b2be454285ca331201452704799f738a0155604051988952818a511660208a01525116604088015251606087015260808601527f5e4688b340694b7c7fd30047fd082117dc46e32acfbf81a44bb1fac0ae65154d60c06bffffffffffffffffffffffff8516968760a0820152a160ff1c338414151661177257602084604051908152f35b6060945f8094819460c494604051998a957f948374ff000000000000000000000000000000000000000000000000000000008752336004880152602487015e608485015260a48401525af115610ec757818080610e51565b7f7983c051000000000000000000000000000000000000000000000000000000005f5260045ffd5b845f525f60205260405f20541561185f57843314158260f81c1615611669576040517f1fbbb462000000000000000000000000000000000000000000000000000000008152336004820152606088602483015e8360848201525f8060a483828a5af1156114185750611669565b7fcd72c3bc000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f270815a0000000000000000000000000000000000000000000000000000000005f5260045ffd5b90501586611656565b601a607f8260181c161161193b5762ffffff1660020b60041b60030b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffab6326d811290811561192d575b501561165c577f10de0c76000000000000000000000000000000000000000000000000000000005f5260045ffd5b630549cd93915013866118ff565b7f218b53af000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f88a2efcf000000000000000000000000000000000000000000000000000000005f5260045ffd5b346101f55760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f5576119c33661347f565b6064356119ce6135b7565b90604083019182518160601b907fffffffffffffffffffffffffffffffffffffffff00000000000000000000000081168214158160fa1c16611c99575b50606085209273ffffffffffffffffffffffffffffffffffffffff831694611a53828787906060929160405192835260208301526040820152205f52600160205260405f2090565b91611a5c61343e565b5f81525f6020820152918051638000000016155f14611c73577fb09b03866d96933565a9435bfb511c8ac5b2be454285ca331201452704799f7287015483527fb09b03866d96933565a9435bfb511c8ac5b2be454285ca331201452704799f7387015460208401525b82611acf8561352f565b90611ad991614339565b94909380516001830155602001519060020155885173ffffffffffffffffffffffffffffffffffffffff169760208a015173ffffffffffffffffffffffffffffffffffffffff16976fffffffffffffffffffffffffffffffff851699611b3e8b61358b565b996fffffffffffffffffffffffffffffffff88169a611b5c8c61358b565b918b60a01c7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0193611b8d94614383565b604051918252602082015282604082015288606082015287608082015260a07fd76ec32fbc3f07c70828b4f94343ee73279d0e8d4d2f28b018a4e67f3749775391a151937fffffffffffffffffffffffffffffffffffffffff000000000000000000000000851614158460f91c16611c10575b6040878782519182526020820152f35b5f94919360e4938695869360606040519c8d977f751fd5df0000000000000000000000000000000000000000000000000000000089526004890152602488015e608486015260a485015260c484015260601c5af115610301578280808080611c00565b9150611c93865460801c60030b8260030b908360201c60030b9089614147565b91611ac5565b5f8060a481936040519485917fdf65d8d100000000000000000000000000000000000000000000000000000000835288600484015260608c602485015e89608484015260601c5af1611a0b573d5f823e3d90fd5b346101f557611d287f3def450d0010a2fef515ce5eba4b363b5a0f42fdd4c53e1c737975db05a2e3a5611d1f3661337b565b9390920161376c565b6040805160039390930b8352901515602083015290f35b346101f55760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f5576044357fffffffffffffffffffffffffffffffff00000000000000000000000000000000811681036101f557611dc860243533600435906060929160405192835260208301526040820152205f52600160205260405f2090565b907fffffffffffffffffffffffffffffffff000000000000000000000000000000008254169060801c1790555f80f35b346101f5575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f557611e2e6135b7565b5f9060a01c817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820160045b368110611e93575050611e6957005b7f7772acfd7e0f66ebb20a058830296c3dc1301b111d23348e1c961d324223190c0190815c01905d005b809192503560601c601482013560601c602883013560801c9081611ec0575b505050603801908391611e5a565b828560a01b017f753dfe4b4dfb3ff6c11bbf6a97f3c094e91c003ce904a55cc5662fbad220f5990196875c9083820191821590150301975d8215611f5c576014526034526fa9059cbb0000000000000000000000005f5260205f6044601082855af1908160015f51141615611f3e575b50506038905b908580611eb2565b3b153d171015611f4f578480611f30565b6390b8ec185f526004601cfd5b5f809350809281925af115611f7357603890611f36565b63b12d13eb5f526004601cfd5b346101f5577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36015f81126101f55760045b368110611fbd57505ff35b8035547ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc820152602001611fb2565b60a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f55761201e6133ce565b60243573ffffffffffffffffffffffffffffffffffffffff8116908181036101f557606435906084359273ffffffffffffffffffffffffffffffffffffffff851610156120f557610051937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6120926135b7565b608060405173ffffffffffffffffffffffffffffffffffffffff831681526060600460208301372080546120e5886fffffffffffffffffffffffffffffffff6120de8a8560801c613503565b9316613503565b9060801b01905560a01c016140ee565b7f5b0110b2000000000000000000000000000000000000000000000000000000005f5260045ffd5b346101f5575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f557601436036121f45761215b614279565b60a01c337fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820160a01b017f753dfe4b4dfb3ff6c11bbf6a97f3c094e91c003ce904a55cc5662fbad220f59901805c9160043560801d830192831590150390816121c5575b50505d005b7f7772acfd7e0f66ebb20a058830296c3dc1301b111d23348e1c961d324223190c0190815c01905d82806121c0565b7ffea66952000000000000000000000000000000000000000000000000000000005f5260045ffd5b346101f5575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f557612252614279565b60a01c7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8101604051915f809260045b3681106122e85750506122b9575b3660011c7f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01683f35b7f7772acfd7e0f66ebb20a058830296c3dc1301b111d23348e1c961d324223190c0190815c01905d8180612290565b90915073ffffffffffffffffffffffffffffffffffffffff81351690817f6747da56dbd05b26a7ecd2a0106781585141cf07098ad54c0e049e4e86dccb8c015f815c915d306014526f70a082310000000000000000000000005f5260205f60246010865afa601f3d11165f5102908060018184030192101990151516028060801c6123eb57602092818060801b7f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff08560011c168a01526123ae575b505001908391612282565b8460a01b017f753dfe4b4dfb3ff6c11bbf6a97f3c094e91c003ce904a55cc5662fbad220f5990190815c96818814881503019603905d86806123a3565b639cac58ca5f526004601cfd5b346101f55760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f55761242f6133ce565b6124376135b7565b817fffffffffffffffffffffffff00000000000000000000000000000000000000008216177f07cc7f5195d862f505d6b095c82f92e00cfc1766f5bca4383c28dc5fca1555fd5d5f80604051937c010000000000000000000000000000000000000000000000000000000085528360048601527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc3601602480870137848236925af115610ec7577f07cc7f5195d862f505d6b095c82f92e00cfc1766f5bca4383c28dc5fca1555fd5d3d5f823e3d90f35b346101f557611d287f3def450d0010a2fef515ce5eba4b363b5a0f42fdd4c53e1c737975db05a2e3a561253a3661337b565b93909201613607565b346101f55760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f557600435602435908160030b82036101f5576044358060030b81036101f5576040926125b5925f60206125a261343e565b8281520152805460801c60030b90614147565b60208251918051835201516020820152f35b5f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f5576125f86133f1565b5f60208201525f60408201526060600482376bffff9a5889f795069a41a8a360643560a01c111567400065a8177fae2760643560a01c101516673fffffffffffffff7fffffffffffffffffffffffffffffffffffffffff3fffffffffffffffffffffff60643560a01c16111615613353576126716135b7565b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000604435168160601b141560443560fe1c166132fe575b6060822090815492831561139a5760643560201c600f0b805f94821515806132ec575b612777575b5050507fffffffffffffffffffffffffffffffffffffffff000000000000000000000000604435168260601b141560443560fd1c16612716575b50505f5260205260405ff35b606090604051927fa4e8f2880000000000000000000000000000000000000000000000000000000084526004840152602483015e60643560848201528160a48201528260c48201525f8060e4838260443560601c5af115611418578061270a565b90919580955060a01c946fffffffffffffffffffffffffffffffff8160801c60030b9116965f81128760643560a01c10816001606435601f1c161415146132c4575f915f925f9a5b5f95819b604435638000000016155f1461321b5760443563ffffffff1661308857606435601f1c600116861461305857630549cd93916bffff9a5889f795069a41a8a35b929d5b606435601f1c60011688146130435760643560a01c808518908511028418905b6fffffffffffffffffffffffffffffffff8116612d7f5750925b6bffffffffffffffffffffffff84811691168103612bc7575050509a846001606435601f1c1614810396612a3a575b505b82158015612a1e575b156127bf576074976002936128fa61295c947fffffffffffffffffffffffffffffffff800000000000000000000000000000008113907fffffffffffffffffffffffffffffffff8000000000000000000000000000000018027fffffffffffffffffffffffffffffffff80000000000000000000000000000000186140c0565b9103606435601f1c60011615612a00576fffffffffffffffffffffffffffffffff169060801b17955b869b916fffffffffffffffffffffffffffffffff73ffffffff0000000000000000000000000000000091169160801b16179060a01b1790565b998a8655146129ca575b50506129a781600f0b8260801d600f0b6024356004357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8a60a01c016140ee565b604051918560601b835260148301526034820152856054820152a08480806126d0565b606435601f1c6001161883017fb09b03866d96933565a9435bfb511c8ac5b2be454285ca331201452704799f7201558780612966565b906fffffffffffffffffffffffffffffffff169060801b1795612923565b5060643560a01c6bffffffffffffffffffffffff8c161461287a565b7f435a5eb89a296820174331cf5a3902d9fca683928d56726d8e7acd6efb28c56890880190810154600f0b606435601f1c6001168087149087180302909101908c15612b8a575b606435601f1c60011685148881017fb09b03866d96933565a9435bfb511c8ac5b2be454285ca331201452704799f72015490612b10577f5695060fdb9cfea656f872ae4887221aff7dbfefc45eaf753e4e70cdfb5cd19c8201805490910390557fd0bf0a0be44c3c54cf61da346063acd98e9363a6de122b16472a1947527c5c9e018054860390555b8c61286f565b7fd0bf0a0be44c3c54cf61da346063acd98e9363a6de122b16472a1947527c5c9e907f5695060fdb9cfea656f872ae4887221aff7dbfefc45eaf753e4e70cdfb5cd19c83015488037f5695060fdb9cfea656f872ae4887221aff7dbfefc45eaf753e4e70cdfb5cd19c840155818301549003910155612b0a565b9b5093507fb09b03866d96933565a9435bfb511c8ac5b2be454285ca331201452704799f72836001606435601f1c1618870101549360019b612a81565b919d509750806bffffffffffffffffffffffff8e1603612be9575b5050612871565b9196509a506a1527370de4706fc9ea63ab612c2f8c60607fffffffffffffffffffffffffffffffffffffffff3fffffffffffffffffffffff82169160591c166002011b90565b8060801c15908082150290825f0304016802e2a8eca5705fc2ee8160801c1e60ff0391600183011c7fffffffffffffffffffffffffffffffff800000000000000000000000000000006f8000000000000000000000000000000082019101607f1b04808002607f1c90818102607f1c828102607f1c838102607f1c90848202607f1c92858402607f1c946003600560076009600b600d600f8d8d02607f1c9d8e02607f1c049c049a0498049604940492040101010101010102607f1c9060401b01905f14612d7a575f035b026e872032ac1300872032ac13008720327fffffffffffffffffffffffffffffffffff78dfcd53ecff78dfcd53ecff78dfce820160801d60030b910160801d60030b91818303612d4f575b5050948c80612be2565b6bffffffffffffffffffffffff612d6584613856565b1611612d72575b80612d45565b90508c612d6c565b612cfa565b959695909390881561302557865b606435601f1c6001161561301457612da6818387613e01565b905b606435601f1c600116808c1888841116908c14888410161715612f2e5750507fffffffffffffffffffffffffffffffffffffffff3fffffffffffffffffffffff85166002605987901c606016011b7fffffffffffffffffffffffffffffffffffffffff3fffffffffffffffffffffff85166002605987901c606016011b808203818311029182900391018a83606435601f1c60011615612f1157612e5992612e53831583838861406a565b94613f9f565b905b8a15612ee457612e7a67ffffffffffffffff60443560201c1683613f70565b809a019801980360801b04935b935b80612e95575b50612840565b9198909f155f14612edd57507fb09b03866d96933565a9435bfb511c8ac5b2be454285ca331201452704799f72876001606435601f1c16188b01015401965b60029e5f612e8f565b0196612ed4565b9098612eff67ffffffffffffffff60443560201c1683613f70565b998a91039803980360801b0493612e87565b612f2892612f228315838388613f9f565b9461406a565b90612e5b565b919295509796896bffffffffffffffffffffffff86166bffffffffffffffffffffffff8b1614155f14612fce5750606435601f1c60011615612fbc57612f768a84878c613f21565b915b8a15612fac575050612f9967ffffffffffffffff60443560201c1682613f70565b809701960360801b04925b5f9693612e89565b91909703960360801b0492612fa4565b612fc88a84878c613ea0565b91612f78565b91509192949750612fe75760801b04915f958293612e89565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52600160045260245ffd5b61301f818387613d0a565b90612da8565b67ffffffffffffffff8060443560201c1688020160401c8703612d8d565b60643560a01c80851890851002841890612826565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffab6326d9167400065a8177fae27612803565b8161312960449e939e3590630549cd93607f62ffffff841660020b60041b9360181c161c808303807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffab6326d03817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffab6326d13020192017ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffab6326d8101630549cd93821302900390565b9182828212159112165f14613166576131579e9190606435601f1c600116891461315d57509d8e5b9e613856565b92612806565b90509d8e613151565b92508d8360030b9060030b125f146131cb5750606435601f1c600116861461319a5761319182613856565b919c5b5f612806565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffab6326d915067400065a8177fae27613191565b909150606435601f1c60011686146131f95750630549cd93906bffff9a5889f795069a41a8a35b919c613194565b906bffffffffffffffffffffffff61321083613856565b9260030b92166131f2565b6131579c919750606435601f1c600116861461327c5761326f637fffffff60643516637fffffff604435168a7f3def450d0010a2fef515ce5eba4b363b5a0f42fdd4c53e1c737975db05a2e3a58d0161376c565b98909d8e995b999e613856565b6132ba637fffffff60643516637fffffff604435168a7f3def450d0010a2fef515ce5eba4b363b5a0f42fdd4c53e1c737975db05a2e3a58d01613607565b98909d8e99613275565b7fa574a6b4000000000000000000000000000000000000000000000000000000005f5260045ffd5b5060643560a01c8760a01c14156126cb565b6040517fca11dba7000000000000000000000000000000000000000000000000000000008152816004820152606083602483015e60643560848201525f8060a4838260443560601c5af11561141857506126a8565b7f9cc3dd4a000000000000000000000000000000000000000000000000000000005f5260045ffd5b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60809101126101f557600435906024358060030b81036101f5579060443563ffffffff811681036101f5579060643590565b6004359073ffffffffffffffffffffffffffffffffffffffff821682036101f557565b604051906060820182811067ffffffffffffffff82111761341157604052565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b604051906040820182811067ffffffffffffffff82111761341157604052565b60405190610100820182811067ffffffffffffffff82111761341157604052565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60609101126101f5576134b16133f1565b9060043573ffffffffffffffffffffffffffffffffffffffff811681036101f557825260243573ffffffffffffffffffffffffffffffffffffffff811681036101f55760208301526044356040830152565b8181019160ff1c8183118116918310901516178160801c176135225790565b631293d6fa5f526004601cfd5b906135386133f1565b9180547fffffffffffffffffffffffffffffffff000000000000000000000000000000008160801b16845260801c6020840152600261357561343e565b9160018101548352015460208201526040830152565b7f80000000000000000000000000000000000000000000000000000000000000008114611131575f0390565b6135bf614279565b903373ffffffffffffffffffffffffffffffffffffffff8316036135df57565b7feed0f119000000000000000000000000000000000000000000000000000000005f5260045ffd5b5f929182810784139083900503630554777f01600881901c9060ff165b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60018060ff858701549416011b01161e806101000361373357507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffaab8881839160081b0102937ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffab6326d8560030b131561370a578015613703577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff908101946136249184910160030b630554777f915f81830712910503019060ff8260081c921690565b5050509091565b50505090507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffab6326d91565b945090507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffaab8880919250600193610100039060081b01010291565b91909161379f825f945b63ffffffff821660030b0160030b630554777f915f81830712910503019060ff8260081c921690565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600160ff8486015493161b011916805f03161e806101000361373357507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffaab8980839160081b010293630549cd938560030b1215613849578015613703577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff019361379f908390613776565b5050509050630549cd9391565b908160030b918260ff1d8084011890630549cd938211613cde576138d49293506d08637b66cd638344daef276cd7c56001831602700100000000000000000000000000000000039160fe8116613bdb575b617f008116613ad1575b621f800081166139e8575b6307e0000081166138fe575b505f126138d757614647565b90565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04614647565b6220000081166139cc575b6240000081166139b0575b628000008116613994575b63010000008116613979575b6302000000811661395f575b630400000016613948575b5f6138c8565b69c0d55d4d7152c25fb13990910260801c90613942565b6cde2ee4bc381afa7089aa84bb6690920260801c91613937565b916e0ee7e32d61fdb0a5e622b820f681d00260801c9161392b565b916f03dc5dac7376e20fc8679758d1bcdcfc0260801c9161391f565b916f1f703399d88f6aa83a28b22d4a1f56e30260801c91613914565b916f59b63684b86e9f486ec54727371ba6ca0260801c91613909565b6180008116613ab5575b620100008116613a99575b620200008116613a7d575b620400008116613a61575b620800008116613a45575b621000008116156138bc57916f978bcb9894317807e5fa4498eee7c0fa0260801c916138bc565b916fc4f76b68947482dc198a48a54348c4ed0260801c91613a1e565b916fe08d35706200796273f0b3a981d90cfd0260801c91613a13565b916fefc2bf59df33ecc28125cf78ec4f167f0260801c91613a08565b916ff7bf5211c72f5185f372aeb1d48f937e0260801c916139fd565b916ffbd701c7cbc4c8a6bb81efd232d1e4e70260801c916139f2565b6101008116613bbf575b6102008116613ba3575b6104008116613b87575b6108008116613b6b575b6110008116613b4f575b6120008116613b33575b6140008116156138b157916ffde95287d26d81bea159c37073122c730260801c916138b1565b916ffef41d1a5f2ae3a20676bec6f7f9459a0260801c91613b0d565b916fff79eb706b9a64c6431d76e63531e9290260801c91613b03565b916fffbceceeb791747f10df216f2e53ec570260801c91613af9565b916fffde7444b28145508125d10077ba83b80260801c91613aef565b916fffef3995a5b6a6267530f207142a57640260801c91613ae5565b916ffff79ca7a4d1bf1ee8556cea23cdbaa50260801c91613adb565b60028116613cc2575b60048116613ca6575b60088116613c8a575b60108116613c6e575b60208116613c52575b60408116613c36575b60808116156138a757916ffffbce4b06c196e9247ac87695d53c600260801c916138a7565b916ffffde72350725cc4ea8feece3b5f13c80260801c91613c11565b916ffffef3911b7cff24ba1b3dbb5f8f59740260801c91613c08565b916fffff79c86a8f6150a32d9778eceef97c0260801c91613bff565b916fffffbce42c7be6c998ad6318193c0b180260801c91613bf6565b916fffffde72140b00a354bd3dc828e976c90260801c91613bed565b916fffffef390978c398134b4ff3764fe4100260801c91613be4565b837f073ee172000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b909180600f0b918215613dfa57613d4d5f9160607fffffffffffffffffffffffffffffffffffffffff3fffffffffffffffffffffff82169160591c166002011b90565b9360801b921215613de6575f03827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048111613dd35782029181831015613dd357613d9a92820391614808565b77fffffffffffffffffffffffc0000000000000000000000008111613dc2576138d490614719565b506bffffffffffffffffffffffff90565b5050506bffffffffffffffffffffffff90565b6138d4928204018082061515910401614719565b9250505090565b613e369060607fffffffffffffffffffffffffffffffffffffffff3fffffffffffffffffffffff82169160591c166002011b90565b915f81600f0b125f14613e6257906138d492915f0360801b908082061515910401808203911102614647565b60801b0481019081108015613e7f575b613dc2576138d490614647565b5077ffffffffffffffffffffffffffffffffffffffffffffffff8111613e72565b90613f10613ede6138d495949360607fffffffffffffffffffffffffffffffffffffffff3fffffffffffffffffffffff82169160591c166002011b90565b9160607fffffffffffffffffffffffffffffffffffffffff3fffffffffffffffffffffff82169160591c166002011b90565b80821181830302809101910361406a565b90613f5f613ede6138d495949360607fffffffffffffffffffffffffffffffffffffffff3fffffffffffffffffffffff82169160591c166002011b90565b808211818303028091019103613f9f565b60401b9068010000000000000000038082049106151501908160801c613f9257565b630d88f5265f526004601cfd5b929160801b915f14613fd957613fb89183820390614808565b9080820615159104018060801c613fcc5790565b63b4ef25465f526004601cfd5b828103908183027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8385099382805f0381169485920992048060030260021880820260020302808202600203028082026002030280820260020302808202600203028091026002030293816001858583030495805f03040193119180821001900303021702048060801c613fcc5790565b900391156140ac578061408f70010000000000000000000000000000000092846148d2565b92091515018060801c61409f5790565b6359d2b24a5f526004601cfd5b6140b5916148d2565b8060801c61409f5790565b806f800000000000000000000000000000000160801c156140e8576335278d125f526004601cfd5b600f0b90565b909392919034614104579361410294614383565b565b9173ffffffffffffffffffffffffffffffffffffffff851661412e57936141029434900392614383565b8293916141029561413e94614383565b345f03906142cc565b93929161415261343e565b60208101938196838101907fd0bf0a0be44c3c54cf61da346063acd98e9363a6de122b16472a1947527c5c9e7f5695060fdb9cfea656f872ae4887221aff7dbfefc45eaf753e4e70cdfb5cd19c83015492015494838201937fd0bf0a0be44c3c54cf61da346063acd98e9363a6de122b16472a1947527c5c9e7f5695060fdb9cfea656f872ae4887221aff7dbfefc45eaf753e4e70cdfb5cd19c8601549501549760030b9160030b82125f1461420d57505050039052039052565b9596959194939160030b1315614270577fb09b03866d96933565a9435bfb511c8ac5b2be454285ca331201452704799f737fb09b03866d96933565a9435bfb511c8ac5b2be454285ca331201452704799f72850154940154930303905203039052565b03905250039052565b7f07cc7f5195d862f505d6b095c82f92e00cfc1766f5bca4383c28dc5fca1555fd5c9081156142a457565b7f1834e265000000000000000000000000000000000000000000000000000000005f5260045ffd5b8060a01b7f753dfe4b4dfb3ff6c11bbf6a97f3c094e91c003ce904a55cc5662fbad220f5990190815c928301928315901503908161430a575b50505d565b7f7772acfd7e0f66ebb20a058830296c3dc1301b111d23348e1c961d324223190d0190815c01905d5f80614305565b919061437f6fffffffffffffffffffffffffffffffff9182614378816040602089015198015193602080865183510396015191015103971680936148d2565b16946148d2565b1690565b92809492915f9561440d575b5050816143cc575b5050816143a2575050565b7f7772acfd7e0f66ebb20a058830296c3dc1301b111d23348e1c961d324223190d0190815c01905d565b8260a09492941b017f753dfe4b4dfb3ff6c11bbf6a97f3c094e91c003ce904a55cc5662fbad220f5990190815c938401938415901503019283915d5f614397565b839195508460a01b017f753dfe4b4dfb3ff6c11bbf6a97f3c094e91c003ce904a55cc5662fbad220f5990194855c908101908115901503955d5f61438f565b9060030b90811561445e5760030b0790565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b90925f905f94600f0b9384156145b0575f8513936fffffffffffffffffffffffffffffffff8560011b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01968060ff1d8091011816936bffffffffffffffffffffffff82166bffffffffffffffffffffffff84168111155f1461453657505050926fffffffffffffffffffffffffffffffff9261452c926145339695613f21565b16026140c0565b91565b929897509092916bffffffffffffffffffffffff8316111561458c57509282826145866fffffffffffffffffffffffffffffffff61457e8582986138d49b9a61452c99613f21565b1687026140c0565b98613ea0565b9661452c9250926138d49594916fffffffffffffffffffffffffffffffff94613ea0565b5050505050505f905f90565b01907fffffffffffffffffffffffffffffffff0000000000000000000000000000000082166145e757565b7f6d862c50000000000000000000000000000000000000000000000000000000005f5260045ffd5b61461761345e565b905f82525f60208301525f60408301525f60608301525f60808301525f60a08301525f60c08301525f60e0830152565b6c0100000000000000000000000081106147135770010000000000000000000000000000000081106146ff577401000000000000000000000000000000000000000081106146eb57780100000000000000000000000000000000000000000000000081106146d7577fa10459f4000000000000000000000000000000000000000000000000000000005f5260045ffd5b60621c6bc000000000000000000000001790565b60421c6b8000000000000000000000001790565b60221c6b4000000000000000000000001790565b60021c90565b600381016c01000000000000000000000000811061480057506403ffffffff810170010000000000000000000000000000000081106147ea57506803ffffffffffffffff8101907401000000000000000000000000000000000000000082106147d5576c03ffffffffffffffffffffffff915001780100000000000000000000000000000000000000000000000081106146d7577fa10459f4000000000000000000000000000000000000000000000000000000005f5260045ffd5b5060421c6b8000000000000000000000001790565b905060221c6b4000000000000000000000001790565b905060021c90565b92918082850281861585888404141702156148445704915b82940961482a5750565b6001019150811561483757565b63ae47f7025f526004601cfd5b90507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8386098181108201900382848709835f03841691808511156148375782850480600302600218808202600203028082026002030280820260020302808202600203028082026002030280910260020302936001848483030494805f0304019211900302170291614820565b818102918082840414821517156148eb57505060801c90565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff910981811082019003908160801c1561492c5763ae47f7025f526004601cfd5b60801c9060801b019056fea26469706673582212208f4790146f3838995be055c447caa079afb5b6eeabdaeb4a4501328670e4187e64736f6c63430008210033

Deployed Bytecode

0x60806040526004361015610053575b3615610018575f80fd5b6100517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff610044614279565b60a01c01345f03906142cc565b005b5f3560e01c80156125c75780626c95a9146125435780630e7f263914612508578063101e8952146123f857806312e103f11461221c57806317c5da6a1461211d578063187437a114611fec578063380eb4e014611f805780633ccfd60b14611df85780635ebb090914611d3f57806366e064a814611ced57806379b45a071461198b578063c0530244146115ac578063cb48dadf146108af578063de6f935f14610553578063e96404f814610376578063ed8328301461030a578063f83d08ba146101f95763f9b6a7960361000e57346101f5577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36015f81126101f557306014526f70a082310000000000000000000000005f5260405160045b3681106101785750f35b8073ffffffffffffffffffffffffffffffffffffffff60209235167ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc82850101838160246010855afa9051601f3d1182160201907f6747da56dbd05b26a7ecd2a0106781585141cf07098ad54c0e049e4e86dccb8c015d0161016e565b5f80fd5b346101f5577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36015f81126101f5577f07cc7f5195d862f505d6b095c82f92e00cfc1766f5bca4383c28dc5fca1555fd5c8060a01c90336001830160a01b177f07cc7f5195d862f505d6b095c82f92e00cfc1766f5bca4383c28dc5fca1555fd5d604051925f8452826004850152600460248501375f80602036018582335af115610301577f07cc7f5195d862f505d6b095c82f92e00cfc1766f5bca4383c28dc5fca1555fd5d807f7772acfd7e0f66ebb20a058830296c3dc1301b111d23348e1c961d324223190d015c6102f157503d5f823e3d90f35b639731ba375f526020526024601cfd5b823d5f823e3d90fd5b346101f5577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36015f81126101f55760045b36811061034757505ff35b80355c7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc82015260200161033c565b60a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f5576103a93661347f565b6064356fffffffffffffffffffffffffffffffff8116918282036101f557608435926fffffffffffffffffffffffffffffffff8416908185036101f5576103ee6135b7565b604084015160601c73ffffffffffffffffffffffffffffffffffffffff8216036101f5577ff7e050d866774820d81a86ca676f3afe7bc72603ee893f82e99c08fbde39af6c9560609561049a928787209682158015809161054a575b6104ad575b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff73ffffffffffffffffffffffffffffffffffffffff602081845116930151169260a01c016140ee565b60405192835260208301526040820152a1005b6fffffffffffffffffffffffffffffffff89541690816104ce575b5061044f565b7fb09b03866d96933565a9435bfb511c8ac5b2be454285ca331201452704799f728a0190610539575b5084610504575b806104c8565b7fb09b03866d96933565a9435bfb511c8ac5b2be454285ca331201452704799f738901908154908660801b040190558a6104fe565b8054828660801b040190558b6104f7565b5084151561044a565b346101f5576101007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f55761058b61345e565b60043580151581036101f557815260243580151581036101f557602082015260443580151581036101f557604082015260643580151581036101f557606082015260843580151581036101f557608082015260a43580151581036101f55760a082015260c43580151581036101f55760c082015260e43580151581036101f55760e082015261061861460f565b5061062161460f565b5060013360981c16159061063361345e565b82158082523360981c6080818116151560208086019190915260408084161515908601528216151560608501526010821615159084015260088116151560a084015260048116151560c0840152600216151560e09092019190915281511515149081610896575b8161087d575b81610864575b8161084b575b81610832575b81610819575b81610801575b501590811561075e575b5061073657335f525f60205260405f20805461070e57600190557fec1256266e470abb868620c851f6bde2a3ff602549dcad318ab9ccfcb2977f146020604051338152a1005b7f8b20f687000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f7865ebfd000000000000000000000000000000000000000000000000000000005f5260045ffd5b801591506107f2575b80156107e3575b80156107d4575b80156107c5575b80156107b6575b80156107a7575b8015610798575b15816106c8565b5060023360981c161515610791565b5060043360981c16151561078a565b5060083360981c161515610783565b5060103360981c16151561077c565b5060203360981c161515610775565b5060403360981c16151561076e565b5060803360981c161515610767565b60e091500151151560023360981c16151514826106be565b905060c0810151151560043360981c16151514906106b8565b905060a0810151151560083360981c16151514906106b2565b90506080810151151560103360981c16151514906106ac565b90506060810151151560203360981c16151514906106a6565b90506040810151151560403360981c16151514906106a0565b90506020810151151560803360981c161515149061069a565b60a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f5576108e23661347f565b608435600f0b608435036101f55760408101515f9063800000008116156114bb5760643560201c60030b60643560030b9182821215611493577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffab6326d82128015611486575b61145e57637fffffff61095d911660030b809261444c565b60030b1591821592611448575b5050611420575b6109796135b7565b60408301517fffffffffffffffffffffffffffffffffffffffff00000000000000000000000081168260601b14158160fc1c166113c2575b506060832091825490811561139a57608435600f0b1561135657506109dd60643560201c60030b613856565b6109fc6109ee60643560030b613856565b8360a01c926084358461448b565b9091610a4060643573ffffffffffffffffffffffffffffffffffffffff871688906060929160405192835260208301526040820152205f52600160205260405f2090565b90610a51608435835460801c6145bc565b610a5961343e565b5f8152602081015f8152819360408c015163800000001615155f1461127c575050506fffffffffffffffffffffffffffffffff811615611255575b60408901517f435a5eb89a296820174331cf5a3902d9fca683928d56726d8e7acd6efb28c56860643560201c60030b8a0101908154610ad86084358260801c6145bc565b916f7fffffffffffffffffffffffffffffff600f83810b60843590910b019081137fffffffffffffffffffffffffffffffff8000000000000000000000000000000090911217611131576fffffffffffffffffffffffffffffffff637fffffff8216630549cd930460011b6001018104166fffffffffffffffffffffffffffffffff84169080821161121a57506fffffffffffffffffffffffffffffffff92918d91158360801c150361115e575b5050608435600f0b90600f0b01169060801b17905560408901517f435a5eb89a296820174331cf5a3902d9fca683928d56726d8e7acd6efb28c56860643560030b8a010154610bda6084358260801c6145bc565b917fffffffffffffffffffffffffffffffff80000000000000000000000000000000608435600f90810b9084900b039081126f7fffffffffffffffffffffffffffffff90911317611131576fffffffffffffffffffffffffffffffff637fffffff8216630549cd930460011b600101810416806fffffffffffffffffffffffffffffffff8516116110e75750906fffffffffffffffffffffffffffffffff91828416158260801c150361102f575b50608435600f0b90600f0b03169060801b177f435a5eb89a296820174331cf5a3902d9fca683928d56726d8e7acd6efb28c56860643560030b8a0101556fffffffffffffffffffffffffffffffff8116611008575b8560801c60030b60643560201c60030b81121580610ffa575b610f63575b50927f704b3ab4a76158ad4d66625a2a43be81edbffd24630e8fde5174e97035370a0794926fffffffffffffffffffffffffffffffff9260c0955b808516610ed0575050600281845f9354168155826020610d5461343e565b828152015282600182015501555b610dce73ffffffffffffffffffffffffffffffffffffffff8a511673ffffffffffffffffffffffffffffffffffffffff60208c0151169083600f0b9186600f0b917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8c60a01c016140ee565b169060801b17946040519073ffffffffffffffffffffffffffffffffffffffff8616825260208201526064356040820152608435600f0b60608201528560808201528360a0820152a15b6040840151907fffffffffffffffffffffffffffffffffffffffff00000000000000000000000082168360601b14158260fb1c16610e5c575b602084604051908152f35b5f92838093610104936060604051998a957f25fa4e690000000000000000000000000000000000000000000000000000000087526004870152602486015e606435608485015260843560a48501528760c485015260e484015260601c5af115610ec757818080610e51565b503d5f823e3d90fd5b918192610ee7600293610ee28461352f565b614339565b9490918354887fffffffffffffffffffffffffffffffff000000000000000000000000000000008360801b169116178455610f2061343e565b9581602088019460801b04875260801b0482526020610f3d61343e565b925f8452818401965f885251835103809452519101510380945260018201550155610d62565b926fffffffffffffffffffffffffffffffff92610fea60c09693987f704b3ab4a76158ad4d66625a2a43be81edbffd24630e8fde5174e97035370a079896610faf6084358884166145bc565b9160a01c916fffffffffffffffffffffffffffffffff73ffffffff0000000000000000000000000000000091169160801b16179060a01b1790565b97888b5592955092509294610cfb565b5060643560030b8112610cf6565b905061102960643560030b60643560201c60030b8760801c60030b8a614147565b90610cdd565b61107b637fffffff7f3def450d0010a2fef515ce5eba4b363b5a0f42fdd4c53e1c737975db05a2e3a5921660643560030b630554777f915f81830712910503019060ff8260081c921690565b908d019091018054600190921b909118905582151560643560030b8c017f5695060fdb9cfea656f872ae4887221aff7dbfefc45eaf753e4e70cdfb5cd19c81018290557fd0bf0a0be44c3c54cf61da346063acd98e9363a6de122b16472a1947527c5c9e01558c610c88565b6fffffffffffffffffffffffffffffffff847fe51274fb000000000000000000000000000000000000000000000000000000005f5260643560030b6004521660245260445260645ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b6111ad637fffffff7f3def450d0010a2fef515ce5eba4b363b5a0f42fdd4c53e1c737975db05a2e3a5921660643560201c60030b630554777f915f81830712910503019060ff8260081c921690565b9201018054600190921b909118905582151560643560201c60030b8d017f5695060fdb9cfea656f872ae4887221aff7dbfefc45eaf753e4e70cdfb5cd19c81018290557fd0bf0a0be44c3c54cf61da346063acd98e9363a6de122b16472a1947527c5c9e01558b8e610b86565b907fe51274fb000000000000000000000000000000000000000000000000000000005f5260643560201c60030b60045260245260445260645ffd5b905061127660643560030b60643560201c60030b8760801c60030b8a614147565b90610a94565b926fffffffffffffffffffffffffffffffff94916113017f704b3ab4a76158ad4d66625a2a43be81edbffd24630e8fde5174e97035370a079997949a60c099966112ca6084358a84166145bc565b60a09190911b73ffffffff000000000000000000000000000000009092166fffffffffffffffffffffffffffffffff909116171790565b998a8d557fb09b03866d96933565a9435bfb511c8ac5b2be454285ca331201452704799f728d015490527fb09b03866d96933565a9435bfb511c8ac5b2be454285ca331201452704799f738c01549052610d36565b92503415610e185761139561136a3461358b565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8460a01c016142cc565b610e18565b7f486aa307000000000000000000000000000000000000000000000000000000005f5260045ffd5b5f8060c481936040519485917e35c72300000000000000000000000000000000000000000000000000000000835287600484015260608a602485015e606435608484015260843560a484015260601c5af16109b1575b3d5f823e3d90fd5b7f89fd41b1000000000000000000000000000000000000000000000000000000005f5260045ffd5b611452925061444c565b60030b1515838061096a565b7fabfa4a31000000000000000000000000000000000000000000000000000000005f5260045ffd5b50630549cd938313610945565b7f68651c5c000000000000000000000000000000000000000000000000000000005f5260045ffd5b611554630549cd93607f62ffffff841660020b60041b9360181c161c808303807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffab6326d03817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffab6326d13020192017ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffab6326d8101630549cd93821302900390565b9060030b60643560201c60030b1490811591611599575b5015610971577f72b1019d000000000000000000000000000000000000000000000000000000005f5260045ffd5b905060030b60643560030b14158361156b565b346101f55760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f5576115e43661347f565b6064358060030b8082036101f55773ffffffffffffffffffffffffffffffffffffffff835116602084019073ffffffffffffffffffffffffffffffffffffffff82511611156119635760408401805190939063800000008116156118b857637fffffff16620aa8ed81119081156118af575b50611887575b83518060601c93846117f2575b606087209586546117ca5773ffffffffffffffffffffffffffffffffffffffff61169285613856565b9573ffffffff000000000000000000000000000000008660801b168760a01b17895560017fb09b03866d96933565a9435bfb511c8ac5b2be454285ca331201452704799f728a015560017fb09b03866d96933565a9435bfb511c8ac5b2be454285ca331201452704799f738a0155604051988952818a511660208a01525116604088015251606087015260808601527f5e4688b340694b7c7fd30047fd082117dc46e32acfbf81a44bb1fac0ae65154d60c06bffffffffffffffffffffffff8516968760a0820152a160ff1c338414151661177257602084604051908152f35b6060945f8094819460c494604051998a957f948374ff000000000000000000000000000000000000000000000000000000008752336004880152602487015e608485015260a48401525af115610ec757818080610e51565b7f7983c051000000000000000000000000000000000000000000000000000000005f5260045ffd5b845f525f60205260405f20541561185f57843314158260f81c1615611669576040517f1fbbb462000000000000000000000000000000000000000000000000000000008152336004820152606088602483015e8360848201525f8060a483828a5af1156114185750611669565b7fcd72c3bc000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f270815a0000000000000000000000000000000000000000000000000000000005f5260045ffd5b90501586611656565b601a607f8260181c161161193b5762ffffff1660020b60041b60030b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffab6326d811290811561192d575b501561165c577f10de0c76000000000000000000000000000000000000000000000000000000005f5260045ffd5b630549cd93915013866118ff565b7f218b53af000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f88a2efcf000000000000000000000000000000000000000000000000000000005f5260045ffd5b346101f55760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f5576119c33661347f565b6064356119ce6135b7565b90604083019182518160601b907fffffffffffffffffffffffffffffffffffffffff00000000000000000000000081168214158160fa1c16611c99575b50606085209273ffffffffffffffffffffffffffffffffffffffff831694611a53828787906060929160405192835260208301526040820152205f52600160205260405f2090565b91611a5c61343e565b5f81525f6020820152918051638000000016155f14611c73577fb09b03866d96933565a9435bfb511c8ac5b2be454285ca331201452704799f7287015483527fb09b03866d96933565a9435bfb511c8ac5b2be454285ca331201452704799f7387015460208401525b82611acf8561352f565b90611ad991614339565b94909380516001830155602001519060020155885173ffffffffffffffffffffffffffffffffffffffff169760208a015173ffffffffffffffffffffffffffffffffffffffff16976fffffffffffffffffffffffffffffffff851699611b3e8b61358b565b996fffffffffffffffffffffffffffffffff88169a611b5c8c61358b565b918b60a01c7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0193611b8d94614383565b604051918252602082015282604082015288606082015287608082015260a07fd76ec32fbc3f07c70828b4f94343ee73279d0e8d4d2f28b018a4e67f3749775391a151937fffffffffffffffffffffffffffffffffffffffff000000000000000000000000851614158460f91c16611c10575b6040878782519182526020820152f35b5f94919360e4938695869360606040519c8d977f751fd5df0000000000000000000000000000000000000000000000000000000089526004890152602488015e608486015260a485015260c484015260601c5af115610301578280808080611c00565b9150611c93865460801c60030b8260030b908360201c60030b9089614147565b91611ac5565b5f8060a481936040519485917fdf65d8d100000000000000000000000000000000000000000000000000000000835288600484015260608c602485015e89608484015260601c5af1611a0b573d5f823e3d90fd5b346101f557611d287f3def450d0010a2fef515ce5eba4b363b5a0f42fdd4c53e1c737975db05a2e3a5611d1f3661337b565b9390920161376c565b6040805160039390930b8352901515602083015290f35b346101f55760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f5576044357fffffffffffffffffffffffffffffffff00000000000000000000000000000000811681036101f557611dc860243533600435906060929160405192835260208301526040820152205f52600160205260405f2090565b907fffffffffffffffffffffffffffffffff000000000000000000000000000000008254169060801c1790555f80f35b346101f5575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f557611e2e6135b7565b5f9060a01c817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820160045b368110611e93575050611e6957005b7f7772acfd7e0f66ebb20a058830296c3dc1301b111d23348e1c961d324223190c0190815c01905d005b809192503560601c601482013560601c602883013560801c9081611ec0575b505050603801908391611e5a565b828560a01b017f753dfe4b4dfb3ff6c11bbf6a97f3c094e91c003ce904a55cc5662fbad220f5990196875c9083820191821590150301975d8215611f5c576014526034526fa9059cbb0000000000000000000000005f5260205f6044601082855af1908160015f51141615611f3e575b50506038905b908580611eb2565b3b153d171015611f4f578480611f30565b6390b8ec185f526004601cfd5b5f809350809281925af115611f7357603890611f36565b63b12d13eb5f526004601cfd5b346101f5577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36015f81126101f55760045b368110611fbd57505ff35b8035547ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc820152602001611fb2565b60a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f55761201e6133ce565b60243573ffffffffffffffffffffffffffffffffffffffff8116908181036101f557606435906084359273ffffffffffffffffffffffffffffffffffffffff851610156120f557610051937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6120926135b7565b608060405173ffffffffffffffffffffffffffffffffffffffff831681526060600460208301372080546120e5886fffffffffffffffffffffffffffffffff6120de8a8560801c613503565b9316613503565b9060801b01905560a01c016140ee565b7f5b0110b2000000000000000000000000000000000000000000000000000000005f5260045ffd5b346101f5575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f557601436036121f45761215b614279565b60a01c337fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820160a01b017f753dfe4b4dfb3ff6c11bbf6a97f3c094e91c003ce904a55cc5662fbad220f59901805c9160043560801d830192831590150390816121c5575b50505d005b7f7772acfd7e0f66ebb20a058830296c3dc1301b111d23348e1c961d324223190c0190815c01905d82806121c0565b7ffea66952000000000000000000000000000000000000000000000000000000005f5260045ffd5b346101f5575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f557612252614279565b60a01c7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8101604051915f809260045b3681106122e85750506122b9575b3660011c7f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01683f35b7f7772acfd7e0f66ebb20a058830296c3dc1301b111d23348e1c961d324223190c0190815c01905d8180612290565b90915073ffffffffffffffffffffffffffffffffffffffff81351690817f6747da56dbd05b26a7ecd2a0106781585141cf07098ad54c0e049e4e86dccb8c015f815c915d306014526f70a082310000000000000000000000005f5260205f60246010865afa601f3d11165f5102908060018184030192101990151516028060801c6123eb57602092818060801b7f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff08560011c168a01526123ae575b505001908391612282565b8460a01b017f753dfe4b4dfb3ff6c11bbf6a97f3c094e91c003ce904a55cc5662fbad220f5990190815c96818814881503019603905d86806123a3565b639cac58ca5f526004601cfd5b346101f55760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f55761242f6133ce565b6124376135b7565b817fffffffffffffffffffffffff00000000000000000000000000000000000000008216177f07cc7f5195d862f505d6b095c82f92e00cfc1766f5bca4383c28dc5fca1555fd5d5f80604051937c010000000000000000000000000000000000000000000000000000000085528360048601527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc3601602480870137848236925af115610ec7577f07cc7f5195d862f505d6b095c82f92e00cfc1766f5bca4383c28dc5fca1555fd5d3d5f823e3d90f35b346101f557611d287f3def450d0010a2fef515ce5eba4b363b5a0f42fdd4c53e1c737975db05a2e3a561253a3661337b565b93909201613607565b346101f55760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f557600435602435908160030b82036101f5576044358060030b81036101f5576040926125b5925f60206125a261343e565b8281520152805460801c60030b90614147565b60208251918051835201516020820152f35b5f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101f5576125f86133f1565b5f60208201525f60408201526060600482376bffff9a5889f795069a41a8a360643560a01c111567400065a8177fae2760643560a01c101516673fffffffffffffff7fffffffffffffffffffffffffffffffffffffffff3fffffffffffffffffffffff60643560a01c16111615613353576126716135b7565b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000604435168160601b141560443560fe1c166132fe575b6060822090815492831561139a5760643560201c600f0b805f94821515806132ec575b612777575b5050507fffffffffffffffffffffffffffffffffffffffff000000000000000000000000604435168260601b141560443560fd1c16612716575b50505f5260205260405ff35b606090604051927fa4e8f2880000000000000000000000000000000000000000000000000000000084526004840152602483015e60643560848201528160a48201528260c48201525f8060e4838260443560601c5af115611418578061270a565b90919580955060a01c946fffffffffffffffffffffffffffffffff8160801c60030b9116965f81128760643560a01c10816001606435601f1c161415146132c4575f915f925f9a5b5f95819b604435638000000016155f1461321b5760443563ffffffff1661308857606435601f1c600116861461305857630549cd93916bffff9a5889f795069a41a8a35b929d5b606435601f1c60011688146130435760643560a01c808518908511028418905b6fffffffffffffffffffffffffffffffff8116612d7f5750925b6bffffffffffffffffffffffff84811691168103612bc7575050509a846001606435601f1c1614810396612a3a575b505b82158015612a1e575b156127bf576074976002936128fa61295c947fffffffffffffffffffffffffffffffff800000000000000000000000000000008113907fffffffffffffffffffffffffffffffff8000000000000000000000000000000018027fffffffffffffffffffffffffffffffff80000000000000000000000000000000186140c0565b9103606435601f1c60011615612a00576fffffffffffffffffffffffffffffffff169060801b17955b869b916fffffffffffffffffffffffffffffffff73ffffffff0000000000000000000000000000000091169160801b16179060a01b1790565b998a8655146129ca575b50506129a781600f0b8260801d600f0b6024356004357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8a60a01c016140ee565b604051918560601b835260148301526034820152856054820152a08480806126d0565b606435601f1c6001161883017fb09b03866d96933565a9435bfb511c8ac5b2be454285ca331201452704799f7201558780612966565b906fffffffffffffffffffffffffffffffff169060801b1795612923565b5060643560a01c6bffffffffffffffffffffffff8c161461287a565b7f435a5eb89a296820174331cf5a3902d9fca683928d56726d8e7acd6efb28c56890880190810154600f0b606435601f1c6001168087149087180302909101908c15612b8a575b606435601f1c60011685148881017fb09b03866d96933565a9435bfb511c8ac5b2be454285ca331201452704799f72015490612b10577f5695060fdb9cfea656f872ae4887221aff7dbfefc45eaf753e4e70cdfb5cd19c8201805490910390557fd0bf0a0be44c3c54cf61da346063acd98e9363a6de122b16472a1947527c5c9e018054860390555b8c61286f565b7fd0bf0a0be44c3c54cf61da346063acd98e9363a6de122b16472a1947527c5c9e907f5695060fdb9cfea656f872ae4887221aff7dbfefc45eaf753e4e70cdfb5cd19c83015488037f5695060fdb9cfea656f872ae4887221aff7dbfefc45eaf753e4e70cdfb5cd19c840155818301549003910155612b0a565b9b5093507fb09b03866d96933565a9435bfb511c8ac5b2be454285ca331201452704799f72836001606435601f1c1618870101549360019b612a81565b919d509750806bffffffffffffffffffffffff8e1603612be9575b5050612871565b9196509a506a1527370de4706fc9ea63ab612c2f8c60607fffffffffffffffffffffffffffffffffffffffff3fffffffffffffffffffffff82169160591c166002011b90565b8060801c15908082150290825f0304016802e2a8eca5705fc2ee8160801c1e60ff0391600183011c7fffffffffffffffffffffffffffffffff800000000000000000000000000000006f8000000000000000000000000000000082019101607f1b04808002607f1c90818102607f1c828102607f1c838102607f1c90848202607f1c92858402607f1c946003600560076009600b600d600f8d8d02607f1c9d8e02607f1c049c049a0498049604940492040101010101010102607f1c9060401b01905f14612d7a575f035b026e872032ac1300872032ac13008720327fffffffffffffffffffffffffffffffffff78dfcd53ecff78dfcd53ecff78dfce820160801d60030b910160801d60030b91818303612d4f575b5050948c80612be2565b6bffffffffffffffffffffffff612d6584613856565b1611612d72575b80612d45565b90508c612d6c565b612cfa565b959695909390881561302557865b606435601f1c6001161561301457612da6818387613e01565b905b606435601f1c600116808c1888841116908c14888410161715612f2e5750507fffffffffffffffffffffffffffffffffffffffff3fffffffffffffffffffffff85166002605987901c606016011b7fffffffffffffffffffffffffffffffffffffffff3fffffffffffffffffffffff85166002605987901c606016011b808203818311029182900391018a83606435601f1c60011615612f1157612e5992612e53831583838861406a565b94613f9f565b905b8a15612ee457612e7a67ffffffffffffffff60443560201c1683613f70565b809a019801980360801b04935b935b80612e95575b50612840565b9198909f155f14612edd57507fb09b03866d96933565a9435bfb511c8ac5b2be454285ca331201452704799f72876001606435601f1c16188b01015401965b60029e5f612e8f565b0196612ed4565b9098612eff67ffffffffffffffff60443560201c1683613f70565b998a91039803980360801b0493612e87565b612f2892612f228315838388613f9f565b9461406a565b90612e5b565b919295509796896bffffffffffffffffffffffff86166bffffffffffffffffffffffff8b1614155f14612fce5750606435601f1c60011615612fbc57612f768a84878c613f21565b915b8a15612fac575050612f9967ffffffffffffffff60443560201c1682613f70565b809701960360801b04925b5f9693612e89565b91909703960360801b0492612fa4565b612fc88a84878c613ea0565b91612f78565b91509192949750612fe75760801b04915f958293612e89565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52600160045260245ffd5b61301f818387613d0a565b90612da8565b67ffffffffffffffff8060443560201c1688020160401c8703612d8d565b60643560a01c80851890851002841890612826565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffab6326d9167400065a8177fae27612803565b8161312960449e939e3590630549cd93607f62ffffff841660020b60041b9360181c161c808303807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffab6326d03817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffab6326d13020192017ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffab6326d8101630549cd93821302900390565b9182828212159112165f14613166576131579e9190606435601f1c600116891461315d57509d8e5b9e613856565b92612806565b90509d8e613151565b92508d8360030b9060030b125f146131cb5750606435601f1c600116861461319a5761319182613856565b919c5b5f612806565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffab6326d915067400065a8177fae27613191565b909150606435601f1c60011686146131f95750630549cd93906bffff9a5889f795069a41a8a35b919c613194565b906bffffffffffffffffffffffff61321083613856565b9260030b92166131f2565b6131579c919750606435601f1c600116861461327c5761326f637fffffff60643516637fffffff604435168a7f3def450d0010a2fef515ce5eba4b363b5a0f42fdd4c53e1c737975db05a2e3a58d0161376c565b98909d8e995b999e613856565b6132ba637fffffff60643516637fffffff604435168a7f3def450d0010a2fef515ce5eba4b363b5a0f42fdd4c53e1c737975db05a2e3a58d01613607565b98909d8e99613275565b7fa574a6b4000000000000000000000000000000000000000000000000000000005f5260045ffd5b5060643560a01c8760a01c14156126cb565b6040517fca11dba7000000000000000000000000000000000000000000000000000000008152816004820152606083602483015e60643560848201525f8060a4838260443560601c5af11561141857506126a8565b7f9cc3dd4a000000000000000000000000000000000000000000000000000000005f5260045ffd5b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60809101126101f557600435906024358060030b81036101f5579060443563ffffffff811681036101f5579060643590565b6004359073ffffffffffffffffffffffffffffffffffffffff821682036101f557565b604051906060820182811067ffffffffffffffff82111761341157604052565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b604051906040820182811067ffffffffffffffff82111761341157604052565b60405190610100820182811067ffffffffffffffff82111761341157604052565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60609101126101f5576134b16133f1565b9060043573ffffffffffffffffffffffffffffffffffffffff811681036101f557825260243573ffffffffffffffffffffffffffffffffffffffff811681036101f55760208301526044356040830152565b8181019160ff1c8183118116918310901516178160801c176135225790565b631293d6fa5f526004601cfd5b906135386133f1565b9180547fffffffffffffffffffffffffffffffff000000000000000000000000000000008160801b16845260801c6020840152600261357561343e565b9160018101548352015460208201526040830152565b7f80000000000000000000000000000000000000000000000000000000000000008114611131575f0390565b6135bf614279565b903373ffffffffffffffffffffffffffffffffffffffff8316036135df57565b7feed0f119000000000000000000000000000000000000000000000000000000005f5260045ffd5b5f929182810784139083900503630554777f01600881901c9060ff165b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60018060ff858701549416011b01161e806101000361373357507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffaab8881839160081b0102937ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffab6326d8560030b131561370a578015613703577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff908101946136249184910160030b630554777f915f81830712910503019060ff8260081c921690565b5050509091565b50505090507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffab6326d91565b945090507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffaab8880919250600193610100039060081b01010291565b91909161379f825f945b63ffffffff821660030b0160030b630554777f915f81830712910503019060ff8260081c921690565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600160ff8486015493161b011916805f03161e806101000361373357507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffaab8980839160081b010293630549cd938560030b1215613849578015613703577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff019361379f908390613776565b5050509050630549cd9391565b908160030b918260ff1d8084011890630549cd938211613cde576138d49293506d08637b66cd638344daef276cd7c56001831602700100000000000000000000000000000000039160fe8116613bdb575b617f008116613ad1575b621f800081166139e8575b6307e0000081166138fe575b505f126138d757614647565b90565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04614647565b6220000081166139cc575b6240000081166139b0575b628000008116613994575b63010000008116613979575b6302000000811661395f575b630400000016613948575b5f6138c8565b69c0d55d4d7152c25fb13990910260801c90613942565b6cde2ee4bc381afa7089aa84bb6690920260801c91613937565b916e0ee7e32d61fdb0a5e622b820f681d00260801c9161392b565b916f03dc5dac7376e20fc8679758d1bcdcfc0260801c9161391f565b916f1f703399d88f6aa83a28b22d4a1f56e30260801c91613914565b916f59b63684b86e9f486ec54727371ba6ca0260801c91613909565b6180008116613ab5575b620100008116613a99575b620200008116613a7d575b620400008116613a61575b620800008116613a45575b621000008116156138bc57916f978bcb9894317807e5fa4498eee7c0fa0260801c916138bc565b916fc4f76b68947482dc198a48a54348c4ed0260801c91613a1e565b916fe08d35706200796273f0b3a981d90cfd0260801c91613a13565b916fefc2bf59df33ecc28125cf78ec4f167f0260801c91613a08565b916ff7bf5211c72f5185f372aeb1d48f937e0260801c916139fd565b916ffbd701c7cbc4c8a6bb81efd232d1e4e70260801c916139f2565b6101008116613bbf575b6102008116613ba3575b6104008116613b87575b6108008116613b6b575b6110008116613b4f575b6120008116613b33575b6140008116156138b157916ffde95287d26d81bea159c37073122c730260801c916138b1565b916ffef41d1a5f2ae3a20676bec6f7f9459a0260801c91613b0d565b916fff79eb706b9a64c6431d76e63531e9290260801c91613b03565b916fffbceceeb791747f10df216f2e53ec570260801c91613af9565b916fffde7444b28145508125d10077ba83b80260801c91613aef565b916fffef3995a5b6a6267530f207142a57640260801c91613ae5565b916ffff79ca7a4d1bf1ee8556cea23cdbaa50260801c91613adb565b60028116613cc2575b60048116613ca6575b60088116613c8a575b60108116613c6e575b60208116613c52575b60408116613c36575b60808116156138a757916ffffbce4b06c196e9247ac87695d53c600260801c916138a7565b916ffffde72350725cc4ea8feece3b5f13c80260801c91613c11565b916ffffef3911b7cff24ba1b3dbb5f8f59740260801c91613c08565b916fffff79c86a8f6150a32d9778eceef97c0260801c91613bff565b916fffffbce42c7be6c998ad6318193c0b180260801c91613bf6565b916fffffde72140b00a354bd3dc828e976c90260801c91613bed565b916fffffef390978c398134b4ff3764fe4100260801c91613be4565b837f073ee172000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b909180600f0b918215613dfa57613d4d5f9160607fffffffffffffffffffffffffffffffffffffffff3fffffffffffffffffffffff82169160591c166002011b90565b9360801b921215613de6575f03827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048111613dd35782029181831015613dd357613d9a92820391614808565b77fffffffffffffffffffffffc0000000000000000000000008111613dc2576138d490614719565b506bffffffffffffffffffffffff90565b5050506bffffffffffffffffffffffff90565b6138d4928204018082061515910401614719565b9250505090565b613e369060607fffffffffffffffffffffffffffffffffffffffff3fffffffffffffffffffffff82169160591c166002011b90565b915f81600f0b125f14613e6257906138d492915f0360801b908082061515910401808203911102614647565b60801b0481019081108015613e7f575b613dc2576138d490614647565b5077ffffffffffffffffffffffffffffffffffffffffffffffff8111613e72565b90613f10613ede6138d495949360607fffffffffffffffffffffffffffffffffffffffff3fffffffffffffffffffffff82169160591c166002011b90565b9160607fffffffffffffffffffffffffffffffffffffffff3fffffffffffffffffffffff82169160591c166002011b90565b80821181830302809101910361406a565b90613f5f613ede6138d495949360607fffffffffffffffffffffffffffffffffffffffff3fffffffffffffffffffffff82169160591c166002011b90565b808211818303028091019103613f9f565b60401b9068010000000000000000038082049106151501908160801c613f9257565b630d88f5265f526004601cfd5b929160801b915f14613fd957613fb89183820390614808565b9080820615159104018060801c613fcc5790565b63b4ef25465f526004601cfd5b828103908183027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8385099382805f0381169485920992048060030260021880820260020302808202600203028082026002030280820260020302808202600203028091026002030293816001858583030495805f03040193119180821001900303021702048060801c613fcc5790565b900391156140ac578061408f70010000000000000000000000000000000092846148d2565b92091515018060801c61409f5790565b6359d2b24a5f526004601cfd5b6140b5916148d2565b8060801c61409f5790565b806f800000000000000000000000000000000160801c156140e8576335278d125f526004601cfd5b600f0b90565b909392919034614104579361410294614383565b565b9173ffffffffffffffffffffffffffffffffffffffff851661412e57936141029434900392614383565b8293916141029561413e94614383565b345f03906142cc565b93929161415261343e565b60208101938196838101907fd0bf0a0be44c3c54cf61da346063acd98e9363a6de122b16472a1947527c5c9e7f5695060fdb9cfea656f872ae4887221aff7dbfefc45eaf753e4e70cdfb5cd19c83015492015494838201937fd0bf0a0be44c3c54cf61da346063acd98e9363a6de122b16472a1947527c5c9e7f5695060fdb9cfea656f872ae4887221aff7dbfefc45eaf753e4e70cdfb5cd19c8601549501549760030b9160030b82125f1461420d57505050039052039052565b9596959194939160030b1315614270577fb09b03866d96933565a9435bfb511c8ac5b2be454285ca331201452704799f737fb09b03866d96933565a9435bfb511c8ac5b2be454285ca331201452704799f72850154940154930303905203039052565b03905250039052565b7f07cc7f5195d862f505d6b095c82f92e00cfc1766f5bca4383c28dc5fca1555fd5c9081156142a457565b7f1834e265000000000000000000000000000000000000000000000000000000005f5260045ffd5b8060a01b7f753dfe4b4dfb3ff6c11bbf6a97f3c094e91c003ce904a55cc5662fbad220f5990190815c928301928315901503908161430a575b50505d565b7f7772acfd7e0f66ebb20a058830296c3dc1301b111d23348e1c961d324223190d0190815c01905d5f80614305565b919061437f6fffffffffffffffffffffffffffffffff9182614378816040602089015198015193602080865183510396015191015103971680936148d2565b16946148d2565b1690565b92809492915f9561440d575b5050816143cc575b5050816143a2575050565b7f7772acfd7e0f66ebb20a058830296c3dc1301b111d23348e1c961d324223190d0190815c01905d565b8260a09492941b017f753dfe4b4dfb3ff6c11bbf6a97f3c094e91c003ce904a55cc5662fbad220f5990190815c938401938415901503019283915d5f614397565b839195508460a01b017f753dfe4b4dfb3ff6c11bbf6a97f3c094e91c003ce904a55cc5662fbad220f5990194855c908101908115901503955d5f61438f565b9060030b90811561445e5760030b0790565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b90925f905f94600f0b9384156145b0575f8513936fffffffffffffffffffffffffffffffff8560011b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01968060ff1d8091011816936bffffffffffffffffffffffff82166bffffffffffffffffffffffff84168111155f1461453657505050926fffffffffffffffffffffffffffffffff9261452c926145339695613f21565b16026140c0565b91565b929897509092916bffffffffffffffffffffffff8316111561458c57509282826145866fffffffffffffffffffffffffffffffff61457e8582986138d49b9a61452c99613f21565b1687026140c0565b98613ea0565b9661452c9250926138d49594916fffffffffffffffffffffffffffffffff94613ea0565b5050505050505f905f90565b01907fffffffffffffffffffffffffffffffff0000000000000000000000000000000082166145e757565b7f6d862c50000000000000000000000000000000000000000000000000000000005f5260045ffd5b61461761345e565b905f82525f60208301525f60408301525f60608301525f60808301525f60a08301525f60c08301525f60e0830152565b6c0100000000000000000000000081106147135770010000000000000000000000000000000081106146ff577401000000000000000000000000000000000000000081106146eb57780100000000000000000000000000000000000000000000000081106146d7577fa10459f4000000000000000000000000000000000000000000000000000000005f5260045ffd5b60621c6bc000000000000000000000001790565b60421c6b8000000000000000000000001790565b60221c6b4000000000000000000000001790565b60021c90565b600381016c01000000000000000000000000811061480057506403ffffffff810170010000000000000000000000000000000081106147ea57506803ffffffffffffffff8101907401000000000000000000000000000000000000000082106147d5576c03ffffffffffffffffffffffff915001780100000000000000000000000000000000000000000000000081106146d7577fa10459f4000000000000000000000000000000000000000000000000000000005f5260045ffd5b5060421c6b8000000000000000000000001790565b905060221c6b4000000000000000000000001790565b905060021c90565b92918082850281861585888404141702156148445704915b82940961482a5750565b6001019150811561483757565b63ae47f7025f526004601cfd5b90507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8386098181108201900382848709835f03841691808511156148375782850480600302600218808202600203028082026002030280820260020302808202600203028082026002030280910260020302936001848483030494805f0304019211900302170291614820565b818102918082840414821517156148eb57505060801c90565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff910981811082019003908160801c1561492c5763ae47f7025f526004601cfd5b60801c9060801b019056fea26469706673582212208f4790146f3838995be055c447caa079afb5b6eeabdaeb4a4501328670e4187e64736f6c63430008210033

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading
0x00000000000014aA86C5d3c41765bb24e11bd701
Net Worth in USD
$6,063,581.07

Net Worth in ETH
2,062.925399

Token Allocations
USDT 28.99%
ETH 19.58%
USDC 17.17%
Others 34.27%
Chain Token Portfolio % Price Amount Value
ETH28.99%$0.9990291,759,240.6251$1,757,532.4
ETH
Ether (ETH)
19.51%$2,939.75402.4108$1,182,986.77
ETH17.11%$0.9997571,037,779.2299$1,037,527.05
ETH15.93%$3,613.55267.2483$965,715.16
ETH8.11%$89,0485.5237$491,871.59
ETH2.38%$2.2963,054.6564$144,395.16
ETH2.04%$0.999354123,782.0331$123,702.07
ETH1.88%$89,2311.2785$114,084.24
ETH1.22%$89,1440.8306$74,042.89
ETH0.75%$0.9993745,441.8226$45,413.19
ETH0.74%$0.99941944,961.9943$44,935.87
ETH0.67%$1.7922,859.7397$40,870.86
ETH0.29%$117,598.6042$17,598.6
ETH0.17%$1.188,535.2771$10,071.63
ETH0.09%$4,941.851.0756$5,315.34
ETH<0.01%$0.9999684.3206$4.32
ARB0.07%$2,940.131.3625$4,005.89
ARB0.06%$0.9997013,506.1236$3,505.08
ARB<0.01%$0.9989082.9534$2.95
Loading...
Loading
Loading...
Loading
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.