@drift-labs/sdk 0.2.0-master.1 → 0.2.0-master.12

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 (142) hide show
  1. package/lib/accounts/types.d.ts +1 -0
  2. package/lib/admin.d.ts +8 -5
  3. package/lib/admin.js +43 -11
  4. package/lib/clearingHouse.d.ts +35 -20
  5. package/lib/clearingHouse.js +497 -154
  6. package/lib/clearingHouseUser.d.ts +12 -17
  7. package/lib/clearingHouseUser.js +97 -88
  8. package/lib/config.js +1 -1
  9. package/lib/constants/banks.d.ts +2 -2
  10. package/lib/constants/banks.js +12 -4
  11. package/lib/constants/numericConstants.d.ts +4 -0
  12. package/lib/constants/numericConstants.js +5 -1
  13. package/lib/events/eventList.js +3 -0
  14. package/lib/events/types.d.ts +2 -1
  15. package/lib/factory/bigNum.d.ts +9 -2
  16. package/lib/factory/bigNum.js +50 -16
  17. package/lib/idl/clearing_house.json +858 -177
  18. package/lib/idl/{mock_usdc_faucet.json → token_faucet.json} +46 -23
  19. package/lib/index.d.ts +4 -2
  20. package/lib/index.js +8 -2
  21. package/lib/math/amm.d.ts +6 -1
  22. package/lib/math/amm.js +124 -41
  23. package/lib/math/auction.js +4 -1
  24. package/lib/math/bankBalance.d.ts +3 -1
  25. package/lib/math/bankBalance.js +54 -1
  26. package/lib/math/margin.d.ts +11 -0
  27. package/lib/math/margin.js +72 -0
  28. package/lib/math/market.d.ts +4 -1
  29. package/lib/math/market.js +35 -1
  30. package/lib/math/orders.d.ts +2 -2
  31. package/lib/math/orders.js +18 -11
  32. package/lib/math/position.d.ts +8 -0
  33. package/lib/math/position.js +44 -12
  34. package/lib/math/repeg.js +1 -1
  35. package/lib/math/trade.d.ts +1 -1
  36. package/lib/math/trade.js +7 -10
  37. package/lib/orderParams.d.ts +14 -5
  38. package/lib/orderParams.js +8 -96
  39. package/lib/orders.d.ts +2 -4
  40. package/lib/orders.js +7 -161
  41. package/lib/slot/SlotSubscriber.d.ts +7 -0
  42. package/lib/slot/SlotSubscriber.js +3 -0
  43. package/lib/{mockUSDCFaucet.d.ts → tokenFaucet.d.ts} +8 -5
  44. package/lib/{mockUSDCFaucet.js → tokenFaucet.js} +63 -51
  45. package/lib/tx/retryTxSender.js +9 -2
  46. package/lib/tx/utils.js +1 -1
  47. package/lib/types.d.ts +159 -15
  48. package/lib/types.js +59 -1
  49. package/lib/util/computeUnits.js +1 -1
  50. package/lib/util/getTokenAddress.d.ts +2 -0
  51. package/lib/util/getTokenAddress.js +9 -0
  52. package/package.json +3 -3
  53. package/src/accounts/bulkAccountLoader.js +197 -0
  54. package/src/accounts/bulkUserSubscription.js +33 -0
  55. package/src/accounts/pollingClearingHouseAccountSubscriber.js +311 -0
  56. package/src/accounts/pollingOracleSubscriber.js +93 -0
  57. package/src/accounts/pollingTokenAccountSubscriber.js +90 -0
  58. package/src/accounts/pollingUserAccountSubscriber.js +132 -0
  59. package/src/accounts/types.js +10 -0
  60. package/src/accounts/utils.js +7 -0
  61. package/src/accounts/webSocketAccountSubscriber.js +93 -0
  62. package/src/accounts/webSocketClearingHouseAccountSubscriber.js +233 -0
  63. package/src/accounts/webSocketUserAccountSubscriber.js +62 -0
  64. package/src/addresses/marketAddresses.js +26 -0
  65. package/src/admin.ts +66 -14
  66. package/src/assert/assert.js +9 -0
  67. package/src/clearingHouse.ts +836 -254
  68. package/src/clearingHouseConfig.js +2 -0
  69. package/src/clearingHouseUser.ts +219 -121
  70. package/src/clearingHouseUserConfig.js +2 -0
  71. package/src/config.ts +1 -1
  72. package/src/constants/banks.js +42 -0
  73. package/src/constants/banks.ts +14 -4
  74. package/src/constants/markets.js +42 -0
  75. package/src/constants/numericConstants.js +41 -0
  76. package/src/constants/numericConstants.ts +5 -0
  77. package/src/events/eventList.js +77 -0
  78. package/src/events/eventList.ts +3 -0
  79. package/src/events/eventSubscriber.js +139 -0
  80. package/src/events/fetchLogs.js +50 -0
  81. package/src/events/pollingLogProvider.js +64 -0
  82. package/src/events/sort.js +44 -0
  83. package/src/events/txEventCache.js +71 -0
  84. package/src/events/types.js +20 -0
  85. package/src/events/types.ts +2 -0
  86. package/src/events/webSocketLogProvider.js +41 -0
  87. package/src/examples/makeTradeExample.js +80 -0
  88. package/src/factory/bigNum.js +390 -0
  89. package/src/factory/bigNum.ts +65 -18
  90. package/src/factory/oracleClient.js +20 -0
  91. package/src/idl/clearing_house.json +858 -177
  92. package/src/idl/{mock_usdc_faucet.json → token_faucet.json} +46 -23
  93. package/src/index.js +69 -0
  94. package/src/index.ts +4 -2
  95. package/src/math/amm.js +369 -0
  96. package/src/math/amm.ts +207 -52
  97. package/src/math/auction.js +42 -0
  98. package/src/math/auction.ts +5 -1
  99. package/src/math/bankBalance.ts +98 -1
  100. package/src/math/conversion.js +11 -0
  101. package/src/math/funding.js +248 -0
  102. package/src/math/margin.ts +124 -0
  103. package/src/math/market.ts +66 -1
  104. package/src/math/oracles.js +26 -0
  105. package/src/math/orders.ts +17 -13
  106. package/src/math/position.ts +63 -9
  107. package/src/math/repeg.js +128 -0
  108. package/src/math/repeg.ts +2 -1
  109. package/src/math/state.js +15 -0
  110. package/src/math/trade.js +253 -0
  111. package/src/math/trade.ts +23 -25
  112. package/src/math/utils.js +0 -1
  113. package/src/mockUSDCFaucet.js +280 -0
  114. package/src/oracles/oracleClientCache.js +19 -0
  115. package/src/oracles/pythClient.js +46 -0
  116. package/src/oracles/quoteAssetOracleClient.js +32 -0
  117. package/src/oracles/switchboardClient.js +69 -0
  118. package/src/oracles/types.js +2 -0
  119. package/src/orderParams.js +20 -0
  120. package/src/orderParams.ts +20 -141
  121. package/src/orders.ts +10 -287
  122. package/src/slot/SlotSubscriber.js +39 -0
  123. package/src/slot/SlotSubscriber.ts +11 -1
  124. package/src/token/index.js +38 -0
  125. package/src/tokenFaucet.js +189 -0
  126. package/src/{mockUSDCFaucet.ts → tokenFaucet.ts} +82 -70
  127. package/src/tx/retryTxSender.ts +11 -3
  128. package/src/tx/types.js +2 -0
  129. package/src/tx/utils.js +17 -0
  130. package/src/tx/utils.ts +1 -1
  131. package/src/types.js +125 -0
  132. package/src/types.ts +155 -17
  133. package/src/userName.js +20 -0
  134. package/src/util/computeUnits.js +21 -11
  135. package/src/util/computeUnits.ts +1 -1
  136. package/src/util/getTokenAddress.js +9 -0
  137. package/src/util/getTokenAddress.ts +18 -0
  138. package/src/util/promiseTimeout.js +14 -0
  139. package/src/util/tps.js +27 -0
  140. package/src/wallet.js +35 -0
  141. package/tests/bn/test.ts +2 -0
  142. package/src/util/computeUnits.js.map +0 -1
