Summary
This RFC proposes migrating the Olympus OHM bridge from LayerZero V1 to a hardened LayerZero V2 deployment built on a four-contract architecture: the LZBridgeGateway policy (the OApp: endpoint messaging, OHM mint/burn, rate limiting, and bridged-supply accounting), the LZCrossChainBridge periphery (the user-facing facilitator), the LZEndpointDelegate policy (the gateway's registered endpoint delegate, through which send/receive-library selection, ULN/Executor config, and inbound channel-management calls are routed), and the LZBridgeAndDelegateConfig timelock policy (which queues those configuration changes behind a delay before they apply).
The upgrade replaces the currently disabled V1 bridge and adds three independent layers of protection: four required DVNs per route with the optional DVN slot explicitly nilified, mandatory bidirectional per-route rate limits with offsetting accounting, and a bridged-supply cap that prevents more OHM from returning to Ethereum than was ever sent out. Every endpoint parameter — message libraries, DVNs, the Executor, source confirmations, enforced gas options, and max message size — is explicitly pinned and validated after write, so the bridge inherits no LayerZero default. Supported routes cover five chains: Ethereum, Arbitrum, Optimism, Base, and Berachain.
The bridge is currently halted as a precaution following the rsETH/KelpDAO DVN incident. This proposal is the migration path forward.
Motivation
Current state
The legacy CrossChainBridge is disabled across all chains. It was a single contract built on LayerZero V1 with the following structural weaknesses:
- No supply accounting on the canonical chain. Nothing tracks how much OHM has been minted onto non-canonical chains. A bad message returning from any L2 mints OHM on Ethereum with no cap.
- Unsafe message retry. The V1
retryMessage function does not re-validate the trusted remote, so messages from peers that have since been removed can still be replayed.
- Default endpoint configuration. V1 inherits LayerZero's default verification libraries, which LayerZero governance can change at any time. It also uses the outdated relayer approach.
- No rate limiting. No per-route or global throughput cap on either side.
Re-enabling V1 would mean re-exposing all of these. The rsETH incident this April made it clear that a single compromised DVN under a default configuration is enough to drain a protocol; we should not bring the bridge back without first addressing the issues.
Objective
Bring OHM bridging back online on top of LayerZero V2 with a configuration that:
- Bounds the blast radius of any single DVN compromise.
- Bounds the protocol's exposure during the window before an incident is detected.
- Makes the system fully halt-able and observable from Olympus' own contracts, with no reliance on LayerZero defaults.
- Ensures no single trusted address can silently re-configure the bridge’s security or accounting parameters. Material changes are queued behind a timelock with a community observation window (see Timelock behaviour).
What we learned from rsETH
In April, a single compromised DVN was used to drain ~$292M from KelpDAO. The attacker did not need to compromise LayerZero, the protocol's own contracts, or its multisig – only the verifier the OApp was relying on.
We are configuring out of that bucket. The defense layers are designed to mitigate the risk of a single DVN compromise by using four required DVNs, rate limits on both directions, and a bridged-supply cap on Ethereum.
In post-incident guidance and public reporting around LayerZero's May 9 follow-up, the bar moved upward: the LayerZero Labs DVN would no longer co-sign for OApps running 1/1 configurations, defaults were reported as migrating toward 5/5 (with a 3/3 floor on chains where fewer DVNs exist), and protocols were pushed to explicitly pin every config parameter rather than inherit defaults. This RFC's 4 required + 0 optional + NIL-pinned configuration clears the 3/3 floor by a margin and is far above the 1/1 and 2/2 setups common before rsETH.
LayerZero and industry best practices
LayerZero's public integration checklist now emphasizes the following best practices: set peers both ways, pin send/receive libraries, explicitly configure required and optional DVNs, explicitly configure the Executor and max message size, set enforced options, and use getConfig / getSendLibrary / getReceiveLibrary checks to catch any fallback to protocol defaults. It also says production pathways should not rely on a single DVN, should include at least one non-LayerZero-operated required DVN, and should keep send-side and receive-side DVN configuration consistent.
This RFC aligns with those recommendations:
- Directional pathway configuration. All 20 routes across the five-chain deployment are configured independently and validated after write.
- Explicit configuration over defaults. LayerZero's guidance recommends pinning the security-critical parameters and using
getConfig checks to catch silent fallback to protocol defaults. Olympus pins and validates all of them: send libraries, receive libraries, ULN config, Executor, max message size, enforced options, peers, confirmations, and DVNs.
- Multi-operator DVN redundancy. Every route requires four DVNs, including non-LayerZero operators, with no optional DVN threshold.
- Symmetric verification posture. The same DVN set and source confirmation value are written into the source chain's send-ULN config and the destination chain's receive-ULN config for that source EID.
- Application-layer blast-radius limits. Rate limits and Ethereum-side
bridgedSupply accounting remain active even if the verifier layer fails.
Several major LayerZero OFT deployments use similar multi-DVN configurations. Some tightened their setups after rsETH; others (such as Ondo) had already adopted multi-verifier configurations beforehand. The snapshot below is drawn from public sources found during review:
| Protocol | Evidence recency | Public DVN posture | Canary usage | Rate / blast-radius limits | Supply / backing control | Notable |
| Olympus OHM (proposed) | Current RFC | 4 required — LZ Labs, Canary, Nethermind, Google (Horizen on Berachain routes) | Required on every route | Bidirectional per-route, 24h sliding, offsetting; 100k/55k on ETH, 50k/100k/110k on L2s | bridgedSupply on Ethereum with underflow check | Per-chain confirmations and per-route limits publicly enumerated |
| Ethena (USDe / sUSDe) | April 2026 post-rsETH public reports of Ethena X statement | 4/4 publicly reported after rsETH | Not publicly enumerated in the post-incident notice | $10M/hour maintained post-upgrade | Canonical mint / OFT supply model | Public reports say 2/2 -> 4/4; DVN identities were not listed in the sources found |
| Ether.fi (weETH) | April 2026 post-rsETH public reports + Canary 2026 site | 4/4 publicly reported after rsETH | Canary publicly names Ether.fi as a Canary user; post-incident notices did not enumerate all four DVNs | Pairwise rate limits tightened; numeric values not publicly disclosed | L2NRM-style backing invariant | Public reports say pinned send/receive libraries, locked DVN set, 4/4 threshold, and removed LayerZero multisig authority over bridge config |
| Ondo (USDY / GM / tokenized equities) | Current Ondo docs + May 2026 LayerZero/Ondo GM article | 3-DVN verifier set publicly documented: Canary, LayerZero Labs, Ondo DVN; GM article states all three attest before minting | Canary publicly listed as one of the verifiers | USDY: 450k outbound / 500k inbound per EVM pathway per day; Solana-EVM 250k / 300k; GM approximately $450k per asset per direction per day, with some Hyperliquid routes lower | 1:1 asset parity / issuer-controlled minting | Current Ondo docs note configurations may change as the bridge evolves |
| USDT0 | Current 2026 USDT0 security post | 3/3 across USDT0 routes and most XAUt0 routes, with stated plan to move toward 4/4 and 5/5 | Required: USDT0 DVN + LayerZero + Canary | Not publicly enumerated per route | Ethereum lockbox, 1:1 USDT-backed | Publicly disclosed proprietary DVN with veto power, pinned libraries, independent chain risk assessments, and $6M bug bounty |
| BitGo WBTC | December 2025 LayerZero essay | Required BitGo signature plus additional DVN attestations publicly described | Canary included alongside LZ Labs and Polyhedra | Not publicly enumerated | BitGo-controlled wrapped-BTC backing model | Useful as a high-value OFT example using Canary, but not treated as a current post-rsETH hardening disclosure |
| Morpho (MORPHO Arbitrum bridge proposal) | July 2025 governance proposal; historical | Proposed 2 required + 1 optional threshold from a 2-required / 2-optional set | Canary proposed as required with LZ Labs; Deutsche Telekom and P2P optional | Proposed 250k MORPHO/month ETH -> Arbitrum; 100k/month Arbitrum -> ETH | L1 lock/release, L2 mint/burn | Kept only as a historical example of Canary selected for client diversity; should not be cited as current post-rsETH configuration without onchain getConfig verification |
Note: confirmation values, optional-DVN fields, exact rate-limit numbers, and route-specific DVN sets for several peer deployments are not fully disclosed publicly and would need to be read directly from each protocol's getConfig on the V2 endpoint to verify. The comparison above deliberately separates current / post-rsETH disclosures from older historical architecture examples.
Specification
Architecture
The monolithic V1 bridge is replaced by four contracts:
LZBridgeGateway (Bophades policy). The OApp. Handles all messaging with the LayerZero V2 endpoint (send/receive), OHM mint/burn via MINTR, peer management, enforced gas options, rate limiting, and bridged-supply accounting on Ethereum.
LZCrossChainBridge (periphery). User-facing facilitator. Accepts OHM from users and calls the gateway. It has no MINTR permissions; its only privileged hook into the gateway is the bridge_facilitator role.
LZEndpointDelegate (Bophades policy). The gateway's registered LayerZero V2 endpoint delegate. Send/receive-library selection, ULN/Executor config (setSendLibrary, setReceiveLibrary, setReceiveLibraryTimeout, setEndpointConfig), and the inbound messaging-channel management functions (skip, nilify, burn, clear) are routed through this stateless policy, which acts as the gateway’s delegate. Configuration mutators are gated by bridge_configurator; the channel-management functions are gated by bridge_channel_manager (alongside bridge_admin and admin).
LZBridgeAndDelegateConfig (Bophades policy). The timelock. It holds the bridge_configurator role on the gateway and delegate and is pinned as the periphery's configurator, so every security- or accounting-relevant configuration change is queued behind a delay (1–30 days, with a 3-day execution window) before it can be applied.
Defense in depth
The configuration assumes that any single defensive layer can fail and is designed so that no single failure compromises the protocol. Four layers stack:
1. Four required DVNs per route. Every message must be verified by all four DVNs before it can be delivered. The optional DVN count is explicitly pinned to a NIL sentinel rather than left at the default of zero, so a future change to LayerZero's defaults does not affect the bridge's existing required-DVN configuration. The required set is LayerZero Labs, Canary, Nethermind, and Google Cloud, with Horizen substituting for Google Cloud on routes that touch Berachain (where Google Cloud is not deployed). Because all four are required, an attacker would have to compromise the entire DVN set to push a fraudulent message through.
2. Mandatory bidirectional per-route rate limits. Each peer has independent outbound and inbound limits on a 24-hour sliding window. Both directions revert with RateLimitExceeded once the limit is exhausted. The limits use offsetting accounting: activity in one direction reduces the in-flight amount of the other (floored at zero), so balanced round trips free capacity faster than purely additive accounting would. Limits are written into the gateway at activation time and the bridge cannot be enabled without them.
3. Bridged-supply cap on Ethereum. Every outbound transfer from Ethereum increments a bridgedSupply counter; every inbound receive decrements it with an underflow check. If the L2 side is ever compromised and tries to send back more OHM than was sent out, the transaction reverts on Ethereum. This caps the worst-case exposure to whatever supply legitimately exists on non-canonical chains at the moment of the attack (currently around 130,000 OHM, ~0.66% of total OHM supply) regardless of how much capacity the rate limiter would otherwise allow.
4. Timelock on configuration changes. The security and accounting-critical parameters above (DVN sets, message libraries, rate limits, and bridged-supply adjustments) are not freely mutable by a trusted address. Changes are queued through the LZBridgeAndDelegateConfig timelock behind a configurable delay (1-30 days, suggested 1 day) before it can take effect, giving the community an observation window to detect and cancel a malicious or mistaken re-configuration. The emergency role can cancel any queued action. See the Timelock behaviour below for the full categorization of which changes are timelocked vs held to direct OCG / admin control.
Endpoint configuration
All endpoint parameters are explicitly pinned and validated post-activation. No part of the bridge inherits a LayerZero default.
- LZ endpoint: the canonical V2 endpoint at
0x1a44076050125825900e736c501f859c50fE728c on Ethereum, Arbitrum, Optimism, and Base, and 0x6F475642a6e85809B1c36Fa62763669b1b48DD5B on Berachain (Berachain uses a different V2 deployment).
- Message libraries:
SendUln302 and ReceiveUln302 pinned per chain (full addresses in Appendix A).
- Confirmations: set per source chain: Ethereum 15, Arbitrum 20, Optimism 20, Base 10, Berachain 20. In V2 these are configured on the source chain's send-side ULN and mirrored on every destination chain's receive-side ULN for that source (so for an
A → B pathway, both A's send ULN and B's receive ULN for A carry A's source-side value).
- Enforced options: a Type 3 floor of 200,000 gas for
lzReceive execution on every destination. Callers cannot send a message with less; excess gas is not refunded, so this number is sized to cover decode, mint, and state updates.
- Executor: the canonical LayerZero V2 executor pinned per chain (per-chain addresses in Appendix A).
- Max message size: 10,000 bytes, set explicitly per send pathway (V2 requires this; defaults are not inherited). The actual `MSG_BRIDGE_OHM` payload — a `uint8` message type plus the encoded `(address recipient, uint256 amount)` body — is well under 200 bytes; 10,000 matches the LayerZero V2 OApp example default and leaves comfortable headroom for any future compose-message extensions.
- Delegate: a dedicated
LZEndpointDelegate policy is registered as the gateway's LayerZero V2 endpoint delegate (via the gateway's setDelegate, gated by bridge_configurator). Send/receive-library selection and ULN/Executor config are performed through the delegate's ILZEndpointV2Authorized functions, which are also gated by bridge_configurator, so every such change is queued through the LZBridgeAndDelegateConfig timelock. The delegate additionally exposes the inbound messaging-channel management functions (skip, nilify, burn, clear), gated by bridge_channel_manager / bridge_admin / admin for immediate incident response with no timelock delay.
Access control and re-enable pattern
Configuration authority is split across several Kernel roles:
bridge_configurator (the LZBridgeAndDelegateConfig timelock policy) – the sole holder of the configuration role on the gateway and delegate, and the periphery's pinned configurator.
bridge_admin (DAO MS) – the primary proposer role inside the timelock for most configuration sub-actions, and the direct gate on the one-time initializeBridgedSupply bootstrap.
bridge_rate_limiter (DAO MS) – narrow proposer role for the four rate-limit mutators (setOutRateLimits, setInRateLimits, clearOutboundInFlight, clearInboundInFlight) only, so routine throughput adjustments can be delegated without granting broader proposer powers. bridge_admin and admin retain proposer access.
bridge_channel_manager (currently not assigned)– narrow role for the delegate's inbound channel-management functions (skip, nilify, burn, clear), which are not timelocked so the bridge can respond to an in-progress incident immediately. bridge_admin and admin also satisfy this gate.
bridge_facilitator (the LZCrossChainBridge periphery contract) – the only role authorized to call burnAndSend on the gateway.
In addition, the gateway and periphery inherit the new EnablerV2 / ReEnablerGracePeriod pattern. ReEnablerGracePeriod introduces a new primitive whereby, after a contract has been disabled, an authorized caller (the DAO MS, acting as MANAGER_ROLE / re-enabler) can re-enable the contract within a configurable grace window (default 1 day) without altering any parameters and without requiring an OCG proposal. Once the grace window expires, re-enablement falls back to a full governance proposal. Going forward, this lets us treat "pause the bridge as a precaution" as a low-friction action rather than something that triggers a multi-week recovery.
Both contracts also expose a rescue function (gated to admin / bridge_admin) for stuck ERC20 or native balances, following the ERC-7528 native-asset convention.
Timelock behaviour {#timelock-behaviour}
The timelock is implemented as the LZBridgeAndDelegateConfig policy, which holds the bridge_configurator role on the gateway and delegate and the periphery's pinned configurator. For any mutator gated by that role, the policy provides the timelock: callers must queue the action and execute it only after the delay elapses. Everything else is gated directly.
Queued actions can be cancelled only by the emergency role, and execution is permissionless once the delay elapses, within a 3-day execution window. The configurable delay is bounded to between 1 and 30 days.
The receive flag is otherwise managed automatically by the policy lifecycle — enable() and the re-enable path set it true, disable() sets it false — so the manual setIsReceiveEnabled override exists only for the disabled state during a gateway replacement, which is why it is not timelocked.
The full per-function matrix is in Appendix B.
Migration plan
Migration is sequenced so that the old bridge stops accepting new sends before any new bridged supply is written, and so that the new bridge only goes live on Ethereum once all L2s have been configured. The operational steps:
- Deploy contracts on all five chains:
LZBridgeGateway, LZEndpointDelegate, LZCrossChainBridge, and LZBridgeAndDelegateConfig. On Ethereum, also deploy the single-use LZBridgeActivator, which batches the full canonical configuration into one call (working around the OCG 15-action limit).
- Pre-OCG MS batch on Ethereum – activate the gateway, delegate and config policies in the Kernel (the old bridge remains active; no user-visible change).
- OCG proposal on Ethereum – grant the permanent roles (
bridge_admin, bridge_rate_limiter, and manager to the DAO MS; bridge_facilitator to the periphery); grant temporary admin / bridge_admin / bridge_configurator to LZBridgeActivator; enable the delegate; run LZBridgeActivator.activate() (which sets the delegate on the gateway, pins libraries and ULN/Executor config with four required DVNs and the NIL-pinned optional slot, sets peers and enforced options, sets the rate limits, and enables the gateway); revoke the activator's temporary roles; enable the LZBridgeAndDelegateConfig timelock; and grant it the permanent bridge_configurator role, so all subsequent configuration is routed through the timelock. Every pinned value is validated after it is written.
- Stop old-bridge traffic – confirm
bridgeActive = false on every old CrossChainBridge (Ethereum + each L2). Note: given that the old bridge is already disabled, this step is only to confirm it.
- Set the bridged-supply snapshot on Ethereum –
calc_bridged_supply.sh verifies the old bridges are disabled, checks for in-flight or unretried-failed V1 messages (with a LayerZero Scan cross-check), and computes the snapshot. If any failed messages exist, retry_failed_messages.sh retries them first. The snapshot is written via the one-time initializeBridgedSupply, which also grants the gateway the matching MINTR mint approval.
- Configure and activate each non-canonical bridge – per L2, a sequence of DAO MS batches: activate the gateway, delegate, and timelock-config policies in the Kernel (deactivating the old bridge); grant roles, including a temporary
bridge_configurator to the DAO MS; run configureAndEnable (sets the delegate, pins libraries, ULN/Executor, peers, enforced options, and rate limits per remote, then enables the gateway and delegate); then revoke the temporary bridge_configurator from the DAO MS and grant it permanently to that L2's timelock-config policy.
- Activate the periphery – pin each periphery bridge's
configurator to its LZBridgeAndDelegateConfig, verify that the old CrossChainBridge in the Kernel is deactivated, and enable LZCrossChainBridge.
Rationale
This upgrade:
- Migrates LZ V1 to V2 and closes design weaknesses that motivated halting the bridge: it adds canonical-chain supply accounting, removes the unsafe V1 retry path, pins the endpoint configuration instead of inheriting LayerZero defaults, and adds per-route rate limits.
- Applies the rsETH lesson: four required DVNs with a NIL-pinned optional slot, bidirectional rate limits, and the bridged-supply cap together ensure no single DVN compromise can drain the protocol.
Staying on LayerZero V2 is a security and decentralization decision. V2 lets the protocol choose its own verifier set and the consensus required over it. Olympus pins four independent DVN operators and requires all of them to attest before any message is delivered. Verification is split between different organizations, so trust is not concentrated in any single party (including LayerZero’s own DVN), and no one of them can pass a message alone.
Preserving existing tooling and operational knowledge is also a benefit. And the facilitator/gateway split keeps the provider decision reversible: because the user-facing periphery holds no protocol permissions.
Risks and open questions
| Risk | Mitigation |
| Smart-contract vulnerability in the new contracts | Guardian audit complete; all audit-flagged findings are remediated in PR #284. Independent fork tests cover the full activator + L2 batch path. |
| Misconfiguration during migration | On Ethereum the LZBridgeActivator validates every pinned value after writing it; on each L2 the configureAndEnable batch performs the same post-write validation. A silent misconfiguration on any chain reverts. |
| Single DVN compromise | Four required DVNs; optional DVN slot pinned to NIL; rate limits and supply cap hold even if the verifier layer fails. |
| Drain via a compromised non-canonical chain | Inbound rate limit per L2 and the bridged-supply cap bound the worst-case loss. |
| Bridged-supply snapshot inaccuracy | calc_bridged_supply.sh verifies all old bridges are disabled and checks for in-flight or failed V1 messages before snapshotting. the timelocked increaseBridgedSupply / decreaseBridgedSupply are available for correction if needed (queued via LZBridgeAndDelegateConfig, proposed by bridge_admin / admin); underflow protection prevents exploitation. |
Open questions for community input:
- Timelock delay length. The bridge timelock (
LZBridgeAndDelegateConfig) exposes a configurable delay set via queueSetTimelockDelay, bounded to between 1 and 30 days, with a fixed 3-day execution window once a queued action becomes executable. The suggested value is 1 day.
Next steps
- Community discussion of the open questions above (timelock delay).
- Final audit sign-off on PR #284 (audit fixes).
- Formalize into an OIP and run the Snapshot vote.
- Execute the OCG proposal on Ethereum and the multisig batches across the five chains.
- Re-enable user-facing bridging.
Appendix A: Full configuration
LayerZero V2 endpoint
| Chain | Endpoint |
| Ethereum, Arbitrum, Optimism, Base | 0x1a44076050125825900e736c501f859c50fE728c |
| Berachain | 0x6F475642a6e85809B1c36Fa62763669b1b48DD5B |
Supported chains
| Chain | Role | LayerZero EID |
| Ethereum | Canonical (mint/burn authority, bridged-supply tracking) | 30101 |
| Arbitrum | Non-canonical | 30110 |
| Optimism | Non-canonical | 30111 |
| Base | Non-canonical | 30184 |
| Berachain | Non-canonical | 30362 |
Message libraries (pinned per chain)
| Chain | SendUln302 | ReceiveUln302 |
| Ethereum | 0xbB2Ea70C9E858123480642Cf96acbcCE1372dCe1 | 0xc02Ab410f0734EFa3F14628780e6e695156024C2 |
| Arbitrum | 0x975bcD720be66659e3EB3C0e4F1866a3020E493A | 0x7B9E184e07a6EE1aC23eAe0fe8D6Be2f663f05e6 |
| Optimism | 0x1322871e4ab09Bc7f5717189434f97bBD9546e95 | 0x3c4962Ff6258dcfCafD23a814237B7d6Eb712063 |
| Base | 0xB5320B0B3a13cC860893E2Bd79FCd7e13484Dda2 | 0xc70AB6f32772f59fBfc23889Caf4Ba3376C84bAf |
| Berachain | 0xC39161c743D0307EB9BCc9FEF03eeb9Dc4802de7 | 0xe1844c5D63a9543023008D332Bd3d2e6f1FE1043 |
Required DVNs (4 per route, no optional DVNs)
| Chain | LayerZero Labs | Canary | Nethermind | Google Cloud / Horizen* |
| Ethereum | 0x589dEDbD617e0CBcB916A9223F4d1300c294236b | 0xa4fe5a5b9a846458a70cd0748228aed3bf65c2cd | 0xa59ba433ac34d2927232918ef5b2eaafcf130ba5 | 0xD56e4eAb23cb81f43168F9F45211Eb027b9aC7cc / 0x380275805876ff19055ea900cdb2b46a94ecf20d |
| Arbitrum | 0x2f55C492897526677C5B68fb199ea31E2c126416 | 0xf2e380c90e6c09721297526dbc74f870e114dfcb | 0xa7b5189bca84cd304d8553977c7c614329750d99 | 0xD56e4eAb23cb81f43168F9F45211Eb027b9aC7cc / 0x19670df5e16bea2ba9b9e68b48c054c5baea06b8 |
| Optimism | 0x6A02D83e8d433304bba74EF1c427913958187142 | 0x5b6735c66d97479ccd18294fc96b3084ecb2fa3f | 0xa7b5189bca84cd304d8553977c7c614329750d99 | 0xD56e4eAb23cb81f43168F9F45211Eb027b9aC7cc / 0x9e930731cb4a6bf7ecc11f695a295c60bdd212eb |
| Base | 0x9e059a54699a285714207b43B055483E78FAac25 | 0x554833698ae0fb22ecc90b01222903fd62ca4b47 | 0xcd37ca043f8479064e10635020c65ffc005d36f6 | 0xD56e4eAb23cb81f43168F9F45211Eb027b9aC7cc / 0xa7b5189bca84cd304d8553977c7c614329750d99 |
| Berachain | 0x282b3386571f7f794450d5789911a9804FA346b4 | 0x06e8042729cef3ae6d6db5350f48f9d736c3675d | 0xdd7b5e1db4aafd5c8ec3b764efb8ed265aa5445b | – / 0xecbaa45c33ce6fa284995e5f8314f5bc7f1c2008 |
Confirmations (source-side; mirrored on receive)
| Source chain | Confirmations |
| Ethereum | 15 |
| Arbitrum | 20 |
| Optimism | 20 |
| Base | 10 |
| Berachain | 20 |
For a directed pathway A → B, A's source-side count is written into A's send-ULN config and into every other chain's receive-ULN config for A's EID.
Executor and message size
| Chain | Executor |
| Ethereum | 0x173272739Bd7Aa6e4e214714048a9fE699453059 |
| Arbitrum | 0x31CAe3B7fB82d847621859fb1585353c5720660D |
| Optimism | 0x2D2ea0697bdbede3F01553D2Ae4B8d0c486B666e |
| Base | 0x2CCA08ae69E0C44b18a57Ab2A87644234dAebaE4 |
| Berachain | 0x4208D6E27538189bB48E603D6123A94b8Abe0A0b |
Max message size: 10,000 bytes, set explicitly per send pathway.
Enforced options: Type 3, 200,000 gas minimum for lzReceive on every destination, for MSG_BRIDGE_OHM (message type 1).
Rate limits (24-hour sliding window, offsetting)
| Source | Direction | Per-remote limit |
| Ethereum | outbound (to any L2) | 100,000 OHM |
| Ethereum | inbound (from any L2) | 55,000 OHM |
| Any L2 | outbound (to Ethereum) | 50,000 OHM |
| Any L2 | outbound (to another L2) | 100,000 OHM |
| Any L2 | inbound (from Ethereum or another L2) | 110,000 OHM |
Sizing rationale (from 24 months of V1 bridge data): the largest historical p95 daily flow on any single route was ETH→Berachain at 118,825 OHM, with p50/day at 844; every other route had p95 < 2,000 OHM/day. The configured limits cover p95 on every route except ETH→Berachain, which sits slightly above p95 in the current draft.
Bridged-supply cap
bridgedSupply is incremented on every outbound burnAndSend from Ethereum and decremented (with underflow check) on every inbound lzReceive on Ethereum. Initial value is set by the DAO MS post-migration via the one-time initializeBridgedSupply to match the legitimate non-canonical OHM supply at the snapshot moment (sum of OHM totalSupply() on Arbitrum, Optimism, Base, and Berachain); the same call grants the gateway the matching MINTR mint approval..
Appendix B: Timelock matrix
The implemented timelock gating. Timelocked actions are queued through the LZBridgeAndDelegateConfig policy (built on the shared TimelockBatchQueue base) and are executable only after the configured delay (1–30 days) elapses, within a 3-day execution window. For timelocked rows, the on-contract gate is bridge_configurator.
LZBridgeGateway
| Function | On-contract gate | Timelocked? (proposer) |
setPeer | admin | no |
setEnforcedOptions | admin | no |
setIsReceiveEnabled | emergency / admin (only while disabled) | no |
initializeBridgedSupply | bridge_admin / admin (one-time bootstrap) | no |
burnAndSend | bridge_facilitator | no |
setDelegate | bridge_configurator | yes (bridge_admin / admin) |
increaseBridgedSupply | bridge_configurator | yes (bridge_admin / admin) |
decreaseBridgedSupply | bridge_configurator | yes (bridge_admin / admin) |
setOutRateLimits | bridge_configurator | yes (bridge_rate_limiter / bridge_admin / admin) |
setInRateLimits | bridge_configurator | yes (bridge_rate_limiter / bridge_admin / admin) |
clearOutboundInFlight | bridge_configurator | yes (bridge_rate_limiter / bridge_admin / admin) |
clearInboundInFlight | bridge_configurator | yes (bridge_rate_limiter / bridge_admin / admin) |
setGracePeriod | bridge_configurator | yes (bridge_admin / admin) |
rescue | manager / admin | no |
enable | admin | no |
disable | emergency / admin | no |
reEnable | manager | no |
LZEndpointDelegate
| Function | On-contract gate | Timelocked? (proposer) |
setSendLibrary | bridge_configurator | yes (bridge_admin / admin) |
setReceiveLibrary | bridge_configurator | yes (bridge_admin / admin) |
setReceiveLibraryTimeout | bridge_configurator | yes (bridge_admin / admin) |
setEndpointConfig | bridge_configurator | yes (bridge_admin / admin) |
skip | bridge_channel_manager / bridge_admin / admin | no |
nilify | bridge_channel_manager / bridge_admin / admin | no |
burn | bridge_channel_manager / bridge_admin / admin | no |
clear | bridge_channel_manager / bridge_admin / admin | no |
enable | admin | no |
disable | emergency / admin | no |
LZBridgeAndDelegateConfig (timelock policy)
| Function | Gate / proposer | Timelocked? |
queueSetTargetGateway / queueSetTargetDelegate / queueSetTargetFacilitator | admin | yes |
queueSetTimelockDelay | admin | yes |
enable | admin | no |
disable | emergency / admin | no |
cancelQueuedAction | emergency | no |
LZCrossChainBridge
| Function | On-contract gate | Timelocked? (proposer) |
setGateway | configurator (timelock) | yes (bridge_admin / admin) |
setReEnabler | configurator (timelock) | yes (bridge_admin / admin) |
setConfigurator | owner (bootstrap) → configurator (rotation) | rotation: yes (admin) |
setGracePeriod | configurator (timelock) | yes (bridge_admin / admin) |
enable / disable | owner | no |
reEnable | re-enabler (DAO MS) | no |
rescue | owner | no |
Appendix C: LayerZero V2 integration checklist compliance
Mapping the RFC's configuration against LayerZero's published OApp/OFT integration checklist, together with the post-rsETH guidance from LayerZero's May 9 statement and Olympus-specific operational checks. The # column is this RFC's own sequential enumeration for reference; it does not correspond to numbered items in any single LayerZero document.
| # | Checklist item | Where addressed |
| 1 | Use latest LZ packages; don't copy contracts directly | The contracts use the package versions @lz‑oapp‑evm‑0.4.1 and @lz‑evm‑protocol‑v2‑3.0.162. The OApp modules (OAppCore, OAppSender, OAppReceiver, OAppOptionsType3) from @layerzerolabs/oapp-evm v0.4.1 are re-implemented inline in LZBridgeGateway rather than inherited, because the upstream bases assume an Ownable owner that conflicts with the Kernel/Policy access model; the rationale is documented in the audit README. |
| 2 | setPeer on every directional pathway | All 20 routes (5 chains × 4 remote peers) set during the OCG proposal + L2 batches; validated post-write. |
| 3 | Peer bytes32 matches deployed remote | Validated by _validateConfigureAndEnable on every L2 batch and by _validateLZConfig in the OCG proposal. |
| 4 | Pin Send Library | SendUln302 pinned per chain (Appendix A); no default inheritance. |
| 5 | Pin Receive Library | ReceiveUln302 pinned per chain (Appendix A); no default inheritance. |
| 6 | Audit getConfig for unintended default fallback | Post-batch validation reads getConfig and asserts pinned values; the L2 batch was extended in PR #284 (L-03) to mirror these checks. |
| 7 | Required DVNs configured explicitly per pathway | 4 required DVNs pinned per route (Appendix A). |
| 8 | Optional DVNs and threshold configured intentionally | 0 optional DVNs, optionalDVNCount pinned to NIL (type(uint8).max) — eliminates the drag-along vector flagged as audit M-01. |
| 9 | DVN config matches on both sides of the pathway | Same DVN set written into source send-ULN and destination recv-ULN; symmetry verified by the activator and L2 batches. |
| 10 | Verify DVN addresses against LZ DVN providers tables | Addresses sourced from docs.layerzero.network/contracts/dvn-addresses; pinned in LZConfigLib.sol. |
| 11 | DVNs and Executor implement their interfaces correctly | Restricted to well-established providers (LZ Labs, Canary, Nethermind, Google Cloud, Horizen). |
| 12 | Post-rsETH: no 1/1 DVN; 3/3 minimum, 5/5 default | 4/4 required + 0 optional pinned to NIL — exceeds the 3/3 floor and matches the hardened post-incident direction seen in Ethena, Ether.fi, USDT0, and Ondo. |
| 13 | Source confirmations set high enough to make reorgs infeasible | Explicit per-chain values pinned (ETH 15, ARB 20, OPT 20, BASE 10, BERA 20); match the current LayerZero V2 defaults for these chains. |
| 14 | Source conf used symmetrically on send + recv | For pathway A → B, A's source-side count is written into A's send-ULN and into every destination's recv-ULN for A's EID. |
| 15 | Set Executor explicitly | Per-chain executor address pinned (Appendix A). |
| 16 | Set maxMessageSize per pathway | 10,000 bytes pinned per send pathway; well above the actual MSG_BRIDGE_OHM payload (<200 bytes). |
| 17 | setEnforcedOptions with per-message-type gas pin | Type 3 enforced option with a 200,000 gas floor for lzReceive on MSG_BRIDGE_OHM. |
| 18 | Protect delegate / admin with multisig + timelock | The configuration role (bridge_configurator) on the gateway and delegate is held only by the LZBridgeAndDelegateConfig timelock; proposer roles are held by the DAO multisig. Material configuration changes are queued through the timelock (Appendix B). |
| 19 | Review and explicitly set delegate per deployment | A dedicated LZEndpointDelegate policy is explicitly deployed and set as the delegate per deployment — an explicit choice. |
| 20 | Restrict lzReceive to EndpointV2 only | Enforced in LZBridgeGateway.lzReceive. |
| 21 | Validate sender against peer for source EID | Enforced in LZBridgeGateway.lzReceive: _getPeerOrRevert(srcEid) must equal the message sender, otherwise it reverts LZBridgeGateway_OnlyPeer. |
| 22 | (OFT) Burn-on-source / mint-on-destination | Burn via MINTR.burnOhm on send; mint via MINTR.mintOhm inside lzReceive, reachable only by the LZ endpoint for a verified peer and bounded by the gateway's MINTR mint approval (which tracks bridgedSupply). |
| 23 | (OFT) Per-pathway rate limiter | OffsettingRateLimiter (PR #282) — mandatory, bidirectional, per-route, 24h sliding window. |
| 24 | Monitor cross-chain mint/burn parity | bridgedSupply counter on Ethereum with underflow protection (Appendix A); reconciliation is also validated at activation via calc_bridged_supply.sh. |
| 25 | Inbound channel-management functions wired and tested (clear, skip, nilify, burn) | All four exposed on the LZEndpointDelegate, gated to bridge_channel_manager / bridge_admin / admin (immediate, not timelocked), and covered by the test suite. Their impact on bridgedSupply bookkeeping is documented in the delegate NatSpec per PR #284 (I-02). |
| 26 | Manual nonce/state sync after skip / burn / clear | Tracked in the delegate NatSpec; an operational runbook update for bridge_channel_manager / bridge_admin is a planned follow-up to this RFC, not blocking activation. |
| 27 | Monitor pathway health, DVN attestations, executor liveness, nonce gaps | Olympus monitoring stack will be extended to track these per route. |
| 28 | Emergency response: precautionary pause, library/DVN re-pinning, per-message control | Three layered responses: (1) the EnablerV2 / ReEnabler pattern (PR #258) gives the DAO MS a fast precautionary disable with a 1-day grace-window re-enable; (2) libraries, DVN sets, and ULN/Executor config can be re-pinned through the delegate (timelocked); and (3) the inbound channel-management functions in rows 26–29 (skip / nilify / burn / clear) contain individual stuck or malicious inbound messages immediately. |