Docs·Metrics reference

Metrics reference

Fees, by leg

The five revenue lines, which are protocol fees, the event carrying the wei of every hop from a trade to the treasury, and the two flat creation fees.

Version
1.6
Updated
2026-09-28
Source
Metric definitions v1.2 · Launch and market fee sources

The five revenue lines#

A fee is a protocol fee if and only if it is destined for the protocol treasury, and every protocol fee passes ProtocolFeeSplitter on the way. Two revenue lines are deliberately not protocol fees and must never be counted as one.

Where each line goes, and whether it reaches the meter
LineDestinationProtocol fee?Passes the meter?
Trade fee (curve and graduated pool)Split four ways; the residual reaches the protocol treasuryPartly — the treasuryShare leg onlyYes, that leg
Launch fee90% protocol treasury, 10% Fortune Wheel jackpot (JACKPOT_SHARE_BPS). The whole fee on a factory deployed before 26-09-2026The 90% legThe 90% leg
Market creation feeSplit four ways — two opening stakes, the wheel jackpot, and the protocolPartly — the PROTOCOL_BPS leg onlyThat leg, and only when the currency is native
Promoted slots and featured placementsmarketingTreasury, directlyNo — sold visibilityNo
THD/ETH pool LP feesthdPoolTreasury, via ThdFeeRouterNo — trading on THDNo

The trade-fee path, end to end#

One fee, taken once on the curve, split at four hops. Every hop has an event carrying the wei of each leg, so no leg has to be inferred.

