lnurlcash-kit 0.4.0 → 0.5.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 +33 -0
- package/dist/index.d.ts +6 -1
- package/dist/index.js +60 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,39 @@
|
|
|
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.5.0 - 2026-08-29
|
|
7
|
+
|
|
8
|
+
**A large merge is folded in batches rather than sent as one over-long
|
|
9
|
+
URL.** LUD-25 bounds a merge by ordinary URL length, not by anything in the
|
|
10
|
+
protocol: every repeated `k1=` costs about 68 characters, and browsers,
|
|
11
|
+
servers and proxies commonly cap a whole URL near 2000. Past roughly 28
|
|
12
|
+
notes `mergeNotes` built a request that something upstream truncates,
|
|
13
|
+
turning a large merge into a malformed one rather than a clean refusal. It
|
|
14
|
+
now measures the URL this SERVICE's own callback actually produces and
|
|
15
|
+
folds the inputs batch into batch, each merge's output carried into the
|
|
16
|
+
next, as the draft advises.
|
|
17
|
+
|
|
18
|
+
A fold can fail with value already moved, which a single merge never could:
|
|
19
|
+
once a batch has landed, the carried note is one the SERVICE has already
|
|
20
|
+
minted, worth every batch folded so far, and the fold holds its only copy.
|
|
21
|
+
It is returned with the failure whatever the class of failure - a network
|
|
22
|
+
drop, a policy refusal, a k1 count the mint caps at, an input mid-melt
|
|
23
|
+
elsewhere - so a caller that persists what `newSecretsOf` hands it cannot
|
|
24
|
+
lose the fold's own output. The error's class is preserved, so a caller
|
|
25
|
+
telling pending apart from spent still can.
|
|
26
|
+
|
|
27
|
+
Batches are bounded by note count as well as URL length. No LUD-25 field
|
|
28
|
+
advertises a SERVICE's own limit on how many `k1` one request may name, and
|
|
29
|
+
the limits in the wild are tighter than 2000 characters allows - moneyer
|
|
30
|
+
defaults to 21, lnurl-mint to 100 - so batching on length alone built
|
|
31
|
+
requests a conforming mint refuses outright. The default is 20, under the
|
|
32
|
+
tightest cap known.
|
|
33
|
+
|
|
34
|
+
`mergeBatches(callback, k1s, options?)` is exported for callers that want to
|
|
35
|
+
plan the batches themselves - `{budget, maxNotes}`, or a bare number for the
|
|
36
|
+
budget as before. A caller that has learned a SERVICE's real limit should
|
|
37
|
+
pass it.
|
|
38
|
+
|
|
6
39
|
## 0.4.0 - 2026-08-26
|
|
7
40
|
|
|
8
41
|
**Breaking: `restoreNotes` asks by hash, and no longer discloses note
|
package/dist/index.d.ts
CHANGED
|
@@ -112,6 +112,11 @@ type SplitResult = {
|
|
|
112
112
|
changeSignature?: string;
|
|
113
113
|
};
|
|
114
114
|
declare const splitNote: (callback: string, k1s: string[], amountMsat: number, options?: LnurlcashOptions) => Promise<SplitResult>;
|
|
115
|
+
type MergeBatchOptions = {
|
|
116
|
+
budget?: number;
|
|
117
|
+
maxNotes?: number;
|
|
118
|
+
};
|
|
119
|
+
declare const mergeBatches: (callback: string, k1s: string[], options?: MergeBatchOptions | number) => string[][];
|
|
115
120
|
declare const mergeNotes: (callback: string, k1s: string[], options?: LnurlcashOptions) => Promise<RotateResult>;
|
|
116
121
|
type SettledNote = {
|
|
117
122
|
k1: string;
|
|
@@ -337,4 +342,4 @@ declare const createClient: (options?: LnurlcashOptions) => {
|
|
|
337
342
|
};
|
|
338
343
|
type LnurlcashClient = ReturnType<typeof createClient>;
|
|
339
344
|
|
|
340
|
-
export { AmbiguousMintError, AmbiguousMutationError, type BoundMintCommitment, type DecodeOptions, HashLookupUnsupportedError, 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, type NoteInfoByHash, 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 UnresolvedIndex, type ValidatedBoundMintReceipt, type VerifyResult, type WithdrawRequestInfo, type WithdrawSuccessResponse, applyMintFee, buildNoteInfoUrlByHash, buildNoteUrl, claimMintedNote, classifyNoteError, createClient, decodeBolt11AmountMsat, decodePaymentRequest, defaultRandomSecret, deriveNoteRoot, deriveNoteSecret, derivedSecretSource, describeMintFee, encodePaymentRequest, fetchInvoiceVerification, fetchMintAddress, fetchNoteInfo, fetchNoteInfoByHash, fetchPayRequest, formatFeePercent, fromBech32Lnurl, fromLud17, grossUpForMintFee, hashK1, isAllowedServiceUrl, isBech32Lnurl, isBolt11Invoice, isLightningAddress, isPaymentRequest, isPreimage, isValidNoteInput, lightningAddressUsername, meltNote, mergeNotes, mergeNotesWithHash, mintAddressUrl, mintFeeBand, namesMintOutput, 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 };
|
|
345
|
+
export { AmbiguousMintError, AmbiguousMutationError, type BoundMintCommitment, type DecodeOptions, HashLookupUnsupportedError, 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, type NoteInfoByHash, 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 UnresolvedIndex, type ValidatedBoundMintReceipt, type VerifyResult, type WithdrawRequestInfo, type WithdrawSuccessResponse, applyMintFee, buildNoteInfoUrlByHash, buildNoteUrl, claimMintedNote, classifyNoteError, createClient, decodeBolt11AmountMsat, decodePaymentRequest, defaultRandomSecret, deriveNoteRoot, deriveNoteSecret, derivedSecretSource, describeMintFee, encodePaymentRequest, fetchInvoiceVerification, fetchMintAddress, fetchNoteInfo, fetchNoteInfoByHash, fetchPayRequest, formatFeePercent, fromBech32Lnurl, fromLud17, grossUpForMintFee, hashK1, isAllowedServiceUrl, isBech32Lnurl, isBolt11Invoice, isLightningAddress, isPaymentRequest, isPreimage, isValidNoteInput, lightningAddressUsername, meltNote, mergeBatches, mergeNotes, mergeNotesWithHash, mintAddressUrl, mintFeeBand, namesMintOutput, 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
|
@@ -948,8 +948,39 @@ var splitNote = async (callback, k1s, amountMsat, options = {}) => {
|
|
|
948
948
|
throw keepingOutputs(err, [newK1, changeK1]);
|
|
949
949
|
}
|
|
950
950
|
};
|
|
951
|
+
var MAX_URL_CHARS = 2e3;
|
|
952
|
+
var MAX_NOTES_PER_BATCH = 20;
|
|
953
|
+
var mergeBatches = (callback, k1s, options = {}) => {
|
|
954
|
+
const { budget = MAX_URL_CHARS, maxNotes = MAX_NOTES_PER_BATCH } = typeof options === "number" ? { budget: options } : options;
|
|
955
|
+
const placeholder = "0".repeat(64);
|
|
956
|
+
try {
|
|
957
|
+
new URL(callback);
|
|
958
|
+
} catch {
|
|
959
|
+
throw new RequestRefusedError("The service provided an invalid callback URL.");
|
|
960
|
+
}
|
|
961
|
+
const fits = (candidate, carried) => {
|
|
962
|
+
const url = new URL(callback);
|
|
963
|
+
if (carried) url.searchParams.append("k1", placeholder);
|
|
964
|
+
for (const k1 of candidate) url.searchParams.append("k1", k1);
|
|
965
|
+
url.searchParams.append("h", placeholder);
|
|
966
|
+
return url.href.length <= budget;
|
|
967
|
+
};
|
|
968
|
+
const batches = [];
|
|
969
|
+
let batch = [];
|
|
970
|
+
for (const k1 of k1s) {
|
|
971
|
+
const next = [...batch, k1];
|
|
972
|
+
if (batch.length > 0 && (next.length > maxNotes || !fits(next, batches.length > 0))) {
|
|
973
|
+
batches.push(batch);
|
|
974
|
+
batch = [k1];
|
|
975
|
+
} else batch = next;
|
|
976
|
+
}
|
|
977
|
+
if (batch.length > 0) batches.push(batch);
|
|
978
|
+
return batches;
|
|
979
|
+
};
|
|
951
980
|
var mergeNotes = async (callback, k1s, options = {}) => {
|
|
952
981
|
const opts = resolveOptions(options);
|
|
982
|
+
const batches = mergeBatches(callback, k1s);
|
|
983
|
+
if (batches.length > 1) return foldNotes(callback, batches, opts, options);
|
|
953
984
|
const newK1 = opts.randomSecret();
|
|
954
985
|
try {
|
|
955
986
|
const result = await mergeNotesWithHash(callback, k1s, hashK1(newK1), options);
|
|
@@ -961,6 +992,34 @@ var mergeNotes = async (callback, k1s, options = {}) => {
|
|
|
961
992
|
throw keepingOutputs(err, [newK1]);
|
|
962
993
|
}
|
|
963
994
|
};
|
|
995
|
+
var foldNotes = async (callback, batches, opts, options) => {
|
|
996
|
+
let carried = null;
|
|
997
|
+
let signature;
|
|
998
|
+
for (const batch of batches) {
|
|
999
|
+
const inputs = carried === null ? batch : [carried, ...batch];
|
|
1000
|
+
const newK1 = opts.randomSecret();
|
|
1001
|
+
try {
|
|
1002
|
+
const result = await mergeNotesWithHash(callback, inputs, hashK1(newK1), options);
|
|
1003
|
+
carried = newK1;
|
|
1004
|
+
signature = result.signature;
|
|
1005
|
+
} catch (err) {
|
|
1006
|
+
const live = carried === null ? [newK1] : [carried, newK1];
|
|
1007
|
+
if (err instanceof AmbiguousMintError) {
|
|
1008
|
+
throw new AmbiguousMutationError(err.message, live);
|
|
1009
|
+
}
|
|
1010
|
+
if (carried === null) throw keepingOutputs(err, live);
|
|
1011
|
+
if (err instanceof ServiceRejectedError) {
|
|
1012
|
+
err.newSecrets = live;
|
|
1013
|
+
throw err;
|
|
1014
|
+
}
|
|
1015
|
+
throw new AmbiguousMutationError(
|
|
1016
|
+
err instanceof Error ? err.message : String(err),
|
|
1017
|
+
live
|
|
1018
|
+
);
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
return { k1: carried, signature };
|
|
1022
|
+
};
|
|
964
1023
|
var settleNote = async (baseUrl, k1, expectedAmountMsat, signature, options = {}) => {
|
|
965
1024
|
const info = await fetchNoteInfo(
|
|
966
1025
|
withNewK1(baseUrl, k1, expectedAmountMsat, signature),
|
|
@@ -1303,4 +1362,4 @@ var createClient = (options = {}) => ({
|
|
|
1303
1362
|
settleNoteForValue: (noteUrl, terms) => settleNoteForValue(noteUrl, terms, options)
|
|
1304
1363
|
});
|
|
1305
1364
|
|
|
1306
|
-
export { AmbiguousMintError, AmbiguousMutationError, HashLookupUnsupportedError, InsufficientValueError, LnurlcashError, NoteSpentError, NoteUnknownError, PAYMENT_REQUEST_PREFIX, PendingNoteError, ProtocolError, RequestRefusedError, ServiceRejectedError, applyMintFee, buildNoteInfoUrlByHash, buildNoteUrl, claimMintedNote, classifyNoteError, createClient, decodeBolt11AmountMsat, decodePaymentRequest, defaultRandomSecret, deriveNoteRoot, deriveNoteSecret, derivedSecretSource, describeMintFee, encodePaymentRequest, fetchInvoiceVerification, fetchMintAddress, fetchNoteInfo, fetchNoteInfoByHash, fetchPayRequest, formatFeePercent, fromBech32Lnurl, fromLud17, grossUpForMintFee, hashK1, isAllowedServiceUrl, isBech32Lnurl, isBolt11Invoice, isLightningAddress, isPaymentRequest, isPreimage, isValidNoteInput, lightningAddressUsername, meltNote, mergeNotes, mergeNotesWithHash, mintAddressUrl, mintFeeBand, namesMintOutput, 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 };
|
|
1365
|
+
export { AmbiguousMintError, AmbiguousMutationError, HashLookupUnsupportedError, InsufficientValueError, LnurlcashError, NoteSpentError, NoteUnknownError, PAYMENT_REQUEST_PREFIX, PendingNoteError, ProtocolError, RequestRefusedError, ServiceRejectedError, applyMintFee, buildNoteInfoUrlByHash, buildNoteUrl, claimMintedNote, classifyNoteError, createClient, decodeBolt11AmountMsat, decodePaymentRequest, defaultRandomSecret, deriveNoteRoot, deriveNoteSecret, derivedSecretSource, describeMintFee, encodePaymentRequest, fetchInvoiceVerification, fetchMintAddress, fetchNoteInfo, fetchNoteInfoByHash, fetchPayRequest, formatFeePercent, fromBech32Lnurl, fromLud17, grossUpForMintFee, hashK1, isAllowedServiceUrl, isBech32Lnurl, isBolt11Invoice, isLightningAddress, isPaymentRequest, isPreimage, isValidNoteInput, lightningAddressUsername, meltNote, mergeBatches, mergeNotes, mergeNotesWithHash, mintAddressUrl, mintFeeBand, namesMintOutput, 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