
Research
/Security News
Shai Hulud Strikes Again (v2)
Another wave of Shai-Hulud campaign hits npm.
@circle-fin/bridge-kit
Advanced tools
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
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
The Bridge Kit follows a three-layer architecture designed for flexibility and type safety:
βββββββββββββββββββ ββββββββββββββββββββ βββββββββββββββββββ
β Bridge Kit ββββββ Provider ββββββ Adapter β
β (Orchestrator) β β (Protocol) β β (Blockchain) β
βββββββββββββββββββ ββββββββββββββββββββ βββββββββββββββββββ
This separation ensures that each component has a single responsibility while maintaining seamless integration across the entire cross-chain bridging lifecycle.
npm install @circle-fin/bridge-kit
# or
yarn add @circle-fin/bridge-kit
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
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.
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',
})
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',
})
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',
})
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',
})
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)
The Bridge Kit supports different configuration patterns to match your use case:
// Create chain-agnostic adapter
const adapter = createAdapterFromPrivateKey({...})
// Always specify chain explicitly for clarity
const adapterContext = { adapter, chain: 'Ethereum' }
// Same as AdapterContext (adapter receives the funds)
const destination = { adapter, chain: 'Base' }
// Or with explicit recipient address
const destination = {
adapter,
chain: 'Base',
recipientAddress: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e',
}
// FAST: Optimized for speed with higher fees
// SLOW: Optimized for lower fees with longer processing time
const config = { transferSpeed: 'FAST' }
// 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' },
})
The kit uses a thoughtful error handling approach:
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'),
)
}
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.
retry<
TFromAdapterCapabilities extends AdapterCapabilities,
TToAdapterCapabilities extends AdapterCapabilities
>(
result: BridgeResult,
context: RetryContext<TFromAdapterCapabilities, TToAdapterCapabilities>
): Promise<BridgeResult>
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)
}
}
from and to contexts.result.steps and tx hashes to aid observability and support.BridgeKit configuration.See a runnable example at
examples/basic-usdc-transfer/src/retry.ts(script:yarn start:retry).
kit.bridge(params) - Execute cross-chain bridge operationkit.estimate(params) - Get cost estimates before bridgingkit.retry(result, context) - Resume actionable failed/partial transferskit.supportsRoute(source, destination, token) - Check route supportkit.on(event, handler) - Listen to bridge eventskit.off(event, handler) - Removes the listener from bridge eventsinterface 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
}
# From the root of the monorepo
nx build @circle-fin/bridge-kit
# From the root of the monorepo
nx test @circle-fin/bridge-kit
# 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
This project is licensed under the Apache 2.0 License. Contact support for details.
FAQs
SDK for seamless cross-chain stablecoin bridging
We found that @circle-fin/bridge-kit demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago.Β It has 3 open source maintainers collaborating on the project.
Did you know?

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.

Research
/Security News
Another wave of Shai-Hulud campaign hits npm.

Product
Add real-time Socket webhook events to your workflows to automatically receive software supply chain alert changes in real time.

Security News
ENISA has become a CVE Program Root, giving the EU a central authority for coordinating vulnerability reporting, disclosure, and cross-border response.