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.

Private Transfers

This guide covers sending tokens privately between privacy addresses.

Basic Transfer

try {
    val result = withContext(Dispatchers.IO) {
        sdk.send(
            tokenAddress = "0x...token-address",
            amount = "1000000000000000000", // 1 token
            recipientPrivacyAddress = "0x04..." // 194-char privacy address
        )
    }
    println("Transaction hash: ${result.txHash}")
} catch (e: SDKError) {
    println("Transfer failed: $e")
}

Transfer Parameters

ParameterTypeRequiredDescription
tokenAddressStringYesToken contract address
amountStringYesAmount in wei (smallest unit)
recipientPrivacyAddressStringYesRecipient’s 194-character privacy address

Transfer Result

data class TransferResult(
    val txHash: String,  // Transaction hash
    val fee: String      // Fee paid (in wei)
)

Privacy Address Validation

Always validate privacy addresses before sending:
val recipientAddress = "0x04..."

require(sdk.isValidPrivacyAddress(recipientAddress)) { "Invalid privacy address" }

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

Looking Up Privacy Addresses

If you have a user’s MPK or Ethereum address, you can look up their privacy address:
val identity = withContext(Dispatchers.IO) {
    sdk.resolveIdentity(identifier = "0x...ethereum-address")
}
println("Privacy address: ${identity.privacyAddress}")

// Now send to their privacy address
val result = withContext(Dispatchers.IO) {
    sdk.send(
        tokenAddress = "0x...",
        amount = "1000000000000000000",
        recipientPrivacyAddress = identity.privacyAddress
    )
}

Checking Balance Before Transfer

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

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

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

    return withContext(Dispatchers.IO) {
        sdk.send(
            tokenAddress = tokenAddress,
            amount = amount,
            recipientPrivacyAddress = recipient
        )
    }
}

Transfer Privacy

Private transfers provide:
  • Sender Privacy: Your wallet address is not linked to the transaction
  • Recipient Privacy: The recipient’s wallet address is not revealed
  • Amount Privacy: The transfer amount is encrypted
  • Relationship Privacy: No public link between sender and recipient
Only the sender and recipient can see the transfer details.

Error Handling

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

Best Practices

1. Validate All Inputs

fun validateTransfer(
    tokenAddress: String,
    amount: String,
    recipient: String
) {
    require(sdk.isValidAddress(tokenAddress)) { "Invalid token address" }
    require(sdk.isValidPrivacyAddress(recipient)) { "Invalid privacy address" }

    val value = amount.toBigIntegerOrNull()
    requireNotNull(value) { "Invalid amount format" }
    require(value > BigInteger.ZERO) { "Amount must be positive" }
}

2. Confirm Large Transfers

suspend fun transferWithConfirmation(
    tokenAddress: String,
    amount: String,
    recipient: String,
    onConfirm: suspend (String) -> Boolean
): TransferResult {
    val formatted = sdk.formatAmount(amount, decimals = 18u)

    val confirmed = onConfirm(
        "Send $formatted tokens to ${recipient.take(10)}...?"
    )
    require(confirmed) { "Transfer cancelled by user" }

    return withContext(Dispatchers.IO) {
        sdk.send(
            tokenAddress = tokenAddress,
            amount = amount,
            recipientPrivacyAddress = recipient
        )
    }
}

3. Handle Network Delays

suspend fun transferWithRetry(
    tokenAddress: String,
    amount: String,
    recipient: String,
    maxRetries: Int = 3
): TransferResult {
    for (attempt in 1..maxRetries) {
        try {
            return withContext(Dispatchers.IO) {
                sdk.send(
                    tokenAddress = tokenAddress,
                    amount = amount,
                    recipientPrivacyAddress = recipient
                )
            }
        } catch (e: SDKError.NetworkError) {
            if (attempt == maxRetries) throw e
            delay(2000L * attempt)
        }
    }
    throw SDKError.NetworkError("Max retries exceeded")
}

Next Steps