πŸš€ DAY 5 OF LAUNCH WEEK:Introducing Webhook Events for Alert Changes.Learn more β†’
Socket
Book a DemoInstallSign in
Socket

@circle-fin/bridge-kit

Package Overview
Dependencies
Maintainers
3
Versions
4
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@circle-fin/bridge-kit

SDK for seamless cross-chain stablecoin bridging

latest
npmnpm
Version
1.1.2
Version published
Maintainers
3
Created
Source

Bridge Kit

npm version TypeScript License Discord

A strongly-typed SDK for seamless cross-chain stablecoin bridging

Making cross-chain stablecoin (USDC, and soon more tokens) transfers as simple as a single function call

Table of Contents

Overview

The Stablecoin Kit ecosystem is Circle’s open-source effort to streamline stablecoin development with SDKs that are easy to use correctly and hard to misuse. Kits are cross-framework (viem, ethers, @solana/web3) and integrate cleanly into any stack. They’re opinionated with sensible defaults, but offer escape hatches for full control. A pluggable architecture makes implementation flexible, and all kits are interoperable, so they can be composed to suit a wide range of use cases.

The Bridge Kit enables cross-chain stablecoin transfers via a type-safe, developer-friendly interface with robust runtime validation. The Kit can have any bridging provider plugged in, by implementing your own BridgingProvider, but comes by default with full CCTPv2 support

Why Bridge Kit?

  • πŸŒ‰ Bridge-first design: All abstractions revolve around source ↔ destination chain pairs
  • ⚑ Zero-config defaults: Built-in reliable RPC endpoints - start building right away
  • πŸ”§ Bring your own infrastructure: Seamlessly integrate with your existing setup when needed
  • πŸ”’ Production-ready security: Leverages Circle's CCTPv2 with deterministic quotes and finality tracking
  • πŸš€ Developer experience: Complete TypeScript support, comprehensive validation, and instant connectivity
  • 🌍 Cross-chain bridging: The Bridge Kit supports 34 chains with 544 total bridge routes through Circle's CCTPv2
    • Mainnet (17 chains): Arbitrum, Avalanche, Base, Codex, Ethereum, HyperEVM, Ink, Linea, OP Mainnet, Plume, Polygon PoS, Sei, Solana, Sonic, Unichain, World Chain, XDC
    • Testnet (17 chains): Arbitrum Sepolia, Avalanche Fuji, Base Sepolia, Codex Testnet, Ethereum Sepolia, HyperEVM Testnet, Ink Testnet, Linea Sepolia, OP Sepolia, Plume Testnet, Polygon PoS Amoy, Sei Testnet, Solana Devnet, Sonic Testnet, Unichain Sepolia, World Chain Sepolia, XDC Apothem
  • 🎯 Flexible adapters: Supporting EVM (Viem, Ethers) and Solana (@solana/web3)
  • βš™οΈ Configurable bridge speeds: FAST/SLOW options with fee optimization
  • πŸ“‘ Real-time event monitoring: Track progress throughout the transfer lifecycle
  • πŸ›‘οΈ Robust error handling: Graceful partial success recovery
  • ✈️ Pre-flight validation: Verify transfers with cost estimation before execution

Architecture Flow

The Bridge Kit follows a three-layer architecture designed for flexibility and type safety:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   Bridge Kit    │────│     Provider     │────│   Adapter       β”‚
β”‚  (Orchestrator) β”‚    β”‚   (Protocol)     β”‚    β”‚ (Blockchain)    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
  • Adapter: Handles blockchain-specific operations (wallets, transactions, gas) and enables you to use whatever framework you're comfortable with (viem, ethers, @solana/web3, and more coming soon)
  • Provider: Implements bridging protocols (currently CCTPv2)
  • BridgeKit: Orchestrates adapters and providers with auto-routing and validation

This separation ensures that each component has a single responsibility while maintaining seamless integration across the entire cross-chain bridging lifecycle.

Installation

npm install @circle-fin/bridge-kit
# or
yarn add @circle-fin/bridge-kit

