@exponent-labs/exponent-sdk 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.
Files changed (68) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/build/addressLookupTableUtil.d.ts +12 -0
  3. package/build/addressLookupTableUtil.js +32 -0
  4. package/build/addressLookupTableUtil.js.map +1 -0
  5. package/build/environment.d.ts +10 -0
  6. package/build/environment.js +13 -0
  7. package/build/environment.js.map +1 -0
  8. package/build/events.d.ts +339 -0
  9. package/build/events.js +231 -0
  10. package/build/events.js.map +1 -0
  11. package/build/flavors.d.ts +24 -0
  12. package/build/flavors.js +713 -0
  13. package/build/flavors.js.map +1 -0
  14. package/build/index.d.ts +11 -0
  15. package/build/index.js +45 -0
  16. package/build/index.js.map +1 -0
  17. package/build/lpPosition.d.ts +35 -0
  18. package/build/lpPosition.js +103 -0
  19. package/build/lpPosition.js.map +1 -0
  20. package/build/market.d.ts +567 -0
  21. package/build/market.js +1445 -0
  22. package/build/market.js.map +1 -0
  23. package/build/syPosition.d.ts +6 -0
  24. package/build/syPosition.js +115 -0
  25. package/build/syPosition.js.map +1 -0
  26. package/build/tokenUtil.d.ts +3 -0
  27. package/build/tokenUtil.js +23 -0
  28. package/build/tokenUtil.js.map +1 -0
  29. package/build/utils/altUtil.d.ts +8 -0
  30. package/build/utils/altUtil.js +35 -0
  31. package/build/utils/altUtil.js.map +1 -0
  32. package/build/utils/binSolver.d.ts +1 -0
  33. package/build/utils/binSolver.js +45 -0
  34. package/build/utils/binSolver.js.map +1 -0
  35. package/build/utils/binSolver.test.d.ts +1 -0
  36. package/build/utils/binSolver.test.js +15 -0
  37. package/build/utils/binSolver.test.js.map +1 -0
  38. package/build/utils/index.d.ts +6 -0
  39. package/build/utils/index.js +31 -0
  40. package/build/utils/index.js.map +1 -0
  41. package/build/utils/ix.d.ts +6 -0
  42. package/build/utils/ix.js +3 -0
  43. package/build/utils/ix.js.map +1 -0
  44. package/build/vault.d.ts +289 -0
  45. package/build/vault.js +615 -0
  46. package/build/vault.js.map +1 -0
  47. package/build/ytPosition.d.ts +86 -0
  48. package/build/ytPosition.js +231 -0
  49. package/build/ytPosition.js.map +1 -0
  50. package/jest.config.js +5 -0
  51. package/package.json +42 -0
  52. package/src/addressLookupTableUtil.ts +34 -0
  53. package/src/environment.ts +19 -0
  54. package/src/events.ts +595 -0
  55. package/src/flavors.ts +773 -0
  56. package/src/index.ts +11 -0
  57. package/src/lpPosition.ts +129 -0
  58. package/src/market.ts +2338 -0
  59. package/src/syPosition.ts +151 -0
  60. package/src/tokenUtil.ts +20 -0
  61. package/src/utils/altUtil.ts +47 -0
  62. package/src/utils/binSolver.test.ts +15 -0
  63. package/src/utils/binSolver.ts +44 -0
  64. package/src/utils/index.ts +32 -0
  65. package/src/utils/ix.ts +7 -0
  66. package/src/vault.ts +999 -0
  67. package/src/ytPosition.ts +313 -0
  68. package/tsconfig.json +38 -0
