Accounting
Handles oracle reports and calculates protocol state changes including rebases, fee distribution, and stVault bad debt internalization.
What is Accounting?​
Accounting is the core contract that processes oracle reports for Lido:
- receives oracle reports from
AccountingOracle - calculates share rate changes and token rebases
- distributes protocol fees to staking modules and treasury
- finalizes withdrawal requests
- internalizes bad debt from stVaults via
VaultHub - notifies external contracts about rebases
The contract acts as the central point for all accounting operations, replacing the previous handleOracleReport logic that was in the Lido contract.
How it works​
HashConsensusreaches consensus on the accounting report hash.AccountingOracle.submitReportData()validates the sender, contract version, consensus version, and report hash.AccountingOraclereports exited validator counts toStakingRouter.AccountingOraclecallsWithdrawalQueue.onOracleReport()to update bunker mode and timing bounds.AccountingOraclecallsAccounting.handleOracleReport().- Accounting snapshots pre-report state and simulates the report (including
WithdrawalQueue.prefinalize()andOracleReportSanityChecker.smoothenTokenRebase()). - Accounting runs sanity checks via
OracleReportSanityChecker.checkAccountingOracleReport(). - Accounting calls
Burner.requestBurnShares()to lock shares for withdrawal finalization (if needed). - Accounting updates CL state via
Lido.processClStateUpdate(). - Accounting internalizes bad debt via
VaultHub.decreaseInternalizedBadDebt()andLido.internalizeExternalBadDebt(). - Accounting calls
Burner.commitSharesToBurn()(if needed). - Accounting calls
Lido.collectRewardsAndProcessWithdrawals()to process withdrawals and rewards. - If fees are due, Accounting mints fee shares, distributes them, and calls
StakingRouter.reportRewardsMinted(). - Accounting notifies the post-rebase receiver and calls
Lido.emitTokenRebase(). AccountingOracleupdatesLazyOracle.updateReportData()and stores extra-data processing state.
Structs​
ReportValues​
Oracle report input data (defined in contracts/common/interfaces/ReportValues.sol):
struct ReportValues {
uint256 timestamp; // Block timestamp when the report is based
uint256 timeElapsed; // Duration since the previous report
uint256 clValidators; // Total count of Lido validators on CL
uint256 clBalance; // Combined balance of all Lido validators on CL
uint256 withdrawalVaultBalance; // Current withdrawal vault holdings
uint256 elRewardsVaultBalance; // Execution Layer rewards vault holdings
uint256 sharesRequestedToBurn; // stETH shares marked for burning via Burner
uint256[] withdrawalFinalizationBatches; // Sorted array of withdrawal request IDs
uint256 simulatedShareRate; // Projected share rate value
}
PreReportState​
Snapshot of protocol state before report processing (internal struct):
struct PreReportState {
uint256 clValidators; // Number of CL validators before report
uint256 clBalance; // CL balance before report
uint256 totalPooledEther; // Total pooled ether before report
uint256 totalShares; // Total shares before report
uint256 depositedValidators; // Number of deposited validators
uint256 externalShares; // Shares backed by external vaults
uint256 externalEther; // Ether in external vaults
uint256 badDebtToInternalize; // Bad debt amount to internalize this report
}
CalculatedValues​
Computed state changes from a report:
struct CalculatedValues {
uint256 withdrawalsVaultTransfer; // ETH to transfer from withdrawal vault
uint256 elRewardsVaultTransfer; // ETH to transfer from EL rewards vault
uint256 etherToFinalizeWQ; // ETH needed to finalize withdrawal queue
uint256 sharesToFinalizeWQ; // Shares to finalize withdrawal queue
uint256 sharesToBurnForWithdrawals; // Shares to burn for withdrawals
uint256 totalSharesToBurn; // Total shares to be burned
uint256 sharesToMintAsFees; // Shares to mint as protocol fees
FeeDistribution feeDistribution; // Fee distribution details
uint256 principalClBalance; // Principal CL balance
uint256 preTotalShares; // Total shares before update
uint256 preTotalPooledEther; // Total pooled ETH before update
uint256 postInternalShares; // Internal shares after update
uint256 postInternalEther; // Internal ETH after update
uint256 postTotalShares; // Total shares after update
uint256 postTotalPooledEther; // Total pooled ETH after update
}
FeeDistribution​
Protocol fee allocation:
struct FeeDistribution {
address[] moduleFeeRecipients; // Addresses receiving module fees
uint256[] moduleIds; // IDs of staking modules
uint256[] moduleSharesToMint; // Shares to mint for each module
uint256 treasurySharesToMint; // Shares to mint for treasury
}
View methods​
simulateOracleReport(ReportValues _report)​
function simulateOracleReport(
ReportValues calldata _report
) external view returns (CalculatedValues memory)
Simulates an oracle report without applying changes. Returns calculated state changes that would result from the report. Used by oracle daemons to compute the simulated share rate before submitting.
Note: For simulation, uses vaultHub.badDebtToInternalize() to fetch the current bad debt value, whereas actual reports use badDebtToInternalizeForLastRefSlot().
Methods​
handleOracleReport(ReportValues _report)​
function handleOracleReport(ReportValues calldata _report) external
Handles an oracle report and applies all calculated state changes to the protocol. Can only be called by the AccountingOracle contract.
The method performs these operations in order:
- Runs sanity checks on report data.
- Requests
Burner.requestBurnShares()for withdrawal queue finalization (if applicable). - Updates consensus layer state on Lido via
processClStateUpdate(). - Internalizes bad debt (calls
VaultHub.decreaseInternalizedBadDebt()andLido.internalizeExternalBadDebt()). - Commits shares to burn via
Burner.commitSharesToBurn(). - Collects EL rewards and processes withdrawals via
Lido.collectRewardsAndProcessWithdrawals(). - If fees are due: mints fee shares, distributes fees, then calls
StakingRouter.reportRewardsMinted(). - Notifies rebase observers via
handlePostTokenRebase(). - Emits token rebase event via
emitTokenRebase().
Errors​
error NotAuthorized(string operation, address addr);
error IncorrectReportTimestamp(uint256 reportTimestamp, uint256 upperBoundTimestamp);
error IncorrectReportValidators(uint256 reportValidators, uint256 minValidators, uint256 maxValidators);
error InternalSharesCantBeZero();