Skip to content
2 changes: 1 addition & 1 deletion src/generated/FlareFtsoWords.pointers.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ pragma solidity ^0.8.25;
// file needs the contract to exist so that it can be compiled.

/// @dev Hash of the known bytecode.
bytes32 constant BYTECODE_HASH = bytes32(0x69ffc4160d6551b77f501f51e6e53d7825dcd7318519779f877261ebd37b7da0);
bytes32 constant BYTECODE_HASH = bytes32(0x2af8cfa90fcca7c0a99347d12496ca176884b297111abf3992d4cad62865840c);

/// @dev The hash of the meta that describes the contract.
bytes32 constant DESCRIBED_BY_META_HASH = bytes32(0x4ccb316fd35a2abbee0016f38926a4c95b6270b229c85a9acf8a3a52b36bfedc);
Expand Down
4 changes: 2 additions & 2 deletions src/lib/lts/LibFtsoV2LTS.sol
Original file line number Diff line number Diff line change
Expand Up @@ -101,9 +101,9 @@ library LibFtsoV2LTS {

(uint256 value, uint64 timestamp) = ftsoRegistry.getFeedByIdInWei{value: fee}(feedId);

// Handle stale prices.
// Handle stale prices. Subtraction avoids overflow when timeout is large.
//slither-disable-next-line timestamp
if (block.timestamp > timestamp + timeout) {
if (block.timestamp > timestamp && block.timestamp - timestamp > timeout) {
revert StalePrice(timestamp, timeout);
}

Expand Down
5 changes: 3 additions & 2 deletions src/lib/price/LibFtsoCurrentPriceUsd.sol
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,10 @@ library LibFtsoCurrentPriceUsd {
revert InconsistentFtso();
}

// Handle stale prices.
// Handle stale prices. Subtraction avoids a uint256 overflow when timeout
// is very large; such a timeout is effectively infinite (never stale).
//slither-disable-next-line timestamp
if (block.timestamp > priceTimestamp + timeout) {
if (block.timestamp > priceTimestamp && block.timestamp - priceTimestamp > timeout) {
revert StalePrice(priceTimestamp, timeout);
}

Expand Down
28 changes: 28 additions & 0 deletions test/src/lib/op/LibOpFtsoCurrentPriceUsd.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {LibFork} from "test/fork/LibFork.sol";
import {BLOCK_NUMBER} from "../registry/LibFlareContractRegistry.t.sol";
import {InactiveFtso, PriceNotFinalized, StalePrice, DecimalsTooLarge, InconsistentFtso} from "src/err/ErrFtso.sol";
import {LibDecimalFloat, Float} from "rain-math-float-0.1.1/src/lib/LibDecimalFloat.sol";
import {LibFtsoCurrentPriceUsd} from "src/lib/price/LibFtsoCurrentPriceUsd.sol";
import {
NegativeFixedDecimalConversion,
LossyConversionFromFloat
Expand Down Expand Up @@ -237,6 +238,33 @@ contract LibOpFtsoCurrentPriceUsdTest is FtsoTest {
this.externalRun(operand, inputs);
}

/// A timeout so large that priceTimestamp + timeout overflows uint256 must not
/// panic. The staleness check uses subtraction instead of addition, so an
/// overflowing timeout is treated as infinite (price is never stale).
/// Mutation: restoring the addition-based check causes Panic(0x11) here.
function testRunTimestampPlusTimeoutOverflow(string memory symbol, PriceDetails memory priceDetails) external {
vm.assume(bytes(symbol).length > 0 && bytes(symbol).length <= 31);

CurrentPrice memory currentPrice;
currentPrice.price = 1;
currentPrice.timestamp = 1;
currentPrice.decimals = 5;

vm.warp(type(uint256).max);

conformPriceDetails(priceDetails, currentPrice);
finalizePrice(priceDetails);

mockRegistry();
mockFtsoRegistry(FTSO, symbol);
activateFtso();
mockPriceDetails(priceDetails);
mockPrice(FTSO, currentPrice);

// timeout = max → priceTimestamp + timeout overflows; must not panic.
LibFtsoCurrentPriceUsd.ftsoCurrentPriceUsd(symbol, type(uint256).max);
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

/// A negative Float timeout must revert with NegativeFixedDecimalConversion.
function testRunNegativeTimeoutReverts(OperandV2 operand, string memory symbol, int256 coeff, int32 exp) external {
vm.assume(bytes(symbol).length <= 31);
Expand Down
Loading