Skip to content

Commit

Permalink
Merge pull request #151 from gildlab/2024-10-27-groom
Browse files Browse the repository at this point in the history
2024 10 27 groom
  • Loading branch information
thedavidmeister authored Oct 27, 2024
2 parents 226ce46 + 9536e9c commit 66ddada
Show file tree
Hide file tree
Showing 20 changed files with 328 additions and 330 deletions.
292 changes: 148 additions & 144 deletions .gas-snapshot

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions .github/workflows/rainix.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,5 @@ jobs:
env:
ETH_RPC_URL: ${{ secrets.CI_DEPLOY_RPC_URL }}
ETHERSCAN_API_KEY: ${{ secrets.EXPLORER_VERIFICATION_KEY }}
RPC_URL_FLARE_FORK: ${{ secrets.RPC_URL_FLARE_FORK }}
run: nix develop -c ${{ matrix.task }}
2 changes: 0 additions & 2 deletions script/Deploy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
pragma solidity =0.8.25;

import {Script} from "forge-std/Script.sol";
import {OwnableOracle} from "src/concrete/oracle/OwnableOracle.sol";
import {
ERC20PriceOracleReceiptVault,
ERC20PriceOracleVaultConfig
Expand Down Expand Up @@ -33,7 +32,6 @@ contract Deploy is Script {
function deployImplementations(uint256 deploymentKey) internal {
vm.startBroadcast(deploymentKey);

new OwnableOracle();
ReceiptContract receipt = new ReceiptContract();
ReceiptVaultConstructionConfig memory receiptVaultConstructionConfig = ReceiptVaultConstructionConfig({
factory: ICloneableFactoryV2(vm.envAddress("CLONE_FACTORY")),
Expand Down
24 changes: 24 additions & 0 deletions src/abstract/PriceOracleV2.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// SPDX-License-Identifier: LicenseRef-DCL-1.0
// SPDX-FileCopyrightText: Copyright (c) 2020 thedavidmeister
pragma solidity =0.8.25;

import {IPriceOracleV2} from "../interface/IPriceOracleV2.sol";
import {Address} from "openzeppelin-contracts/contracts/utils/Address.sol";

abstract contract PriceOracleV2 is IPriceOracleV2 {
/// Hook for implementing contracts to define their own price logic.
function _price() internal virtual returns (uint256);

/// @inheritdoc IPriceOracleV2
function price() external payable override returns (uint256) {
uint256 val = _price();
Address.sendValue(payable(msg.sender), address(this).balance);
return val;
}

/// Need to accept refunds from the oracle.
fallback() external payable {}

/// Need to accept refunds from the oracle.
receive() external payable {}
}
19 changes: 5 additions & 14 deletions src/concrete/oracle/FtsoV2LTSFeedOracle.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,15 @@
// SPDX-FileCopyrightText: Copyright (c) 2020 thedavidmeister
pragma solidity =0.8.25;

import {IPriceOracleV2} from "../../interface/IPriceOracleV2.sol";
import {PriceOracleV2} from "../../abstract/PriceOracleV2.sol";
import {LibFtsoV2LTS} from "rain.flare/lib/lts/LibFtsoV2LTS.sol";
import {Address} from "openzeppelin-contracts/contracts/utils/Address.sol";

struct FtsoV2LTSFeedOracleConfig {
bytes21 feedId;
uint256 staleAfter;
}

contract FtsoV2LTSFeedOracle is IPriceOracleV2 {
contract FtsoV2LTSFeedOracle is PriceOracleV2 {
event Construction(address sender, FtsoV2LTSFeedOracleConfig config);

bytes21 public immutable iFeedId;
Expand All @@ -24,16 +23,8 @@ contract FtsoV2LTSFeedOracle is IPriceOracleV2 {
emit Construction(msg.sender, config);
}

/// @inheritdoc IPriceOracleV2
function price() external payable override returns (uint256) {
uint256 val = LibFtsoV2LTS.ftsoV2LTSGetFeed(iFeedId, iStaleAfter);
Address.sendValue(payable(msg.sender), address(this).balance);
return val;
/// @inheritdoc PriceOracleV2
function _price() internal virtual override returns (uint256) {
return LibFtsoV2LTS.ftsoV2LTSGetFeed(iFeedId, iStaleAfter);
}

/// Need to accept refunds from the oracle.
fallback() external payable {}

/// Need to accept refunds from the oracle.
receive() external payable {}
}
32 changes: 0 additions & 32 deletions src/concrete/oracle/OwnableOracle.sol

This file was deleted.

19 changes: 5 additions & 14 deletions src/concrete/oracle/SceptreStakedFlrOracle.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,12 @@
// SPDX-FileCopyrightText: Copyright (c) 2020 thedavidmeister
pragma solidity =0.8.25;

import {IPriceOracleV2} from "../../interface/IPriceOracleV2.sol";
import {Address} from "openzeppelin-contracts/contracts/utils/Address.sol";
import {PriceOracleV2} from "src/abstract/PriceOracleV2.sol";
import {LibSceptreStakedFlare} from "rain.flare/lib/sflr/LibSceptreStakedFlare.sol";

contract SceptreStakedFlrOracle is IPriceOracleV2 {
/// @inheritdoc IPriceOracleV2
function price() external payable override returns (uint256) {
uint256 val = LibSceptreStakedFlare.getSFLRPerFLR18();
Address.sendValue(payable(msg.sender), address(this).balance);
return val;
contract SceptreStakedFlrOracle is PriceOracleV2 {
/// @inheritdoc PriceOracleV2
function _price() internal view override returns (uint256) {
return LibSceptreStakedFlare.getSFLRPerFLR18();
}

/// Need to accept refunds from the oracle.
fallback() external payable {}

/// Need to accept refunds from the oracle.
receive() external payable {}
}
52 changes: 0 additions & 52 deletions src/concrete/oracle/TwoPriceOracle.sol

This file was deleted.

19 changes: 5 additions & 14 deletions src/concrete/oracle/TwoPriceOracleV2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@
// SPDX-FileCopyrightText: Copyright (c) 2020 thedavidmeister
pragma solidity =0.8.25;

import {IPriceOracleV2} from "../../interface/IPriceOracleV2.sol";
import {
LibFixedPointDecimalArithmeticOpenZeppelin,
Math
} from "rain.math.fixedpoint/lib/LibFixedPointDecimalArithmeticOpenZeppelin.sol";
import {Address} from "openzeppelin-contracts/contracts/utils/Address.sol";
import {PriceOracleV2, IPriceOracleV2} from "src/abstract/PriceOracleV2.sol";

/// Construction config for `TwoPriceOracle`.
/// @param base The base price of the merged pair, will be the numerator.
Expand All @@ -25,7 +24,7 @@ struct TwoPriceOracleConfigV2 {
///
/// For example, an ETH/USD (base) and an XAU/USD (quote) price can be combined
/// to a single ETH/XAU price as (ETH/USD) / (XAU/USD).
contract TwoPriceOracleV2 is IPriceOracleV2 {
contract TwoPriceOracleV2 is PriceOracleV2 {
using LibFixedPointDecimalArithmeticOpenZeppelin for uint256;

/// Emitted upon deployment and construction.
Expand All @@ -46,23 +45,15 @@ contract TwoPriceOracleV2 is IPriceOracleV2 {
/// Calculates the price as `base / quote` using fixed point 18 decimal math.
/// Round UP to avoid edge cases that could return `0` which is disallowed
/// by `IPriceOracleV2` despite compliant sub-oracles.
/// @inheritdoc IPriceOracleV2
function price() external payable override returns (uint256) {
/// @inheritdoc PriceOracleV2
function _price() internal virtual override returns (uint256) {
// This contract is never intended to hold gas, it's only here to pay the
// oracles that might need to be paid.
// This means the slither detector here is a false positive.
//slither-disable-next-line arbitrary-send-eth
uint256 quotePrice = quote.price{value: address(this).balance}();
//slither-disable-next-line arbitrary-send-eth
uint256 basePrice = base.price{value: address(this).balance}();
uint256 val = basePrice.fixedPointDiv(quotePrice, Math.Rounding.Up);
Address.sendValue(payable(msg.sender), address(this).balance);
return val;
return basePrice.fixedPointDiv(quotePrice, Math.Rounding.Up);
}

/// Need to accept refunds from the oracle.
fallback() external payable {}

/// Need to accept refunds from the oracle.
receive() external payable {}
}
File renamed without changes.
2 changes: 1 addition & 1 deletion test/abstract/ERC20PriceOracleReceiptVaultTest.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
} from "src/concrete/vault/ERC20PriceOracleReceiptVault.sol";
import {LibERC20PriceOracleReceiptVaultCreator} from "../lib/LibERC20PriceOracleReceiptVaultCreator.sol";
import {Receipt as ReceiptContract} from "src/concrete/receipt/Receipt.sol";
import {TwoPriceOracle, TwoPriceOracleConfig} from "src/concrete/oracle/TwoPriceOracle.sol";
import {TwoPriceOracleV2, TwoPriceOracleConfigV2} from "src/concrete/oracle/TwoPriceOracleV2.sol";
import {IERC20} from "forge-std/interfaces/IERC20.sol";
import {IPriceOracleV2} from "src/interface/IPriceOracleV2.sol";

Expand Down
47 changes: 47 additions & 0 deletions test/src/abstract/PriceOracleV2.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// SPDX-License-Identifier: LicenseRef-DCL-1.0
// SPDX-FileCopyrightText: Copyright (c) 2020 thedavidmeister
pragma solidity =0.8.25;

import {PriceOracleV2} from "src/abstract/PriceOracleV2.sol";
import {Test} from "forge-std/Test.sol";

contract PriceOracleV2TestImpl is PriceOracleV2 {
uint256 internal sPrice;

constructor(uint256 price) {
sPrice = price;
}

function _price() internal view override returns (uint256) {
return sPrice;
}

function setPrice(uint256 price) external {
sPrice = price;
}
}

contract PriceOracleV2Test is Test {
address constant ALICE = address(uint160(uint256(keccak256("ALICE"))));

function testPriceOracleV2(uint256 priceA, uint256 priceB) external {
PriceOracleV2TestImpl oracle = new PriceOracleV2TestImpl(priceA);

vm.prank(ALICE);
assertEq(oracle.price(), priceA);

oracle.setPrice(priceB);

vm.prank(ALICE);
assertEq(oracle.price(), priceB);
}

function testPriceOracleV2Refund(uint256 priceA) external {
PriceOracleV2TestImpl oracle = new PriceOracleV2TestImpl(priceA);

vm.deal(ALICE, 1e18);
vm.prank(ALICE);
assertEq(oracle.price{value: 1e18}(), priceA);
assertEq(ALICE.balance, 1e18);
}
}
87 changes: 87 additions & 0 deletions test/src/concrete/oracle/FtsoV2LTSFeedOracle.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// SPDX-License-Identifier: LicenseRef-DCL-1.0
// SPDX-FileCopyrightText: Copyright (c) 2020 thedavidmeister
pragma solidity =0.8.25;

import {Test, console2} from "forge-std/Test.sol";
import {FtsoV2LTSFeedOracle, FtsoV2LTSFeedOracleConfig} from "src/concrete/oracle/FtsoV2LTSFeedOracle.sol";
import {FLR_USD_FEED_ID} from "rain.flare/lib/lts/LibFtsoV2LTS.sol";
import {LibFork} from "rain.flare/../test/fork/LibFork.sol";
import {StalePrice} from "rain.flare/err/ErrFtso.sol";
import {IFeeCalculator} from "flare-smart-contracts-v2/userInterfaces/IFeeCalculator.sol";
import {LibFlareContractRegistry} from "rain.flare/lib/registry/LibFlareContractRegistry.sol";
import {IGoverned, IGovernanceSettings} from "rain.flare/interface/IGoverned.sol";
import {IGovernedFeeCalculator} from "rain.flare/interface/IGovernedFeeCalculator.sol";

uint256 constant BLOCK_NUMBER = 31993648;

contract FtsoV2LTSFeedOracleTest is Test {
address constant ALICE = address(uint160(uint256(keccak256("ALICE"))));

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

FtsoV2LTSFeedOracle oracle =
new FtsoV2LTSFeedOracle(FtsoV2LTSFeedOracleConfig({feedId: FLR_USD_FEED_ID, staleAfter: 60}));

vm.prank(ALICE);
assertEq(oracle.price(), 0.0141082e18);
}

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

FtsoV2LTSFeedOracle oracle =
new FtsoV2LTSFeedOracle(FtsoV2LTSFeedOracleConfig({feedId: FLR_USD_FEED_ID, staleAfter: 60}));

uint256 feedTime = 1730042030;

vm.warp(block.timestamp + 61);
vm.prank(ALICE);
vm.expectRevert(abi.encodeWithSelector(StalePrice.selector, feedTime, 60));
oracle.price();
}

/// forge-config: default.fuzz.runs = 1
function testFtsoV2LTSFeedOraclePaid(uint128 fee) external {
vm.assume(fee > 0);
vm.createSelectFork(LibFork.rpcUrlFlare(vm), BLOCK_NUMBER);

uint256 timelock;
{
IFeeCalculator feeCalculator = LibFlareContractRegistry.getFeeCalculator();
address gov = IGoverned(address(feeCalculator)).governance();
IGovernanceSettings govSettings = IGoverned(address(feeCalculator)).governanceSettings();
address[] memory executors = govSettings.getExecutors();
timelock = govSettings.getTimelock();
vm.prank(gov);
bytes21[] memory feeds = new bytes21[](1);
feeds[0] = bytes21(FLR_USD_FEED_ID);
uint256[] memory fees = new uint256[](1);
fees[0] = fee;
bytes4 setFeedsFeesSelector = bytes4(0x755fcecd);
IGovernedFeeCalculator(address(feeCalculator)).setFeedsFees(feeds, fees);
vm.warp(block.timestamp + timelock);
vm.prank(executors[0]);
IGoverned(address(feeCalculator)).executeGovernanceCall(setFeedsFeesSelector);
}

FtsoV2LTSFeedOracle oracle =
new FtsoV2LTSFeedOracle(FtsoV2LTSFeedOracleConfig({feedId: FLR_USD_FEED_ID, staleAfter: 60 + timelock}));

vm.deal(ALICE, fee);
vm.prank(ALICE);
assertEq(oracle.price{value: fee}(), 0.0141082e18);
assertEq(ALICE.balance, 0);

vm.deal(ALICE, fee + 5);
vm.prank(ALICE);
assertEq(oracle.price{value: fee + 5}(), 0.0141082e18);
assertEq(ALICE.balance, 5);

vm.deal(ALICE, fee);
vm.prank(ALICE);
// Out of funds here due to insufficient value for fee.
vm.expectRevert();
oracle.price{value: fee - 1}();
}
}
Loading

0 comments on commit 66ddada

Please sign in to comment.