Skip to content

feat: add platform fee model with treasury routing on report_revenue#493

Open
BigNathan1 wants to merge 2 commits into
RevoraOrg:masterfrom
BigNathan1:feat/platform-fee-model
Open

feat: add platform fee model with treasury routing on report_revenue#493
BigNathan1 wants to merge 2 commits into
RevoraOrg:masterfrom
BigNathan1:feat/platform-fee-model

Conversation

@BigNathan1

Copy link
Copy Markdown

feat: add platform fee model with treasury routing on report_revenue

closes #468

Summary

Adds a configurable, per-offering platform fee so the platform can take a
programmable share of each reported revenue amount, routed to a treasury
address and surfaced transparently in events. Previously report_revenue
allocated the full basis-point budget to holders.

Changes

  • New PlatformFeeModel { fee_bps, treasury } contracttype and
    DataKey2::OfferingPlatformFee(OfferingId) storage key.
  • set_offering_platform_fee(issuer, namespace, token, fee_bps, treasury):
    admin-only setter. Validates the offering exists and enforces the offering-level
    invariant that fee_bps + aggregate holder share <= 10_000, rejecting
    violations with the new RevoraError::FeeExceedsHolderShare. Emits a
    pfee_set config event.
  • get_offering_platform_fee(...) read-only accessor returning the model.
  • report_revenue deducts the configured fee on each recorded report and emits
    a plat_fee event carrying (treasury, fee_bps, fee_amount, period_id).

Security and correctness

  • Fee and holder allocations share the same 10_000 bps budget; the sum is
    enforced at configuration time so the platform and holders can never claim
    more than 100% of reported revenue.
  • The fee is applied only on the paths where a report is actually recorded
    (initial and override). Below-threshold and rejected-duplicate reports return
    early and take no fee.
  • fee_bps = 0, zero-revenue reports, and sub-one-unit fees skip the deduction
    and emit no plat_fee event, so indexers can rely on the event being present
    only when a real, non-zero fee was routed.
  • Fee math uses checked/saturating arithmetic (amount * fee_bps / 10_000,
    truncating down).
  • The new storage key was added to DataKey2 to avoid the primary DataKey
    union's variant cap, and the setter uses a distinct name so it does not
    collide with the existing global platform-fee API.

Tests

New module src/test_platform_fee_model.rs covering: model storage and getter,
config event shape, admin/offering/initialization guards, the
fee+holder-share invariant at and over the 10_000 boundary, full-fee-with-no-
holders, correct fee amount and round-down behavior on report_revenue, the
override path, and the no-event cases (fee_bps=0, no model, zero amount, sub-unit
fee, rejected duplicate, below threshold).

@drips-wave

drips-wave Bot commented Jun 28, 2026

Copy link
Copy Markdown

@BigNathan1 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

Define the missing PauseState enum, four DataKey2 variants
(MinRevenueThreshold, DepositedRevenue, InvestmentConstraints, SupplyCap),
and the StaleConcentrationData error; resolve the duplicate error
discriminant 48; scope DataKey to the crate so it clears the 50-case
spec-union limit. Format the tree, satisfy clippy -D warnings, register
the kani cfg, and fix pre-existing broken tests so the suite compiles
and passes (99 passed, 0 failed).
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.

Add platform fee-model hook with configurable fee_bps routed to a treasury address

1 participant