@@ -12,7 +12,23 @@ import "@pythnetwork/pyth-sdk-solidity/IPyth.sol";
12
12
13
13
/// @title MorphoPythOracle
14
14
/// @author Pyth Data Association
15
- /// @notice Morpho Blue oracle using Pyth Price Feeds.
15
+ /// @notice Morpho oracle implementation that combines Pyth price feeds with ERC-4626 vault pricing
16
+ /// @dev This oracle calculates prices by combining multiple data sources:
17
+ /// - Up to 2 Pyth price feeds each for base and quote assets
18
+ /// - Optional ERC-4626 vault share-to-asset conversion for base and quote
19
+ /// - Configurable staleness checks for price feed age validation
20
+ ///
21
+ /// Price Calculation Formula:
22
+ /// price = SCALE_FACTOR * (baseVaultAssets * baseFeed1 * baseFeed2) / (quoteVaultAssets * quoteFeed1 * quoteFeed2)
23
+ ///
24
+ /// Security Considerations:
25
+ /// - Single priceFeedMaxAge used for all feeds may not suit different asset volatilities
26
+ /// - ERC-4626 vaults can be manipulated through donations, flash loans, or fee changes
27
+ /// - Pyth confidence intervals are not validated, potentially accepting uncertain prices
28
+ /// - Conversion samples must be large enough to avoid rounding to zero
29
+ ///
30
+ /// @dev This contract follows Morpho's design philosophy prioritizing flexibility over safety.
31
+ /// Users must validate all configuration parameters and monitor oracle behavior.
16
32
contract MorphoPythOracle is IMorphoPythOracle {
17
33
using Math for uint256 ;
18
34
@@ -50,8 +66,40 @@ contract MorphoPythOracle is IMorphoPythOracle {
50
66
uint256 public immutable SCALE_FACTOR;
51
67
52
68
/// @inheritdoc IMorphoPythOracle
69
+ /// @dev WARNING: Single staleness threshold applied to all feeds regardless of asset characteristics.
70
+ /// Fast-moving assets may need shorter max age (e.g., 15s) while stable assets could tolerate longer (e.g.,
71
+ /// 60s).
72
+ /// Using a universal value may reject valid stable prices or accept stale volatile prices.
73
+ /// Consider asset-specific staleness checks for improved accuracy and reliability.
53
74
uint256 public PRICE_FEED_MAX_AGE;
54
75
76
+ /// @notice Initializes a new MorphoPythOracle instance
77
+ /// @dev Constructor performs parameter validation but cannot prevent all misconfigurations.
78
+ /// Users must ensure parameters are appropriate for their use case.
79
+ ///
80
+ /// @param pyth_ Address of the Pyth contract - must be the official Pyth contract for the chain
81
+ /// @param baseVault ERC-4626 vault for base asset, or address(0) to skip vault conversion
82
+ /// @param baseVaultConversionSample Sample shares amount for base vault conversion (must provide adequate
83
+ /// precision)
84
+ /// @param baseFeed1 First Pyth price feed ID for base asset, or bytes32(0) for price=1
85
+ /// @param baseFeed2 Second Pyth price feed ID for base asset, or bytes32(0) for price=1
86
+ /// @param baseTokenDecimals Decimal places for base token
87
+ /// @param quoteVault ERC-4626 vault for quote asset, or address(0) to skip vault conversion
88
+ /// @param quoteVaultConversionSample Sample shares amount for quote vault conversion (must provide adequate
89
+ /// precision)
90
+ /// @param quoteFeed1 First Pyth price feed ID for quote asset, or bytes32(0) for price=1
91
+ /// @param quoteFeed2 Second Pyth price feed ID for quote asset, or bytes32(0) for price=1
92
+ /// @param quoteTokenDecimals Decimal places for quote token
93
+ /// @param priceFeedMaxAge Maximum acceptable age in seconds for price feeds (applies to all feeds)
94
+ ///
95
+ /// @dev CRITICAL: Conversion samples must be large enough that convertToAssets() returns non-zero values.
96
+ /// Small samples may round to zero, breaking price calculations. Test with actual vault implementations!
97
+ ///
98
+ /// @dev VAULT SECURITY: If using vaults, ensure they are trusted implementations resistant to:
99
+ /// - Share price manipulation via direct token transfers
100
+ /// - Flash loan attacks that temporarily affect asset/share ratios
101
+ /// - Dynamic fee changes that alter convertToAssets() results
102
+ /// - First depositor attacks setting malicious initial exchange rates
55
103
constructor (
56
104
address pyth_ ,
57
105
IERC4626 baseVault ,
@@ -105,6 +153,21 @@ contract MorphoPythOracle is IMorphoPythOracle {
105
153
/* PRICE */
106
154
107
155
/// @inheritdoc IOracle
156
+ /// @notice Calculates the current price by combining vault asset values and Pyth feed prices
157
+ /// @return The calculated price with 18 decimal precision
158
+ /// @dev Price calculation: SCALE_FACTOR * (baseAssets * baseFeeds) / (quoteAssets * quoteFeeds)
159
+ ///
160
+ /// SECURITY WARNINGS:
161
+ /// - Vault prices can be manipulated if vaults are not manipulation-resistant
162
+ /// - Single PRICE_FEED_MAX_AGE applied to all feeds regardless of asset volatility
163
+ /// - Pyth confidence intervals are ignored - uncertain prices may be accepted
164
+ /// - No per-block deviation caps - prices can change drastically within one block
165
+ ///
166
+ /// @dev This function will revert if:
167
+ /// - Any Pyth feed returns a negative price
168
+ /// - Any feed is older than PRICE_FEED_MAX_AGE
169
+ /// - Vault convertToAssets calls fail
170
+ /// - Arithmetic overflow in multiplication/division
108
171
function price () external view returns (uint256 ) {
109
172
return SCALE_FACTOR.mulDiv (
110
173
BASE_VAULT.getAssets (BASE_VAULT_CONVERSION_SAMPLE)
0 commit comments