@drift-labs/sdk 0.1.22 → 0.1.23-master.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/lib/accounts/bulkAccountLoader.js +2 -2
  2. package/lib/accounts/bulkUserSubscription.js +39 -1
  3. package/lib/accounts/pollingClearingHouseAccountSubscriber.js +3 -3
  4. package/lib/accounts/pollingTokenAccountSubscriber.js +2 -2
  5. package/lib/accounts/pollingUserAccountSubscriber.d.ts +2 -2
  6. package/lib/accounts/pollingUserAccountSubscriber.js +39 -18
  7. package/lib/accounts/types.d.ts +5 -0
  8. package/lib/accounts/webSocketAccountSubscriber.js +2 -2
  9. package/lib/accounts/webSocketClearingHouseAccountSubscriber.js +1 -1
  10. package/lib/accounts/webSocketUserAccountSubscriber.js +2 -2
  11. package/lib/admin.js +7 -7
  12. package/lib/clearingHouse.js +21 -22
  13. package/lib/clearingHouseUser.js +21 -21
  14. package/lib/examples/makeTradeExample.js +6 -6
  15. package/lib/idl/clearing_house.json +68 -0
  16. package/lib/index.d.ts +2 -0
  17. package/lib/index.js +2 -0
  18. package/lib/math/amm.js +12 -12
  19. package/lib/math/conversion.js +1 -1
  20. package/lib/math/funding.js +1 -1
  21. package/lib/math/market.js +2 -2
  22. package/lib/math/orders.d.ts +1 -0
  23. package/lib/math/orders.js +26 -4
  24. package/lib/math/position.js +4 -3
  25. package/lib/math/trade.js +21 -17
  26. package/lib/orders.d.ts +3 -1
  27. package/lib/orders.js +47 -19
  28. package/lib/pythClient.js +1 -1
  29. package/lib/tx/retryTxSender.d.ts +19 -0
  30. package/lib/tx/retryTxSender.js +153 -0
  31. package/lib/tx/types.d.ts +2 -0
  32. package/package.json +1 -1
  33. package/src/accounts/bulkUserSubscription.ts +51 -1
  34. package/src/accounts/pollingUserAccountSubscriber.ts +49 -30
  35. package/src/accounts/types.ts +6 -0
  36. package/src/clearingHouse.ts +3 -3
  37. package/src/idl/clearing_house.json +68 -0
  38. package/src/index.ts +2 -0
  39. package/src/math/orders.ts +33 -0
  40. package/src/math/position.ts +3 -2
  41. package/src/math/trade.ts +8 -3
  42. package/src/orders.ts +56 -3
  43. package/src/tx/retryTxSender.ts +196 -0
  44. package/src/tx/types.ts +3 -0
package/lib/orders.js CHANGED
@@ -1,26 +1,27 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.calculateAmountToTradeForTriggerLimit = exports.calculateAmountToTradeForLimit = exports.calculateNewStateAfterOrder = void 0;
3
+ exports.calculateBaseAssetAmountUserCanExecute = exports.calculateAmountToTradeForTriggerLimit = exports.calculateAmountToTradeForLimit = exports.calculateBaseAssetAmountMarketCanExecute = exports.calculateNewStateAfterOrder = void 0;
4
4
  const types_1 = require("./types");
5
+ const _1 = require(".");
5
6
  const market_1 = require("./math/market");
6
7
  const numericConstants_1 = require("./constants/numericConstants");
7
8
  const amm_1 = require("./math/amm");
8
9
  const position_1 = require("./math/position");
