Contract

0x0bE5821b66b82676a550fdfF0b686b3e2D85Ac62

Overview

S Balance

Sonic LogoSonic LogoSonic Logo0 S

S Value

-

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To

There are no matching entries

Please try again later

Latest 1 internal transaction

Parent Transaction Hash Block From To
9312192024-12-20 21:26:069 hrs ago1734729966  Contract Creation0 S
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
Position

Compiler Version
v0.8.28+commit.7893614a

Optimization Enabled:
Yes with 100 runs

Other Settings:
cancun EvmVersion
File 1 of 12 : Position.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.26;

import {FullMath} from './FullMath.sol';
import {FixedPoint128} from './FixedPoint128.sol';
import {FixedPoint32} from './FixedPoint32.sol';
import {FixedPoint96} from './FixedPoint96.sol';
import {Oracle} from './Oracle.sol';
import {SafeCast} from './SafeCast.sol';
import {Tick} from './Tick.sol';
import {TickBitmap} from './TickBitmap.sol';

import {PoolStorage, PositionInfo, PositionCheckpoint, RewardInfo} from './PoolStorage.sol';

/// @title Position
/// @notice Positions represent an owner address' liquidity between a lower and upper tick boundary
/// @dev Positions store additional state for tracking fees owed to the position
library Position {
    error NP();
    error FTR();

    /// @notice Returns the hash used to store positions in a mapping
    /// @param owner The address of the position owner
    /// @param index The index of the position
    /// @param tickLower The lower tick boundary of the position
    /// @param tickUpper The upper tick boundary of the position
    /// @return _hash The hash used to store positions in a mapping
    function positionHash(
        address owner,
        uint256 index,
        int24 tickLower,
        int24 tickUpper
    ) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked(owner, index, tickLower, tickUpper));
    }

    /// @notice Returns the Info struct of a position, given an owner and position boundaries
    /// @param self The mapping containing all user positions
    /// @param owner The address of the position owner
    /// @param tickLower The lower tick boundary of the position
    /// @param tickUpper The upper tick boundary of the position
    /// @return position The position info struct of the given owners' position
    function get(
        mapping(bytes32 => PositionInfo) storage self,
        address owner,
        uint256 index,
        int24 tickLower,
        int24 tickUpper
    ) internal view returns (PositionInfo storage position) {
        position = self[positionHash(owner, index, tickLower, tickUpper)];
    }

    /// @notice Credits accumulated fees to a user's position
    /// @param self The individual position to update
    /// @param liquidityDelta The change in pool liquidity as a result of the position update
    /// @param feeGrowthInside0X128 The all-time fee growth in token0, per unit of liquidity, inside the position's tick boundaries
    /// @param feeGrowthInside1X128 The all-time fee growth in token1, per unit of liquidity, inside the position's tick boundaries
    function update(
        PositionInfo storage self,
        int128 liquidityDelta,
        uint256 feeGrowthInside0X128,
        uint256 feeGrowthInside1X128,
        bytes32 _positionHash,
        uint256 period,
        uint160 secondsPerLiquidityPeriodX128
    ) internal {
        PoolStorage.PoolState storage $ = PoolStorage.getStorage();

        uint128 liquidity = self.liquidity;
        uint128 liquidityNext;

        if (liquidityDelta == 0) {
            /// @dev disallow pokes for 0 liquidity positions
            if (liquidity <= 0) revert NP();
            liquidityNext = liquidity;
        } else {
            liquidityNext = liquidityDelta < 0
                ? liquidity - uint128(-liquidityDelta)
                : liquidity + uint128(liquidityDelta);
        }

        /// @dev calculate accumulated fees. overflow in the subtraction of fee growth is expected
        uint128 tokensOwed0;
        uint128 tokensOwed1;
        unchecked {
            tokensOwed0 = uint128(
                FullMath.mulDiv(feeGrowthInside0X128 - self.feeGrowthInside0LastX128, liquidity, FixedPoint128.Q128)
            );
            tokensOwed1 = uint128(
                FullMath.mulDiv(feeGrowthInside1X128 - self.feeGrowthInside1LastX128, liquidity, FixedPoint128.Q128)
            );

            /// @dev update the position
            if (liquidityDelta != 0) self.liquidity = liquidityNext;
            self.feeGrowthInside0LastX128 = feeGrowthInside0X128;
            self.feeGrowthInside1LastX128 = feeGrowthInside1X128;
            if (tokensOwed0 > 0 || tokensOwed1 > 0) {
                /// @dev overflow is acceptable, user must withdraw before they hit type(uint128).max fees
                self.tokensOwed0 += tokensOwed0;
                self.tokensOwed1 += tokensOwed1;
            }
        }

        /// @dev write checkpoint, push a checkpoint if the last period is different, overwrite if not
        uint256 checkpointLength = $.positionCheckpoints[_positionHash].length;
        if (checkpointLength == 0 || $.positionCheckpoints[_positionHash][checkpointLength - 1].period != period) {
            $.positionCheckpoints[_positionHash].push(PositionCheckpoint({period: period, liquidity: liquidityNext}));
        } else {
            $.positionCheckpoints[_positionHash][checkpointLength - 1].liquidity = liquidityNext;
        }

        int160 secondsPerLiquidityPeriodIntX128 = int160(secondsPerLiquidityPeriodX128);

        int160 secondsPerLiquidityPeriodStartX128 = self.periodRewardInfo[period].secondsPerLiquidityPeriodStartX128;

        /// @dev take the difference to make the delta positive or zero
        secondsPerLiquidityPeriodIntX128 -= secondsPerLiquidityPeriodStartX128;

        /// @dev these int should never be negative
        if (secondsPerLiquidityPeriodIntX128 < 0) {
            secondsPerLiquidityPeriodIntX128 = 0;
        }

        /// @dev secondsDebtDeltaX96 is declared differently based on the liquidityDelta
        int256 secondsDebtDeltaX96 = liquidityDelta > 0
        /// @dev case: delta > 0
            ? SafeCast.toInt256(
                /// @dev round upwards
                FullMath.mulDivRoundingUp(
                    uint256(uint128(liquidityDelta)),
                    uint256(uint160(secondsPerLiquidityPeriodIntX128)),
                    FixedPoint32.Q32
                )
            )
        /// @dev case: delta <= 0
            : SafeCast.toInt256(
                /// @dev round downwards 
                FullMath.mulDiv(
                    /// @dev flip liquidityDelta sign 
                    uint256(uint128(-liquidityDelta)),
                    uint256(uint160(secondsPerLiquidityPeriodIntX128)),
                    FixedPoint32.Q32
                )
            );

        self.periodRewardInfo[period].secondsDebtX96 = liquidityDelta > 0
            ? self.periodRewardInfo[period].secondsDebtX96 + secondsDebtDeltaX96 /// @dev can't overflow since each period is way less than uint31
            : self.periodRewardInfo[period].secondsDebtX96 - secondsDebtDeltaX96;
    }

    /// @notice gets the checkpoint directly before the period
    /// @dev returns the 0th index if there's no checkpoints
    /// @param checkpoints the position's checkpoints in storage
    /// @param period the period of interest
    function getCheckpoint(
        PositionCheckpoint[] storage checkpoints,
        uint256 period
    ) internal view returns (uint256 checkpointIndex, uint256 checkpointPeriod) {
        {
            uint256 checkpointLength = checkpoints.length;

            /// @dev return 0 if length is 0
            if (checkpointLength == 0) {
                return (0, 0);
            }

            checkpointPeriod = checkpoints[0].period;

            /// @dev return 0 if first checkpoint happened after period
            if (checkpointPeriod > period) {
                return (0, 0);
            }

            checkpointIndex = checkpointLength - 1;
        }

        checkpointPeriod = checkpoints[checkpointIndex].period;

        /// @dev Find relevant checkpoint if latest checkpoint isn't before period of interest
        if (checkpointPeriod > period) {
            uint256 lower = 0;
            uint256 upper = checkpointIndex;

            while (upper > lower) {
                /// @dev ceil, avoiding overflow
                uint256 center = upper - (upper - lower) / 2;
                checkpointPeriod = checkpoints[center].period;
                if (checkpointPeriod == period) {
                    checkpointIndex = center;
                    return (checkpointIndex, checkpointPeriod);
                } else if (checkpointPeriod < period) {
                    lower = center;
                } else {
                    upper = center - 1;
                }
            }
            checkpointIndex = lower;
            checkpointPeriod = checkpoints[checkpointIndex].period;
        }

        return (checkpointIndex, checkpointPeriod);
    }

    struct PositionPeriodSecondsInRangeParams {
        uint256 period;
        address owner;
        uint256 index;
        int24 tickLower;
        int24 tickUpper;
        uint32 _blockTimestamp;
    }

    /// @notice Get the period seconds in range of a specific position
    /// @return periodSecondsInsideX96 seconds the position was not in range for the period
    function positionPeriodSecondsInRange(
        PositionPeriodSecondsInRangeParams memory params
    ) public view returns (uint256 periodSecondsInsideX96) {
        PoolStorage.PoolState storage $ = PoolStorage.getStorage();

        uint256 currentPeriod = $.lastPeriod;
        if (params.period > currentPeriod) revert FTR();

        bytes32 _positionHash = positionHash(params.owner, params.index, params.tickLower, params.tickUpper);

        uint256 liquidity;
        int160 secondsPerLiquidityPeriodStartX128;

        PositionCheckpoint[] storage checkpoints = $.positionCheckpoints[_positionHash];

        /// @dev get checkpoint at period, or last checkpoint before the period
        (uint256 checkpointIndex, uint256 checkpointPeriod) = getCheckpoint(checkpoints, params.period);

        /// @dev Return 0s if checkpointPeriod is 0
        if (checkpointPeriod == 0) {
            return 0;
        }

        liquidity = checkpoints[checkpointIndex].liquidity;

        secondsPerLiquidityPeriodStartX128 = $
            .positions[_positionHash]
            .periodRewardInfo[params.period]
            .secondsPerLiquidityPeriodStartX128;

        uint160 secondsPerLiquidityInsideX128 = Oracle.periodCumulativesInside(
            uint32(params.period),
            params.tickLower,
            params.tickUpper,
            params._blockTimestamp
        );

        /// @dev underflow will be protected by sanity check
        secondsPerLiquidityInsideX128 = uint160(
            int160(secondsPerLiquidityInsideX128) - secondsPerLiquidityPeriodStartX128
        );

        RewardInfo storage rewardInfo = $.positions[_positionHash].periodRewardInfo[params.period];
        int256 secondsDebtX96 = rewardInfo.secondsDebtX96;

        /// @dev addDelta checks for under and overflows
        periodSecondsInsideX96 = FullMath.mulDiv(liquidity, secondsPerLiquidityInsideX128, FixedPoint32.Q32);

        /// @dev Need to check if secondsDebtX96>periodSecondsInsideX96, since rounding can cause underflows
        if (secondsDebtX96 < 0 || periodSecondsInsideX96 > uint256(secondsDebtX96)) {
            periodSecondsInsideX96 = secondsDebtX96 < 0
                ? periodSecondsInsideX96 + uint256(-secondsDebtX96)
                : periodSecondsInsideX96 - uint256(secondsDebtX96);
        } else {
            periodSecondsInsideX96 = 0;
        }

        /// @dev sanity
        if (periodSecondsInsideX96 > 1 weeks * FixedPoint96.Q96) {
            periodSecondsInsideX96 = 0;
        }
    }

    struct UpdatePositionParams {
        /// @dev the owner of the position
        address owner;
        /// @dev the index of the position
        uint256 index;
        /// @dev the lower tick of the position's tick range
        int24 tickLower;
        /// @dev the upper tick of the position's tick range
        int24 tickUpper;
        /// @dev the amount liquidity changes by
        int128 liquidityDelta;
        /// @dev the current tick, passed to avoid sloads
        int24 tick;
        uint32 _blockTimestamp;
        int24 tickSpacing;
        uint128 maxLiquidityPerTick;
    }

    /// @dev Gets and updates a position with the given liquidity delta
    /// @param params the position details and the change to the position's liquidity to effect
    function _updatePosition(UpdatePositionParams memory params) external returns (PositionInfo storage position) {
        PoolStorage.PoolState storage $ = PoolStorage.getStorage();

        /// @dev calculate the period once, and reuse it
        uint256 period = params._blockTimestamp / 1 weeks;

        /// @dev precompute the position hash
        bytes32 _positionHash = positionHash(params.owner, params.index, params.tickLower, params.tickUpper);

        /// @dev fetch the position using the precomputed _positionHash
        position = $.positions[_positionHash];

        /// @dev SLOAD for gas optimization
        uint256 _feeGrowthGlobal0X128 = $.feeGrowthGlobal0X128;
        uint256 _feeGrowthGlobal1X128 = $.feeGrowthGlobal1X128;

        /// @dev use the tick from `$.slot0` instead of `params.tick` for consistency
        int24 currentTick = $.slot0.tick;

        /// @dev check and update ticks if needed
        bool flippedLower;
        bool flippedUpper;
        if (params.liquidityDelta != 0) {
            /// @dev directly use params._blockTimestamp instead of creating a new `time` variable
            (int56 tickCumulative, uint160 secondsPerLiquidityCumulativeX128) = Oracle.observeSingle(
                $.observations,
                params._blockTimestamp,
                0,
                currentTick, /// @dev use `currentTick` consistently
                $.slot0.observationIndex,
                $.liquidity,
                $.slot0.observationCardinality
            );

            flippedLower = Tick.update(
                $._ticks,
                params.tickLower,
                currentTick, /// @dev use `currentTick` consistently
                params.liquidityDelta,
                _feeGrowthGlobal0X128,
                _feeGrowthGlobal1X128,
                secondsPerLiquidityCumulativeX128,
                tickCumulative,
                params._blockTimestamp,
                false,
                params.maxLiquidityPerTick
            );
            flippedUpper = Tick.update(
                $._ticks,
                params.tickUpper,
                currentTick, /// @dev use `currentTick` consistently
                params.liquidityDelta,
                _feeGrowthGlobal0X128,
                _feeGrowthGlobal1X128,
                secondsPerLiquidityCumulativeX128,
                tickCumulative,
                params._blockTimestamp,
                true,
                params.maxLiquidityPerTick
            );

            /// @dev flip ticks if needed
            if (flippedLower) {
                TickBitmap.flipTick($.tickBitmap, params.tickLower, params.tickSpacing);
            }
            if (flippedUpper) {
                TickBitmap.flipTick($.tickBitmap, params.tickUpper, params.tickSpacing);
            }
        }

        /// @dev calculate the fee growth inside
        (uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128) = Tick.getFeeGrowthInside(
            $._ticks,
            params.tickLower,
            params.tickUpper,
            currentTick, /// @dev use `currentTick` consistently
            _feeGrowthGlobal0X128,
            _feeGrowthGlobal1X128
        );

        /// @dev get the seconds per liquidity period cumulatives
        uint160 secondsPerLiquidityPeriodX128 = Oracle.periodCumulativesInside(
            uint32(period),
            params.tickLower,
            params.tickUpper,
            params._blockTimestamp
        );

        /// @dev initialize position reward info if needed
        if (!position.periodRewardInfo[period].initialized || position.liquidity == 0) {
            initializeSecondsStart(
                position,
                PositionPeriodSecondsInRangeParams({
                    period: period,
                    owner: params.owner,
                    index: params.index,
                    tickLower: params.tickLower,
                    tickUpper: params.tickUpper,
                    _blockTimestamp: params._blockTimestamp
                }),
                secondsPerLiquidityPeriodX128
            );
        }

        /// @dev update the position
        update(
            position,
            params.liquidityDelta,
            feeGrowthInside0X128,
            feeGrowthInside1X128,
            _positionHash,
            period,
            secondsPerLiquidityPeriodX128
        );

        /// @dev clear tick data if liquidity delta is negative and the ticks no longer hold liquidity
        if (params.liquidityDelta < 0) {
            if (flippedLower) {
                Tick.clear($._ticks, params.tickLower, period);
            }
            if (flippedUpper) {
                Tick.clear($._ticks, params.tickUpper, period);
            }
        }
    }

    /// @notice Initializes secondsPerLiquidityPeriodStartX128 for a position
    /// @param position The individual position
    /// @param secondsInRangeParams Parameters used to find the seconds in range
    /// @param secondsPerLiquidityPeriodX128 The seconds in range gained per unit of liquidity, inside the position's tick boundaries for this period
    function initializeSecondsStart(
        PositionInfo storage position,
        PositionPeriodSecondsInRangeParams memory secondsInRangeParams,
        uint160 secondsPerLiquidityPeriodX128
    ) internal {
        /// @dev record initialized
        position.periodRewardInfo[secondsInRangeParams.period].initialized = true;

        /// @dev record owed tokens if liquidity > 0 (means position existed before period change)
        if (position.liquidity > 0) {
            uint256 periodSecondsInsideX96 = positionPeriodSecondsInRange(secondsInRangeParams);

            position.periodRewardInfo[secondsInRangeParams.period].secondsDebtX96 = -int256(periodSecondsInsideX96);
        }

        /// @dev convert uint to int
        /// @dev negative expected sometimes, which is allowed
        int160 secondsPerLiquidityPeriodIntX128 = int160(secondsPerLiquidityPeriodX128);

        position
            .periodRewardInfo[secondsInRangeParams.period]
            .secondsPerLiquidityPeriodStartX128 = secondsPerLiquidityPeriodIntX128;
    }
}

File 2 of 12 : FullMath.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

/// @title Contains 512-bit math functions
/// @notice Facilitates multiplication and division that can have overflow of an intermediate value without any loss of precision
/// @dev Handles "phantom overflow" i.e., allows multiplication and division where an intermediate value overflows 256 bits
library FullMath {
    /// @notice Calculates floor(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
    /// @param a The multiplicand
    /// @param b The multiplier
    /// @param denominator The divisor
    /// @return result The 256-bit result
    /// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv
    function mulDiv(uint256 a, uint256 b, uint256 denominator) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = a * b
            // 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 = prod1 * 2**256 + prod0
            uint256 prod0; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(a, b, not(0))
                prod0 := mul(a, b)
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division
            if (prod1 == 0) {
                require(denominator > 0);
                assembly {
                    result := div(prod0, denominator)
                }
                return result;
            }

            // Make sure the result is less than 2**256.
            // Also prevents denominator == 0
            require(denominator > prod1);

            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////

            // Make division exact by subtracting the remainder from [prod1 prod0]
            // Compute remainder using mulmod
            uint256 remainder;
            assembly {
                remainder := mulmod(a, b, denominator)
            }
            // Subtract 256 bit number from 512 bit number
            assembly {
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, remainder)
            }

            // Factor powers of two out of denominator
            // Compute largest power of two divisor of denominator.
            // Always >= 1.
            uint256 twos = (0 - denominator) & denominator;
            // Divide denominator by power of two
            assembly {
                denominator := div(denominator, twos)
            }

            // Divide [prod1 prod0] by the factors of two
            assembly {
                prod0 := div(prod0, twos)
            }
            // Shift in bits from prod1 into prod0. For this we need
            // to flip `twos` such that it is 2**256 / twos.
            // If twos is zero, then it becomes one
            assembly {
                twos := add(div(sub(0, twos), twos), 1)
            }
            prod0 |= prod1 * twos;

            // Invert denominator mod 2**256
            // Now that denominator is an odd number, it has an inverse
            // modulo 2**256 such that denominator * inv = 1 mod 2**256.
            // Compute the inverse by starting with a seed that is correct
            // correct for four bits. That is, denominator * inv = 1 mod 2**4
            uint256 inv = (3 * denominator) ^ 2;
            // 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 *= 2 - denominator * inv; // inverse mod 2**8
            inv *= 2 - denominator * inv; // inverse mod 2**16
            inv *= 2 - denominator * inv; // inverse mod 2**32
            inv *= 2 - denominator * inv; // inverse mod 2**64
            inv *= 2 - denominator * inv; // inverse mod 2**128
            inv *= 2 - denominator * inv; // inverse mod 2**256

            // Because the division is now exact we can divide by multiplying
            // with the modular inverse of denominator. This will give us the
            // correct result modulo 2**256. Since the precoditions guarantee
            // that the outcome is less than 2**256, this is the final result.
            // We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inv;
            return result;
        }
    }

    /// @notice Calculates ceil(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
    /// @param a The multiplicand
    /// @param b The multiplier
    /// @param denominator The divisor
    /// @return result The 256-bit result
    function mulDivRoundingUp(uint256 a, uint256 b, uint256 denominator) internal pure returns (uint256 result) {
        unchecked {
            result = mulDiv(a, b, denominator);
            if (mulmod(a, b, denominator) > 0) {
                require(result < type(uint256).max);
                result++;
            }
        }
    }
}

File 3 of 12 : FixedPoint128.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.4.0;

/// @title FixedPoint128
/// @notice A library for handling binary fixed point numbers, see https://en.wikipedia.org/wiki/Q_(number_format)
library FixedPoint128 {
    uint256 internal constant Q128 = 0x100000000000000000000000000000000;
}

File 4 of 12 : FixedPoint32.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.4.0;

/// @title FixedPoint32
/// @notice A library for handling binary fixed point numbers, see https://en.wikipedia.org/wiki/Q_(number_format)
library FixedPoint32 {
    uint8 internal constant RESOLUTION = 32;
    uint256 internal constant Q32 = 0x100000000;
}

File 5 of 12 : FixedPoint96.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.4.0;

/// @title FixedPoint96
/// @notice A library for handling binary fixed point numbers, see https://en.wikipedia.org/wiki/Q_(number_format)
/// @dev Used in SqrtPriceMath.sol
library FixedPoint96 {
    uint8 internal constant RESOLUTION = 96;
    uint256 internal constant Q96 = 0x1000000000000000000000000;
}

File 6 of 12 : Oracle.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.26;

import {PoolStorage, Observation, TickInfo, Slot0} from './PoolStorage.sol';

