Skip to main content

Wallet Adapters

This guide covers the wallet adapters available in the Privacy Boost SDK.

Overview

Wallet adapters provide a standard interface for connecting different wallets to the SDK. The SDK includes several built-in adapters and supports custom implementations.

WalletAdapter Interface

All adapters implement this interface:
interface WalletAdapter {
  connect(): Promise<{ address: string; chainId: number }>;
  disconnect(): Promise<void>;
  signMessage(message: string): Promise<string>;
  signTypedData(typedData: string): Promise<string>;
  sendTransaction(tx: TransactionRequest): Promise<string>;
  getAddress(): Promise<string>;
  getChainId(): Promise<number>;
}

Built-in Adapters

EIP-1193 Adapter

For MetaMask and other EIP-1193 compatible wallets:
import { Eip1193WalletAdapter } from '@testinprod-io/privacy-boost';

// Using window.ethereum
const adapter = new Eip1193WalletAdapter(window.ethereum);
await sdk.auth.connect(adapter);

// Using a specific provider
const provider = getProvider(); // Your EIP-1193 provider
const adapter = new Eip1193WalletAdapter(provider);

WalletConnect Adapter

For WalletConnect v2, use the Wallet Registry:
import { createWalletRegistry } from '@testinprod-io/privacy-boost';

const registry = createWalletRegistry({
  chains: [1, 137, 42161],
  walletConnect: {
    projectId: 'your-walletconnect-project-id',
    metadata: {
      name: 'Your App',
      description: 'Your app description',
      url: 'https://yourapp.com',
      icons: ['https://yourapp.com/icon.png'],
    },
  },
});

const adapter = registry.getAdapter('walletconnect');
await sdk.auth.connect(adapter);

Coinbase Wallet Adapter

For Coinbase Wallet, use the Wallet Registry:
import { createWalletRegistry } from '@testinprod-io/privacy-boost';

const registry = createWalletRegistry({
  chains: [1],
  coinbase: {
    appName: 'Your App',
    jsonRpcUrl: 'https://mainnet.infura.io/v3/...',
  },
});

const adapter = registry.getAdapter('coinbase');
await sdk.auth.connect(adapter);

Private Key Adapter (Testing Only)

For development and testing:
import { PrivateKeyWalletAdapter } from '@testinprod-io/privacy-boost';

// WARNING: Never use in production!
const adapter = new PrivateKeyWalletAdapter({
  privateKey: '0x...',
  chainId: 1,
  rpcUrl: 'https://mainnet.infura.io/v3/...',
});

await sdk.auth.connect(adapter);

Wallet Registry

Manage multiple wallet options:
import { createWalletRegistry } from '@testinprod-io/privacy-boost';

const registry = createWalletRegistry({
  chains: [1, 137],
  walletConnect: {
    projectId: 'your-project-id',
  },
  coinbase: {
    appName: 'Your App',
    jsonRpcUrl: 'https://mainnet.infura.io/v3/...',
  },
});

// Get available wallets
const wallets = registry.getAvailableWallets();
// Returns: ['injected', 'walletconnect', 'coinbase']

// Get adapter for specific wallet
const adapter = registry.getAdapter('walletconnect');
await sdk.auth.connect(adapter);

Detecting Available Wallets

function getAvailableWallets(): string[] {
  const wallets: string[] = [];

  if (typeof window.ethereum !== 'undefined') {
    wallets.push('metamask');

    // Check for specific wallets
    if (window.ethereum.isMetaMask) wallets.push('metamask');
    if (window.ethereum.isCoinbaseWallet) wallets.push('coinbase');
    if (window.ethereum.isBraveWallet) wallets.push('brave');
  }

  // WalletConnect is always available
  wallets.push('walletconnect');

  return wallets;
}

Custom Wallet Adapter

Create your own adapter for unsupported wallets:
import { WalletAdapter, TransactionRequest } from '@testinprod-io/privacy-boost';

class MyCustomWalletAdapter implements WalletAdapter {
  private wallet: MyWalletSDK;

  constructor(wallet: MyWalletSDK) {
    this.wallet = wallet;
  }

  async connect(): Promise<{ address: string; chainId: number }> {
    await this.wallet.connect();
    return {
      address: await this.wallet.getAddress(),
      chainId: await this.wallet.getChainId(),
    };
  }

  async disconnect(): Promise<void> {
    await this.wallet.disconnect();
  }

