@drift-labs/sdk 0.2.0-master.11 → 0.2.0-master.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/lib/accounts/fetch.d.ts +2 -1
  2. package/lib/accounts/fetch.js +9 -1
  3. package/lib/accounts/pollingUserStatsAccountSubscriber.d.ts +27 -0
  4. package/lib/accounts/pollingUserStatsAccountSubscriber.js +113 -0
  5. package/lib/accounts/types.d.ts +14 -1
  6. package/lib/accounts/webSocketUserStatsAccountSubsriber.d.ts +20 -0
  7. package/lib/accounts/webSocketUserStatsAccountSubsriber.js +47 -0
  8. package/lib/addresses/pda.d.ts +1 -0
  9. package/lib/addresses/pda.js +8 -1
  10. package/lib/admin.d.ts +2 -0
  11. package/lib/admin.js +18 -0
  12. package/lib/clearingHouse.d.ts +24 -3
  13. package/lib/clearingHouse.js +352 -51
  14. package/lib/clearingHouseConfig.d.ts +1 -0
  15. package/lib/clearingHouseUser.d.ts +15 -15
  16. package/lib/clearingHouseUser.js +185 -73
  17. package/lib/clearingHouseUserStats.d.ts +17 -0
  18. package/lib/clearingHouseUserStats.js +36 -0
  19. package/lib/clearingHouseUserStatsConfig.d.ts +14 -0
  20. package/lib/clearingHouseUserStatsConfig.js +2 -0
  21. package/lib/config.js +1 -1
  22. package/lib/constants/banks.d.ts +2 -2
  23. package/lib/constants/banks.js +4 -3
  24. package/lib/constants/numericConstants.d.ts +2 -0
  25. package/lib/constants/numericConstants.js +3 -1
  26. package/lib/events/eventList.js +3 -0
  27. package/lib/events/types.d.ts +2 -1
  28. package/lib/factory/bigNum.d.ts +1 -0
  29. package/lib/factory/bigNum.js +37 -11
  30. package/lib/idl/clearing_house.json +692 -58
  31. package/lib/index.d.ts +1 -0
  32. package/lib/index.js +1 -0
  33. package/lib/math/amm.js +2 -2
  34. package/lib/math/bankBalance.d.ts +7 -1
  35. package/lib/math/bankBalance.js +76 -1
  36. package/lib/math/margin.d.ts +11 -0
  37. package/lib/math/margin.js +72 -0
  38. package/lib/math/market.d.ts +4 -1
  39. package/lib/math/market.js +35 -1
  40. package/lib/math/oracles.d.ts +3 -0
  41. package/lib/math/oracles.js +25 -5
  42. package/lib/math/position.d.ts +8 -0
  43. package/lib/math/position.js +43 -12
  44. package/lib/math/trade.js +2 -2
  45. package/lib/orders.d.ts +1 -2
  46. package/lib/orders.js +2 -77
  47. package/lib/tokenFaucet.d.ts +1 -0
  48. package/lib/tokenFaucet.js +23 -12
  49. package/lib/tx/retryTxSender.js +9 -2
  50. package/lib/types.d.ts +78 -10
  51. package/lib/types.js +12 -0
  52. package/lib/util/getTokenAddress.d.ts +2 -0
  53. package/lib/util/getTokenAddress.js +9 -0
  54. package/package.json +1 -1
  55. package/src/accounts/fetch.ts +27 -2
  56. package/src/accounts/pollingUserStatsAccountSubscriber.ts +172 -0
  57. package/src/accounts/types.ts +18 -0
  58. package/src/accounts/webSocketUserStatsAccountSubsriber.ts +80 -0
  59. package/src/addresses/pda.ts +13 -0
  60. package/src/admin.ts +29 -1
  61. package/src/clearingHouse.ts +619 -62
  62. package/src/clearingHouseConfig.ts +1 -0
  63. package/src/clearingHouseUser.ts +317 -105
  64. package/src/clearingHouseUserStats.ts +53 -0
  65. package/src/clearingHouseUserStatsConfig.ts +18 -0
  66. package/src/config.ts +1 -1
  67. package/src/constants/banks.js +42 -0
  68. package/src/constants/banks.ts +6 -3
  69. package/src/constants/markets.js +42 -0
  70. package/src/constants/numericConstants.js +41 -0
  71. package/src/constants/numericConstants.ts +3 -0
  72. package/src/events/eventList.ts +3 -0
  73. package/src/events/types.ts +2 -0
  74. package/src/factory/bigNum.js +37 -11
  75. package/src/factory/bigNum.ts +43 -13
  76. package/src/idl/clearing_house.json +692 -58
  77. package/src/index.ts +1 -0
  78. package/src/math/amm.ts +8 -5
  79. package/src/math/bankBalance.ts +147 -1
  80. package/src/math/margin.ts +124 -0
  81. package/src/math/market.ts +66 -1
  82. package/src/math/oracles.ts +42 -5
  83. package/src/math/position.ts +60 -9
  84. package/src/math/trade.ts +2 -2
  85. package/src/orders.ts +4 -157
  86. package/src/tokenFaucet.js +189 -0
  87. package/src/tokenFaucet.ts +38 -15
  88. package/src/tx/retryTxSender.ts +11 -3
  89. package/src/types.js +12 -1
  90. package/src/types.ts +83 -10
  91. package/src/{accounts/fetch.js → util/computeUnits.js} +11 -13
  92. package/src/util/getTokenAddress.js +9 -0
  93. package/src/util/getTokenAddress.ts +18 -0
  94. package/tests/bn/test.ts +2 -0
  95. package/src/accounts/bulkAccountLoader.js +0 -197
  96. package/src/accounts/bulkUserSubscription.js +0 -33
  97. package/src/accounts/pollingClearingHouseAccountSubscriber.js +0 -311
  98. package/src/accounts/pollingOracleSubscriber.js +0 -93
  99. package/src/accounts/pollingTokenAccountSubscriber.js +0 -90
  100. package/src/accounts/pollingUserAccountSubscriber.js +0 -132
  101. package/src/accounts/types.js +0 -10
  102. package/src/accounts/utils.js +0 -7
  103. package/src/accounts/webSocketAccountSubscriber.js +0 -93
  104. package/src/accounts/webSocketClearingHouseAccountSubscriber.js +0 -233
  105. package/src/accounts/webSocketUserAccountSubscriber.js +0 -62
  106. package/src/addresses/pda.js +0 -104
  107. package/src/index.js +0 -100
  108. package/src/math/bankBalance.js +0 -75
  109. package/src/math/market.js +0 -57
  110. package/src/math/orders.js +0 -110
  111. package/src/math/position.js +0 -140
  112. package/src/mockUSDCFaucet.js +0 -280
  113. package/src/orders.js +0 -134
  114. package/src/tx/retryTxSender.js +0 -188
