11// SPDX-License-Identifier: GPL-2.0-or-later
22pragma solidity 0.8.21 ;
33
4- import {IChainlinkOracle} from "./interfaces/IChainlinkOracle.sol " ;
54import {IOracle} from "../../lib/morpho-blue/src/interfaces/IOracle.sol " ;
5+ import {IMorphoChainlinkOracleV2} from "./interfaces/IMorphoChainlinkOracleV2.sol " ;
66
7- import {AggregatorV3Interface, ChainlinkDataFeedLib} from "./libraries/ChainlinkDataFeedLib.sol " ;
8- import {IERC4626 , VaultLib} from "./libraries/VaultLib.sol " ;
97import {ErrorsLib} from "./libraries/ErrorsLib.sol " ;
8+ import {IERC4626 , VaultLib} from "./libraries/VaultLib.sol " ;
109import {Math} from "../../lib/openzeppelin-contracts/contracts/utils/math/Math.sol " ;
10+ import {AggregatorV3Interface, ChainlinkDataFeedLib} from "./libraries/ChainlinkDataFeedLib.sol " ;
1111
12- /// @title ChainlinkOracle
12+ /// @title MorphoChainlinkOracleV2
1313/// @author Morpho Labs
1414/// @custom:contact security@morpho.org
1515/// @notice Morpho Blue oracle using Chainlink-compliant feeds.
16- contract ChainlinkOracle is IChainlinkOracle {
16+ contract MorphoChainlinkOracleV2 is IMorphoChainlinkOracleV2 {
1717 using Math for uint256 ;
1818 using VaultLib for IERC4626 ;
1919 using ChainlinkDataFeedLib for AggregatorV3Interface;
2020
2121 /* IMMUTABLES */
2222
23- /// @inheritdoc IChainlinkOracle
24- IERC4626 public immutable VAULT ;
23+ /// @inheritdoc IMorphoChainlinkOracleV2
24+ IERC4626 public immutable BASE_VAULT ;
2525
26- /// @inheritdoc IChainlinkOracle
27- uint256 public immutable VAULT_CONVERSION_SAMPLE ;
26+ /// @inheritdoc IMorphoChainlinkOracleV2
27+ uint256 public immutable BASE_VAULT_CONVERSION_SAMPLE ;
2828
29- /// @inheritdoc IChainlinkOracle
29+ /// @inheritdoc IMorphoChainlinkOracleV2
30+ IERC4626 public immutable QUOTE_VAULT;
31+
32+ /// @inheritdoc IMorphoChainlinkOracleV2
33+ uint256 public immutable QUOTE_VAULT_CONVERSION_SAMPLE;
34+
35+ /// @inheritdoc IMorphoChainlinkOracleV2
3036 AggregatorV3Interface public immutable BASE_FEED_1;
3137
32- /// @inheritdoc IChainlinkOracle
38+ /// @inheritdoc IMorphoChainlinkOracleV2
3339 AggregatorV3Interface public immutable BASE_FEED_2;
3440
35- /// @inheritdoc IChainlinkOracle
41+ /// @inheritdoc IMorphoChainlinkOracleV2
3642 AggregatorV3Interface public immutable QUOTE_FEED_1;
3743
38- /// @inheritdoc IChainlinkOracle
44+ /// @inheritdoc IMorphoChainlinkOracleV2
3945 AggregatorV3Interface public immutable QUOTE_FEED_2;
4046
41- /// @inheritdoc IChainlinkOracle
47+ /// @inheritdoc IMorphoChainlinkOracleV2
4248 uint256 public immutable SCALE_FACTOR;
4349
4450 /* CONSTRUCTOR */
4551
4652 /// @dev Here is the list of assumptions that guarantees the oracle behaves as expected:
47- /// - Feeds are either Chainlink-compliant or the address zero.
48- /// - Feeds have the same behavioral assumptions as Chainlink's.
49- /// - Feeds are set in the correct order.
53+ /// - The vaults, if set, are ERC4626-compliant.
54+ /// - The feeds, if set, are Chainlink-interface-compliant.
5055 /// - Decimals passed as argument are correct.
51- /// - The vault's sample shares quoted as assets and the base feed prices don't overflow when multiplied.
52- /// - The quote feed prices don't overflow when multiplied.
53- /// - The vault, if set, is ERC4626-compliant.
54- /// @param vault Vault. Pass address zero to omit this parameter.
56+ /// - The base vaults's sample shares quoted as assets and the base feed prices don't overflow when multiplied.
57+ /// - The quote vault's sample shares quoted as assets and the quote feed prices don't overflow when multiplied.
58+ /// @param baseVault Base vault. Pass address zero to omit this parameter.
59+ /// @param baseVaultConversionSample The sample amount of base vault shares used to convert to underlying.
60+ /// Pass 1 if the base asset is not a vault. Should be chosen such that converting `baseVaultConversionSample` to
61+ /// assets has enough precision.
5562 /// @param baseFeed1 First base feed. Pass address zero if the price = 1.
5663 /// @param baseFeed2 Second base feed. Pass address zero if the price = 1.
64+ /// @param baseTokenDecimals Base token decimals.
65+ /// @param quoteVault Quote vault. Pass address zero to omit this parameter.
66+ /// @param quoteVaultConversionSample The sample amount of quote vault shares used to convert to underlying.
67+ /// Pass 1 if the quote asset is not a vault. Should be chosen such that converting `quoteVaultConversionSample` to
68+ /// assets has enough precision.
5769 /// @param quoteFeed1 First quote feed. Pass address zero if the price = 1.
5870 /// @param quoteFeed2 Second quote feed. Pass address zero if the price = 1.
59- /// @param vaultConversionSample The sample amount of vault shares used to convert to the underlying asset.
60- /// Pass 1 if the oracle does not use a vault. Should be chosen such that converting `vaultConversionSample` to
61- /// assets has enough precision.
62- /// @param baseTokenDecimals Base token decimals.
6371 /// @param quoteTokenDecimals Quote token decimals.
72+ /// @dev The base asset should be the collateral token and the quote asset the loan token.
6473 constructor (
65- IERC4626 vault ,
74+ IERC4626 baseVault ,
75+ uint256 baseVaultConversionSample ,
6676 AggregatorV3Interface baseFeed1 ,
6777 AggregatorV3Interface baseFeed2 ,
78+ uint256 baseTokenDecimals ,
79+ IERC4626 quoteVault ,
80+ uint256 quoteVaultConversionSample ,
6881 AggregatorV3Interface quoteFeed1 ,
6982 AggregatorV3Interface quoteFeed2 ,
70- uint256 vaultConversionSample ,
71- uint256 baseTokenDecimals ,
7283 uint256 quoteTokenDecimals
7384 ) {
74- // The ERC4626 vault parameter is used to price `VAULT_CONVERSION_SAMPLE` of its shares, so it requires dividing
75- // by that number, hence the division by `VAULT_CONVERSION_SAMPLE` in the `SCALE_FACTOR` definition.
76- // Verify that vault = address(0) => vaultConversionSample = 1.
85+ // The ERC4626 vault parameters are used to price their respective conversion samples of their respective
86+ // shares, so it requires multiplying by `QUOTE_VAULT_CONVERSION_SAMPLE` and dividing
87+ // by `BASE_VAULT_CONVERSION_SAMPLE` in the `SCALE_FACTOR` definition.
88+ // Verify that vault = address(0) => vaultConversionSample = 1 for each vault.
89+ require (
90+ address (baseVault) != address (0 ) || baseVaultConversionSample == 1 ,
91+ ErrorsLib.VAULT_CONVERSION_SAMPLE_IS_NOT_ONE
92+ );
7793 require (
78- address (vault) != address (0 ) || vaultConversionSample == 1 , ErrorsLib.VAULT_CONVERSION_SAMPLE_IS_NOT_ONE
94+ address (quoteVault) != address (0 ) || quoteVaultConversionSample == 1 ,
95+ ErrorsLib.VAULT_CONVERSION_SAMPLE_IS_NOT_ONE
7996 );
80- require (vaultConversionSample != 0 , ErrorsLib.VAULT_CONVERSION_SAMPLE_IS_ZERO);
97+ require (baseVaultConversionSample != 0 , ErrorsLib.VAULT_CONVERSION_SAMPLE_IS_ZERO);
98+ require (quoteVaultConversionSample != 0 , ErrorsLib.VAULT_CONVERSION_SAMPLE_IS_ZERO);
8199
82- VAULT = vault;
83- VAULT_CONVERSION_SAMPLE = vaultConversionSample;
100+ BASE_VAULT = baseVault;
101+ BASE_VAULT_CONVERSION_SAMPLE = baseVaultConversionSample;
102+ QUOTE_VAULT = quoteVault;
103+ QUOTE_VAULT_CONVERSION_SAMPLE = quoteVaultConversionSample;
84104 BASE_FEED_1 = baseFeed1;
85105 BASE_FEED_2 = baseFeed2;
86106 QUOTE_FEED_1 = quoteFeed1;
@@ -103,7 +123,7 @@ contract ChainlinkOracle is IChainlinkOracle {
103123 // = 1e36 * (pB1 * 1e(-dB1) * pB2) / (pQ1 * 1e(-dQ1) * pQ2)
104124
105125 // Let fpB1, fpB2, fpQ1, fpQ2 be the feed precision of the respective prices pB1, pB2, pQ1, pQ2.
106- // Chainlink feeds return pB1 * 1e(fpB1), pB2 * 1e(fpB2), pQ1 * 1e(fpQ1) and pQ2 * 1e(fpQ2).
126+ // Feeds return pB1 * 1e(fpB1), pB2 * 1e(fpB2), pQ1 * 1e(fpQ1) and pQ2 * 1e(fpQ2).
107127
108128 // Based on the implementation of `price()` below, the value of `SCALE_FACTOR` should thus satisfy:
109129 // (pB1 * 1e(fpB1)) * (pB2 * 1e(fpB2)) * SCALE_FACTOR / ((pQ1 * 1e(fpQ1)) * (pQ2 * 1e(fpQ2)))
@@ -115,16 +135,16 @@ contract ChainlinkOracle is IChainlinkOracle {
115135 ** (
116136 36 + quoteTokenDecimals + quoteFeed1.getDecimals () + quoteFeed2.getDecimals () - baseTokenDecimals
117137 - baseFeed1.getDecimals () - baseFeed2.getDecimals ()
118- ) / vaultConversionSample ;
138+ ) * quoteVaultConversionSample / baseVaultConversionSample ;
119139 }
120140
121141 /* PRICE */
122142
123143 /// @inheritdoc IOracle
124144 function price () external view returns (uint256 ) {
125145 return SCALE_FACTOR.mulDiv (
126- VAULT .getAssets (VAULT_CONVERSION_SAMPLE ) * BASE_FEED_1.getPrice () * BASE_FEED_2.getPrice (),
127- QUOTE_FEED_1.getPrice () * QUOTE_FEED_2.getPrice ()
146+ BASE_VAULT .getAssets (BASE_VAULT_CONVERSION_SAMPLE ) * BASE_FEED_1.getPrice () * BASE_FEED_2.getPrice (),
147+ QUOTE_VAULT. getAssets (QUOTE_VAULT_CONVERSION_SAMPLE) * QUOTE_FEED_1.getPrice () * QUOTE_FEED_2.getPrice ()
128148 );
129149 }
130150}
0 commit comments