@jup-ag/lend 0.2.0-beta.1 → 0.2.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,867 @@
1
+ import { SYSVAR_RENT_PUBKEY, PublicKey, SYSVAR_INSTRUCTIONS_PUBKEY, SystemProgram } from '@solana/web3.js';
2
+ import BN from 'bn.js';
3
+ import { Program } from '@coral-xyz/anchor';
4
+ import { g as getVaultsProgram, k as getAccountOwner, u as getTickIndices, p as getLiquidityProgramId, c as MIN_I128, m as getCurrentPositionState, n as getFinalPosition, z as readOraclePrice, t as getTickAtRatio, w as loadRelevantBranchesForLiquidate, y as loadRelevantTicksHasDebtArraysLiquidate, D as oracle, v as loadRelevantBranches, x as loadRelevantTicksHasDebtArrays, d as MIN_TICK, j as findNextTickWithDebt, I as INIT_TICK } from './lend.CVtn-ehf.mjs';
5
+ import { ASSOCIATED_TOKEN_PROGRAM_ID, TOKEN_PROGRAM_ID, getAssociatedTokenAddressSync, NATIVE_MINT } from '@solana/spl-token';
6
+ import { c as getOrCreateATAInstruction } from './lend.B80NmXTG.mjs';
7
+ import { a as getPositionMetadata, c as getPositionTokenAccount, d as getPositionMint, e as getPosition, f as getVaultState, g as getVaultAdmin, h as getVaultConfig, i as getVaultMetadata, j as getLiquidity, k as getClaimAccount, l as getRateModel, m as getUserBorrowPosition, n as getUserSupplyPosition, o as getLiquidityReserve, p as getTickHasDebt, q as getTick, r as getBranch, s as getTickIdLiquidation } from './lend.a53XYkl5.mjs';
8
+
9
+ const MPL_TOKEN_METADATA_PROGRAM_ID = new PublicKey(
10
+ "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"
11
+ );
12
+ async function getOtherInstructionsOperate(vaultId, vaultState, currentPosition, finalPosition, currentTick, currentTickId, program, signer, market) {
13
+ const otherIxs = [];
14
+ const tickToRead = [currentTick];
15
+ let finalTickData;
16
+ tickToRead.push(currentPosition.tick);
17
+ tickToRead.push(finalPosition.tick);
18
+ if (tickToRead.length > 0) {
19
+ const tickData = await program.account.tick.fetchMultiple(
20
+ tickToRead.map((tick) => getTick(vaultId, tick, market))
21
+ );
22
+ tickData[0];
23
+ finalTickData = tickData[2];
24
+ for (const [i, tickDatum] of tickData.entries()) {
25
+ if (!tickDatum) {
26
+ const ix = await program.methods.initTick(vaultId, tickToRead[i]).accounts(getInitTickContext(vaultId, tickToRead[i], signer, market)).instruction();
27
+ otherIxs.push(ix);
28
+ }
29
+ }
30
+ }
31
+ let newBranchId;
32
+ if (vaultState.branchLiquidated) {
33
+ newBranchId = vaultState.totalBranchId + 1;
34
+ let newBranchData = null;
35
+ try {
36
+ newBranchData = await program.account.branch.fetch(
37
+ getBranch(vaultId, newBranchId, market)
38
+ );
39
+ } catch {
40
+ }
41
+ if (!newBranchData) {
42
+ const ix = await program.methods.initBranch(vaultId, newBranchId).accounts(getInitBranchContext(vaultId, newBranchId, signer, market)).instruction();
43
+ otherIxs.push(ix);
44
+ }
45
+ } else {
46
+ newBranchId = vaultState.currentBranchId;
47
+ }
48
+ const newBranchPda = getBranch(vaultId, newBranchId, market);
49
+ const currentTickIdDataPda = getTickIdLiquidation(
50
+ vaultId,
51
+ currentTick,
52
+ // Position tick
53
+ currentTickId,
54
+ // Position tick ID
55
+ market
56
+ );
57
+ const tickIdsToRead = [
58
+ {
59
+ tick: currentTick,
60
+ totalIds: currentTickId
61
+ }
62
+ ];
63
+ const finalTickIdDataPda = getTickIdLiquidation(
64
+ vaultId,
65
+ finalPosition.tick,
66
+ finalTickData ? finalTickData.totalIds : 0,
67
+ market
68
+ );
69
+ if (finalPosition.tick !== currentTick)
70
+ if (finalTickData) {
71
+ tickIdsToRead.push({
72
+ tick: finalPosition.tick,
73
+ totalIds: finalTickData.totalIds
74
+ });
75
+ } else {
76
+ const context = await getInitTickIdLiquidationContext(
77
+ vaultId,
78
+ finalPosition.tick,
79
+ signer,
80
+ program,
81
+ market
82
+ );
83
+ const ix = await program.methods.initTickIdLiquidation(vaultId, finalPosition.tick, 0).accounts(context).instruction();
84
+ otherIxs.push(ix);
85
+ }
86
+ const tickIdData = await program.account.tickIdLiquidation.fetchMultiple(
87
+ tickIdsToRead.map(
88
+ ({ tick, totalIds }) => getTickIdLiquidation(vaultId, tick, totalIds, market)
89
+ )
90
+ );
91
+ if (tickIdData.length > 0) {
92
+ for (const [i, tickIdDatum] of tickIdData.entries()) {
93
+ if (!tickIdDatum) {
94
+ const ix = await program.methods.initTickIdLiquidation(
95
+ vaultId,
96
+ tickIdsToRead[i].tick,
97
+ tickIdsToRead[i].totalIds
98
+ ).accounts(
99
+ await getInitTickIdLiquidationContext(
100
+ vaultId,
101
+ tickIdsToRead[i].tick,
102
+ signer,
103
+ program,
104
+ market
105
+ )
106
+ ).instruction();
107
+ otherIxs.push(ix);
108
+ }
109
+ }
110
+ }
111
+ return {
112
+ otherIxs,
113
+ newBranchPda,
114
+ currentTickIdDataPda,
115
+ finalTickIdDataPda
116
+ };
117
+ }
118
+ const tickHelper = (tickValue) => {
119
+ return tickValue === 0 ? INIT_TICK : tickValue;
120
+ };
121
+ async function getRemainingAccountsOperate(vaultId, vaultState, vaultConfig, finalPositionTick, existingPositionTick, liquidationStatus, postLiquidationBranchId, program, market) {
122
+ const remainingAccounts = [];
123
+ const oracleProgram = new Program(oracle, program.provider);
124
+ const [oracleData, branches, tickHasDebt] = await Promise.all([
125
+ oracleProgram.account.oracle.fetch(new PublicKey(vaultConfig.oracle)),
126
+ // Add branch accounts (next 10 remaining accounts)
127
+ loadRelevantBranches(
128
+ vaultId,
129
+ vaultState,
130
+ liquidationStatus,
131
+ postLiquidationBranchId,
132
+ program,
133
+ market
134
+ ),
135
+ loadRelevantTicksHasDebtArrays(
136
+ vaultId,
137
+ tickHelper(vaultState.topmostTick),
138
+ existingPositionTick,
139
+ finalPositionTick,
140
+ program,
141
+ market
142
+ )
143
+ ]);
144
+ const sourceLength = oracleData.sources.length;
145
+ for (const source of oracleData.sources)
146
+ remainingAccounts.push({
147
+ pubkey: new PublicKey(source.source),
148
+ isWritable: false,
149
+ isSigner: false
150
+ });
151
+ const branchLength = branches.length;
152
+ for (const branch of branches) {
153
+ remainingAccounts.push({
154
+ pubkey: getBranch(vaultId, branch, market),
155
+ isWritable: true,
156
+ isSigner: false
157
+ });
158
+ }
159
+ const tickHasDebtLength = tickHasDebt.length;
160
+ for (const tickHasDebtArray of tickHasDebt)
161
+ remainingAccounts.push({
162
+ pubkey: tickHasDebtArray,
163
+ isWritable: true,
164
+ isSigner: false
165
+ });
166
+ const remainingAccountsIndices = [
167
+ sourceLength,
168
+ branchLength,
169
+ tickHasDebtLength
170
+ ];
171
+ return {
172
+ remainingAccounts,
173
+ remainingAccountsIndices
174
+ };
175
+ }
176
+ async function getOperateContext({
177
+ vaultId,
178
+ positionId,
179
+ program,
180
+ connection,
181
+ signer,
182
+ positionOwner = signer,
183
+ colAmount: newCol,
184
+ debtAmount: newDebt,
185
+ recipient,
186
+ market
187
+ }) {
188
+ program = program ?? getVaultsProgram({ connection, signer, market });
189
+ const [vaultState, vaultConfig, vaultMetadata] = await Promise.all([
190
+ program.account.vaultState.fetch(getVaultState(vaultId, market)),
191
+ program.account.vaultConfig.fetch(
192
+ getVaultConfig(vaultId, market)
193
+ ),
194
+ program.account.vaultMetadata.fetch(
195
+ getVaultMetadata(vaultId, market)
196
+ )
197
+ ]);
198
+ const [supplyTokenProgram, borrowTokenProgram] = await Promise.all([
199
+ getAccountOwner(vaultConfig.supplyToken, connection),
200
+ getAccountOwner(vaultConfig.borrowToken, connection)
201
+ ]);
202
+ const vaultSupplyDecimals = vaultMetadata.supplyMintDecimals;
203
+ const vaultBorrowDecimals = vaultMetadata.borrowMintDecimals;
204
+ if (newCol.gt(MIN_I128)) {
205
+ const decimalsDelta = vaultSupplyDecimals < 9 ? 9 - vaultSupplyDecimals : 0;
206
+ newCol = newCol.mul(new BN(10).pow(new BN(decimalsDelta)));
207
+ }
208
+ if (newDebt.gt(MIN_I128)) {
209
+ const decimalsDelta = vaultBorrowDecimals < 9 ? 9 - vaultBorrowDecimals : 0;
210
+ newDebt = newDebt.mul(new BN(10).pow(new BN(decimalsDelta)));
211
+ }
212
+ const positionData = positionId === 0 ? {
213
+ nftId: vaultState.nextPositionId,
214
+ positionMint: new PublicKey(0),
215
+ isSupplyOnlyPosition: 1,
216
+ tick: -2147483648,
217
+ tickId: 0,
218
+ lastUpdateTimestamp: new BN(0),
219
+ supplyAmount: new BN(0),
220
+ dustDebtAmount: new BN(0)} : await program.account.position.fetch(
221
+ getPosition(vaultId, positionId, market)
222
+ );
223
+ let existingPositionTick = positionData.tick;
224
+ let existingPositionTickId = positionData.tickId;
225
+ const currentPosition = await getCurrentPositionState({
226
+ vaultId,
227
+ position: positionData,
228
+ program,
229
+ market,
230
+ borrowMintDecimals: vaultBorrowDecimals
231
+ });
232
+ if (existingPositionTick === -2147483648) {
233
+ existingPositionTick = currentPosition.tick;
234
+ existingPositionTickId = 0;
235
+ }
236
+ const currentPositionTickPda = getTick(
237
+ vaultId,
238
+ existingPositionTick,
239
+ market
240
+ );
241
+ const finalPosition = await getFinalPosition({
242
+ vaultId,
243
+ currentPosition,
244
+ newColAmount: newCol,
245
+ newDebtAmount: newDebt,
246
+ program,
247
+ connection,
248
+ signer,
249
+ market
250
+ });
251
+ const { otherIxs, newBranchPda, currentTickIdDataPda, finalTickIdDataPda } = await getOtherInstructionsOperate(
252
+ vaultId,
253
+ vaultState,
254
+ currentPosition,
255
+ finalPosition,
256
+ existingPositionTick,
257
+ existingPositionTickId,
258
+ program,
259
+ signer,
260
+ market
261
+ );
262
+ const { remainingAccounts, remainingAccountsIndices } = await getRemainingAccountsOperate(
263
+ vaultId,
264
+ vaultState,
265
+ vaultConfig,
266
+ finalPosition.tick,
267
+ existingPositionTick,
268
+ currentPosition.userLiquidationStatus,
269
+ currentPosition.postLiquidationBranchId,
270
+ program,
271
+ market
272
+ );
273
+ const accounts = {
274
+ signer,
275
+ signerSupplyTokenAccount: getAssociatedTokenAddressSync(
276
+ vaultConfig.supplyToken,
277
+ signer,
278
+ true,
279
+ supplyTokenProgram
280
+ ),
281
+ signerBorrowTokenAccount: getAssociatedTokenAddressSync(
282
+ vaultConfig.borrowToken,
283
+ signer,
284
+ true,
285
+ borrowTokenProgram
286
+ ),
287
+ recipient: recipient ?? null,
288
+ recipientSupplyTokenAccount: recipient ? getAssociatedTokenAddressSync(
289
+ vaultConfig.supplyToken,
290
+ recipient,
291
+ true,
292
+ supplyTokenProgram
293
+ ) : null,
294
+ recipientBorrowTokenAccount: recipient ? getAssociatedTokenAddressSync(
295
+ vaultConfig.borrowToken,
296
+ recipient,
297
+ true,
298
+ borrowTokenProgram
299
+ ) : null,
300
+ vaultConfig: getVaultConfig(vaultId, market),
301
+ vaultState: getVaultState(vaultId, market),
302
+ supplyToken: vaultConfig.supplyToken,
303
+ borrowToken: vaultConfig.borrowToken,
304
+ oracle: new PublicKey(vaultConfig.oracle),
305
+ position: getPosition(vaultId, positionData.nftId, market),
306
+ positionTokenAccount: getPositionTokenAccount(
307
+ vaultId,
308
+ positionData.nftId,
309
+ positionOwner,
310
+ market
311
+ ),
312
+ currentPositionTick: currentPositionTickPda,
313
+ finalPositionTick: getTick(vaultId, finalPosition.tick, market),
314
+ currentPositionTickId: currentTickIdDataPda,
315
+ finalPositionTickId: finalTickIdDataPda,
316
+ newBranch: newBranchPda,
317
+ supplyTokenReservesLiquidity: getLiquidityReserve(
318
+ vaultConfig.supplyToken,
319
+ market
320
+ ),
321
+ borrowTokenReservesLiquidity: getLiquidityReserve(
322
+ vaultConfig.borrowToken,
323
+ market
324
+ ),
325
+ vaultSupplyPositionOnLiquidity: getUserSupplyPosition(
326
+ vaultConfig.supplyToken,
327
+ getVaultConfig(vaultId, market),
328
+ market
329
+ ),
330
+ vaultBorrowPositionOnLiquidity: getUserBorrowPosition(
331
+ vaultConfig.borrowToken,
332
+ getVaultConfig(vaultId, market),
333
+ market
334
+ ),
335
+ supplyRateModel: getRateModel(vaultConfig.supplyToken, market),
336
+ borrowRateModel: getRateModel(vaultConfig.borrowToken, market),
337
+ supplyTokenClaimAccount: null,
338
+ borrowTokenClaimAccount: null,
339
+ liquidity: getLiquidity(market),
340
+ liquidityProgram: getLiquidityProgramId(market),
341
+ vaultSupplyTokenAccount: getAssociatedTokenAddressSync(
342
+ vaultConfig.supplyToken,
343
+ getLiquidity(market),
344
+ true,
345
+ supplyTokenProgram
346
+ ),
347
+ vaultBorrowTokenAccount: getAssociatedTokenAddressSync(
348
+ vaultConfig.borrowToken,
349
+ getLiquidity(market),
350
+ true,
351
+ borrowTokenProgram
352
+ ),
353
+ oracleProgram: new PublicKey(vaultConfig.oracleProgram),
354
+ supplyTokenProgram,
355
+ borrowTokenProgram,
356
+ systemProgram: SystemProgram.programId
357
+ };
358
+ return {
359
+ nftId: positionData.nftId,
360
+ accounts,
361
+ remainingAccounts,
362
+ initPositionIx: positionId === 0 ? await program.methods.initPosition(vaultId, positionData.nftId).accounts(
363
+ getInitPositionContext(
364
+ vaultId,
365
+ positionData.nftId,
366
+ signer,
367
+ market
368
+ )
369
+ ).instruction() : null,
370
+ otherIxs,
371
+ remainingAccountsIndices,
372
+ lookupTable: vaultMetadata.lookupTable
373
+ };
374
+ }
375
+ async function getInitPositionIx({
376
+ vaultId,
377
+ connection,
378
+ signer,
379
+ market = "main"
380
+ }) {
381
+ const program = getVaultsProgram({ connection, signer, market });
382
+ const vaultState = await program.account.vaultState.fetch(
383
+ getVaultState(vaultId, market)
384
+ );
385
+ return {
386
+ ix: await program.methods.initPosition(vaultId, vaultState.nextPositionId).accounts(
387
+ getInitPositionContext(
388
+ vaultId,
389
+ vaultState.nextPositionId,
390
+ signer,
391
+ market
392
+ )
393
+ ).instruction(),
394
+ nftId: vaultState.nextPositionId
395
+ };
396
+ }
397
+ function getInitPositionContext(vaultId, positionId, signer, market) {
398
+ return {
399
+ signer,
400
+ vaultAdmin: getVaultAdmin(market),
401
+ vaultState: getVaultState(vaultId, market),
402
+ position: getPosition(vaultId, positionId, market),
403
+ positionMint: getPositionMint(vaultId, positionId, market),
404
+ positionTokenAccount: getPositionTokenAccount(
405
+ vaultId,
406
+ positionId,
407
+ signer,
408
+ market
409
+ ),
410
+ tokenProgram: TOKEN_PROGRAM_ID,
411
+ metadataAccount: getPositionMetadata(vaultId, positionId, market),
412
+ associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID,
413
+ systemProgram: SystemProgram.programId,
414
+ sysvarInstruction: SYSVAR_INSTRUCTIONS_PUBKEY,
415
+ metadataProgram: MPL_TOKEN_METADATA_PROGRAM_ID,
416
+ rent: SYSVAR_RENT_PUBKEY
417
+ };
418
+ }
419
+ const getOperateIx = async ({
420
+ vaultId,
421
+ positionId,
422
+ colAmount,
423
+ debtAmount,
424
+ connection,
425
+ signer,
426
+ recipient,
427
+ positionOwner,
428
+ market = "main",
429
+ includeATASetup = false
430
+ }) => {
431
+ const program = getVaultsProgram({ connection, signer, market });
432
+ const {
433
+ accounts,
434
+ remainingAccounts,
435
+ remainingAccountsIndices,
436
+ initPositionIx,
437
+ otherIxs,
438
+ lookupTable,
439
+ nftId
440
+ } = await getOperateContext({
441
+ vaultId,
442
+ positionId,
443
+ colAmount,
444
+ debtAmount,
445
+ signer,
446
+ positionOwner,
447
+ connection,
448
+ recipient,
449
+ program,
450
+ market
451
+ });
452
+ if (accounts.borrowToken.toString() !== NATIVE_MINT.toString() && debtAmount.lt(new BN(0))) {
453
+ try {
454
+ const userDebtTokenBalance = await connection.getTokenAccountBalance(
455
+ accounts.signerBorrowTokenAccount
456
+ );
457
+ if (userDebtTokenBalance.value.amount === debtAmount.abs().toString()) {
458
+ debtAmount = debtAmount.add(new BN(1));
459
+ }
460
+ } catch {
461
+ }
462
+ }
463
+ const [operateIx, addressLookupTable] = await Promise.all([
464
+ program.methods.operate(
465
+ colAmount,
466
+ debtAmount,
467
+ { direct: {} },
468
+ Buffer.from(remainingAccountsIndices)
469
+ ).accounts(accounts).remainingAccounts(remainingAccounts).instruction(),
470
+ connection.getAddressLookupTable(lookupTable)
471
+ ]);
472
+ let preIxs = [];
473
+ if (includeATASetup) {
474
+ const owners = !recipient || recipient.equals(signer) ? [signer] : [signer, recipient];
475
+ const mints = [accounts.supplyToken, accounts.borrowToken];
476
+ preIxs = await Promise.all(
477
+ owners.flatMap(
478
+ (owner) => mints.map((mint) => getOrCreateATAInstruction(owner, mint, connection))
479
+ )
480
+ ).then((ixs) => ixs.flat());
481
+ }
482
+ return {
483
+ nftId,
484
+ accounts,
485
+ remainingAccounts,
486
+ remainingAccountsIndices,
487
+ addressLookupTableAddresses: lookupTable ? [lookupTable] : [],
488
+ addressLookupTableAccounts: addressLookupTable.value ? [addressLookupTable.value] : [],
489
+ ixs: initPositionIx ? [initPositionIx, ...preIxs, ...otherIxs, operateIx] : [...preIxs, ...otherIxs, operateIx]
490
+ };
491
+ };
492
+ function getInitBranchContext(vaultId, branchId, signer, market) {
493
+ return {
494
+ signer,
495
+ vaultConfig: getVaultConfig(vaultId, market),
496
+ branch: getBranch(vaultId, branchId, market),
497
+ systemProgram: SystemProgram.programId
498
+ };
499
+ }
500
+ function getInitTickContext(vaultId, tick, signer, market) {
501
+ return {
502
+ signer,
503
+ vaultConfig: getVaultConfig(vaultId, market),
504
+ tickData: getTick(vaultId, tick, market),
505
+ systemProgram: SystemProgram.programId
506
+ };
507
+ }
508
+ async function getInitTickIdLiquidationContext(vaultId, tick, signer, program, market) {
509
+ const tickData = await program.account.tick.fetch(getTick(vaultId, tick, market)).catch(() => null);
510
+ if (!tickData) {
511
+ return {
512
+ signer,
513
+ vaultConfig: getVaultConfig(vaultId, market),
514
+ tickIdLiquidation: getTickIdLiquidation(
515
+ vaultId,
516
+ tick,
517
+ 0,
518
+ market
519
+ ),
520
+ tickData: getTick(vaultId, tick, market),
521
+ systemProgram: SystemProgram.programId
522
+ };
523
+ }
524
+ return {
525
+ signer,
526
+ vaultConfig: getVaultConfig(vaultId, market),
527
+ tickIdLiquidation: getTickIdLiquidation(
528
+ vaultId,
529
+ tick,
530
+ tickData.totalIds,
531
+ market
532
+ ),
533
+ tickData: getTick(vaultId, tick, market),
534
+ systemProgram: SystemProgram.programId
535
+ };
536
+ }
537
+ async function getLiquidateContext({
538
+ vaultId,
539
+ to,
540
+ program,
541
+ connection,
542
+ signer,
543
+ market
544
+ }) {
545
+ program = program ?? getVaultsProgram({ connection, signer, market });
546
+ const [vaultState, vaultConfig, vaultMetadata] = await Promise.all([
547
+ program.account.vaultState.fetch(getVaultState(vaultId, market)),
548
+ program.account.vaultConfig.fetch(
549
+ getVaultConfig(vaultId, market)
550
+ ),
551
+ program.account.vaultMetadata.fetch(
552
+ getVaultMetadata(vaultId, market)
553
+ )
554
+ ]);
555
+ const [supplyTokenProgram, borrowTokenProgram] = await Promise.all([
556
+ getAccountOwner(vaultConfig.supplyToken, connection),
557
+ getAccountOwner(vaultConfig.borrowToken, connection)
558
+ ]);
559
+ const { arrayIndex } = getTickIndices(vaultState.topmostTick);
560
+ let { otherIxs, newBranchPda } = await getOtherInstructionsLiquidate(
561
+ vaultId,
562
+ vaultState,
563
+ program,
564
+ signer,
565
+ market
566
+ );
567
+ const {
568
+ remainingAccounts,
569
+ remainingAccountsIndices,
570
+ otherIxs: finalOtherIxs
571
+ } = await getRemainingAccountsLiquidate(
572
+ vaultId,
573
+ vaultState,
574
+ vaultConfig,
575
+ otherIxs,
576
+ program,
577
+ connection,
578
+ signer,
579
+ market
580
+ );
581
+ return {
582
+ accounts: {
583
+ signer,
584
+ signerTokenAccount: getAssociatedTokenAddressSync(
585
+ vaultConfig.borrowToken,
586
+ signer,
587
+ false,
588
+ borrowTokenProgram
589
+ ),
590
+ to,
591
+ toTokenAccount: getAssociatedTokenAddressSync(
592
+ vaultConfig.supplyToken,
593
+ to,
594
+ false,
595
+ supplyTokenProgram
596
+ ),
597
+ vaultAdmin: getVaultAdmin(market),
598
+ vaultConfig: getVaultConfig(vaultId, market),
599
+ vaultState: getVaultState(vaultId, market),
600
+ supplyToken: vaultConfig.supplyToken,
601
+ borrowToken: vaultConfig.borrowToken,
602
+ oracle: new PublicKey(vaultConfig.oracle),
603
+ tickHasDebt: getTickHasDebt(vaultId, arrayIndex, market),
604
+ newBranch: newBranchPda,
605
+ supplyTokenReservesLiquidity: getLiquidityReserve(
606
+ vaultConfig.supplyToken,
607
+ market
608
+ ),
609
+ borrowTokenReservesLiquidity: getLiquidityReserve(
610
+ vaultConfig.borrowToken,
611
+ market
612
+ ),
613
+ vaultSupplyPositionOnLiquidity: getUserSupplyPosition(
614
+ vaultConfig.supplyToken,
615
+ getVaultConfig(vaultId, market),
616
+ market
617
+ ),
618
+ vaultBorrowPositionOnLiquidity: getUserBorrowPosition(
619
+ vaultConfig.borrowToken,
620
+ getVaultConfig(vaultId, market),
621
+ market
622
+ ),
623
+ supplyRateModel: getRateModel(vaultConfig.supplyToken, market),
624
+ borrowRateModel: getRateModel(vaultConfig.borrowToken, market),
625
+ supplyTokenClaimAccount: getClaimAccount(
626
+ vaultConfig.supplyToken,
627
+ getVaultConfig(vaultId, market),
628
+ market
629
+ ),
630
+ borrowTokenClaimAccount: getClaimAccount(
631
+ vaultConfig.borrowToken,
632
+ getVaultConfig(vaultId, market),
633
+ market
634
+ ),
635
+ liquidity: getLiquidity(market),
636
+ liquidityProgram: getLiquidityProgramId(market),
637
+ vaultSupplyTokenAccount: getAssociatedTokenAddressSync(
638
+ vaultConfig.supplyToken,
639
+ getLiquidity(market),
640
+ true,
641
+ supplyTokenProgram
642
+ ),
643
+ vaultBorrowTokenAccount: getAssociatedTokenAddressSync(
644
+ vaultConfig.borrowToken,
645
+ getLiquidity(market),
646
+ true,
647
+ borrowTokenProgram
648
+ ),
649
+ oracleProgram: new PublicKey(vaultConfig.oracleProgram),
650
+ supplyTokenProgram,
651
+ borrowTokenProgram,
652
+ systemProgram: SystemProgram.programId
653
+ },
654
+ remainingAccounts,
655
+ otherIxs: finalOtherIxs,
656
+ remainingAccountsIndices,
657
+ lookupTable: vaultMetadata.lookupTable
658
+ };
659
+ }
660
+ const getLiquidateIx = async ({
661
+ vaultId,
662
+ debtAmount,
663
+ colPerUnitDebt = new BN(0),
664
+ absorb = false,
665
+ signer,
666
+ to = signer,
667
+ connection,
668
+ market,
669
+ includeATASetup = false
670
+ }) => {
671
+ const program = getVaultsProgram({ connection, signer, market });
672
+ const {
673
+ accounts,
674
+ remainingAccounts,
675
+ remainingAccountsIndices,
676
+ otherIxs,
677
+ lookupTable
678
+ } = await getLiquidateContext({
679
+ vaultId,
680
+ to,
681
+ program,
682
+ signer,
683
+ connection,
684
+ market
685
+ });
686
+ const ixs = [];
687
+ if (includeATASetup) {
688
+ const owners = !to || to.equals(signer) ? [signer] : [signer, to];
689
+ const mints = [accounts.supplyToken, accounts.borrowToken];
690
+ const preIxs = await Promise.all(
691
+ owners.flatMap(
692
+ (owner) => mints.map((mint) => getOrCreateATAInstruction(owner, mint, connection))
693
+ )
694
+ ).then((ixs2) => ixs2.flat());
695
+ ixs.push(...preIxs);
696
+ }
697
+ if (otherIxs.length > 0) {
698
+ ixs.push(...otherIxs);
699
+ }
700
+ const [ix, addressLookupTable] = await Promise.all([
701
+ program.methods.liquidate(
702
+ debtAmount,
703
+ colPerUnitDebt,
704
+ absorb,
705
+ { direct: {} },
706
+ Buffer.from(remainingAccountsIndices)
707
+ ).accounts(accounts).remainingAccounts(remainingAccounts).instruction(),
708
+ connection.getAddressLookupTable(lookupTable)
709
+ ]);
710
+ ixs.push(ix);
711
+ return {
712
+ accounts,
713
+ remainingAccounts,
714
+ remainingAccountsIndices,
715
+ ixs,
716
+ addressLookupTableAddresses: lookupTable ? [lookupTable] : [],
717
+ addressLookupTableAccounts: addressLookupTable.value ? [addressLookupTable.value] : []
718
+ };
719
+ };
720
+ async function getOtherInstructionsLiquidate(vaultId, vaultState, program, signer, market) {
721
+ const otherIxs = [];
722
+ let newBranchId = vaultState.branchLiquidated === 1 ? vaultState.totalBranchId + 1 : vaultState.currentBranchId;
723
+ let newBranchPda = getBranch(
724
+ vaultId,
725
+ newBranchId,
726
+ market
727
+ );
728
+ const [newBranchData, tickData] = await Promise.all([
729
+ program.account.branch.fetch(newBranchPda).catch(() => null),
730
+ // might be possible that liquidation ends on a tick that is not initialized
731
+ program.account.tick.fetch(getTick(vaultId, vaultState.topmostTick, market)).catch(() => null)
732
+ ]);
733
+ if (!newBranchData) {
734
+ const ix = await program.methods.initBranch(vaultId, newBranchId).accounts(getInitBranchContext(vaultId, newBranchId, signer, market)).instruction();
735
+ otherIxs.push(ix);
736
+ }
737
+ if (!tickData) {
738
+ const ix = await program.methods.initTick(vaultId, vaultState.topmostTick).accounts(
739
+ getInitTickContext(vaultId, vaultState.topmostTick, signer, market)
740
+ ).instruction();
741
+ otherIxs.push(ix);
742
+ }
743
+ return {
744
+ otherIxs,
745
+ newBranchPda
746
+ };
747
+ }
748
+ async function loadRelevantTicksForLiquidate(vaultId, vaultState, liquidationTick, program, market) {
749
+ const ticks = [];
750
+ let topTick = vaultState.topmostTick;
751
+ if (topTick > liquidationTick)
752
+ try {
753
+ const topTickData = await program.account.tick.fetch(
754
+ getTick(vaultId, topTick, market)
755
+ );
756
+ if (topTickData) ticks.push({ ...topTickData, tick: topTick });
757
+ } catch {
758
+ }
759
+ let nextTick = MIN_TICK;
760
+ try {
761
+ nextTick = await findNextTickWithDebt(vaultId, topTick, program, market);
762
+ } catch {
763
+ }
764
+ const doesTickExist = (tick) => ticks.some((t) => t.tick === tick);
765
+ while (nextTick > liquidationTick && !doesTickExist(nextTick)) {
766
+ try {
767
+ const nextTickData = await program.account.tick.fetch(
768
+ getTick(vaultId, nextTick, market)
769
+ );
770
+ if (nextTickData) ticks.push({ ...nextTickData, tick: nextTick });
771
+ else throw new Error("Tick not found to load");
772
+ nextTick = await findNextTickWithDebt(vaultId, nextTick, program, market);
773
+ } catch {
774
+ }
775
+ }
776
+ return { ticks, nextTick };
777
+ }
778
+ async function getRemainingAccountsLiquidate(vaultId, vaultState, vaultConfig, otherIxs, program, connection, signer, market) {
779
+ const remainingAccounts = [];
780
+ const { oraclePriceLiquidate, oracleSources } = await readOraclePrice({
781
+ oracle: vaultConfig.oracle,
782
+ connection
783
+ // signer,
784
+ });
785
+ const liquidationRatio = new BN(oraclePriceLiquidate).mul(new BN(281474976710656)).div(new BN(10).pow(new BN(15)));
786
+ const liquidationThresholdRatio = liquidationRatio.mul(new BN(vaultConfig.liquidationThreshold)).div(new BN(10).pow(new BN(3)));
787
+ const liquidationTick = getTickAtRatio(liquidationThresholdRatio);
788
+ for (const source of oracleSources) {
789
+ remainingAccounts.push({
790
+ pubkey: source.source,
791
+ isWritable: false,
792
+ isSigner: false
793
+ });
794
+ }
795
+ const [branches, { ticks: tickAccounts, nextTick }] = await Promise.all([
796
+ loadRelevantBranchesForLiquidate(vaultId, vaultState, program, market),
797
+ loadRelevantTicksForLiquidate(
798
+ vaultId,
799
+ vaultState,
800
+ liquidationTick,
801
+ program,
802
+ market
803
+ )
804
+ ]);
805
+ const tickHasDebt = await loadRelevantTicksHasDebtArraysLiquidate(
806
+ vaultId,
807
+ vaultState.topmostTick,
808
+ nextTick,
809
+ program,
810
+ market
811
+ );
812
+ for (const branch of branches) {
813
+ remainingAccounts.push({
814
+ pubkey: getBranch(vaultId, branch.branchId, market),
815
+ isWritable: true,
816
+ isSigner: false
817
+ });
818
+ }
819
+ const tickToInit = [];
820
+ const existingTicks = await Promise.all(
821
+ tickAccounts.map(
822
+ (tickData) => program.account.tick.fetch(getTick(vaultId, tickData.tick, market)).catch(() => null)
823
+ )
824
+ );
825
+ const initInstructions = await Promise.all(
826
+ tickAccounts.map(async (tickData, index) => {
827
+ const existingTick = existingTicks[index];
828
+ if (!existingTick && !tickToInit.includes(tickData.tick)) {
829
+ tickToInit.push(tickData.tick);
830
+ return program.methods.initTick(vaultId, tickData.tick).accounts(getInitTickContext(vaultId, tickData.tick, signer, market)).instruction();
831
+ }
832
+ return null;
833
+ })
834
+ );
835
+ for (const ix of initInstructions) {
836
+ if (ix) {
837
+ otherIxs.push(ix);
838
+ }
839
+ }
840
+ for (const tickData of tickAccounts) {
841
+ remainingAccounts.push({
842
+ pubkey: getTick(vaultId, tickData.tick, market),
843
+ isWritable: true,
844
+ isSigner: false
845
+ });
846
+ }
847
+ for (const tickHasDebtArray of tickHasDebt) {
848
+ remainingAccounts.push({
849
+ pubkey: getTickHasDebt(vaultId, tickHasDebtArray.index, market),
850
+ isWritable: true,
851
+ isSigner: false
852
+ });
853
+ }
854
+ const remainingAccountsIndices = [
855
+ oracleSources.length,
856
+ branches.length,
857
+ tickAccounts.length,
858
+ tickHasDebt.length
859
+ ];
860
+ return {
861
+ remainingAccounts,
862
+ otherIxs,
863
+ remainingAccountsIndices
864
+ };
865
+ }
866
+
867
+ export { getInitPositionContext as a, getInitPositionIx as b, getLiquidateContext as c, getOperateContext as d, getOperateIx as e, getLiquidateIx as g };