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

[SC-432] Add weETH oracle #22

Merged
merged 2 commits into from
May 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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions src/WEETHExchangeRateOracle.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.0;

import { IPriceSource } from "./interfaces/IPriceSource.sol";

interface IWrappedEtherfiRestakedEth {
function getRate() external view returns (uint256);
}

/**
* @title WEETHExchangeRateOracle
* @dev Provides weETH / USD by multiplying the weETH exchange rate by ETH / USD.
* This provides a "non-market" price. Any depeg event will be ignored.
*/
contract WEETHExchangeRateOracle {

/// @notice Etherfi restaked wrapped eth token contract.
IWrappedEtherfiRestakedEth public immutable weeth;

/// @notice The price source for ETH / USD.
IPriceSource public immutable ethSource;

constructor(address _weeth, address _ethSource) {
// 8 decimals required as AaveOracle assumes this
require(IPriceSource(_ethSource).decimals() == 8, "WEETHExchangeRateOracle/invalid-decimals");

weeth = IWrappedEtherfiRestakedEth(_weeth);
ethSource = IPriceSource(_ethSource);
}

function latestAnswer() external view returns (int256) {
int256 ethUsd = ethSource.latestAnswer();
int256 exchangeRate = int256(weeth.getRate());

if (ethUsd <= 0 || exchangeRate <= 0) {
return 0;
}

return exchangeRate * ethUsd / 1e18;
}

function decimals() external pure returns (uint8) {
return 8;
}

}
27 changes: 27 additions & 0 deletions test/SparkLendMainnetIntegration.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { RateTargetBaseInterestRateStrategy } from "src/RateTargetBaseInterestRa
import { RateTargetKinkInterestRateStrategy } from "src/RateTargetKinkInterestRateStrategy.sol";
import { RETHExchangeRateOracle } from "src/RETHExchangeRateOracle.sol";
import { WSTETHExchangeRateOracle } from "src/WSTETHExchangeRateOracle.sol";
import { WEETHExchangeRateOracle } from "src/WEETHExchangeRateOracle.sol";

import { RateSourceMock } from "./mocks/RateSourceMock.sol";

Expand All @@ -41,6 +42,7 @@ contract SparkLendMainnetIntegrationTest is Test {
address STETH = 0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84;
address WSTETH = 0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0;
address RETH = 0xae78736Cd615f374D3085123A210448E74Fc6393;
address WEETH = 0xCd5fE23C85820F7B72D0926FC9b05b43E359b7ee;

address ETH_IRM = 0xeCe550fB709C85CE9FC999A033447Ee2DF3ce55c;
address USDC_ORACLE = 0x8fFfFfd4AfB6115b954Bd326cbe7B4BA576818f6;
Expand Down Expand Up @@ -322,6 +324,31 @@ contract SparkLendMainnetIntegrationTest is Test {
assertEq(aaveOracle.getSourceOfAsset(WSTETH), address(oracle));
}

function test_weeth_market_oracle() public {
WEETHExchangeRateOracle oracle = new WEETHExchangeRateOracle(WEETH, ETHUSD_ORACLE);

vm.expectRevert(); // Not setup yet
assertEq(aaveOracle.getAssetPrice(WEETH), 0);
assertEq(aaveOracle.getSourceOfAsset(WEETH), address(0));

address[] memory assets = new address[](1);
assets[0] = WEETH;
address[] memory sources = new address[](1);
sources[0] = address(oracle);

vm.prank(ADMIN);
aaveOracle.setAssetSources(
assets,
sources
);

// Nothing is special about this number, it just happens to be the price at this block
uint256 price = 2580.17606917e8;

assertEq(aaveOracle.getAssetPrice(WEETH), price);
assertEq(aaveOracle.getSourceOfAsset(WEETH), address(oracle));
}

/**********************************************************************************************/
/*** Helper Functions ***/
/**********************************************************************************************/
Expand Down
90 changes: 90 additions & 0 deletions test/WEETHExchangeRateOracle.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.0;

import "forge-std/Test.sol";

import { PriceSourceMock } from "./mocks/PriceSourceMock.sol";

import { WEETHExchangeRateOracle } from "../src/WEETHExchangeRateOracle.sol";

contract WEETHMock {

uint256 exchangeRate;

constructor(uint256 _exchangeRate) {
exchangeRate = _exchangeRate;
}

function getRate() external view returns (uint256) {
return exchangeRate;
}

function setExchangeRate(uint256 _exchangeRate) external {
exchangeRate = _exchangeRate;
}

}

contract WEETHExchangeRateOracleTest is Test {

WEETHMock weeth;
PriceSourceMock ethSource;

WEETHExchangeRateOracle oracle;

function setUp() public {
weeth = new WEETHMock(1.2e18);
ethSource = new PriceSourceMock(2000e8, 8);
oracle = new WEETHExchangeRateOracle(address(weeth), address(ethSource));
}

function test_constructor() public {
assertEq(address(oracle.weeth()), address(weeth));
assertEq(address(oracle.ethSource()), address(ethSource));
assertEq(oracle.decimals(), 8);
}

function test_invalid_decimals() public {
ethSource.setLatestAnswer(2000e18);
ethSource.setDecimals(18);
vm.expectRevert("WEETHExchangeRateOracle/invalid-decimals");
new WEETHExchangeRateOracle(address(weeth), address(ethSource));
}

function test_latestAnswer_zeroEthUsd() public {
ethSource.setLatestAnswer(0);
assertEq(oracle.latestAnswer(), 0);
}

function test_latestAnswer_negativeEthUsd() public {
ethSource.setLatestAnswer(-1);
assertEq(oracle.latestAnswer(), 0);
}

function test_latestAnswer_zeroExchangeRate() public {
weeth.setExchangeRate(0);
assertEq(oracle.latestAnswer(), 0);
}

function test_latestAnswer_negativeExchangeRate() public {
// RETH ER can't go negative, but it can have a silent overflow
assertLt(int256(uint256(int256(-1))), 0);
weeth.setExchangeRate(uint256(int256(-1)));
assertEq(oracle.latestAnswer(), 0);
}

function test_latestAnswer() public {
// 1.2 * 2000 = 2400
assertEq(oracle.latestAnswer(), 2400e8);

// 1 * 2000 = 2000
weeth.setExchangeRate(1e18);
assertEq(oracle.latestAnswer(), 2000e8);

// 0.5 * 1200 = 600
weeth.setExchangeRate(0.5e18);
ethSource.setLatestAnswer(1200e8);
assertEq(oracle.latestAnswer(), 600e8);
}

}
Loading