@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/market.ts ADDED
@@ -0,0 +1,2338 @@
1
+ import { AnchorProvider, Program, Wallet, web3 } from "@coral-xyz/anchor"
2
+ import { ExponentFetcher, LiquidityNetBalanceLimits, LpFarm } from "@exponent-labs/exponent-fetcher"
3
+ import { ClaimAmount, ExponentIx } from "@exponent-labs/exponent-ix"
4
+ import {
5
+ createAssociatedTokenAccountIdempotentInstruction,
6
+ getAssociatedTokenAddressSync,
7
+ TOKEN_PROGRAM_ID,
8
+ } from "@solana/spl-token"
9
+ import { fetchAddressLookupTable, makeCpiAccountMetaLists } from "./addressLookupTableUtil"
10
+ import { Environment } from "./environment"
11
+ import { getMultipleMintSupply } from "./tokenUtil"
12
+ import {
13
+ makeFlavorMarginfiSync,
14
+ makeFlavorKaminoSync,
15
+ makeFlavorJitoRestakingSync,
16
+ makeFlavorPerenaSync,
17
+ makeFlavorGenericSync,
18
+ } from "./flavors"
19
+ import { ExponentCore, IDL } from "@exponent-labs/exponent-idl"
20
+ import { BN } from "@coral-xyz/anchor"
21
+ import { ExponentPDA } from "@exponent-labs/exponent-pda"
22
+ import { Vault } from "./vault"
23
+ import { LiquidityAdd, lpOutForTokensIn, MarketCalculator } from "@exponent-labs/market-math"
24
+ import { emitEventAuthority, getExponentAdminStatePda, uniqueRemainingAccounts } from "./utils"
25
+ import { CpiAccountsRaw, Flavor, SyPosition } from "@exponent-labs/exponent-types"
26
+ import { PreciseNumber } from "@exponent-labs/precise-number"
27
+ import { AnchorizedPNum } from "@exponent-labs/exponent-types"
28
+ import { extendAddressLookupTable } from "./utils/altUtil"
29
+ import { makeSyPosition } from "./syPosition"
30
+ import { serializeCpiAccountsRaw } from "@exponent-labs/exponent-ix"
31
+ import { CpiAccountsRawJson } from "@exponent-labs/exponent-types"
32
+
33
+ export { LiquidityAdd }
34
+
35
+ const SECONDS_PER_YEAR = 365 * 24 * 60 * 60
36
+
37
+ interface Emission {
38
+ /** Token account that holds emission tokens */
39
+ escrowAccountAddress: web3.PublicKey
40
+
41
+ /** Mint for the emission token */
42
+ mint: web3.PublicKey
43
+
44
+ /** Token program ID for the emission token */
45
+ tokenProgramAddress: web3.PublicKey
46
+
47
+ /** How many emissions have been claimed by SY holders */
48
+ totalClaimed: bigint
49
+
50
+ /** How many emissions have been earned by the SY robot over its lifetime */
51
+ lastSeenTotalAccruedEmissions: bigint
52
+
53
+ /** Global index for sharing out rewards to SY holders */
54
+ index: AnchorizedPNum
55
+ }
56
+
57
+ export type MarketAdminAction =
58
+ | { setStatus: [number] }
59
+ | { setMaxLpSupply: [BN] }
60
+ | { changeLnFeeRateRoot: [number] }
61
+ | { changeRateScalarRoot: [number] }
62
+ | {
63
+ changeLiquidityNetBalanceLimits: {
64
+ maxNetBalanceChangeNegativePercentage: number
65
+ maxNetBalanceChangePositivePercentage: number
66
+ windowDurationSeconds: number
67
+ }
68
+ }
69
+ | { removeMarketEmission: [number] }
70
+
71
+ type MarketArgs = {
72
+ ptBalance: bigint
73
+ syBalance: bigint
74
+ lpSupply: bigint
75
+ sySupply: bigint
76
+ lpEscrowAmount: bigint
77
+ maxLpSupply: bigint
78
+ rateScalarRoot: number
79
+ lnFeeRateRoot: number
80
+ lastLnImpliedRate: number
81
+ expirationTs: number
82
+ addressLookupTable: web3.PublicKey
83
+ mintSy: web3.PublicKey
84
+ mintPt: web3.PublicKey
85
+ vault: Vault
86
+ mintLp: web3.PublicKey
87
+ tokenPtEscrow: web3.PublicKey
88
+ tokenSyEscrow: web3.PublicKey
89
+ tokenLpEscrow: web3.PublicKey
90
+ syProgram: web3.PublicKey
91
+ statusFlags: number
92
+ cpiAccounts: CpiAccountsRaw
93
+ feeTreasurySyBps: number
94
+ tokenFeeTreasurySy: web3.PublicKey
95
+ isCurrentFlashSwap: boolean
96
+ lpFarm: LpFarm
97
+ flavor: Flavor
98
+ liquidityNetBalanceLimits: LiquidityNetBalanceLimits
99
+ syPosition: SyPosition
100
+ emissions: {
101
+ trackers: {
102
+ tokenEscrow: web3.PublicKey
103
+ lpShareIndex: number
104
+ lastSeenStaged: number
105
+ }[]
106
+ }
107
+ }
108
+
109
+ export class MyWallet implements Wallet {
110
+ constructor(readonly payer: web3.Keypair) {
111
+ this.payer = payer
112
+ }
113
+
114
+ async signTransaction<T extends web3.Transaction | web3.VersionedTransaction>(tx: T): Promise<T> {
115
+ if (tx instanceof web3.Transaction) {
116
+ tx.partialSign(this.payer)
117
+ } else {
118
+ tx.sign([this.payer])
119
+ }
120
+ return tx
121
+ }
122
+
123
+ async signAllTransactions<T extends web3.Transaction | web3.VersionedTransaction>(txs: T[]): Promise<T[]> {
124
+ return txs.map((t) => {
125
+ if (t instanceof web3.Transaction) {
126
+ t.partialSign(this.payer)
127
+ } else if (t instanceof web3.VersionedTransaction) {
128
+ t.sign([this.payer])
129
+ }
130
+ return t
131
+ })
132
+ }
133
+
134
+ get publicKey(): web3.PublicKey {
135
+ return this.payer.publicKey
136
+ }
137
+ }
138
+
139
+ export class Market {
140
+ coreProgram: Program<ExponentCore>
141
+ xponIx: ExponentIx
142
+ xponPda: ExponentPDA
143
+
144
+ constructor(
145
+ public state: MarketArgs,
146
+ public selfAddress: web3.PublicKey,
147
+ public env: Environment,
148
+ public connection: web3.Connection,
149
+ ) {
150
+ this.xponPda = new ExponentPDA(env.coreProgramId)
151
+ this.xponIx = new ExponentIx(env.coreProgramId)
152
+ const mockWallet = new MyWallet(web3.Keypair.generate())
153
+ this.coreProgram = new Program(IDL as ExponentCore, new AnchorProvider(connection, mockWallet))
154
+ }
155
+
156
+ static async load(
157
+ env: Environment,
158
+ connection: web3.Connection,
159
+ address: web3.PublicKey,
160
+ vault?: Vault,
161
+ ): Promise<Market> {
162
+ const fetcher = new ExponentFetcher({ connection })
163
+ const m = await fetcher.fetchMarket(address)
164
+ const [[lpSupply, sySupply], alt, loadedVault] = await Promise.all([
165
+ getMultipleMintSupply(connection, [m.mintLp, m.mintSy]),
166
+ fetchAddressLookupTable(connection, m.addressLookupTable),
167
+ vault || Vault.load(env, connection, m.vault),
168
+ ])
169
+ const cpiAccounts = makeCpiAccountMetaLists(alt, m.cpiAccounts)
170
+
171
+ const flavor = (() => {
172
+ switch (loadedVault.flavor.flavor) {
173
+ case "marginfi":
174
+ return makeFlavorMarginfiSync(loadedVault.flavor)
175
+ case "kamino":
176
+ return makeFlavorKaminoSync(loadedVault.flavor)
177
+ case "jitoRestaking":
178
+ return makeFlavorJitoRestakingSync(loadedVault.flavor)
179
+ case "perena":
180
+ return makeFlavorPerenaSync(loadedVault.flavor)
181
+ case "generic":
182
+ return makeFlavorGenericSync(loadedVault.flavor)
183
+ default:
184
+ throw new Error(`Unknown flavor: ${loadedVault.flavor}`)
185
+ }
186
+ })()
187
+
188
+ const syPosition = await makeSyPosition(fetcher, flavor, m.syProgram, address)
189
+
190
+ const state: MarketArgs = {
191
+ ...m,
192
+ vault: loadedVault,
193
+ cpiAccounts,
194
+ lpSupply,
195
+ sySupply,
196
+ flavor,
197
+ syPosition,
198
+ }
199
+ return new Market(state, address, env, connection)
200
+ }
201
+
202
+ async reload(conn: web3.Connection = this.connection) {
203
+ const m = await Market.load(this.env, conn, this.selfAddress)
204
+ this.state = m.state
205
+ return m
206
+ }
207
+
208
+ /** Convert to a JSON representation */
209
+ toJson() {
210
+ return toJson(this)
211
+ }
212
+
213
+ get vault() {
214
+ return this.state.vault
215
+ }
216
+
217
+ get flavor() {
218
+ return this.state.flavor
219
+ }
220
+
221
+ get lpSupply() {
222
+ return this.state.lpSupply
223
+ }
224
+
225
+ get syBalance() {
226
+ return this.state.syBalance
227
+ }
228
+
229
+ get ptBalance() {
230
+ return this.state.ptBalance
231
+ }
232
+
233
+ get mintSy() {
234
+ return this.state.mintSy
235
+ }
236
+
237
+ get mintPt() {
238
+ return this.state.mintPt
239
+ }
240
+
241
+ get statusFlags() {
242
+ return this.state.statusFlags
243
+ }
244
+
245
+ get mintYt() {
246
+ return this.vault.mintYt
247
+ }
248
+
249
+ get mintLp() {
250
+ return this.state.mintLp
251
+ }
252
+
253
+ get addressLookupTable() {
254
+ return this.state.addressLookupTable
255
+ }
256
+
257
+ get syProgram() {
258
+ return this.state.syProgram
259
+ }
260
+
261
+ get cpiAccounts() {
262
+ return this.state.cpiAccounts
263
+ }
264
+
265
+ get marketEmissions() {
266
+ return this.state.emissions
267
+ }
268
+
269
+ get emissions(): Emission[] {
270
+ if (this.flavor.flavor === "marginfi") {
271
+ return this.flavor.mfiSyState.account.emissions.map((e) => ({
272
+ escrowAccountAddress: e.escrowAccount,
273
+ mint: e.mint,
274
+ tokenProgramAddress: e.tokenProgram,
275
+ totalClaimed: BigInt(e.totalClaimedEmissions.toString()),
276
+ lastSeenTotalAccruedEmissions: BigInt(e.lastSeenTotalAccruedEmissions.toString()),
277
+ index: e.index,
278
+ }))
279
+ }
280
+ if (this.flavor.flavor === "kamino") {
281
+ return this.flavor.kaminoSyState.account.emissions.map((e) => ({
282
+ escrowAccountAddress: e.escrowAccount,
283
+ mint: e.mint,
284
+ tokenProgramAddress: e.tokenProgram,
285
+ totalClaimed: BigInt(e.totalClaimedEmissions.toString()),
286
+ lastSeenTotalAccruedEmissions: BigInt(e.lastSeenTotalAccruedEmissions.toString()),
287
+ index: e.index,
288
+ }))
289
+ }
290
+ if (this.flavor.flavor === "jitoRestaking") {
291
+ return this.flavor.jitoSyState.account.emissions.map((e) => ({
292
+ escrowAccountAddress: e.escrowAccount,
293
+ mint: e.mint,
294
+ tokenProgramAddress: e.tokenProgram,
295
+ totalClaimed: BigInt(e.totalClaimedEmissions.toString()),
296
+ lastSeenTotalAccruedEmissions: BigInt(e.lastSeenTotalAccruedEmissions.toString()),
297
+ index: e.index,
298
+ }))
299
+ }
300
+ if (this.flavor.flavor === "perena") {
301
+ return this.flavor.perenaSyState.account.emissions.map((e) => ({
302
+ escrowAccountAddress: e.escrowAccount,
303
+ mint: e.mint,
304
+ tokenProgramAddress: e.tokenProgram,
305
+ totalClaimed: BigInt(e.totalClaimedEmissions.toString()),
306
+ lastSeenTotalAccruedEmissions: BigInt(e.lastSeenTotalAccruedEmissions.toString()),
307
+ index: e.index,
308
+ }))
309
+ }
310
+ if (this.flavor.flavor === "generic") {
311
+ return this.flavor.genericSyState.account.emissions.map((e) => ({
312
+ escrowAccountAddress: e.escrowAccount,
313
+ mint: e.mint,
314
+ tokenProgramAddress: e.tokenProgram,
315
+ totalClaimed: BigInt(e.totalClaimedEmissions.toString()),
316
+ lastSeenTotalAccruedEmissions: BigInt(e.lastSeenTotalAccruedEmissions.toString()),
317
+ index: e.index,
318
+ }))
319
+ }
320
+ throw new Error("Unknown flavor")
321
+ }
322
+
323
+ /** Get the escrow token account addresses for the emissions, in order */
324
+ get emissionTokenAccounts(): web3.PublicKey[] {
325
+ return this.emissions.map((e) => e.escrowAccountAddress)
326
+ }
327
+
328
+ /** Pass-through SY account owned by the market */
329
+ get tokenSyEscrow(): web3.PublicKey {
330
+ return this.state.tokenSyEscrow
331
+ }
332
+
333
+ /** SY account that holds treasury SY fees from PT trading */
334
+ get tokenFeeTreasurySy(): web3.PublicKey {
335
+ return this.state.tokenFeeTreasurySy
336
+ }
337
+
338
+ /** Market liquidity for PT */
339
+ get tokenPtEscrow(): web3.PublicKey {
340
+ return this.state.tokenPtEscrow
341
+ }
342
+
343
+ get tokenLpEscrow(): web3.PublicKey {
344
+ return this.state.tokenLpEscrow
345
+ }
346
+
347
+ get currentSyExchangeRate(): number {
348
+ return this.flavor.currentSyExchangeRate
349
+ }
350
+
351
+ /** Special account for event emit self-cpi */
352
+ get eventAuthority(): web3.PublicKey {
353
+ return emitEventAuthority(this.env.coreProgramId)
354
+ }
355
+
356
+ get currentPtPriceInSy(): number {
357
+ return this.currentPtPriceInAsset / this.currentSyExchangeRate
358
+ }
359
+
360
+ get currentPtPriceInAsset(): number {
361
+ const c = this.marketCalculator()
362
+ return c.exchangeRate
363
+ }
364
+
365
+ get ptDiscount(): number {
366
+ return 1 / this.currentPtPriceInAsset
367
+ }
368
+
369
+ get lpPriceInAsset(): number {
370
+ // Calculate total value of liquidity in asset units
371
+ const liquidityPoolTvl =
372
+ Number(this.syBalance) * this.flavor.currentSyExchangeRate + Number(this.ptBalance) / this.currentPtPriceInAsset
373
+ // Return LP price in asset units (0 if no supply to avoid division by zero)
374
+ return Number(this.lpSupply) === 0 ? 0 : liquidityPoolTvl / Number(this.lpSupply)
375
+ }
376
+
377
+ get secondsRemaining(): number {
378
+ const now = Date.now() / 1000
379
+ return Math.max(0, Math.round(this.state.expirationTs - now))
380
+ }
381
+
382
+ get absolutePtYield(): number {
383
+ const ptAssetExchangeRate = this.ptDiscount
384
+ return (1 - ptAssetExchangeRate) / ptAssetExchangeRate
385
+ }
386
+
387
+ /** Annualize a rate given the number of seconds remaining until maturity */
388
+ static annualize(r: number, secondsRemaining: number) {
389
+ return (r * SECONDS_PER_YEAR) / secondsRemaining
390
+ }
391
+
392
+ /** Annualized PT fixed rate */
393
+ get ptApr(): number {
394
+ return Market.annualize(this.absolutePtYield, this.secondsRemaining)
395
+ }
396
+
397
+ /** The fee rate taken off of trade fees (typically around 20%) expressed as a BPS number */
398
+ get feeTreasuryBps(): number {
399
+ return this.state.feeTreasurySyBps
400
+ }
401
+
402
+ /** The fee rate on assets when trading PT
403
+ * Expressed as a rational number
404
+ * eg 0.01 = 1%
405
+ */
406
+ get feeRatePtTrade(): number {
407
+ return 1 - this.marketCalculator().feeRate
408
+ }
409
+
410
+ /** The fee rate taken off of trade fees (typically around 20%) expressed as a rational number */
411
+ get feeTreasuryRate(): number {
412
+ return this.feeTreasuryBps / 10_000
413
+ }
414
+
415
+ /** Calculate amonut of LP tokens to expect for tokens in */
416
+ lpOutForTokensIn(syInIntent: bigint, ptInIntent: bigint): LiquidityAdd {
417
+ return lpOutForTokensIn({
418
+ syIntent: Number(syInIntent),
419
+ ptIntent: Number(ptInIntent),
420
+ lpSupply: Number(this.lpSupply),
421
+ syLiquidity: Number(this.syBalance),
422
+ ptLiquidity: Number(this.ptBalance),
423
+ })
424
+ }
425
+
426
+ marketCalculator() {
427
+ const secondsRemaining = Math.max(0, Math.round(this.state.expirationTs - Date.now() / 1000))
428
+ return new MarketCalculator({
429
+ liquiditySy: parseInt(this.syBalance.toString()),
430
+ liquidityPt: parseInt(this.ptBalance.toString()),
431
+ currentSyExchangeRate: this.currentSyExchangeRate,
432
+ lnFeeRateRoot: this.state.lnFeeRateRoot,
433
+ secondsRemaining,
434
+ lpTokenSupply: this.lpSupply,
435
+ rateScalarRoot: this.state.rateScalarRoot,
436
+ lastLnImpliedRate: this.state.lastLnImpliedRate,
437
+ maxNetBalanceChangeNegativePercentage: this.state.liquidityNetBalanceLimits.maxNetBalanceChangeNegativePercentage,
438
+ maxNetBalanceChangePositivePercentage: this.state.liquidityNetBalanceLimits.maxNetBalanceChangePositivePercentage,
439
+ windowStartTimestamp: this.state.liquidityNetBalanceLimits.windowStartTimestamp,
440
+ windowStartNetBalance: this.state.liquidityNetBalanceLimits.windowStartNetBalance.toNumber(),
441
+ windowDurationSeconds: this.state.liquidityNetBalanceLimits.windowDurationSeconds,
442
+ })
443
+ }
444
+
445
+ /** Deposit a pair of tokens as liquidity to the market
446
+ * Adds PT & SY from the `depositor` to the market
447
+ *
448
+ * Due to unforeseeable slippage, the PT & SY amounts intended are effectively the maximum amounts
449
+ * The minimum LP tokens to receive is specified by `minLpOut`
450
+ *
451
+ * The token accounts themselves are optional, and will be derived from the depositor's wallet if not provided
452
+ */
453
+ async ixDepositLiquidity({
454
+ ptInIntent,
455
+ syInIntent,
456
+ minLpOut,
457
+ depositor,
458
+ ptSrc,
459
+ sySrc,
460
+ lpDst,
461
+ }: {
462
+ /** Intended (maximum) amount of PT in */
463
+ ptInIntent: bigint
464
+ /** Intended (maximum) amount of SY in */
465
+ syInIntent: bigint
466
+ /** Minimum LP tokens out */
467
+ minLpOut: bigint
468
+ depositor: web3.PublicKey
469
+ ptSrc?: web3.PublicKey
470
+ sySrc?: web3.PublicKey
471
+ lpDst?: web3.PublicKey
472
+ }) {
473
+ const tokenProgram = TOKEN_PROGRAM_ID
474
+
475
+ sySrc = sySrc || getAssociatedTokenAddressSync(this.mintSy, depositor, true, TOKEN_PROGRAM_ID)
476
+ ptSrc = ptSrc || getAssociatedTokenAddressSync(this.mintPt, depositor, true, TOKEN_PROGRAM_ID)
477
+ lpDst = lpDst || getAssociatedTokenAddressSync(this.mintLp, depositor, true, TOKEN_PROGRAM_ID)
478
+
479
+ const syRemAccounts = this.cpiAccounts.depositSy
480
+
481
+ return this.coreProgram.methods
482
+ .marketTwoDepositLiquidity(
483
+ new BN(ptInIntent.toString()),
484
+ new BN(syInIntent.toString()),
485
+ new BN(minLpOut.toString()),
486
+ )
487
+ .accountsStrict({
488
+ depositor,
489
+ market: this.selfAddress,
490
+ mintLp: this.mintLp,
491
+ tokenPtSrc: ptSrc,
492
+ tokenSySrc: sySrc,
493
+ tokenLpDst: lpDst,
494
+ tokenPtEscrow: this.tokenPtEscrow,
495
+ tokenSyEscrow: this.tokenSyEscrow,
496
+ addressLookupTable: this.addressLookupTable,
497
+ syProgram: this.syProgram,
498
+ tokenProgram,
499
+ eventAuthority: this.vault.eventAuthority,
500
+ program: this.coreProgram.programId,
501
+ })
502
+ .remainingAccounts(syRemAccounts)
503
+ .instruction()
504
+ }
505
+
506
+ async ixModifyMarketSetting({ signer, adminAction }: { signer: web3.PublicKey; adminAction: MarketAdminAction }) {
507
+ return this.coreProgram.methods
508
+ .modifyMarketSetting(adminAction)
509
+ .accountsStrict({
510
+ market: this.selfAddress,
511
+ adminState: getExponentAdminStatePda(),
512
+ signer,
513
+ systemProgram: web3.SystemProgram.programId,
514
+ })
515
+ .instruction()
516
+ }
517
+
518
+ async ixModifyFarm({
519
+ newRate,
520
+ untilTimestamp,
521
+ signer,
522
+ farmMint,
523
+ farmTokenProgram,
524
+ farmTokenSrc,
525
+ }: {
526
+ newRate: bigint
527
+ untilTimestamp: number
528
+ signer: web3.PublicKey
529
+ farmMint: web3.PublicKey
530
+ farmTokenProgram: web3.PublicKey
531
+ farmTokenSrc?: web3.PublicKey
532
+ }) {
533
+ farmTokenSrc = farmTokenSrc || getAssociatedTokenAddressSync(farmMint, signer, true, farmTokenProgram)
534
+
535
+ const tokenFarm = getAssociatedTokenAddressSync(farmMint, this.selfAddress, true, farmTokenProgram)
536
+
537
+ return this.coreProgram.methods
538
+ .modifyFarm(untilTimestamp, new BN(newRate.toString()))
539
+ .accountsStrict({
540
+ adminState: getExponentAdminStatePda(),
541
+ market: this.selfAddress,
542
+ mint: farmMint,
543
+ tokenFarm: tokenFarm,
544
+ signer,
545
+ tokenProgram: farmTokenProgram,
546
+ tokenSource: farmTokenSrc,
547
+ })
548
+ .instruction()
549
+ }
550
+ /**
551
+ * Redeem LP tokens for PT & SY (liquidity removal)
552
+ *
553
+ * The lpIn is exactly the amount of LP tokens to burn
554
+ * The minimum PT & SY out are specified by minPtOut & minSyOut
555
+ * The transaction may fail due to unforeseeable slippage on the redemption rate
556
+ *
557
+ * The token accounts themselves are optional, and will be derived from the withdrawer's wallet if not provided
558
+ */
559
+ async ixWithdrawLiquidity({
560
+ lpIn,
561
+ withdrawer,
562
+ minPtOut,
563
+ minSyOut,
564
+ ptDst,
565
+ syDst,
566
+ lpSrc,
567
+ }: {
568
+ lpIn: bigint
569
+ withdrawer: web3.PublicKey
570
+ minPtOut: bigint
571
+ minSyOut: bigint
572
+ ptDst?: web3.PublicKey
573
+ syDst?: web3.PublicKey
574
+ lpSrc?: web3.PublicKey
575
+ }) {
576
+ ptDst = ptDst || getAssociatedTokenAddressSync(this.mintPt, withdrawer, true, TOKEN_PROGRAM_ID)
577
+ syDst = syDst || getAssociatedTokenAddressSync(this.mintSy, withdrawer, true, TOKEN_PROGRAM_ID)
578
+ lpSrc = lpSrc || getAssociatedTokenAddressSync(this.mintLp, withdrawer, true, TOKEN_PROGRAM_ID)
579
+
580
+ const ptDstAtaIx = createAssociatedTokenAccountIdempotentInstruction(withdrawer, ptDst, withdrawer, this.mintPt)
581
+ const syDstAtaIx = createAssociatedTokenAccountIdempotentInstruction(withdrawer, syDst, withdrawer, this.mintSy)
582
+ const lpSrcAtaIx = createAssociatedTokenAccountIdempotentInstruction(withdrawer, lpSrc, withdrawer, this.mintLp)
583
+
584
+ const syRemAccounts = this.cpiAccounts.withdrawSy
585
+
586
+ const ixs = await this.coreProgram.methods
587
+ .marketTwoWithdrawLiquidity(new BN(lpIn.toString()), new BN(minPtOut.toString()), new BN(minSyOut.toString()))
588
+ .accountsStrict({
589
+ withdrawer,
590
+ market: this.selfAddress,
591
+ mintLp: this.mintLp,
592
+ tokenPtDst: ptDst,
593
+ tokenSyDst: syDst,
594
+ tokenLpSrc: lpSrc,
595
+ tokenPtEscrow: this.tokenPtEscrow,
596
+ tokenSyEscrow: this.tokenSyEscrow,
597
+ addressLookupTable: this.addressLookupTable,
598
+ syProgram: this.syProgram,
599
+ tokenProgram: TOKEN_PROGRAM_ID,
600
+ eventAuthority: this.vault.eventAuthority,
601
+ program: this.coreProgram.programId,
602
+ })
603
+ .remainingAccounts(syRemAccounts)
604
+ .instruction()
605
+
606
+ return { ixs: [ixs], setupIxs: [ptDstAtaIx, syDstAtaIx, lpSrcAtaIx] }
607
+ }
608
+
609
+ /** Buy PT with SY
610
+ *
611
+ * The trader is the account that sends the SY
612
+ * The amountPt is the exact amount of PT the trader intends to buy
613
+ * The syConstraint is the maximum amount of SY the trader is willing to spend
614
+ *
615
+ * The token accounts themselves are optional, and will be derived from the trader's wallet if not provided
616
+ */
617
+ async ixBuyPt({
618
+ trader,
619
+ amountPt,
620
+ maxSySpend,
621
+ tokenPt,
622
+ tokenSy,
623
+ }: {
624
+ trader: web3.PublicKey
625
+ amountPt: bigint
626
+ maxSySpend: bigint
627
+ tokenPt?: web3.PublicKey
628
+ tokenSy?: web3.PublicKey
629
+ }) {
630
+ return this.ixTradePt({
631
+ trader,
632
+ traderPt: amountPt,
633
+ syConstraint: maxSySpend,
634
+ isBuy: true,
635
+ tokenPt,
636
+ tokenSy,
637
+ })
638
+ }
639
+
640
+ /**
641
+ * Sell PT for SY
642
+ * The trader is the account that sends the PT
643
+ * The amountPt is the exact amount of PT the trader intends to sell
644
+ * The minSyReceive is the minimum amount of SY the trader is willing to receive
645
+ *
646
+ * The token accounts themselves are optional, and will be derived from the trader's wallet if not provided
647
+ */
648
+ async ixSellPt({
649
+ trader,
650
+ amountPt,
651
+ minSyReceive,
652
+ tokenPt,
653
+ tokenSy,
654
+ }: {
655
+ trader: web3.PublicKey
656
+ amountPt: bigint
657
+ minSyReceive: bigint
658
+ tokenPt?: web3.PublicKey
659
+ tokenSy?: web3.PublicKey
660
+ }) {
661
+ return this.ixTradePt({
662
+ trader,
663
+ traderPt: amountPt,
664
+ syConstraint: minSyReceive,
665
+ isBuy: false,
666
+ tokenPt,
667
+ tokenSy,
668
+ })
669
+ }
670
+
671
+ async ixTradePt({
672
+ trader,
673
+ traderPt,
674
+ syConstraint,
675
+ isBuy,
676
+ tokenPt,
677
+ tokenSy,
678
+ }: {
679
+ trader: web3.PublicKey
680
+ traderPt: bigint
681
+ syConstraint: bigint
682
+ isBuy: boolean
683
+ tokenPt?: web3.PublicKey
684
+ tokenSy?: web3.PublicKey
685
+ }) {
686
+ tokenPt = tokenPt || getAssociatedTokenAddressSync(this.mintPt, trader, true, TOKEN_PROGRAM_ID)
687
+ tokenSy = tokenSy || getAssociatedTokenAddressSync(this.mintSy, trader, true, TOKEN_PROGRAM_ID)
688
+
689
+ const tokenSyAtaIx = createAssociatedTokenAccountIdempotentInstruction(trader, tokenSy, trader, this.mintSy)
690
+ const tokenPtAtaIx = createAssociatedTokenAccountIdempotentInstruction(trader, tokenPt, trader, this.mintPt)
691
+
692
+ const remainingAccounts = [
693
+ ...this.cpiAccounts.getSyState,
694
+ ...this.cpiAccounts.depositSy,
695
+ ...this.cpiAccounts.withdrawSy,
696
+ ]
697
+
698
+ const netTradePtBN = new BN(traderPt.toString()).mul(isBuy ? new BN(1) : new BN(-1))
699
+ const syConstraintBN = new BN(syConstraint.toString()).mul(isBuy ? new BN(-1) : new BN(1))
700
+
701
+ const ix = await this.coreProgram.methods
702
+ .tradePt(netTradePtBN, syConstraintBN)
703
+ .accountsStrict({
704
+ trader,
705
+ market: this.selfAddress,
706
+ tokenPtTrader: tokenPt,
707
+ tokenSyTrader: tokenSy,
708
+ tokenSyEscrow: this.tokenSyEscrow,
709
+ tokenPtEscrow: this.tokenPtEscrow,
710
+ addressLookupTable: this.addressLookupTable,
711
+ tokenProgram: TOKEN_PROGRAM_ID,
712
+ syProgram: this.syProgram,
713
+ tokenFeeTreasurySy: this.vault.state.treasurySyTokenAccount,
714
+ eventAuthority: this.vault.eventAuthority,
715
+ program: this.coreProgram.programId,
716
+ })
717
+ .remainingAccounts(remainingAccounts)
718
+ .instruction()
719
+
720
+ return { ixs: [ix], setupIxs: [tokenPtAtaIx, tokenSyAtaIx] }
721
+ }
722
+
723
+ /** Sell YT for SY
724
+ *
725
+ * The trader is the account that sends the YT
726
+ *
727
+ * The amountYt is the exact amount of YT the trader intends to sell
728
+ *
729
+ * The minSyOut is the minimum amount of SY the trader is willing to receive
730
+ *
731
+ * The token accounts themselves are optional, and will be derived from the trader's wallet if not provided
732
+ */
733
+ async ixSellYt({
734
+ trader,
735
+ ytIn,
736
+ minSyOut,
737
+ ytSrc,
738
+ ptSrc,
739
+ syDst,
740
+ }: {
741
+ trader: web3.PublicKey
742
+ ytIn: bigint
743
+ minSyOut: bigint
744
+ ytSrc?: web3.PublicKey
745
+ ptSrc?: web3.PublicKey
746
+ syDst?: web3.PublicKey
747
+ }) {
748
+ syDst = syDst || getAssociatedTokenAddressSync(this.mintSy, trader, true, TOKEN_PROGRAM_ID)
749
+ ptSrc = ptSrc || getAssociatedTokenAddressSync(this.mintPt, trader, true, TOKEN_PROGRAM_ID)
750
+ ytSrc = ytSrc || getAssociatedTokenAddressSync(this.mintYt, trader, true, TOKEN_PROGRAM_ID)
751
+
752
+ const syDstAtaIxs = createAssociatedTokenAccountIdempotentInstruction(trader, syDst, trader, this.mintSy)
753
+ const ptSrcAtaIxs = createAssociatedTokenAccountIdempotentInstruction(trader, ptSrc, trader, this.mintPt)
754
+ const ytSrcAtaIxs = createAssociatedTokenAccountIdempotentInstruction(trader, ytSrc, trader, this.mintYt)
755
+
756
+ const mergeAccounts = this.vault.mergeAccounts({ owner: trader, ytSrc, ptSrc, syDst })
757
+ const remainingAccounts = uniqueRemainingAccounts([
758
+ ...this.cpiAccounts.getSyState,
759
+ ...this.cpiAccounts.depositSy,
760
+ ...mergeAccounts.remainingAccounts,
761
+ ])
762
+ remainingAccounts.push({ pubkey: this.coreProgram.programId, isWritable: false, isSigner: false })
763
+
764
+ const ix = await this.coreProgram.methods
765
+ .sellYt(new BN(ytIn.toString()), new BN(minSyOut.toString()))
766
+ .accountsStrict({
767
+ trader,
768
+ market: this.selfAddress,
769
+ tokenYtTrader: ytSrc,
770
+ tokenPtTrader: ptSrc,
771
+ tokenSyTrader: syDst,
772
+ tokenSyEscrow: this.tokenSyEscrow,
773
+ tokenPtEscrow: this.tokenPtEscrow,
774
+ addressLookupTable: this.addressLookupTable,
775
+ tokenProgram: TOKEN_PROGRAM_ID,
776
+ vault: mergeAccounts.mainAccounts.vault,
777
+ authorityVault: mergeAccounts.mainAccounts.authority,
778
+ tokenSyEscrowVault: mergeAccounts.mainAccounts.escrowSy,
779
+ mintYt: this.mintYt,
780
+ mintPt: this.mintPt,
781
+ addressLookupTableVault: mergeAccounts.mainAccounts.addressLookupTable,
782
+ yieldPositionVault: mergeAccounts.mainAccounts.yieldPosition,
783
+ syProgram: this.syProgram,
784
+ tokenFeeTreasurySy: this.vault.state.treasurySyTokenAccount,
785
+ eventAuthority: this.vault.eventAuthority,
786
+ program: this.coreProgram.programId,
787
+ })
788
+ .remainingAccounts(remainingAccounts)
789
+ .instruction()
790
+
791
+ return { ixs: [ix], setupIxs: [syDstAtaIxs, ptSrcAtaIxs, ytSrcAtaIxs] }
792
+ }
793
+
794
+ /** Buy YT with SY
795
+ *
796
+ * The trader is the account that sends the SY
797
+ *
798
+ * The ytOut is the exact amount of YT the trader intends to buy
799
+ *
800
+ * The maxSyIn is the maximum amount of SY the trader is willing to spend
801
+ *
802
+ * The token accounts themselves are optional, and will be derived from the trader's wallet if not provided
803
+ */
804
+ async ixBuyYt({
805
+ trader,
806
+ ytOut,
807
+ maxSyIn,
808
+ ytTrader,
809
+ ptTrader,
810
+ syTrader,
811
+ }: {
812
+ trader: web3.PublicKey
813
+ ytOut: bigint
814
+ maxSyIn: bigint
815
+ ytTrader?: web3.PublicKey
816
+ ptTrader?: web3.PublicKey
817
+ syTrader?: web3.PublicKey
818
+ }) {
819
+ syTrader = syTrader || getAssociatedTokenAddressSync(this.mintSy, trader, true, TOKEN_PROGRAM_ID)
820
+ ptTrader = ptTrader || getAssociatedTokenAddressSync(this.mintPt, trader, true, TOKEN_PROGRAM_ID)
821
+ ytTrader = ytTrader || getAssociatedTokenAddressSync(this.mintYt, trader, true, TOKEN_PROGRAM_ID)
822
+
823
+ const syTraderAtaIx = createAssociatedTokenAccountIdempotentInstruction(trader, syTrader, trader, this.mintSy)
824
+ const ptTraderAtaIx = createAssociatedTokenAccountIdempotentInstruction(trader, ptTrader, trader, this.mintPt)
825
+ const ytTraderAtaIx = createAssociatedTokenAccountIdempotentInstruction(trader, ytTrader, trader, this.mintYt)
826
+
827
+ const stripAccounts = this.vault.stripAccounts({
828
+ depositor: trader,
829
+ ytDst: ytTrader,
830
+ ptDst: ptTrader,
831
+ sySrc: syTrader,
832
+ })
833
+
834
+ const remainingAccounts = uniqueRemainingAccounts([
835
+ ...this.cpiAccounts.getSyState,
836
+ ...this.cpiAccounts.withdrawSy,
837
+ ...this.cpiAccounts.depositSy,
838
+ ...stripAccounts.remainingAccounts,
839
+ ])
840
+ remainingAccounts.push({ pubkey: this.coreProgram.programId, isWritable: false, isSigner: false })
841
+
842
+ const ix = await this.coreProgram.methods
843
+ .buyYt(new BN(maxSyIn.toString()), new BN(ytOut.toString()))
844
+ .accountsStrict({
845
+ trader,
846
+ market: this.selfAddress,
847
+ tokenYtTrader: ytTrader,
848
+ tokenPtTrader: ptTrader,
849
+ tokenSyTrader: syTrader,
850
+ tokenSyEscrow: this.tokenSyEscrow,
851
+ tokenPtEscrow: this.tokenPtEscrow,
852
+ addressLookupTable: this.addressLookupTable,
853
+ tokenProgram: TOKEN_PROGRAM_ID,
854
+ syProgram: this.syProgram,
855
+ vault: stripAccounts.mainAccounts.vault,
856
+ vaultAuthority: stripAccounts.mainAccounts.authority,
857
+ tokenSyEscrowVault: stripAccounts.mainAccounts.escrowSy,
858
+ mintYt: this.mintYt,
859
+ mintPt: this.mintPt,
860
+ addressLookupTableVault: stripAccounts.mainAccounts.addressLookupTable,
861
+ yieldPosition: stripAccounts.mainAccounts.yieldPosition,
862
+ tokenFeeTreasurySy: this.vault.state.treasurySyTokenAccount,
863
+ eventAuthority: this.vault.eventAuthority,
864
+ program: this.coreProgram.programId,
865
+ })
866
+ .remainingAccounts(remainingAccounts)
867
+ .instruction()
868
+
869
+ return { ixs: [ix], setupIxs: [syTraderAtaIx, ptTraderAtaIx, ytTraderAtaIx] }
870
+ }
871
+
872
+ async ixInitLpPosition({ owner, feePayer }: { owner: web3.PublicKey; feePayer?: web3.PublicKey }) {
873
+ const lpPosition = this.xponPda.marketLpPosition({ market: this.selfAddress, owner })
874
+ return this.coreProgram.methods
875
+ .initLpPosition()
876
+ .accountsStrict({
877
+ owner,
878
+ feePayer: feePayer || owner,
879
+ lpPosition,
880
+ market: this.selfAddress,
881
+ systemProgram: web3.SystemProgram.programId,
882
+ eventAuthority: this.vault.eventAuthority,
883
+ program: this.coreProgram.programId,
884
+ })
885
+ .instruction()
886
+ }
887
+
888
+ /** Deposit LP tokens into the farming module to earn rewards */
889
+ async ixDepositLp({ owner, amount, lpSrc }: { owner: web3.PublicKey; amount: bigint; lpSrc?: web3.PublicKey }) {
890
+ lpSrc = lpSrc || getAssociatedTokenAddressSync(this.mintLp, owner, true, TOKEN_PROGRAM_ID)
891
+ const lpPosition = this.xponPda.marketLpPosition({ market: this.selfAddress, owner })
892
+
893
+ return this.coreProgram.methods
894
+ .marketDepositLp(new BN(amount.toString()))
895
+ .accountsStrict({
896
+ owner,
897
+ lpPosition,
898
+ tokenLpSrc: lpSrc,
899
+ tokenLpEscrow: this.tokenLpEscrow,
900
+ market: this.selfAddress,
901
+ addressLookupTable: this.addressLookupTable,
902
+ syProgram: this.syProgram,
903
+ tokenProgram: TOKEN_PROGRAM_ID,
904
+ mintLp: this.mintLp,
905
+ systemProgram: web3.SystemProgram.programId,
906
+ eventAuthority: this.vault.eventAuthority,
907
+ program: this.coreProgram.programId,
908
+ })
909
+ .remainingAccounts(this.cpiAccounts.getPositionState)
910
+ .instruction()
911
+ }
912
+
913
+ /** Withdraw LP tokens from the farming module */
914
+ async ixWithdrawLp({ owner, amount, lpDst }: { owner: web3.PublicKey; amount: bigint; lpDst?: web3.PublicKey }) {
915
+ lpDst = lpDst || getAssociatedTokenAddressSync(this.mintLp, owner, true, TOKEN_PROGRAM_ID)
916
+
917
+ const lpPosition = this.xponPda.marketLpPosition({ market: this.selfAddress, owner })
918
+ return this.coreProgram.methods
919
+ .marketWithdrawLp(new BN(amount.toString()))
920
+ .accountsStrict({
921
+ owner,
922
+ lpPosition,
923
+ tokenLpDst: lpDst,
924
+ tokenLpEscrow: this.tokenLpEscrow,
925
+ market: this.selfAddress,
926
+ addressLookupTable: this.addressLookupTable,
927
+ mintLp: this.mintLp,
928
+ syProgram: this.syProgram,
929
+ tokenProgram: TOKEN_PROGRAM_ID,
930
+ systemProgram: web3.SystemProgram.programId,
931
+ eventAuthority: this.vault.eventAuthority,
932
+ program: this.coreProgram.programId,
933
+ })
934
+ .remainingAccounts(this.cpiAccounts.getPositionState)
935
+ .instruction()
936
+ }
937
+
938
+ async ixWrapperCollectInterest({ claimer, tokenSyDst }: { claimer: web3.PublicKey; tokenSyDst?: web3.PublicKey }) {
939
+ const yieldPosition = this.xponPda.yieldPosition({ owner: claimer, vault: this.vault.selfAddress })
940
+ tokenSyDst = tokenSyDst || getAssociatedTokenAddressSync(this.mintSy, claimer, true, TOKEN_PROGRAM_ID)
941
+ const tokenBaseClaimer = getAssociatedTokenAddressSync(
942
+ this.flavor.mintBase,
943
+ claimer,
944
+ true,
945
+ this.flavor.baseTokenProgram,
946
+ )
947
+ const tokenBaseClaimerAtaIx = createAssociatedTokenAccountIdempotentInstruction(
948
+ claimer,
949
+ tokenBaseClaimer,
950
+ claimer,
951
+ this.flavor.mintBase,
952
+ )
953
+
954
+ const tokenSyClaimer = getAssociatedTokenAddressSync(this.mintSy, claimer, true)
955
+ const tokenSyClaimerAtaIx = createAssociatedTokenAccountIdempotentInstruction(
956
+ claimer,
957
+ tokenSyClaimer,
958
+ claimer,
959
+ this.mintSy,
960
+ )
961
+
962
+ const remainingAccounts = uniqueRemainingAccounts([
963
+ ...this.vault.cpiAccounts.getSyState,
964
+ ...this.vault.cpiAccounts.withdrawSy,
965
+ ])
966
+
967
+ const redeemSyIx = await this.flavor.ixRedeemSy({
968
+ amountSy: "0",
969
+ redeemer: claimer,
970
+ redeemerBaseTokenAccount: tokenBaseClaimer,
971
+ redeemerSyTokenAccount: tokenSyDst,
972
+ })
973
+
974
+ const ix = await this.coreProgram.methods
975
+ .wrapperCollectInterest(redeemSyIx.keys.length)
976
+ .accountsStrict({
977
+ addressLookupTable: this.vault.addressLookupTable,
978
+ authority: this.vault.state.authority,
979
+ claimer,
980
+ escrowSy: this.vault.escrowSy,
981
+ syProgram: this.syProgram,
982
+ vault: this.vault.selfAddress,
983
+ tokenProgram: TOKEN_PROGRAM_ID,
984
+ treasurySyTokenAccount: this.vault.state.treasurySyTokenAccount,
985
+ yieldPosition: yieldPosition,
986
+ tokenSyDst,
987
+ eventAuthority: this.vault.eventAuthority,
988
+ program: this.coreProgram.programId,
989
+ })
990
+ .remainingAccounts(redeemSyIx.keys.concat(remainingAccounts))
991
+ .instruction()
992
+
993
+ return {
994
+ ixs: [
995
+ ...(await this.flavor.preIxs({ signer: claimer })),
996
+ ix,
997
+ ...(await this.flavor.postIxs({ signer: claimer })),
998
+ ],
999
+ setupIxs: [tokenBaseClaimerAtaIx, tokenSyClaimerAtaIx],
1000
+ }
1001
+ }
1002
+
1003
+ async ixWrapperBuyPt({
1004
+ owner,
1005
+ ptOut,
1006
+ maxBaseIn,
1007
+ tokenSyTrader,
1008
+ tokenPtTrader,
1009
+ tokenBaseTrader,
1010
+ }: {
1011
+ owner: web3.PublicKey
1012
+ ptOut: bigint
1013
+ maxBaseIn: bigint
1014
+ tokenSyTrader?: web3.PublicKey
1015
+ tokenPtTrader?: web3.PublicKey
1016
+ tokenBaseTrader?: web3.PublicKey
1017
+ }) {
1018
+ tokenSyTrader = tokenSyTrader || getAssociatedTokenAddressSync(this.mintSy, owner, true, TOKEN_PROGRAM_ID)
1019
+ tokenPtTrader = tokenPtTrader || getAssociatedTokenAddressSync(this.mintPt, owner, true, TOKEN_PROGRAM_ID)
1020
+ tokenBaseTrader =
1021
+ tokenBaseTrader || getAssociatedTokenAddressSync(this.flavor.mintBase, owner, true, this.flavor.baseTokenProgram)
1022
+
1023
+ const tokenSyTraderAtaIx = createAssociatedTokenAccountIdempotentInstruction(
1024
+ owner,
1025
+ tokenSyTrader,
1026
+ owner,
1027
+ this.mintSy,
1028
+ )
1029
+ const tokenPtTraderAtaIx = createAssociatedTokenAccountIdempotentInstruction(
1030
+ owner,
1031
+ tokenPtTrader,
1032
+ owner,
1033
+ this.mintPt,
1034
+ )
1035
+ const tokenBaseTraderAtaIx = createAssociatedTokenAccountIdempotentInstruction(
1036
+ owner,
1037
+ tokenBaseTrader,
1038
+ owner,
1039
+ this.flavor.mintBase,
1040
+ )
1041
+
1042
+ const mintSyIx = await this.flavor.ixMintSy({
1043
+ amountBase: "0",
1044
+ depositor: owner,
1045
+ depositorBaseTokenAccount: tokenBaseTrader,
1046
+ depositorSyTokenAccount: tokenSyTrader,
1047
+ })
1048
+
1049
+ const mintSyRemAccounts = mintSyIx.keys
1050
+
1051
+ console.log("mintSyRemAccounts", mintSyRemAccounts)
1052
+
1053
+ mintSyRemAccounts.push({
1054
+ pubkey: this.coreProgram.programId,
1055
+ isWritable: false,
1056
+ isSigner: false,
1057
+ })
1058
+
1059
+ const remainingAccounts = uniqueRemainingAccounts([
1060
+ ...this.cpiAccounts.getSyState,
1061
+ ...this.cpiAccounts.depositSy,
1062
+ // ...this.cpiAccounts.withdrawSy,
1063
+ ])
1064
+
1065
+ const ix = await this.coreProgram.methods
1066
+ .wrapperBuyPt(new BN(ptOut.toString()), new BN(maxBaseIn.toString()), mintSyRemAccounts.length)
1067
+ .accountsStrict({
1068
+ addressLookupTable: this.addressLookupTable,
1069
+ buyer: owner,
1070
+ market: this.selfAddress,
1071
+ tokenSyTrader,
1072
+ tokenPtTrader,
1073
+ tokenSyEscrow: this.tokenSyEscrow,
1074
+ tokenPtEscrow: this.tokenPtEscrow,
1075
+ tokenProgram: TOKEN_PROGRAM_ID,
1076
+ syProgram: this.syProgram,
1077
+ tokenFeeTreasurySy: this.state.tokenFeeTreasurySy,
1078
+ eventAuthority: this.vault.eventAuthority,
1079
+ program: this.coreProgram.programId,
1080
+ })
1081
+ .remainingAccounts(mintSyRemAccounts.concat(remainingAccounts))
1082
+ .instruction()
1083
+
1084
+ return {
1085
+ ixs: [...(await this.flavor.preIxs({ signer: owner })), ix, ...(await this.flavor.postIxs({ signer: owner }))],
1086
+ setupIxs: [tokenSyTraderAtaIx, tokenPtTraderAtaIx, tokenBaseTraderAtaIx],
1087
+ }
1088
+ }
1089
+
1090
+ async ixWrapperSellPt({
1091
+ owner,
1092
+ amount,
1093
+ minBaseOut,
1094
+ tokenSyTrader,
1095
+ tokenPtTrader,
1096
+ tokenBaseTrader,
1097
+ }: {
1098
+ owner: web3.PublicKey
1099
+ amount: bigint
1100
+ minBaseOut: bigint
1101
+ tokenSyTrader?: web3.PublicKey
1102
+ tokenPtTrader?: web3.PublicKey
1103
+ tokenBaseTrader?: web3.PublicKey
1104
+ }) {
1105
+ tokenSyTrader = tokenSyTrader || getAssociatedTokenAddressSync(this.mintSy, owner, true, TOKEN_PROGRAM_ID)
1106
+ tokenPtTrader = tokenPtTrader || getAssociatedTokenAddressSync(this.mintPt, owner, true, TOKEN_PROGRAM_ID)
1107
+ tokenBaseTrader =
1108
+ tokenBaseTrader || getAssociatedTokenAddressSync(this.flavor.mintBase, owner, true, this.flavor.baseTokenProgram)
1109
+
1110
+ const tokenSyTraderAtaIx = createAssociatedTokenAccountIdempotentInstruction(
1111
+ owner,
1112
+ tokenSyTrader,
1113
+ owner,
1114
+ this.mintSy,
1115
+ )
1116
+ const tokenPtTraderAtaIx = createAssociatedTokenAccountIdempotentInstruction(
1117
+ owner,
1118
+ tokenPtTrader,
1119
+ owner,
1120
+ this.mintPt,
1121
+ )
1122
+ const tokenBaseTraderAtaIx = createAssociatedTokenAccountIdempotentInstruction(
1123
+ owner,
1124
+ tokenBaseTrader,
1125
+ owner,
1126
+ this.flavor.mintBase,
1127
+ )
1128
+
1129
+ const redeemSyIx = await this.flavor.ixRedeemSy({
1130
+ amountSy: "0",
1131
+ redeemer: owner,
1132
+ redeemerBaseTokenAccount: tokenBaseTrader,
1133
+ redeemerSyTokenAccount: tokenSyTrader,
1134
+ })
1135
+
1136
+ const redeemSyRemAccounts = redeemSyIx.keys
1137
+ redeemSyRemAccounts.push({
1138
+ pubkey: this.coreProgram.programId,
1139
+ isWritable: false,
1140
+ isSigner: false,
1141
+ })
1142
+
1143
+ const remainingAccounts = uniqueRemainingAccounts([...this.cpiAccounts.getSyState, ...this.cpiAccounts.withdrawSy])
1144
+
1145
+ const ix = await this.coreProgram.methods
1146
+ .wrapperSellPt(new BN(amount.toString()), new BN(minBaseOut.toString()), redeemSyRemAccounts.length)
1147
+ .accountsStrict({
1148
+ seller: owner,
1149
+ market: this.selfAddress,
1150
+ tokenPtTrader,
1151
+ addressLookupTable: this.addressLookupTable,
1152
+ tokenProgram: TOKEN_PROGRAM_ID,
1153
+ syProgram: this.syProgram,
1154
+ tokenSyTrader,
1155
+ tokenPtEscrow: this.tokenPtEscrow,
1156
+ tokenSyEscrow: this.tokenSyEscrow,
1157
+ tokenFeeTreasurySy: this.state.tokenFeeTreasurySy,
1158
+ eventAuthority: this.vault.eventAuthority,
1159
+ program: this.coreProgram.programId,
1160
+ })
1161
+ .remainingAccounts(redeemSyRemAccounts.concat(remainingAccounts))
1162
+ .instruction()
1163
+
1164
+ return {
1165
+ ixs: [...(await this.flavor.preIxs({ signer: owner })), ix, ...(await this.flavor.postIxs({ signer: owner }))],
1166
+ setupIxs: [tokenSyTraderAtaIx, tokenPtTraderAtaIx, tokenBaseTraderAtaIx],
1167
+ }
1168
+ }
1169
+
1170
+ async ixWrapperBuyYt({
1171
+ owner,
1172
+ ytOut,
1173
+ maxBaseIn,
1174
+ tokenSyTrader,
1175
+ tokenPtTrader,
1176
+ tokenYtTrader,
1177
+ tokenBaseTrader,
1178
+ }: {
1179
+ owner: web3.PublicKey
1180
+ ytOut: bigint
1181
+ maxBaseIn: bigint
1182
+ tokenSyTrader?: web3.PublicKey
1183
+ tokenPtTrader?: web3.PublicKey
1184
+ tokenYtTrader?: web3.PublicKey
1185
+ tokenBaseTrader?: web3.PublicKey
1186
+ }) {
1187
+ tokenSyTrader = tokenSyTrader || getAssociatedTokenAddressSync(this.mintSy, owner, true, TOKEN_PROGRAM_ID)
1188
+ tokenPtTrader = tokenPtTrader || getAssociatedTokenAddressSync(this.mintPt, owner, true, TOKEN_PROGRAM_ID)
1189
+ tokenYtTrader = tokenYtTrader || getAssociatedTokenAddressSync(this.mintYt, owner, true, TOKEN_PROGRAM_ID)
1190
+ tokenBaseTrader =
1191
+ tokenBaseTrader || getAssociatedTokenAddressSync(this.flavor.mintBase, owner, true, this.flavor.baseTokenProgram)
1192
+
1193
+ const tokenSyTraderAtaIx = createAssociatedTokenAccountIdempotentInstruction(
1194
+ owner,
1195
+ tokenSyTrader,
1196
+ owner,
1197
+ this.mintSy,
1198
+ )
1199
+ const tokenPtTraderAtaIx = createAssociatedTokenAccountIdempotentInstruction(
1200
+ owner,
1201
+ tokenPtTrader,
1202
+ owner,
1203
+ this.mintPt,
1204
+ )
1205
+ const tokenYtTraderAtaIx = createAssociatedTokenAccountIdempotentInstruction(
1206
+ owner,
1207
+ tokenYtTrader,
1208
+ owner,
1209
+ this.mintYt,
1210
+ )
1211
+
1212
+ const mintSyIx = await this.flavor.ixMintSy({
1213
+ amountBase: "0",
1214
+ depositor: owner,
1215
+ depositorBaseTokenAccount: tokenBaseTrader,
1216
+ depositorSyTokenAccount: tokenSyTrader,
1217
+ })
1218
+
1219
+ const mintSyRemAccounts = mintSyIx.keys
1220
+
1221
+ const remainingAccounts = uniqueRemainingAccounts([
1222
+ ...this.cpiAccounts.getSyState,
1223
+ ...this.vault.cpiAccounts.depositSy,
1224
+ ...this.vault.cpiAccounts.getSyState,
1225
+ ...this.cpiAccounts.withdrawSy,
1226
+ ...this.cpiAccounts.getPositionState,
1227
+ ])
1228
+
1229
+ const allRemainingAccounts = mintSyRemAccounts.concat(remainingAccounts)
1230
+
1231
+ const ix1 = await this.coreProgram.methods
1232
+ .wrapperBuyYt(new BN(ytOut.toString()), new BN(maxBaseIn.toString()), mintSyRemAccounts.length)
1233
+ .accountsStrict({
1234
+ buyer: owner,
1235
+ market: this.selfAddress,
1236
+ tokenSyTrader,
1237
+ tokenYtTrader,
1238
+ tokenPtTrader,
1239
+ tokenSyEscrow: this.tokenSyEscrow,
1240
+ tokenPtEscrow: this.tokenPtEscrow,
1241
+ marketAddressLookupTable: this.addressLookupTable,
1242
+ vault: this.vault.selfAddress,
1243
+ vaultAuthority: this.vault.authority,
1244
+ tokenSyEscrowVault: this.vault.escrowSy,
1245
+ vaultAddressLookupTable: this.vault.addressLookupTable,
1246
+ tokenProgram: TOKEN_PROGRAM_ID,
1247
+ syProgram: this.syProgram,
1248
+ mintPt: this.vault.mintPt,
1249
+ yieldPosition: this.vault.state.yieldPositonAddress,
1250
+ mintYt: this.vault.mintYt,
1251
+ escrowYt: this.vault.escrowYt,
1252
+ systemProgram: web3.SystemProgram.programId,
1253
+ userYieldPosition: this.xponPda.yieldPosition({ vault: this.vault.selfAddress, owner }),
1254
+ tokenFeeTreasurySy: this.state.tokenFeeTreasurySy,
1255
+ eventAuthority: this.vault.eventAuthority,
1256
+ program: this.coreProgram.programId,
1257
+ })
1258
+ .remainingAccounts(allRemainingAccounts)
1259
+ .instruction()
1260
+
1261
+ const ix2 = await this.coreProgram.methods
1262
+ .depositYt(new BN(ytOut.toString()))
1263
+ .accountsStrict({
1264
+ depositor: owner,
1265
+ vault: this.vault.selfAddress,
1266
+ userYieldPosition: this.xponPda.yieldPosition({ vault: this.vault.selfAddress, owner }),
1267
+ ytSrc: tokenYtTrader,
1268
+ escrowYt: this.vault.escrowYt,
1269
+ tokenProgram: TOKEN_PROGRAM_ID,
1270
+ syProgram: this.syProgram,
1271
+ addressLookupTable: this.vault.addressLookupTable,
1272
+ systemProgram: web3.SystemProgram.programId,
1273
+ eventAuthority: this.vault.eventAuthority,
1274
+ program: this.coreProgram.programId,
1275
+ yieldPosition: this.vault.state.yieldPositonAddress,
1276
+ })
1277
+ .remainingAccounts(remainingAccounts)
1278
+ .instruction()
1279
+
1280
+ return {
1281
+ ixs: [
1282
+ ...(await this.flavor.preIxs({ signer: owner })),
1283
+ ix1,
1284
+ ...(await this.flavor.postIxs({ signer: owner })),
1285
+ ix2,
1286
+ ],
1287
+ setupIxs: [tokenSyTraderAtaIx, tokenPtTraderAtaIx, tokenYtTraderAtaIx],
1288
+ }
1289
+ }
1290
+
1291
+ async ixWrapperSellYt({
1292
+ owner,
1293
+ amount,
1294
+ minBaseOut,
1295
+ tokenBaseTrader,
1296
+ tokenSyTrader,
1297
+ tokenYtTrader,
1298
+ tokenPtTrader,
1299
+ }: {
1300
+ owner: web3.PublicKey
1301
+ amount: bigint
1302
+ minBaseOut: bigint
1303
+ tokenBaseTrader?: web3.PublicKey
1304
+ tokenSyTrader?: web3.PublicKey
1305
+ tokenYtTrader?: web3.PublicKey
1306
+ tokenPtTrader?: web3.PublicKey
1307
+ }) {
1308
+ tokenBaseTrader =
1309
+ tokenBaseTrader || getAssociatedTokenAddressSync(this.flavor.mintBase, owner, true, this.flavor.baseTokenProgram)
1310
+ tokenSyTrader = tokenSyTrader || getAssociatedTokenAddressSync(this.mintSy, owner, true, TOKEN_PROGRAM_ID)
1311
+ tokenYtTrader = tokenYtTrader || getAssociatedTokenAddressSync(this.mintYt, owner, true, TOKEN_PROGRAM_ID)
1312
+ tokenPtTrader = tokenPtTrader || getAssociatedTokenAddressSync(this.mintPt, owner, true, TOKEN_PROGRAM_ID)
1313
+
1314
+ const tokenSyTraderAtaIx = createAssociatedTokenAccountIdempotentInstruction(
1315
+ owner,
1316
+ tokenSyTrader,
1317
+ owner,
1318
+ this.mintSy,
1319
+ )
1320
+ const tokenPtTraderAtaIx = createAssociatedTokenAccountIdempotentInstruction(
1321
+ owner,
1322
+ tokenPtTrader,
1323
+ owner,
1324
+ this.mintPt,
1325
+ )
1326
+ const tokenYtTraderAtaIx = createAssociatedTokenAccountIdempotentInstruction(
1327
+ owner,
1328
+ tokenYtTrader,
1329
+ owner,
1330
+ this.mintYt,
1331
+ )
1332
+ const tokenBaseTraderAtaIx = createAssociatedTokenAccountIdempotentInstruction(
1333
+ owner,
1334
+ tokenBaseTrader,
1335
+ owner,
1336
+ this.flavor.mintBase,
1337
+ )
1338
+
1339
+ const redeemSyIx = await this.flavor.ixRedeemSy({
1340
+ amountSy: "0",
1341
+ redeemer: owner,
1342
+ redeemerBaseTokenAccount: tokenBaseTrader,
1343
+ redeemerSyTokenAccount: tokenSyTrader,
1344
+ })
1345
+
1346
+ const redeemSyRemAccounts = redeemSyIx.keys
1347
+
1348
+ const remainingAccounts = uniqueRemainingAccounts([
1349
+ ...this.cpiAccounts.getSyState,
1350
+ ...this.cpiAccounts.withdrawSy,
1351
+ ...this.vault.cpiAccounts.depositSy,
1352
+ ])
1353
+
1354
+ console.log(" remaining accounts length", remainingAccounts.length)
1355
+
1356
+ // remainingAccounts.push({
1357
+ // pubkey: this.coreProgram.programId,
1358
+ // isWritable: false,
1359
+ // isSigner: false,
1360
+ // })
1361
+
1362
+ const ix1 = await this.coreProgram.methods
1363
+ .withdrawYt(new BN(amount.toString()))
1364
+ .accountsStrict({
1365
+ ytDst: tokenYtTrader,
1366
+ escrowYt: this.vault.escrowYt,
1367
+ tokenProgram: TOKEN_PROGRAM_ID,
1368
+ syProgram: this.syProgram,
1369
+ addressLookupTable: this.vault.addressLookupTable,
1370
+ systemProgram: web3.SystemProgram.programId,
1371
+ eventAuthority: this.vault.eventAuthority,
1372
+ program: this.coreProgram.programId,
1373
+ yieldPosition: this.vault.state.yieldPositonAddress,
1374
+ userYieldPosition: this.xponPda.yieldPosition({ vault: this.vault.selfAddress, owner }),
1375
+ authority: this.vault.authority,
1376
+ owner,
1377
+ vault: this.vault.selfAddress,
1378
+ })
1379
+ .remainingAccounts(remainingAccounts)
1380
+ .instruction()
1381
+
1382
+ const ix2 = await this.coreProgram.methods
1383
+ .wrapperSellYt(new BN(amount.toString()), new BN(minBaseOut.toString()), redeemSyRemAccounts.length)
1384
+ .accountsStrict({
1385
+ seller: owner,
1386
+ market: this.selfAddress,
1387
+ tokenYtTrader,
1388
+ marketAddressLookupTable: this.addressLookupTable,
1389
+ vault: this.vault.selfAddress,
1390
+ vaultAuthority: this.vault.authority,
1391
+ tokenPtTrader,
1392
+ tokenPtEscrow: this.tokenPtEscrow,
1393
+ tokenSyEscrowVault: this.vault.escrowSy,
1394
+ vaultAddressLookupTable: this.vault.addressLookupTable,
1395
+ yieldPosition: this.vault.state.yieldPositonAddress,
1396
+ mintPt: this.vault.mintPt,
1397
+ mintYt: this.vault.mintYt,
1398
+ tokenProgram: TOKEN_PROGRAM_ID,
1399
+ syProgram: this.syProgram,
1400
+ tokenSyTrader,
1401
+ tokenSyEscrow: this.tokenSyEscrow,
1402
+ tokenFeeTreasurySy: this.state.tokenFeeTreasurySy,
1403
+ eventAuthority: this.vault.eventAuthority,
1404
+ program: this.coreProgram.programId,
1405
+ })
1406
+ .remainingAccounts(redeemSyRemAccounts.concat(remainingAccounts))
1407
+ .instruction()
1408
+
1409
+ return {
1410
+ ixs: [
1411
+ ix1,
1412
+ ...(await this.flavor.preIxs({ signer: owner })),
1413
+ ix2,
1414
+ ...(await this.flavor.postIxs({ signer: owner })),
1415
+ ],
1416
+ setupIxs: [tokenSyTraderAtaIx, tokenPtTraderAtaIx, tokenYtTraderAtaIx, tokenBaseTraderAtaIx],
1417
+ }
1418
+ }
1419
+
1420
+ async ixCollectMarketEmission({
1421
+ owner,
1422
+ emissionIndex,
1423
+ emissionDst,
1424
+ }: {
1425
+ owner: web3.PublicKey
1426
+ emissionIndex: number
1427
+ emissionDst?: web3.PublicKey
1428
+ }) {
1429
+ const emission = this.emissions[emissionIndex]
1430
+ const tokenEmissionDst =
1431
+ emissionDst || getAssociatedTokenAddressSync(emission.mint, owner, true, emission.tokenProgramAddress)
1432
+
1433
+ const tokenEmissionDstIx = createAssociatedTokenAccountIdempotentInstruction(
1434
+ owner,
1435
+ tokenEmissionDst,
1436
+ owner,
1437
+ emission.mint,
1438
+ )
1439
+ const ix = await this.coreProgram.methods
1440
+ .marketCollectEmission(emissionIndex)
1441
+ .accountsStrict({
1442
+ addressLookupTable: this.addressLookupTable,
1443
+ lpPosition: this.xponPda.marketLpPosition({ market: this.selfAddress, owner }),
1444
+ market: this.selfAddress,
1445
+ owner,
1446
+ syProgram: this.syProgram,
1447
+ tokenEmissionDst,
1448
+ tokenEmissionEscrow: this.marketEmissions.trackers[emissionIndex].tokenEscrow,
1449
+ tokenProgram: emission.tokenProgramAddress,
1450
+ program: this.coreProgram.programId,
1451
+ eventAuthority: this.eventAuthority,
1452
+ })
1453
+ .remainingAccounts(this.cpiAccounts.claimEmission[emissionIndex])
1454
+ .instruction()
1455
+
1456
+ return {
1457
+ ixs: [ix],
1458
+ setupIxs: [tokenEmissionDstIx],
1459
+ }
1460
+ }
1461
+
1462
+ /** Provide liquidity from a base asset - and receive YT and LP tokens in return */
1463
+ async ixProvideLiquidityNoPriceImpact({
1464
+ depositor,
1465
+ amountBase,
1466
+ minLpOut,
1467
+ tokenSyDepositor,
1468
+ tokenYtDepositor,
1469
+ tokenPtDepositor,
1470
+ tokenBaseDepositor,
1471
+ tokenLpDepositor,
1472
+ }: {
1473
+ depositor: web3.PublicKey
1474
+ amountBase: bigint
1475
+ minLpOut: bigint
1476
+ tokenSyDepositor?: web3.PublicKey
1477
+ tokenYtDepositor?: web3.PublicKey
1478
+ tokenPtDepositor?: web3.PublicKey
1479
+ tokenBaseDepositor?: web3.PublicKey
1480
+ tokenLpDepositor?: web3.PublicKey
1481
+ }) {
1482
+ tokenSyDepositor = tokenSyDepositor || getAssociatedTokenAddressSync(this.mintSy, depositor, true, TOKEN_PROGRAM_ID)
1483
+ tokenYtDepositor = tokenYtDepositor || getAssociatedTokenAddressSync(this.mintYt, depositor, true, TOKEN_PROGRAM_ID)
1484
+ tokenPtDepositor = tokenPtDepositor || getAssociatedTokenAddressSync(this.mintPt, depositor, true, TOKEN_PROGRAM_ID)
1485
+ tokenBaseDepositor =
1486
+ tokenBaseDepositor ||
1487
+ getAssociatedTokenAddressSync(this.flavor.mintBase, depositor, true, this.flavor.baseTokenProgram)
1488
+ tokenLpDepositor = tokenLpDepositor || getAssociatedTokenAddressSync(this.mintLp, depositor, true, TOKEN_PROGRAM_ID)
1489
+
1490
+ const tokenSyDepositorAtaIx = createAssociatedTokenAccountIdempotentInstruction(
1491
+ depositor,
1492
+ tokenSyDepositor,
1493
+ depositor,
1494
+ this.mintSy,
1495
+ )
1496
+ const tokenYtDepositorAtaIx = createAssociatedTokenAccountIdempotentInstruction(
1497
+ depositor,
1498
+ tokenYtDepositor,
1499
+ depositor,
1500
+ this.mintYt,
1501
+ )
1502
+ const tokenPtDepositorAtaIx = createAssociatedTokenAccountIdempotentInstruction(
1503
+ depositor,
1504
+ tokenPtDepositor,
1505
+ depositor,
1506
+ this.mintPt,
1507
+ )
1508
+
1509
+ const tokenLpDepositorAtaIx = createAssociatedTokenAccountIdempotentInstruction(
1510
+ depositor,
1511
+ tokenLpDepositor,
1512
+ depositor,
1513
+ this.mintLp,
1514
+ )
1515
+
1516
+ const amountBaseBn = new BN(amountBase.toString())
1517
+ const minLpOutBn = new BN(minLpOut.toString())
1518
+
1519
+ // TODO - fix this
1520
+ const mintSyIx = await this.flavor.ixMintSy({
1521
+ // argument does not matter - since we are just getting the keys
1522
+ amountBase: "0",
1523
+ depositor,
1524
+ depositorBaseTokenAccount: tokenBaseDepositor,
1525
+ depositorSyTokenAccount: tokenSyDepositor,
1526
+ })
1527
+
1528
+ const mintSyRemAccounts = mintSyIx.keys
1529
+
1530
+ // Log vault accounts before filtering
1531
+ const beforeVaultAccounts = [...this.vault.cpiAccounts.getSyState, ...this.vault.cpiAccounts.depositSy]
1532
+ console.log("Before filtering vault accounts:", beforeVaultAccounts.length, "accounts")
1533
+
1534
+ // Filter out TOKEN_PROGRAM_ID and SystemProgram.programId from vault remaining accounts
1535
+ const vaultCpiRemainingAccounts = beforeVaultAccounts.filter(
1536
+ (account) => account.pubkey.toBase58() !== TOKEN_PROGRAM_ID.toBase58(),
1537
+ )
1538
+
1539
+ // Log vault accounts after filtering
1540
+ console.log("After filtering vault accounts:", vaultCpiRemainingAccounts.length, "accounts")
1541
+
1542
+ // Log market accounts before filtering
1543
+ const beforeMarketAccounts = [...this.cpiAccounts.depositSy]
1544
+ console.log("Before filtering market accounts:", beforeMarketAccounts.length, "accounts")
1545
+
1546
+ // Filter out TOKEN_PROGRAM_ID and SystemProgram.programId from market remaining accounts
1547
+ const marketCpiRemainingAccounts = beforeMarketAccounts.filter(
1548
+ (account) =>
1549
+ account.pubkey.toBase58() !== TOKEN_PROGRAM_ID.toBase58() &&
1550
+ account.pubkey.toBase58() !== web3.SystemProgram.programId.toBase58() &&
1551
+ account.pubkey.toBase58() !== this.selfAddress.toBase58(),
1552
+ )
1553
+
1554
+ // Log market accounts after filtering
1555
+ console.log("After filtering market accounts:", marketCpiRemainingAccounts.length, "accounts")
1556
+
1557
+ // These remaining accounts can be in any order, and they can be unique-ified
1558
+ const unorderedRemainingAccounts = uniqueRemainingAccounts([
1559
+ ...vaultCpiRemainingAccounts,
1560
+ ...marketCpiRemainingAccounts,
1561
+ ])
1562
+
1563
+ const allRemainingAccounts = [...mintSyRemAccounts, ...unorderedRemainingAccounts]
1564
+
1565
+ const lpPosition = this.xponPda.marketLpPosition({ market: this.selfAddress, owner: depositor })
1566
+
1567
+ const ix = await this.coreProgram.methods
1568
+ .wrapperProvideLiquidity(amountBaseBn, minLpOutBn, mintSyRemAccounts.length)
1569
+ .accountsStrict({
1570
+ depositor,
1571
+ authority: this.vault.authority,
1572
+ vault: this.vault.selfAddress,
1573
+ market: this.selfAddress,
1574
+ tokenPtEscrow: this.tokenPtEscrow,
1575
+ tokenSyEscrow: this.tokenSyEscrow,
1576
+ tokenLpDst: tokenLpDepositor,
1577
+ mintLp: this.mintLp,
1578
+ escrowSy: this.vault.escrowSy,
1579
+ escrowYt: this.vault.escrowYt,
1580
+ tokenSyDepositor,
1581
+ tokenYtDepositor,
1582
+ tokenPtDepositor,
1583
+ mintYt: this.vault.mintYt,
1584
+ mintPt: this.vault.mintPt,
1585
+ userYieldPosition: this.xponPda.yieldPosition({ vault: this.vault.selfAddress, owner: depositor }),
1586
+ systemProgram: web3.SystemProgram.programId,
1587
+ tokenProgram: TOKEN_PROGRAM_ID,
1588
+ syProgram: this.syProgram,
1589
+ marketAddressLookupTable: this.addressLookupTable,
1590
+ vaultAddressLookupTable: this.vault.addressLookupTable,
1591
+ vaultRobotYieldPosition: this.vault.state.yieldPositonAddress,
1592
+ eventAuthority: this.vault.eventAuthority,
1593
+ program: this.coreProgram.programId,
1594
+ lpPosition,
1595
+ tokenLpEscrow: this.tokenLpEscrow,
1596
+ })
1597
+ .remainingAccounts(allRemainingAccounts)
1598
+ .instruction()
1599
+
1600
+ console.log("ix accounts length", ix.keys.length)
1601
+
1602
+ return {
1603
+ ixs: [
1604
+ ...(await this.flavor.preIxs({ signer: depositor })),
1605
+ ix,
1606
+ ...(await this.flavor.postIxs({ signer: depositor })),
1607
+ ],
1608
+ setupIxs: [tokenSyDepositorAtaIx, tokenYtDepositorAtaIx, tokenPtDepositorAtaIx, tokenLpDepositorAtaIx],
1609
+ }
1610
+ }
1611
+
1612
+ async ixProvideLiquidityBase({
1613
+ depositor,
1614
+ amountBase,
1615
+ minLpOut,
1616
+ externalPtToBuy,
1617
+ externalSyConstraint,
1618
+ tokenSyDepositor,
1619
+ tokenYtDepositor,
1620
+ tokenPtDepositor,
1621
+ tokenBaseDepositor,
1622
+ tokenLpDepositor,
1623
+ }: {
1624
+ depositor: web3.PublicKey
1625
+ amountBase: bigint
1626
+ minLpOut: bigint
1627
+ externalPtToBuy: bigint
1628
+ externalSyConstraint: bigint
1629
+ tokenSyDepositor?: web3.PublicKey
1630
+ tokenYtDepositor?: web3.PublicKey
1631
+ tokenPtDepositor?: web3.PublicKey
1632
+ tokenBaseDepositor?: web3.PublicKey
1633
+ tokenLpDepositor?: web3.PublicKey
1634
+ }) {
1635
+ tokenSyDepositor = tokenSyDepositor || getAssociatedTokenAddressSync(this.mintSy, depositor, true, TOKEN_PROGRAM_ID)
1636
+ tokenYtDepositor = tokenYtDepositor || getAssociatedTokenAddressSync(this.mintYt, depositor, true, TOKEN_PROGRAM_ID)
1637
+ tokenPtDepositor = tokenPtDepositor || getAssociatedTokenAddressSync(this.mintPt, depositor, true, TOKEN_PROGRAM_ID)
1638
+ tokenBaseDepositor =
1639
+ tokenBaseDepositor ||
1640
+ getAssociatedTokenAddressSync(this.flavor.mintBase, depositor, true, this.flavor.baseTokenProgram)
1641
+ tokenLpDepositor = tokenLpDepositor || getAssociatedTokenAddressSync(this.mintLp, depositor, true, TOKEN_PROGRAM_ID)
1642
+
1643
+ const tokenSyAtaIx = createAssociatedTokenAccountIdempotentInstruction(
1644
+ depositor,
1645
+ tokenSyDepositor,
1646
+ depositor,
1647
+ this.mintSy,
1648
+ )
1649
+ const tokenYtAtaIx = createAssociatedTokenAccountIdempotentInstruction(
1650
+ depositor,
1651
+ tokenYtDepositor,
1652
+ depositor,
1653
+ this.mintYt,
1654
+ )
1655
+ const tokenPtAtaIx = createAssociatedTokenAccountIdempotentInstruction(
1656
+ depositor,
1657
+ tokenPtDepositor,
1658
+ depositor,
1659
+ this.mintPt,
1660
+ )
1661
+ const tokenBaseAtaIx = createAssociatedTokenAccountIdempotentInstruction(
1662
+ depositor,
1663
+ tokenBaseDepositor,
1664
+ depositor,
1665
+ this.flavor.mintBase,
1666
+ this.flavor.baseTokenProgram,
1667
+ )
1668
+ const tokenLpAtaIx = createAssociatedTokenAccountIdempotentInstruction(
1669
+ depositor,
1670
+ tokenLpDepositor,
1671
+ depositor,
1672
+ this.mintLp,
1673
+ )
1674
+
1675
+ const lpPosition = this.xponPda.marketLpPosition({ market: this.selfAddress, owner: depositor })
1676
+
1677
+ const remainingAccounts = uniqueRemainingAccounts([
1678
+ ...this.cpiAccounts.depositSy,
1679
+ ...this.cpiAccounts.getPositionState,
1680
+ ])
1681
+
1682
+ const mintSyIx = await this.flavor.ixMintSy({
1683
+ amountBase: "0",
1684
+ depositor,
1685
+ depositorBaseTokenAccount: tokenBaseDepositor,
1686
+ depositorSyTokenAccount: tokenSyDepositor,
1687
+ })
1688
+
1689
+ const mintSyRemAccounts = mintSyIx.keys
1690
+
1691
+ const ix = await this.coreProgram.methods
1692
+ .wrapperProvideLiquidityBase(
1693
+ new BN(amountBase.toString()),
1694
+ new BN(minLpOut.toString()),
1695
+ mintSyRemAccounts.length,
1696
+ new BN(externalPtToBuy.toString()),
1697
+ new BN(externalSyConstraint.toString()),
1698
+ )
1699
+ .accountsStrict({
1700
+ depositor,
1701
+ lpPosition,
1702
+ market: this.selfAddress,
1703
+ tokenPtEscrow: this.tokenPtEscrow,
1704
+ tokenSyEscrow: this.tokenSyEscrow,
1705
+ tokenLpDst: tokenLpDepositor,
1706
+ mintLp: this.mintLp,
1707
+ syProgram: this.syProgram,
1708
+ tokenFeeTreasurySy: this.state.tokenFeeTreasurySy,
1709
+ tokenLpEscrow: this.tokenLpEscrow,
1710
+ tokenProgram: TOKEN_PROGRAM_ID,
1711
+ marketAddressLookupTable: this.addressLookupTable,
1712
+ tokenPtDepositor,
1713
+ tokenSyDepositor,
1714
+ systemProgram: web3.SystemProgram.programId,
1715
+ eventAuthority: this.vault.eventAuthority,
1716
+ program: this.coreProgram.programId,
1717
+ })
1718
+ .remainingAccounts(mintSyRemAccounts.concat(remainingAccounts))
1719
+ .instruction()
1720
+
1721
+ return {
1722
+ ixs: [
1723
+ ...(await this.flavor.preIxs({ signer: depositor })),
1724
+ ix,
1725
+ ...(await this.flavor.postIxs({ signer: depositor })),
1726
+ ],
1727
+ setupIxs: [tokenSyAtaIx, tokenYtAtaIx, tokenPtAtaIx, tokenBaseAtaIx, tokenLpAtaIx],
1728
+ }
1729
+ }
1730
+
1731
+ async ixProvideLiquidityClassic({
1732
+ depositor,
1733
+ amountBase,
1734
+ amountPt,
1735
+ minLpOut,
1736
+ tokenSyDepositor,
1737
+ tokenYtDepositor,
1738
+ tokenPtDepositor,
1739
+ tokenBaseDepositor,
1740
+ tokenLpDepositor,
1741
+ }: {
1742
+ depositor: web3.PublicKey
1743
+ amountBase: bigint
1744
+ amountPt: bigint
1745
+ minLpOut: bigint
1746
+ tokenSyDepositor?: web3.PublicKey
1747
+ tokenYtDepositor?: web3.PublicKey
1748
+ tokenPtDepositor?: web3.PublicKey
1749
+ tokenBaseDepositor?: web3.PublicKey
1750
+ tokenLpDepositor?: web3.PublicKey
1751
+ }) {
1752
+ tokenSyDepositor = tokenSyDepositor || getAssociatedTokenAddressSync(this.mintSy, depositor, true, TOKEN_PROGRAM_ID)
1753
+ tokenYtDepositor = tokenYtDepositor || getAssociatedTokenAddressSync(this.mintYt, depositor, true, TOKEN_PROGRAM_ID)
1754
+ tokenPtDepositor = tokenPtDepositor || getAssociatedTokenAddressSync(this.mintPt, depositor, true, TOKEN_PROGRAM_ID)
1755
+ tokenBaseDepositor =
1756
+ tokenBaseDepositor ||
1757
+ getAssociatedTokenAddressSync(this.flavor.mintBase, depositor, true, this.flavor.baseTokenProgram)
1758
+ tokenLpDepositor = tokenLpDepositor || getAssociatedTokenAddressSync(this.mintLp, depositor, true, TOKEN_PROGRAM_ID)
1759
+
1760
+ const tokenSyAtaIx = createAssociatedTokenAccountIdempotentInstruction(
1761
+ depositor,
1762
+ tokenSyDepositor,
1763
+ depositor,
1764
+ this.mintSy,
1765
+ )
1766
+
1767
+ const tokenYtAtaIx = createAssociatedTokenAccountIdempotentInstruction(
1768
+ depositor,
1769
+ tokenYtDepositor,
1770
+ depositor,
1771
+ this.mintYt,
1772
+ )
1773
+
1774
+ const tokenPtAtaIx = createAssociatedTokenAccountIdempotentInstruction(
1775
+ depositor,
1776
+ tokenPtDepositor,
1777
+ depositor,
1778
+ this.mintPt,
1779
+ )
1780
+
1781
+ const tokenLpAtaIx = createAssociatedTokenAccountIdempotentInstruction(
1782
+ depositor,
1783
+ tokenLpDepositor,
1784
+ depositor,
1785
+ this.mintLp,
1786
+ )
1787
+
1788
+ const lpPosition = this.xponPda.marketLpPosition({ market: this.selfAddress, owner: depositor })
1789
+
1790
+ const remainingAccounts = uniqueRemainingAccounts([
1791
+ ...this.cpiAccounts.depositSy,
1792
+ ...this.cpiAccounts.getPositionState,
1793
+ ])
1794
+
1795
+ const mintSyIx = await this.flavor.ixMintSy({
1796
+ amountBase: "0",
1797
+ depositor,
1798
+ depositorBaseTokenAccount: tokenBaseDepositor,
1799
+ depositorSyTokenAccount: tokenSyDepositor,
1800
+ })
1801
+
1802
+ const mintSyRemAccounts = mintSyIx.keys
1803
+
1804
+ const ix = await this.coreProgram.methods
1805
+ .wrapperProvideLiquidityClassic(
1806
+ new BN(amountBase.toString()),
1807
+ new BN(amountPt.toString()),
1808
+ new BN(minLpOut.toString()),
1809
+ mintSyRemAccounts.length,
1810
+ )
1811
+ .accountsStrict({
1812
+ depositor,
1813
+ lpPosition,
1814
+ market: this.selfAddress,
1815
+ tokenPtEscrow: this.tokenPtEscrow,
1816
+ tokenSyEscrow: this.tokenSyEscrow,
1817
+ tokenLpDst: tokenLpDepositor,
1818
+ mintLp: this.mintLp,
1819
+ syProgram: this.syProgram,
1820
+ tokenLpEscrow: this.tokenLpEscrow,
1821
+ tokenProgram: TOKEN_PROGRAM_ID,
1822
+ marketAddressLookupTable: this.addressLookupTable,
1823
+ tokenPtDepositor,
1824
+ tokenSyDepositor,
1825
+ systemProgram: web3.SystemProgram.programId,
1826
+ eventAuthority: this.vault.eventAuthority,
1827
+ program: this.coreProgram.programId,
1828
+ })
1829
+ .remainingAccounts(mintSyRemAccounts.concat(remainingAccounts))
1830
+ .instruction()
1831
+
1832
+ return {
1833
+ ixs: [
1834
+ ...(await this.flavor.preIxs({ signer: depositor })),
1835
+ ix,
1836
+ ...(await this.flavor.postIxs({ signer: depositor })),
1837
+ ],
1838
+ setupIxs: [tokenSyAtaIx, tokenYtAtaIx, tokenPtAtaIx, tokenLpAtaIx],
1839
+ }
1840
+ }
1841
+
1842
+ async ixWithdrawLiquidityToBase({
1843
+ owner,
1844
+ amountLp,
1845
+ minBaseOut,
1846
+ tokenSyWithdrawer,
1847
+ tokenYtWithdrawer,
1848
+ tokenPtWithdrawer,
1849
+ tokenBaseWithdrawer,
1850
+ tokenLpWithdrawer,
1851
+ }: {
1852
+ owner: web3.PublicKey
1853
+ amountLp: bigint
1854
+ minBaseOut: bigint
1855
+ tokenSyWithdrawer?: web3.PublicKey
1856
+ tokenYtWithdrawer?: web3.PublicKey
1857
+ tokenPtWithdrawer?: web3.PublicKey
1858
+ tokenBaseWithdrawer?: web3.PublicKey
1859
+ tokenLpWithdrawer?: web3.PublicKey
1860
+ }) {
1861
+ tokenSyWithdrawer = tokenSyWithdrawer || getAssociatedTokenAddressSync(this.mintSy, owner, true, TOKEN_PROGRAM_ID)
1862
+ tokenYtWithdrawer = tokenYtWithdrawer || getAssociatedTokenAddressSync(this.mintYt, owner, true, TOKEN_PROGRAM_ID)
1863
+ tokenPtWithdrawer = tokenPtWithdrawer || getAssociatedTokenAddressSync(this.mintPt, owner, true, TOKEN_PROGRAM_ID)
1864
+ tokenBaseWithdrawer =
1865
+ tokenBaseWithdrawer ||
1866
+ getAssociatedTokenAddressSync(this.flavor.mintBase, owner, true, this.flavor.baseTokenProgram)
1867
+ tokenLpWithdrawer = tokenLpWithdrawer || getAssociatedTokenAddressSync(this.mintLp, owner, true, TOKEN_PROGRAM_ID)
1868
+
1869
+ const tokenSyAtaIx = createAssociatedTokenAccountIdempotentInstruction(owner, tokenSyWithdrawer, owner, this.mintSy)
1870
+ const tokenYtAtaIx = createAssociatedTokenAccountIdempotentInstruction(owner, tokenYtWithdrawer, owner, this.mintYt)
1871
+ const tokenPtAtaIx = createAssociatedTokenAccountIdempotentInstruction(owner, tokenPtWithdrawer, owner, this.mintPt)
1872
+ const tokenBaseAtaIx = createAssociatedTokenAccountIdempotentInstruction(
1873
+ owner,
1874
+ tokenBaseWithdrawer,
1875
+ owner,
1876
+ this.flavor.mintBase,
1877
+ this.flavor.baseTokenProgram,
1878
+ )
1879
+ const tokenLpAtaIx = createAssociatedTokenAccountIdempotentInstruction(owner, tokenLpWithdrawer, owner, this.mintLp)
1880
+
1881
+ const lpPosition = this.xponPda.marketLpPosition({ market: this.selfAddress, owner })
1882
+
1883
+ const remainingAccounts = uniqueRemainingAccounts([
1884
+ ...this.cpiAccounts.withdrawSy,
1885
+ ...this.cpiAccounts.getPositionState,
1886
+ ])
1887
+
1888
+ const redeemSyIx = await this.flavor.ixRedeemSy({
1889
+ amountSy: "0",
1890
+ redeemer: owner,
1891
+ redeemerBaseTokenAccount: tokenBaseWithdrawer,
1892
+ redeemerSyTokenAccount: tokenSyWithdrawer,
1893
+ })
1894
+
1895
+ const redeemSyRemAccounts = redeemSyIx.keys
1896
+
1897
+ const minSyOut = Number(minBaseOut) / this.currentSyExchangeRate
1898
+
1899
+ const ix = await this.coreProgram.methods
1900
+ .wrapperWithdrawLiquidity(
1901
+ new BN(amountLp.toString()),
1902
+ new BN(minSyOut.toFixed(0).toString()),
1903
+ redeemSyRemAccounts.length,
1904
+ )
1905
+ .accountsStrict({
1906
+ market: this.selfAddress,
1907
+ tokenPtEscrow: this.tokenPtEscrow,
1908
+ tokenSyEscrow: this.tokenSyEscrow,
1909
+ tokenLpSrc: tokenLpWithdrawer,
1910
+ lpPosition,
1911
+ marketAddressLookupTable: this.addressLookupTable,
1912
+ eventAuthority: this.vault.eventAuthority,
1913
+ program: this.coreProgram.programId,
1914
+ syProgram: this.syProgram,
1915
+ systemProgram: web3.SystemProgram.programId,
1916
+ tokenFeeTreasurySy: this.state.tokenFeeTreasurySy,
1917
+ tokenLpEscrow: this.tokenLpEscrow,
1918
+ tokenProgram: TOKEN_PROGRAM_ID,
1919
+ tokenPtWithdrawer,
1920
+ tokenSyWithdrawer,
1921
+ mintLp: this.mintLp,
1922
+ withdrawer: owner,
1923
+ })
1924
+ .remainingAccounts(redeemSyRemAccounts.concat(remainingAccounts))
1925
+ .instruction()
1926
+
1927
+ return {
1928
+ ixs: [...(await this.flavor.preIxs({ signer: owner })), ix, ...(await this.flavor.postIxs({ signer: owner }))],
1929
+ setupIxs: [tokenSyAtaIx, tokenYtAtaIx, tokenPtAtaIx, tokenBaseAtaIx, tokenLpAtaIx],
1930
+ }
1931
+ }
1932
+
1933
+ async ixWithdrawLiquidityClassic({
1934
+ owner,
1935
+ amountLp,
1936
+ minBaseOut,
1937
+ tokenSyWithdrawer,
1938
+ tokenYtWithdrawer,
1939
+ tokenPtWithdrawer,
1940
+ tokenBaseWithdrawer,
1941
+ tokenLpWithdrawer,
1942
+ }: {
1943
+ owner: web3.PublicKey
1944
+ amountLp: bigint
1945
+ minBaseOut: bigint
1946
+ tokenSyWithdrawer?: web3.PublicKey
1947
+ tokenYtWithdrawer?: web3.PublicKey
1948
+ tokenPtWithdrawer?: web3.PublicKey
1949
+ tokenBaseWithdrawer?: web3.PublicKey
1950
+ tokenLpWithdrawer?: web3.PublicKey
1951
+ }) {
1952
+ tokenSyWithdrawer = tokenSyWithdrawer || getAssociatedTokenAddressSync(this.mintSy, owner, true, TOKEN_PROGRAM_ID)
1953
+ tokenYtWithdrawer = tokenYtWithdrawer || getAssociatedTokenAddressSync(this.mintYt, owner, true, TOKEN_PROGRAM_ID)
1954
+ tokenPtWithdrawer = tokenPtWithdrawer || getAssociatedTokenAddressSync(this.mintPt, owner, true, TOKEN_PROGRAM_ID)
1955
+ tokenBaseWithdrawer =
1956
+ tokenBaseWithdrawer ||
1957
+ getAssociatedTokenAddressSync(this.flavor.mintBase, owner, true, this.flavor.baseTokenProgram)
1958
+ tokenLpWithdrawer = tokenLpWithdrawer || getAssociatedTokenAddressSync(this.mintLp, owner, true, TOKEN_PROGRAM_ID)
1959
+
1960
+ const tokenSyAtaIx = createAssociatedTokenAccountIdempotentInstruction(owner, tokenSyWithdrawer, owner, this.mintSy)
1961
+ const tokenYtAtaIx = createAssociatedTokenAccountIdempotentInstruction(owner, tokenYtWithdrawer, owner, this.mintYt)
1962
+ const tokenPtAtaIx = createAssociatedTokenAccountIdempotentInstruction(owner, tokenPtWithdrawer, owner, this.mintPt)
1963
+ const tokenBaseAtaIx = createAssociatedTokenAccountIdempotentInstruction(
1964
+ owner,
1965
+ tokenBaseWithdrawer,
1966
+ owner,
1967
+ this.flavor.mintBase,
1968
+ this.flavor.baseTokenProgram,
1969
+ )
1970
+ const tokenLpAtaIx = createAssociatedTokenAccountIdempotentInstruction(owner, tokenLpWithdrawer, owner, this.mintLp)
1971
+
1972
+ const lpPosition = this.xponPda.marketLpPosition({ market: this.selfAddress, owner })
1973
+
1974
+ const remainingAccounts = uniqueRemainingAccounts([
1975
+ ...this.cpiAccounts.withdrawSy,
1976
+ ...this.cpiAccounts.getPositionState,
1977
+ ])
1978
+
1979
+ const redeemSyIx = await this.flavor.ixRedeemSy({
1980
+ amountSy: "0",
1981
+ redeemer: owner,
1982
+ redeemerBaseTokenAccount: tokenBaseWithdrawer,
1983
+ redeemerSyTokenAccount: tokenSyWithdrawer,
1984
+ })
1985
+
1986
+ const redeemSyRemAccounts = redeemSyIx.keys
1987
+
1988
+ const ixn = await this.coreProgram.methods
1989
+ .wrapperWithdrawLiquidityClassic(new BN(amountLp.toString()), redeemSyRemAccounts.length)
1990
+ .accountsStrict({
1991
+ market: this.selfAddress,
1992
+ tokenPtEscrow: this.tokenPtEscrow,
1993
+ tokenSyEscrow: this.tokenSyEscrow,
1994
+ tokenLpSrc: tokenLpWithdrawer,
1995
+ lpPosition,
1996
+ marketAddressLookupTable: this.addressLookupTable,
1997
+ eventAuthority: this.vault.eventAuthority,
1998
+ program: this.coreProgram.programId,
1999
+ syProgram: this.syProgram,
2000
+ systemProgram: web3.SystemProgram.programId,
2001
+ tokenLpEscrow: this.tokenLpEscrow,
2002
+ tokenProgram: TOKEN_PROGRAM_ID,
2003
+ tokenPtWithdrawer,
2004
+ tokenSyWithdrawer,
2005
+ mintLp: this.mintLp,
2006
+ withdrawer: owner,
2007
+ })
2008
+ .remainingAccounts(redeemSyRemAccounts.concat(remainingAccounts))
2009
+ .instruction()
2010
+
2011
+ return {
2012
+ ixs: [...(await this.flavor.preIxs({ signer: owner })), ixn, ...(await this.flavor.postIxs({ signer: owner }))],
2013
+ setupIxs: [tokenSyAtaIx, tokenYtAtaIx, tokenPtAtaIx, tokenBaseAtaIx, tokenLpAtaIx],
2014
+ }
2015
+ }
2016
+
2017
+ async claimFarmEmissions({
2018
+ owner,
2019
+ mint,
2020
+ tokenProgram,
2021
+ tokenDst,
2022
+ }: {
2023
+ owner: web3.PublicKey
2024
+ mint: web3.PublicKey
2025
+ tokenProgram: web3.PublicKey
2026
+ tokenDst?: web3.PublicKey
2027
+ }) {
2028
+ tokenDst = tokenDst || getAssociatedTokenAddressSync(mint, owner, true, tokenProgram)
2029
+ const lpPosition = this.xponPda.marketLpPosition({ market: this.selfAddress, owner })
2030
+
2031
+ const tokenFarm = getAssociatedTokenAddressSync(mint, this.selfAddress, true, tokenProgram)
2032
+ const tokenAtaIx = createAssociatedTokenAccountIdempotentInstruction(owner, tokenDst, owner, mint)
2033
+
2034
+ const ix = await this.coreProgram.methods
2035
+ .claimFarmEmissions({ all: {} })
2036
+ .accountsStrict({
2037
+ market: this.selfAddress,
2038
+ lpPosition,
2039
+ mint,
2040
+ tokenFarm,
2041
+ owner,
2042
+ tokenDst,
2043
+ tokenProgram,
2044
+ eventAuthority: this.eventAuthority,
2045
+ program: this.coreProgram.programId,
2046
+ })
2047
+ .instruction()
2048
+
2049
+ return {
2050
+ ixs: [ix],
2051
+ setupIxs: [tokenAtaIx],
2052
+ }
2053
+ }
2054
+
2055
+ async ixAddStandaloneEmission({
2056
+ signer,
2057
+ emissionMint,
2058
+ emissionTokenProgram = TOKEN_PROGRAM_ID,
2059
+ altAddresses,
2060
+ cpiAccounts,
2061
+ }: {
2062
+ signer: web3.PublicKey
2063
+ emissionMint: web3.PublicKey
2064
+ emissionTokenProgram?: web3.PublicKey
2065
+ altAddresses: web3.PublicKey[]
2066
+ cpiAccounts: CpiAccountsRaw
2067
+ }) {
2068
+ const altUtil = extendAddressLookupTable(altAddresses, cpiAccounts)
2069
+
2070
+ const tokenEmission = getAssociatedTokenAddressSync(emissionMint, this.selfAddress, true, emissionTokenProgram)
2071
+ const ix = await this.coreProgram.methods
2072
+ .addMarketEmission(altUtil.cpiAccountIndexes)
2073
+ .accountsStrict({
2074
+ market: this.selfAddress,
2075
+ adminState: getExponentAdminStatePda(),
2076
+ feePayer: signer,
2077
+ mintNew: emissionMint,
2078
+ signer,
2079
+ systemProgram: web3.SystemProgram.programId,
2080
+ tokenEmission,
2081
+ tokenProgram: emissionTokenProgram,
2082
+ })
2083
+ .instruction()
2084
+
2085
+ return {
2086
+ extendAddressLookupTableExtensionAccounts: altUtil.addressLookupTableExtension,
2087
+ addMarketEmissionIx: ix,
2088
+ }
2089
+ }
2090
+
2091
+ async addFarm({
2092
+ signer,
2093
+ farmMint,
2094
+ farmRewardTokenProgram,
2095
+ emissionsRate,
2096
+ untilTimestamp,
2097
+ farmRewardSrc,
2098
+ feePayer,
2099
+ }: {
2100
+ signer: web3.PublicKey
2101
+ farmMint: web3.PublicKey
2102
+ farmRewardTokenProgram: web3.PublicKey
2103
+ emissionsRate: number
2104
+ untilTimestamp: number
2105
+ farmRewardSrc?: web3.PublicKey
2106
+ feePayer?: web3.PublicKey
2107
+ }) {
2108
+ feePayer = feePayer || signer
2109
+ const farmRewardEscrow = getAssociatedTokenAddressSync(farmMint, this.selfAddress, true, farmRewardTokenProgram)
2110
+ farmRewardSrc = farmRewardSrc || getAssociatedTokenAddressSync(farmMint, signer, true, farmRewardTokenProgram)
2111
+
2112
+ const ix = await this.coreProgram.methods
2113
+ .addFarm(new BN(emissionsRate), untilTimestamp)
2114
+ .accountsStrict({
2115
+ adminState: getExponentAdminStatePda(),
2116
+ market: this.selfAddress,
2117
+ feePayer,
2118
+ signer,
2119
+ mintNew: farmMint,
2120
+ tokenFarm: farmRewardEscrow,
2121
+ tokenProgram: farmRewardTokenProgram,
2122
+ systemProgram: web3.SystemProgram.programId,
2123
+ tokenSource: farmRewardSrc,
2124
+ })
2125
+ .instruction()
2126
+
2127
+ return {
2128
+ ixs: [ix],
2129
+ }
2130
+ }
2131
+
2132
+ /** Calculate available liquidity for PT trades
2133
+ * @param is_buy - true if buying PT with SY, false if selling PT for SY
2134
+ * @param size_pt - amount of PT to trade (in PT decimals)
2135
+ * @returns amount of PT that can be traded given current liquidity
2136
+ */
2137
+ liquidityAvailable(is_buy: boolean, size_pt: bigint): bigint {
2138
+ const calc = this.marketCalculator()
2139
+
2140
+ if (is_buy) {
2141
+ // When buying PT, we're limited by SY liquidity
2142
+ // Convert PT amount to SY using current price
2143
+ const syNeeded = Number(size_pt) * calc.exchangeRate
2144
+ // Available SY in the market
2145
+ const syAvailable = Number(this.syBalance)
2146
+
2147
+ if (syNeeded <= syAvailable) {
2148
+ return size_pt
2149
+ } else {
2150
+ // Calculate max PT that can be bought with available SY
2151
+ return BigInt(Math.floor(syAvailable / calc.exchangeRate))
2152
+ }
2153
+ } else {
2154
+ // When selling PT, we're limited by PT liquidity in the market
2155
+ const ptAvailable = this.ptBalance
2156
+
2157
+ if (size_pt <= ptAvailable) {
2158
+ return size_pt
2159
+ } else {
2160
+ return ptAvailable
2161
+ }
2162
+ }
2163
+ }
2164
+ }
2165
+
2166
+ export type MarketJson = {
2167
+ /** The market's public key */
2168
+ address: string
2169
+ /** The market's address lookup table public key */
2170
+ addressLookupTable: string
2171
+ /** The market's SY escrow account public key - this is a pass-through account which moves SY between an end-user and the SY program where it is deposited and withdrawn from */
2172
+ tokenSyEscrow: string
2173
+ /** The market's PT escrow account public key - this account holds the market's PT liquidity*/
2174
+ tokenPtEscrow: string
2175
+ /** The market's LP escrow account public key - this account holds the market's LP tokens which are used to farm rewards */
2176
+ tokenLpEscrow: string
2177
+
2178
+ /** Token account that holds the market's SY fees */
2179
+ treasurySyTokenAccount: string
2180
+
2181
+ /** The market's LP supply */
2182
+ lpSupply: string
2183
+
2184
+ /** The SY program ID */
2185
+ syProgram: string
2186
+
2187
+ mintPt: string
2188
+ mintSy: string
2189
+ mintLp: string
2190
+
2191
+ /** The market's PT balance */
2192
+ ptBalance: string
2193
+ /** The market's SY balance */
2194
+ syBalance: string
2195
+
2196
+ /** The market's LP escrow amount */
2197
+ lpEscrowAmount: string
2198
+
2199
+ /** The market's current SY rate */
2200
+ currentSyRate: number
2201
+
2202
+ /** The market's expiration timestamp */
2203
+ expirationTs: number
2204
+
2205
+ /** The market's expiration date */
2206
+ expirationDate: string
2207
+
2208
+ /** The number of seconds remaining until the market expires */
2209
+ secondsRemaining: number
2210
+
2211
+ /** The market's maximum LP supply */
2212
+ maxLpSupply: string
2213
+
2214
+ lnFeeRateRoot: number
2215
+
2216
+ rateScalarRoot: number
2217
+
2218
+ feeTreasurySyBps: number
2219
+
2220
+ lastLnImpliedRate: number
2221
+
2222
+ /** The current rate scalar, taking into account the time decay*/
2223
+ currentRateScalar: number
2224
+
2225
+ /** The current rate anchor that preserves the last implied rate */
2226
+ currentRateAnchor: number
2227
+
2228
+ /** How many SY are required to buy 1 PT */
2229
+ currentPtPriceInSy: number
2230
+
2231
+ /** How many assets are required to buy 1 PT */
2232
+ currentPtPriceInAsset: number
2233
+
2234
+ /** Proportion of PT in terms of assets */
2235
+ proportionPtInAsset: number
2236
+
2237
+ /** Proportion of PT in terms of SY */
2238
+ proportionPtInSy: number
2239
+
2240
+ /** Current fee rate */
2241
+ currentFeeRate: number
2242
+
2243
+ /** CPI accounts */
2244
+ cpiAccounts: CpiAccountsRawJson
2245
+
2246
+ /** Annualized yield for PT */
2247
+ ptApr: number
2248
+
2249
+ /** Emission tokens */
2250
+ emissions: {
2251
+ trackers: {
2252
+ tokenEscrow: string
2253
+ lpShareIndex: number
2254
+ lastSeenStaged: number
2255
+ }[]
2256
+ }
2257
+
2258
+ liquidityNetBalanceLimits: {
2259
+ windowStartTimestamp: number
2260
+ windowStartNetBalance: string
2261
+ maxNetBalanceChangeNegativePercentage: number
2262
+ maxNetBalanceChangePositivePercentage: number
2263
+ windowDurationSeconds: number
2264
+ }
2265
+
2266
+ lpFarm: {
2267
+ lastSeenTimestamp: number
2268
+ farmEmissions: {
2269
+ mint: string
2270
+ /** Tokens per second */
2271
+ tokenRate: string
2272
+ expiryTimestamp: number
2273
+ /** Index for converting LP shares into earned emissions */
2274
+ index: string
2275
+ }[]
2276
+ }
2277
+ }
2278
+
2279
+ function toJson(m: Market): MarketJson {
2280
+ const c = m.marketCalculator()
2281
+ return {
2282
+ treasurySyTokenAccount: m.state.tokenFeeTreasurySy.toBase58(),
2283
+ address: m.selfAddress.toBase58(),
2284
+ addressLookupTable: m.addressLookupTable.toBase58(),
2285
+ tokenSyEscrow: m.tokenSyEscrow.toBase58(),
2286
+ tokenPtEscrow: m.tokenPtEscrow.toBase58(),
2287
+ tokenLpEscrow: m.tokenLpEscrow.toBase58(),
2288
+ lpSupply: m.state.lpSupply.toString(),
2289
+ syProgram: m.state.syProgram.toBase58(),
2290
+ mintPt: m.state.mintPt.toBase58(),
2291
+ mintSy: m.state.mintSy.toBase58(),
2292
+ mintLp: m.state.mintLp.toBase58(),
2293
+ feeTreasurySyBps: m.state.feeTreasurySyBps,
2294
+ ptBalance: m.state.ptBalance.toString(),
2295
+ syBalance: m.state.syBalance.toString(),
2296
+ currentSyRate: m.flavor.currentSyExchangeRate,
2297
+ expirationTs: m.state.expirationTs,
2298
+ expirationDate: new Date(m.state.expirationTs * 1000).toISOString(),
2299
+ lnFeeRateRoot: m.state.lnFeeRateRoot,
2300
+ rateScalarRoot: m.state.rateScalarRoot,
2301
+ lastLnImpliedRate: m.state.lastLnImpliedRate,
2302
+ currentPtPriceInSy: m.currentPtPriceInSy,
2303
+ currentPtPriceInAsset: m.currentPtPriceInAsset,
2304
+ secondsRemaining: m.secondsRemaining,
2305
+ proportionPtInAsset: c.proportionPtInAsset,
2306
+ proportionPtInSy: c.proportionPtInSy,
2307
+ currentFeeRate: c.feeRate,
2308
+ currentRateScalar: c.rateScalar,
2309
+ currentRateAnchor: c.findRateAnchor(),
2310
+ cpiAccounts: serializeCpiAccountsRaw(m.cpiAccounts),
2311
+ emissions: {
2312
+ trackers: m.marketEmissions.trackers.map((e) => ({
2313
+ tokenEscrow: e.tokenEscrow.toBase58(),
2314
+ lpShareIndex: e.lpShareIndex,
2315
+ lastSeenStaged: e.lastSeenStaged,
2316
+ })),
2317
+ },
2318
+ ptApr: m.ptApr,
2319
+ lpEscrowAmount: m.state.lpEscrowAmount.toString(),
2320
+ maxLpSupply: m.state.maxLpSupply.toString(),
2321
+ liquidityNetBalanceLimits: {
2322
+ windowStartTimestamp: m.state.liquidityNetBalanceLimits.windowStartTimestamp,
2323
+ windowStartNetBalance: m.state.liquidityNetBalanceLimits.windowStartNetBalance.toString(),
2324
+ maxNetBalanceChangeNegativePercentage: m.state.liquidityNetBalanceLimits.maxNetBalanceChangeNegativePercentage,
2325
+ maxNetBalanceChangePositivePercentage: m.state.liquidityNetBalanceLimits.maxNetBalanceChangePositivePercentage,
2326
+ windowDurationSeconds: m.state.liquidityNetBalanceLimits.windowDurationSeconds,
2327
+ },
2328
+ lpFarm: {
2329
+ lastSeenTimestamp: m.state.lpFarm.lastSeenTimestamp,
2330
+ farmEmissions: m.state.lpFarm.farmEmissions.map((e) => ({
2331
+ mint: e.mint.toBase58(),
2332
+ tokenRate: e.tokenRate.toString(),
2333
+ expiryTimestamp: e.expiryTimestamp,
2334
+ index: PreciseNumber.fromRaw(e.index[0]).valueString,
2335
+ })),
2336
+ },
2337
+ }
2338
+ }