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.

Transaction History

This guide covers querying and filtering transaction history using the Privacy Boost React SDK’s useTransactions hook.

Basic Usage

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

function TransactionList() {
  const { transactions, isLoading, fetchHistory } = useTransactions();

  useEffect(() => {
    fetchHistory();
  }, []);

  if (isLoading) return <div>Loading...</div>;

  return (
    <ul>
      {transactions.map((tx) => (
        <li key={tx.txHash}>
          {tx.type}: {tx.amount?.toString() ?? '-'}
        </li>
      ))}
    </ul>
  );
}

Return Value

interface UseTransactionsResult {
  // Reactive data (locally-tracked pending transactions)
  transactions: PendingTransaction[];
  recentTransactions: PendingTransaction[];
  pending: PendingTransaction[];
  isLoading: boolean;
  hasPending: boolean;
  pendingCount: number;

  // Filtered views
  deposits: PendingTransaction[];
  withdrawals: PendingTransaction[];
  transfers: PendingTransaction[];

  // Actions
  fetchHistory(params?: FetchHistoryParams): Promise<TransactionsResponse>;
  refreshPending(): Promise<number>;
  getByHash(txHash: string): PendingTransaction | undefined;
  getByType(type: TransactionType): PendingTransaction[];
  getByStatus(status: TransactionStatus): PendingTransaction[];
}

Transaction Types

interface TransactionNote {
  value: string;
  direction: string;
  isChange: boolean;
  leafIndex?: number;
  nullifier?: string;
  counterParty?: string;
  counterPartyType?: string;
}

interface Transaction {
  txHash: string;
  txType: string;       // 'deposit' | 'withdraw' | 'transfer'
  direction: string;    // 'incoming' | 'outgoing'
  value: string;
  tokenId: number;
  createdAt: number;
  counterparty?: string;
  counterpartyType?: string;
  notes: TransactionNote[];
}

interface TransactionsResponse {
  data: Transaction[];
  limit: number;
  next?: string;
}

Fetching History

Basic Fetch

const { fetchHistory } = useTransactions();

useEffect(() => {
  fetchHistory();
}, []);

With Filters

// Only deposits
await fetchHistory({ txType: 'deposit' });

// Limited results
await fetchHistory({ limit: 10 });

// Cursor-based pagination
await fetchHistory({ limit: 10, cursor: 'next-cursor-value' });

Filter Parameters

interface FetchHistoryParams {
  txType?: string;
  limit?: number;
  cursor?: string;
}

Complete Example

import { useState, useEffect } from 'react';
import { useTransactions } from '@sunnyside-io/privacy-boost-react';

function TransactionHistory() {
  const { transactions, isLoading, fetchHistory } = useTransactions();
  const [filter, setFilter] = useState<string>('all');

  useEffect(() => {
    if (filter === 'all') {
      fetchHistory({ limit: 50 });
    } else {
      fetchHistory({ txType: filter, limit: 50 });
    }
  }, [filter, fetchHistory]);

  const formatAmount = (amount: bigint, decimals = 18) => {
    return (Number(amount) / 10 ** decimals).toFixed(4);
  };

  const formatDate = (timestamp: number) => {
    return new Date(timestamp).toLocaleDateString();
  };

  return (
    <div>
      {/* Filter Tabs */}
      <div className="tabs">
        {['all', 'deposit', 'withdraw', 'transfer'].map((type) => (
          <button
            key={type}
            onClick={() => setFilter(type)}
            className={filter === type ? 'active' : ''}
          >
            {type.charAt(0).toUpperCase() + type.slice(1)}
          </button>
        ))}
      </div>

      {/* Loading State */}
      {isLoading && <div>Loading transactions...</div>}

      {/* Transaction List */}
      {!isLoading && transactions.length === 0 && (
        <p>No transactions found</p>
      )}

      <table>
        <thead>
          <tr>
            <th>Type</th>
            <th>Amount</th>
            <th>Status</th>
            <th>Date</th>
          </tr>
        </thead>
        <tbody>
          {transactions.map((tx) => (
            <tr key={tx.txHash}>
              <td>
                <span className={`type-${tx.type}`}>
                  {tx.type}
                </span>
                {tx.direction === 'incoming' && ' (received)'}
              </td>
              <td>{tx.amount ? formatAmount(tx.amount) : '-'}</td>
              <td>
                <span className={`status-${tx.status}`}>
                  {tx.status}
                </span>
              </td>
              <td>{formatDate(tx.createdAt)}</td>
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  );
}

Transaction Details Modal

function TransactionDetail({ tx }: { tx: Transaction }) {
  return (
    <div className="tx-detail">
      <h3>{tx.txType.toUpperCase()}</h3>

      <div className="row">
        <label>Direction</label>
        <span>{tx.direction}</span>
      </div>

      <div className="row">
        <label>Value</label>
        <span>{tx.value}</span>
      </div>

      <div className="row">
        <label>Transaction Hash</label>
        <a
          href={`https://etherscan.io/tx/${tx.txHash}`}
          target="_blank"
          rel="noopener noreferrer"
        >
          {tx.txHash.slice(0, 20)}...
        </a>
      </div>

      {tx.counterparty && (
        <div className="row">
          <label>Counterparty</label>
          <span>{tx.counterparty.slice(0, 20)}...</span>
        </div>
      )}

      <div className="row">
        <label>Date</label>
        <span>{new Date(tx.createdAt).toLocaleString()}</span>
      </div>

      {tx.notes.length > 0 && (
        <div className="notes">
          <label>Notes ({tx.notes.length})</label>
          {tx.notes.map((note, i) => (
            <div key={i}>
              {note.direction}: {note.value}
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

Refresh Transactions

function RefreshableHistory() {
  const { transactions, isLoading, fetchHistory } = useTransactions();
  const [lastRefresh, setLastRefresh] = useState<Date | null>(null);

  const handleRefresh = async () => {
    await fetchHistory();
    setLastRefresh(new Date());
  };

  return (
    <div>
      <div className="header">
        <h3>Transaction History</h3>
        <button onClick={handleRefresh} disabled={isLoading}>
          {isLoading ? 'Refreshing...' : 'Refresh'}
        </button>
      </div>

      {lastRefresh && (
        <small>Last refreshed: {lastRefresh.toLocaleTimeString()}</small>
      )}

      {/* Transaction list */}
    </div>
  );
}

Next Steps