Compare commits

...

3 Commits

Author SHA1 Message Date
user
256acbeeec fix: preserve multiple token transfers per tx hash in transaction history
All checks were successful
check / check (push) Successful in 22s
When a swap produces multiple ERC-20 token transfers with the same tx
hash (e.g. send WETH + receive USDC), only the last transfer was kept
because txsByHash was keyed by bare tx hash. This caused the
address-token view to show no transactions for tokens obtained via swap.

Use a composite key (hash:contractAddress) so all token transfers are
preserved. The bare-hash normal-tx entry is removed when token transfers
replace it to avoid duplication.

Closes #72
2026-02-28 12:08:38 -08:00
e8ede7010a Merge pull request 'fix: use formatAddressHtml in receive view for display consistency' (#69) from fix/issue-58-receive-address-consistency into main
All checks were successful
check / check (push) Successful in 9s
Reviewed-on: #69
2026-02-28 20:57:08 +01:00
a2fbb0e30d fix: use formatAddressHtml in receive view for display consistency
All checks were successful
check / check (push) Successful in 22s
The receive view was using raw textContent and a manually constructed
color dot instead of the shared formatAddressHtml helper used by other
views. This violated the display consistency policy ('Same data
formatted identically across all screens').

Changes:
- Use formatAddressHtml() to render address with color dot, title
  (e.g. 'Wallet 1 — Address 1'), and ENS name — matching addressDetail
- Make the address block itself click-to-copy (matching policy:
  'Clicking any address copies the full untruncated value')
- Replace separate receive-dot/receive-address spans with a single
  receive-address-block element
- Address is still shown in full (no truncation) as appropriate for
  the receive view

Closes #58
2026-02-28 11:47:45 -08:00
3 changed files with 39 additions and 11 deletions

View File

@@ -637,9 +637,10 @@
<div class="flex justify-center mb-3">
<canvas id="receive-qr"></canvas>
</div>
<div class="border border-border p-2 break-all mb-3 text-xs">
<span id="receive-dot"></span>
<span id="receive-address" class="select-all"></span>
<div
class="border border-border p-2 break-all mb-3 text-xs cursor-pointer"
>
<span id="receive-address-block" class="select-all"></span>
<span id="receive-etherscan-link"></span>
</div>
<button

View File

@@ -1,4 +1,10 @@
const { $, showView, showFlash, addressDotHtml } = require("./helpers");
const {
$,
showView,
showFlash,
formatAddressHtml,
addressTitle,
} = require("./helpers");
const { state, currentAddress } = require("../../shared/state");
const QRCode = require("qrcode");
@@ -12,8 +18,12 @@ const EXT_ICON =
function show() {
const addr = currentAddress();
const address = addr ? addr.address : "";
$("receive-dot").innerHTML = address ? addressDotHtml(address) : "";
$("receive-address").textContent = address;
const title = address ? addressTitle(address, state.wallets) : null;
const ensName = addr ? addr.ensName || null : null;
$("receive-address-block").innerHTML = address
? formatAddressHtml(address, ensName, null, title)
: "";
$("receive-address-block").dataset.full = address;
const link = address ? `https://etherscan.io/address/${address}` : "";
$("receive-etherscan-link").innerHTML = link
? `<a href="${link}" target="_blank" rel="noopener" class="inline-flex items-center">${EXT_ICON}</a>`
@@ -50,8 +60,16 @@ function show() {
}
function init(ctx) {
$("receive-address-block").addEventListener("click", () => {
const addr = $("receive-address-block").dataset.full;
if (addr) {
navigator.clipboard.writeText(addr);
showFlash("Copied!");
}
});
$("btn-receive-copy").addEventListener("click", () => {
const addr = $("receive-address").textContent;
const addr = $("receive-address-block").dataset.full;
if (addr) {
navigator.clipboard.writeText(addr);
showFlash("Copied!");

View File

@@ -153,9 +153,14 @@ 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,
// but preserve contract call metadata (direction, label, method) so
// swaps and other contract interactions display correctly.
// amount and symbol. Preserve contract call metadata (direction, label,
// method) so swaps and other contract interactions display correctly.
//
// A single tx hash can produce multiple token transfers (e.g. a swap
// sends token A and receives token B). Use a composite key
// (hash:contractAddress) so every transfer is preserved. The original
// normal-tx entry (keyed by bare hash) is removed when at least one
// token transfer replaces it.
for (const tt of ttJson.items || []) {
const parsed = parseTokenTransfer(tt, addrLower);
const existing = txsByHash.get(parsed.hash);
@@ -164,8 +169,12 @@ async function fetchRecentTransactions(address, blockscoutUrl, count = 25) {
parsed.directionLabel = existing.directionLabel;
parsed.isContractCall = true;
parsed.method = existing.method;
// Remove the bare-hash normal tx so it isn't duplicated
txsByHash.delete(parsed.hash);
}
txsByHash.set(parsed.hash, parsed);
// Use composite key so multiple token transfers per tx are kept
const compositeKey = parsed.hash + ":" + (parsed.contractAddress || "");
txsByHash.set(compositeKey, parsed);
}
const txs = [...txsByHash.values()];