Diff
checker
텍스트
텍스트
이미지
문서
Excel
폴더
Legal
Enterprise
데스크톱
요금제
로그인
데스크톱 앱 다운로드
텍스트 비교
두 텍스트 파일의 차이점을 찾아보세요
도구
기록
표시
레이아웃
나란히 보기
합쳐 보기
실시간 편집
공백 변경 숨기기
변경 없는 행 숨기기
줄바꿈 비활성화
비교 단위
스마트
구문
없음
모양 변경
처리
제외
텍스트 변환
첫 변경으로
수정
Diffchecker Desktop
가장 안전하게 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; } } } }
비교하기