Skip to content

Commit 901e8c1

Browse files
committed
feat: wip
1 parent 9e101d5 commit 901e8c1

20 files changed

+190
-229
lines changed

contracts/0.8.9/Accounting.sol

Lines changed: 20 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ import {IPostTokenRebaseReceiver} from "./interfaces/IPostTokenRebaseReceiver.so
1616
import {WithdrawalQueue} from "./WithdrawalQueue.sol";
1717

1818
interface IRedeemsBuffer {
19-
function getRedeemedEther() external view returns (uint256);
20-
function withdrawUnredeemed() external;
19+
function getSettledRedeemedEther() external view returns (uint256);
20+
function getSettledRedeemedShares() external view returns (uint256);
2121
}
2222

2323
interface IStakingRouter {
@@ -194,35 +194,34 @@ contract Accounting {
194194
// Principal CL balance is sum of previous balances and new deposits
195195
update.principalClBalance = _pre.clValidatorsBalance + _pre.clPendingBalance + _pre.depositedBalance;
196196

197-
// Read redemption counters from buffer (on-chain, includes all redemptions)
197+
// Read settled (pre-refSlot) redemption counters from buffer on-chain.
198+
// Bucketing in RedeemsBuffer guarantees these are stable between refSlot and delivery.
198199
(uint256 redeemedShares, uint256 redeemedEther) = _getRedeemedCounters();
199200
update.redeemedEther = redeemedEther;
201+
update.redeemSharesToBurn = redeemedShares;
200202

201203
// Limit the rebase to avoid oracle frontrunning.
202204
// The base is reduced by redeemed ether AND shares so the limiter sees
203205
// a rate-neutral pre-state (as if the redeem never happened).
204-
// Without the shares adjustment the pre-share-rate fed to the limiter
205-
// would be artificially low, shrinking the shares-burn budget.
206+
// sharesRequestedToBurn from oracle includes redeem shares (nonCover on Burner) —
207+
// subtract them so the limiter only governs cover + regular nonCover.
206208
(
207209
update.withdrawalsVaultTransfer,
208210
update.elRewardsVaultTransfer,
209211
update.sharesToBurnForWithdrawals,
210-
update.totalSharesToBurn // shares to burn from Burner balance (WQ + cover)
212+
update.totalSharesToBurn
211213
) = _contracts.oracleReportSanityChecker.smoothenTokenRebase(
212214
_pre.totalPooledEther - _pre.externalEther - redeemedEther,
213215
_pre.totalShares - _pre.externalShares - redeemedShares,
214216
update.principalClBalance,
215217
_report.clValidatorsBalance + _report.clPendingBalance,
216218
_report.withdrawalVaultBalance,
217219
_report.elRewardsVaultBalance,
218-
_report.sharesRequestedToBurn,
220+
_report.sharesRequestedToBurn - redeemedShares,
219221
update.etherToFinalizeWQ,
220222
update.sharesToFinalizeWQ
221223
);
222224

223-
// Redeem shares are burned via a separate commit (outside the rebase limiter, rate-neutral)
224-
update.redeemSharesToBurn = redeemedShares;
225-
226225
uint256 postInternalSharesBeforeFees = _pre.totalShares -
227226
_pre.externalShares - // internal shares before
228227
update.totalSharesToBurn - // shares to be burned (WQ + cover)
@@ -392,15 +391,13 @@ contract Accounting {
392391
LIDO.internalizeExternalBadDebt(_pre.badDebtToInternalize);
393392
}
394393

395-
// Burn all redeem shares (rate-neutral, outside limiter)
396-
// Shares are already on Burner — sent during each redeem() call
397-
if (_update.redeemSharesToBurn > 0) {
398-
_contracts.burner.commitRedeemSharesToBurn();
399-
}
400-
401-
// Burn limiter-constrained cover/nonCover shares
402-
if (_update.totalSharesToBurn > 0) {
403-
_contracts.burner.commitSharesToBurn(_update.totalSharesToBurn);
394+
// Burn shares: limiter-constrained cover/nonCover + all settled redeem shares.
395+
// Redeem shares sit on Burner as nonCover — burned together in one call.
396+
{
397+
uint256 totalBurn = _update.totalSharesToBurn + _update.redeemSharesToBurn;
398+
if (totalBurn > 0) {
399+
_contracts.burner.commitSharesToBurn(totalBurn);
400+
}
404401
}
405402

406403
// collectRewardsAndProcessWithdrawals handles ETH round-trip internally:
@@ -444,14 +441,15 @@ contract Accounting {
444441
);
445442
}
446443

447-
/// @dev Reads redemption counters. Shares from Burner redeem track, ether from buffer.
444+
/// @dev Reads settled (pre-refSlot) redemption counters from RedeemsBuffer.
445+
/// Both values are bucketed on-chain — stable between refSlot and delivery.
448446
/// Returns (0, 0) if no buffer configured.
449447
function _getRedeemedCounters() internal view returns (uint256 redeemedShares, uint256 redeemedEther) {
450448
address buffer = LIDO.getRedeemsBuffer();
451449
if (buffer == address(0)) return (0, 0);
452450

453-
redeemedShares = IBurner(LIDO_LOCATOR.burner()).getRedeemSharesRequestedToBurn();
454-
redeemedEther = IRedeemsBuffer(buffer).getRedeemedEther();
451+
redeemedShares = IRedeemsBuffer(buffer).getSettledRedeemedShares();
452+
redeemedEther = IRedeemsBuffer(buffer).getSettledRedeemedEther();
455453
}
456454

457455
/// @dev checks the provided oracle data internally and against the sanity checker contract

contracts/0.8.9/Burner.sol

Lines changed: 3 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,6 @@ contract Burner is IBurner, AccessControlEnumerable, Versioned {
8181

8282
uint256 totalCoverSharesBurnt;
8383
uint256 totalNonCoverSharesBurnt;
84-
85-
uint256 redeemSharesBurnRequested;
86-
uint256 totalRedeemSharesBurnt;
8784
}
8885

8986
/// @custom:storage-location erc7201:Lido.Core.Burner.IsMigrationAllowed-v3Upgrade
@@ -119,16 +116,6 @@ contract Burner is IBurner, AccessControlEnumerable, Versioned {
119116
*/
120117
event StETHBurnt(bool indexed isCover, uint256 amountOfStETH, uint256 amountOfShares);
121118

122-
/**
123-
* Emitted when a new redeem burn request is added.
124-
*/
125-
event RedeemStETHBurnRequested(address indexed requestedBy, uint256 amountOfStETH, uint256 amountOfShares);
126-
127-
/**
128-
* Emitted when redeem shares are burnt.
129-
*/
130-
event RedeemStETHBurnt(uint256 amountOfStETH, uint256 amountOfShares);
131-
132119
/**
133120
* Emitted when the excessive stETH `amount` (corresponding to `amountOfShares` shares) recovered (i.e. transferred)
134121
* to the Lido treasure address by `requestedBy` sender.
@@ -195,7 +182,7 @@ contract Burner is IBurner, AccessControlEnumerable, Versioned {
195182
Storage storage $ = _storage();
196183
$.totalCoverSharesBurnt = oldBurner.getCoverSharesBurnt();
197184
$.totalNonCoverSharesBurnt = oldBurner.getNonCoverSharesBurnt();
198-
(uint256 coverShares, uint256 nonCoverShares,) = oldBurner.getSharesRequestedToBurn();
185+
(uint256 coverShares, uint256 nonCoverShares) = oldBurner.getSharesRequestedToBurn();
199186
$.coverSharesBurnRequested = coverShares;
200187
$.nonCoverSharesBurnRequested = nonCoverShares;
201188
}
@@ -293,29 +280,6 @@ contract Burner is IBurner, AccessControlEnumerable, Versioned {
293280
_requestBurn(_sharesAmountToBurn, stETHAmount, false /* _isCover */);
294281
}
295282

296-
/**
297-
* @notice BE CAREFUL, the provided stETH shares will be burnt permanently.
298-
*
299-
* Transfers `_sharesAmountToBurn` stETH shares from `_from` and irreversibly locks these
300-
* on the burner contract address. Marks the shares amount for burning on the isolated
301-
* redeem track (burned outside the rebase limiter).
302-
*
303-
* @param _from address to transfer shares from
304-
* @param _sharesAmountToBurn stETH shares to burn
305-
*/
306-
function requestBurnSharesForRedeem(
307-
address _from,
308-
uint256 _sharesAmountToBurn
309-
) external onlyRole(REQUEST_BURN_SHARES_ROLE) {
310-
if (_sharesAmountToBurn == 0) revert ZeroBurnAmount();
311-
312-
uint256 stETHAmount = LIDO.transferSharesFrom(_from, address(this), _sharesAmountToBurn);
313-
314-
emit RedeemStETHBurnRequested(msg.sender, stETHAmount, _sharesAmountToBurn);
315-
316-
_storage().redeemSharesBurnRequested += _sharesAmountToBurn;
317-
}
318-
319283
/**
320284
* Transfers the excess stETH amount (e.g. belonging to the burner contract address
321285
* but not marked for burning) to the Lido treasury address set upon the
@@ -428,35 +392,6 @@ contract Burner is IBurner, AccessControlEnumerable, Versioned {
428392
assert(sharesToBurnNow == _sharesToBurn);
429393
}
430394

431-
/**
432-
* Commit all pending redeem shares to burn. Burns everything — no budget argument.
433-
*
434-
* Redeem shares are burned outside the rebase limiter (rate-neutral),
435-
* so they are always fully burned on each oracle report.
436-
*
437-
* Increments `totalRedeemSharesBurnt` counter.
438-
* Resets `redeemSharesBurnRequested` counter.
439-
* Does nothing if zero shares are pending.
440-
*/
441-
function commitRedeemSharesToBurn() external virtual override {
442-
if (msg.sender != LOCATOR.accounting()) revert AppAuthFailed();
443-
444-
Storage storage $ = _storage();
445-
uint256 redeemShares = $.redeemSharesBurnRequested;
446-
447-
if (redeemShares == 0) {
448-
return;
449-
}
450-
451-
$.redeemSharesBurnRequested = 0;
452-
$.totalRedeemSharesBurnt += redeemShares;
453-
454-
uint256 stETHAmount = LIDO.getPooledEthByShares(redeemShares);
455-
emit RedeemStETHBurnt(stETHAmount, redeemShares);
456-
457-
LIDO.burnShares(redeemShares);
458-
}
459-
460395
/**
461396
* Returns the current amount of shares locked on the contract to be burnt.
462397
*/
@@ -465,12 +400,11 @@ contract Burner is IBurner, AccessControlEnumerable, Versioned {
465400
view
466401
virtual
467402
override
468-
returns (uint256 coverShares, uint256 nonCoverShares, uint256 redeemShares)
403+
returns (uint256 coverShares, uint256 nonCoverShares)
469404
{
470405
Storage storage $ = _storage();
471406
coverShares = $.coverSharesBurnRequested;
472407
nonCoverShares = $.nonCoverSharesBurnRequested;
473-
redeemShares = $.redeemSharesBurnRequested;
474408
}
475409

476410
/**
@@ -487,20 +421,6 @@ contract Burner is IBurner, AccessControlEnumerable, Versioned {
487421
return _storage().totalNonCoverSharesBurnt;
488422
}
489423

490-
/**
491-
* Returns the current amount of redeem shares locked on the contract to be burnt.
492-
*/
493-
function getRedeemSharesRequestedToBurn() external view virtual override returns (uint256) {
494-
return _storage().redeemSharesBurnRequested;
495-
}
496-
497-
/**
498-
* Returns the total redeem shares ever burnt.
499-
*/
500-
function getRedeemSharesBurnt() external view virtual override returns (uint256) {
501-
return _storage().totalRedeemSharesBurnt;
502-
}
503-
504424
/**
505425
* Returns the stETH amount belonging to the burner contract address but not marked for burning.
506426
*/
@@ -510,9 +430,7 @@ contract Burner is IBurner, AccessControlEnumerable, Versioned {
510430

511431
function _getExcessStETHShares() internal view returns (uint256) {
512432
Storage storage $ = _storage();
513-
uint256 sharesBurnRequested = $.coverSharesBurnRequested
514-
+ $.nonCoverSharesBurnRequested
515-
+ $.redeemSharesBurnRequested;
433+
uint256 sharesBurnRequested = ($.coverSharesBurnRequested + $.nonCoverSharesBurnRequested);
516434
uint256 totalShares = LIDO.sharesOf(address(this));
517435

518436
// sanity check, don't revert

0 commit comments

Comments
 (0)