@@ -1,5 +1,5 @@
1
1
  import { AnchorProvider, BN, Idl, Program } from '@project-serum/anchor';
2
- import { TOKEN_PROGRAM_ID } from '@solana/spl-token';
2
+ import { Token, TOKEN_PROGRAM_ID } from '@solana/spl-token';
3
3
  import {
4
4
  StateAccount,
5
5
  IWallet,
@@ -27,6 +27,10 @@ import {
27
27
  Transaction,
28
28
  TransactionInstruction,
29
29
  AccountMeta,
30
+ Keypair,
31
+ LAMPORTS_PER_SOL,
32
+ Signer,
33
+ SystemProgram,
30
34
  } from '@solana/web3.js';
31
35
 
32
36
  import { TokenFaucet } from './tokenFaucet';
@@ -37,6 +41,7 @@ import {
37
41
  getMarketPublicKey,
38
42
  getUserAccountPublicKey,
39
43
  getUserAccountPublicKeySync,
44
+ getUserStatsAccountPublicKey,
40
45
  } from './addresses/pda';
41
46
  import {
42
47
  ClearingHouseAccountSubscriber,
@@ -57,6 +62,8 @@ import { RetryTxSender } from './tx/retryTxSender';
57
62
  import { ClearingHouseUser } from './clearingHouseUser';
58
63
  import { ClearingHouseUserAccountSubscriptionConfig } from './clearingHouseUserConfig';
59
64
  import { getMarketsBanksAndOraclesForSubscription } from './config';
65
+ import { WRAPPED_SOL_MINT } from './constants/banks';
66
+ import { ClearingHouseUserStats } from './clearingHouseUserStats';
60
67
 
61
68
  /**
62
69
  * # ClearingHouse
@@ -69,6 +76,7 @@ export class ClearingHouse {
69
76
  provider: AnchorProvider;
70
77
  opts?: ConfirmOptions;
71
78
  users = new Map<number, ClearingHouseUser>();
79
+ userStats?: ClearingHouseUserStats;
72
80
  activeUserId: number;
73
81
  userAccountSubscriptionConfig: ClearingHouseUserAccountSubscriptionConfig;
74
82
  accountSubscriber: ClearingHouseAccountSubscriber;
@@ -112,6 +120,16 @@ export class ClearingHouse {
112
120
  type: 'websocket',
113
121
  };
114
122
  this.createUsers(userIds, this.userAccountSubscriptionConfig);
123
+ if (config.userStats) {
124
+ this.userStats = new ClearingHouseUserStats({
125
+ clearingHouse: this,
126
+ userStatsAccountPublicKey: getUserStatsAccountPublicKey(
127
+ this.program.programId,
128
+ this.wallet.publicKey
129
+ ),
130
+ accountSubscription: this.userAccountSubscriptionConfig,
131
+ });
132
+ }
115
133
 
116
134
  let marketIndexes = config.marketIndexes;
117
135
  let bankIndexes = config.bankIndexes;
@@ -183,6 +201,9 @@ export class ClearingHouse {
183
201
  const subscribePromises = this.subscribeUsers().concat(
184
202
  this.accountSubscriber.subscribe()
185
203
  );
204
+ if (this.userStats !== undefined) {
205
+ subscribePromises.concat(this.userStats.subscribe());
206
+ }
186
207
  this.isSubscribed = (await Promise.all(subscribePromises)).reduce(
187
208
  (success, prevSuccess) => success && prevSuccess
188
209
  );
@@ -208,6 +229,9 @@ export class ClearingHouse {
208
229
  const unsubscribePromises = this.unsubscribeUsers().concat(
209
230
  this.accountSubscriber.unsubscribe()
210
231
  );
232
+ if (this.userStats !== undefined) {
233
+ unsubscribePromises.concat(this.userStats.unsubscribe());
234
+ }
211
235
  await Promise.all(unsubscribePromises);
212
236
  this.isSubscribed = false;
213
237
  }
@@ -297,6 +321,7 @@ export class ClearingHouse {
297
321
  }
298
322
 
299
323
  this.activeUserId = activeUserId;
324
+ this.userStatsAccountPublicKey = undefined;
300
325
  }
301
326
 
302
327
  public async switchActiveUser(userId: number): Promise<void> {
@@ -320,7 +345,12 @@ export class ClearingHouse {
320
345
  const [userAccountPublicKey, initializeUserAccountIx] =
321
346
  await this.getInitializeUserInstructions(userId, name);
322
347
 
323
- const tx = new Transaction().add(initializeUserAccountIx);
348
+ const tx = new Transaction();
349
+ if (userId === 0) {
350
+ // not the safest assumption, can explicitly check if user stats account exists if it causes problems
351
+ tx.add(await this.getInitializeUserStatsIx());
352
+ }
353
+ tx.add(initializeUserAccountIx);
324
354
  const { txSig } = await this.txSender.send(tx, [], this.opts);
325
355
  return [txSig, userAccountPublicKey];
326
356
  }
@@ -340,6 +370,7 @@ export class ClearingHouse {
340
370
  await this.program.instruction.initializeUser(userId, nameBuffer, {
341
371
  accounts: {
342
372
  user: userAccountPublicKey,
373
+ userStats: this.getUserStatsAccountPublicKey(),
343
374
  authority: this.wallet.publicKey,
344
375
  payer: this.wallet.publicKey,
345
376
  rent: anchor.web3.SYSVAR_RENT_PUBKEY,
@@ -351,6 +382,19 @@ export class ClearingHouse {
351
382
  return [userAccountPublicKey, initializeUserAccountIx];
352
383
  }
353
384
 
385
+ async getInitializeUserStatsIx(): Promise<TransactionInstruction> {
386
+ return await this.program.instruction.initializeUserStats({
387
+ accounts: {
388
+ userStats: this.getUserStatsAccountPublicKey(),
389
+ authority: this.wallet.publicKey,
390
+ payer: this.wallet.publicKey,
391
+ rent: anchor.web3.SYSVAR_RENT_PUBKEY,
392
+ systemProgram: anchor.web3.SystemProgram.programId,
393
+ state: await this.getStatePublicKey(),
394
+ },
395
+ });
396
+ }
397
+
354
398
  public getUser(userId?: number): ClearingHouseUser {
355
399
  userId = userId ?? this.activeUserId;
356
400
  if (!this.users.has(userId)) {
@@ -363,6 +407,23 @@ export class ClearingHouse {
363
407
  return [...this.users.values()];
364
408
  }
365
409
 
410
+ public getUserStats(): ClearingHouseUserStats {
411
+ return this.userStats;
412
+ }
413
+
414
+ userStatsAccountPublicKey: PublicKey;
415
+ public getUserStatsAccountPublicKey(): PublicKey {
416
+ if (this.userStatsAccountPublicKey) {
417
+ return this.userStatsAccountPublicKey;
418
+ }
419
+
420
+ this.userStatsAccountPublicKey = getUserStatsAccountPublicKey(
421
+ this.program.programId,
422
+ this.wallet.publicKey
423
+ );
424
+ return this.userStatsAccountPublicKey;
425
+ }
426
+
366
427
  public async getUserAccountPublicKey(): Promise<PublicKey> {
367
428
  return this.getUser().userAccountPublicKey;
368
429
  }
@@ -399,6 +460,7 @@ export class ClearingHouse {
399
460
  getRemainingAccounts(params: {
400
461
  writableMarketIndex?: BN;
401
462
  writableBankIndex?: BN;
463
+ readableMarketIndex?: BN;
402
464
  }): AccountMeta[] {
403
465
  const userAccountAndSlot = this.getUserAccountAndSlot();
404
466
  if (!userAccountAndSlot) {
@@ -439,8 +501,7 @@ export class ClearingHouse {
439
501
  marketAccountMap.set(marketIndexNum, {
440
502
  pubkey: marketAccount.pubkey,
441
503
  isSigner: false,
442
- // isWritable: false, // TODO
443
- isWritable: true,
504
+ isWritable: false,
444
505
  });
445
506
  oracleAccountMap.set(marketAccount.pubkey.toString(), {
446
507
  pubkey: marketAccount.amm.oracle,
@@ -450,6 +511,22 @@ export class ClearingHouse {
450
511
  }
451
512
  }
452
513
 
514
+ if (params.readableMarketIndex) {
515
+ const marketAccount = this.getMarketAccount(
516
+ params.readableMarketIndex.toNumber()
517
+ );
518
+ marketAccountMap.set(params.readableMarketIndex.toNumber(), {
519
+ pubkey: marketAccount.pubkey,
520
+ isSigner: false,
521
+ isWritable: true,
522
+ });
523
+ oracleAccountMap.set(marketAccount.amm.oracle.toString(), {
524
+ pubkey: marketAccount.amm.oracle,
525
+ isSigner: false,
526
+ isWritable: false,
527
+ });
528
+ }
529
+
453
530
  if (params.writableMarketIndex) {
454
531
  const marketAccount = this.getMarketAccount(
455
532
  params.writableMarketIndex.toNumber()
@@ -527,6 +604,31 @@ export class ClearingHouse {
527
604
  userId?: number,
528
605
  reduceOnly = false
529
606
  ): Promise<TransactionSignature> {
607
+ const tx = new Transaction();
608
+ const additionalSigners: Array<Signer> = [];
609
+
610
+ const bank = this.getBankAccount(bankIndex);
611
+
612
+ const isSolBank = bank.mint.equals(WRAPPED_SOL_MINT);
613
+
614
+ const authority = this.wallet.publicKey;
615
+
616
+ const createWSOLTokenAccount =
617
+ isSolBank && collateralAccountPublicKey.equals(authority);
618
+
619
+ if (createWSOLTokenAccount) {
620
+ const { ixs, signers, pubkey } =
621
+ await this.getWrappedSolAccountCreationIxs(amount);
622
+
623
+ collateralAccountPublicKey = pubkey;
624
+
625
+ ixs.forEach((ix) => {
626
+ tx.add(ix);
627
+ });
628
+
629
+ signers.forEach((signer) => additionalSigners.push(signer));
630
+ }
631
+
530
632
  const depositCollateralIx = await this.getDepositInstruction(
531
633
  amount,
532
634
  bankIndex,
@@ -536,9 +638,26 @@ export class ClearingHouse {
536
638
  true
537
639
  );
538
640
 
539
- const tx = new Transaction().add(depositCollateralIx);
641
+ tx.add(depositCollateralIx);
642
+
643
+ // Close the wrapped sol account at the end of the transaction
644
+ if (createWSOLTokenAccount) {
645
+ tx.add(
646
+ Token.createCloseAccountInstruction(
647
+ TOKEN_PROGRAM_ID,
648
+ collateralAccountPublicKey,
649
+ authority,
650
+ authority,
651
+ []
652
+ )
653
+ );
654
+ }
540
655
 
541
- const { txSig } = await this.txSender.send(tx);
656
+ const { txSig } = await this.txSender.send(
657
+ tx,
658
+ additionalSigners,
659
+ this.opts
660
+ );
542
661
  return txSig;
543
662
  }
544
663
 
@@ -600,12 +719,119 @@ export class ClearingHouse {
600
719
  );
601
720
  }
602
721
 
722
+ private async checkIfAccountExists(account: PublicKey) {
723
+ try {
724
+ const accountInfo = await this.connection.getAccountInfo(account);
725
+ return accountInfo && true;
726
+ } catch (e) {
727
+ // Doesn't already exist
728
+ return false;
729
+ }
730
+ }
731
+
732
+ private async getSolWithdrawalIxs(
733
+ bankIndex: BN,
734
+ amount: BN
735
+ ): Promise<{
736
+ ixs: anchor.web3.TransactionInstruction[];
737
+ signers: Signer[];
738
+ pubkey: PublicKey;
739
+ }> {
740
+ const result = {
741
+ ixs: [],
742
+ signers: [],
743
+ pubkey: PublicKey.default,
744
+ };
745
+
746
+ // Create a temporary wrapped SOL account to store the SOL that we're withdrawing
747
+
748
+ const authority = this.wallet.publicKey;
749
+
750
+ const { ixs, signers, pubkey } = await this.getWrappedSolAccountCreationIxs(
751
+ amount
752
+ );
753
+ result.pubkey = pubkey;
754
+
755
+ ixs.forEach((ix) => {
756
+ result.ixs.push(ix);
757
+ });
758
+
759
+ signers.forEach((ix) => {
760
+ result.signers.push(ix);
761
+ });
762
+
763
+ const withdrawIx = await this.getWithdrawIx(
764
+ amount,
765
+ bankIndex,
766
+ pubkey,
767
+ true
768
+ );
769
+
770
+ result.ixs.push(withdrawIx);
771
+
772
+ result.ixs.push(
773
+ Token.createCloseAccountInstruction(
774
+ TOKEN_PROGRAM_ID,
775
+ pubkey,
776
+ authority,
777
+ authority,
778
+ []
779
+ )
780
+ );
781
+
782
+ return result;
783
+ }
784
+
785
+ private async getWrappedSolAccountCreationIxs(amount: BN): Promise<{
786
+ ixs: anchor.web3.TransactionInstruction[];
787
+ signers: Signer[];
788
+ pubkey: PublicKey;
789
+ }> {
790
+ const wrappedSolAccount = new Keypair();
791
+
792
+ const result = {
793
+ ixs: [],
794
+ signers: [],
795
+ pubkey: wrappedSolAccount.publicKey,
796
+ };
797
+
798
+ const rentSpaceLamports = new BN(LAMPORTS_PER_SOL / 100);
799
+
800
+ const depositAmountLamports = amount.add(rentSpaceLamports);
801
+
802
+ const authority = this.wallet.publicKey;
803
+
804
+ result.ixs.push(
805
+ SystemProgram.createAccount({
806
+ fromPubkey: authority,
807
+ newAccountPubkey: wrappedSolAccount.publicKey,
808
+ lamports: depositAmountLamports.toNumber(),
809
+ space: 165,
810
+ programId: TOKEN_PROGRAM_ID,
811
+ })
812
+ );
813
+
814
+ result.ixs.push(
815
+ Token.createInitAccountInstruction(
816
+ TOKEN_PROGRAM_ID,
817
+ WRAPPED_SOL_MINT,
818
+ wrappedSolAccount.publicKey,
819
+ authority
820
+ )
821
+ );
822
+
823
+ result.signers.push(wrappedSolAccount);
824
+
825
+ return result;
826
+ }
827
+
603
828
  /**
604
829
  * Creates the Clearing House User account for a user, and deposits some initial collateral
605
- * @param userId
606
- * @param name
607
830
  * @param amount
608
831
  * @param userTokenAccount
832
+ * @param bankIndex
833
+ * @param userId
834
+ * @param name
609
835
  * @param fromUserId
610
836
  * @returns
611
837
  */
@@ -620,6 +846,35 @@ export class ClearingHouse {
620
846
  const [userAccountPublicKey, initializeUserAccountIx] =
621
847
  await this.getInitializeUserInstructions(userId, name);
622
848
 
849
+ const additionalSigners: Array<Signer> = [];
850
+
851
+ const bank = this.getBankAccount(bankIndex);
852
+
853
+ const isSolBank = bank.mint.equals(WRAPPED_SOL_MINT);
854
+
855
+ const tx = new Transaction();
856
+
857
+ const authority = this.wallet.publicKey;
858
+
859
+ const createWSOLTokenAccount =
860
+ isSolBank && userTokenAccount.equals(authority);
861
+
862
+ if (createWSOLTokenAccount) {
863
+ const {
864
+ ixs: startIxs,
865
+ signers,
866
+ pubkey,
867
+ } = await this.getWrappedSolAccountCreationIxs(amount);
868
+
869
+ userTokenAccount = pubkey;
870
+
871
+ startIxs.forEach((ix) => {
872
+ tx.add(ix);
873
+ });
874
+
875
+ signers.forEach((signer) => additionalSigners.push(signer));
876
+ }
877
+
623
878
  const depositCollateralIx =
624
879
  fromUserId != null
625
880
  ? await this.getTransferDepositIx(amount, bankIndex, fromUserId, userId)
@@ -632,11 +887,29 @@ export class ClearingHouse {
632
887
  false
633
888
  );
634
889
 
635
- const tx = new Transaction()
636
- .add(initializeUserAccountIx)
637
- .add(depositCollateralIx);
890
+ if (userId === 0) {
891
+ tx.add(await this.getInitializeUserStatsIx());
892
+ }
893
+ tx.add(initializeUserAccountIx).add(depositCollateralIx);
638
894
 
639
- const { txSig } = await this.txSender.send(tx, []);
895
+ // Close the wrapped sol account at the end of the transaction
896
+ if (createWSOLTokenAccount) {
897
+ tx.add(
898
+ Token.createCloseAccountInstruction(
899
+ TOKEN_PROGRAM_ID,
900
+ userTokenAccount,
901
+ authority,
902
+ authority,
903
+ []
904
+ )
905
+ );
906
+ }
907
+
908
+ const { txSig } = await this.txSender.send(
909
+ tx,
910
+ additionalSigners,
911
+ this.opts
912
+ );
640
913
 
641
914
  return [txSig, userAccountPublicKey];
642
915
  }
@@ -666,11 +939,12 @@ export class ClearingHouse {
666
939
  false
667
940
  );
668
941
 
669
- const tx = new Transaction()
670
- .add(createAssociatedAccountIx)
671
- .add(mintToIx)
672
- .add(initializeUserAccountIx)
673
- .add(depositCollateralIx);
942
+ const tx = new Transaction().add(createAssociatedAccountIx).add(mintToIx);
943
+
944
+ if (userId === 0) {
945
+ tx.add(await this.getInitializeUserStatsIx());
946
+ }
947
+ tx.add(initializeUserAccountIx).add(depositCollateralIx);
674
948
 
675
949
  const txSig = await this.program.provider.sendAndConfirm(tx, []);
676
950
 
@@ -683,16 +957,56 @@ export class ClearingHouse {
683
957
  userTokenAccount: PublicKey,
684
958
  reduceOnly = false
685
959
  ): Promise<TransactionSignature> {
686
- const { txSig } = await this.txSender.send(
687
- wrapInTx(
688
- await this.getWithdrawIx(
689
- amount,
690
- bankIndex,
960
+ const tx = new Transaction();
961
+ const additionalSigners: Array<Signer> = [];
962
+
963
+ const bank = this.getBankAccount(bankIndex);
964
+
965
+ const isSolBank = bank.mint.equals(WRAPPED_SOL_MINT);
966
+
967
+ const authority = this.wallet.publicKey;
968
+
969
+ const createWSOLTokenAccount =
970
+ isSolBank && userTokenAccount.equals(authority);
971
+
972
+ if (createWSOLTokenAccount) {
973
+ const { ixs, signers, pubkey } =
974
+ await this.getWrappedSolAccountCreationIxs(amount);
975
+
976
+ userTokenAccount = pubkey;
977
+
978
+ ixs.forEach((ix) => {
979
+ tx.add(ix);
980
+ });
981
+
982
+ signers.forEach((signer) => additionalSigners.push(signer));
983
+ }
984
+
985
+ const withdrawCollateral = await this.getWithdrawIx(
986
+ amount,
987
+ bank.bankIndex,
988
+ userTokenAccount,
989
+ reduceOnly
990
+ );
991
+
992
+ tx.add(withdrawCollateral);
993
+
994
+ // Close the wrapped sol account at the end of the transaction
995
+ if (createWSOLTokenAccount) {
996
+ tx.add(
997
+ Token.createCloseAccountInstruction(
998
+ TOKEN_PROGRAM_ID,
691
999
  userTokenAccount,
692
- reduceOnly
1000
+ authority,
1001
+ authority,
1002
+ []
693
1003
  )
694
- ),
695
- [],
1004
+ );
1005
+ }
1006
+
1007
+ const { txSig } = await this.txSender.send(
1008
+ tx,
1009
+ additionalSigners,
696
1010
  this.opts
697
1011
  );
698
1012
  return txSig;
@@ -802,6 +1116,135 @@ export class ClearingHouse {
802
1116
  });
803
1117
  }
804
1118
 
1119
+ public async settleLP(
1120
+ settleeUserAccountPublicKey: PublicKey,
1121
+ marketIndex: BN
1122
+ ): Promise<TransactionSignature> {
1123
+ const { txSig } = await this.txSender.send(
1124
+ wrapInTx(await this.settleLPIx(settleeUserAccountPublicKey, marketIndex)),
1125
+ [],
1126
+ this.opts
1127
+ );
1128
+ return txSig;
1129
+ }
1130
+
1131
+ public async settleLPIx(
1132
+ settleeUserAccountPublicKey: PublicKey,
1133
+ marketIndex: BN
1134
+ ): Promise<TransactionInstruction> {
1135
+ const settleeUserAccount = (await this.program.account.user.fetch(
1136
+ settleeUserAccountPublicKey
1137
+ )) as UserAccount;
1138
+ const userPositions = settleeUserAccount.positions;
1139
+ const remainingAccounts = [];
1140
+
1141
+ let foundMarket = false;
1142
+ for (const position of userPositions) {
1143
+ if (!positionIsAvailable(position)) {
1144
+ const marketPublicKey = await getMarketPublicKey(
1145
+ this.program.programId,
1146
+ position.marketIndex
1147
+ );
1148
+ remainingAccounts.push({
1149
+ pubkey: marketPublicKey,
1150
+ isWritable: true,
1151
+ isSigner: false,
1152
+ });
1153
+
1154
+ if (marketIndex.eq(position.marketIndex)) {
1155
+ foundMarket = true;
1156
+ }
1157
+ }
1158
+ }
1159
+
1160
+ if (!foundMarket) {
1161
+ console.log(
1162
+ 'Warning: lp is not in the market specified -- tx will likely fail'
1163
+ );
1164
+ }
1165
+
1166
+ return this.program.instruction.settleLp(marketIndex, {
1167
+ accounts: {
1168
+ state: await this.getStatePublicKey(),
1169
+ user: settleeUserAccountPublicKey,
1170
+ },
1171
+ remainingAccounts: remainingAccounts,
1172
+ });
1173
+ }
1174
+
1175
+ public async removeLiquidity(
1176
+ marketIndex: BN,
1177
+ sharesToBurn?: BN
1178
+ ): Promise<TransactionSignature> {
1179
+ const { txSig } = await this.txSender.send(
1180
+ wrapInTx(await this.getRemoveLiquidityIx(marketIndex, sharesToBurn)),
1181
+ [],
1182
+ this.opts
1183
+ );
1184
+ return txSig;
1185
+ }
1186
+
1187
+ public async getRemoveLiquidityIx(
1188
+ marketIndex: BN,
1189
+ sharesToBurn?: BN
1190
+ ): Promise<TransactionInstruction> {
1191
+ const userAccountPublicKey = await this.getUserAccountPublicKey();
1192
+
1193
+ const remainingAccounts = this.getRemainingAccounts({
1194
+ writableMarketIndex: marketIndex,
1195
+ });
1196
+
1197
+ if (sharesToBurn == undefined) {
1198
+ const userAccount = this.getUserAccount();
1199
+ const marketPosition = userAccount.positions.filter((position) =>
1200
+ position.marketIndex.eq(marketIndex)
1201
+ )[0];
1202
+ sharesToBurn = marketPosition.lpShares;
1203
+ console.log('burning lp shares:', sharesToBurn.toString());
1204
+ }
1205
+
1206
+ return this.program.instruction.removeLiquidity(sharesToBurn, marketIndex, {
1207
+ accounts: {
1208
+ state: await this.getStatePublicKey(),
1209
+ user: userAccountPublicKey,
1210
+ authority: this.wallet.publicKey,
1211
+ },
1212
+ remainingAccounts: remainingAccounts,
1213
+ });
1214
+ }
1215
+
1216
+ public async addLiquidity(
1217
+ amount: BN,
1218
+ marketIndex: BN
1219
+ ): Promise<TransactionSignature> {
1220
+ const { txSig, slot } = await this.txSender.send(
1221
+ wrapInTx(await this.getAddLiquidityIx(amount, marketIndex)),
1222
+ [],
1223
+ this.opts
1224
+ );
1225
+ this.marketLastSlotCache.set(marketIndex.toNumber(), slot);
1226
+ return txSig;
1227
+ }
1228
+
1229
+ public async getAddLiquidityIx(
1230
+ amount: BN,
1231
+ marketIndex: BN
1232
+ ): Promise<TransactionInstruction> {
1233
+ const userAccountPublicKey = await this.getUserAccountPublicKey();
1234
+ const remainingAccounts = this.getRemainingAccounts({
1235
+ writableMarketIndex: marketIndex,
1236
+ });
1237
+
1238
+ return this.program.instruction.addLiquidity(amount, marketIndex, {
1239
+ accounts: {
1240
+ state: await this.getStatePublicKey(),
1241
+ user: userAccountPublicKey,
1242
+ authority: this.wallet.publicKey,
1243
+ },
1244
+ remainingAccounts: remainingAccounts,
1245
+ });
1246
+ }
1247
+
805
1248
  public async openPosition(
806
1249
  direction: PositionDirection,
807
1250
  amount: BN,
@@ -840,13 +1283,14 @@ export class ClearingHouse {
840
1283
  const userAccountPublicKey = await this.getUserAccountPublicKey();
841
1284
 
842
1285
  const remainingAccounts = this.getRemainingAccounts({
843
- writableMarketIndex: orderParams.marketIndex,
1286
+ readableMarketIndex: orderParams.marketIndex,
844
1287
  });
845
1288
 
846
1289
  return await this.program.instruction.placeOrder(orderParams, {
847
1290
  accounts: {
848
1291
  state: await this.getStatePublicKey(),
849
1292
  user: userAccountPublicKey,
1293
+ userStats: this.getUserStatsAccountPublicKey(),
850
1294
  authority: this.wallet.publicKey,
851
1295
  },
852
1296
  remainingAccounts,
@@ -974,7 +1418,13 @@ export class ClearingHouse {
974
1418
  order: Order,
975
1419
  makerInfo?: MakerInfo
976
1420
  ): Promise<TransactionInstruction> {
1421
+ const userStatsPublicKey = getUserStatsAccountPublicKey(
1422
+ this.program.programId,
1423
+ userAccount.authority
1424
+ );
1425
+
977
1426
  const fillerPublicKey = await this.getUserAccountPublicKey();
1427
+ const fillerStatsPublicKey = this.getUserStatsAccountPublicKey();
978
1428
 
979
1429
  const marketIndex = order.marketIndex;
980
1430
  const marketAccount = this.getMarketAccount(marketIndex);
@@ -983,24 +1433,13 @@ export class ClearingHouse {
983
1433
  const bankAccountMap = new Map<number, AccountMeta>();
984
1434
  const marketAccountMap = new Map<number, AccountMeta>();
985
1435
 
986
- marketAccountMap.set(marketIndex.toNumber(), {
987
- pubkey: marketAccount.pubkey,
988
- isWritable: true,
989
- isSigner: false,
990
- });
991
- oracleAccountMap.set(marketAccount.amm.oracle.toString(), {
992
- pubkey: marketAccount.amm.oracle,
993
- isWritable: false,
994
- isSigner: false,
995
- });
996
-
997
1436
  for (const bankBalance of userAccount.bankBalances) {
998
1437
  if (!bankBalance.balance.eq(ZERO)) {
999
1438
  const bankAccount = this.getBankAccount(bankBalance.bankIndex);
1000
1439
  bankAccountMap.set(bankBalance.bankIndex.toNumber(), {
1001
1440
  pubkey: bankAccount.pubkey,
1002
1441
  isSigner: false,
1003
- isWritable: true,
1442
+ isWritable: false,
1004
1443
  });
1005
1444
 
1006
1445
  if (!bankAccount.oracle.equals(PublicKey.default)) {
@@ -1021,7 +1460,7 @@ export class ClearingHouse {
1021
1460
  const market = this.getMarketAccount(position.marketIndex);
1022
1461
  marketAccountMap.set(position.marketIndex.toNumber(), {
1023
1462
  pubkey: market.pubkey,
1024
- isWritable: true,
1463
+ isWritable: false,
1025
1464
  isSigner: false,
1026
1465
  });
1027
1466
  oracleAccountMap.set(market.amm.oracle.toString(), {
@@ -1032,6 +1471,17 @@ export class ClearingHouse {
1032
1471
  }
1033
1472
  }
1034
1473
 
1474
+ marketAccountMap.set(marketIndex.toNumber(), {
1475
+ pubkey: marketAccount.pubkey,
1476
+ isWritable: true,
1477
+ isSigner: false,
1478
+ });
1479
+ oracleAccountMap.set(marketAccount.amm.oracle.toString(), {
1480
+ pubkey: marketAccount.amm.oracle,
1481
+ isWritable: false,
1482
+ isSigner: false,
1483
+ });
1484
+
1035
1485
  const remainingAccounts = [
1036
1486
  ...oracleAccountMap.values(),
1037
1487
  ...bankAccountMap.values(),
@@ -1044,6 +1494,11 @@ export class ClearingHouse {
1044
1494
  isWritable: true,
1045
1495
  isSigner: false,
1046
1496
  });
1497
+ remainingAccounts.push({
1498
+ pubkey: makerInfo.makerStats,
1499
+ isWritable: true,
1500
+ isSigner: false,
1501
+ });
1047
1502
  }
1048
1503
 
1049
1504
  const orderId = order.orderId;
@@ -1053,7 +1508,9 @@ export class ClearingHouse {
1053
1508
  accounts: {
1054
1509
  state: await this.getStatePublicKey(),
1055
1510
  filler: fillerPublicKey,
1511
+ fillerStats: fillerStatsPublicKey,
1056
1512
  user: userAccountPublicKey,
1513
+ userStats: userStatsPublicKey,
1057
1514
  authority: this.wallet.publicKey,
1058
1515
  },
1059
1516
  remainingAccounts,
@@ -1087,24 +1544,13 @@ export class ClearingHouse {
1087
1544
  const bankAccountMap = new Map<number, AccountMeta>();
1088
1545
  const marketAccountMap = new Map<number, AccountMeta>();
1089
1546
 
1090
- marketAccountMap.set(marketIndex.toNumber(), {
1091
- pubkey: marketAccount.pubkey,
1092
- isWritable: true,
1093
- isSigner: false,
1094
- });
1095
- oracleAccountMap.set(marketAccount.amm.oracle.toString(), {
1096
- pubkey: marketAccount.amm.oracle,
1097
- isWritable: false,
1098
- isSigner: false,
1099
- });
1100
-
1101
1547
  for (const bankBalance of userAccount.bankBalances) {
1102
1548
  if (!bankBalance.balance.eq(ZERO)) {
1103
1549
  const bankAccount = this.getBankAccount(bankBalance.bankIndex);
1104
1550
  bankAccountMap.set(bankBalance.bankIndex.toNumber(), {
1105
1551
  pubkey: bankAccount.pubkey,
1106
1552
  isSigner: false,
1107
- isWritable: true,
1553
+ isWritable: false,
1108
1554
  });
1109
1555
 
1110
1556
  if (!bankAccount.oracle.equals(PublicKey.default)) {
@@ -1125,7 +1571,7 @@ export class ClearingHouse {
1125
1571
  const market = this.getMarketAccount(position.marketIndex);
1126
1572
  marketAccountMap.set(position.marketIndex.toNumber(), {
1127
1573
  pubkey: market.pubkey,
1128
- isWritable: true,
1574
+ isWritable: false,
1129
1575
  isSigner: false,
1130
1576
  });
1131
1577
  oracleAccountMap.set(market.amm.oracle.toString(), {
@@ -1136,6 +1582,17 @@ export class ClearingHouse {
1136
1582
  }
1137
1583
  }
1138
1584
 
1585
+ marketAccountMap.set(marketIndex.toNumber(), {
1586
+ pubkey: marketAccount.pubkey,
1587
+ isWritable: true,
1588
+ isSigner: false,
1589
+ });
1590
+ oracleAccountMap.set(marketAccount.amm.oracle.toString(), {
1591
+ pubkey: marketAccount.amm.oracle,
1592
+ isWritable: false,
1593
+ isSigner: false,
1594
+ });
1595
+
1139
1596
  const remainingAccounts = [
1140
1597
  ...oracleAccountMap.values(),
1141
1598
  ...bankAccountMap.values(),
@@ -1172,6 +1629,7 @@ export class ClearingHouse {
1172
1629
  makerInfo?: MakerInfo
1173
1630
  ): Promise<TransactionInstruction> {
1174
1631
  orderParams = this.getOrderParams(orderParams);
1632
+ const userStatsPublicKey = await this.getUserStatsAccountPublicKey();
1175
1633
  const userAccountPublicKey = await this.getUserAccountPublicKey();
1176
1634
 
1177
1635
  const remainingAccounts = this.getRemainingAccounts({
@@ -1187,6 +1645,11 @@ export class ClearingHouse {
1187
1645
  isSigner: false,
1188
1646
  isWritable: true,
1189
1647
  });
1648
+ remainingAccounts.push({
1649
+ pubkey: makerInfo.makerStats,
1650
+ isSigner: false,
1651
+ isWritable: true,
1652
+ });
1190
1653
  }
1191
1654
 
1192
1655
  return await this.program.instruction.placeAndTake(
@@ -1196,6 +1659,7 @@ export class ClearingHouse {
1196
1659
  accounts: {
1197
1660
  state: await this.getStatePublicKey(),
1198
1661
  user: userAccountPublicKey,
1662
+ userStats: userStatsPublicKey,
1199
1663
  authority: this.wallet.publicKey,
1200
1664
  },
1201
1665
  remainingAccounts,
@@ -1223,20 +1687,14 @@ export class ClearingHouse {
1223
1687
  takerInfo: TakerInfo
1224
1688
  ): Promise<TransactionInstruction> {
1225
1689
  orderParams = this.getOrderParams(orderParams);
1690
+ const userStatsPublicKey = this.getUserStatsAccountPublicKey();
1226
1691
  const userAccountPublicKey = await this.getUserAccountPublicKey();
1227
1692
 
1228
1693
  const remainingAccounts = this.getRemainingAccounts({
1229
1694
  writableMarketIndex: orderParams.marketIndex,
1230
- writableBankIndex: QUOTE_ASSET_BANK_INDEX,
1231
1695
  });
1232
1696
 
1233
1697
  const takerOrderId = takerInfo!.order!.orderId;
1234
- remainingAccounts.push({
1235
- pubkey: takerInfo.taker,
1236
- isSigner: false,
1237
- isWritable: true,
1238
- });
1239
-
1240
1698
  return await this.program.instruction.placeAndMake(
1241
1699
  orderParams,
1242
1700
  takerOrderId,
@@ -1244,7 +1702,9 @@ export class ClearingHouse {
1244
1702
  accounts: {
1245
1703
  state: await this.getStatePublicKey(),
1246
1704
  user: userAccountPublicKey,
1705
+ userStats: userStatsPublicKey,
1247
1706
  taker: takerInfo.taker,
1707
+ takerStats: takerInfo.takerStats,
1248
1708
  authority: this.wallet.publicKey,
1249
1709
  },
1250
1710
  remainingAccounts,
@@ -1257,7 +1717,10 @@ export class ClearingHouse {
1257
1717
  * @param marketIndex
1258
1718
  * @returns
1259
1719
  */
1260
- public async closePosition(marketIndex: BN): Promise<TransactionSignature> {
1720
+ public async closePosition(
1721
+ marketIndex: BN,
1722
+ limitPrice?: BN
1723
+ ): Promise<TransactionSignature> {
1261
1724
  const userPosition = this.getUser().getUserPosition(marketIndex);
1262
1725
  if (!userPosition) {
1263
1726
  throw Error(`No position in market ${marketIndex.toString()}`);
@@ -1267,8 +1730,9 @@ export class ClearingHouse {
1267
1730
  orderType: OrderType.MARKET,
1268
1731
  marketIndex,
1269
1732
  direction: findDirectionToClose(userPosition),
1270
- baseAssetAmount: userPosition.baseAssetAmount,
1733
+ baseAssetAmount: userPosition.baseAssetAmount.abs(),
1271
1734
  reduceOnly: true,
1735
+ price: limitPrice,
1272
1736
  });
1273
1737
  }
1274
1738
 
@@ -1323,12 +1787,13 @@ export class ClearingHouse {
1323
1787
  const marketAccountMap = new Map<number, AccountMeta>();
1324
1788
  const oracleAccountMap = new Map<string, AccountMeta>();
1325
1789
  const bankAccountMap = new Map<number, AccountMeta>();
1790
+
1326
1791
  for (const position of settleeUserAccount.positions) {
1327
1792
  if (!positionIsAvailable(position)) {
1328
1793
  const market = this.getMarketAccount(position.marketIndex);
1329
1794
  marketAccountMap.set(position.marketIndex.toNumber(), {
1330
1795
  pubkey: market.pubkey,
1331
- isWritable: true, // TODO
1796
+ isWritable: false,
1332
1797
  isSigner: false,
1333
1798
  });
1334
1799
  oracleAccountMap.set(market.amm.oracle.toString(), {
@@ -1418,7 +1883,13 @@ export class ClearingHouse {
1418
1883
  marketIndex: BN,
1419
1884
  maxBaseAssetAmount: BN
1420
1885
  ): Promise<TransactionInstruction> {
1886
+ const userStatsPublicKey = getUserStatsAccountPublicKey(
1887
+ this.program.programId,
1888
+ userAccount.authority
1889
+ );
1890
+
1421
1891
  const liquidatorPublicKey = await this.getUserAccountPublicKey();
1892
+ const liquidatorStatsPublicKey = this.getUserStatsAccountPublicKey();
1422
1893
 
1423
1894
  const remainingAccounts = this.getRemainingAccountsForLiquidation({
1424
1895
  writableMarketIndex: marketIndex,
@@ -1433,7 +1904,9 @@ export class ClearingHouse {
1433
1904
  state: await this.getStatePublicKey(),
1434
1905
  authority: this.wallet.publicKey,
1435
1906
  user: userAccountPublicKey,
1907
+ userStats: userStatsPublicKey,
1436
1908
  liquidator: liquidatorPublicKey,
1909
+ liquidatorStats: liquidatorStatsPublicKey,
1437
1910
  },
1438
1911
  remainingAccounts: remainingAccounts,
1439
1912
  }
@@ -1601,6 +2074,90 @@ export class ClearingHouse {
1601
2074
  );
1602
2075
  }
1603
2076
 
2077
+ public async resolvePerpBankruptcy(
2078
+ userAccountPublicKey: PublicKey,
2079
+ userAccount: UserAccount,
2080
+ marketIndex: BN
2081
+ ): Promise<TransactionSignature> {
2082
+ const { txSig } = await this.txSender.send(
2083
+ wrapInTx(
2084
+ await this.getResolvePerpBankruptcyIx(
2085
+ userAccountPublicKey,
2086
+ userAccount,
2087
+ marketIndex
2088
+ )
2089
+ ),
2090
+ [],
2091
+ this.opts
2092
+ );
2093
+ return txSig;
2094
+ }
2095
+
2096
+ public async getResolvePerpBankruptcyIx(
2097
+ userAccountPublicKey: PublicKey,
2098
+ userAccount: UserAccount,
2099
+ marketIndex: BN
2100
+ ): Promise<TransactionInstruction> {
2101
+ const liquidatorPublicKey = await this.getUserAccountPublicKey();
2102
+
2103
+ const remainingAccounts = this.getRemainingAccountsForLiquidation({
2104
+ writableMarketIndex: marketIndex,
2105
+ userAccount,
2106
+ });
2107
+
2108
+ return await this.program.instruction.resolvePerpBankruptcy(marketIndex, {
2109
+ accounts: {
2110
+ state: await this.getStatePublicKey(),
2111
+ authority: this.wallet.publicKey,
2112
+ user: userAccountPublicKey,
2113
+ liquidator: liquidatorPublicKey,
2114
+ },
2115
+ remainingAccounts: remainingAccounts,
2116
+ });
2117
+ }
2118
+
2119
+ public async resolveBorrowBankruptcy(
2120
+ userAccountPublicKey: PublicKey,
2121
+ userAccount: UserAccount,
2122
+ bankIndex: BN
2123
+ ): Promise<TransactionSignature> {
2124
+ const { txSig } = await this.txSender.send(
2125
+ wrapInTx(
2126
+ await this.getResolveBorrowBankruptcyIx(
2127
+ userAccountPublicKey,
2128
+ userAccount,
2129
+ bankIndex
2130
+ )
2131
+ ),
2132
+ [],
2133
+ this.opts
2134
+ );
2135
+ return txSig;
2136
+ }
2137
+
2138
+ public async getResolveBorrowBankruptcyIx(
2139
+ userAccountPublicKey: PublicKey,
2140
+ userAccount: UserAccount,
2141
+ bankIndex: BN
2142
+ ): Promise<TransactionInstruction> {
2143
+ const liquidatorPublicKey = await this.getUserAccountPublicKey();
2144
+
2145
+ const remainingAccounts = this.getRemainingAccountsForLiquidation({
2146
+ writableBankIndexes: [bankIndex],
2147
+ userAccount,
2148
+ });
2149
+
2150
+ return await this.program.instruction.resolveBorrowBankruptcy(bankIndex, {
2151
+ accounts: {
2152
+ state: await this.getStatePublicKey(),
2153
+ authority: this.wallet.publicKey,
2154
+ user: userAccountPublicKey,
2155
+ liquidator: liquidatorPublicKey,
2156
+ },
2157
+ remainingAccounts: remainingAccounts,
2158
+ });
2159
+ }
2160
+
1604
2161
  getRemainingAccountsForLiquidation(params: {
1605
2162
  userAccount: UserAccount;
1606
2163
  writableMarketIndex?: BN;