/// @title Oracle
/// @notice Provides price and liquidity data useful for a wide variety of system designs
/// @dev Instances of stored oracle data, "observations", are collected in the oracle array
/// Every pool is initialized with an oracle array length of 1. Anyone can pay the SSTOREs to increase the
/// maximum length of the oracle array. New slots will be added when the array is fully populated.
/// Observations are overwritten when the full length of the oracle array is populated.
/// The most recent observation is available, independent of the length of the oracle array, by passing 0 to observe()
library Oracle {
    error I();
    error OLD();

    /// @notice Transforms a previous observation into a new observation, given the passage of time and the current tick and liquidity values
    /// @dev blockTimestamp _must_ be chronologically equal to or greater than last.blockTimestamp, safe for 0 or 1 overflows
    /// @param last The specified observation to be transformed
    /// @param blockTimestamp The timestamp of the new observation
    /// @param tick The active tick at the time of the new observation
    /// @param liquidity The total in-range liquidity at the time of the new observation
    /// @return Observation The newly populated observation
    function transform(
        Observation memory last,
        uint32 blockTimestamp,
        int24 tick,
        uint128 liquidity
    ) private pure returns (Observation memory) {
        unchecked {
            uint32 delta = blockTimestamp - last.blockTimestamp;
            return
                Observation({
                    blockTimestamp: blockTimestamp,
                    tickCumulative: last.tickCumulative + int56(tick) * int56(uint56(delta)),
                    secondsPerLiquidityCumulativeX128: last.secondsPerLiquidityCumulativeX128 +
                        ((uint160(delta) << 128) / (liquidity > 0 ? liquidity : 1)),
                    initialized: true
                });
        }
    }

    /// @notice Initialize the oracle array by writing the first slot. Called once for the lifecycle of the observations array
    /// @param self The stored oracle array
    /// @param time The time of the oracle initialization, via block.timestamp truncated to uint32
    /// @return cardinality The number of populated elements in the oracle array
    /// @return cardinalityNext The new length of the oracle array, independent of population
    function initialize(
        Observation[65535] storage self,
        uint32 time
    ) internal returns (uint16 cardinality, uint16 cardinalityNext) {
        self[0] = Observation({
            blockTimestamp: time,
            tickCumulative: 0,
            secondsPerLiquidityCumulativeX128: 0,
            initialized: true
        });
        return (1, 1);
    }

    /// @notice Writes an oracle observation to the array
    /// @dev Writable at most once per block. Index represents the most recently written element. cardinality and index must be tracked externally.
    /// If the index is at the end of the allowable array length (according to cardinality), and the next cardinality
    /// is greater than the current one, cardinality may be increased. This restriction is created to preserve ordering.
    /// @param self The stored oracle array
    /// @param index The index of the observation that was most recently written to the observations array
    /// @param blockTimestamp The timestamp of the new observation
    /// @param tick The active tick at the time of the new observation
    /// @param liquidity The total in-range liquidity at the time of the new observation
    /// @param cardinality The number of populated elements in the oracle array
    /// @param cardinalityNext The new length of the oracle array, independent of population
    /// @return indexUpdated The new index of the most recently written element in the oracle array
    /// @return cardinalityUpdated The new cardinality of the oracle array
    function write(
        Observation[65535] storage self,
        uint16 index,
        uint32 blockTimestamp,
        int24 tick,
        uint128 liquidity,
        uint16 cardinality,
        uint16 cardinalityNext
    ) internal returns (uint16 indexUpdated, uint16 cardinalityUpdated) {
        unchecked {
            Observation memory last = self[index];

            /// @dev early return if we've already written an observation this block
            if (last.blockTimestamp == blockTimestamp) return (index, cardinality);

            /// @dev if the conditions are right, we can bump the cardinality
            if (cardinalityNext > cardinality && index == (cardinality - 1)) {
                cardinalityUpdated = cardinalityNext;
            } else {
                cardinalityUpdated = cardinality;
            }

            indexUpdated = (index + 1) % cardinalityUpdated;
            self[indexUpdated] = transform(last, blockTimestamp, tick, liquidity);
        }
    }

    /// @notice Prepares the oracle array to store up to `next` observations
    /// @param self The stored oracle array
    /// @param current The current next cardinality of the oracle array
    /// @param next The proposed next cardinality which will be populated in the oracle array
    /// @return next The next cardinality which will be populated in the oracle array
    function grow(Observation[65535] storage self, uint16 current, uint16 next) internal returns (uint16) {
        unchecked {
            if (current <= 0) revert I();
            /// @dev no-op if the passed next value isn't greater than the current next value
            if (next <= current) return current;
            /// @dev store in each slot to prevent fresh SSTOREs in swaps
            /// @dev this data will not be used because the initialized boolean is still false
            for (uint16 i = current; i < next; i++) self[i].blockTimestamp = 1;
            return next;
        }
    }

    /// @notice comparator for 32-bit timestamps
    /// @dev safe for 0 or 1 overflows, a and b _must_ be chronologically before or equal to time
    /// @param time A timestamp truncated to 32 bits
    /// @param a A comparison timestamp from which to determine the relative position of `time`
    /// @param b From which to determine the relative position of `time`
    /// @return Whether `a` is chronologically <= `b`
    function lte(uint32 time, uint32 a, uint32 b) private pure returns (bool) {
        unchecked {
            /// @dev if there hasn't been overflow, no need to adjust
            if (a <= time && b <= time) return a <= b;

            uint256 aAdjusted = a > time ? a : a + 2 ** 32;
            uint256 bAdjusted = b > time ? b : b + 2 ** 32;

            return aAdjusted <= bAdjusted;
        }
    }

    /// @notice Fetches the observations beforeOrAt and atOrAfter a target, i.e. where [beforeOrAt, atOrAfter] is satisfied.
    /// The result may be the same observation, or adjacent observations.
    /// @dev The answer must be contained in the array, used when the target is located within the stored observation
    /// boundaries: older than the most recent observation and younger, or the same age as, the oldest observation
    /// @param self The stored oracle array
    /// @param time The current block.timestamp
    /// @param target The timestamp at which the reserved observation should be for
    /// @param index The index of the observation that was most recently written to the observations array
    /// @param cardinality The number of populated elements in the oracle array
    /// @return beforeOrAt The observation recorded before, or at, the target
    /// @return atOrAfter The observation recorded at, or after, the target
    function binarySearch(
        Observation[65535] storage self,
        uint32 time,
        uint32 target,
        uint16 index,
        uint16 cardinality
    ) private view returns (Observation memory beforeOrAt, Observation memory atOrAfter) {
        unchecked {
            /// @dev oldest observation
            uint256 l = (index + 1) % cardinality; 
            /// @dev newest observation
            uint256 r = l + cardinality - 1; 
            uint256 i;
            while (true) {
                i = (l + r) / 2;

                beforeOrAt = self[i % cardinality];

                /// @dev we've landed on an uninitialized tick, keep searching higher (more recently)
                if (!beforeOrAt.initialized) {
                    l = i + 1;
                    continue;
                }

                atOrAfter = self[(i + 1) % cardinality];

                bool targetAtOrAfter = lte(time, beforeOrAt.blockTimestamp, target);

                /// @dev check if we've found the answer!
                if (targetAtOrAfter && lte(time, target, atOrAfter.blockTimestamp)) break;

                if (!targetAtOrAfter) r = i - 1;
                else l = i + 1;
            }
        }
    }

    /// @notice Fetches the observations beforeOrAt and atOrAfter a given target, i.e. where [beforeOrAt, atOrAfter] is satisfied
    /// @dev Assumes there is at least 1 initialized observation.
    /// Used by observeSingle() to compute the counterfactual accumulator values as of a given block timestamp.
    /// @param self The stored oracle array
    /// @param time The current block.timestamp
    /// @param target The timestamp at which the reserved observation should be for
    /// @param tick The active tick at the time of the returned or simulated observation
    /// @param index The index of the observation that was most recently written to the observations array
    /// @param liquidity The total pool liquidity at the time of the call
    /// @param cardinality The number of populated elements in the oracle array
    /// @return beforeOrAt The observation which occurred at, or before, the given timestamp
    /// @return atOrAfter The observation which occurred at, or after, the given timestamp
    function getSurroundingObservations(
        Observation[65535] storage self,
        uint32 time,
        uint32 target,
        int24 tick,
        uint16 index,
        uint128 liquidity,
        uint16 cardinality
    ) private view returns (Observation memory beforeOrAt, Observation memory atOrAfter) {
        unchecked {
            /// @dev optimistically set before to the newest observation
            beforeOrAt = self[index];

            /// @dev if the target is chronologically at or after the newest observation, we can early return
            if (lte(time, beforeOrAt.blockTimestamp, target)) {
                if (beforeOrAt.blockTimestamp == target) {
                    /// @dev if newest observation equals target, we're in the same block, so we can ignore atOrAfter
                    return (beforeOrAt, atOrAfter);
                } else {
                    /// @dev otherwise, we need to transform
                    return (beforeOrAt, transform(beforeOrAt, target, tick, liquidity));
                }
            }

            /// @dev now, set before to the oldest observation
            beforeOrAt = self[(index + 1) % cardinality];
            if (!beforeOrAt.initialized) beforeOrAt = self[0];

            /// @dev ensure that the target is chronologically at or after the oldest observation
            if (!lte(time, beforeOrAt.blockTimestamp, target)) revert OLD();

            /// @dev if we've reached this point, we have to binary search
            return binarySearch(self, time, target, index, cardinality);
        }
    }

    /// @dev Reverts if an observation at or before the desired observation timestamp does not exist.
    /// 0 may be passed as `secondsAgo' to return the current cumulative values.
    /// If called with a timestamp falling between two observations, returns the counterfactual accumulator values
    /// at exactly the timestamp between the two observations.
    /// @param self The stored oracle array
    /// @param time The current block timestamp
    /// @param secondsAgo The amount of time to look back, in seconds, at which point to return an observation
    /// @param tick The current tick
    /// @param index The index of the observation that was most recently written to the observations array
    /// @param liquidity The current in-range pool liquidity
    /// @param cardinality The number of populated elements in the oracle array
    /// @return tickCumulative The tick * time elapsed since the pool was first initialized, as of `secondsAgo`
    /// @return secondsPerLiquidityCumulativeX128 The time elapsed / max(1, liquidity) since the pool was first initialized, as of `secondsAgo`
    function observeSingle(
        Observation[65535] storage self,
        uint32 time,
        uint32 secondsAgo,
        int24 tick,
        uint16 index,
        uint128 liquidity,
        uint16 cardinality
    ) internal view returns (int56 tickCumulative, uint160 secondsPerLiquidityCumulativeX128) {
        unchecked {
            if (secondsAgo == 0) {
                Observation memory last = self[index];
                if (last.blockTimestamp != time) last = transform(last, time, tick, liquidity);
                return (last.tickCumulative, last.secondsPerLiquidityCumulativeX128);
            }

            uint32 target = time - secondsAgo;

            (Observation memory beforeOrAt, Observation memory atOrAfter) = getSurroundingObservations(
                self,
                time,
                target,
                tick,
                index,
                liquidity,
                cardinality
            );

            if (target == beforeOrAt.blockTimestamp) {
                /// @dev we're at the left boundary
                return (beforeOrAt.tickCumulative, beforeOrAt.secondsPerLiquidityCumulativeX128);
            } else if (target == atOrAfter.blockTimestamp) {
                /// @dev we're at the right boundary
                return (atOrAfter.tickCumulative, atOrAfter.secondsPerLiquidityCumulativeX128);
            } else {
                /// @dev we're in the middle
                uint32 observationTimeDelta = atOrAfter.blockTimestamp - beforeOrAt.blockTimestamp;
                uint32 targetDelta = target - beforeOrAt.blockTimestamp;
                return (
                    beforeOrAt.tickCumulative +
                        ((atOrAfter.tickCumulative - beforeOrAt.tickCumulative) / int56(uint56(observationTimeDelta))) *
                        int56(uint56(targetDelta)),
                    beforeOrAt.secondsPerLiquidityCumulativeX128 +
                        uint160(
                            (uint256(
                                atOrAfter.secondsPerLiquidityCumulativeX128 -
                                    beforeOrAt.secondsPerLiquidityCumulativeX128
                            ) * targetDelta) / observationTimeDelta
                        )
                );
            }
        }
    }

    /// @notice Returns the accumulator values as of each time seconds ago from the given time in the array of `secondsAgos`
    /// @dev Reverts if `secondsAgos` > oldest observation
    /// @param self The stored oracle array
    /// @param time The current block.timestamp
    /// @param secondsAgos Each amount of time to look back, in seconds, at which point to return an observation
    /// @param tick The current tick
    /// @param index The index of the observation that was most recently written to the observations array
    /// @param liquidity The current in-range pool liquidity
    /// @param cardinality The number of populated elements in the oracle array
    /// @return tickCumulatives The tick * time elapsed since the pool was first initialized, as of each `secondsAgo`
    /// @return secondsPerLiquidityCumulativeX128s The cumulative seconds / max(1, liquidity) since the pool was first initialized, as of each `secondsAgo`
    function observe(
        Observation[65535] storage self,
        uint32 time,
        uint32[] memory secondsAgos,
        int24 tick,
        uint16 index,
        uint128 liquidity,
        uint16 cardinality
    ) internal view returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s) {
        unchecked {
            if (cardinality <= 0) revert I();

            tickCumulatives = new int56[](secondsAgos.length);
            secondsPerLiquidityCumulativeX128s = new uint160[](secondsAgos.length);
            for (uint256 i = 0; i < secondsAgos.length; i++) {
                (tickCumulatives[i], secondsPerLiquidityCumulativeX128s[i]) = observeSingle(
                    self,
                    time,
                    secondsAgos[i],
                    tick,
                    index,
                    liquidity,
                    cardinality
                );
            }
        }
    }

    function newPeriod(
        Observation[65535] storage self,
        uint16 index,
        uint256 period
    ) external returns (uint160 secondsPerLiquidityCumulativeX128) {
        Observation memory last = self[index];
        PoolStorage.PoolState storage $ = PoolStorage.getStorage();

        unchecked {
            uint32 delta = uint32(period) * 1 weeks - 1 - last.blockTimestamp;

            secondsPerLiquidityCumulativeX128 =
                last.secondsPerLiquidityCumulativeX128 +
                ((uint160(delta) << 128) / ($.liquidity > 0 ? $.liquidity : 1));

            self[index] = Observation({
                blockTimestamp: uint32(period) * 1 weeks - 1,
                tickCumulative: last.tickCumulative + int56($.slot0.tick) * int56(uint56(delta)),
                secondsPerLiquidityCumulativeX128: secondsPerLiquidityCumulativeX128,
                initialized: last.initialized
            });
        }
    }

    struct SnapShot {
        int56 tickCumulativeLower;
        int56 tickCumulativeUpper;
        uint160 secondsPerLiquidityOutsideLowerX128;
        uint160 secondsPerLiquidityOutsideUpperX128;
        uint32 secondsOutsideLower;
        uint32 secondsOutsideUpper;
    }

    struct SnapshotCumulativesInsideCache {
        uint32 time;
        int56 tickCumulative;
        uint160 secondsPerLiquidityCumulativeX128;
    }

    /// @notice Returns a snapshot of the tick cumulative, seconds per liquidity and seconds inside a tick range
    /// @dev Snapshots must only be compared to other snapshots, taken over a period for which a position existed.
    /// I.e., snapshots cannot be compared if a position is not held for the entire period between when the first
    /// snapshot is taken and the second snapshot is taken. Boosted data is only valid if it's within the same period
    /// @param tickLower The lower tick of the range
    /// @param tickUpper The upper tick of the range
    /// @return tickCumulativeInside The snapshot of the tick accumulator for the range
    /// @return secondsPerLiquidityInsideX128 The snapshot of seconds per liquidity for the range
    /// @return secondsInside The snapshot of seconds per liquidity for the range
    function snapshotCumulativesInside(
        int24 tickLower,
        int24 tickUpper,
        uint32 _blockTimestamp
    ) external view returns (int56 tickCumulativeInside, uint160 secondsPerLiquidityInsideX128, uint32 secondsInside) {
        PoolStorage.PoolState storage $ = PoolStorage.getStorage();

        TickInfo storage lower = $._ticks[tickLower];
        TickInfo storage upper = $._ticks[tickUpper];

        SnapShot memory snapshot;

        bool initializedLower;
        (
            snapshot.tickCumulativeLower,
            snapshot.secondsPerLiquidityOutsideLowerX128,
            snapshot.secondsOutsideLower,
            initializedLower
        ) = (
            lower.tickCumulativeOutside,
            lower.secondsPerLiquidityOutsideX128,
            lower.secondsOutside,
            lower.initialized
        );
        require(initializedLower);

        bool initializedUpper;
        (
            snapshot.tickCumulativeUpper,
            snapshot.secondsPerLiquidityOutsideUpperX128,
            snapshot.secondsOutsideUpper,
            initializedUpper
        ) = (
            upper.tickCumulativeOutside,
            upper.secondsPerLiquidityOutsideX128,
            upper.secondsOutside,
            upper.initialized
        );
        require(initializedUpper);

        Slot0 memory _slot0 = $.slot0;

        unchecked {
            if (_slot0.tick < tickLower) {
                return (
                    snapshot.tickCumulativeLower - snapshot.tickCumulativeUpper,
                    snapshot.secondsPerLiquidityOutsideLowerX128 - snapshot.secondsPerLiquidityOutsideUpperX128,
                    snapshot.secondsOutsideLower - snapshot.secondsOutsideUpper
                );
            } else if (_slot0.tick < tickUpper) {
                SnapshotCumulativesInsideCache memory cache;
                cache.time = _blockTimestamp;
                (cache.tickCumulative, cache.secondsPerLiquidityCumulativeX128) = observeSingle(
                    $.observations,
                    cache.time,
                    0,
                    _slot0.tick,
                    _slot0.observationIndex,
                    $.liquidity,
                    _slot0.observationCardinality
                );
                return (
                    cache.tickCumulative - snapshot.tickCumulativeLower - snapshot.tickCumulativeUpper,
                    cache.secondsPerLiquidityCumulativeX128 -
                        snapshot.secondsPerLiquidityOutsideLowerX128 -
                        snapshot.secondsPerLiquidityOutsideUpperX128,
                    cache.time - snapshot.secondsOutsideLower - snapshot.secondsOutsideUpper
                );
            } else {
                return (
                    snapshot.tickCumulativeUpper - snapshot.tickCumulativeLower,
                    snapshot.secondsPerLiquidityOutsideUpperX128 - snapshot.secondsPerLiquidityOutsideLowerX128,
                    snapshot.secondsOutsideUpper - snapshot.secondsOutsideLower
                );
            }
        }
    }

    /// @notice Returns the seconds per liquidity and seconds inside a tick range for a period
    /// @dev This does not ensure the range is a valid range
    /// @param period The timestamp of the period
    /// @param tickLower The lower tick of the range
    /// @param tickUpper The upper tick of the range
    /// @return secondsPerLiquidityInsideX128 The snapshot of seconds per liquidity for the range
    function periodCumulativesInside(
        uint32 period,
        int24 tickLower,
        int24 tickUpper,
        uint32 _blockTimestamp
    ) external view returns (uint160 secondsPerLiquidityInsideX128) {
        PoolStorage.PoolState storage $ = PoolStorage.getStorage();

        TickInfo storage lower = $._ticks[tickLower];
        TickInfo storage upper = $._ticks[tickUpper];

        SnapShot memory snapshot;

        {
            int24 startTick = $.periods[period].startTick;
            uint256 previousPeriod = $.periods[period].previousPeriod;

            snapshot.secondsPerLiquidityOutsideLowerX128 = uint160(lower.periodSecondsPerLiquidityOutsideX128[period]);

            if (tickLower <= startTick && snapshot.secondsPerLiquidityOutsideLowerX128 == 0) {
                snapshot.secondsPerLiquidityOutsideLowerX128 = $
                    .periods[previousPeriod]
                    .endSecondsPerLiquidityPeriodX128;
            }

            snapshot.secondsPerLiquidityOutsideUpperX128 = uint160(upper.periodSecondsPerLiquidityOutsideX128[period]);
            if (tickUpper <= startTick && snapshot.secondsPerLiquidityOutsideUpperX128 == 0) {
                snapshot.secondsPerLiquidityOutsideUpperX128 = $
                    .periods[previousPeriod]
                    .endSecondsPerLiquidityPeriodX128;
            }
        }

        int24 lastTick;
        uint256 currentPeriod = $.lastPeriod;
        {
            /// @dev if period is already finalized, use period's last tick, if not, use current tick
            if (currentPeriod > period) {
                lastTick = $.periods[period].lastTick;
            } else {
                lastTick = $.slot0.tick;
            }
        }

        unchecked {
            if (lastTick < tickLower) {
                return snapshot.secondsPerLiquidityOutsideLowerX128 - snapshot.secondsPerLiquidityOutsideUpperX128;
            } else if (lastTick < tickUpper) {
                SnapshotCumulativesInsideCache memory cache;
                /// @dev if period's on-going, observeSingle, if finalized, use endSecondsPerLiquidityPeriodX128
                if (currentPeriod <= period) {
                    cache.time = _blockTimestamp;
                    /// @dev limit to the end of period
                    if (cache.time >= currentPeriod * 1 weeks + 1 weeks) {
                        cache.time = uint32(currentPeriod * 1 weeks + 1 weeks - 1);
                    }

                    Slot0 memory _slot0 = $.slot0;

                    (, cache.secondsPerLiquidityCumulativeX128) = observeSingle(
                        $.observations,
                        cache.time,
                        0,
                        _slot0.tick,
                        _slot0.observationIndex,
                        $.liquidity,
                        _slot0.observationCardinality
                    );
                } else {
                    cache.secondsPerLiquidityCumulativeX128 = $.periods[period].endSecondsPerLiquidityPeriodX128;
                }
                return
                    cache.secondsPerLiquidityCumulativeX128 -
                    snapshot.secondsPerLiquidityOutsideLowerX128 -
                    snapshot.secondsPerLiquidityOutsideUpperX128;
            } else {
                return snapshot.secondsPerLiquidityOutsideUpperX128 - snapshot.secondsPerLiquidityOutsideLowerX128;
            }
        }
    }
}

