@@ -12,7 +12,23 @@ import "@pythnetwork/pyth-sdk-solidity/IPyth.sol";
1212
1313/// @title MorphoPythOracle
1414/// @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.
1632contract MorphoPythOracle is IMorphoPythOracle {
1733 using Math for uint256 ;
1834
@@ -50,8 +66,40 @@ contract MorphoPythOracle is IMorphoPythOracle {
5066 uint256 public immutable SCALE_FACTOR;
5167
5268 /// @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.
5374 uint256 public PRICE_FEED_MAX_AGE;
5475
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
55103 constructor (
56104 address pyth_ ,
57105 IERC4626 baseVault ,
@@ -105,6 +153,21 @@ contract MorphoPythOracle is IMorphoPythOracle {
105153 /* PRICE */
106154
107155 /// @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
108171 function price () external view returns (uint256 ) {
109172 return SCALE_FACTOR.mulDiv (
110173 BASE_VAULT.getAssets (BASE_VAULT_CONVERSION_SAMPLE)
0 commit comments