Mattdgn
pm-amm
Rust✨ New

Implementation of the Paradigm pm-AMM on Solana.

Last updated Jun 28, 2026
46
Stars
1
Forks
1
Issues
0
Stars/day
Attention Score
67
Language breakdown
No language data available.
Files click to expand
README

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) / Leff) + Leff  phi((y - x) / L_eff) - y = 0

Where Leff = L0 * 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) | LVRt = Vt / (2*(T-t)) | Std across 7 prices: 0.000% | | Constant E[LVR] (time-independent) | E[LVRt] = V0 / (2T) | Linearity ratio: 0.994 (500 MC runs) | | LP wealth at expiry | E[WT] = W0 / 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
       dCt accrual: tokens released as Leff decreases

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

  • Claim YES+NO tokens at any time
  • Redeem 1 YES + 1 NO = 1 USDC (pair redemption)
  • Sell on the pool via swap
  • 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/
      pmmath.rs         # Fixed-point math (phi, Phi, Phiinv, reserves, swap)
      accrual.rs         # dCt mechanism (compute, apply, accruefirst)
      lut.rs             # 2048-point lookup tables for on-chain perf
      state.rs           # Market, LpPosition structs
      errors.rs          # Error codes
      instructions/      # 11 instructions (fully-backed architecture, Sprint 20)
    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

11 Instructions

| Instruction | Who | Description | |---|---|---| | initialize_market | Anyone | Create market with YES/NO mints, USDC vault, pool ATAs | | depositliquidity | LP | Add USDC, get shares. Bootstraps L0 on first deposit | | mint_pair | Anyone | Burn 1 USDC → mint 1 YES + 1 NO (Sprint 20, fully-backed) | | redeem_pair | Holder | Burn 1 YES + 1 NO → 1 USDC | | swapyesno | Trader | Pure YES↔NO swap on the pm-AMM curve (2 directions) | | withdraw_liquidity | LP | Burn shares, receive YES+NO proportional | | accrue | Anyone | Permissionless dC_t accrual (keeper) | | claimlpresiduals | LP | Claim accrued YES+NO tokens | | resolve_market | Authority | Set winning side after expiration | | claim_winnings | Holder | Burn winning tokens for 1 USDC each | | suggestlzero | Anyone | View: compute optimal L_0 for a budget |

USDC ↔ YES/NO trades are built client-side as atomic instruction combos:

  • BUY YES/NO = mintpair(δ) + swapyes_no (dump the unwanted side)
  • SELL YES/NO = swapyesno (rebalance to a pair) + redeem_pair(δ)
Conservation invariant (Sprint 20): vault.usdc == yesmint.supply == nomint.supply at all times. Fully-backed outcome tokens, Polymarket-style — no structural rug possible.


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("yesmint"), market.toBuffer()], PROGRAMID
);
// ... same for no_mint, vault

suggestlzero is callable via CPI for auto-LP vaults:

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

IDL

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


Test Suite

216 tests total:

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

Run with:

pnpm run test:rust   # Rust unit (62)
pnpm run test        # TS integration on localnet (18)
pnpm run test:all    # Rust + Python (skips TS)
python3 oracle/testoracle.py && python3 oracle/testproperties.py  # Python (136)

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 (anchor build + idl build)

pnpm run build

Run Rust unit tests (62 tests)

pnpm run test:rust

Run integration tests (18 tests, requires local validator)

pnpm run test

Run Python oracle + property tests (136 tests)

cd oracle && python3 testoracle.py && python3 testproperties.py

Run everything except TS integration

pnpm run test:all

Start the frontend

pnpm run dev

Deploy to devnet

pnpm run deploy

Environment Variables

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

NEXTPUBLICRPC_URL=https://api.devnet.solana.com
KVRESTAPI_URL=            # Upstash Redis (optional, for price history)
KVRESTAPI_TOKEN=          # Upstash Redis (optional)
MINTAUTHORITYKEY=         # 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)

© 2026 GitRepoTrend · Mattdgn/pm-amm · Updated daily from GitHub