031159b3f6
Phase C of the onchain zaps plan (amethyst/plans/2026-05-14-onchain-zaps.md). Plumbs the Quartz receive foundation from Phase A.1 into the Android app so incoming kind 8333 zaps show up in note totals and the wallet screen. Subscription / fetch path (kind 8333 rides existing zap filters, no new assemblers per the plan): - FilterRepliesAndReactionsToNotes: OnchainZapEvent.KIND in RepliesAndReactionsKinds (#e on notes) - FilterUserProfileZapReceived: OnchainZapEvent.KIND in UserProfileZapReceiverKinds (#p on profiles) - UserProfileZapsViewModel: kinds = LnZap + Onchain inline - FilterNotificationsToPubkey: OnchainZapEvent.KIND in SummaryKinds - NotificationFeedFilter / NotificationDispatcher: kind 8333 in NOTIFICATION_KINDS - FilterMessagesToLiveStream / FilterGoalForLiveActivity: kind 8333 alongside 9735 Display path (Note.zapsAmount fold-in — no UI changes needed downstream): - commons/model/Note.kt: new `onchainZaps: Map<String, OnchainZapAmount>` (keyed by txid for `(txid, target)` dedup); `addOnchainZap(txid, sats, confirmed)` mutator; `updateZapTotal` now also sums confirmed onchain sats into `zapsAmount`. Pending entries are tracked but excluded from the aggregate total per the NIP-BC spec. - commons/model/OnchainZapAmount.kt: new data class for per-tx state. - LocalCache: new `onchainBackend: OnchainBackend?` slot; new `consume(OnchainZapEvent)` handler that runs the same sig-verify and computeReplyTo pipeline as `consume(LnZapEvent)`, then kicks off async verification on `applicationIOScope`. Self-zaps are rejected synchronously per the spec. Verification failures, mempool-only txs, and confirmed txs all route to `Note.addOnchainZap` with the correct flag. Without a backend wired, events are still cached (so subscriptions see them) but skip the total contribution. - LocalCache.computeReplyTo: new `is OnchainZapEvent ->` branch that enumerates `e`/`a` targets — profile-only zaps return an empty list and flow through profile zap queries instead. Wallet UI: - Wallet screen now renders a "Bitcoin" Card above the existing NIP-47 NWC wallet list. Single card (one onchain wallet per Nostr identity, derived from the pubkey). Shows the bc1p taproot address + Copy button. Balance, recent incoming zaps, and tap-to-detail come in later phases. Settings + wiring: - AccountSettings: new `onchainEsploraEndpoint: MutableStateFlow<String>`, default mempool.space, plumbed for a future settings UI. - AppModules: builds a single shared `EsploraBackend` at app init using the role-based money-flavored OkHttp client (Tor-aware), installs it on `LocalCache.onchainBackend` so verification can run. Secp256k1Instance: new `pubKeyTweakAdd` was added in Phase A.1. No PSBT, no signing, no broadcasting — send-side (Phase A.2 + D) is still pending.