Skip to content

Optional protocol fee module with governed rate and conservation-preserving accounting #354

Description

@grantfox-oss

Difficulty: Expert. Scope: multi-week epic. Contract + SDK + dashboard. Product-direction decision required.

Problem

There is no way for the protocol, or a split operator, to take a fee. amounts() divides the full payment among recipients with zero skim, and there is no fee recipient or fee state anywhere. A marketplace that wants a platform cut, or the protocol itself wanting sustainable revenue, has to build fee logic outside Tributary and defeat the "one transaction, everyone paid" model. This is a common, expected primitive for a payments rail, and it is entirely absent.

This one carries a product-direction decision: whether Tributary should have fees at all, at the protocol level, the split level, or both. The first deliverable is that decision, not code.

Proposal (to be validated by RFC)

An optional fee, off by default and backward compatible:

  • A governed fee rate (basis points) and a fee recipient.
  • On pay/distribute, skim the fee before splitting the remainder among recipients, emitting the fee as its own event leg.
  • Governance over the rate and recipient (should reuse the upgrade-authority / threshold-controller model, not a lone key), with a sane maximum cap enforced on-chain so governance cannot set an abusive rate.

Why this is hard

  • Conservation changes: the invariant becomes amount_in == fee + sum(recipient_amounts). All the dust and rounding rules must be re-derived so the fee plus the splits still reconstruct the input exactly, and the Kani/fuzzing invariants must be updated to match.
  • Interaction surface: fees must compose correctly with nested splits (is the fee taken once at the top or at every level?), with escrow deposit/distribute, and with pay_many.
  • Governance and safety: on-chain rate cap, authority model, and making the fee genuinely optional (a zero rate must cost nothing and behave exactly as today).
  • Transparency: payers and recipients must be able to see the fee before and after, in preview_payout and in events.

Deliverables

  • Product RFC and a maintainer decision on whether/where fees apply.
  • If approved: fee state, governed setters with an on-chain cap, fee-aware amounts/payout, event legs, and preview_payout that shows the fee.
  • SDK and dashboard surfacing of the fee.
  • Tests including conservation-with-fee, zero-rate equivalence to today, and nested-split behavior.

Acceptance criteria

  1. A maintainer decision on scope (no fee / protocol / per-split / both) is recorded before any implementation. If declined, the issue closes as "won't do" with the rationale captured, which is itself a valid outcome.
  2. If implemented: amount_in == fee + sum(recipient_amounts) holds exactly, with updated invariant tests.
  3. Zero fee rate is behaviorally identical to today and costs no extra resources.
  4. The rate is governed with an enforced on-chain maximum; an abusive rate is impossible.
  5. preview_payout and events expose the fee transparently.

Related

Depends on the governance/authority model from the upgradeability and threshold-controller epics. Must update the conservation invariants proven in the Kani and differential-fuzzing epics.

Metadata

Metadata

Assignees

Labels

GrantFox OSSIssue tracked in GrantFox OSSMaybe RewardedIssue may be eligible for a GrantFox rewardOfficial Campaign | FWC26Campaign: Official Campaign | FWC26enhancementNew feature or requesthelp wantedExtra attention is neededrustPull requests that update rust code

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions