Skip to content

26Q2 - Security Upgrades#385

Open
pankajjagtapp wants to merge 412 commits into
masterfrom
pankaj/feat/security-upgrades
Open

26Q2 - Security Upgrades#385
pankajjagtapp wants to merge 412 commits into
masterfrom
pankaj/feat/security-upgrades

Conversation

@pankajjagtapp
Copy link
Copy Markdown
Contributor

@pankajjagtapp pankajjagtapp commented Apr 29, 2026

Note

Medium Risk
Touches many mainnet upgrade, timelock, and migration scripts tied to governance roles and deploy bytecode; mistakes could produce wrong calldata or failed upgrades, though changes are mostly off-chain tooling.

Overview
This PR retools Foundry scripts and ops tooling to match the security upgrade layout: @etherfi/ / @scripts/ / @tests/ remappings, domain-based imports, and centralized RoleRegistry tier roles instead of per-contract role getters.

Governance & deployment scripts now reference consolidated roles (EXECUTOR_OPERATIONS_ROLE, OPERATION_TIMELOCK_ROLE, onlyUpgradeTimelock, etc.), drop checks that EtherFiNode holds its own roleRegistry, and update EtherFiNode / EtherFiRateLimiter deploy args (no roleRegistry on node; rate limiter gets EETH/weETH bucket addresses). Timelock helper names shift from MIN_DELAY_* to minDelay_*. Upgrade verification scripts expect OracleReport without withdrawalRequestsToInvalidate and use richer LiquidityPool / EtherFiRedemptionManager / Liquifier / WithdrawRequestNFT constructor shapes in bytecode checks.

New operational surface: script/operations/v0-migration/ — batch V0 → V1 membership NFT migration (MembershipV0Migrator, MigrateV0ToV1.s.sol, ID JSON, README). SimulateBatchApprove.s.sol is removed.

CI / repo hygiene: forge tests get OP_RPC_URL; foundry.lock is gitignored; Deployed.s.sol adds EigenLayer strategy manager constant.

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

Comment thread src/EtherFiAdmin.sol
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 29, 2026

📊 Forge Coverage Report

