@@ -15,12 +15,9 @@ import {IPostTokenRebaseReceiver} from "./interfaces/IPostTokenRebaseReceiver.so
1515
1616import {WithdrawalQueue} from "./WithdrawalQueue.sol " ;
1717
18- interface IRedeemsReserveVault {
19- function withdrawToLido (uint256 _amount ) external ;
20- function getRedeemedShares () external view returns (uint256 );
18+ interface IRedeemsBuffer {
2119 function getRedeemedEther () external view returns (uint256 );
22- function flushSharesToBurner () external ;
23- function resetRedeemedEther () external ;
20+ function withdrawUnredeemed () external ;
2421}
2522
2623interface IStakingRouter {
@@ -102,6 +99,8 @@ contract Accounting {
10299 uint256 postTotalShares;
103100 /// @notice amount of ether under the protocol after the report is applied
104101 uint256 postTotalPooledEther;
102+ /// @notice number of redeem shares to burn (outside the rebase limiter, rate-neutral)
103+ uint256 redeemSharesToBurn;
105104 }
106105
107106 /// @notice precalculated numbers of shares that should be minted as fee to NO
@@ -193,7 +192,7 @@ contract Accounting {
193192 // Principal CL balance is sum of previous balances and new deposits
194193 update.principalClBalance = _pre.clValidatorsBalance + _pre.clPendingBalance + _pre.depositedBalance;
195194
196- // Read redemption counters from vault (on-chain, includes all redemptions)
195+ // Read redemption counters from buffer (on-chain, includes all redemptions)
197196 (uint256 redeemedShares , uint256 redeemedEther ) = _getRedeemedCounters ();
198197
199198 // Limit the rebase to avoid oracle frontrunning.
@@ -215,12 +214,13 @@ contract Accounting {
215214 update.sharesToFinalizeWQ
216215 );
217216
218- // Add redemption shares outside the limiter — rate-neutral, must all burn on this report
219- update.totalSharesToBurn + = redeemedShares;
217+ // Redeem shares are burned via a separate commit (outside the rebase limiter, rate-neutral)
218+ update.redeemSharesToBurn = redeemedShares;
220219
221220 uint256 postInternalSharesBeforeFees = _pre.totalShares -
222221 _pre.externalShares - // internal shares before
223- update.totalSharesToBurn; // shares to be burned (WQ + cover + redemptions)
222+ update.totalSharesToBurn - // shares to be burned (WQ + cover)
223+ update.redeemSharesToBurn; // redeem shares burned separately
224224
225225 update.postInternalEther =
226226 _pre.totalPooledEther - _pre.externalEther
@@ -386,16 +386,20 @@ contract Accounting {
386386 LIDO.internalizeExternalBadDebt (_pre.badDebtToInternalize);
387387 }
388388
389- // Flush accumulated redemption shares from vault to Burner before commit
390- _flushVaultSharesToBurner ();
389+ // Burn all redeem shares (rate-neutral, outside limiter)
390+ // Shares are already on Burner — sent during each redeem() call
391+ _contracts.burner.commitRedeemSharesToBurn ();
391392
393+ // Burn limiter-constrained cover/nonCover shares
392394 if (_update.totalSharesToBurn > 0 ) {
393395 _contracts.burner.commitSharesToBurn (_update.totalSharesToBurn);
394396 }
395397
396- // Reconcile RedeemsReserveVault — tracked vault ETH updated to actual.
397- _reconcileRedeemsReserveVault ();
398+ // ETH round-trip: withdraw unredeemed → reconcile bufferedEther
399+ _withdrawAndReconcileRedeemsBuffer ();
398400
401+ // Standard flow — runs on clean state (bufferedEther == Lido.balance)
402+ // _updateBufferedEtherAllocation() inside grows the redeems reserve snapshot
399403 LIDO.collectRewardsAndProcessWithdrawals (
400404 _report.timestamp,
401405 _report.clValidatorsBalance + _report.clPendingBalance,
@@ -407,8 +411,8 @@ contract Accounting {
407411 _update.etherToFinalizeWQ
408412 );
409413
410- // Replenish RedeemsReserveVault: push/pull ETH to match target
411- _replenishRedeemsReserveVault ();
414+ // Push new reserve to buffer (bufferedEther NOT decremented — soft-reserved)
415+ _pushRedeemsReserveToBuffer ();
412416
413417 if (_update.sharesToMintAsFees > 0 ) {
414418 // this is a final action that changes share rate.
@@ -437,70 +441,45 @@ contract Accounting {
437441 );
438442 }
439443
440- /// @dev Reads redemption counters from the vault. Returns (0, 0) if no vault configured.
444+ /// @dev Reads redemption counters. Shares from Burner redeem track, ether from buffer.
445+ /// Returns (0, 0) if no buffer configured.
441446 function _getRedeemedCounters () internal view returns (uint256 redeemedShares , uint256 redeemedEther ) {
442- address vault = LIDO.getRedeemsReserveVault ();
443- if (vault == address (0 )) return (0 , 0 );
447+ address buffer = LIDO.getRedeemsBuffer ();
448+ if (buffer == address (0 )) return (0 , 0 );
444449
445- redeemedShares = IRedeemsReserveVault (vault). getRedeemedShares ();
446- redeemedEther = IRedeemsReserveVault (vault ).getRedeemedEther ();
450+ redeemedShares = IBurner (LIDO_LOCATOR. burner ()). getRedeemSharesRequestedToBurn ();
451+ redeemedEther = IRedeemsBuffer (buffer ).getRedeemedEther ();
447452 }
448453
449- /// @dev Flushes accumulated redemption shares from vault to Burner.
450- /// Called before commitSharesToBurn so redeemed shares are included in the burn.
451- function _flushVaultSharesToBurner () internal {
452- address vault = LIDO.getRedeemsReserveVault ();
453- if (vault == address (0 )) return ;
454+ /// @dev ETH round-trip: withdraw unredeemed ETH from buffer back to Lido,
455+ /// then reconcile bufferedEther by subtracting redeemedEther.
456+ /// After this call: bufferedEther == Lido.balance (clean state).
457+ function _withdrawAndReconcileRedeemsBuffer () internal {
458+ address buffer = LIDO.getRedeemsBuffer ();
459+ if (buffer == address (0 )) return ;
454460
455- IRedeemsReserveVault (vault).flushSharesToBurner ();
456- }
461+ uint256 redeemedEther = IRedeemsBuffer (buffer).getRedeemedEther ();
457462
458- /// @dev Reconciles the RedeemsReserveVault tracked ETH to the actual vault balance
459- /// and resets the redeemed ether counter.
460- function _reconcileRedeemsReserveVault () internal {
461- address vault = LIDO.getRedeemsReserveVault ();
462- if (vault == address (0 )) return ;
463+ // Withdraw unredeemed ETH back to Lido (resets buffer counters)
464+ IRedeemsBuffer (buffer).withdrawUnredeemed ();
463465
464- LIDO. reconcileRedeemsReserveVault (vault.balance);
465- IRedeemsReserveVault (vault). resetRedeemedEther ( );
466+ // Reconcile: bufferedEther -= redeemedEther
467+ LIDO. reconcileRedeemedEther (redeemedEther );
466468 }
467469
468- /// @dev Replenishes or drains the RedeemsReserveVault to match the reserve target.
469- /// Called after collectRewardsAndProcessWithdrawals when the buffer is finalized.
470- /// Fills to target from unreserved surplus first. When surplus is insufficient,
471- /// splits the shared allocation (withdrawalsReserve + unreserved) by growthShareBP.
472- function _replenishRedeemsReserveVault () internal {
473- address vault = LIDO.getRedeemsReserveVault ();
474- if (vault == address (0 )) return ;
475-
476- uint256 target = LIDO.getRedeemsReserveTarget ();
477- uint256 actual = vault.balance;
478-
479- if (target > actual) {
480- uint256 deficit = target - actual;
481- uint256 depositableEther = LIDO.getDepositableEther ();
482- uint256 toPush;
483-
484- if (depositableEther >= deficit) {
485- toPush = deficit;
486- } else {
487- uint256 shareBP = LIDO.getRedeemsReserveGrowthShare ();
488- if (shareBP == 0 ) {
489- toPush = depositableEther;
490- } else {
491- uint256 withdrawalsReserve = LIDO.getWithdrawalsReserve ();
492- uint256 sharedAllocation = withdrawalsReserve + depositableEther;
493- uint256 reserveShare = sharedAllocation * shareBP / TOTAL_BASIS_POINTS;
494- toPush = reserveShare > depositableEther ? reserveShare : depositableEther;
495- if (toPush > deficit) toPush = deficit;
496- }
497- }
498-
499- if (toPush > 0 ) {
500- LIDO.pushToRedeemsReserveVault (toPush);
501- }
502- } else if (actual > target) {
503- LIDO.pullFromRedeemsReserveVault (actual - target);
470+ /// @dev After standard flow + _updateBufferedEtherAllocation() grew the reserve,
471+ /// push the new reserve amount to the buffer.
472+ /// bufferedEther is NOT decremented — ETH is soft-reserved.
473+ /// @dev After collectRewardsAndProcessWithdrawals, _updateBufferedEtherAllocation()
474+ /// has set the new REDEEMS_RESERVE_POSITION via _growRedeemsReserve().
475+ /// Push that amount to the buffer. bufferedEther is NOT decremented.
476+ function _pushRedeemsReserveToBuffer () internal {
477+ address buffer = LIDO.getRedeemsBuffer ();
478+ if (buffer == address (0 )) return ;
479+
480+ uint256 toPush = LIDO.getRedeemsReserve ();
481+ if (toPush > 0 ) {
482+ LIDO.pushToRedeemsBuffer (toPush);
504483 }
505484 }
506485
0 commit comments