@forgesworn/moneyer 0.1.0 → 0.1.2
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 +25 -0
- package/README.md +16 -0
- package/dist/config.d.ts +2 -0
- package/dist/config.js +3 -1
- package/dist/server.js +17 -4
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.1.2] - 2026-08-21
|
|
4
|
+
|
|
5
|
+
- `MONEYER_ROUND_FEE_TO_SAT` ceilings the mint fee to a whole sat, as
|
|
6
|
+
dni's lnurl-mint does on purpose so the mint is "never short a sat".
|
|
7
|
+
LUD-25 says nothing about rounding and `lnurlcash-kit`'s `mintFeeBand`
|
|
8
|
+
accepts both readings, so this is a posture choice rather than a
|
|
9
|
+
compliance one. **Off by default**: turning it on raises what the mint
|
|
10
|
+
withholds, which is not a change to make on a redeploy.
|
|
11
|
+
- The mint path called `applyMintFee` directly rather than the fee helper,
|
|
12
|
+
so it would have ignored that setting entirely - advertising one fee and
|
|
13
|
+
withholding another. Every fee site now goes through one
|
|
14
|
+
`netAfterMintFee`.
|
|
15
|
+
- `roundFeeToSat` is optional on `MoneyerConfig`: that type is public API,
|
|
16
|
+
and an embedder constructing a config should not have to name a field it
|
|
17
|
+
does not care about.
|
|
18
|
+
|
|
19
|
+
## [0.1.1] - 2026-08-21
|
|
20
|
+
|
|
21
|
+
- `withdrawLink` is now the plain URL (`https://host/w`), the form
|
|
22
|
+
lnurl-mint emits and the LUD-25 diagram shows, instead of
|
|
23
|
+
`lnurlw://host/w`. Both are legal readings of the draft; the reference
|
|
24
|
+
stack's is the intended one, and a wallet that only fetches the field
|
|
25
|
+
without LUD-17 translation now works. Raised on the spec as
|
|
26
|
+
lnurl/luds#301.
|
|
27
|
+
|
|
3
28
|
## [0.1.0] - 2026-08-20
|
|
4
29
|
|
|
5
30
|
Initial implementation.
|
package/README.md
CHANGED
|
@@ -97,6 +97,19 @@ import {createMoneyer, configFromEnv} from '@forgesworn/moneyer'
|
|
|
97
97
|
const mint = await createMoneyer(configFromEnv())
|
|
98
98
|
```
|
|
99
99
|
|
|
100
|
+
## The mint fee, and the rounding question
|
|
101
|
+
|
|
102
|
+
LUD-25 gives the fee as a flat `base_fee_msat` plus a ppm cut and says
|
|
103
|
+
nothing about rounding. dni's lnurl-mint, the reference, ceilings it to a
|
|
104
|
+
whole sat on purpose so the mint is never short a sat; moneyer withholds
|
|
105
|
+
the msat-exact amount. Neither is out of spec - lnurlcash-kit's
|
|
106
|
+
`mintFeeBand` treats anything between the two as the mint keeping its
|
|
107
|
+
word, and the conformance grader accepts both.
|
|
108
|
+
|
|
109
|
+
`MONEYER_ROUND_FEE_TO_SAT=true` switches this mint to the reference's
|
|
110
|
+
behaviour. Off by default: turning it on raises what the mint withholds,
|
|
111
|
+
and that is not a change to make behind an operator's back on a redeploy.
|
|
112
|
+
|
|
100
113
|
## Endpoints
|
|
101
114
|
|
|
102
115
|
| | |
|
|
@@ -128,6 +141,9 @@ maths, hashing, signature agreement) and
|
|
|
128
141
|
decoding and preimage verification on the melt path). The companion wallet
|
|
129
142
|
is [`@forgesworn/notecase`](https://github.com/forgesworn/notecase).
|
|
130
143
|
|
|
144
|
+
Other mints, wallets and libraries speaking the same protocol are indexed
|
|
145
|
+
in [awesome-lnurlcash](https://github.com/TheCryptoDonkey/awesome-lnurlcash).
|
|
146
|
+
|
|
131
147
|
## Licence
|
|
132
148
|
|
|
133
149
|
MIT.
|
package/dist/config.d.ts
CHANGED
|
@@ -20,6 +20,7 @@ export type MoneyerConfig = {
|
|
|
20
20
|
maxSendableMsat: number;
|
|
21
21
|
minMintMsat: number;
|
|
22
22
|
mintFee: MintFee | null;
|
|
23
|
+
roundFeeToSat?: boolean;
|
|
23
24
|
signingKey?: string;
|
|
24
25
|
dbPath: string;
|
|
25
26
|
backend: BackendConfig;
|
|
@@ -40,5 +41,6 @@ export declare const DEFAULTS: {
|
|
|
40
41
|
readonly verify: true;
|
|
41
42
|
readonly maxK1s: 21;
|
|
42
43
|
readonly sunset: false;
|
|
44
|
+
readonly roundFeeToSat: false;
|
|
43
45
|
};
|
|
44
46
|
export declare const configFromEnv: (env?: NodeJS.ProcessEnv) => MoneyerConfig;
|
package/dist/config.js
CHANGED
|
@@ -9,7 +9,8 @@ export const DEFAULTS = {
|
|
|
9
9
|
dbPath: 'moneyer.sqlite',
|
|
10
10
|
verify: true,
|
|
11
11
|
maxK1s: 21,
|
|
12
|
-
sunset: false
|
|
12
|
+
sunset: false,
|
|
13
|
+
roundFeeToSat: false
|
|
13
14
|
};
|
|
14
15
|
const int = (value, fallback) => {
|
|
15
16
|
if (value === undefined || value === '')
|
|
@@ -89,6 +90,7 @@ export const configFromEnv = (env = process.env) => {
|
|
|
89
90
|
maxSendableMsat,
|
|
90
91
|
minMintMsat: int(env.MONEYER_MIN_MINT_MSAT, DEFAULTS.minMintMsat),
|
|
91
92
|
mintFee: baseFeeMsat === 0 && feePpm === 0 ? null : { baseFeeMsat, feePpm },
|
|
93
|
+
roundFeeToSat: flag(env.MONEYER_ROUND_FEE_TO_SAT, DEFAULTS.roundFeeToSat),
|
|
92
94
|
...(signingKey ? { signingKey: signingKey.toLowerCase() } : {}),
|
|
93
95
|
dbPath: env.MONEYER_DB ?? DEFAULTS.dbPath,
|
|
94
96
|
backend,
|
package/dist/server.js
CHANGED
|
@@ -40,7 +40,17 @@ export const createMoneyer = async (config, deps = {}) => {
|
|
|
40
40
|
// Melt payment hashes with a live attempt in this process. Reconcile
|
|
41
41
|
// skips these - see melt.ts.
|
|
42
42
|
const inFlight = new Set();
|
|
43
|
-
const mintFeeMsat = (grossMsat) =>
|
|
43
|
+
const mintFeeMsat = (grossMsat) => {
|
|
44
|
+
if (!config.mintFee)
|
|
45
|
+
return 0;
|
|
46
|
+
const exact = grossMsat - applyMintFee(grossMsat, config.mintFee);
|
|
47
|
+
// Both readings sit inside lnurlcash-kit's mintFeeBand, so a wallet
|
|
48
|
+
// is not misled either way - it is told the range up front.
|
|
49
|
+
return config.roundFeeToSat === true ? Math.ceil(exact / 1000) * 1000 : exact;
|
|
50
|
+
};
|
|
51
|
+
// Every quote and every credit goes through here, so the fee posture
|
|
52
|
+
// cannot drift between what is advertised and what is withheld.
|
|
53
|
+
const netAfterMintFee = (grossMsat) => Math.max(0, grossMsat - mintFeeMsat(grossMsat));
|
|
44
54
|
// What the mint advertises as its minimum must survive its own fee: a
|
|
45
55
|
// payRequest whose minSendable nets below the dust floor invites a
|
|
46
56
|
// payment it would then refuse.
|
|
@@ -147,7 +157,10 @@ export const createMoneyer = async (config, deps = {}) => {
|
|
|
147
157
|
minSendable: effectiveMinSendableMsat,
|
|
148
158
|
maxSendable: config.maxSendableMsat,
|
|
149
159
|
metadata: JSON.stringify(metadata),
|
|
150
|
-
|
|
160
|
+
// The plain URL, as lnurl-mint emits and the LUD-25 diagram shows.
|
|
161
|
+
// LUD-17's lnurlw:// is the scheme a wallet puts on a QR, not a
|
|
162
|
+
// field in a JSON body; every other URL here is directly fetchable.
|
|
163
|
+
withdrawLink: `${origin}/w`,
|
|
151
164
|
disposable: false
|
|
152
165
|
});
|
|
153
166
|
}
|
|
@@ -161,7 +174,7 @@ export const createMoneyer = async (config, deps = {}) => {
|
|
|
161
174
|
callback: `${origin}/w`,
|
|
162
175
|
minWithdrawable: config.minMintMsat,
|
|
163
176
|
maxWithdrawable: config.mintFee
|
|
164
|
-
?
|
|
177
|
+
? netAfterMintFee(config.maxSendableMsat)
|
|
165
178
|
: config.maxSendableMsat,
|
|
166
179
|
defaultDescription: config.description,
|
|
167
180
|
payLink: `${origin}/.well-known/lnurlp/${lnurlwMatch[1]}`,
|
|
@@ -184,7 +197,7 @@ export const createMoneyer = async (config, deps = {}) => {
|
|
|
184
197
|
if (amount < effectiveMinSendableMsat || amount > config.maxSendableMsat) {
|
|
185
198
|
return fail('Amount out of range.');
|
|
186
199
|
}
|
|
187
|
-
const net =
|
|
200
|
+
const net = netAfterMintFee(amount);
|
|
188
201
|
if (net < config.minMintMsat)
|
|
189
202
|
return fail('Amount too small to mint a note.');
|
|
190
203
|
// The preimage is the future note's spend secret; its hash is the
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@forgesworn/moneyer",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "An LNURLcash (LUD-25) mint - strikes Lightning bearer notes. Independent implementation, cln/lnd funding sources, SQLite, zero HTTP framework.",
|
|
5
5
|
"author": "TheCryptoDonkey",
|
|
6
6
|
"license": "MIT",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"test:watch": "vitest",
|
|
46
46
|
"typecheck": "tsc --noEmit",
|
|
47
47
|
"dev": "node --experimental-strip-types src/cli.ts --dev",
|
|
48
|
-
"conform": "
|
|
48
|
+
"conform": "vitest run test/conformance.test.ts",
|
|
49
49
|
"check": "npm run typecheck && npm run typecheck:web && npm test && npm run build && npm run web:build && npm pack --dry-run",
|
|
50
50
|
"web:dev": "vite web",
|
|
51
51
|
"web:build": "vite build web",
|