feat(v2): principal-based fee split + per-direction config (closes #38) #42
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feat/principal-based-fees"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Layer 1 of the operator-configurable fee architecture (parent: #37). Closes the load-bearing super under-payment bug standalone, independent of Layers 2 (#39) and 3 (
aiolabs/lamassu-next#57).Architectural intent (per #37):
Bug closed (Bug 1 from #37): pre-#38 math interpreted
super_fee_fractionas fraction-of-fee rather than fraction-of-principal, paying the super ~13× below intent on every cash-out since the bitspire wire-shape landed. Now: principal-based, direction-aware.Commits
d87d0db— m009 schema + Pydantic models. Adds 4 directional fee columns (super × 2 directions, machine × 2 directions) +fee_mismatch_satssettlement column. Backfills existingsuper_fee_fractioninto both directional fields, then drops it.4cd0041— CRUD + per-direction fee cap validation. 15% cap per direction (super + operator), enforced at API boundary. Cross-row checks: super-config update validates against all machines; machine update validates against current super-config.1babdfb— Principal-based split math. Newsplit_principal_based(principal, super_frac, operator_frac)replaces the legacysplit_two_stage_commission.parse_settlementsignature changes to takesuper_config: SuperConfig+ reads directional fractions bytx_type. Closes the load-bearing bug.d9e8a04—fee_mismatch_satsPhase-1 observability. Recordsbitspire_fee_sats - (platform + operator)on every settlement. WARN-logs when|delta| > tolerance(=max(1, principal × 0.001)). Option A locked per coord-log §2026-06-01T07:00Z(lnbits advisory): always record, no gate.10f4b50— UI. Per-direction fee inputs in super-config + machine modals. Banner shows both directional super fees side-by-side. Cap (15%) hinted in input labels.Strict-from-the-start
Per workspace CLAUDE.md "Backwards-compatibility on pre-public-launch code": no compat shims. The deprecated
super_fee_fractioncolumn is dropped in m009 after backfill; the legacysplit_two_stage_commissionfunction is removed; the singleton field is gone fromSuperConfig+UpdateSuperConfigData.Test plan
test_principal_based_fees.py— 6 cases forsplit_principal_based, 5 cases forparse_settlementdirectional dispatch (cash-in routes through cash-in fractions; cash-out through cash-out; unknown tx_type raises; cross-direction guard)test_fee_cap_validation.py— 13 cases for both per-direction cap helpers (in/out independently, exact-cap acceptance via 4-decimal rounding, no-super-config degenerate, partial-update PATCH semantics, error-message naming the offending machine)test_fee_mismatch_recording.py— 8 cases (zero / positive / negative deltas, pre-Layer-3 large-delta scenario, tolerance boundary, 1-sat floor on tiny principal)m009migration verified live against the dev container's DB:super_fee_fractioncolumn dropped, directional columns +fee_mismatch_satspresent, backfill carries the live0.33singleton into both directional fieldsaiolabs/lamassu-next#57deploy gaps surfaced today (hardcodedwss://relay.aiolabs.devdefault, deadVITE_LNBITS_HTTP_URLecho, operator-fees subscriber doesn't run in maintenance state). Filed at coord-log §2026-06-01T18:30Z. Not a blocker for this PR — Layer 1 is fully autonomous from bitspire.Migration notes
super_fee_fractionsingleton into both directional fields. Machines get0.0 / 0.0operator fees and the operator sets per-direction values via the UI before activating.0.33 / 0.33backfilled values on the next super-config save. Production will land at0.03 / 0.03(well clear of cap). Pre-Layer-1 historical settlements were paid 13× below intent; operational reconcile is a Padreug-owned item per #37 sequencing.Out of scope (follow-ups)
aiolabs/lamassu-next#57) bitspire consumer + the three deploy gaps from §18:30Zenforce_fee_matchsettlement-reject (after observability data justifies the tighter posture)🤖 Generated with Claude Code
Surfaces the new directional fee fields in the admin dashboard so operators + the LNbits super can configure cash-in and cash-out fees independently: Templates (`templates/satmachineadmin/index.html`): - Platform fee banner now shows both directional super fractions side-by-side ("cash-in X% · cash-out Y% of each transaction's principal"). Wording updated to "principal" not "commission" since the math is now principal-based. - Super-fee edit dialog: replaces the single q-input with two (super_cash_in_fee_fraction + super_cash_out_fee_fraction); each capped at 0.15 via max attr (visual hint; server enforces). - Add-machine + edit-machine dialogs both gain operator_cash_in_fee_ fraction + operator_cash_out_fee_fraction inputs with the same 0.15 cap hint. Hint text mentions the "sits on top of platform fee, total capped at 15% per direction" semantics so operators understand the layering. JS (`static/js/index.js`): - superFeeDialog.data shape switches to the new directional fields. - openSuperFeeDialog / submitSuperFee load + POST the new shape. - _emptyMachineForm / _cleanMachineForm pass through operator directional fields (Number-coerced, default 0). - openEditMachineDialog / submitEditMachine include the operator fee fields in the form data + PUT body. - New computed `superAnyFee` drives the banner styling (sum of both directional fractions — non-zero → blue active banner; zero → muted grey "free instance" banner). All Quasar UMD components use explicit close tags per the UMD-mode parsing rule. Migration carry-over verified in dev container: pre-m009 super_fee_fraction=0.33 backfilled to super_cash_in=0.33 + super_cash_out=0.33 on migrate-up. Note this puts existing dev instances above the new 15% cap; operators will see the cap validation error on their next super-config save and must adjust to ≤0.15 per direction. Production aiolabs/server-deploy will land at 0.03 on both directions (well under cap). 164/164 tests green. Layer 1 (#38) complete; Layer 2 (#39) wire- format publisher is the next milestone. Refs: aiolabs/satmachineadmin#37 (parent), #38 (closes Layer 1). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>