@exponent-labs/generic-sy-sdk 0.9.20 → 0.9.22

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/src/index.ts CHANGED
@@ -4,6 +4,7 @@ import {
4
4
  ASSOCIATED_TOKEN_PROGRAM_ID,
5
5
  TOKEN_2022_PROGRAM_ID,
6
6
  TOKEN_PROGRAM_ID,
7
+ createAssociatedTokenAccountIdempotentInstruction,
7
8
  getAssociatedTokenAddressSync,
8
9
  } from "@solana/spl-token"
9
10
 
@@ -30,15 +31,83 @@ import {
30
31
  GenericSyState,
31
32
  Hook,
32
33
  InterfaceTypeGeneric,
34
+ getGenericSyMetaInitInterfaceType,
35
+ getGenericSyMetaInterfaceType,
33
36
  } from "@exponent-labs/exponent-types"
34
37
  import { FRAGMETRIC_PROGRAM_ID, FragmetricPda } from "@exponent-labs/fragmetric-pda"
35
38
  import { PROGRAM_ID as GENERIC_STANDARD_PROGRAM_ID, GenericStandard, IDL } from "@exponent-labs/generic-sy-idl"
36
39
  import { GenericSyPda } from "@exponent-labs/generic-sy-pda"
37
40
  import { PreciseNumber } from "@exponent-labs/precise-number"
38
41
 
42
+ import {
43
+ buildExponentTranchingUpdateMarketInstruction,
44
+ fetchExponentTranchingRate,
45
+ } from "./interfaces/exponentTranching"
46
+ import { fetchGlowVaultRate } from "./interfaces/glowVault"
39
47
  import { SyInitializeConfig, SyTokenMetadata } from "./types"
40
48
  import { MyWallet } from "./utils"
41
49
 
50
+ const GET_STATE_DISCRIMINATOR = Buffer.from([7])
51
+
52
+ async function fetchOnChainSyRate({
53
+ connection,
54
+ programId,
55
+ meta,
56
+ mintSy,
57
+ tokenSyEscrow,
58
+ remainingAccounts,
59
+ }: {
60
+ connection: web3.Connection
61
+ programId: web3.PublicKey
62
+ meta: web3.PublicKey
63
+ mintSy: web3.PublicKey
64
+ tokenSyEscrow: web3.PublicKey
65
+ remainingAccounts: web3.AccountMeta[]
66
+ }): Promise<number> {
67
+ const instruction = new web3.TransactionInstruction({
68
+ programId,
69
+ keys: [
70
+ { pubkey: meta, isSigner: false, isWritable: true },
71
+ { pubkey: mintSy, isSigner: false, isWritable: false },
72
+ { pubkey: tokenSyEscrow, isSigner: false, isWritable: false },
73
+ ...remainingAccounts,
74
+ ],
75
+ data: GET_STATE_DISCRIMINATOR,
76
+ })
77
+ const [feePayer, { blockhash }] = await Promise.all([
78
+ connection.getSlotLeader("confirmed"),
79
+ connection.getLatestBlockhash("confirmed"),
80
+ ])
81
+ const message = new web3.TransactionMessage({
82
+ payerKey: new web3.PublicKey(feePayer),
83
+ recentBlockhash: blockhash,
84
+ instructions: [instruction],
85
+ }).compileToV0Message()
86
+ const simulation = await connection.simulateTransaction(new web3.VersionedTransaction(message), {
87
+ commitment: "confirmed",
88
+ replaceRecentBlockhash: true,
89
+ sigVerify: false,
90
+ })
91
+
92
+ if (simulation.value.err) {
93
+ const logs = simulation.value.logs?.join("\n") ?? "No simulation logs"
94
+ throw new Error(`Generic Standard get_state simulation failed: ${JSON.stringify(simulation.value.err)}\n${logs}`)
95
+ }
96
+
97
+ const returnData = simulation.value.returnData
98
+ if (!returnData || returnData.programId !== programId.toBase58()) {
99
+ throw new Error("Generic Standard get_state returned invalid program data")
100
+ }
101
+
102
+ const data = Buffer.from(returnData.data[0], "base64")
103
+ if (data.length < 32) {
104
+ throw new Error("Generic Standard get_state return data is too short")
105
+ }
106
+
107
+ const exchangeRate = Array.from({ length: 4 }, (_, index) => new BN(data.subarray(index * 8, (index + 1) * 8), "le"))
108
+ return Number(PreciseNumber.fromRaw(exchangeRate).valueString)
109
+ }
110
+
42
111
  /**
43
112
  * Create a custom updateStakePoolBalance instruction using dynamic program ID
44
113
  */
