Skip to main content

Overview

This guide will walk you through building a complete cross-chain bridge application using the Avail Nexus SDK. You’ll learn how to:
  • Initialize the SDK with a wallet provider
  • Set up user approval hooks
  • Fetch cross-chain balances
  • Execute a bridge transaction
  • Track transaction progress
1

Import the SDK

First, import the necessary components from the SDK:
import { NexusSDK, NEXUS_EVENTS } from '@avail-project/nexus-core';
import type { BridgeParams } from '@avail-project/nexus-core';
These imports give you:
  • NexusSDK - The main SDK class
  • NEXUS_EVENTS - Event constants for tracking transaction progress
  • BridgeParams - TypeScript type for bridge parameters
2

Initialize the SDK

Create and initialize an SDK instance with a wallet provider:
// Create SDK instance (testnet for development)
const sdk = new NexusSDK({ network: 'testnet' });

// Initialize with MetaMask or any EIP-1193 provider
await sdk.initialize(window.ethereum);
For Node.js environments or custom wallet implementations:
import { ethers } from 'ethers';

// Example: Using ethers.js wallet
const wallet = new ethers.Wallet(privateKey, provider);
const sdk = new NexusSDK({ network: 'mainnet' });
await sdk.initialize(wallet);
The SDK supports both 'mainnet' and 'testnet' networks. Always start with 'testnet' during development.
3

Set Up Approval Hooks

Configure hooks to handle user approvals. These hooks are called when the SDK needs user permission:
// Intent approval hook - called when user needs to approve bridge details
sdk.setOnIntentHook(({ intent, allow, deny }) => {
  // Display intent details to user
  console.log('Bridge amount:', intent.destination.amount);
  console.log('Destination chain:', intent.destination.chainName);
  console.log('Total fees:', intent.fees.total);
  console.log('Source chains:', intent.sources);
  
  // User approves the transaction
  allow();
  
  // Or user denies (will throw USER_DENIED_INTENT error)
  // deny();
});

// Allowance approval hook - called when token approval is needed
sdk.setOnAllowanceHook(({ sources, allow, deny }) => {
  // Show which tokens need approval
  sources.forEach(source => {
    console.log(`Approve ${source.token.symbol} on ${source.chain.name}`);
    console.log(`Current: ${source.allowance.current}`);
    console.log(`Required: ${source.allowance.minimum}`);
  });
  
  // Approve minimum required (recommended)
  allow(['min']);
  
  // Or approve unlimited
  // allow(['max']);
});
Using 'min' for allowances is more secure as it only approves the exact amount needed for the transaction.
4

Fetch User Balances

Retrieve the user’s token balances across all supported chains:
const balances = await sdk.getBalancesForBridge();

// Display balances
balances.forEach(asset => {
  console.log(`${asset.symbol}: ${asset.balance} ($${asset.balanceInFiat})`);
  
  // Show per-chain breakdown
  asset.breakdown.forEach(chainBalance => {
    console.log(`  ${chainBalance.chain.name}: ${chainBalance.balance}`);
  });
});
Example output:
USDC: 1,234.56 ($1234.56)
  Ethereum: 500.00
  Polygon: 734.56
ETH: 2.5 ($8,640.00)
  Ethereum: 1.0
  Base: 1.5
5

Execute a Bridge Transaction

Now execute a bridge transaction with real-time progress tracking:
const bridgeParams: BridgeParams = {
  token: 'USDC',
  amount: 100_000_000n, // 100 USDC (6 decimals)
  toChainId: 137, // Polygon
  // Optional: specify source chains
  // sourceChains: [1, 8453], // Ethereum and Base
  // Optional: provide gas on destination
  // gas: 100000n,
};

const result = await sdk.bridge(bridgeParams, {
  onEvent: (event) => {
    // Track all steps
    if (event.name === NEXUS_EVENTS.STEPS_LIST) {
      console.log('Bridge steps:', event.args);
    }
    
    // Track each completed step
    if (event.name === NEXUS_EVENTS.STEP_COMPLETE) {
      const step = event.args;
      console.log(`✓ ${step.type}`);
      
      // Show transaction links
      if (step.data?.explorerURL) {
        console.log(`  View: ${step.data.explorerURL}`);
      }
    }
  },
});

