-
Notifications
You must be signed in to change notification settings - Fork 55
P0 security hardening for #385: oracle price-move cap + restaker pause #445
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: yash/cleanup-tasks
Are you sure you want to change the base?
Changes from all commits
8f66726
4af869d
df243bd
dfac525
9954e56
019e84f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -12,6 +12,7 @@ import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; | |
| import "@etherfi/deposits/Liquifier.sol"; | ||
| import "@etherfi/core/LiquidityPool.sol"; | ||
| import "@etherfi/governance/utils/RolesLibrary.sol"; | ||
| import "@etherfi/governance/utils/PausableUntil.sol"; | ||
|
|
||
| import "@etherfi/eigenlayer-interfaces/IStrategyManager.sol"; | ||
| import "@etherfi/eigenlayer-interfaces/IDelegationManager.sol"; | ||
|
|
@@ -21,7 +22,7 @@ import "@etherfi/deposits/interfaces/ILiquifier.sol"; | |
| import "@etherfi/core/interfaces/ILiquidityPool.sol"; | ||
| import "@etherfi/governance/rate-limiting/interfaces/IEtherFiRateLimiter.sol"; | ||
|
|
||
| contract EtherFiRestaker is Initializable, UUPSUpgradeable, OwnableUpgradeable, PausableUpgradeable, RolesLibrary { | ||
| contract EtherFiRestaker is Initializable, UUPSUpgradeable, OwnableUpgradeable, PausableUpgradeable, PausableUntil, RolesLibrary { | ||
| using SafeERC20 for IERC20; | ||
| using EnumerableSet for EnumerableSet.Bytes32Set; | ||
|
|
||
|
|
@@ -143,7 +144,7 @@ contract EtherFiRestaker is Initializable, UUPSUpgradeable, OwnableUpgradeable, | |
| /// @notice Transfer stETH to a recipient for instant withdrawal | ||
| /// @param recipient The address to receive stETH | ||
| /// @param amount The amount of stETH to transfer | ||
| function transferStETH(address recipient, uint256 amount) external { | ||
| function transferStETH(address recipient, uint256 amount) external whenNotPaused { | ||
| if(msg.sender != etherFiRedemptionManager) revert IncorrectCaller(); | ||
| if (amount > lido.balanceOf(address(this))) revert InsufficientBalance(); | ||
| IERC20(address(lido)).safeTransfer(recipient, amount); | ||
|
|
@@ -158,7 +159,7 @@ contract EtherFiRestaker is Initializable, UUPSUpgradeable, OwnableUpgradeable, | |
|
|
||
| /// @notice Request for a specific amount of stETH holdings | ||
| /// @param _amount the amount of stETH to request | ||
| function stEthRequestWithdrawal(uint256 _amount) public onlyExecutorOperations returns (uint256[] memory) { | ||
| function stEthRequestWithdrawal(uint256 _amount) public onlyExecutorOperations whenNotPaused returns (uint256[] memory) { | ||
| rateLimiter.consume(STETH_REQUEST_WITHDRAWAL_LIMIT_ID, _amountToGwei(_amount)); | ||
|
|
||
| uint256 minAmount = lidoWithdrawalQueue.MIN_STETH_WITHDRAWAL_AMOUNT(); | ||
|
|
@@ -192,7 +193,7 @@ contract EtherFiRestaker is Initializable, UUPSUpgradeable, OwnableUpgradeable, | |
| /// @notice Claim a batch of withdrawal requests if they are finalized sending the ETH to the this contract back | ||
| /// @param _requestIds array of request ids to claim | ||
| /// @param _hints checkpoint hint for each id. Can be obtained with `findCheckpointHints()` | ||
| function stEthClaimWithdrawals(uint256[] calldata _requestIds, uint256[] calldata _hints) external onlyHousekeepingOperations { | ||
| function stEthClaimWithdrawals(uint256[] calldata _requestIds, uint256[] calldata _hints) external onlyHousekeepingOperations whenNotPaused { | ||
| lidoWithdrawalQueue.claimWithdrawals(_requestIds, _hints); | ||
|
|
||
| _withdrawEther(); | ||
|
|
@@ -201,7 +202,7 @@ contract EtherFiRestaker is Initializable, UUPSUpgradeable, OwnableUpgradeable, | |
| } | ||
|
|
||
| // Send the ETH back to the liquidity pool | ||
| function withdrawEther() public onlyHousekeepingOperations { | ||
| function withdrawEther() public onlyHousekeepingOperations whenNotPaused { | ||
| _withdrawEther(); | ||
| } | ||
|
|
||
|
|
@@ -230,7 +231,7 @@ contract EtherFiRestaker is Initializable, UUPSUpgradeable, OwnableUpgradeable, | |
| } | ||
|
|
||
| // undelegate from the current AVS operator & un-restake all | ||
| function undelegate() external onlyOperatingMultisig returns (bytes32[] memory) { | ||
| function undelegate() external onlyOperatingMultisig whenNotPaused returns (bytes32[] memory) { | ||
| bytes32[] memory withdrawalRoots = eigenLayerDelegationManager.undelegate(address(this)); | ||
|
|
||
| for (uint256 i = 0; i < withdrawalRoots.length; i++) { | ||
|
|
@@ -245,7 +246,7 @@ contract EtherFiRestaker is Initializable, UUPSUpgradeable, OwnableUpgradeable, | |
| } | ||
|
|
||
| // deposit the token in holding into the restaking strategy | ||
| function depositIntoStrategy(address token, uint256 amount) external onlyExecutorOperations returns (uint256) { | ||
| function depositIntoStrategy(address token, uint256 amount) external onlyExecutorOperations whenNotPaused returns (uint256) { | ||
| rateLimiter.consume(DEPOSIT_INTO_STRATEGY_LIMIT_ID, _amountToGwei(amount)); | ||
|
|
||
| IERC20(token).safeIncreaseAllowance(address(eigenLayerStrategyManager), amount); | ||
|
|
@@ -260,7 +261,7 @@ contract EtherFiRestaker is Initializable, UUPSUpgradeable, OwnableUpgradeable, | |
| /// Made easy for operators | ||
| /// @param token the token to withdraw | ||
| /// @param amount the amount of token to withdraw | ||
| function queueWithdrawals(address token, uint256 amount) public onlyExecutorOperations returns (bytes32[] memory) { | ||
| function queueWithdrawals(address token, uint256 amount) public onlyExecutorOperations whenNotPaused returns (bytes32[] memory) { | ||
| rateLimiter.consume(QUEUE_WITHDRAWALS_LIMIT_ID, _amountToGwei(amount)); | ||
|
|
||
| uint256 shares = getEigenLayerRestakingStrategy(token).underlyingToSharesView(amount); | ||
|
|
@@ -280,7 +281,7 @@ contract EtherFiRestaker is Initializable, UUPSUpgradeable, OwnableUpgradeable, | |
| function completeQueuedWithdrawals( | ||
| IDelegationManager.Withdrawal[] memory _queuedWithdrawals, | ||
| IERC20[][] memory _tokens | ||
| ) external onlyHousekeepingOperations { | ||
| ) external onlyHousekeepingOperations whenNotPaused { | ||
| uint256 num = _queuedWithdrawals.length; | ||
| bool[] memory receiveAsTokens = new bool[](num); | ||
| for (uint256 i = 0; i < num; i++) { | ||
|
|
@@ -397,16 +398,42 @@ contract EtherFiRestaker is Initializable, UUPSUpgradeable, OwnableUpgradeable, | |
| return total; | ||
| } | ||
|
|
||
| // Pauses the contract | ||
| // Boolean pause (multisig). Indefinite until the multisig unpauses. | ||
| function pauseContract() external onlyOperatingMultisig { | ||
| _pause(); | ||
| } | ||
|
|
||
| // Unpauses the contract | ||
| function unPauseContract() external onlyOperatingMultisig { | ||
| // Unpauses the boolean pause (deliberate, multisig-only) | ||
| function unpauseContract() external onlyOperatingMultisig { | ||
| _unpause(); | ||
| } | ||
|
|
||
| /// @notice Fast, auto-expiring emergency halt callable by the Guardian (Hypernative / | ||
| /// EOA keys). Mirrors the protocol-wide halt model (tokens, LP, redemption): | ||
| /// the Guardian can stop fund movement immediately without assembling the | ||
| /// multisig, the pause auto-expires (8h–30d) with a per-pauser cooldown so a | ||
| /// wrong/compromised halt self-heals, and resume stays deliberate (multisig). | ||
| function pauseContractUntil() external onlyGuardian { | ||
| _pauseUntil(); | ||
| } | ||
|
|
||
| function unpauseContractUntil() external onlyOperatingMultisig { | ||
| _unpauseUntil(); | ||
| } | ||
|
|
||
| function setPauseUntilDuration(uint256 _pauseUntilDuration) external onlyAdmin { | ||
| _setPauseUntilDuration(_pauseUntilDuration); | ||
| } | ||
|
|
||
| /// @dev Fold the auto-expiring PausableUntil check into OZ's `_requireNotPaused`, so | ||
| /// the standard `whenNotPaused` modifier (used on every fund-flow fn) halts on | ||
| /// EITHER the boolean pause or the Guardian's auto-expiring pause — consistent | ||
| /// with the rest of the protocol, no bespoke modifier needed. | ||
| function _requireNotPaused() internal view override { | ||
| _requireNotPausedUntil(); | ||
| super._requireNotPaused(); | ||
| } | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Override blocks boolean pause while PausableUntil is activeMedium Severity The Reviewed by Cursor Bugbot for commit 019e84f. Configure here. |
||
|
|
||
| // INTERNAL functions | ||
|
|
||
| /// @dev Convert wei to gwei for rate-limiter buckets, with overflow check. | ||
|
|
||


Uh oh!
There was an error while loading. Please reload this page.