@drift-labs/sdk 0.2.0-master.11 → 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 (114) 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 +24 -3
  13. package/lib/clearingHouse.js +352 -51
  14. package/lib/clearingHouseConfig.d.ts +1 -0
  15. package/lib/clearingHouseUser.d.ts +15 -15
  16. package/lib/clearingHouseUser.js +185 -73
  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/lib/clearingHouseUserStatsConfig.js +2 -0
  21. package/lib/config.js +1 -1
  22. package/lib/constants/banks.d.ts +2 -2
  23. package/lib/constants/banks.js +4 -3
  24. package/lib/constants/numericConstants.d.ts +2 -0
  25. package/lib/constants/numericConstants.js +3 -1
  26. package/lib/events/eventList.js +3 -0
  27. package/lib/events/types.d.ts +2 -1
  28. package/lib/factory/bigNum.d.ts +1 -0
  29. package/lib/factory/bigNum.js +37 -11
  30. package/lib/idl/clearing_house.json +692 -58
  31. package/lib/index.d.ts +1 -0
  32. package/lib/index.js +1 -0
  33. package/lib/math/amm.js +2 -2
  34. package/lib/math/bankBalance.d.ts +7 -1
  35. package/lib/math/bankBalance.js +76 -1
  36. package/lib/math/margin.d.ts +11 -0
  37. package/lib/math/margin.js +72 -0
  38. package/lib/math/market.d.ts +4 -1
  39. package/lib/math/market.js +35 -1
  40. package/lib/math/oracles.d.ts +3 -0
  41. package/lib/math/oracles.js +25 -5
  42. package/lib/math/position.d.ts +8 -0
  43. package/lib/math/position.js +43 -12
  44. package/lib/math/trade.js +2 -2
  45. package/lib/orders.d.ts +1 -2
  46. package/lib/orders.js +2 -77
  47. package/lib/tokenFaucet.d.ts +1 -0
  48. package/lib/tokenFaucet.js +23 -12
  49. package/lib/tx/retryTxSender.js +9 -2
  50. package/lib/types.d.ts +78 -10
  51. package/lib/types.js +12 -0
  52. package/lib/util/getTokenAddress.d.ts +2 -0
  53. package/lib/util/getTokenAddress.js +9 -0
  54. package/package.json +1 -1
  55. package/src/accounts/fetch.ts +27 -2
  56. package/src/accounts/pollingUserStatsAccountSubscriber.ts +172 -0
  57. package/src/accounts/types.ts +18 -0
  58. package/src/accounts/webSocketUserStatsAccountSubsriber.ts +80 -0
  59. package/src/addresses/pda.ts +13 -0
  60. package/src/admin.ts +29 -1
  61. package/src/clearingHouse.ts +619 -62
  62. package/src/clearingHouseConfig.ts +1 -0
  63. package/src/clearingHouseUser.ts +317 -105
  64. package/src/clearingHouseUserStats.ts +53 -0
  65. package/src/clearingHouseUserStatsConfig.ts +18 -0
  66. package/src/config.ts +1 -1
  67. package/src/constants/banks.js +42 -0
  68. package/src/constants/banks.ts +6 -3
  69. package/src/constants/markets.js +42 -0
  70. package/src/constants/numericConstants.js +41 -0
  71. package/src/constants/numericConstants.ts +3 -0
  72. package/src/events/eventList.ts +3 -0
  73. package/src/events/types.ts +2 -0
  74. package/src/factory/bigNum.js +37 -11
  75. package/src/factory/bigNum.ts +43 -13
  76. package/src/idl/clearing_house.json +692 -58
  77. package/src/index.ts +1 -0
  78. package/src/math/amm.ts +8 -5
  79. package/src/math/bankBalance.ts +147 -1
  80. package/src/math/margin.ts +124 -0
  81. package/src/math/market.ts +66 -1
  82. package/src/math/oracles.ts +42 -5
  83. package/src/math/position.ts +60 -9
  84. package/src/math/trade.ts +2 -2
  85. package/src/orders.ts +4 -157
  86. package/src/tokenFaucet.js +189 -0
  87. package/src/tokenFaucet.ts +38 -15
  88. package/src/tx/retryTxSender.ts +11 -3
  89. package/src/types.js +12 -1
  90. package/src/types.ts +83 -10
  91. package/src/{accounts/fetch.js → util/computeUnits.js} +11 -13
  92. package/src/util/getTokenAddress.js +9 -0
  93. package/src/util/getTokenAddress.ts +18 -0
  94. package/tests/bn/test.ts +2 -0
  95. package/src/accounts/bulkAccountLoader.js +0 -197
  96. package/src/accounts/bulkUserSubscription.js +0 -33
  97. package/src/accounts/pollingClearingHouseAccountSubscriber.js +0 -311
  98. package/src/accounts/pollingOracleSubscriber.js +0 -93
  99. package/src/accounts/pollingTokenAccountSubscriber.js +0 -90
  100. package/src/accounts/pollingUserAccountSubscriber.js +0 -132
  101. package/src/accounts/types.js +0 -10
  102. package/src/accounts/utils.js +0 -7
  103. package/src/accounts/webSocketAccountSubscriber.js +0 -93
  104. package/src/accounts/webSocketClearingHouseAccountSubscriber.js +0 -233
  105. package/src/accounts/webSocketUserAccountSubscriber.js +0 -62
  106. package/src/addresses/pda.js +0 -104
  107. package/src/index.js +0 -100
  108. package/src/math/bankBalance.js +0 -75
  109. package/src/math/market.js +0 -57
  110. package/src/math/orders.js +0 -110
  111. package/src/math/position.js +0 -140
  112. package/src/mockUSDCFaucet.js +0 -280
  113. package/src/orders.js +0 -134
  114. package/src/tx/retryTxSender.js +0 -188
