diff --git a/src/generated/FlareFtsoWords.pointers.sol b/src/generated/FlareFtsoWords.pointers.sol index 6f6a67f..5a209fe 100644 --- a/src/generated/FlareFtsoWords.pointers.sol +++ b/src/generated/FlareFtsoWords.pointers.sol @@ -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(0x278f757af111d597a9a4ca99bcede20e3e1d36caa6db7521bf32b3d6d7c35b32); /// @dev The hash of the meta that describes the contract. bytes32 constant DESCRIBED_BY_META_HASH = bytes32(0x4ccb316fd35a2abbee0016f38926a4c95b6270b229c85a9acf8a3a52b36bfedc); @@ -48,13 +48,13 @@ bytes constant SUB_PARSER_WORD_PARSERS = hex"093509550967"; /// @dev Every two bytes is a function pointer for an operand handler. /// These positional indexes all map to the same indexes looked up in the parse /// meta. -bytes constant OPERAND_HANDLER_FUNCTION_POINTERS = hex"0e430e430e43"; +bytes constant OPERAND_HANDLER_FUNCTION_POINTERS = hex"0e040e040e04"; /// @dev The function pointers for the integrity check fns. -bytes constant INTEGRITY_FUNCTION_POINTERS = hex"0e200e2c0e38"; +bytes constant INTEGRITY_FUNCTION_POINTERS = hex"0de10ded0df9"; /// @dev The function pointers known to the interpreter for dynamic dispatch. /// By setting these as a constant they can be inlined into the interpreter /// and loaded at eval time for very low gas (~100) due to the compiler /// optimising it to a single `codecopy` to build the in memory bytes array. -bytes constant OPCODE_FUNCTION_POINTERS = hex"0a090ac70b1c"; +bytes constant OPCODE_FUNCTION_POINTERS = hex"0a090a880add"; diff --git a/src/lib/op/LibOpFtsoCurrentPriceUsd.sol b/src/lib/op/LibOpFtsoCurrentPriceUsd.sol index 05a8b69..f1d8e1e 100644 --- a/src/lib/op/LibOpFtsoCurrentPriceUsd.sol +++ b/src/lib/op/LibOpFtsoCurrentPriceUsd.sol @@ -5,7 +5,6 @@ pragma solidity ^0.8.19; import {OperandV2, StackItem} from "rain-interpreter-interface-0.1.0/src/interface/IInterpreterV4.sol"; import {LibIntOrAString, IntOrAString} from "rain-intorastring-0.1.0/src/lib/LibIntOrAString.sol"; import {LibFtsoCurrentPriceUsd} from "../price/LibFtsoCurrentPriceUsd.sol"; -import {DecimalsTooLarge} from "../../err/ErrFtso.sol"; import {LibDecimalFloat, Float} from "rain-math-float-0.1.1/src/lib/LibDecimalFloat.sol"; @@ -44,15 +43,10 @@ library LibOpFtsoCurrentPriceUsd { timeout := mload(add(inputs, 0x40)) } - (uint256 price, uint256 decimals) = LibFtsoCurrentPriceUsd.ftsoCurrentPriceUsd( + (uint256 price, uint8 decimals) = LibFtsoCurrentPriceUsd.ftsoCurrentPriceUsd( symbol.toStringV3(), LibDecimalFloat.toFixedDecimalLossless(timeout, 0) ); - if (decimals > type(uint8).max) { - revert DecimalsTooLarge(decimals); - } - // Check above ensures safe downcast. - //forge-lint: disable-next-line(unsafe-typecast) - Float priceFloat = LibDecimalFloat.fromFixedDecimalLosslessPacked(price, uint8(decimals)); + Float priceFloat = LibDecimalFloat.fromFixedDecimalLosslessPacked(price, decimals); StackItem[] memory outputs; assembly ("memory-safe") { diff --git a/src/lib/price/LibFtsoCurrentPriceUsd.sol b/src/lib/price/LibFtsoCurrentPriceUsd.sol index 8274572..9d90d95 100644 --- a/src/lib/price/LibFtsoCurrentPriceUsd.sol +++ b/src/lib/price/LibFtsoCurrentPriceUsd.sol @@ -3,19 +3,20 @@ pragma solidity ^0.8.19; import {IFtsoRegistry, LibFlareContractRegistry} from "../registry/LibFlareContractRegistry.sol"; -import {InactiveFtso, PriceNotFinalized, StalePrice, InconsistentFtso} from "../../err/ErrFtso.sol"; +import {InactiveFtso, PriceNotFinalized, StalePrice, InconsistentFtso, DecimalsTooLarge} from "../../err/ErrFtso.sol"; import {IFtso} from "../../vendor/flare-smart-contracts/userInterfaces/IFtso.sol"; library LibFtsoCurrentPriceUsd { /// @notice Fetches the current FTSO USD price for a symbol and returns the /// raw price together with its native decimal count. The caller is - /// responsible for normalising to 18 decimals and enforcing the - /// DecimalsTooLarge guard. + /// responsible for normalising to 18 decimals. /// @dev Reverts with InactiveFtso if the FTSO is not active. /// Reverts with PriceNotFinalized if the finalization type is not /// WEIGHTED_MEDIAN or TRUSTED_ADDRESSES. /// Reverts with InconsistentFtso if the two price reads return different /// values (indicates an FTSO bug). + /// Reverts with DecimalsTooLarge if the FTSO reports a decimal count that + /// does not fit in uint8. /// Reverts with StalePrice(priceTimestamp, timeout) if the price is older /// than timeout seconds. /// @param symbol The FTSO symbol string (e.g. "FLR", "ETH"). @@ -23,7 +24,7 @@ library LibFtsoCurrentPriceUsd { /// @return price The raw FTSO price in the FTSO's native unit. /// @return decimals The decimal precision of the returned price (typically /// 5 for Flare FTSO v1, but not guaranteed). - function ftsoCurrentPriceUsd(string memory symbol, uint256 timeout) internal view returns (uint256, uint256) { + function ftsoCurrentPriceUsd(string memory symbol, uint256 timeout) internal view returns (uint256, uint8) { // Fetch the FTSO from the registry. IFtsoRegistry ftsoRegistry = LibFlareContractRegistry.getFtsoRegistry(); IFtso ftso = ftsoRegistry.getFtsoBySymbol(symbol); @@ -61,12 +62,18 @@ library LibFtsoCurrentPriceUsd { revert InconsistentFtso(); } + if (decimals > type(uint8).max) { + revert DecimalsTooLarge(decimals); + } + // Handle stale prices. //slither-disable-next-line timestamp if (block.timestamp > priceTimestamp + timeout) { revert StalePrice(priceTimestamp, timeout); } - return (price, decimals); + // Guard above ensures safe downcast. + //forge-lint: disable-next-line(unsafe-typecast) + return (price, uint8(decimals)); } } diff --git a/test/src/lib/op/LibOpFtsoCurrentPriceUsd.t.sol b/test/src/lib/op/LibOpFtsoCurrentPriceUsd.t.sol index 29e6dca..831009a 100644 --- a/test/src/lib/op/LibOpFtsoCurrentPriceUsd.t.sol +++ b/test/src/lib/op/LibOpFtsoCurrentPriceUsd.t.sol @@ -143,6 +143,7 @@ contract LibOpFtsoCurrentPriceUsdTest is FtsoTest { ) external { vm.assume(bytes(symbol).length <= 31); uint256 intSymbol = IntOrAString.unwrap(LibIntOrAString.fromStringV3(symbol)); + currentPrice.decimals = bound(currentPrice.decimals, 0, type(uint8).max); timeout = bound(timeout, 0, uint256(int256(type(int224).max))); currentPrice.timestamp = bound(currentPrice.timestamp, 0, type(uint256).max - timeout - 1);