A permissionless prediction market platform built on Ethereum with an embedded constant product AMM. Users can create binary (YES/NO) prediction markets, trade outcome tokens, and resolve markets through an oracle-based settlement.
Control System is a fully on-chain prediction market protocol that allows users to:
- Create Markets: Deploy binary prediction markets with custom questions and initial liquidity
- Trade Positions: Buy and sell YES/NO outcome tokens using an embedded AMM
- Provide Liquidity: Create paired positions (minting) or close positions (burning)
- Settle Markets: Oracle-based resolution with support for YES, NO, or INVALID outcomes
- Claim Winnings: Redeem winning tokens for collateral after market resolution
All markets share a single ERC-1155 outcome token contract, with token IDs derived from market IDs using bit-shifting operations.
┌─────────────────────────────────────────────────────────────────┐
│ CSFactory │
│ • Deploys new CSMarket instances │
│ • Manages global controller and outcome token │
│ • Enforces minimum liquidity requirements │
│ • Pause/unpause functionality │
└────────────┬────────────────────────────────────┬───────────────┘
│ │
│ deploys │ references
│ │
▼ ▼
┌─────────────────────────┐ ┌──────────────────────────────┐
│ CSMarket │ │ CSMarketController │
│ • Individual market │◄────────┤ • Validation layer │
│ • Embedded AMM (x*y=k) │ calls │ • Token mint/burn gateway │
│ • Position management │ │ • Market registration │
│ • Swap functionality │ └───────────┬──────────────────┘
│ • Oracle resolution │ │
│ • Fee collection │ │ validates & calls
└─────────────────────────┘ │
│ │
│ reads/writes via controller │
│ │
└──────────────────┬───────────────┘
▼
┌──────────────────────────┐
│ CSOutcomeToken │
│ • Shared ERC-1155 │
│ • YES/NO tokens │
│ • Token ID derivation: │
│ YES = marketId << 1 │
│ NO = (marketId<<1)|1 │
└──────────────────────────┘
Flow:
1. Factory deploys Market and registers with Controller
2. Market calls Controller to mint/burn tokens
3. Controller validates caller is registered Market
4. Controller calls OutcomeToken to execute mint/burn
| Contract | Description | Responsibility |
|---|---|---|
| CSFactory | Market factory and registry | Deploys markets, manages controller, enforces global settings |
| CSMarket | Individual prediction market | AMM trading, position management, oracle resolution |
| CSMarketController | Validation gateway | Ensures only registered markets can mint/burn tokens |
| CSOutcomeToken | Shared ERC-1155 token | Manages all YES/NO outcome tokens across all markets |
- Formula:
x * y = kwhere x = YES reserve, y = NO reserve - Dynamic Pricing: Prices adjust based on supply/demand
- Fee Collection: Configurable fee rate (in basis points) on swaps
- Slippage Protection: Minimum amount out checks
- Create Position: Deposit collateral → receive equal YES + NO tokens
- Close Position: Burn equal YES + NO tokens → receive collateral back
- No Impermanent Loss: Paired positions always redeemable 1:1
- Three Outcomes: YES, NO, or INVALID
- Fair Settlement: INVALID splits collateral 50/50 between YES and NO holders
- Single Resolution: Markets can only be resolved once
- Liquidity Withdrawal: Creators can withdraw liquidity after resolution
- Pausability: Factory owner can pause markets globally or individually
- Reentrancy Protection: Guards on all state-changing functions
- Safe ERC20: Protected token transfers using OpenZeppelin SafeERC20
- Access Control: Only registered markets can mint/burn outcome tokens
# 1. Clone and install
git clone https://github.com/mehtaculous/control-system.git
cd control-system
forge install
npm install
# 2. Start Anvil (Terminal 1)
anvil
# 3. Deploy contracts (Terminal 2)
forge script script/Deploy.s.sol --broadcast --rpc-url $ANVIL_RPC_URL
# 4. Start frontend (Terminal 3)
cd frontend
npm install
npm run devVisit http://localhost:5175 and connect your wallet to Anvil (chain ID 31337).
For local development with Anvil, copy .env.example to .env.local:
cp .env.example .env.localOnly these variables are required:
ANVIL_RPC_URL- Local Anvil endpoint (default:http://127.0.0.1:8545)DEPLOYER_PRIVATE_KEY- Anvil private key (default: Account #0)
For testnet or mainnet deployment, create a .env file with:
DEPLOYER_ADDRESS- Your deployer wallet addressDEPLOYER_PRIVATE_KEY- Your private key (never commit!)BASE_MAINNET_RPC_URL- Base Mainnet RPC endpointBASE_SEPOLIA_RPC_URL- Base Sepolia RPC endpointETHERSCAN_API_KEY- BaseScan API key for contract verification
# Local (Anvil)
forge script script/Deploy.s.sol --broadcast --rpc-url $ANVIL_RPC_URL
# Testnet (Base Sepolia)
forge script script/Deploy.s.sol \
--rpc-url $BASE_SEPOLIA_RPC_URL \
--private-key $DEPLOYER_PRIVATE_KEY \
--etherscan-api-key $ETHERSCAN_API_KEY \
--broadcast \
--verify
# Mainnet (Base)
forge script script/Deploy.s.sol \
--rpc-url $BASE_MAINNET_RPC_URL \
--private-key $DEPLOYER_PRIVATE_KEY \
--etherscan-api-key $ETHERSCAN_API_KEY \
--broadcast \
--verify| Contract | Address | Explorer |
|---|---|---|
| CSFactory | TBD | View on BaseScan |
| CSMarketController | TBD | View on BaseScan |
| CSOutcomeToken | TBD | View on BaseScan |
| CSOracleAdapter | TBD | View on BaseScan |
| Contract | Address | Explorer |
|---|---|---|
| CSFactory | 0xcFf03C90A0D78c873beFEF5639BDd711Cda7f845 |
View on BaseScan |
| CSMarketController | 0x935373687073a1376D3F21977EE7cCfDdda30A16 |
View on BaseScan |
| CSOutcomeToken | 0x3dDb23020eaD18024D394bf05dB4c67dA5f62adc |
View on BaseScan |
| CSOracleAdapter | 0x906f16db1E6b7d9B1F7Bd5847a8cc70ae08Aa678 |
View on BaseScan |
| MockUSDC | 0x23eD6A503C1caFE9eD636DC7f9208e0230d4b40e |
View on BaseScan |