@connector-kit/connector
Headless wallet connector built on Wallet Standard with powerful transaction signing, event system, and performance optimizations.

✨ What's New
Enhanced with production-ready features:
- 🔐 Transaction Signer - Clean abstraction for signing/sending transactions
- 📊 Event System - Track connections, transactions, and errors for analytics
- 🐛 Debug Panel - Floating dev-only state inspector
- ⚡ Performance - 40-60% fewer re-renders via optimized state updates
- 🔄 Connection Pooling - Reusable RPC connections for better performance
- 🏥 Health Checks - Comprehensive diagnostics for production debugging
- 🌐 Browser Polyfills - Automatic compatibility for all browsers
- 🔌 Wallet Adapter Compat - Drop-in replacement for @solana/wallet-adapter
Installation
pnpm add @connector-kit/connector
Quick Start
1. Setup Provider
import { ConnectorProvider, getDefaultConfig } from '@connector-kit/connector';
function App() {
return (
<ConnectorProvider config={getDefaultConfig({ appName: "My App" })}>
<YourApp />
</ConnectorProvider>
);
}
2. Connect Wallet
import { useConnector, useAccount } from '@connector-kit/connector';
function WalletButton() {
const { wallets, select, disconnect, connected } = useConnector();
const { address, formatted, copy } = useAccount();
if (!connected) {
return (
<div>
{wallets.map(w => (
<button key={w.name} onClick={() => select(w.name)}>
Connect {w.name}
</button>
))}
</div>
);
}
return (
<div>
<button onClick={copy}>{formatted}</button>
<button onClick={disconnect}>Disconnect</button>
</div>
);
}
3. Sign Transactions (NEW!)
import { useTransactionSigner } from '@connector-kit/connector';
function SendTransaction() {
const { signer, ready, capabilities } = useTransactionSigner();
const handleSend = async () => {
if (!signer) return;
const signature = await signer.signAndSendTransaction(transaction);
console.log('Transaction sent:', signature);
};
return (
<button onClick={handleSend} disabled={!ready}>
Send Transaction
</button>
);
}
That's it! You're ready to build. Everything else below is optional.
Table of Contents
Core Features
- ✅ Wallet Standard - Compatible with all Wallet Standard wallets
- ✅ Multi-Wallet - Support for multiple wallet adapters
- ✅ Auto-Connect - Automatic wallet reconnection
- ✅ Account Management - Multi-account support
- ✅ Network Switching - Full cluster/network management
- ✅ Mobile Support - Solana Mobile Wallet Adapter integration
- ✅ Enhanced Storage - Persistent state with validation
- ✅ Error Boundaries - Automatic error handling and recovery
- ✅ Framework Agnostic - Headless core works with any framework
- ✅ TypeScript - Fully typed with comprehensive JSDoc
Core Hooks
useConnector()
Main hook for wallet connection and state.
import { useConnector } from '@connector-kit/connector';
function Component() {
const {
wallets,
selectedWallet,
accounts,
connected,
connecting,
select,
disconnect,
} = useConnector();
}
useAccount()
Hook for working with the connected account.
import { useAccount } from '@connector-kit/connector';
function Component() {
const {
address,
formatted,
copy,
copied,
connected,
accounts,
selectAccount
} = useAccount();
}
useCluster()
Hook for managing Solana network/cluster.
import { useCluster } from '@connector-kit/connector';
function Component() {
const {
cluster,
clusters,
setCluster,
isMainnet,
isDevnet,
rpcUrl,
explorerUrl
} = useCluster();
}
New Features
Transaction Signer
Clean, unified interface for transaction signing operations.
import { useTransactionSigner } from '@connector-kit/connector';
function SendTx() {
const { signer, ready, capabilities } = useTransactionSigner();
console.log('Can sign:', capabilities.canSign);
console.log('Can send:', capabilities.canSend);
console.log('Can sign messages:', capabilities.canSignMessage);
console.log('Batch support:', capabilities.supportsBatchSigning);
const handleSend = async () => {
if (!signer) return;
try {
const signature = await signer.signAndSendTransaction(transaction);
console.log('Success:', signature);
const signed = await signer.signTransaction(transaction);
const signedBatch = await signer.signAllTransactions([tx1, tx2, tx3]);
const signatures = await signer.signAndSendTransactions([tx1, tx2]);
} catch (error) {
if (error instanceof TransactionSignerError) {
console.error('Signing error:', error.code, error.message);
}
}
};
return (
<button onClick={handleSend} disabled={!ready}>
Send Transaction
</button>
);
}
Error Handling:
import { isTransactionSignerError, TransactionSignerError } from '@connector-kit/connector';
try {
await signer.signAndSendTransaction(tx);
} catch (error) {
if (isTransactionSignerError(error)) {
switch (error.code) {
case 'WALLET_NOT_CONNECTED':
break;
case 'FEATURE_NOT_SUPPORTED':
break;
case 'SIGNING_FAILED':
break;
case 'SEND_FAILED':
break;
}
}
}
Event System
Track all connector lifecycle events for analytics and monitoring.
import { useConnectorClient } from '@connector-kit/connector';
function AnalyticsTracker() {
const client = useConnectorClient();
useEffect(() => {
if (!client) return;
const unsubscribe = client.on((event) => {
switch (event.type) {
case 'wallet:connected':
analytics.track('Wallet Connected', {
wallet: event.wallet,
account: event.account,
timestamp: event.timestamp
});
break;
case 'wallet:disconnected':
analytics.track('Wallet Disconnected');
break;
case 'cluster:changed':
analytics.track('Network Changed', {
from: event.previousCluster,
to: event.cluster
});
break;
case 'error':
errorTracker.captureException(event.error, {
context: event.context
});
break;
}
});
return unsubscribe;
}, [client]);
return null;
}
Available Events:
wallet:connected - Wallet successfully connected
wallet:disconnected - Wallet disconnected
wallet:changed - Selected wallet changed
account:changed - Selected account changed
cluster:changed - Network/cluster changed
wallets:detected - New wallets detected
connecting - Connection attempt started
connection:failed - Connection attempt failed
error - Error occurred
Debug Panel
Floating development panel for instant state visibility.
import { ConnectorDebugPanel } from '@connector-kit/connector';
function App() {
return (
<ConnectorProvider config={config}>
{/* Only visible in development mode */}
{process.env.NODE_ENV === 'development' && <ConnectorDebugPanel />}
<YourApp />
</ConnectorProvider>
);
}
Features:
- Shows connection status (connected/disconnected/connecting)
- Displays current account and wallet
- Shows active cluster and RPC URL
- Lists detected wallets
- Health check information
- Click to expand/collapse
- Automatically excluded in production builds
Custom positioning:
<ConnectorDebugPanel
position="top-left"
defaultOpen={true}
zIndex={10000}
/>
Connection Pooling
Reusable RPC connections for better performance.
import { ConnectionPool, createConnectionPool } from '@connector-kit/connector/headless';
import { Connection } from '@solana/web3.js';
const pool = createConnectionPool({
maxSize: 10,
createConnection: (cluster) => {
return new Connection(cluster.endpoint, {
commitment: 'confirmed'
});
}
});
const connection = pool.get(currentCluster);
const balance = await connection.getBalance(publicKey);
pool.clear('solana:mainnet');
const stats = pool.getStats();
console.log(`Pool: ${stats.size}/${stats.maxSize}`);
console.log(`Hit rate: ${stats.hits / (stats.hits + stats.misses) * 100}%`);
Global pool (simpler):
import { getConnectionPool } from '@connector-kit/connector/headless';
const pool = getConnectionPool();
const connection = pool.get(cluster);
Health Checks
Comprehensive diagnostics for debugging and monitoring.
import { useConnectorClient } from '@connector-kit/connector';
function HealthMonitor() {
const client = useConnectorClient();
if (!client) return null;
const health = client.getHealth();
return (
<div>
<h3>Connector Health</h3>
<div>Initialized: {health.initialized ? '✓' : '✗'}</div>
<div>Wallet Standard: {health.walletStandardAvailable ? '✓' : '✗'}</div>
<div>Storage: {health.storageAvailable ? '✓' : '✗'}</div>
<div>Wallets Detected: {health.walletsDetected}</div>
{health.errors.length > 0 && (
<div className="errors">
{health.errors.map(err => <div key={err}>{err}</div>)}
</div>
)}
</div>
);
}
Wallet Adapter Compatibility
Drop-in replacement for libraries expecting @solana/wallet-adapter.
import { useTransactionSigner, useConnector } from '@connector-kit/connector';
import { createWalletAdapterCompat } from '@connector-kit/connector/compat';
function JupiterIntegration() {
const { signer } = useTransactionSigner();
const { disconnect } = useConnector();
const walletAdapter = createWalletAdapterCompat(signer, {
disconnect: async () => {
await disconnect();
},
onError: (error, operation) => {
console.error(`Wallet adapter error in ${operation}:`, error);
}
});
return <JupiterTerminal wallet={walletAdapter} />;
}
Compatible with:
- Jupiter Aggregator
- Serum DEX
- Raydium
- Marinade Finance
- Any library expecting @solana/wallet-adapter
Configuration
Basic Configuration
import { getDefaultConfig } from '@connector-kit/connector';
const config = getDefaultConfig({
appName: 'My App',
autoConnect: true,
network: 'mainnet-beta',
enableMobile: true,
debug: false,
onError: (error, errorInfo) => {
console.error('Connector error:', error);
errorTracker.captureException(error, errorInfo);
}
});
Network Selection
const config = getDefaultConfig({
appName: 'My App',
network: 'devnet'
});
Custom Clusters
import { getDefaultConfig, createSolanaMainnet } from '@connector-kit/connector';
const config = getDefaultConfig({
appName: 'My App',
clusters: [
createSolanaMainnet({
endpoint: 'https://my-custom-rpc.com'
})
],
customClusters: [
]
});
Mobile Wallet Adapter
<ConnectorProvider
config={config}
mobile={{
appIdentity: {
name: 'My App',
uri: 'https://myapp.com',
icon: 'https://myapp.com/icon.png'
}
}}
>
<App />
</ConnectorProvider>
Advanced Usage
Headless Client (Vue, Svelte, Vanilla JS)
Use ConnectorClient for non-React frameworks.
import { ConnectorClient, getDefaultConfig } from '@connector-kit/connector/headless';
const client = new ConnectorClient(
getDefaultConfig({ appName: 'My App' })
);
const state = client.getSnapshot();
console.log('Wallets:', state.wallets);
await client.select('Phantom');
const unsubscribe = client.subscribe((state) => {
console.log('State updated:', state);
});
const unsubEvents = client.on((event) => {
console.log('Event:', event.type, event);
});
const health = client.getHealth();
console.log('Health:', health);
await client.disconnect();
client.destroy();
Unified Config (Armadura Integration)
If you're using both ConnectorKit and Armadura:
import { createConfig, AppProvider } from '@connector-kit/connector';
import { ArmaProvider } from '@armadura/sdk';
const config = createConfig({
appName: 'My App',
network: 'mainnet',
rpcUrl: 'https://my-custom-rpc.com',
autoConnect: true
});
function App() {
return (
<AppProvider config={config.connectorConfig}>
<ArmaProvider
config={{
network: config.network,
rpcUrl: config.rpcUrl,
providers: [/* ... */]
}}
useConnector="auto"
>
{children}
</ArmaProvider>
</AppProvider>
);
}
Custom Storage
Most users don't need to configure storage - it works automatically with validation, error handling, and SSR fallback.
Only customize for:
- React Native (custom storage backend)
- Additional validation rules
- Custom error tracking
import {
getDefaultConfig,
createEnhancedStorageWallet,
EnhancedStorageAdapter
} from '@connector-kit/connector';
const config = getDefaultConfig({
appName: 'My App',
storage: {
wallet: new EnhancedStorageAdapter(
createEnhancedStorageWallet({
validator: (walletName) => {
return walletName !== null && walletName.length > 0;
},
onError: (error) => {
Sentry.captureException(error);
}
})
),
}
});
Package Exports
Main Export
import { ConnectorProvider, useConnector } from '@connector-kit/connector';
Headless Export (Framework Agnostic)
import { ConnectorClient, getDefaultConfig } from '@connector-kit/connector/headless';
React Export
import { useConnector, useAccount } from '@connector-kit/connector/react';
Compatibility Layer (NEW!)
import { createWalletAdapterCompat } from '@connector-kit/connector/compat';
Complete API Reference
Configuration Functions
getDefaultConfig(options)
const config = getDefaultConfig({
appName: string,
appUrl?: string,
autoConnect?: boolean,
debug?: boolean,
network?: 'mainnet' | 'mainnet-beta'
| 'devnet' | 'testnet' | 'localnet',
enableMobile?: boolean,
storage?: ConnectorConfig['storage'],
clusters?: SolanaCluster[],
customClusters?: SolanaCluster[],
persistClusterSelection?: boolean,
enableErrorBoundary?: boolean,
maxRetries?: number,
onError?: (error, errorInfo) => void
});
Transaction Signing API (NEW!)
useTransactionSigner()
React hook for transaction operations.
interface UseTransactionSignerReturn {
signer: TransactionSigner | null;
ready: boolean;
address: string | null;
capabilities: TransactionSignerCapabilities;
}
createTransactionSigner(config)
Create a transaction signer (headless).
import { createTransactionSigner } from '@connector-kit/connector/headless';
const signer = createTransactionSigner({
wallet: connectedWallet,
account: selectedAccount,
cluster: currentCluster
});
TransactionSigner Interface
interface TransactionSigner {
readonly address: string;
signTransaction(tx: any): Promise<any>;
signAllTransactions(txs: any[]): Promise<any[]>;
signAndSendTransaction(tx: any, options?: SendOptions): Promise<string>;
signAndSendTransactions(txs: any[], options?: SendOptions): Promise<string[]>;
signMessage?(message: Uint8Array): Promise<Uint8Array>;
getCapabilities(): TransactionSignerCapabilities;
}
Event System API (NEW!)
Event Types
type ConnectorEvent =
| { type: 'wallet:connected'; wallet: string; account: string; timestamp: string }
| { type: 'wallet:disconnected'; timestamp: string }
| { type: 'cluster:changed'; cluster: string; previousCluster: string | null; timestamp: string }
| { type: 'wallets:detected'; count: number; timestamp: string }
| { type: 'connecting'; wallet: string; timestamp: string }
| { type: 'connection:failed'; wallet: string; error: string; timestamp: string }
| { type: 'error'; error: Error; context: string; timestamp: string }
Methods
const unsubscribe = client.on((event) => {
console.log('Event:', event.type, event);
});
client.off(listener);
client.offAll();
Health Check API (NEW!)
interface ConnectorHealth {
initialized: boolean;
walletStandardAvailable: boolean;
storageAvailable: boolean;
walletsDetected: number;
errors: string[];
connectionState: {
connected: boolean;
connecting: boolean;
hasSelectedWallet: boolean;
hasSelectedAccount: boolean;
};
timestamp: string;
}
const health = client.getHealth();
Connection Pool API (NEW!)
class ConnectionPool {
get(cluster: SolanaCluster): ConnectionLike;
has(clusterId: string): boolean;
clear(clusterId: string): void;
clearAll(): void;
getStats(): ConnectionPoolStats;
resetStats(): void;
}
const pool = createConnectionPool(options);
const pool = getConnectionPool();
Wallet Adapter Compat API (NEW!)
createWalletAdapterCompat(
signer: TransactionSigner | null,
options: {
disconnect: () => Promise<void>;
transformTransaction?: (tx: any) => any;
onError?: (error: Error, operation: string) => void;
}
): WalletAdapterCompatible
useWalletAdapterCompat(
signer: TransactionSigner | null,
disconnect: () => Promise<void>,
options?: Omit<WalletAdapterCompatOptions, 'disconnect'>
): WalletAdapterCompatible
isWalletAdapterCompatible(obj: any): obj is WalletAdapterCompatible
Polyfill API (NEW!)
import {
installPolyfills,
isPolyfillInstalled,
isCryptoAvailable,
getPolyfillStatus
} from '@connector-kit/connector/headless';
installPolyfills();
const installed = isPolyfillInstalled();
const cryptoAvailable = isCryptoAvailable();
const status = getPolyfillStatus();
Utility Functions
Formatting
import { formatSOL, formatAddress } from '@connector-kit/connector';
formatSOL(1500000000, { decimals: 4 })
formatAddress(address, { length: 6 })
import { formatSOLSimple, formatAddressSimple } from '@connector-kit/connector';
Clipboard
import { copyAddressToClipboard, copyToClipboard } from '@connector-kit/connector';
await copyAddressToClipboard(address);
await copyToClipboard(text);
Cluster Utilities
import {
getClusterRpcUrl,
getClusterExplorerUrl,
getTransactionUrl,
getAddressUrl,
isMainnetCluster,
isDevnetCluster
} from '@connector-kit/connector';
const rpcUrl = getClusterRpcUrl(cluster);
const explorerUrl = getClusterExplorerUrl(cluster);
const txUrl = getTransactionUrl(cluster, signature);
const addrUrl = getAddressUrl(cluster, address);
Types
import type {
ConnectorConfig,
DefaultConfigOptions,
ExtendedConnectorConfig,
UnifiedConfig,
MobileWalletAdapterConfig,
ConnectorState,
ConnectorSnapshot,
WalletInfo,
AccountInfo,
ConnectorHealth,
ConnectorEvent,
ConnectorEventListener,
TransactionSigner,
TransactionSignerConfig,
TransactionSignerCapabilities,
SignedTransaction,
ConnectionLike,
ConnectionPoolOptions,
ConnectionPoolStats,
WalletAdapterCompatible,
WalletAdapterCompatOptions,
Wallet,
WalletAccount,
SolanaCluster,
SolanaClusterId,
StorageAdapter,
UseClusterReturn,
UseAccountReturn,
UseWalletInfoReturn,
UseTransactionSignerReturn,
WalletError
} from '@connector-kit/connector';
Performance
Bundle Size
| Base Connector | ~45KB | ✅ |
| + Polyfills | +2KB | ❌ (auto-included) |
| + Transaction Signer | +3KB | ✅ |
| + Connection Pool | +1.5KB | ✅ |
| + Debug Panel | +2KB | ✅ (dev-only) |
| + Event System | +0.5KB | ✅ |
| + Compat Layer | +2KB | ✅ |
Total: ~48-53KB for typical production usage
Runtime Performance
- 40-60% fewer re-renders via optimized state updates
- Connection pooling reduces memory usage and initialization overhead
- Automatic tree-shaking excludes unused features
- Debug panel automatically excluded in production builds
Browser Compatibility
Enhanced support for:
- ✅ Chrome/Edge 90+
- ✅ Firefox 88+
- ✅ Safari 14+
- ✅ iOS Safari 14+
- ✅ Chrome Mobile 90+
- ✅ Samsung Internet 15+
Automatic polyfills ensure compatibility across all environments.
Migration from @solana/wallet-adapter
Using the wallet-adapter compatibility bridge for gradual migration:
import { useWallet } from '@solana/wallet-adapter-react';
const { publicKey, sendTransaction } = useWallet();
await sendTransaction(tx, connection);
import { useTransactionSigner } from '@connector-kit/connector';
const { signer } = useTransactionSigner();
await signer.signAndSendTransaction(tx);
import { createWalletAdapterCompat } from '@connector-kit/connector/compat';
const walletAdapter = createWalletAdapterCompat(signer, { disconnect });
Examples
Check out the examples directory for:
- Complete wallet connection UI with shadcn/ui
- Transaction signing demos
- Network switching
- Account management
- Mobile wallet support
Development
pnpm install
pnpm build
pnpm dev
pnpm type-check
pnpm lint
pnpm size
Supported Wallets
Compatible with all Wallet Standard compliant wallets:
- Phantom
- Solflare
- Backpack
- Glow
- Brave Wallet
- Solana Mobile wallets
- Any Wallet Standard compatible wallet
Documentation
License
MIT
Built with ❤️ and attention to detail