Skip to main content

← Prediction Game · Prompt Deck · Pictures

Prediction Game Spec

How do you prove a settlement stack works? Ship a game that uses every piece.

Intent Contract

Agent autonomy boundary. Not parsed by engineering — governance instrument for humans and agents.

DimensionStatement
ObjectiveProve the Sui settlement stack works by shipping a prediction game where every capability an agent needs — atomic settlement, gasless onboarding, oracle data, owned assets — executes in a single product.
Outcomes1. 50+ predictions settled atomically on testnet in 30 days. 2. Zero users required to install a wallet extension. 3. Settlement completes in single PTB.
Health MetricsExisting Supabase services must not degrade. Oracle uptime >95%. Sponsored tx success rate >99%.
ConstraintsHard: Testnet only for V1. Steering: Rugby fixtures first, other sports later. Budget: <$50/month infrastructure on testnet.
AutonomyAllowed: UI layout, component choice, Move module internal structure. Escalate: Oracle data source selection, mainnet migration. Never: Mainnet deployment without audit, real-money staking without legal review.
Stop RulesComplete when: all Build Contract rows at Live + 50 predictions settled. Halt when: PTB settlement requires >1 tx, or zero predictions after 30 days.
Counter-metricsOracle must not become a single point of failure — if one operator goes offline, settlement must continue via fallback. Sponsored transactions must not create unbounded liability — cap per-user daily gas spend.
Blast RadiusNew Move packages on testnet. New Next.js app in NX monorepo. New NX libs for Sui integration. Does NOT touch existing apps, Supabase, or Convex services.
RollbackTestnet contracts are disposable — redeploy to rollback. Frontend is a new app — delete the app to rollback. No shared state at risk.

Story Contract

Stories are test contracts. Each row converts to test files. Tests must be RED before implementation starts. Tests going GREEN = value delivered.

Every story follows the P2P arc: Pain (who hurts, measurable cost) → Performance Target (the number that proves it works) → Validated Outcome (the Tight Five dimension this serves).

#WHEN (Trigger + Precondition)THEN (Exact Assertion)ARTIFACTTest TypeFORBIDDEN (Must not happen)OUTCOME (Performance Number)
S1User navigates to /predictions with 5+ fixtures in GameRegistry where status == upcoming and clock < kickoff_epochPage renders fixture cards showing: team names, kickoff time (local TZ), current pool totals per outcome. Cards sorted by kickoff ascending. No wallet connection required.BLOCKER — test path TBD by engineeringe2ePage requires wallet connection to view fixtures. Fixtures with status != upcoming appear in the list. Pool totals show stale data (>30s old).Browse 5+ matches in <3s vs current method of checking multiple betting sites at ~60s per match
S2User clicks "Predict" on a fixture, authenticates via zkLogin (Google OAuth), selects outcome (home/draw/away), confirms stake amountSingle sponsored PTB executes: create Prediction object (owned by user), transfer stake to fixture pool, update pool totals. Prediction.status == pending. Receipt NFT minted with Dynamic Display showing "Pending." No wallet extension installed.BLOCKER — test path TBD by engineeringe2e + integrationUser prompted to install wallet extension. PTB fails silently — UI shows success but no on-chain object created. Stake deducted but Prediction object not created (partial execution). User charged gas.Predict in <30s with 0 gas decisions vs EVM flow requiring wallet install + 2 gas approvals at ~3 min total
S3Oracle agent posts match result for a fixture with 10+ predictions across all three outcome poolssettle_fixture() PTB consumes hot potato OracleResult, calculates proportional payouts, updates all Prediction objects to won/lost, transfers winnings to winners, updates receipt NFT Display to show final status. Single PTB.BLOCKER — test path TBD by engineeringintegrationSettlement requires >1 transaction. OracleResult persists after settlement (hot potato violated). Any Prediction object remains in pending status after settlement. Losers receive payout. Winners receive less than proportional share.Settle entire fixture in <1s in 1 PTB vs EVM requiring per-user claim transactions at ~15-60s each
S4User navigates to /predictions/mine with 3+ settled predictions (mix of won and lost)Page shows prediction cards with: fixture name, predicted outcome, actual result, stake amount, payout (if won), receipt NFT link. Cards sorted by settlement time descending. Won/lost visually distinct.BLOCKER — test path TBD by engineeringe2ePage shows predictions from other users. Won predictions show $0 payout. Lost predictions show a payout amount. Pending predictions mixed with settled without clear status indicator.Track record visible in <5s vs no on-chain history on existing platforms
S5Oracle agent attempts to post conflicting results for the same fixture (result already submitted) OR oracle agent is offline when settlement window opensDuplicate submission: Move module rejects with EAlreadySettled error. Oracle offline: Admin can trigger manual settlement after timeout period. Fixture status transitions: upcoming → live → settling → settled. No intermediate state allows double-settlement.BLOCKER — test path TBD by engineeringintegrationDouble settlement succeeds — fixture pays out twice. Fixture stuck in live forever with no admin override path. Oracle posts result before clock >= kickoff_epoch (clock guard violated).Zero double-settlements and zero stuck fixtures vs historical oracle bugs causing fund loss