@@ -73,8 +73,8 @@ class TokenFaucet {
73
73
  async fetchState() {
74
74
  return await this.program.account.faucetConfig.fetch(await this.getFaucetConfigPublicKey());
75
75
  }
76
- async mintToUser(userTokenAccount, amount) {
77
- return await this.program.rpc.mintToUser(amount, {
76
+ async mintToUserIx(userTokenAccount, amount) {
77
+ return this.program.instruction.mintToUser(amount, {
78
78
  accounts: {
79
79
  faucetConfig: await this.getFaucetConfigPublicKey(),
80
80
  mintAccount: this.mint,
@@ -84,6 +84,12 @@ class TokenFaucet {
84
84
  },
85
85
  });
86
86
  }
87
+ async mintToUser(userTokenAccount, amount) {
88
+ const mintIx = await this.mintToUserIx(userTokenAccount, amount);
89
+ const tx = new web3_js_1.Transaction().add(mintIx);
90
+ const txSig = await this.program.provider.sendAndConfirm(tx, [], this.opts);
91
+ return txSig;
92
+ }
87
93
  async transferMintAuthority() {
88
94
  return await this.program.rpc.transferMintAuthority({
89
95
  accounts: {
@@ -96,8 +102,21 @@ class TokenFaucet {
96
102
  });
97
103
  }
98
104
  async createAssociatedTokenAccountAndMintTo(userPublicKey, amount) {
105
+ const tx = new web3_js_1.Transaction();
99
106
  const [associatedTokenPublicKey, createAssociatedAccountIx, mintToTx] = await this.createAssociatedTokenAccountAndMintToInstructions(userPublicKey, amount);
100
- const tx = new web3_js_1.Transaction().add(createAssociatedAccountIx).add(mintToTx);
107
+ let associatedTokenAccountExists = false;
108
+ try {
109
+ const assosciatedTokenAccount = await this.connection.getAccountInfo(associatedTokenPublicKey);
110
+ associatedTokenAccountExists = !!assosciatedTokenAccount;
111
+ }
112
+ catch (e) {
113
+ // token account doesn't exist
114
+ associatedTokenAccountExists = false;
115
+ }
116
+ const skipAccountCreation = associatedTokenAccountExists;
117
+ if (!skipAccountCreation)
118
+ tx.add(createAssociatedAccountIx);
119
+ tx.add(mintToTx);
101
120
  const txSig = await this.program.provider.sendAndConfirm(tx, [], this.opts);
102
121
  return [associatedTokenPublicKey, txSig];
103
122
  }
@@ -105,15 +124,7 @@ class TokenFaucet {
105
124
  const state = await this.fetchState();
106
125
  const associateTokenPublicKey = await this.getAssosciatedMockUSDMintAddress({ userPubKey: userPublicKey });
107
126
  const createAssociatedAccountIx = spl_token_1.Token.createAssociatedTokenAccountInstruction(spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID, spl_token_1.TOKEN_PROGRAM_ID, state.mint, associateTokenPublicKey, userPublicKey, this.wallet.publicKey);
108
- const mintToIx = await this.program.instruction.mintToUser(amount, {
109
- accounts: {
110
- faucetConfig: await this.getFaucetConfigPublicKey(),
111
- mintAccount: state.mint,
112
- userTokenAccount: associateTokenPublicKey,
113
- mintAuthority: state.mintAuthority,
114
- tokenProgram: spl_token_1.TOKEN_PROGRAM_ID,
115
- },
116
- });
127
+ const mintToIx = await this.mintToUserIx(associateTokenPublicKey, amount);
117
128
  return [associateTokenPublicKey, createAssociatedAccountIx, mintToIx];
118
129
  }
119
130
  async getAssosciatedMockUSDMintAddress(props) {
@@ -25,8 +25,15 @@ class RetryTxSender {
25
25
  await this.prepareTx(tx, additionalSigners, opts);
26
26
  const rawTransaction = tx.serialize();
27
27
  const startTime = this.getTimestamp();
28
- const txid = await this.provider.connection.sendRawTransaction(rawTransaction, opts);
29
- this.sendToAdditionalConnections(rawTransaction, opts);
28
+ let txid;
29
+ try {
30
+ txid = await this.provider.connection.sendRawTransaction(rawTransaction, opts);
31
+ this.sendToAdditionalConnections(rawTransaction, opts);
32
+ }
33
+ catch (e) {
34
+ console.error(e);
35
+ throw e;
36
+ }
30
37
  let done = false;
31
38
  const resolveReference = {
32
39
  resolve: undefined,
package/lib/types.d.ts CHANGED
@@ -113,6 +113,12 @@ export declare class OrderActionExplanation {
113
113
  static readonly MARKET_ORDER_FILLED_TO_LIMIT_PRICE: {
114
114
  marketOrderFilledToLimitPrice: {};
115
115
  };
116
+ static readonly CANCELED_FOR_LIQUIDATION: {
117
+ canceledForLiquidation: {};
118
+ };
119
+ static readonly MARKET_ORDER_AUCTION_EXPIRED: {
120
+ marketOrderAuctionExpired: {};
121
+ };
116
122
  }
117
123
  export declare class OrderTriggerCondition {
118
124
  static readonly ABOVE: {
@@ -192,10 +198,13 @@ export declare type LiquidationRecord = {
192
198
  liquidationType: LiquidationType;
193
199
  marginRequirement: BN;
194
200
  totalCollateral: BN;
201
+ liquidationId: number;
195
202
  liquidatePerp: LiquidatePerpRecord;
196
203
  liquidateBorrow: LiquidateBorrowRecord;
197
204
  liquidateBorrowForPerpPnl: LiquidateBorrowForPerpPnlRecord;
198
205
  liquidatePerpPnlForDeposit: LiquidatePerpPnlForDepositRecord;
206
+ perpBankruptcy: PerpBankruptcyRecord;
207
+ borrowBankruptcy: BorrowBankruptcyRecord;
199
208
  };
200
209
  export declare class LiquidationType {
201
210
  static readonly LIQUIDATE_PERP: {
@@ -210,6 +219,12 @@ export declare class LiquidationType {
210
219
  static readonly LIQUIDATE_PERP_PNL_FOR_DEPOSIT: {
211
220
  liquidatePerpPnlForDeposit: {};
212
221
  };
222
+ static readonly PERP_BANKRUPTCY: {
223
+ perpBankruptcy: {};
224
+ };
225
+ static readonly BORROW_BANKRUPTCY: {
226
+ borrowBankruptcy: {};
227
+ };
213
228
  }
214
229
  export declare type LiquidatePerpRecord = {
215
230
  marketIndex: BN;
@@ -248,14 +263,34 @@ export declare type LiquidatePerpPnlForDepositRecord = {
248
263
  assetPrice: BN;
249
264
  assetTransfer: BN;
250
265
  };
266
+ export declare type PerpBankruptcyRecord = {
267
+ marketIndex: BN;
268
+ pnl: BN;
269
+ cumulativeFundingRateDelta: BN;
270
+ };
271
+ export declare type BorrowBankruptcyRecord = {
272
+ bankIndex: BN;
273
+ borrowAmount: BN;
274
+ cumulativeDepositInterestDelta: BN;
275
+ };
276
+ export declare type SettlePnlRecord = {
277
+ ts: BN;
278
+ user: PublicKey;
279
+ marketIndex: BN;
280
+ pnl: BN;
281
+ baseAssetAmount: BN;
282
+ quoteAssetAmountAfter: BN;
283
+ quoteEntryamount: BN;
284
+ settlePrice: BN;
285
+ };
251
286
  export declare type OrderRecord = {
252
287
  ts: BN;
253
288
  taker: PublicKey;
254
289
  maker: PublicKey;
255
290
  takerOrder: Order;
256
291
  makerOrder: Order;
257
- takerUnsettledPnl: BN;
258
- makerUnsettledPnl: BN;
292
+ takerPnl: BN;
293
+ makerPnl: BN;
259
294
  action: OrderAction;
260
295
  actionExplanation: OrderActionExplanation;
261
296
  filler: PublicKey;
@@ -313,6 +348,10 @@ export declare type MarketAccount = {
313
348
  nextFillRecordId: BN;
314
349
  pnlPool: PoolBalance;
315
350
  liquidationFee: BN;
351
+ imfFactor: BN;
352
+ unsettledImfFactor: BN;
353
+ unsettledInitialAssetWeight: number;
354
+ unsettledMaintenanceAssetWeight: number;
316
355
  };
317
356
  export declare type BankAccount = {
318
357
  bankIndex: BN;
@@ -336,6 +375,11 @@ export declare type BankAccount = {
336
375
  initialLiabilityWeight: BN;
337
376
  maintenanceLiabilityWeight: BN;
338
377
  liquidationFee: BN;
378
+ imfFactor: BN;
379
+ withdrawGuardThreshold: BN;
380
+ depositTokenTwap: BN;
381
+ borrowTokenTwap: BN;
382
+ utilizationTwap: BN;
339
383
  };
340
384
  export declare type PoolBalance = {
341
385
  balance: BN;
@@ -347,8 +391,10 @@ export declare type AMM = {
347
391
  lastFundingRate: BN;
348
392
  lastFundingRateTs: BN;
349
393
  lastMarkPriceTwap: BN;
394
+ lastMarkPriceTwap5min: BN;
350
395
  lastMarkPriceTwapTs: BN;
351
396
  lastOraclePriceTwap: BN;
397
+ lastOraclePriceTwap5min: BN;
352
398
  lastOraclePriceTwapTs: BN;
353
399
  lastOracleMarkSpreadPct: BN;
354
400
  lastOracleConfPct: BN;
@@ -359,11 +405,16 @@ export declare type AMM = {
359
405
  pegMultiplier: BN;
360
406
  cumulativeFundingRateLong: BN;
361
407
  cumulativeFundingRateShort: BN;
408
+ cumulativeFundingRateLp: BN;
362
409
  cumulativeRepegRebateLong: BN;
363
410
  cumulativeRepegRebateShort: BN;
364
411
  totalFeeMinusDistributions: BN;
365
412
  totalFeeWithdrawn: BN;
366
413
  totalFee: BN;
414
+ cumulativeFundingPaymentPerLp: BN;
415
+ cumulativeFeePerLp: BN;
416
+ cumulativeNetBaseAssetAmountPerLp: BN;
417
+ userLpShares: BN;
367
418
  minimumQuoteAssetTradeSize: BN;
368
419
  baseAssetAmountStepSize: BN;
369
420
  maxBaseAssetAmountRatio: number;
@@ -385,6 +436,8 @@ export declare type AMM = {
385
436
  longSpread: BN;
386
437
  shortSpread: BN;
387
438
  maxSpread: number;
439
+ marketPosition: UserPosition;
440
+ marketPositionPerLp: UserPosition;
388
441
  };
389
442
  export declare type UserPosition = {
390
443
  baseAssetAmount: BN;
@@ -393,17 +446,22 @@ export declare type UserPosition = {
393
446
  quoteAssetAmount: BN;
394
447
  quoteEntryAmount: BN;
395
448
  openOrders: BN;
396
- unsettledPnl: BN;
397
449
  openBids: BN;
398
450
  openAsks: BN;
451
+ realizedPnl: BN;
452
+ lpShares: BN;
453
+ lastFeePerLp: BN;
454
+ lastNetBaseAssetAmountPerLp: BN;
455
+ lastNetQuoteAssetAmountPerLp: BN;
399
456
  };
400
- export declare type UserAccount = {
401
- authority: PublicKey;
402
- name: number[];
403
- userId: number;
404
- bankBalances: UserBankBalance[];
405
- collateral: BN;
406
- cumulativeDeposits: BN;
457
+ export declare type UserStatsAccount = {
458
+ numberOfUsers: number;
459
+ makerVolume30D: BN;
460
+ takerVolume30D: BN;
461
+ fillerVolume30D: BN;
462
+ lastMakerVolume30DTs: BN;
463
+ lastTakerVolume30DTs: BN;
464
+ lastFillerVolume30DTs: BN;
407
465
  fees: {
408
466
  totalFeePaid: BN;
409
467
  totalFeeRebate: BN;
@@ -411,9 +469,17 @@ export declare type UserAccount = {
411
469
  totalReferralReward: BN;
412
470
  totalRefereeDiscount: BN;
413
471
  };
472
+ };
473
+ export declare type UserAccount = {
474
+ authority: PublicKey;
475
+ name: number[];
476
+ userId: number;
477
+ bankBalances: UserBankBalance[];
414
478
  positions: UserPosition[];
415
479
  orders: Order[];
416
480
  beingLiquidated: boolean;
481
+ bankrupt: boolean;
482
+ nextLiquidationId: number;
417
483
  };
418
484
  export declare type UserBankBalance = {
419
485
  bankIndex: BN;
@@ -508,10 +574,12 @@ export declare const DefaultOrderParams: {
508
574
  };
509
575
  export declare type MakerInfo = {
510
576
  maker: PublicKey;
577
+ makerStats: PublicKey;
511
578
  order: Order;
512
579
  };
513
580
  export declare type TakerInfo = {
514
581
  taker: PublicKey;
582
+ takerStats: PublicKey;
515
583
  order: Order;
516
584
  };
517
585
  export interface IWallet {
package/lib/types.js CHANGED
@@ -70,6 +70,12 @@ OrderActionExplanation.ORACLE_PRICE_BREACHED_LIMIT_PRICE = {
70
70
  OrderActionExplanation.MARKET_ORDER_FILLED_TO_LIMIT_PRICE = {
71
71
  marketOrderFilledToLimitPrice: {},
72
72
  };
73
+ OrderActionExplanation.CANCELED_FOR_LIQUIDATION = {
74
+ canceledForLiquidation: {},
75
+ };
76
+ OrderActionExplanation.MARKET_ORDER_AUCTION_EXPIRED = {
77
+ marketOrderAuctionExpired: {},
78
+ };
73
79
  class OrderTriggerCondition {
74
80
  }
75
81
  exports.OrderTriggerCondition = OrderTriggerCondition;
@@ -102,6 +108,12 @@ LiquidationType.LIQUIDATE_BORROW_FOR_PERP_PNL = {
102
108
  LiquidationType.LIQUIDATE_PERP_PNL_FOR_DEPOSIT = {
103
109
  liquidatePerpPnlForDeposit: {},
104
110
  };
111
+ LiquidationType.PERP_BANKRUPTCY = {
112
+ perpBankruptcy: {},
113
+ };
114
+ LiquidationType.BORROW_BANKRUPTCY = {
115
+ borrowBankruptcy: {},
116
+ };
105
117
  exports.DefaultOrderParams = {
106
118
  orderType: OrderType.MARKET,
107
119
  userOrderId: 0,
@@ -0,0 +1,2 @@
1
+ import { PublicKey } from '@solana/web3.js';
2
+ export declare const getTokenAddress: (mintAddress: string, userPubKey: string) => Promise<PublicKey>;
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getTokenAddress = void 0;
4
+ const spl_token_1 = require("@solana/spl-token");
5
+ const web3_js_1 = require("@solana/web3.js");
6
+ const getTokenAddress = (mintAddress, userPubKey) => {
7
+ return spl_token_1.Token.getAssociatedTokenAddress(spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID, spl_token_1.TOKEN_PROGRAM_ID, new web3_js_1.PublicKey(mintAddress), new web3_js_1.PublicKey(userPubKey));
8
+ };
9
+ exports.getTokenAddress = getTokenAddress;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drift-labs/sdk",
3
- "version": "0.2.0-master.11",
3
+ "version": "0.2.0-master.14",
4
4
  "main": "lib/index.js",
5
5
  "types": "lib/index.d.ts",
6
6
  "author": "crispheaney",
@@ -1,6 +1,9 @@
1
1
  import { Connection, PublicKey } from '@solana/web3.js';
2
- import { UserAccount } from '../types';
3
- import { getUserAccountPublicKey } from '../addresses/pda';
2
+ import { UserAccount, UserStatsAccount } from '../types';
3
+ import {
4
+ getUserAccountPublicKey,
5
+ getUserStatsAccountPublicKey,
6
+ } from '../addresses/pda';
4
7
  import { Program } from '@project-serum/anchor';
5
8
 
6
9
  export async function fetchUserAccounts(
@@ -31,3 +34,25 @@ export async function fetchUserAccounts(
31
34
  ) as UserAccount;
32
35
  });
33
36
  }
37
+
38
+ export async function fetchUserStatsAccount(
39
+ connection: Connection,
40
+ program: Program,
41
+ authority: PublicKey
42
+ ): Promise<UserStatsAccount | undefined> {
43
+ const userStatsPublicKey = getUserStatsAccountPublicKey(
44
+ program.programId,
45
+ authority
46
+ );
47
+ const accountInfo = await connection.getAccountInfo(
48
+ userStatsPublicKey,
49
+ 'confirmed'
50
+ );
51
+
52
+ return accountInfo
53
+ ? (program.account.user.coder.accounts.decode(
54
+ 'UserStats',
55
+ accountInfo.data
56
+ ) as UserStatsAccount)
57
+ : undefined;
58
+ }
@@ -0,0 +1,172 @@
1
+ import {
2
+ DataAndSlot,
3
+ AccountToPoll,
4
+ NotSubscribedError,
5
+ UserStatsAccountSubscriber,
6
+ UserStatsAccountEvents,
7
+ } from './types';
8
+ import { Program } from '@project-serum/anchor';
9
+ import StrictEventEmitter from 'strict-event-emitter-types';
10
+ import { EventEmitter } from 'events';
11
+ import { PublicKey } from '@solana/web3.js';
12
+ import { UserStatsAccount } from '../types';
13
+ import { BulkAccountLoader } from './bulkAccountLoader';
14
+ import { capitalize } from './utils';
15
+
16
+ export class PollingUserStatsAccountSubscriber
17
+ implements UserStatsAccountSubscriber
18
+ {
19
+ isSubscribed: boolean;
20
+ program: Program;
21
+ eventEmitter: StrictEventEmitter<EventEmitter, UserStatsAccountEvents>;
22
+ userStatsAccountPublicKey: PublicKey;
23
+
24
+ accountLoader: BulkAccountLoader;
25
+ accountsToPoll = new Map<string, AccountToPoll>();
26
+ errorCallbackId?: string;
27
+
28
+ userStats?: DataAndSlot<UserStatsAccount>;
29
+
30
+ public constructor(
31
+ program: Program,
32
+ userStatsAccountPublicKey: PublicKey,
33
+ accountLoader: BulkAccountLoader
34
+ ) {
35
+ this.isSubscribed = false;
36
+ this.program = program;
37
+ this.accountLoader = accountLoader;
38
+ this.eventEmitter = new EventEmitter();
39
+ this.userStatsAccountPublicKey = userStatsAccountPublicKey;
40
+ }
41
+
42
+ async subscribe(): Promise<boolean> {
43
+ if (this.isSubscribed) {
44
+ return true;
45
+ }
46
+
47
+ await this.addToAccountLoader();
48
+
49
+ let subscriptionSucceeded = false;
50
+ let retries = 0;
51
+ while (!subscriptionSucceeded && retries < 5) {
52
+ await this.fetchIfUnloaded();
53
+ subscriptionSucceeded = this.didSubscriptionSucceed();
54
+ retries++;
55
+ }
56
+
57
+ if (subscriptionSucceeded) {
58
+ this.eventEmitter.emit('update');
59
+ }
60
+
61
+ this.isSubscribed = subscriptionSucceeded;
62
+ return subscriptionSucceeded;
63
+ }
64
+
65
+ async addToAccountLoader(): Promise<void> {
66
+ if (this.accountsToPoll.size > 0) {
67
+ return;
68
+ }
69
+
70
+ this.accountsToPoll.set(this.userStatsAccountPublicKey.toString(), {
71
+ key: 'userStats',
72
+ publicKey: this.userStatsAccountPublicKey,
73
+ eventType: 'userStatsAccountUpdate',
74
+ });
75
+
76
+ for (const [_, accountToPoll] of this.accountsToPoll) {
77
+ accountToPoll.callbackId = this.accountLoader.addAccount(
78
+ accountToPoll.publicKey,
79
+ (buffer, slot) => {
80
+ if (!buffer) {
81
+ return;
82
+ }
83
+
84
+ const account = this.program.account[
85
+ accountToPoll.key
86
+ ].coder.accounts.decode(capitalize(accountToPoll.key), buffer);
87
+ this[accountToPoll.key] = { data: account, slot };
88
+ // @ts-ignore
89
+ this.eventEmitter.emit(accountToPoll.eventType, account);
90
+ this.eventEmitter.emit('update');
91
+ }
92
+ );
93
+ }
94
+
95
+ this.errorCallbackId = this.accountLoader.addErrorCallbacks((error) => {
96
+ this.eventEmitter.emit('error', error);
97
+ });
98
+ }
99
+
100
+ async fetchIfUnloaded(): Promise<void> {
101
+ let shouldFetch = false;
102
+ for (const [_, accountToPoll] of this.accountsToPoll) {
103
+ if (this[accountToPoll.key] === undefined) {
104
+ shouldFetch = true;
105
+ break;
106
+ }
107
+ }
108
+
109
+ if (shouldFetch) {
110
+ await this.fetch();
111
+ }
112
+ }
113
+
114
+ async fetch(): Promise<void> {
115
+ await this.accountLoader.load();
116
+ for (const [_, accountToPoll] of this.accountsToPoll) {
117
+ const { buffer, slot } = this.accountLoader.getBufferAndSlot(
118
+ accountToPoll.publicKey
119
+ );
120
+ if (buffer) {
121
+ const account = this.program.account[
122
+ accountToPoll.key
123
+ ].coder.accounts.decode(capitalize(accountToPoll.key), buffer);
124
+ this[accountToPoll.key] = { data: account, slot };
125
+ }
126
+ }
127
+ }
128
+
129
+ didSubscriptionSucceed(): boolean {
130
+ let success = true;
131
+ for (const [_, accountToPoll] of this.accountsToPoll) {
132
+ if (!this[accountToPoll.key]) {
133
+ success = false;
134
+ break;
135
+ }
136
+ }
137
+ return success;
138
+ }
139
+
140
+ async unsubscribe(): Promise<void> {
141
+ if (!this.isSubscribed) {
142
+ return;
143
+ }
144
+
145
+ for (const [_, accountToPoll] of this.accountsToPoll) {
146
+ this.accountLoader.removeAccount(
147
+ accountToPoll.publicKey,
148
+ accountToPoll.callbackId
149
+ );
150
+ }
151
+
152
+ this.accountLoader.removeErrorCallbacks(this.errorCallbackId);
153
+ this.errorCallbackId = undefined;
154
+
155
+ this.accountsToPoll.clear();
156
+
157
+ this.isSubscribed = false;
158
+ }
159
+
160
+ assertIsSubscribed(): void {
161
+ if (!this.isSubscribed) {
162
+ throw new NotSubscribedError(
163
+ 'You must call `subscribe` before using this function'
164
+ );
165
+ }
166
+ }
167
+
168
+ public getUserStatsAccountAndSlot(): DataAndSlot<UserStatsAccount> {
169
+ this.assertIsSubscribed();
170
+ return this.userStats;
171
+ }
172
+ }
@@ -4,6 +4,7 @@ import {
4
4
  OracleSource,
5
5
  StateAccount,
6
6
  UserAccount,
7
+ UserStatsAccount,
7
8
  } from '../types';
8
9
  import StrictEventEmitter from 'strict-event-emitter-types';
9
10
  import { EventEmitter } from 'events';
@@ -130,3 +131,20 @@ export type DataAndSlot<T> = {
130
131
  data: T;
131
132
  slot: number;
132
133
  };
134
+
135
+ export interface UserStatsAccountEvents {
136
+ userStatsAccountUpdate: (payload: UserStatsAccount) => void;
137
+ update: void;
138
+ error: (e: Error) => void;
139
+ }
140
+
141
+ export interface UserStatsAccountSubscriber {
142
+ eventEmitter: StrictEventEmitter<EventEmitter, UserStatsAccountEvents>;
143
+ isSubscribed: boolean;
144
+
145
+ subscribe(): Promise<boolean>;
146
+ fetch(): Promise<void>;
147
+ unsubscribe(): Promise<void>;
148
+
149
+ getUserStatsAccountAndSlot(): DataAndSlot<UserStatsAccount>;
150
+ }
@@ -0,0 +1,80 @@
1
+ import {
2
+ DataAndSlot,
3
+ AccountSubscriber,
4
+ NotSubscribedError,
5
+ UserStatsAccountSubscriber,
6
+ UserStatsAccountEvents,
7
+ } from './types';
8
+ import { Program } from '@project-serum/anchor';
9
+ import StrictEventEmitter from 'strict-event-emitter-types';
10
+ import { EventEmitter } from 'events';
11
+ import { PublicKey } from '@solana/web3.js';
12
+ import { WebSocketAccountSubscriber } from './webSocketAccountSubscriber';
13
+ import { UserStatsAccount } from '../types';
14
+
15
+ export class WebSocketUserStatsAccountSubscriber
16
+ implements UserStatsAccountSubscriber
17
+ {
18
+ isSubscribed: boolean;
19
+ program: Program;
20
+ eventEmitter: StrictEventEmitter<EventEmitter, UserStatsAccountEvents>;
21
+ userStatsAccountPublicKey: PublicKey;
22
+
23
+ userStatsAccountSubscriber: AccountSubscriber<UserStatsAccount>;
24
+
25
+ public constructor(program: Program, userStatsAccountPublicKey: PublicKey) {
26
+ this.isSubscribed = false;
27
+ this.program = program;
28
+ this.userStatsAccountPublicKey = userStatsAccountPublicKey;
29
+ this.eventEmitter = new EventEmitter();
30
+ }
31
+
32
+ async subscribe(): Promise<boolean> {
33
+ if (this.isSubscribed) {
34
+ return true;
35
+ }
36
+
37
+ this.userStatsAccountSubscriber = new WebSocketAccountSubscriber(
38
+ 'userStats',
39
+ this.program,
40
+ this.userStatsAccountPublicKey
41
+ );
42
+ await this.userStatsAccountSubscriber.subscribe(
43
+ (data: UserStatsAccount) => {
44
+ this.eventEmitter.emit('userStatsAccountUpdate', data);
45
+ this.eventEmitter.emit('update');
46
+ }
47
+ );
48
+
49
+ this.eventEmitter.emit('update');
50
+ this.isSubscribed = true;
51
+ return true;
52
+ }
53
+
54
+ async fetch(): Promise<void> {
55
+ await Promise.all([this.userStatsAccountSubscriber.fetch()]);
56
+ }
57
+
58
+ async unsubscribe(): Promise<void> {
59
+ if (!this.isSubscribed) {
60
+ return;
61
+ }
62
+
63
+ await Promise.all([this.userStatsAccountSubscriber.unsubscribe()]);
64
+
65
+ this.isSubscribed = false;
66
+ }
67
+
68
+ assertIsSubscribed(): void {
69
+ if (!this.isSubscribed) {
70
+ throw new NotSubscribedError(
71
+ 'You must call `subscribe` before using this function'
72
+ );
73
+ }
74
+ }
75
+
76
+ public getUserStatsAccountAndSlot(): DataAndSlot<UserStatsAccount> {
77
+ this.assertIsSubscribed();
78
+ return this.userStatsAccountSubscriber.dataAndSlot;
79
+ }
80
+ }
@@ -57,6 +57,19 @@ export function getUserAccountPublicKeySync(
57
57
  )[0];
58
58
  }
59
59
 
60
+ export function getUserStatsAccountPublicKey(
61
+ programId: PublicKey,
62
+ authority: PublicKey
63
+ ): PublicKey {
64
+ return anchor.web3.PublicKey.findProgramAddressSync(
65
+ [
66
+ Buffer.from(anchor.utils.bytes.utf8.encode('user_stats')),
67
+ authority.toBuffer(),
68
+ ],
69
+ programId
70
+ )[0];
71
+ }
72
+
60
73
  export async function getMarketPublicKey(
61
74
  programId: PublicKey,
62
75
  marketIndex: BN