@kamino-finance/klend-sdk 5.11.2-beta.0 → 5.11.3-beta.0
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/action.d.ts +25 -35
- package/dist/classes/action.d.ts.map +1 -1
- package/dist/classes/action.js +92 -388
- package/dist/classes/action.js.map +1 -1
- package/dist/classes/lut_utils.d.ts +29 -0
- package/dist/classes/lut_utils.d.ts.map +1 -0
- package/dist/classes/lut_utils.js +62 -0
- package/dist/classes/lut_utils.js.map +1 -0
- package/dist/classes/manager.d.ts +1 -1
- package/dist/classes/manager.js +1 -1
- package/dist/classes/market.d.ts +3 -3
- package/dist/classes/market.d.ts.map +1 -1
- package/dist/classes/market.js +30 -16
- package/dist/classes/market.js.map +1 -1
- package/dist/classes/obligation.d.ts +3 -1
- package/dist/classes/obligation.d.ts.map +1 -1
- package/dist/classes/obligation.js +6 -1
- package/dist/classes/obligation.js.map +1 -1
- package/dist/classes/vault.js +6 -6
- package/dist/classes/vault.js.map +1 -1
- package/dist/lending_operations/repay_with_collateral_calcs.d.ts.map +1 -1
- package/dist/lending_operations/repay_with_collateral_calcs.js +5 -9
- package/dist/lending_operations/repay_with_collateral_calcs.js.map +1 -1
- package/dist/lending_operations/repay_with_collateral_operations.d.ts +4 -9
- package/dist/lending_operations/repay_with_collateral_operations.d.ts.map +1 -1
- package/dist/lending_operations/repay_with_collateral_operations.js +10 -33
- package/dist/lending_operations/repay_with_collateral_operations.js.map +1 -1
- package/dist/lending_operations/swap_collateral_operations.d.ts +2 -2
- package/dist/lending_operations/swap_collateral_operations.d.ts.map +1 -1
- package/dist/lending_operations/swap_collateral_operations.js +11 -6
- package/dist/lending_operations/swap_collateral_operations.js.map +1 -1
- package/dist/leverage/operations.d.ts +9 -7
- package/dist/leverage/operations.d.ts.map +1 -1
- package/dist/leverage/operations.js +78 -66
- package/dist/leverage/operations.js.map +1 -1
- package/dist/leverage/types.d.ts +4 -4
- package/dist/leverage/types.d.ts.map +1 -1
- package/dist/utils/ObligationType.d.ts +1 -1
- package/dist/utils/ObligationType.d.ts.map +1 -1
- package/dist/utils/lookupTable.d.ts +0 -27
- package/dist/utils/lookupTable.d.ts.map +1 -1
- package/dist/utils/lookupTable.js +0 -58
- package/dist/utils/lookupTable.js.map +1 -1
- package/dist/utils/managerTypes.d.ts.map +1 -1
- package/dist/utils/managerTypes.js +7 -52
- package/dist/utils/managerTypes.js.map +1 -1
- package/dist/utils/oracle.d.ts +3 -3
- package/dist/utils/oracle.d.ts.map +1 -1
- package/dist/utils/seeds.d.ts +1 -11
- package/dist/utils/seeds.d.ts.map +1 -1
- package/dist/utils/seeds.js +3 -13
- package/dist/utils/seeds.js.map +1 -1
- package/dist/utils/userMetadata.js +6 -6
- package/dist/utils/userMetadata.js.map +1 -1
- package/package.json +2 -2
- package/src/classes/action.ts +116 -532
- package/src/classes/lut_utils.ts +63 -0
- package/src/classes/manager.ts +1 -1
- package/src/classes/market.ts +34 -25
- package/src/classes/obligation.ts +7 -1
- package/src/classes/vault.ts +1 -1
- package/src/client.ts +8 -3
- package/src/lending_operations/repay_with_collateral_calcs.ts +5 -14
- package/src/lending_operations/repay_with_collateral_operations.ts +33 -72
- package/src/lending_operations/swap_collateral_operations.ts +19 -7
- package/src/leverage/operations.ts +114 -66
- package/src/leverage/types.ts +4 -4
- package/src/utils/ObligationType.ts +1 -1
- package/src/utils/lookupTable.ts +0 -62
- package/src/utils/managerTypes.ts +10 -52
- package/src/utils/oracle.ts +2 -2
- package/src/utils/seeds.ts +4 -14
- package/src/utils/userMetadata.ts +14 -14
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { AddressLookupTableProgram, Connection, PublicKey, TransactionInstruction } from '@solana/web3.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* This method retuns an instruction that creates a lookup table, alongside the pubkey of the lookup table
|
|
5
|
+
* @param payer - the owner of the lookup table
|
|
6
|
+
* @param slot - the current slot
|
|
7
|
+
* @returns - the instruction to create the lookup table and its address
|
|
8
|
+
*/
|
|
9
|
+
export function initLookupTableIx(payer: PublicKey, slot: number): [TransactionInstruction, PublicKey] {
|
|
10
|
+
const [ixn, address] = AddressLookupTableProgram.createLookupTable({
|
|
11
|
+
authority: payer,
|
|
12
|
+
payer,
|
|
13
|
+
recentSlot: slot,
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
return [ixn, address];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* This method retuns an instruction that deactivates a lookup table, which is needed to close it
|
|
21
|
+
* @param payer - the owner of the lookup table
|
|
22
|
+
* @param lookupTable - the lookup table to deactivate
|
|
23
|
+
* @returns - the instruction to deactivate the lookup table
|
|
24
|
+
*/
|
|
25
|
+
export function deactivateLookupTableIx(payer: PublicKey, lookupTable: PublicKey): TransactionInstruction {
|
|
26
|
+
const ixn = AddressLookupTableProgram.deactivateLookupTable({
|
|
27
|
+
authority: payer,
|
|
28
|
+
lookupTable: lookupTable,
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
return ixn;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* This method returns an instruction that closes a lookup table. That lookup table needs to be disabled at least 500 blocks before closing it.
|
|
36
|
+
* @param payer - the owner of the lookup table
|
|
37
|
+
* @param lookupTable - the lookup table to close
|
|
38
|
+
* @returns - the instruction to close the lookup table
|
|
39
|
+
*/
|
|
40
|
+
/// this require the LUT to be deactivated at least 500 blocks before
|
|
41
|
+
export function closeLookupTableIx(payer: PublicKey, lookupTable: PublicKey): TransactionInstruction {
|
|
42
|
+
const ixn = AddressLookupTableProgram.closeLookupTable({
|
|
43
|
+
authority: payer,
|
|
44
|
+
recipient: payer,
|
|
45
|
+
lookupTable: lookupTable,
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
return ixn;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Returns the accounts in a lookup table
|
|
53
|
+
* @param lookupTable - lookup table to get the accounts from
|
|
54
|
+
* @returns - an array of accounts in the lookup table
|
|
55
|
+
*/
|
|
56
|
+
export async function getAccountsInLUT(connection: Connection, lookupTable: PublicKey): Promise<PublicKey[]> {
|
|
57
|
+
const lutState = await connection.getAddressLookupTable(lookupTable);
|
|
58
|
+
if (!lutState || !lutState.value) {
|
|
59
|
+
throw new Error(`Lookup table ${lookupTable} not found`);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return lutState.value.state.addresses;
|
|
63
|
+
}
|
package/src/classes/manager.ts
CHANGED
|
@@ -949,7 +949,7 @@ export class KaminoManager {
|
|
|
949
949
|
}
|
|
950
950
|
|
|
951
951
|
/**
|
|
952
|
-
* This
|
|
952
|
+
* This returns an array of scope oracle configs to be used to set the scope price and twap oracles for a reserve
|
|
953
953
|
* @param feed - scope feed to fetch prices from
|
|
954
954
|
* @param cluster - cluster to fetch from, this should be left unchanged unless working on devnet or locally
|
|
955
955
|
* @returns - an array of scope oracle configs
|
package/src/classes/market.ts
CHANGED
|
@@ -29,7 +29,7 @@ import Decimal from 'decimal.js';
|
|
|
29
29
|
import { FarmState } from '@kamino-finance/farms-sdk';
|
|
30
30
|
import { PROGRAM_ID } from '../idl_codegen/programId';
|
|
31
31
|
import bs58 from 'bs58';
|
|
32
|
-
import { OraclePrices, Scope } from '@kamino-finance/scope-sdk';
|
|
32
|
+
import { OraclePrices, Scope, U16_MAX } from '@kamino-finance/scope-sdk';
|
|
33
33
|
import { Fraction } from './fraction';
|
|
34
34
|
import { chunks, KaminoPrices, MintToPriceMap } from '@kamino-finance/kliquidity-sdk';
|
|
35
35
|
import { parseTokenSymbol, parseZeroPaddedUtf8 } from './utils';
|
|
@@ -59,8 +59,6 @@ export class KaminoMarket {
|
|
|
59
59
|
|
|
60
60
|
readonly programId: PublicKey;
|
|
61
61
|
|
|
62
|
-
scope: Scope;
|
|
63
|
-
|
|
64
62
|
private readonly recentSlotDurationMs: number;
|
|
65
63
|
|
|
66
64
|
private constructor(
|
|
@@ -68,7 +66,6 @@ export class KaminoMarket {
|
|
|
68
66
|
state: LendingMarket,
|
|
69
67
|
marketAddress: string,
|
|
70
68
|
reserves: Map<PublicKey, KaminoReserve>,
|
|
71
|
-
scope: Scope,
|
|
72
69
|
recentSlotDurationMs: number,
|
|
73
70
|
programId: PublicKey = PROGRAM_ID
|
|
74
71
|
) {
|
|
@@ -78,7 +75,6 @@ export class KaminoMarket {
|
|
|
78
75
|
this.reserves = reserves;
|
|
79
76
|
this.reservesActive = getReservesActive(this.reserves);
|
|
80
77
|
this.programId = programId;
|
|
81
|
-
this.scope = scope;
|
|
82
78
|
this.recentSlotDurationMs = recentSlotDurationMs;
|
|
83
79
|
}
|
|
84
80
|
|
|
@@ -97,7 +93,6 @@ export class KaminoMarket {
|
|
|
97
93
|
marketAddress: PublicKey,
|
|
98
94
|
recentSlotDurationMs: number,
|
|
99
95
|
programId: PublicKey = PROGRAM_ID,
|
|
100
|
-
setupLocalTest: boolean = false,
|
|
101
96
|
withReserves: boolean = true
|
|
102
97
|
) {
|
|
103
98
|
const market = await LendingMarket.fetch(connection, marketAddress, programId);
|
|
@@ -105,26 +100,12 @@ export class KaminoMarket {
|
|
|
105
100
|
if (market === null) {
|
|
106
101
|
return null;
|
|
107
102
|
}
|
|
108
|
-
let scope: Scope;
|
|
109
|
-
if (!setupLocalTest) {
|
|
110
|
-
scope = new Scope('mainnet-beta', connection);
|
|
111
|
-
} else {
|
|
112
|
-
scope = new Scope('localnet', connection);
|
|
113
|
-
}
|
|
114
103
|
|
|
115
104
|
const reserves = withReserves
|
|
116
105
|
? await getReservesForMarket(marketAddress, connection, programId, recentSlotDurationMs)
|
|
117
106
|
: new Map<PublicKey, KaminoReserve>();
|
|
118
107
|
|
|
119
|
-
return new KaminoMarket(
|
|
120
|
-
connection,
|
|
121
|
-
market,
|
|
122
|
-
marketAddress.toString(),
|
|
123
|
-
reserves,
|
|
124
|
-
scope,
|
|
125
|
-
recentSlotDurationMs,
|
|
126
|
-
programId
|
|
127
|
-
);
|
|
108
|
+
return new KaminoMarket(connection, market, marketAddress.toString(), reserves, recentSlotDurationMs, programId);
|
|
128
109
|
}
|
|
129
110
|
|
|
130
111
|
async reload(): Promise<void> {
|
|
@@ -1186,9 +1167,9 @@ export class KaminoMarket {
|
|
|
1186
1167
|
/**
|
|
1187
1168
|
* Get all Scope prices used by all the market reserves
|
|
1188
1169
|
*/
|
|
1189
|
-
async getAllScopePrices(oraclePrices?: OraclePrices): Promise<KaminoPrices> {
|
|
1170
|
+
async getAllScopePrices(scope: Scope, oraclePrices?: OraclePrices): Promise<KaminoPrices> {
|
|
1190
1171
|
if (!oraclePrices) {
|
|
1191
|
-
oraclePrices = await
|
|
1172
|
+
oraclePrices = await scope.getOraclePrices();
|
|
1192
1173
|
}
|
|
1193
1174
|
const spot: MintToPriceMap = {};
|
|
1194
1175
|
const twaps: MintToPriceMap = {};
|
|
@@ -1199,11 +1180,11 @@ export class KaminoMarket {
|
|
|
1199
1180
|
const chain = reserve.state.config.tokenInfo.scopeConfiguration.priceChain;
|
|
1200
1181
|
const twapChain = reserve.state.config.tokenInfo.scopeConfiguration.twapChain.filter((x) => x > 0);
|
|
1201
1182
|
if (oracle && isNotNullPubkey(oracle) && chain && Scope.isScopeChainValid(chain)) {
|
|
1202
|
-
const spotPrice = await
|
|
1183
|
+
const spotPrice = await scope.getPriceFromChain(chain, oraclePrices);
|
|
1203
1184
|
spot[tokenMint] = { price: spotPrice.price, name: tokenName };
|
|
1204
1185
|
}
|
|
1205
1186
|
if (oracle && isNotNullPubkey(oracle) && twapChain && Scope.isScopeChainValid(twapChain)) {
|
|
1206
|
-
const twap = await
|
|
1187
|
+
const twap = await scope.getPriceFromChain(twapChain, oraclePrices);
|
|
1207
1188
|
twaps[tokenMint] = { price: twap.price, name: tokenName };
|
|
1208
1189
|
}
|
|
1209
1190
|
}
|
|
@@ -1527,6 +1508,34 @@ export function getReservesActive(reserves: Map<PublicKey, KaminoReserve>): Map<
|
|
|
1527
1508
|
return reservesActive;
|
|
1528
1509
|
}
|
|
1529
1510
|
|
|
1511
|
+
export function getTokenIdsForScopeRefresh(kaminoMarket: KaminoMarket, reserves: PublicKey[]): number[] {
|
|
1512
|
+
const tokenIds: number[] = [];
|
|
1513
|
+
|
|
1514
|
+
for (const reserveAddress of reserves) {
|
|
1515
|
+
const reserve = kaminoMarket.getReserveByAddress(reserveAddress);
|
|
1516
|
+
if (!reserve) {
|
|
1517
|
+
throw new Error(`Reserve not found for reserve ${reserveAddress.toBase58()}`);
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1520
|
+
if (!reserve.state.config.tokenInfo.scopeConfiguration.priceFeed.equals(PublicKey.default)) {
|
|
1521
|
+
let x = 0;
|
|
1522
|
+
|
|
1523
|
+
while (reserve.state.config.tokenInfo.scopeConfiguration.priceChain[x] !== U16_MAX) {
|
|
1524
|
+
tokenIds.push(reserve.state.config.tokenInfo.scopeConfiguration.priceChain[x]);
|
|
1525
|
+
x++;
|
|
1526
|
+
}
|
|
1527
|
+
|
|
1528
|
+
x = 0;
|
|
1529
|
+
while (reserve.state.config.tokenInfo.scopeConfiguration.twapChain[x] !== U16_MAX) {
|
|
1530
|
+
tokenIds.push(reserve.state.config.tokenInfo.scopeConfiguration.twapChain[x]);
|
|
1531
|
+
x++;
|
|
1532
|
+
}
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1535
|
+
|
|
1536
|
+
return tokenIds;
|
|
1537
|
+
}
|
|
1538
|
+
|
|
1530
1539
|
export async function getReserveFromMintAndMarket(
|
|
1531
1540
|
connection: Connection,
|
|
1532
1541
|
market: KaminoMarket,
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
getObligationPdaWithArgs,
|
|
18
18
|
getObligationType,
|
|
19
19
|
isNotNullPubkey,
|
|
20
|
+
ObligationType,
|
|
20
21
|
PubkeyHashMap,
|
|
21
22
|
TOTAL_NUMBER_OF_IDS_TO_CHECK,
|
|
22
23
|
U64_MAX,
|
|
@@ -266,7 +267,7 @@ export class KaminoObligation {
|
|
|
266
267
|
/**
|
|
267
268
|
* @returns total borrow power of the obligation, relative to max LTV of each asset's reserve
|
|
268
269
|
*/
|
|
269
|
-
|
|
270
|
+
getAllowedBorrowValue(): Decimal {
|
|
270
271
|
return new Fraction(this.state.allowedBorrowValueSf).toDecimal();
|
|
271
272
|
}
|
|
272
273
|
|
|
@@ -1593,3 +1594,8 @@ export class KaminoObligation {
|
|
|
1593
1594
|
}
|
|
1594
1595
|
}
|
|
1595
1596
|
}
|
|
1597
|
+
|
|
1598
|
+
// Create a function that checks if an obligation is of type obligation or obligationType
|
|
1599
|
+
export function isKaminoObligation(obligation: KaminoObligation | ObligationType): obligation is KaminoObligation {
|
|
1600
|
+
return 'obligationAddress' in obligation;
|
|
1601
|
+
}
|
package/src/classes/vault.ts
CHANGED
|
@@ -82,7 +82,7 @@ import {
|
|
|
82
82
|
import { batchFetch, collToLamportsDecimal, ZERO } from '@kamino-finance/kliquidity-sdk';
|
|
83
83
|
import { FullBPSDecimal } from '@kamino-finance/kliquidity-sdk/dist/utils/CreationParameters';
|
|
84
84
|
import { FarmState } from '@kamino-finance/farms-sdk/dist';
|
|
85
|
-
import { getAccountsInLUT, initLookupTableIx } from '
|
|
85
|
+
import { getAccountsInLUT, initLookupTableIx } from './lut_utils';
|
|
86
86
|
import {
|
|
87
87
|
getFarmStakeIxs,
|
|
88
88
|
getFarmUnstakeAndWithdrawIxs,
|
package/src/client.ts
CHANGED
|
@@ -22,6 +22,7 @@ import { VanillaObligation } from './utils/ObligationType';
|
|
|
22
22
|
import { parseTokenSymbol } from './classes/utils';
|
|
23
23
|
import { Env, initEnv } from '../tests/runner/setup_utils';
|
|
24
24
|
import { initializeFarmsForReserve } from '../tests/runner/farms/farms_operations';
|
|
25
|
+
import { Scope } from '@kamino-finance/scope-sdk';
|
|
25
26
|
|
|
26
27
|
const STAGING_LENDING_MARKET = new PublicKey('6WVSwDQXrBZeQVnu6hpnsRZhodaJTZBUaC334SiiBKdb');
|
|
27
28
|
const MAINNET_LENDING_MARKET = new PublicKey('7u3HeHxYDLhnCoErrtycNokbQYbWGzLs6JSDqGAv5PfF');
|
|
@@ -309,7 +310,8 @@ async function deposit(connection: Connection, wallet: Keypair, token: string, d
|
|
|
309
310
|
kaminoMarket.getReserveBySymbol(token)!.getLiquidityMint(),
|
|
310
311
|
wallet.publicKey,
|
|
311
312
|
new VanillaObligation(STAGING_LENDING_MARKET),
|
|
312
|
-
true
|
|
313
|
+
true,
|
|
314
|
+
{ scope: new Scope('mainnet-beta', connection), scopeFeed: 'hubble' }
|
|
313
315
|
);
|
|
314
316
|
console.log('User obligation', kaminoAction.obligation!.obligationAddress.toString());
|
|
315
317
|
|
|
@@ -330,7 +332,8 @@ async function withdraw(connection: Connection, wallet: Keypair, token: string,
|
|
|
330
332
|
kaminoMarket.getReserveBySymbol(token)!.getLiquidityMint(),
|
|
331
333
|
wallet.publicKey,
|
|
332
334
|
new VanillaObligation(new PublicKey(STAGING_LENDING_MARKET)),
|
|
333
|
-
true
|
|
335
|
+
true,
|
|
336
|
+
{ scope: new Scope('mainnet-beta', connection), scopeFeed: 'hubble' }
|
|
334
337
|
);
|
|
335
338
|
console.log('User obligation', kaminoAction.obligation!.obligationAddress.toString());
|
|
336
339
|
|
|
@@ -351,7 +354,8 @@ async function borrow(connection: Connection, wallet: Keypair, token: string, bo
|
|
|
351
354
|
kaminoMarket.getReserveBySymbol(token)!.getLiquidityMint(),
|
|
352
355
|
wallet.publicKey,
|
|
353
356
|
new VanillaObligation(new PublicKey(STAGING_LENDING_MARKET)),
|
|
354
|
-
true
|
|
357
|
+
true,
|
|
358
|
+
{ scope: new Scope('mainnet-beta', connection), scopeFeed: 'hubble' }
|
|
355
359
|
);
|
|
356
360
|
console.log('User obligation', kaminoAction.obligation!.obligationAddress.toString());
|
|
357
361
|
|
|
@@ -373,6 +377,7 @@ async function repay(connection: Connection, wallet: Keypair, token: string, bor
|
|
|
373
377
|
wallet.publicKey,
|
|
374
378
|
new VanillaObligation(new PublicKey(STAGING_LENDING_MARKET)),
|
|
375
379
|
true,
|
|
380
|
+
{ scope: new Scope('mainnet-beta', connection), scopeFeed: 'hubble' },
|
|
376
381
|
await connection.getSlot()
|
|
377
382
|
);
|
|
378
383
|
console.log('User obligation', kaminoAction.obligation!.obligationAddress.toString());
|
|
@@ -2,7 +2,6 @@ import Decimal from 'decimal.js';
|
|
|
2
2
|
import { KaminoMarket, KaminoObligation, KaminoReserve, numberToLamportsDecimal } from '../classes';
|
|
3
3
|
import { PublicKey } from '@solana/web3.js';
|
|
4
4
|
import { lamportsToDecimal } from '../classes/utils';
|
|
5
|
-
import { MaxWithdrawLtvCheck, getMaxWithdrawLtvCheck } from './repay_with_collateral_operations';
|
|
6
5
|
|
|
7
6
|
export function calcRepayAmountWithSlippage(
|
|
8
7
|
kaminoMarket: KaminoMarket,
|
|
@@ -103,7 +102,6 @@ export function calcMaxWithdrawCollateral(
|
|
|
103
102
|
.filter((p) => !p.reserveAddress.equals(borrow.reserveAddress))
|
|
104
103
|
.reduce((acc, b) => acc.add(b.marketValueRefreshed), new Decimal('0'));
|
|
105
104
|
}
|
|
106
|
-
const maxWithdrawLtvCheck = getMaxWithdrawLtvCheck(obligation);
|
|
107
105
|
|
|
108
106
|
let remainingDepositsValueWithLtv = new Decimal('0');
|
|
109
107
|
if (obligation.getDeposits().length > 1) {
|
|
@@ -111,13 +109,8 @@ export function calcMaxWithdrawCollateral(
|
|
|
111
109
|
.getDeposits()
|
|
112
110
|
.filter((p) => !p.reserveAddress.equals(deposit.reserveAddress))
|
|
113
111
|
.reduce((acc, d) => {
|
|
114
|
-
const { maxLtv
|
|
115
|
-
|
|
116
|
-
market.getReserveByAddress(d.reserveAddress)!
|
|
117
|
-
);
|
|
118
|
-
const maxWithdrawLtv =
|
|
119
|
-
maxWithdrawLtvCheck === MaxWithdrawLtvCheck.LIQUIDATION_THRESHOLD ? liquidationLtv : maxLtv;
|
|
120
|
-
return acc.add(d.marketValueRefreshed.mul(maxWithdrawLtv));
|
|
112
|
+
const { maxLtv } = obligation.getLtvForReserve(market, market.getReserveByAddress(d.reserveAddress)!);
|
|
113
|
+
return acc.add(d.marketValueRefreshed.mul(maxLtv));
|
|
121
114
|
}, new Decimal('0'));
|
|
122
115
|
}
|
|
123
116
|
|
|
@@ -130,18 +123,16 @@ export function calcMaxWithdrawCollateral(
|
|
|
130
123
|
repayingAllDebt: repayAmountLamports.gte(borrow.amount),
|
|
131
124
|
};
|
|
132
125
|
} else {
|
|
133
|
-
const { maxLtv: collMaxLtv
|
|
126
|
+
const { maxLtv: collMaxLtv } = obligation.getLtvForReserve(
|
|
134
127
|
market,
|
|
135
128
|
market.getReserveByAddress(depositReserve.address)!
|
|
136
129
|
);
|
|
137
|
-
const maxWithdrawLtv =
|
|
138
|
-
maxWithdrawLtvCheck === MaxWithdrawLtvCheck.LIQUIDATION_THRESHOLD ? collLiquidationLtv : collMaxLtv;
|
|
139
130
|
const numerator = deposit.marketValueRefreshed
|
|
140
|
-
.mul(
|
|
131
|
+
.mul(collMaxLtv)
|
|
141
132
|
.add(remainingDepositsValueWithLtv)
|
|
142
133
|
.sub(remainingBorrowsValue);
|
|
143
134
|
|
|
144
|
-
const denominator = depositReserve.getOracleMarketPrice().mul(
|
|
135
|
+
const denominator = depositReserve.getOracleMarketPrice().mul(collMaxLtv);
|
|
145
136
|
const maxCollWithdrawAmount = numerator.div(denominator);
|
|
146
137
|
const withdrawableCollLamports = maxCollWithdrawAmount.mul(depositReserve.getMintFactor()).floor();
|
|
147
138
|
|
|
@@ -6,12 +6,13 @@ import {
|
|
|
6
6
|
SwapIxs,
|
|
7
7
|
SwapIxsProvider,
|
|
8
8
|
SwapQuoteProvider,
|
|
9
|
+
getScopeRefreshIx,
|
|
9
10
|
} from '../leverage';
|
|
10
11
|
import {
|
|
11
12
|
createAtasIdempotent,
|
|
12
13
|
getComputeBudgetAndPriorityFeeIxns,
|
|
13
14
|
removeBudgetAndAtaIxns,
|
|
14
|
-
|
|
15
|
+
ScopePriceRefreshConfig,
|
|
15
16
|
U64_MAX,
|
|
16
17
|
uniqueAccounts,
|
|
17
18
|
} from '../utils';
|
|
@@ -51,16 +52,11 @@ interface RepayWithCollSwapInputsProps<QuoteResponse> {
|
|
|
51
52
|
repayAmount: Decimal;
|
|
52
53
|
isClosingPosition: boolean;
|
|
53
54
|
budgetAndPriorityFeeIxs?: TransactionInstruction[];
|
|
54
|
-
|
|
55
|
+
scopeRefreshConfig?: ScopePriceRefreshConfig;
|
|
55
56
|
useV2Ixs: boolean;
|
|
56
57
|
quoter: SwapQuoteProvider<QuoteResponse>;
|
|
57
58
|
}
|
|
58
59
|
|
|
59
|
-
export enum MaxWithdrawLtvCheck {
|
|
60
|
-
MAX_LTV,
|
|
61
|
-
LIQUIDATION_THRESHOLD,
|
|
62
|
-
}
|
|
63
|
-
|
|
64
60
|
export async function getRepayWithCollSwapInputs<QuoteResponse>({
|
|
65
61
|
collTokenMint,
|
|
66
62
|
currentSlot,
|
|
@@ -72,7 +68,7 @@ export async function getRepayWithCollSwapInputs<QuoteResponse>({
|
|
|
72
68
|
repayAmount,
|
|
73
69
|
isClosingPosition,
|
|
74
70
|
budgetAndPriorityFeeIxs,
|
|
75
|
-
|
|
71
|
+
scopeRefreshConfig,
|
|
76
72
|
useV2Ixs,
|
|
77
73
|
}: RepayWithCollSwapInputsProps<QuoteResponse>): Promise<{
|
|
78
74
|
swapInputs: SwapInputs;
|
|
@@ -131,7 +127,7 @@ export async function getRepayWithCollSwapInputs<QuoteResponse>({
|
|
|
131
127
|
referrer,
|
|
132
128
|
currentSlot,
|
|
133
129
|
budgetAndPriorityFeeIxs,
|
|
134
|
-
|
|
130
|
+
scopeRefreshConfig,
|
|
135
131
|
{
|
|
136
132
|
preActionIxs: [],
|
|
137
133
|
swapIxs: [],
|
|
@@ -196,7 +192,7 @@ export async function getRepayWithCollIxs<QuoteResponse>({
|
|
|
196
192
|
quoter,
|
|
197
193
|
swapper,
|
|
198
194
|
referrer,
|
|
199
|
-
|
|
195
|
+
scopeRefreshConfig,
|
|
200
196
|
useV2Ixs,
|
|
201
197
|
logger = console.log,
|
|
202
198
|
}: RepayWithCollIxsProps<QuoteResponse>): Promise<RepayWithCollIxsResponse<QuoteResponse>> {
|
|
@@ -211,23 +207,14 @@ export async function getRepayWithCollIxs<QuoteResponse>({
|
|
|
211
207
|
repayAmount,
|
|
212
208
|
isClosingPosition,
|
|
213
209
|
budgetAndPriorityFeeIxs,
|
|
214
|
-
|
|
210
|
+
scopeRefreshConfig,
|
|
215
211
|
useV2Ixs,
|
|
216
212
|
});
|
|
217
213
|
const { debtRepayAmountLamports, flashRepayAmountLamports, maxCollateralWithdrawLamports, swapQuote } = initialInputs;
|
|
218
214
|
const { inputAmountLamports: collSwapInLamports } = swapInputs;
|
|
219
215
|
|
|
220
|
-
const collReserve = kaminoMarket.getReserveByMint(collTokenMint)
|
|
221
|
-
|
|
222
|
-
if (!collReserve) {
|
|
223
|
-
throw new Error(`Collateral reserve with mint ${collTokenMint} not found in market ${kaminoMarket.getAddress()}`);
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
const debtReserve = kaminoMarket.getReserveByMint(debtTokenMint);
|
|
227
|
-
|
|
228
|
-
if (!debtReserve) {
|
|
229
|
-
throw new Error(`Debt reserve with mint ${debtTokenMint} not found in market ${kaminoMarket.getAddress()}`);
|
|
230
|
-
}
|
|
216
|
+
const collReserve = kaminoMarket.getReserveByMint(collTokenMint)!;
|
|
217
|
+
const debtReserve = kaminoMarket.getReserveByMint(debtTokenMint)!;
|
|
231
218
|
|
|
232
219
|
// the client should use these values to prevent this input, but the tx may succeed, so we don't want to fail
|
|
233
220
|
// there is also a chance that the tx will consume debt token from the user's ata which they would not expect
|
|
@@ -258,7 +245,7 @@ export async function getRepayWithCollIxs<QuoteResponse>({
|
|
|
258
245
|
referrer,
|
|
259
246
|
currentSlot,
|
|
260
247
|
budgetAndPriorityFeeIxs,
|
|
261
|
-
|
|
248
|
+
scopeRefreshConfig,
|
|
262
249
|
swapResponse,
|
|
263
250
|
isClosingPosition,
|
|
264
251
|
debtRepayAmountLamports,
|
|
@@ -282,7 +269,7 @@ async function buildRepayWithCollateralIxs(
|
|
|
282
269
|
referrer: PublicKey,
|
|
283
270
|
currentSlot: number,
|
|
284
271
|
budgetAndPriorityFeeIxs: TransactionInstruction[] | undefined,
|
|
285
|
-
|
|
272
|
+
scopeRefreshConfig: ScopePriceRefreshConfig | undefined,
|
|
286
273
|
swapQuoteIxs: SwapIxs,
|
|
287
274
|
isClosingPosition: boolean,
|
|
288
275
|
debtRepayAmountLamports: Decimal,
|
|
@@ -300,9 +287,11 @@ async function buildRepayWithCollateralIxs(
|
|
|
300
287
|
const atasAndIxs = createAtasIdempotent(obligation.state.owner, atas);
|
|
301
288
|
const [, { ata: debtTokenAta }] = atasAndIxs;
|
|
302
289
|
|
|
290
|
+
const scopeRefreshIxn = await getScopeRefreshIx(market, collReserve, debtReserve, obligation, scopeRefreshConfig);
|
|
291
|
+
|
|
303
292
|
// 2. Flash borrow & repay the debt to repay amount needed
|
|
304
293
|
const { flashBorrowIxn, flashRepayIxn } = getFlashLoanInstructions({
|
|
305
|
-
borrowIxnIndex: budgetIxns.length + atasAndIxs.length,
|
|
294
|
+
borrowIxnIndex: budgetIxns.length + atasAndIxs.length + (scopeRefreshIxn.length > 0 ? 1 : 0),
|
|
306
295
|
walletPublicKey: obligation.state.owner,
|
|
307
296
|
lendingMarketAuthority: market.getLendingMarketAuthority(),
|
|
308
297
|
lendingMarketAddress: market.getAddress(),
|
|
@@ -317,54 +306,32 @@ async function buildRepayWithCollateralIxs(
|
|
|
317
306
|
|
|
318
307
|
const requestElevationGroup = !isClosingPosition && obligation.state.elevationGroup !== 0;
|
|
319
308
|
|
|
320
|
-
const maxWithdrawLtvCheck = getMaxWithdrawLtvCheck(obligation);
|
|
321
|
-
|
|
322
309
|
// 3. Repay using the flash borrowed funds & withdraw collateral to swap and pay the flash loan
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
scopeRefresh
|
|
342
|
-
);
|
|
343
|
-
} else {
|
|
344
|
-
repayAndWithdrawAction = await KaminoAction.buildRepayAndWithdrawV2Txns(
|
|
345
|
-
market,
|
|
346
|
-
isClosingPosition ? U64_MAX : debtRepayAmountLamports.toString(),
|
|
347
|
-
debtReserve.getLiquidityMint(),
|
|
348
|
-
isClosingPosition ? U64_MAX : collWithdrawLamports.toString(),
|
|
349
|
-
collReserve.getLiquidityMint(),
|
|
350
|
-
obligation.state.owner,
|
|
351
|
-
currentSlot,
|
|
352
|
-
obligation,
|
|
353
|
-
0,
|
|
354
|
-
false,
|
|
355
|
-
requestElevationGroup,
|
|
356
|
-
undefined,
|
|
357
|
-
undefined,
|
|
358
|
-
referrer,
|
|
359
|
-
scopeRefresh
|
|
360
|
-
);
|
|
361
|
-
}
|
|
310
|
+
const repayAndWithdrawAction = await KaminoAction.buildRepayAndWithdrawTxns(
|
|
311
|
+
market,
|
|
312
|
+
isClosingPosition ? U64_MAX : debtRepayAmountLamports.toString(),
|
|
313
|
+
debtReserve.getLiquidityMint(),
|
|
314
|
+
isClosingPosition ? U64_MAX : collWithdrawLamports.toString(),
|
|
315
|
+
collReserve.getLiquidityMint(),
|
|
316
|
+
obligation.state.owner,
|
|
317
|
+
currentSlot,
|
|
318
|
+
obligation,
|
|
319
|
+
useV2Ixs,
|
|
320
|
+
undefined,
|
|
321
|
+
0,
|
|
322
|
+
false,
|
|
323
|
+
requestElevationGroup,
|
|
324
|
+
undefined,
|
|
325
|
+
undefined,
|
|
326
|
+
referrer
|
|
327
|
+
);
|
|
362
328
|
|
|
363
329
|
// 4. Swap collateral to debt to repay flash loan
|
|
364
330
|
const { preActionIxs, swapIxs } = swapQuoteIxs;
|
|
365
331
|
const swapInstructions = removeBudgetAndAtaIxns(swapIxs, []);
|
|
366
332
|
|
|
367
333
|
return [
|
|
334
|
+
...scopeRefreshIxn,
|
|
368
335
|
...budgetIxns,
|
|
369
336
|
...atasAndIxs.map((x) => x.createAtaIx),
|
|
370
337
|
flashBorrowIxn,
|
|
@@ -374,9 +341,3 @@ async function buildRepayWithCollateralIxs(
|
|
|
374
341
|
flashRepayIxn,
|
|
375
342
|
];
|
|
376
343
|
}
|
|
377
|
-
|
|
378
|
-
export const getMaxWithdrawLtvCheck = (obligation: KaminoObligation) => {
|
|
379
|
-
return obligation.refreshedStats.userTotalBorrowBorrowFactorAdjusted.gte(obligation.refreshedStats.borrowLimit)
|
|
380
|
-
? MaxWithdrawLtvCheck.LIQUIDATION_THRESHOLD
|
|
381
|
-
: MaxWithdrawLtvCheck.MAX_LTV;
|
|
382
|
-
};
|
|
@@ -6,14 +6,14 @@ import {
|
|
|
6
6
|
KaminoObligation,
|
|
7
7
|
KaminoReserve,
|
|
8
8
|
} from '../classes';
|
|
9
|
-
import { getFlashLoanInstructions, SwapIxsProvider, SwapQuoteProvider } from '../leverage';
|
|
9
|
+
import { getFlashLoanInstructions, getScopeRefreshIx, SwapIxsProvider, SwapQuoteProvider } from '../leverage';
|
|
10
10
|
import {
|
|
11
11
|
createAtasIdempotent,
|
|
12
12
|
DEFAULT_MAX_COMPUTE_UNITS,
|
|
13
13
|
getAssociatedTokenAddress,
|
|
14
14
|
getComputeBudgetAndPriorityFeeIxns,
|
|
15
15
|
PublicKeySet,
|
|
16
|
-
|
|
16
|
+
ScopePriceRefreshConfig,
|
|
17
17
|
U64_MAX,
|
|
18
18
|
uniqueAccounts,
|
|
19
19
|
} from '../utils';
|
|
@@ -61,7 +61,7 @@ export interface SwapCollIxnsInputs<QuoteResponse> {
|
|
|
61
61
|
referrer: PublicKey;
|
|
62
62
|
currentSlot: number;
|
|
63
63
|
budgetAndPriorityFeeIxns?: TransactionInstruction[];
|
|
64
|
-
|
|
64
|
+
scopeRefreshConfig?: ScopePriceRefreshConfig;
|
|
65
65
|
useV2Ixs: boolean;
|
|
66
66
|
quoter: SwapQuoteProvider<QuoteResponse>;
|
|
67
67
|
swapper: SwapIxsProvider<QuoteResponse>;
|
|
@@ -205,7 +205,7 @@ type SwapCollContext<QuoteResponse> = {
|
|
|
205
205
|
referrer: PublicKey;
|
|
206
206
|
currentSlot: number;
|
|
207
207
|
useV2Ixs: boolean;
|
|
208
|
-
|
|
208
|
+
scopeRefreshConfig: ScopePriceRefreshConfig | undefined;
|
|
209
209
|
logger: (msg: string, ...extra: any[]) => void;
|
|
210
210
|
};
|
|
211
211
|
|
|
@@ -235,7 +235,7 @@ function extractArgsAndContext<QuoteResponse>(
|
|
|
235
235
|
quoter: inputs.quoter,
|
|
236
236
|
swapper: inputs.swapper,
|
|
237
237
|
referrer: inputs.referrer,
|
|
238
|
-
|
|
238
|
+
scopeRefreshConfig: inputs.scopeRefreshConfig,
|
|
239
239
|
currentSlot: inputs.currentSlot,
|
|
240
240
|
useV2Ixs: inputs.useV2Ixs,
|
|
241
241
|
},
|
|
@@ -264,6 +264,18 @@ async function getKlendIxns(
|
|
|
264
264
|
const { ataCreationIxns, targetCollAta } = getAtaCreationIxns(context);
|
|
265
265
|
const setupIxns = [...context.budgetAndPriorityFeeIxns, ...ataCreationIxns];
|
|
266
266
|
|
|
267
|
+
const scopeRefreshIxn = await getScopeRefreshIx(
|
|
268
|
+
context.market,
|
|
269
|
+
context.sourceCollReserve,
|
|
270
|
+
context.targetCollReserve,
|
|
271
|
+
context.obligation,
|
|
272
|
+
context.scopeRefreshConfig
|
|
273
|
+
);
|
|
274
|
+
|
|
275
|
+
if (scopeRefreshIxn) {
|
|
276
|
+
setupIxns.unshift(...scopeRefreshIxn);
|
|
277
|
+
}
|
|
278
|
+
|
|
267
279
|
const targetCollFlashBorrowedAmount = calculateTargetCollFlashBorrowedAmount(targetCollSwapOutAmount, context);
|
|
268
280
|
const { targetCollFlashBorrowIxn, targetCollFlashRepayIxn } = getTargetCollFlashLoanIxns(
|
|
269
281
|
targetCollFlashBorrowedAmount,
|
|
@@ -380,6 +392,7 @@ async function getDepositTargetCollIxns(
|
|
|
380
392
|
context.obligation.state.owner,
|
|
381
393
|
context.obligation,
|
|
382
394
|
context.useV2Ixs,
|
|
395
|
+
undefined, // we create the scope refresh ixn outside of KaminoAction
|
|
383
396
|
0, // no extra compute budget
|
|
384
397
|
false, // we do not need ATA ixns here (we construct and close them ourselves)
|
|
385
398
|
removesElevationGroup, // we may need to (temporarily) remove the elevation group; the same or a different one will be set on withdraw, if requested
|
|
@@ -387,7 +400,6 @@ async function getDepositTargetCollIxns(
|
|
|
387
400
|
false, // we do not need to create a lookup table, dealing with an existing obligation
|
|
388
401
|
context.referrer,
|
|
389
402
|
context.currentSlot,
|
|
390
|
-
context.scopeRefresh,
|
|
391
403
|
removesElevationGroup ? 0 : undefined // only applicable when removing the group
|
|
392
404
|
);
|
|
393
405
|
return {
|
|
@@ -430,6 +442,7 @@ async function getWithdrawSourceCollIxns(
|
|
|
430
442
|
context.obligation.state.owner,
|
|
431
443
|
context.obligation,
|
|
432
444
|
context.useV2Ixs,
|
|
445
|
+
undefined, // we create the scope refresh ixn outside of KaminoAction
|
|
433
446
|
0, // no extra compute budget
|
|
434
447
|
false, // we do not need ATA ixns here (we construct and close them ourselves)
|
|
435
448
|
requestedElevationGroup !== undefined, // the `elevationGroupIdToRequestAfterWithdraw()` has already decided on this
|
|
@@ -437,7 +450,6 @@ async function getWithdrawSourceCollIxns(
|
|
|
437
450
|
false, // we do not need to create a lookup table, dealing with an existing obligation
|
|
438
451
|
context.referrer,
|
|
439
452
|
context.currentSlot,
|
|
440
|
-
undefined, // we have refreshed scope already, during depositing
|
|
441
453
|
requestedElevationGroup,
|
|
442
454
|
context.obligation.deposits.has(context.targetCollReserve.address) // if our obligation already had the target coll...
|
|
443
455
|
? undefined // ... then we need no customizations here, but otherwise...
|