@percolatorct/sdk 0.5.1 → 1.0.0-beta.2
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 +1423 -196
- 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,6 +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_MATCHER_PROGRAM_OFF = 144;
|
|
1566
|
+
var V12_1_ACCT_MATCHER_CONTEXT_OFF = 176;
|
|
1567
|
+
var V12_1_ACCT_OWNER_OFF = 208;
|
|
1568
|
+
var V12_1_ACCT_FEE_CREDITS_OFF = 240;
|
|
1569
|
+
var V12_1_ACCT_LAST_FEE_SLOT_OFF = 256;
|
|
1570
|
+
var V12_1_ACCT_POSITION_SIZE_OFF = 296;
|
|
1571
|
+
var V12_1_ACCT_ENTRY_PRICE_OFF = 280;
|
|
1572
|
+
var V12_1_ACCT_FUNDING_INDEX_OFF = 288;
|
|
1460
1573
|
var V1M_ENGINE_OFF = 640;
|
|
1461
1574
|
var V1M_CONFIG_LEN = 536;
|
|
1462
1575
|
var V1M_ACCOUNT_SIZE = 248;
|
|
@@ -1494,15 +1607,7 @@ var V1M_ENGINE_EMERGENCY_START_SLOT_OFF = 688;
|
|
|
1494
1607
|
var V1M_ENGINE_LAST_BREAKER_SLOT_OFF = 696;
|
|
1495
1608
|
var V1M_ENGINE_BITMAP_OFF = 720;
|
|
1496
1609
|
var V1M2_ACCOUNT_SIZE = 312;
|
|
1497
|
-
var V1M2_ENGINE_BITMAP_OFF =
|
|
1498
|
-
var V1M2_ENGINE_CURRENT_SLOT_OFF = 408;
|
|
1499
|
-
var V1M2_ENGINE_FUNDING_INDEX_OFF = 416;
|
|
1500
|
-
var V1M2_ENGINE_LAST_FUNDING_SLOT_OFF = 432;
|
|
1501
|
-
var V1M2_ENGINE_FUNDING_RATE_BPS_OFF = 440;
|
|
1502
|
-
var V1M2_ENGINE_MARK_PRICE_OFF = 480;
|
|
1503
|
-
var V1M2_ENGINE_LAST_CRANK_SLOT_OFF = 504;
|
|
1504
|
-
var V1M2_ENGINE_MAX_CRANK_STALENESS_OFF = 512;
|
|
1505
|
-
var V1M2_RUNTIME_SHIFT = 32;
|
|
1610
|
+
var V1M2_ENGINE_BITMAP_OFF = 1008;
|
|
1506
1611
|
var ENGINE_OFF = V1_ENGINE_OFF;
|
|
1507
1612
|
var ENGINE_MARK_PRICE_OFF = V1_ENGINE_MARK_PRICE_OFF;
|
|
1508
1613
|
function computeSlabSize(engineOff, bitmapOff, accountSize, maxAccounts, postBitmap = 18) {
|
|
@@ -1521,6 +1626,9 @@ var V1D_SIZES = /* @__PURE__ */ new Map();
|
|
|
1521
1626
|
var V2_SIZES = /* @__PURE__ */ new Map();
|
|
1522
1627
|
var V1M_SIZES = /* @__PURE__ */ new Map();
|
|
1523
1628
|
var V_ADL_SIZES = /* @__PURE__ */ new Map();
|
|
1629
|
+
var V1M2_SIZES = /* @__PURE__ */ new Map();
|
|
1630
|
+
var V_SETDEXPOOL_SIZES = /* @__PURE__ */ new Map();
|
|
1631
|
+
var V12_1_SIZES = /* @__PURE__ */ new Map();
|
|
1524
1632
|
var V1D_SIZES_LEGACY = /* @__PURE__ */ new Map();
|
|
1525
1633
|
for (const n of TIERS) {
|
|
1526
1634
|
V0_SIZES.set(computeSlabSize(V0_ENGINE_OFF, V0_ENGINE_BITMAP_OFF, V0_ACCOUNT_SIZE, n), n);
|
|
@@ -1531,6 +1639,9 @@ for (const n of TIERS) {
|
|
|
1531
1639
|
V2_SIZES.set(computeSlabSize(V2_ENGINE_OFF, V2_ENGINE_BITMAP_OFF, V2_ACCOUNT_SIZE, n, 18), n);
|
|
1532
1640
|
V1M_SIZES.set(computeSlabSize(V1M_ENGINE_OFF, V1M_ENGINE_BITMAP_OFF, V1M_ACCOUNT_SIZE, n, 18), n);
|
|
1533
1641
|
V_ADL_SIZES.set(computeSlabSize(V_ADL_ENGINE_OFF, V_ADL_ENGINE_BITMAP_OFF, V_ADL_ACCOUNT_SIZE, n, 18), n);
|
|
1642
|
+
V1M2_SIZES.set(computeSlabSize(V1M2_ENGINE_OFF, V1M2_ENGINE_BITMAP_OFF, V1M2_ACCOUNT_SIZE, n, 18), n);
|
|
1643
|
+
V_SETDEXPOOL_SIZES.set(computeSlabSize(V_SETDEXPOOL_ENGINE_OFF, V_ADL_ENGINE_BITMAP_OFF, V_ADL_ACCOUNT_SIZE, n, 18), n);
|
|
1644
|
+
V12_1_SIZES.set(computeSlabSize(V12_1_ENGINE_OFF, V12_1_ENGINE_BITMAP_OFF, V12_1_ACCOUNT_SIZE, n, 18), n);
|
|
1534
1645
|
}
|
|
1535
1646
|
var SLAB_TIERS_V2 = {
|
|
1536
1647
|
small: { maxAccounts: 256, dataSize: 65088, label: "Small", description: "256 slots (V2 BPF intermediate)" },
|
|
@@ -1826,41 +1937,68 @@ function buildLayoutV1M2(maxAccounts) {
|
|
|
1826
1937
|
accountsOff: engineOff + accountsOffRel,
|
|
1827
1938
|
engineInsuranceOff: 16,
|
|
1828
1939
|
engineParamsOff: V1M2_ENGINE_PARAMS_OFF,
|
|
1829
|
-
// 96 — expanded InsuranceFund
|
|
1830
|
-
paramsSize:
|
|
1831
|
-
// 336 — same as
|
|
1832
|
-
// Runtime fields:
|
|
1833
|
-
engineCurrentSlotOff:
|
|
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
|
-
|
|
1860
|
-
|
|
1940
|
+
// 96 — expanded InsuranceFund (same as V_ADL)
|
|
1941
|
+
paramsSize: V_ADL_PARAMS_SIZE,
|
|
1942
|
+
// 336 — same as V_ADL
|
|
1943
|
+
// Runtime fields: V1M2 engine struct is layout-identical to V_ADL — reuse V_ADL constants.
|
|
1944
|
+
engineCurrentSlotOff: V_ADL_ENGINE_CURRENT_SLOT_OFF,
|
|
1945
|
+
// 432
|
|
1946
|
+
engineFundingIndexOff: V_ADL_ENGINE_FUNDING_INDEX_OFF,
|
|
1947
|
+
// 440
|
|
1948
|
+
engineLastFundingSlotOff: V_ADL_ENGINE_LAST_FUNDING_SLOT_OFF,
|
|
1949
|
+
// 456
|
|
1950
|
+
engineFundingRateBpsOff: V_ADL_ENGINE_FUNDING_RATE_BPS_OFF,
|
|
1951
|
+
// 464
|
|
1952
|
+
engineMarkPriceOff: V_ADL_ENGINE_MARK_PRICE_OFF,
|
|
1953
|
+
// 504
|
|
1954
|
+
engineLastCrankSlotOff: V_ADL_ENGINE_LAST_CRANK_SLOT_OFF,
|
|
1955
|
+
// 528
|
|
1956
|
+
engineMaxCrankStalenessOff: V_ADL_ENGINE_MAX_CRANK_STALENESS_OFF,
|
|
1957
|
+
// 536
|
|
1958
|
+
engineTotalOiOff: V_ADL_ENGINE_TOTAL_OI_OFF,
|
|
1959
|
+
// 544
|
|
1960
|
+
engineLongOiOff: V_ADL_ENGINE_LONG_OI_OFF,
|
|
1961
|
+
// 560
|
|
1962
|
+
engineShortOiOff: V_ADL_ENGINE_SHORT_OI_OFF,
|
|
1963
|
+
// 576
|
|
1964
|
+
engineCTotOff: V_ADL_ENGINE_C_TOT_OFF,
|
|
1965
|
+
// 592
|
|
1966
|
+
enginePnlPosTotOff: V_ADL_ENGINE_PNL_POS_TOT_OFF,
|
|
1967
|
+
// 608
|
|
1968
|
+
engineLiqCursorOff: V_ADL_ENGINE_LIQ_CURSOR_OFF,
|
|
1969
|
+
// 640
|
|
1970
|
+
engineGcCursorOff: V_ADL_ENGINE_GC_CURSOR_OFF,
|
|
1971
|
+
// 642
|
|
1972
|
+
engineLastSweepStartOff: V_ADL_ENGINE_LAST_SWEEP_START_OFF,
|
|
1973
|
+
// 648
|
|
1974
|
+
engineLastSweepCompleteOff: V_ADL_ENGINE_LAST_SWEEP_COMPLETE_OFF,
|
|
1975
|
+
// 656
|
|
1976
|
+
engineCrankCursorOff: V_ADL_ENGINE_CRANK_CURSOR_OFF,
|
|
1977
|
+
// 664
|
|
1978
|
+
engineSweepStartIdxOff: V_ADL_ENGINE_SWEEP_START_IDX_OFF,
|
|
1979
|
+
// 666
|
|
1980
|
+
engineLifetimeLiquidationsOff: V_ADL_ENGINE_LIFETIME_LIQUIDATIONS_OFF,
|
|
1981
|
+
// 672
|
|
1982
|
+
engineLifetimeForceClosesOff: V_ADL_ENGINE_LIFETIME_FORCE_CLOSES_OFF,
|
|
1983
|
+
// 680
|
|
1984
|
+
engineNetLpPosOff: V_ADL_ENGINE_NET_LP_POS_OFF,
|
|
1985
|
+
// 904
|
|
1986
|
+
engineLpSumAbsOff: V_ADL_ENGINE_LP_SUM_ABS_OFF,
|
|
1987
|
+
// 920
|
|
1988
|
+
engineLpMaxAbsOff: V_ADL_ENGINE_LP_MAX_ABS_OFF,
|
|
1989
|
+
// 936
|
|
1990
|
+
engineLpMaxAbsSweepOff: V_ADL_ENGINE_LP_MAX_ABS_SWEEP_OFF,
|
|
1991
|
+
// 952
|
|
1992
|
+
engineEmergencyOiModeOff: V_ADL_ENGINE_EMERGENCY_OI_MODE_OFF,
|
|
1993
|
+
// 968
|
|
1994
|
+
engineEmergencyStartSlotOff: V_ADL_ENGINE_EMERGENCY_START_SLOT_OFF,
|
|
1995
|
+
// 976
|
|
1996
|
+
engineLastBreakerSlotOff: V_ADL_ENGINE_LAST_BREAKER_SLOT_OFF,
|
|
1997
|
+
// 984
|
|
1861
1998
|
engineBitmapOff: V1M2_ENGINE_BITMAP_OFF,
|
|
1862
1999
|
postBitmap: 18,
|
|
1863
|
-
acctOwnerOff:
|
|
2000
|
+
acctOwnerOff: V_ADL_ACCT_OWNER_OFF,
|
|
2001
|
+
// 192 — same shift as V_ADL (reserved_pnl u64→u128)
|
|
1864
2002
|
hasInsuranceIsolation: true,
|
|
1865
2003
|
engineInsuranceIsolatedOff: 48,
|
|
1866
2004
|
engineInsuranceIsolationBpsOff: 64
|
|
@@ -1950,7 +2088,7 @@ function buildLayoutVADL(maxAccounts) {
|
|
|
1950
2088
|
engineLastBreakerSlotOff: V_ADL_ENGINE_LAST_BREAKER_SLOT_OFF,
|
|
1951
2089
|
// 984
|
|
1952
2090
|
engineBitmapOff: V_ADL_ENGINE_BITMAP_OFF,
|
|
1953
|
-
//
|
|
2091
|
+
// 1008
|
|
1954
2092
|
postBitmap: 18,
|
|
1955
2093
|
acctOwnerOff: V_ADL_ACCT_OWNER_OFF,
|
|
1956
2094
|
// 192
|
|
@@ -1959,17 +2097,145 @@ function buildLayoutVADL(maxAccounts) {
|
|
|
1959
2097
|
engineInsuranceIsolationBpsOff: 64
|
|
1960
2098
|
};
|
|
1961
2099
|
}
|
|
2100
|
+
var SLAB_TIERS_V_SETDEXPOOL = {};
|
|
2101
|
+
for (const [label, n] of [["Micro", 64], ["Small", 256], ["Medium", 1024], ["Large", 4096]]) {
|
|
2102
|
+
const size = computeSlabSize(V_SETDEXPOOL_ENGINE_OFF, V_ADL_ENGINE_BITMAP_OFF, V_ADL_ACCOUNT_SIZE, n, 18);
|
|
2103
|
+
SLAB_TIERS_V_SETDEXPOOL[label.toLowerCase()] = { maxAccounts: n, dataSize: size, label, description: `${n} slots (V_SETDEXPOOL PERC-SetDexPool)` };
|
|
2104
|
+
}
|
|
2105
|
+
var SLAB_TIERS_V12_1 = {};
|
|
2106
|
+
for (const [label, n] of [["Micro", 64], ["Small", 256], ["Medium", 1024], ["Large", 4096]]) {
|
|
2107
|
+
const size = computeSlabSize(V12_1_ENGINE_OFF, V12_1_ENGINE_BITMAP_OFF, V12_1_ACCOUNT_SIZE, n, 18);
|
|
2108
|
+
SLAB_TIERS_V12_1[label.toLowerCase()] = { maxAccounts: n, dataSize: size, label, description: `${n} slots (v12.1)` };
|
|
2109
|
+
}
|
|
2110
|
+
function buildLayoutVSetDexPool(maxAccounts) {
|
|
2111
|
+
const engineOff = V_SETDEXPOOL_ENGINE_OFF;
|
|
2112
|
+
const bitmapOff = V_ADL_ENGINE_BITMAP_OFF;
|
|
2113
|
+
const accountSize = V_ADL_ACCOUNT_SIZE;
|
|
2114
|
+
const bitmapWords = Math.ceil(maxAccounts / 64);
|
|
2115
|
+
const bitmapBytes = bitmapWords * 8;
|
|
2116
|
+
const postBitmap = 18;
|
|
2117
|
+
const nextFreeBytes = maxAccounts * 2;
|
|
2118
|
+
const preAccountsLen = bitmapOff + bitmapBytes + postBitmap + nextFreeBytes;
|
|
2119
|
+
const accountsOffRel = Math.ceil(preAccountsLen / 8) * 8;
|
|
2120
|
+
return {
|
|
2121
|
+
version: 1,
|
|
2122
|
+
headerLen: V1_HEADER_LEN,
|
|
2123
|
+
configOffset: V1_HEADER_LEN,
|
|
2124
|
+
configLen: V_SETDEXPOOL_CONFIG_LEN,
|
|
2125
|
+
// 544
|
|
2126
|
+
reservedOff: V1_RESERVED_OFF,
|
|
2127
|
+
engineOff,
|
|
2128
|
+
accountSize,
|
|
2129
|
+
maxAccounts,
|
|
2130
|
+
bitmapWords,
|
|
2131
|
+
accountsOff: engineOff + accountsOffRel,
|
|
2132
|
+
engineInsuranceOff: 16,
|
|
2133
|
+
engineParamsOff: V_ADL_ENGINE_PARAMS_OFF,
|
|
2134
|
+
paramsSize: V_ADL_PARAMS_SIZE,
|
|
2135
|
+
engineCurrentSlotOff: V_ADL_ENGINE_CURRENT_SLOT_OFF,
|
|
2136
|
+
engineFundingIndexOff: V_ADL_ENGINE_FUNDING_INDEX_OFF,
|
|
2137
|
+
engineLastFundingSlotOff: V_ADL_ENGINE_LAST_FUNDING_SLOT_OFF,
|
|
2138
|
+
engineFundingRateBpsOff: V_ADL_ENGINE_FUNDING_RATE_BPS_OFF,
|
|
2139
|
+
engineMarkPriceOff: V_ADL_ENGINE_MARK_PRICE_OFF,
|
|
2140
|
+
engineLastCrankSlotOff: V_ADL_ENGINE_LAST_CRANK_SLOT_OFF,
|
|
2141
|
+
engineMaxCrankStalenessOff: V_ADL_ENGINE_MAX_CRANK_STALENESS_OFF,
|
|
2142
|
+
engineTotalOiOff: V_ADL_ENGINE_TOTAL_OI_OFF,
|
|
2143
|
+
engineLongOiOff: V_ADL_ENGINE_LONG_OI_OFF,
|
|
2144
|
+
engineShortOiOff: V_ADL_ENGINE_SHORT_OI_OFF,
|
|
2145
|
+
engineCTotOff: V_ADL_ENGINE_C_TOT_OFF,
|
|
2146
|
+
enginePnlPosTotOff: V_ADL_ENGINE_PNL_POS_TOT_OFF,
|
|
2147
|
+
engineLiqCursorOff: V_ADL_ENGINE_LIQ_CURSOR_OFF,
|
|
2148
|
+
engineGcCursorOff: V_ADL_ENGINE_GC_CURSOR_OFF,
|
|
2149
|
+
engineLastSweepStartOff: V_ADL_ENGINE_LAST_SWEEP_START_OFF,
|
|
2150
|
+
engineLastSweepCompleteOff: V_ADL_ENGINE_LAST_SWEEP_COMPLETE_OFF,
|
|
2151
|
+
engineCrankCursorOff: V_ADL_ENGINE_CRANK_CURSOR_OFF,
|
|
2152
|
+
engineSweepStartIdxOff: V_ADL_ENGINE_SWEEP_START_IDX_OFF,
|
|
2153
|
+
engineLifetimeLiquidationsOff: V_ADL_ENGINE_LIFETIME_LIQUIDATIONS_OFF,
|
|
2154
|
+
engineLifetimeForceClosesOff: V_ADL_ENGINE_LIFETIME_FORCE_CLOSES_OFF,
|
|
2155
|
+
engineNetLpPosOff: V_ADL_ENGINE_NET_LP_POS_OFF,
|
|
2156
|
+
engineLpSumAbsOff: V_ADL_ENGINE_LP_SUM_ABS_OFF,
|
|
2157
|
+
engineLpMaxAbsOff: V_ADL_ENGINE_LP_MAX_ABS_OFF,
|
|
2158
|
+
engineLpMaxAbsSweepOff: V_ADL_ENGINE_LP_MAX_ABS_SWEEP_OFF,
|
|
2159
|
+
engineEmergencyOiModeOff: V_ADL_ENGINE_EMERGENCY_OI_MODE_OFF,
|
|
2160
|
+
engineEmergencyStartSlotOff: V_ADL_ENGINE_EMERGENCY_START_SLOT_OFF,
|
|
2161
|
+
engineLastBreakerSlotOff: V_ADL_ENGINE_LAST_BREAKER_SLOT_OFF,
|
|
2162
|
+
engineBitmapOff: V_ADL_ENGINE_BITMAP_OFF,
|
|
2163
|
+
postBitmap: 18,
|
|
2164
|
+
acctOwnerOff: V_ADL_ACCT_OWNER_OFF,
|
|
2165
|
+
hasInsuranceIsolation: true,
|
|
2166
|
+
engineInsuranceIsolatedOff: 48,
|
|
2167
|
+
engineInsuranceIsolationBpsOff: 64
|
|
2168
|
+
};
|
|
2169
|
+
}
|
|
2170
|
+
function buildLayoutV12_1(maxAccounts) {
|
|
2171
|
+
const engineOff = V12_1_ENGINE_OFF;
|
|
2172
|
+
const bitmapOff = V12_1_ENGINE_BITMAP_OFF;
|
|
2173
|
+
const accountSize = V12_1_ACCOUNT_SIZE;
|
|
2174
|
+
const bitmapWords = Math.ceil(maxAccounts / 64);
|
|
2175
|
+
const bitmapBytes = bitmapWords * 8;
|
|
2176
|
+
const postBitmap = 18;
|
|
2177
|
+
const nextFreeBytes = maxAccounts * 2;
|
|
2178
|
+
const preAccountsLen = bitmapOff + bitmapBytes + postBitmap + nextFreeBytes;
|
|
2179
|
+
const accountsOffRel = Math.ceil(preAccountsLen / 8) * 8;
|
|
2180
|
+
return {
|
|
2181
|
+
version: 1,
|
|
2182
|
+
headerLen: V1_HEADER_LEN,
|
|
2183
|
+
configOffset: V1_HEADER_LEN,
|
|
2184
|
+
configLen: V_SETDEXPOOL_CONFIG_LEN,
|
|
2185
|
+
// 544 (same as V_SETDEXPOOL)
|
|
2186
|
+
reservedOff: V1_RESERVED_OFF,
|
|
2187
|
+
engineOff,
|
|
2188
|
+
accountSize,
|
|
2189
|
+
maxAccounts,
|
|
2190
|
+
bitmapWords,
|
|
2191
|
+
accountsOff: engineOff + accountsOffRel,
|
|
2192
|
+
engineInsuranceOff: 16,
|
|
2193
|
+
engineParamsOff: V12_1_ENGINE_PARAMS_OFF,
|
|
2194
|
+
paramsSize: V12_1_PARAMS_SIZE,
|
|
2195
|
+
engineCurrentSlotOff: V12_1_ENGINE_CURRENT_SLOT_OFF,
|
|
2196
|
+
engineFundingIndexOff: V12_1_ENGINE_FUNDING_INDEX_OFF,
|
|
2197
|
+
engineLastFundingSlotOff: V12_1_ENGINE_LAST_FUNDING_SLOT_OFF,
|
|
2198
|
+
engineFundingRateBpsOff: V12_1_ENGINE_FUNDING_RATE_BPS_OFF,
|
|
2199
|
+
engineMarkPriceOff: V12_1_ENGINE_MARK_PRICE_OFF,
|
|
2200
|
+
engineLastCrankSlotOff: V12_1_ENGINE_LAST_CRANK_SLOT_OFF,
|
|
2201
|
+
engineMaxCrankStalenessOff: V12_1_ENGINE_MAX_CRANK_STALENESS_OFF,
|
|
2202
|
+
engineTotalOiOff: V12_1_ENGINE_TOTAL_OI_OFF,
|
|
2203
|
+
engineLongOiOff: V12_1_ENGINE_LONG_OI_OFF,
|
|
2204
|
+
engineShortOiOff: V12_1_ENGINE_SHORT_OI_OFF,
|
|
2205
|
+
engineCTotOff: V12_1_ENGINE_C_TOT_OFF,
|
|
2206
|
+
enginePnlPosTotOff: V12_1_ENGINE_PNL_POS_TOT_OFF,
|
|
2207
|
+
engineLiqCursorOff: V12_1_ENGINE_LIQ_CURSOR_OFF,
|
|
2208
|
+
engineGcCursorOff: V12_1_ENGINE_GC_CURSOR_OFF,
|
|
2209
|
+
engineLastSweepStartOff: V12_1_ENGINE_LAST_SWEEP_START_OFF,
|
|
2210
|
+
engineLastSweepCompleteOff: V12_1_ENGINE_LAST_SWEEP_COMPLETE_OFF,
|
|
2211
|
+
engineCrankCursorOff: V12_1_ENGINE_CRANK_CURSOR_OFF,
|
|
2212
|
+
engineSweepStartIdxOff: V12_1_ENGINE_SWEEP_START_IDX_OFF,
|
|
2213
|
+
engineLifetimeLiquidationsOff: V12_1_ENGINE_LIFETIME_LIQUIDATIONS_OFF,
|
|
2214
|
+
engineLifetimeForceClosesOff: V12_1_ENGINE_LIFETIME_FORCE_CLOSES_OFF,
|
|
2215
|
+
engineNetLpPosOff: V12_1_ENGINE_NET_LP_POS_OFF,
|
|
2216
|
+
engineLpSumAbsOff: V12_1_ENGINE_LP_SUM_ABS_OFF,
|
|
2217
|
+
engineLpMaxAbsOff: V12_1_ENGINE_LP_MAX_ABS_OFF,
|
|
2218
|
+
engineLpMaxAbsSweepOff: V12_1_ENGINE_LP_MAX_ABS_SWEEP_OFF,
|
|
2219
|
+
engineEmergencyOiModeOff: V12_1_ENGINE_EMERGENCY_OI_MODE_OFF,
|
|
2220
|
+
engineEmergencyStartSlotOff: V12_1_ENGINE_EMERGENCY_START_SLOT_OFF,
|
|
2221
|
+
engineLastBreakerSlotOff: V12_1_ENGINE_LAST_BREAKER_SLOT_OFF,
|
|
2222
|
+
engineBitmapOff: V12_1_ENGINE_BITMAP_OFF,
|
|
2223
|
+
postBitmap: 18,
|
|
2224
|
+
acctOwnerOff: V12_1_ACCT_OWNER_OFF,
|
|
2225
|
+
hasInsuranceIsolation: true,
|
|
2226
|
+
engineInsuranceIsolatedOff: 48,
|
|
2227
|
+
engineInsuranceIsolationBpsOff: 64
|
|
2228
|
+
};
|
|
2229
|
+
}
|
|
1962
2230
|
function detectSlabLayout(dataLen, data) {
|
|
2231
|
+
const v121n = V12_1_SIZES.get(dataLen);
|
|
2232
|
+
if (v121n !== void 0) return buildLayoutV12_1(v121n);
|
|
2233
|
+
const vsdpn = V_SETDEXPOOL_SIZES.get(dataLen);
|
|
2234
|
+
if (vsdpn !== void 0) return buildLayoutVSetDexPool(vsdpn);
|
|
2235
|
+
const v1m2n = V1M2_SIZES.get(dataLen);
|
|
2236
|
+
if (v1m2n !== void 0) return buildLayoutV1M2(v1m2n);
|
|
1963
2237
|
const vadln = V_ADL_SIZES.get(dataLen);
|
|
1964
|
-
if (vadln !== void 0)
|
|
1965
|
-
if (data && data.length >= 752) {
|
|
1966
|
-
const maxAcctsV1M2 = readU64LE(data, V1M2_ENGINE_OFF + V1M2_ENGINE_PARAMS_OFF + 32);
|
|
1967
|
-
if (maxAcctsV1M2 === BigInt(vadln)) {
|
|
1968
|
-
return buildLayoutV1M2(vadln);
|
|
1969
|
-
}
|
|
1970
|
-
}
|
|
1971
|
-
return buildLayoutVADL(vadln);
|
|
1972
|
-
}
|
|
2238
|
+
if (vadln !== void 0) return buildLayoutVADL(vadln);
|
|
1973
2239
|
const v1mn = V1M_SIZES.get(dataLen);
|
|
1974
2240
|
if (v1mn !== void 0) return buildLayoutV1M(v1mn);
|
|
1975
2241
|
const v0n = V0_SIZES.get(dataLen);
|
|
@@ -2202,6 +2468,14 @@ function parseConfig(data, layoutHint) {
|
|
|
2202
2468
|
}
|
|
2203
2469
|
}
|
|
2204
2470
|
}
|
|
2471
|
+
let dexPool = null;
|
|
2472
|
+
const DEX_POOL_REL_OFF = 512;
|
|
2473
|
+
if (configLen >= DEX_POOL_REL_OFF + 32 && data.length >= configOff + DEX_POOL_REL_OFF + 32) {
|
|
2474
|
+
const dexPoolBytes = data.subarray(configOff + DEX_POOL_REL_OFF, configOff + DEX_POOL_REL_OFF + 32);
|
|
2475
|
+
if (dexPoolBytes.some((b) => b !== 0)) {
|
|
2476
|
+
dexPool = new PublicKey3(dexPoolBytes);
|
|
2477
|
+
}
|
|
2478
|
+
}
|
|
2205
2479
|
return {
|
|
2206
2480
|
collateralMint,
|
|
2207
2481
|
vaultPubkey,
|
|
@@ -2244,7 +2518,8 @@ function parseConfig(data, layoutHint) {
|
|
|
2244
2518
|
insuranceIsolationBps,
|
|
2245
2519
|
oraclePhase,
|
|
2246
2520
|
cumulativeVolumeE6,
|
|
2247
|
-
phase2DeltaSlots
|
|
2521
|
+
phase2DeltaSlots,
|
|
2522
|
+
dexPool
|
|
2248
2523
|
};
|
|
2249
2524
|
}
|
|
2250
2525
|
function parseParams(data, layoutHint) {
|
|
@@ -2384,16 +2659,17 @@ function parseAccount(data, idx) {
|
|
|
2384
2659
|
if (data.length < base + layout.accountSize) {
|
|
2385
2660
|
throw new Error("Slab data too short for account");
|
|
2386
2661
|
}
|
|
2662
|
+
const isV12_1 = layout.accountSize >= 320;
|
|
2387
2663
|
const isAdl = layout.accountSize >= 312;
|
|
2388
2664
|
const warmupStartedOff = isAdl ? V_ADL_ACCT_WARMUP_STARTED_OFF : ACCT_WARMUP_STARTED_OFF;
|
|
2389
2665
|
const warmupSlopeOff = isAdl ? V_ADL_ACCT_WARMUP_SLOPE_OFF : ACCT_WARMUP_SLOPE_OFF;
|
|
2390
|
-
const positionSizeOff = isAdl ? V_ADL_ACCT_POSITION_SIZE_OFF : ACCT_POSITION_SIZE_OFF;
|
|
2391
|
-
const entryPriceOff = isAdl ? V_ADL_ACCT_ENTRY_PRICE_OFF : ACCT_ENTRY_PRICE_OFF;
|
|
2392
|
-
const fundingIndexOff = isAdl ? V_ADL_ACCT_FUNDING_INDEX_OFF : ACCT_FUNDING_INDEX_OFF;
|
|
2393
|
-
const matcherProgOff = isAdl ? V_ADL_ACCT_MATCHER_PROGRAM_OFF : ACCT_MATCHER_PROGRAM_OFF;
|
|
2394
|
-
const matcherCtxOff = isAdl ? V_ADL_ACCT_MATCHER_CONTEXT_OFF : ACCT_MATCHER_CONTEXT_OFF;
|
|
2395
|
-
const feeCreditsOff = isAdl ? V_ADL_ACCT_FEE_CREDITS_OFF : ACCT_FEE_CREDITS_OFF;
|
|
2396
|
-
const lastFeeSlotOff = isAdl ? V_ADL_ACCT_LAST_FEE_SLOT_OFF : ACCT_LAST_FEE_SLOT_OFF;
|
|
2666
|
+
const positionSizeOff = isV12_1 ? V12_1_ACCT_POSITION_SIZE_OFF : isAdl ? V_ADL_ACCT_POSITION_SIZE_OFF : ACCT_POSITION_SIZE_OFF;
|
|
2667
|
+
const entryPriceOff = isV12_1 ? V12_1_ACCT_ENTRY_PRICE_OFF : isAdl ? V_ADL_ACCT_ENTRY_PRICE_OFF : ACCT_ENTRY_PRICE_OFF;
|
|
2668
|
+
const fundingIndexOff = isV12_1 ? V12_1_ACCT_FUNDING_INDEX_OFF : isAdl ? V_ADL_ACCT_FUNDING_INDEX_OFF : ACCT_FUNDING_INDEX_OFF;
|
|
2669
|
+
const matcherProgOff = isV12_1 ? V12_1_ACCT_MATCHER_PROGRAM_OFF : isAdl ? V_ADL_ACCT_MATCHER_PROGRAM_OFF : ACCT_MATCHER_PROGRAM_OFF;
|
|
2670
|
+
const matcherCtxOff = isV12_1 ? V12_1_ACCT_MATCHER_CONTEXT_OFF : isAdl ? V_ADL_ACCT_MATCHER_CONTEXT_OFF : ACCT_MATCHER_CONTEXT_OFF;
|
|
2671
|
+
const feeCreditsOff = isV12_1 ? V12_1_ACCT_FEE_CREDITS_OFF : isAdl ? V_ADL_ACCT_FEE_CREDITS_OFF : ACCT_FEE_CREDITS_OFF;
|
|
2672
|
+
const lastFeeSlotOff = isV12_1 ? V12_1_ACCT_LAST_FEE_SLOT_OFF : isAdl ? V_ADL_ACCT_LAST_FEE_SLOT_OFF : ACCT_LAST_FEE_SLOT_OFF;
|
|
2397
2673
|
const kindByte = readU8(data, base + ACCT_KIND_OFF);
|
|
2398
2674
|
const kind = kindByte === 1 ? 1 /* LP */ : 0 /* User */;
|
|
2399
2675
|
return {
|
|
@@ -2406,7 +2682,8 @@ function parseAccount(data, idx) {
|
|
|
2406
2682
|
warmupSlopePerStep: readU128LE(data, base + warmupSlopeOff),
|
|
2407
2683
|
positionSize: readI128LE(data, base + positionSizeOff),
|
|
2408
2684
|
entryPrice: readU64LE(data, base + entryPriceOff),
|
|
2409
|
-
|
|
2685
|
+
// V12_1 changed funding_index from i128 to i64 (legacy field moved to end of account)
|
|
2686
|
+
fundingIndex: isV12_1 ? BigInt(readI64LE(data, base + fundingIndexOff)) : readI128LE(data, base + fundingIndexOff),
|
|
2410
2687
|
matcherProgram: new PublicKey3(data.subarray(base + matcherProgOff, base + matcherProgOff + 32)),
|
|
2411
2688
|
matcherContext: new PublicKey3(data.subarray(base + matcherCtxOff, base + matcherCtxOff + 32)),
|
|
2412
2689
|
owner: new PublicKey3(data.subarray(base + layout.acctOwnerOff, base + layout.acctOwnerOff + 32)),
|
|
@@ -2501,7 +2778,14 @@ function derivePythPushOraclePDA(feedIdHex) {
|
|
|
2501
2778
|
}
|
|
2502
2779
|
const feedId = new Uint8Array(32);
|
|
2503
2780
|
for (let i = 0; i < 32; i++) {
|
|
2504
|
-
|
|
2781
|
+
const hexPair = normalized.substring(i * 2, i * 2 + 2);
|
|
2782
|
+
const byte = parseInt(hexPair, 16);
|
|
2783
|
+
if (Number.isNaN(byte)) {
|
|
2784
|
+
throw new Error(
|
|
2785
|
+
`derivePythPushOraclePDA: failed to parse hex byte at position ${i}: "${hexPair}"`
|
|
2786
|
+
);
|
|
2787
|
+
}
|
|
2788
|
+
feedId[i] = byte;
|
|
2505
2789
|
}
|
|
2506
2790
|
const shardBuf = new Uint8Array(2);
|
|
2507
2791
|
return PublicKey4.findProgramAddressSync(
|
|
@@ -2527,13 +2811,76 @@ async function fetchTokenAccount(connection, address, tokenProgramId = TOKEN_PRO
|
|
|
2527
2811
|
return getAccount(connection, address, void 0, tokenProgramId);
|
|
2528
2812
|
}
|
|
2529
2813
|
|
|
2814
|
+
// src/solana/discovery.ts
|
|
2815
|
+
import { PublicKey as PublicKey6 } from "@solana/web3.js";
|
|
2816
|
+
|
|
2817
|
+
// src/solana/static-markets.ts
|
|
2818
|
+
import { PublicKey as PublicKey5 } from "@solana/web3.js";
|
|
2819
|
+
var MAINNET_MARKETS = [
|
|
2820
|
+
// Populated at mainnet launch — currently empty.
|
|
2821
|
+
// To add entries:
|
|
2822
|
+
// { slabAddress: "ABC123...", symbol: "SOL-PERP", name: "SOL Perpetual" },
|
|
2823
|
+
];
|
|
2824
|
+
var DEVNET_MARKETS = [
|
|
2825
|
+
// Populated from prior discoverMarkets() runs on devnet.
|
|
2826
|
+
// These serve as the tier-3 safety net for devnet users.
|
|
2827
|
+
];
|
|
2828
|
+
var STATIC_REGISTRY = {
|
|
2829
|
+
mainnet: MAINNET_MARKETS,
|
|
2830
|
+
devnet: DEVNET_MARKETS
|
|
2831
|
+
};
|
|
2832
|
+
var USER_MARKETS = {
|
|
2833
|
+
mainnet: [],
|
|
2834
|
+
devnet: []
|
|
2835
|
+
};
|
|
2836
|
+
function getStaticMarkets(network) {
|
|
2837
|
+
const builtin = STATIC_REGISTRY[network] ?? [];
|
|
2838
|
+
const user = USER_MARKETS[network] ?? [];
|
|
2839
|
+
if (user.length === 0) return [...builtin];
|
|
2840
|
+
const seen = /* @__PURE__ */ new Map();
|
|
2841
|
+
for (const entry of builtin) {
|
|
2842
|
+
seen.set(entry.slabAddress, entry);
|
|
2843
|
+
}
|
|
2844
|
+
for (const entry of user) {
|
|
2845
|
+
seen.set(entry.slabAddress, entry);
|
|
2846
|
+
}
|
|
2847
|
+
return [...seen.values()];
|
|
2848
|
+
}
|
|
2849
|
+
function registerStaticMarkets(network, entries) {
|
|
2850
|
+
const existing = USER_MARKETS[network];
|
|
2851
|
+
const seen = new Set(existing.map((e) => e.slabAddress));
|
|
2852
|
+
for (const entry of entries) {
|
|
2853
|
+
if (!entry.slabAddress) continue;
|
|
2854
|
+
if (seen.has(entry.slabAddress)) continue;
|
|
2855
|
+
try {
|
|
2856
|
+
new PublicKey5(entry.slabAddress);
|
|
2857
|
+
} catch {
|
|
2858
|
+
console.warn(
|
|
2859
|
+
`[registerStaticMarkets] Skipping invalid slabAddress: ${entry.slabAddress}`
|
|
2860
|
+
);
|
|
2861
|
+
continue;
|
|
2862
|
+
}
|
|
2863
|
+
seen.add(entry.slabAddress);
|
|
2864
|
+
existing.push(entry);
|
|
2865
|
+
}
|
|
2866
|
+
}
|
|
2867
|
+
function clearStaticMarkets(network) {
|
|
2868
|
+
if (network) {
|
|
2869
|
+
USER_MARKETS[network] = [];
|
|
2870
|
+
} else {
|
|
2871
|
+
USER_MARKETS.mainnet = [];
|
|
2872
|
+
USER_MARKETS.devnet = [];
|
|
2873
|
+
}
|
|
2874
|
+
}
|
|
2875
|
+
|
|
2530
2876
|
// src/solana/discovery.ts
|
|
2531
2877
|
var ENGINE_BITMAP_OFF_V0 = 320;
|
|
2532
2878
|
var MAGIC_BYTES = new Uint8Array([84, 65, 76, 79, 67, 82, 69, 80]);
|
|
2533
2879
|
var SLAB_TIERS = {
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2880
|
+
micro: SLAB_TIERS_V12_1["micro"],
|
|
2881
|
+
small: SLAB_TIERS_V12_1["small"],
|
|
2882
|
+
medium: SLAB_TIERS_V12_1["medium"],
|
|
2883
|
+
large: SLAB_TIERS_V12_1["large"]
|
|
2537
2884
|
};
|
|
2538
2885
|
var SLAB_TIERS_V0 = {
|
|
2539
2886
|
small: { maxAccounts: 256, dataSize: 62808, label: "Small", description: "256 slots \xB7 ~0.44 SOL" },
|
|
@@ -2911,19 +3258,74 @@ async function discoverMarkets(connection, programId, options = {}) {
|
|
|
2911
3258
|
"[discoverMarkets] dataSize filters failed, falling back to memcmp:",
|
|
2912
3259
|
err instanceof Error ? err.message : err
|
|
2913
3260
|
);
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
3261
|
+
try {
|
|
3262
|
+
const fallback = await connection.getProgramAccounts(programId, {
|
|
3263
|
+
filters: [
|
|
3264
|
+
{
|
|
3265
|
+
memcmp: {
|
|
3266
|
+
offset: 0,
|
|
3267
|
+
bytes: "F6P2QNqpQV5"
|
|
3268
|
+
// base58 of TALOCREP (u64 LE magic)
|
|
3269
|
+
}
|
|
2921
3270
|
}
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
3271
|
+
],
|
|
3272
|
+
dataSlice: { offset: 0, length: HEADER_SLICE_LENGTH }
|
|
3273
|
+
});
|
|
3274
|
+
rawAccounts = [...fallback].map((e) => ({ ...e, maxAccounts: 4096, dataSize: SLAB_TIERS.large.dataSize }));
|
|
3275
|
+
} catch (memcmpErr) {
|
|
3276
|
+
console.warn(
|
|
3277
|
+
"[discoverMarkets] memcmp fallback also failed:",
|
|
3278
|
+
memcmpErr instanceof Error ? memcmpErr.message : memcmpErr
|
|
3279
|
+
);
|
|
3280
|
+
}
|
|
3281
|
+
}
|
|
3282
|
+
if (rawAccounts.length === 0 && options.apiBaseUrl) {
|
|
3283
|
+
console.warn(
|
|
3284
|
+
"[discoverMarkets] RPC discovery returned 0 markets, falling back to REST API"
|
|
3285
|
+
);
|
|
3286
|
+
try {
|
|
3287
|
+
const apiResult = await discoverMarketsViaApi(
|
|
3288
|
+
connection,
|
|
3289
|
+
programId,
|
|
3290
|
+
options.apiBaseUrl,
|
|
3291
|
+
{ timeoutMs: options.apiTimeoutMs }
|
|
3292
|
+
);
|
|
3293
|
+
if (apiResult.length > 0) {
|
|
3294
|
+
return apiResult;
|
|
3295
|
+
}
|
|
3296
|
+
console.warn(
|
|
3297
|
+
"[discoverMarkets] REST API returned 0 markets, checking tier-3 static bundle"
|
|
3298
|
+
);
|
|
3299
|
+
} catch (apiErr) {
|
|
3300
|
+
console.warn(
|
|
3301
|
+
"[discoverMarkets] API fallback also failed:",
|
|
3302
|
+
apiErr instanceof Error ? apiErr.message : apiErr
|
|
3303
|
+
);
|
|
3304
|
+
}
|
|
3305
|
+
}
|
|
3306
|
+
if (rawAccounts.length === 0 && options.network) {
|
|
3307
|
+
const staticEntries = getStaticMarkets(options.network);
|
|
3308
|
+
if (staticEntries.length > 0) {
|
|
3309
|
+
console.warn(
|
|
3310
|
+
`[discoverMarkets] Tier 1+2 failed, falling back to static bundle (${staticEntries.length} addresses for ${options.network})`
|
|
3311
|
+
);
|
|
3312
|
+
try {
|
|
3313
|
+
return await discoverMarketsViaStaticBundle(
|
|
3314
|
+
connection,
|
|
3315
|
+
programId,
|
|
3316
|
+
staticEntries
|
|
3317
|
+
);
|
|
3318
|
+
} catch (staticErr) {
|
|
3319
|
+
console.warn(
|
|
3320
|
+
"[discoverMarkets] Static bundle fallback also failed:",
|
|
3321
|
+
staticErr instanceof Error ? staticErr.message : staticErr
|
|
3322
|
+
);
|
|
3323
|
+
}
|
|
3324
|
+
} else {
|
|
3325
|
+
console.warn(
|
|
3326
|
+
`[discoverMarkets] Static bundle has 0 entries for ${options.network} \u2014 skipping tier 3`
|
|
3327
|
+
);
|
|
3328
|
+
}
|
|
2927
3329
|
}
|
|
2928
3330
|
const accounts = rawAccounts;
|
|
2929
3331
|
const markets = [];
|
|
@@ -2963,30 +3365,166 @@ async function discoverMarkets(connection, programId, options = {}) {
|
|
|
2963
3365
|
}
|
|
2964
3366
|
return markets;
|
|
2965
3367
|
}
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
3368
|
+
async function getMarketsByAddress(connection, programId, addresses, options = {}) {
|
|
3369
|
+
if (addresses.length === 0) return [];
|
|
3370
|
+
const {
|
|
3371
|
+
batchSize = 100,
|
|
3372
|
+
interBatchDelayMs = 0
|
|
3373
|
+
} = options;
|
|
3374
|
+
const effectiveBatchSize = Math.max(1, Math.min(batchSize, 100));
|
|
3375
|
+
const fetched = [];
|
|
3376
|
+
for (let offset = 0; offset < addresses.length; offset += effectiveBatchSize) {
|
|
3377
|
+
const batch = addresses.slice(offset, offset + effectiveBatchSize);
|
|
3378
|
+
const response = await connection.getMultipleAccountsInfo(batch);
|
|
3379
|
+
for (let i = 0; i < batch.length; i++) {
|
|
3380
|
+
const info = response[i];
|
|
3381
|
+
if (info && info.data) {
|
|
3382
|
+
if (!info.owner.equals(programId)) {
|
|
3383
|
+
console.warn(
|
|
3384
|
+
`[getMarketsByAddress] Skipping ${batch[i].toBase58()}: owner mismatch (expected ${programId.toBase58()}, got ${info.owner.toBase58()})`
|
|
3385
|
+
);
|
|
3386
|
+
continue;
|
|
3387
|
+
}
|
|
3388
|
+
fetched.push({ pubkey: batch[i], data: info.data });
|
|
3389
|
+
}
|
|
3390
|
+
}
|
|
3391
|
+
if (interBatchDelayMs > 0 && offset + effectiveBatchSize < addresses.length) {
|
|
3392
|
+
await new Promise((r) => setTimeout(r, interBatchDelayMs));
|
|
3393
|
+
}
|
|
3394
|
+
}
|
|
3395
|
+
const markets = [];
|
|
3396
|
+
for (const entry of fetched) {
|
|
3397
|
+
if (!entry) continue;
|
|
3398
|
+
const { pubkey, data: rawData } = entry;
|
|
3399
|
+
const data = new Uint8Array(rawData);
|
|
3400
|
+
let valid = true;
|
|
3401
|
+
for (let i = 0; i < MAGIC_BYTES.length; i++) {
|
|
3402
|
+
if (data[i] !== MAGIC_BYTES[i]) {
|
|
3403
|
+
valid = false;
|
|
3404
|
+
break;
|
|
3405
|
+
}
|
|
3406
|
+
}
|
|
3407
|
+
if (!valid) {
|
|
3408
|
+
console.warn(
|
|
3409
|
+
`[getMarketsByAddress] Skipping ${pubkey.toBase58()}: invalid magic bytes`
|
|
3410
|
+
);
|
|
3411
|
+
continue;
|
|
3412
|
+
}
|
|
3413
|
+
const layout = detectSlabLayout(data.length, data);
|
|
3414
|
+
if (!layout) {
|
|
3415
|
+
console.warn(
|
|
3416
|
+
`[getMarketsByAddress] Skipping ${pubkey.toBase58()}: unrecognized layout for dataSize=${data.length}`
|
|
3417
|
+
);
|
|
3418
|
+
continue;
|
|
3419
|
+
}
|
|
3420
|
+
try {
|
|
3421
|
+
const header = parseHeader(data);
|
|
3422
|
+
const config = parseConfig(data, layout);
|
|
3423
|
+
const engine = parseEngineLight(data, layout, layout.maxAccounts);
|
|
3424
|
+
const params = parseParams(data, layout);
|
|
3425
|
+
markets.push({ slabAddress: pubkey, programId, header, config, engine, params });
|
|
3426
|
+
} catch (err) {
|
|
3427
|
+
console.warn(
|
|
3428
|
+
`[getMarketsByAddress] Failed to parse account ${pubkey.toBase58()}:`,
|
|
3429
|
+
err instanceof Error ? err.message : err
|
|
3430
|
+
);
|
|
3431
|
+
}
|
|
3432
|
+
}
|
|
3433
|
+
return markets;
|
|
3434
|
+
}
|
|
3435
|
+
async function discoverMarketsViaApi(connection, programId, apiBaseUrl, options = {}) {
|
|
3436
|
+
const { timeoutMs = 1e4, onChainOptions } = options;
|
|
3437
|
+
const base = apiBaseUrl.replace(/\/+$/, "");
|
|
3438
|
+
const url = `${base}/markets`;
|
|
3439
|
+
const controller = new AbortController();
|
|
3440
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
3441
|
+
let response;
|
|
3442
|
+
try {
|
|
3443
|
+
response = await fetch(url, {
|
|
3444
|
+
method: "GET",
|
|
3445
|
+
headers: { Accept: "application/json" },
|
|
3446
|
+
signal: controller.signal
|
|
3447
|
+
});
|
|
3448
|
+
} finally {
|
|
3449
|
+
clearTimeout(timer);
|
|
3450
|
+
}
|
|
3451
|
+
if (!response.ok) {
|
|
3452
|
+
throw new Error(
|
|
3453
|
+
`[discoverMarketsViaApi] API returned ${response.status} ${response.statusText} from ${url}`
|
|
3454
|
+
);
|
|
3455
|
+
}
|
|
3456
|
+
const body = await response.json();
|
|
3457
|
+
const apiMarkets = body.markets;
|
|
3458
|
+
if (!Array.isArray(apiMarkets) || apiMarkets.length === 0) {
|
|
3459
|
+
console.warn("[discoverMarketsViaApi] API returned 0 markets");
|
|
3460
|
+
return [];
|
|
3461
|
+
}
|
|
3462
|
+
const addresses = [];
|
|
3463
|
+
for (const entry of apiMarkets) {
|
|
3464
|
+
if (!entry.slab_address || typeof entry.slab_address !== "string") continue;
|
|
3465
|
+
try {
|
|
3466
|
+
addresses.push(new PublicKey6(entry.slab_address));
|
|
3467
|
+
} catch {
|
|
3468
|
+
console.warn(
|
|
3469
|
+
`[discoverMarketsViaApi] Skipping invalid slab address: ${entry.slab_address}`
|
|
3470
|
+
);
|
|
3471
|
+
}
|
|
3472
|
+
}
|
|
3473
|
+
if (addresses.length === 0) {
|
|
3474
|
+
console.warn("[discoverMarketsViaApi] No valid slab addresses from API");
|
|
3475
|
+
return [];
|
|
3476
|
+
}
|
|
3477
|
+
console.log(
|
|
3478
|
+
`[discoverMarketsViaApi] API returned ${addresses.length} slab addresses, fetching on-chain data`
|
|
3479
|
+
);
|
|
3480
|
+
return getMarketsByAddress(connection, programId, addresses, onChainOptions);
|
|
3481
|
+
}
|
|
3482
|
+
async function discoverMarketsViaStaticBundle(connection, programId, entries, options = {}) {
|
|
3483
|
+
if (entries.length === 0) return [];
|
|
3484
|
+
const addresses = [];
|
|
3485
|
+
for (const entry of entries) {
|
|
3486
|
+
if (!entry.slabAddress || typeof entry.slabAddress !== "string") continue;
|
|
3487
|
+
try {
|
|
3488
|
+
addresses.push(new PublicKey6(entry.slabAddress));
|
|
3489
|
+
} catch {
|
|
3490
|
+
console.warn(
|
|
3491
|
+
`[discoverMarketsViaStaticBundle] Skipping invalid slab address: ${entry.slabAddress}`
|
|
3492
|
+
);
|
|
3493
|
+
}
|
|
3494
|
+
}
|
|
3495
|
+
if (addresses.length === 0) {
|
|
3496
|
+
console.warn("[discoverMarketsViaStaticBundle] No valid slab addresses in static bundle");
|
|
3497
|
+
return [];
|
|
3498
|
+
}
|
|
3499
|
+
console.log(
|
|
3500
|
+
`[discoverMarketsViaStaticBundle] Fetching ${addresses.length} slab addresses on-chain`
|
|
3501
|
+
);
|
|
3502
|
+
return getMarketsByAddress(connection, programId, addresses, options.onChainOptions);
|
|
3503
|
+
}
|
|
3504
|
+
|
|
3505
|
+
// src/solana/dex-oracle.ts
|
|
3506
|
+
import { PublicKey as PublicKey7 } from "@solana/web3.js";
|
|
3507
|
+
function detectDexType(ownerProgramId) {
|
|
3508
|
+
if (ownerProgramId.equals(PUMPSWAP_PROGRAM_ID)) return "pumpswap";
|
|
3509
|
+
if (ownerProgramId.equals(RAYDIUM_CLMM_PROGRAM_ID)) return "raydium-clmm";
|
|
3510
|
+
if (ownerProgramId.equals(METEORA_DLMM_PROGRAM_ID)) return "meteora-dlmm";
|
|
3511
|
+
return null;
|
|
3512
|
+
}
|
|
3513
|
+
function parseDexPool(dexType, poolAddress, data) {
|
|
3514
|
+
switch (dexType) {
|
|
3515
|
+
case "pumpswap":
|
|
3516
|
+
return parsePumpSwapPool(poolAddress, data);
|
|
3517
|
+
case "raydium-clmm":
|
|
3518
|
+
return parseRaydiumClmmPool(poolAddress, data);
|
|
3519
|
+
case "meteora-dlmm":
|
|
3520
|
+
return parseMeteoraPool(poolAddress, data);
|
|
3521
|
+
}
|
|
3522
|
+
}
|
|
3523
|
+
function computeDexSpotPriceE6(dexType, data, vaultData) {
|
|
3524
|
+
switch (dexType) {
|
|
3525
|
+
case "pumpswap":
|
|
3526
|
+
if (!vaultData) throw new Error("PumpSwap requires vaultData (base and quote vault accounts)");
|
|
3527
|
+
return computePumpSwapPriceE6(data, vaultData);
|
|
2990
3528
|
case "raydium-clmm":
|
|
2991
3529
|
return computeRaydiumClmmPriceE6(data);
|
|
2992
3530
|
case "meteora-dlmm":
|
|
@@ -3001,10 +3539,10 @@ function parsePumpSwapPool(poolAddress, data) {
|
|
|
3001
3539
|
return {
|
|
3002
3540
|
dexType: "pumpswap",
|
|
3003
3541
|
poolAddress,
|
|
3004
|
-
baseMint: new
|
|
3005
|
-
quoteMint: new
|
|
3006
|
-
baseVault: new
|
|
3007
|
-
quoteVault: new
|
|
3542
|
+
baseMint: new PublicKey7(data.slice(35, 67)),
|
|
3543
|
+
quoteMint: new PublicKey7(data.slice(67, 99)),
|
|
3544
|
+
baseVault: new PublicKey7(data.slice(131, 163)),
|
|
3545
|
+
quoteVault: new PublicKey7(data.slice(163, 195))
|
|
3008
3546
|
};
|
|
3009
3547
|
}
|
|
3010
3548
|
var SPL_TOKEN_AMOUNT_MIN_LEN = 72;
|
|
@@ -3030,8 +3568,8 @@ function parseRaydiumClmmPool(poolAddress, data) {
|
|
|
3030
3568
|
return {
|
|
3031
3569
|
dexType: "raydium-clmm",
|
|
3032
3570
|
poolAddress,
|
|
3033
|
-
baseMint: new
|
|
3034
|
-
quoteMint: new
|
|
3571
|
+
baseMint: new PublicKey7(data.slice(73, 105)),
|
|
3572
|
+
quoteMint: new PublicKey7(data.slice(105, 137))
|
|
3035
3573
|
};
|
|
3036
3574
|
}
|
|
3037
3575
|
var MAX_TOKEN_DECIMALS = 24;
|
|
@@ -3049,9 +3587,7 @@ function computeRaydiumClmmPriceE6(data) {
|
|
|
3049
3587
|
}
|
|
3050
3588
|
const sqrtPriceX64 = readU128LE3(dv3, 253);
|
|
3051
3589
|
if (sqrtPriceX64 === 0n) return 0n;
|
|
3052
|
-
const
|
|
3053
|
-
const term = scaledSqrt >> 64n;
|
|
3054
|
-
const priceE6Raw = term * sqrtPriceX64 >> 64n;
|
|
3590
|
+
const priceE6Raw = sqrtPriceX64 * sqrtPriceX64 * 1000000n >> 128n;
|
|
3055
3591
|
const decimalDiff = 6 + decimals0 - decimals1;
|
|
3056
3592
|
const adjustedDiff = decimalDiff - 6;
|
|
3057
3593
|
if (adjustedDiff >= 0) {
|
|
@@ -3070,8 +3606,8 @@ function parseMeteoraPool(poolAddress, data) {
|
|
|
3070
3606
|
return {
|
|
3071
3607
|
dexType: "meteora-dlmm",
|
|
3072
3608
|
poolAddress,
|
|
3073
|
-
baseMint: new
|
|
3074
|
-
quoteMint: new
|
|
3609
|
+
baseMint: new PublicKey7(data.slice(81, 113)),
|
|
3610
|
+
quoteMint: new PublicKey7(data.slice(113, 145))
|
|
3075
3611
|
};
|
|
3076
3612
|
}
|
|
3077
3613
|
var MAX_BIN_STEP = 1e4;
|
|
@@ -3104,7 +3640,14 @@ function computeMeteoraDlmmPriceE6(data) {
|
|
|
3104
3640
|
let exp = isNeg ? BigInt(-activeId) : BigInt(activeId);
|
|
3105
3641
|
let result = SCALE;
|
|
3106
3642
|
let b = base;
|
|
3643
|
+
let iterations = 0;
|
|
3644
|
+
const MAX_ITERATIONS = 25;
|
|
3107
3645
|
while (exp > 0n) {
|
|
3646
|
+
if (iterations++ >= MAX_ITERATIONS) {
|
|
3647
|
+
throw new Error(
|
|
3648
|
+
`Meteora DLMM: exponentiation loop exceeded ${MAX_ITERATIONS} iterations (activeId=${activeId})`
|
|
3649
|
+
);
|
|
3650
|
+
}
|
|
3108
3651
|
if (exp & 1n) {
|
|
3109
3652
|
result = result * b / SCALE;
|
|
3110
3653
|
}
|
|
@@ -3135,6 +3678,7 @@ function readU128LE3(dv3, offset) {
|
|
|
3135
3678
|
var CHAINLINK_MIN_SIZE = 224;
|
|
3136
3679
|
var MAX_DECIMALS = 18;
|
|
3137
3680
|
var CHAINLINK_DECIMALS_OFFSET = 138;
|
|
3681
|
+
var CHAINLINK_TIMESTAMP_OFFSET = 168;
|
|
3138
3682
|
var CHAINLINK_ANSWER_OFFSET = 216;
|
|
3139
3683
|
function readU82(data, off) {
|
|
3140
3684
|
return data[off];
|
|
@@ -3142,7 +3686,7 @@ function readU82(data, off) {
|
|
|
3142
3686
|
function readBigInt64LE(data, off) {
|
|
3143
3687
|
return new DataView(data.buffer, data.byteOffset, data.byteLength).getBigInt64(off, true);
|
|
3144
3688
|
}
|
|
3145
|
-
function parseChainlinkPrice(data) {
|
|
3689
|
+
function parseChainlinkPrice(data, options) {
|
|
3146
3690
|
if (data.length < CHAINLINK_MIN_SIZE) {
|
|
3147
3691
|
throw new Error(
|
|
3148
3692
|
`Oracle account data too small: ${data.length} bytes (need at least ${CHAINLINK_MIN_SIZE})`
|
|
@@ -3160,7 +3704,18 @@ function parseChainlinkPrice(data) {
|
|
|
3160
3704
|
`Oracle price is non-positive: ${price}`
|
|
3161
3705
|
);
|
|
3162
3706
|
}
|
|
3163
|
-
|
|
3707
|
+
const updatedAtBig = readBigInt64LE(data, CHAINLINK_TIMESTAMP_OFFSET);
|
|
3708
|
+
const updatedAt = Number(updatedAtBig);
|
|
3709
|
+
if (options?.maxStalenessSeconds !== void 0 && updatedAt > 0) {
|
|
3710
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
3711
|
+
const age = now - updatedAt;
|
|
3712
|
+
if (age > options.maxStalenessSeconds) {
|
|
3713
|
+
throw new Error(
|
|
3714
|
+
`Oracle price is stale: last updated ${age}s ago (max ${options.maxStalenessSeconds}s)`
|
|
3715
|
+
);
|
|
3716
|
+
}
|
|
3717
|
+
}
|
|
3718
|
+
return { price, decimals, updatedAt: updatedAt > 0 ? updatedAt : void 0 };
|
|
3164
3719
|
}
|
|
3165
3720
|
function isValidChainlinkOracle(data) {
|
|
3166
3721
|
try {
|
|
@@ -3172,15 +3727,19 @@ function isValidChainlinkOracle(data) {
|
|
|
3172
3727
|
}
|
|
3173
3728
|
|
|
3174
3729
|
// src/solana/token-program.ts
|
|
3175
|
-
import { PublicKey as
|
|
3730
|
+
import { PublicKey as PublicKey8 } from "@solana/web3.js";
|
|
3176
3731
|
import { TOKEN_PROGRAM_ID as TOKEN_PROGRAM_ID3 } from "@solana/spl-token";
|
|
3177
|
-
var TOKEN_2022_PROGRAM_ID = new
|
|
3732
|
+
var TOKEN_2022_PROGRAM_ID = new PublicKey8(
|
|
3178
3733
|
"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"
|
|
3179
3734
|
);
|
|
3180
3735
|
async function detectTokenProgram(connection, mint) {
|
|
3181
3736
|
const info = await connection.getAccountInfo(mint);
|
|
3182
3737
|
if (!info) throw new Error(`Mint account not found: ${mint.toBase58()}`);
|
|
3183
|
-
|
|
3738
|
+
if (info.owner.equals(TOKEN_PROGRAM_ID3)) return TOKEN_PROGRAM_ID3;
|
|
3739
|
+
if (info.owner.equals(TOKEN_2022_PROGRAM_ID)) return TOKEN_2022_PROGRAM_ID;
|
|
3740
|
+
throw new Error(
|
|
3741
|
+
`Mint ${mint.toBase58()} is owned by ${info.owner.toBase58()}, which is neither TOKEN_PROGRAM_ID nor TOKEN_2022_PROGRAM_ID`
|
|
3742
|
+
);
|
|
3184
3743
|
}
|
|
3185
3744
|
function isToken2022(tokenProgramId) {
|
|
3186
3745
|
return tokenProgramId.equals(TOKEN_2022_PROGRAM_ID);
|
|
@@ -3190,11 +3749,11 @@ function isStandardToken(tokenProgramId) {
|
|
|
3190
3749
|
}
|
|
3191
3750
|
|
|
3192
3751
|
// src/solana/stake.ts
|
|
3193
|
-
import { PublicKey as
|
|
3752
|
+
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";
|
|
3194
3753
|
import { TOKEN_PROGRAM_ID as TOKEN_PROGRAM_ID4 } from "@solana/spl-token";
|
|
3195
3754
|
|
|
3196
3755
|
// src/config/program-ids.ts
|
|
3197
|
-
import { PublicKey as
|
|
3756
|
+
import { PublicKey as PublicKey9 } from "@solana/web3.js";
|
|
3198
3757
|
function safeEnv(key) {
|
|
3199
3758
|
try {
|
|
3200
3759
|
return typeof process !== "undefined" && process?.env ? process.env[key] : void 0;
|
|
@@ -3213,25 +3772,29 @@ var PROGRAM_IDS = {
|
|
|
3213
3772
|
}
|
|
3214
3773
|
};
|
|
3215
3774
|
function getProgramId(network) {
|
|
3216
|
-
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
|
|
3775
|
+
if (!network) {
|
|
3776
|
+
const override = safeEnv("PROGRAM_ID");
|
|
3777
|
+
if (override) {
|
|
3778
|
+
console.warn(
|
|
3779
|
+
`[percolator-sdk] PROGRAM_ID env override active: ${override} \u2014 ensure this points to a trusted program`
|
|
3780
|
+
);
|
|
3781
|
+
return new PublicKey9(override);
|
|
3782
|
+
}
|
|
3222
3783
|
}
|
|
3223
3784
|
const detectedNetwork = getCurrentNetwork();
|
|
3224
3785
|
const targetNetwork = network ?? detectedNetwork;
|
|
3225
3786
|
const programId = PROGRAM_IDS[targetNetwork].percolator;
|
|
3226
|
-
return new
|
|
3787
|
+
return new PublicKey9(programId);
|
|
3227
3788
|
}
|
|
3228
3789
|
function getMatcherProgramId(network) {
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3790
|
+
if (!network) {
|
|
3791
|
+
const override = safeEnv("MATCHER_PROGRAM_ID");
|
|
3792
|
+
if (override) {
|
|
3793
|
+
console.warn(
|
|
3794
|
+
`[percolator-sdk] MATCHER_PROGRAM_ID env override active: ${override} \u2014 ensure this points to a trusted program`
|
|
3795
|
+
);
|
|
3796
|
+
return new PublicKey9(override);
|
|
3797
|
+
}
|
|
3235
3798
|
}
|
|
3236
3799
|
const detectedNetwork = getCurrentNetwork();
|
|
3237
3800
|
const targetNetwork = network ?? detectedNetwork;
|
|
@@ -3239,7 +3802,7 @@ function getMatcherProgramId(network) {
|
|
|
3239
3802
|
if (!programId) {
|
|
3240
3803
|
throw new Error(`Matcher program not deployed on ${targetNetwork}`);
|
|
3241
3804
|
}
|
|
3242
|
-
return new
|
|
3805
|
+
return new PublicKey9(programId);
|
|
3243
3806
|
}
|
|
3244
3807
|
function getCurrentNetwork() {
|
|
3245
3808
|
const network = safeEnv("NETWORK")?.toLowerCase();
|
|
@@ -3261,7 +3824,7 @@ function getStakeProgramId(network) {
|
|
|
3261
3824
|
console.warn(
|
|
3262
3825
|
`[percolator-sdk] STAKE_PROGRAM_ID env override active: ${override} \u2014 ensure this points to a trusted program`
|
|
3263
3826
|
);
|
|
3264
|
-
return new
|
|
3827
|
+
return new PublicKey10(override);
|
|
3265
3828
|
}
|
|
3266
3829
|
const detectedNetwork = network ?? (() => {
|
|
3267
3830
|
const n = safeEnv("NEXT_PUBLIC_DEFAULT_NETWORK")?.toLowerCase() ?? safeEnv("NETWORK")?.toLowerCase() ?? "";
|
|
@@ -3273,9 +3836,9 @@ function getStakeProgramId(network) {
|
|
|
3273
3836
|
`Stake program not deployed on ${detectedNetwork}. Set STAKE_PROGRAM_ID env var or wait for DevOps to deploy and update STAKE_PROGRAM_IDS.mainnet.`
|
|
3274
3837
|
);
|
|
3275
3838
|
}
|
|
3276
|
-
return new
|
|
3839
|
+
return new PublicKey10(id);
|
|
3277
3840
|
}
|
|
3278
|
-
var STAKE_PROGRAM_ID = new
|
|
3841
|
+
var STAKE_PROGRAM_ID = new PublicKey10(STAKE_PROGRAM_IDS.devnet);
|
|
3279
3842
|
var STAKE_IX = {
|
|
3280
3843
|
InitPool: 0,
|
|
3281
3844
|
Deposit: 1,
|
|
@@ -3302,19 +3865,19 @@ var STAKE_IX = {
|
|
|
3302
3865
|
};
|
|
3303
3866
|
var TEXT = new TextEncoder();
|
|
3304
3867
|
function deriveStakePool(slab, programId) {
|
|
3305
|
-
return
|
|
3868
|
+
return PublicKey10.findProgramAddressSync(
|
|
3306
3869
|
[TEXT.encode("stake_pool"), slab.toBytes()],
|
|
3307
3870
|
programId ?? getStakeProgramId()
|
|
3308
3871
|
);
|
|
3309
3872
|
}
|
|
3310
3873
|
function deriveStakeVaultAuth(pool, programId) {
|
|
3311
|
-
return
|
|
3874
|
+
return PublicKey10.findProgramAddressSync(
|
|
3312
3875
|
[TEXT.encode("vault_auth"), pool.toBytes()],
|
|
3313
3876
|
programId ?? getStakeProgramId()
|
|
3314
3877
|
);
|
|
3315
3878
|
}
|
|
3316
3879
|
function deriveDepositPda(pool, user, programId) {
|
|
3317
|
-
return
|
|
3880
|
+
return PublicKey10.findProgramAddressSync(
|
|
3318
3881
|
[TEXT.encode("deposit"), pool.toBytes(), user.toBytes()],
|
|
3319
3882
|
programId ?? getStakeProgramId()
|
|
3320
3883
|
);
|
|
@@ -3336,6 +3899,9 @@ function readU16LE3(data, off) {
|
|
|
3336
3899
|
);
|
|
3337
3900
|
}
|
|
3338
3901
|
function u64Le(v) {
|
|
3902
|
+
if (typeof v === "number" && !Number.isSafeInteger(v)) {
|
|
3903
|
+
throw new Error(`u64Le: number ${v} exceeds Number.MAX_SAFE_INTEGER \u2014 use BigInt`);
|
|
3904
|
+
}
|
|
3339
3905
|
const big = BigInt(v);
|
|
3340
3906
|
if (big < 0n) throw new Error(`u64Le: value must be non-negative, got ${big}`);
|
|
3341
3907
|
if (big > 0xFFFFFFFFFFFFFFFFn) throw new Error(`u64Le: value exceeds u64 max`);
|
|
@@ -3344,6 +3910,9 @@ function u64Le(v) {
|
|
|
3344
3910
|
return arr;
|
|
3345
3911
|
}
|
|
3346
3912
|
function u128Le(v) {
|
|
3913
|
+
if (typeof v === "number" && !Number.isSafeInteger(v)) {
|
|
3914
|
+
throw new Error(`u128Le: number ${v} exceeds Number.MAX_SAFE_INTEGER \u2014 use BigInt`);
|
|
3915
|
+
}
|
|
3347
3916
|
const big = BigInt(v);
|
|
3348
3917
|
if (big < 0n) throw new Error(`u128Le: value must be non-negative, got ${big}`);
|
|
3349
3918
|
if (big > (1n << 128n) - 1n) throw new Error(`u128Le: value exceeds u128 max`);
|
|
@@ -3354,7 +3923,7 @@ function u128Le(v) {
|
|
|
3354
3923
|
return arr;
|
|
3355
3924
|
}
|
|
3356
3925
|
function u16Le(v) {
|
|
3357
|
-
if (v < 0 || v > 65535) throw new Error(`u16Le: value
|
|
3926
|
+
if (!Number.isInteger(v) || v < 0 || v > 65535) throw new Error(`u16Le: value must be integer in range 0..65535, got ${v}`);
|
|
3358
3927
|
const arr = new Uint8Array(2);
|
|
3359
3928
|
new DataView(arr.buffer).setUint16(0, v, true);
|
|
3360
3929
|
return arr;
|
|
@@ -3465,15 +4034,15 @@ function decodeStakePool(data) {
|
|
|
3465
4034
|
const adminTransferred = bytes[off] === 1;
|
|
3466
4035
|
off += 1;
|
|
3467
4036
|
off += 4;
|
|
3468
|
-
const slab = new
|
|
4037
|
+
const slab = new PublicKey10(bytes.subarray(off, off + 32));
|
|
3469
4038
|
off += 32;
|
|
3470
|
-
const admin = new
|
|
4039
|
+
const admin = new PublicKey10(bytes.subarray(off, off + 32));
|
|
3471
4040
|
off += 32;
|
|
3472
|
-
const collateralMint = new
|
|
4041
|
+
const collateralMint = new PublicKey10(bytes.subarray(off, off + 32));
|
|
3473
4042
|
off += 32;
|
|
3474
|
-
const lpMint = new
|
|
4043
|
+
const lpMint = new PublicKey10(bytes.subarray(off, off + 32));
|
|
3475
4044
|
off += 32;
|
|
3476
|
-
const vault = new
|
|
4045
|
+
const vault = new PublicKey10(bytes.subarray(off, off + 32));
|
|
3477
4046
|
off += 32;
|
|
3478
4047
|
const totalDeposited = readU64LE4(bytes, off);
|
|
3479
4048
|
off += 8;
|
|
@@ -3489,7 +4058,7 @@ function decodeStakePool(data) {
|
|
|
3489
4058
|
off += 8;
|
|
3490
4059
|
const totalWithdrawn = readU64LE4(bytes, off);
|
|
3491
4060
|
off += 8;
|
|
3492
|
-
const percolatorProgram = new
|
|
4061
|
+
const percolatorProgram = new PublicKey10(bytes.subarray(off, off + 32));
|
|
3493
4062
|
off += 32;
|
|
3494
4063
|
const totalFeesEarned = readU64LE4(bytes, off);
|
|
3495
4064
|
off += 8;
|
|
@@ -3609,7 +4178,9 @@ function computePnlPct(pnl, capital) {
|
|
|
3609
4178
|
}
|
|
3610
4179
|
function isAdlTriggered(slabData) {
|
|
3611
4180
|
const layout = detectSlabLayout(slabData.length);
|
|
3612
|
-
if (!layout)
|
|
4181
|
+
if (!layout) {
|
|
4182
|
+
return false;
|
|
4183
|
+
}
|
|
3613
4184
|
try {
|
|
3614
4185
|
const engine = parseEngine(slabData);
|
|
3615
4186
|
if (engine.pnlPosTot === 0n) return false;
|
|
@@ -3652,6 +4223,14 @@ function rankAdlPositions(slabData) {
|
|
|
3652
4223
|
if (account.kind !== 0 /* User */) continue;
|
|
3653
4224
|
if (account.positionSize === 0n) continue;
|
|
3654
4225
|
const side = account.positionSize > 0n ? "long" : "short";
|
|
4226
|
+
if (side === "long" && account.positionSize <= 0n) {
|
|
4227
|
+
console.warn(`[fetchAdlRankedPositions] account idx=${idx}: side=long but positionSize=${account.positionSize}`);
|
|
4228
|
+
continue;
|
|
4229
|
+
}
|
|
4230
|
+
if (side === "short" && account.positionSize >= 0n) {
|
|
4231
|
+
console.warn(`[fetchAdlRankedPositions] account idx=${idx}: side=short but positionSize=${account.positionSize}`);
|
|
4232
|
+
continue;
|
|
4233
|
+
}
|
|
3655
4234
|
const pnlPct = computePnlPct(account.pnl, account.capital);
|
|
3656
4235
|
positions.push({
|
|
3657
4236
|
idx,
|
|
@@ -3725,7 +4304,11 @@ function parseAdlEvent(logs) {
|
|
|
3725
4304
|
}
|
|
3726
4305
|
if (tag !== ADL_EVENT_TAG) continue;
|
|
3727
4306
|
try {
|
|
3728
|
-
const
|
|
4307
|
+
const targetIdxBig = BigInt(match[2]);
|
|
4308
|
+
if (targetIdxBig < 0n || targetIdxBig > 65535n) {
|
|
4309
|
+
continue;
|
|
4310
|
+
}
|
|
4311
|
+
const targetIdx = Number(targetIdxBig);
|
|
3729
4312
|
const price = BigInt(match[3]);
|
|
3730
4313
|
const closedLo = BigInt(match[4]);
|
|
3731
4314
|
const closedHi = BigInt(match[5]);
|
|
@@ -3753,9 +4336,387 @@ async function fetchAdlRankings(apiBase, slab, fetchFn = fetch) {
|
|
|
3753
4336
|
);
|
|
3754
4337
|
}
|
|
3755
4338
|
const json = await res.json();
|
|
4339
|
+
if (typeof json !== "object" || json === null) {
|
|
4340
|
+
throw new Error("fetchAdlRankings: API returned non-object response");
|
|
4341
|
+
}
|
|
4342
|
+
const obj = json;
|
|
4343
|
+
if (!Array.isArray(obj.rankings)) {
|
|
4344
|
+
throw new Error("fetchAdlRankings: API response missing rankings array");
|
|
4345
|
+
}
|
|
4346
|
+
for (const entry of obj.rankings) {
|
|
4347
|
+
if (typeof entry !== "object" || entry === null) {
|
|
4348
|
+
throw new Error("fetchAdlRankings: invalid ranking entry (not an object)");
|
|
4349
|
+
}
|
|
4350
|
+
const r = entry;
|
|
4351
|
+
if (typeof r.idx !== "number" || !Number.isInteger(r.idx) || r.idx < 0) {
|
|
4352
|
+
throw new Error(`fetchAdlRankings: invalid ranking idx: ${r.idx}`);
|
|
4353
|
+
}
|
|
4354
|
+
}
|
|
3756
4355
|
return json;
|
|
3757
4356
|
}
|
|
3758
4357
|
|
|
4358
|
+
// src/solana/rpc-pool.ts
|
|
4359
|
+
import {
|
|
4360
|
+
Connection as Connection5
|
|
4361
|
+
} from "@solana/web3.js";
|
|
4362
|
+
async function checkRpcHealth(endpoint, timeoutMs = 5e3) {
|
|
4363
|
+
const conn = new Connection5(endpoint, { commitment: "processed" });
|
|
4364
|
+
const start = performance.now();
|
|
4365
|
+
const timeout = rejectAfter(timeoutMs, `Health probe timed out after ${timeoutMs}ms`);
|
|
4366
|
+
try {
|
|
4367
|
+
const slot = await Promise.race([
|
|
4368
|
+
conn.getSlot("processed"),
|
|
4369
|
+
timeout.promise
|
|
4370
|
+
]);
|
|
4371
|
+
const latencyMs = Math.round(performance.now() - start);
|
|
4372
|
+
return { endpoint, healthy: true, latencyMs, slot };
|
|
4373
|
+
} catch (err) {
|
|
4374
|
+
const latencyMs = Math.round(performance.now() - start);
|
|
4375
|
+
return {
|
|
4376
|
+
endpoint,
|
|
4377
|
+
healthy: false,
|
|
4378
|
+
latencyMs,
|
|
4379
|
+
slot: 0,
|
|
4380
|
+
error: err instanceof Error ? err.message : String(err)
|
|
4381
|
+
};
|
|
4382
|
+
} finally {
|
|
4383
|
+
timeout.cancel();
|
|
4384
|
+
}
|
|
4385
|
+
}
|
|
4386
|
+
function resolveRetryConfig(cfg) {
|
|
4387
|
+
if (cfg === false) return null;
|
|
4388
|
+
const c = cfg ?? {};
|
|
4389
|
+
return {
|
|
4390
|
+
maxRetries: c.maxRetries ?? 3,
|
|
4391
|
+
baseDelayMs: c.baseDelayMs ?? 500,
|
|
4392
|
+
maxDelayMs: c.maxDelayMs ?? 1e4,
|
|
4393
|
+
jitterFactor: Math.max(0, Math.min(1, c.jitterFactor ?? 0.25)),
|
|
4394
|
+
retryableStatusCodes: c.retryableStatusCodes ?? [429, 502, 503, 504]
|
|
4395
|
+
};
|
|
4396
|
+
}
|
|
4397
|
+
function normalizeEndpoint(ep) {
|
|
4398
|
+
if (typeof ep === "string") return { url: ep };
|
|
4399
|
+
return ep;
|
|
4400
|
+
}
|
|
4401
|
+
function endpointLabel(ep) {
|
|
4402
|
+
if (ep.label) return ep.label;
|
|
4403
|
+
try {
|
|
4404
|
+
return new URL(ep.url).hostname;
|
|
4405
|
+
} catch {
|
|
4406
|
+
return ep.url.slice(0, 40);
|
|
4407
|
+
}
|
|
4408
|
+
}
|
|
4409
|
+
function isRetryable(err, codes) {
|
|
4410
|
+
if (!err) return false;
|
|
4411
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
4412
|
+
for (const code of codes) {
|
|
4413
|
+
if (msg.includes(String(code))) return true;
|
|
4414
|
+
}
|
|
4415
|
+
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")) {
|
|
4416
|
+
return true;
|
|
4417
|
+
}
|
|
4418
|
+
return false;
|
|
4419
|
+
}
|
|
4420
|
+
function computeDelay(attempt, config) {
|
|
4421
|
+
const raw = Math.min(
|
|
4422
|
+
config.baseDelayMs * Math.pow(2, attempt),
|
|
4423
|
+
config.maxDelayMs
|
|
4424
|
+
);
|
|
4425
|
+
const jitter = Math.floor(Math.random() * raw * config.jitterFactor);
|
|
4426
|
+
return raw + jitter;
|
|
4427
|
+
}
|
|
4428
|
+
function rejectAfter(ms, message) {
|
|
4429
|
+
let timer;
|
|
4430
|
+
const promise = new Promise((_, reject) => {
|
|
4431
|
+
timer = setTimeout(() => reject(new Error(message)), ms);
|
|
4432
|
+
});
|
|
4433
|
+
return { promise, cancel: () => clearTimeout(timer) };
|
|
4434
|
+
}
|
|
4435
|
+
function sleep(ms) {
|
|
4436
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
4437
|
+
}
|
|
4438
|
+
function redactUrl(raw) {
|
|
4439
|
+
try {
|
|
4440
|
+
const u = new URL(raw);
|
|
4441
|
+
const sensitive = /^(api[-_]?key|access[-_]?token|auth[-_]?token|token|secret|key|password|bearer|credential|jwt)$/i;
|
|
4442
|
+
for (const k of [...u.searchParams.keys()]) {
|
|
4443
|
+
if (sensitive.test(k)) {
|
|
4444
|
+
u.searchParams.set(k, "***");
|
|
4445
|
+
}
|
|
4446
|
+
}
|
|
4447
|
+
return u.toString();
|
|
4448
|
+
} catch {
|
|
4449
|
+
return raw;
|
|
4450
|
+
}
|
|
4451
|
+
}
|
|
4452
|
+
var RpcPool = class _RpcPool {
|
|
4453
|
+
endpoints;
|
|
4454
|
+
strategy;
|
|
4455
|
+
retryConfig;
|
|
4456
|
+
requestTimeoutMs;
|
|
4457
|
+
verbose;
|
|
4458
|
+
/** Round-robin index tracker. */
|
|
4459
|
+
rrIndex = 0;
|
|
4460
|
+
/** Consecutive failure threshold before marking an endpoint unhealthy. */
|
|
4461
|
+
static UNHEALTHY_THRESHOLD = 3;
|
|
4462
|
+
/** Minimum endpoints before auto-recovery is attempted. */
|
|
4463
|
+
static MIN_HEALTHY = 1;
|
|
4464
|
+
constructor(config) {
|
|
4465
|
+
if (!config.endpoints || config.endpoints.length === 0) {
|
|
4466
|
+
throw new Error("RpcPool: at least one endpoint is required");
|
|
4467
|
+
}
|
|
4468
|
+
this.strategy = config.strategy ?? "failover";
|
|
4469
|
+
this.retryConfig = resolveRetryConfig(config.retry);
|
|
4470
|
+
this.requestTimeoutMs = config.requestTimeoutMs ?? 3e4;
|
|
4471
|
+
this.verbose = config.verbose ?? true;
|
|
4472
|
+
const commitment = config.commitment ?? "confirmed";
|
|
4473
|
+
this.endpoints = config.endpoints.map((raw) => {
|
|
4474
|
+
const ep = normalizeEndpoint(raw);
|
|
4475
|
+
const connConfig = {
|
|
4476
|
+
commitment,
|
|
4477
|
+
...ep.connectionConfig
|
|
4478
|
+
};
|
|
4479
|
+
return {
|
|
4480
|
+
config: ep,
|
|
4481
|
+
connection: new Connection5(ep.url, connConfig),
|
|
4482
|
+
label: endpointLabel(ep),
|
|
4483
|
+
weight: Math.max(1, ep.weight ?? 1),
|
|
4484
|
+
failures: 0,
|
|
4485
|
+
healthy: true,
|
|
4486
|
+
lastLatencyMs: -1
|
|
4487
|
+
};
|
|
4488
|
+
});
|
|
4489
|
+
}
|
|
4490
|
+
// -----------------------------------------------------------------------
|
|
4491
|
+
// Public API
|
|
4492
|
+
// -----------------------------------------------------------------------
|
|
4493
|
+
/**
|
|
4494
|
+
* Execute a function against a pooled connection with automatic retry
|
|
4495
|
+
* and failover.
|
|
4496
|
+
*
|
|
4497
|
+
* @param fn - Async function that receives a `Connection` and returns a result.
|
|
4498
|
+
* @returns The result of `fn`.
|
|
4499
|
+
* @throws The last error if all retries and failovers are exhausted.
|
|
4500
|
+
*
|
|
4501
|
+
* @example
|
|
4502
|
+
* ```ts
|
|
4503
|
+
* const balance = await pool.call(c => c.getBalance(pubkey));
|
|
4504
|
+
* const markets = await pool.call(c => discoverMarkets(c, programId, opts));
|
|
4505
|
+
* ```
|
|
4506
|
+
*/
|
|
4507
|
+
async call(fn) {
|
|
4508
|
+
const maxAttempts = this.retryConfig ? this.retryConfig.maxRetries + 1 : 1;
|
|
4509
|
+
let lastError;
|
|
4510
|
+
const triedEndpoints = /* @__PURE__ */ new Set();
|
|
4511
|
+
const maxTotalIterations = maxAttempts + this.endpoints.length;
|
|
4512
|
+
let totalIterations = 0;
|
|
4513
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
4514
|
+
if (++totalIterations > maxTotalIterations) break;
|
|
4515
|
+
const epIdx = this.selectEndpoint(triedEndpoints);
|
|
4516
|
+
if (epIdx === -1) {
|
|
4517
|
+
break;
|
|
4518
|
+
}
|
|
4519
|
+
const ep = this.endpoints[epIdx];
|
|
4520
|
+
const timeout = rejectAfter(this.requestTimeoutMs, `RPC request timed out after ${this.requestTimeoutMs}ms (${ep.label})`);
|
|
4521
|
+
try {
|
|
4522
|
+
const result = await Promise.race([
|
|
4523
|
+
fn(ep.connection),
|
|
4524
|
+
timeout.promise
|
|
4525
|
+
]);
|
|
4526
|
+
timeout.cancel();
|
|
4527
|
+
ep.failures = 0;
|
|
4528
|
+
ep.healthy = true;
|
|
4529
|
+
return result;
|
|
4530
|
+
} catch (err) {
|
|
4531
|
+
timeout.cancel();
|
|
4532
|
+
lastError = err;
|
|
4533
|
+
ep.failures++;
|
|
4534
|
+
if (ep.failures >= _RpcPool.UNHEALTHY_THRESHOLD) {
|
|
4535
|
+
ep.healthy = false;
|
|
4536
|
+
if (this.verbose) {
|
|
4537
|
+
console.warn(
|
|
4538
|
+
`[RpcPool] Endpoint ${ep.label} marked unhealthy after ${ep.failures} consecutive failures`
|
|
4539
|
+
);
|
|
4540
|
+
}
|
|
4541
|
+
}
|
|
4542
|
+
const retryable = this.retryConfig ? isRetryable(err, this.retryConfig.retryableStatusCodes) : false;
|
|
4543
|
+
if (!retryable) {
|
|
4544
|
+
if (this.strategy === "failover" && this.endpoints.length > 1) {
|
|
4545
|
+
triedEndpoints.add(epIdx);
|
|
4546
|
+
attempt--;
|
|
4547
|
+
if (triedEndpoints.size >= this.endpoints.length) break;
|
|
4548
|
+
continue;
|
|
4549
|
+
}
|
|
4550
|
+
throw err;
|
|
4551
|
+
}
|
|
4552
|
+
if (this.verbose) {
|
|
4553
|
+
console.warn(
|
|
4554
|
+
`[RpcPool] Retryable error on ${ep.label} (attempt ${attempt + 1}/${maxAttempts}):`,
|
|
4555
|
+
err instanceof Error ? err.message : err
|
|
4556
|
+
);
|
|
4557
|
+
}
|
|
4558
|
+
if (this.strategy === "failover" && this.endpoints.length > 1) {
|
|
4559
|
+
triedEndpoints.add(epIdx);
|
|
4560
|
+
}
|
|
4561
|
+
if (attempt < maxAttempts - 1 && this.retryConfig) {
|
|
4562
|
+
const delay = computeDelay(attempt, this.retryConfig);
|
|
4563
|
+
await sleep(delay);
|
|
4564
|
+
}
|
|
4565
|
+
}
|
|
4566
|
+
}
|
|
4567
|
+
this.maybeRecoverEndpoints();
|
|
4568
|
+
throw lastError ?? new Error("RpcPool: all endpoints exhausted");
|
|
4569
|
+
}
|
|
4570
|
+
/**
|
|
4571
|
+
* Get a raw `Connection` from the current preferred endpoint.
|
|
4572
|
+
* Useful when you need to pass a Connection to external code.
|
|
4573
|
+
*
|
|
4574
|
+
* NOTE: This bypasses retry and failover logic. Prefer `call()`.
|
|
4575
|
+
*
|
|
4576
|
+
* @returns Solana Connection from the current preferred endpoint.
|
|
4577
|
+
*
|
|
4578
|
+
* @example
|
|
4579
|
+
* ```ts
|
|
4580
|
+
* const conn = pool.getConnection();
|
|
4581
|
+
* const balance = await conn.getBalance(pubkey);
|
|
4582
|
+
* ```
|
|
4583
|
+
*/
|
|
4584
|
+
getConnection() {
|
|
4585
|
+
const idx = this.selectEndpoint();
|
|
4586
|
+
if (idx === -1) {
|
|
4587
|
+
this.maybeRecoverEndpoints();
|
|
4588
|
+
return this.endpoints[0].connection;
|
|
4589
|
+
}
|
|
4590
|
+
return this.endpoints[idx].connection;
|
|
4591
|
+
}
|
|
4592
|
+
/**
|
|
4593
|
+
* Run a health check against all endpoints in the pool.
|
|
4594
|
+
*
|
|
4595
|
+
* @param timeoutMs - Per-endpoint probe timeout (default: 5000)
|
|
4596
|
+
* @returns Array of health results, one per endpoint.
|
|
4597
|
+
*
|
|
4598
|
+
* @example
|
|
4599
|
+
* ```ts
|
|
4600
|
+
* const results = await pool.healthCheck();
|
|
4601
|
+
* for (const r of results) {
|
|
4602
|
+
* console.log(`${r.endpoint}: ${r.healthy ? 'UP' : 'DOWN'} (${r.latencyMs}ms, slot ${r.slot})`);
|
|
4603
|
+
* }
|
|
4604
|
+
* ```
|
|
4605
|
+
*/
|
|
4606
|
+
async healthCheck(timeoutMs = 5e3) {
|
|
4607
|
+
const results = await Promise.all(
|
|
4608
|
+
this.endpoints.map(async (ep) => {
|
|
4609
|
+
const result = await checkRpcHealth(ep.config.url, timeoutMs);
|
|
4610
|
+
ep.lastLatencyMs = result.latencyMs;
|
|
4611
|
+
ep.healthy = result.healthy;
|
|
4612
|
+
if (result.healthy) ep.failures = 0;
|
|
4613
|
+
result.endpoint = redactUrl(result.endpoint);
|
|
4614
|
+
return result;
|
|
4615
|
+
})
|
|
4616
|
+
);
|
|
4617
|
+
return results;
|
|
4618
|
+
}
|
|
4619
|
+
/**
|
|
4620
|
+
* Get the number of endpoints in the pool.
|
|
4621
|
+
*/
|
|
4622
|
+
get size() {
|
|
4623
|
+
return this.endpoints.length;
|
|
4624
|
+
}
|
|
4625
|
+
/**
|
|
4626
|
+
* Get the number of currently healthy endpoints.
|
|
4627
|
+
*/
|
|
4628
|
+
get healthyCount() {
|
|
4629
|
+
return this.endpoints.filter((ep) => ep.healthy).length;
|
|
4630
|
+
}
|
|
4631
|
+
/**
|
|
4632
|
+
* Get endpoint labels and their current status.
|
|
4633
|
+
*
|
|
4634
|
+
* @returns Array of `{ label, url, healthy, failures, lastLatencyMs }`.
|
|
4635
|
+
*/
|
|
4636
|
+
status() {
|
|
4637
|
+
return this.endpoints.map((ep) => ({
|
|
4638
|
+
label: ep.label,
|
|
4639
|
+
url: redactUrl(ep.config.url),
|
|
4640
|
+
healthy: ep.healthy,
|
|
4641
|
+
failures: ep.failures,
|
|
4642
|
+
lastLatencyMs: ep.lastLatencyMs
|
|
4643
|
+
}));
|
|
4644
|
+
}
|
|
4645
|
+
// -----------------------------------------------------------------------
|
|
4646
|
+
// Internals
|
|
4647
|
+
// -----------------------------------------------------------------------
|
|
4648
|
+
/**
|
|
4649
|
+
* Select the next endpoint based on strategy.
|
|
4650
|
+
* Returns -1 if no endpoint is available.
|
|
4651
|
+
*/
|
|
4652
|
+
selectEndpoint(exclude) {
|
|
4653
|
+
const healthy = this.endpoints.map((ep, i) => ({ ep, i })).filter(({ ep, i }) => ep.healthy && !exclude?.has(i));
|
|
4654
|
+
if (healthy.length === 0) {
|
|
4655
|
+
const remaining = this.endpoints.map((_, i) => i).filter((i) => !exclude?.has(i));
|
|
4656
|
+
return remaining.length > 0 ? remaining[0] : -1;
|
|
4657
|
+
}
|
|
4658
|
+
if (this.strategy === "failover") {
|
|
4659
|
+
return healthy[0].i;
|
|
4660
|
+
}
|
|
4661
|
+
const totalWeight = healthy.reduce((sum, { ep }) => sum + ep.weight, 0);
|
|
4662
|
+
this.rrIndex = (this.rrIndex + 1) % totalWeight;
|
|
4663
|
+
let cumulative = 0;
|
|
4664
|
+
for (const { ep, i } of healthy) {
|
|
4665
|
+
cumulative += ep.weight;
|
|
4666
|
+
if (this.rrIndex < cumulative) return i;
|
|
4667
|
+
}
|
|
4668
|
+
return healthy[healthy.length - 1].i;
|
|
4669
|
+
}
|
|
4670
|
+
/**
|
|
4671
|
+
* If all endpoints are unhealthy, reset them so we at least try again.
|
|
4672
|
+
*/
|
|
4673
|
+
maybeRecoverEndpoints() {
|
|
4674
|
+
const healthyCount = this.endpoints.filter((ep) => ep.healthy).length;
|
|
4675
|
+
if (healthyCount < _RpcPool.MIN_HEALTHY) {
|
|
4676
|
+
if (this.verbose) {
|
|
4677
|
+
console.warn("[RpcPool] All endpoints unhealthy \u2014 resetting for recovery");
|
|
4678
|
+
}
|
|
4679
|
+
for (const ep of this.endpoints) {
|
|
4680
|
+
ep.healthy = true;
|
|
4681
|
+
ep.failures = 0;
|
|
4682
|
+
}
|
|
4683
|
+
}
|
|
4684
|
+
}
|
|
4685
|
+
};
|
|
4686
|
+
async function withRetry(fn, config) {
|
|
4687
|
+
const resolved = resolveRetryConfig(config) ?? {
|
|
4688
|
+
maxRetries: 3,
|
|
4689
|
+
baseDelayMs: 500,
|
|
4690
|
+
maxDelayMs: 1e4,
|
|
4691
|
+
jitterFactor: 0.25,
|
|
4692
|
+
retryableStatusCodes: [429, 502, 503, 504]
|
|
4693
|
+
};
|
|
4694
|
+
let lastError;
|
|
4695
|
+
const maxAttempts = resolved.maxRetries + 1;
|
|
4696
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
4697
|
+
try {
|
|
4698
|
+
return await fn();
|
|
4699
|
+
} catch (err) {
|
|
4700
|
+
lastError = err;
|
|
4701
|
+
if (!isRetryable(err, resolved.retryableStatusCodes)) {
|
|
4702
|
+
throw err;
|
|
4703
|
+
}
|
|
4704
|
+
if (attempt < maxAttempts - 1) {
|
|
4705
|
+
const delay = computeDelay(attempt, resolved);
|
|
4706
|
+
await sleep(delay);
|
|
4707
|
+
}
|
|
4708
|
+
}
|
|
4709
|
+
}
|
|
4710
|
+
throw lastError ?? new Error("withRetry: all attempts exhausted");
|
|
4711
|
+
}
|
|
4712
|
+
var _internal = {
|
|
4713
|
+
isRetryable,
|
|
4714
|
+
computeDelay,
|
|
4715
|
+
resolveRetryConfig,
|
|
4716
|
+
normalizeEndpoint,
|
|
4717
|
+
endpointLabel
|
|
4718
|
+
};
|
|
4719
|
+
|
|
3759
4720
|
// src/runtime/tx.ts
|
|
3760
4721
|
import {
|
|
3761
4722
|
TransactionInstruction as TransactionInstruction2,
|
|
@@ -3907,6 +4868,139 @@ function formatResult(result, jsonMode) {
|
|
|
3907
4868
|
return lines.join("\n");
|
|
3908
4869
|
}
|
|
3909
4870
|
|
|
4871
|
+
// src/runtime/lighthouse.ts
|
|
4872
|
+
import { PublicKey as PublicKey13, Transaction as Transaction2 } from "@solana/web3.js";
|
|
4873
|
+
var LIGHTHOUSE_PROGRAM_ID = new PublicKey13(
|
|
4874
|
+
"L2TExMFKdjpN9kozasaurPirfHy9P8sbXoAN1qA3S95"
|
|
4875
|
+
);
|
|
4876
|
+
var LIGHTHOUSE_PROGRAM_ID_STR2 = "L2TExMFKdjpN9kozasaurPirfHy9P8sbXoAN1qA3S95";
|
|
4877
|
+
var LIGHTHOUSE_CONSTRAINT_ADDRESS = 6400;
|
|
4878
|
+
var LIGHTHOUSE_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
4879
|
+
6e3,
|
|
4880
|
+
// InstructionMissing
|
|
4881
|
+
6001,
|
|
4882
|
+
// InstructionFallbackNotFound
|
|
4883
|
+
6002,
|
|
4884
|
+
// InstructionDidNotDeserialize
|
|
4885
|
+
6003,
|
|
4886
|
+
// InstructionDidNotSerialize
|
|
4887
|
+
6016,
|
|
4888
|
+
// IdlInstructionStub
|
|
4889
|
+
6032,
|
|
4890
|
+
// ConstraintMut
|
|
4891
|
+
6033,
|
|
4892
|
+
// ConstraintHasOne
|
|
4893
|
+
6034,
|
|
4894
|
+
// ConstraintSigner
|
|
4895
|
+
6035,
|
|
4896
|
+
// ConstraintRaw
|
|
4897
|
+
6036,
|
|
4898
|
+
// ConstraintOwner
|
|
4899
|
+
6037,
|
|
4900
|
+
// ConstraintRentExempt
|
|
4901
|
+
6038,
|
|
4902
|
+
// ConstraintSeeds
|
|
4903
|
+
6039,
|
|
4904
|
+
// ConstraintExecutable
|
|
4905
|
+
6040,
|
|
4906
|
+
// ConstraintState
|
|
4907
|
+
6041,
|
|
4908
|
+
// ConstraintAssociated
|
|
4909
|
+
6042,
|
|
4910
|
+
// ConstraintAssociatedInit
|
|
4911
|
+
6043,
|
|
4912
|
+
// ConstraintClose
|
|
4913
|
+
6400
|
|
4914
|
+
// ConstraintAddress (the one we hit most often)
|
|
4915
|
+
]);
|
|
4916
|
+
function isLighthouseInstruction(ix) {
|
|
4917
|
+
return ix.programId.equals(LIGHTHOUSE_PROGRAM_ID);
|
|
4918
|
+
}
|
|
4919
|
+
function isLighthouseError(error) {
|
|
4920
|
+
const msg = extractErrorMessage(error);
|
|
4921
|
+
if (!msg) return false;
|
|
4922
|
+
if (msg.includes(LIGHTHOUSE_PROGRAM_ID_STR2)) return true;
|
|
4923
|
+
if (/custom\s+program\s+error:\s*0x1900\b/i.test(msg)) return true;
|
|
4924
|
+
if (/"Custom"\s*:\s*6400\b/.test(msg) && /InstructionError/i.test(msg)) return true;
|
|
4925
|
+
return false;
|
|
4926
|
+
}
|
|
4927
|
+
function isLighthouseFailureInLogs(logs) {
|
|
4928
|
+
if (!Array.isArray(logs)) return false;
|
|
4929
|
+
let insideLighthouse = false;
|
|
4930
|
+
for (const line of logs) {
|
|
4931
|
+
if (typeof line !== "string") continue;
|
|
4932
|
+
if (line.includes(`Program ${LIGHTHOUSE_PROGRAM_ID_STR2} invoke`)) {
|
|
4933
|
+
insideLighthouse = true;
|
|
4934
|
+
continue;
|
|
4935
|
+
}
|
|
4936
|
+
if (line.includes(`Program ${LIGHTHOUSE_PROGRAM_ID_STR2} success`)) {
|
|
4937
|
+
insideLighthouse = false;
|
|
4938
|
+
continue;
|
|
4939
|
+
}
|
|
4940
|
+
if (insideLighthouse && /failed/i.test(line)) {
|
|
4941
|
+
return true;
|
|
4942
|
+
}
|
|
4943
|
+
if (line.includes(`Program ${LIGHTHOUSE_PROGRAM_ID_STR2} failed`)) {
|
|
4944
|
+
return true;
|
|
4945
|
+
}
|
|
4946
|
+
}
|
|
4947
|
+
return false;
|
|
4948
|
+
}
|
|
4949
|
+
function stripLighthouseInstructions(instructions, percolatorProgramId) {
|
|
4950
|
+
if (percolatorProgramId) {
|
|
4951
|
+
const hasPercolatorIx = instructions.some(
|
|
4952
|
+
(ix) => ix.programId.equals(percolatorProgramId)
|
|
4953
|
+
);
|
|
4954
|
+
if (!hasPercolatorIx) {
|
|
4955
|
+
return instructions;
|
|
4956
|
+
}
|
|
4957
|
+
}
|
|
4958
|
+
return instructions.filter((ix) => !isLighthouseInstruction(ix));
|
|
4959
|
+
}
|
|
4960
|
+
function stripLighthouseFromTransaction(transaction, percolatorProgramId) {
|
|
4961
|
+
if (percolatorProgramId) {
|
|
4962
|
+
const hasPercolatorIx = transaction.instructions.some(
|
|
4963
|
+
(ix) => ix.programId.equals(percolatorProgramId)
|
|
4964
|
+
);
|
|
4965
|
+
if (!hasPercolatorIx) return transaction;
|
|
4966
|
+
}
|
|
4967
|
+
const hasLighthouse = transaction.instructions.some(isLighthouseInstruction);
|
|
4968
|
+
if (!hasLighthouse) return transaction;
|
|
4969
|
+
const clean = new Transaction2();
|
|
4970
|
+
clean.recentBlockhash = transaction.recentBlockhash;
|
|
4971
|
+
clean.feePayer = transaction.feePayer;
|
|
4972
|
+
for (const ix of transaction.instructions) {
|
|
4973
|
+
if (!isLighthouseInstruction(ix)) {
|
|
4974
|
+
clean.add(ix);
|
|
4975
|
+
}
|
|
4976
|
+
}
|
|
4977
|
+
return clean;
|
|
4978
|
+
}
|
|
4979
|
+
function countLighthouseInstructions(ixsOrTx) {
|
|
4980
|
+
const instructions = Array.isArray(ixsOrTx) ? ixsOrTx : ixsOrTx.instructions;
|
|
4981
|
+
return instructions.filter(isLighthouseInstruction).length;
|
|
4982
|
+
}
|
|
4983
|
+
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";
|
|
4984
|
+
function classifyLighthouseError(error) {
|
|
4985
|
+
if (isLighthouseError(error)) {
|
|
4986
|
+
return LIGHTHOUSE_USER_MESSAGE;
|
|
4987
|
+
}
|
|
4988
|
+
return null;
|
|
4989
|
+
}
|
|
4990
|
+
function extractErrorMessage(error) {
|
|
4991
|
+
if (!error) return null;
|
|
4992
|
+
if (typeof error === "string") return error;
|
|
4993
|
+
if (error instanceof Error) return error.message;
|
|
4994
|
+
if (typeof error === "object" && "message" in error) {
|
|
4995
|
+
return String(error.message);
|
|
4996
|
+
}
|
|
4997
|
+
try {
|
|
4998
|
+
return JSON.stringify(error);
|
|
4999
|
+
} catch {
|
|
5000
|
+
return null;
|
|
5001
|
+
}
|
|
5002
|
+
}
|
|
5003
|
+
|
|
3910
5004
|
// src/math/trading.ts
|
|
3911
5005
|
function computeMarkPnl(positionSize, entryPrice, oraclePrice) {
|
|
3912
5006
|
if (positionSize === 0n || oraclePrice === 0n) return 0n;
|
|
@@ -3954,9 +5048,20 @@ function computeFeeSplit(totalFee, config) {
|
|
|
3954
5048
|
if (config.lpBps === 0n && config.protocolBps === 0n && config.creatorBps === 0n) {
|
|
3955
5049
|
return [totalFee, 0n, 0n];
|
|
3956
5050
|
}
|
|
5051
|
+
const totalBps = config.lpBps + config.protocolBps + config.creatorBps;
|
|
5052
|
+
if (totalBps !== 10000n) {
|
|
5053
|
+
throw new Error(
|
|
5054
|
+
`Fee split must equal exactly 10000 bps (100%): lpBps=${config.lpBps} + protocolBps=${config.protocolBps} + creatorBps=${config.creatorBps} = ${totalBps}`
|
|
5055
|
+
);
|
|
5056
|
+
}
|
|
3957
5057
|
const lp = totalFee * config.lpBps / 10000n;
|
|
3958
5058
|
const protocol = totalFee * config.protocolBps / 10000n;
|
|
3959
5059
|
const creator = totalFee - lp - protocol;
|
|
5060
|
+
if (creator < 0n) {
|
|
5061
|
+
throw new Error(
|
|
5062
|
+
`Internal error: creator fee is negative (${creator}). This should not happen if lpBps + protocolBps + creatorBps <= 10000.`
|
|
5063
|
+
);
|
|
5064
|
+
}
|
|
3960
5065
|
return [lp, protocol, creator];
|
|
3961
5066
|
}
|
|
3962
5067
|
function computePnlPercent(pnlTokens, capital) {
|
|
@@ -3971,8 +5076,17 @@ function computePnlPercent(pnlTokens, capital) {
|
|
|
3971
5076
|
}
|
|
3972
5077
|
function computeEstimatedEntryPrice(oracleE6, tradingFeeBps, direction) {
|
|
3973
5078
|
if (oracleE6 === 0n) return 0n;
|
|
5079
|
+
if (tradingFeeBps < 0n) {
|
|
5080
|
+
throw new Error(`computeEstimatedEntryPrice: tradingFeeBps must be non-negative, got ${tradingFeeBps}`);
|
|
5081
|
+
}
|
|
3974
5082
|
const feeImpact = oracleE6 * tradingFeeBps / 10000n;
|
|
3975
|
-
|
|
5083
|
+
const result = direction === "long" ? oracleE6 + feeImpact : oracleE6 - feeImpact;
|
|
5084
|
+
if (result <= 0n) {
|
|
5085
|
+
throw new Error(
|
|
5086
|
+
`computeEstimatedEntryPrice: result ${result} is non-positive (tradingFeeBps=${tradingFeeBps} too high for oracle=${oracleE6})`
|
|
5087
|
+
);
|
|
5088
|
+
}
|
|
5089
|
+
return result;
|
|
3976
5090
|
}
|
|
3977
5091
|
var MAX_SAFE_BIGINT = BigInt(Number.MAX_SAFE_INTEGER);
|
|
3978
5092
|
var MIN_SAFE_BIGINT = BigInt(-Number.MAX_SAFE_INTEGER);
|
|
@@ -3993,7 +5107,12 @@ function computeMaxLeverage(initialMarginBps) {
|
|
|
3993
5107
|
if (initialMarginBps <= 0n) {
|
|
3994
5108
|
throw new Error("computeMaxLeverage: initialMarginBps must be positive");
|
|
3995
5109
|
}
|
|
3996
|
-
|
|
5110
|
+
const scaledResult = 10000n * 1000000n / initialMarginBps;
|
|
5111
|
+
return Number(scaledResult) / 1e6;
|
|
5112
|
+
}
|
|
5113
|
+
function computeMaxWithdrawable(capital, pnl, reservedPnl) {
|
|
5114
|
+
const maturedPnl = pnl - reservedPnl;
|
|
5115
|
+
return capital + (maturedPnl > 0n ? maturedPnl : 0n);
|
|
3997
5116
|
}
|
|
3998
5117
|
|
|
3999
5118
|
// src/math/warmup.ts
|
|
@@ -4005,6 +5124,9 @@ function computeWarmupUnlockedCapital(totalCapital, currentSlot, warmupStartSlot
|
|
|
4005
5124
|
return totalCapital * elapsed / warmupPeriodSlots;
|
|
4006
5125
|
}
|
|
4007
5126
|
function computeWarmupLeverageCap(initialMarginBps, totalCapital, currentSlot, warmupStartSlot, warmupPeriodSlots) {
|
|
5127
|
+
if (initialMarginBps <= 0n) {
|
|
5128
|
+
throw new Error("computeWarmupLeverageCap: initialMarginBps must be positive");
|
|
5129
|
+
}
|
|
4008
5130
|
const maxLev = computeMaxLeverage(initialMarginBps);
|
|
4009
5131
|
if (warmupPeriodSlots === 0n || warmupStartSlot === 0n) return maxLev;
|
|
4010
5132
|
if (totalCapital <= 0n) return 1;
|
|
@@ -4015,7 +5137,14 @@ function computeWarmupLeverageCap(initialMarginBps, totalCapital, currentSlot, w
|
|
|
4015
5137
|
warmupPeriodSlots
|
|
4016
5138
|
);
|
|
4017
5139
|
if (unlocked <= 0n) return 1;
|
|
4018
|
-
const
|
|
5140
|
+
const scaledResult = BigInt(maxLev) * unlocked / totalCapital;
|
|
5141
|
+
if (scaledResult > BigInt(Number.MAX_SAFE_INTEGER)) {
|
|
5142
|
+
console.warn(
|
|
5143
|
+
`[computeWarmupLeverageCap] Warning: effective leverage ${scaledResult} exceeds MAX_SAFE_INTEGER, returning MAX_SAFE_INTEGER as a safety bound`
|
|
5144
|
+
);
|
|
5145
|
+
return Number.MAX_SAFE_INTEGER;
|
|
5146
|
+
}
|
|
5147
|
+
const effectiveLev = Number(scaledResult);
|
|
4019
5148
|
return Math.max(1, effectiveLev);
|
|
4020
5149
|
}
|
|
4021
5150
|
function computeWarmupMaxPositionSize(initialMarginBps, totalCapital, currentSlot, warmupStartSlot, warmupPeriodSlots) {
|
|
@@ -4028,9 +5157,40 @@ function computeWarmupMaxPositionSize(initialMarginBps, totalCapital, currentSlo
|
|
|
4028
5157
|
);
|
|
4029
5158
|
return unlocked * BigInt(maxLev);
|
|
4030
5159
|
}
|
|
5160
|
+
function computeWarmupProgress(currentSlot, warmupStartedAtSlot, warmupPeriodSlots, pnl, reservedPnl) {
|
|
5161
|
+
if (warmupPeriodSlots === 0n || warmupStartedAtSlot === 0n) {
|
|
5162
|
+
return {
|
|
5163
|
+
maturedPnl: pnl > 0n ? pnl : 0n,
|
|
5164
|
+
reservedPnl: 0n,
|
|
5165
|
+
progressBps: 10000n,
|
|
5166
|
+
// 100%
|
|
5167
|
+
slotsRemaining: 0n
|
|
5168
|
+
};
|
|
5169
|
+
}
|
|
5170
|
+
const elapsed = currentSlot >= warmupStartedAtSlot ? currentSlot - warmupStartedAtSlot : 0n;
|
|
5171
|
+
if (elapsed >= warmupPeriodSlots) {
|
|
5172
|
+
return {
|
|
5173
|
+
maturedPnl: pnl > 0n ? pnl : 0n,
|
|
5174
|
+
reservedPnl: 0n,
|
|
5175
|
+
progressBps: 10000n,
|
|
5176
|
+
// 100%
|
|
5177
|
+
slotsRemaining: 0n
|
|
5178
|
+
};
|
|
5179
|
+
}
|
|
5180
|
+
const progressBps = elapsed * 10000n / warmupPeriodSlots;
|
|
5181
|
+
const slotsRemaining = warmupPeriodSlots - elapsed;
|
|
5182
|
+
const maturedPnl = pnl > 0n ? pnl * progressBps / 10000n : 0n;
|
|
5183
|
+
const locked = reservedPnl > 0n ? reservedPnl : 0n;
|
|
5184
|
+
return {
|
|
5185
|
+
maturedPnl,
|
|
5186
|
+
reservedPnl: locked,
|
|
5187
|
+
progressBps,
|
|
5188
|
+
slotsRemaining
|
|
5189
|
+
};
|
|
5190
|
+
}
|
|
4031
5191
|
|
|
4032
5192
|
// src/validation.ts
|
|
4033
|
-
import { PublicKey as
|
|
5193
|
+
import { PublicKey as PublicKey14 } from "@solana/web3.js";
|
|
4034
5194
|
var U16_MAX = 65535;
|
|
4035
5195
|
var U64_MAX = BigInt("18446744073709551615");
|
|
4036
5196
|
var I64_MIN = BigInt("-9223372036854775808");
|
|
@@ -4060,7 +5220,7 @@ var ValidationError = class extends Error {
|
|
|
4060
5220
|
};
|
|
4061
5221
|
function validatePublicKey(value, field) {
|
|
4062
5222
|
try {
|
|
4063
|
-
return new
|
|
5223
|
+
return new PublicKey14(value);
|
|
4064
5224
|
} catch {
|
|
4065
5225
|
throw new ValidationError(
|
|
4066
5226
|
field,
|
|
@@ -4077,18 +5237,20 @@ function validateIndex(value, field) {
|
|
|
4077
5237
|
`must be <= ${U16_MAX} (u16 max), got ${t}`
|
|
4078
5238
|
);
|
|
4079
5239
|
}
|
|
5240
|
+
if (bi > BigInt(Number.MAX_SAFE_INTEGER)) {
|
|
5241
|
+
throw new ValidationError(field, `internal error: u16 value exceeds MAX_SAFE_INTEGER`);
|
|
5242
|
+
}
|
|
4080
5243
|
return Number(bi);
|
|
4081
5244
|
}
|
|
4082
5245
|
function validateAmount(value, field) {
|
|
4083
|
-
|
|
4084
|
-
|
|
4085
|
-
num = BigInt(value);
|
|
4086
|
-
} catch {
|
|
5246
|
+
const t = value.trim();
|
|
5247
|
+
if (!/^(0|[1-9]\d*)$/.test(t)) {
|
|
4087
5248
|
throw new ValidationError(
|
|
4088
5249
|
field,
|
|
4089
|
-
`"${value}" is not a valid
|
|
5250
|
+
`"${value}" is not a valid non-negative integer. Use decimal digits only.`
|
|
4090
5251
|
);
|
|
4091
5252
|
}
|
|
5253
|
+
const num = BigInt(t);
|
|
4092
5254
|
if (num < 0n) {
|
|
4093
5255
|
throw new ValidationError(field, `must be non-negative, got ${num}`);
|
|
4094
5256
|
}
|
|
@@ -4101,15 +5263,14 @@ function validateAmount(value, field) {
|
|
|
4101
5263
|
return num;
|
|
4102
5264
|
}
|
|
4103
5265
|
function validateU128(value, field) {
|
|
4104
|
-
|
|
4105
|
-
|
|
4106
|
-
num = BigInt(value);
|
|
4107
|
-
} catch {
|
|
5266
|
+
const t = value.trim();
|
|
5267
|
+
if (!/^(0|[1-9]\d*)$/.test(t)) {
|
|
4108
5268
|
throw new ValidationError(
|
|
4109
5269
|
field,
|
|
4110
|
-
`"${value}" is not a valid
|
|
5270
|
+
`"${value}" is not a valid non-negative integer. Use decimal digits only.`
|
|
4111
5271
|
);
|
|
4112
5272
|
}
|
|
5273
|
+
const num = BigInt(t);
|
|
4113
5274
|
if (num < 0n) {
|
|
4114
5275
|
throw new ValidationError(field, `must be non-negative, got ${num}`);
|
|
4115
5276
|
}
|
|
@@ -4122,15 +5283,14 @@ function validateU128(value, field) {
|
|
|
4122
5283
|
return num;
|
|
4123
5284
|
}
|
|
4124
5285
|
function validateI64(value, field) {
|
|
4125
|
-
|
|
4126
|
-
|
|
4127
|
-
num = BigInt(value);
|
|
4128
|
-
} catch {
|
|
5286
|
+
const t = value.trim();
|
|
5287
|
+
if (!/^-?(0|[1-9]\d*)$/.test(t)) {
|
|
4129
5288
|
throw new ValidationError(
|
|
4130
5289
|
field,
|
|
4131
|
-
`"${value}" is not a valid
|
|
5290
|
+
`"${value}" is not a valid integer. Use decimal digits only, with optional leading minus.`
|
|
4132
5291
|
);
|
|
4133
5292
|
}
|
|
5293
|
+
const num = BigInt(t);
|
|
4134
5294
|
if (num < I64_MIN) {
|
|
4135
5295
|
throw new ValidationError(
|
|
4136
5296
|
field,
|
|
@@ -4146,15 +5306,14 @@ function validateI64(value, field) {
|
|
|
4146
5306
|
return num;
|
|
4147
5307
|
}
|
|
4148
5308
|
function validateI128(value, field) {
|
|
4149
|
-
|
|
4150
|
-
|
|
4151
|
-
num = BigInt(value);
|
|
4152
|
-
} catch {
|
|
5309
|
+
const t = value.trim();
|
|
5310
|
+
if (!/^-?(0|[1-9]\d*)$/.test(t)) {
|
|
4153
5311
|
throw new ValidationError(
|
|
4154
5312
|
field,
|
|
4155
|
-
`"${value}" is not a valid
|
|
5313
|
+
`"${value}" is not a valid integer. Use decimal digits only, with optional leading minus.`
|
|
4156
5314
|
);
|
|
4157
5315
|
}
|
|
5316
|
+
const num = BigInt(t);
|
|
4158
5317
|
if (num < I128_MIN) {
|
|
4159
5318
|
throw new ValidationError(
|
|
4160
5319
|
field,
|
|
@@ -4178,6 +5337,9 @@ function validateBps(value, field) {
|
|
|
4178
5337
|
`must be <= 10000 (100%), got ${t}`
|
|
4179
5338
|
);
|
|
4180
5339
|
}
|
|
5340
|
+
if (bi > BigInt(Number.MAX_SAFE_INTEGER)) {
|
|
5341
|
+
throw new ValidationError(field, `internal error: bps value exceeds MAX_SAFE_INTEGER`);
|
|
5342
|
+
}
|
|
4181
5343
|
return Number(bi);
|
|
4182
5344
|
}
|
|
4183
5345
|
function validateU64(value, field) {
|
|
@@ -4192,6 +5354,9 @@ function validateU16(value, field) {
|
|
|
4192
5354
|
`must be <= ${U16_MAX} (u16 max), got ${t}`
|
|
4193
5355
|
);
|
|
4194
5356
|
}
|
|
5357
|
+
if (bi > BigInt(Number.MAX_SAFE_INTEGER)) {
|
|
5358
|
+
throw new ValidationError(field, `internal error: u16 value exceeds MAX_SAFE_INTEGER`);
|
|
5359
|
+
}
|
|
4195
5360
|
return Number(bi);
|
|
4196
5361
|
}
|
|
4197
5362
|
|
|
@@ -4242,7 +5407,9 @@ function parseDexScreenerPairs(json) {
|
|
|
4242
5407
|
else if (liquidity > 1e4) confidence = 60;
|
|
4243
5408
|
else if (liquidity > 1e3) confidence = 45;
|
|
4244
5409
|
const priceUsd = pair.priceUsd;
|
|
4245
|
-
const
|
|
5410
|
+
const rawPrice = typeof priceUsd === "string" || typeof priceUsd === "number" ? parseFloat(String(priceUsd)) : NaN;
|
|
5411
|
+
if (!Number.isFinite(rawPrice) || rawPrice <= 0) continue;
|
|
5412
|
+
const price = rawPrice;
|
|
4246
5413
|
let baseSym = "?";
|
|
4247
5414
|
let quoteSym = "?";
|
|
4248
5415
|
if (isRecord(pair.baseToken) && typeof pair.baseToken.symbol === "string") {
|
|
@@ -4273,8 +5440,8 @@ function parseJupiterMintEntry(json, mint) {
|
|
|
4273
5440
|
if (!isRecord(row)) return null;
|
|
4274
5441
|
const rawPrice = row.price;
|
|
4275
5442
|
if (rawPrice === void 0 || rawPrice === null) return null;
|
|
4276
|
-
const price = parseFloat(String(rawPrice))
|
|
4277
|
-
if (price <= 0) return null;
|
|
5443
|
+
const price = parseFloat(String(rawPrice));
|
|
5444
|
+
if (!Number.isFinite(price) || price <= 0) return null;
|
|
4278
5445
|
let mintSymbol = "?";
|
|
4279
5446
|
if (typeof row.mintSymbol === "string") mintSymbol = row.mintSymbol;
|
|
4280
5447
|
return { price, mintSymbol };
|
|
@@ -4340,10 +5507,17 @@ async function fetchDexSources(mint, signal) {
|
|
|
4340
5507
|
headers: { "User-Agent": "percolator/1.0" }
|
|
4341
5508
|
}
|
|
4342
5509
|
);
|
|
4343
|
-
if (!resp.ok)
|
|
5510
|
+
if (!resp.ok) {
|
|
5511
|
+
console.debug(`[fetchDexSources] HTTP ${resp.status} for mint ${mint}`);
|
|
5512
|
+
return [];
|
|
5513
|
+
}
|
|
4344
5514
|
const json = await resp.json();
|
|
4345
5515
|
return parseDexScreenerPairs(json);
|
|
4346
|
-
} catch {
|
|
5516
|
+
} catch (err) {
|
|
5517
|
+
console.warn(
|
|
5518
|
+
`[fetchDexSources] Error fetching DexScreener data for mint ${mint}:`,
|
|
5519
|
+
err instanceof Error ? err.message : String(err)
|
|
5520
|
+
);
|
|
4347
5521
|
return [];
|
|
4348
5522
|
}
|
|
4349
5523
|
}
|
|
@@ -4354,7 +5528,7 @@ function lookupPythSource(mint) {
|
|
|
4354
5528
|
type: "pyth",
|
|
4355
5529
|
address: entry.feedId,
|
|
4356
5530
|
pairLabel: `${entry.symbol} / USD (Pyth)`,
|
|
4357
|
-
liquidity:
|
|
5531
|
+
liquidity: Number.MAX_SAFE_INTEGER,
|
|
4358
5532
|
// Pyth is considered deep liquidity
|
|
4359
5533
|
price: 0,
|
|
4360
5534
|
// We don't fetch live price here; caller can enrich
|
|
@@ -4371,10 +5545,16 @@ async function fetchJupiterSource(mint, signal) {
|
|
|
4371
5545
|
headers: { "User-Agent": "percolator/1.0" }
|
|
4372
5546
|
}
|
|
4373
5547
|
);
|
|
4374
|
-
if (!resp.ok)
|
|
5548
|
+
if (!resp.ok) {
|
|
5549
|
+
console.debug(`[fetchJupiterSource] HTTP ${resp.status} for mint ${mint}`);
|
|
5550
|
+
return null;
|
|
5551
|
+
}
|
|
4375
5552
|
const json = await resp.json();
|
|
4376
5553
|
const row = parseJupiterMintEntry(json, mint);
|
|
4377
|
-
if (!row)
|
|
5554
|
+
if (!row) {
|
|
5555
|
+
console.debug(`[fetchJupiterSource] No price data from Jupiter for mint ${mint}`);
|
|
5556
|
+
return null;
|
|
5557
|
+
}
|
|
4378
5558
|
return {
|
|
4379
5559
|
type: "jupiter",
|
|
4380
5560
|
address: mint,
|
|
@@ -4385,7 +5565,11 @@ async function fetchJupiterSource(mint, signal) {
|
|
|
4385
5565
|
confidence: 40
|
|
4386
5566
|
// Fallback — lower confidence
|
|
4387
5567
|
};
|
|
4388
|
-
} catch {
|
|
5568
|
+
} catch (err) {
|
|
5569
|
+
console.warn(
|
|
5570
|
+
`[fetchJupiterSource] Error fetching Jupiter data for mint ${mint}:`,
|
|
5571
|
+
err instanceof Error ? err.message : String(err)
|
|
5572
|
+
);
|
|
4389
5573
|
return null;
|
|
4390
5574
|
}
|
|
4391
5575
|
}
|
|
@@ -4400,8 +5584,20 @@ async function resolvePrice(mint, signal, options) {
|
|
|
4400
5584
|
const pythSource = lookupPythSource(mint);
|
|
4401
5585
|
const allSources = [];
|
|
4402
5586
|
if (pythSource) {
|
|
4403
|
-
const
|
|
4404
|
-
|
|
5587
|
+
const dexPrice = dexSources[0]?.price ?? 0;
|
|
5588
|
+
const jupPrice = jupiterSource?.price ?? 0;
|
|
5589
|
+
if (dexPrice > 0 && jupPrice > 0) {
|
|
5590
|
+
const mid = (dexPrice + jupPrice) / 2;
|
|
5591
|
+
const deviation = Math.abs(dexPrice - jupPrice) / mid;
|
|
5592
|
+
if (deviation > 0.5) {
|
|
5593
|
+
pythSource.price = 0;
|
|
5594
|
+
pythSource.confidence = 20;
|
|
5595
|
+
} else {
|
|
5596
|
+
pythSource.price = mid;
|
|
5597
|
+
}
|
|
5598
|
+
} else {
|
|
5599
|
+
pythSource.price = dexPrice || jupPrice || 0;
|
|
5600
|
+
}
|
|
4405
5601
|
allSources.push(pythSource);
|
|
4406
5602
|
}
|
|
4407
5603
|
allSources.push(...dexSources);
|
|
@@ -4433,6 +5629,7 @@ export {
|
|
|
4433
5629
|
ACCOUNTS_FUND_MARKET_INSURANCE,
|
|
4434
5630
|
ACCOUNTS_INIT_LP,
|
|
4435
5631
|
ACCOUNTS_INIT_MARKET,
|
|
5632
|
+
ACCOUNTS_INIT_MATCHER_CTX,
|
|
4436
5633
|
ACCOUNTS_INIT_USER,
|
|
4437
5634
|
ACCOUNTS_KEEPER_CRANK,
|
|
4438
5635
|
ACCOUNTS_LIQUIDATE_AT_ORACLE,
|
|
@@ -4466,15 +5663,22 @@ export {
|
|
|
4466
5663
|
CHAINLINK_ANSWER_OFFSET,
|
|
4467
5664
|
CHAINLINK_DECIMALS_OFFSET,
|
|
4468
5665
|
CHAINLINK_MIN_SIZE,
|
|
5666
|
+
CHAINLINK_TIMESTAMP_OFFSET,
|
|
4469
5667
|
CREATOR_LOCK_SEED,
|
|
4470
5668
|
CTX_VAMM_OFFSET,
|
|
4471
5669
|
DEFAULT_OI_RAMP_SLOTS,
|
|
4472
5670
|
ENGINE_MARK_PRICE_OFF,
|
|
4473
5671
|
ENGINE_OFF,
|
|
4474
5672
|
IX_TAG,
|
|
5673
|
+
LIGHTHOUSE_CONSTRAINT_ADDRESS,
|
|
5674
|
+
LIGHTHOUSE_ERROR_CODES,
|
|
5675
|
+
LIGHTHOUSE_PROGRAM_ID,
|
|
5676
|
+
LIGHTHOUSE_PROGRAM_ID_STR2 as LIGHTHOUSE_PROGRAM_ID_STR,
|
|
5677
|
+
LIGHTHOUSE_USER_MESSAGE,
|
|
4475
5678
|
MARK_PRICE_EMA_ALPHA_E6,
|
|
4476
5679
|
MARK_PRICE_EMA_WINDOW_SLOTS,
|
|
4477
5680
|
MAX_DECIMALS,
|
|
5681
|
+
MAX_ORACLE_PRICE,
|
|
4478
5682
|
METEORA_DLMM_PROGRAM_ID,
|
|
4479
5683
|
ORACLE_PHASE_GROWING,
|
|
4480
5684
|
ORACLE_PHASE_MATURE,
|
|
@@ -4492,9 +5696,11 @@ export {
|
|
|
4492
5696
|
RAMP_START_BPS,
|
|
4493
5697
|
RAYDIUM_CLMM_PROGRAM_ID,
|
|
4494
5698
|
RENOUNCE_ADMIN_CONFIRMATION,
|
|
5699
|
+
RpcPool,
|
|
4495
5700
|
SLAB_TIERS,
|
|
4496
5701
|
SLAB_TIERS_V0,
|
|
4497
5702
|
SLAB_TIERS_V1,
|
|
5703
|
+
SLAB_TIERS_V12_1,
|
|
4498
5704
|
SLAB_TIERS_V1D,
|
|
4499
5705
|
SLAB_TIERS_V1D_LEGACY,
|
|
4500
5706
|
SLAB_TIERS_V1M,
|
|
@@ -4502,6 +5708,7 @@ export {
|
|
|
4502
5708
|
SLAB_TIERS_V2,
|
|
4503
5709
|
SLAB_TIERS_V_ADL,
|
|
4504
5710
|
SLAB_TIERS_V_ADL_DISCOVERY,
|
|
5711
|
+
SLAB_TIERS_V_SETDEXPOOL,
|
|
4505
5712
|
STAKE_IX,
|
|
4506
5713
|
STAKE_POOL_SIZE,
|
|
4507
5714
|
STAKE_PROGRAM_ID,
|
|
@@ -4511,11 +5718,15 @@ export {
|
|
|
4511
5718
|
VAMM_MAGIC,
|
|
4512
5719
|
ValidationError,
|
|
4513
5720
|
WELL_KNOWN,
|
|
5721
|
+
_internal,
|
|
4514
5722
|
buildAccountMetas,
|
|
4515
5723
|
buildAdlInstruction,
|
|
4516
5724
|
buildAdlTransaction,
|
|
4517
5725
|
buildIx,
|
|
4518
5726
|
checkPhaseTransition,
|
|
5727
|
+
checkRpcHealth,
|
|
5728
|
+
classifyLighthouseError,
|
|
5729
|
+
clearStaticMarkets,
|
|
4519
5730
|
computeDexSpotPriceE6,
|
|
4520
5731
|
computeDynamicFeeBps,
|
|
4521
5732
|
computeDynamicTradingFee,
|
|
@@ -4527,6 +5738,7 @@ export {
|
|
|
4527
5738
|
computeLiqPrice,
|
|
4528
5739
|
computeMarkPnl,
|
|
4529
5740
|
computeMaxLeverage,
|
|
5741
|
+
computeMaxWithdrawable,
|
|
4530
5742
|
computePnlPercent,
|
|
4531
5743
|
computePreTradeLiqPrice,
|
|
4532
5744
|
computeRequiredMargin,
|
|
@@ -4534,8 +5746,10 @@ export {
|
|
|
4534
5746
|
computeVammQuote,
|
|
4535
5747
|
computeWarmupLeverageCap,
|
|
4536
5748
|
computeWarmupMaxPositionSize,
|
|
5749
|
+
computeWarmupProgress,
|
|
4537
5750
|
computeWarmupUnlockedCapital,
|
|
4538
5751
|
concatBytes,
|
|
5752
|
+
countLighthouseInstructions,
|
|
4539
5753
|
decodeError,
|
|
4540
5754
|
decodeStakePool,
|
|
4541
5755
|
depositAccounts,
|
|
@@ -4554,6 +5768,8 @@ export {
|
|
|
4554
5768
|
detectSlabLayout,
|
|
4555
5769
|
detectTokenProgram,
|
|
4556
5770
|
discoverMarkets,
|
|
5771
|
+
discoverMarketsViaApi,
|
|
5772
|
+
discoverMarketsViaStaticBundle,
|
|
4557
5773
|
encBool,
|
|
4558
5774
|
encI128,
|
|
4559
5775
|
encI64,
|
|
@@ -4583,6 +5799,7 @@ export {
|
|
|
4583
5799
|
encodeFundMarketInsurance,
|
|
4584
5800
|
encodeInitLP,
|
|
4585
5801
|
encodeInitMarket,
|
|
5802
|
+
encodeInitMatcherCtx,
|
|
4586
5803
|
encodeInitSharedVault,
|
|
4587
5804
|
encodeInitUser,
|
|
4588
5805
|
encodeKeeperCrank,
|
|
@@ -4650,12 +5867,18 @@ export {
|
|
|
4650
5867
|
getCurrentNetwork,
|
|
4651
5868
|
getErrorHint,
|
|
4652
5869
|
getErrorName,
|
|
5870
|
+
getMarketsByAddress,
|
|
4653
5871
|
getMatcherProgramId,
|
|
4654
5872
|
getProgramId,
|
|
4655
5873
|
getStakeProgramId,
|
|
5874
|
+
getStaticMarkets,
|
|
4656
5875
|
initPoolAccounts,
|
|
4657
5876
|
isAccountUsed,
|
|
4658
5877
|
isAdlTriggered,
|
|
5878
|
+
isAnchorErrorCode,
|
|
5879
|
+
isLighthouseError,
|
|
5880
|
+
isLighthouseFailureInLogs,
|
|
5881
|
+
isLighthouseInstruction,
|
|
4659
5882
|
isStandardToken,
|
|
4660
5883
|
isToken2022,
|
|
4661
5884
|
isValidChainlinkOracle,
|
|
@@ -4674,11 +5897,14 @@ export {
|
|
|
4674
5897
|
rankAdlPositions,
|
|
4675
5898
|
readLastThrUpdateSlot,
|
|
4676
5899
|
readNonce,
|
|
5900
|
+
registerStaticMarkets,
|
|
4677
5901
|
resolvePrice,
|
|
4678
5902
|
safeEnv,
|
|
4679
5903
|
simulateOrSend,
|
|
4680
5904
|
slabDataSize,
|
|
4681
5905
|
slabDataSizeV1,
|
|
5906
|
+
stripLighthouseFromTransaction,
|
|
5907
|
+
stripLighthouseInstructions,
|
|
4682
5908
|
validateAmount,
|
|
4683
5909
|
validateBps,
|
|
4684
5910
|
validateI128,
|
|
@@ -4689,6 +5915,7 @@ export {
|
|
|
4689
5915
|
validateU128,
|
|
4690
5916
|
validateU16,
|
|
4691
5917
|
validateU64,
|
|
5918
|
+
withRetry,
|
|
4692
5919
|
withdrawAccounts
|
|
4693
5920
|
};
|
|
4694
5921
|
//# sourceMappingURL=index.js.map
|