Skip to content

[DO NOT MERGE] yash/refactor/security-upgrade#451

Open
0xpanicError wants to merge 63 commits into
pankaj/feat/security-upgradesfrom
yash/security-upgrade-fixes
Open

[DO NOT MERGE] yash/refactor/security-upgrade#451
0xpanicError wants to merge 63 commits into
pankaj/feat/security-upgradesfrom
yash/security-upgrade-fixes

Conversation

@0xpanicError
Copy link
Copy Markdown

@0xpanicError 0xpanicError commented Jun 2, 2026

Note

High Risk
Changes touch eETH/weETH/LiquidityPool accounting, rebase caps, finalized-withdraw semantics, and upgrade storage layout—core TVL and withdrawal paths require careful migration and caller updates.

Overview
This PR is a security-focused refactor of core protocol contracts, deploy scripts, and governance utilities—mostly behavioral and interface changes plus storage-layout preservation for upgrades, not a greenfield rewrite.

Access control & pausing: eETH, weETH, LiquidityPool, Liquifier, DepositAdapter, and related contracts drop live OwnableUpgradeable usage in favor of DeprecatedOZOwnable placeholders and RoleRegistry-driven roles. Legacy bool paused paths and many per-contract pause/unpause entrypoints are removed; tokens lean on PausableUntil / shared Pausable, with pauseUntil() on eETH/weETH gated by super guardian. Deploy scripts stop asserting owner() == timelock and drop transferOwnership where ownership is no longer per-contract.

LiquidityPool (high impact): Constructor loses minAmountForShare; ConstructorAddresses moves to ILiquidityPool. rebase adds a hard MAX_POSITIVE_REBASE_BPS cap on reward increases. Finalized claims use withdraw(amount, amountOfEEth, rate, shareOfEEth) so totalValueOutOfLp is debited by fulfill-time credit, not payout ETH. minAmountForShare invariant checks are removed; reentrancy moves to Solady ReentrancyGuardTransient with DeprecatedOZReentrancyGuard reserving old slots. V2 migration reads legacy locked ETH from __gap_3 via assembly.

Tokens: EETH removes duplicate RolesLibrary inheritance, drops local paused and operating-multisig pause helpers, and drops __Ownable_init. WeETH aligns pausing with whenNotPaused on transfers.

Liquifier / adapters: Liquifier uses deprecated OZ pause/reentrancy placeholders + transient guard; pauseContract-style APIs removed from interfaces. DepositAdapter takes a struct constructor and IDepositAdapter.

Governance / rate limiting: New DeprecatedOZ* layout shims; EtherFiRateLimiter uses custom Pausable; pauseContract / unPauseContract removed from IEtherFiRateLimiter. EarlyAdopterPool aliases OZ Pausable to avoid a name clash with ether.fi Pausable.

Scripts / misc: Bytecode verification updated for new LiquidityPool / Liquifier / WithdrawRequestNFT constructor shapes; cumulative rewards deploy comment reflects RoleRegistry-only access.

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

seongyun-ko and others added 30 commits May 29, 2026 09:10
- EtherFiAdmin: absolute per-report rebase delta cap (SINGLE_REPORT_REBASE_DELTA_CAP_BPS),
  independent of elapsedTime, so a long-spanning report cannot move the exchange rate
  by an outsized absolute amount while passing the annualized APR check.
- EtherFiRestaker: add whenNotPaused to all fund-movement functions (previously the
  pause gated nothing) and add a Guardian fast-halt path (guardianPause).
- Blacklister: bound the instant multisig setBlacklistUntil path to
  MAX_MULTISIG_BLACKLIST_DURATION; document moving permanent blacklist behind the timelock.

Tests: blacklist duration bound + restaker pause-gating (passing on fork).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Removing the setBlacklistUntil 90-day cap added earlier. It only guarded against
an accidental over-long blacklist, which is operationally recoverable (the multisig
can shorten/clear it). It gave no protection against a compromised multisig, which
can permanently blacklist via blacklistUser regardless. Net: friction without a real
security gain. Reverts Blacklister.sol and Blacklist.t.sol to base.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Addresses Cursor Bugbot review on #445: undelegate() queues withdrawal of all
restaked assets (same fund-flow category as queueWithdrawals), so it must also
respect the pause for the halt to be a complete/reliable signal. delegateTo is left
ungated — it only changes delegation posture and queues/moves no funds. Test extended.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Per @0xpanicError's review on #445:

