Post a football bet in plain English. A friend swipes right to match your stake. Three on-device AI models decide who won. No company hosts the market. No company resolves it. No company takes a cut. There is no server to host it on.
All three Tether stacks are required for the core loop. Pears runs the market (Autobase, Hyperswarm, blind-peering, Hyperdrive). QVAC runs the brain (LLM jury, TTS readback, RAG context). WDK runs the money (self-custodial wallets, EIP-3009 gasless staking, ecrecover settlement). Pull any one out and the app stops working.
The on-device AI jury is enforced on-chain. Three peers grade each bet with local models. Two matching WDK signatures release the pot. Escrow.settle verifies them with ecrecover. No oracle. No admin. The contract is the final word.
Gasless staking means the joiner never touches ETH. They sign a TransferWithAuthorization off-chain. A facilitator submits the tx and pays the gas. It is the same key that signs the stake and the verdict.
| Claim | Proof without running the app |
|---|---|
| All three stacks required | Tech stack table: each row names what breaks without it |
| On-device jury is real | npm run jury:demo → 5/5 (after clone) |
| Create pot on-chain | tx 0x98e6dab9… |
| Join pot on-chain | tx 0x9e775918… |
| Settle 2/3 ecrecover | tx 0x403b1dec… |
| Gasless USDT deployed | 0x41fD72…: PuntUSDT (EIP-3009) on Base Sepolia |
| 7 ADRs | docs/adr/: every load-bearing choice documented |
| Judge review guide | docs/PERMALINKS.md: 5-minute walk with pinned code links |
| CI green | badge above |
Judge walk-through: docs/judge-in-5-minutes.md. Live demo: npm run demo:judge. One command: npm run demo:all.
Marketing site. The app runs as a local Electron peer demo (see Testing the App).
| Home | Composer | History | Profile |
|---|---|---|---|
![]() |
![]() |
![]() |
![]() |
Every prediction app is a platform. A company hosts the markets. A company settles the bets. A company takes a cut.
Punt is the version a platform cannot be. Bets replicate peer-to-peer over an Autobase feed. Stakes sit in self-custodial WDK wallets escrowed on-chain. Three peers each running a language model on their own machine decide who gets paid. Built for the Tether Developers Cup on all three Tether stacks.
Compose a bet in plain English. Type it or say it. Whisper transcribes your voice on-device. Silero VAD knows when you stop talking. A local Llama 3.2 1B streams the structured terms into the composer as it writes them, and flags anything it had to guess. On-device TTS reads the bet back so you hear it before you stake.
Swipe to stake. The home screen is a card stack of open bets from every peer in your group. Swipe right to match the stake with real testnet USDT. Swipe left to skip.
Stake without ETH. The joiner signs an EIP-3009 TransferWithAuthorization off-chain with their WDK key. A funded facilitator pays the gas. The joiner's wallet never touches ETH.
No server. No hardcoded address. Peers find each other on the Hyperswarm DHT from the feed key. Jurors use a separate blind-peering DHT topic per bet. They learn about only the bet they grade. Everything else stays private.
Encrypted blocks. Every block is encrypted with a group secret. Knowing where the group meets is not enough to read the pots. You hand the secret to your friends out of band.
Fixed stakes. No bookmaker. No odds. Two-sided pots between friends. Winner takes the pot. Nobody takes a cut.
AI jury with RAG. Three jurors grade each bet against football-data.org results. EmbeddingGemma retrieves similar past verdicts and injects them as few-shot examples. Each juror runs a local Qwen3 4B at temperature 0 with grammar-constrained JSON output. Every verdict is signed by the juror's WDK key. Two matching signatures release the escrow.
Anti-spoofing at the protocol level. The feed runs a deterministic reducer with four invariants. Junk is dropped before the writer gets acked. Bets are accepted only when the author key matches the sending peer. Spam and impersonation never reach anyone.
Peers you can recognize. keet-identity seeds survive restarts. The stack HUD shows each peer's fingerprint.
Evidence that doesn't hammer an API. Match results are cached P2P over Hyperdrive. Later jurors pull from peers, not football-data.org. The API is fallback only.
Everything is WDK-native. Stakes, escrow calls, jury verdicts, and EIP-3009 authorizations all sign with the same WDK key.
Metrics without the cloud. Prometheus on 127.0.0.1:9090. Peer count, open bets, verdicts, jury latency, LLM load time, gasless stake count.
| Layer | Technology | What it does |
|---|---|---|
| Bet feed | Pears (Autobase, Corestore, Hyperswarm, blind-peering, Hyperdrive, hyperblobs, keet-identity-key) | Peers discover on the DHT by feed key. Replicate an encrypted multi-writer Autobase. The view is a deterministic reducer that binds each bet to its author. Jurors use blind-peering for a separate DHT pool per bet. Match evidence is cached P2P over Hyperdrive. Each piece is structural. |
| AI | QVAC (@qvac/sdk): Llama 3.2 1B, Whisper base.en, Qwen3 4B, EmbeddingGemma, Supertonic TTS, Silero VAD |
Speech-to-bet, streaming parse, jury grading at temp 0 with RAG, bet readback, push-to-talk. All on-device. Verify with npm run jury:demo. |
| Wallets | WDK (@tetherto/wdk-wallet-evm) |
Stakes, escrow calls, jury verdicts, and EIP-3009 gasless authorizations all flow through WDK. |
| Escrow | Solidity 0.8 on Base Sepolia | Fixed-stake pots keyed by bet hash. Released by 2-of-3 jury ecrecover. |
| Gasless token | PuntUSDT.sol (EIP-3009) on Base Sepolia | Joiner signs off-chain. Facilitator pays gas. Deployed at 0x41fD722Bc53426fA2a13b42a3dDF82569E870374. |
| Shell | Electron | Phone-shaped desktop window. P2P and AI run in a separate Node daemon. |
Reviewing for the cup? Judge in 5 minutes is a reproducible path with pass/fail gates. Most gates work with no wallet.
- Install Node 22+, clone the repo, run
npm install --registry=https://registry.npmjs.org. - Run
node scripts/fund-wallets.js. Five wallets land in a gitignored.env. - Fund CREATOR and JOINER with Base Sepolia ETH (faucet). Jurors never need gas.
- Run
node scripts/deploy.jsto deploy MockUSDT and Escrow, then mint 100 USDT to each player. - Get a free football-data.org key, add
FOOTBALL_DATA_KEY=<key>to.env.
- Run
npm run demo:judgeornpm run demo. Two phone-shaped windows open. Three juror processes print to the terminal. A checklist shows up. - The stack HUD under the ticker should show: Pears (peers, dht, enc, identity fingerprint), QVAC (AI ready), WDK (balance or last tx).
- On the creator phone, hit
+and type a bet on a finished real match. Example: "Morocco beat Ecuador yesterday, 4 on it." The local model reads it back as structured terms. Press POST. Your stake locks in the escrow. - The bet appears on the joiner's card stack. It arrived over P2P replication, not through a server.
- Swipe right on the joiner. Their stake locks. The pot holds both stakes on-chain.
- Watch the terminal. Each juror fetches the result, runs RAG for similar past verdicts, grades the bet with its own local model, and prints a signed verdict.
- The winner's daemon collects two matching signatures and settles. A toast shows Settled on-chain · 2 of 3 QVAC jurors · WDK · ecrecover with a Basescan link. The winner's USDT balance jumps by the full pot.
If venue Wi-Fi blocks the DHT: PUNT_FEED_LOCAL=1 npm run demo:judge falls back to localhost TCP.
Set PUNT_GASLESS=1 in .env and run node scripts/facilitator.js alongside the peers. The joiner signs an EIP-3009 authorization. The facilitator relays it with its own ETH.
Run node scripts/junk-check.js. A peer appends two junk messages and one valid bet. Only the valid bet reaches the feed.
| Contract | Description |
|---|---|
Escrow.sol |
One pot per bet, keyed by the bet's canonical hash. Creator stakes on create. One joiner counter-stakes. 2-of-3 juror signatures release the pot. Timeout refunds both sides. |
MockUSDT.sol |
Six-decimal test USDT with open faucet mint. Used by the existing Escrow. |
PuntUSDT.sol |
EIP-3009 USDT for gasless staking. No external deps. transferWithAuthorization lets joiners stake without ETH. Deployed at 0x41fD722Bc53426fA2a13b42a3dDF82569E870374. |
Escrow and MockUSDT are deployed on Base Sepolia. PuntUSDT is deployed separately for the gasless path. Addresses land in .env from the deploy scripts.
creator phone joiner phone
(Electron) (Electron)
| |
peer daemon A <----- Autobase feed -----> peer daemon B
(feed+WDK+LLM) optimistic replication (feed+WDK+LLM)
| |
| create pot join pot |
+-----------------+ +---------------------+
v v
Escrow contract (Base Sepolia)
^ ^
| | settle (2-of-3 signatures)
+--------+ +--------+ +--------+
| juror1 | | juror2 | | juror3 | each: own Corestore,
+--------+ +--------+ +--------+ own local LLM, own key,
| | | RAG context, blind-peering
+------------+------------+ pool per bet, Hyperdrive
| evidence cache
football-data.org (fallback only)
Peers find each other on the Hyperswarm DHT using the feed's discovery key. No address to configure. Jurors use blind-peering to discover each other on a per-bet DHT topic. A bet is appended optimistically to the shared Autobase. The apply function is a deterministic reducer: it validates the schema, checks that the bet's author key matches the sending peer, and only then acknowledges the writer. Junk and impersonation never converge. Verdicts travel over the same feed as messages signed by each juror's WDK account. The escrow verifies those signatures on-chain with ecrecover. The contract is the final arbiter of who gets paid.
Trust assumption: honest-majority across the three jurors. Collusion resistance beyond that is out of scope.
Disclosed services: football-data.org for match results, a Base Sepolia RPC endpoint, and an optional local facilitator for gasless staking. Everything else (AI inference, evidence caching, peer discovery) runs on the peers' machines.
Every load-bearing decision is documented in docs/adr/. A commit-pinned review guide is at docs/PERMALINKS.md.
git clone https://github.com/ajanaku1/punt.git && cd punt
npm install --registry=https://registry.npmjs.org
node scripts/fund-wallets.js # generate wallets into .env
# fund CREATOR + JOINER with Base Sepolia ETH, then:
node scripts/deploy.js # deploy MockUSDT + Escrow, mint test funds
npm test # 68 tests (escrow tests need foundry's anvil)
npm run demo # the full two-phone, three-juror demoProofs, each one command:
npm run demo:all # one command: Pears feed, QVAC jury, WDK stake
npm run jury:demo # Qwen3 4B grades tricky fixtures on-device
npm run coverage # 68 tests with coverage
node scripts/p2p-check.js # two-process replication proof
node scripts/junk-check.js # spam rejection proof
node scripts/join-check.js # WDK wallets fund a pot on-chain
node scripts/facilitator.js # EIP-3009 gasless sponsor
npm run peer:creator # one peer daemon, headless
npm run juror1 # one juror, headlesspunt/
contracts/ Escrow.sol, MockUSDT.sol, PuntUSDT.sol (EIP-3009)
packages/
shared/ bet schema, verdict signing, WDK helpers, identity, LLM (TTS + VAD)
feed/ Autobase feed, DHT swarm, blind-peering, evidence Hyperdrive
juror/ grading prompts, football-data client, RAG, juror daemon
app/ peer daemon, Electron shell, swipe UI
relay/ optional relay for NAT traversal
scripts/ fund-wallets, deploy, compile, facilitator, metrics, demo-all, jury-demo, proofs
tests/ 68 node:test specs (schema, feed, escrow, parse, verdicts, WDK, DHT, convergence, edge cases)
docs/
adr/ 7 architecture decision records
PERMALINKS.md commit-pinned code review guide
CODE_REVIEW.md companion narrative for judges
judge-in-5-minutes.md pass/fail walk-through
.github/workflows/ CI: contract compile, tests, coverage
docs/images/ UI screenshots
MIT



