mirror of
https://github.com/Instadapp/fluid-contracts-public.git
synced 2024-07-29 21:57:37 +00:00
217 lines
9.6 KiB
Solidity
217 lines
9.6 KiB
Solidity
//SPDX-License-Identifier: MIT
|
|
pragma solidity 0.8.21;
|
|
|
|
import { Test } from "forge-std/Test.sol";
|
|
|
|
import { IRedstoneOracle } from "../../../contracts/oracle/interfaces/external/IRedstoneOracle.sol";
|
|
import { IUniswapV3Pool } from "../../../contracts/oracle/interfaces/external/IUniswapV3Pool.sol";
|
|
import { IChainlinkAggregatorV3 } from "../../../contracts/oracle/interfaces/external/IChainlinkAggregatorV3.sol";
|
|
import { IWstETH } from "../../../contracts/oracle/interfaces/external/IWstETH.sol";
|
|
import { IWeETH } from "../../../contracts/oracle/interfaces/external/IWeETH.sol";
|
|
import { UniV3CheckCLRSOracle } from "../../../contracts/oracle/oracles/uniV3CheckCLRSOracle.sol";
|
|
import { IFluidOracle } from "../../../contracts/oracle/fluidOracle.sol";
|
|
import { UniV3OracleImpl } from "../../../contracts/oracle/implementations/uniV3OracleImpl.sol";
|
|
import { ChainlinkOracleImpl } from "../../../contracts/oracle/implementations/chainlinkOracleImpl.sol";
|
|
import { RedstoneOracleImpl } from "../../../contracts/oracle/implementations/redstoneOracleImpl.sol";
|
|
|
|
import { FullMath } from "../../../contracts/oracle/libraries/FullMath.sol";
|
|
import { TickMath } from "../../../contracts/oracle/libraries/TickMath.sol";
|
|
|
|
import { MockChainlinkFeed } from "./mocks/mockChainlinkFeed.sol";
|
|
import { MockRedstoneFeed } from "./mocks/mockRedstoneFeed.sol";
|
|
|
|
contract OracleTestSuite is Test {
|
|
IFluidOracle oracle;
|
|
|
|
uint32[] secondsAgos_ = new uint32[](5);
|
|
|
|
uint256[] uniswapTwapDeltas_ = new uint256[](3);
|
|
|
|
address payable internal bob = payable(makeAddr("bob"));
|
|
|
|
IUniswapV3Pool UNIV3_POOL = IUniswapV3Pool(0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640);
|
|
// USDC / ETH feed
|
|
IChainlinkAggregatorV3 CHAINLINK_FEED = IChainlinkAggregatorV3(0x986b5E1e1755e3C2440e960477f25201B0a8bbD4);
|
|
|
|
IChainlinkAggregatorV3 internal constant CHAINLINK_FEED_ETH_USD =
|
|
IChainlinkAggregatorV3(0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419);
|
|
|
|
IChainlinkAggregatorV3 internal constant CHAINLINK_FEED_USDC_USD =
|
|
IChainlinkAggregatorV3(0x8fFfFfd4AfB6115b954Bd326cbe7B4BA576818f6);
|
|
|
|
IChainlinkAggregatorV3 internal constant CHAINLINK_FEED_WBTC_BTC =
|
|
IChainlinkAggregatorV3(0xfdFD9C85aD200c506Cf9e21F1FD8dd01932FBB23);
|
|
|
|
IChainlinkAggregatorV3 internal constant CHAINLINK_FEED_BTC_USD =
|
|
IChainlinkAggregatorV3(0xF4030086522a5bEEa4988F8cA5B36dbC97BeE88c);
|
|
|
|
IChainlinkAggregatorV3 internal constant CHAINLINK_FEED_CRV_USD =
|
|
IChainlinkAggregatorV3(0xCd627aA160A6fA45Eb793D19Ef54f5062F20f33f);
|
|
|
|
IChainlinkAggregatorV3 internal constant CHAINLINK_FEED_SXP_USD =
|
|
IChainlinkAggregatorV3(0xFb0CfD6c19e25DB4a08D8a204a387cEa48Cc138f);
|
|
|
|
IChainlinkAggregatorV3 internal constant CHAINLINK_FEED_STETH_ETH =
|
|
IChainlinkAggregatorV3(0x86392dC19c0b719886221c78AB11eb8Cf5c52812);
|
|
|
|
IWstETH internal constant WSTETH_TOKEN = IWstETH(0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0);
|
|
|
|
IWeETH internal constant WEETH_TOKEN = IWeETH(0xCd5fE23C85820F7B72D0926FC9b05b43E359b7ee);
|
|
// MOCKS
|
|
|
|
MockChainlinkFeed internal MOCK_CHAINLINK_FEED;
|
|
MockRedstoneFeed internal MOCK_REDSTONE_FEED = new MockRedstoneFeed();
|
|
|
|
function setUp() public virtual {
|
|
vm.createSelectFork(vm.envString("MAINNET_RPC_URL"));
|
|
vm.rollFork(18664561);
|
|
|
|
MOCK_CHAINLINK_FEED = new MockChainlinkFeed(CHAINLINK_FEED);
|
|
MOCK_REDSTONE_FEED = new MockRedstoneFeed();
|
|
}
|
|
|
|
function _getDefaultSecondAgosFixed() internal pure returns (uint32[5] memory) {
|
|
uint32[5] memory secondsAgos_ = [uint32(240), 60, 15, 1, 0];
|
|
return secondsAgos_;
|
|
}
|
|
|
|
function _getDefaultSecondAgos() internal pure returns (uint32[] memory) {
|
|
uint32[] memory secondsAgos_ = new uint32[](5);
|
|
secondsAgos_[0] = 240;
|
|
secondsAgos_[1] = 60;
|
|
secondsAgos_[2] = 15;
|
|
secondsAgos_[3] = 1;
|
|
secondsAgos_[4] = 0;
|
|
return secondsAgos_;
|
|
}
|
|
|
|
function _getDefaultUniswapTwapDeltasFixed() internal pure returns (uint256[3] memory) {
|
|
uint256[3] memory uniswapTwapDeltas_ = [uint256(300), 100, 20];
|
|
return uniswapTwapDeltas_;
|
|
}
|
|
|
|
function _getDefaultUniswapTwapDeltas() internal pure returns (uint32[] memory) {
|
|
uint32[] memory uniswapTwapDeltas_ = new uint32[](3);
|
|
uniswapTwapDeltas_[0] = 300;
|
|
uniswapTwapDeltas_[1] = 100;
|
|
uniswapTwapDeltas_[2] = 20;
|
|
return uniswapTwapDeltas_;
|
|
}
|
|
|
|
function runUniV3OracleDataAsserts(
|
|
UniV3CheckCLRSOracle oracle,
|
|
address uniV3Pool,
|
|
bool uniV3InvertRate,
|
|
uint32[] memory uniV3secondsAgos,
|
|
uint32[] memory uniV3TwapDeltas
|
|
) public {
|
|
(
|
|
IUniswapV3Pool uniV3Pool_,
|
|
bool uniV3InvertRate_,
|
|
uint32[] memory uniV3secondsAgos_,
|
|
uint256[] memory uniV3TwapDeltas_,
|
|
uint256 uniV3exchangeRateUnsafe_,
|
|
uint256 uniV3exchangeRate_
|
|
) = oracle.uniV3OracleData();
|
|
assertEq(address(uniV3Pool_), address(uniV3Pool));
|
|
assertEq(uniV3InvertRate_, uniV3InvertRate);
|
|
for (uint256 i = 0; i < uniV3secondsAgos_.length; i++) {
|
|
assertEq(uniV3secondsAgos_[i], uniV3secondsAgos[i]);
|
|
}
|
|
for (uint256 i = 0; i < uniV3TwapDeltas_.length; i++) {
|
|
assertEq(uniV3TwapDeltas_[i], uniV3TwapDeltas[i]);
|
|
}
|
|
(uint160 sqrtPriceX96_, , , , , , ) = IUniswapV3Pool(uniV3Pool).slot0();
|
|
uint256 expectedUniV3exchangeRateUnsafe_ = uniV3InvertRate
|
|
? _invertUniV3Price(_getPriceFromSqrtPriceX96(sqrtPriceX96_))
|
|
: _getPriceFromSqrtPriceX96(sqrtPriceX96_);
|
|
|
|
assertEq(uniV3exchangeRateUnsafe_, expectedUniV3exchangeRateUnsafe_);
|
|
(int56[] memory tickCumulatives, ) = IUniswapV3Pool(uniV3Pool).observe(uniV3secondsAgos);
|
|
int24 product = int24(
|
|
(tickCumulatives[uniV3TwapDeltas_.length + 1] - tickCumulatives[uniV3TwapDeltas_.length]) /
|
|
int256(uint256(uniV3secondsAgos[3] - uniV3secondsAgos[4]))
|
|
);
|
|
uint256 expectedUniV3exchangeRate_ = uniV3InvertRate
|
|
? _invertUniV3Price(_getPriceFromSqrtPriceX96(TickMath.getSqrtRatioAtTick(int24(product))))
|
|
: _getPriceFromSqrtPriceX96(TickMath.getSqrtRatioAtTick(int24(product)));
|
|
assertEq(uniV3exchangeRate_, expectedUniV3exchangeRate_);
|
|
}
|
|
|
|
function runRedstoneOracleDataAsserts(
|
|
UniV3CheckCLRSOracle oracle,
|
|
uint256 redstoneExchangeRate,
|
|
IRedstoneOracle redstoneOracle,
|
|
bool redstoneInvertRate
|
|
) public {
|
|
(uint256 redstoneExchangeRate_, IRedstoneOracle redstoneOracle_, bool redstoneInvertRate_) = oracle
|
|
.redstoneOracleData();
|
|
assertEq(redstoneExchangeRate_, redstoneExchangeRate);
|
|
assertEq(address(redstoneOracle_), address(redstoneOracle));
|
|
assertEq(redstoneInvertRate_, redstoneInvertRate_);
|
|
}
|
|
|
|
struct ChainlinkFeedData {
|
|
IChainlinkAggregatorV3 feed;
|
|
bool invertRate;
|
|
uint256 exchangeRate;
|
|
}
|
|
|
|
function runChainlinkOracleDataAsserts(
|
|
UniV3CheckCLRSOracle oracle,
|
|
ChainlinkFeedData[] memory expectedData
|
|
) public {
|
|
(
|
|
uint256 chainlinkExchangeRate_,
|
|
IChainlinkAggregatorV3 chainlinkFeed1_,
|
|
bool chainlinkInvertRate1_,
|
|
uint256 chainlinkExchangeRate1_,
|
|
IChainlinkAggregatorV3 chainlinkFeed2_,
|
|
bool chainlinkInvertRate2_,
|
|
uint256 chainlinkExchangeRate2_,
|
|
IChainlinkAggregatorV3 chainlinkFeed3_,
|
|
bool chainlinkInvertRate3_,
|
|
uint256 chainlinkExchangeRate3_
|
|
) = oracle.chainlinkOracleData();
|
|
|
|
assertEq(address(expectedData[0].feed), address(chainlinkFeed1_));
|
|
assertEq(expectedData[0].invertRate, chainlinkInvertRate1_);
|
|
assertEq(expectedData[0].exchangeRate, chainlinkExchangeRate1_);
|
|
assertEq(address(expectedData[1].feed), address(chainlinkFeed2_));
|
|
assertEq(expectedData[1].invertRate, chainlinkInvertRate2_);
|
|
assertEq(expectedData[1].exchangeRate, chainlinkExchangeRate2_);
|
|
assertEq(address(expectedData[2].feed), address(chainlinkFeed3_));
|
|
assertEq(expectedData[2].invertRate, chainlinkInvertRate3_);
|
|
assertEq(expectedData[2].exchangeRate, chainlinkExchangeRate3_);
|
|
}
|
|
|
|
function runOracleDataAsserts(uint256 rateCheckMaxDelta, uint256 rateSource, uint256 falbackMainSource) public {
|
|
(uint256 rateCheckMaxDelta_, uint256 rateSource_, uint256 falbackMainSource_) = UniV3CheckCLRSOracle(
|
|
address(oracle)
|
|
).uniV3CheckOracleData();
|
|
assertEq(rateCheckMaxDelta, rateCheckMaxDelta_);
|
|
assertEq(rateSource, rateSource_);
|
|
assertEq(falbackMainSource, falbackMainSource_);
|
|
}
|
|
|
|
/// @dev Get the price from the sqrt price in `OracleUtils.RATE_OUTPUT_DECIMALS`
|
|
/// (see https://blog.uniswap.org/uniswap-v3-math-primer)
|
|
/// @param sqrtPriceX96_ The sqrt price to convert
|
|
// TODO: Create modular absctract contract for _getPriceFromSqrtPriceX96 and _invertUniV3Price that will be also used in uniV3OracleImpl.sol
|
|
function _getPriceFromSqrtPriceX96(uint160 sqrtPriceX96_) internal view returns (uint256 priceX96_) {
|
|
return
|
|
FullMath.mulDiv(
|
|
uint256(sqrtPriceX96_) * uint256(sqrtPriceX96_),
|
|
10 ** 27,
|
|
1 << 192 // 2^96 * 2
|
|
);
|
|
}
|
|
|
|
/// @dev Invert the price
|
|
/// @param price_ The price to invert
|
|
/// @return invertedPrice_ The inverted price in `OracleUtils.RATE_OUTPUT_DECIMALS`
|
|
function _invertUniV3Price(uint256 price_) internal view returns (uint256 invertedPrice_) {
|
|
return 10 ** (27 * 2) / price_;
|
|
}
|
|
}
|