Contract

0x5699bE61618D2386C6F5Feb9843490c08507E33F

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
9355032024-12-20 22:13:329 hrs ago1734732812  Contract Creation0 S
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
Oracle

Compiler Version
v0.8.28+commit.7893614a

Optimization Enabled:
Yes with 933 runs

Other Settings:
cancun EvmVersion
File 1 of 2 : 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 2 of 2 : 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
        }
    }
}

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": 933
  },
  "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": "0x5699bE61618D2386C6F5Feb9843490c08507E33F"
    },
    "contracts/CL/core/libraries/Position.sol": {
      "Position": "0xd940FA70289a13075BfeF3b5cECa8Beb6568aeDd"
    },
    "contracts/CL/core/libraries/ProtocolActions.sol": {
      "ProtocolActions": "0x1a161022Bf556AAa9b0e0281Ec03e19b809e0aE8"
    }
  }
}

Contract Security Audit

Contract ABI

[{"inputs":[],"name":"I","type":"error"},{"inputs":[],"name":"OLD","type":"error"},{"inputs":[{"internalType":"uint32","name":"period","type":"uint32"},{"internalType":"int24","name":"tickLower","type":"int24"},{"internalType":"int24","name":"tickUpper","type":"int24"},{"internalType":"uint32","name":"_blockTimestamp","type":"uint32"}],"name":"periodCumulativesInside","outputs":[{"internalType":"uint160","name":"secondsPerLiquidityInsideX128","type":"uint160"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"int24","name":"tickLower","type":"int24"},{"internalType":"int24","name":"tickUpper","type":"int24"},{"internalType":"uint32","name":"_blockTimestamp","type":"uint32"}],"name":"snapshotCumulativesInside","outputs":[{"internalType":"int56","name":"tickCumulativeInside","type":"int56"},{"internalType":"uint160","name":"secondsPerLiquidityInsideX128","type":"uint160"},{"internalType":"uint32","name":"secondsInside","type":"uint32"}],"stateMutability":"view","type":"function"}]

