Skip to content

0xassou/wager

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

6 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

๐Ÿ”ฎ Wager โ€” "Every wager tells the future."

A full-featured prediction market (Polymarket / Kalshi style) deployed on Arc Testnet (Circle's USDC-native L1), with test-USDC betting and a built-in optimistic dispute-resolution system โ€” no paid oracle required.

๐ŸŒ Live app: wager-arc.vercel.app ๐Ÿ“ฆ This repo: full-stack app (smart contract + Next.js frontend) ๐Ÿงฉ Standalone contract: the resolution engine is also published on its own as a reusable building block for other Arc builders โ€” see Standalone contract repo below.

๐Ÿ“ Current deployment (Arc Testnet)

Contract address 0x74Cb0cdc0b7608f65C777a46f58CF4cE6ad46C7f
USDC (Circle, Arc Testnet) 0x3600000000000000000000000000000000000000
Chain ID 5042002

โœจ Features

  • โœ… Create a market: anyone can post a question + an end date
  • โœ… Bet Yes / No in test USDC (parimutuel system)
  • โœ… Two-phase optimistic resolution: creator proposes โ†’ 24h dispute window โ†’ auto-finalize, or owner arbitration if disputed โ€” see How resolution works below
  • โœ… Low protocol fee: 0.5% on winnings only, owner-configurable, capped at 5%
  • โœ… Discovery: on-chain categories, search, sort (popular / recent / ending soon), category filters
  • โœ… Profile page: identicon, on-chain stats (bets, win rate, net P&L, markets created), activity badge, bet/market history
  • โœ… Browser notifications: follow a market and get alerted when it's closing soon, disputed, or resolved (client-side only โ€” see limitation below)
  • โœ… Dark / light mode, fully responsive
  • โœ… 10 languages (en/fr/es/ar/pt/de/zh/ja/hi/ru), including right-to-left layout for Arabic
  • โœ… Claim winnings, per-market history and volume (on-chain events)

๐Ÿ—‚ Project structure

predictionmarket/
โ”œโ”€โ”€ contracts/                        # Smart contracts (Hardhat + Solidity + OpenZeppelin)
โ”‚   โ”œโ”€โ”€ contracts/
โ”‚   โ”‚   โ”œโ”€โ”€ PredictionMarket.sol      # Main contract (parimutuel + optimistic resolution)
โ”‚   โ”‚   โ””โ”€โ”€ MockUSDC.sol              # Test USDC (fallback if no faucet)
โ”‚   โ”œโ”€โ”€ scripts/deploy.js             # Deployment script
โ”‚   โ””โ”€โ”€ test/                         # Tests (34 tests, all passing)
โ””โ”€โ”€ web/                               # Next.js 14 frontend (App Router)
    โ”œโ”€โ”€ app/                          # Pages: home, /market/[id], /my-markets, /profile
    โ”œโ”€โ”€ components/                   # Cards, modals, bet panel, resolution panel, logo, etc.
    โ”œโ”€โ”€ messages/                     # i18n translations (10 locales)
    โ””โ”€โ”€ lib/                          # wagmi config, Arc chain, contract ABI, categories, follow/notifications

Stack: Solidity 0.8.24 ยท OpenZeppelin 5 ยท Hardhat ยท Next.js 14 ยท TypeScript ยท Tailwind ยท wagmi + viem + RainbowKit ยท next-intl ยท next-themes


๐Ÿš€ Step-by-step deployment (beginner friendly)

Step 0 โ€” Prerequisites

Step 1 โ€” Add Arc Testnet to MetaMask

In MetaMask โ†’ Networks โ†’ Add network manually:

Field Value
Network Name Arc Testnet
RPC URL https://rpc.testnet.arc.network
Chain ID 5042002
Currency Symbol USDC
Block Explorer https://arcscan.io

๐Ÿ’ก Arc quirk: the native currency โ€” the one that pays gas โ€” is USDC, not ETH. You don't need any ETH on this network at all.

Step 2 โ€” Get test USDC

  1. Go to faucet.circle.com, pick Arc Testnet, paste your address. The USDC you receive covers both gas and bets.
  2. The USDC contract address on Arc Testnet is 0x3600000000000000000000000000000000000000 (verified on Circle's official page โ€” already pre-filled in the project's .env files).

๐Ÿ’ก Faucet not working? Leave USDC_ADDRESS empty in step 4: the deploy script will deploy a MockUSDC instead, with a faucet() function you can call to mint yourself 1000 test USDC.

Step 3 โ€” Install dependencies

cd contracts && npm install
cd ../web && npm install

Step 4 โ€” Configure deployment secrets

cd contracts
cp .env.example .env

Open contracts/.env and fill in:

PRIVATE_KEY=your_metamask_private_key   # MetaMask โ†’ Account details โ†’ Export private key
ARC_RPC_URL=https://rpc.testnet.arc.network
USDC_ADDRESS=0x3600000000000000000000000000000000000000   # official USDC on Arc Testnet (or empty โ†’ MockUSDC)
TREASURY_ADDRESS=                        # optional: fee treasury address (defaults to the deployer)

๐Ÿ”’ NEVER use a private key from a wallet holding real funds. Use a wallet dedicated to testnets. The .env file is git-ignored. โ›ฝ Reminder: the deployer wallet pays gas in native USDC (from the Circle faucet), not ETH.

Step 5 โ€” Test, then deploy the contract

# Run the tests locally (recommended)
npx hardhat test   # 34 passing

# Deploy to Arc Testnet
npm run deploy:arc

The script prints, at the end:

NEXT_PUBLIC_MARKET_ADDRESS=0x...
NEXT_PUBLIC_USDC_ADDRESS=0x...

Step 6 โ€” Configure the frontend

cd ../web
cp .env.example .env.local

Paste the two addresses printed by the deploy script into web/.env.local.

Step 7 โ€” Run the app ๐ŸŽ‰

npm run dev

Open http://localhost:3000, connect MetaMask (Arc Testnet network), and:

  1. Create a market ("Open a position" button), optionally picking a category
  2. Bet: pick Yes/No, enter an amount โ†’ Approve then Bet (2 transactions โ€” normal for an ERC-20)
  3. After the end date, the creator proposes a resolution โ€” see the next section for what happens after that
  4. Once finalized, winners claim their payout

๐Ÿง  How payouts work (parimutuel)

All "Yes" bets go into one pool, all "No" bets into another.

payout = your stake + (your stake / winning pool) ร— losing pool โˆ’ protocol fee

The protocol fee (0.5% by default) applies only to the winnings portion, never to your refunded stake โ€” see Protocol fee below.

Example: Yes pool = 200 USDC (100 of it yours), No pool = 400 USDC. The outcome is Yes โ†’ you get 100 + (100/200) ร— 400 = 300 USDC, minus a 0.5% fee on the 200 USDC of winnings (i.e. minus 1 USDC โ†’ 299 USDC net).

Special case: if nobody bet on the winning side, everyone is refunded their exact stake โ€” fee-free.

โš–๏ธ How resolution works (optimistic dispute system)

Instead of trusting the market creator blindly, resolving a market goes through up to four steps:

                    market end date reached
                              โ”‚
                              โ–ผ
                        โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                        โ”‚   Open    โ”‚  betting allowed
                        โ””โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”˜
                              โ”‚ creator proposes an outcome
                              โ”‚ (or the contract owner, after a 7-day grace
                              โ”‚  period, if the creator stayed inactive)
                              โ–ผ
                        โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
              โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค Proposed  โ”‚  24h dispute window running
              โ”‚         โ””โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”˜
   no dispute โ”‚               โ”‚ anyone can dispute
   before the โ”‚               โ”‚ (locks a 5 USDC bond)
   window     โ”‚               โ–ผ
   closes     โ”‚         โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
              โ”‚         โ”‚ Disputed  โ”‚  awaiting the contract owner's ruling
              โ”‚         โ””โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”˜
              โ”‚      โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
              โ”‚      โ”‚                              โ”‚
              โ”‚  owner rules                30 days pass with
              โ”‚  (bond refunded                no ruling at all
              โ”‚   or forfeited)                     โ”‚
              โ”‚      โ”‚                    anyone can trigger a
   anyone finalizes  โ”‚                    neutral full refund
              โ”‚      โ”‚                   (bond returned, no fees,
              โ–ผ      โ–ผ                    nobody favored)
            โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
            โ”‚                 Finalized                  โ”‚
            โ”‚        winners claim their payout           โ”‚
            โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
  1. Propose. After the end date, the market's creator proposes an outcome. If the creator never shows up, the contract owner can propose on their behalf once a 7-day grace period has passed, so a market can't be stuck forever.
  2. Dispute window (24h). Proposing an outcome does not finalize it โ€” it opens a 24-hour challenge window. Anyone can dispute it during that window by locking a 5 USDC bond.
  3. Finalize or arbitrate.
    • No dispute โ†’ anyone can finalize once the window closes; the proposed outcome becomes final.
    • Disputed โ†’ the contract owner rules. If the disputer was right, their bond is refunded; if not, it's forfeited to the protocol treasury.
  4. Anti-stuck-funds safety net. If a disputed market is never ruled on (owner unavailable, lost keys, etc.), anyone can trigger a fully neutral refund after 30 days: everyone gets their exact stake back, the disputer gets their bond back, no fees, no side favored.

This reduces reliance on a single honest creator, without needing a paid external oracle. See Known limitations for what this does not protect against.

๐Ÿ’ฐ Protocol fee

A small fee (0.5% by default, capped at 5%) is taken at claim time, only on the winnings portion โ€” never on a refunded stake, and never at all on a full refund. The rate is owner-configurable, and accrued fees are withdrawable to a treasury address.

๐Ÿ”” Following markets & notifications

On any market page, click "Follow this market". If you allow browser notifications when prompted, you'll get an alert when a followed market is closing soon, gets disputed, or is finalized.

โš ๏ธ Limitation: this is a client-side-only mechanism (Web Notifications API), not a real push/server notification system. It only fires while the Wager tab is open (even in the background) โ€” closing the tab means you won't get notified, since there's no backend to deliver alerts when you're gone.

๐Ÿงฉ Standalone contract repo

The optimistic-resolution engine used here has been extracted into its own standalone, forkable repo, meant as a reusable building block for other Arc projects (prediction markets, conditional-payout escrows, anything that needs "did X happen?" without a paid oracle):

โ†’ github.com/0xassou/arc-optimistic-prediction-market

It contains the same contract (generalized, no Wager-specific naming), the same 34 tests, and a self-contained README aimed at developers who've never heard of Wager โ€” including a full function reference and integration guide. This predictionmarket repo is where that contract actually gets used, wired up to a full frontend.

๐Ÿ” Security

  • SafeERC20 + ReentrancyGuard (OpenZeppelin), checks-effects-interactions pattern throughout
  • Custom errors, no permanently stuck funds (refund paths for an empty winning pool, and for an unruled dispute after 30 days)
  • Optimistic dispute window with a bonded challenge mechanism โ€” see How resolution works

โš ๏ธ Known limitations

  • The dispute system reduces, but does not eliminate, centralized trust. A dishonest market creator can be overridden by disputing their proposal โ€” but the contract owner remains the final arbiter for any disputed market, exactly like a real oracle committee of one. This is not a decentralized oracle (no UMA-style token-weighted voting, no Reality.eth-style crowd escalation) โ€” it's a lightweight, free alternative that trades some decentralization for simplicity and zero external cost.
  • If you need protection against a dishonest owner too (not just a dishonest creator), consider deploying with a multisig as the owner, or putting adminResolve behind a timelock โ€” see the standalone contract repo's README for details.
  • This is a testnet MVP: not audited, not intended for mainnet or real funds as-is.

๐Ÿ›  Useful commands

Where Command Effect
contracts/ npx hardhat test Runs the 34 tests
contracts/ npm run compile Compiles the contracts
contracts/ npm run deploy:arc Deploys to Arc Testnet
web/ npm run dev Dev server (localhost:3000)
web/ npm run build Production build

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages