lnurlcash-kit 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,35 @@
3
3
  Semantic versioning. While the LUD-25 draft is unmerged, `0.x` minor bumps
4
4
  may carry breaking changes; pin an exact version.
5
5
 
6
+ ## 0.3.0 - 2026-08-24
7
+
8
+ - Additive bound-mint receipt parsing and validation for sealed signers:
9
+ `InvoiceResult.mint`, `VerifyResult.mint`, `requireBoundMintQuote` and
10
+ `validateBoundMintReceipt`. The quote must commit the requested `h` and
11
+ exact net amount before payment; the settled LUD-21 response must match the
12
+ invoice and commitment and carry a valid ordinary LUD-25 note signature.
13
+ - `noteSignatureMessageForHash`, `noteSignatureDigestForHash`,
14
+ `verifyNoteSignatureHash` and `verifyNoteSignatureHashAgainst` expose the
15
+ existing signature construction when a signer deliberately retains `k1`.
16
+ Existing secret-based helpers delegate to them unchanged.
17
+
18
+ ## 0.2.1 - 2026-08-22
19
+
20
+ - `WithdrawRequestInfo` carries `payLink`, the way home a SERVICE may
21
+ publish on a note's informational GET. It is the reverse of the
22
+ `withdrawLink` a payRequest advertises, and it is the only route a
23
+ bearer-note wallet has to a mint's discovery document: the document lives
24
+ under a username the note never mentions and cannot be guessed from the
25
+ callback. Without it, a WALLET that has only ever received notes cannot
26
+ read the mint's `previousPubkeys`, so an announced key rotation is
27
+ indistinguishable from a substituted key and gets refused.
28
+ - A `payLink` on any origin but the note's own is dropped rather than
29
+ passed on, so a caller can treat its presence as the fact it looks like.
30
+ Whoever controls the host controls the pin anyway, which is TOFU's own
31
+ argument, but that argument does not stretch to letting a SERVICE
32
+ nominate a THIRD party to vouch for its key history, and refusing costs
33
+ nothing.
34
+
6
35
  ## 0.2.0 - 2026-08-22
7
36
 
8
37
  ### Deterministic note secrets and restore from a seed
package/README.md CHANGED
@@ -358,6 +358,47 @@ is yours from the moment it exists. The claim GET does show the secret to the
358
358
  mint it is a claim on, which is a different thing from showing it to whoever
359
359
  scanned the QR, and you can still rotate if you want the offline signature.
360
360
 
361
+ ### A sealed signer: confirm without exporting the secret
362
+
363
+ A hardware vault cannot use the claim GET above without handing `k1` to its
364
+ companion. A receipt-capable mint can instead commit the quote to the requested
365
+ `h` and exact net amount, then place the ordinary LUD-25 note signature on its
366
+ settled LUD-21 response. The extension is optional; absence means use the
367
+ unchanged preimage-import-and-rotate flow before showing an invoice.
368
+
369
+ ```ts
370
+ import {
371
+ requestInvoice, fetchInvoiceVerification,
372
+ requireBoundMintQuote, validateBoundMintReceipt
373
+ } from 'lnurlcash-kit'
374
+
375
+ const staged = await vault.newSecret() // {id, h}; k1 stays in the vault
376
+ const expectedNetMsat = 21_000
377
+ const quote = await requestInvoice(pay.callback, 21_000, {h: staged.h})
378
+
379
+ // Do this before displaying or paying quote.pr.
380
+ requireBoundMintQuote(quote, staged.h, expectedNetMsat)
381
+ if (!quote.verify) throw new Error('No settlement receipt offered')
382
+
383
+ // After payment, poll quote.verify until settled.
384
+ const verification = await fetchInvoiceVerification(quote.verify)
385
+ const receipt = validateBoundMintReceipt(
386
+ quote,
387
+ verification,
388
+ staged.h,
389
+ expectedNetMsat,
390
+ pinnedMintPubkeys
391
+ )
392
+ await vault.confirm(staged.id, receipt.amountMsat, mintHost, receipt.signature)
393
+ ```
394
+
395
+ `quote.mint` is `{h, amountMsat}` in the typed API (`amount` on the wire).
396
+ The settled response must repeat that commitment and add `signature` (`sig`
397
+ on the wire). Validation matches the invoice, output and amount, refuses a
398
+ pre-settlement signature, and recovers the signer against the pinned current
399
+ or previous mint keys. The payment preimage remains proof of payment; it never
400
+ replaces the vault's staged secret.
401
+
361
402
  ## Asking to be paid
