Smart Contracts

Smart Contract Suite (Overview)

  1. MarketFactory – deploys new markets (binary/multi/scalar) and wires dependencies.
  2. MarketRegistry – index of all markets with metadata (creator, category, status).
  3. PredictionMarket (per market instance) – core trading & lifecycle: buy/sell, lock, resolve, claim.
  4. OutcomeToken (ERC‑1155) – position tokens (e.g., YES/NO) per market; minted on buy, burned on sell/claim.
  5. AMM Pool (optional) – automated pricing & liquidity (if AMM mode is used instead of order book settlement).
  6. Treasury – collects protocol fees and can distribute incentives.
  7. OracleAdapter – pulls/accepts outcomes from chosen oracle (e.g., Chainlink/UMA) with a standard interface.
  8. DisputeModule – time‑boxed challenge flow; bond staking and finalize outcome.
  9. 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 calls MarketFactory.createMarket(...) → new PredictionMarket deployed → registered in MarketRegistry.
    Initial parameters: question, outcomes, endTime, oracle feed, collateral token, AMM mode (on/off), fees.
  • Trade YES/NO (Market + OutcomeToken + AMM):
    User buy(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):
    At endTime, market locks trading. OracleAdapter posts result via reportOutcome(winner).
    If challenged, DisputeModule handles staking & adjudication; then finalizeOutcome(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 )

Shell
// 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

Shell
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

Shell
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

Shell
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)

Shell
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);
    }
}

Search