| Contract                    | Selector                         | Calls | Reverts | Discards |
| FrozenRateWithdrawalHandler | advanceTime                      | 833   | 0       | 0        |
|-----------------------------+----------------------------------+-------+---------+----------|
| FrozenRateWithdrawalHandler | pq_cancel                        | 889   | 0       | 0        |
|-----------------------------+----------------------------------+-------+---------+----------|
| FrozenRateWithdrawalHandler | pq_claim                         | 851   | 0       | 0        |
|-----------------------------+----------------------------------+-------+---------+----------|
| FrozenRateWithdrawalHandler | pq_fulfill                       | 834   | 0       | 0        |
|-----------------------------+----------------------------------+-------+---------+----------|
| FrozenRateWithdrawalHandler | pq_handleRemainder               | 890   | 0       | 0        |
|-----------------------------+----------------------------------+-------+---------+----------|
| FrozenRateWithdrawalHandler | pq_invalidate                    | 888   | 0       | 0        |
|-----------------------------+----------------------------------+-------+---------+----------|
| FrozenRateWithdrawalHandler | pq_requestWithWeETH              | 800   | 0       | 0        |
|-----------------------------+----------------------------------+-------+---------+----------|
| FrozenRateWithdrawalHandler | pq_requestWithdraw               | 857   | 0       | 0        |
|-----------------------------+----------------------------------+-------+---------+----------|
| FrozenRateWithdrawalHandler | pq_request_at_tolerance_boundary | 854   | 0       | 0        |
|-----------------------------+----------------------------------+-------+---------+----------|
| FrozenRateWithdrawalHandler | rebase                           | 829   | 0       | 0        |
|-----------------------------+----------------------------------+-------+---------+----------|
| FrozenRateWithdrawalHandler | rebaseExtreme                    | 862   | 0       | 0        |
|-----------------------------+----------------------------------+-------+---------+----------|
| FrozenRateWithdrawalHandler | verifyFrozenRatePersistence      | 905   | 0       | 0        |
|-----------------------------+----------------------------------+-------+---------+----------|
| FrozenRateWithdrawalHandler | wrn_claim                        | 830   | 0       | 0        |
|-----------------------------+----------------------------------+-------+---------+----------|
| FrozenRateWithdrawalHandler | wrn_handleRemainder              | 846   | 0       | 0        |
|-----------------------------+----------------------------------+-------+---------+----------|
| FrozenRateWithdrawalHandler | wrn_invalidate                   | 872   | 0       | 0        |
|-----------------------------+----------------------------------+-------+---------+----------|
| FrozenRateWithdrawalHandler | wrn_lockAndFinalize              | 838   | 0       | 0        |
|-----------------------------+----------------------------------+-------+---------+----------|
| FrozenRateWithdrawalHandler | wrn_requestWithdraw              | 910   | 0       | 0        |
|-----------------------------+----------------------------------+-------+---------+----------|
| FrozenRateWithdrawalHandler | wrn_safeTransfer                 | 918   | 0       | 0        |
|-----------------------------+----------------------------------+-------+---------+----------|
| FrozenRateWithdrawalHandler | wrn_validate                     | 878   | 0       | 0        |
| Contract                 | Selector                       | Calls | Reverts | Discards |
| RedemptionManagerHandler | admin_pause_and_attempt_redeem | 1540  | 0       | 0        |
|--------------------------+--------------------------------+-------+---------+----------|
| RedemptionManagerHandler | admin_setCapacity              | 1470  | 0       | 0        |
|--------------------------+--------------------------------+-------+---------+----------|
| RedemptionManagerHandler | admin_setExitFeeBps            | 1583  | 0       | 0        |
|--------------------------+--------------------------------+-------+---------+----------|
| RedemptionManagerHandler | admin_setExitFeeSplit          | 1520  | 0       | 0        |
|--------------------------+--------------------------------+-------+---------+----------|
| RedemptionManagerHandler | admin_setLowWatermarkBps       | 1494  | 0       | 0        |
|--------------------------+--------------------------------+-------+---------+----------|
| RedemptionManagerHandler | admin_setRefillRate            | 1412  | 0       | 0        |
|--------------------------+--------------------------------+-------+---------+----------|
| RedemptionManagerHandler | advance_time                   | 1454  | 0       | 0        |
|--------------------------+--------------------------------+-------+---------+----------|
| RedemptionManagerHandler | lp_deposit                     | 1485  | 0       | 0        |
|--------------------------+--------------------------------+-------+---------+----------|
| RedemptionManagerHandler | lp_rebase                      | 1520  | 0       | 0        |
|--------------------------+--------------------------------+-------+---------+----------|
| RedemptionManagerHandler | redeemEEth                     | 1470  | 0       | 0        |
|--------------------------+--------------------------------+-------+---------+----------|
| RedemptionManagerHandler | redeemWeEth                    | 1436  | 0       | 0        |
| Contract                  | Selector                             | Calls | Reverts | Discards |
| ProtocolInvariantsHandler | adversarial_drainHookProof           | 961   | 0       | 0        |
|---------------------------+--------------------------------------+-------+---------+----------|
| ProtocolInvariantsHandler | adversarial_drainToSingleDigitShares | 995   | 0       | 0        |
|---------------------------+--------------------------------------+-------+---------+----------|
| ProtocolInvariantsHandler | adversarial_inflateFirstShare        | 915   | 0       | 0        |
|---------------------------+--------------------------------------+-------+---------+----------|
| ProtocolInvariantsHandler | burnEEthShares                       | 897   | 0       | 0        |
|---------------------------+--------------------------------------+-------+---------+----------|
| ProtocolInvariantsHandler | burnEEthSharesForNonETHWithdrawal    | 1010  | 0       | 0        |
|---------------------------+--------------------------------------+-------+---------+----------|
| ProtocolInvariantsHandler | claimSegregated                      | 974   | 0       | 0        |
|---------------------------+--------------------------------------+-------+---------+----------|
| ProtocolInvariantsHandler | depositEth                           | 928   | 0       | 0        |
|---------------------------+--------------------------------------+-------+---------+----------|
| ProtocolInvariantsHandler | donateEEthToProxy                    | 993   | 0       | 0        |
|---------------------------+--------------------------------------+-------+---------+----------|
| ProtocolInvariantsHandler | observeBackingGap                    | 965   | 0       | 0        |
|---------------------------+--------------------------------------+-------+---------+----------|
| ProtocolInvariantsHandler | pause_lp_and_attempt_deposit         | 991   | 0       | 0        |
|---------------------------+--------------------------------------+-------+---------+----------|
| ProtocolInvariantsHandler | rebase                               | 991   | 0       | 0        |
|---------------------------+--------------------------------------+-------+---------+----------|
| ProtocolInvariantsHandler | rebaseExtreme                        | 949   | 0       | 0        |
|---------------------------+--------------------------------------+-------+---------+----------|
| ProtocolInvariantsHandler | sendRawEth                           | 969   | 0       | 0        |
|---------------------------+--------------------------------------+-------+---------+----------|
| ProtocolInvariantsHandler | transferEEth                         | 994   | 0       | 0        |
|---------------------------+--------------------------------------+-------+---------+----------|
| ProtocolInvariantsHandler | transferWeETH                        | 950   | 0       | 0        |
|---------------------------+--------------------------------------+-------+---------+----------|
| ProtocolInvariantsHandler | unwrap                               | 981   | 0       | 0        |
|---------------------------+--------------------------------------+-------+---------+----------|
| ProtocolInvariantsHandler | wrap                                 | 921   | 0       | 0        |
| Contract                 | Selector            | Calls | Reverts | Discards |
| WithdrawRemainderHandler | advance_time        | 1513  | 0       | 0        |
|--------------------------+---------------------+-------+---------+----------|
| WithdrawRemainderHandler | lp_rebase_negative  | 1466  | 0       | 0        |
|--------------------------+---------------------+-------+---------+----------|
| WithdrawRemainderHandler | lp_rebase_positive  | 1510  | 0       | 0        |
|--------------------------+---------------------+-------+---------+----------|
| WithdrawRemainderHandler | pq_claim            | 1474  | 0       | 0        |
|--------------------------+---------------------+-------+---------+----------|
| WithdrawRemainderHandler | pq_fulfill          | 1497  | 0       | 0        |
|--------------------------+---------------------+-------+---------+----------|
| WithdrawRemainderHandler | pq_handleRemainder  | 1550  | 0       | 0        |
|--------------------------+---------------------+-------+---------+----------|
| WithdrawRemainderHandler | pq_request          | 1400  | 0       | 0        |
|--------------------------+---------------------+-------+---------+----------|
| WithdrawRemainderHandler | wrn_claim           | 1486  | 0       | 0        |
|--------------------------+---------------------+-------+---------+----------|
| WithdrawRemainderHandler | wrn_finalize        | 1468  | 0       | 0        |
|--------------------------+---------------------+-------+---------+----------|
| WithdrawRemainderHandler | wrn_handleRemainder | 1503  | 0       | 0        |
|--------------------------+---------------------+-------+---------+----------|
| WithdrawRemainderHandler | wrn_request         | 1517  | 0       | 0        |
| File                                                     | % Lines            | % Statements       | % Branches       | % Funcs          |
| src/core/EETH.sol                                        | 98.64% (145/147)   | 98.03% (149/152)   | 86.67% (13/15)   | 97.50% (39/40)   |
|----------------------------------------------------------+--------------------+--------------------+------------------+------------------|
| src/core/LiquidityPool.sol                               | 98.88% (266/269)   | 94.38% (336/356)   | 68.33% (41/60)   | 98.31% (58/59)   |
|----------------------------------------------------------+--------------------+--------------------+------------------+------------------|
| src/core/WeETH.sol                                       | 93.48% (86/92)     | 91.75% (89/97)     | 73.33% (11/15)   | 87.50% (21/24)   |
|----------------------------------------------------------+--------------------+--------------------+------------------+------------------|
| src/deposits/DepositAdapter.sol                          | 86.15% (56/65)     | 86.08% (68/79)     | 54.55% (6/11)    | 81.82% (9/11)    |
|----------------------------------------------------------+--------------------+--------------------+------------------+------------------|
| src/deposits/LiquidRefer.sol                             | 88.24% (30/34)     | 88.89% (24/27)     | 100.00% (2/2)    | 80.00% (8/10)    |
|----------------------------------------------------------+--------------------+--------------------+------------------+------------------|
| src/deposits/Liquifier.sol                               | 91.62% (153/167)   | 83.82% (171/204)   | 53.19% (25/47)   | 86.84% (33/38)   |
|----------------------------------------------------------+--------------------+--------------------+------------------+------------------|
| src/governance/Blacklister.sol                           | 86.36% (19/22)     | 81.25% (13/16)     | 50.00% (1/2)     | 77.78% (7/9)     |
|----------------------------------------------------------+--------------------+--------------------+------------------+------------------|
| src/governance/RevokeAdmin.sol                           | 0.00% (0/19)       | 0.00% (0/9)        | 100.00% (0/0)    | 0.00% (0/10)     |
|----------------------------------------------------------+--------------------+--------------------+------------------+------------------|
| src/governance/RoleRegistry.sol                          | 100.00% (46/46)    | 97.73% (43/44)     | 91.67% (11/12)   | 100.00% (20/20)  |
|----------------------------------------------------------+--------------------+--------------------+------------------+------------------|
| src/governance/rate-limiting/EtherFiRateLimiter.sol      | 100.00% (80/80)    | 100.00% (83/83)    | 100.00% (18/18)  | 100.00% (25/25)  |
|----------------------------------------------------------+--------------------+--------------------+------------------+------------------|
| src/governance/rate-limiting/RateLimitedToken.sol        | 100.00% (16/16)    | 96.00% (24/25)     | 50.00% (1/2)     | 100.00% (5/5)    |
|----------------------------------------------------------+--------------------+--------------------+------------------+------------------|
| src/governance/rate-limiting/libraries/BucketLimiter.sol | 100.00% (44/44)    | 100.00% (38/38)    | 100.00% (5/5)    | 100.00% (9/9)    |
|----------------------------------------------------------+--------------------+--------------------+------------------+------------------|
| src/governance/utils/PausableUntil.sol                   | 100.00% (33/33)    | 100.00% (33/33)    | 100.00% (4/4)    | 100.00% (10/10)  |
|----------------------------------------------------------+--------------------+--------------------+------------------+------------------|
| src/governance/utils/ReentrancyGuardNamespaced.sol       | 100.00% (12/12)    | 100.00% (10/10)    | 100.00% (1/1)    | 100.00% (3/3)    |
|----------------------------------------------------------+--------------------+--------------------+------------------+------------------|
| src/governance/utils/RolesLibrary.sol                    | 100.00% (20/20)    | 100.00% (10/10)    | 100.00% (0/0)    | 100.00% (10/10)  |
|----------------------------------------------------------+--------------------+--------------------+------------------+------------------|
| src/membership/MembershipManager.sol                     | 83.52% (147/176)   | 84.74% (161/190)   | 17.65% (3/17)    | 64.10% (25/39)   |
|----------------------------------------------------------+--------------------+--------------------+------------------+------------------|
| src/membership/MembershipNFT.sol                         | 75.20% (94/125)    | 83.78% (124/148)   | 46.15% (6/13)    | 61.29% (19/31)   |
|----------------------------------------------------------+--------------------+--------------------+------------------+------------------|
| src/membership/libraries/GlobalIndexLibrary.sol          | 89.04% (65/73)     | 93.16% (109/117)   | 42.86% (3/7)     | 40.00% (2/5)     |
|----------------------------------------------------------+--------------------+--------------------+------------------+------------------|
| src/oracle/EtherFiAdmin.sol                              | 97.94% (190/194)   | 95.97% (262/273)   | 79.17% (38/48)   | 100.00% (29/29)  |
|----------------------------------------------------------+--------------------+--------------------+------------------+------------------|
| src/oracle/EtherFiOracle.sol                             | 93.01% (133/143)   | 91.28% (157/172)   | 82.76% (24/29)   | 92.59% (25/27)   |
|----------------------------------------------------------+--------------------+--------------------+------------------+------------------|
| src/restaking/EtherFiRestaker.sol                        | 81.82% (126/154)   | 82.65% (162/196)   | 21.43% (3/14)    | 66.67% (20/30)   |
|----------------------------------------------------------+--------------------+--------------------+------------------+------------------|
| src/restaking/RestakingRewardsRouter.sol                 | 100.00% (26/26)    | 100.00% (26/26)    | 100.00% (5/5)    | 100.00% (7/7)    |
|----------------------------------------------------------+--------------------+--------------------+------------------+------------------|
| src/rewards/CumulativeMerkleRewardsDistributor.sol       | 92.11% (70/76)     | 88.16% (67/76)     | 45.45% (5/11)    | 94.74% (18/19)   |
|----------------------------------------------------------+--------------------+--------------------+------------------+------------------|
| src/rewards/EtherFiRewardsRouter.sol                     | 100.00% (26/26)    | 95.83% (23/24)     | 50.00% (1/2)     | 100.00% (8/8)    |
|----------------------------------------------------------+--------------------+--------------------+------------------+------------------|
| src/staking/AuctionManager.sol                           | 87.50% (105/120)   | 87.60% (113/129)   | 60.87% (14/23)   | 80.00% (20/25)   |
|----------------------------------------------------------+--------------------+--------------------+------------------+------------------|
| src/staking/EtherFiNode.sol                              | 98.39% (61/62)     | 90.00% (63/70)     | 28.57% (2/7)     | 100.00% (17/17)  |
|----------------------------------------------------------+--------------------+--------------------+------------------+------------------|
| src/staking/EtherFiNodesManager.sol                      | 98.31% (175/178)   | 96.06% (195/203)   | 86.21% (25/29)   | 100.00% (44/44)  |
|----------------------------------------------------------+--------------------+--------------------+------------------+------------------|
| src/staking/NodeOperatorManager.sol                      | 87.76% (43/49)     | 87.18% (34/39)     | 75.00% (3/4)     | 81.25% (13/16)   |
|----------------------------------------------------------+--------------------+--------------------+------------------+------------------|
| src/staking/StakingManager.sol                           | 100.00% (84/84)    | 95.69% (111/116)   | 73.68% (14/19)   | 100.00% (13/13)  |
|----------------------------------------------------------+--------------------+--------------------+------------------+------------------|
| src/staking/libraries/DepositDataRootGenerator.sol       | 100.00% (18/18)    | 100.00% (20/20)    | 100.00% (0/0)    | 100.00% (2/2)    |
|----------------------------------------------------------+--------------------+--------------------+------------------+------------------|
| src/utils/AssetRecovery.sol                              | 100.00% (16/16)    | 96.77% (30/31)     | 85.71% (6/7)     | 100.00% (3/3)    |
|----------------------------------------------------------+--------------------+--------------------+------------------+------------------|
| src/withdrawals/EtherFiRedemptionManager.sol             | 96.32% (157/163)   | 91.32% (200/219)   | 63.64% (21/33)   | 97.22% (35/36)   |
|----------------------------------------------------------+--------------------+--------------------+------------------+------------------|
| src/withdrawals/PriorityWithdrawalQueue.sol              | 98.35% (239/243)   | 91.87% (305/332)   | 57.63% (34/59)   | 97.78% (44/45)   |
|----------------------------------------------------------+--------------------+--------------------+------------------+------------------|
| src/withdrawals/WeETHWithdrawAdapter.sol                 | 75.00% (42/56)     | 71.70% (38/53)     | 25.00% (2/8)     | 66.67% (10/15)   |
|----------------------------------------------------------+--------------------+--------------------+------------------+------------------|
| src/withdrawals/WithdrawRequestNFT.sol                   | 100.00% (163/163)  | 91.94% (194/211)   | 60.47% (26/43)   | 100.00% (35/35)  |
|----------------------------------------------------------+--------------------+--------------------+------------------+------------------|
| Total                                                    | 92.99% (2986/3211) | 91.04% (3485/3828) | 65.38% (374/572) | 88.61% (646/729) |

