1
1
import { expect } from "chai" ;
2
- import { ContractTransactionReceipt , formatEther , ZeroAddress } from "ethers" ;
2
+ import { ContractTransactionReceipt , ZeroAddress } from "ethers" ;
3
3
import { ethers } from "hardhat" ;
4
4
5
5
import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers" ;
6
+ import { setBalance } from "@nomicfoundation/hardhat-network-helpers" ;
6
7
7
8
import {
8
9
ACL ,
@@ -22,7 +23,6 @@ import { TierParamsStruct } from "typechain-types/contracts/0.8.25/vaults/Operat
22
23
23
24
import {
24
25
advanceChainTime ,
25
- BigIntMath ,
26
26
certainAddress ,
27
27
days ,
28
28
ether ,
@@ -141,27 +141,6 @@ describe("VaultHub.sol:hub", () => {
141
141
) ;
142
142
}
143
143
144
- async function printRecord ( vault : StakingVault__MockForVaultHub ) {
145
- const record = await vaultHub . vaultRecord ( vault ) ;
146
- console . log ( "vaultRecord" , {
147
- report : {
148
- totalValue : formatEther ( record . report . totalValue ) ,
149
- inOutDelta : formatEther ( record . report . inOutDelta ) ,
150
- timestamp : record . report . timestamp ,
151
- } ,
152
- maxLiabilityShares : formatEther ( record . maxLiabilityShares ) ,
153
- liabilityShares : formatEther ( record . liabilityShares ) ,
154
- inOutDelta : {
155
- value : formatEther ( record . inOutDelta [ 0 ] . value ) ,
156
- valueOnRefSlot : formatEther ( record . inOutDelta [ 0 ] . valueOnRefSlot ) ,
157
- refSlot : record . inOutDelta [ 0 ] . refSlot ,
158
- value2 : formatEther ( record . inOutDelta [ 1 ] . value ) ,
159
- valueOnRefSlot2 : formatEther ( record . inOutDelta [ 1 ] . valueOnRefSlot ) ,
160
- refSlot2 : record . inOutDelta [ 1 ] . refSlot ,
161
- } ,
162
- } ) ;
163
- }
164
-
165
144
before ( async ( ) => {
166
145
[ deployer , user , stranger , whale ] = await ethers . getSigners ( ) ;
167
146
@@ -345,73 +324,6 @@ describe("VaultHub.sol:hub", () => {
345
324
expect ( await vaultHub . isVaultHealthy ( vaultAddress ) ) . to . equal ( true ) ;
346
325
} ) ;
347
326
348
- // Looks like fuzzing but it's not [: }
349
- it . skip ( "returns correct value for various parameters" , async ( ) => {
350
- const tbi = ( n : number | bigint , min : number = 1 ) => BigInt ( Math . floor ( Math . random ( ) * Number ( n ) ) + min ) ;
351
-
352
- for ( let i = 0 ; i < 50 ; i ++ ) {
353
- const snapshot = await Snapshot . take ( ) ;
354
- const forcedRebalanceThresholdBP = tbi ( 10000 ) ;
355
- const reserveRatioBP = BigIntMath . min ( forcedRebalanceThresholdBP + tbi ( 100 ) , TOTAL_BASIS_POINTS - 1n ) ;
356
-
357
- const totalValueEth = tbi ( 100 ) ;
358
- const totalValue = ether ( totalValueEth . toString ( ) ) ;
359
-
360
- const mintable = ( totalValue * ( TOTAL_BASIS_POINTS - reserveRatioBP ) ) / TOTAL_BASIS_POINTS ;
361
-
362
- const isSlashing = Math . random ( ) < 0.5 ;
363
- const slashed = isSlashing ? ether ( tbi ( totalValueEth ) . toString ( ) ) : 0n ;
364
- const threshold =
365
- ( ( totalValue - slashed ) * ( TOTAL_BASIS_POINTS - forcedRebalanceThresholdBP ) ) / TOTAL_BASIS_POINTS ;
366
- const expectedHealthy = threshold >= mintable ;
367
-
368
- const { vault } = await createAndConnectVault ( vaultFactory , {
369
- shareLimit : ether ( "100" ) , // just to bypass the share limit check
370
- reserveRatioBP,
371
- forcedRebalanceThresholdBP,
372
- } ) ;
373
-
374
- await vault . fund ( { value : totalValue } ) ;
375
-
376
- await printRecord ( vault ) ;
377
-
378
- let sharesToMint = 0n ;
379
- if ( mintable > 0n ) {
380
- sharesToMint = await lido . getSharesByPooledEth ( mintable ) ;
381
- await reportVault ( { vault } ) ;
382
- await vaultHub . connect ( user ) . mintShares ( vault , user , sharesToMint ) ;
383
- await printRecord ( vault ) ;
384
- }
385
-
386
- // simulate slashing
387
- await reportVault ( {
388
- vault,
389
- totalValue : totalValue - slashed ,
390
- inOutDelta : totalValue ,
391
- liabilityShares : sharesToMint ,
392
- } ) ;
393
- console . log ( "vaultRecord" , await vaultHub . vaultRecord ( vault ) ) ;
394
-
395
- try {
396
- const actualHealthy = await vaultHub . isVaultHealthy ( vault ) ;
397
- expect ( actualHealthy ) . to . equal ( expectedHealthy ) ;
398
- } catch ( error ) {
399
- console . log ( `Test failed with parameters:
400
- Rebalance Threshold: ${ forcedRebalanceThresholdBP }
401
- Reserve Ratio: ${ reserveRatioBP }
402
- Total Value: ${ totalValue } ETH
403
- Minted: ${ mintable } stETH
404
- Slashed: ${ slashed } ETH
405
- Threshold: ${ threshold } stETH
406
- Expected Healthy: ${ expectedHealthy }
407
- ` ) ;
408
- throw error ;
409
- }
410
-
411
- await Snapshot . restore ( snapshot ) ;
412
- }
413
- } ) ;
414
-
415
327
it ( "returns correct value close to the threshold border cases at 1:1 share rate" , async ( ) => {
416
328
const config = {
417
329
shareLimit : ether ( "100" ) , // just to bypass the share limit check
@@ -608,13 +520,7 @@ describe("VaultHub.sol:hub", () => {
608
520
forcedRebalanceThresholdBP : 50_00n , // 50%
609
521
} ) ;
610
522
611
- await reportVault ( { vault, totalValue : ether ( "50" ) , inOutDelta : ether ( "50" ) } ) ;
612
-
613
- const burner = await impersonate ( await locator . burner ( ) , ether ( "1" ) ) ;
614
- await lido . connect ( whale ) . transfer ( burner , ether ( "1" ) ) ;
615
- await lido . connect ( burner ) . burnShares ( ether ( "1" ) ) ;
616
-
617
- expect ( await vaultHub . healthShortfallShares ( vault ) ) . to . equal ( ether ( "0" ) ) ;
523
+ expect ( await vaultHub . healthShortfallShares ( vault ) ) . to . equal ( 0n ) ;
618
524
} ) ;
619
525
620
526
it ( "returns 0 when minted small amount of stETH and vault is healthy" , async ( ) => {
@@ -630,10 +536,6 @@ describe("VaultHub.sol:hub", () => {
630
536
const sharesToMint = await lido . getSharesByPooledEth ( mintingEth ) ;
631
537
await vaultHub . connect ( user ) . mintShares ( vault , user , sharesToMint ) ;
632
538
633
- const burner = await impersonate ( await locator . burner ( ) , ether ( "1" ) ) ;
634
- await lido . connect ( whale ) . transfer ( burner , ether ( "1" ) ) ;
635
- await lido . connect ( burner ) . burnShares ( ether ( "1" ) ) ;
636
-
637
539
expect ( await vaultHub . isVaultHealthy ( vault ) ) . to . equal ( true ) ;
638
540
expect ( await vaultHub . healthShortfallShares ( vault ) ) . to . equal ( 0n ) ;
639
541
} ) ;
@@ -693,6 +595,81 @@ describe("VaultHub.sol:hub", () => {
693
595
} ) ;
694
596
} ) ;
695
597
598
+ context ( "obligationsShortfallValue" , ( ) => {
599
+ it ( "does not revert when vault address is correct" , async ( ) => {
600
+ const { vault } = await createAndConnectVault ( vaultFactory , {
601
+ shareLimit : ether ( "100" ) , // just to bypass the share limit check
602
+ reserveRatioBP : 50_00n , // 50%
603
+ forcedRebalanceThresholdBP : 50_00n , // 50%
604
+ } ) ;
605
+
606
+ await expect ( vaultHub . obligationsShortfallValue ( vault ) ) . not . to . be . reverted ;
607
+ } ) ;
608
+
609
+ it ( "does not revert when vault address is ZeroAddress" , async ( ) => {
610
+ const zeroAddress = ethers . ZeroAddress ;
611
+ await expect ( vaultHub . obligationsShortfallValue ( zeroAddress ) ) . not . to . be . reverted ;
612
+ } ) ;
613
+
614
+ it ( "different cases when vault is healthy, unhealthy and minted > totalValue, and fees are > MIN_BEACON_DEPOSIT" , async ( ) => {
615
+ const { vault } = await createAndConnectVault ( vaultFactory , {
616
+ shareLimit : ether ( "100" ) , // just to bypass the share limit check
617
+ reserveRatioBP : 10_00n , // 10%
618
+ forcedRebalanceThresholdBP : 9_00n , // 9%
619
+ } ) ;
620
+
621
+ await vaultHub . connect ( user ) . fund ( vault , { value : ether ( "1" ) } ) ;
622
+
623
+ await reportVault ( { vault, totalValue : ether ( "2" ) , inOutDelta : ether ( "2" ) } ) ;
624
+
625
+ await vaultHub . connect ( user ) . mintShares ( vault , user , ether ( "0.25" ) ) ;
626
+
627
+ await reportVault ( { vault, totalValue : ether ( "0.5" ) } ) ; // at the threshold
628
+ expect ( await vaultHub . isVaultHealthy ( vault ) ) . to . equal ( true ) ;
629
+ expect ( await vaultHub . obligationsShortfallValue ( vault ) ) . to . equal ( 0n ) ;
630
+
631
+ const balanceBefore = await ethers . provider . getBalance ( vault ) ;
632
+ await setBalance ( await vault . getAddress ( ) , 0n ) ;
633
+ // below the threshold, but with fees
634
+ await reportVault ( { vault, totalValue : ether ( "0.5" ) - 1n , cumulativeLidoFees : ether ( "1" ) } ) ;
635
+ expect ( await vaultHub . isVaultHealthy ( vault ) ) . to . equal ( true ) ;
636
+ expect ( await vaultHub . obligationsShortfallValue ( vault ) ) . to . equal ( ether ( "1" ) ) ;
637
+
638
+ await setBalance ( await vault . getAddress ( ) , balanceBefore ) ;
639
+ await reportVault ( { vault, totalValue : 0n } ) ; // minted > totalValue
640
+ expect ( await vaultHub . isVaultHealthy ( vault ) ) . to . equal ( false ) ;
641
+ expect ( await vaultHub . obligationsShortfallValue ( vault ) ) . to . equal ( MAX_UINT256 ) ;
642
+ } ) ;
643
+
644
+ it ( "returns correct value for rebalance vault" , async ( ) => {
645
+ const { vault } = await createAndConnectVault ( vaultFactory , {
646
+ shareLimit : ether ( "100" ) , // just to bypass the share limit check
647
+ reserveRatioBP : 50_00n , // 50%
648
+ forcedRebalanceThresholdBP : 50_00n , // 50%
649
+ } ) ;
650
+
651
+ await vaultHub . connect ( user ) . fund ( vault , { value : ether ( "49" ) } ) ;
652
+ expect ( await vaultHub . totalValue ( vault ) ) . to . equal ( ether ( "50" ) ) ;
653
+
654
+ await reportVault ( { vault, totalValue : ether ( "50" ) } ) ;
655
+
656
+ const mintingEth = ether ( "25" ) ;
657
+ const sharesToMint = await lido . getSharesByPooledEth ( mintingEth ) ;
658
+ await vaultHub . connect ( user ) . mintShares ( vault , user , sharesToMint ) ;
659
+
660
+ const burner = await impersonate ( await locator . burner ( ) , ether ( "1" ) ) ;
661
+ await lido . connect ( whale ) . transfer ( burner , ether ( "1" ) ) ;
662
+ await lido . connect ( burner ) . burnShares ( ether ( "1" ) ) ;
663
+
664
+ await reportVault ( { vault } ) ;
665
+
666
+ const record = await vaultHub . vaultRecord ( vault ) ;
667
+ const sharesByTotalValue = await lido . getSharesByPooledEth ( await vaultHub . totalValue ( vault ) ) ;
668
+ const shortfall = ( record . liabilityShares * TOTAL_BASIS_POINTS - sharesByTotalValue * 50_00n ) / 50_00n ;
669
+ expect ( await vaultHub . healthShortfallShares ( vault ) ) . to . equal ( shortfall ) ;
670
+ } ) ;
671
+ } ) ;
672
+
696
673
context ( "connectVault" , ( ) => {
697
674
let vault : StakingVault__MockForVaultHub ;
698
675
0 commit comments