Testnet values — not real money. Any concrete feeRateBps,
k, or bps numbers quoted on this page apply to the ADI-testnet
deployment (chainId 99999) and are placeholder rates used for
partner integration and load-testing — no real money flows. The
formula (fee = k × P × (1 − P) × outcomeTokens) and the
integration contract (market.feeTakerBps as the canonical signing
input) are stable across environments.
fee = k × P × (1 − P) × outcomeTokens
P is the execution price, 0 < P < 1.
k is a period-specific coefficient.
outcomeTokens is the number of outcome tokens traded.
- The curve peaks at
P = 0.5 (max uncertainty) and decays to 0 at
the extremes.
- Both sides of every fill pay this fee.
CTFExchange._chargeFee
fires on each leg of _matchOrders / _fillFacingExchange — the
same feeRateBps value the user signed into the EIP-712 Order
is deducted from the asset they’re receiving (collateral on SELL,
outcome tokens on BUY). Earlier docs claimed “maker fee is always 0”;
that matched a transitional MVP path where signed orders carried
feeRateBps = 0. As of the on-chain-fees rollout, every signed
Order carries the live feeTakerBps and the contract takes the
fee on both legs.
Why quadratic (not linear)?
The linear min(P, 1−P) curve (Polymarket’s default) is twice as
expensive at the top of the book. For tail markets the linear curve
still charges meaningful fees; the quadratic curve naturally eases to
zero.
Holding feeRateBps constant, the curves compare as follows
(illustrative — the absolute % depends on the rate set on the market):
| P | Linear peak | Quadratic (PS) | Ratio |
|---|
| 0.05 | 100% of peak | 19% | 5.3× lower |
| 0.25 | 100% of peak | 75% | 1.33× lower |
| 0.5 | 100% | 100% | 1× (curves meet) |
| 0.75 | 100% of peak | 75% | 1.33× lower |
| 0.95 | 100% of peak | 19% | 5.3× lower |
Effective feeRateBps ↔ k: feeRateBps = k × 10_000. Per-wallet
fee tier (and the resulting makerBps / takerBps) is available at
GET /api/me/fee-tier.
Three fee surfaces — which one to use
The API exposes three fee values; partners often ask which one
“actually applies.” Short answer:
| Surface | Where it comes from | What it means | Use it when |
|---|
market.feeTakerBps | GET /api/markets/{symbol} | The per-market on-chain fee that the EIP-712 Order MUST be signed with — CTFExchange reconstructs the canonical Order with this value and verifies your signature against it. Mismatch ⇒ bad_signature. | Always — for signing. This is the only one that goes into Order.feeRateBps in the signed struct. |
/api/platform/fee-config | Platform period policy | The platform-level current period rate: {currentPeriod, feeMakerBps, feeTakerBps}. Same value for every wallet, set by policy. Mostly informational — surfaces the active period so partners can label fee disclosures. | UI/disclosure only. Don’t use for signing — the per-market value (market.feeTakerBps) is the source of truth and may briefly differ around period boundaries until the market refreshes. |
/api/me/fee-tier | Per-wallet VIP tier | Your effective post-discount rate: {tier, makerBps, takerBps} after VIP / volume tier discount. Lower than the platform default for tier-1+ wallets. | Reporting what you actually paid in /api/me/fees; UI display of “your tier”. Don’t use for signing. |
Only market.feeTakerBps belongs in the signed Order. Signing with
the platform period rate or your tier rate produces a digest that
doesn’t match the CTFExchange reconstruction → bad_signature on
every order. Always pull feeTakerBps from the per-market response
and pass it into your signing helper unmodified.
A worked example for a tier-1 partner on a market with
feeTakerBps = 400:
| Surface | Value |
|---|
market.feeTakerBps | 400 ← sign with this |
/api/platform/fee-config.feeTakerBps | 400 (matches market for steady-state markets) |
/api/me/fee-tier.takerBps | 350 (your effective post-discount rate) |
The on-chain charge uses 400 (because that’s what the signed Order
carries); the per-fill /api/me/fees row records 350 (your
post-discount rate after the platform refunds the difference via the
rebate ledger). The two values reconcile in the rebate accrual, not
at fill time.
Charging direction
The asset the fee is taken in is determined by which side of the fill
each party held — the same rule applies regardless of maker/taker
role:
- Seller (whichever side is sending outcome tokens for collateral):
fee deducted from collateral proceeds.
fee_collateral = k × P × (1−P) × outcomeTokens.
- Buyer (whichever side is sending collateral for outcome tokens):
fee deducted in outcome tokens.
fee_tokens = k × (1−P) × outcomeTokens.
Both give the same USD value for the same fill — just denominated
in the asset that side is receiving. On a single match the contract
charges both legs; per-fill /api/me/fees carries one row per
denomination per trade (unit ∈ {USDC, OUTCOME_TOKENS},
role ∈ {maker, taker}).
How fees appear in the response
Every Trade object carries:
{
"id": "...",
"price": "0.52",
"quantity": "100",
"fee": "0.2496",
"side": "buy"
}
For per-fill audit, use GET /api/me/fees.
On-chain equivalence
The off-chain fee matches the on-chain fee charged by CTFExchange
at settlement time, modulo integer-division rounding (≤ 1 atomic USDC
unit). Same feeRateBps value signs into the EIP-712 Order.
Fee cap
Contract enforces feeRateBps ≤ MAX_FEE_RATE_BIPS (1000). Peak-at-0.5
at the cap is 2.5%. Raising requires Board approval and redeploy.
Examples
// BUY 100 YES at P=0.52 with k=0.04 (feeRateBps = 400)
const price = 0.52;
const quantity = 100;
const k = 0.04;
const feeTokens = k * (1 - price) * quantity; // 1.92 YES tokens
const feeUsd = k * price * (1 - price) * quantity; // 1.00 USDC equiv
// SELL 100 YES at P=0.80 with k=0.014 (feeRateBps = 140)
const feeUsd = 0.014 * 0.80 * 0.20 * 100; // 0.224 USDC
Rebates
The schedule covers fees. Mirror-image rebates to market
makers are a separate system (PS-MM Rebate Model v7 §2). Rebates
settle monthly. Contact
partners@predictstreet.com.