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.
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.
| Line | Destination | Protocol fee? | Passes the meter? |
|---|---|---|---|
| Trade fee (curve and graduated pool) | Split four ways; the residual reaches the protocol treasury | Partly — the treasuryShare leg only | Yes, that leg |
| Launch fee | 90% protocol treasury, 10% Fortune Wheel jackpot (JACKPOT_SHARE_BPS). The whole fee on a factory deployed before 26-09-2026 | The 90% leg | The 90% leg |
| Market creation fee | Split four ways — two opening stakes, the wheel jackpot, and the protocol | Partly — the PROTOCOL_BPS leg only | That leg, and only when the currency is native |
| Promoted slots and featured placements | marketingTreasury, directly | No — sold visibility | No |
| THD/ETH pool LP fees | thdPoolTreasury, via ThdFeeRouter | No — trading on THD | No |
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 treasuryThe metric entries#
| id | unit | derivation | source |
|---|---|---|---|
feesTotalWei | wei | Σ Bought.tradeFee + Σ Sold.tradeFee + Σ FeesCollected.ethForwardedNow + launch fees + market fees. Cross-check against totalToCreators + totalToKols + totalToLedger. | Several; see the rows below |
feesCreatorWei | wei | Σ FeeSplit.toCreator | TradeFeeSplitter.FeeSplit |
feesKolWei | wei | Σ FeeSplit.toKol | TradeFeeSplitter.FeeSplit |
feesReferralWei | wei | Σ FeeReceived.referrerShare | ReferralLedger.FeeReceived |
feesProtocolWei | wei | Σ FeeRouted.amount where counted == true | ProtocolFeeSplitter.FeeRouted |
feesMarketingWei | wei | Σ LaunchPromoted.payment — plus MarketingFeeSwept.amount per market, kept per currency | LaunchFactory.LaunchPromoted · ParimutuelMarket.MarketingFeeSwept |
feesLaunchWei | wei | Σ launchFeeUsd_at_that_block priced in ETH at that block | Derived; see the caveat below |
feesThdPoolWei | wei | ThdLiquidityManager.totalEthToRouter | ThdLiquidityManager |
crewCreditedWei | wei | Σ MemberCredited.amount | CrewSplitter.MemberCredited |
Caveats that change the number#
| Caveat | Detail |
|---|---|
| The launch fee has no amount on any event | TokenLaunched 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 ledger | Its protocol leg goes straight to ProtocolFeeSplitter and arrives as a FeeRouted with the factory as from. |
feesProtocolWei from the meter alone under-reports | Real revenue is lifetimeFeesReceived. The shortfall is permanent. See THD and the fee meter. |
feesMarketingWei cannot be one number once markets are included | Launchpad 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 exists | It 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;| Leg | Rate | Event | Then |
|---|---|---|---|
| Protocol | feeBps, 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 |
| Marketing | marketingBps = featuredTier × 50 for a carousel slot, half that for a standard slot | ParimutuelMarket.MarketingFeeSwept(marketingTreasury, amount) | Direct. Never the meter |
| Settler | None | — | 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.
| A launch | A prediction market | |
|---|---|---|
| Charged by | LaunchFactory | PredictionMarketFactory |
| The amount | PricingRules.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 how | launchFeeWei() 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. |
| Split | Two 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. |
| Leg | Constant | bps | Where it lands |
|---|---|---|---|
| Opening stake on YES | SEED_BPS | 2500 | Into the market clone at initialize, credited to the market seeder |
| Opening stake on NO | SEED_BPS, applied again | 2500 | The same |
| Fortune Wheel jackpot | JACKPOT_BPS | 1000 | jackpotAccrued[currency], pushed later by sweepJackpot |
| Protocol | PROTOCOL_BPS | 4000 | protocolFeesAccrued → sweepProtocolFees() → ProtocolFeeSplitter, for a native fee only |
| Total | TOTAL_BPS | 10000 | The 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.