@kamino-finance/klend-sdk 5.12.7 → 5.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/dist/classes/action.d.ts +2 -2
  2. package/dist/classes/action.d.ts.map +1 -1
  3. package/dist/classes/action.js +5 -3
  4. package/dist/classes/action.js.map +1 -1
  5. package/dist/classes/manager.d.ts.map +1 -1
  6. package/dist/classes/manager.js.map +1 -1
  7. package/dist/classes/market.d.ts +10 -0
  8. package/dist/classes/market.d.ts.map +1 -1
  9. package/dist/classes/market.js +17 -10
  10. package/dist/classes/market.js.map +1 -1
  11. package/dist/classes/obligation.d.ts +2 -2
  12. package/dist/classes/obligation.js +1 -1
  13. package/dist/classes/obligationOrder.d.ts +30 -7
  14. package/dist/classes/obligationOrder.d.ts.map +1 -1
  15. package/dist/classes/obligationOrder.js +47 -12
  16. package/dist/classes/obligationOrder.js.map +1 -1
  17. package/dist/classes/utils.d.ts +0 -1
  18. package/dist/classes/utils.d.ts.map +1 -1
  19. package/dist/classes/utils.js +0 -7
  20. package/dist/classes/utils.js.map +1 -1
  21. package/dist/lending_operations/repay_with_collateral_operations.js +1 -1
  22. package/dist/lending_operations/repay_with_collateral_operations.js.map +1 -1
  23. package/dist/lending_operations/swap_collateral_operations.js +1 -1
  24. package/dist/lending_operations/swap_collateral_operations.js.map +1 -1
  25. package/dist/leverage/operations.js +4 -4
  26. package/dist/leverage/operations.js.map +1 -1
  27. package/dist/lib.d.ts +1 -0
  28. package/dist/lib.d.ts.map +1 -1
  29. package/dist/lib.js +1 -0
  30. package/dist/lib.js.map +1 -1
  31. package/dist/obligation_orders/common.d.ts +62 -0
  32. package/dist/obligation_orders/common.d.ts.map +1 -0
  33. package/dist/obligation_orders/common.js +20 -0
  34. package/dist/obligation_orders/common.js.map +1 -0
  35. package/dist/obligation_orders/index.d.ts +4 -0
  36. package/dist/obligation_orders/index.d.ts.map +1 -0
  37. package/dist/obligation_orders/index.js +20 -0
  38. package/dist/obligation_orders/index.js.map +1 -0
  39. package/dist/obligation_orders/internal.d.ts +6 -0
  40. package/dist/obligation_orders/internal.d.ts.map +1 -0
  41. package/dist/obligation_orders/internal.js +72 -0
  42. package/dist/obligation_orders/internal.js.map +1 -0
  43. package/dist/obligation_orders/ltv_based.d.ts +51 -0
  44. package/dist/obligation_orders/ltv_based.d.ts.map +1 -0
  45. package/dist/obligation_orders/ltv_based.js +107 -0
  46. package/dist/obligation_orders/ltv_based.js.map +1 -0
  47. package/dist/obligation_orders/price_based.d.ts +81 -0
  48. package/dist/obligation_orders/price_based.d.ts.map +1 -0
  49. package/dist/obligation_orders/price_based.js +167 -0
  50. package/dist/obligation_orders/price_based.js.map +1 -0
  51. package/dist/utils/instruction.d.ts +1 -1
  52. package/dist/utils/instruction.d.ts.map +1 -1
  53. package/dist/utils/instruction.js +3 -2
  54. package/dist/utils/instruction.js.map +1 -1
  55. package/dist/utils/validations.d.ts +5 -0
  56. package/dist/utils/validations.d.ts.map +1 -0
  57. package/dist/utils/validations.js +36 -0
  58. package/dist/utils/validations.js.map +1 -0
  59. package/package.json +1 -1
  60. package/src/classes/action.ts +6 -4
  61. package/src/classes/manager.ts +1 -4
  62. package/src/classes/market.ts +21 -11
  63. package/src/classes/obligation.ts +2 -2
  64. package/src/classes/obligationOrder.ts +57 -20
  65. package/src/classes/utils.ts +0 -7
  66. package/src/lending_operations/repay_with_collateral_operations.ts +2 -2
  67. package/src/lending_operations/swap_collateral_operations.ts +2 -2
  68. package/src/leverage/operations.ts +5 -5
  69. package/src/lib.ts +1 -0
  70. package/src/obligation_orders/common.ts +70 -0
  71. package/src/obligation_orders/index.ts +3 -0
  72. package/src/obligation_orders/internal.ts +92 -0
  73. package/src/obligation_orders/ltv_based.ts +143 -0
  74. package/src/obligation_orders/price_based.ts +256 -0
  75. package/src/utils/instruction.ts +2 -1
  76. package/src/utils/validations.ts +31 -0
