ethPM enables deterministic, on-chain prediction markets that resolve based on arbitrary smart contract state. Markets can bet on any verifiable on-chain data: TVL, token prices, trading activity, governance votes—without human resolvers.
Core Principles
Deterministic
Resolution via staticcall to any on-chain data source. No oracle dependency or human intervention.
Composable
ERC6909 shares integrate with any DeFi primitive. Modular hook system for custom fee logic.
Immutable
Core contracts deployed without upgrade patterns. Markets persist independently of protocol governance.
The ethPM stack consists of five interconnected layers, each responsible for a specific domain of functionality.
System Architecture Overview
Data Flow
User interactions flow through the stack as follows:
Trade Request: User submits collateral with desired position (YES/NO)
Routing: PMHookRouter evaluates Vault OTC vs AMM pricing
Fee Calculation: PMFeeHook computes dynamic fee based on market state
Execution: zAMM executes swap or PAMM mints new shares
Settlement: User receives conditional tokens
02 // PAMM.sol
PAMM (Prediction AMM) is the core collateral vault that mints and manages ERC6909 conditional tokens (YES/NO shares). Each market is fully collateralized—1 share = 1 wei of collateral.
Key Insight
PAMM is hyperoptimized for zAMM integration. The ERC6909 singleton design enables operator approvals for single-tx share operations and reduces gas costs through warm storage access patterns.
Burns winning shares for collateral minus resolver fee (0-10% configurable).
Storage Layout
Slot
Field
Type
Description
Market.0
resolver + resolved + outcome + canClose + close
packed 31 bytes
Market metadata
Market.1
collateral
address
Collateral token (0x0 = ETH)
Market.2
collateralLocked
uint256
Total collateral in market
03 // PMFeeHook.sol
PMFeeHook is a policy layer that sits between zAMM and PAMM, implementing dynamic swap fees and market protection mechanisms. It uses EIP-1153 transient storage for gas optimization.
Fee Components
Dynamic Fee Calculation
Configuration Parameters
Field
Type
Default
Description
minFeeBps
uint16
10
Steady-state fee (0.10%)
maxFeeBps
uint16
75
Bootstrap starting fee (0.75%)
maxSkewFeeBps
uint16
80
Max skew penalty (0.80%)
feeCapBps
uint16
300
Absolute fee ceiling (3%)
skewRefBps
uint16
4000
Skew threshold (90/10 = 40%)
bootstrapWindow
uint32
172800
Decay duration (2 days)
closeWindow
uint16
3600
Pre-close window (1 hour)
maxPriceImpactBps
uint16
1200
Max single-trade impact (12%)
Flag Bits
flags (uint16):
bit 0: FLAG_SKEW (0x01) - Enable skew fee
bit 1: FLAG_BOOTSTRAP (0x02) - Enable bootstrap decay
bit 2-3: closeWindowMode (0x0C) - 0=halt, 1=fixed, 2=min, 3=dynamic
bit 4: FLAG_ASYMMETRIC (0x10) - Enable directional fee
bit 5: FLAG_PRICE_IMPACT (0x20) - Enable impact check
bit 6: FLAG_VOLATILITY (0x40) - Enable volatility fee
extraFlags (uint16):
bit 0-1: skewCurve - 0=linear, 1=quadratic, 2=cubic, 3=quartic
bit 2-3: decayMode - 0=linear, 1=cubic, 2=sqrt, 3=ease-in
Default flags: 0x37 (skew + bootstrap + closeMode=1 + asymmetric + priceImpact)
Default extraFlags: 0x01 (quadratic skew, linear decay)
04 // PMHookRouter.sol
PMHookRouter provides smart order routing with integrated vault market-making. It creates a "waterfall" effect for best-execution across three venues: Vault OTC, AMM, and Mint fallback.
Order Routing Flow
Buy Order Execution Waterfall
Key Parameters
Constant
Value
Description
MIN_ABSOLUTE_SPREAD_BPS
20
0.2% minimum spread
MAX_SPREAD_BPS
500
5% maximum spread cap
LP_FEE_SPLIT_BPS_BALANCED
9000
90% to LPs when balanced
LP_FEE_SPLIT_BPS_IMBALANCED
7000
70% to LPs when imbalanced
BOOTSTRAP_WINDOW
14400
4h mint path disabled before close
MIN_TWAP_UPDATE_INTERVAL
1800
30 minute TWAP window
05 // PMHookQuoter.sol
View-only quoter contract providing consolidated market data and execution quotes. Separated from routers to keep them under bytecode limit while enabling efficient frontend queries.
Quote functions return a source indicating the execution path:
Source
Meaning
"otc"
Filled via vault OTC
"amm"
Filled via AMM swap
"mint"
Filled via vault mint (collateral → shares)
"mult"
Multiple venues used
Design Note
PMHookQuoter exists as a separate contract because PMHookRouter and MasterRouter are near the 24KB bytecode limit. Moving view functions to a dedicated quoter keeps routers deployable while providing comprehensive query capabilities.
06 // Resolver.sol
The deterministic resolver enables markets that resolve based on arbitrary on-chain reads. No human intervention required—resolution is computed via staticcall at close time.
Resolver Architecture
Resolution Modes
Scalar Mode
Single staticcall decoded as uint256. Compare result against threshold using operator.
target.staticcall(data) → uint256 value
value [op] threshold → YES/NO
Ratio Mode
Two staticcalls with result computed as A * 1e18 / B. Compare ratio against threshold.
targetA.staticcall(dataA) → A
targetB.staticcall(dataB) → B
(A * 1e18 / B) [op] threshold
Comparison Operators
Op
Symbol
Description
0
<
Less than
1
>
Greater than
2
≤
Less or equal
3
≥
Greater or equal
4
==
Equal
5
≠
Not equal
Comparison Operators (Op enum)
Data Sources & Special Cases
Important
The target contract must be immutable or the data being read must be manipulation-resistant. Markets on mutable contracts can be exploited by changing state before resolution.
07 // MasterRouter.sol
MasterRouter provides pooled limit orders for prediction markets. ASK pools sell shares at fixed prices; BID pools buy shares at fixed prices.
MasterRouter System Overview
Order Book Architecture
ASK Pools: Sell shares at fixed price, filled lowest-first by buyWithSweep()
BID Pools: Buy shares at fixed price, filled highest-first by sellWithSweep()
Price Bitmap: 40 uint256s per market/side for O(1) best bid/ask discovery
Accumulator LP: Prevents late-joiner exploitation of pending fills
Pooled Orderbook Architecture
Pooled Orderbook Design
Unlike traditional orderbooks with individual orders, MasterRouter uses pooled limit orders where multiple LPs can deposit into the same price level. This enables gas-efficient fills and fair pro-rata distribution.
Accumulator Pattern
The accumulator LP pattern (similar to MasterChef) tracks rewards per unit of liquidity. When fills occur, accCollPerScaled (ASK) or accSharesPerScaled (BID) increases. Each user's pending rewards are calculated as:
The bitmap enables O(1) discovery of the best available price. Each bit represents a price level (1-9999 bps). Finding the lowest ASK or highest BID requires scanning at most 40 uint256 words using bit manipulation.
Accumulator LP Model
Sweep Operations
The sweep functions (buyWithSweep, sellWithSweep) traverse the price bitmap to fill orders at the best available prices, then fall back to PMHookRouter for any remaining amount.
sellWithSweep() Execution Flow
Mint Operations
MasterRouter provides three mint variants that split collateral into YES/NO shares, keep one side for directional exposure, and route the other side differently based on strategy.
Mint Operations Comparison
Listing Orders (Maker)
Users can place limit orders by depositing assets into price-specific pools. ASK pools list shares for sale; BID pools offer to buy shares.
Function
Action
What You Deposit
What You Receive When Filled
mintAndPool()
Mint shares, keep one side, list other at price
ETH/collateral
Kept shares + collateral when ASK fills
depositSharesToPool()
List existing shares at specific price
YES or NO shares
Collateral when filled
createBidPool()
Offer to buy shares at specific price
ETH/collateral
Shares when filled
Pooled Orders
Multiple users can deposit into the same price level. Fills are distributed pro-rata via the accumulator pattern, preventing front-running and ensuring fair distribution.
Sweeping Orders (Taker)
Takers use sweep functions to execute against the orderbook, automatically filling through multiple price levels for best execution before falling back to AMM liquidity.
Function
Action
Execution Order
buyWithSweep()
Buy shares, sweeping ASK pools low→high
1. Fill ASK pools at best prices up to maxPriceBps 2. Remaining → PMHookRouter (Vault OTC → AMM)
sellWithSweep()
Sell shares, sweeping BID pools high→low
1. Fill BID pools at best prices down to minPriceBps 2. Remaining → PMHookRouter (Vault OTC → AMM)
Sweep Execution: Best Price Discovery
Complete Trading Flow
The MasterRouter enables a complete orderbook experience on top of the ethPM AMM infrastructure:
1. List (Make)
Deposit shares or collateral at your target price. Earn the spread when filled.
2. Sweep (Take)
Execute against best available prices across pools, then AMM fallback.
3. Manage
Withdraw unfilled orders anytime. Claim fills as they happen.
08 // GasPM.sol
GasPM is a self-contained "infinite market" example built on top of ethPM. It demonstrates a specialized resolver for gas price predictions with built-in oracle infrastructure.