Smart Contracts
Smart Contract Suite (Overview)
- MarketFactory – deploys new markets (binary/multi/scalar) and wires dependencies.
- MarketRegistry – index of all markets with metadata (creator, category, status).
- PredictionMarket (per market instance) – core trading & lifecycle: buy/sell, lock, resolve, claim.
- OutcomeToken (ERC‑1155) – position tokens (e.g., YES/NO) per market; minted on buy, burned on sell/claim.
- AMM Pool (optional) – automated pricing & liquidity (if AMM mode is used instead of order book settlement).
- Treasury – collects protocol fees and can distribute incentives.
- OracleAdapter – pulls/accepts outcomes from chosen oracle (e.g., Chainlink/UMA) with a standard interface.
- DisputeModule – time‑boxed challenge flow; bond staking and finalize outcome.
- Account Abstraction Paymaster (optional) – sponsors gas or lets users pay gas in USDC. (4337 infra, not core to markets)
How They’re Used (Key Use Cases)
- Create Market (Factory + Registry + Market):
Creator callsMarketFactory.createMarket(...)→ newPredictionMarketdeployed → registered inMarketRegistry.
Initial parameters: question, outcomes, endTime, oracle feed, collateral token, AMM mode (on/off), fees. - Trade YES/NO (Market + OutcomeToken + AMM):
Userbuy(outcome, amount)→ collateral transferred to market → AMM swap (or order‑book settlement) → ERC‑1155 outcome tokens minted to user (or their Proxy Wallet).sell(outcome, amount)burns ERC‑1155; returns USDC minus fees. - Lock & Resolve (Market + OracleAdapter + Dispute):
AtendTime, market locks trading. OracleAdapter posts result viareportOutcome(winner).
If challenged, DisputeModule handles staking & adjudication; thenfinalizeOutcome(winner). - Claim (Market + OutcomeToken + Treasury):
Holders of the winning ERC‑1155 burn positions to claim USDC. Treasury takes protocol fee if configured. - Fees & Rewards (Treasury):
Protocol fee on trades or claims flows into Treasury; can fund LP incentives or buybacks later. - Gas UX (Paymaster):
Optional ERC‑4337 Paymaster sponsors gas or charges gas in USDC for bundles (approve→buy→record).
Collateral (ERC‑20), IPositions (ERC‑1155) ( SAMPLE CODE STRUCTURE )
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
interface ICollateral {
function transferFrom(address from, address to, uint256 amt) external returns (bool);
function transfer(address to, uint256 amt) external returns (bool);
function balanceOf(address) external view returns (uint256);
function decimals() external view returns (uint8);
}
interface IPositions /* is ERC1155-like */ {
function mint(address to, uint256 id, uint256 amt, bytes calldata data) external;
function burn(address from, uint256 id, uint256 amt) external;
}
Market Types & Oracle Interface
pragma solidity ^0.8.20;
interface IOracleAdapter {
event OutcomeProposed(uint256 marketId, uint256 outcomeId, address proposer);
event OutcomeFinalized(uint256 marketId, uint256 outcomeId);
function proposeOutcome(uint256 marketId, uint256 outcomeId) external;
function finalizeOutcome(uint256 marketId) external;
function getOutcome(uint256 marketId) external view returns (bool finalized, uint256 outcomeId);
}
MarketRegistry
pragma solidity ^0.8.20;
contract MarketRegistry {
struct MarketInfo {
address market;
address creator;
string category;
uint64 endTime;
bool resolved;
}
MarketInfo[] public markets;
event MarketRegistered(uint256 indexed marketId, address market, address creator, string category, uint64 endTime);
function register(address market, address creator, string calldata category, uint64 endTime) external returns (uint256 id) {
markets.push(MarketInfo(market, creator, category, endTime, false));
id = markets.length - 1;
emit MarketRegistered(id, market, creator, category, endTime);
}
function setResolved(uint256 id) external { // restrict in production
markets[id].resolved = true;
}
function getCount() external view returns (uint256) { return markets.length; }
}
MarketFactory
pragma solidity ^0.8.20;
import "./MarketRegistry.sol";
import "./PredictionMarket.sol";
contract MarketFactory {
MarketRegistry public registry;
address public oracleAdapter;
address public positionsImpl; // ERC-1155 positions (cloneable)
address public collateral; // e.g., USDC
address public treasury;
event MarketCreated(uint256 indexed marketId, address market, string question);
constructor(address _registry, address _oracle, address _positions, address _collateral, address _treasury) {
registry = MarketRegistry(_registry);
oracleAdapter = _oracle;
positionsImpl = _positions;
collateral = _collateral;
treasury = _treasury;
}
function createBinary(
string calldata question,
string calldata category,
uint64 endTime,
uint16 feeBps,
bool useAMM
) external returns (address market, uint256 marketId) {
PredictionMarket m = new PredictionMarket(
question, category, endTime, feeBps,
collateral, oracleAdapter, treasury, useAMM
);
market = address(m);
marketId = registry.register(market, msg.sender, category, endTime);
m.setIds(marketId, 2 /* outcomes: YES=1, NO=2 */);
emit MarketCreated(marketId, market, question);
}
}
DisputeModule (skeleton)
pragma solidity ^0.8.20;
contract DisputeModule {
struct Dispute {
address challenger;
uint256 bond;
uint64 openedAt;
bool resolved;
}
mapping(uint256 => Dispute) public disputes; // marketId => dispute
uint64 public constant WINDOW = 24 hours;
uint256 public constant BOND = 100e6; // 100 USDC (example)
event Disputed(uint256 marketId, address challenger);
event DisputeResolved(uint256 marketId, bool uphold);
function open(uint256 marketId) external {
require(disputes[marketId].challenger == address(0), "exists");
disputes[marketId] = Dispute(msg.sender, BOND, uint64(block.timestamp), false);
emit Disputed(marketId, msg.sender);
}
function resolve(uint256 marketId, bool uphold) external {
Dispute storage d = disputes[marketId];
require(!d.resolved && block.timestamp <= d.openedAt + WINDOW, "closed");
d.resolved = true;
emit DisputeResolved(marketId, uphold);
}
}