From df95522f483cab866fb129624f28e0f120599f80 Mon Sep 17 00:00:00 2001 From: sneak Date: Thu, 26 Feb 2026 12:43:11 +0700 Subject: [PATCH] Disable send button after broadcast, show tx link and elapsed timer Once a transaction is successfully broadcast: disable the send button to prevent double-sends, immediately show an etherscan link for the pending tx, and display a live elapsed-time counter that updates every second until confirmation arrives. --- src/popup/views/confirmTx.js | 42 +++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/src/popup/views/confirmTx.js b/src/popup/views/confirmTx.js index 3068e97..30e1d64 100644 --- a/src/popup/views/confirmTx.js +++ b/src/popup/views/confirmTx.js @@ -19,6 +19,7 @@ const { getProvider } = require("../../shared/balances"); const { isScamAddress } = require("../../shared/scamlist"); let pendingTx = null; +let elapsedTimer = null; function show(txInfo) { pendingTx = txInfo; @@ -172,8 +173,43 @@ function init(ctx) { to: pendingTx.to, value: parseEther(pendingTx.amount), }); - statusEl.textContent = "Sent. Waiting for confirmation..."; + + // Disable send button immediately after broadcast + const sendBtn = $("btn-confirm-send"); + sendBtn.disabled = true; + sendBtn.classList.add("text-muted"); + + // Show etherscan link and elapsed timer + const broadcastTime = Date.now(); + statusEl.innerHTML = ""; + statusEl.appendChild( + document.createTextNode( + "Broadcast. Waiting for confirmation... ", + ), + ); + const timerSpan = document.createElement("span"); + timerSpan.textContent = "(0s)"; + statusEl.appendChild(timerSpan); + statusEl.appendChild(document.createElement("br")); + const txLink = document.createElement("a"); + txLink.href = "https://etherscan.io/tx/" + tx.hash; + txLink.target = "_blank"; + txLink.rel = "noopener"; + txLink.className = "underline decoration-dashed break-all"; + txLink.textContent = tx.hash; + statusEl.appendChild(document.createTextNode("Tx: ")); + statusEl.appendChild(txLink); + + if (elapsedTimer) clearInterval(elapsedTimer); + elapsedTimer = setInterval(() => { + const elapsed = Math.floor((Date.now() - broadcastTime) / 1000); + timerSpan.textContent = "(" + elapsed + "s)"; + }, 1000); + const receipt = await tx.wait(); + clearInterval(elapsedTimer); + elapsedTimer = null; + statusEl.innerHTML = ""; statusEl.appendChild( document.createTextNode( @@ -189,6 +225,10 @@ function init(ctx) { statusEl.appendChild(link); ctx.doRefreshAndRender(); } catch (e) { + if (elapsedTimer) { + clearInterval(elapsedTimer); + elapsedTimer = null; + } statusEl.textContent = "Failed: " + (e.shortMessage || e.message); } });