diff --git a/src/popup/views/confirmTx.js b/src/popup/views/confirmTx.js index 522e9e5..8b7724e 100644 --- a/src/popup/views/confirmTx.js +++ b/src/popup/views/confirmTx.js @@ -25,6 +25,7 @@ const { decryptWithPassword } = require("../../shared/vault"); const { formatUsd, getPrice } = require("../../shared/prices"); const { getProvider } = require("../../shared/balances"); const { isScamAddress } = require("../../shared/scamlist"); +const { hasZeroTransactionHistory } = require("../../shared/transactions"); const { ERC20_ABI } = require("../../shared/constants"); const { log } = require("../../shared/log"); const makeBlockie = require("ethereum-blockies-base64"); @@ -244,6 +245,7 @@ function show(txInfo) { showView("confirm-tx"); estimateGas(txInfo); + checkRecipientHistory(txInfo); } async function estimateGas(txInfo) { @@ -286,6 +288,23 @@ async function estimateGas(txInfo) { } } +async function checkRecipientHistory(txInfo) { + const isNew = await hasZeroTransactionHistory( + txInfo.to, + state.blockscoutUrl, + ); + if (!isNew) return; + + const warningsEl = $("confirm-warnings"); + const warningHtml = + `
` + + `WARNING: This address has ZERO transaction history. ` + + `It has never sent or received any funds. ` + + `Double-check the address before sending.
`; + warningsEl.innerHTML = warningHtml + warningsEl.innerHTML; + warningsEl.classList.remove("hidden"); +} + function init(ctx) { $("btn-confirm-send").addEventListener("click", async () => { const password = $("confirm-tx-password").value; diff --git a/src/shared/transactions.js b/src/shared/transactions.js index ad45698..8dd00dd 100644 --- a/src/shared/transactions.js +++ b/src/shared/transactions.js @@ -251,4 +251,40 @@ function filterTransactions(txs, filters = {}) { return { transactions: filtered, newFraudContracts: newFraud }; } -module.exports = { fetchRecentTransactions, filterTransactions }; +/** + * Check whether an address has any on-chain transaction history. + * Returns true if the address has zero normal transactions AND zero + * token transfers on the configured Blockscout instance. + * Returns false on network errors (fail-open: don't block sends). + */ +async function hasZeroTransactionHistory(address, blockscoutUrl) { + try { + const resp = await debugFetch( + blockscoutUrl + "/addresses/" + address + "/transactions?limit=1", + ); + if (!resp.ok) return false; + const json = await resp.json(); + if ((json.items || []).length > 0) return false; + + // Also check token transfers — an address may have only received + // ERC-20 tokens without any native ETH transactions. + const ttResp = await debugFetch( + blockscoutUrl + + "/addresses/" + + address + + "/token-transfers?type=ERC-20&limit=1", + ); + if (!ttResp.ok) return false; + const ttJson = await ttResp.json(); + return (ttJson.items || []).length === 0; + } catch (e) { + log.errorf("hasZeroTransactionHistory check failed:", e.message); + return false; + } +} + +module.exports = { + fetchRecentTransactions, + filterTransactions, + hasZeroTransactionHistory, +};