@percolatorct/sdk 1.0.0-beta.30 → 1.0.0-beta.32

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
- > **EXPERIMENTAL — NOT AUDITED.** `1.0.0-beta.25`. 742 tests passing. Do NOT use with real funds.
5
+ > **EXPERIMENTAL — NOT AUDITED.** `1.0.0-beta.32`. 755 tests passing. Do NOT use with real funds.
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)
@@ -17,7 +17,15 @@ export declare const ACCOUNTS_INIT_MARKET: readonly AccountSpec[];
17
17
  */
18
18
  export declare const ACCOUNTS_INIT_USER: readonly AccountSpec[];
19
19
  /**
20
- * InitLP: 5 accounts (clock/oracle removed in commit 410f947)
20
+ * InitLP: 6 accounts
21
+ * Program at percolator.rs:6607 calls expect_len(accounts, 6).
22
+ * The 6th account (accounts[5]) is the clock sysvar — used via Clock::from_account_info.
23
+ * [0] user signer, writable (LP owner; pays fee)
24
+ * [1] slab writable
25
+ * [2] userAta writable (collateral source for fee)
26
+ * [3] vault writable (collateral destination)
27
+ * [4] tokenProgram read-only
28
+ * [5] clock read-only (SYSVAR_CLOCK_PUBKEY)
21
29
  */
22
30
  export declare const ACCOUNTS_INIT_LP: readonly AccountSpec[];
23
31
  /**
@@ -62,7 +70,23 @@ export declare const ACCOUNTS_SET_RISK_THRESHOLD: readonly AccountSpec[];
62
70
  */
63
71
  export declare const ACCOUNTS_UPDATE_ADMIN: readonly AccountSpec[];
64
72
  /**
65
- * CloseSlab: 2 accounts
73
+ * AcceptAdmin: 2 accounts (tag 82)
74
+ * Second half of two-step admin transfer. The proposed new admin must sign to
75
+ * complete the transfer. Program at percolator.rs:7994 calls expect_len(accounts, 2).
76
+ * [0] pendingAdmin signer, writable (must match config.pending_admin)
77
+ * [1] slab writable
78
+ */
79
+ export declare const ACCOUNTS_ACCEPT_ADMIN: readonly AccountSpec[];
80
+ /**
81
+ * CloseSlab: 6 accounts
82
+ * Drains vault and recovers rent after market is fully resolved and all accounts closed.
83
+ * Program at percolator.rs:8033 calls expect_len(accounts, 6).
84
+ * [0] dest signer, writable (receives rent + drained vault tokens)
85
+ * [1] slab writable
86
+ * [2] vault writable (token account — drained)
87
+ * [3] vaultAuthority read-only (PDA that signs the drain transfer)
88
+ * [4] destAta writable (dest's token ATA receiving drained tokens)
89
+ * [5] tokenProgram read-only
66
90
  */
67
91
  export declare const ACCOUNTS_CLOSE_SLAB: readonly AccountSpec[];
68
92
  /**
@@ -135,6 +135,46 @@ export declare const IX_TAG: {
135
135
  * Note: indexFeedId is the Pyth Pull feed ID (32 bytes hex), NOT an oracle pubkey.
136
136
  * The program validates PriceUpdateV2 accounts against this feed ID at runtime.
137
137
  */
138
+ /**
139
+ * Optional 66-byte extended tail for InitMarket (S-4).
140
+ *
141
+ * When present and any field is non-zero the encoder appends a 66-byte block
142
+ * in the exact order that the program reads it (percolator.rs:1516-1545):
143
+ * insurance_withdraw_max_bps u16 (2 bytes)
144
+ * insurance_withdraw_cooldown_slots u64 (8 bytes)
145
+ * permissionless_resolve_stale_slots u64 (8 bytes)
146
+ * funding_horizon_slots u64 (8 bytes)
147
+ * funding_k_bps u64 (8 bytes)
148
+ * funding_max_premium_bps i64 (8 bytes)
149
+ * funding_max_bps_per_slot i64 (8 bytes)
150
+ * mark_min_fee u64 (8 bytes)
151
+ * force_close_delay_slots u64 (8 bytes)
152
+ * total = 2 + 8*8 = 66 bytes
153
+ *
154
+ * When absent (or all fields are zero) the encoder omits the tail and the
155
+ * program treats all extended fields as their default zero values. This
156
+ * preserves full backward compatibility with existing 344-byte payloads.
157
+ */
158
+ export interface InitMarketExtendedTail {
159
+ /** Maximum percentage of insurance fund withdrawable per cooldown window (0–10 000 bps). */
160
+ insuranceWithdrawMaxBps: number;
161
+ /** Slots that must elapse between insurance withdrawals. Required when insuranceWithdrawMaxBps > 0. */
162
+ insuranceWithdrawCooldownSlots: bigint | string;
163
+ /** Slots after which an unresolved market may be permissionlessly resolved. */
164
+ permissionlessResolveStaleSlots: bigint | string;
165
+ /** Funding rate horizon in slots (custom_funding_k denominator). */
166
+ fundingHorizonSlots: bigint | string;
167
+ /** Funding rate K parameter in bps (0 = disabled). */
168
+ fundingKBps: bigint | string;
169
+ /** Maximum funding premium in bps (i64 — may be negative to flip direction). */
170
+ fundingMaxPremiumBps: bigint | string;
171
+ /** Maximum funding rate change per slot in bps (i64). */
172
+ fundingMaxBpsPerSlot: bigint | string;
173
+ /** Minimum fee charged per mark-price update (u64, in collateral base units). */
174
+ markMinFee: bigint | string;
175
+ /** Slots to delay forced close after trigger condition is met (0 = immediate). */
176
+ forceCloseDelaySlots: bigint | string;
177
+ }
138
178
  export interface InitMarketArgs {
139
179
  admin: PublicKey | string;
140
180
  collateralMint: PublicKey | string;
@@ -171,7 +211,49 @@ export interface InitMarketArgs {
171
211
  minInitialDeposit: bigint | string;
172
212
  minNonzeroMmReq: bigint | string;
173
213
  minNonzeroImReq: bigint | string;
214
+ /**
215
+ * Optional 66-byte extended tail (S-4).
216
+ * When present and any field is non-zero, appended after the 344-byte base payload.
217
+ * When absent (or all zeros), the base 344-byte payload is sent and the program
218
+ * uses default zero values for all extended fields.
219
+ * @see InitMarketExtendedTail
220
+ */
221
+ extendedTail?: InitMarketExtendedTail;
174
222
  }
223
+ /**
224
+ * Encode InitMarket instruction data.
225
+ *
226
+ * Produces either a 344-byte base payload (no extended tail) or a 410-byte
227
+ * payload (344 + 66 extended tail) depending on whether `args.extendedTail`
228
+ * is provided and contains at least one non-zero field.
229
+ *
230
+ * The program (percolator.rs:1527-1545) treats an empty `rest` as all-zero
231
+ * defaults, so the 344-byte form is fully backward-compatible.
232
+ *
233
+ * @param args InitMarket arguments
234
+ * @returns Encoded instruction bytes
235
+ *
236
+ * @example
237
+ * ```ts
238
+ * const ix = encodeInitMarket({
239
+ * admin: adminPk,
240
+ * collateralMint: mintPk,
241
+ * indexFeedId: "0000...0000",
242
+ * // ... required fields ...
243
+ * extendedTail: {
244
+ * insuranceWithdrawMaxBps: 500,
245
+ * insuranceWithdrawCooldownSlots: 216000n,
246
+ * permissionlessResolveStaleSlots: 0n,
247
+ * fundingHorizonSlots: 0n,
248
+ * fundingKBps: 0n,
249
+ * fundingMaxPremiumBps: 0n,
250
+ * fundingMaxBpsPerSlot: 0n,
251
+ * markMinFee: 0n,
252
+ * forceCloseDelaySlots: 0n,
253
+ * },
254
+ * });
255
+ * ```
256
+ */
175
257
  export declare function encodeInitMarket(args: InitMarketArgs): Uint8Array;
176
258
  /**
177
259
  * InitUser instruction data (9 bytes)
package/dist/index.js CHANGED
@@ -258,11 +258,29 @@ function encodeFeedId(feedId) {
258
258
  }
259
259
  return bytes;
260
260
  }
261
- var INIT_MARKET_DATA_LEN = 344;
261
+ var INIT_MARKET_BASE_LEN = 344;
262
+ var INIT_MARKET_EXTENDED_TAIL_LEN = 66;
263
+ function extendedTailHasNonZero(t) {
264
+ const toBigInt = (v) => typeof v === "string" ? BigInt(v) : v;
265
+ return t.insuranceWithdrawMaxBps !== 0 || toBigInt(t.insuranceWithdrawCooldownSlots) !== 0n || toBigInt(t.permissionlessResolveStaleSlots) !== 0n || toBigInt(t.fundingHorizonSlots) !== 0n || toBigInt(t.fundingKBps) !== 0n || toBigInt(t.fundingMaxPremiumBps) !== 0n || toBigInt(t.fundingMaxBpsPerSlot) !== 0n || toBigInt(t.markMinFee) !== 0n || toBigInt(t.forceCloseDelaySlots) !== 0n;
266
+ }
267
+ function encodeExtendedTail(t) {
268
+ return concatBytes(
269
+ encU16(t.insuranceWithdrawMaxBps),
270
+ encU64(t.insuranceWithdrawCooldownSlots),
271
+ encU64(t.permissionlessResolveStaleSlots),
272
+ encU64(t.fundingHorizonSlots),
273
+ encU64(t.fundingKBps),
274
+ encI64(t.fundingMaxPremiumBps),
275
+ encI64(t.fundingMaxBpsPerSlot),
276
+ encU64(t.markMinFee),
277
+ encU64(t.forceCloseDelaySlots)
278
+ );
279
+ }
262
280
  function encodeInitMarket(args) {
263
281
  const hMin = args.hMin ?? args.warmupPeriodSlots ?? 0n;
264
282
  const hMax = args.hMax ?? args.warmupPeriodSlots ?? 0n;
265
- const data = concatBytes(
283
+ const base = concatBytes(
266
284
  encU8(IX_TAG.InitMarket),
267
285
  encPubkey(args.admin),
268
286
  encPubkey(args.collateralMint),
@@ -300,12 +318,21 @@ function encodeInitMarket(args) {
300
318
  encU128(args.minNonzeroMmReq),
301
319
  encU128(args.minNonzeroImReq)
302
320
  );
303
- if (data.length !== INIT_MARKET_DATA_LEN) {
321
+ if (base.length !== INIT_MARKET_BASE_LEN) {
304
322
  throw new Error(
305
- `encodeInitMarket: expected ${INIT_MARKET_DATA_LEN} bytes, got ${data.length}`
323
+ `encodeInitMarket: base payload expected ${INIT_MARKET_BASE_LEN} bytes, got ${base.length}`
306
324
  );
307
325
  }
308
- return data;
326
+ if (args.extendedTail && extendedTailHasNonZero(args.extendedTail)) {
327
+ const tail = encodeExtendedTail(args.extendedTail);
328
+ if (tail.length !== INIT_MARKET_EXTENDED_TAIL_LEN) {
329
+ throw new Error(
330
+ `encodeInitMarket: extended tail expected ${INIT_MARKET_EXTENDED_TAIL_LEN} bytes, got ${tail.length}`
331
+ );
332
+ }
333
+ return concatBytes(base, tail);
334
+ }
335
+ return base;
309
336
  }
310
337
  function encodeInitUser(args) {
311
338
  return concatBytes(encU8(IX_TAG.InitUser), encU64(args.feePayment));
@@ -800,7 +827,8 @@ var ACCOUNTS_INIT_LP = [
800
827
  { name: "slab", signer: false, writable: true },
801
828
  { name: "userAta", signer: false, writable: true },
802
829
  { name: "vault", signer: false, writable: true },
803
- { name: "tokenProgram", signer: false, writable: false }
830
+ { name: "tokenProgram", signer: false, writable: false },
831
+ { name: "clock", signer: false, writable: false }
804
832
  ];
805
833
  var ACCOUNTS_DEPOSIT_COLLATERAL = [
806
834
  { name: "user", signer: true, writable: true },
@@ -874,10 +902,18 @@ var ACCOUNTS_UPDATE_ADMIN = [
874
902
  { name: "admin", signer: true, writable: true },
875
903
  { name: "slab", signer: false, writable: true }
876
904
  ];
877
- var ACCOUNTS_CLOSE_SLAB = [
878
- { name: "admin", signer: true, writable: true },
905
+ var ACCOUNTS_ACCEPT_ADMIN = [
906
+ { name: "pendingAdmin", signer: true, writable: true },
879
907
  { name: "slab", signer: false, writable: true }
880
908
  ];
909
+ var ACCOUNTS_CLOSE_SLAB = [
910
+ { name: "dest", signer: true, writable: true },
911
+ { name: "slab", signer: false, writable: true },
912
+ { name: "vault", signer: false, writable: true },
913
+ { name: "vaultAuthority", signer: false, writable: false },
914
+ { name: "destAta", signer: false, writable: true },
915
+ { name: "tokenProgram", signer: false, writable: false }
916
+ ];
881
917
  var ACCOUNTS_UPDATE_CONFIG = [
882
918
  { name: "admin", signer: true, writable: true },
883
919
  { name: "slab", signer: false, writable: true }
@@ -1568,6 +1604,16 @@ function deriveMintAuthority(programId = NFT_PROGRAM_ID) {
1568
1604
  );
1569
1605
  }
1570
1606
  var POSITION_NFT_STATE_LEN = 208;
1607
+ function readI128FromView(view, offset) {
1608
+ const lo = view.getBigUint64(offset, true);
1609
+ const hi = view.getBigUint64(offset + 8, true);
1610
+ const unsigned = hi << 64n | lo;
1611
+ const SIGN_BIT = 1n << 127n;
1612
+ if (unsigned >= SIGN_BIT) {
1613
+ return unsigned - (1n << 128n);
1614
+ }
1615
+ return unsigned;
1616
+ }
1571
1617
  function parsePositionNftAccount(data) {
1572
1618
  if (data.length < POSITION_NFT_STATE_LEN) {
1573
1619
  throw new Error(
@@ -1584,8 +1630,8 @@ function parsePositionNftAccount(data) {
1584
1630
  entryPriceE6: view.getBigUint64(88, true),
1585
1631
  positionSize: view.getBigUint64(96, true),
1586
1632
  isLong: data[104] === 1,
1587
- positionBasisQ: view.getBigInt64(112, true) | view.getBigInt64(120, true) << 64n,
1588
- lastFundingIndexE18: view.getBigInt64(128, true) | view.getBigInt64(136, true) << 64n,
1633
+ positionBasisQ: readI128FromView(view, 112),
1634
+ lastFundingIndexE18: readI128FromView(view, 128),
1589
1635
  mintedAt: view.getBigInt64(144, true),
1590
1636
  accountId: view.getBigUint64(152, true)
1591
1637
  };
@@ -1899,11 +1945,11 @@ var V12_15_ENGINE_PNL_POS_TOT_OFF = 368;
1899
1945
  var V12_15_ENGINE_PNL_MATURED_POS_TOT_OFF = 384;
1900
1946
  var V12_15_ENGINE_BITMAP_OFF = 862;
1901
1947
  var V12_15_SIZES = /* @__PURE__ */ new Map();
1902
- var V12_17_ENGINE_OFF = 512;
1948
+ var V12_17_ENGINE_OFF = 592;
1903
1949
  var V12_17_ACCOUNT_SIZE = 368;
1904
1950
  var V12_17_ENGINE_BITMAP_OFF = 752;
1905
1951
  var V12_17_RISK_BUF_LEN = 160;
1906
- var V12_17_ENGINE_OFF_SBF = 504;
1952
+ var V12_17_ENGINE_OFF_SBF = 584;
1907
1953
  var V12_17_ACCOUNT_SIZE_SBF = 352;
1908
1954
  var V12_17_ENGINE_BITMAP_OFF_SBF = 712;
1909
1955
  var V12_17_ACCT_CAPITAL_OFF = 0;
@@ -2862,8 +2908,12 @@ function buildLayoutV12_17(maxAccounts, dataLen) {
2862
2908
  // 72
2863
2909
  configOffset: V0_HEADER_LEN,
2864
2910
  // 72
2865
- configLen: 432,
2866
- // upstream 400 + dex_pool 32
2911
+ // configLen = 512 (SBF-aligned MarketConfig size after Phase A/B/E).
2912
+ // Verified field-by-field against percolator-prog/src/percolator.rs MarketConfig struct.
2913
+ // Missing 80 bytes from prior value 432: max_pnl_cap, last_audit_pause_slot,
2914
+ // oi_cap_multiplier_bps, dispute_window_slots, dispute_bond_amount,
2915
+ // lp_collateral_enabled, lp_collateral_ltv_bps, _new_fields_pad, pending_admin.
2916
+ configLen: 512,
2867
2917
  reservedOff: V1_RESERVED_OFF,
2868
2918
  // 80
2869
2919
  engineOff,
@@ -6400,6 +6450,7 @@ async function resolvePrice(mint, signal, options) {
6400
6450
  };
6401
6451
  }
6402
6452
  export {
6453
+ ACCOUNTS_ACCEPT_ADMIN,
6403
6454
  ACCOUNTS_ADVANCE_ORACLE_PHASE,
6404
6455
  ACCOUNTS_AUDIT_CRANK,
6405
6456
  ACCOUNTS_BURN_POSITION_NFT,