> ## Documentation Index
> Fetch the complete documentation index at: https://docs.privacyboost.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Wallet integration

# Wallet Integration

The wallet adapter is the bridge between the Privacy Boost SDK and your wallet integration. This guide covers implementing adapters for various wallet providers.

## Interface Definition

```typescript theme={null}
interface WalletAdapter {
  /** Get the connected wallet address */
  getAddress(): Promise<string>;

  /** Get the current chain ID */
  getChainId(): Promise<number>;

  /** Sign a personal message (EIP-191) */
  signMessage(message: string): Promise<string>;

  /** Sign EIP-712 typed data */
  signTypedData(typedDataJson: string): Promise<string>;

  /** Send a transaction and return the tx hash */
  sendTransaction(tx: {
    to: string;
    value: string;
    data: string;
  }): Promise<string>;

  /** Wait for a mined receipt and return the fields the SDK consumes */
  waitForTransactionReceipt(txHash: string): Promise<{
    transactionHash: string;
    status: boolean;
    logs: Array<{ address: string; topics: string[]; data: string }>;
  }>;
}
```

## Implementation Requirements

### Return Values

* **Addresses**: Checksummed hex strings with `0x` prefix (42 characters)
* **Signatures**: Hex strings with `0x` prefix (132 characters for 65 bytes: r + s + v)
* **Transaction hashes**: Hex strings with `0x` prefix (66 characters)
* **Receipts**: `transactionHash`, boolean `status`, and logs with `address`, `topics`, and `data`
* **Chain ID**: Number matching expected network

### Error Handling

Throw standard JavaScript errors with descriptive messages:

```typescript theme={null}
throw new Error('User rejected the signing request');
throw new Error('Transaction failed: insufficient funds');
```

## MetaMask / Injected Provider

```typescript theme={null}
function createMetaMaskAdapter(): WalletAdapter {
  if (typeof window.ethereum === 'undefined') {
    throw new Error('MetaMask not installed');
  }

  const provider = window.ethereum;

  return {
    async getAddress(): Promise<string> {
      const accounts = await provider.request({
        method: 'eth_requestAccounts'
      });
      return accounts[0];
    },

    async getChainId(): Promise<number> {
      const chainId = await provider.request({
        method: 'eth_chainId'
      });
      return parseInt(chainId, 16);
    },

    async signMessage(message: string): Promise<string> {
      const address = await this.getAddress();
      return provider.request({
        method: 'personal_sign',
        params: [message, address]
      });
    },

    async signTypedData(typedDataJson: string): Promise<string> {
      const address = await this.getAddress();
      const typedData = JSON.parse(typedDataJson);

      return provider.request({
        method: 'eth_signTypedData_v4',
        params: [address, JSON.stringify(typedData)]
      });
    },

    async sendTransaction(tx: {
      to: string;
      value: string;
      data: string;
    }): Promise<string> {
      const address = await this.getAddress();

      return provider.request({
        method: 'eth_sendTransaction',
        params: [{
          from: address,
          to: tx.to,
          value: '0x' + BigInt(tx.value).toString(16),
          data: tx.data
        }]
      });
    },

    async waitForTransactionReceipt(txHash: string): Promise<{
      transactionHash: string;
      status: boolean;
      logs: Array<{ address: string; topics: string[]; data: string }>;
    }> {
      while (true) {
        const receipt = await provider.request({
          method: 'eth_getTransactionReceipt',
          params: [txHash],
        });

        if (receipt) {
          return {
            transactionHash: receipt.transactionHash ?? txHash,
            status: receipt.status !== '0x0',
            logs: (receipt.logs ?? []).map((log: any) => ({
              address: log.address,
              topics: log.topics ?? [],
              data: log.data ?? '0x',
            })),
          };
        }

        await new Promise((resolve) => setTimeout(resolve, 1000));
      }
    }
  };
}
```

## WalletConnect

