Compare commits

...

6 Commits

Author SHA1 Message Date
b9c3d99fed fix: align receive view address display with addressDetail pattern
All checks were successful
check / check (push) Successful in 22s
The receive view was using formatAddressHtml() which rendered addresses
with a different structure (bold title label above address) compared to
addressDetail's pattern (separate dot + full address + etherscan link
inline elements). This violated the display consistency policy.

Now uses the same element structure as addressDetail: separate
receive-address-dot, receive-address-full, and receive-etherscan-link
spans, with ENS name shown in a separate element matching addressDetail.

Closes #58
2026-02-28 11:59:42 -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
24464ffe33 Merge pull request 'fix: resolve token symbols from multiple sources (closes #51)' (#52) from fix/usdc-symbol-display into main
All checks were successful
check / check (push) Successful in 9s
Reviewed-on: #52
2026-02-28 20:40:43 +01:00
34c66d19c4 Merge branch 'main' into fix/usdc-symbol-display
All checks were successful
check / check (push) Successful in 22s
2026-02-28 20:40:10 +01:00
clawbot
9a7aa1f4fc fix: resolve token symbols from multiple sources (closes #51)
All checks were successful
check / check (push) Successful in 22s
When tokenBalances doesn't contain an entry for a token (e.g. before
balances are fetched), the symbol fell back to '?' in addressToken
and send views.

Add resolveSymbol() helper that checks tokenBalances → TOKEN_BY_ADDRESS
(known tokens) → trackedTokens → truncated address as last resort.

Fixes USDC and other known tokens showing '?' when balance data
hasn't loaded yet.
2026-02-28 11:37:38 -08:00
5 changed files with 68 additions and 32 deletions

View File

@@ -637,9 +637,16 @@
<div class="flex justify-center mb-3"> <div class="flex justify-center mb-3">
<canvas id="receive-qr"></canvas> <canvas id="receive-qr"></canvas>
</div> </div>
<div class="border border-border p-2 break-all mb-3 text-xs"> <div
<span id="receive-dot"></span> id="receive-ens"
<span id="receive-address" class="select-all"></span> class="font-bold mb-1 hidden flex items-center"
></div>
<div
class="border border-border p-2 break-all mb-3 text-xs cursor-pointer"
title="Click to copy"
>
<span id="receive-address-dot"></span>
<span id="receive-address-full"></span>
<span id="receive-etherscan-link"></span> <span id="receive-etherscan-link"></span>
</div> </div>
<button <button

View File

@@ -12,7 +12,7 @@ const {
balanceLine, balanceLine,
} = require("./helpers"); } = require("./helpers");
const { state, currentAddress, saveState } = require("../../shared/state"); const { state, currentAddress, saveState } = require("../../shared/state");
const { TOKEN_BY_ADDRESS } = require("../../shared/tokenList"); const { TOKEN_BY_ADDRESS, resolveSymbol } = require("../../shared/tokenList");
const { const {
formatUsd, formatUsd,
getPrice, getPrice,
@@ -96,14 +96,11 @@ function show() {
const tb = (addr.tokenBalances || []).find( const tb = (addr.tokenBalances || []).find(
(t) => t.address.toLowerCase() === tokenId.toLowerCase(), (t) => t.address.toLowerCase() === tokenId.toLowerCase(),
); );
const tracked = (state.trackedTokens || []).find( symbol = resolveSymbol(
(t) => t.address.toLowerCase() === tokenId.toLowerCase(), tokenId,
addr.tokenBalances,
state.trackedTokens,
); );
symbol =
(tb && tb.symbol) ||
(tracked && tracked.symbol) ||
(knownToken && knownToken.symbol) ||
"?";
amount = tb ? parseFloat(tb.balance || "0") : 0; amount = tb ? parseFloat(tb.balance || "0") : 0;
price = getPrice(symbol); price = getPrice(symbol);
} }

View File

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

View File

@@ -10,7 +10,7 @@ const {
const { state, currentAddress } = require("../../shared/state"); const { state, currentAddress } = require("../../shared/state");
let ctx; let ctx;
const { getProvider } = require("../../shared/balances"); const { getProvider } = require("../../shared/balances");
const { KNOWN_SYMBOLS, TOKEN_BY_ADDRESS } = require("../../shared/tokenList"); const { KNOWN_SYMBOLS, resolveSymbol } = require("../../shared/tokenList");
const EXT_ICON = const EXT_ICON =
`<span style="display:inline-block;width:10px;height:10px;margin-left:4px;vertical-align:middle">` + `<span style="display:inline-block;width:10px;height:10px;margin-left:4px;vertical-align:middle">` +
@@ -73,15 +73,11 @@ function updateSendBalance() {
const tb = (addr.tokenBalances || []).find( const tb = (addr.tokenBalances || []).find(
(t) => t.address.toLowerCase() === token.toLowerCase(), (t) => t.address.toLowerCase() === token.toLowerCase(),
); );
const knownToken = TOKEN_BY_ADDRESS.get(token.toLowerCase()); const symbol = resolveSymbol(
const tracked = (state.trackedTokens || []).find( token,
(t) => t.address.toLowerCase() === token.toLowerCase(), addr.tokenBalances,
state.trackedTokens,
); );
const symbol =
(tb && tb.symbol) ||
(tracked && tracked.symbol) ||
(knownToken && knownToken.symbol) ||
"?";
const bal = tb ? tb.balance || "0" : "0"; const bal = tb ? tb.balance || "0" : "0";
$("send-balance").textContent = $("send-balance").textContent =
"Current balance: " + bal + " " + symbol; "Current balance: " + bal + " " + symbol;
@@ -132,15 +128,11 @@ function init(_ctx) {
const tb = (addr.tokenBalances || []).find( const tb = (addr.tokenBalances || []).find(
(t) => t.address.toLowerCase() === token.toLowerCase(), (t) => t.address.toLowerCase() === token.toLowerCase(),
); );
const knownTk = TOKEN_BY_ADDRESS.get(token.toLowerCase()); tokenSymbol = resolveSymbol(
const trackedTk = (state.trackedTokens || []).find( token,
(t) => t.address.toLowerCase() === token.toLowerCase(), addr.tokenBalances,
state.trackedTokens,
); );
tokenSymbol =
(tb && tb.symbol) ||
(trackedTk && trackedTk.symbol) ||
(knownTk && knownTk.symbol) ||
"?";
tokenBalance = tb ? tb.balance || "0" : "0"; tokenBalance = tb ? tb.balance || "0" : "0";
} }

View File

@@ -3645,10 +3645,27 @@ async function getTopTokenPrices(n) {
return prices; return prices;
} }
// Resolve a token symbol from multiple sources, never returning "?".
function resolveSymbol(tokenAddress, tokenBalances, trackedTokens) {
const lower = (tokenAddress || "").toLowerCase();
const tb = (tokenBalances || []).find(
(t) => t.address.toLowerCase() === lower,
);
if (tb && tb.symbol) return tb.symbol;
const known = TOKEN_BY_ADDRESS.get(lower);
if (known && known.symbol) return known.symbol;
const tracked = (trackedTokens || []).find(
(t) => t.address.toLowerCase() === lower,
);
if (tracked && tracked.symbol) return tracked.symbol;
return lower.slice(0, 10) + "\u2026";
}
module.exports = { module.exports = {
TOKENS, TOKENS,
TOKEN_BY_ADDRESS, TOKEN_BY_ADDRESS,
KNOWN_SYMBOLS, KNOWN_SYMBOLS,
getTopTokens, getTopTokens,
getTopTokenPrices, getTopTokenPrices,
resolveSymbol,
}; };