@drift-labs/sdk 0.2.0-master.26 → 0.2.0-master.28
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/pollingClearingHouseAccountSubscriber.d.ts +1 -0
- package/lib/accounts/pollingClearingHouseAccountSubscriber.js +3 -0
- package/lib/accounts/types.d.ts +2 -0
- package/lib/accounts/webSocketClearingHouseAccountSubscriber.d.ts +1 -0
- package/lib/accounts/webSocketClearingHouseAccountSubscriber.js +3 -0
- package/lib/admin.d.ts +5 -2
- package/lib/admin.js +30 -4
- package/lib/clearingHouse.d.ts +3 -0
- package/lib/clearingHouse.js +19 -1
- package/lib/clearingHouseUser.d.ts +11 -9
- package/lib/clearingHouseUser.js +184 -108
- package/lib/config.js +1 -1
- package/lib/dlob/DLOB.d.ts +73 -0
- package/lib/dlob/DLOB.js +557 -0
- package/lib/dlob/DLOBNode.d.ts +52 -0
- package/lib/dlob/DLOBNode.js +82 -0
- package/lib/dlob/NodeList.d.ts +26 -0
- package/lib/dlob/NodeList.js +138 -0
- package/lib/idl/clearing_house.json +123 -203
- package/lib/index.d.ts +5 -0
- package/lib/index.js +5 -0
- package/lib/math/market.d.ts +3 -2
- package/lib/math/market.js +17 -12
- package/lib/math/orders.d.ts +3 -3
- package/lib/math/orders.js +31 -16
- package/lib/math/position.d.ts +3 -3
- package/lib/math/position.js +23 -16
- package/lib/math/repeg.js +8 -0
- package/lib/math/spotBalance.d.ts +3 -0
- package/lib/math/spotBalance.js +18 -1
- package/lib/math/spotPosition.d.ts +5 -1
- package/lib/math/spotPosition.js +16 -1
- package/lib/types.d.ts +25 -24
- package/lib/types.js +9 -3
- package/lib/userMap/userMap.d.ts +25 -0
- package/lib/userMap/userMap.js +73 -0
- package/lib/userMap/userStatsMap.d.ts +19 -0
- package/lib/userMap/userStatsMap.js +68 -0
- package/package.json +6 -3
- package/src/accounts/pollingClearingHouseAccountSubscriber.ts +4 -0
- package/src/accounts/types.ts +1 -0
- package/src/accounts/webSocketClearingHouseAccountSubscriber.ts +6 -0
- package/src/admin.ts +50 -8
- package/src/clearingHouse.ts +30 -1
- package/src/clearingHouseUser.ts +366 -167
- package/src/config.ts +1 -1
- package/src/dlob/DLOB.ts +884 -0
- package/src/dlob/DLOBNode.ts +163 -0
- package/src/dlob/NodeList.ts +185 -0
- package/src/idl/clearing_house.json +123 -203
- package/src/index.ts +5 -0
- package/src/math/market.ts +22 -13
- package/src/math/orders.ts +29 -21
- package/src/math/position.ts +36 -22
- package/src/math/repeg.ts +9 -0
- package/src/math/spotBalance.ts +26 -0
- package/src/math/spotPosition.ts +42 -1
- package/src/types.ts +26 -24
- package/src/userMap/userMap.ts +100 -0
- package/src/userMap/userStatsMap.ts +110 -0
- package/tests/bn/test.ts +2 -3
- package/tests/dlob/helpers.ts +322 -0
- package/tests/dlob/test.ts +2865 -0
- package/my-script/.env +0 -7
- package/my-script/getUserStats.ts +0 -106
- package/my-script/multiConnections.ts +0 -119
- package/my-script/test-regex.ts +0 -11
- package/my-script/utils.ts +0 -52
- package/src/accounts/bulkAccountLoader.js +0 -249
- package/src/accounts/bulkUserStatsSubscription.js +0 -75
- package/src/accounts/bulkUserSubscription.js +0 -75
- package/src/accounts/fetch.js +0 -92
- package/src/accounts/pollingClearingHouseAccountSubscriber.js +0 -465
- package/src/accounts/pollingOracleSubscriber.js +0 -156
- package/src/accounts/pollingTokenAccountSubscriber.js +0 -141
- package/src/accounts/pollingUserAccountSubscriber.js +0 -208
- package/src/accounts/pollingUserStatsAccountSubscriber.js +0 -208
- package/src/accounts/types.js +0 -28
- package/src/accounts/utils.js +0 -7
- package/src/accounts/webSocketAccountSubscriber.js +0 -138
- package/src/accounts/webSocketClearingHouseAccountSubscriber.js +0 -433
- package/src/accounts/webSocketUserAccountSubscriber.js +0 -113
- package/src/accounts/webSocketUserStatsAccountSubsriber.js +0 -113
- package/src/addresses/pda.js +0 -186
- package/src/admin.js +0 -1284
- package/src/assert/assert.js +0 -9
- package/src/clearingHouse.js +0 -3433
- package/src/clearingHouseConfig.js +0 -2
- package/src/clearingHouseUser.js +0 -874
- package/src/clearingHouseUserConfig.js +0 -2
- package/src/clearingHouseUserStats.js +0 -115
- package/src/clearingHouseUserStatsConfig.js +0 -2
- package/src/config.js +0 -80
- package/src/constants/numericConstants.js +0 -48
- package/src/constants/perpMarkets.js +0 -42
- package/src/constants/spotMarkets.js +0 -51
- package/src/events/eventList.js +0 -120
- package/src/events/eventSubscriber.js +0 -202
- package/src/events/fetchLogs.js +0 -117
- package/src/events/pollingLogProvider.js +0 -113
- package/src/events/sort.js +0 -41
- package/src/events/txEventCache.js +0 -74
- package/src/events/types.js +0 -25
- package/src/events/webSocketLogProvider.js +0 -76
- package/src/factory/bigNum.js +0 -393
- package/src/factory/oracleClient.js +0 -20
- package/src/index.js +0 -75
- package/src/math/amm.js +0 -422
- package/src/math/auction.js +0 -42
- package/src/math/conversion.js +0 -12
- package/src/math/funding.js +0 -296
- package/src/math/insurance.js +0 -27
- package/src/math/margin.js +0 -77
- package/src/math/market.js +0 -105
- package/src/math/oracles.js +0 -56
- package/src/math/orders.js +0 -153
- package/src/math/position.js +0 -172
- package/src/math/repeg.js +0 -128
- package/src/math/spotBalance.js +0 -176
- package/src/math/spotMarket.js +0 -8
- package/src/math/spotPosition.js +0 -8
- package/src/math/trade.js +0 -260
- package/src/math/utils.js +0 -27
- package/src/oracles/oracleClientCache.js +0 -20
- package/src/oracles/pythClient.js +0 -81
- package/src/oracles/quoteAssetOracleClient.js +0 -63
- package/src/oracles/switchboardClient.js +0 -101
- package/src/oracles/types.js +0 -2
- package/src/orderParams.js +0 -28
- package/src/serum/serumSubscriber.js +0 -102
- package/src/serum/types.js +0 -2
- package/src/slot/SlotSubscriber.js +0 -86
- package/src/token/index.js +0 -38
- package/src/tokenFaucet.js +0 -323
- package/src/tx/retryTxSender.js +0 -280
- package/src/tx/types.js +0 -2
- package/src/tx/utils.js +0 -18
- package/src/types.js +0 -216
- package/src/userName.js +0 -20
- package/src/util/computeUnits.js +0 -62
- package/src/util/promiseTimeout.js +0 -14
- package/src/util/tps.js +0 -61
- package/src/wallet.js +0 -72
package/src/math/orders.ts
CHANGED
|
@@ -131,9 +131,9 @@ export function standardizeBaseAssetAmount(
|
|
|
131
131
|
|
|
132
132
|
export function getLimitPrice(
|
|
133
133
|
order: Order,
|
|
134
|
-
market: PerpMarketAccount,
|
|
135
134
|
oraclePriceData: OraclePriceData,
|
|
136
|
-
slot: number
|
|
135
|
+
slot: number,
|
|
136
|
+
perpMarket?: PerpMarketAccount
|
|
137
137
|
): BN {
|
|
138
138
|
let limitPrice;
|
|
139
139
|
if (!order.oraclePriceOffset.eq(ZERO)) {
|
|
@@ -143,14 +143,26 @@ export function getLimitPrice(
|
|
|
143
143
|
limitPrice = getAuctionPrice(order, slot);
|
|
144
144
|
} else if (!order.price.eq(ZERO)) {
|
|
145
145
|
limitPrice = order.price;
|
|
146
|
-
} else if (isVariant(order.direction, 'long')) {
|
|
147
|
-
const askPrice = calculateAskPrice(market, oraclePriceData);
|
|
148
|
-
const delta = askPrice.div(new BN(market.amm.maxSlippageRatio));
|
|
149
|
-
limitPrice = askPrice.add(delta);
|
|
150
146
|
} else {
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
147
|
+
if (perpMarket) {
|
|
148
|
+
if (isVariant(order.direction, 'long')) {
|
|
149
|
+
const askPrice = calculateAskPrice(perpMarket, oraclePriceData);
|
|
150
|
+
const delta = askPrice.div(new BN(perpMarket.amm.maxSlippageRatio));
|
|
151
|
+
limitPrice = askPrice.add(delta);
|
|
152
|
+
} else {
|
|
153
|
+
const bidPrice = calculateBidPrice(perpMarket, oraclePriceData);
|
|
154
|
+
const delta = bidPrice.div(new BN(perpMarket.amm.maxSlippageRatio));
|
|
155
|
+
limitPrice = bidPrice.sub(delta);
|
|
156
|
+
}
|
|
157
|
+
} else {
|
|
158
|
+
// check oracle validity?
|
|
159
|
+
const oraclePrice1Pct = oraclePriceData.price.div(new BN(100));
|
|
160
|
+
if (isVariant(order.direction, 'long')) {
|
|
161
|
+
limitPrice = oraclePriceData.price.add(oraclePrice1Pct);
|
|
162
|
+
} else {
|
|
163
|
+
limitPrice = oraclePriceData.price.sub(oraclePrice1Pct);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
154
166
|
}
|
|
155
167
|
} else {
|
|
156
168
|
limitPrice = order.price;
|
|
@@ -163,8 +175,7 @@ export function isFillableByVAMM(
|
|
|
163
175
|
order: Order,
|
|
164
176
|
market: PerpMarketAccount,
|
|
165
177
|
oraclePriceData: OraclePriceData,
|
|
166
|
-
slot: number
|
|
167
|
-
maxAuctionDuration: number
|
|
178
|
+
slot: number
|
|
168
179
|
): boolean {
|
|
169
180
|
return (
|
|
170
181
|
(isAuctionComplete(order, slot) &&
|
|
@@ -174,7 +185,7 @@ export function isFillableByVAMM(
|
|
|
174
185
|
oraclePriceData,
|
|
175
186
|
slot
|
|
176
187
|
).eq(ZERO)) ||
|
|
177
|
-
isOrderExpired(order, slot
|
|
188
|
+
isOrderExpired(order, slot)
|
|
178
189
|
);
|
|
179
190
|
}
|
|
180
191
|
|
|
@@ -191,7 +202,7 @@ export function calculateBaseAssetAmountForAmmToFulfill(
|
|
|
191
202
|
return ZERO;
|
|
192
203
|
}
|
|
193
204
|
|
|
194
|
-
const limitPrice = getLimitPrice(order,
|
|
205
|
+
const limitPrice = getLimitPrice(order, oraclePriceData, slot, market);
|
|
195
206
|
const baseAssetAmount = calculateBaseAssetAmountToFillUpToLimitPrice(
|
|
196
207
|
order,
|
|
197
208
|
market,
|
|
@@ -246,17 +257,14 @@ function isSameDirection(
|
|
|
246
257
|
);
|
|
247
258
|
}
|
|
248
259
|
|
|
249
|
-
export function isOrderExpired(
|
|
250
|
-
order: Order,
|
|
251
|
-
slot: number,
|
|
252
|
-
maxAuctionDuration: number
|
|
253
|
-
): boolean {
|
|
260
|
+
export function isOrderExpired(order: Order, slot: number): boolean {
|
|
254
261
|
if (
|
|
255
|
-
|
|
256
|
-
!isVariant(order.status, 'open')
|
|
262
|
+
isOneOfVariant(order.orderType, ['triggerMarket', 'triggerLimit']) ||
|
|
263
|
+
!isVariant(order.status, 'open') ||
|
|
264
|
+
order.timeInForce === 0
|
|
257
265
|
) {
|
|
258
266
|
return false;
|
|
259
267
|
}
|
|
260
268
|
|
|
261
|
-
return new BN(slot).sub(order.slot).gt(new BN(
|
|
269
|
+
return new BN(slot).sub(order.slot).gt(new BN(order.timeInForce));
|
|
262
270
|
}
|
package/src/math/position.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BN } from '../';
|
|
1
|
+
import { BN, SpotMarketAccount } from '../';
|
|
2
2
|
import {
|
|
3
3
|
AMM_RESERVE_PRECISION,
|
|
4
4
|
AMM_TIMES_PEG_TO_QUOTE_PRECISION_RATIO,
|
|
@@ -17,8 +17,8 @@ import {
|
|
|
17
17
|
calculateAmmReservesAfterSwap,
|
|
18
18
|
getSwapDirection,
|
|
19
19
|
} from './amm';
|
|
20
|
-
|
|
21
20
|
import { calculateBaseAssetValueWithOracle } from './margin';
|
|
21
|
+
import { calculateNetUserPnlImbalance } from './market';
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
24
|
* calculateBaseAssetValue
|
|
@@ -31,7 +31,9 @@ import { calculateBaseAssetValueWithOracle } from './margin';
|
|
|
31
31
|
export function calculateBaseAssetValue(
|
|
32
32
|
market: PerpMarketAccount,
|
|
33
33
|
userPosition: PerpPosition,
|
|
34
|
-
oraclePriceData: OraclePriceData
|
|
34
|
+
oraclePriceData: OraclePriceData,
|
|
35
|
+
useSpread = true,
|
|
36
|
+
skipUpdate = false
|
|
35
37
|
): BN {
|
|
36
38
|
if (userPosition.baseAssetAmount.eq(ZERO)) {
|
|
37
39
|
return ZERO;
|
|
@@ -40,21 +42,25 @@ export function calculateBaseAssetValue(
|
|
|
40
42
|
const directionToClose = findDirectionToClose(userPosition);
|
|
41
43
|
let prepegAmm: Parameters<typeof calculateAmmReservesAfterSwap>[0];
|
|
42
44
|
|
|
43
|
-
if (
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
45
|
+
if (!skipUpdate) {
|
|
46
|
+
if (market.amm.baseSpread > 0 && useSpread) {
|
|
47
|
+
const { baseAssetReserve, quoteAssetReserve, sqrtK, newPeg } =
|
|
48
|
+
calculateUpdatedAMMSpreadReserves(
|
|
49
|
+
market.amm,
|
|
50
|
+
directionToClose,
|
|
51
|
+
oraclePriceData
|
|
52
|
+
);
|
|
53
|
+
prepegAmm = {
|
|
54
|
+
baseAssetReserve,
|
|
55
|
+
quoteAssetReserve,
|
|
56
|
+
sqrtK: sqrtK,
|
|
57
|
+
pegMultiplier: newPeg,
|
|
58
|
+
};
|
|
59
|
+
} else {
|
|
60
|
+
prepegAmm = calculateUpdatedAMM(market.amm, oraclePriceData);
|
|
61
|
+
}
|
|
56
62
|
} else {
|
|
57
|
-
prepegAmm =
|
|
63
|
+
prepegAmm = market.amm;
|
|
58
64
|
}
|
|
59
65
|
|
|
60
66
|
const [newQuoteAssetReserve, _] = calculateAmmReservesAfterSwap(
|
|
@@ -124,8 +130,9 @@ export function calculatePositionPNL(
|
|
|
124
130
|
return pnl;
|
|
125
131
|
}
|
|
126
132
|
|
|
127
|
-
export function
|
|
133
|
+
export function calculateClaimablePnl(
|
|
128
134
|
market: PerpMarketAccount,
|
|
135
|
+
spotMarket: SpotMarketAccount,
|
|
129
136
|
perpPosition: PerpPosition,
|
|
130
137
|
oraclePriceData: OraclePriceData
|
|
131
138
|
): BN {
|
|
@@ -136,16 +143,23 @@ export function calculateUnsettledPnl(
|
|
|
136
143
|
oraclePriceData
|
|
137
144
|
);
|
|
138
145
|
|
|
139
|
-
|
|
146
|
+
const fundingPnL = calculatePositionFundingPNL(market, perpPosition).div(
|
|
147
|
+
PRICE_TO_QUOTE_PRECISION
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
let unsettledPnl = unrealizedPnl.add(fundingPnL);
|
|
140
151
|
if (unrealizedPnl.gt(ZERO)) {
|
|
141
|
-
const
|
|
142
|
-
|
|
152
|
+
const excessPnlPool = BN.max(
|
|
153
|
+
ZERO,
|
|
154
|
+
calculateNetUserPnlImbalance(market, spotMarket, oraclePriceData).mul(
|
|
155
|
+
new BN(-1)
|
|
156
|
+
)
|
|
143
157
|
);
|
|
144
158
|
|
|
145
159
|
const maxPositivePnl = BN.max(
|
|
146
160
|
perpPosition.quoteAssetAmount
|
|
147
161
|
.sub(perpPosition.quoteEntryAmount)
|
|
148
|
-
.add(
|
|
162
|
+
.add(excessPnlPool),
|
|
149
163
|
ZERO
|
|
150
164
|
);
|
|
151
165
|
|
package/src/math/repeg.ts
CHANGED
|
@@ -98,6 +98,15 @@ export function calculateBudgetedKBN(
|
|
|
98
98
|
.div(QUOTE_PRECISION);
|
|
99
99
|
const denom2 = pegged_y_d_d;
|
|
100
100
|
|
|
101
|
+
// protocol is spending to increase k
|
|
102
|
+
if (C.lt(ZERO)) {
|
|
103
|
+
// thus denom1 is negative and solution is unstable
|
|
104
|
+
if (denom1.lt(pegged_y_d_d.abs())) {
|
|
105
|
+
console.log('budget cost exceeds stable K solution');
|
|
106
|
+
return [new BN(10000), new BN(1)];
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
101
110
|
const numerator = numer1.sub(numer2).div(AMM_TO_QUOTE_PRECISION_RATIO);
|
|
102
111
|
const denominator = denom1.add(denom2).div(AMM_TO_QUOTE_PRECISION_RATIO);
|
|
103
112
|
|
package/src/math/spotBalance.ts
CHANGED
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
calculateSizeDiscountAssetWeight,
|
|
20
20
|
calculateSizePremiumLiabilityWeight,
|
|
21
21
|
} from './margin';
|
|
22
|
+
import { OraclePriceData } from '../oracles/types';
|
|
22
23
|
|
|
23
24
|
export function getBalance(
|
|
24
25
|
tokenAmount: BN,
|
|
@@ -54,6 +55,31 @@ export function getTokenAmount(
|
|
|
54
55
|
return balanceAmount.mul(cumulativeInterest).div(precisionDecrease);
|
|
55
56
|
}
|
|
56
57
|
|
|
58
|
+
export function getSignedTokenAmount(
|
|
59
|
+
tokenAmount: BN,
|
|
60
|
+
balanceType: SpotBalanceType
|
|
61
|
+
): BN {
|
|
62
|
+
if (isVariant(balanceType, 'deposit')) {
|
|
63
|
+
return tokenAmount;
|
|
64
|
+
} else {
|
|
65
|
+
return tokenAmount.abs().neg();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function getTokenValue(
|
|
70
|
+
tokenAmount: BN,
|
|
71
|
+
spotDecimals: number,
|
|
72
|
+
oraclePriceData: OraclePriceData
|
|
73
|
+
): BN {
|
|
74
|
+
if (tokenAmount.eq(ZERO)) {
|
|
75
|
+
return ZERO;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const precisionDecrease = TEN.pow(new BN(10 + spotDecimals - 6));
|
|
79
|
+
|
|
80
|
+
return tokenAmount.mul(oraclePriceData.price).div(precisionDecrease);
|
|
81
|
+
}
|
|
82
|
+
|
|
57
83
|
export function calculateAssetWeight(
|
|
58
84
|
balanceAmount: BN,
|
|
59
85
|
spotMarket: SpotMarketAccount,
|
package/src/math/spotPosition.ts
CHANGED
|
@@ -1,6 +1,47 @@
|
|
|
1
|
-
import { SpotPosition } from '../types';
|
|
1
|
+
import { SpotMarketAccount, SpotPosition } from '../types';
|
|
2
2
|
import { ZERO } from '../constants/numericConstants';
|
|
3
|
+
import { BN } from '@project-serum/anchor';
|
|
4
|
+
import {
|
|
5
|
+
getSignedTokenAmount,
|
|
6
|
+
getTokenAmount,
|
|
7
|
+
getTokenValue,
|
|
8
|
+
} from './spotBalance';
|
|
9
|
+
import { OraclePriceData } from '../oracles/types';
|
|
3
10
|
|
|
4
11
|
export function isSpotPositionAvailable(position: SpotPosition): boolean {
|
|
5
12
|
return position.balance.eq(ZERO) && position.openOrders === 0;
|
|
6
13
|
}
|
|
14
|
+
|
|
15
|
+
export function getWorstCaseTokenAmounts(
|
|
16
|
+
spotPosition: SpotPosition,
|
|
17
|
+
spotMarketAccount: SpotMarketAccount,
|
|
18
|
+
oraclePriceData: OraclePriceData
|
|
19
|
+
): [BN, BN] {
|
|
20
|
+
const tokenAmount = getSignedTokenAmount(
|
|
21
|
+
getTokenAmount(
|
|
22
|
+
spotPosition.balance,
|
|
23
|
+
spotMarketAccount,
|
|
24
|
+
spotPosition.balanceType
|
|
25
|
+
),
|
|
26
|
+
spotPosition.balanceType
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
const tokenAmountAllBidsFill = tokenAmount.add(spotPosition.openBids);
|
|
30
|
+
const tokenAmountAllAsksFill = tokenAmount.add(spotPosition.openAsks);
|
|
31
|
+
|
|
32
|
+
if (tokenAmountAllAsksFill.abs().gt(tokenAmountAllBidsFill.abs())) {
|
|
33
|
+
const worstCaseQuoteTokenAmount = getTokenValue(
|
|
34
|
+
spotPosition.openBids.neg(),
|
|
35
|
+
spotMarketAccount.decimals,
|
|
36
|
+
oraclePriceData
|
|
37
|
+
);
|
|
38
|
+
return [tokenAmountAllBidsFill, worstCaseQuoteTokenAmount];
|
|
39
|
+
} else {
|
|
40
|
+
const worstCaseQuoteTokenAmount = getTokenValue(
|
|
41
|
+
spotPosition.openAsks.neg(),
|
|
42
|
+
spotMarketAccount.decimals,
|
|
43
|
+
oraclePriceData
|
|
44
|
+
);
|
|
45
|
+
return [tokenAmountAllAsksFill, worstCaseQuoteTokenAmount];
|
|
46
|
+
}
|
|
47
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -7,6 +7,7 @@ export class MarketStatus {
|
|
|
7
7
|
static readonly INITIALIZED = { initialized: {} };
|
|
8
8
|
static readonly REDUCEONLY = { reduceonly: {} };
|
|
9
9
|
static readonly SETTLEMENT = { settlement: {} };
|
|
10
|
+
static readonly DELISTED = { delisted: {} };
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
export class ContractType {
|
|
@@ -47,6 +48,7 @@ export class OrderType {
|
|
|
47
48
|
static readonly MARKET = { market: {} };
|
|
48
49
|
}
|
|
49
50
|
|
|
51
|
+
export declare type MarketTypeStr = 'perp' | 'spot';
|
|
50
52
|
export class MarketType {
|
|
51
53
|
static readonly SPOT = { spot: {} };
|
|
52
54
|
static readonly PERP = { perp: {} };
|
|
@@ -113,6 +115,10 @@ export function isOneOfVariant(object: unknown, types: string[]) {
|
|
|
113
115
|
}, false);
|
|
114
116
|
}
|
|
115
117
|
|
|
118
|
+
export function getVariant(object: unknown): string {
|
|
119
|
+
return Object.keys(object)[0];
|
|
120
|
+
}
|
|
121
|
+
|
|
116
122
|
export enum TradeSide {
|
|
117
123
|
None = 0,
|
|
118
124
|
Buy = 1,
|
|
@@ -241,6 +247,7 @@ export type LiquidationRecord = {
|
|
|
241
247
|
marginRequirement: BN;
|
|
242
248
|
totalCollateral: BN;
|
|
243
249
|
liquidationId: number;
|
|
250
|
+
canceledOrderIds: BN[];
|
|
244
251
|
liquidatePerp: LiquidatePerpRecord;
|
|
245
252
|
liquidateBorrow: LiquidateBorrowRecord;
|
|
246
253
|
liquidateBorrowForPerpPnl: LiquidateBorrowForPerpPnlRecord;
|
|
@@ -268,17 +275,16 @@ export class LiquidationType {
|
|
|
268
275
|
|
|
269
276
|
export type LiquidatePerpRecord = {
|
|
270
277
|
marketIndex: BN;
|
|
271
|
-
orderIds: BN[];
|
|
272
278
|
oraclePrice: BN;
|
|
273
279
|
baseAssetAmount: BN;
|
|
274
280
|
quoteAssetAmount: BN;
|
|
275
281
|
lpShares: BN;
|
|
276
282
|
userPnl: BN;
|
|
277
283
|
liquidatorPnl: BN;
|
|
278
|
-
canceledOrdersFee: BN;
|
|
279
284
|
userOrderId: BN;
|
|
280
285
|
liquidatorOrderId: BN;
|
|
281
286
|
fillRecordId: BN;
|
|
287
|
+
ifFee: BN;
|
|
282
288
|
};
|
|
283
289
|
|
|
284
290
|
export type LiquidateBorrowRecord = {
|
|
@@ -288,6 +294,7 @@ export type LiquidateBorrowRecord = {
|
|
|
288
294
|
liabilityMarketIndex: BN;
|
|
289
295
|
liabilityPrice: BN;
|
|
290
296
|
liabilityTransfer: BN;
|
|
297
|
+
ifFee: BN;
|
|
291
298
|
};
|
|
292
299
|
|
|
293
300
|
export type LiquidateBorrowForPerpPnlRecord = {
|
|
@@ -378,17 +385,6 @@ export type StateAccount = {
|
|
|
378
385
|
exchangePaused: boolean;
|
|
379
386
|
adminControlsPrices: boolean;
|
|
380
387
|
insuranceVault: PublicKey;
|
|
381
|
-
marginRatioInitial: BN;
|
|
382
|
-
marginRatioMaintenance: BN;
|
|
383
|
-
marginRatioPartial: BN;
|
|
384
|
-
partialLiquidationClosePercentageNumerator: BN;
|
|
385
|
-
partialLiquidationClosePercentageDenominator: BN;
|
|
386
|
-
partialLiquidationPenaltyPercentageNumerator: BN;
|
|
387
|
-
partialLiquidationPenaltyPercentageDenominator: BN;
|
|
388
|
-
fullLiquidationPenaltyPercentageNumerator: BN;
|
|
389
|
-
fullLiquidationPenaltyPercentageDenominator: BN;
|
|
390
|
-
partialLiquidationLiquidatorShareDenominator: BN;
|
|
391
|
-
fullLiquidationLiquidatorShareDenominator: BN;
|
|
392
388
|
perpFeeStructure: FeeStructure;
|
|
393
389
|
spotFeeStructure: FeeStructure;
|
|
394
390
|
totalFee: BN;
|
|
@@ -402,8 +398,9 @@ export type StateAccount = {
|
|
|
402
398
|
minOrderQuoteAssetAmount: BN;
|
|
403
399
|
signer: PublicKey;
|
|
404
400
|
signerNonce: number;
|
|
405
|
-
|
|
406
|
-
|
|
401
|
+
defaultMarketOrderTimeInForce: number;
|
|
402
|
+
minPerpAuctionDuration: number;
|
|
403
|
+
defaultSpotAuctionDuration: number;
|
|
407
404
|
liquidationMarginBufferRatio: number;
|
|
408
405
|
};
|
|
409
406
|
|
|
@@ -423,7 +420,8 @@ export type PerpMarketAccount = {
|
|
|
423
420
|
marginRatioMaintenance: number;
|
|
424
421
|
nextFillRecordId: BN;
|
|
425
422
|
pnlPool: PoolBalance;
|
|
426
|
-
|
|
423
|
+
liquidatorFee: BN;
|
|
424
|
+
ifLiquidationFee: BN;
|
|
427
425
|
imfFactor: BN;
|
|
428
426
|
unrealizedImfFactor: BN;
|
|
429
427
|
unrealizedMaxImbalance: BN;
|
|
@@ -451,7 +449,7 @@ export type SpotMarketAccount = {
|
|
|
451
449
|
|
|
452
450
|
userIfFactor: BN;
|
|
453
451
|
totalIfFactor: BN;
|
|
454
|
-
|
|
452
|
+
ifLiquidationFee: BN;
|
|
455
453
|
|
|
456
454
|
decimals: number;
|
|
457
455
|
optimalUtilization: BN;
|
|
@@ -468,7 +466,7 @@ export type SpotMarketAccount = {
|
|
|
468
466
|
maintenanceAssetWeight: BN;
|
|
469
467
|
initialLiabilityWeight: BN;
|
|
470
468
|
maintenanceLiabilityWeight: BN;
|
|
471
|
-
|
|
469
|
+
liquidatorFee: BN;
|
|
472
470
|
imfFactor: BN;
|
|
473
471
|
|
|
474
472
|
withdrawGuardThreshold: BN;
|
|
@@ -561,7 +559,7 @@ export type PerpPosition = {
|
|
|
561
559
|
openOrders: BN;
|
|
562
560
|
openBids: BN;
|
|
563
561
|
openAsks: BN;
|
|
564
|
-
|
|
562
|
+
settledPnl: BN;
|
|
565
563
|
lpShares: BN;
|
|
566
564
|
lastFeePerLp: BN;
|
|
567
565
|
lastNetBaseAssetAmountPerLp: BN;
|
|
@@ -600,6 +598,7 @@ export type UserAccount = {
|
|
|
600
598
|
bankrupt: boolean;
|
|
601
599
|
nextLiquidationId: number;
|
|
602
600
|
nextOrderId: BN;
|
|
601
|
+
customMarginRatio: number;
|
|
603
602
|
};
|
|
604
603
|
|
|
605
604
|
export type SpotPosition = {
|
|
@@ -638,6 +637,7 @@ export type Order = {
|
|
|
638
637
|
auctionDuration: number;
|
|
639
638
|
auctionStartPrice: BN;
|
|
640
639
|
auctionEndPrice: BN;
|
|
640
|
+
timeInForce: number;
|
|
641
641
|
};
|
|
642
642
|
|
|
643
643
|
export type OrderParams = {
|
|
@@ -655,8 +655,9 @@ export type OrderParams = {
|
|
|
655
655
|
triggerCondition: OrderTriggerCondition;
|
|
656
656
|
positionLimit: BN;
|
|
657
657
|
oraclePriceOffset: BN;
|
|
658
|
-
|
|
659
|
-
|
|
658
|
+
auctionDuration: number | null;
|
|
659
|
+
timeInForce: number | null;
|
|
660
|
+
auctionStartPrice: BN | null;
|
|
660
661
|
};
|
|
661
662
|
|
|
662
663
|
export type NecessaryOrderParams = {
|
|
@@ -685,8 +686,9 @@ export const DefaultOrderParams = {
|
|
|
685
686
|
triggerCondition: OrderTriggerCondition.ABOVE,
|
|
686
687
|
positionLimit: ZERO,
|
|
687
688
|
oraclePriceOffset: ZERO,
|
|
688
|
-
|
|
689
|
-
|
|
689
|
+
auctionDuration: null,
|
|
690
|
+
timeInForce: null,
|
|
691
|
+
auctionStartPrice: null,
|
|
690
692
|
};
|
|
691
693
|
|
|
692
694
|
export type MakerInfo = {
|
|
@@ -748,7 +750,7 @@ export type FeeStructure = {
|
|
|
748
750
|
makerRebateNumerator: BN;
|
|
749
751
|
makerRebateDenominator: BN;
|
|
750
752
|
fillerRewardStructure: OrderFillerRewardStructure;
|
|
751
|
-
|
|
753
|
+
flatFillerFee: BN;
|
|
752
754
|
};
|
|
753
755
|
|
|
754
756
|
export type OracleGuardRails = {
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ClearingHouseUser,
|
|
3
|
+
ClearingHouse,
|
|
4
|
+
UserAccount,
|
|
5
|
+
bulkPollingUserSubscribe,
|
|
6
|
+
OrderRecord,
|
|
7
|
+
ClearingHouseUserAccountSubscriptionConfig,
|
|
8
|
+
} from '..';
|
|
9
|
+
import { ProgramAccount } from '@project-serum/anchor';
|
|
10
|
+
|
|
11
|
+
import { PublicKey } from '@solana/web3.js';
|
|
12
|
+
|
|
13
|
+
export class UserMap {
|
|
14
|
+
private userMap = new Map<string, ClearingHouseUser>();
|
|
15
|
+
private clearingHouse: ClearingHouse;
|
|
16
|
+
private accountSubscription: ClearingHouseUserAccountSubscriptionConfig;
|
|
17
|
+
|
|
18
|
+
constructor(
|
|
19
|
+
clearingHouse: ClearingHouse,
|
|
20
|
+
accountSubscription: ClearingHouseUserAccountSubscriptionConfig
|
|
21
|
+
) {
|
|
22
|
+
this.clearingHouse = clearingHouse;
|
|
23
|
+
this.accountSubscription = accountSubscription;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
public async fetchAllUsers() {
|
|
27
|
+
const userArray: ClearingHouseUser[] = [];
|
|
28
|
+
|
|
29
|
+
const programUserAccounts =
|
|
30
|
+
(await this.clearingHouse.program.account.user.all()) as ProgramAccount<UserAccount>[];
|
|
31
|
+
for (const programUserAccount of programUserAccounts) {
|
|
32
|
+
if (this.userMap.has(programUserAccount.publicKey.toString())) {
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const user = new ClearingHouseUser({
|
|
37
|
+
clearingHouse: this.clearingHouse,
|
|
38
|
+
userAccountPublicKey: programUserAccount.publicKey,
|
|
39
|
+
accountSubscription: this.accountSubscription,
|
|
40
|
+
});
|
|
41
|
+
userArray.push(user);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (this.accountSubscription.type === 'polling') {
|
|
45
|
+
await bulkPollingUserSubscribe(
|
|
46
|
+
userArray,
|
|
47
|
+
this.accountSubscription.accountLoader
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
for (const user of userArray) {
|
|
52
|
+
this.userMap.set(user.getUserAccountPublicKey().toString(), user);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
public async addPubkey(userAccountPublicKey: PublicKey) {
|
|
57
|
+
const user = new ClearingHouseUser({
|
|
58
|
+
clearingHouse: this.clearingHouse,
|
|
59
|
+
userAccountPublicKey,
|
|
60
|
+
accountSubscription: this.accountSubscription,
|
|
61
|
+
});
|
|
62
|
+
await user.subscribe();
|
|
63
|
+
this.userMap.set(userAccountPublicKey.toString(), user);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
public has(key: string): boolean {
|
|
67
|
+
return this.userMap.has(key);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* gets the ClearingHouseUser for a particular userAccountPublicKey, if no ClearingHouseUser exists, undefined is returned
|
|
72
|
+
* @param key userAccountPublicKey to get ClearngHouseUserFor
|
|
73
|
+
* @returns user ClearingHouseUser | undefined
|
|
74
|
+
*/
|
|
75
|
+
public get(key: string): ClearingHouseUser | undefined {
|
|
76
|
+
return this.userMap.get(key);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* gets the ClearingHouseUser for a particular userAccountPublicKey, if no ClearingHouseUser exists, new one is created
|
|
81
|
+
* @param key userAccountPublicKey to get ClearngHouseUserFor
|
|
82
|
+
* @returns ClearingHouseUser
|
|
83
|
+
*/
|
|
84
|
+
public async mustGet(key: string): Promise<ClearingHouseUser> {
|
|
85
|
+
if (!this.has(key)) {
|
|
86
|
+
await this.addPubkey(new PublicKey(key));
|
|
87
|
+
}
|
|
88
|
+
const user = this.userMap.get(key);
|
|
89
|
+
await user.fetchAccounts();
|
|
90
|
+
return user;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
public async updateWithOrderRecord(record: OrderRecord) {
|
|
94
|
+
await this.addPubkey(record.user);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
public values(): IterableIterator<ClearingHouseUser> {
|
|
98
|
+
return this.userMap.values();
|
|
99
|
+
}
|
|
100
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ClearingHouse,
|
|
3
|
+
getUserStatsAccountPublicKey,
|
|
4
|
+
OrderRecord,
|
|
5
|
+
UserStatsAccount,
|
|
6
|
+
ClearingHouseUserStats,
|
|
7
|
+
ClearingHouseUserStatsAccountSubscriptionConfig,
|
|
8
|
+
bulkPollingUserStatsSubscribe,
|
|
9
|
+
} from '..';
|
|
10
|
+
import { ProgramAccount } from '@project-serum/anchor';
|
|
11
|
+
import { PublicKey } from '@solana/web3.js';
|
|
12
|
+
|
|
13
|
+
import { UserMap } from './userMap';
|
|
14
|
+
|
|
15
|
+
export class UserStatsMap {
|
|
16
|
+
/**
|
|
17
|
+
* map from authority pubkey to ClearingHouseUserStats
|
|
18
|
+
*/
|
|
19
|
+
private userStatsMap = new Map<string, ClearingHouseUserStats>();
|
|
20
|
+
private clearingHouse: ClearingHouse;
|
|
21
|
+
private accountSubscription: ClearingHouseUserStatsAccountSubscriptionConfig;
|
|
22
|
+
|
|
23
|
+
constructor(
|
|
24
|
+
clearingHouse: ClearingHouse,
|
|
25
|
+
accountSubscription: ClearingHouseUserStatsAccountSubscriptionConfig
|
|
26
|
+
) {
|
|
27
|
+
this.clearingHouse = clearingHouse;
|
|
28
|
+
this.accountSubscription = accountSubscription;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
public async fetchAllUserStats() {
|
|
32
|
+
const userStatArray: ClearingHouseUserStats[] = [];
|
|
33
|
+
|
|
34
|
+
const programUserAccounts =
|
|
35
|
+
(await this.clearingHouse.program.account.userStats.all()) as ProgramAccount<UserStatsAccount>[];
|
|
36
|
+
|
|
37
|
+
for (const programUserAccount of programUserAccounts) {
|
|
38
|
+
const userStat: UserStatsAccount = programUserAccount.account;
|
|
39
|
+
if (this.userStatsMap.has(userStat.authority.toString())) {
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const chUserStat = new ClearingHouseUserStats({
|
|
44
|
+
clearingHouse: this.clearingHouse,
|
|
45
|
+
userStatsAccountPublicKey: getUserStatsAccountPublicKey(
|
|
46
|
+
this.clearingHouse.program.programId,
|
|
47
|
+
userStat.authority
|
|
48
|
+
),
|
|
49
|
+
accountSubscription: this.accountSubscription,
|
|
50
|
+
});
|
|
51
|
+
userStatArray.push(chUserStat);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (this.accountSubscription.type === 'polling') {
|
|
55
|
+
await bulkPollingUserStatsSubscribe(
|
|
56
|
+
userStatArray,
|
|
57
|
+
this.accountSubscription.accountLoader
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
for (const userStat of userStatArray) {
|
|
62
|
+
this.userStatsMap.set(
|
|
63
|
+
userStat.getAccount().authority.toString(),
|
|
64
|
+
userStat
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
public async addUserStat(authority: PublicKey) {
|
|
70
|
+
const userStat = new ClearingHouseUserStats({
|
|
71
|
+
clearingHouse: this.clearingHouse,
|
|
72
|
+
userStatsAccountPublicKey: getUserStatsAccountPublicKey(
|
|
73
|
+
this.clearingHouse.program.programId,
|
|
74
|
+
authority
|
|
75
|
+
),
|
|
76
|
+
accountSubscription: this.accountSubscription,
|
|
77
|
+
});
|
|
78
|
+
await userStat.subscribe();
|
|
79
|
+
|
|
80
|
+
this.userStatsMap.set(authority.toString(), userStat);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
public async updateWithOrderRecord(record: OrderRecord, userMap: UserMap) {
|
|
84
|
+
if (!this.has(record.user.toString())) {
|
|
85
|
+
const takerUserAccount = await userMap.mustGet(record.user.toString());
|
|
86
|
+
this.addUserStat(takerUserAccount.getUserAccount().authority);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
public has(authorityPublicKey: string): boolean {
|
|
91
|
+
return this.userStatsMap.has(authorityPublicKey);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
public get(authorityPublicKey: string): ClearingHouseUserStats {
|
|
95
|
+
return this.userStatsMap.get(authorityPublicKey);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
public async mustGet(
|
|
99
|
+
authorityPublicKey: string
|
|
100
|
+
): Promise<ClearingHouseUserStats> {
|
|
101
|
+
if (!this.has(authorityPublicKey)) {
|
|
102
|
+
await this.addUserStat(new PublicKey(authorityPublicKey));
|
|
103
|
+
}
|
|
104
|
+
return this.get(authorityPublicKey);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
public values(): IterableIterator<ClearingHouseUserStats> {
|
|
108
|
+
return this.userStatsMap.values();
|
|
109
|
+
}
|
|
110
|
+
}
|