Skip to content

feat: Add watch-only addresses (#55) - #56

Merged
Sword-Smith merged 10 commits into
mainfrom
watch-only-addresses
Jul 23, 2026
Merged

feat: Add watch-only addresses (#55)#56
Sword-Smith merged 10 commits into
mainfrom
watch-only-addresses

Conversation

@Sword-Smith

Copy link
Copy Markdown
Member

Register an external viewing address, scoped to an account, to monitor its incoming UTXOs without any ability to spend. Supports ViewingAddress today (EC hybrid is scoped but gated on a serializable viewing-key form in neptune-wallet).

A single watch-only concept with an optional capability:

  • Viewing key only: shows the total amount received (incoming UTXOs are confirmed against the block via the public receiver digest).
  • Viewing key + receiver preimage (imported as a hex Digest, validated by preimage.hash() == receiver_digest): also detects spends and shows a real, timelock-aware balance (available vs locked, with the next unlock date).

Both announced UTXOs and guesser (mining) rewards are detected; guesser rewards are picked up from the block structure whenever a watched address is the block's guesser, mirroring par_scan_for_incoming_utxo.

Watch-only UTXOs live in their own per-account tables, so they never enter the account balance nor become spendable inputs, and they roll back with reorgs.

Backend: new wallet/watch_only.rs (parse/validate, scan, spend detection, balance breakdown), watch_only_addresses/watch_only_utxos tables + migrations, a scan hook in update_new_tip and rollback in roll_back, and three Tauri commands. Frontend: a new account-scoped /watch-only page with an import form and a per-address amount/balance table.

@Sword-Smith
Sword-Smith marked this pull request as draft July 14, 2026 19:33
@Sword-Smith
Sword-Smith force-pushed the watch-only-addresses branch from 701127e to 0df02fb Compare July 14, 2026 20:05
@Sword-Smith
Sword-Smith marked this pull request as ready for review July 14, 2026 20:06
@Sword-Smith
Sword-Smith force-pushed the watch-only-addresses branch from 5b0e70c to 17d0a83 Compare July 15, 2026 07:41
@codewordneptune

Copy link
Copy Markdown
Collaborator

I think this is a great and welcome feature that opens up many new use cases for the wallet app: checking a cold wallet balance safely, transparently monitoring donation/charity funds, just to name a few. Very inspired by where the wallet app is heading :)

A few considerations, mainly from the UX side:

  • Scope: per account or global? Is per-account scope the best fit, or should watch addresses be global, like Contacts? A watched address isn't derived from the selected account, so the scoping can feel a bit arbitrary. Global would also make checking faster, since the user wouldn't need the right account selected first. Realistically, how many watch addresses is a typical user likely to have? I'd expect only a few, which to me argues for one global list rather than spreading them across accounts. I realize this might affect the storage design, so fine to treat this as a discussion point rather than a blocker.
  • Navigation placement: if watch addresses end up global/not account-specific, I'd move them to the bottom section of the side nav, with the other non-account items. If not, then the current position is fine.
  • Terminology: would "Name" fit better than "Label"? It would align with Accounts and Contacts, which both use a name.
  • Required name/label: I'd make the name/label required rather than optional. It eases management in the long run when every watch address has a name.

Beyond these points, looks good to me!

I assume watch support for generation addresses is coming too (great!)
Neptune-Crypto/neptune-core#952

Sword-Smith and others added 8 commits July 16, 2026 02:08
Register an external viewing address, scoped to an account, to monitor its
incoming UTXOs without any ability to spend. Supports ViewingAddress today
(EC hybrid is scoped but gated on a serializable viewing-key form in
neptune-wallet).

A single watch-only concept with an optional capability:
- Viewing key only: shows the total amount received (incoming UTXOs are
  confirmed against the block via the public receiver digest).
- Viewing key + receiver preimage (imported as a hex Digest, validated by
  preimage.hash() == receiver_digest): also detects spends and shows a real,
  timelock-aware balance (available vs locked, with the next unlock date).

Both announced UTXOs and guesser (mining) rewards are detected; guesser
rewards are picked up from the block structure whenever a watched address is
the block's guesser, mirroring par_scan_for_incoming_utxo.

Watch-only UTXOs live in their own per-account tables, so they never enter the
account balance nor become spendable inputs, and they roll back with reorgs.

Backend: new wallet/watch_only.rs (parse/validate, scan, spend detection,
balance breakdown), watch_only_addresses/watch_only_utxos tables + migrations,
a scan hook in update_new_tip and rollback in roll_back, and three Tauri
commands. Frontend: a new account-scoped /watch-only page with an import form
and a per-address amount/balance table.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Bump neptune-wallet dependency to 0.14.2 which adds support for the
bech32 encoding of EC hybrid viewing keys. And add support for adding EC
hybrid viewing keys as watch-only addresses.

refactor: Drop unused leveldb-sys dependency & crate

Was only needed to shadow an unused/untouched dependency of leveldb-sys
imported transitively from the former dependency neptune-core. After the
upstream crate separation (in v0.14.2) of neptune-archival-mutator-set,
neptune-wallet and neptune-consensus no longer depend on
neptune-database. So this whole unused module that was never executed
can now be deleted. Fewer lines \o/
- total received
- balance, only available if receiver preimage was impoprted

Also auto-update balance when a new block is received.
Allows the detection of UTXOs in the genesis block for watch-only
addresses.

Only generation addresses received UTXOs in the genesis block and
generation addresses don't yet have a viewing key struct so this is
future-proofing for the watch-only capabilities only.

Cf.
Neptune-Crypto/neptune-core#952
Users are more likely to remember and identify the address, not the
viewing key they used to start monitoring that address.
Mirror the check used for regular addresses: Do not record UTXOs if they
carry an unknown type script since we cannot know if we (or the owner of
the address) can satisfy this type script.
…s known

Only changes to frontend: Even when the receiver preimage is not known,
we cna still show the value of all timelocked UTXOs, as they cannot have
been spent yet if they are timelocked.
Can be used to measure *how long* a timelock is applied for.
@Sword-Smith
Sword-Smith force-pushed the watch-only-addresses branch from 66206e6 to 49025b2 Compare July 16, 2026 00:11
@Sword-Smith

Sword-Smith commented Jul 16, 2026

Copy link
Copy Markdown
Member Author

Agreed on point 3 and 4: Change "label" to "name", and make name required.

About the scope: Global or account-tied watch-only addresses?
I think, regardless of whether the watch-only addresses are linked to an account or not, there is a foot gun in the payout policy logic: Once the account that's supposed to do the payouts is no longer active (by the user switching account), the payouts will not be made, since only one account's secret key is unlocked at a time. With the global watch-only addresses, this fact, that the account doing the payouts is not active, might be easier to present to the user though, as the payout-policy icon can be made red or grey to indicate that it's not running. With the account-attached watch-only addresses, the watch-only address and its payout policy will simply not appear when the account is not active. On the other hand, the sync logic for global watch-only addresses might be complicated by the fact that the rest of the sync logic is tied to accounts. But I haven't pursued that hunch rigorously.

Also: A follow-up feature plays well together with associating the watch-only addresses with accounts:
#58

@Sword-Smith
Sword-Smith force-pushed the watch-only-addresses branch from 73ca7c4 to db135d1 Compare July 17, 2026 11:59
@Sword-Smith

Copy link
Copy Markdown
Member Author

3 and 4 have been implemented: Rename "label" to "name" and make this value required.

@Sword-Smith
Sword-Smith merged commit 550b132 into main Jul 23, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants