PLOT TWIST turns verified football events into short, free fan challenges. TxLINE supplies the match truth, Ollama narrates the moment, deterministic rules resolve each call, and a fan can stamp the completed session as a wallet-signed Solana achievement.
The submitted demo uses a verified historical replay of Spain–Belgium
(fixtureId 18218149). It compresses the original timestamps for a judge-friendly
run while preserving event order, match minute, score, team, and TxLINE event ID.
The same pipeline can consume TxLINE's live score and odds streams in production.
The match center also loads the real World Cup fixture catalog and groups it into finished, live, and upcoming matches. Selecting a fixture replaces the demo view with every currently available TxLINE detail: score and clock, key events, 1X2 market signal, conditions, and starting lineups. Missing coverage is shown as unavailable rather than filled with generated data. The Spain–Belgium fixture remains the deterministic 60-second Judge Mode.
| Phase | Fan experience |
|---|---|
| Upcoming | Verified kickoff, teams, published lineups/conditions, and pre-match 1X2. Calls stay locked until TxLINE confirms live coverage. |
| Live | Every live fixture gets an immediate verified-state call round. Confirmed goals, cards, shots on target, and corners then trigger Ollama story headlines and fresh rounds. One free call is settled only by a same-fixture event/team or the verified clock deadline. |
| Finished | Calls close. The UI switches to final score/corners, the available TxLINE event archive, personal accuracy, XP, best streak, and wallet achievement state. |
Settled fan sessions are stored per fixture in the browser. The server filters
both score and odds streams by fixtureId, while event IDs are deduplicated on
the client so reconnects cannot award XP twice.
Most live-data products show fans more numbers. PLOT TWIST converts those numbers into a repeatable participation loop:
verified event -> narrated plot twist -> free fan call -> automatic resolution -> XP -> achievement
There is no wager, stake, payout, token, or promised financial value. The product is a second-screen engagement and loyalty layer.
- Click
JUDGE MODE, wait for the three green checks, then pressStart clean pitch. - Lock
Spain yellow cardand start the verified replay. - TxLINE resolves the call at 42′; Ollama writes the next round's headline.
- Lock
Spain shot on target; TxLINE resolves it at 60′. - Connect a Wallet Standard wallet and click
Stamp achievement on Solana. - After server-side RPC verification, open the transaction in Solana Explorer.
Closing line: AI narrates. TxLINE verifies. Solana remembers.
Hear verified match recap uses a server-side ElevenLabs voice route with
eleven_multilingual_v2 for natural match narration. The API key never reaches
the browser; if the provider or quota is unavailable, playback falls back to the
browser Web Speech API so the fan flow remains usable.
- TxLINE historical score data for Spain–Belgium (
fixtureId 18218149) - A Belgium equalizer event followed by Spain yellow-card and shot-on-target events
- Same-fixture historical 1X2 market movement around the equalizer:
4.6% -> 13.1% - Ollama Cloud (
gpt-oss:20b) narrative generation with a safe local fallback - ElevenLabs premium recap audio with a browser voice fallback
- Fixture-, team-, event-, and deadline-aware deterministic call resolution
- Wallet Standard connection and wallet-approved Solana devnet Memo transaction
- Server-side
getTransactionverification of signature, signer, Memo, fixture, event IDs, call count, and XP before the UI trusts an achievement - Readiness checks for TxLINE, Ollama, and Solana devnet
Existing verified devnet achievement: open transaction in Solana Explorer.
Ollama controls expressive copy: the emotional two-part headline. It does not decide which calls are offered, who won, the XP amount, or any match fact. The server validates the model output and replaces factual recap, market sentence, choices, deadlines, and XP with deterministic values. Unsafe or invalid output falls back to a deterministic template.
Quiet live periods use the deterministic verified-state template directly: there is no narrative event for Ollama to embellish, but fans still receive fixture- scoped cards immediately. Ollama is invoked only for confirmed match actions.
This boundary keeps the product lively without putting an LLM in the scoring path:
TxLINE score/odds payload
-> normalized MatchEvent
-> deterministic trigger + call builder
-> Ollama headline only
-> deterministic resolver + XP
-> server-side ElevenLabs recap audio (browser fallback)
-> wallet-approved Solana achievement Memo
-> server RPC verifier -> trusted achievement UI
Credentials stay server-side. The adapter obtains a short-lived guest JWT,
retries once after 401/403, sends the activation API token separately, and
normalizes upstream payloads before they reach the UI.
Exact upstream endpoints used by the project:
| Method | TxLINE endpoint | Use |
|---|---|---|
POST |
/auth/guest/start |
Obtain guest JWT |
GET |
/api/fixtures/snapshot |
Past, live, and upcoming match catalog |
GET |
/api/scores/snapshot/{fixtureId} |
Selected fixture state, key events, conditions, and lineups |
GET |
/api/scores/historical/{fixtureId} |
Submitted verified replay |
GET |
/api/odds/updates/{epochDay}/{hour}/{interval} |
Submitted historical market shift |
GET |
/api/scores/stream?fixtureId=… |
Fixture-scoped live event and settlement feed |
GET |
/api/odds/stream?fixtureId=… |
Fixture-scoped live odds adapter |
GET |
/api/odds/snapshot/{fixtureId} |
Fixture market snapshot adapter |
Internal routes such as /api/matches, /api/matches/{fixtureId}, and
/api/txline/replay/18218149 proxy and normalize those calls; browser code never
receives TXLINE_API_TOKEN or the guest JWT.
What worked well:
FixtureId,Seq, andTsmake replay ordering and cross-feed identity clear.- Historical scores make a deterministic, judge-safe demo possible.
PriceNamesplusPctpreserve participant mapping without deriving a fake probability from decimal prices.
What would improve developer experience:
- Document the lifetime and renewal contract for both guest JWT and activation token together, with one end-to-end server example.
- Return historical scores as documented JSON or document explicitly that the response uses SSE framing even though it is a finite replay.
- Include team display names in score events, or publish a canonical fixture join example for mapping participant sides to names.
- Make the documented
fixtureIdstream filter more prominent and include it in the canonical SSE example; it is essential for safe per-match fan sessions.
The Memo is a wallet-signed fan achievement, not a cryptographic proof that TxLINE itself signed the events. It contains a versioned compact record with the fixture, resolved TxLINE event IDs, calls, XP, wallet, network, and issue time.
GET /api/solana/achievement/{signature}?wallet=...&fixtureId=... calls Solana
devnet getTransaction with jsonParsed, then checks:
- the transaction succeeded and its first signature matches the requested one;
- the expected wallet is the transaction signer and matches the Memo wallet;
- the instruction belongs to the Memo Program;
- the versioned payload, fixture, event-ID prefixes, call uniqueness, and XP sum pass strict validation.
Legacy v1 achievements remain readable; newly issued achievements use v2 and the
honest wallet-signed-memo verification label. A value from localStorage is
never treated as verified until this RPC check succeeds.
PLOT TWIST is a B2B2C white-label module for broadcasters, clubs, tournament apps, and fan platforms. A partner licenses it per tournament or active fan and sells sponsored story rounds, loyalty rewards, and aggregate engagement analytics. The fan loop remains free; revenue comes from distribution and engagement rather than outcomes.
Requirements: Node.js 20+ and a Solana Wallet Standard browser wallet on devnet.
npm install
cp .env.example .env.local
npm run devOpen http://localhost:3000. Configure the server-only
TxLINE, Ollama, and ElevenLabs values in .env.local; never prefix any secret
with NEXT_PUBLIC_.
If the browser wallet needs devnet SOL, fund its displayed address from a funded CLI wallet:
solana transfer <BROWSER_WALLET_ADDRESS> 0.02 --allow-unfunded-recipient --url devnetnpm test
npm run typecheck
npm run build
npm run startThe test suite covers match phase/catalog normalization, stale fixture-state correction from live score actions, home/away score and player mapping, fixture/team/deadline-aware resolution, compact v2 Memo generation, v1 compatibility, and rejection of forged XP or cross-fixture events.
For deployment, set TXLINE_API_ORIGIN, TXLINE_API_TOKEN, OLLAMA_BASE_URL,
OLLAMA_API_KEY, OLLAMA_MODEL, and ELEVENLABS_API_KEY. The ElevenLabs voice
ID and model have safe defaults but can be overridden. SOLANA_RPC_URL is
optional and defaults to Solana's public devnet endpoint. Then open
/api/demo-readiness; all three core checks must report ready before recording
the demo, and play one recap to verify premium audio.
- Live app: https://plot-twist-six.vercel.app
- Public repository: https://github.com/ILYUTKICK/plot-twist
- Demo video: pending final Loom or unlisted YouTube upload
- 3-minute submission recording script
- Technical documentation
- Competitive interface review
- Ready-to-paste submission copy