@vercora-protocol/sdk 0.0.10 → 0.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -14,97 +14,207 @@ yarn add @vercora-protocol/sdk
14
14
 
15
15
  ```bash
16
16
  npm install @coral-xyz/anchor @solana/web3.js @solana/spl-token bn.js
17
+ # or
18
+ yarn add @coral-xyz/anchor @solana/web3.js @solana/spl-token bn.js
17
19
  ```
18
20
 
19
21
  ## Quick start
20
22
 
21
23
  ```ts
22
24
  import * as anchor from "@coral-xyz/anchor";
23
- import { Program } from "@coral-xyz/anchor";
24
25
  import { Connection, clusterApiUrl } from "@solana/web3.js";
25
- import {
26
- IDL,
27
- PROGRAM_ID,
28
- PredictionMarketClient,
29
- } from "@vercora-protocol/sdk";
26
+ import { IDL, PROGRAM_ID, PredictionMarketClient } from "@vercora-protocol/sdk";
30
27
  import type { Vercora } from "@vercora-protocol/sdk";
31
- import { BN } from "bn.js";
32
28
 
33
- // 1. Set up an Anchor provider (browser wallet / NodeWallet)
34
29
  const connection = new Connection(clusterApiUrl("devnet"));
35
30
  const provider = new anchor.AnchorProvider(connection, wallet, {});
36
31
  anchor.setProvider(provider);
37
32
 
38
- // 2. Program from the bundled IDL (`IDL` includes the program address; `PROGRAM_ID` matches it)
33
+ // Bundled IDL program id is embedded in `IDL.address` (`PROGRAM_ID` matches it)
39
34
  const program = new anchor.Program<Vercora>(IDL, provider);
40
-
41
- // 3. Create the client
42
35
  const client = new PredictionMarketClient(program);
43
36
  ```
44
37
 
45
- ## Platforms and categories
38
+ Most transaction methods use **`provider.wallet.publicKey`** as the signer (`user`, `authority`, `creator`, etc.). Pass a wallet that can sign; read-only flows can use a read-only provider for fetch helpers only.
46
39
 
47
- Markets store **`platform_id`** as **`u32`** and **`category_id`** as **`u8`** on-chain (not pubkeys). See the IDL `CreateMarketArgs` and account layouts for the exact Borsh layout.
40
+ ## Package exports
48
41
 