  async signMessage(message: string): Promise<string> {
    return await this.wallet.signPersonalMessage(message);
  }

  async signTypedData(typedData: string): Promise<string> {
    const data = JSON.parse(typedData);
    return await this.wallet.signTypedDataV4(data);
  }

  async sendTransaction(tx: TransactionRequest): Promise<string> {
    const txHash = await this.wallet.sendTransaction({
      to: tx.to,
      value: tx.value,
      data: tx.data,
      gasLimit: tx.gasLimit,
    });
    return txHash;
  }

  async getAddress(): Promise<string> {
    return await this.wallet.getAddress();
  }

  async getChainId(): Promise<number> {
    return await this.wallet.getChainId();
  }
}

// Usage
const adapter = new MyCustomWalletAdapter(myWallet);
await sdk.auth.connect(adapter);

UI Example: Wallet Selection

function WalletSelector({ onConnect }: { onConnect: () => void }) {
  const [connecting, setConnecting] = useState(false);

  const connectWallet = async (type: string) => {
    setConnecting(true);
    try {
      let adapter: WalletAdapter;

      const registry = createWalletRegistry({
        chains: [1],
        walletConnect: { projectId: 'your-project-id' },
        coinbase: { appName: 'Your App', jsonRpcUrl: 'https://...' },
      });

      switch (type) {
        case 'metamask':
          adapter = new Eip1193WalletAdapter(window.ethereum);
          break;
        case 'walletconnect':
          adapter = registry.getAdapter('walletconnect');
          break;
        case 'coinbase':
          adapter = registry.getAdapter('coinbase');
          break;
        default:
          throw new Error('Unknown wallet type');
      }

      await sdk.auth.connect(adapter);
      await sdk.auth.login();
      onConnect();
    } catch (error) {
      console.error('Connection failed:', error);
    } finally {
      setConnecting(false);
    }
  };

  return (
    <div>
      <h2>Connect Wallet</h2>
      <button
        onClick={() => connectWallet('metamask')}
        disabled={connecting || !window.ethereum}
      >
        MetaMask
      </button>
      <button
        onClick={() => connectWallet('walletconnect')}
        disabled={connecting}
      >
        WalletConnect
      </button>
      <button
        onClick={() => connectWallet('coinbase')}
        disabled={connecting}
      >
        Coinbase Wallet
      </button>
    </div>
  );
}

Handling Connection Events

// MetaMask account change
window.ethereum?.on('accountsChanged', (accounts: string[]) => {
  if (accounts.length === 0) {
    console.log('Disconnected');
    sdk.auth.disconnect();
  } else {
    console.log('Account changed to:', accounts[0]);
    // Reconnect with new account
  }
});

// MetaMask chain change
window.ethereum?.on('chainChanged', (chainId: string) => {
  console.log('Chain changed to:', parseInt(chainId, 16));
  // Reload or reconnect
});

// MetaMask disconnect
window.ethereum?.on('disconnect', (error: { code: number; message: string }) => {
  console.log('Wallet disconnected:', error.message);
  sdk.auth.disconnect();
});

Best Practices

1. Check Wallet Availability

function isWalletAvailable(): boolean {
  return typeof window.ethereum !== 'undefined';
}

function getInjectedWalletName(): string {
  if (!window.ethereum) return 'None';
  if (window.ethereum.isMetaMask) return 'MetaMask';
  if (window.ethereum.isCoinbaseWallet) return 'Coinbase Wallet';
  if (window.ethereum.isBraveWallet) return 'Brave Wallet';
  return 'Unknown Wallet';
}

2. Handle User Rejection

try {
  await sdk.auth.connect(adapter);
} catch (error) {
  if (error.code === 4001) {
    console.log('User rejected connection');
  } else {
    console.log('Connection failed:', error.message);
  }
}

3. Support Multiple Wallets

import {
  Eip1193WalletAdapter,
  createWalletRegistry,
} from '@testinprod-io/privacy-boost';

// Built-in registry includes WalletConnect and Coinbase (lazy-loaded)
const registry = createWalletRegistry();

function getAdapter(walletType: string) {
  if (walletType === 'metamask') {
    return new Eip1193WalletAdapter(window.ethereum);
  }

  // WalletConnect, Coinbase, etc. are resolved from the registry
  const entry = registry.get(walletType);
  if (!entry) throw new Error(`Unknown wallet: ${walletType}`);
  return entry.create({ projectId: '...', appName: '...' });
}

Next Steps