Show tracked tokens with zero balance on main and address pages
Some checks failed
check / check (push) Has been cancelled

Add showZeroBalanceTokens setting (default: on). When enabled,
balanceLinesForAddress merges state.trackedTokens with the address's
tokenBalances, showing 0.0000 lines for tracked tokens that have no
balance on that address. This gives users visibility into all tokens
they're watching across all addresses.
This commit is contained in:
2026-02-26 15:37:39 +07:00
parent 6c4b2753d9
commit 47e690f466
6 changed files with 45 additions and 4 deletions

View File

@@ -502,6 +502,19 @@
</button> </button>
</div> </div>
<div class="bg-well p-3 mx-1 mb-3">
<h3 class="font-bold mb-1">Display</h3>
<label
class="text-xs flex items-center gap-1 cursor-pointer"
>
<input
type="checkbox"
id="settings-show-zero-balances"
/>
Show tracked tokens with zero balance
</label>
</div>
<div class="bg-well p-3 mx-1 mb-3"> <div class="bg-well p-3 mx-1 mb-3">
<h3 class="font-bold mb-1">Ethereum RPC</h3> <h3 class="font-bold mb-1">Ethereum RPC</h3>
<p class="text-xs text-muted mb-1"> <p class="text-xs text-muted mb-1">

View File

@@ -38,7 +38,11 @@ function show() {
} else { } else {
ensEl.classList.add("hidden"); ensEl.classList.add("hidden");
} }
$("address-balances").innerHTML = balanceLinesForAddress(addr); $("address-balances").innerHTML = balanceLinesForAddress(
addr,
state.trackedTokens,
state.showZeroBalanceTokens,
);
renderSendTokenSelect(addr); renderSendTokenSelect(addr);
$("tx-list").innerHTML = $("tx-list").innerHTML =
'<div class="text-muted text-xs py-1">Loading...</div>'; '<div class="text-muted text-xs py-1">Loading...</div>';

View File

@@ -85,16 +85,24 @@ function balanceLine(symbol, amount, price) {
); );
} }
function balanceLinesForAddress(addr) { function balanceLinesForAddress(addr, trackedTokens, showZero) {
let html = balanceLine( let html = balanceLine(
"ETH", "ETH",
parseFloat(addr.balance || "0"), parseFloat(addr.balance || "0"),
getPrice("ETH"), getPrice("ETH"),
); );
const seen = new Set();
for (const t of addr.tokenBalances || []) { for (const t of addr.tokenBalances || []) {
const bal = parseFloat(t.balance || "0"); const bal = parseFloat(t.balance || "0");
if (bal === 0) continue; if (bal === 0 && !showZero) continue;
html += balanceLine(t.symbol, bal, getPrice(t.symbol)); html += balanceLine(t.symbol, bal, getPrice(t.symbol));
seen.add(t.address.toLowerCase());
}
if (showZero && trackedTokens) {
for (const t of trackedTokens) {
if (seen.has(t.address.toLowerCase())) continue;
html += balanceLine(t.symbol, 0, getPrice(t.symbol));
}
} }
return html; return html;
} }

View File

@@ -57,7 +57,11 @@ function render(ctx) {
html += `<span class="flex items-center" style="width:42ch;max-width:100%">${addr.ensName ? "" : dot}${truncateMiddle(addr.address, 40)}</span>`; html += `<span class="flex items-center" style="width:42ch;max-width:100%">${addr.ensName ? "" : dot}${truncateMiddle(addr.address, 40)}</span>`;
html += `<span class="text-right text-muted flex-1">${addrUsd}</span>`; html += `<span class="text-right text-muted flex-1">${addrUsd}</span>`;
html += `</div>`; html += `</div>`;
html += balanceLinesForAddress(addr); html += balanceLinesForAddress(
addr,
state.trackedTokens,
state.showZeroBalanceTokens,
);
html += `</div>`; html += `</div>`;
}); });

View File

@@ -113,6 +113,12 @@ function init(ctx) {
showFlash("Saved."); showFlash("Saved.");
}); });
$("settings-show-zero-balances").checked = state.showZeroBalanceTokens;
$("settings-show-zero-balances").addEventListener("change", async () => {
state.showZeroBalanceTokens = $("settings-show-zero-balances").checked;
await saveState();
});
$("settings-hide-low-holders").checked = state.hideLowHolderTokens; $("settings-hide-low-holders").checked = state.hideLowHolderTokens;
$("settings-hide-low-holders").addEventListener("change", async () => { $("settings-hide-low-holders").addEventListener("change", async () => {
state.hideLowHolderTokens = $("settings-hide-low-holders").checked; state.hideLowHolderTokens = $("settings-hide-low-holders").checked;

View File

@@ -18,6 +18,7 @@ const DEFAULT_STATE = {
allowedSites: {}, allowedSites: {},
deniedSites: {}, deniedSites: {},
rememberSiteChoice: true, rememberSiteChoice: true,
showZeroBalanceTokens: true,
hideLowHolderTokens: true, hideLowHolderTokens: true,
hideFraudContracts: true, hideFraudContracts: true,
hideDustTransactions: true, hideDustTransactions: true,
@@ -44,6 +45,7 @@ async function saveState() {
allowedSites: state.allowedSites, allowedSites: state.allowedSites,
deniedSites: state.deniedSites, deniedSites: state.deniedSites,
rememberSiteChoice: state.rememberSiteChoice, rememberSiteChoice: state.rememberSiteChoice,
showZeroBalanceTokens: state.showZeroBalanceTokens,
hideLowHolderTokens: state.hideLowHolderTokens, hideLowHolderTokens: state.hideLowHolderTokens,
hideFraudContracts: state.hideFraudContracts, hideFraudContracts: state.hideFraudContracts,
hideDustTransactions: state.hideDustTransactions, hideDustTransactions: state.hideDustTransactions,
@@ -78,6 +80,10 @@ async function loadState() {
saved.rememberSiteChoice !== undefined saved.rememberSiteChoice !== undefined
? saved.rememberSiteChoice ? saved.rememberSiteChoice
: true; : true;
state.showZeroBalanceTokens =
saved.showZeroBalanceTokens !== undefined
? saved.showZeroBalanceTokens
: true;
state.hideLowHolderTokens = state.hideLowHolderTokens =
saved.hideLowHolderTokens !== undefined saved.hideLowHolderTokens !== undefined
? saved.hideLowHolderTokens ? saved.hideLowHolderTokens