github cashubtc/cashu-ts v5.0.0-rc.11

latest release: v4.11.0
pre-release3 hours ago

This release finishes the nutroot wire format for v3 (BLS) keysets, adds abort signals and an optional keepalive to the transport, and closes a long list of findings from building real wallets against rc.10 on the web, Node and Deno. There are a few more breaking changes since rc.10; the migration guide has each one with before and after code.

Upgrading from rc.10: read these even if you never touch a v3 keyset

  • Quote lock keys derived from a seed are now scoped to the mint's NUT-06 identity pubkey, one counter per mint under quoteCounterKey(mintPubkey); the single QUOTE_COUNTER_KEY export is gone and a custom CounterSource must accept mint-quote-lock:<pubkey> keys. Keys derived by rc.10 are not recoverable by scan, so keep the privkey of any unpaid rc.10 quote. The derivation is marked @experimental: it follows draft NUT-13 and NUT-06 and may change again before GA, which is why the contract remains "persist the privkey with its quote".
  • Every Mint method that took a trailing customRequest now takes { customRequest?, signal? }. Wallet operation configs, builders (.signal()) and quote or state checks accept an AbortSignal too; a one-shot operation honours it until preparation finishes and never mid-commit. An aborted call rejects with CallerAbortError, now exported alongside UncancellableReadError.
  • Amount throws AmountError on implicit numeric coercion (a + 1, a < b, Number(a)); string contexts still work. Amount.from accepts a structuredClone'd Amount, so IndexedDB and postMessage round trips no longer need special handling.
  • getDecodedTokenBinary requires the keyset id list. Mint.oidcAuth is removed in favour of OIDCAuth.fromMintInfo, and OIDC endpoints must match the discovery document's scheme. Lock families are named 'nutroot' | 'p2pk' rather than by version.
  • Requests time out after 5 minutes per attempt by default (requestTimeout: Infinity disables it), retries back off more slowly and log at info, and a NetworkError names the mint origin and the underlying cause.
  • The ES build preserves modules, so a bundler that imports only Amount or the token codec no longer pays for the whole wallet. Wallet itself is unchanged in size.
  • NUT-17: a subscription whose socket closes now has its error callback invoked (any close code), so a plain on.mintQuotePaid can resubscribe or fall back. The optional wsKeepaliveMs on Wallet or Mint probes the socket and treats silence as a close, for the half-open connections a proxy leaves behind. WSConnection.onClose takes a WSCloseEvent, so the shipped types no longer need the DOM lib on Node.
  • mnemonicToSeedSync derives the BIP-39 seed from a phrase with no new dependency; validate the phrase first with @scure/bip39 or similar.
  • Every mint, batch mint and melt preview now has a serialize and deserialize pair.

v3 keysets

Nutroot is still a draft and v3 keysets are served only by demo mints, so nothing here affects funds on a public mint. The transcript picked up three spec changes since rc.10 (the commit leaf, melt outputs priced at quote plus fee reserve, and proof inputs named by Y), which is what makes rc.11 spend against current Nutshell. BLS keysets carry no DLEQ; requireDleq accepts 02 proofs on the strength of the pairing check. A wallet now refuses to bind to a keyset id version this build cannot spend rather than failing later, and refuses pre-v3 locks or NUT-29 batch operations the mint does not advertise.


