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/adapter-solana

v1.6.2

Published

Solana blockchain adapter powered by @solana/web3.js

Readme

Solana Adapter

npm version TypeScript License Discord

Solana Adapter implementation powered by @solana/web3.js

Seamlessly integrate your existing Solana setup with the App Kits ecosystem

Table of Contents

Overview

The Solana Adapter is a strongly-typed implementation of the Adapter interface for the Solana blockchain. Built on top of the popular @solana/web3.js library, it provides type-safe blockchain interactions through a unified interface that's designed to work seamlessly with the Bridge Kit for cross-chain USDC transfers between Solana and EVM networks, as well as any future kits for additional stablecoin operations. It can be used by any Kit built using the App Kits architecture and/or any providers plugged into those kits.

Why Solana Adapter?

  • ⚡ Zero-config defaults - Built-in reliable RPC endpoints for Solana mainnet and devnet
  • 🌐 Full Solana compatibility: Works with Solana mainnet and devnet
  • 🔧 Bring your own setup: Use your existing Solana Connection and Keypair instances
  • 🚀 Instant connectivity: Connect without researching RPC providers
  • ⚡ High performance: Leverages Solana's fast and low-cost blockchain
  • 🔒 Type-safe: Built with TypeScript strict mode for complete type safety
  • 🎯 Simple API: Clean abstraction over complex blockchain operations
  • 🔄 Transaction lifecycle: Complete prepare/estimate/execute workflow
  • 🌉 Cross-chain ready: Seamlessly bridge USDC between Solana and EVM chains

When and How Should I Use The Solana Adapter?

I'm a developer using a kit

If you're using one of the kits to do some action, e.g. bridging from chain 'A' to chain 'B', then you only need to instantiate the adapter for your chain and pass it to the kit.

Example
const adapter = new SolanaAdapter({
  connection,
  signer: keypair,
})

I'm a developer making a Kit Provider

If you are making a provider for other Kit users to plug in to the kit, e.g. a BridgeProvider, and you'll need to interact with diff chains, then you'll need to use the abstracted Adapter methods to execute on chain.

Installation

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

Peer Dependencies

This adapter requires @solana/web3.js as a peer dependency. Install it alongside the adapter.

Browser Support

Zero configuration required! 🎉

The adapter automatically handles browser compatibility by:

  • Bundling the Buffer polyfill inline (~6KB gzipped)
  • Setting up global Buffer before Solana libraries load
  • Working seamlessly in all modern browsers

No additional packages or configuration needed!

Supported Versions:

  • @solana/web3.js: ^1.98.2

Troubleshooting Version Conflicts

If you encounter peer dependency warnings:

  • Check your package version: npm ls @solana/web3.js
  • Ensure the version meets the minimum requirement
  • Use npm install @solana/web3.js@^1.98.2 to install a compatible version

Quick Start

🚀 Easy Setup with Factory Methods (Recommended)

The simplest way to get started is with our factory methods. With reliable default RPC endpoints - no need to configure Solana RPC providers!

import { createSolanaAdapterFromPrivateKey } from '@circle-fin/adapter-solana'

// Create an adapter with built-in reliable RPC endpoints
const adapter = createSolanaAdapterFromPrivateKey({
  privateKey: process.env.SOLANA_PRIVATE_KEY!, // Base58, Base64, or JSON array format
})

// Ready to use with App Kit!
const address = await adapter.getAddress()
console.log('Connected address:', address)

✨ Key Feature: All Solana chains include reliable default RPC endpoints:

  • Solana Mainnet: https://solana-mainnet.public.blastapi.io
  • Solana Devnet: https://solana-devnet.public.blastapi.io

🏭 Production Considerations

⚠️ Important for Production: The factory methods use Solana's default public RPC endpoints by default when no custom connection is provided, which may have rate limits and lower reliability. For production applications, we strongly recommend providing a custom connection with dedicated RPC providers like Helius, QuickNode, or Alchemy.

import { createSolanaAdapterFromPrivateKey } from '@circle-fin/adapter-solana'
import { Connection } from '@solana/web3.js'

// Production-ready setup with custom RPC endpoints
const customConnection = new Connection(
  `https://solana-mainnet.g.alchemy.com/v2/${process.env.ALCHEMY_KEY}`,
  {
    commitment: 'confirmed',
    wsEndpoint: `wss://solana-mainnet.g.alchemy.com/v2/${process.env.ALCHEMY_KEY}`,
  },
)

const adapter = createSolanaAdapterFromPrivateKey({
  privateKey: process.env.SOLANA_PRIVATE_KEY!,
  connection: customConnection,
})

🌐 Browser Support with Wallet Providers

For browser environments with wallet providers like Phantom, Solflare, or Backpack:

import { createSolanaAdapterFromProvider } from '@circle-fin/adapter-solana'

// Create an adapter from a browser wallet
const adapter = await createSolanaAdapterFromProvider({
  provider: window.solana, // Phantom, Solflare, etc.
})

🔧 Advanced Manual Setup

For advanced use cases requiring full control over Connection and Keypair:

import { SolanaAdapter } from '@circle-fin/adapter-solana'
import { Connection, Keypair } from '@solana/web3.js'
import bs58 from 'bs58'

// Use your existing Solana setup
const connection = new Connection('https://api.mainnet-beta.solana.com')
const keypair = Keypair.fromSecretKey(
  bs58.decode(process.env.SOLANA_PRIVATE_KEY),
)

// Create the adapter
const adapter = new SolanaAdapter(
  {
    connection,
    signer: keypair,
  },
  {
    addressContext: 'developer-controlled',
    supportedChains: [Solana],
  },
)

🎯 Context-Aware Lazy Signer Initialization

For advanced scenarios like hardware wallets, multi-chain operations, or dynamic signer selection:

import { SolanaAdapter } from '@circle-fin/adapter-solana'
import { Connection } from '@solana/web3.js'
import { Solana, SolanaDevnet } from '@circle-fin/bridge-kit/chains'

// Create adapter with lazy signer initialization
const adapter = new SolanaAdapter(
  {
    connection: new Connection('https://api.mainnet-beta.solana.com'),
    // Signer is initialized on-demand with context about the operation
    signer: async (ctx) => {
      // Access operation context: chain, address, etc.
      console.log('Signing for chain:', ctx.chain.name)

      // Example: Select different signers based on chain
      if (ctx.chain.name === 'Solana') {
        return await getMainnetSigner()
      } else {
        return await getDevnetSigner()
      }
    },
  },
  {
    addressContext: 'user-controlled',
    supportedChains: [Solana, SolanaDevnet],
  },
)

// Signer is initialized only when needed
const prepared = await adapter.prepare(params, { chain: Solana })

Benefits of lazy initialization:

  • Performance - Signer initialized only when needed
  • 🔐 Security - Shorter lifetime of sensitive keys in memory
  • 🎯 Context-aware - Select signers based on chain, address, or other factors
  • 🔄 Flexible - Support hardware wallets, KMS, or dynamic signer sources

Backward Compatibility:

// Traditional approach still works - no context parameter needed
const adapter = new SolanaAdapter(
  {
    connection,
    signer: async () => {
      // No ctx parameter - works for simple cases
      return await initializeSigner()
    },
  },
  capabilities,
)

Next-Generation API (/next)

Preview — The /next entrypoint is the upcoming default adapter architecture. It is at parity with the current entrypoint for the supported bridge flows and fully compatible with all existing kits (Bridge Kit, etc.). In the next major release, /next will become the default import and the current entrypoint will be retired. See Current limitations of /next before adopting it.

Getting Started with /next

Switch to the new architecture by changing a single import path. Everything else stays the same:

import { createSolanaAdapterFromPrivateKey } from '@circle-fin/adapter-solana/next'

const adapter = createSolanaAdapterFromPrivateKey({
  privateKey: process.env.SOLANA_PRIVATE_KEY!, // Base58, Base64, or JSON array
})

// Pass it to the Bridge Kit exactly as before — no changes needed downstream

Browser wallets work identically (note: the provider factory is async):

import { createSolanaAdapterFromProvider } from '@circle-fin/adapter-solana/next'

const adapter = await createSolanaAdapterFromProvider({
  provider: window.solana, // Phantom, Solflare, etc.
})

Why /next?

| | Current entrypoint | /next entrypoint | | ---------------------- | ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | | Bundle size | Entire adapter is included | Per-primitive imports unlock tree-shaking; the default factory still pulls the compat shim — see bundle-size note | | Prepare result | estimate() and execute() | estimate(), simulate(), and execute() | | Fee model | Adapter-specific shape | { fee, units, unitPrice } — consistent across all ecosystems | | Observability | Not available | Built-in logging, metrics, and event instrumentation | | Retry & resilience | Not available | Configurable retry with exponential backoff for transient failures | | Token resolution | Hard-coded addresses | Pluggable token registry — resolve any token by symbol across chains | | Transaction tuning | Fixed defaults | Configurable confirmation timeout and retry policy |

Bundle-size note

