Diff
checker
文本
文本
图像
文档
Excel
文件夹
Legal
Enterprise
桌面版
定价
登录
下载 Diffchecker 桌面版
比较文本
查找两个文本文件之间的差异
工具
历史
显示
视图
拆分
统一
实时编辑器
隐藏空白更改
折叠未更改行
关闭换行
比对精度
智能
语法
无
更改外观
处理
忽略
文本转换
转到第一个差异
编辑输入
Diffchecker Desktop
运行Diffchecker最安全的方式。获取Diffchecker桌面应用:您的差异永远不会离开您的电脑!
获取桌面版
LRTOracle
创建于
12个月前
差异永不过期
清除
导出
分享
解释
68 删除
行
总计
删除
字符
总计
删除
要继续使用此功能,请升级到
Diff
checker
Pro
查看价格
215 行
全部复制
180 添加
行
总计
添加
字符
总计
添加
要继续使用此功能,请升级到
Diff
checker
Pro
查看价格
317 行
全部复制
// 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 { 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);
}
}
/// @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;
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;
复制
已复制
复制
已复制
//
legacy
variables
//
Daily fee minting limit
variables
uint256 public
legacy_cooldownPeriodInTimestamp
;
uint256 public
currentPeriodMintedFeeAmount;
uint256 public
legacy_lastUpdatedCoolDownTimestamp;
uint256 public feePeriodStartTime
;
uint256 public
maxFeeMintAmountPerDay;
/// @notice New variable added for pausable functionality
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() {
if (paused) revert ContractPaused();
_;
}
modifier whenPaused() {
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);
}
}
/*//////////////////////////////////////////////////////////////
/*//////////////////////////////////////////////////////////////
write functions
write functions
//////////////////////////////////////////////////////////////*/
//////////////////////////////////////////////////////////////*/
/// @notice updates RSETH/ETH exchange rate
/// @notice updates RSETH/ETH exchange rate
复制
已复制
复制
已复制
/// @dev calculates
based on stakedAsset value received from
eigen layer
/// @dev calculates
rsETH price
based on stakedAsset value received from
EigenLayer
function updateRSETHPrice
() public {
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 updateRSETHPrice
AsManager() 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);
}
Text moved from lines 168-180
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();
address rsETHTokenAddress = lrtConfig.rsETH();
uint256 rsethSupply = IRSETH(rsETHTokenAddress).totalSupply();
uint256 rsethSupply = IRSETH(rsETHTokenAddress).totalSupply();
if (rsethSupply == 0) {
if (rsethSupply == 0) {
rsETHPrice = 1 ether;
rsETHPrice = 1 ether;
highestRsethPrice = 1 ether;
highestRsethPrice = 1 ether;
return;
return;
}
}
if (highestRsethPrice == 0) {
if (highestRsethPrice == 0) {
highestRsethPrice = rsETHPrice;
highestRsethPrice = rsETHPrice;
}
}
uint256 previousPrice = rsETHPrice;
uint256 previousPrice = rsETHPrice;
复制
已复制
复制
已复制
// get total ETH in the protocol
// get total ETH in the protocol
(normalized to 1e18)
uint256 totalETHInProtocol = _getTotalEthInProtocol();
uint256 totalETHInProtocol = _getTotalEthInProtocol();
// calculate previousTVL using rsethSupply multiplied by rsETHPrice
// calculate previousTVL using rsethSupply multiplied by rsETHPrice
复制
已复制
复制
已复制
uint256 previousTVL = rsethSupply
*
rsETHPrice
;
uint256 previousTVL = rsethSupply
.mulWad(
rsETHPrice
)
;
复制
已复制
复制
已复制
// only take fee if TVL increased
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;
uint256 protocolFeeInETH = 0;
复制
已复制
复制
已复制
if (
totalETHInProtocol > previousTVL) {
if (
!protocolPaused &&
totalETHInProtocol > previousTVL) {
uint256 rewardAmount = totalETHInProtocol - previousTVL;
uint256 rewardAmount = totalETHInProtocol - previousTVL;
protocolFeeInETH = (rewardAmount * lrtConfig.protocolFeeInBPS()) / 10_000;
protocolFeeInETH = (rewardAmount * lrtConfig.protocolFeeInBPS()) / 10_000;
}
}
// compute new rsETH price based on total ETH minus fee
// compute new rsETH price based on total ETH minus fee
复制
已复制
复制
已复制
uint256 newRsETHPrice = (totalETHInProtocol - protocolFeeInETH)
/
rsethSupply
;
uint256 newRsETHPrice = (totalETHInProtocol - protocolFeeInETH)
.divWad(
rsethSupply
)
;
if (newRsETHPrice > highestRsethPrice) {
if (newRsETHPrice > highestRsethPrice) {
// check if the price is above the threshold
// check if the price is above the threshold
uint256 priceDifference = newRsETHPrice - highestRsethPrice;
uint256 priceDifference = newRsETHPrice - highestRsethPrice;
复制
已复制
复制
已复制
// pricePercentageLimit is in 1e18 precision (100% = 1e18, 1% = 1e16)
bool isPriceIncreaseOffLimit =
bool isPriceIncreaseOffLimit =
复制
已复制
复制
已复制
pricePercentageLimit > 0 && priceDifference >
(
pricePercentageLimit
*
highestRsethPrice)
/ 1e18
;
pricePercentageLimit > 0 && priceDifference >
pricePercentageLimit
.mulWad(
highestRsethPrice)
;
// check if the price difference is above the threshold
// check if the price difference is above the threshold
if (isPriceIncreaseOffLimit) {
if (isPriceIncreaseOffLimit) {
// if sender has a manager role, this doesn't revert.
// if sender has a manager role, this doesn't revert.
已保存差异
原始文本
打开文件
// 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 { 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; } /// @title LRTOracle Contract /// @notice oracle contract that calculates the exchange rate of assets contract LRTOracle is ILRTOracle, LRTConfigRoleChecker, Initializable { mapping(address asset => address priceOracle) public override assetPriceOracle; uint256 public override rsETHPrice; uint256 public pricePercentageLimit; uint256 public highestRsethPrice; // legacy variables uint256 public legacy_cooldownPeriodInTimestamp; uint256 public legacy_lastUpdatedCoolDownTimestamp; modifier onlySupportedOracle(address asset) { if (assetPriceOracle[asset] == address(0)) { revert AssetOracleNotSupported(); } _; } /// @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 based on stakedAsset value received from eigen layer function updateRSETHPrice() public { 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 uint256 totalETHInProtocol = _getTotalEthInProtocol(); // calculate previousTVL using rsethSupply multiplied by rsETHPrice uint256 previousTVL = rsethSupply * rsETHPrice; // only take fee if TVL increased uint256 protocolFeeInETH = 0; if (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) / rsethSupply; if (newRsETHPrice > highestRsethPrice) { // check if the price is above the threshold uint256 priceDifference = newRsETHPrice - highestRsethPrice; bool isPriceIncreaseOffLimit = pricePercentageLimit > 0 && priceDifference > (pricePercentageLimit * highestRsethPrice) / 1e18; // 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; bool isPriceDecreaseOffLimit = pricePercentageLimit > 0 && diff > (pricePercentageLimit * highestRsethPrice) / 1e18; emit RsETHPriceDecrease(highestRsethPrice, newRsETHPrice); if (isPriceDecreaseOffLimit) { IPausable lrtDepositPool = IPausable(lrtConfig.getContract(LRTConstants.LRT_DEPOSIT_POOL)); IPausable withdrawalManager = IPausable(lrtConfig.getContract(LRTConstants.LRT_WITHDRAW_MANAGER)); lrtDepositPool.pause(); withdrawalManager.pause(); return; } } // 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) { uint256 rsethAmountToMintAsProtocolFee = protocolFeeInETH / newRsETHPrice; if (rsethAmountToMintAsProtocolFee > 0) { address treasury = lrtConfig.getContract(LRTConstants.PROTOCOL_TREASURY); IRSETH(rsETHTokenAddress).mint(treasury, rsethAmountToMintAsProtocolFee); emit FeeMinted(treasury, rsethAmountToMintAsProtocolFee); } } rsETHPrice = newRsETHPrice; emit RsETHPriceUpdate(rsETHPrice, previousPrice); } /// @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 onlyLRTManager is allowed 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 that oracle has 1e18 precision uint256 price = IPriceFetcher(priceOracle).getAssetPrice(asset); if (price > 1e19 || price < 1e18) { 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 { 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); } /*////////////////////////////////////////////////////////////// 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); } /*////////////////////////////////////////////////////////////// private functions //////////////////////////////////////////////////////////////*/ /// @notice get total ETH in protocol /// @return totalETHInProtocol total ETH in protocol 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]; uint256 assetER = getAssetPrice(asset); uint256 totalAssetAmt = ILRTDepositPool(lrtDepositPoolAddr).getTotalAssetDeposits(asset); totalETHInProtocol += totalAssetAmt * 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); } /*////////////////////////////////////////////////////////////// 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; } } } }
查找差异