@jup-ag/lend 0.1.10 → 0.2.0-beta.2

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