Back a side. Get paid in what it is made of.
A duel is a head-to-head question with a date on it: "Will Apple be more valuable than Nvidia on December 31, 2026?" Two sides, YES and NO, each fully backed by USDC. Bet Apple and the fee on your trade is collected in tokenized Apple stock and paid to everyone holding the Apple side. Bet Nvidia, get paid in Nvidia. On the date, Pyth reads both prices, the winner redeems for one USDC a token, and the loser keeps the shares it was paid. One small Anchor program plus Meteora DAMM v2, Pyth, and xStocks.
mint_settakes 1 USDC and mints 1 YES plus 1 NO;merge_setreverses it.- YES trades in a Meteora DAMM v2 pool against AAPLx, NO against NVDAx. Both pools collect their fee in the stock token only.
- A crank claims each pool's fees, deposits them into that side's reward vault, snapshots holders, and pays out pro-rata in the stock token.
- After
resolve_ts, anyone callsresolvewith posted PythPriceUpdateV2accounts. Price times stored share count decides it. Winning tokensredeemfor one USDC each.
Details: docs/ARCHITECTURE.md.
| Path | What | Status |
|---|---|---|
programs/duel |
Anchor 0.32.1 program, ID AN2TEyFH3zCsv5MENn2uo9LJx69J2EUC8iScAVeDbW25 |
Built in WSL; 39 mocha and 10 unit tests pass |
packages/sdk |
TypeScript client: program, pools, Pyth, routing, odds, rewards, registry | Verified on localnet |
scripts |
validator, bootstrap, bet, crank, resolve, faucet, smoke |
Verified on localnet |
apps/web |
Next.js 15 app: landing, board, duel, create, positions, how it works | Complete; chain adapter in integration |
docs |
Architecture, pitch, submission, prior art | Written |
Rust and Solana tooling in WSL Ubuntu; Node 22 and pnpm 9 on Windows. Cold start on localnet:
pnpm install
# terminal 1: validator, mainnet clones of DAMM v2 and Pyth, duel preloaded
pnpm --filter scripts validator -- --run --rpc-port 8999
# terminal 2: mock mints, pools, three duels, one resolvable test market
export RPC_URL=http://127.0.0.1:8999
pnpm --filter scripts bootstrap -- --with-test-market
# terminal 3: web app in chain mode
NEXT_PUBLIC_DEPLOYMENT=localnet NEXT_PUBLIC_RPC_URL=http://127.0.0.1:8999 pnpm --filter versus-web dev
# terminal 2, after a few bets: pay holders
pnpm --filter scripts crank -- --onceOpen http://localhost:3000. On localnet and devnet the wallet picker offers a Burner test wallet, no extension needed; the rail faucet mints mock USDC. scripts/README.md has the full CLI run-through: bet, sell, ledger, resolve --manual, redeem. Stop the validator with wsl -d Ubuntu -- pkill -f solana-test-validator.
Demo mode. NEXT_PUBLIC_DEMO=1 pnpm --filter versus-web dev renders every route from fixtures with ticking odds and no network. Default on a fresh checkout.
Every push deploys through GitHub Actions (.github/workflows/deploy.yml) with one shared Vercel token, so any collaborator's commit deploys, not only the Vercel project owner's. main goes to production; any other branch gets a preview URL in the run summary.
One-time setup by the Vercel project owner, who also needs admin on this repo:
vercel login
vercel link # at the repo root; writes .vercel/project.json (gitignored)
gh secret set VERCEL_ORG_ID -R aayusha59/Versus # paste orgId from .vercel/project.json
gh secret set VERCEL_PROJECT_ID -R aayusha59/Versus # paste projectId from .vercel/project.json
gh secret set VERCEL_TOKEN -R aayusha59/Versus # paste a token from vercel.com/account/tokensThen disconnect the Git integration in the Vercel project (Settings, Git, Disconnect) so Vercel stops deploying alongside Actions. Env vars stay in the Vercel project settings; the workflow pulls them at build time.
- Program built and tested (39 mocha, 10 unit); IDL committed to
packages/sdk/idl. - SDK and scripts verified end to end on localnet (Agave 3.0.13, Sept 26, 2026).
- Web app complete, with a chain adapter and Burner wallet.
- Deployed on devnet: the program, three duels, six outcome pools, and test markets.
packages/sdk/deployments/devnet.jsonlists them. A Bitcoin vs Ethereum test market has been settled through Pyth on devnet, and another is left open for the demo:
RPC_URL=https://api.devnet.solana.com pnpm --filter scripts resolve -- --market 5AhqSroPQ2uZ7fB23huAzeFPfpeB22Hok8odV1DF9hE5- The operator wallet owns the Meteora LP positions and claims their fees; duels created in the web app hand their positions to it.
- Rewards accounting is off-chain; the payout is on-chain through
distribute, which checks the crank key and the vault balance. resolve_manualby the resolver afterresolve_ts + grace_secsis the fallback for stale feeds.- Hermes now requires an API key (
PYTH_API_KEY), and the key must be entitled to each feed. Crypto feeds settle through plainresolve; US equity feeds need a plan that includes them, otherwiseresolve --manual. - xStocks are mocked on devnet and localnet; the registry maps each mock to its mainnet mint.
- Share counts for
CapCompareare placeholders set at creation.
Upgrade path for each: docs/ARCHITECTURE.md §8.
docs/ARCHITECTURE.md: mechanism, diagrams, risksdocs/PITCH.md: pitch and demo scripts, judge Q&Adocs/SUBMISSION.md: targets, judging criteria, checklistdocs/PRIOR_ART.md: differentiation, market context
MIT. See LICENSE.
Not offered in restricted jurisdictions, including the United States. Outcome tokens can go to zero. Fee rewards are a redistribution of other traders' fees. xStocks can be paused or seized by their issuer. Not legal or investment advice.