Compare commits

...

4 Commits

Author SHA1 Message Date
user
d2f7284975 fix: persist confirm-tx view across popup close/reopen
All checks were successful
check / check (push) Successful in 22s
The confirm-tx view was not in RESTORABLE_VIEWS, so closing and
reopening the popup would fall back to the main view, losing the
pending transaction confirmation.

Save pendingTx data to state.viewData when showing confirm-tx and
add confirm-tx to the set of restorable views with proper restore
logic.

Closes #77
2026-02-28 12:25:55 -08:00
9444b06b52 Merge pull request 'fix: show token transaction history on address-token view (closes #72)' (#76) from fix/72-address-token-tx-history into main
All checks were successful
check / check (push) Successful in 9s
Reviewed-on: #76
2026-02-28 21:22:05 +01:00
c2fdb3e0c1 Merge branch 'main' into fix/72-address-token-tx-history
All checks were successful
check / check (push) Successful in 23s
2026-02-28 21:21:48 +01:00
user
4157732f4b fix: preserve multiple token transfers per tx hash for address-token view
All checks were successful
check / check (push) Successful in 21s
A single transaction (e.g. a DEX swap) can produce multiple ERC-20
token transfers. The transaction merger was keyed by tx hash alone,
so only the last token transfer survived. This meant the address-token
view's contract-address filter often matched nothing.

Use a composite key (hash + contract address) so all token transfers
are preserved. Also remove the bare normal-tx entry when it gets
replaced by token transfers to avoid duplicates.

Closes #72
2026-02-28 12:08:47 -08:00
3 changed files with 24 additions and 5 deletions

View File

@@ -75,6 +75,7 @@ const RESTORABLE_VIEWS = new Set([
"settings", "settings",
"settings-addtoken", "settings-addtoken",
"transaction", "transaction",
"confirm-tx",
"success-tx", "success-tx",
"error-tx", "error-tx",
]); ]);
@@ -134,6 +135,13 @@ function restoreView() {
fallbackView(); fallbackView();
} }
break; break;
case "confirm-tx":
if (state.viewData && state.viewData.pendingTx) {
confirmTx.show(state.viewData.pendingTx);
} else {
fallbackView();
}
break;
case "success-tx": case "success-tx":
if (state.viewData && state.viewData.hash) { if (state.viewData && state.viewData.hash) {
txStatus.renderSuccess(); txStatus.renderSuccess();

View File

@@ -18,7 +18,7 @@ const {
addressDotHtml, addressDotHtml,
escapeHtml, escapeHtml,
} = require("./helpers"); } = require("./helpers");
const { state } = require("../../shared/state"); const { state, saveState } = require("../../shared/state");
const { getSignerForAddress } = require("../../shared/wallet"); const { getSignerForAddress } = require("../../shared/wallet");
const { decryptWithPassword } = require("../../shared/vault"); const { decryptWithPassword } = require("../../shared/vault");
const { formatUsd, getPrice } = require("../../shared/prices"); const { formatUsd, getPrice } = require("../../shared/prices");
@@ -219,6 +219,10 @@ function show(txInfo) {
$("confirm-fee-amount").textContent = "Estimating..."; $("confirm-fee-amount").textContent = "Estimating...";
showView("confirm-tx"); showView("confirm-tx");
// Persist txInfo so the view survives popup close/reopen
state.viewData = { pendingTx: txInfo };
saveState();
estimateGas(txInfo); estimateGas(txInfo);
} }

View File

@@ -153,9 +153,11 @@ async function fetchRecentTransactions(address, blockscoutUrl, count = 25) {
// When a token transfer shares a hash with a normal tx, the normal tx // 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 // 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. A single transaction (e.g. a swap) can produce
// but preserve contract call metadata (direction, label, method) so // multiple token transfers (one per token involved), so we key token
// swaps and other contract interactions display correctly. // transfers by hash + contract address to keep all of them. We also
// preserve contract-call metadata (direction, label, method) from the
// matching normal tx so swaps display correctly.
for (const tt of ttJson.items || []) { for (const tt of ttJson.items || []) {
const parsed = parseTokenTransfer(tt, addrLower); const parsed = parseTokenTransfer(tt, addrLower);
const existing = txsByHash.get(parsed.hash); const existing = txsByHash.get(parsed.hash);
@@ -164,8 +166,13 @@ async function fetchRecentTransactions(address, blockscoutUrl, count = 25) {
parsed.directionLabel = existing.directionLabel; parsed.directionLabel = existing.directionLabel;
parsed.isContractCall = true; parsed.isContractCall = true;
parsed.method = existing.method; parsed.method = existing.method;
// Remove the bare-hash normal tx so it doesn't appear as a
// duplicate with empty value; token transfers replace it.
txsByHash.delete(parsed.hash);
} }
txsByHash.set(parsed.hash, parsed); // Use composite key so multiple token transfers per tx are kept.
const ttKey = parsed.hash + ":" + (parsed.contractAddress || "");
txsByHash.set(ttKey, parsed);
} }
const txs = [...txsByHash.values()]; const txs = [...txsByHash.values()];