npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@circle-fin/app-kit

v1.1.0

Published

<div align="center">

Downloads

321

Readme

App Kit

npm version TypeScript License Discord

A unified, strongly-typed SDK for seamless stablecoin operations across chains

Making cross-chain transfers and same-chain swaps as simple as a single function call

Table of Contents

Overview

The App 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 App Kit provides a unified interface for both cross-chain transfers and same-chain swaps, abstracting away the complexity of choosing between bridging and swapping operations. It combines the power of Bridge Kit and Swap Kit into a single, cohesive API.

Why App Kit?

  • 🎯 Unified interface: Single API for both bridge and swap operations
  • 🤖 Smart routing: Automatically selects the appropriate operation (bridge vs swap)
  • ⚡ 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 for bridging and trusted swap providers
  • 🚀 Developer experience: Complete TypeScript support, comprehensive validation, and instant connectivity
  • 🌍 Multi-chain support: Bridge across 41 chains with 800 total bridge routes through Circle's CCTPv2
    • Mainnet (20 chains): Arbitrum, Avalanche, Base, Codex, Edge, Ethereum, HyperEVM, Ink, Linea, Monad, Morph, OP Mainnet, Plume, Polygon PoS, Sei, Solana, Sonic, Unichain, World Chain, XDC
    • Testnet (21 chains): Arc Testnet, Arbitrum Sepolia, Avalanche Fuji, Base Sepolia, Codex Testnet, Edge Testnet, Ethereum Sepolia, HyperEVM Testnet, Ink Testnet, Linea Sepolia, Monad Testnet, Morph Testnet, OP Sepolia, Plume Testnet, Polygon PoS Amoy, Sei Testnet, Solana Devnet, Sonic Testnet, Unichain Sepolia, World Chain Sepolia, XDC Apothem
  • 🔄 Swap support: Same-chain token swaps powered by Circle's Stablecoin Service
  • 🎯 Flexible adapters: Supporting EVM (Viem, Ethers) and Solana (@solana/web3)
  • 📡 Real-time event monitoring: Track progress throughout the operation lifecycle
  • 🛡️ Robust error handling: Graceful partial success recovery
  • ✈️ Pre-flight validation: Verify operations with cost estimation before execution

Architecture Flow

The App Kit follows a composable architecture that integrates Bridge Kit and Swap Kit:

┌─────────────────────────────────────────────────────────────┐
│                        App Kit                               │
│                   (Unified Interface)                       │
└──────────────────────┬──────────────────────────────────────┘
                       │
         ┌─────────────┴─────────────┐
         │                           │
         ▼                           ▼
┌─────────────────┐         ┌─────────────────┐
│   Bridge Kit    │         │    Swap Kit     │
│ (Cross-Chain)   │         │  (Same-Chain)   │
└────────┬────────┘         └────────┬────────┘
         │                           │
         ▼                           ▼
┌─────────────────┐         ┌─────────────────┐
│     Provider    │         │     Provider    │
│   (CCTPv2)      │         │   (Service)     │
└────────┬────────┘         └────────┬────────┘
         │                           │
         └─────────────┬─────────────┘
                       ▼
               ┌───────────────┐
               │    Adapter    │
               │ (Blockchain)  │
               └───────────────┘
  1. Adapter: Handles blockchain-specific operations (wallets, transactions, gas)
  2. Provider: Implements protocols (CCTPv2 for bridging, Circle Service for swaps)
  3. Bridge/Swap Kit: Specialized kits for each operation type
  4. App Kit: Unified orchestration layer providing a consistent API

This separation ensures that each component has a single responsibility while maintaining seamless integration across the entire stablecoin operation lifecycle.

Installation

npm install @circle-fin/app-kit
# or
yarn add @circle-fin/app-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

Chain Definitions

Import chain definitions directly from the kit:

import { Ethereum, Base, Polygon, Solana } from '@circle-fin/app-kit/chains'

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

All 41 CCTPv2-supported chains are available for import.

Quick Start

🌉 Cross-Chain Bridge

Transfer USDC from one chain to another:

import { AppKit } from '@circle-fin/app-kit'
import { createViemAdapterFromPrivateKey } from '@circle-fin/adapter-viem-v2'

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

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

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

🔄 Same-Chain Swap

Swap tokens on the same chain with support for multiple stablecoins:

// Swap USDC to USDT on Ethereum
const result = await kit.swap({
  from: { adapter, chain: 'Ethereum' },
  tokenIn: 'USDC',
  tokenOut: 'USDT',
  amountIn: '100.0',
})

