Skip to content

yash/fix/nethermind-audit#419

Open
0xpanicError wants to merge 31 commits into
pankaj/feat/security-upgradesfrom
yash/fix/nethermind-audit
Open

yash/fix/nethermind-audit#419
0xpanicError wants to merge 31 commits into
pankaj/feat/security-upgradesfrom
yash/fix/nethermind-audit

Conversation

@0xpanicError
Copy link
Copy Markdown

@0xpanicError 0xpanicError commented May 21, 2026

Note

High Risk
Touches core protocol paths (oracle report handling, withdrawal finalization/claiming, and liquidity accounting) and changes access control for asset recovery, so mistakes could impact redemption safety or governance permissions despite added guardrails/tests.

Overview
Hardens several core protocol flows around oracle reporting and withdrawals. EtherFiAdmin.finalizeWithdrawalsWhenStale now keys off EtherFiOracle.lastPublishedReportRefBlock(), adds a cooldown, caps the number of requests finalized per call/report, and switches liquidity checks to liquidityPool.totalValueInLp(); report validation also adds the same request-range cap and updates math to use Math helpers.

Withdrawal mechanics are adjusted for the escrow-migration model: LiquidityPool replaces fixed MIN/MAX withdrawal constants with operator-set minWithdrawAmount/maxWithdrawAmount, and WithdrawRequestNFT removes fee handling from requests/claims, tightens escrow accounting, adds a small tolerance buffer when validating outputs, gates fulfillRequests on escrowMigrationCompleted, and restricts seizeInvalidRequest to the upgrade timelock.

Across the codebase, ERC20 interactions are made safer via SafeERC20 (e.g., DepositAdapter, WeETH, MembershipManager, EtherFiRestaker), multiple ETH forwarding sites increase gas stipends to a shared no-grief constant, and BucketRateLimiter switches to ceilDiv with an explicit RATE_PRECISION. Finally, CumulativeMerkleRewardsDistributor gains receive() plus admin-only recoverETH/recoverERC20, and EETH/WeETH recovery functions are tightened from operations to admin; migration-only initializers are removed from AuctionManager and NodeOperatorManager.

Reviewed by Cursor Bugbot for commit 449fa98. Bugbot is set up for automated code reviews on this repo. Configure here.

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Credits must be used to enable repository wide code reviews.

Comment thread src/WithdrawRequestNFT.sol
Comment thread src/EtherFiAdmin.sol Outdated
Comment thread src/EtherFiOracle.sol
Comment thread src/EtherFiAdmin.sol Outdated
Comment thread src/BucketRateLimiter.sol Outdated
Comment thread src/CumulativeMerkleRewardsDistributor.sol Outdated
Comment thread src/EarlyAdopterPool.sol Outdated
Comment thread src/EtherFiAdmin.sol Outdated
Comment thread src/StakingManager.sol Outdated
Comment thread src/StakingManager.sol
Comment thread src/WithdrawRequestNFT.sol Outdated
Comment thread src/EtherFiAdmin.sol Outdated
Comment thread src/AuctionManager.sol
Comment thread src/BucketRateLimiter.sol Outdated
Comment thread src/EtherFiAdmin.sol Outdated
Comment thread src/EtherFiAdmin.sol
// valdate finalized request id
uint32 lastFinalizedRequestId = withdrawRequestNft.lastFinalizedRequestId();
if (_report.lastFinalizedWithdrawalRequestId < lastFinalizedRequestId) return (false, "EtherFiAdmin: finalized withdrawal request id is less than last finalized request id");
if (_report.lastFinalizedWithdrawalRequestId - lastFinalizedRequestId > maxNumberOfRequestsToFinalizePerReport) return (false, "EtherFiAdmin: number of requests to finalize exceeds max");
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is this for? is this useful?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to prevent a griefing scenario. in oracle sanity checks, added a loop to confirm amount in each request sum equals total amount, need to ensure each reprot doesnt have too many requests else can be gas griefed. practical value could something like 2-5k

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why would the attack work? do we have O(N) component in withdrawal processing? are we scanning all requests?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

at time of finalization we loop over all requests to finalize (to sanity check individual balances sum equal total withdraw amount to finalize), that adds O(N) complexity so need this check to prevent griefing.

Comment thread src/EtherFiOracle.sol
Comment thread src/EtherFiRestaker.sol
Comment thread src/EtherFiRestaker.sol
Comment thread src/LiquidityPool.sol Outdated
Comment thread src/LiquidityPool.sol Outdated

// liquidity pool supplies 1 eth per validator
uint256 outboundEthAmountFromLp = 1 ether * _bidIds.length;
uint256 outboundEthAmountFromLp = LP_INITIAL_VALIDATOR_DEPOSIT * _bidIds.length;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

variable naming, INITIAL_VALIDATOR_DEPOSIT; remove LP_

Comment thread src/LiquidityPool.sol

// we have already deposited the initial amount to create the validator on the beacon chain
uint256 remainingEthPerValidator = validatorSizeWei - stakingManager.initialDepositAmount();
uint256 remainingEthPerValidator = validatorSizeWei - stakingManager.INITIAL_DEPOSIT_AMOUNT();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do both LP and StakingMgr contracts have their own INITIAL DEPOSIT AMOUNT variables?

why not use only one as a single source of truth


uint256 amountForShares = liquidityPool.amountForShare(request.shareOfEEth);
if (amountForShares < request.amountWithFee) revert InvalidOutputAmount();
if (amountForShares + _TOLERANCE_BUFFER < request.amountWithFee) revert InvalidOutputAmount();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there was negative rebasing, what happens here?

if negative rebasing is happening, we shuold not finalize any PWQ request? should put that to Oracle bot?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, decided with nonce team that it;s fine to not handle negative rebase case for priority queue. will emsure relevant changes in BE.

uint32 feeGwei = uint32(fee / 1 gwei);

_requests[requestId] = IWithdrawRequestNFT.WithdrawRequest(amountOfEEth, shareOfEEth, true, feeGwei);
_requests[requestId] = IWithdrawRequestNFT.WithdrawRequest(amountOfEEth, shareOfEEth, true, 0);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we do this change?
i know we dont have the fee on withdrawal.

let's keep the function as it is though

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

using the difference between balance and eth amount locked to get stranded ETH to treasury in case of negative rebase. earlier this was used for fee path which we're not using (and confirmed with jv we have no plans for this in future), also fee was hardcoded to 0 so changing logic would've require upgrade anyway.

I implemented a different version previously: a82e295

but discussed to use current approach instead: a82e295#r3285119385

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok. can we leave dev note on the function signature? there is no fee and the input param will be ingored? OR we shuold just revert if non zero

Copy link
Copy Markdown
Author

@0xpanicError 0xpanicError May 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed accepting the fee from all places in contracts, functions were callable within protocol so no external function signature changes required.

Comment thread src/EtherFiAdmin.sol
Comment thread src/EtherFiAdmin.sol
Comment thread src/EtherFiAdmin.sol Outdated
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 449fa98. Configure here.

Comment thread src/LiquidityPool.sol
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants