Skip to main content

Transaction History

This guide covers querying and paginating transaction history in the Privacy Boost React Native SDK.

Basic Usage

getTransactionHistory is async and returns a paginated result, not a raw array. Iterate over result.data:
import { PrivacyBoost } from '@sunnyside-io/privacy-boost-react-native';

const result = await sdk.getTransactionHistory();
for (const tx of result.data) {
  console.log(`${tx.txType}: ${tx.value} - ${tx.txHash}`);
}

Transaction Type

Each Transaction groups the on-chain event with zero or more shielded notes it produced or consumed. Money movement lives in value/tokenId/notes — not in the pre-0.2 amount/tokenAddress/receivers shape.
interface Transaction {
  txHash: string;                     // Transaction hash
  txType: string;                     // "shield" | "unshield" | "transfer"
  direction: string;                  // "incoming" | "outgoing"
  value: string;                      // Net value in wei
  tokenId: bigint;                    // Registered token ID
  createdAt: bigint;                  // Unix timestamp
  counterparty?: string;              // Other party's address or pubkey
  counterpartyType?: string;          // "wallet" | "privacy"
  notes: TransactionNote[];           // Shielded notes affected
}

interface TransactionNote {
  value: string;                      // Note value in wei
  direction: string;                  // "incoming" | "outgoing"
  isChange: boolean;                  // True if this is a change note
  leafIndex?: bigint;                 // Merkle leaf index
  nullifier?: string;                 // Spent-note nullifier
  counterparty?: string;
  counterpartyType?: string;
}

interface TransactionsResult {
  data: Transaction[];
  limit: number;
  next?: string;                      // Cursor for next page (undefined = end)
}
To resolve a tokenId to a token address/symbol, call sdk.getRegisteredTokens() once and cache the lookup.

Filtering History

By Transaction Type

// Only shields (deposits)
const shields = await sdk.getTransactionHistory('shield');

// Only unshields (withdrawals)
const unshields = await sdk.getTransactionHistory('unshield');

// Only transfers
const transfers = await sdk.getTransactionHistory('transfer');

With Limit

// First 10 transactions
const result = await sdk.getTransactionHistory(undefined, 10);

Pagination

The previous page’s next cursor is the third argument:
const first = await sdk.getTransactionHistory(undefined, 25);

if (first.next) {
  const second = await sdk.getTransactionHistory(undefined, 25, first.next);
}

Parameters

ParameterTypeDescription
txTypestring?Filter by type: "shield", "unshield", "transfer"
limitnumber?Page size
cursorstring?Opaque continuation token from previous result.next
Token filtering is not a parameter on this method — fetch all types and filter client-side by tx.tokenId, or use sdk.getUnspentNotes(tokenAddress) for balance-level queries.

Complete Example

import React, { useState, useEffect, useCallback } from 'react';
import { View, Text, FlatList, ActivityIndicator } from 'react-native';
import type {
  PrivacyBoost,
  Transaction,
} from '@sunnyside-io/privacy-boost-react-native';

function TransactionHistoryScreen({ sdk }: { sdk: PrivacyBoost }) {
  const [transactions, setTransactions] = useState<Transaction[]>([]);
  const [cursor, setCursor] = useState<string | undefined>(undefined);
  const [filter, setFilter] = useState<string | undefined>(undefined);
  const [loading, setLoading] = useState(true);

  const loadMore = useCallback(async () => {
    setLoading(true);
    try {
      const page = await sdk.getTransactionHistory(filter, 25, cursor);
      setTransactions((prev) => (cursor ? [...prev, ...page.data] : page.data));
      setCursor(page.next);
    } catch (error) {
      console.log('Failed to load transactions:', error);
    }
    setLoading(false);
  }, [sdk, filter, cursor]);

  // Reset when filter changes
  useEffect(() => {
    setCursor(undefined);
    setTransactions([]);
    loadMore();
  }, [filter]);

  const formatDate = (timestamp: bigint) =>
    new Date(Number(timestamp) * 1000).toLocaleDateString();

  return (
    <View style={{ padding: 16 }}>
      <FlatList
        data={transactions}
        keyExtractor={(item) => item.txHash}
        renderItem={({ item }) => (
          <View style={{ paddingVertical: 8 }}>
            <Text style={{ fontWeight: 'bold' }}>
              {item.txType} · {item.direction}
            </Text>
            <Text>Value: {sdk.formatAmount(item.value, 18)}</Text>
            <Text style={{ color: 'gray' }}>{formatDate(item.createdAt)}</Text>
          </View>
        )}
        onEndReached={() => cursor && loadMore()}
        onEndReachedThreshold={0.5}
        ListFooterComponent={loading ? <ActivityIndicator /> : null}
        ListEmptyComponent={!loading ? <Text>No transactions</Text> : null}
      />
    </View>
  );
}

Error Handling

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

try {
  const result = await sdk.getTransactionHistory();
} catch (error) {
  if (SdkError.NotAuthenticated.instanceOf(error)) {
    console.log('Please log in first');
  } else if (SdkError.NetworkError.instanceOf(error)) {
    console.log('Network error');
  } else {
    console.log('Failed to get history:', error);
  }
}

Best Practices

1. Page through results

Always pass a limit and follow the next cursor. Unbounded history fetches get expensive fast for active accounts.

2. Cache token metadata

Transaction.tokenId is a numeric ID. Resolve to display symbol/decimals via sdk.getRegisteredTokens() and cache the result — the registered-token list changes rarely.

3. Refresh after operations

Call getTransactionHistory (with no cursor) after a successful shield / unshield / transfer so the UI reflects the new state.

Next Steps