@@ -86,48 +155,7 @@ async function loadAccountState(
86
155
  genericStandardProgramId: web3.PublicKey,
87
156
  interfaceType: InterfaceTypeGeneric,
88
157
  ) {
89
- let interfaceIndex: number
90
- if ("pyth" in interfaceType) {
91
- interfaceIndex = InterfaceType.Pyth
92
- } else if ("splStakePool" in interfaceType) {
93
- interfaceIndex = InterfaceType.SplStakePool
94
- } else if ("one" in interfaceType) {
95
- interfaceIndex = InterfaceType.One
96
- } else if ("fragmetric" in interfaceType) {
97
- interfaceIndex = InterfaceType.Fragmetric
98
- } else if ("meteora" in interfaceType) {
99
- interfaceIndex = InterfaceType.Meteora
100
- } else if ("sanctum" in interfaceType) {
101
- interfaceIndex = InterfaceType.Sanctum
102
- } else if ("fragmetricSupportedAsset" in interfaceType) {
103
- interfaceIndex = InterfaceType.FragmetricSupportedAsset
104
- } else if ("jupiterPerps" in interfaceType) {
105
- interfaceIndex = InterfaceType.JupiterPerps
106
- } else if ("adrena" in interfaceType) {
107
- interfaceIndex = InterfaceType.Adrena
108
- } else if ("hyloLpToken" in interfaceType) {
109
- interfaceIndex = InterfaceType.HyloLpToken
110
- } else if ("jupiterLend" in interfaceType) {
111
- interfaceIndex = InterfaceType.JupiterLend
112
- } else if ("kaminoVault" in interfaceType) {
113
- interfaceIndex = InterfaceType.KaminoVault
114
- } else if ("solstice" in interfaceType) {
115
- interfaceIndex = InterfaceType.Solstice
116
- } else if ("reflect" in interfaceType) {
117
- interfaceIndex = InterfaceType.Reflect
118
- } else if ("ore" in interfaceType) {
119
- interfaceIndex = InterfaceType.Ore
120
- } else if ("chainlink" in interfaceType) {
121
- interfaceIndex = InterfaceType.Chainlink
122
- } else if ("chainlinkDataStream" in interfaceType) {
123
- interfaceIndex = InterfaceType.ChainlinkDataStream
124
- } else if ("scope" in interfaceType) {
125
- interfaceIndex = InterfaceType.Scope
126
- } else if ("solsticeGlamVault" in interfaceType) {
127
- interfaceIndex = InterfaceType.SolsticeGlamVault
128
- } else {
129
- throw new Error("Unsupported interface type")
130
- }
158
+ const interfaceIndex = getInterfaceTypeIndex(interfaceType)
131
159
  const pda = new GenericSyPda(genericStandardProgramId, interfaceIndex)
132
160
  const fetcher = new ExponentFetcher({ connection: cnx, genericStandardProgramId })
133
161
  const syMetaAddress = pda.syMeta({ yieldBearingMint })
@@ -157,6 +185,36 @@ export enum InterfaceType {
157
185
  ChainlinkDataStream = 16,
158
186
  Scope = 17,
159
187
  SolsticeGlamVault = 18,
188
+ ExponentTranching = 19,
189
+ GlowVault = 20,
190
+ LoopscaleVaultLp = 21,
191
+ }
192
+
193
+ export function getInterfaceTypeIndex(interfaceType: InterfaceTypeGeneric): InterfaceType {
194
+ if ("pyth" in interfaceType) return InterfaceType.Pyth
195
+ if ("splStakePool" in interfaceType) return InterfaceType.SplStakePool
196
+ if ("one" in interfaceType) return InterfaceType.One
197
+ if ("fragmetricSupportedAsset" in interfaceType) return InterfaceType.FragmetricSupportedAsset
198
+ if ("fragmetric" in interfaceType) return InterfaceType.Fragmetric
199
+ if ("meteora" in interfaceType) return InterfaceType.Meteora
200
+ if ("sanctum" in interfaceType) return InterfaceType.Sanctum
201
+ if ("jupiterPerps" in interfaceType) return InterfaceType.JupiterPerps
202
+ if ("adrena" in interfaceType) return InterfaceType.Adrena
203
+ if ("hyloLpToken" in interfaceType) return InterfaceType.HyloLpToken
204
+ if ("jupiterLend" in interfaceType) return InterfaceType.JupiterLend
205
+ if ("kaminoVault" in interfaceType) return InterfaceType.KaminoVault
206
+ if ("solstice" in interfaceType) return InterfaceType.Solstice
207
+ if ("reflect" in interfaceType) return InterfaceType.Reflect
208
+ if ("ore" in interfaceType) return InterfaceType.Ore
209
+ if ("chainlinkDataStream" in interfaceType) return InterfaceType.ChainlinkDataStream
210
+ if ("chainlink" in interfaceType) return InterfaceType.Chainlink
211
+ if ("scope" in interfaceType) return InterfaceType.Scope
212
+ if ("solsticeGlamVault" in interfaceType) return InterfaceType.SolsticeGlamVault
213
+ if ("exponentTranching" in interfaceType) return InterfaceType.ExponentTranching
214
+ if ("glowVault" in interfaceType) return InterfaceType.GlowVault
215
+ if ("loopscaleVaultLp" in interfaceType) return InterfaceType.LoopscaleVaultLp
216
+
217
+ throw new Error("Unsupported interface type")
160
218
  }
161
219
 
162
220
  export function getInterfaceTypeObj(
@@ -219,6 +277,12 @@ export function getInterfaceTypeObj(
219
277
  return { scope: { priceChain: scopePriceChain, maximumAgeSeconds: new BN(scopeMaximumAgeSeconds) } }
220
278
  case InterfaceType.SolsticeGlamVault:
221
279
  return { solsticeGlamVault: { padding: [] } }
280
+ case InterfaceType.ExponentTranching:
281
+ return { exponentTranching: {} }
282
+ case InterfaceType.GlowVault:
283
+ return { glowVault: { padding: [] } }
284
+ case InterfaceType.LoopscaleVaultLp:
285
+ return { loopscaleVaultLp: { padding: [] } }
222
286
  default:
223
287
  throw new Error(`Unsupported interface type: ${interfaceType}`)
224
288
  }
@@ -278,6 +342,13 @@ export function getInterfaceTypeFromString(interfaceType: string): InterfaceType
278
342
  return { scope: { priceChain: [0, 512, 512, 512], maximumAgeSeconds: new BN(3600) } }
279
343
  case "solstice-glam-vault":
280
344
  return { solsticeGlamVault: { padding: [] } }
345
+ case "exponent-tranching":
346
+ return { exponentTranching: {} }
347
+ case "glow-vault":
348
+ return { glowVault: { padding: [] } }
349
+ case "loopscale-vault":
350
+ case "loopscale-vault-lp":
351
+ return { loopscaleVaultLp: { padding: [] } }
281
352
  default:
282
353
  throw new Error(`Unsupported interface type: ${interfaceType}`)
283
354
  }
@@ -306,61 +377,26 @@ export class GenericSySdk {
306
377
  new AnchorProvider(new web3.Connection("https://api.devnet.solana.com"), new MyWallet(web3.Keypair.generate())),
307
378
  )
308
379
 
309
- // Determine interface index based on the interface type from state
310
- let interfaceIndex: number
311
- if ("pyth" in this.state.account.interfaceType) {
312
- interfaceIndex = InterfaceType.Pyth
313
- } else if ("splStakePool" in this.state.account.interfaceType) {
314
- interfaceIndex = InterfaceType.SplStakePool
315
- } else if ("one" in this.state.account.interfaceType) {
316
- interfaceIndex = InterfaceType.One
317
- } else if ("fragmetricSupportedAsset" in this.state.account.interfaceType) {
318
- interfaceIndex = InterfaceType.FragmetricSupportedAsset
319
- } else if ("fragmetric" in this.state.account.interfaceType) {
320
- interfaceIndex = InterfaceType.Fragmetric
321
- } else if ("meteora" in this.state.account.interfaceType) {
322
- interfaceIndex = InterfaceType.Meteora
323
- } else if ("sanctum" in this.state.account.interfaceType) {
324
- interfaceIndex = InterfaceType.Sanctum
325
- } else if ("jupiterPerps" in this.state.account.interfaceType) {
326
- interfaceIndex = InterfaceType.JupiterPerps
327
- } else if ("adrena" in this.state.account.interfaceType) {
328
- interfaceIndex = InterfaceType.Adrena
329
- } else if ("hyloLpToken" in this.state.account.interfaceType) {
330
- interfaceIndex = InterfaceType.HyloLpToken
331
- } else if ("jupiterLend" in this.state.account.interfaceType) {
332
- interfaceIndex = InterfaceType.JupiterLend
333
- } else if ("kaminoVault" in this.state.account.interfaceType) {
334
- interfaceIndex = InterfaceType.KaminoVault
335
- } else if ("solstice" in this.state.account.interfaceType) {
336
- interfaceIndex = InterfaceType.Solstice
337
- } else if ("reflect" in this.state.account.interfaceType) {
338
- interfaceIndex = InterfaceType.Reflect
339
- } else if ("ore" in this.state.account.interfaceType) {
340
- interfaceIndex = InterfaceType.Ore
341
- } else if ("chainlink" in this.state.account.interfaceType) {
342
- interfaceIndex = InterfaceType.Chainlink
343
- } else if ("chainlinkDataStream" in this.state.account.interfaceType) {
344
- interfaceIndex = InterfaceType.ChainlinkDataStream
345
- } else if ("scope" in this.state.account.interfaceType) {
346
- interfaceIndex = InterfaceType.Scope
347
- } else if ("solsticeGlamVault" in this.state.account.interfaceType) {
348
- interfaceIndex = InterfaceType.SolsticeGlamVault
349
- } else {
350
- throw new Error("Unsupported interface type")
351
- }
352
-
380
+ const interfaceIndex = getInterfaceTypeIndex(getGenericSyMetaInitInterfaceType(this.state.account))
353
381
  this.syPda = new GenericSyPda(programId, interfaceIndex)
354
382
  }
