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
);
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
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
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.