From 560065dd776f183245d39cd29806d181238e971c Mon Sep 17 00:00:00 2001 From: user Date: Fri, 27 Feb 2026 12:06:22 -0800 Subject: [PATCH 01/19] fix: show ERC-20 contract details in address-token view (closes #9) --- src/popup/index.html | 6 ++++ src/popup/views/addressToken.js | 51 +++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/src/popup/index.html b/src/popup/index.html index 6922bcd..889aa51 100644 --- a/src/popup/index.html +++ b/src/popup/index.html @@ -358,6 +358,12 @@ + + +
- - -
+ + +
diff --git a/src/popup/views/addressToken.js b/src/popup/views/addressToken.js index 11f377e..e7763df 100644 --- a/src/popup/views/addressToken.js +++ b/src/popup/views/addressToken.js @@ -142,30 +142,24 @@ function show() { const tokenHolders = tb && tb.holders != null ? tb.holders : null; const dot = addressDotHtml(tokenId); const tokenLink = `https://etherscan.io/token/${escapeHtml(tokenId)}`; - let infoHtml = - `
Contract Address
` + - `
${dot}` + + const knownToken = TOKEN_BY_ADDRESS.get(tokenId.toLowerCase()); + const projectUrl = knownToken && knownToken.url ? knownToken.url : null; + let infoHtml = `
Contract Address
`; + infoHtml += + `
${dot}` + `${escapeHtml(tokenId)}` + `${EXT_ICON}` + `
`; - const details = []; if (tokenName) - details.push(`Name: ${tokenName}`); + infoHtml += `
Name: ${tokenName}
`; if (tokenSymbol) - details.push( - `Symbol: ${tokenSymbol}`, - ); + infoHtml += `
Symbol: ${tokenSymbol}
`; if (tokenDecimals != null) - details.push( - `Decimals: ${tokenDecimals}`, - ); + infoHtml += `
Decimals: ${tokenDecimals}
`; if (tokenHolders != null) - details.push( - `Holders: ${Number(tokenHolders).toLocaleString()}`, - ); - if (details.length > 0) { - infoHtml += `
${details.join("")}
`; - } + infoHtml += `
Holders: ${Number(tokenHolders).toLocaleString()}
`; + if (projectUrl) + infoHtml += ``; contractInfo.innerHTML = infoHtml; contractInfo.classList.remove("hidden"); } else { From 8332570758569f77ad44629b58224f000b5f8d1b Mon Sep 17 00:00:00 2001 From: clawbot Date: Fri, 27 Feb 2026 12:27:23 -0800 Subject: [PATCH 04/19] fix: increase well horizontal margin to mx-4 per review --- src/popup/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/popup/index.html b/src/popup/index.html index ba10b29..e6562c2 100644 --- a/src/popup/index.html +++ b/src/popup/index.html @@ -377,7 +377,7 @@ From 76059c36745e253d2717bec59fdbca4b00127de9 Mon Sep 17 00:00:00 2001 From: clawbot Date: Fri, 27 Feb 2026 12:03:57 -0800 Subject: [PATCH 05/19] fix: display swaps and contract calls correctly in tx history (closes #3) - Preserve contract call metadata (direction, label, method) when token transfers merge with normal txs in fetchRecentTransactions - Handle 'contract' direction in counterparty display for home and address detail list views - Add decoded calldata display to transaction detail view, fetching raw input from Blockscout and using decodeCalldata from approval.js - Show 'Unknown contract call' with raw hex for unrecognized calldata - Export decodeCalldata from approval.js for reuse --- src/popup/index.html | 9 ++++ src/popup/views/addressDetail.js | 5 +- src/popup/views/approval.js | 2 +- src/popup/views/home.js | 5 +- src/popup/views/transactionDetail.js | 72 ++++++++++++++++++++++++++++ src/shared/transactions.js | 11 ++++- 6 files changed, 100 insertions(+), 4 deletions(-) diff --git a/src/popup/index.html b/src/popup/index.html index 889aa51..78fae1a 100644 --- a/src/popup/index.html +++ b/src/popup/index.html @@ -945,6 +945,15 @@
To
+
Transaction hash
diff --git a/src/popup/views/addressDetail.js b/src/popup/views/addressDetail.js index a91cf20..3f4f4f1 100644 --- a/src/popup/views/addressDetail.js +++ b/src/popup/views/addressDetail.js @@ -185,7 +185,10 @@ function renderTransactions(txs) { let html = ""; let i = 0; for (const tx of txs) { - const counterparty = tx.direction === "sent" ? tx.to : tx.from; + const counterparty = + tx.direction === "sent" || tx.direction === "contract" + ? tx.to + : tx.from; const ensName = ensNameMap.get(counterparty) || null; const dirLabel = tx.directionLabel; const amountStr = tx.value diff --git a/src/popup/views/approval.js b/src/popup/views/approval.js index 359b506..58319ed 100644 --- a/src/popup/views/approval.js +++ b/src/popup/views/approval.js @@ -453,4 +453,4 @@ function init(ctx) { }); } -module.exports = { init, show }; +module.exports = { init, show, decodeCalldata }; diff --git a/src/popup/views/home.js b/src/popup/views/home.js index 62b352b..78fbff2 100644 --- a/src/popup/views/home.js +++ b/src/popup/views/home.js @@ -102,7 +102,10 @@ function renderHomeTxList(ctx) { let html = ""; let i = 0; for (const tx of homeTxs) { - const counterparty = tx.direction === "sent" ? tx.to : tx.from; + const counterparty = + tx.direction === "sent" || tx.direction === "contract" + ? tx.to + : tx.from; const dirLabel = tx.directionLabel; const amountStr = tx.value ? escapeHtml(tx.value + " " + tx.symbol) diff --git a/src/popup/views/transactionDetail.js b/src/popup/views/transactionDetail.js index 8ecbfe3..53c2005 100644 --- a/src/popup/views/transactionDetail.js +++ b/src/popup/views/transactionDetail.js @@ -13,6 +13,8 @@ const { } = require("./helpers"); const { state } = require("../../shared/state"); const makeBlockie = require("ethereum-blockies-base64"); +const { log, debugFetch } = require("../../shared/log"); +const { decodeCalldata } = require("./approval"); const EXT_ICON = `` + @@ -85,9 +87,15 @@ function show(tx) { fromEns: tx.fromEns || null, toEns: tx.toEns || null, directionLabel: tx.directionLabel || null, + direction: tx.direction || null, + isContractCall: tx.isContractCall || false, + method: tx.method || null, }, }; render(); + if (tx.isContractCall || tx.direction === "contract") { + loadCalldata(tx.hash, tx.to); + } } function render() { @@ -121,6 +129,10 @@ function render() { nativeEl.parentElement.classList.add("hidden"); } + // Hide calldata section by default; loadCalldata will show it if needed + const calldataSection = $("tx-detail-calldata-section"); + if (calldataSection) calldataSection.classList.add("hidden"); + $("tx-detail-time").textContent = isoDate(tx.timestamp) + " (" + timeAgo(tx.timestamp) + ")"; $("tx-detail-status").textContent = tx.isError ? "Failed" : "Success"; @@ -137,6 +149,66 @@ function render() { }); } +function renderDecodedCalldata(decoded) { + let html = `
${escapeHtml(decoded.name)}
`; + if (decoded.description) { + html += `
${escapeHtml(decoded.description)}
`; + } + for (const detail of decoded.details || []) { + html += `
${escapeHtml(detail.label)}: `; + if (detail.address) { + const dot = addressDotHtml(detail.address); + html += `${dot}${copyableHtml(detail.value, "break-all")}`; + } else { + html += escapeHtml(detail.value); + } + html += `
`; + } + return html; +} + +async function loadCalldata(txHash, toAddress) { + const section = $("tx-detail-calldata-section"); + const container = $("tx-detail-calldata"); + if (!section || !container) return; + + try { + const resp = await debugFetch( + state.blockscoutUrl + "/transactions/" + txHash, + ); + if (!resp.ok) return; + const txData = await resp.json(); + const inputData = txData.raw_input || txData.input || null; + if (!inputData || inputData === "0x") return; + + const decoded = decodeCalldata(inputData, toAddress || ""); + if (decoded) { + container.innerHTML = renderDecodedCalldata(decoded); + } else { + const method = txData.method || "Unknown method"; + let html = `
Unknown contract call
`; + html += `
${escapeHtml(method)}
`; + const displayData = + inputData.length > 202 + ? inputData.slice(0, 202) + "…" + : inputData; + html += `
${copyableHtml(inputData, "break-all")}
`; + container.innerHTML = html; + } + section.classList.remove("hidden"); + + // Bind copy handlers for new elements + section.querySelectorAll("[data-copy]").forEach((el) => { + el.onclick = () => { + navigator.clipboard.writeText(el.dataset.copy); + showFlash("Copied!"); + }; + }); + } catch (e) { + log.errorf("loadCalldata failed:", e.message); + } +} + function init(_ctx) { ctx = _ctx; $("btn-tx-back").addEventListener("click", () => { diff --git a/src/shared/transactions.js b/src/shared/transactions.js index f926784..d58fb86 100644 --- a/src/shared/transactions.js +++ b/src/shared/transactions.js @@ -139,9 +139,18 @@ async function fetchRecentTransactions(address, blockscoutUrl, count = 25) { // When a token transfer shares a hash with a normal tx, the normal tx // is the contract call (0 ETH) and the token transfer has the real - // amount and symbol. Replace the normal tx with the token transfer. + // amount and symbol. Replace the normal tx with the token transfer, + // but preserve contract call metadata (direction, label, method) so + // swaps and other contract interactions display correctly. for (const tt of ttJson.items || []) { const parsed = parseTokenTransfer(tt, addrLower); + const existing = txsByHash.get(parsed.hash); + if (existing && existing.direction === "contract") { + parsed.direction = "contract"; + parsed.directionLabel = existing.directionLabel; + parsed.isContractCall = true; + parsed.method = existing.method; + } txsByHash.set(parsed.hash, parsed); } From 3fd3e30f44faef0e7adec0a0b429779987d0c2f1 Mon Sep 17 00:00:00 2001 From: clawbot Date: Fri, 27 Feb 2026 12:07:47 -0800 Subject: [PATCH 06/19] fix: label swap methods as "Swap" in tx lists, remove unused variable - Map known DEX methods (execute, swap, multicall, etc.) to "Swap" label instead of raw method name like "Execute" - Remove unused displayData variable in transactionDetail.js Addresses review feedback on PR #10. --- src/popup/views/transactionDetail.js | 4 ---- src/shared/transactions.js | 16 +++++++++++++++- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/popup/views/transactionDetail.js b/src/popup/views/transactionDetail.js index 53c2005..04c9859 100644 --- a/src/popup/views/transactionDetail.js +++ b/src/popup/views/transactionDetail.js @@ -188,10 +188,6 @@ async function loadCalldata(txHash, toAddress) { const method = txData.method || "Unknown method"; let html = `
Unknown contract call
`; html += `
${escapeHtml(method)}
`; - const displayData = - inputData.length > 202 - ? inputData.slice(0, 202) + "…" - : inputData; html += `
${copyableHtml(inputData, "break-all")}
`; container.innerHTML = html; } diff --git a/src/shared/transactions.js b/src/shared/transactions.js index d58fb86..e12d310 100644 --- a/src/shared/transactions.js +++ b/src/shared/transactions.js @@ -37,7 +37,21 @@ function parseTx(tx, addrLower) { if (token) { symbol = token.symbol; } - const label = method.charAt(0).toUpperCase() + method.slice(1); + // Map known DEX methods to "Swap" for cleaner display + const SWAP_METHODS = new Set([ + "execute", + "swap", + "swapExactTokensForTokens", + "swapTokensForExactTokens", + "swapExactETHForTokens", + "swapTokensForExactETH", + "swapExactTokensForETH", + "swapETHForExactTokens", + "multicall", + ]); + const label = SWAP_METHODS.has(method) + ? "Swap" + : method.charAt(0).toUpperCase() + method.slice(1); direction = "contract"; directionLabel = label; value = ""; From 8b7d73cc35db4c6c729f74a4f88532f740eb8d28 Mon Sep 17 00:00:00 2001 From: clawbot Date: Fri, 27 Feb 2026 12:34:23 -0800 Subject: [PATCH 07/19] fix: pass UUID approval ID as string, not parseInt (closes #4) The approval ID was changed from sequential integers to crypto.randomUUID() strings for security, but the popup still called parseInt() on it, which converted the UUID to NaN. This caused every approval lookup to fail, preventing the confirmation popup from displaying pending tx/sign requests. --- src/popup/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/popup/index.js b/src/popup/index.js index 9413805..5a12180 100644 --- a/src/popup/index.js +++ b/src/popup/index.js @@ -189,7 +189,7 @@ async function init() { const params = new URLSearchParams(window.location.search); const approvalId = params.get("approval"); if (approvalId) { - approval.show(parseInt(approvalId, 10)); + approval.show(approvalId); showView("approve-site"); return; } From 3b419c7517e7c15270bd23d039d3d956a46cbcf3 Mon Sep 17 00:00:00 2001 From: clawbot Date: Fri, 27 Feb 2026 12:50:26 -0800 Subject: [PATCH 08/19] fix: add missing TOKEN_BY_ADDRESS import in addressToken view --- src/popup/views/addressToken.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/popup/views/addressToken.js b/src/popup/views/addressToken.js index e7763df..1284029 100644 --- a/src/popup/views/addressToken.js +++ b/src/popup/views/addressToken.js @@ -11,6 +11,7 @@ const { balanceLine, } = require("./helpers"); const { state, currentAddress, saveState } = require("../../shared/state"); +const { TOKEN_BY_ADDRESS } = require("../../shared/tokenList"); const { formatUsd, getPrice, From 689bcbf1717c2884f521f74653bf3b452380c4ad Mon Sep 17 00:00:00 2001 From: user Date: Fri, 27 Feb 2026 12:11:46 -0800 Subject: [PATCH 09/19] feat: add wallet deletion from settings (closes #13) --- src/popup/index.html | 53 ++++++++++++++++++++++++++++ src/popup/views/settings.js | 69 +++++++++++++++++++++++++++++++++++++ 2 files changed, 122 insertions(+) diff --git a/src/popup/index.html b/src/popup/index.html index 889aa51..68edf66 100644 --- a/src/popup/index.html +++ b/src/popup/index.html @@ -843,6 +843,59 @@

+ +
+

Danger Zone

+

+ Permanently delete the currently selected wallet. This + cannot be undone. Make sure you have backed up your + recovery phrase before proceeding. +

+ +
+ +
diff --git a/src/popup/views/settings.js b/src/popup/views/settings.js index 8562b96..eb01cf3 100644 --- a/src/popup/views/settings.js +++ b/src/popup/views/settings.js @@ -2,6 +2,7 @@ const { $, showView, showFlash, escapeHtml } = require("./helpers"); const { state, saveState } = require("../../shared/state"); const { ETHEREUM_MAINNET_CHAIN_ID } = require("../../shared/constants"); const { log, debugFetch } = require("../../shared/log"); +const { decryptWithPassword } = require("../../shared/vault"); const runtime = typeof browser !== "undefined" ? browser.runtime : chrome.runtime; @@ -192,6 +193,74 @@ function init(ctx) { ctx.renderWalletList(); showView("main"); }); + + $("btn-delete-wallet").addEventListener("click", () => { + if (state.selectedWallet === null || state.wallets.length === 0) { + showFlash("No wallet selected."); + return; + } + const wallet = state.wallets[state.selectedWallet]; + $("delete-wallet-name").textContent = wallet.name || "this wallet"; + $("delete-wallet-password").value = ""; + $("delete-wallet-confirm").classList.remove("hidden"); + }); + + $("btn-delete-wallet-cancel").addEventListener("click", () => { + $("delete-wallet-confirm").classList.add("hidden"); + $("delete-wallet-password").value = ""; + }); + + $("btn-delete-wallet-confirm").addEventListener("click", async () => { + const pw = $("delete-wallet-password").value; + if (!pw) { + showFlash("Password required."); + return; + } + + const walletIdx = state.selectedWallet; + const wallet = state.wallets[walletIdx]; + + // Verify password against the wallet's encrypted data + try { + await decryptWithPassword(wallet.encrypted, pw); + } catch (_e) { + showFlash("Wrong password."); + return; + } + + // Collect addresses to clean up from allowedSites/deniedSites + const addresses = (wallet.addresses || []).map((a) => a.address); + + // Remove wallet + state.wallets.splice(walletIdx, 1); + + // Clean up site permissions for deleted addresses + for (const addr of addresses) { + delete state.allowedSites[addr]; + delete state.deniedSites[addr]; + } + + if (state.wallets.length === 0) { + // No wallets left — reset selection and show welcome + state.selectedWallet = null; + state.selectedAddress = null; + state.activeAddress = null; + await saveState(); + $("delete-wallet-confirm").classList.add("hidden"); + showView("welcome"); + } else { + // Switch to first wallet if deleted wallet was active + state.selectedWallet = 0; + state.selectedAddress = 0; + state.activeAddress = + state.wallets[0].addresses[0]?.address || null; + await saveState(); + $("delete-wallet-confirm").classList.add("hidden"); + showFlash("Wallet deleted."); + ctx.renderWalletList(); + showView("main"); + } + }); } module.exports = { init, show, renderSiteLists }; From 34cd72be88a5cf5471f390fe61e5cc077a1f8392 Mon Sep 17 00:00:00 2001 From: clawbot Date: Fri, 27 Feb 2026 12:44:40 -0800 Subject: [PATCH 10/19] fix: rework wallet deletion per review feedback - Remove all red/danger styling, use standard monochrome colors - Add wallet picker dropdown instead of relying on selectedWallet - Fix encryptedSecret field name (was wallet.encrypted) - Populate dropdown when settings view opens - Confirmation modal uses standard border styling --- src/popup/views/settings.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/popup/views/settings.js b/src/popup/views/settings.js index eb01cf3..a6f9083 100644 --- a/src/popup/views/settings.js +++ b/src/popup/views/settings.js @@ -71,6 +71,17 @@ function show() { $("settings-blockscout").value = state.blockscoutUrl; renderTrackedTokens(); renderSiteLists(); + // Populate wallet deletion dropdown + const sel = $("delete-wallet-select"); + sel.innerHTML = ""; + for (let i = 0; i < state.wallets.length; i++) { + const opt = document.createElement("option"); + opt.value = i; + opt.textContent = state.wallets[i].name || "Wallet " + (i + 1); + sel.appendChild(opt); + } + $("delete-wallet-confirm").classList.add("hidden"); + showView("settings"); } From 655b90c7dfa3341e1fa19727162776a3f8098fca Mon Sep 17 00:00:00 2001 From: clawbot Date: Fri, 27 Feb 2026 12:46:03 -0800 Subject: [PATCH 11/19] feat: add wallet deletion from settings (closes #13) - Per-wallet [delete] links in settings wallet list - Monochrome styling throughout, no red/danger colors - Password confirmation modal with warning text - Cleans up site permissions for deleted addresses - Switches to first remaining wallet or shows welcome if none left --- src/popup/index.html | 38 +++++----------------- src/popup/views/settings.js | 63 ++++++++++++++++++++++++------------- 2 files changed, 50 insertions(+), 51 deletions(-) diff --git a/src/popup/index.html b/src/popup/index.html index 68edf66..4a2b899 100644 --- a/src/popup/index.html +++ b/src/popup/index.html @@ -708,9 +708,7 @@

Wallets

-

- Add a new wallet from a recovery phrase or private key. -

+
-
-

Danger Zone

-

- Permanently delete the currently selected wallet. This - cannot be undone. Make sure you have backed up your - recovery phrase before proceeding. -

- -
- `; }); container.innerHTML = html; @@ -94,6 +94,38 @@ function renderWalletListSettings() { $("delete-wallet-confirm").classList.remove("hidden"); }); }); + + // Inline rename on click + container.querySelectorAll(".settings-wallet-name").forEach((span) => { + span.addEventListener("click", () => { + const idx = parseInt(span.dataset.idx, 10); + const wallet = state.wallets[idx]; + const input = document.createElement("input"); + input.type = "text"; + input.className = + "border border-border p-0 text-xs bg-bg text-fg w-full"; + input.value = wallet.name || "Wallet " + (idx + 1); + span.replaceWith(input); + input.focus(); + input.select(); + const finish = async () => { + const val = input.value.trim(); + if (val && val !== wallet.name) { + wallet.name = val; + await saveState(); + } + renderWalletListSettings(); + }; + input.addEventListener("blur", finish); + input.addEventListener("keydown", (e) => { + if (e.key === "Enter") input.blur(); + if (e.key === "Escape") { + input.value = wallet.name || "Wallet " + (idx + 1); + input.blur(); + } + }); + }); + }); } function show() { From aaeb38d7c664f465cc20bbf2ceb54e04df20bb0b Mon Sep 17 00:00:00 2001 From: user Date: Fri, 27 Feb 2026 13:00:07 -0800 Subject: [PATCH 13/19] fix: show Swap type label and heading on transaction detail page --- src/popup/index.html | 8 +++++++- src/popup/views/transactionDetail.js | 15 +++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/popup/index.html b/src/popup/index.html index 78fae1a..e0c6130 100644 --- a/src/popup/index.html +++ b/src/popup/index.html @@ -920,7 +920,13 @@ > < Back -

