@exponent-labs/exponent-fetcher 0.0.3

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.
@@ -0,0 +1,895 @@
1
+ import { AnchorProvider, BN, Idl, Program, Wallet, web3 } from "@coral-xyz/anchor"
2
+ import { ExponentCore, IDL, PROGRAM_ID } from "@exponent-labs/exponent-idl"
3
+ import { PROGRAM_ID as KAMINO_STANDARD_PROGRAM_ID } from "@exponent-labs/kamino-sy-idl"
4
+ import { KaminoLendStandard } from "@exponent-labs/kamino-sy-idl"
5
+ import {
6
+ PROGRAM_ID as MARGINFI_SY_PROGRAM_ID,
7
+ MarginfiStandard,
8
+ IDL as MarginfiSyIdl,
9
+ } from "@exponent-labs/marginfi-sy-idl"
10
+ import { PROGRAM_ID as PERENA_STANDARD_PROGRAM_ID } from "@exponent-labs/perena-sy-idl"
11
+ import { PROGRAM_ID as JITO_RESTAKING_SY_PROGRAM_ID } from "@exponent-labs/jito-restaking-sy-idl"
12
+ import { IDL as KaminoSyIdl } from "@exponent-labs/kamino-lend-standard"
13
+ import { PreciseNumber } from "@exponent-labs/precise-number"
14
+ import { Reserve } from "@exponent-labs/kamino-reserve-deserializer"
15
+ import { JitoRestakingStandard, IDL as JitoRestakingSyIdl } from "@exponent-labs/jito-restaking-sy-idl"
16
+ import { IDL as PerenaSyIdl } from "@exponent-labs/perena-sy-idl"
17
+ import { getStakePoolAccount } from "@solana/spl-stake-pool"
18
+ import { IDL as FragmetricIdl } from "@exponent-labs/fragmetric-idl"
19
+ import { decodePoolAccount, decodeVaultAccount } from "@exponent-labs/meteora-idl"
20
+ import { getMint, getAccount } from "@solana/spl-token"
21
+ import { BorshCoder } from "@coral-xyz/anchor"
22
+ import {
23
+ AnchorizedPNum,
24
+ AnchorizedPNumJson,
25
+ MarginfiSyMeta,
26
+ MarginfiSyMetaRaw,
27
+ VaultEmission,
28
+ VaultEmissionJson,
29
+ CpiAccountIndexes,
30
+ JitoRestakingSyMetaAccountRaw,
31
+ JitoRestakingSyMetaAccount,
32
+ deserializeJitoRestakingSyMetaAccountRaw,
33
+ InterfaceType,
34
+ SyEmissionRaw,
35
+ GenericSyMetaAccount,
36
+ deserializeGenericSyMetaAccountRaw,
37
+ GenericSyMetaAccountRaw,
38
+ deserializePerenaSyMetaAccountRaw,
39
+ PerenaSyMetaAccountRaw,
40
+ PerenaSyMetaAccount,
41
+ } from "@exponent-labs/exponent-types"
42
+ import Decimal from "decimal.js"
43
+ import { PerenaStandard } from "@exponent-labs/perena-sy-idl"
44
+ import { MintLayout } from "@solana/spl-token"
45
+ import { GenericStandard } from "@exponent-labs/generic-sy-idl"
46
+ import { PROGRAM_ID as GENERIC_STANDARD_PROGRAM_ID, IDL as GenericStandardIdl } from "@exponent-labs/generic-sy-idl"
47
+ import { computeD, getAmountByShare } from "./utils/meteora"
48
+
49
+ export function serializeAnchorizedPNumFromJson(pnum: AnchorizedPNum): AnchorizedPNumJson {
50
+ const serializedArray = pnum[0].map((bn) => bn.toString())
51
+ return { 0: serializedArray }
52
+ }
53
+
54
+ export function deserializeAnchorizedPNumFromJson(serialized: AnchorizedPNumJson): AnchorizedPNum {
55
+ const bnArray = serialized[0].map((str) => new BN(str))
56
+ return { 0: bnArray }
57
+ }
58
+
59
+ export class MyWallet implements Wallet {
60
+ constructor(readonly payer: web3.Keypair) {
61
+ this.payer = payer
62
+ }
63
+
64
+ async signTransaction<T extends web3.Transaction | web3.VersionedTransaction>(tx: T): Promise<T> {
65
+ if (tx instanceof web3.Transaction) {
66
+ tx.partialSign(this.payer)
67
+ } else {
68
+ tx.sign([this.payer])
69
+ }
70
+ return tx
71
+ }
72
+
73
+ async signAllTransactions<T extends web3.Transaction | web3.VersionedTransaction>(txs: T[]): Promise<T[]> {
74
+ return txs.map((t) => {
75
+ if (t instanceof web3.Transaction) {
76
+ t.partialSign(this.payer)
77
+ } else if (t instanceof web3.VersionedTransaction) {
78
+ t.sign([this.payer])
79
+ }
80
+ return t
81
+ })
82
+ }
83
+
84
+ get publicKey(): web3.PublicKey {
85
+ return this.payer.publicKey
86
+ }
87
+ }
88
+
89
+ export class ExponentFetcher {
90
+ public program: Program<ExponentCore>
91
+ public marginfiSyProgram: Program<MarginfiStandard>
92
+ public kaminoSyProgram: Program<KaminoLendStandard>
93
+ public jitoRestakingSyProgram: Program<JitoRestakingStandard>
94
+ public perenaSyProgram: Program<PerenaStandard>
95
+ public genericStandardProgram: Program<GenericStandard>
96
+ public connection: web3.Connection
97
+ public coreProgramId: web3.PublicKey
98
+ public marginfiSyProgramId: web3.PublicKey
99
+ public kaminoSyProgramId: web3.PublicKey
100
+ public jitoRestakingSyProgramId: web3.PublicKey
101
+ public perenaSyProgramId: web3.PublicKey
102
+ public genericStandardProgramId: web3.PublicKey
103
+
104
+ constructor({
105
+ connection,
106
+ coreProgramId = new web3.PublicKey(PROGRAM_ID),
107
+ marginfiSyProgramId = new web3.PublicKey(MARGINFI_SY_PROGRAM_ID),
108
+ kaminoSyProgramId = new web3.PublicKey(KAMINO_STANDARD_PROGRAM_ID),
109
+ jitoRestakingSyProgramId = new web3.PublicKey(JITO_RESTAKING_SY_PROGRAM_ID),
110
+ perenaSyProgramId = new web3.PublicKey(PERENA_STANDARD_PROGRAM_ID),
111
+ genericStandardProgramId = new web3.PublicKey(GENERIC_STANDARD_PROGRAM_ID),
112
+ }: {
113
+ connection: web3.Connection
114
+ coreProgramId?: web3.PublicKey
115
+ marginfiSyProgramId?: web3.PublicKey
116
+ kaminoSyProgramId?: web3.PublicKey
117
+ jitoRestakingSyProgramId?: web3.PublicKey
118
+ perenaSyProgramId?: web3.PublicKey
119
+ genericStandardProgramId?: web3.PublicKey
120
+ }) {
121
+ this.connection = connection
122
+ this.coreProgramId = coreProgramId
123
+ this.marginfiSyProgramId = marginfiSyProgramId
124
+ this.kaminoSyProgramId = kaminoSyProgramId
125
+ this.jitoRestakingSyProgramId = jitoRestakingSyProgramId
126
+ this.perenaSyProgramId = perenaSyProgramId
127
+ this.genericStandardProgramId = genericStandardProgramId
128
+ // Note - keypair does not matter. this is purely read-only fetching
129
+ const mockWallet = new MyWallet(web3.Keypair.generate())
130
+ const provider = new AnchorProvider(connection, mockWallet)
131
+
132
+ this.program = new Program<ExponentCore>(IDL as ExponentCore, provider)
133
+ this.marginfiSyProgram = new Program<MarginfiStandard>(MarginfiSyIdl as MarginfiStandard, provider)
134
+ this.kaminoSyProgram = new Program<KaminoLendStandard>(KaminoSyIdl as KaminoLendStandard, provider)
135
+ this.jitoRestakingSyProgram = new Program<JitoRestakingStandard>(
136
+ JitoRestakingSyIdl as JitoRestakingStandard,
137
+ provider,
138
+ )
139
+ this.perenaSyProgram = new Program<PerenaStandard>(PerenaSyIdl as PerenaStandard, provider)
140
+ this.genericStandardProgram = new Program<GenericStandard>(GenericStandardIdl, provider)
141
+ }
142
+
143
+ async fetchVault(address: web3.PublicKey) {
144
+ try {
145
+ const v: VaultRaw = await this.program.account.vault.fetch(address)
146
+ return deserializeVault(v)
147
+ } catch (e) {
148
+ console.error(`Error fetching vault ${address.toBase58()}`)
149
+ console.error(e)
150
+ throw e
151
+ }
152
+ }
153
+
154
+ async fetchMarket(address: web3.PublicKey): Promise<MarketTwo> {
155
+ try {
156
+ const m: MarketTwoRaw = await this.program.account.marketTwo.fetch(address)
157
+ return deserializeMarketTwo(m)
158
+ } catch (e) {
159
+ console.error(`Error fetching market ${address.toBase58()}`)
160
+ console.error(e)
161
+ throw e
162
+ }
163
+ }
164
+
165
+ async fetchMarginfiSyMeta(address: web3.PublicKey): Promise<MarginfiSyMeta> {
166
+ const x: MarginfiSyMetaRaw = await this.marginfiSyProgram.account.syMeta.fetch(address)
167
+ return deserializeMarginfiSyMeta(x)
168
+ }
169
+
170
+ async fetchMarginfiSyPosition(address: web3.PublicKey): Promise<SyPosition> {
171
+ const x = await this.marginfiSyProgram.account.position.fetch(address)
172
+ const emissions = x.rewardIndexes.map((r) => ({
173
+ mint: r.mint,
174
+ staged: BigInt(r.claimableRewardsAmount.toString()),
175
+ lastSeenIndex: parseFloat(PreciseNumber.fromRaw(r.lastSeenShareIndex[0]).valueString),
176
+ }))
177
+
178
+ return {
179
+ owner: x.owner,
180
+ balanceSy: BigInt(x.amount.toString()),
181
+ emissions,
182
+ }
183
+ }
184
+
185
+ async fetchKaminoSyPosition(address: web3.PublicKey): Promise<SyPosition> {
186
+ const x = await this.kaminoSyProgram.account.position.fetch(address)
187
+ const emissions = x.rewardIndexes.map((r) => ({
188
+ mint: r.mint,
189
+ staged: BigInt(r.claimableRewardsAmount.toString()),
190
+ lastSeenIndex: parseFloat(PreciseNumber.fromRaw(r.lastSeenShareIndex[0]).valueString),
191
+ }))
192
+
193
+ return {
194
+ owner: x.owner,
195
+ balanceSy: BigInt(x.amount.toString()),
196
+ emissions,
197
+ }
198
+ }
199
+
200
+ async fetchJitoRestakingSyPosition(address: web3.PublicKey): Promise<SyPosition> {
201
+ const x = await this.jitoRestakingSyProgram.account.position.fetch(address)
202
+ const emissions = x.rewardIndexes.map((r) => ({
203
+ mint: r.mint,
204
+ staged: BigInt(r.claimableRewardsAmount.toString()),
205
+ lastSeenIndex: parseFloat(PreciseNumber.fromRaw(r.lastSeenShareIndex[0]).valueString),
206
+ }))
207
+
208
+ return {
209
+ owner: x.owner,
210
+ balanceSy: BigInt(x.amount.toString()),
211
+ emissions,
212
+ }
213
+ }
214
+
215
+ async fetchPerenaSyPosition(address: web3.PublicKey): Promise<SyPosition> {
216
+ const x = await this.perenaSyProgram.account.position.fetch(address)
217
+ const emissions = x.rewardIndexes.map((r) => ({
218
+ mint: r.mint,
219
+ staged: BigInt(r.claimableRewardsAmount.toString()),
220
+ lastSeenIndex: parseFloat(PreciseNumber.fromRaw(r.lastSeenShareIndex[0]).valueString),
221
+ }))
222
+ return { owner: x.owner, balanceSy: BigInt(x.amount.toString()), emissions }
223
+ }
224
+
225
+ async fetchGenericSyPosition(address: web3.PublicKey): Promise<SyPosition> {
226
+ const x = await this.genericStandardProgram.account.position.fetch(address)
227
+ const emissions = x.rewardIndexes.map((r) => ({
228
+ mint: r.mint,
229
+ staged: BigInt(r.claimableRewardsAmount.toString()),
230
+ lastSeenIndex: parseFloat(PreciseNumber.fromRaw(r.lastSeenShareIndex[0]).valueString),
231
+ }))
232
+ return { owner: x.owner, balanceSy: BigInt(x.amount.toString()), emissions }
233
+ }
234
+
235
+ async fetchKaminoSyMeta(address: web3.PublicKey): Promise<KaminoSyMeta> {
236
+ const x: KaminoSyMetaRaw = await this.kaminoSyProgram.account.syMeta.fetch(address)
237
+ return {
238
+ ...x,
239
+ emissions: x.emissions.map(deserializeSyEmissionRaw),
240
+ }
241
+ }
242
+
243
+ async fetchLpPosition(address: web3.PublicKey): Promise<LpPosition> {
244
+ const x: LpPositionRaw = await this.program.account.lpPosition.fetch(address)
245
+ console.log("FARMs SHOULD BE HERE", x.farms)
246
+ return deserializeLpPosition(x)
247
+ }
248
+
249
+ async fetchYtPosition(address: web3.PublicKey): Promise<YtPosition> {
250
+ const x: YtPositionRaw = await this.program.account.yieldTokenPosition.fetch(address)
251
+ return deserializeYtPosition(x)
252
+ }
253
+
254
+ async fetchJitoRestakingSyMeta(address: web3.PublicKey): Promise<JitoRestakingSyMetaAccount> {
255
+ const x: JitoRestakingSyMetaAccountRaw = await this.jitoRestakingSyProgram.account.syMeta.fetch(address)
256
+
257
+ return deserializeJitoRestakingSyMetaAccountRaw(x)
258
+ }
259
+
260
+ async fetchPerenaSyMeta(address: web3.PublicKey): Promise<PerenaSyMetaAccount> {
261
+ const x: PerenaSyMetaAccountRaw = await this.perenaSyProgram.account.syMeta.fetch(address)
262
+ return deserializePerenaSyMetaAccountRaw(x)
263
+ }
264
+
265
+ async fetchGenericSyMeta(address: web3.PublicKey): Promise<GenericSyMetaAccount> {
266
+ const x: GenericSyMetaAccountRaw = await this.genericStandardProgram.account.syMeta.fetch(address)
267
+
268
+ return deserializeGenericSyMetaAccountRaw(x)
269
+ }
270
+ }
271
+
272
+ function deserializeMarketTwo(m: MarketTwoRaw): MarketTwo {
273
+ return {
274
+ ptBalance: BigInt(m.financials.ptBalance.toString()),
275
+ syBalance: BigInt(m.financials.syBalance.toString()),
276
+ lpEscrowAmount: BigInt(m.lpEscrowAmount.toString()),
277
+ maxLpSupply: BigInt(m.maxLpSupply.toString()),
278
+ rateScalarRoot: m.financials.rateScalarRoot,
279
+ lnFeeRateRoot: m.financials.lnFeeRateRoot,
280
+ lastLnImpliedRate: m.financials.lastLnImpliedRate,
281
+ expirationTs: m.financials.expirationTs.toNumber(),
282
+ addressLookupTable: m.addressLookupTable,
283
+ mintSy: m.mintSy,
284
+ mintPt: m.mintPt,
285
+ mintLp: m.mintLp,
286
+ vault: m.vault,
287
+ tokenLpEscrow: m.tokenLpEscrow,
288
+ tokenSyEscrow: m.tokenSyEscrow,
289
+ tokenPtEscrow: m.tokenPtEscrow,
290
+ tokenFeeTreasurySy: m.tokenFeeTreasurySy,
291
+ selfAddress: m.selfAddress,
292
+ syProgram: m.syProgram,
293
+ statusFlags: m.statusFlags,
294
+ cpiAccounts: m.cpiAccounts,
295
+ feeTreasurySyBps: m.feeTreasurySyBps,
296
+ isCurrentFlashSwap: m.isCurrentFlashSwap,
297
+ lpFarm: m.lpFarm,
298
+ emissions: {
299
+ trackers: m.emissions.trackers.map((t) => ({
300
+ tokenEscrow: t.tokenEscrow,
301
+ lpShareIndex: deserializeAnchorizedPNum(t.lpShareIndex),
302
+ lastSeenStaged: Number(t.lastSeenStaged),
303
+ })),
304
+ },
305
+ liquidityNetBalanceLimits: m.liquidityNetBalanceLimits,
306
+ }
307
+ }
308
+
309
+ function deserializeVault(x: VaultRaw): Vault {
310
+ return {
311
+ syProgram: x.syProgram,
312
+ mintSy: x.mintSy,
313
+ mintYt: x.mintYt,
314
+ mintPt: x.mintPt,
315
+ escrowSy: x.escrowSy,
316
+ escrowYt: x.escrowYt,
317
+ startTs: x.startTs,
318
+ duration: x.duration,
319
+ authority: x.authority,
320
+ lastSeenSyExchangeRate: deserializeAnchorizedPNum(x.lastSeenSyExchangeRate),
321
+ allTimeHighSyExchangeRate: deserializeAnchorizedPNum(x.allTimeHighSyExchangeRate),
322
+ finalSyExchangeRate: deserializeAnchorizedPNum(x.finalSyExchangeRate),
323
+ addressLookupTable: x.addressLookupTable,
324
+ ptSupply: BigInt(x.ptSupply.toString()),
325
+ syForPt: BigInt(x.syForPt.toString()),
326
+ yieldPosition: x.yieldPosition,
327
+ emissions: x.emissions,
328
+ cpiAccounts: x.cpiAccounts,
329
+ treasurySy: x.treasurySy,
330
+ totalSyInEscrow: BigInt(x.totalSyInEscrow.toString()),
331
+ interestBpsFee: x.interestBpsFee,
332
+ status: x.status,
333
+ treasurySyTokenAccount: x.treasurySyTokenAccount,
334
+ minOpSizeStrip: BigInt(x.minOpSizeStrip.toString()),
335
+ minOpSizeMerge: BigInt(x.minOpSizeMerge.toString()),
336
+ maxPySupply: BigInt(x.maxPySupply.toString()),
337
+ }
338
+ }
339
+
340
+ function deserializeMarginfiSyMeta(x: MarginfiSyMetaRaw): MarginfiSyMeta {
341
+ return {
342
+ ...x,
343
+ maxSySupply: BigInt(x.maxSySupply.toString()),
344
+ minMintSize: BigInt(x.minMintSize.toString()),
345
+ minRedeemSize: BigInt(x.minRedeemSize.toString()),
346
+ }
347
+ }
348
+
349
+ function deserializeSyEmissionRaw(x: {
350
+ mint: web3.PublicKey
351
+ index: AnchorizedPNum
352
+ lastSeenTotalAccruedEmissions: BN
353
+ totalClaimedEmissions: BN
354
+ tokenProgram: web3.PublicKey
355
+ escrowAccount: web3.PublicKey
356
+ treasuryEmission: BN
357
+ lastSeenIndex: AnchorizedPNum
358
+ }): SyEmissionRaw {
359
+ return {
360
+ mint: x.mint,
361
+ index: x.index,
362
+ lastSeenTotalAccruedEmissions: new BN(x.lastSeenTotalAccruedEmissions.toString()),
363
+ totalClaimedEmissions: new BN(x.totalClaimedEmissions.toString()),
364
+ tokenProgram: x.tokenProgram,
365
+ escrowAccount: x.escrowAccount,
366
+ treasuryEmission: new BN(x.treasuryEmission.toString()),
367
+ lastSeenIndex: x.lastSeenIndex,
368
+ }
369
+ }
370
+
371
+ function deserializeLpPosition(x: LpPositionRaw): LpPosition {
372
+ return {
373
+ owner: x.owner,
374
+ market: x.market,
375
+ lpBalance: BigInt(x.lpBalance.toString()),
376
+ emissions: x.emissions.trackers.map((t) => ({
377
+ staged: BigInt(t.staged.toString()),
378
+ lastSeenIndex: parseFloat(PreciseNumber.fromRaw(t.lastSeenIndex[0]).valueString),
379
+ })),
380
+ farms: x.farms.trackers.map((t) => ({
381
+ staged: BigInt(t.staged.toString()),
382
+ lastSeenIndex: parseFloat(PreciseNumber.fromRaw(t.lastSeenIndex[0]).valueString),
383
+ })),
384
+ }
385
+ }
386
+
387
+ function deserializeYtPosition(x: YtPositionRaw): YtPosition {
388
+ return {
389
+ owner: x.owner,
390
+ vault: x.vault,
391
+ ytBalance: BigInt(x.ytBalance.toString()),
392
+ interest: deserializeYieldTokenTracker(x.interest),
393
+ emissions: x.emissions.map(deserializeYieldTokenTracker),
394
+ }
395
+ }
396
+
397
+ function deserializeYieldTokenTracker(x: YieldTokenTrackerRaw): YieldTokenTracker {
398
+ return {
399
+ staged: BigInt(x.staged.toString()),
400
+ lastSeenIndex: parseFloat(PreciseNumber.fromRaw(x.lastSeenIndex[0]).valueString),
401
+ }
402
+ }
403
+
404
+ export async function fetchKaminoReserve(address: web3.PublicKey, connection: web3.Connection) {
405
+ const reserve = await Reserve.fetch(connection, address)
406
+ if (!reserve) {
407
+ throw new Error("Reserve not found")
408
+ }
409
+
410
+ return {
411
+ lendingMarket: reserve.lendingMarket,
412
+ baseMint: reserve.liquidity.mintPubkey,
413
+ assetShareValue: reserve.getCollateralExchangeRate(),
414
+ }
415
+ }
416
+
417
+ export function serializeEmission(emission: VaultEmission): VaultEmissionJson {
418
+ return {
419
+ tokenAccount: emission.tokenAccount.toString(),
420
+ initialIndex: serializeAnchorizedPNumFromJson(emission.initialIndex),
421
+ lastSeenIndex: serializeAnchorizedPNumFromJson(emission.lastSeenIndex),
422
+ treasuryTokenAccount: emission.treasuryTokenAccount.toString(),
423
+ feeBps: emission.feeBps,
424
+ treasuryEmission: emission.treasuryEmission.toString(),
425
+ }
426
+ }
427
+
428
+ export function deserializeEmission(emission: VaultEmissionJson): VaultEmission {
429
+ return {
430
+ tokenAccount: new web3.PublicKey(emission.tokenAccount),
431
+ initialIndex: deserializeAnchorizedPNumFromJson(emission.initialIndex),
432
+ lastSeenIndex: deserializeAnchorizedPNumFromJson(emission.lastSeenIndex),
433
+ treasuryTokenAccount: new web3.PublicKey(emission.treasuryTokenAccount),
434
+ feeBps: emission.feeBps,
435
+ treasuryEmission: new BN(emission.treasuryEmission),
436
+ }
437
+ }
438
+
439
+ export interface MarketTwo {
440
+ ptBalance: bigint
441
+ syBalance: bigint
442
+ rateScalarRoot: number
443
+ lnFeeRateRoot: number
444
+ lastLnImpliedRate: number
445
+ expirationTs: number
446
+ addressLookupTable: web3.PublicKey
447
+ mintSy: web3.PublicKey
448
+ mintPt: web3.PublicKey
449
+ mintLp: web3.PublicKey
450
+ vault: web3.PublicKey
451
+ tokenPtEscrow: web3.PublicKey
452
+ tokenSyEscrow: web3.PublicKey
453
+ tokenLpEscrow: web3.PublicKey
454
+ tokenFeeTreasurySy: web3.PublicKey
455
+ syProgram: web3.PublicKey
456
+ selfAddress: web3.PublicKey
457
+ statusFlags: number
458
+ lpEscrowAmount: bigint
459
+ maxLpSupply: bigint
460
+ cpiAccounts: CpiAccountIndexes
461
+ feeTreasurySyBps: number
462
+ isCurrentFlashSwap: boolean
463
+ lpFarm: LpFarm
464
+ emissions: {
465
+ trackers: {
466
+ tokenEscrow: web3.PublicKey
467
+ lpShareIndex: number
468
+ lastSeenStaged: number
469
+ }[]
470
+ }
471
+ liquidityNetBalanceLimits: LiquidityNetBalanceLimits
472
+ }
473
+
474
+ export interface Vault {
475
+ syProgram: web3.PublicKey
476
+ mintSy: web3.PublicKey
477
+ mintYt: web3.PublicKey
478
+ mintPt: web3.PublicKey
479
+ escrowSy: web3.PublicKey
480
+ startTs: number
481
+ duration: number
482
+ authority: web3.PublicKey
483
+ lastSeenSyExchangeRate: number
484
+ allTimeHighSyExchangeRate: number
485
+ finalSyExchangeRate: number
486
+ addressLookupTable: web3.PublicKey
487
+ totalSyInEscrow: bigint
488
+ ptSupply: bigint
489
+ syForPt: bigint
490
+ yieldPosition: web3.PublicKey
491
+ escrowYt: web3.PublicKey
492
+ treasurySy: BN
493
+ treasurySyTokenAccount: web3.PublicKey
494
+ interestBpsFee: number
495
+ emissions: VaultEmission[]
496
+ status: number
497
+ minOpSizeStrip: bigint
498
+ minOpSizeMerge: bigint
499
+ cpiAccounts: CpiAccountIndexes
500
+ maxPySupply: bigint
501
+ }
502
+
503
+ export interface KaminoSyMeta {
504
+ kaminoReserve: web3.PublicKey
505
+ kaminoObligation: web3.PublicKey
506
+ kaminoFarm: web3.PublicKey
507
+ kaminoUserMetadata: web3.PublicKey
508
+ mintSy: web3.PublicKey
509
+ /** Where the SY tokens get deposited */
510
+ tokenSyEscrow: web3.PublicKey
511
+ minMintSize: BN
512
+ minRedeemSize: BN
513
+ emissions: SyEmissionRaw[]
514
+ }
515
+
516
+ export interface SyPosition {
517
+ balanceSy: bigint
518
+ owner: web3.PublicKey
519
+ emissions: {
520
+ /** Last seen index for SY-to-emission exchange rate */
521
+ lastSeenIndex: number
522
+ /** mint of emission token */
523
+ mint: web3.PublicKey
524
+ /** How many emissions are staged to claim */
525
+ staged: bigint
526
+ }[]
527
+ }
528
+
529
+ export interface LpPosition {
530
+ owner: web3.PublicKey
531
+ market: web3.PublicKey
532
+ lpBalance: bigint
533
+ emissions: { staged: bigint; lastSeenIndex: number }[]
534
+ farms: { staged: bigint; lastSeenIndex: number }[]
535
+ }
536
+
537
+ export interface YtPosition {
538
+ owner: web3.PublicKey
539
+ vault: web3.PublicKey
540
+ ytBalance: bigint
541
+ interest: YieldTokenTracker
542
+ emissions: YieldTokenTracker[]
543
+ }
544
+
545
+ export interface YieldTokenTracker {
546
+ staged: bigint
547
+ lastSeenIndex: number
548
+ }
549
+
550
+ interface LpPositionRaw {
551
+ owner: web3.PublicKey
552
+ market: web3.PublicKey
553
+ lpBalance: BN
554
+ emissions: { trackers: { staged: BN; lastSeenIndex: AnchorizedPNum }[] }
555
+ farms: { trackers: { staged: BN; lastSeenIndex: AnchorizedPNum }[] }
556
+ }
557
+
558
+ export interface LpFarm {
559
+ lastSeenTimestamp: number
560
+ farmEmissions: FarmEmissionRaw[]
561
+ }
562
+
563
+ interface FarmEmissionRaw {
564
+ mint: web3.PublicKey
565
+ tokenRate: BN
566
+ expiryTimestamp: number
567
+ index: AnchorizedPNum
568
+ }
569
+
570
+ export interface LiquidityNetBalanceLimits {
571
+ windowStartTimestamp: number
572
+ windowStartNetBalance: BN
573
+ maxNetBalanceChangeNegativePercentage: number
574
+ maxNetBalanceChangePositivePercentage: number
575
+ windowDurationSeconds: number
576
+ }
577
+
578
+ export interface MarketEmissions {
579
+ trackers: MarketEmission[]
580
+ }
581
+
582
+ interface MarketEmission {
583
+ tokenEscrow: web3.PublicKey
584
+ lpShareIndex: AnchorizedPNum
585
+ lastSeenStaged: BN
586
+ }
587
+
588
+ interface MarketTwoRaw {
589
+ financials: MarketFinancialsRaw
590
+ addressLookupTable: web3.PublicKey
591
+ vault: web3.PublicKey
592
+ mintSy: web3.PublicKey
593
+ mintLp: web3.PublicKey
594
+ mintPt: web3.PublicKey
595
+ tokenPtEscrow: web3.PublicKey
596
+ tokenSyEscrow: web3.PublicKey
597
+ tokenLpEscrow: web3.PublicKey
598
+ tokenFeeTreasurySy: web3.PublicKey
599
+ syProgram: web3.PublicKey
600
+ selfAddress: web3.PublicKey
601
+ statusFlags: number
602
+ lpEscrowAmount: BN
603
+ maxLpSupply: BN
604
+ cpiAccounts: CpiAccountIndexes
605
+ /** Fee rate taken off of trade fees (typically around 20%) */
606
+ feeTreasurySyBps: number
607
+ isCurrentFlashSwap: boolean
608
+ lpFarm: LpFarm
609
+ liquidityNetBalanceLimits: LiquidityNetBalanceLimits
610
+ emissions: MarketEmissions
611
+ }
612
+
613
+ interface MarketFinancialsRaw {
614
+ ptBalance: BN
615
+ syBalance: BN
616
+ rateScalarRoot: number
617
+ lnFeeRateRoot: number
618
+ lastLnImpliedRate: number
619
+ expirationTs: BN
620
+ }
621
+
622
+ interface VaultRaw {
623
+ syProgram: web3.PublicKey
624
+ mintSy: web3.PublicKey
625
+ mintYt: web3.PublicKey
626
+ mintPt: web3.PublicKey
627
+ escrowSy: web3.PublicKey
628
+ startTs: number
629
+ duration: number
630
+ authority: web3.PublicKey
631
+ lastSeenSyExchangeRate: AnchorizedPNum
632
+ allTimeHighSyExchangeRate: AnchorizedPNum
633
+ finalSyExchangeRate: AnchorizedPNum
634
+ addressLookupTable: web3.PublicKey
635
+ ptSupply: BN
636
+ syForPt: BN
637
+ escrowYt: web3.PublicKey
638
+ totalSyInEscrow: BN
639
+ yieldPosition: web3.PublicKey
640
+ treasurySy: BN
641
+ treasurySyTokenAccount: web3.PublicKey
642
+ interestBpsFee: number
643
+ emissions: VaultEmission[]
644
+ status: number
645
+ minOpSizeStrip: BN
646
+ minOpSizeMerge: BN
647
+ cpiAccounts: CpiAccountIndexes
648
+ maxPySupply: BN
649
+ }
650
+
651
+ interface KaminoSyMetaRaw {
652
+ kaminoReserve: web3.PublicKey
653
+ kaminoObligation: web3.PublicKey
654
+ kaminoFarm: web3.PublicKey
655
+ kaminoUserMetadata: web3.PublicKey
656
+ mintSy: web3.PublicKey
657
+ minMintSize: BN
658
+ minRedeemSize: BN
659
+ /** Where the SY tokens get deposited */
660
+ tokenSyEscrow: web3.PublicKey
661
+ emissions: SyEmissionRaw[]
662
+ }
663
+
664
+ interface YtPositionRaw {
665
+ owner: web3.PublicKey
666
+ vault: web3.PublicKey
667
+ ytBalance: BN
668
+ interest: YieldTokenTrackerRaw
669
+ emissions: YieldTokenTrackerRaw[]
670
+ }
671
+
672
+ interface YieldTokenTrackerRaw {
673
+ staged: BN
674
+ lastSeenIndex: AnchorizedPNum
675
+ }
676
+
677
+ function deserializeAnchorizedPNum(x: AnchorizedPNum): number {
678
+ return parseFloat(PreciseNumber.fromRaw(x[0]).valueString)
679
+ }
680
+
681
+ /** Fetch the exchange rate of a JitoRestaking vault's VRT to JitoSOL */
682
+ async function fetchJitoVaultData({
683
+ connection,
684
+ vaultAddress,
685
+ }: {
686
+ connection: web3.Connection
687
+ vaultAddress: web3.PublicKey
688
+ }) {
689
+ const vaultAccountInfo = await connection.getAccountInfo(vaultAddress)
690
+ const d = vaultAccountInfo.data
691
+
692
+ // the vault has an 8 byte discriminator at the beginning
693
+ const discriminatorOffset = 8
694
+ const vrtMintOffset = 32 + discriminatorOffset
695
+ const vrtSupplyOffset = 96 + discriminatorOffset
696
+ const jitoVaultTotalDepositsOffset = 104 + discriminatorOffset
697
+ const mintBase = new web3.PublicKey(d.slice(vrtMintOffset, vrtMintOffset + 32))
698
+
699
+ // For Borsh, numbers are serialized in little-endian format
700
+ const jitoVaultTotalSharesBuffer = d.slice(vrtSupplyOffset, vrtSupplyOffset + 8)
701
+ const jitoVaultTotalShares = new BN(jitoVaultTotalSharesBuffer, "le")
702
+
703
+ const jitoVaultTotalDepositsBuffer = d.slice(jitoVaultTotalDepositsOffset, jitoVaultTotalDepositsOffset + 8)
704
+ const jitoVaultTotalDeposits = new BN(jitoVaultTotalDepositsBuffer, "le")
705
+
706
+ const jitoVaultTotalSharesD = new Decimal(jitoVaultTotalShares.toString())
707
+ const jitoVaultTotalDepositsD = new Decimal(jitoVaultTotalDeposits.toString())
708
+
709
+ const exchangeRate = jitoVaultTotalDepositsD.isZero()
710
+ ? "1.0"
711
+ : jitoVaultTotalDepositsD.div(jitoVaultTotalSharesD).toString()
712
+
713
+ return { exchangeRate: parseFloat(exchangeRate), mintBase }
714
+ }
715
+
716
+ async function fetchJitoSolToSolExchangeRate({
717
+ connection,
718
+ interfaceType,
719
+ accounts,
720
+ }: {
721
+ connection: web3.Connection
722
+ /** Interface type of the JitoRestaking interface program that Exponent manages */
723
+ interfaceType: InterfaceType
724
+ /** "remaining accounts" */
725
+ accounts: web3.PublicKey[]
726
+ }) {
727
+ if (interfaceType.splStakePool) {
728
+ const stakePool = await getStakePoolAccount(connection, accounts[0])
729
+
730
+ return Number(stakePool.account.data.totalLamports) / Number(stakePool.account.data.poolTokenSupply)
731
+ }
732
+
733
+ throw new Error("Unsupported interface type")
734
+ }
735
+
736
+ export async function fetchPerenaStablePoolData({
737
+ connection,
738
+ perenaStablePool,
739
+ }: {
740
+ connection: web3.Connection
741
+ perenaStablePool: web3.PublicKey
742
+ }) {
743
+ const [lpMint, _] = web3.PublicKey.findProgramAddressSync(
744
+ [perenaStablePool.toBuffer(), Buffer.from("liquidity")],
745
+ new web3.PublicKey("NUMERUNsFCP3kuNmWZuXtm1AaQCPj9uw6Guv2Ekoi5P"),
746
+ )
747
+
748
+ const [accountInfo, lpMintInfo] = await connection.getMultipleAccountsInfo([perenaStablePool, lpMint])
749
+ const lpMintDeserialized = MintLayout.decode(lpMintInfo.data)
750
+ const d = accountInfo.data
751
+
752
+ const discriminatorOffset = 8
753
+ const invTOffset = discriminatorOffset + 32 + 32 + 32 + 32 // 4 Pubkeys before invT
754
+ const invTBuffer = d.slice(invTOffset, invTOffset + 8)
755
+ const invT = Buffer.from(invTBuffer).readBigUInt64LE(0)
756
+
757
+ const exchangeRate = new Decimal(invT.toString()).div(new Decimal(lpMintDeserialized.supply.toString())).toString()
758
+
759
+ return { lpSupply: lpMintDeserialized.supply, invT, exchangeRate, lpMint }
760
+ }
761
+
762
+ /**
763
+ * Fetch the exchange rate of a JitoRestaking vault
764
+ * @param connection
765
+ * @param accounts "remaining accounts"
766
+ * @param vaultAddress "address of the Jito Restaking vault"
767
+ * @param interfaceType "interface type of the Jito Restaking interface program that Exponent manages"
768
+ * @returns
769
+ */
770
+ export async function fetchJitoRestaking(
771
+ connection: web3.Connection,
772
+ accounts: web3.PublicKey[],
773
+ interfaceType: InterfaceType,
774
+ vaultAddress: web3.PublicKey,
775
+ ): Promise<{ mintBase: web3.PublicKey; exchangeRateUnderlying: number; exchangeRate: string }> {
776
+ const vaultDataP = fetchJitoVaultData({ connection, vaultAddress })
777
+
778
+ const solToSolExchangeRateP = fetchJitoSolToSolExchangeRate({ connection, interfaceType, accounts })
779
+
780
+ const [{ exchangeRate: e1, mintBase }, e2] = await Promise.all([vaultDataP, solToSolExchangeRateP])
781
+
782
+ // exchange rate of VRT to SOL is the product of the two exchange rates
783
+ const exr = e1 * e2
784
+
785
+ return {
786
+ mintBase,
787
+ exchangeRateUnderlying: e2,
788
+ exchangeRate: exr.toString(),
789
+ }
790
+ }
791
+
792
+ export async function fetchPyth(connection: web3.Connection): Promise<string> {
793
+ return ""
794
+ }
795
+
796
+ export async function fetchGenericSyMetaIndex({
797
+ connection,
798
+ genericSyMeta,
799
+ }: {
800
+ connection: web3.Connection
801
+ genericSyMeta: web3.PublicKey
802
+ }) {
803
+ const [accountInfo] = await connection.getMultipleAccountsInfo([genericSyMeta])
804
+ const d = accountInfo.data
805
+
806
+ // Calculate offset:
807
+ // 8 (discriminator) +
808
+ // 32 (mint_sy) +
809
+ // 32 (token_sy_escrow) +
810
+ // 8 (max_sy_supply) +
811
+ // 8 (min_mint_size) +
812
+ // 8 (min_redeem_size) +
813
+ // 1 (self_address_bump)
814
+ const indexOffset = 8 + 32 + 32 + 8 + 8 + 8 + 1
815
+ const indexBuffer = d.slice(indexOffset, indexOffset + 32)
816
+
817
+ // Split the 32 byte buffer into 4 u64 (8 bytes each)
818
+ const nums = []
819
+ for (let i = 0; i < 4; i++) {
820
+ const slice = indexBuffer.slice(i * 8, (i + 1) * 8)
821
+ nums.push(new BN(slice, undefined, "le")) // Added 'le' for little-endian
822
+ }
823
+
824
+ // Convert the buffer to AnchorizedPNum format with 4 BNs
825
+ const indexRaw = { 0: nums }
826
+ const index = parseFloat(PreciseNumber.fromRaw(indexRaw[0]).valueString)
827
+
828
+ return { index }
829
+ }
830
+
831
+ export async function fetchFragmetricIndex({
832
+ connection,
833
+ fragmetricFund,
834
+ }: {
835
+ connection: web3.Connection
836
+ fragmetricFund: web3.PublicKey
837
+ }) {
838
+ const account = await connection.getAccountInfo(fragmetricFund)
839
+ const coder = new BorshCoder(FragmetricIdl as Idl)
840
+ const data = coder.accounts.decode("FundAccount", account.data)
841
+ const index = Number(data.one_receipt_token_as_sol) / Number(10 ** data.receipt_token_decimals)
842
+
843
+ const receiptTokenMint = new web3.PublicKey(data.receipt_token_mint)
844
+ const wrappedTokenMint = new web3.PublicKey(data.wrapped_token.mint)
845
+
846
+ return { index, receiptTokenMint, wrappedTokenMint }
847
+ }
848
+
849
+ export async function fetchMeteoraUsdcUsdtIndex({
850
+ connection,
851
+ accounts,
852
+ onChainTime,
853
+ }: {
854
+ connection: web3.Connection
855
+ accounts: any
856
+ onChainTime: number
857
+ }): Promise<number> {
858
+ try {
859
+ const VIRTUAL_PRICE_PRECISION = new BN(100_000_000)
860
+
861
+ const pool = await decodePoolAccount(connection, accounts.pool)
862
+
863
+ const poolLpSupply = new BN((await getMint(connection, pool.lpMint)).supply.toString())
864
+
865
+ const vaultA = await decodeVaultAccount(connection, accounts.vaultA)
866
+ const vaultB = await decodeVaultAccount(connection, accounts.vaultB)
867
+
868
+ const vaultALpSupply = (await getMint(connection, vaultA.lpMint)).supply
869
+ const vaultBLpSupply = (await getMint(connection, vaultB.lpMint)).supply
870
+
871
+ const poolVaultALpTokenAmount = (await getAccount(connection, pool.aVaultLp)).amount
872
+ const poolVaultBLpTokenAmount = (await getAccount(connection, pool.bVaultLp)).amount
873
+
874
+ const tokenAAmount = getAmountByShare(
875
+ new BN(poolVaultALpTokenAmount.toString()),
876
+ new BN(vaultALpSupply.toString()),
877
+ vaultA,
878
+ onChainTime,
879
+ )
880
+ const tokenBAmount = getAmountByShare(
881
+ new BN(poolVaultBLpTokenAmount.toString()),
882
+ new BN(vaultBLpSupply.toString()),
883
+ vaultB,
884
+ onChainTime,
885
+ )
886
+
887
+ const d = computeD(pool.curveType, tokenAAmount, tokenBAmount)
888
+ const virtualPriceBigNum = poolLpSupply.isZero() ? new BN(0) : d.mul(VIRTUAL_PRICE_PRECISION).div(poolLpSupply)
889
+ const virtualPrice = new Decimal(virtualPriceBigNum.toString()).div(VIRTUAL_PRICE_PRECISION.toString()).toNumber()
890
+
891
+ return virtualPrice
892
+ } catch (error) {
893
+ throw error
894
+ }
895
+ }