Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions src/lib/lts/LibFtsoV2LTS.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {IFtsoRegistry, LibFlareContractRegistry} from "../registry/LibFlareContr
import {FtsoV2Interface} from "../../vendor/flare-smart-contracts-v2/userInterfaces/LTS/FtsoV2Interface.sol";
import {StalePrice} from "../../err/ErrFtso.sol";
import {IFeeCalculator} from "../../vendor/flare-smart-contracts-v2/userInterfaces/IFeeCalculator.sol";
import {LibDecimalFloat, Float} from "rain-math-float-0.1.1/src/lib/LibDecimalFloat.sol";

/// @dev FTSO feed IDs.
/// https://dev.flare.network/ftso/feeds
Expand Down Expand Up @@ -83,9 +84,11 @@ library LibFtsoV2LTS {
/// the feed timestamp plus timeout.
/// @param feedId The bytes21 Flare V2 feed ID to read.
/// @param timeout Max age in seconds before the price is considered stale.
/// @return value The feed value in wei (1e18 fixed-point).
/// @return The feed value as a Float, tagged at this boundary with the
/// 18-decimal (wei) scale of `getFeedByIdInWei`, so callers cannot
/// reinterpret the scale.
//forge-lint: disable-next-line(mixed-case-function)
function ftsoV2LTSGetFeed(bytes21 feedId, uint256 timeout) internal returns (uint256) {
function ftsoV2LTSGetFeed(bytes21 feedId, uint256 timeout) internal returns (Float) {
// Fetch the FTSO from the registry.
FtsoV2Interface ftsoRegistry = LibFlareContractRegistry.getFtsoV2LTS();
IFeeCalculator feeCalculator = LibFlareContractRegistry.getFeeCalculator();
Expand All @@ -107,6 +110,6 @@ library LibFtsoV2LTS {
revert StalePrice(timestamp, timeout);
}

return value;
return LibDecimalFloat.fromFixedDecimalLosslessPacked(value, 18);
}
}
3 changes: 2 additions & 1 deletion test/lib/lts/FeedConsumer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
pragma solidity =0.8.25;

import {LibFtsoV2LTS} from "src/lib/lts/LibFtsoV2LTS.sol";
import {Float} from "rain-math-float-0.1.1/src/lib/LibDecimalFloat.sol";

contract FeedConsumer {
function getFeedValue(bytes21 feedId, uint256 timeout) external payable returns (uint256) {
function getFeedValue(bytes21 feedId, uint256 timeout) external payable returns (Float) {
return LibFtsoV2LTS.ftsoV2LTSGetFeed(feedId, timeout);
}
}
10 changes: 10 additions & 0 deletions test/src/lib/flreth/LibDineroFlrEth.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,16 @@ contract LibDineroFlrEthTest is Test {
assertEq(rate18, 0.989103076939285809e18);
}

function testFlrEthRateScaleBand() external view {
uint256 ethPerFlrEth = LibDineroFlrEth.getETHPerFLRETH18();
assertGe(ethPerFlrEth, 0.1e18, "ETH/flrETH rate below 0.1 -- scale may have changed");
assertLe(ethPerFlrEth, 10e18, "ETH/flrETH rate above 10 -- scale may have changed");

uint256 flrEthPerEth = LibDineroFlrEth.getFLRETHPerETH18();
assertGe(flrEthPerEth, 0.1e18, "flrETH/ETH rate below 0.1 -- scale may have changed");
assertLe(flrEthPerEth, 10e18, "flrETH/ETH rate above 10 -- scale may have changed");
}

// External wrappers needed so vm.expectRevert captures the outer call frame
// (not the inner LSTPerToken/tokensPerLST staticcall which returns, not reverts).
function _callGetETHPerFLRETH18() external {
Expand Down
26 changes: 18 additions & 8 deletions test/src/lib/lts/LibFtsoV2LTS.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,23 @@ import {IGoverned, IGovernanceSettings} from "src/interface/IGoverned.sol";
import {IGovernedFeeCalculator} from "src/interface/IGovernedFeeCalculator.sol";
import {StalePrice} from "src/err/ErrFtso.sol";
import {FeedConsumer} from "test/lib/lts/FeedConsumer.sol";
import {LibDecimalFloat, Float} from "rain-math-float-0.1.1/src/lib/LibDecimalFloat.sol";

contract LibFtsoV2LTSTest is Test {
function testFtsoV2LTSGetFeed() external {
vm.createSelectFork(LibFork.rpcUrlFlare(vm), BLOCK_NUMBER);

uint256 feedValue = LibFtsoV2LTS.ftsoV2LTSGetFeed(ETH_USD_FEED_ID, 3600);
assertEq(feedValue, 2522.575e18);
Float feedValue = LibFtsoV2LTS.ftsoV2LTSGetFeed(ETH_USD_FEED_ID, 3600);
assertEq(LibDecimalFloat.toFixedDecimalLossless(feedValue, 18), 2522.575e18);
}

function testLtsFeedScaleBand() external {
vm.createSelectFork(LibFork.rpcUrlFlare(vm), BLOCK_NUMBER);

Float feedValue = LibFtsoV2LTS.ftsoV2LTSGetFeed(ETH_USD_FEED_ID, 3600);
uint256 feedValueWei = LibDecimalFloat.toFixedDecimalLossless(feedValue, 18);
assertGe(feedValueWei, 1e18, "ETH/USD LTS feed below 1 -- scale may have changed");
assertLe(feedValueWei, 1e24, "ETH/USD LTS feed above 1e6 -- scale may have changed");
}

function testFtsoV2LTSGetFeedStale() external {
Expand All @@ -37,7 +47,7 @@ contract LibFtsoV2LTSTest is Test {

FeedConsumer feedConsumer = new FeedConsumer();
vm.warp(1729795768 + 3600);
uint256 value = feedConsumer.getFeedValue(ETH_USD_FEED_ID, 3600);
uint256 value = LibDecimalFloat.toFixedDecimalLossless(feedConsumer.getFeedValue(ETH_USD_FEED_ID, 3600), 18);
assertGt(value, 0);
}

Expand Down Expand Up @@ -68,7 +78,7 @@ contract LibFtsoV2LTSTest is Test {

FeedConsumer feedConsumer = new FeedConsumer();
vm.warp(1729795768);
uint256 value = feedConsumer.getFeedValue(ETH_USD_FEED_ID, 0);
uint256 value = LibDecimalFloat.toFixedDecimalLossless(feedConsumer.getFeedValue(ETH_USD_FEED_ID, 0), 18);
assertGt(value, 0);
}

Expand Down Expand Up @@ -116,15 +126,15 @@ contract LibFtsoV2LTSTest is Test {

vm.deal(alice, fee);
assertEq(alice.balance, fee);
uint256 feedValue = feedConsumer.getFeedValue{value: alice.balance}(ETH_USD_FEED_ID, 3600);
assertEq(feedValue, 2522.575e18);
Float feedValue = feedConsumer.getFeedValue{value: alice.balance}(ETH_USD_FEED_ID, 3600);
assertEq(LibDecimalFloat.toFixedDecimalLossless(feedValue, 18), 2522.575e18);
assertEq(alice.balance, 0);

// #56 — overpayment: surplus is stranded in the consumer (documents current behavior;
// replace with refund assertion when a refund mechanism is added).
vm.deal(alice, uint256(fee) + 1 ether);
uint256 overpaidValue = feedConsumer.getFeedValue{value: alice.balance}(ETH_USD_FEED_ID, 3600);
assertEq(overpaidValue, 2522.575e18);
Float overpaidValue = feedConsumer.getFeedValue{value: alice.balance}(ETH_USD_FEED_ID, 3600);
assertEq(LibDecimalFloat.toFixedDecimalLossless(overpaidValue, 18), 2522.575e18);
assertEq(address(feedConsumer).balance, 1 ether);
assertEq(alice.balance, 0);
}
Expand Down
6 changes: 6 additions & 0 deletions test/src/lib/sflr/LibSceptreStakedFlare.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ contract LibSceptreStakedFlareTest is Test {
assertEq(rate18, 0.877817288626455057e18);
}

function testSflrRateScaleBand() external view {
uint256 rate18 = LibSceptreStakedFlare.getSFLRPerFLR18();
assertGe(rate18, 0.1e18, "sFLR/FLR rate below 0.1 -- scale may have changed");
assertLe(rate18, 10e18, "sFLR/FLR rate above 10 -- scale may have changed");
}

function testGetSFLRPerFLR18ZeroReverts() external {
vm.mockCall(
address(SFLR_CONTRACT),
Expand Down
Loading