Rewards
The fee meter and unlock
What counts toward the meter, the daily ceiling, how a counted fee becomes unlocked THD, and how to re-derive the rate.
THD unlocks against protocol fees, and only against fees ProtocolFeeSplitter counts. The count is one monotonic number, totalProtocolFeesReceived, with no setter. THDEmissionControl reads it live; nothing is pushed to it.
Status#
| Contract | Status |
|---|---|
ProtocolFeeSplitter | Deployed |
THDEmissionControl — reads that splitter as an immutable | Deployed |
FeeConverter | Deployed |
| THD’s pool | Not seededTHD has not launched |
What counts#
A receipt moves the meter only when its sender is a registered and armed payer. Two keys are needed to add one, and neither can do it alone.
| Sender | Set by | Receipt |
|---|---|---|
| Unregistered | — | Paid wholly to the treasury. Counted nowhere; recorded in totalUncounted. |
| Registered, disarmed | Owner Safe, setPayer — always leaves the payer disarmed | Refused (PayerIsDisabled). The fee waits upstream in the payer. |
| Registered, armed | Guardian Safe, enablePayer | Counted, up to the day’s ceiling, then split. |
| Test, in order | Rule |
|---|---|
| Who chooses the amount? | If whoever pays can size it at will, it never meters, whatever its destination. |
| Which treasury is it destined for? | The protocol treasury makes it a protocol fee. Any other treasury TreasuryRegistry lists is paid directly and never meters. |
| Which contract pays it? | Exactly one registered, armed payer per revenue line. |
| What does that payer accept, and from whom? | A payer the public can pay through is never armed: it would let anyone buy THD unlock. |
| Revenue | Counted as |
|---|---|
| Curve and graduated-pool trade fees, protocol share, and the launch tax | ETH as received; an ERC-20 as the ETH FeeConverter realised |
| A lapsed KOL window’s platform half | The remainder after the launch’s referrer is paid their rate, through ReferralLedger |
| Launch-creation fee, protocol leg | ETH as received; a raise currency as the ETH it realised |
| Market-creation fee and surge, protocol legs; market settlement cut | ETH as received; an ERC-20 as the ETH it realised |
| Internal coup fee | ETH as received |
| Settlement-currency listing fee | The ETH it realised |
| Step | Rule |
|---|---|
| Receipt | receiveFee accepts only from a registered fee source and credits the measured balance change to attributedOf[token]. A token sent any other way is never converted. |
| Sale | convert is permissionless. It sells along a listed route to native ETH and refuses any sale that realises less than the posted-price value less maxDeviationBps (bounded on chain to 10–1,000 bps). A posted price older than MAX_PRICE_AGE, one hour, defers the sale. |
| Payment | The ETH goes to ProtocolFeeSplitter as an ordinary payer. The converter refuses to convert (SplitterWouldNotCountPayment) if the splitter would not count it. |
| Conversion switched off | The token goes to the protocol treasury unconverted and uncounted. |
ProtocolFeeSplitter itself holds no ERC-20 logic: only native ETH reaches it.
What never meters#
| Revenue | Paid to | Why it does not meter |
|---|---|---|
| Promoted slots, featured placements, shop-opening fees | Marketing treasury, direct | Sold visibility, not a protocol fee |
| Sponsored-drop fee, both legs | Marketing treasury, direct | The sponsor sizes the drop |
| Premium bought in ETH or a listed currency | Marketing treasury, direct | A product, and the buyer decides to buy |
| Premium bought in THD | Burned | THD never unlocks THD |
| THD/ETH pool LP fees, ETH leg | THD pool treasury, direct | Earned by trading THD |
| Creator milestone sales, platform 25% | Protocol treasury, direct | The creator sets the price and pack size |
MarketSeeder’s recovered opening stakes | Protocol treasury, direct | Strangers’ stakes decide the size |
| Auto-payout fee | Management treasury, direct | Sized by the user’s own earnings |
| A launch fee paid in THD | 90% burned, 10% to the Fortune Wheel | THD never unlocks THD |
| ETH sent to the splitter by anyone else | Protocol treasury | Not a registered payer |
The daily ceiling#
| Item | Value |
|---|---|
maxCountedPerEpoch | Constructor immutable, from money.protocolFeeMaxCountedPerEpochWei — 2 ETH on production |
EPOCH_SECONDS | 1 day; epoch = timestamp / 86,400, a fixed UTC day |
| Above the day’s headroom | Paid out in full; the excess is never counted and is added to totalRefusedByCeiling, with CeilingRefusedCount emitted |
| Carry to a later day | None — no call recovers a refused count |
| Reads | countedThisEpochNow(), countedRemainingThisEpoch(), currentEpochEndsAt() |
Where a counted receipt goes#
Every armed receipt is paid out in full, whatever the ceiling counted. The split applies to the whole receipt; donations are never split.
// ProtocolFeeSplitter.receive(), armed payer
toLiquidity = amount * share / 10_000 // while a manager is set, until its target is met, never after finalise
toTreasury = amount - toLiquidity
toSeason = (part of toTreasury above this month's opex threshold) * seasonShareBps / 10_000
toTreasury -= toSeason| Leg | Rule | Production |
|---|---|---|
| Liquidity | liquidityShareBps (or the payer’s own share) of the receipt to ThdLiquidityManager, capped at its remaining targetEthWei. Cap on every share: MAX_LIQUIDITY_SHARE_BPS = 5,000. A share may not be lowered while the manager is unseeded. | 5,000 bps · target 75 ETH |
| Season | seasonShareBps of the treasury leg above the month’s opexThresholdWei, measured on treasuryIntakeInMonth per UTC calendar month and split at the exact wei. Paid to seasonPool; inert while it is unset. Cap: MAX_SEASON_SHARE_BPS = 5,000. A change applies from the next month. | 2,500 bps above 25 ETH a month |
| Treasury | The rest, to treasury, re-checked against TreasuryRegistry on every payment (TreasuryNoLongerListed). | — |
From counted fees to unlocked THD#
// THDEmissionControl.totalUnlocked()
effective = min( feeSplitter.totalProtocolFeesReceived(), FEES_TO_FULL_EMISSION );
unlocked = effective * TOTAL_SUPPLY / FEES_TO_FULL_EMISSION; // TOTAL_SUPPLY = 1,000,000,000 THD| Parameter | Source | Production |
|---|---|---|
TOTAL_SUPPLY | Constant | 1,000,000,000 THD |
FEES_TO_FULL_EMISSION | Constructor immutable, from money.feesToFullEmissionWei. No setter. | 150 ETH of counted fees |
| Shortest possible emission | FEES_TO_FULL_EMISSION / maxCountedPerEpoch | 75 days |
| Function | Returns |
|---|---|
totalUnlocked() | THD unlocked so far |
remainingToUnlock() | TOTAL_SUPPLY − totalUnlocked() |
feesToCompletion() | Counted wei still needed, rounded up |
isComplete() | totalUnlocked() >= TOTAL_SUPPLY |
claimableOf(bucket) | A bucket’s accrued share less what claim(bucket) already paid its beneficiary |
Each unlock is split across the eleven allocations at their fixed shares — see the eleven allocations. claim(bucket) is permissionless and pays only the bucket’s beneficiary.
Re-deriving the rate#
The THD-per-ETH rate of the unlock is derived from two live reads on THDEmissionControl, never transcribed:
rate (THD per counted ETH) = TOTAL_SUPPLY() / FEES_TO_FULL_EMISSION()
cast call <THDEmissionControl> "TOTAL_SUPPLY()(uint256)" --rpc-url <rpc>
cast call <THDEmissionControl> "FEES_TO_FULL_EMISSION()(uint256)" --rpc-url <rpc>The quotient is a repeating decimal, so any written figure is rounded. A screen may round it for display; the exact value is always these two reads. FEES_TO_FULL_EMISSION is per deployment, so a testnet rate is a rate against a different denominator. The address is on contract addresses; the meter’s published metrics are on THD and the fee meter.