BondingCurve.tradeFeesAccrued
  └─ claimTradeFees()  →  TradeFeesClaimed(tradeFeeSplitter, amount)
       │  [after graduation the same money arrives instead as]
       └─ LaunchLiquidityLocker.FeesCollected.ethForwardedNow
            └─ TradeFeeSplitter.receive()
                 →  FeeSplit(source, kind, amount, toCreator, toKol, toLedger)
                      ├─ toCreator → CrewSplitter (or the creator's recipient directly)
                      ├─ toKol     → KolSplitter.FeeReceived.amount
                      └─ toLedger  → ReferralLedger.creditFor()
                                       →  FeeReceived(payer, source, referrer, amount,
                                                      referrerShare, treasuryShare, rateBps)
                                            ├─ referrerShare → the referrer
                                            └─ treasuryShare → claimTreasury()
                                                 →  TreasuryClaimed(treasury, amount)
                                                      → ProtocolFeeSplitter.receive()
                                                          →  FeeRouted(from, counted, amount,
                                                                       toLiquidity, toTreasury,
                                                                       meterAfter, label)

The arithmetic, verbatim from the contracts:

// TradeFeeSplitter.receive()
toCreator = amount * terms.creatorShareBps / 10_000;
toKol     = amount * terms.kolShareBps     / 10_000;
toLedger  = amount - toCreator - toKol;      // remainder by subtraction; division dust lands here

// ReferralLedger._creditFee()
rateBps       = rateBpsOf(referrer);
referrerShare = amount * rateBps / 10_000;
treasuryShare = amount - referrerShare;      // dust to the treasury

The metric entries#

Fee figures, by leg
idunitderivationsource
feesTotalWeiweiΣ Bought.tradeFee + Σ Sold.tradeFee + Σ FeesCollected.ethForwardedNow + launch fees + market fees. Cross-check against totalToCreators + totalToKols + totalToLedger.Several; see the rows below
feesCreatorWeiweiΣ FeeSplit.toCreatorTradeFeeSplitter.FeeSplit
feesKolWeiweiΣ FeeSplit.toKolTradeFeeSplitter.FeeSplit
feesReferralWeiweiΣ FeeReceived.referrerShareReferralLedger.FeeReceived
feesProtocolWeiweiΣ FeeRouted.amount where counted == trueProtocolFeeSplitter.FeeRouted
feesMarketingWeiweiΣ LaunchPromoted.payment — plus MarketingFeeSwept.amount per market, kept per currencyLaunchFactory.LaunchPromoted · ParimutuelMarket.MarketingFeeSwept
feesLaunchWeiweiΣ launchFeeUsd_at_that_block priced in ETH at that blockDerived; see the caveat below
feesThdPoolWeiweiThdLiquidityManager.totalEthToRouterThdLiquidityManager
crewCreditedWeiweiΣ MemberCredited.amountCrewSplitter.MemberCredited

Caveats that change the number#

CaveatDetail
The launch fee has no amount on any eventTokenLaunched carries no fee field — its initialBuy is already the residual after the fee and any promotion payment. The fee is the LAUNCH_CREATION price in PricingRules, priced into wei through the shared USD oracle at the moment of the launch. Since 26-09-2026 each charge is on PricingRules.PriceRedeemed, with the voucher or discount that applied. Before that it was launchFeeUsd on the factory, replayed from LaunchFeeUsdSet(uint256 usd) and priced at that block’s ETH/USD. A launch made before 21-09-2026 paid graduationTarget × launchFeeBps / 10_000 instead, replayed from LaunchFeeBpsSet(uint16 bps) — the two eras are not one series. On a factory from 28-09-2026 the fee is held by its LaunchFeeDesk, which emits LaunchFeeReceived(address indexed currency, uint256 amount, uint256 jackpotLeg) per fee. Before that the only wei-carrying event is the sweep, LaunchFeesClaimed(address indexed treasury, uint256 amount) — emitted by the desk from 28-09-2026, by the factory before — and it is a batch.
The launch fee does not pass the referral ledgerIts protocol leg goes straight to ProtocolFeeSplitter and arrives as a FeeRouted with the factory as from.
feesProtocolWei from the meter alone under-reportsReal revenue is lifetimeFeesReceived. The shortfall is permanent. See THD and the fee meter.
feesMarketingWei cannot be one number once markets are includedLaunchpad promoted slots are paid in wei at creation; a market’s featured cut is taken at settlement in that market’s own currency, and there is no factory-side accumulator for it at all. Publish the wei line and the per-currency lines separately.
toCreator is not the creator’s income when a crew existsIt arrives at the launch’s CrewSplitter, which splits again — Synced(asset, amount, toCrew, toCreator, caller) and MemberCredited(asset, member, amount, allocationBpsApplied). The creator’s own share is the residual toCreator on Synced, paid to creatorRecipient, which is not necessarily the creator.

Prediction-market fees#

The market fee is not taken per stake. It is one cut of the whole pot at settlement:

// ParimutuelMarket._feeAmount()
if (voided || totalYes == 0 || totalNo == 0) return 0;    // refund mode: zero fee, both legs
return (totalYes + totalNo) * feeBps / 10_000;
The two legs a settlement pays, and the one it does not
LegRateEventThen
ProtocolfeeBps, snapshotted at initialize from the settlement registry, capped at MAX_FEE_BPS = 500 (200 on production)ParimutuelMarket.FeeSwept(feeRecipient, amount)feeRecipient is PredictionMarketFactory itself, which books ProtocolFeeReceived, then sweepProtocolFees() → ProtocolFeesSwept → the meter
MarketingmarketingBps = featuredTier × 50 for a carousel slot, half that for a standard slotParimutuelMarket.MarketingFeeSwept(marketingTreasury, amount)Direct. Never the meter
SettlerNone—Settlement is permissionless and unpaid

One figure this makes impossible#

There is no single marketing-revenue number. One side is wei paid up front at launch; the other is a settlement-time share of a market’s own currency with no accumulator anywhere. Any one figure covering both would silently add currencies — see figures we do not publish.

Creation fees — flat, and split four ways#

Creating something costs a flat dollar, not a share of what it raises. A hundred-ETH launch and a two-ETH launch pay the same. The two creation fees are charged by different contracts, in different currencies, and they are split differently — they are not one series and must not be summed.

The two creation fees
A launchA prediction market
Charged byLaunchFactoryPredictionMarketFactory
The amountPricingRules.basePriceOf(LAUNCH_CREATION, ETH) — $1 at 18 decimals on production. Capped at $10 by MAX_LAUNCH_CREATION_USD. Owner-settable, not timelocked; a discount or voucher lowers one charge.PricingRules.basePriceOf(MARKET_CREATION, currency) — a USD figure at 18 decimals, per currency; setBasePrice refuses any other kind for this product. SettlementRegistry.creationFeeOf before 26-09-2026.
Priced howlaunchFeeWei() converts through UsdPriceOracle.weiForUsd at the moment of the launch, rounding down. It takes no graduation target.PricingRules.basePriceFor at the moment of the creation, rounding down: native ETH through UsdPriceOracle.weiForUsd, an ERC-20 through its own feed (PricingRules.currencyUsdFeedOf). A missing or stale feed reverts CurrencyUsdPriceUnavailable. Only the native protocol leg meters.
SplitTwo legs of the fee actually paid: 90% to launchFeesAccrued, swept by claimLaunchFees() to ProtocolFeeSplitter; 10% to jackpotAccrued, pushed by sweepJackpot() to the Fortune Wheel. Held and swept by the factory’s LaunchFeeDesk from 28-09-2026, by the factory itself before. A voucher makes both zero. Before 26-09-2026: none — the whole fee to ProtocolFeeSplitter.Four ways — below.
CreationFeeSplit — the market creation fee's four legs
LegConstantbpsWhere it lands
Opening stake on YESSEED_BPS2500Into the market clone at initialize, credited to the market seeder
Opening stake on NOSEED_BPS, applied again2500The same
Fortune Wheel jackpotJACKPOT_BPS1000jackpotAccrued[currency], pushed later by sweepJackpot
ProtocolPROTOCOL_BPS4000protocolFeesAccrued → sweepProtocolFees() → ProtocolFeeSplitter, for a native fee only
TotalTOTAL_BPS10000The denominator, exactly

Each leg is an independent floor division and the residual is an explicit dust — at most three base units — which is never re-attributed to a leg and stays with the factory. The whole split is in one log:

MarketCreationFeePaid(marketId, currency, payer,
                      fee, openingStakePerSide, jackpot, protocol, dust)

// and in every one of them, exactly:
openingStakePerSide * 2 + jackpot + protocol + dust == fee + seedTopUp

// seedTopUp is non-zero only on a discounted or voucher creation (MarketSeedToppedUp):
// the seeds are sized by the full price, the other legs by what was paid.

The jackpot leg comes out of the platform’s half rather than off the top, which is why the two seed legs are untouched by it. If no price can be obtained at all, a launch reverts — with UsdPriceOracleUnset when no oracle is configured, or with the oracle’s own EthUsdPriceUnavailable(cachedFeed, cachedUpdatedAt, cachedMaxAge, liveFeed, liveUpdatedAt, liveMaxAge, nowTime) when neither the cached feed nor the live aggregator can answer. Setting the LAUNCH_CREATION price to zero in PricingRules short-circuits the oracle entirely and is the documented outage hatch.