Files
amethyst/commons/src
Claude 1fc45e4bf6 feat(cli): amy cashu sync — page the wallet off the relays into the store
amy's cashu reads project the local event store and never touch the
network: that is the contract, and it is why `cashu balance` is instant
and works offline. The gap was that nothing in amy ever put the NIP-60
kinds INTO the store. CashuContext.snapshot() queries kinds 17375/7375/
7376/7374/10019/38000 out of ctx.store, and a grep for CashuTokenEvent.KIND
across cli/ returns exactly that one call site — a store query. So a
wallet created on the phone read as an empty wallet here, and `cashu
wallet show` answered "no kind:17375 wallet — run `cashu wallet create`",
which for a replaceable kind is advice that would have overwritten the
user's real wallet.

Adds `amy cashu sync`, plus `--sync` on `cashu balance` and `cashu wallet
show` for the common case. Kept opt-in rather than folded into the reads:
an implicit network round-trip inside a command documented as a local
projection is a contract change, and the offline read is worth keeping.

It pages rather than issuing one REQ, for the same reason the Android
backfill does: a relay answers an unbounded REQ with its own cap applied
to the newest matching events, and kind:7376 history outnumbers the
kind:7375 proofs by an order of magnitude on a wallet with any history,
so what falls off the bottom is the proofs at mints the user hasn't
touched lately — the balance reads low with nothing to indicate it.
drainAllPages walks each relay on its own until cursor to exhaustion.
Relay sets are split exactly as the Android subscription splits them:
own events from the outbox, inbound nutzaps from the inbox.

The filter builders move to commons per the thin-assembly rule, and
generalize what the Android backfill already had: cashuProofBackfillFilters
is now the kind:7375 narrowing of cashuOwnEventBackfillFilters, which
defaults to every kind the account authors. cashuInboundNutzapBackfillFilters
covers the #p half. A test pins that the own-event backfill covers every
kind the live subscription authors, so the gap can't reopen by someone
adding a kind to one and not the other.

Verified against a real binary, not just the compiler: `cashu sync`
emits its six JSON keys and exits 0 with no relays configured, `--sync`
parses on both readers, an unknown flag still exits 2, and the reworded
no_wallet error goes to stderr with exit 1.

New --json keys (additive): events_downloaded, token_events,
history_events on `cashu sync`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HaZ8RprmKC3sidsq6W8dKY
2026-08-17 01:14:15 +00:00
..