File 7 of 12 : SafeCast.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title Safe casting methods
/// @notice Contains methods for safely casting between types
library SafeCast {
    /// @notice Cast a uint256 to a uint160, revert on overflow
    /// @param y The uint256 to be downcasted
    /// @return z The downcasted integer, now type uint160
    function toUint160(uint256 y) internal pure returns (uint160 z) {
        require((z = uint160(y)) == y);
    }

    /// @notice Cast a int256 to a int128, revert on overflow or underflow
    /// @param y The int256 to be downcasted
    /// @return z The downcasted integer, now type int128
    function toInt128(int256 y) internal pure returns (int128 z) {
        require((z = int128(y)) == y);
    }

    /// @notice Cast a uint256 to a int256, revert on overflow
    /// @param y The uint256 to be casted
    /// @return z The casted integer, now type int256
    function toInt256(uint256 y) internal pure returns (int256 z) {
        require(y < 2 ** 255);
        z = int256(y);
    }
}

File 8 of 12 : Tick.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.26;

import {SafeCast} from './SafeCast.sol';

import {TickMath} from './TickMath.sol';

import {TickInfo} from './PoolStorage.sol';

/// @title Tick
/// @notice Contains functions for managing tick processes and relevant calculations
library Tick {
    error LO();

    using SafeCast for int256;

    /// @notice Derives max liquidity per tick from given tick spacing
    /// @dev Executed within the pool constructor
    /// @param tickSpacing The amount of required tick separation, realized in multiples of `tickSpacing`
    ///     e.g., a tickSpacing of 3 requires ticks to be initialized every 3rd tick i.e., ..., -6, -3, 0, 3, 6, ...
    /// @return The max liquidity per tick
    function tickSpacingToMaxLiquidityPerTick(int24 tickSpacing) internal pure returns (uint128) {
        unchecked {
            int24 minTick = (TickMath.MIN_TICK / tickSpacing) * tickSpacing;
            int24 maxTick = (TickMath.MAX_TICK / tickSpacing) * tickSpacing;
            uint24 numTicks = uint24((maxTick - minTick) / tickSpacing) + 1;
            return type(uint128).max / numTicks;
        }
    }

    /// @notice Retrieves fee growth data
    /// @param self The mapping containing all tick information for initialized ticks
    /// @param tickLower The lower tick boundary of the position
    /// @param tickUpper The upper tick boundary of the position
    /// @param tickCurrent The current tick
    /// @param feeGrowthGlobal0X128 The all-time global fee growth, per unit of liquidity, in token0
    /// @param feeGrowthGlobal1X128 The all-time global fee growth, per unit of liquidity, in token1
    /// @return feeGrowthInside0X128 The all-time fee growth in token0, per unit of liquidity, inside the position's tick boundaries
    /// @return feeGrowthInside1X128 The all-time fee growth in token1, per unit of liquidity, inside the position's tick boundaries
    function getFeeGrowthInside(
        mapping(int24 => TickInfo) storage self,
        int24 tickLower,
        int24 tickUpper,
        int24 tickCurrent,
        uint256 feeGrowthGlobal0X128,
        uint256 feeGrowthGlobal1X128
    ) internal view returns (uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128) {
        unchecked {
            TickInfo storage lower = self[tickLower];
            TickInfo storage upper = self[tickUpper];

            /// @dev calculate fee growth below
            uint256 feeGrowthBelow0X128;
            uint256 feeGrowthBelow1X128;
            if (tickCurrent >= tickLower) {
                feeGrowthBelow0X128 = lower.feeGrowthOutside0X128;
                feeGrowthBelow1X128 = lower.feeGrowthOutside1X128;
            } else {
                feeGrowthBelow0X128 = feeGrowthGlobal0X128 - lower.feeGrowthOutside0X128;
                feeGrowthBelow1X128 = feeGrowthGlobal1X128 - lower.feeGrowthOutside1X128;
            }

            /// @dev calculate fee growth above
            uint256 feeGrowthAbove0X128;
            uint256 feeGrowthAbove1X128;
            if (tickCurrent < tickUpper) {
                feeGrowthAbove0X128 = upper.feeGrowthOutside0X128;
                feeGrowthAbove1X128 = upper.feeGrowthOutside1X128;
            } else {
                feeGrowthAbove0X128 = feeGrowthGlobal0X128 - upper.feeGrowthOutside0X128;
                feeGrowthAbove1X128 = feeGrowthGlobal1X128 - upper.feeGrowthOutside1X128;
            }

            feeGrowthInside0X128 = feeGrowthGlobal0X128 - feeGrowthBelow0X128 - feeGrowthAbove0X128;
            feeGrowthInside1X128 = feeGrowthGlobal1X128 - feeGrowthBelow1X128 - feeGrowthAbove1X128;
        }
    }

    /// @notice Updates a tick and returns true if the tick was flipped from initialized to uninitialized, or vice versa
    /// @param self The mapping containing all tick information for initialized ticks
    /// @param tick The tick that will be updated
    /// @param tickCurrent The current tick
    /// @param liquidityDelta A new amount of liquidity to be added (subtracted) when tick is crossed from left to right (right to left)
    /// @param feeGrowthGlobal0X128 The all-time global fee growth, per unit of liquidity, in token0
    /// @param feeGrowthGlobal1X128 The all-time global fee growth, per unit of liquidity, in token1
    /// @param secondsPerLiquidityCumulativeX128 The all-time seconds per max(1, liquidity) of the pool
    /// @param tickCumulative The tick * time elapsed since the pool was first initialized
    /// @param time The current block timestamp cast to a uint32
    /// @param upper true for updating a position's upper tick, or false for updating a position's lower tick
    /// @param maxLiquidity The maximum liquidity allocation for a single tick
    /// @return flipped Whether the tick was flipped from initialized to uninitialized, or vice versa
    function update(
        mapping(int24 => TickInfo) storage self,
        int24 tick,
        int24 tickCurrent,
        int128 liquidityDelta,
        uint256 feeGrowthGlobal0X128,
        uint256 feeGrowthGlobal1X128,
        uint160 secondsPerLiquidityCumulativeX128,
        int56 tickCumulative,
        uint32 time,
        bool upper,
        uint128 maxLiquidity
    ) internal returns (bool flipped) {
        TickInfo storage info = self[tick];

        uint128 liquidityGrossBefore = info.liquidityGross;
        uint128 liquidityGrossAfter = liquidityDelta < 0
            ? liquidityGrossBefore - uint128(-liquidityDelta)
            : liquidityGrossBefore + uint128(liquidityDelta);

        if (liquidityGrossAfter > maxLiquidity) revert LO();

        flipped = (liquidityGrossAfter == 0) != (liquidityGrossBefore == 0);

        if (liquidityGrossBefore == 0) {
            /// @dev by convention, we assume that all growth before a tick was initialized happened _below_ the tick
            if (tick <= tickCurrent) {
                info.feeGrowthOutside0X128 = feeGrowthGlobal0X128;
                info.feeGrowthOutside1X128 = feeGrowthGlobal1X128;
                info.secondsPerLiquidityOutsideX128 = secondsPerLiquidityCumulativeX128;
                info.tickCumulativeOutside = tickCumulative;
                info.secondsOutside = time;
            }
            info.initialized = true;
        }

        info.liquidityGross = liquidityGrossAfter;

        /// @dev when the lower (upper) tick is crossed left to right (right to left), liquidity must be added (removed)
        info.liquidityNet = upper ? info.liquidityNet - liquidityDelta : info.liquidityNet + liquidityDelta;
    }

    /// @notice Clears tick data
    /// @param self The mapping containing all initialized tick information for initialized ticks
    /// @param tick The tick that will be cleared
    /// @param period The period the tick was cleared on
    function clear(mapping(int24 => TickInfo) storage self, int24 tick, uint256 period) internal {
        delete self[tick].periodSecondsPerLiquidityOutsideX128[period];
        delete self[tick];
    }

    /// @notice Transitions to next tick as needed by price movement
    /// @param self The mapping containing all tick information for initialized ticks
    /// @param tick The destination tick of the transition
    /// @param feeGrowthGlobal0X128 The all-time global fee growth, per unit of liquidity, in token0
    /// @param feeGrowthGlobal1X128 The all-time global fee growth, per unit of liquidity, in token1
    /// @param secondsPerLiquidityCumulativeX128 The current seconds per liquidity
    /// @param tickCumulative The tick * time elapsed since the pool was first initialized
    /// @param time The current block.timestamp
    /// @return liquidityNet The amount of liquidity added (subtracted) when tick is crossed from left to right (right to left)
    function cross(
        mapping(int24 => TickInfo) storage self,
        int24 tick,
        uint256 feeGrowthGlobal0X128,
        uint256 feeGrowthGlobal1X128,
        uint160 secondsPerLiquidityCumulativeX128,
        int56 tickCumulative,
        uint32 time,
        uint256 endSecondsPerLiquidityPeriodX128,
        int24 periodStartTick
    ) internal returns (int128 liquidityNet) {
        unchecked {
            TickInfo storage info = self[tick];
            uint256 period = time / 1 weeks;

            info.feeGrowthOutside0X128 = feeGrowthGlobal0X128 - info.feeGrowthOutside0X128;
            info.feeGrowthOutside1X128 = feeGrowthGlobal1X128 - info.feeGrowthOutside1X128;
            info.secondsPerLiquidityOutsideX128 =
                secondsPerLiquidityCumulativeX128 -
                info.secondsPerLiquidityOutsideX128;
            info.tickCumulativeOutside = tickCumulative - info.tickCumulativeOutside;
            info.secondsOutside = time - info.secondsOutside;
            liquidityNet = info.liquidityNet;

            uint256 periodSecondsPerLiquidityOutsideX128;
            uint256 periodSecondsPerLiquidityOutsideBeforeX128 = info.periodSecondsPerLiquidityOutsideX128[period];
            if (tick <= periodStartTick && periodSecondsPerLiquidityOutsideBeforeX128 == 0) {
                periodSecondsPerLiquidityOutsideX128 =
                    secondsPerLiquidityCumulativeX128 -
                    /* periodSecondsPerLiquidityOutsideBeforeX128 - */
                    endSecondsPerLiquidityPeriodX128;
            } else {
                periodSecondsPerLiquidityOutsideX128 =
                    secondsPerLiquidityCumulativeX128 -
                    periodSecondsPerLiquidityOutsideBeforeX128;
            }
            info.periodSecondsPerLiquidityOutsideX128[period] = periodSecondsPerLiquidityOutsideX128;
        }
    }
}

