Skip to content

Feat: Multi unit support#174

Open
igbopharaoh wants to merge 7 commits into
cashubtc:masterfrom
igbopharaoh:feat/multi-unit-support
Open

Feat: Multi unit support#174
igbopharaoh wants to merge 7 commits into
cashubtc:masterfrom
igbopharaoh:feat/multi-unit-support

Conversation

@igbopharaoh
Copy link
Copy Markdown
Contributor

Description

This pull request adds first-class support for custom Cashu units across Coco while preserving existing bare-amount sat behavior. It makes amount and unit travel together through public APIs, operations, proof storage, balance queries, quote handling, payment requests, React hooks, persistence adapters, migrations, docs, and tests.

This pull request resolves #159.

Problem

Coco previously assumed sat in many internal and public flows. That made custom-unit Cashu usage unsafe because mint/send/receive/melt flows could silently fall back to sats, balances could be interpreted without unit scope, persisted proofs and operations lacked complete unit metadata, and adapter migrations did not consistently round-trip custom units.

Summary

  • Added core unit primitives and parsing helpers in packages/core/amounts.ts, including default sat behavior, lowercase normalization, object-form amount input, and unit assertions.
  • Updated public APIs for mint, send, melt, wallet decode/restore/sweep, balances, and payment requests to accept and preserve units.
  • Made WalletService unit-scoped, with mint+unit wallet caching and active keyset resolution by unit.
  • Added NUT-04/NUT-05 method-unit capability validation while keeping legacy sat quote behavior for mints without capability metadata.
  • Made proof storage, proof selection, output generation, inflight checks, reservations, and balance APIs unit-aware.
  • Propagated unit through mint, send, receive, melt, restore, sweep, recovery, history, and event payloads.
  • Updated TokenService to infer unit from proof keysets when token metadata is absent, reject token/keyset unit mismatches, and fallback to sat for legacy/unresolvable unitless tokens.
  • Kept public MeltQuoteService and made quote creation/payment unit-aware.
  • Added payment request unit validation so custom-unit requests cannot be funded with sats.
  • Updated memory, SQLite3, SQLite-bun, Expo SQLite, and IndexedDB repositories/schemas/migrations to persist proof and operation units.
  • Updated React balance context/hooks and operation hooks for unit-scoped balances and custom-unit mint/send/melt inputs.
  • Added docs for multi-unit usage and updated existing minting, sending/receiving, payment request, and BIP39 docs.
  • Added broad unit, adapter contract, schema migration, React, and optional live integration coverage for custom-unit flows.
  • Added .changeset/many-units-restore.md for public API and migration behavior.

Verification

  • bun run --filter='@cashu/coco-core' test:unit
  • bun run --filter='@cashu/coco-core' typecheck
  • bun run --filter='@cashu/coco-react' typecheck
  • bun run --filter='@cashu/coco-react' test -- src/lib/hooks/operationHooks.test.tsx src/lib/hooks/useBalances.test.tsx src/lib/providers/root.test.tsx
  • bun run --filter='@cashu/coco-adapter-tests' build
  • Adapter typechecks for SQLite3, SQLite-bun, Expo SQLite, and IndexedDB
  • SQLite-family contract/schema tests passed directly

Changeset

  • Added .changeset/many-units-restore.md

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 14, 2026

🦋 Changeset detected

Latest commit: 597eea4

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 7 packages
Name Type
@cashu/coco-core Major
@cashu/coco-react Major
@cashu/coco-indexeddb Major
@cashu/coco-expo-sqlite Major
@cashu/coco-sqlite Major
@cashu/coco-sqlite-bun Major
@cashu/coco-adapter-tests Major

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@igbopharaoh igbopharaoh force-pushed the feat/multi-unit-support branch from 0f23030 to 597eea4 Compare May 14, 2026 17:08
@igbopharaoh
Copy link
Copy Markdown
Contributor Author

@codex kindly trigger auto review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 597eea40f3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

methodData: MintMethodData = {},
): Promise<PendingMintOperation> {
const initOperation = await this.init(mintUrl, { amount, unit }, method, methodData);
const parsed = parseUnitAmount(amount, { explicitUnit: unit });
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Parse amount without implicit sat override

prepareNewQuote now accepts UnitAmountLike, but it always passes explicitUnit: unit to parseUnitAmount while unit defaults to 'sat'. This makes object-form inputs like { amount: 10, unit: 'usd' } fail with a unit-mismatch unless callers redundantly pass the same unit as the third argument, which breaks the intended object-form API for custom units. The parser should only enforce explicitUnit when the caller actually provided a unit override.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Backlog

Development

Successfully merging this pull request may close these issues.

feat: multi-unit support

1 participant