@@ -10,8 +10,8 @@ import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Ini
1010import {IPauseRegistry} from "./interfaces/IPauseRegistry.sol " ;
1111
1212/// @title PauseRegistry
13- /// @notice Centralized registry for pause state; all Flyover contracts read from here
14- /// @dev Only accounts with PAUSER_ROLE can pause/unpause . Uses namespaced storage.
13+ /// @notice Centralized registry for pause state; all Flyover contracts read from here.
14+ /// @dev Level 0 = normal, 1 = soft (no new business), 2 = hard (full freeze) . Uses namespaced storage.
1515contract PauseRegistry is
1616 Initializable ,
1717 AccessControlDefaultAdminRulesUpgradeable ,
@@ -20,8 +20,17 @@ contract PauseRegistry is
2020 /// @custom:storage-location erc7201:rsk.flyover.PauseRegistry
2121 struct PauseRegistryStorage {
2222 bool paused;
23+ IPauseRegistry.PauseLevel pauseLevel;
2324 uint64 pauseTimestamp;
2425 string pauseReason;
26+ HardPause[] hardPauses;
27+ }
28+
29+ struct HardPause {
30+ uint64 startTimestamp;
31+ uint64 endTimestamp; // 0 while ongoing
32+ uint64 startBlock;
33+ uint64 endBlock; // 0 while ongoing
2534 }
2635
2736 bytes32 public constant PAUSER_ROLE = keccak256 ("PAUSER_ROLE " );
@@ -46,28 +55,35 @@ contract PauseRegistry is
4655 }
4756
4857 /// @inheritdoc IPauseRegistry
49- function pause (string calldata reason ) external onlyRole (PAUSER_ROLE) {
50- PauseRegistryStorage storage $ = _getPauseRegistryStorage ();
51- if ($.paused) return ;
52- $.paused = true ;
53- $.pauseReason = reason;
54- $.pauseTimestamp = uint64 (block .timestamp );
55- emit EmergencyPaused (msg .sender , reason);
58+ function setPauseLevel (
59+ IPauseRegistry.PauseLevel level ,
60+ string calldata reason
61+ ) external onlyRole (PAUSER_ROLE) {
62+ _setPauseLevelWithReason (level, reason);
5663 }
5764
58- /// @inheritdoc IPauseRegistry
59- function unpause () external onlyRole (PAUSER_ROLE) {
65+ function _setPauseLevelWithReason (
66+ IPauseRegistry.PauseLevel level ,
67+ string memory reason
68+ ) internal {
6069 PauseRegistryStorage storage $ = _getPauseRegistryStorage ();
61- if (! $.paused) return ;
62- $.paused = false ;
63- $.pauseReason = "" ;
64- $.pauseTimestamp = 0 ;
65- emit EmergencyUnpaused (msg .sender );
70+ bool wasPaused = $.pauseLevel != IPauseRegistry.PauseLevel.None;
71+ if (level != IPauseRegistry.PauseLevel.None) {
72+ $.pauseReason = reason;
73+ }
74+ _setPauseLevel (level);
75+ bool isPaused = level != IPauseRegistry.PauseLevel.None;
76+ if (! wasPaused && isPaused) {
77+ emit EmergencyPaused (msg .sender , $.pauseReason);
78+ } else if (wasPaused && ! isPaused) {
79+ emit EmergencyUnpaused (msg .sender );
80+ }
6681 }
6782
6883 /// @inheritdoc IPauseRegistry
6984 function paused () external view returns (bool ) {
70- return _getPauseRegistryStorage ().paused;
85+ PauseRegistryStorage storage $ = _getPauseRegistryStorage ();
86+ return $.pauseLevel != IPauseRegistry.PauseLevel.None;
7187 }
7288
7389 /// @inheritdoc IPauseRegistry
@@ -77,7 +93,132 @@ contract PauseRegistry is
7793 returns (bool isPaused , string memory reason , uint64 since )
7894 {
7995 PauseRegistryStorage storage $ = _getPauseRegistryStorage ();
80- return ($.paused, $.pauseReason, $.pauseTimestamp);
96+ isPaused = $.pauseLevel != IPauseRegistry.PauseLevel.None;
97+ reason = $.pauseReason;
98+ since = $.pauseTimestamp;
99+ }
100+
101+ /// @inheritdoc IPauseRegistry
102+ function pauseLevel () external view returns (IPauseRegistry.PauseLevel) {
103+ return _getPauseRegistryStorage ().pauseLevel;
104+ }
105+
106+ /// @inheritdoc IPauseRegistry
107+ function hardPausesCount () external view returns (uint256 ) {
108+ return _getPauseRegistryStorage ().hardPauses.length ;
109+ }
110+
111+ /// @inheritdoc IPauseRegistry
112+ function hardPauses (uint256 index )
113+ external
114+ view
115+ returns (
116+ uint64 startTimestamp ,
117+ uint64 endTimestamp ,
118+ uint64 startBlock ,
119+ uint64 endBlock
120+ )
121+ {
122+ HardPause storage p = _getPauseRegistryStorage ().hardPauses[index];
123+ return (p.startTimestamp, p.endTimestamp, p.startBlock, p.endBlock);
124+ }
125+
126+ /// @inheritdoc IPauseRegistry
127+ function computePauseOverlap (uint256 startTimestamp , uint256 endTimestamp )
128+ external
129+ view
130+ returns (uint256 totalPauseTime )
131+ {
132+ HardPause[] storage pauses = _getPauseRegistryStorage ().hardPauses;
133+ uint256 n = pauses.length ;
134+ for (uint256 i = n; i > 0 ;) {
135+ unchecked {
136+ -- i;
137+ }
138+ HardPause storage p = pauses[i];
139+ uint64 pEnd = p.endTimestamp;
140+ if (pEnd != 0 && ! (pEnd > startTimestamp)) break ;
141+ uint256 effectiveStart = startTimestamp;
142+ if (p.startTimestamp > effectiveStart) effectiveStart = p.startTimestamp;
143+ uint256 effectiveEnd = endTimestamp;
144+ uint256 pEndOrNow = pEnd == 0 ? block .timestamp : pEnd;
145+ if (pEndOrNow < effectiveEnd) effectiveEnd = pEndOrNow;
146+ if (effectiveEnd > effectiveStart) {
147+ totalPauseTime += effectiveEnd - effectiveStart;
148+ }
149+ }
150+ }
151+
152+ /// @inheritdoc IPauseRegistry
153+ function computePauseOverlapBlocks (uint256 startBlock , uint256 endBlock )
154+ external
155+ view
156+ returns (uint256 totalPauseBlocks )
157+ {
158+ HardPause[] storage pauses = _getPauseRegistryStorage ().hardPauses;
159+ uint256 n = pauses.length ;
160+ for (uint256 i = n; i > 0 ;) {
161+ unchecked {
162+ -- i;
163+ }
164+ HardPause storage p = pauses[i];
165+ uint64 pEndBlock = p.endBlock;
166+ if (pEndBlock != 0 && ! (pEndBlock > startBlock)) break ;
167+ uint256 effectiveStart = startBlock;
168+ if (p.startBlock > effectiveStart) effectiveStart = p.startBlock;
169+ uint256 effectiveEnd = endBlock;
170+ uint256 pEndOrNow = pEndBlock == 0 ? block .number : pEndBlock;
171+ if (pEndOrNow < effectiveEnd) effectiveEnd = pEndOrNow;
172+ if (effectiveEnd > effectiveStart) {
173+ totalPauseBlocks += effectiveEnd - effectiveStart;
174+ }
175+ }
176+ }
177+
178+ function _setPauseLevel (IPauseRegistry.PauseLevel level ) internal {
179+ PauseRegistryStorage storage $ = _getPauseRegistryStorage ();
180+ IPauseRegistry.PauseLevel prev = IPauseRegistry.PauseLevel ($.pauseLevel);
181+ $.pauseLevel = level;
182+ $.paused = (level != IPauseRegistry.PauseLevel.None);
183+ if (
184+ prev == IPauseRegistry.PauseLevel.None &&
185+ level != IPauseRegistry.PauseLevel.None
186+ ) {
187+ $.pauseTimestamp = uint64 (block .timestamp );
188+ } else if (
189+ prev != IPauseRegistry.PauseLevel.None &&
190+ level == IPauseRegistry.PauseLevel.None
191+ ) {
192+ $.pauseTimestamp = 0 ;
193+ $.pauseReason = "" ;
194+ }
195+
196+ if (
197+ prev == IPauseRegistry.PauseLevel.Hard &&
198+ level != IPauseRegistry.PauseLevel.Hard
199+ ) {
200+ HardPause[] storage pauses = $.hardPauses;
201+ uint256 len = pauses.length ;
202+ if (len > 0 ) {
203+ HardPause storage last = pauses[len - 1 ];
204+ if (last.endTimestamp == 0 ) {
205+ last.endTimestamp = uint64 (block .timestamp );
206+ last.endBlock = uint64 (block .number );
207+ }
208+ }
209+ } else if (
210+ prev != IPauseRegistry.PauseLevel.Hard &&
211+ level == IPauseRegistry.PauseLevel.Hard
212+ ) {
213+ $.hardPauses.push (
214+ HardPause ({
215+ startTimestamp: uint64 (block .timestamp ),
216+ endTimestamp: 0 ,
217+ startBlock: uint64 (block .number ),
218+ endBlock: 0
219+ })
220+ );
221+ }
81222 }
82223
83224 function _getPauseRegistryStorage ()
@@ -89,4 +230,5 @@ contract PauseRegistry is
89230 $.slot := _PAUSE_REGISTRY_STORAGE
90231 }
91232 }
233+
92234}
0 commit comments