Transaction

+

+ Transaction +

+
Status
diff --git a/src/popup/views/transactionDetail.js b/src/popup/views/transactionDetail.js index 04c9859..70d4af2 100644 --- a/src/popup/views/transactionDetail.js +++ b/src/popup/views/transactionDetail.js @@ -129,6 +129,21 @@ function render() { nativeEl.parentElement.classList.add("hidden"); } + // Show type label for contract interactions (Swap, Execute, etc.) + const typeSection = $("tx-detail-type-section"); + const typeEl = $("tx-detail-type"); + const headingEl = $("tx-detail-heading"); + if (tx.direction === "contract" && tx.directionLabel) { + if (typeSection) { + typeEl.textContent = tx.directionLabel; + typeSection.classList.remove("hidden"); + } + if (headingEl) headingEl.textContent = tx.directionLabel; + } else { + if (typeSection) typeSection.classList.add("hidden"); + if (headingEl) headingEl.textContent = "Transaction"; + } + // Hide calldata section by default; loadCalldata will show it if needed const calldataSection = $("tx-detail-calldata-section"); if (calldataSection) calldataSection.classList.add("hidden"); From 8824237db622ffdf1f93f26d4389d32cf1ee6e6c Mon Sep 17 00:00:00 2001 From: user Date: Fri, 27 Feb 2026 13:01:53 -0800 Subject: [PATCH 14/19] fix: match approval view display consistency for decoded calldata - Restructured calldata section to use same well layout as approval view: Action label + bold name + structured details - Always show raw data section below decoded well - Unknown contract calls show method name in well instead of inline --- src/popup/index.html | 26 +++++++++--- src/popup/views/transactionDetail.js | 63 ++++++++++++++++------------ 2 files changed, 57 insertions(+), 32 deletions(-) diff --git a/src/popup/index.html b/src/popup/index.html index e0c6130..bc25e3d 100644 --- a/src/popup/index.html +++ b/src/popup/index.html @@ -952,13 +952,27 @@
+
Transaction hash
diff --git a/src/popup/views/transactionDetail.js b/src/popup/views/transactionDetail.js index 70d4af2..43d482d 100644 --- a/src/popup/views/transactionDetail.js +++ b/src/popup/views/transactionDetail.js @@ -164,28 +164,14 @@ function render() { }); } -function renderDecodedCalldata(decoded) { - let html = `
${escapeHtml(decoded.name)}
`; - if (decoded.description) { - html += `
${escapeHtml(decoded.description)}
`; - } - for (const detail of decoded.details || []) { - html += `
${escapeHtml(detail.label)}: `; - if (detail.address) { - const dot = addressDotHtml(detail.address); - html += `${dot}${copyableHtml(detail.value, "break-all")}`; - } else { - html += escapeHtml(detail.value); - } - html += `
`; - } - return html; -} - async function loadCalldata(txHash, toAddress) { const section = $("tx-detail-calldata-section"); - const container = $("tx-detail-calldata"); - if (!section || !container) return; + const actionEl = $("tx-detail-calldata-action"); + const detailsEl = $("tx-detail-calldata-details"); + const wellEl = $("tx-detail-calldata-well"); + const rawSection = $("tx-detail-rawdata-section"); + const rawEl = $("tx-detail-rawdata"); + if (!section || !actionEl || !detailsEl) return; try { const resp = await debugFetch( @@ -198,14 +184,39 @@ async function loadCalldata(txHash, toAddress) { const decoded = decodeCalldata(inputData, toAddress || ""); if (decoded) { - container.innerHTML = renderDecodedCalldata(decoded); + // Render decoded calldata matching approval view style + actionEl.textContent = decoded.name; + let detailsHtml = ""; + if (decoded.description) { + detailsHtml += `
${escapeHtml(decoded.description)}
`; + } + for (const d of decoded.details || []) { + detailsHtml += `
`; + detailsHtml += `
${escapeHtml(d.label)}
`; + if (d.address) { + const dot = addressDotHtml(d.address); + detailsHtml += `
${dot}${copyableHtml(d.value, "break-all")}
`; + } else { + detailsHtml += `
${escapeHtml(d.value)}
`; + } + detailsHtml += `
`; + } + detailsEl.innerHTML = detailsHtml; + if (wellEl) wellEl.classList.remove("hidden"); } else { - const method = txData.method || "Unknown method"; - let html = `
Unknown contract call
`; - html += `
${escapeHtml(method)}
`; - html += `
${copyableHtml(inputData, "break-all")}
`; - container.innerHTML = html; + // Unknown contract call — show method name in well + const method = txData.method || "Unknown contract call"; + actionEl.textContent = method; + detailsEl.innerHTML = ""; + if (wellEl) wellEl.classList.remove("hidden"); } + + // Always show raw data + if (rawSection && rawEl) { + rawEl.innerHTML = copyableHtml(inputData, "break-all"); + rawSection.classList.remove("hidden"); + } + section.classList.remove("hidden"); // Bind copy handlers for new elements From 593619967677c4a8f3eb3378e98c35431316ed7d Mon Sep 17 00:00:00 2001 From: clawbot Date: Fri, 27 Feb 2026 13:03:43 -0800 Subject: [PATCH 15/19] fix: place color dot next to address, not title, matching convention --- src/popup/views/transactionDetail.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/popup/views/transactionDetail.js b/src/popup/views/transactionDetail.js index 43d482d..1091005 100644 --- a/src/popup/views/transactionDetail.js +++ b/src/popup/views/transactionDetail.js @@ -44,11 +44,11 @@ function txAddressHtml(address, ensName, title) { const extLink = `${EXT_ICON}`; let html = `
${blockie}
`; if (title) { - html += `
${dot}${escapeHtml(title)}
`; + html += `
${escapeHtml(title)}
`; } if (ensName) { html += - `
${title ? "" : dot}` + + `
${dot}` + copyableHtml(ensName, "") + extLink + `
` + @@ -57,7 +57,7 @@ function txAddressHtml(address, ensName, title) { `
`; } else { html += - `
${title ? "" : dot}` + + `
${dot}` + copyableHtml(address, "break-all") + extLink + `
`; From 2b0b889b0118d82f765946f610f3adb28a5a68b5 Mon Sep 17 00:00:00 2001 From: clawbot Date: Fri, 27 Feb 2026 13:52:08 -0800 Subject: [PATCH 16/19] fix: use wallet.encryptedSecret not wallet.encrypted for password verify --- src/popup/views/settings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/popup/views/settings.js b/src/popup/views/settings.js index 535d38a..34a0de2 100644 --- a/src/popup/views/settings.js +++ b/src/popup/views/settings.js @@ -283,7 +283,7 @@ function init(ctx) { // Verify password against the wallet's encrypted data try { - await decryptWithPassword(wallet.encrypted, pw); + await decryptWithPassword(wallet.encryptedSecret, pw); } catch (_e) { showFlash("Wrong password."); return; From 2bffa910459fd8b97cbe5e74ffde75bc3f5f1a6c Mon Sep 17 00:00:00 2001 From: clawbot Date: Fri, 27 Feb 2026 13:54:19 -0800 Subject: [PATCH 17/19] fix: reduce contract info well margins to prevent address wrapping --- src/popup/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/popup/index.html b/src/popup/index.html index e6562c2..ba10b29 100644 --- a/src/popup/index.html +++ b/src/popup/index.html @@ -377,7 +377,7 @@ From 8893f5dce78608d6c7f2f019e52b91c0ace44ace Mon Sep 17 00:00:00 2001 From: clawbot Date: Fri, 27 Feb 2026 13:58:58 -0800 Subject: [PATCH 18/19] refactor: delete-wallet-confirm as standalone full view Replace the inline confirmation div at the bottom of Settings with a proper full-screen view (view-delete-wallet-confirm). This fixes the issue where the confirmation was offscreen on the 360x600 popup. - New view with back button, title, warning text, password input, and red-text Confirm Delete button - Dedicated flash area for password errors - New deleteWallet.js module with init/show pattern - Added delete-wallet-confirm to VIEWS array in helpers.js - Removed old inline confirmation HTML and logic from settings --- src/popup/index.html | 54 ++++++++++---------- src/popup/views/deleteWallet.js | 90 +++++++++++++++++++++++++++++++++ src/popup/views/helpers.js | 1 + src/popup/views/settings.js | 81 ++--------------------------- 4 files changed, 123 insertions(+), 103 deletions(-) create mode 100644 src/popup/views/deleteWallet.js diff --git a/src/popup/index.html b/src/popup/index.html index 4a2b899..0818067 100644 --- a/src/popup/index.html +++ b/src/popup/index.html @@ -841,39 +841,41 @@

+
-