60808060405234601b57610c8390816100208239308160a10152f35b5f80fdfe60806040526004361015610011575f80fd5b5f3560e01c80639b7beb60146102ad578063c51185d81461009e5763feb0d7171461003a575f80fd5b608036600319011261009a5760043563ffffffff8116810361009a5761005e610313565b906044358060020b810361009a576064359063ffffffff8216820361009a5760209361008993610777565b6001600160a01b0360405191168152f35b5f80fd5b307f00000000000000000000000000000000000000000000000000000000000000001461009a57606036600319011261009a5760043560243561ffff8116810361009a576100f56100ef82846106e3565b50610707565b63ffffffff5f198162093a808160443516021601169263ffffffff8251168403916101fb6001600160a01b03806040840151166001600160801b037ff047b0c59244a0faf8e48cb6b6fde518e6717176152b6dd953628cd9dccb280554168015155f1461029a576001600160801b03610189915b1673ffffffff000000000000000000000000000000008860801b16610745565b011694602083015160060b60607ff047b0c59244a0faf8e48cb6b6fde518e6717176152b6dd953628cd9dccb28005460a01c60020b9401511515936101cc610323565b98895263ffffffff60208a01971660060b0260060b0160060b85526040870193868552606088019384526106e3565b939093610287579451945191519051602092831b6affffffffffffff000000001663ffffffff969096169590951760589190911b7effffffffffffffffffffffffffffffffffffffff0000000000000000000000161793151560f81b7fff0000000000000000000000000000000000000000000000000000000000000016939093179055604051908152f35b634e487b7160e01b5f525f60045260245ffd5b506101896001600160801b036001610169565b606036600319011261009a576004358060020b810361009a576102ce610313565b6044359063ffffffff8216820361009a576102f96001600160a01b039263ffffffff92606095610461565b9193906040519460060b8552166020840152166040820152f35b602435908160020b820361009a57565b604051906080820182811067ffffffffffffffff82111761034357604052565b634e487b7160e01b5f52604160045260245ffd5b6040519060c0820182811067ffffffffffffffff821117610343576040525f60a0838281528260208201528260408201528260608201528260808201520152565b6040519060e0820182811067ffffffffffffffff821117610343576040528160c060ff7ff047b0c59244a0faf8e48cb6b6fde518e6717176152b6dd953628cd9dccb2800546001600160a01b03811684528060a01c60020b602085015261ffff8160b81c16604085015261ffff8160c81c16606085015261ffff8160d81c166080850152818160e81c1660a085015260f01c161515910152565b604051906060820182811067ffffffffffffffff821117610343576040525f6040838281528260208201520152565b9290916104988460020b5f527ff047b0c59244a0faf8e48cb6b6fde518e6717176152b6dd953628cd9dccb280660205260405f2090565b936104cd8460020b5f527ff047b0c59244a0faf8e48cb6b6fde518e6717176152b6dd953628cd9dccb280660205260405f2090565b9360036104d8610357565b960154956040810195608082019763ffffffff8160d81c1689526001600160a01b038160381c1688528060060b835260f81c1561009a5760030154936020820192606083019460a084019663ffffffff8160d81c1688526001600160a01b038160381c1687528060060b865260f81c1561009a57610554610398565b906020820192835160020b9160020b82125f146105ab5750505050506001600160a01b0363ffffffff94938180879586955160060b905160060b900360060b99511691511690031696511691511690031691929190565b939a96959360020b13156106a3576105c1610432565b9963ffffffff1691828b525160020b90604081015161ffff167ff047b0c59244a0faf8e48cb6b6fde518e6717176152b6dd953628cd9dccb2805546001600160801b0316916060015161ffff169261061894610b01565b6001600160a01b039093919316928360408b015260060b908160208b01525160060b900360060b905160060b900360060b95516001600160a01b031690036001600160a01b031690516001600160a01b031690036001600160a01b0316945163ffffffff16905163ffffffff16900363ffffffff16905163ffffffff16900363ffffffff1691929190565b505063ffffffff9497508492966001600160a01b0380859481945160060b905160060b900360060b99511691511690031696511691511690031691929190565b61ffff8210156106f35701905f90565b634e487b7160e01b5f52603260045260245ffd5b90610710610323565b915463ffffffff811683528060201c60060b60208401526001600160a01b038160581c16604084015260f81c15156060830152565b906001600160a01b0316908115610763576001600160a01b03160490565b634e487b7160e01b5f52601260045260245ffd5b926001600160a01b03936107b58360020b5f527ff047b0c59244a0faf8e48cb6b6fde518e6717176152b6dd953628cd9dccb280660205260405f2090565b916107ea8560020b5f527ff047b0c59244a0faf8e48cb6b6fde518e6717176152b6dd953628cd9dccb280660205260405f2090565b9263ffffffff6107f8610357565b931690815f527ff047b0c59244a0faf8e48cb6b6fde518e6717176152b6dd953628cd9dccc280860205260405f205460201c60020b93825f527ff047b0c59244a0faf8e48cb6b6fde518e6717176152b6dd953628cd9dccc2808602052600463ffffffff60405f20541692845f520160205260048960405f205416966040830198888a5260020b97878913159081610af8575b50610ac0575b845f520160205260608960405f20541691019781895260020b948513159081610ab7575b50610a7f575b507ff047b0c59244a0faf8e48cb6b6fde518e6717176152b6dd953628cd9dccc2809549281841194855f14610a5257825f527ff047b0c59244a0faf8e48cb6b6fde518e6717176152b6dd953628cd9dccc280860205260405f205460381c60020b5b60020b9081121561093c57505050505050828091511691511690031690565b1215610a3f57604087959394869586948594610956610432565b93610a04575063ffffffff1680835262093a8091820291820111156109f1575b50826109d7610983610398565b63ffffffff84511690602081015160020b61ffff87830151169061ffff60606001600160801b037ff047b0c59244a0faf8e48cb6b6fde518e6717176152b6dd953628cd9dccb280554169401511693610b01565b905016828201525b01511691511690031691511690031690565b62093a7f0163ffffffff1681525f610976565b9150505f527ff047b0c59244a0faf8e48cb6b6fde518e6717176152b6dd953628cd9dccc280860205282825f205460501c16828201526109df565b5050505090828091511691511690031690565b7ff047b0c59244a0faf8e48cb6b6fde518e6717176152b6dd953628cd9dccb28005460a01c60020b61091d565b5f527ff047b0c59244a0faf8e48cb6b6fde518e6717176152b6dd953628cd9dccc28086020528660405f205460501c1686525f6108bb565b9050155f6108b5565b835f527ff047b0c59244a0faf8e48cb6b6fde518e6717176152b6dd953628cd9dccc28086020528a60405f205460501c168952610891565b9050155f61088b565b92919094935061ffff8110156106f357610b3c907ff047b0c59244a0faf8e48cb6b6fde518e6717176152b6dd953628cd9dccb280901610707565b9363ffffffff85511663ffffffff841603610b6e575b5050506001600160a01b036040602084015160060b9301511690565b5f949192939450610b7d610c2d565b5063ffffffff6001600160a01b0381845116860394816040602087015160060b96015116906001600160801b03811615155f14610c1a576001600160801b03610be0911673ffffffff000000000000000000000000000000008860801b16610745565b01169381610bec610323565b961686521660060b9060020b0260060b0160060b6020830152604082015260016060820152905f8080610b52565b50610be06001600160801b036001610169565b610c35610323565b905f82525f60208301525f60408301525f606083015256fea26469706673582212209bc278ec3b701c43746769559a4baba08fbc4d18ae3ba14045382d1c5c2b4d4e64736f6c634300081c0033

