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

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 (121) hide show
  1. package/lib/admin.d.ts +8 -5
  2. package/lib/admin.js +43 -11
  3. package/lib/clearingHouse.d.ts +28 -18
  4. package/lib/clearingHouse.js +366 -143
  5. package/lib/clearingHouseUser.d.ts +2 -2
  6. package/lib/clearingHouseUser.js +8 -17
  7. package/lib/config.js +1 -1
  8. package/lib/constants/banks.js +9 -2
  9. package/lib/constants/numericConstants.d.ts +2 -0
  10. package/lib/constants/numericConstants.js +3 -1
  11. package/lib/factory/bigNum.d.ts +8 -2
  12. package/lib/factory/bigNum.js +14 -6
  13. package/lib/idl/clearing_house.json +805 -202
  14. package/lib/idl/{mock_usdc_faucet.json → token_faucet.json} +46 -23
  15. package/lib/index.d.ts +3 -2
  16. package/lib/index.js +7 -2
  17. package/lib/math/amm.d.ts +6 -1
  18. package/lib/math/amm.js +124 -41
  19. package/lib/math/auction.js +4 -1
  20. package/lib/math/orders.d.ts +2 -2
  21. package/lib/math/orders.js +18 -11
  22. package/lib/math/position.js +3 -1
  23. package/lib/math/repeg.js +1 -1
  24. package/lib/math/trade.d.ts +1 -1
  25. package/lib/math/trade.js +7 -10
  26. package/lib/orderParams.d.ts +14 -5
  27. package/lib/orderParams.js +8 -96
  28. package/lib/orders.d.ts +1 -2
  29. package/lib/orders.js +6 -85
  30. package/lib/slot/SlotSubscriber.d.ts +7 -0
  31. package/lib/slot/SlotSubscriber.js +3 -0
  32. package/lib/{mockUSDCFaucet.d.ts → tokenFaucet.d.ts} +7 -5
  33. package/lib/{mockUSDCFaucet.js → tokenFaucet.js} +41 -40
  34. package/lib/tx/utils.js +1 -1
  35. package/lib/types.d.ts +137 -14
  36. package/lib/types.js +53 -1
  37. package/lib/util/computeUnits.js +1 -1
  38. package/package.json +3 -3
  39. package/src/accounts/bulkAccountLoader.js +197 -0
  40. package/src/accounts/bulkUserSubscription.js +33 -0
  41. package/src/accounts/fetch.js +29 -0
  42. package/src/accounts/pollingClearingHouseAccountSubscriber.js +311 -0
  43. package/src/accounts/pollingOracleSubscriber.js +93 -0
  44. package/src/accounts/pollingTokenAccountSubscriber.js +90 -0
  45. package/src/accounts/pollingUserAccountSubscriber.js +132 -0
  46. package/src/accounts/types.js +10 -0
  47. package/src/accounts/utils.js +7 -0
  48. package/src/accounts/webSocketAccountSubscriber.js +93 -0
  49. package/src/accounts/webSocketClearingHouseAccountSubscriber.js +233 -0
  50. package/src/accounts/webSocketUserAccountSubscriber.js +62 -0
  51. package/src/addresses/marketAddresses.js +26 -0
  52. package/src/addresses/pda.js +104 -0
  53. package/src/admin.ts +66 -14
  54. package/src/assert/assert.js +9 -0
  55. package/src/clearingHouse.ts +558 -230
  56. package/src/clearingHouseUser.ts +12 -23
  57. package/src/config.ts +1 -1
  58. package/src/constants/banks.ts +9 -2
  59. package/src/constants/numericConstants.ts +2 -0
  60. package/src/events/eventList.js +77 -0
  61. package/src/events/eventSubscriber.js +139 -0
  62. package/src/events/fetchLogs.js +50 -0
  63. package/src/events/pollingLogProvider.js +64 -0
  64. package/src/events/sort.js +44 -0
  65. package/src/events/txEventCache.js +71 -0
  66. package/src/events/types.js +20 -0
  67. package/src/events/webSocketLogProvider.js +41 -0
  68. package/src/examples/makeTradeExample.js +80 -0
  69. package/src/factory/bigNum.js +364 -0
  70. package/src/factory/bigNum.ts +26 -9
  71. package/src/factory/oracleClient.js +20 -0
  72. package/src/idl/clearing_house.json +805 -202
  73. package/src/idl/{mock_usdc_faucet.json → token_faucet.json} +46 -23
  74. package/src/index.js +100 -0
  75. package/src/index.ts +3 -2
  76. package/src/math/amm.js +369 -0
  77. package/src/math/amm.ts +207 -52
  78. package/src/math/auction.js +42 -0
  79. package/src/math/auction.ts +5 -1
  80. package/src/math/bankBalance.js +75 -0
  81. package/src/math/conversion.js +11 -0
  82. package/src/math/funding.js +248 -0
  83. package/src/math/market.js +57 -0
  84. package/src/math/oracles.js +26 -0
  85. package/src/math/orders.js +110 -0
  86. package/src/math/orders.ts +17 -13
  87. package/src/math/position.js +140 -0
  88. package/src/math/position.ts +5 -1
  89. package/src/math/repeg.js +128 -0
  90. package/src/math/repeg.ts +2 -1
  91. package/src/math/state.js +15 -0
  92. package/src/math/trade.js +253 -0
  93. package/src/math/trade.ts +23 -25
  94. package/src/math/utils.js +0 -1
  95. package/src/mockUSDCFaucet.js +280 -0
  96. package/src/oracles/oracleClientCache.js +19 -0
  97. package/src/oracles/pythClient.js +46 -0
  98. package/src/oracles/quoteAssetOracleClient.js +32 -0
  99. package/src/oracles/switchboardClient.js +69 -0
  100. package/src/oracles/types.js +2 -0
  101. package/src/orderParams.js +20 -0
  102. package/src/orderParams.ts +20 -141
  103. package/src/orders.js +134 -0
  104. package/src/orders.ts +7 -131
  105. package/src/slot/SlotSubscriber.js +39 -0
  106. package/src/slot/SlotSubscriber.ts +11 -1
  107. package/src/token/index.js +38 -0
  108. package/src/{mockUSDCFaucet.ts → tokenFaucet.ts} +48 -59
  109. package/src/tx/retryTxSender.js +188 -0
  110. package/src/tx/types.js +2 -0
  111. package/src/tx/utils.js +17 -0
  112. package/src/tx/utils.ts +1 -1
  113. package/src/types.js +114 -0
  114. package/src/types.ts +132 -16
  115. package/src/userName.js +20 -0
  116. package/src/util/computeUnits.ts +1 -1
  117. package/src/util/promiseTimeout.js +14 -0
  118. package/src/util/tps.js +27 -0
  119. package/src/wallet.js +35 -0
  120. package/src/util/computeUnits.js +0 -17
  121. package/src/util/computeUnits.js.map +0 -1
@@ -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';
@@ -25,7 +29,7 @@ import {
25
29
  AccountMeta,
26
30
  } from '@solana/web3.js';
27
31
 
28
- import { MockUSDCFaucet } from './mockUSDCFaucet';
32
+ import { TokenFaucet } from './tokenFaucet';
29
33
  import { EventEmitter } from 'events';
30
34
  import StrictEventEmitter from 'strict-event-emitter-types';
31
35
  import {
@@ -52,7 +56,6 @@ import { WebSocketClearingHouseAccountSubscriber } from './accounts/webSocketCle
52
56
  import { RetryTxSender } from './tx/retryTxSender';
53
57
  import { ClearingHouseUser } from './clearingHouseUser';
54
58
  import { ClearingHouseUserAccountSubscriptionConfig } from './clearingHouseUserConfig';
55
- import { getMarketOrderParams } from './orderParams';
56
59
  import { getMarketsBanksAndOraclesForSubscription } from './config';
57
60
 
58
61
  /**
@@ -561,13 +564,19 @@ export class ClearingHouse {
561
564
  writableBankIndex: bankIndex,
562
565
  });
563
566
  } else {
564
- remainingAccounts = [
565
- {
566
- pubkey: this.getBankAccount(bankIndex).pubkey,
567
+ const bankAccount = this.getBankAccount(bankIndex);
568
+ if (!bankAccount.oracle.equals(PublicKey.default)) {
569
+ remainingAccounts.push({
570
+ pubkey: bankAccount.oracle,
567
571
  isSigner: false,
568
- isWritable: true,
569
- },
570
- ];
572
+ isWritable: false,
573
+ });
574
+ }
575
+ remainingAccounts.push({
576
+ pubkey: bankAccount.pubkey,
577
+ isSigner: false,
578
+ isWritable: true,
579
+ });
571
580
  }
572
581
 
573
582
  const bank = this.getBankAccount(bankIndex);
@@ -635,11 +644,12 @@ export class ClearingHouse {
635
644
  public async initializeUserAccountForDevnet(
636
645
  userId = 0,
637
646
  name = DEFAULT_USER_NAME,
638
- mockUSDCFaucet: MockUSDCFaucet,
647
+ bankIndex: BN,
648
+ tokenFaucet: TokenFaucet,
639
649
  amount: BN
640
650
  ): Promise<[TransactionSignature, PublicKey]> {
641
651
  const [associateTokenPublicKey, createAssociatedAccountIx, mintToIx] =
642
- await mockUSDCFaucet.createAssociatedTokenAccountAndMintToInstructions(
652
+ await tokenFaucet.createAssociatedTokenAccountAndMintToInstructions(
643
653
  this.wallet.publicKey,
644
654
  amount
645
655
  );
@@ -649,7 +659,7 @@ export class ClearingHouse {
649
659
 
650
660
  const depositCollateralIx = await this.getDepositInstruction(
651
661
  amount,
652
- new BN(0),
662
+ bankIndex,
653
663
  associateTokenPublicKey,
654
664
  userId,
655
665
  false,
@@ -798,20 +808,17 @@ export class ClearingHouse {
798
808
  marketIndex: BN,
799
809
  limitPrice?: BN
800
810
  ): Promise<TransactionSignature> {
801
- return await this.placeAndTake(
802
- getMarketOrderParams(
803
- marketIndex,
804
- direction,
805
- ZERO,
806
- amount,
807
- false,
808
- limitPrice
809
- )
810
- );
811
+ return await this.placeAndTake({
812
+ orderType: OrderType.MARKET,
813
+ marketIndex,
814
+ direction,
815
+ baseAssetAmount: amount,
816
+ price: limitPrice,
817
+ });
811
818
  }
812
819
 
813
820
  public async placeOrder(
814
- orderParams: OrderParams
821
+ orderParams: OptionalOrderParams
815
822
  ): Promise<TransactionSignature> {
816
823
  const { txSig, slot } = await this.txSender.send(
817
824
  wrapInTx(await this.getPlaceOrderIx(orderParams)),
@@ -822,14 +829,16 @@ export class ClearingHouse {
822
829
  return txSig;
823
830
  }
824
831
 
832
+ getOrderParams(optionalOrderParams: OptionalOrderParams): OrderParams {
833
+ return Object.assign({}, DefaultOrderParams, optionalOrderParams);
834
+ }
835
+
825
836
  public async getPlaceOrderIx(
826
- orderParams: OrderParams
837
+ orderParams: OptionalOrderParams
827
838
  ): Promise<TransactionInstruction> {
839
+ orderParams = this.getOrderParams(orderParams);
828
840
  const userAccountPublicKey = await this.getUserAccountPublicKey();
829
841
 
830
- const priceOracle = this.getMarketAccount(orderParams.marketIndex).amm
831
- .oracle;
832
-
833
842
  const remainingAccounts = this.getRemainingAccounts({
834
843
  writableMarketIndex: orderParams.marketIndex,
835
844
  });
@@ -839,38 +848,11 @@ export class ClearingHouse {
839
848
  state: await this.getStatePublicKey(),
840
849
  user: userAccountPublicKey,
841
850
  authority: this.wallet.publicKey,
842
- oracle: priceOracle,
843
851
  },
844
852
  remainingAccounts,
845
853
  });
846
854
  }
847
855
 
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
856
  public async updateAMMs(marketIndexes: BN[]): Promise<TransactionSignature> {
875
857
  const { txSig } = await this.txSender.send(
876
858
  wrapInTx(await this.getUpdateAMMsIx(marketIndexes)),
@@ -914,7 +896,7 @@ export class ClearingHouse {
914
896
  });
915
897
  }
916
898
 
917
- public async cancelOrder(orderId: BN): Promise<TransactionSignature> {
899
+ public async cancelOrder(orderId?: BN): Promise<TransactionSignature> {
918
900
  const { txSig } = await this.txSender.send(
919
901
  wrapInTx(await this.getCancelOrderIx(orderId)),
920
902
  [],
@@ -923,20 +905,16 @@ export class ClearingHouse {
923
905
  return txSig;
924
906
  }
925
907
 
926
- public async getCancelOrderIx(orderId: BN): Promise<TransactionInstruction> {
908
+ public async getCancelOrderIx(orderId?: BN): Promise<TransactionInstruction> {
927
909
  const userAccountPublicKey = await this.getUserAccountPublicKey();
928
910
 
929
- const order = this.getOrder(orderId);
930
- const oracle = this.getMarketAccount(order.marketIndex).amm.oracle;
931
-
932
911
  const remainingAccounts = this.getRemainingAccounts({});
933
912
 
934
- return await this.program.instruction.cancelOrder(orderId, {
913
+ return await this.program.instruction.cancelOrder(orderId ?? null, {
935
914
  accounts: {
936
915
  state: await this.getStatePublicKey(),
937
916
  user: userAccountPublicKey,
938
917
  authority: this.wallet.publicKey,
939
- oracle,
940
918
  },
941
919
  remainingAccounts,
942
920
  });
@@ -974,36 +952,107 @@ export class ClearingHouse {
974
952
  });
975
953
  }
976
954
 
977
- public async cancelAllOrders(
978
- bestEffort?: boolean
955
+ public async fillOrder(
956
+ userAccountPublicKey: PublicKey,
957
+ user: UserAccount,
958
+ order?: Order,
959
+ makerInfo?: MakerInfo
979
960
  ): Promise<TransactionSignature> {
980
961
  const { txSig } = await this.txSender.send(
981
- wrapInTx(await this.getCancelAllOrdersIx(bestEffort)),
962
+ wrapInTx(
963
+ await this.getFillOrderIx(userAccountPublicKey, user, order, makerInfo)
964
+ ),
982
965
  [],
983
966
  this.opts
984
967
  );
985
968
  return txSig;
986
969
  }
987
970
 
988
- public async getCancelAllOrdersIx(
989
- bestEffort?: boolean
971
+ public async getFillOrderIx(
972
+ userAccountPublicKey: PublicKey,
973
+ userAccount: UserAccount,
974
+ order: Order,
975
+ makerInfo?: MakerInfo
990
976
  ): Promise<TransactionInstruction> {
991
- const userAccountPublicKey = await this.getUserAccountPublicKey();
977
+ const fillerPublicKey = await this.getUserAccountPublicKey();
992
978
 
993
- const remainingAccounts = this.getRemainingAccounts({});
979
+ const marketIndex = order.marketIndex;
980
+ const marketAccount = this.getMarketAccount(marketIndex);
981
+
982
+ const oracleAccountMap = new Map<string, AccountMeta>();
983
+ const bankAccountMap = new Map<number, AccountMeta>();
984
+ const marketAccountMap = new Map<number, AccountMeta>();
994
985
 
995
- for (const order of this.getUserAccount().orders) {
996
- const oracle = this.getMarketAccount(order.marketIndex).amm.oracle;
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
+ for (const bankBalance of userAccount.bankBalances) {
998
+ if (!bankBalance.balance.eq(ZERO)) {
999
+ const bankAccount = this.getBankAccount(bankBalance.bankIndex);
1000
+ bankAccountMap.set(bankBalance.bankIndex.toNumber(), {
1001
+ pubkey: bankAccount.pubkey,
1002
+ isSigner: false,
1003
+ isWritable: true,
1004
+ });
1005
+
1006
+ if (!bankAccount.oracle.equals(PublicKey.default)) {
1007
+ oracleAccountMap.set(bankAccount.oracle.toString(), {
1008
+ pubkey: bankAccount.oracle,
1009
+ isSigner: false,
1010
+ isWritable: false,
1011
+ });
1012
+ }
1013
+ }
1014
+ }
1015
+
1016
+ for (const position of userAccount.positions) {
1017
+ if (
1018
+ !positionIsAvailable(position) &&
1019
+ !position.marketIndex.eq(order.marketIndex)
1020
+ ) {
1021
+ const market = this.getMarketAccount(position.marketIndex);
1022
+ marketAccountMap.set(position.marketIndex.toNumber(), {
1023
+ pubkey: market.pubkey,
1024
+ isWritable: true,
1025
+ isSigner: false,
1026
+ });
1027
+ oracleAccountMap.set(market.amm.oracle.toString(), {
1028
+ pubkey: market.amm.oracle,
1029
+ isWritable: false,
1030
+ isSigner: false,
1031
+ });
1032
+ }
1033
+ }
1034
+
1035
+ const remainingAccounts = [
1036
+ ...oracleAccountMap.values(),
1037
+ ...bankAccountMap.values(),
1038
+ ...marketAccountMap.values(),
1039
+ ];
1040
+
1041
+ if (makerInfo) {
997
1042
  remainingAccounts.push({
998
- pubkey: oracle,
999
- isWritable: false,
1043
+ pubkey: makerInfo.maker,
1044
+ isWritable: true,
1000
1045
  isSigner: false,
1001
1046
  });
1002
1047
  }
1003
1048
 
1004
- return await this.program.instruction.cancelAllOrders(bestEffort, {
1049
+ const orderId = order.orderId;
1050
+ const makerOrderId = makerInfo ? makerInfo.order.orderId : null;
1051
+
1052
+ return await this.program.instruction.fillOrder(orderId, makerOrderId, {
1005
1053
  accounts: {
1006
1054
  state: await this.getStatePublicKey(),
1055
+ filler: fillerPublicKey,
1007
1056
  user: userAccountPublicKey,
1008
1057
  authority: this.wallet.publicKey,
1009
1058
  },
@@ -1011,153 +1060,102 @@ export class ClearingHouse {
1011
1060
  });
1012
1061
  }
1013
1062
 
1014
- public async cancelOrdersByMarketAndSide(
1015
- bestEffort?: boolean,
1016
- marketIndexOnly?: BN,
1017
- directionOnly?: PositionDirection
1018
- ): Promise<TransactionSignature> {
1019
- const { txSig } = await this.txSender.send(
1020
- wrapInTx(
1021
- await this.getCancelOrdersByMarketAndSideIx(
1022
- bestEffort,
1023
- marketIndexOnly,
1024
- directionOnly
1025
- )
1026
- ),
1027
- [],
1028
- this.opts
1029
- );
1030
- return txSig;
1031
- }
1032
-
1033
- public async getCancelOrdersByMarketAndSideIx(
1034
- bestEffort?: boolean,
1035
- marketIndexOnly?: BN,
1036
- directionOnly?: PositionDirection
1037
- ): Promise<TransactionInstruction> {
1038
- const userAccountPublicKey = await this.getUserAccountPublicKey();
1039
-
1040
- const remainingAccounts = this.getRemainingAccounts({});
1041
-
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
- }
1065
-
1066
- public async fillOrder(
1063
+ public async triggerOrder(
1067
1064
  userAccountPublicKey: PublicKey,
1068
1065
  user: UserAccount,
1069
- order: Order,
1070
- makerInfo?: MakerInfo
1066
+ order: Order
1071
1067
  ): Promise<TransactionSignature> {
1072
1068
  const { txSig } = await this.txSender.send(
1073
- wrapInTx(
1074
- await this.getFillOrderIx(userAccountPublicKey, user, order, makerInfo)
1075
- ),
1069
+ wrapInTx(await this.getTriggerOrderIx(userAccountPublicKey, user, order)),
1076
1070
  [],
1077
1071
  this.opts
1078
1072
  );
1079
1073
  return txSig;
1080
1074
  }
1081
1075
 
1082
- public async getFillOrderIx(
1076
+ public async getTriggerOrderIx(
1083
1077
  userAccountPublicKey: PublicKey,
1084
1078
  userAccount: UserAccount,
1085
- order: Order,
1086
- makerInfo?: MakerInfo
1079
+ order: Order
1087
1080
  ): Promise<TransactionInstruction> {
1088
1081
  const fillerPublicKey = await this.getUserAccountPublicKey();
1089
1082
 
1090
1083
  const marketIndex = order.marketIndex;
1091
1084
  const marketAccount = this.getMarketAccount(marketIndex);
1092
- const oracle = marketAccount.amm.oracle;
1093
1085
 
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
- ];
1086
+ const oracleAccountMap = new Map<string, AccountMeta>();
1087
+ const bankAccountMap = new Map<number, AccountMeta>();
1088
+ const marketAccountMap = new Map<number, AccountMeta>();
1089
+
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
+ for (const bankBalance of userAccount.bankBalances) {
1102
+ if (!bankBalance.balance.eq(ZERO)) {
1103
+ const bankAccount = this.getBankAccount(bankBalance.bankIndex);
1104
+ bankAccountMap.set(bankBalance.bankIndex.toNumber(), {
1105
+ pubkey: bankAccount.pubkey,
1106
+ isSigner: false,
1107
+ isWritable: true,
1108
+ });
1109
+
1110
+ if (!bankAccount.oracle.equals(PublicKey.default)) {
1111
+ oracleAccountMap.set(bankAccount.oracle.toString(), {
1112
+ pubkey: bankAccount.oracle,
1113
+ isSigner: false,
1114
+ isWritable: false,
1115
+ });
1116
+ }
1117
+ }
1118
+ }
1119
+
1115
1120
  for (const position of userAccount.positions) {
1116
1121
  if (
1117
1122
  !positionIsAvailable(position) &&
1118
1123
  !position.marketIndex.eq(order.marketIndex)
1119
1124
  ) {
1120
1125
  const market = this.getMarketAccount(position.marketIndex);
1121
- marketAccountInfos.push({
1126
+ marketAccountMap.set(position.marketIndex.toNumber(), {
1122
1127
  pubkey: market.pubkey,
1123
- isWritable: false,
1128
+ isWritable: true,
1124
1129
  isSigner: false,
1125
1130
  });
1126
- oracleAccountInfos.push({
1131
+ oracleAccountMap.set(market.amm.oracle.toString(), {
1127
1132
  pubkey: market.amm.oracle,
1128
1133
  isWritable: false,
1129
1134
  isSigner: false,
1130
1135
  });
1131
1136
  }
1132
1137
  }
1133
- const remainingAccounts = oracleAccountInfos.concat(
1134
- bankAccountInfos.concat(marketAccountInfos)
1135
- );
1136
1138
 
1137
- if (makerInfo) {
1138
- remainingAccounts.push({
1139
- pubkey: makerInfo.maker,
1140
- isWritable: true,
1141
- isSigner: false,
1142
- });
1143
- }
1139
+ const remainingAccounts = [
1140
+ ...oracleAccountMap.values(),
1141
+ ...bankAccountMap.values(),
1142
+ ...marketAccountMap.values(),
1143
+ ];
1144
1144
 
1145
1145
  const orderId = order.orderId;
1146
- const makerOrderId = makerInfo ? makerInfo.order.orderId : null;
1147
- return await this.program.instruction.fillOrder(orderId, makerOrderId, {
1146
+ return await this.program.instruction.triggerOrder(orderId, {
1148
1147
  accounts: {
1149
1148
  state: await this.getStatePublicKey(),
1150
1149
  filler: fillerPublicKey,
1151
1150
  user: userAccountPublicKey,
1152
1151
  authority: this.wallet.publicKey,
1153
- oracle: oracle,
1154
1152
  },
1155
1153
  remainingAccounts,
1156
1154
  });
1157
1155
  }
1158
1156
 
1159
1157
  public async placeAndTake(
1160
- orderParams: OrderParams,
1158
+ orderParams: OptionalOrderParams,
1161
1159
  makerInfo?: MakerInfo
1162
1160
  ): Promise<TransactionSignature> {
1163
1161
  const { txSig, slot } = await this.txSender.send(
@@ -1170,14 +1168,12 @@ export class ClearingHouse {
1170
1168
  }
1171
1169
 
1172
1170
  public async getPlaceAndTakeIx(
1173
- orderParams: OrderParams,
1171
+ orderParams: OptionalOrderParams,
1174
1172
  makerInfo?: MakerInfo
1175
1173
  ): Promise<TransactionInstruction> {
1174
+ orderParams = this.getOrderParams(orderParams);
1176
1175
  const userAccountPublicKey = await this.getUserAccountPublicKey();
1177
1176
 
1178
- const priceOracle = this.getMarketAccount(orderParams.marketIndex).amm
1179
- .oracle;
1180
-
1181
1177
  const remainingAccounts = this.getRemainingAccounts({
1182
1178
  writableMarketIndex: orderParams.marketIndex,
1183
1179
  writableBankIndex: QUOTE_ASSET_BANK_INDEX,
@@ -1201,7 +1197,55 @@ export class ClearingHouse {
1201
1197
  state: await this.getStatePublicKey(),
1202
1198
  user: userAccountPublicKey,
1203
1199
  authority: this.wallet.publicKey,
1204
- oracle: priceOracle,
1200
+ },
1201
+ remainingAccounts,
1202
+ }
1203
+ );
1204
+ }
1205
+
1206
+ public async placeAndMake(
1207
+ orderParams: OptionalOrderParams,
1208
+ takerInfo: TakerInfo
1209
+ ): Promise<TransactionSignature> {
1210
+ const { txSig, slot } = await this.txSender.send(
1211
+ wrapInTx(await this.getPlaceAndMakeIx(orderParams, takerInfo)),
1212
+ [],
1213
+ this.opts
1214
+ );
1215
+
1216
+ this.marketLastSlotCache.set(orderParams.marketIndex.toNumber(), slot);
1217
+
1218
+ return txSig;
1219
+ }
1220
+
1221
+ public async getPlaceAndMakeIx(
1222
+ orderParams: OptionalOrderParams,
1223
+ takerInfo: TakerInfo
1224
+ ): Promise<TransactionInstruction> {
1225
+ orderParams = this.getOrderParams(orderParams);
1226
+ const userAccountPublicKey = await this.getUserAccountPublicKey();
1227
+
1228
+ const remainingAccounts = this.getRemainingAccounts({
1229
+ writableMarketIndex: orderParams.marketIndex,
1230
+ writableBankIndex: QUOTE_ASSET_BANK_INDEX,
1231
+ });
1232
+
1233
+ const takerOrderId = takerInfo!.order!.orderId;
1234
+ remainingAccounts.push({
1235
+ pubkey: takerInfo.taker,
1236
+ isSigner: false,
1237
+ isWritable: true,
1238
+ });
1239
+
1240
+ return await this.program.instruction.placeAndMake(
1241
+ orderParams,
1242
+ takerOrderId,
1243
+ {
1244
+ accounts: {
1245
+ state: await this.getStatePublicKey(),
1246
+ user: userAccountPublicKey,
1247
+ taker: takerInfo.taker,
1248
+ authority: this.wallet.publicKey,
1205
1249
  },
1206
1250
  remainingAccounts,
1207
1251
  }
@@ -1219,16 +1263,13 @@ export class ClearingHouse {
1219
1263
  throw Error(`No position in market ${marketIndex.toString()}`);
1220
1264
  }
1221
1265
 
1222
- return await this.placeAndTake(
1223
- getMarketOrderParams(
1224
- marketIndex,
1225
- findDirectionToClose(userPosition),
1226
- ZERO,
1227
- userPosition.baseAssetAmount,
1228
- true,
1229
- undefined
1230
- )
1231
- );
1266
+ return await this.placeAndTake({
1267
+ orderType: OrderType.MARKET,
1268
+ marketIndex,
1269
+ direction: findDirectionToClose(userPosition),
1270
+ baseAssetAmount: userPosition.baseAssetAmount,
1271
+ reduceOnly: true,
1272
+ });
1232
1273
  }
1233
1274
 
1234
1275
  public async settlePNLs(
@@ -1350,72 +1391,359 @@ export class ClearingHouse {
1350
1391
  });
1351
1392
  }
1352
1393
 
1353
- public async liquidate(
1354
- liquidateeUserAccountPublicKey: PublicKey
1394
+ public async liquidatePerp(
1395
+ userAccountPublicKey: PublicKey,
1396
+ userAccount: UserAccount,
1397
+ marketIndex: BN,
1398
+ maxBaseAssetAmount: BN
1355
1399
  ): Promise<TransactionSignature> {
1356
1400
  const { txSig } = await this.txSender.send(
1357
- wrapInTx(await this.getLiquidateIx(liquidateeUserAccountPublicKey)),
1401
+ wrapInTx(
1402
+ await this.getLiquidatePerpIx(
1403
+ userAccountPublicKey,
1404
+ userAccount,
1405
+ marketIndex,
1406
+ maxBaseAssetAmount
1407
+ )
1408
+ ),
1358
1409
  [],
1359
1410
  this.opts
1360
1411
  );
1361
1412
  return txSig;
1362
1413
  }
1363
1414
 
1364
- public async getLiquidateIx(
1365
- liquidateeUserAccountPublicKey: PublicKey
1415
+ public async getLiquidatePerpIx(
1416
+ userAccountPublicKey: PublicKey,
1417
+ userAccount: UserAccount,
1418
+ marketIndex: BN,
1419
+ maxBaseAssetAmount: BN
1366
1420
  ): Promise<TransactionInstruction> {
1367
- const userAccountPublicKey = await this.getUserAccountPublicKey();
1368
- const liquidateeUserAccount = (await this.program.account.user.fetch(
1369
- liquidateeUserAccountPublicKey
1370
- )) as UserAccount;
1421
+ const liquidatorPublicKey = await this.getUserAccountPublicKey();
1422
+
1423
+ const remainingAccounts = this.getRemainingAccountsForLiquidation({
1424
+ writableMarketIndex: marketIndex,
1425
+ userAccount,
1426
+ });
1371
1427
 
1372
- const bankAccountInfos = [
1428
+ return await this.program.instruction.liquidatePerp(
1429
+ marketIndex,
1430
+ maxBaseAssetAmount,
1373
1431
  {
1374
- pubkey: this.getQuoteAssetBankAccount().pubkey,
1375
- isSigner: false,
1376
- isWritable: true,
1377
- },
1378
- ];
1379
- const marketAccountInfos = [];
1380
- const oracleAccountInfos = [];
1432
+ accounts: {
1433
+ state: await this.getStatePublicKey(),
1434
+ authority: this.wallet.publicKey,
1435
+ user: userAccountPublicKey,
1436
+ liquidator: liquidatorPublicKey,
1437
+ },
1438
+ remainingAccounts: remainingAccounts,
1439
+ }
1440
+ );
1441
+ }
1442
+
1443
+ public async liquidateBorrow(
1444
+ userAccountPublicKey: PublicKey,
1445
+ userAccount: UserAccount,
1446
+ assetBankIndex: BN,
1447
+ liabilityBankIndex: BN,
1448
+ maxLiabilityTransfer: BN
1449
+ ): Promise<TransactionSignature> {
1450
+ const { txSig } = await this.txSender.send(
1451
+ wrapInTx(
1452
+ await this.getLiquidateBorrowIx(
1453
+ userAccountPublicKey,
1454
+ userAccount,
1455
+ assetBankIndex,
1456
+ liabilityBankIndex,
1457
+ maxLiabilityTransfer
1458
+ )
1459
+ ),
1460
+ [],
1461
+ this.opts
1462
+ );
1463
+ return txSig;
1464
+ }
1465
+
1466
+ public async getLiquidateBorrowIx(
1467
+ userAccountPublicKey: PublicKey,
1468
+ userAccount: UserAccount,
1469
+ assetBankIndex: BN,
1470
+ liabilityBankIndex: BN,
1471
+ maxLiabilityTransfer: BN
1472
+ ): Promise<TransactionInstruction> {
1473
+ const liquidatorPublicKey = await this.getUserAccountPublicKey();
1474
+
1475
+ const remainingAccounts = this.getRemainingAccountsForLiquidation({
1476
+ userAccount,
1477
+ writableBankIndexes: [liabilityBankIndex, assetBankIndex],
1478
+ });
1479
+
1480
+ return await this.program.instruction.liquidateBorrow(
1481
+ assetBankIndex,
1482
+ liabilityBankIndex,
1483
+ maxLiabilityTransfer,
1484
+ {
1485
+ accounts: {
1486
+ state: await this.getStatePublicKey(),
1487
+ authority: this.wallet.publicKey,
1488
+ user: userAccountPublicKey,
1489
+ liquidator: liquidatorPublicKey,
1490
+ },
1491
+ remainingAccounts: remainingAccounts,
1492
+ }
1493
+ );
1494
+ }
1495
+
1496
+ public async liquidateBorrowForPerpPnl(
1497
+ userAccountPublicKey: PublicKey,
1498
+ userAccount: UserAccount,
1499
+ perpMarketIndex: BN,
1500
+ liabilityBankIndex: BN,
1501
+ maxLiabilityTransfer: BN
1502
+ ): Promise<TransactionSignature> {
1503
+ const { txSig } = await this.txSender.send(
1504
+ wrapInTx(
1505
+ await this.getLiquidateBorrowForPerpPnlIx(
1506
+ userAccountPublicKey,
1507
+ userAccount,
1508
+ perpMarketIndex,
1509
+ liabilityBankIndex,
1510
+ maxLiabilityTransfer
1511
+ )
1512
+ ),
1513
+ [],
1514
+ this.opts
1515
+ );
1516
+ return txSig;
1517
+ }
1518
+
1519
+ public async getLiquidateBorrowForPerpPnlIx(
1520
+ userAccountPublicKey: PublicKey,
1521
+ userAccount: UserAccount,
1522
+ perpMarketIndex: BN,
1523
+ liabilityBankIndex: BN,
1524
+ maxLiabilityTransfer: BN
1525
+ ): Promise<TransactionInstruction> {
1526
+ const liquidatorPublicKey = await this.getUserAccountPublicKey();
1527
+
1528
+ const remainingAccounts = this.getRemainingAccountsForLiquidation({
1529
+ userAccount,
1530
+ writableMarketIndex: perpMarketIndex,
1531
+ writableBankIndexes: [liabilityBankIndex],
1532
+ });
1533
+
1534
+ return await this.program.instruction.liquidateBorrowForPerpPnl(
1535
+ perpMarketIndex,
1536
+ liabilityBankIndex,
1537
+ maxLiabilityTransfer,
1538
+ {
1539
+ accounts: {
1540
+ state: await this.getStatePublicKey(),
1541
+ authority: this.wallet.publicKey,
1542
+ user: userAccountPublicKey,
1543
+ liquidator: liquidatorPublicKey,
1544
+ },
1545
+ remainingAccounts: remainingAccounts,
1546
+ }
1547
+ );
1548
+ }
1549
+
1550
+ public async liquidatePerpPnlForDeposit(
1551
+ userAccountPublicKey: PublicKey,
1552
+ userAccount: UserAccount,
1553
+ perpMarketIndex: BN,
1554
+ assetBankIndex: BN,
1555
+ maxPnlTransfer: BN
1556
+ ): Promise<TransactionSignature> {
1557
+ const { txSig } = await this.txSender.send(
1558
+ wrapInTx(
1559
+ await this.getLiquidatePerpPnlForDepositIx(
1560
+ userAccountPublicKey,
1561
+ userAccount,
1562
+ perpMarketIndex,
1563
+ assetBankIndex,
1564
+ maxPnlTransfer
1565
+ )
1566
+ ),
1567
+ [],
1568
+ this.opts
1569
+ );
1570
+ return txSig;
1571
+ }
1572
+
1573
+ public async getLiquidatePerpPnlForDepositIx(
1574
+ userAccountPublicKey: PublicKey,
1575
+ userAccount: UserAccount,
1576
+ perpMarketIndex: BN,
1577
+ assetBankIndex: BN,
1578
+ maxPnlTransfer: BN
1579
+ ): Promise<TransactionInstruction> {
1580
+ const liquidatorPublicKey = await this.getUserAccountPublicKey();
1581
+
1582
+ const remainingAccounts = this.getRemainingAccountsForLiquidation({
1583
+ userAccount,
1584
+ writableMarketIndex: perpMarketIndex,
1585
+ writableBankIndexes: [assetBankIndex],
1586
+ });
1587
+
1588
+ return await this.program.instruction.liquidatePerpPnlForDeposit(
1589
+ perpMarketIndex,
1590
+ assetBankIndex,
1591
+ maxPnlTransfer,
1592
+ {
1593
+ accounts: {
1594
+ state: await this.getStatePublicKey(),
1595
+ authority: this.wallet.publicKey,
1596
+ user: userAccountPublicKey,
1597
+ liquidator: liquidatorPublicKey,
1598
+ },
1599
+ remainingAccounts: remainingAccounts,
1600
+ }
1601
+ );
1602
+ }
1603
+
1604
+ getRemainingAccountsForLiquidation(params: {
1605
+ userAccount: UserAccount;
1606
+ writableMarketIndex?: BN;
1607
+ writableBankIndexes?: BN[];
1608
+ }): AccountMeta[] {
1609
+ const liquidateeUserAccount = params.userAccount;
1610
+
1611
+ const oracleAccountMap = new Map<string, AccountMeta>();
1612
+ const bankAccountMap = new Map<number, AccountMeta>();
1613
+ const marketAccountMap = new Map<number, AccountMeta>();
1614
+ for (const bankBalance of liquidateeUserAccount.bankBalances) {
1615
+ if (!bankBalance.balance.eq(ZERO)) {
1616
+ const bankAccount = this.getBankAccount(bankBalance.bankIndex);
1617
+ bankAccountMap.set(bankBalance.bankIndex.toNumber(), {
1618
+ pubkey: bankAccount.pubkey,
1619
+ isSigner: false,
1620
+ isWritable: false,
1621
+ });
1622
+
1623
+ if (!bankAccount.oracle.equals(PublicKey.default)) {
1624
+ oracleAccountMap.set(bankAccount.oracle.toString(), {
1625
+ pubkey: bankAccount.oracle,
1626
+ isSigner: false,
1627
+ isWritable: false,
1628
+ });
1629
+ }
1630
+ }
1631
+ }
1381
1632
  for (const position of liquidateeUserAccount.positions) {
1382
1633
  if (!positionIsAvailable(position)) {
1383
1634
  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,
1635
+ marketAccountMap.set(position.marketIndex.toNumber(), {
1636
+ pubkey: market.pubkey,
1637
+ isWritable: false,
1391
1638
  isSigner: false,
1392
1639
  });
1393
- oracleAccountInfos.push({
1640
+ oracleAccountMap.set(market.amm.oracle.toString(), {
1394
1641
  pubkey: market.amm.oracle,
1395
1642
  isWritable: false,
1396
1643
  isSigner: false,
1397
1644
  });
1398
1645
  }
1399
1646
  }
1400
- const remainingAccounts = oracleAccountInfos.concat(
1401
- bankAccountInfos.concat(marketAccountInfos)
1402
- );
1403
1647
 
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
- });
1648
+ const userAccountAndSlot = this.getUserAccountAndSlot();
1649
+ if (!userAccountAndSlot) {
1650
+ throw Error(
1651
+ 'No user account found. Most likely user account does not exist or failed to fetch account'
1652
+ );
1653
+ }
1654
+ const { data: userAccount, slot: lastUserPositionsSlot } =
1655
+ userAccountAndSlot;
1656
+
1657
+ for (const [marketIndexNum, slot] of this.marketLastSlotCache.entries()) {
1658
+ // if cache has more recent slot than user positions account slot, add market to remaining accounts
1659
+ // otherwise remove from slot
1660
+ if (slot > lastUserPositionsSlot) {
1661
+ const marketAccount = this.getMarketAccount(marketIndexNum);
1662
+ marketAccountMap.set(marketIndexNum, {
1663
+ pubkey: marketAccount.pubkey,
1664
+ isSigner: false,
1665
+ isWritable: false,
1666
+ });
1667
+ oracleAccountMap.set(marketAccount.amm.oracle.toString(), {
1668
+ pubkey: marketAccount.amm.oracle,
1669
+ isSigner: false,
1670
+ isWritable: false,
1671
+ });
1672
+ } else {
1673
+ this.marketLastSlotCache.delete(marketIndexNum);
1674
+ }
1675
+ }
1676
+ for (const bankBalance of userAccount.bankBalances) {
1677
+ if (!bankBalance.balance.eq(ZERO)) {
1678
+ const bankAccount = this.getBankAccount(bankBalance.bankIndex);
1679
+ bankAccountMap.set(bankBalance.bankIndex.toNumber(), {
1680
+ pubkey: bankAccount.pubkey,
1681
+ isSigner: false,
1682
+ isWritable: false,
1683
+ });
1684
+
1685
+ if (!bankAccount.oracle.equals(PublicKey.default)) {
1686
+ oracleAccountMap.set(bankAccount.oracle.toString(), {
1687
+ pubkey: bankAccount.oracle,
1688
+ isSigner: false,
1689
+ isWritable: false,
1690
+ });
1691
+ }
1692
+ }
1693
+ }
1694
+ for (const position of userAccount.positions) {
1695
+ if (!positionIsAvailable(position)) {
1696
+ const market = this.getMarketAccount(position.marketIndex);
1697
+ marketAccountMap.set(position.marketIndex.toNumber(), {
1698
+ pubkey: market.pubkey,
1699
+ isWritable: false,
1700
+ isSigner: false,
1701
+ });
1702
+ oracleAccountMap.set(market.amm.oracle.toString(), {
1703
+ pubkey: market.amm.oracle,
1704
+ isWritable: false,
1705
+ isSigner: false,
1706
+ });
1707
+ }
1708
+ }
1709
+
1710
+ if (params.writableMarketIndex) {
1711
+ const market = this.getMarketAccount(params.writableMarketIndex);
1712
+ marketAccountMap.set(market.marketIndex.toNumber(), {
1713
+ pubkey: market.pubkey,
1714
+ isSigner: false,
1715
+ isWritable: true,
1716
+ });
1717
+ oracleAccountMap.set(market.amm.oracle.toString(), {
1718
+ pubkey: market.amm.oracle,
1719
+ isSigner: false,
1720
+ isWritable: false,
1721
+ });
1722
+ }
1723
+
1724
+ if (params.writableBankIndexes) {
1725
+ for (const writableBankIndex of params.writableBankIndexes) {
1726
+ const bank = this.getBankAccount(writableBankIndex);
1727
+ bankAccountMap.set(bank.bankIndex.toNumber(), {
1728
+ pubkey: bank.pubkey,
1729
+ isSigner: false,
1730
+ isWritable: true,
1731
+ });
1732
+ if (!bank.oracle.equals(PublicKey.default)) {
1733
+ oracleAccountMap.set(bank.oracle.toString(), {
1734
+ pubkey: bank.oracle,
1735
+ isSigner: false,
1736
+ isWritable: false,
1737
+ });
1738
+ }
1739
+ }
1740
+ }
1741
+
1742
+ return [
1743
+ ...oracleAccountMap.values(),
1744
+ ...bankAccountMap.values(),
1745
+ ...marketAccountMap.values(),
1746
+ ];
1419
1747
  }
1420
1748
 
1421
1749
  public async updateFundingRate(