Skip to content

Commit

Permalink
Merge pull request #32 from Ion-Protocol/hrikb/H-05
Browse files Browse the repository at this point in the history
H-05, M-05, N-21 (OZ Audit)
  • Loading branch information
HrikB committed Jan 17, 2024
2 parents 656a428 + ea5ed53 commit 7f6be8c
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 4 deletions.
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

0 comments on commit 7f6be8c

Please sign in to comment.