// Swap DAI to USDC (18 decimals handled automatically)
const daiSwap = await kit.swap({
  from: { adapter, chain: 'Ethereum' },
  tokenIn: 'DAI',
  tokenOut: 'USDC',
  amountIn: '500.0',
})

// Swap native ETH to stablecoin
const nativeSwap = await kit.swap({
  from: { adapter, chain: 'Ethereum' },
  tokenIn: 'NATIVE', // ETH on Ethereum
  tokenOut: 'USDC',
  amountIn: '1.5',
})

📊 Estimate Operations

Get cost estimates before executing operations:

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

// Estimate swap operation
const swapEstimate = await kit.estimateSwap({
  from: { adapter, chain: 'Ethereum' },
  tokenIn: 'USDC',
  tokenOut: 'USDT',
  amountIn: '100.0',
})

console.log('Bridge fees:', bridgeEstimate.fees)
console.log(
  'Swap estimated output:',
  swapEstimate.estimatedOutput?.amount,
  swapEstimate.estimatedOutput?.token,
)

🔍 Query Supported Chains

Check which chains support specific operations:

// Get all chains that support bridging
const bridgeChains = kit.getSupportedChains('bridge')

// Get all chains that support swapping
const swapChains = kit.getSupportedChains('swap')

// Get all chains (both bridge and swap)
const allChains = kit.getSupportedChains()

Configuration

Send Parameters

The send() method provides a unified interface for transferring tokens. It automatically determines whether to use bridging or swapping based on the source and destination chains:

interface SendParams {
  from: AdapterContext // Source wallet and chain
  to: Adapter | string // Destination wallet or address
  amount: string // Amount to transfer (e.g., '10.50')
  token?: TokenAlias | string // Optional, defaults to 'USDC'
}

Token Support

The token field accepts both known aliases and custom token contract addresses:

Known aliases (for send operations):

  • 'USDC' - USD Coin (6 decimals)
  • 'USDT' - Tether USD (6 decimals)
  • 'NATIVE' - Chain's native currency (ETH, SOL, etc.)

Custom addresses:

  • EVM contract addresses (e.g., 0x6B175474E89094C44Da98b954EedeAC495271d0F)
  • Solana SPL mint addresses

Note: For swap operations, additional stablecoins (EURC, DAI, USDE, PYUSD) are supported. See Swap Parameters section below.

Examples

// Using USDC (default)
await kit.send({
  from: { adapter, chain: 'Ethereum' },
  to: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e',
  amount: '10.0',
  token: 'USDC',
})

// Using USDT
await kit.send({
  from: { adapter, chain: 'Ethereum' },
  to: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e',
  amount: '100.0',
  token: 'USDT',
})

// Using NATIVE
await kit.send({
  from: { adapter, chain: 'Ethereum' },
  to: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e',
  amount: '1.5',
  token: 'NATIVE', // ETH on Ethereum
})

// Using a custom ERC-20 token address (e.g., DAI)
await kit.send({
  from: { adapter, chain: 'Ethereum' },
  to: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e',
  amount: '50.0',
  token: '0x6B175474E89094C44Da98b954EedeAC495271d0F', // DAI contract address
})

// Using a custom SPL token on Solana
await kit.send({
  from: { adapter, chain: 'Solana' },
  to: 'DhzPkKCLJGHBZbs1AzmK2tRNLZkV8J3yWF3LuWMuKJpN',
  amount: '50.0',
  token: 'So11111111111111111111111111111111111111112', // Wrapped SOL
})

Swap Parameters

For explicit same-chain swaps:

interface SwapParams {
  from: AdapterContext // Source wallet and chain
  tokenIn: SupportedToken // Input token
  tokenOut: SupportedToken // Output token
  amountIn: string // Amount to swap
  to?: string // Optional recipient address
  config?: SwapConfig // Optional swap configuration
}

// SupportedToken includes:
// - Stablecoins (6 decimals): 'USDC', 'EURC', 'USDT', 'PYUSD'
// - Stablecoins (18 decimals): 'DAI', 'USDE'
// - Native: 'NATIVE' (chain's native currency)

Swap Examples with Different Tokens

// Swap between 6-decimal stablecoins
await kit.swap({
  from: { adapter, chain: 'Ethereum' },
  tokenIn: 'EURC',
  tokenOut: 'USDC',
  amountIn: '100.0',
})