Adapters

Choose the appropriate adapter for your target chains:

# For EVM chains (Ethereum, Base, Arbitrum, etc.)
npm install @circle-fin/adapter-viem-v2 viem
# or
yarn add @circle-fin/adapter-viem-v2 viem

# For EVM chains using Ethers.js
npm install @circle-fin/adapter-ethers-v6
# or
yarn add @circle-fin/adapter-ethers-v6

# For Solana
npm install @circle-fin/adapter-solana @solana/web3.js @solana/spl-token
# or
yarn add @circle-fin/adapter-solana @solana/web3.js @solana/spl-token

Quick Start

πŸš€ Easiest Setup: Single Adapter, Multiple Chains

Best for: Getting started quickly, simple transfers using one wallet across chains

The factory methods make it incredibly easy to get started with built-in reliable RPC endpoints. No need to research providers or configure endpoints - just start building! Create one adapter and use it across different chains!

import { BridgeKit } from '@circle-fin/bridge-kit'
import { createAdapterFromPrivateKey } from '@circle-fin/adapter-viem-v2'

// Initialize the kit
const kit = new BridgeKit()

// Create ONE adapter that works across all chains!
const adapter = createAdapterFromPrivateKey({
  privateKey: process.env.PRIVATE_KEY as string,
})

const result = await kit.bridge({
  from: { adapter, chain: 'Ethereum' },
  to: { adapter, chain: 'Base' },
  amount: '10.50',
})

✨ Key Feature: All supported chains include reliable default RPC endpoints.

🎯 Send to Different Address

Best for: Sending funds to someone else's wallet, custodial services

Use BridgeDestinationWithAddress when the recipient is different from your adapter's address:

// Send to a different address on the destination chain
const result = await kit.bridge({
  from: { adapter, chain: 'Ethereum' },
  to: {
    adapter,
    chain: 'Base',
    recipientAddress: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e',
  },
  amount: '10.50',
})

// Or use a different adapter for the destination chain
const baseAdapter = createAdapterFromPrivateKey({
  privateKey: process.env.PRIVATE_KEY as string,
})

const resultWithDifferentAdapter = await kit.bridge({
  from: { adapter, chain: 'Ethereum' },
  to: {
    adapter: baseAdapter,
    chain: 'Base',
    recipientAddress: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e',
  },
  amount: '10.50',
})

🏭 Production Setup: Custom RPC Providers

Best for: Production applications, better reliability, custom configuration

import { createPublicClient, http } from 'viem'

// Production-ready setup with custom RPC endpoints
const adapter = createAdapterFromPrivateKey({
  privateKey: process.env.PRIVATE_KEY as string,
  getPublicClient: ({ chain }) =>
    createPublicClient({
      chain,
      transport: http(
        `https://eth-mainnet.alchemyapi.io/v2/${process.env.ALCHEMY_KEY}`,
        {
          retryCount: 3,
          timeout: 10000,
        },
      ),
    }),
})

// Same simple usage, but with production-grade RPC
const result = await kit.bridge({
  from: { adapter, chain: 'Ethereum' },
  to: { adapter, chain: 'Base' },
  amount: '10.50',
})

🌐 Browser/Wallet Provider Support

Best for: Browser applications, wallet integrations, user-controlled transactions

import { createAdapterFromProvider } from '@circle-fin/adapter-viem-v2'

// Create adapters from browser wallet providers
const adapter = await createAdapterFromProvider({
  provider: window.ethereum,
})

// Execute bridge operation
const result = await kit.bridge({
  from: { adapter, chain: 'Ethereum' },
  to: { adapter, chain: 'Base' },
  amount: '10.50',
})

πŸ”§ Advanced Setup: Full Control

Best for: Advanced users, custom client configuration, specific RPC requirements

import { BridgeKit } from '@circle-fin/bridge-kit'
import { Ethereum, Base } from '@circle-fin/bridge-kit/chains'
import { ViemAdapter } from '@circle-fin/adapter-viem-v2'
import { createPublicClient, createWalletClient, http } from 'viem'
import { privateKeyToAccount } from 'viem/accounts'

