Skip to content

NIP-47 max fee on pay_invoice - #2444

Open
frnandu wants to merge 3 commits into
nostr-protocol:masterfrom
frnandu:nip47-max-fee-sat
Open

NIP-47 max fee on pay_invoice#2444
frnandu wants to merge 3 commits into
nostr-protocol:masterfrom
frnandu:nip47-max-fee-sat

Conversation

@frnandu

@frnandu frnandu commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

NIP-47: add optional max_fee to pay_invoice

Summary

Adds an optional max_fee parameter (in msats) to the pay_invoice command and a corresponding FEE_LIMIT_EXCEEDED error code, letting clients cap the routing fees a wallet service may pay on top of an invoice amount.

Motivation

Today, NIP-47 provides no way for a client to bound routing fees. A pay_invoice request grants the wallet service implicit authority to pay arbitrary Lightning routing fees on top of the invoice amount. This creates real-world risks for payment coordinators, market makers, and automated services:

  • High-fee route exploitation: a recipient controlling the destination node can route the payment through expensive channels they own, collecting outsized routing fees from the sender's wallet.
  • Unpredictable spend: automated systems cannot reason about worst-case cost per payment, complicating accounting and treasury management.
  • No reconciliation signal: clients have no protocol-level way to verify whether any fee cap was honored, since fees_paid in the response is optional and there's no way to signal a budget.

Lightning node implementations (LND, CLN, Eclair) all expose fee-limit controls in their native RPCs. NIP-47 currently provides no equivalent for wallets serving remote clients.

Changes

  • pay_invoice params: new optional max_fee (integer, msats). Wallet services honoring it MUST NOT send a payment whose total routing cost exceeds amount + max_fee, and MUST return FEE_LIMIT_EXCEEDED instead.
  • New error code: FEE_LIMIT_EXCEEDED added to the global error list; defined as "no payment attempted" so the semantics are atomic with no ambiguous partial state.
  • Reconciliation guidance: clients should treat an absent fees_paid field in the response as a signal that the max_fee budget may not have been enforced — supporting wallets MUST include fees_paid so clients can audit.

Compatibility

Fully backward compatible:

  • Wallets that do not implement fee limits ignore the parameter (per existing NIP-47 unknown-param behavior) and process the payment as before.
  • Clients that don't send max_fee see no behavior change.
  • No new event kinds, no changes to encryption, no changes to other commands.

Design notes

  • Units in msats: matches amount and fees_paid elsewhere in the spec.
  • max_fee covers routing fees only: invoice amount is separately explicit; budget is the delta between debited amount and invoice amount.
  • FEE_LIMIT_EXCEEDED means "no debit occurred": removes any ambiguity about partial attempts or stuck HTLCs.

fmar and others added 3 commits July 20, 2026 12:41
Adds an optional `max_fee` parameter (in msats) to the `pay_invoice`
command. Wallet services honoring the parameter must not send a payment
whose total routing fee exceeds the budget and must return
`FEE_LIMIT_EXCEEDED` instead. Also adds the corresponding error code
to the global error list and clarifies client-side reconciliation
expectations via `fees_paid`.
@frnandu frnandu changed the title NIP-47 max fee sat on pay_invoice NIP-47 max fee on pay_invoice Aug 21, 2026
@frnandu

frnandu commented Aug 27, 2026

Copy link
Copy Markdown
Contributor Author

Would you consider implementing this on coinos wallet @asoltys ?

@frnandu

frnandu commented Aug 27, 2026

Copy link
Copy Markdown
Contributor Author

@MegalithicBTC and on rizful ?

@MegalithicBTC

Copy link
Copy Markdown

Yes, this looks reasonable to me.

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.

2 participants