---
Ran 1 test for test/fork-tests/RoleMigrationStorageIntegrity.t.sol:RoleMigrationStorageIntegrityTest
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 3.86ms (2.10ms CPU time)
Ran 17 tests for test/RoleRegistry.t.sol:RoleRegistryTest
Suite result: ok. 17 passed; 0 failed; 0 skipped; finished in 17.83ms (12.86ms CPU time)
Ran 5 tests for test/AddressProvider.t.sol:AddressProviderTest
Suite result: ok. 5 passed; 0 failed; 0 skipped; finished in 57.85ms (9.41ms CPU time)
Ran 21 tests for test/AuctionManager.t.sol:AuctionManagerTest
Suite result: ok. 21 passed; 0 failed; 0 skipped; finished in 64.36ms (38.20ms CPU time)
Ran 1 test for test/BNFT.t.sol:BNFTTest
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 32.27ms (1.16ms CPU time)
Ran 38 tests for test/Blacklist.t.sol:BlacklistTest
Suite result: ok. 38 passed; 0 failed; 0 skipped; finished in 78.37ms (48.98ms CPU time)
Ran 58 tests for test/BucketRaterLimiter.t.sol:BucketRateLimiterTest
Suite result: ok. 58 passed; 0 failed; 0 skipped; finished in 28.56ms (26.87ms CPU time)
Ran 2 tests for test/fork-tests/pectra-fork-tests/Consolidation-through-EOA.sol:ConsolidationThroughEOATest
Suite result: ok. 2 passed; 0 failed; 0 skipped; finished in 823.03ms (512.82ms CPU time)
Ran 2 tests for test/ContractCodeChecker.t.sol:ContractCodeCheckerTest
Suite result: ok. 2 passed; 0 failed; 0 skipped; finished in 742.61ms (40.97ms CPU time)
Ran 27 tests for test/CumulativeMerkleRewardsDistributor.t.sol:CumulativeMerkleRewardsDistributorTest
Suite result: ok. 27 passed; 0 failed; 0 skipped; finished in 81.06ms (45.04ms CPU time)
Ran 56 tests for test/EtherFiRateLimiter.t.sol:EtherFiRateLimiterTest
Suite result: ok. 56 passed; 0 failed; 0 skipped; finished in 2.26s (2.22s CPU time)
Ran 19 tests for test/StakingManager.t.sol:StakingManagerTest
Suite result: ok. 19 passed; 0 failed; 0 skipped; finished in 2.25s (484.82ms CPU time)
Ran 2 tests for test/TNFT.t.sol:TnftTest
Suite result: ok. 2 passed; 0 failed; 0 skipped; finished in 30.32ms (2.39ms CPU time)
Ran 6 tests for test/TVLOracle.t.sol:TVLOracleTest
Suite result: ok. 6 passed; 0 failed; 0 skipped; finished in 34.86ms (7.27ms CPU time)
Ran 46 tests for test/TokenRateLimit.t.sol:TokenRateLimitTest
Suite result: ok. 46 passed; 0 failed; 0 skipped; finished in 136.65ms (107.37ms CPU time)
Ran 8 tests for test/integration-tests/Deposit.t.sol:DepositIntegrationTest
Suite result: ok. 8 passed; 0 failed; 0 skipped; finished in 1.14s (1.06s CPU time)
Ran 6 tests for test/DepositAdapter.t.sol:DepositAdapterTest
Suite result: ok. 6 passed; 0 failed; 0 skipped; finished in 150.60ms (92.09ms CPU time)
Ran 47 tests for test/EETH.t.sol:EETHTest
Suite result: ok. 47 passed; 0 failed; 0 skipped; finished in 122.16ms (88.73ms CPU time)
Ran 1 test for test/behaviour-tests/pectra-fork-tests/EL-withdrawals.t.sol:ELExitsTest
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 187.90ms (134.72ms CPU time)
Ran 2 tests for test/behaviour-tests/ELExitsForkTestingDeployment.t.sol:ELExitsForkTestingDeploymentTest
Suite result: ok. 2 passed; 0 failed; 0 skipped; finished in 1.65ms (162.58µs CPU time)
Ran 101 tests for test/EtherFiNodesManager.t.sol:EtherFiNodesManagerTest
Suite result: ok. 101 passed; 0 failed; 0 skipped; finished in 1.63s (1.35s CPU time)
Ran 7 tests for test/EtherFiOperationParameters.t.sol:EtherFiOperationParametersTest
Suite result: ok. 7 passed; 0 failed; 0 skipped; finished in 507.42ms (496.88ms CPU time)
Ran 116 tests for test/EtherFiOracle.t.sol:EtherFiOracleTest
Suite result: ok. 116 passed; 0 failed; 0 skipped; finished in 406.93ms (374.11ms CPU time)
Ran 30 tests for test/PausableUntil.t.sol:PausableUntilTest
Suite result: ok. 30 passed; 0 failed; 0 skipped; finished in 311.20ms (310.10ms CPU time)
Ran 47 tests for test/Liquifier.t.sol:LiquifierTest
Suite result: ok. 47 passed; 0 failed; 0 skipped; finished in 6.92s (6.90s CPU time)
Ran 11 tests for test/LiquifierStEthPriceFeed.t.sol:LiquifierStEthPriceFeedTest
Suite result: ok. 11 passed; 0 failed; 0 skipped; finished in 84.69ms (82.90ms CPU time)
Ran 59 tests for test/EtherFiRedemptionManager.t.sol:EtherFiRedemptionManagerTest
Suite result: ok. 59 passed; 0 failed; 0 skipped; finished in 4.85s (4.82s CPU time)
Ran 9 tests for test/fork-tests/LiquifierStEthPriceFeedFork.t.sol:LiquifierStEthPriceFeedForkTest
Suite result: ok. 9 passed; 0 failed; 0 skipped; finished in 632.61ms (579.22ms CPU time)
Ran 3 tests for test/LpRebaseWrnClaimUnderflow.t.sol:LpRebaseWrnClaimUnderflowTest
Suite result: ok. 3 passed; 0 failed; 0 skipped; finished in 33.27ms (7.44ms CPU time)
Ran 97 tests for test/PriorityWithdrawalQueue.t.sol:PriorityWithdrawalQueueTest
Suite result: FAILED. 96 passed; 1 failed; 0 skipped; finished in 1.91s (1.79s CPU time)
Ran 5 tests for test/MembershipDeprecationUpgradeFork.t.sol:MembershipDeprecationUpgradeForkTest
Suite result: ok. 5 passed; 0 failed; 0 skipped; finished in 1.12s (668.02ms CPU time)
Ran 1 test for test/MembershipManager.t.sol:MembershipManagerTest
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 29.11ms (2.72ms CPU time)
Ran 10 tests for test/EtherFiRestaker.t.sol:EtherFiRestakerTest
Suite result: ok. 10 passed; 0 failed; 0 skipped; finished in 2.20s (2.00s CPU time)
Ran 32 tests for test/EtherFiRewardsRouter.t.sol:EtherFiRewardsRouterTest
Suite result: ok. 32 passed; 0 failed; 0 skipped; finished in 13.86ms (11.28ms CPU time)
Ran 6 tests for test/EtherFiTimelock.t.sol:TimelockTest
Suite result: ok. 6 passed; 0 failed; 0 skipped; finished in 2.16s (2.29s CPU time)
Ran 1 test for test/EtherFiViewer.t.sol:EtherFiViewerTest
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 137.44ms (94.49ms CPU time)
Ran 15 tests for test/integration-tests/MinAmountForShare.t.sol:MinAmountForShareForkTest
Suite result: ok. 15 passed; 0 failed; 0 skipped; finished in 3.09s (793.42ms CPU time)
Ran 6 tests for test/NodeOperatorManager.t.sol:NodeOperatorManagerTest
Suite result: ok. 6 passed; 0 failed; 0 skipped; finished in 53.37ms (12.72ms CPU time)
Ran 4 tests for test/fork-tests/UpgradeStorageIntegrity.t.sol:UpgradeStorageIntegrityTest
Suite result: ok. 4 passed; 0 failed; 0 skipped; finished in 11.21s (11.18s CPU time)
Ran 1 test for test/V0MigrationFork.t.sol:V0MigrationForkTest
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 8.44s (8.41s CPU time)
Ran 2 tests for test/integration-tests/Validator-Flows.t.sol:ValidatorFlowsIntegrationTest
Suite result: ok. 2 passed; 0 failed; 0 skipped; finished in 3.10s (951.38ms CPU time)
Ran 46 tests for test/WeETH.t.sol:WeETHTest
Suite result: ok. 46 passed; 0 failed; 0 skipped; finished in 339.60ms (250.93ms CPU time)
Ran 18 tests for test/WeETHWithdrawAdapter.t.sol:WeETHWithdrawAdapterTest
Suite result: ok. 18 passed; 0 failed; 0 skipped; finished in 90.34ms (47.91ms CPU time)
Ran 11 tests for test/integration-tests/Withdraw.t.sol:WithdrawIntegrationTest
Suite result: ok. 11 passed; 0 failed; 0 skipped; finished in 2.53s (2.38s CPU time)
Ran 5 tests for test/integration-tests/WithdrawEscrowE2E.t.sol:WithdrawEscrowE2ETest
Suite result: ok. 5 passed; 0 failed; 0 skipped; finished in 284.72ms (128.67ms CPU time)
Ran 1 test for test/invariant/FrozenRateWithdrawal.invariant.t.sol:FrozenRateWithdrawalInvariantTest
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 30.15s (30.11s CPU time)
Ran 1 test for test/invariant/RedemptionManager.invariant.t.sol:RedemptionManagerInvariantTest
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 30.47s (30.41s CPU time)
Ran 6 tests for test/ReentrancyGuard.t.sol:ReentrancyGuardTest
Suite result: ok. 6 passed; 0 failed; 0 skipped; finished in 50.21ms (20.93ms CPU time)
Ran 13 tests for test/ReentrancyGuardStorage.t.sol:ReentrancyGuardStorageTest
Suite result: ok. 13 passed; 0 failed; 0 skipped; finished in 259.47ms (232.34ms CPU time)
Ran 3 tests for test/behaviour-tests/pectra-fork-tests/Request-consolidation.t.sol:RequestConsolidationTest
Suite result: ok. 3 passed; 0 failed; 0 skipped; finished in 2.25s (715.48ms CPU time)
Ran 26 tests for test/RestakingRewardsRouter.t.sol:RestakingRewardsRouterTest
Suite result: ok. 26 passed; 0 failed; 0 skipped; finished in 18.34ms (13.47ms CPU time)
Ran 3 tests for test/integration-tests/Handle-Remainder-Shares.t.sol:HandleRemainderSharesIntegrationTest
Suite result: ok. 3 passed; 0 failed; 0 skipped; finished in 3.83s (1.34s CPU time)
Ran 5 tests for test/HandleRemainderRoundingEquivalence.t.sol:HandleRemainderRoundingEquivalenceTest
Suite result: ok. 5 passed; 0 failed; 0 skipped; finished in 1.28s (1.24s CPU time)
Ran 1 test for test/liquid-tests/LiquidReferBtc.t.sol:LiquidReferBtcOPTest
Suite result: FAILED. 0 passed; 1 failed; 0 skipped; finished in 1.22ms (0.00ns CPU time)
Ran 4 tests for test/liquid-tests/LiquidReferBtc.t.sol:LiquidReferBtcTest
Suite result: ok. 4 passed; 0 failed; 0 skipped; finished in 913.94ms (779.52ms CPU time)
Ran 1 test for test/liquid-tests/LiquidReferEth.t.sol:LiquidReferETHOPTest
Suite result: FAILED. 0 passed; 1 failed; 0 skipped; finished in 922.89µs (0.00ns CPU time)
Ran 4 tests for test/liquid-tests/LiquidReferEth.t.sol:LiquidReferEthTest
Suite result: ok. 4 passed; 0 failed; 0 skipped; finished in 766.26ms (712.20ms CPU time)
Ran 1 test for test/liquid-tests/LiquidReferUsdPermit.t.sol:LiquidReferUsdPermitOPTest
Suite result: FAILED. 0 passed; 1 failed; 0 skipped; finished in 1.02ms (0.00ns CPU time)
Ran 6 tests for test/liquid-tests/LiquidReferUsdPermit.t.sol:LiquidReferUsdPermitTest
Suite result: ok. 6 passed; 0 failed; 0 skipped; finished in 1.66s (1.61s CPU time)
Ran 12 tests for test/liquid-tests/LiquidReferWhitelist.t.sol:LiquidReferWhitelistTest
Suite result: ok. 12 passed; 0 failed; 0 skipped; finished in 304.82ms (271.28ms CPU time)
Ran 1 test for test/invariant/ProtocolInvariants.invariant.t.sol:ProtocolInvariantsInvariantTest
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 42.07s (42.03s CPU time)
Ran 1 test for test/invariant/WithdrawRemainder.invariant.t.sol:WithdrawRemainderInvariantTest
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 23.75s (23.70s CPU time)
Ran 80 tests for test/WithdrawRequestNFT.t.sol:WithdrawRequestNFTTest
Suite result: ok. 80 passed; 0 failed; 0 skipped; finished in 6.97s (6.93s CPU time)
Ran 146 tests for test/LiquidityPool.t.sol:LiquidityPoolTest
Suite result: ok. 146 passed; 0 failed; 0 skipped; finished in 2.87s (2.82s CPU time)
Ran 24 tests for test/fork-tests/validator-key-gen.t.sol:ValidatorKeyGenTest
Suite result: ok. 24 passed; 0 failed; 0 skipped; finished in 1.19s (1.38s CPU time)
Ran 27 tests for test/ProtocolInvariants.t.sol:ProtocolInvariantsTest
Suite result: ok. 27 passed; 0 failed; 0 skipped; finished in 5.36s (7.83s CPU time)
Ran 42 tests for test/behaviour-tests/prelude.t.sol:PreludeTest
Suite result: ok. 42 passed; 0 failed; 0 skipped; finished in 3.70s (6.86s CPU time)
Ran 67 test suites in 56.33s (218.19s CPU time): 1412 tests passed, 4 failed, 0 skipped (1416 total tests)

