import React, { useMemo } from 'react'; import { format } from 'date-fns'; import { FiHash, FiClock, FiExternalLink } from 'react-icons/fi'; import { cn } from '../../../lib/utils'; import { useTokenInfoBatch } from '../../../hooks/useTokenInfo'; import { TransactionDetailsProps } from './types'; import { TransactionSummary } from './CommonFields'; import { HbarTransfersSection, TokenTransfersSection, AirdropSection, } from './TransferComponents'; import { TokenCreationSection, TokenMintSection, TokenBurnSection, TokenUpdateSection, TokenDeleteSection, TokenAssociateSection, TokenDissociateSection, TokenFreezeSection, TokenUnfreezeSection, TokenGrantKycSection, TokenRevokeKycSection, TokenPauseSection, TokenUnpauseSection, TokenWipeSection, TokenFeeScheduleUpdateSection, } from './TokenTransactions'; import { CryptoCreateAccountSection, CryptoUpdateAccountSection, CryptoDeleteSection, CryptoApproveAllowanceSection, CryptoDeleteAllowanceSection, } from './AccountTransactions'; import { ContractCallSection, ContractCreateSection, ContractUpdateSection, ContractDeleteSection, } from './ContractTransactions'; import { ConsensusCreateTopicSection, ConsensusSubmitMessageSection, ConsensusUpdateTopicSection, ConsensusDeleteTopicSection, } from './ConsensusTransactions'; import { ScheduleCreateSection, ScheduleSignSection, ScheduleDeleteSection, } from './ScheduleTransactions'; import { UtilPrngSection, FreezeSection } from './UtilityTransactions'; const hasValidContent = (obj: unknown): boolean => { if (!obj || typeof obj !== 'object') return false; return Object.values(obj).some( (value) => value !== undefined && value !== null && value !== '' && (typeof value !== 'object' || hasValidContent(value)) ); }; export const TransactionDetails: React.FC = ( props ) => { const { type, humanReadableType, transfers, tokenTransfers, memo, expirationTime, scheduleId, className, hideHeader, executedTransactionEntityId, executedTransactionType, network = 'testnet', variant = 'default', tokenCreationInfo, tokenCreation, tokenMint, tokenBurn, tokenUpdate, tokenDelete, tokenAssociate, tokenDissociate, tokenFreeze, tokenUnfreeze, tokenGrantKyc, tokenRevokeKyc, tokenPause, tokenUnpause, tokenWipeAccount, tokenFeeScheduleUpdate, airdrop, tokenAirdrop, cryptoCreateAccount, cryptoUpdateAccount, cryptoDelete, cryptoApproveAllowance, cryptoDeleteAllowance, contractCall, contractCreate, contractUpdate, contractDelete, consensusCreateTopic, consensusSubmitMessage, consensusUpdateTopic, consensusDeleteTopic, scheduleCreate, scheduleSign, scheduleDelete, utilPrng, freeze, } = props; const hasTransfers = transfers.length > 0; const hasTokenTransfers = tokenTransfers.length > 0; const tokenCreationData = tokenCreationInfo || tokenCreation; const hasTokenCreation = !!tokenCreationData; const tokenIds = useMemo(() => { const ids = new Set(); tokenTransfers.forEach((transfer) => ids.add(transfer.tokenId)); if (airdrop?.tokenTransfers) { airdrop.tokenTransfers.forEach((t) => ids.add(t.tokenId)); } if (tokenAirdrop?.tokenTransfers) { tokenAirdrop.tokenTransfers.forEach((t) => ids.add(t.tokenId)); } return Array.from(ids); }, [tokenTransfers, airdrop, tokenAirdrop]); const tokenInfoMap = useTokenInfoBatch(tokenIds); let airdropData = airdrop || tokenAirdrop; if (!airdropData && type === 'TOKENAIRDROP' && hasTokenTransfers) { const tokenMap = new Map< string, Array<{ accountId: string; amount: string }> >(); tokenTransfers.forEach((transfer) => { if (!tokenMap.has(transfer.tokenId)) { tokenMap.set(transfer.tokenId, []); } tokenMap.get(transfer.tokenId)!.push({ accountId: transfer.accountId, amount: transfer.amount.toString(), }); }); airdropData = { tokenTransfers: Array.from(tokenMap.entries()).map( ([tokenId, transfers]) => ({ tokenId, transfers, }) ), }; } const hasAirdrop = !!airdropData?.tokenTransfers?.length; const formattedExpirationTime = expirationTime ? (() => { try { return format(new Date(expirationTime), 'PPpp'); } catch { return expirationTime; } })() : undefined; return (
{scheduleId ? (
Schedule ID: {scheduleId}
) : null} {formattedExpirationTime ? (
Expires: {formattedExpirationTime}
) : null} {memo ? (
Transaction Memo
{memo}
) : null} {hasTransfers ? : null} {hasTokenTransfers && ( )} {(() => { if ( hasTokenCreation && tokenCreationData && hasValidContent(tokenCreationData) ) { return ( ); } return null; })()} {tokenMint ? : null} {tokenBurn ? : null} {tokenUpdate ? : null} {tokenDelete ? : null} {tokenAssociate ? ( ) : null} {tokenDissociate && ( )} {tokenFreeze ? : null} {tokenUnfreeze && ( )} {tokenGrantKyc && ( )} {tokenRevokeKyc && ( )} {tokenPause ? : null} {tokenUnpause ? ( ) : null} {tokenWipeAccount && ( )} {tokenFeeScheduleUpdate && ( )} {hasAirdrop && airdropData ? ( ) : null} {cryptoCreateAccount && ( )} {cryptoUpdateAccount && ( )} {cryptoDelete ? ( ) : null} {cryptoApproveAllowance && ( )} {cryptoDeleteAllowance && ( )} {contractCall ? ( ) : null} {contractCreate && ( )} {contractUpdate && ( )} {contractDelete && ( )} {hasValidContent(consensusCreateTopic) && consensusCreateTopic ? ( ) : null} {hasValidContent(consensusSubmitMessage) && consensusSubmitMessage ? ( ) : null} {hasValidContent(consensusUpdateTopic) && consensusUpdateTopic ? ( ) : null} {hasValidContent(consensusDeleteTopic) && consensusDeleteTopic ? ( ) : null} {scheduleCreate && ( )} {scheduleSign ? ( ) : null} {scheduleDelete && ( )} {utilPrng ? : null} {freeze ? : null}
); }; export default TransactionDetails;