@@ -19,26 +19,48 @@ contract WithdrawIntegrationTest is TestSetup, Deployed {
1919 _syncOracleReportState ();
2020 }
2121
22- /// @dev Advances the admin's lastHandledReportRefSlot to match the oracle's lastPublishedReportRefSlot.
23- /// On mainnet fork there may be a published report the admin hasn't processed yet.
24- /// We advance the admin forward (not rewind the oracle) so that slotForNextReport()
25- /// returns the correct next slot and committee members can still submit new reports.
22+ /// @dev Syncs oracle/admin state so AVS_OPERATOR_1 and AVS_OPERATOR_2 can submit reports.
23+ ///
24+ /// Two conditions can block report submission on a mainnet fork:
25+ ///
26+ /// (A) Admin hasn't processed the latest published report yet:
27+ /// shouldSubmitReport() requires lastPublishedReportRefSlot == lastHandledReportRefSlot.
28+ /// Fix: advance admin's lastHandledReportRefSlot to match the oracle.
29+ ///
30+ /// (B) Operators already submitted for slotForNextReport() (quorum not reached on mainnet):
31+ /// shouldSubmitReport() returns false when lastReportRefSlot >= slotForNextReport().
32+ /// Fix: remove and re-add each operator via the oracle owner. addCommitteeMember()
33+ /// resets CommitteeMemberState to (registered=true, enabled=true, lastReportRefSlot=0),
34+ /// clearing the stale submission without adding new committee members.
2635 function _syncOracleReportState () internal {
36+ // Step A: sync admin to oracle
2737 uint32 lastPublished = etherFiOracleInstance.lastPublishedReportRefSlot ();
28- uint32 lastHandled = etherFiAdminInstance.lastHandledReportRefSlot ();
29-
38+ uint32 lastHandled = etherFiAdminInstance.lastHandledReportRefSlot ();
3039 if (lastPublished != lastHandled) {
3140 uint32 lastPublishedBlock = etherFiOracleInstance.lastPublishedReportRefBlock ();
32-
33- // EtherFiAdmin slot 209 packs: lastHandledReportRefSlot (4B @ offset 0) +
34- // lastHandledReportRefBlock (4B @ offset 4) + other fields in higher bytes
3541 bytes32 slot209 = vm.load (address (etherFiAdminInstance), bytes32 (uint256 (209 )));
3642 uint256 val = uint256 (slot209);
37- val &= ~ uint256 (0xFFFFFFFFFFFFFFFF ); // clear low 64 bits (both uint32 fields)
43+ val &= ~ uint256 (0xFFFFFFFFFFFFFFFF ); // clear bits 0-63
3844 val |= uint256 (lastPublished);
3945 val |= uint256 (lastPublishedBlock) << 32 ;
4046 vm.store (address (etherFiAdminInstance), bytes32 (uint256 (209 )), bytes32 (val));
4147 }
48+
49+ // Step B: if AVS_OPERATOR_1 has already submitted for slotForNextReport(), their
50+ // lastReportRefSlot == slotForNextReport(), so shouldSubmitReport() returns false.
51+ // Fix: remove then re-add each operator via the oracle owner. addCommitteeMember()
52+ // resets CommitteeMemberState to (registered=true, enabled=true, lastReportRefSlot=0,
53+ // numReports=0), clearing the stale submission without touching raw storage or adding
54+ // new committee members.
55+ if (! etherFiOracleInstance.shouldSubmitReport (AVS_OPERATOR_1)) {
56+ address oracleOwner = etherFiOracleInstance.owner ();
57+ vm.startPrank (oracleOwner);
58+ etherFiOracleInstance.removeCommitteeMember (AVS_OPERATOR_1);
59+ etherFiOracleInstance.addCommitteeMember (AVS_OPERATOR_1);
60+ etherFiOracleInstance.removeCommitteeMember (AVS_OPERATOR_2);
61+ etherFiOracleInstance.addCommitteeMember (AVS_OPERATOR_2);
62+ vm.stopPrank ();
63+ }
4264 }
4365
4466 function test_Withdraw_EtherFiRedemptionManager_redeemEEth () public {
0 commit comments