Skip to main content

Transaction History

This guide covers querying and filtering transaction history in the Privacy Boost Android SDK.

Basic Usage

val transactions = withContext(Dispatchers.IO) {
    sdk.getTransactionHistory(
        txType = null,
        tokenAddress = null,
        limit = null
    )
}

for (tx in transactions) {
    println("${tx.txType}: ${tx.amount} - ${tx.txHash}")
}

Transaction Type

data class Transaction(
    val txHash: String,           // Transaction hash
    val txType: String,           // "deposit", "withdraw", or "transfer"
    val tokenAddress: String,     // Token contract address
    val amount: String,           // Amount in wei
    val direction: String,        // "incoming" or "outgoing"
    val senderPubKey: String,     // Sender's public key
    val receiverPubKeys: List<String>, // Receiver public keys
    val createdAt: ULong          // Unix timestamp
)

Filtering History

By Transaction Type

// Only deposits
val deposits = withContext(Dispatchers.IO) {
    sdk.getTransactionHistory(txType = "deposit", tokenAddress = null, limit = null)
}

// Only withdrawals
val withdrawals = withContext(Dispatchers.IO) {
    sdk.getTransactionHistory(txType = "withdraw", tokenAddress = null, limit = null)
}

// Only transfers
val transfers = withContext(Dispatchers.IO) {
    sdk.getTransactionHistory(txType = "transfer", tokenAddress = null, limit = null)
}

By Token

val usdcHistory = withContext(Dispatchers.IO) {
    sdk.getTransactionHistory(
        txType = null,
        tokenAddress = "0x...usdc-address",
        limit = null
    )
}

With Limit

// Get last 10 transactions
val recent = withContext(Dispatchers.IO) {
    sdk.getTransactionHistory(txType = null, tokenAddress = null, limit = 10u)
}

Combined Filters

// Last 5 USDC deposits
val recentUsdcDeposits = withContext(Dispatchers.IO) {
    sdk.getTransactionHistory(
        txType = "deposit",
        tokenAddress = "0x...usdc-address",
        limit = 5u
    )
}

Filter Parameters

ParameterTypeDescription
txTypeString?Filter by type: "deposit", "withdraw", "transfer"
tokenAddressString?Filter by token contract address
limitUInt?Maximum number of results

ViewModel Integration

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

    private val _transactions = MutableStateFlow<List<Transaction>>(emptyList())
    val transactions: StateFlow<List<Transaction>> = _transactions.asStateFlow()

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

    private val _filter = MutableStateFlow("all")
    val filter: StateFlow<String> = _filter.asStateFlow()

    fun setFilter(type: String) {
        _filter.value = type
        loadTransactions()
    }

    fun loadTransactions() {
        viewModelScope.launch {
            _isLoading.value = true
            try {
                val typeFilter = if (_filter.value == "all") null else _filter.value
                val result = withContext(Dispatchers.IO) {
                    sdk.getTransactionHistory(
                        txType = typeFilter,
                        tokenAddress = null,
                        limit = 50u
                    )
                }
                _transactions.value = result
            } catch (e: SDKError) {
                println("Failed to load transactions: $e")
            }
            _isLoading.value = false
        }
    }
}

Displaying Transactions in Compose

@Composable
fun TransactionHistory(viewModel: TransactionViewModel) {
    val transactions by viewModel.transactions.collectAsState()
    val isLoading by viewModel.isLoading.collectAsState()
    val filter by viewModel.filter.collectAsState()

    val filters = listOf("all", "deposit", "withdraw", "transfer")

    LaunchedEffect(Unit) {
        viewModel.loadTransactions()
    }

    Column {
        // Filter tabs
        Row(
            modifier = Modifier.fillMaxWidth(),
            horizontalArrangement = Arrangement.SpaceEvenly
        ) {
            filters.forEach { type ->
                FilterChip(
                    selected = filter == type,
                    onClick = { viewModel.setFilter(type) },
                    label = { Text(type.replaceFirstChar { it.uppercase() }) }
                )
            }
        }

        // Transaction list
        if (isLoading) {
            CircularProgressIndicator(modifier = Modifier.align(Alignment.CenterHorizontally))
        } else if (transactions.isEmpty()) {
            Text("No transactions found", color = Color.Gray)
        } else {
            LazyColumn {
                items(transactions) { tx ->
                    TransactionRow(transaction = tx)
                }
            }
        }
    }
}

@Composable
fun TransactionRow(transaction: Transaction) {
    val icon = when (transaction.txType) {
        "deposit" -> Icons.Default.ArrowDownward
        "withdraw" -> Icons.Default.ArrowUpward
        "transfer" -> Icons.Default.SwapHoriz
        else -> Icons.Default.Circle
    }

    val dateFormatter = SimpleDateFormat("MMM d, yyyy HH:mm", Locale.getDefault())
    val formattedDate = dateFormatter.format(Date(transaction.createdAt.toLong() * 1000))

    Row(
        modifier = Modifier
            .fillMaxWidth()
            .padding(16.dp),
        verticalAlignment = Alignment.CenterVertically
    ) {
        Icon(imageVector = icon, contentDescription = transaction.txType)
        Spacer(modifier = Modifier.width(12.dp))
        Column(modifier = Modifier.weight(1f)) {
            Text(
                text = transaction.txType.replaceFirstChar { it.uppercase() },
                style = MaterialTheme.typography.titleSmall
            )
            Text(
                text = formattedDate,
                style = MaterialTheme.typography.bodySmall,
                color = Color.Gray
            )
        }
        Text(
            text = transaction.amount,
            style = MaterialTheme.typography.bodyMedium
        )
    }
}

Error Handling

try {
    val transactions = withContext(Dispatchers.IO) {
        sdk.getTransactionHistory(txType = null, tokenAddress = null, limit = null)
    }
} catch (e: SDKError) {
    when (e) {
        is SDKError.NotAuthenticated ->
            println("Please log in first")
        is SDKError.NetworkError ->
            println("Network error: ${e.message}")
        else ->
            println("Failed to get history: $e")
    }
}

Best Practices

1. Paginate Results

For apps with many transactions, use the limit parameter:
suspend fun loadPage(pageSize: UInt = 20u): List<Transaction> {
    return withContext(Dispatchers.IO) {
        sdk.getTransactionHistory(txType = null, tokenAddress = null, limit = pageSize)
    }
}

2. Refresh After Operations

Fetch updated history after deposits, withdrawals, or transfers:
val shieldResult = withContext(Dispatchers.IO) {
    sdk.shield(tokenAddress = tokenAddress, amount = amount)
}

// Refresh transaction list
val updatedHistory = withContext(Dispatchers.IO) {
    sdk.getTransactionHistory(txType = null, tokenAddress = null, limit = 20u)
}

Next Steps