@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.
- package/dist/classes/action.d.ts +2 -2
- package/dist/classes/action.d.ts.map +1 -1
- package/dist/classes/action.js +5 -3
- package/dist/classes/action.js.map +1 -1
- package/dist/classes/manager.d.ts.map +1 -1
- package/dist/classes/manager.js.map +1 -1
- package/dist/classes/market.d.ts +10 -0
- package/dist/classes/market.d.ts.map +1 -1
- package/dist/classes/market.js +17 -10
- package/dist/classes/market.js.map +1 -1
- package/dist/classes/obligation.d.ts +2 -2
- package/dist/classes/obligation.js +1 -1
- package/dist/classes/obligationOrder.d.ts +30 -7
- package/dist/classes/obligationOrder.d.ts.map +1 -1
- package/dist/classes/obligationOrder.js +47 -12
- package/dist/classes/obligationOrder.js.map +1 -1
- package/dist/classes/utils.d.ts +0 -1
- package/dist/classes/utils.d.ts.map +1 -1
- package/dist/classes/utils.js +0 -7
- package/dist/classes/utils.js.map +1 -1
- package/dist/lending_operations/repay_with_collateral_operations.js +1 -1
- package/dist/lending_operations/repay_with_collateral_operations.js.map +1 -1
- package/dist/lending_operations/swap_collateral_operations.js +1 -1
- package/dist/lending_operations/swap_collateral_operations.js.map +1 -1
- package/dist/leverage/operations.js +4 -4
- package/dist/leverage/operations.js.map +1 -1
- package/dist/lib.d.ts +1 -0
- package/dist/lib.d.ts.map +1 -1
- package/dist/lib.js +1 -0
- package/dist/lib.js.map +1 -1
- package/dist/obligation_orders/common.d.ts +62 -0
- package/dist/obligation_orders/common.d.ts.map +1 -0
- package/dist/obligation_orders/common.js +20 -0
- package/dist/obligation_orders/common.js.map +1 -0
- package/dist/obligation_orders/index.d.ts +4 -0
- package/dist/obligation_orders/index.d.ts.map +1 -0
- package/dist/obligation_orders/index.js +20 -0
- package/dist/obligation_orders/index.js.map +1 -0
- package/dist/obligation_orders/internal.d.ts +6 -0
- package/dist/obligation_orders/internal.d.ts.map +1 -0
- package/dist/obligation_orders/internal.js +72 -0
- package/dist/obligation_orders/internal.js.map +1 -0
- package/dist/obligation_orders/ltv_based.d.ts +51 -0
- package/dist/obligation_orders/ltv_based.d.ts.map +1 -0
- package/dist/obligation_orders/ltv_based.js +107 -0
- package/dist/obligation_orders/ltv_based.js.map +1 -0
- package/dist/obligation_orders/price_based.d.ts +81 -0
- package/dist/obligation_orders/price_based.d.ts.map +1 -0
- package/dist/obligation_orders/price_based.js +167 -0
- package/dist/obligation_orders/price_based.js.map +1 -0
- package/dist/utils/instruction.d.ts +1 -1
- package/dist/utils/instruction.d.ts.map +1 -1
- package/dist/utils/instruction.js +3 -2
- package/dist/utils/instruction.js.map +1 -1
- package/dist/utils/validations.d.ts +5 -0
- package/dist/utils/validations.d.ts.map +1 -0
- package/dist/utils/validations.js +36 -0
- package/dist/utils/validations.js.map +1 -0
- package/package.json +1 -1
- package/src/classes/action.ts +6 -4
- package/src/classes/manager.ts +1 -4
- package/src/classes/market.ts +21 -11
- package/src/classes/obligation.ts +2 -2
- package/src/classes/obligationOrder.ts +57 -20
- package/src/classes/utils.ts +0 -7
- package/src/lending_operations/repay_with_collateral_operations.ts +2 -2
- package/src/lending_operations/swap_collateral_operations.ts +2 -2
- package/src/leverage/operations.ts +5 -5
- package/src/lib.ts +1 -0
- package/src/obligation_orders/common.ts +70 -0
- package/src/obligation_orders/index.ts +3 -0
- package/src/obligation_orders/internal.ts +92 -0
- package/src/obligation_orders/ltv_based.ts +143 -0
- package/src/obligation_orders/price_based.ts +256 -0
- package/src/utils/instruction.ts +2 -1
- 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
|
+
}
|
package/src/utils/instruction.ts
CHANGED
|
@@ -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
|
|
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
|
+
}
|