@exponent-labs/exponent-fetcher 0.1.7 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/build/constants.d.ts +26 -0
  2. package/build/constants.js +58 -0
  3. package/build/constants.js.map +1 -0
  4. package/build/exponentFetcher.d.ts +328 -31
  5. package/build/exponentFetcher.js +779 -168
  6. package/build/exponentFetcher.js.map +1 -1
  7. package/build/index.d.ts +11 -0
  8. package/build/index.js +11 -0
  9. package/build/index.js.map +1 -1
  10. package/build/utils/adrena.d.ts +13 -0
  11. package/build/utils/adrena.js +29 -0
  12. package/build/utils/adrena.js.map +1 -0
  13. package/build/utils/fragmetric.d.ts +17 -0
  14. package/build/utils/fragmetric.js +23 -0
  15. package/build/utils/fragmetric.js.map +1 -0
  16. package/build/utils/jito.d.ts +7 -0
  17. package/build/utils/jito.js +29 -0
  18. package/build/utils/jito.js.map +1 -0
  19. package/build/utils/jupiter.d.ts +30 -0
  20. package/build/utils/jupiter.js +63 -0
  21. package/build/utils/jupiter.js.map +1 -0
  22. package/build/utils/kamino.d.ts +5 -0
  23. package/build/utils/kamino.js +10 -0
  24. package/build/utils/kamino.js.map +1 -0
  25. package/build/utils/meteora.d.ts +19 -0
  26. package/build/utils/meteora.js +36 -1
  27. package/build/utils/meteora.js.map +1 -1
  28. package/build/utils/ore.d.ts +74 -0
  29. package/build/utils/ore.js +217 -0
  30. package/build/utils/ore.js.map +1 -0
  31. package/build/utils/perena.d.ts +12 -0
  32. package/build/utils/perena.js +27 -0
  33. package/build/utils/perena.js.map +1 -0
  34. package/build/utils/sanctum.d.ts +6 -0
  35. package/build/utils/sanctum.js +26 -0
  36. package/build/utils/sanctum.js.map +1 -0
  37. package/build/utils/solstice.d.ts +12 -0
  38. package/build/utils/solstice.js +45 -0
  39. package/build/utils/solstice.js.map +1 -0
  40. package/package.json +21 -18
  41. package/src/constants.ts +56 -0
  42. package/src/exponentFetcher.ts +1238 -222
  43. package/src/index.ts +11 -0
  44. package/src/utils/adrena.ts +44 -0
  45. package/src/utils/fragmetric.ts +34 -0
  46. package/src/utils/jito.ts +30 -0
  47. package/src/utils/jupiter.ts +98 -0
  48. package/src/utils/kamino.ts +6 -0
  49. package/src/utils/meteora.ts +73 -1
  50. package/src/utils/ore.ts +322 -0
  51. package/src/utils/perena.ts +28 -0
  52. package/src/utils/sanctum.ts +24 -0
  53. package/src/utils/solstice.ts +51 -0
  54. package/tsconfig.json +4 -1
@@ -3,18 +3,19 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.fetchSolsticeRedemptionRate = exports.fetchSanctumIndex = exports.fetchAdrenaIndex = exports.fetchMeteoraIndex = exports.fetchFragmetricSupportedTokenIndex = exports.fetchKaminoVaultIndex = exports.fetchJupiterLendIndex = exports.fetchFragmetricIndex = exports.fetchGenericSyMetaIndex = exports.fetchPyth = exports.fetchJupiterPerpsIndex = exports.fetchJitoRestaking = exports.fetchPerenaStablePoolData = exports.fetchSplStakePoolIndex = exports.deserializeEmission = exports.serializeEmission = exports.fetchAllKaminoObligationsByKaminoReserve = exports.fetchKaminoReserve = exports.ExponentFetcher = exports.MyWallet = exports.deserializeAnchorizedPNumFromJson = exports.serializeAnchorizedPNumFromJson = void 0;
6
+ exports.fetchChainlinkRate = exports.fetchOreExchangeRate = exports.fetchReflectRedemptionRate = exports.fetchSolsticeRedemptionRate = exports.fetchSanctumIndex = exports.fetchAdrenaIndex = exports.fetchMeteoraIndex = exports.fetchFragmetricSupportedTokenIndex = exports.fetchKaminoVaultIndex = exports.fetchJupiterLendIndex = exports.fetchFragmetricIndex = exports.fetchGenericSyMetaIndex = exports.fetchPyth = exports.fetchJupiterPerpsIndex = exports.fetchJitoRestaking = exports.fetchPerenaStablePoolData = exports.fetchSplStakePoolIndex = exports.anchorizedPNumToRawBigint = exports.deserializeEmission = exports.serializeEmission = exports.fetchAllKaminoObligationsByKaminoReserve = exports.fetchKaminoReserve = exports.deserializeYtPosition = exports.deserializeLpPosition = exports.deserializeMarketThree = exports.deserializeMarketThreeTicks = exports.ExponentFetcher = exports.MyWallet = exports.deserializeAnchorizedPNumFromJson = exports.serializeAnchorizedPNumFromJson = void 0;
7
7
  const anchor_1 = require("@coral-xyz/anchor");
8
8
  const anchor_2 = require("@coral-xyz/anchor");
9
9
  const spl_stake_pool_1 = require("@solana/spl-stake-pool");
10
10
  const spl_token_1 = require("@solana/spl-token");
11
- const spl_token_2 = require("@solana/spl-token");
11
+ const bs58_1 = __importDefault(require("bs58"));
12
12
  const decimal_js_1 = __importDefault(require("decimal.js"));
13
+ const chainlink_idl_1 = require("@exponent-labs/chainlink-idl");
14
+ const exponent_clmm_idl_1 = require("@exponent-labs/exponent-clmm-idl");
13
15
  const exponent_idl_1 = require("@exponent-labs/exponent-idl");
16
+ const exponent_orderbook_idl_1 = require("@exponent-labs/exponent-orderbook-idl");
14
17
  const exponent_types_1 = require("@exponent-labs/exponent-types");
15
- const fragmetric_idl_1 = require("@exponent-labs/fragmetric-idl");
16
- const jupiter_lend_idl_1 = require("@exponent-labs/jupiter-lend-idl");
17
- const kamino_vault_idl_1 = require("@exponent-labs/kamino-vault-idl");
18
+ const exponent_types_2 = require("@exponent-labs/exponent-types");
18
19
  const generic_sy_idl_1 = require("@exponent-labs/generic-sy-idl");
19
20
  const jito_restaking_sy_idl_1 = require("@exponent-labs/jito-restaking-sy-idl");
20
21
  const jito_restaking_sy_idl_2 = require("@exponent-labs/jito-restaking-sy-idl");
@@ -22,17 +23,22 @@ const kamino_reserve_deserializer_1 = require("@exponent-labs/kamino-reserve-des
22
23
  const kamino_reserve_deserializer_2 = require("@exponent-labs/kamino-reserve-deserializer");
23
24
  const kamino_sy_idl_1 = require("@exponent-labs/kamino-sy-idl");
24
25
  const kamino_sy_idl_2 = require("@exponent-labs/kamino-sy-idl");
26
+ const kamino_vault_idl_1 = require("@exponent-labs/kamino-vault-idl");
25
27
  const marginfi_sy_idl_1 = require("@exponent-labs/marginfi-sy-idl");
26
28
  const meteora_idl_1 = require("@exponent-labs/meteora-idl");
27
29
  const perena_sy_idl_1 = require("@exponent-labs/perena-sy-idl");
28
30
  const perena_sy_idl_2 = require("@exponent-labs/perena-sy-idl");
29
31
  const precise_number_1 = require("@exponent-labs/precise-number");
30
- const sanctum_idl_1 = require("@exponent-labs/sanctum-idl");
31
- const adrena_idl_1 = require("@exponent-labs/adrena-idl");
32
- const solstice_idl_1 = require("@exponent-labs/solstice-idl");
32
+ const constants_1 = require("./constants");
33
+ const adrena_1 = require("./utils/adrena");
34
+ const fragmetric_1 = require("./utils/fragmetric");
35
+ const jito_1 = require("./utils/jito");
36
+ const jupiter_1 = require("./utils/jupiter");
33
37
  const meteora_1 = require("./utils/meteora");
34
- // Decode Jupiter Perps pool accounts via helper using Anchor 0.29.0
35
- const jupiter_perps_idl_1 = require("@exponent-labs/jupiter-perps-idl");
38
+ const ore_1 = require("./utils/ore");
39
+ const perena_1 = require("./utils/perena");
40
+ const sanctum_1 = require("./utils/sanctum");
41
+ const solstice_1 = require("./utils/solstice");
36
42
  function serializeAnchorizedPNumFromJson(pnum) {
37
43
  const serializedArray = pnum[0].map((bn) => bn.toString());
38
44
  return { 0: serializedArray };
@@ -81,6 +87,8 @@ class ExponentFetcher {
81
87
  jitoRestakingSyProgram;
82
88
  perenaSyProgram;
83
89
  genericStandardProgram;
90
+ exponentClmmProgram;
91
+ orderbookProgram;
84
92
  connection;
85
93
  coreProgramId;
86
94
  marginfiSyProgramId;
@@ -105,6 +113,8 @@ class ExponentFetcher {
105
113
  this.jitoRestakingSyProgram = new anchor_1.Program(jito_restaking_sy_idl_2.IDL, provider);
106
114
  this.perenaSyProgram = new anchor_1.Program(perena_sy_idl_2.IDL, provider);
107
115
  this.genericStandardProgram = new anchor_1.Program(generic_sy_idl_1.IDL, provider);
116
+ this.exponentClmmProgram = new anchor_1.Program(exponent_clmm_idl_1.IDL, provider);
117
+ this.orderbookProgram = new anchor_1.Program(exponent_orderbook_idl_1.IDL, provider);
108
118
  }
109
119
  async fetchVault(address) {
110
120
  try {
@@ -128,6 +138,28 @@ class ExponentFetcher {
128
138
  throw e;
129
139
  }
130
140
  }
141
+ async fetchOrderbook(address) {
142
+ try {
143
+ const o = (await this.connection.getAccountInfo(address)).data;
144
+ return deserializeOrderbook(o);
145
+ }
146
+ catch (e) {
147
+ console.error(`Error fetching orderbook ${address.toBase58()}`);
148
+ console.error(e);
149
+ throw e;
150
+ }
151
+ }
152
+ async fetchOrderbookCpiAccounts(address) {
153
+ try {
154
+ const orderbookCpiAccounts = await this.orderbookProgram.account.cpiAccountsOrderbook.fetch(address);
155
+ return orderbookCpiAccounts;
156
+ }
157
+ catch (e) {
158
+ console.error(`Error fetching orderbook ${address.toBase58()}`);
159
+ console.error(e);
160
+ throw e;
161
+ }
162
+ }
131
163
  async fetchMarginfiSyMeta(address) {
132
164
  const x = await this.marginfiSyProgram.account.syMeta.fetch(address);
133
165
  return deserializeMarginfiSyMeta(x);
@@ -200,10 +232,59 @@ class ExponentFetcher {
200
232
  const x = await this.program.account.lpPosition.fetch(address);
201
233
  return deserializeLpPosition(x);
202
234
  }
235
+ async fetchLpPositionCLMM(address) {
236
+ try {
237
+ const raw = await this.exponentClmmProgram.account.lpPosition.fetch(address);
238
+ const v = {
239
+ owner: raw.owner,
240
+ market: raw.market,
241
+ feeInsideLastPt: raw.feeInsideLastPt,
242
+ feeInsideLastSy: raw.feeInsideLastSy,
243
+ lpBalance: raw.lpBalance,
244
+ tokensOwedSy: raw.tokensOwedSy,
245
+ tokensOwedPt: raw.tokensOwedPt,
246
+ lowerTickIdx: raw.lowerTickIdx,
247
+ upperTickIdx: raw.upperTickIdx,
248
+ farms: {
249
+ trackers: raw.farms.trackers.map((t) => ({
250
+ staged: t.staged,
251
+ lastSeenIndex: t.lastSeenIndex,
252
+ })),
253
+ },
254
+ shareTrackers: {
255
+ trackers: raw.shareTrackers.trackers.map((tracker) => ({
256
+ tickIdx: tracker.tickIdx,
257
+ rightTickIdx: tracker.rightTickIdx,
258
+ splitEpoch: tracker.splitEpoch,
259
+ lpShare: tracker.lpShare,
260
+ emissions: {
261
+ trackers: tracker.emissions.trackers.map((e) => ({
262
+ staged: e.staged,
263
+ lastSeenIndex: e.lastSeenIndex,
264
+ })),
265
+ },
266
+ })),
267
+ },
268
+ };
269
+ return deserializeLpPositionCLMM(v);
270
+ }
271
+ catch (e) {
272
+ console.error(`Error fetching clmm lp position ${address.toBase58()}`);
273
+ console.error(e);
274
+ throw e;
275
+ }
276
+ }
203
277
  async fetchYtPosition(address) {
204
278
  const x = await this.program.account.yieldTokenPosition.fetch(address);
205
279
  return deserializeYtPosition(x);
206
280
  }
281
+ /** Batch fetch multiple YT positions in a single RPC call */
282
+ async fetchYtPositions(addresses) {
283
+ if (addresses.length === 0)
284
+ return [];
285
+ const results = await this.program.account.yieldTokenPosition.fetchMultiple(addresses);
286
+ return results.map((x) => (x ? deserializeYtPosition(x) : null));
287
+ }
207
288
  async fetchJitoRestakingSyMeta(address) {
208
289
  const x = await this.jitoRestakingSyProgram.account.syMeta.fetch(address);
209
290
  return (0, exponent_types_1.deserializeJitoRestakingSyMetaAccountRaw)(x);
@@ -216,8 +297,314 @@ class ExponentFetcher {
216
297
  const x = await this.genericStandardProgram.account.syMeta.fetch(address);
217
298
  return (0, exponent_types_1.deserializeGenericSyMetaAccountRaw)(x);
218
299
  }
300
+ async fetchAllMarketThree() {
301
+ //TODO Replace with the following code when all damaged markets are removed onchain
302
+ // const marketsProgramAccounts: ProgramAccount<MarketThreeRaw>[] =
303
+ // await this.exponentClmmProgram.account.marketThree.all()
304
+ // return marketsProgramAccounts.map(({ account }) => deserializeMarketThree(account))
305
+ const MARKET_THREE_DISCRIMINATOR = Buffer.from([242, 240, 26, 15, 148, 186, 185, 205]);
306
+ const marketsProgramAccounts = await this.connection.getProgramAccounts(this.exponentClmmProgram.programId, {
307
+ filters: [
308
+ {
309
+ memcmp: {
310
+ offset: 0,
311
+ bytes: bs58_1.default.encode(MARKET_THREE_DISCRIMINATOR),
312
+ },
313
+ },
314
+ ],
315
+ });
316
+ return marketsProgramAccounts
317
+ .filter(({ pubkey }) => !(0, constants_1.isMarketIgnored)(pubkey.toBase58()))
318
+ .map(({ account, pubkey }) => {
319
+ try {
320
+ return this.exponentClmmProgram.coder.accounts.decode("marketThree", account.data);
321
+ }
322
+ catch (error) {
323
+ return null;
324
+ }
325
+ })
326
+ .filter((m) => !!m);
327
+ }
328
+ async fetchMarketThree(address) {
329
+ const addressStr = address.toBase58();
330
+ if ((0, constants_1.isMarketIgnored)(addressStr)) {
331
+ throw new constants_1.IgnoredClmmEntityError("market", addressStr);
332
+ }
333
+ try {
334
+ const m = await this.exponentClmmProgram.account.marketThree.fetch(address);
335
+ return deserializeMarketThree(m);
336
+ }
337
+ catch (e) {
338
+ console.error(`Error fetching market ${addressStr}`);
339
+ console.error(e);
340
+ throw e;
341
+ }
342
+ }
343
+ async fetchAllMarketThreeTicks() {
344
+ const TICKS_DISCRIMINATOR = Buffer.from([122, 104, 41, 141, 214, 36, 222, 37]);
345
+ const ticksAccounts = await this.connection.getProgramAccounts(this.exponentClmmProgram.programId, {
346
+ filters: [
347
+ {
348
+ memcmp: {
349
+ offset: 0,
350
+ bytes: bs58_1.default.encode(TICKS_DISCRIMINATOR),
351
+ },
352
+ },
353
+ ],
354
+ });
355
+ return ticksAccounts
356
+ .filter(({ pubkey }) => !(0, constants_1.isTicksIgnored)(pubkey.toBase58()))
357
+ .map(({ account }) => deserializeMarketThreeTicks(account.data));
358
+ }
359
+ async fetchMarketThreeTicks(address) {
360
+ const addressStr = address.toBase58();
361
+ if ((0, constants_1.isTicksIgnored)(addressStr)) {
362
+ throw new constants_1.IgnoredClmmEntityError("ticks", addressStr);
363
+ }
364
+ try {
365
+ const m = (await this.connection.getAccountInfo(address)).data;
366
+ return deserializeMarketThreeTicks(m);
367
+ }
368
+ catch (e) {
369
+ console.error(`Error fetching ticks ${addressStr}`);
370
+ console.error(e);
371
+ throw e;
372
+ }
373
+ }
219
374
  }
220
375
  exports.ExponentFetcher = ExponentFetcher;
376
+ /**
377
+ * Deserializes a CLMM Ticks account from raw buffer data.
378
+ *
379
+ * Layout matches Rust struct `Ticks` with `RedBlackTree<u32, Tick, 1000>`:
380
+ * - Discriminator: 8 bytes
381
+ * - RedBlackTree header: root(4) + padding(12) + size(8) + bump(4) + freeIdx(4) = 32 bytes
382
+ * - 1000 RBTree nodes, each: nodeHeader(16) + key(4) + padding(4) + Tick = variable bytes
383
+ * - Ticks footer: market(32) + feeGrowthPt(16) + feeGrowthSy(16) + prefixSum(8) + spotPrice(8) + currentTick(4) + padding(12) = 96 bytes
384
+ */
385
+ function deserializeMarketThreeTicks(data) {
386
+ const MAX_TICK_NODES = 1000;
387
+ const PERSONAL_TICK_YIELD_TRACKER_SIZE = 2;
388
+ let offset = 8; // Skip discriminator
389
+ // ─── Helper functions ─────────────────────────────────────────────────────
390
+ const readU64 = () => {
391
+ const val = data.readBigUInt64LE(offset);
392
+ offset += 8;
393
+ return val;
394
+ };
395
+ const readU128 = () => {
396
+ const lo = data.readBigUInt64LE(offset);
397
+ const hi = data.readBigUInt64LE(offset + 8);
398
+ offset += 16;
399
+ return (hi << 64n) + lo;
400
+ };
401
+ const readI128 = () => {
402
+ const lo = data.readBigUInt64LE(offset);
403
+ const hi = data.readBigInt64LE(offset + 8); // High part is signed
404
+ offset += 16;
405
+ return (hi << 64n) + lo;
406
+ };
407
+ const readF64 = () => {
408
+ const val = data.readDoubleLE(offset);
409
+ offset += 8;
410
+ return val;
411
+ };
412
+ const readU32 = () => {
413
+ const val = data.readUInt32LE(offset);
414
+ offset += 4;
415
+ return val;
416
+ };
417
+ const readPubkey = () => {
418
+ const pk = new anchor_1.web3.PublicKey(data.slice(offset, offset + 32));
419
+ offset += 32;
420
+ return pk;
421
+ };
422
+ /** Reads a PreciseNumber (Number type in Rust) as float - 32 bytes (4 x u64) */
423
+ const readPreciseNumberAsFloat = () => {
424
+ const nums = [];
425
+ for (let i = 0; i < 4; i++) {
426
+ nums.push(new anchor_1.BN(data.slice(offset + i * 8, offset + (i + 1) * 8), undefined, "le"));
427
+ }
428
+ offset += 32;
429
+ return parseFloat(precise_number_1.PreciseNumber.fromRaw(nums).valueString);
430
+ };
431
+ /** Reads a PreciseNumber (Number type in Rust) as raw bigint - 32 bytes (4 x u64 = 256 bits) */
432
+ const readPreciseNumberAsBigint = () => {
433
+ let val = 0n;
434
+ for (let i = 0; i < 4; i++) {
435
+ const chunk = data.readBigUInt64LE(offset + i * 8);
436
+ val += chunk << BigInt(i * 64);
437
+ }
438
+ offset += 32;
439
+ return val;
440
+ };
441
+ const skip = (bytes) => {
442
+ offset += bytes;
443
+ };
444
+ // ─── Parse RedBlackTree header ────────────────────────────────────────────
445
+ skip(4); // root: u32
446
+ skip(12); // padding to align NodeAllocator
447
+ skip(8); // size: u64
448
+ skip(4); // bump_index: u32
449
+ skip(4); // free_list_head: u32
450
+ // ─── Parse tick nodes ─────────────────────────────────────────────────────
451
+ const ticks = [];
452
+ for (let i = 0; i < MAX_TICK_NODES; i++) {
453
+ // RBNode header: left(4) + right(4) + parent(4) + color(4) = 16 bytes
454
+ skip(16);
455
+ // Key: u32 + padding to 8 bytes
456
+ const apyBasePoints = readU32();
457
+ skip(4); // padding
458
+ // Tick value (416 bytes total)
459
+ const feeGrowthOutsidePt = readU128(); // 16 bytes
460
+ const feeGrowthOutsideSy = readU128(); // 16 bytes
461
+ const liquidityNet = readI128(); // 16 bytes
462
+ const liquidityGross = readU64(); // 8 bytes
463
+ const spotPrice = readF64(); // 8 bytes
464
+ const principalPt = readU64(); // 8 bytes
465
+ const principalSy = readU64(); // 8 bytes
466
+ const principalShareSupply = readPreciseNumberAsBigint(); // 32 bytes - kept as bigint for arithmetic
467
+ // FarmYieldTrackers: 2 x FarmYieldTracker(32 bytes) = 64 bytes
468
+ const farms = [];
469
+ for (let j = 0; j < PERSONAL_TICK_YIELD_TRACKER_SIZE; j++) {
470
+ farms.push({ lastSeenIndex: readPreciseNumberAsFloat() });
471
+ }
472
+ // EmissionYieldTrackers: 2 x EmissionYieldTracker(64 bytes) = 128 bytes
473
+ const emissions = [];
474
+ for (let j = 0; j < PERSONAL_TICK_YIELD_TRACKER_SIZE; j++) {
475
+ emissions.push({
476
+ lastSeenIndex: readPreciseNumberAsFloat(),
477
+ lastPositionIndex: readPreciseNumberAsFloat(),
478
+ });
479
+ }
480
+ const lastSplitEpoch = readU64(); // 8 bytes
481
+ const frozenLiquidity = readU64(); // 8 bytes
482
+ ticks.push({
483
+ apyBasePoints,
484
+ liquidityNet,
485
+ feeGrowthOutsidePt,
486
+ feeGrowthOutsideSy,
487
+ liquidityGross,
488
+ impliedRate: spotPrice, // Legacy field name kept for compatibility
489
+ principalPt,
490
+ principalSy,
491
+ principalShareSupply,
492
+ farms,
493
+ emissions,
494
+ lastSplitEpoch,
495
+ frozenLiquidity,
496
+ });
497
+ }
498
+ // ─── Parse Ticks footer ───────────────────────────────────────────────────
499
+ const market = readPubkey(); // 32 bytes
500
+ const feeGrowthIndexGlobalPt = readU128(); // 16 bytes
501
+ const feeGrowthIndexGlobalSy = readU128(); // 16 bytes
502
+ const currentPrefixSum = readU64(); // 8 bytes
503
+ const currentSpotPrice = readF64(); // 8 bytes
504
+ const currentTick = readU32(); // 4 bytes
505
+ skip(12); // padding
506
+ return {
507
+ ticksTree: ticks,
508
+ market,
509
+ feeGrowthIndexGlobalPt,
510
+ feeGrowthIndexGlobalSy,
511
+ currentPrefixSum,
512
+ currentSpotPrice,
513
+ currentTick,
514
+ };
515
+ }
516
+ exports.deserializeMarketThreeTicks = deserializeMarketThreeTicks;
517
+ /** Decoded account may use snake_case (from JSON IDL); normalize to camelCase for app use. */
518
+ function normalizeCpiContext(a) {
519
+ return {
520
+ altIndex: a.altIndex ?? a.alt_index ?? 0,
521
+ isSigner: a.isSigner ?? a.is_signer ?? false,
522
+ isWritable: a.isWritable ?? a.is_writable ?? false,
523
+ };
524
+ }
525
+ function normalizeCpiAccountIndexes(raw) {
526
+ const arr = (key, snake) => {
527
+ const a = raw[key] ?? raw[snake];
528
+ return Array.isArray(a) ? a.map((x) => normalizeCpiContext(x)) : [];
529
+ };
530
+ const arr2 = (key, snake) => {
531
+ const a = raw[key] ?? raw[snake];
532
+ return Array.isArray(a) ? a.map((inner) => (Array.isArray(inner) ? inner.map((x) => normalizeCpiContext(x)) : [])) : [];
533
+ };
534
+ return {
535
+ getSyState: arr("getSyState", "get_sy_state"),
536
+ withdrawSy: arr("withdrawSy", "withdraw_sy"),
537
+ depositSy: arr("depositSy", "deposit_sy"),
538
+ claimEmission: arr2("claimEmission", "claim_emission"),
539
+ getPositionState: arr("getPositionState", "get_position_state"),
540
+ };
541
+ }
542
+ function normalizeMarketCpiCoreIndexes(raw) {
543
+ const arr = (key, snake) => {
544
+ const a = raw[key] ?? raw[snake];
545
+ return Array.isArray(a) ? a.map((x) => normalizeCpiContext(x)) : [];
546
+ };
547
+ return {
548
+ stripSy: arr("stripSy", "strip_sy"),
549
+ mergeSy: arr("mergeSy", "merge_sy"),
550
+ };
551
+ }
552
+ function deserializeMarketThree(m) {
553
+ const rawCpiSy = m.cpiSyAccounts ?? m.cpi_sy_accounts;
554
+ const rawCpiCore = m.cpiCoreAccounts ?? m.cpi_core_accounts;
555
+ const cpiSyAccounts = rawCpiSy != null && typeof rawCpiSy === "object"
556
+ ? normalizeCpiAccountIndexes(rawCpiSy)
557
+ : (m.cpiSyAccounts ?? { getSyState: [], withdrawSy: [], depositSy: [], claimEmission: [], getPositionState: [] });
558
+ const cpiCoreAccounts = rawCpiCore != null && typeof rawCpiCore === "object"
559
+ ? normalizeMarketCpiCoreIndexes(rawCpiCore)
560
+ : (m.cpiCoreAccounts ?? { stripSy: [], mergeSy: [] });
561
+ return {
562
+ addressLookupTable: m.addressLookupTable,
563
+ mintSy: m.mintSy,
564
+ mintPt: m.mintPt,
565
+ vault: m.vault,
566
+ tokenSyEscrow: m.tokenSyEscrow,
567
+ tokenPtEscrow: m.tokenPtEscrow,
568
+ tokenFeeTreasurySy: m.tokenFeeTreasurySy,
569
+ tokenFeeTreasuryPt: m.tokenFeeTreasuryPt,
570
+ selfAddress: m.selfAddress,
571
+ syProgram: m.syProgram,
572
+ statusFlags: m.statusFlags,
573
+ cpiSyAccounts,
574
+ isCurrentFlashSwap: m.isCurrentFlashSwap,
575
+ lpFarm: m.lpFarm,
576
+ mintYt: m.mintYt,
577
+ tokenYtEscrow: m.tokenYtEscrow,
578
+ emissions: {
579
+ trackers: m.emissions.trackers.map((t) => ({
580
+ tokenEscrow: t.tokenEscrow,
581
+ lpShareIndex: deserializeAnchorizedPNum(t.lpShareIndex),
582
+ lastSeenStaged: Number(t.lastSeenStaged),
583
+ })),
584
+ },
585
+ liquidityNetBalanceLimits: m.liquidityNetBalanceLimits,
586
+ admin: m.admin,
587
+ ticks: m.ticks,
588
+ configurationOptions: {
589
+ lnFeeRateRoot: m.configurationOptions.lnFeeRateRoot,
590
+ treasuryFeeBps: m.configurationOptions.treasuryFeeBps,
591
+ minLpTickAmount: BigInt(m.configurationOptions.minLpTickAmount.toString()),
592
+ epsilonClamp: m.configurationOptions.epsilonClamp,
593
+ maxLpSupply: BigInt(m.configurationOptions.maxLpSupply.toString()),
594
+ tickSpace: m.configurationOptions.tickSpace,
595
+ },
596
+ financials: {
597
+ expirationTs: BigInt(m.financials.expirationTs),
598
+ ptBalance: BigInt(m.financials.ptBalance.toString()),
599
+ syBalance: BigInt(m.financials.syBalance.toString()),
600
+ liquidityBalance: BigInt(m.financials.liquidityBalance.toString()),
601
+ },
602
+ cpiCoreAccounts,
603
+ exponentCoreProgram: m.exponentCoreProgram,
604
+ seedId: m.seedId,
605
+ };
606
+ }
607
+ exports.deserializeMarketThree = deserializeMarketThree;
221
608
  function deserializeMarketTwo(m) {
222
609
  return {
223
610
  ptBalance: BigInt(m.financials.ptBalance.toString()),
@@ -255,6 +642,33 @@ function deserializeMarketTwo(m) {
255
642
  seedId: m.seedId,
256
643
  };
257
644
  }
645
+ function deserializeLpPositionCLMM(x) {
646
+ return {
647
+ owner: x.owner,
648
+ market: x.market,
649
+ feeInsideLastPt: BigInt(x.feeInsideLastPt.toString()),
650
+ feeInsideLastSy: BigInt(x.feeInsideLastSy.toString()),
651
+ lpBalance: BigInt(x.lpBalance.toString()),
652
+ tokensOwedSy: BigInt(x.tokensOwedSy.toString()),
653
+ tokensOwedPt: BigInt(x.tokensOwedPt.toString()),
654
+ lowerTickIdx: x.lowerTickIdx,
655
+ upperTickIdx: x.upperTickIdx,
656
+ farms: x.farms.trackers.map((t) => ({
657
+ staged: BigInt(t.staged.toString()),
658
+ lastSeenIndex: parseFloat(precise_number_1.PreciseNumber.fromRaw(t.lastSeenIndex[0]).valueString),
659
+ })),
660
+ shareTrackers: x.shareTrackers.trackers.map((tracker) => ({
661
+ tickIdx: tracker.tickIdx,
662
+ rightTickIdx: tracker.rightTickIdx,
663
+ splitEpoch: BigInt(tracker.splitEpoch.toString()),
664
+ lpShare: anchorizedPNumToRawBigint(tracker.lpShare),
665
+ emissions: tracker.emissions.trackers.map((e) => ({
666
+ staged: BigInt(e.staged.toString()),
667
+ lastSeenIndex: parseFloat(precise_number_1.PreciseNumber.fromRaw(e.lastSeenIndex[0]).valueString),
668
+ })),
669
+ })),
670
+ };
671
+ }
258
672
  function deserializeVault(x) {
259
673
  return {
260
674
  syProgram: x.syProgram,
@@ -285,6 +699,243 @@ function deserializeVault(x) {
285
699
  maxPySupply: BigInt(x.maxPySupply.toString()),
286
700
  };
287
701
  }
702
+ function deserializeOrderbook(data) {
703
+ let offset = 0;
704
+ // 1) Skip Anchor discriminator
705
+ offset += anchor_1.DISCRIMINATOR_SIZE;
706
+ const readPubkey = () => {
707
+ const pk = new anchor_1.web3.PublicKey(data.slice(offset, offset + 32));
708
+ offset += 32;
709
+ return pk;
710
+ };
711
+ // ConfigurationOptions struct
712
+ const thresholdAmount = data.readBigUInt64LE(offset);
713
+ offset += 8;
714
+ const lnMakerFeeRate = data.readDoubleLE(offset);
715
+ offset += 8;
716
+ const lnTakerFeeRate = data.readDoubleLE(offset);
717
+ offset += 8;
718
+ const priceDecimals = data.readUint8(offset);
719
+ offset += 1;
720
+ // Skip ConfigurationOptions padding: _placeholder_one[15] + _placeholder_two[32] + _placeholder_three[32] + _placeholder_four[32] + _reserved[1024] = 1135 bytes
721
+ offset += 1135;
722
+ // Pubkeys
723
+ const vault = readPubkey();
724
+ const yieldPosition = readPubkey();
725
+ const addressLookupTable = readPubkey();
726
+ const exponentCoreProgram = readPubkey();
727
+ const syProgram = readPubkey();
728
+ const tokenEscrowSy = readPubkey();
729
+ const tokenEscrowYt = readPubkey();
730
+ const tokenEscrowPt = readPubkey();
731
+ const cpiAccountOrderbook = readPubkey();
732
+ const admin = readPubkey();
733
+ // last_sy_exchange_rate (Number type = 32 bytes, PreciseNumber with 12 decimals)
734
+ const lastSyExchangeRateRaw = (() => {
735
+ let val = 0n;
736
+ for (let i = 0; i < 4; i++) {
737
+ val += data.readBigUInt64LE(offset + i * 8) << BigInt(i * 64);
738
+ }
739
+ return val;
740
+ })();
741
+ offset += 32;
742
+ // OrderbookFinancials struct
743
+ // Skip last_seen_sy_index (Number type = 32 bytes)
744
+ offset += 32;
745
+ const ytBalance = data.readBigUInt64LE(offset);
746
+ offset += 8;
747
+ const syBalance = data.readBigUInt64LE(offset);
748
+ offset += 8;
749
+ const ptBalance = data.readBigUInt64LE(offset);
750
+ offset += 8;
751
+ const ytFeeBalance = data.readBigUInt64LE(offset);
752
+ offset += 8;
753
+ const syFeeBalance = data.readBigUInt64LE(offset);
754
+ offset += 8;
755
+ const ptFeeBalance = data.readBigUInt64LE(offset);
756
+ offset += 8;
757
+ const stagedSyBalance = data.readBigUInt64LE(offset);
758
+ offset += 8;
759
+ const expirationTs = data.readUInt32LE(offset);
760
+ offset += 4;
761
+ // Skip financials _padding: [u8; 4]
762
+ offset += 4;
763
+ const configurationOptions = {
764
+ priceDecimals,
765
+ thresholdAmount,
766
+ lnMakerFeeRate,
767
+ lnTakerFeeRate,
768
+ };
769
+ const financials = {
770
+ expirationTs,
771
+ syBalance: syBalance,
772
+ ytBalance: ytBalance,
773
+ ptBalance: ptBalance,
774
+ ytFeeBalance,
775
+ syFeeBalance,
776
+ ptFeeBalance,
777
+ stagedSy: stagedSyBalance,
778
+ };
779
+ // console.log("financials", financials)
780
+ // ─── Parse RedBlackTree slab ───────────────────────────────────────────────
781
+ // RedBlackTree struct: root: u32, _padding: [u32; 3], allocator: NodeAllocator<...>
782
+ // Total before allocator = 4 + 12 = 16 bytes
783
+ const root = data.readUInt32LE(offset);
784
+ offset += 4;
785
+ const padding = 12; // _padding: [u32; 3] in RedBlackTree struct
786
+ offset += padding;
787
+ // NodeAllocator<T=RBNode<u32,PriceNode>, N=MAX_PRICE_NODES, R=3>
788
+ // header: size:u64, bump_index:u32, free_list_head:u32
789
+ const priceTreeSize = Number(data.readBigUInt64LE(offset));
790
+ offset += 8;
791
+ const _priceTreeBump = data.readUInt32LE(offset);
792
+ offset += 4;
793
+ const _priceTreeFreeIdx = data.readUInt32LE(offset);
794
+ offset += 4;
795
+ // each RBNode entry = registers[3] + key:u32 + first_offer:u32
796
+ const prices = [];
797
+ for (let i = 0; i < exponent_types_2.MAX_PRICE_NODES; i++) {
798
+ const left = data.readUInt32LE(offset);
799
+ offset += 4;
800
+ const right = data.readUInt32LE(offset);
801
+ offset += 4;
802
+ const parent = data.readUInt32LE(offset);
803
+ offset += 4;
804
+ offset += 4; // skip color
805
+ const key = data.readUInt32LE(offset);
806
+ offset += 4;
807
+ const firstOfferSellYt = data.readUInt32LE(offset);
808
+ offset += 4;
809
+ const firstOfferBuyYt = data.readUInt32LE(offset);
810
+ offset += 4;
811
+ const lastOfferSellYt = data.readUInt32LE(offset);
812
+ offset += 4;
813
+ const lastOfferBuyYt = data.readUInt32LE(offset);
814
+ offset += 4;
815
+ if (key === 0)
816
+ continue;
817
+ prices.push({ key, firstOfferSellYt, firstOfferBuyYt, lastOfferSellYt, lastOfferBuyYt, parent, left, right });
818
+ }
819
+ // ─── Parse Offers slab ────────────────────────────────────────────────────
820
+ // NodeAllocator<Offer, MAX_OFFERS, 4>
821
+ // header: size:u64, bump_index:u32, free_list_head:u32
822
+ const offersSize = Number(data.readBigUInt64LE(offset));
823
+ offset += 8;
824
+ const offersBumpIndex = data.readUInt32LE(offset);
825
+ offset += 4;
826
+ const offersFreeListHead = data.readUInt32LE(offset);
827
+ offset += 4;
828
+ const offers = [];
829
+ for (let i = 0; i < exponent_types_2.MAX_OFFERS; i++) {
830
+ const register = data.readUInt32LE(offset);
831
+ offset += 4;
832
+ const nextOfferPointer = data.readUInt32LE(offset);
833
+ offset += 4;
834
+ const userVaultPointer = data.readUInt32LE(offset);
835
+ offset += 4;
836
+ const pricePointer = data.readUInt32LE(offset);
837
+ offset += 4;
838
+ const amount = data.readBigUInt64LE(offset);
839
+ offset += 8;
840
+ const expiryAt = data.readUInt32LE(offset);
841
+ offset += 4;
842
+ const createdAt = data.readUInt32LE(offset);
843
+ offset += 4;
844
+ const virtualOffer = data.readUInt8(offset) !== 0;
845
+ offset += 1;
846
+ const orderTypeFlag = data.readUInt8(offset);
847
+ offset += 1;
848
+ const fillOrKill = data.readUInt8(offset) !== 0;
849
+ offset += 1;
850
+ offset += 5; // reserved padding
851
+ if (userVaultPointer === 0)
852
+ continue;
853
+ offers.push({
854
+ offerIndex: i + 1,
855
+ nextOfferPointer,
856
+ amount,
857
+ userVaultPointer,
858
+ expiryAt,
859
+ createdAt,
860
+ virtualOffer,
861
+ orderTypeFlag,
862
+ fillOrKill,
863
+ pricePointer,
864
+ });
865
+ }
866
+ // ─── Parse UserEscrow slab ────────────────────────────────────────────────
867
+ // NodeAllocator<UserEscrow, MAX_USER_ESCROWS, 2>
868
+ // header: size:u64, bump_index:u32, free_list_head:u32
869
+ const escSize = Number(data.readBigUInt64LE(offset));
870
+ offset += 8;
871
+ const _escBump = data.readUInt32LE(offset);
872
+ offset += 4;
873
+ const _escFreeIdx = data.readUInt32LE(offset);
874
+ offset += 4;
875
+ // each Node = [ no registers ] + UserEscrow.value
876
+ const userEscrows = [];
877
+ for (let i = 0; i < exponent_types_2.MAX_USER_ESCROWS; i++) {
878
+ const register = data.readUInt32LE(offset);
879
+ offset += 4;
880
+ const register2 = data.readUInt32LE(offset);
881
+ offset += 4;
882
+ const user = new anchor_1.web3.PublicKey(data.slice(offset, offset + 32));
883
+ offset += 32;
884
+ const yieldIndexRaw = [[new anchor_1.BN(0), new anchor_1.BN(0), new anchor_1.BN(0), new anchor_1.BN(0)]];
885
+ for (let word = 0; word < 4; word++) {
886
+ yieldIndexRaw[0][word] = new anchor_1.BN(data.subarray(offset + word * 8, offset + (word + 1) * 8), "le");
887
+ }
888
+ const yieldIndex = deserializeAnchorizedPNum(yieldIndexRaw);
889
+ offset += 32;
890
+ const ptAmount = data.readBigUInt64LE(offset);
891
+ offset += 8;
892
+ const syAmount = data.readBigUInt64LE(offset);
893
+ offset += 8;
894
+ const ytAmount = data.readBigUInt64LE(offset);
895
+ offset += 8;
896
+ const stakedYtAmount = data.readBigInt64LE(offset);
897
+ offset += 8;
898
+ const staged = data.readBigInt64LE(offset);
899
+ offset += 8;
900
+ offset += 8; // reserved
901
+ // if (user.toBase58() == "11111111111111111111111111111111") continue
902
+ userEscrows.push({ user, yieldIndex, ptAmount, syAmount, ytAmount, stakedYtAmount, staged });
903
+ }
904
+ // ─── Finally, seed_id + signer_bump + reserved ─────────────────────────────
905
+ // seed_id: [u8; 4]
906
+ const seedId = [
907
+ data.readUInt8(offset),
908
+ data.readUInt8(offset + 1),
909
+ data.readUInt8(offset + 2),
910
+ data.readUInt8(offset + 3),
911
+ ];
912
+ offset += 4;
913
+ // signer_bump: [u8; 1]
914
+ const signerBump = data.readUInt8(offset);
915
+ offset += 1;
916
+ // _reserved: [u8; 3] - skip
917
+ offset += 3;
918
+ return {
919
+ vault,
920
+ yieldPosition,
921
+ addressLookupTable,
922
+ exponentCoreProgram,
923
+ syProgram,
924
+ admin,
925
+ tokenEscrowSy,
926
+ tokenEscrowYt,
927
+ tokenEscrowPt,
928
+ cpiAccountOrderbook,
929
+ lastSyExchangeRate: lastSyExchangeRateRaw,
930
+ financials,
931
+ prices,
932
+ configurationOptions,
933
+ offers,
934
+ userEscrows,
935
+ offersBumpIndex,
936
+ offersFreeListHead,
937
+ };
938
+ }
288
939
  function deserializeMarginfiSyMeta(x) {
289
940
  return {
290
941
  ...x,
@@ -320,6 +971,7 @@ function deserializeLpPosition(x) {
320
971
  })),
321
972
  };
322
973
  }
974
+ exports.deserializeLpPosition = deserializeLpPosition;
323
975
  function deserializeYtPosition(x) {
324
976
  return {
325
977
  owner: x.owner,
@@ -329,6 +981,7 @@ function deserializeYtPosition(x) {
329
981
  emissions: x.emissions.map(deserializeYieldTokenTracker),
330
982
  };
331
983
  }
984
+ exports.deserializeYtPosition = deserializeYtPosition;
332
985
  function deserializeYieldTokenTracker(x) {
333
986
  return {
334
987
  staged: BigInt(x.staged.toString()),
@@ -340,10 +993,13 @@ async function fetchKaminoReserve(address, connection) {
340
993
  if (!reserve) {
341
994
  throw new Error("Reserve not found");
342
995
  }
996
+ const rawScopePriceFeed = reserve.config.tokenInfo.scopeConfiguration.priceFeed;
997
+ const scopePriceFeed = rawScopePriceFeed.equals(anchor_1.web3.PublicKey.default) ? undefined : rawScopePriceFeed;
343
998
  return {
344
999
  lendingMarket: reserve.lendingMarket,
345
1000
  baseMint: reserve.liquidity.mintPubkey,
346
1001
  assetShareValue: reserve.getCollateralExchangeRate(),
1002
+ scopePriceFeed,
347
1003
  };
348
1004
  }
349
1005
  exports.fetchKaminoReserve = fetchKaminoReserve;
@@ -403,27 +1059,20 @@ exports.deserializeEmission = deserializeEmission;
403
1059
  function deserializeAnchorizedPNum(x) {
404
1060
  return parseFloat(precise_number_1.PreciseNumber.fromRaw(x[0]).valueString);
405
1061
  }
1062
+ /** Convert PreciseNumber (Number type in Rust) from Anchor format to raw 256-bit bigint */
1063
+ function anchorizedPNumToRawBigint(pnum) {
1064
+ const bnArray = pnum[0];
1065
+ let val = 0n;
1066
+ for (let i = 0; i < 4; i++) {
1067
+ val += BigInt(bnArray[i].toString()) << BigInt(i * 64);
1068
+ }
1069
+ return val;
1070
+ }
1071
+ exports.anchorizedPNumToRawBigint = anchorizedPNumToRawBigint;
406
1072
  /** Fetch the exchange rate of a JitoRestaking vault's VRT to JitoSOL */
407
1073
  async function fetchJitoVaultData({ connection, vaultAddress, }) {
408
1074
  const vaultAccountInfo = await connection.getAccountInfo(vaultAddress);
409
- const d = vaultAccountInfo.data;
410
- // the vault has an 8 byte discriminator at the beginning
411
- const discriminatorOffset = 8;
412
- const vrtMintOffset = 32 + discriminatorOffset;
413
- const vrtSupplyOffset = 96 + discriminatorOffset;
414
- const jitoVaultTotalDepositsOffset = 104 + discriminatorOffset;
415
- const mintBase = new anchor_1.web3.PublicKey(d.slice(vrtMintOffset, vrtMintOffset + 32));
416
- // For Borsh, numbers are serialized in little-endian format
417
- const jitoVaultTotalSharesBuffer = d.slice(vrtSupplyOffset, vrtSupplyOffset + 8);
418
- const jitoVaultTotalShares = new anchor_1.BN(jitoVaultTotalSharesBuffer, "le");
419
- const jitoVaultTotalDepositsBuffer = d.slice(jitoVaultTotalDepositsOffset, jitoVaultTotalDepositsOffset + 8);
420
- const jitoVaultTotalDeposits = new anchor_1.BN(jitoVaultTotalDepositsBuffer, "le");
421
- const jitoVaultTotalSharesD = new decimal_js_1.default(jitoVaultTotalShares.toString());
422
- const jitoVaultTotalDepositsD = new decimal_js_1.default(jitoVaultTotalDeposits.toString());
423
- const exchangeRate = jitoVaultTotalDepositsD.isZero()
424
- ? "1.0"
425
- : jitoVaultTotalDepositsD.div(jitoVaultTotalSharesD).toString();
426
- return { exchangeRate: parseFloat(exchangeRate), mintBase };
1075
+ return (0, jito_1.decodeJitoVaultData)(vaultAccountInfo.data);
427
1076
  }
428
1077
  async function fetchJitoSolToSolExchangeRate({ connection, interfaceType, accounts, }) {
429
1078
  if (interfaceType.splStakePool) {
@@ -438,16 +1087,13 @@ async function fetchSplStakePoolIndex({ connection, stakePool, }) {
438
1087
  }
439
1088
  exports.fetchSplStakePoolIndex = fetchSplStakePoolIndex;
440
1089
  async function fetchPerenaStablePoolData({ connection, perenaStablePool, }) {
441
- const [lpMint, _] = anchor_1.web3.PublicKey.findProgramAddressSync([perenaStablePool.toBuffer(), Buffer.from("liquidity")], new anchor_1.web3.PublicKey("NUMERUNsFCP3kuNmWZuXtm1AaQCPj9uw6Guv2Ekoi5P"));
1090
+ const lpMint = (0, perena_1.getPerenaLpMint)(perenaStablePool);
442
1091
  const [accountInfo, lpMintInfo] = await connection.getMultipleAccountsInfo([perenaStablePool, lpMint]);
443
- const lpMintDeserialized = spl_token_2.MintLayout.decode(lpMintInfo.data);
444
- const d = accountInfo.data;
445
- const discriminatorOffset = 8;
446
- const invTOffset = discriminatorOffset + 32 + 32 + 32 + 32; // 4 Pubkeys before invT
447
- const invTBuffer = d.slice(invTOffset, invTOffset + 8);
448
- const invT = Buffer.from(invTBuffer).readBigUInt64LE(0);
449
- const exchangeRate = new decimal_js_1.default(invT.toString()).div(new decimal_js_1.default(lpMintDeserialized.supply.toString())).toString();
450
- return { lpSupply: lpMintDeserialized.supply, invT, exchangeRate, lpMint };
1092
+ const { lpSupply, invT, exchangeRate } = (0, perena_1.getPerenaStablePoolData)({
1093
+ perenaStablePoolData: accountInfo.data,
1094
+ lpMintData: lpMintInfo.data,
1095
+ });
1096
+ return { lpSupply, invT, exchangeRate, lpMint };
451
1097
  }
452
1098
  exports.fetchPerenaStablePoolData = fetchPerenaStablePoolData;
453
1099
  /**
@@ -477,40 +1123,14 @@ exports.fetchJitoRestaking = fetchJitoRestaking;
477
1123
  * and tracks last_* values to compute the next index increment similar to the program.
478
1124
  */
479
1125
  async function fetchJupiterPerpsIndex({ connection, pool, lastFeeUsdResetUnixTimestamp, lastRealizedFeeUsd, lastAumUsd, currentIndex, lastRealizedFeeUsdUpdateUnixTimestamp, }) {
480
- // Decode pool account using helper that leverages Anchor 0.29.0
481
1126
  const accountInfo = await connection.getAccountInfo(pool);
482
- const account = (0, jupiter_perps_idl_1.decodePoolAccount)(accountInfo?.data);
483
- const SECONDS_PER_YEAR = 365 * 24 * 60 * 60;
484
- let newFeesBn;
485
- if (lastRealizedFeeUsdUpdateUnixTimestamp === 0) {
486
- newFeesBn = new anchor_1.BN(account.poolApr.realizedFeeUsd.toString());
487
- }
488
- else if (Number(account.poolApr.lastUpdated) > lastFeeUsdResetUnixTimestamp) {
489
- const timeBetweenResets = Number(account.poolApr.lastUpdated) - lastFeeUsdResetUnixTimestamp;
490
- const feeAprBps = new anchor_1.BN(account.poolApr.feeAprBps);
491
- const estTotalFees = lastAumUsd.gt(new anchor_1.BN(0)) && timeBetweenResets > 0
492
- ? feeAprBps.mul(lastAumUsd).mul(new anchor_1.BN(timeBetweenResets)).div(new anchor_1.BN(SECONDS_PER_YEAR)).div(new anchor_1.BN(10_000))
493
- : new anchor_1.BN(0);
494
- const missingFees = estTotalFees.sub(lastRealizedFeeUsd);
495
- const feesSinceReset = new anchor_1.BN(account.poolApr.realizedFeeUsd.toString());
496
- newFeesBn = missingFees.add(feesSinceReset);
497
- }
498
- else {
499
- newFeesBn = new anchor_1.BN(account.poolApr.realizedFeeUsd.toString()).sub(lastRealizedFeeUsd);
500
- }
501
- const aumUsd = new anchor_1.BN(account.aumUsd.toString());
502
- const indexIncrease = new decimal_js_1.default(newFeesBn.toString()).div(new decimal_js_1.default(aumUsd.toString())).toNumber();
503
- const currentIndexNum = parseFloat(precise_number_1.PreciseNumber.fromRaw(currentIndex[0]).valueString);
504
- const nextIndex = currentIndexNum + indexIncrease;
505
- return {
506
- index: nextIndex,
507
- newState: {
508
- lastAumUsd: aumUsd,
509
- lastRealizedFeeUsd: new anchor_1.BN(account.poolApr.realizedFeeUsd.toString()),
510
- lastFeeUsdResetUnixTimestamp: Number(account.poolApr.lastUpdated),
511
- lastRealizedFeeUsdUpdateUnixTimestamp: Math.floor(Date.now() / 1000),
512
- },
513
- };
1127
+ return (0, jupiter_1.calculateJupiterPerpsIndex)({ pool: accountInfo.data }, {
1128
+ lastFeeUsdResetUnixTimestamp,
1129
+ lastRealizedFeeUsd,
1130
+ lastAumUsd,
1131
+ currentIndex,
1132
+ lastRealizedFeeUsdUpdateUnixTimestamp,
1133
+ });
514
1134
  }
515
1135
  exports.fetchJupiterPerpsIndex = fetchJupiterPerpsIndex;
516
1136
  async function fetchPyth(connection) {
@@ -543,28 +1163,27 @@ async function fetchGenericSyMetaIndex({ connection, genericSyMeta, }) {
543
1163
  }
544
1164
  exports.fetchGenericSyMetaIndex = fetchGenericSyMetaIndex;
545
1165
  async function fetchFragmetricIndex({ connection, fragmetricFund, }) {
546
- const account = await connection.getAccountInfo(fragmetricFund);
547
- const coder = new anchor_2.BorshCoder(fragmetric_idl_1.IDL);
548
- const data = coder.accounts.decode("FundAccount", account.data);
549
- const index = Number(data.one_receipt_token_as_sol) / Number(10 ** data.receipt_token_decimals);
550
- const receiptTokenMint = new anchor_1.web3.PublicKey(data.receipt_token_mint);
551
- const wrappedTokenMint = new anchor_1.web3.PublicKey(data.wrapped_token.mint);
1166
+ const fragmetricFundRaw = await connection.getAccountInfo(fragmetricFund);
1167
+ const { index, receiptTokenMint, wrappedTokenMint } = (0, fragmetric_1.calculateFragmetricIndex)({
1168
+ fragmetricFund: fragmetricFundRaw.data,
1169
+ });
552
1170
  return { index, receiptTokenMint, wrappedTokenMint };
553
1171
  }
554
1172
  exports.fetchFragmetricIndex = fetchFragmetricIndex;
555
1173
  async function fetchJupiterLendIndex({ connection, jupiterLendAccount, }) {
556
1174
  const account = await connection.getAccountInfo(jupiterLendAccount);
557
- const coder = new anchor_2.BorshCoder(jupiter_lend_idl_1.IDL);
558
- const data = coder.accounts.decode("Lending", account.data);
559
- const index = Number(data.token_exchange_price) / Number(10 ** 12);
560
- const tokenReservesLiquidity = data.token_reserves_liquidity;
561
- const lendingSupplyPosition = data.supply_position_on_liquidity;
562
- const rewardsRateModel = data.rewards_rate_model;
563
1175
  const rateModel = jupiterLendAccount.equals(new anchor_1.web3.PublicKey("BeAqbxfrcXmzEYT2Ra62oW2MqkuFDHaCtps47Mzg6Zj3"))
564
1176
  ? new anchor_1.web3.PublicKey("Acvyi9HBGmqh3Exe1N4PjBVyY8fokq2AdC6fSLqV6KSo")
565
1177
  : new anchor_1.web3.PublicKey("6iHHKAK9Mqjn57CVmWe4szAPyTH8s8pniXSj6vWaKW5r");
566
- console.log("token reserves liquidity in fetch jupiter lend index", tokenReservesLiquidity.toBase58());
567
- return { index, baseTokenMint: data.mint, tokenReservesLiquidity, lendingSupplyPosition, rewardsRateModel, rateModel };
1178
+ const { index, baseTokenMint, tokenReservesLiquidity, lendingSupplyPosition, rewardsRateModel } = (0, jupiter_1.calculateJupiterLendIndex)({ jupiterLend: account.data });
1179
+ return {
1180
+ rateModel,
1181
+ index,
1182
+ baseTokenMint,
1183
+ tokenReservesLiquidity,
1184
+ lendingSupplyPosition,
1185
+ rewardsRateModel,
1186
+ };
568
1187
  }
569
1188
  exports.fetchJupiterLendIndex = fetchJupiterLendIndex;
570
1189
  async function fetchKaminoVaultIndex({ connection, kaminoVaultAccount, }) {
@@ -593,24 +1212,18 @@ async function fetchKaminoVaultIndex({ connection, kaminoVaultAccount, }) {
593
1212
  exports.fetchKaminoVaultIndex = fetchKaminoVaultIndex;
594
1213
  async function fetchFragmetricSupportedTokenIndex({ connection, fragmetricFund, index, }) {
595
1214
  const account = await connection.getAccountInfo(fragmetricFund);
596
- const coder = new anchor_2.BorshCoder(fragmetric_idl_1.IDL);
597
- const data = coder.accounts.decode("FundAccount", account.data);
598
- const syIndex = Number(10 ** data.supported_tokens[index].decimals) /
599
- Number(data.supported_tokens[index].one_token_as_receipt_token);
600
- const receiptTokenMint = new anchor_1.web3.PublicKey(data.receipt_token_mint);
601
- const wrappedTokenMint = new anchor_1.web3.PublicKey(data.wrapped_token.mint);
602
- return { index: syIndex, receiptTokenMint, wrappedTokenMint };
1215
+ return (0, fragmetric_1.calculateFragmetricSupportedTokenIndex)({ fragmetricFund: account.data }, index);
603
1216
  }
604
1217
  exports.fetchFragmetricSupportedTokenIndex = fetchFragmetricSupportedTokenIndex;
605
1218
  async function fetchMeteoraIndex({ connection, accounts, onChainTime, }) {
606
1219
  try {
607
1220
  const VIRTUAL_PRICE_PRECISION = new anchor_1.BN(100_000_000);
608
- const pool = await (0, meteora_idl_1.decodePoolAccount)(connection, accounts.pool);
1221
+ const pool = await (0, meteora_idl_1.fetchPoolAccount)(connection, accounts.pool);
609
1222
  const poolMint = await (0, spl_token_1.getMint)(connection, pool.lpMint);
610
1223
  const poolLpSupply = new anchor_1.BN(poolMint.supply.toString());
611
1224
  const poolLpDecimals = Number(poolMint.decimals.toString());
612
- const vaultA = await (0, meteora_idl_1.decodeVaultAccount)(connection, accounts.vaultA);
613
- const vaultB = await (0, meteora_idl_1.decodeVaultAccount)(connection, accounts.vaultB);
1225
+ const vaultA = await (0, meteora_idl_1.fetchVaultAccount)(connection, accounts.vaultA);
1226
+ const vaultB = await (0, meteora_idl_1.fetchVaultAccount)(connection, accounts.vaultB);
614
1227
  const vaultLpMintA = await (0, spl_token_1.getMint)(connection, vaultA.lpMint);
615
1228
  const vaultLpMintB = await (0, spl_token_1.getMint)(connection, vaultB.lpMint);
616
1229
  const vaultALpSupply = vaultLpMintA.supply;
@@ -640,7 +1253,6 @@ async function fetchMeteoraIndex({ connection, accounts, onChainTime, }) {
640
1253
  exports.fetchMeteoraIndex = fetchMeteoraIndex;
641
1254
  async function fetchAdrenaIndex({ connection, accounts, previousTotalFees, currentIndex, }) {
642
1255
  try {
643
- const zero = new anchor_1.BN(0);
644
1256
  // Fetch all account data in a single RPC call
645
1257
  const accountInfos = await connection.getMultipleAccountsInfo([
646
1258
  accounts.pool,
@@ -652,19 +1264,13 @@ async function fetchAdrenaIndex({ connection, accounts, previousTotalFees, curre
652
1264
  if (!accountInfos[0] || !accountInfos[1] || !accountInfos[2] || !accountInfos[3] || !accountInfos[4]) {
653
1265
  throw new Error("One or more Adrena accounts not found");
654
1266
  }
655
- // Decode accounts using adrena-idl package functions
656
- const { poolAccount, custodyAccounts } = (0, adrena_idl_1.decodePoolAndCustodyAccounts)(accountInfos);
657
- // @ts-ignore
658
- const currentTotalFees = (0, adrena_idl_1.calculateTotalFeesFromCustodies)(custodyAccounts);
659
- let yieldIncrement;
660
- let aumValue = poolAccount.aumUsd.low;
661
- if (aumValue.eq(zero)) {
662
- yieldIncrement = 0;
663
- }
664
- let feeDifference = currentTotalFees.gte(previousTotalFees) ? currentTotalFees.sub(previousTotalFees) : zero;
665
- yieldIncrement = feeDifference.toNumber() / aumValue.toNumber();
666
- let currentIndexNumber = parseFloat(precise_number_1.PreciseNumber.fromRaw(currentIndex[0]).valueString);
667
- return { index: currentIndexNumber + yieldIncrement };
1267
+ return (0, adrena_1.calculateAdrenaIndex)({
1268
+ pool: accountInfos[0].data,
1269
+ custody1: accountInfos[1].data,
1270
+ custody2: accountInfos[2].data,
1271
+ custody3: accountInfos[3].data,
1272
+ custody4: accountInfos[4].data,
1273
+ }, previousTotalFees, currentIndex);
668
1274
  }
669
1275
  catch (error) {
670
1276
  throw error;
@@ -672,70 +1278,75 @@ async function fetchAdrenaIndex({ connection, accounts, previousTotalFees, curre
672
1278
  }
673
1279
  exports.fetchAdrenaIndex = fetchAdrenaIndex;
674
1280
  async function fetchSanctumIndex({ connection, accounts, }) {
675
- try {
676
- const poolState = await (0, sanctum_idl_1.decodePoolStateAccount)(connection, accounts.poolState);
677
- const lpMint = await (0, spl_token_1.getMint)(connection, accounts.lpMint);
678
- const zeroBn = new anchor_1.BN(0);
679
- const precision = new anchor_1.BN(10 ** lpMint.decimals);
680
- const lpTokenSupplyBn = new anchor_1.BN(lpMint.supply.toString());
681
- const poolTotalSolValueBn = new anchor_1.BN(poolState.totalSolValue.toString());
682
- if (lpTokenSupplyBn.eq(zeroBn) || poolTotalSolValueBn.eq(zeroBn)) {
683
- return 1;
684
- }
685
- const exchangeRateBn = lpTokenSupplyBn.mul(precision).div(poolTotalSolValueBn);
686
- const exchangeRate = new decimal_js_1.default(exchangeRateBn.toString()).div(precision.toString()).toNumber();
687
- return 1 / exchangeRate; // We return inverse exchange rate
688
- }
689
- catch (error) {
690
- throw error;
691
- }
1281
+ const [poolStateAccountRaw, lpMintAccountRaw] = await Promise.all([accounts.poolState, accounts.lpMint].map((pk) => connection.getAccountInfo(pk)));
1282
+ return (0, sanctum_1.calculateSanctumIndex)({
1283
+ poolStateAccountData: poolStateAccountRaw.data,
1284
+ lpMintAccountData: lpMintAccountRaw.data,
1285
+ });
692
1286
  }
693
1287
  exports.fetchSanctumIndex = fetchSanctumIndex;
694
1288
  async function fetchSolsticeRedemptionRate({ connection, yieldPool, vestingSchedule, }) {
695
- try {
696
- // Fetch both accounts in a single RPC call for efficiency
697
- const accountInfos = await connection.getMultipleAccountsInfo([yieldPool, vestingSchedule]);
698
- if (!accountInfos[0] || !accountInfos[1]) {
699
- throw new Error("One or more Solstice accounts not found");
700
- }
701
- // Decode accounts using solstice-idl package function
702
- const { yieldPoolAccount, vestingScheduleAccount } = (0, solstice_idl_1.decodeYieldPoolAndVestingScheduleAccounts)(accountInfos);
703
- // Extract fields for calculation (convert BN to bigint)
704
- const poolTotalAssets = BigInt(yieldPoolAccount.total_assets.toString());
705
- const poolSharesSupply = BigInt(yieldPoolAccount.shares_supply.toString());
706
- const vestingAmount = BigInt(vestingScheduleAccount.vesting_amount.toString());
707
- // Calculate total vested assets (mimicking the Rust logic)
708
- let totalVestedAssets;
709
- if (vestingAmount === 0n) {
710
- totalVestedAssets = poolTotalAssets; // If no vesting amount, all assets are considered vested
711
- }
712
- else {
713
- const vestingStart = BigInt(vestingScheduleAccount.start_time.toString());
714
- const vestingEnd = BigInt(vestingScheduleAccount.end_time.toString());
715
- const currentTime = BigInt(Math.floor(Date.now() / 1000)); // Current Unix timestamp
716
- // Calculate unvested amount
717
- let unvestedAmount;
718
- if (currentTime > vestingEnd) {
719
- unvestedAmount = 0n; // If current time passed the vesting end, no unvested amount
720
- }
721
- else {
722
- unvestedAmount = (vestingAmount * (vestingEnd - currentTime)) / (vestingEnd - vestingStart);
723
- }
724
- totalVestedAssets = poolTotalAssets - unvestedAmount;
725
- }
726
- // Calculate redemption rate: (total_vested_assets + 1) / (pool_shares_supply + 1)
727
- const redemptionRate = Number(totalVestedAssets + 1n) / Number(poolSharesSupply + 1n);
728
- return {
729
- redemptionRate,
730
- totalAssets: poolTotalAssets.toString(),
731
- sharesSupply: poolSharesSupply.toString(),
732
- vestingAmount: vestingAmount.toString(),
733
- totalVestedAssets: totalVestedAssets.toString(),
734
- };
735
- }
736
- catch (error) {
737
- throw error;
1289
+ // Fetch both accounts in a single RPC call for efficiency
1290
+ const accountInfos = await connection.getMultipleAccountsInfo([yieldPool, vestingSchedule]);
1291
+ if (!accountInfos[0] || !accountInfos[1]) {
1292
+ throw new Error("One or more Solstice accounts not found");
738
1293
  }
1294
+ return (0, solstice_1.calculateSolsticeRedemptionRate)({ yieldPool: accountInfos[0].data, vestingSchedule: accountInfos[1].data });
739
1295
  }
740
1296
  exports.fetchSolsticeRedemptionRate = fetchSolsticeRedemptionRate;
1297
+ const REFLECT_ORACLE_LEN = 17;
1298
+ const REFLECT_MAX_STALENESS_SLOTS = 15000000;
1299
+ async function fetchReflectRedemptionRate({ connection, oracle, }) {
1300
+ const accountInfo = await connection.getAccountInfo(oracle);
1301
+ if (!accountInfo) {
1302
+ throw new Error("Reflect oracle account not found");
1303
+ }
1304
+ if (accountInfo.data.length !== REFLECT_ORACLE_LEN) {
1305
+ throw new Error(`Reflect oracle account has invalid length: ${accountInfo.data.length}`);
1306
+ }
1307
+ const slot = Number(accountInfo.data.readBigUInt64LE(0));
1308
+ const price = Number(accountInfo.data.readBigUInt64LE(8));
1309
+ const precision = accountInfo.data.readUInt8(16);
1310
+ const currentSlot = await connection.getSlot();
1311
+ if (slot > currentSlot) {
1312
+ throw new Error("Reflect oracle slot is ahead of the current slot");
1313
+ }
1314
+ const slotDelta = currentSlot - slot;
1315
+ if (slotDelta > REFLECT_MAX_STALENESS_SLOTS) {
1316
+ throw new Error("Reflect oracle data is stale");
1317
+ }
1318
+ const scale = Math.pow(10, precision);
1319
+ if (scale === 0) {
1320
+ throw new Error("Invalid oracle precision");
1321
+ }
1322
+ return price / scale;
1323
+ }
1324
+ exports.fetchReflectRedemptionRate = fetchReflectRedemptionRate;
1325
+ async function fetchOreExchangeRate({ connection, storeMint, stakeAccount, treasury, }) {
1326
+ // Fetch all accounts in parallel for efficiency
1327
+ const [storeMintInfo, stakeAccountInfo, treasuryAccountInfo] = await Promise.all([
1328
+ connection.getAccountInfo(storeMint),
1329
+ connection.getAccountInfo(stakeAccount),
1330
+ connection.getAccountInfo(treasury),
1331
+ ]);
1332
+ if (!storeMintInfo || !stakeAccountInfo || !treasuryAccountInfo) {
1333
+ throw new Error("One or more ORE accounts not found");
1334
+ }
1335
+ return (0, ore_1.calculateOreExchangeRate)({
1336
+ stakeAccount: stakeAccountInfo.data,
1337
+ treasuryAccount: treasuryAccountInfo.data,
1338
+ storeMint: storeMintInfo.data,
1339
+ });
1340
+ }
1341
+ exports.fetchOreExchangeRate = fetchOreExchangeRate;
1342
+ async function fetchChainlinkRate({ connection, priceFeed, }) {
1343
+ const accountInfo = await connection.getAccountInfo(priceFeed);
1344
+ if (!accountInfo) {
1345
+ throw new Error("Chainlink price feed account not found");
1346
+ }
1347
+ const { answer, header } = (0, chainlink_idl_1.decodeChainlinkPriceAccount)(accountInfo);
1348
+ const scale = Math.pow(10, header.decimals);
1349
+ return Number(answer) / scale;
1350
+ }
1351
+ exports.fetchChainlinkRate = fetchChainlinkRate;
741
1352
  //# sourceMappingURL=exponentFetcher.js.map