Tight Five coverage check:

P DimensionStoriesGap?
Pain (multi-tx EVM settlement)S2 (single PTB predict), S3 (single PTB settle)No
Demand (rugby fans, crypto-curious)S1 (browse without wallet), S4 (track record)No
Edge (Move type system, PTB composition)S3 (hot potato oracle), S5 (compiler-enforced safety)No
Trend (onchain gaming, gasless UX)S2 (zkLogin + sponsored tx)No
Conversion (dogfood proof)S1 (frictionless browse), S2 (frictionless predict)No

Build Contract

The deliverable. Engineering builds from this. Commissioning reads this.

Job 1: Find and Pick

#FeatureIDFunctionArtifactSuccess TestSafety TestRegression TestValueState
1WCAP-007Browse upcoming fixturesMatch list page with fixture cardsPage loads <2s, shows 5+ fixtures sorted by kickoffNo wallet required to viewExisting app routes unaffectedSee what's available without commitmentGap
2WCAP-007Filter fixtures by competition/dateFilter controls on match listFilters reduce list correctly, URL params persistFilter state doesn't leak between sessionsN/A — new pageFind the match you care aboutGap
3WCAP-008View fixture detail with pool totalsMatch detail pageShows teams, odds, pool distribution, prediction countPool totals refresh without full page reloadN/A — new pageUnderstand the market before committingGap
4WCAP-008Place prediction on fixture outcomePrediction form + confirmationPrediction object created on-chain, receipt mintedCannot predict after kickoff (clock guard)N/A — new moduleBack your conviction with a stakeGap

Job 2: Settle Atomically

#FeatureIDFunctionArtifactSuccess TestSafety TestRegression TestValueState
5WCAP-019Oracle posts match resultoracle.move + agent scriptOracleResult created as hot potato, consumed in same PTBDuplicate results rejected (EAlreadySettled)N/A — new moduleBring real-world truth on-chainGap
6WCAP-019Settle fixture atomicallytreasury.move settlement PTBSingle PTB: consume oracle result + calculate payouts + distributeHot potato enforces atomic consumption. No partial settlement.N/A — new moduleProve atomic settlement worksGap
7WCAP-020Calculate proportional payoutstreasury.move payout mathWinners receive (user_stake / winning_pool) * total_poolSum of payouts <= total pool (no inflation). Zero-winner edge case handled.N/A — new moduleFair distribution without intermediaryGap
8WCAP-019Update prediction status post-settlementprediction.move state transitionAll predictions transition from pending to won/lostNo prediction remains pending after fixture settledN/A — new moduleKnow your result immediatelyGap
9WCAP-020Update receipt NFT displayDynamic Display updateReceipt shows current status (pending/won/lost/claimed)Display template resolves from object state, not cachedN/A — new moduleProof of prediction that evolvesGap

Job 3: Track Record

#FeatureIDFunctionArtifactSuccess TestSafety TestRegression TestValueState
10WCAP-011View my predictionsMy predictions pageShows all user predictions with status, stake, payoutOnly shows predictions owned by authenticated userN/A — new pageSee your track recordGap
11WCAP-011View prediction receipt NFTReceipt detail viewShows full prediction metadata: fixture, outcome, stake, result, payoutReceipt object ownership verified before displayN/A — new pageProve your convictionGap
12WCAP-011Leaderboard by seasonLeaderboard pageRanked by win rate with minimum prediction countNo PII exposed — address or SuiNS onlyN/A — new pageCompete on conviction qualityGap
13WCAP-011Season stats and trendsStats dashboardWin rate, total staked, total won, streakStats computed from on-chain data, not cachedN/A — new pageUnderstand your prediction patternsGap

Job 4: Onboard Frictionlessly

#FeatureIDFunctionArtifactSuccess TestSafety TestRegression TestValueState
14WCAP-008Authenticate via zkLoginzkLogin integrationUser signs in with Google/Apple, gets Sui addressOAuth token never stored on-chain. ZKP salt managed securely.Existing auth flows unaffectedNo seed phrase, no wallet installGap
15WCAP-007Sponsor user transactionsGas station backendAll user PTBs gas-free. Platform co-signs.Per-user daily gas cap enforced. Balance alerts at 50%.N/A — new serviceZero gas decisionsGap
16WCAP-008First-time user flowOnboarding screensNew user: browse → pick → authenticate → predict in <60sNo step requires prior crypto knowledgeN/A — new flowConvert curious to committedGap
17WCAP-007Wallet extension fallbackdApp Kit wallet connectUsers with existing Sui wallets can connect directlyWallet adapter handles all Sui wallets uniformlyN/A — new integrationSupport power usersGap
18WCAP-019Admin override for oracle failureadmin.move + admin UIAdmin can trigger manual settlement after timeoutAdmin actions require AdminCap — no unauthorized settlementN/A — new moduleSystem never gets stuckGap

Screen Contracts

One contract per screen. Each screen is the atomic execution and test unit.

Screen: Match List (/predictions)

Serves: S1, Build Contract #1, #2

Flow and States

DimensionSpec
Route/predictions
Entry fromSidebar "Predictions" link, homepage CTA
SuccessPage renders fixture cards within 2s
ErrorEmpty state with "No upcoming fixtures" message + retry
Auth deniedN/A — page is public, no auth required
LoadingSkeleton cards (3 placeholder cards with shimmer)
Empty"No upcoming fixtures right now. Check back before the weekend." + link to past results
DisabledN/A — read-only page

Elements

ElementSelectorStates
Fixture cardrole="article", name={fixtureName}upcoming, live, settled
Competition filterrole="combobox", label="Competition"default (all), selected
Date filterrole="group", label="Date range"default (upcoming), custom range
Pool total badgerole="status"loading, loaded
Predict buttonrole="link", name="Predict"enabled (upcoming), disabled (live/settled)

Screen: Prediction Form (/predictions/{matchId}/predict)

Serves: S2, Build Contract #3, #4

Flow and States

DimensionSpec
Route/predictions/{matchId}/predict
Entry from"Predict" button on fixture card
SuccessPrediction confirmed → redirect to /predictions/mine with success toast
ErrorSame page + revert + error toast with specific message
Auth deniedRedirect to zkLogin flow, return to form after auth
LoadingSubmit button shows spinner, form inputs disabled
EmptyN/A — fixture data required to render
DisabledSubmit button disabled during PTB execution, all inputs locked

Elements

ElementSelectorStates
Fixture headerrole="heading", name={fixtureName}loaded
Outcome selectorrole="radiogroup", label="Your prediction"unselected, home, draw, away
Stake inputrole="spinbutton", label="Stake amount"empty, valid, error (below minimum)
Pool distributionrole="meter", name={outcome}loading, loaded (shows % split)
Submit buttonrole="button", name="Place Prediction"enabled, loading, disabled
Auth promptrole="dialog", label="Sign in to predict"hidden, visible

