@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
@@ -9,3 +9,7 @@ export declare function calculateInterestAccumulated(bank: BankAccount, now: BN)
9
9
  borrowInterest: BN;
10
10
  depositInterest: BN;
11
11
  };
12
+ export declare function calculateWithdrawLimit(bank: BankAccount, now: BN): {
13
+ borrowLimit: BN;
14
+ withdrawLimit: BN;
15
+ };
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.calculateInterestAccumulated = exports.calculateLiabilityWeight = exports.calculateAssetWeight = exports.getTokenAmount = exports.getBalance = void 0;
3
+ exports.calculateWithdrawLimit = exports.calculateInterestAccumulated = exports.calculateLiabilityWeight = exports.calculateAssetWeight = exports.getTokenAmount = exports.getBalance = void 0;
4
4
  const types_1 = require("../types");
5
5
  const anchor_1 = require("@project-serum/anchor");
6
6
  const numericConstants_1 = require("../constants/numericConstants");
@@ -126,3 +126,25 @@ function calculateInterestAccumulated(bank, now) {
126
126
  return { borrowInterest, depositInterest };
127
127
  }
128
128
  exports.calculateInterestAccumulated = calculateInterestAccumulated;
129
+ function calculateWithdrawLimit(bank, now) {
130
+ const bankDepositTokenAmount = getTokenAmount(bank.depositBalance, bank, types_1.BankBalanceType.DEPOSIT);
131
+ const bankBorrowTokenAmount = getTokenAmount(bank.borrowBalance, bank, types_1.BankBalanceType.BORROW);
132
+ const twentyFourHours = new anchor_1.BN(60 * 60 * 24);
133
+ const sinceLast = now.sub(bank.lastUpdated);
134
+ const sinceStart = anchor_1.BN.max(numericConstants_1.ZERO, twentyFourHours.sub(sinceLast));
135
+ const borrowTokenTwapLive = bank.borrowTokenTwap
136
+ .mul(sinceStart)
137
+ .add(bankBorrowTokenAmount.mul(sinceLast))
138
+ .div(sinceLast.add(sinceLast));
139
+ const depositTokenTwapLive = bank.depositTokenTwap
140
+ .mul(sinceStart)
141
+ .add(bankDepositTokenAmount.mul(sinceLast))
142
+ .div(sinceLast.add(sinceLast));
143
+ const maxBorrowTokens = anchor_1.BN.min(anchor_1.BN.max(bankDepositTokenAmount.div(new anchor_1.BN(6)), borrowTokenTwapLive.add(borrowTokenTwapLive.div(new anchor_1.BN(5)))), bankDepositTokenAmount.sub(bankDepositTokenAmount.div(new anchor_1.BN(10)))); // between ~15-90% utilization with friction on twap
144
+ const minDepositTokens = depositTokenTwapLive.sub(anchor_1.BN.min(anchor_1.BN.max(depositTokenTwapLive.div(new anchor_1.BN(5)), bank.withdrawGuardThreshold), depositTokenTwapLive));
145
+ return {
146
+ borrowLimit: maxBorrowTokens.sub(bankBorrowTokenAmount),
147
+ withdrawLimit: bankDepositTokenAmount.sub(minDepositTokens),
148
+ };
149
+ }
150
+ exports.calculateWithdrawLimit = calculateWithdrawLimit;
@@ -1,3 +1,6 @@
1
+ /// <reference types="bn.js" />
1
2
  import { AMM, OracleGuardRails } from '../types';
2
3
  import { OraclePriceData } from '../oracles/types';
4
+ import { BN } from '../index';
3
5
  export declare function isOracleValid(amm: AMM, oraclePriceData: OraclePriceData, oracleGuardRails: OracleGuardRails, slot: number): boolean;
6
+ export declare function isOracleTooDivergent(amm: AMM, oraclePriceData: OraclePriceData, oracleGuardRails: OracleGuardRails, now: BN): boolean;
@@ -1,19 +1,21 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isOracleValid = void 0;
3
+ exports.isOracleTooDivergent = exports.isOracleValid = void 0;
4
4
  const numericConstants_1 = require("../constants/numericConstants");
5
5
  const index_1 = require("../index");
