@@ -183,13 +183,13 @@ contract PauseTest is Test {
183183 .pauseStatus ();
184184
185185 assertTrue (isPausedD);
186- assertEq (reasonD, "Emergency system-wide pause " );
186+ assertEq (reasonD, "" );
187187 assertTrue (isPausedC);
188- assertEq (reasonC, "Emergency system-wide pause " );
188+ assertEq (reasonC, "" );
189189 assertTrue (isPausedPI);
190- assertEq (reasonPI, "Emergency system-wide pause " );
190+ assertEq (reasonPI, "" );
191191 assertTrue (isPausedPO);
192- assertEq (reasonPO, "Emergency system-wide pause " );
192+ assertEq (reasonPO, "" );
193193 }
194194
195195 function test_CanUnpauseAllContractsSimultaneously () public {
@@ -422,26 +422,6 @@ contract PauseTest is Test {
422422 assertTrue (isPausedPO, "PegOut should be paused " );
423423 }
424424
425- function test_CanPerformEmergencyPauseWithCustomReason () public {
426- _grantPauserRole ();
427-
428- string
429- memory reason = "Critical security vulnerability detected - immediate pause required " ;
430-
431- vm.prank (pauser);
432- pauseRegistry.pause (reason);
433-
434- (, string memory reasonD , ) = flyoverDiscovery.pauseStatus ();
435- (, string memory reasonC , ) = collateralManagement.pauseStatus ();
436- (, string memory reasonPI , ) = pegInContract.pauseStatus ();
437- (, string memory reasonPO , ) = pegOutContract.pauseStatus ();
438-
439- assertEq (reasonD, reason);
440- assertEq (reasonC, reason);
441- assertEq (reasonPI, reason);
442- assertEq (reasonPO, reason);
443- }
444-
445425 function test_MaintainsPauseStateAcrossMultipleOperations () public {
446426 _grantPauserRole ();
447427
@@ -478,4 +458,221 @@ contract PauseTest is Test {
478458 assertTrue (isPausedPI);
479459 assertTrue (isPausedPO);
480460 }
461+
462+ // ---------- Two-level pause (soft vs hard) ----------
463+
464+ function test_SoftPause_BlocksNewBusiness_AllowsContinuations () public {
465+ _grantPauserRole ();
466+ vm.prank (signers[1 ], signers[1 ]);
467+ flyoverDiscovery.register {value: 1 ether }(
468+ "LP " ,
469+ "http://localhost/api " ,
470+ true ,
471+ Flyover.ProviderType.PegIn
472+ );
473+ collateralManagement.grantRole (
474+ collateralManagement.COLLATERAL_ADDER (),
475+ owner
476+ );
477+ collateralManagement.addPegInCollateralTo {value: 0.5 ether }(signers[1 ]);
478+ vm.prank (signers[1 ]);
479+ pegInContract.deposit {value: 0.5 ether }();
480+
481+ vm.prank (pauser);
482+ pauseRegistry.pause ("Soft pause " ); // level 1
483+
484+ assertEq (pauseRegistry.pauseLevel (), 1 );
485+
486+ // New business blocked
487+ vm.expectRevert (abi.encodeWithSignature ("EnforcedPause() " ));
488+ flyoverDiscovery.register {value: 0.1 ether }(
489+ "LP2 " ,
490+ "url " ,
491+ true ,
492+ Flyover.ProviderType.PegIn
493+ );
494+ vm.expectRevert (abi.encodeWithSignature ("EnforcedPause() " ));
495+ vm.prank (signers[1 ]);
496+ pegInContract.deposit {value: 0.1 ether }();
497+
498+ // LP metadata updates are allowed during soft pause (not a new flow initiation)
499+ vm.prank (signers[1 ]);
500+ flyoverDiscovery.updateProvider (
501+ "LP-updated " ,
502+ "http://localhost/api/v2 "
503+ );
504+
505+ // Continuations allowed (whenNotHardPaused passes at level 1)
506+ vm.prank (signers[1 ]);
507+ pegInContract.withdraw (0.3 ether);
508+ }
509+
510+ function test_HardPause_BlocksAllStateChangingIncludingContinuations ()
511+ public
512+ {
513+ _grantPauserRole ();
514+ vm.prank (signers[1 ], signers[1 ]);
515+ flyoverDiscovery.register {value: 1 ether }(
516+ "LP " ,
517+ "http://localhost/api " ,
518+ true ,
519+ Flyover.ProviderType.PegIn
520+ );
521+ collateralManagement.grantRole (
522+ collateralManagement.COLLATERAL_ADDER (),
523+ owner
524+ );
525+ collateralManagement.addPegInCollateralTo {value: 0.5 ether }(signers[1 ]);
526+ vm.prank (signers[1 ]);
527+ pegInContract.deposit {value: 0.5 ether }();
528+
529+ vm.prank (pauser);
530+ pauseRegistry.setPauseLevel (2 ); // hard pause
531+
532+ assertEq (pauseRegistry.pauseLevel (), 2 );
533+
534+ // New business blocked
535+ vm.expectRevert (abi.encodeWithSignature ("EnforcedPause() " ));
536+ flyoverDiscovery.register {value: 0.1 ether }(
537+ "LP2 " ,
538+ "url " ,
539+ true ,
540+ Flyover.ProviderType.PegIn
541+ );
542+
543+ // Metadata updates are blocked on hard pause (full freeze)
544+ vm.expectRevert (abi.encodeWithSignature ("EnforcedPause() " ));
545+ vm.prank (signers[1 ]);
546+ flyoverDiscovery.updateProvider (
547+ "LP-updated " ,
548+ "http://localhost/api/v2 "
549+ );
550+
551+ // Continuations/outflows also blocked at level 2
552+ vm.expectRevert (abi.encodeWithSignature ("EnforcedPause() " ));
553+ vm.prank (signers[1 ]);
554+ pegInContract.withdraw (0.3 ether);
555+
556+ vm.expectRevert (abi.encodeWithSignature ("EnforcedPause() " ));
557+ vm.prank (signers[1 ]);
558+ collateralManagement.resign ();
559+
560+ vm.expectRevert (abi.encodeWithSignature ("EnforcedPause() " ));
561+ vm.prank (signers[1 ]);
562+ collateralManagement.withdrawRewards ();
563+
564+ vm.expectRevert (abi.encodeWithSignature ("EnforcedPause() " ));
565+ vm.prank (signers[1 ]);
566+ pegOutContract.withdraw (payable (signers[1 ]), 0 );
567+ }
568+
569+ function test_PauseUnpause_BackwardCompatible_Level1 () public {
570+ _grantPauserRole ();
571+ vm.prank (pauser);
572+ pauseRegistry.pause ("Reason " );
573+ assertEq (pauseRegistry.pauseLevel (), 1 );
574+ assertTrue (pauseRegistry.paused ());
575+ (bool isPaused , , ) = pauseRegistry.pauseStatus ();
576+ assertTrue (isPaused);
577+
578+ vm.prank (pauser);
579+ pauseRegistry.unpause ();
580+ assertEq (pauseRegistry.pauseLevel (), 0 );
581+ assertFalse (pauseRegistry.paused ());
582+ }
583+
584+ // ---------- Hard-pause timer overlap ----------
585+
586+ function test_HardPause_AppendsAndClosesLog () public {
587+ _grantPauserRole ();
588+ assertEq (pauseRegistry.hardPausesCount (), 0 );
589+
590+ vm.prank (pauser);
591+ pauseRegistry.setPauseLevel (2 );
592+ assertEq (pauseRegistry.hardPausesCount (), 1 );
593+ (uint64 sTs , uint64 eTs , uint64 sBl , uint64 eBl ) = pauseRegistry
594+ .hardPauses (0 );
595+ assertTrue (sTs > 0 && sBl > 0 );
596+ assertEq (eTs, 0 );
597+ assertEq (eBl, 0 );
598+
599+ vm.prank (pauser);
600+ pauseRegistry.setPauseLevel (0 );
601+ assertEq (pauseRegistry.hardPausesCount (), 1 );
602+ (, eTs, , eBl) = pauseRegistry.hardPauses (0 );
603+ assertTrue (eTs > 0 && eBl > 0 );
604+ }
605+
606+ function test_ComputePauseOverlap_AccountsForHardPauseWindow () public {
607+ _grantPauserRole ();
608+ uint256 startTs = block .timestamp ;
609+
610+ vm.prank (pauser);
611+ pauseRegistry.setPauseLevel (2 );
612+ vm.warp (block .timestamp + 90 );
613+
614+ vm.prank (pauser);
615+ pauseRegistry.setPauseLevel (0 );
616+
617+ uint256 overlap = pauseRegistry.computePauseOverlap (
618+ startTs,
619+ block .timestamp
620+ );
621+ assertEq (overlap, 90 );
622+ }
623+
624+ function test_ComputePauseOverlapBlocks_AccountsForHardPauseWindow ()
625+ public
626+ {
627+ _grantPauserRole ();
628+ uint256 startBlock = block .number ;
629+
630+ vm.prank (pauser);
631+ pauseRegistry.setPauseLevel (2 );
632+ vm.roll (block .number + 25 );
633+
634+ vm.prank (pauser);
635+ pauseRegistry.setPauseLevel (0 );
636+
637+ uint256 overlap = pauseRegistry.computePauseOverlapBlocks (
638+ startBlock,
639+ block .number
640+ );
641+ assertEq (overlap, 25 );
642+ }
643+
644+ function test_ResignDelay_ExcludesHardPausedBlocks () public {
645+ _grantPauserRole ();
646+
647+ vm.prank (signers[2 ], signers[2 ]);
648+ flyoverDiscovery.register {value: 1 ether }(
649+ "PauseDelay LP " ,
650+ "http://localhost/api " ,
651+ true ,
652+ Flyover.ProviderType.PegIn
653+ );
654+
655+ vm.prank (signers[2 ]);
656+ collateralManagement.resign ();
657+ uint256 resignationBlock = collateralManagement.getResignationBlock (
658+ signers[2 ]
659+ );
660+
661+ vm.roll (resignationBlock + 200 );
662+
663+ vm.prank (pauser);
664+ pauseRegistry.setPauseLevel (2 );
665+ vm.roll (block .number + 400 );
666+ vm.prank (pauser);
667+ pauseRegistry.setPauseLevel (0 );
668+
669+ // Effective elapsed = 200 (400 hard-pause blocks should be excluded), so delay is not met yet.
670+ vm.expectRevert ();
671+ vm.prank (signers[2 ]);
672+ collateralManagement.withdrawCollateral ();
673+
674+ vm.roll (block .number + 300 );
675+ vm.prank (signers[2 ]);
676+ collateralManagement.withdrawCollateral ();
677+ }
481678}
0 commit comments