Threat Model#
Detailed analysis of attack surfaces and mitigations.
Adversary Model#
Capabilities#
| Capability | Description | Risk Level |
|---|---|---|
| Arbitrary Transactions | Can submit any valid Solana tx | High |
| Sybil Accounts | Can create unlimited wallets | Medium |
| Flash Loans | Large capital for single tx | Medium |
| MEV Extraction | Frontrun/sandwich attacks | Medium |
| Market Manipulation | Trade to move prices | Medium |
Limitations#
| Limitation | Assumption | Confidence |
|---|---|---|
| Forge Signatures | Ed25519 secure | Very High |
| Break Pyth | Oracle honest | High |
| Control Solana | Network honest | High |
| Infinite Capital | Finite per tx | High |
Trusted and Semi-Trusted Roles#
| Role | Trust Level | Powers | Explicit Non-Powers |
|---|---|---|---|
| Config authority | Semi-trusted | Bounded governance updates, fee/treasury configuration, pauser rotation, emergency ops | Cannot seize user collateral; token destinations remain validated by instruction |
| Pauser guardian | Semi-trusted | Global Pause; may impose or raise per-market emergency severity | Cannot Unpause, cannot call SetPauser, cannot clear or lower emergency severity |
| Market creator | Limited | Market-scoped cap updates and creator-scoped PostOnly toggles | Cannot impose admin Paused/ForceCancelOnly; cannot redirect refunds |
| Crank operators | Permissionless | Lifecycle transactions and closer-reward collection | Cannot change outcomes or user destinations |
Attack Surfaces#
1. Instruction-Level Attacks#
| Instruction | Attack Vector | Mitigation |
|---|---|---|
| create_market | Spam creation | Rent cost limits |
| place_order | Orderbook manipulation | Solvency constraints |
| place_order | Self-trade wash | Self-trade prevention |
| cancel_order | Unauthorized cancel | Owner signature |
| snapshot_* | Oracle manipulation | Pyth security |
| resolve_market | Wrong outcome | Deterministic logic |
| redeem | Double claim | Settled flag |
| close_market | Premature close | All-settled check |
2. Economic Attacks#
| Attack | Description | Mitigation |
|---|---|---|
| Naked Short | Sell without owning | Balance validation |
| Undercollateralized Buy | Buy without funds | Collateral locking |
| Oracle Front-running | Trade on future prices | Pyth latency |
| Market Corner | Accumulate to manipulate | No position limits (v1) |
3. Protocol-Level Attacks#
| Attack | Description | Mitigation |
|---|---|---|
| Reentrancy | Callback during execution | Solana locks, no callbacks |
| Integer Overflow | Arithmetic overflow | Checked math |
| PDA Collision | Same PDA, different purpose | Unique seed prefixes |
| Account Confusion | Wrong account type | Discriminator checks |
| Rent Drain | Force account closure | Rent-exempt accounts |
| Issuer Freeze | Settlement token issuer freezes a market vault or user ATA | Accepted settlement-asset tail risk; monitor, contain with global/per-market emergency controls and shard rerouting, and require an audited migration for any mint change (MINT-01) |
4. Operational Attacks#
| Attack | Description | Mitigation |
|---|---|---|
| Crank DoS | Prevent lifecycle | Multiple operators |
| Treasury Drain | Exhaust crank rewards | Capped rewards |
| State Bloat | Create many accounts | Rent costs |
| Pauser Key Abuse | Escalate halt state unnecessarily | Pauser cannot unpause, clear, or lower emergency state; authority rotation runbook |
Detailed Mitigations#
Reentrancy Protection#
Solana's runtime provides inherent protection:
- Account Locking: All accounts locked for transaction
- No Recursive CPI: Cannot borrow mutably twice
- No Token Callbacks: Unlike ERC-777, SPL Token has no hooks
- Idempotency: Settlement checks
settledflag first
Oracle Security#
Price validation applies the following checks to every oracle reading before it is accepted:
- Positive price: the price must be greater than zero, otherwise the reading is rejected (
InvalidPrice). - Published after the boundary: the reading's
publish_timemust be at or after the relevant snapshot boundary, otherwise it is rejected as stale (StaleOracle). The reading'sprev_publish_timeis also used to prove it is the first price at or after the boundary (Sampling Rule A). - Optional confidence gate: when a confidence limit is configured, the ratio of the reading's confidence interval to its price must fall within that limit, otherwise the reading is rejected.
The protocol reads the Pyth PriceUpdateV2 account directly at fixed byte offsets (it does not depend on a Pyth client SDK), verifies the account's discriminator and verification level, then extracts the price, confidence, exponent, and timestamps before applying the checks above.
Solvency Enforcement#
Invariant:
vault.balance >= max(total_yes_shares, total_no_shares) + accumulated_creator_fees + accumulated_referral_fees
Known Considerations and Mitigations#
This section calls out subtle risk areas integrators sometimes ask about, and how the protocol handles each.
Oracle Price Staleness#
Consideration: Stale Pyth prices could affect how a market resolves.
How it is handled: Sampling Rule A requires the first price at or after each boundary:
- Start snapshot:
publish_time >= t_start - End snapshot:
publish_time >= t_end
A reading published before the boundary is rejected, and the candidate's predecessor timestamp is used to confirm it is the first qualifying price.
Settlement Double-Claim#
Consideration: A user might try to settle the same position more than once.
How it is handled: Each position carries a settled flag that is checked first; once settled, further settlement calls are no-ops with no additional payout. See the idempotency code in Architecture Security → Reentrancy Protection.
Crank Liveness#
Consideration: A market could stall if no one performs its lifecycle operations (snapshotting, resolution, settlement).
How it is handled: All lifecycle operations are permissionless and reward-incentivized, so anyone can perform them, and multiple independent operators are expected. A force-close fallback exists for positions in markets that are not resolved within the expiration window.
Settlement-Token Issuer Freeze#
Consideration: The current Solana beta uses a single settlement mint per market. If that mint has an active freeze authority, the issuer can freeze a market vault PDA or a user's associated token account. A frozen treasury recipient can be bypassed by routing protocol fees to another configured recipient shard, but a frozen market vault or payout destination blocks the affected SPL Token transfer until the issuer unfreezes the account.
How it is handled: This is an accepted settlement-asset tail risk, not an on-chain bug. The program cannot override an issuer freeze. Operators must:
- monitor live market vault addresses and known payout/treasury addresses against the issuer's freeze or blacklist surface;
- contain affected markets with global
Pause (0x25)when the default mint underpins all live markets, or with per-marketSetMarketEmergencyStatus (0x2C)/ post-only mode for a scoped freeze, while preserving unaffected markets; reserved discriminator0x32rejects every payload and provides no mint-rotation lever; - communicate that affected redemptions or fee claims are blocked by the settlement token program until unfreeze, while unrelated markets continue normally;
- treat per-asset settlement allowlists and multi-mint risk controls as a
v-next structural improvement. Any future default mint change requires an
audited program/config migration that atomically updates
config.default_settlement_mint, all eightconfig.treasury_recipients, and the eight referrer-treasury shard PDAs; see the MINT-01 asset-registry design.
Expired-Order Lane Starvation#
The maker-band probe skips at most MAX_MATCH_LIMIT (12) expired nodes per
lane and fails closed with InvalidState when more remain, so an actor who
rests 13 minimum-notional orders at a lane head and lets them expire can
block Limit/PostOnly placement until ReclaimExpiredOrder clears them.
Mitigations: IOC placement never runs the probe (takers are unaffected);
MIN_ORDER_TTL_SECONDS (60) forces the attacker to lock capital for at
least a minute per cycle; reclaim is permissionless and pays a bounty, and
operator keepers watch for ExpiredClaimable nodes. Residual: a funded
attacker can repeat the cycle; the cost is bounded by 13 × min notional per
minute per market.
Epoch Boundary Race#
Consideration: Clock drift near epoch boundaries could create timing gaps.
How it is handled: Boundaries use inclusive comparisons (>=), so there is no gap between consecutive epochs.
Price Conversion Precision#
Consideration: Converting NO-side quotes to the canonical YES book could lose precision.
How it is handled: Conversions use exact integer arithmetic with protocol-favorable rounding, backed by extensive test and property-test coverage.
Known Limitations (v1)#
| Limitation | Description | Future |
|---|---|---|
| No position limits | Unlimited accumulation | Add optional limits |
| Single oracle | Only Pyth | Multi-oracle support |
| Deep orderbook tiers | 64-4096 orders per side per market | Add larger tiers after measured demand |
| Full collateral | No leverage | Add margin |
-
Mirror-market timeline drift. A Kalshi market's on-chain close can be extended repeatedly (
0x56), but each extension needs its own verified claim signed and submitted before the market's current close. A LeftOpen halt (0x55) stops trading but does not foreclose settlement: a later finalized claim still resolves the market at its true outcome. Once a close passes with no further extension, the mirror can only be resolved by a finalized claim that clears the finality bound. If Kalshi finalizes afterclose_ts + max(config.market_expiration_window_seconds, 30 days), anyone may expire the mirror at 50/50 (0x4D) even though the source outcome is known. Traders on mirror markets accept this residual par-settlement risk. -
Mirror-market settlement trusts a threshold of Reclaim's attestors. The standalone verifier accepts a claim only when exactly
thresholddistinct members of the active epoch snapshot's member set sign it, and Seesaw independently recomputes the signer-set commitment from its own read of that snapshot plus the receipt's signer bitmap. The shipped configuration is 1-of-3: the three attestor addresses Reclaim publishes, threshold 1. All three are Reclaim-operated keys — Reclaim's decentralized attestor AVS is Holesky-only and whitelisted as of 2026-09 — so the trust root is Reclaim as an operator, and compromise of any one published key can resolve any mirror market arbitrarily. Raising the threshold is a verifier-governance staging action behindmin_governance_delay_sand needs no program change, but a genuine reduction in this risk also needs independently operated attestors.
What to Expect If an Issue Occurs#
Because every protocol rule is enforced on-chain, a transaction that would violate an invariant simply fails rather than committing bad state. For broader issues, the protocol exposes safety controls and a disclosure process you can rely on:
- Trading can be halted. The protocol can be paused globally (
Pause) or restricted per market (SetMarketEmergencyStatus, including a post-only mode that lets makers cancel but blocks new aggressive flow). The separate pauser guardian is escalation-only: it can halt or make a market more restrictive, but it cannot unpause, clear emergency state, or rotate itself. Pausing stops new trading while a fix is prepared; it does not seize user funds. - Funds remain fully collateralized. Every share is backed by collateral held in the market vault, and solvency is enforced at runtime, so a pause or delay does not put deposited collateral at risk.
- Markets have a force-close fallback. Positions in a market that is not resolved within its expiration window can be force-closed permissionlessly, so funds are not stranded if normal lifecycle operations stall.
- Vulnerabilities are handled under responsible disclosure. If you find a security issue, report it privately (see Reporting Vulnerabilities) and allow time for a fix before any public disclosure.
Next Steps#
- Review Invariants for formal guarantees
- See Architecture Security for implementation