Diff
checker
テキスト
テキスト
画像
ドキュメント
Excel
フォルダ
Legal
Enterprise
デスクトップ
料金
ログイン
Diffchecker デスクトップのダウンロード
テキスト比較
2 つのテキスト ファイルの違いを見つける
ツール
履歴
表示
レイアウト
分割
統合
ライブエディター
空白の変更を非表示
未変更行を折りたたむ
折り返しなし
比較精度
スマート
構文
なし
外観を変更
処理
無視
テキスト変換
最初の差分へ移動
入力を編集
Diffchecker Desktop
Diffcheckerを実行する最も安全な方法。Diffchecker Desktopアプリを入手:あなたの差分はコンピューターから出ることはありません!
Desktopを入手
LRTOracle
作成日
10 か月前
差分は期限切れになりません
クリア
エクスポート
共有
説明
15 削除
行
合計
削除
文字
合計
削除
この機能を引き続き使用するには、アップグレードしてください
Diff
checker
Pro
価格を見る
316 行
すべてコピー
50 追加
行
合計
追加
文字
合計
追加
この機能を引き続き使用するには、アップグレードしてください
Diff
checker
Pro
価格を見る
351 行
すべてコピー
// SPDX-License-Identifier: BUSL-1.1
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.27;
pragma solidity 0.8.27;
import { UtilLib } from "./utils/UtilLib.sol";
import { UtilLib } from "./utils/UtilLib.sol";
import { LRTConstants } from "./utils/LRTConstants.sol";
import { LRTConstants } from "./utils/LRTConstants.sol";
import { LRTConfigRoleChecker, ILRTConfig } from "./utils/LRTConfigRoleChecker.sol";
import { LRTConfigRoleChecker, ILRTConfig } from "./utils/LRTConfigRoleChecker.sol";
import { WadMath } from "./utils/WadMath.sol";
import { WadMath } from "./utils/WadMath.sol";
import { IRSETH } from "./interfaces/IRSETH.sol";
import { IRSETH } from "./interfaces/IRSETH.sol";
import { IPriceFetcher } from "./interfaces/IPriceFetcher.sol";
import { IPriceFetcher } from "./interfaces/IPriceFetcher.sol";
import { ILRTOracle } from "./interfaces/ILRTOracle.sol";
import { ILRTOracle } from "./interfaces/ILRTOracle.sol";
import { ILRTDepositPool } from "./interfaces/ILRTDepositPool.sol";
import { ILRTDepositPool } from "./interfaces/ILRTDepositPool.sol";
import { IAccessControl } from "@openzeppelin/contracts/access/IAccessControl.sol";
import { IAccessControl } from "@openzeppelin/contracts/access/IAccessControl.sol";
import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
interface IPausable {
interface IPausable {
function pause() external;
function pause() external;
function paused() external view returns (bool);
function paused() external view returns (bool);
}
}
/// @title LRTOracle Contract
/// @title LRTOracle Contract
/// @notice oracle contract that calculates the exchange rate of assets
/// @notice oracle contract that calculates the exchange rate of assets
contract LRTOracle is ILRTOracle, LRTConfigRoleChecker, Initializable {
contract LRTOracle is ILRTOracle, LRTConfigRoleChecker, Initializable {
using WadMath for uint256;
using WadMath for uint256;
mapping(address asset => address priceOracle) public override assetPriceOracle;
mapping(address asset => address priceOracle) public override assetPriceOracle;
uint256 public override rsETHPrice;
uint256 public override rsETHPrice;
uint256 public pricePercentageLimit;
uint256 public pricePercentageLimit;
uint256 public highestRsethPrice;
uint256 public highestRsethPrice;
// Daily fee minting limit variables
// Daily fee minting limit variables
uint256 public currentPeriodMintedFeeAmount;
uint256 public currentPeriodMintedFeeAmount;
uint256 public feePeriodStartTime;
uint256 public feePeriodStartTime;
uint256 public maxFeeMintAmountPerDay;
uint256 public maxFeeMintAmountPerDay;
/// @notice New variable added for pausable functionality
/// @notice New variable added for pausable functionality
bool public paused;
bool public paused;
modifier onlySupportedOracle(address asset) {
modifier onlySupportedOracle(address asset) {
if (assetPriceOracle[asset] == address(0)) {
if (assetPriceOracle[asset] == address(0)) {
revert AssetOracleNotSupported();
revert AssetOracleNotSupported();
}
}
_;
_;
}
}
modifier whenNotPaused() {
modifier whenNotPaused() {
if (paused) revert ContractPaused();
if (paused) revert ContractPaused();
_;
_;
}
}
modifier whenPaused() {
modifier whenPaused() {
if (!paused) revert ContractNotPaused();
if (!paused) revert ContractNotPaused();
_;
_;
}
}
/// @custom:oz-upgrades-unsafe-allow constructor
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
constructor() {
_disableInitializers();
_disableInitializers();
}
}
/// @dev Initializes the contract
/// @dev Initializes the contract
/// @param lrtConfigAddr LRT config address
/// @param lrtConfigAddr LRT config address
function initialize(address lrtConfigAddr) external initializer {
function initialize(address lrtConfigAddr) external initializer {
UtilLib.checkNonZeroAddress(lrtConfigAddr);
UtilLib.checkNonZeroAddress(lrtConfigAddr);
lrtConfig = ILRTConfig(lrtConfigAddr);
lrtConfig = ILRTConfig(lrtConfigAddr);
emit UpdatedLRTConfig(lrtConfigAddr);
emit UpdatedLRTConfig(lrtConfigAddr);
}
}
コピー
コピー済み
コピー
コピー済み
/// @notice Initializes the contract with a fee period start time
/// @param _feePeriodStartTime The start time of the fee period
function reinitialize(uint256 _feePeriodStartTime) external reinitializer(2) onlyLRTManager {
if (_feePeriodStartTime > block.timestamp || _feePeriodStartTime <= block.timestamp - 1 days) {
revert PeriodStartTimeShouldBeWithin24Hours();
}
feePeriodStartTime = _feePeriodStartTime;
emit FeePeriodStartTimeSet(_feePeriodStartTime);
}
/*//////////////////////////////////////////////////////////////
/*//////////////////////////////////////////////////////////////
write functions
write functions
//////////////////////////////////////////////////////////////*/
//////////////////////////////////////////////////////////////*/
/// @notice updates RSETH/ETH exchange rate
/// @notice updates RSETH/ETH exchange rate
/// @dev calculates rsETH price based on stakedAsset value received from EigenLayer
/// @dev calculates rsETH price based on stakedAsset value received from EigenLayer
function updateRSETHPrice() public whenNotPaused {
function updateRSETHPrice() public whenNotPaused {
_updateRsETHPrice();
_updateRsETHPrice();
}
}
/// @dev update rsETH price as an manager account
/// @dev update rsETH price as an manager account
/// @dev main benefit is to be able to update the price in case of the price going above the threshold
/// @dev main benefit is to be able to update the price in case of the price going above the threshold
/// @dev only LRT manager is allowed to call this function
/// @dev only LRT manager is allowed to call this function
function updateRSETHPriceAsManager() external onlyLRTManager {
function updateRSETHPriceAsManager() external onlyLRTManager {
_updateRsETHPrice();
_updateRsETHPrice();
}
}
/// @dev add/update the price oracle of any asset
/// @dev add/update the price oracle of any asset
/// @dev only onlyLRTAdmin is allowed
/// @dev only onlyLRTAdmin is allowed
/// @param asset asset address for which oracle price needs to be added/updated
/// @param asset asset address for which oracle price needs to be added/updated
function updatePriceOracleForValidated(address asset, address priceOracle) external onlyLRTAdmin {
function updatePriceOracleForValidated(address asset, address priceOracle) external onlyLRTAdmin {
// Sanity check: oracle price must have precision between 1e16 and 1e19
// Sanity check: oracle price must have precision between 1e16 and 1e19
uint256 price = IPriceFetcher(priceOracle).getAssetPrice(asset);
uint256 price = IPriceFetcher(priceOracle).getAssetPrice(asset);
if (price > 1e19 || price < 1e16) {
if (price > 1e19 || price < 1e16) {
revert InvalidPriceOracle();
revert InvalidPriceOracle();
}
}
updatePriceOracleFor(asset, priceOracle);
updatePriceOracleFor(asset, priceOracle);
}
}
/// @dev add/update the price oracle of any asset
/// @dev add/update the price oracle of any asset
保存された差分
原文
ファイルを開く
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.27; import { UtilLib } from "./utils/UtilLib.sol"; import { LRTConstants } from "./utils/LRTConstants.sol"; import { LRTConfigRoleChecker, ILRTConfig } from "./utils/LRTConfigRoleChecker.sol"; import { WadMath } from "./utils/WadMath.sol"; import { IRSETH } from "./interfaces/IRSETH.sol"; import { IPriceFetcher } from "./interfaces/IPriceFetcher.sol"; import { ILRTOracle } from "./interfaces/ILRTOracle.sol"; import { ILRTDepositPool } from "./interfaces/ILRTDepositPool.sol"; import { IAccessControl } from "@openzeppelin/contracts/access/IAccessControl.sol"; import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; interface IPausable { function pause() external; function paused() external view returns (bool); } /// @title LRTOracle Contract /// @notice oracle contract that calculates the exchange rate of assets contract LRTOracle is ILRTOracle, LRTConfigRoleChecker, Initializable { using WadMath for uint256; mapping(address asset => address priceOracle) public override assetPriceOracle; uint256 public override rsETHPrice; uint256 public pricePercentageLimit; uint256 public highestRsethPrice; // Daily fee minting limit variables uint256 public currentPeriodMintedFeeAmount; uint256 public feePeriodStartTime; uint256 public maxFeeMintAmountPerDay; /// @notice New variable added for pausable functionality bool public paused; modifier onlySupportedOracle(address asset) { if (assetPriceOracle[asset] == address(0)) { revert AssetOracleNotSupported(); } _; } modifier whenNotPaused() { if (paused) revert ContractPaused(); _; } modifier whenPaused() { if (!paused) revert ContractNotPaused(); _; } /// @custom:oz-upgrades-unsafe-allow constructor constructor() { _disableInitializers(); } /// @dev Initializes the contract /// @param lrtConfigAddr LRT config address function initialize(address lrtConfigAddr) external initializer { UtilLib.checkNonZeroAddress(lrtConfigAddr); lrtConfig = ILRTConfig(lrtConfigAddr); emit UpdatedLRTConfig(lrtConfigAddr); } /*////////////////////////////////////////////////////////////// write functions //////////////////////////////////////////////////////////////*/ /// @notice updates RSETH/ETH exchange rate /// @dev calculates rsETH price based on stakedAsset value received from EigenLayer function updateRSETHPrice() public whenNotPaused { _updateRsETHPrice(); } /// @dev update rsETH price as an manager account /// @dev main benefit is to be able to update the price in case of the price going above the threshold /// @dev only LRT manager is allowed to call this function function updateRSETHPriceAsManager() external onlyLRTManager { _updateRsETHPrice(); } /// @dev add/update the price oracle of any asset /// @dev only onlyLRTAdmin is allowed /// @param asset asset address for which oracle price needs to be added/updated function updatePriceOracleForValidated(address asset, address priceOracle) external onlyLRTAdmin { // Sanity check: oracle price must have precision between 1e16 and 1e19 uint256 price = IPriceFetcher(priceOracle).getAssetPrice(asset); if (price > 1e19 || price < 1e16) { revert InvalidPriceOracle(); } updatePriceOracleFor(asset, priceOracle); } /// @dev add/update the price oracle of any asset /// @dev only onlyLRTAdmin is allowed /// @param asset asset address for which oracle price needs to be added/updated function updatePriceOracleFor(address asset, address priceOracle) public onlyLRTAdmin { if (lrtConfig.isSupportedAsset(asset)) { UtilLib.checkNonZeroAddress(priceOracle); } assetPriceOracle[asset] = priceOracle; emit AssetPriceOracleUpdate(asset, priceOracle); } /// @dev set the price percentage limit. Only onlyLRTAdmin is allowed /// @dev PricePercentageLimit for 1% is 1e16 /// @dev Price Percentage Limit for 100% is 1e18 /// @param _pricePercentageLimit price percentage limit function setPricePercentageLimit(uint256 _pricePercentageLimit) external onlyLRTAdmin { pricePercentageLimit = _pricePercentageLimit; emit PricePercentageLimitUpdate(_pricePercentageLimit); } /// @dev set the maximum fee minting amount per day. Only onlyLRTManager is allowed /// @param _maxFeeMintAmountPerDay maximum amount of fee that can be minted per day function setMaxFeeMintAmountPerDay(uint256 _maxFeeMintAmountPerDay) external onlyLRTManager { maxFeeMintAmountPerDay = _maxFeeMintAmountPerDay; emit MaxFeeMintAmountPerDayUpdated(_maxFeeMintAmountPerDay); } /// @dev Pauses the pausable methods in the contract function pause() external onlyLRTManager whenNotPaused { _pause(); } /// @dev Unpauses the pausable methods in the contract function unpause() external onlyLRTAdmin whenPaused { paused = false; emit Unpaused(msg.sender); } /*////////////////////////////////////////////////////////////// view functions //////////////////////////////////////////////////////////////*/ /// @notice Provides Asset/ETH exchange rate /// @dev reads from priceFetcher interface which may fetch price from any supported oracle /// @param asset the asset for which exchange rate is required /// @return assetPrice exchange rate of asset function getAssetPrice(address asset) public view onlySupportedOracle(asset) returns (uint256) { return IPriceFetcher(assetPriceOracle[asset]).getAssetPrice(asset); } /*////////////////////////////////////////////////////////////// internal functions //////////////////////////////////////////////////////////////*/ /// @dev Checks and updates daily fee minting limits /// @param feeAmount The amount of fee to be minted function _checkAndUpdateDailyFeeMintLimit(uint256 feeAmount) internal { // Reset or initialize the period if it's unset or a day has passed if (feePeriodStartTime == 0 || block.timestamp >= feePeriodStartTime + 1 days) { currentPeriodMintedFeeAmount = 0; feePeriodStartTime = block.timestamp; } // Check if minting would exceed the daily limit if (currentPeriodMintedFeeAmount + feeAmount > maxFeeMintAmountPerDay) { revert DailyFeeMintLimitExceeded(currentPeriodMintedFeeAmount + feeAmount, maxFeeMintAmountPerDay); } currentPeriodMintedFeeAmount += feeAmount; } /// @dev Internal function to update rsETH price // solhint-disable-next-line code-complexity function _updateRsETHPrice() internal { address rsETHTokenAddress = lrtConfig.rsETH(); uint256 rsethSupply = IRSETH(rsETHTokenAddress).totalSupply(); if (rsethSupply == 0) { rsETHPrice = 1 ether; highestRsethPrice = 1 ether; return; } if (highestRsethPrice == 0) { highestRsethPrice = rsETHPrice; } uint256 previousPrice = rsETHPrice; // get total ETH in the protocol (normalized to 1e18) uint256 totalETHInProtocol = _getTotalEthInProtocol(); // calculate previousTVL using rsethSupply multiplied by rsETHPrice uint256 previousTVL = rsethSupply.mulWad(rsETHPrice); IPausable lrtDepositPool = IPausable(lrtConfig.getContract(LRTConstants.LRT_DEPOSIT_POOL)); IPausable withdrawalManager = IPausable(lrtConfig.getContract(LRTConstants.LRT_WITHDRAW_MANAGER)); // determine if the protocol is active (not paused) bool protocolPaused = lrtDepositPool.paused() || withdrawalManager.paused() || paused; // only take fee if TVL increased and protocol is not paused uint256 protocolFeeInETH = 0; if (!protocolPaused && totalETHInProtocol > previousTVL) { uint256 rewardAmount = totalETHInProtocol - previousTVL; protocolFeeInETH = (rewardAmount * lrtConfig.protocolFeeInBPS()) / 10_000; } // compute new rsETH price based on total ETH minus fee uint256 newRsETHPrice = (totalETHInProtocol - protocolFeeInETH).divWad(rsethSupply); if (newRsETHPrice > highestRsethPrice) { // check if the price is above the threshold uint256 priceDifference = newRsETHPrice - highestRsethPrice; // pricePercentageLimit is in 1e18 precision (100% = 1e18, 1% = 1e16) bool isPriceIncreaseOffLimit = pricePercentageLimit > 0 && priceDifference > pricePercentageLimit.mulWad(highestRsethPrice); // check if the price difference is above the threshold if (isPriceIncreaseOffLimit) { // if sender has a manager role, this doesn't revert. // if not, it reverts as price went above the threshold if (!IAccessControl(address(lrtConfig)).hasRole(LRTConstants.MANAGER, msg.sender)) { revert PriceAboveDailyThreshold(); } } } // downside protection — pause if price drops too far if (newRsETHPrice < highestRsethPrice) { uint256 diff = highestRsethPrice - newRsETHPrice; // normalizing to 1e18 bool isPriceDecreaseOffLimit = pricePercentageLimit > 0 && diff > pricePercentageLimit.mulWad(highestRsethPrice); // if price decrease is off limit, pause the protocol (unless it's already paused) if (isPriceDecreaseOffLimit) { if (!lrtDepositPool.paused()) lrtDepositPool.pause(); if (!withdrawalManager.paused()) withdrawalManager.pause(); _pause(); return; } // if price has decreased compared to the previous price, emit an event to reflect that if (previousPrice > newRsETHPrice) { emit RsETHPriceDecrease(newRsETHPrice, previousPrice); } // emit an event to notify that the price is currently below the peak (all time high) price emit RsETHPriceBelowPeak(highestRsethPrice, newRsETHPrice); } // update highest price if new price exceeds it if (newRsETHPrice > highestRsethPrice) { highestRsethPrice = newRsETHPrice; } // mint protocol fee as rsETH if there's a fee to take if (protocolFeeInETH > 0) { // Calculate rsETH amount to mint as protocol fee uint256 rsethAmountToMintAsProtocolFee = protocolFeeInETH.divWad(newRsETHPrice); // Calculate TVL increase in rsETH uint256 tvlIncreaseInRsETH = (totalETHInProtocol - previousTVL).divWad(newRsETHPrice); // invariant ensures protocol fee doesn't exceed TVL increase // this prevents taking more fees than the actual gains in the protocol if (rsethAmountToMintAsProtocolFee > tvlIncreaseInRsETH) { revert ProtocolFeeExceededTVLIncrease(); } _checkAndUpdateDailyFeeMintLimit(rsethAmountToMintAsProtocolFee); if (rsethAmountToMintAsProtocolFee > 0) { address treasury = lrtConfig.getContract(LRTConstants.PROTOCOL_TREASURY); IRSETH(rsETHTokenAddress).mint(treasury, rsethAmountToMintAsProtocolFee); emit FeeMinted(treasury, rsethAmountToMintAsProtocolFee); } } else { _checkAndUpdateDailyFeeMintLimit(0); } rsETHPrice = newRsETHPrice; emit RsETHPriceUpdate(rsETHPrice, previousPrice); } /// @dev Internal function to pause the contract function _pause() internal { if (paused) return; paused = true; emit Paused(msg.sender); } /*////////////////////////////////////////////////////////////// private functions //////////////////////////////////////////////////////////////*/ /// @notice get total ETH in protocol /// @return totalETHInProtocol total ETH in protocol (normalized to 1e18) function _getTotalEthInProtocol() private view returns (uint256 totalETHInProtocol) { address lrtDepositPoolAddr = lrtConfig.getContract(LRTConstants.LRT_DEPOSIT_POOL); address[] memory supportedAssets = lrtConfig.getSupportedAssetList(); uint256 supportedAssetCount = supportedAssets.length; for (uint16 assetIdx; assetIdx < supportedAssetCount;) { address asset = supportedAssets[assetIdx]; // assetER is in 1e18 precision (1.0 = 1e18) uint256 assetER = getAssetPrice(asset); // totalAssetAmt is in 1e18 precision (standard token decimals) uint256 totalAssetAmt = ILRTDepositPool(lrtDepositPoolAddr).getTotalAssetDeposits(asset); totalETHInProtocol += totalAssetAmt.mulWad(assetER); unchecked { ++assetIdx; } } } }
変更されたテキスト
ファイルを開く
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.27; import { UtilLib } from "./utils/UtilLib.sol"; import { LRTConstants } from "./utils/LRTConstants.sol"; import { LRTConfigRoleChecker, ILRTConfig } from "./utils/LRTConfigRoleChecker.sol"; import { WadMath } from "./utils/WadMath.sol"; import { IRSETH } from "./interfaces/IRSETH.sol"; import { IPriceFetcher } from "./interfaces/IPriceFetcher.sol"; import { ILRTOracle } from "./interfaces/ILRTOracle.sol"; import { ILRTDepositPool } from "./interfaces/ILRTDepositPool.sol"; import { IAccessControl } from "@openzeppelin/contracts/access/IAccessControl.sol"; import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; interface IPausable { function pause() external; function paused() external view returns (bool); } /// @title LRTOracle Contract /// @notice oracle contract that calculates the exchange rate of assets contract LRTOracle is ILRTOracle, LRTConfigRoleChecker, Initializable { using WadMath for uint256; mapping(address asset => address priceOracle) public override assetPriceOracle; uint256 public override rsETHPrice; uint256 public pricePercentageLimit; uint256 public highestRsethPrice; // Daily fee minting limit variables uint256 public currentPeriodMintedFeeAmount; uint256 public feePeriodStartTime; uint256 public maxFeeMintAmountPerDay; /// @notice New variable added for pausable functionality bool public paused; modifier onlySupportedOracle(address asset) { if (assetPriceOracle[asset] == address(0)) { revert AssetOracleNotSupported(); } _; } modifier whenNotPaused() { if (paused) revert ContractPaused(); _; } modifier whenPaused() { if (!paused) revert ContractNotPaused(); _; } /// @custom:oz-upgrades-unsafe-allow constructor constructor() { _disableInitializers(); } /// @dev Initializes the contract /// @param lrtConfigAddr LRT config address function initialize(address lrtConfigAddr) external initializer { UtilLib.checkNonZeroAddress(lrtConfigAddr); lrtConfig = ILRTConfig(lrtConfigAddr); emit UpdatedLRTConfig(lrtConfigAddr); } /// @notice Initializes the contract with a fee period start time /// @param _feePeriodStartTime The start time of the fee period function reinitialize(uint256 _feePeriodStartTime) external reinitializer(2) onlyLRTManager { if (_feePeriodStartTime > block.timestamp || _feePeriodStartTime <= block.timestamp - 1 days) { revert PeriodStartTimeShouldBeWithin24Hours(); } feePeriodStartTime = _feePeriodStartTime; emit FeePeriodStartTimeSet(_feePeriodStartTime); } /*////////////////////////////////////////////////////////////// write functions //////////////////////////////////////////////////////////////*/ /// @notice updates RSETH/ETH exchange rate /// @dev calculates rsETH price based on stakedAsset value received from EigenLayer function updateRSETHPrice() public whenNotPaused { _updateRsETHPrice(); } /// @dev update rsETH price as an manager account /// @dev main benefit is to be able to update the price in case of the price going above the threshold /// @dev only LRT manager is allowed to call this function function updateRSETHPriceAsManager() external onlyLRTManager { _updateRsETHPrice(); } /// @dev add/update the price oracle of any asset /// @dev only onlyLRTAdmin is allowed /// @param asset asset address for which oracle price needs to be added/updated function updatePriceOracleForValidated(address asset, address priceOracle) external onlyLRTAdmin { // Sanity check: oracle price must have precision between 1e16 and 1e19 uint256 price = IPriceFetcher(priceOracle).getAssetPrice(asset); if (price > 1e19 || price < 1e16) { revert InvalidPriceOracle(); } updatePriceOracleFor(asset, priceOracle); } /// @dev add/update the price oracle of any asset /// @dev only onlyLRTAdmin is allowed /// @param asset asset address for which oracle price needs to be added/updated function updatePriceOracleFor(address asset, address priceOracle) public onlyLRTAdmin { if (lrtConfig.isSupportedAsset(asset)) { UtilLib.checkNonZeroAddress(priceOracle); } assetPriceOracle[asset] = priceOracle; emit AssetPriceOracleUpdate(asset, priceOracle); } /// @dev set the price percentage limit. Only onlyLRTAdmin is allowed /// @dev PricePercentageLimit for 1% is 1e16 /// @dev Price Percentage Limit for 100% is 1e18 /// @param _pricePercentageLimit price percentage limit function setPricePercentageLimit(uint256 _pricePercentageLimit) external onlyLRTAdmin { pricePercentageLimit = _pricePercentageLimit; emit PricePercentageLimitUpdate(_pricePercentageLimit); } /// @dev set the maximum fee minting amount per day. Only onlyLRTManager is allowed /// @param _maxFeeMintAmountPerDay maximum amount of fee that can be minted per day function setMaxFeeMintAmountPerDay(uint256 _maxFeeMintAmountPerDay) external onlyLRTManager { maxFeeMintAmountPerDay = _maxFeeMintAmountPerDay; emit MaxFeeMintAmountPerDayUpdated(_maxFeeMintAmountPerDay); } /// @dev Triggers stopped state. Contract must not be paused. function pause() external whenNotPaused onlyRole(LRTConstants.PAUSER_ROLE) { _pause(); } /// @dev Returns to normal state. Contract must be paused. function unpause() external whenPaused onlyLRTAdmin { paused = false; emit Unpaused(msg.sender); } /*////////////////////////////////////////////////////////////// view functions //////////////////////////////////////////////////////////////*/ /// @notice Provides Asset/ETH exchange rate /// @dev reads from priceFetcher interface which may fetch price from any supported oracle /// @param asset the asset for which exchange rate is required /// @return assetPrice exchange rate of asset function getAssetPrice(address asset) public view onlySupportedOracle(asset) returns (uint256) { return IPriceFetcher(assetPriceOracle[asset]).getAssetPrice(asset); } /// @notice Gets the start timestamp of the current 24-hour fee minting period /// @return uint256 The start timestamp of the current 24-hour period, calculated from the fee period start time function getCurrentPeriodStartTime() public view returns (uint256) { // Calculate the full (complete) days elapsed since the period start time (floors the result) uint256 daysElapsed = (block.timestamp - feePeriodStartTime) / 1 days; return feePeriodStartTime + daysElapsed * 1 days; } /// @notice Gets the remaining daily fee minting limit for the current period /// @return uint256 The remaining daily fee minting limit function remainingDailyFeeMintLimit() external view returns (uint256) { if (maxFeeMintAmountPerDay == 0) return 0; // If we're on a new day but no mint has occurred yet, treat currentPeriodMintedAmount as 0 uint256 effectiveDailyFeeMintAmount = (block.timestamp >= feePeriodStartTime + 1 days) ? 0 : currentPeriodMintedFeeAmount; return maxFeeMintAmountPerDay > effectiveDailyFeeMintAmount ? maxFeeMintAmountPerDay - effectiveDailyFeeMintAmount : 0; } /// @notice Returns the timestamp at which the current effective daily fee minting limit period ends, /// accounting for any skipped days during which no fee minting occurred /// @dev A fee mint executed at exactly this timestamp is counted towards the next period's fee minting limit /// @return uint256 The timestamp at which the effective current fee minting limit period ends function getNextDailyLimitResetTimestamp() external view returns (uint256) { return getCurrentPeriodStartTime() + 1 days; } /*////////////////////////////////////////////////////////////// internal functions //////////////////////////////////////////////////////////////*/ /// @dev Checks and updates daily fee minting limits /// @param feeAmount The amount of fee to be minted function _checkAndUpdateDailyFeeMintLimit(uint256 feeAmount) internal { // Reset the period if it's unset or a day has passed if (block.timestamp >= feePeriodStartTime + 1 days) { currentPeriodMintedFeeAmount = 0; feePeriodStartTime = getCurrentPeriodStartTime(); } // Check if minting would exceed the daily limit if (currentPeriodMintedFeeAmount + feeAmount > maxFeeMintAmountPerDay) { revert DailyFeeMintLimitExceeded(currentPeriodMintedFeeAmount + feeAmount, maxFeeMintAmountPerDay); } currentPeriodMintedFeeAmount += feeAmount; } /// @dev Internal function to update rsETH price // solhint-disable-next-line code-complexity function _updateRsETHPrice() internal { address rsETHTokenAddress = lrtConfig.rsETH(); uint256 rsethSupply = IRSETH(rsETHTokenAddress).totalSupply(); if (rsethSupply == 0) { rsETHPrice = 1 ether; highestRsethPrice = 1 ether; return; } if (highestRsethPrice == 0) { highestRsethPrice = rsETHPrice; } uint256 previousPrice = rsETHPrice; // get total ETH in the protocol (normalized to 1e18) uint256 totalETHInProtocol = _getTotalEthInProtocol(); // calculate previousTVL using rsethSupply multiplied by rsETHPrice uint256 previousTVL = rsethSupply.mulWad(rsETHPrice); IPausable lrtDepositPool = IPausable(lrtConfig.getContract(LRTConstants.LRT_DEPOSIT_POOL)); IPausable withdrawalManager = IPausable(lrtConfig.getContract(LRTConstants.LRT_WITHDRAW_MANAGER)); // determine if the protocol is active (not paused) bool protocolPaused = lrtDepositPool.paused() || withdrawalManager.paused() || paused; // only take fee if TVL increased and protocol is not paused uint256 protocolFeeInETH = 0; if (!protocolPaused && totalETHInProtocol > previousTVL) { uint256 rewardAmount = totalETHInProtocol - previousTVL; protocolFeeInETH = (rewardAmount * lrtConfig.protocolFeeInBPS()) / 10_000; } // compute new rsETH price based on total ETH minus fee uint256 newRsETHPrice = (totalETHInProtocol - protocolFeeInETH).divWad(rsethSupply); if (newRsETHPrice > highestRsethPrice) { // check if the price is above the threshold uint256 priceDifference = newRsETHPrice - highestRsethPrice; // pricePercentageLimit is in 1e18 precision (100% = 1e18, 1% = 1e16) bool isPriceIncreaseOffLimit = pricePercentageLimit > 0 && priceDifference > pricePercentageLimit.mulWad(highestRsethPrice); // check if the price difference is above the threshold if (isPriceIncreaseOffLimit) { // if sender has a manager role, this doesn't revert. // if not, it reverts as price went above the threshold if (!IAccessControl(address(lrtConfig)).hasRole(LRTConstants.MANAGER, msg.sender)) { revert PriceAboveDailyThreshold(); } } } // downside protection — pause if price drops too far if (newRsETHPrice < highestRsethPrice) { uint256 diff = highestRsethPrice - newRsETHPrice; // normalizing to 1e18 bool isPriceDecreaseOffLimit = pricePercentageLimit > 0 && diff > pricePercentageLimit.mulWad(highestRsethPrice); // if price decrease is off limit, pause the protocol (unless it's already paused) if (isPriceDecreaseOffLimit) { if (!lrtDepositPool.paused()) lrtDepositPool.pause(); if (!withdrawalManager.paused()) withdrawalManager.pause(); _pause(); return; } // if price has decreased compared to the previous price, emit an event to reflect that if (previousPrice > newRsETHPrice) { emit RsETHPriceDecrease(newRsETHPrice, previousPrice); } // emit an event to notify that the price is currently below the peak (all time high) price emit RsETHPriceBelowPeak(highestRsethPrice, newRsETHPrice); } // update highest price if new price exceeds it if (newRsETHPrice > highestRsethPrice) { highestRsethPrice = newRsETHPrice; } // mint protocol fee as rsETH if there's a fee to take if (protocolFeeInETH > 0) { // Calculate rsETH amount to mint as protocol fee uint256 rsethAmountToMintAsProtocolFee = protocolFeeInETH.divWad(newRsETHPrice); _checkAndUpdateDailyFeeMintLimit(rsethAmountToMintAsProtocolFee); if (rsethAmountToMintAsProtocolFee > 0) { address treasury = lrtConfig.getContract(LRTConstants.PROTOCOL_TREASURY); IRSETH(rsETHTokenAddress).mint(treasury, rsethAmountToMintAsProtocolFee); emit FeeMinted(treasury, rsethAmountToMintAsProtocolFee); } } else { _checkAndUpdateDailyFeeMintLimit(0); } rsETHPrice = newRsETHPrice; emit RsETHPriceUpdate(rsETHPrice, previousPrice); } /// @dev Internal function to pause the contract function _pause() internal { if (paused) return; paused = true; emit Paused(msg.sender); } /*////////////////////////////////////////////////////////////// private functions //////////////////////////////////////////////////////////////*/ /// @notice get total ETH in protocol /// @return totalETHInProtocol total ETH in protocol (normalized to 1e18) function _getTotalEthInProtocol() private view returns (uint256 totalETHInProtocol) { address lrtDepositPoolAddr = lrtConfig.getContract(LRTConstants.LRT_DEPOSIT_POOL); address[] memory supportedAssets = lrtConfig.getSupportedAssetList(); uint256 supportedAssetCount = supportedAssets.length; for (uint16 assetIdx; assetIdx < supportedAssetCount;) { address asset = supportedAssets[assetIdx]; // assetER is in 1e18 precision (1.0 = 1e18) uint256 assetER = getAssetPrice(asset); // totalAssetAmt is in 1e18 precision (standard token decimals) uint256 totalAssetAmt = ILRTDepositPool(lrtDepositPoolAddr).getTotalAssetDeposits(asset); totalETHInProtocol += totalAssetAmt.mulWad(assetER); unchecked { ++assetIdx; } } } }
違いを見つける