402-mcp 4.1.1 → 4.2.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.
@@ -0,0 +1,80 @@
1
+ import type { LnurlcashNoteStore, StoredNote } from '../store/lnurlcash-notes.js';
2
+ /**
3
+ * Note operations shared by the wallet provider (which melts a note to pay a
4
+ * BOLT-11 invoice) and the lnurlcash HTTP rail (which hands a note straight to
5
+ * a paywall). Both need the same crash-safe split and the same reconciliation,
6
+ * and two copies of that would drift.
7
+ */
8
+ /** LUD-03/LUD-25 responses are either a success object or `{status:'ERROR'}`. */
9
+ export interface LnurlError {
10
+ status: 'ERROR';
11
+ reason?: string;
12
+ }
13
+ export interface WithdrawRequest {
14
+ status?: string;
15
+ tag?: string;
16
+ callback?: string;
17
+ k1?: string;
18
+ minWithdrawable?: number;
19
+ maxWithdrawable?: number;
20
+ }
21
+ export interface CallbackOk {
22
+ status?: string;
23
+ verify?: string;
24
+ pr?: string;
25
+ reason?: string;
26
+ }
27
+ export declare function isError(res: unknown): res is LnurlError;
28
+ /**
29
+ * Mint responses are the only thing safe to surface. Anything thrown by the
30
+ * HTTP layer may carry a URL, and our URLs carry note secrets in the query
31
+ * string, so exceptions are never stringified outward.
32
+ */
33
+ export declare class MintRefused extends Error {
34
+ }
35
+ /**
36
+ * The mint doesn't recognise a note we thought we held. Expected, not
37
+ * exceptional: a bearer note can be spent by anyone holding a copy, so every
38
+ * local balance is a cache and the mint is the only authority. Signals "drop
39
+ * this one and try the next", never "fail the payment".
40
+ */
41
+ export declare class NoteGone extends Error {
42
+ }
43
+ export interface NoteOpsOptions {
44
+ /** Per-request HTTP timeout. */
45
+ httpTimeoutMs?: number;
46
+ fetchImpl?: typeof fetch;
47
+ }
48
+ export interface ResolvedNote {
49
+ amountMsat: number;
50
+ callback: string;
51
+ }
52
+ /**
53
+ * `refused` is the mint saying it does not honour this note: unknown, already
54
+ * spent, or pending another operation. `unreachable` is the mint saying
55
+ * nothing at all. The difference matters because a note is only safe to drop
56
+ * on the first: a bearer note is money, and a flaky network must never destroy
57
+ * it.
58
+ */
59
+ export type NoteProbe = {
60
+ ok: true;
61
+ note: ResolvedNote;
62
+ } | {
63
+ ok: false;
64
+ reason: 'refused' | 'unreachable';
65
+ };
66
+ export interface NoteOps {
67
+ probeNote(mint: string, secret: string): Promise<NoteProbe>;
68
+ resolveNote(mint: string, secret: string): Promise<ResolvedNote | null>;
69
+ reconcile(): Promise<void>;
70
+ split(parent: StoredNote, amountMsat: number): Promise<StoredNote>;
71
+ }
72
+ /** Appends params to a callback URL that may already carry some. */
73
+ export declare function callbackUrl(base: string, params: Record<string, string>): string;
74
+ /**
75
+ * The note as a LUD-25 withdraw URL: the secret as `k1`, its value alongside.
76
+ * This is the form a wallet shows and the form a paywall is handed. The
77
+ * `amount` is only a claim; the mint's `maxWithdrawable` is authoritative.
78
+ */
79
+ export declare function noteUrl(note: Pick<StoredNote, 'mint' | 'secret' | 'amountMsat'>): string;
80
+ export declare function createNoteOps(store: LnurlcashNoteStore, options?: NoteOpsOptions): NoteOps;
@@ -0,0 +1,137 @@
1
+ import { generatePreimage, computePaymentHash, fetchJson } from 'farrier-kit';
2
+ export function isError(res) {
3
+ return typeof res === 'object' && res !== null && res.status === 'ERROR';
4
+ }
5
+ /**
6
+ * Mint responses are the only thing safe to surface. Anything thrown by the
7
+ * HTTP layer may carry a URL, and our URLs carry note secrets in the query
8
+ * string, so exceptions are never stringified outward.
9
+ */
10
+ export class MintRefused extends Error {
11
+ }
12
+ /**
13
+ * The mint doesn't recognise a note we thought we held. Expected, not
14
+ * exceptional: a bearer note can be spent by anyone holding a copy, so every
15
+ * local balance is a cache and the mint is the only authority. Signals "drop
16
+ * this one and try the next", never "fail the payment".
17
+ */
18
+ export class NoteGone extends Error {
19
+ }
20
+ const DEFAULT_HTTP_TIMEOUT_MS = 8_000;
21
+ /** Appends params to a callback URL that may already carry some. */
22
+ export function callbackUrl(base, params) {
23
+ const url = new URL(base);
24
+ for (const [k, v] of Object.entries(params))
25
+ url.searchParams.set(k, v);
26
+ return url.toString();
27
+ }
28
+ /**
29
+ * The note as a LUD-25 withdraw URL: the secret as `k1`, its value alongside.
30
+ * This is the form a wallet shows and the form a paywall is handed. The
31
+ * `amount` is only a claim; the mint's `maxWithdrawable` is authoritative.
32
+ */
33
+ export function noteUrl(note) {
34
+ const url = new URL('/w', note.mint);
35
+ url.searchParams.set('k1', note.secret);
36
+ url.searchParams.set('amount', String(note.amountMsat));
37
+ return url.toString();
38
+ }
39
+ export function createNoteOps(store, options = {}) {
40
+ const httpTimeoutMs = options.httpTimeoutMs ?? DEFAULT_HTTP_TIMEOUT_MS;
41
+ const get = async (url) => fetchJson(url, { timeoutMs: httpTimeoutMs, fetchImpl: options.fetchImpl });
42
+ /** Reads a note's current value from the mint, keeping why it failed. */
43
+ async function probeNote(mint, secret) {
44
+ let res;
45
+ try {
46
+ res = await get(callbackUrl(new URL('/w', mint).toString(), { k1: secret }));
47
+ }
48
+ catch {
49
+ return { ok: false, reason: 'unreachable' };
50
+ }
51
+ if (isError(res) || typeof res.maxWithdrawable !== 'number' || typeof res.callback !== 'string') {
52
+ return { ok: false, reason: 'refused' };
53
+ }
54
+ return { ok: true, note: { amountMsat: res.maxWithdrawable, callback: res.callback } };
55
+ }
56
+ /**
57
+ * Reads a note's current value from the mint. `null` means the mint doesn't
58
+ * recognise it: unknown, already spent, or pending another operation.
59
+ */
60
+ async function resolveNote(mint, secret) {
61
+ const probe = await probeNote(mint, secret);
62
+ return probe.ok ? probe.note : null;
63
+ }
64
+ /**
65
+ * Settles notes left in an indeterminate state by an earlier crash, a melt
66
+ * whose outcome never came back, or a note handed to a paywall that may or
67
+ * may not have rotated it. A provisional note the mint knows about means its
68
+ * split landed; one it doesn't means the split never happened. A melting note
69
+ * the mint still honours means the spend failed and the mint restored it.
70
+ */
71
+ async function reconcile() {
72
+ for (const note of [...store.byState('provisional'), ...store.byState('melting')]) {
73
+ const resolved = await resolveNote(note.mint, note.secret);
74
+ if (resolved) {
75
+ store.confirm(note.secret, resolved.amountMsat);
76
+ // A landed split burned its parent, whatever our local copy says.
77
+ if (note.parent)
78
+ store.remove(note.parent);
79
+ }
80
+ else {
81
+ store.remove(note.secret);
82
+ }
83
+ }
84
+ }
85
+ /**
86
+ * Splits `parent` into a note worth exactly `amountMsat` plus change, and
87
+ * returns the exact-value note. A melt invoice must match the note value
88
+ * exactly and a paywall must not be overpaid, so this is how an arbitrary
89
+ * amount gets covered.
90
+ */
91
+ async function split(parent, amountMsat) {
92
+ const resolved = await resolveNote(parent.mint, parent.secret);
93
+ if (!resolved) {
94
+ store.remove(parent.secret);
95
+ throw new NoteGone('note not recognised by the mint');
96
+ }
97
+ const paySecret = generatePreimage();
98
+ const changeSecret = generatePreimage();
99
+ const addedAt = new Date().toISOString();
100
+ // Persisted BEFORE the request, deliberately. The mint keys the new notes
101
+ // to hashes of these secrets and never learns the secrets themselves, so a
102
+ // crash between its swap and our write would destroy the value outright.
103
+ store.addMany([
104
+ { secret: paySecret, mint: parent.mint, amountMsat, state: 'provisional', parent: parent.secret, addedAt },
105
+ { secret: changeSecret, mint: parent.mint, amountMsat: 0, state: 'provisional', parent: parent.secret, addedAt },
106
+ ]);
107
+ let res;
108
+ try {
109
+ res = await get(callbackUrl(resolved.callback, {
110
+ k1: parent.secret,
111
+ amount: String(amountMsat),
112
+ h: computePaymentHash(paySecret),
113
+ h2: computePaymentHash(changeSecret),
114
+ }));
115
+ }
116
+ catch {
117
+ // Outcome unknown. Leave both children provisional; reconcile() decides
118
+ // on the next call, once the mint can be asked again.
119
+ throw new MintRefused('Split outcome unknown; it will be reconciled on the next attempt');
120
+ }
121
+ if (isError(res)) {
122
+ // A refusal is synchronous and burns nothing, so the children are dead.
123
+ store.removeMany([paySecret, changeSecret]);
124
+ throw new MintRefused(res.reason ?? 'Mint refused the split');
125
+ }
126
+ store.remove(parent.secret);
127
+ store.confirm(paySecret, amountMsat);
128
+ const change = await resolveNote(parent.mint, changeSecret);
129
+ if (change)
130
+ store.confirm(changeSecret, change.amountMsat);
131
+ else
132
+ store.remove(changeSecret);
133
+ return store.find(paySecret);
134
+ }
135
+ return { probeNote, resolveNote, reconcile, split };
136
+ }
137
+ //# sourceMappingURL=lnurlcash-ops.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lnurlcash-ops.js","sourceRoot":"","sources":["../../src/wallet/lnurlcash-ops.ts"],"names":[],"mappings":"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"}
@@ -1,40 +1,17 @@
1
- import { tryDecodeBolt11, bolt11AmountMsats, generatePreimage, computePaymentHash, verifyLud21, fetchJson, } from 'farrier-kit';
1
+ import { tryDecodeBolt11, bolt11AmountMsats, verifyLud21, fetchJson, } from 'farrier-kit';
2
+ import { createNoteOps, callbackUrl, isError, MintRefused, NoteGone, } from './lnurlcash-ops.js';
2
3
  const METHOD = 'lnurlcash';
