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’suseTransactions 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>
);
}