355
383
 
356
- private static async calculateSyRate(
357
- account: GenericSyMetaAccount,
358
- interfaceType: InterfaceTypeGeneric,
359
- cnx: web3.Connection,
360
- ): Promise<{
384
+ private static async calculateSyRate({
385
+ account,
386
+ cnx,
387
+ syMetaAddress,
388
+ genericStandardProgramId,
389
+ }: {
390
+ account: GenericSyMetaAccount
391
+ cnx: web3.Connection
392
+ syMetaAddress: web3.PublicKey
393
+ genericStandardProgramId: web3.PublicKey
394
+ }): Promise<{
361
395
  syRate: number
362
396
  additionalData?: AdditionalDataGeneric
363
397
  }> {
398
+ const interfaceType = getGenericSyMetaInterfaceType(account)
399
+
364
400
  if ("pyth" in interfaceType) {
365
401
  const syRate = Number(PreciseNumber.fromRaw(account.index[0]).valueString)
366
402
  return { syRate: syRate }
@@ -414,7 +450,7 @@ export class GenericSySdk {
414
450
  } else if ("meteora" in interfaceType) {
415
451
  const currentTs = Math.floor(Date.now() / 1000)
416
452
 
417
- const accounts = {
453
+ let accounts = {
418
454
  pool: account.interfaceAccounts[0],
419
455
  vaultA: account.interfaceAccounts[4],
420
456
  vaultB: account.interfaceAccounts[5],
@@ -434,11 +470,10 @@ export class GenericSySdk {
434
470
 
435
471
  return { syRate: exchangeRate }
436
472
  } else if ("adrena" in interfaceType) {
437
- // @ts-ignore
438
- const previousTotalFees = account.interfaceType.adrena.previousTotalFees
473
+ const previousTotalFees = interfaceType.adrena.previousTotalFees
439
474
  const currentIndex = account.index
440
475
 
441
- let accounts = {
476
+ const accounts = {
442
477
  pool: account.interfaceAccounts[0],
443
478
  custody1: account.interfaceAccounts[1],
444
479
  custody2: account.interfaceAccounts[2],
@@ -450,8 +485,7 @@ export class GenericSySdk {
450
485
 
451
486
  return { syRate: syRate.index }
452
487
  } else if ("jupiterPerps" in interfaceType) {
453
- // @ts-ignore
454
- const j = account.interfaceType.jupiterPerps
488
+ const j = interfaceType.jupiterPerps
455
489
  const { index } = await fetchJupiterPerpsIndex({
456
490
  connection: cnx,
457
491
  pool: account.interfaceAccounts[0],
@@ -574,6 +608,50 @@ export class GenericSySdk {
574
608
  stslxMint,
575
609
  })
576
610
  return { syRate }
611
+ } else if ("exponentTranching" in interfaceType) {
612
+ const marketAddress = account.interfaceAccounts[0]
613
+ if (account.interfaceAccounts.length !== 1 || !marketAddress) {
614
+ throw new Error("Exponent Tranching interface requires exactly one market account")
615
+ }
616
+ const syRate = await fetchExponentTranchingRate({
617
+ connection: cnx,
618
+ marketAddress,
619
+ yieldBearingMint: account.yieldBearingMint,
620
+ })
621
+ return { syRate }
622
+ } else if ("glowVault" in interfaceType) {
623
+ const vaultAddress = account.interfaceAccounts[0]
624
+ if (account.interfaceAccounts.length !== 1 || !vaultAddress) {
625
+ throw new Error("Glow Vault interface requires exactly one vault account")
626
+ }
627
+ const syRate = await fetchGlowVaultRate({
628
+ connection: cnx,
629
+ vaultAddress,
630
+ expectedShareMint: account.yieldBearingMint,
631
+ })
632
+ return { syRate }
633
+ } else if ("loopscaleVaultLp" in interfaceType) {
634
+ if (account.interfaceAccounts.length !== 3) {
635
+ throw new Error("Loopscale Vault LP interface requires exactly three accounts")
636
+ }
637
+
638
+ const syRate = await fetchOnChainSyRate({
639
+ connection: cnx,
640
+ programId: genericStandardProgramId,
641
+ meta: syMetaAddress,
642
+ mintSy: account.mintSy,
643
+ tokenSyEscrow: account.tokenSyEscrow,
644
+ remainingAccounts: account.interfaceAccounts
645
+ .map((pubkey) => ({ pubkey, isSigner: false, isWritable: false }))
646
+ .concat(
647
+ account.emissions.map((emission) => ({
648
+ pubkey: emission.escrowAccount,
649
+ isSigner: false,
650
+ isWritable: false,
651
+ })),
652
+ ),
653
+ })
654
+ return { syRate }
577
655
  } else {
578
656
  return { syRate: 1 }
579
657
  }
@@ -596,53 +674,17 @@ export class GenericSySdk {
596
674
  const account = await loadAccountState(cnx, yieldBearingMint, genericStandardProgramId, interfaceType)
597
675
  const tokenProgramBase = (await cnx.getAccountInfo(yieldBearingMint))?.owner
598
676
 
599
- let interfaceIndex: number
600
- if ("pyth" in interfaceType) {
601
- interfaceIndex = InterfaceType.Pyth
602
- } else if ("splStakePool" in interfaceType) {
603
- interfaceIndex = InterfaceType.SplStakePool
604
- } else if ("one" in interfaceType) {
605
- interfaceIndex = InterfaceType.One
606
- } else if ("fragmetricSupportedAsset" in interfaceType) {
607
- interfaceIndex = InterfaceType.FragmetricSupportedAsset
608
- } else if ("fragmetric" in interfaceType) {
609
- interfaceIndex = InterfaceType.Fragmetric
610
- } else if ("meteora" in interfaceType) {
611
- interfaceIndex = InterfaceType.Meteora
612
- } else if ("sanctum" in interfaceType) {
613
- interfaceIndex = InterfaceType.Sanctum
614
- } else if ("jupiterPerps" in interfaceType) {
615
- interfaceIndex = InterfaceType.JupiterPerps
616
- } else if ("adrena" in interfaceType) {
617
- interfaceIndex = InterfaceType.Adrena
618
- } else if ("hyloLpToken" in interfaceType) {
619
- interfaceIndex = InterfaceType.HyloLpToken
620
- } else if ("jupiterLend" in interfaceType) {
621
- interfaceIndex = InterfaceType.JupiterLend
622
- } else if ("kaminoVault" in interfaceType) {
623
- interfaceIndex = InterfaceType.KaminoVault
624
- } else if ("solstice" in interfaceType) {
625
- interfaceIndex = InterfaceType.Solstice
626
- } else if ("reflect" in interfaceType) {
627
- interfaceIndex = InterfaceType.Reflect
628
- } else if ("ore" in interfaceType) {
629
- interfaceIndex = InterfaceType.Ore
630
- } else if ("chainlink" in interfaceType) {
631
- interfaceIndex = InterfaceType.Chainlink
632
- } else if ("chainlinkDataStream" in interfaceType) {
633
- interfaceIndex = InterfaceType.ChainlinkDataStream
634
- } else if ("scope" in interfaceType) {
635
- interfaceIndex = InterfaceType.Scope
636
- } else if ("solsticeGlamVault" in interfaceType) {
637
- interfaceIndex = InterfaceType.SolsticeGlamVault
638
- } else {
639
- throw new Error("Unsupported interface type")
640
- }
677
+ const interfaceIndex = getInterfaceTypeIndex(getGenericSyMetaInitInterfaceType(account))
641
678
  const selfAddress = new GenericSyPda(genericStandardProgramId, interfaceIndex).syMeta({
642
679
  yieldBearingMint,
643
680
  })
644
681
 
645
- const { syRate, additionalData } = await GenericSySdk.calculateSyRate(account, interfaceType, cnx)
682
+ const { syRate, additionalData } = await GenericSySdk.calculateSyRate({
683
+ account,
684
+ cnx,
685
+ syMetaAddress: selfAddress,
686
+ genericStandardProgramId,
687
+ })
646
688
 
647
689
  const state: GenericSyState = {
648
690
  selfAddress,
@@ -671,7 +713,12 @@ export class GenericSySdk {
671
713
  const account = await fetcher.fetchGenericSyMeta(syMetaAddress)
672
714
  const tokenProgramBase = (await cnx.getAccountInfo(account.yieldBearingMint))?.owner
673
715
 
674
- const { syRate, additionalData } = await GenericSySdk.calculateSyRate(account, account.interfaceType, cnx)
716
+ const { syRate, additionalData } = await GenericSySdk.calculateSyRate({
717
+ account,
718
+ cnx,
719
+ syMetaAddress,
720
+ genericStandardProgramId,
721
+ })
675
722
 
676
723
  const state: GenericSyState = {
677
724
  selfAddress: syMetaAddress,
@@ -686,14 +733,16 @@ export class GenericSySdk {
686
733
 
687
734
  /** Reload the state of the account */
688
735
  async reload(cnx: web3.Connection) {
689
- const account = await loadAccountState(
690
- cnx,
691
- this.state.account.yieldBearingMint,
692
- this.program.programId,
693
- this.state.account.interfaceType,
694
- )
736
+ const genericStandardProgramId = this.syPda.programId
737
+ const fetcher = new ExponentFetcher({ connection: cnx, genericStandardProgramId })
738
+ const account = await fetcher.fetchGenericSyMeta(this.state.selfAddress)
695
739
  this.state.account = account
696
- const { syRate, additionalData } = await GenericSySdk.calculateSyRate(account, account.interfaceType, cnx)
740
+ const { syRate, additionalData } = await GenericSySdk.calculateSyRate({
741
+ account,
742
+ cnx,
743
+ syMetaAddress: this.state.selfAddress,
744
+ genericStandardProgramId,
745
+ })
697
746
  this.state.syRate = syRate
698
747
  this.state.additionalData = additionalData
699
748
  }
@@ -702,6 +751,14 @@ export class GenericSySdk {
702
751
  return this.state.account
703
752
  }
704
753
 
754
+ get interfaceType() {
755
+ return getGenericSyMetaInterfaceType(this.account)
756
+ }
757
+
758
+ get initInterfaceType() {
759
+ return getGenericSyMetaInitInterfaceType(this.account)
760
+ }
761
+
705
762
  get mintSy() {
706
763
  return this.account.mintSy
707
764
  }
@@ -751,10 +808,7 @@ export class GenericSySdk {
751
808
  }
752
809
 
753
810
  get fragmetricData() {
754
- if (
755
- !("fragmetric" in this.state.account.interfaceType) &&
756
- !("fragmetricSupportedAsset" in this.state.account.interfaceType)
757
- ) {
811
+ if (!("fragmetric" in this.interfaceType) && !("fragmetricSupportedAsset" in this.interfaceType)) {
758
812
  throw new Error("Not a fragmetric interface")
759
813
  }
760
814
  if (!this.state.additionalData?.fragmetricData) {
@@ -764,7 +818,7 @@ export class GenericSySdk {
764
818
  }
765
819
 
766
820
  get jupiterLendData() {
767
- if (!("jupiterLend" in this.state.account.interfaceType)) {
821
+ if (!("jupiterLend" in this.interfaceType)) {
768
822
  throw new Error("Not a Jupiter Lend interface")
769
823
  }
770
824
  if (!this.state.additionalData?.jupiterLendData) {
@@ -774,7 +828,7 @@ export class GenericSySdk {
774
828
  }
775
829
 
776
830
  get kaminoVaultData() {
777
- if (!("kaminoVault" in this.state.account.interfaceType)) {
831
+ if (!("kaminoVault" in this.interfaceType)) {
778
832
  throw new Error("Not a Kamino Vault interface")
779
833
  }
780
834
  if (!this.state.additionalData?.kaminoVaultData) {
@@ -804,11 +858,7 @@ export class GenericSySdk {
804
858
  }))
805
859
  .concat(this.emissions.map((e) => ({ pubkey: e.escrowAccount, isSigner: false, isWritable: false })))
806
860
 
807
- if (
808
- ("fragmetric" in this.state.account.interfaceType ||
809
- "fragmetricSupportedAsset" in this.state.account.interfaceType) &&
810
- signer
811
- ) {
861
+ if (("fragmetric" in this.interfaceType || "fragmetricSupportedAsset" in this.interfaceType) && signer) {
812
862
  // If skipWrap is true, return minimal accounts (skip additional interface-specific accounts)
813
863
  if (this.config.skipWrap) {
814
864
  return accounts
@@ -881,7 +931,7 @@ export class GenericSySdk {
881
931
  )
882
932
  }
883
933
 
884
- if ("jupiterLend" in this.state.account.interfaceType) {
934
+ if ("jupiterLend" in this.interfaceType) {
885
935
  // If skipWrap is true, return minimal accounts (skip additional interface-specific accounts)
886
936
  if (this.config.skipWrap) {
887
937
  return accounts
@@ -1024,7 +1074,7 @@ export class GenericSySdk {
1024
1074
  }
1025
1075
  }
1026
1076
 
1027
- if ("kaminoVault" in this.state.account.interfaceType) {
1077
+ if ("kaminoVault" in this.interfaceType) {
1028
1078
  // If skipWrap is true, return minimal accounts (skip additional interface-specific accounts)
1029
1079
  if (this.config.skipWrap) {
1030
1080
  return accounts
@@ -1155,9 +1205,33 @@ export class GenericSySdk {
1155
1205
  .instruction()
1156
1206
  }
1157
1207
 
1158
- preInstructions() {
1159
- let ixs = []
1160
- if ("jupiterLend" in this.state.account.interfaceType) {
1208
+ private getFragmetricReceiptTokenAccountSetupInstruction(signer?: web3.PublicKey) {
1209
+ const isFragmetric = "fragmetric" in this.interfaceType || "fragmetricSupportedAsset" in this.interfaceType
1210
+ if (!signer || !isFragmetric || this.config.skipWrap) {
1211
+ return null
1212
+ }
1213
+
1214
+ const { receiptTokenMint } = this.fragmetricData
1215
+ const userReceiptTokenAccount = getAssociatedTokenAddressSync(receiptTokenMint, signer, true, TOKEN_2022_PROGRAM_ID)
1216
+
1217
+ return createAssociatedTokenAccountIdempotentInstruction(
1218
+ signer,
1219
+ userReceiptTokenAccount,
1220
+ signer,
1221
+ receiptTokenMint,
1222
+ TOKEN_2022_PROGRAM_ID,
1223
+ )
1224
+ }
1225
+
1226
+ /** Returns interface-specific setup instructions for the signer. */
1227
+ preInstructions(signer?: web3.PublicKey) {
1228
+ const ixs: web3.TransactionInstruction[] = []
1229
+ const receiptTokenAccountSetupIx = this.getFragmetricReceiptTokenAccountSetupInstruction(signer)
1230
+ if (receiptTokenAccountSetupIx) {
1231
+ ixs.push(receiptTokenAccountSetupIx)
1232
+ }
1233
+
1234
+ if ("jupiterLend" in this.interfaceType) {
1161
1235
  const jupiterLendData = this.state.additionalData?.jupiterLendData
1162
1236
  if (!jupiterLendData) {
1163
1237
  throw new Error("Jupiter lend data not loaded")
@@ -1197,7 +1271,7 @@ export class GenericSySdk {
1197
1271
  ixs.push(refreshInstruction)
1198
1272
  }
1199
1273
 
1200
- if ("splStakePool" in this.state.account.interfaceType) {
1274
+ if ("splStakePool" in this.interfaceType) {
1201
1275
  const splStakePoolData = this.state.additionalData?.splStakePoolData
1202
1276
  if (!splStakePoolData) {
1203
1277
  throw new Error("SPL stake pool data not loaded")
@@ -1226,6 +1300,23 @@ export class GenericSySdk {
1226
1300
  return ixs
1227
1301
  }
1228
1302
 
1303
+ /** Returns signer setup and refresh instructions, including those that require live interface state. */
1304
+ async getPreInstructions(cnx: web3.Connection, signer?: web3.PublicKey): Promise<web3.TransactionInstruction[]> {
1305
+ const instructions = this.preInstructions(signer)
1306
+ if (!("exponentTranching" in this.interfaceType)) return instructions
1307
+
1308
+ const marketAddress = this.account.interfaceAccounts[0]
1309
+ if (this.account.interfaceAccounts.length !== 1 || !marketAddress) {
1310
+ throw new Error("Exponent Tranching interface requires exactly one market account")
1311
+ }
1312
+
1313
+ const updateMarket = await buildExponentTranchingUpdateMarketInstruction({
1314
+ connection: cnx,
1315
+ marketAddress,
1316
+ })
1317
+ return [updateMarket, ...instructions]
1318
+ }
1319
+
1229
1320
  getSyState() {
1230
1321
  return this.program.methods
1231
1322
  .getState()
@@ -1304,6 +1395,37 @@ export class GenericSySdk {
1304
1395
  })
1305
1396
  .instruction()
1306
1397
  }
1398
+
1399
+ ixChangeInterfaceType({
1400
+ signer,
1401
+ interfaceAccounts,
1402
+ interfaceType,
1403
+ }: {
1404
+ signer: web3.PublicKey
1405
+ interfaceAccounts: web3.PublicKey[]
1406
+ interfaceType: InterfaceTypeGeneric
1407
+ }) {
1408
+ return this.program.methods
1409
+ .changeInterfaceType(interfaceAccounts, interfaceType)
1410
+ .accountsStrict({
1411
+ adminState: this.adminState,
1412
+ feePayer: signer,
1413
+ meta: this.selfAddress,
1414
+ mintSy: this.mintSy,
1415
+ signer,
1416
+ systemProgram: web3.SystemProgram.programId,
1417
+ tokenSyEscrow: this.tokenSyEscrow,
1418
+ yieldBearingMint: this.vrtMint,
1419
+ })
1420
+ .remainingAccounts(
1421
+ interfaceAccounts.map((pubkey) => ({
1422
+ pubkey,
1423
+ isSigner: false,
1424
+ isWritable: true,
1425
+ })),
1426
+ )
1427
+ .instruction()
1428
+ }
1307
1429
  }
1308
1430
 
1309
1431
  export function initSy(