{ "language": "Solidity", "sources": { "contracts/oracle/error.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\ncontract Error {\n error FluidOracleError(uint256 errorId_);\n}\n" }, "contracts/oracle/errorTypes.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nlibrary ErrorTypes {\n /***********************************|\n | FluidOracleL2 | \n |__________________________________*/\n\n /// @notice thrown when sequencer on a L2 has an outage and grace period has not yet passed.\n uint256 internal constant FluidOracleL2__SequencerOutage = 60000;\n\n /***********************************|\n | UniV3CheckCLRSOracle | \n |__________________________________*/\n\n /// @notice thrown when the delta between main price source and check rate source is exceeding the allowed delta\n uint256 internal constant UniV3CheckCLRSOracle__InvalidPrice = 60001;\n\n /// @notice thrown when an invalid parameter is passed to a method\n uint256 internal constant UniV3CheckCLRSOracle__InvalidParams = 60002;\n\n /// @notice thrown when the exchange rate is zero, even after all possible fallbacks depending on config\n uint256 internal constant UniV3CheckCLRSOracle__ExchangeRateZero = 60003;\n\n /***********************************|\n | FluidOracle | \n |__________________________________*/\n\n /// @notice thrown when an invalid info name is passed into a fluid oracle (e.g. not set or too long)\n uint256 internal constant FluidOracle__InvalidInfoName = 60010;\n\n /***********************************|\n | sUSDe Oracle | \n |__________________________________*/\n\n /// @notice thrown when an invalid parameter is passed to a method\n uint256 internal constant SUSDeOracle__InvalidParams = 60102;\n\n /***********************************|\n | Pendle Oracle | \n |__________________________________*/\n\n /// @notice thrown when an invalid parameter is passed to a method\n uint256 internal constant PendleOracle__InvalidParams = 60201;\n\n /// @notice thrown when the Pendle market Oracle has not been initialized yet\n uint256 internal constant PendleOracle__MarketNotInitialized = 60202;\n\n /// @notice thrown when the Pendle market does not have 18 decimals\n uint256 internal constant PendleOracle__MarketInvalidDecimals = 60203;\n\n /// @notice thrown when the Pendle market returns an unexpected price\n uint256 internal constant PendleOracle__InvalidPrice = 60204;\n\n /***********************************|\n | CLRS2UniV3CheckCLRSOracleL2 | \n |__________________________________*/\n\n /// @notice thrown when the exchange rate is zero, even after all possible fallbacks depending on config\n uint256 internal constant CLRS2UniV3CheckCLRSOracleL2__ExchangeRateZero = 60301;\n\n /***********************************|\n | Ratio2xFallbackCLRSOracleL2 | \n |__________________________________*/\n\n /// @notice thrown when the exchange rate is zero, even after all possible fallbacks depending on config\n uint256 internal constant Ratio2xFallbackCLRSOracleL2__ExchangeRateZero = 60311;\n\n /***********************************|\n | Chainlink Oracle | \n |__________________________________*/\n\n /// @notice thrown when an invalid parameter is passed to a method\n uint256 internal constant ChainlinkOracle__InvalidParams = 61001;\n\n /***********************************|\n | UniswapV3 Oracle | \n |__________________________________*/\n\n /// @notice thrown when an invalid parameter is passed to a method\n uint256 internal constant UniV3Oracle__InvalidParams = 62001;\n\n /// @notice thrown when constructor is called with invalid ordered seconds agos values\n uint256 internal constant UniV3Oracle__InvalidSecondsAgos = 62002;\n\n /// @notice thrown when constructor is called with invalid delta values > 100%\n uint256 internal constant UniV3Oracle__InvalidDeltas = 62003;\n\n /***********************************|\n | WstETh Oracle | \n |__________________________________*/\n\n /// @notice thrown when an invalid parameter is passed to a method\n uint256 internal constant WstETHOracle__InvalidParams = 63001;\n\n /***********************************|\n | Redstone Oracle | \n |__________________________________*/\n\n /// @notice thrown when an invalid parameter is passed to a method\n uint256 internal constant RedstoneOracle__InvalidParams = 64001;\n\n /***********************************|\n | Fallback Oracle | \n |__________________________________*/\n\n /// @notice thrown when an invalid parameter is passed to a method\n uint256 internal constant FallbackOracle__InvalidParams = 65001;\n\n /***********************************|\n | FallbackCLRSOracle | \n |__________________________________*/\n\n /// @notice thrown when the exchange rate is zero, even for the fallback oracle source (if enabled)\n uint256 internal constant FallbackCLRSOracle__ExchangeRateZero = 66001;\n\n /***********************************|\n | WstETHCLRSOracle | \n |__________________________________*/\n\n /// @notice thrown when the exchange rate is zero, even for the fallback oracle source (if enabled)\n uint256 internal constant WstETHCLRSOracle__ExchangeRateZero = 67001;\n\n /***********************************|\n | CLFallbackUniV3Oracle | \n |__________________________________*/\n\n /// @notice thrown when the exchange rate is zero, even for the uniV3 rate\n uint256 internal constant CLFallbackUniV3Oracle__ExchangeRateZero = 68001;\n\n /***********************************|\n | WstETHCLRS2UniV3CheckCLRSOracle | \n |__________________________________*/\n\n /// @notice thrown when the exchange rate is zero, even for the uniV3 rate\n uint256 internal constant WstETHCLRS2UniV3CheckCLRSOracle__ExchangeRateZero = 69001;\n\n /***********************************|\n | WeETh Oracle | \n |__________________________________*/\n\n /// @notice thrown when an invalid parameter is passed to a method\n uint256 internal constant WeETHOracle__InvalidParams = 70001;\n}\n" }, "contracts/oracle/fluidOracle.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { IFluidOracle } from \"./interfaces/iFluidOracle.sol\";\nimport { ErrorTypes } from \"./errorTypes.sol\";\nimport { Error as OracleError } from \"./error.sol\";\n\n/// @title FluidOracle\n/// @notice Base contract that any Fluid Oracle must implement\nabstract contract FluidOracle is IFluidOracle, OracleError {\n /// @dev short helper string to easily identify the oracle. E.g. token symbols\n //\n // using a bytes32 because string can not be immutable.\n bytes32 private immutable _infoName;\n\n constructor(string memory infoName_) {\n if (bytes(infoName_).length > 32 || bytes(infoName_).length == 0) {\n revert FluidOracleError(ErrorTypes.FluidOracle__InvalidInfoName);\n }\n\n // convert string to bytes32\n bytes32 infoNameBytes32_;\n assembly {\n infoNameBytes32_ := mload(add(infoName_, 32))\n }\n _infoName = infoNameBytes32_;\n }\n\n /// @inheritdoc IFluidOracle\n function infoName() external view returns (string memory) {\n // convert bytes32 to string\n uint256 length_;\n while (length_ < 32 && _infoName[length_] != 0) {\n length_++;\n }\n bytes memory infoNameBytes_ = new bytes(length_);\n for (uint256 i; i < length_; i++) {\n infoNameBytes_[i] = _infoName[i];\n }\n return string(infoNameBytes_);\n }\n\n /// @inheritdoc IFluidOracle\n function getExchangeRate() external view virtual returns (uint256 exchangeRate_);\n\n /// @inheritdoc IFluidOracle\n function getExchangeRateOperate() external view virtual returns (uint256 exchangeRate_);\n\n /// @inheritdoc IFluidOracle\n function getExchangeRateLiquidate() external view virtual returns (uint256 exchangeRate_);\n}\n" }, "contracts/oracle/fluidOracleL2.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { IFluidOracle } from \"./interfaces/iFluidOracle.sol\";\nimport { ErrorTypes } from \"./errorTypes.sol\";\nimport { IChainlinkAggregatorV3 } from \"./interfaces/external/IChainlinkAggregatorV3.sol\";\nimport { Error as OracleError } from \"./error.sol\";\n\n/// @title FluidOracleL2\n/// @notice Base contract that any Fluid Oracle L2 must implement\nabstract contract FluidOracleL2 is IFluidOracle, OracleError {\n /// @dev Chainlink L2 Sequencer Uptime feed to detect sequencer outages\n IChainlinkAggregatorV3 internal _SEQUENCER_ORACLE;\n /// @dev max time period until oracle assumes normal behavior after a sequencer outage.\n uint256 internal constant _SEQUENCER_MAX_GRACE_PERIOD = 45 minutes;\n\n /// @notice sets the L2 sequencer uptime Chainlink feed\n constructor(address sequencerUptimeFeed_) {\n _SEQUENCER_ORACLE = IChainlinkAggregatorV3(sequencerUptimeFeed_);\n }\n\n /// @notice returns all sequencer uptime feed related data\n function sequencerL2Data()\n public\n view\n returns (\n address sequencerUptimeFeed_,\n uint256 maxGracePeriod_,\n bool isSequencerUp_,\n uint256 lastUptimeStartedAt_,\n uint256 gracePeriod_,\n bool gracePeriodPassed_,\n uint256 lastOutageStartedAt_,\n bool isSequencerUpAndValid_\n )\n {\n uint80 uptimeStartRoundId_;\n (isSequencerUp_, uptimeStartRoundId_, lastUptimeStartedAt_) = _sequencerUpStatus();\n\n if (isSequencerUp_) {\n (gracePeriod_, gracePeriodPassed_, lastOutageStartedAt_) = _gracePeriod(\n uptimeStartRoundId_,\n lastUptimeStartedAt_\n );\n } else {\n gracePeriod_ = _SEQUENCER_MAX_GRACE_PERIOD;\n (uint80 roundId_, , , , ) = _SEQUENCER_ORACLE.latestRoundData();\n lastOutageStartedAt_ = _lastSequencerOutageStart(roundId_ + 1);\n }\n\n return (\n address(_SEQUENCER_ORACLE),\n _SEQUENCER_MAX_GRACE_PERIOD,\n isSequencerUp_,\n lastUptimeStartedAt_,\n gracePeriod_,\n gracePeriodPassed_,\n lastOutageStartedAt_,\n isSequencerUp_ && gracePeriodPassed_\n );\n }\n\n /// @dev ensures that the sequencer is up and grace period has passed\n function _ensureSequencerUpAndValid() internal view {\n (bool isSequencerUp_, uint80 uptimeStartRoundId_, uint256 uptimeStartedAt_) = _sequencerUpStatus();\n\n if (!isSequencerUp_) {\n revert FluidOracleError(ErrorTypes.FluidOracleL2__SequencerOutage);\n }\n\n (, bool gracePeriodPassed_, ) = _gracePeriod(uptimeStartRoundId_, uptimeStartedAt_);\n if (!gracePeriodPassed_) {\n revert FluidOracleError(ErrorTypes.FluidOracleL2__SequencerOutage);\n }\n }\n\n /// @inheritdoc IFluidOracle\n function getExchangeRate() external view virtual returns (uint256 exchangeRate_);\n\n /// @inheritdoc IFluidOracle\n function getExchangeRateOperate() external view virtual returns (uint256 exchangeRate_);\n\n /// @inheritdoc IFluidOracle\n function getExchangeRateLiquidate() external view virtual returns (uint256 exchangeRate_);\n\n /// @dev finds last round before `uptimeStartRoundId_` where sequencer status was down, incl. handling cases of\n /// consecutive rounds where status was down.\n function _lastSequencerOutageStart(uint80 uptimeStartRoundId_) private view returns (uint256 outageStartedAt_) {\n uint80 roundId_ = uptimeStartRoundId_;\n int256 answer_;\n uint256 startedAt_;\n do {\n (roundId_, answer_, startedAt_, , ) = _SEQUENCER_ORACLE.getRoundData(roundId_ - 1);\n if (answer_ != 0) {\n // sequencer was down at this round, update outage started at data\n outageStartedAt_ = startedAt_;\n } // else: while loop is going to break\n } while (answer_ != 0 && startedAt_ > 0);\n }\n\n /// @dev finds last round where sequencer status was up, incl. handling cases of consecutive rounds where status was up.\n function _sequencerUpStatus()\n private\n view\n returns (bool isSequencerUp_, uint80 uptimeStartRoundId_, uint256 uptimeStartedAt_)\n {\n (uint80 roundId_, int256 answer_, uint256 startedAt_, , ) = _SEQUENCER_ORACLE.latestRoundData();\n if (answer_ != 0) {\n // sequencer is down currently.\n return (false, 0, 0);\n }\n\n isSequencerUp_ = true;\n\n // cover case where there were other consecutive uptime report rounds in between\n uptimeStartRoundId_ = roundId_;\n uptimeStartedAt_ = startedAt_;\n if (uptimeStartedAt_ > 0) {\n do {\n (roundId_, answer_, startedAt_, , ) = _SEQUENCER_ORACLE.getRoundData(roundId_ - 1);\n if (answer_ == 0) {\n // sequencer was up at this round, consecutive uptime so update uptime start data\n uptimeStartRoundId_ = roundId_;\n uptimeStartedAt_ = startedAt_;\n } // else: while loop is going to break\n } while (answer_ == 0 && startedAt_ > 0);\n } // else if startedAt == 0, then it is the first ever round.\n }\n\n /// @dev returns the `gracePeriod_` duration and if the grace period has `passed_` based on\n /// current uptime round data vs the last sequencer outage duration.\n function _gracePeriod(\n uint80 uptimeStartRoundId_,\n uint256 uptimeStartedAt_\n ) private view returns (uint256 gracePeriod_, bool passed_, uint256 outageStartedAt_) {\n uint256 uptimeDuration_ = block.timestamp - uptimeStartedAt_;\n if (uptimeStartedAt_ == 0 || uptimeDuration_ > _SEQUENCER_MAX_GRACE_PERIOD) {\n return (_SEQUENCER_MAX_GRACE_PERIOD, true, 0);\n }\n\n outageStartedAt_ = _lastSequencerOutageStart(uptimeStartRoundId_);\n\n // grace period is outage duration, capped at _SEQUENCER_MAX_GRACE_PERIOD\n gracePeriod_ = uptimeStartedAt_ - outageStartedAt_; // outage duration\n if (gracePeriod_ > _SEQUENCER_MAX_GRACE_PERIOD) {\n gracePeriod_ = _SEQUENCER_MAX_GRACE_PERIOD;\n }\n\n return (gracePeriod_, uptimeDuration_ > gracePeriod_, outageStartedAt_);\n }\n}\n" }, "contracts/oracle/implementations/chainlinkOracleImpl.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { ErrorTypes } from \"../errorTypes.sol\";\nimport { IChainlinkAggregatorV3 } from \"../interfaces/external/IChainlinkAggregatorV3.sol\";\nimport { Error as OracleError } from \"../error.sol\";\nimport { OracleUtils } from \"../libraries/oracleUtils.sol\";\nimport { ChainlinkStructs } from \"./structs.sol\";\n\n/// @title Chainlink Oracle implementation\n/// @notice This contract is used to get the exchange rate via up to 3 hops at Chainlink price feeds.\n/// The rate is multiplied with the previous rate at each hop.\n/// E.g. to go from wBTC to USDC (assuming rates for example):\n/// 1. wBTC -> BTC https://data.chain.link/ethereum/mainnet/crypto-other/wbtc-btc, rate: 0.92.\n/// 2. BTC -> USD https://data.chain.link/ethereum/mainnet/crypto-usd/btc-usd rate: 30,000.\n/// 3. USD -> USDC https://data.chain.link/ethereum/mainnet/stablecoins/usdc-usd rate: 0.98. Must invert feed: 1.02\n/// finale rate would be: 0.92 * 30,000 * 1.02 = 28,152\nabstract contract ChainlinkOracleImpl is OracleError, ChainlinkStructs {\n /// @notice Chainlink price feed 1 to check for the exchange rate\n IChainlinkAggregatorV3 internal immutable _CHAINLINK_FEED1;\n /// @notice Chainlink price feed 2 to check for the exchange rate\n IChainlinkAggregatorV3 internal immutable _CHAINLINK_FEED2;\n /// @notice Chainlink price feed 3 to check for the exchange rate\n IChainlinkAggregatorV3 internal immutable _CHAINLINK_FEED3;\n\n /// @notice Flag to invert the price or not for feed 1 (to e.g. for WETH/USDC pool return prive of USDC per 1 WETH)\n bool internal immutable _CHAINLINK_INVERT_RATE1;\n /// @notice Flag to invert the price or not for feed 2 (to e.g. for WETH/USDC pool return prive of USDC per 1 WETH)\n bool internal immutable _CHAINLINK_INVERT_RATE2;\n /// @notice Flag to invert the price or not for feed 3 (to e.g. for WETH/USDC pool return prive of USDC per 1 WETH)\n bool internal immutable _CHAINLINK_INVERT_RATE3;\n\n /// @notice constant value for price scaling to reduce gas usage for feed 1\n uint256 internal immutable _CHAINLINK_PRICE_SCALER_MULTIPLIER1;\n /// @notice constant value for inverting price to reduce gas usage for feed 1\n uint256 internal immutable _CHAINLINK_INVERT_PRICE_DIVIDEND1;\n\n /// @notice constant value for price scaling to reduce gas usage for feed 2\n uint256 internal immutable _CHAINLINK_PRICE_SCALER_MULTIPLIER2;\n /// @notice constant value for inverting price to reduce gas usage for feed 2\n uint256 internal immutable _CHAINLINK_INVERT_PRICE_DIVIDEND2;\n\n /// @notice constant value for price scaling to reduce gas usage for feed 3\n uint256 internal immutable _CHAINLINK_PRICE_SCALER_MULTIPLIER3;\n /// @notice constant value for inverting price to reduce gas usage for feed 3\n uint256 internal immutable _CHAINLINK_INVERT_PRICE_DIVIDEND3;\n\n /// @notice constructor sets the Chainlink price feed and invertRate flag for each hop.\n /// E.g. `invertRate_` should be true if for the USDC/ETH pool it's expected that the oracle returns USDC per 1 ETH\n constructor(ChainlinkConstructorParams memory params_) {\n if (\n (params_.hops < 1 || params_.hops > 3) || // hops must be 1, 2 or 3\n (address(params_.feed1.feed) == address(0) || params_.feed1.token0Decimals == 0) || // first feed must always be defined\n (params_.hops > 1 && (address(params_.feed2.feed) == address(0) || params_.feed2.token0Decimals == 0)) || // if hops > 1, feed 2 must be defined\n (params_.hops > 2 && (address(params_.feed3.feed) == address(0) || params_.feed3.token0Decimals == 0)) // if hops > 2, feed 3 must be defined\n ) {\n revert FluidOracleError(ErrorTypes.ChainlinkOracle__InvalidParams);\n }\n\n _CHAINLINK_FEED1 = params_.feed1.feed;\n _CHAINLINK_FEED2 = params_.feed2.feed;\n _CHAINLINK_FEED3 = params_.feed3.feed;\n\n _CHAINLINK_INVERT_RATE1 = params_.feed1.invertRate;\n _CHAINLINK_INVERT_RATE2 = params_.feed2.invertRate;\n _CHAINLINK_INVERT_RATE3 = params_.feed3.invertRate;\n\n // Actual desired output rate example USDC/ETH (6 decimals / 18 decimals).\n // Note ETH has 12 decimals more than USDC.\n // 0.000515525322211842331991619857165357691 // 39 decimals. ETH for 1 USDC\n // 1954.190000000000433 // 15 decimals. USDC for 1 ETH\n\n // to get to PRICE_SCLAER_MULTIPLIER and INVERT_PRICE_DIVIDEND:\n // fetched Chainlink price is in token1Decimals per 1 token0Decimals.\n // E.g. for an USDC/ETH price feed it's in ETH 18 decimals.\n // for an BTC/USD price feed it's in USD 8 decimals.\n // So to scale to 1e27 we need to multiply by 1e27 - token0Decimals.\n // E.g. for USDC/ETH it would be: fetchedPrice * 1e21\n //\n // or for inverted (x token0 per 1 token1), formula would be:\n // = 1e27 * 10**token0Decimals / fetchedPrice\n // E.g. for USDC/ETH it would be: 1e33 / fetchedPrice\n\n // no support for token1Decimals with more than OracleUtils.RATE_OUTPUT_DECIMALS decimals for now as extremely unlikely case\n _CHAINLINK_PRICE_SCALER_MULTIPLIER1 = 10 ** (OracleUtils.RATE_OUTPUT_DECIMALS - params_.feed1.token0Decimals);\n _CHAINLINK_INVERT_PRICE_DIVIDEND1 = 10 ** (OracleUtils.RATE_OUTPUT_DECIMALS + params_.feed1.token0Decimals);\n\n _CHAINLINK_PRICE_SCALER_MULTIPLIER2 = params_.hops > 1\n ? 10 ** (OracleUtils.RATE_OUTPUT_DECIMALS - params_.feed2.token0Decimals)\n : 1;\n _CHAINLINK_INVERT_PRICE_DIVIDEND2 = params_.hops > 1\n ? 10 ** (OracleUtils.RATE_OUTPUT_DECIMALS + params_.feed2.token0Decimals)\n : 1;\n\n _CHAINLINK_PRICE_SCALER_MULTIPLIER3 = params_.hops > 2\n ? 10 ** (OracleUtils.RATE_OUTPUT_DECIMALS - params_.feed3.token0Decimals)\n : 1;\n _CHAINLINK_INVERT_PRICE_DIVIDEND3 = params_.hops > 2\n ? 10 ** (OracleUtils.RATE_OUTPUT_DECIMALS + params_.feed3.token0Decimals)\n : 1;\n }\n\n /// @dev Get the exchange rate from Chainlike oracle price feed(s)\n /// @return rate_ The exchange rate in `OracleUtils.RATE_OUTPUT_DECIMALS`\n function _getChainlinkExchangeRate() internal view returns (uint256 rate_) {\n rate_ = _readFeedRate(\n _CHAINLINK_FEED1,\n _CHAINLINK_INVERT_RATE1,\n _CHAINLINK_PRICE_SCALER_MULTIPLIER1,\n _CHAINLINK_INVERT_PRICE_DIVIDEND1\n );\n if (rate_ == 0 || address(_CHAINLINK_FEED2) == address(0)) {\n // rate 0 or only 1 hop -> return rate of price feed 1\n return rate_;\n }\n rate_ =\n (rate_ *\n _readFeedRate(\n _CHAINLINK_FEED2,\n _CHAINLINK_INVERT_RATE2,\n _CHAINLINK_PRICE_SCALER_MULTIPLIER2,\n _CHAINLINK_INVERT_PRICE_DIVIDEND2\n )) /\n (10 ** OracleUtils.RATE_OUTPUT_DECIMALS);\n\n if (rate_ == 0 || address(_CHAINLINK_FEED3) == address(0)) {\n // rate 0 or 2 hops -> return rate of feed 1 combined with feed 2\n return rate_;\n }\n\n // 3 hops -> return rate of feed 1 combined with feed 2 & feed 3\n rate_ =\n (rate_ *\n _readFeedRate(\n _CHAINLINK_FEED3,\n _CHAINLINK_INVERT_RATE3,\n _CHAINLINK_PRICE_SCALER_MULTIPLIER3,\n _CHAINLINK_INVERT_PRICE_DIVIDEND3\n )) /\n (10 ** OracleUtils.RATE_OUTPUT_DECIMALS);\n }\n\n /// @dev reads the exchange `rate_` from a Chainlink price `feed_` taking into account scaling and `invertRate_`\n function _readFeedRate(\n IChainlinkAggregatorV3 feed_,\n bool invertRate_,\n uint256 priceMultiplier_,\n uint256 invertDividend_\n ) private view returns (uint256 rate_) {\n try feed_.latestRoundData() returns (uint80, int256 exchangeRate_, uint256, uint256, uint80) {\n // Return the price in `OracleUtils.RATE_OUTPUT_DECIMALS`\n if (invertRate_) {\n return invertDividend_ / uint256(exchangeRate_);\n } else {\n return uint256(exchangeRate_) * priceMultiplier_;\n }\n } catch {\n return 0;\n }\n }\n\n /// @notice returns all Chainlink oracle related data as utility for easy off-chain use / block explorer in a single view method\n function chainlinkOracleData()\n public\n view\n returns (\n uint256 chainlinkExchangeRate_,\n IChainlinkAggregatorV3 chainlinkFeed1_,\n bool chainlinkInvertRate1_,\n uint256 chainlinkExchangeRate1_,\n IChainlinkAggregatorV3 chainlinkFeed2_,\n bool chainlinkInvertRate2_,\n uint256 chainlinkExchangeRate2_,\n IChainlinkAggregatorV3 chainlinkFeed3_,\n bool chainlinkInvertRate3_,\n uint256 chainlinkExchangeRate3_\n )\n {\n return (\n _getChainlinkExchangeRate(),\n _CHAINLINK_FEED1,\n _CHAINLINK_INVERT_RATE1,\n _readFeedRate(\n _CHAINLINK_FEED1,\n _CHAINLINK_INVERT_RATE1,\n _CHAINLINK_PRICE_SCALER_MULTIPLIER1,\n _CHAINLINK_INVERT_PRICE_DIVIDEND1\n ),\n _CHAINLINK_FEED2,\n _CHAINLINK_INVERT_RATE2,\n address(_CHAINLINK_FEED2) == address(0)\n ? 0\n : _readFeedRate(\n _CHAINLINK_FEED2,\n _CHAINLINK_INVERT_RATE2,\n _CHAINLINK_PRICE_SCALER_MULTIPLIER2,\n _CHAINLINK_INVERT_PRICE_DIVIDEND2\n ),\n _CHAINLINK_FEED3,\n _CHAINLINK_INVERT_RATE3,\n address(_CHAINLINK_FEED3) == address(0)\n ? 0\n : _readFeedRate(\n _CHAINLINK_FEED3,\n _CHAINLINK_INVERT_RATE3,\n _CHAINLINK_PRICE_SCALER_MULTIPLIER3,\n _CHAINLINK_INVERT_PRICE_DIVIDEND3\n )\n );\n }\n}\n" }, "contracts/oracle/implementations/chainlinkOracleImpl2.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { ErrorTypes } from \"../errorTypes.sol\";\nimport { IChainlinkAggregatorV3 } from \"../interfaces/external/IChainlinkAggregatorV3.sol\";\nimport { Error as OracleError } from \"../error.sol\";\nimport { OracleUtils } from \"../libraries/oracleUtils.sol\";\nimport { ChainlinkStructs } from \"./structs.sol\";\n\n// @dev Exact same contract as ChainlinkOracleImpl, just with all vars, immutables etc. renamed with a \"2\" to avoid\n// conflicts when ChainlinkOracleImpl would have to be inherited twice.\n\n/// @title Chainlink Oracle implementation.\n/// @notice This contract is used to get the exchange rate via up to 3 hops at Chainlink price feeds.\n/// The rate is multiplied with the previous rate at each hop.\n/// E.g. to go from wBTC to USDC (assuming rates for example):\n/// 1. wBTC -> BTC https://data.chain.link/ethereum/mainnet/crypto-other/wbtc-btc, rate: 0.92.\n/// 2. BTC -> USD https://data.chain.link/ethereum/mainnet/crypto-usd/btc-usd rate: 30,000.\n/// 3. USD -> USDC https://data.chain.link/ethereum/mainnet/stablecoins/usdc-usd rate: 0.98. Must invert feed: 1.02\n/// finale rate would be: 0.92 * 30,000 * 1.02 = 28,152\nabstract contract ChainlinkOracleImpl2 is OracleError, ChainlinkStructs {\n /// @notice Chainlink price feed 1 to check for the exchange rate\n IChainlinkAggregatorV3 internal immutable _CHAINLINK2_FEED1;\n /// @notice Chainlink price feed 2 to check for the exchange rate\n IChainlinkAggregatorV3 internal immutable _CHAINLINK2_FEED2;\n /// @notice Chainlink price feed 3 to check for the exchange rate\n IChainlinkAggregatorV3 internal immutable _CHAINLINK2_FEED3;\n\n /// @notice Flag to invert the price or not for feed 1 (to e.g. for WETH/USDC pool return prive of USDC per 1 WETH)\n bool internal immutable _CHAINLINK2_INVERT_RATE1;\n /// @notice Flag to invert the price or not for feed 2 (to e.g. for WETH/USDC pool return prive of USDC per 1 WETH)\n bool internal immutable _CHAINLINK2_INVERT_RATE2;\n /// @notice Flag to invert the price or not for feed 3 (to e.g. for WETH/USDC pool return prive of USDC per 1 WETH)\n bool internal immutable _CHAINLINK2_INVERT_RATE3;\n\n /// @notice constant value for price scaling to reduce gas usage for feed 1\n uint256 internal immutable _CHAINLINK2_PRICE_SCALER_MULTIPLIER1;\n /// @notice constant value for inverting price to reduce gas usage for feed 1\n uint256 internal immutable _CHAINLINK2_INVERT_PRICE_DIVIDEND1;\n\n /// @notice constant value for price scaling to reduce gas usage for feed 2\n uint256 internal immutable _CHAINLINK2_PRICE_SCALER_MULTIPLIER2;\n /// @notice constant value for inverting price to reduce gas usage for feed 2\n uint256 internal immutable _CHAINLINK2_INVERT_PRICE_DIVIDEND2;\n\n /// @notice constant value for price scaling to reduce gas usage for feed 3\n uint256 internal immutable _CHAINLINK2_PRICE_SCALER_MULTIPLIER3;\n /// @notice constant value for inverting price to reduce gas usage for feed 3\n uint256 internal immutable _CHAINLINK2_INVERT_PRICE_DIVIDEND3;\n\n /// @notice constructor sets the Chainlink price feed and invertRate flag for each hop.\n /// E.g. `invertRate_` should be true if for the USDC/ETH pool it's expected that the oracle returns USDC per 1 ETH\n constructor(ChainlinkConstructorParams memory params_) {\n if (\n (params_.hops < 1 || params_.hops > 3) || // hops must be 1, 2 or 3\n (address(params_.feed1.feed) == address(0) || params_.feed1.token0Decimals == 0) || // first feed must always be defined\n (params_.hops > 1 && (address(params_.feed2.feed) == address(0) || params_.feed2.token0Decimals == 0)) || // if hops > 1, feed 2 must be defined\n (params_.hops > 2 && (address(params_.feed3.feed) == address(0) || params_.feed3.token0Decimals == 0)) // if hops > 2, feed 3 must be defined\n ) {\n revert FluidOracleError(ErrorTypes.ChainlinkOracle__InvalidParams);\n }\n\n _CHAINLINK2_FEED1 = params_.feed1.feed;\n _CHAINLINK2_FEED2 = params_.feed2.feed;\n _CHAINLINK2_FEED3 = params_.feed3.feed;\n\n _CHAINLINK2_INVERT_RATE1 = params_.feed1.invertRate;\n _CHAINLINK2_INVERT_RATE2 = params_.feed2.invertRate;\n _CHAINLINK2_INVERT_RATE3 = params_.feed3.invertRate;\n\n // Actual desired output rate example USDC/ETH (6 decimals / 18 decimals).\n // Note ETH has 12 decimals more than USDC.\n // 0.000515525322211842331991619857165357691 // 39 decimals. ETH for 1 USDC\n // 1954.190000000000433 // 15 decimals. USDC for 1 ETH\n\n // to get to PRICE_SCLAER_MULTIPLIER and INVERT_PRICE_DIVIDEND:\n // fetched Chainlink price is in token1Decimals per 1 token0Decimals.\n // E.g. for an USDC/ETH price feed it's in ETH 18 decimals.\n // for an BTC/USD price feed it's in USD 8 decimals.\n // So to scale to 1e27 we need to multiply by 1e27 - token0Decimals.\n // E.g. for USDC/ETH it would be: fetchedPrice * 1e21\n //\n // or for inverted (x token0 per 1 token1), formula would be:\n // = 1e27 * 10**token0Decimals / fetchedPrice\n // E.g. for USDC/ETH it would be: 1e33 / fetchedPrice\n\n // no support for token1Decimals with more than OracleUtils.RATE_OUTPUT_DECIMALS decimals for now as extremely unlikely case\n _CHAINLINK2_PRICE_SCALER_MULTIPLIER1 = 10 ** (OracleUtils.RATE_OUTPUT_DECIMALS - params_.feed1.token0Decimals);\n _CHAINLINK2_INVERT_PRICE_DIVIDEND1 = 10 ** (OracleUtils.RATE_OUTPUT_DECIMALS + params_.feed1.token0Decimals);\n\n _CHAINLINK2_PRICE_SCALER_MULTIPLIER2 = params_.hops > 1\n ? 10 ** (OracleUtils.RATE_OUTPUT_DECIMALS - params_.feed2.token0Decimals)\n : 1;\n _CHAINLINK2_INVERT_PRICE_DIVIDEND2 = params_.hops > 1\n ? 10 ** (OracleUtils.RATE_OUTPUT_DECIMALS + params_.feed2.token0Decimals)\n : 1;\n\n _CHAINLINK2_PRICE_SCALER_MULTIPLIER3 = params_.hops > 2\n ? 10 ** (OracleUtils.RATE_OUTPUT_DECIMALS - params_.feed3.token0Decimals)\n : 1;\n _CHAINLINK2_INVERT_PRICE_DIVIDEND3 = params_.hops > 2\n ? 10 ** (OracleUtils.RATE_OUTPUT_DECIMALS + params_.feed3.token0Decimals)\n : 1;\n }\n\n /// @dev Get the exchange rate from Chainlike oracle price feed(s)\n /// @return rate_ The exchange rate in `OracleUtils.RATE_OUTPUT_DECIMALS`\n function _getChainlinkExchangeRate2() internal view returns (uint256 rate_) {\n rate_ = _readFeedRate2(\n _CHAINLINK2_FEED1,\n _CHAINLINK2_INVERT_RATE1,\n _CHAINLINK2_PRICE_SCALER_MULTIPLIER1,\n _CHAINLINK2_INVERT_PRICE_DIVIDEND1\n );\n if (rate_ == 0 || address(_CHAINLINK2_FEED2) == address(0)) {\n // rate 0 or only 1 hop -> return rate of price feed 1\n return rate_;\n }\n rate_ =\n (rate_ *\n _readFeedRate2(\n _CHAINLINK2_FEED2,\n _CHAINLINK2_INVERT_RATE2,\n _CHAINLINK2_PRICE_SCALER_MULTIPLIER2,\n _CHAINLINK2_INVERT_PRICE_DIVIDEND2\n )) /\n (10 ** OracleUtils.RATE_OUTPUT_DECIMALS);\n\n if (rate_ == 0 || address(_CHAINLINK2_FEED3) == address(0)) {\n // rate 0 or 2 hops -> return rate of feed 1 combined with feed 2\n return rate_;\n }\n\n // 3 hops -> return rate of feed 1 combined with feed 2 & feed 3\n rate_ =\n (rate_ *\n _readFeedRate2(\n _CHAINLINK2_FEED3,\n _CHAINLINK2_INVERT_RATE3,\n _CHAINLINK2_PRICE_SCALER_MULTIPLIER3,\n _CHAINLINK2_INVERT_PRICE_DIVIDEND3\n )) /\n (10 ** OracleUtils.RATE_OUTPUT_DECIMALS);\n }\n\n /// @dev reads the exchange `rate_` from a Chainlink price `feed_` taking into account scaling and `invertRate_`\n function _readFeedRate2(\n IChainlinkAggregatorV3 feed_,\n bool invertRate_,\n uint256 priceMultiplier_,\n uint256 invertDividend_\n ) private view returns (uint256 rate_) {\n try feed_.latestRoundData() returns (uint80, int256 exchangeRate_, uint256, uint256, uint80) {\n // Return the price in `OracleUtils.RATE_OUTPUT_DECIMALS`\n if (invertRate_) {\n return invertDividend_ / uint256(exchangeRate_);\n } else {\n return uint256(exchangeRate_) * priceMultiplier_;\n }\n } catch {\n return 0;\n }\n }\n\n /// @notice returns all Chainlink oracle related data as utility for easy off-chain use / block explorer in a single view method\n function chainlinkOracleData2()\n public\n view\n returns (\n uint256 chainlinkExchangeRate_,\n IChainlinkAggregatorV3 chainlinkFeed1_,\n bool chainlinkInvertRate1_,\n uint256 chainlinkExchangeRate1_,\n IChainlinkAggregatorV3 chainlinkFeed2_,\n bool chainlinkInvertRate2_,\n uint256 chainlinkExchangeRate2_,\n IChainlinkAggregatorV3 chainlinkFeed3_,\n bool chainlinkInvertRate3_,\n uint256 chainlinkExchangeRate3_\n )\n {\n return (\n _getChainlinkExchangeRate2(),\n _CHAINLINK2_FEED1,\n _CHAINLINK2_INVERT_RATE1,\n _readFeedRate2(\n _CHAINLINK2_FEED1,\n _CHAINLINK2_INVERT_RATE1,\n _CHAINLINK2_PRICE_SCALER_MULTIPLIER1,\n _CHAINLINK2_INVERT_PRICE_DIVIDEND1\n ),\n _CHAINLINK2_FEED2,\n _CHAINLINK2_INVERT_RATE2,\n address(_CHAINLINK2_FEED2) == address(0)\n ? 0\n : _readFeedRate2(\n _CHAINLINK2_FEED2,\n _CHAINLINK2_INVERT_RATE2,\n _CHAINLINK2_PRICE_SCALER_MULTIPLIER2,\n _CHAINLINK2_INVERT_PRICE_DIVIDEND2\n ),\n _CHAINLINK2_FEED3,\n _CHAINLINK2_INVERT_RATE3,\n address(_CHAINLINK2_FEED3) == address(0)\n ? 0\n : _readFeedRate2(\n _CHAINLINK2_FEED3,\n _CHAINLINK2_INVERT_RATE3,\n _CHAINLINK2_PRICE_SCALER_MULTIPLIER3,\n _CHAINLINK2_INVERT_PRICE_DIVIDEND3\n )\n );\n }\n}\n" }, "contracts/oracle/implementations/fallbackOracleImpl.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { ErrorTypes } from \"../errorTypes.sol\";\nimport { IRedstoneOracle } from \"../interfaces/external/IRedstoneOracle.sol\";\nimport { Error as OracleError } from \"../error.sol\";\nimport { ChainlinkOracleImpl } from \"./chainlinkOracleImpl.sol\";\nimport { RedstoneOracleImpl } from \"./redstoneOracleImpl.sol\";\n\n/// @title Fallback Oracle implementation\n/// @notice This contract is used to get the exchange rate from a main oracle feed and a fallback oracle feed.\n//\n// @dev inheriting contracts should implement a view method to expose `_FALLBACK_ORACLE_MAIN_SOURCE`\nabstract contract FallbackOracleImpl is OracleError, RedstoneOracleImpl, ChainlinkOracleImpl {\n /// @dev which oracle to use as main source:\n /// - 1 = Chainlink ONLY (no fallback)\n /// - 2 = Chainlink with Redstone Fallback\n /// - 3 = Redstone with Chainlink Fallback\n uint8 internal immutable _FALLBACK_ORACLE_MAIN_SOURCE;\n\n /// @notice sets the main source, Chainlink Oracle and Redstone Oracle data.\n /// @param mainSource_ which oracle to use as main source:\n /// - 1 = Chainlink ONLY (no fallback)\n /// - 2 = Chainlink with Redstone Fallback\n /// - 3 = Redstone with Chainlink Fallback\n /// @param chainlinkParams_ chainlink Oracle constructor params struct.\n /// @param redstoneOracle_ Redstone Oracle data. (address can be set to zero address if using Chainlink only)\n constructor(\n uint8 mainSource_,\n ChainlinkConstructorParams memory chainlinkParams_,\n RedstoneOracleData memory redstoneOracle_\n )\n ChainlinkOracleImpl(chainlinkParams_)\n RedstoneOracleImpl(\n address(redstoneOracle_.oracle) == address(0)\n ? RedstoneOracleData(IRedstoneOracle(_REDSTONE_ORACLE_NOT_SET_ADDRESS), false, 1)\n : redstoneOracle_\n )\n {\n if (mainSource_ < 1 || mainSource_ > 3) {\n revert FluidOracleError(ErrorTypes.FallbackOracle__InvalidParams);\n }\n _FALLBACK_ORACLE_MAIN_SOURCE = mainSource_;\n }\n\n /// @dev returns the exchange rate for the main oracle source, or the fallback source (if configured) if the main exchange rate\n /// fails to be fetched. If returned rate is 0, fetching rate failed or something went wrong.\n /// @return exchangeRate_ exchange rate\n /// @return fallback_ whether fallback was necessary or not\n function _getRateWithFallback() internal view returns (uint256 exchangeRate_, bool fallback_) {\n if (_FALLBACK_ORACLE_MAIN_SOURCE == 1) {\n // 1 = Chainlink ONLY (no fallback)\n exchangeRate_ = _getChainlinkExchangeRate();\n } else if (_FALLBACK_ORACLE_MAIN_SOURCE == 2) {\n // 2 = Chainlink with Redstone Fallback\n exchangeRate_ = _getChainlinkExchangeRate();\n if (exchangeRate_ == 0) {\n fallback_ = true;\n exchangeRate_ = _getRedstoneExchangeRate();\n }\n } else {\n // 3 = Redstone with Chainlink Fallback\n exchangeRate_ = _getRedstoneExchangeRate();\n if (exchangeRate_ == 0) {\n fallback_ = true;\n exchangeRate_ = _getChainlinkExchangeRate();\n }\n }\n }\n\n /// @dev returns the exchange rate for Chainlink, or Redstone if configured & Chainlink fails.\n function _getChainlinkOrRedstoneAsFallback() internal view returns (uint256 exchangeRate_) {\n exchangeRate_ = _getChainlinkExchangeRate();\n\n if (exchangeRate_ == 0 && _FALLBACK_ORACLE_MAIN_SOURCE != 1) {\n // Chainlink failed but Redstone is configured too -> try Redstone\n exchangeRate_ = _getRedstoneExchangeRate();\n }\n }\n}\n" }, "contracts/oracle/implementations/fallbackOracleImpl2.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { ErrorTypes } from \"../errorTypes.sol\";\nimport { IRedstoneOracle } from \"../interfaces/external/IRedstoneOracle.sol\";\nimport { Error as OracleError } from \"../error.sol\";\nimport { ChainlinkOracleImpl2 } from \"./chainlinkOracleImpl2.sol\";\nimport { RedstoneOracleImpl2 } from \"./redstoneOracleImpl2.sol\";\n\n// @dev Exact same contract as FallbackOracleImpl, just with all vars, immutables etc. renamed with a \"2\" and inheriting\n// to ChainlinkOracleImpl2 and RedstoneOracleImpl2 to avoid conflicts when FallbackOracleImpl would have to be inherited twice.\n\n/// @title Fallback Oracle implementation\n/// @notice This contract is used to get the exchange rate from a main oracle feed and a fallback oracle feed.\n//\n// @dev inheriting contracts should implement a view method to expose `_FALLBACK_ORACLE2_MAIN_SOURCE`\nabstract contract FallbackOracleImpl2 is OracleError, RedstoneOracleImpl2, ChainlinkOracleImpl2 {\n /// @dev which oracle to use as main source:\n /// - 1 = Chainlink ONLY (no fallback)\n /// - 2 = Chainlink with Redstone Fallback\n /// - 3 = Redstone with Chainlink Fallback\n uint8 internal immutable _FALLBACK_ORACLE2_MAIN_SOURCE;\n\n /// @notice sets the main source, Chainlink Oracle and Redstone Oracle data.\n /// @param mainSource_ which oracle to use as main source:\n /// - 1 = Chainlink ONLY (no fallback)\n /// - 2 = Chainlink with Redstone Fallback\n /// - 3 = Redstone with Chainlink Fallback\n /// @param chainlinkParams_ chainlink Oracle constructor params struct.\n /// @param redstoneOracle_ Redstone Oracle data. (address can be set to zero address if using Chainlink only)\n constructor(\n uint8 mainSource_,\n ChainlinkConstructorParams memory chainlinkParams_,\n RedstoneOracleData memory redstoneOracle_\n )\n ChainlinkOracleImpl2(chainlinkParams_)\n RedstoneOracleImpl2(\n address(redstoneOracle_.oracle) == address(0)\n ? RedstoneOracleData(IRedstoneOracle(_REDSTONE2_ORACLE_NOT_SET_ADDRESS), false, 1)\n : redstoneOracle_\n )\n {\n if (mainSource_ < 1 || mainSource_ > 3) {\n revert FluidOracleError(ErrorTypes.FallbackOracle__InvalidParams);\n }\n _FALLBACK_ORACLE2_MAIN_SOURCE = mainSource_;\n }\n\n /// @dev returns the exchange rate for the main oracle source, or the fallback source (if configured) if the main exchange rate\n /// fails to be fetched. If returned rate is 0, fetching rate failed or something went wrong.\n /// @return exchangeRate_ exchange rate\n /// @return fallback_ whether fallback was necessary or not\n function _getRateWithFallback2() internal view returns (uint256 exchangeRate_, bool fallback_) {\n if (_FALLBACK_ORACLE2_MAIN_SOURCE == 1) {\n // 1 = Chainlink ONLY (no fallback)\n exchangeRate_ = _getChainlinkExchangeRate2();\n } else if (_FALLBACK_ORACLE2_MAIN_SOURCE == 2) {\n // 2 = Chainlink with Redstone Fallback\n exchangeRate_ = _getChainlinkExchangeRate2();\n if (exchangeRate_ == 0) {\n fallback_ = true;\n exchangeRate_ = _getRedstoneExchangeRate2();\n }\n } else {\n // 3 = Redstone with Chainlink Fallback\n exchangeRate_ = _getRedstoneExchangeRate2();\n if (exchangeRate_ == 0) {\n fallback_ = true;\n exchangeRate_ = _getChainlinkExchangeRate2();\n }\n }\n }\n\n /// @dev returns the exchange rate for Chainlink, or Redstone if configured & Chainlink fails.\n function _getChainlinkOrRedstoneAsFallback2() internal view returns (uint256 exchangeRate_) {\n exchangeRate_ = _getChainlinkExchangeRate2();\n\n if (exchangeRate_ == 0 && _FALLBACK_ORACLE2_MAIN_SOURCE != 1) {\n // Chainlink failed but Redstone is configured too -> try Redstone\n exchangeRate_ = _getRedstoneExchangeRate2();\n }\n }\n}\n" }, "contracts/oracle/implementations/redstoneOracleImpl.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { ErrorTypes } from \"../errorTypes.sol\";\nimport { IRedstoneOracle } from \"../interfaces/external/IRedstoneOracle.sol\";\nimport { Error as OracleError } from \"../error.sol\";\nimport { OracleUtils } from \"../libraries/oracleUtils.sol\";\nimport { RedstoneStructs } from \"./structs.sol\";\n\n/// @title Redstone Oracle implementation\n/// @notice This contract is used to get the exchange rate from a Redstone Oracle\nabstract contract RedstoneOracleImpl is OracleError, RedstoneStructs {\n /// @notice Redstone price oracle to check for the exchange rate\n IRedstoneOracle internal immutable _REDSTONE_ORACLE;\n /// @notice Flag to invert the price or not (to e.g. for WETH/USDC pool return prive of USDC per 1 WETH)\n bool internal immutable _REDSTONE_INVERT_RATE;\n\n /// @notice constant value for price scaling to reduce gas usage\n uint256 internal immutable _REDSTONE_PRICE_SCALER_MULTIPLIER;\n /// @notice constant value for inverting price to reduce gas usage\n uint256 internal immutable _REDSTONE_INVERT_PRICE_DIVIDEND;\n\n address internal immutable _REDSTONE_ORACLE_NOT_SET_ADDRESS = 0x000000000000000000000000000000000000dEaD;\n\n /// @notice constructor sets the Redstone oracle data\n constructor(RedstoneOracleData memory oracleData_) {\n if (address(oracleData_.oracle) == address(0) || oracleData_.token0Decimals == 0) {\n revert FluidOracleError(ErrorTypes.RedstoneOracle__InvalidParams);\n }\n\n _REDSTONE_ORACLE = oracleData_.oracle;\n _REDSTONE_INVERT_RATE = oracleData_.invertRate;\n\n // for explanation on how to get to scaler multiplier and dividend see `chainlinkOracleImpl.sol`.\n // no support for token1Decimals with more than OracleUtils.RATE_OUTPUT_DECIMALS decimals for now as extremely unlikely case\n _REDSTONE_PRICE_SCALER_MULTIPLIER = address(oracleData_.oracle) == _REDSTONE_ORACLE_NOT_SET_ADDRESS\n ? 1\n : 10 ** (OracleUtils.RATE_OUTPUT_DECIMALS - oracleData_.token0Decimals);\n _REDSTONE_INVERT_PRICE_DIVIDEND = address(oracleData_.oracle) == _REDSTONE_ORACLE_NOT_SET_ADDRESS\n ? 1\n : 10 ** (OracleUtils.RATE_OUTPUT_DECIMALS + oracleData_.token0Decimals);\n }\n\n /// @dev Get the exchange rate from Redstone oracle\n /// @param rate_ The exchange rate in `OracleUtils.RATE_OUTPUT_DECIMALS`\n function _getRedstoneExchangeRate() internal view returns (uint256 rate_) {\n try _REDSTONE_ORACLE.getExchangeRate() returns (uint256 exchangeRate_) {\n if (_REDSTONE_INVERT_RATE) {\n // invert the price\n return _REDSTONE_INVERT_PRICE_DIVIDEND / exchangeRate_;\n } else {\n return exchangeRate_ * _REDSTONE_PRICE_SCALER_MULTIPLIER;\n }\n } catch {\n return 0;\n }\n }\n\n /// @notice returns all Redstone oracle related data as utility for easy off-chain use / block explorer in a single view method\n function redstoneOracleData()\n public\n view\n returns (uint256 redstoneExchangeRate_, IRedstoneOracle redstoneOracle_, bool redstoneInvertRate_)\n {\n return (\n address(_REDSTONE_ORACLE) == _REDSTONE_ORACLE_NOT_SET_ADDRESS ? 0 : _getRedstoneExchangeRate(),\n _REDSTONE_ORACLE,\n _REDSTONE_INVERT_RATE\n );\n }\n}\n" }, "contracts/oracle/implementations/redstoneOracleImpl2.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { ErrorTypes } from \"../errorTypes.sol\";\nimport { IRedstoneOracle } from \"../interfaces/external/IRedstoneOracle.sol\";\nimport { Error as OracleError } from \"../error.sol\";\nimport { OracleUtils } from \"../libraries/oracleUtils.sol\";\nimport { RedstoneStructs } from \"./structs.sol\";\n\n// @dev Exact same contract as RedstoneOracleImpl, just with all vars, immutables etc. renamed with a \"2\" to avoid\n// conflicts when RedstoneOracleImpl would have to be inherited twice.\n\n/// @title Redstone Oracle implementation\n/// @notice This contract is used to get the exchange rate from a Redstone Oracle\nabstract contract RedstoneOracleImpl2 is OracleError, RedstoneStructs {\n /// @notice Redstone price oracle to check for the exchange rate\n IRedstoneOracle internal immutable _REDSTONE2_ORACLE;\n /// @notice Flag to invert the price or not (to e.g. for WETH/USDC pool return prive of USDC per 1 WETH)\n bool internal immutable _REDSTONE2_INVERT_RATE;\n\n /// @notice constant value for price scaling to reduce gas usage\n uint256 internal immutable _REDSTONE2_PRICE_SCALER_MULTIPLIER;\n /// @notice constant value for inverting price to reduce gas usage\n uint256 internal immutable _REDSTONE2_INVERT_PRICE_DIVIDEND;\n\n address internal immutable _REDSTONE2_ORACLE_NOT_SET_ADDRESS = 0x000000000000000000000000000000000000dEaD;\n\n /// @notice constructor sets the Redstone oracle data\n constructor(RedstoneOracleData memory oracleData_) {\n if (address(oracleData_.oracle) == address(0) || oracleData_.token0Decimals == 0) {\n revert FluidOracleError(ErrorTypes.RedstoneOracle__InvalidParams);\n }\n\n _REDSTONE2_ORACLE = oracleData_.oracle;\n _REDSTONE2_INVERT_RATE = oracleData_.invertRate;\n\n // for explanation on how to get to scaler multiplier and dividend see `chainlinkOracleImpl.sol`.\n // no support for token1Decimals with more than OracleUtils.RATE_OUTPUT_DECIMALS decimals for now as extremely unlikely case\n _REDSTONE2_PRICE_SCALER_MULTIPLIER = address(oracleData_.oracle) == _REDSTONE2_ORACLE_NOT_SET_ADDRESS\n ? 1\n : 10 ** (OracleUtils.RATE_OUTPUT_DECIMALS - oracleData_.token0Decimals);\n _REDSTONE2_INVERT_PRICE_DIVIDEND = address(oracleData_.oracle) == _REDSTONE2_ORACLE_NOT_SET_ADDRESS\n ? 1\n : 10 ** (OracleUtils.RATE_OUTPUT_DECIMALS + oracleData_.token0Decimals);\n }\n\n /// @dev Get the exchange rate from Redstone oracle\n /// @param rate_ The exchange rate in `OracleUtils.RATE_OUTPUT_DECIMALS`\n function _getRedstoneExchangeRate2() internal view returns (uint256 rate_) {\n try _REDSTONE2_ORACLE.getExchangeRate() returns (uint256 exchangeRate_) {\n if (_REDSTONE2_INVERT_RATE) {\n // invert the price\n return _REDSTONE2_INVERT_PRICE_DIVIDEND / exchangeRate_;\n } else {\n return exchangeRate_ * _REDSTONE2_PRICE_SCALER_MULTIPLIER;\n }\n } catch {\n return 0;\n }\n }\n\n /// @notice returns all Redstone oracle related data as utility for easy off-chain use / block explorer in a single view method\n function redstoneOracleData2()\n public\n view\n returns (uint256 redstoneExchangeRate_, IRedstoneOracle redstoneOracle_, bool redstoneInvertRate_)\n {\n return (\n address(_REDSTONE2_ORACLE) == _REDSTONE2_ORACLE_NOT_SET_ADDRESS ? 0 : _getRedstoneExchangeRate2(),\n _REDSTONE2_ORACLE,\n _REDSTONE2_INVERT_RATE\n );\n }\n}\n" }, "contracts/oracle/implementations/structs.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { IChainlinkAggregatorV3 } from \"../interfaces/external/IChainlinkAggregatorV3.sol\";\nimport { IRedstoneOracle } from \"../interfaces/external/IRedstoneOracle.sol\";\n\nabstract contract ChainlinkStructs {\n struct ChainlinkFeedData {\n /// @param feed address of Chainlink feed.\n IChainlinkAggregatorV3 feed;\n /// @param invertRate true if rate read from price feed must be inverted.\n bool invertRate;\n /// @param token0Decimals decimals of asset 0. E.g. for a USDC/ETH feed, USDC is token0 and has 6 decimals.\n /// (token1Decimals are available directly via Chainlink `FEED.decimals()`)\n uint256 token0Decimals;\n }\n\n struct ChainlinkConstructorParams {\n /// @param param hops count of hops, used for sanity checks. Must be 1, 2 or 3.\n uint8 hops;\n /// @param feed1 Chainlink feed 1 data. Required.\n ChainlinkFeedData feed1;\n /// @param feed2 Chainlink feed 2 data. Required if hops > 1.\n ChainlinkFeedData feed2;\n /// @param feed3 Chainlink feed 3 data. Required if hops > 2.\n ChainlinkFeedData feed3;\n }\n}\n\nabstract contract RedstoneStructs {\n struct RedstoneOracleData {\n /// @param oracle address of Redstone oracle.\n IRedstoneOracle oracle;\n /// @param invertRate true if rate read from price feed must be inverted.\n bool invertRate;\n /// @param token0Decimals decimals of asset 0. E.g. for a USDC/ETH feed, USDC is token0 and has 6 decimals.\n /// (token1Decimals are available directly via Redstone `Oracle.decimals()`)\n uint256 token0Decimals;\n }\n}\n" }, "contracts/oracle/interfaces/external/IChainlinkAggregatorV3.sol": { "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.21;\n\n/// from https://github.com/smartcontractkit/chainlink/blob/master/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol\n/// Copyright (c) 2018 SmartContract ChainLink, Ltd.\n\ninterface IChainlinkAggregatorV3 {\n /// @notice represents the number of decimals the aggregator responses represent.\n function decimals() external view returns (uint8);\n\n function description() external view returns (string memory);\n\n function version() external view returns (uint256);\n\n function getRoundData(\n uint80 _roundId\n )\n external\n view\n returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);\n\n function latestRoundData()\n external\n view\n returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);\n}\n" }, "contracts/oracle/interfaces/external/IRedstoneOracle.sol": { "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.21;\n\ninterface IRedstoneOracle {\n /// @notice Get the `exchangeRate_` between the underlying asset and the peg asset\n // @dev custom Redstone adapter for Instadapp implementation\n function getExchangeRate() external view returns (uint256 exchangeRate_);\n\n /**\n * @notice Returns the number of decimals for the price feed\n * @dev By default, RedStone uses 8 decimals for data feeds\n * @return decimals The number of decimals in the price feed values\n */\n // see https://github.com/redstone-finance/redstone-oracles-monorepo/blob/main/packages/on-chain-relayer/contracts/price-feeds/PriceFeedBase.sol#L51C12-L51C20\n function decimals() external view returns (uint8);\n}\n" }, "contracts/oracle/interfaces/iFluidOracle.sol": { "content": "// SPDX-License-Identifier: MIT\npragma solidity 0.8.21;\n\ninterface IFluidOracle {\n /// @dev Deprecated. Use `getExchangeRateOperate()` and `getExchangeRateLiquidate()` instead. Only implemented for\n /// backwards compatibility.\n function getExchangeRate() external view returns (uint256 exchangeRate_);\n\n /// @notice Get the `exchangeRate_` between the underlying asset and the peg asset in 1e27 for operates\n function getExchangeRateOperate() external view returns (uint256 exchangeRate_);\n\n /// @notice Get the `exchangeRate_` between the underlying asset and the peg asset in 1e27 for liquidations\n function getExchangeRateLiquidate() external view returns (uint256 exchangeRate_);\n\n /// @notice helper string to easily identify the oracle. E.g. token symbols\n function infoName() external view returns (string memory);\n}\n" }, "contracts/oracle/libraries/oracleUtils.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\n/// @title Oracle utils library\n/// @notice implements common utility methods for Fluid Oracles\nlibrary OracleUtils {\n /// @dev The scaler for max delta point math (100%)\n uint256 internal constant HUNDRED_PERCENT_DELTA_SCALER = 10_000;\n /// @dev output precision of rates\n uint256 internal constant RATE_OUTPUT_DECIMALS = 27;\n\n /// @dev checks if `mainSourceRate_` is within a `maxDeltaPercent_` of `checkSourceRate_`. Returns true if so.\n function isRateOutsideDelta(\n uint256 mainSourceRate_,\n uint256 checkSourceRate_,\n uint256 maxDeltaPercent_\n ) internal pure returns (bool) {\n uint256 offset_ = (checkSourceRate_ * maxDeltaPercent_) / HUNDRED_PERCENT_DELTA_SCALER;\n return (mainSourceRate_ > (checkSourceRate_ + offset_) || mainSourceRate_ < (checkSourceRate_ - offset_));\n }\n}\n" }, "contracts/oracle/oracles/fallbackCLRSOracle.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { FluidOracle } from \"../fluidOracle.sol\";\nimport { FallbackOracleImpl } from \"../implementations/fallbackOracleImpl.sol\";\nimport { ErrorTypes } from \"../errorTypes.sol\";\n\n/// @title Chainlink / Redstone Oracle (with fallback)\n/// @notice Gets the exchange rate between the underlying asset and the peg asset by using:\n/// the price from a Chainlink price feed or a Redstone Oracle with one of them being used as main source and\n/// the other one acting as a fallback if the main source fails for any reason. Reverts if fetched rate is 0.\ncontract FallbackCLRSOracle is FluidOracle, FallbackOracleImpl {\n /// @notice sets the main source, Chainlink Oracle and Redstone Oracle data.\n /// @param infoName_ Oracle identify helper name.\n /// @param mainSource_ which oracle to use as main source: 1 = Chainlink, 2 = Redstone (other one is fallback).\n /// @param chainlinkParams_ chainlink Oracle constructor params struct.\n /// @param redstoneOracle_ Redstone Oracle data. (address can be set to zero address if using Chainlink only)\n constructor(\n string memory infoName_,\n uint8 mainSource_,\n ChainlinkConstructorParams memory chainlinkParams_,\n RedstoneOracleData memory redstoneOracle_\n ) FallbackOracleImpl(mainSource_, chainlinkParams_, redstoneOracle_) FluidOracle(infoName_) {}\n\n /// @inheritdoc FluidOracle\n function getExchangeRateOperate() public view virtual override returns (uint256 exchangeRate_) {\n (exchangeRate_, ) = _getRateWithFallback();\n\n if (exchangeRate_ == 0) {\n revert FluidOracleError(ErrorTypes.FallbackCLRSOracle__ExchangeRateZero);\n }\n }\n\n /// @inheritdoc FluidOracle\n function getExchangeRateLiquidate() public view virtual override returns (uint256 exchangeRate_) {\n (exchangeRate_, ) = _getRateWithFallback();\n\n if (exchangeRate_ == 0) {\n revert FluidOracleError(ErrorTypes.FallbackCLRSOracle__ExchangeRateZero);\n }\n }\n\n /// @inheritdoc FluidOracle\n function getExchangeRate() public view virtual override returns (uint256 exchangeRate_) {\n return getExchangeRateOperate();\n }\n\n /// @notice which oracle to use as main source:\n /// - 1 = Chainlink ONLY (no fallback)\n /// - 2 = Chainlink with Redstone Fallback\n /// - 3 = Redstone with Chainlink Fallback\n function FALLBACK_ORACLE_MAIN_SOURCE() public view returns (uint8) {\n return _FALLBACK_ORACLE_MAIN_SOURCE;\n }\n}\n" }, "contracts/oracle/oraclesL2/fallbackCLRSOracleL2.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { FluidOracleL2 } from \"../fluidOracleL2.sol\";\nimport { FallbackCLRSOracle } from \"../oracles/fallbackCLRSOracle.sol\";\n\n/// @title Chainlink / Redstone Oracle (with fallback) for Layer 2 (with sequencer outage detection)\n/// @notice Gets the exchange rate between the underlying asset and the peg asset by using:\n/// the price from a Chainlink price feed or a Redstone Oracle with one of them being used as main source and\n/// the other one acting as a fallback if the main source fails for any reason. Reverts if fetched rate is 0.\ncontract FallbackCLRSOracleL2 is FluidOracleL2, FallbackCLRSOracle {\n struct CLRSConstructorParams {\n /// @param mainSource which oracle to use as main source for wstETH <> CLRS: 1 = Chainlink, 2 = Redstone (other one is fallback).\n uint8 mainSource;\n /// @param chainlinkParams chainlink Oracle constructor params struct for wstETH <> CLRS.\n ChainlinkConstructorParams chainlinkParams;\n /// @param redstoneOracle Redstone Oracle data for wstETH <> CLRS. (address can be set to zero address if using Chainlink only)\n RedstoneOracleData redstoneOracle;\n }\n\n /// @notice sets the main source, Chainlink Oracle and Redstone Oracle data.\n /// @param infoName_ Oracle identify helper name.\n /// @param cLRSParams_ CLRS Fallback Oracle data\n /// @param sequencerUptimeFeed_ L2 sequencer uptime Chainlink feed\n constructor(\n string memory infoName_,\n CLRSConstructorParams memory cLRSParams_,\n address sequencerUptimeFeed_\n )\n FallbackCLRSOracle(infoName_, cLRSParams_.mainSource, cLRSParams_.chainlinkParams, cLRSParams_.redstoneOracle)\n FluidOracleL2(sequencerUptimeFeed_)\n {}\n\n /// @inheritdoc FluidOracleL2\n function getExchangeRateOperate()\n public\n view\n virtual\n override(FallbackCLRSOracle, FluidOracleL2)\n returns (uint256 exchangeRate_)\n {\n _ensureSequencerUpAndValid();\n return super.getExchangeRateOperate();\n }\n\n /// @inheritdoc FluidOracleL2\n function getExchangeRateLiquidate()\n public\n view\n virtual\n override(FallbackCLRSOracle, FluidOracleL2)\n returns (uint256 exchangeRate_)\n {\n _ensureSequencerUpAndValid();\n return super.getExchangeRateLiquidate();\n }\n\n /// @inheritdoc FluidOracleL2\n function getExchangeRate()\n public\n view\n virtual\n override(FallbackCLRSOracle, FluidOracleL2)\n returns (uint256 exchangeRate_)\n {\n return getExchangeRateOperate();\n }\n}\n" }, "contracts/oracle/oraclesL2/ratio2xFallbackCLRSOracleL2.sol": { "content": "// SPDX-License-Identifier: BUSL-1.1\npragma solidity 0.8.21;\n\nimport { FluidOracleL2 } from \"../fluidOracleL2.sol\";\nimport { FallbackCLRSOracleL2 } from \"./fallbackCLRSOracleL2.sol\";\nimport { FallbackOracleImpl2 } from \"../implementations/fallbackOracleImpl2.sol\";\nimport { OracleUtils } from \"../libraries/oracleUtils.sol\";\nimport { ErrorTypes } from \"../errorTypes.sol\";\n\n/// @title Ratio of 2 Chainlink / Redstone Oracles (with fallback) for Layer 2 (with sequencer outage detection)\n/// @notice Gets the exchange rate between the underlying asset and the peg asset by using:\n/// 1. the price from a Chainlink price feed or a Redstone Oracle with one of them being used as main source and\n/// the other one acting as a fallback if the main source fails for any reason. Reverts if fetched rate is 0.\n/// 2. set into ratio with another price fetched the same way.\n/// I.e. it is possible to do Chainlink Oracle / Chainlink Oracle. E.g. wstETH per 1 weETH on a L2 via CL feeds.\ncontract Ratio2xFallbackCLRSOracleL2 is FluidOracleL2, FallbackOracleImpl2, FallbackCLRSOracleL2 {\n /// @notice sets the two CLRS oracle configs\n /// @param infoName_ Oracle identify helper name.\n /// @param cLRSParams1_ CLRS Fallback Oracle data for ratio dividend\n /// @param cLRSParams2_ CLRS Fallback Oracle data for ratio divisor\n /// @param sequencerUptimeFeed_ L2 sequencer uptime Chainlink feed\n constructor(\n string memory infoName_,\n FallbackCLRSOracleL2.CLRSConstructorParams memory cLRSParams1_,\n FallbackCLRSOracleL2.CLRSConstructorParams memory cLRSParams2_,\n address sequencerUptimeFeed_\n )\n FallbackCLRSOracleL2(infoName_, cLRSParams1_, sequencerUptimeFeed_)\n FallbackOracleImpl2(cLRSParams2_.mainSource, cLRSParams2_.chainlinkParams, cLRSParams2_.redstoneOracle)\n {}\n\n /// @inheritdoc FluidOracleL2\n function getExchangeRateOperate()\n public\n view\n override(FluidOracleL2, FallbackCLRSOracleL2)\n returns (uint256 exchangeRate_)\n {\n // 1. get CLRS Oracle rate from FallbackOracleImpl2 for divisor (cLRSParams2_)\n (exchangeRate_, ) = _getRateWithFallback2();\n if (exchangeRate_ == 0) {\n // revert if fetched exchange rate is 0\n revert FluidOracleError(ErrorTypes.Ratio2xFallbackCLRSOracleL2__ExchangeRateZero);\n }\n\n // 2. get rate from FallbackCLRSOracleL2 (cLRSParams1_). This already checks and reverts in case of price being 0.\n // (super.getExchangeRate returns FallbackCLRSOracleL2 rate, no other inherited contract has this.)\n // includes _ensureSequencerUpAndValid();\n\n // 3. Setting into ratio cLRSParams1_ rate / cLRSParams2_ rate\n exchangeRate_ = (super.getExchangeRateOperate() * (10 ** OracleUtils.RATE_OUTPUT_DECIMALS)) / exchangeRate_;\n // e.g. FallbackCLRSOracleL2 configured to return weETH rate, _getRateWithFallback2 configured to return wstETH:\n // result is wstETH per weETH\n }\n\n /// @inheritdoc FluidOracleL2\n function getExchangeRateLiquidate()\n public\n view\n override(FluidOracleL2, FallbackCLRSOracleL2)\n returns (uint256 exchangeRate_)\n {\n // 1. get CLRS Oracle rate from FallbackOracleImpl2 for divisor\n (exchangeRate_, ) = _getRateWithFallback2();\n if (exchangeRate_ == 0) {\n // revert if fetched exchange rate is 0\n revert FluidOracleError(ErrorTypes.Ratio2xFallbackCLRSOracleL2__ExchangeRateZero);\n }\n\n // 2. get rate from FallbackCLRSOracleL2. This already checks and reverts in case of price being 0.\n // (super.getExchangeRate returns FallbackCLRSOracleL2 rate, no other inherited contract has this.)\n // includes _ensureSequencerUpAndValid();\n exchangeRate_ = (super.getExchangeRateLiquidate() * (10 ** OracleUtils.RATE_OUTPUT_DECIMALS)) / exchangeRate_;\n }\n\n /// @inheritdoc FluidOracleL2\n function getExchangeRate()\n public\n view\n override(FluidOracleL2, FallbackCLRSOracleL2)\n returns (uint256 exchangeRate_)\n {\n return getExchangeRateOperate();\n }\n\n /// @notice which oracle to use as main source:\n /// - 1 = Chainlink ONLY (no fallback)\n /// - 2 = Chainlink with Redstone Fallback\n /// - 3 = Redstone with Chainlink Fallback\n function FALLBACK_ORACLE2_MAIN_SOURCE() public view returns (uint8) {\n return _FALLBACK_ORACLE2_MAIN_SOURCE;\n }\n}\n" } }, "settings": { "optimizer": { "enabled": true, "runs": 10000000 }, "evmVersion": "paris", "outputSelection": { "*": { "*": [ "abi", "evm.bytecode", "evm.deployedBytecode", "evm.methodIdentifiers", "metadata", "devdoc", "userdoc", "storageLayout", "evm.gasEstimates" ], "": [ "ast" ] } }, "metadata": { "useLiteralContent": true } } }