@drift-labs/sdk 0.1.29-master.1 → 0.1.30-master.1

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 (73) hide show
  1. package/lib/accounts/bulkAccountLoader.d.ts +2 -1
  2. package/lib/accounts/bulkAccountLoader.js +44 -32
  3. package/lib/accounts/pollingClearingHouseAccountSubscriber.d.ts +15 -0
  4. package/lib/accounts/pollingClearingHouseAccountSubscriber.js +50 -23
  5. package/lib/clearingHouse.d.ts +4 -2
  6. package/lib/clearingHouse.js +49 -14
  7. package/lib/constants/accounts.d.ts +15 -0
  8. package/lib/constants/accounts.js +18 -0
  9. package/lib/constants/markets.js +11 -0
  10. package/lib/factory/clearingHouse.d.ts +14 -4
  11. package/lib/factory/clearingHouse.js +23 -6
  12. package/lib/idl/clearing_house.json +53 -33
  13. package/lib/math/amm.d.ts +18 -0
  14. package/lib/math/amm.js +88 -1
  15. package/lib/oracles/pythClient.js +1 -1
  16. package/lib/orderParams.d.ts +1 -1
  17. package/lib/orderParams.js +2 -2
  18. package/lib/tx/retryTxSender.d.ts +5 -2
  19. package/lib/tx/retryTxSender.js +14 -1
  20. package/lib/util/promiseTimeout.d.ts +1 -0
  21. package/lib/util/promiseTimeout.js +14 -0
  22. package/package.json +1 -1
  23. package/src/accounts/bulkAccountLoader.ts +51 -40
  24. package/src/accounts/pollingClearingHouseAccountSubscriber.ts +73 -30
  25. package/src/clearingHouse.ts +71 -13
  26. package/src/constants/accounts.ts +26 -0
  27. package/src/constants/markets.ts +11 -0
  28. package/src/factory/clearingHouse.ts +47 -7
  29. package/src/idl/clearing_house.json +53 -33
  30. package/src/math/amm.ts +127 -2
  31. package/src/oracles/pythClient.ts +1 -1
  32. package/src/orderParams.ts +3 -2
  33. package/src/tx/retryTxSender.ts +19 -1
  34. package/src/util/promiseTimeout.ts +14 -0
  35. package/lib/math/repeg.d.ts +0 -32
  36. package/lib/math/repeg.js +0 -178
  37. package/src/accounts/bulkUserSubscription.js +0 -56
  38. package/src/accounts/bulkUserSubscription.js.map +0 -1
  39. package/src/accounts/pollingClearingHouseAccountSubscriber.js +0 -210
  40. package/src/accounts/pollingClearingHouseAccountSubscriber.js.map +0 -1
  41. package/src/accounts/pollingOracleSubscriber.js +0 -65
  42. package/src/accounts/pollingOracleSubscriber.js.map +0 -1
  43. package/src/accounts/pollingTokenAccountSubscriber.js +0 -65
  44. package/src/accounts/pollingTokenAccountSubscriber.js.map +0 -1
  45. package/src/accounts/pollingUserAccountSubscriber.js +0 -139
  46. package/src/accounts/pollingUserAccountSubscriber.js.map +0 -1
  47. package/src/accounts/types.js +0 -11
  48. package/src/accounts/types.js.map +0 -1
  49. package/src/accounts/utils.js +0 -8
  50. package/src/accounts/utils.js.map +0 -1
  51. package/src/accounts/webSocketAccountSubscriber.js +0 -64
  52. package/src/accounts/webSocketAccountSubscriber.js.map +0 -1
  53. package/src/accounts/webSocketClearingHouseAccountSubscriber.js +0 -212
  54. package/src/accounts/webSocketClearingHouseAccountSubscriber.js.map +0 -1
  55. package/src/accounts/webSocketUserAccountSubscriber.js +0 -78
  56. package/src/accounts/webSocketUserAccountSubscriber.js.map +0 -1
  57. package/src/constants/markets.js +0 -167
  58. package/src/constants/markets.js.map +0 -1
  59. package/src/constants/numericConstants.js +0 -22
  60. package/src/constants/numericConstants.js.map +0 -1
  61. package/src/factory/clearingHouse.js +0 -65
  62. package/src/factory/clearingHouse.js.map +0 -1
  63. package/src/factory/clearingHouseUser.js +0 -35
  64. package/src/factory/clearingHouseUser.js.map +0 -1
  65. package/src/factory/oracleClient.js +0 -17
  66. package/src/factory/oracleClient.js.map +0 -1
  67. package/src/math/amm.js +0 -285
  68. package/src/math/amm.js.map +0 -1
  69. package/src/math/repeg.ts +0 -253
  70. package/src/oracles/pythClient.js +0 -39
  71. package/src/oracles/pythClient.js.map +0 -1
  72. package/src/tx/retryTxSender.js +0 -137
  73. package/src/tx/retryTxSender.js.map +0 -1
