Skip to main content

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.

Getting Started

This guide walks you through integrating the Privacy Boost React Native SDK into your app.
For a minimal copy-paste example, see the React Native Quickstart. This guide explains each step in detail.

Prerequisites

Overview

The React Native SDK follows a simple flow:
  1. Initialize - Create SDK instance with configuration
  2. Authenticate - Connect wallet and authenticate via WalletDelegate
  3. Use - Deposit, unshield, transfer tokens

Basic Setup

1. Import the SDK

import { PrivacyBoost } from '@sunnyside-io/privacy-boost-react-native';

2. Configure the SDK

import {
  PrivacyBoost,
  PrivacyBoostConfig,
} from '@sunnyside-io/privacy-boost-react-native';

const config = PrivacyBoostConfig.create({
  serverUrl: 'https://test-api.privacyboost.io',
  chainId: undefined,                   // optional; discovered from server
  shieldContractAddress: undefined,     // optional; discovered from server
  wethContractAddress: '0x4200000000000000000000000000000000000006',
  appId: 'app_abc123xyz',
  teePublicKey: undefined,              // optional; discovered from server
  persistenceStorage: undefined,        // see "Session Persistence" below
  persistenceUnlock: undefined,
});
PrivacyBoostConfig.create() validates the fields and fills in defaults. Only serverUrl and appId are required — chainId, shieldContractAddress, and teePublicKey are automatically discovered from the server if omitted. Passing undefined explicitly is required for nullable fields because the generated types declare them as required-but-nullable.

3. Initialize

try {
  const sdk = new PrivacyBoost(config);
} catch (error) {
  console.error('Failed to initialize SDK:', error);
}

Connecting a Wallet

The SDK uses the WalletDelegate interface to interact with wallets. You must implement this interface:
import type { WalletDelegate } from '@sunnyside-io/privacy-boost-react-native';

const walletDelegate: WalletDelegate = {
  getAddress: async (): Promise<string> => {
    // Return the connected wallet address
    return '0x...';
  },

  getChainId: async (): Promise<bigint> => {
    // Return the current chain ID
    return BigInt(1);
  },

  signMessage: async (message: string): Promise<string> => {
    // Sign using EIP-191 personal_sign
    // Return signature as hex string with 0x prefix
    return '0x...';
  },

  signTypedData: async (typedDataJson: string): Promise<string> => {
    // Sign using EIP-712 eth_signTypedData_v4
    return '0x...';
  },

  sendTransaction: async (
    toAddress: string,
    value: string,
    data: string
  ): Promise<string> => {
    // Submit transaction and return hash
    return '0x...';
  },

  waitForTransactionReceipt: async (txHash: string) => {
    // Poll for the receipt. Return { txHash, status, logs }.
    // `status` is true for success. `logs` is an array of { address, topics, data }.
    return { txHash, status: true, logs: [] };
  },
};
You can use any React Native web3 library (such as ethers.js, viem, or WalletConnect) to implement this delegate. Then authenticate. authenticate() returns a tagged union with four possible outcomes — you must handle each:
import { KeySource } from '@sunnyside-io/privacy-boost-react-native';

try {
  // Default: wallet-derived keys. Also accepts:
  //   new KeySource.Mnemonic('twelve word seed phrase ...')
  //   new KeySource.RawSeed('0xdeadbeef...')
  const result = await sdk.authenticate(
    walletDelegate,
    new KeySource.WalletDerived(),
    undefined // optional TokenProvider for app auth
  );

  switch (result.tag) {
    case 'Authenticated':
      // Returning user.
      console.log('Privacy Address:', result.loginResult.privacyAddress);
      console.log('MPK:', result.loginResult.mpk);
      console.log('Is new user:', result.loginResult.isNewUser);
      break;

    case 'MnemonicGenerated':
      // First-time user — SDK generated a recovery phrase. Show it to the
      // user, have them confirm they've saved it, then continue:
      console.log('Save this phrase:', result.mnemonic);
      const loginResult = await sdk.proceedAfterMnemonic(undefined);
      console.log('Privacy Address:', loginResult.privacyAddress);
      break;

    case 'CredentialRequired':
      // Persistence with PIN/password/biometric/passkey unlock is configured.
      // Prompt the user for their credential, then:
      const credentialResult = await sdk.submitCredential(credential, undefined);
      console.log('Privacy Address:', credentialResult.privacyAddress);
      break;

    case 'RecoveryRequired':
      // Device storage is inconsistent — account exists but local keys are
      // missing. Prompt the user to enter their recovery mnemonic and
      // re-authenticate with KeySource.Mnemonic.
      console.log('Recovery needed:', result.challenge.reason);
      break;
  }
} catch (error) {
  console.error('Authentication failed:', error);
}
CredentialRequired only fires when persistence with PIN/password/biometric unlock is configured (see the persistenceUnlock field on PrivacyBoostConfig). For the simple case (no persistence), authenticate() returns Authenticated (returning user) or MnemonicGenerated (first-time user) directly.

