@kamino-finance/klend-sdk 7.1.2 → 7.1.3

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.
@@ -1,7 +1,6 @@
1
1
  import { Account, Address, Instruction, Option, Slot, TransactionSigner } from '@solana/kit';
2
2
  import Decimal from 'decimal.js';
3
3
  import { KaminoMarket, KaminoObligation } from '../classes';
4
- import { Kamino, StrategyWithAddress } from '@kamino-finance/kliquidity-sdk';
5
4
  import { ObligationType, ObligationTypeTag, ScopePriceRefreshConfig } from '../utils';
6
5
  import { AddressLookupTable } from '@solana-program/address-lookup-table';
7
6
 
@@ -30,8 +29,6 @@ export type SwapIxs<QuoteResponse> = {
30
29
 
31
30
  export type PriceAinBProvider = (mintA: Address, mintB: Address) => Promise<Decimal>;
32
31
 
33
- export type IsKtokenProvider = (token: Address) => Promise<boolean>;
34
-
35
32
  export type FlashLoanInfo = {
36
33
  flashBorrowReserve: Address;
37
34
  flashLoanFee: Decimal;
@@ -47,7 +44,6 @@ export type SwapInputs = {
47
44
  minOutAmountLamports?: Decimal;
48
45
  inputMint: Address;
49
46
  outputMint: Address;
50
- amountDebtAtaBalance: Decimal | undefined;
51
47
  };
52
48
 
53
49
  export type BaseLeverageIxsResponse<QuoteResponse> = {
@@ -63,9 +59,7 @@ export type LeverageInitialInputs<LeverageCalcsResult, QuoteResponse> = {
63
59
  swapQuote: SwapQuote<QuoteResponse>;
64
60
  currentSlot: Slot;
65
61
  klendAccounts: Array<Address>;
66
- collIsKtoken: boolean;
67
62
  obligation: KaminoObligation | ObligationType | undefined;
68
- strategy: StrategyWithAddress | undefined;
69
63
  };
70
64
 
71
65
  export interface BaseLeverageSwapInputsProps<QuoteResponse> {
@@ -77,10 +71,8 @@ export interface BaseLeverageSwapInputsProps<QuoteResponse> {
77
71
  currentSlot: Slot;
78
72
  slippagePct: Decimal;
79
73
  budgetAndPriorityFeeIxs?: Instruction[];
80
- kamino: Kamino | undefined;
81
74
  scopeRefreshConfig?: ScopePriceRefreshConfig;
82
75
  quoteBufferBps: Decimal;
83
- isKtoken: IsKtokenProvider;
84
76
  quoter: SwapQuoteProvider<QuoteResponse>;
85
77
  useV2Ixs: boolean;
86
78
  }
@@ -94,9 +86,7 @@ export type DepositLeverageInitialInputs<QuoteResponse> = {
94
86
  swapQuote: SwapQuote<QuoteResponse>;
95
87
  currentSlot: Slot;
96
88
  klendAccounts: Array<Address>;
97
- collIsKtoken: boolean;
98
89
  obligation: KaminoObligation | ObligationType | undefined;
99
- strategy: StrategyWithAddress | undefined;
100
90
  };
101
91
 
102
92
  export interface DepositWithLeverageSwapInputsProps<QuoteResponse> extends BaseLeverageSwapInputsProps<QuoteResponse> {
@@ -106,7 +96,6 @@ export interface DepositWithLeverageSwapInputsProps<QuoteResponse> extends BaseL
106
96
  priceDebtToColl: Decimal;
107
97
  targetLeverage: Decimal;
108
98
  selectedTokenMint: Address;
109
- priceAinB: PriceAinBProvider;
110
99
  // currently only used to disable requesting elevation group when this value is 0
111
100
  // to be implemented properly in the future
112
101
  elevationGroupOverride?: number;
@@ -123,9 +112,6 @@ export type DepositLeverageCalcsResult = {
123
112
  collTokenToDeposit: Decimal;
124
113
  swapDebtTokenIn: Decimal;
125
114
  swapCollTokenExpectedOut: Decimal;
126
- flashBorrowInDebtTokenKtokenOnly: Decimal;
127
- singleSidedDepositKtokenOnly: Decimal;
128
- requiredCollateralKtokenOnly: Decimal;
129
115
  };
130
116
 
131
117
  export type WithdrawLeverageIxsResponse<QuoteResponse> = BaseLeverageIxsResponse<QuoteResponse> & {
@@ -137,9 +123,7 @@ export type WithdrawLeverageInitialInputs<QuoteResponse> = {
137
123
  swapQuote: SwapQuote<QuoteResponse>;
138
124
  currentSlot: Slot;
139
125
  klendAccounts: Array<Address>;
140
- collIsKtoken: boolean;
141
126
  obligation: KaminoObligation | ObligationType | undefined;
142
- strategy: StrategyWithAddress | undefined;
143
127
  };
144
128
 
145
129
  export interface WithdrawWithLeverageSwapInputsProps<QuoteResponse> extends BaseLeverageSwapInputsProps<QuoteResponse> {
@@ -176,9 +160,7 @@ export type AdjustLeverageInitialInputs<QuoteResponse> = {
176
160
  currentSlot: Slot;
177
161
  klendAccounts: Array<Address>;
178
162
  isDeposit: boolean;
179
- collIsKtoken: boolean;
180
163
  obligation: KaminoObligation | ObligationType | undefined;
181
- strategy: StrategyWithAddress | undefined;
182
164
  };
183
165
 
184
166
  export interface AdjustLeverageSwapInputsProps<QuoteResponse> extends BaseLeverageSwapInputsProps<QuoteResponse> {
@@ -188,7 +170,6 @@ export interface AdjustLeverageSwapInputsProps<QuoteResponse> extends BaseLevera
188
170
  targetLeverage: Decimal;
189
171
  priceCollToDebt: Decimal;
190
172
  priceDebtToColl: Decimal;
191
- priceAinB: PriceAinBProvider;
192
173
  withdrawSlotOffset?: number;
193
174
  }
194
175
 
@@ -201,6 +182,5 @@ export type AdjustLeverageCalcsResult = {
201
182
  adjustBorrowPosition: Decimal;
202
183
  amountToFlashBorrowDebt: Decimal;
203
184
  borrowAmount: Decimal;
204
- expectedDebtTokenAtaBalance: Decimal;
205
185
  withdrawAmountWithSlippageAndFlashLoanFee: Decimal;
206
186
  };
@@ -1,325 +1,8 @@
1
- import { Kamino, StrategyWithAddress } from '@kamino-finance/kliquidity-sdk';
2
- import { KaminoMarket, KaminoReserve, lamportsToNumberDecimal } from '../classes';
3
- import {
4
- Address,
5
- Instruction,
6
- GetAccountInfoApi,
7
- Rpc,
8
- GetTokenAccountBalanceApi,
9
- TransactionSigner,
10
- } from '@solana/kit';
1
+ import { KaminoReserve, lamportsToNumberDecimal } from '../classes';
2
+ import { Address, GetAccountInfoApi, Rpc, GetTokenAccountBalanceApi } from '@solana/kit';
11
3
  import Decimal from 'decimal.js';
12
- import { getLookupTableAccounts, getTokenAccountBalanceDecimal } from '../utils';
13
- import { numberToLamportsDecimal } from '../classes/utils';
4
+ import { getTokenAccountBalanceDecimal } from '../utils';
14
5
  import BN from 'bn.js';
15
- import { PriceAinBProvider, SwapInputs, SwapQuote, SwapIxs, SwapIxsProvider } from './types';
16
-
17
- export interface KaminoSwapperIxBuilder {
18
- (
19
- input: DepositAmountsForSwap,
20
- tokenAMint: Address,
21
- tokenBMint: Address,
22
- owner: TransactionSigner,
23
- slippage: Decimal,
24
- allKeys: Address[]
25
- ): Promise<[Instruction[], Address[]]>;
26
- }
27
-
28
- export interface DepositAmountsForSwap {
29
- requiredAAmountToDeposit: Decimal;
30
- requiredBAmountToDeposit: Decimal;
31
- tokenAToSwapAmount: Decimal;
32
- tokenBToSwapAmount: Decimal;
33
- }
34
-
35
- export async function getTokenToKtokenSwapper<QuoteResponse>(
36
- kaminoMarket: KaminoMarket,
37
- kamino: Kamino,
38
- depositor: TransactionSigner,
39
- slippagePct: Decimal,
40
- swapper: SwapIxsProvider<QuoteResponse>,
41
- priceAinB: PriceAinBProvider,
42
- includeAtaIxs: boolean = true
43
- ): Promise<SwapIxsProvider<QuoteResponse>> {
44
- return async (
45
- inputs: SwapInputs,
46
- klendAccounts: Array<Address>,
47
- quote: SwapQuote<QuoteResponse>
48
- ): Promise<Array<SwapIxs<QuoteResponse>>> => {
49
- const slippageBps = new Decimal(slippagePct).mul('100');
50
- const mintInDecimals = kaminoMarket.getExistingReserveByMint(inputs.inputMint).getMintDecimals();
51
- const amountIn = lamportsToNumberDecimal(inputs.inputAmountLamports, mintInDecimals);
52
- console.debug('Depositing token', inputs.inputMint, ' for ', inputs.outputMint, 'ktoken');
53
- if (inputs.amountDebtAtaBalance === undefined) {
54
- throw Error('Amount in debt ATA balance is undefined for leverage ktoken deposit');
55
- }
56
-
57
- const ixWithLookup = (await getKtokenDepositIxs(
58
- kaminoMarket.getRpc(),
59
- kamino,
60
- depositor,
61
- inputs.inputMint,
62
- inputs.outputMint,
63
- amountIn,
64
- slippageBps,
65
- inputs.amountDebtAtaBalance,
66
- swapper,
67
- priceAinB,
68
- includeAtaIxs,
69
- klendAccounts,
70
- quote
71
- ))!;
72
-
73
- const luts = await getLookupTableAccounts(kaminoMarket.getRpc(), ixWithLookup.lookupTablesAddresses);
74
-
75
- return [
76
- {
77
- preActionIxs: [],
78
- swapIxs: ixWithLookup.instructions,
79
- lookupTables: luts,
80
- // TODO: Ktoken only supports one swap at a time for now (to be updated if we enable ktokens)
81
- quote: {
82
- priceAInB: new Decimal(0),
83
- quoteResponse: undefined,
84
- },
85
- },
86
- ];
87
- };
88
- }
89
-
90
- export async function getKtokenDepositIxs<QuoteResponse>(
91
- rpc: Rpc<GetAccountInfoApi & GetTokenAccountBalanceApi>,
92
- kamino: Kamino,
93
- depositor: TransactionSigner,
94
- depositTokenMint: Address,
95
- ktokenMint: Address,
96
- amountToDeposit: Decimal,
97
- slippageBps: Decimal,
98
- amountExpectedDepositAtaBalance: Decimal,
99
- swapper: SwapIxsProvider<QuoteResponse>,
100
- priceAinB: PriceAinBProvider,
101
- includeAtaIxs: boolean = true,
102
- klendAccounts: Array<Address>,
103
- quote: SwapQuote<QuoteResponse>
104
- ) {
105
- const kaminoStrategy = await kamino.getStrategyByKTokenMint(ktokenMint);
106
- const tokenAMint = kaminoStrategy?.strategy.tokenAMint!;
107
- const tokenBMint = kaminoStrategy?.strategy.tokenBMint!;
108
- const priceAinBDecimal = await priceAinB(tokenAMint, tokenBMint);
109
-
110
- if (tokenAMint === depositTokenMint) {
111
- const bBalance = await getTokenAccountBalanceDecimal(rpc, tokenBMint, depositor.address);
112
- const tokensBalances = { a: amountExpectedDepositAtaBalance, b: bBalance };
113
- console.log('amountToDeposit', amountToDeposit);
114
- return await kamino.singleSidedDepositTokenA(
115
- kaminoStrategy!,
116
- amountToDeposit,
117
- depositor,
118
- slippageBps,
119
- undefined,
120
- swapProviderToKaminoSwapProvider(swapper, klendAccounts, quote),
121
- tokensBalances,
122
- priceAinBDecimal,
123
- includeAtaIxs
124
- );
125
- } else if (tokenBMint === depositTokenMint) {
126
- const aBalance = await getTokenAccountBalanceDecimal(rpc, tokenAMint, depositor.address);
127
- const tokensBalances = { a: aBalance, b: amountExpectedDepositAtaBalance };
128
- return await kamino.singleSidedDepositTokenB(
129
- kaminoStrategy!,
130
- amountToDeposit,
131
- depositor,
132
- slippageBps,
133
- undefined,
134
- swapProviderToKaminoSwapProvider(swapper, klendAccounts, quote),
135
- tokensBalances,
136
- priceAinBDecimal,
137
- includeAtaIxs
138
- );
139
- } else {
140
- throw Error('Deposit token is neither A nor B in the strategy');
141
- }
142
- }
143
-
144
- export async function getKtokenToTokenSwapper<QuoteResponse>(
145
- kaminoMarket: KaminoMarket,
146
- kamino: Kamino,
147
- depositor: TransactionSigner,
148
- swapper: SwapIxsProvider<QuoteResponse>
149
- ): Promise<SwapIxsProvider<QuoteResponse>> {
150
- return async (inputs: SwapInputs, klendAccounts: Array<Address>, quote: SwapQuote<QuoteResponse>) => {
151
- const amountInDecimals = kaminoMarket.getExistingReserveByMint(inputs.inputMint).getMintDecimals();
152
- const amountToWithdraw = lamportsToNumberDecimal(inputs.inputAmountLamports, amountInDecimals);
153
- const kaminoStrategy = await kamino.getStrategyByKTokenMint(inputs.inputMint);
154
-
155
- console.log('Withdrawing ktoken', inputs.inputMint.toString(), ' for ', inputs.outputMint.toString(), 'token');
156
-
157
- const ixWithdraw = (await getKtokenWithdrawIxs(kamino, depositor, kaminoStrategy!, amountToWithdraw))!;
158
-
159
- const [estimatedAOut, estimatedBOut] = await getKtokenWithdrawEstimatesAndPrice(
160
- kamino,
161
- kaminoStrategy!,
162
- amountToWithdraw
163
- );
164
-
165
- if (inputs.outputMint === kaminoStrategy!.strategy.tokenAMint!) {
166
- const swapArray = await swapper(
167
- {
168
- inputAmountLamports: estimatedBOut,
169
- inputMint: kaminoStrategy!.strategy.tokenBMint!,
170
- outputMint: kaminoStrategy!.strategy.tokenAMint!,
171
- amountDebtAtaBalance: new Decimal(0),
172
- },
173
- klendAccounts,
174
- quote
175
- );
176
- // TODO: Ktoken only supports one swap at a time for now (to be updated if we enable ktokens)
177
- const swap = swapArray[0];
178
-
179
- return [
180
- {
181
- preActionIxs: [],
182
- swapIxs: [...ixWithdraw.prerequisiteIxs, ixWithdraw.withdrawIx, ...swap.swapIxs],
183
- lookupTables: swap.lookupTables,
184
- quote: swap.quote,
185
- },
186
- ];
187
- } else if (inputs.outputMint === kaminoStrategy!.strategy.tokenBMint) {
188
- const swapArray = await swapper(
189
- {
190
- inputAmountLamports: estimatedAOut,
191
- inputMint: kaminoStrategy!.strategy.tokenAMint!,
192
- outputMint: kaminoStrategy!.strategy.tokenBMint!,
193
- amountDebtAtaBalance: new Decimal(0),
194
- },
195
- klendAccounts,
196
- quote
197
- );
198
- // TODO: Ktoken only supports one swap at a time for now (to be updated if we enable ktokens)
199
- const swap = swapArray[0];
200
-
201
- return [
202
- {
203
- preActionIxs: [],
204
- swapIxs: [...ixWithdraw.prerequisiteIxs, ixWithdraw.withdrawIx, ...swap.swapIxs],
205
- lookupTables: swap.lookupTables,
206
- quote: swap.quote,
207
- },
208
- ];
209
- } else {
210
- throw Error('Deposit token is neither A nor B in the strategy');
211
- }
212
- };
213
- }
214
-
215
- export async function getKtokenWithdrawIxs(
216
- kamino: Kamino,
217
- withdrawer: TransactionSigner,
218
- kaminoStrategy: StrategyWithAddress,
219
- amountToWithdraw: Decimal
220
- ) {
221
- return await kamino.withdrawShares(kaminoStrategy!, amountToWithdraw, withdrawer);
222
- }
223
-
224
- export async function getKtokenWithdrawEstimatesAndPrice(
225
- kamino: Kamino,
226
- kaminoStrategy: StrategyWithAddress,
227
- amountToWithdraw: Decimal
228
- ) {
229
- const sharesData = await kamino.getStrategyShareData(kaminoStrategy);
230
- const withdrawPct = amountToWithdraw
231
- .div(
232
- lamportsToNumberDecimal(
233
- new Decimal(kaminoStrategy.strategy.sharesIssued.toString()),
234
- kaminoStrategy.strategy.sharesMintDecimals.toNumber()
235
- )
236
- )
237
- .toDecimalPlaces(18);
238
-
239
- const withdrawFee = new Decimal(10_000).sub(new Decimal(kaminoStrategy.strategy.withdrawFee.toString()));
240
-
241
- // TODO: Mihai/Marius improve - currently subtracting due to decimal accuracy issues compared to yvaults SC
242
- // for both A and B op: .sub(0.000002)
243
-
244
- const estimatedAOut = sharesData.balance.computedHoldings.invested.a
245
- .add(sharesData.balance.computedHoldings.available.a)
246
- .mul(withdrawPct)
247
- .toDecimalPlaces(kaminoStrategy.strategy.tokenAMintDecimals.toNumber(), 1)
248
- .sub(0.000002)
249
- .mul(withdrawFee)
250
- .div(10_000)
251
- .toDecimalPlaces(kaminoStrategy.strategy.tokenAMintDecimals.toNumber());
252
-
253
- const estimatedAOutDecimal = numberToLamportsDecimal(
254
- estimatedAOut,
255
- kaminoStrategy.strategy.tokenAMintDecimals.toNumber()
256
- ).floor();
257
-
258
- const estimatedBOut = sharesData.balance.computedHoldings.invested.b
259
- .add(sharesData.balance.computedHoldings.available.b)
260
- .mul(withdrawPct)
261
- .toDecimalPlaces(kaminoStrategy.strategy.tokenBMintDecimals.toNumber(), 1)
262
- .sub(0.000002)
263
- .mul(withdrawFee)
264
- .div(10_000)
265
- .toDecimalPlaces(kaminoStrategy.strategy.tokenAMintDecimals.toNumber());
266
-
267
- const estimatedBOutDecimal = numberToLamportsDecimal(
268
- estimatedBOut,
269
- kaminoStrategy.strategy.tokenBMintDecimals.toNumber()
270
- ).floor();
271
-
272
- console.log('a-out', estimatedAOutDecimal.toString());
273
- console.log('b-out', estimatedBOut.toString());
274
- return [estimatedAOutDecimal, estimatedBOutDecimal];
275
- }
276
-
277
- export function swapProviderToKaminoSwapProvider<QuoteResponse>(
278
- swapper: SwapIxsProvider<QuoteResponse>,
279
- klendAccounts: Array<Address>,
280
- swapQuote: SwapQuote<QuoteResponse>
281
- ): KaminoSwapperIxBuilder {
282
- return async (
283
- input: DepositAmountsForSwap,
284
- tokenAMint: Address,
285
- tokenBMint: Address,
286
- _owner: TransactionSigner,
287
- _slippage: Decimal,
288
- _allKeys: Address[]
289
- ): Promise<[Instruction[], Address[]]> => {
290
- if (input.tokenBToSwapAmount.lt(0)) {
291
- const swapperIxsArray = await swapper(
292
- {
293
- inputAmountLamports: input.tokenBToSwapAmount.abs(),
294
- inputMint: tokenBMint,
295
- outputMint: tokenAMint,
296
- amountDebtAtaBalance: undefined,
297
- },
298
- klendAccounts,
299
- swapQuote
300
- );
301
- // TODO: Ktoken only supports one swap at a time for now (to be updated if we enable ktokens)
302
- const swapperIxs = swapperIxsArray[0];
303
- return [swapperIxs.swapIxs, swapperIxs.lookupTables.map((lt) => lt.address)];
304
- } else if (input.tokenAToSwapAmount.lt(0)) {
305
- const swapperIxsArray = await swapper(
306
- {
307
- inputAmountLamports: input.tokenAToSwapAmount.abs(),
308
- inputMint: tokenAMint,
309
- outputMint: tokenBMint,
310
- amountDebtAtaBalance: undefined,
311
- },
312
- klendAccounts,
313
- swapQuote
314
- );
315
- // TODO: Ktoken only supports one swap at a time for now (to be updated if we enable ktokens)
316
- const swapperIxs = swapperIxsArray[0];
317
- return [swapperIxs.swapIxs, swapperIxs.lookupTables.map((lt) => lt.address)];
318
- } else {
319
- throw Error('Nothing to swap');
320
- }
321
- };
322
- }
323
6
 
324
7
  export const getExpectedTokenBalanceAfterBorrow = async (
325
8
  rpc: Rpc<GetAccountInfoApi & GetTokenAccountBalanceApi>,