The /next entrypoint's clean architecture is designed to be tree-shakable on a per-primitive basis: importing createPrepare without createBatchExecute should only ship the prepare path. In the current preview release the default createAdapter factory still pulls in the legacy compat shim (withLegacyCompat@core/adapter-compat's action registry) so that consumers can pass the resulting adapter to existing kits and providers without changes. That shim is what makes the default /next bundle measure ~11% larger than the legacy entrypoint in our internal benchmarks.

If bundle size is critical to you, compose primitives directly via assembleAdapter from @core/adapter-base and skip withLegacyCompat. A future minor release will split the compat shim behind a separate entrypoint so the default factory ships the new API only.

Current limitations of /next

The /next Solana adapter is at parity with the current entrypoint for the supported bridge flows, but a couple of caveats apply. They are tracked in the project issue tracker for follow-up releases:

  • The Circle Wallets adapter has not been migrated to /next. Only the EVM (viem/ethers) and Solana adapters expose a /next entrypoint; Circle Wallets remains on the current entrypoint.
  • Abort semantics stop the local wait only. Passing an aborted (or abortable) signal cancels waitForTransaction and prevents an unsent execute from broadcasting, but once a transaction has been broadcast, aborting does not cancel it on-chain — the transaction continues and should be reconciled via its signature.

Prepared Transactions: Estimate, Simulate, Execute

When you prepare a transaction through the /next adapter, you get back a richer object with three lifecycle methods:

const prepared = await adapter.prepare(
  {
    instructions: [transferInstruction],
    feePayer: payerPublicKey,
  },
  { chain: 'Solana' },
)

// 1. Estimate fees before committing
const { fee, units, unitPrice } = await prepared.estimate()

// 2. Simulate the transaction to catch errors without spending fees
const simulation = await prepared.simulate()

// 3. Execute when ready
const result = await prepared.execute()

// `result` is a `{ txId, wait }` envelope:
// - `txId` is the transaction signature (base58 string on Solana —
//   named `txId` for cross-ecosystem symmetry with the EVM adapters).
// - `wait()` resolves once the transaction is confirmed on the cluster.
console.log('submitted signature:', result.txId)
const confirmation = await result.wait()

The simulate() step is new in /next — it dry-runs the transaction against the current ledger state and surfaces errors before you pay transaction fees.

Logging

Pass a runtime option to get structured logs for every adapter operation, including RPC calls, retries, and errors. The quickest way to see what's happening is to use the built-in createRuntime helper:

import {
  createSolanaAdapterFromPrivateKey,
  createRuntime,
} from '@circle-fin/adapter-solana/next'

const adapter = createSolanaAdapterFromPrivateKey({
  privateKey: process.env.SOLANA_PRIVATE_KEY!,
  runtime: createRuntime(), // Logs to stdout via pino at 'info' level
})

To integrate with your own logger, pass any object that implements debug, info, warn, error, and child to createRuntime. The logger interface uses (message, fields) argument order — libraries like pino use (fields, message), so a thin wrapper is needed:

import {
  createSolanaAdapterFromPrivateKey,
  createRuntime,
} from '@circle-fin/adapter-solana/next'
import pino from 'pino'

function wrapPino(p: pino.Logger) {
  return {
    debug: (msg: string, fields?: Record<string, unknown>) =>
      p.debug(fields, msg),
    info: (msg: string, fields?: Record<string, unknown>) =>
      p.info(fields, msg),
    warn: (msg: string, fields?: Record<string, unknown>) =>
      p.warn(fields, msg),
    error: (msg: string, fields?: Record<string, unknown>) =>
      p.error(fields, msg),
    child: (tags: Record<string, unknown>) => wrapPino(p.child(tags)),
  }
}

const adapter = createSolanaAdapterFromPrivateKey({
  privateKey: process.env.SOLANA_PRIVATE_KEY!,
  runtime: createRuntime({ logger: wrapPino(pino({ level: 'debug' })) }),
})

Metrics

The runtime option also accepts a metrics implementation for counters, histograms, and timers. Plug into Prometheus, StatsD, Datadog, OpenTelemetry, or any other backend:

import {
  createSolanaAdapterFromPrivateKey,
  createRuntime,
} from '@circle-fin/adapter-solana/next'

const adapter = createSolanaAdapterFromPrivateKey({
  privateKey: process.env.SOLANA_PRIVATE_KEY!,
  runtime: createRuntime({
    metrics: {
      counter: (name) => ({
        inc: (labels, value) => {
          /* emit counter */
        },
      }),
      histogram: (name) => ({
        observe: (labels, value) => {
          /* emit histogram */
        },
      }),
      timer: (name) => ({
        start: (labels) => {
          const t0 = Date.now()
          return () => {
            /* observe Date.now() - t0 */
          }
        },
      }),
      child: (labels) => {
        /* return scoped Metrics with base labels merged */
      },
    },
  }),
})

