SPOILS documentation
SPOILS is a daily faction war between two real stock tickers, settled on Robinhood Chain by the same Chainlink price feeds the tickers trade on.
This page is the full mechanism: what a front is, where every unit of value goes, what holding the token does, how settlement is proved, and what can go wrong. It is written to be checkable against the contracts rather than believed.
Pre-launch. Some of what follows is specified and audited by hand but not shipped yet. Anything in that state is marked not shipped. Numbers shown on the marketing site are illustrative until the token launches.
What SPOILS is
SPOILS is a daily faction war between two stock tickers: you stake $SPOILS on one side before the US market opens, and at the close the ticker with the higher open-to-close return takes 90% of the losing side's stake.
That is the whole game. There is no order book, no quoted probability, no position to manage. You pick a side, you commit an amount, you wait for the closing bell.
What you put in, what you get back
| What you stake | $SPOILS, the game token. Never a stock token |
| What you win | Your stake back, plus a share of the losing faction's pot |
| What you lose if your side loses | 100% of your stake |
| Who decides | Chainlink price feeds for both tickers on Robinhood Chain |
| How long it lasts | One US trading session, 09:30 to 16:00 ET (13:00 on half days) |
| When enlistment closes | 09:25 ET, five minutes before the open |
| Can you withdraw after enlisting | No. The contract has no withdraw function |
The judgement is a single comparison. Each side's return is measured from the first oracle round at or after the open to the last round at or before the close. Higher return wins. If the gap between the two returns is under 0.05 percentage points, the fight is a draw and everyone is refunded in full.
The split
Only the losing side's pot moves. Winners' principal is never touched by anyone.
| Share of the loser pot | Default | Where it goes |
|---|---|---|
| Winners | 90% | Split across the winning faction, pro rata by stake |
| Burn | 5% | Sent to the dead address inside the settlement transaction |
| Founder treasury | 5% | Pulled to the treasury contract, plus rounding dust |
These are per fight parameters, set at creation. The only hard limit the contract enforces is that burn plus founder must stay under 100%.
A worked example
NVDA faction pot: 60,000. TSLA faction pot: 40,000. TSLA closes up more.
- 01Loser pot is 60,000. Winners take 54,000, burn is 3,000, founder is 3,000.
- 02You staked 400 on TSLA, which is 1% of the winning pot.
- 03You claim 400 (your principal) + 540 (1% of 54,000) = 940.
- 04Everyone on the NVDA side receives exactly 0.
Your payout multiple is 1 + 0.9 x (loser pot / winner pot), so it depends on how the two pots balance, not on any quoted price:
| Loser pot vs winner pot | You get back |
|---|---|
| Equal | 1.90x |
| Winner side twice as large | 1.45x |
| Winner side half as large | 2.80x |
Both pots move until 09:25 ET, so the ratio you see while enlisting is not the ratio you settle at.
Why a faction war and not a probability market
A probability market asks you to price an outcome against a counterparty. SPOILS asks you which side you are on. One number applies to your whole faction, and it is set by how many people joined the other side, not by a market maker. There is nothing to trade in and out of, nothing to hedge, and no resolution committee to argue with: two price feeds decide it, and any wallet can trigger settlement once the session is over.
The tickers are real and the prices are real. The stakes and payouts are not: everything in the pool is $SPOILS. You never hold, receive, or transfer a tokenized share by playing.
Say it plainly
You can lose everything you enlist. Losing factions receive zero, deposits cannot be pulled back once made, and the burn share is destroyed rather than returned. The only outcomes that refund you in full are a draw or a void (too few participants, missing oracle rounds, or oracle timestamps too far apart), and those pay no burn and no founder share.
How a front works
A front is one trading session. Two tickers, two factions, two pots, settled by the two Chainlink price feeds on Robinhood Chain. Everything below is what the contract actually enforces (the fight pool contract); the clock values and the per-fight parameters are what the keeper submits when it creates the fight, and they are configurable rather than hard-coded on chain.
The day
| Time (ET) | What happens | What you can do | What you cannot do |
|---|---|---|---|
| Ahead of the session | Keeper calls createFight with the two feeds, the three timestamps, and the fight parameters | Nothing yet, the fight has to exist first | Only the keeper (contract owner) can open a front |
| Until 09:25 (open minus 5 min) | Enlist window | enlist(fightId, faction, amount) in $SPOILS, top up until you hit the per-wallet cap for that fight | Withdraw. There is no exit function. Deposits only go up |
| 09:25 | Lock | Watch | Enlist. There is no lock transaction, the deadline simply makes enlist revert |
| 09:30 (bell) | recordOpen snapshots the first oracle round at or after the open, for both feeds | Anyone can call it, including you | Change sides, add, or leave |
| 09:30 to 16:00 | The battle is just the market moving (13:00 on half days) | Nothing. Prices decide | Everything |
| After 16:00 | settle reads the last round at or before the close, picks the winner, burns the burn share in the same transaction | Anyone can call it | Contest the result. The caller submits round ids, the contract verifies they are the only valid ones |
| After settle | claim, claimFor, or claimMany pays out | Claim, or let anyone claim for you (funds always land in your own wallet) | Nothing to claim if you lost |
Keeper-side push payouts are decided but not yet wired into the keeper command table, which today runs create, open, and settle and no claim command. So for now claiming is a transaction someone has to send.
The money
Winners' deposits are never touched. Only the losing faction's pot is split, 90 / 5 / 5 by default (the split is a per-fight parameter, capped only by burn + founder < 100%).
| Piece | Share of loser pot | Destination |
|---|---|---|
| Loot for winners | 90% | Winning wallets, pro rata to stake |
| Burn | 5% | 0x…dEaD, sent inside the settle transaction |
| Founder / treasury | 5% plus rounding dust | The founder treasury address, moved afterwards by a separate permissionless claimProtocolShares call |
Worked example. Losing faction staked 100,000 $SPOILS, winning faction 50,000.
- Burn 5,000. Treasury 5,000. Loot pool 90,000.
- You staked 1,000 on the winner (2% of the winning side): you claim 1,000 principal + 1,800 loot = 2,800.
- You staked 1,000 on the loser: you claim 0. Your entire stake became the three rows above.
That is the honest shape of it. Every wallet on the losing side loses its whole deposit.
Three more enlist rules
- Per-wallet floor and cap. Your running total on a faction has to clear a minimum stake and stay under a per-wallet cap. Both are per-fight parameters and neither value is fixed yet.
- 3x faction cap. A faction cannot exceed 3x the other faction's total. If your side is already 3x ahead, your enlist reverts until the other side catches up. The cap is not checked while the other side is still empty, so the first faction can run ahead until the first opposing deposit lands.
- Nothing stops you from staking both sides, but the contract only pays out the winning faction's stake, so the other half is forfeited exactly like anyone else's losing stake.
When nobody wins: void
Void means full refunds, both sides, no burn and no founder cut.
| Reason | Trigger | Who can fire it |
|---|---|---|
| NotViable | A faction is short of minimum wallets or minimum total at the deadline | Auto at recordOpen, or anyone from 09:25 |
| NoOpen | No open recorded 30 minutes past the bell | Anyone |
| CloseSkew | The two close rounds are more than 15 minutes apart | Auto at settle |
| Draw | The two returns are within 0.05 percentage points | Auto at settle |
| Emergency | Still unsettled 24 hours after the close | Anyone |
Open rounds more than 10 minutes apart revert instead of voiding, so the call can be retried; if it never succeeds, the fight falls through to NoOpen or Emergency. The design bias is simple: if the price cannot be proven, everyone gets their money back.
Where the money goes
You stake $SPOILS on a faction. If your side wins, your stake comes back untouched plus a share of what the other side left behind. If your side loses, you get nothing back. That is the whole trade, and it is worth reading twice: a loss is a total loss of the amount you enlisted.
The split applies to the loser's pot only
The 90/5/5 is not a cut of the total pot. It is the breakdown of the losing faction's deposits. Winner principal is never touched by burn or by the treasury.
| Slice | Share of loser pot | Where it goes | When |
|---|---|---|---|
| Loot | 90% | Winning wallets, pro rata to stake | On claim, paid together with your principal (a keeper can push it via claimFor / claimMany) |
| Burn | 5% | 0x000...dEaD | Inside the settlement transaction itself |
| Founder treasury | 5% + rounding dust | the treasury | Permissionless claimProtocolShares, a separate transaction after settlement |
| Loser stake | 0% returned | Nowhere. It is gone | Booked at settlement |
Both the winner pool and the burn are floor divisions, so at most a couple of wei per fight fall out as remainder. settle computes the founder share as the leftover (loserPool - winnerPool - burnAmount), which puts that dust in the treasury, and test_BpsDustGoesToFounder pins it there.
enlist credits the balance delta the contract actually receives, not the amount you passed in. Nothing is skimmed by this contract on the way in; a token that taxes plain transfers would simply credit you less than you sent.
A worked example
Assume a fight settles with these totals:
| Input | Amount |
|---|---|
| Losing faction total stake | 1,000,000 $SPOILS |
| Winning faction total stake | 600,000 $SPOILS |
| Your stake on the winning side | 30,000 $SPOILS |
Then:
burn = 1,000,000 x 5% = 50,000 (sent to dead address at settlement)
founder = 1,000,000 x 5% = 50,000 (+ dust)
winner pool = 1,000,000 x 90% = 900,000
your loot = 900,000 x (30,000 / 600,000) = 45,000
your payout = 30,000 principal + 45,000 loot = 75,000 $SPOILSYour return on the enlisted amount is +150%. Had you enlisted the same 30,000 on the losing side, your payout would have been 0.
Loot scales linearly with the pot ratio: loot per 1 staked = 0.90 x (loser pot / winner stake). Reference points:
| Case | Loot per 1 staked | Payout per 1 staked |
|---|---|---|
| Loser pot at 1/3 of winner stake | 0.30 | 1.30 |
| Even sides | 0.90 | 1.90 |
| Loser pot at 3x winner stake | 2.70 | 3.70 |
Do not read those as hard bounds. FACTION_CAP_MULTIPLE is 3, and enlist reverts if your deposit would push your faction's total past 3x the other faction's total, but the check returns early while the opposing faction is still empty. Deposits that land before the first opponent arrives are unbounded, and the cap is never re-evaluated after enlistment closes, so a fight can settle at a ratio well past 3x. Read the two faction totals on the fight you are entering.
Every fee, in full
| Fee | Rate | Charged where | Status |
|---|---|---|---|
| Enlist fee | 0 | Nothing is skimmed on the way in | Live: the contract has no fee on enlist (D-011 removed the 1% enlist tax) |
| War burn | 5% of loser pot | Settlement | Live |
| Founder treasury | 5% of loser pot | Settlement | Live |
| Creator tax on $SPOILS swaps | Target 2%, not yet fixed | Token swaps, not the fight pool | Decided in principle (D-011), routed 100% to holder dividends. Rate is fixed once at Pons launch and cannot be raised afterwards, and is still gated on reading maxCreatorTaxBps() on chain. Not yet wired |
| Web frontend fee | 0.2% of deposit, waived on direct contract calls | Frontend only | Written in DESIGN section 6, not implemented anywhere |
Burn and founder rates are per fight parameters, not constants. The keeper's default is 500 bps each, and the contract carries the same 500 as DEFAULT_BURN_BPS / DEFAULT_FOUNDER_BPS, but createFight takes the rates explicitly and does not apply those defaults for you. The only ceiling the contract enforces is burnBps + founderBps < 10000, so always read the rates on the fight you are entering rather than assuming 5/5.
If the fight does not resolve
A fight can end voided for five reasons: a faction missed the minimum wallet count or minimum total at the enlist deadline, no open was recorded within 30 minutes of battleStart, the two feeds' close timestamps differ by more than 15 minutes, the two returns land within 0.05 percentage points of each other, or anyone triggers the emergency refund 24 hours past battleEnd. In every one of those cases both sides get their full deposits back through the same claim path, which a keeper can also push. No burn, no founder share, no loot, and claimProtocolShares reverts on a voided fight.
Close skew voids are a normal outcome, not an incident. Only 4 of the last 11 measured sessions cleared the 15 minute close gate.
Why hold $SPOILS
Holding $SPOILS does two things: it makes you eligible for the daily spoils dividend, and it exposes you to a supply that shrinks whenever a battle settles with a burn share set. It does not entitle you to fees, equity, or any claim on assets. Fighting is optional. You can lose your entire stake when you fight, and the token itself can lose value whether you fight or not.
The dividend formula
The dividend is paid in the stock token of the ticker that won that day. Funding comes from the creator tax on $SPOILS swaps, not from the fight pot (D-013). The tax is collected in the pairing asset, and the keeper buys the winning stock token with it. The tax rate is targeted at 2% and the pairing asset is still open; both are set once at the Pons launch and cannot be changed afterward, so neither is fixed yet.
effective balance = wallet balance
+ sum of your stake in fights with status Created or OpenRecorded
your payout = dividend pool * (your effective balance / total effective balance)Snapshot rules, as decided in D-015:
| Rule | Value |
|---|---|
| Stake in an open fight (Created, OpenRecorded) | Counted |
| Stake in a Settled or Voided fight | Not counted (avoids double counting once pushed back to your wallet) |
| Excluded addresses | FightPool, Treasury, DividendVault, 0x000...dEaD, Pons liquidity pools |
| Effect of fighting on weight | None while the fight is open. Win and your next weight grows, lose and it shrinks |
| Flash loan guard (hardening from D-014) | min(snapshot, live balance), applied to the wallet portion only |
Worked example, with a pool of 1,000 units of the winning stock token and 50,000,000 $SPOILS of total effective balance:
| Holder | Wallet | Open stake | Effective | Share | Payout |
|---|---|---|---|---|---|
| A | 300,000 | 200,000 | 500,000 | 1.0% | 10.0 |
| B | 500,000 | 0 | 500,000 | 1.0% | 10.0 |
| C | 100,000 | 0 | 100,000 | 0.2% | 2.0 |
A and B are paid the same. Enlisting neither adds nor removes dividend weight.
Supply and burn
A settled fight burns the burn share of the losing side's stake inside the settlement transaction itself, sent directly to 0x000...dEaD. The burn is not queued and nobody has to trigger it separately. The other two shares are allocated at settlement but move in later transactions that anyone can call: winners pull their principal plus loot with claim, and the keeper can push it for them with claimFor or claimMany, while the founder share is transferred to the treasury by a permissionless claimProtocolShares.
| Loser pot of 100,000 $SPOILS | Amount | Destination |
|---|---|---|
| Winners (pro rata to stake) | 90,000 | Winning soldiers, on claim |
| Burn | 5,000 | 0x000...dEaD, inside settle |
| Founder treasury | 5,000 plus rounding dust | Treasury, on claimProtocolShares |
Winner principal is never touched. A voided fight, including a draw, refunds every enlistment in full and burns nothing.
The 5/5 split is a per fight parameter, not a constant. The keeper default is 500/500 bps and is overridable by environment variable, and the only limit the contract enforces is burnBps + founderBps < 10000. There is no enlistment fee in the contract. Total supply is set at the Pons launch and is not yet fixed in this repository.
What is live and what is not
Nothing is deployed yet. The contracts pass their full unit suite (64 tests across the pool, the vault, the treasury, and the testnet mirror) plus a mainnet fork rehearsal of the whole lifecycle. There has been no third party audit.
| Piece | Status |
|---|---|
| Fight settlement and in-transaction burn | Written and tested, permissionless, not deployed |
claim / claimFor / claimMany payouts | In the contract. The keeper that pushes them is in progress |
| DividendVault, idempotent per (token, epoch) | Written and tested, not deployed. distribute is owner only |
| Indexer that computes effective balances | Not built |
| Distributor that runs epochs | Not built |
min(snapshot, live) inside distribute | Not implemented. Amounts are supplied by the keeper |
| ERC20 payout batches | Use safeTransfer, so one reverting recipient fails the whole epoch. The ETH path accrues instead of reverting |
| Creator tax rate and pairing asset | Not yet fixed |
Until those rows change, the dividend is a decided design, not a running system. Treat any dividend number you see as illustrative.
Settlement and the oracle
The contract never fetches a price on its own, and it never trusts the keeper's opinion about one. A settler submits two Chainlink round IDs, and the contract proves those rounds are the only rounds that could have been submitted.
The window
The keeper sets three timestamps when it creates a fight. The contract enforces now < enlistDeadline <= battleStart < battleEnd, plus sanity checks on the two feed addresses and the stake parameters. It does not know what a trading session is. The clock times below come from the keeper's NYSE calendar, not from the contract.
| Marker | Value | Set by |
|---|---|---|
enlistDeadline | 09:25 ET (open minus 5 minutes) | keeper |
battleStart | 09:30 ET | keeper |
battleEnd | 16:00 ET (13:00 ET on half days) | keeper |
There is no lock transaction. After enlistDeadline, enlist simply reverts. There is no withdraw function at all, so a stake is final from the moment it lands.
Choosing the round
recordOpen and settle are both permissionless. Anyone can call them, including you. recordOpen is callable once battleStart has passed, settle once battleEnd has passed.
| Step | What the caller submits | What the contract proves |
|---|---|---|
recordOpen | first round at or after battleStart for each feed | updatedAt >= battleStart, and the previous round in the same phase, when it is readable, updated before battleStart |
settle | last round at or before battleEnd for each feed | updatedAt <= battleEnd, updatedAt >= openUpdatedAt, and the next round, when it exists, updated after battleEnd |
Every round is also checked for answer > 0 and updatedAt != 0. If Chainlink has rolled to a new aggregator phase, the neighbour proof cannot be reconstructed and the call reverts rather than guessing. A submitted round that fails any of these checks does not settle the fight badly. It does not settle the fight at all.
The math
Returns are computed in WAD from the two proven answers:
returnA = (closeA - openA) * 1e18 / openAWorked example (illustrative prices, real feeds are RHNVDA/USD and RHTSLA/USD at 8 decimals):
| Ticker | Open | Close | Return |
|---|---|---|---|
| NVDA | 100.00 | 101.20 | +1.20% |
| TSLA | 200.00 | 201.00 | +0.50% |
Gap is 0.70 percentage points, above the 0.05pp draw threshold, so NVDA's faction wins. The losing faction's entire stake is then split by that fight's own basis points, which the keeper sets at createFight. The shipped defaults are 90 / 5 / 5 (winners / burn / founder treasury), and the contract accepts any pair whose burn plus founder share stays under 100 percent. The burn goes to 0x…dEaD inside the settle transaction. The founder share stays in the pool until anyone calls claimProtocolShares, which can only pay the founder treasury address fixed at deployment. Winners claim their own stake back plus a pro rata cut of the winner pool. Losers receive zero. That is the point of the game: you can lose everything you enlisted.
Void conditions
| Reason | Trigger | Who can fire it |
|---|---|---|
NotViable | a faction misses min wallets or min total | anyone from enlistDeadline onward, or automatically at recordOpen |
NoOpen | still no open recorded more than 30 minutes after battleStart | anyone |
CloseSkew | close rounds more than 15 minutes apart | fires during settle |
Draw | return gap below 0.05pp | fires during settle |
Emergency | more than 24 hours past battleEnd and still unsettled | anyone |
Every void path makes both factions' deposits claimable in full. Burn is zero, founder share is zero.
Why nobody can move the number
createFight is the only fight-facing function behind onlyOwner. Ownership can be transferred or renounced, which is the stock Ownable surface, but there is no admin settle, no price override, no pause, no withdraw. Open skew above 10 minutes reverts instead of voiding, so a settler cannot pick a stale pair to shade the result. Payouts always go to the soldier's own address, even when a third party calls claimFor or claimMany. If the keeper disappears entirely, the fight still settles from any address, and failing that it refunds.
Risks and limits
SPOILS is a game you can lose money in. The loser faction receives nothing. Not a reduced payout, not a rebate: zero. Every token enlisted on the losing side is split 90 / 5 / 5 at default parameters (winners, burn, founder treasury) and none of it comes back.
The payout is parimutuel, so the crowd sets your upside
The contract enforces a per wallet cap and a faction level cap of FACTION_CAP_MULTIPLE = 3, but that 3x is not a ceiling on the final ratio. _checkFactionCap returns early when the opposing faction is still empty, and it only checks the faction that is enlisting. One side can therefore run unbounded before the first opposing deposit arrives, and the ratio is never re-evaluated after that. Deposits are monotonic (no withdrawal during the enlist window), which means an early imbalance cannot be undone. Read both faction totals on the fight you are entering. Your upside is set by the size of the *other* faction, not by how right you were, and it is not bounded by the constant.
| Scenario (defaults: burn 5%, founder 5%) | Loser pot | Your stake | You are paid |
|---|---|---|---|
| You are on the small side (100k vs 300k) and win | 300,000 | 1,000 | 3,700 (principal + 2,700) |
| You are on the crowded side (300k vs 100k) and win | 100,000 | 1,000 | 1,300 (principal + 300) |
| You lose, either side | your side is the pot | 1,000 | 0 |
Those two rows assume both factions are already populated and near the cap. They are examples, not bounds: as described above, the cap does not pin the ratio, so the real range is wider in both directions. Picking the ticker that outperforms is necessary. It is not sufficient for a payout worth the risk.
Days that pay nothing at all
A fight can end with no winner. Every one of these refunds all stakes in full, with zero burn and zero founder share, but it also means a day of capital locked for nothing.
| Void reason | Trigger |
|---|---|
| NotViable | A faction is under 5 wallets or under 100 tokens (keeper defaults; the contract takes these as per fight parameters) |
| NoOpen | No one records the open within 30 minutes of battleStart |
| CloseSkew | The two close rounds are more than 15 minutes apart |
| Draw | The return gap is under 0.05 percentage points |
| Emergency | 24 hours past battleEnd with nothing settled |
Voids are a normal outcome, not an exception. Of the last 11 measured sessions, only 4 would have cleared the 15 minute close skew gate.
The oracle is honest but coarse
Chainlink deviation feeds have no concept of a closing print. They update on movement, with an 86400 second heartbeat and a 0.5% deviation threshold. Measured medians on the megacap feeds run 13.8 to 25.0 minutes, but the tail is what decides a fight: p90 reaches 131 minutes on AAPL, and SPY's median is 141.6 minutes. Cadence is not a constant and has been falling (NVDA went from 17.8 to 6.2 updates per session across five weeks).
The contract proves it took the first round at or after battleStart and the last round at or before battleEnd, and it refuses rather than guesses when a Chainlink phase boundary makes that unprovable. But "last print before 16:00 ET" is not the official close, and open skew over 10 minutes reverts. On thin or quiet tickers, expect voids.
The contract also does not know what a trading session is. battleStart and battleEnd are numbers the keeper supplies at createFight. A wrong schedule produces a valid, unarguable, wrong fight. The same applies to corporate actions: a 2:1 split prints as roughly minus 50% on that feed and the faction loses undeservedly. The only guard is a keeper preflight before the fight is created, not anything the contract can check.
Contract and operator risk
- No third party security audit. What exists is 65 passing tests and one mainnet fork rehearsal against a real session, including four oracle negative cases.
- The pool owner key controls
createFightonly. It cannot alter a settlement, pause, or withdraw; there is no pause function in the pool at all. But nothing bounds the fee split beyondburnBps + founderBps < 10000, so a malicious or fat fingered fight could set the founder share near 99%. Read the parameters before you enlist. - One wallet enlisting on both factions is not blocked. Hedging that way does not protect you: only the winning side's stake pays, and the losing side's stake is gone.
DividendVault.distributeis a keeper push with arbitrary recipients and amounts. The 48 hour timelock guards scheduled withdrawals, notdistributeorswapApprove. A compromised keeper key drains the vault. This is documented as intended v1 centralization, not an oversight.- The vault briefly holds the winning ticker's stock token between purchase and distribution, so during that window it is exposed to the issuer's powers over those tokens (admin burn, pause, blocklist). The fight pool itself never holds stock tokens.
Dividends do not exist yet
The 5% cut goes to the founder treasury, not to holders. The holder dividend (paid in the winning ticker's stock token, funded by the creator tax on $SPOILS swaps) has a contract slot, but the indexer, the executor, and the flash loan hardening are unbuilt. The tax rate itself is not yet fixed: it can only be set at launch on Pons and cannot be changed afterward. Do not buy $SPOILS for a payout that has not shipped.
Liquidity and regulation
$SPOILS has not launched. There is no market maker by design and exit liquidity will be a single Pons pool. Regulatory posture: no legal opinion has been obtained. The relative performance structure resembles a total return swap more than a spot game, and the founders accepted that risk explicitly rather than resolving it. The game can stop.
Who should not play
People who need the money back. People who want a hedge, an investment, or exposure to equities: stakes and payouts are denominated in $SPOILS, and the fight pool never holds or transfers stock tokens, so there is no equity exposure here. Anyone in a jurisdiction that treats this as gambling or as an unregistered securities activity, which may include participants and not only operators.