@jup-ag/lend 0.2.0-beta.1 → 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.
- package/dist/borrow/index.d.mts +102 -11
- package/dist/borrow/index.d.ts +102 -11
- package/dist/borrow/index.mjs +5 -3
- package/dist/dex/index.d.mts +247 -8
- package/dist/dex/index.d.ts +247 -8
- package/dist/dex/index.mjs +356 -27
- package/dist/index.d.mts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.mjs +3 -3
- package/dist/refinance/index.mjs +3 -2
- package/dist/shared/{lend.CTcB5yyK.mjs → lend.DcHJFKzT.mjs} +24 -842
- package/dist/shared/{lend.ByiEG4eI.mjs → lend.a53XYkl5.mjs} +1 -1
- package/dist/shared/lend.s0kjiHy3.mjs +29 -0
- package/dist/shared/lend.siYu8A-9.mjs +845 -0
- package/package.json +1 -1
- package/dist/shared/lend.BC5Y1398.mjs +0 -18
|
@@ -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 };
|