@kamino-finance/klend-sdk 5.12.8 → 5.13.1
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 +46 -22
- package/dist/classes/action.d.ts.map +1 -1
- package/dist/classes/action.js +60 -37
- package/dist/classes/action.js.map +1 -1
- package/dist/classes/fraction.d.ts +2 -0
- package/dist/classes/fraction.d.ts.map +1 -1
- package/dist/classes/fraction.js +6 -0
- package/dist/classes/fraction.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.d.ts.map +1 -1
- package/dist/classes/obligation.js +2 -7
- package/dist/classes/obligation.js.map +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/reserve.d.ts.map +1 -1
- package/dist/classes/reserve.js +1 -4
- package/dist/classes/reserve.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/classes/vault.d.ts.map +1 -1
- package/dist/classes/vault.js +3 -0
- package/dist/classes/vault.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 +2 -2
- package/dist/lending_operations/repay_with_collateral_operations.js.map +1 -1
- package/dist/lending_operations/swap_collateral_operations.js +2 -4
- package/dist/lending_operations/swap_collateral_operations.js.map +1 -1
- package/dist/leverage/operations.d.ts.map +1 -1
- package/dist/leverage/operations.js +7 -14
- 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/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 +61 -52
- package/src/classes/fraction.ts +7 -0
- package/src/classes/manager.ts +1 -4
- package/src/classes/market.ts +21 -11
- package/src/classes/obligation.ts +4 -9
- package/src/classes/obligationOrder.ts +57 -20
- package/src/classes/reserve.ts +2 -5
- package/src/classes/utils.ts +0 -7
- package/src/classes/vault.ts +3 -0
- package/src/lending_operations/repay_with_collateral_operations.ts +0 -2
- package/src/lending_operations/swap_collateral_operations.ts +2 -4
- package/src/leverage/operations.ts +7 -13
- 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/validations.ts +31 -0
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import Decimal from 'decimal.js';
|
|
2
|
+
import { ObligationOrderAtIndex, OrderCondition, UserLtvAbove, UserLtvBelow } from '../classes/obligationOrder';
|
|
3
|
+
import { checkThat } from '../utils/validations';
|
|
4
|
+
import { OrderContext, OrderSpecification, OrderType } from './common';
|
|
5
|
+
import { createConditionBasedOrder, readTriggerBasedOrder, toOrderIndex } from './internal';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Creates an LTV-based {@link ObligationOrderAtIndex} based on the given stop-loss or take-profit specification.
|
|
9
|
+
*
|
|
10
|
+
* The returned object can then be passed directly to {@link KaminoAction.buildSetObligationOrderIxn()} to build an
|
|
11
|
+
* instruction which replaces (or cancels, if the specification is `null`) the given obligation's stop-loss or
|
|
12
|
+
* take-profit order on-chain.
|
|
13
|
+
*
|
|
14
|
+
* The given obligation cannot use 0-LTV collaterals (see {@link checkObligationCompatible()} for rationale).
|
|
15
|
+
*/
|
|
16
|
+
export function createLtvBasedOrder(
|
|
17
|
+
context: OrderContext,
|
|
18
|
+
orderType: OrderType,
|
|
19
|
+
specification: LtvBasedOrderSpecification | null
|
|
20
|
+
): ObligationOrderAtIndex {
|
|
21
|
+
checkObligationCompatible(context);
|
|
22
|
+
const index = toOrderIndex(orderType);
|
|
23
|
+
if (specification === null) {
|
|
24
|
+
return ObligationOrderAtIndex.empty(index);
|
|
25
|
+
}
|
|
26
|
+
const condition = toOrderCondition(orderType, specification.trigger);
|
|
27
|
+
checkThat(
|
|
28
|
+
condition.threshold().gte(MIN_LTV_THRESHOLD) && condition.threshold().lte(MAX_LTV_THRESHOLD),
|
|
29
|
+
`LTV-based trigger outside valid range [${MIN_LTV_THRESHOLD}%; ${MAX_LTV_THRESHOLD}%]: ${condition.threshold()}%`
|
|
30
|
+
);
|
|
31
|
+
return createConditionBasedOrder(context, condition, specification).atIndex(index);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Parses an {@link OrderSpecification} from the selected stop-loss or take-profit order of the given obligation.
|
|
36
|
+
*
|
|
37
|
+
* The given obligation cannot use 0-LTV collaterals (see {@link checkObligationCompatible()} for rationale).
|
|
38
|
+
*
|
|
39
|
+
* The selected order is expected to be of matching type (i.e. as if it was created using the
|
|
40
|
+
* {@link createLtvBasedOrder()}).
|
|
41
|
+
*/
|
|
42
|
+
export function readLtvBasedOrder(context: OrderContext, orderType: OrderType): LtvBasedOrderSpecification | null {
|
|
43
|
+
checkObligationCompatible(context);
|
|
44
|
+
const kaminoOrder = context.kaminoObligation.getOrders()[toOrderIndex(orderType)];
|
|
45
|
+
if (kaminoOrder === null) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
const trigger = toTrigger(kaminoOrder.condition, orderType);
|
|
49
|
+
return readTriggerBasedOrder(kaminoOrder, trigger);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* A high-level specification of an LTV-based order.
|
|
54
|
+
*/
|
|
55
|
+
export type LtvBasedOrderSpecification = OrderSpecification<LtvBasedOrderTrigger>;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* A discriminator enum for {@link LtvBasedOrderTrigger};
|
|
59
|
+
*/
|
|
60
|
+
export enum LtvBasedOrderTriggerType {
|
|
61
|
+
StopLoss = 'StopLoss',
|
|
62
|
+
TakeProfit = 'TakeProfit',
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* One of possible triggers depending on the obligation's type and the price bracket's side.
|
|
67
|
+
*/
|
|
68
|
+
export type LtvBasedOrderTrigger = StopLoss | TakeProfit;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* A trigger for a stop-loss on LTV.
|
|
72
|
+
*/
|
|
73
|
+
export type StopLoss = {
|
|
74
|
+
type: LtvBasedOrderTriggerType.StopLoss;
|
|
75
|
+
whenLtvPctAbove: number;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* A trigger for a take-profit on LTV.
|
|
80
|
+
*/
|
|
81
|
+
export type TakeProfit = {
|
|
82
|
+
type: LtvBasedOrderTriggerType.TakeProfit;
|
|
83
|
+
whenLtvPctBelow: number;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
// Only internals below:
|
|
87
|
+
|
|
88
|
+
const FULL_PCT = 100;
|
|
89
|
+
const MIN_LTV_THRESHOLD = 0.01;
|
|
90
|
+
const MAX_LTV_THRESHOLD = 0.99;
|
|
91
|
+
|
|
92
|
+
function checkObligationCompatible({ kaminoMarket, kaminoObligation }: OrderContext) {
|
|
93
|
+
for (const depositReserveAddress of kaminoObligation.deposits.keys()) {
|
|
94
|
+
const depositReserve = kaminoMarket.getExistingReserveByAddress(depositReserveAddress);
|
|
95
|
+
// Note: the seemingly over-cautious requirement below ensures that the user-facing LTV calculation gives the same
|
|
96
|
+
// result as on the Klend SC side (they differ in the handling of 0-LTV collaterals; see
|
|
97
|
+
// `KaminoObligation.loanToValue()` doc for details). We may unify the 0-LTV handling some day and remove this.
|
|
98
|
+
checkThat(
|
|
99
|
+
depositReserve.state.config.loanToValuePct !== 0,
|
|
100
|
+
`LTV-based orders cannot be used with a 0-LTV collateral: ${depositReserve.symbol}`
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function toOrderCondition(orderType: OrderType, trigger: LtvBasedOrderTrigger): OrderCondition {
|
|
106
|
+
switch (orderType) {
|
|
107
|
+
case OrderType.StopLoss:
|
|
108
|
+
if (trigger.type === LtvBasedOrderTriggerType.StopLoss) {
|
|
109
|
+
return new UserLtvAbove(new Decimal(trigger.whenLtvPctAbove).div(FULL_PCT));
|
|
110
|
+
}
|
|
111
|
+
break;
|
|
112
|
+
case OrderType.TakeProfit:
|
|
113
|
+
if (trigger.type === LtvBasedOrderTriggerType.TakeProfit) {
|
|
114
|
+
return new UserLtvBelow(new Decimal(trigger.whenLtvPctBelow).div(FULL_PCT));
|
|
115
|
+
}
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
throw new Error(`an LTV-based ${orderType} order cannot use ${trigger.type} condition`);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function toTrigger(condition: OrderCondition, orderType: OrderType): LtvBasedOrderTrigger {
|
|
122
|
+
switch (orderType) {
|
|
123
|
+
case OrderType.StopLoss:
|
|
124
|
+
if (condition instanceof UserLtvAbove) {
|
|
125
|
+
return {
|
|
126
|
+
type: LtvBasedOrderTriggerType.StopLoss,
|
|
127
|
+
whenLtvPctAbove: condition.minUserLtvExclusive.mul(FULL_PCT).toNumber(),
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
break;
|
|
131
|
+
case OrderType.TakeProfit:
|
|
132
|
+
if (condition instanceof UserLtvBelow) {
|
|
133
|
+
return {
|
|
134
|
+
type: LtvBasedOrderTriggerType.TakeProfit,
|
|
135
|
+
whenLtvPctBelow: condition.maxUserLtvExclusive.mul(FULL_PCT).toNumber(),
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
break;
|
|
139
|
+
}
|
|
140
|
+
throw new Error(
|
|
141
|
+
`an LTV-based ${orderType} order has an incompatible on-chain condition ${condition.constructor.name}`
|
|
142
|
+
);
|
|
143
|
+
}
|
|
@@ -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
|
+
}
|
|
@@ -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
|
+
}
|