File 9 of 12 : TickBitmap.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.26;

import {BitMath} from './BitMath.sol';

/// @title Packed tick initialized state library
/// @notice Stores a packed mapping of tick index to its initialized state
/// @dev The mapping uses int16 for keys since ticks are represented as int24 and there are 256 (2^8) values per word.
library TickBitmap {
    /// @notice Computes the position in the mapping where the initialized bit for a tick lives
    /// @param tick The tick for which to compute the position
    /// @return wordPos The key in the mapping containing the word in which the bit is stored
    /// @return bitPos The bit position in the word where the flag is stored
    function position(int24 tick) private pure returns (int16 wordPos, uint8 bitPos) {
        unchecked {
            wordPos = int16(tick >> 8);
            bitPos = uint8(int8(tick % 256));
        }
    }

    /// @notice Flips the initialized state for a given tick from false to true, or vice versa
    /// @param self The mapping in which to flip the tick
    /// @param tick The tick to flip
    /// @param tickSpacing The spacing between usable ticks
    function flipTick(mapping(int16 => uint256) storage self, int24 tick, int24 tickSpacing) internal {
        unchecked {
            /// @dev ensure that the tick is spaced
            require(tick % tickSpacing == 0); 
            (int16 wordPos, uint8 bitPos) = position(tick / tickSpacing);
            uint256 mask = 1 << bitPos;
            self[wordPos] ^= mask;
        }
    }

    /// @notice Returns the next initialized tick contained in the same word (or adjacent word) as the tick that is either
    /// to the left (less than or equal to) or right (greater than) of the given tick
    /// @param self The mapping in which to compute the next initialized tick
    /// @param tick The starting tick
    /// @param tickSpacing The spacing between usable ticks
    /// @param lte Whether to search for the next initialized tick to the left (less than or equal to the starting tick)
    /// @return next The next initialized or uninitialized tick up to 256 ticks away from the current tick
    /// @return initialized Whether the next tick is initialized, as the function only searches within up to 256 ticks
    function nextInitializedTickWithinOneWord(
        mapping(int16 => uint256) storage self,
        int24 tick,
        int24 tickSpacing,
        bool lte
    ) internal view returns (int24 next, bool initialized) {
        unchecked {
            int24 compressed = tick / tickSpacing;
            if (tick < 0 && tick % tickSpacing != 0) compressed--; /// @dev round towards negative infinity

            if (lte) {
                (int16 wordPos, uint8 bitPos) = position(compressed);
                /// @dev all the 1s at or to the right of the current bitPos
                uint256 mask = (1 << bitPos) - 1 + (1 << bitPos);
                uint256 masked = self[wordPos] & mask;

                /// @dev if there are no initialized ticks to the right of or at the current tick, return rightmost in the word
                initialized = masked != 0;
                /// @dev overflow/underflow is possible, but prevented externally by limiting both tickSpacing and tick
                next = initialized
                    ? (compressed - int24(uint24(bitPos - BitMath.mostSignificantBit(masked)))) * tickSpacing
                    : (compressed - int24(uint24(bitPos))) * tickSpacing;
            } else {
                /// @dev start from the word of the next tick, since the current tick state doesn't matter
                (int16 wordPos, uint8 bitPos) = position(compressed + 1);
                /// @dev all the 1s at or to the left of the bitPos
                uint256 mask = ~((1 << bitPos) - 1);
                uint256 masked = self[wordPos] & mask;

                /// @dev if there are no initialized ticks to the left of the current tick, return leftmost in the word
                initialized = masked != 0;
                /// @dev overflow/underflow is possible, but prevented externally by limiting both tickSpacing and tick
                next = initialized
                    ? (compressed + 1 + int24(uint24(BitMath.leastSignificantBit(masked) - bitPos))) * tickSpacing
                    : (compressed + 1 + int24(uint24(type(uint8).max - bitPos))) * tickSpacing;
            }
        }
    }
}

File 10 of 12 : PoolStorage.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.26;

struct Slot0 {
    /// @dev the current price
    uint160 sqrtPriceX96;
    /// @dev the current tick
    int24 tick;
    /// @dev the most-recently updated index of the observations array
    uint16 observationIndex;
    /// @dev the current maximum number of observations that are being stored
    uint16 observationCardinality;
    /// @dev the next maximum number of observations to store, triggered in observations.write
    uint16 observationCardinalityNext;
    /// @dev the current protocol fee as a percentage of the swap fee taken on withdrawal
    /// @dev represented as an integer denominator (1/x)%
    uint8 feeProtocol;
    /// @dev whether the pool is locked
    bool unlocked;
}

struct Observation {
    /// @dev the block timestamp of the observation
    uint32 blockTimestamp;
    /// @dev the tick accumulator, i.e. tick * time elapsed since the pool was first initialized
    int56 tickCumulative;
    /// @dev the seconds per liquidity, i.e. seconds elapsed / max(1, liquidity) since the pool was first initialized
    uint160 secondsPerLiquidityCumulativeX128;
    /// @dev whether or not the observation is initialized
    bool initialized;
}

struct RewardInfo {
    /// @dev used to account for changes in the deposit amount
    int256 secondsDebtX96;
    /// @dev used to check if starting seconds have already been written
    bool initialized;
    /// @dev used to account for changes in secondsPerLiquidity
    int160 secondsPerLiquidityPeriodStartX128;
}

/// @dev info stored for each user's position
struct PositionInfo {
    /// @dev the amount of liquidity owned by this position
    uint128 liquidity;
    /// @dev fee growth per unit of liquidity as of the last update to liquidity or fees owed
    uint256 feeGrowthInside0LastX128;
    uint256 feeGrowthInside1LastX128;
    /// @dev the fees owed to the position owner in token0/token1
    uint128 tokensOwed0;
    uint128 tokensOwed1;
    mapping(uint256 => RewardInfo) periodRewardInfo;
}

/// @dev info stored for each initialized individual tick
struct TickInfo {
    /// @dev the total position liquidity that references this tick
    uint128 liquidityGross;
    /// @dev amount of net liquidity added (subtracted) when tick is crossed from left to right (right to left),
    int128 liquidityNet;
    /// @dev fee growth per unit of liquidity on the _other_ side of this tick (relative to the current tick)
    /// @dev only has relative meaning, not absolute — the value depends on when the tick is initialized
    uint256 feeGrowthOutside0X128;
    uint256 feeGrowthOutside1X128;
    /// @dev the cumulative tick value on the other side of the tick
    int56 tickCumulativeOutside;
    /// @dev the seconds per unit of liquidity on the _other_ side of this tick (relative to the current tick)
    /// @dev only has relative meaning, not absolute — the value depends on when the tick is initialized
    uint160 secondsPerLiquidityOutsideX128;
    /// @dev the seconds spent on the other side of the tick (relative to the current tick)
    /// @dev only has relative meaning, not absolute — the value depends on when the tick is initialized
    uint32 secondsOutside;
    /// @dev true iff the tick is initialized, i.e. the value is exactly equivalent to the expression liquidityGross != 0
    /// @dev these 8 bits are set to prevent fresh sstores when crossing newly initialized ticks
    bool initialized;
    /// @dev secondsPerLiquidityOutsideX128 separated into periods, placed here to preserve struct slots
    mapping(uint256 => uint256) periodSecondsPerLiquidityOutsideX128;
}

/// @dev info stored for each period
struct PeriodInfo {
    uint32 previousPeriod;
    int24 startTick;
    int24 lastTick;
    uint160 endSecondsPerLiquidityPeriodX128;
}

/// @dev accumulated protocol fees in token0/token1 units
struct ProtocolFees {
    uint128 token0;
    uint128 token1;
}

/// @dev Position period and liquidity
struct PositionCheckpoint {
    uint256 period;
    uint256 liquidity;
}

library PoolStorage {
    /// @dev keccak256(abi.encode(uint256(keccak256("pool.storage")) - 1)) & ~bytes32(uint256(0xff));
    bytes32 public constant POOL_STORAGE_LOCATION = 0xf047b0c59244a0faf8e48cb6b6fde518e6717176152b6dd953628cd9dccb2800;

    /// @custom꞉storage‑location erc7201꞉pool.storage
    struct PoolState {
        Slot0 slot0;
        uint24 fee;
        uint256 feeGrowthGlobal0X128;
        uint256 feeGrowthGlobal1X128;
        ProtocolFees protocolFees;
        uint128 liquidity;
        mapping(int24 => TickInfo) _ticks;
        mapping(int16 => uint256) tickBitmap;
        mapping(bytes32 => PositionInfo) positions;
        Observation[65535] observations;
        mapping(uint256 => PeriodInfo) periods;
        uint256 lastPeriod;
        mapping(bytes32 => PositionCheckpoint[]) positionCheckpoints;
        bool initialized;
        address nfpManager;
    }

    /// @dev Return state storage struct for reading and writing
    function getStorage() internal pure returns (PoolState storage $) {
        assembly {
            $.slot := POOL_STORAGE_LOCATION
        }
    }
}

