Skip to content
✈️ TokenFlight SDK

Custom Wallet Adapter

TokenFlight widgets don’t bundle a wallet — they talk to wallets through an adapter interface. Official adapters exist for Privy, AppKit, Thirdweb, wagmi, and ethers, but you can build your own for any provider.

import { TokenFlightSwap } from '@tokenflight/swap';
import { AppKitWalletAdapter } from '@tokenflight/adapter-appkit';
const swap = new TokenFlightSwap({
container: '#swap',
config: { theme: 'dark' },
walletAdapter: new AppKitWalletAdapter(appkitInstance),
});
swap.initialize();
PackageProvider
@tokenflight/adapter-privyPrivy
@tokenflight/adapter-appkitWalletConnect / AppKit
@tokenflight/adapter-thirdwebThirdweb
@tokenflight/adapter-wagmiwagmi
@tokenflight/adapter-ethersethers

Implement this interface to connect any wallet:

import type {
IWalletAdapter,
WalletAction,
WalletActionResult,
WalletActionType,
WalletEvent,
WalletEventType,
ChainType,
} from '@tokenflight/swap';
class MyAdapter implements IWalletAdapter {
readonly name = 'My Wallet';
readonly icon = 'https://example.com/icon.svg'; // optional
readonly supportedActionTypes: WalletActionType[] = [
'eip1193_request', // EVM JSON-RPC
'solana_signAndSendTransaction', // Solana
];
async connect(chainType?: ChainType): Promise<void> { /* ... */ }
async disconnect(): Promise<void> { /* ... */ }
isConnected(chainType?: ChainType): boolean { /* ... */ }
async getAddress(chainType?: ChainType): Promise<string | null> { /* ... */ }
async executeWalletAction(action: WalletAction): Promise<WalletActionResult> { /* ... */ }
// Optional
async signMessage?(message: string, chainType?: ChainType): Promise<string> { /* ... */ }
on(event: WalletEventType, handler: (e: WalletEvent) => void): void { /* ... */ }
off(event: WalletEventType, handler: (e: WalletEvent) => void): void { /* ... */ }
}
type ChainType = 'evm' | 'solana';

The widget tells the adapter which chain family it needs. If your wallet only supports one chain type, ignore the parameter and throw on unsupported types.

The widget sends transaction requests as typed actions:

// EVM — a standard JSON-RPC request
interface EvmWalletAction {
type: 'eip1193_request';
chainId: number;
method: string; // e.g. 'eth_sendTransaction'
params: unknown[];
}
// Solana — sign a base64-encoded transaction
interface SolanaSignAndSendAction {
type: 'solana_signAndSendTransaction';
transaction: string; // base64
}

Return a standardized result:

interface WalletActionResult {
success: boolean;
data?: unknown; // provider-specific payload
error?: string; // human-readable error
txHash?: string; // if a transaction was sent
}

Emit lifecycle events so the widget reacts to wallet state changes:

type WalletEventType = 'connect' | 'disconnect' | 'chainChanged' | 'accountsChanged';
import type {
IWalletAdapter,
WalletAction,
WalletActionResult,
WalletActionType,
WalletEvent,
WalletEventType,
ChainType,
} from '@tokenflight/swap';
export class EthersAdapter implements IWalletAdapter {
readonly name = 'Ethers.js';
readonly supportedActionTypes: WalletActionType[] = ['eip1193_request'];
private provider: any; // ethers BrowserProvider
private signer: any;
private address: string | null = null;
private listeners = new Map<WalletEventType, Set<(e: WalletEvent) => void>>();
constructor(private ethereum: any) {} // window.ethereum
async connect(): Promise<void> {
const { BrowserProvider } = await import('ethers');
this.provider = new BrowserProvider(this.ethereum);
this.signer = await this.provider.getSigner();
this.address = await this.signer.getAddress();
this.emit('connect', { address: this.address });
}
async disconnect(): Promise<void> {
this.address = null;
this.signer = null;
this.emit('disconnect');
}
isConnected(): boolean {
return this.address !== null;
}
async getAddress(): Promise<string | null> {
return this.address;
}
async executeWalletAction(action: WalletAction): Promise<WalletActionResult> {
if (action.type !== 'eip1193_request') {
return { success: false, error: 'Unsupported action type' };
}
try {
const result = await this.ethereum.request({
method: action.method,
params: action.params,
});
return { success: true, data: result, txHash: typeof result === 'string' ? result : undefined };
} catch (err: any) {
return { success: false, error: err.message };
}
}
on(event: WalletEventType, handler: (e: WalletEvent) => void): void {
if (!this.listeners.has(event)) this.listeners.set(event, new Set());
this.listeners.get(event)!.add(handler);
}
off(event: WalletEventType, handler: (e: WalletEvent) => void): void {
this.listeners.get(event)?.delete(handler);
}
private emit(type: WalletEventType, data?: unknown): void {
for (const h of this.listeners.get(type) ?? []) h({ type, data });
}
}

Usage:

import { TokenFlightSwap } from '@tokenflight/swap';
import { EthersAdapter } from './ethers-adapter';
const swap = new TokenFlightSwap({
container: '#swap',
config: { theme: 'dark' },
walletAdapter: new EthersAdapter(window.ethereum),
});
swap.initialize();