@drift-labs/sdk 0.2.0-master.13 → 0.2.0-master.14

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.
Files changed (63) hide show
  1. package/lib/accounts/fetch.d.ts +2 -1
  2. package/lib/accounts/fetch.js +9 -1
  3. package/lib/accounts/pollingUserStatsAccountSubscriber.d.ts +27 -0
  4. package/lib/accounts/pollingUserStatsAccountSubscriber.js +113 -0
  5. package/lib/accounts/types.d.ts +14 -1
  6. package/lib/accounts/webSocketUserStatsAccountSubsriber.d.ts +20 -0
  7. package/lib/accounts/webSocketUserStatsAccountSubsriber.js +47 -0
  8. package/lib/addresses/pda.d.ts +1 -0
  9. package/lib/addresses/pda.js +8 -1
  10. package/lib/admin.d.ts +2 -0
  11. package/lib/admin.js +18 -0
  12. package/lib/clearingHouse.d.ts +17 -1
  13. package/lib/clearingHouse.js +195 -14
  14. package/lib/clearingHouseConfig.d.ts +1 -0
  15. package/lib/clearingHouseUser.d.ts +5 -0
  16. package/lib/clearingHouseUser.js +97 -3
  17. package/lib/clearingHouseUserStats.d.ts +17 -0
  18. package/lib/clearingHouseUserStats.js +36 -0
  19. package/lib/clearingHouseUserStatsConfig.d.ts +14 -0
  20. package/{src/clearingHouseConfig.js → lib/clearingHouseUserStatsConfig.js} +0 -0
  21. package/lib/config.js +1 -1
  22. package/lib/idl/clearing_house.json +614 -64
  23. package/lib/math/bankBalance.d.ts +4 -0
  24. package/lib/math/bankBalance.js +23 -1
  25. package/lib/math/oracles.d.ts +3 -0
  26. package/lib/math/oracles.js +25 -5
  27. package/lib/math/position.js +2 -1
  28. package/lib/math/trade.js +2 -2
  29. package/lib/types.d.ts +55 -8
  30. package/lib/types.js +6 -0
  31. package/package.json +1 -1
  32. package/src/accounts/fetch.ts +27 -2
  33. package/src/accounts/pollingUserStatsAccountSubscriber.ts +172 -0
  34. package/src/accounts/types.ts +18 -0
  35. package/src/accounts/webSocketUserStatsAccountSubsriber.ts +80 -0
  36. package/src/addresses/pda.ts +13 -0
  37. package/src/admin.ts +29 -1
  38. package/src/clearingHouse.ts +318 -15
  39. package/src/clearingHouseConfig.ts +1 -0
  40. package/src/clearingHouseUser.ts +113 -10
  41. package/src/clearingHouseUserStats.ts +53 -0
  42. package/src/clearingHouseUserStatsConfig.ts +18 -0
  43. package/src/config.ts +1 -1
  44. package/src/idl/clearing_house.json +614 -64
  45. package/src/math/bankBalance.ts +49 -0
  46. package/src/math/oracles.ts +42 -5
  47. package/src/math/position.ts +2 -1
  48. package/src/math/trade.ts +2 -2
  49. package/src/types.ts +59 -8
  50. package/src/accounts/bulkAccountLoader.js +0 -197
  51. package/src/accounts/bulkUserSubscription.js +0 -33
  52. package/src/accounts/pollingClearingHouseAccountSubscriber.js +0 -311
  53. package/src/accounts/pollingOracleSubscriber.js +0 -93
  54. package/src/accounts/pollingTokenAccountSubscriber.js +0 -90
  55. package/src/accounts/pollingUserAccountSubscriber.js +0 -132
  56. package/src/accounts/types.js +0 -10
  57. package/src/accounts/utils.js +0 -7
  58. package/src/accounts/webSocketAccountSubscriber.js +0 -93
  59. package/src/accounts/webSocketClearingHouseAccountSubscriber.js +0 -233
  60. package/src/accounts/webSocketUserAccountSubscriber.js +0 -62
  61. package/src/clearingHouseUserConfig.js +0 -2
  62. package/src/index.js +0 -69
  63. package/src/mockUSDCFaucet.js +0 -280
@@ -45,6 +45,7 @@ const retryTxSender_1 = require("./tx/retryTxSender");
45
45
  const clearingHouseUser_1 = require("./clearingHouseUser");
46
46
  const config_1 = require("./config");
47
47
  const banks_1 = require("./constants/banks");
48
+ const clearingHouseUserStats_1 = require("./clearingHouseUserStats");
48
49
  /**
49
50
  * # ClearingHouse
50
51
  * This class is the main way to interact with Drift Protocol. It allows you to subscribe to the various accounts where the Market's state is stored, as well as: opening positions, liquidating, settling funding, depositing & withdrawing, and more.
@@ -72,6 +73,13 @@ class ClearingHouse {
72
73
  type: 'websocket',
73
74
  };
74
75
  this.createUsers(userIds, this.userAccountSubscriptionConfig);
76
+ if (config.userStats) {
77
+ this.userStats = new clearingHouseUserStats_1.ClearingHouseUserStats({
78
+ clearingHouse: this,
79
+ userStatsAccountPublicKey: (0, pda_1.getUserStatsAccountPublicKey)(this.program.programId, this.wallet.publicKey),
80
+ accountSubscription: this.userAccountSubscriptionConfig,
81
+ });
82
+ }
75
83
  let marketIndexes = config.marketIndexes;
76
84
  let bankIndexes = config.bankIndexes;
77
85
  let oracleInfos = config.oracleInfos;
@@ -112,6 +120,9 @@ class ClearingHouse {
112
120
  }
113
121
  async subscribe() {
114
122
  const subscribePromises = this.subscribeUsers().concat(this.accountSubscriber.subscribe());
123
+ if (this.userStats !== undefined) {
124
+ subscribePromises.concat(this.userStats.subscribe());
125
+ }
115
126
  this.isSubscribed = (await Promise.all(subscribePromises)).reduce((success, prevSuccess) => success && prevSuccess);
116
127
  return this.isSubscribed;
117
128
  }
@@ -128,6 +139,9 @@ class ClearingHouse {
128
139
  }
129
140
  async unsubscribe() {
130
141
  const unsubscribePromises = this.unsubscribeUsers().concat(this.accountSubscriber.unsubscribe());
142
+ if (this.userStats !== undefined) {
143
+ unsubscribePromises.concat(this.userStats.unsubscribe());
144
+ }
131
145
  await Promise.all(unsubscribePromises);
132
146
  this.isSubscribed = false;
133
147
  }
@@ -188,6 +202,7 @@ class ClearingHouse {
188
202
  await Promise.all(this.subscribeUsers());
189
203
  }
190
204
  this.activeUserId = activeUserId;
205
+ this.userStatsAccountPublicKey = undefined;
191
206
  }
192
207
  async switchActiveUser(userId) {
193
208
  this.activeUserId = userId;
@@ -202,7 +217,12 @@ class ClearingHouse {
202
217
  }
203
218
  async initializeUserAccount(userId = 0, name = userName_1.DEFAULT_USER_NAME) {
204
219
  const [userAccountPublicKey, initializeUserAccountIx] = await this.getInitializeUserInstructions(userId, name);
205
- const tx = new web3_js_1.Transaction().add(initializeUserAccountIx);
220
+ const tx = new web3_js_1.Transaction();
221
+ if (userId === 0) {
222
+ // not the safest assumption, can explicitly check if user stats account exists if it causes problems
223
+ tx.add(await this.getInitializeUserStatsIx());
224
+ }
225
+ tx.add(initializeUserAccountIx);
206
226
  const { txSig } = await this.txSender.send(tx, [], this.opts);
207
227
  return [txSig, userAccountPublicKey];
208
228
  }
@@ -212,6 +232,7 @@ class ClearingHouse {
212
232
  const initializeUserAccountIx = await this.program.instruction.initializeUser(userId, nameBuffer, {
213
233
  accounts: {
214
234
  user: userAccountPublicKey,
235
+ userStats: this.getUserStatsAccountPublicKey(),
215
236
  authority: this.wallet.publicKey,
216
237
  payer: this.wallet.publicKey,
217
238
  rent: anchor.web3.SYSVAR_RENT_PUBKEY,
@@ -221,6 +242,18 @@ class ClearingHouse {
221
242
  });
222
243
  return [userAccountPublicKey, initializeUserAccountIx];
223
244
  }
245
+ async getInitializeUserStatsIx() {
246
+ return await this.program.instruction.initializeUserStats({
247
+ accounts: {
248
+ userStats: this.getUserStatsAccountPublicKey(),
249
+ authority: this.wallet.publicKey,
250
+ payer: this.wallet.publicKey,
251
+ rent: anchor.web3.SYSVAR_RENT_PUBKEY,
252
+ systemProgram: anchor.web3.SystemProgram.programId,
253
+ state: await this.getStatePublicKey(),
254
+ },
255
+ });
256
+ }
224
257
  getUser(userId) {
225
258
  userId = userId !== null && userId !== void 0 ? userId : this.activeUserId;
226
259
  if (!this.users.has(userId)) {
@@ -231,6 +264,16 @@ class ClearingHouse {
231
264
  getUsers() {
232
265
  return [...this.users.values()];
233
266
  }
267
+ getUserStats() {
268
+ return this.userStats;
269
+ }
270
+ getUserStatsAccountPublicKey() {
271
+ if (this.userStatsAccountPublicKey) {
272
+ return this.userStatsAccountPublicKey;
273
+ }
274
+ this.userStatsAccountPublicKey = (0, pda_1.getUserStatsAccountPublicKey)(this.program.programId, this.wallet.publicKey);
275
+ return this.userStatsAccountPublicKey;
276
+ }
234
277
  async getUserAccountPublicKey() {
235
278
  return this.getUser().userAccountPublicKey;
236
279
  }
@@ -511,6 +554,9 @@ class ClearingHouse {
511
554
  const depositCollateralIx = fromUserId != null
512
555
  ? await this.getTransferDepositIx(amount, bankIndex, fromUserId, userId)
513
556
  : await this.getDepositInstruction(amount, bankIndex, userTokenAccount, userId, false, false);
557
+ if (userId === 0) {
558
+ tx.add(await this.getInitializeUserStatsIx());
559
+ }
514
560
  tx.add(initializeUserAccountIx).add(depositCollateralIx);
515
561
  // Close the wrapped sol account at the end of the transaction
516
562
  if (createWSOLTokenAccount) {
@@ -523,11 +569,11 @@ class ClearingHouse {
523
569
  const [associateTokenPublicKey, createAssociatedAccountIx, mintToIx] = await tokenFaucet.createAssociatedTokenAccountAndMintToInstructions(this.wallet.publicKey, amount);
524
570
  const [userAccountPublicKey, initializeUserAccountIx] = await this.getInitializeUserInstructions(userId, name);
525
571
  const depositCollateralIx = await this.getDepositInstruction(amount, bankIndex, associateTokenPublicKey, userId, false, false);
526
- const tx = new web3_js_1.Transaction()
527
- .add(createAssociatedAccountIx)
528
- .add(mintToIx)
529
- .add(initializeUserAccountIx)
530
- .add(depositCollateralIx);
572
+ const tx = new web3_js_1.Transaction().add(createAssociatedAccountIx).add(mintToIx);
573
+ if (userId === 0) {
574
+ tx.add(await this.getInitializeUserStatsIx());
575
+ }
576
+ tx.add(initializeUserAccountIx).add(depositCollateralIx);
531
577
  const txSig = await this.program.provider.sendAndConfirm(tx, []);
532
578
  return [txSig, userAccountPublicKey];
533
579
  }
@@ -607,6 +653,82 @@ class ClearingHouse {
607
653
  },
608
654
  });
609
655
  }
656
+ async settleLP(settleeUserAccountPublicKey, marketIndex) {
657
+ const { txSig } = await this.txSender.send((0, utils_1.wrapInTx)(await this.settleLPIx(settleeUserAccountPublicKey, marketIndex)), [], this.opts);
658
+ return txSig;
659
+ }
660
+ async settleLPIx(settleeUserAccountPublicKey, marketIndex) {
661
+ const settleeUserAccount = (await this.program.account.user.fetch(settleeUserAccountPublicKey));
662
+ const userPositions = settleeUserAccount.positions;
663
+ const remainingAccounts = [];
664
+ let foundMarket = false;
665
+ for (const position of userPositions) {
666
+ if (!(0, position_1.positionIsAvailable)(position)) {
667
+ const marketPublicKey = await (0, pda_1.getMarketPublicKey)(this.program.programId, position.marketIndex);
668
+ remainingAccounts.push({
669
+ pubkey: marketPublicKey,
670
+ isWritable: true,
671
+ isSigner: false,
672
+ });
673
+ if (marketIndex.eq(position.marketIndex)) {
674
+ foundMarket = true;
675
+ }
676
+ }
677
+ }
678
+ if (!foundMarket) {
679
+ console.log('Warning: lp is not in the market specified -- tx will likely fail');
680
+ }
681
+ return this.program.instruction.settleLp(marketIndex, {
682
+ accounts: {
683
+ state: await this.getStatePublicKey(),
684
+ user: settleeUserAccountPublicKey,
685
+ },
686
+ remainingAccounts: remainingAccounts,
687
+ });
688
+ }
689
+ async removeLiquidity(marketIndex, sharesToBurn) {
690
+ const { txSig } = await this.txSender.send((0, utils_1.wrapInTx)(await this.getRemoveLiquidityIx(marketIndex, sharesToBurn)), [], this.opts);
691
+ return txSig;
692
+ }
693
+ async getRemoveLiquidityIx(marketIndex, sharesToBurn) {
694
+ const userAccountPublicKey = await this.getUserAccountPublicKey();
695
+ const remainingAccounts = this.getRemainingAccounts({
696
+ writableMarketIndex: marketIndex,
697
+ });
698
+ if (sharesToBurn == undefined) {
699
+ const userAccount = this.getUserAccount();
700
+ const marketPosition = userAccount.positions.filter((position) => position.marketIndex.eq(marketIndex))[0];
701
+ sharesToBurn = marketPosition.lpShares;
702
+ console.log('burning lp shares:', sharesToBurn.toString());
703
+ }
704
+ return this.program.instruction.removeLiquidity(sharesToBurn, marketIndex, {
705
+ accounts: {
706
+ state: await this.getStatePublicKey(),
707
+ user: userAccountPublicKey,
708
+ authority: this.wallet.publicKey,
709
+ },
710
+ remainingAccounts: remainingAccounts,
711
+ });
712
+ }
713
+ async addLiquidity(amount, marketIndex) {
714
+ const { txSig, slot } = await this.txSender.send((0, utils_1.wrapInTx)(await this.getAddLiquidityIx(amount, marketIndex)), [], this.opts);
715
+ this.marketLastSlotCache.set(marketIndex.toNumber(), slot);
716
+ return txSig;
717
+ }
718
+ async getAddLiquidityIx(amount, marketIndex) {
719
+ const userAccountPublicKey = await this.getUserAccountPublicKey();
720
+ const remainingAccounts = this.getRemainingAccounts({
721
+ writableMarketIndex: marketIndex,
722
+ });
723
+ return this.program.instruction.addLiquidity(amount, marketIndex, {
724
+ accounts: {
725
+ state: await this.getStatePublicKey(),
726
+ user: userAccountPublicKey,
727
+ authority: this.wallet.publicKey,
728
+ },
729
+ remainingAccounts: remainingAccounts,
730
+ });
731
+ }
610
732
  async openPosition(direction, amount, marketIndex, limitPrice) {
611
733
  return await this.placeAndTake({
612
734
  orderType: types_1.OrderType.MARKET,
@@ -634,6 +756,7 @@ class ClearingHouse {
634
756
  accounts: {
635
757
  state: await this.getStatePublicKey(),
636
758
  user: userAccountPublicKey,
759
+ userStats: this.getUserStatsAccountPublicKey(),
637
760
  authority: this.wallet.publicKey,
638
761
  },
639
762
  remainingAccounts,
@@ -713,7 +836,9 @@ class ClearingHouse {
713
836
  return txSig;
714
837
  }
715
838
  async getFillOrderIx(userAccountPublicKey, userAccount, order, makerInfo) {
839
+ const userStatsPublicKey = (0, pda_1.getUserStatsAccountPublicKey)(this.program.programId, userAccount.authority);
716
840
  const fillerPublicKey = await this.getUserAccountPublicKey();
841
+ const fillerStatsPublicKey = this.getUserStatsAccountPublicKey();
717
842
  const marketIndex = order.marketIndex;
718
843
  const marketAccount = this.getMarketAccount(marketIndex);
719
844
  const oracleAccountMap = new Map();
@@ -773,6 +898,11 @@ class ClearingHouse {
773
898
  isWritable: true,
774
899
  isSigner: false,
775
900
  });
901
+ remainingAccounts.push({
902
+ pubkey: makerInfo.makerStats,
903
+ isWritable: true,
904
+ isSigner: false,
905
+ });
776
906
  }
777
907
  const orderId = order.orderId;
778
908
  const makerOrderId = makerInfo ? makerInfo.order.orderId : null;
@@ -780,7 +910,9 @@ class ClearingHouse {
780
910
  accounts: {
781
911
  state: await this.getStatePublicKey(),
782
912
  filler: fillerPublicKey,
913
+ fillerStats: fillerStatsPublicKey,
783
914
  user: userAccountPublicKey,
915
+ userStats: userStatsPublicKey,
784
916
  authority: this.wallet.publicKey,
785
917
  },
786
918
  remainingAccounts,
@@ -863,6 +995,7 @@ class ClearingHouse {
863
995
  }
864
996
  async getPlaceAndTakeIx(orderParams, makerInfo) {
865
997
  orderParams = this.getOrderParams(orderParams);
998
+ const userStatsPublicKey = await this.getUserStatsAccountPublicKey();
866
999
  const userAccountPublicKey = await this.getUserAccountPublicKey();
867
1000
  const remainingAccounts = this.getRemainingAccounts({
868
1001
  writableMarketIndex: orderParams.marketIndex,
@@ -876,11 +1009,17 @@ class ClearingHouse {
876
1009
  isSigner: false,
877
1010
  isWritable: true,
878
1011
  });
1012
+ remainingAccounts.push({
1013
+ pubkey: makerInfo.makerStats,
1014
+ isSigner: false,
1015
+ isWritable: true,
1016
+ });
879
1017
  }
880
1018
  return await this.program.instruction.placeAndTake(orderParams, makerOrderId, {
881
1019
  accounts: {
882
1020
  state: await this.getStatePublicKey(),
883
1021
  user: userAccountPublicKey,
1022
+ userStats: userStatsPublicKey,
884
1023
  authority: this.wallet.publicKey,
885
1024
  },
886
1025
  remainingAccounts,
@@ -893,22 +1032,19 @@ class ClearingHouse {
893
1032
  }
894
1033
  async getPlaceAndMakeIx(orderParams, takerInfo) {
895
1034
  orderParams = this.getOrderParams(orderParams);
1035
+ const userStatsPublicKey = this.getUserStatsAccountPublicKey();
896
1036
  const userAccountPublicKey = await this.getUserAccountPublicKey();
897
1037
  const remainingAccounts = this.getRemainingAccounts({
898
1038
  writableMarketIndex: orderParams.marketIndex,
899
- writableBankIndex: numericConstants_1.QUOTE_ASSET_BANK_INDEX,
900
1039
  });
901
1040
  const takerOrderId = takerInfo.order.orderId;
902
- remainingAccounts.push({
903
- pubkey: takerInfo.taker,
904
- isSigner: false,
905
- isWritable: true,
906
- });
907
1041
  return await this.program.instruction.placeAndMake(orderParams, takerOrderId, {
908
1042
  accounts: {
909
1043
  state: await this.getStatePublicKey(),
910
1044
  user: userAccountPublicKey,
1045
+ userStats: userStatsPublicKey,
911
1046
  taker: takerInfo.taker,
1047
+ takerStats: takerInfo.takerStats,
912
1048
  authority: this.wallet.publicKey,
913
1049
  },
914
1050
  remainingAccounts,
@@ -919,7 +1055,7 @@ class ClearingHouse {
919
1055
  * @param marketIndex
920
1056
  * @returns
921
1057
  */
