11import  {  expect  }  from  "chai" ; 
2+ import  {  ContractTransactionReceipt ,  ZeroAddress  }  from  "ethers" ; 
23import  {  ethers  }  from  "hardhat" ; 
34
45import  {  HardhatEthersSigner  }  from  "@nomicfoundation/hardhat-ethers/signers" ; 
6+ import  {  setBalance  }  from  "@nomicfoundation/hardhat-network-helpers" ; 
57
68import  {  Dashboard ,  StakingVault  }  from  "typechain-types" ; 
79
8- import  {  MAX_UINT256  }  from  "lib" ; 
10+ import  {  MAX_UINT256 ,   ONE_GWEI  }  from  "lib" ; 
911import  { 
1012  changeTier , 
1113  createVaultWithDashboard , 
1214  DEFAULT_TIER_PARAMS , 
15+   finalizeWQViaElVault , 
1316  getProtocolContext , 
17+   getReportTimeElapsed , 
1418  ProtocolContext , 
1519  report , 
1620  reportVaultDataWithProof , 
@@ -36,9 +40,13 @@ describe("Integration: Vault with bad debt", () => {
3640
3741  before ( async  ( )  =>  { 
3842    ctx  =  await  getProtocolContext ( ) ; 
39-     const  {  lido,  stakingVaultFactory,  vaultHub }  =  ctx . contracts ; 
43+     const  {  lido,  stakingVaultFactory,  vaultHub,  elRewardsVault  }  =  ctx . contracts ; 
4044    originalSnapshot  =  await  Snapshot . take ( ) ; 
4145
46+     await  waitNextAvailableReportTime ( ctx ) ; 
47+     await  finalizeWQViaElVault ( ctx ) ; 
48+     await  setBalance ( elRewardsVault . address ,  0 ) ; 
49+ 
4250    [ ,  owner ,  nodeOperator ,  otherOwner ,  daoAgent ]  =  await  ethers . getSigners ( ) ; 
4351    await  setupLidoForVaults ( ctx ) ; 
4452
@@ -74,6 +82,12 @@ describe("Integration: Vault with bad debt", () => {
7482    await  vaultHub . connect ( await  ctx . getSigner ( "agent" ) ) . grantRole ( await  vaultHub . BAD_DEBT_MASTER_ROLE ( ) ,  daoAgent ) ; 
7583  } ) ; 
7684
85+   const  getFirstEvent  =  ( receipt : ContractTransactionReceipt ,  eventName : string )  =>  { 
86+     const  events  =  ctx . getEvents ( receipt ,  eventName ) ; 
87+     expect ( events . length ) . to . be . greaterThan ( 0 ) ; 
88+     return  events [ 0 ] ; 
89+   } ; 
90+ 
7791  beforeEach ( async  ( )  =>  ( snapshot  =  await  Snapshot . take ( ) ) ) ; 
7892  afterEach ( async  ( )  =>  await  Snapshot . restore ( snapshot ) ) ; 
7993  after ( async  ( )  =>  await  Snapshot . restore ( originalSnapshot ) ) ; 
@@ -233,4 +247,95 @@ describe("Integration: Vault with bad debt", () => {
233247      expect ( await  vaultHub . badDebtToInternalize ( ) ) . to . be . equal ( 0n ) ; 
234248    } ) ; 
235249  } ) ; 
250+ 
251+   describe ( "Report simulation (accounting)" ,  ( )  =>  { 
252+     it ( "simulateOracleReport result matches handleOracleReport while bad debt" ,  async  ( )  =>  { 
253+       const  {  lido,  hashConsensus,  accounting,  elRewardsVault,  withdrawalVault,  withdrawalQueue,  vaultHub }  = 
254+         ctx . contracts ; 
255+ 
256+       const  clRebase  =  ether ( "50" ) ; 
257+       const  elRewards  =  ether ( "100" ) ; 
258+       const  withdrawalVaultBalance  =  ether ( "100" ) ; 
259+       const  withdrawalRequestAmount  =  ether ( "20" ) ; 
260+ 
261+       await  lido . connect ( otherOwner ) . submit ( ZeroAddress ,  {  value : withdrawalRequestAmount  } ) ; 
262+       await  lido . connect ( otherOwner ) . approve ( withdrawalQueue . address ,  withdrawalRequestAmount ) ; 
263+       await  withdrawalQueue . connect ( otherOwner ) . requestWithdrawals ( [ withdrawalRequestAmount ] ,  otherOwner . address ) ; 
264+       const  withdrawalRequestId  =  await  withdrawalQueue . getLastRequestId ( ) ; 
265+ 
266+       await  setBalance ( elRewardsVault . address ,  elRewards ) ; 
267+       await  setBalance ( withdrawalVault . address ,  withdrawalVaultBalance ) ; 
268+ 
269+       const  badDebtShares  = 
270+         ( await  dashboard . liabilityShares ( ) )  -  ( await  lido . getSharesByPooledEth ( await  dashboard . totalValue ( ) ) ) ; 
271+       await  vaultHub . connect ( daoAgent ) . internalizeBadDebt ( stakingVault ,  badDebtShares ) ; 
272+ 
273+       const  refSlot  =  ( await  hashConsensus . getCurrentFrame ( ) ) . refSlot ; 
274+       const  {  genesisTime,  secondsPerSlot }  =  await  hashConsensus . getChainConfig ( ) ; 
275+       const  reportTimestamp  =  genesisTime  +  refSlot  *  secondsPerSlot ; 
276+       const  {  timeElapsed }  =  await  getReportTimeElapsed ( ctx ) ; 
277+ 
278+       const  params  =  {  clDiff : clRebase ,  reportElVault : true ,  reportWithdrawalsVault : true ,  dryRun : true  } ; 
279+       const  {  data : reportData  }  =  await  report ( ctx ,  params ) ; 
280+ 
281+       const  externalSharesBefore  =  await  lido . getExternalShares ( ) ; 
282+       const  totalSharesBefore  =  await  lido . getTotalShares ( ) ; 
283+       const  internalSharesBefore  =  totalSharesBefore  -  externalSharesBefore ; 
284+ 
285+       const  elRewardsBalanceBefore  =  await  ethers . provider . getBalance ( elRewardsVault ) ; 
286+       const  withdrawalVaultBalanceBefore  =  await  ethers . provider . getBalance ( withdrawalVault ) ; 
287+ 
288+       const  simulated  =  await  accounting . simulateOracleReport ( { 
289+         timestamp : reportTimestamp , 
290+         timeElapsed, 
291+         clValidators : reportData . numValidators , 
292+         clBalance : BigInt ( reportData . clBalanceGwei )  *  ONE_GWEI , 
293+         withdrawalVaultBalance : reportData . withdrawalVaultBalance , 
294+         elRewardsVaultBalance : reportData . elRewardsVaultBalance , 
295+         sharesRequestedToBurn : reportData . sharesRequestedToBurn , 
296+         withdrawalFinalizationBatches : reportData . withdrawalFinalizationBatches , 
297+         simulatedShareRate : reportData . simulatedShareRate , 
298+       } ) ; 
299+ 
300+       const  {  reportTx }  =  await  report ( ctx ,  {  ...params ,  dryRun : false  } ) ; 
301+ 
302+       const  reportTxReceipt  =  await  reportTx ! . wait ( ) ; 
303+       const  tokenRebasedEvent  =  getFirstEvent ( reportTxReceipt ! ,  "TokenRebased" ) ; 
304+ 
305+       expect ( simulated . preTotalShares ) . to . equal ( tokenRebasedEvent . args . preTotalShares ) ; 
306+       expect ( simulated . preTotalPooledEther ) . to . equal ( tokenRebasedEvent . args . preTotalEther ) ; 
307+       expect ( simulated . postTotalShares ) . to . equal ( tokenRebasedEvent . args . postTotalShares ) ; 
308+       expect ( simulated . postTotalPooledEther ) . to . equal ( tokenRebasedEvent . args . postTotalEther ) ; 
309+ 
310+       const  externalSharesAfter  =  await  lido . getExternalShares ( ) ; 
311+       const  totalSharesAfter  =  await  lido . getTotalShares ( ) ; 
312+       const  totalPooledEtherAfter  =  await  lido . getTotalPooledEther ( ) ; 
313+ 
314+       const  elRewardsBalanceAfter  =  await  ethers . provider . getBalance ( elRewardsVault ) ; 
315+       const  withdrawalVaultBalanceAfter  =  await  ethers . provider . getBalance ( withdrawalVault ) ; 
316+ 
317+       expect ( elRewardsBalanceBefore  -  simulated . elRewardsVaultTransfer ) . to . equal ( elRewardsBalanceAfter ) ; 
318+       expect ( withdrawalVaultBalanceBefore  -  simulated . withdrawalsVaultTransfer ) . to . equal ( withdrawalVaultBalanceAfter ) ; 
319+ 
320+       const  [ withdrawalRequestData ]  =  await  withdrawalQueue . getWithdrawalStatus ( [ withdrawalRequestId ] ) ; 
321+       const  actualBadDebtInternalized  =  externalSharesBefore  -  externalSharesAfter ; 
322+ 
323+       expect ( simulated . etherToFinalizeWQ ) . to . equal ( withdrawalRequestAmount ) ; 
324+       expect ( simulated . etherToFinalizeWQ ) . to . equal ( withdrawalRequestData . amountOfStETH ) ; 
325+       expect ( simulated . sharesToFinalizeWQ ) . to . equal ( withdrawalRequestData . amountOfShares ) ; 
326+       expect ( simulated . sharesToBurnForWithdrawals ) . to . equal ( withdrawalRequestData . amountOfShares ) ; 
327+       expect ( simulated . totalSharesToBurn ) . to . equal ( totalSharesBefore  -  totalSharesAfter  +  simulated . sharesToMintAsFees ) ; 
328+ 
329+       expect ( simulated . postInternalShares ) . to . equal ( totalSharesAfter  -  externalSharesAfter ) ; 
330+       expect ( simulated . postInternalShares ) . to . equal ( 
331+         internalSharesBefore  -  simulated . totalSharesToBurn  +  simulated . sharesToMintAsFees  +  actualBadDebtInternalized , 
332+       ) ; 
333+ 
334+       expect ( simulated . postInternalEther ) . to . equal ( totalPooledEtherAfter  -  ( await  lido . getExternalEther ( ) ) ) ; 
335+       expect ( simulated . sharesToMintAsFees ) . to . equal ( tokenRebasedEvent . args . sharesMintedAsFees ) ; 
336+ 
337+       const  elRewardsReceived  =  ctx . getEvents ( reportTxReceipt ! ,  "ELRewardsReceived" ) ; 
338+       expect ( simulated . elRewardsVaultTransfer ) . to . equal ( elRewardsReceived [ 0 ] . args . amount ) ; 
339+     } ) ; 
340+   } ) ; 
236341} ) ; 
0 commit comments