```typescript theme={null}
import { EthereumProvider } from '@walletconnect/ethereum-provider';

async function createWalletConnectAdapter(
  projectId: string,
  chainId: number
): Promise<WalletAdapter> {
  const provider = await EthereumProvider.init({
    projectId,
    chains: [chainId],
    showQrModal: true,
  });

  await provider.enable();

  return {
    async getAddress(): Promise<string> {
      const accounts = provider.accounts;
      if (accounts.length === 0) {
        throw new Error('No accounts connected');
      }
      return accounts[0];
    },

    async getChainId(): Promise<number> {
      return provider.chainId;
    },

    async signMessage(message: string): Promise<string> {
      const address = await this.getAddress();
      return provider.request({
        method: 'personal_sign',
        params: [message, address]
      });
    },

    async signTypedData(typedDataJson: string): Promise<string> {
      const address = await this.getAddress();
      return provider.request({
        method: 'eth_signTypedData_v4',
        params: [address, typedDataJson]
      });
    },

    async sendTransaction(tx: {
      to: string;
      value: string;
      data: string;
    }): Promise<string> {
      const address = await this.getAddress();
      return provider.request({
        method: 'eth_sendTransaction',
        params: [{
          from: address,
          to: tx.to,
          value: '0x' + BigInt(tx.value).toString(16),
          data: tx.data
        }]
      });
    },

    async waitForTransactionReceipt(txHash: string): Promise<{
      transactionHash: string;
      status: boolean;
      logs: Array<{ address: string; topics: string[]; data: string }>;
    }> {
      while (true) {
        const receipt = await provider.request({
          method: 'eth_getTransactionReceipt',
          params: [txHash],
        });

        if (receipt) {
          return {
            transactionHash: receipt.transactionHash ?? txHash,
            status: receipt.status !== '0x0',
            logs: (receipt.logs ?? []).map((log: any) => ({
              address: log.address,
              topics: log.topics ?? [],
              data: log.data ?? '0x',
            })),
          };
        }

        await new Promise((resolve) => setTimeout(resolve, 1000));
      }
    }
  };
}
```

## Coinbase Wallet

```typescript theme={null}
import CoinbaseWalletSDK from '@coinbase/wallet-sdk';

function createCoinbaseAdapter(appName: string, rpcUrl: string): WalletAdapter {
  const sdk = new CoinbaseWalletSDK({ appName });
  const provider = sdk.makeWeb3Provider(rpcUrl);

  return {
    async getAddress(): Promise<string> {
      const accounts = await provider.request({
        method: 'eth_requestAccounts'
      });
      return accounts[0];
    },

    async getChainId(): Promise<number> {
      const chainId = await provider.request({
        method: 'eth_chainId'
      });
      return parseInt(chainId as string, 16);
    },

    async signMessage(message: string): Promise<string> {
      const address = await this.getAddress();
      return provider.request({
        method: 'personal_sign',
        params: [message, address]
      }) as Promise<string>;
    },

    async signTypedData(typedDataJson: string): Promise<string> {
      const address = await this.getAddress();
      return provider.request({
        method: 'eth_signTypedData_v4',
        params: [address, typedDataJson]
      }) as Promise<string>;
    },

    async sendTransaction(tx: {
      to: string;
      value: string;
      data: string;
    }): Promise<string> {
      const address = await this.getAddress();
      return provider.request({
        method: 'eth_sendTransaction',
        params: [{
          from: address,
          to: tx.to,
          value: '0x' + BigInt(tx.value).toString(16),
          data: tx.data
        }]
      }) as Promise<string>;
    },

    async waitForTransactionReceipt(txHash: string): Promise<{
      transactionHash: string;
      status: boolean;
      logs: Array<{ address: string; topics: string[]; data: string }>;
    }> {
      while (true) {
        const receipt = await provider.request({
          method: 'eth_getTransactionReceipt',
          params: [txHash],
        });

        if (receipt) {
          return {
            transactionHash: receipt.transactionHash ?? txHash,
            status: receipt.status !== '0x0',
            logs: (receipt.logs ?? []).map((log: any) => ({
              address: log.address,
              topics: log.topics ?? [],
              data: log.data ?? '0x',
            })),
          };
        }

        await new Promise((resolve) => setTimeout(resolve, 1000));
      }
    }
  };
}
```

## Safe (Gnosis Safe)

```typescript theme={null}
import Safe from '@safe-global/protocol-kit';
import SafeApiKit from '@safe-global/api-kit';

async function createSafeAdapter(
  safeAddress: string,
  signerPrivateKey: string,
  rpcUrl: string
): Promise<WalletAdapter> {
  const safe = await Safe.create({
    ethAdapter: /* your eth adapter */,
    safeAddress
  });

  const apiKit = new SafeApiKit({
    chainId: BigInt(await safe.getChainId())
  });

  return {
    async getAddress(): Promise<string> {
      return safeAddress;
    },

    async getChainId(): Promise<number> {
      return Number(await safe.getChainId());
    },

    async signMessage(message: string): Promise<string> {
      const messageHash = await safe.createMessage(message);
      const signedMessage = await safe.signMessage(messageHash);
      // For multi-sig, you may need to propose and collect signatures
      return signedMessage.encodedSignatures();
    },

    async signTypedData(typedDataJson: string): Promise<string> {
      const typedData = JSON.parse(typedDataJson);
      // Safe SDK handles EIP-712 signing
      const signature = await safe.signTypedData(typedData);
      return signature.encodedSignatures();
    },

    async sendTransaction(tx: {
      to: string;
      value: string;
      data: string;
    }): Promise<string> {
      const safeTransaction = await safe.createTransaction({
        transactions: [{
          to: tx.to,
          value: tx.value,
          data: tx.data
        }]
      });

      // Sign and execute (or propose for multi-sig)
      const signedTx = await safe.signTransaction(safeTransaction);
      const response = await safe.executeTransaction(signedTx);

      return response.hash;
    },

    async waitForTransactionReceipt(txHash: string): Promise<{
      transactionHash: string;
      status: boolean;
      logs: Array<{ address: string; topics: string[]; data: string }>;
    }> {
      const receipt = await safe.getEthAdapter().getProvider().waitForTransaction(txHash);
      if (!receipt) {
        throw new Error(`Transaction not found: ${txHash}`);
      }

      return {
        transactionHash: receipt.transactionHash ?? receipt.hash ?? txHash,
        status: receipt.status === 1 || receipt.status === 'success',
        logs: (receipt.logs ?? []).map((log: any) => ({
          address: log.address,
          topics: log.topics ?? [],
          data: log.data ?? '0x',
        })),
      };
    }
  };
}
```

