Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

H-05, M-05, N-21 (OZ Audit) #32

Merged
merged 1 commit into from
Jan 17, 2024
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/oracles/reserve/ReserveOracle.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { WadRayMath } from "src/libraries/math/WadRayMath.sol";

// should equal to the number of feeds available in the contract
uint8 constant MAX_FEED_COUNT = 3;
uint256 constant UPDATE_COOLDOWN = 1 hours;

abstract contract ReserveOracle {
using SafeCast for *;
Expand All @@ -22,6 +23,7 @@ abstract contract ReserveOracle {
IReserveFeed public immutable FEED2;

uint256 public currentExchangeRate; // [wad] the bounded queried last time
uint256 public lastUpdated; // [wad] the bounded queried last time

// --- Events ---
event UpdateExchangeRate(uint256 exchangeRate);
Expand All @@ -30,6 +32,7 @@ abstract contract ReserveOracle {
error InvalidQuorum(uint8 quorum);
error InvalidFeedLength(uint256 length);
error InvalidInitialization(uint256 exchangeRate);
error UpdateCooldown(uint256 lastUpdated);

// --- Override ---
function _getProtocolExchangeRate() internal view virtual returns (uint256);
Expand Down Expand Up @@ -93,6 +96,8 @@ abstract contract ReserveOracle {
// then bounds it up to the maximum change and writes the bounded value to the state.
// NOTE: keepers should call this update to reflect recent values
function updateExchangeRate() public {
if (block.timestamp - lastUpdated < UPDATE_COOLDOWN) revert UpdateCooldown(lastUpdated);

uint256 _currentExchangeRate = currentExchangeRate;

uint256 minimum = Math.min(_getProtocolExchangeRate(), _aggregate(ILK_INDEX));
Expand All @@ -101,6 +106,8 @@ abstract contract ReserveOracle {
uint256 bounded = _bound(minimum, _currentExchangeRate - diff, _currentExchangeRate + diff);
currentExchangeRate = bounded;

lastUpdated = block.timestamp;

emit UpdateExchangeRate(bounded);
}
}
12 changes: 8 additions & 4 deletions src/oracles/spot/EthXSpotOracle.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ import { IChainlink } from "src/interfaces/IChainlink.sol";
import { WadRayMath } from "src/libraries/math/WadRayMath.sol";

interface IRedstonePriceFeed {
function latestAnswer() external view returns (int256 answer);
function latestRoundData()
external
view
returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
}

uint8 constant REDSTONE_DECIMALS = 8;
Expand All @@ -31,15 +34,16 @@ contract EthXSpotOracle is SpotOracle {
USD_PER_ETH_CHAINLINK = IChainlink(_usdPerEthChainlink);
}

// @notice Gets the price of ETHx in ETH.
// @notice Gets the price of ETHx in ETH.
// @dev redstone oracle returns dollar value per ETHx with 6 decimals.
// This needs to be converted to [wad] and to ETH denomination.
// @return ethPerEthX price of ETHx in ETH [wad]
// @return ethPerEthX price of ETHx in ETH [wad]
function getPrice() public view override returns (uint256 ethPerEthX) {
// get price from the protocol feed
// usd per ETHx
(, int256 answer,,,) = REDSTONE_ETHX_PRICE_FEED.latestRoundData();

uint256 usdPerEthX = uint256(REDSTONE_ETHX_PRICE_FEED.latestAnswer()).scaleUpToWad(REDSTONE_DECIMALS); //
uint256 usdPerEthX = uint256(answer).scaleUpToWad(REDSTONE_DECIMALS); //

// usd per ETH
(, int256 _usdPerEth,,,) = USD_PER_ETH_CHAINLINK.latestRoundData(); // price of stETH denominated in ETH
Expand Down
20 changes: 20 additions & 0 deletions test/fork/concrete/EthXReserveOracleFork.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,33 @@ import { EthXReserveOracle } from "src/oracles/reserve/EthXReserveOracle.sol";
import { ReserveFeed } from "src/oracles/reserve/ReserveFeed.sol";
import { IStaderStakePoolsManager } from "src/interfaces/ProviderInterfaces.sol";
import { WadRayMath, RAY } from "src/libraries/math/WadRayMath.sol";
import { ReserveOracle } from "../../../src/oracles/reserve/ReserveOracle.sol";

import { ReserveOracleSharedSetup } from "test/helpers/ReserveOracleSharedSetup.sol";

contract EthXReserveOracleForkTest is ReserveOracleSharedSetup {
using WadRayMath for *;

// --- ETHx Reserve Oracle Test ---

function test_RevertWhen_UpdateIsOnCooldown() public {
uint256 maxChange = 3e25; // 0.03 3%
address[] memory feeds = new address[](3);
uint8 quorum = 0;
EthXReserveOracle ethXReserveOracle = new EthXReserveOracle(
STADER_STAKE_POOLS_MANAGER,
ETHX_ILK_INDEX,
feeds,
quorum,
maxChange
);

ethXReserveOracle.updateExchangeRate();

vm.expectRevert(abi.encodeWithSelector(ReserveOracle.UpdateCooldown.selector, block.timestamp));
ethXReserveOracle.updateExchangeRate();
}

function test_EthXReserveOracleGetProtocolExchangeRate() public {
uint256 maxChange = 3e25; // 0.03 3%
address[] memory feeds = new address[](3);
Expand Down
20 changes: 20 additions & 0 deletions test/fork/concrete/SwEthReserveOracleFork.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,31 @@ import { SwEthReserveOracle } from "src/oracles/reserve/SwEthReserveOracle.sol";
import { ReserveFeed } from "src/oracles/reserve/ReserveFeed.sol";
import { ISwEth } from "src/interfaces/ProviderInterfaces.sol";
import { RAY } from "src/libraries/math/WadRayMath.sol";
import { ReserveOracle } from "../../../src/oracles/reserve/ReserveOracle.sol";

import { ReserveOracleSharedSetup } from "test/helpers/ReserveOracleSharedSetup.sol";

contract SwEthReserveOracleForkTest is ReserveOracleSharedSetup {
// --- swETH Reserve Oracle Test ---

function test_RevertWhen_UpdateIsOnCooldown() public {
uint256 maxChange = 3e25; // 0.03 3%
address[] memory feeds = new address[](3);
uint8 quorum = 0;
SwEthReserveOracle swEthReserveOracle = new SwEthReserveOracle(
SWETH,
SWETH_ILK_INDEX,
feeds,
quorum,
maxChange
);

swEthReserveOracle.updateExchangeRate();

vm.expectRevert(abi.encodeWithSelector(ReserveOracle.UpdateCooldown.selector, block.timestamp));
swEthReserveOracle.updateExchangeRate();
}

function test_SwEthReserveOracleGetProtocolExchangeRate() public {
uint256 maxChange = 3e25; // 0.03 3%
uint8 quorum = 0;
Expand Down
13 changes: 13 additions & 0 deletions test/fork/concrete/WstEthReserveOracleFork.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,19 @@ import { ReserveOracleSharedSetup } from "test/helpers/ReserveOracleSharedSetup.
contract WstEthReserveOracleForkTest is ReserveOracleSharedSetup {
// --- stETH Reserve Oracle Test ---

function test_RevertWhen_UpdateIsOnCooldown() public {
uint256 maxChange = 3e25; // 0.03 3%
address[] memory feeds = new address[](3);
uint8 quorum = 0;
WstEthReserveOracle wstEthReserveOracle =
new WstEthReserveOracle(WSTETH, STETH_ILK_INDEX, feeds, quorum, maxChange);

wstEthReserveOracle.updateExchangeRate();

vm.expectRevert(abi.encodeWithSelector(ReserveOracle.UpdateCooldown.selector, block.timestamp));
wstEthReserveOracle.updateExchangeRate();
}

function test_WstEthReserveOracleGetProtocolExchangeRate() public {
uint256 maxChange = 3e25; // 0.03 3%
address[] memory feeds = new address[](3);
Expand Down