Skip to content

wallet-sdk PR4: accounts + scan#1122

Closed
orveth wants to merge 1 commit into
sdk/pr3-auth-userfrom
sdk/pr4-accounts-scan
Closed

wallet-sdk PR4: accounts + scan#1122
orveth wants to merge 1 commit into
sdk/pr3-auth-userfrom
sdk/pr4-accounts-scan

Conversation

@orveth

@orveth orveth commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

PR4 of the @agicash/wallet-sdk stack — stacked on PR3 (#1121). Implements Slice 2 (accounts + scan) of the no-cache contract, replacing PR2's NotImplemented stubs and wiring the real domains into Sdk.create.

What's implemented (vs stubbed)

AccountsDomain (src/domains/accounts.ts) — list / get / getDefault / add / setDefault / getBalance / suggestFor:

  • src/internal/account-repository.tsget / getAllActive / add over the SDK-owned Supabase client (re-housed framework-free from master account-repository.ts + account-service.ts + account-hooks.ts).
  • src/internal/db-account.ts — the wallet.accounts / cashu_proofs row types + the details json-model schemas + isCashuAccount/isSparkAccount guards + dbAccountToAccount mapper (lifted from agicash-db/database.ts + json-models/*; rows hand-written as in PR3's db-user.ts).
  • setDefault writes the per-currency default-account column on wallet.users (one default per currency, BTC+USD).
  • getBalance = the verbatim pure getAccountBalance (src/internal/account-balance.ts: cashu proof sum / spark balance).
  • suggestFor (src/internal/suggest-account.ts) — NET-NEW + PURE over the passed-in accounts: generalizes master's findMatchingOfferOrGiftCardAccount + an isOnline filter + a default-account fallback. Cheap-first (purpose priority → balance → age); no cross-protocol cost comparison. Handles send / receive / token-receive intents.

ScanDomain (src/domains/scan.ts) — parse(input) re-houses master classifyInput returning the KIND only (bolt11 | ln-address | cashu-token), no merchant/contact info (decision 3). The bolt11 / cashu-token / lnurl decode libs are SDK-internal (src/internal/lib-scan.ts). Unrecognised input throws DomainError (the contract types parse as non-null, unlike master's null). allowLocalhost is a constructor option (default false) replacing master's import.meta.env.MODE gate.

Wallet handle: DEFERRED to Slice 3 (per the build plan)

The build plan assigns the live wallet handle construction (ExtendedCashuWallet via the networked mint init / BreezSdk via @agicash/breez-sdk-spark) to Slice 3 ("THE HEAVY one"). In master, account-repository.toAccount builds the live handle and decrypts cashu proofs together, using shared/cashu + shared/spark + shared/encryption — all Slice-3 subsystems (the mint init is a 10s-timeout networked keyset/key fetch; proof decryption needs the OpenSecret-derived encryption key + ECIES).

So this PR keeps PR1's ExtendedCashuWallet / BreezSdk / SparkNetwork placeholders and introduces an explicit seam — AccountHandleResolver (src/internal/account-handle-resolver.ts) — injected into the repository. The Slice-2 DeferredAccountHandleResolver maps the DB fields Slice 2 owns and leaves the live handle a lazy stub that throws NotImplementedError on any use (the account's plain DB fields stay fully usable; isOnline=false, cashu proofs=[], spark balance=null until Slice 3). Slice 3 swaps in the real resolver with no change to the repository. This keeps the PR small and the boundary auditable.

Tests (+74; 64 → 138, both packages green)

  • scan.test.ts — parse cases (cashu A/B/URL/prefix, bolt11 decode + prefixes, ln-address lowercase + localhost policy, unrecognised → DomainError, priority).
  • suggest-account.test.ts — every branch: online filter, BTC currency filter, balance split, ranking (purpose/balance/age, cashu+spark mix), default fallback, receive/token-receive.
  • account-repository.test.tsget / getAllActive / add (insert-row shape + 409/LIMIT_REACHED/generic error paths) over a fake Supabase client.
  • db-account.test.ts — guards, mapper (cashu/spark/unknown), forward-compat with a fake Slice-3 resolver, deferred-handle-throws.
  • account-balance.test.ts + accounts.test.ts (domain over fakes).

CI gate (run locally, all green)

biome format --write packages/wallet-sdk → no fixes; root bun --filter='*' run test → wallet-sdk 138 pass / 0 fail, web-wallet 133 pass / 0 fail (exit 0); package tsc --noEmit, biome format (check), biome lint → all pass. Framework-free constraint verified (no react/@TanStack in the package or its transitive lib re-exports).

🤖 Generated with Claude Code

Implements AccountsDomain + ScanDomain (Slice 2 of the no-cache contract),
replacing PR2's NotImplemented stubs and wiring them into Sdk.create.

AccountsDomain (list/get/getDefault/add/setDefault/getBalance/suggestFor):
- account-repository over the SDK-owned Supabase client (re-housed from
  account-repository.ts + account-service.ts + account-hooks.ts, framework-free)
- db-account row types + dbAccountToAccount mapper (lifted from agicash-db/database.ts
  + json-models, hand-written rows as in db-user.ts)
- setDefault writes the per-currency default-account column on wallet.users
- getBalance = the verbatim pure getAccountBalance (cashu proof sum / spark balance)
- suggestFor: NET-NEW pure logic generalizing findMatchingOfferOrGiftCardAccount
  + online filter + default fallback; cheap-first, no cross-protocol cost comparison

ScanDomain (parse): re-houses master classifyInput (kind only: bolt11 | ln-address |
cashu-token); bolt11/cashu/lnurl decode libs are SDK-internal; unrecognised input
throws DomainError (contract types parse as non-null).

WALLET HANDLE DEFERRED to Slice 3 per the build plan: an account's live `wallet`
handle (ExtendedCashuWallet/BreezSdk) + decrypted cashu proofs are entangled in
master's toAccount with the heavy networked mint/Breez init + the shared/encryption
subsystem, all assigned to Slice 3. An AccountHandleResolver seam is injected into the
repository; the Slice-2 DeferredAccountHandleResolver maps the DB fields and leaves the
live handle a lazy throwing stub. PR1 placeholders (ExtendedCashuWallet/BreezSdk/
SparkNetwork) are KEPT.

Tests: scan parse cases, suggestFor branches (online/currency/balance/ranking/
fallback/receive), account-repository (get/getAllActive/add error paths) with a fake
Supabase client, db-account mapper + guards + deferred-handle stub, account-balance.
+74 tests (64 -> 138); full CI gate green (both packages test, tsc, biome lint+format).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 3, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
agicash Error Error Jun 3, 2026 10:06pm
agicash-root-dir-test Ready Ready Preview, Comment Jun 3, 2026 10:06pm

Request Review

@supabase

supabase Bot commented Jun 3, 2026

Copy link
Copy Markdown

This pull request has been ignored for the connected project hrebgkfhjpkbxpztqqke because there are no changes detected in supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd say these and similar tests are trivial and thus useless

getCashuUnit,
normalizeMintUrl,
} from '../../../../apps/web-wallet/app/lib/cashu/utils';
export { sumProofs } from '../../../../apps/web-wallet/app/lib/cashu/proof';

@jbojcic1 jbojcic1 Jun 5, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

package should never import from the app. these utils, if they will only be used by sdk should be moved to sdk or if used in other places too where importing sdk does not make sense, they can be moved to separate package

* @throws Error if the update fails (e.g. the DB constraint that a default currency must
* have a default account set).
*/
async setDefaultAccount(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this needed when it doesn't exist in the web wallets user-repository?

@orveth

orveth commented Jun 5, 2026

Copy link
Copy Markdown
Contributor Author

Superseded by the reactive design-B stack (SDK #1129-#1138 + lib-extract #1140-#1143). This no-cache stack was the reference substrate the reactive PRs were built from (~90% reused, reads adapted to Query); it was never reviewed/merged, so nothing is lost. Closing as superseded — branch retained for reference.

@orveth orveth closed this Jun 5, 2026
@pmilic021 pmilic021 deleted the sdk/pr4-accounts-scan branch June 22, 2026 09:43
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