@drift-labs/sdk 0.2.0-master.2 → 0.2.0-master.22

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 (149) hide show
  1. package/lib/accounts/bulkUserStatsSubscription.d.ts +7 -0
  2. package/lib/accounts/bulkUserStatsSubscription.js +21 -0
  3. package/lib/accounts/bulkUserSubscription.js +0 -1
  4. package/lib/accounts/fetch.d.ts +2 -1
  5. package/lib/accounts/fetch.js +9 -1
  6. package/lib/accounts/pollingUserStatsAccountSubscriber.d.ts +27 -0
  7. package/lib/accounts/pollingUserStatsAccountSubscriber.js +113 -0
  8. package/lib/accounts/types.d.ts +14 -1
  9. package/lib/accounts/webSocketUserStatsAccountSubsriber.d.ts +20 -0
  10. package/lib/accounts/webSocketUserStatsAccountSubsriber.js +47 -0
  11. package/lib/addresses/pda.d.ts +1 -0
  12. package/lib/addresses/pda.js +8 -1
  13. package/lib/admin.d.ts +9 -5
  14. package/lib/admin.js +52 -11
  15. package/lib/clearingHouse.d.ts +52 -23
  16. package/lib/clearingHouse.js +736 -200
  17. package/lib/clearingHouseConfig.d.ts +1 -0
  18. package/lib/clearingHouseUser.d.ts +24 -16
  19. package/lib/clearingHouseUser.js +223 -101
  20. package/lib/clearingHouseUserStats.d.ts +18 -0
  21. package/lib/clearingHouseUserStats.js +49 -0
  22. package/lib/clearingHouseUserStatsConfig.d.ts +14 -0
  23. package/lib/clearingHouseUserStatsConfig.js +2 -0
  24. package/lib/config.js +1 -1
  25. package/lib/constants/banks.d.ts +2 -2
  26. package/lib/constants/banks.js +12 -4
  27. package/lib/constants/numericConstants.d.ts +5 -0
  28. package/lib/constants/numericConstants.js +8 -3
  29. package/lib/events/eventList.js +3 -0
  30. package/lib/events/types.d.ts +3 -1
  31. package/lib/events/types.js +2 -0
  32. package/lib/factory/bigNum.d.ts +1 -0
  33. package/lib/factory/bigNum.js +34 -10
  34. package/lib/idl/clearing_house.json +1603 -377
  35. package/lib/idl/{mock_usdc_faucet.json → token_faucet.json} +46 -23
  36. package/lib/index.d.ts +9 -3
  37. package/lib/index.js +13 -3
  38. package/lib/math/amm.d.ts +1 -0
  39. package/lib/math/amm.js +22 -38
  40. package/lib/math/auction.js +4 -1
  41. package/lib/math/bankBalance.d.ts +7 -1
  42. package/lib/math/bankBalance.js +77 -2
  43. package/lib/math/margin.d.ts +11 -0
  44. package/lib/math/margin.js +72 -0
  45. package/lib/math/market.d.ts +4 -1
  46. package/lib/math/market.js +35 -1
  47. package/lib/math/oracles.d.ts +3 -0
  48. package/lib/math/oracles.js +25 -5
  49. package/lib/math/orders.d.ts +6 -2
  50. package/lib/math/orders.js +62 -12
  51. package/lib/math/position.d.ts +8 -0
  52. package/lib/math/position.js +45 -12
  53. package/lib/math/trade.d.ts +1 -1
  54. package/lib/math/trade.js +7 -10
  55. package/lib/orderParams.d.ts +14 -5
  56. package/lib/orderParams.js +8 -96
  57. package/lib/slot/SlotSubscriber.d.ts +7 -0
  58. package/lib/slot/SlotSubscriber.js +3 -0
  59. package/lib/{mockUSDCFaucet.d.ts → tokenFaucet.d.ts} +8 -5
  60. package/lib/{mockUSDCFaucet.js → tokenFaucet.js} +63 -51
  61. package/lib/tx/retryTxSender.js +9 -2
  62. package/lib/tx/utils.js +1 -1
  63. package/lib/types.d.ts +236 -26
  64. package/lib/types.js +64 -1
  65. package/lib/util/computeUnits.js +1 -1
  66. package/lib/util/getTokenAddress.d.ts +2 -0
  67. package/lib/util/getTokenAddress.js +9 -0
  68. package/package.json +3 -3
  69. package/src/accounts/bulkUserStatsSubscription.ts +33 -0
  70. package/src/accounts/bulkUserSubscription.ts +0 -1
  71. package/src/accounts/fetch.ts +27 -2
  72. package/src/accounts/pollingUserStatsAccountSubscriber.ts +172 -0
  73. package/src/accounts/types.ts +18 -0
  74. package/src/accounts/webSocketUserStatsAccountSubsriber.ts +80 -0
  75. package/src/addresses/marketAddresses.js +26 -0
  76. package/src/addresses/pda.ts +13 -0
  77. package/src/admin.ts +82 -15
  78. package/src/assert/assert.js +9 -0
  79. package/src/clearingHouse.ts +1232 -323
  80. package/src/clearingHouseConfig.ts +1 -0
  81. package/src/clearingHouseUser.ts +343 -155
  82. package/src/clearingHouseUserStats.ts +75 -0
  83. package/src/clearingHouseUserStatsConfig.ts +18 -0
  84. package/src/config.ts +1 -1
  85. package/src/constants/banks.js +42 -0
  86. package/src/constants/banks.ts +14 -4
  87. package/src/constants/markets.js +42 -0
  88. package/src/constants/numericConstants.js +41 -0
  89. package/src/constants/numericConstants.ts +14 -2
  90. package/src/events/eventList.js +77 -0
  91. package/src/events/eventList.ts +3 -0
  92. package/src/events/eventSubscriber.js +139 -0
  93. package/src/events/sort.js +44 -0
  94. package/src/events/txEventCache.js +71 -0
  95. package/src/events/types.ts +6 -0
  96. package/src/examples/makeTradeExample.js +80 -0
  97. package/src/factory/bigNum.js +390 -0
  98. package/src/factory/bigNum.ts +42 -13
  99. package/src/factory/oracleClient.js +20 -0
  100. package/src/idl/clearing_house.json +1603 -377
  101. package/src/idl/{mock_usdc_faucet.json → token_faucet.json} +46 -23
  102. package/src/index.ts +9 -3
  103. package/src/math/amm.ts +54 -55
  104. package/src/math/auction.js +42 -0
  105. package/src/math/auction.ts +5 -1
  106. package/src/math/bankBalance.ts +148 -2
  107. package/src/math/conversion.js +11 -0
  108. package/src/math/funding.js +248 -0
  109. package/src/math/margin.ts +124 -0
  110. package/src/math/market.ts +66 -1
  111. package/src/math/oracles.js +26 -0
  112. package/src/math/oracles.ts +42 -5
  113. package/src/math/orders.ts +129 -13
  114. package/src/math/position.ts +64 -9
  115. package/src/math/repeg.js +128 -0
  116. package/src/math/state.js +15 -0
  117. package/src/math/trade.js +253 -0
  118. package/src/math/trade.ts +23 -25
  119. package/src/math/utils.js +0 -1
  120. package/src/oracles/oracleClientCache.js +19 -0
  121. package/src/oracles/pythClient.js +46 -0
  122. package/src/oracles/quoteAssetOracleClient.js +32 -0
  123. package/src/oracles/switchboardClient.js +69 -0
  124. package/src/oracles/types.js +2 -0
  125. package/src/orderParams.js +20 -0
  126. package/src/orderParams.ts +20 -141
  127. package/src/slot/SlotSubscriber.js +39 -0
  128. package/src/slot/SlotSubscriber.ts +11 -1
  129. package/src/token/index.js +38 -0
  130. package/src/tokenFaucet.js +189 -0
  131. package/src/{mockUSDCFaucet.ts → tokenFaucet.ts} +82 -70
  132. package/src/tx/retryTxSender.ts +11 -3
  133. package/src/tx/types.js +2 -0
  134. package/src/tx/utils.js +17 -0
  135. package/src/tx/utils.ts +1 -1
  136. package/src/types.ts +239 -27
  137. package/src/userName.js +20 -0
  138. package/src/util/computeUnits.js +21 -11
  139. package/src/util/computeUnits.ts +1 -1
  140. package/src/util/getTokenAddress.js +9 -0
  141. package/src/util/getTokenAddress.ts +18 -0
  142. package/src/util/promiseTimeout.js +14 -0
  143. package/src/util/tps.js +27 -0
  144. package/src/wallet.js +35 -0
  145. package/tests/bn/test.ts +10 -0
  146. package/lib/orders.d.ts +0 -8
  147. package/lib/orders.js +0 -142
  148. package/src/orders.ts +0 -251
  149. package/src/util/computeUnits.js.map +0 -1
@@ -29,6 +29,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
29
29
  exports.ClearingHouse = void 0;
30
30
  const anchor_1 = require("@project-serum/anchor");
31
31
  const spl_token_1 = require("@solana/spl-token");
32
+ const types_1 = require("./types");
32
33
  const anchor = __importStar(require("@project-serum/anchor"));
33
34
  const clearing_house_json_1 = __importDefault(require("./idl/clearing_house.json"));
34
35
  const web3_js_1 = require("@solana/web3.js");
