Rewards
Reward Catalog and just-in-time prizes
How FLIP supports many rewards without warehousing every stock token.
JIT Functional Testnet PASS · 7 stock + 1 cash fallback · Formal certification deferredOne versioned Reward Catalog
Coin, Parity, Mystery, capacity checks, and the user interface consume one ordered asset set. Only publish creates a new append-only version and ordered-assets hash; adding, removing, or reordering therefore means publishing a new set. setActiveVersion points admission at an existing eligible version, while deactivateActiveVersion sets the configured pointer to zero. Every accepted Ticket freezes the set identity. Manual freezes its selected reward at acceptance; Mystery freezes only the set and unassigned all-member capacity at acceptance, then freezes the concrete reward in the verified VRF selection callback before RandomReady or AwaitingReport.
Unified USDG Reserve, not one vault per stock
At acceptance, FLIP locks the extra House coverage needed for the Ticket and reserves JIT route capacity. A winner’s principal plus Reserve lock becomes one complete USDG prize liability. The protocol does not have to carry long-lived NVDA, AAPL, TSLA, and every future reward inventory before anyone wins.
Reserve USDG balance
≥ active prize liabilities
+ claimable cash fallbacks
+ every other locked Reserve responsibilityUnsettled principal, undelivered stock purchases, unreleased Provider/Reporter Bond, and unrealized FLIP price appreciation are obligations or exposures—not revenue. A single liability key can terminate only once through stock delivery or complete USDG fallback.
Static amount limits are only the first capacity check
baseAdditionalRaw = additionalReserveRaw >= bonusRaw
? additionalReserveRaw - bonusRaw
: additionalReserveRaw
generalReserveAvailable =
availableGeneralRaw >= safetyFloorRaw + baseAdditionalRaw
protectedExposureAvailable =
totalPendingPrizeNotionalRaw + totalLiabilityRaw + prizeNotionalRaw
<= maxProtectedExposureRaw
reservationSlotAvailable = activeReservations < maxActiveReservations
mysteryReserveAvailable = manual
OR (mystery.epochOpen AND mystery.maxStakeRaw >= principalRaw)inventoryCapacityRaw =
floor(inventoryRaw × route.reserveDenominator / route.rewardNumerator)
effectiveCapacityRaw = min(routeCapacityRaw, inventoryCapacityRaw)
requiredExposureRaw =
manualLockedRaw + selectedMysteryLockedRaw + unassignedMysteryLockedRaw
availableCapacityRaw = routeLive
? max(effectiveCapacityRaw - requiredExposureRaw, 0)
: 0
manual JIT check: selected asset availableCapacityRaw >= prizeNotionalRaw
mystery JIT check: every active Catalog asset availableCapacityRaw >= prizeNotionalRawready =
amountWithinLimits
AND catalogActive
AND rewardSupported
AND !gamePaused
AND reserveAcceptingGame
AND jitAcceptingGame
AND reservationSlotAvailable
AND protectedExposureAvailable
AND generalReserveAvailable
AND mysteryReserveAvailable
AND jitCapacityAvailable
AND !reserve.requiresBootstrap
ready == true ⇔ blockers.length == 0routeLive also requires an enabled nonzero route whose validity covers the Guard’s minimum TTL. The response exposes the finalized quote, raw Reserve fields, each asset’s route/inventory/effective/locked/available capacity, every admission boolean, and the exact blocker codes. It is advisory only: Bond funding, concurrent state changes, settlement, and the Gateway’s final atomic checks can still reject the transaction.
Winner delivery flow
Bet / Ticket wins
→ LiabilityCreated
→ JitQuoteAttempted
→ StockDelivered
execution failure:
JitPurchaseFailed → liability remains PurchasePending → new quote / retry
SLA expiry:
CashFallbackAvailable → CashFallbackClaimed- 01
Create liability
Win settlement fixes player, reward token, notional, rules, Catalog, and cash fallback time.
- 02
Publish signed quote
An authorized signer binds chain, Guard, Reserve, liability key, recipient, venue, notional, min output, expiry, and nonce.
- 03
Execute against an approved route
The Executor checks quote and route capacity, spends exactly the liability USDG, and measures actual reward delivery.
- 04
Record StockDelivered
Delivery succeeds only when the player’s real token balance increases by at least signed min-out and all accounting deltas match.
- 05
Fallback if the SLA expires
After the frozen SLA, anyone—including a Keeper—may call
makeCashClaimable(game, id)to close the JIT window. Only the recorded winner may callclaimCash(game, id)and receive the full USDG notional;claimCashcan perform both steps when still PurchasePending.
Prize liability state machine
PurchasePending → StockDelivered
PurchasePending → CashClaimable → CashClaimedStockDelivered and CashClaimed are mutually exclusive terminal states. A failed JIT attempt does not create a third terminal state, reduce the notional, return the Ticket to a loss, or consume the liability; it remains PurchasePending until delivery succeeds or the frozen cash-fallback SLA opens.
Failures cannot erase a win
- A malformed direct call or a pre-attempt state error can revert. Quote-guard failures and Executor/venue failures reached through
attemptJitPurchaseare caught, emitJitPurchaseFailed, returnfalse, and do not consume the liability. - Expired/wrong-domain/wrong-signer/replayed quotes, route/inventory failure, venue revert, or delivery/min-out mismatch leave
PurchasePending; the attempt can be retried with a new valid quote until the frozen SLA closes. - Stock delivery and USDG cash claim are mutually exclusive terminal paths.
makeCashClaimableis permissionless after the SLA but moves no user funds;claimCashenforcesmsg.sender == liability.playerand always pays that player.- The formal Testnet acceptance matrix includes 1/10/50/100/200 USDG notional boundaries, but it must not be labeled accepted until matching finalized evidence is published.
