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 to any public Ethereum address.

Basic Withdrawal

try {
    val result = withContext(Dispatchers.IO) {
        sdk.unshield(
            tokenAddress = "0x...token-address",
            amount = "1000000000000000000", // 1 token
            recipient = "0x...recipient-address"
        )
    }
    println("Transaction hash: ${result.txHash}")
} catch (e: SDKError) {
    println("Withdrawal failed: $e")
}

Withdrawal Parameters

ParameterTypeRequiredDescription
tokenAddressStringYesToken contract address
amountStringYesAmount in wei (smallest unit)
recipientStringYesDestination Ethereum address

Withdrawal Steps

Withdrawals involve zero-knowledge proof generation, which can take up to 30 seconds:
StepDescription
PreparingPreparing withdrawal data
SigningSigning transaction data
ProvingGenerating zero-knowledge proof
UnshieldingExecuting withdrawal transaction
UnwrappingUnwrapping WETH to ETH (if applicable)

Withdrawal Result

data class UnshieldResult(
    val txHash: String,  // Main withdrawal transaction hash
    val fee: String      // Fee paid (in wei)
)

Withdraw to Self

Withdraw to your connected wallet address:
val myAddress = sdk.getWalletAddress()!!

val result = withContext(Dispatchers.IO) {
    sdk.unshield(
        tokenAddress = "0x...",
        amount = "1000000000000000000",
        recipient = myAddress
    )
}

Withdraw to Any Address

You can withdraw to any valid Ethereum address:
val recipientAddress = "0x..."

// Validate address first
require(sdk.isValidAddress(recipientAddress)) { "Invalid recipient address" }

val result = withContext(Dispatchers.IO) {
    sdk.unshield(
        tokenAddress = "0x...",
        amount = "1000000000000000000",
        recipient = recipientAddress
    )
}

Partial Withdrawals

Withdraw any amount up to your shielded balance:
// Check available balance first
val balance = withContext(Dispatchers.IO) {
    sdk.getBalance(tokenAddress = tokenAddress)
}

val shielded = balance.shieldedBalance.toBigInteger()
val unshieldAmount = amount.toBigInteger()

require(unshieldAmount <= shielded) { "Insufficient shielded balance" }

val result = withContext(Dispatchers.IO) {
    sdk.unshield(
        tokenAddress = tokenAddress,
        amount = amount,
        recipient = recipientAddress
    )
}

Error Handling

try {
    val result = withContext(Dispatchers.IO) {
        sdk.unshield(
            tokenAddress = tokenAddress,
            amount = amount,
            recipient = recipient
        )
    }
} catch (e: SDKError) {
    when (e) {
        is SDKError.InsufficientBalance ->
            println("Not enough shielded balance")
        is SDKError.InvalidAddress ->
            println("Invalid recipient address")
        is SDKError.InvalidAmount ->
            println("Invalid amount format")
        is SDKError.WalletError ->
            println("Wallet error: ${e.message}")
        is SDKError.NetworkError ->
            println("Network error: ${e.message}")
        else ->
            println("Withdrawal error: $e")
    }
}

Best Practices

1. Validate Balance Before Withdrawal

suspend fun validateWithdrawal(tokenAddress: String, amount: String) {
    val balance = withContext(Dispatchers.IO) {
        sdk.getBalance(tokenAddress = tokenAddress)
    }

    val shielded = balance.shieldedBalance.toBigInteger()
    val requested = amount.toBigInteger()

    require(requested <= shielded) {
        "Insufficient balance. Available: ${balance.shieldedBalance}, Requested: $amount"
    }
}

2. Validate Recipient Address

fun validateRecipient(address: String) {
    require(sdk.isValidAddress(address)) { "Invalid Ethereum address" }
    require(address != "0x0000000000000000000000000000000000000000") {
        "Cannot withdraw to zero address"
    }
}

3. Handle Long Operations

Withdrawals include zero-knowledge proof generation which can take up to 30 seconds. Show a loading indicator and inform users about the expected wait time.
// In a ViewModel
viewModelScope.launch {
    _state.value = WithdrawState.Loading("Generating proof... This may take up to 30 seconds.")

    try {
        val result = withContext(Dispatchers.IO) {
            sdk.unshield(
                tokenAddress = tokenAddress,
                amount = amount,
                recipient = recipient
            )
        }
        _state.value = WithdrawState.Success(result.txHash)
    } catch (e: SDKError) {
        _state.value = WithdrawState.Error(e)
    }
}

Next Steps