@@ -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,
@@ -11,6 +11,10 @@ import {
11
11
  BankAccount,
12
12
  UserBankBalance,
13
13
  MakerInfo,
14
+ TakerInfo,
15
+ OptionalOrderParams,
16
+ DefaultOrderParams,
17
+ OrderType,
14
18
  } from './types';
15
19
  import * as anchor from '@project-serum/anchor';
16
20
  import clearingHouseIDL from './idl/clearing_house.json';
@@ -23,9 +27,13 @@ import {
23
27
  Transaction,
24
28
  TransactionInstruction,
25
29
  AccountMeta,
30
+ Keypair,
31
+ LAMPORTS_PER_SOL,
32
+ Signer,
33
+ SystemProgram,
26
34
  } from '@solana/web3.js';
27
35
 
28
- import { MockUSDCFaucet } from './mockUSDCFaucet';
36
+ import { TokenFaucet } from './tokenFaucet';
29
37
  import { EventEmitter } from 'events';
30
38
  import StrictEventEmitter from 'strict-event-emitter-types';
31
39
  import {
@@ -52,8 +60,8 @@ import { WebSocketClearingHouseAccountSubscriber } from './accounts/webSocketCle
52
60
  import { RetryTxSender } from './tx/retryTxSender';
53
61
  import { ClearingHouseUser } from './clearingHouseUser';
54
62
  import { ClearingHouseUserAccountSubscriptionConfig } from './clearingHouseUserConfig';
55
- import { getMarketOrderParams } from './orderParams';
56
63
  import { getMarketsBanksAndOraclesForSubscription } from './config';
64
+ import { WRAPPED_SOL_MINT } from './constants/banks';
57
65
 
58
66
  /**
59
67
  * # ClearingHouse
@@ -396,6 +404,7 @@ export class ClearingHouse {
396
404
  getRemainingAccounts(params: {
397
405
  writableMarketIndex?: BN;
398
406
  writableBankIndex?: BN;
407
+ readableMarketIndex?: BN;
399
408
  }): AccountMeta[] {
400
409
  const userAccountAndSlot = this.getUserAccountAndSlot();
401
410
  if (!userAccountAndSlot) {
@@ -436,8 +445,7 @@ export class ClearingHouse {
436
445
  marketAccountMap.set(marketIndexNum, {
437
446
  pubkey: marketAccount.pubkey,
438
447
  isSigner: false,
439
- // isWritable: false, // TODO
440
- isWritable: true,
448
+ isWritable: false,
441
449
  });
442
450
  oracleAccountMap.set(marketAccount.pubkey.toString(), {
443
451
  pubkey: marketAccount.amm.oracle,
@@ -447,6 +455,22 @@ export class ClearingHouse {
447
455
  }
448
456
  }
449
457
 
458
+ if (params.readableMarketIndex) {
459
+ const marketAccount = this.getMarketAccount(
460
+ params.readableMarketIndex.toNumber()
461
+ );
462
+ marketAccountMap.set(params.readableMarketIndex.toNumber(), {
463
+ pubkey: marketAccount.pubkey,
464
+ isSigner: false,
465
+ isWritable: true,
466
+ });
467
+ oracleAccountMap.set(marketAccount.amm.oracle.toString(), {
468
+ pubkey: marketAccount.amm.oracle,
469
+ isSigner: false,
470
+ isWritable: false,
471
+ });
472
+ }
473
+
450
474
  if (params.writableMarketIndex) {
451
475
  const marketAccount = this.getMarketAccount(
452
476
  params.writableMarketIndex.toNumber()
@@ -524,6 +548,31 @@ export class ClearingHouse {
524
548
  userId?: number,
525
549
  reduceOnly = false
526
550
  ): Promise<TransactionSignature> {
551
+ const tx = new Transaction();
552
+ const additionalSigners: Array<Signer> = [];
553
+
554
+ const bank = this.getBankAccount(bankIndex);
555
+
556
+ const isSolBank = bank.mint.equals(WRAPPED_SOL_MINT);
557
+
558
+ const authority = this.wallet.publicKey;
559
+
560
+ const createWSOLTokenAccount =
561
+ isSolBank && collateralAccountPublicKey.equals(authority);
562
+
563
+ if (createWSOLTokenAccount) {
564
+ const { ixs, signers, pubkey } =
565
+ await this.getWrappedSolAccountCreationIxs(amount);
566
+
567
+ collateralAccountPublicKey = pubkey;
568
+
569
+ ixs.forEach((ix) => {
570
+ tx.add(ix);
571
+ });
572
+
573
+ signers.forEach((signer) => additionalSigners.push(signer));
574
+ }
575
+
527
576
  const depositCollateralIx = await this.getDepositInstruction(
528
577
  amount,
529
578
  bankIndex,
@@ -533,9 +582,26 @@ export class ClearingHouse {
533
582
  true
534
583
  );
535
584
 
536
- const tx = new Transaction().add(depositCollateralIx);
585
+ tx.add(depositCollateralIx);
586
+
587
+ // Close the wrapped sol account at the end of the transaction
588
+ if (createWSOLTokenAccount) {
589
+ tx.add(
590
+ Token.createCloseAccountInstruction(
591
+ TOKEN_PROGRAM_ID,
592
+ collateralAccountPublicKey,
593
+ authority,
594
+ authority,
595
+ []
596
+ )
597
+ );
598
+ }
537
599
 
538
- const { txSig } = await this.txSender.send(tx);
600
+ const { txSig } = await this.txSender.send(
601
+ tx,
602
+ additionalSigners,
603
+ this.opts
604
+ );
539
605
  return txSig;
540
606
  }
541
607
 
@@ -561,13 +627,19 @@ export class ClearingHouse {
561
627
  writableBankIndex: bankIndex,
562
628
  });
563
629
  } else {
564
- remainingAccounts = [
565
- {
566
- pubkey: this.getBankAccount(bankIndex).pubkey,
630
+ const bankAccount = this.getBankAccount(bankIndex);
631
+ if (!bankAccount.oracle.equals(PublicKey.default)) {
632
+ remainingAccounts.push({
633
+ pubkey: bankAccount.oracle,
567
634
  isSigner: false,
568
- isWritable: true,
569
- },
570
- ];
635
+ isWritable: false,
636
+ });
637
+ }
638
+ remainingAccounts.push({
639
+ pubkey: bankAccount.pubkey,
640
+ isSigner: false,
641
+ isWritable: true,
642
+ });
571
643
  }
572
644
 
573
645
  const bank = this.getBankAccount(bankIndex);
@@ -591,12 +663,119 @@ export class ClearingHouse {
591
663
  );
592
664
  }
593
665
 
666
+ private async checkIfAccountExists(account: PublicKey) {
667
+ try {
668
+ const accountInfo = await this.connection.getAccountInfo(account);
669
+ return accountInfo && true;
670
+ } catch (e) {
671
+ // Doesn't already exist
672
+ return false;
673
+ }
674
+ }
675
+
676
+ private async getSolWithdrawalIxs(
677
+ bankIndex: BN,
678
+ amount: BN
679
+ ): Promise<{
680
+ ixs: anchor.web3.TransactionInstruction[];
681
+ signers: Signer[];
682
+ pubkey: PublicKey;
683
+ }> {
684
+ const result = {
685
+ ixs: [],
686
+ signers: [],
687
+ pubkey: PublicKey.default,
688
+ };
689
+
690
+ // Create a temporary wrapped SOL account to store the SOL that we're withdrawing
691
+
692
+ const authority = this.wallet.publicKey;
693
+
694
+ const { ixs, signers, pubkey } = await this.getWrappedSolAccountCreationIxs(
695
+ amount
696
+ );
697
+ result.pubkey = pubkey;
698
+
699
+ ixs.forEach((ix) => {
700
+ result.ixs.push(ix);
701
+ });
702
+
703
+ signers.forEach((ix) => {
704
+ result.signers.push(ix);
705
+ });
706
+
707
+ const withdrawIx = await this.getWithdrawIx(
708
+ amount,
709
+ bankIndex,
710
+ pubkey,
711
+ true
712
+ );
713
+
714
+ result.ixs.push(withdrawIx);
715
+
716
+ result.ixs.push(
717
+ Token.createCloseAccountInstruction(
718
+ TOKEN_PROGRAM_ID,
719
+ pubkey,
720
+ authority,
721
+ authority,
722
+ []
723
+ )
724
+ );
725
+
726
+ return result;
727
+ }
728
+
729
+ private async getWrappedSolAccountCreationIxs(amount: BN): Promise<{
730
+ ixs: anchor.web3.TransactionInstruction[];
731
+ signers: Signer[];
732
+ pubkey: PublicKey;
733
+ }> {
734
+ const wrappedSolAccount = new Keypair();
735
+
736
+ const result = {
737
+ ixs: [],
738
+ signers: [],
739
+ pubkey: wrappedSolAccount.publicKey,
740
+ };
741
+
742
+ const rentSpaceLamports = new BN(LAMPORTS_PER_SOL / 100);
743
+
744
+ const depositAmountLamports = amount.add(rentSpaceLamports);
745
+
746
+ const authority = this.wallet.publicKey;
747
+
748
+ result.ixs.push(
749
+ SystemProgram.createAccount({
750
+ fromPubkey: authority,
751
+ newAccountPubkey: wrappedSolAccount.publicKey,
752
+ lamports: depositAmountLamports.toNumber(),
753
+ space: 165,
754
+ programId: TOKEN_PROGRAM_ID,
755
+ })
756
+ );
757
+
758
+ result.ixs.push(
759
+ Token.createInitAccountInstruction(
760
+ TOKEN_PROGRAM_ID,
761
+ WRAPPED_SOL_MINT,
762
+ wrappedSolAccount.publicKey,
763
+ authority
764
+ )
765
+ );
766
+
767
+ result.signers.push(wrappedSolAccount);
768
+
769
+ return result;
770
+ }
771
+
594
772
  /**
595
773
  * Creates the Clearing House User account for a user, and deposits some initial collateral
596
- * @param userId
597
- * @param name
598
774
  * @param amount
599
775
  * @param userTokenAccount
776
+ * @param bankIndex
777
+ * @param userId
778
+ * @param name
600
779
  * @param fromUserId
601
780
  * @returns
602
781
  */
@@ -611,6 +790,35 @@ export class ClearingHouse {
611
790
  const [userAccountPublicKey, initializeUserAccountIx] =
612
791
  await this.getInitializeUserInstructions(userId, name);
613
792
 
793
+ const additionalSigners: Array<Signer> = [];
794
+
795
+ const bank = this.getBankAccount(bankIndex);
796
+
797
+ const isSolBank = bank.mint.equals(WRAPPED_SOL_MINT);
798
+
799
+ const tx = new Transaction();
800
+
801
+ const authority = this.wallet.publicKey;
802
+
803
+ const createWSOLTokenAccount =
804
+ isSolBank && userTokenAccount.equals(authority);
805
+
806
+ if (createWSOLTokenAccount) {
807
+ const {
808
+ ixs: startIxs,
809
+ signers,
810
+ pubkey,
811
+ } = await this.getWrappedSolAccountCreationIxs(amount);
812
+
813
+ userTokenAccount = pubkey;
814
+
815
+ startIxs.forEach((ix) => {
816
+ tx.add(ix);
817
+ });
818
+
819
+ signers.forEach((signer) => additionalSigners.push(signer));
820
+ }
821
+
614
822
  const depositCollateralIx =
615
823
  fromUserId != null
616
824
  ? await this.getTransferDepositIx(amount, bankIndex, fromUserId, userId)
@@ -623,11 +831,26 @@ export class ClearingHouse {
623
831
  false
624
832
  );
625
833
 
626
- const tx = new Transaction()
627
- .add(initializeUserAccountIx)
628
- .add(depositCollateralIx);
834
+ tx.add(initializeUserAccountIx).add(depositCollateralIx);
835
+
836
+ // Close the wrapped sol account at the end of the transaction
837
+ if (createWSOLTokenAccount) {
838
+ tx.add(
839
+ Token.createCloseAccountInstruction(
840
+ TOKEN_PROGRAM_ID,
841
+ userTokenAccount,
842
+ authority,
843
+ authority,
844
+ []
845
+ )
846
+ );
847
+ }
629
848
 
630
- const { txSig } = await this.txSender.send(tx, []);
849
+ const { txSig } = await this.txSender.send(
850
+ tx,
851
+ additionalSigners,
852
+ this.opts
853
+ );
631
854
 
632
855
  return [txSig, userAccountPublicKey];
633
856
  }
@@ -635,11 +858,12 @@ export class ClearingHouse {
635
858
  public async initializeUserAccountForDevnet(
636
859
  userId = 0,
637
860
  name = DEFAULT_USER_NAME,
638
- mockUSDCFaucet: MockUSDCFaucet,
861
+ bankIndex: BN,
862
+ tokenFaucet: TokenFaucet,
639
863
  amount: BN
640
864
  ): Promise<[TransactionSignature, PublicKey]> {
641
865
  const [associateTokenPublicKey, createAssociatedAccountIx, mintToIx] =
642
- await mockUSDCFaucet.createAssociatedTokenAccountAndMintToInstructions(
866
+ await tokenFaucet.createAssociatedTokenAccountAndMintToInstructions(
643
867
  this.wallet.publicKey,
644
868
  amount
645
869
  );
@@ -649,7 +873,7 @@ export class ClearingHouse {
649
873
 
650
874
  const depositCollateralIx = await this.getDepositInstruction(
651
875
  amount,
652
- new BN(0),
876
+ bankIndex,
653
877
  associateTokenPublicKey,
654
878
  userId,
655
879
  false,
@@ -673,16 +897,56 @@ export class ClearingHouse {
673
897
  userTokenAccount: PublicKey,
674
898
  reduceOnly = false
675
899
  ): Promise<TransactionSignature> {
676
- const { txSig } = await this.txSender.send(
677
- wrapInTx(
678
- await this.getWithdrawIx(
679
- amount,
680
- bankIndex,
900
+ const tx = new Transaction();
901
+ const additionalSigners: Array<Signer> = [];
902
+
903
+ const bank = this.getBankAccount(bankIndex);
904
+
905
+ const isSolBank = bank.mint.equals(WRAPPED_SOL_MINT);
906
+
907
+ const authority = this.wallet.publicKey;
908
+
909
+ const createWSOLTokenAccount =
910
+ isSolBank && userTokenAccount.equals(authority);
911
+
912
+ if (createWSOLTokenAccount) {
913
+ const { ixs, signers, pubkey } =
914
+ await this.getWrappedSolAccountCreationIxs(amount);
915
+
916
+ userTokenAccount = pubkey;
917
+
918
+ ixs.forEach((ix) => {
919
+ tx.add(ix);
920
+ });
921
+
922
+ signers.forEach((signer) => additionalSigners.push(signer));
923
+ }
924
+
925
+ const withdrawCollateral = await this.getWithdrawIx(
926
+ amount,
927
+ bank.bankIndex,
928
+ userTokenAccount,
929
+ reduceOnly
930
+ );
931
+
932
+ tx.add(withdrawCollateral);
933
+
934
+ // Close the wrapped sol account at the end of the transaction
935
+ if (createWSOLTokenAccount) {
936
+ tx.add(
937
+ Token.createCloseAccountInstruction(
938
+ TOKEN_PROGRAM_ID,
681
939
  userTokenAccount,
682
- reduceOnly
940
+ authority,
941
+ authority,
942
+ []
683
943
  )
684
- ),
685
- [],
944
+ );
945
+ }
946
+
947
+ const { txSig } = await this.txSender.send(
948
+ tx,
949
+ additionalSigners,
686
950
  this.opts
687
951
  );
688
952
  return txSig;
@@ -798,20 +1062,17 @@ export class ClearingHouse {
798
1062
  marketIndex: BN,
799
1063
  limitPrice?: BN
800
1064
  ): Promise<TransactionSignature> {
801
- return await this.placeAndTake(
802
- getMarketOrderParams(
803
- marketIndex,
804
- direction,
805
- ZERO,
806
- amount,
807
- false,
808
- limitPrice
809
- )
810
- );
1065
+ return await this.placeAndTake({
1066
+ orderType: OrderType.MARKET,
1067
+ marketIndex,
1068
+ direction,
1069
+ baseAssetAmount: amount,
1070
+ price: limitPrice,
1071
+ });
811
1072
  }
812
1073
 
813
1074
  public async placeOrder(
814
- orderParams: OrderParams
1075
+ orderParams: OptionalOrderParams
815
1076
  ): Promise<TransactionSignature> {
816
1077
  const { txSig, slot } = await this.txSender.send(
817
1078
  wrapInTx(await this.getPlaceOrderIx(orderParams)),
@@ -822,16 +1083,18 @@ export class ClearingHouse {
822
1083
  return txSig;
823
1084
  }
824
1085
 
1086
+ getOrderParams(optionalOrderParams: OptionalOrderParams): OrderParams {
1087
+ return Object.assign({}, DefaultOrderParams, optionalOrderParams);
1088
+ }
1089
+
825
1090
  public async getPlaceOrderIx(
826
- orderParams: OrderParams
1091
+ orderParams: OptionalOrderParams
827
1092
  ): Promise<TransactionInstruction> {
1093
+ orderParams = this.getOrderParams(orderParams);
828
1094
  const userAccountPublicKey = await this.getUserAccountPublicKey();
829
1095
 
830
- const priceOracle = this.getMarketAccount(orderParams.marketIndex).amm
831
- .oracle;
832
-
833
1096
  const remainingAccounts = this.getRemainingAccounts({
834
- writableMarketIndex: orderParams.marketIndex,
1097
+ readableMarketIndex: orderParams.marketIndex,
835
1098
  });
836
1099
 
837
1100
  return await this.program.instruction.placeOrder(orderParams, {
@@ -839,38 +1102,11 @@ export class ClearingHouse {
839
1102
  state: await this.getStatePublicKey(),
840
1103
  user: userAccountPublicKey,
841
1104
  authority: this.wallet.publicKey,
842
- oracle: priceOracle,
843
1105
  },
844
1106
  remainingAccounts,
845
1107
  });
846
1108
  }
847
1109
 
848
- public async expireOrders(
849
- userAccountPublicKey: PublicKey
850
- ): Promise<TransactionSignature> {
851
- const { txSig } = await this.txSender.send(
852
- wrapInTx(await this.getExpireOrdersIx(userAccountPublicKey)),
853
- [],
854
- this.opts
855
- );
856
- return txSig;
857
- }
858
-
859
- public async getExpireOrdersIx(
860
- userAccountPublicKey: PublicKey
861
- ): Promise<TransactionInstruction> {
862
- const fillerPublicKey = await this.getUserAccountPublicKey();
863
-
864
- return await this.program.instruction.expireOrders({
865
- accounts: {
866
- state: await this.getStatePublicKey(),
867
- filler: fillerPublicKey,
868
- user: userAccountPublicKey,
869
- authority: this.wallet.publicKey,
870
- },
871
- });
872
- }
873
-
874
1110
  public async updateAMMs(marketIndexes: BN[]): Promise<TransactionSignature> {
875
1111
  const { txSig } = await this.txSender.send(
876
1112
  wrapInTx(await this.getUpdateAMMsIx(marketIndexes)),
@@ -914,7 +1150,7 @@ export class ClearingHouse {
914
1150
  });
915
1151
  }
916
1152
 
917
- public async cancelOrder(orderId: BN): Promise<TransactionSignature> {
1153
+ public async cancelOrder(orderId?: BN): Promise<TransactionSignature> {
918
1154
  const { txSig } = await this.txSender.send(
919
1155
  wrapInTx(await this.getCancelOrderIx(orderId)),
920
1156
  [],
@@ -923,20 +1159,16 @@ export class ClearingHouse {
923
1159
  return txSig;
924
1160
  }
925
1161
 
926
- public async getCancelOrderIx(orderId: BN): Promise<TransactionInstruction> {
1162
+ public async getCancelOrderIx(orderId?: BN): Promise<TransactionInstruction> {
927
1163
  const userAccountPublicKey = await this.getUserAccountPublicKey();
928
1164
 
929
- const order = this.getOrder(orderId);
930
- const oracle = this.getMarketAccount(order.marketIndex).amm.oracle;
931
-
932
1165
  const remainingAccounts = this.getRemainingAccounts({});
933
1166
 
934
- return await this.program.instruction.cancelOrder(orderId, {
1167
+ return await this.program.instruction.cancelOrder(orderId ?? null, {
935
1168
  accounts: {
936
1169
  state: await this.getStatePublicKey(),
937
1170
  user: userAccountPublicKey,
938
1171
  authority: this.wallet.publicKey,
939
- oracle,
940
1172
  },
941
1173
  remainingAccounts,
942
1174
  });
@@ -974,36 +1206,107 @@ export class ClearingHouse {
974
1206
  });
975
1207
  }
976
1208
 
977
- public async cancelAllOrders(
978
- bestEffort?: boolean
1209
+ public async fillOrder(
1210
+ userAccountPublicKey: PublicKey,
1211
+ user: UserAccount,
1212
+ order?: Order,
1213
+ makerInfo?: MakerInfo
979
1214
  ): Promise<TransactionSignature> {
980
1215
  const { txSig } = await this.txSender.send(
981
- wrapInTx(await this.getCancelAllOrdersIx(bestEffort)),
1216
+ wrapInTx(
1217
+ await this.getFillOrderIx(userAccountPublicKey, user, order, makerInfo)
1218
+ ),
982
1219
  [],
983
1220
  this.opts
984
1221
  );
985
1222
  return txSig;
986
1223
  }
987
1224
 
988
- public async getCancelAllOrdersIx(
989
- bestEffort?: boolean
1225
+ public async getFillOrderIx(
1226
+ userAccountPublicKey: PublicKey,
1227
+ userAccount: UserAccount,
1228
+ order: Order,
1229
+ makerInfo?: MakerInfo
990
1230
  ): Promise<TransactionInstruction> {
991
- const userAccountPublicKey = await this.getUserAccountPublicKey();
1231
+ const fillerPublicKey = await this.getUserAccountPublicKey();
992
1232
 
993
- const remainingAccounts = this.getRemainingAccounts({});
1233
+ const marketIndex = order.marketIndex;
1234
+ const marketAccount = this.getMarketAccount(marketIndex);
1235
+
1236
+ const oracleAccountMap = new Map<string, AccountMeta>();
1237
+ const bankAccountMap = new Map<number, AccountMeta>();
1238
+ const marketAccountMap = new Map<number, AccountMeta>();
1239
+
1240
+ for (const bankBalance of userAccount.bankBalances) {
1241
+ if (!bankBalance.balance.eq(ZERO)) {
1242
+ const bankAccount = this.getBankAccount(bankBalance.bankIndex);
1243
+ bankAccountMap.set(bankBalance.bankIndex.toNumber(), {
1244
+ pubkey: bankAccount.pubkey,
1245
+ isSigner: false,
1246
+ isWritable: false,
1247
+ });
1248
+
1249
+ if (!bankAccount.oracle.equals(PublicKey.default)) {
1250
+ oracleAccountMap.set(bankAccount.oracle.toString(), {
1251
+ pubkey: bankAccount.oracle,
1252
+ isSigner: false,
1253
+ isWritable: false,
1254
+ });
1255
+ }
1256
+ }
1257
+ }
1258
+
1259
+ for (const position of userAccount.positions) {
1260
+ if (
1261
+ !positionIsAvailable(position) &&
1262
+ !position.marketIndex.eq(order.marketIndex)
1263
+ ) {
1264
+ const market = this.getMarketAccount(position.marketIndex);
1265
+ marketAccountMap.set(position.marketIndex.toNumber(), {
1266
+ pubkey: market.pubkey,
1267
+ isWritable: false,
1268
+ isSigner: false,
1269
+ });
1270
+ oracleAccountMap.set(market.amm.oracle.toString(), {
1271
+ pubkey: market.amm.oracle,
1272
+ isWritable: false,
1273
+ isSigner: false,
1274
+ });
1275
+ }
1276
+ }
1277
+
1278
+ marketAccountMap.set(marketIndex.toNumber(), {
1279
+ pubkey: marketAccount.pubkey,
1280
+ isWritable: true,
1281
+ isSigner: false,
1282
+ });
1283
+ oracleAccountMap.set(marketAccount.amm.oracle.toString(), {
1284
+ pubkey: marketAccount.amm.oracle,
1285
+ isWritable: false,
1286
+ isSigner: false,
1287
+ });
1288
+
1289
+ const remainingAccounts = [
1290
+ ...oracleAccountMap.values(),
1291
+ ...bankAccountMap.values(),
1292
+ ...marketAccountMap.values(),
1293
+ ];
994
1294
 
995
- for (const order of this.getUserAccount().orders) {
996
- const oracle = this.getMarketAccount(order.marketIndex).amm.oracle;
1295
+ if (makerInfo) {
997
1296
  remainingAccounts.push({
998
- pubkey: oracle,
999
- isWritable: false,
1297
+ pubkey: makerInfo.maker,
1298
+ isWritable: true,
1000
1299
  isSigner: false,
1001
1300
  });
1002
1301
  }
1003
1302
 
1004
- return await this.program.instruction.cancelAllOrders(bestEffort, {
1303
+ const orderId = order.orderId;
1304
+ const makerOrderId = makerInfo ? makerInfo.order.orderId : null;
1305
+
1306
+ return await this.program.instruction.fillOrder(orderId, makerOrderId, {
1005
1307
  accounts: {
1006
1308
  state: await this.getStatePublicKey(),
1309
+ filler: fillerPublicKey,
1007
1310
  user: userAccountPublicKey,
1008
1311
  authority: this.wallet.publicKey,
1009
1312
  },
@@ -1011,153 +1314,102 @@ export class ClearingHouse {
1011
1314
  });
1012
1315
  }
1013
1316
 
1014
- public async cancelOrdersByMarketAndSide(
1015
- bestEffort?: boolean,
1016
- marketIndexOnly?: BN,
1017
- directionOnly?: PositionDirection
1317
+ public async triggerOrder(
1318
+ userAccountPublicKey: PublicKey,
1319
+ user: UserAccount,
1320
+ order: Order
1018
1321
  ): Promise<TransactionSignature> {
1019
1322
  const { txSig } = await this.txSender.send(
1020
- wrapInTx(
1021
- await this.getCancelOrdersByMarketAndSideIx(
1022
- bestEffort,
1023
- marketIndexOnly,
1024
- directionOnly
1025
- )
1026
- ),
1323
+ wrapInTx(await this.getTriggerOrderIx(userAccountPublicKey, user, order)),
1027
1324
  [],
1028
1325
  this.opts
1029
1326
  );
1030
1327
  return txSig;
1031
1328
  }
1032
1329
 
1033
- public async getCancelOrdersByMarketAndSideIx(
1034
- bestEffort?: boolean,
1035
- marketIndexOnly?: BN,
1036
- directionOnly?: PositionDirection
1330
+ public async getTriggerOrderIx(
1331
+ userAccountPublicKey: PublicKey,
1332
+ userAccount: UserAccount,
1333
+ order: Order
1037
1334
  ): Promise<TransactionInstruction> {
1038
- const userAccountPublicKey = await this.getUserAccountPublicKey();
1039
-
1040
- const remainingAccounts = this.getRemainingAccounts({});
1335
+ const fillerPublicKey = await this.getUserAccountPublicKey();
1041
1336
 
1042
- for (const order of this.getUserAccount().orders) {
1043
- const oracle = this.getMarketAccount(order.marketIndex).amm.oracle;
1044
- remainingAccounts.push({
1045
- pubkey: oracle,
1046
- isWritable: false,
1047
- isSigner: false,
1048
- });
1049
- }
1050
-
1051
- return await this.program.instruction.cancelOrdersByMarketAndSide(
1052
- bestEffort,
1053
- marketIndexOnly,
1054
- directionOnly,
1055
- {
1056
- accounts: {
1057
- state: await this.getStatePublicKey(),
1058
- user: userAccountPublicKey,
1059
- authority: this.wallet.publicKey,
1060
- },
1061
- remainingAccounts,
1062
- }
1063
- );
1064
- }
1337
+ const marketIndex = order.marketIndex;
1338
+ const marketAccount = this.getMarketAccount(marketIndex);
1065
1339
 
1066
- public async fillOrder(
1067
- userAccountPublicKey: PublicKey,
1068
- user: UserAccount,
1069
- order: Order,
1070
- makerInfo?: MakerInfo
1071
- ): Promise<TransactionSignature> {
1072
- const { txSig } = await this.txSender.send(
1073
- wrapInTx(
1074
- await this.getFillOrderIx(userAccountPublicKey, user, order, makerInfo)
1075
- ),
1076
- [],
1077
- this.opts
1078
- );
1079
- return txSig;
1080
- }
1340
+ const oracleAccountMap = new Map<string, AccountMeta>();
1341
+ const bankAccountMap = new Map<number, AccountMeta>();
1342
+ const marketAccountMap = new Map<number, AccountMeta>();
1081
1343
 
1082
- public async getFillOrderIx(
1083
- userAccountPublicKey: PublicKey,
1084
- userAccount: UserAccount,
1085
- order: Order,
1086
- makerInfo?: MakerInfo
1087
- ): Promise<TransactionInstruction> {
1088
- const fillerPublicKey = await this.getUserAccountPublicKey();
1344
+ for (const bankBalance of userAccount.bankBalances) {
1345
+ if (!bankBalance.balance.eq(ZERO)) {
1346
+ const bankAccount = this.getBankAccount(bankBalance.bankIndex);
1347
+ bankAccountMap.set(bankBalance.bankIndex.toNumber(), {
1348
+ pubkey: bankAccount.pubkey,
1349
+ isSigner: false,
1350
+ isWritable: false,
1351
+ });
1089
1352
 
1090
- const marketIndex = order.marketIndex;
1091
- const marketAccount = this.getMarketAccount(marketIndex);
1092
- const oracle = marketAccount.amm.oracle;
1353
+ if (!bankAccount.oracle.equals(PublicKey.default)) {
1354
+ oracleAccountMap.set(bankAccount.oracle.toString(), {
1355
+ pubkey: bankAccount.oracle,
1356
+ isSigner: false,
1357
+ isWritable: false,
1358
+ });
1359
+ }
1360
+ }
1361
+ }
1093
1362
 
1094
- const bankAccountInfos = [
1095
- {
1096
- pubkey: this.getQuoteAssetBankAccount().pubkey,
1097
- isSigner: false,
1098
- isWritable: true,
1099
- },
1100
- ];
1101
- const marketAccountInfos = [
1102
- {
1103
- pubkey: marketAccount.pubkey,
1104
- isWritable: true,
1105
- isSigner: false,
1106
- },
1107
- ];
1108
- const oracleAccountInfos = [
1109
- {
1110
- pubkey: marketAccount.amm.oracle,
1111
- isWritable: false,
1112
- isSigner: false,
1113
- },
1114
- ];
1115
1363
  for (const position of userAccount.positions) {
1116
1364
  if (
1117
1365
  !positionIsAvailable(position) &&
1118
1366
  !position.marketIndex.eq(order.marketIndex)
1119
1367
  ) {
1120
1368
  const market = this.getMarketAccount(position.marketIndex);
1121
- marketAccountInfos.push({
1369
+ marketAccountMap.set(position.marketIndex.toNumber(), {
1122
1370
  pubkey: market.pubkey,
1123
1371
  isWritable: false,
1124
1372
  isSigner: false,
1125
1373
  });
1126
- oracleAccountInfos.push({
1374
+ oracleAccountMap.set(market.amm.oracle.toString(), {
1127
1375
  pubkey: market.amm.oracle,
1128
1376
  isWritable: false,
1129
1377
  isSigner: false,
1130
1378
  });
1131
1379
  }
1132
1380
  }
1133
- const remainingAccounts = oracleAccountInfos.concat(
1134
- bankAccountInfos.concat(marketAccountInfos)
1135
- );
1136
1381
 
1137
- if (makerInfo) {
1138
- remainingAccounts.push({
1139
- pubkey: makerInfo.maker,
1140
- isWritable: true,
1141
- isSigner: false,
1142
- });
1143
- }
1382
+ marketAccountMap.set(marketIndex.toNumber(), {
1383
+ pubkey: marketAccount.pubkey,
1384
+ isWritable: true,
1385
+ isSigner: false,
1386
+ });
1387
+ oracleAccountMap.set(marketAccount.amm.oracle.toString(), {
1388
+ pubkey: marketAccount.amm.oracle,
1389
+ isWritable: false,
1390
+ isSigner: false,
1391
+ });
1392
+
1393
+ const remainingAccounts = [
1394
+ ...oracleAccountMap.values(),
1395
+ ...bankAccountMap.values(),
1396
+ ...marketAccountMap.values(),
1397
+ ];
1144
1398
 
1145
1399
  const orderId = order.orderId;
1146
- const makerOrderId = makerInfo ? makerInfo.order.orderId : null;
1147
- return await this.program.instruction.fillOrder(orderId, makerOrderId, {
1400
+ return await this.program.instruction.triggerOrder(orderId, {
1148
1401
  accounts: {
1149
1402
  state: await this.getStatePublicKey(),
1150
1403
  filler: fillerPublicKey,
1151
1404
  user: userAccountPublicKey,
1152
1405
  authority: this.wallet.publicKey,
1153
- oracle: oracle,
1154
1406
  },
1155
1407
  remainingAccounts,
1156
1408
  });
1157
1409
  }
1158
1410
 
1159
1411
  public async placeAndTake(
1160
- orderParams: OrderParams,
1412
+ orderParams: OptionalOrderParams,
1161
1413
  makerInfo?: MakerInfo
1162
1414
  ): Promise<TransactionSignature> {
1163
1415
  const { txSig, slot } = await this.txSender.send(
@@ -1170,14 +1422,12 @@ export class ClearingHouse {
1170
1422
  }
1171
1423
 
1172
1424
  public async getPlaceAndTakeIx(
1173
- orderParams: OrderParams,
1425
+ orderParams: OptionalOrderParams,
1174
1426
  makerInfo?: MakerInfo
1175
1427
  ): Promise<TransactionInstruction> {
1428
+ orderParams = this.getOrderParams(orderParams);
1176
1429
  const userAccountPublicKey = await this.getUserAccountPublicKey();
1177
1430
 
1178
- const priceOracle = this.getMarketAccount(orderParams.marketIndex).amm
1179
- .oracle;
1180
-
1181
1431
  const remainingAccounts = this.getRemainingAccounts({
1182
1432
  writableMarketIndex: orderParams.marketIndex,
1183
1433
  writableBankIndex: QUOTE_ASSET_BANK_INDEX,
@@ -1201,7 +1451,55 @@ export class ClearingHouse {
1201
1451
  state: await this.getStatePublicKey(),
1202
1452
  user: userAccountPublicKey,
1203
1453
  authority: this.wallet.publicKey,
1204
- oracle: priceOracle,
1454
+ },
1455
+ remainingAccounts,
1456
+ }
1457
+ );
1458
+ }
1459
+
1460
+ public async placeAndMake(
1461
+ orderParams: OptionalOrderParams,
1462
+ takerInfo: TakerInfo
1463
+ ): Promise<TransactionSignature> {
1464
+ const { txSig, slot } = await this.txSender.send(
1465
+ wrapInTx(await this.getPlaceAndMakeIx(orderParams, takerInfo)),
1466
+ [],
1467
+ this.opts
1468
+ );
1469
+
1470
+ this.marketLastSlotCache.set(orderParams.marketIndex.toNumber(), slot);
1471
+
1472
+ return txSig;
1473
+ }
1474
+
1475
+ public async getPlaceAndMakeIx(
1476
+ orderParams: OptionalOrderParams,
1477
+ takerInfo: TakerInfo
1478
+ ): Promise<TransactionInstruction> {
1479
+ orderParams = this.getOrderParams(orderParams);
1480
+ const userAccountPublicKey = await this.getUserAccountPublicKey();
1481
+
1482
+ const remainingAccounts = this.getRemainingAccounts({
1483
+ writableMarketIndex: orderParams.marketIndex,
1484
+ writableBankIndex: QUOTE_ASSET_BANK_INDEX,
1485
+ });
1486
+
1487
+ const takerOrderId = takerInfo!.order!.orderId;
1488
+ remainingAccounts.push({
1489
+ pubkey: takerInfo.taker,
1490
+ isSigner: false,
1491
+ isWritable: true,
1492
+ });
1493
+
1494
+ return await this.program.instruction.placeAndMake(
1495
+ orderParams,
1496
+ takerOrderId,
1497
+ {
1498
+ accounts: {
1499
+ state: await this.getStatePublicKey(),
1500
+ user: userAccountPublicKey,
1501
+ taker: takerInfo.taker,
1502
+ authority: this.wallet.publicKey,
1205
1503
  },
1206
1504
  remainingAccounts,
1207
1505
  }
@@ -1219,16 +1517,13 @@ export class ClearingHouse {
1219
1517
  throw Error(`No position in market ${marketIndex.toString()}`);
1220
1518
  }
1221
1519
 
1222
- return await this.placeAndTake(
1223
- getMarketOrderParams(
1224
- marketIndex,
1225
- findDirectionToClose(userPosition),
1226
- ZERO,
1227
- userPosition.baseAssetAmount,
1228
- true,
1229
- undefined
1230
- )
1231
- );
1520
+ return await this.placeAndTake({
1521
+ orderType: OrderType.MARKET,
1522
+ marketIndex,
1523
+ direction: findDirectionToClose(userPosition),
1524
+ baseAssetAmount: userPosition.baseAssetAmount,
1525
+ reduceOnly: true,
1526
+ });
1232
1527
  }
1233
1528
 
1234
1529
  public async settlePNLs(
@@ -1287,7 +1582,7 @@ export class ClearingHouse {
1287
1582
  const market = this.getMarketAccount(position.marketIndex);
1288
1583
  marketAccountMap.set(position.marketIndex.toNumber(), {
1289
1584
  pubkey: market.pubkey,
1290
- isWritable: true, // TODO
1585
+ isWritable: false,
1291
1586
  isSigner: false,
1292
1587
  });
1293
1588
  oracleAccountMap.set(market.amm.oracle.toString(), {
@@ -1350,72 +1645,359 @@ export class ClearingHouse {
1350
1645
  });
1351
1646
  }
1352
1647
 
1353
- public async liquidate(
1354
- liquidateeUserAccountPublicKey: PublicKey
1648
+ public async liquidatePerp(
1649
+ userAccountPublicKey: PublicKey,
1650
+ userAccount: UserAccount,
1651
+ marketIndex: BN,
1652
+ maxBaseAssetAmount: BN
1355
1653
  ): Promise<TransactionSignature> {
1356
1654
  const { txSig } = await this.txSender.send(
1357
- wrapInTx(await this.getLiquidateIx(liquidateeUserAccountPublicKey)),
1655
+ wrapInTx(
1656
+ await this.getLiquidatePerpIx(
1657
+ userAccountPublicKey,
1658
+ userAccount,
1659
+ marketIndex,
1660
+ maxBaseAssetAmount
1661
+ )
1662
+ ),
1358
1663
  [],
1359
1664
  this.opts
1360
1665
  );
1361
1666
  return txSig;
1362
1667
  }
1363
1668
 
1364
- public async getLiquidateIx(
1365
- liquidateeUserAccountPublicKey: PublicKey
1669
+ public async getLiquidatePerpIx(
1670
+ userAccountPublicKey: PublicKey,
1671
+ userAccount: UserAccount,
1672
+ marketIndex: BN,
1673
+ maxBaseAssetAmount: BN
1366
1674
  ): Promise<TransactionInstruction> {
1367
- const userAccountPublicKey = await this.getUserAccountPublicKey();
1368
- const liquidateeUserAccount = (await this.program.account.user.fetch(
1369
- liquidateeUserAccountPublicKey
1370
- )) as UserAccount;
1675
+ const liquidatorPublicKey = await this.getUserAccountPublicKey();
1676
+
1677
+ const remainingAccounts = this.getRemainingAccountsForLiquidation({
1678
+ writableMarketIndex: marketIndex,
1679
+ userAccount,
1680
+ });
1371
1681
 
1372
- const bankAccountInfos = [
1682
+ return await this.program.instruction.liquidatePerp(
1683
+ marketIndex,
1684
+ maxBaseAssetAmount,
1373
1685
  {
1374
- pubkey: this.getQuoteAssetBankAccount().pubkey,
1375
- isSigner: false,
1376
- isWritable: true,
1377
- },
1378
- ];
1379
- const marketAccountInfos = [];
1380
- const oracleAccountInfos = [];
1686
+ accounts: {
1687
+ state: await this.getStatePublicKey(),
1688
+ authority: this.wallet.publicKey,
1689
+ user: userAccountPublicKey,
1690
+ liquidator: liquidatorPublicKey,
1691
+ },
1692
+ remainingAccounts: remainingAccounts,
1693
+ }
1694
+ );
1695
+ }
1696
+
1697
+ public async liquidateBorrow(
1698
+ userAccountPublicKey: PublicKey,
1699
+ userAccount: UserAccount,
1700
+ assetBankIndex: BN,
1701
+ liabilityBankIndex: BN,
1702
+ maxLiabilityTransfer: BN
1703
+ ): Promise<TransactionSignature> {
1704
+ const { txSig } = await this.txSender.send(
1705
+ wrapInTx(
1706
+ await this.getLiquidateBorrowIx(
1707
+ userAccountPublicKey,
1708
+ userAccount,
1709
+ assetBankIndex,
1710
+ liabilityBankIndex,
1711
+ maxLiabilityTransfer
1712
+ )
1713
+ ),
1714
+ [],
1715
+ this.opts
1716
+ );
1717
+ return txSig;
1718
+ }
1719
+
1720
+ public async getLiquidateBorrowIx(
1721
+ userAccountPublicKey: PublicKey,
1722
+ userAccount: UserAccount,
1723
+ assetBankIndex: BN,
1724
+ liabilityBankIndex: BN,
1725
+ maxLiabilityTransfer: BN
1726
+ ): Promise<TransactionInstruction> {
1727
+ const liquidatorPublicKey = await this.getUserAccountPublicKey();
1728
+
1729
+ const remainingAccounts = this.getRemainingAccountsForLiquidation({
1730
+ userAccount,
1731
+ writableBankIndexes: [liabilityBankIndex, assetBankIndex],
1732
+ });
1733
+
1734
+ return await this.program.instruction.liquidateBorrow(
1735
+ assetBankIndex,
1736
+ liabilityBankIndex,
1737
+ maxLiabilityTransfer,
1738
+ {
1739
+ accounts: {
1740
+ state: await this.getStatePublicKey(),
1741
+ authority: this.wallet.publicKey,
1742
+ user: userAccountPublicKey,
1743
+ liquidator: liquidatorPublicKey,
1744
+ },
1745
+ remainingAccounts: remainingAccounts,
1746
+ }
1747
+ );
1748
+ }
1749
+
1750
+ public async liquidateBorrowForPerpPnl(
1751
+ userAccountPublicKey: PublicKey,
1752
+ userAccount: UserAccount,
1753
+ perpMarketIndex: BN,
1754
+ liabilityBankIndex: BN,
1755
+ maxLiabilityTransfer: BN
1756
+ ): Promise<TransactionSignature> {
1757
+ const { txSig } = await this.txSender.send(
1758
+ wrapInTx(
1759
+ await this.getLiquidateBorrowForPerpPnlIx(
1760
+ userAccountPublicKey,
1761
+ userAccount,
1762
+ perpMarketIndex,
1763
+ liabilityBankIndex,
1764
+ maxLiabilityTransfer
1765
+ )
1766
+ ),
1767
+ [],
1768
+ this.opts
1769
+ );
1770
+ return txSig;
1771
+ }
1772
+
1773
+ public async getLiquidateBorrowForPerpPnlIx(
1774
+ userAccountPublicKey: PublicKey,
1775
+ userAccount: UserAccount,
1776
+ perpMarketIndex: BN,
1777
+ liabilityBankIndex: BN,
1778
+ maxLiabilityTransfer: BN
1779
+ ): Promise<TransactionInstruction> {
1780
+ const liquidatorPublicKey = await this.getUserAccountPublicKey();
1781
+
1782
+ const remainingAccounts = this.getRemainingAccountsForLiquidation({
1783
+ userAccount,
1784
+ writableMarketIndex: perpMarketIndex,
1785
+ writableBankIndexes: [liabilityBankIndex],
1786
+ });
1787
+
1788
+ return await this.program.instruction.liquidateBorrowForPerpPnl(
1789
+ perpMarketIndex,
1790
+ liabilityBankIndex,
1791
+ maxLiabilityTransfer,
1792
+ {
1793
+ accounts: {
1794
+ state: await this.getStatePublicKey(),
1795
+ authority: this.wallet.publicKey,
1796
+ user: userAccountPublicKey,
1797
+ liquidator: liquidatorPublicKey,
1798
+ },
1799
+ remainingAccounts: remainingAccounts,
1800
+ }
1801
+ );
1802
+ }
1803
+
1804
+ public async liquidatePerpPnlForDeposit(
1805
+ userAccountPublicKey: PublicKey,
1806
+ userAccount: UserAccount,
1807
+ perpMarketIndex: BN,
1808
+ assetBankIndex: BN,
1809
+ maxPnlTransfer: BN
1810
+ ): Promise<TransactionSignature> {
1811
+ const { txSig } = await this.txSender.send(
1812
+ wrapInTx(
1813
+ await this.getLiquidatePerpPnlForDepositIx(
1814
+ userAccountPublicKey,
1815
+ userAccount,
1816
+ perpMarketIndex,
1817
+ assetBankIndex,
1818
+ maxPnlTransfer
1819
+ )
1820
+ ),
1821
+ [],
1822
+ this.opts
1823
+ );
1824
+ return txSig;
1825
+ }
1826
+
1827
+ public async getLiquidatePerpPnlForDepositIx(
1828
+ userAccountPublicKey: PublicKey,
1829
+ userAccount: UserAccount,
1830
+ perpMarketIndex: BN,
1831
+ assetBankIndex: BN,
1832
+ maxPnlTransfer: BN
1833
+ ): Promise<TransactionInstruction> {
1834
+ const liquidatorPublicKey = await this.getUserAccountPublicKey();
1835
+
1836
+ const remainingAccounts = this.getRemainingAccountsForLiquidation({
1837
+ userAccount,
1838
+ writableMarketIndex: perpMarketIndex,
1839
+ writableBankIndexes: [assetBankIndex],
1840
+ });
1841
+
1842
+ return await this.program.instruction.liquidatePerpPnlForDeposit(
1843
+ perpMarketIndex,
1844
+ assetBankIndex,
1845
+ maxPnlTransfer,
1846
+ {
1847
+ accounts: {
1848
+ state: await this.getStatePublicKey(),
1849
+ authority: this.wallet.publicKey,
1850
+ user: userAccountPublicKey,
1851
+ liquidator: liquidatorPublicKey,
1852
+ },
1853
+ remainingAccounts: remainingAccounts,
1854
+ }
1855
+ );
1856
+ }
1857
+
1858
+ getRemainingAccountsForLiquidation(params: {
1859
+ userAccount: UserAccount;
1860
+ writableMarketIndex?: BN;
1861
+ writableBankIndexes?: BN[];
1862
+ }): AccountMeta[] {
1863
+ const liquidateeUserAccount = params.userAccount;
1864
+
1865
+ const oracleAccountMap = new Map<string, AccountMeta>();
1866
+ const bankAccountMap = new Map<number, AccountMeta>();
1867
+ const marketAccountMap = new Map<number, AccountMeta>();
1868
+ for (const bankBalance of liquidateeUserAccount.bankBalances) {
1869
+ if (!bankBalance.balance.eq(ZERO)) {
1870
+ const bankAccount = this.getBankAccount(bankBalance.bankIndex);
1871
+ bankAccountMap.set(bankBalance.bankIndex.toNumber(), {
1872
+ pubkey: bankAccount.pubkey,
1873
+ isSigner: false,
1874
+ isWritable: false,
1875
+ });
1876
+
1877
+ if (!bankAccount.oracle.equals(PublicKey.default)) {
1878
+ oracleAccountMap.set(bankAccount.oracle.toString(), {
1879
+ pubkey: bankAccount.oracle,
1880
+ isSigner: false,
1881
+ isWritable: false,
1882
+ });
1883
+ }
1884
+ }
1885
+ }
1381
1886
  for (const position of liquidateeUserAccount.positions) {
1382
1887
  if (!positionIsAvailable(position)) {
1383
1888
  const market = this.getMarketAccount(position.marketIndex);
1384
- const marketPublicKey = await getMarketPublicKey(
1385
- this.program.programId,
1386
- position.marketIndex
1387
- );
1388
- marketAccountInfos.push({
1389
- pubkey: marketPublicKey,
1390
- isWritable: true,
1889
+ marketAccountMap.set(position.marketIndex.toNumber(), {
1890
+ pubkey: market.pubkey,
1891
+ isWritable: false,
1391
1892
  isSigner: false,
1392
1893
  });
1393
- oracleAccountInfos.push({
1894
+ oracleAccountMap.set(market.amm.oracle.toString(), {
1394
1895
  pubkey: market.amm.oracle,
1395
1896
  isWritable: false,
1396
1897
  isSigner: false,
1397
1898
  });
1398
1899
  }
1399
1900
  }
1400
- const remainingAccounts = oracleAccountInfos.concat(
1401
- bankAccountInfos.concat(marketAccountInfos)
1402
- );
1403
1901
 
1404
- const state = this.getStateAccount();
1405
- const quoteAssetBankAccount = this.getQuoteAssetBankAccount();
1406
- return await this.program.instruction.liquidate({
1407
- accounts: {
1408
- state: await this.getStatePublicKey(),
1409
- authority: this.wallet.publicKey,
1410
- user: liquidateeUserAccountPublicKey,
1411
- liquidator: userAccountPublicKey,
1412
- bankVault: quoteAssetBankAccount.vault,
1413
- bankVaultAuthority: quoteAssetBankAccount.vaultAuthority,
1414
- insuranceVault: state.insuranceVault,
1415
- tokenProgram: TOKEN_PROGRAM_ID,
1416
- },
1417
- remainingAccounts: remainingAccounts,
1418
- });
1902
+ const userAccountAndSlot = this.getUserAccountAndSlot();
1903
+ if (!userAccountAndSlot) {
1904
+ throw Error(
1905
+ 'No user account found. Most likely user account does not exist or failed to fetch account'
1906
+ );
1907
+ }
1908
+ const { data: userAccount, slot: lastUserPositionsSlot } =
1909
+ userAccountAndSlot;
1910
+
1911
+ for (const [marketIndexNum, slot] of this.marketLastSlotCache.entries()) {
1912
+ // if cache has more recent slot than user positions account slot, add market to remaining accounts
1913
+ // otherwise remove from slot
1914
+ if (slot > lastUserPositionsSlot) {
1915
+ const marketAccount = this.getMarketAccount(marketIndexNum);
1916
+ marketAccountMap.set(marketIndexNum, {
1917
+ pubkey: marketAccount.pubkey,
1918
+ isSigner: false,
1919
+ isWritable: false,
1920
+ });
1921
+ oracleAccountMap.set(marketAccount.amm.oracle.toString(), {
1922
+ pubkey: marketAccount.amm.oracle,
1923
+ isSigner: false,
1924
+ isWritable: false,
1925
+ });
1926
+ } else {
1927
+ this.marketLastSlotCache.delete(marketIndexNum);
1928
+ }
1929
+ }
1930
+ for (const bankBalance of userAccount.bankBalances) {
1931
+ if (!bankBalance.balance.eq(ZERO)) {
1932
+ const bankAccount = this.getBankAccount(bankBalance.bankIndex);
1933
+ bankAccountMap.set(bankBalance.bankIndex.toNumber(), {
1934
+ pubkey: bankAccount.pubkey,
1935
+ isSigner: false,
1936
+ isWritable: false,
1937
+ });
1938
+
1939
+ if (!bankAccount.oracle.equals(PublicKey.default)) {
1940
+ oracleAccountMap.set(bankAccount.oracle.toString(), {
1941
+ pubkey: bankAccount.oracle,
1942
+ isSigner: false,
1943
+ isWritable: false,
1944
+ });
1945
+ }
1946
+ }
1947
+ }
1948
+ for (const position of userAccount.positions) {
1949
+ if (!positionIsAvailable(position)) {
1950
+ const market = this.getMarketAccount(position.marketIndex);
1951
+ marketAccountMap.set(position.marketIndex.toNumber(), {
1952
+ pubkey: market.pubkey,
1953
+ isWritable: false,
1954
+ isSigner: false,
1955
+ });
1956
+ oracleAccountMap.set(market.amm.oracle.toString(), {
1957
+ pubkey: market.amm.oracle,
1958
+ isWritable: false,
1959
+ isSigner: false,
1960
+ });
1961
+ }
1962
+ }
1963
+
1964
+ if (params.writableMarketIndex) {
1965
+ const market = this.getMarketAccount(params.writableMarketIndex);
1966
+ marketAccountMap.set(market.marketIndex.toNumber(), {
1967
+ pubkey: market.pubkey,
1968
+ isSigner: false,
1969
+ isWritable: true,
1970
+ });
1971
+ oracleAccountMap.set(market.amm.oracle.toString(), {
1972
+ pubkey: market.amm.oracle,
1973
+ isSigner: false,
1974
+ isWritable: false,
1975
+ });
1976
+ }
1977
+
1978
+ if (params.writableBankIndexes) {
1979
+ for (const writableBankIndex of params.writableBankIndexes) {
1980
+ const bank = this.getBankAccount(writableBankIndex);
1981
+ bankAccountMap.set(bank.bankIndex.toNumber(), {
1982
+ pubkey: bank.pubkey,
1983
+ isSigner: false,
1984
+ isWritable: true,
1985
+ });
1986
+ if (!bank.oracle.equals(PublicKey.default)) {
1987
+ oracleAccountMap.set(bank.oracle.toString(), {
1988
+ pubkey: bank.oracle,
1989
+ isSigner: false,
1990
+ isWritable: false,
1991
+ });
1992
+ }
1993
+ }
1994
+ }
1995
+
1996
+ return [
1997
+ ...oracleAccountMap.values(),
1998
+ ...bankAccountMap.values(),
1999
+ ...marketAccountMap.values(),
2000
+ ];
1419
2001
  }
1420
2002
 
1421
2003
  public async updateFundingRate(