Screen: My Predictions (/predictions/mine)

Serves: S4, Build Contract #10, #11

Flow and States

DimensionSpec
Route/predictions/mine
Entry fromSidebar "My Predictions" link, post-prediction redirect
SuccessPage renders prediction cards sorted by settlement time desc
ErrorError toast + retry button
Auth deniedRedirect to zkLogin flow
LoadingSkeleton cards with shimmer
Empty"No predictions yet. Browse upcoming matches to get started." + link to /predictions
DisabledN/A — read-only page

Elements

ElementSelectorStates
Prediction cardrole="article", name={fixtureName}pending, won, lost, claimed
Status badgerole="status"pending (amber), won (green), lost (red), claimed (blue)
Payout amounttestid="payout-{predictionId}"hidden (lost/pending), visible (won/claimed)
Receipt linkrole="link", name="View Receipt"enabled
Stats summaryrole="region", label="Your Stats"loading, loaded

Screen: Match Detail (/predictions/{matchId})

Serves: S1, S3, Build Contract #3, #9

Flow and States

DimensionSpec
Route/predictions/{matchId}
Entry fromFixture card click on match list
SuccessPage renders fixture detail with pool distribution and prediction list
Error"Fixture not found" with link back to match list
Auth deniedN/A — page is public
LoadingSkeleton layout with header + pool chart placeholder
EmptyFixture exists but zero predictions — show pool at 0 with "Be the first to predict" CTA
DisabledPredict button disabled if fixture is live or settled

Elements

ElementSelectorStates
Fixture headerrole="heading", name={fixtureName}upcoming, live, settled
Pool chartrole="img", label="Pool distribution"loading, loaded, empty
Prediction listrole="list", label="Recent predictions"loading, loaded, empty
Result bannerrole="alert"hidden (upcoming/live), visible (settled)
Predict CTArole="link", name="Make Your Prediction"enabled (upcoming), hidden (live/settled)

How does the user move from pain to effortless performance?

Pain-to-Perform Journey

StageUser StateNavigation Must DoCurrent StateTarget State
PainPrediction = multiple sites, multiple transactions, gas confusionShow the cost of the current pathNo on-chain prediction productMatch list visible without auth
AwarenessThere's a simpler way to predictSurface the new entry without disrupting existing flowN/A — new product"Predictions" in sidebar, homepage CTA
First ValueThat was genuinely fastDeliver one complete prediction immediatelyN/AzkLogin → predict → confirm in <30s
HabitI check before every matchMake predictions the natural pre-match routineN/APush notification before kickoff (future)
MasteryI track my conviction qualitySupport power-user shortcutsN/ALeaderboard, season stats, export
EffortlessThe game suggests what I'd pickProactive suggestions based on historyN/AAI-assisted prediction (future)
StateSidebarDashboardSection PagesWidget
Flag OFFExisting nav unchangedNo prediction widgetsNo cross-linksHidden
Flag ON (T0)"Predictions" added after existing sectionsUpcoming matches cardN/AHidden
Flag ON (T2+)SameSame + win streak widgetCross-links to prediction historyMatch reminder floating button

Cross-Cutting Navigation

FromToTriggerWhat Happens
Homepage/predictions"Predictions" sidebar link or hero CTANavigate to match list
Match list/predictions/{matchId}Click fixture cardNavigate to match detail
Match detail/predictions/{matchId}/predictClick "Predict" buttonNavigate to prediction form (auth check)
Prediction form/predictions/mineSuccessful predictionRedirect with success toast
My predictions/predictions/{matchId}Click fixture name on prediction cardNavigate to match detail
Any pagezkLogin flowAny authenticated action when not signed inModal overlay, return to origin after auth
Predictions (new section)
├── Upcoming Matches → /predictions
├── My Predictions → /predictions/mine
└── Leaderboard → /predictions/leaderboard

Positioned after existing product sections. Rationale: prediction game is a new product vertical, not a sub-feature of existing tools.

Performance

Priority Score

