The Fee Engine: Where the Money Flows

Part 3: How 1% of every trade gets split, why creator earnings work across two lifecycle stages, and the defensive patterns that keep fee distribution from breaking trades.

Cover Image for The Fee Engine: Where the Money Flows

Part 3 of "Building on Polkadot Hub: A Builder's Journal"


TL;DR

plaza.fun charges a 1% fee on every bonding curve trade. 25% goes to the creator. The rest goes to the platform. After graduation, FeeKeyNFT holders can harvest yield from the 10% locked LP position, while PlazaSwap protocol fees route separately to treasury. Two mechanisms, two lifecycle stages, one goal: make creators earn from the first trade through post-graduation activity.


Two Stages, Two Fee Mechanisms

The most common question we get: "How do creators earn?" The answer depends on where the token is in its lifecycle.

┌─────────────────────────────────────────────────────────────┐
│  BEFORE GRADUATION — Bonding Curve                          │
│                                                             │
│  Trade (e.g. 100 DOT)                                       │
│    └─ 1% Fee (1 DOT)                                        │
│         ├─ 25% → Creator (0.25 DOT)  → claimCreatorFees()   │
│         └─ 75% → Platform (0.75 DOT) → feeRecipient         │
│                                                             │
├─────────────────────── graduation ──────────────────────────┤
│                                                             │
│  AFTER GRADUATION — PlazaSwap DEX                           │
│                                                             │
│  Trade                                                      │
│    └─ LP fees grow pair value                               │
│         ├─ 10% locked LP harvest → FeeKeyNFT holder         │
│         └─ Factory.feeTo protocol fee → treasury collector  │
└─────────────────────────────────────────────────────────────┘

Before graduation (bonding curve stage): Every buy and sell on the bonding curve incurs a 1% fee. The contract splits it immediately:

  • 75% → Platform (feeRecipient)
  • 25% → Creator (accumulated in the contract, claimable anytime)

The creator's share accumulates on-chain in creatorFees[tokenId]. They call claimCreatorFees() whenever they want. No vesting, no lockup, no permission needed.

After graduation (DEX stage): The token trades on PlazaSwap with a 0.3% LP fee (standard Uniswap V2 mechanics). The creator's FeeKeyNFT entitles them to a share of the LP fee growth on their locked 10% LP position. This is a different mechanism — covered in detail in Part 4.

Where the Parameters Live

One thing we got wrong early: we put creation fees and minimum trade amounts in FeeConfig. That made sense when DOT was the only quote token. Then we added multi-quote-token support.

Creation fee (6.28 DOT) and minimum trade (0.1 DOT) are now per-quote-token configurations in QuoteTokenRegistry. Different quote tokens can have different fee thresholds. FeeConfig handles the trading fee rate and distribution logic — things that are the same regardless of which token you're trading against.

The split:

ParameterLives inWhy
tradingFeeBps (100 = 1%)FeeConfigSame rate for all tokens
creatorShareBps (2500 = 25%)FeeConfigSame share for all tokens
feeRecipientFeeConfigOne platform wallet
creationFee (6.28 DOT)QuoteTokenRegistryPer quote token
minTrade (0.1 DOT)QuoteTokenRegistryPer quote token

Fee Distribution: The Defensive Pattern

Distributing fees sounds simple until you realize any recipient address can revert your transaction.

Imagine a creator sets their address to a contract that reverts on receive. Without protection, every trade involving their token would fail — the fee distribution would revert, which would revert the trade, which would effectively freeze the token.

We use a push-pull pattern:

For each fee recipient:
  try: transfer fee directly (push)
  catch: queue fee for manual withdrawal (pull)

The contract first attempts a direct native transfer. If that fails (reverts, out of gas, contract with no receive function), it falls back to crediting the amount in pendingFeeWithdrawals[recipient][quoteToken]. The recipient can call withdrawPendingFees() later.

This means no external address — malicious or misconfigured — can block trading. The worst case is the fee recipient has to manually claim instead of receiving automatically.

Fee Economics at Scale

Let's do the math for a single token that reaches graduation.

The bonding curve fills at 6,280 DOT in accumulated quote volume. With a 1% fee on every trade, that means:

  • Total fees during bonding curve stage: ~62.8 DOT per graduated token
  • Creator earns: ~15.7 DOT (25%)
  • Platform earns: ~47.1 DOT (75%)

That's from one token. Once live, the platform is self-sustaining — no ongoing venture subsidy needed, no token sale required. Revenue starts from the first graduation.

After graduation, PlazaSwap LP fees can create harvestable growth in the 10% locked LP position. Separately, the treasury fee collector — an immutable, non-upgradeable contract — collects the protocol's Factory.feeTo LP, swaps accumulated meme tokens to WDOT, unwraps to native DOT, and transfers the proceeds to treasury. All on-chain, all verifiable.

What We Learned

Separate configuration concerns early. We had to migrate creationFee and minTrade out of FeeConfig into QuoteTokenRegistry once we added multi-token support. The UUPS storage layout makes this kind of refactor painful — you can't just rename or remove storage slots. We had to deprecate them with __gap_deprecated_* placeholders to preserve the layout. Get the separation right from the start.

Immutable where possible. The treasury fee collector has no upgrade path, no treasury setter, no arbitrary call, and no rescueToken function. Funds flow from PlazaSwap pairs to the configured treasury and nowhere else. We could have made it upgradeable "just in case," but every upgrade path is an attack surface. For a contract that handles fee collection, immutability is the feature.

The push-pull pattern is essential, not optional. We almost shipped with direct-transfer-only fee distribution. In testing, we found that any reverting recipient would freeze all trading for that token. The fallback queue adds complexity but prevents a trivial griefing attack.


Next: Part 4 — The Token Lifecycle — graduation, 90% LP burn, PlazaSwap, and how FeeKeyNFT turns LP fees into permanent creator earnings.

Previous: Part 2 — The Bonding Curve

Follow @plaza_fun for updates. · Discord