@kamino-finance/klend-sdk 5.0.3 → 5.0.5
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 +38 -4
- package/dist/classes/manager.d.ts.map +1 -1
- package/dist/classes/manager.js +98 -4
- package/dist/classes/manager.js.map +1 -1
- package/dist/classes/market.d.ts.map +1 -1
- package/dist/classes/market.js +2 -25
- package/dist/classes/market.js.map +1 -1
- package/dist/classes/vault.d.ts +12 -8
- package/dist/classes/vault.d.ts.map +1 -1
- package/dist/classes/vault.js +94 -66
- package/dist/classes/vault.js.map +1 -1
- package/dist/client_kamino_manager.d.ts.map +1 -1
- package/dist/client_kamino_manager.js +14 -0
- package/dist/client_kamino_manager.js.map +1 -1
- package/dist/idl_codegen_kamino_vault/accounts/Reserve.d.ts +3 -3
- package/dist/idl_codegen_kamino_vault/accounts/Reserve.d.ts.map +1 -1
- package/dist/idl_codegen_kamino_vault/accounts/Reserve.js +22 -18
- package/dist/idl_codegen_kamino_vault/accounts/Reserve.js.map +1 -1
- package/dist/idl_codegen_kamino_vault/accounts/VaultState.d.ts +45 -15
- package/dist/idl_codegen_kamino_vault/accounts/VaultState.d.ts.map +1 -1
- package/dist/idl_codegen_kamino_vault/accounts/VaultState.js +101 -39
- package/dist/idl_codegen_kamino_vault/accounts/VaultState.js.map +1 -1
- package/dist/idl_codegen_kamino_vault/accounts/index.d.ts +4 -4
- package/dist/idl_codegen_kamino_vault/accounts/index.d.ts.map +1 -1
- package/dist/idl_codegen_kamino_vault/accounts/index.js.map +1 -1
- package/dist/idl_codegen_kamino_vault/errors/anchor.d.ts.map +1 -1
- package/dist/idl_codegen_kamino_vault/errors/anchor.js +162 -162
- package/dist/idl_codegen_kamino_vault/errors/anchor.js.map +1 -1
- package/dist/idl_codegen_kamino_vault/errors/custom.d.ts +144 -32
- package/dist/idl_codegen_kamino_vault/errors/custom.d.ts.map +1 -1
- package/dist/idl_codegen_kamino_vault/errors/custom.js +305 -109
- package/dist/idl_codegen_kamino_vault/errors/custom.js.map +1 -1
- package/dist/idl_codegen_kamino_vault/errors/index.d.ts +3 -3
- package/dist/idl_codegen_kamino_vault/errors/index.d.ts.map +1 -1
- package/dist/idl_codegen_kamino_vault/errors/index.js +7 -2
- package/dist/idl_codegen_kamino_vault/errors/index.js.map +1 -1
- package/dist/idl_codegen_kamino_vault/instructions/deposit.d.ts +4 -2
- package/dist/idl_codegen_kamino_vault/instructions/deposit.d.ts.map +1 -1
- package/dist/idl_codegen_kamino_vault/instructions/deposit.js +3 -1
- package/dist/idl_codegen_kamino_vault/instructions/deposit.js.map +1 -1
- package/dist/idl_codegen_kamino_vault/instructions/giveUpPendingFees.d.ts +13 -0
- package/dist/idl_codegen_kamino_vault/instructions/giveUpPendingFees.d.ts.map +1 -0
- package/dist/idl_codegen_kamino_vault/instructions/giveUpPendingFees.js +47 -0
- package/dist/idl_codegen_kamino_vault/instructions/giveUpPendingFees.js.map +1 -0
- package/dist/idl_codegen_kamino_vault/instructions/index.d.ts +18 -10
- package/dist/idl_codegen_kamino_vault/instructions/index.d.ts.map +1 -1
- package/dist/idl_codegen_kamino_vault/instructions/index.js +9 -1
- package/dist/idl_codegen_kamino_vault/instructions/index.js.map +1 -1
- package/dist/idl_codegen_kamino_vault/instructions/initVault.d.ts +2 -1
- package/dist/idl_codegen_kamino_vault/instructions/initVault.d.ts.map +1 -1
- package/dist/idl_codegen_kamino_vault/instructions/initVault.js +1 -0
- package/dist/idl_codegen_kamino_vault/instructions/initVault.js.map +1 -1
- package/dist/idl_codegen_kamino_vault/instructions/invest.d.ts +6 -2
- package/dist/idl_codegen_kamino_vault/instructions/invest.d.ts.map +1 -1
- package/dist/idl_codegen_kamino_vault/instructions/invest.js +9 -1
- package/dist/idl_codegen_kamino_vault/instructions/invest.js.map +1 -1
- package/dist/idl_codegen_kamino_vault/instructions/updateAdmin.d.ts +7 -0
- package/dist/idl_codegen_kamino_vault/instructions/updateAdmin.d.ts.map +1 -0
- package/dist/idl_codegen_kamino_vault/instructions/updateAdmin.js +16 -0
- package/dist/idl_codegen_kamino_vault/instructions/updateAdmin.js.map +1 -0
- package/dist/idl_codegen_kamino_vault/instructions/updateReserveAllocation.d.ts +2 -2
- package/dist/idl_codegen_kamino_vault/instructions/updateReserveAllocation.d.ts.map +1 -1
- package/dist/idl_codegen_kamino_vault/instructions/updateReserveAllocation.js +1 -1
- package/dist/idl_codegen_kamino_vault/instructions/updateReserveAllocation.js.map +1 -1
- package/dist/idl_codegen_kamino_vault/instructions/updateVaultConfig.d.ts +14 -0
- package/dist/idl_codegen_kamino_vault/instructions/updateVaultConfig.d.ts.map +1 -0
- package/dist/idl_codegen_kamino_vault/instructions/updateVaultConfig.js +52 -0
- package/dist/idl_codegen_kamino_vault/instructions/updateVaultConfig.js.map +1 -0
- package/dist/idl_codegen_kamino_vault/instructions/withdraw.d.ts +4 -2
- package/dist/idl_codegen_kamino_vault/instructions/withdraw.d.ts.map +1 -1
- package/dist/idl_codegen_kamino_vault/instructions/withdraw.js +8 -2
- package/dist/idl_codegen_kamino_vault/instructions/withdraw.js.map +1 -1
- package/dist/idl_codegen_kamino_vault/instructions/withdrawPendingFees.d.ts +23 -0
- package/dist/idl_codegen_kamino_vault/instructions/withdrawPendingFees.d.ts.map +1 -0
- package/dist/idl_codegen_kamino_vault/instructions/withdrawPendingFees.js +51 -0
- package/dist/idl_codegen_kamino_vault/instructions/withdrawPendingFees.js.map +1 -0
- package/dist/idl_codegen_kamino_vault/programId.d.ts +1 -1
- package/dist/idl_codegen_kamino_vault/programId.d.ts.map +1 -1
- package/dist/idl_codegen_kamino_vault/programId.js +1 -1
- package/dist/idl_codegen_kamino_vault/programId.js.map +1 -1
- package/dist/idl_codegen_kamino_vault/types/BigFractionBytes.d.ts +2 -2
- package/dist/idl_codegen_kamino_vault/types/BigFractionBytes.d.ts.map +1 -1
- package/dist/idl_codegen_kamino_vault/types/BigFractionBytes.js +4 -1
- package/dist/idl_codegen_kamino_vault/types/BigFractionBytes.js.map +1 -1
- package/dist/idl_codegen_kamino_vault/types/BorrowRateCurve.d.ts +1 -1
- package/dist/idl_codegen_kamino_vault/types/BorrowRateCurve.d.ts.map +1 -1
- package/dist/idl_codegen_kamino_vault/types/BorrowRateCurve.js +1 -1
- package/dist/idl_codegen_kamino_vault/types/BorrowRateCurve.js.map +1 -1
- package/dist/idl_codegen_kamino_vault/types/CurvePoint.d.ts +1 -1
- package/dist/idl_codegen_kamino_vault/types/CurvePoint.d.ts.map +1 -1
- package/dist/idl_codegen_kamino_vault/types/CurvePoint.js +1 -1
- package/dist/idl_codegen_kamino_vault/types/CurvePoint.js.map +1 -1
- package/dist/idl_codegen_kamino_vault/types/LastUpdate.d.ts +2 -2
- package/dist/idl_codegen_kamino_vault/types/LastUpdate.d.ts.map +1 -1
- package/dist/idl_codegen_kamino_vault/types/LastUpdate.js +6 -1
- package/dist/idl_codegen_kamino_vault/types/LastUpdate.js.map +1 -1
- package/dist/idl_codegen_kamino_vault/types/PriceHeuristic.d.ts +2 -2
- package/dist/idl_codegen_kamino_vault/types/PriceHeuristic.d.ts.map +1 -1
- package/dist/idl_codegen_kamino_vault/types/PriceHeuristic.js +1 -1
- package/dist/idl_codegen_kamino_vault/types/PriceHeuristic.js.map +1 -1
- package/dist/idl_codegen_kamino_vault/types/PythConfiguration.d.ts +2 -2
- package/dist/idl_codegen_kamino_vault/types/PythConfiguration.d.ts.map +1 -1
- package/dist/idl_codegen_kamino_vault/types/PythConfiguration.js +1 -1
- package/dist/idl_codegen_kamino_vault/types/PythConfiguration.js.map +1 -1
- package/dist/idl_codegen_kamino_vault/types/ReserveCollateral.d.ts +3 -3
- package/dist/idl_codegen_kamino_vault/types/ReserveCollateral.d.ts.map +1 -1
- package/dist/idl_codegen_kamino_vault/types/ReserveCollateral.js +5 -5
- package/dist/idl_codegen_kamino_vault/types/ReserveCollateral.js.map +1 -1
- package/dist/idl_codegen_kamino_vault/types/ReserveConfig.d.ts +27 -24
- package/dist/idl_codegen_kamino_vault/types/ReserveConfig.d.ts.map +1 -1
- package/dist/idl_codegen_kamino_vault/types/ReserveConfig.js +59 -53
- package/dist/idl_codegen_kamino_vault/types/ReserveConfig.js.map +1 -1
- package/dist/idl_codegen_kamino_vault/types/ReserveFees.d.ts +2 -2
- package/dist/idl_codegen_kamino_vault/types/ReserveFees.d.ts.map +1 -1
- package/dist/idl_codegen_kamino_vault/types/ReserveFees.js +5 -1
- package/dist/idl_codegen_kamino_vault/types/ReserveFees.js.map +1 -1
- package/dist/idl_codegen_kamino_vault/types/ReserveLiquidity.d.ts +11 -3
- package/dist/idl_codegen_kamino_vault/types/ReserveLiquidity.d.ts.map +1 -1
- package/dist/idl_codegen_kamino_vault/types/ReserveLiquidity.js +25 -17
- package/dist/idl_codegen_kamino_vault/types/ReserveLiquidity.js.map +1 -1
- package/dist/idl_codegen_kamino_vault/types/ScopeConfiguration.d.ts +2 -2
- package/dist/idl_codegen_kamino_vault/types/ScopeConfiguration.d.ts.map +1 -1
- package/dist/idl_codegen_kamino_vault/types/ScopeConfiguration.js +3 -3
- package/dist/idl_codegen_kamino_vault/types/ScopeConfiguration.js.map +1 -1
- package/dist/idl_codegen_kamino_vault/types/SwitchboardConfiguration.d.ts +2 -2
- package/dist/idl_codegen_kamino_vault/types/SwitchboardConfiguration.d.ts.map +1 -1
- package/dist/idl_codegen_kamino_vault/types/SwitchboardConfiguration.js +1 -1
- package/dist/idl_codegen_kamino_vault/types/SwitchboardConfiguration.js.map +1 -1
- package/dist/idl_codegen_kamino_vault/types/TokenInfo.d.ts +3 -3
- package/dist/idl_codegen_kamino_vault/types/TokenInfo.d.ts.map +1 -1
- package/dist/idl_codegen_kamino_vault/types/TokenInfo.js +11 -11
- package/dist/idl_codegen_kamino_vault/types/TokenInfo.js.map +1 -1
- package/dist/idl_codegen_kamino_vault/types/VaultAllocation.d.ts +18 -8
- package/dist/idl_codegen_kamino_vault/types/VaultAllocation.d.ts.map +1 -1
- package/dist/idl_codegen_kamino_vault/types/VaultAllocation.js +27 -13
- package/dist/idl_codegen_kamino_vault/types/VaultAllocation.js.map +1 -1
- package/dist/idl_codegen_kamino_vault/types/VaultConfigField.d.ts +110 -0
- package/dist/idl_codegen_kamino_vault/types/VaultConfigField.d.ts.map +1 -0
- package/dist/idl_codegen_kamino_vault/types/VaultConfigField.js +242 -0
- package/dist/idl_codegen_kamino_vault/types/VaultConfigField.js.map +1 -0
- package/dist/idl_codegen_kamino_vault/types/index.d.ts +34 -30
- package/dist/idl_codegen_kamino_vault/types/index.d.ts.map +1 -1
- package/dist/idl_codegen_kamino_vault/types/index.js +26 -1
- package/dist/idl_codegen_kamino_vault/types/index.js.map +1 -1
- package/dist/lending_operations/repay_with_collateral_operations.d.ts.map +1 -1
- package/dist/lending_operations/repay_with_collateral_operations.js +6 -0
- package/dist/lending_operations/repay_with_collateral_operations.js.map +1 -1
- package/dist/leverage/calcs.d.ts +28 -1
- package/dist/leverage/calcs.d.ts.map +1 -1
- package/dist/leverage/calcs.js +204 -8
- package/dist/leverage/calcs.js.map +1 -1
- package/dist/leverage/index.d.ts +1 -0
- package/dist/leverage/index.d.ts.map +1 -1
- package/dist/leverage/index.js +1 -0
- package/dist/leverage/index.js.map +1 -1
- package/dist/leverage/operations.d.ts +14 -241
- package/dist/leverage/operations.d.ts.map +1 -1
- package/dist/leverage/operations.js +508 -776
- package/dist/leverage/operations.js.map +1 -1
- package/dist/leverage/types.d.ts +173 -0
- package/dist/leverage/types.d.ts.map +1 -0
- package/dist/leverage/types.js +3 -0
- package/dist/leverage/types.js.map +1 -0
- package/dist/leverage/utils.d.ts +5 -5
- package/dist/leverage/utils.d.ts.map +1 -1
- package/dist/leverage/utils.js +68 -33
- package/dist/leverage/utils.js.map +1 -1
- package/dist/utils/constants.d.ts +1 -0
- package/dist/utils/constants.d.ts.map +1 -1
- package/dist/utils/constants.js +2 -1
- package/dist/utils/constants.js.map +1 -1
- package/dist/utils/fuzz.d.ts +3 -0
- package/dist/utils/fuzz.d.ts.map +1 -0
- package/dist/utils/fuzz.js +11 -0
- package/dist/utils/fuzz.js.map +1 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -0
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/rpc.d.ts.map +1 -1
- package/dist/utils/rpc.js +2 -1
- package/dist/utils/rpc.js.map +1 -1
- package/package.json +2 -1
- package/src/classes/manager.ts +122 -4
- package/src/classes/market.ts +1 -1
- package/src/classes/vault.ts +123 -49
- package/src/client_kamino_manager.ts +18 -0
- package/src/idl_codegen_kamino_vault/accounts/Reserve.ts +132 -105
- package/src/idl_codegen_kamino_vault/accounts/VaultState.ts +217 -124
- package/src/idl_codegen_kamino_vault/accounts/index.ts +4 -4
- package/src/idl_codegen_kamino_vault/errors/anchor.ts +335 -326
- package/src/idl_codegen_kamino_vault/errors/custom.ts +353 -157
- package/src/idl_codegen_kamino_vault/errors/index.ts +35 -22
- package/src/idl_codegen_kamino_vault/instructions/deposit.ts +33 -25
- package/src/idl_codegen_kamino_vault/instructions/giveUpPendingFees.ts +40 -0
- package/src/idl_codegen_kamino_vault/instructions/index.ts +27 -10
- package/src/idl_codegen_kamino_vault/instructions/initVault.ts +25 -20
- package/src/idl_codegen_kamino_vault/instructions/invest.ts +40 -25
- package/src/idl_codegen_kamino_vault/instructions/updateAdmin.ts +24 -0
- package/src/idl_codegen_kamino_vault/instructions/updateReserveAllocation.ts +24 -24
- package/src/idl_codegen_kamino_vault/instructions/updateVaultConfig.ts +49 -0
- package/src/idl_codegen_kamino_vault/instructions/withdraw.ts +45 -33
- package/src/idl_codegen_kamino_vault/instructions/withdrawPendingFees.ts +75 -0
- package/src/idl_codegen_kamino_vault/programId.ts +5 -3
- package/src/idl_codegen_kamino_vault/types/BigFractionBytes.ts +24 -18
- package/src/idl_codegen_kamino_vault/types/BorrowRateCurve.ts +21 -16
- package/src/idl_codegen_kamino_vault/types/CurvePoint.ts +21 -18
- package/src/idl_codegen_kamino_vault/types/LastUpdate.ts +32 -27
- package/src/idl_codegen_kamino_vault/types/PriceHeuristic.ts +25 -22
- package/src/idl_codegen_kamino_vault/types/PythConfiguration.ts +14 -14
- package/src/idl_codegen_kamino_vault/types/ReserveCollateral.ts +35 -35
- package/src/idl_codegen_kamino_vault/types/ReserveConfig.ts +232 -191
- package/src/idl_codegen_kamino_vault/types/ReserveFees.ts +27 -23
- package/src/idl_codegen_kamino_vault/types/ReserveLiquidity.ts +116 -98
- package/src/idl_codegen_kamino_vault/types/ScopeConfiguration.ts +25 -25
- package/src/idl_codegen_kamino_vault/types/SwitchboardConfiguration.ts +21 -18
- package/src/idl_codegen_kamino_vault/types/TokenInfo.ts +92 -74
- package/src/idl_codegen_kamino_vault/types/VaultAllocation.ts +67 -49
- package/src/idl_codegen_kamino_vault/types/VaultConfigField.ts +270 -0
- package/src/idl_codegen_kamino_vault/types/index.ts +77 -30
- package/src/lending_operations/repay_with_collateral_operations.ts +2 -0
- package/src/leverage/calcs.ts +315 -8
- package/src/leverage/index.ts +1 -0
- package/src/leverage/operations.ts +1079 -1331
- package/src/leverage/types.ts +211 -0
- package/src/leverage/utils.ts +103 -64
- package/src/utils/constants.ts +2 -0
- package/src/utils/fuzz.ts +5 -0
- package/src/utils/index.ts +1 -0
- package/src/utils/rpc.ts +2 -1
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
AddressLookupTableAccount,
|
|
3
|
-
Connection,
|
|
4
|
-
LAMPORTS_PER_SOL,
|
|
5
|
-
PublicKey,
|
|
6
|
-
TransactionInstruction,
|
|
7
|
-
} from '@solana/web3.js';
|
|
1
|
+
import { LAMPORTS_PER_SOL, PublicKey, TransactionInstruction } from '@solana/web3.js';
|
|
8
2
|
import Decimal from 'decimal.js';
|
|
9
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
KaminoAction,
|
|
5
|
+
KaminoMarket,
|
|
6
|
+
KaminoObligation,
|
|
7
|
+
KaminoReserve,
|
|
8
|
+
lamportsToNumberDecimal as fromLamports,
|
|
9
|
+
} from '../classes';
|
|
10
10
|
import { getFlashLoanInstructions } from './instructions';
|
|
11
11
|
|
|
12
12
|
import { numberToLamportsDecimal as toLamports } from '../classes';
|
|
@@ -15,373 +15,248 @@ import {
|
|
|
15
15
|
MultiplyObligation,
|
|
16
16
|
ObligationType,
|
|
17
17
|
ObligationTypeTag,
|
|
18
|
-
|
|
18
|
+
SOL_DECIMALS,
|
|
19
19
|
U64_MAX,
|
|
20
20
|
WRAPPED_SOL_MINT,
|
|
21
|
+
createAtasIdempotent,
|
|
21
22
|
getAssociatedTokenAddress,
|
|
22
|
-
getAtasWithCreateIxnsIfMissing,
|
|
23
23
|
getComputeBudgetAndPriorityFeeIxns,
|
|
24
24
|
getDepositWsolIxns,
|
|
25
|
+
getLookupTableAccount,
|
|
25
26
|
removeBudgetAndAtaIxns,
|
|
26
27
|
uniqueAccounts,
|
|
27
28
|
} from '../utils';
|
|
28
|
-
import { calcAdjustAmounts, calcWithdrawAmounts, simulateMintKToken, toJson } from './calcs';
|
|
29
|
-
import { TOKEN_PROGRAM_ID, createCloseAccountInstruction } from '@solana/spl-token';
|
|
30
29
|
import {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
30
|
+
adjustDepositLeverageCalcs,
|
|
31
|
+
adjustWithdrawLeverageCalcs,
|
|
32
|
+
calcAdjustAmounts,
|
|
33
|
+
depositLeverageCalcs,
|
|
34
|
+
depositLeverageKtokenCalcs,
|
|
35
|
+
toJson,
|
|
36
|
+
withdrawLeverageCalcs,
|
|
37
|
+
} from './calcs';
|
|
38
|
+
import { TOKEN_PROGRAM_ID, createCloseAccountInstruction, getAssociatedTokenAddressSync } from '@solana/spl-token';
|
|
39
|
+
import { Kamino, StrategyWithAddress } from '@kamino-finance/kliquidity-sdk';
|
|
36
40
|
import { getExpectedTokenBalanceAfterBorrow, getKtokenToTokenSwapper, getTokenToKtokenSwapper } from './utils';
|
|
41
|
+
import { FullBPS } from '@kamino-finance/kliquidity-sdk/dist/utils/CreationParameters';
|
|
42
|
+
import {
|
|
43
|
+
AdjustLeverageCalcsResult,
|
|
44
|
+
AdjustLeverageInitialInputs,
|
|
45
|
+
AdjustLeverageIxsResponse,
|
|
46
|
+
AdjustLeverageProps,
|
|
47
|
+
AdjustLeverageSwapInputsProps,
|
|
48
|
+
DepositLeverageCalcsResult,
|
|
49
|
+
DepositLeverageInitialInputs,
|
|
50
|
+
DepositWithLeverageProps,
|
|
51
|
+
DepositWithLeverageSwapInputsProps,
|
|
52
|
+
DepsoitLeverageIxsResponse,
|
|
53
|
+
PriceAinBProvider,
|
|
54
|
+
SwapInputs,
|
|
55
|
+
SwapQuoteIxs,
|
|
56
|
+
SwapQuoteIxsProvider,
|
|
57
|
+
WithdrawLeverageCalcsResult,
|
|
58
|
+
WithdrawLeverageInitialInputs,
|
|
59
|
+
WithdrawLeverageIxsResponse,
|
|
60
|
+
WithdrawWithLeverageProps,
|
|
61
|
+
WithdrawWithLeverageSwapInputsProps,
|
|
62
|
+
} from './types';
|
|
63
|
+
|
|
64
|
+
export async function getDepositWithLeverageSwapInputs<QuoteResponse>({
|
|
65
|
+
owner,
|
|
66
|
+
kaminoMarket,
|
|
67
|
+
debtTokenMint,
|
|
68
|
+
collTokenMint,
|
|
69
|
+
depositAmount,
|
|
70
|
+
priceDebtToColl,
|
|
71
|
+
slippagePct,
|
|
72
|
+
obligation,
|
|
73
|
+
referrer,
|
|
74
|
+
currentSlot,
|
|
75
|
+
targetLeverage,
|
|
76
|
+
selectedTokenMint,
|
|
77
|
+
kamino,
|
|
78
|
+
obligationTypeTagOverride,
|
|
79
|
+
scopeFeed,
|
|
80
|
+
budgetAndPriorityFeeIxs,
|
|
81
|
+
quoteBufferBps,
|
|
82
|
+
priceAinB,
|
|
83
|
+
isKtoken,
|
|
84
|
+
quoter,
|
|
85
|
+
}: DepositWithLeverageSwapInputsProps<QuoteResponse>): Promise<{
|
|
86
|
+
swapInputs: SwapInputs;
|
|
87
|
+
initialInputs: DepositLeverageInitialInputs<QuoteResponse>;
|
|
88
|
+
}> {
|
|
89
|
+
const collReserve = kaminoMarket.getReserveByMint(collTokenMint);
|
|
90
|
+
const debtReserve = kaminoMarket.getReserveByMint(debtTokenMint);
|
|
91
|
+
const solTokenReserve = kaminoMarket.getReserveByMint(WRAPPED_SOL_MINT);
|
|
92
|
+
const flashLoanFee = collReserve!.getFlashLoanFee() || new Decimal(0);
|
|
37
93
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
export type SwapQuoteProvider<QuoteResponse> = (
|
|
47
|
-
inputs: SwapInputs,
|
|
48
|
-
klendAccounts: Array<PublicKey>
|
|
49
|
-
) => Promise<SwapQuote<QuoteResponse>>;
|
|
50
|
-
|
|
51
|
-
export type SwapQuoteIxsProvider<QuoteResponse> = (
|
|
52
|
-
inputs: SwapInputs,
|
|
53
|
-
klendAccounts: Array<PublicKey>,
|
|
54
|
-
quote: SwapQuote<QuoteResponse>
|
|
55
|
-
) => Promise<SwapQuoteIxs>;
|
|
56
|
-
|
|
57
|
-
export type SwapQuote<QuoteResponse> = {
|
|
58
|
-
priceAInB: Decimal;
|
|
59
|
-
quoteResponse?: QuoteResponse;
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
export type SwapQuoteIxs = {
|
|
63
|
-
preActionIxs: TransactionInstruction[];
|
|
64
|
-
swapIxs: TransactionInstruction[];
|
|
65
|
-
lookupTables: AddressLookupTableAccount[];
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
export type PriceAinBProvider = (mintA: PublicKey, mintB: PublicKey) => Promise<Decimal>;
|
|
69
|
-
|
|
70
|
-
export type IsKtokenProvider = (token: PublicKey | string) => Promise<boolean>;
|
|
71
|
-
|
|
72
|
-
export type SwapInputs = {
|
|
73
|
-
inputAmountLamports: Decimal;
|
|
74
|
-
inputMint: PublicKey;
|
|
75
|
-
outputMint: PublicKey;
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
export type KaminoDepositSwapOverride = (
|
|
79
|
-
kaminoMarket: KaminoMarket,
|
|
80
|
-
kamino: Kamino,
|
|
81
|
-
depositor: PublicKey,
|
|
82
|
-
amountInMint: PublicKey,
|
|
83
|
-
amountOutMint: PublicKey,
|
|
84
|
-
amountIn: Decimal,
|
|
85
|
-
slippageFactor: Decimal,
|
|
86
|
-
amountDebtAtaBalance: Decimal
|
|
87
|
-
) => Promise<InstructionsWithLookupTables>;
|
|
88
|
-
|
|
89
|
-
export const depositLeverageCalcs = (props: {
|
|
90
|
-
depositAmount: Decimal;
|
|
91
|
-
depositTokenIsCollToken: boolean;
|
|
92
|
-
depositTokenIsSol: boolean;
|
|
93
|
-
priceDebtToColl: Decimal;
|
|
94
|
-
targetLeverage: Decimal;
|
|
95
|
-
slippagePct: Decimal;
|
|
96
|
-
flashLoanFee: Decimal;
|
|
97
|
-
}): {
|
|
98
|
-
flashBorrowInCollToken: Decimal;
|
|
99
|
-
initDepositInSol: Decimal;
|
|
100
|
-
debtTokenToBorrow: Decimal;
|
|
101
|
-
collTokenToDeposit: Decimal;
|
|
102
|
-
swapDebtTokenIn: Decimal;
|
|
103
|
-
swapCollTokenExpectedOut: Decimal;
|
|
104
|
-
} => {
|
|
105
|
-
// Initialize local variables from the props object
|
|
106
|
-
const {
|
|
94
|
+
const selectedTokenIsCollToken = selectedTokenMint.equals(collTokenMint);
|
|
95
|
+
const depositTokenIsSol = !solTokenReserve ? false : selectedTokenMint.equals(solTokenReserve!.getLiquidityMint());
|
|
96
|
+
|
|
97
|
+
const collIsKtoken = await isKtoken(collTokenMint);
|
|
98
|
+
const strategy = collIsKtoken ? (await kamino!.getStrategyByKTokenMint(collTokenMint))! : undefined;
|
|
99
|
+
|
|
100
|
+
const calcs = await getDepositWithLeverageCalcs(
|
|
107
101
|
depositAmount,
|
|
108
|
-
|
|
102
|
+
selectedTokenIsCollToken,
|
|
103
|
+
collIsKtoken,
|
|
109
104
|
depositTokenIsSol,
|
|
110
105
|
priceDebtToColl,
|
|
111
106
|
targetLeverage,
|
|
112
107
|
slippagePct,
|
|
113
108
|
flashLoanFee,
|
|
114
|
-
} = props;
|
|
115
|
-
const slippage = slippagePct.div('100');
|
|
116
|
-
|
|
117
|
-
const initDepositInSol = depositTokenIsSol ? depositAmount : new Decimal(0);
|
|
118
|
-
|
|
119
|
-
// Core logic
|
|
120
|
-
if (depositTokenIsCollToken) {
|
|
121
|
-
const y = targetLeverage.mul(priceDebtToColl);
|
|
122
|
-
const x = flashLoanFee.add('1').mul(slippage.add('1')).div(priceDebtToColl);
|
|
123
|
-
const finalColl = depositAmount.mul(x).div(x.sub(targetLeverage.sub('1').div(y)));
|
|
124
|
-
const debt = finalColl.sub(depositAmount).mul(x);
|
|
125
|
-
const flashBorrowColl = finalColl.sub(depositAmount).mul(flashLoanFee.add('1'));
|
|
126
|
-
|
|
127
|
-
return {
|
|
128
|
-
flashBorrowInCollToken: flashBorrowColl,
|
|
129
|
-
initDepositInSol,
|
|
130
|
-
debtTokenToBorrow: debt,
|
|
131
|
-
collTokenToDeposit: finalColl,
|
|
132
|
-
swapDebtTokenIn: debt,
|
|
133
|
-
swapCollTokenExpectedOut: finalColl.sub(depositAmount),
|
|
134
|
-
};
|
|
135
|
-
} else {
|
|
136
|
-
const y = targetLeverage.mul(priceDebtToColl);
|
|
137
|
-
const x = flashLoanFee.add('1').mul(slippage.add('1')).div(priceDebtToColl);
|
|
138
|
-
const finalColl = depositAmount.div(x.sub(targetLeverage.sub('1').div(y)));
|
|
139
|
-
const flashBorrowColl = finalColl.mul(flashLoanFee.add('1'));
|
|
140
|
-
const debt = targetLeverage.sub('1').mul(finalColl).div(y);
|
|
141
|
-
|
|
142
|
-
return {
|
|
143
|
-
flashBorrowInCollToken: flashBorrowColl,
|
|
144
|
-
initDepositInSol,
|
|
145
|
-
debtTokenToBorrow: debt,
|
|
146
|
-
collTokenToDeposit: finalColl,
|
|
147
|
-
swapDebtTokenIn: debt.add(depositAmount),
|
|
148
|
-
swapCollTokenExpectedOut: finalColl,
|
|
149
|
-
};
|
|
150
|
-
}
|
|
151
|
-
};
|
|
152
|
-
|
|
153
|
-
export const depositLeverageKtokenCalcs = async (props: {
|
|
154
|
-
kamino: Kamino;
|
|
155
|
-
strategy: StrategyWithAddress;
|
|
156
|
-
debtTokenMint: PublicKey;
|
|
157
|
-
depositAmount: Decimal;
|
|
158
|
-
depositTokenIsCollToken: boolean;
|
|
159
|
-
depositTokenIsSol: boolean;
|
|
160
|
-
priceDebtToColl: Decimal;
|
|
161
|
-
targetLeverage: Decimal;
|
|
162
|
-
slippagePct: Decimal;
|
|
163
|
-
flashLoanFee: Decimal;
|
|
164
|
-
priceAinB: PriceAinBProvider;
|
|
165
|
-
strategyHoldings?: TokenAmounts;
|
|
166
|
-
}): Promise<{
|
|
167
|
-
flashBorrowInDebtToken: Decimal;
|
|
168
|
-
initDepositInSol: Decimal;
|
|
169
|
-
collTokenToDeposit: Decimal;
|
|
170
|
-
debtTokenToBorrow: Decimal; // debtTokenToBorrow = flashBorrowInDebtToken + flashLoanFee
|
|
171
|
-
requiredCollateral: Decimal;
|
|
172
|
-
singleSidedDeposit: Decimal;
|
|
173
|
-
}> => {
|
|
174
|
-
const {
|
|
175
109
|
kamino,
|
|
176
110
|
strategy,
|
|
177
111
|
debtTokenMint,
|
|
178
|
-
depositAmount,
|
|
179
|
-
depositTokenIsCollToken,
|
|
180
|
-
depositTokenIsSol,
|
|
181
|
-
priceDebtToColl,
|
|
182
|
-
targetLeverage,
|
|
183
|
-
slippagePct,
|
|
184
|
-
flashLoanFee,
|
|
185
112
|
priceAinB,
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
const initDepositInSol = depositTokenIsSol ? depositAmount : new Decimal(0);
|
|
189
|
-
const slippage = slippagePct.div('100');
|
|
190
|
-
|
|
191
|
-
let flashBorrowInDebtToken: Decimal;
|
|
192
|
-
let collTokenToDeposit: Decimal;
|
|
193
|
-
let debtTokenToBorrow: Decimal;
|
|
194
|
-
|
|
195
|
-
if (depositTokenIsCollToken) {
|
|
196
|
-
const x = slippage.add('1').div(priceDebtToColl);
|
|
197
|
-
const y = flashLoanFee.add('1').mul(priceDebtToColl);
|
|
198
|
-
const z = targetLeverage.mul(y).div(targetLeverage.sub(1));
|
|
199
|
-
flashBorrowInDebtToken = depositAmount.div(z.minus(new Decimal(1).div(x)));
|
|
200
|
-
collTokenToDeposit = depositAmount.add(flashBorrowInDebtToken.div(x));
|
|
201
|
-
debtTokenToBorrow = flashBorrowInDebtToken.mul(new Decimal(1).add(flashLoanFee));
|
|
113
|
+
debtReserve!
|
|
114
|
+
);
|
|
202
115
|
|
|
203
|
-
|
|
204
|
-
flashBorrowInDebtToken,
|
|
205
|
-
initDepositInSol,
|
|
206
|
-
collTokenToDeposit,
|
|
207
|
-
debtTokenToBorrow,
|
|
208
|
-
requiredCollateral: collTokenToDeposit.sub(depositAmount), // Assuming netValue is requiredCollateral, adjust as needed
|
|
209
|
-
singleSidedDeposit: flashBorrowInDebtToken,
|
|
210
|
-
};
|
|
211
|
-
} else {
|
|
212
|
-
const y = targetLeverage.mul(priceDebtToColl);
|
|
213
|
-
// although we will only swap ~half of the debt token, we account for the slippage on the entire amount as we are working backwards from the minimum collateral and do not know the exact swap proportion in advance
|
|
214
|
-
// This also allows for some variation in the pool ratios between calculation + submitting the tx
|
|
215
|
-
const x = flashLoanFee.add('1').mul(slippage.add('1')).div(priceDebtToColl);
|
|
216
|
-
// Calculate the amount of collateral tokens we will deposit in order to achieve the desired leverage after swapping a portion of the debt token and flash loan fees
|
|
217
|
-
const finalColl = depositAmount.div(x.sub(targetLeverage.sub('1').div(y)));
|
|
218
|
-
// Calculate how many A and B tokens we will need to actually mint the desired amount of ktoken collateral
|
|
219
|
-
// The actual amount of ktokens received may be less than the finalColl due to smart proportional contract logic
|
|
220
|
-
// So we use the actualColl as the amount we will deposit
|
|
221
|
-
const [estimatedA, estimatedB, actualColl] = await simulateMintKToken(
|
|
222
|
-
kamino!,
|
|
223
|
-
strategy!,
|
|
224
|
-
finalColl,
|
|
225
|
-
strategyHoldings
|
|
226
|
-
);
|
|
227
|
-
const pxAinB = await priceAinB(strategy!.strategy.tokenAMint, strategy!.strategy.tokenBMint);
|
|
228
|
-
const isTokenADeposit = strategy.strategy.tokenAMint.equals(debtTokenMint);
|
|
229
|
-
// Calculate the amount we need to flash borrow by combining value of A and B into the debt token
|
|
230
|
-
const singleSidedDepositAmount = isTokenADeposit
|
|
231
|
-
? estimatedA.add(estimatedB.div(pxAinB))
|
|
232
|
-
: estimatedB.add(estimatedA.mul(pxAinB));
|
|
233
|
-
|
|
234
|
-
// Add slippage to the entire amount, add flash loan fee to part we will flash borrow
|
|
235
|
-
flashBorrowInDebtToken = singleSidedDepositAmount
|
|
236
|
-
.div(new Decimal('1').sub(slippage))
|
|
237
|
-
.sub(depositAmount)
|
|
238
|
-
.div(new Decimal('1').sub(flashLoanFee));
|
|
239
|
-
// Deposit the min ktoken amount we calculated at the beginning
|
|
240
|
-
// Any slippage will be left in the user's wallet as ktokens
|
|
241
|
-
collTokenToDeposit = actualColl;
|
|
242
|
-
debtTokenToBorrow = flashBorrowInDebtToken.div(new Decimal('1').sub(flashLoanFee));
|
|
243
|
-
// Add slippage to ensure we try to swap/deposit as much as possible after flash loan fees
|
|
244
|
-
const singleSidedDeposit = singleSidedDepositAmount.div(new Decimal('1').sub(slippage));
|
|
116
|
+
console.log('Ops Calcs', toJson(calcs));
|
|
245
117
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
118
|
+
let obligationType: ObligationType;
|
|
119
|
+
if (obligationTypeTagOverride === ObligationTypeTag.Multiply) {
|
|
120
|
+
// multiply
|
|
121
|
+
obligationType = new MultiplyObligation(collTokenMint, debtTokenMint, kaminoMarket.programId);
|
|
122
|
+
} else if (obligationTypeTagOverride === ObligationTypeTag.Leverage) {
|
|
123
|
+
// leverage
|
|
124
|
+
obligationType = new LeverageObligation(collTokenMint, debtTokenMint, kaminoMarket.programId);
|
|
125
|
+
} else {
|
|
126
|
+
throw Error('Obligation type tag not supported for leverage, please use 1 - multiply or 3 - leverage');
|
|
254
127
|
}
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
depositAmount: Decimal;
|
|
259
|
-
priceDebtToColl: Decimal;
|
|
260
|
-
slippagePct: Decimal;
|
|
261
|
-
targetLeverage: Decimal;
|
|
262
|
-
kaminoMarket: KaminoMarket;
|
|
263
|
-
selectedTokenMint: PublicKey;
|
|
264
|
-
debtTokenMint: PublicKey;
|
|
265
|
-
collTokenMint: PublicKey;
|
|
266
|
-
}): {
|
|
267
|
-
swapInputs: SwapInputs;
|
|
268
|
-
} => {
|
|
269
|
-
const {
|
|
270
|
-
depositAmount,
|
|
271
|
-
priceDebtToColl,
|
|
272
|
-
slippagePct,
|
|
273
|
-
targetLeverage,
|
|
128
|
+
|
|
129
|
+
// Build the repay & withdraw collateral tx to get the number of accounts
|
|
130
|
+
const klendIxs = await buildDepositWithLeverageIxns(
|
|
274
131
|
kaminoMarket,
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
132
|
+
debtReserve!,
|
|
133
|
+
collReserve!,
|
|
134
|
+
owner,
|
|
135
|
+
obligation ? obligation : obligationType,
|
|
136
|
+
referrer,
|
|
137
|
+
currentSlot,
|
|
138
|
+
depositTokenIsSol,
|
|
139
|
+
scopeFeed,
|
|
140
|
+
calcs,
|
|
141
|
+
budgetAndPriorityFeeIxs,
|
|
142
|
+
{
|
|
143
|
+
preActionIxs: [],
|
|
144
|
+
swapIxs: [],
|
|
145
|
+
lookupTables: [],
|
|
146
|
+
},
|
|
147
|
+
strategy,
|
|
148
|
+
collIsKtoken
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
const uniqueKlendAccounts = uniqueAccounts(klendIxs);
|
|
152
|
+
|
|
153
|
+
const swapInputAmount = toLamports(
|
|
154
|
+
!collIsKtoken ? calcs.swapDebtTokenIn : calcs.singleSidedDepositKtokenOnly,
|
|
155
|
+
debtReserve!.stats.decimals
|
|
156
|
+
).ceil();
|
|
157
|
+
|
|
158
|
+
const swapInputsForQuote: SwapInputs = {
|
|
159
|
+
inputAmountLamports: swapInputAmount.mul(new Decimal(1).add(quoteBufferBps.div(FullBPS))),
|
|
160
|
+
inputMint: debtTokenMint,
|
|
161
|
+
outputMint: collTokenMint,
|
|
162
|
+
amountDebtAtaBalance: new Decimal(0), // Only needed for ktokens swaps
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
const swapQuote = await quoter(swapInputsForQuote, uniqueKlendAccounts);
|
|
284
166
|
|
|
285
|
-
const
|
|
167
|
+
const quotePriceCalcs = await getDepositWithLeverageCalcs(
|
|
286
168
|
depositAmount,
|
|
287
|
-
|
|
169
|
+
selectedTokenIsCollToken,
|
|
170
|
+
collIsKtoken,
|
|
288
171
|
depositTokenIsSol,
|
|
289
|
-
|
|
172
|
+
swapQuote.priceAInB,
|
|
290
173
|
targetLeverage,
|
|
291
174
|
slippagePct,
|
|
292
175
|
flashLoanFee,
|
|
293
|
-
|
|
176
|
+
kamino,
|
|
177
|
+
strategy,
|
|
178
|
+
debtTokenMint,
|
|
179
|
+
priceAinB,
|
|
180
|
+
debtReserve!
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
const swapInputAmountQuotePrice = toLamports(
|
|
184
|
+
!collIsKtoken ? quotePriceCalcs.swapDebtTokenIn : quotePriceCalcs.singleSidedDepositKtokenOnly,
|
|
185
|
+
debtReserve!.stats.decimals
|
|
186
|
+
).ceil();
|
|
187
|
+
|
|
188
|
+
let expectedDebtTokenAtaBalance = new Decimal(0);
|
|
189
|
+
|
|
190
|
+
if (collIsKtoken) {
|
|
191
|
+
let futureBalanceInAta = new Decimal(0);
|
|
192
|
+
if (debtTokenMint.equals(WRAPPED_SOL_MINT)) {
|
|
193
|
+
futureBalanceInAta = futureBalanceInAta.add(
|
|
194
|
+
!collIsKtoken ? quotePriceCalcs.initDepositInSol : quotePriceCalcs.initDepositInSol
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
futureBalanceInAta = futureBalanceInAta.add(
|
|
198
|
+
!collIsKtoken ? quotePriceCalcs.debtTokenToBorrow : quotePriceCalcs.flashBorrowInDebtTokenKtokenOnly
|
|
199
|
+
);
|
|
200
|
+
expectedDebtTokenAtaBalance = await getExpectedTokenBalanceAfterBorrow(
|
|
201
|
+
kaminoMarket.getConnection(),
|
|
202
|
+
debtTokenMint,
|
|
203
|
+
owner,
|
|
204
|
+
toLamports(futureBalanceInAta.toDecimalPlaces(debtReserve!.stats.decimals), debtReserve!.stats.decimals),
|
|
205
|
+
debtReserve!.state.liquidity.mintDecimals.toNumber()
|
|
206
|
+
);
|
|
207
|
+
}
|
|
294
208
|
|
|
295
209
|
return {
|
|
296
210
|
swapInputs: {
|
|
297
|
-
inputAmountLamports:
|
|
298
|
-
calcs.swapDebtTokenIn,
|
|
299
|
-
debtReserve!.state.liquidity.mintDecimals.toNumber()
|
|
300
|
-
).ceil(),
|
|
211
|
+
inputAmountLamports: swapInputAmountQuotePrice,
|
|
301
212
|
inputMint: debtTokenMint,
|
|
302
213
|
outputMint: collTokenMint,
|
|
214
|
+
amountDebtAtaBalance: expectedDebtTokenAtaBalance,
|
|
215
|
+
},
|
|
216
|
+
initialInputs: {
|
|
217
|
+
calcs: quotePriceCalcs,
|
|
218
|
+
swapQuote,
|
|
219
|
+
currentSlot,
|
|
220
|
+
collIsKtoken,
|
|
221
|
+
strategy,
|
|
222
|
+
obligation: obligation ? obligation : obligationType,
|
|
223
|
+
klendAccounts: uniqueKlendAccounts,
|
|
303
224
|
},
|
|
304
225
|
};
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
const {
|
|
336
|
-
connection,
|
|
337
|
-
budgetAndPriorityFeeIxns,
|
|
338
|
-
user,
|
|
339
|
-
amount,
|
|
340
|
-
selectedTokenMint,
|
|
341
|
-
collTokenMint,
|
|
342
|
-
debtTokenMint,
|
|
343
|
-
targetLeverage,
|
|
344
|
-
kaminoMarket,
|
|
345
|
-
slippagePct,
|
|
346
|
-
priceDebtToColl,
|
|
347
|
-
swapper,
|
|
348
|
-
referrer,
|
|
349
|
-
isKtoken,
|
|
350
|
-
priceAinB,
|
|
351
|
-
kamino,
|
|
352
|
-
obligationTypeTagOverride = 1,
|
|
353
|
-
obligation,
|
|
354
|
-
currentSlot,
|
|
355
|
-
getTotalKlendAccountsOnly,
|
|
356
|
-
scopeFeed,
|
|
357
|
-
} = props;
|
|
358
|
-
const collReserve = kaminoMarket.getReserveByMint(collTokenMint);
|
|
359
|
-
const debtReserve = kaminoMarket.getReserveByMint(debtTokenMint);
|
|
360
|
-
const solTokenReserve = kaminoMarket.getReserveByMint(WRAPPED_SOL_MINT);
|
|
361
|
-
const flashLoanFee = collReserve!.getFlashLoanFee() || new Decimal(0);
|
|
362
|
-
|
|
363
|
-
const selectedTokenIsCollToken = selectedTokenMint.equals(collTokenMint);
|
|
364
|
-
const depositTokenIsSol = !solTokenReserve ? false : selectedTokenMint.equals(solTokenReserve!.getLiquidityMint());
|
|
365
|
-
|
|
366
|
-
const collIsKtoken = await isKtoken(collTokenMint);
|
|
367
|
-
const strategy = collIsKtoken ? (await kamino!.getStrategyByKTokenMint(collTokenMint))! : undefined;
|
|
368
|
-
|
|
369
|
-
const calcs = depositLeverageCalcs({
|
|
370
|
-
depositAmount: amount,
|
|
371
|
-
depositTokenIsCollToken: selectedTokenIsCollToken,
|
|
372
|
-
depositTokenIsSol,
|
|
373
|
-
priceDebtToColl,
|
|
374
|
-
targetLeverage,
|
|
375
|
-
slippagePct,
|
|
376
|
-
flashLoanFee,
|
|
377
|
-
});
|
|
378
|
-
let calcsKtoken;
|
|
379
|
-
if (collIsKtoken) {
|
|
380
|
-
calcsKtoken = await depositLeverageKtokenCalcs({
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
async function getDepositWithLeverageCalcs(
|
|
229
|
+
depositAmount: Decimal,
|
|
230
|
+
selectedTokenIsCollToken: boolean,
|
|
231
|
+
collIsKtoken: boolean,
|
|
232
|
+
depositTokenIsSol: boolean,
|
|
233
|
+
priceDebtToColl: Decimal,
|
|
234
|
+
targetLeverage: Decimal,
|
|
235
|
+
slippagePct: Decimal,
|
|
236
|
+
flashLoanFee: Decimal,
|
|
237
|
+
kamino: Kamino | undefined,
|
|
238
|
+
strategy: StrategyWithAddress | undefined,
|
|
239
|
+
debtTokenMint: PublicKey,
|
|
240
|
+
priceAinB: PriceAinBProvider,
|
|
241
|
+
debtReserve: KaminoReserve
|
|
242
|
+
): Promise<DepositLeverageCalcsResult> {
|
|
243
|
+
let calcs: DepositLeverageCalcsResult;
|
|
244
|
+
if (!collIsKtoken) {
|
|
245
|
+
calcs = depositLeverageCalcs({
|
|
246
|
+
depositAmount: depositAmount,
|
|
247
|
+
depositTokenIsCollToken: selectedTokenIsCollToken,
|
|
248
|
+
depositTokenIsSol,
|
|
249
|
+
priceDebtToColl,
|
|
250
|
+
targetLeverage,
|
|
251
|
+
slippagePct,
|
|
252
|
+
flashLoanFee,
|
|
253
|
+
});
|
|
254
|
+
} else {
|
|
255
|
+
calcs = await depositLeverageKtokenCalcs({
|
|
381
256
|
kamino: kamino!,
|
|
382
257
|
strategy: strategy!,
|
|
383
258
|
debtTokenMint,
|
|
384
|
-
depositAmount:
|
|
259
|
+
depositAmount: depositAmount,
|
|
385
260
|
depositTokenIsCollToken: selectedTokenIsCollToken,
|
|
386
261
|
depositTokenIsSol,
|
|
387
262
|
priceDebtToColl,
|
|
@@ -391,37 +266,160 @@ export const getDepositWithLeverageIxns = async (props: {
|
|
|
391
266
|
priceAinB,
|
|
392
267
|
});
|
|
393
268
|
// Rounding to exact number of decimals so this value is passed through in all calcs without rounding inconsistencies
|
|
394
|
-
|
|
395
|
-
debtReserve
|
|
269
|
+
calcs.flashBorrowInDebtTokenKtokenOnly = calcs.flashBorrowInDebtTokenKtokenOnly.toDecimalPlaces(
|
|
270
|
+
debtReserve!.state.liquidity.mintDecimals.toNumber()!,
|
|
396
271
|
Decimal.ROUND_CEIL
|
|
397
272
|
);
|
|
398
|
-
|
|
399
|
-
debtReserve
|
|
273
|
+
calcs.debtTokenToBorrow = calcs.debtTokenToBorrow.toDecimalPlaces(
|
|
274
|
+
debtReserve!.state.liquidity.mintDecimals.toNumber()!,
|
|
400
275
|
Decimal.ROUND_CEIL
|
|
401
276
|
);
|
|
402
|
-
|
|
403
|
-
debtReserve
|
|
277
|
+
calcs.singleSidedDepositKtokenOnly = calcs.singleSidedDepositKtokenOnly.toDecimalPlaces(
|
|
278
|
+
debtReserve!.state.liquidity.mintDecimals.toNumber()!,
|
|
404
279
|
Decimal.ROUND_CEIL
|
|
405
280
|
);
|
|
406
281
|
}
|
|
282
|
+
return calcs;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
export async function getDepositWithLeverageIxns<QuoteResponse>({
|
|
286
|
+
owner,
|
|
287
|
+
kaminoMarket,
|
|
288
|
+
debtTokenMint,
|
|
289
|
+
collTokenMint,
|
|
290
|
+
depositAmount,
|
|
291
|
+
priceDebtToColl,
|
|
292
|
+
slippagePct,
|
|
293
|
+
obligation,
|
|
294
|
+
referrer,
|
|
295
|
+
currentSlot,
|
|
296
|
+
targetLeverage,
|
|
297
|
+
selectedTokenMint,
|
|
298
|
+
kamino,
|
|
299
|
+
obligationTypeTagOverride,
|
|
300
|
+
scopeFeed,
|
|
301
|
+
budgetAndPriorityFeeIxs,
|
|
302
|
+
quoteBufferBps,
|
|
303
|
+
priceAinB,
|
|
304
|
+
isKtoken,
|
|
305
|
+
quoter,
|
|
306
|
+
swapper,
|
|
307
|
+
}: DepositWithLeverageProps<QuoteResponse>): Promise<DepsoitLeverageIxsResponse<QuoteResponse>> {
|
|
308
|
+
const { swapInputs, initialInputs } = await getDepositWithLeverageSwapInputs({
|
|
309
|
+
owner,
|
|
310
|
+
kaminoMarket,
|
|
311
|
+
debtTokenMint,
|
|
312
|
+
collTokenMint,
|
|
313
|
+
depositAmount,
|
|
314
|
+
priceDebtToColl,
|
|
315
|
+
slippagePct,
|
|
316
|
+
obligation,
|
|
317
|
+
referrer,
|
|
318
|
+
currentSlot,
|
|
319
|
+
targetLeverage,
|
|
320
|
+
selectedTokenMint,
|
|
321
|
+
kamino,
|
|
322
|
+
obligationTypeTagOverride,
|
|
323
|
+
scopeFeed,
|
|
324
|
+
budgetAndPriorityFeeIxs,
|
|
325
|
+
quoteBufferBps,
|
|
326
|
+
priceAinB,
|
|
327
|
+
isKtoken,
|
|
328
|
+
quoter,
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
let depositSwapper: SwapQuoteIxsProvider<QuoteResponse>;
|
|
332
|
+
|
|
333
|
+
if (!initialInputs.collIsKtoken) {
|
|
334
|
+
depositSwapper = swapper;
|
|
335
|
+
} else {
|
|
336
|
+
if (kamino === undefined) {
|
|
337
|
+
throw Error('Ktoken use as collateral for leverage without Kamino instance');
|
|
338
|
+
}
|
|
339
|
+
depositSwapper = await getTokenToKtokenSwapper(kaminoMarket, kamino, owner, slippagePct, swapper, priceAinB, false);
|
|
340
|
+
}
|
|
407
341
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
depositTokenIsSol,
|
|
413
|
-
selectedTokenIsCollToken,
|
|
414
|
-
initDepositInSol: calcs.initDepositInSol,
|
|
415
|
-
})
|
|
342
|
+
const { swapIxs, lookupTables } = await depositSwapper(
|
|
343
|
+
swapInputs,
|
|
344
|
+
initialInputs.klendAccounts,
|
|
345
|
+
initialInputs.swapQuote
|
|
416
346
|
);
|
|
417
347
|
|
|
348
|
+
if (initialInputs.collIsKtoken) {
|
|
349
|
+
if (initialInputs.strategy!.strategy.strategyLookupTable) {
|
|
350
|
+
const strategyLut = await getLookupTableAccount(
|
|
351
|
+
kaminoMarket.getConnection(),
|
|
352
|
+
initialInputs.strategy!.strategy.strategyLookupTable!
|
|
353
|
+
);
|
|
354
|
+
lookupTables.push(strategyLut!);
|
|
355
|
+
} else {
|
|
356
|
+
console.log('Strategy lookup table not found');
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
const collReserve = kaminoMarket.getReserveByMint(collTokenMint);
|
|
361
|
+
const debtReserve = kaminoMarket.getReserveByMint(debtTokenMint);
|
|
362
|
+
const solTokenReserve = kaminoMarket.getReserveByMint(WRAPPED_SOL_MINT);
|
|
363
|
+
const depositTokenIsSol = !solTokenReserve ? false : selectedTokenMint.equals(solTokenReserve!.getLiquidityMint());
|
|
364
|
+
|
|
365
|
+
const ixs = await buildDepositWithLeverageIxns(
|
|
366
|
+
kaminoMarket,
|
|
367
|
+
debtReserve!,
|
|
368
|
+
collReserve!,
|
|
369
|
+
owner,
|
|
370
|
+
initialInputs.obligation,
|
|
371
|
+
referrer,
|
|
372
|
+
currentSlot,
|
|
373
|
+
depositTokenIsSol,
|
|
374
|
+
scopeFeed,
|
|
375
|
+
initialInputs.calcs,
|
|
376
|
+
budgetAndPriorityFeeIxs,
|
|
377
|
+
{
|
|
378
|
+
preActionIxs: [],
|
|
379
|
+
swapIxs: swapIxs,
|
|
380
|
+
lookupTables: lookupTables,
|
|
381
|
+
},
|
|
382
|
+
initialInputs.strategy,
|
|
383
|
+
initialInputs.collIsKtoken
|
|
384
|
+
);
|
|
385
|
+
|
|
386
|
+
return {
|
|
387
|
+
ixs,
|
|
388
|
+
lookupTables,
|
|
389
|
+
swapInputs,
|
|
390
|
+
initialInputs,
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
async function buildDepositWithLeverageIxns(
|
|
395
|
+
market: KaminoMarket,
|
|
396
|
+
debtReserve: KaminoReserve,
|
|
397
|
+
collReserve: KaminoReserve,
|
|
398
|
+
owner: PublicKey,
|
|
399
|
+
obligation: KaminoObligation | ObligationType | undefined,
|
|
400
|
+
referrer: PublicKey,
|
|
401
|
+
currentSlot: number,
|
|
402
|
+
depositTokenIsSol: boolean,
|
|
403
|
+
scopeFeed: string | undefined,
|
|
404
|
+
calcs: DepositLeverageCalcsResult,
|
|
405
|
+
budgetAndPriorityFeeIxs: TransactionInstruction[] | undefined,
|
|
406
|
+
swapQuoteIxs: SwapQuoteIxs,
|
|
407
|
+
strategy: StrategyWithAddress | undefined,
|
|
408
|
+
collIsKtoken: boolean
|
|
409
|
+
): Promise<TransactionInstruction[]> {
|
|
410
|
+
const budgetIxns = budgetAndPriorityFeeIxs || getComputeBudgetAndPriorityFeeIxns(3000000);
|
|
411
|
+
const collTokenMint = collReserve.getLiquidityMint();
|
|
412
|
+
const debtTokenMint = debtReserve.getLiquidityMint();
|
|
413
|
+
const collTokenAta = getAssociatedTokenAddressSync(collTokenMint, owner);
|
|
414
|
+
const debtTokenAta = getAssociatedTokenAddressSync(debtTokenMint, owner);
|
|
415
|
+
|
|
418
416
|
// 1. Create atas & budget txns
|
|
419
417
|
let mintsToCreateAtas: Array<{ mint: PublicKey; tokenProgram: PublicKey }>;
|
|
420
418
|
if (collIsKtoken) {
|
|
421
419
|
const secondTokenAta = strategy!.strategy.tokenAMint.equals(debtTokenMint)
|
|
422
420
|
? strategy!.strategy.tokenBMint
|
|
423
421
|
: strategy!.strategy.tokenAMint;
|
|
424
|
-
const secondTokenTokenProgarm = strategy
|
|
422
|
+
const secondTokenTokenProgarm = strategy!.strategy.tokenAMint.equals(debtTokenMint)
|
|
425
423
|
? strategy!.strategy.tokenBTokenProgram.equals(PublicKey.default)
|
|
426
424
|
? TOKEN_PROGRAM_ID
|
|
427
425
|
: strategy!.strategy.tokenBTokenProgram
|
|
@@ -431,14 +429,14 @@ export const getDepositWithLeverageIxns = async (props: {
|
|
|
431
429
|
mintsToCreateAtas = [
|
|
432
430
|
{
|
|
433
431
|
mint: collTokenMint,
|
|
434
|
-
tokenProgram: collReserve
|
|
432
|
+
tokenProgram: collReserve.getLiquidityTokenProgram(),
|
|
435
433
|
},
|
|
436
434
|
{
|
|
437
435
|
mint: debtTokenMint,
|
|
438
|
-
tokenProgram: debtReserve
|
|
436
|
+
tokenProgram: debtReserve.getLiquidityTokenProgram(),
|
|
439
437
|
},
|
|
440
438
|
{
|
|
441
|
-
mint: collReserve
|
|
439
|
+
mint: collReserve.getCTokenMint(),
|
|
442
440
|
tokenProgram: TOKEN_PROGRAM_ID,
|
|
443
441
|
},
|
|
444
442
|
{
|
|
@@ -450,11 +448,11 @@ export const getDepositWithLeverageIxns = async (props: {
|
|
|
450
448
|
mintsToCreateAtas = [
|
|
451
449
|
{
|
|
452
450
|
mint: collTokenMint,
|
|
453
|
-
tokenProgram: collReserve
|
|
451
|
+
tokenProgram: collReserve.getLiquidityTokenProgram(),
|
|
454
452
|
},
|
|
455
453
|
{
|
|
456
454
|
mint: debtTokenMint,
|
|
457
|
-
tokenProgram: debtReserve
|
|
455
|
+
tokenProgram: debtReserve.getLiquidityTokenProgram(),
|
|
458
456
|
},
|
|
459
457
|
{
|
|
460
458
|
mint: collReserve!.getCTokenMint(),
|
|
@@ -463,75 +461,51 @@ export const getDepositWithLeverageIxns = async (props: {
|
|
|
463
461
|
];
|
|
464
462
|
}
|
|
465
463
|
|
|
466
|
-
const
|
|
467
|
-
const {
|
|
468
|
-
atas: [collTokenAta, debtTokenAta],
|
|
469
|
-
createAtaIxs,
|
|
470
|
-
} = await getAtasWithCreateIxnsIfMissing(connection, user, mintsToCreateAtas);
|
|
464
|
+
const atasAndCreateIxns = createAtasIdempotent(owner, mintsToCreateAtas);
|
|
471
465
|
|
|
472
|
-
// TODO: this needs to work the other way around also
|
|
473
|
-
// TODO: marius test this with shorting leverage and with leverage looping
|
|
474
466
|
const fillWsolAtaIxns: TransactionInstruction[] = [];
|
|
475
467
|
if (depositTokenIsSol) {
|
|
476
468
|
fillWsolAtaIxns.push(
|
|
477
469
|
...getDepositWsolIxns(
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
toLamports(calcs.initDepositInSol,
|
|
470
|
+
owner,
|
|
471
|
+
getAssociatedTokenAddressSync(WRAPPED_SOL_MINT, owner),
|
|
472
|
+
toLamports(calcs.initDepositInSol, SOL_DECIMALS).ceil()
|
|
481
473
|
)
|
|
482
474
|
);
|
|
483
475
|
}
|
|
484
476
|
|
|
485
|
-
//
|
|
477
|
+
// 2. Flash borrow & repay the collateral amount needed for given leverage
|
|
486
478
|
// if user deposits coll, then we borrow the diff, else we borrow the entire amount
|
|
487
479
|
const { flashBorrowIxn, flashRepayIxn } = getFlashLoanInstructions({
|
|
488
|
-
borrowIxnIndex: budgetIxns.length +
|
|
489
|
-
walletPublicKey:
|
|
490
|
-
lendingMarketAuthority:
|
|
491
|
-
lendingMarketAddress:
|
|
492
|
-
reserve: !collIsKtoken ? collReserve
|
|
480
|
+
borrowIxnIndex: budgetIxns.length + atasAndCreateIxns.length + fillWsolAtaIxns.length,
|
|
481
|
+
walletPublicKey: owner,
|
|
482
|
+
lendingMarketAuthority: market.getLendingMarketAuthority(),
|
|
483
|
+
lendingMarketAddress: market.getAddress(),
|
|
484
|
+
reserve: !collIsKtoken ? collReserve : debtReserve,
|
|
493
485
|
amountLamports: toLamports(
|
|
494
|
-
!collIsKtoken ? calcs.flashBorrowInCollToken :
|
|
495
|
-
!collIsKtoken ? collReserve
|
|
486
|
+
!collIsKtoken ? calcs.flashBorrowInCollToken : calcs.flashBorrowInDebtTokenKtokenOnly,
|
|
487
|
+
!collIsKtoken ? collReserve.stats.decimals : debtReserve.stats.decimals
|
|
496
488
|
),
|
|
497
489
|
destinationAta: !collIsKtoken ? collTokenAta : debtTokenAta,
|
|
498
|
-
referrerAccount:
|
|
499
|
-
referrerTokenState:
|
|
500
|
-
programId:
|
|
490
|
+
referrerAccount: market.programId,
|
|
491
|
+
referrerTokenState: market.programId,
|
|
492
|
+
programId: market.programId,
|
|
501
493
|
});
|
|
502
494
|
|
|
503
|
-
console.log(
|
|
504
|
-
'Borrowing: ',
|
|
505
|
-
toLamports(!collIsKtoken ? calcs.debtTokenToBorrow : calcsKtoken!.debtTokenToBorrow, debtReserve!.stats.decimals)
|
|
506
|
-
.ceil()
|
|
507
|
-
.toString()
|
|
508
|
-
);
|
|
509
495
|
// 3. Deposit initial tokens + borrowed tokens into reserve
|
|
510
|
-
let obligationType: ObligationType;
|
|
511
|
-
if (obligationTypeTagOverride === ObligationTypeTag.Multiply) {
|
|
512
|
-
// multiply
|
|
513
|
-
obligationType = new MultiplyObligation(collTokenMint, debtTokenMint, kaminoMarket.programId);
|
|
514
|
-
} else if (obligationTypeTagOverride === ObligationTypeTag.Leverage) {
|
|
515
|
-
// leverage
|
|
516
|
-
obligationType = new LeverageObligation(collTokenMint, debtTokenMint, kaminoMarket.programId);
|
|
517
|
-
} else {
|
|
518
|
-
throw Error('Obligation type tag not supported for leverage, please use 1 - multiply or 3 - leverage');
|
|
519
|
-
}
|
|
520
|
-
|
|
521
496
|
const scopeRefresh = scopeFeed ? { includeScopeRefresh: true, scopeFeed: scopeFeed } : undefined;
|
|
522
|
-
|
|
523
497
|
const kaminoDepositAndBorrowAction = await KaminoAction.buildDepositAndBorrowTxns(
|
|
524
|
-
|
|
525
|
-
toLamports(!collIsKtoken ? calcs.collTokenToDeposit :
|
|
498
|
+
market,
|
|
499
|
+
toLamports(!collIsKtoken ? calcs.collTokenToDeposit : calcs.collTokenToDeposit, collReserve.stats.decimals)
|
|
526
500
|
.floor()
|
|
527
501
|
.toString(),
|
|
528
502
|
collTokenMint,
|
|
529
|
-
toLamports(!collIsKtoken ? calcs.debtTokenToBorrow :
|
|
503
|
+
toLamports(!collIsKtoken ? calcs.debtTokenToBorrow : calcs.debtTokenToBorrow, debtReserve.stats.decimals)
|
|
530
504
|
.ceil()
|
|
531
505
|
.toString(),
|
|
532
506
|
debtTokenMint,
|
|
533
|
-
|
|
534
|
-
obligation
|
|
507
|
+
owner,
|
|
508
|
+
obligation!,
|
|
535
509
|
0,
|
|
536
510
|
false,
|
|
537
511
|
true, // emode
|
|
@@ -541,353 +515,310 @@ export const getDepositWithLeverageIxns = async (props: {
|
|
|
541
515
|
scopeRefresh
|
|
542
516
|
);
|
|
543
517
|
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
'debt for',
|
|
548
|
-
!collIsKtoken ? calcs.swapCollTokenExpectedOut.toString() : calcsKtoken!.requiredCollateral.toNumber().toString(),
|
|
549
|
-
'coll'
|
|
550
|
-
);
|
|
551
|
-
|
|
552
|
-
const ixns = [
|
|
553
|
-
...budgetIxns,
|
|
554
|
-
...createAtaIxs,
|
|
555
|
-
...fillWsolAtaIxns,
|
|
556
|
-
...[flashBorrowIxn],
|
|
557
|
-
...kaminoDepositAndBorrowAction.setupIxs,
|
|
558
|
-
...[kaminoDepositAndBorrowAction.lendingIxs[0]],
|
|
559
|
-
...kaminoDepositAndBorrowAction.inBetweenIxs,
|
|
560
|
-
...[kaminoDepositAndBorrowAction.lendingIxs[1]],
|
|
561
|
-
...kaminoDepositAndBorrowAction.cleanupIxs,
|
|
562
|
-
...[flashRepayIxn],
|
|
563
|
-
];
|
|
564
|
-
|
|
565
|
-
const uniqueAccounts = new PublicKeySet<PublicKey>([]);
|
|
566
|
-
ixns.forEach((ixn) => {
|
|
567
|
-
ixn.keys.forEach((key) => {
|
|
568
|
-
uniqueAccounts.add(key.pubkey);
|
|
569
|
-
});
|
|
570
|
-
});
|
|
571
|
-
const totalKlendAccounts = uniqueAccounts.toArray().length;
|
|
572
|
-
|
|
573
|
-
// return early to avoid extra swapper calls
|
|
574
|
-
if (getTotalKlendAccountsOnly) {
|
|
575
|
-
return {
|
|
576
|
-
ixns: [],
|
|
577
|
-
lookupTablesAddresses: [],
|
|
578
|
-
swapInputs: {
|
|
579
|
-
inputAmountLamports: new Decimal('0'),
|
|
580
|
-
inputMint: PublicKey.default,
|
|
581
|
-
outputMint: PublicKey.default,
|
|
582
|
-
},
|
|
583
|
-
totalKlendAccounts: totalKlendAccounts,
|
|
584
|
-
};
|
|
585
|
-
}
|
|
586
|
-
|
|
587
|
-
let depositSwapper: SwapIxnsProvider;
|
|
588
|
-
let expectedDebtTokenAtaBalance: Decimal = new Decimal(0); // only needed for kTokens
|
|
518
|
+
// 4. Swap
|
|
519
|
+
const { swapIxs } = swapQuoteIxs;
|
|
520
|
+
const swapInstructions = removeBudgetAndAtaIxns(swapIxs, []);
|
|
589
521
|
|
|
590
522
|
if (!collIsKtoken) {
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
futureBalanceInAta = futureBalanceInAta.add(
|
|
605
|
-
!collIsKtoken ? calcs.debtTokenToBorrow : calcsKtoken!.flashBorrowInDebtToken
|
|
606
|
-
);
|
|
607
|
-
expectedDebtTokenAtaBalance = await getExpectedTokenBalanceAfterBorrow(
|
|
608
|
-
connection,
|
|
609
|
-
debtTokenMint,
|
|
610
|
-
user,
|
|
611
|
-
toLamports(futureBalanceInAta.toDecimalPlaces(debtReserve!.stats.decimals), debtReserve!.stats.decimals),
|
|
612
|
-
debtReserve!.state.liquidity.mintDecimals.toNumber()
|
|
613
|
-
);
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
const swapInputs: SwapInputs = {
|
|
617
|
-
inputAmountLamports: toLamports(
|
|
618
|
-
!collIsKtoken ? calcs.swapDebtTokenIn : calcsKtoken!.singleSidedDeposit,
|
|
619
|
-
debtReserve!.stats.decimals
|
|
620
|
-
).ceil(),
|
|
621
|
-
inputMint: debtTokenMint,
|
|
622
|
-
outputMint: collTokenMint,
|
|
623
|
-
};
|
|
624
|
-
|
|
625
|
-
const [swapIxns, lookupTablesAddresses] = await depositSwapper(
|
|
626
|
-
swapInputs.inputAmountLamports.toNumber(),
|
|
627
|
-
swapInputs.inputMint,
|
|
628
|
-
swapInputs.outputMint,
|
|
629
|
-
slippagePct.toNumber(),
|
|
630
|
-
expectedDebtTokenAtaBalance
|
|
631
|
-
);
|
|
632
|
-
|
|
633
|
-
if (collIsKtoken) {
|
|
634
|
-
if (strategy?.strategy.strategyLookupTable) {
|
|
635
|
-
lookupTablesAddresses.push(strategy?.strategy.strategyLookupTable!);
|
|
636
|
-
} else {
|
|
637
|
-
console.log('Strategy lookup table not found');
|
|
638
|
-
}
|
|
639
|
-
}
|
|
640
|
-
|
|
641
|
-
const swapInstructions = removeBudgetAndAtaIxns(swapIxns, []);
|
|
642
|
-
|
|
643
|
-
if (!collIsKtoken) {
|
|
644
|
-
return {
|
|
645
|
-
ixns: [
|
|
646
|
-
...budgetIxns,
|
|
647
|
-
...createAtaIxs,
|
|
648
|
-
...fillWsolAtaIxns,
|
|
649
|
-
...[flashBorrowIxn],
|
|
650
|
-
...kaminoDepositAndBorrowAction.setupIxs,
|
|
651
|
-
...[kaminoDepositAndBorrowAction.lendingIxs[0]],
|
|
652
|
-
...kaminoDepositAndBorrowAction.inBetweenIxs,
|
|
653
|
-
...[kaminoDepositAndBorrowAction.lendingIxs[1]],
|
|
654
|
-
...kaminoDepositAndBorrowAction.cleanupIxs,
|
|
655
|
-
...swapInstructions,
|
|
656
|
-
...[flashRepayIxn],
|
|
657
|
-
],
|
|
658
|
-
lookupTablesAddresses,
|
|
659
|
-
swapInputs,
|
|
660
|
-
totalKlendAccounts: totalKlendAccounts,
|
|
661
|
-
};
|
|
523
|
+
return [
|
|
524
|
+
...budgetIxns,
|
|
525
|
+
...atasAndCreateIxns.map((x) => x.createAtaIx),
|
|
526
|
+
...fillWsolAtaIxns,
|
|
527
|
+
...[flashBorrowIxn],
|
|
528
|
+
...kaminoDepositAndBorrowAction.setupIxs,
|
|
529
|
+
...[kaminoDepositAndBorrowAction.lendingIxs[0]],
|
|
530
|
+
...kaminoDepositAndBorrowAction.inBetweenIxs,
|
|
531
|
+
...[kaminoDepositAndBorrowAction.lendingIxs[1]],
|
|
532
|
+
...kaminoDepositAndBorrowAction.cleanupIxs,
|
|
533
|
+
...swapInstructions,
|
|
534
|
+
...[flashRepayIxn],
|
|
535
|
+
];
|
|
662
536
|
} else {
|
|
663
|
-
return
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
],
|
|
677
|
-
lookupTablesAddresses,
|
|
678
|
-
swapInputs,
|
|
679
|
-
totalKlendAccounts: totalKlendAccounts,
|
|
680
|
-
};
|
|
537
|
+
return [
|
|
538
|
+
...budgetIxns,
|
|
539
|
+
...atasAndCreateIxns.map((x) => x.createAtaIx),
|
|
540
|
+
...fillWsolAtaIxns,
|
|
541
|
+
...[flashBorrowIxn],
|
|
542
|
+
...swapInstructions,
|
|
543
|
+
...kaminoDepositAndBorrowAction.setupIxs,
|
|
544
|
+
...[kaminoDepositAndBorrowAction.lendingIxs[0]],
|
|
545
|
+
...kaminoDepositAndBorrowAction.inBetweenIxs,
|
|
546
|
+
...[kaminoDepositAndBorrowAction.lendingIxs[1]],
|
|
547
|
+
...kaminoDepositAndBorrowAction.cleanupIxs,
|
|
548
|
+
...[flashRepayIxn],
|
|
549
|
+
];
|
|
681
550
|
}
|
|
682
|
-
}
|
|
683
|
-
|
|
684
|
-
export
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
export async function getWithdrawWithLeverageSwapInputs<QuoteResponse>({
|
|
554
|
+
owner,
|
|
555
|
+
kaminoMarket,
|
|
556
|
+
debtTokenMint,
|
|
557
|
+
collTokenMint,
|
|
558
|
+
deposited,
|
|
559
|
+
borrowed,
|
|
560
|
+
obligation,
|
|
561
|
+
referrer,
|
|
562
|
+
currentSlot,
|
|
563
|
+
withdrawAmount,
|
|
564
|
+
priceCollToDebt,
|
|
565
|
+
slippagePct,
|
|
566
|
+
isClosingPosition,
|
|
567
|
+
selectedTokenMint,
|
|
568
|
+
budgetAndPriorityFeeIxs,
|
|
569
|
+
kamino,
|
|
570
|
+
scopeFeed,
|
|
571
|
+
quoteBufferBps,
|
|
572
|
+
isKtoken,
|
|
573
|
+
quoter,
|
|
574
|
+
}: WithdrawWithLeverageSwapInputsProps<QuoteResponse>): Promise<{
|
|
698
575
|
swapInputs: SwapInputs;
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
576
|
+
initialInputs: WithdrawLeverageInitialInputs<QuoteResponse>;
|
|
577
|
+
}> {
|
|
578
|
+
const collReserve = kaminoMarket.getReserveByMint(collTokenMint);
|
|
579
|
+
const debtReserve = kaminoMarket.getReserveByMint(debtTokenMint);
|
|
580
|
+
const flashLoanFee = debtReserve!.getFlashLoanFee() || new Decimal(0);
|
|
581
|
+
const selectedTokenIsCollToken = selectedTokenMint.equals(collTokenMint);
|
|
582
|
+
const collIsKtoken = await isKtoken(collTokenMint);
|
|
583
|
+
const strategy = collIsKtoken ? (await kamino!.getStrategyByKTokenMint(collTokenMint))! : undefined;
|
|
584
|
+
|
|
585
|
+
const solTokenReserve = kaminoMarket.getReserveByMint(WRAPPED_SOL_MINT);
|
|
586
|
+
const depositTokenIsSol = !solTokenReserve ? false : selectedTokenMint.equals(solTokenReserve!.getLiquidityMint());
|
|
587
|
+
|
|
588
|
+
const calcs = withdrawLeverageCalcs(
|
|
589
|
+
kaminoMarket,
|
|
590
|
+
collReserve!,
|
|
591
|
+
debtReserve!,
|
|
592
|
+
priceCollToDebt,
|
|
593
|
+
withdrawAmount,
|
|
702
594
|
deposited,
|
|
703
595
|
borrowed,
|
|
704
|
-
|
|
705
|
-
slippagePct,
|
|
596
|
+
currentSlot,
|
|
706
597
|
isClosingPosition,
|
|
707
|
-
|
|
598
|
+
selectedTokenIsCollToken,
|
|
708
599
|
selectedTokenMint,
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
600
|
+
obligation,
|
|
601
|
+
flashLoanFee,
|
|
602
|
+
slippagePct
|
|
603
|
+
);
|
|
604
|
+
|
|
605
|
+
const klendIxs = await buildWithdrawWithLeverageIxns(
|
|
606
|
+
kaminoMarket,
|
|
607
|
+
debtReserve!,
|
|
608
|
+
collReserve!,
|
|
609
|
+
owner,
|
|
610
|
+
obligation,
|
|
611
|
+
referrer,
|
|
712
612
|
currentSlot,
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
613
|
+
isClosingPosition,
|
|
614
|
+
depositTokenIsSol,
|
|
615
|
+
scopeFeed,
|
|
616
|
+
calcs,
|
|
617
|
+
budgetAndPriorityFeeIxs,
|
|
618
|
+
{
|
|
619
|
+
preActionIxs: [],
|
|
620
|
+
swapIxs: [],
|
|
621
|
+
lookupTables: [],
|
|
622
|
+
},
|
|
623
|
+
strategy,
|
|
624
|
+
collIsKtoken
|
|
625
|
+
);
|
|
718
626
|
|
|
719
|
-
const
|
|
720
|
-
? { adjustDepositPosition: deposited, adjustBorrowPosition: borrowed }
|
|
721
|
-
: calcWithdrawAmounts({
|
|
722
|
-
collTokenMint: collTokenMint,
|
|
723
|
-
priceCollToDebt: new Decimal(priceCollToDebt),
|
|
724
|
-
currentDepositPosition: deposited,
|
|
725
|
-
currentBorrowPosition: borrowed,
|
|
726
|
-
withdrawAmount: new Decimal(amount),
|
|
727
|
-
selectedTokenMint: selectedTokenMint,
|
|
728
|
-
});
|
|
627
|
+
const uniqueKlendAccounts = uniqueAccounts(klendIxs);
|
|
729
628
|
|
|
730
|
-
const
|
|
731
|
-
.
|
|
732
|
-
|
|
629
|
+
const swapInputAmount = toLamports(
|
|
630
|
+
calcs.collTokenSwapIn,
|
|
631
|
+
collReserve!.state.liquidity.mintDecimals.toNumber()
|
|
632
|
+
).ceil();
|
|
733
633
|
|
|
734
|
-
const
|
|
735
|
-
.mul(
|
|
736
|
-
|
|
634
|
+
const swapInputsForQuote: SwapInputs = {
|
|
635
|
+
inputAmountLamports: swapInputAmount.mul(new Decimal(1).add(quoteBufferBps.div(FullBPS))),
|
|
636
|
+
inputMint: collTokenMint,
|
|
637
|
+
outputMint: debtTokenMint,
|
|
638
|
+
amountDebtAtaBalance: new Decimal(0), // Only needed for ktokens deposits
|
|
639
|
+
};
|
|
737
640
|
|
|
738
|
-
const
|
|
739
|
-
.mul(new Decimal(1).plus(flashLoanFee))
|
|
740
|
-
.mul(new Decimal(1 + slippagePct / 100))
|
|
741
|
-
.div(priceCollToDebt);
|
|
641
|
+
const swapQuote = await quoter(swapInputsForQuote, uniqueKlendAccounts);
|
|
742
642
|
|
|
743
|
-
const
|
|
643
|
+
const calcsQuotePrice = withdrawLeverageCalcs(
|
|
644
|
+
kaminoMarket,
|
|
645
|
+
collReserve!,
|
|
646
|
+
debtReserve!,
|
|
647
|
+
collIsKtoken ? swapQuote.priceAInB : priceCollToDebt,
|
|
648
|
+
withdrawAmount,
|
|
649
|
+
deposited,
|
|
650
|
+
borrowed,
|
|
651
|
+
currentSlot,
|
|
652
|
+
isClosingPosition,
|
|
653
|
+
selectedTokenIsCollToken,
|
|
654
|
+
selectedTokenMint,
|
|
655
|
+
obligation,
|
|
656
|
+
flashLoanFee,
|
|
657
|
+
slippagePct
|
|
658
|
+
);
|
|
744
659
|
|
|
745
|
-
const
|
|
660
|
+
const swapInputAmountQuotePrice = toLamports(
|
|
661
|
+
calcsQuotePrice.collTokenSwapIn,
|
|
662
|
+
collReserve!.state.liquidity.mintDecimals.toNumber()
|
|
663
|
+
).ceil();
|
|
746
664
|
|
|
747
665
|
return {
|
|
748
666
|
swapInputs: {
|
|
749
|
-
inputAmountLamports:
|
|
667
|
+
inputAmountLamports: swapInputAmountQuotePrice,
|
|
750
668
|
inputMint: collTokenMint,
|
|
751
669
|
outputMint: debtTokenMint,
|
|
670
|
+
amountDebtAtaBalance: new Decimal(0), // Only needed for ktokens deposits
|
|
671
|
+
},
|
|
672
|
+
initialInputs: {
|
|
673
|
+
calcs: calcsQuotePrice,
|
|
674
|
+
swapQuote,
|
|
675
|
+
currentSlot,
|
|
676
|
+
collIsKtoken,
|
|
677
|
+
strategy,
|
|
678
|
+
obligation,
|
|
679
|
+
klendAccounts: uniqueKlendAccounts,
|
|
752
680
|
},
|
|
753
681
|
};
|
|
754
|
-
}
|
|
755
|
-
|
|
756
|
-
export
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
}
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
amount,
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
export async function getWithdrawWithLeverageIxns<QuoteResponse>({
|
|
685
|
+
owner,
|
|
686
|
+
kaminoMarket,
|
|
687
|
+
debtTokenMint,
|
|
688
|
+
collTokenMint,
|
|
689
|
+
obligation,
|
|
690
|
+
deposited,
|
|
691
|
+
borrowed,
|
|
692
|
+
referrer,
|
|
693
|
+
currentSlot,
|
|
694
|
+
withdrawAmount,
|
|
695
|
+
priceCollToDebt,
|
|
696
|
+
slippagePct,
|
|
697
|
+
isClosingPosition,
|
|
698
|
+
selectedTokenMint,
|
|
699
|
+
budgetAndPriorityFeeIxs,
|
|
700
|
+
kamino,
|
|
701
|
+
scopeFeed,
|
|
702
|
+
quoteBufferBps,
|
|
703
|
+
isKtoken,
|
|
704
|
+
quoter,
|
|
705
|
+
swapper,
|
|
706
|
+
}: WithdrawWithLeverageProps<QuoteResponse>): Promise<WithdrawLeverageIxsResponse<QuoteResponse>> {
|
|
707
|
+
const collReserve = kaminoMarket.getReserveByMint(collTokenMint);
|
|
708
|
+
const debtReserve = kaminoMarket.getReserveByMint(debtTokenMint);
|
|
709
|
+
|
|
710
|
+
const solTokenReserve = kaminoMarket.getReserveByMint(WRAPPED_SOL_MINT);
|
|
711
|
+
const depositTokenIsSol = !solTokenReserve ? false : selectedTokenMint.equals(solTokenReserve!.getLiquidityMint());
|
|
712
|
+
const { swapInputs, initialInputs } = await getWithdrawWithLeverageSwapInputs({
|
|
713
|
+
owner,
|
|
714
|
+
kaminoMarket,
|
|
715
|
+
debtTokenMint,
|
|
716
|
+
collTokenMint,
|
|
790
717
|
deposited,
|
|
791
718
|
borrowed,
|
|
792
|
-
|
|
793
|
-
|
|
719
|
+
obligation,
|
|
720
|
+
referrer,
|
|
721
|
+
currentSlot,
|
|
722
|
+
withdrawAmount,
|
|
794
723
|
priceCollToDebt,
|
|
795
|
-
selectedTokenMint,
|
|
796
|
-
isClosingPosition,
|
|
797
|
-
kaminoMarket,
|
|
798
724
|
slippagePct,
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
725
|
+
isClosingPosition,
|
|
726
|
+
selectedTokenMint,
|
|
727
|
+
budgetAndPriorityFeeIxs,
|
|
802
728
|
kamino,
|
|
803
|
-
obligationTypeTagOverride,
|
|
804
|
-
obligation,
|
|
805
|
-
currentSlot,
|
|
806
|
-
getTotalKlendAccountsOnly,
|
|
807
729
|
scopeFeed,
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
const flashLoanFee = debtReserve?.getFlashLoanFee() || new Decimal(0);
|
|
813
|
-
const collIsKtoken = await isKtoken(collTokenMint);
|
|
730
|
+
quoteBufferBps,
|
|
731
|
+
isKtoken,
|
|
732
|
+
quoter,
|
|
733
|
+
});
|
|
814
734
|
|
|
815
|
-
|
|
816
|
-
const selectedTokenIsCollToken = selectedTokenMint.equals(collTokenMint);
|
|
817
|
-
const depositTokenIsSol = !solTokenReserve ? false : selectedTokenMint.equals(solTokenReserve!.getLiquidityMint());
|
|
735
|
+
let withdrawSwapper: SwapQuoteIxsProvider<QuoteResponse>;
|
|
818
736
|
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
// leverage
|
|
825
|
-
obligationType = new LeverageObligation(collTokenMint, debtTokenMint, kaminoMarket.programId);
|
|
737
|
+
if (initialInputs.collIsKtoken) {
|
|
738
|
+
if (kamino === undefined) {
|
|
739
|
+
throw Error('Ktoken use as collateral for leverage without Kamino instance');
|
|
740
|
+
}
|
|
741
|
+
withdrawSwapper = await getKtokenToTokenSwapper(kaminoMarket, kamino, owner, swapper);
|
|
826
742
|
} else {
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
743
|
+
withdrawSwapper = swapper;
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
const { swapIxs, lookupTables } = await withdrawSwapper(
|
|
747
|
+
swapInputs,
|
|
748
|
+
initialInputs.klendAccounts,
|
|
749
|
+
initialInputs.swapQuote
|
|
750
|
+
);
|
|
751
|
+
|
|
752
|
+
if (initialInputs.collIsKtoken) {
|
|
753
|
+
if (initialInputs.strategy!.strategy.strategyLookupTable) {
|
|
754
|
+
const strategyLut = await getLookupTableAccount(
|
|
755
|
+
kaminoMarket.getConnection(),
|
|
756
|
+
initialInputs.strategy!.strategy.strategyLookupTable!
|
|
757
|
+
);
|
|
758
|
+
lookupTables.push(strategyLut!);
|
|
759
|
+
} else {
|
|
760
|
+
console.log('Strategy lookup table not found');
|
|
761
|
+
}
|
|
830
762
|
}
|
|
831
763
|
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
const
|
|
882
|
-
|
|
883
|
-
console.log('Expecting to swap', collTokenSwapIn.toString(), 'coll for', debtTokenExpectedSwapOut.toString(), 'debt');
|
|
764
|
+
const ixs = await buildWithdrawWithLeverageIxns(
|
|
765
|
+
kaminoMarket,
|
|
766
|
+
debtReserve!,
|
|
767
|
+
collReserve!,
|
|
768
|
+
owner,
|
|
769
|
+
obligation,
|
|
770
|
+
referrer,
|
|
771
|
+
currentSlot,
|
|
772
|
+
isClosingPosition,
|
|
773
|
+
depositTokenIsSol,
|
|
774
|
+
scopeFeed,
|
|
775
|
+
initialInputs.calcs,
|
|
776
|
+
budgetAndPriorityFeeIxs,
|
|
777
|
+
{
|
|
778
|
+
preActionIxs: [],
|
|
779
|
+
swapIxs,
|
|
780
|
+
lookupTables,
|
|
781
|
+
},
|
|
782
|
+
initialInputs.strategy,
|
|
783
|
+
initialInputs.collIsKtoken
|
|
784
|
+
);
|
|
785
|
+
|
|
786
|
+
// Send ixns and lookup tables
|
|
787
|
+
return {
|
|
788
|
+
ixs,
|
|
789
|
+
lookupTables,
|
|
790
|
+
swapInputs,
|
|
791
|
+
initialInputs: initialInputs,
|
|
792
|
+
};
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
export async function buildWithdrawWithLeverageIxns(
|
|
796
|
+
market: KaminoMarket,
|
|
797
|
+
debtReserve: KaminoReserve,
|
|
798
|
+
collReserve: KaminoReserve,
|
|
799
|
+
owner: PublicKey,
|
|
800
|
+
obligation: KaminoObligation,
|
|
801
|
+
referrer: PublicKey,
|
|
802
|
+
currentSlot: number,
|
|
803
|
+
isClosingPosition: boolean,
|
|
804
|
+
depositTokenIsSol: boolean,
|
|
805
|
+
scopeFeed: string | undefined,
|
|
806
|
+
calcs: WithdrawLeverageCalcsResult,
|
|
807
|
+
budgetAndPriorityFeeIxs: TransactionInstruction[] | undefined,
|
|
808
|
+
swapQuoteIxs: SwapQuoteIxs,
|
|
809
|
+
strategy: StrategyWithAddress | undefined,
|
|
810
|
+
collIsKtoken: boolean
|
|
811
|
+
): Promise<TransactionInstruction[]> {
|
|
812
|
+
const collTokenMint = collReserve.getLiquidityMint();
|
|
813
|
+
const debtTokenMint = debtReserve.getLiquidityMint();
|
|
814
|
+
const debtTokenAta = getAssociatedTokenAddressSync(debtTokenMint, owner);
|
|
884
815
|
// 1. Create atas & budget txns & user metadata
|
|
885
816
|
let mintsToCreateAtas: Array<{ mint: PublicKey; tokenProgram: PublicKey }>;
|
|
886
817
|
if (collIsKtoken) {
|
|
887
818
|
const secondTokenAta = strategy!.strategy.tokenAMint.equals(debtTokenMint)
|
|
888
819
|
? strategy!.strategy.tokenBMint
|
|
889
820
|
: strategy!.strategy.tokenAMint;
|
|
890
|
-
const secondTokenTokenProgram = strategy
|
|
821
|
+
const secondTokenTokenProgram = strategy!.strategy.tokenAMint.equals(debtTokenMint)
|
|
891
822
|
? strategy!.strategy.tokenBTokenProgram.equals(PublicKey.default)
|
|
892
823
|
? TOKEN_PROGRAM_ID
|
|
893
824
|
: strategy!.strategy.tokenBTokenProgram
|
|
@@ -897,14 +828,14 @@ export const getWithdrawWithLeverageIxns = async (props: {
|
|
|
897
828
|
mintsToCreateAtas = [
|
|
898
829
|
{
|
|
899
830
|
mint: collTokenMint,
|
|
900
|
-
tokenProgram: collReserve
|
|
831
|
+
tokenProgram: collReserve.getLiquidityTokenProgram(),
|
|
901
832
|
},
|
|
902
833
|
{
|
|
903
834
|
mint: debtTokenMint,
|
|
904
|
-
tokenProgram: debtReserve
|
|
835
|
+
tokenProgram: debtReserve.getLiquidityTokenProgram(),
|
|
905
836
|
},
|
|
906
837
|
{
|
|
907
|
-
mint: collReserve
|
|
838
|
+
mint: collReserve.getCTokenMint(),
|
|
908
839
|
tokenProgram: TOKEN_PROGRAM_ID,
|
|
909
840
|
},
|
|
910
841
|
{
|
|
@@ -929,57 +860,59 @@ export const getWithdrawWithLeverageIxns = async (props: {
|
|
|
929
860
|
];
|
|
930
861
|
}
|
|
931
862
|
|
|
932
|
-
const
|
|
933
|
-
atas: [, debtTokenAta],
|
|
934
|
-
createAtaIxs,
|
|
935
|
-
} = await getAtasWithCreateIxnsIfMissing(connection, user, mintsToCreateAtas);
|
|
863
|
+
const atasAndCreateIxns = createAtasIdempotent(owner, mintsToCreateAtas);
|
|
936
864
|
|
|
937
865
|
const closeWsolAtaIxns: TransactionInstruction[] = [];
|
|
938
866
|
if (depositTokenIsSol || debtTokenMint.equals(WRAPPED_SOL_MINT)) {
|
|
939
|
-
const wsolAta = getAssociatedTokenAddress(WRAPPED_SOL_MINT,
|
|
940
|
-
closeWsolAtaIxns.push(createCloseAccountInstruction(wsolAta,
|
|
867
|
+
const wsolAta = getAssociatedTokenAddress(WRAPPED_SOL_MINT, owner, false);
|
|
868
|
+
closeWsolAtaIxns.push(createCloseAccountInstruction(wsolAta, owner, owner, [], TOKEN_PROGRAM_ID));
|
|
941
869
|
}
|
|
942
870
|
|
|
943
|
-
const budgetIxns =
|
|
871
|
+
const budgetIxns = budgetAndPriorityFeeIxs || getComputeBudgetAndPriorityFeeIxns(3000000);
|
|
944
872
|
|
|
945
|
-
// TODO:
|
|
873
|
+
// TODO: Might be worth removing as it's only needed for Ktokens
|
|
946
874
|
// This is here so that we have enough wsol to repay in case the kAB swapped to sol after estimates is not enough
|
|
947
875
|
const fillWsolAtaIxns: TransactionInstruction[] = [];
|
|
948
876
|
if (debtTokenMint.equals(WRAPPED_SOL_MINT)) {
|
|
949
|
-
const halfSolBalance = (await
|
|
877
|
+
const halfSolBalance = (await market.getConnection().getBalance(owner)) / LAMPORTS_PER_SOL / 2;
|
|
950
878
|
const balanceToWrap = halfSolBalance < 0.1 ? halfSolBalance : 0.1;
|
|
951
879
|
fillWsolAtaIxns.push(
|
|
952
|
-
...getDepositWsolIxns(
|
|
880
|
+
...getDepositWsolIxns(
|
|
881
|
+
owner,
|
|
882
|
+
getAssociatedTokenAddressSync(WRAPPED_SOL_MINT, owner),
|
|
883
|
+
toLamports(balanceToWrap, SOL_DECIMALS).ceil()
|
|
884
|
+
)
|
|
953
885
|
);
|
|
954
886
|
}
|
|
955
887
|
|
|
956
888
|
// 2. Prepare the flash borrow and flash repay amounts and ixns
|
|
957
889
|
// We borrow exactly how much we need to repay
|
|
958
890
|
// and repay that + flash amount fee
|
|
959
|
-
|
|
960
891
|
const { flashBorrowIxn, flashRepayIxn } = getFlashLoanInstructions({
|
|
961
|
-
borrowIxnIndex: budgetIxns.length +
|
|
962
|
-
walletPublicKey:
|
|
963
|
-
lendingMarketAuthority:
|
|
964
|
-
lendingMarketAddress:
|
|
892
|
+
borrowIxnIndex: budgetIxns.length + atasAndCreateIxns.length + fillWsolAtaIxns.length,
|
|
893
|
+
walletPublicKey: owner,
|
|
894
|
+
lendingMarketAuthority: market.getLendingMarketAuthority(),
|
|
895
|
+
lendingMarketAddress: market.getAddress(),
|
|
965
896
|
reserve: debtReserve!,
|
|
966
|
-
amountLamports: toLamports(repayAmount, debtReserve!.stats.decimals),
|
|
897
|
+
amountLamports: toLamports(calcs.repayAmount, debtReserve!.stats.decimals),
|
|
967
898
|
destinationAta: debtTokenAta,
|
|
968
|
-
referrerAccount:
|
|
969
|
-
referrerTokenState:
|
|
970
|
-
programId:
|
|
899
|
+
referrerAccount: market.programId,
|
|
900
|
+
referrerTokenState: market.programId,
|
|
901
|
+
programId: market.programId,
|
|
971
902
|
});
|
|
972
903
|
|
|
973
904
|
// 6. Repay borrowed tokens and Withdraw tokens from reserve that will be swapped to repay flash loan
|
|
974
905
|
const repayAndWithdrawAction = await KaminoAction.buildRepayAndWithdrawTxns(
|
|
975
|
-
|
|
976
|
-
isClosingPosition ? U64_MAX : toLamports(repayAmount, debtReserve!.stats.decimals).floor().toString(),
|
|
906
|
+
market,
|
|
907
|
+
isClosingPosition ? U64_MAX : toLamports(calcs.repayAmount, debtReserve!.stats.decimals).floor().toString(),
|
|
977
908
|
debtTokenMint,
|
|
978
|
-
isClosingPosition
|
|
909
|
+
isClosingPosition
|
|
910
|
+
? U64_MAX
|
|
911
|
+
: toLamports(calcs.depositTokenWithdrawAmount, collReserve!.stats.decimals).ceil().toString(),
|
|
979
912
|
collTokenMint,
|
|
980
|
-
|
|
913
|
+
owner,
|
|
981
914
|
currentSlot,
|
|
982
|
-
|
|
915
|
+
obligation,
|
|
983
916
|
0,
|
|
984
917
|
false,
|
|
985
918
|
false,
|
|
@@ -989,76 +922,11 @@ export const getWithdrawWithLeverageIxns = async (props: {
|
|
|
989
922
|
{ includeScopeRefresh: true, scopeFeed: scopeFeed! }
|
|
990
923
|
);
|
|
991
924
|
|
|
992
|
-
const
|
|
993
|
-
...budgetIxns,
|
|
994
|
-
...createAtaIxs,
|
|
995
|
-
...fillWsolAtaIxns,
|
|
996
|
-
...[flashBorrowIxn],
|
|
997
|
-
...repayAndWithdrawAction.setupIxs,
|
|
998
|
-
...[repayAndWithdrawAction.lendingIxs[0]],
|
|
999
|
-
...repayAndWithdrawAction.inBetweenIxs,
|
|
1000
|
-
...[repayAndWithdrawAction.lendingIxs[1]],
|
|
1001
|
-
...repayAndWithdrawAction.cleanupIxs,
|
|
1002
|
-
...[flashRepayIxn],
|
|
1003
|
-
...closeWsolAtaIxns,
|
|
1004
|
-
];
|
|
1005
|
-
|
|
1006
|
-
const uniqueAccs = uniqueAccounts(klendIxns);
|
|
1007
|
-
const totalKlendAccounts = uniqueAccs.length;
|
|
1008
|
-
|
|
1009
|
-
// return early to avoid extra swapper calls
|
|
1010
|
-
if (getTotalKlendAccountsOnly) {
|
|
1011
|
-
return {
|
|
1012
|
-
ixns: [],
|
|
1013
|
-
lookupTablesAddresses: [],
|
|
1014
|
-
swapInputs: {
|
|
1015
|
-
inputAmountLamports: new Decimal('0'),
|
|
1016
|
-
inputMint: PublicKey.default,
|
|
1017
|
-
outputMint: PublicKey.default,
|
|
1018
|
-
},
|
|
1019
|
-
totalKlendAccounts: totalKlendAccounts,
|
|
1020
|
-
};
|
|
1021
|
-
}
|
|
1022
|
-
|
|
1023
|
-
let withdrawSwapper: SwapIxnsProvider;
|
|
1024
|
-
|
|
1025
|
-
if (collIsKtoken) {
|
|
1026
|
-
if (kamino === undefined) {
|
|
1027
|
-
throw Error('Ktoken use as collateral for leverage without Kamino instance');
|
|
1028
|
-
}
|
|
1029
|
-
withdrawSwapper = await getKtokenToTokenSwapper(kaminoMarket, kamino, user, swapper);
|
|
1030
|
-
} else {
|
|
1031
|
-
withdrawSwapper = swapper;
|
|
1032
|
-
}
|
|
1033
|
-
|
|
1034
|
-
const swapInputs: SwapInputs = {
|
|
1035
|
-
inputAmountLamports: toLamports(collTokenSwapIn, collReserve!.stats.decimals).ceil(),
|
|
1036
|
-
inputMint: collTokenMint,
|
|
1037
|
-
outputMint: debtTokenMint,
|
|
1038
|
-
};
|
|
1039
|
-
|
|
1040
|
-
const [swapIxns, lookupTablesAddresses] = await withdrawSwapper(
|
|
1041
|
-
swapInputs.inputAmountLamports.toNumber(),
|
|
1042
|
-
swapInputs.inputMint,
|
|
1043
|
-
swapInputs.outputMint,
|
|
1044
|
-
slippagePct
|
|
1045
|
-
);
|
|
1046
|
-
|
|
1047
|
-
// TODO MARIUS: remove first instruction that is setBudget ixn
|
|
925
|
+
const swapInstructions = removeBudgetAndAtaIxns(swapQuoteIxs.swapIxs, []);
|
|
1048
926
|
|
|
1049
|
-
|
|
1050
|
-
if (strategy?.strategy.strategyLookupTable) {
|
|
1051
|
-
lookupTablesAddresses.push(strategy?.strategy.strategyLookupTable!);
|
|
1052
|
-
} else {
|
|
1053
|
-
console.log('Strategy lookup table not found');
|
|
1054
|
-
}
|
|
1055
|
-
}
|
|
1056
|
-
|
|
1057
|
-
const swapInstructions = removeBudgetAndAtaIxns(swapIxns, []);
|
|
1058
|
-
|
|
1059
|
-
const ixns = [
|
|
927
|
+
return [
|
|
1060
928
|
...budgetIxns,
|
|
1061
|
-
...
|
|
929
|
+
...atasAndCreateIxns.map((x) => x.createAtaIx),
|
|
1062
930
|
...fillWsolAtaIxns,
|
|
1063
931
|
...[flashBorrowIxn],
|
|
1064
932
|
...repayAndWithdrawAction.setupIxs,
|
|
@@ -1070,43 +938,48 @@ export const getWithdrawWithLeverageIxns = async (props: {
|
|
|
1070
938
|
...[flashRepayIxn],
|
|
1071
939
|
...closeWsolAtaIxns,
|
|
1072
940
|
];
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
priceCollToDebt
|
|
1087
|
-
priceDebtToColl
|
|
1088
|
-
slippagePct
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
export async function getAdjustLeverageSwapInputs<QuoteResponse>({
|
|
944
|
+
owner,
|
|
945
|
+
kaminoMarket,
|
|
946
|
+
debtTokenMint,
|
|
947
|
+
collTokenMint,
|
|
948
|
+
obligation,
|
|
949
|
+
depositedLamports,
|
|
950
|
+
borrowedLamports,
|
|
951
|
+
referrer,
|
|
952
|
+
currentSlot,
|
|
953
|
+
targetLeverage,
|
|
954
|
+
priceCollToDebt,
|
|
955
|
+
priceDebtToColl,
|
|
956
|
+
slippagePct,
|
|
957
|
+
budgetAndPriorityFeeIxs,
|
|
958
|
+
kamino,
|
|
959
|
+
scopeFeed,
|
|
960
|
+
quoteBufferBps,
|
|
961
|
+
isKtoken,
|
|
962
|
+
quoter,
|
|
963
|
+
}: AdjustLeverageSwapInputsProps<QuoteResponse>): Promise<{
|
|
1094
964
|
swapInputs: SwapInputs;
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
deposited,
|
|
1098
|
-
borrowed,
|
|
1099
|
-
priceCollToDebt,
|
|
1100
|
-
priceDebtToColl,
|
|
1101
|
-
slippagePct,
|
|
1102
|
-
targetLeverage,
|
|
1103
|
-
kaminoMarket,
|
|
1104
|
-
debtTokenMint,
|
|
1105
|
-
collTokenMint,
|
|
1106
|
-
} = props;
|
|
965
|
+
initialInputs: AdjustLeverageInitialInputs<QuoteResponse>;
|
|
966
|
+
}> {
|
|
1107
967
|
const collReserve = kaminoMarket.getReserveByMint(collTokenMint);
|
|
1108
968
|
const debtReserve = kaminoMarket.getReserveByMint(debtTokenMint);
|
|
1109
|
-
const
|
|
969
|
+
const deposited = fromLamports(depositedLamports, collReserve!.stats.decimals);
|
|
970
|
+
const borrowed = fromLamports(borrowedLamports, debtReserve!.stats.decimals);
|
|
971
|
+
const collIsKtoken = await isKtoken(collTokenMint);
|
|
972
|
+
const strategy = collIsKtoken ? (await kamino!.getStrategyByKTokenMint(collTokenMint))! : undefined;
|
|
973
|
+
|
|
974
|
+
// Getting current flash loan fee
|
|
975
|
+
const currentLeverage = obligation.refreshedStats.leverage;
|
|
976
|
+
const isDepositViaLeverage = targetLeverage.gte(new Decimal(currentLeverage));
|
|
977
|
+
let flashLoanFee;
|
|
978
|
+
if (isDepositViaLeverage) {
|
|
979
|
+
flashLoanFee = collReserve!.getFlashLoanFee() || new Decimal(0);
|
|
980
|
+
} else {
|
|
981
|
+
flashLoanFee = debtReserve!.getFlashLoanFee() || new Decimal(0);
|
|
982
|
+
}
|
|
1110
983
|
|
|
1111
984
|
const { adjustDepositPosition, adjustBorrowPosition } = calcAdjustAmounts({
|
|
1112
985
|
currentDepositPosition: deposited,
|
|
@@ -1115,253 +988,376 @@ export const getAdjustLeverageSwapInputs = (props: {
|
|
|
1115
988
|
priceCollToDebt: priceCollToDebt,
|
|
1116
989
|
flashLoanFee: new Decimal(flashLoanFee),
|
|
1117
990
|
});
|
|
991
|
+
|
|
1118
992
|
const isDeposit = adjustDepositPosition.gte(0) && adjustBorrowPosition.gte(0);
|
|
993
|
+
if (isDepositViaLeverage !== isDeposit) {
|
|
994
|
+
throw new Error('Invalid target leverage');
|
|
995
|
+
}
|
|
1119
996
|
|
|
1120
997
|
if (isDeposit) {
|
|
1121
|
-
const
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
998
|
+
const calcs = await adjustDepositLeverageCalcs(
|
|
999
|
+
kaminoMarket,
|
|
1000
|
+
owner,
|
|
1001
|
+
debtReserve!,
|
|
1002
|
+
adjustDepositPosition,
|
|
1003
|
+
adjustBorrowPosition,
|
|
1004
|
+
priceDebtToColl,
|
|
1005
|
+
flashLoanFee,
|
|
1006
|
+
slippagePct,
|
|
1007
|
+
collIsKtoken
|
|
1008
|
+
);
|
|
1009
|
+
|
|
1010
|
+
// Build the repay & withdraw collateral tx to get the number of accounts
|
|
1011
|
+
const klendIxs = await buildIncreaseLeverageIxns(
|
|
1012
|
+
owner,
|
|
1013
|
+
kaminoMarket,
|
|
1014
|
+
collTokenMint,
|
|
1015
|
+
debtTokenMint,
|
|
1016
|
+
obligation,
|
|
1017
|
+
referrer,
|
|
1018
|
+
currentSlot,
|
|
1019
|
+
calcs,
|
|
1020
|
+
strategy,
|
|
1021
|
+
scopeFeed,
|
|
1022
|
+
collIsKtoken,
|
|
1023
|
+
{
|
|
1024
|
+
preActionIxs: [],
|
|
1025
|
+
swapIxs: [],
|
|
1026
|
+
lookupTables: [],
|
|
1027
|
+
},
|
|
1028
|
+
budgetAndPriorityFeeIxs
|
|
1029
|
+
);
|
|
1030
|
+
|
|
1031
|
+
const uniqueKlendAccounts = uniqueAccounts(klendIxs);
|
|
1032
|
+
|
|
1033
|
+
const swapInputAmount = toLamports(
|
|
1034
|
+
!collIsKtoken ? calcs.borrowAmount : calcs.amountToFlashBorrowDebt,
|
|
1035
|
+
debtReserve!.state.liquidity.mintDecimals.toNumber()
|
|
1036
|
+
).ceil();
|
|
1037
|
+
|
|
1038
|
+
const swapInputsForQuote: SwapInputs = {
|
|
1039
|
+
inputAmountLamports: swapInputAmount.mul(new Decimal(1).add(quoteBufferBps.div(FullBPS))),
|
|
1040
|
+
inputMint: debtTokenMint,
|
|
1041
|
+
outputMint: collTokenMint,
|
|
1042
|
+
amountDebtAtaBalance: new Decimal(0), // Only needed for ktokens swaps
|
|
1043
|
+
};
|
|
1044
|
+
|
|
1045
|
+
const swapQuote = await quoter(swapInputsForQuote, uniqueKlendAccounts);
|
|
1046
|
+
|
|
1047
|
+
const {
|
|
1048
|
+
adjustDepositPosition: adjustDepositPositionQuotePrice,
|
|
1049
|
+
adjustBorrowPosition: adjustBorrowPositionQuotePrice,
|
|
1050
|
+
} = calcAdjustAmounts({
|
|
1051
|
+
currentDepositPosition: deposited,
|
|
1052
|
+
currentBorrowPosition: borrowed,
|
|
1053
|
+
targetLeverage: targetLeverage,
|
|
1054
|
+
priceCollToDebt: new Decimal(1).div(swapQuote.priceAInB),
|
|
1055
|
+
flashLoanFee: new Decimal(flashLoanFee),
|
|
1056
|
+
});
|
|
1057
|
+
|
|
1058
|
+
const calcsQuotePrice = await adjustDepositLeverageCalcs(
|
|
1059
|
+
kaminoMarket,
|
|
1060
|
+
owner,
|
|
1061
|
+
debtReserve!,
|
|
1062
|
+
adjustDepositPositionQuotePrice,
|
|
1063
|
+
adjustBorrowPositionQuotePrice,
|
|
1064
|
+
swapQuote.priceAInB,
|
|
1065
|
+
flashLoanFee,
|
|
1066
|
+
slippagePct,
|
|
1067
|
+
collIsKtoken
|
|
1068
|
+
);
|
|
1069
|
+
|
|
1070
|
+
const swapInputAmountQuotePrice = toLamports(
|
|
1071
|
+
!collIsKtoken ? calcsQuotePrice.borrowAmount : calcsQuotePrice.amountToFlashBorrowDebt,
|
|
1072
|
+
debtReserve!.state.liquidity.mintDecimals.toNumber()
|
|
1073
|
+
).ceil();
|
|
1074
|
+
|
|
1075
|
+
let expectedDebtTokenAtaBalance = new Decimal(0);
|
|
1076
|
+
if (collIsKtoken) {
|
|
1077
|
+
expectedDebtTokenAtaBalance = await getExpectedTokenBalanceAfterBorrow(
|
|
1078
|
+
kaminoMarket.getConnection(),
|
|
1079
|
+
debtTokenMint,
|
|
1080
|
+
owner,
|
|
1081
|
+
toLamports(
|
|
1082
|
+
!collIsKtoken ? calcsQuotePrice.borrowAmount : calcsQuotePrice.amountToFlashBorrowDebt,
|
|
1083
|
+
debtReserve!.stats.decimals
|
|
1084
|
+
).floor(),
|
|
1085
|
+
debtReserve!.state.liquidity.mintDecimals.toNumber()
|
|
1086
|
+
);
|
|
1087
|
+
}
|
|
1125
1088
|
|
|
1126
1089
|
return {
|
|
1127
1090
|
swapInputs: {
|
|
1128
|
-
inputAmountLamports:
|
|
1091
|
+
inputAmountLamports: swapInputAmountQuotePrice,
|
|
1129
1092
|
inputMint: debtTokenMint,
|
|
1130
1093
|
outputMint: collTokenMint,
|
|
1094
|
+
amountDebtAtaBalance: expectedDebtTokenAtaBalance,
|
|
1095
|
+
},
|
|
1096
|
+
initialInputs: {
|
|
1097
|
+
calcs: calcsQuotePrice,
|
|
1098
|
+
swapQuote,
|
|
1099
|
+
currentSlot,
|
|
1100
|
+
collIsKtoken,
|
|
1101
|
+
strategy,
|
|
1102
|
+
obligation: obligation,
|
|
1103
|
+
klendAccounts: uniqueKlendAccounts,
|
|
1104
|
+
isDeposit: isDeposit,
|
|
1131
1105
|
},
|
|
1132
1106
|
};
|
|
1133
1107
|
} else {
|
|
1134
|
-
const
|
|
1135
|
-
|
|
1136
|
-
|
|
1108
|
+
const calcs = adjustWithdrawLeverageCalcs(adjustDepositPosition, adjustBorrowPosition, flashLoanFee, slippagePct);
|
|
1109
|
+
|
|
1110
|
+
const klendIxs = await buildDecreaseLeverageIxns(
|
|
1111
|
+
owner,
|
|
1112
|
+
kaminoMarket,
|
|
1113
|
+
collTokenMint,
|
|
1114
|
+
debtTokenMint,
|
|
1115
|
+
obligation,
|
|
1116
|
+
referrer,
|
|
1117
|
+
currentSlot,
|
|
1118
|
+
calcs,
|
|
1119
|
+
strategy,
|
|
1120
|
+
scopeFeed,
|
|
1121
|
+
collIsKtoken,
|
|
1122
|
+
{
|
|
1123
|
+
preActionIxs: [],
|
|
1124
|
+
swapIxs: [],
|
|
1125
|
+
lookupTables: [],
|
|
1126
|
+
},
|
|
1127
|
+
budgetAndPriorityFeeIxs
|
|
1128
|
+
);
|
|
1129
|
+
|
|
1130
|
+
const uniqueKlendAccounts = uniqueAccounts(klendIxs);
|
|
1131
|
+
|
|
1132
|
+
const swapInputAmount = toLamports(
|
|
1133
|
+
calcs.withdrawAmountWithSlippageAndFlashLoanFee,
|
|
1134
|
+
collReserve!.state.liquidity.mintDecimals.toNumber()
|
|
1135
|
+
).ceil();
|
|
1136
|
+
|
|
1137
|
+
const swapInputsForQuote: SwapInputs = {
|
|
1138
|
+
inputAmountLamports: swapInputAmount.mul(new Decimal(1).add(quoteBufferBps.div(FullBPS))),
|
|
1139
|
+
inputMint: collTokenMint,
|
|
1140
|
+
outputMint: debtTokenMint,
|
|
1141
|
+
amountDebtAtaBalance: new Decimal(0), // Only needed for ktokens deposits
|
|
1142
|
+
};
|
|
1143
|
+
|
|
1144
|
+
const swapQuote = await quoter(swapInputsForQuote, uniqueKlendAccounts);
|
|
1145
|
+
|
|
1146
|
+
const {
|
|
1147
|
+
adjustDepositPosition: adjustDepositPositionQuotePrice,
|
|
1148
|
+
adjustBorrowPosition: adjustBorrowPositionQuotePrice,
|
|
1149
|
+
} = calcAdjustAmounts({
|
|
1150
|
+
currentDepositPosition: deposited,
|
|
1151
|
+
currentBorrowPosition: borrowed,
|
|
1152
|
+
targetLeverage: targetLeverage,
|
|
1153
|
+
priceCollToDebt: swapQuote.priceAInB,
|
|
1154
|
+
flashLoanFee: new Decimal(flashLoanFee),
|
|
1155
|
+
});
|
|
1156
|
+
|
|
1157
|
+
const calcsQuotePrice = adjustWithdrawLeverageCalcs(
|
|
1158
|
+
adjustDepositPositionQuotePrice,
|
|
1159
|
+
adjustBorrowPositionQuotePrice,
|
|
1160
|
+
flashLoanFee,
|
|
1161
|
+
slippagePct
|
|
1162
|
+
);
|
|
1163
|
+
|
|
1164
|
+
const swapInputAmountQuotePrice = toLamports(
|
|
1165
|
+
calcsQuotePrice.withdrawAmountWithSlippageAndFlashLoanFee,
|
|
1166
|
+
collReserve!.state.liquidity.mintDecimals.toNumber()
|
|
1167
|
+
).ceil();
|
|
1137
1168
|
|
|
1138
1169
|
return {
|
|
1139
1170
|
swapInputs: {
|
|
1140
|
-
inputAmountLamports:
|
|
1141
|
-
withdrawAmountWithSlippageAndFlashLoanFee,
|
|
1142
|
-
collReserve!.state.liquidity.mintDecimals.toNumber()
|
|
1143
|
-
).ceil(),
|
|
1171
|
+
inputAmountLamports: swapInputAmountQuotePrice,
|
|
1144
1172
|
inputMint: collTokenMint,
|
|
1145
1173
|
outputMint: debtTokenMint,
|
|
1174
|
+
amountDebtAtaBalance: new Decimal(0), // Only needed for ktokens deposits
|
|
1175
|
+
},
|
|
1176
|
+
initialInputs: {
|
|
1177
|
+
calcs: calcsQuotePrice,
|
|
1178
|
+
swapQuote,
|
|
1179
|
+
currentSlot,
|
|
1180
|
+
collIsKtoken,
|
|
1181
|
+
strategy,
|
|
1182
|
+
obligation,
|
|
1183
|
+
klendAccounts: uniqueKlendAccounts,
|
|
1184
|
+
isDeposit,
|
|
1146
1185
|
},
|
|
1147
1186
|
};
|
|
1148
1187
|
}
|
|
1149
|
-
}
|
|
1150
|
-
|
|
1151
|
-
export
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
}
|
|
1175
|
-
|
|
1176
|
-
connection,
|
|
1177
|
-
budgetAndPriorityFeeIxns,
|
|
1178
|
-
user,
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
export async function getAdjustLeverageIxns<QuoteResponse>({
|
|
1191
|
+
owner,
|
|
1192
|
+
kaminoMarket,
|
|
1193
|
+
debtTokenMint,
|
|
1194
|
+
collTokenMint,
|
|
1195
|
+
obligation,
|
|
1196
|
+
depositedLamports,
|
|
1197
|
+
borrowedLamports,
|
|
1198
|
+
referrer,
|
|
1199
|
+
currentSlot,
|
|
1200
|
+
targetLeverage,
|
|
1201
|
+
priceCollToDebt,
|
|
1202
|
+
priceDebtToColl,
|
|
1203
|
+
slippagePct,
|
|
1204
|
+
budgetAndPriorityFeeIxs,
|
|
1205
|
+
kamino,
|
|
1206
|
+
scopeFeed,
|
|
1207
|
+
quoteBufferBps,
|
|
1208
|
+
priceAinB,
|
|
1209
|
+
isKtoken,
|
|
1210
|
+
quoter,
|
|
1211
|
+
swapper,
|
|
1212
|
+
}: AdjustLeverageProps<QuoteResponse>): Promise<AdjustLeverageIxsResponse<QuoteResponse>> {
|
|
1213
|
+
const { swapInputs, initialInputs } = await getAdjustLeverageSwapInputs({
|
|
1214
|
+
owner,
|
|
1179
1215
|
kaminoMarket,
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
slippagePct,
|
|
1216
|
+
debtTokenMint,
|
|
1217
|
+
collTokenMint,
|
|
1218
|
+
obligation,
|
|
1184
1219
|
depositedLamports,
|
|
1185
1220
|
borrowedLamports,
|
|
1186
|
-
collTokenMint,
|
|
1187
|
-
debtTokenMint,
|
|
1188
|
-
swapper,
|
|
1189
1221
|
referrer,
|
|
1190
|
-
isKtoken,
|
|
1191
|
-
priceAinB,
|
|
1192
|
-
kamino,
|
|
1193
|
-
obligationTypeTagOverride,
|
|
1194
|
-
obligation,
|
|
1195
1222
|
currentSlot,
|
|
1196
|
-
|
|
1223
|
+
targetLeverage,
|
|
1224
|
+
priceCollToDebt,
|
|
1225
|
+
priceDebtToColl,
|
|
1226
|
+
slippagePct,
|
|
1227
|
+
budgetAndPriorityFeeIxs,
|
|
1228
|
+
kamino,
|
|
1197
1229
|
scopeFeed,
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
const deposited = fromLamports(depositedLamports, collReserve!.stats.decimals);
|
|
1204
|
-
const borrowed = fromLamports(borrowedLamports, debtReserve!.stats.decimals);
|
|
1205
|
-
const userObligation = obligation
|
|
1206
|
-
? obligation
|
|
1207
|
-
: (await kaminoMarket.getUserObligationsByTag(obligationTypeTagOverride, user)).filter(
|
|
1208
|
-
(obligation: KaminoObligation) =>
|
|
1209
|
-
obligation.getBorrowByMint(debtReserve!.getLiquidityMint()) !== undefined &&
|
|
1210
|
-
obligation.getDepositByMint(collReserve!.getLiquidityMint()) !== undefined
|
|
1211
|
-
)[0];
|
|
1212
|
-
const currentLeverage = userObligation!.refreshedStats.leverage;
|
|
1213
|
-
const isDepositViaLeverage = targetLeverage.gte(new Decimal(currentLeverage));
|
|
1214
|
-
|
|
1215
|
-
let flashLoanFee;
|
|
1216
|
-
if (isDepositViaLeverage) {
|
|
1217
|
-
flashLoanFee = collReserve!.getFlashLoanFee() || new Decimal(0);
|
|
1218
|
-
} else {
|
|
1219
|
-
flashLoanFee = debtReserve!.getFlashLoanFee() || new Decimal(0);
|
|
1220
|
-
}
|
|
1221
|
-
|
|
1222
|
-
const { adjustDepositPosition, adjustBorrowPosition } = calcAdjustAmounts({
|
|
1223
|
-
currentDepositPosition: deposited,
|
|
1224
|
-
currentBorrowPosition: borrowed,
|
|
1225
|
-
targetLeverage: targetLeverage,
|
|
1226
|
-
priceCollToDebt: priceCollToDebt,
|
|
1227
|
-
flashLoanFee: new Decimal(flashLoanFee),
|
|
1230
|
+
quoteBufferBps,
|
|
1231
|
+
priceAinB,
|
|
1232
|
+
isKtoken,
|
|
1233
|
+
quoter,
|
|
1228
1234
|
});
|
|
1229
1235
|
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1236
|
+
// leverage increased so we need to deposit and borrow more
|
|
1237
|
+
if (initialInputs.isDeposit) {
|
|
1238
|
+
let depositSwapper: SwapQuoteIxsProvider<QuoteResponse>;
|
|
1239
|
+
|
|
1240
|
+
if (initialInputs.collIsKtoken) {
|
|
1241
|
+
if (kamino === undefined) {
|
|
1242
|
+
throw Error('Ktoken use as collateral for leverage without Kamino instance');
|
|
1243
|
+
}
|
|
1244
|
+
depositSwapper = await getTokenToKtokenSwapper(
|
|
1245
|
+
kaminoMarket,
|
|
1246
|
+
kamino,
|
|
1247
|
+
owner,
|
|
1248
|
+
slippagePct,
|
|
1249
|
+
swapper,
|
|
1250
|
+
priceAinB,
|
|
1251
|
+
false
|
|
1252
|
+
);
|
|
1253
|
+
} else {
|
|
1254
|
+
depositSwapper = swapper;
|
|
1255
|
+
}
|
|
1234
1256
|
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1257
|
+
const { swapIxs, lookupTables } = await depositSwapper(
|
|
1258
|
+
swapInputs,
|
|
1259
|
+
initialInputs.klendAccounts,
|
|
1260
|
+
initialInputs.swapQuote
|
|
1261
|
+
);
|
|
1239
1262
|
|
|
1240
|
-
// leverage increased so we need to deposit and borrow more
|
|
1241
|
-
if (isDeposit) {
|
|
1242
|
-
console.log('Increasing leaverage');
|
|
1243
1263
|
// TODO: marius why are we not using both adjustDepositPosition & adjustBorrowPosition
|
|
1244
|
-
const
|
|
1245
|
-
|
|
1246
|
-
budgetAndPriorityFeeIxns,
|
|
1247
|
-
user,
|
|
1264
|
+
const ixs = await buildIncreaseLeverageIxns(
|
|
1265
|
+
owner,
|
|
1248
1266
|
kaminoMarket,
|
|
1249
|
-
depositAmount: adjustDepositPosition,
|
|
1250
1267
|
collTokenMint,
|
|
1251
1268
|
debtTokenMint,
|
|
1252
|
-
|
|
1253
|
-
priceDebtToColl,
|
|
1254
|
-
priceCollToDebt,
|
|
1255
|
-
swapper,
|
|
1269
|
+
obligation,
|
|
1256
1270
|
referrer,
|
|
1257
|
-
isKtoken,
|
|
1258
|
-
priceAinB,
|
|
1259
|
-
kamino,
|
|
1260
|
-
obligationTypeTagOverride,
|
|
1261
|
-
obligation: userObligation,
|
|
1262
1271
|
currentSlot,
|
|
1263
|
-
|
|
1272
|
+
initialInputs.calcs,
|
|
1273
|
+
initialInputs.strategy,
|
|
1264
1274
|
scopeFeed,
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1275
|
+
initialInputs.collIsKtoken,
|
|
1276
|
+
{
|
|
1277
|
+
preActionIxs: [],
|
|
1278
|
+
swapIxs,
|
|
1279
|
+
lookupTables,
|
|
1280
|
+
},
|
|
1281
|
+
budgetAndPriorityFeeIxs
|
|
1282
|
+
);
|
|
1283
|
+
return {
|
|
1284
|
+
ixs,
|
|
1285
|
+
lookupTables,
|
|
1286
|
+
swapInputs,
|
|
1287
|
+
initialInputs,
|
|
1288
|
+
};
|
|
1270
1289
|
} else {
|
|
1271
1290
|
console.log('Decreasing leverage');
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1291
|
+
|
|
1292
|
+
let withdrawSwapper: SwapQuoteIxsProvider<QuoteResponse>;
|
|
1293
|
+
|
|
1294
|
+
if (initialInputs.collIsKtoken) {
|
|
1295
|
+
if (kamino === undefined) {
|
|
1296
|
+
throw Error('Ktoken use as collateral for leverage without Kamino instance');
|
|
1297
|
+
}
|
|
1298
|
+
withdrawSwapper = await getKtokenToTokenSwapper(kaminoMarket, kamino, owner, swapper);
|
|
1299
|
+
} else {
|
|
1300
|
+
withdrawSwapper = swapper;
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
// 5. Get swap ixns
|
|
1304
|
+
const { swapIxs, lookupTables } = await withdrawSwapper(
|
|
1305
|
+
swapInputs,
|
|
1306
|
+
initialInputs.klendAccounts,
|
|
1307
|
+
initialInputs.swapQuote
|
|
1308
|
+
);
|
|
1309
|
+
|
|
1310
|
+
const ixs = await buildDecreaseLeverageIxns(
|
|
1311
|
+
owner,
|
|
1276
1312
|
kaminoMarket,
|
|
1277
|
-
withdrawAmount: Decimal.abs(adjustDepositPosition),
|
|
1278
|
-
repayAmount: Decimal.abs(adjustBorrowPosition),
|
|
1279
1313
|
collTokenMint,
|
|
1280
1314
|
debtTokenMint,
|
|
1281
|
-
|
|
1282
|
-
swapper,
|
|
1315
|
+
obligation,
|
|
1283
1316
|
referrer,
|
|
1284
|
-
isKtoken,
|
|
1285
|
-
kamino,
|
|
1286
|
-
obligationTypeTagOverride,
|
|
1287
|
-
obligation: userObligation,
|
|
1288
1317
|
currentSlot,
|
|
1289
|
-
|
|
1318
|
+
initialInputs.calcs,
|
|
1319
|
+
initialInputs.strategy,
|
|
1290
1320
|
scopeFeed,
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1321
|
+
initialInputs.collIsKtoken,
|
|
1322
|
+
{
|
|
1323
|
+
preActionIxs: [],
|
|
1324
|
+
swapIxs,
|
|
1325
|
+
lookupTables,
|
|
1326
|
+
},
|
|
1327
|
+
budgetAndPriorityFeeIxs
|
|
1328
|
+
);
|
|
1297
1329
|
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
}
|
|
1330
|
+
return {
|
|
1331
|
+
ixs,
|
|
1332
|
+
lookupTables,
|
|
1333
|
+
swapInputs,
|
|
1334
|
+
initialInputs,
|
|
1335
|
+
};
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1305
1338
|
|
|
1306
1339
|
/**
|
|
1307
1340
|
* Deposit and borrow tokens if leverage increased
|
|
1308
1341
|
*/
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
kamino: Kamino | undefined;
|
|
1325
|
-
obligationTypeTagOverride: ObligationTypeTag;
|
|
1326
|
-
obligation: KaminoObligation | null;
|
|
1327
|
-
currentSlot: number;
|
|
1328
|
-
getTotalKlendAccountsOnly: boolean;
|
|
1329
|
-
scopeFeed: string | undefined;
|
|
1330
|
-
}) => {
|
|
1331
|
-
const {
|
|
1332
|
-
connection,
|
|
1333
|
-
budgetAndPriorityFeeIxns,
|
|
1334
|
-
user,
|
|
1335
|
-
kaminoMarket,
|
|
1336
|
-
depositAmount,
|
|
1337
|
-
collTokenMint,
|
|
1338
|
-
debtTokenMint,
|
|
1339
|
-
slippagePct,
|
|
1340
|
-
priceDebtToColl,
|
|
1341
|
-
priceCollToDebt,
|
|
1342
|
-
swapper,
|
|
1343
|
-
referrer,
|
|
1344
|
-
isKtoken,
|
|
1345
|
-
priceAinB,
|
|
1346
|
-
kamino,
|
|
1347
|
-
obligationTypeTagOverride = 1,
|
|
1348
|
-
obligation,
|
|
1349
|
-
currentSlot,
|
|
1350
|
-
getTotalKlendAccountsOnly,
|
|
1351
|
-
scopeFeed,
|
|
1352
|
-
} = props;
|
|
1342
|
+
async function buildIncreaseLeverageIxns(
|
|
1343
|
+
owner: PublicKey,
|
|
1344
|
+
kaminoMarket: KaminoMarket,
|
|
1345
|
+
collTokenMint: PublicKey,
|
|
1346
|
+
debtTokenMint: PublicKey,
|
|
1347
|
+
obligation: KaminoObligation,
|
|
1348
|
+
referrer: PublicKey,
|
|
1349
|
+
currentSlot: number,
|
|
1350
|
+
calcs: AdjustLeverageCalcsResult,
|
|
1351
|
+
strategy: StrategyWithAddress | undefined,
|
|
1352
|
+
scopeFeed: string | undefined,
|
|
1353
|
+
collIsKtoken: boolean,
|
|
1354
|
+
swapQuoteIxs: SwapQuoteIxs,
|
|
1355
|
+
budgetAndPriorityFeeIxns: TransactionInstruction[] | undefined
|
|
1356
|
+
): Promise<TransactionInstruction[]> {
|
|
1353
1357
|
const collReserve = kaminoMarket.getReserveByMint(collTokenMint);
|
|
1354
1358
|
const debtReserve = kaminoMarket.getReserveByMint(debtTokenMint);
|
|
1355
|
-
const
|
|
1356
|
-
|
|
1357
|
-
const flashLoanFee = collReserve!.getFlashLoanFee() || new Decimal(0);
|
|
1358
|
-
|
|
1359
|
-
if (!priceDebtToColl || !priceCollToDebt) {
|
|
1360
|
-
throw new Error('Price is not loaded. Please, reload the page and try again');
|
|
1361
|
-
}
|
|
1362
|
-
|
|
1363
|
-
// TODO: why are we recalculating here again
|
|
1364
|
-
const strategy = collIsKtoken ? await kamino?.getStrategyByKTokenMint(collTokenMint) : undefined;
|
|
1359
|
+
const debtTokenAta = getAssociatedTokenAddressSync(debtTokenMint, owner);
|
|
1360
|
+
const collTokenAta = getAssociatedTokenAddressSync(collTokenMint, owner);
|
|
1365
1361
|
|
|
1366
1362
|
// 1. Create atas & budget txns
|
|
1367
1363
|
const budgetIxns = budgetAndPriorityFeeIxns || getComputeBudgetAndPriorityFeeIxns(3000000);
|
|
@@ -1412,28 +1408,17 @@ export const getIncreaseLeverageIxns = async (props: {
|
|
|
1412
1408
|
];
|
|
1413
1409
|
}
|
|
1414
1410
|
|
|
1415
|
-
const
|
|
1416
|
-
atas: [collTokenAta, debtTokenAta],
|
|
1417
|
-
createAtaIxs,
|
|
1418
|
-
} = await getAtasWithCreateIxnsIfMissing(connection, user, mintsToCreateAtas);
|
|
1411
|
+
const atasAndCreateIxns = createAtasIdempotent(owner, mintsToCreateAtas);
|
|
1419
1412
|
|
|
1420
1413
|
// 2. Create borrow flash loan instruction
|
|
1421
|
-
|
|
1422
|
-
// used if coll is Ktoken and we borrow debt token instead
|
|
1423
|
-
const amountToFashBorrowDebt = depositAmount
|
|
1424
|
-
.div(priceDebtToColl)
|
|
1425
|
-
.mul(new Decimal(1 + slippagePct / 100))
|
|
1426
|
-
.toDecimalPlaces(debtReserve!.stats.decimals, Decimal.ROUND_UP);
|
|
1427
|
-
// .toDecimalPlaces(debtReserve?.state.liquidity.mintDecimals.toNumber());
|
|
1428
|
-
|
|
1429
1414
|
const { flashBorrowIxn, flashRepayIxn } = getFlashLoanInstructions({
|
|
1430
|
-
borrowIxnIndex: budgetIxns.length +
|
|
1431
|
-
walletPublicKey:
|
|
1415
|
+
borrowIxnIndex: budgetIxns.length + atasAndCreateIxns.length, // TODO: how about user metadata ixns
|
|
1416
|
+
walletPublicKey: owner,
|
|
1432
1417
|
lendingMarketAuthority: kaminoMarket.getLendingMarketAuthority(),
|
|
1433
1418
|
lendingMarketAddress: kaminoMarket.getAddress(),
|
|
1434
1419
|
reserve: !collIsKtoken ? collReserve! : debtReserve!,
|
|
1435
1420
|
amountLamports: toLamports(
|
|
1436
|
-
!collIsKtoken ?
|
|
1421
|
+
!collIsKtoken ? calcs.adjustDepositPosition : calcs.amountToFlashBorrowDebt,
|
|
1437
1422
|
!collIsKtoken ? collReserve!.stats.decimals : debtReserve!.stats.decimals
|
|
1438
1423
|
),
|
|
1439
1424
|
destinationAta: !collIsKtoken ? collTokenAta : debtTokenAta,
|
|
@@ -1442,24 +1427,12 @@ export const getIncreaseLeverageIxns = async (props: {
|
|
|
1442
1427
|
programId: kaminoMarket.programId,
|
|
1443
1428
|
});
|
|
1444
1429
|
|
|
1445
|
-
// 3. Deposit initial tokens + borrowed tokens into reserve
|
|
1446
|
-
let obligationType: ObligationType;
|
|
1447
|
-
if (obligationTypeTagOverride === ObligationTypeTag.Multiply) {
|
|
1448
|
-
// multiply
|
|
1449
|
-
obligationType = new MultiplyObligation(collTokenMint, debtTokenMint, kaminoMarket.programId);
|
|
1450
|
-
} else if (obligationTypeTagOverride === ObligationTypeTag.Leverage) {
|
|
1451
|
-
// leverage
|
|
1452
|
-
obligationType = new LeverageObligation(collTokenMint, debtTokenMint, kaminoMarket.programId);
|
|
1453
|
-
} else {
|
|
1454
|
-
throw Error('Obligation type tag not supported for leverage, please use 1 - multiply or 3 - leverage');
|
|
1455
|
-
}
|
|
1456
|
-
|
|
1457
1430
|
const depositAction = await KaminoAction.buildDepositTxns(
|
|
1458
1431
|
kaminoMarket,
|
|
1459
|
-
toLamports(
|
|
1432
|
+
toLamports(calcs.adjustDepositPosition, collReserve!.stats.decimals).floor().toString(),
|
|
1460
1433
|
collTokenMint,
|
|
1461
|
-
|
|
1462
|
-
obligation
|
|
1434
|
+
owner,
|
|
1435
|
+
obligation,
|
|
1463
1436
|
0,
|
|
1464
1437
|
false,
|
|
1465
1438
|
false,
|
|
@@ -1469,21 +1442,13 @@ export const getIncreaseLeverageIxns = async (props: {
|
|
|
1469
1442
|
{ includeScopeRefresh: true, scopeFeed: scopeFeed! }
|
|
1470
1443
|
);
|
|
1471
1444
|
|
|
1472
|
-
// 4.
|
|
1473
|
-
const borrowAmount = depositAmount
|
|
1474
|
-
.mul(new Decimal(1).plus(flashLoanFee))
|
|
1475
|
-
.mul(new Decimal(1 + slippagePct / 100))
|
|
1476
|
-
.div(priceDebtToColl);
|
|
1477
|
-
|
|
1478
|
-
const _collTokenExpectedSwapOut = depositAmount.mul(new Decimal(1).plus(flashLoanFee));
|
|
1479
|
-
|
|
1480
|
-
// 5. Borrow tokens in borrow token reserve that will be swapped to repay flash loan
|
|
1445
|
+
// 4. Borrow tokens in borrow token reserve that will be swapped to repay flash loan
|
|
1481
1446
|
const borrowAction = await KaminoAction.buildBorrowTxns(
|
|
1482
1447
|
kaminoMarket,
|
|
1483
|
-
toLamports(borrowAmount, debtReserve!.stats.decimals).ceil().toString(),
|
|
1448
|
+
toLamports(calcs.borrowAmount, debtReserve!.stats.decimals).ceil().toString(),
|
|
1484
1449
|
debtTokenMint,
|
|
1485
|
-
|
|
1486
|
-
obligation
|
|
1450
|
+
owner,
|
|
1451
|
+
obligation,
|
|
1487
1452
|
0,
|
|
1488
1453
|
false,
|
|
1489
1454
|
false,
|
|
@@ -1493,84 +1458,12 @@ export const getIncreaseLeverageIxns = async (props: {
|
|
|
1493
1458
|
{ includeScopeRefresh: true, scopeFeed: scopeFeed! }
|
|
1494
1459
|
);
|
|
1495
1460
|
|
|
1496
|
-
const
|
|
1497
|
-
...budgetIxns,
|
|
1498
|
-
...createAtaIxs,
|
|
1499
|
-
...[flashBorrowIxn],
|
|
1500
|
-
...depositAction.setupIxs,
|
|
1501
|
-
...depositAction.lendingIxs,
|
|
1502
|
-
...depositAction.cleanupIxs,
|
|
1503
|
-
...borrowAction.setupIxs,
|
|
1504
|
-
...borrowAction.lendingIxs,
|
|
1505
|
-
...borrowAction.cleanupIxs,
|
|
1506
|
-
...[flashRepayIxn],
|
|
1507
|
-
];
|
|
1508
|
-
|
|
1509
|
-
const uniqueAccounts = new PublicKeySet<PublicKey>([]);
|
|
1510
|
-
klendIxns.forEach((ixn) => {
|
|
1511
|
-
ixn.keys.forEach((key) => {
|
|
1512
|
-
uniqueAccounts.add(key.pubkey);
|
|
1513
|
-
});
|
|
1514
|
-
});
|
|
1515
|
-
const totalKlendAccounts = uniqueAccounts.toArray().length;
|
|
1516
|
-
|
|
1517
|
-
// return early to avoid extra swapper calls
|
|
1518
|
-
if (getTotalKlendAccountsOnly) {
|
|
1519
|
-
return {
|
|
1520
|
-
ixns: [],
|
|
1521
|
-
lookupTablesAddresses: [],
|
|
1522
|
-
swapInputs: {
|
|
1523
|
-
inputAmountLamports: new Decimal('0'),
|
|
1524
|
-
inputMint: PublicKey.default,
|
|
1525
|
-
outputMint: PublicKey.default,
|
|
1526
|
-
},
|
|
1527
|
-
totalKlendAccounts: totalKlendAccounts,
|
|
1528
|
-
};
|
|
1529
|
-
}
|
|
1530
|
-
|
|
1531
|
-
let depositSwapper: SwapIxnsProvider;
|
|
1532
|
-
let expectedDebtTokenAtaBalance = new Decimal(0);
|
|
1533
|
-
|
|
1534
|
-
if (collIsKtoken) {
|
|
1535
|
-
if (kamino === undefined) {
|
|
1536
|
-
throw Error('Ktoken use as collateral for leverage without Kamino instance');
|
|
1537
|
-
}
|
|
1538
|
-
depositSwapper = await getTokenToKtokenSwapper(connection, kaminoMarket, kamino, user, swapper, priceAinB, false);
|
|
1539
|
-
|
|
1540
|
-
expectedDebtTokenAtaBalance = await getExpectedTokenBalanceAfterBorrow(
|
|
1541
|
-
connection,
|
|
1542
|
-
debtTokenMint,
|
|
1543
|
-
user,
|
|
1544
|
-
toLamports(!collIsKtoken ? borrowAmount : amountToFashBorrowDebt, debtReserve!.stats.decimals).floor(),
|
|
1545
|
-
debtReserve!.state.liquidity.mintDecimals.toNumber()
|
|
1546
|
-
);
|
|
1547
|
-
} else {
|
|
1548
|
-
depositSwapper = swapper;
|
|
1549
|
-
}
|
|
1550
|
-
|
|
1551
|
-
const swapInputs: SwapInputs = {
|
|
1552
|
-
inputAmountLamports: toLamports(
|
|
1553
|
-
!collIsKtoken ? borrowAmount : amountToFashBorrowDebt,
|
|
1554
|
-
debtReserve!.stats.decimals
|
|
1555
|
-
).ceil(),
|
|
1556
|
-
inputMint: debtTokenMint,
|
|
1557
|
-
outputMint: collTokenMint,
|
|
1558
|
-
};
|
|
1559
|
-
|
|
1560
|
-
const [swapIxns, lookupTablesAddresses] = await depositSwapper(
|
|
1561
|
-
swapInputs.inputAmountLamports.toNumber(),
|
|
1562
|
-
swapInputs.inputMint,
|
|
1563
|
-
swapInputs.outputMint,
|
|
1564
|
-
slippagePct,
|
|
1565
|
-
expectedDebtTokenAtaBalance
|
|
1566
|
-
);
|
|
1567
|
-
|
|
1568
|
-
const swapInstructions = removeBudgetAndAtaIxns(swapIxns, []);
|
|
1461
|
+
const swapInstructions = removeBudgetAndAtaIxns(swapQuoteIxs.swapIxs, []);
|
|
1569
1462
|
|
|
1570
|
-
const
|
|
1463
|
+
const ixs = !collIsKtoken
|
|
1571
1464
|
? [
|
|
1572
1465
|
...budgetIxns,
|
|
1573
|
-
...
|
|
1466
|
+
...atasAndCreateIxns.map((x) => x.createAtaIx),
|
|
1574
1467
|
...[flashBorrowIxn],
|
|
1575
1468
|
...depositAction.setupIxs,
|
|
1576
1469
|
...depositAction.lendingIxs,
|
|
@@ -1583,7 +1476,7 @@ export const getIncreaseLeverageIxns = async (props: {
|
|
|
1583
1476
|
]
|
|
1584
1477
|
: [
|
|
1585
1478
|
...budgetIxns,
|
|
1586
|
-
...
|
|
1479
|
+
...atasAndCreateIxns.map((x) => x.createAtaIx),
|
|
1587
1480
|
...[flashBorrowIxn],
|
|
1588
1481
|
...swapInstructions,
|
|
1589
1482
|
...depositAction.setupIxs,
|
|
@@ -1595,81 +1488,30 @@ export const getIncreaseLeverageIxns = async (props: {
|
|
|
1595
1488
|
...[flashRepayIxn],
|
|
1596
1489
|
];
|
|
1597
1490
|
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
});
|
|
1601
|
-
|
|
1602
|
-
// Create and send transaction
|
|
1603
|
-
if (collIsKtoken) {
|
|
1604
|
-
if (strategy?.strategy.strategyLookupTable) {
|
|
1605
|
-
lookupTablesAddresses.push(strategy?.strategy.strategyLookupTable!);
|
|
1606
|
-
} else {
|
|
1607
|
-
console.log('Strategy lookup table not found');
|
|
1608
|
-
}
|
|
1609
|
-
}
|
|
1610
|
-
return {
|
|
1611
|
-
ixns,
|
|
1612
|
-
lookupTablesAddresses,
|
|
1613
|
-
swapInputs,
|
|
1614
|
-
totalKlendAccounts,
|
|
1615
|
-
};
|
|
1616
|
-
};
|
|
1491
|
+
return ixs;
|
|
1492
|
+
}
|
|
1617
1493
|
|
|
1618
1494
|
/**
|
|
1619
1495
|
* Withdraw and repay tokens if leverage decreased
|
|
1620
1496
|
*/
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
obligation: KaminoObligation | null;
|
|
1637
|
-
currentSlot: number;
|
|
1638
|
-
getTotalKlendAccountsOnly: boolean;
|
|
1639
|
-
scopeFeed: string | undefined;
|
|
1640
|
-
}) => {
|
|
1641
|
-
const {
|
|
1642
|
-
connection,
|
|
1643
|
-
budgetAndPriorityFeeIxns,
|
|
1644
|
-
user,
|
|
1645
|
-
kaminoMarket,
|
|
1646
|
-
withdrawAmount,
|
|
1647
|
-
repayAmount,
|
|
1648
|
-
collTokenMint,
|
|
1649
|
-
debtTokenMint,
|
|
1650
|
-
slippagePct,
|
|
1651
|
-
swapper,
|
|
1652
|
-
referrer,
|
|
1653
|
-
isKtoken,
|
|
1654
|
-
kamino,
|
|
1655
|
-
obligationTypeTagOverride = 1,
|
|
1656
|
-
obligation,
|
|
1657
|
-
currentSlot,
|
|
1658
|
-
getTotalKlendAccountsOnly,
|
|
1659
|
-
scopeFeed,
|
|
1660
|
-
} = props;
|
|
1661
|
-
|
|
1662
|
-
console.log(
|
|
1663
|
-
'getDecreaseLeverageIxns',
|
|
1664
|
-
toJson({ withdrawAmount, repayAmount, collTokenMint, debtTokenMint, slippagePct })
|
|
1665
|
-
);
|
|
1497
|
+
async function buildDecreaseLeverageIxns(
|
|
1498
|
+
owner: PublicKey,
|
|
1499
|
+
kaminoMarket: KaminoMarket,
|
|
1500
|
+
collTokenMint: PublicKey,
|
|
1501
|
+
debtTokenMint: PublicKey,
|
|
1502
|
+
obligation: KaminoObligation,
|
|
1503
|
+
referrer: PublicKey,
|
|
1504
|
+
currentSlot: number,
|
|
1505
|
+
calcs: AdjustLeverageCalcsResult,
|
|
1506
|
+
strategy: StrategyWithAddress | undefined,
|
|
1507
|
+
scopeFeed: string | undefined,
|
|
1508
|
+
collIsKtoken: boolean,
|
|
1509
|
+
swapQuoteIxs: SwapQuoteIxs,
|
|
1510
|
+
budgetAndPriorityFeeIxns: TransactionInstruction[] | undefined
|
|
1511
|
+
): Promise<TransactionInstruction[]> {
|
|
1666
1512
|
const collReserve = kaminoMarket.getReserveByMint(collTokenMint);
|
|
1667
1513
|
const debtReserve = kaminoMarket.getReserveByMint(debtTokenMint);
|
|
1668
|
-
const
|
|
1669
|
-
|
|
1670
|
-
const flashLoanFee = debtReserve?.getFlashLoanFee() || new Decimal(0);
|
|
1671
|
-
|
|
1672
|
-
const strategy = collIsKtoken ? await kamino?.getStrategyByKTokenMint(collTokenMint) : undefined;
|
|
1514
|
+
const debtTokenAta = getAssociatedTokenAddressSync(debtTokenMint, owner);
|
|
1673
1515
|
|
|
1674
1516
|
// 1. Create atas & budget txns
|
|
1675
1517
|
const budgetIxns = budgetAndPriorityFeeIxns || getComputeBudgetAndPriorityFeeIxns(3000000);
|
|
@@ -1719,36 +1561,32 @@ export const getDecreaseLeverageIxns = async (props: {
|
|
|
1719
1561
|
},
|
|
1720
1562
|
];
|
|
1721
1563
|
}
|
|
1722
|
-
const
|
|
1723
|
-
atas: [, debtTokenAta],
|
|
1724
|
-
createAtaIxs,
|
|
1725
|
-
} = await getAtasWithCreateIxnsIfMissing(connection, user, mintsToCreateAtas);
|
|
1564
|
+
const atasAndCreateIxns = createAtasIdempotent(owner, mintsToCreateAtas);
|
|
1726
1565
|
|
|
1727
1566
|
// TODO: Mihai/Marius check if we can improve this logic and not convert any SOL
|
|
1728
1567
|
// This is here so that we have enough wsol to repay in case the kAB swapped to sol after estimates is not enough
|
|
1729
1568
|
const closeWsolAtaIxns: TransactionInstruction[] = [];
|
|
1730
|
-
if (debtTokenMint.equals(WRAPPED_SOL_MINT)) {
|
|
1731
|
-
const wsolAta = await getAssociatedTokenAddress(WRAPPED_SOL_MINT, user, false);
|
|
1732
|
-
closeWsolAtaIxns.push(createCloseAccountInstruction(wsolAta, user, user, [], TOKEN_PROGRAM_ID));
|
|
1733
|
-
}
|
|
1734
|
-
|
|
1735
1569
|
const fillWsolAtaIxns: TransactionInstruction[] = [];
|
|
1736
1570
|
if (debtTokenMint.equals(WRAPPED_SOL_MINT)) {
|
|
1737
|
-
const
|
|
1571
|
+
const wsolAta = getAssociatedTokenAddress(WRAPPED_SOL_MINT, owner, false);
|
|
1572
|
+
|
|
1573
|
+
closeWsolAtaIxns.push(createCloseAccountInstruction(wsolAta, owner, owner, [], TOKEN_PROGRAM_ID));
|
|
1574
|
+
|
|
1575
|
+
const halfSolBalance = (await kaminoMarket.getConnection().getBalance(owner)) / LAMPORTS_PER_SOL / 2;
|
|
1738
1576
|
const balanceToWrap = halfSolBalance < 0.1 ? halfSolBalance : 0.1;
|
|
1739
1577
|
fillWsolAtaIxns.push(
|
|
1740
|
-
...getDepositWsolIxns(
|
|
1578
|
+
...getDepositWsolIxns(owner, wsolAta, toLamports(balanceToWrap, debtReserve!.stats.decimals).ceil())
|
|
1741
1579
|
);
|
|
1742
1580
|
}
|
|
1743
1581
|
|
|
1744
1582
|
// 3. Flash borrow & repay amount to repay (debt)
|
|
1745
1583
|
const { flashBorrowIxn, flashRepayIxn } = getFlashLoanInstructions({
|
|
1746
|
-
borrowIxnIndex: budgetIxns.length +
|
|
1747
|
-
walletPublicKey:
|
|
1584
|
+
borrowIxnIndex: budgetIxns.length + atasAndCreateIxns.length + fillWsolAtaIxns.length,
|
|
1585
|
+
walletPublicKey: owner,
|
|
1748
1586
|
lendingMarketAuthority: kaminoMarket.getLendingMarketAuthority(),
|
|
1749
1587
|
lendingMarketAddress: kaminoMarket.getAddress(),
|
|
1750
1588
|
reserve: debtReserve!,
|
|
1751
|
-
amountLamports: toLamports(
|
|
1589
|
+
amountLamports: toLamports(Decimal.abs(calcs.adjustBorrowPosition), debtReserve!.stats.decimals),
|
|
1752
1590
|
destinationAta: debtTokenAta,
|
|
1753
1591
|
referrerAccount: kaminoMarket.programId,
|
|
1754
1592
|
referrerTokenState: kaminoMarket.programId,
|
|
@@ -1756,25 +1594,13 @@ export const getDecreaseLeverageIxns = async (props: {
|
|
|
1756
1594
|
});
|
|
1757
1595
|
|
|
1758
1596
|
// 4. Actually do the repay of the flash borrowed amounts
|
|
1759
|
-
let obligationType: ObligationType;
|
|
1760
|
-
if (obligationTypeTagOverride === ObligationTypeTag.Multiply) {
|
|
1761
|
-
// multiply
|
|
1762
|
-
obligationType = new MultiplyObligation(collTokenMint, debtTokenMint, kaminoMarket.programId);
|
|
1763
|
-
} else if (obligationTypeTagOverride === ObligationTypeTag.Leverage) {
|
|
1764
|
-
// leverage
|
|
1765
|
-
obligationType = new LeverageObligation(collTokenMint, debtTokenMint, kaminoMarket.programId);
|
|
1766
|
-
} else {
|
|
1767
|
-
throw Error('Obligation type tag not supported for leverage, please use 1 - multiply or 3 - leverage');
|
|
1768
|
-
}
|
|
1769
|
-
|
|
1770
1597
|
const scopeRefresh = scopeFeed ? { includeScopeRefresh: true, scopeFeed: scopeFeed } : undefined;
|
|
1771
|
-
|
|
1772
1598
|
const repayAction = await KaminoAction.buildRepayTxns(
|
|
1773
1599
|
kaminoMarket,
|
|
1774
|
-
toLamports(
|
|
1600
|
+
toLamports(Decimal.abs(calcs.adjustBorrowPosition), debtReserve!.stats.decimals).floor().toString(),
|
|
1775
1601
|
debtTokenMint,
|
|
1776
|
-
|
|
1777
|
-
obligation
|
|
1602
|
+
owner,
|
|
1603
|
+
obligation,
|
|
1778
1604
|
currentSlot,
|
|
1779
1605
|
undefined,
|
|
1780
1606
|
0,
|
|
@@ -1786,16 +1612,12 @@ export const getDecreaseLeverageIxns = async (props: {
|
|
|
1786
1612
|
);
|
|
1787
1613
|
|
|
1788
1614
|
// 6. Withdraw collateral (a little bit more to be able to pay for the slippage on swap)
|
|
1789
|
-
const withdrawAmountWithSlippageAndFlashLoanFee = withdrawAmount
|
|
1790
|
-
.mul(new Decimal(1).plus(flashLoanFee))
|
|
1791
|
-
.mul(1 + slippagePct / 100);
|
|
1792
|
-
|
|
1793
1615
|
const withdrawAction = await KaminoAction.buildWithdrawTxns(
|
|
1794
1616
|
kaminoMarket,
|
|
1795
|
-
toLamports(withdrawAmountWithSlippageAndFlashLoanFee, collReserve!.stats.decimals).ceil().toString(),
|
|
1617
|
+
toLamports(calcs.withdrawAmountWithSlippageAndFlashLoanFee, collReserve!.stats.decimals).ceil().toString(),
|
|
1796
1618
|
collTokenMint,
|
|
1797
|
-
|
|
1798
|
-
obligation
|
|
1619
|
+
owner,
|
|
1620
|
+
obligation,
|
|
1799
1621
|
0,
|
|
1800
1622
|
false,
|
|
1801
1623
|
false,
|
|
@@ -1805,68 +1627,11 @@ export const getDecreaseLeverageIxns = async (props: {
|
|
|
1805
1627
|
{ includeScopeRefresh: true, scopeFeed: scopeFeed! }
|
|
1806
1628
|
);
|
|
1807
1629
|
|
|
1808
|
-
const
|
|
1809
|
-
...budgetIxns,
|
|
1810
|
-
...createAtaIxs,
|
|
1811
|
-
...fillWsolAtaIxns,
|
|
1812
|
-
...[flashBorrowIxn],
|
|
1813
|
-
...repayAction.setupIxs,
|
|
1814
|
-
...repayAction.lendingIxs,
|
|
1815
|
-
...repayAction.cleanupIxs,
|
|
1816
|
-
...withdrawAction.setupIxs,
|
|
1817
|
-
...withdrawAction.lendingIxs,
|
|
1818
|
-
...withdrawAction.cleanupIxs,
|
|
1819
|
-
...[flashRepayIxn],
|
|
1820
|
-
...closeWsolAtaIxns,
|
|
1821
|
-
];
|
|
1822
|
-
|
|
1823
|
-
const uniqueAccs = uniqueAccounts(klendIxns);
|
|
1824
|
-
const totalKlendAccounts = uniqueAccs.length;
|
|
1825
|
-
|
|
1826
|
-
// return early to avoid extra swapper calls
|
|
1827
|
-
if (getTotalKlendAccountsOnly) {
|
|
1828
|
-
return {
|
|
1829
|
-
ixns: [],
|
|
1830
|
-
lookupTablesAddresses: [],
|
|
1831
|
-
swapInputs: {
|
|
1832
|
-
inputAmountLamports: new Decimal('0'),
|
|
1833
|
-
inputMint: PublicKey.default,
|
|
1834
|
-
outputMint: PublicKey.default,
|
|
1835
|
-
},
|
|
1836
|
-
totalKlendAccounts: totalKlendAccounts,
|
|
1837
|
-
};
|
|
1838
|
-
}
|
|
1839
|
-
|
|
1840
|
-
let withdrawSwapper: SwapIxnsProvider;
|
|
1841
|
-
|
|
1842
|
-
if (collIsKtoken) {
|
|
1843
|
-
if (kamino === undefined) {
|
|
1844
|
-
throw Error('Ktoken use as collateral for leverage without Kamino instance');
|
|
1845
|
-
}
|
|
1846
|
-
withdrawSwapper = await getKtokenToTokenSwapper(kaminoMarket, kamino, user, swapper);
|
|
1847
|
-
} else {
|
|
1848
|
-
withdrawSwapper = swapper;
|
|
1849
|
-
}
|
|
1850
|
-
|
|
1851
|
-
const swapInputs: SwapInputs = {
|
|
1852
|
-
inputAmountLamports: toLamports(withdrawAmountWithSlippageAndFlashLoanFee, collReserve!.stats.decimals).ceil(),
|
|
1853
|
-
inputMint: collTokenMint,
|
|
1854
|
-
outputMint: debtTokenMint,
|
|
1855
|
-
};
|
|
1856
|
-
|
|
1857
|
-
// 5. Get swap ixns
|
|
1858
|
-
const [swapIxns, lookupTablesAddresses] = await withdrawSwapper(
|
|
1859
|
-
swapInputs.inputAmountLamports.toNumber(),
|
|
1860
|
-
swapInputs.inputMint,
|
|
1861
|
-
swapInputs.outputMint,
|
|
1862
|
-
slippagePct
|
|
1863
|
-
);
|
|
1864
|
-
|
|
1865
|
-
const swapInstructions = removeBudgetAndAtaIxns(swapIxns, []);
|
|
1630
|
+
const swapInstructions = removeBudgetAndAtaIxns(swapQuoteIxs.swapIxs, []);
|
|
1866
1631
|
|
|
1867
1632
|
const ixns = [
|
|
1868
1633
|
...budgetIxns,
|
|
1869
|
-
...
|
|
1634
|
+
...atasAndCreateIxns.map((x) => x.createAtaIx),
|
|
1870
1635
|
...fillWsolAtaIxns,
|
|
1871
1636
|
...[flashBorrowIxn],
|
|
1872
1637
|
...repayAction.setupIxs,
|
|
@@ -1880,22 +1645,5 @@ export const getDecreaseLeverageIxns = async (props: {
|
|
|
1880
1645
|
...closeWsolAtaIxns,
|
|
1881
1646
|
];
|
|
1882
1647
|
|
|
1883
|
-
ixns
|
|
1884
|
-
|
|
1885
|
-
});
|
|
1886
|
-
|
|
1887
|
-
if (collIsKtoken) {
|
|
1888
|
-
if (strategy?.strategy.strategyLookupTable) {
|
|
1889
|
-
lookupTablesAddresses.push(strategy?.strategy.strategyLookupTable!);
|
|
1890
|
-
} else {
|
|
1891
|
-
console.log('Strategy lookup table not found');
|
|
1892
|
-
}
|
|
1893
|
-
}
|
|
1894
|
-
// Create and send transaction
|
|
1895
|
-
return {
|
|
1896
|
-
ixns,
|
|
1897
|
-
lookupTablesAddresses,
|
|
1898
|
-
swapInputs,
|
|
1899
|
-
totalKlendAccounts,
|
|
1900
|
-
};
|
|
1901
|
-
};
|
|
1648
|
+
return ixns;
|
|
1649
|
+
}
|