Skip to content

feat: a mint fee band, because the two live mints disagree - #3

Merged
TheCryptoDonkey merged 1 commit into
mainfrom
feat/mint-fee-band
Aug 21, 2026
Merged

feat: a mint fee band, because the two live mints disagree#3
TheCryptoDonkey merged 1 commit into
mainfrom
feat/mint-fee-band

Conversation

@TheCryptoDonkey

Copy link
Copy Markdown
Owner

LUD-25 states the mint fee as base_fee_msat plus a ppm cut and says nothing about rounding. Both live implementations read that differently and both are defensible:

  • dni's lnurl-mint — the reference, and what every public mint on the awesome list except moneyer runs — ceilings the fee to a whole sat on purpose so the mint is "never short a sat". _mint_fee_msat ends return -(-fee_msat // 1000) * 1000.
  • moneyer withholds the msat-exact amount.

So applyMintFee is right about exactly one of them, and a wallet comparing against it warns spuriously against the other.

Measured on real sats today: 40,000 msat at mint.forgesworn.dev with a 1000 + 1000 ppm fee credited 38,000, not the 38,960 the formula gives. notecase called that a discrepancy when the mint had done exactly what it documents.

What this adds

mintFeeBand(grossMsat, fee) returns the range: the formula is the most a holder can be credited, the sat-ceilinged fee the least. withinMintFeeBand(grossMsat, netMsat, fee) is what a caller should compare a credited note against, rather than an equality against applyMintFee — an exact match is the best case, not the only compliant one.

applyMintFee is unchanged and still means the formula. It is now documented as the generous edge of the band rather than the answer.

What this does not do

It does not decide which reading is correct. That is a question for lnurl/luds#301. Until it settles, both are accepted and anything outside the band is still wrong.

Tests use the measured numbers from both mints, plus the degenerate cases: a fee already on a whole sat collapses the band to a point, and a fee larger than the payment never reports a negative net. 272 tests green.

LUD-25 states the mint fee as base_fee_msat plus a ppm cut and says
nothing about rounding. Both live implementations read it differently and
both are defensible:

  - dni's lnurl-mint, the reference, and what every public mint on the
    awesome list except moneyer runs, ceilings the fee to a whole sat on
    purpose so the mint is "never short a sat". _mint_fee_msat ends
    `return -(-fee_msat // 1000) * 1000`.
  - moneyer withholds the msat-exact amount.

So applyMintFee is right about exactly one of them, and a wallet that
compares against it warns spuriously against the other. Measured on real
sats today: 40_000 msat at mint.forgesworn.dev with a 1000 + 1000ppm fee
credited 38_000, not the 38_960 the formula gives, and notecase called
that a discrepancy when the mint had done exactly what it documents.

mintFeeBand gives the range instead: the formula is the most a holder can
be credited, the sat-ceilinged fee the least. withinMintFeeBand is what a
caller should compare a credited note against, rather than an equality
against applyMintFee - an exact match is the best case, not the only
compliant one.

Nothing here decides which reading is correct. That is a question for
lnurl/luds#301; until it settles, both are accepted and anything outside
the band is still wrong.
@TheCryptoDonkey
TheCryptoDonkey merged commit b3a9e3b into main Aug 21, 2026
2 checks passed
@TheCryptoDonkey
TheCryptoDonkey deleted the feat/mint-fee-band branch August 21, 2026 11:50
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