From 9d76f3366b62a7f1e79c6dcc35c0693638ee99c3 Mon Sep 17 00:00:00 2001 From: failingtwice Date: Wed, 6 May 2026 16:20:49 +0500 Subject: [PATCH 1/3] feat: add circuitbreaker spec --- docs/contracts/circuit-breaker.md | 377 ++++++++++++++++++++++++++++++ docs/contracts/gate-seal.md | 4 + sidebars.js | 1 + 3 files changed, 382 insertions(+) create mode 100644 docs/contracts/circuit-breaker.md diff --git a/docs/contracts/circuit-breaker.md b/docs/contracts/circuit-breaker.md new file mode 100644 index 000000000..4bffe42f3 --- /dev/null +++ b/docs/contracts/circuit-breaker.md @@ -0,0 +1,377 @@ +# CircuitBreaker + +An emergency-pause layer for Lido protocol contracts. + +| Network | Address | +|---------|-------------------------------------------------------------------------------------------------------------------------------| +| Mainnet | [`0x6019CB557978296BA3C08a7B73225C0975DFB2F7`](https://etherscan.io/address/0x6019CB557978296BA3C08a7B73225C0975DFB2F7) | +| Hoodi | [`0x44a5789dFeDa59cD176Ab5709ec2F4829dE4d555`](https://hoodi.etherscan.io/address/0x44a5789dFeDa59cD176Ab5709ec2F4829dE4d555) | + +## What is CircuitBreaker? + +CircuitBreaker is a single, permanent contract that lets DAO-designated pauser committees instantly pause registered Lido contracts for a bounded duration without waiting for a governance vote. It is the successor to [GateSeal](/contracts/gate-seal): instead of single-use, expiring instances that must be redeployed every year, CircuitBreaker is reset on each use and operates indefinitely. + +- [Source code](https://github.com/lidofinance/circuit-breaker/blob/main/src/CircuitBreaker.sol) +- [Repository](https://github.com/lidofinance/circuit-breaker) +- [LIP-34: Programmable panic layer](https://github.com/lidofinance/lido-improvement-proposals/blob/develop/LIPS/lip-34.md) +- [Research forum proposal](https://research.lido.fi/t/circuitbreaker-programmable-panic-layer/11400/1) + +## Why use a CircuitBreaker? + +Putting critical Lido components on hold via a DAO vote can take many days. CircuitBreaker provides a way to temporarily pause these contracts immediately while the DAO investigates, deliberates, and executes a decision. + +It is operated by committees, multisig accounts authorized to pull the brake in an emergency. Granting a committee unilateral pause authority is non-trivial, so CircuitBreaker has a number of safeguards: + +- **Single-use per pausable**: a successful pause unregisters the committee from that pausable contract. To pause the same contract again, the pauser must be re-assigned by a full DAO vote. A misbehaving committee can pause only its assigned contracts and only once. +- **Bounded pause duration**: the pause has a limited duration controlled by the DAO, i.e. the pauser does not choose the duration when triggering the pause. +- **Pause only**: CircuitBreaker holds only the pause role on its registered pausables. The contract cannot resume the pausables, doesn't manage funds, doesn't have a proxy. +- **Liveness via heartbeats**: each pauser maintains its own heartbeat. If the heartbeat expires, the pauser can neither pause nor self-prolong authority. This means that unresponsive committees lose authority automatically. +- **Immutable admin**: the admin address is set at construction and cannot be changed, eliminating ownership-transfer exploits. + +### Roles + +- **`ADMIN`** — immutable address, the DAO Agent. Configures the registry and controls the pause duration and heartbeat interval. +- **Pauser** — a multisig committee assigned to one or more pausables. Can pause any pausable it is registered for, and must periodically call `heartbeat()` to remain authorized. + +### Immutable bounds and current values + +CircuitBreaker is deployed with immutable bounds: + +- `MIN_PAUSE_DURATION` / `MAX_PAUSE_DURATION` — inclusive lower/upper bounds for `pauseDuration`. +- `MIN_HEARTBEAT_INTERVAL` / `MAX_HEARTBEAT_INTERVAL` — inclusive lower/upper bounds for `heartbeatInterval`. + +Within these bounds, the admin can adjust `pauseDuration` and `heartbeatInterval` at any time without redeployment. Changes to `heartbeatInterval` apply only to **subsequent** heartbeats, i.e. already-stored expiries are not retroactively updated. + +The deployed parameter sets are: + +| Parameter | Mainnet | Hoodi | +|----------------------------|----------------------|----------------------| +| `MIN_PAUSE_DURATION` | 5 days (432,000 s) | 60 s | +| `MAX_PAUSE_DURATION` | 60 days (5,184,000 s)| 30 days (2,592,000 s)| +| `MIN_HEARTBEAT_INTERVAL` | 30 days (2,592,000 s)| 60 s | +| `MAX_HEARTBEAT_INTERVAL` | 3 years (94,608,000 s)| 3 years (94,608,000 s)| +| Initial `pauseDuration` | 21 days (1,814,400 s)| 1 hour (3,600 s) | +| Initial `heartbeatInterval`| 1 year (31,536,000 s)| 1 year (31,536,000 s)| + +The mainnet 21-day initial pause duration is sized to cover the worst-case governance timeline: two consecutive Aragon votes (≈10 days), a minimum Dual Governance timelock (4 days), and a 7-day buffer for analysis and coordination. Hoodi uses relaxed bounds appropriate for testnet drills. + +### Heartbeat mechanism + +Each pauser has its own heartbeat expiry timestamp. The pauser is considered *live* while their expiry timestamp is in the future. While live, the pauser can pause any of its assigned contracts and can extend its expiry by sending a heartbeat. Once the expiry passes, the pauser is no longer considered live and can neither pause nor extend expiry. + +A heartbeat is a drill transaction that updates the caller's heartbeat. An expired pauser cannot revive itself, so the pauser must renew their heartebat before it expires. + +The expiry is also updated on registration and pause: + +- When the DAO assigns a pauser to a pausable, that pauser's expiry is updated, regardless of its previous value. +- When a pauser is unassigned from its last remaining pausable (either by the DAO or by triggering a pause) its expiry is cleared. +- A successful pause that leaves the caller with at least one other assigned pausable refreshes the caller's expiry the same way a heartbeat would. + +### Pause flow + +When a registered, live pauser triggers a pause on one of its assigned pausables, CircuitBreaker: + +1. Unregisters the pauser from that pausable. +2. Pauses the pausable for the preconfigured pause duration. The pausable is expected to follow the [`PausableUntil`](https://github.com/lidofinance/core/blob/master/contracts/0.8.9/utils/PausableUntil.sol) pattern. +3. Reads back the pausable's state to confirm the pause actually took effect, reverting if it did not. +4. Updates the caller's heartbeat expiry as described above. + +A reentrancy guard prevents a malicious pausable from calling back into CircuitBreaker during this flow to trigger additional pauses. + +CircuitBreaker does not verify at registration time that a pausable implements the expected interface or that CircuitBreaker has been granted the pause role on it. These properties can also change later, for example through a proxy upgrade or a role revocation. The DAO is therefore responsible for ensuring the pause role is granted before assigning a pauser. + +## Covered pausables + +The set of pausables and their assigned pausers is maintained by the DAO. See the deployed-contracts pages for the current registry on each network: + +- [Mainnet deployments](/deployed-contracts/) #TODO +- [Hoodi deployments — CircuitBreaker](/deployed-contracts/hoodi#circuit-breaker) + +## View Methods + +### ADMIN() + +Returns the immutable admin address. + +```solidity +function ADMIN() external view returns (address); +``` + +### MIN_PAUSE_DURATION() / MAX_PAUSE_DURATION() + +Inclusive lower and upper bounds, in seconds, for `pauseDuration`. Set at deployment, immutable thereafter. + +```solidity +function MIN_PAUSE_DURATION() external view returns (uint256); +function MAX_PAUSE_DURATION() external view returns (uint256); +``` + +### MIN_HEARTBEAT_INTERVAL() / MAX_HEARTBEAT_INTERVAL() + +Inclusive lower and upper bounds, in seconds, for `heartbeatInterval`. Set at deployment, immutable thereafter. + +```solidity +function MIN_HEARTBEAT_INTERVAL() external view returns (uint256); +function MAX_HEARTBEAT_INTERVAL() external view returns (uint256); +``` + +### pauseDuration() + +Current pause duration, in seconds, applied to a pausable on a successful trigger. + +```solidity +function pauseDuration() external view returns (uint256); +``` + +### heartbeatInterval() + +Current heartbeat interval, in seconds. The window after a heartbeat during which the pauser remains authorized. + +```solidity +function heartbeatInterval() external view returns (uint256); +``` + +### heartbeatExpiry() + +Returns the timestamp after which the given pauser is no longer authorized to heartbeat or pause. + +```solidity +function heartbeatExpiry(address pauser) external view returns (uint256); +``` + +#### Parameters + +| Name | Type | Description | +|----------|-----------|----------------------------| +| `pauser` | `address` | Pauser address to look up. | + +### getPauser() + +Returns the pauser currently registered for a pausable, or the zero address if none. + +```solidity +function getPauser(address _pausable) external view returns (address); +``` + +#### Parameters + +| Name | Type | Description | +|-------------|-----------|-----------------------------| +| `_pausable` | `address` | Pausable contract address. | + +### getPausables() + +Returns all pausable addresses currently registered. + +```solidity +function getPausables() external view returns (address[] memory); +``` + +### getPausableCount() + +Returns the number of pausables assigned to a pauser. + +```solidity +function getPausableCount(address _pauser) external view returns (uint256); +``` + +#### Parameters + +| Name | Type | Description | +|-----------|-----------|-------------------| +| `_pauser` | `address` | Pauser address. | + +### isPauserLive() + +Returns whether the pauser's heartbeat has not expired. + +```solidity +function isPauserLive(address _pauser) external view returns (bool); +``` + +#### Parameters + +| Name | Type | Description | +|-----------|-----------|-------------------| +| `_pauser` | `address` | Pauser address. | + +Returns `true` when `block.timestamp < heartbeatExpiry[_pauser]`. + +## Write Methods + +### Admin methods + +The following methods can be called only by `ADMIN`. They revert with `SenderNotAdmin` otherwise. + +#### setPauseDuration() + +Sets the pause duration applied on subsequent triggers. The new value takes effect immediately for any pauses called afterward. + +```solidity +function setPauseDuration(uint256 _newPauseDuration) external; +``` + +#### Parameters + +| Name | Type | Description | +|---------------------|-----------|--------------------------------------------| +| `_newPauseDuration` | `uint256` | New pause duration, in seconds. | + +:::note +Reverts if any of the following is true: + +- caller is not `ADMIN` (`SenderNotAdmin`) +- `_newPauseDuration < MIN_PAUSE_DURATION` (`PauseDurationBelowMin`) +- `_newPauseDuration > MAX_PAUSE_DURATION` (`PauseDurationAboveMax`) +::: + +Emits `PauseDurationUpdated(previousPauseDuration, newPauseDuration)`. + +#### setHeartbeatInterval() + +Sets the heartbeat interval pausers must maintain to remain authorized. The new value applies only to subsequent heartbeats and registrations; already-stored `heartbeatExpiry` values are not changed retroactively. + +```solidity +function setHeartbeatInterval(uint256 _newHeartbeatInterval) external; +``` + +#### Parameters + +| Name | Type | Description | +|-------------------------|-----------|--------------------------------------| +| `_newHeartbeatInterval` | `uint256` | New heartbeat interval, in seconds. | + +:::note +Reverts if any of the following is true: + +- caller is not `ADMIN` (`SenderNotAdmin`) +- `_newHeartbeatInterval < MIN_HEARTBEAT_INTERVAL` (`HeartbeatIntervalBelowMin`) +- `_newHeartbeatInterval > MAX_HEARTBEAT_INTERVAL` (`HeartbeatIntervalAboveMax`) +::: + +Emits `HeartbeatIntervalUpdated(previousHeartbeatInterval, newHeartbeatInterval)`. + +#### registerPauser() + +Registers, replaces, or unregisters a pauser for a pausable. + +- The previous pauser, if any, is overwritten. If they are left with zero remaining pausables, their `heartbeatExpiry` is cleared to `0`. +- The new pauser's `heartbeatExpiry` is set to `block.timestamp + heartbeatInterval` (extending or initializing it). +- Passing `address(0)` as `_newPauser` unregisters the pausable's current pauser. + +```solidity +function registerPauser(address _pausable, address _newPauser) external; +``` + +#### Parameters + +| Name | Type | Description | +|--------------|-----------|----------------------------------------------------------------------| +| `_pausable` | `address` | Pausable contract address. | +| `_newPauser` | `address` | New pauser address. Zero unregisters the current pauser, if any. | + +:::note +- Reverts if caller is not `ADMIN` (`SenderNotAdmin`). +- Does **not** verify that CircuitBreaker holds the pause role on `_pausable`, or that `_pausable` implements `IPausable`. The DAO is responsible for ensuring these invariants when assigning pausers. +::: + +Emits `HeartbeatUpdated` for the previous pauser (if their expiry was cleared) and for the new pauser. + +### Pauser methods + +#### heartbeat() + +Records a liveness proof, extending the caller's `heartbeatExpiry` to `block.timestamp + heartbeatInterval`. + +```solidity +function heartbeat() external; +``` + +:::note +Reverts if any of the following is true: + +- caller is not registered as a pauser for any pausable (`SenderNotPauser`) +- caller's heartbeat has already expired (`HeartbeatExpired`) — a lapsed pauser cannot self-renew; the DAO must explicitly re-register them +::: + +Emits `HeartbeatUpdated(pauser, newHeartbeatExpiry)`. + +#### pause() + +Pauses a registered pausable for the current `pauseDuration`. Single-use: the caller is unregistered from this pausable on success. + +```solidity +function pause(address _pausable) external; +``` + +The target must implement the minimal `IPausable` interface that CircuitBreaker calls into: + +```solidity +interface IPausable { + function isPaused() external view returns (bool); + function pauseFor(uint256 _duration) external; +} +``` + +#### Parameters + +| Name | Type | Description | +|-------------|-----------|-----------------------------------| +| `_pausable` | `address` | Pausable contract to pause. | + +The execution flow is: + +1. Verify `msg.sender` is the registered pauser of `_pausable` and is live. +2. Unregister `msg.sender` from `_pausable`. +3. Call `IPausable(_pausable).pauseFor(pauseDuration)`. +4. Verify `IPausable(_pausable).isPaused()` is `true`. +5. Update the caller's `heartbeatExpiry`: extended to `block.timestamp + heartbeatInterval` if any other pausables are still assigned to them, or cleared to `0` otherwise. + +:::note +Reverts if any of the following is true: + +- caller is not the registered pauser of `_pausable` (`SenderNotPauser`) +- caller's heartbeat has expired (`HeartbeatExpired`) +- `pauseFor()` succeeded but the target does not report itself paused (`PauseFailed`) +- the call reentered (`ReentrantCall`) +::: + +Emits `PauseTriggered(pausable, pauser, pauseDuration)` and `HeartbeatUpdated(pauser, newHeartbeatExpiry)`. + +## Events + +```solidity +event CircuitBreakerInitialized( + address indexed admin, + uint256 minPauseDuration, + uint256 maxPauseDuration, + uint256 minHeartbeatInterval, + uint256 maxHeartbeatInterval +); +``` + +Emitted once at construction with the immutable admin and bounds. + +```solidity +event PauseDurationUpdated(uint256 previousPauseDuration, uint256 newPauseDuration); +``` + +Emitted on `setPauseDuration` and once at construction for the initial value. + +```solidity +event HeartbeatIntervalUpdated(uint256 previousHeartbeatInterval, uint256 newHeartbeatInterval); +``` + +Emitted on `setHeartbeatInterval` and once at construction for the initial value. + +```solidity +event HeartbeatUpdated(address indexed pauser, uint256 newHeartbeatExpiry); +``` + +Emitted whenever a pauser's heartbeat expiry changes — on `heartbeat()`, `pause()`, and `registerPauser()`. + +```solidity +event PauseTriggered(address indexed pausable, address indexed pauser, uint256 pauseDuration); +``` + +Emitted on a successful `pause()`. diff --git a/docs/contracts/gate-seal.md b/docs/contracts/gate-seal.md index f1188be00..0a81691b0 100644 --- a/docs/contracts/gate-seal.md +++ b/docs/contracts/gate-seal.md @@ -1,5 +1,9 @@ # GateSeal +:::info +GateSeals have been deprecated and replaced by [CircuitBreaker](/contracts/circuit-breaker) ([LIP-34](https://github.com/lidofinance/lido-improvement-proposals/blob/develop/LIPS/lip-34.md)). +::: + A one-time panic button for pausable contracts. | Active GateSeal | Address | Expiration date (UTC) | diff --git a/sidebars.js b/sidebars.js index b50b76cbe..0546ee68a 100644 --- a/sidebars.js +++ b/sidebars.js @@ -149,6 +149,7 @@ module.exports = { 'contracts/mev-boost-relays-allowed-list', 'contracts/trp-vesting-escrow', 'contracts/gate-seal', + 'contracts/circuit-breaker', 'contracts/insurance', 'contracts/ossifiable-proxy' ], From 8f8da3b59e3711a0ff75ea9e366061d14eb572f4 Mon Sep 17 00:00:00 2001 From: failingtwice <39704351+failingtwice@users.noreply.github.com> Date: Tue, 12 May 2026 13:15:52 +0500 Subject: [PATCH 2/3] Apply suggestions from code review Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- docs/contracts/circuit-breaker.md | 2 +- docs/contracts/gate-seal.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/contracts/circuit-breaker.md b/docs/contracts/circuit-breaker.md index 4bffe42f3..3726a9814 100644 --- a/docs/contracts/circuit-breaker.md +++ b/docs/contracts/circuit-breaker.md @@ -59,7 +59,7 @@ The mainnet 21-day initial pause duration is sized to cover the worst-case gover Each pauser has its own heartbeat expiry timestamp. The pauser is considered *live* while their expiry timestamp is in the future. While live, the pauser can pause any of its assigned contracts and can extend its expiry by sending a heartbeat. Once the expiry passes, the pauser is no longer considered live and can neither pause nor extend expiry. -A heartbeat is a drill transaction that updates the caller's heartbeat. An expired pauser cannot revive itself, so the pauser must renew their heartebat before it expires. +A heartbeat is a drill transaction that updates the caller's heartbeat. An expired pauser cannot revive itself, so the pauser must renew their heartbeat before it expires. The expiry is also updated on registration and pause: diff --git a/docs/contracts/gate-seal.md b/docs/contracts/gate-seal.md index 0a81691b0..71c93220a 100644 --- a/docs/contracts/gate-seal.md +++ b/docs/contracts/gate-seal.md @@ -1,7 +1,7 @@ # GateSeal -:::info -GateSeals have been deprecated and replaced by [CircuitBreaker](/contracts/circuit-breaker) ([LIP-34](https://github.com/lidofinance/lido-improvement-proposals/blob/develop/LIPS/lip-34.md)). +:::warning +**DEPRECATED**: GateSeals have been deprecated and replaced by [CircuitBreaker](/contracts/circuit-breaker) ([LIP-34](https://github.com/lidofinance/lido-improvement-proposals/blob/develop/LIPS/lip-34.md)). ::: A one-time panic button for pausable contracts. From 2ec291e0fa081d58d4a5b910f279737c2ee602c7 Mon Sep 17 00:00:00 2001 From: failingtwice Date: Tue, 12 May 2026 13:43:32 +0500 Subject: [PATCH 3/3] feat: mainnet proposed --- docs/contracts/gate-seal.md | 2 +- docs/deployed-contracts/index.md | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/docs/contracts/gate-seal.md b/docs/contracts/gate-seal.md index 71c93220a..88ef02e7d 100644 --- a/docs/contracts/gate-seal.md +++ b/docs/contracts/gate-seal.md @@ -1,7 +1,7 @@ # GateSeal :::warning -**DEPRECATED**: GateSeals have been deprecated and replaced by [CircuitBreaker](/contracts/circuit-breaker) ([LIP-34](https://github.com/lidofinance/lido-improvement-proposals/blob/develop/LIPS/lip-34.md)). +GateSeals are proposed to be deprecated and replaced by [CircuitBreaker](/contracts/circuit-breaker) ([LIP-34](https://github.com/lidofinance/lido-improvement-proposals/blob/develop/LIPS/lip-34.md)). ::: A one-time panic button for pausable contracts. diff --git a/docs/deployed-contracts/index.md b/docs/deployed-contracts/index.md index 791e933de..eb23c49d0 100644 --- a/docs/deployed-contracts/index.md +++ b/docs/deployed-contracts/index.md @@ -121,6 +121,28 @@ This page lists production contract addresses on mainnets, including Ethereum an - Ethereum Ecosystem Sub Committee [`0xBF048f2111497B6Df5E062811f5fC422804D4baE`](https://etherscan.io/address/0xBF048f2111497B6Df5E062811f5fC422804D4baE) - Time Constraints: [`0x2a30F5aC03187674553024296bed35Aa49749DDa`](https://etherscan.io/address/0x2a30F5aC03187674553024296bed35Aa49749DDa) +## 🔌 CircuitBreaker \[Proposed\] {#circuit-breaker} + +- CircuitBreaker: [`0x6019CB557978296BA3C08a7B73225C0975DFB2F7`](https://etherscan.io/address/0x6019CB557978296BA3C08a7B73225C0975DFB2F7) + +### Covered pausables and their pausers + +Each pausable contract below is proposed to be covered by the CircuitBreaker, with a designated pauser authorized to trigger a pause. Pauser assignments are pending DAO approval ([LIP-34](https://github.com/lidofinance/lido-improvement-proposals/blob/develop/LIPS/lip-34.md)); until then, the existing [GateSeals](#dao-contracts) remain the active emergency-pause mechanism. The proposed pausers are the same multisigs that currently operate the GateSeals: the **CircuitBreaker Committee** (listed as [GateSeal Committee](#emergency-brakes-multisigs) until the migration completes) for core protocol pausables, and the **CSM Committee** (listed as [Community Staking Module Committee](#committees)) for CSM pausables. + +| Pausable | Pauser | +| --- | --- | +| [Withdrawal Queue ERC721](https://etherscan.io/address/0x889edC2eDab5f40e902b864aD4d7AdE8E412F9B1) | [CircuitBreaker Committee](https://etherscan.io/address/0x8772E3a2D86B9347A2688f9bc1808A6d8917760C) | +| [Validators Exit Bus Oracle](https://etherscan.io/address/0x0De4Ea0184c2ad0BacA7183356Aea5B8d5Bf5c6e) | [CircuitBreaker Committee](https://etherscan.io/address/0x8772E3a2D86B9347A2688f9bc1808A6d8917760C) | +| [Triggerable Withdrawals Gateway](https://etherscan.io/address/0xDC00116a0D3E064427dA2600449cfD2566B3037B) | [CircuitBreaker Committee](https://etherscan.io/address/0x8772E3a2D86B9347A2688f9bc1808A6d8917760C) | +| [Vault Hub](https://etherscan.io/address/0x1d201BE093d847f6446530Efb0E8Fb426d176709) | [CircuitBreaker Committee](https://etherscan.io/address/0x8772E3a2D86B9347A2688f9bc1808A6d8917760C) | +| [Predeposit Guarantee](https://etherscan.io/address/0xF4bF42c6D6A0E38825785048124DBAD6c9eaaac3) | [CircuitBreaker Committee](https://etherscan.io/address/0x8772E3a2D86B9347A2688f9bc1808A6d8917760C) | +| [CSModule](https://etherscan.io/address/0xdA7dE2ECdDfccC6c3AF10108Db212ACBBf9EA83F) | [CSM Committee](https://etherscan.io/address/0xC52fC3081123073078698F1EAc2f1Dc7Bd71880f) | +| [CSAccounting](https://etherscan.io/address/0x4d72BFF1BeaC69925F8Bd12526a39BAAb069e5Da) | [CSM Committee](https://etherscan.io/address/0xC52fC3081123073078698F1EAc2f1Dc7Bd71880f) | +| [CSFeeOracle](https://etherscan.io/address/0x4D4074628678Bd302921c20573EEa1ed38DdF7FB) | [CSM Committee](https://etherscan.io/address/0xC52fC3081123073078698F1EAc2f1Dc7Bd71880f) | +| [CSVerifier](https://etherscan.io/address/0xdC5FE1782B6943f318E05230d688713a560063DC) | [CSM Committee](https://etherscan.io/address/0xC52fC3081123073078698F1EAc2f1Dc7Bd71880f) | +| [CSEjector](https://etherscan.io/address/0xc72b58aa02E0e98cF8A4a0E9Dce75e763800802C) | [CSM Committee](https://etherscan.io/address/0xC52fC3081123073078698F1EAc2f1Dc7Bd71880f) | +| [VettedGate (IdentifiedCommunityStakersGate)](https://etherscan.io/address/0xB314D4A76C457c93150d308787939063F4Cc67E0) | [CSM Committee](https://etherscan.io/address/0xC52fC3081123073078698F1EAc2f1Dc7Bd71880f) | + ## 📊 Data Bus {#data-bus} - DataBus on Gnosis Chain: [`0x37De961D6bb5865867aDd416be07189D2Dd960e6`](https://gnosis.blockscout.com/address/0x37De961D6bb5865867aDd416be07189D2Dd960e6)