MEDIUM severity: Incorrect oracle truncation allows successive observations to exceed `MAX_ABS_TICK_MOVE`. **Description:** The Bunni `Oracle` library is a modified version of the Uniswap V3 library of the same name. The most notable difference is the inclusion of a constant `MAX_ABS_TICK_MOVE = 9116` which specifies the maximum amount of ticks in either direction that the pool is allowed to move at...

Vulnerable code:
function write(
        Observation[MAX_CARDINALITY] storage self,
        Observation memory intermediate,
        uint32 index,
        uint32 blockTimestamp,
        int24 tick,
        uint32 cardinality,
        uint32 cardinalityNext,
        uint32 minInterval
    ) internal returns (Observation memory intermediateUpdated, uint32 indexUpdated, uint32 cardinalityUpdated) {
        unchecked {
            // early return if we've already written an observation this block
            if (intermediate.blockTimestamp == blockTimestamp) {
                return (intermediate, index, cardinality);
            }

            // update the intermediate observation using the most recent observation
            // which is always the current intermediate observation
@>          intermediateUpdated = transform(intermediate, blockTimestamp, tick);

            // if the time since the last recorded observation is less than the minimum interval, we store the observation in the intermediate observation
            if (blockTimestamp - self[index].blockTimestamp < minInterval) {
                return (intermediateUpdated, index, cardinality);
            }

            // 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] = intermediateUpdated;
        }
    }

    function transform(Observation memory last, uint32 blockTimestamp, int24 tick)
        private
        pure
        returns (Observation memory)
    {
        unchecked {
            uint32 delta = blockTimestamp - last.blockTimestamp;

            // if the current tick moves more than the max abs tick movement
            // then we truncate it down
            if ((tick - last.prevTick) > MAX_ABS_TICK_MOVE) {
                tick = last.prevTick + MAX_ABS_TICK_MOVE;
            } else if ((tick - last.prevTick) < -MAX_ABS_TICK_MOVE) {
                tick = last.prevTick - MAX_ABS_TICK_MOVE;
            }

            return Observation({
                blockTimestamp: blockTimestamp,
                prevTick: tick,
                tickCumulative: last.tickCumulative + int56(tick) * int56(uint56(delta)),
                initialized: true
            });
        }
    }

| Timestamp    | Tick     | State changes                                                 |
|--------------|----------|---------------------------------------------------------------|
| timestamp 0  | 100 000  |                                                               |
| timestamp 12 | 109 116  | create an observation with 109 116 and set it as intermediate |
| timestamp 24 | 118 232  | set 118 232 as intermediate                                   |
| timestamp 36 | 127 348  | set 127 348 as intermediate                                   |
| timestamp 48 | 136 464  | set 136 464 as intermediate                                   |
| timestamp 60 | 145 580  | create an observation with 145 580 and set it as intermediate |