flowchart TD
CreatePage[/create page/] -->|mechanism: cpmm-1 or dpm-2| MarketAPI[POST /v0/market]
MarketAPI --> NewContract[getNewContract]
NewContract --> BinaryCpmm[getBinaryCpmmProps]
NewContract --> BinaryDpm[getBinaryDpmProps]
PlaceBet[POST /v0/bet] --> Dispatch{contract.mechanism}
Dispatch -->|cpmm-1| CpmmBet[getBinaryCpmmBetInfo]
Dispatch -->|cpmm-multi-1| MultiBet[getNewMultiCpmmBetInfo]
Dispatch -->|dpm-2| DpmBet[getBinaryDpmBetInfo]
MarketPage[Market page] -->|creator, dpm-2 only| ConvertAPI[POST /v0/convert-dpm-to-cpmm]
ConvertAPI --> ConvertHelper[dpmToCpmmHelper]
ConvertHelper -->|rewrite shares, rebuild metrics, update pool, set mechanism| DB[(contracts, contract_bets, user_contract_metrics)]
ResolveAPI[POST /v0/market/.../resolve] --> ResolveGuard{mechanism == dpm-2?}
ResolveGuard -->|yes| Reject[APIError: convert first]
ResolveGuard -->|no| Resolve[resolveMarketHelper]
dpm-2 as a live mechanism: new DPM & Binary variant on Contract, new math module, new branch in place-bet, unchanged Bet row shape (DPM bets use the same shares/amount/outcome/limitProb/fills fields as CPMM).computeDpmFills analogous to CPMM's computeFills. Limit orders live in the same contract_bets table with limitProb / orderAmount — same schema that CPMM already uses.computeDpmFills by clamping DPM-curve segments so probability never crosses 0.01–0.99. Refund any residual order amount beyond the cap.shares *= C/y for YES, shares *= C/n for NO), rebuild user_contract_metrics from the rewritten bets, seed the new CPMM pool entirely from the ante's tokens using the Maniswap curvature formula, flip mechanism to 'cpmm-1', leave resting limit orders untouched (prices and budgets coincide per the migration doc).dpm-2 market.dpm-2 through every payout and metrics helper using a pool-weighted mark-to-market. Profile "profit", portfolio-history graph, per-position PnL, day/week/month deltas, and league mana_earned all share a handful of common helpers (calculatePayout, getProfitMetrics, calculatePayoutFromShares, calculateMetricsFromProbabilityChanges, calculateProfitMetricsAtProbOrCancel) that currently either fall through to bet.amount or throw for non-CPMM mechanisms. Each needs a dpm-2 branch so DPM activity is visible across all surfaces during the DPM phase./create; on the market page hide the sell UI (already implicit — sell is gated on cpmm-1) and render a "Convert to Classic" panel visible only to the creator (and admins/mods) when mechanism === 'dpm-2' && !isResolved. Hide the sell tab and the "sell shares" button for DPM. The frontend work needs a broader audit than BuyPanel because several binary views and helpers special-case cpmm-1.Legacy DPM code handling: the tree has no live DPM runtime (common/src/calculate-dpm.ts is absent; new-contract.ts has a commented-out DPM block; old migration scripts reference deleted code). We will introduce a fresh calculate-dpm.ts with only the math this feature needs and delete the stale comment block. Old migration scripts in backend/scripts/ remain untouched (they reference an old-shape DPM model that no longer applies; they were already broken pre-feature). We also need to avoid inheriting fixed-payout-only behavior from the current CPMM stack, especially redeemShares() and any binary helpers that assume cpmm-1.
DPM shape to common/src/contract.tsAdd a new DPM record that reuses the existing Binary intersection pattern. Include it in AnyContractType, MarketContract, and export convenience aliases.
export type DPM = {
mechanism: "dpm-2";
pool: { YES: number; NO: number };
initialPool: { YES: number; NO: number };
initialProbability: number;
prob: number;
probChanges: { day: number; week: number; month: number };
totalLiquidity: number;
subsidyPool: 0;
};
export type DPMContract = Contract & DPM;
export type BinaryContract = Contract & Binary;
Extend AnyContractType to include (DPM & Binary) and extend MarketContract / tradingAllowed predicates so DPM is considered a trading mechanism. Also audit helper unions that currently assume all binary trading markets are cpmm-1, such as common/src/calculate.ts, web/components/contract/contract-price.tsx, web/components/bet/user-bet-summary.tsx, and web/components/bet/limit-orders-table.tsx.
Pure functions for DPM math. No state, no I/O.
export const dpmCost = (y, n) => Math.sqrt(y * y + n * n)
export const getDpmProbability = (pool: { YES: number; NO: number }) =>
pool.YES ** 2 / (pool.YES ** 2 + pool.NO ** 2)
export const dpmInitialPool = (ante: number, initialProb: number) => ({
YES: ante * Math.sqrt(initialProb),
NO: ante * Math.sqrt(1 - initialProb),
})
// b = sqrt((y+s)^2 + n^2) - sqrt(y^2 + n^2); solve for s
export const dpmBuyShares = (pool, outcome, amount): {
shares: number; newPool: typeof pool
} => { ... }
// Max YES shares that can be bought before prob reaches p*:
// s_max = n * sqrt(p*/(1-p*)) - y (symmetric for NO)
export const dpmSharesToReachProb = (pool, targetProb, outcome): {
shares: number; cost: number
} => { ... }