Generated by workflow run #752

Comment thread src/core/LiquidityPool.sol
Comment thread src/withdrawals/PriorityWithdrawalQueue.sol
Comment thread src/deposits/Liquifier.sol
Comment thread src/EtherFiAdmin.sol Outdated
Comment thread src/staking/EtherFiNodesManager.sol
Comment thread src/LiquidityPool.sol
Comment thread src/Liquifier.sol Outdated
@seongyun-ko seongyun-ko changed the title Security Upgrades to harden Protocol Security 26Q2 - Security Upgrades May 7, 2026
Comment thread src/Liquifier.sol Outdated
Comment thread src/oracle/EtherFiAdmin.sol
Comment thread src/oracle/EtherFiAdmin.sol
0xpanicError and others added 8 commits May 12, 2026 15:45
… auto-sweep

Addresses review feedback: EtherFiNodesManager.completeQueuedWithdrawals
silently dropped the swept amount, so off-chain indexers watching only the
manager contract missed those transfers (sweepFunds and
completeQueuedETHWithdrawals both already emit it).

- IEtherFiNode.completeQueuedWithdrawals now returns the swept balance.
- EtherFiNode.completeQueuedWithdrawals forwards the _sweepToLiquidityPool
  return value.
- EtherFiNodesManager.completeQueuedWithdrawals captures that balance and
  emits FundsTransferred(node, balance) when > 0.