@@ -11,18 +11,19 @@ const webSocketClearingHouseAccountSubscriber_1 = require("../accounts/webSocket
11
11
  const defaultTxSender_1 = require("../tx/defaultTxSender");
12
12
  const pollingClearingHouseAccountSubscriber_1 = require("../accounts/pollingClearingHouseAccountSubscriber");
13
13
  const admin_1 = require("../admin");
14
- function getWebSocketClearingHouseConfig(connection, wallet, programID, opts = anchor_1.Provider.defaultOptions(), txSender) {
14
+ const retryTxSender_1 = require("../tx/retryTxSender");
15
+ function getWebSocketClearingHouseConfig(connection, wallet, programID, opts = anchor_1.Provider.defaultOptions(), txSenderConfig) {
15
16
  return {
16
17
  type: 'websocket',
17
18
  connection,
18
19
  wallet,
19
20
  programID,
20
21
  opts,
21
- txSender,
22
+ txSenderConfig,
22
23
  };
23
24
  }
24
25
  exports.getWebSocketClearingHouseConfig = getWebSocketClearingHouseConfig;
25
- function getPollingClearingHouseConfig(connection, wallet, programID, accountLoader, opts = anchor_1.Provider.defaultOptions(), txSender) {
26
+ function getPollingClearingHouseConfig(connection, wallet, programID, accountLoader, opts = anchor_1.Provider.defaultOptions(), txSenderConfig) {
26
27
  return {
27
28
  type: 'polling',
28
29
  connection,
@@ -30,11 +31,12 @@ function getPollingClearingHouseConfig(connection, wallet, programID, accountLoa
30
31
  programID,
31
32
  accountLoader,
32
33
  opts,
33
- txSender,
34
+ txSenderConfig,
34
35
  };
35
36
  }
36
37
  exports.getPollingClearingHouseConfig = getPollingClearingHouseConfig;
37
38
  function getClearingHouse(config) {
39
+ var _a;
38
40
  const provider = new anchor_1.Provider(config.connection, config.wallet, config.opts);
39
41
  const program = new anchor_1.Program(clearing_house_json_1.default, config.programID, provider);
40
42
  let accountSubscriber;
@@ -44,11 +46,19 @@ function getClearingHouse(config) {
44
46
  else if (config.type === 'polling') {
45
47
  accountSubscriber = new pollingClearingHouseAccountSubscriber_1.PollingClearingHouseAccountSubscriber(program, config.accountLoader);
46
48
  }
47
- const txSender = config.txSender || new defaultTxSender_1.DefaultTxSender(provider);
49
+ let txSender;
50
+ if (((_a = config.txSenderConfig) === null || _a === void 0 ? void 0 : _a.type) === 'retry') {
51
+ const txSenderConfig = config.txSenderConfig;
52
+ txSender = new retryTxSender_1.RetryTxSender(provider, txSenderConfig.timeout, txSenderConfig.retrySleep, txSenderConfig.additionalConnections);
53
+ }
54
+ else {
55
+ txSender = new defaultTxSender_1.DefaultTxSender(provider);
56
+ }
48
57
  return new clearingHouse_1.ClearingHouse(config.connection, config.wallet, program, accountSubscriber, txSender, config.opts);
49
58
  }
50
59
  exports.getClearingHouse = getClearingHouse;
51
60
  function getAdmin(config) {
61
+ var _a;
52
62
  const provider = new anchor_1.Provider(config.connection, config.wallet, config.opts);
53
63
  const program = new anchor_1.Program(clearing_house_json_1.default, config.programID, provider);
54
64
  let accountSubscriber;
@@ -58,7 +68,14 @@ function getAdmin(config) {
58
68
  else if (config.type === 'polling') {
59
69
  accountSubscriber = new pollingClearingHouseAccountSubscriber_1.PollingClearingHouseAccountSubscriber(program, config.accountLoader);
60
70
  }
61
- const txSender = config.txSender || new defaultTxSender_1.DefaultTxSender(provider);
71
+ let txSender;
72
+ if (((_a = config.txSenderConfig) === null || _a === void 0 ? void 0 : _a.type) === 'retry') {
73
+ const txSenderConfig = config.txSenderConfig;
74
+ txSender = new retryTxSender_1.RetryTxSender(provider, txSenderConfig.timeout, txSenderConfig.retrySleep, txSenderConfig.additionalConnections);
75
+ }
76
+ else {
77
+ txSender = new defaultTxSender_1.DefaultTxSender(provider);
78
+ }
62
79
  return new admin_1.Admin(config.connection, config.wallet, program, accountSubscriber, txSender, config.opts);
63
80
  }
64
81
  exports.getAdmin = getAdmin;
@@ -677,6 +677,57 @@
677
677
  }
678
678
  ]
679
679
  },
680
+ {
681
+ "name": "cancelAllOrders",
682
+ "accounts": [
683
+ {
684
+ "name": "state",
685
+ "isMut": false,
686
+ "isSigner": false
687
+ },
688
+ {
689
+ "name": "orderState",
690
+ "isMut": false,
691
+ "isSigner": false
692
+ },
693
+ {
694
+ "name": "user",
695
+ "isMut": false,
696
+ "isSigner": false
697
+ },
698
+ {
699
+ "name": "authority",
700
+ "isMut": false,
701
+ "isSigner": true
702
+ },
703
+ {
704
+ "name": "markets",
705
+ "isMut": false,
706
+ "isSigner": false
707
+ },
708
+ {
709
+ "name": "userPositions",
710
+ "isMut": true,
711
+ "isSigner": false
712
+ },
713
+ {
714
+ "name": "userOrders",
715
+ "isMut": true,
716
+ "isSigner": false
717
+ },
718
+ {
719
+ "name": "fundingPaymentHistory",
720
+ "isMut": true,
721
+ "isSigner": false
722
+ },
723
+ {
724
+ "name": "orderHistory",
725
+ "isMut": true,
726
+ "isSigner": false
727
+ }
728
+ ],
729
+ "args": []
730
+ },
680
731
  {
681
732
  "name": "expireOrders",
682
733
  "accounts": [
@@ -1431,32 +1482,6 @@
1431
1482
  }
1432
1483
  ]
1433
1484
  },
1434
- {
1435
- "name": "deleteUser",
1436
- "accounts": [
1437
- {
1438
- "name": "user",
1439
- "isMut": true,
1440
- "isSigner": false
1441
- },
1442
- {
1443
- "name": "userPositions",
1444
- "isMut": true,
1445
- "isSigner": false
1446
- },
1447
- {
1448
- "name": "userOrders",
1449
- "isMut": true,
1450
- "isSigner": false
1451
- },
1452
- {
1453
- "name": "authority",
1454
- "isMut": false,
1455
- "isSigner": true
1456
- }
1457
- ],
1458
- "args": []
1459
- },
1460
1485
  {
1461
1486
  "name": "settleFundingPayment",
1462
1487
  "accounts": [
@@ -3241,8 +3266,8 @@
3241
3266
  "type": "u128"
3242
3267
  },
3243
3268
  {
3244
- "name": "netRevenueSinceLastFunding",
3245
- "type": "i64"
3269
+ "name": "padding1",
3270
+ "type": "u64"
3246
3271
  },
3247
3272
  {
3248
3273
  "name": "padding2",
@@ -4249,11 +4274,6 @@
4249
4274
  "code": 6060,
4250
4275
  "name": "CantExpireOrders",
4251
4276
  "msg": "CantExpireOrders"
4252
- },
4253
- {
4254
- "code": 6061,
4255
- "name": "InvalidRepegPriceImpact",
4256
- "msg": "AMM repeg mark price impact vs oracle too large"
4257
4277
  }
4258
4278
  ]
4259
4279
  }
package/lib/math/amm.d.ts CHANGED
@@ -38,6 +38,24 @@ export declare function calculateSwapOutput(inputAssetReserve: BN, swapAmount: B
38
38
  * @param positionDirection
39
39
  */
40
40
  export declare function getSwapDirection(inputAssetType: AssetType, positionDirection: PositionDirection): SwapDirection;
41
+ /**
42
+ * Helper function calculating adjust k cost
43
+ * @param market
44
+ * @param marketIndex
45
+ * @param numerator
46
+ * @param denomenator
47
+ * @returns cost : Precision QUOTE_ASSET_PRECISION
48
+ */
49
+ export declare function calculateAdjustKCost(market: Market, marketIndex: BN, numerator: BN, denomenator: BN): BN;
50
+ /**
51
+ * Helper function calculating adjust pegMultiplier (repeg) cost
52
+ *
53
+ * @param market
54
+ * @param marketIndex
55
+ * @param newPeg
56
+ * @returns cost : Precision QUOTE_ASSET_PRECISION
57
+ */
58
+ export declare function calculateRepegCost(market: Market, marketIndex: BN, newPeg: BN): BN;
41
59
  /**
42
60
  * Helper function calculating terminal price of amm
43
61
  *
package/lib/math/amm.js CHANGED
@@ -1,8 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.calculateBudgetedPeg = exports.calculateBudgetedK = exports.calculateMaxBaseAssetAmountToTrade = exports.calculateTerminalPrice = exports.getSwapDirection = exports.calculateSwapOutput = exports.calculateAmmReservesAfterSwap = exports.calculatePrice = void 0;
3
+ exports.calculateBudgetedPeg = exports.calculateBudgetedK = exports.calculateMaxBaseAssetAmountToTrade = exports.calculateTerminalPrice = exports.calculateRepegCost = exports.calculateAdjustKCost = exports.getSwapDirection = exports.calculateSwapOutput = exports.calculateAmmReservesAfterSwap = exports.calculatePrice = void 0;
4
4
  const anchor_1 = require("@project-serum/anchor");
5
5
  const numericConstants_1 = require("../constants/numericConstants");
6
+ const position_1 = require("./position");
6
7
  const types_1 = require("../types");
7
8
  const assert_1 = require("../assert/assert");
8
9
  const __1 = require("..");
@@ -87,6 +88,92 @@ function getSwapDirection(inputAssetType, positionDirection) {
87
88
  return types_1.SwapDirection.ADD;
88
89
  }
89
90
  exports.getSwapDirection = getSwapDirection;
91
+ /**
92
+ * Helper function calculating adjust k cost
93
+ * @param market
94
+ * @param marketIndex
95
+ * @param numerator
96
+ * @param denomenator
97
+ * @returns cost : Precision QUOTE_ASSET_PRECISION
98
+ */
99
+ function calculateAdjustKCost(market, marketIndex, numerator, denomenator) {
100
+ const netUserPosition = {
101
+ baseAssetAmount: market.baseAssetAmount,
102
+ lastCumulativeFundingRate: market.amm.cumulativeFundingRate,
103
+ marketIndex: new anchor_1.BN(marketIndex),
104
+ quoteAssetAmount: new anchor_1.BN(0),
105
+ openOrders: new anchor_1.BN(0),
106
+ };
107
+ const currentValue = (0, position_1.calculateBaseAssetValue)(market, netUserPosition);
108
+ const marketNewK = Object.assign({}, market);
109
+ marketNewK.amm = Object.assign({}, market.amm);
110
+ marketNewK.amm.baseAssetReserve = market.amm.baseAssetReserve
111
+ .mul(numerator)
112
+ .div(denomenator);
113
+ marketNewK.amm.quoteAssetReserve = market.amm.quoteAssetReserve
114
+ .mul(numerator)
115
+ .div(denomenator);
116
+ marketNewK.amm.sqrtK = market.amm.sqrtK.mul(numerator).div(denomenator);
117
+ netUserPosition.quoteAssetAmount = currentValue;
118
+ const cost = (0, __1.calculatePositionPNL)(marketNewK, netUserPosition);
119
+ const p = numericConstants_1.PEG_PRECISION.mul(numerator).div(denomenator);
120
+ const x = market.amm.baseAssetReserve;
121
+ const y = market.amm.quoteAssetReserve;
122
+ const delta = market.baseAssetAmount;
123
+ const k = market.amm.sqrtK.mul(market.amm.sqrtK);
124
+ const numer1 = numericConstants_1.PEG_PRECISION.sub(p).mul(y).div(numericConstants_1.PEG_PRECISION);
125
+ const numer20 = k
126
+ .mul(p)
127
+ .mul(p)
128
+ .div(numericConstants_1.PEG_PRECISION)
129
+ .div(numericConstants_1.PEG_PRECISION)
130
+ .div(x.mul(p).div(numericConstants_1.PEG_PRECISION).add(delta));
131
+ const numer21 = k.div(x.add(delta));
132
+ const formulaCost = numer21
133
+ .sub(numer20)
134
+ .sub(numer1)
135
+ .mul(market.amm.pegMultiplier)
136
+ .div(numericConstants_1.AMM_TIMES_PEG_TO_QUOTE_PRECISION_RATIO);
137
+ console.log((0, __1.convertToNumber)(formulaCost, numericConstants_1.QUOTE_PRECISION));
138
+ // p.div(p.mul(x).add(delta)).sub()
139
+ return cost;
140
+ }
141
+ exports.calculateAdjustKCost = calculateAdjustKCost;
142
+ /**
143
+ * Helper function calculating adjust pegMultiplier (repeg) cost
144
+ *
145
+ * @param market
146
+ * @param marketIndex
147
+ * @param newPeg
148
+ * @returns cost : Precision QUOTE_ASSET_PRECISION
149
+ */
150
+ function calculateRepegCost(market, marketIndex, newPeg) {
151
+ const netUserPosition = {
152
+ baseAssetAmount: market.baseAssetAmount,
153
+ lastCumulativeFundingRate: market.amm.cumulativeFundingRate,
154
+ marketIndex: new anchor_1.BN(marketIndex),
155
+ quoteAssetAmount: new anchor_1.BN(0),
156
+ openOrders: new anchor_1.BN(0),
157
+ };
158
+ const currentValue = (0, position_1.calculateBaseAssetValue)(market, netUserPosition);
159
+ netUserPosition.quoteAssetAmount = currentValue;
160
+ const prevMarketPrice = (0, __1.calculateMarkPrice)(market);
161
+ const marketNewPeg = Object.assign({}, market);
162
+ marketNewPeg.amm = Object.assign({}, market.amm);
163
+ // const marketNewPeg = JSON.parse(JSON.stringify(market));
164
+ marketNewPeg.amm.pegMultiplier = newPeg;
165
+ console.log('Price moves from', (0, __1.convertToNumber)(prevMarketPrice), 'to', (0, __1.convertToNumber)((0, __1.calculateMarkPrice)(marketNewPeg)));
166
+ const cost = (0, __1.calculatePositionPNL)(marketNewPeg, netUserPosition);
167
+ const k = market.amm.sqrtK.mul(market.amm.sqrtK);
168
+ const newQuoteAssetReserve = k.div(market.amm.baseAssetReserve.add(netUserPosition.baseAssetAmount));
169
+ const deltaQuoteAssetReserves = newQuoteAssetReserve.sub(market.amm.quoteAssetReserve);
170
+ const cost2 = deltaQuoteAssetReserves
171
+ .mul(market.amm.pegMultiplier.sub(newPeg))
172
+ .div(numericConstants_1.AMM_TIMES_PEG_TO_QUOTE_PRECISION_RATIO);
173
+ console.log((0, __1.convertToNumber)(cost2, numericConstants_1.QUOTE_PRECISION));
174
+ return cost;
175
+ }
176
+ exports.calculateRepegCost = calculateRepegCost;
90
177
  /**
91
178
  * Helper function calculating terminal price of amm
92
179
  *
@@ -33,7 +33,7 @@ class PythClient {
33
33
  return __awaiter(this, void 0, void 0, function* () {
34
34
  const priceData = (0, client_1.parsePriceData)(buffer);
35
35
  return {
36
- price: convertPythPrice(priceData.price, priceData.exponent),
36
+ price: convertPythPrice(priceData.aggregate.price, priceData.exponent),
37
37
  slot: new anchor_1.BN(priceData.lastSlot.toString()),
38
38
  confidence: convertPythPrice(priceData.confidence, priceData.exponent),
39
39
  twap: convertPythPrice(priceData.twap.value, priceData.exponent),
@@ -1,7 +1,7 @@
1
1
  /// <reference types="bn.js" />
2
2
  import { OrderParams, OrderTriggerCondition, PositionDirection } from './types';
3
3
  import { BN } from '@project-serum/anchor';
4
- export declare function getLimitOrderParams(marketIndex: BN, direction: PositionDirection, baseAssetAmount: BN, price: BN, reduceOnly: boolean, discountToken?: boolean, referrer?: boolean, userOrderId?: number, postOnly?: boolean, oraclePriceOffset?: BN): OrderParams;
4
+ export declare function getLimitOrderParams(marketIndex: BN, direction: PositionDirection, baseAssetAmount: BN, price: BN, reduceOnly: boolean, discountToken?: boolean, referrer?: boolean, userOrderId?: number, postOnly?: boolean, oraclePriceOffset?: BN, immediateOrCancel?: boolean): OrderParams;
5
5
  export declare function getTriggerMarketOrderParams(marketIndex: BN, direction: PositionDirection, baseAssetAmount: BN, triggerPrice: BN, triggerCondition: OrderTriggerCondition, reduceOnly: boolean, discountToken?: boolean, referrer?: boolean, userOrderId?: number): OrderParams;
6
6
  export declare function getTriggerLimitOrderParams(marketIndex: BN, direction: PositionDirection, baseAssetAmount: BN, price: BN, triggerPrice: BN, triggerCondition: OrderTriggerCondition, reduceOnly: boolean, discountToken?: boolean, referrer?: boolean, userOrderId?: number): OrderParams;
7
7
  export declare function getMarketOrderParams(marketIndex: BN, direction: PositionDirection, quoteAssetAmount: BN, baseAssetAmount: BN, reduceOnly: boolean, price?: BN, discountToken?: boolean, referrer?: boolean): OrderParams;
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getMarketOrderParams = exports.getTriggerLimitOrderParams = exports.getTriggerMarketOrderParams = exports.getLimitOrderParams = void 0;
4
4
  const types_1 = require("./types");
5
5
  const numericConstants_1 = require("./constants/numericConstants");
6
- function getLimitOrderParams(marketIndex, direction, baseAssetAmount, price, reduceOnly, discountToken = false, referrer = false, userOrderId = 0, postOnly = false, oraclePriceOffset = numericConstants_1.ZERO) {
6
+ function getLimitOrderParams(marketIndex, direction, baseAssetAmount, price, reduceOnly, discountToken = false, referrer = false, userOrderId = 0, postOnly = false, oraclePriceOffset = numericConstants_1.ZERO, immediateOrCancel = false) {
7
7
  return {
8
8
  orderType: types_1.OrderType.LIMIT,
9
9
  userOrderId,
@@ -14,7 +14,7 @@ function getLimitOrderParams(marketIndex, direction, baseAssetAmount, price, red
14
14
  price,
15
15
  reduceOnly,
16
16
  postOnly,
17
- immediateOrCancel: false,
17
+ immediateOrCancel,
18
18
  positionLimit: numericConstants_1.ZERO,
19
19
  padding0: true,
20
20
  padding1: numericConstants_1.ZERO,
@@ -1,5 +1,6 @@
1
+ /// <reference types="node" />
1
2
  import { TxSender } from './types';
2
- import { Commitment, ConfirmOptions, RpcResponseAndContext, Signer, SignatureResult, Transaction, TransactionSignature } from '@solana/web3.js';
3
+ import { Commitment, ConfirmOptions, RpcResponseAndContext, Signer, SignatureResult, Transaction, TransactionSignature, Connection } from '@solana/web3.js';
3
4
  import { Provider } from '@project-serum/anchor';
4
5
  declare type ResolveReference = {
5
6
  resolve?: () => void;
@@ -8,12 +9,14 @@ export declare class RetryTxSender implements TxSender {
8
9
  provider: Provider;
9
10
  timeout: number;
10
11
  retrySleep: number;
11
- constructor(provider: Provider, timeout?: number, retrySleep?: number);
12
+ additionalConnections: Connection[];
13
+ constructor(provider: Provider, timeout?: number, retrySleep?: number, additionalConnections?: Connection[]);
12
14
  send(tx: Transaction, additionalSigners?: Array<Signer>, opts?: ConfirmOptions): Promise<TransactionSignature>;
13
15
  prepareTx(tx: Transaction, additionalSigners: Array<Signer>, opts: ConfirmOptions): Promise<Transaction>;
14
16
  confirmTransaction(signature: TransactionSignature, commitment?: Commitment): Promise<RpcResponseAndContext<SignatureResult>>;
15
17
  getTimestamp(): number;
16
18
  sleep(reference: ResolveReference): Promise<void>;
17
19
  promiseTimeout<T>(promise: Promise<T>, timeoutMs: number): Promise<T | null>;
20
+ sendToAdditionalConnections(rawTx: Buffer, opts: ConfirmOptions): void;
18
21
  }
19
22
  export {};
@@ -18,10 +18,11 @@ const bs58_1 = __importDefault(require("bs58"));
18
18
  const DEFAULT_TIMEOUT = 35000;
19
19
  const DEFAULT_RETRY = 8000;
20
20
  class RetryTxSender {
21
- constructor(provider, timeout, retrySleep) {
21
+ constructor(provider, timeout, retrySleep, additionalConnections = new Array()) {
22
22
  this.provider = provider;
23
23
  this.timeout = timeout !== null && timeout !== void 0 ? timeout : DEFAULT_TIMEOUT;
24
24
  this.retrySleep = retrySleep !== null && retrySleep !== void 0 ? retrySleep : DEFAULT_RETRY;
25
+ this.additionalConnections = additionalConnections;
25
26
  }
26
27
  send(tx, additionalSigners, opts) {
27
28
  return __awaiter(this, void 0, void 0, function* () {
@@ -35,6 +36,7 @@ class RetryTxSender {
35
36
  const rawTransaction = tx.serialize();
36
37
  const startTime = this.getTimestamp();
37
38
  const txid = yield this.provider.connection.sendRawTransaction(rawTransaction, opts);
39
+ this.sendToAdditionalConnections(rawTransaction, opts);
38
40
  let done = false;
39
41
  const resolveReference = {
40
42
  resolve: undefined,
@@ -55,6 +57,7 @@ class RetryTxSender {
55
57
  console.error(e);
56
58
  stopWaiting();
57
59
  });
60
+ this.sendToAdditionalConnections(rawTransaction, opts);
58
61
  }
59
62
  }
60
63
  }))();
@@ -149,5 +152,15 @@ class RetryTxSender {
149
152
  return result;
150
153
  });
151
154
  }
155
+ sendToAdditionalConnections(rawTx, opts) {
156
+ this.additionalConnections.map((connection) => {
157
+ connection.sendRawTransaction(rawTx, opts).catch((e) => {
158
+ console.error(
159
+ // @ts-ignore
160
+ `error sending tx to additional connection ${connection._rpcEndpoint}`);
161
+ console.error(e);
162
+ });
163
+ });
164
+ }
152
165
  }
153
166
  exports.RetryTxSender = RetryTxSender;
@@ -0,0 +1 @@
1
+ export declare function promiseTimeout<T>(promise: Promise<T>, timeoutMs: number): Promise<T | null>;
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.promiseTimeout = void 0;
4
+ function promiseTimeout(promise, timeoutMs) {
5
+ let timeoutId;
6
+ const timeoutPromise = new Promise((resolve) => {
7
+ timeoutId = setTimeout(() => resolve(null), timeoutMs);
8
+ });
9
+ return Promise.race([promise, timeoutPromise]).then((result) => {
10
+ clearTimeout(timeoutId);
11
+ return result;
12
+ });
13
+ }
14
+ exports.promiseTimeout = promiseTimeout;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drift-labs/sdk",
3
- "version": "0.1.29-master.1",
3
+ "version": "0.1.30-master.1",
4
4
  "main": "lib/index.js",
5
5
  "types": "lib/index.d.ts",
6
6
  "author": "crispheaney",
@@ -1,6 +1,7 @@
1
1
  import { Commitment, Connection, PublicKey } from '@solana/web3.js';
2
2
  import { v4 as uuidv4 } from 'uuid';
3
3
  import { AccountData } from './types';
4
+ import { promiseTimeout } from '../util/promiseTimeout';
4
5
 
5
6
  type AccountToLoad = {
6
7
  publicKey: PublicKey;
@@ -10,7 +11,6 @@ type AccountToLoad = {
10
11
  const GET_MULTIPLE_ACCOUNTS_CHUNK_SIZE = 99;
11
12
 
12
13
  const oneMinute = 60 * 1000;
13
- const fiveMinutes = 5 * 60 * 1000;
14
14
 
15
15
  export class BulkAccountLoader {
16
16
  connection: Connection;
@@ -24,7 +24,7 @@ export class BulkAccountLoader {
24
24
  loadPromise?: Promise<void>;
25
25
  loadPromiseResolver: () => void;
26
26
  loggingEnabled = false;
27
- lastUpdate = Date.now();
27
+ lastTimeLoadingPromiseCleared = Date.now();
28
28
 
29
29
  public constructor(
30
30
  connection: Connection,
@@ -43,10 +43,15 @@ export class BulkAccountLoader {
43
43
  const existingSize = this.accountsToLoad.size;
44
44
 
45
45
  const callbackId = uuidv4();
46
+ this.log(
47
+ `Adding account ${publicKey.toString()} callback id ${callbackId}`
48
+ );
46
49
  const existingAccountToLoad = this.accountsToLoad.get(publicKey.toString());
47
50
  if (existingAccountToLoad) {
51
+ this.log(`account already exists`);
48
52
  existingAccountToLoad.callbacks.set(callbackId, callback);
49
53
  } else {
54
+ this.log(`account doesn't already exist. creating new callback map`);
50
55
  const callbacks = new Map<string, (buffer: Buffer) => void>();
51
56
  callbacks.set(callbackId, callback);
52
57
  const newAccountToLoad = {
@@ -67,6 +72,9 @@ export class BulkAccountLoader {
67
72
  }
68
73
 
69
74
  public removeAccount(publicKey: PublicKey, callbackId: string): void {
75
+ this.log(
76
+ `Removing account ${publicKey.toString()} callback id ${callbackId}`
77
+ );
70
78
  const existingAccountToLoad = this.accountsToLoad.get(publicKey.toString());
71
79
  if (existingAccountToLoad) {
72
80
  existingAccountToLoad.callbacks.delete(callbackId);
@@ -99,17 +107,29 @@ export class BulkAccountLoader {
99
107
 
100
108
  public async load(): Promise<void> {
101
109
  if (this.loadPromise) {
102
- return this.loadPromise;
110
+ const now = Date.now();
111
+ if (now - this.lastTimeLoadingPromiseCleared > oneMinute) {
112
+ this.log(`Load promise hasnt been clearing for one minute. Clearing.`);
113
+ this.loadPromise = undefined;
114
+ } else {
115
+ this.log(`Load promise exists. Returning early`);
116
+ return this.loadPromise;
117
+ }
103
118
  }
119
+
104
120
  this.loadPromise = new Promise((resolver) => {
105
121
  this.loadPromiseResolver = resolver;
106
122
  });
123
+ this.lastTimeLoadingPromiseCleared = Date.now();
124
+
125
+ this.log(`Loading`);
107
126
 
108
127
  try {
109
128
  const chunks = this.chunks(
110
129
  Array.from(this.accountsToLoad.values()),
111
130
  GET_MULTIPLE_ACCOUNTS_CHUNK_SIZE
112
131
  );
132
+ this.log(`${chunks.length} chunks`);
113
133
 
114
134
  await Promise.all(
115
135
  chunks.map((chunk) => {
@@ -122,28 +142,17 @@ export class BulkAccountLoader {
122
142
  for (const [_, callback] of this.errorCallbacks) {
123
143
  callback(e);
124
144
  }
145
+ this.log('finished error callbacks');
125
146
  } finally {
147
+ this.log(`resetting load promise`);
126
148
  this.loadPromiseResolver();
127
149
  this.loadPromise = undefined;
128
-
129
- const now = Date.now();
130
- if (now - this.lastUpdate > fiveMinutes) {
131
- if (this.loggingEnabled) {
132
- console.log(
133
- "Haven't seen updated account in five minutes. Bulk account loader creating new Connection Object"
134
- );
135
- }
136
- this.connection = new Connection(
137
- // @ts-ignore
138
- this.connection._rpcEndpoint,
139
- this.connection.commitment
140
- );
141
- }
142
150
  }
143
151
  }
144
152
 
145
153
  async loadChunk(accountsToLoad: AccountToLoad[]): Promise<void> {
146
154
  if (accountsToLoad.length === 0) {
155
+ this.log(`no accounts in chunk`);
147
156
  return;
148
157
  }
149
158
 
@@ -154,15 +163,15 @@ export class BulkAccountLoader {
154
163
  { commitment: this.commitment },
155
164
  ];
156
165
 
157
- // @ts-ignore
158
- const rpcResponse = await this.connection._rpcRequest(
159
- 'getMultipleAccounts',
160
- args
166
+ const rpcResponse: any | null = await promiseTimeout(
167
+ // @ts-ignore
168
+ this.connection._rpcRequest('getMultipleAccounts', args),
169
+ 10 * 1000 // 30 second timeout
161
170
  );
162
171
 
163
- const oneMinuteSinceLastUpdate = Date.now() - this.lastUpdate > oneMinute;
164
- if (this.loggingEnabled && oneMinuteSinceLastUpdate) {
165
- console.log('rpcResponse', JSON.stringify(rpcResponse));
172
+ if (rpcResponse === null) {
173
+ this.log('request to rpc timed out', true);
174
+ return;
166
175
  }
167
176
 
168
177
  const newSlot = rpcResponse.result.context.slot;
@@ -179,44 +188,44 @@ export class BulkAccountLoader {
179
188
  newBuffer = Buffer.from(raw, dataType);
180
189
  }
181
190
 
182
- if (this.loggingEnabled && oneMinuteSinceLastUpdate) {
183
- console.log('oldRPCResponse', oldRPCResponse);
184
- }
185
-
186
191
  if (!oldRPCResponse) {
192
+ this.log('No old rpc response, updating account data');
187
193
  this.accountData.set(key, {
188
194
  slot: newSlot,
189
195
  buffer: newBuffer,
190
196
  });
191
197
  this.handleAccountCallbacks(accountToLoad, newBuffer);
192
- this.lastUpdate = Date.now();
193
198
  continue;
194
199
  }
195
200
 
196
201
  if (newSlot <= oldRPCResponse.slot) {
202
+ this.log(`new slot ${newSlot} old slot ${oldRPCResponse.slot}`);
197
203
  continue;
198
204
  }
199
205
 
200
206
  const oldBuffer = oldRPCResponse.buffer;
201
207
  if (newBuffer && (!oldBuffer || !newBuffer.equals(oldBuffer))) {
208
+ this.log('new buffer, updating account data');
202
209
  this.accountData.set(key, {
203
210
  slot: newSlot,
204
211
  buffer: newBuffer,
205
212
  });
206
213
  this.handleAccountCallbacks(accountToLoad, newBuffer);
207
- this.lastUpdate = Date.now();
208
- } else if (this.loggingEnabled) {
209
- console.log('unable to update account for newest slot');
210
- console.log('oldBuffer', oldBuffer);
211
- console.log('newBuffer', newBuffer);
214
+ } else {
215
+ this.log('unable to update account for newest slot');
216
+ this.log('oldBuffer ' + oldBuffer);
217
+ this.log('newBuffer ' + newBuffer);
218
+ this.log('buffers equal ' + newBuffer.equals(oldBuffer));
212
219
  }
213
220
  }
214
221
  }
215
222
 
216
223
  handleAccountCallbacks(accountToLoad: AccountToLoad, buffer: Buffer): void {
224
+ this.log('handling account callbacks');
217
225
  for (const [_, callback] of accountToLoad.callbacks) {
218
226
  callback(buffer);
219
227
  }
228
+ this.log('finished account callbacks');
220
229
  }
221
230
 
222
231
  public getAccountData(publicKey: PublicKey): Buffer | undefined {
@@ -229,9 +238,7 @@ export class BulkAccountLoader {
229
238
  return;
230
239
  }
231
240
 
232
- if (this.loggingEnabled) {
233
- console.log(`startPolling`);
234
- }
241
+ this.log('startPolling');
235
242
 
236
243
  this.intervalId = setInterval(this.load.bind(this), this.pollingFrequency);
237
244
  }
@@ -241,9 +248,13 @@ export class BulkAccountLoader {
241
248
  clearInterval(this.intervalId);
242
249
  this.intervalId = undefined;
243
250
 
244
- if (this.loggingEnabled) {
245
- console.log(`stopPolling`);
246
- }
251
+ this.log('stopPolling');
252
+ }
253
+ }
254
+
255
+ public log(msg: string, force = false): void {
256
+ if (this.loggingEnabled || force) {
257
+ console.log(msg);
247
258
  }
248
259
  }
249
260
  }