feat: a mint fee band, because the two live mints disagree - #3
Merged
Conversation
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.
This was referenced Aug 21, 2026
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
LUD-25 states the mint fee as
base_fee_msatplus a ppm cut and says nothing about rounding. Both live implementations read that differently and both are defensible:_mint_fee_msatendsreturn -(-fee_msat // 1000) * 1000.So
applyMintFeeis 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 againstapplyMintFee— an exact match is the best case, not the only compliant one.applyMintFeeis 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.