console.log('Bridge complete!');
console.log('Transaction:', result.explorerUrl);
console.log('Source transactions:', result.sourceTxs);
Important: Token amounts must be provided in the token’s smallest unit (wei for ETH, 6 decimals for USDC).Examples:
  • 1 ETH = 1_000_000_000_000_000_000n (18 decimals)
  • 100 USDC = 100_000_000n (6 decimals)
  • 50 USDT = 50_000_000n (6 decimals)

Complete Example

Here’s a full working example you can copy and adapt:
import { NexusSDK, NEXUS_EVENTS } from '@avail-project/nexus-core';
import type { BridgeParams } from '@avail-project/nexus-core';

async function main() {
  // 1. Initialize SDK
  const sdk = new NexusSDK({ network: 'testnet' });
  await sdk.initialize(window.ethereum);
  
  // 2. Set up hooks
  sdk.setOnIntentHook(({ intent, allow, deny }) => {
    const confirmed = confirm(
      `Bridge ${intent.destination.amount} to ${intent.destination.chainName}?\n` +
      `Fees: ${intent.fees.total}`
    );
    confirmed ? allow() : deny();
  });
  
  sdk.setOnAllowanceHook(({ sources, allow, deny }) => {
    const confirmed = confirm(
      `Approve ${sources.length} token(s)?`
    );
    confirmed ? allow(['min']) : deny();
  });
  
  // 3. Fetch balances
  const balances = await sdk.getBalancesForBridge();
  console.log('Your balances:', balances);
  
  // 4. Execute bridge
  const params: BridgeParams = {
    token: 'USDC',
    amount: 10_000_000n, // 10 USDC
    toChainId: 84532, // Base Sepolia
  };
  
  const result = await sdk.bridge(params, {
    onEvent: (event) => {
      if (event.name === NEXUS_EVENTS.STEP_COMPLETE) {
        console.log('Step completed:', event.args.type);
      }
    },
  });
  
  console.log('Success!', result.explorerUrl);
}

main().catch(console.error);

Understanding Bridge Steps

During a bridge transaction, you’ll see these key steps:
StepDescription
INTENT_ACCEPTEDIntent created and accepted by solver
INTENT_HASH_SIGNEDUser signed the intent hash
ALLOWANCE_USER_APPROVALWaiting for token approval
ALLOWANCE_APPROVAL_MINEDApproval transaction mined
INTENT_DEPOSITDeposit initiated on source chain
INTENT_DEPOSITS_CONFIRMEDAll deposits confirmed
INTENT_FULFILLEDIntent fulfilled on destination
TRANSACTION_CONFIRMEDFinal transaction confirmed
Each step includes an explorerURL in its data, allowing you to provide users with real-time transaction links.

Error Handling

Always wrap SDK calls in try-catch blocks:
import { NexusError, ERROR_CODES } from '@avail-project/nexus-core';

try {
  const result = await sdk.bridge(params);
  console.log('Success:', result.explorerUrl);
} catch (error) {
  if (error instanceof NexusError) {
    switch (error.code) {
      case ERROR_CODES.USER_DENIED_INTENT:
        console.log('User cancelled transaction');
        break;
      case ERROR_CODES.INSUFFICIENT_BALANCE:
        console.error('Insufficient balance for bridge');
        break;
      case ERROR_CODES.TRANSACTION_TIMEOUT:
        console.error('Transaction timed out, check explorer');
        break;
      default:
        console.error('Bridge failed:', error.message);
    }
  } else {
    console.error('Unexpected error:', error);
  }
}

Simulating Transactions

Before executing a bridge, you can simulate it to preview fees and requirements:
const simulation = await sdk.simulateBridge({
  token: 'USDC',
  amount: 100_000_000n,
  toChainId: 137,
});

console.log('Estimated fees:', simulation.intent.fees.total);
console.log('Source chains:', simulation.intent.sources);
console.log('Destination amount:', simulation.intent.destination.amount);

// Show user the simulation before confirming
if (confirm('Proceed with bridge?')) {
  await sdk.bridge(params);
}

Next Steps

Bridge Operations

Explore advanced bridging features and options

Swap Operations

Learn how to perform cross-chain swaps

Balance Management

Work with multi-chain balance aggregation

Event Handling

Build real-time progress UIs with events

Testing on TestnetAlways test your integration on testnet first:
  1. Use network: 'testnet' in SDK configuration
  2. Get testnet tokens from faucets (Sepolia, Base Sepolia, etc.)
  3. Verify your integration works end-to-end
  4. Switch to network: 'mainnet' only when ready for production
Testnet chain IDs are different from mainnet - check the supported networks documentation.

Build docs developers (and LLMs) love