fluid-contracts-public/test/foundry/oracle/fallbackCLRSOracle.t.sol

273 lines
11 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 { IChainlinkAggregatorV3 } from "../../../contracts/oracle/interfaces/external/IChainlinkAggregatorV3.sol";
import { FallbackCLRSOracle } from "../../../contracts/oracle/oracles/fallbackCLRSOracle.sol";
import { ChainlinkOracleImpl } from "../../../contracts/oracle/implementations/chainlinkOracleImpl.sol";
import { ChainlinkStructs, RedstoneStructs } from "../../../contracts/oracle/implementations/structs.sol";
import { RedstoneOracleImpl } from "../../../contracts/oracle/implementations/redstoneOracleImpl.sol";
import { ErrorTypes } from "../../../contracts/oracle/errorTypes.sol";
import { Error } from "../../../contracts/oracle/error.sol";
import { MockChainlinkFeed } from "./mocks/mockChainlinkFeed.sol";
import { OracleTestSuite } from "./oracleTestSuite.t.sol";
contract FallbackCLRS_Chainlink2Hops_OracleTest is OracleTestSuite {
function setUp() public override {
super.setUp();
oracle = new FallbackCLRSOracle(
1, // mainsource = chainlink
ChainlinkStructs.ChainlinkConstructorParams({
hops: 2,
feed1: ChainlinkStructs.ChainlinkFeedData({
feed: CHAINLINK_FEED_ETH_USD,
invertRate: false,
token0Decimals: 18 // ETH has 18 decimals
}),
feed2: ChainlinkStructs.ChainlinkFeedData({
feed: CHAINLINK_FEED_USDC_USD,
invertRate: true,
token0Decimals: 6 // USDC has 6 decimals
}),
feed3: ChainlinkStructs.ChainlinkFeedData({
feed: IChainlinkAggregatorV3(address(0)),
invertRate: false,
token0Decimals: 0
})
}),
RedstoneStructs.RedstoneOracleData({
oracle: IRedstoneOracle(address(CHAINLINK_FEED_USDC_USD)),
invertRate: false,
token0Decimals: 1
})
);
}
function test_getExchangeRate() public {
(, int256 exchangeRateEthUsd_, , , ) = CHAINLINK_FEED_ETH_USD.latestRoundData();
assertEq(exchangeRateEthUsd_, 201805491600);
// 2018,05491600 -> USD -> ETH
// 2018,05491600 = 201805491600
(, int256 exchangeRateUsdcUsd_, , , ) = CHAINLINK_FEED_USDC_USD.latestRoundData();
assertEq(exchangeRateUsdcUsd_, 99990875);
// 0,99990875 -> USD -> USDC
uint256 rateEthUsd = uint256(exchangeRateEthUsd_) * (1e27) * (1e6); // 1e27 -> Oracle precision, 1e6 -> USDC decimals
uint256 expectedRate = rateEthUsd / uint256(exchangeRateUsdcUsd_) / 1e18;
uint256 rate = oracle.getExchangeRate();
assertEq(rate, expectedRate); // 2018239080316078842
// 15 decimals (27 + 6 - 18)
// 2018.239080316078842
}
}
contract FallbackCLRS_Chainlink2Hops_OracleTest2 is OracleTestSuite {
function setUp() public override {
super.setUp();
oracle = new FallbackCLRSOracle(
1, // mainsource = chainlink
ChainlinkStructs.ChainlinkConstructorParams({
hops: 2,
feed1: ChainlinkStructs.ChainlinkFeedData({
feed: CHAINLINK_FEED_CRV_USD,
invertRate: false,
token0Decimals: 18 // CRV has 18 decimals
}),
feed2: ChainlinkStructs.ChainlinkFeedData({
feed: CHAINLINK_FEED_SXP_USD,
invertRate: true,
token0Decimals: 18 // SXP has 18 decimals
}),
feed3: ChainlinkStructs.ChainlinkFeedData({
feed: IChainlinkAggregatorV3(address(0)),
invertRate: false,
token0Decimals: 0
})
}),
RedstoneStructs.RedstoneOracleData({
oracle: IRedstoneOracle(address(CHAINLINK_FEED_SXP_USD)),
invertRate: false,
token0Decimals: 1
})
);
}
function test_getExchangeRate() public {
(, int256 exchangeRateCrvUsd_, , , ) = CHAINLINK_FEED_CRV_USD.latestRoundData();
assertEq(exchangeRateCrvUsd_, 56035000);
// 0.56035000 -> CRV -> USD
// 0.56035000 = 56035000
(, int256 exchangeRateSxpUsd_, , , ) = CHAINLINK_FEED_SXP_USD.latestRoundData();
assertEq(exchangeRateSxpUsd_, 33365151);
// 0,33365151 -> USD -> USDC
uint256 rateEthUsd = uint256(exchangeRateCrvUsd_) * (1e27) * (1e18); // 1e27 -> Oracle precision, 1e18 -> SXP decimals
uint256 expectedRate = rateEthUsd / uint256(exchangeRateSxpUsd_) / 1e18; // 1e18 -> CRV decimals
uint256 rate = oracle.getExchangeRate();
assertEq(rate, expectedRate); // 99990875
}
}
contract FallbackCLRS_Chainlink2Hops_OracleTest3 is OracleTestSuite {
function setUp() public override {
super.setUp();
oracle = new FallbackCLRSOracle(
1, // mainsource = chainlink
ChainlinkStructs.ChainlinkConstructorParams({
hops: 2,
feed1: ChainlinkStructs.ChainlinkFeedData({
feed: CHAINLINK_FEED_WBTC_BTC,
invertRate: false,
token0Decimals: 8 // WBTC has 8 decimals
}),
feed2: ChainlinkStructs.ChainlinkFeedData({
feed: CHAINLINK_FEED_BTC_USD,
invertRate: true,
token0Decimals: 8 // BTC has 8 decimals
}),
feed3: ChainlinkStructs.ChainlinkFeedData({
feed: IChainlinkAggregatorV3(address(0)),
invertRate: false,
token0Decimals: 0
})
}),
RedstoneStructs.RedstoneOracleData({
oracle: IRedstoneOracle(address(CHAINLINK_FEED_BTC_USD)),
invertRate: false,
token0Decimals: 1
})
);
}
function test_getExchangeRate() public {
(, int256 exchangeRateWbtcBtc_, , , ) = CHAINLINK_FEED_WBTC_BTC.latestRoundData();
assertEq(exchangeRateWbtcBtc_, 99841321);
// 0.99841321 -> WBTC -> BTC
// 0.99841321 = 99841321
(, int256 exchangeRateBtcUsd_, , , ) = CHAINLINK_FEED_BTC_USD.latestRoundData();
assertEq(exchangeRateBtcUsd_, 3704705000000);
// 37047,05000000 -> BTC -> USD
// 37047,05000000 = 3704705000000
//check WBTC -> USD
uint256 rateWbtcBtc = uint256(exchangeRateWbtcBtc_) * (1e27) * (1e8); // 1e27 -> Oracle precision, 1e8 -> BTC decimals
uint256 expectedRate = rateWbtcBtc / uint256(exchangeRateBtcUsd_) / 1e8; // 1e8 -> WBTC decimals
uint256 rate = oracle.getExchangeRate();
assertEq(rate, expectedRate);
// 0.000026949870772436671745793
}
}
contract FallbackCLRS_Chainlink3Hops_OracleTest is OracleTestSuite {
function setUp() public override {
super.setUp();
oracle = new FallbackCLRSOracle(
1, // mainsource = chainlink
ChainlinkStructs.ChainlinkConstructorParams({
hops: 3,
feed1: ChainlinkStructs.ChainlinkFeedData({
feed: CHAINLINK_FEED_WBTC_BTC,
invertRate: false,
token0Decimals: 8 // WBTC has 8 decimals
}),
feed2: ChainlinkStructs.ChainlinkFeedData({
feed: CHAINLINK_FEED_BTC_USD,
invertRate: false,
token0Decimals: 8 // BTC has 8 decimals
}),
feed3: ChainlinkStructs.ChainlinkFeedData({
feed: CHAINLINK_FEED_USDC_USD,
invertRate: true,
token0Decimals: 6 // USDC has 6 decimals
})
}),
RedstoneStructs.RedstoneOracleData({
oracle: IRedstoneOracle(address(CHAINLINK_FEED_USDC_USD)),
invertRate: false,
token0Decimals: 1
})
);
}
function test_getExchangeRate() public {
(, int256 exchangeRateWbtcBtc_, , , ) = CHAINLINK_FEED_WBTC_BTC.latestRoundData();
assertEq(exchangeRateWbtcBtc_, 99841321);
// 0.99841321 -> WBTC -> BTC
// 0.99841321 = 99841321
(, int256 exchangeRateBtcUsd_, , , ) = CHAINLINK_FEED_BTC_USD.latestRoundData();
assertEq(exchangeRateBtcUsd_, 3704705000000);
// 37047,05000000 -> BTC -> USD
// 37047,05000000 = 3704705000000
//convert WBTC -> BTC (wbtc/btc rate)
uint256 rateWbtcBtc = (uint256(exchangeRateWbtcBtc_) * (1e27)) / 1e8; // 1e27 -> Oracle precision
//convert BTC -> USD (btc/usd rate)
exchangeRateBtcUsd_ = (exchangeRateBtcUsd_ * 1e27) / 1e8;
uint256 wbtcUsdRate = (rateWbtcBtc * uint256(exchangeRateBtcUsd_)) / 1e27; // 1e8 -> BTC decimals
(, int256 exchangeRateUsdcUsd_, , , ) = CHAINLINK_FEED_USDC_USD.latestRoundData();
assertEq(exchangeRateUsdcUsd_, 99990875);
//invert USDC/USD rate to get USD to USDC rate
uint256 usdUsdcRate = (1e27 * 1e6) / uint256(exchangeRateUsdcUsd_); // 1e6 -> USDC decimals
// WBTC -> USDC rate
uint256 expectedRate = (wbtcUsdRate * usdUsdcRate) / 1e27; // 1e27 division adjusts for the Oracle's precision and 1e8 division was introduced by BTC's 8 decimal
uint256 rate = oracle.getExchangeRate();
assertEq(rate, expectedRate); // 369916395986438762537081487906
// 36991.6395986438762537081487906
}
function test_getExchangeRate_ThrowExchangeRateZero() public {
// USDC / ETH feed
IChainlinkAggregatorV3 CHAINLINK_FEED = IChainlinkAggregatorV3(0x986b5E1e1755e3C2440e960477f25201B0a8bbD4);
oracle = new FallbackCLRSOracle(
1, // mainsource = chainlink
ChainlinkStructs.ChainlinkConstructorParams({
hops: 1,
feed1: ChainlinkStructs.ChainlinkFeedData({
feed: MOCK_CHAINLINK_FEED,
invertRate: false,
token0Decimals: 6 // USDC has 6 decimals
}),
feed2: ChainlinkStructs.ChainlinkFeedData({
feed: IChainlinkAggregatorV3(address(0)),
invertRate: false,
token0Decimals: 0
}),
feed3: ChainlinkStructs.ChainlinkFeedData({
feed: IChainlinkAggregatorV3(address(0)),
invertRate: false,
token0Decimals: 0
})
}),
RedstoneStructs.RedstoneOracleData({
oracle: IRedstoneOracle(address(0)),
invertRate: false,
token0Decimals: 0
})
);
MOCK_CHAINLINK_FEED.setExchangeRate(0);
vm.expectRevert(
abi.encodeWithSelector(Error.FluidOracleError.selector, ErrorTypes.FallbackCLRSOracle__ExchangeRateZero)
);
oracle.getExchangeRate();
}
}