Rebase caps — replace the single symmetric 200bps constant with asymmetric, configurable
bounds (defaults as proposed):
- EtherFiAdmin: negative (slashing) cap on accruedRewards, default 3 bps of TVL
  (max initial slashing penalty ~2.44 bps), settable behind the operating timelock
  (maxNegativeRebaseBps, 0 = default). Raise-able during a correlated-slashing event.
- LiquidityPool.rebase: positive (reward) upper bound enforced at the share-rate
  chokepoint, default 25 bps (~1 month at 3% APR), settable behind the operating timelock
  (maxPositiveRebaseBps, 0 = default). Defense-in-depth independent of the oracle path.

Restaker — switch from OZ Pausable to the protocol-wide PausableUntil model: Guardian
pauseContractUntil() (auto-expiring, cooldown) + multisig boolean pause; whenNotHalted
gates all fund-flow fns (incl. undelegate). Replaces guardianPause().

TestSetup disables both caps for generic suites (large artificial rebases on small TVL);
dedicated tests exercise enforcement.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- LiquidityPool: positive rebase cap is now a hard 25 bps constant (MAX_POSITIVE_REBASE_BPS),
  not governance-configurable (per review: no legitimate single report raises rate >25 bps).
  Dropped the setter/storage/event/error. Negative (slashing) cap in EtherFiAdmin stays
  configurable behind the operating timelock.
- EtherFiRestaker: use the standard whenNotPaused everywhere by overriding _requireNotPaused()
  to also check _requireNotPausedUntil() (per review), instead of a bespoke whenNotHalted.
- EtherFiRestaker: rename unPauseContract -> unpauseContract (+ interface).
- PausableUntil: _pauseUntil falls back to MIN_PAUSE_DURATION when duration is unset, so the
  Guardian's emergency pause is never a silent no-op that still burns the cooldown (bot finding).
- Tests: rewrote the rebase tests that used unrealistically large (5%-100%) rebases on tiny
  fresh-deploy TVL to use within-cap (<=0.25%) rebases with recomputed assertions; restaker
  pause tests updated for the rename. Pre-existing ERC721/withdraw fork-harness failures
  (identical on base) are unrelated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@seongyun-ko seongyun-ko changed the title yash/security-upgrade-fixes (DO NOT MERGE) yash/security-upgrade-fixes Jun 2, 2026
0xpanicError and others added 16 commits June 2, 2026 13:06
Follow-up to PR #454 review. The fix there (debit totalValueOutOfLp by
the eETH amount credited at fulfill, not the ETH paid on claim) had no
test that distinguished the two: every withdraw test passes the two
amounts equal, so they pass against the buggy code too.

- Add test_withdraw_debitsAmountOfEEth_notAmountPaid: pins the divergent
  down-rebase case (_amount < _amountOfEEth) at the LP boundary where the
  bug lived. Asserts the debit equals the fulfill-time credit and does not
  track the smaller ETH paid.
- Document _amountOfEEth in the withdraw natspec: it is trusted
  caller-supplied state, not bounded by Guards 1-3; the checked
  subtraction is the only LP-local backstop, and the negative-rebase
  underflow threshold is _amountOfEEth (not _amount).
- Fix stale comment in WithdrawEscrowE2E step4 that described the
  pre-fix behavior (debit by claimable).
- Update the stale LiquidityPool.sol:297 _amount reference in the
  LpRebaseWrnClaimUnderflow finding header.
…th-tests

Test + docs: pin withdraw(_amountOfEEth) accounting (follow-up to #454)
…eentrancy

yash/fix/deprecate-oz-pausing-reentrancy
Comment thread src/governance/utils/Pausable.sol
Comment thread src/core/LiquidityPool.sol
@0xpanicError 0xpanicError changed the title (DO NOT MERGE) yash/security-upgrade-fixes [DO NOT MERGE] yash/refactor/security-upgrade Jun 4, 2026
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.

There are 3 total unresolved issues (including 2 from previous reviews).

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 29c5ad6. Configure here.

Comment thread src/core/EETH.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.

2 participants