FAQ — Frequently Asked Questions#
Common questions about trading, fees, oracle behavior, and the protocol.
General#
What is Seesaw?#
Seesaw is a permissionless, non-custodial binary prediction-market protocol on Solana. See the docs landing page for a full product overview.
How do binary prediction markets work?#
You buy YES shares if you think a price will be UP by the end of a window, or NO shares if you think it will be DOWN. Winning shares pay 1 USDT each; losing shares pay 0. For the full mechanics see Understanding Shares and How It Works.
Trading#
How do I place an order?#
- Connect a Solana wallet (Phantom, Solflare, or Backpack).
- Pick a market — SOL/USD, BTC/USD, or ETH/USD are the seed markets at launch.
- Select YES (price UP) or NO (price DOWN).
- Enter quantity (shares) and limit price (0.01–0.99 USDT per share).
- Click Buy and approve the transaction.
Your cost is price × quantity. Buying 100 YES at 0.60 USDT requires 60 USDT
in collateral (plus a small taker fee if your order fills immediately).
What order types are available?#
| Type | Behaviour |
|---|---|
| Limit | Rests on the book at your price if not immediately matchable |
| PostOnly | Rests on the book; rejected if it would match immediately |
| ImmediateOrCancel (IOC) | Fills what it can right now; the unfilled remainder is cancelled |
IOC orders are exempt from the maker price band.
What happens if my order does not fill?#
A Limit or PostOnly order rests on the book until another trader matches it, you
cancel it, or the market's trading period ends. Resting orders that exceed their
per-order TTL (max_age_seconds) become reclaimable via ReclaimExpiredOrder.
Important: unfilled bid collateral is not automatically returned when the market closes. Cancel the order while trading is live to get it back immediately, or it is released together with your payout when you redeem after resolution. See What if I forget to claim?.
Fees#
What fees do I pay?#
Seesaw charges a taker-only, capped-linear-decay fee. Makers (resting orders) pay nothing.
fee_bps(p) = min(fee_cap_bps, decay_rate_bps × (10_000 − p) / 10_000)
With default parameters (fee_cap_bps = 200, decay_rate_bps = 600):
| Fill price | Effective taker fee |
|---|---|
| 0.00–0.67 USDT | 2.00% (capped) |
| 0.80 USDT | 1.20% |
| 0.90 USDT | 0.60% |
| 0.95 USDT | 0.30% |
| 0.99 USDT | 0.06% |
The fee is rounded UP; payouts are rounded DOWN — both in the protocol's favor.
Where do fees go?#
Every taker fee is split three ways:
| Recipient | Share |
|---|---|
| Protocol treasury | 50% (routed to one of 8 treasury_recipients shards) |
| Market creator | 10% (accrues in market; claimed via ClaimCreatorFees) |
| Referrer | 40% (accrues in on-chain earnings account; if no referrer, flows to protocol) |
The 8-shard protocol treasury means a single key compromise drains at most 1/8 of accumulated protocol revenue. See Flow of Funds for the full accounting.
Referrals#
How does the referral program work?#
- Click a referral link or call
SetReferrer (0x21)directly. - An on-chain
ReferralAccountis created, binding you to that referrer for 365 days — first-touch, immutable until expiry. - Every taker fee you pay during that period routes its 40% referral share to
your referrer's
ReferrerEarningsAccount.
Referrers call ClaimReferrerEarnings (0x24) to withdraw accumulated earnings
at any time — no minimum balance, no cooldown.
Without a referrer, the 40% referral portion defaults to the protocol treasury. Your total fee is the same either way.
Settlement and Claiming#
When do markets resolve?#
After the market's duration window closes (e.g. 15 minutes for the default):
- Anyone calls
SnapshotEnd (0x04)to capture the end price from Pyth. - Anyone calls
ResolveMarket (0x05)to compare end vs. start price and record the outcome: UP if end ≥ start, DOWN otherwise (equality resolves UP). - Winners redeem for 1 USDT per share via
Redeem (0x1A).
All lifecycle cranks are permissionless — anyone can execute them.
What if I forget to claim after a market resolves?#
This is the most important risk to understand.
Winnings are NOT pushed to your wallet automatically. You must call Redeem.
The forfeiture timeline:
- Market resolves.
- 7 days after
resolved_at:CloseMarket (0x1E)becomes eligible. - Before
CloseMarketruns, the permissionlessMarkPositionSettled (0x1C)crank pays each abandoned position's owed value directly to the owner's settlement token account. This is your safety net — third parties can trigger it on your behalf, and it pays owners, not the creator. CloseMarketexecutes: the market is torn down. Any residual dust still in the vault (from rounding, donated tokens, or positions that could not be settled for other reasons) is swept to the market creator's stablecoin ATA.
Safe habit: redeem every resolved market within a few days. Don't leave positions or resting orders abandoned in finished markets. See Flow of Funds for the full timeline.
What if the oracle is down at settlement time?#
If the Pyth oracle cannot supply a usable post-boundary price by
t_end + market_expiration_window_seconds (default 7 days after the
market's end time), the market transitions to Expired via
ExpireMarket (0x06). In that state:
- Both YES and NO shares redeem at 0.50 USDT each (50/50 split).
- Unfilled bid collateral is released.
- Funds are never stranded by an oracle outage.
Orders and Price Band#
Why was my order rejected?#
Several conditions can cause a resting order to be rejected:
| Error | Code | Cause |
|---|---|---|
OrderPriceOutOfBand | 0x3014 | Resting price is outside the maker price band |
PostOnlyModeActive | 0x300B | Market is in PostOnly mode; Limit and PostOnly orders are still accepted |
ProtocolPaused | 0x6001 | Protocol is paused; only settlement instructions remain available |
WouldCross | 0x3004 | A PostOnly order would immediately match |
TradingEnded | 0x1004 | Market's trading period has closed |
OrderbookFull | 0x3002 | 63 resting orders per side limit reached |
MinRestingNotionalNotMet | 0x3013 | Remaining quantity after partial fill is below the minimum |
MaxTotalSharesExceeded | 0x300F | Order would push the market over its share cap |
Price band in detail: When both sides of the order book have resting orders, new resting orders must sit within ±1000 bps of the midpoint. When one or both sides are empty, the static bounds apply: bids must be ≥ 100 bps, asks ≤ 9900 bps. IOC orders are exempt — they never rest.
Market emergency status: Each market has an independent emergency status set
by the admin via SetMarketEmergencyStatus (0x2C):
| Status | Effect |
|---|---|
| None | Normal trading |
| PostOnly | Only PostOnly orders may rest; IOC/Limit takers blocked |
| Paused | All order placement blocked; settlement still works |
| ForceCancelOnly | New orders blocked; ForceCancelMarketOrders (0x2D) can cancel all resting orders |
Settlement instructions (Redeem, ForceClose, MarkPositionSettled, etc.)
remain available in all non-None emergency states.
Oracle#
What is the difference between push and pull oracle markets?#
Markets are created in one of two Pyth integration modes:
Push mode (default — oracle_mode = 0):
- The market stores the address of a Pyth-maintained on-chain feed account.
- At snapshot time, the crank reads the latest price from that account.
- Validation: address binding + owner ==
config.pyth_program_id.
Pull mode (oracle_mode = 1):
- The market stores
pyth_feed = [0u8; 32](the pull indicator — no address). - At snapshot time, anyone posts an ephemeral
PriceUpdateV2produced by the official Pyth Solana Receiver program (via Pyth Hermes) to the transaction, and the program validates feed-id + owner ==config.pyth_receiver_program_id. - Pull mode makes Sampling Rule A firstness deterministic — Hermes can fetch exactly the first post-boundary price update, so there is no ambiguity about which sample qualifies.
Both modes resolve with the same rule: UP if end ≥ start, DOWN if end < start.
For technical background see Design → Price Oracle.
Is there a planned Pyth upgrade I should know about?#
Pyth is upgrading its Core programs on 2026-07-31. Seesaw handles this via an on-chain two-step governance flow:
ProposePythProgramId (0x30)arms the update (requires protocol authority).- A 48-hour timelock elapses.
ApplyPythProgramId (0x31)applies the new program ID.
target = 0 re-points the push feed PDA derivation program; target = 1 re-points the Pyth
Receiver. No program redeploy is required. Active markets are unaffected mid-life.
SDK#
What SDK should I use to integrate?#
Two families:
Trust-based SDKs (convenience; indexer/API in the read path):
- TypeScript:
@seesaw/core(@solana/kitv6) - Python:
seesaw-sdk(PyPI, solders-based) - Rust:
seesaw-sdk(crates.io)
Trustless SDK (no indexer in the trust path; resolves everything from any Solana RPC; fail-closed; slot-anchored):
- TypeScript:
@seesaw/trustless - Python:
seesaw-trustless - Rust:
seesaw-trustless
See Trustless SDK Guide for the indexer-free integration.
Security#
Is the protocol audited?#
The Seesaw on-chain program has not undergone a human third-party security audit as of the current release. Security measures in place include:
- ~1,628 unit tests and ~49 property tests (as of 2026-05-19)
- Fuzz testing (36 targets via
cargo-fuzz) - Mutation testing (
cargo-mutants) - Kani formal verification proofs
- Runtime invariant checks (solvency, no-crossed-book, conservation)
- All arithmetic uses checked operations; rounding always favors the protocol
This status will be updated when a human third-party audit is completed. Trade accordingly — smart-contract risk is real. See For Traders → Risks for the full risk disclosure.
Common Error Codes#
See Error Codes for the complete, categorized reference. Common codes you may encounter while trading are listed there alongside their causes and recovery actions.
Getting Help#
If your issue is not covered here:
- Check the Troubleshooting guide.
- Search Discord — many issues have been discussed before.
- Post in #support with: error code, transaction signature, wallet type, browser, and steps to reproduce.
- Open a GitHub issue for bugs or feature requests.