Diff
checker
テキスト
テキスト
画像
ドキュメント
Excel
フォルダ
Legal
Enterprise
デスクトップ
料金
ログイン
Diffchecker デスクトップのダウンロード
テキスト比較
2 つのテキスト ファイルの違いを見つける
ツール
履歴
表示
レイアウト
分割
統合
ライブエディター
空白の変更を非表示
未変更行を折りたたむ
折り返しなし
比較精度
スマート
構文
なし
外観を変更
処理
無視
テキスト変換
最初の差分へ移動
入力を編集
Diffchecker Desktop
Diffcheckerを実行する最も安全な方法。Diffchecker Desktopアプリを入手:あなたの差分はコンピューターから出ることはありません!
Desktopを入手
RSETH
作成日
10 か月前
差分は期限切れになりません
クリア
エクスポート
共有
説明
14 削除
行
合計
削除
文字
合計
削除
この機能を引き続き使用するには、アップグレードしてください
Diff
checker
Pro
価格を見る
98 行
すべてコピー
230 追加
行
合計
追加
文字
合計
追加
この機能を引き続き使用するには、アップグレードしてください
Diff
checker
Pro
価格を見る
315 行
すべてコピー
// 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 { LRTConfigRoleChecker, ILRTConfig, LRTConstants } from "./utils/LRTConfigRoleChecker.sol";
import { LRTConfigRoleChecker, ILRTConfig, LRTConstants } from "./utils/LRTConfigRoleChecker.sol";
import { ERC20Upgradeable, Initializable } from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
import { ERC20Upgradeable, Initializable } from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
import { PausableUpgradeable } from "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
import { PausableUpgradeable } from "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
/// @title rsETH token Contract
/// @title rsETH token Contract
/// @author Kelp DAO
/// @author Kelp DAO
/// @notice The ERC20 contract for the rsETH token
/// @notice The ERC20 contract for the rsETH token
contract RSETH is Initializable, LRTConfigRoleChecker, ERC20Upgradeable, PausableUpgradeable {
contract RSETH is Initializable, LRTConfigRoleChecker, ERC20Upgradeable, PausableUpgradeable {
コピー
コピー済み
コピー
コピー済み
error DailyMintLimitExceeded(uint256 currentAmount, uint256 maxAmount);
/*//////////////////////////////////////////////////////////////
State Variables
event MaxMintAmountPerDayUpdated(uint256 newMaxMintAmountPerDay);
//////////////////////////////////////////////////////////////*/
コピー
コピー済み
コピー
コピー済み
/// @notice Maximum amount that can be minted in a 24-hour period
uint256 public maxMintAmountPerDay;
uint256 public maxMintAmountPerDay;
コピー
コピー済み
コピー
コピー済み
/// @notice Amount minted in the current 24-hour period
uint256 public currentPeriodMintedAmount;
uint256 public currentPeriodMintedAmount;
コピー
コピー済み
コピー
コピー済み
/// @notice Start time of the current 24-hour period
uint256 public periodStartTime;
uint256 public periodStartTime;
コピー
コピー済み
コピー
コピー済み
/// @notice Address to which recovered funds are sent
address public custodyAddress;
/// @dev If > 0, transfers TO or FROM this address are blocked until timestamp (24h block)
mapping(address account => uint256 blockedUntil) public transfersBlockedUntil;
/// @dev Permanently exempt addresses mapping (rsETH transfers for these can never be blocked)
mapping(address account => bool isExempt) public isPermanentlyExempt;
/*//////////////////////////////////////////////////////////////
Modifiers
//////////////////////////////////////////////////////////////*/
/// @dev Modifier to check and update daily mint limits
/// @dev Modifier to check and update daily mint limits
/// @param amount The amount to be minted
/// @param amount The amount to be minted
modifier checkDailyMintLimit(uint256 amount) {
modifier checkDailyMintLimit(uint256 amount) {
コピー
コピー済み
コピー
コピー済み
// Check if we need to reset the period
or initialize the period
// Check if we need to reset the period
if it has been more than 24 hours
if (
periodStartTime == 0 ||
block.timestamp >= periodStartTime + 1 days) {
if (
block.timestamp >= periodStartTime + 1 days) {
currentPeriodMintedAmount = 0;
currentPeriodMintedAmount = 0;
コピー
コピー済み
コピー
コピー済み
periodStartTime =
block.timestamp
;
periodStartTime =
getCurrentPeriodStartTime()
;
}
}
// Check if minting would exceed the daily limit
// Check if minting would exceed the daily limit
if (currentPeriodMintedAmount + amount > maxMintAmountPerDay) {
if (currentPeriodMintedAmount + amount > maxMintAmountPerDay) {
revert DailyMintLimitExceeded(currentPeriodMintedAmount + amount, maxMintAmountPerDay);
revert DailyMintLimitExceeded(currentPeriodMintedAmount + amount, maxMintAmountPerDay);
}
}
currentPeriodMintedAmount += amount;
currentPeriodMintedAmount += amount;
_;
_;
}
}
コピー
コピー済み
コピー
コピー済み
/*//////////////////////////////////////////////////////////////
Custom Errors
//////////////////////////////////////////////////////////////*/
error PeriodStartTimeShouldBeWithin24Hours();
error DailyMintLimitExceeded(uint256 currentAmount, uint256 maxAmount);
error TransfersBlocked(address account, uint256 blockedUntil);
error CannotPermanentlyExemptBlockedAddress(address account, uint256 blockedUntil);
error AddressPermanentlyExempt(address account);
error NoActiveTransferBlock(address account);
/*//////////////////////////////////////////////////////////////
Events
//////////////////////////////////////////////////////////////*/
event MaxMintAmountPerDayUpdated(uint256 newMaxMintAmountPerDay);
event FrozenFundsRecovered(address indexed from, address indexed to, uint256 amount);
event UserTransfersBlocked(address indexed user, uint256 until);
event PermanentExemptionAdded(address indexed account);
event CustodyAddressUpdated(address indexed newCustodyAddress);
event PeriodStartTimeSet(uint256 newPeriodStartTime);
/*//////////////////////////////////////////////////////////////
Constructor
//////////////////////////////////////////////////////////////*/
/// @custom:oz-upgrades-unsafe-allow constructor
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
constructor() {
_disableInitializers();
_disableInitializers();
}
}
コピー
コピー済み
コピー
コピー済み
/*//////////////////////////////////////////////////////////////
Initializers
//////////////////////////////////////////////////////////////*/
/// @dev Initializes the contract
/// @dev Initializes the contract
/// @param admin Admin address
/// @param admin Admin address
/// @param lrtConfigAddr LRT config address
/// @param lrtConfigAddr LRT config address
function initialize(address admin, address lrtConfigAddr) external initializer {
function initialize(address admin, address lrtConfigAddr) external initializer {
UtilLib.checkNonZeroAddress(admin);
UtilLib.checkNonZeroAddress(admin);
UtilLib.checkNonZeroAddress(lrtConfigAddr);
UtilLib.checkNonZeroAddress(lrtConfigAddr);
__ERC20_init("rsETH", "rsETH");
__ERC20_init("rsETH", "rsETH");
__Pausable_init();
__Pausable_init();
lrtConfig = ILRTConfig(lrtConfigAddr);
lrtConfig = ILRTConfig(lrtConfigAddr);
emit UpdatedLRTConfig(lrtConfigAddr);
emit UpdatedLRTConfig(lrtConfigAddr);
}
}
コピー
コピー済み
コピー
コピー済み
/// @notice Initializes the contract with a period start time and the custody address
/// @param _periodStartTime The period start time
/// @param _custodyAddress The custody address for recovered funds
function reinitialize(uint256 _periodStartTime, address _custodyAddress) external reinitializer(2) onlyLRTManager {
if (_periodStartTime > block.timestamp || _periodStartTime <= block.timestamp - 1 days) {
revert PeriodStartTimeShouldBeWithin24Hours();
}
periodStartTime = _periodStartTime;
emit PeriodStartTimeSet(_periodStartTime);
_setCustodyAddress(_custodyAddress);
}
/*//////////////////////////////////////////////////////////////
Manager Functions
//////////////////////////////////////////////////////////////*/
/// @notice Sets the maximum amount that can be minted in 24 hours
/// @notice Sets the maximum amount that can be minted in 24 hours
/// @param _maxMintAmountPerDay The maximum amount that can be minted in 24 hours
/// @param _maxMintAmountPerDay The maximum amount that can be minted in 24 hours
function setMaxMintAmountPerDay(uint256 _maxMintAmountPerDay) external onlyLRTManager {
function setMaxMintAmountPerDay(uint256 _maxMintAmountPerDay) external onlyLRTManager {
maxMintAmountPerDay = _maxMintAmountPerDay;
maxMintAmountPerDay = _maxMintAmountPerDay;
emit MaxMintAmountPerDayUpdated(_maxMintAmountPerDay);
emit MaxMintAmountPerDayUpdated(_maxMintAmountPerDay);
}
}
コピー
コピー済み
コピー
コピー済み
/// @notice Permanently add accounts to the exempted list (non-reversible)
/// @param accounts Accounts to mark as permanently exempt (cannot have transfers blocked)
function addPermanentExemptions(address[] calldata accounts) external onlyLRTManager {
uint256 length = accounts.length;
for (uint256 i = 0; i < length; ++i) {
address account = accounts[i];
UtilLib.checkNonZeroAddress(account);
// Ensure the account is not currently blocked
uint256 blockedUntil = transfersBlockedUntil[account];
if (blockedUntil != 0) {
if (block.timestamp < blockedUntil) {
revert CannotPermanentlyExemptBlockedAddress(account, blockedUntil);
}
// Auto-clean up expired block
delete transfersBlockedUntil[account];
}
if (!isPermanentlyExempt[account]) {
isPermanentlyExempt[account] = true;
emit PermanentExemptionAdded(account);
}
}
}
/// @notice Block transfers TO and FROM given users for 24 hours
/// @dev Re-applying the block before expiry refreshes the hold to `block.timestamp + 1 days`
/// (i.e. not cumulative; never more than 24h from the latest call). Exempt addresses cannot be blocked.
/// Emits {UserTransfersBlocked} only when the timestamp changes.
/// @param accounts Addresses to block.
function blockUserTransfers(address[] calldata accounts) external onlyLRTManager {
uint256 blockedUntil = block.timestamp + 1 days;
uint256 length = accounts.length;
for (uint256 i = 0; i < length; ++i) {
address account = accounts[i];
if (isPermanentlyExempt[account] || account == address(0)) continue;
uint256 prevBlockedUntil = transfersBlockedUntil[account];
if (blockedUntil != prevBlockedUntil) {
transfersBlockedUntil[account] = blockedUntil;
emit UserTransfersBlocked(account, blockedUntil);
}
}
}
/*//////////////////////////////////////////////////////////////
Pause Management
//////////////////////////////////////////////////////////////*/
/// @dev Triggers stopped state. Contract must not be paused.
function pause() external onlyRole(LRTConstants.PAUSER_ROLE) {
_pause();
}
/// @dev Returns to normal state. Contract must be paused.
function unpause() external onlyLRTAdmin {
_unpause();
}
/*//////////////////////////////////////////////////////////////
Admin Functions
//////////////////////////////////////////////////////////////*/
/// @notice Sets the custody address for recovered funds
/// @param newCustodyAddress The new custody address
function setCustodyAddress(address newCustodyAddress) external onlyLRTAdmin {
_setCustodyAddress(newCustodyAddress);
}
/// @notice Recover the entire balance from a currently blocked, non-exempt address to a designated custody address
/// @dev Only callable by LRT admin. Works only while the block is active.
/// Emits {FrozenFundsRecovered} even if the recovered amount is zero (for transparency and completeness).
function recoverFrozenFunds(address from) external onlyLRTAdmin {
UtilLib.checkNonZeroAddress(from);
UtilLib.checkNonZeroAddress(custodyAddress);
if (isPermanentlyExempt[from]) revert AddressPermanentlyExempt(from);
uint256 blockedUntil = transfersBlockedUntil[from];
if (blockedUntil == 0 || block.timestamp >= blockedUntil) revert NoActiveTransferBlock(from);
uint256 accountBalance = balanceOf(from);
// Bypass transfer block enforcement when transferring to custody address
super._transfer(from, custodyAddress, accountBalance);
emit FrozenFundsRecovered(from, custodyAddress, accountBalance);
}
/*//////////////////////////////////////////////////////////////
Mint & Burn
//////////////////////////////////////////////////////////////*/
/// @notice Mints rsETH when called by an authorized caller
/// @notice Mints rsETH when called by an authorized caller
/// @param to the account to mint to
/// @param to the account to mint to
/// @param amount the amount of rsETH to mint
/// @param amount the amount of rsETH to mint
function mint(
function mint(
address to,
address to,
uint256 amount
uint256 amount
)
)
external
external
onlyRole(LRTConstants.MINTER_ROLE)
onlyRole(LRTConstants.MINTER_ROLE)
whenNotPaused
whenNotPaused
checkDailyMintLimit(amount)
checkDailyMintLimit(amount)
{
{
コピー
コピー済み
コピー
コピー済み
_enforceNotBlocked(to);
_mint(to, amount);
_mint(to, amount);
}
}
/// @notice Burns rsETH when called by an authorized caller
/// @notice Burns rsETH when called by an authorized caller
/// @param account the account to burn from
/// @param account the account to burn from
/// @param amount the amount of rsETH to burn
/// @param amount the amount of rsETH to burn
function burnFrom(address account, uint256 amount) external onlyRole(LRTConstants.BURNER_ROLE) whenNotPaused {
function burnFrom(address account, uint256 amount) external onlyRole(LRTConstants.BURNER_ROLE) whenNotPaused {
コピー
コピー済み
コピー
コピー済み
_enforceNotBlocked(account);
_burn(account, amount);
_burn(account, amount);
}
}
コピー
コピー済み
コピー
コピー済み
/// @dev Triggers stopped state.
/*//////////////////////////////////////////////////////////////
/// @dev Only callable by LRT config manager. Contract must NOT be paused.
View Functions
function pause() external onlyLRTManager {
//////////////////////////////////////////////////////////////*/
_pause();
/// @notice Returns the aligned start timestamp of the effective current 24-hour minting period, accounting for any
/// possibly skipped days
/// @return uint256 The start timestamp of the effective current 24-hour period
function getCurrentPeriodStartTime() public view returns (uint256) {
// Calculate the full (complete) days elapsed since the period start time (floors the result)
uint256 daysElapsed = (block.timestamp - periodStartTime) / 1 days;
return periodStartTime + daysElapsed * 1 days;
}
}
コピー
コピー済み
コピー
コピー済み
/// @notice
Returns to normal state.
/// @notice
Gets the remaining daily minting limit
/// @dev Only callable by the rsETH admin. Contract must be paused
/// @return uint256 The remaining daily minting limit
function unpause() external onlyLRTAdmin {
function remainingDailyMintLimit() external view returns (uint256) {
_unpause();
if (maxMintAmountPerDay == 0) return 0;
// If we're on a new day but no mint has occurred yet, treat currentPeriodMintedAmount as 0
uint256 effectiveDailyMintAmount = (block.timestamp >= periodStartTime + 1 days) ? 0 : currentPeriodMintedAmount;
return maxMintAmountPerDay > effectiveDailyMintAmount ? maxMintAmountPerDay - effectiveDailyMintAmount : 0;
}
/// @notice Returns the timestamp at which the current effective daily minting period ends,
/// accounting for any skipped days during which no minting occurred
/// @dev A mint executed at exactly this timestamp is counted towards the next period's minting limit
/// @return uint256 The timestamp at which the effective current minting period ends
function getNextDailyLimitResetTimestamp() external view returns (uint256) {
return getCurrentPeriodStartTime() + 1 days;
}
/*//////////////////////////////////////////////////////////////
Internal Functions
//////////////////////////////////////////////////////////////*/
/// @dev Override ERC20 `_transfer` to enforce transfer blocks on `from` and `to` addresses
function _transfer(address from, address to, uint256 amount) internal override {
_enforceNotBlocked(from);
_enforceNotBlocked(to);
super._transfer(from, to, amount);
}
/// @dev Reverts if `account` is currently blocked (used for transfers, mints, and burns)
function _enforceNotBlocked(address account) internal {
// Addresses that are permanently exempt can never be blocked
if (isPermanentlyExempt[account]) return;
// Check if the account has an active transfer block
uint256 blockedUntil = transfersBlockedUntil[account];
if (blockedUntil == 0) return;
if (block.timestamp < blockedUntil) revert TransfersBlocked(account, blockedUntil);
// Auto-clean up expired block
delete transfersBlockedUntil[account];
}
/// @dev Internal function to set the custody address
function _setCustodyAddress(address newCustodyAddress) internal {
UtilLib.checkNonZeroAddress(newCustodyAddress);
custodyAddress = newCustodyAddress;
emit CustodyAddressUpdated(newCustodyAddress);
}
}
}
}
コピー
コピー済み
コピー
コピー済み
保存された差分
原文
ファイルを開く
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.27; import { UtilLib } from "./utils/UtilLib.sol"; import { LRTConfigRoleChecker, ILRTConfig, LRTConstants } from "./utils/LRTConfigRoleChecker.sol"; import { ERC20Upgradeable, Initializable } from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol"; import { PausableUpgradeable } from "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol"; /// @title rsETH token Contract /// @author Kelp DAO /// @notice The ERC20 contract for the rsETH token contract RSETH is Initializable, LRTConfigRoleChecker, ERC20Upgradeable, PausableUpgradeable { error DailyMintLimitExceeded(uint256 currentAmount, uint256 maxAmount); event MaxMintAmountPerDayUpdated(uint256 newMaxMintAmountPerDay); uint256 public maxMintAmountPerDay; uint256 public currentPeriodMintedAmount; uint256 public periodStartTime; /// @dev Modifier to check and update daily mint limits /// @param amount The amount to be minted modifier checkDailyMintLimit(uint256 amount) { // Check if we need to reset the period or initialize the period if (periodStartTime == 0 || block.timestamp >= periodStartTime + 1 days) { currentPeriodMintedAmount = 0; periodStartTime = block.timestamp; } // Check if minting would exceed the daily limit if (currentPeriodMintedAmount + amount > maxMintAmountPerDay) { revert DailyMintLimitExceeded(currentPeriodMintedAmount + amount, maxMintAmountPerDay); } currentPeriodMintedAmount += amount; _; } /// @custom:oz-upgrades-unsafe-allow constructor constructor() { _disableInitializers(); } /// @dev Initializes the contract /// @param admin Admin address /// @param lrtConfigAddr LRT config address function initialize(address admin, address lrtConfigAddr) external initializer { UtilLib.checkNonZeroAddress(admin); UtilLib.checkNonZeroAddress(lrtConfigAddr); __ERC20_init("rsETH", "rsETH"); __Pausable_init(); lrtConfig = ILRTConfig(lrtConfigAddr); emit UpdatedLRTConfig(lrtConfigAddr); } /// @notice Sets the maximum amount that can be minted in 24 hours /// @param _maxMintAmountPerDay The maximum amount that can be minted in 24 hours function setMaxMintAmountPerDay(uint256 _maxMintAmountPerDay) external onlyLRTManager { maxMintAmountPerDay = _maxMintAmountPerDay; emit MaxMintAmountPerDayUpdated(_maxMintAmountPerDay); } /// @notice Mints rsETH when called by an authorized caller /// @param to the account to mint to /// @param amount the amount of rsETH to mint function mint( address to, uint256 amount ) external onlyRole(LRTConstants.MINTER_ROLE) whenNotPaused checkDailyMintLimit(amount) { _mint(to, amount); } /// @notice Burns rsETH when called by an authorized caller /// @param account the account to burn from /// @param amount the amount of rsETH to burn function burnFrom(address account, uint256 amount) external onlyRole(LRTConstants.BURNER_ROLE) whenNotPaused { _burn(account, amount); } /// @dev Triggers stopped state. /// @dev Only callable by LRT config manager. Contract must NOT be paused. function pause() external onlyLRTManager { _pause(); } /// @notice Returns to normal state. /// @dev Only callable by the rsETH admin. Contract must be paused function unpause() external onlyLRTAdmin { _unpause(); } }
変更されたテキスト
ファイルを開く
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.27; import { UtilLib } from "./utils/UtilLib.sol"; import { LRTConfigRoleChecker, ILRTConfig, LRTConstants } from "./utils/LRTConfigRoleChecker.sol"; import { ERC20Upgradeable, Initializable } from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol"; import { PausableUpgradeable } from "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol"; /// @title rsETH token Contract /// @author Kelp DAO /// @notice The ERC20 contract for the rsETH token contract RSETH is Initializable, LRTConfigRoleChecker, ERC20Upgradeable, PausableUpgradeable { /*////////////////////////////////////////////////////////////// State Variables //////////////////////////////////////////////////////////////*/ /// @notice Maximum amount that can be minted in a 24-hour period uint256 public maxMintAmountPerDay; /// @notice Amount minted in the current 24-hour period uint256 public currentPeriodMintedAmount; /// @notice Start time of the current 24-hour period uint256 public periodStartTime; /// @notice Address to which recovered funds are sent address public custodyAddress; /// @dev If > 0, transfers TO or FROM this address are blocked until timestamp (24h block) mapping(address account => uint256 blockedUntil) public transfersBlockedUntil; /// @dev Permanently exempt addresses mapping (rsETH transfers for these can never be blocked) mapping(address account => bool isExempt) public isPermanentlyExempt; /*////////////////////////////////////////////////////////////// Modifiers //////////////////////////////////////////////////////////////*/ /// @dev Modifier to check and update daily mint limits /// @param amount The amount to be minted modifier checkDailyMintLimit(uint256 amount) { // Check if we need to reset the period if it has been more than 24 hours if (block.timestamp >= periodStartTime + 1 days) { currentPeriodMintedAmount = 0; periodStartTime = getCurrentPeriodStartTime(); } // Check if minting would exceed the daily limit if (currentPeriodMintedAmount + amount > maxMintAmountPerDay) { revert DailyMintLimitExceeded(currentPeriodMintedAmount + amount, maxMintAmountPerDay); } currentPeriodMintedAmount += amount; _; } /*////////////////////////////////////////////////////////////// Custom Errors //////////////////////////////////////////////////////////////*/ error PeriodStartTimeShouldBeWithin24Hours(); error DailyMintLimitExceeded(uint256 currentAmount, uint256 maxAmount); error TransfersBlocked(address account, uint256 blockedUntil); error CannotPermanentlyExemptBlockedAddress(address account, uint256 blockedUntil); error AddressPermanentlyExempt(address account); error NoActiveTransferBlock(address account); /*////////////////////////////////////////////////////////////// Events //////////////////////////////////////////////////////////////*/ event MaxMintAmountPerDayUpdated(uint256 newMaxMintAmountPerDay); event FrozenFundsRecovered(address indexed from, address indexed to, uint256 amount); event UserTransfersBlocked(address indexed user, uint256 until); event PermanentExemptionAdded(address indexed account); event CustodyAddressUpdated(address indexed newCustodyAddress); event PeriodStartTimeSet(uint256 newPeriodStartTime); /*////////////////////////////////////////////////////////////// Constructor //////////////////////////////////////////////////////////////*/ /// @custom:oz-upgrades-unsafe-allow constructor constructor() { _disableInitializers(); } /*////////////////////////////////////////////////////////////// Initializers //////////////////////////////////////////////////////////////*/ /// @dev Initializes the contract /// @param admin Admin address /// @param lrtConfigAddr LRT config address function initialize(address admin, address lrtConfigAddr) external initializer { UtilLib.checkNonZeroAddress(admin); UtilLib.checkNonZeroAddress(lrtConfigAddr); __ERC20_init("rsETH", "rsETH"); __Pausable_init(); lrtConfig = ILRTConfig(lrtConfigAddr); emit UpdatedLRTConfig(lrtConfigAddr); } /// @notice Initializes the contract with a period start time and the custody address /// @param _periodStartTime The period start time /// @param _custodyAddress The custody address for recovered funds function reinitialize(uint256 _periodStartTime, address _custodyAddress) external reinitializer(2) onlyLRTManager { if (_periodStartTime > block.timestamp || _periodStartTime <= block.timestamp - 1 days) { revert PeriodStartTimeShouldBeWithin24Hours(); } periodStartTime = _periodStartTime; emit PeriodStartTimeSet(_periodStartTime); _setCustodyAddress(_custodyAddress); } /*////////////////////////////////////////////////////////////// Manager Functions //////////////////////////////////////////////////////////////*/ /// @notice Sets the maximum amount that can be minted in 24 hours /// @param _maxMintAmountPerDay The maximum amount that can be minted in 24 hours function setMaxMintAmountPerDay(uint256 _maxMintAmountPerDay) external onlyLRTManager { maxMintAmountPerDay = _maxMintAmountPerDay; emit MaxMintAmountPerDayUpdated(_maxMintAmountPerDay); } /// @notice Permanently add accounts to the exempted list (non-reversible) /// @param accounts Accounts to mark as permanently exempt (cannot have transfers blocked) function addPermanentExemptions(address[] calldata accounts) external onlyLRTManager { uint256 length = accounts.length; for (uint256 i = 0; i < length; ++i) { address account = accounts[i]; UtilLib.checkNonZeroAddress(account); // Ensure the account is not currently blocked uint256 blockedUntil = transfersBlockedUntil[account]; if (blockedUntil != 0) { if (block.timestamp < blockedUntil) { revert CannotPermanentlyExemptBlockedAddress(account, blockedUntil); } // Auto-clean up expired block delete transfersBlockedUntil[account]; } if (!isPermanentlyExempt[account]) { isPermanentlyExempt[account] = true; emit PermanentExemptionAdded(account); } } } /// @notice Block transfers TO and FROM given users for 24 hours /// @dev Re-applying the block before expiry refreshes the hold to `block.timestamp + 1 days` /// (i.e. not cumulative; never more than 24h from the latest call). Exempt addresses cannot be blocked. /// Emits {UserTransfersBlocked} only when the timestamp changes. /// @param accounts Addresses to block. function blockUserTransfers(address[] calldata accounts) external onlyLRTManager { uint256 blockedUntil = block.timestamp + 1 days; uint256 length = accounts.length; for (uint256 i = 0; i < length; ++i) { address account = accounts[i]; if (isPermanentlyExempt[account] || account == address(0)) continue; uint256 prevBlockedUntil = transfersBlockedUntil[account]; if (blockedUntil != prevBlockedUntil) { transfersBlockedUntil[account] = blockedUntil; emit UserTransfersBlocked(account, blockedUntil); } } } /*////////////////////////////////////////////////////////////// Pause Management //////////////////////////////////////////////////////////////*/ /// @dev Triggers stopped state. Contract must not be paused. function pause() external onlyRole(LRTConstants.PAUSER_ROLE) { _pause(); } /// @dev Returns to normal state. Contract must be paused. function unpause() external onlyLRTAdmin { _unpause(); } /*////////////////////////////////////////////////////////////// Admin Functions //////////////////////////////////////////////////////////////*/ /// @notice Sets the custody address for recovered funds /// @param newCustodyAddress The new custody address function setCustodyAddress(address newCustodyAddress) external onlyLRTAdmin { _setCustodyAddress(newCustodyAddress); } /// @notice Recover the entire balance from a currently blocked, non-exempt address to a designated custody address /// @dev Only callable by LRT admin. Works only while the block is active. /// Emits {FrozenFundsRecovered} even if the recovered amount is zero (for transparency and completeness). function recoverFrozenFunds(address from) external onlyLRTAdmin { UtilLib.checkNonZeroAddress(from); UtilLib.checkNonZeroAddress(custodyAddress); if (isPermanentlyExempt[from]) revert AddressPermanentlyExempt(from); uint256 blockedUntil = transfersBlockedUntil[from]; if (blockedUntil == 0 || block.timestamp >= blockedUntil) revert NoActiveTransferBlock(from); uint256 accountBalance = balanceOf(from); // Bypass transfer block enforcement when transferring to custody address super._transfer(from, custodyAddress, accountBalance); emit FrozenFundsRecovered(from, custodyAddress, accountBalance); } /*////////////////////////////////////////////////////////////// Mint & Burn //////////////////////////////////////////////////////////////*/ /// @notice Mints rsETH when called by an authorized caller /// @param to the account to mint to /// @param amount the amount of rsETH to mint function mint( address to, uint256 amount ) external onlyRole(LRTConstants.MINTER_ROLE) whenNotPaused checkDailyMintLimit(amount) { _enforceNotBlocked(to); _mint(to, amount); } /// @notice Burns rsETH when called by an authorized caller /// @param account the account to burn from /// @param amount the amount of rsETH to burn function burnFrom(address account, uint256 amount) external onlyRole(LRTConstants.BURNER_ROLE) whenNotPaused { _enforceNotBlocked(account); _burn(account, amount); } /*////////////////////////////////////////////////////////////// View Functions //////////////////////////////////////////////////////////////*/ /// @notice Returns the aligned start timestamp of the effective current 24-hour minting period, accounting for any /// possibly skipped days /// @return uint256 The start timestamp of the effective current 24-hour period function getCurrentPeriodStartTime() public view returns (uint256) { // Calculate the full (complete) days elapsed since the period start time (floors the result) uint256 daysElapsed = (block.timestamp - periodStartTime) / 1 days; return periodStartTime + daysElapsed * 1 days; } /// @notice Gets the remaining daily minting limit /// @return uint256 The remaining daily minting limit function remainingDailyMintLimit() external view returns (uint256) { if (maxMintAmountPerDay == 0) return 0; // If we're on a new day but no mint has occurred yet, treat currentPeriodMintedAmount as 0 uint256 effectiveDailyMintAmount = (block.timestamp >= periodStartTime + 1 days) ? 0 : currentPeriodMintedAmount; return maxMintAmountPerDay > effectiveDailyMintAmount ? maxMintAmountPerDay - effectiveDailyMintAmount : 0; } /// @notice Returns the timestamp at which the current effective daily minting period ends, /// accounting for any skipped days during which no minting occurred /// @dev A mint executed at exactly this timestamp is counted towards the next period's minting limit /// @return uint256 The timestamp at which the effective current minting period ends function getNextDailyLimitResetTimestamp() external view returns (uint256) { return getCurrentPeriodStartTime() + 1 days; } /*////////////////////////////////////////////////////////////// Internal Functions //////////////////////////////////////////////////////////////*/ /// @dev Override ERC20 `_transfer` to enforce transfer blocks on `from` and `to` addresses function _transfer(address from, address to, uint256 amount) internal override { _enforceNotBlocked(from); _enforceNotBlocked(to); super._transfer(from, to, amount); } /// @dev Reverts if `account` is currently blocked (used for transfers, mints, and burns) function _enforceNotBlocked(address account) internal { // Addresses that are permanently exempt can never be blocked if (isPermanentlyExempt[account]) return; // Check if the account has an active transfer block uint256 blockedUntil = transfersBlockedUntil[account]; if (blockedUntil == 0) return; if (block.timestamp < blockedUntil) revert TransfersBlocked(account, blockedUntil); // Auto-clean up expired block delete transfersBlockedUntil[account]; } /// @dev Internal function to set the custody address function _setCustodyAddress(address newCustodyAddress) internal { UtilLib.checkNonZeroAddress(newCustodyAddress); custodyAddress = newCustodyAddress; emit CustodyAddressUpdated(newCustodyAddress); } }
違いを見つける