const account = privateKeyToAccount(process.env.PRIVATE_KEY as string)

// Chain-specific RPC URLs
const rpcUrls = {
  [Ethereum.id]: 'https://eth-mainnet.alchemyapi.io/v2/YOUR_KEY',
  [Base.id]: 'https://base-mainnet.g.alchemy.com/v2/YOUR_KEY',
}

// Create one multi-chain adapter with chain-specific RPC configuration
const adapter = new ViemAdapter(
  {
    getPublicClient: ({ chain }) =>
      createPublicClient({
        chain,
        transport: http(rpcUrls[chain.id]),
      }),
    getWalletClient: ({ chain }) =>
      createWalletClient({
        account,
        chain,
        transport: http(rpcUrls[chain.id]),
      }),
  },
  {
    addressContext: 'user-controlled',
    supportedChains: [Ethereum, Base], // Support multiple chains!
  },
)

const kit = new BridgeKit()
// Execute bridge operation using the same adapter for both chains
const result = await kit.bridge({
  from: { adapter, chain: 'Ethereum' },
  to: { adapter, chain: 'Base' },
  amount: '10.50',
})

πŸ“Š Cost Estimation

Best for: Showing users fees upfront, budget planning

// Get cost estimate before bridging
const estimate = await kit.estimate({
  from: { adapter, chain: 'Ethereum' },
  to: { adapter, chain: 'Base' },
  amount: '10.50',
})

console.log('Estimated fees:', estimate.fees)
console.log('Estimated gas:', estimate.gasFees)

Configuration

Bridge Configuration Types

The Bridge Kit supports different configuration patterns to match your use case:

1. AdapterContext - Your Transfer Endpoint

// Create chain-agnostic adapter
const adapter = createAdapterFromPrivateKey({...})

// Always specify chain explicitly for clarity
const adapterContext = { adapter, chain: 'Ethereum' }

2. BridgeDestination - Where Funds Go

// Same as AdapterContext (adapter receives the funds)
const destination = { adapter, chain: 'Base' }

// Or with explicit recipient address
const destination = {
  adapter,
  chain: 'Base',
  recipientAddress: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e',
}

3. BridgeConfig - Transfer Settings

// FAST: Optimized for speed with higher fees
// SLOW: Optimized for lower fees with longer processing time
const config = { transferSpeed: 'FAST' }

Bridge Speed Configuration

// Fast transfer (higher fees, faster completion)
const result = await kit.bridge({
  from: { adapter, chain: 'Ethereum' },
  to: { adapter, chain: 'Base' },
  amount: '100.0',
  config: { transferSpeed: 'FAST' },
})

// Slow transfer (lower fees, slower completion)
const result = await kit.bridge({
  from: { adapter, chain: 'Ethereum' },
  to: { adapter, chain: 'Base' },
  amount: '100.0',
  config: { transferSpeed: 'SLOW' },
})

Error Handling

The kit uses a thoughtful error handling approach:

  • Hard errors (thrown): Validation, configuration, and authentication errors
  • Soft errors (returned): Recoverable issues like insufficient balance or network errors
import { BridgeKit } from '@circle-fin/bridge-kit'
import { createAdapterFromPrivateKey } from '@circle-fin/adapter-viem-v2'

const kit = new BridgeKit()
const adapter = createAdapterFromPrivateKey({
  privateKey: process.env.PRIVATE_KEY as string,
})

const params = {
  from: { adapter, chain: 'Ethereum' },
  to: { adapter, chain: 'Base' },
  amount: '100.0',
}

const result = await kit.bridge(params)

if (result.state === 'success') {
  console.log('Bridge successful!')
} else {
  // Handle partial completion with recovery information
  console.log(
    'Successful steps:',
    result.steps.filter((s) => s.state === 'success'),
  )
}

Retrying Failed Transfers