49
- 1. **`register_platform`** (program authority) — assigns the next id from `GlobalConfig.next_platform_id` and creates a **`PlatformRegistry`** PDA: seeds `["platform", platform_id.to_le_bytes()]` (4-byte little-endian `u32`). Holds `next_category_id` and `profile_authority` (who may edit the **`PlatformProfile`** for that id).
50
- 2. **`create_market_category`** args `(platform_id, category_id, name)`; **`category_id`** must equal **`PlatformRegistry.next_category_id`** (then the registry is bumped after init). PDA seeds: `["market-category", platform_id le u32, category_id le u8]`.
51
- 3. **`create_market`** — args include `platform_id` and `category_id`. Rules: if `platform_id == 0` then `category_id` must be `0` and no registry/category accounts are passed. If `platform_id > 0`, pass the **`platform_registry`** account; if `category_id > 0`, pass the matching **`market_category`** account.
42
+ | Export | Description |
43
+ | ------------------------ | -------------------------------------------------------------------------------------------- |
44
+ | `PredictionMarketClient` | High-level program wrapper (see below). |
45
+ | `IDL`, `PROGRAM_ID` | Anchor IDL JSON and `PublicKey` for the deployed program. |
46
+ | `pda` helpers | `deriveMarket`, `deriveVault`, `deriveParimutuelState`, … (see [PDA helpers](#pda-helpers)). |
47
+ | `types` | Params and account shapes (`CreateMarketParams`, `ListedMarket`, …). |
48
+ | `marketUi` | Pure UI helpers (`getMarketLifecycleStatus`, `formatTimeLeft`, …). |
49
+ | `Vercora` (type) | Anchor `Program` generic for `Program<Vercora>`. |
52
50
 
53
- Use the SDK helpers **`derivePlatformRegistry`**, **`derivePlatformProfile`**, and **`deriveMarketCategory(programId, platformId, categoryId)`** when building transactions manually.
51
+ ---
54
52
 
55
- ## Market types
53
+ ## `PredictionMarketClient` API
56
54
 
57
- Use `marketType: 'parimutuel'` or `marketType: 'completeSet'` when creating a market (the Anchor default is `completeSet` if you omit the field — set `parimutuel` explicitly for pool mode). The choice is permanent.
55
+ Public properties: **`program`**, **`connection`**, **`globalConfig`** (PDA).
58
56
 
59
- ### `parimutuel` pool markets (**easiest to get started**)
57
+ ### Global config (authority)
60
58
 
61
- Stakes go into shared outcome pools on-chain. No outcome SPL tokens are minted. Odds follow pool sizes; the winning side splits the pot at settlement.
59
+ | Method | Purpose |
60
+ | ----------------------------------- | ---------------------------------------------- |
61
+ | `initializeConfig(params)` | One-time init; `authority` = connected wallet. |
62
+ | `updateConfig(params)` | Update fees, treasury, authorities. |
63
+ | `addAllowedCollateralMint(mint)` | Allowlist a collateral mint. |
64
+ | `removeAllowedCollateralMint(mint)` | Remove from allowlist. |
62
65
 
63
- - **No liquidity layer required** — the pool is the market. Ship without wiring a DEX or running an AMM.
64
- - **Minimal integration** — stake, withdraw, claim; no outcome-token listings to maintain.
65
- - Positions are ledger entries, not transferable tokens. Early withdrawal may use a configurable penalty.
66
- - **Best for:** most prediction-style apps and teams that want live odds and settlement without operating liquidity infrastructure.
66
+ ### Platforms & categories
67
67
 
68
- ### `completeSet` — token markets (**needs a liquidity layer**)
68
+ | Method | Purpose |
69
+ | ------------------------------ | ------------------------------------------------------------------------ |
70
+ | `registerPlatform(params)` | Next `platform_id` from `GlobalConfig`; returns `{ platformId, sig }`. |
71
+ | `createMarketCategory(params)` | Next category id per `PlatformRegistry` (must match `next_category_id`). |
72
+ | `updateMarketCategory(params)` | Rename / toggle active. |
69
73
 
70
- The protocol mints one SPL outcome token per outcome when users deposit collateral. The **program does not include an order book or AMM** — you must provide or integrate liquidity so users can trade between outcomes.
74
+ ### Market creation
75
+
76
+ | Method | Purpose |
77
+ | --------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- |
78
+ | `createMarket(creator, collateralMint, creatorFeeAccount, params)` | Step 1: market + vault. |
79
+ | `initializeMarketResolverSlots(marketPda, params, opts?, parimutuelStateParams?)` | Resolver PDAs; optional `parimutuelStateParams` appends `initializeParimutuelState` in the same tx. |
80
+ | `initializeMarketOutcomeSlots(marketPda, params)` | `MarketOutcome` labels (complete-set & pari). |
81
+ | `initializeMarketMints(marketPda, marketId)` | Mint 8 outcome SPLs (complete-set only). |
82
+ | `initializeParimutuelState(marketPda, params)` | Standalone pari pool + penalty params (if not bundled with resolvers). |
83
+ | `createMarketFull(creator, collateralMint, creatorFeeAccount, resolverPubkeys, params)` | Runs create → outcomes → resolvers (+ mints **or** pari state in one flow). |
84
+ | `updateParimutuelState(marketPda, params)` | Creator updates early-exit penalty (open pari pool). |
85
+
86
+ **`CreateMarketParams` (pari-mutuel):** optional **`isEarlyWithdrawAllowed`** (default **`true`**). When **`false`**, the program rejects **`parimutuelWithdraw`** with **`EarlyWithdrawNotAllowed`** until close or resolution. Ignored for complete-set markets. Read back on **`fetchMarket(marketPda).isEarlyWithdrawAllowed`**.
87
+
88
+ ### Complete-set trading
89
+
90
+ | Method | Purpose |
91
+ | ---------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |
92
+ | `mintCompleteSet(user, marketPda, collateralMint, userCollateralAta, platformTreasury, creatorFeeAccount, params, opts?, tokenProgram?)` | Mint full set; creates missing outcome ATAs. |
93
+ | `redeemCompleteSet(user, marketPda, collateralMint, userCollateralAta, params)` | Burn full set for collateral. |
94
+ | `redeemWinning(user, marketPda, collateralMint, userCollateralAta, params)` | After resolution, redeem winning outcome tokens. |
95
+
96
+ ### Parimutuel trading
71
97
 
72
- - Outcome tokens are composable SPL assets (wallets, DEXes, DeFi).
73
- - Mint/redeem full complete sets before close without a counterparty for that step.
74
- - **Single-outcome trading requires external liquidity** (AMM, RFQ, market-maker, etc.) or users cannot reliably move between legs.
75
- - **Best for:** teams that control or integrate a DEX, run their own pools, or pair the program with an explicit liquidity strategy.
98
+ | Method | Purpose |
99
+ | --------------------------------------- | ------------------------------------------------------- |
100
+ | `parimutuelStake(marketPda, params)` | Stake; signer is **provider wallet** (`user` on-chain). |
101
+ | `parimutuelWithdraw(marketPda, params)` | Early exit (penalty may apply). Errors if `Market.isEarlyWithdrawAllowed` is false. |
102
+ | `parimutuelClaim(marketPda, params)` | Claim after resolution. |
103
+
104
+ ### Resolution & lifecycle
105
+
106
+ | Method | Purpose |
107
+ | ----------------------------------------- | --------------------------------------------- |
108
+ | `voteResolution(marketPda, params)` | Resolver vote. |
109
+ | `revokeResolutionVote(marketPda, params)` | Clear vote before changing. |
110
+ | `finalizeResolution(marketPda, params)` | Anyone, once threshold met. |
111
+ | `closeMarketEarly(marketPda, params)` | Creator / config authority before `close_at`. |
112
+ | `voidMarket(marketPda, params)` | Void market. |
113
+ | `abandonMarket(marketPda, params)` | Creator abandons empty market (reclaim rent). |
114
+
115
+ ### Discovery & reads
116
+
117
+ | Method | Purpose |
118
+ | ----------------------------------------------------------------- | --------------------------------------------------------------------------------------------- |
119
+ | `fetchGlobalConfig()` | Global config account. |
120
+ | `fetchMarket(marketPda)` | `Market` account. |
121
+ | `fetchAllMarkets(platformId?)` | All markets (optional `memcmp` on `platform_id`); includes outcome labels. |
122
+ | `fetchMarketsByPlatform(platformId)` | Same as `fetchAllMarkets(platformId)`. |
123
+ | `fetchMarketsByCreator(creator)` | Raw rows `{ pubkey, account }[]`, memcmp on creator. |
124
+ | `getUsersMarkets(creator, filters?)` | **`ListedMarket[]`** for creator; optional `platformId` (RPC) + `categoryId` (client filter). |
125
+ | `fetchMarketOutcomeLabels(marketPda, outcomeCount)` | Outcome labels. |
126
+ | `fetchVaultBalance(marketPda)` | Vault balance (bigint base units). |
127
+ | `fetchOutcomeBalance(marketPda, user, outcomeIndex)` | Single outcome token balance (complete-set). |
128
+ | `fetchAllOutcomeBalances(marketPda, user, outcomeCount)` | All outcome balances for user. |
129
+ | `fetchMarketCategory(categoryPda)` | One category. |
130
+ | `fetchAllMarketCategories()` | All categories (sorted). |
131
+ | `fetchResolutionVote(marketPda, resolverIndex)` | Vote PDA or null. |
132
+ | `fetchMarketOutcomesSnapshot(marketPda, outcomeCount)` | Decoded outcomes + tallies. |
133
+ | `fetchOutcomeTallyCounts(marketPda)` | Quick tally array. |
134
+ | `fetchAllowedCollateralMints()` | Allowlist mints. |
135
+ | `fetchUserProfile(wallet)` | User profile or null. |
136
+ | `fetchPlatformProfile(platformId)` | Platform profile or null. |
137
+ | `fetchParimutuelState(marketPda)` | Pari pool state. |
138
+ | `fetchParimutuelPosition(marketPda, user, outcomeIndex)` | Position or null. |
139
+ | `fetchParimutuelActiveStakesBatch(marketPda, user, outcomeCount)` | Active stakes per outcome. |
140
+ | `computeParimutuelOdds(state, outcomeCount)` | Implied probs + payout multipliers. |
141
+ | `fetchResolver(marketPda, index)` | One resolver account. |
142
+ | `fetchAllResolvers(marketPda, numResolvers)` | Initialized resolver slots. |
143
+
144
+ ### Profiles
145
+
146
+ | Method | Purpose |
147
+ | ---------------------------------- | ------------------------------------------- |
148
+ | `upsertUserProfile(params)` | Display name / URL. |
149
+ | `closeUserProfile()` | Close caller’s profile. |
150
+ | `verifyUserProfile(params)` | Verifier marks verified. |
151
+ | `upsertPlatformProfile(params)` | Platform profile (scoped by `platform_id`). |
152
+ | `closePlatformProfile(platformId)` | Close profile. |
153
+ | `verifyPlatformProfile(params)` | Verify platform profile. |
76
154
 
77
155
  ---
78
156
 
79
- ## Usage examples
157
+ ## Examples
80
158
 
81
- ### Create a market
159
+ ### Global config (first deploy)
82
160
 
83
161
  ```ts
84
- import { BN } from "bn.js";
162
+ import { PublicKey } from "@solana/web3.js";
163
+ import BN from "bn.js";
164
+
165
+ await client.initializeConfig({
166
+ // System program / “none” placeholder — use a real secondary authority pubkey in production
167
+ secondaryAuthority: new PublicKey("11111111111111111111111111111111"),
168
+ depositPlatformFeeBps: 100,
169
+ platformTreasuryWallet: treasuryPubkey,
170
+ platformFeeLamports: new BN(357_000),
171
+ parimutuelWithdrawPlatformFeeBps: 50,
172
+ });
173
+ ```
174
+
175
+ ### Register platform & category
176
+
177
+ ```ts
178
+ const { platformId } = await client.registerPlatform({
179
+ profileAuthority: profileSignerPubkey,
180
+ });
85
181
 
86
- const marketId = new BN(Date.now()); // unique u64 per creator
182
+ await client.createMarketCategory({
183
+ platformId,
184
+ name: "Politics",
185
+ });
186
+ ```
187
+
188
+ ### Create a market (manual steps, complete-set)
189
+
190
+ ```ts
191
+ import BN from "bn.js";
192
+
193
+ const marketId = new BN(Date.now());
87
194
  const { marketPda } = await client.createMarket(
88
- provider.wallet.publicKey, // creator
89
- collateralMintPubkey,
90
- creatorFeeAccountPubkey,
195
+ creatorPubkey,
196
+ collateralMint,
197
+ creatorFeeAta,
91
198
  {
92
199
  marketId,
93
200
  outcomeCount: 2,
94
201
  resolutionThreshold: 1,
95
- closeAt: new BN(Math.floor(Date.now() / 1000) + 86400), // 24 h from now
202
+ closeAt: new BN(Math.floor(Date.now() / 1000) + 86400),
96
203
  creatorFeeBps: 50,
97
- depositPlatformFeeBps: 0, // 0 = inherit global config
204
+ depositPlatformFeeBps: 0,
98
205
  numResolvers: 1,
206
+ maxOutcomeInvestment: new BN(0),
99
207
  title: "Will it rain tomorrow?",
100
- marketType: "completeSet", // or 'parimutuel'
101
- // Optional: scoped market (omit or use new BN(0) for unscoped / uncategorized)
208
+ marketType: "completeSet",
102
209
  platformId: new BN(0),
103
210
  categoryId: new BN(0),
104
211
  },
105
212
  );
106
213
 
107
- // Resolver slots and outcome mints must be initialised before trading
214
+ await client.initializeMarketOutcomeSlots(marketPda, {
215
+ marketId,
216
+ labels: ["Yes", "No"],
217
+ });
108
218
  await client.initializeMarketResolverSlots(marketPda, {
109
219
  marketId,
110
220
  resolverPubkeys: [resolverPubkey],
@@ -112,98 +222,192 @@ await client.initializeMarketResolverSlots(marketPda, {
112
222
  await client.initializeMarketMints(marketPda, marketId);
113
223
  ```
114
224
 
115
- ### Complete-set trading
225
+ ### Create market in one call (`createMarketFull`)
226
+
227
+ Complete-set (mints outcome tokens):
116
228
 
117
229
  ```ts
118
- // Mint one complete set (all outcomes)
119
- await client.mintCompleteSet(
120
- user,
121
- marketPda,
230
+ const marketPda = await client.createMarketFull(
231
+ creatorPubkey,
122
232
  collateralMint,
123
- userCollateralAta,
124
- platformTreasury,
125
- creatorFeeAccount,
126
- { marketId, amount: new BN(1_000_000) },
233
+ creatorFeeAta,
234
+ [resolverPubkey],
235
+ {
236
+ marketId,
237
+ outcomeCount: 2,
238
+ resolutionThreshold: 1,
239
+ closeAt: new BN(Math.floor(Date.now() / 1000) + 86400),
240
+ creatorFeeBps: 50,
241
+ depositPlatformFeeBps: 0,
242
+ numResolvers: 1,
243
+ maxOutcomeInvestment: new BN(0),
244
+ title: "Two-outcome market",
245
+ marketType: "completeSet",
246
+ outcomeLabels: ["Yes", "No"],
247
+ },
127
248
  );
249
+ ```
128
250
 
129
- // Redeem a complete set back to collateral
130
- await client.redeemCompleteSet(
131
- user,
132
- marketPda,
251
+ Parimutuel (resolver tx also initializes pari state; optional `parimutuelInit` overrides defaults):
252
+
253
+ ```ts
254
+ const marketPda = await client.createMarketFull(
255
+ creatorPubkey,
133
256
  collateralMint,
134
- userCollateralAta,
135
- { marketId },
257
+ creatorFeeAta,
258
+ [resolverPubkey],
259
+ {
260
+ marketId,
261
+ outcomeCount: 2,
262
+ resolutionThreshold: 1,
263
+ closeAt: new BN(Math.floor(Date.now() / 1000) + 86400),
264
+ creatorFeeBps: 50,
265
+ depositPlatformFeeBps: 0,
266
+ numResolvers: 1,
267
+ maxOutcomeInvestment: new BN(0),
268
+ title: "Pari pool",
269
+ marketType: "parimutuel",
270
+ outcomeLabels: ["A", "B"],
271
+ // Optional — default true. Set false to lock stakes until close/resolution (no early parimutuelWithdraw).
272
+ isEarlyWithdrawAllowed: true,
273
+ parimutuelInit: {
274
+ earlyWithdrawPenaltyBps: 500,
275
+ penaltyKeptInPoolBps: 8000,
276
+ },
277
+ },
136
278
  );
137
-
138
- // After resolution — redeem the winning outcome tokens
139
- await client.redeemWinning(user, marketPda, collateralMint, userCollateralAta, {
140
- marketId,
141
- amount: winningBalance,
142
- });
143
279
  ```
144
280
 
145
- ### Parimutuel trading
281
+ ### Complete-set trading
146
282
 
147
283
  ```ts
148
- // Stake on an outcome
149
- await client.parimutuelStake(
150
- user,
284
+ const treasury = (await client.fetchGlobalConfig()).platformTreasury;
285
+
286
+ await client.mintCompleteSet(
287
+ userPubkey,
151
288
  marketPda,
152
289
  collateralMint,
153
290
  userCollateralAta,
154
- platformTreasury,
155
- creatorFeeAccount,
156
- { marketId, outcomeIndex: 0, amount: new BN(500_000) },
291
+ treasury,
292
+ creatorFeeAta,
293
+ { marketId, amount: new BN(1_000_000) },
157
294
  );
158
295
 
159
- // Withdraw before market closes (penalty may apply)
160
- await client.parimutuelWithdraw(
161
- user,
296
+ await client.redeemCompleteSet(
297
+ userPubkey,
162
298
  marketPda,
163
299
  collateralMint,
164
300
  userCollateralAta,
165
- { marketId, outcomeIndex: 0, amount: new BN(500_000) },
301
+ { marketId },
166
302
  );
167
303
 
168
- // Claim winnings after resolution
169
- await client.parimutuelClaim(
170
- user,
304
+ await client.redeemWinning(
305
+ userPubkey,
171
306
  marketPda,
172
307
  collateralMint,
173
308
  userCollateralAta,
174
- { marketId, outcomeIndex: 0 },
309
+ {
310
+ marketId,
311
+ amount: winningAmountBn,
312
+ },
175
313
  );
176
314
  ```
177
315
 
316
+ ### Parimutuel trading
317
+
318
+ Signer is always the **connected wallet** (no `user` first argument):
319
+
320
+ ```ts
321
+ await client.parimutuelStake(marketPda, {
322
+ marketId,
323
+ outcomeIndex: 0,
324
+ amount: new BN(500_000),
325
+ });
326
+
327
+ await client.parimutuelWithdraw(marketPda, {
328
+ marketId,
329
+ outcomeIndex: 0,
330
+ amount: new BN(500_000),
331
+ });
332
+ // If create used isEarlyWithdrawAllowed: false, parimutuelWithdraw throws (Anchor: EarlyWithdrawNotAllowed).
333
+
334
+ await client.parimutuelClaim(marketPda, {
335
+ marketId,
336
+ outcomeIndex: 0,
337
+ });
338
+
339
+ const state = await client.fetchParimutuelState(marketPda);
340
+ const odds = client.computeParimutuelOdds(state, 2);
341
+ ```
342
+
178
343
  ### Resolution
179
344
 
180
345
  ```ts
181
- // Resolver votes on the winning outcome
182
346
  await client.voteResolution(marketPda, {
183
347
  marketId,
184
348
  resolverIndex: 0,
185
- outcomeIndex: 1, // 0-based
349
+ outcomeIndex: 1,
186
350
  });
187
351
 
188
- // Once the threshold is met, anyone can finalize
189
352
  await client.finalizeResolution(marketPda, { marketId });
190
353
  ```
191
354
 
192
- ### Read on-chain state
355
+ ### Discovery
356
+
357
+ ```ts
358
+ // Every market (heavy on RPC)
359
+ const all = await client.fetchAllMarkets();
360
+
361
+ // By platform (memcmp)
362
+ const byPlatform = await client.fetchAllMarkets(new BN(1));
363
+
364
+ // Markets created by a wallet — full rows with labels + filters
365
+ const mine = await client.getUsersMarkets(creatorPubkey, {
366
+ platformId: new BN(1), // optional RPC filter
367
+ categoryId: 2, // optional; applied after decode
368
+ });
369
+
370
+ // Raw accounts only (no labels)
371
+ const raw = await client.fetchMarketsByCreator(creatorPubkey);
372
+ ```
373
+
374
+ ### User profile
193
375
 
194
376
  ```ts
195
- const market = await client.fetchMarket(marketPda);
196
- console.log(market.title, market.marketType, market.platformId.toString(), market.categoryId.toString());
377
+ await client.upsertUserProfile({
378
+ displayName: "Alice",
379
+ url: "https://example.com",
380
+ });
197
381
 
198
- const config = await client.fetchGlobalConfig();
382
+ const profile = await client.fetchUserProfile(wallet.publicKey);
199
383
  ```
200
384
 
385
+ ---
386
+
387
+ ## Platforms and categories
388
+
389
+ On-chain, **`platform_id`** is **`u32`** and **`category_id`** is **`u8`** in `CreateMarketArgs` (not pubkeys).
390
+
391
+ 1. **`register_platform`** — assigns the next id from `GlobalConfig.next_platform_id`; PDA `["platform", platform_id le u32]`.
392
+ 2. **`create_market_category`** — `category_id` must equal `PlatformRegistry.next_category_id` before bump; PDA `["market-category", platform_id, category_id]`.
393
+ 3. **`create_market`** — if `platform_id == 0` then `category_id` must be `0`. If `platform_id > 0`, pass `platform_registry`; if `category_id > 0`, pass `market_category`.
394
+
395
+ Use **`derivePlatformRegistry`**, **`derivePlatformProfile`**, **`deriveMarketCategory`** when building instructions manually.
396
+
397
+ ## Market types
398
+
399
+ Use `marketType: 'parimutuel'` or `'completeSet'` at creation; the choice is permanent.
400
+
401
+ - **Parimutuel** — pooled stakes, no outcome SPLs; good default for prediction apps. Optional **`isEarlyWithdrawAllowed`** on create (default **allowed**); when disabled, users cannot call **`parimutuelWithdraw`** early.
402
+ - **Complete-set** — outcome SPL tokens; you still need external liquidity for single-leg trading.
403
+
201
404
  ## PDA helpers
202
405
 
203
- All PDAs are derived in `pda.ts` and re-exported from the package:
406
+ From `@vercora-protocol/sdk`:
204
407
 
205
408
  ```ts
206
409
  import {
410
+ PROGRAM_ID,
207
411
  deriveGlobalConfig,
208
412
  deriveAllowedMint,
209
413
  deriveMarket,
@@ -213,24 +417,49 @@ import {
213
417
  deriveResolver,
214
418
  deriveAllResolvers,
215
419
  deriveResolutionVote,
420
+ deriveMarketOutcome,
421
+ deriveAllMarketOutcomes,
422
+ deriveParimutuelState,
423
+ deriveParimutuelPosition,
216
424
  deriveUserProfile,
217
425
  derivePlatformRegistry,
218
426
  derivePlatformProfile,
219
427
  deriveMarketCategory,
428
+ bnLike,
429
+ bnToU32,
430
+ bnToU8,
220
431
  } from "@vercora-protocol/sdk";
221
432
 
222
- const marketPda = deriveMarket(program.programId, creatorPubkey, marketId);
223
- const vaultPda = deriveVault(program.programId, marketPda);
224
- const [mint0] = deriveAllOutcomeMints(program.programId, marketPda, 2);
433
+ const marketPda = deriveMarket(PROGRAM_ID, creatorPubkey, marketId);
434
+ const vaultPda = deriveVault(PROGRAM_ID, marketPda);
225
435
  ```
226
436
 
227
- ## TypeScript types
437
+ Offsets **`MARKET_ACCOUNT_CREATOR_MEMCMP_OFFSET`** and **`MARKET_ACCOUNT_PLATFORM_ID_MEMCMP_OFFSET`** are exported for custom `getProgramAccounts` filters.
438
+
439
+ ## `marketUi` helpers
440
+
441
+ ```ts
442
+ import {
443
+ getMarketLifecycleStatus,
444
+ formatTimeLeft,
445
+ listedMarketFeedTag,
446
+ } from "@vercora-protocol/sdk";
228
447
 
229
- All parameter and account types are exported directly:
448
+ const status = getMarketLifecycleStatus({
449
+ isVoided: false,
450
+ isClosedEarly: false,
451
+ winningOutcomeIndex: null,
452
+ closeAt: ts,
453
+ });
454
+ ```
455
+
456
+ ## TypeScript types
230
457
 
231
458
  ```ts
232
459
  import type {
233
460
  CreateMarketParams,
461
+ ListedMarket,
462
+ GetUsersMarketsFilters,
234
463
  MarketAccount,
235
464
  ParimutuelStateAccount,
236
465
  ParimutuelOdds,
@@ -241,20 +470,18 @@ import type {
241
470
 
242
471
  ## AI agent integration
243
472
 
244
- If you are building agents or automation on top of this protocol:
245
-
246
- 1. **Bootstrap**: create a `Connection`, wallet, `Program<Vercora>`, then `PredictionMarketClient`.
247
- 2. **Read + route**: call `fetchMarket(marketPda)` and branch by `market.marketType`.
248
- 3. **Flow**:
249
- - Optional: `registerPlatform` → `createMarketCategory` for a scoped listing; then `createMarket` with matching `platformId` / `categoryId`.
250
- - Create: `createMarket` → `initializeMarketResolverSlots` `initializeMarketMints` (or `initializeParimutuelState`)
251
- - Complete-set: `mintCompleteSet` → `redeemCompleteSet` → `redeemWinning`
252
- - Parimutuel: `parimutuelStake` `parimutuelWithdraw` `parimutuelClaim`
253
- - Resolution: `voteResolution` `finalizeResolution`
254
- 4. **Safety**: verify signer role, derive PDAs with helpers, handle missing ATAs, return tx signatures.
473
+ 1. **Bootstrap**: `Connection` `AnchorProvider` `Program<Vercora>` from `IDL` `PredictionMarketClient`.
474
+ 2. **Branch**: `fetchMarket` → `market.marketType` (`completeSet` vs `parimutuel`).
475
+ 3. **Flows**:
476
+ - Optional: `registerPlatform` `createMarketCategory` `createMarket` with ids.
477
+ - Create: `createMarketFull` **or** `createMarket` + resolver / outcome / mint **or** pari init.
478
+ - Complete-set: `mintCompleteSet` → `redeemCompleteSet` `redeemWinning`.
479
+ - Parimutuel: `parimutuelStake` → optional `parimutuelWithdraw` (if `isEarlyWithdrawAllowed`) `parimutuelClaim`.
480
+ - Resolution: `voteResolution` → `finalizeResolution`.
481
+ 4. **Discovery**: `fetchAllMarkets`, `getUsersMarkets`, `fetchMarketsByCreator`.
482
+ 5. **Safety**: verify signers, PDAs, ATAs, and RPC limits on `getProgramAccounts`.
255
483
 
256
484
  ## Links
257
485
 
258
- - [GitHub source](https://github.com/vercora/vercora-anchor)
259
486
  - [Vercora app](https://vercora.xyz)
260
487
  - [npm package](https://www.npmjs.com/package/@vercora-protocol/sdk)
package/dist/client.d.ts CHANGED
@@ -2,7 +2,7 @@ import * as anchor from '@coral-xyz/anchor';
2
2
  import { Program, BN } from '@coral-xyz/anchor';
3
3
  import { Connection, PublicKey } from '@solana/web3.js';
4
4
  import type { Vercora } from './generated/vercora';
5
- import type { CreateMarketParams, InitializeParimutuelStateParams, ParimutuelStakeParams, ParimutuelWithdrawParams, ParimutuelClaimParams, InitializeConfigParams, UpdateConfigParams, InitializeMarketResolverSlotsParams, InitializeMarketOutcomeSlotsParams, MintCompleteSetParams, RedeemCompleteSetParams, VoteResolutionParams, FinalizeResolutionParams, RevokeResolutionVoteParams, RedeemWinningParams, CloseMarketEarlyParams, VoidMarketParams, AbandonMarketParams, GlobalConfigAccount, MarketAccount, ResolverAccount, UpsertUserProfileParams, VerifyUserProfileParams, UserProfileAccount, UpsertPlatformProfileParams, VerifyPlatformProfileParams, PlatformProfileAccount, ParimutuelStateAccount, ParimutuelPositionAccount, ParimutuelOdds, MarketCategoryAccount, CreateMarketCategoryParams, UpdateMarketCategoryParams, RegisterPlatformParams, UpdateParimutuelStateParams, ResolutionVoteAccount, ListedMarket } from './types';
5
+ import type { CreateMarketParams, InitializeParimutuelStateParams, ParimutuelStakeParams, ParimutuelWithdrawParams, ParimutuelClaimParams, InitializeConfigParams, UpdateConfigParams, InitializeMarketResolverSlotsParams, InitializeMarketOutcomeSlotsParams, MintCompleteSetParams, RedeemCompleteSetParams, VoteResolutionParams, FinalizeResolutionParams, RevokeResolutionVoteParams, RedeemWinningParams, CloseMarketEarlyParams, VoidMarketParams, AbandonMarketParams, GlobalConfigAccount, MarketAccount, ResolverAccount, UpsertUserProfileParams, VerifyUserProfileParams, UserProfileAccount, UpsertPlatformProfileParams, VerifyPlatformProfileParams, PlatformProfileAccount, ParimutuelStateAccount, ParimutuelPositionAccount, ParimutuelOdds, MarketCategoryAccount, CreateMarketCategoryParams, UpdateMarketCategoryParams, RegisterPlatformParams, UpdateParimutuelStateParams, ResolutionVoteAccount, ListedMarket, GetUsersMarketsFilters } from './types';
6
6
  export declare class PredictionMarketClient {
7
7
  readonly program: Program<Vercora>;
8
8
  readonly connection: Connection;
@@ -56,6 +56,9 @@ export declare class PredictionMarketClient {
56
56
  /**
57
57
  * Step 1 — Create Market + Vault.
58
58
  * Returns the market PDA and the transaction signature.
59
+ *
60
+ * For pari-mutuel markets, `params.isEarlyWithdrawAllowed` (default `true`) is stored on-chain.
61
+ * When `false`, `parimutuelWithdraw` fails until close/resolution (see `PredictionMarketError::EarlyWithdrawNotAllowed`).
59
62
  */
60
63
  createMarket(creator: PublicKey, collateralMint: PublicKey, creatorFeeAccount: PublicKey, params: CreateMarketParams, opts?: anchor.web3.ConfirmOptions): Promise<{
61
64
  marketPda: PublicKey;
@@ -79,6 +82,8 @@ export declare class PredictionMarketClient {
79
82
  /**
80
83
  * Convenience: run all 3 market creation steps in sequence.
81
84
  * Returns the market PDA.
85
+ *
86
+ * Pari-mutuel: passes `params.isEarlyWithdrawAllowed` through `createMarket` (default allows early withdraw).
82
87
  */
83
88
  createMarketFull(creator: PublicKey, collateralMint: PublicKey, creatorFeeAccount: PublicKey,
84
89
  /** Length must equal `params.numResolvers` (typically the first N of an 8-slot UI). */
@@ -91,6 +96,11 @@ export declare class PredictionMarketClient {
91
96
  */
92
97
  updateParimutuelState(marketPda: PublicKey, params: UpdateParimutuelStateParams, opts?: anchor.web3.ConfirmOptions): Promise<string>;
93
98
  parimutuelStake(marketPda: PublicKey, params: ParimutuelStakeParams, opts?: anchor.web3.ConfirmOptions): Promise<string>;
99
+ /**
100
+ * Reduce stake before `close_at` / resolution (early exit). Penalties and platform fees may apply.
101
+ * Fails with Anchor error **`EarlyWithdrawNotAllowed`** when `fetchMarket(marketPda).isEarlyWithdrawAllowed === false`
102
+ * (set at `createMarket` / `createMarketFull` via `CreateMarketParams.isEarlyWithdrawAllowed`).
103
+ */
94
104
  parimutuelWithdraw(marketPda: PublicKey, params: ParimutuelWithdrawParams, opts?: anchor.web3.ConfirmOptions): Promise<string>;
95
105
  parimutuelClaim(marketPda: PublicKey, params: ParimutuelClaimParams, opts?: anchor.web3.ConfirmOptions): Promise<string>;
96
106
  /**
@@ -143,6 +153,10 @@ export declare class PredictionMarketClient {
143
153
  fetchGlobalConfig(): Promise<GlobalConfigAccount>;
144
154
  fetchMarket(market: PublicKey): Promise<MarketAccount>;
145
155
  private bnFieldToListedString;
156
+ /**
157
+ * Map raw `Market` account rows to {@link ListedMarket} (including outcome labels).
158
+ */
159
+ private buildListedMarketsFromRawRows;
146
160
  /**
147
161
  * List on-chain `Market` accounts, with outcome labels fetched per market.
148
162
  * Uses Anchor `program.account.market.all()`; when `platformId` is set, uses RPC `memcmp` on
@@ -163,6 +177,14 @@ export declare class PredictionMarketClient {
163
177
  pubkey: PublicKey;
164
178
  account: MarketAccount;
165
179
  }[]>;
180
+ /**
181
+ * Markets created by `creator`, as {@link ListedMarket} rows (with outcome labels).
182
+ *
183
+ * Uses RPC `memcmp` on `creator` and optionally on `platform_id` (when `filters.platformId` is set).
184
+ * When `filters.categoryId` is set, results are narrowed after decode (see {@link GetUsersMarketsFilters}).
185
+ * Sorted by `market_id` ascending.
186
+ */
187
+ getUsersMarkets(creator: PublicKey, filters?: GetUsersMarketsFilters): Promise<ListedMarket[]>;
166
188
  /**
167
189
  * Read `MarketOutcome.label` for each active outcome index (parallel fetches).
168
190
  * Missing accounts yield `Outcome {i+1}` placeholders.