@@ -42,8 +43,9 @@ const pollingClearingHouseAccountSubscriber_1 = require("./accounts/pollingClear
42
43
  const webSocketClearingHouseAccountSubscriber_1 = require("./accounts/webSocketClearingHouseAccountSubscriber");
43
44
  const retryTxSender_1 = require("./tx/retryTxSender");
44
45
  const clearingHouseUser_1 = require("./clearingHouseUser");
45
- const orderParams_1 = require("./orderParams");
46
46
  const config_1 = require("./config");
47
+ const banks_1 = require("./constants/banks");
48
+ const clearingHouseUserStats_1 = require("./clearingHouseUserStats");
47
49
  /**
48
50
  * # ClearingHouse
49
51
  * This class is the main way to interact with Drift Protocol. It allows you to subscribe to the various accounts where the Market's state is stored, as well as: opening positions, liquidating, settling funding, depositing & withdrawing, and more.
@@ -71,6 +73,13 @@ class ClearingHouse {
71
73
  type: 'websocket',
72
74
  };
73
75
  this.createUsers(userIds, this.userAccountSubscriptionConfig);
76
+ if (config.userStats) {
77
+ this.userStats = new clearingHouseUserStats_1.ClearingHouseUserStats({
78
+ clearingHouse: this,
79
+ userStatsAccountPublicKey: (0, pda_1.getUserStatsAccountPublicKey)(this.program.programId, this.wallet.publicKey),
80
+ accountSubscription: this.userAccountSubscriptionConfig,
81
+ });
82
+ }
74
83
  let marketIndexes = config.marketIndexes;
75
84
  let bankIndexes = config.bankIndexes;
76
85
  let oracleInfos = config.oracleInfos;
@@ -111,6 +120,9 @@ class ClearingHouse {
111
120
  }
112
121
  async subscribe() {
113
122
  const subscribePromises = this.subscribeUsers().concat(this.accountSubscriber.subscribe());
123
+ if (this.userStats !== undefined) {
124
+ subscribePromises.concat(this.userStats.subscribe());
125
+ }
114
126
  this.isSubscribed = (await Promise.all(subscribePromises)).reduce((success, prevSuccess) => success && prevSuccess);
115
127
  return this.isSubscribed;
116
128
  }
@@ -121,12 +133,19 @@ class ClearingHouse {
121
133
  * Forces the accountSubscriber to fetch account updates from rpc
122
134
  */
123
135
  async fetchAccounts() {
124
- await Promise.all([...this.users.values()]
136
+ const promises = [...this.users.values()]
125
137
  .map((user) => user.fetchAccounts())
126
- .concat(this.accountSubscriber.fetch()));
138
+ .concat(this.accountSubscriber.fetch());
139
+ if (this.userStats) {
140
+ promises.concat(this.userStats.fetchAccounts());
141
+ }
142
+ await Promise.all(promises);
127
143
  }
128
144
  async unsubscribe() {
129
145
  const unsubscribePromises = this.unsubscribeUsers().concat(this.accountSubscriber.unsubscribe());
146
+ if (this.userStats !== undefined) {
147
+ unsubscribePromises.concat(this.userStats.unsubscribe());
148
+ }
130
149
  await Promise.all(unsubscribePromises);
131
150
  this.isSubscribed = false;
132
151
  }
@@ -187,6 +206,7 @@ class ClearingHouse {
187
206
  await Promise.all(this.subscribeUsers());
188
207
  }
189
208
  this.activeUserId = activeUserId;
209
+ this.userStatsAccountPublicKey = undefined;
190
210
  }
