import React, { useEffect, useState } from 'react'; import { usePolkadot } from '@/contexts/PolkadotContext'; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, } from '@/components/ui/dialog'; import { Button } from '@/components/ui/button'; import { History, ExternalLink, ArrowUpRight, ArrowDownRight, RefreshCw } from 'lucide-react'; import { useToast } from '@/hooks/use-toast'; interface TransactionHistoryProps { isOpen: boolean; onClose: () => void; } interface Transaction { blockNumber: number; extrinsicIndex: number; hash: string; method: string; section: string; from: string; to?: string; amount?: string; success: boolean; timestamp?: number; } export const TransactionHistory: React.FC = ({ isOpen, onClose }) => { const { api, isApiReady, selectedAccount } = usePolkadot(); const { toast } = useToast(); const [transactions, setTransactions] = useState([]); const [isLoading, setIsLoading] = useState(false); const fetchTransactions = async () => { if (!api || !isApiReady || !selectedAccount) return; setIsLoading(true); try { console.log('Fetching transactions...'); const currentBlock = await api.rpc.chain.getBlock(); const currentBlockNumber = currentBlock.block.header.number.toNumber(); console.log('Current block number:', currentBlockNumber); const txList: Transaction[] = []; const blocksToCheck = Math.min(200, currentBlockNumber); for (let i = 0; i < blocksToCheck && txList.length < 20; i++) { const blockNumber = currentBlockNumber - i; try { const blockHash = await api.rpc.chain.getBlockHash(blockNumber); const block = await api.rpc.chain.getBlock(blockHash); // Try to get timestamp, but don't fail if state is pruned let timestamp = 0; try { const ts = await api.query.timestamp.now.at(blockHash); timestamp = ts.toNumber(); } catch (error) { // State pruned, use current time as fallback timestamp = Date.now(); } console.log(`Block #${blockNumber}: ${block.block.extrinsics.length} extrinsics`); // Check each extrinsic in the block block.block.extrinsics.forEach((extrinsic, index) => { // Skip unsigned extrinsics (system calls) if (!extrinsic.isSigned) { return; } const { method, signer } = extrinsic; console.log(` Extrinsic #${index}: ${method.section}.${method.method}, signer: ${signer.toString()}`); // Check if transaction involves our account const fromAddress = signer.toString(); const isFromOurAccount = fromAddress === selectedAccount.address; // Parse balances.transfer or balances.transferKeepAlive if (method.section === 'balances' && (method.method === 'transfer' || method.method === 'transferKeepAlive')) { const [dest, value] = method.args; const toAddress = dest.toString(); const isToOurAccount = toAddress === selectedAccount.address; if (isFromOurAccount || isToOurAccount) { txList.push({ blockNumber, extrinsicIndex: index, hash: extrinsic.hash.toHex(), method: method.method, section: method.section, from: fromAddress, to: toAddress, amount: value.toString(), success: true, timestamp: timestamp, }); } } // Parse assets.transfer (PEZ, USDT, etc.) if (method.section === 'assets' && method.method === 'transfer') { const [assetId, dest, value] = method.args; const toAddress = dest.toString(); const isToOurAccount = toAddress === selectedAccount.address; if (isFromOurAccount || isToOurAccount) { txList.push({ blockNumber, extrinsicIndex: index, hash: extrinsic.hash.toHex(), method: `${method.method} (Asset ${assetId.toString()})`, section: method.section, from: fromAddress, to: toAddress, amount: value.toString(), success: true, timestamp: timestamp, }); } } }); } catch (blockError) { console.warn(`Error processing block #${blockNumber}:`, blockError); // Continue to next block } } console.log('Found transactions:', txList.length); setTransactions(txList); } catch (error) { console.error('Failed to fetch transactions:', error); toast({ title: "Error", description: "Failed to fetch transaction history", variant: "destructive", }); } finally { setIsLoading(false); } }; useEffect(() => { if (isOpen) { fetchTransactions(); } }, [isOpen, api, isApiReady, selectedAccount]); const formatAmount = (amount: string, decimals: number = 12) => { const value = parseInt(amount) / Math.pow(10, decimals); return value.toFixed(4); }; const formatTimestamp = (timestamp?: number) => { if (!timestamp) return 'Unknown'; const date = new Date(timestamp); return date.toLocaleString(); }; const isIncoming = (tx: Transaction) => { return tx.to === selectedAccount?.address; }; return (
Transaction History Recent transactions involving your account
{isLoading ? (

Loading transactions...

) : transactions.length === 0 ? (

No transactions found

Your recent transactions will appear here

) : ( transactions.map((tx, index) => (
{isIncoming(tx) ? (
) : (
)}
{isIncoming(tx) ? 'Received' : 'Sent'}
{tx.section}.{tx.method}
{isIncoming(tx) ? '+' : '-'}{formatAmount(tx.amount || '0')}
Block #{tx.blockNumber}
From:
{tx.from.slice(0, 8)}...{tx.from.slice(-6)}
{tx.to && (
To:
{tx.to.slice(0, 8)}...{tx.to.slice(-6)}
)}
{formatTimestamp(tx.timestamp)}
)) )}
); };