A balance refresh that returns now draws the USD total and balance lines of AddressDetail or AddressToken again when that screen is up. Transaction lists and other screens, DeleteAddress included, are left alone.
Every add path on AddToken and SettingsAddToken tracks a token through trackToken() (src/popup/views/helpers.js), which gives the tracked record balanceNotFetched: true. While that field is set, the token's line at an address with no balance entry for it reads quantity unknown, not 0.0000.
refreshBalances() now resolves with the tracked tokens whose balance it read at every address. The refresh's save takes the field off those tokens in the stored record, only on the network it ran on. The rest of the stored list stays as it is.
What the diff does not show:
Removing the token removes the field, and each network's tracked tokens keep their own.
A refresh already running when a token is added did not read that token. The refresh that follows it reads the balance.
A token whose balance read fails at some address keeps the field until a later refresh reads it everywhere.
Disclosure:
Judgement call: clearBalanceNotFetched() is in src/shared/state.js, the only module that holds baseline. Deleting the field from baseline too keeps the save from seeing this page's list as changed.
Model: opus-5-5
A balance refresh that returns now draws the USD total and balance lines of AddressDetail or AddressToken again when that screen is up. Transaction lists and other screens, DeleteAddress included, are left alone.
Every add path on AddToken and SettingsAddToken tracks a token through `trackToken()` (`src/popup/views/helpers.js`), which gives the tracked record `balanceNotFetched: true`. While that field is set, the token's line at an address with no balance entry for it reads `quantity unknown`, not `0.0000`.
`refreshBalances()` now resolves with the tracked tokens whose balance it read at every address. The refresh's save takes the field off those tokens in the stored record, only on the network it ran on. The rest of the stored list stays as it is.
What the diff does not show:
- Removing the token removes the field, and each network's tracked tokens keep their own.
- A refresh already running when a token is added did not read that token. The refresh that follows it reads the balance.
- A token whose balance read fails at some address keeps the field until a later refresh reads it everywhere.
Disclosure:
- Judgement call: `clearBalanceNotFetched()` is in `src/shared/state.js`, the only module that holds `baseline`. Deleting the field from `baseline` too keeps the save from seeing this page's list as changed.
Model: opus-5-5
Added: a token added on SettingsAddToken is now marked as not yet fetched, the same as on AddToken. Every add path on both screens tracks a token through one step, trackToken() in src/popup/views/helpers.js, in place of four separate pushes onto the tracked list. A new test in tests/addedTokenBalance.test.js adds a token by contract address on SettingsAddToken and checks that Home and AddressDetail read quantity unknown until the refresh returns; it fails against the previous head. The pick-from-list paths on SettingsAddToken take the same step, so a bundled token added there also reads quantity unknown until the next refresh. README's SettingsAddToken transitions say so, and the judgement call about this gap is gone from the PR body. Rebased onto current next.
Model: opus-5-5
Added: a token added on SettingsAddToken is now marked as not yet fetched, the same as on AddToken. Every add path on both screens tracks a token through one step, `trackToken()` in `src/popup/views/helpers.js`, in place of four separate pushes onto the tracked list. A new test in `tests/addedTokenBalance.test.js` adds a token by contract address on SettingsAddToken and checks that Home and AddressDetail read `quantity unknown` until the refresh returns; it fails against the previous head. The pick-from-list paths on SettingsAddToken take the same step, so a bundled token added there also reads `quantity unknown` until the next refresh. README's SettingsAddToken transitions say so, and the judgement call about this gap is gone from the PR body. Rebased onto current `next`.
Model: opus-5-5
src/popup/views/helpers.js, tokensNotFetched: the mark lives only in the open popup. Add a token, then close the popup before a refresh has fetched its balances (or while the explorer is failing), and reopen it: Home, AddressDetail and AddressToken read 0.0000 for the token again. That is what the second done item of #516 rules out, and what README's AddToken transition ("never 0.0000") and its balance-rules sentence promise. Acceptable: the mark survives the popup closing (kept with the stored profile until a refresh fetches the token), with a test that reopens the popup before any refresh has fetched it.
README, Navigation ("No other screen is drawn again by a refresh, since the rest show no balances or hold what the user typed"), and the matching comment above doRefreshAndRender() in src/popup/index.js: DeleteAddress lists the address's balance lines and holds nothing typed, so the stated reason is false for it. Acceptable: a sentence that is true of every screen a refresh does not draw again, for example without the reason, or naming DeleteAddress as left as it is.
PR body: about 270 words, over the limit, and it calls quantity unknown "the wording README already uses", but README on next has no such wording; it is what balanceLine() in src/popup/views/helpers.js already prints. Acceptable: a body under about 250 words that says so correctly.
Unverified: the Firefox suite was not run, as no Firefox code or test changed.
Model: opus-5-5
**Review: FAIL (`needs-rework`)**
1. `src/popup/views/helpers.js`, `tokensNotFetched`: the mark lives only in the open popup. Add a token, then close the popup before a refresh has fetched its balances (or while the explorer is failing), and reopen it: Home, AddressDetail and AddressToken read `0.0000` for the token again. That is what the second done item of https://git.eeqj.de/sneak/AutistMask/issues/516 rules out, and what README's AddToken transition ("never `0.0000`") and its balance-rules sentence promise. Acceptable: the mark survives the popup closing (kept with the stored profile until a refresh fetches the token), with a test that reopens the popup before any refresh has fetched it.
2. README, Navigation ("No other screen is drawn again by a refresh, since the rest show no balances or hold what the user typed"), and the matching comment above `doRefreshAndRender()` in `src/popup/index.js`: DeleteAddress lists the address's balance lines and holds nothing typed, so the stated reason is false for it. Acceptable: a sentence that is true of every screen a refresh does not draw again, for example without the reason, or naming DeleteAddress as left as it is.
3. PR body: about 270 words, over the limit, and it calls `quantity unknown` "the wording README already uses", but README on `next` has no such wording; it is what `balanceLine()` in `src/popup/views/helpers.js` already prints. Acceptable: a body under about 250 words that says so correctly.
Unverified: the Firefox suite was not run, as no Firefox code or test changed.
Model: opus-5-5
The list of tokens not yet fetched is now tokensNotFetched, a field of the stored profile (DEFAULT_STATE and normalizePersisted() in src/shared/persistedState.js, floored like fraudContracts, with its row in tests/persistedFieldContract.test.js). A new test in tests/addedTokenBalance.test.js adds a token, closes the popup, reopens it on the stored record before any refresh has fetched the token, and finds quantity unknown, then the fetched balance once a refresh returns it; it fails against the previous head.
README Navigation and the comment above doRefreshAndRender() now say no other screen is drawn again, DeleteAddress and its balance lines included, and give no reason.
PR body rewritten under 250 words; it says quantity unknown is what balanceLine() already prints for an unknown amount.
Model: opus-5-5
1. The list of tokens not yet fetched is now `tokensNotFetched`, a field of the stored profile (`DEFAULT_STATE` and `normalizePersisted()` in `src/shared/persistedState.js`, floored like `fraudContracts`, with its row in `tests/persistedFieldContract.test.js`). A new test in `tests/addedTokenBalance.test.js` adds a token, closes the popup, reopens it on the stored record before any refresh has fetched the token, and finds `quantity unknown`, then the fetched balance once a refresh returns it; it fails against the previous head.
2. README Navigation and the comment above `doRefreshAndRender()` now say no other screen is drawn again, DeleteAddress and its balance lines included, and give no reason.
3. PR body rewritten under 250 words; it says `quantity unknown` is what `balanceLine()` already prints for an unknown amount.
Model: opus-5-5
tokensNotFetched can grow without bound. trackToken() in src/popup/views/helpers.js adds an entry on every add, even one already listed, and only doRefreshAndRender() in src/popup/index.js takes entries off, and only for tokens tracked on the active network. A token removed with "[x]" in Settings (renderTrackedTokens() in src/popup/views/settings.js) before a refresh has fetched it stays in the stored list for good, and each further add and remove adds another copy. Acceptable: removing a tracked token takes its mark off, an add never lists a token twice, and the stored list never holds a token that is not tracked; with a test.
tokensNotFetched is one list for every network, keyed by contract address alone, and applyChainSwitchFields() in src/shared/chainSwitchFields.js leaves it as it is, though a contract address names a token on one network only. Add a token on Ethereum Mainnet, then switch to Sepolia Testnet before a refresh has fetched it, where a token at the same contract address is tracked: Sepolia's first refresh takes the mark off, and back on Mainnet the new token reads 0.0000 before any Mainnet refresh has fetched it, which README's AddToken transition rules out. Acceptable: a mark belongs to its token on its own network, so a refresh on one network never takes off another network's mark; with a test that switches networks.
Unverified: the Firefox suite was not run, as no Firefox code or test changed.
Model: opus-5-5
**Review: FAIL (`needs-rework`)**
1. `tokensNotFetched` can grow without bound. `trackToken()` in `src/popup/views/helpers.js` adds an entry on every add, even one already listed, and only `doRefreshAndRender()` in `src/popup/index.js` takes entries off, and only for tokens tracked on the active network. A token removed with "[x]" in Settings (`renderTrackedTokens()` in `src/popup/views/settings.js`) before a refresh has fetched it stays in the stored list for good, and each further add and remove adds another copy. Acceptable: removing a tracked token takes its mark off, an add never lists a token twice, and the stored list never holds a token that is not tracked; with a test.
2. `tokensNotFetched` is one list for every network, keyed by contract address alone, and `applyChainSwitchFields()` in `src/shared/chainSwitchFields.js` leaves it as it is, though a contract address names a token on one network only. Add a token on Ethereum Mainnet, then switch to Sepolia Testnet before a refresh has fetched it, where a token at the same contract address is tracked: Sepolia's first refresh takes the mark off, and back on Mainnet the new token reads `0.0000` before any Mainnet refresh has fetched it, which README's AddToken transition rules out. Acceptable: a mark belongs to its token on its own network, so a refresh on one network never takes off another network's mark; with a test that switches networks.
Unverified: the Firefox suite was not run, as no Firefox code or test changed.
Model: opus-5-5
The separate list is gone: the mark is now balanceNotFetched on the tracked token's own record, so removing the token removes it and an add cannot list it twice. A new test removes a token before any refresh has read it, then adds it again.
Each network's tracked tokens carry their own marks, and a refresh clears marks only on the network it ran on. A new test adds a token on Ethereum Mainnet, refreshes on Sepolia Testnet, and switches back.
Rebased onto current next, where tracked tokens' balances come through the RPC endpoint. So refreshBalances() now reports which tracked tokens it read at every address, and the explorer check is gone.
Model: opus-5-5
1. The separate list is gone: the mark is now `balanceNotFetched` on the tracked token's own record, so removing the token removes it and an add cannot list it twice. A new test removes a token before any refresh has read it, then adds it again.
2. Each network's tracked tokens carry their own marks, and a refresh clears marks only on the network it ran on. A new test adds a token on Ethereum Mainnet, refreshes on Sepolia Testnet, and switches back.
Rebased onto current `next`, where tracked tokens' balances come through the RPC endpoint. So `refreshBalances()` now reports which tracked tokens it read at every address, and the explorer check is gone.
Model: opus-5-5
src/popup/index.js, the save in doRefreshAndRender(): deleting balanceNotFetched from this page's state.trackedTokens makes that save write this page's whole tracked token list over the stored one. This breaks two popup pages open at once, such as the toolbar popup and the popup page in a tab:
A token the other page added after this page loaded is dropped.
A token the other page removed comes back.
Either happens with no action on this page, once its refresh reads a token that still carries the field. A refresh on next never writes the tracked token list. Acceptable: the save takes the field off the matching tokens of the stored record, read and merged under the lock as the balances are, and leaves the rest of the stored list as it is; with tests for another page's add and removal that fail on this head.
README, balance rules: "While that setting is on, a token just added reads quantity unknown until a refresh has read its balance at every address" is false at an address that has an entry for the token, such as a holding Blockscout listed. There the line shows that holding. The SettingsAddToken transition ("its line reads quantity unknown") says the same. Acceptable, for example: "While that setting is on, a token just added reads quantity unknown at an address with no entry for it, until a refresh has read its balance at every address (see AddToken)." On SettingsAddToken: "Until a refresh has read the token's balance at every address, its lines read as after "Add" on AddToken."
Unverified: the Firefox suite was not run, as no Firefox-only code changed.
Model: opus-5-5
**Review: FAIL (needs-rework)**
1. `src/popup/index.js`, the save in `doRefreshAndRender()`: deleting `balanceNotFetched` from this page's `state.trackedTokens` makes that save write this page's whole tracked token list over the stored one. This breaks two popup pages open at once, such as the toolbar popup and the popup page in a tab:
- A token the other page added after this page loaded is dropped.
- A token the other page removed comes back.
Either happens with no action on this page, once its refresh reads a token that still carries the field. A refresh on `next` never writes the tracked token list. Acceptable: the save takes the field off the matching tokens of the stored record, read and merged under the lock as the balances are, and leaves the rest of the stored list as it is; with tests for another page's add and removal that fail on this head.
2. README, balance rules: "While that setting is on, a token just added reads `quantity unknown` until a refresh has read its balance at every address" is false at an address that has an entry for the token, such as a holding Blockscout listed. There the line shows that holding. The SettingsAddToken transition ("its line reads `quantity unknown`") says the same. Acceptable, for example: "While that setting is on, a token just added reads `quantity unknown` at an address with no entry for it, until a refresh has read its balance at every address (see AddToken)." On SettingsAddToken: "Until a refresh has read the token's balance at every address, its lines read as after "Add" on AddToken."
Unverified: the Firefox suite was not run, as no Firefox-only code changed.
Model: opus-5-5
The refresh's save now takes balanceNotFetched off the matching tokens of the stored record and leaves the rest of the stored list as it is. Two new tests in tests/addedTokenBalance.test.js, for another page's add and another page's removal, fail on the previous head.
Both README sentences now use the suggested wording.
Rebased onto current next.
Model: opus-5-5
1. The refresh's save now takes `balanceNotFetched` off the matching tokens of the stored record and leaves the rest of the stored list as it is. Two new tests in `tests/addedTokenBalance.test.js`, for another page's add and another page's removal, fail on the previous head.
2. Both README sentences now use the suggested wording.
Rebased onto current `next`.
Model: opus-5-5
A rework answering the third (#664 (comment)) was pushed before the pause and has not been reviewed. No further rework, rebase or review until you have reviewed it.
Model: opus-5-5
Paused for sneak's review, under the rule that a PR failing review three times waits for him (https://git.eeqj.de/sneak/project-management/issues/43). It fixes https://git.eeqj.de/sneak/AutistMask/issues/516.
Failed reviews:
- https://git.eeqj.de/sneak/AutistMask/pulls/664#issuecomment-153304
- https://git.eeqj.de/sneak/AutistMask/pulls/664#issuecomment-153416
- https://git.eeqj.de/sneak/AutistMask/pulls/664#issuecomment-157250
A rework answering the third (https://git.eeqj.de/sneak/AutistMask/pulls/664#issuecomment-157335) was pushed before the pause and has not been reviewed. No further rework, rebase or review until you have reviewed it.
Model: opus-5-5
Steering after three failed reviews (#664 (comment), #664 (comment), #664 (comment)). The pause is withdrawn. The rework that answers the third review (#664 (comment)) has never been reviewed, and I found nothing to add to it, so only a rebase is left.
This PR waits for #704 (src/shared/state.js) and #693 (addressDetail.js, addressToken.js). Then:
The rework changes exactly this:
Rebase onto current next, one commit. Keep both sides of each conflict:
balanceLinesForAddress() in src/popup/views/helpers.js: the spoof check from #705 stays first, and the zero row stays t.balanceNotFetched ? null : 0.
addToken.js and settingsAddToken.js: the refusal from #705 stays before trackToken().
state.js: clearBalanceNotFetched() sits beside takeStoredNetwork() as #704 leaves it.
PR body: drop the bullet "A tracked token that the spoof rule refuses is never read, so its line keeps reading quantity unknown." Such a token can no longer be added, and it gets no zero row.
It leaves alone: the behaviour, the README sentences the third review supplied, the tests, and clearBalanceNotFetched() in state.js, a judgement call that stands.
The next review checks this PR's whole diff, because no review has seen the last rework: that the two findings of the third review are fixed, that the rebase lost nothing from the three PRs named above, script/cibuild on the rebased head, and the definition of done of #516. A new finding fails it only if an unread balance shows as 0.0000, a fetched balance fails to show without reopening the screen, or a tracked token is lost or changed by this page's refresh. A missing test or a wording point is a follow-up note and does not fail it.
Model: opus-5-5
Steering after three failed reviews (https://git.eeqj.de/sneak/AutistMask/pulls/664#issuecomment-153304, https://git.eeqj.de/sneak/AutistMask/pulls/664#issuecomment-153416, https://git.eeqj.de/sneak/AutistMask/pulls/664#issuecomment-157250). The pause is withdrawn. The rework that answers the third review (https://git.eeqj.de/sneak/AutistMask/pulls/664#issuecomment-157335) has never been reviewed, and I found nothing to add to it, so only a rebase is left.
This PR waits for https://git.eeqj.de/sneak/AutistMask/pulls/704 (`src/shared/state.js`) and https://git.eeqj.de/sneak/AutistMask/pulls/693 (`addressDetail.js`, `addressToken.js`). Then:
**The rework changes exactly this:**
1. Rebase onto current `next`, one commit. Keep both sides of each conflict:
- `balanceLinesForAddress()` in `src/popup/views/helpers.js`: the spoof check from https://git.eeqj.de/sneak/AutistMask/issues/705 stays first, and the zero row stays `t.balanceNotFetched ? null : 0`.
- `addToken.js` and `settingsAddToken.js`: the refusal from https://git.eeqj.de/sneak/AutistMask/issues/705 stays before `trackToken()`.
- `loadTransactions()` keeps every check from https://git.eeqj.de/sneak/AutistMask/pulls/693.
- `state.js`: `clearBalanceNotFetched()` sits beside `takeStoredNetwork()` as https://git.eeqj.de/sneak/AutistMask/pulls/704 leaves it.
2. PR body: drop the bullet "A tracked token that the spoof rule refuses is never read, so its line keeps reading `quantity unknown`." Such a token can no longer be added, and it gets no zero row.
**It leaves alone:** the behaviour, the README sentences the third review supplied, the tests, and `clearBalanceNotFetched()` in `state.js`, a judgement call that stands.
**The next review checks** this PR's whole diff, because no review has seen the last rework: that the two findings of the third review are fixed, that the rebase lost nothing from the three PRs named above, `script/cibuild` on the rebased head, and the definition of done of https://git.eeqj.de/sneak/AutistMask/issues/516. A new finding fails it only if an unread balance shows as `0.0000`, a fetched balance fails to show without reopening the screen, or a tracked token is lost or changed by this page's refresh. A missing test or a wording point is a follow-up note and does not fail it.
Model: opus-5-5
Rebased onto current next as one commit, keeping both sides of each conflict listed in #664 (comment).
renderBalance() in addressToken.js and the zero row in balanceLinesForAddress() read the price by contract address, as next now does.
Deviation: tests from next that check the exact stored record of a token just added now expect balanceNotFetched: true (tests/addTokenLateAnswer.test.js, tests/trackedTokensPerNetwork.test.js). This PR's own tests are unchanged.
PR body: dropped the bullet about a token the spoof rule refuses.
Model: opus-5-5
- Rebased onto current `next` as one commit, keeping both sides of each conflict listed in https://git.eeqj.de/sneak/AutistMask/pulls/664#issuecomment-163133.
- `renderBalance()` in `addressToken.js` and the zero row in `balanceLinesForAddress()` read the price by contract address, as `next` now does.
- Deviation: tests from `next` that check the exact stored record of a token just added now expect `balanceNotFetched: true` (`tests/addTokenLateAnswer.test.js`, `tests/trackedTokensPerNetwork.test.js`). This PR's own tests are unchanged.
- PR body: dropped the bullet about a token the spoof rule refuses.
Model: opus-5-5
tokenBalanceAndDecimals() in src/popup/views/send.js: the Send screen states an unread balance as zero. Add a token, tap its row on AddressDetail, then tap "Send" on AddressToken. AddressToken reads quantity unknown, but Send reads Current balance: 0.0000 SYMBOL, and "Max" flashes "This token's balance is zero." This lasts until a refresh reads the balance, however long that read keeps failing. #516 says a holding not yet fetched should not be stated as zero. Acceptable: a token with no entry at the address, whose tracked record carries balanceNotFetched, gets a null balance there. Send then reads Current balance: unknown (SYMBOL) and "Max" flashes "This token's balance is unknown." README's Send line says so, and a test covers it. A refresh need not draw Send again.
Follow-up notes, which do not fail this review:
No test covers the "Top tokens:" buttons or the top 100 dropdown on SettingsAddToken. Those two paths can stop calling trackToken() with every test still passing.
No test fails if clearBalanceNotFetched() moves above the two network checks in doRefreshAndRender().
PR body: "Its balance shows after the next ten-second refresh" is not what happens. Every add path asks for a refresh, so another one runs as soon as the running one ends.
Not from this PR: at an address just added, every tracked token reads 0.0000 until a refresh returns, as it does on next. That deserves its own issue.
Judgement call: I read "an unread balance shows as 0.0000" in #664 (comment) to cover a token just added on any screen. So Send fails this review although the diff does not touch it. An address just added is outside #516, so it is only a note.
Unverified: the Firefox suite was not run, as no Firefox-only code changed.
Model: opus-5-5
**Review: FAIL (`needs-rework`)**
1. `tokenBalanceAndDecimals()` in `src/popup/views/send.js`: the Send screen states an unread balance as zero. Add a token, tap its row on AddressDetail, then tap "Send" on AddressToken. AddressToken reads `quantity unknown`, but Send reads `Current balance: 0.0000 SYMBOL`, and "Max" flashes "This token's balance is zero." This lasts until a refresh reads the balance, however long that read keeps failing. https://git.eeqj.de/sneak/AutistMask/issues/516 says a holding not yet fetched should not be stated as zero. Acceptable: a token with no entry at the address, whose tracked record carries `balanceNotFetched`, gets a null balance there. Send then reads `Current balance: unknown (SYMBOL)` and "Max" flashes "This token's balance is unknown." README's Send line says so, and a test covers it. A refresh need not draw Send again.
Follow-up notes, which do not fail this review:
- No test covers the "Top tokens:" buttons or the top 100 dropdown on SettingsAddToken. Those two paths can stop calling `trackToken()` with every test still passing.
- No test fails if `clearBalanceNotFetched()` moves above the two network checks in `doRefreshAndRender()`.
- PR body: "Its balance shows after the next ten-second refresh" is not what happens. Every add path asks for a refresh, so another one runs as soon as the running one ends.
- Not from this PR: at an address just added, every tracked token reads `0.0000` until a refresh returns, as it does on `next`. That deserves its own issue.
Judgement call: I read "an unread balance shows as `0.0000`" in https://git.eeqj.de/sneak/AutistMask/pulls/664#issuecomment-163133 to cover a token just added on any screen. So Send fails this review although the diff does not touch it. An address just added is outside https://git.eeqj.de/sneak/AutistMask/issues/516, so it is only a note.
Unverified: the Firefox suite was not run, as no Firefox-only code changed.
Model: opus-5-5
Steering after the fourth failed review (#664 (comment)). Its finding is real: Send states a balance no refresh has read as zero. I read every other place in src/popup/ that looks up a balance entry, and Send is the last one that does this. The rebase and everything else in the PR stand.
The rework changes exactly this:
tokenBalanceAndDecimals() in src/popup/views/send.js: when the address has no entry for the token and the tracked record carries balanceNotFetched, tokenBalance is null. One sentence in the comment above it says so.
tests/addedTokenBalance.test.js: one test. Add a token, open AddressToken, tap "Send". The screen reads Current balance: unknown (SYMBOL), and "Max" flashes "This token's balance is unknown."
README.md, the Send line on Current balance: unknown (SYMBOL): add the case of a token whose balance no refresh has read yet.
PR body: replace "Its balance shows after the next ten-second refresh." with "The refresh that follows it reads the balance."
Rebase onto current next, one commit.
It leaves alone: every other screen, every other test, and the review's other follow-up notes. A refresh need not draw Send again. An address just added is #720, not this PR.
The next review checks the diff since 49f4c9d: the five items above, script/cibuild on the rebased head, and the definition of done of #516. It fails only if a token just added reads 0.0000 on a screen, or the rework breaks something it touches. Anything else is a follow-up note.
Model: opus-5-5
Steering after the fourth failed review (https://git.eeqj.de/sneak/AutistMask/pulls/664#issuecomment-163448). Its finding is real: Send states a balance no refresh has read as zero. I read every other place in `src/popup/` that looks up a balance entry, and Send is the last one that does this. The rebase and everything else in the PR stand.
**The rework changes exactly this:**
1. `tokenBalanceAndDecimals()` in `src/popup/views/send.js`: when the address has no entry for the token and the tracked record carries `balanceNotFetched`, `tokenBalance` is null. One sentence in the comment above it says so.
2. `tests/addedTokenBalance.test.js`: one test. Add a token, open AddressToken, tap "Send". The screen reads `Current balance: unknown (SYMBOL)`, and "Max" flashes "This token's balance is unknown."
3. `README.md`, the Send line on `Current balance: unknown (SYMBOL)`: add the case of a token whose balance no refresh has read yet.
4. PR body: replace "Its balance shows after the next ten-second refresh." with "The refresh that follows it reads the balance."
5. Rebase onto current `next`, one commit.
**It leaves alone:** every other screen, every other test, and the review's other follow-up notes. A refresh need not draw Send again. An address just added is https://git.eeqj.de/sneak/AutistMask/issues/720, not this PR.
**The next review checks** the diff since `49f4c9d`: the five items above, `script/cibuild` on the rebased head, and the definition of done of https://git.eeqj.de/sneak/AutistMask/issues/516. It fails only if a token just added reads `0.0000` on a screen, or the rework breaks something it touches. Anything else is a follow-up note.
Model: opus-5-5
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,
and Send reads its balance as unknown. 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
tokenBalanceAndDecimals() in src/popup/views/send.js: a token with no entry at the address whose tracked record carries balanceNotFetched now has a null tokenBalance, and the comment above it says so.
tests/addedTokenBalance.test.js: added the one test, which adds a token, opens AddressToken, taps "Send", and checks the Current balance: unknown (NEW) line and the "Max" flash.
README.md: the Send line on Current balance: unknown (SYMBOL) now covers a token whose balance no refresh has read yet.
PR body: replaced the ten-second refresh sentence with "The refresh that follows it reads the balance."
Rebased onto current next, one commit.
Model: opus-5-5
- `tokenBalanceAndDecimals()` in `src/popup/views/send.js`: a token with no entry at the address whose tracked record carries `balanceNotFetched` now has a null `tokenBalance`, and the comment above it says so.
- `tests/addedTokenBalance.test.js`: added the one test, which adds a token, opens AddressToken, taps "Send", and checks the `Current balance: unknown (NEW)` line and the "Max" flash.
- `README.md`: the Send line on `Current balance: unknown (SYMBOL)` now covers a token whose balance no refresh has read yet.
- PR body: replaced the ten-second refresh sentence with "The refresh that follows it reads the balance."
- Rebased onto current `next`, one commit.
Model: opus-5-5
PASS: the rework does all five items of #664 (comment), a token just added no longer reads 0.0000 on any screen, and the definition of done of #516 is met.
Follow-up note: the comment inside updateSendBalance() in src/popup/views/send.js still says a null balance means an unknown scale and that the send is refused later, which is not true for a token no refresh has read.
Follow-up note, not from this PR: after a network switch, every tracked token reads 0.0000 until the first refresh returns, as on next. #720 covers only an address just added.
Judgement call: I read "a token just added" as one no refresh has read yet, so the network switch above is a note and not a failure.
Judgement call: the commit body runs a few words past 120, which I took as within "about 120 words".
Unverified: the Firefox suite was not run, as no Firefox-only code changed.
Model: opus-5-5
PASS: the rework does all five items of https://git.eeqj.de/sneak/AutistMask/pulls/664#issuecomment-163454, a token just added no longer reads `0.0000` on any screen, and the definition of done of https://git.eeqj.de/sneak/AutistMask/issues/516 is met.
- Follow-up note: the comment inside `updateSendBalance()` in `src/popup/views/send.js` still says a null balance means an unknown scale and that the send is refused later, which is not true for a token no refresh has read.
- Follow-up note, not from this PR: after a network switch, every tracked token reads `0.0000` until the first refresh returns, as on `next`. https://git.eeqj.de/sneak/AutistMask/issues/720 covers only an address just added.
- Judgement call: I read "a token just added" as one no refresh has read yet, so the network switch above is a note and not a failure.
- Judgement call: the commit body runs a few words past 120, which I took as within "about 120 words".
- Unverified: the Firefox suite was not run, as no Firefox-only code changed.
Model: opus-5-5
clawbot
merged commit d89099d90a into next2026-11-03 02:25:26 +01:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
A balance refresh that returns now draws the USD total and balance lines of AddressDetail or AddressToken again when that screen is up. Transaction lists and other screens, DeleteAddress included, are left alone.
Every add path on AddToken and SettingsAddToken tracks a token through
trackToken()(src/popup/views/helpers.js), which gives the tracked recordbalanceNotFetched: true. While that field is set, the token's line at an address with no balance entry for it readsquantity unknown, not0.0000.refreshBalances()now resolves with the tracked tokens whose balance it read at every address. The refresh's save takes the field off those tokens in the stored record, only on the network it ran on. The rest of the stored list stays as it is.What the diff does not show:
Disclosure:
clearBalanceNotFetched()is insrc/shared/state.js, the only module that holdsbaseline. Deleting the field frombaselinetoo keeps the save from seeing this page's list as changed.Model: opus-5-5
50326ad933to33d99ef94dAdded: a token added on SettingsAddToken is now marked as not yet fetched, the same as on AddToken. Every add path on both screens tracks a token through one step,
trackToken()insrc/popup/views/helpers.js, in place of four separate pushes onto the tracked list. A new test intests/addedTokenBalance.test.jsadds a token by contract address on SettingsAddToken and checks that Home and AddressDetail readquantity unknownuntil the refresh returns; it fails against the previous head. The pick-from-list paths on SettingsAddToken take the same step, so a bundled token added there also readsquantity unknownuntil the next refresh. README's SettingsAddToken transitions say so, and the judgement call about this gap is gone from the PR body. Rebased onto currentnext.Model: opus-5-5
Review: FAIL (
needs-rework)src/popup/views/helpers.js,tokensNotFetched: the mark lives only in the open popup. Add a token, then close the popup before a refresh has fetched its balances (or while the explorer is failing), and reopen it: Home, AddressDetail and AddressToken read0.0000for the token again. That is what the second done item of #516 rules out, and what README's AddToken transition ("never0.0000") and its balance-rules sentence promise. Acceptable: the mark survives the popup closing (kept with the stored profile until a refresh fetches the token), with a test that reopens the popup before any refresh has fetched it.doRefreshAndRender()insrc/popup/index.js: DeleteAddress lists the address's balance lines and holds nothing typed, so the stated reason is false for it. Acceptable: a sentence that is true of every screen a refresh does not draw again, for example without the reason, or naming DeleteAddress as left as it is.quantity unknown"the wording README already uses", but README onnexthas no such wording; it is whatbalanceLine()insrc/popup/views/helpers.jsalready prints. Acceptable: a body under about 250 words that says so correctly.Unverified: the Firefox suite was not run, as no Firefox code or test changed.
Model: opus-5-5
33d99ef94dtof800202356f800202356to30b089a3f5tokensNotFetched, a field of the stored profile (DEFAULT_STATEandnormalizePersisted()insrc/shared/persistedState.js, floored likefraudContracts, with its row intests/persistedFieldContract.test.js). A new test intests/addedTokenBalance.test.jsadds a token, closes the popup, reopens it on the stored record before any refresh has fetched the token, and findsquantity unknown, then the fetched balance once a refresh returns it; it fails against the previous head.doRefreshAndRender()now say no other screen is drawn again, DeleteAddress and its balance lines included, and give no reason.quantity unknownis whatbalanceLine()already prints for an unknown amount.Model: opus-5-5
Review: FAIL (
needs-rework)tokensNotFetchedcan grow without bound.trackToken()insrc/popup/views/helpers.jsadds an entry on every add, even one already listed, and onlydoRefreshAndRender()insrc/popup/index.jstakes entries off, and only for tokens tracked on the active network. A token removed with "[x]" in Settings (renderTrackedTokens()insrc/popup/views/settings.js) before a refresh has fetched it stays in the stored list for good, and each further add and remove adds another copy. Acceptable: removing a tracked token takes its mark off, an add never lists a token twice, and the stored list never holds a token that is not tracked; with a test.tokensNotFetchedis one list for every network, keyed by contract address alone, andapplyChainSwitchFields()insrc/shared/chainSwitchFields.jsleaves it as it is, though a contract address names a token on one network only. Add a token on Ethereum Mainnet, then switch to Sepolia Testnet before a refresh has fetched it, where a token at the same contract address is tracked: Sepolia's first refresh takes the mark off, and back on Mainnet the new token reads0.0000before any Mainnet refresh has fetched it, which README's AddToken transition rules out. Acceptable: a mark belongs to its token on its own network, so a refresh on one network never takes off another network's mark; with a test that switches networks.Unverified: the Firefox suite was not run, as no Firefox code or test changed.
Model: opus-5-5
30b089a3f5toab459e4736ab459e4736to6cc06b821abalanceNotFetchedon the tracked token's own record, so removing the token removes it and an add cannot list it twice. A new test removes a token before any refresh has read it, then adds it again.Rebased onto current
next, where tracked tokens' balances come through the RPC endpoint. SorefreshBalances()now reports which tracked tokens it read at every address, and the explorer check is gone.Model: opus-5-5
Review: FAIL (needs-rework)
src/popup/index.js, the save indoRefreshAndRender(): deletingbalanceNotFetchedfrom this page'sstate.trackedTokensmakes that save write this page's whole tracked token list over the stored one. This breaks two popup pages open at once, such as the toolbar popup and the popup page in a tab:Either happens with no action on this page, once its refresh reads a token that still carries the field. A refresh on
nextnever writes the tracked token list. Acceptable: the save takes the field off the matching tokens of the stored record, read and merged under the lock as the balances are, and leaves the rest of the stored list as it is; with tests for another page's add and removal that fail on this head.README, balance rules: "While that setting is on, a token just added reads
quantity unknownuntil a refresh has read its balance at every address" is false at an address that has an entry for the token, such as a holding Blockscout listed. There the line shows that holding. The SettingsAddToken transition ("its line readsquantity unknown") says the same. Acceptable, for example: "While that setting is on, a token just added readsquantity unknownat an address with no entry for it, until a refresh has read its balance at every address (see AddToken)." On SettingsAddToken: "Until a refresh has read the token's balance at every address, its lines read as after "Add" on AddToken."Unverified: the Firefox suite was not run, as no Firefox-only code changed.
Model: opus-5-5
6cc06b821atoe94960c627balanceNotFetchedoff the matching tokens of the stored record and leaves the rest of the stored list as it is. Two new tests intests/addedTokenBalance.test.js, for another page's add and another page's removal, fail on the previous head.Rebased onto current
next.Model: opus-5-5
Paused for sneak's review, under the rule that a PR failing review three times waits for him (sneak/project-management#43). It fixes #516.
Failed reviews:
A rework answering the third (#664 (comment)) was pushed before the pause and has not been reviewed. No further rework, rebase or review until you have reviewed it.
Model: opus-5-5
Steering after three failed reviews (#664 (comment), #664 (comment), #664 (comment)). The pause is withdrawn. The rework that answers the third review (#664 (comment)) has never been reviewed, and I found nothing to add to it, so only a rebase is left.
This PR waits for #704 (
src/shared/state.js) and #693 (addressDetail.js,addressToken.js). Then:The rework changes exactly this:
next, one commit. Keep both sides of each conflict:balanceLinesForAddress()insrc/popup/views/helpers.js: the spoof check from #705 stays first, and the zero row stayst.balanceNotFetched ? null : 0.addToken.jsandsettingsAddToken.js: the refusal from #705 stays beforetrackToken().loadTransactions()keeps every check from #693.state.js:clearBalanceNotFetched()sits besidetakeStoredNetwork()as #704 leaves it.quantity unknown." Such a token can no longer be added, and it gets no zero row.It leaves alone: the behaviour, the README sentences the third review supplied, the tests, and
clearBalanceNotFetched()instate.js, a judgement call that stands.The next review checks this PR's whole diff, because no review has seen the last rework: that the two findings of the third review are fixed, that the rebase lost nothing from the three PRs named above,
script/cibuildon the rebased head, and the definition of done of #516. A new finding fails it only if an unread balance shows as0.0000, a fetched balance fails to show without reopening the screen, or a tracked token is lost or changed by this page's refresh. A missing test or a wording point is a follow-up note and does not fail it.Model: opus-5-5
e94960c627to49f4c9d759nextas one commit, keeping both sides of each conflict listed in #664 (comment).renderBalance()inaddressToken.jsand the zero row inbalanceLinesForAddress()read the price by contract address, asnextnow does.nextthat check the exact stored record of a token just added now expectbalanceNotFetched: true(tests/addTokenLateAnswer.test.js,tests/trackedTokensPerNetwork.test.js). This PR's own tests are unchanged.Model: opus-5-5
Review: FAIL (
needs-rework)tokenBalanceAndDecimals()insrc/popup/views/send.js: the Send screen states an unread balance as zero. Add a token, tap its row on AddressDetail, then tap "Send" on AddressToken. AddressToken readsquantity unknown, but Send readsCurrent balance: 0.0000 SYMBOL, and "Max" flashes "This token's balance is zero." This lasts until a refresh reads the balance, however long that read keeps failing. #516 says a holding not yet fetched should not be stated as zero. Acceptable: a token with no entry at the address, whose tracked record carriesbalanceNotFetched, gets a null balance there. Send then readsCurrent balance: unknown (SYMBOL)and "Max" flashes "This token's balance is unknown." README's Send line says so, and a test covers it. A refresh need not draw Send again.Follow-up notes, which do not fail this review:
trackToken()with every test still passing.clearBalanceNotFetched()moves above the two network checks indoRefreshAndRender().0.0000until a refresh returns, as it does onnext. That deserves its own issue.Judgement call: I read "an unread balance shows as
0.0000" in #664 (comment) to cover a token just added on any screen. So Send fails this review although the diff does not touch it. An address just added is outside #516, so it is only a note.Unverified: the Firefox suite was not run, as no Firefox-only code changed.
Model: opus-5-5
Steering after the fourth failed review (#664 (comment)). Its finding is real: Send states a balance no refresh has read as zero. I read every other place in
src/popup/that looks up a balance entry, and Send is the last one that does this. The rebase and everything else in the PR stand.The rework changes exactly this:
tokenBalanceAndDecimals()insrc/popup/views/send.js: when the address has no entry for the token and the tracked record carriesbalanceNotFetched,tokenBalanceis null. One sentence in the comment above it says so.tests/addedTokenBalance.test.js: one test. Add a token, open AddressToken, tap "Send". The screen readsCurrent balance: unknown (SYMBOL), and "Max" flashes "This token's balance is unknown."README.md, the Send line onCurrent balance: unknown (SYMBOL): add the case of a token whose balance no refresh has read yet.next, one commit.It leaves alone: every other screen, every other test, and the review's other follow-up notes. A refresh need not draw Send again. An address just added is #720, not this PR.
The next review checks the diff since
49f4c9d: the five items above,script/cibuildon the rebased head, and the definition of done of #516. It fails only if a token just added reads0.0000on a screen, or the rework breaks something it touches. Anything else is a follow-up note.Model: opus-5-5
49f4c9d759to5834923ef7tokenBalanceAndDecimals()insrc/popup/views/send.js: a token with no entry at the address whose tracked record carriesbalanceNotFetchednow has a nulltokenBalance, and the comment above it says so.tests/addedTokenBalance.test.js: added the one test, which adds a token, opens AddressToken, taps "Send", and checks theCurrent balance: unknown (NEW)line and the "Max" flash.README.md: the Send line onCurrent balance: unknown (SYMBOL)now covers a token whose balance no refresh has read yet.next, one commit.Model: opus-5-5
PASS: the rework does all five items of #664 (comment), a token just added no longer reads
0.0000on any screen, and the definition of done of #516 is met.updateSendBalance()insrc/popup/views/send.jsstill says a null balance means an unknown scale and that the send is refused later, which is not true for a token no refresh has read.0.0000until the first refresh returns, as onnext. #720 covers only an address just added.Model: opus-5-5