Skip to content

On-Chain Fee Registry, Creator Revenue Share & Community Voting #681

Description

@grantfox-oss

Overview

CrowdPay's platform fee is currently hardcoded. As the platform grows, the community of creators and contributors should have a voice in fee changes, and the fee structure itself should be transparent and verifiable on-chain. This issue implements three things: a Soroban fee registry contract that stores the platform fee percentage on-chain, a revenue share system that distributes a percentage of platform fees back to high-volume creators, and a lightweight on-chain governance mechanism where CrowdPay token holders can propose and vote on fee changes.

What needs to be built

contracts/soroban/fee_registry/ — Soroban contract (Rust)

  • Contract storage: platform_fee_bps: u32, creator_share_bps: u32, governance_token: Address, admin: Address, pending_proposal: Option<FeeProposal>

  • FeeProposal: { id: u32, proposed_fee_bps: u32, proposed_creator_share_bps: u32, votes_for: i128, votes_against: i128, deadline: u64, status: ProposalStatus }

  • Functions:

    • get_fee() -> u32 — returns platform_fee_bps — called by the CrowdPay backend before every contribution to get the current fee
    • propose_change(new_fee_bps, new_creator_share_bps) — callable by any governance token holder with balance > 1,000 tokens; creates a FeeProposal with deadline = now + 7 days
    • vote(proposal_id, in_favor: bool) — callable by any token holder; vote weight = token balance at the time of the vote (snapshot via token contract balance())
    • execute_proposal(proposal_id) — callable by anyone after deadline; if votes_for > votes_against AND votes_for > 10% of total token supply: updates platform_fee_bps and creator_share_bps; else: marks as failed
    • admin_set_fee(fee_bps, creator_share_bps) — callable by admin only; bypasses governance (emergency use)
      backend/src/services/feeRegistry.js — Backend integration
  • On startup and every 5 minutes: call get_fee() on the contract; cache in Redis

  • Contribution fee calculation: use cached platform_fee_bps from the contract — not a hardcoded value

  • Creator revenue share: at campaign withdrawal time, compute creator_share = collected_fees_for_this_campaign * (creator_share_bps / 10000); include an additional Payment operation in the withdrawal transaction sending creator_share from the platform fee wallet to the creator's wallet
    backend/src/services/governance.js — Governance API

  • GET /api/governance/proposals — list all proposals (active and historical) from contract state via getLedgerEntries

  • GET /api/governance/proposals/:id — single proposal detail: votes for/against, deadline, token holder participation rate, current outcome projection

  • POST /api/governance/proposals/:id/vote — user votes:

    • Validates user holds governance tokens in their connected Stellar wallet (checked via Horizon account balances)
    • Calls vote(proposal_id, in_favor) on the contract — user must sign the transaction via Freighter
  • GET /api/governance/fee — returns current fee from Redis cache + the on-chain contract ID for verification
    Frontend

  • /governance page:

    • Current fee display: Platform fee: X bps (Y%) — with a link to view the contract on Stellar Expert
    • Creator revenue share: Creators receive Z% of platform fees collected on their campaigns
    • Active proposal (if any): vote count, deadline countdown, "Vote For" / "Vote Against" buttons (requires Freighter connection)
    • Proposal history: table of past proposals with outcome
  • "Create Proposal" form (gated by 1,000 token balance check): proposed fee bps, proposed creator share bps, rationale text (stored off-chain in proposals table)
    Database migrations

  • governance_proposals_meta: id, stellar_proposal_id, proposer, rationale_text, created_at

  • governance_votes_log: id, proposal_id, voter_public_key, in_favor, token_balance_at_vote, voted_at

Acceptance criteria

  • get_fee() on the contract returns the correct platform_fee_bps — contribution fee calculation matches the contract value exactly
  • A proposal that receives more votes_for than votes_against AND exceeds 10% participation threshold is executed — platform_fee_bps updates on the contract after execute_proposal is called
  • A proposal that fails the 10% participation threshold is marked failed even if votes_for > votes_against — governance quorum is enforced
  • Creator revenue share payment is included in the withdrawal transaction — confirmed by inspecting the XDR operation list
  • propose_change correctly rejects a caller with fewer than 1,000 governance tokens — returns a Soroban auth error
  • Backend fee cache correctly refreshes within 5 minutes of an admin_set_fee call on the contract — no stale fee used for contributions

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions