Skip to content

Commit

Permalink
fix: reentrant guards (#1107)
Browse files Browse the repository at this point in the history
**Motivation:**

Concerns about reentrancy in the DelegationManager and interactions of
completed withdrawals which can call untrusted ERC20 transfers

**Modifications:**

Added reentrant guards across external functions

**Result:**

Preventing cross-function reentrancy in the DelegationManager

---------

Co-authored-by: wadealexc <[email protected]>
  • Loading branch information
8sunyuan and wadealexc authored Feb 14, 2025
1 parent 067010c commit 81fef63
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 21 deletions.
19 changes: 11 additions & 8 deletions src/contracts/core/DelegationManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ contract DelegationManager is
address initDelegationApprover,
uint32 allocationDelay,
string calldata metadataURI
) external {
) external nonReentrant {
require(!isDelegated(msg.sender), ActivelyDelegated());

allocationManager.setAllocationDelay(msg.sender, allocationDelay);
Expand All @@ -110,7 +110,10 @@ contract DelegationManager is
}

/// @inheritdoc IDelegationManager
function modifyOperatorDetails(address operator, address newDelegationApprover) external checkCanCall(operator) {
function modifyOperatorDetails(
address operator,
address newDelegationApprover
) external checkCanCall(operator) nonReentrant {
require(isOperator(operator), OperatorNotRegistered());
_setDelegationApprover(operator, newDelegationApprover);
}
Expand All @@ -126,7 +129,7 @@ contract DelegationManager is
address operator,
SignatureWithExpiry memory approverSignatureAndExpiry,
bytes32 approverSalt
) public {
) public nonReentrant {
require(!isDelegated(msg.sender), ActivelyDelegated());
require(isOperator(operator), OperatorNotRegistered());

Expand All @@ -145,7 +148,7 @@ contract DelegationManager is
/// @inheritdoc IDelegationManager
function undelegate(
address staker
) public returns (bytes32[] memory withdrawalRoots) {
) public nonReentrant returns (bytes32[] memory withdrawalRoots) {
// Check that the `staker` can undelegate
require(isDelegated(staker), NotActivelyDelegated());
require(!isOperator(staker), OperatorsCannotUndelegate());
Expand Down Expand Up @@ -176,7 +179,7 @@ contract DelegationManager is
/// @inheritdoc IDelegationManager
function queueWithdrawals(
QueuedWithdrawalParams[] calldata params
) external onlyWhenNotPaused(PAUSED_ENTER_WITHDRAWAL_QUEUE) returns (bytes32[] memory) {
) external onlyWhenNotPaused(PAUSED_ENTER_WITHDRAWAL_QUEUE) nonReentrant returns (bytes32[] memory) {
bytes32[] memory withdrawalRoots = new bytes32[](params.length);
address operator = delegatedTo[msg.sender];

Expand Down Expand Up @@ -228,7 +231,7 @@ contract DelegationManager is
IStrategy strategy,
uint256 prevDepositShares,
uint256 addedShares
) external onlyStrategyManagerOrEigenPodManager {
) external onlyStrategyManagerOrEigenPodManager nonReentrant {
/// Note: Unlike `decreaseDelegatedShares`, we don't return early if the staker has no operator.
/// This is because `_increaseDelegation` updates the staker's deposit scaling factor, which we
/// need to do even if not delegated.
Expand All @@ -252,7 +255,7 @@ contract DelegationManager is
address staker,
uint256 curDepositShares,
uint64 beaconChainSlashingFactorDecrease
) external onlyEigenPodManager {
) external onlyEigenPodManager nonReentrant {
if (!isDelegated(staker)) {
return;
}
Expand Down Expand Up @@ -282,7 +285,7 @@ contract DelegationManager is
IStrategy strategy,
uint64 prevMaxMagnitude,
uint64 newMaxMagnitude
) external onlyAllocationManager {
) external onlyAllocationManager nonReentrant {
/// forgefmt: disable-next-item
uint256 operatorSharesSlashed = SlashingLib.calcSlashedAmount({
operatorShares: operatorShares[operator][strategy],
Expand Down
17 changes: 10 additions & 7 deletions src/contracts/core/StrategyManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ contract StrategyManager is
address staker,
IStrategy strategy,
uint256 depositSharesToRemove
) external onlyDelegationManager returns (uint256) {
) external onlyDelegationManager nonReentrant returns (uint256) {
(, uint256 sharesAfter) = _removeDepositShares(staker, strategy, depositSharesToRemove);
return sharesAfter;
}
Expand All @@ -127,7 +127,7 @@ contract StrategyManager is
address staker,
IStrategy strategy,
uint256 shares
) external onlyDelegationManager returns (uint256, uint256) {
) external onlyDelegationManager nonReentrant returns (uint256, uint256) {
return _addShares(staker, strategy, shares);
}

Expand All @@ -137,12 +137,15 @@ contract StrategyManager is
IStrategy strategy,
IERC20 token,
uint256 shares
) external onlyDelegationManager {
) external onlyDelegationManager nonReentrant {
strategy.withdraw(staker, token, shares);
}

/// @inheritdoc IShareManager
function increaseBurnableShares(IStrategy strategy, uint256 addedSharesToBurn) external onlyDelegationManager {
function increaseBurnableShares(
IStrategy strategy,
uint256 addedSharesToBurn
) external onlyDelegationManager nonReentrant {
(, uint256 currentShares) = EnumerableMap.tryGet(burnableShares, address(strategy));
EnumerableMap.set(burnableShares, address(strategy), currentShares + addedSharesToBurn);
emit BurnableSharesIncreased(strategy, addedSharesToBurn);
Expand All @@ -162,14 +165,14 @@ contract StrategyManager is
/// @inheritdoc IStrategyManager
function setStrategyWhitelister(
address newStrategyWhitelister
) external onlyOwner {
) external onlyOwner nonReentrant {
_setStrategyWhitelister(newStrategyWhitelister);
}

/// @inheritdoc IStrategyManager
function addStrategiesToDepositWhitelist(
IStrategy[] calldata strategiesToWhitelist
) external onlyStrategyWhitelister {
) external onlyStrategyWhitelister nonReentrant {
uint256 strategiesToWhitelistLength = strategiesToWhitelist.length;
for (uint256 i = 0; i < strategiesToWhitelistLength; ++i) {
// change storage and emit event only if strategy is not already in whitelist
Expand All @@ -183,7 +186,7 @@ contract StrategyManager is
/// @inheritdoc IStrategyManager
function removeStrategiesFromDepositWhitelist(
IStrategy[] calldata strategiesToRemoveFromWhitelist
) external onlyStrategyWhitelister {
) external onlyStrategyWhitelister nonReentrant {
uint256 strategiesToRemoveFromWhitelistLength = strategiesToRemoveFromWhitelist.length;
for (uint256 i = 0; i < strategiesToRemoveFromWhitelistLength; ++i) {
// change storage and emit event only if strategy is already in whitelist
Expand Down
12 changes: 6 additions & 6 deletions src/contracts/pods/EigenPodManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ contract EigenPodManager is
}

/// @inheritdoc IEigenPodManager
function createPod() external onlyWhenNotPaused(PAUSED_NEW_EIGENPODS) returns (address) {
function createPod() external onlyWhenNotPaused(PAUSED_NEW_EIGENPODS) nonReentrant returns (address) {
require(!hasPod(msg.sender), EigenPodAlreadyExists());
// deploy a pod if the sender doesn't have one already
IEigenPod pod = _deployPod();
Expand All @@ -79,7 +79,7 @@ contract EigenPodManager is
bytes calldata pubkey,
bytes calldata signature,
bytes32 depositDataRoot
) external payable onlyWhenNotPaused(PAUSED_NEW_EIGENPODS) {
) external payable onlyWhenNotPaused(PAUSED_NEW_EIGENPODS) nonReentrant {
IEigenPod pod = ownerToPod[msg.sender];
if (address(pod) == address(0)) {
//deploy a pod if the sender doesn't have one already
Expand Down Expand Up @@ -148,7 +148,7 @@ contract EigenPodManager is
address staker,
IStrategy strategy,
uint256 depositSharesToRemove
) external onlyDelegationManager returns (uint256) {
) external onlyDelegationManager nonReentrant returns (uint256) {
require(strategy == beaconChainETHStrategy, InvalidStrategy());
int256 updatedShares = podOwnerDepositShares[staker] - int256(depositSharesToRemove);
require(updatedShares >= 0, SharesNegative());
Expand All @@ -169,7 +169,7 @@ contract EigenPodManager is
address staker,
IStrategy strategy,
uint256 shares
) external onlyDelegationManager returns (uint256, uint256) {
) external onlyDelegationManager nonReentrant returns (uint256, uint256) {
require(strategy == beaconChainETHStrategy, InvalidStrategy());
return _addShares(staker, shares);
}
Expand All @@ -185,7 +185,7 @@ contract EigenPodManager is
IStrategy strategy,
IERC20,
uint256 shares
) external onlyDelegationManager {
) external onlyDelegationManager nonReentrant {
require(strategy == beaconChainETHStrategy, InvalidStrategy());
require(staker != address(0), InputAddressZero());
require(int256(shares) > 0, SharesNegative());
Expand Down Expand Up @@ -226,7 +226,7 @@ contract EigenPodManager is
}

/// @inheritdoc IShareManager
function increaseBurnableShares(IStrategy, uint256 addedSharesToBurn) external onlyDelegationManager {
function increaseBurnableShares(IStrategy, uint256 addedSharesToBurn) external onlyDelegationManager nonReentrant {
burnableETHShares += addedSharesToBurn;
emit BurnableETHSharesIncreased(addedSharesToBurn);
}
Expand Down

0 comments on commit 81fef63

Please sign in to comment.