KatanaV3Pool.observe() will likely revert when the current timestamp wraps around the 2**32 boundary, and after time >= 2**32, observations before < 2**32 all become not available to observe,
Audit: 2024-10-ronin-findings
Severity: MEDIUM

# Lines of code

KatanaV3Pool.sol#L223-L232
Oracle.sol#L286-L304
Oracle.sol#L231-L273


# Vulnerability details

## Proof of Concept
The root cause lies in the following line in function ```Oracle.observeSingle()``` L246. 
Since it will affect function ```KatanaV3Pool.observe()```, this is a valid funding according to "If you discover a bug in any contract or library outside of the files listed above that impact following contracts, we will consider the issue valid". 

First of all, Oracle.sol uses time as uint32 and deals with the wrapping around using the ```lte()``` function: 

```
 function lte(uint32 time, uint32 a, uint32 b) private pure returns (bool) {
    // 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;
  }
```

In other words, when time is greater than 2***32, it will round back to 0, the lte function makes adjustment so that the comparison between ```a``` and ```b``` deal with such wrapping around

Unfortunately, such wrapping around boundary case is not dealt with in the following line L246 in function ```observeSingle```: 

```javascript
 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) {
    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) {
      // we're at the left boundary
      return (beforeOrAt.tickCumulative, beforeOrAt.secondsPerLiquidityCumulativeX128);
    } else if (target == atOrAfter.blockTimestamp) {
      // we're at the right boundary
      return (atOrAfter.tickCumulative, atOrAfter.secondsPerLiquidityCumulativeX128);
    } else {
      // we're in the middle
      uint32 observationTimeDelta = atOrAfter.blockTimestamp - beforeOrAt.blockTimestamp;
      uint32 targetDelta = target - beforeOrAt.blockTimestamp;
      return (
        beforeOrAt.tickCumulative
          + ((atOrAfter.tickCumulative - beforeOrAt.tickCumulative) / observationTimeDelta) * targetDelta,
        beforeOrAt.secondsPerLiquidityCumulativeX128
          + uint160(
            (
              uint256(atOrAfter.secondsPerLiquidityCumulativeX128 - beforeOrAt.secondsPerLiquidityCumulativeX128)
                * targetDelta
            ) / observationTimeDelta
          )
      );
    }
  }
```

As a result, when tim