DimensionScoreEvidence
Pain4Multi-transaction settlement on EVM requires 4-5 txs, wallet install, gas management. Real friction measured in the value stream map.
Demand3Rugby community is niche but passionate. Crypto prediction market demand proven (Polymarket $1B+ volume). No Sui-native prediction product exists.
Edge5PTB atomic settlement, hot potato oracle pattern, Move type safety — none available on EVM. Compiler prevents the bugs competitors write.
Trend5Onchain gaming growing. Gasless UX becoming table stakes. Google chose Sui for agent commerce. zkLogin adoption accelerating.
Conversion3Testnet dogfood with rugby community. No revenue model yet. Conversion thesis: frictionless UX converts crypto-curious sports fans.
Composite9004 × 3 × 5 × 5 × 3

Quality Targets

MetricTargetMethod
Prediction-to-settlement time<1s (single PTB)On-chain transaction timing
First prediction time (new user)<60s from landingSession recording
Oracle data freshness<5 min after match endOracle agent monitoring
Sponsored tx success rate>99%Gas station metrics

Kill Signal

Zero predictions on testnet after 30 days of deployment, OR PTB settlement requires >1 transaction. Either means the thesis is wrong for this use case.

Platform

Current State

LayerWhat ExistsWhat's Needed
Move contracts14 packages deployed to testnet (DePIN, identity, mandate)New prediction_game package (7 modules)
FrontendNext.js 15 app with dApp KitNew prediction app in NX monorepo
OracleNoneCustom agent-operated oracle + integration
Gas stationNoneSponsored transaction backend
zkLoginNone deployedzkLogin integration in new app

Build Ratio

CategoryExistingNewRatio
Move patterns14 packages, 2,500+ lines7 modules, ~1,500 lines60% reuse of patterns
FrontenddApp Kit, shadcn, Tailwind4 new pages, prediction components70% reuse of stack
InfrastructureTestnet deployment, CIGas station, oracle agent30% new

Process

Build Order

SprintFeaturesWhatEffortAcceptance
1#1-4, #14, #15Core contracts + zkLogin + sponsored tx2 weeksPrediction created on testnet via zkLogin
2#5-9Oracle + settlement + receipts2 weeksFull settlement cycle in single PTB
3#10-13, #16-17Frontend polish + history + onboarding1 weekComplete user journey from browse to settle
4#18, #12Admin tools + leaderboard + dogfood1 week50 predictions from real users

Commissioning

GateWhatEvidence Required
L0Spec existsThis document
L1Core contracts deployedgame.move, prediction.move, treasury.move on testnet
L2Settlement worksFull predict → settle → payout cycle in single PTB
L3Dogfood passed50+ predictions from real users, zero double-settlements
L4Production readyMainnet audit passed, monitoring live, gas station funded

Players

Demand-Side Jobs

WhoJobCurrent SolutionSwitching Trigger
Rugby fan (crypto-curious)Back my convictions on match outcomesInformal bets with friends, traditional bookmakersZero-friction entry (no wallet, no gas, no KYC)
Crypto nativeProve prediction skill with on-chain recordPolymarket (EVM), no Sui-native optionSui-native UX, atomic settlement, Dynamic NFT receipts
Builder (dogfood)Validate Sui settlement patterns for other productsNo working reference implementationWorking prediction game proves PTB, zkLogin, oracle patterns

Roles

RoleResponsibility
Product (Dream)PRD spec, stories, acceptance criteria
EngineeringMove contracts, frontend, oracle agent
Oracle operatorMatch data feed, result attestation
AdminManual settlement override, season management
Dogfood usersReal predictions on testnet, feedback

Context

Questions

If the prediction game proves the settlement stack works, what's the next product that inherits the patterns — and which patterns don't transfer?

  • Is rugby the right sport for dogfood, or does the niche community limit what we learn about onboarding?
  • The hot potato oracle pattern prevents double-settlement — but what happens when the oracle is wrong, not malicious?
  • If zkLogin eliminates seed phrases, what new trust assumption replaces them — and is it actually better?