@percolatorct/sdk 1.0.0-beta.13 → 1.0.0-beta.14

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 DELETED
@@ -1,754 +0,0 @@
1
- # @percolator/sdk
2
-
3
- TypeScript SDK for building clients, bots, and UIs on top of the [Percolator](https://github.com/dcccrypto/percolator) perpetual futures protocol on Solana.
4
-
5
- > **⚠️ BETA** — `1.0.0-beta.1`. Security audit (0x-SquidSol) complete. All 709 tests passing. Pending Helius API key + mainnet market deployment before `1.0.0` stable.
6
-
7
- [![npm](https://img.shields.io/npm/v/@percolator/sdk?color=14F195)](https://www.npmjs.com/package/@percolator/sdk)
8
- [![License](https://img.shields.io/badge/license-Apache--2.0-blue)](LICENSE)
9
-
10
- ---
11
-
12
- ## Installation
13
-
14
- ```bash
15
- pnpm add @percolator/sdk
16
- # or
17
- npm install @percolator/sdk
18
- ```
19
-
20
- **Peer dependency:** `@solana/web3.js ^1.95`
21
-
22
- ---
23
-
24
- ## Quick Start
25
-
26
- ```typescript
27
- import {
28
- getProgramId,
29
- deriveVaultAuthority,
30
- encodeInitUser,
31
- encodeDepositCollateral,
32
- encodeTradeNoCpi,
33
- parseHeader,
34
- parseConfig,
35
- parseAllAccounts,
36
- detectSlabLayout,
37
- computeMarkPnl,
38
- computeLiqPrice,
39
- simulateOrSend,
40
- } from "@percolator/sdk";
41
-
42
- // Get program ID (defaults to devnet)
43
- const programId = getProgramId("devnet");
44
-
45
- // Derive vault authority PDA
46
- const [vaultAuth, bump] = deriveVaultAuthority(programId, slabPubkey);
47
-
48
- // Read and parse on-chain slab account
49
- const slabInfo = await connection.getAccountInfo(slabPubkey);
50
- const slabData = new Uint8Array(slabInfo!.data);
51
- const header = parseHeader(slabData);
52
- const layout = detectSlabLayout(slabData.length);
53
- const config = parseConfig(slabData, layout!);
54
- const accounts = parseAllAccounts(slabData);
55
-
56
- // Compute PnL for a position
57
- const pnl = computeMarkPnl(positionSize, entryPrice, oraclePrice);
58
- const liqPrice = computeLiqPrice(entryPrice, capital, positionSize, 500n);
59
- ```
60
-
61
- ---
62
-
63
- ## Features
64
-
65
- ### ABI Encoding & Decoding
66
-
67
- Type-safe instruction builders matching the on-chain Rust layout byte-for-byte:
68
-
69
- ```typescript
70
- import { buildInitMarketIxData, buildTradeNoCpiIxData, IX_TAG } from "@percolator/sdk";
71
-
72
- // Build InitMarket instruction data (256 bytes)
73
- const data = buildInitMarketIxData({
74
- admin: adminPubkey,
75
- collateralMint: mintPubkey,
76
- indexFeedId: pythFeedId,
77
- maxStaleSecs: 60n,
78
- confFilterBps: 250,
79
- invert: false,
80
- unitScale: 1_000_000_000, // lamports per unit
81
- riskParams: { /* ... */ },
82
- });
83
-
84
- // Build trade instruction
85
- const tradeData = buildTradeNoCpiIxData({
86
- userIdx: 0,
87
- lpIdx: 0,
88
- requestedSize: 1_000_000n, // positive = long, negative = short
89
- maxSlippage: 50, // bps
90
- });
91
- ```
92
-
93
- **Supported instructions:** `InitMarket`, `InitUser`, `InitLP`, `DepositCollateral`, `WithdrawCollateral`, `KeeperCrank`, `TradeNoCpi`, `TradeCpi`, `LiquidateAtOracle`, `CloseAccount`, `TopUpInsurance`, `SetRiskThreshold`, `UpdateAdmin`, `UpdateConfig`, `SetMaintenanceFee`, `PushOraclePrice`, `ResolveMarket`, `SetOiImbalanceHardBlock`, `SetOracleAuthority`, and more.
94
-
95
- ### Admin Instructions
96
-
97
- #### SetOiImbalanceHardBlock (tag=71)
98
-
99
- Prevents new trades from pushing the long/short OI skew above a configurable threshold.
100
- When triggered, the on-chain error `OiImbalanceHardBlock` (code 59) is returned.
101
-
102
- ```typescript
103
- import {
104
- encodeSetOiImbalanceHardBlock,
105
- ACCOUNTS_SET_OI_IMBALANCE_HARD_BLOCK,
106
- buildAccountMetas,
107
- buildIx,
108
- simulateOrSend,
109
- } from "@percolator/sdk";
110
-
111
- // threshold_bps = 0 → hard block disabled (default)
112
- // threshold_bps = 5_000 → block trades that push skew above 50%
113
- // threshold_bps = 8_000 → block trades that push skew above 80%
114
- // threshold_bps = 10_000 → lock dominant side once any OI exists
115
-
116
- const data = encodeSetOiImbalanceHardBlock({ thresholdBps: 8_000 });
117
- const keys = buildAccountMetas(ACCOUNTS_SET_OI_IMBALANCE_HARD_BLOCK, [
118
- adminPublicKey, // [signer]
119
- slabPublicKey, // [writable]
120
- ]);
121
- const ix = buildIx({ programId, keys, data });
122
-
123
- const result = await simulateOrSend({ connection, ix, signers: [admin] });
124
- console.log("signature:", result.signature);
125
- ```
126
-
127
- #### SetOracleAuthority (tag=16)
128
-
129
- Delegates the `PushOraclePrice` right to a specific keypair (e.g. a crank bot).
130
- Pass `PublicKey.default` (all zeros) to revoke — the program then falls back to Pyth/Chainlink.
131
-
132
- ```typescript
133
- import { PublicKey } from "@solana/web3.js";
134
- import {
135
- encodeSetOracleAuthority,
136
- ACCOUNTS_SET_ORACLE_AUTHORITY,
137
- buildAccountMetas,
138
- buildIx,
139
- simulateOrSend,
140
- } from "@percolator/sdk";
141
-
142
- // Delegate to a crank bot
143
- const data = encodeSetOracleAuthority({ newAuthority: crankBot.publicKey });
144
- const keys = buildAccountMetas(ACCOUNTS_SET_ORACLE_AUTHORITY, [
145
- adminPublicKey, // [signer, writable]
146
- slabPublicKey, // [writable]
147
- ]);
148
- const ix = buildIx({ programId, keys, data });
149
-
150
- await simulateOrSend({ connection, ix, signers: [admin] });
151
-
152
- // Revoke — fall back to Pyth/Chainlink
153
- const revokeData = encodeSetOracleAuthority({ newAuthority: PublicKey.default });
154
- ```
155
-
156
- See [`examples/admin-instructions.ts`](examples/admin-instructions.ts) for full end-to-end examples.
157
-
158
- ### Account Deserialization
159
-
160
- Parse the on-chain slab account into typed TypeScript objects:
161
-
162
- ```typescript
163
- import {
164
- parseHeader,
165
- parseConfig,
166
- parseAllAccounts,
167
- detectSlabLayout,
168
- } from "@percolator/sdk";
169
-
170
- const slabData = new Uint8Array(accountInfo.data);
171
- const header = parseHeader(slabData);
172
- const layout = detectSlabLayout(slabData.length)!;
173
- const config = parseConfig(slabData, layout);
174
- const accounts = parseAllAccounts(slabData);
175
-
176
- // header.magic, header.version, header.admin, header.nonce
177
- // header.resolved, header.paused
178
-
179
- // config.collateralMint, config.vaultPubkey, config.indexFeedId
180
- // config.maxStalenessSlots, config.confFilterBps
181
- // config.fundingHorizonSlots, config.fundingKBps
182
- // config.threshFloor, config.threshRiskBps
183
-
184
- // accounts[i].account.owner, accounts[i].account.capital,
185
- // accounts[i].account.pnl, accounts[i].account.positionSize
186
- ```
187
-
188
- ### PDA Derivation
189
-
190
- All program-derived addresses with correct seeds:
191
-
192
- ```typescript
193
- import {
194
- deriveVaultAuthority,
195
- deriveLpPda,
196
- deriveInsuranceLpMint,
197
- } from "@percolator/sdk";
198
-
199
- const [vaultAuth, bump] = deriveVaultAuthority(programId, slab);
200
- const [lpPda, lpBump] = deriveLpPda(programId, slab, lpIndex);
201
- const [insMint, insBump] = deriveInsuranceLpMint(programId, slab);
202
- ```
203
-
204
- ### Trading Math
205
-
206
- Coin-margined perpetual math utilities (all BigInt, no floating-point):
207
-
208
- ```typescript
209
- import {
210
- computeMarkPnl,
211
- computeLiqPrice,
212
- computeEntryPrice,
213
- computeEffectiveLeverage,
214
- } from "@percolator/sdk";
215
-
216
- // Mark-to-market PnL (in native token units)
217
- const pnl = computeMarkPnl(positionSize, entryPriceE6, oraclePriceE6);
218
-
219
- // Liquidation price given capital and maintenance margin
220
- const liqPrice = computeLiqPrice(entryPriceE6, capital, positionSize, 500n);
221
-
222
- // All values use e6 format: 1 USD = 1_000_000
223
- ```
224
-
225
- ### Oracle Price Router
226
-
227
- Automatic oracle discovery and ranking for any Solana token:
228
-
229
- ```typescript
230
- import { resolvePrice } from "@percolator/sdk";
231
-
232
- const result = await resolvePrice(tokenMint);
233
- // result.bestSource — highest-confidence price source
234
- // result.allSources — all discovered sources ranked by liquidity
235
- ```
236
-
237
- Supports **Pyth**, **DexScreener** (Raydium, Orca, Meteora), and **Jupiter** price feeds.
238
-
239
- ### Program ID Configuration
240
-
241
- Network-aware program ID resolution:
242
-
243
- ```typescript
244
- import { getProgramId, getMatcherProgramId } from "@percolator/sdk";
245
-
246
- // Defaults to devnet
247
- const programId = getProgramId();
248
-
249
- // Explicit network selection
250
- const mainnetId = getProgramId("mainnet");
251
-
252
- // Environment variable override: PROGRAM_ID=<your-id>
253
- ```
254
-
255
- ### Auto-Deleveraging (ADL)
256
-
257
- ADL (Auto-Deleveraging) reduces the most-profitable opposing positions when the insurance fund is depleted. The SDK provides both on-chain and API-based ADL utilities.
258
-
259
- #### Checking if ADL is triggered
260
-
261
- ```typescript
262
- import { isAdlTriggered } from "@percolator/sdk";
263
-
264
- const accountInfo = await connection.getAccountInfo(slabKey);
265
- if (isAdlTriggered(accountInfo!.data)) {
266
- console.log("Insurance fund depleted — ADL is active");
267
- }
268
- ```
269
-
270
- #### Ranking positions for ADL
271
-
272
- ```typescript
273
- import { fetchAdlRankedPositions } from "@percolator/sdk";
274
-
275
- const { ranked, longs, shorts, isTriggered } = await fetchAdlRankedPositions(
276
- connection,
277
- slabKey,
278
- );
279
- // ranked — all positions sorted by PnL% descending (ADL priority order)
280
- // longs — top-ranked long position (ADL target if insurance negative on short side)
281
- // shorts — top-ranked short position
282
- // isTriggered — whether pnl_pos_tot exceeds max_pnl_cap on-chain
283
- ```
284
-
285
- #### Building an ADL instruction
286
-
287
- ```typescript
288
- import { buildAdlInstruction, buildAdlTransaction, getProgramId } from "@percolator/sdk";
289
-
290
- const programId = getProgramId("devnet");
291
-
292
- // Build instruction directly (caller already has target index)
293
- const ix = buildAdlInstruction(
294
- callerPublicKey, // keeper / crank wallet
295
- slabPublicKey,
296
- oracleFeedPublicKey,
297
- programId,
298
- targetAccountIndex, // number — index of account to deleverage
299
- );
300
-
301
- // OR: fetch + rank + pick top target automatically
302
- const ix2 = await buildAdlTransaction(
303
- connection,
304
- callerPublicKey,
305
- slabPublicKey,
306
- oracleFeedPublicKey,
307
- programId,
308
- "long", // side to deleverage ("long" | "short")
309
- );
310
- ```
311
-
312
- #### Decoding on-chain ADL events
313
-
314
- ```typescript
315
- import { parseAdlEvent } from "@percolator/sdk";
316
-
317
- // After sending / confirming an ExecuteAdl transaction:
318
- const tx = await connection.getTransaction(sig, { commitment: "confirmed" });
319
- const event = parseAdlEvent(tx?.meta?.logMessages ?? []);
320
- // event.tag — 0xAD1E_0001 (2904424449)
321
- // event.targetIdx — account index that was deleveraged
322
- // event.price — oracle price at execution (e6)
323
- // event.closedAbs — absolute position size closed (i128)
324
- ```
325
-
326
- #### Fetching ADL rankings via HTTP API
327
-
328
- ```typescript
329
- import { fetchAdlRankings } from "@percolator/sdk";
330
-
331
- const result = await fetchAdlRankings(
332
- "https://percolatorlaunch.com/api",
333
- slabAddress,
334
- );
335
- // result.slabAddress — slab public key (base58)
336
- // result.adlNeeded — true if ADL is triggered (capExceeded or utilizationTriggered)
337
- // result.capExceeded — true if pnlPosTot > maxPnlCap
338
- // result.insuranceDepleted — true if insurance fund balance == 0
339
- // result.utilizationTriggered — true if utilization BPS exceeds the configured ADL threshold
340
- // result.pnlPosTot — aggregate profitable PnL (decimal string)
341
- // result.maxPnlCap — max PnL cap from market config (decimal string, "0" if unconfigured)
342
- // result.excess — excess PnL above cap (decimal string)
343
- // result.insuranceFundBalance — insurance fund balance (decimal string)
344
- // result.insuranceFundFeeRevenue — insurance fund lifetime fee revenue (decimal string)
345
- // result.insuranceUtilizationBps — insurance utilization in basis points (0–10000)
346
- // result.rankings — AdlApiRanking[] sorted by rank (1 = first to deleverage)
347
- // .rank — rank (1 = highest PnL%, deleveraged first)
348
- // .idx — slab account index (pass as targetIdx to buildAdlInstruction)
349
- // .pnlAbs — absolute PnL in lamports (decimal string)
350
- // .capital — capital at entry in lamports (decimal string)
351
- // .pnlPctMillionths — pnl * 1_000_000 / capital (decimal string)
352
- ```
353
-
354
- #### ADL error codes (61–65)
355
-
356
- | Code | Name | Description |
357
- |------|------|-------------|
358
- | 61 | `EngineSideBlocked` | Trade blocked — this side is in DrainOnly or ResetPending mode |
359
- | 62 | `EngineCorruptState` | Slab state corrupt — critical internal error, please report |
360
- | 63 | `InsuranceFundNotDepleted` | ADL not triggered yet (insurance fund healthy) |
361
- | 64 | `NoAdlCandidates` | No eligible positions to deleverage |
362
- | 65 | `BankruptPositionAlreadyClosed` | Target position already closed |
363
-
364
- ---
365
-
366
- ### Transaction Helpers
367
-
368
- Build, simulate, and send transactions with error parsing:
369
-
370
- ```typescript
371
- import { buildIx, simulateOrSend } from "@percolator/sdk";
372
-
373
- const ix = buildIx({ programId, keys: accountMetas, data: ixData });
374
-
375
- const result = await simulateOrSend({
376
- connection,
377
- ix,
378
- signers: [payer],
379
- simulate: false, // true = simulate only
380
- computeUnitLimit: 400_000,
381
- });
382
-
383
- // result.signature, result.slot, result.err, result.logs
384
- // Errors are automatically parsed from logs into human-readable messages
385
- ```
386
-
387
- ### Client-Side Validation
388
-
389
- Validate parameters before submitting transactions:
390
-
391
- ```typescript
392
- import {
393
- validatePublicKey,
394
- validateAmount,
395
- validateBps,
396
- validateI128,
397
- validateIndex,
398
- } from "@percolator/sdk";
399
-
400
- // Validates a public key string (throws ValidationError on invalid input)
401
- const slabKey = validatePublicKey(slabAddress, "slab");
402
-
403
- // Validates a u64 amount (throws ValidationError if negative or > u64 max)
404
- const amount = validateAmount("1000000000", "depositAmount");
405
-
406
- // Validates basis points (0-10000)
407
- const feeBps = validateBps("50", "tradingFee");
408
- ```
409
-
410
- ---
411
-
412
- ## Mainnet vs Devnet
413
-
414
- By default the SDK targets **devnet** (safety default — PERC-697). The `NETWORK` environment
415
- variable and the `network` parameter to `getProgramId()` / `discoverMarkets()` control which
416
- program IDs and RPC endpoints are used. Production deployments always set `NETWORK=mainnet`
417
- explicitly via Railway env vars.
418
-
419
- ```typescript
420
- import { getProgramId, discoverMarkets } from "@percolator/sdk";
421
-
422
- // Devnet (default — safe fallback)
423
- const programId = getProgramId("devnet");
424
-
425
- // Mainnet — set env or pass explicitly
426
- // NETWORK=mainnet
427
- const mainnetProgramId = getProgramId("mainnet");
428
-
429
- // Market discovery — uses network from NETWORK env by default
430
- const markets = await discoverMarkets(connection);
431
- ```
432
-
433
- ### Program Addresses
434
-
435
- | Program | Network | Address |
436
- |---------|---------|---------|
437
- | Percolator | Mainnet | `GM8zjJ8LTBMv9xEsverh6H6wLyevgMHEJXcEzyY3rY24` |
438
- | Matcher | Mainnet | `DHP6DtwXP1yJsz8YzfoeigRFPB979gzmumkmCxDLSkUX` |
439
- | Percolator | Devnet | `FxfD37s1AZTeWfFQps9Zpebi2dNQ9QSSDtfMKdbsfKrD` |
440
- | Matcher | Devnet | `GTRgyTDfrMvBubALAqtHuQwT8tbGyXid7svXZKtWfC9k` |
441
-
442
- > Use `PROGRAM_ID` / `MATCHER_PROGRAM_ID` env vars to override for local test validators.
443
-
444
- ---
445
-
446
- ## RPC Connection Pool, Retry, and Health Probes
447
-
448
- The SDK provides a production-grade RPC connection pool with automatic retry,
449
- failover, and health probing. Use `RpcPool` for mainnet reliability.
450
-
451
- ### RpcPool — Multi-endpoint with failover/round-robin
452
-
453
- ```typescript
454
- import { RpcPool } from "@percolator/sdk";
455
-
456
- const pool = new RpcPool({
457
- endpoints: [
458
- { url: "https://mainnet.helius-rpc.com/?api-key=KEY", weight: 10, label: "helius" },
459
- { url: "https://api.mainnet-beta.solana.com", weight: 1, label: "public" },
460
- ],
461
- strategy: "failover", // or "round-robin"
462
- retry: {
463
- maxRetries: 3, // default: 3
464
- baseDelayMs: 500, // default: 500 (exponential backoff)
465
- maxDelayMs: 10_000, // default: 10s cap
466
- jitterFactor: 0.25, // default: 0.25 (avoid thundering herd)
467
- },
468
- requestTimeoutMs: 30_000, // default: 30s per request
469
- commitment: "confirmed", // default: confirmed
470
- });
471
-
472
- // Execute any async call through the pool — auto-retry + failover
473
- const slot = await pool.call(conn => conn.getSlot());
474
- const balance = await pool.call(conn => conn.getBalance(pubkey));
475
-
476
- // Use with discoverMarkets
477
- const markets = await pool.call(conn =>
478
- discoverMarkets(conn, programId, { apiBaseUrl: "https://percolatorlaunch.com/api" })
479
- );
480
-
481
- // Pool status & diagnostics
482
- console.log(pool.status()); // [{ label, url, healthy, failures, lastLatencyMs }]
483
- console.log(`${pool.healthyCount}/${pool.size} endpoints healthy`);
484
- ```
485
-
486
- ### Standalone retry wrapper
487
-
488
- ```typescript
489
- import { withRetry } from "@percolator/sdk";
490
- import { Connection } from "@solana/web3.js";
491
-
492
- const conn = new Connection("https://api.mainnet-beta.solana.com");
493
- const slot = await withRetry(
494
- () => conn.getSlot(),
495
- { maxRetries: 3, baseDelayMs: 1000 },
496
- );
497
- ```
498
-
499
- ### RPC health probes
500
-
501
- ```typescript
502
- import { checkRpcHealth } from "@percolator/sdk";
503
-
504
- const health = await checkRpcHealth("https://api.mainnet-beta.solana.com", 5000);
505
- console.log(`${health.endpoint}: ${health.healthy ? "UP" : "DOWN"} — ${health.latencyMs}ms, slot ${health.slot}`);
506
-
507
- // Or check all pool endpoints at once
508
- const results = await pool.healthCheck();
509
- ```
510
-
511
- ---
512
-
513
- ## RPC Concurrency
514
-
515
- `discoverMarkets()` fires one `getProgramAccounts` request per known slab tier size.
516
- There are ~15 known tier sizes. To avoid hitting rate limits on public or free-tier endpoints,
517
- the SDK caps parallel in-flight requests at **6** by default.
518
-
519
- For production use a [Helius](https://helius.dev) paid-tier key. On the free tier,
520
- pass `sequential: true` to serialize requests with exponential backoff:
521
-
522
- ```typescript
523
- import { discoverMarkets } from "@percolator/sdk";
524
-
525
- // Helius paid tier — parallel (default, fast)
526
- const markets = await discoverMarkets(connection, { maxParallelTiers: 6 });
527
-
528
- // Free-tier or public RPC — sequential with 429 backoff
529
- const marketsSafe = await discoverMarkets(connection, { sequential: true });
530
-
531
- // Custom concurrency
532
- const marketsCustom = await discoverMarkets(connection, { maxParallelTiers: 3 });
533
- ```
534
-
535
- > **Note:** Public mainnet-beta RPC (`api.mainnet-beta.solana.com`) rejects
536
- > `getProgramAccounts` calls entirely. Use the **API fallback** (below) or a
537
- > Helius/QuickNode endpoint.
538
-
539
- ## Market Discovery — 3-Tier Fallback Chain
540
-
541
- Public mainnet RPCs reject `getProgramAccounts`, which blocks `discoverMarkets()`.
542
- The SDK provides a resilient 3-tier fallback chain that works on any RPC endpoint:
543
-
544
- | Tier | Method | Requires |
545
- |------|--------|----------|
546
- | 1 | `getProgramAccounts` (RPC) | Helius/premium RPC key |
547
- | 2 | REST API (`GET /markets`) | Percolator API online |
548
- | 3 | Static bundle (bundled addresses) | Nothing — works offline |
549
-
550
- All tiers verify data on-chain via `getMultipleAccounts` (works on all RPCs).
551
-
552
- ### Recommended: Full 3-tier fallback
553
-
554
- Pass both `apiBaseUrl` and `network` to enable all three tiers:
555
-
556
- ```typescript
557
- import { discoverMarkets, getProgramId } from "@percolator/sdk";
558
- import { Connection } from "@solana/web3.js";
559
-
560
- const connection = new Connection("https://api.mainnet-beta.solana.com");
561
- const markets = await discoverMarkets(connection, getProgramId("mainnet"), {
562
- apiBaseUrl: "https://percolatorlaunch.com/api",
563
- network: "mainnet", // enables tier-3 static fallback
564
- });
565
- ```
566
-
567
- ### API-only discovery via `discoverMarketsViaApi()`
568
-
569
- Skip `getProgramAccounts` entirely — query the REST API for slab addresses,
570
- then fetch full on-chain data:
571
-
572
- ```typescript
573
- import { discoverMarketsViaApi, getProgramId } from "@percolator/sdk";
574
- import { Connection } from "@solana/web3.js";
575
-
576
- const connection = new Connection("https://api.mainnet-beta.solana.com");
577
- const programId = getProgramId("mainnet");
578
- const markets = await discoverMarketsViaApi(
579
- connection,
580
- programId,
581
- "https://percolatorlaunch.com/api",
582
- );
583
- ```
584
-
585
- ### Static-only discovery via `discoverMarketsViaStaticBundle()`
586
-
587
- Use the bundled address list directly (no network calls except `getMultipleAccounts`):
588
-
589
- ```typescript
590
- import {
591
- discoverMarketsViaStaticBundle,
592
- getStaticMarkets,
593
- getProgramId,
594
- } from "@percolator/sdk";
595
- import { Connection } from "@solana/web3.js";
596
-
597
- const connection = new Connection("https://api.mainnet-beta.solana.com");
598
- const entries = getStaticMarkets("mainnet");
599
- const markets = await discoverMarketsViaStaticBundle(
600
- connection,
601
- getProgramId("mainnet"),
602
- entries,
603
- );
604
- ```
605
-
606
- ### Extending the static registry at runtime
607
-
608
- The static bundle can be augmented before calling `discoverMarkets()`:
609
-
610
- ```typescript
611
- import { registerStaticMarkets, discoverMarkets, getProgramId } from "@percolator/sdk";
612
- import { Connection } from "@solana/web3.js";
613
-
614
- // Register known slab addresses before discovery
615
- registerStaticMarkets("mainnet", [
616
- { slabAddress: "ABC123...", symbol: "SOL-PERP" },
617
- { slabAddress: "DEF456...", symbol: "ETH-PERP" },
618
- ]);
619
-
620
- const connection = new Connection("https://api.mainnet-beta.solana.com");
621
- const markets = await discoverMarkets(connection, getProgramId("mainnet"), {
622
- apiBaseUrl: "https://percolatorlaunch.com/api",
623
- network: "mainnet",
624
- });
625
- ```
626
-
627
- ### Known addresses via `getMarketsByAddress()`
628
-
629
- If you already know your market slab addresses (e.g. from an indexer or
630
- hardcoded list), fetch them directly:
631
-
632
- ```typescript
633
- import { getMarketsByAddress, getProgramId } from "@percolator/sdk";
634
- import { Connection, PublicKey } from "@solana/web3.js";
635
-
636
- const connection = new Connection("https://api.mainnet-beta.solana.com");
637
- const markets = await getMarketsByAddress(
638
- connection,
639
- getProgramId("mainnet"),
640
- [new PublicKey("..."), new PublicKey("...")],
641
- );
642
- ```
643
-
644
- ---
645
-
646
- ## Architecture
647
-
648
- ```
649
- @percolator/sdk
650
- ├── abi/ # Binary encoding/decoding matching on-chain layout
651
- │ ├── instructions.ts # Instruction data builders (all 72 instructions)
652
- │ ├── accounts.ts # Account struct deserialization
653
- │ ├── encode.ts # Low-level binary encoding (u8/u16/u32/u64/i128/pubkey)
654
- │ ├── errors.ts # On-chain error code → human-readable parsing
655
- │ └── index.ts
656
- ├── solana/ # Solana-specific helpers
657
- │ ├── slab.ts # Slab account parser (header + config + accounts)
658
- │ ├── pda.ts # PDA derivation (vault, LP, insurance mint)
659
- │ ├── discovery.ts # Market discovery (find all Percolator markets)
660
- │ ├── rpc-pool.ts # RPC connection pool, retry, failover, health probes
661
- │ ├── dex-oracle.ts # DEX oracle price integration
662
- │ ├── token-program.ts # SPL Token helpers
663
- │ ├── ata.ts # Associated Token Account helpers
664
- │ └── index.ts
665
- ├── runtime/ # Transaction building and submission
666
- │ ├── tx.ts # buildIx, simulateOrSend, error handling
667
- │ └── index.ts
668
- ├── math/ # Trading math (all BigInt)
669
- │ ├── trading.ts # PnL, liquidation price, leverage, entry price
670
- │ └── index.ts
671
- ├── oracle/ # Price feed integration
672
- │ └── price-router.ts # Multi-source oracle resolution (Pyth, DEX, Jupiter)
673
- ├── config/ # Configuration
674
- │ └── program-ids.ts # Network-aware program IDs
675
- ├── validation.ts # Client-side parameter validation
676
- └── index.ts # Public API re-exports
677
- ```
678
-
679
- ---
680
-
681
- ## Development
682
-
683
- ### Prerequisites
684
-
685
- - Node.js 20+ and pnpm 9+
686
-
687
- ### Commands
688
-
689
- ```bash
690
- pnpm install # Install dependencies
691
- pnpm build # Build with tsup (outputs to dist/)
692
- pnpm test # Run test suite (vitest)
693
- pnpm lint # Type-check (tsc --noEmit)
694
- ```
695
-
696
- ### Testing
697
-
698
- Tests cover ABI encoding roundtrips, PDA derivation, slab parsing, validation, and trading math:
699
-
700
- ```bash
701
- pnpm test # Run all tests
702
- pnpm test -- --watch # Watch mode
703
- ```
704
-
705
- ### Publishing
706
-
707
- ```bash
708
- pnpm build
709
- npm publish --access public
710
- ```
711
-
712
- ---
713
-
714
- ## Configuration
715
-
716
- | Environment Variable | Description | Default |
717
- |---------------------|-------------|---------|
718
- | `PROGRAM_ID` | Override Percolator program ID | Network default |
719
- | `MATCHER_PROGRAM_ID` | Override Matcher program ID | Network default |
720
- | `NETWORK` | Target network (`devnet` / `mainnet`) | `devnet` |
721
-
722
- ### Devnet Program Addresses
723
-
724
- | Program | Address |
725
- |---------|---------|
726
- | Percolator | `FxfD37s1AZTeWfFQps9Zpebi2dNQ9QSSDtfMKdbsfKrD` |
727
- | Matcher | `4HcGCsyjAqnFua5ccuXyt8KRRQzKFbGTJkVChpS7Yfzy` |
728
-
729
- ---
730
-
731
- ## Browser Compatibility
732
-
733
- The SDK uses `DataView` for all binary reads (no Node.js `Buffer` dependency). Works in:
734
- - Node.js 20+
735
- - Modern browsers (Chrome, Firefox, Safari, Edge)
736
- - React Native (via `@solana/web3.js`)
737
-
738
- ---
739
-
740
- ## Related Repositories
741
-
742
- | Repository | Description |
743
- |-----------|-------------|
744
- | [percolator](https://github.com/dcccrypto/percolator) | Core risk engine crate (Rust) |
745
- | [percolator-prog](https://github.com/dcccrypto/percolator-prog) | Solana on-chain program (wrapper) |
746
- | [percolator-matcher](https://github.com/dcccrypto/percolator-matcher) | Reference matcher program for LP pricing |
747
- | [percolator-stake](https://github.com/dcccrypto/percolator-stake) | Insurance LP staking program |
748
- | [percolator-ops](https://github.com/dcccrypto/percolator-ops) | Operations dashboard |
749
- | [percolator-mobile](https://github.com/dcccrypto/percolator-mobile) | Solana Seeker mobile trading app |
750
- | [percolator-launch](https://github.com/dcccrypto/percolator-launch) | Full-stack launch platform (monorepo) |
751
-
752
- ## License
753
-
754
- Apache 2.0 — see [LICENSE](LICENSE).