File 11 of 12 : TickMath.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.26;

/// @title Math library for computing sqrt prices from ticks and vice versa
/// @notice Computes sqrt price for ticks of size 1.0001, i.e. sqrt(1.0001^tick) as fixed point Q64.96 numbers. Supports
/// prices between 2**-128 and 2**128
library TickMath {
    error T();
    error R();

    /// @dev The minimum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**-128
    int24 internal constant MIN_TICK = -887272;
    /// @dev The maximum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**128
    int24 internal constant MAX_TICK = -MIN_TICK;

    /// @dev The minimum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MIN_TICK)
    uint160 internal constant MIN_SQRT_RATIO = 4295128739;
    /// @dev The maximum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MAX_TICK)
    uint160 internal constant MAX_SQRT_RATIO = 1461446703485210103287273052203988822378723970342;

    /// @notice Calculates sqrt(1.0001^tick) * 2^96
    /// @dev Throws if |tick| > max tick
    /// @param tick The input tick for the above formula
    /// @return sqrtPriceX96 A Fixed point Q64.96 number representing the sqrt of the ratio of the two assets (token1/token0)
    /// at the given tick
    function getSqrtRatioAtTick(int24 tick) internal pure returns (uint160 sqrtPriceX96) {
        unchecked {
            uint256 absTick = tick < 0 ? uint256(-int256(tick)) : uint256(int256(tick));
            if (absTick > uint256(int256(MAX_TICK))) revert T();

            uint256 ratio = absTick & 0x1 != 0
                ? 0xfffcb933bd6fad37aa2d162d1a594001
                : 0x100000000000000000000000000000000;
            if (absTick & 0x2 != 0) ratio = (ratio * 0xfff97272373d413259a46990580e213a) >> 128;
            if (absTick & 0x4 != 0) ratio = (ratio * 0xfff2e50f5f656932ef12357cf3c7fdcc) >> 128;
            if (absTick & 0x8 != 0) ratio = (ratio * 0xffe5caca7e10e4e61c3624eaa0941cd0) >> 128;
            if (absTick & 0x10 != 0) ratio = (ratio * 0xffcb9843d60f6159c9db58835c926644) >> 128;
            if (absTick & 0x20 != 0) ratio = (ratio * 0xff973b41fa98c081472e6896dfb254c0) >> 128;
            if (absTick & 0x40 != 0) ratio = (ratio * 0xff2ea16466c96a3843ec78b326b52861) >> 128;
            if (absTick & 0x80 != 0) ratio = (ratio * 0xfe5dee046a99a2a811c461f1969c3053) >> 128;
            if (absTick & 0x100 != 0) ratio = (ratio * 0xfcbe86c7900a88aedcffc83b479aa3a4) >> 128;
            if (absTick & 0x200 != 0) ratio = (ratio * 0xf987a7253ac413176f2b074cf7815e54) >> 128;
            if (absTick & 0x400 != 0) ratio = (ratio * 0xf3392b0822b70005940c7a398e4b70f3) >> 128;
            if (absTick & 0x800 != 0) ratio = (ratio * 0xe7159475a2c29b7443b29c7fa6e889d9) >> 128;
            if (absTick & 0x1000 != 0) ratio = (ratio * 0xd097f3bdfd2022b8845ad8f792aa5825) >> 128;
            if (absTick & 0x2000 != 0) ratio = (ratio * 0xa9f746462d870fdf8a65dc1f90e061e5) >> 128;
            if (absTick & 0x4000 != 0) ratio = (ratio * 0x70d869a156d2a1b890bb3df62baf32f7) >> 128;
            if (absTick & 0x8000 != 0) ratio = (ratio * 0x31be135f97d08fd981231505542fcfa6) >> 128;
            if (absTick & 0x10000 != 0) ratio = (ratio * 0x9aa508b5b7a84e1c677de54f3e99bc9) >> 128;
            if (absTick & 0x20000 != 0) ratio = (ratio * 0x5d6af8dedb81196699c329225ee604) >> 128;
            if (absTick & 0x40000 != 0) ratio = (ratio * 0x2216e584f5fa1ea926041bedfe98) >> 128;
            if (absTick & 0x80000 != 0) ratio = (ratio * 0x48a170391f7dc42444e8fa2) >> 128;

            if (tick > 0) ratio = type(uint256).max / ratio;

            /// @dev this divides by 1<<32 rounding up to go from a Q128.128 to a Q128.96.
            /// @dev we then downcast because we know the result always fits within 160 bits due to our tick input constraint
            /// @dev we round up in the division so getTickAtSqrtRatio of the output price is always consistent
            sqrtPriceX96 = uint160((ratio >> 32) + (ratio % (1 << 32) == 0 ? 0 : 1));
        }
    }

    /// @notice Calculates the greatest tick value such that getRatioAtTick(tick) <= ratio
    /// @dev Throws in case sqrtPriceX96 < MIN_SQRT_RATIO, as MIN_SQRT_RATIO is the lowest value getRatioAtTick may
    /// ever return.
    /// @param sqrtPriceX96 The sqrt ratio for which to compute the tick as a Q64.96
    /// @return tick The greatest tick for which the ratio is less than or equal to the input ratio
    function getTickAtSqrtRatio(uint160 sqrtPriceX96) internal pure returns (int24 tick) {
        unchecked {
            /// @dev second inequality must be < because the price can never reach the price at the max tick
            if (!(sqrtPriceX96 >= MIN_SQRT_RATIO && sqrtPriceX96 < MAX_SQRT_RATIO)) revert R();
            uint256 ratio = uint256(sqrtPriceX96) << 32;

            uint256 r = ratio;
            uint256 msb = 0;

            assembly {
                let f := shl(7, gt(r, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))
                msb := or(msb, f)
                r := shr(f, r)
            }
            assembly {
                let f := shl(6, gt(r, 0xFFFFFFFFFFFFFFFF))
                msb := or(msb, f)
                r := shr(f, r)
            }
            assembly {
                let f := shl(5, gt(r, 0xFFFFFFFF))
                msb := or(msb, f)
                r := shr(f, r)
            }
            assembly {
                let f := shl(4, gt(r, 0xFFFF))
                msb := or(msb, f)
                r := shr(f, r)
            }
            assembly {
                let f := shl(3, gt(r, 0xFF))
                msb := or(msb, f)
                r := shr(f, r)
            }
            assembly {
                let f := shl(2, gt(r, 0xF))
                msb := or(msb, f)
                r := shr(f, r)
            }
            assembly {
                let f := shl(1, gt(r, 0x3))
                msb := or(msb, f)
                r := shr(f, r)
            }
            assembly {
                let f := gt(r, 0x1)
                msb := or(msb, f)
            }

            if (msb >= 128) r = ratio >> (msb - 127);
            else r = ratio << (127 - msb);

            int256 log_2 = (int256(msb) - 128) << 64;

            assembly {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(63, f))
                r := shr(f, r)
            }
            assembly {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(62, f))
                r := shr(f, r)
            }
            assembly {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(61, f))
                r := shr(f, r)
            }
            assembly {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(60, f))
                r := shr(f, r)
            }
            assembly {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(59, f))
                r := shr(f, r)
            }
            assembly {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(58, f))
                r := shr(f, r)
            }
            assembly {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(57, f))
                r := shr(f, r)
            }
            assembly {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(56, f))
                r := shr(f, r)
            }
            assembly {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(55, f))
                r := shr(f, r)
            }
            assembly {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(54, f))
                r := shr(f, r)
            }
            assembly {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(53, f))
                r := shr(f, r)
            }
            assembly {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(52, f))
                r := shr(f, r)
            }
            assembly {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(51, f))
                r := shr(f, r)
            }
            assembly {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(50, f))
            }

            int256 log_sqrt10001 = log_2 * 255738958999603826347141; /// @dev 128.128 number

            int24 tickLow = int24((log_sqrt10001 - 3402992956809132418596140100660247210) >> 128);
            int24 tickHi = int24((log_sqrt10001 + 291339464771989622907027621153398088495) >> 128);

            tick = tickLow == tickHi
                ? tickLow
                : getSqrtRatioAtTick(tickHi) <= sqrtPriceX96
                    ? tickHi
                    : tickLow;
        }
    }
}

File 12 of 12 : BitMath.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.26;

/// @title BitMath
/// @dev This library provides functionality for computing bit properties of an unsigned integer
library BitMath {
    /// @notice Returns the index of the most significant bit of the number,
    ///     where the least significant bit is at index 0 and the most significant bit is at index 255
    /// @dev The function satisfies the property:
    ///     x >= 2**mostSignificantBit(x) and x < 2**(mostSignificantBit(x)+1)
    /// @param x the value for which to compute the most significant bit, must be greater than 0
    /// @return r the index of the most significant bit
    function mostSignificantBit(uint256 x) internal pure returns (uint8 r) {
        require(x > 0);

        unchecked {
            if (x >= 0x100000000000000000000000000000000) {
                x >>= 128;
                r += 128;
            }
            if (x >= 0x10000000000000000) {
                x >>= 64;
                r += 64;
            }
            if (x >= 0x100000000) {
                x >>= 32;
                r += 32;
            }
            if (x >= 0x10000) {
                x >>= 16;
                r += 16;
            }
            if (x >= 0x100) {
                x >>= 8;
                r += 8;
            }
            if (x >= 0x10) {
                x >>= 4;
                r += 4;
            }
            if (x >= 0x4) {
                x >>= 2;
                r += 2;
            }
            if (x >= 0x2) r += 1;
        }
    }

    /// @notice Returns the index of the least significant bit of the number,
    ///     where the least significant bit is at index 0 and the most significant bit is at index 255
    /// @dev The function satisfies the property:
    ///     (x & 2**leastSignificantBit(x)) != 0 and (x & (2**(leastSignificantBit(x)) - 1)) == 0)
    /// @param x the value for which to compute the least significant bit, must be greater than 0
    /// @return r the index of the least significant bit
    function leastSignificantBit(uint256 x) internal pure returns (uint8 r) {
        require(x > 0);

        unchecked {
            r = 255;
            if (x & type(uint128).max > 0) {
                r -= 128;
            } else {
                x >>= 128;
            }
            if (x & type(uint64).max > 0) {
                r -= 64;
            } else {
                x >>= 64;
            }
            if (x & type(uint32).max > 0) {
                r -= 32;
            } else {
                x >>= 32;
            }
            if (x & type(uint16).max > 0) {
                r -= 16;
            } else {
                x >>= 16;
            }
            if (x & type(uint8).max > 0) {
                r -= 8;
            } else {
                x >>= 8;
            }
            if (x & 0xf > 0) {
                r -= 4;
            } else {
                x >>= 4;
            }
            if (x & 0x3 > 0) {
                r -= 2;
            } else {
                x >>= 2;
            }
            if (x & 0x1 > 0) r -= 1;
        }
    }
}

