From 6c686997f394b2401b957a5b6e6a90171035d870 Mon Sep 17 00:00:00 2001 From: user Date: Sat, 28 Feb 2026 14:06:34 -0800 Subject: [PATCH] feat: show red warning on confirm-tx for addresses with zero tx history Closes #82 Adds a Blockscout-backed check that warns users when sending to an address with no prior transaction history. The warning appears in red on the confirmation screen. The check is async and fail-open (errors are silently ignored to avoid blocking legitimate transactions). --- src/popup/views/confirmTx.js | 22 ++++++++++++++++++++++ src/shared/transactions.js | 26 +++++++++++++++++++++++++- 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/src/popup/views/confirmTx.js b/src/popup/views/confirmTx.js index 522e9e5..b3f4297 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 { hasZeroTxHistory } = 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); + checkNewAddress(txInfo); } async function estimateGas(txInfo) { @@ -286,6 +288,26 @@ async function estimateGas(txInfo) { } } +async function checkNewAddress(txInfo) { + try { + const isNew = await hasZeroTxHistory(txInfo.to, state.blockscoutUrl); + if (!isNew) return; + // Only append if we're still on the same pending tx + if (pendingTx !== txInfo) return; + const warningsEl = $("confirm-warnings"); + const div = document.createElement("div"); + div.className = + "border border-red-500 border-dashed p-2 mb-1 text-xs font-bold text-red-500"; + div.textContent = + "WARNING: This address has ZERO transaction history. " + + "Double-check that you have the correct address before sending."; + warningsEl.appendChild(div); + warningsEl.classList.remove("hidden"); + } catch (e) { + // Fail silently — don't block the transaction + } +} + 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..ed3d8ce 100644 --- a/src/shared/transactions.js +++ b/src/shared/transactions.js @@ -251,4 +251,28 @@ function filterTransactions(txs, filters = {}) { return { transactions: filtered, newFraudContracts: newFraud }; } -module.exports = { fetchRecentTransactions, filterTransactions }; +// Check whether an address has zero transaction history via Blockscout +// counters endpoint. Returns true if the address has never appeared in +// any transaction (sent or received), false otherwise. On fetch errors +// returns false (fail-open: don't show a spurious warning). +async function hasZeroTxHistory(address, blockscoutUrl) { + try { + const resp = await fetch( + blockscoutUrl + "/addresses/" + address + "/counters", + ); + if (!resp.ok) return false; + const data = await resp.json(); + const txCount = parseInt(data.transactions_count || "0", 10); + const transferCount = parseInt(data.token_transfers_count || "0", 10); + return txCount === 0 && transferCount === 0; + } catch (e) { + log.errorf("hasZeroTxHistory check failed:", e.message); + return false; + } +} + +module.exports = { + fetchRecentTransactions, + filterTransactions, + hasZeroTxHistory, +}; -- 2.49.1