Payout melts and drain ignore mint input fees (balance_tolerance_percent is accidental compensation)
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 5/5
- Estimated time
- Over a week
- Newbie friendliness
- 30/100
- Issue type
- Feature
- Clarity
- Needs clarification
- Activity status
- Active
- Tech stack
- go
- Domain
- backend, fintech-quant, payments
Research direction
Read src/merchant/merchant.go, especially processPayout and PayoutShare (lines 335–347 and 428–435), then inspect DrainMint (943–979) and the wallet fee handling added for #409. Start by comparing the existing payout and drain behavior with a fee-charging wallet; the issue leaves the accounting approach to a maintainer decision. Done means the chosen fee treatment is explicit and covered by tests, including a 100-ppk mint and protection of min_balance.
Written by the indexing model from the issue text.
Description
Fee-economics gap (surfaced by #409/#413): payout melts and drain ignore mint input fees — aimed amounts overstate what a fee-charging mint will actually pay.
Code path (verified on main): processPayout (src/merchant/merchant.go:335–347) computes aimedPaymentAmount := balance - mintConfig.MinBalance from the wallet's face-value balance, and PayoutShare (merchant.go:428–435) melts exactly that amount with MeltToLightning(mint, aimedPaymentAmount, maxCost, addr). At a mint charging input_fee_ppk, melting N sats consumes N + ceil(Σppk/1000) of proofs — the same fee #409 now handles on the incoming side. Nothing on the outgoing side accounts for it:
balance_tolerance_percent(added toaimedPaymentAmountasmaxCost) is the only thing standing between the payout and failure/underpayment at fee-charging mints — a percentage knob doing accidental fee compensation, not fee accounting.DrainMint(merchant.go:943–979) explicitly uses the walletDrain"which doesn't include fees" (merchant.go:953) — the returned amount is face value.- Pricing (
price_per_step+margin) prices the incoming swap fee implicitly at best; the operator's outgoing melt fee per payout cycle is unpriced. On a busy router at a 100-ppk mint this is a small but permanent leak out ofmin_balance.
Repro vehicle: the cloud lab now has a fee-charging mint (#413, mint-fees, input_fee_ppk=100); the signet lane (see the CLN/rune lane issue) makes it real. Unit-level: a PayoutShare test with a stubbed wallet that charges a melt input fee and asserts the aimed amount fails or dips into min_balance.
Live evidence for the premise: mint.coinos.io charges input_fee_ppk=100 (the #409 observation); our own signet mint testnut.cashu.exchange currently charges 0 on all keysets (verified GET /v1/keysets today) — so the gap is invisible in our current testing and only materializes against real-world mints.
Suggested fix shapes (maintainer decision):
- Fee-aware aim: before melting, query the wallet's proof-set fee (the
SwapFeeSatsprimitive from #409 generalizes to "fee to spend wallet proofs at mint X") and aim forbalance − fee − min_balance. - Fee-aware pricing: fold an expected per-payout fee into
price_per_step/marginaccounting (config or docs), keeping melts as-is. - Document the tolerance: if
balance_tolerance_percentis deemed sufficient, document it as the fee mitigation and add a lab test pinning that a 100-ppk mint still pays out within tolerance.
Why an issue, not a PR: choosing between accounting-in-code vs pricing-in-config is a product/economics decision, and shape 1 extends a wallet-port interface that just gained a method in #409 — better decided once #409/#413 land.
Refs: #409 (incoming-side fee handling), #413 (fee-charging lab mint), #339 (v0.6.0 scope).
- Dominant language
- Go
- Stars
- 12
- Forks
- 14
- Avg merge
- 1d 5h
- Merged PRs (30d)
- 246
Getting set up
- No Dockerfile or Docker Compose file
- No pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from OpenTollGate/tollgate-module-basic-go
-
Session event emits the customer's MAC address in the p tag — spec-invalid value on every payment responsePossibly taken @Amperstrand claimed this 2 days ago. Open
Difficulty 2/5 1-3 hours Newbie friendliness 70/100
OpenTollGate/tollgate-module-basic-go#949 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
OpenTollGate/tollgate-module-basic-go#833 · 1 comment ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 65/100
OpenTollGate/tollgate-module-basic-go#813 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 70/100
OpenTollGate/tollgate-module-basic-go#804 ·
Maintainers usually reply within 1 day
-
go-battery needs an ndsctl on PATH: TestPurchaseSessionGuardHoldsThroughTheOutcomeUnknownWindow fails on bare hosts (passes with stub)Possibly taken @Amperstrand claimed this 6 days ago. Open
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
OpenTollGate/tollgate-module-basic-go#726 · 2 comments ·
Maintainers usually reply within 1 day
All issues in OpenTollGate/tollgate-module-basic-go
Similar issues
-
documentation good first issue
Difficulty 2/5 1-3 hours Newbie friendliness 90/100
Maintainers usually reply within 1 day
-
install-path local lint proofs will exit 5 on roster-less machines once #3263 release is pinnedOpenraised-by:reviewer
Difficulty 1/5 1-3 hours Newbie friendliness 90/100
medici-finance/assay#3333 ·
Maintainers usually reply within 1 day
-
enhancement good first issue
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
onurbaydas/VirtoMir#9 ·
-
Add --version flagOpenenhancement good first issue
Difficulty 1/5 Under an hour Newbie friendliness 95/100
syncronix-labs/aegora#12 ·
-
auto-fixable pkg:api
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
tphakala/birdnet-go#4650 · 2 comments · 1 reaction ·
Maintainers usually reply within 1 day