@percolatorct/sdk 0.3.1 → 0.4.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/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
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
4
 
5
- > **⚠️ DISCLAIMER: FOR EDUCATIONAL PURPOSES ONLY** This code has NOT been audited. Do NOT use in production or with real funds.
5
+ > **⚠️ RELEASE CANDIDATE** `1.0.0-rc.1`. Security audit (0x-SquidSol) complete. Pending final mainnet program verification before `1.0.0` stable. Use at your own risk.
6
6
 
7
7
  [![npm](https://img.shields.io/npm/v/@percolator/sdk?color=14F195)](https://www.npmjs.com/package/@percolator/sdk)
8
8
  [![License](https://img.shields.io/badge/license-Apache--2.0-blue)](LICENSE)
@@ -382,6 +382,67 @@ if (errors.length > 0) {
382
382
 
383
383
  ---
384
384
 
385
+ ## Mainnet vs Devnet
386
+
387
+ By default the SDK targets **devnet** (safety default — PERC-697). The `NETWORK` environment
388
+ variable and the `network` parameter to `getProgramId()` / `discoverMarkets()` control which
389
+ program IDs and RPC endpoints are used. Production deployments always set `NETWORK=mainnet`
390
+ explicitly via Railway env vars.
391
+
392
+ ```typescript
393
+ import { getProgramId, discoverMarkets } from "@percolator/sdk";
394
+
395
+ // Devnet (default — safe fallback)
396
+ const programId = getProgramId("devnet");
397
+
398
+ // Mainnet — set env or pass explicitly
399
+ // NETWORK=mainnet
400
+ const mainnetProgramId = getProgramId("mainnet");
401
+
402
+ // Market discovery — uses network from NETWORK env by default
403
+ const markets = await discoverMarkets(connection);
404
+ ```
405
+
406
+ ### Program Addresses
407
+
408
+ | Program | Network | Address |
409
+ |---------|---------|---------|
410
+ | Percolator | Mainnet | `FxfD37s1AZTeWfFQps9Zpebi2dNQ9QSSDtfMKdbsfKrD` |
411
+ | Matcher | Mainnet | see `getMatcherProgramId("mainnet")` |
412
+ | Percolator | Devnet | `FxfD37s1AZTeWfFQps9Zpebi2dNQ9QSSDtfMKdbsfKrD` |
413
+
414
+ > Use `PROGRAM_ID` / `MATCHER_PROGRAM_ID` env vars to override for local test validators.
415
+
416
+ ---
417
+
418
+ ## RPC Concurrency
419
+
420
+ `discoverMarkets()` fires one `getProgramAccounts` request per known slab tier size.
421
+ There are ~15 known tier sizes. To avoid hitting rate limits on public or free-tier endpoints,
422
+ the SDK caps parallel in-flight requests at **6** by default.
423
+
424
+ For production use a [Helius](https://helius.dev) paid-tier key. On the free tier,
425
+ pass `sequential: true` to serialize requests with exponential backoff:
426
+
427
+ ```typescript
428
+ import { discoverMarkets } from "@percolator/sdk";
429
+
430
+ // Helius paid tier — parallel (default, fast)
431
+ const markets = await discoverMarkets(connection, { maxParallelTiers: 6 });
432
+
433
+ // Free-tier or public RPC — sequential with 429 backoff
434
+ const marketsSafe = await discoverMarkets(connection, { sequential: true });
435
+
436
+ // Custom concurrency
437
+ const marketsCustom = await discoverMarkets(connection, { maxParallelTiers: 3 });
438
+ ```
439
+
440
+ > **Note:** Public mainnet-beta RPC (`api.mainnet-beta.solana.com`) rejects
441
+ > `getProgramAccounts` calls entirely. A Helius or QuickNode endpoint is required for
442
+ > `discoverMarkets()` in production.
443
+
444
+ ---
445
+
385
446
  ## Architecture
386
447
 
387
448
  ```
package/dist/index.js CHANGED
@@ -1457,39 +1457,49 @@ var V_ADL_ACCT_MATCHER_CONTEXT_OFF = 160;
1457
1457
  var V_ADL_ACCT_OWNER_OFF = 192;
1458
1458
  var V_ADL_ACCT_FEE_CREDITS_OFF = 224;
1459
1459
  var V_ADL_ACCT_LAST_FEE_SLOT_OFF = 240;
1460
- var V1M_ENGINE_OFF = 632;
1461
- var V1M_CONFIG_LEN = 528;
1460
+ var V1M_ENGINE_OFF = 640;
1461
+ var V1M_CONFIG_LEN = 536;
1462
1462
  var V1M_ACCOUNT_SIZE = 248;
1463
- var V1M_ENGINE_PARAMS_OFF = 80;
1463
+ var V1M_ENGINE_PARAMS_OFF = 72;
1464
1464
  var V1M_PARAMS_SIZE = 336;
1465
- var V1M_ENGINE_CURRENT_SLOT_OFF = 416;
1466
- var V1M_ENGINE_FUNDING_INDEX_OFF = 424;
1467
- var V1M_ENGINE_LAST_FUNDING_SLOT_OFF = 440;
1468
- var V1M_ENGINE_FUNDING_RATE_BPS_OFF = 448;
1469
- var V1M_ENGINE_MARK_PRICE_OFF = 456;
1470
- var V1M_ENGINE_LAST_CRANK_SLOT_OFF = 480;
1471
- var V1M_ENGINE_MAX_CRANK_STALENESS_OFF = 488;
1472
- var V1M_ENGINE_TOTAL_OI_OFF = 496;
1473
- var V1M_ENGINE_LONG_OI_OFF = 512;
1474
- var V1M_ENGINE_SHORT_OI_OFF = 528;
1475
- var V1M_ENGINE_C_TOT_OFF = 544;
1476
- var V1M_ENGINE_PNL_POS_TOT_OFF = 560;
1477
- var V1M_ENGINE_LIQ_CURSOR_OFF = 576;
1478
- var V1M_ENGINE_GC_CURSOR_OFF = 578;
1479
- var V1M_ENGINE_LAST_SWEEP_START_OFF = 584;
1480
- var V1M_ENGINE_LAST_SWEEP_COMPLETE_OFF = 592;
1481
- var V1M_ENGINE_CRANK_CURSOR_OFF = 600;
1482
- var V1M_ENGINE_SWEEP_START_IDX_OFF = 602;
1483
- var V1M_ENGINE_LIFETIME_LIQUIDATIONS_OFF = 608;
1484
- var V1M_ENGINE_LIFETIME_FORCE_CLOSES_OFF = 616;
1485
- var V1M_ENGINE_NET_LP_POS_OFF = 624;
1465
+ var V1M_ENGINE_CURRENT_SLOT_OFF = 408;
1466
+ var V1M_ENGINE_FUNDING_INDEX_OFF = 416;
1467
+ var V1M_ENGINE_LAST_FUNDING_SLOT_OFF = 432;
1468
+ var V1M_ENGINE_FUNDING_RATE_BPS_OFF = 440;
1469
+ var V1M_ENGINE_MARK_PRICE_OFF = 448;
1470
+ var V1M_ENGINE_LAST_CRANK_SLOT_OFF = 472;
1471
+ var V1M_ENGINE_MAX_CRANK_STALENESS_OFF = 480;
1472
+ var V1M_ENGINE_TOTAL_OI_OFF = 488;
1473
+ var V1M_ENGINE_LONG_OI_OFF = 504;
1474
+ var V1M_ENGINE_SHORT_OI_OFF = 520;
1475
+ var V1M_ENGINE_C_TOT_OFF = 536;
1476
+ var V1M_ENGINE_PNL_POS_TOT_OFF = 552;
1477
+ var V1M_ENGINE_LIQ_CURSOR_OFF = 568;
1478
+ var V1M_ENGINE_GC_CURSOR_OFF = 570;
1479
+ var V1M_ENGINE_LAST_SWEEP_START_OFF = 576;
1480
+ var V1M_ENGINE_LAST_SWEEP_COMPLETE_OFF = 584;
1481
+ var V1M_ENGINE_CRANK_CURSOR_OFF = 592;
1482
+ var V1M_ENGINE_SWEEP_START_IDX_OFF = 594;
1483
+ var V1M_ENGINE_LIFETIME_LIQUIDATIONS_OFF = 600;
1484
+ var V1M_ENGINE_LIFETIME_FORCE_CLOSES_OFF = 608;
1485
+ var V1M_ENGINE_NET_LP_POS_OFF = 616;
1486
1486
  var V1M_ENGINE_LP_SUM_ABS_OFF = 632;
1487
1487
  var V1M_ENGINE_LP_MAX_ABS_OFF = 648;
1488
1488
  var V1M_ENGINE_LP_MAX_ABS_SWEEP_OFF = 664;
1489
1489
  var V1M_ENGINE_EMERGENCY_OI_MODE_OFF = 680;
1490
1490
  var V1M_ENGINE_EMERGENCY_START_SLOT_OFF = 688;
1491
1491
  var V1M_ENGINE_LAST_BREAKER_SLOT_OFF = 696;
1492
- var V1M_ENGINE_BITMAP_OFF = 728;
1492
+ var V1M_ENGINE_BITMAP_OFF = 720;
1493
+ var V1M2_ACCOUNT_SIZE = 312;
1494
+ var V1M2_ENGINE_BITMAP_OFF = 990;
1495
+ var V1M2_ENGINE_CURRENT_SLOT_OFF = 408;
1496
+ var V1M2_ENGINE_FUNDING_INDEX_OFF = 416;
1497
+ var V1M2_ENGINE_LAST_FUNDING_SLOT_OFF = 432;
1498
+ var V1M2_ENGINE_FUNDING_RATE_BPS_OFF = 440;
1499
+ var V1M2_ENGINE_MARK_PRICE_OFF = 480;
1500
+ var V1M2_ENGINE_LAST_CRANK_SLOT_OFF = 504;
1501
+ var V1M2_ENGINE_MAX_CRANK_STALENESS_OFF = 512;
1502
+ var V1M2_RUNTIME_SHIFT = 32;
1493
1503
  var ENGINE_OFF = V1_ENGINE_OFF;
1494
1504
  var ENGINE_MARK_PRICE_OFF = V1_ENGINE_MARK_PRICE_OFF;
1495
1505
  function computeSlabSize(engineOff, bitmapOff, accountSize, maxAccounts, postBitmap = 18) {
@@ -1528,6 +1538,11 @@ for (const [label, n] of [["Micro", 64], ["Small", 256], ["Medium", 1024], ["Lar
1528
1538
  const size = computeSlabSize(V1M_ENGINE_OFF, V1M_ENGINE_BITMAP_OFF, V1M_ACCOUNT_SIZE, n, 18);
1529
1539
  SLAB_TIERS_V1M[label.toLowerCase()] = { maxAccounts: n, dataSize: size, label, description: `${n} slots (V1M mainnet)` };
1530
1540
  }
1541
+ var SLAB_TIERS_V1M2 = {};
1542
+ for (const [label, n] of [["Micro", 64], ["Small", 256], ["Medium", 1024], ["Large", 4096]]) {
1543
+ const size = computeSlabSize(V1M_ENGINE_OFF, V1M2_ENGINE_BITMAP_OFF, V1M2_ACCOUNT_SIZE, n, 18);
1544
+ SLAB_TIERS_V1M2[label.toLowerCase()] = { maxAccounts: n, dataSize: size, label, description: `${n} slots (V1M2 mainnet upgraded)` };
1545
+ }
1531
1546
  var SLAB_TIERS_V_ADL = {};
1532
1547
  for (const [label, n] of [["Micro", 64], ["Small", 256], ["Medium", 1024], ["Large", 4096]]) {
1533
1548
  const size = computeSlabSize(V_ADL_ENGINE_OFF, V_ADL_ENGINE_BITMAP_OFF, V_ADL_ACCOUNT_SIZE, n, 18);
@@ -1785,6 +1800,69 @@ function buildLayoutV1M(maxAccounts) {
1785
1800
  engineInsuranceIsolationBpsOff: 64
1786
1801
  };
1787
1802
  }
1803
+ function buildLayoutV1M2(maxAccounts) {
1804
+ const engineOff = V1M_ENGINE_OFF;
1805
+ const bitmapOff = V1M2_ENGINE_BITMAP_OFF;
1806
+ const accountSize = V1M2_ACCOUNT_SIZE;
1807
+ const bitmapWords = Math.ceil(maxAccounts / 64);
1808
+ const bitmapBytes = bitmapWords * 8;
1809
+ const postBitmap = 18;
1810
+ const nextFreeBytes = maxAccounts * 2;
1811
+ const preAccountsLen = bitmapOff + bitmapBytes + postBitmap + nextFreeBytes;
1812
+ const accountsOffRel = Math.ceil(preAccountsLen / 8) * 8;
1813
+ return {
1814
+ version: 1,
1815
+ headerLen: V1_HEADER_LEN,
1816
+ configOffset: V1_HEADER_LEN,
1817
+ configLen: V1M_CONFIG_LEN,
1818
+ reservedOff: V1_RESERVED_OFF,
1819
+ engineOff,
1820
+ accountSize,
1821
+ maxAccounts,
1822
+ bitmapWords,
1823
+ accountsOff: engineOff + accountsOffRel,
1824
+ engineInsuranceOff: 16,
1825
+ engineParamsOff: V1M_ENGINE_PARAMS_OFF,
1826
+ // 72 — same as V1M
1827
+ paramsSize: V1M_PARAMS_SIZE,
1828
+ // 336 — same as V1M
1829
+ // Runtime fields: same as V1M up to fundingRateBps, then +32 shift
1830
+ engineCurrentSlotOff: V1M2_ENGINE_CURRENT_SLOT_OFF,
1831
+ engineFundingIndexOff: V1M2_ENGINE_FUNDING_INDEX_OFF,
1832
+ engineLastFundingSlotOff: V1M2_ENGINE_LAST_FUNDING_SLOT_OFF,
1833
+ engineFundingRateBpsOff: V1M2_ENGINE_FUNDING_RATE_BPS_OFF,
1834
+ engineMarkPriceOff: V1M2_ENGINE_MARK_PRICE_OFF,
1835
+ engineLastCrankSlotOff: V1M2_ENGINE_LAST_CRANK_SLOT_OFF,
1836
+ engineMaxCrankStalenessOff: V1M2_ENGINE_MAX_CRANK_STALENESS_OFF,
1837
+ // Fields after maxCrankStaleness: apply same +32 shift from V1M
1838
+ engineTotalOiOff: V1M_ENGINE_TOTAL_OI_OFF + V1M2_RUNTIME_SHIFT,
1839
+ engineLongOiOff: V1M_ENGINE_LONG_OI_OFF + V1M2_RUNTIME_SHIFT,
1840
+ engineShortOiOff: V1M_ENGINE_SHORT_OI_OFF + V1M2_RUNTIME_SHIFT,
1841
+ engineCTotOff: V1M_ENGINE_C_TOT_OFF + V1M2_RUNTIME_SHIFT,
1842
+ enginePnlPosTotOff: V1M_ENGINE_PNL_POS_TOT_OFF + V1M2_RUNTIME_SHIFT,
1843
+ engineLiqCursorOff: V1M_ENGINE_LIQ_CURSOR_OFF + V1M2_RUNTIME_SHIFT,
1844
+ engineGcCursorOff: V1M_ENGINE_GC_CURSOR_OFF + V1M2_RUNTIME_SHIFT,
1845
+ engineLastSweepStartOff: V1M_ENGINE_LAST_SWEEP_START_OFF + V1M2_RUNTIME_SHIFT,
1846
+ engineLastSweepCompleteOff: V1M_ENGINE_LAST_SWEEP_COMPLETE_OFF + V1M2_RUNTIME_SHIFT,
1847
+ engineCrankCursorOff: V1M_ENGINE_CRANK_CURSOR_OFF + V1M2_RUNTIME_SHIFT,
1848
+ engineSweepStartIdxOff: V1M_ENGINE_SWEEP_START_IDX_OFF + V1M2_RUNTIME_SHIFT,
1849
+ engineLifetimeLiquidationsOff: V1M_ENGINE_LIFETIME_LIQUIDATIONS_OFF + V1M2_RUNTIME_SHIFT,
1850
+ engineLifetimeForceClosesOff: V1M_ENGINE_LIFETIME_FORCE_CLOSES_OFF + V1M2_RUNTIME_SHIFT,
1851
+ engineNetLpPosOff: V1M_ENGINE_NET_LP_POS_OFF + V1M2_RUNTIME_SHIFT,
1852
+ engineLpSumAbsOff: V1M_ENGINE_LP_SUM_ABS_OFF + V1M2_RUNTIME_SHIFT,
1853
+ engineLpMaxAbsOff: V1M_ENGINE_LP_MAX_ABS_OFF + V1M2_RUNTIME_SHIFT,
1854
+ engineLpMaxAbsSweepOff: V1M_ENGINE_LP_MAX_ABS_SWEEP_OFF + V1M2_RUNTIME_SHIFT,
1855
+ engineEmergencyOiModeOff: V1M_ENGINE_EMERGENCY_OI_MODE_OFF + V1M2_RUNTIME_SHIFT,
1856
+ engineEmergencyStartSlotOff: V1M_ENGINE_EMERGENCY_START_SLOT_OFF + V1M2_RUNTIME_SHIFT,
1857
+ engineLastBreakerSlotOff: V1M_ENGINE_LAST_BREAKER_SLOT_OFF + V1M2_RUNTIME_SHIFT,
1858
+ engineBitmapOff: V1M2_ENGINE_BITMAP_OFF,
1859
+ postBitmap: 18,
1860
+ acctOwnerOff: ACCT_OWNER_OFF,
1861
+ hasInsuranceIsolation: true,
1862
+ engineInsuranceIsolatedOff: 48,
1863
+ engineInsuranceIsolationBpsOff: 64
1864
+ };
1865
+ }
1788
1866
  function buildLayoutVADL(maxAccounts) {
1789
1867
  const engineOff = V_ADL_ENGINE_OFF;
1790
1868
  const bitmapOff = V_ADL_ENGINE_BITMAP_OFF;
@@ -1880,7 +1958,15 @@ function buildLayoutVADL(maxAccounts) {
1880
1958
  }
1881
1959
  function detectSlabLayout(dataLen, data) {
1882
1960
  const vadln = V_ADL_SIZES.get(dataLen);
1883
- if (vadln !== void 0) return buildLayoutVADL(vadln);
1961
+ if (vadln !== void 0) {
1962
+ if (data && data.length >= 752) {
1963
+ const maxAcctsV1M2 = readU64LE(data, V1M_ENGINE_OFF + V1M_ENGINE_PARAMS_OFF + 32);
1964
+ if (maxAcctsV1M2 === BigInt(vadln)) {
1965
+ return buildLayoutV1M2(vadln);
1966
+ }
1967
+ }
1968
+ return buildLayoutVADL(vadln);
1969
+ }
1884
1970
  const v1mn = V1M_SIZES.get(dataLen);
1885
1971
  if (v1mn !== void 0) return buildLayoutV1M(v1mn);
1886
1972
  const v0n = V0_SIZES.get(dataLen);
@@ -1961,7 +2047,7 @@ function computeEffectiveOiCapBps(config, currentSlot) {
1961
2047
  return result < target ? result : target;
1962
2048
  }
1963
2049
  function readNonce(data) {
1964
- const layout = detectSlabLayout(data.length);
2050
+ const layout = detectSlabLayout(data.length, data);
1965
2051
  if (!layout) {
1966
2052
  throw new Error(`readNonce: unrecognized slab data length ${data.length}`);
1967
2053
  }
@@ -1970,7 +2056,7 @@ function readNonce(data) {
1970
2056
  return readU64LE(data, roff);
1971
2057
  }
1972
2058
  function readLastThrUpdateSlot(data) {
1973
- const layout = detectSlabLayout(data.length);
2059
+ const layout = detectSlabLayout(data.length, data);
1974
2060
  if (!layout) {
1975
2061
  throw new Error(`readLastThrUpdateSlot: unrecognized slab data length ${data.length}`);
1976
2062
  }
@@ -1990,7 +2076,7 @@ function parseHeader(data) {
1990
2076
  const bump = readU8(data, 12);
1991
2077
  const flags = readU8(data, 13);
1992
2078
  const admin = new PublicKey3(data.subarray(16, 48));
1993
- const layout = detectSlabLayout(data.length);
2079
+ const layout = detectSlabLayout(data.length, data);
1994
2080
  const roff = layout ? layout.reservedOff : V0_RESERVED_OFF;
1995
2081
  const nonce = readU64LE(data, roff);
1996
2082
  const lastThrUpdateSlot = readU64LE(data, roff + 8);
@@ -2007,7 +2093,7 @@ function parseHeader(data) {
2007
2093
  };
2008
2094
  }
2009
2095
  function parseConfig(data, layoutHint) {
2010
- const layout = layoutHint !== void 0 ? layoutHint : detectSlabLayout(data.length);
2096
+ const layout = layoutHint !== void 0 ? layoutHint : detectSlabLayout(data.length, data);
2011
2097
  const configOff = layout ? layout.configOffset : V0_HEADER_LEN;
2012
2098
  const configLen = layout ? layout.configLen : V0_CONFIG_LEN;
2013
2099
  const minLen = configOff + Math.min(configLen, 120);
@@ -2159,7 +2245,7 @@ function parseConfig(data, layoutHint) {
2159
2245
  };
2160
2246
  }
2161
2247
  function parseParams(data, layoutHint) {
2162
- const layout = layoutHint !== void 0 ? layoutHint : detectSlabLayout(data.length);
2248
+ const layout = layoutHint !== void 0 ? layoutHint : detectSlabLayout(data.length, data);
2163
2249
  const engineOff = layout ? layout.engineOff : V0_ENGINE_OFF;
2164
2250
  const paramsOff = layout ? layout.engineParamsOff : V0_ENGINE_PARAMS_OFF;
2165
2251
  const paramsSize = layout ? layout.paramsSize : V0_PARAMS_SIZE;
@@ -2195,7 +2281,7 @@ function parseParams(data, layoutHint) {
2195
2281
  return result;
2196
2282
  }
2197
2283
  function parseEngine(data) {
2198
- const layout = detectSlabLayout(data.length);
2284
+ const layout = detectSlabLayout(data.length, data);
2199
2285
  if (!layout) {
2200
2286
  throw new Error(`Unrecognized slab data length: ${data.length}. Cannot determine layout version.`);
2201
2287
  }
@@ -2249,7 +2335,7 @@ function parseEngine(data) {
2249
2335
  };
2250
2336
  }
2251
2337
  function parseUsedIndices(data) {
2252
- const layout = detectSlabLayout(data.length);
2338
+ const layout = detectSlabLayout(data.length, data);
2253
2339
  if (!layout) throw new Error(`Unrecognized slab data length: ${data.length}`);
2254
2340
  const base = layout.engineOff + layout.engineBitmapOff;
2255
2341
  if (data.length < base + layout.bitmapWords * 8) {
@@ -2268,7 +2354,7 @@ function parseUsedIndices(data) {
2268
2354
  return used;
2269
2355
  }
2270
2356
  function isAccountUsed(data, idx) {
2271
- const layout = detectSlabLayout(data.length);
2357
+ const layout = detectSlabLayout(data.length, data);
2272
2358
  if (!layout) return false;
2273
2359
  if (!Number.isInteger(idx) || idx < 0 || idx >= layout.maxAccounts) return false;
2274
2360
  const base = layout.engineOff + layout.engineBitmapOff;
@@ -2285,7 +2371,7 @@ function maxAccountIndex(dataLen) {
2285
2371
  return Math.floor(accountsEnd / layout.accountSize);
2286
2372
  }
2287
2373
  function parseAccount(data, idx) {
2288
- const layout = detectSlabLayout(data.length);
2374
+ const layout = detectSlabLayout(data.length, data);
2289
2375
  if (!layout) throw new Error(`Unrecognized slab data length: ${data.length}`);
2290
2376
  const maxIdx = maxAccountIndex(data.length);
2291
2377
  if (!Number.isInteger(idx) || idx < 0 || idx >= maxIdx) {
@@ -4409,6 +4495,7 @@ export {
4409
4495
  SLAB_TIERS_V1D,
4410
4496
  SLAB_TIERS_V1D_LEGACY,
4411
4497
  SLAB_TIERS_V1M,
4498
+ SLAB_TIERS_V1M2,
4412
4499
  SLAB_TIERS_V2,
4413
4500
  SLAB_TIERS_V_ADL,
4414
4501
  SLAB_TIERS_V_ADL_DISCOVERY,