Settings
{
  "remappings": [
    "@openzeppelin-contracts-upgradeable-5.1.0/=dependencies/@openzeppelin-contracts-upgradeable-5.1.0/",
    "@openzeppelin/contracts/=dependencies/@openzeppelin-contracts-5.1.0/",
    "forge-std/=dependencies/forge-std-1.9.4/src/",
    "permit2/=lib/permit2/",
    "@openzeppelin-3.4.2/=node_modules/@openzeppelin-3.4.2/",
    "@openzeppelin-contracts-5.1.0/=dependencies/@openzeppelin-contracts-5.1.0/",
    "@uniswap/=node_modules/@uniswap/",
    "base64-sol/=node_modules/base64-sol/",
    "eth-gas-reporter/=node_modules/eth-gas-reporter/",
    "forge-std-1.9.4/=dependencies/forge-std-1.9.4/src/",
    "hardhat/=node_modules/hardhat/",
    "solmate/=node_modules/solmate/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 100
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "cancun",
  "viaIR": true,
  "libraries": {
    "contracts/CL/core/libraries/Oracle.sol": {
      "Oracle": "0x739CB2b30fC47c19262aB023eaE966A15c2F9A28"
    },
    "contracts/CL/core/libraries/Position.sol": {
      "Position": "0x0bE5821b66b82676a550fdfF0b686b3e2D85Ac62"
    },
    "contracts/CL/core/libraries/ProtocolActions.sol": {
      "ProtocolActions": "0x2D26aa011df96ab6Ff66f7cB8EF1ed13Afb8A43B"
    }
  }
}

Contract Security Audit

Contract ABI

[{"inputs":[],"name":"FTR","type":"error"},{"inputs":[],"name":"LO","type":"error"},{"inputs":[],"name":"NP","type":"error"},{"inputs":[],"name":"OLD","type":"error"},{"inputs":[{"components":[{"internalType":"uint256","name":"period","type":"uint256"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"int24","name":"tickLower","type":"int24"},{"internalType":"int24","name":"tickUpper","type":"int24"},{"internalType":"uint32","name":"_blockTimestamp","type":"uint32"}],"internalType":"struct Position.PositionPeriodSecondsInRangeParams","name":"params","type":"tuple"}],"name":"positionPeriodSecondsInRange","outputs":[{"internalType":"uint256","name":"periodSecondsInsideX96","type":"uint256"}],"stateMutability":"view","type":"function"}]

60808060405234601b576115a290816100208239308160350152f35b5f80fdfe60a0806040526004361015610012575f80fd5b5f3560e01c908163d2e6311b14610cbe575063fc32287914610032575f80fd5b307f0000000000000000000000000000000000000000000000000000000000000000146105b1576101203660031901126105b157604051610120810181811067ffffffffffffffff82111761061e576040526004356001600160a01b03811681036105b157815260243560208201526044358060020b81036105b15760408201526100bb610d9a565b606082015260843580600f0b81036105b157608082015260a4358060020b81036105b15760a082015260c4359063ffffffff8216918281036105b15760c082015260e4358060020b81036105b15760e0820152610104356001600160801b03811681036105b15761010082015261015160018060a01b038251166020830151604084015160020b90606085015160020b92611076565b91825f525f51602061150d5f395f51905f5260205260405f20917ff047b0c59244a0faf8e48cb6b6fde518e6717176152b6dd953628cd9dccb2802547ff047b0c59244a0faf8e48cb6b6fde518e6717176152b6dd953628cd9dccb280354907ff047b0c59244a0faf8e48cb6b6fde518e6717176152b6dd953628cd9dccb2800546080525f905f926080850151600f0b6108d6575b604085015160020b9763ffffffff606087015160020b6102058b6113c0565b9061020f816113c0565b9b60805160a01c60020b12155f146108c25760026001830154920154905b60805160a01c60020b12156108ae57600260018d01549c0154905b602062093a808c6102898d604081015160020b908960c0606083015160020b92015116916040519a8b96879663feb0d71760e01b8852041660048601610df6565b038173739cb2b30fc47c19262ab023eae966a15c2f9a285af49586156108a3578c945f97610872575b5063ffffffff62093a808d04165f52600485016020528a8c60ff600160405f20015416158015610860575b610775575b50608001519d8e9460018060801b038754169686600f0b1596875f1461072a575050861561071b578e91600191889b5b838060801b0361032b8b868801548686860303036111a8565b1699610347858060801b039160028801548b8b8b0303036111a8565b169815610701575b0303910155030360028b015581158015906106f8575b6106b9575b5050805f525f51602061154d5f395f51905f5260205260405f205480158015610677575b1561063257505f525f51602061154d5f395f51905f5260205260405f20604051906040820182811067ffffffffffffffff82111761061e5760405262093a80880463ffffffff1682526001600160801b03909316602082019081528354909390600160401b81101561061e5761040991600182018155610daa565b91909161060b5761045393600191518355519101555b60018060a01b031660130b63ffffffff62093a808704165f5260048701602052600160405f20015460081c60130b90610e22565b5f8160130b12610604575b5f87600f0b1396875f146105c6576001600160801b031690640100000000906001600160a01b031661049081846111f8565b92096105b5575b600160ff1b8110156105b157955b156105795763ffffffff62093a808504165f526004850160205260405f20545f8782019788129112908015821691151617610565576020955b63ffffffff62093a808604165f5260048601875260405f20555f6080840151600f0b12610510575b8585604051908152f35b610544575b610521575b8080610506565b606063ffffffff62093a8061053d94041691015160020b61148f565b5f8061051a565b61056063ffffffff62093a80850416604084015160020b61148f565b610515565b634e487b7160e01b5f52601160045260245ffd5b63ffffffff62093a808504165f526004850160205260405f20545f8782039712818812811691881390151617610565576020956104de565b5f80fd5b5f198110156105b157600101610497565b6105f291906001600160801b03906105e090600f0b6113f6565b6001600160a01b0390921691166111f8565b600160ff1b8110156105b157956104a5565b505f61045e565b634e487b7160e01b5f525f60045260245ffd5b634e487b7160e01b5f52604160045260245ffd5b92905f525f51602061154d5f395f51905f5260205260405f20925f1981019081116105655761066660019161045395610daa565b5091818060801b031691015561041f565b50815f525f51602061154d5f395f51905f5260205260405f205f198201828111610565576106a491610daa565b505462093a80890463ffffffff16141561038e565b60038a0180546001600160801b03808216949094019093166001600160801b03199384168117608090811c9390930190921b9092161790555f8061036a565b50801515610365565b838060801b038d16848060801b031986541617855561034f565b632aabfc9960e11b5f5260045ffd5b909260019290915f600f82900b121561075f5761074c61075991600f0b6113f6565b848060801b03168a61140d565b9b610312565b61077090848060801b03168a6113dd565b610759565b62093a8063ffffffff9160018060a01b0384511693602081015190604081015160020b8560c0606084015160020b9301511692604051976107b589610d40565b87878704168952602089015260408801526060870152608086015260a085015204165f5260048601602052600160405f2001600160ff1982541617905560018060801b0386541661083a575b515f90815260048601602052604090206001018054610100600160a81b031916600889901b610100600160a81b03161790558a8c6102e2565b61084b61084682610e64565b610e54565b81515f526004870160205260405f2055610801565b5086546001600160801b0316156102dd565b61089591975060203d60201161089c575b61088d8183610d78565b810190610dd7565b955f6102b2565b503d610883565b6040513d5f823e3d90fd5b600260018d015487039c0154850390610248565b60026001830154870392015485039061022d565b9150915061093763ffffffff60c08501511660018060801b037ff047b0c59244a0faf8e48cb6b6fde518e6717176152b6dd953628cd9dccb2805541661ffff60805160c81c169161ffff60805160b81c169060805160a01c60020b90611247565b929091604085015160020b93608086015160c08701519060018060801b0361010089015116875f525f51602061152d5f395f51905f5260205260405f209260018060801b03845416985f84600f0b125f14610ca5576109aa61099b85600f0b6113f6565b6001600160801b03168b61140d565b925b6001600160801b03841611610bf7576001600160801b038316159915998a141599610c1e575b505082546001600160801b0319166001600160801b03919091161780835560801d600f90810b91900b0160016001607f1b03811360016001607f1b03198212176105655781546001600160801b03908116608092831b6001600160801b0319161790925560608801519088015160c08901516101008a015160029390930b5f8181525f51602061152d5f395f51905f526020526040812080548c9b93989197600f87900b9781169692949391169190871215610c065750610aa4610a95876113f6565b6001600160801b03168261140d565b6001600160801b0316948511610bf7571596878515141597610b64575b505084546001600160801b0319169290921780855560801d600f0b9290920391505060016001607f1b0319811260016001607f1b038213176105655781546001600160801b031660809190911b6001600160801b031916179055938490610b46575b156101e657610b41606086015160020b60e087015160020b90611426565b6101e6565b610b5f604087015160020b60e088015160020b90611426565b610b23565b60805160a01c60020b1215610b97575b5050506003830180546001600160f81b0316600160f81b1790555f808080610ac1565b600186018990556002860188905560038601805466ffffffffffffff929092166001600160f81b031990921660389390931b600160381b600160d81b0316929092171760d89290921b63ffffffff60d81b169190911790555f8080610b74565b6368d2be8f60e01b5f5260045ffd5b610c19906001600160801b0316826113dd565b610aa4565b60805160a01c60020b1215610c4d575b506003830180546001600160f81b0316600160f81b1790555f806109d2565b600184018790556002840186905560038401805466ffffffffffffff8a166001600160f81b0319909116603888901b600160381b600160d81b0316171760d89290921b63ffffffff60d81b169190911790555f610c2e565b610cb86001600160801b0385168b6113dd565b926109ac565b60c03660031901126105b157610cd381610d40565b60043581526024356001600160a01b03811681036105b15760208201526044356040820152610d00610d9a565b60608201526084358060020b81036105b157608082015260a43563ffffffff811681036105b15781610d389160a06020940152610e64565b604051908152f35b60c0810190811067ffffffffffffffff82111761061e57604052565b6080810190811067ffffffffffffffff82111761061e57604052565b90601f8019910116810190811067ffffffffffffffff82111761061e57604052565b606435908160020b82036105b157565b8054821015610dc3575f5260205f209060011b01905f90565b634e487b7160e01b5f52603260045260245ffd5b908160209103126105b157516001600160a01b03811681036105b15790565b929363ffffffff91959460609383608087019816865260020b602086015260020b604085015216910152565b601391820b910b039060016001609f1b0319821260016001609f1b0383131761056557565b9190820391821161056557565b600160ff1b8114610565575f0390565b7ff047b0c59244a0faf8e48cb6b6fde518e6717176152b6dd953628cd9dccc2809548151116110675760018060a01b03602082015116604082015190610ebd6060840191825160020b6080860194855160020b92611076565b92835f525f51602061154d5f395f51905f5260205260405f20610ee18251826110c1565b9190911561105d57600191610ef591610daa565b50015491845f525f51602061150d5f395f51905f52602052600460405f200182515f526020526020600160405f20015460081c60130b9463ffffffff845116925160020b905160020b92610f6863ffffffff60a087015116604051958694859463feb0d71760e01b865260048601610df6565b038173739cb2b30fc47c19262ab023eae966a15c2f9a285af49384156108a357610fdf94610fa9925f9161103e575b506001600160a01b031660130b610e22565b935f525f51602061150d5f395f51905f52602052600460405f200190515f5260205260405f20549260018060a01b0316906111f8565b905f8112808015611035575b1561102b571561101d57610ffe90610e54565b8101809111610565575b905b61127560671b821161101857565b5f9150565b61102691610e47565b611008565b5050505f9061100a565b50818311610feb565b611057915060203d60201161089c5761088d8183610d78565b5f610f97565b5050505050505f90565b634789d90160e01b5f5260045ffd5b9290916040519260208401946bffffffffffffffffffffffff199060601b168552603484015260e81b605483015260e81b6057820152603a81526110bb605a82610d78565b51902090565b919091805490811561119e57805415610dc357805f528360205f20541161119e575f19820191821161056557816110f88183610daa565b50549480861161110a575b5050509190565b9194505f92505b828111611130575050806111259193610daa565b5054915f8080611103565b61114a611141848397949597610e47565b60011c82610e47565b926111558484610daa565b50549581870361116757505050509190565b90809396949294105f1461117c575091611111565b92505f1981019081111561111157634e487b7160e01b5f52601160045260245ffd5b505090505f905f90565b5f91905f1982820991808202938480851094039380850394146111ee57600160801b8410156111eb5750600160801b910990828211900360801b910360801c1790565b80fd5b5050505060801c90565b5f915f19818309918181029384808510940393808503941461123d578364010000000011156111eb575090640100000000910990828211900360e01b910360201c1790565b5050505060201c90565b92919094935061ffff811015610dc3577ff047b0c59244a0faf8e48cb6b6fde518e6717176152b6dd953628cd9dccb280901906040519161128783610d5c565b5463ffffffff811683528060201c60060b602084015260018060a01b038160581c16604084015260f81c15156060830152819463ffffffff83511663ffffffff8516036112ec575b50505050602082015160060b91604060018060a01b039101511690565b9091929394506112fa6114e8565b508251602084015160409094015163ffffffff9091168503936001600160a01b039091169260069190910b906001600160801b038116156113b8575b6001600160801b03169182156113a45763ffffffff6040519661135888610d5c565b16865263ffffffff851660060b9060020b0260060b0160060b602085015260018060a01b039263ffffffff60801b9060801b16040116604082015260016060820152905f8080806112cf565b634e487b7160e01b5f52601260045260245ffd5b506001611336565b60020b5f525f51602061152d5f395f51905f5260205260405f2090565b6001600160801b03918216908216019190821161056557565b600f0b60016001607f1b03198114610565575f0390565b6001600160801b03918216908216039190821161056557565b9060020b9081156113a457818160020b0760020b6105b15781156113a45760020b0560020b61010081079060081d60010b5f527ff047b0c59244a0faf8e48cb6b6fde518e6717176152b6dd953628cd9dccb2807602052600160ff60405f2092161b8154189055565b60049060020b91825f525f51602061152d5f395f51905f5260205260405f20905f52016020525f60408120555f525f51602061152d5f395f51905f526020525f6003604082208281558260018201558260028201550155565b604051906114f582610d5c565b5f606083828152826020820152826040820152015256fef047b0c59244a0faf8e48cb6b6fde518e6717176152b6dd953628cd9dccb2808f047b0c59244a0faf8e48cb6b6fde518e6717176152b6dd953628cd9dccb2806f047b0c59244a0faf8e48cb6b6fde518e6717176152b6dd953628cd9dccc280aa2646970667358221220b697fe6f46f97272b9f4c88e07a910b1cc18b9adda1162f276c62dac4b34129d64736f6c634300081c0033

Deployed Bytecode

0x60a0806040526004361015610012575f80fd5b5f3560e01c908163d2e6311b14610cbe575063fc32287914610032575f80fd5b307f0000000000000000000000000be5821b66b82676a550fdff0b686b3e2d85ac62146105b1576101203660031901126105b157604051610120810181811067ffffffffffffffff82111761061e576040526004356001600160a01b03811681036105b157815260243560208201526044358060020b81036105b15760408201526100bb610d9a565b606082015260843580600f0b81036105b157608082015260a4358060020b81036105b15760a082015260c4359063ffffffff8216918281036105b15760c082015260e4358060020b81036105b15760e0820152610104356001600160801b03811681036105b15761010082015261015160018060a01b038251166020830151604084015160020b90606085015160020b92611076565b91825f525f51602061150d5f395f51905f5260205260405f20917ff047b0c59244a0faf8e48cb6b6fde518e6717176152b6dd953628cd9dccb2802547ff047b0c59244a0faf8e48cb6b6fde518e6717176152b6dd953628cd9dccb280354907ff047b0c59244a0faf8e48cb6b6fde518e6717176152b6dd953628cd9dccb2800546080525f905f926080850151600f0b6108d6575b604085015160020b9763ffffffff606087015160020b6102058b6113c0565b9061020f816113c0565b9b60805160a01c60020b12155f146108c25760026001830154920154905b60805160a01c60020b12156108ae57600260018d01549c0154905b602062093a808c6102898d604081015160020b908960c0606083015160020b92015116916040519a8b96879663feb0d71760e01b8852041660048601610df6565b038173739cb2b30fc47c19262ab023eae966a15c2f9a285af49586156108a3578c945f97610872575b5063ffffffff62093a808d04165f52600485016020528a8c60ff600160405f20015416158015610860575b610775575b50608001519d8e9460018060801b038754169686600f0b1596875f1461072a575050861561071b578e91600191889b5b838060801b0361032b8b868801548686860303036111a8565b1699610347858060801b039160028801548b8b8b0303036111a8565b169815610701575b0303910155030360028b015581158015906106f8575b6106b9575b5050805f525f51602061154d5f395f51905f5260205260405f205480158015610677575b1561063257505f525f51602061154d5f395f51905f5260205260405f20604051906040820182811067ffffffffffffffff82111761061e5760405262093a80880463ffffffff1682526001600160801b03909316602082019081528354909390600160401b81101561061e5761040991600182018155610daa565b91909161060b5761045393600191518355519101555b60018060a01b031660130b63ffffffff62093a808704165f5260048701602052600160405f20015460081c60130b90610e22565b5f8160130b12610604575b5f87600f0b1396875f146105c6576001600160801b031690640100000000906001600160a01b031661049081846111f8565b92096105b5575b600160ff1b8110156105b157955b156105795763ffffffff62093a808504165f526004850160205260405f20545f8782019788129112908015821691151617610565576020955b63ffffffff62093a808604165f5260048601875260405f20555f6080840151600f0b12610510575b8585604051908152f35b610544575b610521575b8080610506565b606063ffffffff62093a8061053d94041691015160020b61148f565b5f8061051a565b61056063ffffffff62093a80850416604084015160020b61148f565b610515565b634e487b7160e01b5f52601160045260245ffd5b63ffffffff62093a808504165f526004850160205260405f20545f8782039712818812811691881390151617610565576020956104de565b5f80fd5b5f198110156105b157600101610497565b6105f291906001600160801b03906105e090600f0b6113f6565b6001600160a01b0390921691166111f8565b600160ff1b8110156105b157956104a5565b505f61045e565b634e487b7160e01b5f525f60045260245ffd5b634e487b7160e01b5f52604160045260245ffd5b92905f525f51602061154d5f395f51905f5260205260405f20925f1981019081116105655761066660019161045395610daa565b5091818060801b031691015561041f565b50815f525f51602061154d5f395f51905f5260205260405f205f198201828111610565576106a491610daa565b505462093a80890463ffffffff16141561038e565b60038a0180546001600160801b03808216949094019093166001600160801b03199384168117608090811c9390930190921b9092161790555f8061036a565b50801515610365565b838060801b038d16848060801b031986541617855561034f565b632aabfc9960e11b5f5260045ffd5b909260019290915f600f82900b121561075f5761074c61075991600f0b6113f6565b848060801b03168a61140d565b9b610312565b61077090848060801b03168a6113dd565b610759565b62093a8063ffffffff9160018060a01b0384511693602081015190604081015160020b8560c0606084015160020b9301511692604051976107b589610d40565b87878704168952602089015260408801526060870152608086015260a085015204165f5260048601602052600160405f2001600160ff1982541617905560018060801b0386541661083a575b515f90815260048601602052604090206001018054610100600160a81b031916600889901b610100600160a81b03161790558a8c6102e2565b61084b61084682610e64565b610e54565b81515f526004870160205260405f2055610801565b5086546001600160801b0316156102dd565b61089591975060203d60201161089c575b61088d8183610d78565b810190610dd7565b955f6102b2565b503d610883565b6040513d5f823e3d90fd5b600260018d015487039c0154850390610248565b60026001830154870392015485039061022d565b9150915061093763ffffffff60c08501511660018060801b037ff047b0c59244a0faf8e48cb6b6fde518e6717176152b6dd953628cd9dccb2805541661ffff60805160c81c169161ffff60805160b81c169060805160a01c60020b90611247565b929091604085015160020b93608086015160c08701519060018060801b0361010089015116875f525f51602061152d5f395f51905f5260205260405f209260018060801b03845416985f84600f0b125f14610ca5576109aa61099b85600f0b6113f6565b6001600160801b03168b61140d565b925b6001600160801b03841611610bf7576001600160801b038316159915998a141599610c1e575b505082546001600160801b0319166001600160801b03919091161780835560801d600f90810b91900b0160016001607f1b03811360016001607f1b03198212176105655781546001600160801b03908116608092831b6001600160801b0319161790925560608801519088015160c08901516101008a015160029390930b5f8181525f51602061152d5f395f51905f526020526040812080548c9b93989197600f87900b9781169692949391169190871215610c065750610aa4610a95876113f6565b6001600160801b03168261140d565b6001600160801b0316948511610bf7571596878515141597610b64575b505084546001600160801b0319169290921780855560801d600f0b9290920391505060016001607f1b0319811260016001607f1b038213176105655781546001600160801b031660809190911b6001600160801b031916179055938490610b46575b156101e657610b41606086015160020b60e087015160020b90611426565b6101e6565b610b5f604087015160020b60e088015160020b90611426565b610b23565b60805160a01c60020b1215610b97575b5050506003830180546001600160f81b0316600160f81b1790555f808080610ac1565b600186018990556002860188905560038601805466ffffffffffffff929092166001600160f81b031990921660389390931b600160381b600160d81b0316929092171760d89290921b63ffffffff60d81b169190911790555f8080610b74565b6368d2be8f60e01b5f5260045ffd5b610c19906001600160801b0316826113dd565b610aa4565b60805160a01c60020b1215610c4d575b506003830180546001600160f81b0316600160f81b1790555f806109d2565b600184018790556002840186905560038401805466ffffffffffffff8a166001600160f81b0319909116603888901b600160381b600160d81b0316171760d89290921b63ffffffff60d81b169190911790555f610c2e565b610cb86001600160801b0385168b6113dd565b926109ac565b60c03660031901126105b157610cd381610d40565b60043581526024356001600160a01b03811681036105b15760208201526044356040820152610d00610d9a565b60608201526084358060020b81036105b157608082015260a43563ffffffff811681036105b15781610d389160a06020940152610e64565b604051908152f35b60c0810190811067ffffffffffffffff82111761061e57604052565b6080810190811067ffffffffffffffff82111761061e57604052565b90601f8019910116810190811067ffffffffffffffff82111761061e57604052565b606435908160020b82036105b157565b8054821015610dc3575f5260205f209060011b01905f90565b634e487b7160e01b5f52603260045260245ffd5b908160209103126105b157516001600160a01b03811681036105b15790565b929363ffffffff91959460609383608087019816865260020b602086015260020b604085015216910152565b601391820b910b039060016001609f1b0319821260016001609f1b0383131761056557565b9190820391821161056557565b600160ff1b8114610565575f0390565b7ff047b0c59244a0faf8e48cb6b6fde518e6717176152b6dd953628cd9dccc2809548151116110675760018060a01b03602082015116604082015190610ebd6060840191825160020b6080860194855160020b92611076565b92835f525f51602061154d5f395f51905f5260205260405f20610ee18251826110c1565b9190911561105d57600191610ef591610daa565b50015491845f525f51602061150d5f395f51905f52602052600460405f200182515f526020526020600160405f20015460081c60130b9463ffffffff845116925160020b905160020b92610f6863ffffffff60a087015116604051958694859463feb0d71760e01b865260048601610df6565b038173739cb2b30fc47c19262ab023eae966a15c2f9a285af49384156108a357610fdf94610fa9925f9161103e575b506001600160a01b031660130b610e22565b935f525f51602061150d5f395f51905f52602052600460405f200190515f5260205260405f20549260018060a01b0316906111f8565b905f8112808015611035575b1561102b571561101d57610ffe90610e54565b8101809111610565575b905b61127560671b821161101857565b5f9150565b61102691610e47565b611008565b5050505f9061100a565b50818311610feb565b611057915060203d60201161089c5761088d8183610d78565b5f610f97565b5050505050505f90565b634789d90160e01b5f5260045ffd5b9290916040519260208401946bffffffffffffffffffffffff199060601b168552603484015260e81b605483015260e81b6057820152603a81526110bb605a82610d78565b51902090565b919091805490811561119e57805415610dc357805f528360205f20541161119e575f19820191821161056557816110f88183610daa565b50549480861161110a575b5050509190565b9194505f92505b828111611130575050806111259193610daa565b5054915f8080611103565b61114a611141848397949597610e47565b60011c82610e47565b926111558484610daa565b50549581870361116757505050509190565b90809396949294105f1461117c575091611111565b92505f1981019081111561111157634e487b7160e01b5f52601160045260245ffd5b505090505f905f90565b5f91905f1982820991808202938480851094039380850394146111ee57600160801b8410156111eb5750600160801b910990828211900360801b910360801c1790565b80fd5b5050505060801c90565b5f915f19818309918181029384808510940393808503941461123d578364010000000011156111eb575090640100000000910990828211900360e01b910360201c1790565b5050505060201c90565b92919094935061ffff811015610dc3577ff047b0c59244a0faf8e48cb6b6fde518e6717176152b6dd953628cd9dccb280901906040519161128783610d5c565b5463ffffffff811683528060201c60060b602084015260018060a01b038160581c16604084015260f81c15156060830152819463ffffffff83511663ffffffff8516036112ec575b50505050602082015160060b91604060018060a01b039101511690565b9091929394506112fa6114e8565b508251602084015160409094015163ffffffff9091168503936001600160a01b039091169260069190910b906001600160801b038116156113b8575b6001600160801b03169182156113a45763ffffffff6040519661135888610d5c565b16865263ffffffff851660060b9060020b0260060b0160060b602085015260018060a01b039263ffffffff60801b9060801b16040116604082015260016060820152905f8080806112cf565b634e487b7160e01b5f52601260045260245ffd5b506001611336565b60020b5f525f51602061152d5f395f51905f5260205260405f2090565b6001600160801b03918216908216019190821161056557565b600f0b60016001607f1b03198114610565575f0390565b6001600160801b03918216908216039190821161056557565b9060020b9081156113a457818160020b0760020b6105b15781156113a45760020b0560020b61010081079060081d60010b5f527ff047b0c59244a0faf8e48cb6b6fde518e6717176152b6dd953628cd9dccb2807602052600160ff60405f2092161b8154189055565b60049060020b91825f525f51602061152d5f395f51905f5260205260405f20905f52016020525f60408120555f525f51602061152d5f395f51905f526020525f6003604082208281558260018201558260028201550155565b604051906114f582610d5c565b5f606083828152826020820152826040820152015256fef047b0c59244a0faf8e48cb6b6fde518e6717176152b6dd953628cd9dccb2808f047b0c59244a0faf8e48cb6b6fde518e6717176152b6dd953628cd9dccb2806f047b0c59244a0faf8e48cb6b6fde518e6717176152b6dd953628cd9dccc280aa2646970667358221220b697fe6f46f97272b9f4c88e07a910b1cc18b9adda1162f276c62dac4b34129d64736f6c634300081c0033

Block Transaction Gas Used Reward
view all blocks produced

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

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
[ 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.