precog-markets 1.0.0

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/LICENSE.md ADDED
@@ -0,0 +1,21 @@
1
+ # MIT License
2
+
3
+ Copyright (c) 2026 Precog Markets
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,368 @@
1
+ # precog-markets
2
+
3
+ A complete, zero-dependency JavaScript SDK (ESM) for interacting with the Solana **Precog Markets** program — a trustless pari-mutuel prediction market supporting native SOL, SPL Token, and Token-2022 denominations with on-chain multi-sig governance.
4
+
5
+ > **No Anchor required.** This SDK uses raw `@solana/web3.js` `TransactionInstruction` objects with hand-rolled Borsh serialization.
6
+
7
+ ## Features
8
+
9
+ - **14 instruction builders** covering the full program lifecycle
10
+ - **5 account decoders** (Market, UserPosition, ProtocolConfig, MultisigAuthority, MultisigProposal)
11
+ - **PDA derivation** helpers for every account type
12
+ - **High-level `PrecogMarketsClient`** with auto-PDA resolution, `sendTransaction`, and batch/gPA queries
13
+ - **Discriminator-filtered RPC queries** — all `getProgramAccounts` calls use 8-byte account discriminator `memcmp` filters for efficient fetching
14
+ - **Low-level `BorshWriter`/`BorshReader`** for custom serialization needs
15
+ - **Full TypeScript declarations** (`index.d.ts`)
16
+ - **ESM-only** (`"type": "module"`)
17
+ - **Peer dependency** on `@solana/web3.js ^1.87` — no other runtime deps
18
+
19
+ ## Installation
20
+
21
+ ```bash
22
+ npm install precog-markets @solana/web3.js
23
+ ```
24
+
25
+ ## Quick Start
26
+
27
+ ```js
28
+ import { Connection, Keypair, LAMPORTS_PER_SOL } from "@solana/web3.js";
29
+ import { PrecogMarketsClient } from "precog-markets";
30
+
31
+ const connection = new Connection("https://api.devnet.solana.com", "confirmed");
32
+ const client = new PrecogMarketsClient(connection);
33
+ const admin = Keypair.generate();
34
+
35
+ // 1. Initialize protocol (one-time)
36
+ const { protocolConfig } = await client.initializeProtocol(
37
+ admin,
38
+ admin.publicKey, // treasury
39
+ 200 // 2% default fee
40
+ );
41
+
42
+ // 2. Create a SOL market
43
+ const { market } = await client.createSolMarket({
44
+ payer: admin,
45
+ marketId: 1n,
46
+ title: "Will $PELF hit 10m MC by Jun?",
47
+ description: "Market cap milestone",
48
+ outcomeLabels: ["Yes", "No"],
49
+ resolutionDeadline: BigInt(Math.floor(Date.now() / 1000) + 86400 * 30),
50
+ });
51
+
52
+ // 3. Place a bet
53
+ const bettor = Keypair.generate();
54
+ const { position } = await client.placeSolBet({
55
+ bettor,
56
+ market,
57
+ outcomeIndex: 0, // "Yes"
58
+ amount: LAMPORTS_PER_SOL, // 1 SOL
59
+ });
60
+
61
+ // 4. Fetch & inspect
62
+ const mkt = await client.fetchMarket(market);
63
+ console.log(mkt.title); // "Will $PELF hit 10m MC by Jun?"
64
+ console.log(mkt.outcomeLabels); // ["Yes", "No"]
65
+ console.log(mkt.outcomePools); // [1000000000n, 0n]
66
+ console.log(mkt.statusName); // "Open"
67
+
68
+ // 5. Implied probabilities
69
+ const probs = PrecogMarketsClient.getImpliedProbabilities(
70
+ mkt.outcomePools, mkt.totalPool
71
+ );
72
+ console.log(probs); // [1, 0]
73
+ ```
74
+
75
+ ## Architecture
76
+
77
+ ```
78
+ src/
79
+ ├── index.js # Barrel re-exports
80
+ ├── index.d.ts # TypeScript declarations
81
+ ├── constants.js # Program ID, seeds, discriminators, enums, errors
82
+ ├── pda.js # PDA derivation for all account types
83
+ ├── serialization.js # BorshWriter / BorshReader
84
+ ├── accounts.js # Account decoders (Market, UserPosition, etc.)
85
+ ├── instructions.js # Instruction builders (all 14 instructions)
86
+ └── client.js # High-level PrecogMarketsClient
87
+ ```
88
+
89
+ ## Module Structure
90
+
91
+ ### Constants (`precog-markets/constants`)
92
+
93
+ | Export | Description |
94
+ |--------|-------------|
95
+ | `PROGRAM_ID` | On-chain program address |
96
+ | `MarketStatus` | `{ Open: 0, Resolved: 1, Finalized: 2, Voided: 3 }` |
97
+ | `TokenDenomination` | `{ NativeSol: 0, SplToken: 1, Token2022: 2 }` |
98
+ | `ProposalActionTag` | Enum tags for multisig proposal actions |
99
+ | `ErrorCode` / `ErrorName` | Bidirectional error code ↔ name maps |
100
+ | `SEEDS` | PDA seed buffers |
101
+ | `DISCRIMINATORS` | Single-byte (`u8`) instruction discriminators |
102
+ | `ACCOUNT_DISCRIMINATORS` | 8-byte account magic headers for `memcmp` filtering |
103
+ | `MAX_OUTCOMES`, `MAX_FEE_BPS`, etc. | Protocol limits |
104
+
105
+ ### PDA Derivation (`precog-markets/pda`)
106
+
107
+ Every PDA the program uses has a corresponding `find*Address()` function:
108
+
109
+ ```js
110
+ import { findMarketAddress, findPositionAddress } from "precog-markets";
111
+
112
+ const [marketPda, bump] = await findMarketAddress(authority, marketId);
113
+ const [positionPda] = await findPositionAddress(market, owner, outcomeIndex);
114
+ ```
115
+
116
+ | Function | Seeds |
117
+ |----------|-------|
118
+ | `findProtocolConfigAddress()` | `["protocol_config"]` |
119
+ | `findMarketAddress(authority, marketId)` | `["market", authority, marketId_le]` |
120
+ | `findVaultAddress(market)` | `["vault", market]` |
121
+ | `findVaultAuthorityAddress(market)` | `["vault_authority", market]` |
122
+ | `findPositionAddress(market, owner, index)` | `["position", market, owner, index]` |
123
+ | `findMultisigAddress(creator, nonce)` | `["multisig", creator, nonce_le]` |
124
+ | `findProposalAddress(multisig, proposalId)` | `["proposal", multisig, proposalId_le]` |
125
+
126
+ ### Account Decoders (`precog-markets/accounts`)
127
+
128
+ ```js
129
+ import { decodeMarket } from "precog-markets";
130
+
131
+ const accountInfo = await connection.getAccountInfo(marketAddress);
132
+ const market = decodeMarket(accountInfo.data);
133
+ ```
134
+
135
+ | Decoder | Returns |
136
+ |---------|---------|
137
+ | `decodeMarket(data)` | `MarketAccount` |
138
+ | `decodeUserPosition(data)` | `UserPositionAccount` |
139
+ | `decodeProtocolConfig(data)` | `ProtocolConfigAccount` |
140
+ | `decodeMultisigAuthority(data)` | `MultisigAuthorityAccount` |
141
+ | `decodeMultisigProposal(data)` | `MultisigProposalAccount` |
142
+
143
+ #### Market Account Fields
144
+
145
+ The Market account includes Token-2022 transfer fee metadata:
146
+
147
+ | Field | Type | Description |
148
+ |-------|------|-------------|
149
+ | `hasTransferFee` | `boolean` | Whether the token mint has a transfer fee extension |
150
+ | `transferFeeBps` | `number` | Transfer fee in basis points (Token-2022 only) |
151
+ | `maxTransferFee` | `bigint` | Maximum transfer fee in token base units |
152
+
153
+ ### Instruction Builders (`precog-markets/instructions`)
154
+
155
+ Each builder returns a `TransactionInstruction`. Pass your own accounts — the SDK never does PDA resolution at this level.
156
+
157
+ > **Note:** Instruction data uses a single `u8` byte discriminator (NOT a 4-byte or 8-byte Anchor discriminator).
158
+
159
+ ```js
160
+ import { placeBet } from "precog-markets";
161
+
162
+ const ix = placeBet(
163
+ { market, vault, position, bettor: bettor.publicKey },
164
+ { outcomeIndex: 0, amount: 1_000_000_000n }
165
+ );
166
+ ```
167
+
168
+ | Builder | Instruction |
169
+ |---------|-------------|
170
+ | `initializeProtocol(accounts, args)` | One-time protocol setup |
171
+ | `createMarket(accounts, args)` | Create a prediction market |
172
+ | `placeBet(accounts, args)` | Deposit SOL/tokens on an outcome |
173
+ | `resolveMarket(accounts, args)` | Single-sig resolve |
174
+ | `finalizeMarket(accounts)` | Permissionless crank after dispute window |
175
+ | `claimWinnings(accounts)` | Claim payout |
176
+ | `voidMarket(accounts)` | Void a market |
177
+ | `claimRefund(accounts)` | Refund on voided markets |
178
+ | `updateProtocolConfig(accounts, args)` | Admin config update |
179
+ | `createMultisig(accounts, args)` | Create M-of-N multisig |
180
+ | `createProposal(accounts, args)` | Propose multisig action |
181
+ | `approveProposal(accounts)` | Approve a proposal |
182
+ | `executeProposal(accounts)` | Execute approved proposal |
183
+ | `harvestWithheldTokens(accounts)` | Harvest Token-2022 transfer fees |
184
+
185
+ ### High-Level Client (`precog-markets/client`)
186
+
187
+ The `PrecogMarketsClient` wraps everything with automatic PDA derivation and transaction sending:
188
+
189
+ ```js
190
+ const client = new PrecogMarketsClient(connection);
191
+
192
+ // Transactional (sign + send)
193
+ await client.createSolMarket({ ... });
194
+ await client.placeSolBet({ ... });
195
+ await client.resolveMarket(authority, market, 0);
196
+ await client.finalizeMarket(payer, market);
197
+ await client.claimSolWinnings({ ... });
198
+
199
+ // Fetching
200
+ const market = await client.fetchMarket(address);
201
+ const positions = await client.getPositionsByOwner(owner);
202
+ const allMarkets = await client.getAllMarkets({ authority });
203
+
204
+ // Utilities
205
+ const payout = PrecogMarketsClient.calculatePayout(amount, winPool, totalPool, feeBps);
206
+ const probs = PrecogMarketsClient.getImpliedProbabilities(pools, total);
207
+ ```
208
+
209
+ ## SPL Token / Token-2022 Markets
210
+
211
+ For non-SOL markets, use `createTokenMarket` and `placeTokenBet`:
212
+
213
+ ```js
214
+ import { TOKEN_PROGRAM_ID, TokenDenomination } from "precog-markets";
215
+
216
+ const { market, vaultAuthority } = await client.createTokenMarket({
217
+ payer: admin,
218
+ marketId: 2n,
219
+ title: "USDC market",
220
+ description: "...",
221
+ outcomeLabels: ["Yes", "No"],
222
+ resolutionDeadline: deadline,
223
+ tokenMint: usdcMint,
224
+ tokenVault: vaultAta, // ATA owned by vaultAuthority PDA
225
+ tokenProgram: TOKEN_PROGRAM_ID,
226
+ denomination: TokenDenomination.SplToken, // 1
227
+ });
228
+
229
+ await client.placeTokenBet({
230
+ bettor,
231
+ market,
232
+ outcomeIndex: 0,
233
+ amount: 1_000_000n, // 1 USDC (6 decimals)
234
+ bettorTokenAccount: bettorAta,
235
+ tokenVault: vaultAta,
236
+ tokenMint: usdcMint,
237
+ tokenProgram: TOKEN_PROGRAM_ID,
238
+ });
239
+ ```
240
+
241
+ ## Multi-Sig Governance
242
+
243
+ Markets can be governed by an on-chain M-of-N multisig:
244
+
245
+ ```js
246
+ // Create a 2-of-3 multisig
247
+ const { multisig } = await client.createMultisig(
248
+ creator, 0n, 2, [signer1.publicKey, signer2.publicKey, signer3.publicKey]
249
+ );
250
+
251
+ // Create market with multisig authority
252
+ const { market } = await client.createSolMarket({
253
+ payer: creator,
254
+ authority: multisig, // Use multisig PDA as authority
255
+ authorityIsMultisig: true,
256
+ ...
257
+ });
258
+
259
+ // Propose resolution (auto-approves for proposer)
260
+ const { proposal } = await client.createProposal({
261
+ proposer: signer1,
262
+ multisig,
263
+ market,
264
+ action: { type: "ResolveMarket", fields: { winningOutcome: 0 } },
265
+ });
266
+
267
+ // Second signer approves
268
+ await client.approveProposal(signer2, proposal, multisig);
269
+
270
+ // Anyone can execute once threshold is met
271
+ await client.executeProposal(anyPayer, proposal, multisig, market);
272
+ ```
273
+
274
+ ### Multisig Governance Actions
275
+
276
+ | Action | Fields |
277
+ |--------|--------|
278
+ | `ResolveMarket` | `{ winningOutcome: number }` |
279
+ | `VoidMarket` | (none) |
280
+ | `UpdateDeadline` | `{ newDeadline: bigint }` |
281
+ | `UpdateFeeBps` | `{ newFeeBps: number }` |
282
+ | `AddSigner` | `{ newSigner: PublicKey }` |
283
+ | `RemoveSigner` | `{ signer: PublicKey }` |
284
+ | `ChangeThreshold` | `{ newThreshold: number }` |
285
+
286
+ ## Market Lifecycle
287
+
288
+ ```
289
+ Open ──→ Resolved ──→ (24h dispute) ──→ Finalized
290
+ │ │
291
+ │ ┌──────────────────────────┐ │
292
+ └──→ Voided │ ▼
293
+ │ │ claimWinnings()
294
+ ▼ │
295
+ claimRefund() │
296
+
297
+ finalizeMarket() ◄─────────────────┘
298
+ ```
299
+
300
+ 1. **Open** — Bets accepted until `resolutionDeadline`
301
+ 2. **Resolved** — Authority (or multisig) declares winning outcome
302
+ 3. **Dispute window** — 24h period for challenges
303
+ 4. **Finalized** — Anyone cranks `finalizeMarket`; winners claim payouts
304
+ 5. **Voided** (alternate) — Authority voids; all bettors get full refunds
305
+
306
+ ## Account Discriminators
307
+
308
+ Every on-chain account begins with an 8-byte magic header used for type identification:
309
+
310
+ | Account | Discriminator (hex) | ASCII |
311
+ |---------|-------------------|-------|
312
+ | Market | `4d 41 52 4b 45 54 56 32` | `MARKETV2` |
313
+ | UserPosition | `50 4f 53 49 54 4e 56 31` | `POSITNV1` |
314
+ | ProtocolConfig | `50 52 4f 54 4f 43 4f 4c` | `PROTOCOL` |
315
+ | MultisigAuthority | `4d 55 4c 54 53 49 47 31` | `MULTSIG1` |
316
+ | MultisigProposal | `50 52 4f 50 4f 53 4c 31` | `PROPOSL1` |
317
+
318
+ The SDK uses these discriminators in all `getProgramAccounts` calls via `memcmp` filters for efficient RPC queries.
319
+
320
+ ## Error Handling
321
+
322
+ The SDK exports all 56 program error codes:
323
+
324
+ ```js
325
+ import { ErrorCode, ErrorName } from "precog-markets";
326
+
327
+ ErrorCode[6]; // "MarketNotOpen"
328
+ ErrorName["MarketNotOpen"]; // 6
329
+ ```
330
+
331
+ ## Custom Program ID
332
+
333
+ Every function and the client accept an optional `programId` override:
334
+
335
+ ```js
336
+ import { PrecogMarketsClient, findMarketAddress } from "precog-markets";
337
+
338
+ const customProgramId = new PublicKey("YourProgram...");
339
+ const client = new PrecogMarketsClient(connection, customProgramId);
340
+ const [market] = await findMarketAddress(authority, 1n, customProgramId);
341
+ ```
342
+
343
+ ## Serialization Utilities
344
+
345
+ The `BorshWriter`/`BorshReader` classes are exported for advanced use:
346
+
347
+ ```js
348
+ import { BorshWriter, BorshReader } from "precog-markets";
349
+
350
+ const writer = new BorshWriter();
351
+ writer.writeU64(42n).writeString("hello").writeBool(true);
352
+ const buf = writer.toBuffer();
353
+
354
+ const reader = new BorshReader(buf);
355
+ console.log(reader.readU64()); // 42n
356
+ console.log(reader.readString()); // "hello"
357
+ console.log(reader.readBool()); // true
358
+ ```
359
+
360
+ ## Requirements
361
+
362
+ - Node.js ≥ 18
363
+ - `@solana/web3.js` ^1.87.0 (peer dependency)
364
+ - `@solana/spl-token` (optional peer, for creating ATAs)
365
+
366
+ ## License
367
+
368
+ MIT
package/package.json ADDED
@@ -0,0 +1,69 @@
1
+ {
2
+ "name": "precog-markets",
3
+ "version": "1.0.0",
4
+ "description": "JavaScript SDK for Precog Markets — a Solana prediction market program — pari-mutuel markets with SOL, SPL Token, and Token-2022 support",
5
+ "type": "module",
6
+ "main": "./src/index.js",
7
+ "module": "./src/index.js",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./src/index.js",
11
+ "types": "./src/index.d.ts"
12
+ },
13
+ "./constants": {
14
+ "import": "./src/constants.js",
15
+ "types": "./src/constants.d.ts"
16
+ },
17
+ "./pda": {
18
+ "import": "./src/pda.js",
19
+ "types": "./src/pda.d.ts"
20
+ },
21
+ "./serialization": {
22
+ "import": "./src/serialization.js",
23
+ "types": "./src/serialization.d.ts"
24
+ },
25
+ "./accounts": {
26
+ "import": "./src/accounts.js",
27
+ "types": "./src/accounts.d.ts"
28
+ },
29
+ "./instructions": {
30
+ "import": "./src/instructions.js",
31
+ "types": "./src/instructions.d.ts"
32
+ },
33
+ "./client": {
34
+ "import": "./src/client.js",
35
+ "types": "./src/client.d.ts"
36
+ }
37
+ },
38
+ "files": [
39
+ "src/**/*.js",
40
+ "src/**/*.d.ts",
41
+ "README.md",
42
+ "LICENSE"
43
+ ],
44
+ "scripts": {
45
+ "test": "node --test test/",
46
+ "lint": "echo 'no lint configured'"
47
+ },
48
+ "peerDependencies": {
49
+ "@solana/web3.js": "^1.87.0"
50
+ },
51
+ "peerDependenciesMeta": {
52
+ "@solana/spl-token": {
53
+ "optional": true
54
+ }
55
+ },
56
+ "keywords": [
57
+ "solana",
58
+ "prediction-market",
59
+ "pari-mutuel",
60
+ "spl-token",
61
+ "token-2022",
62
+ "multisig",
63
+ "sdk"
64
+ ],
65
+ "license": "MIT",
66
+ "engines": {
67
+ "node": ">=18"
68
+ }
69
+ }