When no metrics is provided, metric calls are silently no-op'd — zero overhead.

Token Registry

By default the adapter knows about USDC (and other built-in tokens) on every supported chain. The tokens option lets you register additional tokens so they can be referenced by symbol instead of raw addresses.

import {
  createSolanaAdapterFromPrivateKey,
  createTokenRegistry,
} from '@circle-fin/adapter-solana/next'

const tokens = createTokenRegistry({
  tokens: [
    {
      symbol: 'BONK',
      decimals: 5,
      locators: {
        Solana: 'DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263',
      },
    },
  ],
})

const adapter = createSolanaAdapterFromPrivateKey({
  privateKey: process.env.SOLANA_PRIVATE_KEY!,
  tokens,
})

The registry resolves the correct program address and decimals for each chain automatically, so downstream code can refer to 'BONK' rather than hard-coding mint addresses.

Transaction Tuning

The config option lets you tune retry behavior and confirmation timeouts:

import { createSolanaAdapterFromPrivateKey } from '@circle-fin/adapter-solana/next'

const adapter = createSolanaAdapterFromPrivateKey({
  privateKey: process.env.SOLANA_PRIVATE_KEY!,
  config: {
    transaction: {
      confirmationTimeoutMs: 60_000, // 60 second confirmation timeout
    },
    retry: {
      maxAttempts: 5,
      baseDelayMs: 500,
    },
  },
})

| Setting | What it controls | Default | | ----------------------- | ------------------------------------------------------------------------------------------ | -------------- | | confirmationTimeoutMs | How long waitForTransaction waits before timing out | Client default | | retry.maxAttempts | Maximum number of retry attempts (not including the initial attempt) | 3 | | retry.baseDelayMs | Base delay between retries in milliseconds (doubles on each retry via exponential backoff) | 200 |

Set retry: false to disable automatic retries entirely.

Migration Path

The /next entrypoint is designed for a zero-friction migration:

  1. Today — Import from @circle-fin/adapter-solana/next. The adapter returned is fully compatible with all existing kits and providers — no changes needed on their side.
  2. Next major release — The /next architecture becomes the default entrypoint (@circle-fin/adapter-solana). The current class-based adapter will be removed.

What do I need to change?

  • Change the import path to @circle-fin/adapter-solana/next.
  • The chain parameter has been removed from the factory. Chains are now resolved via capabilities.supportedChains (all Solana chains by default) and specified per-operation through OperationContext.
  • If you only pass the adapter to a kit, the kit handles chain selection automatically — no other changes needed.

Features

  • Full Solana compatibility - Works with Solana mainnet and devnet
  • Solana/web3.js integration - Uses your existing Connection and Keypair
  • Wallet provider support - Works with Phantom, Solflare, and other Solana wallets
  • Factory methods - Easy setup with sensible defaults
  • Network validation - Automatically validates wallet/connection matches configured network
  • Transaction lifecycle - Complete prepare/estimate/execute workflow
  • Type safety - Full TypeScript support with strict mode
  • Cross-chain ready - Seamlessly bridge USDC between Solana and EVM chains

Usage Examples

Multiple Private Key Format Support

The createSolanaAdapterFromPrivateKey function automatically detects and handles different private key formats:

// Base58 format (most common for Solana)
const adapterBase58 = createSolanaAdapterFromPrivateKey({
  privateKey: '5Kk7z8gvn...', // Base58 string
})

// Base64 format
const adapterBase64 = createSolanaAdapterFromPrivateKey({
  privateKey: 'MIGHAgEAMB...', // Base64 string
})

// JSON array format (array of bytes)
const adapterArray = createSolanaAdapterFromPrivateKey({
  privateKey: '[1,2,3,4,5,...]', // JSON array string
})

Browser Wallet Integration

import { createSolanaAdapterFromProvider } from '@circle-fin/adapter-solana'

// Connect to user's Solana wallet
const adapter = await createSolanaAdapterFromProvider({
  provider: window.solana, // Phantom, Solflare, etc.
})

Manual Setup with Solana Clients

import { SolanaAdapter } from '@circle-fin/adapter-solana'
import { Connection, Keypair } from '@solana/web3.js'
import bs58 from 'bs58'

// Initialize with your Solana setup
const connection = new Connection('https://api.mainnet-beta.solana.com')
const keypair = Keypair.fromSecretKey(
  bs58.decode(process.env.SOLANA_PRIVATE_KEY),
)