3
4
  const DEFAULTS = {
4
5
  meltTimeoutMs: 60_000,
5
6
  meltPollMs: 1_000,
6
7
  httpTimeoutMs: 8_000,
7
8
  };
8
- function isError(res) {
9
- return typeof res === 'object' && res !== null && res.status === 'ERROR';
10
- }
11
- /**
12
- * Mint responses are the only thing safe to surface. Anything thrown by the
13
- * HTTP layer may carry a URL, and our URLs carry note secrets in the query
14
- * string, so exceptions are never stringified outward.
15
- */
16
- class MintRefused extends Error {
17
- }
18
- /**
19
- * The mint doesn't recognise a note we thought we held. Expected, not
20
- * exceptional: a bearer note can be spent by anyone holding a copy, so every
21
- * local balance is a cache and the mint is the only authority. Signals "drop
22
- * this one and try the next", never "fail the payment".
23
- */
24
- class NoteGone extends Error {
25
- }
26
9
  function sleep(ms) {
27
10
  return new Promise(resolve => setTimeout(resolve, ms));
28
11
  }
29
- /** Appends params to a callback URL that may already carry some. */
30
- function callbackUrl(base, params) {
31
- const url = new URL(base);
32
- for (const [k, v] of Object.entries(params))
33
- url.searchParams.set(k, v);
34
- return url.toString();
35
- }
36
12
  export function createLnurlcashWallet(store, options = {}, lock) {
37
13
  const cfg = { ...DEFAULTS, ...options };
14
+ const ops = createNoteOps(store, { httpTimeoutMs: cfg.httpTimeoutMs, fetchImpl: cfg.fetchImpl });
38
15
  let internalLock = Promise.resolve();
39
16
  const defaultLock = (fn) => {
40
17
  const result = internalLock.catch(() => { }).then(() => fn());
@@ -46,95 +23,9 @@ export function createLnurlcashWallet(store, options = {}, lock) {
46
23
  timeoutMs: cfg.httpTimeoutMs,
47
24
  fetchImpl: cfg.fetchImpl,
48
25
  });
49
- /**
50
- * Reads a note's current value from the mint. `null` means the mint doesn't
51
- * recognise it: unknown, already spent, or pending another operation.
52
- */
53
- async function resolveNote(mint, secret) {
54
- let res;
55
- try {
56
- res = await get(callbackUrl(new URL('/w', mint).toString(), { k1: secret }));
57
- }
58
- catch {
59
- return null;
60
- }
61
- if (isError(res) || typeof res.maxWithdrawable !== 'number' || typeof res.callback !== 'string')
62
- return null;
63
- return { amountMsat: res.maxWithdrawable, callback: res.callback };
64
- }
65
- /**
66
- * Settles notes left in an indeterminate state by an earlier crash or a melt
67
- * whose outcome never came back. A provisional note the mint knows about
68
- * means its split landed; one it doesn't means the split never happened. A
69
- * melting note the mint still honours means the payment failed and the mint
70
- * restored it.
71
- */
72
- async function reconcile() {
73
- for (const note of [...store.byState('provisional'), ...store.byState('melting')]) {
74
- const resolved = await resolveNote(note.mint, note.secret);
75
- if (resolved) {
76
- store.confirm(note.secret, resolved.amountMsat);
77
- // A landed split burned its parent, whatever our local copy says.
78
- if (note.parent)
79
- store.remove(note.parent);
80
- }
81
- else {
82
- store.remove(note.secret);
83
- }
84
- }
85
- }
86
- /**
87
- * Splits `parent` into a note worth exactly `amountMsat` plus change, and
88
- * returns the exact-value note. The mint requires a melt invoice to match
89
- * the note value exactly, so this is how an arbitrary invoice gets paid.
90
- */
91
- async function split(parent, amountMsat) {
92
- const resolved = await resolveNote(parent.mint, parent.secret);
93
- if (!resolved) {
94
- store.remove(parent.secret);
95
- throw new NoteGone('note not recognised by the mint');
96
- }
97
- const paySecret = generatePreimage();
98
- const changeSecret = generatePreimage();
99
- const addedAt = new Date().toISOString();
100
- // Persisted BEFORE the request, deliberately. The mint keys the new notes
101
- // to hashes of these secrets and never learns the secrets themselves, so a
102
- // crash between its swap and our write would destroy the value outright.
103
- store.addMany([
104
- { secret: paySecret, mint: parent.mint, amountMsat, state: 'provisional', parent: parent.secret, addedAt },
105
- { secret: changeSecret, mint: parent.mint, amountMsat: 0, state: 'provisional', parent: parent.secret, addedAt },
106
- ]);
107
- let res;
108
- try {
109
- res = await get(callbackUrl(resolved.callback, {
110
- k1: parent.secret,
111
- amount: String(amountMsat),
112
- h: computePaymentHash(paySecret),
113
- h2: computePaymentHash(changeSecret),
114
- }));
115
- }
116
- catch {
117
- // Outcome unknown. Leave both children provisional; reconcile() decides
118
- // on the next call, once the mint can be asked again.
119
- throw new MintRefused('Split outcome unknown; it will be reconciled on the next attempt');
120
- }
121
- if (isError(res)) {
122
- // A refusal is synchronous and burns nothing, so the children are dead.
123
- store.removeMany([paySecret, changeSecret]);
124
- throw new MintRefused(res.reason ?? 'Mint refused the split');
125
- }
126
- store.remove(parent.secret);
127
- store.confirm(paySecret, amountMsat);
128
- const change = await resolveNote(parent.mint, changeSecret);
129
- if (change)
130
- store.confirm(changeSecret, change.amountMsat);
131
- else
132
- store.remove(changeSecret);
133
- return store.find(paySecret);
134
- }
135
26
  /** Melts `note` against `invoice` and waits for cryptographic settlement proof. */
136
27
  async function melt(note, invoice, paymentHashHex) {
137
- const resolved = await resolveNote(note.mint, note.secret);
28
+ const resolved = await ops.resolveNote(note.mint, note.secret);
138
29
  if (!resolved) {
139
30
  store.remove(note.secret);
140
31
  throw new NoteGone('note not recognised by the mint');
@@ -221,7 +112,7 @@ export function createLnurlcashWallet(store, options = {}, lock) {
221
112
  return { paid: false, method: METHOD, reason: 'Amountless BOLT-11 invoices require an explicit amount and are refused' };
222
113
  }
223
114
  try {
224
- await reconcile();
115
+ await ops.reconcile();
225
116
  }
226
117
  catch { /* best effort; selection below simply sees fewer notes */ }
227
118
  const live = store.live();
@@ -239,7 +130,7 @@ export function createLnurlcashWallet(store, options = {}, lock) {
239
130
  try {
240
131
  const exact = candidate.amountMsat === needMsat
241
132
  ? candidate
242
- : await split(candidate, needMsat);
133
+ : await ops.split(candidate, needMsat);
243
134
  return await melt(exact, invoice, decoded.paymentHashHex);
244
135
  }
245
136
  catch (error) {
@@ -1 +1 @@
1
- {"version":3,"file":"lnurlcash.js","sourceRoot":"","sources":["../../src/wallet/lnurlcash.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,iBAAiB,EACjB,gBAAgB,EAChB,kBAAkB,EAClB,WAAW,EACX,SAAS,GACV,MAAM,aAAa,CAAA;AAIpB,MAAM,MAAM,GAAG,WAAoB,CAAA;AAcnC,MAAM,QAAQ,GAAG;IACf,aAAa,EAAE,MAAM;IACrB,UAAU,EAAE,KAAK;IACjB,aAAa,EAAE,KAAK;CACrB,CAAA;AAcD,SAAS,OAAO,CAAC,GAAY;IAC3B,OAAO,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,IAAK,GAAkB,CAAC,MAAM,KAAK,OAAO,CAAA;AAC1F,CAAC;AAED;;;;GAIG;AACH,MAAM,WAAY,SAAQ,KAAK;CAAG;AAElC;;;;;GAKG;AACH,MAAM,QAAS,SAAQ,KAAK;CAAG;AAE/B,SAAS,KAAK,CAAC,EAAU;IACvB,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAA;AACxD,CAAC;AAED,oEAAoE;AACpE,SAAS,WAAW,CAAC,IAAY,EAAE,MAA8B;IAC/D,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,CAAA;IACzB,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC;QAAE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;IACvE,OAAO,GAAG,CAAC,QAAQ,EAAE,CAAA;AACvB,CAAC;AAED,MAAM,UAAU,qBAAqB,CACnC,KAAyB,EACzB,UAAkC,EAAE,EACpC,IAA8C;IAE9C,MAAM,GAAG,GAAG,EAAE,GAAG,QAAQ,EAAE,GAAG,OAAO,EAAE,CAAA;IAEvC,IAAI,YAAY,GAAqB,OAAO,CAAC,OAAO,EAAE,CAAA;IACtD,MAAM,WAAW,GAAG,CAAI,EAAoB,EAAc,EAAE;QAC1D,MAAM,MAAM,GAAG,YAAY,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,CAAA;QAC5D,YAAY,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAA;QACrC,OAAO,MAAM,CAAA;IACf,CAAC,CAAA;IACD,MAAM,QAAQ,GAAG,IAAI,IAAI,WAAW,CAAA;IAEpC,MAAM,GAAG,GAAG,KAAK,EAAK,GAAW,EAAc,EAAE,CAC/C,SAAS,CAAI,GAAG,EAAE;QAChB,SAAS,EAAE,GAAG,CAAC,aAAa;QAC5B,SAAS,EAAE,GAAG,CAAC,SAAS;KACzB,CAAC,CAAA;IAEJ;;;OAGG;IACH,KAAK,UAAU,WAAW,CAAC,IAAY,EAAE,MAAc;QACrD,IAAI,GAAiC,CAAA;QACrC,IAAI,CAAC;YACH,GAAG,GAAG,MAAM,GAAG,CAA+B,WAAW,CAAC,IAAI,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,QAAQ,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,CAAA;QAC5G,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAA;QACb,CAAC;QACD,IAAI,OAAO,CAAC,GAAG,CAAC,IAAI,OAAO,GAAG,CAAC,eAAe,KAAK,QAAQ,IAAI,OAAO,GAAG,CAAC,QAAQ,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAA;QAC5G,OAAO,EAAE,UAAU,EAAE,GAAG,CAAC,eAAe,EAAE,QAAQ,EAAE,GAAG,CAAC,QAAQ,EAAE,CAAA;IACpE,CAAC;IAED;;;;;;OAMG;IACH,KAAK,UAAU,SAAS;QACtB,KAAK,MAAM,IAAI,IAAI,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,GAAG,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC;YAClF,MAAM,QAAQ,GAAG,MAAM,WAAW,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,CAAA;YAC1D,IAAI,QAAQ,EAAE,CAAC;gBACb,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,UAAU,CAAC,CAAA;gBAC/C,kEAAkE;gBAClE,IAAI,IAAI,CAAC,MAAM;oBAAE,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;YAC5C,CAAC;iBAAM,CAAC;gBACN,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;YAC3B,CAAC;QACH,CAAC;IACH,CAAC;IAED;;;;OAIG;IACH,KAAK,UAAU,KAAK,CAAC,MAAkB,EAAE,UAAkB;QACzD,MAAM,QAAQ,GAAG,MAAM,WAAW,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,CAAA;QAC9D,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;YAC3B,MAAM,IAAI,QAAQ,CAAC,iCAAiC,CAAC,CAAA;QACvD,CAAC;QAED,MAAM,SAAS,GAAG,gBAAgB,EAAE,CAAA;QACpC,MAAM,YAAY,GAAG,gBAAgB,EAAE,CAAA;QACvC,MAAM,OAAO,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAA;QAExC,0EAA0E;QAC1E,2EAA2E;QAC3E,yEAAyE;QACzE,KAAK,CAAC,OAAO,CAAC;YACZ,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE;YAC1G,EAAE,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,UAAU,EAAE,CAAC,EAAE,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE;SACjH,CAAC,CAAA;QAEF,IAAI,GAA4B,CAAA;QAChC,IAAI,CAAC;YACH,GAAG,GAAG,MAAM,GAAG,CAA0B,WAAW,CAAC,QAAQ,CAAC,QAAQ,EAAE;gBACtE,EAAE,EAAE,MAAM,CAAC,MAAM;gBACjB,MAAM,EAAE,MAAM,CAAC,UAAU,CAAC;gBAC1B,CAAC,EAAE,kBAAkB,CAAC,SAAS,CAAC;gBAChC,EAAE,EAAE,kBAAkB,CAAC,YAAY,CAAC;aACrC,CAAC,CAAC,CAAA;QACL,CAAC;QAAC,MAAM,CAAC;YACP,wEAAwE;YACxE,sDAAsD;YACtD,MAAM,IAAI,WAAW,CAAC,kEAAkE,CAAC,CAAA;QAC3F,CAAC;QAED,IAAI,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;YACjB,wEAAwE;YACxE,KAAK,CAAC,UAAU,CAAC,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC,CAAA;YAC3C,MAAM,IAAI,WAAW,CAAC,GAAG,CAAC,MAAM,IAAI,wBAAwB,CAAC,CAAA;QAC/D,CAAC;QAED,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;QAC3B,KAAK,CAAC,OAAO,CAAC,SAAS,EAAE,UAAU,CAAC,CAAA;QAEpC,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,MAAM,CAAC,IAAI,EAAE,YAAY,CAAC,CAAA;QAC3D,IAAI,MAAM;YAAE,KAAK,CAAC,OAAO,CAAC,YAAY,EAAE,MAAM,CAAC,UAAU,CAAC,CAAA;;YACrD,KAAK,CAAC,MAAM,CAAC,YAAY,CAAC,CAAA;QAE/B,OAAO,KAAK,CAAC,IAAI,CAAC,SAAS,CAAE,CAAA;IAC/B,CAAC;IAED,mFAAmF;IACnF,KAAK,UAAU,IAAI,CAAC,IAAgB,EAAE,OAAe,EAAE,cAAsB;QAC3E,MAAM,QAAQ,GAAG,MAAM,WAAW,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,CAAA;QAC1D,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;YACzB,MAAM,IAAI,QAAQ,CAAC,iCAAiC,CAAC,CAAA;QACvD,CAAC;QAED,wEAAwE;QACxE,8DAA8D;QAC9D,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,SAAS,CAAC,CAAA;QAEtC,IAAI,GAA4B,CAAA;QAChC,IAAI,CAAC;YACH,GAAG,GAAG,MAAM,GAAG,CAA0B,WAAW,CAAC,QAAQ,CAAC,QAAQ,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC,CAAA;QAC5G,CAAC;QAAC,MAAM,CAAC;YACP,OAAO;gBACL,IAAI,EAAE,KAAK;gBACX,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,SAAS;gBAClB,MAAM,EAAE,mGAAmG;aAC5G,CAAA;QACH,CAAC;QAED,IAAI,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;YACjB,gEAAgE;YAChE,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;YACnC,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,IAAI,uBAAuB,EAAE,CAAA;QACvF,CAAC;QAED,4EAA4E;QAC5E,uEAAuE;QACvE,IAAI,OAAO,GAAG,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;YACnC,OAAO;gBACL,IAAI,EAAE,KAAK;gBACX,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,SAAS;gBAClB,MAAM,EAAE,yFAAyF;aAClG,CAAA;QACH,CAAC;QAED,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,GAAG,CAAC,aAAa,CAAA;QAC/C,SAAS,CAAC;YACR,IAAI,MAAM,CAAA;YACV,IAAI,CAAC;gBACH,MAAM,GAAG,MAAM,WAAW,CAAC;oBACzB,SAAS,EAAE,GAAG,CAAC,MAAM;oBACrB,cAAc;oBACd,SAAS,EAAE,GAAG,CAAC,SAAS;oBACxB,SAAS,EAAE,GAAG,CAAC,aAAa;oBAC5B,QAAQ,EAAE,GAAG,CAAC,QAAQ;iBACvB,CAAC,CAAA;YACJ,CAAC;YAAC,MAAM,CAAC;gBACP,MAAM,GAAG,SAAS,CAAA;YACpB,CAAC;YAED,IAAI,MAAM,EAAE,OAAO,EAAE,CAAC;gBACpB,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;gBACzB,IAAI,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;oBACvC,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,CAAA;gBAClE,CAAC;gBACD,uEAAuE;gBACvE,qEAAqE;gBACrE,OAAO;oBACL,IAAI,EAAE,KAAK;oBACX,MAAM,EAAE,MAAM;oBACd,OAAO,EAAE,SAAS;oBAClB,MAAM,EAAE,qEAAqE;iBAC9E,CAAA;YACH,CAAC;YAED,IAAI,IAAI,CAAC,GAAG,EAAE,IAAI,QAAQ,EAAE,CAAC;gBAC3B,OAAO;oBACL,IAAI,EAAE,KAAK;oBACX,MAAM,EAAE,MAAM;oBACd,OAAO,EAAE,SAAS;oBAClB,MAAM,EAAE,kHAAkH;iBAC3H,CAAA;YACH,CAAC;YACD,MAAM,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,CAAA;QAC7B,CAAC;IACH,CAAC;IAED,KAAK,UAAU,YAAY,CAAC,OAAe,EAAE,QAA4B;QACvE,MAAM,OAAO,GAAG,eAAe,CAAC,OAAO,CAAC,CAAA;QACxC,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,yBAAyB,EAAE,CAAA;QAC3E,CAAC;QACD,2EAA2E;QAC3E,uEAAuE;QACvE,MAAM,QAAQ,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAA;QAC3C,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;YACtB,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,wEAAwE,EAAE,CAAA;QAC1H,CAAC;QAED,IAAI,CAAC;YACH,MAAM,SAAS,EAAE,CAAA;QACnB,CAAC;QAAC,MAAM,CAAC,CAAC,0DAA0D,CAAC,CAAC;QAEtE,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,EAAE,CAAA;QAEzB,4EAA4E;QAC5E,0EAA0E;QAC1E,2EAA2E;QAC3E,yEAAyE;QACzE,+DAA+D;QAC/D,MAAM,UAAU,GAAG;YACjB,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,KAAK,QAAQ,CAAC;YAC9C,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,GAAG,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,UAAU,CAAC;SACzF,CAAA;QAED,IAAI,IAAI,GAAG,CAAC,CAAA;QACZ,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;YACnC,IAAI,CAAC;gBACH,MAAM,KAAK,GAAG,SAAS,CAAC,UAAU,KAAK,QAAQ;oBAC7C,CAAC,CAAC,SAAS;oBACX,CAAC,CAAC,MAAM,KAAK,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAA;gBACpC,OAAO,MAAM,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,cAAc,CAAC,CAAA;YAC3D,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,KAAK,YAAY,QAAQ,EAAE,CAAC;oBAAC,IAAI,EAAE,CAAC;oBAAC,SAAQ;gBAAC,CAAC;gBACnD,OAAO;oBACL,IAAI,EAAE,KAAK;oBACX,MAAM,EAAE,MAAM;oBACd,MAAM,EAAE,KAAK,YAAY,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,8CAA8C;iBACtG,CAAA;YACH,CAAC;QACH,CAAC;QAED,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,CAAA;QAC5E,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,oEAAoE,CAAC,CAAC,CAAC,EAAE,CAAA;QAC/G,OAAO;YACL,IAAI,EAAE,KAAK;YACX,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,8BAA8B,QAAQ,+BAA+B,IAAI,QAAQ,SAAS,EAAE;SACrG,CAAA;IACH,CAAC;IAED,OAAO;QACL,MAAM,EAAE,MAAM;QACd,IAAI,SAAS;YACX,OAAO,KAAK,CAAC,gBAAgB,EAAE,GAAG,CAAC,CAAA;QACrC,CAAC;QACD,UAAU,CAAC,OAAe,EAAE,OAA2B;YACrD,OAAO,QAAQ,CAAC,GAAG,EAAE,CAAC,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAA;QACvD,CAAC;KACF,CAAA;AACH,CAAC"}
1
+ {"version":3,"file":"lnurlcash.js","sourceRoot":"","sources":["../../src/wallet/lnurlcash.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ export interface LnurlcashChallenge {
2
+ /** Price in sats. */
3
+ amount: number;
4
+ unit: 'sat';
5
+ /** Mints whose notes the server will accept, as hosts or URLs. */
6
+ mints: string[];
7
+ }
8
+ /** Detects whether a 402 response carries an lnurlcash challenge. */
9
+ export declare function isLnurlcashChallenge(headers: Headers): boolean;
10
+ /**
11
+ * Parses an lnurlcash payment request from the X-LNURLcash header value:
12
+ * `lnurlcashreq1` followed by unpadded base64url of the request's RFC 8785
13
+ * canonical JSON.
14
+ *
15
+ * The request is `{ v, id, amount, currency, methodDetails: { mints } }`,
16
+ * with the amount a decimal STRING of whole sats. That is the shape the
17
+ * conformance vectors pin and `lnurlcash-kit` encodes.
18
+ *
19
+ * A shorter object, `{ a: number, u, m }`, briefly went out under the same
20
+ * prefix before the format settled. It is still read here, because a decoder
21
+ * that returns nothing on a string it can plainly understand helps no one,
22
+ * and because the kit's own decoder accepts it too. Nothing emits it.
23
+ *
24
+ * Only `sat` exists today; anything else returns null rather than guessing at
25
+ * a denomination.
26
+ */
27
+ export declare function parseLnurlcashChallenge(header: string): LnurlcashChallenge | null;
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Payment requests are prefixed like a bech32 human-readable part so a bare
3
+ * string is self-describing, the way NUT-18 uses `creqA`.
4
+ */
5
+ const PREFIX = 'lnurlcashreq1';
6
+ /** Detects whether a 402 response carries an lnurlcash challenge. */
7
+ export function isLnurlcashChallenge(headers) {
8
+ const value = headers.get('x-lnurlcash');
9
+ return !!value && value.startsWith(PREFIX);
10
+ }
11
+ /** A whole number of sats that survives the conversion to msat everything downstream works in. */
12
+ function satsFrom(value) {
13
+ const n = typeof value === 'number'
14
+ ? value
15
+ : typeof value === 'string' && /^[0-9]+$/.test(value)
16
+ ? Number(value)
17
+ : NaN;
18
+ if (!Number.isSafeInteger(n) || n <= 0)
19
+ return null;
20
+ return Number.isSafeInteger(n * 1000) ? n : null;
21
+ }
22
+ function stringsFrom(value) {
23
+ if (!Array.isArray(value))
24
+ return null;
25
+ const out = value.filter((m) => typeof m === 'string' && m.length > 0);
26
+ return out.length > 0 ? out : null;
27
+ }
28
+ /**
29
+ * Parses an lnurlcash payment request from the X-LNURLcash header value:
30
+ * `lnurlcashreq1` followed by unpadded base64url of the request's RFC 8785
31
+ * canonical JSON.
32
+ *
33
+ * The request is `{ v, id, amount, currency, methodDetails: { mints } }`,
34
+ * with the amount a decimal STRING of whole sats. That is the shape the
35
+ * conformance vectors pin and `lnurlcash-kit` encodes.
36
+ *
37
+ * A shorter object, `{ a: number, u, m }`, briefly went out under the same
38
+ * prefix before the format settled. It is still read here, because a decoder
39
+ * that returns nothing on a string it can plainly understand helps no one,
40
+ * and because the kit's own decoder accepts it too. Nothing emits it.
41
+ *
42
+ * Only `sat` exists today; anything else returns null rather than guessing at
43
+ * a denomination.
44
+ */
45
+ export function parseLnurlcashChallenge(header) {
46
+ if (!header.startsWith(PREFIX))
47
+ return null;
48
+ try {
49
+ const b64 = header.slice(PREFIX.length);
50
+ const json = Buffer.from(b64, 'base64url').toString('utf-8');
51
+ const data = JSON.parse(json);
52
+ if (typeof data !== 'object' || data === null)
53
+ return null;
54
+ const details = data.methodDetails;
55
+ const longForm = data.amount !== undefined || data.v !== undefined;
56
+ const amount = satsFrom(longForm ? data.amount : data.a);
57
+ const currency = longForm ? data.currency : data.u;
58
+ const mints = stringsFrom(longForm
59
+ ? typeof details === 'object' && details !== null
60
+ ? details.mints
61
+ : undefined
62
+ : data.m);
63
+ if (amount === null || currency !== 'sat' || !mints)
64
+ return null;
65
+ return { amount, unit: 'sat', mints };
66
+ }
67
+ catch {
68
+ return null;
69
+ }
70
+ }
71
+ //# sourceMappingURL=parse.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse.js","sourceRoot":"","sources":["../../src/xlnurlcash/parse.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ import type { LnurlcashNoteStore } from '../store/lnurlcash-notes.js';
2
+ import { type NoteOps, type NoteOpsOptions } from '../wallet/lnurlcash-ops.js';
3
+ import type { LnurlcashChallenge } from './parse.js';
4
+ export interface LnurlcashPaymentResult {
5
+ /** The bearer note URL to send in the X-LNURLcash header. */
6
+ header: string;
7
+ /** Amount in sats the note covers. */
8
+ amountSats: number;
9
+ /**
10
+ * Records what the paywall did with the note. `true` means it granted
11
+ * access, so it rotated the note and our secret is dead. `false` leaves the
12
+ * note held pending: only the mint can say whether it was rotated, and the
13
+ * next lnurlcash operation asks.
14
+ */
15
+ settle: (accepted: boolean) => void;
16
+ }
17
+ /**
18
+ * Mints arrive as bare hosts (`mint.example.com`), origins, or full discovery
19
+ * URLs. Compared on host so all three forms match a stored note's origin.
20
+ */
21
+ export declare function mintHost(value: string): string | null;
22
+ /**
23
+ * Builds a bearer-note payment for an lnurlcash challenge: an exact-value note
24
+ * from an accepted mint, or one split at the mint to the exact price so the
25
+ * paywall is never overpaid and the change stays in the store. Returns null
26
+ * when no note can cover it, so the caller falls through to another rail.
27
+ *
28
+ * The chosen note is held pending before it is returned. Handing a bearer note
29
+ * to a server puts it beyond our control: the server settles by rotating it,
30
+ * and only the mint knows whether that happened.
31
+ */
32
+ export declare function attemptLnurlcashPayment(opts: {
33
+ challenge: LnurlcashChallenge;
34
+ noteStore: LnurlcashNoteStore;
35
+ ops?: NoteOps;
36
+ options?: NoteOpsOptions;
37
+ }): Promise<LnurlcashPaymentResult | null>;