From 28c819ec856ac3279963ddbf28b54c35b590c377 Mon Sep 17 00:00:00 2001 From: void-57 Date: Wed, 14 Jan 2026 17:20:14 +0530 Subject: [PATCH] Enhance transaction processing to accurately extract sender, receiver, and transaction types (transfer, swap, nft_mint) --- index.html | 229 +++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 195 insertions(+), 34 deletions(-) diff --git a/index.html b/index.html index 354dffe..a6ee76a 100644 --- a/index.html +++ b/index.html @@ -1539,15 +1539,34 @@ const amount = calculateTransactionAmount(tx, address); - // Extract from and to addresses with proper null checks + // Extract from and to addresses - prioritize parsed instructions let fromAddress = "N/A"; let toAddress = "N/A"; - // Add null checks before accessing transaction properties - if (tx?.transaction?.message?.accountKeys) { + // First, try to get from parsed instructions (most reliable) + if (tx?.transaction?.message?.instructions) { + for (const ix of tx.transaction.message.instructions) { + if (ix.parsed && ix.parsed.info) { + // System transfer + if (ix.parsed.type === "transfer" && ix.parsed.info.source && ix.parsed.info.destination) { + fromAddress = ix.parsed.info.source; + toAddress = ix.parsed.info.destination; + break; + } + // Token transfer + if (ix.parsed.info.authority && ix.parsed.info.destination) { + fromAddress = ix.parsed.info.authority; + toAddress = ix.parsed.info.destination; + break; + } + } + } + } + + // Fallback: use account keys if parsed instructions not available + if (fromAddress === "N/A" && tx?.transaction?.message?.accountKeys) { const keys = tx.transaction.message.accountKeys; - fromAddress = - keys[0]?.pubkey?.toString() || String(keys[0]) || "N/A"; + fromAddress = keys[0]?.pubkey?.toString() || String(keys[0]) || "N/A"; toAddress = keys[1]?.pubkey?.toString() || String(keys[1]) || "N/A"; } @@ -1576,6 +1595,49 @@ } txid = String(txid); + // Detect transaction type + let transactionType = "transfer"; + const logMessages = tx.meta?.logMessages || []; + + // Check for NFT mint + const isNFTMint = logMessages.some(log => + log.includes("MintToCollectionV1") || + log.includes("MintV1") || + log.includes("Bubblegum") || + (log.includes("Instruction: Mint") && !log.includes("MintTo") && !log.includes("JUP")) + ); + + if (isNFTMint) { + transactionType = "nft_mint"; + } + // Check for token swap + else if (tx.meta?.preTokenBalances && tx.meta?.postTokenBalances) { + const preTokens = tx.meta.preTokenBalances; + const postTokens = tx.meta.postTokenBalances; + + let hasTokenSent = false; + let hasTokenReceived = false; + + for (const preTok of preTokens) { + const postTok = postTokens.find(p => p.accountIndex === preTok.accountIndex); + if (postTok) { + const preAmount = parseFloat(preTok.uiTokenAmount.amount); + const postAmount = parseFloat(postTok.uiTokenAmount.amount); + + if (preAmount > postAmount && (preAmount - postAmount) > 0.000001) { + hasTokenSent = true; + } + if (postAmount > preAmount && (postAmount - preAmount) > 0.000001) { + hasTokenReceived = true; + } + } + } + + if (hasTokenSent && hasTokenReceived) { + transactionType = "swap"; + } + } + return { txid: txid, time: tx.blockTime ? getFormattedTime(tx.blockTime) : "Unknown", @@ -1586,6 +1648,9 @@ sender: fromAddress, receiver: toAddress, address: address, + transactionType: transactionType, + typeLabel: transactionType === "swap" ? "Swap" : transactionType === "nft_mint" ? "NFT Mint" : "Transfer", + }; } catch (err) { console.error("Error in formatTransaction:", err); @@ -1837,13 +1902,14 @@
-
+ ${transactionDetails.transactionType === "transfer" ? `
${transactionDetails.type === - "out" - ? "Sent" - : "Received" - } -
+ "out" + ? "Sent" + : "Received" + } +
` : ""} + ${transactionDetails.typeLabel ? ` ${transactionDetails.typeLabel}` : ""}
- ${transactionDetails.sender + ${transactionDetails.transactionType === "transfer" && transactionDetails.sender ? `
From: ${fromAddress}
` : "" } - ${transactionDetails.receiver + ${transactionDetails.transactionType === "transfer" && transactionDetails.receiver ? `
To: ${toAddress} @@ -2481,15 +2547,25 @@ html`
-
-
-
-

-

+ @@ -2515,6 +2591,13 @@
+ +
+ +
+
+ +
@@ -2578,7 +2661,7 @@ status === "confirmed" ? `Included in Slot #${tx.slot}` : ""; } - + // Extract sender and receiver from transaction let sender = "Unknown"; let receiver = "Unknown"; @@ -2639,25 +2722,78 @@ // If we found a token swap, use that instead of FROM/TO if (tokenSent && tokenReceived) { transactionType = "swap"; - sender = `${tokenSent.symbol}`; - receiver = `${tokenReceived.symbol}`; + + // Map of common token mint addresses to symbols + const tokenSymbolMap = { + "So11111111111111111111111111111111111111112": "SOL", + "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v": "USDC", + "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB": "USDT", + "DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263": "Bonk", + "7vfCXTUXx5WJV5JADk17DUJ4ksgau7utNKj4b963voxs": "Ether (Wormhole)", + "mSoLzYCxHdYgdzU16g5QSh3i5K3z3KZK7ytfqcJm7So": "mSOL", + "7dHbWXmci3dT8UFYWYZweBLXgycu7Y3iL6trKn1Y7ARj": "stSOL", + "SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt": "SRM", + "kinXdEcpDQeHPEuQnqmUgtYykqKGVFq6CeVX5iAHJq6": "KIN", + "orcaEKTdK7LKz57vaAYr9QeNsVEPfiu6QeMU1kektZE": "ORCA", + "RLBxxFkseAZ4RgJH3Sqn8jXxhmGoz9jWxDNJMh8pL7a": "RLB", + "MangoCzJ36AjZyKwVj3VnYU4GTonjfVEnJmvvWaxLac": "MNGO", + "SHDWyBxihqiCj6YekG2GUr7wqKLeLAMK1gHZck9pL6y": "SHDW", + "HZ1JovNiVvGrGNiiYvEozEVgZ58xaU3RKwX8eACQBCt3": "PYTH", + "JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN": "JUP", + }; + + // Get token symbols or use truncated addresses + const getTokenSymbol = (mint) => { + if (tokenSymbolMap[mint]) { + return tokenSymbolMap[mint]; + } + // Fallback to truncated address + if (mint.length > 20) { + return mint.substring(0, 8) + "..." + mint.substring(mint.length - 4); + } + return mint; + }; + + sender = getTokenSymbol(tokenSent.mint); + receiver = getTokenSymbol(tokenReceived.mint); } } // If not a swap or NFT mint, try to find the transfer instruction if (transactionType === "transfer") { + // First priority: Check for parsed instructions (most reliable) for (const ix of instructions) { - if (ix.accounts && ix.accounts.length >= 2) { - if (ix.data) { - try { - const dataBytes = bs58.decode(ix.data); - if (dataBytes[0] === 2) { - sender = accountKeys[ix.accounts[0]]?.toString() || "Unknown"; - receiver = accountKeys[ix.accounts[1]]?.toString() || "Unknown"; - break; + if (ix.parsed && ix.parsed.info) { + // System transfer + if (ix.parsed.type === "transfer" && ix.parsed.info.source && ix.parsed.info.destination) { + sender = ix.parsed.info.source; + receiver = ix.parsed.info.destination; + break; + } + // Token transfer + if (ix.parsed.info.authority && ix.parsed.info.destination) { + sender = ix.parsed.info.authority; + receiver = ix.parsed.info.destination; + break; + } + } + } + + // Second priority: Try raw instruction decoding + if (sender === "Unknown") { + for (const ix of instructions) { + if (ix.accounts && ix.accounts.length >= 2) { + if (ix.data) { + try { + const dataBytes = bs58.decode(ix.data); + if (dataBytes[0] === 2) { + sender = accountKeys[ix.accounts[0]]?.toString() || "Unknown"; + receiver = accountKeys[ix.accounts[1]]?.toString() || "Unknown"; + break; + } + } catch (e) { + // If decode fails, continue } - } catch (e) { - // If decode fails, continue } } } @@ -2718,8 +2854,33 @@ : "Address unknown"; } + // Hide FROM/TO section for swaps and NFT mints + const addressSection = document.querySelector(".tx-address-section"); + if (addressSection) { + if (transactionType === "swap" || transactionType === "nft_mint") { + addressSection.style.display = "none"; + } else { + addressSection.style.display = "flex"; + } + } + if (getRef("tx_hash")) getRef("tx_hash").value = txId; + // Set transaction type label + if (getRef("tx_type")) { + const typeLabels = { + "swap": "Swap", + "nft_mint": "NFT Mint", + "transfer": "Transfer" + }; + getRef("tx_type").textContent = typeLabels[transactionType] || "Transfer"; + } + + // Set explorer link + if (getRef("tx_explorer_link_header")) { + getRef("tx_explorer_link_header").href = `https://solscan.io/tx/${txId}`; + } + if (getRef("tx_value")) { getRef("tx_value").dataset.sol = value; getRef("tx_value").dataset.timestamp = tx.blockTime || "";