const adapter = new SolanaAdapter({
  connection,
  signer: keypair,
})

Supported Networks

Works with all Solana networks supported by the App Kit:

  • Solana Mainnet - Production network
  • Solana Devnet - Development and testing network

API Reference

Factory Methods

createSolanaAdapterFromPrivateKey(params)

Create an adapter from a private key (supports multiple formats).

interface CreateSolanaAdapterFromPrivateKeyParams {
  privateKey: string // Supports Base58, Base64, or JSON array formats
  connection?: Connection // Optional pre-configured Connection instance
  capabilities?: AdapterCapabilities // Optional addressContext and supportedChains
}

createSolanaAdapterFromProvider(params)

Create an adapter from a Solana wallet provider.

interface CreateSolanaAdapterFromProviderParams {
  provider: SolanaWalletProvider // Phantom, Solflare, etc.
  connection?: Connection // Optional pre-configured Connection instance
  capabilities?: AdapterCapabilities // Optional addressContext and supportedChains
}

Constructor Options

interface SolanaAdapterOptions<TAdapterCapabilities = AdapterCapabilities> {
  connection?: Connection // Optional - can use getConnection instead
  getConnection?: (params: { chain: ChainDefinition }) => Connection // Lazy connection init
  signer:
    | Signer // Direct signer (Keypair or ProviderSigner)
    | ((ctx: OperationContext<TAdapterCapabilities>) => Promise<Signer>) // Context-aware lazy init
}

Signer Options:

  • Direct Signer: Pass a Keypair or browser wallet ProviderSigner directly
  • Context-Aware Function: Pass an async function that receives operation context and returns a signer
    • Enables chain-specific or address-specific signer selection
    • Supports lazy initialization for hardware wallets or KMS
    • Backward compatible - functions can ignore the context parameter if not needed

Methods

  • getAddress() - Get the connected wallet address
  • prepare() - Prepare transactions for execution
  • estimate() - Estimate transaction costs
  • execute() - Execute prepared transactions
  • waitForTransaction() - Wait for transaction confirmation
  • calculateTransactionFee() - Calculate transaction fees with optional buffer

You can see the implementation of each of these methods in the Solana Adapter's main implementation file.

Network Validation

The Solana adapter includes automatic network validation to prevent common errors where your wallet/connection is on a different network than your code configuration.

How It Works

When you first use an adapter (via prepare()), the system automatically:

  1. Check the genesis hash of your connection/wallet
  2. Compare it against the expected network from your chain configuration
  3. Throw a helpful error if there's a mismatch
  4. Cache the validation result for subsequent operations

Common Scenarios

import { SolanaDevnet } from '@circle-fin/bridge-kit/chains'

// Adapter creation succeeds (no network validation yet)
const adapter = await createSolanaAdapterFromProvider({
  provider: window.solana, // Connected to mainnet
  capabilities: {
    supportedChains: [SolanaDevnet], // But code expects devnet!
  },
})

// ❌ This will throw an error when you try to prepare a transaction
await adapter.prepare({
  instructions: [...]
})
// Error: Network mismatch detected! Your wallet/connection is on Solana Mainnet,
// but the code is configured for Solana Devnet...

// ✅ Solutions:
// Option 1: Switch your wallet to devnet
// Option 2: Update capabilities.supportedChains to include Solana mainnet

Error Messages

Network validation provides clear, actionable error messages:

  • Network mismatch: Shows which network you're on vs. expected, with specific steps to fix
  • Unsupported networks: Identifies unsupported networks by genesis hash
  • Connection issues: Wraps connection errors with helpful context

When Validation Runs

Network validation occurs:

  • During the first prepare() call - when you first attempt to prepare a transaction
  • Cached after first validation - subsequent operations use the cached result
  • Not during factory method calls
  • Not during manual SolanaAdapter construction

Integration with App Kit

This adapter is designed to work seamlessly across the App Kit ecosystem. Here's an example with the Bridge Kit:

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

const kit = new BridgeKit()

// Solana adapter
const solanaAdapter = createSolanaAdapterFromPrivateKey({
  privateKey: process.env.SOLANA_PRIVATE_KEY!,
})

// EVM adapter
const evmAdapter = createViemAdapterFromPrivateKey({
  privateKey: process.env.EVM_PRIVATE_KEY as `0x${string}`,
})

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

Development

This package is part of the App Kits monorepo.

# Build
nx build @circle-fin/adapter-solana

# Test
nx test @circle-fin/adapter-solana

License

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


Ready to integrate?

Join DiscordVisit our Help-Desk

Built with ❤️ by Circle