Skip to main content

Mass exit operations

This section explains how to trigger the mass exit process and enable users to withdraw their funds using snapshot proofs.

When mass exit is triggered, standard deposits and withdrawals are immediately blocked, and the bridge enters mass exit mode. Users must then obtain and submit proofs of their account state from the last known valid block.

Step 1: Triggering mass exit

The bridge owner can trigger the mass exit.

Basic form (for trusted validator set):

function triggerMassExit(
bytes memory blockHeader, // The block header from a recent valid block
bytes[] memory sigs, // Validator signatures on the block header
address[] memory signers, // Public keys
Data.ExtraProofData memory extraProof // Additional proof data
)

If the validator contract has been hijacked:

function triggerMassExitWithHistoricalValidators(
bytes memory blockHeader, // Block header from a recent valid block
bytes[] memory sigs, // Validator signatures from the recent block header
address[] memory signers, // Public keys
Data.ExtraProofData memory extraProof, // Additional proof data
address[] memory historicalValidators // Historical validator addresses to use for verification
)
  • Ensure that the blockHeader corresponds to a valid block no older than 3 days.
  • The signatures must align with the validator set at the time of that block.

✅ Once triggered, deposits and standard withdrawals are disabled.
✅ Snapshot-based withdrawal becomes the only supported method.

Step 2: Withdraw via snapshot proof

Retrieve state slot IDs

val state_slot_ids = hbridge.get_state_slot_ids_for_address(
beneficiary, // EVM address of the user
network_id // EVM network ID
);

Obtain account state proof

# Use the Chromia CLI to query the account state Merkle proof.
# Replace $MASS_EXIT_BLOCK_HEIGHT with the block height at mass exit,
# and $ACCOUNT_STATE_SLOT_ID with the specific account state slot ID.
chr query -brid $BRIDGE get_account_state_merkle_proof \
-- blockHeight=$MASS_EXIT_BLOCK_HEIGHT \
accountNumber=$ACCOUNT_STATE_SLOT_ID

Submit proof on EVM

Submit the snapshot proof on the EVM to withdraw funds.

function withdrawBySnapshot(
bytes calldata snapshot, // Snapshot data of the account state
Data.Proof memory stateProof // Merkle proof for the snapshot
)

This function allows users to withdraw their balance from the bridge using the snapshot state.

Step 3: Complete in-progress withdrawals

If a user initiated a withdrawal before the mass exit, they can complete it by following these steps:

Get withdrawal by transaction + index

Retrieve the specific withdrawal details using the transaction RID and operation index.

val withdrawal = hbridge.get_erc20_withdrawal_by_tx(
tx_rid, // Rell transaction RID
op_index // Operation index within that transaction
);

Get event proof

Obtain the Merkle proof for the withdrawal event.

# Replace $WITHDRAWAL_EVENT_HASH with the hash of the withdrawal event.
chr query -brid $BRIDGE get_event_merkle_proof \
-- eventHash=$WITHDRAWAL_EVENT_HASH

Retrieve withdrawal state slot IDs

Retrieve the state slot IDs associated with the beneficiary's withdrawals.

val slot_ids = hbridge.get_withdrawal_state_slot_ids_for_address(
beneficiary, // The beneficiary's address
network_id // The EVM network ID
);

Obtain account state proof again

Query the Chromia CLI for the account state Merkle proof using the block height at mass exit.

chr query -brid $BRIDGE get_account_state_merkle_proof \
-- blockHeight=$MASS_EXIT_BLOCK_HEIGHT \
accountNumber=$ACCOUNT_STATE_SLOT_ID

Complete the withdrawal

function completeWithdrawalBySnapshot(
bytes calldata _stateRecord, // Account state data; contains header and a list of withdrawal event hashes
uint64 n, // Index of the withdrawal hash
bytes memory _event, // Withdrawal event
Data.Proof memory stateProof // Snapshot proof
)

Step 4: Emergency withdraw period

After triggering a mass exit, users have 90 days to submit snapshot proofs for their balances. If a balance doesn't have an associated account state snapshot—whether due to user inaction or misconfiguration on the Rell side—the snapshot-based withdrawal will fail. Following the 90-day period, the bridge owner can reclaim and withdraw those funds.

Step 5: Pausing the bridge

Any validator can temporarily pause the bridge if they detect unusual behavior:

  • This action will freeze deposits and withdrawals.
  • The pause allows time for investigation before a mass exit is initiated.

The bridge owner can later unpause it if the anomaly turns out to be a false positive.