Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Self-Resolving Prediction Markets

A production-grade smart contract system for self-resolving subjective prediction/opinion markets using a bounded-loss LMSR (Logarithmic Market Scoring Rule) cost-function AMM.

Overview

This system enables binary prediction markets that self-resolve to their terminal probability distribution. Instead of requiring an external oracle to determine outcomes, markets terminate randomly after trades (with configurable probability α) and resolve to whatever probabilities the market has discovered at that moment.

Key Features

  • LMSR AMM: Bounded-loss automated market maker with mathematically guaranteed liquidity
  • Self-Resolution: Markets resolve to terminal probabilities r = q_current upon random termination
  • VRF Integration: Secure randomness via Chainlink VRF v2.5 (or compatible) with rolling seed approach
  • ERC20 Collateral: Support for arbitrary ERC20 tokens (USDC recommended)
  • Fee System: Configurable trading fees with treasury accumulation
  • Admin Controls: Pausable trading, configurable parameters, emergency resolution

Architecture

┌─────────────────────────────────────────────────────────────┐
│                      MarketFactory                          │
│  - Creates and registers markets                            │
│  - Manages allowed collateral tokens                        │
│  - Stores VRF seed for new market initialization            │
└─────────────────────┬───────────────────────────────────────┘
                      │ creates
                      ▼
┌─────────────────────────────────────────────────────────────┐
│                  SelfResolvingMarket                        │
│  - Holds collateral                                         │
│  - Tracks share inventory c[i] per outcome                  │
│  - Implements LMSR pricing (buy/sell)                       │
│  - Random termination check after trades                    │
│  - Claim settlement after resolution                        │
└─────────────────────────────────────────────────────────────┘
                      │
                      │ uses
                      ▼
┌─────────────────────────────────────────────────────────────┐
│                   FixedPointMath                            │
│  - UD60x18 fixed-point arithmetic                           │
│  - exp() and ln() implementations                           │
│  - LMSR cost function calculations                          │
└─────────────────────────────────────────────────────────────┘

LMSR Math

Cost Function

For a binary market with shares outstanding c[0] and c[1]:

C(c₀, c₁) = b × ln(exp(c₀/b) + exp(c₁/b))

Where b is the liquidity parameter controlling price sensitivity.

Instantaneous Probability

q[i] = exp(c[i]/b) / Σⱼ exp(c[j]/b)

Trade Cost

For a user buying δ shares of outcome 1:

amountIn = C(c₀, c₁ + δ) - C(c₀, c₁)

Closed-Form Solution (Binary)

To calculate shares out for a given collateral input:

Let A = exp(c₁/b), B = exp(c₀/b)

δ = b × ln(((B + A) × exp(amountIn/b) - B) / A)

Random Self-Resolution

Rolling Seed VRF Approach

The system uses a "rolling seed" approach for randomness:

  1. VRF Seed: A vrfSeed is maintained and updated periodically via VRF callback
  2. Per-Trade Randomness: Each trade derives its random value from:
    rand = keccak256(vrfSeed, tradeCount, msg.sender, block.timestamp, block.prevrandao)
    
  3. Termination Check: If rand % 1e18 < alpha, market resolves immediately
  4. Resolution: Probabilities r[i] = q[i] are frozen, trading disabled, claims enabled

Security Assumptions

  • VRF seed is unpredictable at trade submission time
  • Block parameters provide additional entropy
  • Alpha is properly calibrated for desired market duration

User Flow

1. Market Creation

MarketFactory.MarketParams memory params = MarketFactory.MarketParams({
    collateral: USDC_ADDRESS,
    collateralDecimals: 6,
    b: 1000e18,           // Liquidity parameter
    alpha: 1e16,          // 1% resolution chance per trade
    feeBps: 100,          // 1% fee
    feeRecipient: treasury,
    question: "Will ETH reach $10k in 2025?",
    outcomes: ["YES", "NO"]
});

address market = factory.createMarket(params);

2. Trading

// Approve collateral first
USDC.approve(market, amount);

// Buy shares
(uint256 sharesQuoted, uint256[2] memory newProbs) = market.quoteBuy(1, amount);
uint256 sharesOut = market.buy(1, amount, minShares, deadline);

// Sell shares
(uint256 amountQuoted, uint256[2] memory newProbs) = market.quoteSell(1, shares);
uint256 amountOut = market.sell(1, shares, minAmount, deadline);

3. Resolution & Claiming

Markets resolve automatically after random termination. Once resolved:

// Check resolution
(bool resolved, , , uint256[2] memory r) = market.state();

