@drift-labs/sdk 2.34.1-beta.1 → 2.34.1-beta.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.
@@ -3,14 +3,55 @@ import {
3
3
  ZERO,
4
4
  calculateSpotMarketBorrowCapacity,
5
5
  SPOT_MARKET_CUMULATIVE_INTEREST_PRECISION,
6
+ calculateSizePremiumLiabilityWeight,
6
7
  } from '../../src';
7
8
  import { mockSpotMarkets } from '../dlob/helpers';
9
+ import * as _ from 'lodash';
8
10
 
9
11
  import { assert } from '../../src/assert/assert';
10
12
 
11
13
  describe('Spot Tests', () => {
14
+ it('size premium via imf factor', () => {
15
+ const maintLiabWgt = new BN(1.1 * 1e4);
16
+
17
+ const ans0 = calculateSizePremiumLiabilityWeight(
18
+ new BN(200000 * 1e9),
19
+ ZERO,
20
+ maintLiabWgt,
21
+ new BN(1e4)
22
+ );
23
+ assert(ans0.eq(maintLiabWgt));
24
+
25
+ const ans = calculateSizePremiumLiabilityWeight(
26
+ new BN(200000 * 1e9),
27
+ new BN(0.00055 * 1e6),
28
+ maintLiabWgt,
29
+ new BN(1e4)
30
+ );
31
+ assert(ans.eq(new BN('11259')));
32
+ assert(ans.gt(maintLiabWgt));
33
+
34
+ const ans2 = calculateSizePremiumLiabilityWeight(
35
+ new BN(10000 * 1e9),
36
+ new BN(0.003 * 1e6),
37
+ maintLiabWgt,
38
+ new BN(1e4)
39
+ );
40
+ assert(ans2.eq(new BN('11800')));
41
+ assert(ans.gt(maintLiabWgt));
42
+
43
+ const ans3 = calculateSizePremiumLiabilityWeight(
44
+ new BN(100000 * 1e9),
45
+ new BN(0.003 * 1e6),
46
+ maintLiabWgt,
47
+ new BN(1e4)
48
+ );
49
+ assert(ans3.eq(new BN('18286')));
50
+ assert(ans3.gt(maintLiabWgt));
51
+ });
52
+
12
53
  it('base borrow capacity', () => {
13
- const mockSpot = mockSpotMarkets[0];
54
+ const mockSpot = _.cloneDeep(mockSpotMarkets[0]);
14
55
  mockSpot.maxBorrowRate = 1000000;
15
56
  mockSpot.optimalBorrowRate = 100000;
16
57
  mockSpot.optimalUtilization = 700000;
@@ -70,7 +111,7 @@ describe('Spot Tests', () => {
70
111
  });
71
112
 
72
113
  it('complex borrow capacity', () => {
73
- const mockSpot = mockSpotMarkets[0];
114
+ const mockSpot = _.cloneDeep(mockSpotMarkets[0]);
74
115
  mockSpot.maxBorrowRate = 1000000;
75
116
  mockSpot.optimalBorrowRate = 70000;
76
117
  mockSpot.optimalUtilization = 700000;
@@ -0,0 +1,89 @@
1
+ import { PublicKey } from '@solana/web3.js';
2
+
3
+ import {
4
+ SpotPosition,
5
+ SpotBalanceType,
6
+ Order,
7
+ OrderStatus,
8
+ MarketType,
9
+ OrderType,
10
+ PositionDirection,
11
+ OrderTriggerCondition,
12
+ UserStatus,
13
+ UserAccount,
14
+ ZERO,
15
+ } from '../../src';
16
+
17
+ import { mockPerpPosition } from '../dlob/helpers';
18
+
19
+ export const mockOrder: Order = {
20
+ status: OrderStatus.INIT,
21
+ orderType: OrderType.MARKET,
22
+ marketType: MarketType.PERP,
23
+ slot: ZERO,
24
+ orderId: 0,
25
+ userOrderId: 0,
26
+ marketIndex: 0,
27
+ price: ZERO,
28
+ baseAssetAmount: ZERO,
29
+ baseAssetAmountFilled: ZERO,
30
+ quoteAssetAmount: ZERO,
31
+ quoteAssetAmountFilled: ZERO,
32
+ direction: PositionDirection.LONG,
33
+ reduceOnly: false,
34
+ triggerPrice: ZERO,
35
+ triggerCondition: OrderTriggerCondition.ABOVE,
36
+ existingPositionDirection: PositionDirection.LONG,
37
+ postOnly: false,
38
+ immediateOrCancel: false,
39
+ oraclePriceOffset: 0,
40
+ auctionDuration: 0,
41
+ auctionStartPrice: ZERO,
42
+ auctionEndPrice: ZERO,
43
+ maxTs: ZERO,
44
+ };
45
+
46
+ export const mockSpotPosition: SpotPosition = {
47
+ marketIndex: 0,
48
+ balanceType: SpotBalanceType.DEPOSIT,
49
+ scaledBalance: ZERO,
50
+ openOrders: 0,
51
+ openBids: ZERO,
52
+ openAsks: ZERO,
53
+ cumulativeDeposits: ZERO,
54
+ };
55
+
56
+ export const mockUserAccount: UserAccount = {
57
+ authority: PublicKey.default,
58
+ delegate: PublicKey.default,
59
+ name: [1],
60
+ subAccountId: 0,
61
+ spotPositions: Array.from({ length: 8 }, function () {
62
+ return Object.assign({}, mockSpotPosition);
63
+ }),
64
+ perpPositions: Array.from({ length: 8 }, function () {
65
+ return Object.assign({}, mockPerpPosition);
66
+ }),
67
+ orders: Array.from({ length: 8 }, function () {
68
+ return Object.assign({}, mockOrder);
69
+ }),
70
+ status: UserStatus.ACTIVE,
71
+ nextLiquidationId: 0,
72
+ nextOrderId: 0,
73
+ maxMarginRatio: 0,
74
+ lastAddPerpLpSharesTs: ZERO,
75
+ settledPerpPnl: ZERO,
76
+ totalDeposits: ZERO,
77
+ totalWithdraws: ZERO,
78
+ totalSocialLoss: ZERO,
79
+ cumulativePerpFunding: ZERO,
80
+ cumulativeSpotFees: ZERO,
81
+ liquidationMarginFreed: ZERO,
82
+ lastActiveSlot: ZERO,
83
+ isMarginTradingEnabled: true,
84
+ idle: false,
85
+ openOrders: 0,
86
+ hasOpenOrder: false,
87
+ openAuctions: 0,
88
+ hasOpenAuction: false,
89
+ };
@@ -0,0 +1,286 @@
1
+ import {
2
+ BN,
3
+ ZERO,
4
+ User,
5
+ UserAccount,
6
+ PublicKey,
7
+ PerpMarketAccount,
8
+ SpotMarketAccount,
9
+ PRICE_PRECISION,
10
+ OraclePriceData,
11
+ BASE_PRECISION,
12
+ QUOTE_PRECISION,
13
+ calculatePositionPNL,
14
+ SPOT_MARKET_BALANCE_PRECISION,
15
+ } from '../../src';
16
+ import { MockUserMap, mockPerpMarkets, mockSpotMarkets } from '../dlob/helpers';
17
+ import { assert } from '../../src/assert/assert';
18
+ import { mockUserAccount } from './helpers';
19
+ import * as _ from 'lodash';
20
+
21
+ async function makeMockUser(
22
+ myMockPerpMarkets,
23
+ myMockSpotMarkets,
24
+ myMockUserAccount,
25
+ perpOraclePriceList,
26
+ spotOraclePriceList
27
+ ): Promise<User> {
28
+ const umap = new MockUserMap();
29
+ const mockUser: User = await umap.mustGet('1');
30
+ mockUser._isSubscribed = true;
31
+ mockUser.driftClient._isSubscribed = true;
32
+ const oraclePriceMap = {};
33
+ // console.log(perpOraclePriceList, myMockPerpMarkets.length);
34
+ // console.log(spotOraclePriceList, myMockSpotMarkets.length);
35
+
36
+ for (let i = 0; i < myMockPerpMarkets.length; i++) {
37
+ oraclePriceMap[myMockPerpMarkets[i].amm.oracle.toString()] =
38
+ perpOraclePriceList[i];
39
+ }
40
+ for (let i = 0; i < myMockSpotMarkets.length; i++) {
41
+ oraclePriceMap[myMockSpotMarkets[i].oracle.toString()] =
42
+ spotOraclePriceList[i];
43
+ }
44
+ // console.log(oraclePriceMap);
45
+
46
+ function getMockUserAccount(): UserAccount {
47
+ return myMockUserAccount;
48
+ }
49
+ function getMockPerpMarket(marketIndex): PerpMarketAccount {
50
+ return myMockPerpMarkets[marketIndex];
51
+ }
52
+ function getMockSpotMarket(marketIndex): SpotMarketAccount {
53
+ return myMockSpotMarkets[marketIndex];
54
+ }
55
+ function getMockOracle(oracleKey: PublicKey) {
56
+ // console.log('oracleKey.toString():', oracleKey.toString());
57
+ // console.log(
58
+ // 'oraclePriceMap[oracleKey.toString()]:',
59
+ // oraclePriceMap[oracleKey.toString()]
60
+ // );
61
+
62
+ const QUOTE_ORACLE_PRICE_DATA: OraclePriceData = {
63
+ price: new BN(
64
+ oraclePriceMap[oracleKey.toString()] * PRICE_PRECISION.toNumber()
65
+ ),
66
+ slot: new BN(0),
67
+ confidence: new BN(1),
68
+ hasSufficientNumberOfDataPoints: true,
69
+ };
70
+
71
+ return {
72
+ data: QUOTE_ORACLE_PRICE_DATA,
73
+ slot: 0,
74
+ };
75
+ }
76
+
77
+ mockUser.getUserAccount = getMockUserAccount;
78
+ mockUser.driftClient.getPerpMarketAccount = getMockPerpMarket;
79
+ mockUser.driftClient.getSpotMarketAccount = getMockSpotMarket;
80
+ mockUser.driftClient.getOraclePriceDataAndSlot = getMockOracle;
81
+ return mockUser;
82
+ }
83
+
84
+ describe('User Tests', () => {
85
+ it('empty user account', async () => {
86
+ console.log(mockSpotMarkets[0]);
87
+ const myMockPerpMarkets = _.cloneDeep(mockPerpMarkets);
88
+ const myMockSpotMarkets = _.cloneDeep(mockSpotMarkets);
89
+ const myMockUserAccount = _.cloneDeep(mockUserAccount);
90
+ console.log(
91
+ 'spot cumulativeDepositInterest:',
92
+ mockSpotMarkets[0].cumulativeDepositInterest.toString()
93
+ );
94
+ const mockUser: User = await makeMockUser(
95
+ myMockPerpMarkets,
96
+ myMockSpotMarkets,
97
+ myMockUserAccount,
98
+ [1, 1, 1, 1, 1, 1, 1, 1],
99
+ [1, 1, 1, 1, 1, 1, 1, 1]
100
+ );
101
+ const uA = mockUser.getUserAccount();
102
+ assert(uA.idle == false);
103
+ console.log(
104
+ 'spot cumulativeDepositInterest:',
105
+ myMockSpotMarkets[0].cumulativeDepositInterest.toString()
106
+ );
107
+ assert(mockUser.getFreeCollateral().eq(ZERO));
108
+
109
+ console.log(mockUser.getHealth());
110
+ assert(mockUser.getHealth() == 100);
111
+
112
+ console.log(mockUser.getMaxLeverageForPerp(0));
113
+ assert(mockUser.getMaxLeverageForPerp(0).eq(ZERO));
114
+ });
115
+
116
+ it('user account unsettled pnl', async () => {
117
+ // no collateral, but positive upnl no liability
118
+ const myMockPerpMarkets = _.cloneDeep(mockPerpMarkets);
119
+ const myMockSpotMarkets = _.cloneDeep(mockSpotMarkets);
120
+ const myMockUserAccount = _.cloneDeep(mockUserAccount);
121
+
122
+ myMockUserAccount.perpPositions[0].baseAssetAmount = new BN(
123
+ 0 * BASE_PRECISION.toNumber()
124
+ );
125
+ myMockUserAccount.perpPositions[0].quoteAssetAmount = new BN(
126
+ 10 * QUOTE_PRECISION.toNumber()
127
+ );
128
+ assert(
129
+ myMockUserAccount.perpPositions[0].quoteAssetAmount.eq(new BN('10000000'))
130
+ ); // $10
131
+
132
+ const mockUser: User = await makeMockUser(
133
+ myMockPerpMarkets,
134
+ myMockSpotMarkets,
135
+ myMockUserAccount,
136
+ [1, 1, 1, 1, 1, 1, 1, 1],
137
+ [1, 1, 1, 1, 1, 1, 1, 1]
138
+ );
139
+ const uA = mockUser.getUserAccount();
140
+ assert(uA.idle == false);
141
+ const activePerps = mockUser.getActivePerpPositions();
142
+ assert(activePerps.length == 1);
143
+ assert(uA.perpPositions[0].quoteAssetAmount.eq(new BN('10000000'))); // $10
144
+ assert(mockUser.getFreeCollateral().eq(ZERO));
145
+
146
+ const quotePrice = mockUser.driftClient.getOracleDataForSpotMarket(0).price;
147
+ console.log('quotePrice:', quotePrice.toString());
148
+ assert(quotePrice.eq(new BN('1000000')));
149
+ const pnl1 = calculatePositionPNL(
150
+ myMockPerpMarkets[0],
151
+ activePerps[0],
152
+ false,
153
+ mockUser.driftClient.getOracleDataForPerpMarket(0)
154
+ );
155
+ console.log('pnl1:', pnl1.toString());
156
+ assert(pnl1.eq(new BN('10000000')));
157
+
158
+ const upnl = mockUser.getUnrealizedPNL(false, undefined, undefined, false);
159
+ console.log('upnl:', upnl.toString());
160
+ assert(upnl.eq(new BN('10000000'))); // $10
161
+
162
+ const liqResult = mockUser.canBeLiquidated();
163
+ console.log(liqResult);
164
+ assert(liqResult.canBeLiquidated == false);
165
+ assert(liqResult.marginRequirement.eq(ZERO));
166
+ assert(liqResult.totalCollateral.eq(ZERO));
167
+
168
+ console.log(mockUser.getHealth());
169
+ assert(mockUser.getHealth() == 100);
170
+
171
+ console.log(mockUser.getMaxLeverageForPerp(0));
172
+ assert(mockUser.getMaxLeverageForPerp(0).eq(ZERO));
173
+ });
174
+
175
+ it('liquidatable long user account', async () => {
176
+ // no collateral, but positive upnl w/ liability
177
+
178
+ const myMockPerpMarkets = _.cloneDeep(mockPerpMarkets);
179
+ const myMockSpotMarkets = _.cloneDeep(mockSpotMarkets);
180
+ const myMockUserAccount = _.cloneDeep(mockUserAccount);
181
+ myMockUserAccount.perpPositions[0].baseAssetAmount = new BN(
182
+ 20 * BASE_PRECISION.toNumber()
183
+ );
184
+ myMockUserAccount.perpPositions[0].quoteAssetAmount = new BN(
185
+ -10 * QUOTE_PRECISION.toNumber()
186
+ );
187
+
188
+ const mockUser: User = await makeMockUser(
189
+ myMockPerpMarkets,
190
+ myMockSpotMarkets,
191
+ myMockUserAccount,
192
+ [1, 1, 1, 1, 1, 1, 1, 1],
193
+ [1, 1, 1, 1, 1, 1, 1, 1]
194
+ );
195
+ const uA = mockUser.getUserAccount();
196
+ assert(uA.idle == false);
197
+
198
+ assert(mockUser.getFreeCollateral().eq(ZERO));
199
+ const upnl = mockUser.getUnrealizedPNL(true, 0, undefined, false);
200
+ console.log('upnl:', upnl.toString());
201
+ assert(upnl.eq(new BN('10000000'))); // $10
202
+
203
+ const liqResult = mockUser.canBeLiquidated();
204
+ console.log(liqResult);
205
+ assert(liqResult.canBeLiquidated == true);
206
+ assert(liqResult.marginRequirement.eq(new BN('2000000'))); //10x maint leverage
207
+ assert(liqResult.totalCollateral.eq(ZERO));
208
+
209
+ console.log(mockUser.getHealth());
210
+ assert(mockUser.getHealth() == 0);
211
+
212
+ console.log(mockUser.getMaxLeverageForPerp(0));
213
+ assert(mockUser.getMaxLeverageForPerp(0).eq(new BN('20000')));
214
+ });
215
+
216
+ it('large usdc user account', async () => {
217
+ // no collateral, but positive upnl w/ liability
218
+
219
+ const myMockPerpMarkets = _.cloneDeep(mockPerpMarkets);
220
+ const myMockSpotMarkets = _.cloneDeep(mockSpotMarkets);
221
+ const myMockUserAccount = _.cloneDeep(mockUserAccount);
222
+
223
+ myMockPerpMarkets[0].imfFactor = 550;
224
+ myMockUserAccount.spotPositions[0].scaledBalance = new BN(
225
+ 100000 * SPOT_MARKET_BALANCE_PRECISION.toNumber()
226
+ ); //100k
227
+
228
+ const mockUser: User = await makeMockUser(
229
+ myMockPerpMarkets,
230
+ myMockSpotMarkets,
231
+ myMockUserAccount,
232
+ [1, 1, 1, 1, 1, 1, 1, 1],
233
+ [1, 1, 1, 1, 1, 1, 1, 1]
234
+ );
235
+ const uA = mockUser.getUserAccount();
236
+ assert(uA.idle == false);
237
+
238
+ assert(uA.perpPositions[0].baseAssetAmount.eq(ZERO));
239
+ assert(uA.perpPositions[0].quoteAssetAmount.eq(ZERO));
240
+ assert(mockUser.getActivePerpPositions().length == 0);
241
+
242
+ assert(
243
+ uA.spotPositions[0].scaledBalance.eq(
244
+ new BN(100000 * SPOT_MARKET_BALANCE_PRECISION.toNumber())
245
+ )
246
+ );
247
+ for (let i = 1; i < 8; i++) {
248
+ assert(uA.spotPositions[i].scaledBalance.eq(ZERO));
249
+ }
250
+ console.log(
251
+ 'mockUser.getTokenAmount():',
252
+ mockUser.getTokenAmount(0).toString()
253
+ );
254
+ console.log(
255
+ 'spot cumulativeDepositInterest:',
256
+ mockSpotMarkets[0].cumulativeDepositInterest.toString()
257
+ );
258
+ assert(mockUser.getTokenAmount(0).eq(new BN('10000000000')));
259
+ assert(mockUser.getNetSpotMarketValue().eq(new BN('10000000000')));
260
+ assert(
261
+ mockUser
262
+ .getSpotMarketAssetAndLiabilityValue()
263
+ .totalLiabilityValue.eq(ZERO)
264
+ );
265
+
266
+ assert(mockUser.getFreeCollateral().eq(ZERO));
267
+ const upnl = mockUser.getUnrealizedPNL(true, 0, undefined, false);
268
+ console.log('upnl:', upnl.toString());
269
+ assert(upnl.eq(new BN('0'))); // $10
270
+
271
+ const liqResult = mockUser.canBeLiquidated();
272
+ console.log(liqResult);
273
+ assert(liqResult.canBeLiquidated == false);
274
+ assert(liqResult.marginRequirement.eq(new BN('0'))); //10x maint leverage
275
+ assert(liqResult.totalCollateral.eq(ZERO));
276
+
277
+ console.log(mockUser.getHealth());
278
+ assert(mockUser.getHealth() == 100);
279
+
280
+ console.log(
281
+ 'getMaxLeverageForPerp:',
282
+ mockUser.getMaxLeverageForPerp(0).toString()
283
+ );
284
+ assert(mockUser.getMaxLeverageForPerp(0).eq(new BN('0')));
285
+ });
286
+ });