1 Commits
Author SHA1 Message Date
sneak 49f4c9d759 fix: the address screen shows a token just added at its fetched balance, not 0.0000 (closes #516)
check / check (push) Canceled after 0s
e2e / e2e-chrome (push) Canceled after 0s
e2e / e2e-firefox (push) Canceled after 0s
A balance refresh that returns now draws the balances on AddressDetail or
AddressToken again when that screen is up, not only the wallet list. Their
transaction lists are left alone.

Every add path on AddToken and SettingsAddToken tracks a token through
trackToken(), which gives the tracked record `balanceNotFetched: true`. While
the field is set, the token's line reads `quantity unknown` instead of 0.0000.
refreshBalances() now resolves with the tracked tokens whose balance it read
at every address. The popup's refresh deletes the field from those tokens in
the stored record and leaves the rest of the stored list as it is. Removing
the token removes the field, and each network keeps its own records.

Model: opus-5-5
2026-11-02 23:24:57 +00:00
3 changed files with 3 additions and 29 deletions

No files matched your search

+1 -2
View File
@@ -1942,8 +1942,7 @@ view would leave a wallet one click from deletion.
- To: address or ENS name input, with an inline validation message - To: address or ENS name input, with an inline validation message
- Amount input with current balance display, which reads - Amount input with current balance display, which reads
`Current balance: unknown (SYMBOL)` for a token whose scale is unknown, as `Current balance: unknown (SYMBOL)` for a token whose scale is unknown, as
ConfirmTx's balance line does (see Unknown token scale). It reads the same ConfirmTx's balance line does (see Unknown token scale)
for a token whose balance no refresh has read yet (see AddToken)
- "Max" button beside the amount input, always in place. It fills in a - "Max" button beside the amount input, always in place. It fills in a
token's balance, cut down to the 18 decimal places ConfirmTx accepts for a token's balance, cut down to the 18 decimal places ConfirmTx accepts for a
token that has more, or for ETH the exact balance minus the network fee token that has more, or for ETH the exact balance minus the network fee
+2 -10
View File
@@ -198,21 +198,13 @@ function renderSendTokenSelect(addr) {
// null rather than "0": this screen states it as unknown, as the confirmation // null rather than "0": this screen states it as unknown, as the confirmation
// screen does, and Max fills nothing in for it. Only a stored quantity is // screen does, and Max fills nothing in for it. Only a stored quantity is
// withdrawn: the "0" for a token that has no row at all is an absence of // withdrawn: the "0" for a token that has no row at all is an absence of
// holdings, which is true at every scale. A token with no row whose tracked // holdings, which is true at every scale.
// record carries `balanceNotFetched` gets null instead, because no refresh has
// read its balance.
function tokenBalanceAndDecimals(addr, token) { function tokenBalanceAndDecimals(addr, token) {
const tb = (addr.tokenBalances || []).find( const tb = (addr.tokenBalances || []).find(
(t) => t.address.toLowerCase() === token.toLowerCase(), (t) => t.address.toLowerCase() === token.toLowerCase(),
); );
const tokenDecimals = resolveTokenDecimals(token, tokenSourcesByChainId()); const tokenDecimals = resolveTokenDecimals(token, tokenSourcesByChainId());
if (!tb) { if (!tb) return { tokenBalance: "0", tokenDecimals };
const tracked = state.trackedTokens.find(
(t) => t.address.toLowerCase() === token.toLowerCase(),
);
const notRead = tracked && tracked.balanceNotFetched;
return { tokenBalance: notRead ? null : "0", tokenDecimals };
}
if (tokenDecimals === null) return { tokenBalance: null, tokenDecimals }; if (tokenDecimals === null) return { tokenBalance: null, tokenDecimals };
return { tokenBalance: tb.balance ?? null, tokenDecimals }; return { tokenBalance: tb.balance ?? null, tokenDecimals };
} }
-17
View File
@@ -293,23 +293,6 @@ test("AddressToken for the new token reads quantity unknown until the refresh re
); );
}); });
test("Send for the new token reads its balance as unknown before any refresh has read it, and Max says so", async () => {
const page = await bootOnAddress();
const finish = holdNextRefresh(page);
await addNewToken(page);
await openToken(page, NEW_TOKEN.toLowerCase());
await page.click("btn-address-token-send");
expect(page.visibleViews()).toEqual(["send"]);
expect(page.text("send-balance")).toBe("Current balance: unknown (NEW)");
await page.click("btn-send-max");
expect(page.text("flash-msg")).toBe("This token's balance is unknown.");
await finish([MILLION]);
});
test("a token added by contract address on SettingsAddToken reads quantity unknown until the refresh returns", async () => { test("a token added by contract address on SettingsAddToken reads quantity unknown until the refresh returns", async () => {
const page = await bootPopup(unversionedValidProfile()); const page = await bootPopup(unversionedValidProfile());
expect(page.visibleViews()).toEqual(["main"]); expect(page.visibleViews()).toEqual(["main"]);