// Calculate payout
uint256 payout = market.calculatePayout(user);

// Claim
uint256 received = market.claim();

Settlement Formula

Each share of outcome i pays r[i] units of collateral:

payout = Σᵢ userShares[user][i] × r[i]

Installation

# Clone repository
git clone <repo-url>
cd prediction-market

# Install dependencies
forge install

# Build
forge build

# Test
forge test -vvv

# Test with gas reporting
forge test --gas-report

Testing

# Run all tests
forge test

# Run specific test file
forge test --match-path test/SelfResolvingMarket.t.sol

# Run with verbosity
forge test -vvvv

# Run fuzz tests with more runs
forge test --fuzz-runs 10000

Test Coverage

  • ✅ LMSR math correctness (exp, ln, cost function)
  • ✅ Buy/sell price impact verification
  • ✅ Quote-execution consistency
  • ✅ Slippage protection
  • ✅ Resolution state transitions
  • ✅ Claim payout accuracy
  • ✅ Fee accumulation and withdrawal
  • ✅ Randomness integration (mock VRF)
  • ✅ Invariants (no negative balances, probabilities sum to 1)

Deployment

Prerequisites

  1. Deploy or connect to Chainlink VRF v2.5 Coordinator
  2. Create and fund VRF subscription
  3. Have treasury address ready
  4. Have allowed collateral tokens ready

Deployment Steps

// 1. Deploy VRF Consumer
VRFConsumer vrfConsumer = new VRFConsumer(
    vrfCoordinatorAddress,
    subscriptionId,
    keyHash,
    callbackGasLimit,
    requestConfirmations
);

// 2. Deploy Factory
MarketFactory factory = new MarketFactory(
    treasury,
    defaultFeeBps,    // e.g., 100 for 1%
    defaultAlpha      // e.g., 1e16 for 1%
);

// 3. Configure
factory.setCollateralAllowed(USDC, true);
factory.setVrfCoordinator(address(vrfConsumer));
vrfConsumer.setFactory(address(factory));

// 4. Initial VRF seed
vrfConsumer.requestRandomness();
// Wait for callback...

Security Considerations

Implemented Safeguards

  • ReentrancyGuard: All external functions protected
  • SafeERC20: Safe token transfers
  • Overflow Protection: Fixed-point math with bounds checking
  • Probability Clamping: Epsilon bounds prevent log(0)
  • Slippage Protection: minSharesOut/minAmountOut parameters
  • Deadline Enforcement: Transaction expiry
  • Single Resolution: Market can only resolve once

Known Limitations

  1. Decimal Scaling: USDC (6 decimals) requires careful scaling to internal 1e18
  2. Randomness Quality: Rolling seed approach has weaker guarantees than per-trade VRF
  3. Gas Costs: exp/ln computations are gas-intensive
  4. MEV: Trades visible in mempool could be front-run

Recommended Audits

Before mainnet deployment:

  • Professional security audit
  • Economic modeling review
  • VRF integration verification
  • Gas optimization analysis

Configuration Parameters

Parameter Description Recommended Range
b Liquidity parameter 100-10000 (in collateral units × 1e18)
alpha Resolution probability per trade 1e15 - 1e17 (0.1% - 10%)
feeBps Trading fee 10-500 (0.1% - 5%)
minTradesBeforeResolution Minimum trades before resolution possible 1-100
maxTradeSize Maximum single trade size Depends on liquidity

Gas Estimates

Operation Estimated Gas
createMarket ~800,000
buy ~200,000-300,000
sell ~200,000-300,000
claim ~100,000

License

MIT

Contributing

  1. Fork the repository
  2. Create feature branch
  3. Write tests for new functionality
  4. Submit pull request

Support

For questions or issues, please open a GitHub issue.

forge verify-contract
--chain base
--watch
--compiler-version v0.8.24
0x7d441a847Ca972F520d728055Cd9E947ac67984A
src/MarketFactoryV2.sol:MarketFactoryV2
--constructor-args $(cast abi-encode "constructor(address,uint256,uint256,bool)" 0x6A9abb446fcA45Dc9BE5Fa3051c4167cd96D1f99 10000000000000000 100 true) \
--etherscan-api-key $ETHERSCAN_API_KEY

forge verify-contract
--chain base
--watch
--compiler-version v0.8.24
0xB138C21Aa20Bf12bCe62DE07cadd9809C607F471
src/mocks/MockERC20.sol:MockUSDC
--etherscan-api-key $ETHERSCAN_API_KEY

About

Self Resolving Opinion markets that use a bounded LMSR market maker

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages