@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.
- 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 +328 -31
- package/build/exponentFetcher.js +779 -168
- package/build/exponentFetcher.js.map +1 -1
- package/build/index.d.ts +11 -0
- package/build/index.js +11 -0
- package/build/index.js.map +1 -1
- package/build/utils/adrena.d.ts +13 -0
- package/build/utils/adrena.js +29 -0
- package/build/utils/adrena.js.map +1 -0
- package/build/utils/fragmetric.d.ts +17 -0
- package/build/utils/fragmetric.js +23 -0
- package/build/utils/fragmetric.js.map +1 -0
- package/build/utils/jito.d.ts +7 -0
- package/build/utils/jito.js +29 -0
- package/build/utils/jito.js.map +1 -0
- package/build/utils/jupiter.d.ts +30 -0
- package/build/utils/jupiter.js +63 -0
- package/build/utils/jupiter.js.map +1 -0
- package/build/utils/kamino.d.ts +5 -0
- package/build/utils/kamino.js +10 -0
- package/build/utils/kamino.js.map +1 -0
- package/build/utils/meteora.d.ts +19 -0
- package/build/utils/meteora.js +36 -1
- package/build/utils/meteora.js.map +1 -1
- package/build/utils/ore.d.ts +74 -0
- package/build/utils/ore.js +217 -0
- package/build/utils/ore.js.map +1 -0
- package/build/utils/perena.d.ts +12 -0
- package/build/utils/perena.js +27 -0
- package/build/utils/perena.js.map +1 -0
- package/build/utils/sanctum.d.ts +6 -0
- package/build/utils/sanctum.js +26 -0
- package/build/utils/sanctum.js.map +1 -0
- package/build/utils/solstice.d.ts +12 -0
- package/build/utils/solstice.js +45 -0
- package/build/utils/solstice.js.map +1 -0
- package/package.json +21 -18
- package/src/constants.ts +56 -0
- package/src/exponentFetcher.ts +1238 -222
- package/src/index.ts +11 -0
- package/src/utils/adrena.ts +44 -0
- package/src/utils/fragmetric.ts +34 -0
- package/src/utils/jito.ts +30 -0
- package/src/utils/jupiter.ts +98 -0
- package/src/utils/kamino.ts +6 -0
- package/src/utils/meteora.ts +73 -1
- package/src/utils/ore.ts +322 -0
- package/src/utils/perena.ts +28 -0
- package/src/utils/sanctum.ts +24 -0
- package/src/utils/solstice.ts +51 -0
- package/tsconfig.json +4 -1
package/src/exponentFetcher.ts
CHANGED
|
@@ -1,14 +1,32 @@
|
|
|
1
|
-
import { AnchorProvider, BN, Idl, Program, Wallet, web3 } from "@coral-xyz/anchor"
|
|
1
|
+
import { AnchorProvider, BN, DISCRIMINATOR_SIZE, Idl, Program, ProgramAccount, Wallet, web3 } from "@coral-xyz/anchor"
|
|
2
2
|
import { BorshCoder } from "@coral-xyz/anchor"
|
|
3
3
|
import { getStakePoolAccount } from "@solana/spl-stake-pool"
|
|
4
4
|
import { getAccount, getMint } from "@solana/spl-token"
|
|
5
|
-
import
|
|
5
|
+
import bs58 from "bs58"
|
|
6
6
|
import Decimal from "decimal.js"
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
CustodyState,
|
|
10
|
+
FeesStats,
|
|
11
|
+
PoolState,
|
|
12
|
+
calculateTotalFeesFromCustodies,
|
|
13
|
+
decodePoolAccount as decodeAdrenaPoolAccount,
|
|
14
|
+
decodeCustodyAccount,
|
|
15
|
+
decodePoolAndCustodyAccounts,
|
|
16
|
+
} from "@exponent-labs/adrena-idl"
|
|
17
|
+
import { decodeChainlinkPriceAccount } from "@exponent-labs/chainlink-idl"
|
|
18
|
+
import {
|
|
19
|
+
PROGRAM_ID as EXPONENT_CLMM_PROGRAM_ID,
|
|
20
|
+
ExponentClmm,
|
|
21
|
+
IDL as ExponentClmmIdl,
|
|
22
|
+
} from "@exponent-labs/exponent-clmm-idl"
|
|
7
23
|
import { ExponentCore, IDL, PROGRAM_ID } from "@exponent-labs/exponent-idl"
|
|
24
|
+
import { IDL as EXPONENT_ORDERBOOK_IDL, ExponentOrderbook } from "@exponent-labs/exponent-orderbook-idl"
|
|
8
25
|
import {
|
|
9
26
|
AnchorizedPNum,
|
|
10
27
|
AnchorizedPNumJson,
|
|
11
28
|
CpiAccountIndexes,
|
|
29
|
+
ExponentCoreCpiIndexes,
|
|
12
30
|
GenericSyMetaAccount,
|
|
13
31
|
GenericSyMetaAccountRaw,
|
|
14
32
|
InterfaceType,
|
|
@@ -16,6 +34,7 @@ import {
|
|
|
16
34
|
JitoRestakingSyMetaAccountRaw,
|
|
17
35
|
MarginfiSyMeta,
|
|
18
36
|
MarginfiSyMetaRaw,
|
|
37
|
+
MarketCpiCoreIndexes,
|
|
19
38
|
PerenaSyMetaAccount,
|
|
20
39
|
PerenaSyMetaAccountRaw,
|
|
21
40
|
SyEmissionRaw,
|
|
@@ -25,44 +44,48 @@ import {
|
|
|
25
44
|
deserializeJitoRestakingSyMetaAccountRaw,
|
|
26
45
|
deserializePerenaSyMetaAccountRaw,
|
|
27
46
|
} from "@exponent-labs/exponent-types"
|
|
28
|
-
import {
|
|
29
|
-
|
|
30
|
-
|
|
47
|
+
import {
|
|
48
|
+
MAX_OFFERS,
|
|
49
|
+
MAX_PRICE_NODES,
|
|
50
|
+
MAX_USER_ESCROWS,
|
|
51
|
+
OfferNode,
|
|
52
|
+
PriceTreeNode,
|
|
53
|
+
UserEscrowNode,
|
|
54
|
+
} from "@exponent-labs/exponent-types"
|
|
31
55
|
import { GenericStandard } from "@exponent-labs/generic-sy-idl"
|
|
32
56
|
import { PROGRAM_ID as GENERIC_STANDARD_PROGRAM_ID, IDL as GenericStandardIdl } from "@exponent-labs/generic-sy-idl"
|
|
33
57
|
import { PROGRAM_ID as JITO_RESTAKING_SY_PROGRAM_ID } from "@exponent-labs/jito-restaking-sy-idl"
|
|
34
58
|
import { JitoRestakingStandard, IDL as JitoRestakingSyIdl } from "@exponent-labs/jito-restaking-sy-idl"
|
|
59
|
+
// Decode Jupiter Perps pool accounts via helper using Anchor 0.29.0
|
|
60
|
+
import { decodePoolAccount as decodeJupiterPerpsPoolAccount } from "@exponent-labs/jupiter-perps-idl"
|
|
35
61
|
import { Obligation, Reserve } from "@exponent-labs/kamino-reserve-deserializer"
|
|
36
62
|
import { PROGRAM_ID as KAMINO_LEND_PROGRAM_ID } from "@exponent-labs/kamino-reserve-deserializer"
|
|
37
63
|
import { PROGRAM_ID as KAMINO_STANDARD_PROGRAM_ID } from "@exponent-labs/kamino-sy-idl"
|
|
38
64
|
import { KaminoLendStandard } from "@exponent-labs/kamino-sy-idl"
|
|
39
65
|
import { IDL as KaminoSyIdl } from "@exponent-labs/kamino-sy-idl"
|
|
66
|
+
import { IDL as KaminoVaultIdl } from "@exponent-labs/kamino-vault-idl"
|
|
40
67
|
import {
|
|
41
68
|
PROGRAM_ID as MARGINFI_SY_PROGRAM_ID,
|
|
42
69
|
MarginfiStandard,
|
|
43
70
|
IDL as MarginfiSyIdl,
|
|
44
71
|
} from "@exponent-labs/marginfi-sy-idl"
|
|
45
|
-
import {
|
|
72
|
+
import { fetchPoolAccount, fetchVaultAccount } from "@exponent-labs/meteora-idl"
|
|
46
73
|
import { PROGRAM_ID as PERENA_STANDARD_PROGRAM_ID } from "@exponent-labs/perena-sy-idl"
|
|
47
74
|
import { IDL as PerenaSyIdl } from "@exponent-labs/perena-sy-idl"
|
|
48
75
|
import { PerenaStandard } from "@exponent-labs/perena-sy-idl"
|
|
49
76
|
import { PreciseNumber } from "@exponent-labs/precise-number"
|
|
50
|
-
import { decodePoolStateAccount } from "@exponent-labs/sanctum-idl"
|
|
51
|
-
import {
|
|
52
|
-
decodePoolAccount as decodeAdrenaPoolAccount,
|
|
53
|
-
decodeCustodyAccount,
|
|
54
|
-
CustodyState,
|
|
55
|
-
FeesStats,
|
|
56
|
-
PoolState,
|
|
57
|
-
decodePoolAndCustodyAccounts,
|
|
58
|
-
calculateTotalFeesFromCustodies,
|
|
59
|
-
} from "@exponent-labs/adrena-idl"
|
|
60
77
|
import { decodeYieldPoolAndVestingScheduleAccounts } from "@exponent-labs/solstice-idl"
|
|
61
78
|
|
|
79
|
+
import { IgnoredClmmEntityError, isMarketIgnored, isTicksIgnored } from "./constants"
|
|
80
|
+
import { calculateAdrenaIndex } from "./utils/adrena"
|
|
81
|
+
import { calculateFragmetricIndex, calculateFragmetricSupportedTokenIndex } from "./utils/fragmetric"
|
|
82
|
+
import { decodeJitoVaultData } from "./utils/jito"
|
|
83
|
+
import { calculateJupiterLendIndex, calculateJupiterPerpsIndex } from "./utils/jupiter"
|
|
62
84
|
import { computeD, getAmountByShare } from "./utils/meteora"
|
|
63
|
-
import {
|
|
64
|
-
|
|
65
|
-
import {
|
|
85
|
+
import { calculateOreExchangeRate } from "./utils/ore"
|
|
86
|
+
import { getPerenaLpMint, getPerenaStablePoolData } from "./utils/perena"
|
|
87
|
+
import { calculateSanctumIndex } from "./utils/sanctum"
|
|
88
|
+
import { calculateSolsticeRedemptionRate } from "./utils/solstice"
|
|
66
89
|
|
|
67
90
|
export function serializeAnchorizedPNumFromJson(pnum: AnchorizedPNum): AnchorizedPNumJson {
|
|
68
91
|
const serializedArray = pnum[0].map((bn) => bn.toString())
|
|
@@ -111,6 +134,8 @@ export class ExponentFetcher {
|
|
|
111
134
|
public jitoRestakingSyProgram: Program<JitoRestakingStandard>
|
|
112
135
|
public perenaSyProgram: Program<PerenaStandard>
|
|
113
136
|
public genericStandardProgram: Program<GenericStandard>
|
|
137
|
+
public exponentClmmProgram: Program<ExponentClmm>
|
|
138
|
+
public orderbookProgram: Program<ExponentOrderbook>
|
|
114
139
|
public connection: web3.Connection
|
|
115
140
|
public coreProgramId: web3.PublicKey
|
|
116
141
|
public marginfiSyProgramId: web3.PublicKey
|
|
@@ -156,6 +181,8 @@ export class ExponentFetcher {
|
|
|
156
181
|
)
|
|
157
182
|
this.perenaSyProgram = new Program<PerenaStandard>(PerenaSyIdl as PerenaStandard, provider)
|
|
158
183
|
this.genericStandardProgram = new Program<GenericStandard>(GenericStandardIdl, provider)
|
|
184
|
+
this.exponentClmmProgram = new Program<ExponentClmm>(ExponentClmmIdl as ExponentClmm, provider)
|
|
185
|
+
this.orderbookProgram = new Program<ExponentOrderbook>(EXPONENT_ORDERBOOK_IDL as ExponentOrderbook, provider)
|
|
159
186
|
}
|
|
160
187
|
|
|
161
188
|
async fetchVault(address: web3.PublicKey) {
|
|
@@ -180,6 +207,30 @@ export class ExponentFetcher {
|
|
|
180
207
|
}
|
|
181
208
|
}
|
|
182
209
|
|
|
210
|
+
async fetchOrderbook(address: web3.PublicKey): Promise<Orderbook> {
|
|
211
|
+
try {
|
|
212
|
+
const o = (await this.connection.getAccountInfo(address)).data
|
|
213
|
+
|
|
214
|
+
return deserializeOrderbook(o)
|
|
215
|
+
} catch (e) {
|
|
216
|
+
console.error(`Error fetching orderbook ${address.toBase58()}`)
|
|
217
|
+
console.error(e)
|
|
218
|
+
throw e
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
async fetchOrderbookCpiAccounts(address: web3.PublicKey): Promise<OrderbookCpiAccountsRaw> {
|
|
223
|
+
try {
|
|
224
|
+
const orderbookCpiAccounts: OrderbookCpiAccountsRaw =
|
|
225
|
+
await this.orderbookProgram.account.cpiAccountsOrderbook.fetch(address)
|
|
226
|
+
return orderbookCpiAccounts
|
|
227
|
+
} catch (e) {
|
|
228
|
+
console.error(`Error fetching orderbook ${address.toBase58()}`)
|
|
229
|
+
console.error(e)
|
|
230
|
+
throw e
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
183
234
|
async fetchMarginfiSyMeta(address: web3.PublicKey): Promise<MarginfiSyMeta> {
|
|
184
235
|
const x: MarginfiSyMetaRaw = await this.marginfiSyProgram.account.syMeta.fetch(address)
|
|
185
236
|
return deserializeMarginfiSyMeta(x)
|
|
@@ -263,11 +314,62 @@ export class ExponentFetcher {
|
|
|
263
314
|
return deserializeLpPosition(x)
|
|
264
315
|
}
|
|
265
316
|
|
|
317
|
+
async fetchLpPositionCLMM(address: web3.PublicKey) {
|
|
318
|
+
try {
|
|
319
|
+
const raw = await this.exponentClmmProgram.account.lpPosition.fetch(address)
|
|
320
|
+
|
|
321
|
+
const v: LpPositionCLMMRaw = {
|
|
322
|
+
owner: raw.owner,
|
|
323
|
+
market: raw.market,
|
|
324
|
+
feeInsideLastPt: raw.feeInsideLastPt,
|
|
325
|
+
feeInsideLastSy: raw.feeInsideLastSy,
|
|
326
|
+
lpBalance: raw.lpBalance,
|
|
327
|
+
tokensOwedSy: raw.tokensOwedSy,
|
|
328
|
+
tokensOwedPt: raw.tokensOwedPt,
|
|
329
|
+
lowerTickIdx: raw.lowerTickIdx,
|
|
330
|
+
upperTickIdx: raw.upperTickIdx,
|
|
331
|
+
farms: {
|
|
332
|
+
trackers: raw.farms.trackers.map((t: any) => ({
|
|
333
|
+
staged: t.staged,
|
|
334
|
+
lastSeenIndex: t.lastSeenIndex,
|
|
335
|
+
})),
|
|
336
|
+
},
|
|
337
|
+
shareTrackers: {
|
|
338
|
+
trackers: raw.shareTrackers.trackers.map((tracker: any) => ({
|
|
339
|
+
tickIdx: tracker.tickIdx,
|
|
340
|
+
rightTickIdx: tracker.rightTickIdx,
|
|
341
|
+
splitEpoch: tracker.splitEpoch,
|
|
342
|
+
lpShare: tracker.lpShare,
|
|
343
|
+
emissions: {
|
|
344
|
+
trackers: tracker.emissions.trackers.map((e: any) => ({
|
|
345
|
+
staged: e.staged,
|
|
346
|
+
lastSeenIndex: e.lastSeenIndex,
|
|
347
|
+
})),
|
|
348
|
+
},
|
|
349
|
+
})),
|
|
350
|
+
},
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
return deserializeLpPositionCLMM(v)
|
|
354
|
+
} catch (e) {
|
|
355
|
+
console.error(`Error fetching clmm lp position ${address.toBase58()}`)
|
|
356
|
+
console.error(e)
|
|
357
|
+
throw e
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
266
361
|
async fetchYtPosition(address: web3.PublicKey): Promise<YtPosition> {
|
|
267
362
|
const x: YtPositionRaw = await this.program.account.yieldTokenPosition.fetch(address)
|
|
268
363
|
return deserializeYtPosition(x)
|
|
269
364
|
}
|
|
270
365
|
|
|
366
|
+
/** Batch fetch multiple YT positions in a single RPC call */
|
|
367
|
+
async fetchYtPositions(addresses: web3.PublicKey[]): Promise<(YtPosition | null)[]> {
|
|
368
|
+
if (addresses.length === 0) return []
|
|
369
|
+
const results = await this.program.account.yieldTokenPosition.fetchMultiple(addresses)
|
|
370
|
+
return results.map((x) => (x ? deserializeYtPosition(x as YtPositionRaw) : null))
|
|
371
|
+
}
|
|
372
|
+
|
|
271
373
|
async fetchJitoRestakingSyMeta(address: web3.PublicKey): Promise<JitoRestakingSyMetaAccount> {
|
|
272
374
|
const x: JitoRestakingSyMetaAccountRaw = await this.jitoRestakingSyProgram.account.syMeta.fetch(address)
|
|
273
375
|
|
|
@@ -284,6 +386,365 @@ export class ExponentFetcher {
|
|
|
284
386
|
|
|
285
387
|
return deserializeGenericSyMetaAccountRaw(x)
|
|
286
388
|
}
|
|
389
|
+
|
|
390
|
+
async fetchAllMarketThree(): Promise<MarketThree[]> {
|
|
391
|
+
//TODO Replace with the following code when all damaged markets are removed onchain
|
|
392
|
+
// const marketsProgramAccounts: ProgramAccount<MarketThreeRaw>[] =
|
|
393
|
+
// await this.exponentClmmProgram.account.marketThree.all()
|
|
394
|
+
// return marketsProgramAccounts.map(({ account }) => deserializeMarketThree(account))
|
|
395
|
+
|
|
396
|
+
const MARKET_THREE_DISCRIMINATOR = Buffer.from([242, 240, 26, 15, 148, 186, 185, 205])
|
|
397
|
+
const marketsProgramAccounts = await this.connection.getProgramAccounts(this.exponentClmmProgram.programId, {
|
|
398
|
+
filters: [
|
|
399
|
+
{
|
|
400
|
+
memcmp: {
|
|
401
|
+
offset: 0,
|
|
402
|
+
bytes: bs58.encode(MARKET_THREE_DISCRIMINATOR),
|
|
403
|
+
},
|
|
404
|
+
},
|
|
405
|
+
],
|
|
406
|
+
})
|
|
407
|
+
|
|
408
|
+
return marketsProgramAccounts
|
|
409
|
+
.filter(({ pubkey }) => !isMarketIgnored(pubkey.toBase58()))
|
|
410
|
+
.map(({ account, pubkey }) => {
|
|
411
|
+
try {
|
|
412
|
+
return this.exponentClmmProgram.coder.accounts.decode("marketThree", account.data)
|
|
413
|
+
} catch (error) {
|
|
414
|
+
return null
|
|
415
|
+
}
|
|
416
|
+
})
|
|
417
|
+
.filter((m) => !!m)
|
|
418
|
+
}
|
|
419
|
+
|
|
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
|
+
|
|
426
|
+
try {
|
|
427
|
+
const m: MarketThreeRaw = await this.exponentClmmProgram.account.marketThree.fetch(address)
|
|
428
|
+
return deserializeMarketThree(m)
|
|
429
|
+
} catch (e) {
|
|
430
|
+
console.error(`Error fetching market ${addressStr}`)
|
|
431
|
+
console.error(e)
|
|
432
|
+
throw e
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
async fetchAllMarketThreeTicks(): Promise<Ticks[]> {
|
|
437
|
+
const TICKS_DISCRIMINATOR = Buffer.from([122, 104, 41, 141, 214, 36, 222, 37])
|
|
438
|
+
|
|
439
|
+
const ticksAccounts = await this.connection.getProgramAccounts(this.exponentClmmProgram.programId, {
|
|
440
|
+
filters: [
|
|
441
|
+
{
|
|
442
|
+
memcmp: {
|
|
443
|
+
offset: 0,
|
|
444
|
+
bytes: bs58.encode(TICKS_DISCRIMINATOR),
|
|
445
|
+
},
|
|
446
|
+
},
|
|
447
|
+
],
|
|
448
|
+
})
|
|
449
|
+
|
|
450
|
+
return ticksAccounts
|
|
451
|
+
.filter(({ pubkey }) => !isTicksIgnored(pubkey.toBase58()))
|
|
452
|
+
.map(({ account }) => deserializeMarketThreeTicks(account.data))
|
|
453
|
+
}
|
|
454
|
+
|
|
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
|
+
|
|
461
|
+
try {
|
|
462
|
+
const m = (await this.connection.getAccountInfo(address)).data
|
|
463
|
+
return deserializeMarketThreeTicks(m)
|
|
464
|
+
} catch (e) {
|
|
465
|
+
console.error(`Error fetching ticks ${addressStr}`)
|
|
466
|
+
console.error(e)
|
|
467
|
+
throw e
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
}
|
|
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
|
+
*/
|
|
481
|
+
export function deserializeMarketThreeTicks(data: Buffer): Ticks {
|
|
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
|
+
}
|
|
518
|
+
|
|
519
|
+
const readPubkey = (): web3.PublicKey => {
|
|
520
|
+
const pk = new web3.PublicKey(data.slice(offset, offset + 32))
|
|
521
|
+
offset += 32
|
|
522
|
+
return pk
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
/** Reads a PreciseNumber (Number type in Rust) as float - 32 bytes (4 x u64) */
|
|
526
|
+
const readPreciseNumberAsFloat = (): number => {
|
|
527
|
+
const nums: BN[] = []
|
|
528
|
+
for (let i = 0; i < 4; i++) {
|
|
529
|
+
nums.push(new BN(data.slice(offset + i * 8, offset + (i + 1) * 8), undefined, "le"))
|
|
530
|
+
}
|
|
531
|
+
offset += 32
|
|
532
|
+
return parseFloat(PreciseNumber.fromRaw(nums).valueString)
|
|
533
|
+
}
|
|
534
|
+
|
|
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
|
+
}
|
|
545
|
+
|
|
546
|
+
const skip = (bytes: number): void => {
|
|
547
|
+
offset += bytes
|
|
548
|
+
}
|
|
549
|
+
|
|
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
|
|
556
|
+
|
|
557
|
+
// ─── Parse tick nodes ─────────────────────────────────────────────────────
|
|
558
|
+
const ticks: Tick[] = []
|
|
559
|
+
|
|
560
|
+
for (let i = 0; i < MAX_TICK_NODES; i++) {
|
|
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 }[] = []
|
|
580
|
+
for (let j = 0; j < PERSONAL_TICK_YIELD_TRACKER_SIZE; j++) {
|
|
581
|
+
farms.push({ lastSeenIndex: readPreciseNumberAsFloat() })
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
// EmissionYieldTrackers: 2 x EmissionYieldTracker(64 bytes) = 128 bytes
|
|
585
|
+
const emissions: { lastSeenIndex: number; lastPositionIndex: number }[] = []
|
|
586
|
+
for (let j = 0; j < PERSONAL_TICK_YIELD_TRACKER_SIZE; j++) {
|
|
587
|
+
emissions.push({
|
|
588
|
+
lastSeenIndex: readPreciseNumberAsFloat(),
|
|
589
|
+
lastPositionIndex: readPreciseNumberAsFloat(),
|
|
590
|
+
})
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
const lastSplitEpoch = readU64() // 8 bytes
|
|
594
|
+
const frozenLiquidity = readU64() // 8 bytes
|
|
595
|
+
|
|
596
|
+
ticks.push({
|
|
597
|
+
apyBasePoints,
|
|
598
|
+
liquidityNet,
|
|
599
|
+
feeGrowthOutsidePt,
|
|
600
|
+
feeGrowthOutsideSy,
|
|
601
|
+
liquidityGross,
|
|
602
|
+
impliedRate: spotPrice, // Legacy field name kept for compatibility
|
|
603
|
+
principalPt,
|
|
604
|
+
principalSy,
|
|
605
|
+
principalShareSupply,
|
|
606
|
+
farms,
|
|
607
|
+
emissions,
|
|
608
|
+
lastSplitEpoch,
|
|
609
|
+
frozenLiquidity,
|
|
610
|
+
})
|
|
611
|
+
}
|
|
612
|
+
|
|
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
|
|
621
|
+
|
|
622
|
+
return {
|
|
623
|
+
ticksTree: ticks,
|
|
624
|
+
market,
|
|
625
|
+
feeGrowthIndexGlobalPt,
|
|
626
|
+
feeGrowthIndexGlobalSy,
|
|
627
|
+
currentPrefixSum,
|
|
628
|
+
currentSpotPrice,
|
|
629
|
+
currentTick,
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
|
|
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
|
+
|
|
703
|
+
return {
|
|
704
|
+
addressLookupTable: m.addressLookupTable,
|
|
705
|
+
mintSy: m.mintSy,
|
|
706
|
+
mintPt: m.mintPt,
|
|
707
|
+
vault: m.vault,
|
|
708
|
+
tokenSyEscrow: m.tokenSyEscrow,
|
|
709
|
+
tokenPtEscrow: m.tokenPtEscrow,
|
|
710
|
+
tokenFeeTreasurySy: m.tokenFeeTreasurySy,
|
|
711
|
+
tokenFeeTreasuryPt: m.tokenFeeTreasuryPt,
|
|
712
|
+
selfAddress: m.selfAddress,
|
|
713
|
+
syProgram: m.syProgram,
|
|
714
|
+
statusFlags: m.statusFlags,
|
|
715
|
+
cpiSyAccounts,
|
|
716
|
+
isCurrentFlashSwap: m.isCurrentFlashSwap,
|
|
717
|
+
lpFarm: m.lpFarm,
|
|
718
|
+
mintYt: m.mintYt,
|
|
719
|
+
tokenYtEscrow: m.tokenYtEscrow,
|
|
720
|
+
emissions: {
|
|
721
|
+
trackers: m.emissions.trackers.map((t) => ({
|
|
722
|
+
tokenEscrow: t.tokenEscrow,
|
|
723
|
+
lpShareIndex: deserializeAnchorizedPNum(t.lpShareIndex),
|
|
724
|
+
lastSeenStaged: Number(t.lastSeenStaged),
|
|
725
|
+
})),
|
|
726
|
+
},
|
|
727
|
+
liquidityNetBalanceLimits: m.liquidityNetBalanceLimits,
|
|
728
|
+
admin: m.admin,
|
|
729
|
+
ticks: m.ticks,
|
|
730
|
+
configurationOptions: {
|
|
731
|
+
lnFeeRateRoot: m.configurationOptions.lnFeeRateRoot,
|
|
732
|
+
treasuryFeeBps: m.configurationOptions.treasuryFeeBps,
|
|
733
|
+
minLpTickAmount: BigInt(m.configurationOptions.minLpTickAmount.toString()),
|
|
734
|
+
epsilonClamp: m.configurationOptions.epsilonClamp,
|
|
735
|
+
maxLpSupply: BigInt(m.configurationOptions.maxLpSupply.toString()),
|
|
736
|
+
tickSpace: m.configurationOptions.tickSpace,
|
|
737
|
+
},
|
|
738
|
+
financials: {
|
|
739
|
+
expirationTs: BigInt(m.financials.expirationTs),
|
|
740
|
+
ptBalance: BigInt(m.financials.ptBalance.toString()),
|
|
741
|
+
syBalance: BigInt(m.financials.syBalance.toString()),
|
|
742
|
+
liquidityBalance: BigInt(m.financials.liquidityBalance.toString()),
|
|
743
|
+
},
|
|
744
|
+
cpiCoreAccounts,
|
|
745
|
+
exponentCoreProgram: m.exponentCoreProgram,
|
|
746
|
+
seedId: m.seedId,
|
|
747
|
+
}
|
|
287
748
|
}
|
|
288
749
|
|
|
289
750
|
function deserializeMarketTwo(m: MarketTwoRaw): MarketTwo {
|
|
@@ -324,6 +785,34 @@ function deserializeMarketTwo(m: MarketTwoRaw): MarketTwo {
|
|
|
324
785
|
}
|
|
325
786
|
}
|
|
326
787
|
|
|
788
|
+
function deserializeLpPositionCLMM(x: LpPositionCLMMRaw): LpPositionCLMM {
|
|
789
|
+
return {
|
|
790
|
+
owner: x.owner,
|
|
791
|
+
market: x.market,
|
|
792
|
+
feeInsideLastPt: BigInt(x.feeInsideLastPt.toString()),
|
|
793
|
+
feeInsideLastSy: BigInt(x.feeInsideLastSy.toString()),
|
|
794
|
+
lpBalance: BigInt(x.lpBalance.toString()),
|
|
795
|
+
tokensOwedSy: BigInt(x.tokensOwedSy.toString()),
|
|
796
|
+
tokensOwedPt: BigInt(x.tokensOwedPt.toString()),
|
|
797
|
+
lowerTickIdx: x.lowerTickIdx,
|
|
798
|
+
upperTickIdx: x.upperTickIdx,
|
|
799
|
+
farms: x.farms.trackers.map((t) => ({
|
|
800
|
+
staged: BigInt(t.staged.toString()),
|
|
801
|
+
lastSeenIndex: parseFloat(PreciseNumber.fromRaw(t.lastSeenIndex[0]).valueString),
|
|
802
|
+
})),
|
|
803
|
+
shareTrackers: x.shareTrackers.trackers.map((tracker) => ({
|
|
804
|
+
tickIdx: tracker.tickIdx,
|
|
805
|
+
rightTickIdx: tracker.rightTickIdx,
|
|
806
|
+
splitEpoch: BigInt(tracker.splitEpoch.toString()),
|
|
807
|
+
lpShare: anchorizedPNumToRawBigint(tracker.lpShare),
|
|
808
|
+
emissions: tracker.emissions.trackers.map((e) => ({
|
|
809
|
+
staged: BigInt(e.staged.toString()),
|
|
810
|
+
lastSeenIndex: parseFloat(PreciseNumber.fromRaw(e.lastSeenIndex[0]).valueString),
|
|
811
|
+
})),
|
|
812
|
+
})),
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
|
|
327
816
|
function deserializeVault(x: VaultRaw): Vault {
|
|
328
817
|
return {
|
|
329
818
|
syProgram: x.syProgram,
|
|
@@ -355,6 +844,263 @@ function deserializeVault(x: VaultRaw): Vault {
|
|
|
355
844
|
}
|
|
356
845
|
}
|
|
357
846
|
|
|
847
|
+
function deserializeOrderbook(data: Buffer): Orderbook {
|
|
848
|
+
let offset = 0
|
|
849
|
+
|
|
850
|
+
// 1) Skip Anchor discriminator
|
|
851
|
+
offset += DISCRIMINATOR_SIZE
|
|
852
|
+
|
|
853
|
+
const readPubkey = (): web3.PublicKey => {
|
|
854
|
+
const pk = new web3.PublicKey(data.slice(offset, offset + 32))
|
|
855
|
+
offset += 32
|
|
856
|
+
return pk
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
// ConfigurationOptions struct
|
|
860
|
+
const thresholdAmount = data.readBigUInt64LE(offset)
|
|
861
|
+
offset += 8
|
|
862
|
+
const lnMakerFeeRate = data.readDoubleLE(offset)
|
|
863
|
+
offset += 8
|
|
864
|
+
const lnTakerFeeRate = data.readDoubleLE(offset)
|
|
865
|
+
offset += 8
|
|
866
|
+
const priceDecimals = data.readUint8(offset)
|
|
867
|
+
offset += 1
|
|
868
|
+
// Skip ConfigurationOptions padding: _placeholder_one[15] + _placeholder_two[32] + _placeholder_three[32] + _placeholder_four[32] + _reserved[1024] = 1135 bytes
|
|
869
|
+
offset += 1135
|
|
870
|
+
|
|
871
|
+
// Pubkeys
|
|
872
|
+
const vault = readPubkey()
|
|
873
|
+
const yieldPosition = readPubkey()
|
|
874
|
+
const addressLookupTable = readPubkey()
|
|
875
|
+
const exponentCoreProgram = readPubkey()
|
|
876
|
+
const syProgram = readPubkey()
|
|
877
|
+
const tokenEscrowSy = readPubkey()
|
|
878
|
+
const tokenEscrowYt = readPubkey()
|
|
879
|
+
const tokenEscrowPt = readPubkey()
|
|
880
|
+
const cpiAccountOrderbook = readPubkey()
|
|
881
|
+
const admin = readPubkey()
|
|
882
|
+
|
|
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
|
+
})()
|
|
891
|
+
offset += 32
|
|
892
|
+
|
|
893
|
+
// OrderbookFinancials struct
|
|
894
|
+
// Skip last_seen_sy_index (Number type = 32 bytes)
|
|
895
|
+
offset += 32
|
|
896
|
+
const ytBalance = data.readBigUInt64LE(offset)
|
|
897
|
+
offset += 8
|
|
898
|
+
const syBalance = data.readBigUInt64LE(offset)
|
|
899
|
+
offset += 8
|
|
900
|
+
const ptBalance = data.readBigUInt64LE(offset)
|
|
901
|
+
offset += 8
|
|
902
|
+
const ytFeeBalance = data.readBigUInt64LE(offset)
|
|
903
|
+
offset += 8
|
|
904
|
+
const syFeeBalance = data.readBigUInt64LE(offset)
|
|
905
|
+
offset += 8
|
|
906
|
+
const ptFeeBalance = data.readBigUInt64LE(offset)
|
|
907
|
+
offset += 8
|
|
908
|
+
const stagedSyBalance = data.readBigUInt64LE(offset)
|
|
909
|
+
offset += 8
|
|
910
|
+
const expirationTs = data.readUInt32LE(offset)
|
|
911
|
+
offset += 4
|
|
912
|
+
// Skip financials _padding: [u8; 4]
|
|
913
|
+
offset += 4
|
|
914
|
+
|
|
915
|
+
const configurationOptions: ConfigurationOptions = {
|
|
916
|
+
priceDecimals,
|
|
917
|
+
thresholdAmount,
|
|
918
|
+
lnMakerFeeRate,
|
|
919
|
+
lnTakerFeeRate,
|
|
920
|
+
}
|
|
921
|
+
const financials: OrderbookFinancials = {
|
|
922
|
+
expirationTs,
|
|
923
|
+
syBalance: syBalance,
|
|
924
|
+
ytBalance: ytBalance,
|
|
925
|
+
ptBalance: ptBalance,
|
|
926
|
+
ytFeeBalance,
|
|
927
|
+
syFeeBalance,
|
|
928
|
+
ptFeeBalance,
|
|
929
|
+
stagedSy: stagedSyBalance,
|
|
930
|
+
}
|
|
931
|
+
// console.log("financials", financials)
|
|
932
|
+
// ─── Parse RedBlackTree slab ───────────────────────────────────────────────
|
|
933
|
+
// RedBlackTree struct: root: u32, _padding: [u32; 3], allocator: NodeAllocator<...>
|
|
934
|
+
// Total before allocator = 4 + 12 = 16 bytes
|
|
935
|
+
|
|
936
|
+
const root = data.readUInt32LE(offset)
|
|
937
|
+
offset += 4
|
|
938
|
+
const padding = 12 // _padding: [u32; 3] in RedBlackTree struct
|
|
939
|
+
offset += padding
|
|
940
|
+
|
|
941
|
+
// NodeAllocator<T=RBNode<u32,PriceNode>, N=MAX_PRICE_NODES, R=3>
|
|
942
|
+
// header: size:u64, bump_index:u32, free_list_head:u32
|
|
943
|
+
|
|
944
|
+
const priceTreeSize = Number(data.readBigUInt64LE(offset))
|
|
945
|
+
offset += 8
|
|
946
|
+
|
|
947
|
+
const _priceTreeBump = data.readUInt32LE(offset)
|
|
948
|
+
offset += 4
|
|
949
|
+
|
|
950
|
+
const _priceTreeFreeIdx = data.readUInt32LE(offset)
|
|
951
|
+
offset += 4
|
|
952
|
+
|
|
953
|
+
// each RBNode entry = registers[3] + key:u32 + first_offer:u32
|
|
954
|
+
const prices: PriceTreeNode[] = []
|
|
955
|
+
for (let i = 0; i < MAX_PRICE_NODES; i++) {
|
|
956
|
+
const left = data.readUInt32LE(offset)
|
|
957
|
+
offset += 4
|
|
958
|
+
const right = data.readUInt32LE(offset)
|
|
959
|
+
offset += 4
|
|
960
|
+
const parent = data.readUInt32LE(offset)
|
|
961
|
+
offset += 4
|
|
962
|
+
offset += 4 // skip color
|
|
963
|
+
const key = data.readUInt32LE(offset)
|
|
964
|
+
offset += 4
|
|
965
|
+
|
|
966
|
+
const firstOfferSellYt = data.readUInt32LE(offset)
|
|
967
|
+
offset += 4
|
|
968
|
+
const firstOfferBuyYt = data.readUInt32LE(offset)
|
|
969
|
+
offset += 4
|
|
970
|
+
const lastOfferSellYt = data.readUInt32LE(offset)
|
|
971
|
+
offset += 4
|
|
972
|
+
const lastOfferBuyYt = data.readUInt32LE(offset)
|
|
973
|
+
offset += 4
|
|
974
|
+
if (key === 0) continue
|
|
975
|
+
prices.push({ key, firstOfferSellYt, firstOfferBuyYt, lastOfferSellYt, lastOfferBuyYt, parent, left, right })
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
// ─── Parse Offers slab ────────────────────────────────────────────────────
|
|
979
|
+
// NodeAllocator<Offer, MAX_OFFERS, 4>
|
|
980
|
+
// header: size:u64, bump_index:u32, free_list_head:u32
|
|
981
|
+
|
|
982
|
+
const offersSize = Number(data.readBigUInt64LE(offset))
|
|
983
|
+
offset += 8
|
|
984
|
+
const offersBumpIndex = data.readUInt32LE(offset)
|
|
985
|
+
offset += 4
|
|
986
|
+
const offersFreeListHead = data.readUInt32LE(offset)
|
|
987
|
+
offset += 4
|
|
988
|
+
|
|
989
|
+
const offers: OfferNode[] = []
|
|
990
|
+
for (let i = 0; i < MAX_OFFERS; i++) {
|
|
991
|
+
const register = data.readUInt32LE(offset)
|
|
992
|
+
offset += 4
|
|
993
|
+
const nextOfferPointer = data.readUInt32LE(offset)
|
|
994
|
+
offset += 4
|
|
995
|
+
const userVaultPointer = data.readUInt32LE(offset)
|
|
996
|
+
offset += 4
|
|
997
|
+
const pricePointer = data.readUInt32LE(offset)
|
|
998
|
+
offset += 4
|
|
999
|
+
const amount = data.readBigUInt64LE(offset)
|
|
1000
|
+
offset += 8
|
|
1001
|
+
const expiryAt = data.readUInt32LE(offset)
|
|
1002
|
+
offset += 4
|
|
1003
|
+
const createdAt = data.readUInt32LE(offset)
|
|
1004
|
+
offset += 4
|
|
1005
|
+
const virtualOffer = data.readUInt8(offset) !== 0
|
|
1006
|
+
offset += 1
|
|
1007
|
+
const orderTypeFlag = data.readUInt8(offset)
|
|
1008
|
+
offset += 1
|
|
1009
|
+
const fillOrKill = data.readUInt8(offset) !== 0
|
|
1010
|
+
offset += 1
|
|
1011
|
+
offset += 5 // reserved padding
|
|
1012
|
+
if (userVaultPointer === 0) continue
|
|
1013
|
+
offers.push({
|
|
1014
|
+
offerIndex: i + 1,
|
|
1015
|
+
nextOfferPointer,
|
|
1016
|
+
amount,
|
|
1017
|
+
userVaultPointer,
|
|
1018
|
+
expiryAt,
|
|
1019
|
+
createdAt,
|
|
1020
|
+
virtualOffer,
|
|
1021
|
+
orderTypeFlag,
|
|
1022
|
+
fillOrKill,
|
|
1023
|
+
pricePointer,
|
|
1024
|
+
})
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
// ─── Parse UserEscrow slab ────────────────────────────────────────────────
|
|
1028
|
+
// NodeAllocator<UserEscrow, MAX_USER_ESCROWS, 2>
|
|
1029
|
+
// header: size:u64, bump_index:u32, free_list_head:u32
|
|
1030
|
+
const escSize = Number(data.readBigUInt64LE(offset))
|
|
1031
|
+
offset += 8
|
|
1032
|
+
const _escBump = data.readUInt32LE(offset)
|
|
1033
|
+
offset += 4
|
|
1034
|
+
const _escFreeIdx = data.readUInt32LE(offset)
|
|
1035
|
+
offset += 4
|
|
1036
|
+
|
|
1037
|
+
// each Node = [ no registers ] + UserEscrow.value
|
|
1038
|
+
const userEscrows: UserEscrowNode[] = []
|
|
1039
|
+
for (let i = 0; i < MAX_USER_ESCROWS; i++) {
|
|
1040
|
+
const register = data.readUInt32LE(offset)
|
|
1041
|
+
offset += 4
|
|
1042
|
+
const register2 = data.readUInt32LE(offset)
|
|
1043
|
+
offset += 4
|
|
1044
|
+
const user = new web3.PublicKey(data.slice(offset, offset + 32))
|
|
1045
|
+
offset += 32
|
|
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
|
|
1052
|
+
const ptAmount = data.readBigUInt64LE(offset)
|
|
1053
|
+
offset += 8
|
|
1054
|
+
const syAmount = data.readBigUInt64LE(offset)
|
|
1055
|
+
offset += 8
|
|
1056
|
+
const ytAmount = data.readBigUInt64LE(offset)
|
|
1057
|
+
offset += 8
|
|
1058
|
+
const stakedYtAmount = data.readBigInt64LE(offset)
|
|
1059
|
+
offset += 8
|
|
1060
|
+
const staged = data.readBigInt64LE(offset)
|
|
1061
|
+
offset += 8
|
|
1062
|
+
offset += 8 // reserved
|
|
1063
|
+
// if (user.toBase58() == "11111111111111111111111111111111") continue
|
|
1064
|
+
userEscrows.push({ user, yieldIndex, ptAmount, syAmount, ytAmount, stakedYtAmount, staged })
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
// ─── Finally, seed_id + signer_bump + reserved ─────────────────────────────
|
|
1068
|
+
// seed_id: [u8; 4]
|
|
1069
|
+
const seedId = [
|
|
1070
|
+
data.readUInt8(offset),
|
|
1071
|
+
data.readUInt8(offset + 1),
|
|
1072
|
+
data.readUInt8(offset + 2),
|
|
1073
|
+
data.readUInt8(offset + 3),
|
|
1074
|
+
]
|
|
1075
|
+
offset += 4
|
|
1076
|
+
// signer_bump: [u8; 1]
|
|
1077
|
+
const signerBump = data.readUInt8(offset)
|
|
1078
|
+
offset += 1
|
|
1079
|
+
// _reserved: [u8; 3] - skip
|
|
1080
|
+
offset += 3
|
|
1081
|
+
|
|
1082
|
+
return {
|
|
1083
|
+
vault,
|
|
1084
|
+
yieldPosition,
|
|
1085
|
+
addressLookupTable,
|
|
1086
|
+
exponentCoreProgram,
|
|
1087
|
+
syProgram,
|
|
1088
|
+
admin,
|
|
1089
|
+
tokenEscrowSy,
|
|
1090
|
+
tokenEscrowYt,
|
|
1091
|
+
tokenEscrowPt,
|
|
1092
|
+
cpiAccountOrderbook,
|
|
1093
|
+
lastSyExchangeRate: lastSyExchangeRateRaw,
|
|
1094
|
+
financials,
|
|
1095
|
+
prices,
|
|
1096
|
+
configurationOptions,
|
|
1097
|
+
offers,
|
|
1098
|
+
userEscrows,
|
|
1099
|
+
offersBumpIndex,
|
|
1100
|
+
offersFreeListHead,
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
|
|
358
1104
|
function deserializeMarginfiSyMeta(x: MarginfiSyMetaRaw): MarginfiSyMeta {
|
|
359
1105
|
return {
|
|
360
1106
|
...x,
|
|
@@ -386,7 +1132,7 @@ function deserializeSyEmissionRaw(x: {
|
|
|
386
1132
|
}
|
|
387
1133
|
}
|
|
388
1134
|
|
|
389
|
-
function deserializeLpPosition(x: LpPositionRaw): LpPosition {
|
|
1135
|
+
export function deserializeLpPosition(x: LpPositionRaw): LpPosition {
|
|
390
1136
|
return {
|
|
391
1137
|
owner: x.owner,
|
|
392
1138
|
market: x.market,
|
|
@@ -402,7 +1148,7 @@ function deserializeLpPosition(x: LpPositionRaw): LpPosition {
|
|
|
402
1148
|
}
|
|
403
1149
|
}
|
|
404
1150
|
|
|
405
|
-
function deserializeYtPosition(x: YtPositionRaw): YtPosition {
|
|
1151
|
+
export function deserializeYtPosition(x: YtPositionRaw): YtPosition {
|
|
406
1152
|
return {
|
|
407
1153
|
owner: x.owner,
|
|
408
1154
|
vault: x.vault,
|
|
@@ -419,16 +1165,30 @@ function deserializeYieldTokenTracker(x: YieldTokenTrackerRaw): YieldTokenTracke
|
|
|
419
1165
|
}
|
|
420
1166
|
}
|
|
421
1167
|
|
|
422
|
-
export
|
|
1168
|
+
export interface KaminoReserveSummary {
|
|
1169
|
+
lendingMarket: web3.PublicKey
|
|
1170
|
+
baseMint: web3.PublicKey
|
|
1171
|
+
assetShareValue: Decimal
|
|
1172
|
+
scopePriceFeed?: web3.PublicKey
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1175
|
+
export async function fetchKaminoReserve(
|
|
1176
|
+
address: web3.PublicKey,
|
|
1177
|
+
connection: web3.Connection,
|
|
1178
|
+
): Promise<KaminoReserveSummary> {
|
|
423
1179
|
const reserve = await Reserve.fetch(connection, address)
|
|
424
1180
|
if (!reserve) {
|
|
425
1181
|
throw new Error("Reserve not found")
|
|
426
1182
|
}
|
|
427
1183
|
|
|
1184
|
+
const rawScopePriceFeed = reserve.config.tokenInfo.scopeConfiguration.priceFeed
|
|
1185
|
+
const scopePriceFeed = rawScopePriceFeed.equals(web3.PublicKey.default) ? undefined : rawScopePriceFeed
|
|
1186
|
+
|
|
428
1187
|
return {
|
|
429
1188
|
lendingMarket: reserve.lendingMarket,
|
|
430
1189
|
baseMint: reserve.liquidity.mintPubkey,
|
|
431
1190
|
assetShareValue: reserve.getCollateralExchangeRate(),
|
|
1191
|
+
scopePriceFeed,
|
|
432
1192
|
}
|
|
433
1193
|
}
|
|
434
1194
|
|
|
@@ -492,6 +1252,160 @@ export function deserializeEmission(emission: VaultEmissionJson): VaultEmission
|
|
|
492
1252
|
}
|
|
493
1253
|
}
|
|
494
1254
|
|
|
1255
|
+
export interface MarketThree {
|
|
1256
|
+
admin: web3.PublicKey
|
|
1257
|
+
addressLookupTable: web3.PublicKey
|
|
1258
|
+
mintPt: web3.PublicKey
|
|
1259
|
+
mintSy: web3.PublicKey
|
|
1260
|
+
mintYt: web3.PublicKey
|
|
1261
|
+
vault: web3.PublicKey
|
|
1262
|
+
tokenPtEscrow: web3.PublicKey
|
|
1263
|
+
tokenSyEscrow: web3.PublicKey
|
|
1264
|
+
tokenYtEscrow: web3.PublicKey
|
|
1265
|
+
tokenFeeTreasurySy: web3.PublicKey
|
|
1266
|
+
tokenFeeTreasuryPt: web3.PublicKey
|
|
1267
|
+
syProgram: web3.PublicKey
|
|
1268
|
+
exponentCoreProgram: web3.PublicKey
|
|
1269
|
+
selfAddress: web3.PublicKey
|
|
1270
|
+
ticks: web3.PublicKey
|
|
1271
|
+
statusFlags: number
|
|
1272
|
+
configurationOptions: MarketConfigurationOptions
|
|
1273
|
+
financials: MarketThreeFinancials
|
|
1274
|
+
cpiSyAccounts: CpiAccountIndexes
|
|
1275
|
+
cpiCoreAccounts: MarketCpiCoreIndexes
|
|
1276
|
+
isCurrentFlashSwap: boolean
|
|
1277
|
+
lpFarm: LpFarm
|
|
1278
|
+
emissions: {
|
|
1279
|
+
trackers: {
|
|
1280
|
+
tokenEscrow: web3.PublicKey
|
|
1281
|
+
lpShareIndex: number
|
|
1282
|
+
lastSeenStaged: number
|
|
1283
|
+
}[]
|
|
1284
|
+
}
|
|
1285
|
+
liquidityNetBalanceLimits: LiquidityNetBalanceLimits
|
|
1286
|
+
seedId: number[]
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
export interface Ticks {
|
|
1290
|
+
/** Current tick (left boundary of the active interval) */
|
|
1291
|
+
currentTick: number
|
|
1292
|
+
/** Array of ticks (simplified from RBTree for TypeScript) */
|
|
1293
|
+
ticksTree: Tick[]
|
|
1294
|
+
/** Market address this ticks account belongs to */
|
|
1295
|
+
market: web3.PublicKey
|
|
1296
|
+
/** Fee growth index global for PT */
|
|
1297
|
+
feeGrowthIndexGlobalPt: bigint
|
|
1298
|
+
/** Fee growth index global for SY */
|
|
1299
|
+
feeGrowthIndexGlobalSy: bigint
|
|
1300
|
+
/** Current prefix sum - the active liquidity at the current tick */
|
|
1301
|
+
currentPrefixSum: bigint
|
|
1302
|
+
/** Current spot price (ln implied rate) */
|
|
1303
|
+
currentSpotPrice: number
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
export interface Tick {
|
|
1307
|
+
liquidityNet: bigint
|
|
1308
|
+
/** Total liquidity referencing this tick as a boundary */
|
|
1309
|
+
liquidityGross: bigint
|
|
1310
|
+
/** Fee growth index outside this tick for PT */
|
|
1311
|
+
feeGrowthOutsidePt: bigint
|
|
1312
|
+
/** Fee growth index outside this tick for SY */
|
|
1313
|
+
feeGrowthOutsideSy: bigint
|
|
1314
|
+
/** Principal PT accrued in the interval starting at this tick */
|
|
1315
|
+
impliedRate: number
|
|
1316
|
+
principalPt: bigint
|
|
1317
|
+
/** Principal SY accrued in the interval starting at this tick */
|
|
1318
|
+
principalSy: bigint
|
|
1319
|
+
apyBasePoints: number
|
|
1320
|
+
principalShareSupply: bigint
|
|
1321
|
+
/** Farm yield trackers (2 trackers) */
|
|
1322
|
+
farms: { lastSeenIndex: number }[]
|
|
1323
|
+
/** Emission yield trackers (2 trackers) */
|
|
1324
|
+
emissions: { lastSeenIndex: number; lastPositionIndex: number }[]
|
|
1325
|
+
/** Last split epoch for this tick */
|
|
1326
|
+
lastSplitEpoch: bigint
|
|
1327
|
+
/** Frozen liquidity that cannot be withdrawn */
|
|
1328
|
+
frozenLiquidity: bigint
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1331
|
+
export interface MarketThreeRaw {
|
|
1332
|
+
admin: web3.PublicKey
|
|
1333
|
+
addressLookupTable: web3.PublicKey
|
|
1334
|
+
mintPt: web3.PublicKey
|
|
1335
|
+
mintSy: web3.PublicKey
|
|
1336
|
+
mintYt: web3.PublicKey
|
|
1337
|
+
vault: web3.PublicKey
|
|
1338
|
+
tokenPtEscrow: web3.PublicKey
|
|
1339
|
+
tokenSyEscrow: web3.PublicKey
|
|
1340
|
+
tokenYtEscrow: web3.PublicKey
|
|
1341
|
+
tokenFeeTreasurySy: web3.PublicKey
|
|
1342
|
+
tokenFeeTreasuryPt: web3.PublicKey
|
|
1343
|
+
syProgram: web3.PublicKey
|
|
1344
|
+
exponentCoreProgram: web3.PublicKey
|
|
1345
|
+
selfAddress: web3.PublicKey
|
|
1346
|
+
ticks: web3.PublicKey
|
|
1347
|
+
statusFlags: number
|
|
1348
|
+
configurationOptions: MarketConfigurationOptionsRaw
|
|
1349
|
+
financials: MarketThreeFinancialsRaw
|
|
1350
|
+
cpiSyAccounts: CpiAccountIndexes
|
|
1351
|
+
cpiCoreAccounts: MarketCpiCoreIndexes
|
|
1352
|
+
isCurrentFlashSwap: boolean
|
|
1353
|
+
lpFarm: LpFarm
|
|
1354
|
+
emissions: MarketEmissions
|
|
1355
|
+
liquidityNetBalanceLimits: LiquidityNetBalanceLimits
|
|
1356
|
+
seedId: number[]
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1359
|
+
export interface MarketConfigurationOptions {
|
|
1360
|
+
/** ln of fee rate root */
|
|
1361
|
+
lnFeeRateRoot: number
|
|
1362
|
+
/** Treasury fee in basis points */
|
|
1363
|
+
treasuryFeeBps: number
|
|
1364
|
+
/** Minimum LP amount per tick */
|
|
1365
|
+
minLpTickAmount: bigint
|
|
1366
|
+
/** Epsilon clamp for numerical stability */
|
|
1367
|
+
epsilonClamp: number
|
|
1368
|
+
/** Maximum LP supply */
|
|
1369
|
+
maxLpSupply: bigint
|
|
1370
|
+
/** Tick space */
|
|
1371
|
+
tickSpace: number
|
|
1372
|
+
// priceDecimals: number ??
|
|
1373
|
+
}
|
|
1374
|
+
|
|
1375
|
+
export interface MarketThreeFinancials {
|
|
1376
|
+
expirationTs: bigint
|
|
1377
|
+
ptBalance: bigint
|
|
1378
|
+
syBalance: bigint
|
|
1379
|
+
liquidityBalance: bigint
|
|
1380
|
+
}
|
|
1381
|
+
|
|
1382
|
+
export interface MarketConfigurationOptionsRaw {
|
|
1383
|
+
lnFeeRateRoot: number
|
|
1384
|
+
treasuryFeeBps: number
|
|
1385
|
+
minLpTickAmount: BN
|
|
1386
|
+
epsilonClamp: number
|
|
1387
|
+
maxLpSupply: BN
|
|
1388
|
+
tickSpace: number
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
export interface OrderbookConfigurationOptionsRaw {
|
|
1392
|
+
thresholdAmount: BN
|
|
1393
|
+
lnMakerFeeRate: number
|
|
1394
|
+
lnTakerFeeRate: number
|
|
1395
|
+
priceDecimals: number
|
|
1396
|
+
}
|
|
1397
|
+
|
|
1398
|
+
export interface MarketThreeFinancialsRaw {
|
|
1399
|
+
/** Expiration timestamp */
|
|
1400
|
+
expirationTs: number
|
|
1401
|
+
/** PT balance in the market */
|
|
1402
|
+
ptBalance: BN
|
|
1403
|
+
/** SY balance in the market */
|
|
1404
|
+
syBalance: BN
|
|
1405
|
+
/** Total liquidity balance */
|
|
1406
|
+
liquidityBalance: BN
|
|
1407
|
+
}
|
|
1408
|
+
|
|
495
1409
|
export interface MarketTwo {
|
|
496
1410
|
ptBalance: bigint
|
|
497
1411
|
syBalance: bigint
|
|
@@ -557,6 +1471,48 @@ export interface Vault {
|
|
|
557
1471
|
maxPySupply: bigint
|
|
558
1472
|
}
|
|
559
1473
|
|
|
1474
|
+
interface OrderbookFinancials {
|
|
1475
|
+
expirationTs: number
|
|
1476
|
+
ytBalance: bigint
|
|
1477
|
+
syBalance: bigint
|
|
1478
|
+
ptBalance: bigint
|
|
1479
|
+
ytFeeBalance: bigint
|
|
1480
|
+
syFeeBalance: bigint
|
|
1481
|
+
ptFeeBalance: bigint
|
|
1482
|
+
stagedSy: bigint
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1485
|
+
export interface ConfigurationOptions {
|
|
1486
|
+
priceDecimals: number
|
|
1487
|
+
thresholdAmount: bigint
|
|
1488
|
+
lnMakerFeeRate: number
|
|
1489
|
+
lnTakerFeeRate: number
|
|
1490
|
+
}
|
|
1491
|
+
|
|
1492
|
+
export interface Orderbook {
|
|
1493
|
+
vault: web3.PublicKey
|
|
1494
|
+
yieldPosition: web3.PublicKey
|
|
1495
|
+
addressLookupTable: web3.PublicKey
|
|
1496
|
+
exponentCoreProgram: web3.PublicKey
|
|
1497
|
+
syProgram: web3.PublicKey
|
|
1498
|
+
tokenEscrowSy: web3.PublicKey
|
|
1499
|
+
tokenEscrowYt: web3.PublicKey
|
|
1500
|
+
tokenEscrowPt: web3.PublicKey
|
|
1501
|
+
cpiAccountOrderbook: web3.PublicKey
|
|
1502
|
+
admin: web3.PublicKey
|
|
1503
|
+
/** Raw 256-bit PreciseNumber (12 decimals) for last SY exchange rate */
|
|
1504
|
+
lastSyExchangeRate: bigint
|
|
1505
|
+
configurationOptions: ConfigurationOptions
|
|
1506
|
+
financials: OrderbookFinancials
|
|
1507
|
+
prices: PriceTreeNode[]
|
|
1508
|
+
offers: OfferNode[]
|
|
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
|
|
1514
|
+
}
|
|
1515
|
+
|
|
560
1516
|
export interface KaminoSyMeta {
|
|
561
1517
|
kaminoReserve: web3.PublicKey
|
|
562
1518
|
kaminoObligation: web3.PublicKey
|
|
@@ -604,7 +1560,7 @@ export interface YieldTokenTracker {
|
|
|
604
1560
|
lastSeenIndex: number
|
|
605
1561
|
}
|
|
606
1562
|
|
|
607
|
-
interface LpPositionRaw {
|
|
1563
|
+
export interface LpPositionRaw {
|
|
608
1564
|
owner: web3.PublicKey
|
|
609
1565
|
market: web3.PublicKey
|
|
610
1566
|
lpBalance: BN
|
|
@@ -612,6 +1568,48 @@ interface LpPositionRaw {
|
|
|
612
1568
|
farms: { trackers: { staged: BN; lastSeenIndex: AnchorizedPNum }[] }
|
|
613
1569
|
}
|
|
614
1570
|
|
|
1571
|
+
interface LpPositionCLMMRaw {
|
|
1572
|
+
owner: web3.PublicKey
|
|
1573
|
+
market: web3.PublicKey
|
|
1574
|
+
feeInsideLastPt: BN
|
|
1575
|
+
feeInsideLastSy: BN
|
|
1576
|
+
lpBalance: BN
|
|
1577
|
+
tokensOwedSy: BN
|
|
1578
|
+
tokensOwedPt: BN
|
|
1579
|
+
lowerTickIdx: number
|
|
1580
|
+
upperTickIdx: number
|
|
1581
|
+
farms: { trackers: { staged: BN; lastSeenIndex: AnchorizedPNum }[] }
|
|
1582
|
+
shareTrackers: {
|
|
1583
|
+
trackers: {
|
|
1584
|
+
tickIdx: number
|
|
1585
|
+
rightTickIdx: number
|
|
1586
|
+
splitEpoch: BN
|
|
1587
|
+
lpShare: AnchorizedPNum
|
|
1588
|
+
emissions: { trackers: { staged: BN; lastSeenIndex: AnchorizedPNum }[] }
|
|
1589
|
+
}[]
|
|
1590
|
+
}
|
|
1591
|
+
}
|
|
1592
|
+
|
|
1593
|
+
export interface LpPositionCLMM {
|
|
1594
|
+
owner: web3.PublicKey
|
|
1595
|
+
market: web3.PublicKey
|
|
1596
|
+
feeInsideLastPt: bigint
|
|
1597
|
+
feeInsideLastSy: bigint
|
|
1598
|
+
lpBalance: bigint
|
|
1599
|
+
tokensOwedSy: bigint
|
|
1600
|
+
tokensOwedPt: bigint
|
|
1601
|
+
lowerTickIdx: number
|
|
1602
|
+
upperTickIdx: number
|
|
1603
|
+
farms: { staged: bigint; lastSeenIndex: number }[]
|
|
1604
|
+
shareTrackers: {
|
|
1605
|
+
tickIdx: number
|
|
1606
|
+
rightTickIdx: number
|
|
1607
|
+
splitEpoch: bigint
|
|
1608
|
+
lpShare: bigint
|
|
1609
|
+
emissions: { staged: bigint; lastSeenIndex: number }[]
|
|
1610
|
+
}[]
|
|
1611
|
+
}
|
|
1612
|
+
|
|
615
1613
|
export interface LpFarm {
|
|
616
1614
|
lastSeenTimestamp: number
|
|
617
1615
|
farmEmissions: FarmEmissionRaw[]
|
|
@@ -636,7 +1634,7 @@ export interface MarketEmissions {
|
|
|
636
1634
|
trackers: MarketEmission[]
|
|
637
1635
|
}
|
|
638
1636
|
|
|
639
|
-
interface MarketEmission {
|
|
1637
|
+
export interface MarketEmission {
|
|
640
1638
|
tokenEscrow: web3.PublicKey
|
|
641
1639
|
lpShareIndex: AnchorizedPNum
|
|
642
1640
|
lastSeenStaged: BN
|
|
@@ -719,7 +1717,7 @@ interface KaminoSyMetaRaw {
|
|
|
719
1717
|
emissions: SyEmissionRaw[]
|
|
720
1718
|
}
|
|
721
1719
|
|
|
722
|
-
interface YtPositionRaw {
|
|
1720
|
+
export interface YtPositionRaw {
|
|
723
1721
|
owner: web3.PublicKey
|
|
724
1722
|
vault: web3.PublicKey
|
|
725
1723
|
ytBalance: BN
|
|
@@ -732,10 +1730,76 @@ interface YieldTokenTrackerRaw {
|
|
|
732
1730
|
lastSeenIndex: AnchorizedPNum
|
|
733
1731
|
}
|
|
734
1732
|
|
|
1733
|
+
export interface OrderbookCpiAccountsRaw {
|
|
1734
|
+
syCpiAccounts: CpiAccountIndexes
|
|
1735
|
+
exponentCoreCpiAccounts: ExponentCoreCpiIndexes
|
|
1736
|
+
}
|
|
1737
|
+
|
|
1738
|
+
interface OrderbookRaw {
|
|
1739
|
+
vault: web3.PublicKey
|
|
1740
|
+
yieldPosition: web3.PublicKey
|
|
1741
|
+
addressLookupTable: web3.PublicKey
|
|
1742
|
+
exponentCoreProgram: web3.PublicKey
|
|
1743
|
+
cpiAccountOrderbook: web3.PublicKey
|
|
1744
|
+
tokenEscrowSy: web3.PublicKey
|
|
1745
|
+
tokenEscrowYt: web3.PublicKey
|
|
1746
|
+
tokenEscrowPt: web3.PublicKey
|
|
1747
|
+
nodeCount: number
|
|
1748
|
+
financials: OrderbookFinancialsRaw
|
|
1749
|
+
prices: PriceTreeNodeRaw[]
|
|
1750
|
+
offers: OfferNodeRaw[]
|
|
1751
|
+
userEscrows: UserEscrowNodeRaw[]
|
|
1752
|
+
}
|
|
1753
|
+
|
|
1754
|
+
interface OrderbookFinancialsRaw {
|
|
1755
|
+
expirationTs: number
|
|
1756
|
+
ytBalance: BN
|
|
1757
|
+
syBalance: BN
|
|
1758
|
+
ptBalance: BN
|
|
1759
|
+
}
|
|
1760
|
+
|
|
1761
|
+
export interface PriceTreeNodeRaw {
|
|
1762
|
+
key: number
|
|
1763
|
+
firstOffer: number
|
|
1764
|
+
parent: number
|
|
1765
|
+
left: number
|
|
1766
|
+
right: number
|
|
1767
|
+
}
|
|
1768
|
+
|
|
1769
|
+
export interface OfferNodeRaw {
|
|
1770
|
+
register: number
|
|
1771
|
+
amount: BN
|
|
1772
|
+
userVaultPointer: number
|
|
1773
|
+
expiryAt: number
|
|
1774
|
+
createdAt: number
|
|
1775
|
+
virtualOffer: boolean
|
|
1776
|
+
orderTypeFlag: number
|
|
1777
|
+
fillOrKill: boolean
|
|
1778
|
+
}
|
|
1779
|
+
|
|
1780
|
+
export interface UserEscrowNodeRaw {
|
|
1781
|
+
user: web3.PublicKey
|
|
1782
|
+
yieldIndex: number
|
|
1783
|
+
ptAmount: BN
|
|
1784
|
+
syAmount: BN
|
|
1785
|
+
ytAmount: BN
|
|
1786
|
+
staged: number
|
|
1787
|
+
}
|
|
1788
|
+
|
|
735
1789
|
function deserializeAnchorizedPNum(x: AnchorizedPNum): number {
|
|
736
1790
|
return parseFloat(PreciseNumber.fromRaw(x[0]).valueString)
|
|
737
1791
|
}
|
|
738
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
|
+
|
|
739
1803
|
/** Fetch the exchange rate of a JitoRestaking vault's VRT to JitoSOL */
|
|
740
1804
|
async function fetchJitoVaultData({
|
|
741
1805
|
connection,
|
|
@@ -745,30 +1809,7 @@ async function fetchJitoVaultData({
|
|
|
745
1809
|
vaultAddress: web3.PublicKey
|
|
746
1810
|
}) {
|
|
747
1811
|
const vaultAccountInfo = await connection.getAccountInfo(vaultAddress)
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
// the vault has an 8 byte discriminator at the beginning
|
|
751
|
-
const discriminatorOffset = 8
|
|
752
|
-
const vrtMintOffset = 32 + discriminatorOffset
|
|
753
|
-
const vrtSupplyOffset = 96 + discriminatorOffset
|
|
754
|
-
const jitoVaultTotalDepositsOffset = 104 + discriminatorOffset
|
|
755
|
-
const mintBase = new web3.PublicKey(d.slice(vrtMintOffset, vrtMintOffset + 32))
|
|
756
|
-
|
|
757
|
-
// For Borsh, numbers are serialized in little-endian format
|
|
758
|
-
const jitoVaultTotalSharesBuffer = d.slice(vrtSupplyOffset, vrtSupplyOffset + 8)
|
|
759
|
-
const jitoVaultTotalShares = new BN(jitoVaultTotalSharesBuffer, "le")
|
|
760
|
-
|
|
761
|
-
const jitoVaultTotalDepositsBuffer = d.slice(jitoVaultTotalDepositsOffset, jitoVaultTotalDepositsOffset + 8)
|
|
762
|
-
const jitoVaultTotalDeposits = new BN(jitoVaultTotalDepositsBuffer, "le")
|
|
763
|
-
|
|
764
|
-
const jitoVaultTotalSharesD = new Decimal(jitoVaultTotalShares.toString())
|
|
765
|
-
const jitoVaultTotalDepositsD = new Decimal(jitoVaultTotalDeposits.toString())
|
|
766
|
-
|
|
767
|
-
const exchangeRate = jitoVaultTotalDepositsD.isZero()
|
|
768
|
-
? "1.0"
|
|
769
|
-
: jitoVaultTotalDepositsD.div(jitoVaultTotalSharesD).toString()
|
|
770
|
-
|
|
771
|
-
return { exchangeRate: parseFloat(exchangeRate), mintBase }
|
|
1812
|
+
return decodeJitoVaultData(vaultAccountInfo.data)
|
|
772
1813
|
}
|
|
773
1814
|
|
|
774
1815
|
async function fetchJitoSolToSolExchangeRate({
|
|
@@ -810,23 +1851,16 @@ export async function fetchPerenaStablePoolData({
|
|
|
810
1851
|
connection: web3.Connection
|
|
811
1852
|
perenaStablePool: web3.PublicKey
|
|
812
1853
|
}) {
|
|
813
|
-
const
|
|
814
|
-
[perenaStablePool.toBuffer(), Buffer.from("liquidity")],
|
|
815
|
-
new web3.PublicKey("NUMERUNsFCP3kuNmWZuXtm1AaQCPj9uw6Guv2Ekoi5P"),
|
|
816
|
-
)
|
|
1854
|
+
const lpMint = getPerenaLpMint(perenaStablePool)
|
|
817
1855
|
|
|
818
1856
|
const [accountInfo, lpMintInfo] = await connection.getMultipleAccountsInfo([perenaStablePool, lpMint])
|
|
819
|
-
const lpMintDeserialized = MintLayout.decode(lpMintInfo.data)
|
|
820
|
-
const d = accountInfo.data
|
|
821
|
-
|
|
822
|
-
const discriminatorOffset = 8
|
|
823
|
-
const invTOffset = discriminatorOffset + 32 + 32 + 32 + 32 // 4 Pubkeys before invT
|
|
824
|
-
const invTBuffer = d.slice(invTOffset, invTOffset + 8)
|
|
825
|
-
const invT = Buffer.from(invTBuffer).readBigUInt64LE(0)
|
|
826
1857
|
|
|
827
|
-
const exchangeRate =
|
|
1858
|
+
const { lpSupply, invT, exchangeRate } = getPerenaStablePoolData({
|
|
1859
|
+
perenaStablePoolData: accountInfo.data,
|
|
1860
|
+
lpMintData: lpMintInfo.data,
|
|
1861
|
+
})
|
|
828
1862
|
|
|
829
|
-
return { lpSupply
|
|
1863
|
+
return { lpSupply, invT, exchangeRate, lpMint }
|
|
830
1864
|
}
|
|
831
1865
|
|
|
832
1866
|
/**
|
|
@@ -880,53 +1914,19 @@ export async function fetchJupiterPerpsIndex({
|
|
|
880
1914
|
lastAumUsd: BN
|
|
881
1915
|
currentIndex: AnchorizedPNum
|
|
882
1916
|
lastRealizedFeeUsdUpdateUnixTimestamp: number
|
|
883
|
-
}): Promise<{
|
|
884
|
-
index: number
|
|
885
|
-
newState: {
|
|
886
|
-
lastAumUsd: BN
|
|
887
|
-
lastRealizedFeeUsd: BN
|
|
888
|
-
lastFeeUsdResetUnixTimestamp: number
|
|
889
|
-
lastRealizedFeeUsdUpdateUnixTimestamp: number
|
|
890
|
-
}
|
|
891
|
-
}> {
|
|
892
|
-
// Decode pool account using helper that leverages Anchor 0.29.0
|
|
1917
|
+
}): Promise<ReturnType<typeof calculateJupiterPerpsIndex>> {
|
|
893
1918
|
const accountInfo = await connection.getAccountInfo(pool)
|
|
894
|
-
const account: any = decodeJupiterPerpsPoolAccount(accountInfo?.data as Buffer)
|
|
895
|
-
const SECONDS_PER_YEAR = 365 * 24 * 60 * 60
|
|
896
|
-
|
|
897
|
-
let newFeesBn: BN
|
|
898
|
-
|
|
899
|
-
if (lastRealizedFeeUsdUpdateUnixTimestamp === 0) {
|
|
900
|
-
newFeesBn = new BN(account.poolApr.realizedFeeUsd.toString())
|
|
901
|
-
} else if (Number(account.poolApr.lastUpdated) > lastFeeUsdResetUnixTimestamp) {
|
|
902
|
-
const timeBetweenResets = Number(account.poolApr.lastUpdated) - lastFeeUsdResetUnixTimestamp
|
|
903
|
-
const feeAprBps = new BN(account.poolApr.feeAprBps)
|
|
904
|
-
const estTotalFees =
|
|
905
|
-
lastAumUsd.gt(new BN(0)) && timeBetweenResets > 0
|
|
906
|
-
? feeAprBps.mul(lastAumUsd).mul(new BN(timeBetweenResets)).div(new BN(SECONDS_PER_YEAR)).div(new BN(10_000))
|
|
907
|
-
: new BN(0)
|
|
908
|
-
|
|
909
|
-
const missingFees = estTotalFees.sub(lastRealizedFeeUsd)
|
|
910
|
-
const feesSinceReset = new BN(account.poolApr.realizedFeeUsd.toString())
|
|
911
|
-
newFeesBn = missingFees.add(feesSinceReset)
|
|
912
|
-
} else {
|
|
913
|
-
newFeesBn = new BN(account.poolApr.realizedFeeUsd.toString()).sub(lastRealizedFeeUsd)
|
|
914
|
-
}
|
|
915
1919
|
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
lastAumUsd: aumUsd,
|
|
925
|
-
lastRealizedFeeUsd: new BN(account.poolApr.realizedFeeUsd.toString()),
|
|
926
|
-
lastFeeUsdResetUnixTimestamp: Number(account.poolApr.lastUpdated),
|
|
927
|
-
lastRealizedFeeUsdUpdateUnixTimestamp: Math.floor(Date.now() / 1000),
|
|
1920
|
+
return calculateJupiterPerpsIndex(
|
|
1921
|
+
{ pool: accountInfo.data },
|
|
1922
|
+
{
|
|
1923
|
+
lastFeeUsdResetUnixTimestamp,
|
|
1924
|
+
lastRealizedFeeUsd,
|
|
1925
|
+
lastAumUsd,
|
|
1926
|
+
currentIndex,
|
|
1927
|
+
lastRealizedFeeUsdUpdateUnixTimestamp,
|
|
928
1928
|
},
|
|
929
|
-
|
|
1929
|
+
)
|
|
930
1930
|
}
|
|
931
1931
|
|
|
932
1932
|
export async function fetchPyth(connection: web3.Connection): Promise<string> {
|
|
@@ -975,13 +1975,11 @@ export async function fetchFragmetricIndex({
|
|
|
975
1975
|
connection: web3.Connection
|
|
976
1976
|
fragmetricFund: web3.PublicKey
|
|
977
1977
|
}) {
|
|
978
|
-
const
|
|
979
|
-
const coder = new BorshCoder(FragmetricIdl as Idl)
|
|
980
|
-
const data = coder.accounts.decode("FundAccount", account.data)
|
|
981
|
-
const index = Number(data.one_receipt_token_as_sol) / Number(10 ** data.receipt_token_decimals)
|
|
1978
|
+
const fragmetricFundRaw = await connection.getAccountInfo(fragmetricFund)
|
|
982
1979
|
|
|
983
|
-
const receiptTokenMint =
|
|
984
|
-
|
|
1980
|
+
const { index, receiptTokenMint, wrappedTokenMint } = calculateFragmetricIndex({
|
|
1981
|
+
fragmetricFund: fragmetricFundRaw.data,
|
|
1982
|
+
})
|
|
985
1983
|
|
|
986
1984
|
return { index, receiptTokenMint, wrappedTokenMint }
|
|
987
1985
|
}
|
|
@@ -992,21 +1990,24 @@ export async function fetchJupiterLendIndex({
|
|
|
992
1990
|
}: {
|
|
993
1991
|
connection: web3.Connection
|
|
994
1992
|
jupiterLendAccount: web3.PublicKey
|
|
995
|
-
}) {
|
|
1993
|
+
}): Promise<ReturnType<typeof calculateJupiterLendIndex> & { rateModel: web3.PublicKey }> {
|
|
996
1994
|
const account = await connection.getAccountInfo(jupiterLendAccount)
|
|
997
|
-
|
|
998
|
-
const data = coder.accounts.decode("Lending", account.data)
|
|
999
|
-
const index = Number(data.token_exchange_price) / Number(10 ** 12)
|
|
1000
|
-
const tokenReservesLiquidity = data.token_reserves_liquidity
|
|
1001
|
-
const lendingSupplyPosition = data.supply_position_on_liquidity
|
|
1002
|
-
const rewardsRateModel = data.rewards_rate_model
|
|
1995
|
+
|
|
1003
1996
|
const rateModel = jupiterLendAccount.equals(new web3.PublicKey("BeAqbxfrcXmzEYT2Ra62oW2MqkuFDHaCtps47Mzg6Zj3"))
|
|
1004
1997
|
? new web3.PublicKey("Acvyi9HBGmqh3Exe1N4PjBVyY8fokq2AdC6fSLqV6KSo")
|
|
1005
1998
|
: new web3.PublicKey("6iHHKAK9Mqjn57CVmWe4szAPyTH8s8pniXSj6vWaKW5r")
|
|
1006
1999
|
|
|
1007
|
-
|
|
2000
|
+
const { index, baseTokenMint, tokenReservesLiquidity, lendingSupplyPosition, rewardsRateModel } =
|
|
2001
|
+
calculateJupiterLendIndex({ jupiterLend: account.data })
|
|
1008
2002
|
|
|
1009
|
-
return {
|
|
2003
|
+
return {
|
|
2004
|
+
rateModel,
|
|
2005
|
+
index,
|
|
2006
|
+
baseTokenMint,
|
|
2007
|
+
tokenReservesLiquidity,
|
|
2008
|
+
lendingSupplyPosition,
|
|
2009
|
+
rewardsRateModel,
|
|
2010
|
+
}
|
|
1010
2011
|
}
|
|
1011
2012
|
|
|
1012
2013
|
export async function fetchKaminoVaultIndex({
|
|
@@ -1053,18 +2054,10 @@ export async function fetchFragmetricSupportedTokenIndex({
|
|
|
1053
2054
|
connection: web3.Connection
|
|
1054
2055
|
fragmetricFund: web3.PublicKey
|
|
1055
2056
|
index: number
|
|
1056
|
-
}) {
|
|
2057
|
+
}): Promise<ReturnType<typeof calculateFragmetricSupportedTokenIndex>> {
|
|
1057
2058
|
const account = await connection.getAccountInfo(fragmetricFund)
|
|
1058
|
-
const coder = new BorshCoder(FragmetricIdl as Idl)
|
|
1059
|
-
const data = coder.accounts.decode("FundAccount", account.data)
|
|
1060
|
-
const syIndex =
|
|
1061
|
-
Number(10 ** data.supported_tokens[index].decimals) /
|
|
1062
|
-
Number(data.supported_tokens[index].one_token_as_receipt_token)
|
|
1063
|
-
|
|
1064
|
-
const receiptTokenMint = new web3.PublicKey(data.receipt_token_mint)
|
|
1065
|
-
const wrappedTokenMint = new web3.PublicKey(data.wrapped_token.mint)
|
|
1066
2059
|
|
|
1067
|
-
return {
|
|
2060
|
+
return calculateFragmetricSupportedTokenIndex({ fragmetricFund: account.data }, index)
|
|
1068
2061
|
}
|
|
1069
2062
|
|
|
1070
2063
|
interface AccountsInterface {
|
|
@@ -1090,14 +2083,14 @@ export async function fetchMeteoraIndex({
|
|
|
1090
2083
|
try {
|
|
1091
2084
|
const VIRTUAL_PRICE_PRECISION = new BN(100_000_000)
|
|
1092
2085
|
|
|
1093
|
-
const pool = await
|
|
2086
|
+
const pool = await fetchPoolAccount(connection, accounts.pool)
|
|
1094
2087
|
|
|
1095
2088
|
const poolMint = await getMint(connection, pool.lpMint)
|
|
1096
2089
|
const poolLpSupply = new BN(poolMint.supply.toString())
|
|
1097
2090
|
const poolLpDecimals = Number(poolMint.decimals.toString())
|
|
1098
2091
|
|
|
1099
|
-
const vaultA = await
|
|
1100
|
-
const vaultB = await
|
|
2092
|
+
const vaultA = await fetchVaultAccount(connection, accounts.vaultA)
|
|
2093
|
+
const vaultB = await fetchVaultAccount(connection, accounts.vaultB)
|
|
1101
2094
|
|
|
1102
2095
|
const vaultLpMintA = await getMint(connection, vaultA.lpMint)
|
|
1103
2096
|
const vaultLpMintB = await getMint(connection, vaultB.lpMint)
|
|
@@ -1155,8 +2148,6 @@ export async function fetchAdrenaIndex({
|
|
|
1155
2148
|
index: number
|
|
1156
2149
|
}> {
|
|
1157
2150
|
try {
|
|
1158
|
-
const zero = new BN(0)
|
|
1159
|
-
|
|
1160
2151
|
// Fetch all account data in a single RPC call
|
|
1161
2152
|
const accountInfos = await connection.getMultipleAccountsInfo([
|
|
1162
2153
|
accounts.pool,
|
|
@@ -1170,25 +2161,17 @@ export async function fetchAdrenaIndex({
|
|
|
1170
2161
|
throw new Error("One or more Adrena accounts not found")
|
|
1171
2162
|
}
|
|
1172
2163
|
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
let feeDifference = currentTotalFees.gte(previousTotalFees) ? currentTotalFees.sub(previousTotalFees) : zero
|
|
1186
|
-
|
|
1187
|
-
yieldIncrement = feeDifference.toNumber() / aumValue.toNumber()
|
|
1188
|
-
|
|
1189
|
-
let currentIndexNumber = parseFloat(PreciseNumber.fromRaw(currentIndex[0]).valueString)
|
|
1190
|
-
|
|
1191
|
-
return { index: currentIndexNumber + yieldIncrement }
|
|
2164
|
+
return calculateAdrenaIndex(
|
|
2165
|
+
{
|
|
2166
|
+
pool: accountInfos[0].data,
|
|
2167
|
+
custody1: accountInfos[1].data,
|
|
2168
|
+
custody2: accountInfos[2].data,
|
|
2169
|
+
custody3: accountInfos[3].data,
|
|
2170
|
+
custody4: accountInfos[4].data,
|
|
2171
|
+
},
|
|
2172
|
+
previousTotalFees,
|
|
2173
|
+
currentIndex,
|
|
2174
|
+
)
|
|
1192
2175
|
} catch (error) {
|
|
1193
2176
|
throw error
|
|
1194
2177
|
}
|
|
@@ -1201,26 +2184,14 @@ export async function fetchSanctumIndex({
|
|
|
1201
2184
|
connection: web3.Connection
|
|
1202
2185
|
accounts: AccountsInterface
|
|
1203
2186
|
}): Promise<number> {
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
const zeroBn = new BN(0)
|
|
1209
|
-
const precision = new BN(10 ** lpMint.decimals)
|
|
1210
|
-
const lpTokenSupplyBn = new BN(lpMint.supply.toString())
|
|
1211
|
-
const poolTotalSolValueBn = new BN(poolState.totalSolValue.toString())
|
|
1212
|
-
|
|
1213
|
-
if (lpTokenSupplyBn.eq(zeroBn) || poolTotalSolValueBn.eq(zeroBn)) {
|
|
1214
|
-
return 1
|
|
1215
|
-
}
|
|
1216
|
-
|
|
1217
|
-
const exchangeRateBn = lpTokenSupplyBn.mul(precision).div(poolTotalSolValueBn)
|
|
1218
|
-
const exchangeRate = new Decimal(exchangeRateBn.toString()).div(precision.toString()).toNumber()
|
|
2187
|
+
const [poolStateAccountRaw, lpMintAccountRaw] = await Promise.all(
|
|
2188
|
+
[accounts.poolState, accounts.lpMint].map((pk) => connection.getAccountInfo(pk)),
|
|
2189
|
+
)
|
|
1219
2190
|
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
}
|
|
2191
|
+
return calculateSanctumIndex({
|
|
2192
|
+
poolStateAccountData: poolStateAccountRaw.data,
|
|
2193
|
+
lpMintAccountData: lpMintAccountRaw.data,
|
|
2194
|
+
})
|
|
1224
2195
|
}
|
|
1225
2196
|
|
|
1226
2197
|
export async function fetchSolsticeRedemptionRate({
|
|
@@ -1231,60 +2202,105 @@ export async function fetchSolsticeRedemptionRate({
|
|
|
1231
2202
|
connection: web3.Connection
|
|
1232
2203
|
yieldPool: web3.PublicKey
|
|
1233
2204
|
vestingSchedule: web3.PublicKey
|
|
1234
|
-
}): Promise<{
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
sharesSupply: string
|
|
1238
|
-
vestingAmount: string
|
|
1239
|
-
totalVestedAssets: string
|
|
1240
|
-
}> {
|
|
1241
|
-
try {
|
|
1242
|
-
// Fetch both accounts in a single RPC call for efficiency
|
|
1243
|
-
const accountInfos = await connection.getMultipleAccountsInfo([yieldPool, vestingSchedule])
|
|
2205
|
+
}): Promise<ReturnType<typeof calculateSolsticeRedemptionRate>> {
|
|
2206
|
+
// Fetch both accounts in a single RPC call for efficiency
|
|
2207
|
+
const accountInfos = await connection.getMultipleAccountsInfo([yieldPool, vestingSchedule])
|
|
1244
2208
|
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
2209
|
+
if (!accountInfos[0] || !accountInfos[1]) {
|
|
2210
|
+
throw new Error("One or more Solstice accounts not found")
|
|
2211
|
+
}
|
|
1248
2212
|
|
|
1249
|
-
|
|
1250
|
-
|
|
2213
|
+
return calculateSolsticeRedemptionRate({ yieldPool: accountInfos[0].data, vestingSchedule: accountInfos[1].data })
|
|
2214
|
+
}
|
|
1251
2215
|
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
const poolSharesSupply = BigInt(yieldPoolAccount.shares_supply.toString())
|
|
1255
|
-
const vestingAmount = BigInt(vestingScheduleAccount.vesting_amount.toString())
|
|
2216
|
+
const REFLECT_ORACLE_LEN = 17
|
|
2217
|
+
const REFLECT_MAX_STALENESS_SLOTS = 15000000
|
|
1256
2218
|
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
// Calculate unvested amount
|
|
1267
|
-
let unvestedAmount: bigint
|
|
1268
|
-
if (currentTime > vestingEnd) {
|
|
1269
|
-
unvestedAmount = 0n // If current time passed the vesting end, no unvested amount
|
|
1270
|
-
} else {
|
|
1271
|
-
unvestedAmount = (vestingAmount * (vestingEnd - currentTime)) / (vestingEnd - vestingStart)
|
|
1272
|
-
}
|
|
2219
|
+
export async function fetchReflectRedemptionRate({
|
|
2220
|
+
connection,
|
|
2221
|
+
oracle,
|
|
2222
|
+
}: {
|
|
2223
|
+
connection: web3.Connection
|
|
2224
|
+
oracle: web3.PublicKey
|
|
2225
|
+
}): Promise<number> {
|
|
2226
|
+
const accountInfo = await connection.getAccountInfo(oracle)
|
|
1273
2227
|
|
|
1274
|
-
|
|
1275
|
-
|
|
2228
|
+
if (!accountInfo) {
|
|
2229
|
+
throw new Error("Reflect oracle account not found")
|
|
2230
|
+
}
|
|
1276
2231
|
|
|
1277
|
-
|
|
1278
|
-
|
|
2232
|
+
if (accountInfo.data.length !== REFLECT_ORACLE_LEN) {
|
|
2233
|
+
throw new Error(`Reflect oracle account has invalid length: ${accountInfo.data.length}`)
|
|
2234
|
+
}
|
|
1279
2235
|
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
} catch (error) {
|
|
1288
|
-
throw error
|
|
2236
|
+
const slot = Number(accountInfo.data.readBigUInt64LE(0))
|
|
2237
|
+
const price = Number(accountInfo.data.readBigUInt64LE(8))
|
|
2238
|
+
const precision = accountInfo.data.readUInt8(16)
|
|
2239
|
+
|
|
2240
|
+
const currentSlot = await connection.getSlot()
|
|
2241
|
+
if (slot > currentSlot) {
|
|
2242
|
+
throw new Error("Reflect oracle slot is ahead of the current slot")
|
|
1289
2243
|
}
|
|
2244
|
+
const slotDelta = currentSlot - slot
|
|
2245
|
+
|
|
2246
|
+
if (slotDelta > REFLECT_MAX_STALENESS_SLOTS) {
|
|
2247
|
+
throw new Error("Reflect oracle data is stale")
|
|
2248
|
+
}
|
|
2249
|
+
|
|
2250
|
+
const scale = Math.pow(10, precision)
|
|
2251
|
+
|
|
2252
|
+
if (scale === 0) {
|
|
2253
|
+
throw new Error("Invalid oracle precision")
|
|
2254
|
+
}
|
|
2255
|
+
|
|
2256
|
+
return price / scale
|
|
2257
|
+
}
|
|
2258
|
+
|
|
2259
|
+
export async function fetchOreExchangeRate({
|
|
2260
|
+
connection,
|
|
2261
|
+
storeMint,
|
|
2262
|
+
stakeAccount,
|
|
2263
|
+
treasury,
|
|
2264
|
+
}: {
|
|
2265
|
+
connection: web3.Connection
|
|
2266
|
+
storeMint: web3.PublicKey
|
|
2267
|
+
stakeAccount: web3.PublicKey
|
|
2268
|
+
treasury: web3.PublicKey
|
|
2269
|
+
}): Promise<number> {
|
|
2270
|
+
// Fetch all accounts in parallel for efficiency
|
|
2271
|
+
const [storeMintInfo, stakeAccountInfo, treasuryAccountInfo] = await Promise.all([
|
|
2272
|
+
connection.getAccountInfo(storeMint),
|
|
2273
|
+
connection.getAccountInfo(stakeAccount),
|
|
2274
|
+
connection.getAccountInfo(treasury),
|
|
2275
|
+
])
|
|
2276
|
+
|
|
2277
|
+
if (!storeMintInfo || !stakeAccountInfo || !treasuryAccountInfo) {
|
|
2278
|
+
throw new Error("One or more ORE accounts not found")
|
|
2279
|
+
}
|
|
2280
|
+
|
|
2281
|
+
return calculateOreExchangeRate({
|
|
2282
|
+
stakeAccount: stakeAccountInfo.data,
|
|
2283
|
+
treasuryAccount: treasuryAccountInfo.data,
|
|
2284
|
+
storeMint: storeMintInfo.data,
|
|
2285
|
+
})
|
|
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
|
|
1290
2306
|
}
|