package/src/vault.ts ADDED
@@ -0,0 +1,999 @@
1
+ import { BN, Program, web3, AnchorProvider } from "@coral-xyz/anchor"
2
+ import { ClaimAmount } from "@exponent-labs/exponent-ix"
3
+ import { deserializeEmission, ExponentFetcher, serializeEmission } from "@exponent-labs/exponent-fetcher"
4
+ import { fetchAddressLookupTable, makeCpiAccountMetaLists } from "./addressLookupTableUtil"
5
+ import {
6
+ deserializeFlavorGeneric,
7
+ deserializeFlavorKamino,
8
+ deserializeFlavorMarginfi,
9
+ deserializeFlavorParena,
10
+ deserializeFlavorJitoRestaking,
11
+ makeFlavor,
12
+ makeFlavorGenericSync,
13
+ makeFlavorKaminoSync,
14
+ makeFlavorMarginfiSync,
15
+ makeFlavorPerenaSync,
16
+ makeFlavorJitoRestakingSync,
17
+ serializeFlavorGeneric,
18
+ serializeFlavorJitoRestaking,
19
+ serializeFlavorKamino,
20
+ serializeFlavorMarginfi,
21
+ serializeFlavorParena,
22
+ } from "./flavors"
23
+ import { Environment } from "./environment"
24
+ import { getAssociatedTokenAddressSync, TOKEN_PROGRAM_ID } from "@solana/spl-token"
25
+ import { deserializeSyPosition, makeSyPosition, serializeSyPostion } from "./syPosition"
26
+ import { ExponentCore, IDL } from "@exponent-labs/exponent-idl"
27
+ import { emitEventAuthority, InstructionAccounts, uniqueRemainingAccounts } from "./utils"
28
+ import { EXPONENT_ADMIN_PROGRAM_ID, ExponentAdminPda } from "@exponent-labs/exponent-admin-pda"
29
+ import { ExponentPDA } from "@exponent-labs/exponent-pda"
30
+ import { MyWallet } from "./market"
31
+ import { deserializeCpiAccountsRaw, serializeCpiAccountsRaw } from "@exponent-labs/exponent-ix"
32
+ import { VaultEmission, VaultJson, VaultState, AnchorizedPNum, CpiAccountsRaw } from "@exponent-labs/exponent-types"
33
+ import { extendAddressLookupTable } from "./utils/altUtil"
34
+
35
+ export type CollectTreasuryEmissionKind =
36
+ | { yieldPosition: {} }
37
+ | {
38
+ treasuryEmission: {}
39
+ }
40
+
41
+ export type CollectTreasuryInterestKind =
42
+ | {
43
+ yieldPosition: {}
44
+ }
45
+ | {
46
+ treasuryInterest: {}
47
+ }
48
+
49
+ /** Emission info for SY program */
50
+ interface SyEmission {
51
+ /** Token account that holds emission tokens */
52
+ escrowAccountAddress: web3.PublicKey
53
+
54
+ /** Mint for the emission token */
55
+ mint: web3.PublicKey
56
+
57
+ /** Token program ID for the emission token */
58
+ tokenProgramAddress: web3.PublicKey
59
+
60
+ /** How many emissions have been claimed by SY holders */
61
+ totalClaimed: BN
62
+
63
+ /** How many emissions have been earned by the SY robot over its lifetime */
64
+ lastSeenTotalAccruedEmissions: BN
65
+
66
+ /** Global index for sharing out rewards to SY holders */
67
+ index: AnchorizedPNum
68
+ }
69
+
70
+ export class Vault {
71
+ private coreProgram: Program<ExponentCore>
72
+
73
+ constructor(
74
+ public state: VaultState,
75
+ public selfAddress: web3.PublicKey,
76
+ public env: Environment,
77
+ public connection: web3.Connection,
78
+ ) {
79
+ const mockWallet = new MyWallet(web3.Keypair.generate())
80
+ this.coreProgram = new Program(IDL as ExponentCore, new AnchorProvider(connection, mockWallet))
81
+ }
82
+
83
+ static loadFromJson(v: VaultJson, address: web3.PublicKey, env: Environment, connection: web3.Connection) {
84
+ const state = toVaultState(v)
85
+ return new Vault(state, address, env, connection)
86
+ }
87
+
88
+ static async load(env: Environment, connection: web3.Connection, address: web3.PublicKey) {
89
+ const fetcher = new ExponentFetcher({ connection })
90
+ const v = await fetcher.fetchVault(address)
91
+
92
+ const alt = await fetchAddressLookupTable(connection, v.addressLookupTable)
93
+ const cpiAccounts = makeCpiAccountMetaLists(alt, v.cpiAccounts)
94
+ const flavor = await makeFlavor(fetcher, env, v.syProgram, cpiAccounts)
95
+ const positionInCpiAccounts = cpiAccounts.depositSy[6]
96
+ console.log("positionInCpiAccounts", positionInCpiAccounts.pubkey.toBase58())
97
+ const syPosition = await makeSyPosition(fetcher, flavor, v.syProgram, v.authority)
98
+
99
+ const state: VaultState = {
100
+ ptSupply: v.ptSupply,
101
+ syForPt: v.syForPt,
102
+ startTs: v.startTs,
103
+ durationSeconds: v.duration,
104
+ authority: v.authority,
105
+ addressLookupTable: v.addressLookupTable,
106
+ lastSeenSyExchangeRate: v.lastSeenSyExchangeRate,
107
+ allTimeHighSyExchangeRate: v.allTimeHighSyExchangeRate,
108
+ syProgram: v.syProgram,
109
+ mintPt: v.mintPt,
110
+ mintSy: v.mintSy,
111
+ mintYt: v.mintYt,
112
+ escrowSy: v.escrowSy,
113
+ escrowYt: v.escrowYt,
114
+ yieldPositonAddress: v.yieldPosition,
115
+ cpiAccounts,
116
+ flavor,
117
+ syPosition,
118
+ totalSyInEscrow: v.totalSyInEscrow,
119
+ treasurySyTokenAccount: v.treasurySyTokenAccount,
120
+ interestBpsFee: v.interestBpsFee,
121
+ emissions: v.emissions,
122
+ minOperationSizeStrip: v.minOpSizeStrip,
123
+ minOperationSizeMerge: v.minOpSizeMerge,
124
+ status: v.status,
125
+ finalSyExchangeRate: v.finalSyExchangeRate,
126
+ maxPySupply: v.maxPySupply,
127
+ }
128
+
129
+ return new Vault(state, address, env, connection)
130
+ }
131
+
132
+ async reload(conn: web3.Connection = this.connection) {
133
+ const v = await Vault.load(this.env, conn, this.selfAddress)
134
+ this.state = v.state
135
+ return v
136
+ }
137
+
138
+ toJson(): VaultJson {
139
+ return {
140
+ startTs: this.state.startTs,
141
+ duration: this.state.durationSeconds,
142
+ authority: this.state.authority.toString(),
143
+ syBalance: this.syBalance.toString(),
144
+ addressLookupTable: this.state.addressLookupTable.toString(),
145
+ lastSeenSyExchangeRate: this.state.lastSeenSyExchangeRate,
146
+ selfAddress: this.selfAddress.toString(),
147
+ syProgram: this.state.syProgram.toString(),
148
+ mintPt: this.state.mintPt.toString(),
149
+ mintYt: this.state.mintYt.toString(),
150
+ mintSy: this.state.mintSy.toString(),
151
+ currentSyExchangeRate: this.state.flavor.currentSyExchangeRate,
152
+ startDateString: new Date(this.state.startTs * 1000).toISOString(),
153
+ escrowSy: this.state.escrowSy.toString(),
154
+ escrowYt: this.state.escrowYt.toString(),
155
+ treasurySyTokenAccount: this.state.treasurySyTokenAccount.toString(),
156
+ allTimeHighSyExchangeRate: this.state.allTimeHighSyExchangeRate,
157
+ finalSyExchangeRate: this.state.finalSyExchangeRate,
158
+ ptSupply: this.state.ptSupply.toString(),
159
+ syForPt: this.state.syForPt.toString(),
160
+ totalSyInEscrow: this.state.totalSyInEscrow.toString(),
161
+ cpiAccounts: serializeCpiAccountsRaw(this.state.cpiAccounts),
162
+ status: this.state.status,
163
+ syPosition: serializeSyPostion(this.state.syPosition),
164
+ minOperationSizeStrip: this.state.minOperationSizeStrip.toString(),
165
+ minOperationSizeMerge: this.state.minOperationSizeMerge.toString(),
166
+ yieldPositonAddress: this.state.yieldPositonAddress.toString(),
167
+ interestFeeBps: this.state.interestBpsFee,
168
+ flavor: (() => {
169
+ switch (this.state.flavor.flavor) {
170
+ case "marginfi":
171
+ return serializeFlavorMarginfi(this.state.flavor)
172
+ case "kamino":
173
+ return serializeFlavorKamino(this.state.flavor)
174
+ case "jitoRestaking":
175
+ return serializeFlavorJitoRestaking(this.state.flavor)
176
+ case "perena":
177
+ return serializeFlavorParena(this.state.flavor)
178
+ case "generic":
179
+ return serializeFlavorGeneric(this.state.flavor)
180
+ default:
181
+ throw new Error(`Unknown flavor: ${this.state.flavor}`)
182
+ }
183
+ })(),
184
+ emissions: this.state.emissions.map((e) => serializeEmission(e)),
185
+ maxPySupply: this.state.maxPySupply.toString(),
186
+ }
187
+ }
188
+
189
+ get pda() {
190
+ return new ExponentPDA(this.coreProgram.programId)
191
+ }
192
+
193
+ get syBalance(): bigint {
194
+ return this.state.syPosition.balanceSy
195
+ }
196
+
197
+ get mintSy(): web3.PublicKey {
198
+ return this.state.mintSy
199
+ }
200
+
201
+ getAdminStatePda() {
202
+ return new ExponentAdminPda(EXPONENT_ADMIN_PROGRAM_ID).exponentAdmin()
203
+ }
204
+
205
+ get mintPt(): web3.PublicKey {
206
+ return this.state.mintPt
207
+ }
208
+
209
+ get mintYt(): web3.PublicKey {
210
+ return this.state.mintYt
211
+ }
212
+
213
+ get flavor() {
214
+ return this.state.flavor
215
+ }
216
+
217
+ get cpiAccounts() {
218
+ return this.state.cpiAccounts
219
+ }
220
+
221
+ get authority() {
222
+ return this.state.authority
223
+ }
224
+
225
+ get escrowSy() {
226
+ return this.state.escrowSy
227
+ }
228
+
229
+ get addressLookupTable() {
230
+ return this.state.addressLookupTable
231
+ }
232
+
233
+ get eventAuthority() {
234
+ return emitEventAuthority(this.coreProgram.programId)
235
+ }
236
+
237
+ /** Emission metadata from SY program */
238
+ get syEmissions(): SyEmission[] {
239
+ if (this.flavor.flavor === "marginfi") {
240
+ return this.flavor.mfiSyState.account.emissions.map((e) => ({
241
+ escrowAccountAddress: e.escrowAccount,
242
+ mint: e.mint,
243
+ tokenProgramAddress: e.tokenProgram,
244
+ totalClaimed: e.totalClaimedEmissions,
245
+ lastSeenTotalAccruedEmissions: e.lastSeenTotalAccruedEmissions,
246
+ index: e.index,
247
+ }))
248
+ }
249
+ if (this.flavor.flavor === "kamino") {
250
+ return this.flavor.kaminoSyState.account.emissions.map((e) => ({
251
+ escrowAccountAddress: e.escrowAccount,
252
+ mint: e.mint,
253
+ tokenProgramAddress: e.tokenProgram,
254
+ totalClaimed: e.totalClaimedEmissions,
255
+ lastSeenTotalAccruedEmissions: e.lastSeenTotalAccruedEmissions,
256
+ index: e.index,
257
+ }))
258
+ }
259
+ if (this.flavor.flavor === "jitoRestaking") {
260
+ return this.flavor.jitoSyState.account.emissions.map((e) => ({
261
+ escrowAccountAddress: e.escrowAccount,
262
+ mint: e.mint,
263
+ tokenProgramAddress: e.tokenProgram,
264
+ totalClaimed: e.totalClaimedEmissions,
265
+ lastSeenTotalAccruedEmissions: e.lastSeenTotalAccruedEmissions,
266
+ index: e.index,
267
+ }))
268
+ }
269
+ if (this.flavor.flavor === "perena") {
270
+ return this.flavor.perenaSyState.account.emissions.map((e) => ({
271
+ escrowAccountAddress: e.escrowAccount,
272
+ mint: e.mint,
273
+ tokenProgramAddress: e.tokenProgram,
274
+ totalClaimed: e.totalClaimedEmissions,
275
+ lastSeenTotalAccruedEmissions: e.lastSeenTotalAccruedEmissions,
276
+ index: e.index,
277
+ }))
278
+ }
279
+ if (this.flavor.flavor === "generic") {
280
+ return this.flavor.genericSyState.account.emissions.map((e) => ({
281
+ escrowAccountAddress: e.escrowAccount,
282
+ mint: e.mint,
283
+ tokenProgramAddress: e.tokenProgram,
284
+ totalClaimed: e.totalClaimedEmissions,
285
+ lastSeenTotalAccruedEmissions: e.lastSeenTotalAccruedEmissions,
286
+ index: e.index,
287
+ }))
288
+ }
289
+ throw new Error("Unknown flavor")
290
+ }
291
+
292
+ /** Emissions from the vault */
293
+ get vaultEmissions(): VaultEmission[] {
294
+ return this.state.emissions
295
+ }
296
+
297
+ /** Get the escrow token account addresses for the emissions, in order */
298
+ get vaultEmissionTokenAccounts(): web3.PublicKey[] {
299
+ return this.vaultEmissions.map((e) => e.tokenAccount)
300
+ }
301
+
302
+ get currentSyExchangeRate(): number {
303
+ return this.flavor.currentSyExchangeRate
304
+ }
305
+
306
+ get escrowYt(): web3.PublicKey {
307
+ return this.state.escrowYt
308
+ }
309
+
310
+ /** UNIX Timestamp (seconds) at which the vault expires */
311
+ get expirationTimestamp(): number {
312
+ return this.state.startTs + this.state.durationSeconds
313
+ }
314
+
315
+ /** Date at which the vault expires */
316
+ get expirationDate(): Date {
317
+ return new Date(this.expirationTimestamp * 1000)
318
+ }
319
+
320
+ /** Collect an emission earned with a YT position */
321
+ ixCollectEmission({
322
+ owner,
323
+ emissionIndex,
324
+ amount,
325
+ emissionDst,
326
+ }: {
327
+ owner: web3.PublicKey
328
+ emissionIndex: number
329
+ amount: ClaimAmount
330
+ emissionDst?: web3.PublicKey
331
+ }) {
332
+ const accounts = this.collectEmissionAccounts({ emissionIndex, owner, emissionDst })
333
+
334
+ return this.coreProgram.methods
335
+ .collectEmission(emissionIndex, amount)
336
+ .accountsStrict(accounts.mainAccounts)
337
+ .remainingAccounts(accounts.remainingAccounts)
338
+ .instruction()
339
+ }
340
+
341
+ /** Strip SY into PT & YT */
342
+ ixStrip({
343
+ syIn,
344
+ depositor,
345
+ ptDst,
346
+ ytDst,
347
+ sySrc,
348
+ }: {
349
+ syIn: bigint
350
+ depositor: web3.PublicKey
351
+ ptDst?: web3.PublicKey
352
+ ytDst?: web3.PublicKey
353
+ sySrc?: web3.PublicKey
354
+ }): Promise<web3.TransactionInstruction> {
355
+ const { mainAccounts, remainingAccounts } = this.stripAccounts({ depositor, ptDst, ytDst, sySrc })
356
+ return this.coreProgram.methods
357
+ .strip(new BN(syIn.toString()))
358
+ .accountsStrict(mainAccounts)
359
+ .remainingAccounts(remainingAccounts)
360
+ .instruction()
361
+ }
362
+
363
+ async ixStripFromBase({
364
+ owner,
365
+ amountBase,
366
+ baseSrc,
367
+ sySrc,
368
+ ptDst,
369
+ ytDst,
370
+ }: {
371
+ owner: web3.PublicKey
372
+ amountBase: bigint
373
+ baseSrc?: web3.PublicKey
374
+ sySrc?: web3.PublicKey
375
+ ptDst?: web3.PublicKey
376
+ ytDst?: web3.PublicKey
377
+ }): Promise<web3.TransactionInstruction> {
378
+ baseSrc = baseSrc || getAssociatedTokenAddressSync(this.mintPt, owner, true, TOKEN_PROGRAM_ID)
379
+ sySrc = sySrc || getAssociatedTokenAddressSync(this.mintSy, owner, true, TOKEN_PROGRAM_ID)
380
+ ptDst = ptDst || getAssociatedTokenAddressSync(this.mintPt, owner, true, TOKEN_PROGRAM_ID)
381
+ ytDst = ytDst || getAssociatedTokenAddressSync(this.mintYt, owner, true, TOKEN_PROGRAM_ID)
382
+
383
+ const mintSyIx = await this.state.flavor.ixMintSy({
384
+ amountBase: "0",
385
+ depositor: owner,
386
+ depositorBaseTokenAccount: baseSrc,
387
+ depositorSyTokenAccount: sySrc,
388
+ })
389
+
390
+ const mintSyRemAccounts = mintSyIx.keys
391
+
392
+ const remainingAccounts = uniqueRemainingAccounts([...this.cpiAccounts.depositSy])
393
+
394
+ const userYieldPosition = this.pda.yieldPosition({ owner, vault: this.selfAddress })
395
+
396
+ return this.coreProgram.methods
397
+ .wrapperStrip(new BN(amountBase.toString()), mintSyRemAccounts.length)
398
+ .accountsStrict({
399
+ depositor: owner,
400
+ authority: this.authority,
401
+ vault: this.selfAddress,
402
+ mintPt: this.mintPt,
403
+ mintYt: this.mintYt,
404
+ tokenProgram: TOKEN_PROGRAM_ID,
405
+ syProgram: this.state.syProgram,
406
+ tokenSyDepositor: sySrc,
407
+ tokenYtDepositor: ytDst,
408
+ escrowSy: this.escrowSy,
409
+ tokenPtDepositor: ptDst,
410
+ systemProgram: web3.SystemProgram.programId,
411
+ vaultAddressLookupTable: this.addressLookupTable,
412
+ vaultRobotYieldPosition: this.state.yieldPositonAddress,
413
+ eventAuthority: this.eventAuthority,
414
+ program: this.coreProgram.programId,
415
+ escrowYt: this.escrowYt,
416
+ userYieldPosition,
417
+ })
418
+ .remainingAccounts(mintSyRemAccounts.concat(remainingAccounts))
419
+ .instruction()
420
+ }
421
+
422
+ async ixMergeToBase({
423
+ owner,
424
+ amountPy,
425
+ ptSrc,
426
+ ytSrc,
427
+ syDst,
428
+ baseDst,
429
+ }: {
430
+ owner: web3.PublicKey
431
+ amountPy: bigint
432
+ ptSrc?: web3.PublicKey
433
+ ytSrc?: web3.PublicKey
434
+ syDst?: web3.PublicKey
435
+ baseDst?: web3.PublicKey
436
+ }): Promise<web3.TransactionInstruction> {
437
+ baseDst = baseDst || getAssociatedTokenAddressSync(this.mintPt, owner, true, TOKEN_PROGRAM_ID)
438
+ syDst = syDst || getAssociatedTokenAddressSync(this.mintSy, owner, true, TOKEN_PROGRAM_ID)
439
+ ptSrc = ptSrc || getAssociatedTokenAddressSync(this.mintPt, owner, true, TOKEN_PROGRAM_ID)
440
+ ytSrc = ytSrc || getAssociatedTokenAddressSync(this.mintYt, owner, true, TOKEN_PROGRAM_ID)
441
+
442
+ const redeemSyIx = await this.state.flavor.ixRedeemSy({
443
+ amountSy: "0",
444
+ redeemer: owner,
445
+ redeemerBaseTokenAccount: baseDst,
446
+ redeemerSyTokenAccount: syDst,
447
+ })
448
+
449
+ const mintSyRemAccounts = redeemSyIx.keys
450
+
451
+ const remainingAccounts = uniqueRemainingAccounts([...this.cpiAccounts.withdrawSy, ...this.cpiAccounts.getSyState])
452
+
453
+ return this.coreProgram.methods
454
+ .wrapperMerge(new BN(amountPy.toString()), mintSyRemAccounts.length)
455
+ .accountsStrict({
456
+ merger: owner,
457
+ authority: this.authority,
458
+ vault: this.selfAddress,
459
+ mintPt: this.mintPt,
460
+ mintYt: this.mintYt,
461
+ tokenProgram: TOKEN_PROGRAM_ID,
462
+ syProgram: this.state.syProgram,
463
+ tokenSyMerger: syDst,
464
+ tokenYtMerger: ytSrc,
465
+ tokenPtMerger: ptSrc,
466
+ escrowSy: this.escrowSy,
467
+ systemProgram: web3.SystemProgram.programId,
468
+ vaultAddressLookupTable: this.addressLookupTable,
469
+ vaultRobotYieldPosition: this.state.yieldPositonAddress,
470
+ eventAuthority: this.eventAuthority,
471
+ program: this.coreProgram.programId,
472
+ })
473
+ .remainingAccounts(mintSyRemAccounts.concat(remainingAccounts))
474
+ .instruction()
475
+ }
476
+
477
+ /** Merge PT & YT into SY */
478
+ ixMerge({
479
+ pyIn,
480
+ depositor,
481
+ ptSrc,
482
+ ytSrc,
483
+ syDst,
484
+ }: {
485
+ pyIn: bigint
486
+ depositor: web3.PublicKey
487
+ ptSrc?: web3.PublicKey
488
+ ytSrc?: web3.PublicKey
489
+ syDst?: web3.PublicKey
490
+ }): Promise<web3.TransactionInstruction> {
491
+ const accounts = this.mergeAccounts({ owner: depositor, ptSrc, ytSrc, syDst })
492
+
493
+ return this.coreProgram.methods
494
+ .merge(new BN(pyIn.toString()))
495
+ .accountsStrict(accounts.mainAccounts)
496
+ .remainingAccounts(accounts.remainingAccounts)
497
+ .instruction()
498
+ }
499
+
500
+ /** Initialize the yield position for a vault */
501
+ ixInitializeYieldPosition({ owner }: { owner: web3.PublicKey }): Promise<web3.TransactionInstruction> {
502
+ const accounts = this.initializeYieldPositionAccounts({ owner })
503
+
504
+ return this.coreProgram.methods.initializeYieldPosition().accountsStrict(accounts.mainAccounts).instruction()
505
+ }
506
+
507
+ /** Collect interest (as SY) from a yield position */
508
+ ixCollectInterest({ owner, amount, syDst }: { owner: web3.PublicKey; amount: ClaimAmount; syDst?: web3.PublicKey }) {
509
+ const accounts = this.collectInterestAccounts({ owner, syDst })
510
+ return this.coreProgram.methods
511
+ .collectInterest(amount)
512
+ .accountsStrict(accounts.mainAccounts)
513
+ .remainingAccounts(accounts.remainingAccounts)
514
+ .instruction()
515
+ }
516
+
517
+ /** Administrative action to configure a vault */
518
+ /** Administrative action to configure a vault */
519
+ ixConfigureVault({
520
+ signer,
521
+ action,
522
+ }: {
523
+ signer: web3.PublicKey
524
+ action: AdminAction
525
+ }): Promise<web3.TransactionInstruction> {
526
+ const accounts = this.configureVaultAccounts({ signer })
527
+
528
+ return this.coreProgram.methods
529
+ .modifyVaultSetting(action)
530
+ .accountsStrict(accounts.mainAccounts)
531
+ .remainingAccounts(accounts.remainingAccounts)
532
+ .instruction()
533
+ }
534
+
535
+ collectEmissionAccounts({
536
+ emissionIndex,
537
+ owner,
538
+ emissionDst,
539
+ }: {
540
+ emissionIndex: number
541
+ owner: web3.PublicKey
542
+ emissionDst?: web3.PublicKey
543
+ }): InstructionAccounts<CollectEmissionAccounts> {
544
+ const syE = this.syEmissions[emissionIndex]
545
+ if (!syE) {
546
+ throw new Error("Emission index out of bounds")
547
+ }
548
+
549
+ const vaultE = this.vaultEmissions[emissionIndex]
550
+ if (!vaultE) {
551
+ throw new Error("Emission index out of bounds")
552
+ }
553
+
554
+ const tokenProgram = syE.tokenProgramAddress
555
+
556
+ const emissionEscrow = vaultE.tokenAccount
557
+ const treasuryEmissionTokenAccount = vaultE.treasuryTokenAccount
558
+ const position = this.pda.yieldPosition({ owner, vault: this.selfAddress })
559
+ emissionDst = emissionDst || getAssociatedTokenAddressSync(syE.mint, owner, true, syE.tokenProgramAddress)
560
+ const remainingAccounts = uniqueRemainingAccounts([...this.cpiAccounts.claimEmission[emissionIndex]])
561
+
562
+ return {
563
+ mainAccounts: {
564
+ owner,
565
+ addressLookupTable: this.addressLookupTable,
566
+ vault: this.selfAddress,
567
+ authority: this.authority,
568
+ emissionDst,
569
+ emissionEscrow,
570
+ treasuryEmissionTokenAccount,
571
+ syProgram: this.state.syProgram,
572
+ position,
573
+ tokenProgram,
574
+ eventAuthority: this.eventAuthority,
575
+ program: this.coreProgram.programId,
576
+ },
577
+ remainingAccounts,
578
+ }
579
+ }
580
+
581
+ stripAccounts({
582
+ depositor,
583
+ ptDst,
584
+ ytDst,
585
+ sySrc,
586
+ }: {
587
+ depositor: web3.PublicKey
588
+ ptDst?: web3.PublicKey
589
+ ytDst?: web3.PublicKey
590
+ sySrc?: web3.PublicKey
591
+ }): InstructionAccounts<StripAccounts> {
592
+ ptDst = ptDst || getAssociatedTokenAddressSync(this.mintPt, depositor, true, TOKEN_PROGRAM_ID)
593
+ ytDst = ytDst || getAssociatedTokenAddressSync(this.mintYt, depositor, true, TOKEN_PROGRAM_ID)
594
+ sySrc = sySrc || getAssociatedTokenAddressSync(this.mintSy, depositor, true, TOKEN_PROGRAM_ID)
595
+
596
+ console.log("sysrc account: ", sySrc.toBase58())
597
+
598
+ const remainingAccounts = uniqueRemainingAccounts([...this.cpiAccounts.depositSy])
599
+
600
+ return {
601
+ mainAccounts: {
602
+ depositor,
603
+ authority: this.authority,
604
+ vault: this.selfAddress,
605
+ escrowSy: this.escrowSy,
606
+ ytDst,
607
+ ptDst,
608
+ sySrc,
609
+ mintYt: this.mintYt,
610
+ mintPt: this.mintPt,
611
+ tokenProgram: TOKEN_PROGRAM_ID,
612
+ addressLookupTable: this.addressLookupTable,
613
+ syProgram: this.state.syProgram,
614
+ yieldPosition: this.state.yieldPositonAddress,
615
+ eventAuthority: this.eventAuthority,
616
+ program: this.coreProgram.programId,
617
+ },
618
+ remainingAccounts,
619
+ }
620
+ }
621
+
622
+ ixCollectTreasuryInterest({
623
+ signer,
624
+ syDst,
625
+ amount,
626
+ kind,
627
+ }: {
628
+ signer: web3.PublicKey
629
+ syDst: web3.PublicKey
630
+ amount: ClaimAmount
631
+ kind: CollectTreasuryInterestKind
632
+ }): Promise<web3.TransactionInstruction> {
633
+ return this.coreProgram.methods
634
+ .collectTreasuryInterest(amount, kind)
635
+ .accountsStrict({
636
+ addressLookupTable: this.addressLookupTable,
637
+ signer,
638
+ vault: this.selfAddress,
639
+ admin: this.getAdminStatePda(),
640
+ authority: this.authority,
641
+ escrowSy: this.escrowSy,
642
+ syProgram: this.state.syProgram,
643
+ tokenProgram: TOKEN_PROGRAM_ID,
644
+ yieldPosition: this.state.yieldPositonAddress,
645
+ syDst,
646
+ })
647
+ .remainingAccounts(this.cpiAccounts.withdrawSy)
648
+ .instruction()
649
+ }
650
+
651
+ ixCollectTreasuryEmission({
652
+ index,
653
+ amount,
654
+ emissionDst,
655
+ signer,
656
+ kind,
657
+ }: {
658
+ index: number
659
+ amount: ClaimAmount
660
+ emissionDst: web3.PublicKey
661
+ signer: web3.PublicKey
662
+ kind: CollectTreasuryEmissionKind
663
+ }): Promise<web3.TransactionInstruction> {
664
+ return this.coreProgram.methods
665
+ .collectTreasuryEmission(index, amount, kind)
666
+ .accountsStrict({
667
+ addressLookupTable: this.addressLookupTable,
668
+ vault: this.selfAddress,
669
+ admin: this.getAdminStatePda(),
670
+ authority: this.authority,
671
+ emissionDst,
672
+ emissionEscrow: this.state.emissions[index].tokenAccount,
673
+ signer,
674
+ syProgram: this.state.syProgram,
675
+ tokenProgram: this.syEmissions[index].tokenProgramAddress,
676
+ yieldPosition: this.state.yieldPositonAddress,
677
+ })
678
+ .remainingAccounts(this.cpiAccounts.claimEmission[index])
679
+ .instruction()
680
+ }
681
+
682
+ async ixAddStandaloneEmission({
683
+ signer,
684
+ emissionMint,
685
+ emissionTokenProgram = TOKEN_PROGRAM_ID,
686
+ treasuryTokenAccountEmission,
687
+ treasuryFeeBps,
688
+ altAddresses,
689
+ cpiAccounts,
690
+ }: {
691
+ signer: web3.PublicKey
692
+ emissionMint: web3.PublicKey
693
+ emissionTokenProgram?: web3.PublicKey
694
+ treasuryTokenAccountEmission: web3.PublicKey
695
+ treasuryFeeBps: number
696
+ altAddresses: web3.PublicKey[]
697
+ cpiAccounts: CpiAccountsRaw
698
+ }) {
699
+ const altUtil = extendAddressLookupTable(altAddresses, cpiAccounts)
700
+
701
+ const tokenEmission = getAssociatedTokenAddressSync(emissionMint, this.authority, true, emissionTokenProgram)
702
+ const ix = await this.coreProgram.methods
703
+ .addEmission(altUtil.cpiAccountIndexes, treasuryFeeBps)
704
+ .accountsStrict({
705
+ vault: this.selfAddress,
706
+ addressLookupTable: this.addressLookupTable,
707
+ admin: this.getAdminStatePda(),
708
+ authority: signer,
709
+ syProgram: this.state.syProgram,
710
+ yieldPosition: this.state.yieldPositonAddress,
711
+ robotTokenAccount: tokenEmission,
712
+ treasuryTokenAccount: treasuryTokenAccountEmission,
713
+ feePayer: signer,
714
+ systemProgram: web3.SystemProgram.programId,
715
+ })
716
+ .remainingAccounts(
717
+ this.cpiAccounts.depositSy.concat(
718
+ altUtil.addressLookupTableExtension.map((pubkey) => ({
719
+ pubkey,
720
+ isSigner: false,
721
+ isWritable: false,
722
+ })),
723
+ ),
724
+ )
725
+ .instruction()
726
+
727
+ return {
728
+ extendAddressLookupTableExtensionAccounts: altUtil.addressLookupTableExtension,
729
+ ix,
730
+ }
731
+ }
732
+
733
+ collectInterestAccounts({
734
+ owner,
735
+ syDst,
736
+ }: {
737
+ owner: web3.PublicKey
738
+ syDst?: web3.PublicKey
739
+ }): InstructionAccounts<CollectInterestAccounts> {
740
+ syDst = syDst || getAssociatedTokenAddressSync(this.mintSy, owner, true, TOKEN_PROGRAM_ID)
741
+ const yieldPosition = this.pda.yieldPosition({ owner, vault: this.selfAddress })
742
+ const remainingAccounts = [...this.cpiAccounts.withdrawSy]
743
+
744
+ return {
745
+ mainAccounts: {
746
+ owner,
747
+ vault: this.selfAddress,
748
+ authority: this.authority,
749
+ escrowSy: this.escrowSy,
750
+ syProgram: this.state.syProgram,
751
+ tokenProgram: TOKEN_PROGRAM_ID,
752
+ yieldPosition,
753
+ addressLookupTable: this.addressLookupTable,
754
+ treasurySyTokenAccount: this.state.treasurySyTokenAccount,
755
+ tokenSyDst: syDst,
756
+ eventAuthority: this.eventAuthority,
757
+ program: this.coreProgram.programId,
758
+ },
759
+ remainingAccounts,
760
+ }
761
+ }
762
+
763
+ /** Construct accounts for Merge instruction */
764
+ mergeAccounts({
765
+ owner,
766
+ ptSrc,
767
+ ytSrc,
768
+ syDst,
769
+ }: {
770
+ owner: web3.PublicKey
771
+ ptSrc?: web3.PublicKey
772
+ ytSrc?: web3.PublicKey
773
+ syDst?: web3.PublicKey
774
+ }): InstructionAccounts<MergeAccounts> {
775
+ ptSrc = ptSrc || getAssociatedTokenAddressSync(this.mintPt, owner, true, TOKEN_PROGRAM_ID)
776
+ ytSrc = ytSrc || getAssociatedTokenAddressSync(this.mintYt, owner, true, TOKEN_PROGRAM_ID)
777
+ syDst = syDst || getAssociatedTokenAddressSync(this.mintSy, owner, true, TOKEN_PROGRAM_ID)
778
+ const remainingAccounts = [...this.cpiAccounts.getSyState, ...this.cpiAccounts.withdrawSy]
779
+ return {
780
+ mainAccounts: {
781
+ owner,
782
+ authority: this.authority,
783
+ vault: this.selfAddress,
784
+ escrowSy: this.escrowSy,
785
+ ytSrc,
786
+ ptSrc,
787
+ syDst,
788
+ mintPt: this.mintPt,
789
+ mintYt: this.mintYt,
790
+ addressLookupTable: this.addressLookupTable,
791
+ syProgram: this.state.syProgram,
792
+ tokenProgram: TOKEN_PROGRAM_ID,
793
+ yieldPosition: this.state.yieldPositonAddress,
794
+ eventAuthority: this.eventAuthority,
795
+ program: this.coreProgram.programId,
796
+ },
797
+ remainingAccounts,
798
+ }
799
+ }
800
+
801
+ initializeYieldPositionAccounts({
802
+ owner,
803
+ }: {
804
+ owner: web3.PublicKey
805
+ }): InstructionAccounts<InitializeYieldPositionAccounts> {
806
+ const yieldPosition = this.pda.yieldPosition({ owner, vault: this.selfAddress })
807
+ return {
808
+ mainAccounts: {
809
+ owner,
810
+ vault: this.selfAddress,
811
+ yieldPosition,
812
+ systemProgram: web3.SystemProgram.programId,
813
+ eventAuthority: this.eventAuthority,
814
+ program: this.coreProgram.programId,
815
+ },
816
+ remainingAccounts: [],
817
+ }
818
+ }
819
+
820
+ /** Construct accounts for Merge instruction */
821
+ configureVaultAccounts({ signer }: { signer: web3.PublicKey }): InstructionAccounts<ConfigureVaultAccounts> {
822
+ return {
823
+ mainAccounts: {
824
+ signer,
825
+ vault: this.selfAddress,
826
+ adminState: this.getAdminStatePda(),
827
+ systemProgram: web3.SystemProgram.programId,
828
+ },
829
+ remainingAccounts: [],
830
+ }
831
+ }
832
+ }
833
+
834
+ function toVaultState(vault: VaultJson): VaultState {
835
+ return {
836
+ ptSupply: BigInt(vault.ptSupply),
837
+ syForPt: BigInt(vault.syForPt),
838
+ startTs: vault.startTs,
839
+ durationSeconds: vault.duration,
840
+ authority: new web3.PublicKey(vault.authority),
841
+ addressLookupTable: new web3.PublicKey(vault.addressLookupTable),
842
+ lastSeenSyExchangeRate: vault.lastSeenSyExchangeRate,
843
+ allTimeHighSyExchangeRate: vault.allTimeHighSyExchangeRate,
844
+ escrowSy: new web3.PublicKey(vault.escrowSy),
845
+ escrowYt: new web3.PublicKey(vault.escrowYt),
846
+ syProgram: new web3.PublicKey(vault.syProgram),
847
+ syPosition: deserializeSyPosition(vault.syPosition),
848
+ mintPt: new web3.PublicKey(vault.mintPt),
849
+ status: vault.status,
850
+ minOperationSizeStrip: BigInt(vault.minOperationSizeStrip),
851
+ minOperationSizeMerge: BigInt(vault.minOperationSizeMerge),
852
+ mintYt: new web3.PublicKey(vault.mintYt),
853
+ mintSy: new web3.PublicKey(vault.mintSy),
854
+ yieldPositonAddress: new web3.PublicKey(vault.yieldPositonAddress),
855
+ treasurySyTokenAccount: new web3.PublicKey(vault.treasurySyTokenAccount),
856
+ interestBpsFee: vault.interestFeeBps,
857
+ totalSyInEscrow: BigInt(vault.totalSyInEscrow),
858
+ cpiAccounts: deserializeCpiAccountsRaw(vault.cpiAccounts),
859
+ finalSyExchangeRate: vault.finalSyExchangeRate,
860
+ flavor: (() => {
861
+ switch (vault.flavor.flavor) {
862
+ case "marginfi":
863
+ return makeFlavorMarginfiSync(deserializeFlavorMarginfi(vault.flavor))
864
+ case "kamino":
865
+ return makeFlavorKaminoSync(deserializeFlavorKamino(vault.flavor))
866
+ case "jitoRestaking":
867
+ return makeFlavorJitoRestakingSync(deserializeFlavorJitoRestaking(vault.flavor))
868
+ case "perena":
869
+ return makeFlavorPerenaSync(deserializeFlavorParena(vault.flavor))
870
+ case "generic":
871
+ return makeFlavorGenericSync(deserializeFlavorGeneric(vault.flavor))
872
+ default:
873
+ throw new Error(`Unknown flavor: ${vault.flavor}`)
874
+ }
875
+ })(),
876
+ emissions: vault.emissions.map((e) => deserializeEmission(e)),
877
+ maxPySupply: BigInt(vault.maxPySupply),
878
+ }
879
+ }
880
+
881
+ export interface CollectInterestAccounts {
882
+ vault: web3.PublicKey
883
+ owner: web3.PublicKey
884
+ authority: web3.PublicKey
885
+ escrowSy: web3.PublicKey
886
+ tokenSyDst: web3.PublicKey
887
+ tokenProgram: web3.PublicKey
888
+ yieldPosition: web3.PublicKey
889
+ addressLookupTable: web3.PublicKey
890
+ syProgram: web3.PublicKey
891
+ treasurySyTokenAccount: web3.PublicKey
892
+ eventAuthority: web3.PublicKey
893
+ program: web3.PublicKey
894
+ }
895
+
896
+ export interface CollectEmissionAccounts {
897
+ owner: web3.PublicKey
898
+ vault: web3.PublicKey
899
+ position: web3.PublicKey
900
+ syProgram: web3.PublicKey
901
+ addressLookupTable: web3.PublicKey
902
+ authority: web3.PublicKey
903
+ emissionEscrow: web3.PublicKey
904
+ emissionDst: web3.PublicKey
905
+ treasuryEmissionTokenAccount: web3.PublicKey
906
+ tokenProgram: web3.PublicKey
907
+ eventAuthority: web3.PublicKey
908
+ program: web3.PublicKey
909
+ }
910
+
911
+ /** Accounts used by Merge */
912
+ export interface MergeAccounts {
913
+ owner: web3.PublicKey
914
+ authority: web3.PublicKey
915
+ vault: web3.PublicKey
916
+ escrowSy: web3.PublicKey
917
+ ytSrc: web3.PublicKey
918
+ ptSrc: web3.PublicKey
919
+ syDst: web3.PublicKey
920
+ mintPt: web3.PublicKey
921
+ mintYt: web3.PublicKey
922
+ addressLookupTable: web3.PublicKey
923
+ syProgram: web3.PublicKey
924
+ tokenProgram: web3.PublicKey
925
+ yieldPosition: web3.PublicKey
926
+ eventAuthority: web3.PublicKey
927
+ program: web3.PublicKey
928
+ }
929
+
930
+ /** Accounts used by Strip */
931
+ export interface StripAccounts {
932
+ depositor: web3.PublicKey
933
+ authority: web3.PublicKey
934
+ vault: web3.PublicKey
935
+ sySrc: web3.PublicKey
936
+ escrowSy: web3.PublicKey
937
+ ytDst: web3.PublicKey
938
+ ptDst: web3.PublicKey
939
+ mintYt: web3.PublicKey
940
+ mintPt: web3.PublicKey
941
+ tokenProgram: web3.PublicKey
942
+ addressLookupTable: web3.PublicKey
943
+ syProgram: web3.PublicKey
944
+ yieldPosition: web3.PublicKey
945
+ eventAuthority: web3.PublicKey
946
+ program: web3.PublicKey
947
+ }
948
+
949
+ /** Accounts used by Initialize Yield Position */
950
+ export interface InitializeYieldPositionAccounts {
951
+ owner: web3.PublicKey
952
+ vault: web3.PublicKey
953
+ yieldPosition: web3.PublicKey
954
+ systemProgram: web3.PublicKey
955
+ eventAuthority: web3.PublicKey
956
+ program: web3.PublicKey
957
+ }
958
+
959
+ /** Accounts used by Configure Vault */
960
+ export interface ConfigureVaultAccounts {
961
+ vault: web3.PublicKey
962
+ signer: web3.PublicKey
963
+ adminState: web3.PublicKey
964
+ systemProgram: web3.PublicKey
965
+ }
966
+
967
+ export type AdminAction =
968
+ | { setVaultStatus: [number] }
969
+ | { changeVaultBpsFee: [number] }
970
+ | { changeVaultTreasuryTokenAccount: [web3.PublicKey] }
971
+ | {
972
+ changeEmissionTreasuryTokenAccount: {
973
+ emissionIndex: number
974
+ newTokenAccount: web3.PublicKey
975
+ }
976
+ }
977
+ | {
978
+ changeMinOperationSize: {
979
+ isStrip: boolean
980
+ newSize: BN
981
+ }
982
+ }
983
+ | {
984
+ changeEmissionBpsFee: {
985
+ emissionIndex: number
986
+ newFeeBps: number
987
+ }
988
+ }
989
+ | {
990
+ changeClaimLimits: {
991
+ maxClaimAmountPerWindow: BN
992
+ claimWindowDurationSeconds: number
993
+ }
994
+ }
995
+ | {
996
+ changeMaxPySupply: {
997
+ newMaxPySupply: BN
998
+ }
999
+ }