1
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
2
pragma solidity 0.8.21 ;
3
3
4
- import {IChainlinkOracle} from "./interfaces/IChainlinkOracle.sol " ;
5
4
import {IOracle} from "../../lib/morpho-blue/src/interfaces/IOracle.sol " ;
5
+ import {IMorphoChainlinkOracleV2} from "./interfaces/IMorphoChainlinkOracleV2.sol " ;
6
6
7
- import {AggregatorV3Interface, ChainlinkDataFeedLib} from "./libraries/ChainlinkDataFeedLib.sol " ;
8
- import {IERC4626 , VaultLib} from "./libraries/VaultLib.sol " ;
9
7
import {ErrorsLib} from "./libraries/ErrorsLib.sol " ;
8
+ import {IERC4626 , VaultLib} from "./libraries/VaultLib.sol " ;
10
9
import {Math} from "../../lib/openzeppelin-contracts/contracts/utils/math/Math.sol " ;
10
+ import {AggregatorV3Interface, ChainlinkDataFeedLib} from "./libraries/ChainlinkDataFeedLib.sol " ;
11
11
12
- /// @title ChainlinkOracle
12
+ /// @title MorphoChainlinkOracleV2
13
13
/// @author Morpho Labs
14
14
/// @custom:contact security@morpho.org
15
15
/// @notice Morpho Blue oracle using Chainlink-compliant feeds.
16
- contract ChainlinkOracle is IChainlinkOracle {
16
+ contract MorphoChainlinkOracleV2 is IMorphoChainlinkOracleV2 {
17
17
using Math for uint256 ;
18
18
using VaultLib for IERC4626 ;
19
19
using ChainlinkDataFeedLib for AggregatorV3Interface;
20
20
21
21
/* IMMUTABLES */
22
22
23
- /// @inheritdoc IChainlinkOracle
24
- IERC4626 public immutable VAULT ;
23
+ /// @inheritdoc IMorphoChainlinkOracleV2
24
+ IERC4626 public immutable BASE_VAULT ;
25
25
26
- /// @inheritdoc IChainlinkOracle
27
- uint256 public immutable VAULT_CONVERSION_SAMPLE ;
26
+ /// @inheritdoc IMorphoChainlinkOracleV2
27
+ uint256 public immutable BASE_VAULT_CONVERSION_SAMPLE ;
28
28
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
30
36
AggregatorV3Interface public immutable BASE_FEED_1;
31
37
32
- /// @inheritdoc IChainlinkOracle
38
+ /// @inheritdoc IMorphoChainlinkOracleV2
33
39
AggregatorV3Interface public immutable BASE_FEED_2;
34
40
35
- /// @inheritdoc IChainlinkOracle
41
+ /// @inheritdoc IMorphoChainlinkOracleV2
36
42
AggregatorV3Interface public immutable QUOTE_FEED_1;
37
43
38
- /// @inheritdoc IChainlinkOracle
44
+ /// @inheritdoc IMorphoChainlinkOracleV2
39
45
AggregatorV3Interface public immutable QUOTE_FEED_2;
40
46
41
- /// @inheritdoc IChainlinkOracle
47
+ /// @inheritdoc IMorphoChainlinkOracleV2
42
48
uint256 public immutable SCALE_FACTOR;
43
49
44
50
/* CONSTRUCTOR */
45
51
46
52
/// @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.
50
55
/// - 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.
55
62
/// @param baseFeed1 First base feed. Pass address zero if the price = 1.
56
63
/// @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.
57
69
/// @param quoteFeed1 First quote feed. Pass address zero if the price = 1.
58
70
/// @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.
63
71
/// @param quoteTokenDecimals Quote token decimals.
72
+ /// @dev The base asset should be the collateral token and the quote asset the loan token.
64
73
constructor (
65
- IERC4626 vault ,
74
+ IERC4626 baseVault ,
75
+ uint256 baseVaultConversionSample ,
66
76
AggregatorV3Interface baseFeed1 ,
67
77
AggregatorV3Interface baseFeed2 ,
78
+ uint256 baseTokenDecimals ,
79
+ IERC4626 quoteVault ,
80
+ uint256 quoteVaultConversionSample ,
68
81
AggregatorV3Interface quoteFeed1 ,
69
82
AggregatorV3Interface quoteFeed2 ,
70
- uint256 vaultConversionSample ,
71
- uint256 baseTokenDecimals ,
72
83
uint256 quoteTokenDecimals
73
84
) {
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
+ );
77
93
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
79
96
);
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);
81
99
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;
84
104
BASE_FEED_1 = baseFeed1;
85
105
BASE_FEED_2 = baseFeed2;
86
106
QUOTE_FEED_1 = quoteFeed1;
@@ -103,7 +123,7 @@ contract ChainlinkOracle is IChainlinkOracle {
103
123
// = 1e36 * (pB1 * 1e(-dB1) * pB2) / (pQ1 * 1e(-dQ1) * pQ2)
104
124
105
125
// 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).
107
127
108
128
// Based on the implementation of `price()` below, the value of `SCALE_FACTOR` should thus satisfy:
109
129
// (pB1 * 1e(fpB1)) * (pB2 * 1e(fpB2)) * SCALE_FACTOR / ((pQ1 * 1e(fpQ1)) * (pQ2 * 1e(fpQ2)))
@@ -115,16 +135,16 @@ contract ChainlinkOracle is IChainlinkOracle {
115
135
** (
116
136
36 + quoteTokenDecimals + quoteFeed1.getDecimals () + quoteFeed2.getDecimals () - baseTokenDecimals
117
137
- baseFeed1.getDecimals () - baseFeed2.getDecimals ()
118
- ) / vaultConversionSample ;
138
+ ) * quoteVaultConversionSample / baseVaultConversionSample ;
119
139
}
120
140
121
141
/* PRICE */
122
142
123
143
/// @inheritdoc IOracle
124
144
function price () external view returns (uint256 ) {
125
145
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 ()
128
148
);
129
149
}
130
150
}
0 commit comments