Skip to content

Mattdgn/pm-amm

Repository files navigation

pm-AMM — Paradigm Dynamic AMM for Prediction Markets on Solana

CI License: MIT Solana Anchor

First production implementation of the Paradigm pm-AMM on Solana. 100% fidelity to the paper. Uniform LVR in price and time.

Based on pm-AMM: A Prediction Market AMM by Ciamac Moallemi & Dan Robinson (Paradigm, Nov 2024).

Program ID: 8V872cTKfH1gC5zBvQhrQN2DXSmRNokPPjPsBE46MZNj (Devnet Explorer)


The Math

The dynamic pm-AMM invariant (paper section 8):

(y - x) * Phi((y - x) / L_eff) + L_eff * phi((y - x) / L_eff) - y = 0

Where L_eff = L_0 * sqrt(T - t) decreases over time, and phi/Phi are the standard normal PDF/CDF.

Three properties proven by the paper, verified on-chain:

Property Formula Our test result
Uniform LVR (price-independent) LVR_t = V_t / (2*(T-t)) Std across 7 prices: 0.000%
Constant E[LVR] (time-independent) E[LVR_t] = V_0 / (2T) Linearity ratio: 0.994 (500 MC runs)
LP wealth at expiry E[W_T] = W_0 / 2 Measured: 0.518 (500 MC runs, 5% tolerance)

The dC_t Mechanism — Why This is Different

Traditional AMMs leave LPs fully exposed until they withdraw. The pm-AMM actively redistributes liquidity to LPs over time:

deposit 1000 USDC         claim YES+NO          redeem for USDC
       |                       |                       |
       v                       v                       v
  |---------|---------|---------|---------|---------| 
  t=0       t=1d      t=2d      t=3d      ...     T
            |         |         |
            v         v         v
       dC_t accrual: tokens released as L_eff decreases

As time passes, L_eff = L_0 * sqrt(T-t) shrinks. The reserves scale proportionally, releasing YES+NO tokens to LPs via per-share accumulators. LPs can:

  1. Claim YES+NO tokens at any time
  2. Redeem 1 YES + 1 NO = 1 USDC (pair redemption)
  3. Sell on the pool via swap
  4. Hold until resolution for the winning side

Conservation verified:

At fixed price (no arbitrage), 100% of pool value returns to LPs. With random walks (Gaussian score dynamics), exactly 50% returns (the other 50% is LVR consumed by arbitrageurs). Both verified in our test suite.


Architecture

pm-amm/
  anchor/                # Solana program (Anchor/Rust)
    programs/pm_amm/src/
      pm_math.rs         # Fixed-point math (phi, Phi, Phi_inv, reserves, swap)
      accrual.rs         # dC_t mechanism (compute, apply, accrue_first)
      lut.rs             # 2048-point lookup tables for on-chain perf
      state.rs           # Market, LpPosition structs
      errors.rs          # Error codes
      instructions/      # 10 instructions
    tests/               # 18 TS integration tests
    scripts/             # Deploy + seed scripts
  app/                   # Next.js frontend
  oracle/                # Python truth oracle (scipy reference)
  doc/                   # Paper reference, PRD, sprint definitions

10 Instructions

Instruction Who Description
initialize_market Anyone Create market with YES/NO mints, USDC vault
deposit_liquidity LP Add USDC, get shares. Bootstraps L_0 on first deposit
swap Trader 6 directions: USDC/YES/NO combinations
withdraw_liquidity LP Burn shares, receive YES+NO proportional
accrue Anyone Permissionless dC_t accrual (keeper)
claim_lp_residuals LP Claim accrued YES+NO tokens
redeem_pair Holder Burn 1 YES + 1 NO = 1 USDC
resolve_market Authority Set winning side after expiration
claim_winnings Holder Burn winning tokens for 1 USDC each
suggest_l_zero Anyone View: compute optimal L_0 for a budget

Robustness Beyond the Gaussian Model

Test Setup Result
Jump (deterministic) P=0.5 -> P=0.87 in one swap Invariant: 0.00e+00, no overflow
MC with jumps 200 runs, 20% jump probability LVR -35.8% vs Gaussian (lower because jumps push prices to extremes where V is lower)
100 random swaps Alternating directions, random sizes Max invariant: 9e-13

Composability

All accounts are deterministic PDAs:

// Derive all addresses from market_id alone
const [market] = PublicKey.findProgramAddressSync(
  [Buffer.from("market"), marketId.toArrayLike(Buffer, "le", 8)],
  PROGRAM_ID
);
const [yesMint] = PublicKey.findProgramAddressSync(
  [Buffer.from("yes_mint"), market.toBuffer()], PROGRAM_ID
);
// ... same for no_mint, vault

suggest_l_zero is callable via CPI for auto-LP vaults:

await program.methods
  .suggestLZero(budgetUsdc, sigmaBps)
  .accounts({ market })
  .rpc();
// Emits LZeroSuggestion event with suggested_l_zero, daily_lvr, warnings

IDL

The Anchor IDL is available at idl/pm_amm.json for integrators building on top of pm-AMM.


Test Suite

200 tests total:

Category Count Coverage
Rust unit tests (pm_math, accrual, state) 52 All math functions, Q64.64 roundtrips, accrual properties, solver precision
TS integration tests 18 Full lifecycle: init -> deposit -> swap -> claim -> resolve
Python property tests 18 Paradigm properties A/B/C, robustness D/E/F
Python oracle tests 112 Cross-validation against scipy

Known Limitations

  • Oracle: admin-only resolution (no oracle integration)
  • Binary only: YES/NO outcomes, no multi-outcome
  • 0% fees: no trading fees (pure LVR model)

Roadmap

  • Oracle integration (Switchboard/Pyth for auto-resolution)
  • Multi-outcome markets (categorical pm-AMM)
  • Trading fees (LP incentive beyond dC_t)
  • Delta hedging tools for sophisticated LPs

Prerequisites

Quick Start

# Install dependencies
pnpm install

# Build the program
pnpm run build

# Run Rust unit tests (52 tests)
cd anchor && cargo test --package pm_amm

# Run integration tests (18 tests, requires local validator)
pnpm run test

# Run Python oracle tests (130 tests)
cd oracle && python3 test_oracle.py && python3 test_properties.py

# Start the frontend
pnpm run dev

# Deploy to devnet
pnpm run deploy

Environment Variables

Copy .env.example to .env.local in the app/ directory:

NEXT_PUBLIC_RPC_URL=https://api.devnet.solana.com
KV_REST_API_URL=            # Upstash Redis (optional, for price history)
KV_REST_API_TOKEN=          # Upstash Redis (optional)
MINT_AUTHORITY_KEY=         # Base64-encoded keypair for mock USDC faucet

Contributing

See CONTRIBUTING.md for setup, code standards, and PR guidelines.

License

MIT


Built for the $PREDICT hackathon by @matt.

Paper: Paradigm pm-AMM (Moallemi & Robinson, Nov 2024)

About

Implementation of the Paradigm pm-AMM on Solana.

Topics

Resources

License

Contributing

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors