Transaction History
This guide covers querying and filtering transaction history using the Privacy Boost React SDK’suseTransactions hook.
Basic Usage
import { useTransactions } from '@testinprod-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 '@testinprod-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>
);
}