@percolatorct/sdk 0.5.0 → 1.0.0-beta.1
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 +224 -23
- package/dist/abi/accounts.d.ts +7 -0
- package/dist/abi/errors.d.ts +11 -0
- package/dist/abi/instructions.d.ts +68 -0
- package/dist/index.js +1407 -188
- package/dist/index.js.map +1 -1
- package/dist/math/trading.d.ts +116 -1
- package/dist/math/warmup.d.ts +50 -0
- package/dist/runtime/index.d.ts +1 -0
- package/dist/runtime/lighthouse.d.ts +170 -0
- package/dist/solana/discovery.d.ts +253 -24
- package/dist/solana/index.d.ts +2 -0
- package/dist/solana/oracle.d.ts +10 -2
- package/dist/solana/rpc-pool.d.ts +347 -0
- package/dist/solana/slab.d.ts +37 -5
- package/dist/solana/static-markets.d.ts +86 -0
- package/dist/validation.d.ts +26 -1
- package/package.json +5 -4
package/dist/index.js
CHANGED
|
@@ -93,6 +93,7 @@ function concatBytes(...arrays) {
|
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
// src/abi/instructions.ts
|
|
96
|
+
var MAX_ORACLE_PRICE = 1000000000000n;
|
|
96
97
|
var IX_TAG = {
|
|
97
98
|
InitMarket: 0,
|
|
98
99
|
InitUser: 1,
|
|
@@ -199,8 +200,17 @@ var IX_TAG = {
|
|
|
199
200
|
/** PERC-8111: Set per-wallet position cap (admin only, cap_e6=0 disables). */
|
|
200
201
|
SetWalletCap: 70,
|
|
201
202
|
/** PERC-8110: Set OI imbalance hard-block threshold (admin only). */
|
|
202
|
-
SetOiImbalanceHardBlock: 71
|
|
203
|
+
SetOiImbalanceHardBlock: 71,
|
|
204
|
+
/** PERC-8270: Rescue orphan vault — recover tokens from a closed market's vault (admin). */
|
|
205
|
+
RescueOrphanVault: 72,
|
|
206
|
+
/** PERC-8270: Close orphan slab — reclaim rent from a slab whose market closed unexpectedly (admin). */
|
|
207
|
+
CloseOrphanSlab: 73,
|
|
208
|
+
/** PERC-SetDexPool: Pin admin-approved DEX pool address for a HYPERP market (admin). */
|
|
209
|
+
SetDexPool: 74,
|
|
210
|
+
/** CPI to the matcher program to initialize a matcher context account for an LP slot. Admin-only. */
|
|
211
|
+
InitMatcherCtx: 75
|
|
203
212
|
};
|
|
213
|
+
Object.freeze(IX_TAG);
|
|
204
214
|
var HEX_RE = /^[0-9a-fA-F]{64}$/;
|
|
205
215
|
function encodeFeedId(feedId) {
|
|
206
216
|
const hex = feedId.startsWith("0x") ? feedId.slice(2) : feedId;
|
|
@@ -211,7 +221,13 @@ function encodeFeedId(feedId) {
|
|
|
211
221
|
}
|
|
212
222
|
const bytes = new Uint8Array(32);
|
|
213
223
|
for (let i = 0; i < 64; i += 2) {
|
|
214
|
-
|
|
224
|
+
const byte = parseInt(hex.substring(i, i + 2), 16);
|
|
225
|
+
if (Number.isNaN(byte)) {
|
|
226
|
+
throw new Error(
|
|
227
|
+
`Failed to parse hex byte at position ${i}: "${hex.substring(i, i + 2)}"`
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
bytes[i / 2] = byte;
|
|
215
231
|
}
|
|
216
232
|
return bytes;
|
|
217
233
|
}
|
|
@@ -362,9 +378,16 @@ function encodeSetOracleAuthority(args) {
|
|
|
362
378
|
);
|
|
363
379
|
}
|
|
364
380
|
function encodePushOraclePrice(args) {
|
|
381
|
+
const price = typeof args.priceE6 === "string" ? BigInt(args.priceE6) : args.priceE6;
|
|
382
|
+
if (price === 0n) {
|
|
383
|
+
throw new Error("encodePushOraclePrice: price cannot be zero (division by zero in engine)");
|
|
384
|
+
}
|
|
385
|
+
if (price > MAX_ORACLE_PRICE) {
|
|
386
|
+
throw new Error(`encodePushOraclePrice: price exceeds maximum (${MAX_ORACLE_PRICE}), got ${price}`);
|
|
387
|
+
}
|
|
365
388
|
return concatBytes(
|
|
366
389
|
encU8(IX_TAG.PushOraclePrice),
|
|
367
|
-
encU64(
|
|
390
|
+
encU64(price),
|
|
368
391
|
encI64(args.timestamp)
|
|
369
392
|
);
|
|
370
393
|
}
|
|
@@ -441,17 +464,15 @@ function encodeSetPythOracle(args) {
|
|
|
441
464
|
}
|
|
442
465
|
var PYTH_RECEIVER_PROGRAM_ID = "rec5EKMGg6MxZYaMdyBfgwp4d5rB9T1VQH5pJv5LtFJ";
|
|
443
466
|
async function derivePythPriceUpdateAccount(feedId, shardId = 0) {
|
|
444
|
-
const { PublicKey:
|
|
467
|
+
const { PublicKey: PublicKey15 } = await import("@solana/web3.js");
|
|
445
468
|
const shardBuf = new Uint8Array(2);
|
|
446
469
|
new DataView(shardBuf.buffer).setUint16(0, shardId, true);
|
|
447
|
-
const [pda] =
|
|
470
|
+
const [pda] = PublicKey15.findProgramAddressSync(
|
|
448
471
|
[shardBuf, feedId],
|
|
449
|
-
new
|
|
472
|
+
new PublicKey15(PYTH_RECEIVER_PROGRAM_ID)
|
|
450
473
|
);
|
|
451
474
|
return pda.toBase58();
|
|
452
475
|
}
|
|
453
|
-
IX_TAG["SetPythOracle"] = 32;
|
|
454
|
-
IX_TAG["UpdateMarkPrice"] = 33;
|
|
455
476
|
function encodeUpdateMarkPrice() {
|
|
456
477
|
return new Uint8Array([33]);
|
|
457
478
|
}
|
|
@@ -462,7 +483,7 @@ function computeEmaMarkPrice(markPrevE6, oracleE6, dtSlots, alphaE6 = MARK_PRICE
|
|
|
462
483
|
if (markPrevE6 === 0n || dtSlots === 0n) return oracleE6;
|
|
463
484
|
let oracleClamped = oracleE6;
|
|
464
485
|
if (capE2bps > 0n) {
|
|
465
|
-
const maxDelta = markPrevE6 * capE2bps * dtSlots
|
|
486
|
+
const maxDelta = markPrevE6 * capE2bps / 1000000n * dtSlots;
|
|
466
487
|
const lo = markPrevE6 > maxDelta ? markPrevE6 - maxDelta : 0n;
|
|
467
488
|
const hi = markPrevE6 + maxDelta;
|
|
468
489
|
if (oracleClamped < lo) oracleClamped = lo;
|
|
@@ -472,7 +493,6 @@ function computeEmaMarkPrice(markPrevE6, oracleE6, dtSlots, alphaE6 = MARK_PRICE
|
|
|
472
493
|
const oneMinusAlpha = 1000000n - effectiveAlpha;
|
|
473
494
|
return (oracleClamped * effectiveAlpha + markPrevE6 * oneMinusAlpha) / 1000000n;
|
|
474
495
|
}
|
|
475
|
-
IX_TAG["UpdateHyperpMark"] = 34;
|
|
476
496
|
function encodeUpdateHyperpMark() {
|
|
477
497
|
return new Uint8Array([34]);
|
|
478
498
|
}
|
|
@@ -616,6 +636,22 @@ function encodeTransferOwnershipCpi(args) {
|
|
|
616
636
|
function encodeSetWalletCap(args) {
|
|
617
637
|
return concatBytes(encU8(IX_TAG.SetWalletCap), encU64(args.capE6));
|
|
618
638
|
}
|
|
639
|
+
function encodeInitMatcherCtx(args) {
|
|
640
|
+
return concatBytes(
|
|
641
|
+
encU8(IX_TAG.InitMatcherCtx),
|
|
642
|
+
encU16(args.lpIdx),
|
|
643
|
+
encU8(args.kind),
|
|
644
|
+
encU32(args.tradingFeeBps),
|
|
645
|
+
encU32(args.baseSpreadBps),
|
|
646
|
+
encU32(args.maxTotalBps),
|
|
647
|
+
encU32(args.impactKBps),
|
|
648
|
+
encU128(args.liquidityNotionalE6),
|
|
649
|
+
encU128(args.maxFillAbs),
|
|
650
|
+
encU128(args.maxInventoryAbs),
|
|
651
|
+
encU16(args.feeToInsuranceBps),
|
|
652
|
+
encU16(args.skewSpreadMultBps)
|
|
653
|
+
);
|
|
654
|
+
}
|
|
619
655
|
|
|
620
656
|
// src/abi/accounts.ts
|
|
621
657
|
import {
|
|
@@ -942,6 +978,13 @@ var ACCOUNTS_SET_WALLET_CAP = [
|
|
|
942
978
|
{ name: "admin", signer: true, writable: false },
|
|
943
979
|
{ name: "slab", signer: false, writable: true }
|
|
944
980
|
];
|
|
981
|
+
var ACCOUNTS_INIT_MATCHER_CTX = [
|
|
982
|
+
{ name: "admin", signer: true, writable: false },
|
|
983
|
+
{ name: "slab", signer: false, writable: false },
|
|
984
|
+
{ name: "matcherCtx", signer: false, writable: true },
|
|
985
|
+
{ name: "matcherProg", signer: false, writable: false },
|
|
986
|
+
{ name: "lpPda", signer: false, writable: false }
|
|
987
|
+
];
|
|
945
988
|
var WELL_KNOWN = {
|
|
946
989
|
tokenProgram: TOKEN_PROGRAM_ID,
|
|
947
990
|
clock: SYSVAR_CLOCK_PUBKEY,
|
|
@@ -1226,11 +1269,24 @@ function getErrorName(code) {
|
|
|
1226
1269
|
function getErrorHint(code) {
|
|
1227
1270
|
return PERCOLATOR_ERRORS[code]?.hint;
|
|
1228
1271
|
}
|
|
1272
|
+
var LIGHTHOUSE_PROGRAM_ID_STR = "L2TExMFKdjpN9kozasaurPirfHy9P8sbXoAN1qA3S95";
|
|
1273
|
+
var ANCHOR_ERROR_RANGE_START = 6e3;
|
|
1274
|
+
var ANCHOR_ERROR_RANGE_END = 8191;
|
|
1275
|
+
var ANCHOR_ERROR_NAMES = {
|
|
1276
|
+
6032: "ConstraintMut",
|
|
1277
|
+
6036: "ConstraintOwner",
|
|
1278
|
+
6038: "ConstraintSeeds",
|
|
1279
|
+
6400: "ConstraintAddress"
|
|
1280
|
+
};
|
|
1281
|
+
function isAnchorErrorCode(code) {
|
|
1282
|
+
return code >= ANCHOR_ERROR_RANGE_START && code <= ANCHOR_ERROR_RANGE_END;
|
|
1283
|
+
}
|
|
1229
1284
|
var CUSTOM_ERROR_HEX_MAX_LEN = 8;
|
|
1230
1285
|
function parseErrorFromLogs(logs) {
|
|
1231
1286
|
if (!Array.isArray(logs)) {
|
|
1232
1287
|
return null;
|
|
1233
1288
|
}
|
|
1289
|
+
let insideLighthouse = false;
|
|
1234
1290
|
const re = new RegExp(
|
|
1235
1291
|
`custom program error: 0x([0-9a-fA-F]{1,${CUSTOM_ERROR_HEX_MAX_LEN}})(?![0-9a-fA-F])`,
|
|
1236
1292
|
"i"
|
|
@@ -1239,17 +1295,32 @@ function parseErrorFromLogs(logs) {
|
|
|
1239
1295
|
if (typeof log !== "string") {
|
|
1240
1296
|
continue;
|
|
1241
1297
|
}
|
|
1298
|
+
if (log.includes(`Program ${LIGHTHOUSE_PROGRAM_ID_STR} invoke`)) {
|
|
1299
|
+
insideLighthouse = true;
|
|
1300
|
+
} else if (log.includes(`Program ${LIGHTHOUSE_PROGRAM_ID_STR} success`)) {
|
|
1301
|
+
insideLighthouse = false;
|
|
1302
|
+
}
|
|
1242
1303
|
const match = log.match(re);
|
|
1243
1304
|
if (match) {
|
|
1244
1305
|
const code = parseInt(match[1], 16);
|
|
1245
1306
|
if (!Number.isFinite(code) || code < 0 || code > 4294967295) {
|
|
1246
1307
|
continue;
|
|
1247
1308
|
}
|
|
1309
|
+
if (isAnchorErrorCode(code) || insideLighthouse) {
|
|
1310
|
+
const anchorName = ANCHOR_ERROR_NAMES[code] ?? `AnchorError(0x${code.toString(16)})`;
|
|
1311
|
+
return {
|
|
1312
|
+
code,
|
|
1313
|
+
name: `Lighthouse:${anchorName}`,
|
|
1314
|
+
hint: "This error comes from the Lighthouse/Blowfish wallet guard, not from Percolator. The transaction itself is valid. Disable transaction simulation in your wallet settings, or use a wallet without Blowfish protection (e.g., Backpack, Solflare).",
|
|
1315
|
+
source: "lighthouse"
|
|
1316
|
+
};
|
|
1317
|
+
}
|
|
1248
1318
|
const info = decodeError(code);
|
|
1249
1319
|
return {
|
|
1250
1320
|
code,
|
|
1251
1321
|
name: info?.name ?? `Unknown(${code})`,
|
|
1252
|
-
hint: info?.hint
|
|
1322
|
+
hint: info?.hint,
|
|
1323
|
+
source: info ? "percolator" : "unknown"
|
|
1253
1324
|
};
|
|
1254
1325
|
}
|
|
1255
1326
|
}
|
|
@@ -1416,6 +1487,8 @@ var V2_ENGINE_LP_MAX_ABS_OFF = 536;
|
|
|
1416
1487
|
var V2_ENGINE_LP_MAX_ABS_SWEEP_OFF = 552;
|
|
1417
1488
|
var V_ADL_ENGINE_OFF = 624;
|
|
1418
1489
|
var V_ADL_CONFIG_LEN = 520;
|
|
1490
|
+
var V_SETDEXPOOL_CONFIG_LEN = 544;
|
|
1491
|
+
var V_SETDEXPOOL_ENGINE_OFF = 648;
|
|
1419
1492
|
var V_ADL_ACCOUNT_SIZE = 312;
|
|
1420
1493
|
var V_ADL_ENGINE_PARAMS_OFF = 96;
|
|
1421
1494
|
var V_ADL_PARAMS_SIZE = 336;
|
|
@@ -1446,7 +1519,7 @@ var V_ADL_ENGINE_LP_MAX_ABS_SWEEP_OFF = 952;
|
|
|
1446
1519
|
var V_ADL_ENGINE_EMERGENCY_OI_MODE_OFF = 968;
|
|
1447
1520
|
var V_ADL_ENGINE_EMERGENCY_START_SLOT_OFF = 976;
|
|
1448
1521
|
var V_ADL_ENGINE_LAST_BREAKER_SLOT_OFF = 984;
|
|
1449
|
-
var V_ADL_ENGINE_BITMAP_OFF =
|
|
1522
|
+
var V_ADL_ENGINE_BITMAP_OFF = 1008;
|
|
1450
1523
|
var V_ADL_ACCT_WARMUP_STARTED_OFF = 64;
|
|
1451
1524
|
var V_ADL_ACCT_WARMUP_SLOPE_OFF = 72;
|
|
1452
1525
|
var V_ADL_ACCT_POSITION_SIZE_OFF = 88;
|
|
@@ -1457,12 +1530,46 @@ var V_ADL_ACCT_MATCHER_CONTEXT_OFF = 160;
|
|
|
1457
1530
|
var V_ADL_ACCT_OWNER_OFF = 192;
|
|
1458
1531
|
var V_ADL_ACCT_FEE_CREDITS_OFF = 224;
|
|
1459
1532
|
var V_ADL_ACCT_LAST_FEE_SLOT_OFF = 240;
|
|
1533
|
+
var V12_1_ENGINE_OFF = 648;
|
|
1534
|
+
var V12_1_ACCOUNT_SIZE = 320;
|
|
1535
|
+
var V12_1_ENGINE_BITMAP_OFF = 1016;
|
|
1536
|
+
var V12_1_ENGINE_PARAMS_OFF = 96;
|
|
1537
|
+
var V12_1_PARAMS_SIZE = 352;
|
|
1538
|
+
var V12_1_ENGINE_CURRENT_SLOT_OFF = 448;
|
|
1539
|
+
var V12_1_ENGINE_FUNDING_RATE_BPS_OFF = 456;
|
|
1540
|
+
var V12_1_ENGINE_LAST_CRANK_SLOT_OFF = 464;
|
|
1541
|
+
var V12_1_ENGINE_MAX_CRANK_STALENESS_OFF = 472;
|
|
1542
|
+
var V12_1_ENGINE_C_TOT_OFF = 480;
|
|
1543
|
+
var V12_1_ENGINE_PNL_POS_TOT_OFF = 496;
|
|
1544
|
+
var V12_1_ENGINE_LIQ_CURSOR_OFF = 528;
|
|
1545
|
+
var V12_1_ENGINE_GC_CURSOR_OFF = 530;
|
|
1546
|
+
var V12_1_ENGINE_LAST_SWEEP_START_OFF = 536;
|
|
1547
|
+
var V12_1_ENGINE_LAST_SWEEP_COMPLETE_OFF = 544;
|
|
1548
|
+
var V12_1_ENGINE_CRANK_CURSOR_OFF = 552;
|
|
1549
|
+
var V12_1_ENGINE_SWEEP_START_IDX_OFF = 554;
|
|
1550
|
+
var V12_1_ENGINE_LIFETIME_LIQUIDATIONS_OFF = 560;
|
|
1551
|
+
var V12_1_ENGINE_TOTAL_OI_OFF = 816;
|
|
1552
|
+
var V12_1_ENGINE_LONG_OI_OFF = 832;
|
|
1553
|
+
var V12_1_ENGINE_SHORT_OI_OFF = 848;
|
|
1554
|
+
var V12_1_ENGINE_NET_LP_POS_OFF = 864;
|
|
1555
|
+
var V12_1_ENGINE_LP_SUM_ABS_OFF = 880;
|
|
1556
|
+
var V12_1_ENGINE_LP_MAX_ABS_OFF = 896;
|
|
1557
|
+
var V12_1_ENGINE_LP_MAX_ABS_SWEEP_OFF = 912;
|
|
1558
|
+
var V12_1_ENGINE_MARK_PRICE_OFF = 928;
|
|
1559
|
+
var V12_1_ENGINE_FUNDING_INDEX_OFF = 936;
|
|
1560
|
+
var V12_1_ENGINE_LAST_FUNDING_SLOT_OFF = 944;
|
|
1561
|
+
var V12_1_ENGINE_EMERGENCY_OI_MODE_OFF = 968;
|
|
1562
|
+
var V12_1_ENGINE_EMERGENCY_START_SLOT_OFF = 976;
|
|
1563
|
+
var V12_1_ENGINE_LAST_BREAKER_SLOT_OFF = 984;
|
|
1564
|
+
var V12_1_ENGINE_LIFETIME_FORCE_CLOSES_OFF = 1008;
|
|
1565
|
+
var V12_1_ACCT_OWNER_OFF = 208;
|
|
1460
1566
|
var V1M_ENGINE_OFF = 640;
|
|
1461
1567
|
var V1M_CONFIG_LEN = 536;
|
|
1462
1568
|
var V1M_ACCOUNT_SIZE = 248;
|
|
1463
1569
|
var V1M2_ENGINE_OFF = 616;
|
|
1464
1570
|
var V1M2_CONFIG_LEN = 512;
|
|
1465
1571
|
var V1M_ENGINE_PARAMS_OFF = 72;
|
|
1572
|
+
var V1M2_ENGINE_PARAMS_OFF = 96;
|
|
1466
1573
|
var V1M_PARAMS_SIZE = 336;
|
|
1467
1574
|
var V1M_ENGINE_CURRENT_SLOT_OFF = 408;
|
|
1468
1575
|
var V1M_ENGINE_FUNDING_INDEX_OFF = 416;
|
|
@@ -1493,15 +1600,7 @@ var V1M_ENGINE_EMERGENCY_START_SLOT_OFF = 688;
|
|
|
1493
1600
|
var V1M_ENGINE_LAST_BREAKER_SLOT_OFF = 696;
|
|
1494
1601
|
var V1M_ENGINE_BITMAP_OFF = 720;
|
|
1495
1602
|
var V1M2_ACCOUNT_SIZE = 312;
|
|
1496
|
-
var V1M2_ENGINE_BITMAP_OFF =
|
|
1497
|
-
var V1M2_ENGINE_CURRENT_SLOT_OFF = 408;
|
|
1498
|
-
var V1M2_ENGINE_FUNDING_INDEX_OFF = 416;
|
|
1499
|
-
var V1M2_ENGINE_LAST_FUNDING_SLOT_OFF = 432;
|
|
1500
|
-
var V1M2_ENGINE_FUNDING_RATE_BPS_OFF = 440;
|
|
1501
|
-
var V1M2_ENGINE_MARK_PRICE_OFF = 480;
|
|
1502
|
-
var V1M2_ENGINE_LAST_CRANK_SLOT_OFF = 504;
|
|
1503
|
-
var V1M2_ENGINE_MAX_CRANK_STALENESS_OFF = 512;
|
|
1504
|
-
var V1M2_RUNTIME_SHIFT = 32;
|
|
1603
|
+
var V1M2_ENGINE_BITMAP_OFF = 1008;
|
|
1505
1604
|
var ENGINE_OFF = V1_ENGINE_OFF;
|
|
1506
1605
|
var ENGINE_MARK_PRICE_OFF = V1_ENGINE_MARK_PRICE_OFF;
|
|
1507
1606
|
function computeSlabSize(engineOff, bitmapOff, accountSize, maxAccounts, postBitmap = 18) {
|
|
@@ -1520,6 +1619,9 @@ var V1D_SIZES = /* @__PURE__ */ new Map();
|
|
|
1520
1619
|
var V2_SIZES = /* @__PURE__ */ new Map();
|
|
1521
1620
|
var V1M_SIZES = /* @__PURE__ */ new Map();
|
|
1522
1621
|
var V_ADL_SIZES = /* @__PURE__ */ new Map();
|
|
1622
|
+
var V1M2_SIZES = /* @__PURE__ */ new Map();
|
|
1623
|
+
var V_SETDEXPOOL_SIZES = /* @__PURE__ */ new Map();
|
|
1624
|
+
var V12_1_SIZES = /* @__PURE__ */ new Map();
|
|
1523
1625
|
var V1D_SIZES_LEGACY = /* @__PURE__ */ new Map();
|
|
1524
1626
|
for (const n of TIERS) {
|
|
1525
1627
|
V0_SIZES.set(computeSlabSize(V0_ENGINE_OFF, V0_ENGINE_BITMAP_OFF, V0_ACCOUNT_SIZE, n), n);
|
|
@@ -1530,6 +1632,9 @@ for (const n of TIERS) {
|
|
|
1530
1632
|
V2_SIZES.set(computeSlabSize(V2_ENGINE_OFF, V2_ENGINE_BITMAP_OFF, V2_ACCOUNT_SIZE, n, 18), n);
|
|
1531
1633
|
V1M_SIZES.set(computeSlabSize(V1M_ENGINE_OFF, V1M_ENGINE_BITMAP_OFF, V1M_ACCOUNT_SIZE, n, 18), n);
|
|
1532
1634
|
V_ADL_SIZES.set(computeSlabSize(V_ADL_ENGINE_OFF, V_ADL_ENGINE_BITMAP_OFF, V_ADL_ACCOUNT_SIZE, n, 18), n);
|
|
1635
|
+
V1M2_SIZES.set(computeSlabSize(V1M2_ENGINE_OFF, V1M2_ENGINE_BITMAP_OFF, V1M2_ACCOUNT_SIZE, n, 18), n);
|
|
1636
|
+
V_SETDEXPOOL_SIZES.set(computeSlabSize(V_SETDEXPOOL_ENGINE_OFF, V_ADL_ENGINE_BITMAP_OFF, V_ADL_ACCOUNT_SIZE, n, 18), n);
|
|
1637
|
+
V12_1_SIZES.set(computeSlabSize(V12_1_ENGINE_OFF, V12_1_ENGINE_BITMAP_OFF, V12_1_ACCOUNT_SIZE, n, 18), n);
|
|
1533
1638
|
}
|
|
1534
1639
|
var SLAB_TIERS_V2 = {
|
|
1535
1640
|
small: { maxAccounts: 256, dataSize: 65088, label: "Small", description: "256 slots (V2 BPF intermediate)" },
|
|
@@ -1824,42 +1929,69 @@ function buildLayoutV1M2(maxAccounts) {
|
|
|
1824
1929
|
bitmapWords,
|
|
1825
1930
|
accountsOff: engineOff + accountsOffRel,
|
|
1826
1931
|
engineInsuranceOff: 16,
|
|
1827
|
-
engineParamsOff:
|
|
1828
|
-
//
|
|
1829
|
-
paramsSize:
|
|
1830
|
-
// 336 — same as
|
|
1831
|
-
// Runtime fields:
|
|
1832
|
-
engineCurrentSlotOff:
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
//
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1932
|
+
engineParamsOff: V1M2_ENGINE_PARAMS_OFF,
|
|
1933
|
+
// 96 — expanded InsuranceFund (same as V_ADL)
|
|
1934
|
+
paramsSize: V_ADL_PARAMS_SIZE,
|
|
1935
|
+
// 336 — same as V_ADL
|
|
1936
|
+
// Runtime fields: V1M2 engine struct is layout-identical to V_ADL — reuse V_ADL constants.
|
|
1937
|
+
engineCurrentSlotOff: V_ADL_ENGINE_CURRENT_SLOT_OFF,
|
|
1938
|
+
// 432
|
|
1939
|
+
engineFundingIndexOff: V_ADL_ENGINE_FUNDING_INDEX_OFF,
|
|
1940
|
+
// 440
|
|
1941
|
+
engineLastFundingSlotOff: V_ADL_ENGINE_LAST_FUNDING_SLOT_OFF,
|
|
1942
|
+
// 456
|
|
1943
|
+
engineFundingRateBpsOff: V_ADL_ENGINE_FUNDING_RATE_BPS_OFF,
|
|
1944
|
+
// 464
|
|
1945
|
+
engineMarkPriceOff: V_ADL_ENGINE_MARK_PRICE_OFF,
|
|
1946
|
+
// 504
|
|
1947
|
+
engineLastCrankSlotOff: V_ADL_ENGINE_LAST_CRANK_SLOT_OFF,
|
|
1948
|
+
// 528
|
|
1949
|
+
engineMaxCrankStalenessOff: V_ADL_ENGINE_MAX_CRANK_STALENESS_OFF,
|
|
1950
|
+
// 536
|
|
1951
|
+
engineTotalOiOff: V_ADL_ENGINE_TOTAL_OI_OFF,
|
|
1952
|
+
// 544
|
|
1953
|
+
engineLongOiOff: V_ADL_ENGINE_LONG_OI_OFF,
|
|
1954
|
+
// 560
|
|
1955
|
+
engineShortOiOff: V_ADL_ENGINE_SHORT_OI_OFF,
|
|
1956
|
+
// 576
|
|
1957
|
+
engineCTotOff: V_ADL_ENGINE_C_TOT_OFF,
|
|
1958
|
+
// 592
|
|
1959
|
+
enginePnlPosTotOff: V_ADL_ENGINE_PNL_POS_TOT_OFF,
|
|
1960
|
+
// 608
|
|
1961
|
+
engineLiqCursorOff: V_ADL_ENGINE_LIQ_CURSOR_OFF,
|
|
1962
|
+
// 640
|
|
1963
|
+
engineGcCursorOff: V_ADL_ENGINE_GC_CURSOR_OFF,
|
|
1964
|
+
// 642
|
|
1965
|
+
engineLastSweepStartOff: V_ADL_ENGINE_LAST_SWEEP_START_OFF,
|
|
1966
|
+
// 648
|
|
1967
|
+
engineLastSweepCompleteOff: V_ADL_ENGINE_LAST_SWEEP_COMPLETE_OFF,
|
|
1968
|
+
// 656
|
|
1969
|
+
engineCrankCursorOff: V_ADL_ENGINE_CRANK_CURSOR_OFF,
|
|
1970
|
+
// 664
|
|
1971
|
+
engineSweepStartIdxOff: V_ADL_ENGINE_SWEEP_START_IDX_OFF,
|
|
1972
|
+
// 666
|
|
1973
|
+
engineLifetimeLiquidationsOff: V_ADL_ENGINE_LIFETIME_LIQUIDATIONS_OFF,
|
|
1974
|
+
// 672
|
|
1975
|
+
engineLifetimeForceClosesOff: V_ADL_ENGINE_LIFETIME_FORCE_CLOSES_OFF,
|
|
1976
|
+
// 680
|
|
1977
|
+
engineNetLpPosOff: V_ADL_ENGINE_NET_LP_POS_OFF,
|
|
1978
|
+
// 904
|
|
1979
|
+
engineLpSumAbsOff: V_ADL_ENGINE_LP_SUM_ABS_OFF,
|
|
1980
|
+
// 920
|
|
1981
|
+
engineLpMaxAbsOff: V_ADL_ENGINE_LP_MAX_ABS_OFF,
|
|
1982
|
+
// 936
|
|
1983
|
+
engineLpMaxAbsSweepOff: V_ADL_ENGINE_LP_MAX_ABS_SWEEP_OFF,
|
|
1984
|
+
// 952
|
|
1985
|
+
engineEmergencyOiModeOff: V_ADL_ENGINE_EMERGENCY_OI_MODE_OFF,
|
|
1986
|
+
// 968
|
|
1987
|
+
engineEmergencyStartSlotOff: V_ADL_ENGINE_EMERGENCY_START_SLOT_OFF,
|
|
1988
|
+
// 976
|
|
1989
|
+
engineLastBreakerSlotOff: V_ADL_ENGINE_LAST_BREAKER_SLOT_OFF,
|
|
1990
|
+
// 984
|
|
1860
1991
|
engineBitmapOff: V1M2_ENGINE_BITMAP_OFF,
|
|
1861
1992
|
postBitmap: 18,
|
|
1862
|
-
acctOwnerOff:
|
|
1993
|
+
acctOwnerOff: V_ADL_ACCT_OWNER_OFF,
|
|
1994
|
+
// 192 — same shift as V_ADL (reserved_pnl u64→u128)
|
|
1863
1995
|
hasInsuranceIsolation: true,
|
|
1864
1996
|
engineInsuranceIsolatedOff: 48,
|
|
1865
1997
|
engineInsuranceIsolationBpsOff: 64
|
|
@@ -1949,7 +2081,7 @@ function buildLayoutVADL(maxAccounts) {
|
|
|
1949
2081
|
engineLastBreakerSlotOff: V_ADL_ENGINE_LAST_BREAKER_SLOT_OFF,
|
|
1950
2082
|
// 984
|
|
1951
2083
|
engineBitmapOff: V_ADL_ENGINE_BITMAP_OFF,
|
|
1952
|
-
//
|
|
2084
|
+
// 1008
|
|
1953
2085
|
postBitmap: 18,
|
|
1954
2086
|
acctOwnerOff: V_ADL_ACCT_OWNER_OFF,
|
|
1955
2087
|
// 192
|
|
@@ -1958,17 +2090,145 @@ function buildLayoutVADL(maxAccounts) {
|
|
|
1958
2090
|
engineInsuranceIsolationBpsOff: 64
|
|
1959
2091
|
};
|
|
1960
2092
|
}
|
|
2093
|
+
var SLAB_TIERS_V_SETDEXPOOL = {};
|
|
2094
|
+
for (const [label, n] of [["Micro", 64], ["Small", 256], ["Medium", 1024], ["Large", 4096]]) {
|
|
2095
|
+
const size = computeSlabSize(V_SETDEXPOOL_ENGINE_OFF, V_ADL_ENGINE_BITMAP_OFF, V_ADL_ACCOUNT_SIZE, n, 18);
|
|
2096
|
+
SLAB_TIERS_V_SETDEXPOOL[label.toLowerCase()] = { maxAccounts: n, dataSize: size, label, description: `${n} slots (V_SETDEXPOOL PERC-SetDexPool)` };
|
|
2097
|
+
}
|
|
2098
|
+
var SLAB_TIERS_V12_1 = {};
|
|
2099
|
+
for (const [label, n] of [["Micro", 64], ["Small", 256], ["Medium", 1024], ["Large", 4096]]) {
|
|
2100
|
+
const size = computeSlabSize(V12_1_ENGINE_OFF, V12_1_ENGINE_BITMAP_OFF, V12_1_ACCOUNT_SIZE, n, 18);
|
|
2101
|
+
SLAB_TIERS_V12_1[label.toLowerCase()] = { maxAccounts: n, dataSize: size, label, description: `${n} slots (v12.1)` };
|
|
2102
|
+
}
|
|
2103
|
+
function buildLayoutVSetDexPool(maxAccounts) {
|
|
2104
|
+
const engineOff = V_SETDEXPOOL_ENGINE_OFF;
|
|
2105
|
+
const bitmapOff = V_ADL_ENGINE_BITMAP_OFF;
|
|
2106
|
+
const accountSize = V_ADL_ACCOUNT_SIZE;
|
|
2107
|
+
const bitmapWords = Math.ceil(maxAccounts / 64);
|
|
2108
|
+
const bitmapBytes = bitmapWords * 8;
|
|
2109
|
+
const postBitmap = 18;
|
|
2110
|
+
const nextFreeBytes = maxAccounts * 2;
|
|
2111
|
+
const preAccountsLen = bitmapOff + bitmapBytes + postBitmap + nextFreeBytes;
|
|
2112
|
+
const accountsOffRel = Math.ceil(preAccountsLen / 8) * 8;
|
|
2113
|
+
return {
|
|
2114
|
+
version: 1,
|
|
2115
|
+
headerLen: V1_HEADER_LEN,
|
|
2116
|
+
configOffset: V1_HEADER_LEN,
|
|
2117
|
+
configLen: V_SETDEXPOOL_CONFIG_LEN,
|
|
2118
|
+
// 544
|
|
2119
|
+
reservedOff: V1_RESERVED_OFF,
|
|
2120
|
+
engineOff,
|
|
2121
|
+
accountSize,
|
|
2122
|
+
maxAccounts,
|
|
2123
|
+
bitmapWords,
|
|
2124
|
+
accountsOff: engineOff + accountsOffRel,
|
|
2125
|
+
engineInsuranceOff: 16,
|
|
2126
|
+
engineParamsOff: V_ADL_ENGINE_PARAMS_OFF,
|
|
2127
|
+
paramsSize: V_ADL_PARAMS_SIZE,
|
|
2128
|
+
engineCurrentSlotOff: V_ADL_ENGINE_CURRENT_SLOT_OFF,
|
|
2129
|
+
engineFundingIndexOff: V_ADL_ENGINE_FUNDING_INDEX_OFF,
|
|
2130
|
+
engineLastFundingSlotOff: V_ADL_ENGINE_LAST_FUNDING_SLOT_OFF,
|
|
2131
|
+
engineFundingRateBpsOff: V_ADL_ENGINE_FUNDING_RATE_BPS_OFF,
|
|
2132
|
+
engineMarkPriceOff: V_ADL_ENGINE_MARK_PRICE_OFF,
|
|
2133
|
+
engineLastCrankSlotOff: V_ADL_ENGINE_LAST_CRANK_SLOT_OFF,
|
|
2134
|
+
engineMaxCrankStalenessOff: V_ADL_ENGINE_MAX_CRANK_STALENESS_OFF,
|
|
2135
|
+
engineTotalOiOff: V_ADL_ENGINE_TOTAL_OI_OFF,
|
|
2136
|
+
engineLongOiOff: V_ADL_ENGINE_LONG_OI_OFF,
|
|
2137
|
+
engineShortOiOff: V_ADL_ENGINE_SHORT_OI_OFF,
|
|
2138
|
+
engineCTotOff: V_ADL_ENGINE_C_TOT_OFF,
|
|
2139
|
+
enginePnlPosTotOff: V_ADL_ENGINE_PNL_POS_TOT_OFF,
|
|
2140
|
+
engineLiqCursorOff: V_ADL_ENGINE_LIQ_CURSOR_OFF,
|
|
2141
|
+
engineGcCursorOff: V_ADL_ENGINE_GC_CURSOR_OFF,
|
|
2142
|
+
engineLastSweepStartOff: V_ADL_ENGINE_LAST_SWEEP_START_OFF,
|
|
2143
|
+
engineLastSweepCompleteOff: V_ADL_ENGINE_LAST_SWEEP_COMPLETE_OFF,
|
|
2144
|
+
engineCrankCursorOff: V_ADL_ENGINE_CRANK_CURSOR_OFF,
|
|
2145
|
+
engineSweepStartIdxOff: V_ADL_ENGINE_SWEEP_START_IDX_OFF,
|
|
2146
|
+
engineLifetimeLiquidationsOff: V_ADL_ENGINE_LIFETIME_LIQUIDATIONS_OFF,
|
|
2147
|
+
engineLifetimeForceClosesOff: V_ADL_ENGINE_LIFETIME_FORCE_CLOSES_OFF,
|
|
2148
|
+
engineNetLpPosOff: V_ADL_ENGINE_NET_LP_POS_OFF,
|
|
2149
|
+
engineLpSumAbsOff: V_ADL_ENGINE_LP_SUM_ABS_OFF,
|
|
2150
|
+
engineLpMaxAbsOff: V_ADL_ENGINE_LP_MAX_ABS_OFF,
|
|
2151
|
+
engineLpMaxAbsSweepOff: V_ADL_ENGINE_LP_MAX_ABS_SWEEP_OFF,
|
|
2152
|
+
engineEmergencyOiModeOff: V_ADL_ENGINE_EMERGENCY_OI_MODE_OFF,
|
|
2153
|
+
engineEmergencyStartSlotOff: V_ADL_ENGINE_EMERGENCY_START_SLOT_OFF,
|
|
2154
|
+
engineLastBreakerSlotOff: V_ADL_ENGINE_LAST_BREAKER_SLOT_OFF,
|
|
2155
|
+
engineBitmapOff: V_ADL_ENGINE_BITMAP_OFF,
|
|
2156
|
+
postBitmap: 18,
|
|
2157
|
+
acctOwnerOff: V_ADL_ACCT_OWNER_OFF,
|
|
2158
|
+
hasInsuranceIsolation: true,
|
|
2159
|
+
engineInsuranceIsolatedOff: 48,
|
|
2160
|
+
engineInsuranceIsolationBpsOff: 64
|
|
2161
|
+
};
|
|
2162
|
+
}
|
|
2163
|
+
function buildLayoutV12_1(maxAccounts) {
|
|
2164
|
+
const engineOff = V12_1_ENGINE_OFF;
|
|
2165
|
+
const bitmapOff = V12_1_ENGINE_BITMAP_OFF;
|
|
2166
|
+
const accountSize = V12_1_ACCOUNT_SIZE;
|
|
2167
|
+
const bitmapWords = Math.ceil(maxAccounts / 64);
|
|
2168
|
+
const bitmapBytes = bitmapWords * 8;
|
|
2169
|
+
const postBitmap = 18;
|
|
2170
|
+
const nextFreeBytes = maxAccounts * 2;
|
|
2171
|
+
const preAccountsLen = bitmapOff + bitmapBytes + postBitmap + nextFreeBytes;
|
|
2172
|
+
const accountsOffRel = Math.ceil(preAccountsLen / 8) * 8;
|
|
2173
|
+
return {
|
|
2174
|
+
version: 1,
|
|
2175
|
+
headerLen: V1_HEADER_LEN,
|
|
2176
|
+
configOffset: V1_HEADER_LEN,
|
|
2177
|
+
configLen: V_SETDEXPOOL_CONFIG_LEN,
|
|
2178
|
+
// 544 (same as V_SETDEXPOOL)
|
|
2179
|
+
reservedOff: V1_RESERVED_OFF,
|
|
2180
|
+
engineOff,
|
|
2181
|
+
accountSize,
|
|
2182
|
+
maxAccounts,
|
|
2183
|
+
bitmapWords,
|
|
2184
|
+
accountsOff: engineOff + accountsOffRel,
|
|
2185
|
+
engineInsuranceOff: 16,
|
|
2186
|
+
engineParamsOff: V12_1_ENGINE_PARAMS_OFF,
|
|
2187
|
+
paramsSize: V12_1_PARAMS_SIZE,
|
|
2188
|
+
engineCurrentSlotOff: V12_1_ENGINE_CURRENT_SLOT_OFF,
|
|
2189
|
+
engineFundingIndexOff: V12_1_ENGINE_FUNDING_INDEX_OFF,
|
|
2190
|
+
engineLastFundingSlotOff: V12_1_ENGINE_LAST_FUNDING_SLOT_OFF,
|
|
2191
|
+
engineFundingRateBpsOff: V12_1_ENGINE_FUNDING_RATE_BPS_OFF,
|
|
2192
|
+
engineMarkPriceOff: V12_1_ENGINE_MARK_PRICE_OFF,
|
|
2193
|
+
engineLastCrankSlotOff: V12_1_ENGINE_LAST_CRANK_SLOT_OFF,
|
|
2194
|
+
engineMaxCrankStalenessOff: V12_1_ENGINE_MAX_CRANK_STALENESS_OFF,
|
|
2195
|
+
engineTotalOiOff: V12_1_ENGINE_TOTAL_OI_OFF,
|
|
2196
|
+
engineLongOiOff: V12_1_ENGINE_LONG_OI_OFF,
|
|
2197
|
+
engineShortOiOff: V12_1_ENGINE_SHORT_OI_OFF,
|
|
2198
|
+
engineCTotOff: V12_1_ENGINE_C_TOT_OFF,
|
|
2199
|
+
enginePnlPosTotOff: V12_1_ENGINE_PNL_POS_TOT_OFF,
|
|
2200
|
+
engineLiqCursorOff: V12_1_ENGINE_LIQ_CURSOR_OFF,
|
|
2201
|
+
engineGcCursorOff: V12_1_ENGINE_GC_CURSOR_OFF,
|
|
2202
|
+
engineLastSweepStartOff: V12_1_ENGINE_LAST_SWEEP_START_OFF,
|
|
2203
|
+
engineLastSweepCompleteOff: V12_1_ENGINE_LAST_SWEEP_COMPLETE_OFF,
|
|
2204
|
+
engineCrankCursorOff: V12_1_ENGINE_CRANK_CURSOR_OFF,
|
|
2205
|
+
engineSweepStartIdxOff: V12_1_ENGINE_SWEEP_START_IDX_OFF,
|
|
2206
|
+
engineLifetimeLiquidationsOff: V12_1_ENGINE_LIFETIME_LIQUIDATIONS_OFF,
|
|
2207
|
+
engineLifetimeForceClosesOff: V12_1_ENGINE_LIFETIME_FORCE_CLOSES_OFF,
|
|
2208
|
+
engineNetLpPosOff: V12_1_ENGINE_NET_LP_POS_OFF,
|
|
2209
|
+
engineLpSumAbsOff: V12_1_ENGINE_LP_SUM_ABS_OFF,
|
|
2210
|
+
engineLpMaxAbsOff: V12_1_ENGINE_LP_MAX_ABS_OFF,
|
|
2211
|
+
engineLpMaxAbsSweepOff: V12_1_ENGINE_LP_MAX_ABS_SWEEP_OFF,
|
|
2212
|
+
engineEmergencyOiModeOff: V12_1_ENGINE_EMERGENCY_OI_MODE_OFF,
|
|
2213
|
+
engineEmergencyStartSlotOff: V12_1_ENGINE_EMERGENCY_START_SLOT_OFF,
|
|
2214
|
+
engineLastBreakerSlotOff: V12_1_ENGINE_LAST_BREAKER_SLOT_OFF,
|
|
2215
|
+
engineBitmapOff: V12_1_ENGINE_BITMAP_OFF,
|
|
2216
|
+
postBitmap: 18,
|
|
2217
|
+
acctOwnerOff: V12_1_ACCT_OWNER_OFF,
|
|
2218
|
+
hasInsuranceIsolation: true,
|
|
2219
|
+
engineInsuranceIsolatedOff: 48,
|
|
2220
|
+
engineInsuranceIsolationBpsOff: 64
|
|
2221
|
+
};
|
|
2222
|
+
}
|
|
1961
2223
|
function detectSlabLayout(dataLen, data) {
|
|
2224
|
+
const v121n = V12_1_SIZES.get(dataLen);
|
|
2225
|
+
if (v121n !== void 0) return buildLayoutV12_1(v121n);
|
|
2226
|
+
const vsdpn = V_SETDEXPOOL_SIZES.get(dataLen);
|
|
2227
|
+
if (vsdpn !== void 0) return buildLayoutVSetDexPool(vsdpn);
|
|
2228
|
+
const v1m2n = V1M2_SIZES.get(dataLen);
|
|
2229
|
+
if (v1m2n !== void 0) return buildLayoutV1M2(v1m2n);
|
|
1962
2230
|
const vadln = V_ADL_SIZES.get(dataLen);
|
|
1963
|
-
if (vadln !== void 0)
|
|
1964
|
-
if (data && data.length >= 752) {
|
|
1965
|
-
const maxAcctsV1M2 = readU64LE(data, V1M2_ENGINE_OFF + V1M_ENGINE_PARAMS_OFF + 32);
|
|
1966
|
-
if (maxAcctsV1M2 === BigInt(vadln)) {
|
|
1967
|
-
return buildLayoutV1M2(vadln);
|
|
1968
|
-
}
|
|
1969
|
-
}
|
|
1970
|
-
return buildLayoutVADL(vadln);
|
|
1971
|
-
}
|
|
2231
|
+
if (vadln !== void 0) return buildLayoutVADL(vadln);
|
|
1972
2232
|
const v1mn = V1M_SIZES.get(dataLen);
|
|
1973
2233
|
if (v1mn !== void 0) return buildLayoutV1M(v1mn);
|
|
1974
2234
|
const v0n = V0_SIZES.get(dataLen);
|
|
@@ -2201,6 +2461,14 @@ function parseConfig(data, layoutHint) {
|
|
|
2201
2461
|
}
|
|
2202
2462
|
}
|
|
2203
2463
|
}
|
|
2464
|
+
let dexPool = null;
|
|
2465
|
+
const DEX_POOL_REL_OFF = 512;
|
|
2466
|
+
if (configLen >= DEX_POOL_REL_OFF + 32 && data.length >= configOff + DEX_POOL_REL_OFF + 32) {
|
|
2467
|
+
const dexPoolBytes = data.subarray(configOff + DEX_POOL_REL_OFF, configOff + DEX_POOL_REL_OFF + 32);
|
|
2468
|
+
if (dexPoolBytes.some((b) => b !== 0)) {
|
|
2469
|
+
dexPool = new PublicKey3(dexPoolBytes);
|
|
2470
|
+
}
|
|
2471
|
+
}
|
|
2204
2472
|
return {
|
|
2205
2473
|
collateralMint,
|
|
2206
2474
|
vaultPubkey,
|
|
@@ -2243,7 +2511,8 @@ function parseConfig(data, layoutHint) {
|
|
|
2243
2511
|
insuranceIsolationBps,
|
|
2244
2512
|
oraclePhase,
|
|
2245
2513
|
cumulativeVolumeE6,
|
|
2246
|
-
phase2DeltaSlots
|
|
2514
|
+
phase2DeltaSlots,
|
|
2515
|
+
dexPool
|
|
2247
2516
|
};
|
|
2248
2517
|
}
|
|
2249
2518
|
function parseParams(data, layoutHint) {
|
|
@@ -2500,7 +2769,14 @@ function derivePythPushOraclePDA(feedIdHex) {
|
|
|
2500
2769
|
}
|
|
2501
2770
|
const feedId = new Uint8Array(32);
|
|
2502
2771
|
for (let i = 0; i < 32; i++) {
|
|
2503
|
-
|
|
2772
|
+
const hexPair = normalized.substring(i * 2, i * 2 + 2);
|
|
2773
|
+
const byte = parseInt(hexPair, 16);
|
|
2774
|
+
if (Number.isNaN(byte)) {
|
|
2775
|
+
throw new Error(
|
|
2776
|
+
`derivePythPushOraclePDA: failed to parse hex byte at position ${i}: "${hexPair}"`
|
|
2777
|
+
);
|
|
2778
|
+
}
|
|
2779
|
+
feedId[i] = byte;
|
|
2504
2780
|
}
|
|
2505
2781
|
const shardBuf = new Uint8Array(2);
|
|
2506
2782
|
return PublicKey4.findProgramAddressSync(
|
|
@@ -2526,13 +2802,76 @@ async function fetchTokenAccount(connection, address, tokenProgramId = TOKEN_PRO
|
|
|
2526
2802
|
return getAccount(connection, address, void 0, tokenProgramId);
|
|
2527
2803
|
}
|
|
2528
2804
|
|
|
2805
|
+
// src/solana/discovery.ts
|
|
2806
|
+
import { PublicKey as PublicKey6 } from "@solana/web3.js";
|
|
2807
|
+
|
|
2808
|
+
// src/solana/static-markets.ts
|
|
2809
|
+
import { PublicKey as PublicKey5 } from "@solana/web3.js";
|
|
2810
|
+
var MAINNET_MARKETS = [
|
|
2811
|
+
// Populated at mainnet launch — currently empty.
|
|
2812
|
+
// To add entries:
|
|
2813
|
+
// { slabAddress: "ABC123...", symbol: "SOL-PERP", name: "SOL Perpetual" },
|
|
2814
|
+
];
|
|
2815
|
+
var DEVNET_MARKETS = [
|
|
2816
|
+
// Populated from prior discoverMarkets() runs on devnet.
|
|
2817
|
+
// These serve as the tier-3 safety net for devnet users.
|
|
2818
|
+
];
|
|
2819
|
+
var STATIC_REGISTRY = {
|
|
2820
|
+
mainnet: MAINNET_MARKETS,
|
|
2821
|
+
devnet: DEVNET_MARKETS
|
|
2822
|
+
};
|
|
2823
|
+
var USER_MARKETS = {
|
|
2824
|
+
mainnet: [],
|
|
2825
|
+
devnet: []
|
|
2826
|
+
};
|
|
2827
|
+
function getStaticMarkets(network) {
|
|
2828
|
+
const builtin = STATIC_REGISTRY[network] ?? [];
|
|
2829
|
+
const user = USER_MARKETS[network] ?? [];
|
|
2830
|
+
if (user.length === 0) return [...builtin];
|
|
2831
|
+
const seen = /* @__PURE__ */ new Map();
|
|
2832
|
+
for (const entry of builtin) {
|
|
2833
|
+
seen.set(entry.slabAddress, entry);
|
|
2834
|
+
}
|
|
2835
|
+
for (const entry of user) {
|
|
2836
|
+
seen.set(entry.slabAddress, entry);
|
|
2837
|
+
}
|
|
2838
|
+
return [...seen.values()];
|
|
2839
|
+
}
|
|
2840
|
+
function registerStaticMarkets(network, entries) {
|
|
2841
|
+
const existing = USER_MARKETS[network];
|
|
2842
|
+
const seen = new Set(existing.map((e) => e.slabAddress));
|
|
2843
|
+
for (const entry of entries) {
|
|
2844
|
+
if (!entry.slabAddress) continue;
|
|
2845
|
+
if (seen.has(entry.slabAddress)) continue;
|
|
2846
|
+
try {
|
|
2847
|
+
new PublicKey5(entry.slabAddress);
|
|
2848
|
+
} catch {
|
|
2849
|
+
console.warn(
|
|
2850
|
+
`[registerStaticMarkets] Skipping invalid slabAddress: ${entry.slabAddress}`
|
|
2851
|
+
);
|
|
2852
|
+
continue;
|
|
2853
|
+
}
|
|
2854
|
+
seen.add(entry.slabAddress);
|
|
2855
|
+
existing.push(entry);
|
|
2856
|
+
}
|
|
2857
|
+
}
|
|
2858
|
+
function clearStaticMarkets(network) {
|
|
2859
|
+
if (network) {
|
|
2860
|
+
USER_MARKETS[network] = [];
|
|
2861
|
+
} else {
|
|
2862
|
+
USER_MARKETS.mainnet = [];
|
|
2863
|
+
USER_MARKETS.devnet = [];
|
|
2864
|
+
}
|
|
2865
|
+
}
|
|
2866
|
+
|
|
2529
2867
|
// src/solana/discovery.ts
|
|
2530
2868
|
var ENGINE_BITMAP_OFF_V0 = 320;
|
|
2531
2869
|
var MAGIC_BYTES = new Uint8Array([84, 65, 76, 79, 67, 82, 69, 80]);
|
|
2532
2870
|
var SLAB_TIERS = {
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2871
|
+
micro: SLAB_TIERS_V12_1["micro"],
|
|
2872
|
+
small: SLAB_TIERS_V12_1["small"],
|
|
2873
|
+
medium: SLAB_TIERS_V12_1["medium"],
|
|
2874
|
+
large: SLAB_TIERS_V12_1["large"]
|
|
2536
2875
|
};
|
|
2537
2876
|
var SLAB_TIERS_V0 = {
|
|
2538
2877
|
small: { maxAccounts: 256, dataSize: 62808, label: "Small", description: "256 slots \xB7 ~0.44 SOL" },
|
|
@@ -2910,19 +3249,74 @@ async function discoverMarkets(connection, programId, options = {}) {
|
|
|
2910
3249
|
"[discoverMarkets] dataSize filters failed, falling back to memcmp:",
|
|
2911
3250
|
err instanceof Error ? err.message : err
|
|
2912
3251
|
);
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
3252
|
+
try {
|
|
3253
|
+
const fallback = await connection.getProgramAccounts(programId, {
|
|
3254
|
+
filters: [
|
|
3255
|
+
{
|
|
3256
|
+
memcmp: {
|
|
3257
|
+
offset: 0,
|
|
3258
|
+
bytes: "F6P2QNqpQV5"
|
|
3259
|
+
// base58 of TALOCREP (u64 LE magic)
|
|
3260
|
+
}
|
|
2920
3261
|
}
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
3262
|
+
],
|
|
3263
|
+
dataSlice: { offset: 0, length: HEADER_SLICE_LENGTH }
|
|
3264
|
+
});
|
|
3265
|
+
rawAccounts = [...fallback].map((e) => ({ ...e, maxAccounts: 4096, dataSize: SLAB_TIERS.large.dataSize }));
|
|
3266
|
+
} catch (memcmpErr) {
|
|
3267
|
+
console.warn(
|
|
3268
|
+
"[discoverMarkets] memcmp fallback also failed:",
|
|
3269
|
+
memcmpErr instanceof Error ? memcmpErr.message : memcmpErr
|
|
3270
|
+
);
|
|
3271
|
+
}
|
|
3272
|
+
}
|
|
3273
|
+
if (rawAccounts.length === 0 && options.apiBaseUrl) {
|
|
3274
|
+
console.warn(
|
|
3275
|
+
"[discoverMarkets] RPC discovery returned 0 markets, falling back to REST API"
|
|
3276
|
+
);
|
|
3277
|
+
try {
|
|
3278
|
+
const apiResult = await discoverMarketsViaApi(
|
|
3279
|
+
connection,
|
|
3280
|
+
programId,
|
|
3281
|
+
options.apiBaseUrl,
|
|
3282
|
+
{ timeoutMs: options.apiTimeoutMs }
|
|
3283
|
+
);
|
|
3284
|
+
if (apiResult.length > 0) {
|
|
3285
|
+
return apiResult;
|
|
3286
|
+
}
|
|
3287
|
+
console.warn(
|
|
3288
|
+
"[discoverMarkets] REST API returned 0 markets, checking tier-3 static bundle"
|
|
3289
|
+
);
|
|
3290
|
+
} catch (apiErr) {
|
|
3291
|
+
console.warn(
|
|
3292
|
+
"[discoverMarkets] API fallback also failed:",
|
|
3293
|
+
apiErr instanceof Error ? apiErr.message : apiErr
|
|
3294
|
+
);
|
|
3295
|
+
}
|
|
3296
|
+
}
|
|
3297
|
+
if (rawAccounts.length === 0 && options.network) {
|
|
3298
|
+
const staticEntries = getStaticMarkets(options.network);
|
|
3299
|
+
if (staticEntries.length > 0) {
|
|
3300
|
+
console.warn(
|
|
3301
|
+
`[discoverMarkets] Tier 1+2 failed, falling back to static bundle (${staticEntries.length} addresses for ${options.network})`
|
|
3302
|
+
);
|
|
3303
|
+
try {
|
|
3304
|
+
return await discoverMarketsViaStaticBundle(
|
|
3305
|
+
connection,
|
|
3306
|
+
programId,
|
|
3307
|
+
staticEntries
|
|
3308
|
+
);
|
|
3309
|
+
} catch (staticErr) {
|
|
3310
|
+
console.warn(
|
|
3311
|
+
"[discoverMarkets] Static bundle fallback also failed:",
|
|
3312
|
+
staticErr instanceof Error ? staticErr.message : staticErr
|
|
3313
|
+
);
|
|
3314
|
+
}
|
|
3315
|
+
} else {
|
|
3316
|
+
console.warn(
|
|
3317
|
+
`[discoverMarkets] Static bundle has 0 entries for ${options.network} \u2014 skipping tier 3`
|
|
3318
|
+
);
|
|
3319
|
+
}
|
|
2926
3320
|
}
|
|
2927
3321
|
const accounts = rawAccounts;
|
|
2928
3322
|
const markets = [];
|
|
@@ -2962,29 +3356,165 @@ async function discoverMarkets(connection, programId, options = {}) {
|
|
|
2962
3356
|
}
|
|
2963
3357
|
return markets;
|
|
2964
3358
|
}
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
3359
|
+
async function getMarketsByAddress(connection, programId, addresses, options = {}) {
|
|
3360
|
+
if (addresses.length === 0) return [];
|
|
3361
|
+
const {
|
|
3362
|
+
batchSize = 100,
|
|
3363
|
+
interBatchDelayMs = 0
|
|
3364
|
+
} = options;
|
|
3365
|
+
const effectiveBatchSize = Math.max(1, Math.min(batchSize, 100));
|
|
3366
|
+
const fetched = [];
|
|
3367
|
+
for (let offset = 0; offset < addresses.length; offset += effectiveBatchSize) {
|
|
3368
|
+
const batch = addresses.slice(offset, offset + effectiveBatchSize);
|
|
3369
|
+
const response = await connection.getMultipleAccountsInfo(batch);
|
|
3370
|
+
for (let i = 0; i < batch.length; i++) {
|
|
3371
|
+
const info = response[i];
|
|
3372
|
+
if (info && info.data) {
|
|
3373
|
+
if (!info.owner.equals(programId)) {
|
|
3374
|
+
console.warn(
|
|
3375
|
+
`[getMarketsByAddress] Skipping ${batch[i].toBase58()}: owner mismatch (expected ${programId.toBase58()}, got ${info.owner.toBase58()})`
|
|
3376
|
+
);
|
|
3377
|
+
continue;
|
|
3378
|
+
}
|
|
3379
|
+
fetched.push({ pubkey: batch[i], data: info.data });
|
|
3380
|
+
}
|
|
3381
|
+
}
|
|
3382
|
+
if (interBatchDelayMs > 0 && offset + effectiveBatchSize < addresses.length) {
|
|
3383
|
+
await new Promise((r) => setTimeout(r, interBatchDelayMs));
|
|
3384
|
+
}
|
|
3385
|
+
}
|
|
3386
|
+
const markets = [];
|
|
3387
|
+
for (const entry of fetched) {
|
|
3388
|
+
if (!entry) continue;
|
|
3389
|
+
const { pubkey, data: rawData } = entry;
|
|
3390
|
+
const data = new Uint8Array(rawData);
|
|
3391
|
+
let valid = true;
|
|
3392
|
+
for (let i = 0; i < MAGIC_BYTES.length; i++) {
|
|
3393
|
+
if (data[i] !== MAGIC_BYTES[i]) {
|
|
3394
|
+
valid = false;
|
|
3395
|
+
break;
|
|
3396
|
+
}
|
|
3397
|
+
}
|
|
3398
|
+
if (!valid) {
|
|
3399
|
+
console.warn(
|
|
3400
|
+
`[getMarketsByAddress] Skipping ${pubkey.toBase58()}: invalid magic bytes`
|
|
3401
|
+
);
|
|
3402
|
+
continue;
|
|
3403
|
+
}
|
|
3404
|
+
const layout = detectSlabLayout(data.length, data);
|
|
3405
|
+
if (!layout) {
|
|
3406
|
+
console.warn(
|
|
3407
|
+
`[getMarketsByAddress] Skipping ${pubkey.toBase58()}: unrecognized layout for dataSize=${data.length}`
|
|
3408
|
+
);
|
|
3409
|
+
continue;
|
|
3410
|
+
}
|
|
3411
|
+
try {
|
|
3412
|
+
const header = parseHeader(data);
|
|
3413
|
+
const config = parseConfig(data, layout);
|
|
3414
|
+
const engine = parseEngineLight(data, layout, layout.maxAccounts);
|
|
3415
|
+
const params = parseParams(data, layout);
|
|
3416
|
+
markets.push({ slabAddress: pubkey, programId, header, config, engine, params });
|
|
3417
|
+
} catch (err) {
|
|
3418
|
+
console.warn(
|
|
3419
|
+
`[getMarketsByAddress] Failed to parse account ${pubkey.toBase58()}:`,
|
|
3420
|
+
err instanceof Error ? err.message : err
|
|
3421
|
+
);
|
|
3422
|
+
}
|
|
3423
|
+
}
|
|
3424
|
+
return markets;
|
|
3425
|
+
}
|
|
3426
|
+
async function discoverMarketsViaApi(connection, programId, apiBaseUrl, options = {}) {
|
|
3427
|
+
const { timeoutMs = 1e4, onChainOptions } = options;
|
|
3428
|
+
const base = apiBaseUrl.replace(/\/+$/, "");
|
|
3429
|
+
const url = `${base}/markets`;
|
|
3430
|
+
const controller = new AbortController();
|
|
3431
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
3432
|
+
let response;
|
|
3433
|
+
try {
|
|
3434
|
+
response = await fetch(url, {
|
|
3435
|
+
method: "GET",
|
|
3436
|
+
headers: { Accept: "application/json" },
|
|
3437
|
+
signal: controller.signal
|
|
3438
|
+
});
|
|
3439
|
+
} finally {
|
|
3440
|
+
clearTimeout(timer);
|
|
3441
|
+
}
|
|
3442
|
+
if (!response.ok) {
|
|
3443
|
+
throw new Error(
|
|
3444
|
+
`[discoverMarketsViaApi] API returned ${response.status} ${response.statusText} from ${url}`
|
|
3445
|
+
);
|
|
3446
|
+
}
|
|
3447
|
+
const body = await response.json();
|
|
3448
|
+
const apiMarkets = body.markets;
|
|
3449
|
+
if (!Array.isArray(apiMarkets) || apiMarkets.length === 0) {
|
|
3450
|
+
console.warn("[discoverMarketsViaApi] API returned 0 markets");
|
|
3451
|
+
return [];
|
|
3452
|
+
}
|
|
3453
|
+
const addresses = [];
|
|
3454
|
+
for (const entry of apiMarkets) {
|
|
3455
|
+
if (!entry.slab_address || typeof entry.slab_address !== "string") continue;
|
|
3456
|
+
try {
|
|
3457
|
+
addresses.push(new PublicKey6(entry.slab_address));
|
|
3458
|
+
} catch {
|
|
3459
|
+
console.warn(
|
|
3460
|
+
`[discoverMarketsViaApi] Skipping invalid slab address: ${entry.slab_address}`
|
|
3461
|
+
);
|
|
3462
|
+
}
|
|
3463
|
+
}
|
|
3464
|
+
if (addresses.length === 0) {
|
|
3465
|
+
console.warn("[discoverMarketsViaApi] No valid slab addresses from API");
|
|
3466
|
+
return [];
|
|
3467
|
+
}
|
|
3468
|
+
console.log(
|
|
3469
|
+
`[discoverMarketsViaApi] API returned ${addresses.length} slab addresses, fetching on-chain data`
|
|
3470
|
+
);
|
|
3471
|
+
return getMarketsByAddress(connection, programId, addresses, onChainOptions);
|
|
3472
|
+
}
|
|
3473
|
+
async function discoverMarketsViaStaticBundle(connection, programId, entries, options = {}) {
|
|
3474
|
+
if (entries.length === 0) return [];
|
|
3475
|
+
const addresses = [];
|
|
3476
|
+
for (const entry of entries) {
|
|
3477
|
+
if (!entry.slabAddress || typeof entry.slabAddress !== "string") continue;
|
|
3478
|
+
try {
|
|
3479
|
+
addresses.push(new PublicKey6(entry.slabAddress));
|
|
3480
|
+
} catch {
|
|
3481
|
+
console.warn(
|
|
3482
|
+
`[discoverMarketsViaStaticBundle] Skipping invalid slab address: ${entry.slabAddress}`
|
|
3483
|
+
);
|
|
3484
|
+
}
|
|
3485
|
+
}
|
|
3486
|
+
if (addresses.length === 0) {
|
|
3487
|
+
console.warn("[discoverMarketsViaStaticBundle] No valid slab addresses in static bundle");
|
|
3488
|
+
return [];
|
|
3489
|
+
}
|
|
3490
|
+
console.log(
|
|
3491
|
+
`[discoverMarketsViaStaticBundle] Fetching ${addresses.length} slab addresses on-chain`
|
|
3492
|
+
);
|
|
3493
|
+
return getMarketsByAddress(connection, programId, addresses, options.onChainOptions);
|
|
3494
|
+
}
|
|
3495
|
+
|
|
3496
|
+
// src/solana/dex-oracle.ts
|
|
3497
|
+
import { PublicKey as PublicKey7 } from "@solana/web3.js";
|
|
3498
|
+
function detectDexType(ownerProgramId) {
|
|
3499
|
+
if (ownerProgramId.equals(PUMPSWAP_PROGRAM_ID)) return "pumpswap";
|
|
3500
|
+
if (ownerProgramId.equals(RAYDIUM_CLMM_PROGRAM_ID)) return "raydium-clmm";
|
|
3501
|
+
if (ownerProgramId.equals(METEORA_DLMM_PROGRAM_ID)) return "meteora-dlmm";
|
|
3502
|
+
return null;
|
|
3503
|
+
}
|
|
3504
|
+
function parseDexPool(dexType, poolAddress, data) {
|
|
3505
|
+
switch (dexType) {
|
|
3506
|
+
case "pumpswap":
|
|
3507
|
+
return parsePumpSwapPool(poolAddress, data);
|
|
3508
|
+
case "raydium-clmm":
|
|
3509
|
+
return parseRaydiumClmmPool(poolAddress, data);
|
|
3510
|
+
case "meteora-dlmm":
|
|
3511
|
+
return parseMeteoraPool(poolAddress, data);
|
|
3512
|
+
}
|
|
3513
|
+
}
|
|
3514
|
+
function computeDexSpotPriceE6(dexType, data, vaultData) {
|
|
3515
|
+
switch (dexType) {
|
|
3516
|
+
case "pumpswap":
|
|
3517
|
+
if (!vaultData) throw new Error("PumpSwap requires vaultData (base and quote vault accounts)");
|
|
2988
3518
|
return computePumpSwapPriceE6(data, vaultData);
|
|
2989
3519
|
case "raydium-clmm":
|
|
2990
3520
|
return computeRaydiumClmmPriceE6(data);
|
|
@@ -3000,10 +3530,10 @@ function parsePumpSwapPool(poolAddress, data) {
|
|
|
3000
3530
|
return {
|
|
3001
3531
|
dexType: "pumpswap",
|
|
3002
3532
|
poolAddress,
|
|
3003
|
-
baseMint: new
|
|
3004
|
-
quoteMint: new
|
|
3005
|
-
baseVault: new
|
|
3006
|
-
quoteVault: new
|
|
3533
|
+
baseMint: new PublicKey7(data.slice(35, 67)),
|
|
3534
|
+
quoteMint: new PublicKey7(data.slice(67, 99)),
|
|
3535
|
+
baseVault: new PublicKey7(data.slice(131, 163)),
|
|
3536
|
+
quoteVault: new PublicKey7(data.slice(163, 195))
|
|
3007
3537
|
};
|
|
3008
3538
|
}
|
|
3009
3539
|
var SPL_TOKEN_AMOUNT_MIN_LEN = 72;
|
|
@@ -3029,8 +3559,8 @@ function parseRaydiumClmmPool(poolAddress, data) {
|
|
|
3029
3559
|
return {
|
|
3030
3560
|
dexType: "raydium-clmm",
|
|
3031
3561
|
poolAddress,
|
|
3032
|
-
baseMint: new
|
|
3033
|
-
quoteMint: new
|
|
3562
|
+
baseMint: new PublicKey7(data.slice(73, 105)),
|
|
3563
|
+
quoteMint: new PublicKey7(data.slice(105, 137))
|
|
3034
3564
|
};
|
|
3035
3565
|
}
|
|
3036
3566
|
var MAX_TOKEN_DECIMALS = 24;
|
|
@@ -3048,9 +3578,7 @@ function computeRaydiumClmmPriceE6(data) {
|
|
|
3048
3578
|
}
|
|
3049
3579
|
const sqrtPriceX64 = readU128LE3(dv3, 253);
|
|
3050
3580
|
if (sqrtPriceX64 === 0n) return 0n;
|
|
3051
|
-
const
|
|
3052
|
-
const term = scaledSqrt >> 64n;
|
|
3053
|
-
const priceE6Raw = term * sqrtPriceX64 >> 64n;
|
|
3581
|
+
const priceE6Raw = sqrtPriceX64 * sqrtPriceX64 * 1000000n >> 128n;
|
|
3054
3582
|
const decimalDiff = 6 + decimals0 - decimals1;
|
|
3055
3583
|
const adjustedDiff = decimalDiff - 6;
|
|
3056
3584
|
if (adjustedDiff >= 0) {
|
|
@@ -3069,8 +3597,8 @@ function parseMeteoraPool(poolAddress, data) {
|
|
|
3069
3597
|
return {
|
|
3070
3598
|
dexType: "meteora-dlmm",
|
|
3071
3599
|
poolAddress,
|
|
3072
|
-
baseMint: new
|
|
3073
|
-
quoteMint: new
|
|
3600
|
+
baseMint: new PublicKey7(data.slice(81, 113)),
|
|
3601
|
+
quoteMint: new PublicKey7(data.slice(113, 145))
|
|
3074
3602
|
};
|
|
3075
3603
|
}
|
|
3076
3604
|
var MAX_BIN_STEP = 1e4;
|
|
@@ -3103,7 +3631,14 @@ function computeMeteoraDlmmPriceE6(data) {
|
|
|
3103
3631
|
let exp = isNeg ? BigInt(-activeId) : BigInt(activeId);
|
|
3104
3632
|
let result = SCALE;
|
|
3105
3633
|
let b = base;
|
|
3634
|
+
let iterations = 0;
|
|
3635
|
+
const MAX_ITERATIONS = 25;
|
|
3106
3636
|
while (exp > 0n) {
|
|
3637
|
+
if (iterations++ >= MAX_ITERATIONS) {
|
|
3638
|
+
throw new Error(
|
|
3639
|
+
`Meteora DLMM: exponentiation loop exceeded ${MAX_ITERATIONS} iterations (activeId=${activeId})`
|
|
3640
|
+
);
|
|
3641
|
+
}
|
|
3107
3642
|
if (exp & 1n) {
|
|
3108
3643
|
result = result * b / SCALE;
|
|
3109
3644
|
}
|
|
@@ -3134,6 +3669,7 @@ function readU128LE3(dv3, offset) {
|
|
|
3134
3669
|
var CHAINLINK_MIN_SIZE = 224;
|
|
3135
3670
|
var MAX_DECIMALS = 18;
|
|
3136
3671
|
var CHAINLINK_DECIMALS_OFFSET = 138;
|
|
3672
|
+
var CHAINLINK_TIMESTAMP_OFFSET = 168;
|
|
3137
3673
|
var CHAINLINK_ANSWER_OFFSET = 216;
|
|
3138
3674
|
function readU82(data, off) {
|
|
3139
3675
|
return data[off];
|
|
@@ -3141,7 +3677,7 @@ function readU82(data, off) {
|
|
|
3141
3677
|
function readBigInt64LE(data, off) {
|
|
3142
3678
|
return new DataView(data.buffer, data.byteOffset, data.byteLength).getBigInt64(off, true);
|
|
3143
3679
|
}
|
|
3144
|
-
function parseChainlinkPrice(data) {
|
|
3680
|
+
function parseChainlinkPrice(data, options) {
|
|
3145
3681
|
if (data.length < CHAINLINK_MIN_SIZE) {
|
|
3146
3682
|
throw new Error(
|
|
3147
3683
|
`Oracle account data too small: ${data.length} bytes (need at least ${CHAINLINK_MIN_SIZE})`
|
|
@@ -3159,7 +3695,18 @@ function parseChainlinkPrice(data) {
|
|
|
3159
3695
|
`Oracle price is non-positive: ${price}`
|
|
3160
3696
|
);
|
|
3161
3697
|
}
|
|
3162
|
-
|
|
3698
|
+
const updatedAtBig = readBigInt64LE(data, CHAINLINK_TIMESTAMP_OFFSET);
|
|
3699
|
+
const updatedAt = Number(updatedAtBig);
|
|
3700
|
+
if (options?.maxStalenessSeconds !== void 0 && updatedAt > 0) {
|
|
3701
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
3702
|
+
const age = now - updatedAt;
|
|
3703
|
+
if (age > options.maxStalenessSeconds) {
|
|
3704
|
+
throw new Error(
|
|
3705
|
+
`Oracle price is stale: last updated ${age}s ago (max ${options.maxStalenessSeconds}s)`
|
|
3706
|
+
);
|
|
3707
|
+
}
|
|
3708
|
+
}
|
|
3709
|
+
return { price, decimals, updatedAt: updatedAt > 0 ? updatedAt : void 0 };
|
|
3163
3710
|
}
|
|
3164
3711
|
function isValidChainlinkOracle(data) {
|
|
3165
3712
|
try {
|
|
@@ -3171,15 +3718,19 @@ function isValidChainlinkOracle(data) {
|
|
|
3171
3718
|
}
|
|
3172
3719
|
|
|
3173
3720
|
// src/solana/token-program.ts
|
|
3174
|
-
import { PublicKey as
|
|
3721
|
+
import { PublicKey as PublicKey8 } from "@solana/web3.js";
|
|
3175
3722
|
import { TOKEN_PROGRAM_ID as TOKEN_PROGRAM_ID3 } from "@solana/spl-token";
|
|
3176
|
-
var TOKEN_2022_PROGRAM_ID = new
|
|
3723
|
+
var TOKEN_2022_PROGRAM_ID = new PublicKey8(
|
|
3177
3724
|
"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"
|
|
3178
3725
|
);
|
|
3179
3726
|
async function detectTokenProgram(connection, mint) {
|
|
3180
3727
|
const info = await connection.getAccountInfo(mint);
|
|
3181
3728
|
if (!info) throw new Error(`Mint account not found: ${mint.toBase58()}`);
|
|
3182
|
-
|
|
3729
|
+
if (info.owner.equals(TOKEN_PROGRAM_ID3)) return TOKEN_PROGRAM_ID3;
|
|
3730
|
+
if (info.owner.equals(TOKEN_2022_PROGRAM_ID)) return TOKEN_2022_PROGRAM_ID;
|
|
3731
|
+
throw new Error(
|
|
3732
|
+
`Mint ${mint.toBase58()} is owned by ${info.owner.toBase58()}, which is neither TOKEN_PROGRAM_ID nor TOKEN_2022_PROGRAM_ID`
|
|
3733
|
+
);
|
|
3183
3734
|
}
|
|
3184
3735
|
function isToken2022(tokenProgramId) {
|
|
3185
3736
|
return tokenProgramId.equals(TOKEN_2022_PROGRAM_ID);
|
|
@@ -3189,11 +3740,11 @@ function isStandardToken(tokenProgramId) {
|
|
|
3189
3740
|
}
|
|
3190
3741
|
|
|
3191
3742
|
// src/solana/stake.ts
|
|
3192
|
-
import { PublicKey as
|
|
3743
|
+
import { PublicKey as PublicKey10, SystemProgram as SystemProgram2, SYSVAR_RENT_PUBKEY as SYSVAR_RENT_PUBKEY2, SYSVAR_CLOCK_PUBKEY as SYSVAR_CLOCK_PUBKEY2 } from "@solana/web3.js";
|
|
3193
3744
|
import { TOKEN_PROGRAM_ID as TOKEN_PROGRAM_ID4 } from "@solana/spl-token";
|
|
3194
3745
|
|
|
3195
3746
|
// src/config/program-ids.ts
|
|
3196
|
-
import { PublicKey as
|
|
3747
|
+
import { PublicKey as PublicKey9 } from "@solana/web3.js";
|
|
3197
3748
|
function safeEnv(key) {
|
|
3198
3749
|
try {
|
|
3199
3750
|
return typeof process !== "undefined" && process?.env ? process.env[key] : void 0;
|
|
@@ -3212,25 +3763,29 @@ var PROGRAM_IDS = {
|
|
|
3212
3763
|
}
|
|
3213
3764
|
};
|
|
3214
3765
|
function getProgramId(network) {
|
|
3215
|
-
|
|
3216
|
-
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3766
|
+
if (!network) {
|
|
3767
|
+
const override = safeEnv("PROGRAM_ID");
|
|
3768
|
+
if (override) {
|
|
3769
|
+
console.warn(
|
|
3770
|
+
`[percolator-sdk] PROGRAM_ID env override active: ${override} \u2014 ensure this points to a trusted program`
|
|
3771
|
+
);
|
|
3772
|
+
return new PublicKey9(override);
|
|
3773
|
+
}
|
|
3221
3774
|
}
|
|
3222
3775
|
const detectedNetwork = getCurrentNetwork();
|
|
3223
3776
|
const targetNetwork = network ?? detectedNetwork;
|
|
3224
3777
|
const programId = PROGRAM_IDS[targetNetwork].percolator;
|
|
3225
|
-
return new
|
|
3778
|
+
return new PublicKey9(programId);
|
|
3226
3779
|
}
|
|
3227
3780
|
function getMatcherProgramId(network) {
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3781
|
+
if (!network) {
|
|
3782
|
+
const override = safeEnv("MATCHER_PROGRAM_ID");
|
|
3783
|
+
if (override) {
|
|
3784
|
+
console.warn(
|
|
3785
|
+
`[percolator-sdk] MATCHER_PROGRAM_ID env override active: ${override} \u2014 ensure this points to a trusted program`
|
|
3786
|
+
);
|
|
3787
|
+
return new PublicKey9(override);
|
|
3788
|
+
}
|
|
3234
3789
|
}
|
|
3235
3790
|
const detectedNetwork = getCurrentNetwork();
|
|
3236
3791
|
const targetNetwork = network ?? detectedNetwork;
|
|
@@ -3238,7 +3793,7 @@ function getMatcherProgramId(network) {
|
|
|
3238
3793
|
if (!programId) {
|
|
3239
3794
|
throw new Error(`Matcher program not deployed on ${targetNetwork}`);
|
|
3240
3795
|
}
|
|
3241
|
-
return new
|
|
3796
|
+
return new PublicKey9(programId);
|
|
3242
3797
|
}
|
|
3243
3798
|
function getCurrentNetwork() {
|
|
3244
3799
|
const network = safeEnv("NETWORK")?.toLowerCase();
|
|
@@ -3260,7 +3815,7 @@ function getStakeProgramId(network) {
|
|
|
3260
3815
|
console.warn(
|
|
3261
3816
|
`[percolator-sdk] STAKE_PROGRAM_ID env override active: ${override} \u2014 ensure this points to a trusted program`
|
|
3262
3817
|
);
|
|
3263
|
-
return new
|
|
3818
|
+
return new PublicKey10(override);
|
|
3264
3819
|
}
|
|
3265
3820
|
const detectedNetwork = network ?? (() => {
|
|
3266
3821
|
const n = safeEnv("NEXT_PUBLIC_DEFAULT_NETWORK")?.toLowerCase() ?? safeEnv("NETWORK")?.toLowerCase() ?? "";
|
|
@@ -3272,9 +3827,9 @@ function getStakeProgramId(network) {
|
|
|
3272
3827
|
`Stake program not deployed on ${detectedNetwork}. Set STAKE_PROGRAM_ID env var or wait for DevOps to deploy and update STAKE_PROGRAM_IDS.mainnet.`
|
|
3273
3828
|
);
|
|
3274
3829
|
}
|
|
3275
|
-
return new
|
|
3830
|
+
return new PublicKey10(id);
|
|
3276
3831
|
}
|
|
3277
|
-
var STAKE_PROGRAM_ID = new
|
|
3832
|
+
var STAKE_PROGRAM_ID = new PublicKey10(STAKE_PROGRAM_IDS.devnet);
|
|
3278
3833
|
var STAKE_IX = {
|
|
3279
3834
|
InitPool: 0,
|
|
3280
3835
|
Deposit: 1,
|
|
@@ -3301,19 +3856,19 @@ var STAKE_IX = {
|
|
|
3301
3856
|
};
|
|
3302
3857
|
var TEXT = new TextEncoder();
|
|
3303
3858
|
function deriveStakePool(slab, programId) {
|
|
3304
|
-
return
|
|
3859
|
+
return PublicKey10.findProgramAddressSync(
|
|
3305
3860
|
[TEXT.encode("stake_pool"), slab.toBytes()],
|
|
3306
3861
|
programId ?? getStakeProgramId()
|
|
3307
3862
|
);
|
|
3308
3863
|
}
|
|
3309
3864
|
function deriveStakeVaultAuth(pool, programId) {
|
|
3310
|
-
return
|
|
3865
|
+
return PublicKey10.findProgramAddressSync(
|
|
3311
3866
|
[TEXT.encode("vault_auth"), pool.toBytes()],
|
|
3312
3867
|
programId ?? getStakeProgramId()
|
|
3313
3868
|
);
|
|
3314
3869
|
}
|
|
3315
3870
|
function deriveDepositPda(pool, user, programId) {
|
|
3316
|
-
return
|
|
3871
|
+
return PublicKey10.findProgramAddressSync(
|
|
3317
3872
|
[TEXT.encode("deposit"), pool.toBytes(), user.toBytes()],
|
|
3318
3873
|
programId ?? getStakeProgramId()
|
|
3319
3874
|
);
|
|
@@ -3335,6 +3890,9 @@ function readU16LE3(data, off) {
|
|
|
3335
3890
|
);
|
|
3336
3891
|
}
|
|
3337
3892
|
function u64Le(v) {
|
|
3893
|
+
if (typeof v === "number" && !Number.isSafeInteger(v)) {
|
|
3894
|
+
throw new Error(`u64Le: number ${v} exceeds Number.MAX_SAFE_INTEGER \u2014 use BigInt`);
|
|
3895
|
+
}
|
|
3338
3896
|
const big = BigInt(v);
|
|
3339
3897
|
if (big < 0n) throw new Error(`u64Le: value must be non-negative, got ${big}`);
|
|
3340
3898
|
if (big > 0xFFFFFFFFFFFFFFFFn) throw new Error(`u64Le: value exceeds u64 max`);
|
|
@@ -3343,6 +3901,9 @@ function u64Le(v) {
|
|
|
3343
3901
|
return arr;
|
|
3344
3902
|
}
|
|
3345
3903
|
function u128Le(v) {
|
|
3904
|
+
if (typeof v === "number" && !Number.isSafeInteger(v)) {
|
|
3905
|
+
throw new Error(`u128Le: number ${v} exceeds Number.MAX_SAFE_INTEGER \u2014 use BigInt`);
|
|
3906
|
+
}
|
|
3346
3907
|
const big = BigInt(v);
|
|
3347
3908
|
if (big < 0n) throw new Error(`u128Le: value must be non-negative, got ${big}`);
|
|
3348
3909
|
if (big > (1n << 128n) - 1n) throw new Error(`u128Le: value exceeds u128 max`);
|
|
@@ -3353,7 +3914,7 @@ function u128Le(v) {
|
|
|
3353
3914
|
return arr;
|
|
3354
3915
|
}
|
|
3355
3916
|
function u16Le(v) {
|
|
3356
|
-
if (v < 0 || v > 65535) throw new Error(`u16Le: value
|
|
3917
|
+
if (!Number.isInteger(v) || v < 0 || v > 65535) throw new Error(`u16Le: value must be integer in range 0..65535, got ${v}`);
|
|
3357
3918
|
const arr = new Uint8Array(2);
|
|
3358
3919
|
new DataView(arr.buffer).setUint16(0, v, true);
|
|
3359
3920
|
return arr;
|
|
@@ -3464,15 +4025,15 @@ function decodeStakePool(data) {
|
|
|
3464
4025
|
const adminTransferred = bytes[off] === 1;
|
|
3465
4026
|
off += 1;
|
|
3466
4027
|
off += 4;
|
|
3467
|
-
const slab = new
|
|
4028
|
+
const slab = new PublicKey10(bytes.subarray(off, off + 32));
|
|
3468
4029
|
off += 32;
|
|
3469
|
-
const admin = new
|
|
4030
|
+
const admin = new PublicKey10(bytes.subarray(off, off + 32));
|
|
3470
4031
|
off += 32;
|
|
3471
|
-
const collateralMint = new
|
|
4032
|
+
const collateralMint = new PublicKey10(bytes.subarray(off, off + 32));
|
|
3472
4033
|
off += 32;
|
|
3473
|
-
const lpMint = new
|
|
4034
|
+
const lpMint = new PublicKey10(bytes.subarray(off, off + 32));
|
|
3474
4035
|
off += 32;
|
|
3475
|
-
const vault = new
|
|
4036
|
+
const vault = new PublicKey10(bytes.subarray(off, off + 32));
|
|
3476
4037
|
off += 32;
|
|
3477
4038
|
const totalDeposited = readU64LE4(bytes, off);
|
|
3478
4039
|
off += 8;
|
|
@@ -3488,7 +4049,7 @@ function decodeStakePool(data) {
|
|
|
3488
4049
|
off += 8;
|
|
3489
4050
|
const totalWithdrawn = readU64LE4(bytes, off);
|
|
3490
4051
|
off += 8;
|
|
3491
|
-
const percolatorProgram = new
|
|
4052
|
+
const percolatorProgram = new PublicKey10(bytes.subarray(off, off + 32));
|
|
3492
4053
|
off += 32;
|
|
3493
4054
|
const totalFeesEarned = readU64LE4(bytes, off);
|
|
3494
4055
|
off += 8;
|
|
@@ -3608,7 +4169,9 @@ function computePnlPct(pnl, capital) {
|
|
|
3608
4169
|
}
|
|
3609
4170
|
function isAdlTriggered(slabData) {
|
|
3610
4171
|
const layout = detectSlabLayout(slabData.length);
|
|
3611
|
-
if (!layout)
|
|
4172
|
+
if (!layout) {
|
|
4173
|
+
return false;
|
|
4174
|
+
}
|
|
3612
4175
|
try {
|
|
3613
4176
|
const engine = parseEngine(slabData);
|
|
3614
4177
|
if (engine.pnlPosTot === 0n) return false;
|
|
@@ -3651,6 +4214,14 @@ function rankAdlPositions(slabData) {
|
|
|
3651
4214
|
if (account.kind !== 0 /* User */) continue;
|
|
3652
4215
|
if (account.positionSize === 0n) continue;
|
|
3653
4216
|
const side = account.positionSize > 0n ? "long" : "short";
|
|
4217
|
+
if (side === "long" && account.positionSize <= 0n) {
|
|
4218
|
+
console.warn(`[fetchAdlRankedPositions] account idx=${idx}: side=long but positionSize=${account.positionSize}`);
|
|
4219
|
+
continue;
|
|
4220
|
+
}
|
|
4221
|
+
if (side === "short" && account.positionSize >= 0n) {
|
|
4222
|
+
console.warn(`[fetchAdlRankedPositions] account idx=${idx}: side=short but positionSize=${account.positionSize}`);
|
|
4223
|
+
continue;
|
|
4224
|
+
}
|
|
3654
4225
|
const pnlPct = computePnlPct(account.pnl, account.capital);
|
|
3655
4226
|
positions.push({
|
|
3656
4227
|
idx,
|
|
@@ -3724,7 +4295,11 @@ function parseAdlEvent(logs) {
|
|
|
3724
4295
|
}
|
|
3725
4296
|
if (tag !== ADL_EVENT_TAG) continue;
|
|
3726
4297
|
try {
|
|
3727
|
-
const
|
|
4298
|
+
const targetIdxBig = BigInt(match[2]);
|
|
4299
|
+
if (targetIdxBig < 0n || targetIdxBig > 65535n) {
|
|
4300
|
+
continue;
|
|
4301
|
+
}
|
|
4302
|
+
const targetIdx = Number(targetIdxBig);
|
|
3728
4303
|
const price = BigInt(match[3]);
|
|
3729
4304
|
const closedLo = BigInt(match[4]);
|
|
3730
4305
|
const closedHi = BigInt(match[5]);
|
|
@@ -3752,9 +4327,387 @@ async function fetchAdlRankings(apiBase, slab, fetchFn = fetch) {
|
|
|
3752
4327
|
);
|
|
3753
4328
|
}
|
|
3754
4329
|
const json = await res.json();
|
|
4330
|
+
if (typeof json !== "object" || json === null) {
|
|
4331
|
+
throw new Error("fetchAdlRankings: API returned non-object response");
|
|
4332
|
+
}
|
|
4333
|
+
const obj = json;
|
|
4334
|
+
if (!Array.isArray(obj.rankings)) {
|
|
4335
|
+
throw new Error("fetchAdlRankings: API response missing rankings array");
|
|
4336
|
+
}
|
|
4337
|
+
for (const entry of obj.rankings) {
|
|
4338
|
+
if (typeof entry !== "object" || entry === null) {
|
|
4339
|
+
throw new Error("fetchAdlRankings: invalid ranking entry (not an object)");
|
|
4340
|
+
}
|
|
4341
|
+
const r = entry;
|
|
4342
|
+
if (typeof r.idx !== "number" || !Number.isInteger(r.idx) || r.idx < 0) {
|
|
4343
|
+
throw new Error(`fetchAdlRankings: invalid ranking idx: ${r.idx}`);
|
|
4344
|
+
}
|
|
4345
|
+
}
|
|
3755
4346
|
return json;
|
|
3756
4347
|
}
|
|
3757
4348
|
|
|
4349
|
+
// src/solana/rpc-pool.ts
|
|
4350
|
+
import {
|
|
4351
|
+
Connection as Connection5
|
|
4352
|
+
} from "@solana/web3.js";
|
|
4353
|
+
async function checkRpcHealth(endpoint, timeoutMs = 5e3) {
|
|
4354
|
+
const conn = new Connection5(endpoint, { commitment: "processed" });
|
|
4355
|
+
const start = performance.now();
|
|
4356
|
+
const timeout = rejectAfter(timeoutMs, `Health probe timed out after ${timeoutMs}ms`);
|
|
4357
|
+
try {
|
|
4358
|
+
const slot = await Promise.race([
|
|
4359
|
+
conn.getSlot("processed"),
|
|
4360
|
+
timeout.promise
|
|
4361
|
+
]);
|
|
4362
|
+
const latencyMs = Math.round(performance.now() - start);
|
|
4363
|
+
return { endpoint, healthy: true, latencyMs, slot };
|
|
4364
|
+
} catch (err) {
|
|
4365
|
+
const latencyMs = Math.round(performance.now() - start);
|
|
4366
|
+
return {
|
|
4367
|
+
endpoint,
|
|
4368
|
+
healthy: false,
|
|
4369
|
+
latencyMs,
|
|
4370
|
+
slot: 0,
|
|
4371
|
+
error: err instanceof Error ? err.message : String(err)
|
|
4372
|
+
};
|
|
4373
|
+
} finally {
|
|
4374
|
+
timeout.cancel();
|
|
4375
|
+
}
|
|
4376
|
+
}
|
|
4377
|
+
function resolveRetryConfig(cfg) {
|
|
4378
|
+
if (cfg === false) return null;
|
|
4379
|
+
const c = cfg ?? {};
|
|
4380
|
+
return {
|
|
4381
|
+
maxRetries: c.maxRetries ?? 3,
|
|
4382
|
+
baseDelayMs: c.baseDelayMs ?? 500,
|
|
4383
|
+
maxDelayMs: c.maxDelayMs ?? 1e4,
|
|
4384
|
+
jitterFactor: Math.max(0, Math.min(1, c.jitterFactor ?? 0.25)),
|
|
4385
|
+
retryableStatusCodes: c.retryableStatusCodes ?? [429, 502, 503, 504]
|
|
4386
|
+
};
|
|
4387
|
+
}
|
|
4388
|
+
function normalizeEndpoint(ep) {
|
|
4389
|
+
if (typeof ep === "string") return { url: ep };
|
|
4390
|
+
return ep;
|
|
4391
|
+
}
|
|
4392
|
+
function endpointLabel(ep) {
|
|
4393
|
+
if (ep.label) return ep.label;
|
|
4394
|
+
try {
|
|
4395
|
+
return new URL(ep.url).hostname;
|
|
4396
|
+
} catch {
|
|
4397
|
+
return ep.url.slice(0, 40);
|
|
4398
|
+
}
|
|
4399
|
+
}
|
|
4400
|
+
function isRetryable(err, codes) {
|
|
4401
|
+
if (!err) return false;
|
|
4402
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
4403
|
+
for (const code of codes) {
|
|
4404
|
+
if (msg.includes(String(code))) return true;
|
|
4405
|
+
}
|
|
4406
|
+
if (msg.toLowerCase().includes("rate limit") || msg.toLowerCase().includes("too many requests") || msg.toLowerCase().includes("econnreset") || msg.toLowerCase().includes("econnrefused") || msg.toLowerCase().includes("socket hang up") || msg.toLowerCase().includes("network") || msg.toLowerCase().includes("timeout") || msg.toLowerCase().includes("abort")) {
|
|
4407
|
+
return true;
|
|
4408
|
+
}
|
|
4409
|
+
return false;
|
|
4410
|
+
}
|
|
4411
|
+
function computeDelay(attempt, config) {
|
|
4412
|
+
const raw = Math.min(
|
|
4413
|
+
config.baseDelayMs * Math.pow(2, attempt),
|
|
4414
|
+
config.maxDelayMs
|
|
4415
|
+
);
|
|
4416
|
+
const jitter = Math.floor(Math.random() * raw * config.jitterFactor);
|
|
4417
|
+
return raw + jitter;
|
|
4418
|
+
}
|
|
4419
|
+
function rejectAfter(ms, message) {
|
|
4420
|
+
let timer;
|
|
4421
|
+
const promise = new Promise((_, reject) => {
|
|
4422
|
+
timer = setTimeout(() => reject(new Error(message)), ms);
|
|
4423
|
+
});
|
|
4424
|
+
return { promise, cancel: () => clearTimeout(timer) };
|
|
4425
|
+
}
|
|
4426
|
+
function sleep(ms) {
|
|
4427
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
4428
|
+
}
|
|
4429
|
+
function redactUrl(raw) {
|
|
4430
|
+
try {
|
|
4431
|
+
const u = new URL(raw);
|
|
4432
|
+
const sensitive = /^(api[-_]?key|access[-_]?token|auth[-_]?token|token|secret|key|password|bearer|credential|jwt)$/i;
|
|
4433
|
+
for (const k of [...u.searchParams.keys()]) {
|
|
4434
|
+
if (sensitive.test(k)) {
|
|
4435
|
+
u.searchParams.set(k, "***");
|
|
4436
|
+
}
|
|
4437
|
+
}
|
|
4438
|
+
return u.toString();
|
|
4439
|
+
} catch {
|
|
4440
|
+
return raw;
|
|
4441
|
+
}
|
|
4442
|
+
}
|
|
4443
|
+
var RpcPool = class _RpcPool {
|
|
4444
|
+
endpoints;
|
|
4445
|
+
strategy;
|
|
4446
|
+
retryConfig;
|
|
4447
|
+
requestTimeoutMs;
|
|
4448
|
+
verbose;
|
|
4449
|
+
/** Round-robin index tracker. */
|
|
4450
|
+
rrIndex = 0;
|
|
4451
|
+
/** Consecutive failure threshold before marking an endpoint unhealthy. */
|
|
4452
|
+
static UNHEALTHY_THRESHOLD = 3;
|
|
4453
|
+
/** Minimum endpoints before auto-recovery is attempted. */
|
|
4454
|
+
static MIN_HEALTHY = 1;
|
|
4455
|
+
constructor(config) {
|
|
4456
|
+
if (!config.endpoints || config.endpoints.length === 0) {
|
|
4457
|
+
throw new Error("RpcPool: at least one endpoint is required");
|
|
4458
|
+
}
|
|
4459
|
+
this.strategy = config.strategy ?? "failover";
|
|
4460
|
+
this.retryConfig = resolveRetryConfig(config.retry);
|
|
4461
|
+
this.requestTimeoutMs = config.requestTimeoutMs ?? 3e4;
|
|
4462
|
+
this.verbose = config.verbose ?? true;
|
|
4463
|
+
const commitment = config.commitment ?? "confirmed";
|
|
4464
|
+
this.endpoints = config.endpoints.map((raw) => {
|
|
4465
|
+
const ep = normalizeEndpoint(raw);
|
|
4466
|
+
const connConfig = {
|
|
4467
|
+
commitment,
|
|
4468
|
+
...ep.connectionConfig
|
|
4469
|
+
};
|
|
4470
|
+
return {
|
|
4471
|
+
config: ep,
|
|
4472
|
+
connection: new Connection5(ep.url, connConfig),
|
|
4473
|
+
label: endpointLabel(ep),
|
|
4474
|
+
weight: Math.max(1, ep.weight ?? 1),
|
|
4475
|
+
failures: 0,
|
|
4476
|
+
healthy: true,
|
|
4477
|
+
lastLatencyMs: -1
|
|
4478
|
+
};
|
|
4479
|
+
});
|
|
4480
|
+
}
|
|
4481
|
+
// -----------------------------------------------------------------------
|
|
4482
|
+
// Public API
|
|
4483
|
+
// -----------------------------------------------------------------------
|
|
4484
|
+
/**
|
|
4485
|
+
* Execute a function against a pooled connection with automatic retry
|
|
4486
|
+
* and failover.
|
|
4487
|
+
*
|
|
4488
|
+
* @param fn - Async function that receives a `Connection` and returns a result.
|
|
4489
|
+
* @returns The result of `fn`.
|
|
4490
|
+
* @throws The last error if all retries and failovers are exhausted.
|
|
4491
|
+
*
|
|
4492
|
+
* @example
|
|
4493
|
+
* ```ts
|
|
4494
|
+
* const balance = await pool.call(c => c.getBalance(pubkey));
|
|
4495
|
+
* const markets = await pool.call(c => discoverMarkets(c, programId, opts));
|
|
4496
|
+
* ```
|
|
4497
|
+
*/
|
|
4498
|
+
async call(fn) {
|
|
4499
|
+
const maxAttempts = this.retryConfig ? this.retryConfig.maxRetries + 1 : 1;
|
|
4500
|
+
let lastError;
|
|
4501
|
+
const triedEndpoints = /* @__PURE__ */ new Set();
|
|
4502
|
+
const maxTotalIterations = maxAttempts + this.endpoints.length;
|
|
4503
|
+
let totalIterations = 0;
|
|
4504
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
4505
|
+
if (++totalIterations > maxTotalIterations) break;
|
|
4506
|
+
const epIdx = this.selectEndpoint(triedEndpoints);
|
|
4507
|
+
if (epIdx === -1) {
|
|
4508
|
+
break;
|
|
4509
|
+
}
|
|
4510
|
+
const ep = this.endpoints[epIdx];
|
|
4511
|
+
const timeout = rejectAfter(this.requestTimeoutMs, `RPC request timed out after ${this.requestTimeoutMs}ms (${ep.label})`);
|
|
4512
|
+
try {
|
|
4513
|
+
const result = await Promise.race([
|
|
4514
|
+
fn(ep.connection),
|
|
4515
|
+
timeout.promise
|
|
4516
|
+
]);
|
|
4517
|
+
timeout.cancel();
|
|
4518
|
+
ep.failures = 0;
|
|
4519
|
+
ep.healthy = true;
|
|
4520
|
+
return result;
|
|
4521
|
+
} catch (err) {
|
|
4522
|
+
timeout.cancel();
|
|
4523
|
+
lastError = err;
|
|
4524
|
+
ep.failures++;
|
|
4525
|
+
if (ep.failures >= _RpcPool.UNHEALTHY_THRESHOLD) {
|
|
4526
|
+
ep.healthy = false;
|
|
4527
|
+
if (this.verbose) {
|
|
4528
|
+
console.warn(
|
|
4529
|
+
`[RpcPool] Endpoint ${ep.label} marked unhealthy after ${ep.failures} consecutive failures`
|
|
4530
|
+
);
|
|
4531
|
+
}
|
|
4532
|
+
}
|
|
4533
|
+
const retryable = this.retryConfig ? isRetryable(err, this.retryConfig.retryableStatusCodes) : false;
|
|
4534
|
+
if (!retryable) {
|
|
4535
|
+
if (this.strategy === "failover" && this.endpoints.length > 1) {
|
|
4536
|
+
triedEndpoints.add(epIdx);
|
|
4537
|
+
attempt--;
|
|
4538
|
+
if (triedEndpoints.size >= this.endpoints.length) break;
|
|
4539
|
+
continue;
|
|
4540
|
+
}
|
|
4541
|
+
throw err;
|
|
4542
|
+
}
|
|
4543
|
+
if (this.verbose) {
|
|
4544
|
+
console.warn(
|
|
4545
|
+
`[RpcPool] Retryable error on ${ep.label} (attempt ${attempt + 1}/${maxAttempts}):`,
|
|
4546
|
+
err instanceof Error ? err.message : err
|
|
4547
|
+
);
|
|
4548
|
+
}
|
|
4549
|
+
if (this.strategy === "failover" && this.endpoints.length > 1) {
|
|
4550
|
+
triedEndpoints.add(epIdx);
|
|
4551
|
+
}
|
|
4552
|
+
if (attempt < maxAttempts - 1 && this.retryConfig) {
|
|
4553
|
+
const delay = computeDelay(attempt, this.retryConfig);
|
|
4554
|
+
await sleep(delay);
|
|
4555
|
+
}
|
|
4556
|
+
}
|
|
4557
|
+
}
|
|
4558
|
+
this.maybeRecoverEndpoints();
|
|
4559
|
+
throw lastError ?? new Error("RpcPool: all endpoints exhausted");
|
|
4560
|
+
}
|
|
4561
|
+
/**
|
|
4562
|
+
* Get a raw `Connection` from the current preferred endpoint.
|
|
4563
|
+
* Useful when you need to pass a Connection to external code.
|
|
4564
|
+
*
|
|
4565
|
+
* NOTE: This bypasses retry and failover logic. Prefer `call()`.
|
|
4566
|
+
*
|
|
4567
|
+
* @returns Solana Connection from the current preferred endpoint.
|
|
4568
|
+
*
|
|
4569
|
+
* @example
|
|
4570
|
+
* ```ts
|
|
4571
|
+
* const conn = pool.getConnection();
|
|
4572
|
+
* const balance = await conn.getBalance(pubkey);
|
|
4573
|
+
* ```
|
|
4574
|
+
*/
|
|
4575
|
+
getConnection() {
|
|
4576
|
+
const idx = this.selectEndpoint();
|
|
4577
|
+
if (idx === -1) {
|
|
4578
|
+
this.maybeRecoverEndpoints();
|
|
4579
|
+
return this.endpoints[0].connection;
|
|
4580
|
+
}
|
|
4581
|
+
return this.endpoints[idx].connection;
|
|
4582
|
+
}
|
|
4583
|
+
/**
|
|
4584
|
+
* Run a health check against all endpoints in the pool.
|
|
4585
|
+
*
|
|
4586
|
+
* @param timeoutMs - Per-endpoint probe timeout (default: 5000)
|
|
4587
|
+
* @returns Array of health results, one per endpoint.
|
|
4588
|
+
*
|
|
4589
|
+
* @example
|
|
4590
|
+
* ```ts
|
|
4591
|
+
* const results = await pool.healthCheck();
|
|
4592
|
+
* for (const r of results) {
|
|
4593
|
+
* console.log(`${r.endpoint}: ${r.healthy ? 'UP' : 'DOWN'} (${r.latencyMs}ms, slot ${r.slot})`);
|
|
4594
|
+
* }
|
|
4595
|
+
* ```
|
|
4596
|
+
*/
|
|
4597
|
+
async healthCheck(timeoutMs = 5e3) {
|
|
4598
|
+
const results = await Promise.all(
|
|
4599
|
+
this.endpoints.map(async (ep) => {
|
|
4600
|
+
const result = await checkRpcHealth(ep.config.url, timeoutMs);
|
|
4601
|
+
ep.lastLatencyMs = result.latencyMs;
|
|
4602
|
+
ep.healthy = result.healthy;
|
|
4603
|
+
if (result.healthy) ep.failures = 0;
|
|
4604
|
+
result.endpoint = redactUrl(result.endpoint);
|
|
4605
|
+
return result;
|
|
4606
|
+
})
|
|
4607
|
+
);
|
|
4608
|
+
return results;
|
|
4609
|
+
}
|
|
4610
|
+
/**
|
|
4611
|
+
* Get the number of endpoints in the pool.
|
|
4612
|
+
*/
|
|
4613
|
+
get size() {
|
|
4614
|
+
return this.endpoints.length;
|
|
4615
|
+
}
|
|
4616
|
+
/**
|
|
4617
|
+
* Get the number of currently healthy endpoints.
|
|
4618
|
+
*/
|
|
4619
|
+
get healthyCount() {
|
|
4620
|
+
return this.endpoints.filter((ep) => ep.healthy).length;
|
|
4621
|
+
}
|
|
4622
|
+
/**
|
|
4623
|
+
* Get endpoint labels and their current status.
|
|
4624
|
+
*
|
|
4625
|
+
* @returns Array of `{ label, url, healthy, failures, lastLatencyMs }`.
|
|
4626
|
+
*/
|
|
4627
|
+
status() {
|
|
4628
|
+
return this.endpoints.map((ep) => ({
|
|
4629
|
+
label: ep.label,
|
|
4630
|
+
url: redactUrl(ep.config.url),
|
|
4631
|
+
healthy: ep.healthy,
|
|
4632
|
+
failures: ep.failures,
|
|
4633
|
+
lastLatencyMs: ep.lastLatencyMs
|
|
4634
|
+
}));
|
|
4635
|
+
}
|
|
4636
|
+
// -----------------------------------------------------------------------
|
|
4637
|
+
// Internals
|
|
4638
|
+
// -----------------------------------------------------------------------
|
|
4639
|
+
/**
|
|
4640
|
+
* Select the next endpoint based on strategy.
|
|
4641
|
+
* Returns -1 if no endpoint is available.
|
|
4642
|
+
*/
|
|
4643
|
+
selectEndpoint(exclude) {
|
|
4644
|
+
const healthy = this.endpoints.map((ep, i) => ({ ep, i })).filter(({ ep, i }) => ep.healthy && !exclude?.has(i));
|
|
4645
|
+
if (healthy.length === 0) {
|
|
4646
|
+
const remaining = this.endpoints.map((_, i) => i).filter((i) => !exclude?.has(i));
|
|
4647
|
+
return remaining.length > 0 ? remaining[0] : -1;
|
|
4648
|
+
}
|
|
4649
|
+
if (this.strategy === "failover") {
|
|
4650
|
+
return healthy[0].i;
|
|
4651
|
+
}
|
|
4652
|
+
const totalWeight = healthy.reduce((sum, { ep }) => sum + ep.weight, 0);
|
|
4653
|
+
this.rrIndex = (this.rrIndex + 1) % totalWeight;
|
|
4654
|
+
let cumulative = 0;
|
|
4655
|
+
for (const { ep, i } of healthy) {
|
|
4656
|
+
cumulative += ep.weight;
|
|
4657
|
+
if (this.rrIndex < cumulative) return i;
|
|
4658
|
+
}
|
|
4659
|
+
return healthy[healthy.length - 1].i;
|
|
4660
|
+
}
|
|
4661
|
+
/**
|
|
4662
|
+
* If all endpoints are unhealthy, reset them so we at least try again.
|
|
4663
|
+
*/
|
|
4664
|
+
maybeRecoverEndpoints() {
|
|
4665
|
+
const healthyCount = this.endpoints.filter((ep) => ep.healthy).length;
|
|
4666
|
+
if (healthyCount < _RpcPool.MIN_HEALTHY) {
|
|
4667
|
+
if (this.verbose) {
|
|
4668
|
+
console.warn("[RpcPool] All endpoints unhealthy \u2014 resetting for recovery");
|
|
4669
|
+
}
|
|
4670
|
+
for (const ep of this.endpoints) {
|
|
4671
|
+
ep.healthy = true;
|
|
4672
|
+
ep.failures = 0;
|
|
4673
|
+
}
|
|
4674
|
+
}
|
|
4675
|
+
}
|
|
4676
|
+
};
|
|
4677
|
+
async function withRetry(fn, config) {
|
|
4678
|
+
const resolved = resolveRetryConfig(config) ?? {
|
|
4679
|
+
maxRetries: 3,
|
|
4680
|
+
baseDelayMs: 500,
|
|
4681
|
+
maxDelayMs: 1e4,
|
|
4682
|
+
jitterFactor: 0.25,
|
|
4683
|
+
retryableStatusCodes: [429, 502, 503, 504]
|
|
4684
|
+
};
|
|
4685
|
+
let lastError;
|
|
4686
|
+
const maxAttempts = resolved.maxRetries + 1;
|
|
4687
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
4688
|
+
try {
|
|
4689
|
+
return await fn();
|
|
4690
|
+
} catch (err) {
|
|
4691
|
+
lastError = err;
|
|
4692
|
+
if (!isRetryable(err, resolved.retryableStatusCodes)) {
|
|
4693
|
+
throw err;
|
|
4694
|
+
}
|
|
4695
|
+
if (attempt < maxAttempts - 1) {
|
|
4696
|
+
const delay = computeDelay(attempt, resolved);
|
|
4697
|
+
await sleep(delay);
|
|
4698
|
+
}
|
|
4699
|
+
}
|
|
4700
|
+
}
|
|
4701
|
+
throw lastError ?? new Error("withRetry: all attempts exhausted");
|
|
4702
|
+
}
|
|
4703
|
+
var _internal = {
|
|
4704
|
+
isRetryable,
|
|
4705
|
+
computeDelay,
|
|
4706
|
+
resolveRetryConfig,
|
|
4707
|
+
normalizeEndpoint,
|
|
4708
|
+
endpointLabel
|
|
4709
|
+
};
|
|
4710
|
+
|
|
3758
4711
|
// src/runtime/tx.ts
|
|
3759
4712
|
import {
|
|
3760
4713
|
TransactionInstruction as TransactionInstruction2,
|
|
@@ -3906,6 +4859,139 @@ function formatResult(result, jsonMode) {
|
|
|
3906
4859
|
return lines.join("\n");
|
|
3907
4860
|
}
|
|
3908
4861
|
|
|
4862
|
+
// src/runtime/lighthouse.ts
|
|
4863
|
+
import { PublicKey as PublicKey13, Transaction as Transaction2 } from "@solana/web3.js";
|
|
4864
|
+
var LIGHTHOUSE_PROGRAM_ID = new PublicKey13(
|
|
4865
|
+
"L2TExMFKdjpN9kozasaurPirfHy9P8sbXoAN1qA3S95"
|
|
4866
|
+
);
|
|
4867
|
+
var LIGHTHOUSE_PROGRAM_ID_STR2 = "L2TExMFKdjpN9kozasaurPirfHy9P8sbXoAN1qA3S95";
|
|
4868
|
+
var LIGHTHOUSE_CONSTRAINT_ADDRESS = 6400;
|
|
4869
|
+
var LIGHTHOUSE_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
4870
|
+
6e3,
|
|
4871
|
+
// InstructionMissing
|
|
4872
|
+
6001,
|
|
4873
|
+
// InstructionFallbackNotFound
|
|
4874
|
+
6002,
|
|
4875
|
+
// InstructionDidNotDeserialize
|
|
4876
|
+
6003,
|
|
4877
|
+
// InstructionDidNotSerialize
|
|
4878
|
+
6016,
|
|
4879
|
+
// IdlInstructionStub
|
|
4880
|
+
6032,
|
|
4881
|
+
// ConstraintMut
|
|
4882
|
+
6033,
|
|
4883
|
+
// ConstraintHasOne
|
|
4884
|
+
6034,
|
|
4885
|
+
// ConstraintSigner
|
|
4886
|
+
6035,
|
|
4887
|
+
// ConstraintRaw
|
|
4888
|
+
6036,
|
|
4889
|
+
// ConstraintOwner
|
|
4890
|
+
6037,
|
|
4891
|
+
// ConstraintRentExempt
|
|
4892
|
+
6038,
|
|
4893
|
+
// ConstraintSeeds
|
|
4894
|
+
6039,
|
|
4895
|
+
// ConstraintExecutable
|
|
4896
|
+
6040,
|
|
4897
|
+
// ConstraintState
|
|
4898
|
+
6041,
|
|
4899
|
+
// ConstraintAssociated
|
|
4900
|
+
6042,
|
|
4901
|
+
// ConstraintAssociatedInit
|
|
4902
|
+
6043,
|
|
4903
|
+
// ConstraintClose
|
|
4904
|
+
6400
|
|
4905
|
+
// ConstraintAddress (the one we hit most often)
|
|
4906
|
+
]);
|
|
4907
|
+
function isLighthouseInstruction(ix) {
|
|
4908
|
+
return ix.programId.equals(LIGHTHOUSE_PROGRAM_ID);
|
|
4909
|
+
}
|
|
4910
|
+
function isLighthouseError(error) {
|
|
4911
|
+
const msg = extractErrorMessage(error);
|
|
4912
|
+
if (!msg) return false;
|
|
4913
|
+
if (msg.includes(LIGHTHOUSE_PROGRAM_ID_STR2)) return true;
|
|
4914
|
+
if (/custom\s+program\s+error:\s*0x1900\b/i.test(msg)) return true;
|
|
4915
|
+
if (/"Custom"\s*:\s*6400\b/.test(msg) && /InstructionError/i.test(msg)) return true;
|
|
4916
|
+
return false;
|
|
4917
|
+
}
|
|
4918
|
+
function isLighthouseFailureInLogs(logs) {
|
|
4919
|
+
if (!Array.isArray(logs)) return false;
|
|
4920
|
+
let insideLighthouse = false;
|
|
4921
|
+
for (const line of logs) {
|
|
4922
|
+
if (typeof line !== "string") continue;
|
|
4923
|
+
if (line.includes(`Program ${LIGHTHOUSE_PROGRAM_ID_STR2} invoke`)) {
|
|
4924
|
+
insideLighthouse = true;
|
|
4925
|
+
continue;
|
|
4926
|
+
}
|
|
4927
|
+
if (line.includes(`Program ${LIGHTHOUSE_PROGRAM_ID_STR2} success`)) {
|
|
4928
|
+
insideLighthouse = false;
|
|
4929
|
+
continue;
|
|
4930
|
+
}
|
|
4931
|
+
if (insideLighthouse && /failed/i.test(line)) {
|
|
4932
|
+
return true;
|
|
4933
|
+
}
|
|
4934
|
+
if (line.includes(`Program ${LIGHTHOUSE_PROGRAM_ID_STR2} failed`)) {
|
|
4935
|
+
return true;
|
|
4936
|
+
}
|
|
4937
|
+
}
|
|
4938
|
+
return false;
|
|
4939
|
+
}
|
|
4940
|
+
function stripLighthouseInstructions(instructions, percolatorProgramId) {
|
|
4941
|
+
if (percolatorProgramId) {
|
|
4942
|
+
const hasPercolatorIx = instructions.some(
|
|
4943
|
+
(ix) => ix.programId.equals(percolatorProgramId)
|
|
4944
|
+
);
|
|
4945
|
+
if (!hasPercolatorIx) {
|
|
4946
|
+
return instructions;
|
|
4947
|
+
}
|
|
4948
|
+
}
|
|
4949
|
+
return instructions.filter((ix) => !isLighthouseInstruction(ix));
|
|
4950
|
+
}
|
|
4951
|
+
function stripLighthouseFromTransaction(transaction, percolatorProgramId) {
|
|
4952
|
+
if (percolatorProgramId) {
|
|
4953
|
+
const hasPercolatorIx = transaction.instructions.some(
|
|
4954
|
+
(ix) => ix.programId.equals(percolatorProgramId)
|
|
4955
|
+
);
|
|
4956
|
+
if (!hasPercolatorIx) return transaction;
|
|
4957
|
+
}
|
|
4958
|
+
const hasLighthouse = transaction.instructions.some(isLighthouseInstruction);
|
|
4959
|
+
if (!hasLighthouse) return transaction;
|
|
4960
|
+
const clean = new Transaction2();
|
|
4961
|
+
clean.recentBlockhash = transaction.recentBlockhash;
|
|
4962
|
+
clean.feePayer = transaction.feePayer;
|
|
4963
|
+
for (const ix of transaction.instructions) {
|
|
4964
|
+
if (!isLighthouseInstruction(ix)) {
|
|
4965
|
+
clean.add(ix);
|
|
4966
|
+
}
|
|
4967
|
+
}
|
|
4968
|
+
return clean;
|
|
4969
|
+
}
|
|
4970
|
+
function countLighthouseInstructions(ixsOrTx) {
|
|
4971
|
+
const instructions = Array.isArray(ixsOrTx) ? ixsOrTx : ixsOrTx.instructions;
|
|
4972
|
+
return instructions.filter(isLighthouseInstruction).length;
|
|
4973
|
+
}
|
|
4974
|
+
var LIGHTHOUSE_USER_MESSAGE = "Your wallet's transaction guard (Blowfish/Lighthouse) is blocking this transaction. This is a known compatibility issue \u2014 the transaction itself is valid. Try one of these workarounds:\n1. Disable transaction simulation in your wallet settings\n2. Use a wallet without Blowfish protection (e.g., Backpack, Solflare)\n3. The SDK will automatically retry without the guard";
|
|
4975
|
+
function classifyLighthouseError(error) {
|
|
4976
|
+
if (isLighthouseError(error)) {
|
|
4977
|
+
return LIGHTHOUSE_USER_MESSAGE;
|
|
4978
|
+
}
|
|
4979
|
+
return null;
|
|
4980
|
+
}
|
|
4981
|
+
function extractErrorMessage(error) {
|
|
4982
|
+
if (!error) return null;
|
|
4983
|
+
if (typeof error === "string") return error;
|
|
4984
|
+
if (error instanceof Error) return error.message;
|
|
4985
|
+
if (typeof error === "object" && "message" in error) {
|
|
4986
|
+
return String(error.message);
|
|
4987
|
+
}
|
|
4988
|
+
try {
|
|
4989
|
+
return JSON.stringify(error);
|
|
4990
|
+
} catch {
|
|
4991
|
+
return null;
|
|
4992
|
+
}
|
|
4993
|
+
}
|
|
4994
|
+
|
|
3909
4995
|
// src/math/trading.ts
|
|
3910
4996
|
function computeMarkPnl(positionSize, entryPrice, oraclePrice) {
|
|
3911
4997
|
if (positionSize === 0n || oraclePrice === 0n) return 0n;
|
|
@@ -3953,9 +5039,20 @@ function computeFeeSplit(totalFee, config) {
|
|
|
3953
5039
|
if (config.lpBps === 0n && config.protocolBps === 0n && config.creatorBps === 0n) {
|
|
3954
5040
|
return [totalFee, 0n, 0n];
|
|
3955
5041
|
}
|
|
5042
|
+
const totalBps = config.lpBps + config.protocolBps + config.creatorBps;
|
|
5043
|
+
if (totalBps !== 10000n) {
|
|
5044
|
+
throw new Error(
|
|
5045
|
+
`Fee split must equal exactly 10000 bps (100%): lpBps=${config.lpBps} + protocolBps=${config.protocolBps} + creatorBps=${config.creatorBps} = ${totalBps}`
|
|
5046
|
+
);
|
|
5047
|
+
}
|
|
3956
5048
|
const lp = totalFee * config.lpBps / 10000n;
|
|
3957
5049
|
const protocol = totalFee * config.protocolBps / 10000n;
|
|
3958
5050
|
const creator = totalFee - lp - protocol;
|
|
5051
|
+
if (creator < 0n) {
|
|
5052
|
+
throw new Error(
|
|
5053
|
+
`Internal error: creator fee is negative (${creator}). This should not happen if lpBps + protocolBps + creatorBps <= 10000.`
|
|
5054
|
+
);
|
|
5055
|
+
}
|
|
3959
5056
|
return [lp, protocol, creator];
|
|
3960
5057
|
}
|
|
3961
5058
|
function computePnlPercent(pnlTokens, capital) {
|
|
@@ -3970,8 +5067,17 @@ function computePnlPercent(pnlTokens, capital) {
|
|
|
3970
5067
|
}
|
|
3971
5068
|
function computeEstimatedEntryPrice(oracleE6, tradingFeeBps, direction) {
|
|
3972
5069
|
if (oracleE6 === 0n) return 0n;
|
|
5070
|
+
if (tradingFeeBps < 0n) {
|
|
5071
|
+
throw new Error(`computeEstimatedEntryPrice: tradingFeeBps must be non-negative, got ${tradingFeeBps}`);
|
|
5072
|
+
}
|
|
3973
5073
|
const feeImpact = oracleE6 * tradingFeeBps / 10000n;
|
|
3974
|
-
|
|
5074
|
+
const result = direction === "long" ? oracleE6 + feeImpact : oracleE6 - feeImpact;
|
|
5075
|
+
if (result <= 0n) {
|
|
5076
|
+
throw new Error(
|
|
5077
|
+
`computeEstimatedEntryPrice: result ${result} is non-positive (tradingFeeBps=${tradingFeeBps} too high for oracle=${oracleE6})`
|
|
5078
|
+
);
|
|
5079
|
+
}
|
|
5080
|
+
return result;
|
|
3975
5081
|
}
|
|
3976
5082
|
var MAX_SAFE_BIGINT = BigInt(Number.MAX_SAFE_INTEGER);
|
|
3977
5083
|
var MIN_SAFE_BIGINT = BigInt(-Number.MAX_SAFE_INTEGER);
|
|
@@ -3992,7 +5098,12 @@ function computeMaxLeverage(initialMarginBps) {
|
|
|
3992
5098
|
if (initialMarginBps <= 0n) {
|
|
3993
5099
|
throw new Error("computeMaxLeverage: initialMarginBps must be positive");
|
|
3994
5100
|
}
|
|
3995
|
-
|
|
5101
|
+
const scaledResult = 10000n * 1000000n / initialMarginBps;
|
|
5102
|
+
return Number(scaledResult) / 1e6;
|
|
5103
|
+
}
|
|
5104
|
+
function computeMaxWithdrawable(capital, pnl, reservedPnl) {
|
|
5105
|
+
const maturedPnl = pnl - reservedPnl;
|
|
5106
|
+
return capital + (maturedPnl > 0n ? maturedPnl : 0n);
|
|
3996
5107
|
}
|
|
3997
5108
|
|
|
3998
5109
|
// src/math/warmup.ts
|
|
@@ -4004,6 +5115,9 @@ function computeWarmupUnlockedCapital(totalCapital, currentSlot, warmupStartSlot
|
|
|
4004
5115
|
return totalCapital * elapsed / warmupPeriodSlots;
|
|
4005
5116
|
}
|
|
4006
5117
|
function computeWarmupLeverageCap(initialMarginBps, totalCapital, currentSlot, warmupStartSlot, warmupPeriodSlots) {
|
|
5118
|
+
if (initialMarginBps <= 0n) {
|
|
5119
|
+
throw new Error("computeWarmupLeverageCap: initialMarginBps must be positive");
|
|
5120
|
+
}
|
|
4007
5121
|
const maxLev = computeMaxLeverage(initialMarginBps);
|
|
4008
5122
|
if (warmupPeriodSlots === 0n || warmupStartSlot === 0n) return maxLev;
|
|
4009
5123
|
if (totalCapital <= 0n) return 1;
|
|
@@ -4014,7 +5128,14 @@ function computeWarmupLeverageCap(initialMarginBps, totalCapital, currentSlot, w
|
|
|
4014
5128
|
warmupPeriodSlots
|
|
4015
5129
|
);
|
|
4016
5130
|
if (unlocked <= 0n) return 1;
|
|
4017
|
-
const
|
|
5131
|
+
const scaledResult = BigInt(maxLev) * unlocked / totalCapital;
|
|
5132
|
+
if (scaledResult > BigInt(Number.MAX_SAFE_INTEGER)) {
|
|
5133
|
+
console.warn(
|
|
5134
|
+
`[computeWarmupLeverageCap] Warning: effective leverage ${scaledResult} exceeds MAX_SAFE_INTEGER, returning MAX_SAFE_INTEGER as a safety bound`
|
|
5135
|
+
);
|
|
5136
|
+
return Number.MAX_SAFE_INTEGER;
|
|
5137
|
+
}
|
|
5138
|
+
const effectiveLev = Number(scaledResult);
|
|
4018
5139
|
return Math.max(1, effectiveLev);
|
|
4019
5140
|
}
|
|
4020
5141
|
function computeWarmupMaxPositionSize(initialMarginBps, totalCapital, currentSlot, warmupStartSlot, warmupPeriodSlots) {
|
|
@@ -4027,9 +5148,40 @@ function computeWarmupMaxPositionSize(initialMarginBps, totalCapital, currentSlo
|
|
|
4027
5148
|
);
|
|
4028
5149
|
return unlocked * BigInt(maxLev);
|
|
4029
5150
|
}
|
|
5151
|
+
function computeWarmupProgress(currentSlot, warmupStartedAtSlot, warmupPeriodSlots, pnl, reservedPnl) {
|
|
5152
|
+
if (warmupPeriodSlots === 0n || warmupStartedAtSlot === 0n) {
|
|
5153
|
+
return {
|
|
5154
|
+
maturedPnl: pnl > 0n ? pnl : 0n,
|
|
5155
|
+
reservedPnl: 0n,
|
|
5156
|
+
progressBps: 10000n,
|
|
5157
|
+
// 100%
|
|
5158
|
+
slotsRemaining: 0n
|
|
5159
|
+
};
|
|
5160
|
+
}
|
|
5161
|
+
const elapsed = currentSlot >= warmupStartedAtSlot ? currentSlot - warmupStartedAtSlot : 0n;
|
|
5162
|
+
if (elapsed >= warmupPeriodSlots) {
|
|
5163
|
+
return {
|
|
5164
|
+
maturedPnl: pnl > 0n ? pnl : 0n,
|
|
5165
|
+
reservedPnl: 0n,
|
|
5166
|
+
progressBps: 10000n,
|
|
5167
|
+
// 100%
|
|
5168
|
+
slotsRemaining: 0n
|
|
5169
|
+
};
|
|
5170
|
+
}
|
|
5171
|
+
const progressBps = elapsed * 10000n / warmupPeriodSlots;
|
|
5172
|
+
const slotsRemaining = warmupPeriodSlots - elapsed;
|
|
5173
|
+
const maturedPnl = pnl > 0n ? pnl * progressBps / 10000n : 0n;
|
|
5174
|
+
const locked = reservedPnl > 0n ? reservedPnl : 0n;
|
|
5175
|
+
return {
|
|
5176
|
+
maturedPnl,
|
|
5177
|
+
reservedPnl: locked,
|
|
5178
|
+
progressBps,
|
|
5179
|
+
slotsRemaining
|
|
5180
|
+
};
|
|
5181
|
+
}
|
|
4030
5182
|
|
|
4031
5183
|
// src/validation.ts
|
|
4032
|
-
import { PublicKey as
|
|
5184
|
+
import { PublicKey as PublicKey14 } from "@solana/web3.js";
|
|
4033
5185
|
var U16_MAX = 65535;
|
|
4034
5186
|
var U64_MAX = BigInt("18446744073709551615");
|
|
4035
5187
|
var I64_MIN = BigInt("-9223372036854775808");
|
|
@@ -4059,7 +5211,7 @@ var ValidationError = class extends Error {
|
|
|
4059
5211
|
};
|
|
4060
5212
|
function validatePublicKey(value, field) {
|
|
4061
5213
|
try {
|
|
4062
|
-
return new
|
|
5214
|
+
return new PublicKey14(value);
|
|
4063
5215
|
} catch {
|
|
4064
5216
|
throw new ValidationError(
|
|
4065
5217
|
field,
|
|
@@ -4076,18 +5228,20 @@ function validateIndex(value, field) {
|
|
|
4076
5228
|
`must be <= ${U16_MAX} (u16 max), got ${t}`
|
|
4077
5229
|
);
|
|
4078
5230
|
}
|
|
5231
|
+
if (bi > BigInt(Number.MAX_SAFE_INTEGER)) {
|
|
5232
|
+
throw new ValidationError(field, `internal error: u16 value exceeds MAX_SAFE_INTEGER`);
|
|
5233
|
+
}
|
|
4079
5234
|
return Number(bi);
|
|
4080
5235
|
}
|
|
4081
5236
|
function validateAmount(value, field) {
|
|
4082
|
-
|
|
4083
|
-
|
|
4084
|
-
num = BigInt(value);
|
|
4085
|
-
} catch {
|
|
5237
|
+
const t = value.trim();
|
|
5238
|
+
if (!/^(0|[1-9]\d*)$/.test(t)) {
|
|
4086
5239
|
throw new ValidationError(
|
|
4087
5240
|
field,
|
|
4088
|
-
`"${value}" is not a valid
|
|
5241
|
+
`"${value}" is not a valid non-negative integer. Use decimal digits only.`
|
|
4089
5242
|
);
|
|
4090
5243
|
}
|
|
5244
|
+
const num = BigInt(t);
|
|
4091
5245
|
if (num < 0n) {
|
|
4092
5246
|
throw new ValidationError(field, `must be non-negative, got ${num}`);
|
|
4093
5247
|
}
|
|
@@ -4100,15 +5254,14 @@ function validateAmount(value, field) {
|
|
|
4100
5254
|
return num;
|
|
4101
5255
|
}
|
|
4102
5256
|
function validateU128(value, field) {
|
|
4103
|
-
|
|
4104
|
-
|
|
4105
|
-
num = BigInt(value);
|
|
4106
|
-
} catch {
|
|
5257
|
+
const t = value.trim();
|
|
5258
|
+
if (!/^(0|[1-9]\d*)$/.test(t)) {
|
|
4107
5259
|
throw new ValidationError(
|
|
4108
5260
|
field,
|
|
4109
|
-
`"${value}" is not a valid
|
|
5261
|
+
`"${value}" is not a valid non-negative integer. Use decimal digits only.`
|
|
4110
5262
|
);
|
|
4111
5263
|
}
|
|
5264
|
+
const num = BigInt(t);
|
|
4112
5265
|
if (num < 0n) {
|
|
4113
5266
|
throw new ValidationError(field, `must be non-negative, got ${num}`);
|
|
4114
5267
|
}
|
|
@@ -4121,15 +5274,14 @@ function validateU128(value, field) {
|
|
|
4121
5274
|
return num;
|
|
4122
5275
|
}
|
|
4123
5276
|
function validateI64(value, field) {
|
|
4124
|
-
|
|
4125
|
-
|
|
4126
|
-
num = BigInt(value);
|
|
4127
|
-
} catch {
|
|
5277
|
+
const t = value.trim();
|
|
5278
|
+
if (!/^-?(0|[1-9]\d*)$/.test(t)) {
|
|
4128
5279
|
throw new ValidationError(
|
|
4129
5280
|
field,
|
|
4130
|
-
`"${value}" is not a valid
|
|
5281
|
+
`"${value}" is not a valid integer. Use decimal digits only, with optional leading minus.`
|
|
4131
5282
|
);
|
|
4132
5283
|
}
|
|
5284
|
+
const num = BigInt(t);
|
|
4133
5285
|
if (num < I64_MIN) {
|
|
4134
5286
|
throw new ValidationError(
|
|
4135
5287
|
field,
|
|
@@ -4145,15 +5297,14 @@ function validateI64(value, field) {
|
|
|
4145
5297
|
return num;
|
|
4146
5298
|
}
|
|
4147
5299
|
function validateI128(value, field) {
|
|
4148
|
-
|
|
4149
|
-
|
|
4150
|
-
num = BigInt(value);
|
|
4151
|
-
} catch {
|
|
5300
|
+
const t = value.trim();
|
|
5301
|
+
if (!/^-?(0|[1-9]\d*)$/.test(t)) {
|
|
4152
5302
|
throw new ValidationError(
|
|
4153
5303
|
field,
|
|
4154
|
-
`"${value}" is not a valid
|
|
5304
|
+
`"${value}" is not a valid integer. Use decimal digits only, with optional leading minus.`
|
|
4155
5305
|
);
|
|
4156
5306
|
}
|
|
5307
|
+
const num = BigInt(t);
|
|
4157
5308
|
if (num < I128_MIN) {
|
|
4158
5309
|
throw new ValidationError(
|
|
4159
5310
|
field,
|
|
@@ -4177,6 +5328,9 @@ function validateBps(value, field) {
|
|
|
4177
5328
|
`must be <= 10000 (100%), got ${t}`
|
|
4178
5329
|
);
|
|
4179
5330
|
}
|
|
5331
|
+
if (bi > BigInt(Number.MAX_SAFE_INTEGER)) {
|
|
5332
|
+
throw new ValidationError(field, `internal error: bps value exceeds MAX_SAFE_INTEGER`);
|
|
5333
|
+
}
|
|
4180
5334
|
return Number(bi);
|
|
4181
5335
|
}
|
|
4182
5336
|
function validateU64(value, field) {
|
|
@@ -4191,6 +5345,9 @@ function validateU16(value, field) {
|
|
|
4191
5345
|
`must be <= ${U16_MAX} (u16 max), got ${t}`
|
|
4192
5346
|
);
|
|
4193
5347
|
}
|
|
5348
|
+
if (bi > BigInt(Number.MAX_SAFE_INTEGER)) {
|
|
5349
|
+
throw new ValidationError(field, `internal error: u16 value exceeds MAX_SAFE_INTEGER`);
|
|
5350
|
+
}
|
|
4194
5351
|
return Number(bi);
|
|
4195
5352
|
}
|
|
4196
5353
|
|
|
@@ -4241,7 +5398,9 @@ function parseDexScreenerPairs(json) {
|
|
|
4241
5398
|
else if (liquidity > 1e4) confidence = 60;
|
|
4242
5399
|
else if (liquidity > 1e3) confidence = 45;
|
|
4243
5400
|
const priceUsd = pair.priceUsd;
|
|
4244
|
-
const
|
|
5401
|
+
const rawPrice = typeof priceUsd === "string" || typeof priceUsd === "number" ? parseFloat(String(priceUsd)) : NaN;
|
|
5402
|
+
if (!Number.isFinite(rawPrice) || rawPrice <= 0) continue;
|
|
5403
|
+
const price = rawPrice;
|
|
4245
5404
|
let baseSym = "?";
|
|
4246
5405
|
let quoteSym = "?";
|
|
4247
5406
|
if (isRecord(pair.baseToken) && typeof pair.baseToken.symbol === "string") {
|
|
@@ -4272,8 +5431,8 @@ function parseJupiterMintEntry(json, mint) {
|
|
|
4272
5431
|
if (!isRecord(row)) return null;
|
|
4273
5432
|
const rawPrice = row.price;
|
|
4274
5433
|
if (rawPrice === void 0 || rawPrice === null) return null;
|
|
4275
|
-
const price = parseFloat(String(rawPrice))
|
|
4276
|
-
if (price <= 0) return null;
|
|
5434
|
+
const price = parseFloat(String(rawPrice));
|
|
5435
|
+
if (!Number.isFinite(price) || price <= 0) return null;
|
|
4277
5436
|
let mintSymbol = "?";
|
|
4278
5437
|
if (typeof row.mintSymbol === "string") mintSymbol = row.mintSymbol;
|
|
4279
5438
|
return { price, mintSymbol };
|
|
@@ -4339,10 +5498,17 @@ async function fetchDexSources(mint, signal) {
|
|
|
4339
5498
|
headers: { "User-Agent": "percolator/1.0" }
|
|
4340
5499
|
}
|
|
4341
5500
|
);
|
|
4342
|
-
if (!resp.ok)
|
|
5501
|
+
if (!resp.ok) {
|
|
5502
|
+
console.debug(`[fetchDexSources] HTTP ${resp.status} for mint ${mint}`);
|
|
5503
|
+
return [];
|
|
5504
|
+
}
|
|
4343
5505
|
const json = await resp.json();
|
|
4344
5506
|
return parseDexScreenerPairs(json);
|
|
4345
|
-
} catch {
|
|
5507
|
+
} catch (err) {
|
|
5508
|
+
console.warn(
|
|
5509
|
+
`[fetchDexSources] Error fetching DexScreener data for mint ${mint}:`,
|
|
5510
|
+
err instanceof Error ? err.message : String(err)
|
|
5511
|
+
);
|
|
4346
5512
|
return [];
|
|
4347
5513
|
}
|
|
4348
5514
|
}
|
|
@@ -4353,7 +5519,7 @@ function lookupPythSource(mint) {
|
|
|
4353
5519
|
type: "pyth",
|
|
4354
5520
|
address: entry.feedId,
|
|
4355
5521
|
pairLabel: `${entry.symbol} / USD (Pyth)`,
|
|
4356
|
-
liquidity:
|
|
5522
|
+
liquidity: Number.MAX_SAFE_INTEGER,
|
|
4357
5523
|
// Pyth is considered deep liquidity
|
|
4358
5524
|
price: 0,
|
|
4359
5525
|
// We don't fetch live price here; caller can enrich
|
|
@@ -4370,10 +5536,16 @@ async function fetchJupiterSource(mint, signal) {
|
|
|
4370
5536
|
headers: { "User-Agent": "percolator/1.0" }
|
|
4371
5537
|
}
|
|
4372
5538
|
);
|
|
4373
|
-
if (!resp.ok)
|
|
5539
|
+
if (!resp.ok) {
|
|
5540
|
+
console.debug(`[fetchJupiterSource] HTTP ${resp.status} for mint ${mint}`);
|
|
5541
|
+
return null;
|
|
5542
|
+
}
|
|
4374
5543
|
const json = await resp.json();
|
|
4375
5544
|
const row = parseJupiterMintEntry(json, mint);
|
|
4376
|
-
if (!row)
|
|
5545
|
+
if (!row) {
|
|
5546
|
+
console.debug(`[fetchJupiterSource] No price data from Jupiter for mint ${mint}`);
|
|
5547
|
+
return null;
|
|
5548
|
+
}
|
|
4377
5549
|
return {
|
|
4378
5550
|
type: "jupiter",
|
|
4379
5551
|
address: mint,
|
|
@@ -4384,7 +5556,11 @@ async function fetchJupiterSource(mint, signal) {
|
|
|
4384
5556
|
confidence: 40
|
|
4385
5557
|
// Fallback — lower confidence
|
|
4386
5558
|
};
|
|
4387
|
-
} catch {
|
|
5559
|
+
} catch (err) {
|
|
5560
|
+
console.warn(
|
|
5561
|
+
`[fetchJupiterSource] Error fetching Jupiter data for mint ${mint}:`,
|
|
5562
|
+
err instanceof Error ? err.message : String(err)
|
|
5563
|
+
);
|
|
4388
5564
|
return null;
|
|
4389
5565
|
}
|
|
4390
5566
|
}
|
|
@@ -4399,8 +5575,20 @@ async function resolvePrice(mint, signal, options) {
|
|
|
4399
5575
|
const pythSource = lookupPythSource(mint);
|
|
4400
5576
|
const allSources = [];
|
|
4401
5577
|
if (pythSource) {
|
|
4402
|
-
const
|
|
4403
|
-
|
|
5578
|
+
const dexPrice = dexSources[0]?.price ?? 0;
|
|
5579
|
+
const jupPrice = jupiterSource?.price ?? 0;
|
|
5580
|
+
if (dexPrice > 0 && jupPrice > 0) {
|
|
5581
|
+
const mid = (dexPrice + jupPrice) / 2;
|
|
5582
|
+
const deviation = Math.abs(dexPrice - jupPrice) / mid;
|
|
5583
|
+
if (deviation > 0.5) {
|
|
5584
|
+
pythSource.price = 0;
|
|
5585
|
+
pythSource.confidence = 20;
|
|
5586
|
+
} else {
|
|
5587
|
+
pythSource.price = mid;
|
|
5588
|
+
}
|
|
5589
|
+
} else {
|
|
5590
|
+
pythSource.price = dexPrice || jupPrice || 0;
|
|
5591
|
+
}
|
|
4404
5592
|
allSources.push(pythSource);
|
|
4405
5593
|
}
|
|
4406
5594
|
allSources.push(...dexSources);
|
|
@@ -4432,6 +5620,7 @@ export {
|
|
|
4432
5620
|
ACCOUNTS_FUND_MARKET_INSURANCE,
|
|
4433
5621
|
ACCOUNTS_INIT_LP,
|
|
4434
5622
|
ACCOUNTS_INIT_MARKET,
|
|
5623
|
+
ACCOUNTS_INIT_MATCHER_CTX,
|
|
4435
5624
|
ACCOUNTS_INIT_USER,
|
|
4436
5625
|
ACCOUNTS_KEEPER_CRANK,
|
|
4437
5626
|
ACCOUNTS_LIQUIDATE_AT_ORACLE,
|
|
@@ -4465,15 +5654,22 @@ export {
|
|
|
4465
5654
|
CHAINLINK_ANSWER_OFFSET,
|
|
4466
5655
|
CHAINLINK_DECIMALS_OFFSET,
|
|
4467
5656
|
CHAINLINK_MIN_SIZE,
|
|
5657
|
+
CHAINLINK_TIMESTAMP_OFFSET,
|
|
4468
5658
|
CREATOR_LOCK_SEED,
|
|
4469
5659
|
CTX_VAMM_OFFSET,
|
|
4470
5660
|
DEFAULT_OI_RAMP_SLOTS,
|
|
4471
5661
|
ENGINE_MARK_PRICE_OFF,
|
|
4472
5662
|
ENGINE_OFF,
|
|
4473
5663
|
IX_TAG,
|
|
5664
|
+
LIGHTHOUSE_CONSTRAINT_ADDRESS,
|
|
5665
|
+
LIGHTHOUSE_ERROR_CODES,
|
|
5666
|
+
LIGHTHOUSE_PROGRAM_ID,
|
|
5667
|
+
LIGHTHOUSE_PROGRAM_ID_STR2 as LIGHTHOUSE_PROGRAM_ID_STR,
|
|
5668
|
+
LIGHTHOUSE_USER_MESSAGE,
|
|
4474
5669
|
MARK_PRICE_EMA_ALPHA_E6,
|
|
4475
5670
|
MARK_PRICE_EMA_WINDOW_SLOTS,
|
|
4476
5671
|
MAX_DECIMALS,
|
|
5672
|
+
MAX_ORACLE_PRICE,
|
|
4477
5673
|
METEORA_DLMM_PROGRAM_ID,
|
|
4478
5674
|
ORACLE_PHASE_GROWING,
|
|
4479
5675
|
ORACLE_PHASE_MATURE,
|
|
@@ -4491,9 +5687,11 @@ export {
|
|
|
4491
5687
|
RAMP_START_BPS,
|
|
4492
5688
|
RAYDIUM_CLMM_PROGRAM_ID,
|
|
4493
5689
|
RENOUNCE_ADMIN_CONFIRMATION,
|
|
5690
|
+
RpcPool,
|
|
4494
5691
|
SLAB_TIERS,
|
|
4495
5692
|
SLAB_TIERS_V0,
|
|
4496
5693
|
SLAB_TIERS_V1,
|
|
5694
|
+
SLAB_TIERS_V12_1,
|
|
4497
5695
|
SLAB_TIERS_V1D,
|
|
4498
5696
|
SLAB_TIERS_V1D_LEGACY,
|
|
4499
5697
|
SLAB_TIERS_V1M,
|
|
@@ -4501,6 +5699,7 @@ export {
|
|
|
4501
5699
|
SLAB_TIERS_V2,
|
|
4502
5700
|
SLAB_TIERS_V_ADL,
|
|
4503
5701
|
SLAB_TIERS_V_ADL_DISCOVERY,
|
|
5702
|
+
SLAB_TIERS_V_SETDEXPOOL,
|
|
4504
5703
|
STAKE_IX,
|
|
4505
5704
|
STAKE_POOL_SIZE,
|
|
4506
5705
|
STAKE_PROGRAM_ID,
|
|
@@ -4510,11 +5709,15 @@ export {
|
|
|
4510
5709
|
VAMM_MAGIC,
|
|
4511
5710
|
ValidationError,
|
|
4512
5711
|
WELL_KNOWN,
|
|
5712
|
+
_internal,
|
|
4513
5713
|
buildAccountMetas,
|
|
4514
5714
|
buildAdlInstruction,
|
|
4515
5715
|
buildAdlTransaction,
|
|
4516
5716
|
buildIx,
|
|
4517
5717
|
checkPhaseTransition,
|
|
5718
|
+
checkRpcHealth,
|
|
5719
|
+
classifyLighthouseError,
|
|
5720
|
+
clearStaticMarkets,
|
|
4518
5721
|
computeDexSpotPriceE6,
|
|
4519
5722
|
computeDynamicFeeBps,
|
|
4520
5723
|
computeDynamicTradingFee,
|
|
@@ -4526,6 +5729,7 @@ export {
|
|
|
4526
5729
|
computeLiqPrice,
|
|
4527
5730
|
computeMarkPnl,
|
|
4528
5731
|
computeMaxLeverage,
|
|
5732
|
+
computeMaxWithdrawable,
|
|
4529
5733
|
computePnlPercent,
|
|
4530
5734
|
computePreTradeLiqPrice,
|
|
4531
5735
|
computeRequiredMargin,
|
|
@@ -4533,8 +5737,10 @@ export {
|
|
|
4533
5737
|
computeVammQuote,
|
|
4534
5738
|
computeWarmupLeverageCap,
|
|
4535
5739
|
computeWarmupMaxPositionSize,
|
|
5740
|
+
computeWarmupProgress,
|
|
4536
5741
|
computeWarmupUnlockedCapital,
|
|
4537
5742
|
concatBytes,
|
|
5743
|
+
countLighthouseInstructions,
|
|
4538
5744
|
decodeError,
|
|
4539
5745
|
decodeStakePool,
|
|
4540
5746
|
depositAccounts,
|
|
@@ -4553,6 +5759,8 @@ export {
|
|
|
4553
5759
|
detectSlabLayout,
|
|
4554
5760
|
detectTokenProgram,
|
|
4555
5761
|
discoverMarkets,
|
|
5762
|
+
discoverMarketsViaApi,
|
|
5763
|
+
discoverMarketsViaStaticBundle,
|
|
4556
5764
|
encBool,
|
|
4557
5765
|
encI128,
|
|
4558
5766
|
encI64,
|
|
@@ -4582,6 +5790,7 @@ export {
|
|
|
4582
5790
|
encodeFundMarketInsurance,
|
|
4583
5791
|
encodeInitLP,
|
|
4584
5792
|
encodeInitMarket,
|
|
5793
|
+
encodeInitMatcherCtx,
|
|
4585
5794
|
encodeInitSharedVault,
|
|
4586
5795
|
encodeInitUser,
|
|
4587
5796
|
encodeKeeperCrank,
|
|
@@ -4649,12 +5858,18 @@ export {
|
|
|
4649
5858
|
getCurrentNetwork,
|
|
4650
5859
|
getErrorHint,
|
|
4651
5860
|
getErrorName,
|
|
5861
|
+
getMarketsByAddress,
|
|
4652
5862
|
getMatcherProgramId,
|
|
4653
5863
|
getProgramId,
|
|
4654
5864
|
getStakeProgramId,
|
|
5865
|
+
getStaticMarkets,
|
|
4655
5866
|
initPoolAccounts,
|
|
4656
5867
|
isAccountUsed,
|
|
4657
5868
|
isAdlTriggered,
|
|
5869
|
+
isAnchorErrorCode,
|
|
5870
|
+
isLighthouseError,
|
|
5871
|
+
isLighthouseFailureInLogs,
|
|
5872
|
+
isLighthouseInstruction,
|
|
4658
5873
|
isStandardToken,
|
|
4659
5874
|
isToken2022,
|
|
4660
5875
|
isValidChainlinkOracle,
|
|
@@ -4673,11 +5888,14 @@ export {
|
|
|
4673
5888
|
rankAdlPositions,
|
|
4674
5889
|
readLastThrUpdateSlot,
|
|
4675
5890
|
readNonce,
|
|
5891
|
+
registerStaticMarkets,
|
|
4676
5892
|
resolvePrice,
|
|
4677
5893
|
safeEnv,
|
|
4678
5894
|
simulateOrSend,
|
|
4679
5895
|
slabDataSize,
|
|
4680
5896
|
slabDataSizeV1,
|
|
5897
|
+
stripLighthouseFromTransaction,
|
|
5898
|
+
stripLighthouseInstructions,
|
|
4681
5899
|
validateAmount,
|
|
4682
5900
|
validateBps,
|
|
4683
5901
|
validateI128,
|
|
@@ -4688,6 +5906,7 @@ export {
|
|
|
4688
5906
|
validateU128,
|
|
4689
5907
|
validateU16,
|
|
4690
5908
|
validateU64,
|
|
5909
|
+
withRetry,
|
|
4691
5910
|
withdrawAccounts
|
|
4692
5911
|
};
|
|
4693
5912
|
//# sourceMappingURL=index.js.map
|