- test/EtherFiNodesManager.t.sol setUp upgrades the EtherFiNode beacon impl
  to the locally compiled one so the new uint256 return is correctly
  decoded (the deployed mainnet impl still returns void until the
  beacon-impl upgrade ships).
- test_completeQueuedWithdrawals_autoSweeps_to_LP now asserts the manager
  emits FundsTransferred with the node address as the indexed topic.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Comment thread src/utils/PausableUntil.sol Outdated

import "../interfaces/IRoleRegistry.sol";

contract PausableUntil {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Does it make sense to make this abstract? All functions internal and never to be deployed ?

Copy link
Copy Markdown
Contributor

@seongyun-ko seongyun-ko May 15, 2026

Choose a reason for hiding this comment

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

fine to make it abstract

Comment thread src/EtherFiRateLimiter.sol Outdated
import "./utils/PausableUntil.sol";

contract EtherFiRateLimiter is IEtherFiRateLimiter, Initializable, UUPSUpgradeable, PausableUpgradeable {
contract EtherFiRateLimiter is IEtherFiRateLimiter, Initializable, UUPSUpgradeable, PausableUpgradeable, PausableUntil {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

What advantage do we have of adding pausable on Ratelimiter ?

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.

agreed,

Comment thread src/EETH.sol Outdated
Comment on lines +203 to +205
function _transferShares(address _sender, address _recipient, uint256 _sharesAmount) internal whenNotPaused {
blacklister.nonBlacklisted(_sender);
blacklister.nonBlacklisted(_recipient);
Copy link
Copy Markdown
Contributor Author

@pankajjagtapp pankajjagtapp May 13, 2026

Choose a reason for hiding this comment

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

Need to add this for safe guard from transferFrom similar to MembershipNFT checks:

blacklister.nonBlacklisted(msg.sender);

Comment thread src/utils/PausableUntil.sol Outdated
}
}

uint256 public constant MAX_PAUSE_DURATION = 7 days;
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

We would be reverting back to 1 day?

Comment thread src/EETH.sol Outdated
Comment thread src/staking/AuctionManager.sol
Comment thread src/staking/AuctionManager.sol
Comment thread src/deposits/DepositAdapter.sol
0xpanicError and others added 2 commits May 27, 2026 15:37
…lize ordering

Two follow-up fixes from the multi-lens audit of PR #385.

H-5 (AuctionManager): pre-PR, every consumed bid forwarded `bid.amount` to
`membershipManagerContractAddress` via `processAuctionFeeTransfer`. That fn
was deleted in this branch and `updateSelectedBidInformation` now only flips
`bid.isActive=false`, stranding the consumed-bid ETH in the contract. The
`treasury` immutable was added but never read.

This change rewires `updateSelectedBidInformation` to forward `bid.amount`
to `treasury` immediately on consumption, matching the previously-removed
revenue path. The `membershipManagerContractAddress` immutable and its
constructor param are dropped (unused). Bid-cancellation refund is
unaffected (`_cancelBid` still refunds the bidder).

The currently-stranded ~0.097 ETH (`accumulatedRevenue` below the 0.2 ETH
auto-forward threshold) is left to be handled separately; the per-call
forward stops new revenue from getting trapped going forward.

H-3 (WithdrawRequestNFT): `finalizeRequests` lacked any guard that the
share-rate-freeze sentinel had been pushed. If `initializeShareRateFreezeUpgrade`
is skipped between proxy upgrade and the first oracle report, the first
`finalizeRequests(N)` pushes `(N, rateNow)` as the trace's first entry.
Every legacy tokenId then resolves to `rateNow` via `lowerLookup` instead
of the `0` sentinel that signals "fall back to live LP rate", silently
mispaying every pre-upgrade holder.

This change adds `NotInitialized` + a `length() == 0` precondition to
`finalizeRequests`, making the upgrade-ordering trap impossible.

Tests:
- `TestSetup` pushes the sentinel as part of standard setup (matches
  post-upgrade reality on mainnet).
- `WithdrawRequestNFTIntrusive` gains `clearFinalizationRatesForTest()`
  so the four tests that explicitly exercise the pre-init transition can
  reset the trace to length 0.
- All AuctionManager constructor call-sites updated for the dropped param.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
bytes32 ETHERFI_NODES_MANAGER_EIGENLAYER_ADMIN_ROLE = keccak256(
"ETHERFI_NODES_MANAGER_EIGENLAYER_ADMIN_ROLE"
);
bytes32 ETHERFI_NODES_MANAGER_CALL_FORWARDER_ROLE = keccak256(
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Missing UPGRADE_TIMELOCK_ROLE check in verification script

Medium Severity

The comment at line 267–269 states that all per-contract roles now map to one of 8 consolidated tier roles and explicitly lists UPGRADE_TIMELOCK_ROLE first. However, the verification code only checks 7 of the 8 roles — UPGRADE_TIMELOCK_ROLE is missing. This means the most privileged role (controlling upgrades) is never verified, defeating the purpose of this safety check.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit f756609. Configure here.

Comment thread script/upgrades/priority-queue/transactionsPriorityQueue.s.sol
Comment thread script/upgrades/reaudit-fixes/transactions-reaudit-fixes.s.sol
0xpanicError and others added 7 commits May 27, 2026 16:39
…nto seongyun/fix/auction-revenue-wrnft-init-guard
…venue-wrnft-init-guard

fix: forward AuctionManager bid revenue + guard WRNFT finalize ordering
…nto seongyun/feat/lp-withdraw-max-burn-guard
…aw-max-burn-guard

feat(lp): max-burn segregated withdraw guard
Comment thread script/upgrades/CrossPodApproval/transactions.s.sol
seongyun-ko added a commit that referenced this pull request May 28, 2026
Addresses H-3 from the multi-lens audit of PR #385.

PriorityWithdrawalQueue.claimWithdraw was callable on behalf of any
`request.user`, including a sanctioned address that became blacklisted between
request finalization and claim — proceeds would flow to the blacklisted user via
a non-blacklisted accomplice. Add blacklister as an immutable, wire it through
the constructor with a zero-check, and call `blacklister.nonBlacklisted(request.user)`
at the top of `_claimWithdraw` so both `claimWithdraw` and `batchClaimWithdraw`
fail-closed on sanctioned recipients. Cancel path is already covered transitively
via eETH's own `nonBlacklisted` on transfer.

H-2 — bid-revenue treasury setter was acked rather than implemented.
Storage-collision risk with the pre-deprecation DEPRECATED_admin slot makes
the safe fix non-trivial; deferred.

Test/script call-site updates thread the new PWQ blacklister arg.
* fix(pwq): blacklist gate on PriorityWithdrawalQueue claim

Addresses H-3 from the multi-lens audit of PR #385.

PriorityWithdrawalQueue.claimWithdraw was callable on behalf of any
`request.user`, including a sanctioned address that became blacklisted between
request finalization and claim — proceeds would flow to the blacklisted user via
a non-blacklisted accomplice. Add blacklister as an immutable, wire it through
the constructor with a zero-check, and call `blacklister.nonBlacklisted(request.user)`
at the top of `_claimWithdraw` so both `claimWithdraw` and `batchClaimWithdraw`
fail-closed on sanctioned recipients. Cancel path is already covered transitively
via eETH's own `nonBlacklisted` on transfer.

H-2 — bid-revenue treasury setter was acked rather than implemented.
Storage-collision risk with the pre-deprecation DEPRECATED_admin slot makes
the safe fix non-trivial; deferred.

Test/script call-site updates thread the new PWQ blacklister arg.

* fix: blacklist msg.sender too

---------

Co-authored-by: Yash Saraswat <yash@ether.fi>
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 2 total unresolved issues (including 1 from previous review).

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 87bc3d6. Configure here.

bytes32 ETHERFI_NODES_MANAGER_ADMIN_ROLE = EtherFiNodesManager(payable(etherFiNodesManagerImpl)).OPERATION_TIMELOCK_ROLE();
bytes32 ETHERFI_NODES_MANAGER_CALL_FORWARDER_ROLE = EtherFiNodesManager(payable(etherFiNodesManagerImpl)).EIGENPOD_OPERATIONS_ROLE();
bytes32 ETHERFI_NODES_MANAGER_EIGENLAYER_ADMIN_ROLE = EtherFiNodesManager(payable(etherFiNodesManagerImpl)).HOUSEKEEPING_OPERATIONS_ROLE();
bytes32 LIQUIDITY_POOL_VALIDATOR_APPROVER_ROLE = LiquidityPool(payable(liquidityPoolImpl)).EXECUTOR_OPERATIONS_ROLE();
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Role getters called on contracts that don't define them

Medium Severity

The new code calls role constant getters like EXECUTOR_OPERATIONS_ROLE(), OPERATION_TIMELOCK_ROLE(), EIGENPOD_OPERATIONS_ROLE(), and HOUSEKEEPING_OPERATIONS_ROLE() on StakingManager, EtherFiNodesManager, and LiquidityPool contract instances. These role constants are defined on RoleRegistry, not on those contracts. Grep confirms these function names don't exist in StakingManager.sol, EtherFiNodesManager.sol, or LiquidityPool.sol. Unless RolesLibrary re-exports them (unlikely given the naming pattern), these calls will revert at script initialization. The old code correctly called contract-specific getters that did exist on those contracts. These should reference RoleRegistry directly instead.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 87bc3d6. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants