@drift-labs/sdk 0.1.19-master.1 → 0.1.21-master.3
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 +31 -0
- package/lib/accounts/bulkAccountLoader.js +177 -0
- package/lib/accounts/bulkUserSubscription.d.ts +7 -0
- package/lib/accounts/bulkUserSubscription.js +28 -0
- package/lib/accounts/pollingClearingHouseAccountSubscriber.d.ts +49 -0
- package/lib/accounts/pollingClearingHouseAccountSubscriber.js +228 -0
- package/lib/accounts/pollingTokenAccountSubscriber.d.ts +25 -0
- package/lib/accounts/pollingTokenAccountSubscriber.js +79 -0
- package/lib/accounts/pollingUserAccountSubscriber.d.ts +32 -0
- package/lib/accounts/pollingUserAccountSubscriber.js +136 -0
- package/lib/accounts/types.d.ts +38 -3
- package/lib/accounts/utils.d.ts +1 -0
- package/lib/accounts/utils.js +7 -0
- package/lib/accounts/webSocketAccountSubscriber.d.ts +6 -3
- package/lib/accounts/webSocketAccountSubscriber.js +43 -12
- package/lib/accounts/{defaultClearingHouseAccountSubscriber.d.ts → webSocketClearingHouseAccountSubscriber.d.ts} +8 -3
- package/lib/accounts/{defaultClearingHouseAccountSubscriber.js → webSocketClearingHouseAccountSubscriber.js} +30 -4
- package/lib/accounts/{defaultUserAccountSubscriber.d.ts → webSocketUserAccountSubscriber.d.ts} +6 -3
- package/lib/accounts/{defaultUserAccountSubscriber.js → webSocketUserAccountSubscriber.js} +16 -4
- package/lib/addresses.d.ts +4 -1
- package/lib/addresses.js +28 -1
- package/lib/admin.d.ts +10 -4
- package/lib/admin.js +54 -17
- package/lib/assert/assert.d.ts +0 -1
- package/lib/clearingHouse.d.ts +39 -4
- package/lib/clearingHouse.js +334 -23
- package/lib/clearingHouseUser.d.ts +26 -20
- package/lib/clearingHouseUser.js +149 -118
- package/lib/config.d.ts +0 -1
- package/lib/constants/markets.d.ts +2 -2
- package/lib/constants/markets.js +28 -15
- package/lib/constants/numericConstants.d.ts +4 -2
- package/lib/constants/numericConstants.js +16 -17
- package/lib/examples/makeTradeExample.d.ts +0 -1
- package/lib/examples/makeTradeExample.js +6 -6
- package/lib/factory/clearingHouse.d.ts +25 -0
- package/lib/factory/clearingHouse.js +64 -0
- package/lib/factory/clearingHouseUser.d.ts +19 -0
- package/lib/factory/clearingHouseUser.js +34 -0
- package/lib/idl/clearing_house.json +1066 -39
- package/lib/index.d.ts +11 -3
- package/lib/index.js +12 -2
- package/lib/math/amm.d.ts +1 -1
- package/lib/math/amm.js +38 -15
- package/lib/math/conversion.d.ts +1 -2
- package/lib/math/conversion.js +1 -1
- package/lib/math/funding.d.ts +0 -1
- package/lib/math/funding.js +1 -1
- package/lib/math/insuranceFund.d.ts +2 -2
- package/lib/math/insuranceFund.js +3 -6
- package/lib/math/market.d.ts +2 -2
- package/lib/math/market.js +12 -2
- package/lib/math/orders.d.ts +3 -0
- package/lib/math/orders.js +32 -0
- package/lib/math/position.d.ts +6 -3
- package/lib/math/position.js +21 -10
- package/lib/math/trade.d.ts +0 -1
- package/lib/math/trade.js +16 -16
- package/lib/math/utils.d.ts +2 -2
- package/lib/math/utils.js +3 -3
- package/lib/mockUSDCFaucet.d.ts +2 -2
- package/lib/orderParams.d.ts +7 -0
- package/lib/orderParams.js +108 -0
- package/lib/orders.d.ts +6 -0
- package/lib/orders.js +136 -0
- package/lib/pythClient.d.ts +0 -1
- package/lib/pythClient.js +1 -1
- package/lib/token/index.d.ts +3 -0
- package/lib/token/index.js +38 -0
- package/lib/tx/defaultTxSender.d.ts +0 -1
- package/lib/tx/types.d.ts +0 -1
- package/lib/tx/utils.d.ts +0 -1
- package/lib/types.d.ts +147 -3
- package/lib/types.js +36 -1
- package/lib/util/computeUnits.d.ts +0 -1
- package/lib/util/tps.d.ts +0 -1
- package/lib/wallet.d.ts +0 -1
- package/package.json +11 -3
- package/src/accounts/bulkAccountLoader.ts +215 -0
- package/src/accounts/bulkUserSubscription.ts +28 -0
- package/src/accounts/pollingClearingHouseAccountSubscriber.ts +326 -0
- package/src/accounts/pollingTokenAccountSubscriber.ts +99 -0
- package/src/accounts/pollingUserAccountSubscriber.ts +194 -0
- package/src/accounts/types.ts +48 -1
- package/src/accounts/utils.ts +3 -0
- package/src/accounts/webSocketAccountSubscriber.ts +67 -17
- package/src/accounts/{defaultClearingHouseAccountSubscriber.ts → webSocketClearingHouseAccountSubscriber.ts} +51 -1
- package/src/accounts/{defaultUserAccountSubscriber.ts → webSocketUserAccountSubscriber.ts} +33 -3
- package/src/addresses.ts +37 -0
- package/src/admin.ts +92 -24
- package/src/clearingHouse.ts +455 -22
- package/src/clearingHouseUser.ts +190 -108
- package/src/constants/markets.ts +17 -1
- package/src/constants/numericConstants.ts +3 -1
- package/src/examples/makeTradeExample.ts +4 -1
- package/src/factory/clearingHouse.ts +125 -0
- package/src/factory/clearingHouseUser.ts +73 -0
- package/src/idl/clearing_house.json +1066 -39
- package/src/index.ts +11 -2
- package/src/math/amm.ts +47 -14
- package/src/math/conversion.ts +1 -1
- package/src/math/insuranceFund.ts +1 -1
- package/src/math/market.ts +28 -2
- package/src/math/orders.ts +44 -0
- package/src/math/position.ts +24 -4
- package/src/math/utils.ts +1 -1
- package/src/mockUSDCFaucet.ts +1 -1
- package/src/orderParams.ts +151 -0
- package/src/orders.ts +236 -0
- package/src/token/index.ts +37 -0
- package/src/types.ts +130 -2
- package/tsconfig.json +0 -1
- package/lib/accounts/defaultClearingHouseAccountSubscriber.d.ts.map +0 -1
- package/lib/accounts/defaultUserAccountSubscriber.d.ts.map +0 -1
- package/lib/accounts/types.d.ts.map +0 -1
- package/lib/accounts/webSocketAccountSubscriber.d.ts.map +0 -1
- package/lib/addresses.d.ts.map +0 -1
- package/lib/admin.d.ts.map +0 -1
- package/lib/assert/assert.d.ts.map +0 -1
- package/lib/clearingHouse.d.ts.map +0 -1
- package/lib/clearingHouseUser.d.ts.map +0 -1
- package/lib/config.d.ts.map +0 -1
- package/lib/constants/markets.d.ts.map +0 -1
- package/lib/constants/numericConstants.d.ts.map +0 -1
- package/lib/examples/makeTradeExample.d.ts.map +0 -1
- package/lib/index.d.ts.map +0 -1
- package/lib/math/amm.d.ts.map +0 -1
- package/lib/math/conversion.d.ts.map +0 -1
- package/lib/math/funding.d.ts.map +0 -1
- package/lib/math/insuranceFund.d.ts.map +0 -1
- package/lib/math/market.d.ts.map +0 -1
- package/lib/math/position.d.ts.map +0 -1
- package/lib/math/trade.d.ts.map +0 -1
- package/lib/math/utils.d.ts.map +0 -1
- package/lib/mockUSDCFaucet.d.ts.map +0 -1
- package/lib/pythClient.d.ts.map +0 -1
- package/lib/tx/defaultTxSender.d.ts.map +0 -1
- package/lib/tx/types.d.ts.map +0 -1
- package/lib/tx/utils.d.ts.map +0 -1
- package/lib/types.d.ts.map +0 -1
- package/lib/util/computeUnits.d.ts.map +0 -1
- package/lib/util/tps.d.ts.map +0 -1
- package/lib/wallet.d.ts.map +0 -1
package/src/clearingHouseUser.ts
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import { PublicKey } from '@solana/web3.js';
|
|
2
|
-
import BN from 'bn.js';
|
|
3
2
|
import { EventEmitter } from 'events';
|
|
4
3
|
import StrictEventEmitter from 'strict-event-emitter-types';
|
|
5
4
|
import { ClearingHouse } from './clearingHouse';
|
|
6
|
-
import {
|
|
7
|
-
|
|
5
|
+
import {
|
|
6
|
+
Order,
|
|
7
|
+
UserAccount,
|
|
8
|
+
UserOrdersAccount,
|
|
9
|
+
UserPosition,
|
|
10
|
+
UserPositionsAccount,
|
|
11
|
+
} from './types';
|
|
12
|
+
import { calculateEntryPrice, isEmptyPosition } from './math/position';
|
|
8
13
|
import {
|
|
9
14
|
MARK_PRICE_PRECISION,
|
|
10
15
|
AMM_TO_QUOTE_PRECISION_RATIO,
|
|
@@ -18,34 +23,58 @@ import {
|
|
|
18
23
|
PRICE_TO_QUOTE_PRECISION,
|
|
19
24
|
} from './constants/numericConstants';
|
|
20
25
|
import { UserAccountSubscriber, UserAccountEvents } from './accounts/types';
|
|
21
|
-
import { DefaultUserAccountSubscriber } from './accounts/defaultUserAccountSubscriber';
|
|
22
26
|
import {
|
|
23
27
|
calculateMarkPrice,
|
|
24
28
|
calculateBaseAssetValue,
|
|
25
29
|
calculatePositionFundingPNL,
|
|
26
30
|
calculatePositionPNL,
|
|
27
31
|
PositionDirection,
|
|
32
|
+
getUserOrdersAccountPublicKey,
|
|
33
|
+
calculateNewStateAfterOrder,
|
|
28
34
|
calculateTradeSlippage,
|
|
35
|
+
BN,
|
|
29
36
|
} from '.';
|
|
30
37
|
import { getUserAccountPublicKey } from './addresses';
|
|
38
|
+
import {
|
|
39
|
+
getClearingHouseUser,
|
|
40
|
+
getWebSocketClearingHouseUserConfig,
|
|
41
|
+
} from './factory/clearingHouseUser';
|
|
31
42
|
|
|
32
43
|
export class ClearingHouseUser {
|
|
33
44
|
clearingHouse: ClearingHouse;
|
|
34
45
|
authority: PublicKey;
|
|
35
46
|
accountSubscriber: UserAccountSubscriber;
|
|
36
47
|
userAccountPublicKey?: PublicKey;
|
|
37
|
-
|
|
48
|
+
userOrdersAccountPublicKey?: PublicKey;
|
|
49
|
+
_isSubscribed = false;
|
|
38
50
|
eventEmitter: StrictEventEmitter<EventEmitter, UserAccountEvents>;
|
|
39
51
|
|
|
52
|
+
public get isSubscribed() {
|
|
53
|
+
return this._isSubscribed && this.accountSubscriber.isSubscribed;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
public set isSubscribed(val: boolean) {
|
|
57
|
+
this._isSubscribed = val;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* @deprecated You should use getClearingHouseUser factory method instead
|
|
62
|
+
* @param clearingHouse
|
|
63
|
+
* @param authority
|
|
64
|
+
* @returns
|
|
65
|
+
*/
|
|
40
66
|
public static from(
|
|
41
67
|
clearingHouse: ClearingHouse,
|
|
42
68
|
authority: PublicKey
|
|
43
69
|
): ClearingHouseUser {
|
|
44
|
-
|
|
45
|
-
|
|
70
|
+
if (clearingHouse.accountSubscriber.type !== 'websocket')
|
|
71
|
+
throw 'This method only works for clearing houses with a websocket account listener. Try using the getClearingHouseUser factory method to initialize a ClearingHouseUser instead';
|
|
72
|
+
|
|
73
|
+
const config = getWebSocketClearingHouseUserConfig(
|
|
74
|
+
clearingHouse,
|
|
46
75
|
authority
|
|
47
76
|
);
|
|
48
|
-
return
|
|
77
|
+
return getClearingHouseUser(config);
|
|
49
78
|
}
|
|
50
79
|
|
|
51
80
|
public constructor(
|
|
@@ -91,6 +120,10 @@ export class ClearingHouseUser {
|
|
|
91
120
|
return this.accountSubscriber.getUserPositionsAccount();
|
|
92
121
|
}
|
|
93
122
|
|
|
123
|
+
public getUserOrdersAccount(): UserOrdersAccount | undefined {
|
|
124
|
+
return this.accountSubscriber.getUserOrdersAccount();
|
|
125
|
+
}
|
|
126
|
+
|
|
94
127
|
/**
|
|
95
128
|
* Gets the user's current position for a given market. If the user has no position returns undefined
|
|
96
129
|
* @param marketIndex
|
|
@@ -108,9 +141,30 @@ export class ClearingHouseUser {
|
|
|
108
141
|
lastCumulativeFundingRate: ZERO,
|
|
109
142
|
marketIndex,
|
|
110
143
|
quoteAssetAmount: ZERO,
|
|
144
|
+
openOrders: ZERO,
|
|
111
145
|
};
|
|
112
146
|
}
|
|
113
147
|
|
|
148
|
+
/**
|
|
149
|
+
* @param orderId
|
|
150
|
+
* @returns Order
|
|
151
|
+
*/
|
|
152
|
+
public getOrder(orderId: BN): Order | undefined {
|
|
153
|
+
return this.getUserOrdersAccount().orders.find((order) =>
|
|
154
|
+
order.orderId.eq(orderId)
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* @param userOrderId
|
|
160
|
+
* @returns Order
|
|
161
|
+
*/
|
|
162
|
+
public getOrderByUserOrderId(userOrderId: number): Order | undefined {
|
|
163
|
+
return this.getUserOrdersAccount().orders.find(
|
|
164
|
+
(order) => order.userOrderId === userOrderId
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
|
|
114
168
|
public async getUserAccountPublicKey(): Promise<PublicKey> {
|
|
115
169
|
if (this.userAccountPublicKey) {
|
|
116
170
|
return this.userAccountPublicKey;
|
|
@@ -123,6 +177,18 @@ export class ClearingHouseUser {
|
|
|
123
177
|
return this.userAccountPublicKey;
|
|
124
178
|
}
|
|
125
179
|
|
|
180
|
+
public async getUserOrdersAccountPublicKey(): Promise<PublicKey> {
|
|
181
|
+
if (this.userOrdersAccountPublicKey) {
|
|
182
|
+
return this.userOrdersAccountPublicKey;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
this.userOrdersAccountPublicKey = await getUserOrdersAccountPublicKey(
|
|
186
|
+
this.clearingHouse.program.programId,
|
|
187
|
+
await this.getUserAccountPublicKey()
|
|
188
|
+
);
|
|
189
|
+
return this.userOrdersAccountPublicKey;
|
|
190
|
+
}
|
|
191
|
+
|
|
126
192
|
public async exists(): Promise<boolean> {
|
|
127
193
|
const userAccountPublicKey = await this.getUserAccountPublicKey();
|
|
128
194
|
const userAccountRPCResponse =
|
|
@@ -418,6 +484,7 @@ export class ClearingHouseUser {
|
|
|
418
484
|
),
|
|
419
485
|
lastCumulativeFundingRate: new BN(0),
|
|
420
486
|
quoteAssetAmount: new BN(0),
|
|
487
|
+
openOrders: new BN(0),
|
|
421
488
|
};
|
|
422
489
|
|
|
423
490
|
const market = this.clearingHouse.getMarket(
|
|
@@ -508,14 +575,14 @@ export class ClearingHouseUser {
|
|
|
508
575
|
// solves formula for example calc below
|
|
509
576
|
|
|
510
577
|
/* example: assume BTC price is $40k (examine 10% up/down)
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
578
|
+
|
|
579
|
+
if 10k deposit and levered 10x short BTC => BTC up $400 means:
|
|
580
|
+
1. higher base_asset_value (+$4k)
|
|
581
|
+
2. lower collateral (-$4k)
|
|
582
|
+
3. (10k - 4k)/(100k + 4k) => 6k/104k => .0576
|
|
583
|
+
|
|
584
|
+
for 10x long, BTC down $400:
|
|
585
|
+
3. (10k - 4k) / (100k - 4k) = 6k/96k => .0625 */
|
|
519
586
|
|
|
520
587
|
const tc = this.getTotalCollateral();
|
|
521
588
|
const tpv = this.getTotalPositionValue();
|
|
@@ -546,6 +613,7 @@ export class ClearingHouseUser {
|
|
|
546
613
|
lastCumulativeFundingRate:
|
|
547
614
|
currentMarketPosition.lastCumulativeFundingRate,
|
|
548
615
|
quoteAssetAmount: new BN(0),
|
|
616
|
+
openOrders: new BN(0),
|
|
549
617
|
};
|
|
550
618
|
|
|
551
619
|
const market = this.clearingHouse.getMarket(
|
|
@@ -665,21 +733,6 @@ export class ClearingHouseUser {
|
|
|
665
733
|
|
|
666
734
|
/**
|
|
667
735
|
* Get the maximum trade size for a given market, taking into account the user's current leverage, positions, collateral, etc.
|
|
668
|
-
*
|
|
669
|
-
* To Calculate Max Quote Available:
|
|
670
|
-
*
|
|
671
|
-
* Case 1: SameSide
|
|
672
|
-
* => Remaining quote to get to maxLeverage
|
|
673
|
-
*
|
|
674
|
-
* Case 2: NOT SameSide && currentLeverage <= maxLeverage
|
|
675
|
-
* => Current opposite position x2 + remaining to get to maxLeverage
|
|
676
|
-
*
|
|
677
|
-
* Case 3: NOT SameSide && currentLeverage > maxLeverage && otherPositions - currentPosition > maxLeverage
|
|
678
|
-
* => strictly reduce current position size
|
|
679
|
-
*
|
|
680
|
-
* Case 4: NOT SameSide && currentLeverage > maxLeverage && otherPositions - currentPosition < maxLeverage
|
|
681
|
-
* => current position + remaining to get to maxLeverage
|
|
682
|
-
*
|
|
683
736
|
* @param marketIndex
|
|
684
737
|
* @param tradeSide
|
|
685
738
|
* @param userMaxLeverageSetting - leverage : Precision TEN_THOUSAND
|
|
@@ -690,28 +743,34 @@ export class ClearingHouseUser {
|
|
|
690
743
|
tradeSide: PositionDirection,
|
|
691
744
|
userMaxLeverageSetting: BN
|
|
692
745
|
): BN {
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
746
|
+
// inline function which get's the current position size on the opposite side of the target trade
|
|
747
|
+
const getOppositePositionValueUSDC = () => {
|
|
748
|
+
if (!currentPosition) return ZERO;
|
|
696
749
|
|
|
697
|
-
|
|
750
|
+
const side = tradeSide === PositionDirection.SHORT ? 'short' : 'long';
|
|
698
751
|
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
752
|
+
if (side === 'long' && currentPosition?.baseAssetAmount.isNeg()) {
|
|
753
|
+
return this.getPositionValue(targetMarketIndex);
|
|
754
|
+
} else if (
|
|
755
|
+
side === 'short' &&
|
|
756
|
+
!currentPosition?.baseAssetAmount.isNeg()
|
|
757
|
+
) {
|
|
758
|
+
return this.getPositionValue(targetMarketIndex);
|
|
759
|
+
}
|
|
702
760
|
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
: targetSide === currentPositionSide;
|
|
761
|
+
return ZERO;
|
|
762
|
+
};
|
|
706
763
|
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
: this.getPositionValue(targetMarketIndex);
|
|
764
|
+
const currentPosition =
|
|
765
|
+
this.getUserPosition(targetMarketIndex) ||
|
|
766
|
+
this.getEmptyPosition(targetMarketIndex);
|
|
711
767
|
|
|
712
768
|
// get current leverage
|
|
713
769
|
const currentLeverage = this.getLeverage();
|
|
714
770
|
|
|
771
|
+
// remaining leverage
|
|
772
|
+
// let remainingLeverage = userMaxLeverageSetting;
|
|
773
|
+
|
|
715
774
|
const remainingLeverage = BN.max(
|
|
716
775
|
userMaxLeverageSetting.sub(currentLeverage),
|
|
717
776
|
ZERO
|
|
@@ -725,57 +784,10 @@ export class ClearingHouseUser {
|
|
|
725
784
|
.mul(totalCollateral)
|
|
726
785
|
.div(TEN_THOUSAND);
|
|
727
786
|
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
// case 1 : Regular trade where current total position less than max, and no opposite position to account for
|
|
731
|
-
// do nothing
|
|
732
|
-
} else {
|
|
733
|
-
// case 2 : trade where current total position less than max, but need to account for flipping the current position over to the other side
|
|
734
|
-
maxPositionSize = maxPositionSize.add(
|
|
735
|
-
oppositeSizeValueUSDC.mul(new BN(2))
|
|
736
|
-
);
|
|
737
|
-
}
|
|
738
|
-
} else {
|
|
739
|
-
// current leverage is greater than max leverage - can only reduce position size
|
|
740
|
-
|
|
741
|
-
if (!targettingSameSide) {
|
|
742
|
-
const currentPositionQuoteSize =
|
|
743
|
-
this.getPositionValue(targetMarketIndex);
|
|
744
|
-
|
|
745
|
-
const currentTotalQuoteSize = currentLeverage
|
|
746
|
-
.mul(totalCollateral)
|
|
747
|
-
.div(TEN_THOUSAND);
|
|
748
|
-
|
|
749
|
-
const otherPositionsTotalQuoteSize = currentTotalQuoteSize.sub(
|
|
750
|
-
currentPositionQuoteSize
|
|
751
|
-
);
|
|
752
|
-
|
|
753
|
-
const quoteValueOfMaxLeverage = userMaxLeverageSetting
|
|
754
|
-
.mul(totalCollateral)
|
|
755
|
-
.div(TEN_THOUSAND);
|
|
756
|
-
|
|
757
|
-
if (
|
|
758
|
-
otherPositionsTotalQuoteSize
|
|
759
|
-
.sub(currentPositionQuoteSize)
|
|
760
|
-
.gte(quoteValueOfMaxLeverage)
|
|
761
|
-
) {
|
|
762
|
-
// case 3: Can only reduce the current position because it will still be greater than max leverage
|
|
763
|
-
|
|
764
|
-
maxPositionSize = currentPositionQuoteSize;
|
|
765
|
-
} else {
|
|
766
|
-
// case 4: Can reduce the position, and then take extra remaining quote to get to max leverage
|
|
767
|
-
|
|
768
|
-
const allowedQuoteSizeAfterClosingCurrentPosition =
|
|
769
|
-
quoteValueOfMaxLeverage.sub(otherPositionsTotalQuoteSize);
|
|
787
|
+
// add any position we have on the opposite side of the current trade, because we can "flip" the size of this position without taking any extra leverage.
|
|
788
|
+
const oppositeSizeValueUSDC = getOppositePositionValueUSDC();
|
|
770
789
|
|
|
771
|
-
|
|
772
|
-
allowedQuoteSizeAfterClosingCurrentPosition
|
|
773
|
-
);
|
|
774
|
-
}
|
|
775
|
-
} else {
|
|
776
|
-
// do nothing if targetting same side
|
|
777
|
-
}
|
|
778
|
-
}
|
|
790
|
+
maxPositionSize = maxPositionSize.add(oppositeSizeValueUSDC.mul(new BN(2)));
|
|
779
791
|
|
|
780
792
|
// subtract oneMillionth of maxPositionSize
|
|
781
793
|
// => to avoid rounding errors when taking max leverage
|
|
@@ -820,19 +832,13 @@ export class ClearingHouseUser {
|
|
|
820
832
|
|
|
821
833
|
const totalPositionAfterTradeExcludingTargetMarket =
|
|
822
834
|
this.getTotalPositionValueExcludingMarket(targetMarketIndex);
|
|
835
|
+
const newLeverage = currentMarketPositionAfterTrade
|
|
836
|
+
.add(totalPositionAfterTradeExcludingTargetMarket)
|
|
837
|
+
.abs()
|
|
838
|
+
.mul(TEN_THOUSAND)
|
|
839
|
+
.div(this.getTotalCollateral());
|
|
823
840
|
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
if (totalCollateral.gt(ZERO)) {
|
|
827
|
-
const newLeverage = currentMarketPositionAfterTrade
|
|
828
|
-
.add(totalPositionAfterTradeExcludingTargetMarket)
|
|
829
|
-
.abs()
|
|
830
|
-
.mul(TEN_THOUSAND)
|
|
831
|
-
.div(totalCollateral);
|
|
832
|
-
return newLeverage;
|
|
833
|
-
} else {
|
|
834
|
-
return new BN(0);
|
|
835
|
-
}
|
|
841
|
+
return newLeverage;
|
|
836
842
|
}
|
|
837
843
|
|
|
838
844
|
/**
|
|
@@ -860,9 +866,85 @@ export class ClearingHouseUser {
|
|
|
860
866
|
|
|
861
867
|
let currentMarketPositionValueUSDC = ZERO;
|
|
862
868
|
if (currentMarketPosition) {
|
|
863
|
-
|
|
869
|
+
const market = this.clearingHouse.getMarket(
|
|
870
|
+
currentMarketPosition.marketIndex
|
|
871
|
+
);
|
|
872
|
+
currentMarketPositionValueUSDC = calculateBaseAssetValue(
|
|
873
|
+
market,
|
|
874
|
+
currentMarketPosition
|
|
875
|
+
);
|
|
864
876
|
}
|
|
865
877
|
|
|
866
878
|
return this.getTotalPositionValue().sub(currentMarketPositionValueUSDC);
|
|
867
879
|
}
|
|
880
|
+
|
|
881
|
+
public canFillOrder(order: Order): boolean {
|
|
882
|
+
const userAccount = this.getUserAccount();
|
|
883
|
+
const userPositionsAccount = this.getUserPositionsAccount();
|
|
884
|
+
const userPosition = this.getUserPosition(order.marketIndex);
|
|
885
|
+
const market = this.clearingHouse.getMarket(order.marketIndex);
|
|
886
|
+
|
|
887
|
+
if (isEmptyPosition(userPosition)) {
|
|
888
|
+
return false;
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
const newState = calculateNewStateAfterOrder(
|
|
892
|
+
userAccount,
|
|
893
|
+
userPosition,
|
|
894
|
+
market,
|
|
895
|
+
order
|
|
896
|
+
);
|
|
897
|
+
if (newState === null) {
|
|
898
|
+
return false;
|
|
899
|
+
}
|
|
900
|
+
const [userAccountAfter, userPositionAfter, marketAfter] = newState;
|
|
901
|
+
|
|
902
|
+
const totalPositionValue = userPositionsAccount.positions.reduce(
|
|
903
|
+
(positionValue, marketPosition) => {
|
|
904
|
+
let market = this.clearingHouse.getMarket(marketPosition.marketIndex);
|
|
905
|
+
if (marketPosition.marketIndex.eq(order.marketIndex)) {
|
|
906
|
+
market = marketAfter;
|
|
907
|
+
marketPosition = userPositionAfter;
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
return positionValue.add(
|
|
911
|
+
calculateBaseAssetValue(market, marketPosition)
|
|
912
|
+
);
|
|
913
|
+
},
|
|
914
|
+
ZERO
|
|
915
|
+
);
|
|
916
|
+
|
|
917
|
+
if (totalPositionValue.eq(ZERO)) {
|
|
918
|
+
return true;
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
const unrealizedPnL = userPositionsAccount.positions.reduce(
|
|
922
|
+
(pnl, marketPosition) => {
|
|
923
|
+
let market = this.clearingHouse.getMarket(marketPosition.marketIndex);
|
|
924
|
+
pnl = pnl.add(
|
|
925
|
+
calculatePositionFundingPNL(market, marketPosition).div(
|
|
926
|
+
PRICE_TO_QUOTE_PRECISION
|
|
927
|
+
)
|
|
928
|
+
);
|
|
929
|
+
|
|
930
|
+
if (marketPosition.marketIndex.eq(order.marketIndex)) {
|
|
931
|
+
market = marketAfter;
|
|
932
|
+
marketPosition = userPositionAfter;
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
// update
|
|
936
|
+
return pnl.add(calculatePositionPNL(market, marketPosition, false));
|
|
937
|
+
},
|
|
938
|
+
ZERO
|
|
939
|
+
);
|
|
940
|
+
const totalCollateral = userAccountAfter.collateral.add(unrealizedPnL);
|
|
941
|
+
|
|
942
|
+
const marginRatioAfter = totalCollateral
|
|
943
|
+
.mul(TEN_THOUSAND)
|
|
944
|
+
.div(totalPositionValue);
|
|
945
|
+
|
|
946
|
+
const marginRatioInitial =
|
|
947
|
+
this.clearingHouse.getStateAccount().marginRatioInitial;
|
|
948
|
+
return marginRatioAfter.gte(marginRatioInitial);
|
|
949
|
+
}
|
|
868
950
|
}
|
package/src/constants/markets.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import BN from '
|
|
1
|
+
import { BN } from '../';
|
|
2
2
|
|
|
3
3
|
type Market = {
|
|
4
4
|
symbol: string;
|
|
@@ -98,6 +98,22 @@ export const Markets: Market[] = [
|
|
|
98
98
|
mainnetPythOracle: 'HqFyq1wh1xKvL7KDqqT7NJeSPdAqsDqnmBisUC2XdXAX',
|
|
99
99
|
launchTs: 1643686767000,
|
|
100
100
|
},
|
|
101
|
+
{
|
|
102
|
+
symbol: 'FTT-PERP',
|
|
103
|
+
baseAssetSymbol: 'FTT',
|
|
104
|
+
marketIndex: new BN(11),
|
|
105
|
+
devnetPythOracle: '6vivTRs5ZPeeXbjo7dfburfaYDWoXjBtdtuYgQRuGfu',
|
|
106
|
+
mainnetPythOracle: '8JPJJkmDScpcNmBRKGZuPuG2GYAveQgP3t5gFuMymwvF',
|
|
107
|
+
launchTs: 1644382122000,
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
symbol: 'LTC-PERP',
|
|
111
|
+
baseAssetSymbol: 'LTC',
|
|
112
|
+
marketIndex: new BN(12),
|
|
113
|
+
devnetPythOracle: 'BLArYBCUYhdWiY8PCUTpvFE21iaJq85dvxLk9bYMobcU',
|
|
114
|
+
mainnetPythOracle: '8RMnV1eD55iqUFJLMguPkYBkq8DCtx81XcmAja93LvRR',
|
|
115
|
+
launchTs: 1645027429000,
|
|
116
|
+
},
|
|
101
117
|
// {
|
|
102
118
|
// symbol: 'mSOL-PERP',
|
|
103
119
|
// baseAssetSymbol: 'mSOL',
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import BN from '
|
|
1
|
+
import { BN } from '../';
|
|
2
2
|
|
|
3
3
|
export const ZERO = new BN(0);
|
|
4
4
|
export const ONE = new BN(1);
|
|
5
|
+
export const TWO = new BN(2);
|
|
5
6
|
export const TEN_THOUSAND = new BN(10000);
|
|
6
7
|
export const BN_MAX = new BN(Number.MAX_SAFE_INTEGER);
|
|
7
8
|
|
|
@@ -15,6 +16,7 @@ export const FUNDING_PAYMENT_PRECISION = new BN(10000);
|
|
|
15
16
|
export const PEG_PRECISION = new BN(1000);
|
|
16
17
|
|
|
17
18
|
export const AMM_RESERVE_PRECISION = new BN(10 ** 13);
|
|
19
|
+
export const BASE_PRECISION = AMM_RESERVE_PRECISION;
|
|
18
20
|
export const AMM_TO_QUOTE_PRECISION_RATIO =
|
|
19
21
|
AMM_RESERVE_PRECISION.div(QUOTE_PRECISION); // 10^7
|
|
20
22
|
export const PRICE_TO_QUOTE_PRECISION =
|
|
@@ -95,7 +95,10 @@ const main = async () => {
|
|
|
95
95
|
clearingHouse.getMarket(solMarketInfo.marketIndex)
|
|
96
96
|
);
|
|
97
97
|
|
|
98
|
-
const formattedPrice = convertToNumber(
|
|
98
|
+
const formattedPrice = convertToNumber(
|
|
99
|
+
currentMarketPrice,
|
|
100
|
+
MARK_PRICE_PRECISION
|
|
101
|
+
);
|
|
99
102
|
|
|
100
103
|
console.log(`Current Market Price is $${formattedPrice}`);
|
|
101
104
|
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { ConfirmOptions, Connection, PublicKey } from '@solana/web3.js';
|
|
2
|
+
import { IWallet } from '../types';
|
|
3
|
+
import { BulkAccountLoader } from '../accounts/bulkAccountLoader';
|
|
4
|
+
import { TxSender } from '../tx/types';
|
|
5
|
+
import { Idl, Program, Provider } from '@project-serum/anchor';
|
|
6
|
+
import { ClearingHouse } from '../clearingHouse';
|
|
7
|
+
import clearingHouseIDL from '../idl/clearing_house.json';
|
|
8
|
+
import { WebSocketClearingHouseAccountSubscriber } from '../accounts/webSocketClearingHouseAccountSubscriber';
|
|
9
|
+
import { DefaultTxSender } from '../tx/defaultTxSender';
|
|
10
|
+
import { ClearingHouseAccountSubscriber } from '../accounts/types';
|
|
11
|
+
import { PollingClearingHouseAccountSubscriber } from '../accounts/pollingClearingHouseAccountSubscriber';
|
|
12
|
+
import { Admin } from '../admin';
|
|
13
|
+
|
|
14
|
+
export type ClearingHouseConfigType = 'websocket' | 'polling' | 'custom';
|
|
15
|
+
|
|
16
|
+
type BaseClearingHouseConfig = {
|
|
17
|
+
type: ClearingHouseConfigType;
|
|
18
|
+
connection: Connection;
|
|
19
|
+
wallet: IWallet;
|
|
20
|
+
programID: PublicKey;
|
|
21
|
+
opts?: ConfirmOptions;
|
|
22
|
+
txSender?: TxSender;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
type WebSocketClearingHouseConfiguration = BaseClearingHouseConfig;
|
|
26
|
+
|
|
27
|
+
type PollingClearingHouseConfiguration = BaseClearingHouseConfig & {
|
|
28
|
+
accountLoader: BulkAccountLoader;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
type ClearingHouseConfig =
|
|
32
|
+
| PollingClearingHouseConfiguration
|
|
33
|
+
| WebSocketClearingHouseConfiguration;
|
|
34
|
+
|
|
35
|
+
export function getWebSocketClearingHouseConfig(
|
|
36
|
+
connection: Connection,
|
|
37
|
+
wallet: IWallet,
|
|
38
|
+
programID: PublicKey,
|
|
39
|
+
opts: ConfirmOptions = Provider.defaultOptions(),
|
|
40
|
+
txSender?: TxSender
|
|
41
|
+
): WebSocketClearingHouseConfiguration {
|
|
42
|
+
return {
|
|
43
|
+
type: 'websocket',
|
|
44
|
+
connection,
|
|
45
|
+
wallet,
|
|
46
|
+
programID,
|
|
47
|
+
opts,
|
|
48
|
+
txSender,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function getPollingClearingHouseConfig(
|
|
53
|
+
connection: Connection,
|
|
54
|
+
wallet: IWallet,
|
|
55
|
+
programID: PublicKey,
|
|
56
|
+
accountLoader: BulkAccountLoader,
|
|
57
|
+
opts: ConfirmOptions = Provider.defaultOptions(),
|
|
58
|
+
txSender?: TxSender
|
|
59
|
+
): PollingClearingHouseConfiguration {
|
|
60
|
+
return {
|
|
61
|
+
type: 'polling',
|
|
62
|
+
connection,
|
|
63
|
+
wallet,
|
|
64
|
+
programID,
|
|
65
|
+
accountLoader,
|
|
66
|
+
opts,
|
|
67
|
+
txSender,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function getClearingHouse(config: ClearingHouseConfig): ClearingHouse {
|
|
72
|
+
const provider = new Provider(config.connection, config.wallet, config.opts);
|
|
73
|
+
const program = new Program(
|
|
74
|
+
clearingHouseIDL as Idl,
|
|
75
|
+
config.programID,
|
|
76
|
+
provider
|
|
77
|
+
);
|
|
78
|
+
let accountSubscriber: ClearingHouseAccountSubscriber;
|
|
79
|
+
if (config.type === 'websocket') {
|
|
80
|
+
accountSubscriber = new WebSocketClearingHouseAccountSubscriber(program);
|
|
81
|
+
} else if (config.type === 'polling') {
|
|
82
|
+
accountSubscriber = new PollingClearingHouseAccountSubscriber(
|
|
83
|
+
program,
|
|
84
|
+
(config as PollingClearingHouseConfiguration).accountLoader
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const txSender = config.txSender || new DefaultTxSender(provider);
|
|
89
|
+
return new ClearingHouse(
|
|
90
|
+
config.connection,
|
|
91
|
+
config.wallet,
|
|
92
|
+
program,
|
|
93
|
+
accountSubscriber,
|
|
94
|
+
txSender,
|
|
95
|
+
config.opts
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function getAdmin(config: ClearingHouseConfig): Admin {
|
|
100
|
+
const provider = new Provider(config.connection, config.wallet, config.opts);
|
|
101
|
+
const program = new Program(
|
|
102
|
+
clearingHouseIDL as Idl,
|
|
103
|
+
config.programID,
|
|
104
|
+
provider
|
|
105
|
+
);
|
|
106
|
+
let accountSubscriber: ClearingHouseAccountSubscriber;
|
|
107
|
+
if (config.type === 'websocket') {
|
|
108
|
+
accountSubscriber = new WebSocketClearingHouseAccountSubscriber(program);
|
|
109
|
+
} else if (config.type === 'polling') {
|
|
110
|
+
accountSubscriber = new PollingClearingHouseAccountSubscriber(
|
|
111
|
+
program,
|
|
112
|
+
(config as PollingClearingHouseConfiguration).accountLoader
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const txSender = config.txSender || new DefaultTxSender(provider);
|
|
117
|
+
return new Admin(
|
|
118
|
+
config.connection,
|
|
119
|
+
config.wallet,
|
|
120
|
+
program,
|
|
121
|
+
accountSubscriber,
|
|
122
|
+
txSender,
|
|
123
|
+
config.opts
|
|
124
|
+
);
|
|
125
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { PublicKey } from '@solana/web3.js';
|
|
2
|
+
import { ClearingHouse } from '../clearingHouse';
|
|
3
|
+
import { BulkAccountLoader } from '../accounts/bulkAccountLoader';
|
|
4
|
+
import { ClearingHouseUser } from '../clearingHouseUser';
|
|
5
|
+
import { UserAccountSubscriber } from '../accounts/types';
|
|
6
|
+
import { WebSocketUserAccountSubscriber } from '../accounts/webSocketUserAccountSubscriber';
|
|
7
|
+
import { PollingUserAccountSubscriber } from '../accounts/pollingUserAccountSubscriber';
|
|
8
|
+
|
|
9
|
+
export type ClearingHouseUserConfigType = 'websocket' | 'polling' | 'custom';
|
|
10
|
+
|
|
11
|
+
type BaseClearingHouseUserConfig = {
|
|
12
|
+
type: ClearingHouseUserConfigType;
|
|
13
|
+
clearingHouse: ClearingHouse;
|
|
14
|
+
authority: PublicKey;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
type WebSocketClearingHouseUserConfig = BaseClearingHouseUserConfig;
|
|
18
|
+
|
|
19
|
+
type PollingClearingHouseUserConfig = BaseClearingHouseUserConfig & {
|
|
20
|
+
accountLoader: BulkAccountLoader;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
type ClearingHouseUserConfig =
|
|
24
|
+
| PollingClearingHouseUserConfig
|
|
25
|
+
| WebSocketClearingHouseUserConfig;
|
|
26
|
+
|
|
27
|
+
export function getWebSocketClearingHouseUserConfig(
|
|
28
|
+
clearingHouse: ClearingHouse,
|
|
29
|
+
authority: PublicKey
|
|
30
|
+
): WebSocketClearingHouseUserConfig {
|
|
31
|
+
return {
|
|
32
|
+
type: 'websocket',
|
|
33
|
+
clearingHouse,
|
|
34
|
+
authority,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function getPollingClearingHouseUserConfig(
|
|
39
|
+
clearingHouse: ClearingHouse,
|
|
40
|
+
authority: PublicKey,
|
|
41
|
+
accountLoader: BulkAccountLoader
|
|
42
|
+
): PollingClearingHouseUserConfig {
|
|
43
|
+
return {
|
|
44
|
+
type: 'polling',
|
|
45
|
+
clearingHouse,
|
|
46
|
+
authority,
|
|
47
|
+
accountLoader,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function getClearingHouseUser(
|
|
52
|
+
config: ClearingHouseUserConfig
|
|
53
|
+
): ClearingHouseUser {
|
|
54
|
+
let accountSubscriber: UserAccountSubscriber;
|
|
55
|
+
if (config.type === 'websocket') {
|
|
56
|
+
accountSubscriber = new WebSocketUserAccountSubscriber(
|
|
57
|
+
config.clearingHouse.program,
|
|
58
|
+
config.authority
|
|
59
|
+
);
|
|
60
|
+
} else if (config.type === 'polling') {
|
|
61
|
+
accountSubscriber = new PollingUserAccountSubscriber(
|
|
62
|
+
config.clearingHouse.program,
|
|
63
|
+
config.authority,
|
|
64
|
+
(config as PollingClearingHouseUserConfig).accountLoader
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return new ClearingHouseUser(
|
|
69
|
+
config.clearingHouse,
|
|
70
|
+
config.authority,
|
|
71
|
+
accountSubscriber
|
|
72
|
+
);
|
|
73
|
+
}
|