What's Changed

  • fix(utils): resolve short keyset IDs in getDecodedTokenBinary by @Hardeezah in #1153
  • feat(nutroot): the commit leaf (NUT-10 type 0x04) by @robwoodgate in #1151
  • test: raise the vitest timeout and shrink two heavy prepareMint fixtures by @robwoodgate in #1157
  • fix(mint): check a bolt11 melt preimage against the invoice payment hash by @robwoodgate in #1161
  • fix(utils): apply the keyset unit alphabet to version 01 ids too by @robwoodgate in #1162
  • feat(wallet): add serialize helpers for mint, batch mint and melt previews by @robwoodgate in #1159
  • fix(auth): check OIDC endpoint schemes against the discovery document by @robwoodgate in #1160
  • refactor: route the last UTF-8 decodes through utils/bytes by @robwoodgate in #1168
  • docs: say where transport policy lives by @robwoodgate in #1170
  • chore(docker): update NUT_IMAGE to cashubtc/nutshell:0.21.0 by @robwoodgate in #1173
  • feat(nutroot): melt output amount is the quote amount plus its fee reserve by @robwoodgate in #1172
  • fix(model): require boolean true for advertised NUT support by @KvngMikey in #1176
  • feat(nutroot): name transcript proof inputs by Y by @robwoodgate in #1175
  • chore(deps): bump typescript-eslint from 8.69.0 to 8.70.0 in the eslint group by @dependabot[bot] in #1180
  • fix(utils): getDecodedTokenBinary requires the keyset id list by @robwoodgate in #1178
  • fix(core): keep a token's witness verbatim when re-encoding by @robwoodgate in #1179
  • chore(deps): bump the vitest group with 2 updates by @dependabot[bot] in #1182
  • chore(deps): bump the dev-tooling group with 3 updates by @dependabot[bot] in #1183
  • chore(deps): bump the vitest family to 5.0.0 by @robwoodgate in #1187
  • test(mutation): give the Stryker vitest config a 20s test timeout by @robwoodgate in #1189
  • ci(mutation): pin vitest 4 for the weekly Stryker run by @robwoodgate in #1191
  • chore(docker): update CDK_IMAGE to cashubtc/mintd:0.18.1 by @robwoodgate in #1193
  • fix(wallet): refuse pre-v3 locks the mint does not advertise by @Kelbie in #1195
  • fix(wallet): refuse keyset id versions this build cannot spend by @robwoodgate in #1199
  • docs: correct usage snippets that no longer compile by @robwoodgate in #1198
  • test(wallet): pin that a proof subscription refuses an unreadable keyset by @robwoodgate in #1201
  • fix(wallet): reject unspendable proofs for an unlocked payment request by @Kelbie in #1197
  • feat(wallet): scope quote lock keys to the mint identity by @robwoodgate in #1202
  • fix(wallet): fill NUT-04 base fields on mint quote subscription updates by @robwoodgate in #1203
  • fix(core): refuse implicit numeric coercion of Amount by @robwoodgate in #1204
  • docs: point at the repository for files the package does not ship by @robwoodgate in #1206
  • fix(utils): refuse text with a lone surrogate in the CBOR and TLV encoders by @robwoodgate in #1209
  • fix(wallet): report the websocket mode on subscriptions without pollMs by @robwoodgate in #1207
  • fix(wallet): normalize melt quote subscription updates like HTTP responses by @robwoodgate in #1208
  • docs: document websocket teardown and injectWebSocketImpl by @robwoodgate in #1215
  • fix(transport): log a failed unsubscribe at info by @robwoodgate in #1214
  • fix(events): skip the batched quote check where the mint does not offer it by @robwoodgate in #1213
  • docs: correct the CREQB1 size claim and scope the pre-flight lock check to v3 by @robwoodgate in #1216
  • fix(transport): export CallerAbortError and UncancellableReadError by @robwoodgate in #1212
  • docs: say that proof-set concurrency is the app's job by @robwoodgate in #1220
  • feat(crypto): add mnemonicToSeedSync by @robwoodgate in #1221
  • fix(wallet): refuse NUT-29 batch operations the mint does not advertise by @robwoodgate in #1222
  • fix(transport): time out request attempts after 5 minutes by default by @robwoodgate in #1224
  • docs: lead the send pages with the recoverable pattern by @robwoodgate in #1223
  • fix(model): accept a structuredClone'd Amount in Amount.from by @robwoodgate in #1228
  • docs: say how Amount survives persistence by @robwoodgate in #1229
  • fix(crypto): write the BLS field orders as literals by @robwoodgate in #1231
  • refactor(auth): build OIDCAuth from mint info, not from Mint by @robwoodgate in #1232
  • build: preserve modules in the ES build by @robwoodgate in #1233
  • docs: make every docs-src snippet type-check by @robwoodgate in #1234
  • docs: tell errors apart by instanceof and name, not constructor.name by @robwoodgate in #1237
  • chore(eslint): use defineConfig instead of the deprecated tseslint.config by @robwoodgate in #1236
  • ci: type-check docs-src snippets against the shipped types by @robwoodgate in #1235
  • fix(wallet): log a keyset summary on load instead of the whole cache by @robwoodgate in #1238
  • fix(transport): quieter retry logging, slower backoff, and NetworkError names the mint by @robwoodgate in #1240
  • refactor(wallet): name lock families by encoding, not version by @robwoodgate in #1242
  • docs: say which keyset a wallet is on and how to tell the curve by @robwoodgate in #1243
  • feat(mint): per-call options with an abort signal on every Mint method by @robwoodgate in #1244
  • feat(wallet): accept an abort signal on wallet operations by @robwoodgate in #1245
  • chore(errors): move stray error classes into model/Errors by @robwoodgate in #1246
  • docs(keysets): say BLS keysets carry no DLEQ and requireDleq accepts them by @robwoodgate in #1248
  • fix(transport): drop the DOM CloseEvent type from the WSConnection public surface by @robwoodgate in #1249
  • fix(transport): fail established subscriptions when the socket closes by @robwoodgate in #1251
  • feat(transport): optional keepalive probe for NUT-17 sockets by @robwoodgate in #1253
  • docs(wallet): mark the quote lock seed derivation experimental by @robwoodgate in #1254
  • chore(main): release 5.0.0-rc.11 by @robwoodgate in #1155

New Contributors

Full Changelog: v5.0.0-rc.10...v5.0.0-rc.11

Don't miss a new cashu-ts release

NewReleases is sending notifications on new releases.