Use BridgeKit.retry to resume failed or incomplete bridge operations when the failure is actionable (e.g., transient RPC issues, dropped transactions, or a failed step in a multi-step flow). The kit delegates retry to the original provider (CCTPv2 supports actionable retries) and continues from the appropriate step.

Method signature

retry<
  TFromAdapterCapabilities extends AdapterCapabilities,
  TToAdapterCapabilities extends AdapterCapabilities
>(
  result: BridgeResult,
  context: RetryContext<TFromAdapterCapabilities, TToAdapterCapabilities>
): Promise<BridgeResult>

Basic usage (EVM β†’ EVM)

import { BridgeKit } from '@circle-fin/bridge-kit'
import { createAdapterFromPrivateKey } from '@circle-fin/adapter-viem-v2'

const kit = new BridgeKit()
const adapter = createAdapterFromPrivateKey({
  privateKey: process.env.PRIVATE_KEY as `0x${string}`,
})

const result = await kit.bridge({
  from: { adapter, chain: 'Ethereum_Sepolia' },
  to: { adapter, chain: 'Base_Sepolia' },
  amount: '1',
})

if (result.state === 'error') {
  try {
    const retryResult = await kit.retry(result, { from: adapter, to: adapter })
    console.log('Retry state:', retryResult.state)
  } catch (error) {
    console.error('Retry failed:', error)
  }
}

When to retry vs manual intervention

  • Retry: transient network/RPC errors, gas repricing/dropped txs, step failure with progress recorded.
  • Manual: insufficient funds, incorrect recipient, unsupported route, or errors indicating non-actionable state.

Limitations

  • Only actionable failures can be retried; some failures require user action first.
  • Source and destination chains must still be supported by the provider (CCTPv2).
  • Provide valid adapters for both from and to contexts.

Performance and best practices

  • Use exponential backoff on transient failures; avoid rapid replay.
  • Reprice gas sensibly on congested networks.
  • Persist result.steps and tx hashes to aid observability and support.

Troubleshooting

  • "Retry not supported for this result, requires user action": fix balances/addresses/attestation issues and try again.
  • "Provider not found": ensure the same provider (e.g., CCTPv2) is present in BridgeKit configuration.

See a runnable example at examples/basic-usdc-transfer/src/retry.ts (script: yarn start:retry).

API Reference

Core Methods

  • kit.bridge(params) - Execute cross-chain bridge operation
  • kit.estimate(params) - Get cost estimates before bridging
  • kit.retry(result, context) - Resume actionable failed/partial transfers
  • kit.supportsRoute(source, destination, token) - Check route support
  • kit.on(event, handler) - Listen to bridge events
  • kit.off(event, handler) - Removes the listener from bridge events

Bridge Parameters

interface BridgeParams {
  from: AdapterContext // Source wallet and chain
  to: BridgeDestination // Destination wallet/address and chain
  amount: string // Amount to transfer (e.g., '10.50')
  token?: 'USDC' // Optional, defaults to 'USDC'
  config?: BridgeConfig // Optional bridge configuration (e.g., transfer speed). If omitted, defaults will be used
}

// AdapterContext: Your blockchain connection
type AdapterContext = {
  adapter: Adapter
  chain: ChainIdentifier
  address?: string // Required for developer-controlled adapters; forbidden for user-controlled
}

// BridgeDestination: Where funds go
type BridgeDestination =
  | AdapterContext
  | {
      adapter: Adapter // Adapter for the destination chain
      chain: ChainIdentifier // Chain identifier
      recipientAddress: string // Custom recipient address
    }

Development

Building

# From the root of the monorepo
nx build @circle-fin/bridge-kit

Testing

# From the root of the monorepo
nx test @circle-fin/bridge-kit

Local Development

# Install dependencies
yarn install

# Build all packages
yarn build

# Build the bridge-kit specifically
nx build @circle-fin/bridge-kit

# Run tests
nx test @circle-fin/bridge-kit

Community & Support

License

This project is licensed under the Apache 2.0 License. Contact support for details.

Ready to start bridging?

Join Discord

Built with ❀️ by Circle

FAQs

Package last updated on 17 Nov 2025

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts