@exponent-labs/exponent-fetcher 0.1.8 → 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.
- package/build/constants.d.ts +26 -0
- package/build/constants.js +58 -0
- package/build/constants.js.map +1 -0
- package/build/exponentFetcher.d.ts +28 -3
- package/build/exponentFetcher.js +209 -96
- package/build/exponentFetcher.js.map +1 -1
- package/build/index.d.ts +1 -0
- package/build/index.js +1 -0
- package/build/index.js.map +1 -1
- package/package.json +21 -20
- package/src/constants.ts +56 -0
- package/src/exponentFetcher.ts +270 -109
- package/src/index.ts +1 -0
- package/tsconfig.json +2 -1
package/src/exponentFetcher.ts
CHANGED
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
decodeCustodyAccount,
|
|
15
15
|
decodePoolAndCustodyAccounts,
|
|
16
16
|
} from "@exponent-labs/adrena-idl"
|
|
17
|
+
import { decodeChainlinkPriceAccount } from "@exponent-labs/chainlink-idl"
|
|
17
18
|
import {
|
|
18
19
|
PROGRAM_ID as EXPONENT_CLMM_PROGRAM_ID,
|
|
19
20
|
ExponentClmm,
|
|
@@ -75,6 +76,7 @@ import { PerenaStandard } from "@exponent-labs/perena-sy-idl"
|
|
|
75
76
|
import { PreciseNumber } from "@exponent-labs/precise-number"
|
|
76
77
|
import { decodeYieldPoolAndVestingScheduleAccounts } from "@exponent-labs/solstice-idl"
|
|
77
78
|
|
|
79
|
+
import { IgnoredClmmEntityError, isMarketIgnored, isTicksIgnored } from "./constants"
|
|
78
80
|
import { calculateAdrenaIndex } from "./utils/adrena"
|
|
79
81
|
import { calculateFragmetricIndex, calculateFragmetricSupportedTokenIndex } from "./utils/fragmetric"
|
|
80
82
|
import { decodeJitoVaultData } from "./utils/jito"
|
|
@@ -90,12 +92,6 @@ export function serializeAnchorizedPNumFromJson(pnum: AnchorizedPNum): Anchorize
|
|
|
90
92
|
return { 0: serializedArray }
|
|
91
93
|
}
|
|
92
94
|
|
|
93
|
-
function readU128LE(buf: Buffer, offset: number): bigint {
|
|
94
|
-
const lo = buf.readBigUInt64LE(offset)
|
|
95
|
-
const hi = buf.readBigUInt64LE(offset + 8)
|
|
96
|
-
return (hi << 64n) + lo
|
|
97
|
-
}
|
|
98
|
-
|
|
99
95
|
export function deserializeAnchorizedPNumFromJson(serialized: AnchorizedPNumJson): AnchorizedPNum {
|
|
100
96
|
const bnArray = serialized[0].map((str) => new BN(str))
|
|
101
97
|
return { 0: bnArray }
|
|
@@ -410,6 +406,7 @@ export class ExponentFetcher {
|
|
|
410
406
|
})
|
|
411
407
|
|
|
412
408
|
return marketsProgramAccounts
|
|
409
|
+
.filter(({ pubkey }) => !isMarketIgnored(pubkey.toBase58()))
|
|
413
410
|
.map(({ account, pubkey }) => {
|
|
414
411
|
try {
|
|
415
412
|
return this.exponentClmmProgram.coder.accounts.decode("marketThree", account.data)
|
|
@@ -421,11 +418,16 @@ export class ExponentFetcher {
|
|
|
421
418
|
}
|
|
422
419
|
|
|
423
420
|
async fetchMarketThree(address: web3.PublicKey): Promise<MarketThree> {
|
|
421
|
+
const addressStr = address.toBase58()
|
|
422
|
+
if (isMarketIgnored(addressStr)) {
|
|
423
|
+
throw new IgnoredClmmEntityError("market", addressStr)
|
|
424
|
+
}
|
|
425
|
+
|
|
424
426
|
try {
|
|
425
427
|
const m: MarketThreeRaw = await this.exponentClmmProgram.account.marketThree.fetch(address)
|
|
426
428
|
return deserializeMarketThree(m)
|
|
427
429
|
} catch (e) {
|
|
428
|
-
console.error(`Error fetching market ${
|
|
430
|
+
console.error(`Error fetching market ${addressStr}`)
|
|
429
431
|
console.error(e)
|
|
430
432
|
throw e
|
|
431
433
|
}
|
|
@@ -445,25 +447,74 @@ export class ExponentFetcher {
|
|
|
445
447
|
],
|
|
446
448
|
})
|
|
447
449
|
|
|
448
|
-
return ticksAccounts
|
|
450
|
+
return ticksAccounts
|
|
451
|
+
.filter(({ pubkey }) => !isTicksIgnored(pubkey.toBase58()))
|
|
452
|
+
.map(({ account }) => deserializeMarketThreeTicks(account.data))
|
|
449
453
|
}
|
|
450
454
|
|
|
451
455
|
async fetchMarketThreeTicks(address: web3.PublicKey): Promise<Ticks> {
|
|
456
|
+
const addressStr = address.toBase58()
|
|
457
|
+
if (isTicksIgnored(addressStr)) {
|
|
458
|
+
throw new IgnoredClmmEntityError("ticks", addressStr)
|
|
459
|
+
}
|
|
460
|
+
|
|
452
461
|
try {
|
|
453
462
|
const m = (await this.connection.getAccountInfo(address)).data
|
|
454
463
|
return deserializeMarketThreeTicks(m)
|
|
455
464
|
} catch (e) {
|
|
456
|
-
console.error(`Error fetching
|
|
465
|
+
console.error(`Error fetching ticks ${addressStr}`)
|
|
457
466
|
console.error(e)
|
|
458
467
|
throw e
|
|
459
468
|
}
|
|
460
469
|
}
|
|
461
470
|
}
|
|
462
471
|
|
|
472
|
+
/**
|
|
473
|
+
* Deserializes a CLMM Ticks account from raw buffer data.
|
|
474
|
+
*
|
|
475
|
+
* Layout matches Rust struct `Ticks` with `RedBlackTree<u32, Tick, 1000>`:
|
|
476
|
+
* - Discriminator: 8 bytes
|
|
477
|
+
* - RedBlackTree header: root(4) + padding(12) + size(8) + bump(4) + freeIdx(4) = 32 bytes
|
|
478
|
+
* - 1000 RBTree nodes, each: nodeHeader(16) + key(4) + padding(4) + Tick = variable bytes
|
|
479
|
+
* - Ticks footer: market(32) + feeGrowthPt(16) + feeGrowthSy(16) + prefixSum(8) + spotPrice(8) + currentTick(4) + padding(12) = 96 bytes
|
|
480
|
+
*/
|
|
463
481
|
export function deserializeMarketThreeTicks(data: Buffer): Ticks {
|
|
464
|
-
|
|
465
|
-
const
|
|
466
|
-
|
|
482
|
+
const MAX_TICK_NODES = 1000
|
|
483
|
+
const PERSONAL_TICK_YIELD_TRACKER_SIZE = 2
|
|
484
|
+
let offset = 8 // Skip discriminator
|
|
485
|
+
|
|
486
|
+
// ─── Helper functions ─────────────────────────────────────────────────────
|
|
487
|
+
const readU64 = (): bigint => {
|
|
488
|
+
const val = data.readBigUInt64LE(offset)
|
|
489
|
+
offset += 8
|
|
490
|
+
return val
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
const readU128 = (): bigint => {
|
|
494
|
+
const lo = data.readBigUInt64LE(offset)
|
|
495
|
+
const hi = data.readBigUInt64LE(offset + 8)
|
|
496
|
+
offset += 16
|
|
497
|
+
return (hi << 64n) + lo
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
const readI128 = (): bigint => {
|
|
501
|
+
const lo = data.readBigUInt64LE(offset)
|
|
502
|
+
const hi = data.readBigInt64LE(offset + 8) // High part is signed
|
|
503
|
+
offset += 16
|
|
504
|
+
return (hi << 64n) + lo
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
const readF64 = (): number => {
|
|
508
|
+
const val = data.readDoubleLE(offset)
|
|
509
|
+
offset += 8
|
|
510
|
+
return val
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
const readU32 = (): number => {
|
|
514
|
+
const val = data.readUInt32LE(offset)
|
|
515
|
+
offset += 4
|
|
516
|
+
return val
|
|
517
|
+
}
|
|
467
518
|
|
|
468
519
|
const readPubkey = (): web3.PublicKey => {
|
|
469
520
|
const pk = new web3.PublicKey(data.slice(offset, offset + 32))
|
|
@@ -471,9 +522,9 @@ export function deserializeMarketThreeTicks(data: Buffer): Ticks {
|
|
|
471
522
|
return pk
|
|
472
523
|
}
|
|
473
524
|
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
const nums = []
|
|
525
|
+
/** Reads a PreciseNumber (Number type in Rust) as float - 32 bytes (4 x u64) */
|
|
526
|
+
const readPreciseNumberAsFloat = (): number => {
|
|
527
|
+
const nums: BN[] = []
|
|
477
528
|
for (let i = 0; i < 4; i++) {
|
|
478
529
|
nums.push(new BN(data.slice(offset + i * 8, offset + (i + 1) * 8), undefined, "le"))
|
|
479
530
|
}
|
|
@@ -481,108 +532,92 @@ export function deserializeMarketThreeTicks(data: Buffer): Ticks {
|
|
|
481
532
|
return parseFloat(PreciseNumber.fromRaw(nums).valueString)
|
|
482
533
|
}
|
|
483
534
|
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
const tickTreeSize = Number(data.readBigUInt64LE(offset))
|
|
496
|
-
offset += 8
|
|
535
|
+
/** Reads a PreciseNumber (Number type in Rust) as raw bigint - 32 bytes (4 x u64 = 256 bits) */
|
|
536
|
+
const readPreciseNumberAsBigint = (): bigint => {
|
|
537
|
+
let val = 0n
|
|
538
|
+
for (let i = 0; i < 4; i++) {
|
|
539
|
+
const chunk = data.readBigUInt64LE(offset + i * 8)
|
|
540
|
+
val += chunk << BigInt(i * 64)
|
|
541
|
+
}
|
|
542
|
+
offset += 32
|
|
543
|
+
return val
|
|
544
|
+
}
|
|
497
545
|
|
|
498
|
-
const
|
|
499
|
-
|
|
546
|
+
const skip = (bytes: number): void => {
|
|
547
|
+
offset += bytes
|
|
548
|
+
}
|
|
500
549
|
|
|
501
|
-
|
|
502
|
-
|
|
550
|
+
// ─── Parse RedBlackTree header ────────────────────────────────────────────
|
|
551
|
+
skip(4) // root: u32
|
|
552
|
+
skip(12) // padding to align NodeAllocator
|
|
553
|
+
skip(8) // size: u64
|
|
554
|
+
skip(4) // bump_index: u32
|
|
555
|
+
skip(4) // free_list_head: u32
|
|
503
556
|
|
|
557
|
+
// ─── Parse tick nodes ─────────────────────────────────────────────────────
|
|
504
558
|
const ticks: Tick[] = []
|
|
505
559
|
|
|
506
560
|
for (let i = 0; i < MAX_TICK_NODES; i++) {
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
const
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
const
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
const
|
|
519
|
-
|
|
520
|
-
const
|
|
521
|
-
|
|
522
|
-
const
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
const impliedRate = data.readDoubleLE(offset)
|
|
527
|
-
offset += 8
|
|
528
|
-
const principalPt = data.readBigInt64LE(offset)
|
|
529
|
-
offset += 8
|
|
530
|
-
const principalSy = data.readBigInt64LE(offset)
|
|
531
|
-
offset += 8
|
|
532
|
-
const principalShareSupply = data.readBigInt64LE(offset)
|
|
533
|
-
offset += 8
|
|
534
|
-
|
|
535
|
-
// Parse FarmYieldTrackers (3 trackers x 32 bytes each)
|
|
536
|
-
const farms = []
|
|
561
|
+
// RBNode header: left(4) + right(4) + parent(4) + color(4) = 16 bytes
|
|
562
|
+
skip(16)
|
|
563
|
+
|
|
564
|
+
// Key: u32 + padding to 8 bytes
|
|
565
|
+
const apyBasePoints = readU32()
|
|
566
|
+
skip(4) // padding
|
|
567
|
+
|
|
568
|
+
// Tick value (416 bytes total)
|
|
569
|
+
const feeGrowthOutsidePt = readU128() // 16 bytes
|
|
570
|
+
const feeGrowthOutsideSy = readU128() // 16 bytes
|
|
571
|
+
const liquidityNet = readI128() // 16 bytes
|
|
572
|
+
const liquidityGross = readU64() // 8 bytes
|
|
573
|
+
const spotPrice = readF64() // 8 bytes
|
|
574
|
+
const principalPt = readU64() // 8 bytes
|
|
575
|
+
const principalSy = readU64() // 8 bytes
|
|
576
|
+
const principalShareSupply = readPreciseNumberAsBigint() // 32 bytes - kept as bigint for arithmetic
|
|
577
|
+
|
|
578
|
+
// FarmYieldTrackers: 2 x FarmYieldTracker(32 bytes) = 64 bytes
|
|
579
|
+
const farms: { lastSeenIndex: number }[] = []
|
|
537
580
|
for (let j = 0; j < PERSONAL_TICK_YIELD_TRACKER_SIZE; j++) {
|
|
538
|
-
farms.push({ lastSeenIndex:
|
|
581
|
+
farms.push({ lastSeenIndex: readPreciseNumberAsFloat() })
|
|
539
582
|
}
|
|
540
583
|
|
|
541
|
-
//
|
|
542
|
-
const emissions = []
|
|
584
|
+
// EmissionYieldTrackers: 2 x EmissionYieldTracker(64 bytes) = 128 bytes
|
|
585
|
+
const emissions: { lastSeenIndex: number; lastPositionIndex: number }[] = []
|
|
543
586
|
for (let j = 0; j < PERSONAL_TICK_YIELD_TRACKER_SIZE; j++) {
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
587
|
+
emissions.push({
|
|
588
|
+
lastSeenIndex: readPreciseNumberAsFloat(),
|
|
589
|
+
lastPositionIndex: readPreciseNumberAsFloat(),
|
|
590
|
+
})
|
|
547
591
|
}
|
|
548
592
|
|
|
549
|
-
|
|
550
|
-
const
|
|
551
|
-
offset += 8
|
|
552
|
-
|
|
553
|
-
// Skip padding (u64)
|
|
554
|
-
offset += 8
|
|
593
|
+
const lastSplitEpoch = readU64() // 8 bytes
|
|
594
|
+
const frozenLiquidity = readU64() // 8 bytes
|
|
555
595
|
|
|
556
|
-
if (apyBasePoints === 0) continue
|
|
557
596
|
ticks.push({
|
|
558
597
|
apyBasePoints,
|
|
559
598
|
liquidityNet,
|
|
560
599
|
feeGrowthOutsidePt,
|
|
561
600
|
feeGrowthOutsideSy,
|
|
562
601
|
liquidityGross,
|
|
563
|
-
impliedRate,
|
|
602
|
+
impliedRate: spotPrice, // Legacy field name kept for compatibility
|
|
564
603
|
principalPt,
|
|
565
604
|
principalSy,
|
|
566
605
|
principalShareSupply,
|
|
567
606
|
farms,
|
|
568
607
|
emissions,
|
|
569
608
|
lastSplitEpoch,
|
|
609
|
+
frozenLiquidity,
|
|
570
610
|
})
|
|
571
|
-
// console.log(ticks)
|
|
572
611
|
}
|
|
573
612
|
|
|
574
|
-
|
|
575
|
-
const
|
|
576
|
-
|
|
577
|
-
const feeGrowthIndexGlobalSy =
|
|
578
|
-
|
|
579
|
-
const
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
offset += 8
|
|
583
|
-
const currentTick = data.readUint32LE(offset)
|
|
584
|
-
offset += 4
|
|
585
|
-
offset += 12 // padding
|
|
613
|
+
// ─── Parse Ticks footer ───────────────────────────────────────────────────
|
|
614
|
+
const market = readPubkey() // 32 bytes
|
|
615
|
+
const feeGrowthIndexGlobalPt = readU128() // 16 bytes
|
|
616
|
+
const feeGrowthIndexGlobalSy = readU128() // 16 bytes
|
|
617
|
+
const currentPrefixSum = readU64() // 8 bytes
|
|
618
|
+
const currentSpotPrice = readF64() // 8 bytes
|
|
619
|
+
const currentTick = readU32() // 4 bytes
|
|
620
|
+
skip(12) // padding
|
|
586
621
|
|
|
587
622
|
return {
|
|
588
623
|
ticksTree: ticks,
|
|
@@ -595,7 +630,76 @@ export function deserializeMarketThreeTicks(data: Buffer): Ticks {
|
|
|
595
630
|
}
|
|
596
631
|
}
|
|
597
632
|
|
|
598
|
-
|
|
633
|
+
/** Decoded account may use snake_case (from JSON IDL); normalize to camelCase for app use. */
|
|
634
|
+
function normalizeCpiContext(a: { altIndex?: number; alt_index?: number; isSigner?: boolean; is_signer?: boolean; isWritable?: boolean; is_writable?: boolean }): { altIndex: number; isSigner: boolean; isWritable: boolean } {
|
|
635
|
+
return {
|
|
636
|
+
altIndex: a.altIndex ?? (a as { alt_index?: number }).alt_index ?? 0,
|
|
637
|
+
isSigner: a.isSigner ?? (a as { is_signer?: boolean }).is_signer ?? false,
|
|
638
|
+
isWritable: a.isWritable ?? (a as { is_writable?: boolean }).is_writable ?? false,
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
function normalizeCpiAccountIndexes(
|
|
643
|
+
raw: {
|
|
644
|
+
getSyState?: unknown[]
|
|
645
|
+
get_sy_state?: unknown[]
|
|
646
|
+
withdrawSy?: unknown[]
|
|
647
|
+
withdraw_sy?: unknown[]
|
|
648
|
+
depositSy?: unknown[]
|
|
649
|
+
deposit_sy?: unknown[]
|
|
650
|
+
claimEmission?: unknown[][]
|
|
651
|
+
claim_emission?: unknown[][]
|
|
652
|
+
getPositionState?: unknown[]
|
|
653
|
+
get_position_state?: unknown[]
|
|
654
|
+
}
|
|
655
|
+
): CpiAccountIndexes {
|
|
656
|
+
const arr = (key: string, snake: string) => {
|
|
657
|
+
const a = (raw as Record<string, unknown>)[key] ?? (raw as Record<string, unknown>)[snake]
|
|
658
|
+
return Array.isArray(a) ? a.map((x) => normalizeCpiContext(x as Record<string, unknown>)) : []
|
|
659
|
+
}
|
|
660
|
+
const arr2 = (key: string, snake: string) => {
|
|
661
|
+
const a = (raw as Record<string, unknown>)[key] ?? (raw as Record<string, unknown>)[snake]
|
|
662
|
+
return Array.isArray(a) ? a.map((inner) => (Array.isArray(inner) ? inner.map((x) => normalizeCpiContext(x as Record<string, unknown>)) : [])) : []
|
|
663
|
+
}
|
|
664
|
+
return {
|
|
665
|
+
getSyState: arr("getSyState", "get_sy_state"),
|
|
666
|
+
withdrawSy: arr("withdrawSy", "withdraw_sy"),
|
|
667
|
+
depositSy: arr("depositSy", "deposit_sy"),
|
|
668
|
+
claimEmission: arr2("claimEmission", "claim_emission"),
|
|
669
|
+
getPositionState: arr("getPositionState", "get_position_state"),
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
function normalizeMarketCpiCoreIndexes(
|
|
674
|
+
raw: {
|
|
675
|
+
stripSy?: unknown[]
|
|
676
|
+
strip_sy?: unknown[]
|
|
677
|
+
mergeSy?: unknown[]
|
|
678
|
+
merge_sy?: unknown[]
|
|
679
|
+
}
|
|
680
|
+
): MarketCpiCoreIndexes {
|
|
681
|
+
const arr = (key: string, snake: string) => {
|
|
682
|
+
const a = (raw as Record<string, unknown>)[key] ?? (raw as Record<string, unknown>)[snake]
|
|
683
|
+
return Array.isArray(a) ? a.map((x) => normalizeCpiContext(x as Record<string, unknown>)) : []
|
|
684
|
+
}
|
|
685
|
+
return {
|
|
686
|
+
stripSy: arr("stripSy", "strip_sy"),
|
|
687
|
+
mergeSy: arr("mergeSy", "merge_sy"),
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
export function deserializeMarketThree(m: MarketThreeRaw): MarketThree {
|
|
692
|
+
const rawCpiSy = m.cpiSyAccounts ?? (m as unknown as { cpi_sy_accounts?: unknown }).cpi_sy_accounts
|
|
693
|
+
const rawCpiCore = m.cpiCoreAccounts ?? (m as unknown as { cpi_core_accounts?: unknown }).cpi_core_accounts
|
|
694
|
+
const cpiSyAccounts =
|
|
695
|
+
rawCpiSy != null && typeof rawCpiSy === "object"
|
|
696
|
+
? normalizeCpiAccountIndexes(rawCpiSy as Parameters<typeof normalizeCpiAccountIndexes>[0])
|
|
697
|
+
: (m.cpiSyAccounts ?? { getSyState: [], withdrawSy: [], depositSy: [], claimEmission: [], getPositionState: [] })
|
|
698
|
+
const cpiCoreAccounts =
|
|
699
|
+
rawCpiCore != null && typeof rawCpiCore === "object"
|
|
700
|
+
? normalizeMarketCpiCoreIndexes(rawCpiCore as Parameters<typeof normalizeMarketCpiCoreIndexes>[0])
|
|
701
|
+
: (m.cpiCoreAccounts ?? { stripSy: [], mergeSy: [] })
|
|
702
|
+
|
|
599
703
|
return {
|
|
600
704
|
addressLookupTable: m.addressLookupTable,
|
|
601
705
|
mintSy: m.mintSy,
|
|
@@ -608,7 +712,7 @@ function deserializeMarketThree(m: MarketThreeRaw): MarketThree {
|
|
|
608
712
|
selfAddress: m.selfAddress,
|
|
609
713
|
syProgram: m.syProgram,
|
|
610
714
|
statusFlags: m.statusFlags,
|
|
611
|
-
cpiSyAccounts
|
|
715
|
+
cpiSyAccounts,
|
|
612
716
|
isCurrentFlashSwap: m.isCurrentFlashSwap,
|
|
613
717
|
lpFarm: m.lpFarm,
|
|
614
718
|
mintYt: m.mintYt,
|
|
@@ -637,8 +741,9 @@ function deserializeMarketThree(m: MarketThreeRaw): MarketThree {
|
|
|
637
741
|
syBalance: BigInt(m.financials.syBalance.toString()),
|
|
638
742
|
liquidityBalance: BigInt(m.financials.liquidityBalance.toString()),
|
|
639
743
|
},
|
|
640
|
-
cpiCoreAccounts
|
|
744
|
+
cpiCoreAccounts,
|
|
641
745
|
exponentCoreProgram: m.exponentCoreProgram,
|
|
746
|
+
seedId: m.seedId,
|
|
642
747
|
}
|
|
643
748
|
}
|
|
644
749
|
|
|
@@ -699,7 +804,7 @@ function deserializeLpPositionCLMM(x: LpPositionCLMMRaw): LpPositionCLMM {
|
|
|
699
804
|
tickIdx: tracker.tickIdx,
|
|
700
805
|
rightTickIdx: tracker.rightTickIdx,
|
|
701
806
|
splitEpoch: BigInt(tracker.splitEpoch.toString()),
|
|
702
|
-
lpShare:
|
|
807
|
+
lpShare: anchorizedPNumToRawBigint(tracker.lpShare),
|
|
703
808
|
emissions: tracker.emissions.trackers.map((e) => ({
|
|
704
809
|
staged: BigInt(e.staged.toString()),
|
|
705
810
|
lastSeenIndex: parseFloat(PreciseNumber.fromRaw(e.lastSeenIndex[0]).valueString),
|
|
@@ -760,8 +865,8 @@ function deserializeOrderbook(data: Buffer): Orderbook {
|
|
|
760
865
|
offset += 8
|
|
761
866
|
const priceDecimals = data.readUint8(offset)
|
|
762
867
|
offset += 1
|
|
763
|
-
// Skip ConfigurationOptions padding: _placeholder_one[15] + _placeholder_two[32] + _placeholder_three[32] + _placeholder_four[32] =
|
|
764
|
-
offset +=
|
|
868
|
+
// Skip ConfigurationOptions padding: _placeholder_one[15] + _placeholder_two[32] + _placeholder_three[32] + _placeholder_four[32] + _reserved[1024] = 1135 bytes
|
|
869
|
+
offset += 1135
|
|
765
870
|
|
|
766
871
|
// Pubkeys
|
|
767
872
|
const vault = readPubkey()
|
|
@@ -775,7 +880,14 @@ function deserializeOrderbook(data: Buffer): Orderbook {
|
|
|
775
880
|
const cpiAccountOrderbook = readPubkey()
|
|
776
881
|
const admin = readPubkey()
|
|
777
882
|
|
|
778
|
-
//
|
|
883
|
+
// last_sy_exchange_rate (Number type = 32 bytes, PreciseNumber with 12 decimals)
|
|
884
|
+
const lastSyExchangeRateRaw = (() => {
|
|
885
|
+
let val = 0n
|
|
886
|
+
for (let i = 0; i < 4; i++) {
|
|
887
|
+
val += data.readBigUInt64LE(offset + i * 8) << BigInt(i * 64)
|
|
888
|
+
}
|
|
889
|
+
return val
|
|
890
|
+
})()
|
|
779
891
|
offset += 32
|
|
780
892
|
|
|
781
893
|
// OrderbookFinancials struct
|
|
@@ -818,12 +930,13 @@ function deserializeOrderbook(data: Buffer): Orderbook {
|
|
|
818
930
|
}
|
|
819
931
|
// console.log("financials", financials)
|
|
820
932
|
// ─── Parse RedBlackTree slab ───────────────────────────────────────────────
|
|
821
|
-
//
|
|
933
|
+
// RedBlackTree struct: root: u32, _padding: [u32; 3], allocator: NodeAllocator<...>
|
|
934
|
+
// Total before allocator = 4 + 12 = 16 bytes
|
|
822
935
|
|
|
823
936
|
const root = data.readUInt32LE(offset)
|
|
824
937
|
offset += 4
|
|
825
|
-
const
|
|
826
|
-
offset +=
|
|
938
|
+
const padding = 12 // _padding: [u32; 3] in RedBlackTree struct
|
|
939
|
+
offset += padding
|
|
827
940
|
|
|
828
941
|
// NodeAllocator<T=RBNode<u32,PriceNode>, N=MAX_PRICE_NODES, R=3>
|
|
829
942
|
// header: size:u64, bump_index:u32, free_list_head:u32
|
|
@@ -868,9 +981,9 @@ function deserializeOrderbook(data: Buffer): Orderbook {
|
|
|
868
981
|
|
|
869
982
|
const offersSize = Number(data.readBigUInt64LE(offset))
|
|
870
983
|
offset += 8
|
|
871
|
-
const
|
|
984
|
+
const offersBumpIndex = data.readUInt32LE(offset)
|
|
872
985
|
offset += 4
|
|
873
|
-
const
|
|
986
|
+
const offersFreeListHead = data.readUInt32LE(offset)
|
|
874
987
|
offset += 4
|
|
875
988
|
|
|
876
989
|
const offers: OfferNode[] = []
|
|
@@ -898,6 +1011,7 @@ function deserializeOrderbook(data: Buffer): Orderbook {
|
|
|
898
1011
|
offset += 5 // reserved padding
|
|
899
1012
|
if (userVaultPointer === 0) continue
|
|
900
1013
|
offers.push({
|
|
1014
|
+
offerIndex: i + 1,
|
|
901
1015
|
nextOfferPointer,
|
|
902
1016
|
amount,
|
|
903
1017
|
userVaultPointer,
|
|
@@ -929,7 +1043,12 @@ function deserializeOrderbook(data: Buffer): Orderbook {
|
|
|
929
1043
|
offset += 4
|
|
930
1044
|
const user = new web3.PublicKey(data.slice(offset, offset + 32))
|
|
931
1045
|
offset += 32
|
|
932
|
-
|
|
1046
|
+
const yieldIndexRaw: AnchorizedPNum = [[new BN(0), new BN(0), new BN(0), new BN(0)]]
|
|
1047
|
+
for (let word = 0; word < 4; word++) {
|
|
1048
|
+
yieldIndexRaw[0][word] = new BN(data.subarray(offset + word * 8, offset + (word + 1) * 8), "le")
|
|
1049
|
+
}
|
|
1050
|
+
const yieldIndex = deserializeAnchorizedPNum(yieldIndexRaw)
|
|
1051
|
+
offset += 32
|
|
933
1052
|
const ptAmount = data.readBigUInt64LE(offset)
|
|
934
1053
|
offset += 8
|
|
935
1054
|
const syAmount = data.readBigUInt64LE(offset)
|
|
@@ -941,8 +1060,8 @@ function deserializeOrderbook(data: Buffer): Orderbook {
|
|
|
941
1060
|
const staged = data.readBigInt64LE(offset)
|
|
942
1061
|
offset += 8
|
|
943
1062
|
offset += 8 // reserved
|
|
944
|
-
if (user.toBase58() == "11111111111111111111111111111111") continue
|
|
945
|
-
userEscrows.push({ user, yieldIndex
|
|
1063
|
+
// if (user.toBase58() == "11111111111111111111111111111111") continue
|
|
1064
|
+
userEscrows.push({ user, yieldIndex, ptAmount, syAmount, ytAmount, stakedYtAmount, staged })
|
|
946
1065
|
}
|
|
947
1066
|
|
|
948
1067
|
// ─── Finally, seed_id + signer_bump + reserved ─────────────────────────────
|
|
@@ -971,11 +1090,14 @@ function deserializeOrderbook(data: Buffer): Orderbook {
|
|
|
971
1090
|
tokenEscrowYt,
|
|
972
1091
|
tokenEscrowPt,
|
|
973
1092
|
cpiAccountOrderbook,
|
|
1093
|
+
lastSyExchangeRate: lastSyExchangeRateRaw,
|
|
974
1094
|
financials,
|
|
975
1095
|
prices,
|
|
976
1096
|
configurationOptions,
|
|
977
1097
|
offers,
|
|
978
1098
|
userEscrows,
|
|
1099
|
+
offersBumpIndex,
|
|
1100
|
+
offersFreeListHead,
|
|
979
1101
|
}
|
|
980
1102
|
}
|
|
981
1103
|
|
|
@@ -1161,6 +1283,7 @@ export interface MarketThree {
|
|
|
1161
1283
|
}[]
|
|
1162
1284
|
}
|
|
1163
1285
|
liquidityNetBalanceLimits: LiquidityNetBalanceLimits
|
|
1286
|
+
seedId: number[]
|
|
1164
1287
|
}
|
|
1165
1288
|
|
|
1166
1289
|
export interface Ticks {
|
|
@@ -1195,12 +1318,14 @@ export interface Tick {
|
|
|
1195
1318
|
principalSy: bigint
|
|
1196
1319
|
apyBasePoints: number
|
|
1197
1320
|
principalShareSupply: bigint
|
|
1198
|
-
/** Farm yield trackers (
|
|
1321
|
+
/** Farm yield trackers (2 trackers) */
|
|
1199
1322
|
farms: { lastSeenIndex: number }[]
|
|
1200
|
-
/** Emission yield trackers (
|
|
1323
|
+
/** Emission yield trackers (2 trackers) */
|
|
1201
1324
|
emissions: { lastSeenIndex: number; lastPositionIndex: number }[]
|
|
1202
1325
|
/** Last split epoch for this tick */
|
|
1203
1326
|
lastSplitEpoch: bigint
|
|
1327
|
+
/** Frozen liquidity that cannot be withdrawn */
|
|
1328
|
+
frozenLiquidity: bigint
|
|
1204
1329
|
}
|
|
1205
1330
|
|
|
1206
1331
|
export interface MarketThreeRaw {
|
|
@@ -1375,11 +1500,17 @@ export interface Orderbook {
|
|
|
1375
1500
|
tokenEscrowPt: web3.PublicKey
|
|
1376
1501
|
cpiAccountOrderbook: web3.PublicKey
|
|
1377
1502
|
admin: web3.PublicKey
|
|
1503
|
+
/** Raw 256-bit PreciseNumber (12 decimals) for last SY exchange rate */
|
|
1504
|
+
lastSyExchangeRate: bigint
|
|
1378
1505
|
configurationOptions: ConfigurationOptions
|
|
1379
1506
|
financials: OrderbookFinancials
|
|
1380
1507
|
prices: PriceTreeNode[]
|
|
1381
1508
|
offers: OfferNode[]
|
|
1382
1509
|
userEscrows: UserEscrowNode[]
|
|
1510
|
+
/** Next offer index that will be allocated (from NodeAllocator free list) */
|
|
1511
|
+
offersFreeListHead: number
|
|
1512
|
+
/** Bump index boundary for offers allocator */
|
|
1513
|
+
offersBumpIndex: number
|
|
1383
1514
|
}
|
|
1384
1515
|
|
|
1385
1516
|
export interface KaminoSyMeta {
|
|
@@ -1453,7 +1584,7 @@ interface LpPositionCLMMRaw {
|
|
|
1453
1584
|
tickIdx: number
|
|
1454
1585
|
rightTickIdx: number
|
|
1455
1586
|
splitEpoch: BN
|
|
1456
|
-
lpShare:
|
|
1587
|
+
lpShare: AnchorizedPNum
|
|
1457
1588
|
emissions: { trackers: { staged: BN; lastSeenIndex: AnchorizedPNum }[] }
|
|
1458
1589
|
}[]
|
|
1459
1590
|
}
|
|
@@ -1648,7 +1779,7 @@ export interface OfferNodeRaw {
|
|
|
1648
1779
|
|
|
1649
1780
|
export interface UserEscrowNodeRaw {
|
|
1650
1781
|
user: web3.PublicKey
|
|
1651
|
-
yieldIndex:
|
|
1782
|
+
yieldIndex: number
|
|
1652
1783
|
ptAmount: BN
|
|
1653
1784
|
syAmount: BN
|
|
1654
1785
|
ytAmount: BN
|
|
@@ -1659,6 +1790,16 @@ function deserializeAnchorizedPNum(x: AnchorizedPNum): number {
|
|
|
1659
1790
|
return parseFloat(PreciseNumber.fromRaw(x[0]).valueString)
|
|
1660
1791
|
}
|
|
1661
1792
|
|
|
1793
|
+
/** Convert PreciseNumber (Number type in Rust) from Anchor format to raw 256-bit bigint */
|
|
1794
|
+
export function anchorizedPNumToRawBigint(pnum: AnchorizedPNum): bigint {
|
|
1795
|
+
const bnArray = pnum[0]
|
|
1796
|
+
let val = 0n
|
|
1797
|
+
for (let i = 0; i < 4; i++) {
|
|
1798
|
+
val += BigInt(bnArray[i].toString()) << BigInt(i * 64)
|
|
1799
|
+
}
|
|
1800
|
+
return val
|
|
1801
|
+
}
|
|
1802
|
+
|
|
1662
1803
|
/** Fetch the exchange rate of a JitoRestaking vault's VRT to JitoSOL */
|
|
1663
1804
|
async function fetchJitoVaultData({
|
|
1664
1805
|
connection,
|
|
@@ -2073,7 +2214,7 @@ export async function fetchSolsticeRedemptionRate({
|
|
|
2073
2214
|
}
|
|
2074
2215
|
|
|
2075
2216
|
const REFLECT_ORACLE_LEN = 17
|
|
2076
|
-
const REFLECT_MAX_STALENESS_SLOTS =
|
|
2217
|
+
const REFLECT_MAX_STALENESS_SLOTS = 15000000
|
|
2077
2218
|
|
|
2078
2219
|
export async function fetchReflectRedemptionRate({
|
|
2079
2220
|
connection,
|
|
@@ -2143,3 +2284,23 @@ export async function fetchOreExchangeRate({
|
|
|
2143
2284
|
storeMint: storeMintInfo.data,
|
|
2144
2285
|
})
|
|
2145
2286
|
}
|
|
2287
|
+
|
|
2288
|
+
export async function fetchChainlinkRate({
|
|
2289
|
+
connection,
|
|
2290
|
+
priceFeed,
|
|
2291
|
+
}: {
|
|
2292
|
+
connection: web3.Connection
|
|
2293
|
+
priceFeed: web3.PublicKey
|
|
2294
|
+
}): Promise<number> {
|
|
2295
|
+
const accountInfo = await connection.getAccountInfo(priceFeed)
|
|
2296
|
+
|
|
2297
|
+
if (!accountInfo) {
|
|
2298
|
+
throw new Error("Chainlink price feed account not found")
|
|
2299
|
+
}
|
|
2300
|
+
|
|
2301
|
+
const { answer, header } = decodeChainlinkPriceAccount(accountInfo)
|
|
2302
|
+
|
|
2303
|
+
const scale = Math.pow(10, header.decimals)
|
|
2304
|
+
|
|
2305
|
+
return Number(answer) / scale
|
|
2306
|
+
}
|
package/src/index.ts
CHANGED