Original release notes
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 singleQUOTE_COUNTER_KEYexport is gone and a customCounterSourcemust acceptmint-quote-lock:<pubkey>keys. Keys derived by rc.10 are not recoverable by scan, so keep theprivkeyof 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 theprivkeywith its quote". - Every
Mintmethod that took a trailingcustomRequestnow takes{ customRequest?, signal? }. Wallet operation configs, builders (.signal()) and quote or state checks accept anAbortSignaltoo; a one-shot operation honours it until preparation finishes and never mid-commit. An aborted call rejects withCallerAbortError, now exported alongsideUncancellableReadError. AmountthrowsAmountErroron implicit numeric coercion (a + 1,a < b,Number(a)); string contexts still work.Amount.fromaccepts astructuredClone'd Amount, so IndexedDB andpostMessageround trips no longer need special handling.getDecodedTokenBinaryrequires the keyset id list.Mint.oidcAuthis removed in favour ofOIDCAuth.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: Infinitydisables it), retries back off more slowly and log at info, and aNetworkErrornames the mint origin and the underlying cause. - The ES build preserves modules, so a bundler that imports only
Amountor the token codec no longer pays for the whole wallet.Walletitself is unchanged in size. - NUT-17: a subscription whose socket closes now has its error callback invoked (any close code), so a plain
on.mintQuotePaidcan resubscribe or fall back. The optionalwsKeepaliveMsonWalletorMintprobes the socket and treats silence as a close, for the half-open connections a proxy leaves behind.WSConnection.onClosetakes aWSCloseEvent, so the shipped types no longer need the DOM lib on Node. mnemonicToSeedSyncderives the BIP-39 seed from a phrase with no new dependency; validate the phrase first with@scure/bip39or 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 https://github.com/cashubtc/cashu-ts/pull/1153
- feat(nutroot): the commit leaf (NUT-10 type 0x04) by @robwoodgate in https://github.com/cashubtc/cashu-ts/pull/1151
- test: raise the vitest timeout and shrink two heavy prepareMint fixtures by @robwoodgate in https://github.com/cashubtc/cashu-ts/pull/1157
- fix(mint): check a bolt11 melt preimage against the invoice payment hash by @robwoodgate in https://github.com/cashubtc/cashu-ts/pull/1161
- fix(utils): apply the keyset unit alphabet to version 01 ids too by @robwoodgate in https://github.com/cashubtc/cashu-ts/pull/1162
- feat(wallet): add serialize helpers for mint, batch mint and melt previews by @robwoodgate in https://github.com/cashubtc/cashu-ts/pull/1159
- fix(auth): check OIDC endpoint schemes against the discovery document by @robwoodgate in https://github.com/cashubtc/cashu-ts/pull/1160
- refactor: route the last UTF-8 decodes through utils/bytes by @robwoodgate in https://github.com/cashubtc/cashu-ts/pull/1168
- docs: say where transport policy lives by @robwoodgate in https://github.com/cashubtc/cashu-ts/pull/1170
- chore(docker): update NUT_IMAGE to cashubtc/nutshell:0.21.0 by @robwoodgate in https://github.com/cashubtc/cashu-ts/pull/1173
- feat(nutroot): melt output amount is the quote amount plus its fee reserve by @robwoodgate in https://github.com/cashubtc/cashu-ts/pull/1172
- fix(model): require boolean true for advertised NUT support by @KvngMikey in https://github.com/cashubtc/cashu-ts/pull/1176
- feat(nutroot): name transcript proof inputs by Y by @robwoodgate in https://github.com/cashubtc/cashu-ts/pull/1175
- chore(deps): bump typescript-eslint from 8.69.0 to 8.70.0 in the eslint group by @dependabot[bot] in https://github.com/cashubtc/cashu-ts/pull/1180
- fix(utils): getDecodedTokenBinary requires the keyset id list by @robwoodgate in https://github.com/cashubtc/cashu-ts/pull/1178
- fix(core): keep a token's witness verbatim when re-encoding by @robwoodgate in https://github.com/cashubtc/cashu-ts/pull/1179
- chore(deps): bump the vitest group with 2 updates by @dependabot[bot] in https://github.com/cashubtc/cashu-ts/pull/1182
- chore(deps): bump the dev-tooling group with 3 updates by @dependabot[bot] in https://github.com/cashubtc/cashu-ts/pull/1183
- chore(deps): bump the vitest family to 5.0.0 by @robwoodgate in https://github.com/cashubtc/cashu-ts/pull/1187
- test(mutation): give the Stryker vitest config a 20s test timeout by @robwoodgate in https://github.com/cashubtc/cashu-ts/pull/1189
- ci(mutation): pin vitest 4 for the weekly Stryker run by @robwoodgate in https://github.com/cashubtc/cashu-ts/pull/1191
- chore(docker): update CDK_IMAGE to cashubtc/mintd:0.18.1 by @robwoodgate in https://github.com/cashubtc/cashu-ts/pull/1193
- fix(wallet): refuse pre-v3 locks the mint does not advertise by @Kelbie in https://github.com/cashubtc/cashu-ts/pull/1195
- fix(wallet): refuse keyset id versions this build cannot spend by @robwoodgate in https://github.com/cashubtc/cashu-ts/pull/1199
- docs: correct usage snippets that no longer compile by @robwoodgate in https://github.com/cashubtc/cashu-ts/pull/1198
- test(wallet): pin that a proof subscription refuses an unreadable keyset by @robwoodgate in https://github.com/cashubtc/cashu-ts/pull/1201
- fix(wallet): reject unspendable proofs for an unlocked payment request by @Kelbie in https://github.com/cashubtc/cashu-ts/pull/1197
- feat(wallet): scope quote lock keys to the mint identity by @robwoodgate in https://github.com/cashubtc/cashu-ts/pull/1202
- fix(wallet): fill NUT-04 base fields on mint quote subscription updates by @robwoodgate in https://github.com/cashubtc/cashu-ts/pull/1203
- fix(core): refuse implicit numeric coercion of Amount by @robwoodgate in https://github.com/cashubtc/cashu-ts/pull/1204
- docs: point at the repository for files the package does not ship by @robwoodgate in https://github.com/cashubtc/cashu-ts/pull/1206
- fix(utils): refuse text with a lone surrogate in the CBOR and TLV encoders by @robwoodgate in https://github.com/cashubtc/cashu-ts/pull/1209
- fix(wallet): report the websocket mode on subscriptions without pollMs by @robwoodgate in https://github.com/cashubtc/cashu-ts/pull/1207
- fix(wallet): normalize melt quote subscription updates like HTTP responses by @robwoodgate in https://github.com/cashubtc/cashu-ts/pull/1208
- docs: document websocket teardown and injectWebSocketImpl by @robwoodgate in https://github.com/cashubtc/cashu-ts/pull/1215
- fix(transport): log a failed unsubscribe at info by @robwoodgate in https://github.com/cashubtc/cashu-ts/pull/1214
- fix(events): skip the batched quote check where the mint does not offer it by @robwoodgate in https://github.com/cashubtc/cashu-ts/pull/1213
- docs: correct the CREQB1 size claim and scope the pre-flight lock check to v3 by @robwoodgate in https://github.com/cashubtc/cashu-ts/pull/1216
- fix(transport): export CallerAbortError and UncancellableReadError by @robwoodgate in https://github.com/cashubtc/cashu-ts/pull/1212
- docs: say that proof-set concurrency is the app's job by @robwoodgate in https://github.com/cashubtc/cashu-ts/pull/1220
- feat(crypto): add mnemonicToSeedSync by @robwoodgate in https://github.com/cashubtc/cashu-ts/pull/1221
- fix(wallet): refuse NUT-29 batch operations the mint does not advertise by @robwoodgate in https://github.com/cashubtc/cashu-ts/pull/1222
- fix(transport): time out request attempts after 5 minutes by default by @robwoodgate in https://github.com/cashubtc/cashu-ts/pull/1224
- docs: lead the send pages with the recoverable pattern by @robwoodgate in https://github.com/cashubtc/cashu-ts/pull/1223
- fix(model): accept a structuredClone'd Amount in Amount.from by @robwoodgate in https://github.com/cashubtc/cashu-ts/pull/1228
- docs: say how Amount survives persistence by @robwoodgate in https://github.com/cashubtc/cashu-ts/pull/1229
- fix(crypto): write the BLS field orders as literals by @robwoodgate in https://github.com/cashubtc/cashu-ts/pull/1231
- refactor(auth): build OIDCAuth from mint info, not from Mint by @robwoodgate in https://github.com/cashubtc/cashu-ts/pull/1232
- build: preserve modules in the ES build by @robwoodgate in https://github.com/cashubtc/cashu-ts/pull/1233
- docs: make every docs-src snippet type-check by @robwoodgate in https://github.com/cashubtc/cashu-ts/pull/1234
- docs: tell errors apart by instanceof and name, not constructor.name by @robwoodgate in https://github.com/cashubtc/cashu-ts/pull/1237
- chore(eslint): use defineConfig instead of the deprecated tseslint.config by @robwoodgate in https://github.com/cashubtc/cashu-ts/pull/1236
- ci: type-check docs-src snippets against the shipped types by @robwoodgate in https://github.com/cashubtc/cashu-ts/pull/1235
- fix(wallet): log a keyset summary on load instead of the whole cache by @robwoodgate in https://github.com/cashubtc/cashu-ts/pull/1238
- fix(transport): quieter retry logging, slower backoff, and NetworkError names the mint by @robwoodgate in https://github.com/cashubtc/cashu-ts/pull/1240
- refactor(wallet): name lock families by encoding, not version by @robwoodgate in https://github.com/cashubtc/cashu-ts/pull/1242
- docs: say which keyset a wallet is on and how to tell the curve by @robwoodgate in https://github.com/cashubtc/cashu-ts/pull/1243
- feat(mint): per-call options with an abort signal on every Mint method by @robwoodgate in https://github.com/cashubtc/cashu-ts/pull/1244
- feat(wallet): accept an abort signal on wallet operations by @robwoodgate in https://github.com/cashubtc/cashu-ts/pull/1245
- chore(errors): move stray error classes into model/Errors by @robwoodgate in https://github.com/cashubtc/cashu-ts/pull/1246
- docs(keysets): say BLS keysets carry no DLEQ and requireDleq accepts them by @robwoodgate in https://github.com/cashubtc/cashu-ts/pull/1248
- fix(transport): drop the DOM CloseEvent type from the WSConnection public surface by @robwoodgate in https://github.com/cashubtc/cashu-ts/pull/1249
- fix(transport): fail established subscriptions when the socket closes by @robwoodgate in https://github.com/cashubtc/cashu-ts/pull/1251
- feat(transport): optional keepalive probe for NUT-17 sockets by @robwoodgate in https://github.com/cashubtc/cashu-ts/pull/1253
- docs(wallet): mark the quote lock seed derivation experimental by @robwoodgate in https://github.com/cashubtc/cashu-ts/pull/1254
- chore(main): release 5.0.0-rc.11 by @robwoodgate in https://github.com/cashubtc/cashu-ts/pull/1155
New Contributors
- @Hardeezah made their first contribution in https://github.com/cashubtc/cashu-ts/pull/1153
Full Changelog: https://github.com/cashubtc/cashu-ts/compare/v5.0.0-rc.10...v5.0.0-rc.11

