@drift-labs/sdk 0.1.36-master.5 → 0.2.0-master.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/lib/accounts/bulkAccountLoader.d.ts +7 -6
- package/lib/accounts/bulkAccountLoader.js +83 -93
- package/lib/accounts/bulkUserSubscription.js +13 -57
- package/lib/accounts/fetch.d.ts +4 -0
- package/lib/accounts/fetch.js +18 -0
- package/lib/accounts/pollingClearingHouseAccountSubscriber.d.ts +34 -38
- package/lib/accounts/pollingClearingHouseAccountSubscriber.js +224 -224
- package/lib/accounts/pollingOracleSubscriber.d.ts +3 -3
- package/lib/accounts/pollingOracleSubscriber.js +37 -49
- package/lib/accounts/pollingTokenAccountSubscriber.d.ts +3 -3
- package/lib/accounts/pollingTokenAccountSubscriber.js +35 -50
- package/lib/accounts/pollingUserAccountSubscriber.d.ts +7 -13
- package/lib/accounts/pollingUserAccountSubscriber.js +71 -134
- package/lib/accounts/types.d.ts +34 -41
- package/lib/accounts/webSocketAccountSubscriber.d.ts +6 -4
- package/lib/accounts/webSocketAccountSubscriber.js +39 -35
- package/lib/accounts/webSocketClearingHouseAccountSubscriber.d.ts +33 -28
- package/lib/accounts/webSocketClearingHouseAccountSubscriber.js +153 -185
- package/lib/accounts/webSocketUserAccountSubscriber.d.ts +5 -11
- package/lib/accounts/webSocketUserAccountSubscriber.js +22 -67
- package/lib/addresses/marketAddresses.d.ts +4 -0
- package/lib/addresses/marketAddresses.js +15 -0
- package/lib/addresses/pda.d.ts +12 -0
- package/lib/addresses/pda.js +83 -0
- package/lib/admin.d.ts +8 -12
- package/lib/admin.js +366 -490
- package/lib/clearingHouse.d.ts +84 -103
- package/lib/clearingHouse.js +779 -810
- package/lib/clearingHouseConfig.d.ts +34 -0
- package/lib/clearingHouseConfig.js +2 -0
- package/lib/clearingHouseUser.d.ts +19 -18
- package/lib/clearingHouseUser.js +157 -115
- package/lib/clearingHouseUserConfig.d.ts +14 -0
- package/lib/clearingHouseUserConfig.js +2 -0
- package/lib/config.d.ts +12 -0
- package/lib/config.js +35 -4
- package/lib/constants/banks.d.ts +16 -0
- package/lib/constants/banks.js +34 -0
- package/lib/constants/markets.d.ts +8 -3
- package/lib/constants/markets.js +13 -206
- package/lib/constants/numericConstants.d.ts +16 -0
- package/lib/constants/numericConstants.js +22 -6
- package/lib/events/eventList.d.ts +22 -0
- package/lib/events/eventList.js +77 -0
- package/lib/events/eventSubscriber.d.ts +34 -0
- package/lib/events/eventSubscriber.js +126 -0
- package/lib/events/fetchLogs.d.ts +13 -0
- package/lib/events/fetchLogs.js +39 -0
- package/lib/events/pollingLogProvider.d.ts +15 -0
- package/lib/events/pollingLogProvider.js +53 -0
- package/lib/events/sort.d.ts +2 -0
- package/lib/events/sort.js +44 -0
- package/lib/events/txEventCache.d.ts +24 -0
- package/lib/events/txEventCache.js +71 -0
- package/lib/events/types.d.ts +49 -0
- package/lib/events/types.js +20 -0
- package/lib/events/webSocketLogProvider.d.ts +12 -0
- package/lib/events/webSocketLogProvider.js +30 -0
- package/lib/examples/makeTradeExample.js +26 -27
- package/lib/factory/bigNum.d.ts +112 -0
- package/lib/factory/bigNum.js +356 -0
- package/lib/factory/oracleClient.d.ts +1 -2
- package/lib/factory/oracleClient.js +6 -2
- package/lib/idl/clearing_house.json +1739 -2287
- package/lib/index.d.ts +13 -4
- package/lib/index.js +13 -4
- package/lib/math/amm.d.ts +19 -29
- package/lib/math/amm.js +129 -179
- package/lib/math/auction.d.ts +5 -0
- package/lib/math/auction.js +39 -0
- package/lib/math/bankBalance.d.ts +9 -0
- package/lib/math/bankBalance.js +75 -0
- package/lib/math/conversion.d.ts +0 -1
- package/lib/math/conversion.js +1 -5
- package/lib/math/funding.d.ts +6 -6
- package/lib/math/funding.js +158 -175
- package/lib/math/market.d.ts +6 -6
- package/lib/math/market.js +10 -9
- package/lib/math/oracles.d.ts +3 -0
- package/lib/math/oracles.js +26 -0
- package/lib/math/orders.d.ts +5 -0
- package/lib/math/orders.js +31 -1
- package/lib/math/position.d.ts +7 -4
- package/lib/math/position.js +27 -9
- package/lib/math/repeg.d.ts +22 -0
- package/lib/math/repeg.js +128 -0
- package/lib/math/trade.d.ts +13 -8
- package/lib/math/trade.js +85 -22
- package/lib/mockUSDCFaucet.js +87 -116
- package/lib/oracles/oracleClientCache.d.ts +8 -0
- package/lib/oracles/oracleClientCache.js +19 -0
- package/lib/oracles/pythClient.d.ts +3 -5
- package/lib/oracles/pythClient.js +12 -30
- package/lib/oracles/quoteAssetOracleClient.d.ts +9 -0
- package/lib/oracles/quoteAssetOracleClient.js +21 -0
- package/lib/oracles/switchboardClient.d.ts +3 -5
- package/lib/oracles/switchboardClient.js +29 -47
- package/lib/oracles/types.d.ts +7 -1
- package/lib/orders.d.ts +6 -6
- package/lib/orders.js +10 -9
- package/lib/slot/SlotSubscriber.d.ts +12 -0
- package/lib/slot/SlotSubscriber.js +23 -0
- package/lib/tx/retryTxSender.d.ts +2 -2
- package/lib/tx/retryTxSender.js +108 -123
- package/lib/tx/types.d.ts +5 -1
- package/lib/tx/utils.d.ts +1 -1
- package/lib/tx/utils.js +11 -2
- package/lib/types.d.ts +105 -98
- package/lib/types.js +13 -1
- package/lib/userName.d.ts +4 -0
- package/lib/userName.js +20 -0
- package/lib/util/computeUnits.js +10 -21
- package/lib/util/tps.js +11 -22
- package/lib/wallet.js +7 -20
- package/package.json +10 -3
- package/src/accounts/bulkAccountLoader.ts +26 -15
- package/src/accounts/bulkUserSubscription.ts +1 -45
- package/src/accounts/fetch.ts +33 -0
- package/src/accounts/pollingClearingHouseAccountSubscriber.ts +249 -214
- package/src/accounts/pollingOracleSubscriber.ts +16 -8
- package/src/accounts/pollingTokenAccountSubscriber.ts +11 -8
- package/src/accounts/pollingUserAccountSubscriber.ts +21 -86
- package/src/accounts/types.ts +41 -70
- package/src/accounts/webSocketAccountSubscriber.ts +33 -16
- package/src/accounts/webSocketClearingHouseAccountSubscriber.ts +190 -240
- package/src/accounts/webSocketUserAccountSubscriber.ts +11 -71
- package/src/addresses/marketAddresses.ts +18 -0
- package/src/addresses/pda.ts +118 -0
- package/src/admin.ts +209 -267
- package/src/clearingHouse.ts +921 -829
- package/src/clearingHouseConfig.ts +37 -0
- package/src/clearingHouseUser.ts +280 -127
- package/src/clearingHouseUserConfig.ts +18 -0
- package/src/config.ts +54 -1
- package/src/constants/banks.ts +43 -0
- package/src/constants/markets.ts +16 -207
- package/src/constants/numericConstants.ts +33 -5
- package/src/events/eventList.ts +94 -0
- package/src/events/eventSubscriber.ts +194 -0
- package/src/events/fetchLogs.ts +80 -0
- package/src/events/pollingLogProvider.ts +79 -0
- package/src/events/sort.ts +65 -0
- package/src/events/txEventCache.ts +74 -0
- package/src/events/types.ts +98 -0
- package/src/events/webSocketLogProvider.ts +38 -0
- package/src/examples/makeTradeExample.ts +20 -11
- package/src/factory/bigNum.ts +507 -0
- package/src/factory/oracleClient.ts +7 -4
- package/src/idl/clearing_house.json +1739 -2287
- package/src/index.ts +13 -4
- package/src/math/amm.ts +229 -245
- package/src/math/auction.ts +39 -0
- package/src/math/bankBalance.ts +112 -0
- package/src/math/conversion.ts +1 -11
- package/src/math/funding.ts +12 -9
- package/src/math/market.ts +37 -30
- package/src/math/oracles.ts +36 -0
- package/src/math/orders.ts +38 -0
- package/src/math/position.ts +48 -13
- package/src/math/repeg.ts +175 -0
- package/src/math/trade.ts +114 -36
- package/src/math/utils.js +27 -0
- package/src/math/utils.js.map +1 -0
- package/src/oracles/oracleClientCache.ts +20 -0
- package/src/oracles/pythClient.ts +5 -11
- package/src/oracles/quoteAssetOracleClient.ts +25 -0
- package/src/oracles/switchboardClient.ts +16 -24
- package/src/oracles/types.ts +8 -1
- package/src/orders.ts +35 -20
- package/src/slot/SlotSubscriber.ts +32 -0
- package/src/tx/retryTxSender.ts +6 -4
- package/src/tx/types.ts +6 -1
- package/src/tx/utils.ts +22 -3
- package/src/types.ts +108 -110
- package/src/userName.ts +20 -0
- package/src/util/computeUnits.js +17 -0
- package/src/util/computeUnits.js.map +1 -0
- package/tests/bn/test.ts +255 -0
- package/tsconfig.json +12 -12
- package/lib/addresses.d.ts +0 -9
- package/lib/addresses.js +0 -87
- package/lib/constants/accounts.d.ts +0 -15
- package/lib/constants/accounts.js +0 -18
- package/lib/factory/clearingHouse.d.ts +0 -35
- package/lib/factory/clearingHouse.js +0 -81
- package/lib/factory/clearingHouseUser.d.ts +0 -19
- package/lib/factory/clearingHouseUser.js +0 -34
- package/lib/math/insuranceFund.d.ts +0 -15
- package/lib/math/insuranceFund.js +0 -33
- package/lib/tx/defaultTxSender.d.ts +0 -8
- package/lib/tx/defaultTxSender.js +0 -12
- package/src/addresses.ts +0 -71
- package/src/constants/accounts.ts +0 -26
- package/src/factory/clearingHouse.ts +0 -173
- package/src/factory/clearingHouseUser.ts +0 -73
- package/src/math/insuranceFund.ts +0 -29
- package/src/tx/defaultTxSender.ts +0 -24
package/src/math/trade.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { MarketAccount, PositionDirection, SwapDirection } from '../types';
|
|
2
2
|
import { BN } from '@project-serum/anchor';
|
|
3
3
|
import { assert } from '../assert/assert';
|
|
4
4
|
import {
|
|
@@ -7,15 +7,22 @@ import {
|
|
|
7
7
|
AMM_TO_QUOTE_PRECISION_RATIO,
|
|
8
8
|
ZERO,
|
|
9
9
|
} from '../constants/numericConstants';
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
calculateBidPrice,
|
|
12
|
+
calculateAskPrice,
|
|
13
|
+
calculateMarkPrice,
|
|
14
|
+
} from './market';
|
|
11
15
|
import {
|
|
12
16
|
calculateAmmReservesAfterSwap,
|
|
13
17
|
calculatePrice,
|
|
14
18
|
getSwapDirection,
|
|
15
19
|
AssetType,
|
|
16
|
-
|
|
20
|
+
calculateUpdatedAMMSpreadReserves,
|
|
21
|
+
calculateQuoteAssetAmountSwapped,
|
|
17
22
|
} from './amm';
|
|
18
23
|
import { squareRootBN } from './utils';
|
|
24
|
+
import { isVariant } from '../types';
|
|
25
|
+
import { OraclePriceData } from '../oracles/types';
|
|
19
26
|
|
|
20
27
|
const MAXPCT = new BN(1000); //percentage units are [0,1000] => [0,1]
|
|
21
28
|
|
|
@@ -37,6 +44,8 @@ export type PriceImpactUnit =
|
|
|
37
44
|
* @param direction
|
|
38
45
|
* @param amount
|
|
39
46
|
* @param market
|
|
47
|
+
* @param inputAssetType which asset is being traded
|
|
48
|
+
* @param useSpread whether to consider spread with calculating slippage
|
|
40
49
|
* @return [pctAvgSlippage, pctMaxSlippage, entryPrice, newPrice]
|
|
41
50
|
*
|
|
42
51
|
* 'pctAvgSlippage' => the percentage change to entryPrice (average est slippage in execution) : Precision MARK_PRICE_PRECISION
|
|
@@ -50,10 +59,22 @@ export type PriceImpactUnit =
|
|
|
50
59
|
export function calculateTradeSlippage(
|
|
51
60
|
direction: PositionDirection,
|
|
52
61
|
amount: BN,
|
|
53
|
-
market:
|
|
54
|
-
inputAssetType: AssetType = 'quote'
|
|
62
|
+
market: MarketAccount,
|
|
63
|
+
inputAssetType: AssetType = 'quote',
|
|
64
|
+
oraclePriceData?: OraclePriceData,
|
|
65
|
+
useSpread = true
|
|
55
66
|
): [BN, BN, BN, BN] {
|
|
56
|
-
|
|
67
|
+
let oldPrice: BN;
|
|
68
|
+
|
|
69
|
+
if (useSpread && market.amm.baseSpread > 0) {
|
|
70
|
+
if (isVariant(direction, 'long')) {
|
|
71
|
+
oldPrice = calculateAskPrice(market, oraclePriceData);
|
|
72
|
+
} else {
|
|
73
|
+
oldPrice = calculateBidPrice(market, oraclePriceData);
|
|
74
|
+
}
|
|
75
|
+
} else {
|
|
76
|
+
oldPrice = calculateMarkPrice(market, oraclePriceData);
|
|
77
|
+
}
|
|
57
78
|
if (amount.eq(ZERO)) {
|
|
58
79
|
return [ZERO, ZERO, oldPrice, oldPrice];
|
|
59
80
|
}
|
|
@@ -61,25 +82,49 @@ export function calculateTradeSlippage(
|
|
|
61
82
|
direction,
|
|
62
83
|
amount,
|
|
63
84
|
market,
|
|
64
|
-
inputAssetType
|
|
85
|
+
inputAssetType,
|
|
86
|
+
oraclePriceData,
|
|
87
|
+
useSpread
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
const swapDirection = isVariant(direction, 'long')
|
|
91
|
+
? SwapDirection.REMOVE
|
|
92
|
+
: SwapDirection.ADD;
|
|
93
|
+
const quoteAssetAmountAcquired = calculateQuoteAssetAmountSwapped(
|
|
94
|
+
acquiredQuote.abs(),
|
|
95
|
+
market.amm.pegMultiplier,
|
|
96
|
+
swapDirection
|
|
65
97
|
);
|
|
66
98
|
|
|
67
|
-
const entryPrice =
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
99
|
+
const entryPrice = quoteAssetAmountAcquired
|
|
100
|
+
.mul(AMM_TO_QUOTE_PRECISION_RATIO)
|
|
101
|
+
.mul(MARK_PRICE_PRECISION)
|
|
102
|
+
.div(acquiredBase.abs());
|
|
103
|
+
|
|
104
|
+
let amm: Parameters<typeof calculateAmmReservesAfterSwap>[0];
|
|
105
|
+
if (useSpread && market.amm.baseSpread > 0) {
|
|
106
|
+
const { baseAssetReserve, quoteAssetReserve, sqrtK, newPeg } =
|
|
107
|
+
calculateUpdatedAMMSpreadReserves(market.amm, direction, oraclePriceData);
|
|
108
|
+
amm = {
|
|
109
|
+
baseAssetReserve,
|
|
110
|
+
quoteAssetReserve,
|
|
111
|
+
sqrtK: sqrtK,
|
|
112
|
+
pegMultiplier: newPeg,
|
|
113
|
+
};
|
|
114
|
+
} else {
|
|
115
|
+
amm = market.amm;
|
|
116
|
+
}
|
|
72
117
|
|
|
73
118
|
const newPrice = calculatePrice(
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
119
|
+
amm.baseAssetReserve.sub(acquiredBase),
|
|
120
|
+
amm.quoteAssetReserve.sub(acquiredQuote),
|
|
121
|
+
amm.pegMultiplier
|
|
77
122
|
);
|
|
78
123
|
|
|
79
124
|
if (direction == PositionDirection.SHORT) {
|
|
80
|
-
assert(newPrice.
|
|
125
|
+
assert(newPrice.lte(oldPrice));
|
|
81
126
|
} else {
|
|
82
|
-
assert(oldPrice.
|
|
127
|
+
assert(oldPrice.lte(newPrice));
|
|
83
128
|
}
|
|
84
129
|
|
|
85
130
|
const pctMaxSlippage = newPrice
|
|
@@ -104,14 +149,15 @@ export function calculateTradeSlippage(
|
|
|
104
149
|
* @param inputAssetType
|
|
105
150
|
* @param useSpread
|
|
106
151
|
* @return
|
|
107
|
-
* | 'acquiredBase' => positive/negative change in user's base : BN
|
|
152
|
+
* | 'acquiredBase' => positive/negative change in user's base : BN AMM_RESERVE_PRECISION
|
|
108
153
|
* | 'acquiredQuote' => positive/negative change in user's quote : BN TODO-PRECISION
|
|
109
154
|
*/
|
|
110
155
|
export function calculateTradeAcquiredAmounts(
|
|
111
156
|
direction: PositionDirection,
|
|
112
157
|
amount: BN,
|
|
113
|
-
market:
|
|
158
|
+
market: MarketAccount,
|
|
114
159
|
inputAssetType: AssetType = 'quote',
|
|
160
|
+
oraclePriceData: OraclePriceData,
|
|
115
161
|
useSpread = true
|
|
116
162
|
): [BN, BN] {
|
|
117
163
|
if (amount.eq(ZERO)) {
|
|
@@ -122,15 +168,13 @@ export function calculateTradeAcquiredAmounts(
|
|
|
122
168
|
|
|
123
169
|
let amm: Parameters<typeof calculateAmmReservesAfterSwap>[0];
|
|
124
170
|
if (useSpread && market.amm.baseSpread > 0) {
|
|
125
|
-
const { baseAssetReserve, quoteAssetReserve } =
|
|
126
|
-
market.amm,
|
|
127
|
-
direction
|
|
128
|
-
);
|
|
171
|
+
const { baseAssetReserve, quoteAssetReserve, sqrtK, newPeg } =
|
|
172
|
+
calculateUpdatedAMMSpreadReserves(market.amm, direction, oraclePriceData);
|
|
129
173
|
amm = {
|
|
130
174
|
baseAssetReserve,
|
|
131
175
|
quoteAssetReserve,
|
|
132
|
-
sqrtK:
|
|
133
|
-
pegMultiplier:
|
|
176
|
+
sqrtK: sqrtK,
|
|
177
|
+
pegMultiplier: newPeg,
|
|
134
178
|
};
|
|
135
179
|
} else {
|
|
136
180
|
amm = market.amm;
|
|
@@ -150,44 +194,65 @@ export function calculateTradeAcquiredAmounts(
|
|
|
150
194
|
* @param market
|
|
151
195
|
* @param targetPrice
|
|
152
196
|
* @param pct optional default is 100% gap filling, can set smaller.
|
|
197
|
+
* @param outputAssetType which asset to trade.
|
|
198
|
+
* @param useSpread whether or not to consider the spread when calculating the trade size
|
|
153
199
|
* @returns trade direction/size in order to push price to a targetPrice,
|
|
154
200
|
*
|
|
155
201
|
* [
|
|
156
|
-
* direction => direction of trade required,
|
|
202
|
+
* direction => direction of trade required, PositionDirection
|
|
157
203
|
* tradeSize => size of trade required, TODO-PRECISION
|
|
158
|
-
* entryPrice => the entry price for the trade,
|
|
159
|
-
* targetPrice => the target price
|
|
204
|
+
* entryPrice => the entry price for the trade, MARK_PRICE_PRECISION
|
|
205
|
+
* targetPrice => the target price MARK_PRICE_PRECISION
|
|
160
206
|
* ]
|
|
161
207
|
*/
|
|
162
208
|
export function calculateTargetPriceTrade(
|
|
163
|
-
market:
|
|
209
|
+
market: MarketAccount,
|
|
164
210
|
targetPrice: BN,
|
|
165
211
|
pct: BN = MAXPCT,
|
|
166
|
-
outputAssetType: AssetType = 'quote'
|
|
212
|
+
outputAssetType: AssetType = 'quote',
|
|
213
|
+
oraclePriceData?: OraclePriceData,
|
|
214
|
+
useSpread = true
|
|
167
215
|
): [PositionDirection, BN, BN, BN] {
|
|
168
216
|
assert(market.amm.baseAssetReserve.gt(ZERO));
|
|
169
217
|
assert(targetPrice.gt(ZERO));
|
|
170
218
|
assert(pct.lte(MAXPCT) && pct.gt(ZERO));
|
|
171
219
|
|
|
172
|
-
const markPriceBefore = calculateMarkPrice(market);
|
|
220
|
+
const markPriceBefore = calculateMarkPrice(market, oraclePriceData);
|
|
221
|
+
const bidPriceBefore = calculateBidPrice(market, oraclePriceData);
|
|
222
|
+
const askPriceBefore = calculateAskPrice(market, oraclePriceData);
|
|
173
223
|
|
|
224
|
+
let direction;
|
|
174
225
|
if (targetPrice.gt(markPriceBefore)) {
|
|
175
226
|
const priceGap = targetPrice.sub(markPriceBefore);
|
|
176
227
|
const priceGapScaled = priceGap.mul(pct).div(MAXPCT);
|
|
177
228
|
targetPrice = markPriceBefore.add(priceGapScaled);
|
|
229
|
+
direction = PositionDirection.LONG;
|
|
178
230
|
} else {
|
|
179
231
|
const priceGap = markPriceBefore.sub(targetPrice);
|
|
180
232
|
const priceGapScaled = priceGap.mul(pct).div(MAXPCT);
|
|
181
233
|
targetPrice = markPriceBefore.sub(priceGapScaled);
|
|
234
|
+
direction = PositionDirection.SHORT;
|
|
182
235
|
}
|
|
183
236
|
|
|
184
|
-
let direction;
|
|
185
237
|
let tradeSize;
|
|
186
238
|
let baseSize;
|
|
187
239
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
240
|
+
let baseAssetReserveBefore: BN;
|
|
241
|
+
let quoteAssetReserveBefore: BN;
|
|
242
|
+
|
|
243
|
+
let peg = market.amm.pegMultiplier;
|
|
244
|
+
|
|
245
|
+
if (useSpread && market.amm.baseSpread > 0) {
|
|
246
|
+
const { baseAssetReserve, quoteAssetReserve, newPeg } =
|
|
247
|
+
calculateUpdatedAMMSpreadReserves(market.amm, direction, oraclePriceData);
|
|
248
|
+
baseAssetReserveBefore = baseAssetReserve;
|
|
249
|
+
quoteAssetReserveBefore = quoteAssetReserve;
|
|
250
|
+
peg = newPeg;
|
|
251
|
+
} else {
|
|
252
|
+
baseAssetReserveBefore = market.amm.baseAssetReserve;
|
|
253
|
+
quoteAssetReserveBefore = market.amm.quoteAssetReserve;
|
|
254
|
+
}
|
|
255
|
+
|
|
191
256
|
const invariant = market.amm.sqrtK.mul(market.amm.sqrtK);
|
|
192
257
|
const k = invariant.mul(MARK_PRICE_PRECISION);
|
|
193
258
|
|
|
@@ -196,7 +261,20 @@ export function calculateTargetPriceTrade(
|
|
|
196
261
|
const biasModifier = new BN(1);
|
|
197
262
|
let markPriceAfter;
|
|
198
263
|
|
|
199
|
-
if (
|
|
264
|
+
if (
|
|
265
|
+
useSpread &&
|
|
266
|
+
targetPrice.lt(askPriceBefore) &&
|
|
267
|
+
targetPrice.gt(bidPriceBefore)
|
|
268
|
+
) {
|
|
269
|
+
// no trade, market is at target
|
|
270
|
+
if (markPriceBefore.gt(targetPrice)) {
|
|
271
|
+
direction = PositionDirection.SHORT;
|
|
272
|
+
} else {
|
|
273
|
+
direction = PositionDirection.LONG;
|
|
274
|
+
}
|
|
275
|
+
tradeSize = ZERO;
|
|
276
|
+
return [direction, tradeSize, targetPrice, targetPrice];
|
|
277
|
+
} else if (markPriceBefore.gt(targetPrice)) {
|
|
200
278
|
// overestimate y2
|
|
201
279
|
baseAssetReserveAfter = squareRootBN(
|
|
202
280
|
k.div(targetPrice).mul(peg).div(PEG_PRECISION).sub(biasModifier)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.squareRootBN = void 0;
|
|
4
|
+
const __1 = require("../");
|
|
5
|
+
const squareRootBN = (n, closeness = new __1.BN(1)) => {
|
|
6
|
+
// Assuming the sqrt of n as n only
|
|
7
|
+
let x = n;
|
|
8
|
+
// The closed guess will be stored in the root
|
|
9
|
+
let root;
|
|
10
|
+
// To count the number of iterations
|
|
11
|
+
let count = 0;
|
|
12
|
+
const TWO = new __1.BN(2);
|
|
13
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
14
|
+
while (count < Number.MAX_SAFE_INTEGER) {
|
|
15
|
+
count++;
|
|
16
|
+
// Calculate more closed x
|
|
17
|
+
root = x.add(n.div(x)).div(TWO);
|
|
18
|
+
// Check for closeness
|
|
19
|
+
if (x.sub(root).abs().lte(closeness))
|
|
20
|
+
break;
|
|
21
|
+
// Update root
|
|
22
|
+
x = root;
|
|
23
|
+
}
|
|
24
|
+
return root;
|
|
25
|
+
};
|
|
26
|
+
exports.squareRootBN = squareRootBN;
|
|
27
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["utils.ts"],"names":[],"mappings":";;;AAAA,2BAAyB;AAElB,MAAM,YAAY,GAAG,CAAC,CAAC,EAAE,SAAS,GAAG,IAAI,MAAE,CAAC,CAAC,CAAC,EAAE,EAAE;IACxD,mCAAmC;IACnC,IAAI,CAAC,GAAG,CAAC,CAAC;IAEV,8CAA8C;IAC9C,IAAI,IAAI,CAAC;IAET,oCAAoC;IACpC,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,MAAM,GAAG,GAAG,IAAI,MAAE,CAAC,CAAC,CAAC,CAAC;IAEtB,6DAA6D;IAC7D,OAAO,KAAK,GAAG,MAAM,CAAC,gBAAgB,EAAE;QACvC,KAAK,EAAE,CAAC;QAER,0BAA0B;QAC1B,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAEhC,sBAAsB;QACtB,IAAI,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,SAAS,CAAC;YAAE,MAAM;QAE5C,cAAc;QACd,CAAC,GAAG,IAAI,CAAC;KACT;IAED,OAAO,IAAI,CAAC;AACb,CAAC,CAAC;AA1BW,QAAA,YAAY,gBA0BvB"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { OracleClient } from './types';
|
|
2
|
+
import { OracleSource } from '../types';
|
|
3
|
+
import { getOracleClient } from '../factory/oracleClient';
|
|
4
|
+
import { Connection } from '@solana/web3.js';
|
|
5
|
+
|
|
6
|
+
export class OracleClientCache {
|
|
7
|
+
cache = new Map<string, OracleClient>();
|
|
8
|
+
public constructor() {}
|
|
9
|
+
|
|
10
|
+
public get(oracleSource: OracleSource, connection: Connection) {
|
|
11
|
+
const key = Object.keys(oracleSource)[0];
|
|
12
|
+
if (this.cache.has(key)) {
|
|
13
|
+
return this.cache.get(key);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const client = getOracleClient(oracleSource, connection);
|
|
17
|
+
this.cache.set(key, client);
|
|
18
|
+
return client;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -1,21 +1,16 @@
|
|
|
1
|
-
import { parsePriceData
|
|
1
|
+
import { parsePriceData } from '@pythnetwork/client';
|
|
2
2
|
import { Connection, PublicKey } from '@solana/web3.js';
|
|
3
|
-
import { OraclePriceData } from './types';
|
|
3
|
+
import { OracleClient, OraclePriceData } from './types';
|
|
4
4
|
import { BN } from '@project-serum/anchor';
|
|
5
5
|
import { MARK_PRICE_PRECISION, TEN } from '../constants/numericConstants';
|
|
6
6
|
|
|
7
|
-
export class PythClient {
|
|
7
|
+
export class PythClient implements OracleClient {
|
|
8
8
|
private connection: Connection;
|
|
9
9
|
|
|
10
10
|
public constructor(connection: Connection) {
|
|
11
11
|
this.connection = connection;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
public async getPriceData(pricePublicKey: PublicKey): Promise<PriceData> {
|
|
15
|
-
const account = await this.connection.getAccountInfo(pricePublicKey);
|
|
16
|
-
return parsePriceData(account.data);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
14
|
public async getOraclePriceData(
|
|
20
15
|
pricePublicKey: PublicKey
|
|
21
16
|
): Promise<OraclePriceData> {
|
|
@@ -23,9 +18,7 @@ export class PythClient {
|
|
|
23
18
|
return this.getOraclePriceDataFromBuffer(accountInfo.data);
|
|
24
19
|
}
|
|
25
20
|
|
|
26
|
-
public
|
|
27
|
-
buffer: Buffer
|
|
28
|
-
): Promise<OraclePriceData> {
|
|
21
|
+
public getOraclePriceDataFromBuffer(buffer: Buffer): OraclePriceData {
|
|
29
22
|
const priceData = parsePriceData(buffer);
|
|
30
23
|
return {
|
|
31
24
|
price: convertPythPrice(priceData.aggregate.price, priceData.exponent),
|
|
@@ -36,6 +29,7 @@ export class PythClient {
|
|
|
36
29
|
priceData.twac.value,
|
|
37
30
|
priceData.exponent
|
|
38
31
|
),
|
|
32
|
+
hasSufficientNumberOfDataPoints: true,
|
|
39
33
|
};
|
|
40
34
|
}
|
|
41
35
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { PublicKey } from '@solana/web3.js';
|
|
2
|
+
import { OracleClient, OraclePriceData } from './types';
|
|
3
|
+
import { BN } from '@project-serum/anchor';
|
|
4
|
+
import { MARK_PRICE_PRECISION } from '../constants/numericConstants';
|
|
5
|
+
|
|
6
|
+
export const QUOTE_ORACLE_PRICE_DATA: OraclePriceData = {
|
|
7
|
+
price: MARK_PRICE_PRECISION,
|
|
8
|
+
slot: new BN(0),
|
|
9
|
+
confidence: new BN(1),
|
|
10
|
+
hasSufficientNumberOfDataPoints: true,
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export class QuoteAssetOracleClient implements OracleClient {
|
|
14
|
+
public constructor() {}
|
|
15
|
+
|
|
16
|
+
public async getOraclePriceData(
|
|
17
|
+
_pricePublicKey: PublicKey
|
|
18
|
+
): Promise<OraclePriceData> {
|
|
19
|
+
return Promise.resolve(QUOTE_ORACLE_PRICE_DATA);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
public getOraclePriceDataFromBuffer(_buffer: Buffer): OraclePriceData {
|
|
23
|
+
return QUOTE_ORACLE_PRICE_DATA;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -1,25 +1,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
getSwitchboardPid,
|
|
3
|
-
SwitchboardDecimal,
|
|
4
|
-
} from '@switchboard-xyz/switchboard-v2';
|
|
1
|
+
import { SwitchboardDecimal } from '@switchboard-xyz/switchboard-v2';
|
|
5
2
|
import { Connection, Keypair, PublicKey } from '@solana/web3.js';
|
|
6
|
-
import { DriftEnv } from '../config';
|
|
7
3
|
import { BN, Program, Idl, AnchorProvider } from '@project-serum/anchor';
|
|
8
4
|
import { MARK_PRICE_PRECISION, TEN } from '../constants/numericConstants';
|
|
9
5
|
import { OracleClient, OraclePriceData } from './types';
|
|
10
6
|
import { Wallet } from '../wallet';
|
|
11
7
|
import switchboardV2Idl from '../idl/switchboard_v2.json';
|
|
12
8
|
|
|
13
|
-
|
|
14
|
-
const programMap = new Map<string, Program>();
|
|
9
|
+
let program: Program | undefined;
|
|
15
10
|
|
|
16
11
|
export class SwitchboardClient implements OracleClient {
|
|
17
12
|
connection: Connection;
|
|
18
|
-
env: DriftEnv;
|
|
19
13
|
|
|
20
|
-
public constructor(connection: Connection
|
|
14
|
+
public constructor(connection: Connection) {
|
|
21
15
|
this.connection = connection;
|
|
22
|
-
this.env = env;
|
|
23
16
|
}
|
|
24
17
|
|
|
25
18
|
public async getOraclePriceData(
|
|
@@ -29,10 +22,8 @@ export class SwitchboardClient implements OracleClient {
|
|
|
29
22
|
return this.getOraclePriceDataFromBuffer(accountInfo.data);
|
|
30
23
|
}
|
|
31
24
|
|
|
32
|
-
public
|
|
33
|
-
|
|
34
|
-
): Promise<OraclePriceData> {
|
|
35
|
-
const program = await this.getProgram();
|
|
25
|
+
public getOraclePriceDataFromBuffer(buffer: Buffer): OraclePriceData {
|
|
26
|
+
const program = this.getProgram();
|
|
36
27
|
|
|
37
28
|
const aggregatorAccountData =
|
|
38
29
|
program.account.aggregatorAccountData.coder.accounts.decode(
|
|
@@ -48,31 +39,32 @@ export class SwitchboardClient implements OracleClient {
|
|
|
48
39
|
.stdDeviation as SwitchboardDecimal
|
|
49
40
|
);
|
|
50
41
|
|
|
42
|
+
const hasSufficientNumberOfDataPoints =
|
|
43
|
+
aggregatorAccountData.latestConfirmedRound.numSuccess >=
|
|
44
|
+
aggregatorAccountData.minOracleResults;
|
|
45
|
+
|
|
51
46
|
const slot: BN = aggregatorAccountData.latestConfirmedRound.roundOpenSlot;
|
|
52
47
|
return {
|
|
53
48
|
price,
|
|
54
49
|
slot,
|
|
55
50
|
confidence,
|
|
51
|
+
hasSufficientNumberOfDataPoints,
|
|
56
52
|
};
|
|
57
53
|
}
|
|
58
54
|
|
|
59
|
-
public
|
|
60
|
-
if (
|
|
61
|
-
return
|
|
55
|
+
public getProgram(): Program {
|
|
56
|
+
if (program) {
|
|
57
|
+
return program;
|
|
62
58
|
}
|
|
63
59
|
|
|
64
|
-
|
|
65
|
-
programMap.set(this.env, program);
|
|
60
|
+
program = getSwitchboardProgram(this.connection);
|
|
66
61
|
return program;
|
|
67
62
|
}
|
|
68
63
|
}
|
|
69
64
|
|
|
70
|
-
|
|
71
|
-
env: DriftEnv,
|
|
72
|
-
connection: Connection
|
|
73
|
-
): Promise<Program> {
|
|
65
|
+
function getSwitchboardProgram(connection: Connection): Program {
|
|
74
66
|
const DEFAULT_KEYPAIR = Keypair.fromSeed(new Uint8Array(32).fill(1));
|
|
75
|
-
const programId =
|
|
67
|
+
const programId = PublicKey.default;
|
|
76
68
|
const wallet = new Wallet(DEFAULT_KEYPAIR);
|
|
77
69
|
const provider = new AnchorProvider(connection, wallet, {});
|
|
78
70
|
|
package/src/oracles/types.ts
CHANGED
|
@@ -1,15 +1,22 @@
|
|
|
1
1
|
import { BN } from '@project-serum/anchor';
|
|
2
2
|
import { PublicKey } from '@solana/web3.js';
|
|
3
|
+
import { OracleSource } from '../types';
|
|
3
4
|
|
|
4
5
|
export type OraclePriceData = {
|
|
5
6
|
price: BN;
|
|
6
7
|
slot: BN;
|
|
7
8
|
confidence: BN;
|
|
9
|
+
hasSufficientNumberOfDataPoints: boolean;
|
|
8
10
|
twap?: BN;
|
|
9
11
|
twapConfidence?: BN;
|
|
10
12
|
};
|
|
11
13
|
|
|
14
|
+
export type OracleInfo = {
|
|
15
|
+
publicKey: PublicKey;
|
|
16
|
+
source: OracleSource;
|
|
17
|
+
};
|
|
18
|
+
|
|
12
19
|
export interface OracleClient {
|
|
13
|
-
getOraclePriceDataFromBuffer(buffer: Buffer):
|
|
20
|
+
getOraclePriceDataFromBuffer(buffer: Buffer): OraclePriceData;
|
|
14
21
|
getOraclePriceData(publicKey: PublicKey): Promise<OraclePriceData>;
|
|
15
22
|
}
|
package/src/orders.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
isVariant,
|
|
3
|
-
|
|
3
|
+
MarketAccount,
|
|
4
4
|
Order,
|
|
5
5
|
PositionDirection,
|
|
6
6
|
SwapDirection,
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
calculateSpreadReserves,
|
|
15
15
|
ClearingHouseUser,
|
|
16
16
|
isOrderRiskIncreasingInSameDirection,
|
|
17
|
+
standardizeBaseAssetAmount,
|
|
17
18
|
TEN_THOUSAND,
|
|
18
19
|
} from '.';
|
|
19
20
|
import {
|
|
@@ -36,9 +37,9 @@ import { OraclePriceData } from '.';
|
|
|
36
37
|
export function calculateNewStateAfterOrder(
|
|
37
38
|
userAccount: UserAccount,
|
|
38
39
|
userPosition: UserPosition,
|
|
39
|
-
market:
|
|
40
|
+
market: MarketAccount,
|
|
40
41
|
order: Order
|
|
41
|
-
): [UserAccount, UserPosition,
|
|
42
|
+
): [UserAccount, UserPosition, MarketAccount] | null {
|
|
42
43
|
if (isVariant(order.status, 'init')) {
|
|
43
44
|
return null;
|
|
44
45
|
}
|
|
@@ -47,7 +48,7 @@ export function calculateNewStateAfterOrder(
|
|
|
47
48
|
market,
|
|
48
49
|
order
|
|
49
50
|
);
|
|
50
|
-
if (baseAssetAmountToTrade.lt(market.amm.
|
|
51
|
+
if (baseAssetAmountToTrade.lt(market.amm.baseAssetAmountStepSize)) {
|
|
51
52
|
return null;
|
|
52
53
|
}
|
|
53
54
|
|
|
@@ -155,8 +156,8 @@ export function calculateNewStateAfterOrder(
|
|
|
155
156
|
}
|
|
156
157
|
|
|
157
158
|
function calculateAmountSwapped(
|
|
158
|
-
marketBefore:
|
|
159
|
-
marketAfter:
|
|
159
|
+
marketBefore: MarketAccount,
|
|
160
|
+
marketAfter: MarketAccount
|
|
160
161
|
): { quoteAssetAmountSwapped: BN; baseAssetAmountSwapped: BN } {
|
|
161
162
|
return {
|
|
162
163
|
quoteAssetAmountSwapped: marketBefore.amm.quoteAssetReserve
|
|
@@ -172,7 +173,7 @@ function calculateAmountSwapped(
|
|
|
172
173
|
}
|
|
173
174
|
|
|
174
175
|
export function calculateBaseAssetAmountMarketCanExecute(
|
|
175
|
-
market:
|
|
176
|
+
market: MarketAccount,
|
|
176
177
|
order: Order,
|
|
177
178
|
oraclePriceData?: OraclePriceData
|
|
178
179
|
): BN {
|
|
@@ -189,7 +190,7 @@ export function calculateBaseAssetAmountMarketCanExecute(
|
|
|
189
190
|
}
|
|
190
191
|
|
|
191
192
|
export function calculateAmountToTradeForLimit(
|
|
192
|
-
market:
|
|
193
|
+
market: MarketAccount,
|
|
193
194
|
order: Order,
|
|
194
195
|
oraclePriceData?: OraclePriceData
|
|
195
196
|
): BN {
|
|
@@ -213,8 +214,12 @@ export function calculateAmountToTradeForLimit(
|
|
|
213
214
|
const [maxAmountToTrade, direction] = calculateMaxBaseAssetAmountToTrade(
|
|
214
215
|
market.amm,
|
|
215
216
|
limitPrice,
|
|
216
|
-
order.direction
|
|
217
|
-
|
|
217
|
+
order.direction
|
|
218
|
+
);
|
|
219
|
+
|
|
220
|
+
const baseAssetAmount = standardizeBaseAssetAmount(
|
|
221
|
+
maxAmountToTrade,
|
|
222
|
+
market.amm.baseAssetAmountStepSize
|
|
218
223
|
);
|
|
219
224
|
|
|
220
225
|
// Check that directions are the same
|
|
@@ -223,13 +228,13 @@ export function calculateAmountToTradeForLimit(
|
|
|
223
228
|
return ZERO;
|
|
224
229
|
}
|
|
225
230
|
|
|
226
|
-
return
|
|
231
|
+
return baseAssetAmount.gt(order.baseAssetAmount)
|
|
227
232
|
? order.baseAssetAmount
|
|
228
|
-
:
|
|
233
|
+
: baseAssetAmount;
|
|
229
234
|
}
|
|
230
235
|
|
|
231
236
|
export function calculateAmountToTradeForTriggerLimit(
|
|
232
|
-
market:
|
|
237
|
+
market: MarketAccount,
|
|
233
238
|
order: Order
|
|
234
239
|
): BN {
|
|
235
240
|
if (order.baseAssetAmountFilled.eq(ZERO)) {
|
|
@@ -256,7 +261,7 @@ function isSameDirection(
|
|
|
256
261
|
}
|
|
257
262
|
|
|
258
263
|
function calculateAmountToTradeForTriggerMarket(
|
|
259
|
-
market:
|
|
264
|
+
market: MarketAccount,
|
|
260
265
|
order: Order
|
|
261
266
|
): BN {
|
|
262
267
|
return isTriggerConditionSatisfied(market, order)
|
|
@@ -264,8 +269,12 @@ function calculateAmountToTradeForTriggerMarket(
|
|
|
264
269
|
: ZERO;
|
|
265
270
|
}
|
|
266
271
|
|
|
267
|
-
function isTriggerConditionSatisfied(
|
|
268
|
-
|
|
272
|
+
function isTriggerConditionSatisfied(
|
|
273
|
+
market: MarketAccount,
|
|
274
|
+
order: Order,
|
|
275
|
+
oraclePriceData?: OraclePriceData
|
|
276
|
+
): boolean {
|
|
277
|
+
const markPrice = calculateMarkPrice(market, oraclePriceData);
|
|
269
278
|
if (isVariant(order.triggerCondition, 'above')) {
|
|
270
279
|
return markPrice.gt(order.triggerPrice);
|
|
271
280
|
} else {
|
|
@@ -274,9 +283,10 @@ function isTriggerConditionSatisfied(market: Market, order: Order): boolean {
|
|
|
274
283
|
}
|
|
275
284
|
|
|
276
285
|
export function calculateBaseAssetAmountUserCanExecute(
|
|
277
|
-
market:
|
|
286
|
+
market: MarketAccount,
|
|
278
287
|
order: Order,
|
|
279
|
-
user: ClearingHouseUser
|
|
288
|
+
user: ClearingHouseUser,
|
|
289
|
+
oraclePriceData?: OraclePriceData
|
|
280
290
|
): BN {
|
|
281
291
|
const maxLeverage = user.getMaxLeverage(order.marketIndex, 'Initial');
|
|
282
292
|
const freeCollateral = user.getFreeCollateral();
|
|
@@ -287,7 +297,11 @@ export function calculateBaseAssetAmountUserCanExecute(
|
|
|
287
297
|
const position =
|
|
288
298
|
user.getUserPosition(order.marketIndex) ||
|
|
289
299
|
user.getEmptyPosition(order.marketIndex);
|
|
290
|
-
const positionValue = calculateBaseAssetValue(
|
|
300
|
+
const positionValue = calculateBaseAssetValue(
|
|
301
|
+
market,
|
|
302
|
+
position,
|
|
303
|
+
oraclePriceData
|
|
304
|
+
);
|
|
291
305
|
quoteAssetAmount = freeCollateral
|
|
292
306
|
.mul(maxLeverage)
|
|
293
307
|
.div(TEN_THOUSAND)
|
|
@@ -307,7 +321,8 @@ export function calculateBaseAssetAmountUserCanExecute(
|
|
|
307
321
|
if (useSpread) {
|
|
308
322
|
const { baseAssetReserve, quoteAssetReserve } = calculateSpreadReserves(
|
|
309
323
|
market.amm,
|
|
310
|
-
order.direction
|
|
324
|
+
order.direction,
|
|
325
|
+
oraclePriceData
|
|
311
326
|
);
|
|
312
327
|
amm = {
|
|
313
328
|
baseAssetReserve,
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Connection } from '@solana/web3.js';
|
|
2
|
+
|
|
3
|
+
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
4
|
+
type SlotSubscriberConfig = {}; // for future customization
|
|
5
|
+
|
|
6
|
+
export class SlotSubscriber {
|
|
7
|
+
currentSlot: number;
|
|
8
|
+
subscriptionId: number;
|
|
9
|
+
|
|
10
|
+
public constructor(
|
|
11
|
+
private connection: Connection,
|
|
12
|
+
_config?: SlotSubscriberConfig
|
|
13
|
+
) {}
|
|
14
|
+
|
|
15
|
+
public async subscribe(): Promise<void> {
|
|
16
|
+
this.currentSlot = await this.connection.getSlot('confirmed');
|
|
17
|
+
|
|
18
|
+
this.subscriptionId = this.connection.onSlotChange((slotInfo) => {
|
|
19
|
+
this.currentSlot = slotInfo.slot;
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
public getSlot(): number {
|
|
24
|
+
return this.currentSlot;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
public async unsubscribe(): Promise<void> {
|
|
28
|
+
if (this.subscriptionId) {
|
|
29
|
+
await this.connection.removeSlotChangeListener(this.subscriptionId);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|