@@ -0,0 +1,256 @@
1
+ import { KaminoMarket } from '../classes';
2
+ import Decimal from 'decimal.js';
3
+ import {
4
+ DebtCollPriceRatioAbove,
5
+ DebtCollPriceRatioBelow,
6
+ ObligationOrderAtIndex,
7
+ OrderCondition,
8
+ } from '../classes/obligationOrder';
9
+ import { PublicKeySet } from '../utils';
10
+ import { PublicKey } from '@solana/web3.js';
11
+ import { checkThat, getSingleElement } from '../utils/validations';
12
+ import { OrderContext, OrderSpecification, OrderType } from './common';
13
+ import { createConditionBasedOrder, readTriggerBasedOrder, toOrderIndex } from './internal';
14
+
15
+ /**
16
+ * Creates a price-based {@link ObligationOrderAtIndex} based on the given stop-loss or take-profit specification.
17
+ *
18
+ * The returned object can then be passed directly to {@link KaminoAction.buildSetObligationOrderIxn()} to build an
19
+ * instruction which replaces (or cancels, if the specification is `null`) the given obligation's stop-loss or
20
+ * take-profit order on-chain.
21
+ *
22
+ * The given obligation is expected to be a "price-based position" - a single-debt, single-coll obligation which either
23
+ * deposits or borrows a stablecoin (i.e. a long or short position of some token against a stablecoin).
24
+ */
25
+ export function createPriceBasedOrder(
26
+ context: PriceBasedOrderContext,
27
+ orderType: OrderType,
28
+ specification: PriceBasedOrderSpecification | null
29
+ ): ObligationOrderAtIndex {
30
+ const positionType = resolvePositionType(context); // resolving this first has an intentional side effect of validating the obligation being compatible
31
+ const index = toOrderIndex(orderType);
32
+ if (specification === null) {
33
+ return ObligationOrderAtIndex.empty(index);
34
+ }
35
+ const condition = toOrderCondition(positionType, orderType, specification.trigger);
36
+ return createConditionBasedOrder(context, condition, specification).atIndex(index);
37
+ }
38
+
39
+ /**
40
+ * Parses an {@link PriceBasedOrderSpecification} from the selected stop-loss or take-profit order of the given obligation.
41
+ *
42
+ * The given obligation is expected to be a "price-based position" - a single-debt, single-coll obligation which either
43
+ * deposits or borrows a stablecoin (i.e. a long or short position of some token against a stablecoin).
44
+ *
45
+ * The selected order is expected to be of matching type (i.e. as if it was created using the
46
+ * {@link createPriceBasedOrder()}).
47
+ */
48
+ export function readPriceBasedOrder(
49
+ context: PriceBasedOrderContext,
50
+ orderType: OrderType
51
+ ): PriceBasedOrderSpecification | null {
52
+ const positionType = resolvePositionType(context); // resolving this first has an intentional side effect of validating the obligation being compatible
53
+ const kaminoOrder = context.kaminoObligation.getOrders()[toOrderIndex(orderType)];
54
+ if (kaminoOrder === null) {
55
+ return null;
56
+ }
57
+ const trigger = toTrigger(positionType, kaminoOrder.condition, orderType);
58
+ return readTriggerBasedOrder(kaminoOrder, trigger);
59
+ }
60
+
61
+ /**
62
+ * An extended {@link OrderContext} needed to interpret orders on "price-based position" obligations.
63
+ */
64
+ export type PriceBasedOrderContext = OrderContext & {
65
+ stablecoins: SymbolOrMintAddress[];
66
+ };
67
+
68
+ /**
69
+ * A convenient multi-way of specifying a token.
70
+ */
71
+ export type SymbolOrMintAddress = string | PublicKey;
72
+
73
+ /**
74
+ * A high-level specification of a price-based order.
75
+ */
76
+ export type PriceBasedOrderSpecification = OrderSpecification<PriceBasedOrderTrigger>;
77
+
78
+ /**
79
+ * A discriminator enum for {@link PriceBasedOrderTrigger};
80
+ */
81
+ export enum PriceBasedOrderTriggerType {
82
+ LongStopLoss = 'LongStopLoss',
83
+ LongTakeProfit = 'LongTakeProfit',
84
+ ShortStopLoss = 'ShortStopLoss',
85
+ ShortTakeProfit = 'ShortTakeProfit',
86
+ }
87
+
88
+ /**
89
+ * One of possible triggers depending on the obligation's type and the price bracket's side.
90
+ */
91
+ export type PriceBasedOrderTrigger = LongStopLoss | LongTakeProfit | ShortStopLoss | ShortTakeProfit;
92
+
93
+ /**
94
+ * A trigger for a stop-loss on a long position.
95
+ */
96
+ export type LongStopLoss = {
97
+ type: PriceBasedOrderTriggerType.LongStopLoss;
98
+ whenCollateralPriceBelow: Decimal;
99
+ };
100
+
101
+ /**
102
+ * A trigger for a take-profit on a long position.
103
+ */
104
+ export type LongTakeProfit = {
105
+ type: PriceBasedOrderTriggerType.LongTakeProfit;
106
+ whenCollateralPriceAbove: Decimal;
107
+ };
108
+
109
+ /**
110
+ * A trigger for a stop-loss on a short position.
111
+ */
112
+ export type ShortStopLoss = {
113
+ type: PriceBasedOrderTriggerType.ShortStopLoss;
114
+ whenDebtPriceAbove: Decimal;
115
+ };
116
+
117
+ /**
118
+ * A trigger for a take-profit on a short position.
119
+ */
120
+ export type ShortTakeProfit = {
121
+ type: PriceBasedOrderTriggerType.ShortTakeProfit;
122
+ whenDebtPriceBelow: Decimal;
123
+ };
124
+
125
+ // Only internals below:
126
+
127
+ function toOrderCondition(
128
+ positionType: PositionType,
129
+ orderType: OrderType,
130
+ trigger: PriceBasedOrderTrigger
131
+ ): OrderCondition {
132
+ switch (positionType) {
133
+ case PositionType.Long:
134
+ switch (orderType) {
135
+ case OrderType.StopLoss:
136
+ if (trigger.type === PriceBasedOrderTriggerType.LongStopLoss) {
137
+ return new DebtCollPriceRatioAbove(invertPriceRatio(trigger.whenCollateralPriceBelow));
138
+ }
139
+ break;
140
+ case OrderType.TakeProfit:
141
+ if (trigger.type === PriceBasedOrderTriggerType.LongTakeProfit) {
142
+ return new DebtCollPriceRatioBelow(invertPriceRatio(trigger.whenCollateralPriceAbove));
143
+ }
144
+ break;
145
+ }
146
+ break;
147
+ case PositionType.Short:
148
+ switch (orderType) {
149
+ case OrderType.StopLoss:
150
+ if (trigger.type === PriceBasedOrderTriggerType.ShortStopLoss) {
151
+ return new DebtCollPriceRatioAbove(trigger.whenDebtPriceAbove);
152
+ }
153
+ break;
154
+ case OrderType.TakeProfit:
155
+ if (trigger.type === PriceBasedOrderTriggerType.ShortTakeProfit) {
156
+ return new DebtCollPriceRatioBelow(trigger.whenDebtPriceBelow);
157
+ }
158
+ break;
159
+ }
160
+ break;
161
+ }
162
+ throw new Error(`a ${orderType} order on a ${positionType} position cannot use ${trigger.type} condition`);
163
+ }
164
+
165
+ function toTrigger(
166
+ positionType: PositionType,
167
+ condition: OrderCondition,
168
+ orderType: OrderType
169
+ ): PriceBasedOrderTrigger {
170
+ switch (positionType) {
171
+ case PositionType.Long:
172
+ switch (orderType) {
173
+ case OrderType.StopLoss:
174
+ if (condition instanceof DebtCollPriceRatioAbove) {
175
+ return {
176
+ type: PriceBasedOrderTriggerType.LongStopLoss,
177
+ whenCollateralPriceBelow: invertPriceRatio(condition.minDebtCollPriceRatioExclusive),
178
+ };
179
+ }
180
+ break;
181
+ case OrderType.TakeProfit:
182
+ if (condition instanceof DebtCollPriceRatioBelow) {
183
+ return {
184
+ type: PriceBasedOrderTriggerType.LongTakeProfit,
185
+ whenCollateralPriceAbove: invertPriceRatio(condition.maxDebtCollPriceRatioExclusive),
186
+ };
187
+ }
188
+ break;
189
+ }
190
+ break;
191
+ case PositionType.Short:
192
+ switch (orderType) {
193
+ case OrderType.StopLoss:
194
+ if (condition instanceof DebtCollPriceRatioAbove) {
195
+ return {
196
+ type: PriceBasedOrderTriggerType.ShortStopLoss,
197
+ whenDebtPriceAbove: condition.minDebtCollPriceRatioExclusive,
198
+ };
199
+ }
200
+ break;
201
+ case OrderType.TakeProfit:
202
+ if (condition instanceof DebtCollPriceRatioBelow) {
203
+ return {
204
+ type: PriceBasedOrderTriggerType.ShortTakeProfit,
205
+ whenDebtPriceBelow: condition.maxDebtCollPriceRatioExclusive,
206
+ };
207
+ }
208
+ break;
209
+ }
210
+ break;
211
+ }
212
+ throw new Error(
213
+ `a ${orderType} order on a ${positionType} position has an incompatible on-chain condition ${condition.constructor.name}`
214
+ );
215
+ }
216
+
217
+ function invertPriceRatio(priceRatio: Decimal): Decimal {
218
+ return new Decimal(1).div(priceRatio);
219
+ }
220
+
221
+ enum PositionType {
222
+ Long = 'Long',
223
+ Short = 'Short',
224
+ }
225
+
226
+ function resolvePositionType(context: PriceBasedOrderContext): PositionType {
227
+ const collateralReserveAddress = getSingleElement(context.kaminoObligation.deposits.keys(), 'deposit');
228
+ const debtReserveAddress = getSingleElement(context.kaminoObligation.borrows.keys(), 'borrow');
229
+ const stablecoinReserveAddresses = collectReserveAddresses(context.kaminoMarket, context.stablecoins);
230
+ if (stablecoinReserveAddresses.contains(collateralReserveAddress)) {
231
+ checkThat(
232
+ !stablecoinReserveAddresses.contains(debtReserveAddress),
233
+ 'cannot resolve long vs short position from all-stablecoins obligation'
234
+ );
235
+ return PositionType.Short;
236
+ } else {
237
+ checkThat(
238
+ stablecoinReserveAddresses.contains(debtReserveAddress),
239
+ 'cannot resolve long vs short position from no-stablecoins obligation'
240
+ );
241
+ return PositionType.Long;
242
+ }
243
+ }
244
+
245
+ function collectReserveAddresses(
246
+ kaminoMarket: KaminoMarket,
247
+ symbolOrMintAddresses: SymbolOrMintAddress[]
248
+ ): PublicKeySet<PublicKey> {
249
+ return new PublicKeySet(
250
+ symbolOrMintAddresses.map((symbolOrMintAddress) =>
251
+ typeof symbolOrMintAddress === 'string'
252
+ ? kaminoMarket.getExistingReserveBySymbol(symbolOrMintAddress).address
253
+ : kaminoMarket.getExistingReserveByMint(symbolOrMintAddress).address
254
+ )
255
+ );
256
+ }
@@ -260,7 +260,7 @@ export function notEmpty<TValue>(value: TValue | null | undefined): value is TVa
260
260
  return true;
