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.

Withdrawals

This guide covers withdrawing tokens from your private balance back to a wallet address using the Privacy Boost React Native SDK.

Basic Withdrawal

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

const result = await sdk.unshield(
  '0x...token-address',
  '500000000000000000', // 0.5 tokens
  '0x...recipient-address'
);
console.log('Withdraw TX:', result.txHash);

Withdrawal Parameters

ParameterTypeRequiredDescription
tokenAddressstringYesERC-20 token contract address
amountstringYesAmount in wei (smallest unit)
recipientstringYesRecipient Ethereum address

Withdrawal Result

interface UnshieldResult {
  txHash: string;  // Transaction hash
  fee: string;     // Fee paid (in wei)
}

Withdraw to Self

The most common case — withdraw to the connected wallet:
const walletAddress = sdk.getWalletAddress();
if (walletAddress) {
  const result = await sdk.unshield(tokenAddress, amount, walletAddress);
  console.log('Withdraw TX:', result.txHash);
}

Complete Example

import React, { useState } from 'react';
import { View, Text, TextInput, Button, ActivityIndicator } from 'react-native';
import { SdkError } from '@sunnyside-io/privacy-boost-react-native';

function WithdrawScreen({ sdk, tokenAddress }: { sdk: PrivacyBoost; tokenAddress: string }) {
  const [amount, setAmount] = useState('');
  const [recipient, setRecipient] = useState('');
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);

  const handleUnshield = async () => {
    const target = recipient || sdk.getWalletAddress();
    if (!target) return;

    setLoading(true);
    setError(null);

    try {
      const weiAmount = sdk.parseAmount(amount, 18);
      const result = await sdk.unshield(tokenAddress, weiAmount, target);
      console.log('Withdraw TX:', result.txHash);
      setAmount('');
    } catch (err: any) {
      if (SdkError.SignatureRejected.instanceOf(err)) return;
      setError(err?.inner?.message ?? err?.message ?? 'Withdrawal failed');
    } finally {
      setLoading(false);
    }
  };

  return (
    <View style={{ padding: 16 }}>
      <TextInput
        value={amount}
        onChangeText={setAmount}
        placeholder="Amount (e.g. 0.5)"
        keyboardType="decimal-pad"
        editable={!loading}
      />
      <TextInput
        value={recipient}
        onChangeText={setRecipient}
        placeholder="Recipient (leave empty for self)"
        editable={!loading}
      />
      {error && <Text style={{ color: 'red' }}>{error}</Text>}
      {loading ? (
        <ActivityIndicator />
      ) : (
        <Button title="Withdraw" onPress={handleUnshield} disabled={!amount} />
      )}
    </View>
  );
}

Error Handling

try {
  const result = await sdk.unshield(tokenAddress, amount, recipient);
} catch (error: any) {
  switch (error.tag) {
    case 'SignatureRejected':
      return;
    case 'InsufficientBalance':
      console.log('Not enough shielded balance');
      break;
    case 'InvalidAddress':
      console.log('Invalid recipient address');
      break;
    case 'NetworkError':
      console.log('Network error:', error.message);
      break;
    default:
      console.log('Withdrawal error:', error);
  }
}

Best Practices

1. Default to Self-Withdrawal

Pre-fill the recipient with the connected wallet address for the most common use case.

2. Refresh Balance After Withdrawal

const result = await sdk.unshield(tokenAddress, amount, recipient);
const updatedBalance = await sdk.getBalance(tokenAddress);
console.log('Remaining shielded:', updatedBalance.shieldedBalance);

3. Show Proof Generation State

Withdrawals require zero-knowledge proof generation which takes time. Always show a loading indicator.

Next Steps