Diff
checker
Text
Text
Images
Documents
Excel
Folders
Legal
Enterprise
Desktop
Pricing
Sign in
Download Diffchecker Desktop
Compare text
Find the difference between two text files
Tools
History
Display
Layout
Split
Unified
Real-time editor
Hide whitespace changes
Hide unchanged lines
Disable line wrap
Diff precision
Smart
Syntax
None
Change appearance
Process
Ignore
Transform text
Go to first change
Edit input
Diffchecker Desktop
The most secure way to run Diffchecker. Get the Diffchecker Desktop app: your diffs never leave your computer!
Get Desktop
RSETH
Created
10 months ago
Diff never expires
Clear
Export
Share
Explain
14 removals
Lines
Total
Removed
Characters
Total
Removed
To continue using this feature, upgrade to
Diff
checker
Pro
View Pricing
98 lines
Copy
230 additions
Lines
Total
Added
Characters
Total
Added
To continue using this feature, upgrade to
Diff
checker
Pro
View Pricing
315 lines
Copy
// 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 {
Copy
Copied
Copy
Copied
error DailyMintLimitExceeded(uint256 currentAmount, uint256 maxAmount);
/*//////////////////////////////////////////////////////////////
State Variables
event MaxMintAmountPerDayUpdated(uint256 newMaxMintAmountPerDay);
//////////////////////////////////////////////////////////////*/
Copy
Copied
Copy
Copied
/// @notice Maximum amount that can be minted in a 24-hour period
uint256 public maxMintAmountPerDay;
uint256 public maxMintAmountPerDay;
Copy
Copied
Copy
Copied
/// @notice Amount minted in the current 24-hour period
uint256 public currentPeriodMintedAmount;
uint256 public currentPeriodMintedAmount;
Copy
Copied
Copy
Copied
/// @notice Start time of the current 24-hour period
uint256 public periodStartTime;
uint256 public periodStartTime;
Copy
Copied
Copy
Copied
/// @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) {
Copy
Copied
Copy
Copied
// 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;
Copy
Copied
Copy
Copied
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;
_;
_;
}
}
Copy
Copied
Copy
Copied
/*//////////////////////////////////////////////////////////////
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();
}
}
Copy
Copied
Copy
Copied
/*//////////////////////////////////////////////////////////////
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);
}
}
Copy
Copied
Copy
Copied
/// @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);
}
}
Copy
Copied
Copy
Copied
/// @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)
{
{
Copy
Copied
Copy
Copied
_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 {
Copy
Copied
Copy
Copied
_enforceNotBlocked(account);
_burn(account, amount);
_burn(account, amount);
}
}
Copy
Copied
Copy
Copied
/// @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;
}
}
Copy
Copied
Copy
Copied
/// @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);
}
}
}
}
Copy
Copied
Copy
Copied
Saved diffs
Original text
Open file
// 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(); } }
Changed text
Open file
// 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); } }
Find difference