## Testing Adapter

For testing, create a mock adapter:

```typescript theme={null}
import { Wallet } from 'ethers';

function createTestAdapter(privateKey: string): WalletAdapter {
  const wallet = new Wallet(privateKey);

  return {
    async getAddress(): Promise<string> {
      return wallet.address;
    },

    async getChainId(): Promise<number> {
      return 1; // Mainnet
    },

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

    async signTypedData(typedDataJson: string): Promise<string> {
      const typedData = JSON.parse(typedDataJson);
      const { domain, types, message: data } = typedData;
      const { EIP712Domain, ...signingTypes } = types;
      return wallet.signTypedData(domain, signingTypes, data);
    },

    async sendTransaction(tx: {
      to: string;
      value: string;
      data: string;
    }): Promise<string> {
      // Return mock transaction hash
      return '0x' + '0'.repeat(64);
    },

    async waitForTransactionReceipt(txHash: string): Promise<{
      transactionHash: string;
      status: boolean;
      logs: Array<{ address: string; topics: string[]; data: string }>;
    }> {
      return {
        transactionHash: txHash,
        status: true,
        logs: [],
      };
    }
  };
}
```

## Adapter Factory

Create a unified factory for multiple wallet types:

```typescript theme={null}
type WalletType = 'metamask' | 'walletconnect' | 'coinbase';

async function createAdapter(
  type: WalletType,
  options?: { projectId?: string; rpcUrl?: string }
): Promise<WalletAdapter> {
  switch (type) {
    case 'metamask':
      return createMetaMaskAdapter();
    case 'walletconnect':
      if (!options?.projectId) throw new Error('projectId required');
      return createWalletConnectAdapter(options.projectId, 1);
    case 'coinbase':
      if (!options?.rpcUrl) throw new Error('rpcUrl required');
      return createCoinbaseAdapter('My App', options.rpcUrl);
    default:
      throw new Error(`Unknown wallet type: ${type}`);
  }
}
```

## Best Practices

1. **Validate chain ID** - Check that the wallet is on the expected network
2. **Handle rejections** - Users may reject signing requests
3. **Timeout long operations** - Set reasonable timeouts for wallet interactions
4. **Cache address** - Avoid repeated `getAddress()` calls
5. **Handle disconnections** - Watch for wallet disconnect events

```typescript theme={null}
// Example with validation and caching
class ValidatingAdapter implements WalletAdapter {
  private cachedAddress: string | null = null;
  private expectedChainId: number;

  constructor(
    private inner: WalletAdapter,
    expectedChainId: number
  ) {
    this.expectedChainId = expectedChainId;
  }

  async getAddress(): Promise<string> {
    if (!this.cachedAddress) {
      this.cachedAddress = await this.inner.getAddress();
    }
    return this.cachedAddress;
  }

  async getChainId(): Promise<number> {
    const chainId = await this.inner.getChainId();
    if (chainId !== this.expectedChainId) {
      throw new Error(
        `Wrong network: expected ${this.expectedChainId}, got ${chainId}`
      );
    }
    return chainId;
  }

  // ... delegate other methods with timeout wrapper
  async signMessage(message: string): Promise<string> {
    return this.inner.signMessage(message);
  }

  async signTypedData(typedDataJson: string): Promise<string> {
    return this.inner.signTypedData(typedDataJson);
  }

  async sendTransaction(tx: {
    to: string;
    value: string;
    data: string;
  }): Promise<string> {
    return this.inner.sendTransaction(tx);
  }

  async waitForTransactionReceipt(txHash: string): Promise<{
    transactionHash: string;
    status: boolean;
    logs: Array<{ address: string; topics: string[]; data: string }>;
  }> {
    return this.inner.waitForTransactionReceipt(txHash);
  }
}
```

## Next Steps

* [Deposits Guide](./deposits) - Deposit tokens into your private balance
* [Withdrawals Guide](./withdrawals) - Withdraw tokens from your private balance
* [Transfers Guide](./transfers) - Send private transfers
* [API Reference](../api-reference) - Complete API documentation


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.