362
403
 
363
404
  "Send me 500 sat" today means handing over a Lightning Address, which is a
package/dist/index.d.ts CHANGED
@@ -39,6 +39,7 @@ type WithdrawRequestInfo = {
39
39
  maxWithdrawable: number;
40
40
  defaultDescription?: string;
41
41
  mintPubkey?: string;
42
+ payLink?: string;
42
43
  };
43
44
  declare const fetchNoteInfo: (url: string, options?: LnurlcashOptions) => Promise<WithdrawRequestInfo>;
44
45
  declare const probeBurnedNote: (url: string, options?: LnurlcashOptions) => Promise<"live" | "gone" | "unknown">;
@@ -132,6 +133,15 @@ type InvoiceResult = {
132
133
  verify?: string;
133
134
  disposable: boolean;
134
135
  mintToHash: boolean;
136
+ mint?: BoundMintCommitment;
137
+ };
138
+ type BoundMintCommitment = {
139
+ h: string;
140
+ amountMsat: number;
141
+ signature?: string;
142
+ };
143
+ type ValidatedBoundMintReceipt = Required<BoundMintCommitment> & {
144
+ pubkey: string;
135
145
  };
136
146
  type InvoiceRequestOptions = LnurlcashOptions & {
137
147
  h?: string;
@@ -141,8 +151,11 @@ type VerifyResult = {
141
151
  settled: boolean;
142
152
  preimage: string | null;
143
153
  pr: string;
154
+ mint?: BoundMintCommitment;
144
155
  };
145
156
  declare const fetchInvoiceVerification: (verifyUrl: string, options?: LnurlcashOptions) => Promise<VerifyResult>;
157
+ declare const requireBoundMintQuote: (invoice: InvoiceResult, expectedH: string, expectedAmountMsat: number) => BoundMintCommitment;
158
+ declare const validateBoundMintReceipt: (invoice: InvoiceResult, verification: VerifyResult, expectedH: string, expectedAmountMsat: number, mintPubkeys: string | string[]) => ValidatedBoundMintReceipt;
146
159
  type MintClaim = {
147
160
  state: 'minted' | 'unminted' | 'pending' | 'spent';
148
161
  k1: string;
@@ -225,7 +238,9 @@ declare const decodePaymentRequest: (value: string, { now }?: DecodeOptions) =>
225
238
  declare const isPaymentRequest: (value: string) => boolean;
226
239
 
227
240
  declare const noteSignatureMessage: (k1: string, amountMsat: number) => string;
241
+ declare const noteSignatureMessageForHash: (h: string, amountMsat: number) => string;
228
242
  declare const noteSignatureDigest: (k1: string, amountMsat: number) => Uint8Array;
243
+ declare const noteSignatureDigestForHash: (h: string, amountMsat: number) => Uint8Array;
229
244
  type SignatureCheck = {
230
245
  valid: true;
231
246
  pubkey: string;
@@ -234,7 +249,9 @@ type SignatureCheck = {
234
249
  pubkey: null;
235
250
  };
236
251
  declare const verifyNoteSignatureAgainst: (k1: string, amountMsat: number, signatureHex: string, mintPubkeys: string | string[]) => SignatureCheck;
252
+ declare const verifyNoteSignatureHashAgainst: (h: string, amountMsat: number, signatureHex: string, mintPubkeys: string | string[]) => SignatureCheck;
237
253
  declare const verifyNoteSignature: (k1: string, amountMsat: number, signatureHex: string, mintPubkeys: string | string[]) => boolean;
254
+ declare const verifyNoteSignatureHash: (h: string, amountMsat: number, signatureHex: string, mintPubkeys: string | string[]) => boolean;
238
255
 
239
256
  declare const isBolt11Invoice: (value: string) => boolean;
240
257
  declare const sameInvoice: (a: string, b: string) => boolean;
@@ -296,4 +313,4 @@ declare const createClient: (options?: LnurlcashOptions) => {
296
313
  };
297
314
  type LnurlcashClient = ReturnType<typeof createClient>;
298
315
 
299
- export { AmbiguousMintError, AmbiguousMutationError, type DecodeOptions, type HashedMutationResult, type HashedSplitResult, InsufficientValueError, type InvoiceRequestOptions, type InvoiceResult, type LnurlcashClient, LnurlcashError, type LnurlcashOptions, type MeltResult, type MintAddressInfo, type MintClaim, type MintContact, type MintFee, type MintFeeBand, NoteSpentError, NoteUnknownError, PAYMENT_REQUEST_PREFIX, type PayRequestInfo, type PaymentRequest, type PaymentRequestMethodDetails, PendingNoteError, ProtocolError, type RandomSecret, RequestRefusedError, type RestoreOptions, type RestoreResult, type RestoredNote, type RotateResult, ServiceRejectedError, type SettleForValueOptions, type SettledForValue, type SettledNote, type SignatureCheck, type SplitResult, type VerifyResult, type WithdrawRequestInfo, type WithdrawSuccessResponse, applyMintFee, buildNoteUrl, claimMintedNote, classifyNoteError, createClient, decodeBolt11AmountMsat, decodePaymentRequest, defaultRandomSecret, deriveNoteRoot, deriveNoteSecret, derivedSecretSource, describeMintFee, encodePaymentRequest, fetchInvoiceVerification, fetchMintAddress, fetchNoteInfo, fetchPayRequest, formatFeePercent, fromBech32Lnurl, fromLud17, grossUpForMintFee, hashK1, isAllowedServiceUrl, isBech32Lnurl, isBolt11Invoice, isLightningAddress, isPaymentRequest, isPreimage, isValidNoteInput, lightningAddressUsername, meltNote, mergeNotes, mergeNotesWithHash, mintAddressUrl, mintFeeBand, newSecretsOf, noteDeclaredAmount, noteK1, noteSignature, noteSignatureDigest, noteSignatureMessage, parseMintFee, paymentRequestAmountMsat, probeBurnedNote, requestInvoice, requireNoteK1, resolveLnurlInput, resolveMintInput, resolveNoteInput, restoreNotes, rotateNote, rotateNoteWithHash, sameInvoice, serverOf, settleNote, settleNoteForValue, splitNote, splitNoteWithHash, toBech32Lnurl, toLud17w, verifyNoteSignature, verifyNoteSignatureAgainst, withNewK1, withinMintFeeBand, withoutK1 };
316
+ export { AmbiguousMintError, AmbiguousMutationError, type BoundMintCommitment, type DecodeOptions, type HashedMutationResult, type HashedSplitResult, InsufficientValueError, type InvoiceRequestOptions, type InvoiceResult, type LnurlcashClient, LnurlcashError, type LnurlcashOptions, type MeltResult, type MintAddressInfo, type MintClaim, type MintContact, type MintFee, type MintFeeBand, NoteSpentError, NoteUnknownError, PAYMENT_REQUEST_PREFIX, type PayRequestInfo, type PaymentRequest, type PaymentRequestMethodDetails, PendingNoteError, ProtocolError, type RandomSecret, RequestRefusedError, type RestoreOptions, type RestoreResult, type RestoredNote, type RotateResult, ServiceRejectedError, type SettleForValueOptions, type SettledForValue, type SettledNote, type SignatureCheck, type SplitResult, type ValidatedBoundMintReceipt, type VerifyResult, type WithdrawRequestInfo, type WithdrawSuccessResponse, applyMintFee, buildNoteUrl, claimMintedNote, classifyNoteError, createClient, decodeBolt11AmountMsat, decodePaymentRequest, defaultRandomSecret, deriveNoteRoot, deriveNoteSecret, derivedSecretSource, describeMintFee, encodePaymentRequest, fetchInvoiceVerification, fetchMintAddress, fetchNoteInfo, fetchPayRequest, formatFeePercent, fromBech32Lnurl, fromLud17, grossUpForMintFee, hashK1, isAllowedServiceUrl, isBech32Lnurl, isBolt11Invoice, isLightningAddress, isPaymentRequest, isPreimage, isValidNoteInput, lightningAddressUsername, meltNote, mergeNotes, mergeNotesWithHash, mintAddressUrl, mintFeeBand, newSecretsOf, noteDeclaredAmount, noteK1, noteSignature, noteSignatureDigest, noteSignatureDigestForHash, noteSignatureMessage, noteSignatureMessageForHash, parseMintFee, paymentRequestAmountMsat, probeBurnedNote, requestInvoice, requireBoundMintQuote, requireNoteK1, resolveLnurlInput, resolveMintInput, resolveNoteInput, restoreNotes, rotateNote, rotateNoteWithHash, sameInvoice, serverOf, settleNote, settleNoteForValue, splitNote, splitNoteWithHash, toBech32Lnurl, toLud17w, validateBoundMintReceipt, verifyNoteSignature, verifyNoteSignatureAgainst, verifyNoteSignatureHash, verifyNoteSignatureHashAgainst, withNewK1, withinMintFeeBand, withoutK1 };
package/dist/index.js CHANGED
@@ -627,6 +627,62 @@ var describeMintFee = (fee) => [
627
627
  fee.baseFeeMsat > 0 ? `${Math.round(fee.baseFeeMsat / 1e3)} sat flat` : null,
628
628
  fee.feePpm > 0 ? `${formatFeePercent(fee.feePpm)}% of the amount paid` : null
629
629
  ].filter(Boolean).join(" + ");
630
+ var LIGHTNING_SIGNED_MESSAGE_PREFIX = utf8ToBytes("Lightning Signed Message:");
631
+ var noteSignatureMessage = (k1, amountMsat) => noteSignatureMessageForHash(hashK1(k1), amountMsat);
632
+ var noteSignatureMessageForHash = (h, amountMsat) => `LNURLcash:${amountMsat}:${h.trim().toLowerCase()}`;
633
+ var noteSignatureDigest = (k1, amountMsat) => noteSignatureDigestForHash(hashK1(k1), amountMsat);
634
+ var noteSignatureDigestForHash = (h, amountMsat) => sha256(
635
+ sha256(
636
+ new Uint8Array([
637
+ ...LIGHTNING_SIGNED_MESSAGE_PREFIX,
638
+ ...utf8ToBytes(noteSignatureMessageForHash(h, amountMsat))
639
+ ])
640
+ )
641
+ );
642
+ var NO_MATCH = { valid: false, pubkey: null };
643
+ var verifyNoteSignatureAgainst = (k1, amountMsat, signatureHex, mintPubkeys) => {
644
+ let h;
645
+ try {
646
+ h = hashK1(k1);
647
+ } catch {
648
+ return NO_MATCH;
649
+ }
650
+ return verifyNoteSignatureHashAgainst(h, amountMsat, signatureHex, mintPubkeys);
651
+ };
652
+ var verifyNoteSignatureHashAgainst = (h, amountMsat, signatureHex, mintPubkeys) => {
653
+ const targets = (Array.isArray(mintPubkeys) ? mintPubkeys : [mintPubkeys]).filter((key) => typeof key === "string").map((key) => key.trim().toLowerCase());
654
+ if (targets.length === 0) return NO_MATCH;
655
+ let wireSig;
656
+ try {
657
+ wireSig = hexToBytes(signatureHex);
658
+ } catch {
659
+ return NO_MATCH;
660
+ }
661
+ if (wireSig.length !== 65) return NO_MATCH;
662
+ let digest;
663
+ try {
664
+ if (!/^[0-9a-fA-F]{64}$/.test(h.trim())) return NO_MATCH;
665
+ digest = noteSignatureDigestForHash(h, amountMsat);
666
+ } catch {
667
+ return NO_MATCH;
668
+ }
669
+ const recoveryIdFirst = new Uint8Array([
670
+ wireSig[64],
671
+ ...wireSig.subarray(0, 64)
672
+ ]);
673
+ for (const candidate of [recoveryIdFirst, wireSig]) {
674
+ try {
675
+ const recovered = bytesToHex(
676
+ secp256k1.recoverPublicKey(candidate, digest, { prehash: false })
677
+ );
678
+ if (targets.includes(recovered)) return { valid: true, pubkey: recovered };
679
+ } catch {
680
+ }
681
+ }
682
+ return NO_MATCH;
683
+ };
684
+ var verifyNoteSignature = (k1, amountMsat, signatureHex, mintPubkeys) => verifyNoteSignatureAgainst(k1, amountMsat, signatureHex, mintPubkeys).valid;
685
+ var verifyNoteSignatureHash = (h, amountMsat, signatureHex, mintPubkeys) => verifyNoteSignatureHashAgainst(h, amountMsat, signatureHex, mintPubkeys).valid;
630
686
 
631
687
  // src/client.ts
632
688
  var fetchNoteInfo = async (url, options = {}) => {
@@ -649,7 +705,22 @@ var fetchNoteInfo = async (url, options = {}) => {
649
705
  "The service echoed back a different k1 than was queried - the note may have been redeemed elsewhere, or the service isn't spec-compliant."
650
706
  );
651
707
  }
652
- return body;
708
+ const info = body;
709
+ const payLink = sameOriginPayLink(body.payLink, reqUrl);
710
+ if (payLink === void 0) delete info.payLink;
711
+ else info.payLink = payLink;
712
+ return info;
713
+ };
714
+ var sameOriginPayLink = (value, noteUrl) => {
715
+ if (typeof value !== "string" || value.length === 0) return void 0;
716
+ let candidate;
717
+ try {
718
+ candidate = new URL(value, noteUrl);
719
+ } catch {
720
+ return void 0;
721
+ }
722
+ if (candidate.origin !== noteUrl.origin) return void 0;
723
+ return candidate.toString();
653
724
  };
654
725
  var probeBurnedNote = async (url, options = {}) => {
655
726
  try {
@@ -890,6 +961,18 @@ var fetchPayRequest = async (url, options = {}) => {
890
961
  mintToHash: asBoolean(body.mintToHash)
891
962
  };
892
963
  };
964
+ var asBoundMintCommitment = (value) => {
965
+ if (!value || typeof value !== "object") return void 0;
966
+ const raw = value;
967
+ if (typeof raw.h !== "string" || !/^[0-9a-fA-F]{64}$/.test(raw.h) || typeof raw.amount !== "number" || !Number.isSafeInteger(raw.amount) || raw.amount <= 0 || raw.sig !== void 0 && typeof raw.sig !== "string") {
968
+ return void 0;
969
+ }
970
+ return {
971
+ h: raw.h.toLowerCase(),
972
+ amountMsat: raw.amount,
973
+ ...typeof raw.sig === "string" ? { signature: raw.sig } : {}
974
+ };
975
+ };
893
976
  var requestInvoice = async (payCallback, amountMsat, options = {}) => {
894
977
  const cbUrl = new URL(payCallback);
895
978
  cbUrl.searchParams.set("amount", String(amountMsat));
@@ -915,7 +998,8 @@ var requestInvoice = async (payCallback, amountMsat, options = {}) => {
915
998
  pr: body.pr,
916
999
  verify: typeof body.verify === "string" ? body.verify : void 0,
917
1000
  disposable: body.disposable !== false,
918
- mintToHash: body.mintToHash === true
1001
+ mintToHash: body.mintToHash === true,
1002
+ mint: asBoundMintCommitment(body.mint)
919
1003
  };
920
1004
  };
921
1005
  var fetchInvoiceVerification = async (verifyUrl, options = {}) => {
@@ -926,9 +1010,56 @@ var fetchInvoiceVerification = async (verifyUrl, options = {}) => {
926
1010
  return {
927
1011
  settled: body.settled,
928
1012
  preimage: typeof body.preimage === "string" ? body.preimage : null,
929
- pr: body.pr
1013
+ pr: body.pr,
1014
+ mint: asBoundMintCommitment(body.mint)
930
1015
  };
931
1016
  };
1017
+ var requireBoundMintQuote = (invoice, expectedH, expectedAmountMsat) => {
1018
+ const h = expectedH.trim().toLowerCase();
1019
+ if (!isPreimage(h)) throw new ProtocolError("The expected mint output hash is malformed.");
1020
+ if (!Number.isSafeInteger(expectedAmountMsat) || expectedAmountMsat <= 0) {
1021
+ throw new ProtocolError("The expected mint amount must be positive integer millisatoshis.");
1022
+ }
1023
+ if (!invoice.mintToHash || !invoice.mint) {
1024
+ throw new ProtocolError("The service did not commit this quote to a bound mint output.");
1025
+ }
1026
+ if (invoice.mint.h !== h) {
1027
+ throw new ProtocolError("The service committed the quote to a different mint output.");
1028
+ }
1029
+ if (invoice.mint.amountMsat !== expectedAmountMsat) {
1030
+ throw new ProtocolError("The service committed the quote to a different mint amount.");
1031
+ }
1032
+ if (invoice.mint.signature !== void 0) {
1033
+ throw new ProtocolError("The service signed a mint output before the invoice settled.");
1034
+ }
1035
+ return invoice.mint;
1036
+ };
1037
+ var validateBoundMintReceipt = (invoice, verification, expectedH, expectedAmountMsat, mintPubkeys) => {
1038
+ const quote = requireBoundMintQuote(invoice, expectedH, expectedAmountMsat);
1039
+ if (!verification.settled) {
1040
+ throw new ProtocolError("The invoice has not settled.");
1041
+ }
1042
+ if (!sameInvoice(invoice.pr, verification.pr)) {
1043
+ throw new ProtocolError("The settlement receipt names a different invoice.");
1044
+ }
1045
+ const receipt = verification.mint;
1046
+ if (!receipt || receipt.h !== quote.h || receipt.amountMsat !== quote.amountMsat) {
1047
+ throw new ProtocolError("The settlement receipt does not match the mint quote.");
1048
+ }
1049
+ if (!receipt.signature) {
1050
+ throw new ProtocolError("The settled mint receipt has no note signature.");
1051
+ }
1052
+ const checked = verifyNoteSignatureHashAgainst(
1053
+ receipt.h,
1054
+ receipt.amountMsat,
1055
+ receipt.signature,
1056
+ mintPubkeys
1057
+ );
1058
+ if (!checked.valid) {
1059
+ throw new ProtocolError("The settled mint receipt has an invalid note signature.");
1060
+ }
1061
+ return { ...receipt, signature: receipt.signature, pubkey: checked.pubkey };
1062
+ };
932
1063
  var claimMintedNote = async (withdrawLink, k1, options = {}) => {
933
1064
  const secret = k1.trim().toLowerCase();
934
1065
  if (!isPreimage(secret)) {
@@ -956,49 +1087,6 @@ var claimMintedNote = async (withdrawLink, k1, options = {}) => {
956
1087
  throw err;
957
1088
  }
958
1089
  };
959
- var LIGHTNING_SIGNED_MESSAGE_PREFIX = utf8ToBytes("Lightning Signed Message:");
960
- var noteSignatureMessage = (k1, amountMsat) => `LNURLcash:${amountMsat}:${hashK1(k1)}`;
961
- var noteSignatureDigest = (k1, amountMsat) => sha256(
962
- sha256(
963
- new Uint8Array([
964
- ...LIGHTNING_SIGNED_MESSAGE_PREFIX,
965
- ...utf8ToBytes(noteSignatureMessage(k1, amountMsat))
966
- ])
967
- )
968
- );
969
- var NO_MATCH = { valid: false, pubkey: null };
970
- var verifyNoteSignatureAgainst = (k1, amountMsat, signatureHex, mintPubkeys) => {
971
- const targets = (Array.isArray(mintPubkeys) ? mintPubkeys : [mintPubkeys]).filter((key) => typeof key === "string").map((key) => key.trim().toLowerCase());
972
- if (targets.length === 0) return NO_MATCH;
973
- let wireSig;
974
- try {
975
- wireSig = hexToBytes(signatureHex);
976
- } catch {
977
- return NO_MATCH;
978
- }
979
- if (wireSig.length !== 65) return NO_MATCH;
980
- let digest;
981
- try {
982
- digest = noteSignatureDigest(k1, amountMsat);
983
- } catch {
984
- return NO_MATCH;
985
- }
986
- const recoveryIdFirst = new Uint8Array([
987
- wireSig[64],
988
- ...wireSig.subarray(0, 64)
989
- ]);
990
- for (const candidate of [recoveryIdFirst, wireSig]) {
991
- try {
992
- const recovered = bytesToHex(
993
- secp256k1.recoverPublicKey(candidate, digest, { prehash: false })
994
- );
995
- if (targets.includes(recovered)) return { valid: true, pubkey: recovered };
996
- } catch {
997
- }
998
- }
999
- return NO_MATCH;
1000
- };
1001
- var verifyNoteSignature = (k1, amountMsat, signatureHex, mintPubkeys) => verifyNoteSignatureAgainst(k1, amountMsat, signatureHex, mintPubkeys).valid;
1002
1090
 
1003
1091
  // src/settle.ts
1004
1092
  var normaliseHost = (value) => serverOf(value.trim().replace(/^@/, "")).toLowerCase();
@@ -1104,4 +1192,4 @@ var createClient = (options = {}) => ({
1104
1192
  settleNoteForValue: (noteUrl, terms) => settleNoteForValue(noteUrl, terms, options)
1105
1193
  });
1106
1194
 
1107
- export { AmbiguousMintError, AmbiguousMutationError, InsufficientValueError, LnurlcashError, NoteSpentError, NoteUnknownError, PAYMENT_REQUEST_PREFIX, PendingNoteError, ProtocolError, RequestRefusedError, ServiceRejectedError, applyMintFee, buildNoteUrl, claimMintedNote, classifyNoteError, createClient, decodeBolt11AmountMsat, decodePaymentRequest, defaultRandomSecret, deriveNoteRoot, deriveNoteSecret, derivedSecretSource, describeMintFee, encodePaymentRequest, fetchInvoiceVerification, fetchMintAddress, fetchNoteInfo, fetchPayRequest, formatFeePercent, fromBech32Lnurl, fromLud17, grossUpForMintFee, hashK1, isAllowedServiceUrl, isBech32Lnurl, isBolt11Invoice, isLightningAddress, isPaymentRequest, isPreimage, isValidNoteInput, lightningAddressUsername, meltNote, mergeNotes, mergeNotesWithHash, mintAddressUrl, mintFeeBand, newSecretsOf, noteDeclaredAmount, noteK1, noteSignature, noteSignatureDigest, noteSignatureMessage, parseMintFee, paymentRequestAmountMsat, probeBurnedNote, requestInvoice, requireNoteK1, resolveLnurlInput, resolveMintInput, resolveNoteInput, restoreNotes, rotateNote, rotateNoteWithHash, sameInvoice, serverOf, settleNote, settleNoteForValue, splitNote, splitNoteWithHash, toBech32Lnurl, toLud17w, verifyNoteSignature, verifyNoteSignatureAgainst, withNewK1, withinMintFeeBand, withoutK1 };
1195
+ export { AmbiguousMintError, AmbiguousMutationError, InsufficientValueError, LnurlcashError, NoteSpentError, NoteUnknownError, PAYMENT_REQUEST_PREFIX, PendingNoteError, ProtocolError, RequestRefusedError, ServiceRejectedError, applyMintFee, buildNoteUrl, claimMintedNote, classifyNoteError, createClient, decodeBolt11AmountMsat, decodePaymentRequest, defaultRandomSecret, deriveNoteRoot, deriveNoteSecret, derivedSecretSource, describeMintFee, encodePaymentRequest, fetchInvoiceVerification, fetchMintAddress, fetchNoteInfo, fetchPayRequest, formatFeePercent, fromBech32Lnurl, fromLud17, grossUpForMintFee, hashK1, isAllowedServiceUrl, isBech32Lnurl, isBolt11Invoice, isLightningAddress, isPaymentRequest, isPreimage, isValidNoteInput, lightningAddressUsername, meltNote, mergeNotes, mergeNotesWithHash, mintAddressUrl, mintFeeBand, newSecretsOf, noteDeclaredAmount, noteK1, noteSignature, noteSignatureDigest, noteSignatureDigestForHash, noteSignatureMessage, noteSignatureMessageForHash, parseMintFee, paymentRequestAmountMsat, probeBurnedNote, requestInvoice, requireBoundMintQuote, requireNoteK1, resolveLnurlInput, resolveMintInput, resolveNoteInput, restoreNotes, rotateNote, rotateNoteWithHash, sameInvoice, serverOf, settleNote, settleNoteForValue, splitNote, splitNoteWithHash, toBech32Lnurl, toLud17w, validateBoundMintReceipt, verifyNoteSignature, verifyNoteSignatureAgainst, verifyNoteSignatureHash, verifyNoteSignatureHashAgainst, withNewK1, withinMintFeeBand, withoutK1 };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lnurlcash-kit",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "LNURLcash (LUD-25) bearer note client for TypeScript - mint, rotate, split, merge, melt, and verify offline",
5
5
  "author": "TheCryptoDonkey",
6
6
  "license": "MIT",
@@ -60,7 +60,7 @@
60
60
  },
61
61
  "devDependencies": {
62
62
  "@types/node": "^26.2.0",
63
- "lnurlcash-conformance": "^0.1.1",
63
+ "lnurlcash-conformance": "^0.3.0",
64
64
  "tsup": "^8.5.0",
65
65
  "typescript": "^5.7.0",
66
66
  "vitest": "^3.0.0"