Most prediction products ask you to come to them: download the app, make an account, fund a balance. The World Cup’s actual attention surface is nowhere near that — it’s the social feed, mid-match, thumb already moving. StreakBlink is a Solana Blink that puts a next-goal prediction market directly inside that feed: a fan sees “Next goal — Spain or Argentina?”, taps, signs with a wallet they already have, and is back to scrolling in five seconds. It’s one of six entries filed tonight for the Superteam Earn × TxODDS World Cup hackathon, in the Consumer and Fan Experiences track, built on TxODDS’s live TxLINE scores feed — all part of PulsePlay, our family of open sports-prediction tools.
The pitch has a neat trick underneath it: there is no database. Every pick is a real signed transaction — a 0-lamport transfer plus an SPL memo ({ qid, pick, wallet }) — referencing a single public board account. Because every prediction points at the same account, one getSignaturesForAddress call discovers the entire history of picks. Streaks and the leaderboard aren’t stored anywhere; they’re recomputed live from chain data every time, which means anyone can independently verify them — nobody has to trust the app’s word for who’s on a streak.
That architecture is also why tonight was a real test and not a demo. The project runs two games side by side: a 90-second synthetic practice loop (clearly labeled everywhere as replayed, so judging rounds are reproducible), and a live market wired straight to the actual World Cup Final. Tonight, for the first time, the live market ran against a real match end to end — and partway through, the team made a call a demo never has to make: move the live market off devnet and onto Solana mainnet, during the game.
What actually happened, in order
The team’s own /story page on the live site narrates this with real transaction signatures, and the git history backs it up commit by commit — so here’s what’s verifiable, not remembered:
- Pre-kickoff, devnet. The Final market went live on devnet first, same cluster as the practice game, with real signed transactions before the whistle.
- A status field that lied. The live TxLINE feed’s
GameStatefield stayed"scheduled"for the entire match — it never once said the game had started. The real signal turned out to be a different field,StatusId(1 = pre-kickoff, 2 = live, 3 = halftime), confirmed against the actual kickoff transition and fixed on the spot. - A false-positive goal. Goal detection matched any feed action containing the string “goal” — which briefly caught
goal_kick, a routine goalkeeper restart, not a score. Caught and excluded live, mid-match. - A VAR-overturned goal, tracked correctly. When the feed flagged a goal for review, the overturn wasn’t a keyword on that same record — it showed up as a separate later record, action
action_discarded, matched back to the original goal by an internal ID. A same-record keyword scan could never catch that; the fix (commit42f5816) walks the whole feed for discarded IDs before building the scoreline, and the team cross-checked the correction against an independent live broadcast before shipping it. (The real Final tonight had more than one goal disallowed by video review before the final whistle — this is the kind of feed behavior that made getting each one right non-trivial.) - Halftime: the pivot. At halftime, the live market moved from devnet to real Solana mainnet — decided and shipped during the event itself, not planned weeks out. The first pick to clear end-to-end on the new cluster is a real, checkable mainnet transaction.
- A market that flapped, and the fix. The public market briefly served three different scorelines within about ten minutes — not a caching bug (headers already said
no-store), but the free-tier TxLINE snapshot endpoint answering near-simultaneous requests from different backend nodes at different lag. The fix (commitb268de4) fetches the snapshot three times in parallel and unions every record seen, deduplicated by ID, sequence, action, and timestamp — since goal detection only ever gains information as more records are seen, merging is safe without needing any storage of its own. - An honest failure mode for empty wallets. A wallet with nothing in it to cover the network fee (predictions cost 0 lamports, but a transaction still needs gas) was failing with Dialect’s generic “signing failed” and no explanation. A pre-check now catches that case and returns a plain-language message — “your wallet needs a small amount of SOL to cover the network fee — the pick itself is free” — instead of a dead end.
- Full time. The match ended with the feed’s real
game_finalisedaction — again, not theGameStatefield, which stayed"scheduled"right through the final whistle. Spain won it 1–0, the goal standing after review, the result settled in extra time.
Every one of those fixes shipped during the live match, against the real feed, with the market open to real picks the whole time. Nothing here is asserted from memory — the code comments cite what was observed and when, down to the transaction signatures and commit hashes.
Why the honesty rails matter more than the polish
The team’s own framing is the right one: a match-day demo can fake a scoreboard. It can’t fake a wallet failing to sign live and a fix landing before the next goal. What StreakBlink actually proves is narrower and more interesting than “we built a betting app” — it’s that a market with zero stored state, resolved by a live third-party feed with no guarantees about consistency, can stay honest under real conditions: void a pick rather than guess at it, merge conflicting data instead of trusting whichever server answered first, and tell a user the truth about why a transaction failed instead of hiding behind someone else’s generic error.
The demo game still runs its own separate, clearly-labeled replay loop — nothing about tonight changes how that works, and no stakes or payouts exist anywhere in the product; a streak is bragging rights, not money. But the live Final market spent the second half and extra time of a real World Cup Final taking real transactions from real phones, and every fix it needed along the way is sitting in the commit history for anyone to check.
Try it: streakblink.vercel.app · the full story with transaction receipts: streakblink.vercel.app/story · source: github.com/0xPulsePlay/streakblink
← all posts