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

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