6
6
  function isOracleValid(amm, oraclePriceData, oracleGuardRails, slot) {
7
- const isOraclePriceNonPositive = oraclePriceData.price.lt(numericConstants_1.ZERO);
7
+ const isOraclePriceNonPositive = oraclePriceData.price.lte(numericConstants_1.ZERO);
8
8
  const isOraclePriceTooVolatile = oraclePriceData.price
9
9
  .div(index_1.BN.max(numericConstants_1.ONE, amm.lastOraclePriceTwap))
10
10
  .gt(oracleGuardRails.validity.tooVolatileRatio) ||
11
11
  amm.lastOraclePriceTwap
12
12
  .div(index_1.BN.max(numericConstants_1.ONE, oraclePriceData.price))
13
13
  .gt(oracleGuardRails.validity.tooVolatileRatio);
14
- const isConfidenceTooLarge = oraclePriceData.price
15
- .div(index_1.BN.max(numericConstants_1.ONE, oraclePriceData.confidence))
16
- .lt(oracleGuardRails.validity.confidenceIntervalMaxSize);
14
+ const isConfidenceTooLarge = new index_1.BN(amm.baseSpread)
15
+ .add(index_1.BN.max(numericConstants_1.ONE, oraclePriceData.confidence))
16
+ .mul(numericConstants_1.BID_ASK_SPREAD_PRECISION)
17
+ .div(oraclePriceData.price)
18
+ .gt(new index_1.BN(amm.maxSpread));
17
19
  const oracleIsStale = oraclePriceData.slot
18
20
  .sub(new index_1.BN(slot))
19
21
  .gt(oracleGuardRails.validity.slotsBeforeStale);
@@ -24,3 +26,21 @@ function isOracleValid(amm, oraclePriceData, oracleGuardRails, slot) {
24
26
  isConfidenceTooLarge);
25
27
  }
26
28
  exports.isOracleValid = isOracleValid;
29
+ function isOracleTooDivergent(amm, oraclePriceData, oracleGuardRails, now) {
30
+ const sinceLastUpdate = now.sub(amm.lastOraclePriceTwapTs);
31
+ const sinceStart = index_1.BN.max(numericConstants_1.ZERO, new index_1.BN(60 * 5).sub(sinceLastUpdate));
32
+ const oracleTwap5min = amm.lastOraclePriceTwap5min
33
+ .mul(sinceStart)
34
+ .add(oraclePriceData.price)
35
+ .mul(sinceLastUpdate)
36
+ .div(sinceStart.add(sinceLastUpdate));
37
+ const oracleSpread = oracleTwap5min.sub(oraclePriceData.price);
38
+ const oracleSpreadPct = oracleSpread
39
+ .mul(numericConstants_1.MARK_PRICE_PRECISION)
40
+ .div(oracleTwap5min);
41
+ const tooDivergent = oracleSpreadPct
42
+ .abs()
43
+ .gte(numericConstants_1.BID_ASK_SPREAD_PRECISION.mul(oracleGuardRails.priceDivergence.markOracleDivergenceNumerator).div(oracleGuardRails.priceDivergence.markOracleDivergenceDenominator));
44
+ return tooDivergent;
45
+ }
46
+ exports.isOracleTooDivergent = isOracleTooDivergent;
@@ -117,7 +117,8 @@ exports.calculatePositionFundingPNL = calculatePositionFundingPNL;
117
117
  function positionIsAvailable(position) {
118
118
  return (position.baseAssetAmount.eq(numericConstants_1.ZERO) &&
119
119
  position.openOrders.eq(numericConstants_1.ZERO) &&
120
- position.quoteAssetAmount.eq(numericConstants_1.ZERO));
120
+ position.quoteAssetAmount.eq(numericConstants_1.ZERO) &&
121
+ position.lpShares.eq(numericConstants_1.ZERO));
121
122
  }
122
123
  exports.positionIsAvailable = positionIsAvailable;