9
10
  function calculateNewStateAfterOrder(userAccount, userPosition, market, order) {
10
- if (types_1.isVariant(order.status, 'init')) {
11
+ if ((0, types_1.isVariant)(order.status, 'init')) {
11
12
  return null;
12
13
  }
13
- const baseAssetAmountToTrade = calculateAmountToTrade(market, order);
14
+ const baseAssetAmountToTrade = calculateBaseAssetAmountMarketCanExecute(market, order);
14
15
  if (baseAssetAmountToTrade.lt(market.amm.minimumBaseAssetTradeSize)) {
15
16
  return null;
16
17
  }
17
18
  const userAccountAfter = Object.assign({}, userAccount);
18
19
  const userPositionAfter = Object.assign({}, userPosition);
19
- const currentPositionDirection = position_1.positionCurrentDirection(userPosition);
20
+ const currentPositionDirection = (0, position_1.positionCurrentDirection)(userPosition);
20
21
  const increasePosition = userPosition.baseAssetAmount.eq(numericConstants_1.ZERO) ||
21
22
  isSameDirection(order.direction, currentPositionDirection);
22
23
  if (increasePosition) {
23
- const marketAfter = market_1.calculateNewMarketAfterTrade(baseAssetAmountToTrade, order.direction, market);
24
+ const marketAfter = (0, market_1.calculateNewMarketAfterTrade)(baseAssetAmountToTrade, order.direction, market);
24
25
  const { quoteAssetAmountSwapped, baseAssetAmountSwapped } = calculateAmountSwapped(market, marketAfter);
25
26
  userPositionAfter.baseAssetAmount = userPositionAfter.baseAssetAmount.add(baseAssetAmountSwapped);
26
27
  userPositionAfter.quoteAssetAmount = userPositionAfter.quoteAssetAmount.add(quoteAssetAmountSwapped);
@@ -29,10 +30,10 @@ function calculateNewStateAfterOrder(userAccount, userPosition, market, order) {
29
30
  else {
30
31
  const reversePosition = baseAssetAmountToTrade.gt(userPosition.baseAssetAmount.abs());
31
32
  if (reversePosition) {
32
- const intermediateMarket = market_1.calculateNewMarketAfterTrade(userPosition.baseAssetAmount, position_1.findDirectionToClose(userPosition), market);
33
+ const intermediateMarket = (0, market_1.calculateNewMarketAfterTrade)(userPosition.baseAssetAmount, (0, position_1.findDirectionToClose)(userPosition), market);
33
34
  const { quoteAssetAmountSwapped: baseAssetValue } = calculateAmountSwapped(market, intermediateMarket);
34
35
  let pnl;
35
- if (types_1.isVariant(currentPositionDirection, 'long')) {
36
+ if ((0, types_1.isVariant)(currentPositionDirection, 'long')) {
36
37
  pnl = baseAssetValue.sub(userPosition.quoteAssetAmount);
37
38
  }
38
39
  else {
@@ -40,20 +41,20 @@ function calculateNewStateAfterOrder(userAccount, userPosition, market, order) {
40
41
  }
41
42
  userAccountAfter.collateral = userAccountAfter.collateral.add(pnl);
42
43
  const baseAssetAmountLeft = baseAssetAmountToTrade.sub(userPosition.baseAssetAmount.abs());
43
- const marketAfter = market_1.calculateNewMarketAfterTrade(baseAssetAmountLeft, order.direction, intermediateMarket);
44
+ const marketAfter = (0, market_1.calculateNewMarketAfterTrade)(baseAssetAmountLeft, order.direction, intermediateMarket);
44
45
  const { quoteAssetAmountSwapped, baseAssetAmountSwapped } = calculateAmountSwapped(intermediateMarket, marketAfter);
45
46
  userPositionAfter.quoteAssetAmount = quoteAssetAmountSwapped;
46
47
  userPositionAfter.baseAssetAmount = baseAssetAmountSwapped;
47
48
  return [userAccountAfter, userPositionAfter, marketAfter];
48
49
  }
49
50
  else {
50
- const marketAfter = market_1.calculateNewMarketAfterTrade(baseAssetAmountToTrade, order.direction, market);
51
+ const marketAfter = (0, market_1.calculateNewMarketAfterTrade)(baseAssetAmountToTrade, order.direction, market);
51
52
  const { quoteAssetAmountSwapped: baseAssetValue, baseAssetAmountSwapped, } = calculateAmountSwapped(market, marketAfter);
52
53
  const costBasisRealized = userPosition.quoteAssetAmount
53
54
  .mul(baseAssetAmountSwapped.abs())
54
55
  .div(userPosition.baseAssetAmount.abs());
55
56
  let pnl;
56
- if (types_1.isVariant(currentPositionDirection, 'long')) {
57
+ if ((0, types_1.isVariant)(currentPositionDirection, 'long')) {
57
58
  pnl = baseAssetValue.sub(costBasisRealized);
58
59
  }
59
60
  else {
@@ -79,14 +80,14 @@ function calculateAmountSwapped(marketBefore, marketAfter) {
79
80
  baseAssetAmountSwapped: marketBefore.amm.baseAssetReserve.sub(marketAfter.amm.baseAssetReserve),
80
81
  };
81
82
  }
82
- function calculateAmountToTrade(market, order) {
83
- if (types_1.isVariant(order.orderType, 'limit')) {
83
+ function calculateBaseAssetAmountMarketCanExecute(market, order) {
84
+ if ((0, types_1.isVariant)(order.orderType, 'limit')) {
84
85
  return calculateAmountToTradeForLimit(market, order);
85
86
  }
86
- else if (types_1.isVariant(order.orderType, 'triggerLimit')) {
87
+ else if ((0, types_1.isVariant)(order.orderType, 'triggerLimit')) {
87
88
  return calculateAmountToTradeForTriggerLimit(market, order);
88
89
  }
89
- else if (types_1.isVariant(order.orderType, 'market')) {
90
+ else if ((0, types_1.isVariant)(order.orderType, 'market')) {
90
91
  // should never be a market order queued
91
92
  return numericConstants_1.ZERO;
92
93
  }
@@ -94,8 +95,9 @@ function calculateAmountToTrade(market, order) {
94
95
  return calculateAmountToTradeForTriggerMarket(market, order);
95
96
  }
96
97
  }
98
+ exports.calculateBaseAssetAmountMarketCanExecute = calculateBaseAssetAmountMarketCanExecute;
97
99
  function calculateAmountToTradeForLimit(market, order) {
98
- const [maxAmountToTrade, direction] = amm_1.calculateMaxBaseAssetAmountToTrade(market.amm, order.price);
100
+ const [maxAmountToTrade, direction] = (0, amm_1.calculateMaxBaseAssetAmountToTrade)(market.amm, order.price);
99
101
  // Check that directions are the same
100
102
  const sameDirection = isSameDirection(direction, order.direction);
101
103
  if (!sameDirection) {
@@ -117,8 +119,8 @@ function calculateAmountToTradeForTriggerLimit(market, order) {
117
119
  }
118
120
  exports.calculateAmountToTradeForTriggerLimit = calculateAmountToTradeForTriggerLimit;
119
121
  function isSameDirection(firstDirection, secondDirection) {
120
- return ((types_1.isVariant(firstDirection, 'long') && types_1.isVariant(secondDirection, 'long')) ||
121
- (types_1.isVariant(firstDirection, 'short') && types_1.isVariant(secondDirection, 'short')));
122
+ return (((0, types_1.isVariant)(firstDirection, 'long') && (0, types_1.isVariant)(secondDirection, 'long')) ||
123
+ ((0, types_1.isVariant)(firstDirection, 'short') && (0, types_1.isVariant)(secondDirection, 'short')));
122
124
  }
123
125
  function calculateAmountToTradeForTriggerMarket(market, order) {
124
126
  return isTriggerConditionSatisfied(market, order)
@@ -126,11 +128,37 @@ function calculateAmountToTradeForTriggerMarket(market, order) {
126
128
  : numericConstants_1.ZERO;
127
129
  }
128
130
  function isTriggerConditionSatisfied(market, order) {
129
- const markPrice = market_1.calculateMarkPrice(market);
130
- if (types_1.isVariant(order.triggerCondition, 'above')) {
131
+ const markPrice = (0, market_1.calculateMarkPrice)(market);
132
+ if ((0, types_1.isVariant)(order.triggerCondition, 'above')) {
131
133
  return markPrice.gt(order.triggerPrice);
132
134
  }
133
135
  else {
134
136
  return markPrice.lt(order.triggerPrice);
135
137
  }
136
138
  }
139
+ function calculateBaseAssetAmountUserCanExecute(market, order, user) {
140
+ const maxLeverage = user.getMaxLeverage('Initial');
141
+ const freeCollateral = user.getFreeCollateral();
142
+ let quoteAssetAmount;
143
+ if ((0, _1.isOrderRiskIncreasingInSameDirection)(user, order)) {
144
+ quoteAssetAmount = freeCollateral.mul(maxLeverage).div(_1.TEN_THOUSAND);
145
+ }
146
+ else {
147
+ const position = user.getUserPosition(order.marketIndex) ||
148
+ user.getEmptyPosition(order.marketIndex);
149
+ const positionValue = (0, _1.calculateBaseAssetValue)(market, position);
150
+ quoteAssetAmount = freeCollateral
151
+ .mul(maxLeverage)
152
+ .div(_1.TEN_THOUSAND)
153
+ .add(positionValue.mul(numericConstants_1.TWO));
154
+ }
155
+ if (quoteAssetAmount.lte(numericConstants_1.ZERO)) {
156
+ return numericConstants_1.ZERO;
157
+ }
158
+ const baseAssetReservesBefore = market.amm.baseAssetReserve;
159
+ const [_, baseAssetReservesAfter] = (0, _1.calculateAmmReservesAfterSwap)(market.amm, 'quote', quoteAssetAmount, (0, types_1.isVariant)(order.direction, 'long')
160
+ ? types_1.SwapDirection.ADD
161
+ : types_1.SwapDirection.REMOVE);
162
+ return baseAssetReservesBefore.sub(baseAssetReservesAfter).abs();
163
+ }
164
+ exports.calculateBaseAssetAmountUserCanExecute = calculateBaseAssetAmountUserCanExecute;
package/lib/pythClient.js CHANGED
@@ -18,7 +18,7 @@ class PythClient {
18
18
  getPriceData(pricePublicKey) {
19
19
  return __awaiter(this, void 0, void 0, function* () {
20
20
  const account = yield this.connection.getAccountInfo(pricePublicKey);
21
- return client_1.parsePriceData(account.data);
21
+ return (0, client_1.parsePriceData)(account.data);
22
22
  });
23
23
  }
24
24
  }
@@ -0,0 +1,19 @@
1
+ import { TxSender } from './types';
2
+ import { Commitment, ConfirmOptions, RpcResponseAndContext, Signer, SignatureResult, Transaction, TransactionSignature } from '@solana/web3.js';
3
+ import { Provider } from '@project-serum/anchor';
4
+ declare type ResolveReference = {
5
+ resolve?: () => void;
6
+ };
7
+ export declare class RetryTxSender implements TxSender {
8
+ provider: Provider;
9
+ timeout: number;
10
+ retrySleep: number;
11
+ constructor(provider: Provider, timeout?: number, retrySleep?: number);
12
+ send(tx: Transaction, additionalSigners?: Array<Signer>, opts?: ConfirmOptions): Promise<TransactionSignature>;
13
+ prepareTx(tx: Transaction, additionalSigners: Array<Signer>, opts: ConfirmOptions): Promise<Transaction>;
14
+ confirmTransaction(signature: TransactionSignature, commitment?: Commitment): Promise<RpcResponseAndContext<SignatureResult>>;
15
+ getTimestamp(): number;
16
+ sleep(reference: ResolveReference): Promise<void>;
17
+ promiseTimeout<T>(promise: Promise<T>, timeoutMs: number): Promise<T | null>;
18
+ }
19
+ export {};
@@ -0,0 +1,153 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.RetryTxSender = void 0;
16
+ const assert_1 = __importDefault(require("assert"));
17
+ const bs58_1 = __importDefault(require("bs58"));
18
+ const DEFAULT_TIMEOUT = 35000;
19
+ const DEFAULT_RETRY = 8000;
20
+ class RetryTxSender {
21
+ constructor(provider, timeout, retrySleep) {
22
+ this.provider = provider;
23
+ this.timeout = timeout !== null && timeout !== void 0 ? timeout : DEFAULT_TIMEOUT;
24
+ this.retrySleep = retrySleep !== null && retrySleep !== void 0 ? retrySleep : DEFAULT_RETRY;
25
+ }
26
+ send(tx, additionalSigners, opts) {
27
+ return __awaiter(this, void 0, void 0, function* () {
28
+ if (additionalSigners === undefined) {
29
+ additionalSigners = [];
30
+ }
31
+ if (opts === undefined) {
32
+ opts = this.provider.opts;
33
+ }
34
+ yield this.prepareTx(tx, additionalSigners, opts);
35
+ const rawTransaction = tx.serialize();
36
+ const startTime = this.getTimestamp();
37
+ const txid = yield this.provider.connection.sendRawTransaction(rawTransaction, opts);
38
+ let done = false;
39
+ const resolveReference = {
40
+ resolve: undefined,
41
+ };
42
+ const stopWaiting = () => {
43
+ done = true;
44
+ if (resolveReference.resolve) {
45
+ resolveReference.resolve();
46
+ }
47
+ };
48
+ (() => __awaiter(this, void 0, void 0, function* () {
49
+ while (!done && this.getTimestamp() - startTime < this.timeout) {
50
+ yield this.sleep(resolveReference);
51
+ if (!done) {
52
+ this.provider.connection
53
+ .sendRawTransaction(rawTransaction, opts)
54
+ .catch((e) => {
55
+ console.error(e);
56
+ stopWaiting();
57
+ });
58
+ }
59
+ }
60
+ }))();
61
+ try {
62
+ yield this.confirmTransaction(txid, opts.commitment);
63
+ }
64
+ catch (e) {
65
+ console.error(e);
66
+ throw e;
67
+ }
68
+ finally {
69
+ stopWaiting();
70
+ }
71
+ return txid;
72
+ });
73
+ }
74
+ prepareTx(tx, additionalSigners, opts) {
75
+ return __awaiter(this, void 0, void 0, function* () {
76
+ tx.feePayer = this.provider.wallet.publicKey;
77
+ tx.recentBlockhash = (yield this.provider.connection.getRecentBlockhash(opts.preflightCommitment)).blockhash;
78
+ yield this.provider.wallet.signTransaction(tx);
79
+ additionalSigners
80
+ .filter((s) => s !== undefined)
81
+ .forEach((kp) => {
82
+ tx.partialSign(kp);
83
+ });
84
+ return tx;
85
+ });
86
+ }
87
+ confirmTransaction(signature, commitment) {
88
+ return __awaiter(this, void 0, void 0, function* () {
89
+ let decodedSignature;
90
+ try {
91
+ decodedSignature = bs58_1.default.decode(signature);
92
+ }
93
+ catch (err) {
94
+ throw new Error('signature must be base58 encoded: ' + signature);
95
+ }
96
+ (0, assert_1.default)(decodedSignature.length === 64, 'signature has invalid length');
97
+ const start = Date.now();
98
+ const subscriptionCommitment = commitment || this.provider.opts.commitment;
99
+ let subscriptionId;
100
+ let response = null;
101
+ const confirmPromise = new Promise((resolve, reject) => {
102
+ try {
103
+ subscriptionId = this.provider.connection.onSignature(signature, (result, context) => {
104
+ subscriptionId = undefined;
105
+ response = {
106
+ context,
107
+ value: result,
108
+ };
109
+ resolve(null);
110
+ }, subscriptionCommitment);
111
+ }
112
+ catch (err) {
113
+ reject(err);
114
+ }
115
+ });
116
+ try {
117
+ yield this.promiseTimeout(confirmPromise, this.timeout);
118
+ }
119
+ finally {
120
+ if (subscriptionId) {
121
+ this.provider.connection.removeSignatureListener(subscriptionId);
122
+ }
123
+ }
124
+ if (response === null) {
125
+ const duration = (Date.now() - start) / 1000;
126
+ throw new Error(`Transaction was not confirmed in ${duration.toFixed(2)} seconds. It is unknown if it succeeded or failed. Check signature ${signature} using the Solana Explorer or CLI tools.`);
127
+ }
128
+ return response;
129
+ });
130
+ }
131
+ getTimestamp() {
132
+ return new Date().getTime();
133
+ }
134
+ sleep(reference) {
135
+ return __awaiter(this, void 0, void 0, function* () {
136
+ return new Promise((resolve) => {
137
+ reference.resolve = resolve;
138
+ setTimeout(resolve, this.retrySleep);
139
+ });
140
+ });
141
+ }
142
+ promiseTimeout(promise, timeoutMs) {
143
+ let timeoutId;
144
+ const timeoutPromise = new Promise((resolve) => {
145
+ timeoutId = setTimeout(() => resolve(null), timeoutMs);
146
+ });
147
+ return Promise.race([promise, timeoutPromise]).then((result) => {
148
+ clearTimeout(timeoutId);
149
+ return result;
150
+ });
151
+ }
152
+ }
153
+ exports.RetryTxSender = RetryTxSender;
package/lib/tx/types.d.ts CHANGED
@@ -1,4 +1,6 @@
1
+ import { Provider } from '@project-serum/anchor';
1
2
  import { ConfirmOptions, Signer, Transaction, TransactionSignature } from '@solana/web3.js';
2
3
  export interface TxSender {
4
+ provider: Provider;
3
5
  send(tx: Transaction, additionalSigners?: Array<Signer>, opts?: ConfirmOptions): Promise<TransactionSignature>;
4
6
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drift-labs/sdk",
3
- "version": "0.1.22",
3
+ "version": "0.1.23-master.3",
4
4
  "main": "lib/index.js",
5
5
  "types": "lib/index.d.ts",
6
6
  "author": "crispheaney",
@@ -1,6 +1,9 @@
1
1
  import { ClearingHouseUser } from '../clearingHouseUser';
2
2
  import { BulkAccountLoader } from './bulkAccountLoader';
3
3
  import { PollingUserAccountSubscriber } from './pollingUserAccountSubscriber';
4
+ import { UserAccount, UserOrdersAccount } from '../types';
5
+ import { UserPublicKeys } from './types';
6
+ import { ProgramAccount } from '@project-serum/anchor';
4
7
 
5
8
  /**
6
9
  * @param users
@@ -10,11 +13,58 @@ export async function bulkPollingUserSubscribe(
10
13
  users: ClearingHouseUser[],
11
14
  accountLoader: BulkAccountLoader
12
15
  ): Promise<void> {
16
+ if (users.length === 0) {
17
+ await accountLoader.load();
18
+ return;
19
+ }
20
+
21
+ // Fetch all the accounts first
22
+ const program = users[0].clearingHouse.program;
23
+ let userProgramAccounts: ProgramAccount[];
24
+ let orderProgramAccounts: ProgramAccount[];
25
+ await Promise.all([
26
+ (async () => {
27
+ userProgramAccounts = await program.account.user.all();
28
+ })(),
29
+ (async () => {
30
+ orderProgramAccounts = await program.account.userOrders.all();
31
+ })(),
32
+ ]);
33
+
34
+ // Create a map of the authority to keys
35
+ const authorityToKeys = new Map<string, UserPublicKeys>();
36
+ const userToAuthority = new Map<string, string>();
37
+ for (const userProgramAccount of userProgramAccounts) {
38
+ const userAccountPublicKey = userProgramAccount.publicKey;
39
+ const userAccount = userProgramAccount.account as UserAccount;
40
+
41
+ authorityToKeys.set(userAccount.authority.toString(), {
42
+ user: userAccountPublicKey,
43
+ userPositions: userAccount.positions,
44
+ userOrders: undefined,
45
+ });
46
+
47
+ userToAuthority.set(
48
+ userAccountPublicKey.toString(),
49
+ userAccount.authority.toString()
50
+ );
51
+ }
52
+ for (const orderProgramAccount of orderProgramAccounts) {
53
+ const userOrderAccountPublicKey = orderProgramAccount.publicKey;
54
+ const userOrderAccount = orderProgramAccount.account as UserOrdersAccount;
55
+
56
+ const authority = userToAuthority.get(userOrderAccount.user.toString());
57
+ const userPublicKeys = authorityToKeys.get(authority);
58
+ userPublicKeys.userOrders = userOrderAccountPublicKey;
59
+ }
60
+
13
61
  await Promise.all(
14
62
  users.map((user) => {
63
+ // Pull the keys from the authority map so we can skip fetching them in addToAccountLoader
64
+ const userPublicKeys = authorityToKeys.get(user.authority.toString());
15
65
  return (
16
66
  user.accountSubscriber as PollingUserAccountSubscriber
17
- ).addToAccountLoader();
67
+ ).addToAccountLoader(userPublicKeys);
18
68
  })
19
69
  );
20
70
 
@@ -3,6 +3,7 @@ import {
3
3
  NotSubscribedError,
4
4
  UserAccountEvents,
5
5
  UserAccountSubscriber,
6
+ UserPublicKeys,
6
7
  } from './types';
7
8
  import { Program } from '@project-serum/anchor';
8
9
  import StrictEventEmitter from 'strict-event-emitter-types';
@@ -58,55 +59,73 @@ export class PollingUserAccountSubscriber implements UserAccountSubscriber {
58
59
  return true;
59
60
  }
60
61
 
61
- async addToAccountLoader(): Promise<void> {
62
+ async addToAccountLoader(userPublicKeys?: UserPublicKeys): Promise<void> {
62
63
  if (this.accountsToPoll.size > 0) {
63
64
  return;
64
65
  }
65
66
 
66
- const userPublicKey = await getUserAccountPublicKey(
67
- this.program.programId,
68
- this.authority
69
- );
67
+ if (!userPublicKeys) {
68
+ const userPublicKey = await getUserAccountPublicKey(
69
+ this.program.programId,
70
+ this.authority
71
+ );
70
72
 
71
- const userAccount = (await this.program.account.user.fetch(
72
- userPublicKey
73
- )) as UserAccount;
73
+ const userAccount = (await this.program.account.user.fetch(
74
+ userPublicKey
75
+ )) as UserAccount;
74
76
 
75
- this.accountsToPoll.set(userPublicKey.toString(), {
76
- key: 'user',
77
- publicKey: userPublicKey,
78
- eventType: 'userAccountData',
79
- });
77
+ this.accountsToPoll.set(userPublicKey.toString(), {
78
+ key: 'user',
79
+ publicKey: userPublicKey,
80
+ eventType: 'userAccountData',
81
+ });
80
82
 
81
- this.accountsToPoll.set(userAccount.positions.toString(), {
82
- key: 'userPositions',
83
- publicKey: userAccount.positions,
84
- eventType: 'userPositionsData',
85
- });
83
+ this.accountsToPoll.set(userAccount.positions.toString(), {
84
+ key: 'userPositions',
85
+ publicKey: userAccount.positions,
86
+ eventType: 'userPositionsData',
87
+ });
88
+
89
+ const userOrdersPublicKey = await getUserOrdersAccountPublicKey(
90
+ this.program.programId,
91
+ userPublicKey
92
+ );
86
93
 
87
- const userOrdersPublicKey = await getUserOrdersAccountPublicKey(
88
- this.program.programId,
89
- userPublicKey
90
- );
91
-
92
- const userOrdersExist =
93
- (
94
- await this.program.provider.connection.getParsedAccountInfo(
95
- userOrdersPublicKey
96
- )
97
- ).value !== null;
98
- if (userOrdersExist) {
99
94
  this.accountsToPoll.set(userOrdersPublicKey.toString(), {
100
95
  key: 'userOrders',
101
96
  publicKey: userOrdersPublicKey,
102
97
  eventType: 'userOrdersData',
103
98
  });
99
+ } else {
100
+ this.accountsToPoll.set(userPublicKeys.user.toString(), {
101
+ key: 'user',
102
+ publicKey: userPublicKeys.user,
103
+ eventType: 'userAccountData',
104
+ });
105
+
106
+ this.accountsToPoll.set(userPublicKeys.userPositions.toString(), {
107
+ key: 'userPositions',
108
+ publicKey: userPublicKeys.userPositions,
109
+ eventType: 'userPositionsData',
110
+ });
111
+
112
+ if (userPublicKeys.userOrders) {
113
+ this.accountsToPoll.set(userPublicKeys.userOrders.toString(), {
114
+ key: 'userOrders',
115
+ publicKey: userPublicKeys.userOrders,
116
+ eventType: 'userOrdersData',
117
+ });
118
+ }
104
119
  }
105
120
 
106
121
  for (const [_, accountToPoll] of this.accountsToPoll) {
107
122
  accountToPoll.callbackId = this.accountLoader.addAccount(
108
123
  accountToPoll.publicKey,
109
124
  (buffer) => {
125
+ if (!buffer) {
126
+ return;
127
+ }
128
+
110
129
  const account = this.program.account[
111
130
  accountToPoll.key
112
131
  ].coder.accounts.decode(capitalize(accountToPoll.key), buffer);
@@ -82,6 +82,12 @@ export interface ClearingHouseAccountSubscriber {
82
82
  type: ClearingHouseConfigType;
83
83
  }
84
84
 
85
+ export type UserPublicKeys = {
86
+ user: PublicKey;
87
+ userPositions: PublicKey;
88
+ userOrders: PublicKey | undefined;
89
+ };
90
+
85
91
  export interface UserAccountEvents {
86
92
  userAccountData: (payload: UserAccount) => void;
87
93
  userPositionsData: (payload: UserPositionsAccount) => void;
@@ -52,7 +52,6 @@ import {
52
52
  ClearingHouseAccountTypes,
53
53
  } from './accounts/types';
54
54
  import { TxSender } from './tx/types';
55
- import { DefaultTxSender } from './tx/defaultTxSender';
56
55
  import { wrapInTx } from './tx/utils';
57
56
  import {
58
57
  getClearingHouse,
@@ -250,12 +249,13 @@ export class ClearingHouse {
250
249
  this.program.programId,
251
250
  newProvider
252
251
  );
253
- const newTxSender = new DefaultTxSender(newProvider);
252
+
253
+ // Update provider for txSender with new wallet details
254
+ this.txSender.provider = newProvider;
254
255
 
255
256
  this.wallet = newWallet;
256
257
  this.provider = newProvider;
257
258
  this.program = newProgram;
258
- this.txSender = newTxSender;
259
259
  this.userAccountPublicKey = undefined;
260
260
  this.userAccount = undefined;
261
261
  }
@@ -1321,6 +1321,52 @@
1321
1321
  }
1322
1322
  ]
1323
1323
  },
1324
+ {
1325
+ "name": "initializeUserOrdersWithExplicitPayer",
1326
+ "accounts": [
1327
+ {
1328
+ "name": "user",
1329
+ "isMut": false,
1330
+ "isSigner": false
1331
+ },
1332
+ {
1333
+ "name": "userOrders",
1334
+ "isMut": true,
1335
+ "isSigner": false
1336
+ },
1337
+ {
1338
+ "name": "state",
1339
+ "isMut": false,
1340
+ "isSigner": false
1341
+ },
1342
+ {
1343
+ "name": "authority",
1344
+ "isMut": false,
1345
+ "isSigner": true
1346
+ },
1347
+ {
1348
+ "name": "payer",
1349
+ "isMut": true,
1350
+ "isSigner": true
1351
+ },
1352
+ {
1353
+ "name": "rent",
1354
+ "isMut": false,
1355
+ "isSigner": false
1356
+ },
1357
+ {
1358
+ "name": "systemProgram",
1359
+ "isMut": false,
1360
+ "isSigner": false
1361
+ }
1362
+ ],
1363
+ "args": [
1364
+ {
1365
+ "name": "userOrdersNonce",
1366
+ "type": "u8"
1367
+ }
1368
+ ]
1369
+ },
1324
1370
  {
1325
1371
  "name": "deleteUser",
1326
1372
  "accounts": [
@@ -3667,6 +3713,23 @@
3667
3713
  ]
3668
3714
  }
3669
3715
  },
3716
+ {
3717
+ "name": "LiquidationType",
3718
+ "type": {
3719
+ "kind": "enum",
3720
+ "variants": [
3721
+ {
3722
+ "name": "NONE"
3723
+ },
3724
+ {
3725
+ "name": "PARTIAL"
3726
+ },
3727
+ {
3728
+ "name": "FULL"
3729
+ }
3730
+ ]
3731
+ }
3732
+ },
3670
3733
  {
3671
3734
  "name": "OracleSource",
3672
3735
  "type": {
@@ -4064,6 +4127,11 @@
4064
4127
  "code": 6055,
4065
4128
  "name": "UserOrderIdAlreadyInUse",
4066
4129
  "msg": "User Order Id Already In Use"
4130
+ },
4131
+ {
4132
+ "code": 6056,
4133
+ "name": "NoPositionsLiquidatable",
4134
+ "msg": "No positions liquidatable"
4067
4135
  }
4068
4136
  ]
4069
4137
  }
package/src/index.ts CHANGED
@@ -7,6 +7,7 @@ export * from './types';
7
7
  export * from './constants/markets';
8
8
  export * from './accounts/webSocketClearingHouseAccountSubscriber';
9
9
  export * from './accounts/bulkAccountLoader';
10
+ export * from './accounts/bulkUserSubscription';
10
11
  export * from './accounts/pollingClearingHouseAccountSubscriber';
11
12
  export * from './accounts/pollingTokenAccountSubscriber';
12
13
  export * from './accounts/types';
@@ -31,6 +32,7 @@ export * from './types';
31
32
  export * from './math/utils';
32
33
  export * from './config';
33
34
  export * from './constants/numericConstants';
35
+ export * from './tx/retryTxSender';
34
36
  export * from './util/computeUnits';
35
37
  export * from './util/tps';
36
38