922
- async closePosition(marketIndex) {
1058
+ async closePosition(marketIndex, limitPrice) {
923
1059
  const userPosition = this.getUser().getUserPosition(marketIndex);
924
1060
  if (!userPosition) {
925
1061
  throw Error(`No position in market ${marketIndex.toString()}`);
@@ -928,8 +1064,9 @@ class ClearingHouse {
928
1064
  orderType: types_1.OrderType.MARKET,
929
1065
  marketIndex,
930
1066
  direction: (0, position_1.findDirectionToClose)(userPosition),
931
- baseAssetAmount: userPosition.baseAssetAmount,
1067
+ baseAssetAmount: userPosition.baseAssetAmount.abs(),
932
1068
  reduceOnly: true,
1069
+ price: limitPrice,
933
1070
  });
934
1071
  }
935
1072
  async settlePNLs(users, marketIndex) {
@@ -1016,7 +1153,9 @@ class ClearingHouse {
1016
1153
  return txSig;
1017
1154
  }
1018
1155
  async getLiquidatePerpIx(userAccountPublicKey, userAccount, marketIndex, maxBaseAssetAmount) {
1156
+ const userStatsPublicKey = (0, pda_1.getUserStatsAccountPublicKey)(this.program.programId, userAccount.authority);
1019
1157
  const liquidatorPublicKey = await this.getUserAccountPublicKey();
1158
+ const liquidatorStatsPublicKey = this.getUserStatsAccountPublicKey();
1020
1159
  const remainingAccounts = this.getRemainingAccountsForLiquidation({
1021
1160
  writableMarketIndex: marketIndex,
1022
1161
  userAccount,
@@ -1026,7 +1165,9 @@ class ClearingHouse {
1026
1165
  state: await this.getStatePublicKey(),
1027
1166
  authority: this.wallet.publicKey,
1028
1167
  user: userAccountPublicKey,
1168
+ userStats: userStatsPublicKey,
1029
1169
  liquidator: liquidatorPublicKey,
1170
+ liquidatorStats: liquidatorStatsPublicKey,
1030
1171
  },
1031
1172
  remainingAccounts: remainingAccounts,
1032
1173
  });
@@ -1093,6 +1234,46 @@ class ClearingHouse {
1093
1234
  remainingAccounts: remainingAccounts,
1094
1235
  });
1095
1236
  }
1237
+ async resolvePerpBankruptcy(userAccountPublicKey, userAccount, marketIndex) {
1238
+ const { txSig } = await this.txSender.send((0, utils_1.wrapInTx)(await this.getResolvePerpBankruptcyIx(userAccountPublicKey, userAccount, marketIndex)), [], this.opts);
1239
+ return txSig;
1240
+ }
1241
+ async getResolvePerpBankruptcyIx(userAccountPublicKey, userAccount, marketIndex) {
1242
+ const liquidatorPublicKey = await this.getUserAccountPublicKey();
1243
+ const remainingAccounts = this.getRemainingAccountsForLiquidation({
1244
+ writableMarketIndex: marketIndex,
1245
+ userAccount,
1246
+ });
1247
+ return await this.program.instruction.resolvePerpBankruptcy(marketIndex, {
1248
+ accounts: {
1249
+ state: await this.getStatePublicKey(),
1250
+ authority: this.wallet.publicKey,
1251
+ user: userAccountPublicKey,
1252
+ liquidator: liquidatorPublicKey,
1253
+ },
1254
+ remainingAccounts: remainingAccounts,
1255
+ });
1256
+ }
1257
+ async resolveBorrowBankruptcy(userAccountPublicKey, userAccount, bankIndex) {
1258
+ const { txSig } = await this.txSender.send((0, utils_1.wrapInTx)(await this.getResolveBorrowBankruptcyIx(userAccountPublicKey, userAccount, bankIndex)), [], this.opts);
1259
+ return txSig;
1260
+ }
1261
+ async getResolveBorrowBankruptcyIx(userAccountPublicKey, userAccount, bankIndex) {
1262
+ const liquidatorPublicKey = await this.getUserAccountPublicKey();
1263
+ const remainingAccounts = this.getRemainingAccountsForLiquidation({
1264
+ writableBankIndexes: [bankIndex],
1265
+ userAccount,
1266
+ });
1267
+ return await this.program.instruction.resolveBorrowBankruptcy(bankIndex, {
1268
+ accounts: {
1269
+ state: await this.getStatePublicKey(),
1270
+ authority: this.wallet.publicKey,
1271
+ user: userAccountPublicKey,
1272
+ liquidator: liquidatorPublicKey,
1273
+ },
1274
+ remainingAccounts: remainingAccounts,
1275
+ });
1276
+ }
1096
1277
  getRemainingAccountsForLiquidation(params) {
1097
1278
  const liquidateeUserAccount = params.userAccount;
1098
1279
  const oracleAccountMap = new Map();
@@ -18,6 +18,7 @@ export declare type ClearingHouseConfig = {
18
18
  bankIndexes?: BN[];
19
19
  oracleInfos?: OracleInfo[];
20
20
  env?: DriftEnv;
21
+ userStats?: boolean;
21
22
  };
22
23
  declare type ClearingHouseSubscriptionConfig = {
23
24
  type: 'websocket';
@@ -49,6 +49,11 @@ export declare class ClearingHouseUser {
49
49
  getOrderByUserOrderId(userOrderId: number): Order | undefined;
50
50
  getUserAccountPublicKey(): PublicKey;
51
51
  exists(): Promise<boolean>;
52
+ /**
53
+ * calculates the market position if the lp position was settled
54
+ * @returns : userPosition
55
+ */
56
+ getSettledLPPosition(marketIndex: BN): [UserPosition, BN];
52
57
  /**
53
58
  * calculates Buying Power = FC * MAX_LEVERAGE
54
59
  * @returns : Precision QUOTE_PRECISION
@@ -71,6 +71,11 @@ class ClearingHouseUser {
71
71
  openOrders: numericConstants_1.ZERO,
72
72
  openBids: numericConstants_1.ZERO,
73
73
  openAsks: numericConstants_1.ZERO,
74
+ realizedPnl: numericConstants_1.ZERO,
75
+ lpShares: numericConstants_1.ZERO,
76
+ lastFeePerLp: numericConstants_1.ZERO,
77
+ lastNetBaseAssetAmountPerLp: numericConstants_1.ZERO,
78
+ lastNetQuoteAssetAmountPerLp: numericConstants_1.ZERO,
74
79
  };
75
80
  }
76
81
  /**
@@ -94,6 +99,89 @@ class ClearingHouseUser {
94
99
  const userAccountRPCResponse = await this.clearingHouse.connection.getParsedAccountInfo(this.userAccountPublicKey);
95
100
  return userAccountRPCResponse.value !== null;
96
101
  }
102
+ /**
103
+ * calculates the market position if the lp position was settled
104
+ * @returns : userPosition
105
+ */
106
+ getSettledLPPosition(marketIndex) {
107
+ const position = this.getUserPosition(marketIndex);
108
+ const market = this.clearingHouse.getMarketAccount(position.marketIndex);
109
+ const nShares = position.lpShares;
110
+ const deltaBaa = market.amm.marketPositionPerLp.baseAssetAmount
111
+ .sub(position.lastNetBaseAssetAmountPerLp)
112
+ .mul(nShares)
113
+ .div(numericConstants_1.AMM_RESERVE_PRECISION);
114
+ const deltaQaa = market.amm.marketPositionPerLp.quoteAssetAmount
115
+ .sub(position.lastNetQuoteAssetAmountPerLp)
116
+ .mul(nShares)
117
+ .div(numericConstants_1.AMM_RESERVE_PRECISION);
118
+ function sign(v) {
119
+ const sign = { true: new _1.BN(1), false: new _1.BN(-1) }[v.gte(numericConstants_1.ZERO).toString()];
120
+ return sign;
121
+ }
122
+ const remainder = deltaBaa
123
+ .abs()
124
+ .mod(market.amm.baseAssetAmountStepSize)
125
+ .mul(sign(deltaBaa));
126
+ const _standardizedBaa = deltaBaa.sub(remainder);
127
+ let remainderBaa;
128
+ if (_standardizedBaa.abs().gte(market.amm.baseAssetAmountStepSize)) {
129
+ remainderBaa = remainder;
130
+ }
131
+ else {
132
+ remainderBaa = deltaBaa;
133
+ }
134
+ const standardizedBaa = deltaBaa.sub(remainderBaa);
135
+ const reaminderPerLP = remainderBaa.mul(numericConstants_1.AMM_RESERVE_PRECISION).div(nShares);
136
+ position.baseAssetAmount = position.baseAssetAmount.add(standardizedBaa);
137
+ position.quoteAssetAmount = position.quoteAssetAmount.add(deltaQaa);
138
+ position.lastNetBaseAssetAmountPerLp =
139
+ market.amm.marketPositionPerLp.baseAssetAmount.sub(reaminderPerLP);
140
+ let updateType;
141
+ if (position.baseAssetAmount.eq(numericConstants_1.ZERO)) {
142
+ updateType = 'open';
143
+ }
144
+ else if (sign(position.baseAssetAmount).eq(sign(deltaBaa))) {
145
+ updateType = 'increase';
146
+ }
147
+ else if (position.baseAssetAmount.abs().gt(deltaBaa.abs())) {
148
+ updateType = 'reduce';
149
+ }
150
+ else if (position.baseAssetAmount.abs().eq(deltaBaa.abs())) {
151
+ updateType = 'close';
152
+ }
153
+ else {
154
+ updateType = 'flip';
155
+ }
156
+ let newQuoteEntry;
157
+ let pnl;
158
+ if (updateType == 'open' || updateType == 'increase') {
159
+ newQuoteEntry = position.quoteEntryAmount.add(deltaQaa);
160
+ pnl = 0;
161
+ }
162
+ else if (updateType == 'reduce' || updateType == 'close') {
163
+ newQuoteEntry = position.quoteEntryAmount.sub(position.quoteEntryAmount
164
+ .mul(deltaBaa.abs())
165
+ .div(position.baseAssetAmount.abs()));
166
+ pnl = position.quoteEntryAmount.sub(newQuoteEntry);
167
+ }
168
+ else {
169
+ newQuoteEntry = deltaQaa.sub(deltaQaa.mul(position.baseAssetAmount.abs()).div(deltaBaa.abs()));
170
+ pnl = position.quoteEntryAmount.add(deltaQaa.sub(newQuoteEntry));
171
+ }
172
+ position.quoteEntryAmount = newQuoteEntry;
173
+ if (position.baseAssetAmount.gt(numericConstants_1.ZERO)) {
174
+ position.lastCumulativeFundingRate = market.amm.cumulativeFundingRateLong;
175
+ }
176
+ else if (position.baseAssetAmount.lt(numericConstants_1.ZERO)) {
177
+ position.lastCumulativeFundingRate =
178
+ market.amm.cumulativeFundingRateShort;
179
+ }
180
+ else {
181
+ position.lastCumulativeFundingRate = numericConstants_1.ZERO;
182
+ }
183
+ return [position, pnl];
184
+ }
97
185
  /**
98
186
  * calculates Buying Power = FC * MAX_LEVERAGE
99
187
  * @returns : Precision QUOTE_PRECISION
@@ -411,12 +499,17 @@ class ClearingHouseUser {
411
499
  const proposedMarketPosition = {
412
500
  marketIndex: marketPosition.marketIndex,
413
501
  baseAssetAmount: proposedBaseAssetAmount,
414
- lastCumulativeFundingRate: currentMarketPosition.lastCumulativeFundingRate,
415
502
  quoteAssetAmount: new _1.BN(0),
503
+ lastCumulativeFundingRate: numericConstants_1.ZERO,
416
504
  quoteEntryAmount: new _1.BN(0),
417
505
  openOrders: new _1.BN(0),
418
506
  openBids: new _1.BN(0),
419
507
  openAsks: new _1.BN(0),
508
+ realizedPnl: numericConstants_1.ZERO,
509
+ lpShares: numericConstants_1.ZERO,
510
+ lastFeePerLp: numericConstants_1.ZERO,
511
+ lastNetBaseAssetAmountPerLp: numericConstants_1.ZERO,
512
+ lastNetQuoteAssetAmountPerLp: numericConstants_1.ZERO,
420
513
  };
421
514
  if (proposedBaseAssetAmount.eq(numericConstants_1.ZERO))
422
515
  return new _1.BN(-1);
@@ -428,8 +521,9 @@ class ClearingHouseUser {
428
521
  if (!position.marketIndex.eq(marketPosition.marketIndex)) {
429
522
  const market = this.clearingHouse.getMarketAccount(position.marketIndex);
430
523
  const positionValue = (0, margin_1.calculateMarginBaseAssetValue)(market, position, this.getOracleDataForMarket(market.marketIndex));
431
- const marketMarginRequirement = positionValue;
432
- new _1.BN((0, _1.calculateMarketMarginRatio)(market, position.baseAssetAmount.abs(), 'Maintenance')).div(numericConstants_1.MARGIN_PRECISION);
524
+ const marketMarginRequirement = positionValue
525
+ .mul(new _1.BN((0, _1.calculateMarketMarginRatio)(market, position.baseAssetAmount.abs(), 'Maintenance')))
526
+ .div(numericConstants_1.MARGIN_PRECISION);
433
527
  totalMarginRequirement = totalMarginRequirement.add(marketMarginRequirement);
434
528
  }
435
529
  return totalMarginRequirement;
@@ -0,0 +1,17 @@
1
+ import { ClearingHouse } from './clearingHouse';
2
+ import { PublicKey } from '@solana/web3.js';
3
+ import { DataAndSlot, UserStatsAccountSubscriber } from './accounts/types';
4
+ import { ClearingHouseUserStatsConfig } from './clearingHouseUserStatsConfig';
5
+ import { UserStatsAccount } from './types';
6
+ export declare class ClearingHouseUserStats {
7
+ clearingHouse: ClearingHouse;
8
+ userStatsAccountPublicKey: PublicKey;
9
+ accountSubscriber: UserStatsAccountSubscriber;
10
+ isSubscribed: boolean;
11
+ constructor(config: ClearingHouseUserStatsConfig);
12
+ subscribe(): Promise<boolean>;
13
+ fetchAccounts(): Promise<void>;
14
+ unsubscribe(): Promise<void>;
15
+ getAccountAndSlot(): DataAndSlot<UserStatsAccount>;
16
+ getAccount(): UserStatsAccount;
17
+ }
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ClearingHouseUserStats = void 0;
4
+ const pollingUserStatsAccountSubscriber_1 = require("./accounts/pollingUserStatsAccountSubscriber");
5
+ const webSocketUserStatsAccountSubsriber_1 = require("./accounts/webSocketUserStatsAccountSubsriber");
6
+ class ClearingHouseUserStats {
7
+ constructor(config) {
8
+ var _a;
9
+ this.clearingHouse = config.clearingHouse;
10
+ this.userStatsAccountPublicKey = config.userStatsAccountPublicKey;
11
+ if (((_a = config.accountSubscription) === null || _a === void 0 ? void 0 : _a.type) === 'polling') {
12
+ this.accountSubscriber = new pollingUserStatsAccountSubscriber_1.PollingUserStatsAccountSubscriber(config.clearingHouse.program, config.userStatsAccountPublicKey, config.accountSubscription.accountLoader);
13
+ }
14
+ else {
15
+ this.accountSubscriber = new webSocketUserStatsAccountSubsriber_1.WebSocketUserStatsAccountSubscriber(config.clearingHouse.program, config.userStatsAccountPublicKey);
16
+ }
17
+ }
18
+ async subscribe() {
19
+ this.isSubscribed = await this.accountSubscriber.subscribe();
20
+ return this.isSubscribed;
21
+ }
22
+ async fetchAccounts() {
23
+ await this.accountSubscriber.fetch();
24
+ }
25
+ async unsubscribe() {
26
+ await this.accountSubscriber.unsubscribe();
27
+ this.isSubscribed = false;
28
+ }
29
+ getAccountAndSlot() {
30
+ return this.accountSubscriber.getUserStatsAccountAndSlot();
31
+ }
32
+ getAccount() {
33
+ return this.accountSubscriber.getUserStatsAccountAndSlot().data;
34
+ }
35
+ }
36
+ exports.ClearingHouseUserStats = ClearingHouseUserStats;
@@ -0,0 +1,14 @@
1
+ import { ClearingHouse } from './clearingHouse';
2
+ import { PublicKey } from '@solana/web3.js';
3
+ import { BulkAccountLoader } from './accounts/bulkAccountLoader';
4
+ export declare type ClearingHouseUserStatsConfig = {
5
+ accountSubscription?: ClearingHouseUserStatsAccountSubscriptionConfig;
6
+ clearingHouse: ClearingHouse;
7
+ userStatsAccountPublicKey: PublicKey;
8
+ };
9
+ export declare type ClearingHouseUserStatsAccountSubscriptionConfig = {
10
+ type: 'websocket';
11
+ } | {
12
+ type: 'polling';
13
+ accountLoader: BulkAccountLoader;
14
+ };
package/lib/config.js CHANGED
@@ -7,7 +7,7 @@ exports.configs = {
7
7
  devnet: {
8
8
  ENV: 'devnet',
9
9
  PYTH_ORACLE_MAPPING_ADDRESS: 'BmA9Z6FjioHJPpjT39QazZyhDRUdZy2ezwx4GiDdE2u2',
10
- CLEARING_HOUSE_PROGRAM_ID: '7HDuhZ94TVTWpH3vba3dJhGWyHvQuy2zBjniRxE7PU88',
10
+ CLEARING_HOUSE_PROGRAM_ID: 'D9bW92Maa1yDigJqvabRgr5S5VybPNDB5xxSpQD6mkkV',
11
11
  USDC_MINT_ADDRESS: '8zGuJQqwhZafTah7Uc7Z4tXRnguqkn5KLFAP8oV6PHe2',
12
12
  MARKETS: markets_1.DevnetMarkets,
13
13
  BANKS: banks_1.DevnetBanks,