From 279e0345ab8572e7a88dc718ec492794d310bd1f Mon Sep 17 00:00:00 2001 From: Mikhailo Shabodyash Date: Thu, 26 Sep 2024 13:28:01 +0300 Subject: [PATCH 1/3] feat: contracts --- .../pricefeeds/METHExchangeRatePriceFeed.sol | 99 +++++++++++++++++++ contracts/vendor/mantle/IRateProvider.sol | 6 ++ 2 files changed, 105 insertions(+) create mode 100644 contracts/pricefeeds/METHExchangeRatePriceFeed.sol create mode 100644 contracts/vendor/mantle/IRateProvider.sol diff --git a/contracts/pricefeeds/METHExchangeRatePriceFeed.sol b/contracts/pricefeeds/METHExchangeRatePriceFeed.sol new file mode 100644 index 000000000..f8868ca9c --- /dev/null +++ b/contracts/pricefeeds/METHExchangeRatePriceFeed.sol @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.15; + +import "../vendor/mantle/IRateProvider.sol"; +import "../IPriceFeed.sol"; + +/** + * @title mETH Scaling price feed + * @notice A custom price feed that scales up or down the price received from an underlying Renzo mETH / ETH exchange rate price feed and returns the result + * @author Compound + */ +contract METHExchangeRatePriceFeed is IPriceFeed { + /** Custom errors **/ + error InvalidInt256(); + error BadDecimals(); + + /// @notice Version of the price feed + uint public constant VERSION = 1; + + /// @notice Description of the price feed + string public description; + + /// @notice Number of decimals for returned prices + uint8 public immutable override decimals; + + /// @notice mETH price feed where prices are fetched from + address public immutable underlyingPriceFeed; + + /// @notice Whether or not the price should be upscaled + bool internal immutable shouldUpscale; + + /// @notice The amount to upscale or downscale the price by + int256 internal immutable rescaleFactor; + + /** + * @notice Construct a new mETH scaling price feed + * @param mETHRateProvider The address of the underlying price feed to fetch prices from + * @param decimals_ The number of decimals for the returned prices + **/ + constructor(address mETHRateProvider, uint8 decimals_, string memory description_) { + underlyingPriceFeed = mETHRateProvider; + if (decimals_ > 18) revert BadDecimals(); + decimals = decimals_; + description = description_; + + uint8 mETHRateProviderDecimals = 18; + // Note: Solidity does not allow setting immutables in if/else statements + shouldUpscale = mETHRateProviderDecimals < decimals_ ? true : false; + rescaleFactor = (shouldUpscale + ? signed256(10 ** (decimals_ - mETHRateProviderDecimals)) + : signed256(10 ** (mETHRateProviderDecimals - decimals_)) + ); + } + + /** + * @notice Price for the latest round + * @return roundId Round id from the underlying price feed + * @return answer Latest price for the asset in terms of ETH + * @return startedAt Timestamp when the round was started; passed on from underlying price feed + * @return updatedAt Timestamp when the round was last updated; passed on from underlying price feed + * @return answeredInRound Round id in which the answer was computed; passed on from underlying price feed + **/ + function latestRoundData() override external view returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ) { + // https://etherscan.io/address/0xe3cBd06D7dadB3F4e6557bAb7EdD924CD1489E8f#readProxyContract#F19 + // rate = 1 mETH in ETH + uint256 rate = IRateProvider(underlyingPriceFeed).mETHToETH(1e18); + // protocol uses only the answer value. Other data fields are not provided by the underlying pricefeed and are not used in Comet protocol + return (1, scalePrice(signed256(rate)), block.timestamp, block.timestamp, 1); + } + + function signed256(uint256 n) internal pure returns (int256) { + if (n > uint256(type(int256).max)) revert InvalidInt256(); + return int256(n); + } + + function scalePrice(int256 price) internal view returns (int256) { + int256 scaledPrice; + if (shouldUpscale) { + scaledPrice = price * rescaleFactor; + } else { + scaledPrice = price / rescaleFactor; + } + return scaledPrice; + } + + /** + * @notice Price for the latest round + * @return The version of the price feed contract + **/ + function version() external pure returns (uint256) { + return VERSION; + } +} diff --git a/contracts/vendor/mantle/IRateProvider.sol b/contracts/vendor/mantle/IRateProvider.sol new file mode 100644 index 000000000..d6e994837 --- /dev/null +++ b/contracts/vendor/mantle/IRateProvider.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity 0.8.15; + +interface IRateProvider { + function mETHToETH(uint256) external view returns (uint256); +} From 3cff00db694b92dfe34cf68a35baa854288cbde3 Mon Sep 17 00:00:00 2001 From: Mikhailo Shabodyash Date: Tue, 12 Nov 2024 17:17:14 +0200 Subject: [PATCH 2/3] fix: audit --- contracts/pricefeeds/METHExchangeRatePriceFeed.sol | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/contracts/pricefeeds/METHExchangeRatePriceFeed.sol b/contracts/pricefeeds/METHExchangeRatePriceFeed.sol index f8868ca9c..61b264aff 100644 --- a/contracts/pricefeeds/METHExchangeRatePriceFeed.sol +++ b/contracts/pricefeeds/METHExchangeRatePriceFeed.sol @@ -15,7 +15,7 @@ contract METHExchangeRatePriceFeed is IPriceFeed { error BadDecimals(); /// @notice Version of the price feed - uint public constant VERSION = 1; + uint internal constant VERSION = 1; /// @notice Description of the price feed string public description; @@ -67,7 +67,6 @@ contract METHExchangeRatePriceFeed is IPriceFeed { uint256 updatedAt, uint80 answeredInRound ) { - // https://etherscan.io/address/0xe3cBd06D7dadB3F4e6557bAb7EdD924CD1489E8f#readProxyContract#F19 // rate = 1 mETH in ETH uint256 rate = IRateProvider(underlyingPriceFeed).mETHToETH(1e18); // protocol uses only the answer value. Other data fields are not provided by the underlying pricefeed and are not used in Comet protocol @@ -90,7 +89,7 @@ contract METHExchangeRatePriceFeed is IPriceFeed { } /** - * @notice Price for the latest round + * @notice Get the version of the price feed contract * @return The version of the price feed contract **/ function version() external pure returns (uint256) { From 05bc49c0d8a8aa687536752016835683a777e3d0 Mon Sep 17 00:00:00 2001 From: Mikhailo Shabodyash Date: Wed, 13 Nov 2024 10:51:02 +0200 Subject: [PATCH 3/3] fix: comment fix --- contracts/pricefeeds/METHExchangeRatePriceFeed.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/pricefeeds/METHExchangeRatePriceFeed.sol b/contracts/pricefeeds/METHExchangeRatePriceFeed.sol index 61b264aff..b97f475d6 100644 --- a/contracts/pricefeeds/METHExchangeRatePriceFeed.sol +++ b/contracts/pricefeeds/METHExchangeRatePriceFeed.sol @@ -6,7 +6,7 @@ import "../IPriceFeed.sol"; /** * @title mETH Scaling price feed - * @notice A custom price feed that scales up or down the price received from an underlying Renzo mETH / ETH exchange rate price feed and returns the result + * @notice A custom price feed that scales up or down the price received from an underlying Mantle mETH / ETH exchange rate price feed and returns the result * @author Compound */ contract METHExchangeRatePriceFeed is IPriceFeed {