191
211
  async switchActiveUser(userId) {
192
212
  this.activeUserId = userId;
@@ -199,27 +219,59 @@ class ClearingHouse {
199
219
  await user.subscribe();
200
220
  this.users.set(userId, user);
201
221
  }
202
- async initializeUserAccount(userId = 0, name = userName_1.DEFAULT_USER_NAME) {
203
- const [userAccountPublicKey, initializeUserAccountIx] = await this.getInitializeUserInstructions(userId, name);
204
- const tx = new web3_js_1.Transaction().add(initializeUserAccountIx);
222
+ async initializeUserAccount(userId = 0, name = userName_1.DEFAULT_USER_NAME, referrerInfo) {
223
+ const [userAccountPublicKey, initializeUserAccountIx] = await this.getInitializeUserInstructions(userId, name, referrerInfo);
224
+ const tx = new web3_js_1.Transaction();
225
+ if (userId === 0) {
226
+ // not the safest assumption, can explicitly check if user stats account exists if it causes problems
227
+ tx.add(await this.getInitializeUserStatsIx());
228
+ }
229
+ tx.add(initializeUserAccountIx);
205
230
  const { txSig } = await this.txSender.send(tx, [], this.opts);
206
231
  return [txSig, userAccountPublicKey];
207
232
  }
208
- async getInitializeUserInstructions(userId = 0, name = userName_1.DEFAULT_USER_NAME) {
233
+ async getInitializeUserInstructions(userId = 0, name = userName_1.DEFAULT_USER_NAME, referrerInfo) {
209
234
  const userAccountPublicKey = await (0, pda_1.getUserAccountPublicKey)(this.program.programId, this.wallet.publicKey, userId);
235
+ const remainingAccounts = new Array();
236
+ if (referrerInfo !== undefined) {
237
+ remainingAccounts.push({
238
+ pubkey: referrerInfo.referrer,
239
+ isWritable: true,
240
+ isSigner: false,
241
+ });
242
+ remainingAccounts.push({
243
+ pubkey: referrerInfo.referrerStats,
244
+ isWritable: true,
245
+ isSigner: false,
246
+ });
247
+ }
210
248
  const nameBuffer = (0, userName_1.encodeName)(name);
211
249
  const initializeUserAccountIx = await this.program.instruction.initializeUser(userId, nameBuffer, {
212
250
  accounts: {
213
251
  user: userAccountPublicKey,
252
+ userStats: this.getUserStatsAccountPublicKey(),
214
253
  authority: this.wallet.publicKey,
215
254
  payer: this.wallet.publicKey,
216
255
  rent: anchor.web3.SYSVAR_RENT_PUBKEY,
217
256
  systemProgram: anchor.web3.SystemProgram.programId,
218
257
  state: await this.getStatePublicKey(),
219
258
  },
259
+ remainingAccounts,
220
260
  });
221
261
  return [userAccountPublicKey, initializeUserAccountIx];
222
262
  }
263
+ async getInitializeUserStatsIx() {
264
+ return await this.program.instruction.initializeUserStats({
265
+ accounts: {
266
+ userStats: this.getUserStatsAccountPublicKey(),
267
+ authority: this.wallet.publicKey,
268
+ payer: this.wallet.publicKey,
269
+ rent: anchor.web3.SYSVAR_RENT_PUBKEY,
270
+ systemProgram: anchor.web3.SystemProgram.programId,
271
+ state: await this.getStatePublicKey(),
272
+ },
273
+ });
274
+ }
223
275
  getUser(userId) {
224
276
  userId = userId !== null && userId !== void 0 ? userId : this.activeUserId;
225
277
  if (!this.users.has(userId)) {
@@ -230,6 +282,16 @@ class ClearingHouse {
230
282
  getUsers() {
231
283
  return [...this.users.values()];
232
284
  }
285
+ getUserStats() {
286
+ return this.userStats;
287
+ }
288
+ getUserStatsAccountPublicKey() {
289
+ if (this.userStatsAccountPublicKey) {
290
+ return this.userStatsAccountPublicKey;
291
+ }
292
+ this.userStatsAccountPublicKey = (0, pda_1.getUserStatsAccountPublicKey)(this.program.programId, this.wallet.publicKey);
293
+ return this.userStatsAccountPublicKey;
294
+ }
233
295
  async getUserAccountPublicKey() {
234
296
  return this.getUser().userAccountPublicKey;
235
297
  }
@@ -284,8 +346,7 @@ class ClearingHouse {
284
346
  marketAccountMap.set(marketIndexNum, {
285
347
  pubkey: marketAccount.pubkey,
286
348
  isSigner: false,
287
- // isWritable: false, // TODO
288
- isWritable: true,
349
+ isWritable: false,
289
350
  });
290
351
  oracleAccountMap.set(marketAccount.pubkey.toString(), {
291
352
  pubkey: marketAccount.amm.oracle,
@@ -294,6 +355,19 @@ class ClearingHouse {
294
355
  });
295
356
  }
296
357
  }
358
+ if (params.readableMarketIndex) {
359
+ const marketAccount = this.getMarketAccount(params.readableMarketIndex.toNumber());
360
+ marketAccountMap.set(params.readableMarketIndex.toNumber(), {
361
+ pubkey: marketAccount.pubkey,
362
+ isSigner: false,
363
+ isWritable: true,
364
+ });
365
+ oracleAccountMap.set(marketAccount.amm.oracle.toString(), {
366
+ pubkey: marketAccount.amm.oracle,
367
+ isSigner: false,
368
+ isWritable: false,
369
+ });
370
+ }
297
371
  if (params.writableMarketIndex) {
298
372
  const marketAccount = this.getMarketAccount(params.writableMarketIndex.toNumber());
299
373
  marketAccountMap.set(params.writableMarketIndex.toNumber(), {
@@ -355,9 +429,27 @@ class ClearingHouse {
355
429
  return (_a = this.getUserAccount()) === null || _a === void 0 ? void 0 : _a.orders.find((order) => order.userOrderId === userOrderId);
356
430
  }
357
431
  async deposit(amount, bankIndex, collateralAccountPublicKey, userId, reduceOnly = false) {
432
+ const tx = new web3_js_1.Transaction();
433
+ const additionalSigners = [];
434
+ const bank = this.getBankAccount(bankIndex);
435
+ const isSolBank = bank.mint.equals(banks_1.WRAPPED_SOL_MINT);
436
+ const authority = this.wallet.publicKey;
437
+ const createWSOLTokenAccount = isSolBank && collateralAccountPublicKey.equals(authority);
438
+ if (createWSOLTokenAccount) {
439
+ const { ixs, signers, pubkey } = await this.getWrappedSolAccountCreationIxs(amount);
440
+ collateralAccountPublicKey = pubkey;
441
+ ixs.forEach((ix) => {
442
+ tx.add(ix);
443
+ });
444
+ signers.forEach((signer) => additionalSigners.push(signer));
445
+ }
358
446
  const depositCollateralIx = await this.getDepositInstruction(amount, bankIndex, collateralAccountPublicKey, userId, reduceOnly, true);
359
- const tx = new web3_js_1.Transaction().add(depositCollateralIx);
360
- const { txSig } = await this.txSender.send(tx);
447
+ tx.add(depositCollateralIx);
448
+ // Close the wrapped sol account at the end of the transaction
449
+ if (createWSOLTokenAccount) {
450
+ tx.add(spl_token_1.Token.createCloseAccountInstruction(spl_token_1.TOKEN_PROGRAM_ID, collateralAccountPublicKey, authority, authority, []));
451
+ }
452
+ const { txSig } = await this.txSender.send(tx, additionalSigners, this.opts);
361
453
  return txSig;
362
454
  }
363
455
  async getDepositInstruction(amount, bankIndex, userTokenAccount, userId, reduceOnly = false, userInitialized = true) {
@@ -371,13 +463,19 @@ class ClearingHouse {
371
463
  });
372
464
  }
373
465
  else {
374
- remainingAccounts = [
375
- {
376
- pubkey: this.getBankAccount(bankIndex).pubkey,
466
+ const bankAccount = this.getBankAccount(bankIndex);
467
+ if (!bankAccount.oracle.equals(web3_js_1.PublicKey.default)) {
468
+ remainingAccounts.push({
469
+ pubkey: bankAccount.oracle,
377
470
  isSigner: false,
378
- isWritable: true,
379
- },
380
- ];
471
+ isWritable: false,
472
+ });
473
+ }
474
+ remainingAccounts.push({
475
+ pubkey: bankAccount.pubkey,
476
+ isSigner: false,
477
+ isWritable: true,
478
+ });
381
479
  }
382
480
  const bank = this.getBankAccount(bankIndex);
383
481
  return await this.program.instruction.deposit(bankIndex, amount, reduceOnly, {
@@ -386,6 +484,7 @@ class ClearingHouse {
386
484
  bank: bank.pubkey,
387
485
  bankVault: bank.vault,
388
486
  user: userAccountPublicKey,
487
+ userStats: this.getUserStatsAccountPublicKey(),
389
488
  userTokenAccount: userTokenAccount,
390
489
  authority: this.wallet.publicKey,
391
490
  tokenProgram: spl_token_1.TOKEN_PROGRAM_ID,
@@ -393,40 +492,132 @@ class ClearingHouse {
393
492
  remainingAccounts,
394
493
  });
395
494
  }
495
+ async checkIfAccountExists(account) {
496
+ try {
497
+ const accountInfo = await this.connection.getAccountInfo(account);
498
+ return accountInfo && true;
499
+ }
500
+ catch (e) {
501
+ // Doesn't already exist
502
+ return false;
503
+ }
504
+ }
505
+ async getSolWithdrawalIxs(bankIndex, amount) {
506
+ const result = {
507
+ ixs: [],
508
+ signers: [],
509
+ pubkey: web3_js_1.PublicKey.default,
510
+ };
511
+ // Create a temporary wrapped SOL account to store the SOL that we're withdrawing
512
+ const authority = this.wallet.publicKey;
513
+ const { ixs, signers, pubkey } = await this.getWrappedSolAccountCreationIxs(amount);
514
+ result.pubkey = pubkey;
515
+ ixs.forEach((ix) => {
516
+ result.ixs.push(ix);
517
+ });
518
+ signers.forEach((ix) => {
519
+ result.signers.push(ix);
520
+ });
521
+ const withdrawIx = await this.getWithdrawIx(amount, bankIndex, pubkey, true);
522
+ result.ixs.push(withdrawIx);
523
+ result.ixs.push(spl_token_1.Token.createCloseAccountInstruction(spl_token_1.TOKEN_PROGRAM_ID, pubkey, authority, authority, []));
524
+ return result;
525
+ }
526
+ async getWrappedSolAccountCreationIxs(amount) {
527
+ const wrappedSolAccount = new web3_js_1.Keypair();
528
+ const result = {
529
+ ixs: [],
530
+ signers: [],
531
+ pubkey: wrappedSolAccount.publicKey,
532
+ };
533
+ const rentSpaceLamports = new anchor_1.BN(web3_js_1.LAMPORTS_PER_SOL / 100);
534
+ const depositAmountLamports = amount.add(rentSpaceLamports);
535
+ const authority = this.wallet.publicKey;
536
+ result.ixs.push(web3_js_1.SystemProgram.createAccount({
537
+ fromPubkey: authority,
538
+ newAccountPubkey: wrappedSolAccount.publicKey,
539
+ lamports: depositAmountLamports.toNumber(),
540
+ space: 165,
541
+ programId: spl_token_1.TOKEN_PROGRAM_ID,
542
+ }));
543
+ result.ixs.push(spl_token_1.Token.createInitAccountInstruction(spl_token_1.TOKEN_PROGRAM_ID, banks_1.WRAPPED_SOL_MINT, wrappedSolAccount.publicKey, authority));
544
+ result.signers.push(wrappedSolAccount);
545
+ return result;
546
+ }
396
547
  /**
397
548
  * Creates the Clearing House User account for a user, and deposits some initial collateral
398
- * @param userId
399
- * @param name
400
549
  * @param amount
401
550
  * @param userTokenAccount
551
+ * @param bankIndex
552
+ * @param userId
553
+ * @param name
402
554
  * @param fromUserId
403
555
  * @returns
404
556
  */
405
- async initializeUserAccountAndDepositCollateral(amount, userTokenAccount, bankIndex = new anchor_1.BN(0), userId = 0, name = userName_1.DEFAULT_USER_NAME, fromUserId) {
406
- const [userAccountPublicKey, initializeUserAccountIx] = await this.getInitializeUserInstructions(userId, name);
557
+ async initializeUserAccountAndDepositCollateral(amount, userTokenAccount, bankIndex = new anchor_1.BN(0), userId = 0, name = userName_1.DEFAULT_USER_NAME, fromUserId, referrerInfo) {
558
+ const [userAccountPublicKey, initializeUserAccountIx] = await this.getInitializeUserInstructions(userId, name, referrerInfo);
559
+ const additionalSigners = [];
560
+ const bank = this.getBankAccount(bankIndex);
561
+ const isSolBank = bank.mint.equals(banks_1.WRAPPED_SOL_MINT);
562
+ const tx = new web3_js_1.Transaction();
563
+ const authority = this.wallet.publicKey;
564
+ const createWSOLTokenAccount = isSolBank && userTokenAccount.equals(authority);
565
+ if (createWSOLTokenAccount) {
566
+ const { ixs: startIxs, signers, pubkey, } = await this.getWrappedSolAccountCreationIxs(amount);
567
+ userTokenAccount = pubkey;
568
+ startIxs.forEach((ix) => {
569
+ tx.add(ix);
570
+ });
571
+ signers.forEach((signer) => additionalSigners.push(signer));
572
+ }
407
573
  const depositCollateralIx = fromUserId != null
408
574
  ? await this.getTransferDepositIx(amount, bankIndex, fromUserId, userId)
409
575
  : await this.getDepositInstruction(amount, bankIndex, userTokenAccount, userId, false, false);
410
- const tx = new web3_js_1.Transaction()
411
- .add(initializeUserAccountIx)
412
- .add(depositCollateralIx);
413
- const { txSig } = await this.txSender.send(tx, []);
576
+ if (userId === 0) {
577
+ tx.add(await this.getInitializeUserStatsIx());
578
+ }
579
+ tx.add(initializeUserAccountIx).add(depositCollateralIx);
580
+ // Close the wrapped sol account at the end of the transaction
581
+ if (createWSOLTokenAccount) {
582
+ tx.add(spl_token_1.Token.createCloseAccountInstruction(spl_token_1.TOKEN_PROGRAM_ID, userTokenAccount, authority, authority, []));
583
+ }
584
+ const { txSig } = await this.txSender.send(tx, additionalSigners, this.opts);
414
585
  return [txSig, userAccountPublicKey];
415
586
  }
416
- async initializeUserAccountForDevnet(userId = 0, name = userName_1.DEFAULT_USER_NAME, mockUSDCFaucet, amount) {
417
- const [associateTokenPublicKey, createAssociatedAccountIx, mintToIx] = await mockUSDCFaucet.createAssociatedTokenAccountAndMintToInstructions(this.wallet.publicKey, amount);
418
- const [userAccountPublicKey, initializeUserAccountIx] = await this.getInitializeUserInstructions(userId, name);
419
- const depositCollateralIx = await this.getDepositInstruction(amount, new anchor_1.BN(0), associateTokenPublicKey, userId, false, false);
420
- const tx = new web3_js_1.Transaction()
421
- .add(createAssociatedAccountIx)
422
- .add(mintToIx)
423
- .add(initializeUserAccountIx)
424
- .add(depositCollateralIx);
587
+ async initializeUserAccountForDevnet(userId = 0, name = userName_1.DEFAULT_USER_NAME, bankIndex, tokenFaucet, amount, referrerInfo) {
588
+ const [associateTokenPublicKey, createAssociatedAccountIx, mintToIx] = await tokenFaucet.createAssociatedTokenAccountAndMintToInstructions(this.wallet.publicKey, amount);
589
+ const [userAccountPublicKey, initializeUserAccountIx] = await this.getInitializeUserInstructions(userId, name, referrerInfo);
590
+ const depositCollateralIx = await this.getDepositInstruction(amount, bankIndex, associateTokenPublicKey, userId, false, false);
591
+ const tx = new web3_js_1.Transaction().add(createAssociatedAccountIx).add(mintToIx);
592
+ if (userId === 0) {
593
+ tx.add(await this.getInitializeUserStatsIx());
594
+ }
595
+ tx.add(initializeUserAccountIx).add(depositCollateralIx);
425
596
  const txSig = await this.program.provider.sendAndConfirm(tx, []);
426
597
  return [txSig, userAccountPublicKey];
427
598
  }
428
599
  async withdraw(amount, bankIndex, userTokenAccount, reduceOnly = false) {
429
- const { txSig } = await this.txSender.send((0, utils_1.wrapInTx)(await this.getWithdrawIx(amount, bankIndex, userTokenAccount, reduceOnly)), [], this.opts);
600
+ const tx = new web3_js_1.Transaction();
601
+ const additionalSigners = [];
602
+ const bank = this.getBankAccount(bankIndex);
603
+ const isSolBank = bank.mint.equals(banks_1.WRAPPED_SOL_MINT);
604
+ const authority = this.wallet.publicKey;
605
+ const createWSOLTokenAccount = isSolBank && userTokenAccount.equals(authority);
606
+ if (createWSOLTokenAccount) {
607
+ const { ixs, signers, pubkey } = await this.getWrappedSolAccountCreationIxs(amount);
608
+ userTokenAccount = pubkey;
609
+ ixs.forEach((ix) => {
610
+ tx.add(ix);
611
+ });
612
+ signers.forEach((signer) => additionalSigners.push(signer));
613
+ }
614
+ const withdrawCollateral = await this.getWithdrawIx(amount, bank.bankIndex, userTokenAccount, reduceOnly);
615
+ tx.add(withdrawCollateral);
616
+ // Close the wrapped sol account at the end of the transaction
617
+ if (createWSOLTokenAccount) {
618
+ tx.add(spl_token_1.Token.createCloseAccountInstruction(spl_token_1.TOKEN_PROGRAM_ID, userTokenAccount, authority, authority, []));
619
+ }
620
+ const { txSig } = await this.txSender.send(tx, additionalSigners, this.opts);
430
621
  return txSig;
431
622
  }
432
623
  async getWithdrawIx(amount, bankIndex, userTokenAccount, reduceOnly = false) {
@@ -442,6 +633,7 @@ class ClearingHouse {
442
633
  bankVault: bank.vault,
443
634
  bankVaultAuthority: bank.vaultAuthority,
444
635
  user: userAccountPublicKey,
636
+ userStats: this.getUserStatsAccountPublicKey(),
445
637
  userTokenAccount: userTokenAccount,
446
638
  authority: this.wallet.publicKey,
447
639
  tokenProgram: spl_token_1.TOKEN_PROGRAM_ID,
@@ -464,6 +656,7 @@ class ClearingHouse {
464
656
  authority: this.wallet.publicKey,
465
657
  fromUser,
466
658
  toUser,
659
+ userStats: this.getUserStatsAccountPublicKey(),
467
660
  state: await this.getStatePublicKey(),
468
661
  },
469
662
  remainingAccounts,
@@ -481,44 +674,113 @@ class ClearingHouse {
481
674
  },
482
675
  });
483
676
  }
484
- async openPosition(direction, amount, marketIndex, limitPrice) {
485
- return await this.placeAndTake((0, orderParams_1.getMarketOrderParams)(marketIndex, direction, numericConstants_1.ZERO, amount, false, limitPrice));
677
+ async settleLP(settleeUserAccountPublicKey, marketIndex) {
678
+ const { txSig } = await this.txSender.send((0, utils_1.wrapInTx)(await this.settleLPIx(settleeUserAccountPublicKey, marketIndex)), [], this.opts);
679
+ return txSig;
486
680
  }
487
- async placeOrder(orderParams) {
488
- const { txSig, slot } = await this.txSender.send((0, utils_1.wrapInTx)(await this.getPlaceOrderIx(orderParams)), [], this.opts);
489
- this.marketLastSlotCache.set(orderParams.marketIndex.toNumber(), slot);
681
+ async settleLPIx(settleeUserAccountPublicKey, marketIndex) {
682
+ const settleeUserAccount = (await this.program.account.user.fetch(settleeUserAccountPublicKey));
683
+ const userPositions = settleeUserAccount.positions;
684
+ const remainingAccounts = [];
685
+ let foundMarket = false;
686
+ for (const position of userPositions) {
687
+ if (!(0, position_1.positionIsAvailable)(position)) {
688
+ const marketPublicKey = await (0, pda_1.getMarketPublicKey)(this.program.programId, position.marketIndex);
689
+ remainingAccounts.push({
690
+ pubkey: marketPublicKey,
691
+ isWritable: true,
692
+ isSigner: false,
693
+ });
694
+ if (marketIndex.eq(position.marketIndex)) {
695
+ foundMarket = true;
696
+ }
697
+ }
698
+ }
699
+ if (!foundMarket) {
700
+ console.log('Warning: lp is not in the market specified -- tx will likely fail');
701
+ }
702
+ return this.program.instruction.settleLp(marketIndex, {
703
+ accounts: {
704
+ state: await this.getStatePublicKey(),
705
+ user: settleeUserAccountPublicKey,
706
+ },
707
+ remainingAccounts: remainingAccounts,
708
+ });
709
+ }
710
+ async removeLiquidity(marketIndex, sharesToBurn) {
711
+ const { txSig } = await this.txSender.send((0, utils_1.wrapInTx)(await this.getRemoveLiquidityIx(marketIndex, sharesToBurn)), [], this.opts);
490
712
  return txSig;
491
713
  }
492
- async getPlaceOrderIx(orderParams) {
714
+ async getRemoveLiquidityIx(marketIndex, sharesToBurn) {
493
715
  const userAccountPublicKey = await this.getUserAccountPublicKey();
494
- const priceOracle = this.getMarketAccount(orderParams.marketIndex).amm
495
- .oracle;
496
716
  const remainingAccounts = this.getRemainingAccounts({
497
- writableMarketIndex: orderParams.marketIndex,
717
+ writableMarketIndex: marketIndex,
498
718
  });
499
- return await this.program.instruction.placeOrder(orderParams, {
719
+ if (sharesToBurn == undefined) {
720
+ const userAccount = this.getUserAccount();
721
+ const marketPosition = userAccount.positions.filter((position) => position.marketIndex.eq(marketIndex))[0];
722
+ sharesToBurn = marketPosition.lpShares;
723
+ console.log('burning lp shares:', sharesToBurn.toString());
724
+ }
725
+ return this.program.instruction.removeLiquidity(sharesToBurn, marketIndex, {
500
726
  accounts: {
501
727
  state: await this.getStatePublicKey(),
502
728
  user: userAccountPublicKey,
503
729
  authority: this.wallet.publicKey,
504
- oracle: priceOracle,
505
730
  },
506
- remainingAccounts,
731
+ remainingAccounts: remainingAccounts,
507
732
  });
508
733
  }
509
- async expireOrders(userAccountPublicKey) {
510
- const { txSig } = await this.txSender.send((0, utils_1.wrapInTx)(await this.getExpireOrdersIx(userAccountPublicKey)), [], this.opts);
734
+ async addLiquidity(amount, marketIndex) {
735
+ const { txSig, slot } = await this.txSender.send((0, utils_1.wrapInTx)(await this.getAddLiquidityIx(amount, marketIndex)), [], this.opts);
736
+ this.marketLastSlotCache.set(marketIndex.toNumber(), slot);
511
737
  return txSig;
512
738
  }
513
- async getExpireOrdersIx(userAccountPublicKey) {
514
- const fillerPublicKey = await this.getUserAccountPublicKey();
515
- return await this.program.instruction.expireOrders({
739
+ async getAddLiquidityIx(amount, marketIndex) {
740
+ const userAccountPublicKey = await this.getUserAccountPublicKey();
741
+ const remainingAccounts = this.getRemainingAccounts({
742
+ writableMarketIndex: marketIndex,
743
+ });
744
+ return this.program.instruction.addLiquidity(amount, marketIndex, {
745
+ accounts: {
746
+ state: await this.getStatePublicKey(),
747
+ user: userAccountPublicKey,
748
+ authority: this.wallet.publicKey,
749
+ },
750
+ remainingAccounts: remainingAccounts,
751
+ });
752
+ }
753
+ async openPosition(direction, amount, marketIndex, limitPrice) {
754
+ return await this.placeAndTake({
755
+ orderType: types_1.OrderType.MARKET,
756
+ marketIndex,
757
+ direction,
758
+ baseAssetAmount: amount,
759
+ price: limitPrice,
760
+ });
761
+ }
762
+ async placeOrder(orderParams) {
763
+ const { txSig, slot } = await this.txSender.send((0, utils_1.wrapInTx)(await this.getPlaceOrderIx(orderParams)), [], this.opts);
764
+ this.marketLastSlotCache.set(orderParams.marketIndex.toNumber(), slot);
765
+ return txSig;
766
+ }
767
+ getOrderParams(optionalOrderParams) {
768
+ return Object.assign({}, types_1.DefaultOrderParams, optionalOrderParams);
769
+ }
770
+ async getPlaceOrderIx(orderParams) {
771
+ orderParams = this.getOrderParams(orderParams);
772
+ const userAccountPublicKey = await this.getUserAccountPublicKey();
773
+ const remainingAccounts = this.getRemainingAccounts({
774
+ readableMarketIndex: orderParams.marketIndex,
775
+ });
776
+ return await this.program.instruction.placeOrder(orderParams, {
516
777
  accounts: {
517
778
  state: await this.getStatePublicKey(),
518
- filler: fillerPublicKey,
519
779
  user: userAccountPublicKey,
780
+ userStats: this.getUserStatsAccountPublicKey(),
520
781
  authority: this.wallet.publicKey,
521
782
  },
783
+ remainingAccounts,
522
784
  });
523
785
  }
524
786
  async updateAMMs(marketIndexes) {
@@ -590,107 +852,92 @@ class ClearingHouse {
590
852
  remainingAccounts,
591
853
  });
592
854
  }
593
- async cancelAllOrders(bestEffort) {
594
- const { txSig } = await this.txSender.send((0, utils_1.wrapInTx)(await this.getCancelAllOrdersIx(bestEffort)), [], this.opts);
595
- return txSig;
596
- }
597
- async getCancelAllOrdersIx(bestEffort) {
598
- const userAccountPublicKey = await this.getUserAccountPublicKey();
599
- const remainingAccounts = this.getRemainingAccounts({});
600
- for (const order of this.getUserAccount().orders) {
601
- const oracle = this.getMarketAccount(order.marketIndex).amm.oracle;
602
- remainingAccounts.push({
603
- pubkey: oracle,
604
- isWritable: false,
605
- isSigner: false,
606
- });
607
- }
608
- return await this.program.instruction.cancelAllOrders(bestEffort, {
609
- accounts: {
610
- state: await this.getStatePublicKey(),
611
- user: userAccountPublicKey,
612
- authority: this.wallet.publicKey,
613
- },
614
- remainingAccounts,
615
- });
616
- }
617
- async cancelOrdersByMarketAndSide(bestEffort, marketIndexOnly, directionOnly) {
618
- const { txSig } = await this.txSender.send((0, utils_1.wrapInTx)(await this.getCancelOrdersByMarketAndSideIx(bestEffort, marketIndexOnly, directionOnly)), [], this.opts);
619
- return txSig;
620
- }
621
- async getCancelOrdersByMarketAndSideIx(bestEffort, marketIndexOnly, directionOnly) {
622
- const userAccountPublicKey = await this.getUserAccountPublicKey();
623
- const remainingAccounts = this.getRemainingAccounts({});
624
- for (const order of this.getUserAccount().orders) {
625
- const oracle = this.getMarketAccount(order.marketIndex).amm.oracle;
626
- remainingAccounts.push({
627
- pubkey: oracle,
628
- isWritable: false,
629
- isSigner: false,
630
- });
631
- }
632
- return await this.program.instruction.cancelOrdersByMarketAndSide(bestEffort, marketIndexOnly, directionOnly, {
633
- accounts: {
634
- state: await this.getStatePublicKey(),
635
- user: userAccountPublicKey,
636
- authority: this.wallet.publicKey,
637
- },
638
- remainingAccounts,
639
- });
640
- }
641
- async fillOrder(userAccountPublicKey, user, order, makerInfo) {
642
- const { txSig } = await this.txSender.send((0, utils_1.wrapInTx)(await this.getFillOrderIx(userAccountPublicKey, user, order, makerInfo)), [], this.opts);
855
+ async fillOrder(userAccountPublicKey, user, order, makerInfo, referrerInfo) {
856
+ const { txSig } = await this.txSender.send((0, utils_1.wrapInTx)(await this.getFillOrderIx(userAccountPublicKey, user, order, makerInfo, referrerInfo)), [], this.opts);
643
857
  return txSig;
644
858
  }
645
- async getFillOrderIx(userAccountPublicKey, userAccount, order, makerInfo) {
859
+ async getFillOrderIx(userAccountPublicKey, userAccount, order, makerInfo, referrerInfo) {
860
+ const userStatsPublicKey = (0, pda_1.getUserStatsAccountPublicKey)(this.program.programId, userAccount.authority);
646
861
  const fillerPublicKey = await this.getUserAccountPublicKey();
647
- const marketIndex = order.marketIndex;
862
+ const fillerStatsPublicKey = this.getUserStatsAccountPublicKey();
863
+ const marketIndex = order
864
+ ? order.marketIndex
865
+ : userAccount.orders.find((order) => order.orderId.eq(userAccount.nextOrderId.sub(numericConstants_1.ONE))).marketIndex;
648
866
  const marketAccount = this.getMarketAccount(marketIndex);
649
- const oracle = marketAccount.amm.oracle;
650
- const bankAccountInfos = [
651
- {
652
- pubkey: this.getQuoteAssetBankAccount().pubkey,
653
- isSigner: false,
654
- isWritable: true,
655
- },
656
- ];
657
- const marketAccountInfos = [
658
- {
659
- pubkey: marketAccount.pubkey,
660
- isWritable: true,
661
- isSigner: false,
662
- },
663
- ];
664
- const oracleAccountInfos = [
665
- {
666
- pubkey: marketAccount.amm.oracle,
667
- isWritable: false,
668
- isSigner: false,
669
- },
670
- ];
867
+ const oracleAccountMap = new Map();
868
+ const bankAccountMap = new Map();
869
+ const marketAccountMap = new Map();
870
+ for (const bankBalance of userAccount.bankBalances) {
871
+ if (!bankBalance.balance.eq(numericConstants_1.ZERO)) {
872
+ const bankAccount = this.getBankAccount(bankBalance.bankIndex);
873
+ bankAccountMap.set(bankBalance.bankIndex.toNumber(), {
874
+ pubkey: bankAccount.pubkey,
875
+ isSigner: false,
876
+ isWritable: false,
877
+ });
878
+ if (!bankAccount.oracle.equals(web3_js_1.PublicKey.default)) {
879
+ oracleAccountMap.set(bankAccount.oracle.toString(), {
880
+ pubkey: bankAccount.oracle,
881
+ isSigner: false,
882
+ isWritable: false,
883
+ });
884
+ }
885
+ }
886
+ }
671
887
  for (const position of userAccount.positions) {
672
888
  if (!(0, position_1.positionIsAvailable)(position) &&
673
889
  !position.marketIndex.eq(order.marketIndex)) {
674
890
  const market = this.getMarketAccount(position.marketIndex);
675
- marketAccountInfos.push({
891
+ marketAccountMap.set(position.marketIndex.toNumber(), {
676
892
  pubkey: market.pubkey,
677
893
  isWritable: false,
678
894
  isSigner: false,
679
895
  });
680
- oracleAccountInfos.push({
896
+ oracleAccountMap.set(market.amm.oracle.toString(), {
681
897
  pubkey: market.amm.oracle,
682
898
  isWritable: false,
683
899
  isSigner: false,
684
900
  });
685
901
  }
686
902
  }
687
- const remainingAccounts = oracleAccountInfos.concat(bankAccountInfos.concat(marketAccountInfos));
903
+ marketAccountMap.set(marketIndex.toNumber(), {
904
+ pubkey: marketAccount.pubkey,
905
+ isWritable: true,
906
+ isSigner: false,
907
+ });
908
+ oracleAccountMap.set(marketAccount.amm.oracle.toString(), {
909
+ pubkey: marketAccount.amm.oracle,
910
+ isWritable: false,
911
+ isSigner: false,
912
+ });
913
+ const remainingAccounts = [
914
+ ...oracleAccountMap.values(),
915
+ ...bankAccountMap.values(),
916
+ ...marketAccountMap.values(),
917
+ ];
688
918
  if (makerInfo) {
689
919
  remainingAccounts.push({
690
920
  pubkey: makerInfo.maker,
691
921
  isWritable: true,
692
922
  isSigner: false,
693
923
  });
924
+ remainingAccounts.push({
925
+ pubkey: makerInfo.makerStats,
926
+ isWritable: true,
927
+ isSigner: false,
928
+ });
929
+ }
930
+ if (referrerInfo) {
931
+ remainingAccounts.push({
932
+ pubkey: referrerInfo.referrer,
933
+ isWritable: true,
934
+ isSigner: false,
935
+ });
936
+ remainingAccounts.push({
937
+ pubkey: referrerInfo.referrerStats,
938
+ isWritable: true,
939
+ isSigner: false,
940
+ });
694
941
  }
695
942
  const orderId = order.orderId;
696
943
  const makerOrderId = makerInfo ? makerInfo.order.orderId : null;
@@ -698,9 +945,10 @@ class ClearingHouse {
698
945
  accounts: {
699
946
  state: await this.getStatePublicKey(),
700
947
  filler: fillerPublicKey,
948
+ fillerStats: fillerStatsPublicKey,
701
949
  user: userAccountPublicKey,
950
+ userStats: userStatsPublicKey,
702
951
  authority: this.wallet.publicKey,
703
- oracle: oracle,
704
952
  },
705
953
  remainingAccounts,
706
954
  });
@@ -713,44 +961,57 @@ class ClearingHouse {
713
961
  const fillerPublicKey = await this.getUserAccountPublicKey();
714
962
  const marketIndex = order.marketIndex;
715
963
  const marketAccount = this.getMarketAccount(marketIndex);
716
- const bankAccountInfos = [
717
- {
718
- pubkey: this.getQuoteAssetBankAccount().pubkey,
719
- isSigner: false,
720
- isWritable: true,
721
- },
722
- ];
723
- const marketAccountInfos = [
724
- {
725
- pubkey: marketAccount.pubkey,
726
- isWritable: true,
727
- isSigner: false,
728
- },
729
- ];
730
- const oracleAccountInfos = [
731
- {
732
- pubkey: marketAccount.amm.oracle,
733
- isWritable: false,
734
- isSigner: false,
735
- },
736
- ];
964
+ const oracleAccountMap = new Map();
965
+ const bankAccountMap = new Map();
966
+ const marketAccountMap = new Map();
967
+ for (const bankBalance of userAccount.bankBalances) {
968
+ if (!bankBalance.balance.eq(numericConstants_1.ZERO)) {
969
+ const bankAccount = this.getBankAccount(bankBalance.bankIndex);
970
+ bankAccountMap.set(bankBalance.bankIndex.toNumber(), {
971
+ pubkey: bankAccount.pubkey,
972
+ isSigner: false,
973
+ isWritable: false,
974
+ });
975
+ if (!bankAccount.oracle.equals(web3_js_1.PublicKey.default)) {
976
+ oracleAccountMap.set(bankAccount.oracle.toString(), {
977
+ pubkey: bankAccount.oracle,
978
+ isSigner: false,
979
+ isWritable: false,
980
+ });
981
+ }
982
+ }
983
+ }
737
984
  for (const position of userAccount.positions) {
738
985
  if (!(0, position_1.positionIsAvailable)(position) &&
739
986
  !position.marketIndex.eq(order.marketIndex)) {
740
987
  const market = this.getMarketAccount(position.marketIndex);
741
- marketAccountInfos.push({
988
+ marketAccountMap.set(position.marketIndex.toNumber(), {
742
989
  pubkey: market.pubkey,
743
990
  isWritable: false,
744
991
  isSigner: false,
745
992
  });
746
- oracleAccountInfos.push({
993
+ oracleAccountMap.set(market.amm.oracle.toString(), {
747
994
  pubkey: market.amm.oracle,
748
995
  isWritable: false,
749
996
  isSigner: false,
750
997
  });
751
998
  }
752
999
  }
753
- const remainingAccounts = oracleAccountInfos.concat(bankAccountInfos.concat(marketAccountInfos));
1000
+ marketAccountMap.set(marketIndex.toNumber(), {
1001
+ pubkey: marketAccount.pubkey,
1002
+ isWritable: true,
1003
+ isSigner: false,
1004
+ });
1005
+ oracleAccountMap.set(marketAccount.amm.oracle.toString(), {
1006
+ pubkey: marketAccount.amm.oracle,
1007
+ isWritable: false,
1008
+ isSigner: false,
1009
+ });
1010
+ const remainingAccounts = [
1011
+ ...oracleAccountMap.values(),
1012
+ ...bankAccountMap.values(),
1013
+ ...marketAccountMap.values(),
1014
+ ];
754
1015
  const orderId = order.orderId;
755
1016
  return await this.program.instruction.triggerOrder(orderId, {
756
1017
  accounts: {
@@ -762,15 +1023,15 @@ class ClearingHouse {
762
1023
  remainingAccounts,
763
1024
  });
764
1025
  }
765
- async placeAndTake(orderParams, makerInfo) {
766
- const { txSig, slot } = await this.txSender.send((0, utils_1.wrapInTx)(await this.getPlaceAndTakeIx(orderParams, makerInfo)), [], this.opts);
1026
+ async placeAndTake(orderParams, makerInfo, referrerInfo) {
1027
+ const { txSig, slot } = await this.txSender.send((0, utils_1.wrapInTx)(await this.getPlaceAndTakeIx(orderParams, makerInfo, referrerInfo)), [], this.opts);
767
1028
  this.marketLastSlotCache.set(orderParams.marketIndex.toNumber(), slot);
768
1029
  return txSig;
769
1030
  }
770
- async getPlaceAndTakeIx(orderParams, makerInfo) {
1031
+ async getPlaceAndTakeIx(orderParams, makerInfo, referrerInfo) {
1032
+ orderParams = this.getOrderParams(orderParams);
1033
+ const userStatsPublicKey = await this.getUserStatsAccountPublicKey();
771
1034
  const userAccountPublicKey = await this.getUserAccountPublicKey();
772
- const priceOracle = this.getMarketAccount(orderParams.marketIndex).amm
773
- .oracle;
774
1035
  const remainingAccounts = this.getRemainingAccounts({
775
1036
  writableMarketIndex: orderParams.marketIndex,
776
1037
  writableBankIndex: numericConstants_1.QUOTE_ASSET_BANK_INDEX,
@@ -783,13 +1044,69 @@ class ClearingHouse {
783
1044
  isSigner: false,
784
1045
  isWritable: true,
785
1046
  });
1047
+ remainingAccounts.push({
1048
+ pubkey: makerInfo.makerStats,
1049
+ isSigner: false,
1050
+ isWritable: true,
1051
+ });
1052
+ }
1053
+ if (referrerInfo) {
1054
+ remainingAccounts.push({
1055
+ pubkey: referrerInfo.referrer,
1056
+ isWritable: true,
1057
+ isSigner: false,
1058
+ });
1059
+ remainingAccounts.push({
1060
+ pubkey: referrerInfo.referrerStats,
1061
+ isWritable: true,
1062
+ isSigner: false,
1063
+ });
786
1064
  }
787
1065
  return await this.program.instruction.placeAndTake(orderParams, makerOrderId, {
788
1066
  accounts: {
789
1067
  state: await this.getStatePublicKey(),
790
1068
  user: userAccountPublicKey,
1069
+ userStats: userStatsPublicKey,
1070
+ authority: this.wallet.publicKey,
1071
+ },
1072
+ remainingAccounts,
1073
+ });
1074
+ }
1075
+ async placeAndMake(orderParams, takerInfo, referrerInfo) {
1076
+ const { txSig, slot } = await this.txSender.send((0, utils_1.wrapInTx)(await this.getPlaceAndMakeIx(orderParams, takerInfo, referrerInfo)), [], this.opts);
1077
+ this.marketLastSlotCache.set(orderParams.marketIndex.toNumber(), slot);
1078
+ return txSig;
1079
+ }
1080
+ async getPlaceAndMakeIx(orderParams, takerInfo, referrerInfo) {
1081
+ orderParams = this.getOrderParams(orderParams);
1082
+ const userStatsPublicKey = this.getUserStatsAccountPublicKey();
1083
+ const userAccountPublicKey = await this.getUserAccountPublicKey();
1084
+ // todo merge this with getRemainingAccounts
1085
+ const remainingAccounts = this.getRemainingAccountsWithCounterparty({
1086
+ counterPartyUserAccount: takerInfo.takerUserAccount,
1087
+ writableMarketIndex: orderParams.marketIndex,
1088
+ });
1089
+ if (referrerInfo) {
1090
+ remainingAccounts.push({
1091
+ pubkey: referrerInfo.referrer,
1092
+ isWritable: true,
1093
+ isSigner: false,
1094
+ });
1095
+ remainingAccounts.push({
1096
+ pubkey: referrerInfo.referrerStats,
1097
+ isWritable: true,
1098
+ isSigner: false,
1099
+ });
1100
+ }
1101
+ const takerOrderId = takerInfo.order.orderId;
1102
+ return await this.program.instruction.placeAndMake(orderParams, takerOrderId, {
1103
+ accounts: {
1104
+ state: await this.getStatePublicKey(),
1105
+ user: userAccountPublicKey,
1106
+ userStats: userStatsPublicKey,
1107
+ taker: takerInfo.taker,
1108
+ takerStats: takerInfo.takerStats,
791
1109
  authority: this.wallet.publicKey,
792
- oracle: priceOracle,
793
1110
  },
794
1111
  remainingAccounts,
795
1112
  });
@@ -799,19 +1116,31 @@ class ClearingHouse {
799
1116
  * @param marketIndex
800
1117
  * @returns
801
1118
  */
802
- async closePosition(marketIndex) {
1119
+ async closePosition(marketIndex, limitPrice) {
803
1120
  const userPosition = this.getUser().getUserPosition(marketIndex);
804
1121
  if (!userPosition) {
805
1122
  throw Error(`No position in market ${marketIndex.toString()}`);
806
1123
  }
807
- return await this.placeAndTake((0, orderParams_1.getMarketOrderParams)(marketIndex, (0, position_1.findDirectionToClose)(userPosition), numericConstants_1.ZERO, userPosition.baseAssetAmount, true, undefined));
1124
+ return await this.placeAndTake({
1125
+ orderType: types_1.OrderType.MARKET,
1126
+ marketIndex,
1127
+ direction: (0, position_1.findDirectionToClose)(userPosition),
1128
+ baseAssetAmount: userPosition.baseAssetAmount.abs(),
1129
+ reduceOnly: true,
1130
+ price: limitPrice,
1131
+ });
808
1132
  }
809
1133
  async settlePNLs(users, marketIndex) {
810
1134
  const ixs = [];
811
1135
  for (const { settleeUserAccountPublicKey, settleeUserAccount } of users) {
812
1136
  ixs.push(await this.settlePNLIx(settleeUserAccountPublicKey, settleeUserAccount, marketIndex));
813
1137
  }
814
- const tx = new web3_js_1.Transaction().add(...ixs);
1138
+ const tx = new web3_js_1.Transaction()
1139
+ .add(web3_js_1.ComputeBudgetProgram.requestUnits({
1140
+ units: 1000000,
1141
+ additionalFee: 0,
1142
+ }))
1143
+ .add(...ixs);
815
1144
  const { txSig } = await this.txSender.send(tx, [], this.opts);
816
1145
  return txSig;
817
1146
  }
@@ -828,7 +1157,7 @@ class ClearingHouse {
828
1157
  const market = this.getMarketAccount(position.marketIndex);
829
1158
  marketAccountMap.set(position.marketIndex.toNumber(), {
830
1159
  pubkey: market.pubkey,
831
- isWritable: true,
1160
+ isWritable: false,
832
1161
  isSigner: false,
833
1162
  });
834
1163
  oracleAccountMap.set(market.amm.oracle.toString(), {
@@ -885,54 +1214,261 @@ class ClearingHouse {
885
1214
  remainingAccounts: remainingAccounts,
886
1215
  });
887
1216
  }
888
- async liquidate(liquidateeUserAccountPublicKey) {
889
- const { txSig } = await this.txSender.send((0, utils_1.wrapInTx)(await this.getLiquidateIx(liquidateeUserAccountPublicKey)), [], this.opts);
1217
+ async liquidatePerp(userAccountPublicKey, userAccount, marketIndex, maxBaseAssetAmount) {
1218
+ const { txSig } = await this.txSender.send((0, utils_1.wrapInTx)(await this.getLiquidatePerpIx(userAccountPublicKey, userAccount, marketIndex, maxBaseAssetAmount)), [], this.opts);
890
1219
  return txSig;
891
1220
  }
892
- async getLiquidateIx(liquidateeUserAccountPublicKey) {
893
- const userAccountPublicKey = await this.getUserAccountPublicKey();
894
- const liquidateeUserAccount = (await this.program.account.user.fetch(liquidateeUserAccountPublicKey));
895
- const bankAccountInfos = [
896
- {
897
- pubkey: this.getQuoteAssetBankAccount().pubkey,
898
- isSigner: false,
899
- isWritable: true,
1221
+ async getLiquidatePerpIx(userAccountPublicKey, userAccount, marketIndex, maxBaseAssetAmount) {
1222
+ const userStatsPublicKey = (0, pda_1.getUserStatsAccountPublicKey)(this.program.programId, userAccount.authority);
1223
+ const liquidatorPublicKey = await this.getUserAccountPublicKey();
1224
+ const liquidatorStatsPublicKey = this.getUserStatsAccountPublicKey();
1225
+ const remainingAccounts = this.getRemainingAccountsWithCounterparty({
1226
+ writableMarketIndex: marketIndex,
1227
+ counterPartyUserAccount: userAccount,
1228
+ });
1229
+ return await this.program.instruction.liquidatePerp(marketIndex, maxBaseAssetAmount, {
1230
+ accounts: {
1231
+ state: await this.getStatePublicKey(),
1232
+ authority: this.wallet.publicKey,
1233
+ user: userAccountPublicKey,
1234
+ userStats: userStatsPublicKey,
1235
+ liquidator: liquidatorPublicKey,
1236
+ liquidatorStats: liquidatorStatsPublicKey,
900
1237
  },
901
- ];
902
- const marketAccountInfos = [];
903
- const oracleAccountInfos = [];
904
- for (const position of liquidateeUserAccount.positions) {
1238
+ remainingAccounts: remainingAccounts,
1239
+ });
1240
+ }
1241
+ async liquidateBorrow(userAccountPublicKey, userAccount, assetBankIndex, liabilityBankIndex, maxLiabilityTransfer) {
1242
+ const { txSig } = await this.txSender.send((0, utils_1.wrapInTx)(await this.getLiquidateBorrowIx(userAccountPublicKey, userAccount, assetBankIndex, liabilityBankIndex, maxLiabilityTransfer)), [], this.opts);
1243
+ return txSig;
1244
+ }
1245
+ async getLiquidateBorrowIx(userAccountPublicKey, userAccount, assetBankIndex, liabilityBankIndex, maxLiabilityTransfer) {
1246
+ const liquidatorPublicKey = await this.getUserAccountPublicKey();
1247
+ const remainingAccounts = this.getRemainingAccountsWithCounterparty({
1248
+ counterPartyUserAccount: userAccount,
1249
+ writableBankIndexes: [liabilityBankIndex, assetBankIndex],
1250
+ });
1251
+ return await this.program.instruction.liquidateBorrow(assetBankIndex, liabilityBankIndex, maxLiabilityTransfer, {
1252
+ accounts: {
1253
+ state: await this.getStatePublicKey(),
1254
+ authority: this.wallet.publicKey,
1255
+ user: userAccountPublicKey,
1256
+ liquidator: liquidatorPublicKey,
1257
+ },
1258
+ remainingAccounts: remainingAccounts,
1259
+ });
1260
+ }
1261
+ async liquidateBorrowForPerpPnl(userAccountPublicKey, userAccount, perpMarketIndex, liabilityBankIndex, maxLiabilityTransfer) {
1262
+ const { txSig } = await this.txSender.send((0, utils_1.wrapInTx)(await this.getLiquidateBorrowForPerpPnlIx(userAccountPublicKey, userAccount, perpMarketIndex, liabilityBankIndex, maxLiabilityTransfer)), [], this.opts);
1263
+ return txSig;
1264
+ }
1265
+ async getLiquidateBorrowForPerpPnlIx(userAccountPublicKey, userAccount, perpMarketIndex, liabilityBankIndex, maxLiabilityTransfer) {
1266
+ const liquidatorPublicKey = await this.getUserAccountPublicKey();
1267
+ const remainingAccounts = this.getRemainingAccountsWithCounterparty({
1268
+ counterPartyUserAccount: userAccount,
1269
+ writableMarketIndex: perpMarketIndex,
1270
+ writableBankIndexes: [liabilityBankIndex],
1271
+ });
1272
+ return await this.program.instruction.liquidateBorrowForPerpPnl(perpMarketIndex, liabilityBankIndex, maxLiabilityTransfer, {
1273
+ accounts: {
1274
+ state: await this.getStatePublicKey(),
1275
+ authority: this.wallet.publicKey,
1276
+ user: userAccountPublicKey,
1277
+ liquidator: liquidatorPublicKey,
1278
+ },
1279
+ remainingAccounts: remainingAccounts,
1280
+ });
1281
+ }
1282
+ async liquidatePerpPnlForDeposit(userAccountPublicKey, userAccount, perpMarketIndex, assetBankIndex, maxPnlTransfer) {
1283
+ const { txSig } = await this.txSender.send((0, utils_1.wrapInTx)(await this.getLiquidatePerpPnlForDepositIx(userAccountPublicKey, userAccount, perpMarketIndex, assetBankIndex, maxPnlTransfer)), [], this.opts);
1284
+ return txSig;
1285
+ }
1286
+ async getLiquidatePerpPnlForDepositIx(userAccountPublicKey, userAccount, perpMarketIndex, assetBankIndex, maxPnlTransfer) {
1287
+ const liquidatorPublicKey = await this.getUserAccountPublicKey();
1288
+ const remainingAccounts = this.getRemainingAccountsWithCounterparty({
1289
+ counterPartyUserAccount: userAccount,
1290
+ writableMarketIndex: perpMarketIndex,
1291
+ writableBankIndexes: [assetBankIndex],
1292
+ });
1293
+ return await this.program.instruction.liquidatePerpPnlForDeposit(perpMarketIndex, assetBankIndex, maxPnlTransfer, {
1294
+ accounts: {
1295
+ state: await this.getStatePublicKey(),
1296
+ authority: this.wallet.publicKey,
1297
+ user: userAccountPublicKey,
1298
+ liquidator: liquidatorPublicKey,
1299
+ },
1300
+ remainingAccounts: remainingAccounts,
1301
+ });
1302
+ }
1303
+ async resolvePerpBankruptcy(userAccountPublicKey, userAccount, marketIndex) {
1304
+ const { txSig } = await this.txSender.send((0, utils_1.wrapInTx)(await this.getResolvePerpBankruptcyIx(userAccountPublicKey, userAccount, marketIndex)), [], this.opts);
1305
+ return txSig;
1306
+ }
1307
+ async getResolvePerpBankruptcyIx(userAccountPublicKey, userAccount, marketIndex) {
1308
+ const liquidatorPublicKey = await this.getUserAccountPublicKey();
1309
+ const remainingAccounts = this.getRemainingAccountsWithCounterparty({
1310
+ writableMarketIndex: marketIndex,
1311
+ counterPartyUserAccount: userAccount,
1312
+ });
1313
+ return await this.program.instruction.resolvePerpBankruptcy(marketIndex, {
1314
+ accounts: {
1315
+ state: await this.getStatePublicKey(),
1316
+ authority: this.wallet.publicKey,
1317
+ user: userAccountPublicKey,
1318
+ liquidator: liquidatorPublicKey,
1319
+ },
1320
+ remainingAccounts: remainingAccounts,
1321
+ });
1322
+ }
1323
+ async resolveBorrowBankruptcy(userAccountPublicKey, userAccount, bankIndex) {
1324
+ const { txSig } = await this.txSender.send((0, utils_1.wrapInTx)(await this.getResolveBorrowBankruptcyIx(userAccountPublicKey, userAccount, bankIndex)), [], this.opts);
1325
+ return txSig;
1326
+ }
1327
+ async getResolveBorrowBankruptcyIx(userAccountPublicKey, userAccount, bankIndex) {
1328
+ const liquidatorPublicKey = await this.getUserAccountPublicKey();
1329
+ const remainingAccounts = this.getRemainingAccountsWithCounterparty({
1330
+ writableBankIndexes: [bankIndex],
1331
+ counterPartyUserAccount: userAccount,
1332
+ });
1333
+ return await this.program.instruction.resolveBorrowBankruptcy(bankIndex, {
1334
+ accounts: {
1335
+ state: await this.getStatePublicKey(),
1336
+ authority: this.wallet.publicKey,
1337
+ user: userAccountPublicKey,
1338
+ liquidator: liquidatorPublicKey,
1339
+ },
1340
+ remainingAccounts: remainingAccounts,
1341
+ });
1342
+ }
1343
+ getRemainingAccountsWithCounterparty(params) {
1344
+ const counterPartyUserAccount = params.counterPartyUserAccount;
1345
+ const oracleAccountMap = new Map();
1346
+ const bankAccountMap = new Map();
1347
+ const marketAccountMap = new Map();
1348
+ for (const bankBalance of counterPartyUserAccount.bankBalances) {
1349
+ if (!bankBalance.balance.eq(numericConstants_1.ZERO)) {
1350
+ const bankAccount = this.getBankAccount(bankBalance.bankIndex);
1351
+ bankAccountMap.set(bankBalance.bankIndex.toNumber(), {
1352
+ pubkey: bankAccount.pubkey,
1353
+ isSigner: false,
1354
+ isWritable: false,
1355
+ });
1356
+ if (!bankAccount.oracle.equals(web3_js_1.PublicKey.default)) {
1357
+ oracleAccountMap.set(bankAccount.oracle.toString(), {
1358
+ pubkey: bankAccount.oracle,
1359
+ isSigner: false,
1360
+ isWritable: false,
1361
+ });
1362
+ }
1363
+ }
1364
+ }
1365
+ for (const position of counterPartyUserAccount.positions) {
905
1366
  if (!(0, position_1.positionIsAvailable)(position)) {
906
1367
  const market = this.getMarketAccount(position.marketIndex);
907
- const marketPublicKey = await (0, pda_1.getMarketPublicKey)(this.program.programId, position.marketIndex);
908
- marketAccountInfos.push({
909
- pubkey: marketPublicKey,
910
- isWritable: true,
1368
+ marketAccountMap.set(position.marketIndex.toNumber(), {
1369
+ pubkey: market.pubkey,
1370
+ isWritable: false,
911
1371
  isSigner: false,
912
1372
  });
913
- oracleAccountInfos.push({
1373
+ oracleAccountMap.set(market.amm.oracle.toString(), {
914
1374
  pubkey: market.amm.oracle,
915
1375
  isWritable: false,
916
1376
  isSigner: false,
917
1377
  });
918
1378
  }
919
1379
  }
920
- const remainingAccounts = oracleAccountInfos.concat(bankAccountInfos.concat(marketAccountInfos));
921
- const state = this.getStateAccount();
922
- const quoteAssetBankAccount = this.getQuoteAssetBankAccount();
923
- return await this.program.instruction.liquidate({
924
- accounts: {
925
- state: await this.getStatePublicKey(),
926
- authority: this.wallet.publicKey,
927
- user: liquidateeUserAccountPublicKey,
928
- liquidator: userAccountPublicKey,
929
- bankVault: quoteAssetBankAccount.vault,
930
- bankVaultAuthority: quoteAssetBankAccount.vaultAuthority,
931
- insuranceVault: state.insuranceVault,
932
- tokenProgram: spl_token_1.TOKEN_PROGRAM_ID,
933
- },
934
- remainingAccounts: remainingAccounts,
935
- });
1380
+ const userAccountAndSlot = this.getUserAccountAndSlot();
1381
+ if (!userAccountAndSlot) {
1382
+ throw Error('No user account found. Most likely user account does not exist or failed to fetch account');
1383
+ }
1384
+ const { data: userAccount, slot: lastUserPositionsSlot } = userAccountAndSlot;
1385
+ for (const [marketIndexNum, slot] of this.marketLastSlotCache.entries()) {
1386
+ // if cache has more recent slot than user positions account slot, add market to remaining accounts
1387
+ // otherwise remove from slot
1388
+ if (slot > lastUserPositionsSlot) {
1389
+ const marketAccount = this.getMarketAccount(marketIndexNum);
1390
+ marketAccountMap.set(marketIndexNum, {
1391
+ pubkey: marketAccount.pubkey,
1392
+ isSigner: false,
1393
+ isWritable: false,
1394
+ });
1395
+ oracleAccountMap.set(marketAccount.amm.oracle.toString(), {
1396
+ pubkey: marketAccount.amm.oracle,
1397
+ isSigner: false,
1398
+ isWritable: false,
1399
+ });
1400
+ }
1401
+ else {
1402
+ this.marketLastSlotCache.delete(marketIndexNum);
1403
+ }
1404
+ }
1405
+ for (const bankBalance of userAccount.bankBalances) {
1406
+ if (!bankBalance.balance.eq(numericConstants_1.ZERO)) {
1407
+ const bankAccount = this.getBankAccount(bankBalance.bankIndex);
1408
+ bankAccountMap.set(bankBalance.bankIndex.toNumber(), {
1409
+ pubkey: bankAccount.pubkey,
1410
+ isSigner: false,
1411
+ isWritable: false,
1412
+ });
1413
+ if (!bankAccount.oracle.equals(web3_js_1.PublicKey.default)) {
1414
+ oracleAccountMap.set(bankAccount.oracle.toString(), {
1415
+ pubkey: bankAccount.oracle,
1416
+ isSigner: false,
1417
+ isWritable: false,
1418
+ });
1419
+ }
1420
+ }
1421
+ }
1422
+ for (const position of userAccount.positions) {
1423
+ if (!(0, position_1.positionIsAvailable)(position)) {
1424
+ const market = this.getMarketAccount(position.marketIndex);
1425
+ marketAccountMap.set(position.marketIndex.toNumber(), {
1426
+ pubkey: market.pubkey,
1427
+ isWritable: false,
1428
+ isSigner: false,
1429
+ });
1430
+ oracleAccountMap.set(market.amm.oracle.toString(), {
1431
+ pubkey: market.amm.oracle,
1432
+ isWritable: false,
1433
+ isSigner: false,
1434
+ });
1435
+ }
1436
+ }
1437
+ if (params.writableMarketIndex) {
1438
+ const market = this.getMarketAccount(params.writableMarketIndex);
1439
+ marketAccountMap.set(market.marketIndex.toNumber(), {
1440
+ pubkey: market.pubkey,
1441
+ isSigner: false,
1442
+ isWritable: true,
1443
+ });
1444
+ oracleAccountMap.set(market.amm.oracle.toString(), {
1445
+ pubkey: market.amm.oracle,
1446
+ isSigner: false,
1447
+ isWritable: false,
1448
+ });
1449
+ }
1450
+ if (params.writableBankIndexes) {
1451
+ for (const writableBankIndex of params.writableBankIndexes) {
1452
+ const bank = this.getBankAccount(writableBankIndex);
1453
+ bankAccountMap.set(bank.bankIndex.toNumber(), {
1454
+ pubkey: bank.pubkey,
1455
+ isSigner: false,
1456
+ isWritable: true,
1457
+ });
1458
+ if (!bank.oracle.equals(web3_js_1.PublicKey.default)) {
1459
+ oracleAccountMap.set(bank.oracle.toString(), {
1460
+ pubkey: bank.oracle,
1461
+ isSigner: false,
1462
+ isWritable: false,
1463
+ });
1464
+ }
1465
+ }
1466
+ }
1467
+ return [
1468
+ ...oracleAccountMap.values(),
1469
+ ...bankAccountMap.values(),
1470
+ ...marketAccountMap.values(),
1471
+ ];
936
1472
  }
937
1473
  async updateFundingRate(oracle, marketIndex) {
938
1474
  const { txSig } = await this.txSender.send((0, utils_1.wrapInTx)(await this.getUpdateFundingRateIx(oracle, marketIndex)), [], this.opts);