Deployed Bytecode

0x60806040526004361015610011575f80fd5b5f3560e01c80639b7beb60146102ad578063c51185d81461009e5763feb0d7171461003a575f80fd5b608036600319011261009a5760043563ffffffff8116810361009a5761005e610313565b906044358060020b810361009a576064359063ffffffff8216820361009a5760209361008993610777565b6001600160a01b0360405191168152f35b5f80fd5b307f0000000000000000000000005699be61618d2386c6f5feb9843490c08507e33f1461009a57606036600319011261009a5760043560243561ffff8116810361009a576100f56100ef82846106e3565b50610707565b63ffffffff5f198162093a808160443516021601169263ffffffff8251168403916101fb6001600160a01b03806040840151166001600160801b037ff047b0c59244a0faf8e48cb6b6fde518e6717176152b6dd953628cd9dccb280554168015155f1461029a576001600160801b03610189915b1673ffffffff000000000000000000000000000000008860801b16610745565b011694602083015160060b60607ff047b0c59244a0faf8e48cb6b6fde518e6717176152b6dd953628cd9dccb28005460a01c60020b9401511515936101cc610323565b98895263ffffffff60208a01971660060b0260060b0160060b85526040870193868552606088019384526106e3565b939093610287579451945191519051602092831b6affffffffffffff000000001663ffffffff969096169590951760589190911b7effffffffffffffffffffffffffffffffffffffff0000000000000000000000161793151560f81b7fff0000000000000000000000000000000000000000000000000000000000000016939093179055604051908152f35b634e487b7160e01b5f525f60045260245ffd5b506101896001600160801b036001610169565b606036600319011261009a576004358060020b810361009a576102ce610313565b6044359063ffffffff8216820361009a576102f96001600160a01b039263ffffffff92606095610461565b9193906040519460060b8552166020840152166040820152f35b602435908160020b820361009a57565b604051906080820182811067ffffffffffffffff82111761034357604052565b634e487b7160e01b5f52604160045260245ffd5b6040519060c0820182811067ffffffffffffffff821117610343576040525f60a0838281528260208201528260408201528260608201528260808201520152565b6040519060e0820182811067ffffffffffffffff821117610343576040528160c060ff7ff047b0c59244a0faf8e48cb6b6fde518e6717176152b6dd953628cd9dccb2800546001600160a01b03811684528060a01c60020b602085015261ffff8160b81c16604085015261ffff8160c81c16606085015261ffff8160d81c166080850152818160e81c1660a085015260f01c161515910152565b604051906060820182811067ffffffffffffffff821117610343576040525f6040838281528260208201520152565b9290916104988460020b5f527ff047b0c59244a0faf8e48cb6b6fde518e6717176152b6dd953628cd9dccb280660205260405f2090565b936104cd8460020b5f527ff047b0c59244a0faf8e48cb6b6fde518e6717176152b6dd953628cd9dccb280660205260405f2090565b9360036104d8610357565b960154956040810195608082019763ffffffff8160d81c1689526001600160a01b038160381c1688528060060b835260f81c1561009a5760030154936020820192606083019460a084019663ffffffff8160d81c1688526001600160a01b038160381c1687528060060b865260f81c1561009a57610554610398565b906020820192835160020b9160020b82125f146105ab5750505050506001600160a01b0363ffffffff94938180879586955160060b905160060b900360060b99511691511690031696511691511690031691929190565b939a96959360020b13156106a3576105c1610432565b9963ffffffff1691828b525160020b90604081015161ffff167ff047b0c59244a0faf8e48cb6b6fde518e6717176152b6dd953628cd9dccb2805546001600160801b0316916060015161ffff169261061894610b01565b6001600160a01b039093919316928360408b015260060b908160208b01525160060b900360060b905160060b900360060b95516001600160a01b031690036001600160a01b031690516001600160a01b031690036001600160a01b0316945163ffffffff16905163ffffffff16900363ffffffff16905163ffffffff16900363ffffffff1691929190565b505063ffffffff9497508492966001600160a01b0380859481945160060b905160060b900360060b99511691511690031696511691511690031691929190565b61ffff8210156106f35701905f90565b634e487b7160e01b5f52603260045260245ffd5b90610710610323565b915463ffffffff811683528060201c60060b60208401526001600160a01b038160581c16604084015260f81c15156060830152565b906001600160a01b0316908115610763576001600160a01b03160490565b634e487b7160e01b5f52601260045260245ffd5b926001600160a01b03936107b58360020b5f527ff047b0c59244a0faf8e48cb6b6fde518e6717176152b6dd953628cd9dccb280660205260405f2090565b916107ea8560020b5f527ff047b0c59244a0faf8e48cb6b6fde518e6717176152b6dd953628cd9dccb280660205260405f2090565b9263ffffffff6107f8610357565b931690815f527ff047b0c59244a0faf8e48cb6b6fde518e6717176152b6dd953628cd9dccc280860205260405f205460201c60020b93825f527ff047b0c59244a0faf8e48cb6b6fde518e6717176152b6dd953628cd9dccc2808602052600463ffffffff60405f20541692845f520160205260048960405f205416966040830198888a5260020b97878913159081610af8575b50610ac0575b845f520160205260608960405f20541691019781895260020b948513159081610ab7575b50610a7f575b507ff047b0c59244a0faf8e48cb6b6fde518e6717176152b6dd953628cd9dccc2809549281841194855f14610a5257825f527ff047b0c59244a0faf8e48cb6b6fde518e6717176152b6dd953628cd9dccc280860205260405f205460381c60020b5b60020b9081121561093c57505050505050828091511691511690031690565b1215610a3f57604087959394869586948594610956610432565b93610a04575063ffffffff1680835262093a8091820291820111156109f1575b50826109d7610983610398565b63ffffffff84511690602081015160020b61ffff87830151169061ffff60606001600160801b037ff047b0c59244a0faf8e48cb6b6fde518e6717176152b6dd953628cd9dccb280554169401511693610b01565b905016828201525b01511691511690031691511690031690565b62093a7f0163ffffffff1681525f610976565b9150505f527ff047b0c59244a0faf8e48cb6b6fde518e6717176152b6dd953628cd9dccc280860205282825f205460501c16828201526109df565b5050505090828091511691511690031690565b7ff047b0c59244a0faf8e48cb6b6fde518e6717176152b6dd953628cd9dccb28005460a01c60020b61091d565b5f527ff047b0c59244a0faf8e48cb6b6fde518e6717176152b6dd953628cd9dccc28086020528660405f205460501c1686525f6108bb565b9050155f6108b5565b835f527ff047b0c59244a0faf8e48cb6b6fde518e6717176152b6dd953628cd9dccc28086020528a60405f205460501c168952610891565b9050155f61088b565b92919094935061ffff8110156106f357610b3c907ff047b0c59244a0faf8e48cb6b6fde518e6717176152b6dd953628cd9dccb280901610707565b9363ffffffff85511663ffffffff841603610b6e575b5050506001600160a01b036040602084015160060b9301511690565b5f949192939450610b7d610c2d565b5063ffffffff6001600160a01b0381845116860394816040602087015160060b96015116906001600160801b03811615155f14610c1a576001600160801b03610be0911673ffffffff000000000000000000000000000000008860801b16610745565b01169381610bec610323565b961686521660060b9060020b0260060b0160060b6020830152604082015260016060820152905f8080610b52565b50610be06001600160801b036001610169565b610c35610323565b905f82525f60208301525f60408301525f606083015256fea26469706673582212209bc278ec3b701c43746769559a4baba08fbc4d18ae3ba14045382d1c5c2b4d4e64736f6c634300081c0033

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.