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.
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#
| Constant | Value | What it fixes |
|---|---|---|
RELEASE_DAYS | 1000 | The schedule’s whole length in UTC days. Not settable, not extendable, not pausable. |
EPOCH_SECONDS | 1 days — 86,400 | One measurement window. The same window ProtocolFeeSplitter.dailyFeesWei is keyed on, which is why the two can be compared at all. |
START_DELAY | 24 hours | Between 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_BPS | 2000 | The Fortune Wheel’s unconditional share of every day, reserved first. |
SHORTFALL_TO_WHEEL_BPS | 5000 | The 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:
| Immutable | Where it comes from | Value today |
|---|---|---|
BENCHMARK_WEI | ProtocolFeeSplitter.maxCountedPerEpoch(), read off the live splitter at construction — not typed into a profile | Would be 2 ETH. That is money.protocolFeeMaxCountedPerEpochWei, which is 2000000000000000000 on all three profiles and is what every live launchpad record carries. |
MINIMUM_POT | Derived 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 subtractiontoUsers + 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:
| That day’s fees | To users | To the wheel | Burned |
|---|---|---|---|
| 2.0 ETH | 88,000 | 22,000 | 0 |
| 1.5 ETH | 66,000 | 33,000 | 11,000 |
| 1.0 ETH | 44,000 | 44,000 | 22,000 |
| 0.5 ETH | 22,000 | 55,000 | 33,000 |
| 0.0 ETH | 0 | 66,000 | 44,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:
| Destination | bps at construction | Page |
|---|---|---|
| Battlepass | 5000 | The battlepass |
| Bands | 3000 | Bands and seasons |
| Seasons | 2000 | Bands 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#
Field on DayReleased | Meaning |
|---|---|
day | The UTC day index — timestamp / 86_400, the same key ProtocolFeeSplitter.dailyFeesWei uses. |
feesWei | That 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. |
consumed | dailyConsumption (after any spread of THD that arrived since the last release), or the whole remaining balance on the thousandth day. |
toUsers · toWheel · burned | The three legs. They sum to consumed. |
thdTotalSupply | THD.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.