fix: _isPriceStable fallback interval can still revert on pools with very short history (#610)

Wrap the fallback pool.observe() call in a try/catch so that pools with
insufficient observation history for both the primary (30s) and fallback
(6000s) intervals return false (price unstable) instead of reverting with
an opaque Uniswap V3 error. This prevents recenter() from failing for
unpermissioned callers on newly created pools.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
johba 2026-03-22 20:31:04 +00:00
parent 1691128f91
commit db1c26838d
2 changed files with 18 additions and 3 deletions

View file

@ -43,9 +43,15 @@ abstract contract PriceOracle {
// Fallback to longer timeframe if recent data unavailable
uint32 fallbackInterval = PRICE_STABILITY_INTERVAL * 200; // 6,000 seconds
secondsAgo[0] = fallbackInterval;
(int56[] memory tickCumulatives,) = pool.observe(secondsAgo);
int56 tickCumulativeDiff = tickCumulatives[1] - tickCumulatives[0];
averageTick = int24(tickCumulativeDiff / int56(int32(fallbackInterval)));
try pool.observe(secondsAgo) returns (int56[] memory fallbackCumulatives, uint160[] memory) {
int56 tickCumulativeDiff = fallbackCumulatives[1] - fallbackCumulatives[0];
averageTick = int24(tickCumulativeDiff / int56(int32(fallbackInterval)));
} catch {
// Pool has insufficient observation history for both intervals.
// Treat price as unstable (safe default) prevents recenter() from
// reverting with an opaque Uniswap error on pools with very short history.
return false;
}
}
isStable = (currentTick >= averageTick - MAX_TICK_DEVIATION && currentTick <= averageTick + MAX_TICK_DEVIATION);

View file

@ -228,6 +228,15 @@ contract PriceOracleTest is Test {
assertTrue(isStable, "Price stability should work with negative ticks");
}
function testDoubleFailureReturnsFalse() public {
// When pool has < 6000 seconds of history, both observe() calls fail.
// _isPriceStable should return false (safe default) instead of reverting.
mockPool.setShouldRevert(true);
bool isStable = priceOracle.isPriceStable(1000);
assertFalse(isStable, "Double observe failure should return false, not revert");
}
// ========================================
// PRICE MOVEMENT VALIDATION TESTS
// ========================================