mirror of
https://github.com/minibits-cash/minibits_wallet.git
synced 2026-07-22 07:48:28 +00:00
Reported from device: with an 11k sat amount on a mint that advertises onchain
and NUT-20, the "Bitcoin address" button never appeared.
Two causes, both mine.
1. A missing `time` was being read as FRESH. The stamp only started being
written when the capability model landed (d008d45), so every mint info
cached before that has `time: undefined`. `now - undefined` is NaN, and
every comparison against NaN is false — so `now - time > TTL` reported
those records as fresh and never refetched them. A mint that had since
gained onchain support kept looking like one that never had it, forever.
isMintInfoStale() now treats a missing or non-finite timestamp as stale, and
lives in its own module (no stores, no services) so the NaN behaviour is
pinned by tests rather than rediscovered on a device.
2. Nothing on TopupScreen ever refreshed capabilities. The screen gates on the
mint's cached NUT-06 info but otherwise never talks to the mint, so even
with (1) fixed there was no trigger. Selecting a mint now kicks getMint(),
which only hits the network when the info is genuinely stale; the screen is
an observer, so the option appears by itself once fresher capabilities land.
Also logs the gate inputs (supportsOnchain / nut20 / mintMin / floor / amount),
since "the button is missing" is otherwise indistinguishable from a dozen
different causes.
260/260 tests pass; typecheck introduces no new errors.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
51 lines
1.8 KiB
TypeScript
51 lines
1.8 KiB
TypeScript
/**
|
|
* Mint info staleness (WalletStore.isMintInfoStale).
|
|
*
|
|
* The reason this is its own test: a MISSING `time` must count as STALE.
|
|
*
|
|
* The stamp was only added when the capability model landed, so every mint info
|
|
* cached before that has `time: undefined`. `now - undefined` is NaN, and every
|
|
* comparison against NaN is false — so the obvious `now - time > TTL` check reports
|
|
* those records as FRESH, forever. A mint that has since gained onchain support then
|
|
* keeps looking like a mint that never had it, and the wallet quietly refuses to
|
|
* offer a feature the mint supports.
|
|
*
|
|
* That is not hypothetical: it is exactly what happened on device.
|
|
*
|
|
* @jest-environment node
|
|
*/
|
|
import {isMintInfoStale, MINT_INFO_TTL_SECONDS} from '../src/models/helpers/mintInfoStale'
|
|
|
|
const NOW = 1_800_000_000
|
|
|
|
const infoAt = (time?: number) => ({time} as any)
|
|
|
|
describe('isMintInfoStale', () => {
|
|
it('treats absent info as stale', () => {
|
|
expect(isMintInfoStale(undefined, NOW)).toBe(true)
|
|
})
|
|
|
|
it('treats info with NO timestamp as stale (the legacy-cache trap)', () => {
|
|
// `now - undefined` is NaN; `NaN > TTL` is false. A naive check calls this
|
|
// fresh and never refetches.
|
|
expect(isMintInfoStale(infoAt(undefined), NOW)).toBe(true)
|
|
})
|
|
|
|
it('treats a non-finite timestamp as stale', () => {
|
|
expect(isMintInfoStale(infoAt(NaN), NOW)).toBe(true)
|
|
})
|
|
|
|
it('is fresh within the TTL', () => {
|
|
expect(isMintInfoStale(infoAt(NOW - 60), NOW)).toBe(false)
|
|
expect(isMintInfoStale(infoAt(NOW), NOW)).toBe(false)
|
|
})
|
|
|
|
it('is stale past the TTL', () => {
|
|
expect(isMintInfoStale(infoAt(NOW - MINT_INFO_TTL_SECONDS - 1), NOW)).toBe(true)
|
|
})
|
|
|
|
it('is fresh exactly at the TTL boundary', () => {
|
|
expect(isMintInfoStale(infoAt(NOW - MINT_INFO_TTL_SECONDS), NOW)).toBe(false)
|
|
})
|
|
})
|