Rewards
The battlepass
The season pass and the daily THD pot: points, premium and where its payment goes, and claiming.
Status#
The battlepass and the platform shop are the platform’s two THD sinks: the only places where a user spends THD and the supply falls. Everything else in the reward layer pays THD out. The battlepass is also the largest destination of the Spoils user pool — 5,000 bps of it at construction. See Spoils.
A season is a calendar quarter#
A season is a fixed calendar quarter beginning 1 January, 1 April, 1 July and 1 October, UTC. There is no season-duration constant and nothing an owner can move: the index is year × 4 + (quarter − 1), derived from block.timestamp by the same civil-date routine ProtocolFeeSplitter uses.
| Constant or view | Value |
|---|---|
MONTHS_PER_SEASON | 3 |
seasonDays(index) | 90, 91 or 92 — measured, not assumed. A fixed 90 would over-charge every buyer in a 92-day quarter by two days’ worth. |
BACK_CLAIM_WINDOW | 30 days — 2,592,000 seconds, after seasonEnd |
claimDeadline(index) | seasonEnd(index) + 30 days |
The two points boards: Spending and Earnings#
// lib/points/formula.ts — dayPoints, once per board
a board's day points = the board's measure // Spending, or Earnings
× (checked in that day ? 1 : 0)
× streak multiplier // 1 + 0.5 × min(1, (days ÷ 20)^0.7)
× premium multiplier // 1.25 while a pass is held, else 1| Input | What counts |
|---|---|
| Spending | Net bonding-curve buying (below) and prediction-market stakes in full |
| Earnings | The creator’s trade-fee share, the creator’s half of a KOL window that closed unallocated, founding-crew credits (and the creator’s remainder of a crew sync), KOL allocations and referrer credits — in ETH or in the launch’s ERC-20 |
| Not earnings | Trading profit, LP income, prediction winnings, settler THD and wheel prizes. Markets pay no fee split to a user |
| Unpriced | An amount with no posted price in force at its event is left out and counted, never taken at face value |
| Check-in | A day with no check-in scores zero on both boards, whatever moved |
| Streak | × 1 to × 1.5, flat from day 20, covered days included |
| Premium | × 1.25 while a pass or a trial is held |
| Rule | As built |
|---|---|
| Daily rank | Points, descending. Equal points share a rank — 1, 2, 2, 4 — and the season points and pot share that go with it. Time decides nothing; tied rows are listed by address |
| Season score | Σ max(0, 101 − rank) per board. Every shared rank of 100 or better scores, so ties at the cut can put more than 100 wallets in the money |
| Pot between the boards | Split daily by fee value: the fees the day’s counted spending paid, against the fee income paid to the day’s earners. A board with nobody on it that day passes its share to the other |
| Pot inside a board | Pro rata by points |
| Frozen days | Each closed UTC day is frozen once, with every event it read, and served at /api/v1/points/{spending|earnings} |
| Figure | Value |
|---|---|
| Curve volume, buys and sells | 10 ETH |
| Trade fee, 200 bps — Spending’s fee value | 0.2 ETH |
| Creator 3,000 bps + KOLs 4,000 bps + referrer 1,000 bps of the 3,000 bps ledger leg — Earnings’ fee value | 0.06 + 0.08 + 0.006 = 0.146 ETH |
| Split | 57.8% Spending · 42.2% Earnings |
| Trade | Counts as spending |
|---|---|
| Buy 1 ETH | +1 |
| Sell for 1 ETH | +1 |
| Buy 1 ETH, sell for 1 ETH | +2 |
| Redeem from a curve that did not graduate | 0 |
| Prediction stake 1 ETH | +1 |
// lib/points/curve-volume.ts
spending = Σ ETH paid on curve buys + Σ ETH received on curve sells + Σ prediction stakes
// at event-time ETH value; graduated-pool trades never countPremium, and where the payment goes#
| Property | Value |
|---|---|
| Currency | Before TGE: ETH or any currency CurrencyRegistry lists, at the pass’s dollar price. After TGE: THD by default (burned, 1×), any other listed currency at 2×. Currency payments go to the marketing treasury and never meter. |
| Price | $4.99 a month, always pro-rated; one purchase covers the rest of the season (owner ruling 15-10-2026). The monthly rate is PricingRules’ PREMIUM_PASS price in THD, read by the monthlyPrice of quotePremium and settable by the owner Safe; a change reaches future purchases only. The opening figure is $4.99 converted once at deploy through the TGE price (targetEthWei ÷ targetThd on the liquidity manager) and the posted ETH/USD — about 1,486 THD at ETH ≈ $2,686. |
| Calibration key | money.premiumMonthlyPriceUsd — $4.99 on production, scaled on dev and staging together with the TGE ETH target, so the THD figure is the same everywhere. |
| Scope | One pass per account per season; it ends when the season does, 00:00 UTC on 1 January, April, July or October. Days premium you already hold (a trial) covers are credited, and a season already covered to its end is refused (PremiumAlreadyCovered). Contracts deployed before 15-10-2026 sell one calendar month instead. buyPremiumAtMost buys for msg.sender and nobody else — gifting is refused by design. |
| Currency price | PricingRules PREMIUM_PASS for native ETH in USD ($4.99), converted at the posted price; a listed ERC-20 inherits it at its own posted feed. Sold by PremiumDesk.buyPremiumAtMost(currency, maxPrice), recorded on Battlepass. |
| What it grants | A 1.25× points multiplier, applied off chain, and the premium discount in the shop. Nothing else. |
The price is pro-rated by day within each month of the season, over the days not already covered, and each month’s term rounds down, in the buyer’s favour:
// PremiumSeason.quote (Battlepass.quotePremium, PremiumDesk.quotePremium)
payFrom = today + daysCredited // daysCredited: whole days your current premium covers
price = sum over the season's 3 months m of
monthlyPrice * payableDays(m) * (10_000 - discountBps)
/ (daysIn(m) * 10_000)
// payableDays(m) = days of m from payFrom to the season's end; a full season = 3 x the rateWaiting therefore does not save money, it buys less. The one approved milestone discount is 5,000 bps — 50% off the season’s pass, at day 30, proved against a second Merkle root published per season, spent once a season, and reserving nothing.
Paid in THD, premium is deflation, not revenue. Paid in ETH or another listed currency it goes to the marketing treasury, to fund user acquisition. Neither ever reaches totalProtocolFeesReceived and neither unlocks THD. Trading contributes fees, which the meter counts; buying premium contributes burn, which the meter never sees. The two are not interchangeable and must never be added together. See THD and the fee meter.
Claiming, and the 25% late penalty#
Rewards are committed as one cumulative Merkle root per season, republishable as often as daily. Per-account progress is a running total in claimedOf[seasonIndex][account], and a claim pays the delta — which is what makes the root republishable without a per-claim bitmap. The leaf is keccak256(abi.encode(seasonIndex, account, cumulative)).
| When | Paid | Burned |
|---|---|---|
| Before the season ends | The whole delta | Nothing |
| Within the 30-day back-claim window | 75% | 25% — BACK_CLAIM_BURN_BPS = 2500 |
| After the deadline | Nothing — claim reverts | The whole residue — burnExpired(season), callable by anyone |
| Rule | Value | Refusal |
|---|---|---|
| THD claim gate | Passed once: the minimum earned or spent on the platform, and X connected | ClaimGateNotPassed(account, claimGate) |
| Vest | None | — |
| Unclaimed after the back-claim | Burned — totalSupply() falls | SeasonWindowClosed(season, closesAt, now) |
totalThdBurned() is the three burn counters combined — totalPremiumBurned, totalLateClaimBurned and totalExpiredBurned. A root may never allocate more than the contract holds: publishRewards checks the new outstanding against available() plus the season’s existing outstanding, so an over-allocation reverts at publication rather than at the last claimant.
The battlepass keeper publishes both roots, bounded by the pot. A reward root becomes claimable 12 hours after it is queued (ROOT_ACTIVATION_DELAY), and the owner or guardian Safe may cancel it before then with vetoPendingRewards.
THD spent in the platform shop is the other burn. Shops, their prices and where every shop receipt goes: The shop.
Three things that must never be added#
Three design rules, not implementation notes:
| Never | Why |
|---|---|
| A reward that grants points directly | Points already drive the daily share, the season standing and the band contribution. A reward denominated in points pays for the same behaviour twice. A tier jump is the same thing under another name, and was removed once already. |
| A bigger season-standing share for premium | Zero-sum — it moves shares without growing the pot — and it stacks with the 1.25× multiplier into a compounding advantage. |
| Premium-only milestones | The 3/7/14/30 ladder is the retention funnel for new users. Gating it behind payment removes the mechanic from the only cohort it exists for. |
The 1.25× multiplier is the single permitted exception to activity-weighted rewards, and it is permitted because the payment is burned and it is capped: its value to the buyer decays to zero as adoption rises, at which point the whole cohort has burned THD for nothing but the burn. It is not a precedent for a second lever.