Documentation

How GoalRush runs on Ethereum.

GoalRush is a World Cup 2026 prediction game that lives entirely in smart contracts. No bookmaker, no house edge, no backend deciding who wins. This page is the full reference — the token, the soulbound passport, the Uniswap V4 hook, the parimutuel math, the tiers, the match lifecycle, and the four contracts behind it all.

Overview

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.

The $GOAL demand loop$GOALonly currencyBUYETH → $GOALSTAKEback an outcomeWINsplit the potREBUYredeploy gainsFixed supply + the only betting chip = structural demand for the whole World Cup.
The $GOAL demand loop — the only betting chip means playing is holding.

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.
The currency

$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.

1,000,000,000
total supply, 18 decimals
0
mint / burn after deploy
100%
minted at construction

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.

Reputation

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.

Uniswap V4

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.

afterInitialize

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%).

afterSwap

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.

beforeSwap

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.

Buy to passport mint flowWalletconnects + swapsV4 PoolETH → $GOALafterSwaphook callbackPassportminted$GOALin walletETH inif first buy &&ethIn ≥ minBuyEthONE TRANSACTION — NO SEPARATE CLAIMsoulbound · 1 / wallet
Buy → mint: a single transaction swaps ETH for $GOAL and mints the soulbound passport.
Tier-based dynamic fee overrideSwap beginsbeforeSwap firesRead PassporttierOf(trader)Fee overridefee | OVERRIDE_FEE_FLAGTIER → FEEBronze1.00%Silver0.85%Gold0.75%Diamond0.50%Fee identity is the signing EOA (tx.origin) — nobody can borrow another wallet's discount.
beforeSwap reads your tier and overrides the pool fee, Bronze → Diamond.

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.

TierFee
Bronze1.00%
Silver0.85%
Gold0.75%
Diamond0.50%

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.

The pots

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

Winning claim
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.

Parimutuel pot and payout mathHOME3,000DRAW3,000AWAY4,000TOTAL POT10,000 $GOALORACLE RESULTAWAY winsPAYOUT FORMULApayout = totalPot × yourStake ÷ winnerPotthen − 2% protocol fee (waived if everyone wins, or on refunds)WORKED EXAMPLEstake 1,000 on AWAY10,000 × 1,000 ÷ 4,000 = 2,500− 2% → 2,450 $GOALCLAIM 2,450 $GOALpull-pattern · once per match
Stakes pool, the oracle posts a result, the winning pot splits the whole pot pro-rata, then claim minus 2%.

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

Reputation

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.

Tier progression ladderBronzeDefault — every passport starts here1.00%Silver≥ 5 bets · winrate > 40%0.85%Gold≥ 15 bets · winrate > 50%0.75%Diamond≥ 30 bets · winrate > 55% · streak ≥ 50.50%SHARPER · CHEAPER SWAPS
The tier ladder — Bronze → Diamond, with the exact on-chain unlock requirements.
On-chain tier rule (winrate over settled = wins + losses)
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
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).

State machine

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.

Match lifecycle state machineOPENbets acceptedCLOSEDkickoff passedRESOLVEDresult postedClaimswinnerskickoffsetResult5-MIN SETTLE / CORRECTION WINDOWowner may correct before any settle/claimCANCELLEDfull refund, no feecancelMatchEMERGENCY REFUND7 days, no resulttimeoutNO-WINNER REFUNDwinnerPot = 0 → pro-ratarefundKnockout matches have no DRAW outcome — HOME / AWAY only (who qualifies after ET/penalties).Group matches are created in batch up front; knockouts are created as qualifiers become known.
Match lifecycle — the happy path plus the cancel and refund escape routes.
  1. OPENBets accepted while block.timestamp < kickoff. Each stake escrows your $GOAL and bumps your passport's bet count.
  2. CLOSEDKickoff has passed; no more stakes. Awaiting the oracle's result.
  3. RESOLVEDOracle posted the result via setResult. The 5-min correction window runs, then settles and claims open.
  4. CANCELLEDCancelled before resolution by owner/oracle. Everyone refunds their full stake, no fee.
The contracts

Four contracts, one game

GoalRush is four smart contracts working together, wired into the Uniswap V4 PoolManager. Each does one job, in the open.

GoalRush architecture mapUNISWAP V4PoolManagercanonical ETH / $GOAL poolGoalRushHookafterInit · beforeSwap · afterSwapPredictorNFTsoulbound passport · stats · tierPredictionGamematches · pots · settle · claimGoalRushToken$GOAL · fixed supply ERC-20Treasuryreceives the 2% claim feeV4 callbacksmintFor · tierOfreads tierrecordBet · recordResult$GOAL escrow2% fee
System map — the four contracts and how they connect to the V4 PoolManager.
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 game role); mints only by the hook (the hook role).
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.
Trust

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.
Quick answers

FAQ & glossary

Do I need the passport to bet?+
No. The passport is minted by the hook on a qualifying buy, but staking is decoupled from it. If you acquired $GOAL another way (a plain transfer, a sub-minimum buy), you can still stake — that bet simply goes unrecorded on a passport you don't hold.
Why does my wallet still show my old tier on the card?+
The data is live — any on-chain read is current. The rendered image updates on your wallet or marketplace's metadata cache refresh, which is not instant. Your real stats and fee already reflect the change.
What happens if nobody bets the winning outcome?+
The pot is fully refundable: everyone reclaims their stake pro-rata with no fee, and the match can't be settled into wins or losses.
Can the result be changed after the match?+
Only within the 5-minute correction window and only by the owner, before anyone settles, claims or takes a no-winner refund. After that the result is permanently committed.
Is there a house edge?+
No. Players bet against each other. The only deduction is a flat 2% on winning claims, waived when everyone won and on all refunds.
What if the oracle never posts a result?+
Seven days after kickoff, anyone can call emergencyRefund and take their stake back. The match then locks permanently into refund mode.

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.
Reference

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.