EVM Parimutuel Prediction Market
SENQ is a prediction market DApp built on EVM using parimutuel betting mechanics. Users bet on binary outcomes (YES/NO) with ETH, and winners share the entire pool proportionally.
- EVM-Native Design: Uses EVM primitives (ETH transfer, calldata, Multi-Sign)
- Parimutuel Pricing: No complex AMM math — simple pool-based payouts
- Verifiable On-Chain: All bets and outcomes recorded on the EVM blockchain
- Multi-Sign Resolution: 2-of-3 governance prevents manipulation
┌─────────────────────────────────────────────────────────────────┐
│ Frontend (Next.js) │
│ Vercel │
└─────────────────────────────┬───────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Backend (Hono) │
│ Fly.io + SQLite │
└─────────────────────────────┬───────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ EVM Testnet │
│ ETH Transfer │ calldata │ Multi-Sign │
└─────────────────────────────────────────────────────────────────┘
| Feature | Usage |
|---|---|
| ETH Transfer | Pool ETH bets and release to winners |
| calldata | On-chain metadata for all transactions |
| Multi-Sign | 2-of-3 resolution governance |
- Market Created → Admin creates market with betting deadline
- Bets Placed → Users bet ETH on YES or NO
- Resolution → Multi-sign committee resolves outcome
- Payout → Winners receive proportional share of pool
| Layer | Technology |
|---|---|
| Frontend | Next.js 16, React, Tailwind CSS, shadcn/ui |
| Backend | Hono, Bun, SQLite (WAL mode) |
| Blockchain | EVM (Anvil locally / any EVM testnet), viem |
| Deployment | Vercel (frontend), Fly.io (backend) |
Anvil is a local EVM node that ships with Foundry. It starts with 10 pre-funded accounts — no faucet or real ETH needed.
curl -L https://foundry.paradigm.xyz | bash
foundryupVerify:
anvil --versiongit clone https://github.com/thinkshake/senq.git
cd senq
bun installIn a dedicated terminal:
anvilAnvil starts at http://127.0.0.1:8545 (Chain ID: 31337) and prints pre-funded accounts:
Available Accounts
==================
(0) 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 (10000 ETH)
Private Keys
==================
(0) 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
Keep this terminal running throughout development.
Backend:
cp apps/api/.env.example apps/api/.envEdit apps/api/.env:
PORT=3001
DATABASE_PATH=./data/senq.db
EVM_RPC_URL=http://127.0.0.1:8545
EVM_CHAIN_ID=31337
EVM_OPERATOR_ADDRESS=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
EVM_OPERATOR_PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
ADMIN_API_KEY=dev-admin-key
⚠️ These are Anvil's well-known dev keys — safe to use locally, never use in production.
Frontend:
echo 'NEXT_PUBLIC_API_URL=http://localhost:3001/api' > apps/web/.env.localcd apps/api
bun run migrate
cd ../..Open two more terminals:
Terminal 2 — API:
cd apps/api
bun run devTerminal 3 — Web:
cd apps/web
bun run dev| Service | URL |
|---|---|
| Frontend | http://localhost:3000 |
| Backend API | http://localhost:3001 |
| Anvil RPC | http://127.0.0.1:8545 |
Quick sanity checks:
# API health
curl http://localhost:3001/api/markets
# Anvil block number
cast block-number --rpc-url http://127.0.0.1:8545Anvil is included as a service in docker-compose.yml — no separate install needed.
git clone https://github.com/thinkshake/senq.git
cd senq
# Configure the API environment (Anvil keys pre-filled)
cp apps/api/.env.example apps/api/.env
# EVM_RPC_URL and EVM_CHAIN_ID are overridden by docker-compose automatically
# Start everything (Anvil → API → Web)
docker-compose up -d
# View logs
docker-compose logs -f| Service | URL |
|---|---|
| Frontend | http://localhost:3000 |
| Backend API | http://localhost:3001 |
| Anvil RPC | http://localhost:8545 |
The API container connects to Anvil via the internal Docker network (
http://anvil:8545). Anvil is exposed on your host athttp://localhost:8545for tools likecastor MetaMask.
git clone https://github.com/thinkshake/senq.git
cd senq
bun install
cp apps/api/.env.example apps/api/.env
# Edit .env with your testnet RPC URL and operator wallet
cd apps/api && bun run migrate && cd ../..
bun run devYou need an EVM testnet account as Operator (holds ETH, receives bets, sends payouts).
-
Get testnet ETH (e.g. Sepolia): https://sepoliafaucet.com
Or generate a wallet:cast wallet new -
Generate an Admin API Key:
openssl rand -hex 16
Backend (apps/api/.env)
PORT=3001
DATABASE_PATH=./data/senq.db
EVM_RPC_URL=http://127.0.0.1:8545 # Anvil; or https://sepolia.infura.io/v3/KEY
EVM_CHAIN_ID=31337 # Anvil; or 11155111 for Sepolia
EVM_OPERATOR_ADDRESS=0xYourOperatorAddress
EVM_OPERATOR_PRIVATE_KEY=0xYourPrivateKey
ADMIN_API_KEY=your-secret-keyFrontend (apps/web/.env.local)
NEXT_PUBLIC_API_URL=http://localhost:3001/apiGET /api/markets— List all marketsGET /api/markets/:id— Market detailsPOST /api/markets— Create market (admin)POST /api/markets/:id/close— Close market (admin)
POST /api/markets/:id/bets— Place bet (returns tx payloads)POST /api/markets/:id/bets/confirm— Confirm betGET /api/markets/:id/bets/preview— Preview payout
POST /api/markets/:id/offers— Create EVM tradeGET /api/markets/:id/trades— List trades
POST /api/markets/:id/resolve— Resolve market (admin)POST /api/markets/:id/payouts— Execute payouts (admin)GET /api/markets/:id/payouts— List payouts
SENQ uses a SENQMarket Solidity contract (contracts/src/SENQMarket.sol) for trustless, on-chain prediction markets. All bets, resolution, and payouts happen on-chain — no operator EOA pooling required.
- Parimutuel pool: ETH bets go directly into the contract
- Owner-only resolution: Market outcomes resolved by contract owner
- Automatic payouts: Winners claim proportional share on-chain (2% protocol fee)
- Cancel & refund: Owner can cancel markets; bettors reclaim their ETH
| Component | Implementation |
|---|---|
| Pool | SENQMarket contract holds all ETH |
| Bets | betYes() / betNo() — payable functions |
| Resolution | resolve() — owner-only, after deadline |
| Payouts | claimPayout() — winners claim proportional share |
| Fees | 2% of losing pool; withdrawable by owner |
cd contracts
forge build
forge test
forge script script/Deploy.s.sol --rpc-url http://127.0.0.1:8545 --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 --broadcast
# Copy deployed address to EVM_CONTRACT_ADDRESS in apps/api/.envforge script script/Deploy.s.sol --rpc-url $EVM_RPC_URL --private-key $EVM_OPERATOR_PRIVATE_KEY --broadcast --verifyThe operator wallet deploys the contract and resolves markets. It needs ETH for gas.
Anvil's pre-funded dev account is used automatically — no setup needed.
-
Generate a new wallet:
cast wallet new
-
Fund it from a faucet (e.g. https://sepoliafaucet.com)
-
Set in your
.env:EVM_RPC_URL=https://sepolia.infura.io/v3/YOUR_KEY EVM_CHAIN_ID=11155111 EVM_OPERATOR_ADDRESS=0xYourAddress EVM_OPERATOR_PRIVATE_KEY=0xYourPrivateKey EVM_CONTRACT_ADDRESS=0xDeployedContractAddress
Same as testnet — use a mainnet RPC and fund the operator wallet with real ETH for gas.
⚠️ KeepEVM_OPERATOR_PRIVATE_KEYsecret. Use environment secrets (Fly.io secrets, Vercel env vars) — never commit it.
cd apps/api
fly launch
fly secrets set EVM_OPERATOR_ADDRESS=0xXXX...
fly secrets set EVM_OPERATOR_PRIVATE_KEY=0xXXX...
fly secrets set ADMIN_API_KEY=your-secret-key
fly deploycd apps/web
vercel
# Set NEXT_PUBLIC_API_URL to your Fly.io URLSee docs/demo-script.md for the 3-minute demo walkthrough.
MIT
Built for JFIIP Hackathon 2026 by: