Skip to main content

Set up mass exit

This guide helps you enable the mass exit mechanism by configuring your bridge contracts and Chromia chains for snapshot-based withdrawals. Remember that you must prepare for mass exit in advance; you cannot retrofit it after deployment. If your bridge isn't set to snapshot mode, you won't be able to trigger a mass exit.

Prerequisites

Before you start, make sure that:

  • You have an EVM-compatible bridge set up or in progress.
  • You are using a bridge contract that supports snapshot-based withdrawals, such as TokenBridgeWithSnapshotWithdraw.
  • Your Chromia Bridge Chain registers tokens in foreign mode with use_snapshots: true.

Token modes and mass exit implications

Foreign mode (EVM-originated tokens):

  • The token originates on EVM.
  • FT4 tokens are minted when moving from EVM → FT4.
  • FT4 tokens are burned when moving from FT4 → EVM.
  • Snapshot-based mass exit is supported and required to ensure asset recovery in case of bridge failure.
  • Simpler and preferred mode for enabling mass exits.

Native mode (FT4-originated tokens):

  • The token originates on FT4.
  • Tokens are locked in a special blockchain account when moving FT4 → EVM.
  • Tokens are released from that account on EVM → FT4.
  • This ensures total supply is protected against faulty EVM contracts.
  • Mass exit is not supported in this mode. If Chromia is compromised, the demand to exit native assets to EVM is expected to be low.

Step 1: Deploy snapshot-ready EVM bridge

To deploy the EVM bridge contract with snapshot support, run the following command:

npx hardhat deploy:snapshots \
--network sepolia \
--verify \
--validator-address <VALIDATOR_CONTRACT_ADDRESS> \
--offset 2
  • Replace <VALIDATOR_CONTRACT_ADDRESS> with the address of your validator or directory validator contract.
  • The --offset parameter sets the dispute period (lockup interval) in blocks, and a 72-hour equivalent is recommended.

Step 2: Register FT4 asset on the Bridge Chain

In your Rell deployment on the Chromia Bridge Chain, register the asset using the following code:

val asset = ft4.assets.Unsafe.register_asset(
name,
symbol,
decimals,
blockchain_rid,
icon_url,
type // defaults to ASSET_TYPE_FT4
);

Step 3: Register ERC-20 token with snapshot support

Next, register your ERC-20 token on the Bridge Chain with this command:

val erc20_asset = hbridge.register_erc20_asset(
network_id, // EVM network ID (e.g., 97 for BSC testnet)
token_address, // Deployed ERC-20 contract address
asset, // FT4 asset returned from the previous step
bridge_mode.foreign, // Must be set to 'foreign' mode for snapshot support
true // set use_snapshots = true
);
note

Setting use_snapshots = true is essential for enabling mass exit support.

Step 4: Create a bridge contract

While still in the initialization phase, create a bridge contract:

val bridge_contract = hbridge.get_or_create_bridge(
network_id, // The EVM network ID
bridge_address // The address where the bridge contract is (or will be) deployed
);

Step 5: Bind ERC-20 to the bridge contract

While still in the initialization phase, bind the ERC-20 asset to the bridge contract using this code:

create bridge_erc20_asset(
bridge_contract, // The Bridge contract
erc20_asset // The ERC-20 token to bind to the bridge contract
);

Step 6: Cross-chain mass exit (recovery contract)

If tokens settle on a downstream chain, deploy a RecoveryContract:

npx hardhat deploy:recovery --network sepolia --verify \
--validator-address <VALIDATOR_CONTRACT_ADDRESS> // The address of the managed validator contract used by the bridge contract
note

To obtain the validator contract address, execute the inspect:bridge script:

$ npx hardhat inspect:bridge --network sepolia --bridge-address {BRIDGE_CONTRACT_ADDRESS}

Set the RID for the downstream chain

npx hardhat setBlockchainRid:recovery \
--network sepolia \
--address <RECOVERY_CONTRACT_ADDRESS> \
--blockchain-rid <DOWNSTREAM_CHAIN_RID>

Register the cross-chain FT4 asset on the downstream chain

val asset = crosschain.Unsafe.register_crosschain_asset(
id, // The ID of the asset to be registered
name, // The name of the asset to be registered
symbol, // The symbol of the asset to be registered
decimals, // The decimals of the asset to be registered
issuing_blockchain_rid, // The blockchain RID of the issuing chain
icon_url, // The URL of the icon for the asset
type, // The type of the asset to be registered (see FT4 docs)
uniqueness_resolver, // The uniqueness resolver for the asset (see FT4 docs)
origin_blockchain_rid // The blockchain we'll receive this asset from
// (might not be the same as issuing_blockchain_rid)
);

Register the ERC-20 token with snapshot support

Lastly, register your ERC-20 token on the downstream chain:

val erc20_asset = hbridge.register_erc20_asset(
network_id, // EVM network ID (e.g., 97 for BSC testnet)
token_address, // Deployed ERC-20 contract address
asset, // FT4 asset returned from previous step
note

Setting use_snapshots = true is essential for enabling mass exit support.

Create a bridge contract

During the initialization phase, create a bridge contract:

val bridge_contract = hbridge.get_or_create_bridge(
network_id, // The EVM network ID
bridge_address // The address of the bridge contract
);

Bind ERC-20 to the bridge contract

While still in the initialization phase, bind the ERC-20 asset to the bridge contract using the following code:

create bridge_erc20_asset(
bridge_contract, // The bridge contract
erc20_asset // The ERC-20 token to bind to the bridge contract
);

Register recovery contract

On the downstream chain, register the recovery contract using this command:

chr tx -brid $DOWNSTREAM_CHAIN_RID register_recovery_contract \
-- $NETWORK_ID x"'$RECOVERY_CONTRACT_ADDRESS'"

Next, register it on the upstream chain via ICCF:

chr tx -brid $BRIDGE --iccf-tx $ICCF_TX_RID \
--iccf-source $DOWNSTREAM_CHAIN_RID \
register_recovery_contract -- 0

Once registered, the recovery contract can receive funds, and users can withdraw based on their snapshots on the downstream chain.

Step 7: Verify GTX modules

Ensure your Bridge and Event Receiver chains include the correct modules in chromia.yml:

Bridge Chain

gtx:
modules:
- "net.postchain.eif.EifGTXModule"
- "net.postchain.d1.icmf.IcmfReceiverGTXModule"
sync_ext:
- "net.postchain.d1.icmf.IcmfReceiverSynchronizationInfrastructureExtension"

Event Receiver Chain

gtx:
modules:
- "net.postchain.eif.EifGTXModule"
- "net.postchain.d1.icmf.IcmfSenderGTXModule"
sync_ext:
- "net.postchain.eif.EifSynchronizationInfrastructureExtension"

With these steps completed, you are now ready to handle mass exit operations if a critical validator compromise occurs.