Skip to content

Trader Guide

The end-to-end path for placing a parlay. Unlike CLOB orders, parlays are placed by sending a Solana transaction yourself — the Platform API mirrors the resulting state for reads, but it does not accept the bet.

A leg is one market plus a YES/NO pick. Between 2 and 10 of them.

Before adding a market, check it’s eligible:

  • Status open, and not past end_time
  • Old enough to have a full TWAP window of price history — at least one hour, and never less than the averaging window
  • Outcome probability inside the configured band (hard bounds: 1%–99%)
  • Deep enough and traded enough to price — the vault sets minimum liquidity and volume floors
  • Traded recently enough that the TWAP isn’t stale
  • Not the same market as another leg
  • Not in the same event as another leg

Every threshold above is admin-tunable config, not a constant. Read them from the parlay config account rather than hardcoding — the deployed floor is not necessarily the hard bound.

The webapp greys out ineligible markets before you can add them. If you’re building your own client, mirror these client-side — every one is also enforced on-chain, but failing there costs a transaction.

→ Leg Eligibility

You cannot put two legs from the same event on one slip. “Team A wins” and “Team A wins by 2+” are almost the same bet; multiplying their probabilities as if independent would price the slip far below its true risk. The program rejects it with SameEventCorrelation.

combined = p₀ × p₁ × … × pₙ
multiplier = 1 / combined
payout = stake × multiplier
LegsEach atCombinedMultiplier
250%0.254×
350%0.1258×
350% / 40% / 25%0.0520×
430%0.0081123×
520%0.000323,125×

The multiplier compounds fast. So does the chance of missing: five legs at 20% each land together 0.032% of the time.

Within the configured bounds — typically $1 minimum. The resulting payout must clear config.max_payout and fit the vault’s exposure headroom, so a large stake on a long slip can be rejected even when the stake itself is fine.

If you hit ExposureLimitExceeded, reduce the stake. The vault is protecting its solvency, not rejecting you.

create_parlay takes a min_payout slippage floor. If the computed payout comes out below it, the transaction reverts with SlippageExceeded rather than filling you at a worse multiplier.

Send a create_parlay transaction to the high-market-parlay program.

Parlay PDA seeds = ["parlay", user_pubkey, user_seq_le_bytes]
ParameterMeaning
legsVec<LegInput> — one { selected_outcome } per leg
stakeMicro-USDC
min_payoutYour slippage floor
user_seqYour per-user sequence number

The market references travel in remaining_accounts, length exactly 2 × legs.len():

RangeAccounts
[0..N]Market accounts, in leg order
[N..2N]MarketEventLink PDAs, derived per leg’s market_id

The PDA is keyed on (user, user_seq), deliberately — two users can both create their first parlay in the same slot without racing a global counter. But you can race yourself: two browser tabs both computing max(existing) + 1 will collide.

Handle it: on an “account already in use” error, increment user_seq and retry. The webapp retries up to four times.

The stake transfers from your USDC ATA into the parlay vault in the same transaction.

Terminal window
curl -s https://prod-api.gomarket.io/api/parlays/user/ \
-H "Authorization: Bearer $JWT"
curl -s https://prod-api.gomarket.io/api/parlays/user/?status=active \
-H "Authorization: Bearer $JWT"
curl -s https://prod-api.gomarket.io/api/parlays/{parlay_id}/ \
-H "Authorization: Bearer $JWT"

The detail endpoint is scoped to the caller — you cannot read someone else’s slip, and a parlay that isn’t yours returns the same 404 as one that doesn’t exist.

StatusMeaningClaimable
activeAt least one leg still pending—
wonAll legs terminal, none lostyes — payout
lostA leg lost; nothing to collectno
voidedEvery leg’s market cancelledyes — stake refund
refundedAdmin already paid the stake backno
claimedPaid outno

voided and refunded are easy to confuse: voided still owes you a claim, refunded has already been settled.

Legs don’t settle themselves. Once a leg’s market is Resolved or Cancelled, anyone can call settle_leg(leg_index) — you, an operator, an indexer.

ErrorMeans
MarketNotResolved (6013)Too early — the market isn’t terminal yet
LegNotPending (6033)Already settled; harmless

Settling is idempotent through that second check, so a retry loop is safe.

claim_parlay is signed by you and accepts two statuses:

Won fee = floor(potential_payout × fee_bps_at_create / 10_000)
net = potential_payout − fee
Voided net = stake fee = 0 // every leg's market cancelled

The vault pays net to your USDC ATA and, for a winner, fee to the treasury in the same transaction. Status flips to Claimed.

fee_bps_at_create is snapshotted when you placed the slip — an admin fee change in between cannot touch what you pay.

ErrorCodeWhat to do
ExposureLimitExceeded6011Reduce the stake — the vault is near its limit
PayoutExceedsMax6010Reduce the stake or shorten the slip
StakeTooLow / StakeTooHigh6008/6009Stake outside the configured bounds
MarketNotOpen6012A leg’s market closed while you were building
ProbabilityOutOfRange6014A leg sits outside the configured band
SameEventCorrelation6005Two legs from one event
DuplicateMarket6004Same market twice
SlippageExceeded6042The TWAP price moved past your min_payout
Paused6000Parlay creation is paused
TooFewLegs / TooManyLegs6002/6003Outside 2–max_legs

Full list: Error Codes.

The Platform API serves curated templates:

Terminal window
curl -s https://prod-api.gomarket.io/api/markets/parlay-suggestions/

In the webapp these are previews. Demo legs carry a synthetic negative market ID and no market address, and are blocked from submission — they exist to show the shape of a combo, not to be placed.

  • Be sold or exited. No secondary market, no partial close. Once placed, it runs to settlement.
  • Be transferred. The Parlay account is bound to your pubkey; only you can claim.
  • Be repriced. probability_at_entry is written once. Later market moves don’t change your multiplier, in either direction.