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.

Balance Management

This guide covers querying and managing token balances in the Privacy Boost Android SDK.

Understanding Balances

Privacy Boost tracks two types of balances:
Balance TypeDescription
ShieldedPrivate balance in the privacy pool — only you can see it
WalletPublic balance in your connected wallet on-chain

Get Single Token Balance

val balance = withContext(Dispatchers.IO) {
    sdk.getBalance(tokenAddress = "0x...")
}

println("Token: ${balance.tokenAddress}")
println("Shielded: ${balance.shieldedBalance}")
println("Wallet: ${balance.walletBalance}")
println("Symbol: ${balance.symbol}")
println("Decimals: ${balance.decimals}")

Get All Balances

val balances = withContext(Dispatchers.IO) {
    sdk.getAllBalances()
}

for (balance in balances) {
    println("${balance.symbol ?: balance.tokenAddress}:")
    println("  Shielded: ${balance.shieldedBalance}")
    println("  Wallet: ${balance.walletBalance}")
}

Balance Type

data class TokenBalance(
    val tokenAddress: String,    // Token contract address
    val shieldedBalance: String, // Private balance in wei
    val walletBalance: String,   // Public balance in wei
    val symbol: String?,         // Token symbol (e.g., "USDC")
    val decimals: UByte          // Token decimals (e.g., 18)
)

Formatting Amounts

Convert between wei strings and human-readable format:
// Parse human-readable to wei
val weiAmount = sdk.parseAmount("1.5", decimals = 18u)
// Returns: "1500000000000000000"

// Format wei to human-readable
val humanAmount = sdk.formatAmount("1500000000000000000", decimals = 18u)
// Returns: "1.5"

// Format a balance for display
val balance = withContext(Dispatchers.IO) {
    sdk.getBalance(tokenAddress = tokenAddress)
}
val formatted = sdk.formatAmount(balance.shieldedBalance, decimals = balance.decimals)
println("Shielded: $formatted ${balance.symbol ?: ""}")

Checking Sufficient Balance

Before performing operations, verify the user has enough balance:
suspend fun hasSufficientBalance(
    tokenAddress: String,
    requiredAmount: String
): Boolean {
    val balance = withContext(Dispatchers.IO) {
        sdk.getBalance(tokenAddress = tokenAddress)
    }

    val shielded = balance.shieldedBalance.toBigIntegerOrNull() ?: return false
    val required = requiredAmount.toBigIntegerOrNull() ?: return false

    return shielded >= required
}

// Usage
if (hasSufficientBalance(token, amount)) {
    val result = withContext(Dispatchers.IO) {
        sdk.unshield(
            tokenAddress = token,
            amount = amount,
            recipient = recipient
        )
    }
}

ViewModel Integration

class BalanceViewModel(private val sdk: PrivacyBoost) : ViewModel() {

    private val _balances = MutableStateFlow<List<TokenBalance>>(emptyList())
    val balances: StateFlow<List<TokenBalance>> = _balances.asStateFlow()

    private val _isLoading = MutableStateFlow(true)
    val isLoading: StateFlow<Boolean> = _isLoading.asStateFlow()

    fun loadBalances() {
        viewModelScope.launch {
            _isLoading.value = true
            try {
                val result = withContext(Dispatchers.IO) {
                    sdk.getAllBalances()
                }
                _balances.value = result
            } catch (e: SDKError) {
                println("Failed to load balances: $e")
            }
            _isLoading.value = false
        }
    }

    fun formatBalance(balance: TokenBalance): String {
        return try {
            sdk.formatAmount(balance.shieldedBalance, decimals = balance.decimals)
        } catch (e: Exception) {
            "0"
        }
    }
}

Displaying Balances in Compose

@Composable
fun BalanceList(viewModel: BalanceViewModel) {
    val balances by viewModel.balances.collectAsState()
    val isLoading by viewModel.isLoading.collectAsState()

    LaunchedEffect(Unit) {
        viewModel.loadBalances()
    }

    if (isLoading) {
        CircularProgressIndicator()
    } else if (balances.isEmpty()) {
        Text("No balances found", color = Color.Gray)
    } else {
        LazyColumn {
            items(balances) { balance ->
                BalanceRow(balance = balance, viewModel = viewModel)
            }
        }
    }
}

@Composable
fun BalanceRow(balance: TokenBalance, viewModel: BalanceViewModel) {
    Row(
        modifier = Modifier
            .fillMaxWidth()
            .padding(16.dp),
        horizontalArrangement = Arrangement.SpaceBetween
    ) {
        Text(
            text = balance.symbol ?: balance.tokenAddress.take(10),
            style = MaterialTheme.typography.titleMedium
        )
        Column(horizontalAlignment = Alignment.End) {
            Text("Shielded: ${viewModel.formatBalance(balance)}")
            Text(
                text = "Wallet: ${sdk.formatAmount(balance.walletBalance, balance.decimals)}",
                color = Color.Gray
            )
        }
    }
}

Error Handling

try {
    val balance = withContext(Dispatchers.IO) {
        sdk.getBalance(tokenAddress = tokenAddress)
    }
} catch (e: SDKError) {
    when (e) {
        is SDKError.NotAuthenticated ->
            println("Please log in first")
        is SDKError.NetworkError ->
            println("Network error: ${e.message}")
        else ->
            println("Balance error: $e")
    }
}

Best Practices

1. Refresh After Operations

// Refresh balance after a deposit
val shieldResult = withContext(Dispatchers.IO) {
    sdk.shield(tokenAddress = tokenAddress, amount = amount)
}

val updatedBalance = withContext(Dispatchers.IO) {
    sdk.getBalance(tokenAddress = tokenAddress)
}

2. Handle Zero Balances

fun displayBalance(balance: TokenBalance): String {
    if (balance.shieldedBalance == "0" && balance.walletBalance == "0") {
        return "0"
    }
    return try {
        sdk.formatAmount(balance.shieldedBalance, decimals = balance.decimals)
    } catch (e: Exception) {
        "0"
    }
}

3. Filter Non-Zero Balances

val allBalances = withContext(Dispatchers.IO) { sdk.getAllBalances() }
val nonZero = allBalances.filter { balance ->
    balance.shieldedBalance != "0" || balance.walletBalance != "0"
}

Next Steps