Typescript SDK

A guide on using the Snowbridge TypeScript SDK for integration.

Packages

The following packages are used in the Snowbridge SDK:

  • @snowbridge/api This is the main entry point for developers integrating with Snowbridge. It provides all core interfaces and helper functions to initiate, validate, and send cross-chain transactions. It abstracts over the complexities of constructing and handling XCM messages, Ethereum transactions, and relayer coordination.

    • Use the toPolkadotV2 module for sending packages from Ethereum -> Polkadot.

    • Use the toEthereumV2 module for sending packages from Polkadot -> Ethereum.

  • @snowbridge/registry This package contains the asset and parachain registry used by Snowbridge. It defines the list of supported tokens, parachains, and associated metadata (like contract addresses and decimals). It ensures your transfers use valid combinations of assets and destinations.

  • @snowbridge/contract-types Contains TypeScript typings and contract ABIs for the Ethereum contracts Snowbridge interacts with. Use this package to interact with contracts directly, or to extend SDK functionality.

  • @snowbridge/contracts Provides deployed contract addresses and metadata for Snowbridge smart contracts on supported networks. This is useful when you need to interact with Snowbridge's Ethereum-side contracts directly.

  • @snowbridge/base-types Defines common data types used throughout the SDK, such as asset representations, transfer objects, parachain locations, and more. These types are shared between @snowbridge/api and the registry.

Examples

The following examples show how to do an Ether transfer from Ethereum to Polkadot, and back. The full example code can be viewed by following the stated links.

Ethereum to Polkadot

Full example: send_ether_from_eth_to_assethub.ts

Uses the @snowbridge/api toPolkadotV2 package to send the transaction.

Setup

This step prepares all required state and dependencies for the transfer operation. This includes loading the asset registry, initializing the context, which sets up the connections to Ethereum and Substrate-based networks and loading the user wallets for both Ethereum and Substrate chains.

// Initialize polkadot-js 
crypto await cryptoWaitReady()
// Get the registry of parachains and assets.
const environment = "polkadot_mainnet"
const registry = assetRegistryFor(environment)
// Initialize the context which establishes and pool connections
const context = new Context(contextConfigFor(environment))

// Initialize ethereum wallet.
const ETHEREUM_ACCOUNT = new Wallet(
    process.env.ETHEREUM_KEY ?? "Your Key Goes Here",
    context.ethereum()
)
const ETHEREUM_ACCOUNT_PUBLIC = await ETHEREUM_ACCOUNT.getAddress()

// Initialize substrate wallet.
const polkadot_keyring = new Keyring({ type: "sr25519" })
const POLKADOT_ACCOUNT = polkadot_keyring.addFromUri(
    process.env.SUBSTRATE_KEY ?? "Your Key Goes Here"
)
const POLKADOT_ACCOUNT_PUBLIC = POLKADOT_ACCOUNT.address

Step 1: Get Delivery Fee

Use getDeliveryFee() to calculate how much the user must pay in order to deliver the message across chains. This includes relayer fees and any protocol-specific gas or weight costs. Displaying this to the user upfront ensures clarity and reduces failed transactions due to underpayment.

// Select the token you want to send. In this case we use Ether. The registry 
// contains the list of tokens.
const TOKEN_CONTRACT = assetsV2.ETHER_TOKEN_ADDRESS
// Select the destination parachain. In this case it is Asset Hub.
const SOURCE_PARACHAIN = 1000
const fee = await toPolkadotV2.getDeliveryFee(
    context, // The context
    registry, // Asset registry
    TOKEN_CONTRACT, // The erc20 token contract address
    DESTINATION_PARACHAIN // Destination parachain
)

Step 2: Create Transfer

The createTransfer() function generates a transfer object, which includes source and destination accounts, the amount to send, the token being transferred and the precomputed delivery fee. This object contains all data necessary to execute the cross-chain transfer and is later used for validation and signing.

const amount = 15_000_000_000_000n // 0.000015 ETH
const transfer = await toPolkadotV2.createTransfer(
    registry, // Asset registry
    ETHEREUM_ACCOUNT_PUBLIC, // Source account
    POLKADOT_ACCOUNT_PUBLIC, // Destination account
    TOKEN_CONTRACT, // The erc20 token contract address
    DESTINATION_PARACHAIN, // Destination parachain
    amount, // Transfer Amount
    fee // The delivery fee
)

Step 3: Validate Transfer

Although optional, validateTransfer() is strongly recommended. It performs local checks and dry-runs the transaction (when possible) to ensure:

  • The sender has enough funds

  • The asset is supported

  • The constructed transaction will succeed on-chain

This step can save users from wasting gas or fees on transactions that would otherwise revert.

const validation = await toPolkadotV2.validateTransfer(
    context, // The context
    transfer // The transfer tx
)

if (!validation.success) {
    console.error(validation.logs)
    throw Error(`validation has one of more errors.`)
}

Step 4: Send Transaction

Finally, the transaction is signed and submitted to the source chain. Use the Wallet instance to send the transaction.

const response = await ETHEREUM_ACCOUNT.sendTransaction(transfer.tx)
const receipt = await response.wait(1)
if (!receipt) {
    throw Error(`Transaction ${response.hash} not included.`)
}

Polkadot to Ethereum

Full example: send_ether_from_assethub_to_eth.ts

Uses the @snowbridge/api toEthereumV2 module to send the transaction.

Setup

This step prepares all required state and dependencies for the transfer operation. This includes loading the asset registry, initializing the context, which sets up the connections to Ethereum and Substrate-based networks and loading the user wallets for both Ethereum and Substrate chains.

// Initialize polkadot-js crypto
await cryptoWaitReady()
// Get the registry of parachains and assets.
const environment = "polkadot_mainnet"
const registry = assetRegistryFor(environment)

// Initialize the context which establishes and pool connections
const context = new Context(contextConfigFor(environment))
// Initialize ethereum wallet.
const ETHEREUM_ACCOUNT = new Wallet(
    process.env.ETHEREUM_KEY ?? "Your Key Goes Here",
    context.ethereum()
)
const ETHEREUM_ACCOUNT_PUBLIC = await ETHEREUM_ACCOUNT.getAddress()

// Initialize substrate wallet.
const polkadot_keyring = new Keyring({ type: "sr25519" })
const POLKADOT_ACCOUNT = polkadot_keyring.addFromUri(
    process.env.SUBSTRATE_KEY ?? "Your Key Goes Here"
)
const POLKADOT_ACCOUNT_PUBLIC = POLKADOT_ACCOUNT.address

// Select the token you want to send. In this case we use Ether. The registry contains the list of tokens.
const TOKEN_CONTRACT = assetsV2.ETHER_TOKEN_ADDRESS
// Select the destination parachain. In this case it is Asset Hub.
const SOURCE_PARACHAIN = 1000

Step 1: Get Delivery Fee

Use getDeliveryFee() to calculate how much the user must pay in order to deliver the message across chains. This includes relayer fees and any protocol-specific gas or weight costs. Displaying this to the user upfront ensures clarity and reduces failed transactions due to underpayment.

const fee = await toEthereumV2.getDeliveryFee(
    context, // The context
    SOURCE_PARACHAIN, // Source parachain Id
    registry, // The asset registry
    TOKEN_CONTRACT // The token being transferred
)

Step 2: Create Transfer

The createTransfer() function generates a transfer object, which includes source and destination accounts, the amount to send, the token being transferred and the precomputed delivery fee. This object contains all data necessary to execute the cross-chain transfer and is later used for validation and signing.

const amount = 15_000_000_000_000n // 0.000015 ETH
const transfer = await toEthereumV2.createTransfer(
    { sourceParaId: SOURCE_PARACHAIN, context }, // The context and source parachain
    registry, // The asset registry
    POLKADOT_ACCOUNT_PUBLIC, // The source account
    ETHEREUM_ACCOUNT_PUBLIC, // The destination account
    TOKEN_CONTRACT, // The transfer token
    amount, // The transfer amount
    fee // The fee
)

Step 3: Validate Transfer

Although optional, validateTransfer() is strongly recommended. It performs local checks and dry-runs the transaction (when possible) to ensure:

  • The sender has enough funds

  • The asset is supported

  • The constructed transaction will succeed on-chain

This step can save users from wasting gas or fees on transactions that would otherwise revert.

const validation = await toEthereumV2.validateTransfer(
    context, // The context
    transfer
)

if (!validation.success) {
    console.error(validation.logs)
    throw Error(`validation has one of more errors.`)
}

Step 4: Send Transaction

Finally, the transaction is signed and submitted to the source chain. Use the SDK helper signAndSend() which manages construction, signing, and submission of the extrinsic.

const response = await toEthereumV2.signAndSend(
    context, // The context
    transfer,
    POLKADOT_ACCOUNT,
    { withSignedTransaction: true }
)
if (!response) {
    throw Error(`Transaction ${response} not included.`)
}
if (!response.messageId) {
    throw Error(
        `Transaction ${response} did not have a message id. Did your transaction revert?`
    )
}

Last updated