// Swap DAI (18 decimals) to USDT
await kit.swap({
  from: { adapter, chain: 'Ethereum' },
  tokenIn: 'DAI',
  tokenOut: 'USDT',
  amountIn: '500.0', // SDK automatically handles 18-decimal precision
})

// Swap native token to stablecoin
await kit.swap({
  from: { adapter, chain: 'Ethereum' },
  tokenIn: 'NATIVE', // ETH on Ethereum
  tokenOut: 'USDC',
  amountIn: '2.5',
})

Bridge Parameters

For explicit cross-chain bridges:

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
}

Custom Fees

App Kit supports custom developer fees on both bridge and swap operations.

Operation-Level Custom Fees

Apply custom fees to individual operations:

// Bridge with custom fee
await kit.bridge({
  from: { adapter, chain: 'Ethereum' },
  to: { adapter, chain: 'Base' },
  amount: '1000',
  config: {
    customFee: {
      value: '10', // 10 USDC fee
      recipientAddress: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0',
    },
  },
})

// Swap with custom fee
await kit.swap({
  from: { adapter, chain: 'Ethereum' },
  tokenIn: 'USDC',
  tokenOut: 'USDT',
  amountIn: '1000',
  config: {
    customFee: {
      value: '10',
      recipientAddress: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0',
    },
  },
})

Kit-Level Fee Policies

For dynamic fee calculation across all operations, use kit-level policies:

import { AppKit } from '@circle-fin/app-kit'
import { formatUnits } from 'viem'

const kit = new AppKit()

kit.setCustomFeePolicy({
  calculateFee: (params) => {
    // Calculate fee based on operation type and parameters
    const amount = Number(formatUnits(BigInt(params.amount), 6))
    const feePercentage = type === 'bridge' ? 0.01 : 0.005 // 1% for bridge, 0.5% for swap

    return (amount * feePercentage).toFixed(6)
  },
  resolveFeeRecipientAddress: (type, info) => {
    // Return appropriate address based on operation type and chain
    return info.chain.type === 'solana'
      ? 'SolanaAddressBase58...'
      : '0xEvmAddress...'
  },
})

// All subsequent operations will use this policy
await kit.bridge({
  from: { adapter, chain: 'Ethereum' },
  to: { adapter, chain: 'Base' },
  amount: '1000', // Custom fee calculated automatically
})

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 { AppKit } from '@circle-fin/app-kit'
import { createViemAdapterFromPrivateKey } from '@circle-fin/adapter-viem-v2'

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

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

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

Examples

Basic Cross-Chain Transfer

import { AppKit } from '@circle-fin/app-kit'
import { createViemAdapterFromPrivateKey } from '@circle-fin/adapter-viem-v2'

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

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

console.log('Bridge result:', result)

Same-Chain Token Swap

// Swap USDC to USDT on Ethereum
const result = await kit.swap({
  from: { adapter, chain: 'Ethereum' },
  tokenIn: 'USDC',
  tokenOut: 'USDT',
  amountIn: '100.0',
})

console.log('Swap result:', result)

Send to Different Address

// Send to a different address (automatically chooses bridge or swap)
const result = await kit.send({
  from: { adapter, chain: 'Ethereum' },
  to: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e',
  amount: '10.50',
  token: 'USDC',
})

Event Monitoring

// Listen to bridge events
kit.on('bridge.approve', (payload) => {
  console.log('Approval transaction:', payload.values.txHash)
})

kit.on('bridge.burn', (payload) => {
  console.log('Burn transaction:', payload.values.txHash)
})

// Listen to all events
kit.on('*', (payload) => {
  console.log('Action:', payload.method)
})

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

API Reference

Core Methods

  • kit.send(params) - Unified interface for transfers (auto-routes to bridge or swap)
  • kit.bridge(params) - Execute cross-chain bridge operation
  • kit.swap(params) - Execute same-chain swap operation
  • kit.estimateBridge(params) - Get cost estimates for bridging
  • kit.estimateSwap(params) - Get cost estimates for swapping
  • kit.estimateSend(params) - Get cost estimates for send operation
  • kit.getSupportedChains(operationType?) - Query supported chains by operation type
  • kit.setCustomFeePolicy(policy) - Set kit-level custom fee policy
  • kit.on(event, handler) - Listen to operation events
  • kit.off(event, handler) - Remove event listener

Development

Building

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

Testing

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

Local Development

# Install dependencies
yarn install

# Build all packages
yarn build

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

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

Community & Support

License

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


Ready to start building?

Join Discord

Built with ❤️ by Circle