Skip to content

feat: melt lnurlcash notes with @lnurlcash/kit 0.20.2, keeping older mints and note shapes - #17

Merged
TheCryptoDonkey merged 2 commits into
mainfrom
feat/kit-0.20-lud25-compat
Oct 6, 2026
Merged

TheCryptoDonkey merged 2 commits into
mainfrom
feat/kit-0.20-lud25-compat

Conversation

@TheCryptoDonkey

Copy link
Copy Markdown
Owner

What

Moves the lnurlcash melt off the retired lnurlcash-kit (^0.2.1) onto @lnurlcash/kit 0.20.2, exact pin. That kit carries LUD-25 as revised for unified taproot notes: a bearer note is named by its cp1 on the wire, and a certificate travels as c=cs1<amount>.

Backwards compatibility

The kit's fetchNoteInfo now looks a bearer note up only by its cp1 (?p=cp1…), and its raw-k1 fallback is gone. Mints from before the revision read only k1 on that lookup, so using it as-is would leave satgate unable to melt notes from them.

src/lnurlcash-melt.ts therefore asks the mint about a note by its spend (GET <note>?k1=<preimage>), using the kit's guarded lnurlFetch and classifyNoteError:

  • Every mint generation answers ?k1=. A current mint also checks the spend in full.
  • The melt sends the 64-hex preimage as k1, a short form that both generations accept. No …Short variant applies here: a melt names no output, and the only short lookup (fetchNoteInfoByHashShort) sends p=, which k1-only mints don't read.
  • The lookup drops any certificate on the note URL (sig as hex, sig as fixed-HRP cs1, or the current c=cs1<amount>) along with amount. A melt needs no certificate and the mint states the value itself, so a note in any of these shapes melts the same way.
  • The lookup is read loosely: only callback is required, so a mint with no signing key of its own still works.

The unused per-call timeoutMs option is removed (no caller passed it). The kit applies its own timeout, and its transport is process-wide, so satgate configures nothing on it. Otherwise it would also override toll-booth's settings once toll-booth uses the same kit.

ReceivedNote from @forgesworn/toll-booth is used unchanged. Tested against toll-booth 6.2.0 (the lockfile) and 6.2.6 (what ^6.2.0 resolves to today).

Tests

src/lnurlcash.test.ts covers two mint generations. Neither involves live sats: both use the fake Lightning backend.

  • @forgesworn/moneyer 0.17.0 (exact): current LUD-25.
  • moneyer-legacy = npm:@forgesworn/moneyer@0.3.1: an older mint that reads only k1.

For each generation:

  • the full 402 → note → completion → melt path, plus the existing refusal and announcement cases
  • a melt of a held note in each URL shape: c=cs1<amount>, bare k1, k1 with amount, sig as hex, and sig as fixed-HRP cs1. Each test asserts that the mint paid the operator's invoice and that the note is gone
  • a second melt of the same note reports paid: false with an error and does not claim success

One further test shows that the kit's own fetchNoteInfo fails against the legacy mint while satgate's lookup succeeds. I checked that the tests catch the problem: swapping the kit's lookup back in makes the three legacy-mint melt paths fail.

typecheck, test (401), build and check:publish-dependencies pass on Node 22 and 24.

Also

  • chore: an in-range lockfile refresh (npm audit fix, no --force) that clears three advisories already present on main (proxy-addr, ip-address, source-map-js). Without it, CI's npm audit step fails.

Version

I did not bump the version or edit CHANGELOG.md by hand. auto-release bumps from package.json, so a hand-written 1.18.0 plus this feat: commit would release as 1.19.0. When this PR is squash-merged under its feat: title, auto-release produces 1.18.0 with the Features line.

…mints and note shapes

lnurlcash-kit is retired. Its successor, @lnurlcash/kit, carries LUD-25 as
revised for unified taproot notes: a bearer note is named by its cp1 on the
wire and a certificate travels as c=cs1<amount>.

The kit's own lookup now asks after a bearer note only by cp1 (?p=), which
a mint from before that revision does not read. The melt therefore asks
the mint about a note by its spend (?k1=), through the kit's guarded fetch:
every mint generation answers that, and a current one checks the spend in
full. The melt itself still sends the 64-hex preimage as k1, the short form
both generations accept. Any certificate on the note URL - the old sig as
hex or fixed-HRP cs1, or the current c - is dropped from the lookup, so a
note held in any of those shapes melts the same way.

Tests grade both mint generations: moneyer 0.17.0 (current LUD-25) and
moneyer 0.3.1 as moneyer-legacy (reads only k1), through the full 402 path
and by melting each note shape directly. The unused per-call timeoutMs
option goes: the kit sets its own timeout, and its transport is
process-wide.
@TheCryptoDonkey
TheCryptoDonkey merged commit 084dd6d into main Oct 6, 2026
4 checks passed
@TheCryptoDonkey
TheCryptoDonkey deleted the feat/kit-0.20-lud25-compat branch October 6, 2026 10:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant