Cross-chain bridges sit at the most dangerous intersection in decentralized finance: they hold concentrated value, operate across trust boundaries they cannot fully control, and introduce complexity that compounds with every chain they support. Cross-chain bridges have become the single most exploited attack surface in Web3. Between June 2021 and September 2024, approximately $4.3 billion was stolen across 49 bridge incidents, representing roughly 40% of all Web3 value hacked during that period.
That number is not a product of bad luck. It reflects a structural reality: bridges represent less than 10% of DeFi’s total value locked, yet account for over 50% of all funds stolen in DeFi exploits, because bridges are uniquely complex — they must maintain security guarantees across multiple trust boundaries, consensus mechanisms, and execution environments simultaneously.
This article dissects the specific failure categories that make bridges uniquely hazardous: where message replay originates, how validator rotation creates windows of exposure, why finality assumptions fail across chains, and what differentiates the failure modes of optimistic and ZK-based bridge architectures. Code and a practical audit checklist follow.
1. Message Replay Across Chain Forks and the Chain ID Fix
The most fundamental cross-chain vulnerability is also the oldest: a signed message that is valid on one chain can be valid on another if the two chains share the same signature scheme and the signed payload does not contain a chain-specific identifier.
Suppose two chains share the same transaction format, signature scheme, and address derivation. A transaction signed for one chain may also verify on the other unless something in the signed payload distinguishes them. This became a practical issue around contentious forks. The key insight is that a signature is only as context-specific as the data it signs. If chain identity is not in the signed message, the cryptography cannot infer it after the fact.
EIP-155 adds simple replay attack protection by including the chain ID in transaction signatures. Transactions include the chain identifier (1 for mainnet, etc.) when computing the signing hash, preventing a transaction signed for one network from being replayed on another. This was critical after the Ethereum/Ethereum Classic split. Without chain IDs, a transaction sent on one chain could be copied and executed on the other.
The ETHW Bridge Exploit: A Live Example
The mechanics of this attack were demonstrated in production immediately after the Ethereum Merge in September 2022. BlockSec detected that exploiters were replaying the message (calldata) of the PoS chain on EthereumPow. According to BlockSec, the root cause of the exploit was due to the fact that the Omni cross-chain bridge on the ETHW chain used old chainID and was not correctly verifying the correct chainID of the cross-chain message.
The detail here matters. The bridge had chainID verification logic. It simply read the wrong value. BlockSec’s analysis of the Omni bridge source code showed that the logic to verify chainID was present, but the verified chainID used in the contract was pulled from a value stored in the storage named unitStorage. The team explained that this was not the correct chainID collected through the CHAINID opcode, which was proposed by EIP-1344 and exacerbated by the resulting fork after the Ethereum Merge — “This is probably due to the fact that the code is quite old (using Solidity 0.4.24).”
The exploiter first transferred 200 WETH through the Omni bridge of the Gnosis chain, and then replayed the same message on the PoW chain and got an extra 200 ETHW. This is the canonical pattern: lock on the canonical chain, replay the message on the fork, receive double the output.
The EIP-1344 Opcode Distinction
The correct fix is to use the CHAINID opcode (introduced in EIP-1344, part of Istanbul), not a storage variable. Storage variables can be set incorrectly at deployment or lag behind after a fork. The opcode returns the live chain ID as reported by the chain itself. For bridge message hashing, chain ID should be a mandatory field computed dynamically at call time, not cached.
EIP-155 prevents cross-chain replay by including CHAIN_ID in the transaction signing hash and encoding the chain identity into the signature recovery value v, so a signature for one chain does not verify as the same authorization on a different chain; however, old-style signatures (v = 27/28) remain valid for backward compatibility and can keep replay surfaces alive until the ecosystem stops supporting them.
Message Replay Prevention: Solidity Implementation
The following demonstrates correct message hash construction for cross-chain bridges, combining chain ID binding with a nonce-based replay guard:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/// @title BridgeMessageHasher
/// @notice Canonical message hash construction and replay prevention for cross-chain bridges.
/// Chain ID is read live via assembly to prevent stale-storage attacks post-fork.
contract BridgeMessageHasher {
// ─── Storage ────────────────────────────────────────────────────────────────
/// @dev Tracks consumed nonces per originating chain per sender.
/// mapping(sourceChainId => mapping(sender => mapping(nonce => used)))
mapping(uint256 => mapping(address => mapping(uint256 => bool))) public usedNonces;
/// @dev Tracks processed message hashes to prevent message-level replay
/// independently of nonce state (defense-in-depth).
mapping(bytes32 => bool) public processedMessages;
// ─── Events ─────────────────────────────────────────────────────────────────
event MessageExecuted(bytes32 indexed messageHash, address indexed recipient, uint256 amount);
// ─── Errors ─────────────────────────────────────────────────────────────────
error ReplayedNonce(uint256 sourceChainId, address sender, uint256 nonce);
error ReplayedMessageHash(bytes32 messageHash);
error InvalidSignature();
error WrongDestinationChain(uint256 expected, uint256 got);
// ─── Core hash construction ──────────────────────────────────────────────────
/// @notice Constructs the canonical message hash for a cross-chain transfer.
///
/// Field layout (all 32 bytes each, ABI-encoded then keccak256):
/// [0] Domain separator tag (prevents hash collisions with other typed structs)
/// [1] sourceChainId (chain where the deposit was made)
/// [2] destinationChainId (this chain — MUST match block.chainid at execution)
/// [3] nonce (per-sender, per-source-chain counter)
/// [4] sender (depositor on the source chain)
/// [5] recipient (beneficiary on the destination chain)
/// [6] token (canonical token address on this chain)
/// [7] amount (gross amount to mint/release)
/// [8] deadline (unix timestamp after which this message is invalid)
///
/// @dev destinationChainId is read via assembly to guarantee the live opcode value,
/// not a cached storage slot that could be wrong after a fork.
function buildMessageHash(
uint256 sourceChainId,
uint256 nonce,
address sender,
address recipient,
address token,
uint256 amount,
uint256 deadline
) public view returns (bytes32) {
// Read destination chain ID from the CHAINID opcode — never from storage.
uint256 destChainId;
assembly {
destChainId := chainid()
}
return keccak256(
abi.encode(
// Domain separator: prevents cross-type hash collisions
keccak256("BridgeMessage(uint256 sourceChainId,uint256 destinationChainId,"
"uint256 nonce,address sender,address recipient,"
"address token,uint256 amount,uint256 deadline)"),
sourceChainId,
destChainId, // live opcode — fork-safe
nonce,
sender,
recipient,
token,
amount,
deadline
)
);
}
/// @notice Executes an inbound cross-chain message after verifying no replay has occurred.
/// @dev In production, `sig` would be verified against a trusted validator set or
/// ZK proof. Simplified here to focus on replay logic.
function executeMessage(
uint256 sourceChainId,
uint256 destinationChainId,
uint256 nonce,
address sender,
address recipient,
address token,
uint256 amount,
uint256 deadline,
bytes calldata sig // validator signature over the message hash
) external {
// 1. Enforce this message was meant for this specific chain.
uint256 liveChainId;
assembly { liveChainId := chainid() }
if (destinationChainId != liveChainId)
revert WrongDestinationChain(liveChainId, destinationChainId);
// 2. Enforce deadline — prevents time-warp replay after a long delay.
require(block.timestamp <= deadline, "Message expired");
// 3. Reconstruct the canonical hash.
bytes32 msgHash = buildMessageHash(
sourceChainId, nonce, sender, recipient, token, amount, deadline
);
// 4. Reject if this exact message hash was already processed
// (message-level replay guard, independent of nonce state).
if (processedMessages[msgHash])
revert ReplayedMessageHash(msgHash);
// 5. Reject if this nonce was already consumed by this sender on this source chain.
if (usedNonces[sourceChainId][sender][nonce])
revert ReplayedNonce(sourceChainId, sender, nonce);
// 6. Verify validator signature (production: multi-sig or ZK proof here).
_verifySignature(msgHash, sig);
// 7. Mark consumed — both guards updated atomically before any state change.
processedMessages[msgHash] = true;
usedNonces[sourceChainId][sender][nonce] = true;
// 8. Execute: mint/release tokens to recipient.
_release(token, recipient, amount);
emit MessageExecuted(msgHash, recipient, amount);
}
// ─── Internal helpers ────────────────────────────────────────────────────────
/// @dev Placeholder for actual multi-sig or threshold signature verification.
function _verifySignature(bytes32 /* msgHash */, bytes calldata /* sig */) internal pure {
// Production: ecrecover against a validator set, or verify a SNARK.
}
/// @dev Placeholder for the vault's release / mint logic.
function _release(address /* token */, address /* recipient */, uint256 /* amount */) internal {
// Production: IERC20(token).transfer(recipient, amount) or IMintable(token).mint(...)
}
}
Key design choices:
chainid()is read via assembly on every call — not from a constructor variable that could be wrong post-fork.- Both a nonce map and a processed-hash map provide redundant replay guards. If a nonce implementation has a bug (e.g., accepts nonce 0 repeatedly), the hash map stops it.
- The domain separator tag is hashed into the payload so the same field layout cannot collide with a different message type.
- Deadline enforcement prevents an attacker from archiving a valid signature and replaying it long after the sender’s intent has changed.
2. Validator Set Manipulation During Rotation
As of May 2024, most cross-chain bridges still validate cross-chain transactions through external validators and federations. In this case, the attacker can compromise the majority of the small-size validator committee by stealing their private keys.
Many cross-chain bridges are operated using a small group of validators or a multisignature wallet managed by a limited number of parties. While this may simplify development and coordination, it creates a single point of failure. If a majority of these validators are compromised — through phishing, malware, social engineering, or insider collusion — attackers can effectively seize control of the bridge.
The Ronin exploit is the canonical case. Ronin was using a Proof-of-Authority (PoA) consensus model that sacrificed some decentralization and security in favor of speedy transaction processing. This meant that the security of the entire Ronin network relied on nine validators. If an attacker could compromise the private keys of a majority — specifically five out of the nine — they could validate any malicious transactions. An attacker exploited this vulnerability by creating and approving a transaction that transferred 173,600 ETH and 25.5M USDC to their own address.
The Rotation Window Problem
Static validator sets are dangerous. But rotating validator sets introduce a distinct class of attack: the window between when old validators are decommissioned and new ones are recognized.
During a validator rotation, several failure modes emerge:
1. Transition front-running. If the pending new validator set is observable on-chain before the rotation is finalized, an attacker can try to acquire enough old-set keys to push through a fraudulent message in the final blocks before the transition.
2. Double-signing across eras. If old validator keys are not revoked and messages do not include an epoch or set version, a message signed by the old set can be accepted as though it were signed by the new set — or injected after a rollback that re-instates old state.
3. Bribe/collude on exit. A validator who is about to be rotated out has a smaller long-term reputation stake. That makes them cheaper to bribe in the final epoch of their tenure. Bridges that do not slash exiting validators for post-rotation signatures are particularly exposed.
Mitigation pattern: Bind every message to a specific validator set epoch. The message hash must include a validatorSetHash or epochNumber field so that a signature from epoch N-1 cannot be applied against epoch N’s acceptance logic. Validator keys should be considered burned — not merely inactive — once rotation is complete.
3. Finality Assumptions and Reorg Risk
A blockchain reorganization (reorg) occurs when a previously accepted canonical chain is replaced by a longer or heavier chain. This is a normal part of Proof-of-Work and some Proof-of-Stake consensus mechanisms, but it introduces risk. For applications like decentralized exchanges, bridges, or payment processors, a reorg can lead to double-spending, transaction rollbacks, and broken state assumptions.
For bridges, the asymmetry is the core hazard: if a bridge assumes a transaction is permanent on the source chain after 10 blocks, but the source chain undergoes a reorganization and deletes those blocks, the bridge has already minted assets on the destination chain based on a transaction that no longer exists. This creates a “double spend” scenario.
Different chains have fundamentally different finality models:
- Ethereum (post-Merge): Single-slot finality under normal conditions via Casper FFG. Approximately 2 epochs (~12.8 minutes) for economic finality. Reorgs beyond 1 slot are rare but have occurred.
- Bitcoin: Probabilistic finality. The standard 6-confirmation rule (~60 minutes) assumes a 51% attack is impractical, not impossible.
- Proof-of-Authority L2s / Alt-L1s: Finality depends on the validator count and liveness. A halted chain means finality never arrives.
- Optimistic rollups: L2-to-L1 messages are not final until the fraud proof window closes — 7 days on Arbitrum and Optimism mainnet.
Finality requirements are chain-appropriate — Ethereum needs ~15 minutes for economic finality; Solana achieves it in seconds. Your bridge must respect each chain’s finality guarantees.
The “Fast Bridge” Trap
Many bridges advertise instant bridging by front-running the finality window with liquidity providers. These fast bridges accept the reorg risk explicitly — the LP absorbs the risk in exchange for a fee. The hazard arises when a protocol that should wait for finality quietly skips the wait to improve UX, without documenting or pricing the risk to users.
Not all blockchains offer instant finality. Some bridges confirm messages from blocks that could later be reorganized. This lets attackers forge deposits. The attack flow: deposit on source, receive on destination before finality, reorg the source deposit away, keep the destination funds.
For cross-chain applications, the reorg risk is multiplicative; you must evaluate the reorg tolerance on both the source and destination chains.
4. Canonical vs. Non-Canonical Bridge Trust Models
Not all bridges carry equal trust weight. The distinction between canonical and non-canonical bridges is one of the most practically important concepts in cross-chain security.
Canonical bridges are deployed and maintained by the chain or rollup team itself. The Arbitrum native bridge, the Optimism gateway, and Starknet’s official bridge are canonical. Their trust model derives from the underlying chain’s security — an honest majority of the L1 validator set implicitly backs the canonical bridge’s state commitments. For security, use the native “canonical” bridge of the network.
Non-canonical (third-party) bridges introduce their own trust assumption on top of the underlying chains. Bridges are attractive targets because they hold large pools of locked liquidity and introduce complex multi-chain logic that is difficult to audit exhaustively. A single vulnerability can drain assets across multiple chains simultaneously.
The critical implication: when a user bridges via a third-party protocol, they accept the security model of that protocol in addition to the security models of both chains. If the third-party bridge uses a 5-of-9 multisig and either chain is secure, the weakest link is the bridge’s multisig — not the chain.
Trust Accumulation and the “Security Stack” Problem
A composed DeFi position that involves:
- Depositing into a vault on Chain A
- Bridging a receipt token via a third-party bridge
- Using that receipt token as collateral on Chain B
…inherits the failure modes of every layer. Canonical bridges at least compress the trust stack by removing one independent trust assumption. Non-canonical bridges are not necessarily less secure, but their security must be evaluated independently and with rigor.
5. Oracle Dependencies for Cross-Chain State
Bridges often rely on “oracles” to tell them the price or status of a chain. If the oracle is compromised, the bridge can be exploited.
The oracle dependency problem in bridges goes deeper than price feeds. Many bridges rely on external data providers to resolve questions like:
- “Has this deposit transaction been finalized on Chain A?”
- “What is the current validator set on Chain A?”
- “Has this withdrawal been processed on the destination?”
Critical data management requires full replicas of data in a trusted environment under complete control. When this is not possible, organisations turn to trusted data providers, such as AWS or Infura, to access their needed data. Trusting data providers can lead to issues of censorship or data breaches.
Oracle Failure Modes in Bridge Context
1. Stale state relay. An oracle that reports source-chain state with a lag can cause the bridge to act on outdated information — either blocking legitimate transactions (false negative) or accepting fraudulent ones based on superseded state (false positive).
2. Single-source oracle. If bridge logic depends on a single RPC provider to confirm finality, that provider becomes a trusted third party. A compromised Infura endpoint returning fabricated inclusion proofs could trigger fraudulent mints.
3. Oracle front-running. In bridges that use price oracles to compute exchange rates, an attacker can manipulate the price oracle in the same block as a bridge transaction to extract value from the exchange rate calculation.
4. Liveness failure. If the oracle network halts, the bridge stalls. Assets are locked. Depending on recovery logic, this can be exploited by an attacker who triggers oracle downtime and then attempts to execute against stale state.
Mitigation: Use Merkle inclusion proofs verified on-chain rather than oracle attestations wherever possible. For validator set changes and state commitments, a light client model that verifies cryptographic proofs against the source chain’s consensus rules eliminates the oracle trust assumption entirely — at the cost of higher on-chain computation.
6. The Trusted vs. Trustless Bridge Spectrum
Bridge security is not binary. It exists on a spectrum defined by how many external parties must behave honestly for the bridge to be secure.
| Architecture | Trust Assumption | Example |
|---|---|---|
| Centralized custodian | 1 of 1 | Custodial CEX bridge |
| Multisig federation | K of N signers | Ronin (5/9), Harmony (2/5) |
| Optimistic (fraud proof) | 1 of N watchers | Nomad, early Rainbow Bridge |
| Light client | Honest majority of source chain | IBC (Cosmos) |
| ZK proof | Correct proof system + honest prover | zkBridge, Wormhole ZK |
Different architectures fail in different ways. Validator-based bridges concentrate risk in signer sets, optimistic systems depend on correct challenge and message-handling logic, and rollup bridges inherit security from proof and data-availability assumptions on the base layer.
The most common message-passing failures are not mysterious: bad verification logic, replay or double-execution paths, weak role separation, unsafe upgrade surfaces, and off-chain components that silently become part of the security boundary.
The Watcher Liveness Problem
Optimistic and fraud-proof bridges reduce trust to “1 of N honest watchers” — a significant improvement over K-of-N multisigs. But the improvement only holds if watchers are live, well-funded for gas, and monitoring the correct state. Optimistic verification reduces validator-key risk but introduces a window during which a malicious relayer can attempt fraud before watchers respond.
An attacker who can censor watchers — through gas price manipulation, L1 congestion, or targeted DDoS — for the duration of the challenge window can finalize a fraudulent state root. This is an economic censorship attack, where an attacker censors the defender’s transactions by bribing block proposers.
7. Failure Modes: Optimistic Bridges vs. ZK Bridges
Optimistic Bridges
Optimistic systems assume a message or state update is valid unless proven otherwise within a dispute window. In rollups, this model is familiar from fraud proofs and challenge periods.
In an Optimistic Rollup, state commitments are published to L1 without any direct proof of the validity of these commitments. Instead, these commitments are considered pending for a period of time called the “challenge window.” If a proposed state commitment goes unchallenged for the duration of the challenge window (currently set to 7 days), then it is considered final.
Specific failure modes:
Uninitialized/incorrect state root acceptance. The Nomad exploit ($190M, 2022) is the definitive example. Due to a vulnerability in the Nomad bridge smart contract, approximately $190 million of assets were stolen. The root cause was that a contract upgrade set the trusted root to 0x00, which meant any message could be processed immediately as if it were already proven — no fraud proof needed. Every subsequent attacker simply replayed the original exploit transaction with their own address.
Challenge period griefing. Fraud proofs only protect the rollup if there is enough time for an honest participant to notice bad claims, gather the relevant L1 data, and respond. But they must also terminate. A dispute protocol with unbounded delay is vulnerable to griefing even if its final correctness is sound in principle.
Fail-open on error. This model is elegant because it can inherit a lot of security from the base layer, but only if several conditions hold at once: state data has to be available, the challenge machinery has to function, and message handling has to avoid unsafe shortcuts. If the optimistic assumptions are broken by bad implementation, the whole design can fail open.
Liquidity pressure incentivizing risk. In optimistic ecosystems, native bridges have problems: cryptoeconomic incentives and withdrawal delays that force users into fee-extracting third-party bridges if you want to execute swaps in less than a week. Fraud proofs require a 7 day withdrawal delay. This incentive pressure pushes users toward less secure alternatives.
ZK Bridges
ZK bridges verify cross-chain messages using cryptographic proofs rather than trusting a set of validators. This removes the human element: security relies on math, not reputation.
A prover generates a succinct mathematical proof that a specific event occurred on Chain A under that chain’s consensus rules. A lightweight verifier contract on Chain B checks the proof. No validators to compromise. No waiting period. No social trust required — only the correctness of the cryptography.
Specific failure modes:
Trusted setup compromise (SNARKs with CRS). Many ZK proof systems require a trusted setup ceremony to generate a common reference string (CRS). If the toxic waste from the ceremony is not destroyed — or if the ceremony is compromised — a malicious prover can construct false proofs that verify as valid. This is a one-time, catastrophic, and silent failure mode. STARKs avoid trusted setups, but at a verification cost penalty.
Verifier contract bugs. The proof itself may be mathematically valid, but the on-chain verifier contract could accept proofs that do not correspond to the claimed statement. Verifier contracts are highly non-trivial Solidity or bytecode implementations of polynomial commitment schemes. A bug in the verifier is worse than a validator key compromise: it is systematic and undetectable via monitoring.
Circuit underconstrained. For correctness, zkBridge does not introduce extra trust assumptions besides those made by the underlying blockchains — namely, both the sender blockchain and the receiver blockchain are consistent and live. However, if the arithmetic circuit that encodes “a valid deposit occurred on Chain A” is underconstrained — meaning it accepts witness values that don’t actually correspond to a real deposit — an attacker can generate a valid proof for a fake event. Circuit auditing is a distinct, specialized skill from Solidity auditing.
Prover centralization. While the verification of ZK proofs is trustless, proof generation is computationally intensive. ZK bridge technology is still early. Wormhole has begun integrating ZK proofs to replace Guardian signatures for certain transfers, and Across V4 adds ZK-proven settlement via Succinct zkVM. Full ZK bridge verification at scale remains a work in progress. A centralized prover service is an operational chokepoint: if it halts, the bridge stalls; if it is compromised, it could withhold proofs selectively (censorship) or, in poorly designed systems, influence which proofs are submitted.
Proof system maturity. No bridge architecture has proven itself immune to exploitation. Zero-knowledge proofs are the most promising path toward trustless verification, with both Wormhole and Across integrating ZK-based settlement in recent upgrades, but the technology is still maturing.
8. The Accounting Invariant: The Cross-Cutting Check
One of the most powerful cross-cutting defenses for any bridge architecture is explicit accounting. The lack of explicit accounting is the shared and signal failure across virtually all bridge attacks. Considering the twelve largest attacks (each over $1M), the details of their vulnerabilities vary considerably, but all share the property that transactions are allowed to be “unbalanced” — outflow can exceed inflow.
Auditing transactions for this property is sufficient to retrospectively and automatically identify each of these past attacks. Moreover, such audits can be performed in real-time — either to alert bridge operators about potential fraud, or prevent such unbalanced transactions from completing altogether.
The invariant to enforce: at no point should the total value of assets released on any destination chain exceed the total value of assets verifiably locked on the source chain. Circuit breakers that pause withdrawals when this invariant is violated are a last-resort but high-value defense.
// Simplified circuit breaker pattern for bridge vaults
contract BridgeVault {
uint256 public totalLocked;
uint256 public totalReleased;
uint256 public constant MAX_OUTFLOW_RATIO_BPS = 9500; // 95% — never release more than locked
bool public paused;
address public guardian;
error CircuitBreakerTripped();
error BridgePaused();
modifier notPaused() {
if (paused) revert BridgePaused();
_;
}
function release(address token, address to, uint256 amount) external notPaused {
// Accounting invariant check before every release
uint256 projectedReleased = totalReleased + amount;
if (projectedReleased * 10_000 > totalLocked * MAX_OUTFLOW_RATIO_BPS) {
paused = true; // auto-pause — requires governance to re-enable
revert CircuitBreakerTripped();
}
totalReleased = projectedReleased;
IERC20(token).transfer(to, amount);
}
function deposit(address token, uint256 amount) external notPaused {
IERC20(token).transferFrom(msg.sender, address(this), amount);
totalLocked += amount;
}
}
interface IERC20 {
function transfer(address, uint256) external returns (bool);
function transferFrom(address, address, uint256) external returns (bool);
}
Bridge Audit Checklist
Use this checklist as a pre-audit gate, not a substitute for a full audit. Every item below corresponds to a real exploit vector documented in production bridge hacks.
Message Integrity
- Message hash includes
chainid()opcode (not a storage variable) for both source and destination chains - Message hash includes a per-sender nonce or a globally unique message ID
- Processed message hashes are recorded on-chain and checked before execution (defense-in-depth against nonce bugs)
- Message hash includes a deadline / expiry timestamp
- Domain separator is included in the hash to prevent cross-type collisions
- Signature verification uses the correct signer set for the current validator epoch, not a stale or rotated-out set
- Validator set rotation is handled atomically — no window where old and new sets overlap without explicit transition logic
Finality and reorg risk
- Minimum confirmation depth is documented per source chain
- Actions with irreversible consequences (mints, native asset releases) wait for finality, not just inclusion
- The bridge has an explicit policy for what happens to in-flight messages during a source-chain reorg
Liquidity and economic security
- Liquidity is sized relative to the value at risk — an under-collateralized bridge pool is a systemic risk
- Large individual transfers are rate-limited or require additional delay to limit single-event exposure
- The bridge’s incentive model for validators or relayers does not create perverse incentives to delay or censor messages