!! Note: We are in process of simplifying our API. The current snippets below will be simplified for better DevEx soon.
The first step is to get the Snowbridge asset registry. It is a list of assets that may be transferred between chains. The list is a static file hosted in @snowbridge/registry.
1import { assetRegistryFor } from "@snowbridge/registry";
2
3// Configuration - Use 'polkadot_mainnet' for production, 'paseo_sepolia' for
4// testnet
5const environmentName = "polkadot_mainnet";
6
7// Get the asset registry for the environment
8const registry = assetRegistryFor(environmentName);
Set the context, containing connections to all the required chains.
1const snowbridgeEnv = environment.SNOWBRIDGE_ENV[environmentName];
2
3if (!snowbridgeEnv) {
4 throw new Error(`Unknown environment '${environmentName}'`);
5}
6
7// Build the context from registry options
8const context = new Context({
9 environment: environmentName,
10 ethereum: {
11 ethChainId: snowbridgeEnv.ethChainId,
12 ethChains: Object.fromEntries(
13 Object.keys(snowbridgeEnv.config.ETHEREUM_CHAINS).map(chainId => [
14 chainId,
15 snowbridgeEnv.config.ETHEREUM_CHAINS[chainId](ethApiKey)
16 ])
17 ),
18 beacon_url: snowbridgeEnv.config.BEACON_HTTP_API,
19 },
20 polkadot: {
21 assetHubParaId: registryOptions.assetHubParaId,
22 bridgeHubParaId: registryOptions.bridgeHubParaId,
23 relaychain: registryOptions.relaychain,
24 parachains: snowbridgeEnv.config.PARACHAINS,
25 },
26 appContracts: {
27 gateway: registryOptions.gatewayAddress,
28 beefy: snowbridgeEnv.config.BEEFY_CONTRACT,
29 },
30});
Before sending a transaction, calculate the delivery fee.
1const deliveryFee = await toPolkadotV2.getDeliveryFee(
2 {
3 gateway: context.gateway(),
4 assetHub: await context.assetHub(),
5 destination: await context.parachain(destinationParaId),
6 },
7 registry,
8 tokenAddress,
9 destinationParaId
10);
Create the transfer object.
1const transfer = await toPolkadotV2.createTransfer(
2 registry,
3 ethereumAddress,
4 polkadotAddress,
5 tokenAddress,
6 destinationParaId,
7 transferAmount,
8 deliveryFee
9);
Validate the transfer. This dry runs the transaction on all relevant chains to ensure the transaction will succeed. This step is crucial to ensure no funds are lost when the transaction is done.
1const validation = await toPolkadotV2.validateTransfer(
2 {
3 ethereum: context.ethereum(),
4 gateway: context.gateway(),
5 bridgeHub: await context.bridgeHub(),
6 assetHub: await context.assetHub(),
7 destParachain: destinationParaId !== 1000
8 ? await context.parachain(destinationParaId)
9 : undefined,
10 },
11 transfer
12);
13
14// Check for validation errors
15const errors = validation.logs.filter(log =>
16 log.kind === toPolkadotV2.ValidationKind.Error
17);
18
19if (errors.length > 0) {
20 console.error("❌ Validation errors:", errors);
21 throw new Error("Transfer validation failed");
22}
Calculate the gas that should be sent with the transaction.
1 const { tx, computed: { totalValue } } = transfer;
2 const estimatedGas = await context.ethereum().estimateGas(tx);
3 const feeData = await context.ethereum().getFeeData();
4 const executionFee = (feeData.gasPrice ?? 0n) * estimatedGas;
1const response = await ETHEREUM_ACCOUNT.sendTransaction(tx)
2const receipt = await response.wait(1)
3if (!receipt) {
4 throw Error(`Transaction ${response.hash} not included.`)
5}
6
7const message = await toPolkadotV2.getMessageReceipt(receipt)
8if (!message) {
9 throw Error(`Transaction ${receipt.hash} did not emit a message.`)
10}
11console.log(
12 `Success message with message id: ${message.messageId}
13 block number: ${message.blockNumber}
14 tx hash: ${message.txHash}`
15)
Parachains who would like to integrate with Snowbridge need to meet certain requirements to integrate. Teams are encouraged to reach out on Telegram for additional support from our team, since each parachain is different and might require specific changes. Please log an issue on Snowfork/snowbridge to get in touch.
For the first version of Snowbridge, parachains need to ensure that:
transfer_assets_using_type_and_then
Snowbridge V2 is set to go live in August 2025. It relies heavily on pallet-xcm::execute. Documentation will be added closer to the launch of V2.