# Fora Markets — Full Documentation Source: https://sharp.xyz/llms-full.txt Index: https://sharp.xyz/llms.txt This file concatenates every page under https://sharp.xyz/docs into a single markdown document. Each section below is delimited by an H1 with the canonical URL of that page. # https://sharp.xyz/docs/api # REST API Base URL: **`https://api.fora.co`** The API has three layers: 1. **Public read endpoints** — markets, trades, prices. No auth. 2. **Transaction-build endpoints** — return an unsigned Solana transaction for the client to sign and submit. **Privy JWT required.** 3. **Tinybird data proxy** — historical OHLCV, price series, leaderboards. Public. For real-time order book and trade streams, see the WebSocket at `wss://fire.fora.co` (separate reference, in progress). ## Authentication Transaction-build endpoints (everything under `/tx/build/*`) and admin endpoints (resolve, airdrop, user settings) require a Privy JWT in the `Authorization` header: ``` Authorization: Bearer ``` You get the token by signing in via [Privy](https://www.privy.io) — the same auth flow that powers sharp.xyz. The Sharp app ID is `cm9ld05e2008wji0mmils1a2y`. ## Public read endpoints ### Markets | Method | Path | Returns | |---|---|---| | `GET` | `/markets` | List all markets (filterable) | | `GET` | `/markets/{ticker}` | Single market detail | | `GET` | `/markets/{ticker}/status` | Lifecycle state + winning outcome | | `GET` | `/markets/{ticker}/twap` | Current TWAP for Vibe markets | | `GET` | `/markets/{ticker}/twap-series` | TWAP history | | `GET` | `/markets/events` | List events (groups of related markets) | | `GET` | `/markets/events/{event_ticker}` | Event detail | | `GET` | `/orderbook/{ticker}` | Current YES + NO order books | | `GET` | `/orders/{ticker}/{trader}` | Open orders for a trader on a market | ### Trades | Method | Path | Returns | |---|---|---| | `GET` | `/trades` | Recent trades (filterable by market, time range) | ### Search & discovery | Method | Path | Returns | |---|---|---| | `GET` | `/search` | Full-text search across markets and events | | `GET` | `/category/{category}` | Markets by category | | `GET` | `/recommend/{user_id}` | Personalized market recommendations | ### Health | Method | Path | Returns | |---|---|---| | `GET` | `/health` | `"OK"` if alive | | `GET` | `/airdrop/collateral_mint` | Returns the configured USDC mint pubkey | ## Transaction-build endpoints All require Privy JWT. All return an unsigned base64-encoded Solana transaction the client signs and submits to the network. | Method | Path | Builds | |---|---|---| | `POST` | `/tx/build/swap` | Cross-market swap (YES → NO or vice versa) | | `POST` | `/tx/build/order` | Limit or market order on a Manifest book | | `POST` | `/tx/build/claim_seat` | Claim a Manifest seat on a YES or NO book | | `POST` | `/tx/build/deposit` | Deposit collateral to a market | | `POST` | `/tx/build/cancel_order` | Cancel an open order | | `POST` | `/tx/build/create-market` | Create a new prediction market | | `POST` | `/tx/create-usdc-ata` | Create the user's USDC associated token account | ### Admin endpoints Authority-only. Privy JWT must belong to an account with the corresponding role. | Method | Path | Action | |---|---|---| | `POST` | `/markets/resolve` | Resolve a market (manual mechanism) | | `POST` | `/markets/{ticker}/resolve-and-close` | Resolve + close in one call | | `GET` | `/airdrop` | Devnet/localnet USDC airdrop | | `GET` | `/users` | List all users | | `GET` | `/user/{id}/settings` | Per-user settings | ## Historical data (Tinybird proxy) | Method | Path | Returns | |---|---|---| | `GET` | `/data/{pipe_name}` | Generic Tinybird pipe proxy | | `GET` | `/fora/price-series` | Sharp price series (params below) | | `GET` | `/fora/ohlcv` | Sharp OHLCV candles | | `GET` | `/kalshi/price-series` | Mirrored Kalshi price series | | `GET` | `/kalshi/ohlcv` | Mirrored Kalshi OHLCV | ### Required query parameters The Tinybird-backed endpoints take **specific parameter names** that don't match what earlier docs claimed: - `market` *or* `market_ticker` — the market ticker (string). **Not `ticker`.** - `window_minutes` — TWAP/aggregation window in minutes. **Not `interval`.** - `start_ms` / `end_ms` — time bounds in Unix milliseconds. **Not `hours`.** - `slot_min` / `slot_max` — slot bounds for `/trades`. Example: ```bash curl "https://api.fora.co/fora/ohlcv?market=KXMARMAD-26-DUKE&window_minutes=60&start_ms=1714521600000&end_ms=1714608000000" ``` ### Response envelope All Tinybird-backed endpoints return: ```json { "success": true, "data": [ ... ], "meta": { "total": 42, "page": 1, "limit": 100, "has_more": false }, "query": { "market": "KXMARMAD-26-DUKE", "window_minutes": 60, ... } } ``` The `data` array contents depend on the pipe. ## Other | Method | Path | Action | |---|---|---| | `POST` | `/users/sync` | Sync user state from Privy | | `POST` | `/markets/metadata` | Submit market metadata | | `POST` | `/onramp/session` | Start a fiat → USDC onramp session | | `POST` | `/auth/telegram/callback` | Telegram OAuth callback | | `GET` | `/auth/telegram/status` | Check Telegram auth status | | `POST` | `/chat/send` | Send a chat message (publishes to Kafka, broadcasts via WS) | | `POST` | `/events` | Track a frontend analytics event | | `GET` | `/notifications` | List user notifications | | `POST` | `/notifications/read` | Mark a notification read | ## Source The Axum router lives at [`api/src/main.rs`](https://github.com/KaneLabs/fora-markets/blob/main/api/src/main.rs); handlers at [`api/src/handlers.rs`](https://github.com/KaneLabs/fora-markets/blob/main/api/src/handlers.rs). When in doubt, the source is canonical. --- # https://sharp.xyz/docs/cli # Install fora-cli `fora-cli` is the command-line interface to the Sharp protocol. Trade, claim Manifest seats, deposit, query order books, query historical data, and crank lifecycle transitions — all from the terminal. ## Install Requires the Rust toolchain (1.75+). If you don't have it: [rustup.rs](https://rustup.rs). ```bash cargo install fora-cli ``` The binary is named **`fora-cli`** (not `fora`). ```bash fora-cli --version fora-cli --help ``` A precompiled `curl ... | sh` installer is on the roadmap. Until then, `cargo install` is the only path. ## Configure ```bash fora-cli config init ``` This generates `~/.config/fora-cli/config.toml`. Edit it to set your RPC endpoint and signer keypair path. Defaults point at Solana mainnet via the public RPC. For local development against a localnet validator: ```bash fora-cli config set cluster localnet fora-cli local airdrop 1000 ``` The `airdrop` amount is in **whole USDC, not atoms**. `1000` airdrops 1000 USDC. (Earlier docs incorrectly suggested atoms — copying-and-pasting `1000000000` from those docs would have airdropped 1 billion USDC.) ## First trade The full quickstart is on [Quickstart](/docs/quickstart). The shortest version: ```bash # Find a market fora-cli market list # Inspect its books fora-cli market orderbook KXMARMAD-26-DUKE # Claim a seat for the YES side fora-cli trade seat claim KXMARMAD-26-DUKE # Buy 5 USDC of YES at market fora-cli trade buy KXMARMAD-26-DUKE --side yes --amount 5 ``` ## Top-level commands | Command | What it does | |---|---| | `fora-cli config` | Manage the local config file | | `fora-cli wallet` | Generate / import / inspect signer keypairs | | `fora-cli market` | List, create, inspect, and resolve markets | | `fora-cli trade` | Buy, sell, place limit orders, claim seats, deposit | | `fora-cli global` | Manage Manifest globals (advanced; cross-market liquidity) | | `fora-cli fee` | Inspect fee vault state and fee rate | | `fora-cli kalshi` | Mirror Kalshi events as Sharp markets (operator command) | | `fora-cli pipeline` | Sync registry / metadata / backfill (operator command) | | `fora-cli local` | Localnet helpers — airdrops, etc. | Run `fora-cli --help` for the full subcommand list. Most commands accept `--fire-and-forget` to skip waiting for transaction confirmation. ## Source - [`fora-cli/`](https://github.com/KaneLabs/fora-markets/tree/main/fora-cli) — Rust source for the CLI - [`fora-sdk/`](https://github.com/KaneLabs/fora-markets/tree/main/fora-sdk) — the Rust SDK the CLI is built on (currently the only SDK; a TypeScript SDK is on the roadmap) --- # https://sharp.xyz/docs/glossary # Glossary Definitions of every term that's specific to Sharp — and a few inherited from Manifest, Solana, or Kalshi that show up frequently in the docs and code. ## Binary market A prediction market with two mutually exclusive outcomes — YES and NO — and a single $1 USDC backing per token pair. Every Sharp market is binary. Multi-outcome events (e.g. "Who wins the championship?" with N teams) are modeled as N separate binary markets, one per outcome. ## CLOB Central limit order book. Buyers post bids, sellers post asks, the protocol matches them in price-time priority. Distinct from an AMM (automated market maker), where prices are determined by a curve over reserves. Sharp is built on [Manifest](#manifest), a CLOB DEX. ## Cross-market matching When a YES bid at price `p` and a NO bid at price `q` satisfy `p + q ≥ $1.00`, the protocol mints a fresh token pair from the buyers' combined collateral and fills both sides atomically. The `p + q - $1.00` surplus accrues to the protocol fee vault. This is the mechanism that lets two strangers with opposite views — neither of whom holds an existing token — get matched. Without cross-market matching, a YES buyer could only match against an existing NO holder selling. With it, the protocol creates the token pair on demand whenever willingness-to-pay covers the full $1 backing. ## Vibe market A binary market that resolves to the **on-chain TWAP** (time-weighted average price) of its YES book at expiry — no external oracle. Named for the property that the market is its own oracle of itself. Mechanics: - At expiry slot, anyone can crank the resolution. - The protocol reads the accumulated TWAP from the YES book and converts it to basis points (0–10000). - The market enters `DETERMINED` with `winning_outcome = TWAP`. - YES tokens redeem at `TWAP / 10000` of $1; NO redeems at the complement. Vibe markets require **at least 10 distinct-slot fills** (`GIRARD_MIN_FILLS`) to resolve via TWAP. With fewer fills, the market resolves as `SPLIT` (both sides at $0.50) — protection against manipulation on a thin book. The design avoids the trust assumption of every other prediction market protocol: there's no oracle to bribe, no authority to capture, no settlement source to attack. The cost is that the market only works for events whose outcome is reflected in pricing during the trading window — sports outcomes don't fit; *probability of an event happening by time T* does fit. See [Market Lifecycle → Resolution mechanisms](/docs/lifecycle#resolution-mechanisms) for the on-chain flow. ## Kalshi oracle The Sharp service that resolves markets which mirror a [Kalshi](https://kalshi.com) event. Subscribes to Kalshi's lifecycle WebSocket; when an upstream Kalshi market settles, the service submits a resolution instruction with the matching outcome. Source: [`kalshi-oracle/`](https://github.com/KaneLabs/fora-markets/tree/main/kalshi-oracle). The on-chain instruction is permissionless once Kalshi has emitted the settlement event — anyone can crank, the oracle just races to do it first. ## Lifecycle The deterministic state machine every market moves through: ``` INITIALIZED → ACTIVE ⇄ PAUSED → DETERMINED → CLOSED → SETTLED → CLEANED → ARCHIVED ``` See the dedicated [Market Lifecycle](/docs/lifecycle) page. ## Manifest The formally-verified central limit order book that Sharp builds on. Maintained by [CKS Systems](https://github.com/CKS-Systems/manifest). Each Sharp market wraps two Manifest markets — one for YES/USDC and one for NO/USDC — and adds the cross-market matching layer plus the Sharp-specific lifecycle. ## Maker rebate 10 basis points (0.10%) of the taker's quote-token amount, credited to the maker on every fill. Funded out of the protocol fee. See [Protocol fee](#protocol-fee). ## Mechanism The resolution mechanism for a market. Stored as a `u8` field on the market state: | Value | Mechanism | Resolved by | |---:|---|---| | 0 | Kalshi | Kalshi oracle service | | 1 | Decentralized | Reserved | | 2 | Manual | Market authority | | 3 | Vibe | On-chain TWAP at expiry | ## Permissionless cleanup Every state transition after `DETERMINED` (CLOSED → SETTLED → CLEANED → ARCHIVED) can be cranked by *any* Solana account, not just the market authority. This means Sharp markets can never get stuck. If the authority disappears, anyone can finish the lifecycle and recover token redemptions. ## Protocol fee 30 basis points (0.30%) of the taker's quote-token amount on every fill. Of that, 10 bps is paid back to the maker as a [maker rebate](#maker-rebate); the remaining 20 bps accrues to the protocol fee vault. Cross-market matching surplus also accrues to the same vault. ## Seat A Manifest concept. To place an order on a Manifest market you need a "seat" — a small account allocated for your order tracking on that book. Seats are claimed on first use and persist until the market is cleaned. The CLI command `fora-cli trade seat claim` allocates one. ## TWAP Time-weighted average price. Sum of `price × slot_duration` divided by total slot duration. On Sharp, the YES book's TWAP is accumulated on-chain (`twap_cumulative`, `twap_last_slot`, `twap_fill_count`) and used to resolve [Vibe markets](#vibe-market). ## YES / NO tokens Token-2022 mints with `PermanentDelegate` extension. One YES + one NO is minted per $1 USDC of collateral; one YES + one NO can be burned to recover the $1. The permanent delegate is the protocol, which uses it to atomically settle winning redemptions without requiring user signatures. --- # https://sharp.xyz/docs/lifecycle # Market Lifecycle Every Sharp prediction market moves through a deterministic state machine. Each state is an integer on-chain (`market_state` field of `PredictionMarketStateFixed`); each transition is a public instruction anyone can crank. ## The states | Value | State | Trading | Notes | |------:|---|---|---| | 0 | `INITIALIZED` | No | Market created. Awaiting authority activation. | | 1 | `ACTIVE` | Yes | Trading is open. Orders, swaps, cross-market matching all live. | | 2 | `PAUSED` | No | Authority-paused. Existing orders frozen; no new trades. Returns to ACTIVE. | | 3 | `DETERMINED` | No | Outcome decided. Winner is recorded in `winning_outcome`. | | 4 | `CLOSED` | No | Trading is permanently closed. Order books frozen. | | 5 | `SETTLED` | No | Winners can redeem winning tokens for USDC. Losers' tokens are worthless. | | 6 | `CLEANED` | No | Manifest seats and intermediate state torn down. Collateral fully drained. | | 7 | `ARCHIVED` | No | Terminal state. Account closed and rent reclaimed. | ## The transitions ``` INITIALIZED ──→ ACTIVE ⇄ PAUSED ──→ DETERMINED ──→ CLOSED ──→ SETTLED ──→ CLEANED ──→ ARCHIVED │ └──────────── (authority cancel, no trading) ────────────→ CLEANED ──→ ARCHIVED ``` Each arrow corresponds to an on-chain instruction: - `INITIALIZED → ACTIVE` — `activate_market`. Authority-only. - `ACTIVE ⇄ PAUSED` — `pause_market` / `activate_market`. Authority-only. Used during emergencies or oracle disputes. - `ACTIVE | PAUSED → DETERMINED` — `resolve_market` (manual / Kalshi oracle / Vibe TWAP). Anyone can crank a Vibe or Kalshi resolution after the trigger condition; manual requires the authority. - `DETERMINED → CLOSED` — `close_market`. Permissionless once determined. - `CLOSED → SETTLED` — `batch_settle_prediction_market`. Permissionless. Cranks user redemptions in batches. - `SETTLED → CLEANED` — cleanup instructions. Permissionless. Tears down Manifest seats, drains the collateral vault. - `CLEANED → ARCHIVED` — `archive_market`. Permissionless. Closes the account. The shortcut path `INITIALIZED → CLEANED → ARCHIVED` exists for markets that are cancelled before any trading occurs. The authority calls a cancel instruction, which fast-paths to CLEANED, then anyone can archive. ## Resolution mechanisms When a market reaches `DETERMINED`, its `winning_outcome` is set: | Value | Outcome | Meaning | |------:|---|---| | 0 | `UNRESOLVED` | Default. Set during INITIALIZED/ACTIVE/PAUSED. | | 1 | `YES` | YES tokens redeem at $1; NO tokens worthless. | | 2 | `NO` | NO tokens redeem at $1; YES tokens worthless. | | 3 | `SPLIT` | Both YES and NO tokens redeem at $0.50. Used for true ties or insufficient TWAP data. | | 4 | `TWAP` | Vibe outcome. YES redeems at `TWAP / 10000` of $1; NO redeems at `(10000 - TWAP) / 10000`. Computed TWAP in basis points stored in `last_price`. | ### Manual The market authority calls `resolve_market` with the winning outcome. The simplest mechanism — used for markets where Sharp chooses the resolution criteria. ### Kalshi oracle Markets whose ticker matches a Kalshi event are resolved automatically by the [Kalshi Oracle service](https://github.com/KaneLabs/fora-markets/tree/main/kalshi-oracle), which subscribes to Kalshi's lifecycle WebSocket and submits a resolution instruction when the upstream Kalshi market settles. Anyone can crank the resolution once Kalshi has emitted the settlement signal. ### Vibe The market is its own oracle. After `expiry_slot`, the on-chain TWAP from the YES book is computed and stored as `last_price` (in basis points). The market resolves to `TWAP` outcome, and YES tokens redeem at `TWAP / 10000` while NO redeems at the complement. A Vibe market requires at least **`GIRARD_MIN_FILLS = 10`** distinct-slot fills to resolve via TWAP. With fewer fills the market resolves to `SPLIT` (both sides at $0.50) — there's not enough trading history for a meaningful TWAP. This protects against last-minute manipulation by a thin order book. See the [glossary entry](/docs/glossary#vibe-market) for the design intuition behind Vibe markets. ## Why it's a state machine and not a status flag Each state is a *contract*. When you query `/markets/{ticker}/status` and see `SETTLED`, you know: - Trading is permanently over. - The outcome is fixed. - The collateral vault still holds funds (until CLEANED). - Token holders can call settlement instructions to redeem. The state machine is enforced on-chain. No instruction can transition out of order. No state can be skipped (except the cancel shortcut). Every market that enters `INITIALIZED` will, eventually, reach `ARCHIVED` — or sit in an intermediate state with funds fully accounted for. ## Source - State machine constants: [`programs/fora-markets/src/state/prediction_market_state.rs`](https://github.com/KaneLabs/fora-markets/blob/main/programs/fora-markets/src/state/prediction_market_state.rs) — `pub mod market_state` and `pub mod winning_outcome`. - Transition processors: [`programs/fora-markets/src/program/processor/`](https://github.com/KaneLabs/fora-markets/tree/main/programs/fora-markets/src/program/processor) — one file per instruction. - Crank service: [`market-lifecycle-processor/`](https://github.com/KaneLabs/fora-markets/tree/main/market-lifecycle-processor) — the event-driven service that automates determine → close → settle → archive in production. --- # https://sharp.xyz/docs # Sharp Sharp is a **prediction market CLOB DEX on Solana**. Trade YES/NO outcomes on real-world events. Permissionless, non-custodial, no KYC. ## How it works Every market is a binary question — *"Will Duke win March Madness 2026?"* — with two tokens: YES and NO. Each token represents a $1 claim on the outcome. **YES + NO are minted 1:1 from USDC**, so $1 of USDC always backs one YES + one NO. At expiry, the winner redeems for $1, the loser for $0. Trading happens on a **central limit order book**, not an AMM. Orders are matched on [Manifest](https://github.com/CKS-Systems/manifest), the formally-verified CLOB Sharp is built on. Two order books per market — one for YES, one for NO — and the protocol matches buyers and sellers natively. **Cross-market matching.** When a YES bid and a NO bid sum to ≥ $1.00, the protocol mints a fresh token pair from the buyers' combined collateral and fills both sides atomically. Two strangers with opposite views get matched because their combined willingness to pay covers the full $1 backing. **Permissionless lifecycle.** Anyone can create, resolve, settle, and archive markets. Cleanup is crankable. No stuck markets, no stuck collateral. **Three resolution mechanisms.** - **Manual** — a designated authority resolves at expiry. - **Kalshi Oracle** — markets that mirror a Kalshi event resolve automatically when the upstream Kalshi market settles. - **Vibe** — the market is its own oracle. The outcome is computed from the on-chain TWAP (time-weighted average price) at expiry. No external oracle required. See [Vibe market in the glossary](/docs/glossary#vibe-market). ## Fees | Side | Rate | |---|---| | Taker (protocol fee) | 30 bps (0.30%) | | Maker (rebate) | 10 bps (0.10%) | | Net protocol take | 20 bps | Cross-market matching surplus — when YES + NO bids combined exceed $1.00 — accrues to the protocol fee vault. ## Get started ```bash cargo install fora-cli ``` Then [place your first trade](/docs/quickstart) — about 60 seconds end to end. ## Where things live - **Trading UI** — [sharp.xyz](/) (this site) - **REST API** — [api.fora.co](https://api.fora.co) ([reference](/docs/api)) - **WebSocket** — `wss://fire.fora.co` - **Source** — [github.com/KaneLabs/fora-markets](https://github.com/KaneLabs/fora-markets) - **Solana program** — mainnet-beta, USDC settled (`EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v`) ## Status Sharp is **soft-launched on mainnet**. Markets are real, USDC is real, settlements are real. The docs site is being rewritten from scratch — pages land here as they're written and verified against current code. If something looks off, [open an issue](https://github.com/KaneLabs/fora-markets/issues). --- # https://sharp.xyz/docs/quickstart # Quickstart Goal: install the CLI, fund a wallet, place a trade. Stub page — full content lands shortly. ## 1. Install ```bash cargo install fora-cli ``` ## 2. Configure ```bash fora-cli config init ``` ## 3. Trade ```bash fora-cli market list fora-cli trade buy --side yes --amount 5 ``` --- This page is a placeholder while the rewrite is in progress. Every command above is real, but the surrounding context (wallet setup, USDC funding, fee schedule, error recovery) is still being written. Source of truth: [fora-cli/src/main.rs](https://github.com/KaneLabs/fora-markets/blob/main/fora-cli/src/main.rs). --- # https://sharp.xyz/docs/reference/constants # Constants Reference Every named constant exposed by the Sharp protocol. If you're building a bot, indexer, or off-chain analytics, these are the numbers you encode. ## Economic | Constant | Value | Meaning | |---|---:|---| | `PROTOCOL_FEE_BPS` | `30` | Taker fee in basis points (0.30%). Charged on every fill — swaps and crossing limit orders. Collected on-chain into the per-mint `fee_vault`. | | `MAKER_REBATE_BPS` | `10` | Rebate to the maker, in basis points (0.10%). Funded out of the taker's protocol fee. Net protocol take is 20 bps. | | `GAS_DEPOSIT_LAMPORTS` | `5_000` | Lamports deposited into the global account on every global order. Returned when the order is filled, cancelled, expired, or evicted. Economic disincentive to spam. | ## Resolution | Constant | Value | Meaning | |---|---:|---| | `GIRARD_MIN_FILLS` | `10` | Minimum distinct-slot fills required for a Vibe market to resolve via TWAP. With fewer fills the market resolves as `SPLIT` (both sides at $0.50). See [Vibe markets](/docs/glossary#vibe-market). | | `NO_EXPIRATION_LAST_VALID_SLOT` | `0` | Sentinel for orders with no expiration — `last_valid_slot = 0` means the order is good-til-cancel. | ## Account sizing Fixed sizes of the major account types. Useful when sizing rent exemption calculations or off-chain Borsh deserializers. | Constant | Bytes | |---|---:| | `PREDICTION_MARKET_STATE_FIXED_SIZE` | `296` | | `MARKET_FIXED_SIZE` | `256` | | `GLOBAL_FIXED_SIZE` | `96` | | `MARKET_BLOCK_SIZE` | `80` | | `GLOBAL_BLOCK_SIZE` | `64` | The Manifest red-black-tree overhead is 16 bytes per block, so `RestingOrder` and `ClaimedSeat` payloads are `MARKET_BLOCK_SIZE - 16 = 64` bytes each. `GlobalTrader` and `GlobalDeposit` payloads are `GLOBAL_BLOCK_SIZE - 16 = 48` bytes each. ## Capacity | Constant | Value (mainnet) | Value (test) | |---|---:|---:| | `MAX_GLOBAL_SEATS` | `999` | `4` | Hard limit on global seats per global account. Sized so the global account never exceeds Solana account limits before the cap is hit, but generous enough that it really only matters as anti-spam. ## Discriminants The leading 8 bytes (`u64`) of every account, used to identify the account type when deserializing. | Constant | Value | ASCII | |---|---|---| | `PREDICTION_MARKET_DISCRIMINANT` | `0x50524544_4d4b5400` | `"PRED_MKT"` (null-terminated) | | `MARKET_FIXED_DISCRIMINANT` | `0x464F5241_4D524B54` | `"FORAMRKT"` | | `GLOBAL_FIXED_DISCRIMINANT` | `0x464F5241_474C424C` | `"FORAGLBL"` | When parsing an account, check the first 8 bytes against these values before deserializing the rest. ## Privileged authorities Pubkeys that hold protocol-level privileges. **These rotate** — always re-check the source if you're hardcoding them into off-chain tooling. | Constant | Privilege | |---|---| | `KALSHI_ORACLE_AUTHORITY` | Can resolve markets using the Kalshi Oracle mechanism. Held by the Kalshi Oracle service signer. | | `MARKET_MAKER_AUTHORITY` | Cannot be evicted from the global account. Held by the Sharp market maker. | Both addresses are inlined in [`programs/fora-markets/src/state/prediction_market_state.rs`](https://github.com/KaneLabs/fora-markets/blob/main/programs/fora-markets/src/state/prediction_market_state.rs) lines 85–92. Don't copy them into your own code — read them from the on-chain program if you need them, or use the constants directly via the SDK. ## Source All constants come from one of: - [`programs/fora-markets/src/state/prediction_market_state.rs`](https://github.com/KaneLabs/fora-markets/blob/main/programs/fora-markets/src/state/prediction_market_state.rs) — economic, resolution, authorities, account-state discriminant + size - [`programs/fora-markets/src/state/constants.rs`](https://github.com/KaneLabs/fora-markets/blob/main/programs/fora-markets/src/state/constants.rs) — sizing, gas deposit, capacity, Manifest discriminants --- # https://sharp.xyz/docs/reference/errors # Error Reference Every custom error code the Sharp program can return. When a transaction fails, Solana logs include a line like `Program log: AnchorError caused by account: ...` or `custom program error: 0x1A`. The hex code maps to one of the variants below. The protocol has **two error layers**: - **Manifest errors** (codes 0–23) — from the underlying CLOB. Returned when low-level order book operations fail. - **Sharp errors** (codes 22–50) — Sharp-specific. Returned by the prediction market protocol on top of Manifest. Note the overlap at codes 22 and 23 — the codes come from two different enums in two different programs, so disambiguation depends on which program ID emitted the log line. ## Manifest errors (codes 0–23) From the underlying CLOB. These surface when the low-level order book mechanics fail. | Code | Variant | Description | |---:|---|---| | 0 | `InvalidMarketParameters` | Invalid market parameters | | 1 | `InvalidDepositAccounts` | Invalid deposit accounts | | 2 | `InvalidWithdrawAccounts` | Invalid withdraw accounts | | 3 | `InvalidCancel` | Invalid cancel | | 4 | `InvalidFreeList` | Internal free list corruption | | 5 | `AlreadyClaimedSeat` | Cannot claim a second seat for the same trader | | 6 | `PostOnlyCrosses` | Matched on a post-only order | | 7 | `AlreadyExpired` | New order is already expired | | 8 | `InsufficientOut` | Less than minimum out amount (slippage) | | 9 | `InvalidPlaceOrderFromWalletParams` | Invalid place-order-from-wallet params | | 10 | `WrongIndexHintParams` | Index hint did not match actual index | | 11 | `PriceNotPositive` | Price is not positive | | 12 | `OrderWouldOverflow` | Order settlement would overflow | | 13 | `OrderTooSmall` | Order too small to settle any value | | 14 | `Overflow` | Overflow in token addition | | 15 | `MissingGlobal` | Missing Global account | | 16 | `GlobalInsufficient` | Insufficient funds on global account to rest an order | | 17 | `IncorrectAccount` | Account key did not match expected | | 18 | `InvalidMint` | Mint not allowed for market | | 19 | `TooManyGlobalSeats` | Cannot claim a new global seat — use evict | | 20 | `InvalidEvict` | Can only evict the lowest depositor | | 21 | `InvalidClean` | Tried to clean order that was not eligible to be cleaned | | 22 | `EvictProtected` | This seat is protected and cannot be evicted | | 23 | `InvalidGlobalCreate` | Global create / close-orphan-vault validation failed | ## Sharp errors (codes 22–50) From the Sharp prediction market layer on top of Manifest. | Code | Variant | Description | |---:|---|---| | 22 | `InvalidMetadata` | Invalid metadata URI or hash | | 23 | `InsufficientCollateral` | Insufficient collateral | | 24 | `TokenMintFailed` | Token mint failed | | 25 | `MismatchedTokenAmounts` | Mismatched token amounts when burning | | 26 | `InvalidMarket` | Invalid market | | 27 | `PricesDoNotCross` | Prices do not cross (yes_price + no_price < 1.00) | | 28 | `SupplyInvariantViolated` | Supply invariant violated (YES_supply != NO_supply) | | 29 | `OrderNotFound` | Order not found | | 30 | `NumericOverflow` | Numeric overflow | | 31 | `InvalidDiscriminant` | Invalid discriminant | | 32 | `InvalidAuthority` | Invalid authority | | 33 | `InvalidSigner` | Invalid signer | | 34 | `InvalidTokenAccount` | Invalid token account | | 35 | `InvalidMint` | Invalid mint (Sharp layer — not the same as Manifest's code 18) | | 36 | `MarketAlreadyInitialized` | Market already initialized | | 37 | `MarketNotInitialized` | Market not initialized | | 38 | `InvalidManifestProgram` | Invalid Manifest program | | 39 | `InvalidPredictionMarketState` | Invalid prediction market state | | 40 | `MarketClosed` | Market is closed | | 41 | `SlippageExceeded` | Slippage exceeded | | 42 | `MarketNotResolved` | Market not resolved | | 43 | `MarketNotSettled` | Market not fully settled (traders still have balances) | | 44 | `MarketNotDetermined` | Market is not in DETERMINED state | | 45 | `MarketNotClosed` | Market is not in CLOSED state | | 46 | `InvalidUserCount` | Invalid user count in batch settle params | | 47 | `SeatNotFound` | Seat not found in market | | 48 | `SeatOrderMismatch` | Seat order mismatch — user does not match expected seat trader | | 49 | `UnexpectedClobSeat` | Unexpected CLOB seat — user has seat but was not processed in earlier phases | | 50 | `InvalidOrderType` | Global orders cannot be placed through prediction market batch update | ## Quick lookup Searching for a specific failure mode? A few common ones: | If you see... | It usually means | |---|---| | `MarketClosed` (40) | Trying to trade on a market that's already past `ACTIVE` — see [Market Lifecycle](/docs/lifecycle) | | `MarketNotDetermined` (44) | Tried to close/settle before `resolve` was called | | `SlippageExceeded` (41) | Order's `min_out` not met — increase tolerance or split the trade | | `AlreadyClaimedSeat` (5) | Calling `claim_seat` twice; idempotent at the protocol level but no-op | | `GlobalInsufficient` (16) | Cross-market matching couldn't find enough opposite-side global liquidity | | `PricesDoNotCross` (27) | Cross-market match attempted but bids don't sum to ≥ $1.00 | | `PostOnlyCrosses` (6) | Post-only limit order would have taken liquidity — placed during a `ACTIVE` market with crossing book | ## Source - Manifest errors: [`programs/fora-markets/src/program/error.rs`](https://github.com/KaneLabs/fora-markets/blob/main/programs/fora-markets/src/program/error.rs) — `pub enum ManifestError` - Sharp errors: same file — `pub enum PredictionMarketError` --- # https://sharp.xyz/docs/reference/pdas # PDA Reference Every Program-Derived Address (PDA) used by the Sharp program. Each entry shows the seed pattern as it appears in the Rust source. > All PDAs are derived with `Pubkey::find_program_address(seeds, &fora_program_id)`. The trailing `bump` byte appended by `find_program_address` is stored on the relevant account for canonical re-derivation. ## Prediction Market The account holding all per-market state — market_state, winning_outcome, supplies, TWAP accumulators, references to the YES and NO Manifest markets, etc. ```rust let (prediction_market_pda, _bump) = Pubkey::find_program_address( &[PREDICTION_MARKET_SEED, metadata_hash.as_ref()], &fora_program_id, ); ``` | Component | Value | |---|---| | Seed const | `PREDICTION_MARKET_SEED = b"prediction_market"` | | Variable seed | `metadata_hash: [u8; 32]` — SHA-256 of the canonical metadata URI | | Bump location | `PredictionMarketStateFixed.bump` | The market PDA is **keyed by `metadata_hash`, not by ticker**. The ticker is metadata; the hash is the canonical identity. ## YES Mint Token-2022 mint for the YES side of the market. ```rust let (yes_mint_pda, _bump) = Pubkey::find_program_address( &[YES_MINT_SEED, prediction_market_pda.as_ref()], &fora_program_id, ); ``` | Component | Value | |---|---| | Seed const | `YES_MINT_SEED = b"yes_mint"` | | Variable seed | The market PDA | | Bump location | `PredictionMarketStateFixed.yes_mint_bump` | ## NO Mint Token-2022 mint for the NO side of the market. ```rust let (no_mint_pda, _bump) = Pubkey::find_program_address( &[NO_MINT_SEED, prediction_market_pda.as_ref()], &fora_program_id, ); ``` | Component | Value | |---|---| | Seed const | `NO_MINT_SEED = b"no_mint"` | | Variable seed | The market PDA | | Bump location | `PredictionMarketStateFixed.no_mint_bump` | ## Collateral Vault The per-market token account that holds USDC backing minted YES + NO tokens. 1 USDC sits here for every (1 YES + 1 NO) outstanding. ```rust let (collateral_vault_pda, _bump) = Pubkey::find_program_address( &[ COLLATERAL_VAULT_SEED, prediction_market_pda.as_ref(), collateral_mint.as_ref(), ], &fora_program_id, ); ``` | Component | Value | |---|---| | Seed const | `COLLATERAL_VAULT_SEED = b"vault"` | | Variable seeds | The market PDA + the collateral mint (USDC on mainnet) | Note the seed string is `"vault"`, not `"collateral_vault"`. Earlier docs had this wrong. ## Fee Vault A single global token account per collateral mint that collects all protocol fees from taker fills. ```rust let (fee_vault_pda, _bump) = Pubkey::find_program_address( &[FEE_VAULT_SEED, quote_mint.as_ref()], &fora_program_id, ); ``` | Component | Value | |---|---| | Seed const | `FEE_VAULT_SEED = b"fee_vault"` | | Variable seed | The quote mint (USDC) | One fee vault per quote mint — not per market. All fees across all markets collateralized by the same mint accrue here. ## Burn Account Token account used to permanently retire winning/losing tokens during settlement. ```rust let (burn_account_pda, _bump) = Pubkey::find_program_address( &[BURN_ACCOUNT_SEED], &fora_program_id, ); ``` | Component | Value | |---|---| | Seed const | `BURN_ACCOUNT_SEED = b"burn"` | | Variable seed | None — singleton | ## Cleanup Authority PDA that can sign cleanup instructions for evicting orders and closing Manifest seats during the `CLEANED` lifecycle phase. See [Market Lifecycle](/docs/lifecycle). ```rust let (cleanup_authority_pda, _bump) = Pubkey::find_program_address( &[CLEANUP_AUTHORITY_SEED], &fora_program_id, ); ``` | Component | Value | |---|---| | Seed const | `CLEANUP_AUTHORITY_SEED = b"cleanup_authority"` | | Variable seed | None — singleton | ## Manifest Global (inherited) Manifest's per-mint global state. Used by the cross-market matching path. Not under the Sharp program ID — derived against the **Manifest** program ID. ```rust let (global_state, _) = Pubkey::find_program_address( &[b"global", quote_mint.as_ref()], &manifest_program_id, ); let (global_vault, _) = Pubkey::find_program_address( &[b"global-vault", quote_mint.as_ref()], &manifest_program_id, ); ``` | PDA | Seeds | Program | |---|---|---| | Global state | `[b"global", quote_mint]` | Manifest | | Global vault | `[b"global-vault", quote_mint]` | Manifest | ## Quick reference | PDA | Seeds | Program | Variable | |---|---|---|---| | Prediction Market | `["prediction_market", metadata_hash]` | Sharp | metadata_hash | | YES Mint | `["yes_mint", market_pda]` | Sharp | market_pda | | NO Mint | `["no_mint", market_pda]` | Sharp | market_pda | | Collateral Vault | `["vault", market_pda, collateral_mint]` | Sharp | market_pda + collateral_mint | | Fee Vault | `["fee_vault", quote_mint]` | Sharp | quote_mint | | Burn Account | `["burn"]` | Sharp | none | | Cleanup Authority | `["cleanup_authority"]` | Sharp | none | | Manifest Global State | `["global", quote_mint]` | Manifest | quote_mint | | Manifest Global Vault | `["global-vault", quote_mint]` | Manifest | quote_mint | ## Source - Seed constants: [`programs/fora-markets/src/state/prediction_market_state.rs`](https://github.com/KaneLabs/fora-markets/blob/main/programs/fora-markets/src/state/prediction_market_state.rs) lines 9–14, 134 - Derivation sites: [`programs/fora-markets/src/program/instruction_builders/`](https://github.com/KaneLabs/fora-markets/tree/main/programs/fora-markets/src/program/instruction_builders) — one file per instruction - Manifest globals: built on [`@cks-systems/manifest`](https://github.com/CKS-Systems/manifest) — see Manifest's own docs for `global` / `global-vault` seed conventions