@kamino-finance/klend-sdk 5.2.12 → 5.2.14
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/classes/manager.d.ts +36 -7
- package/dist/classes/manager.d.ts.map +1 -1
- package/dist/classes/manager.js +41 -7
- package/dist/classes/manager.js.map +1 -1
- package/dist/classes/obligation.d.ts +2 -0
- package/dist/classes/obligation.d.ts.map +1 -1
- package/dist/classes/obligation.js +12 -6
- package/dist/classes/obligation.js.map +1 -1
- package/dist/classes/types.d.ts +23 -0
- package/dist/classes/types.d.ts.map +1 -0
- package/dist/classes/types.js +3 -0
- package/dist/classes/types.js.map +1 -0
- package/dist/classes/vault.d.ts +45 -9
- package/dist/classes/vault.d.ts.map +1 -1
- package/dist/classes/vault.js +349 -20
- package/dist/classes/vault.js.map +1 -1
- package/dist/client_kamino_manager.d.ts.map +1 -1
- package/dist/client_kamino_manager.js +100 -16
- package/dist/client_kamino_manager.js.map +1 -1
- package/dist/lending_operations/repay_with_collateral_calcs.d.ts +4 -2
- package/dist/lending_operations/repay_with_collateral_calcs.d.ts.map +1 -1
- package/dist/lending_operations/repay_with_collateral_calcs.js +45 -52
- package/dist/lending_operations/repay_with_collateral_calcs.js.map +1 -1
- package/dist/lending_operations/repay_with_collateral_operations.d.ts +7 -0
- package/dist/lending_operations/repay_with_collateral_operations.d.ts.map +1 -1
- package/dist/lending_operations/repay_with_collateral_operations.js +13 -3
- package/dist/lending_operations/repay_with_collateral_operations.js.map +1 -1
- package/package.json +1 -1
- package/src/classes/manager.ts +57 -10
- package/src/classes/obligation.ts +15 -6
- package/src/classes/types.ts +27 -0
- package/src/classes/vault.ts +511 -26
- package/src/client_kamino_manager.ts +174 -19
- package/src/lending_operations/repay_with_collateral_calcs.ts +55 -61
- package/src/lending_operations/repay_with_collateral_operations.ts +24 -4
- package/src/leverage/operations.ts +1 -1
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
ReserveAllocationConfig,
|
|
27
27
|
ReserveWithAddress,
|
|
28
28
|
signSendAndConfirmRawTransactionWithRetry,
|
|
29
|
+
sleep,
|
|
29
30
|
Web3Client,
|
|
30
31
|
} from './lib';
|
|
31
32
|
import * as anchor from '@coral-xyz/anchor';
|
|
@@ -52,6 +53,7 @@ import {
|
|
|
52
53
|
PerformanceFeeBps,
|
|
53
54
|
} from './idl_codegen_kamino_vault/types/VaultConfigField';
|
|
54
55
|
import { getAccountOwner } from './utils/rpc';
|
|
56
|
+
import { Farms, FarmState, getUserStatePDA } from '@kamino-finance/farms-sdk';
|
|
55
57
|
|
|
56
58
|
dotenv.config({
|
|
57
59
|
path: `.env${process.env.ENV ? '.' + process.env.ENV : ''}`,
|
|
@@ -224,9 +226,10 @@ async function main() {
|
|
|
224
226
|
`--mode <string>`,
|
|
225
227
|
'simulate - to print txn simulation, inspect - to get txn simulation in explorer, execute - execute txn, multisig - to get bs58 txn for multisig usage'
|
|
226
228
|
)
|
|
229
|
+
.option(`--name`, 'The onchain name of the strat')
|
|
227
230
|
.option(`--staging`, 'If true, will use the staging programs')
|
|
228
231
|
.option(`--multisig <string>`, 'If using multisig mode this is required, otherwise will be ignored')
|
|
229
|
-
.action(async ({ mint, mode, staging, multisig }) => {
|
|
232
|
+
.action(async ({ mint, mode, name, staging, multisig }) => {
|
|
230
233
|
const env = initializeClient(mode === 'multisig', staging);
|
|
231
234
|
const tokenMint = new PublicKey(mint);
|
|
232
235
|
|
|
@@ -244,11 +247,14 @@ async function main() {
|
|
|
244
247
|
tokenMintProgramId: tokenProgramID,
|
|
245
248
|
performanceFeeRate: new Decimal(0.0),
|
|
246
249
|
managementFeeRate: new Decimal(0.0),
|
|
250
|
+
name,
|
|
247
251
|
});
|
|
248
252
|
|
|
249
|
-
const { vault: vaultKp,
|
|
253
|
+
const { vault: vaultKp, initVaultIxs: instructions } = await kaminoManager.createVaultIxs(kaminoVaultConfig);
|
|
250
254
|
|
|
251
|
-
const _createVaultSig = await processTxn(env.client, env.payer, instructions, mode, 2500, [vaultKp]);
|
|
255
|
+
const _createVaultSig = await processTxn(env.client, env.payer, instructions.initVaultIxs, mode, 2500, [vaultKp]);
|
|
256
|
+
await sleep(5000);
|
|
257
|
+
const _populateLUTSig = await processTxn(env.client, env.payer, instructions.populateLUTIxs, mode, 2500, []);
|
|
252
258
|
|
|
253
259
|
mode === 'execute' && console.log('Vault created:', vaultKp.publicKey.toBase58());
|
|
254
260
|
});
|
|
@@ -274,9 +280,16 @@ async function main() {
|
|
|
274
280
|
const kaminoManager = new KaminoManager(env.connection, env.kLendProgramId, env.kVaultProgramId);
|
|
275
281
|
|
|
276
282
|
const kaminoVault = new KaminoVault(vaultAddress, undefined, env.kVaultProgramId);
|
|
277
|
-
const
|
|
278
|
-
|
|
279
|
-
const updateVaultPendingAdminSig = await processTxn(
|
|
283
|
+
const instructions = await kaminoManager.updateVaultConfigIxs(kaminoVault, new PendingVaultAdmin(), newAdmin);
|
|
284
|
+
|
|
285
|
+
const updateVaultPendingAdminSig = await processTxn(
|
|
286
|
+
env.client,
|
|
287
|
+
env.payer,
|
|
288
|
+
[instructions.updateVaultConfigIx, ...instructions.updateLUTIxs],
|
|
289
|
+
mode,
|
|
290
|
+
2500,
|
|
291
|
+
[]
|
|
292
|
+
);
|
|
280
293
|
|
|
281
294
|
mode === 'execute' && console.log('Pending admin updated:', updateVaultPendingAdminSig);
|
|
282
295
|
});
|
|
@@ -302,13 +315,84 @@ async function main() {
|
|
|
302
315
|
const kaminoManager = new KaminoManager(env.connection, env.kLendProgramId, env.kVaultProgramId);
|
|
303
316
|
|
|
304
317
|
const kaminoVault = new KaminoVault(vaultAddress, undefined, env.kVaultProgramId);
|
|
305
|
-
const
|
|
318
|
+
const instructions = await kaminoManager.updateVaultConfigIxs(kaminoVault, new ManagementFeeBps(), feeBps);
|
|
319
|
+
|
|
320
|
+
const updateVaultConfigSig = await processTxn(
|
|
321
|
+
env.client,
|
|
322
|
+
env.payer,
|
|
323
|
+
[instructions.updateVaultConfigIx, ...instructions.updateLUTIxs],
|
|
324
|
+
mode,
|
|
325
|
+
2500,
|
|
326
|
+
[]
|
|
327
|
+
);
|
|
328
|
+
|
|
329
|
+
mode === 'execute' && console.log('Management fee updated:', updateVaultConfigSig);
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
commands
|
|
333
|
+
.command('insert-into-lut')
|
|
334
|
+
.requiredOption('--lut <string>', 'Lookup table address')
|
|
335
|
+
.requiredOption('--addresses <string>', 'The addresses to insert into the LUT, space separated')
|
|
336
|
+
.requiredOption(
|
|
337
|
+
`--mode <string>`,
|
|
338
|
+
'simulate - to print txn simulation, inspect - to get txn simulation in explorer, execute - execute txn, multisig - to get bs58 txn for multisig usage'
|
|
339
|
+
)
|
|
340
|
+
.option(`--staging`, 'If true, will use the staging programs')
|
|
341
|
+
.option(`--multisig <string>`, 'If using multisig mode this is required, otherwise will be ignored')
|
|
342
|
+
.action(async ({ lut, addresses, mode, staging, multisig }) => {
|
|
343
|
+
const env = initializeClient(mode === 'multisig', staging);
|
|
344
|
+
const lutAddress = new PublicKey(lut);
|
|
345
|
+
|
|
346
|
+
const addressesArr = addresses.split(' ').map((address: string) => new PublicKey(address));
|
|
347
|
+
|
|
348
|
+
if (mode === 'multisig' && !multisig) {
|
|
349
|
+
throw new Error('If using multisig mode, multisig is required');
|
|
350
|
+
}
|
|
306
351
|
|
|
307
|
-
const
|
|
352
|
+
const kaminoManager = new KaminoManager(env.connection, env.kLendProgramId, env.kVaultProgramId);
|
|
353
|
+
|
|
354
|
+
const instructions = await kaminoManager.insertIntoLUT(env.payer.publicKey, lutAddress, addressesArr);
|
|
355
|
+
|
|
356
|
+
const updateVaultConfigSig = await processTxn(env.client, env.payer, instructions, mode, 2500, []);
|
|
308
357
|
|
|
309
358
|
mode === 'execute' && console.log('Management fee updated:', updateVaultConfigSig);
|
|
310
359
|
});
|
|
311
360
|
|
|
361
|
+
commands
|
|
362
|
+
.command('sync-vault-lut')
|
|
363
|
+
.requiredOption('--vault <string>', 'The vault address to sync')
|
|
364
|
+
.requiredOption(
|
|
365
|
+
`--mode <string>`,
|
|
366
|
+
'simulate - to print txn simulation, inspect - to get txn simulation in explorer, execute - execute txn, multisig - to get bs58 txn for multisig usage'
|
|
367
|
+
)
|
|
368
|
+
.option(`--staging`, 'If true, will use the staging programs')
|
|
369
|
+
.option(`--multisig <string>`, 'If using multisig mode this is required, otherwise will be ignored')
|
|
370
|
+
.action(async ({ vault, mode, staging, multisig }) => {
|
|
371
|
+
const env = initializeClient(mode === 'multisig', staging);
|
|
372
|
+
const vaultAddress = new PublicKey(vault);
|
|
373
|
+
|
|
374
|
+
if (mode === 'multisig' && !multisig) {
|
|
375
|
+
throw new Error('If using multisig mode, multisig is required');
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
const kaminoManager = new KaminoManager(env.connection, env.kLendProgramId, env.kVaultProgramId);
|
|
379
|
+
|
|
380
|
+
const kaminoVault = new KaminoVault(vaultAddress, undefined, env.kVaultProgramId);
|
|
381
|
+
const syncLUTIxs = await kaminoManager.syncVaultLUT(kaminoVault);
|
|
382
|
+
|
|
383
|
+
// if we need to create the LUT we have to do that in a separate tx and wait a little bit after
|
|
384
|
+
if (syncLUTIxs.setupLUTIfNeededIxs.length > 0) {
|
|
385
|
+
const setupLUTSig = await processTxn(env.client, env.payer, syncLUTIxs.setupLUTIfNeededIxs, mode, 2500, []);
|
|
386
|
+
await sleep(5000);
|
|
387
|
+
mode === 'execute' && console.log('LUT created and set to the vault:', setupLUTSig);
|
|
388
|
+
}
|
|
389
|
+
// if there are accounts to be added to the LUT we have to do that in a separate tx
|
|
390
|
+
for (const ix of syncLUTIxs.syncLUTIxs) {
|
|
391
|
+
const insertIntoLUTSig = await processTxn(env.client, env.payer, [ix], mode, 2500, []);
|
|
392
|
+
mode === 'execute' && console.log('Accounts added to the LUT:', insertIntoLUTSig);
|
|
393
|
+
}
|
|
394
|
+
});
|
|
395
|
+
|
|
312
396
|
commands
|
|
313
397
|
.command('update-vault-perf-fee')
|
|
314
398
|
.requiredOption('--vault <string>', 'Vault address')
|
|
@@ -330,9 +414,16 @@ async function main() {
|
|
|
330
414
|
const kaminoManager = new KaminoManager(env.connection, env.kLendProgramId, env.kVaultProgramId);
|
|
331
415
|
|
|
332
416
|
const kaminoVault = new KaminoVault(vaultAddress, undefined, env.kVaultProgramId);
|
|
333
|
-
const
|
|
334
|
-
|
|
335
|
-
const updateVaultPerfFeeSig = await processTxn(
|
|
417
|
+
const instructions = await kaminoManager.updateVaultConfigIxs(kaminoVault, new PerformanceFeeBps(), feeBps);
|
|
418
|
+
|
|
419
|
+
const updateVaultPerfFeeSig = await processTxn(
|
|
420
|
+
env.client,
|
|
421
|
+
env.payer,
|
|
422
|
+
[instructions.updateVaultConfigIx, ...instructions.updateLUTIxs],
|
|
423
|
+
mode,
|
|
424
|
+
2500,
|
|
425
|
+
[]
|
|
426
|
+
);
|
|
336
427
|
|
|
337
428
|
mode === 'execute' && console.log('Performance fee updated:', updateVaultPerfFeeSig);
|
|
338
429
|
});
|
|
@@ -357,9 +448,16 @@ async function main() {
|
|
|
357
448
|
const kaminoManager = new KaminoManager(env.connection, env.kLendProgramId, env.kVaultProgramId);
|
|
358
449
|
|
|
359
450
|
const kaminoVault = new KaminoVault(vaultAddress, undefined, env.kVaultProgramId);
|
|
360
|
-
const
|
|
361
|
-
|
|
362
|
-
const acceptVaultOwnershipSig = await processTxn(
|
|
451
|
+
const instructions = await kaminoManager.acceptVaultOwnershipIxs(kaminoVault);
|
|
452
|
+
|
|
453
|
+
const acceptVaultOwnershipSig = await processTxn(
|
|
454
|
+
env.client,
|
|
455
|
+
env.payer,
|
|
456
|
+
[instructions.acceptVaultOwnershipIx, ...instructions.updateLUTIxs],
|
|
457
|
+
mode,
|
|
458
|
+
2500,
|
|
459
|
+
[]
|
|
460
|
+
);
|
|
363
461
|
|
|
364
462
|
mode === 'execute' && console.log('Vault ownership accepted:', acceptVaultOwnershipSig);
|
|
365
463
|
});
|
|
@@ -422,6 +520,56 @@ async function main() {
|
|
|
422
520
|
mode === 'execute' && console.log('Pending fees withdrawn:', withdrawPendingFeesSig);
|
|
423
521
|
});
|
|
424
522
|
|
|
523
|
+
commands
|
|
524
|
+
.command('stake')
|
|
525
|
+
.requiredOption('--vault <string>', 'Vault address')
|
|
526
|
+
.requiredOption('--farm <string>', 'Farm address')
|
|
527
|
+
.requiredOption('--amount <string>', 'The number of kTokens to stake')
|
|
528
|
+
.requiredOption(
|
|
529
|
+
`--mode <string>`,
|
|
530
|
+
'simulate - to print txn simulation, inspect - to get txn simulation in explorer, execute - execute txn, multisig - to get bs58 txn for multisig usage'
|
|
531
|
+
)
|
|
532
|
+
.option(`--staging`, 'If true, will use the staging programs')
|
|
533
|
+
.option(`--multisig <string>`, 'If using multisig mode this is required, otherwise will be ignored')
|
|
534
|
+
.action(async ({ vault, farm, amount, mode, staging, multisig }) => {
|
|
535
|
+
const env = initializeClient(mode === 'multisig', staging);
|
|
536
|
+
const vaultAddress = new PublicKey(vault);
|
|
537
|
+
const farmAddress = new PublicKey(farm);
|
|
538
|
+
|
|
539
|
+
const farmClient = new Farms(env.connection);
|
|
540
|
+
|
|
541
|
+
const farmState = await FarmState.fetch(env.connection, farmAddress);
|
|
542
|
+
const vaultState = await new KaminoVault(vaultAddress, undefined, env.kVaultProgramId).getState(env.connection);
|
|
543
|
+
|
|
544
|
+
const scopePricesArg = farmState!.scopePrices.equals(PublicKey.default)
|
|
545
|
+
? farmClient.getProgramID()
|
|
546
|
+
: farmState!.scopePrices;
|
|
547
|
+
|
|
548
|
+
const ixns: TransactionInstruction[] = [];
|
|
549
|
+
const userState = getUserStatePDA(farmClient.getProgramID(), farmAddress, env.provider.publicKey);
|
|
550
|
+
if (!userState) {
|
|
551
|
+
const createUserIx = await farmClient.createNewUserIx(env.provider.publicKey, farmAddress);
|
|
552
|
+
ixns.push(createUserIx);
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
// todo: fix in farms sdk to make this not async
|
|
556
|
+
const stakeIx = await farmClient.stakeIx(
|
|
557
|
+
env.payer.publicKey,
|
|
558
|
+
farmAddress,
|
|
559
|
+
new Decimal(amount),
|
|
560
|
+
vaultState.sharesMint,
|
|
561
|
+
scopePricesArg
|
|
562
|
+
);
|
|
563
|
+
ixns.push(stakeIx);
|
|
564
|
+
|
|
565
|
+
if (mode === 'multisig' && !multisig) {
|
|
566
|
+
throw new Error('If using multisig mode, multisig is required');
|
|
567
|
+
}
|
|
568
|
+
const withdrawPendingFeesSig = await processTxn(env.client, env.payer, ixns, mode, 2500, []);
|
|
569
|
+
|
|
570
|
+
mode === 'execute' && console.log('Pending fees withdrawn:', withdrawPendingFeesSig);
|
|
571
|
+
});
|
|
572
|
+
|
|
425
573
|
commands
|
|
426
574
|
.command('update-vault-reserve-allocation')
|
|
427
575
|
.requiredOption('--vault <string>', 'Vault address')
|
|
@@ -466,7 +614,14 @@ async function main() {
|
|
|
466
614
|
firstReserveAllocationConfig
|
|
467
615
|
);
|
|
468
616
|
|
|
469
|
-
const updateVaultAllocationSig = await processTxn(
|
|
617
|
+
const updateVaultAllocationSig = await processTxn(
|
|
618
|
+
env.client,
|
|
619
|
+
env.payer,
|
|
620
|
+
[instructions.updateReserveAllocationIx, ...instructions.updateLUTIxs],
|
|
621
|
+
mode,
|
|
622
|
+
2500,
|
|
623
|
+
[]
|
|
624
|
+
);
|
|
470
625
|
|
|
471
626
|
mode === 'execute' && console.log('Vault allocation updated:', updateVaultAllocationSig);
|
|
472
627
|
});
|
|
@@ -557,7 +712,7 @@ async function main() {
|
|
|
557
712
|
for (let i = 0; i < instructions.length; i++) {
|
|
558
713
|
const txInstructions: TransactionInstruction[] = [];
|
|
559
714
|
txInstructions.push(instructions[i]);
|
|
560
|
-
const investReserveSig = await processTxn(env.client, env.payer, txInstructions, mode, 2500, [],
|
|
715
|
+
const investReserveSig = await processTxn(env.client, env.payer, txInstructions, mode, 2500, [], 800000);
|
|
561
716
|
|
|
562
717
|
mode === 'execute' && console.log('Reserve invested:', investReserveSig);
|
|
563
718
|
}
|
|
@@ -599,7 +754,7 @@ async function main() {
|
|
|
599
754
|
kaminoVault,
|
|
600
755
|
reserveWithAddress
|
|
601
756
|
);
|
|
602
|
-
const investReserveSig = await processTxn(env.client, env.payer, instructions, mode, 2500, [],
|
|
757
|
+
const investReserveSig = await processTxn(env.client, env.payer, instructions, mode, 2500, [], 800_000);
|
|
603
758
|
|
|
604
759
|
mode === 'execute' && console.log('Reserve invested:', investReserveSig);
|
|
605
760
|
});
|
|
@@ -1059,7 +1214,7 @@ async function processTxn(
|
|
|
1059
1214
|
if (simulation.value.logs && simulation.value.logs.length > 0) {
|
|
1060
1215
|
console.log('Simulation: \n' + simulation.value.logs);
|
|
1061
1216
|
} else {
|
|
1062
|
-
console.log('Simulation failed: \n' + simulation);
|
|
1217
|
+
console.log('Simulation failed: \n' + simulation.value.err);
|
|
1063
1218
|
}
|
|
1064
1219
|
} else if (mode === 'inspect') {
|
|
1065
1220
|
console.log(
|
|
@@ -1076,7 +1231,7 @@ async function processTxn(
|
|
|
1076
1231
|
function createAddExtraComputeUnitFeeTransaction(units: number, microLamports: number): TransactionInstruction[] {
|
|
1077
1232
|
const ixns: TransactionInstruction[] = [];
|
|
1078
1233
|
ixns.push(ComputeBudgetProgram.setComputeUnitLimit({ units }));
|
|
1079
|
-
ixns.push(ComputeBudgetProgram.setComputeUnitPrice({ microLamports }));
|
|
1234
|
+
ixns.push(ComputeBudgetProgram.setComputeUnitPrice({ microLamports: new Decimal(microLamports).floor().toNumber() }));
|
|
1080
1235
|
return ixns;
|
|
1081
1236
|
}
|
|
1082
1237
|
|
|
@@ -76,79 +76,73 @@ export const calcFlashRepayAmount = (props: {
|
|
|
76
76
|
};
|
|
77
77
|
|
|
78
78
|
export function calcMaxWithdrawCollateral(
|
|
79
|
-
|
|
79
|
+
market: KaminoMarket,
|
|
80
|
+
obligation: KaminoObligation,
|
|
80
81
|
collReserveAddr: PublicKey,
|
|
81
82
|
debtReserveAddr: PublicKey,
|
|
82
|
-
obligation: KaminoObligation,
|
|
83
83
|
repayAmountLamports: Decimal
|
|
84
84
|
): {
|
|
85
|
-
|
|
85
|
+
repayAmountLamports: Decimal;
|
|
86
86
|
withdrawableCollLamports: Decimal;
|
|
87
|
+
canWithdrawAllColl: boolean;
|
|
88
|
+
repayingAllDebt: boolean;
|
|
87
89
|
} {
|
|
88
|
-
const
|
|
89
|
-
const
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
if (debtPosition.reserveAddress.equals(debtReserveAddr)) {
|
|
103
|
-
remainingDebtAmountLamports = remainingDebtAmountLamports.sub(repayAmountLamports);
|
|
104
|
-
}
|
|
105
|
-
const remainingDebtBfWeightedValue = remainingDebtAmountLamports
|
|
106
|
-
.ceil()
|
|
107
|
-
.div(debtReserve.getMintFactor())
|
|
108
|
-
.mul(debtBorrowFactor)
|
|
109
|
-
.mul(debtOraclePx);
|
|
110
|
-
totalRemainingDebtValue = totalRemainingDebtValue.add(remainingDebtBfWeightedValue);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
let canWithdrawRemainingColl = false;
|
|
114
|
-
if (totalRemainingDebtValue.lte(new Decimal(0)) && borrows.length === 1) {
|
|
115
|
-
canWithdrawRemainingColl = true;
|
|
90
|
+
const deposit = obligation.getDepositByReserve(collReserveAddr)!;
|
|
91
|
+
const borrow = obligation.getBorrowByReserve(debtReserveAddr)!;
|
|
92
|
+
const depositReserve = market.getReserveByAddress(deposit.reserveAddress)!;
|
|
93
|
+
const debtReserve = market.getReserveByAddress(borrow.reserveAddress)!;
|
|
94
|
+
const depositTotalLamports = deposit.amount.floor();
|
|
95
|
+
|
|
96
|
+
const remainingBorrowLamports = borrow.amount.sub(repayAmountLamports).ceil();
|
|
97
|
+
const remainingBorrowAmount = remainingBorrowLamports.div(debtReserve.getMintFactor());
|
|
98
|
+
let remainingBorrowsValue = remainingBorrowAmount.mul(debtReserve.getOracleMarketPrice());
|
|
99
|
+
if (obligation.getBorrows().length > 1) {
|
|
100
|
+
remainingBorrowsValue = obligation
|
|
101
|
+
.getBorrows()
|
|
102
|
+
.filter((p) => !p.reserveAddress.equals(borrow.reserveAddress))
|
|
103
|
+
.reduce((acc, b) => acc.add(b.marketValueRefreshed), new Decimal('0'));
|
|
116
104
|
}
|
|
117
105
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
.floor()
|
|
128
|
-
.div(otherCollReserve.getMintFactor())
|
|
129
|
-
.mul(otherCollOraclePx)
|
|
130
|
-
.mul(otherCollMaxLtv);
|
|
131
|
-
totalOtherCollateralValue = totalOtherCollateralValue.add(otherCollValue);
|
|
106
|
+
let remainingDepositsValueWithLtv = new Decimal('0');
|
|
107
|
+
if (obligation.getDeposits().length > 1) {
|
|
108
|
+
remainingDepositsValueWithLtv = obligation
|
|
109
|
+
.getDeposits()
|
|
110
|
+
.filter((p) => !p.reserveAddress.equals(deposit.reserveAddress))
|
|
111
|
+
.reduce((acc, d) => {
|
|
112
|
+
const { maxLtv } = obligation.getLtvForReserve(market, market.getReserveByAddress(d.reserveAddress)!);
|
|
113
|
+
return acc.add(d.marketValueRefreshed.mul(maxLtv));
|
|
114
|
+
}, new Decimal('0'));
|
|
132
115
|
}
|
|
133
116
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
const maxCollWithdrawAmount = numerator.div(denominator);
|
|
143
|
-
const maxCollateralWithdrawalAmountLamports = maxCollWithdrawAmount.mul(collReserve.getMintFactor()).floor();
|
|
144
|
-
|
|
145
|
-
let withdrawableCollLamports: Decimal;
|
|
146
|
-
if (canWithdrawRemainingColl) {
|
|
147
|
-
withdrawableCollLamports = Decimal.min(maxCollateralWithdrawalAmountLamports, collPosition.amount).floor();
|
|
117
|
+
// can withdraw all coll
|
|
118
|
+
if (remainingDepositsValueWithLtv.gte(remainingBorrowsValue)) {
|
|
119
|
+
return {
|
|
120
|
+
repayAmountLamports: repayAmountLamports,
|
|
121
|
+
withdrawableCollLamports: depositTotalLamports,
|
|
122
|
+
canWithdrawAllColl: true,
|
|
123
|
+
repayingAllDebt: repayAmountLamports.gte(borrow.amount),
|
|
124
|
+
};
|
|
148
125
|
} else {
|
|
149
|
-
|
|
126
|
+
const { maxLtv: collMaxLtv } = obligation.getLtvForReserve(
|
|
127
|
+
market,
|
|
128
|
+
market.getReserveByAddress(depositReserve.address)!
|
|
129
|
+
);
|
|
130
|
+
const numerator = deposit.marketValueRefreshed
|
|
131
|
+
.mul(collMaxLtv)
|
|
132
|
+
.add(remainingDepositsValueWithLtv)
|
|
133
|
+
.sub(remainingBorrowsValue);
|
|
134
|
+
|
|
135
|
+
const denominator = depositReserve.getOracleMarketPrice().mul(collMaxLtv);
|
|
136
|
+
const maxCollWithdrawAmount = numerator.div(denominator);
|
|
137
|
+
const withdrawableCollLamports = maxCollWithdrawAmount.mul(depositReserve.getMintFactor()).floor();
|
|
138
|
+
|
|
139
|
+
return {
|
|
140
|
+
repayAmountLamports: repayAmountLamports,
|
|
141
|
+
withdrawableCollLamports,
|
|
142
|
+
canWithdrawAllColl: false,
|
|
143
|
+
repayingAllDebt: repayAmountLamports.gte(borrow.amount),
|
|
144
|
+
};
|
|
150
145
|
}
|
|
151
|
-
return { canWithdrawRemainingColl, withdrawableCollLamports };
|
|
152
146
|
}
|
|
153
147
|
|
|
154
148
|
export function estimateDebtRepaymentWithColl(props: {
|
|
@@ -29,6 +29,13 @@ export type RepayWithCollIxsResponse<QuoteResponse> = {
|
|
|
29
29
|
export type InitialInputs<QuoteResponse> = {
|
|
30
30
|
debtRepayAmountLamports: Decimal;
|
|
31
31
|
flashRepayAmountLamports: Decimal;
|
|
32
|
+
/**
|
|
33
|
+
* The amount of collateral available to withdraw, if this is less than the swap input amount, then the swap may fail due to slippage, or tokens may be debited from the user's ATA, so the caller needs to check this
|
|
34
|
+
*/
|
|
35
|
+
maxCollateralWithdrawLamports: Decimal;
|
|
36
|
+
/**
|
|
37
|
+
* The quote from the provided quoter
|
|
38
|
+
*/
|
|
32
39
|
swapQuote: SwapQuote<QuoteResponse>;
|
|
33
40
|
currentSlot: number;
|
|
34
41
|
klendAccounts: Array<PublicKey>;
|
|
@@ -93,9 +100,9 @@ export async function getRepayWithCollSwapInputs<QuoteResponse>({
|
|
|
93
100
|
}
|
|
94
101
|
const { withdrawableCollLamports } = calcMaxWithdrawCollateral(
|
|
95
102
|
kaminoMarket,
|
|
103
|
+
obligation,
|
|
96
104
|
collReserve.address,
|
|
97
105
|
debtReserve.address,
|
|
98
|
-
obligation,
|
|
99
106
|
repayAmountLamports
|
|
100
107
|
);
|
|
101
108
|
|
|
@@ -156,6 +163,7 @@ export async function getRepayWithCollSwapInputs<QuoteResponse>({
|
|
|
156
163
|
initialInputs: {
|
|
157
164
|
debtRepayAmountLamports: repayAmountLamports,
|
|
158
165
|
flashRepayAmountLamports,
|
|
166
|
+
maxCollateralWithdrawLamports: withdrawableCollLamports,
|
|
159
167
|
swapQuote,
|
|
160
168
|
currentSlot,
|
|
161
169
|
klendAccounts: uniqueKlendAccounts,
|
|
@@ -196,18 +204,30 @@ export async function getRepayWithCollIxs<QuoteResponse>({
|
|
|
196
204
|
budgetAndPriorityFeeIxs,
|
|
197
205
|
scopeRefresh,
|
|
198
206
|
});
|
|
199
|
-
const { debtRepayAmountLamports, flashRepayAmountLamports, swapQuote } = initialInputs;
|
|
207
|
+
const { debtRepayAmountLamports, flashRepayAmountLamports, maxCollateralWithdrawLamports, swapQuote } = initialInputs;
|
|
200
208
|
const { inputAmountLamports: collSwapInLamports } = swapInputs;
|
|
201
209
|
|
|
202
210
|
const collReserve = kaminoMarket.getReserveByMint(collTokenMint)!;
|
|
203
211
|
const debtReserve = kaminoMarket.getReserveByMint(debtTokenMint)!;
|
|
204
212
|
|
|
213
|
+
// the client should use these values to prevent this input, but the tx may succeed, so we don't want to fail
|
|
214
|
+
// there is also a chance that the tx will consume debt token from the user's ata which they would not expect
|
|
215
|
+
if (collSwapInLamports.greaterThan(maxCollateralWithdrawLamports)) {
|
|
216
|
+
logger(
|
|
217
|
+
`Collateral swap in amount ${collSwapInLamports} exceeds max withdrawable collateral ${maxCollateralWithdrawLamports}, tx may fail with slippage`
|
|
218
|
+
);
|
|
219
|
+
swapInputs.inputAmountLamports = maxCollateralWithdrawLamports;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const actualSwapInLamports = Decimal.min(collSwapInLamports, maxCollateralWithdrawLamports);
|
|
205
223
|
logger(
|
|
206
|
-
`Expected to swap in: ${
|
|
224
|
+
`Expected to swap in: ${actualSwapInLamports.div(collReserve.getMintFactor())} ${
|
|
207
225
|
collReserve.symbol
|
|
208
226
|
}, for: ${flashRepayAmountLamports.div(debtReserve.getMintFactor())} ${debtReserve.symbol}, quoter px: ${
|
|
209
227
|
swapQuote.priceAInB
|
|
210
|
-
} ${debtReserve.symbol}/${collReserve.symbol}
|
|
228
|
+
} ${debtReserve.symbol}/${collReserve.symbol}, required px: ${flashRepayAmountLamports
|
|
229
|
+
.div(debtReserve.getMintFactor())
|
|
230
|
+
.div(actualSwapInLamports.div(collReserve.getMintFactor()))} ${debtReserve.symbol}/${collReserve.symbol}`
|
|
211
231
|
);
|
|
212
232
|
|
|
213
233
|
const swapResponse = await swapper(swapInputs, initialInputs.klendAccounts, swapQuote);
|
|
@@ -1045,7 +1045,7 @@ export async function getAdjustLeverageSwapInputs<QuoteResponse>({
|
|
|
1045
1045
|
|
|
1046
1046
|
const swapInputAmount = toLamports(
|
|
1047
1047
|
!collIsKtoken ? calcs.borrowAmount : calcs.amountToFlashBorrowDebt,
|
|
1048
|
-
debtReserve.stats.decimals
|
|
1048
|
+
debtReserve.stats.decimals
|
|
1049
1049
|
).ceil();
|
|
1050
1050
|
|
|
1051
1051
|
const swapInputsForQuote: SwapInputs = {
|