Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion src/interface/IDineroFlrEth.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ interface IDineroFlrEth {
//slither-disable-start naming-convention
//forge-lint: disable-next-line(mixed-case-function)
function LSTPerToken() external view returns (uint256 ethAmount);
//slither-disable-end
//slither-disable-end naming-convention

//forge-lint: disable-next-line(mixed-case-function)
function tokensPerLST() external view returns (uint256 tokenAmount);
Expand Down
7 changes: 5 additions & 2 deletions src/lib/lts/LibFtsoV2LTS.sol
Original file line number Diff line number Diff line change
Expand Up @@ -101,11 +101,14 @@ library LibFtsoV2LTS {

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

// Handle stale prices.
//slither-disable-next-line timestamp
// Handle stale prices. Comparing against block.timestamp IS the
// staleness check, so the lint's manipulation concern is the behavior.
//slither-disable-start timestamp
//forge-lint: disable-next-line(block-timestamp)
if (block.timestamp > timestamp + timeout) {
revert StalePrice(timestamp, timeout);
}
//slither-disable-end timestamp

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

// Handle stale prices.
//slither-disable-next-line timestamp
// Handle stale prices. Comparing against block.timestamp IS the
// staleness check, so the lint's manipulation concern is the behavior.
//slither-disable-start timestamp
//forge-lint: disable-next-line(block-timestamp)
if (block.timestamp > priceTimestamp + timeout) {
revert StalePrice(priceTimestamp, timeout);
}
//slither-disable-end timestamp

return (price, decimals);
}
Expand Down
2 changes: 1 addition & 1 deletion test/fork/ForkConstants.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
pragma solidity =0.8.25;

/// @dev Block number used for Flare mainnet fork tests (FtsoRegistry, sflr, LTS, USD price pair).
uint256 constant BLOCK_NUMBER = 31843105;
uint256 constant BLOCK_NUMBER = 37796420;

/// @dev Block number used for Flare mainnet fork tests involving the flrETH contract,
/// which was deployed later than the feeds used by BLOCK_NUMBER.
Expand Down
12 changes: 3 additions & 9 deletions test/src/concrete/FlareFtsoWords.ftsoCurrentPricePair.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,7 @@ contract FlareFtsoWordsFtsoCurrentPricePairTest is OpTest {
StackItem[] memory expectedStack = new StackItem[](1);
expectedStack[0] = StackItem.wrap(
Float.unwrap(
LibDecimalFloat.packLossless(
0.03731119849395329429139187416029293517999955454915312408433138156716e68, -68
)
LibDecimalFloat.packLossless(2780193435782859109269589980156436027045273051983740074270490015516, -68)
)
);

Expand All @@ -47,9 +45,7 @@ contract FlareFtsoWordsFtsoCurrentPricePairTest is OpTest {
StackItem[] memory expectedStack = new StackItem[](1);
expectedStack[0] = StackItem.wrap(
Float.unwrap(
LibDecimalFloat.packLossless(
0.000005630014253715341229403249093895330805483328231149412663808026269472e72, -72
)
LibDecimalFloat.packLossless(7471283741695112996296236583567719364072468887361328723293549999340, -72)
)
);

Expand All @@ -67,9 +63,7 @@ contract FlareFtsoWordsFtsoCurrentPricePairTest is OpTest {

expectedStack[0] = StackItem.wrap(
Float.unwrap(
LibDecimalFloat.packLossless(
177619.4437412095639943741209563994374120956399437412095639943741209e61, -61
)
LibDecimalFloat.packLossless(13384580676802354095144678764100049043648847474252084355076017655713, -62)
)
);
checkHappy(
Expand Down
2 changes: 1 addition & 1 deletion test/src/concrete/FlareFtsoWords.ftsoCurrentPriceUsd.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ contract FlareFtsoWordsFtsoCurrentPriceUsdTest is OpTest {
FlareFtsoWords flareFtsoWords = new FlareFtsoWords();

StackItem[] memory expectedStack = new StackItem[](1);
expectedStack[0] = StackItem.wrap(Float.unwrap(LibDecimalFloat.packLossless(2525.74849e5, -5)));
expectedStack[0] = StackItem.wrap(Float.unwrap(LibDecimalFloat.packLossless(2729.116e5, -5)));

checkHappy(
bytes(
Expand Down
5 changes: 5 additions & 0 deletions test/src/concrete/FlareFtsoWords.pointers.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,13 @@ contract FlareFtsoWordsPointersTest is Test {
function testAuthoringMetaContent() external pure {
AuthoringMetaV2[] memory m = abi.decode(LibFlareFtsoSubParser.authoringMetaV2(), (AuthoringMetaV2[]));
assertEq(m.length, SUB_PARSER_WORD_PARSERS_LENGTH);
// The word names are ASCII literals well under 32 bytes, so bytes32()
// is the encoding being asserted rather than a truncating cast.
//forge-lint: disable-next-line(unsafe-typecast)
assertEq(m[SUB_PARSER_WORD_FTSO_CURRENT_PRICE_USD].word, bytes32("ftso-current-price-usd"));
//forge-lint: disable-next-line(unsafe-typecast)
assertEq(m[SUB_PARSER_WORD_FTSO_CURRENT_PRICE_PAIR].word, bytes32("ftso-current-price-pair"));
//forge-lint: disable-next-line(unsafe-typecast)
assertEq(m[SUB_PARSER_WORD_SFLR_EXCHANGE_RATE].word, bytes32("sflr-exchange-rate"));
assertEq(
m[SUB_PARSER_WORD_FTSO_CURRENT_PRICE_USD].description,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ contract FlareSflrCurrentExchangeRateTest is OpTest {
FlareFtsoWords flareFtsoWords = new FlareFtsoWords();

StackItem[] memory expectedStack = new StackItem[](1);
expectedStack[0] = StackItem.wrap(Float.unwrap(LibDecimalFloat.packLossless(0.877817288626455057e18, -18)));
expectedStack[0] = StackItem.wrap(Float.unwrap(LibDecimalFloat.packLossless(0.775006975937778042e18, -18)));

checkHappy(
bytes(
Expand Down
5 changes: 5 additions & 0 deletions test/src/concrete/FlareFtsoWords.wordOpcodeAlignment.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,13 @@ contract FlareFtsoWordsWordOpcodeAlignmentTest is Test {

function testAuthoringMetaWordNamesMatchOpcodeSlots() external pure {
AuthoringMetaV2[] memory m = abi.decode(LibFlareFtsoSubParser.authoringMetaV2(), (AuthoringMetaV2[]));
// The word names are ASCII literals well under 32 bytes, so bytes32()
// is the encoding being asserted rather than a truncating cast.
//forge-lint: disable-next-line(unsafe-typecast)
assertEq(m[OPCODE_FTSO_CURRENT_PRICE_USD].word, bytes32("ftso-current-price-usd"));
//forge-lint: disable-next-line(unsafe-typecast)
assertEq(m[OPCODE_FTSO_CURRENT_PRICE_PAIR].word, bytes32("ftso-current-price-pair"));
//forge-lint: disable-next-line(unsafe-typecast)
assertEq(m[OPCODE_SFLR_CURRENT_EXCHANGE_RATE].word, bytes32("sflr-exchange-rate"));
}
}
4 changes: 2 additions & 2 deletions test/src/lib/flreth/LibDineroFlrEth.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ contract LibDineroFlrEthTest is Test {

// External wrappers needed so vm.expectRevert captures the outer call frame
// (not the inner LSTPerToken/tokensPerLST staticcall which returns, not reverts).
function _callGetETHPerFLRETH18() external {
function _callGetETHPerFLRETH18() external view {
LibDineroFlrEth.getETHPerFLRETH18();
}

function _callGetFLRETHPerETH18() external {
function _callGetFLRETHPerETH18() external view {
LibDineroFlrEth.getFLRETHPerETH18();
}

Expand Down
31 changes: 18 additions & 13 deletions test/src/lib/lts/LibFtsoV2LTS.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ contract LibFtsoV2LTSTest is Test {
vm.createSelectFork(LibFork.rpcUrlFlare(vm), BLOCK_NUMBER);

uint256 feedValue = LibFtsoV2LTS.ftsoV2LTSGetFeed(ETH_USD_FEED_ID, 3600);
assertEq(feedValue, 2522.575e18);
assertEq(feedValue, 2729.116e18);
}

function testFtsoV2LTSGetFeedStale() external {
Expand All @@ -41,7 +41,7 @@ contract LibFtsoV2LTSTest is Test {
vm.createSelectFork(LibFork.rpcUrlFlare(vm), BLOCK_NUMBER);

FeedConsumer feedConsumer = new FeedConsumer();
vm.warp(1729795768 + 3600);
vm.warp(1740156916 + 3600);
uint256 value = feedConsumer.getFeedValue(ETH_USD_FEED_ID, 3600);
assertGt(value, 0);
}
Expand All @@ -52,8 +52,8 @@ contract LibFtsoV2LTSTest is Test {
vm.createSelectFork(LibFork.rpcUrlFlare(vm), BLOCK_NUMBER);

FeedConsumer feedConsumer = new FeedConsumer();
vm.warp(1729795768 + 3600 + 1);
vm.expectRevert(abi.encodeWithSelector(StalePrice.selector, 1729795768, 3600));
vm.warp(1740156916 + 3600 + 1);
vm.expectRevert(abi.encodeWithSelector(StalePrice.selector, 1740156916, 3600));
feedConsumer.getFeedValue(ETH_USD_FEED_ID, 3600);
}

Expand All @@ -62,8 +62,8 @@ contract LibFtsoV2LTSTest is Test {
vm.createSelectFork(LibFork.rpcUrlFlare(vm), BLOCK_NUMBER);

FeedConsumer feedConsumer = new FeedConsumer();
vm.warp(1729795768 + 1);
vm.expectRevert(abi.encodeWithSelector(StalePrice.selector, 1729795768, 0));
vm.warp(1740156916 + 1);
vm.expectRevert(abi.encodeWithSelector(StalePrice.selector, 1740156916, 0));
feedConsumer.getFeedValue(ETH_USD_FEED_ID, 0);
}

Expand All @@ -72,7 +72,7 @@ contract LibFtsoV2LTSTest is Test {
vm.createSelectFork(LibFork.rpcUrlFlare(vm), BLOCK_NUMBER);

FeedConsumer feedConsumer = new FeedConsumer();
vm.warp(1729795768);
vm.warp(1740156916);
uint256 value = feedConsumer.getFeedValue(ETH_USD_FEED_ID, 0);
assertGt(value, 0);
}
Expand All @@ -91,6 +91,11 @@ contract LibFtsoV2LTSTest is Test {

address[] memory executors = govSettings.getExecutors();
uint256 timelock = govSettings.getTimelock();
// Enacting the fee change warps block.timestamp forward by the whole
// timelock, so the staleness timeout must cover that as well as the
// feed's age at the fork block, or every read below reverts StalePrice
// before any fee is charged.
uint256 timeout = timelock + 3600;

vm.prank(gov);

Expand All @@ -114,22 +119,22 @@ contract LibFtsoV2LTSTest is Test {
vm.startPrank(alice);
// OutOfFunds is an EVM error with no Solidity revert data.
vm.expectRevert(new bytes(0));
feedConsumer.getFeedValue(ETH_USD_FEED_ID, 3600);
feedConsumer.getFeedValue(ETH_USD_FEED_ID, timeout);

vm.expectRevert(new bytes(0));
feedConsumer.getFeedValue{value: alice.balance}(ETH_USD_FEED_ID, 3600);
feedConsumer.getFeedValue{value: alice.balance}(ETH_USD_FEED_ID, timeout);

vm.deal(alice, fee);
assertEq(alice.balance, fee);
uint256 feedValue = feedConsumer.getFeedValue{value: alice.balance}(ETH_USD_FEED_ID, 3600);
assertEq(feedValue, 2522.575e18);
uint256 feedValue = feedConsumer.getFeedValue{value: alice.balance}(ETH_USD_FEED_ID, timeout);
assertEq(feedValue, 2729.116e18);
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);
uint256 overpaidValue = feedConsumer.getFeedValue{value: alice.balance}(ETH_USD_FEED_ID, timeout);
assertEq(overpaidValue, 2729.116e18);
assertEq(address(feedConsumer).balance, 1 ether);
assertEq(alice.balance, 0);
}
Expand Down
5 changes: 5 additions & 0 deletions test/src/lib/lts/LibFtsoV2LTSFeedIds.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,16 @@ contract LibFtsoV2LTSFeedIdsTest is Test {
for (uint256 i = 0; i < nameBytes.length; i++) {
packed[i + 1] = nameBytes[i];
}
// `packed` is allocated at exactly 21 bytes above, so bytes21() reads
// all of it and drops nothing.
//forge-lint: disable-next-line(unsafe-typecast)
return bytes21(packed);
}

/// The category byte of a feed id is its first (most significant) byte.
function categoryByteOf(bytes21 feedId) internal pure returns (bytes1) {
// Keeping only the leading byte is the behavior this helper exists for.
//forge-lint: disable-next-line(unsafe-typecast)
return bytes1(feedId);
}

Expand Down
8 changes: 2 additions & 6 deletions test/src/lib/op/LibOpFtsoCurrentPricePair.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -252,9 +252,7 @@ contract LibOpFtsoCurrentPricePairTest is FtsoTest {
assertEq(
StackItem.unwrap(outputs[0]),
Float.unwrap(
LibDecimalFloat.packLossless(
0.03731119849395329429139187416029293517999955454915312408433138156716e68, -68
)
LibDecimalFloat.packLossless(2780193435782859109269589980156436027045273051983740074270490015516, -68)
)
);

Expand All @@ -265,9 +263,7 @@ contract LibOpFtsoCurrentPricePairTest is FtsoTest {
assertEq(
StackItem.unwrap(outputs[0]),
Float.unwrap(
LibDecimalFloat.packLossless(
26.80160488980436844683612975257089038188438152842367927140678999277e65, -65
)
LibDecimalFloat.packLossless(3596872027425730529592732591798956145506457035904666566023576865182, -65)
)
);
}
Expand Down
8 changes: 4 additions & 4 deletions test/src/lib/op/LibOpFtsoCurrentPriceUsd.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -40,24 +40,24 @@ contract LibOpFtsoCurrentPriceUsdTest is FtsoTest {
inputs[1] = StackItem.wrap(bytes32(uint256(3600)));
StackItem[] memory outputs = this.externalRun(OperandV2.wrap(0), inputs);
assertEq(outputs.length, 1);
assertEq(StackItem.unwrap(outputs[0]), Float.unwrap(LibDecimalFloat.packLossless(2525.74849e5, -5)));
assertEq(StackItem.unwrap(outputs[0]), Float.unwrap(LibDecimalFloat.packLossless(2729.116e5, -5)));

inputs[0] = StackItem.wrap(bytes32(IntOrAString.unwrap(LibIntOrAString.fromStringV3("BTC"))));
outputs = this.externalRun(OperandV2.wrap(0), inputs);
assertEq(outputs.length, 1);
assertEq(StackItem.unwrap(outputs[0]), Float.unwrap(LibDecimalFloat.packLossless(67694.11308e5, -5)));
assertEq(StackItem.unwrap(outputs[0]), Float.unwrap(LibDecimalFloat.packLossless(98162.81e5, -5)));

inputs[0] = StackItem.wrap(bytes32(IntOrAString.unwrap(LibIntOrAString.fromStringV3("XRP"))));
outputs = this.externalRun(OperandV2.wrap(0), inputs);
assertEq(outputs.length, 1);
assertEq(StackItem.unwrap(outputs[0]), Float.unwrap(LibDecimalFloat.packLossless(0.53163e5, -5)));
assertEq(StackItem.unwrap(outputs[0]), Float.unwrap(LibDecimalFloat.packLossless(2.6189e5, -5)));

// USDT is interesting as it probably has different decimals to the
// others, but should still get normalized to 18 decimals.
inputs[0] = StackItem.wrap(bytes32(IntOrAString.unwrap(LibIntOrAString.fromStringV3("USDT"))));
outputs = this.externalRun(OperandV2.wrap(0), inputs);
assertEq(outputs.length, 1);
assertEq(StackItem.unwrap(outputs[0]), Float.unwrap(LibDecimalFloat.packLossless(0.99919e5, -5)));
assertEq(StackItem.unwrap(outputs[0]), Float.unwrap(LibDecimalFloat.packLossless(0.99997e5, -5)));
}

function testRunHappy(
Expand Down
2 changes: 1 addition & 1 deletion test/src/lib/sflr/LibSceptreStakedFlare.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ contract LibSceptreStakedFlareTest is Test {

function testGetSFLRPerFLR18() external view {
uint256 rate18 = LibSceptreStakedFlare.getSFLRPerFLR18();
assertEq(rate18, 0.877817288626455057e18);
assertEq(rate18, 0.775006975937778042e18);
}

function testGetSFLRPerFLR18ZeroReverts() external {
Expand Down
Loading