261
261
  }
262
262
 
263
- export function uniqueAccounts(
263
+ export function uniqueAccountsWithProgramIds(
264
264
  ixs: TransactionInstruction[],
265
265
  addressLookupTables: PublicKey[] | AddressLookupTableAccount[] = []
266
266
  ): Array<PublicKey> {
@@ -273,6 +273,7 @@ export function uniqueAccounts(
273
273
 
274
274
  const uniqueAccounts = new PublicKeySet<PublicKey>(luts);
275
275
  ixs.forEach((ixn) => {
276
+ uniqueAccounts.add(ixn.programId);
276
277
  ixn.keys.forEach((key) => {
277
278
  uniqueAccounts.add(key.pubkey);
278
279
  });
@@ -0,0 +1,31 @@
1
+ export function checkThat(evaluationResult: boolean, message: string = 'precondition failed'): void {
2
+ if (!evaluationResult) {
3
+ throw new Error(message);
4
+ }
5
+ }
6
+
7
+ export function checkDefined<T>(value: T | undefined, message: string = 'value undefined'): T {
8
+ checkThat(value !== undefined, message);
9
+ return value as T;
10
+ }
11
+
12
+ export function checkNotNull<T>(value: T | null, message: string = 'value null'): T {
13
+ checkThat(value !== null, message);
14
+ return value as T;
15
+ }
16
+
17
+ export function getSingleElement<T>(iterable: Iterable<T>, nameWithinMessage: string = 'element'): T {
18
+ const nothingReturnedMarker = {};
19
+ let single: T | {} = nothingReturnedMarker;
20
+ for (const element of iterable) {
21
+ if (single === nothingReturnedMarker) {
22
+ single = element;
23
+ } else {
24
+ throw new Error(`exactly one ${nameWithinMessage} expected, but multiple found`);
25
+ }
26
+ }
27
+ if (single === nothingReturnedMarker) {
28
+ throw new Error(`exactly one ${nameWithinMessage} expected, but none found`);
29
+ }
30
+ return single as T;
31
+ }