> ## Documentation Index
> Fetch the complete documentation index at: https://cyphers-3138df4b.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Changelog

> SDK version history and migration notes.

<Update label="v0.8.9" description="Current release" tags={["Feature"]}>
  **Friendlier read-only wallet error.** `readonlyWallet` now throws a typed `ReadonlyWalletError` instead of a generic `Error`, with a user-friendly message and a stable `code` for programmatic detection.

  * New export: `ReadonlyWalletError` (`code === "READONLY_WALLET"`, `name === "ReadonlyWalletError"`).
  * New default message: `"Wallet is read-only. Connect a wallet before sending a transaction."`
  * Detect the case with `err instanceof ReadonlyWalletError` to show a "connect wallet" prompt instead of leaking the SDK detail to your user.
</Update>

<Update label="v0.8.8" tags={["Feature"]}>
  **RPC batching layer.** New `src/rpc/` module that chunks bulk reads past Solana's 100-key `getMultipleAccountsInfo` cap, runs with bounded concurrency, and retries transient errors (`429` / `5xx` / network blips) with exponential backoff. Fixes the practical bug where `fetchMarketQuestions` broke once a deployment had more than 100 markets.

  * New low-level helper: `batchedGetMultipleAccountsInfo(connection, pubkeys, overrides?)`.
  * New `CypherClientOptions.rpcOptions?: Partial<BatchOptions>` — tune `chunkSize`, `concurrency`, `retries`, `retryBaseMs` per client. Conservative defaults (chunkSize 100, concurrency 5, retries 3) survive free-tier RPCs; bump `concurrency` on paid plans.
  * New paginated fetcher: `fetchMarketsByIds(client, ids[])` / `client.markets.byIds(ids)` — fetch a known list of markets without pulling the entire program payload. Preferred over `.all()` once the program has more than a few hundred markets.
  * `client.marketQuestions.fetchMany(markets)` — exposes the existing batch helper on the client surface, now routed through the chunked fetcher.
  * React: `useMarkets({ ids })` filter mode that routes to `byIds`.
  * React: `useMarketQuestions(markets)` — TanStack-Query wrapper over `fetchMany` with a 60s stale time (questions are immutable after market creation).
  * New query-key factory: `marketQuestionKeys.byMarkets(pdas)` for manual invalidation.
</Update>

<Update label="v0.8.7" tags={["Feature", "Breaking"]}>
  **Multi-bet support.** Users can now hold multiple positions per market. Each bet gets an auto-incrementing `betIndex`.

  * `placeBetAction` reads `user_state.next_bet_index` automatically and retries up to 3 times on concurrent-bet index conflicts.
  * `ClaimInputs` and `useClaimPayout` / `useClaimRefund` now accept `betIndex` (defaults to `0n`).
  * `EncryptedPositionAccount` adds `betIndex: bigint` field.
  * `saveSecret(market, betIndex, privateKey)` - signature changed to include `betIndex`.
  * `positionPda(market, user, betIndex?)` - `betIndex` defaults to `0n`.

  **Separate `MarketQuestion` PDA.** Question text for new markets moves to a dedicated account to save space in `MarketAccount`.

  * Use `fetchMarketQuestions(client, marketPublicKeys)` to hydrate questions in batch.
  * Legacy markets still have `account.inlineQuestion`.
  * `marketFormatVersion(accountSize)` returns `"v1"` | `"v2"` | `"v3"` | `null`.

  **Challenge window.** Dispute resolution system added.

  * New actions: `flagResolutionAction`, `finalizeResolutionAction`, `adminOverrideResolutionAction`.
  * New React hooks: `useFlagResolution`, `useFinalizeResolution`, `useAdminOverrideResolution`.
  * New events: `ResolutionFlaggedEvent`, `MarketFinalizedEvent`, `ResolutionOverriddenEvent`.
  * Markets pass through `PendingResolution` (state `4`) between reveal and claim.
  * New constants: `MIN_CHALLENGE_PERIOD_SECS` (24h), `MAX_CHALLENGE_PERIOD_SECS` (48h).
</Update>

<Update label="v0.8.6" tags={["Breaking"]}>
  **Breaking change.** The `betIndex` parameter was removed from the low-level `placePrivateBetYesnoIx` and `placePrivateBetMultiIx` instruction builders. The on-chain `user_state` PDA now tracks `next_bet_index` automatically.

  * `userStatePda(user)` - new PDA for per-user bet index counter.
  * `readNextBetIndex(client, user)` - helper to fetch the current index when building low-level instructions. Not needed if you use `placeBetAction`.
  * High-level `placeBetAction` handles index tracking and retries automatically - no changes needed for callers using `client.actions.placeBet`.
</Update>

<Update label="v0.8.5" tags={["Fix"]}>
  **Position decoder fix.** `fetchUserPositions` (and `client.positions.byUser`) now correctly handles mixed legacy 208-byte and current 216-byte position accounts.

  * Previously, a single legacy account in the result set would cause the entire query to fail silently (returning zero positions).
  * The fix uses per-account size-aware decoding instead of Anchor's all-or-nothing decoder.
</Update>

<Update label="v0.8.0 and earlier">
  * One position per user per market (no `betIndex`).
  * `saveSecret(market, secret)` took two arguments (no `betIndex`).
  * Questions always stored inline in `MarketAccount.inlineQuestion`.
  * No challenge window - markets moved directly from `PendingResolution` to `Resolved` after the MPC callback.
</Update>
