Account types
MarketAccount
The central account for a prediction market. Exists at the market PDA.
| Field | Type | Description |
|---|---|---|
marketId |
bigint |
Auto-incrementing ID assigned at creation |
marketType |
number |
0 = YesNo, 1 = MultiOutcome |
numOutcomes |
number |
Number of outcome options (2 for YesNo, 2-4 for multi) |
category |
number |
Market category enum (0-6) |
creator |
PublicKey |
Market creator |
resolver |
PublicKey |
Wallet authorized to post the outcome |
creatorBond |
bigint |
Bond deposited by creator at market creation (micro-USDC) |
bondWithdrawn |
boolean |
Whether the creator has already withdrawn their bond |
totalBetsCount |
bigint |
Total number of bets placed |
revealedPool0 |
bigint |
Revealed pool for outcome 0 (set by MPC callback after resolution) |
revealedPool1 |
bigint |
Revealed pool for outcome 1 |
revealedPool2 |
bigint |
Revealed pool for outcome 2 (multi only) |
revealedPool3 |
bigint |
Revealed pool for outcome 3 (multi only) |
state |
number |
Current state (0=Active, 1=Closed, 2=Resolved, 3=Unresolved, 4=PendingResolution) |
outcome |
number |
Confirmed winning outcome index (set after resolution is finalized) |
pendingOutcome |
number |
Outcome posted by the resolver - not yet finalized (0 until resolveMarket is called) |
closeTime |
bigint |
Unix timestamp when betting closes |
resolutionTime |
bigint |
Unix timestamp when resolveMarket was called |
payoutRatio |
bigint |
Payout per unit stake × ODDS_SCALE (1e9) |
minBet |
bigint |
Market-specific minimum bet in micro-USDC |
resolutionDeadline |
bigint |
Timestamp by which the resolver must post an outcome, or refunds open |
claimDeadline |
bigint |
Timestamp when the claim period ends |
refundDeadline |
bigint |
Timestamp when the refund period ends |
challengePeriod |
bigint |
Dispute window duration in seconds (24-48h, set at creation) |
challengeDeadline |
bigint |
resolutionTime + challengePeriod - when the window closes |
disputed |
boolean |
Whether flagResolution was called during the challenge window |
inlineQuestion |
string |
Question text for legacy (v1/v2) market accounts only - always "" on v3+ |
bump |
number |
PDA bump seed |
Note: v3 markets (393-byte accounts, all markets created with the current program) store the question in a separate
MarketQuestionPDA -inlineQuestionwill be"". UsefetchMarketQuestionsto hydrate questions in batch.
EncryptedPositionAccount
Records an encrypted bet. Exists at the position PDA keyed by (market, user, betIndex).
| Field | Type | Description |
|---|---|---|
user |
PublicKey |
Bettor's wallet |
market |
PublicKey |
Market PDA |
encryptedAmount |
Uint8Array |
32-byte ciphertext of the bet stake |
encryptedSide |
Uint8Array |
32-byte ciphertext of the chosen outcome |
userPubkey |
Uint8Array |
Bettor's x25519 public key (needed for decryption) |
nonce |
bigint |
u128 nonce used for encryption (convert to 16 LE bytes to decrypt) |
entryOdds |
bigint |
Locked-in odds × ODDS_SCALE at bet time |
netAmount |
bigint |
Plaintext net stake after fees (asserted by circuit) |
betIndex |
bigint |
Index of this bet for the (user, market) pair |
claimed |
boolean |
Whether payout or refund was claimed |
computationQueued |
boolean |
Whether the MPC computation was queued |
bump |
number |
PDA bump seed |
GlobalStateAccount
Singleton protocol config. There is only one per deployment.
| Field | Type | Description |
|---|---|---|
marketCounter |
bigint |
Next market ID to assign |
protocolFeeRate |
number |
Protocol fee in basis points (e.g., 50 = 0.5%) |
lpFeeRate |
number |
LP (creator) fee in basis points (e.g., 150 = 1.5%) |
protocolTreasury |
PublicKey |
Wallet receiving protocol fees |
acceptedMint |
PublicKey |
USDC mint accepted by this deployment |
admin |
PublicKey |
Admin wallet for overrides |
bump |
number |
PDA bump seed |
LpPositionAccount
Tracks the creator's liquidity and accumulated fees per market.
| Field | Type | Description |
|---|---|---|
lpProvider |
PublicKey |
Market creator wallet |
market |
PublicKey |
Market PDA |
liquidityProvided |
bigint |
Bond deposited at market creation (micro-USDC) |
feesEarned |
bigint |
Accumulated LP fees (reserved - currently always 0n; use Market.accumulatedLpFees) |
feesClaimed |
boolean |
Whether withdrawCreatorFunds has been called |
feesClaimedAmount |
bigint |
Actual payout amount at withdrawal time |
bump |
number |
PDA bump seed |
MarketQuestionAccount v0.2+
Stores the question text for v0.2+ markets (separate PDA to save space in MarketAccount).
| Field | Type | Description |
|---|---|---|
question |
string |
Full question text decoded from on-chain bytes |
questionLen |
number |
Length of the question in bytes (max 200) |
bump |
number |
PDA bump seed |
Fetchers
All fetchers are also available as methods on the CypherClient namespaces.
import {
fetchMarket,
fetchAllMarkets,
fetchMarketsByIds,
fetchUserPositions,
fetchMarketQuestions,
} from "@cypher-zk/sdk";
// Fetch a single market by ID
const market = await client.markets.fetch(42n);
// Fetch all markets — expensive at scale, prefer byIds for paginated views
const markets = await client.markets.all();
// Fetch only the markets you actually need (mixed bigint / number / PublicKey).
// Routes through the batched RPC layer (chunking + concurrency + retries).
const page = await client.markets.byIds([1n, 2n, 3n]);
// Fetch all positions for a user
const positions = await client.positions.byUser(userPublicKey);
// Batch-fetch questions for a page of markets (v0.2+).
// `fetchMany` is the client-namespace wrapper around `fetchMarketQuestions`.
const questionMap = await client.marketQuestions.fetchMany(page);
// Merge questions
const withQuestions = page.map(({ publicKey, account }) => ({
publicKey,
account,
question: questionMap.get(publicKey.toBase58()) ?? account.inlineQuestion,
}));Note:
client.markets.byIdsandclient.marketQuestions.fetchManywere added in v0.8.8. Both chunk pubkey lists past Solana's 100-keygetMultipleAccountsInfocap and retry transient errors. Tune viaCypherClientOptions.rpcOptionsor pass per-call overrides.
Display helpers
import {
marketPhase,
marketStateName,
marketTypeName,
marketCategoryName,
cancelEligibility,
marketFormatVersion,
parseEmbeddedOptions,
} from "@cypher-zk/sdk";marketPhase
Returns a human-readable phase string based on the market's state and timestamps. Use this to drive UI state.
type MarketPhase =
| "betting"
| "awaitingResolve"
| "pendingResolution"
| "awaitingFinalize"
| "disputed"
| "claimable"
| "refundable"
| "expired"
| "cancelled";
const phase = marketPhase(market.account);cancelEligibility
Pre-flight check before showing a cancel button.
const { ok, reason } = cancelEligibility(market.account);
if (!ok) console.log("Cannot cancel:", reason);parseEmbeddedOptions
Extracts option labels from a [A|B|C] question suffix.
const labels = parseEmbeddedOptions("Which chain leads TVL? [Solana|Ethereum|Base]");
// ["Solana", "Ethereum", "Base"]
// Map side index to label
const sideLabel = labels[position.side] ?? `Option ${position.side}`;marketFormatVersion
Detects the market account layout version from byte length.
const version = marketFormatVersion(accountData.length); // "v1" | "v2" | "v3" | nullLegacy v1 and v2 accounts have inline questions; v3 accounts use the MarketQuestion PDA.