import React from "react"; import { PublicKey, ConfirmedSignatureInfo, ParsedInstruction, } from "@solana/web3.js"; import { FetchStatus } from "providers/accounts"; import { useAccountHistories, useFetchAccountHistory, } from "providers/accounts/history"; import { useAccountOwnedTokens, TokenAccountData, } from "providers/accounts/tokens"; import { ErrorCard } from "components/common/ErrorCard"; import { LoadingCard } from "components/common/LoadingCard"; import { Signature } from "components/common/Signature"; import { Address } from "components/common/Address"; import { useTransactionDetails } from "providers/transactions"; import { useFetchTransactionDetails } from "providers/transactions/details"; import { coerce } from "superstruct"; import { ParsedInfo } from "validators"; import { TokenInstructionType, IX_TITLES, } from "components/instruction/token/types"; export function TokenHistoryCard({ pubkey }: { pubkey: PublicKey }) { const address = pubkey.toBase58(); const ownedTokens = useAccountOwnedTokens(address); if (ownedTokens === undefined) { return null; } const { tokens } = ownedTokens; if (tokens === undefined || tokens.length === 0) return null; return ; } function TokenHistoryTable({ tokens }: { tokens: TokenAccountData[] }) { const accountHistories = useAccountHistories(); const fetchAccountHistory = useFetchAccountHistory(); const fetchHistories = (refresh?: boolean) => { tokens.forEach((token) => { fetchAccountHistory(token.pubkey, refresh); }); }; // Fetch histories on load React.useEffect(() => { tokens.forEach((token) => { const address = token.pubkey.toBase58(); if (!accountHistories[address]) { fetchAccountHistory(token.pubkey, true); } }); }, []); // eslint-disable-line react-hooks/exhaustive-deps const fetchedFullHistory = tokens.every((token) => { const history = accountHistories[token.pubkey.toBase58()]; return history && history.foundOldest === true; }); const fetching = tokens.some((token) => { const history = accountHistories[token.pubkey.toBase58()]; return history && history.status === FetchStatus.Fetching; }); const failed = tokens.some((token) => { const history = accountHistories[token.pubkey.toBase58()]; return history && history.status === FetchStatus.FetchFailed; }); const mintAndTxs = tokens .map((token) => ({ mint: token.mint, history: accountHistories[token.pubkey.toBase58()], })) .filter(({ history }) => { return ( history !== undefined && history.fetched && history.fetched.length > 0 ); }) .flatMap(({ mint, history }) => (history.fetched as ConfirmedSignatureInfo[]).map((tx) => ({ mint, tx })) ); if (mintAndTxs.length === 0) { if (fetching) { return ; } else if (failed) { return ( fetchHistories(true)} text="Failed to fetch transaction history" /> ); } return ( fetchHistories(true)} retryText="Try again" text="No transaction history found" /> ); } mintAndTxs.sort((a, b) => { if (a.tx.slot > b.tx.slot) return -1; if (a.tx.slot < b.tx.slot) return 1; return 0; }); return (

Token History

{mintAndTxs.map(({ mint, tx }) => ( ))}
Slot Result Token Instruction Type Transaction Signature
{fetchedFullHistory ? (
Fetched full history
) : ( )}
); } function TokenTransactionRow({ mint, tx, }: { mint: PublicKey; tx: ConfirmedSignatureInfo; }) { const details = useTransactionDetails(tx.signature); const fetchDetails = useFetchTransactionDetails(); // Fetch details on load React.useEffect(() => { if (!details) fetchDetails(tx.signature); }, []); // eslint-disable-line react-hooks/exhaustive-deps const instructions = details?.transaction?.transaction.message.instructions; if (instructions) { const tokenInstructions = instructions.filter( (ix) => "parsed" in ix && ix.program === "spl-token" ) as ParsedInstruction[]; if (tokenInstructions.length > 0) { return ( <> {tokenInstructions.map((ix, index) => { const parsed = coerce(ix.parsed, ParsedInfo); const { type: rawType } = parsed; const type = coerce(rawType, TokenInstructionType); const typeName = IX_TITLES[type]; let statusText; let statusClass; if (tx.err) { statusClass = "warning"; statusText = "Failed"; } else { statusClass = "success"; statusText = "Success"; } return ( {tx.slot} {statusText}
{typeName} ); })} ); } } let statusText; let statusClass; if (tx.err) { statusClass = "warning"; statusText = "Failed"; } else { statusClass = "success"; statusText = "Success"; } return ( {tx.slot} {statusText} Loading
); }