Docs·Rewards and mechanics

Rewards and mechanics

Spoils

A thousand fixed days, a benchmark that is the fee meter's own ceiling, and why a quiet day destroys supply instead of paying it out.

Version
1.0
Updated
2026-09-21
Source
THD contract sources · THD — Full Plan v1.43

Status#

Spoils is the eleventh of the eleven THD allocations — 1,100 bps, 110,000,000 THD. It is the only one whose release schedule is a clock rather than the unlock meter: the other public pots are funded as the meter moves, and Spoils is funded once and then spent down a fixed number of days at a time.

The thousand days#

Compile-time constants of SpoilsVault, identical in every deployment
ConstantValueWhat it fixes
RELEASE_DAYS1000The schedule’s whole length in UTC days. Not settable, not extendable, not pausable.
EPOCH_SECONDS1 days — 86,400One measurement window. The same window ProtocolFeeSplitter.dailyFeesWei is keyed on, which is why the two can be compared at all.
START_DELAY24 hoursBetween the THD/ETH pool being seeded and the first releasable day. The vault’s clock hangs off ThdLiquidityManager, not off its own deployment.
WHEEL_BASELINE_BPS2000The Fortune Wheel’s unconditional share of every day, reserved first.
SHORTFALL_TO_WHEEL_BPS5000The half of a day’s shortfall that tops the wheel up. The other half is burned.

Two values are immutable, set at construction, and therefore facts about a deployment rather than about the source:

ImmutableWhere it comes fromValue today
BENCHMARK_WEIProtocolFeeSplitter.maxCountedPerEpoch(), read off the live splitter at construction — not typed into a profileWould be 2 ETH. That is money.protocolFeeMaxCountedPerEpochWei, which is 2000000000000000000 on all three profiles and is what every live launchpad record carries.
MINIMUM_POTDerived from three live reads — TOTAL_SUPPLY × bpsOf(Spoils) / BPS_DENOMINATOR. No literal anywhere.Would be 110,000,000 THD, the Spoils bucket’s 1,100 bps of 1,000,000,000

start() snapshots the vault’s THD balance as potAtStart and fixes dailyConsumption = potAtStart / RELEASE_DAYS. From then on every day consumes that same amount, until THD arrives after the start: the next release(day) spreads it over the days still to release, raising dailyConsumption by inflow / daysLeft and logging InflowSpread. THD held for projects that never graduated arrives this way, at the claim deadline. release(day) is permissionless, refuses a day that has not closed, refuses a day already released, and on the thousandth day consumes the whole remaining balance so that whatever the spread could not divide leaves by the same path as every other day.

How a day divides#

The whole arithmetic, from `SpoilsVault._divide`:

baseline  = consumed * WHEEL_BASELINE_BPS / 10_000;   // 20%
variable  = consumed - baseline;                      // the remaining 80%

capped    = min(feeSplitter.dailyFeesWei(day), BENCHMARK_WEI);
toUsers   = variable * capped / BENCHMARK_WEI;

remainder   = variable - toUsers;
wheelExtra  = remainder * SHORTFALL_TO_WHEEL_BPS / 10_000;   // half

toWheel   = baseline + wheelExtra;
burned    = remainder - wheelExtra;                   // by subtraction

toUsers + toWheel + burned equals the day’s consumption exactly, at every fee level. Both variable and the burn leg are taken by subtraction, so nothing is left behind; the only rounding is in toUsers, and what it rounds away falls into the remainder and is destroyed rather than stranded.

The contract’s own worked example, on a 110,000 THD daily consumption and a 2 ETH benchmark:

SpoilsVault, source example — consumption 110,000 THD, benchmark 2 ETH
That day’s feesTo usersTo the wheelBurned
2.0 ETH88,00022,0000
1.5 ETH66,00033,00011,000
1.0 ETH44,00044,00022,000
0.5 ETH22,00055,00033,000
0.0 ETH066,00044,000

Those row figures follow from the two values construction would supply — a 110,000,000 THD minimum pot over 1,000 days, and a 2 ETH benchmark. They are an illustration of the formula rather than a claim about a deployment: the real consumption is potAtStart / 1000, where potAtStart is whatever balance start() snapshots, and an inflow above the 11% floor raises it — before the start by enlarging the pot, after it by being spread over the days left.

Why a quiet day burns#

The same amount of THD leaves the vault every day regardless of revenue. The fees decide only who gets it. A quiet platform does not stretch the schedule, pause it or accumulate an overhang for later — it burns the part nobody earned.

That inverts the usual shape. In an ordinary emission, weak revenue and heavy sell pressure arrive together: tokens keep unlocking into a market that has nothing to absorb them. Here a weak day reduces supply instead, and the reduction is proportional to how weak the day was.

A weak day also does not only destroy supply: it moves the shortfall’s other half into the Fortune Wheel, which is reachable only by creating a launch or a market. A swollen wheel is therefore an incentive to produce the exact activity the weak day was short of.

Where the user pool goes#

The users’ leg does not reach users directly. It reaches SpoilsSplitter, which divides it three ways on a distribute() anybody may call:

SpoilsSplitter — the three shares it is constructed with
Destinationbps at constructionPage
Battlepass5000The battlepass
Bands3000Bands and seasons
Seasons2000Bands and seasons

The three are storage, not immutables: the owner may change them with setShares, and a change takes effect from the next season rather than mid-season — MONTHS_PER_SEASON is 3, and seasonIndexNow() is the quarter the current block falls in. sharesNow() is what a distribute() would use at this block and is the value to read; the raw battlepassBps, bandsBps and seasonsBps slots can lag it by a season.

The burn happens upstream of all of this. SpoilsSplitter and the wheel only ever see THD that has already been released, so nothing downstream of the vault can change how much THD enters circulation — whoever owns the splitter and whatever is built below it later.

Reading it from the chain#

What a released day records
Field on DayReleasedMeaning
dayThe UTC day index — timestamp / 86_400, the same key ProtocolFeeSplitter.dailyFeesWei uses.
feesWeiThat day’s armed-payer revenue as the splitter recorded it. A zero here is a real zero — a day with no log produces no row at all, so the two states are distinguishable.
consumeddailyConsumption (after any spread of THD that arrived since the last release), or the whole remaining balance on the thousandth day.
toUsers · toWheel · burnedThe three legs. They sum to consumed.
thdTotalSupplyTHD.totalSupply() after the day’s burn. The supply figure, in the log.

Lifetime totalToUsers, totalToWheel and totalBurned are in the vault’s own storage, so they never depend on a log scan reaching far enough back. A history assembled from logs that cannot reach the vault’s first block is a window, not a record, and understates the lifetime burn — compare it against totalBurned rather than presenting it as complete.