An on-chain World Cup, settled by code
GoalRush is an on-chain prediction game for the 2026 FIFA World Cup, built on Ethereum. You back a match outcome — HOME, DRAW or AWAY — with the $GOAL token. Everyone's stakes flow into one shared pot. When the match is resolved on-chain, everyone who called it right splits that pot in proportion to what they staked.
There is no operator setting odds and no company holding your money. This is parimutuel betting: players bet against each other, not against a house. The token, the pots, the payouts and your track record all live in contracts that run the same way for everyone — verifiable on any block explorer.
The thesis
A prediction game needs a chip, and that chip needs demand. GoalRush ties the two together: $GOAL is the only currency you can bet with, so wanting to play is wanting to hold $GOAL. The loop — buy, stake, win, rebuy — creates structural demand for the token across the entire tournament.
Why Ethereum + Uniswap V4
- Settlement you can audit. Stakes, results and payouts are public transactions. No trust in an off-chain ledger, no withdrawal gate.
- Uniswap V4 hooks. V4 lets a pool call custom contract logic at key moments of a swap. GoalRush uses that to mint your passport on your first buy and to discount your fee as you climb the tiers — natively, inside the trade, with nothing to redeem.
- One liquid venue. A single canonical ETH/$GOAL pool keeps price discovery and the fee mechanic in one place the protocol recognises.
$GOAL — the only chip on the table
$GOAL is a fixed-supply ERC-20 token on Ethereum (GoalRushToken). It is the only currency you can bet with: every stake, pot and payout is denominated in $GOAL.
The entire supply is minted once, at deployment. There is no minting switch, no inflation, no founder faucet topping it up later — the contract has no mint or burn path after construction. That fixed cap is what makes the demand loop meaningful: new demand cannot be diluted away.
You acquire $GOAL by swapping ETH for it through the canonical Uniswap V4 pool. That swap is also where your reputation starts working for you — see the hook below.
The Predictor Passport
Your Predictor Passport is a soulbound ERC-721 (PredictorNFT) — it lives in your wallet and cannot be sold, sent or transferred. There is exactly one per wallet, and it is minted automatically by the V4 hook on your first qualifying buy. No claim step, no second transaction.
The passport keeps your record on-chain. Every stat is stored in the contract and read live on every call:
- bets — total stakes you have placed
- wins / losses — settled outcomes
- currentStreak — consecutive wins, resets on a loss
- bestStreak — your peak run
From those numbers the contract derives your tier on the fly — it is never stored, always computed — and that tier is exactly what the hook reads to set your swap fee.
Fully on-chain art
The passport's image is not hosted anywhere. The contract's tokenURI builds the entire card as a base64 SVG embedded in a base64 JSON data URI — a dark trading-card with your tier frame, address, stat grid and POWER / swap-fee footers. It renders in any wallet or marketplace with no server in the loop.
The hook that rewards the sharpest
The ETH/$GOAL pool is a Uniswap V4 pool with a custom hook (GoalRushHook). Hooks are contracts the pool calls at specific moments of its lifecycle. GoalRush wires three of them.
Fires once, when the pool is created, and locks the canonical pool. It strictly validates the key: currency0 == ETH (address 0), currency1 == $GOAL, the fee is the DYNAMIC_FEE_FLAG, and tickSpacing == 60. Any other pool key reverts, so no impostor pool can shadow the real one. It then seeds the default Bronze fee (1.00%).
On a qualifying buy — ETH → $GOAL (zeroForOne) with at least minBuyEth in (default 0.01 ETH) — if the buyer has no passport yet, the hook mints one. The mint is wrapped in try/catch so it can never brick a swap.
Before each swap, the hook reads the trader's passport, derives their tier, and returns a dynamic fee override (fee | OVERRIDE_FEE_FLAG). Better predictors swap cheaper — enforced by the pool itself, with nothing to redeem.
The fee identity is always the signing EOA (tx.origin); an explicit hookData address is only honoured when it equals that EOA. A swapper can never pass a Diamond holder's address to borrow their discount.
| Tier | Fee | Pips | Unlock |
|---|---|---|---|
| Bronze | 1.00% | 10000 | Default — every passport starts here |
| Silver | 0.85% | 8500 | ≥ 5 bets and winrate > 40% |
| Gold | 0.75% | 7500 | ≥ 15 bets and winrate > 50% |
| Diamond | 0.50% | 5000 | ≥ 30 bets, winrate > 55% and streak ≥ 5 |
V4 fees are in pips (1e6 = 100%). Winrate is measured over settled bets (wins + losses), so you cannot reach a tier by staking without ever resolving.
Parimutuel — no house, no edge
There is no bookmaker setting prices against you. Every stake on a match flows into one shared pot inside PredictionGame. You stake $GOAL on HOME, DRAW or AWAY; hedging is allowed (stake more than once, or on more than one outcome — stakes accumulate). Betting closes the moment kickoff passes (block.timestamp ≥ kickoff).
After kickoff the oracle posts the result. A 5-minute settle / correction window then opens before any payout or stat is frozen: the owner can correct a misposted result, but only while no one has settled or claimed yet. After it elapses, winners can claim and anyone can settle.
The payout formula
payout = totalPot × yourStake ÷ winnerPot claim = payout − 2% protocol fee → treasury
Integer division leaves negligible dust captive in the contract, which stays solvent. The fee is waived when winnerPot == totalPot (everyone won) and on all refunds.
The 2% protocol fee
The only deduction is a flat 2% fee taken on each winning claim (200 bps), sent to the treasury. There is no spread, no vig, and no house balance that profits when you lose. The fee is waived when every bettor won (so your own stake is never taxed) and on every refund.
Edge cases
Tiers & how POWER is scored
Tiers are the on-chain ladder that drives your swap fee. They are derived live from your settled record — Bronze by default, up to Diamond. Climbing requires both volume (a minimum bet count) and accuracy (a winrate threshold), and Diamond also demands a hot streak.
Diamond bets ≥ 30 && wins·100 > 55·settled && streak ≥ 5 Gold bets ≥ 15 && wins·100 > 50·settled Silver bets ≥ 5 && wins·100 > 40·settled Bronze otherwise (the default)
POWER score
POWER is a single headline number for the leaderboard and your profile. It is computed off-chain on the front end from your on-chain stats (and mirrored in the passport SVG) — deterministic, never stored, and zero for a fresh wallet.
POWER = wins × 10
+ bestStreak × 15
+ currentStreak × 5
+ floor(winratePct)wins·10 = productive output · bestStreak·15 = peak form (rarest) · currentStreak·5 = live 'on fire' bonus · winratePct = accuracy tiebreaker (0–100).
A match, end to end
Every match moves through a small set of states. The happy path is OPEN → CLOSED → RESOLVED → claims; the escape hatches are CANCELLED and the timeout-driven emergency refund.
- OPENBets accepted while block.timestamp < kickoff. Each stake escrows your $GOAL and bumps your passport's bet count.
- CLOSEDKickoff has passed; no more stakes. Awaiting the oracle's result.
- RESOLVEDOracle posted the result via setResult. The 5-min correction window runs, then settles and claims open.
- CANCELLEDCancelled before resolution by owner/oracle. Everyone refunds their full stake, no fee.
Four contracts, one game
GoalRush is four smart contracts working together, wired into the Uniswap V4 PoolManager. Each does one job, in the open.
- GoalRushToken
- The fixed-supply ERC-20 $GOAL token — the unit of account for the whole game. Standard, transferable, verifiable on-chain.
- PredictorNFT
- The soulbound Predictor Passport. Records bets, wins, losses and streak, and derives your tier. One per wallet, minted by the hook. Stats are written only by the game (the
gamerole); mints only by the hook (thehookrole). - PredictionGame
- The parimutuel engine. Holds matches, escrows stakes, locks betting at kickoff, records results, settles win/loss onto passports, and pays winners their proportional share minus 2%.
- GoalRushHook
- The Uniswap V4 hook on the ETH/$GOAL pool. Locks the canonical pool, mints the passport on a first buy, and overrides the swap fee by tier. Deployed via HookMiner + CREATE2 so its address encodes its permission flags.
Security & trust model
- Non-custodial & on-chain. The contract escrows stakes only for the duration of a match; payouts use a pull pattern with a reentrancy guard and SafeERC20. Every stake, result, claim and refund is a public transaction.
- Soulbound integrity. Passport transfers revert and approvals are no-ops, so reputation can't be sold or laundered. Bet count is bumped at stake; settles are permissionless and idempotent, and a settle can only run for an actual participant — nobody can brand a phantom loss on a wallet that never bet.
- Oracle + correction window. A dedicated oracle role posts results (separate from the owner). A misposted result can be corrected only inside the 5-minute window, and only before any settle, claim or no-winner refund — each of which permanently locks the result.
- Bounded admin power. The settle delay is owner-settable but hard-capped at 1 hour, so the owner can never indefinitely stall claims. The passport's hook/game roles can be permanently frozen via
freezeConfig. - Frozen config & anti-brick. The canonical pool key is locked on first init; impostor pools revert. Hook callbacks never revert in normal operation — mint failures fall through and fee reads fall back to Bronze — so a bad state can't freeze trading.
FAQ & glossary
Do I need the passport to bet?+
Why does my wallet still show my old tier on the card?+
What happens if nobody bets the winning outcome?+
Can the result be changed after the match?+
Is there a house edge?+
What if the oracle never posts a result?+
Glossary
- Parimutuel
- A betting model where all stakes go into a shared pot and the winners split it among themselves — players bet against each other, not against a bookmaker.
- Soulbound
- A non-transferable NFT bound to one wallet. It can be minted but never sent, sold or approved away.
- Dynamic fee
- A Uniswap V4 pool fee that isn't fixed — the hook sets it per swap via an override flag. Here it's set by the trader's tier.
- Hook
- A contract a V4 pool calls at set points (init, before/after swap). GoalRush's hook mints passports and overrides fees.
- Tier
- The Bronze → Diamond rank derived live from your settled record; it determines your swap fee.
- POWER
- A front-end-only headline score from your stats (wins, streaks, winrate) used to rank the leaderboard. Not stored on-chain.
Contracts on Ethereum
The deployed contract addresses. Mainnet values are published here at launch — we do not list addresses that don't exist yet.
- GoalRushTokenFixed-supply $GOAL ERC-200xD975…21dA
- PredictorNFTSoulbound Predictor Passport0x75E2…018A
- PredictionGameParimutuel betting engine0x9df5…C7fc
- GoalRushHookV4 mint + dynamic-fee hook0x554f…10c0
Showing the current Sepolia testnet deployment.
Read it, then go call the tournament.
The rules are simple and the math is in the open. Mint your Predictor on your first buy and start staking.