App Authentication (TokenProvider)

If your backend issues app-level auth tokens, implement TokenProvider and pass it to authenticate():
import type {
  TokenProvider,
  TokenResponse,
} from '@sunnyside-io/privacy-boost-react-native';

const tokenProvider: TokenProvider = {
  async getToken(loginPayloadJson: string): Promise<TokenResponse> {
    const resp = await fetch('https://your-backend/auth', {
      method: 'POST',
      body: loginPayloadJson,
    });
    const { token, expiresIn } = await resp.json();
    return { token, expiresIn: BigInt(expiresIn) };
  },
};

await sdk.authenticate(walletDelegate, new KeySource.WalletDerived(), tokenProvider);

Core Operations

Check Balance

try {
  const balance = await sdk.getBalance('0x...');
  console.log('Shielded:', balance.shieldedBalance);
  console.log('Wallet:', balance.walletBalance);
} catch (error) {
  console.error('Failed to get balance:', error);
}

Deposit Tokens

Move tokens from wallet to shielded pool:
try {
  const result = await sdk.shield(
    '0x...', // token address
    '1000000000000000000' // 1 token (18 decimals)
  );
  console.log('Deposit TX:', result.txHash);
  console.log('Commitment:', result.commitment);
  console.log('Request ID:', result.requestId);
  if (result.wrapTxHash) {
    console.log('Wrap TX (ETH→WETH):', result.wrapTxHash);
  }
} catch (error) {
  console.error('Deposit failed:', error);
}
Use requestId with sdk.getShieldStatus(requestId) to poll the shield’s finality state. wrapTxHash is only present when depositing native ETH (the SDK wraps to WETH first).

Withdraw Tokens

Move tokens from shielded pool to wallet:
try {
  const result = await sdk.unshield(
    '0x...', // token address
    '500000000000000000', // amount
    '0x...' // recipient address
  );
  console.log('Withdraw TX:', result.txHash);
} catch (error) {
  console.error('Withdraw failed:', error);
}

Private Transfer

Send tokens privately to another user:
try {
  const result = await sdk.send(
    '0x...', // token address
    '250000000000000000', // amount
    '0x04...' // 194-char privacy address
  );
  console.log('Transfer TX:', result.txHash);
} catch (error) {
  console.error('Transfer failed:', error);
}

Error Handling

The SDK throws SDKError for various failure conditions:
import { SdkError, SdkError_Tags } from '@sunnyside-io/privacy-boost-react-native';

try {
  const result = await sdk.shield(tokenAddress, amount);
} catch (error) {
  // SdkError is a tagged union. Each variant is a class you can instanceof-check.
  if (SdkError.NotConnected.instanceOf(error)) {
    console.log('Wallet not connected');
  } else if (SdkError.NotAuthenticated.instanceOf(error)) {
    console.log('Not logged in');
  } else if (SdkError.InsufficientBalance.instanceOf(error)) {
    console.log('Insufficient balance');
  } else if (SdkError.InvalidAmount.instanceOf(error)) {
    console.log('Invalid amount format');
  } else if (SdkError.WalletError.instanceOf(error)) {
    console.log('Wallet error:', (error as any).inner?.message);
  } else if (SdkError.NetworkError.instanceOf(error)) {
    console.log('Network error:', (error as any).inner?.message);
  } else {
    console.log('Unknown error:', error);
  }
}

Session Persistence

Save and restore sessions to avoid re-signing:
import AsyncStorage from '@react-native-async-storage/async-storage';

// Export session
const session = sdk.exportSession();
if (session) {
  await AsyncStorage.setItem('privacy_session', JSON.stringify(session));
}

// Import session
const stored = await AsyncStorage.getItem('privacy_session');
if (stored) {
  try {
    const session = JSON.parse(stored);
    const success = sdk.importSession(session);
    if (success) {
      console.log('Session restored');
    }
  } catch (error) {
    console.log('Session expired or invalid');
  }
}

Next Steps

Deposits

Deposit tokens into the shielded pool

Withdrawals

Withdraw tokens from the shielded pool

Private Transfers

Send tokens privately between users

Balances

Query and display token balances

Transaction History

View and filter transaction history

Wallet Integration

Connect wallets and authenticate

Session Storage

Secure persistence with AsyncStorage and Keychain

Error Handling

Error codes and recovery patterns