123
124
  /**
package/lib/math/trade.js CHANGED
@@ -113,8 +113,8 @@ function calculateTradeAcquiredAmounts(direction, amount, market, inputAssetType
113
113
  const [newQuoteAssetReserve, newBaseAssetReserve] = (0, amm_1.calculateAmmReservesAfterSwap)(amm, inputAssetType, amount, swapDirection);
114
114
  const acquiredBase = amm.baseAssetReserve.sub(newBaseAssetReserve);
115
115
  const acquiredQuote = amm.quoteAssetReserve.sub(newQuoteAssetReserve);
116
- const acquiredQuoteAssetamount = (0, amm_1.calculateQuoteAssetAmountSwapped)(acquiredQuote.abs(), amm.pegMultiplier, swapDirection);
117
- return [acquiredBase, acquiredQuote, acquiredQuoteAssetamount];
116
+ const acquiredQuoteAssetAmount = (0, amm_1.calculateQuoteAssetAmountSwapped)(acquiredQuote.abs(), amm.pegMultiplier, swapDirection);
117
+ return [acquiredBase, acquiredQuote, acquiredQuoteAssetAmount];
118
118
  }
119
119
  exports.calculateTradeAcquiredAmounts = calculateTradeAcquiredAmounts;
120
120
  /**
package/lib/types.d.ts CHANGED
@@ -203,6 +203,8 @@ export declare type LiquidationRecord = {
203
203
  liquidateBorrow: LiquidateBorrowRecord;
204
204
  liquidateBorrowForPerpPnl: LiquidateBorrowForPerpPnlRecord;
205
205
  liquidatePerpPnlForDeposit: LiquidatePerpPnlForDepositRecord;
206
+ perpBankruptcy: PerpBankruptcyRecord;
207
+ borrowBankruptcy: BorrowBankruptcyRecord;
206
208
  };
207
209
  export declare class LiquidationType {
208
210
  static readonly LIQUIDATE_PERP: {
@@ -217,6 +219,12 @@ export declare class LiquidationType {
217
219
  static readonly LIQUIDATE_PERP_PNL_FOR_DEPOSIT: {
218
220
  liquidatePerpPnlForDeposit: {};
219
221
  };
222
+ static readonly PERP_BANKRUPTCY: {
223
+ perpBankruptcy: {};
224
+ };
225
+ static readonly BORROW_BANKRUPTCY: {
226
+ borrowBankruptcy: {};
227
+ };
220
228
  }
221
229
  export declare type LiquidatePerpRecord = {
222
230
  marketIndex: BN;
@@ -255,14 +263,25 @@ export declare type LiquidatePerpPnlForDepositRecord = {
255
263
  assetPrice: BN;
256
264
  assetTransfer: BN;
257
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
+ };
258
276
  export declare type SettlePnlRecord = {
259
277
  ts: BN;
278
+ user: PublicKey;
260
279
  marketIndex: BN;
261
280
  pnl: BN;
262
281
  baseAssetAmount: BN;
263
282
  quoteAssetAmountAfter: BN;
264
283
  quoteEntryamount: BN;
265
- oraclePrice: BN;
284
+ settlePrice: BN;
266
285
  };
267
286
  export declare type OrderRecord = {
268
287
  ts: BN;
@@ -357,6 +376,10 @@ export declare type BankAccount = {
357
376
  maintenanceLiabilityWeight: BN;
358
377
  liquidationFee: BN;
359
378
  imfFactor: BN;
379
+ withdrawGuardThreshold: BN;
380
+ depositTokenTwap: BN;
381
+ borrowTokenTwap: BN;
382
+ utilizationTwap: BN;
360
383
  };
361
384
  export declare type PoolBalance = {
362
385
  balance: BN;
@@ -368,8 +391,10 @@ export declare type AMM = {
368
391
  lastFundingRate: BN;
369
392
  lastFundingRateTs: BN;
370
393
  lastMarkPriceTwap: BN;
394
+ lastMarkPriceTwap5min: BN;
371
395
  lastMarkPriceTwapTs: BN;
372
396
  lastOraclePriceTwap: BN;
397
+ lastOraclePriceTwap5min: BN;
373
398
  lastOraclePriceTwapTs: BN;
374
399
  lastOracleMarkSpreadPct: BN;
375
400
  lastOracleConfPct: BN;
@@ -380,11 +405,16 @@ export declare type AMM = {
380
405
  pegMultiplier: BN;
381
406
  cumulativeFundingRateLong: BN;
382
407
  cumulativeFundingRateShort: BN;
408
+ cumulativeFundingRateLp: BN;
383
409
  cumulativeRepegRebateLong: BN;
384
410
  cumulativeRepegRebateShort: BN;
385
411
  totalFeeMinusDistributions: BN;
386
412
  totalFeeWithdrawn: BN;
387
413
  totalFee: BN;
414
+ cumulativeFundingPaymentPerLp: BN;
415
+ cumulativeFeePerLp: BN;
416
+ cumulativeNetBaseAssetAmountPerLp: BN;
417
+ userLpShares: BN;
388
418
  minimumQuoteAssetTradeSize: BN;
389
419
  baseAssetAmountStepSize: BN;
390
420
  maxBaseAssetAmountRatio: number;
@@ -406,6 +436,8 @@ export declare type AMM = {
406
436
  longSpread: BN;
407
437
  shortSpread: BN;
408
438
  maxSpread: number;
439
+ marketPosition: UserPosition;
440
+ marketPositionPerLp: UserPosition;
409
441
  };
410
442
  export declare type UserPosition = {
411
443
  baseAssetAmount: BN;
@@ -416,14 +448,20 @@ export declare type UserPosition = {
416
448
  openOrders: BN;
417
449
  openBids: BN;
418
450
  openAsks: BN;
451
+ realizedPnl: BN;
452
+ lpShares: BN;
453
+ lastFeePerLp: BN;
454
+ lastNetBaseAssetAmountPerLp: BN;
455
+ lastNetQuoteAssetAmountPerLp: BN;
419
456
  };
420
- export declare type UserAccount = {
421
- authority: PublicKey;
422
- name: number[];
423
- userId: number;
424
- bankBalances: UserBankBalance[];
425
- collateral: BN;
426
- 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;
427
465
  fees: {
428
466
  totalFeePaid: BN;
429
467
  totalFeeRebate: BN;
@@ -431,9 +469,16 @@ export declare type UserAccount = {
431
469
  totalReferralReward: BN;
432
470
  totalRefereeDiscount: BN;
433
471
  };
472
+ };
473
+ export declare type UserAccount = {
474
+ authority: PublicKey;
475
+ name: number[];
476
+ userId: number;
477
+ bankBalances: UserBankBalance[];
434
478
  positions: UserPosition[];
435
479
  orders: Order[];
436
480
  beingLiquidated: boolean;
481
+ bankrupt: boolean;
437
482
  nextLiquidationId: number;
438
483
  };
439
484
  export declare type UserBankBalance = {
@@ -529,10 +574,12 @@ export declare const DefaultOrderParams: {
529
574
  };
530
575
  export declare type MakerInfo = {
531
576
  maker: PublicKey;
577
+ makerStats: PublicKey;
532
578
  order: Order;
533
579
  };
534
580
  export declare type TakerInfo = {
535
581
  taker: PublicKey;
582
+ takerStats: PublicKey;
536
583
  order: Order;
537
584
  };
538
585
  export interface IWallet {
package/lib/types.js CHANGED
@@ -108,6 +108,12 @@ LiquidationType.LIQUIDATE_BORROW_FOR_PERP_PNL = {
108
108
  LiquidationType.LIQUIDATE_PERP_PNL_FOR_DEPOSIT = {
109
109
  liquidatePerpPnlForDeposit: {},
110
110
  };
111
+ LiquidationType.PERP_BANKRUPTCY = {
112
+ perpBankruptcy: {},
113
+ };
114
+ LiquidationType.BORROW_BANKRUPTCY = {
115
+ borrowBankruptcy: {},
116
+ };
111
117
  exports.DefaultOrderParams = {
112
118
  orderType: OrderType.MARKET,
113
119
  userOrderId: 0,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drift-labs/sdk",
3
- "version": "0.2.0-master.13",
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
package/src/admin.ts CHANGED
@@ -575,6 +575,35 @@ export class Admin extends ClearingHouse {
575
575
  });
576
576
  }
577
577
 
578
+ public async updateBankWithdrawGuardThreshold(
579
+ bankIndex: BN,
580
+ withdrawGuardThreshold: BN
581
+ ): Promise<TransactionSignature> {
582
+ return await this.program.rpc.updateBankWithdrawGuardThreshold(
583
+ withdrawGuardThreshold,
584
+ {
585
+ accounts: {
586
+ admin: this.wallet.publicKey,
587
+ state: await this.getStatePublicKey(),
588
+ bank: await getBankPublicKey(this.program.programId, bankIndex),
589
+ },
590
+ }
591
+ );
592
+ }
593
+
594
+ public async updateLpCooldownTime(
595
+ marketIndex: BN,
596
+ cooldownTime: BN
597
+ ): Promise<TransactionSignature> {
598
+ return await this.program.rpc.updateLpCooldownTime(cooldownTime, {
599
+ accounts: {
600
+ admin: this.wallet.publicKey,
601
+ state: await this.getStatePublicKey(),
602
+ market: await getMarketPublicKey(this.program.programId, marketIndex),
603
+ },
604
+ });
605
+ }
606
+
578
607
  public async updateMarketOracle(
579
608
  marketIndex: BN,
580
609
  oracle: PublicKey,
@@ -662,7 +691,6 @@ export class Admin extends ClearingHouse {
662
691
  },
663
692
  });
664
693
  }
665
-
666
694
  public async updateExchangePaused(
667
695
  exchangePaused: boolean
668
696
  ): Promise<TransactionSignature> {