@drift-labs/sdk 0.2.0-temp.0 → 0.2.0-temp.1

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 (115) hide show
  1. package/lib/admin.d.ts +7 -5
  2. package/lib/admin.js +34 -11
  3. package/lib/clearingHouse.d.ts +23 -9
  4. package/lib/clearingHouse.js +335 -95
  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 +1 -0
  10. package/lib/constants/numericConstants.js +2 -1
  11. package/lib/idl/clearing_house.json +689 -153
  12. package/lib/idl/{mock_usdc_faucet.json → token_faucet.json} +46 -23
  13. package/lib/index.d.ts +3 -2
  14. package/lib/index.js +7 -2
  15. package/lib/math/amm.js +7 -35
  16. package/lib/math/auction.js +4 -1
  17. package/lib/math/orders.d.ts +2 -2
  18. package/lib/math/orders.js +19 -2
  19. package/lib/math/position.js +3 -1
  20. package/lib/math/trade.d.ts +1 -1
  21. package/lib/math/trade.js +7 -10
  22. package/lib/orderParams.d.ts +14 -5
  23. package/lib/orderParams.js +8 -96
  24. package/lib/orders.js +1 -1
  25. package/lib/slot/SlotSubscriber.d.ts +7 -0
  26. package/lib/slot/SlotSubscriber.js +3 -0
  27. package/lib/{mockUSDCFaucet.d.ts → tokenFaucet.d.ts} +7 -5
  28. package/lib/{mockUSDCFaucet.js → tokenFaucet.js} +41 -40
  29. package/lib/tx/utils.js +1 -1
  30. package/lib/types.d.ts +132 -14
  31. package/lib/types.js +52 -1
  32. package/lib/util/computeUnits.js +1 -1
  33. package/package.json +3 -3
  34. package/src/accounts/bulkAccountLoader.js +197 -0
  35. package/src/accounts/bulkUserSubscription.js +33 -0
  36. package/src/accounts/pollingClearingHouseAccountSubscriber.js +311 -0
  37. package/src/accounts/pollingOracleSubscriber.js +93 -0
  38. package/src/accounts/pollingTokenAccountSubscriber.js +90 -0
  39. package/src/accounts/pollingUserAccountSubscriber.js +132 -0
  40. package/src/accounts/types.js +10 -0
  41. package/src/accounts/utils.js +7 -0
  42. package/src/accounts/webSocketAccountSubscriber.js +93 -0
  43. package/src/accounts/webSocketClearingHouseAccountSubscriber.js +233 -0
  44. package/src/accounts/webSocketUserAccountSubscriber.js +62 -0
  45. package/src/addresses/marketAddresses.js +26 -0
  46. package/src/admin.js +517 -0
  47. package/src/admin.ts +53 -14
  48. package/src/assert/assert.js +9 -0
  49. package/src/clearingHouse.ts +510 -131
  50. package/src/clearingHouseConfig.js +2 -0
  51. package/src/clearingHouseUser.ts +12 -23
  52. package/src/clearingHouseUserConfig.js +2 -0
  53. package/src/config.js +67 -0
  54. package/src/config.ts +1 -1
  55. package/src/constants/banks.js +42 -0
  56. package/src/constants/banks.ts +9 -2
  57. package/src/constants/markets.js +42 -0
  58. package/src/constants/numericConstants.js +41 -0
  59. package/src/constants/numericConstants.ts +1 -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 +390 -0
  70. package/src/factory/oracleClient.js +20 -0
  71. package/src/idl/clearing_house.json +689 -153
  72. package/src/idl/{mock_usdc_faucet.json → token_faucet.json} +46 -23
  73. package/src/index.js +69 -0
  74. package/src/index.ts +3 -2
  75. package/src/math/amm.js +369 -0
  76. package/src/math/amm.ts +19 -49
  77. package/src/math/auction.js +42 -0
  78. package/src/math/auction.ts +5 -1
  79. package/src/math/conversion.js +11 -0
  80. package/src/math/funding.js +248 -0
  81. package/src/math/oracles.js +26 -0
  82. package/src/math/orders.ts +17 -3
  83. package/src/math/position.ts +5 -1
  84. package/src/math/repeg.js +128 -0
  85. package/src/math/state.js +15 -0
  86. package/src/math/trade.js +253 -0
  87. package/src/math/trade.ts +23 -25
  88. package/src/math/utils.js +0 -1
  89. package/src/mockUSDCFaucet.js +280 -0
  90. package/src/oracles/oracleClientCache.js +19 -0
  91. package/src/oracles/pythClient.js +46 -0
  92. package/src/oracles/quoteAssetOracleClient.js +32 -0
  93. package/src/oracles/switchboardClient.js +69 -0
  94. package/src/oracles/types.js +2 -0
  95. package/src/orderParams.js +20 -0
  96. package/src/orderParams.ts +20 -141
  97. package/src/orders.ts +2 -1
  98. package/src/slot/SlotSubscriber.js +39 -0
  99. package/src/slot/SlotSubscriber.ts +11 -1
  100. package/src/token/index.js +38 -0
  101. package/src/tokenFaucet.js +189 -0
  102. package/src/{mockUSDCFaucet.ts → tokenFaucet.ts} +48 -59
  103. package/src/tx/types.js +2 -0
  104. package/src/tx/utils.js +17 -0
  105. package/src/tx/utils.ts +1 -1
  106. package/src/types.js +125 -0
  107. package/src/types.ts +128 -15
  108. package/src/userName.js +20 -0
  109. package/src/util/computeUnits.js +21 -11
  110. package/src/util/computeUnits.ts +1 -1
  111. package/src/util/getTokenAddress.js +9 -0
  112. package/src/util/promiseTimeout.js +14 -0
  113. package/src/util/tps.js +27 -0
  114. package/src/wallet.js +35 -0
  115. 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,9 +829,14 @@ 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
842
  const remainingAccounts = this.getRemainingAccounts({
@@ -967,48 +979,64 @@ export class ClearingHouse {
967
979
  const marketIndex = order.marketIndex;
968
980
  const marketAccount = this.getMarketAccount(marketIndex);
969
981
 
970
- const bankAccountInfos = [
971
- {
972
- pubkey: this.getQuoteAssetBankAccount().pubkey,
973
- isSigner: false,
974
- isWritable: true,
975
- },
976
- ];
977
- const marketAccountInfos = [
978
- {
979
- pubkey: marketAccount.pubkey,
980
- isWritable: true,
981
- isSigner: false,
982
- },
983
- ];
984
- const oracleAccountInfos = [
985
- {
986
- pubkey: marketAccount.amm.oracle,
987
- isWritable: false,
988
- isSigner: false,
989
- },
990
- ];
982
+ const oracleAccountMap = new Map<string, AccountMeta>();
983
+ const bankAccountMap = new Map<number, AccountMeta>();
984
+ const marketAccountMap = new Map<number, AccountMeta>();
985
+
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
+
991
1016
  for (const position of userAccount.positions) {
992
1017
  if (
993
1018
  !positionIsAvailable(position) &&
994
1019
  !position.marketIndex.eq(order.marketIndex)
995
1020
  ) {
996
1021
  const market = this.getMarketAccount(position.marketIndex);
997
- marketAccountInfos.push({
1022
+ marketAccountMap.set(position.marketIndex.toNumber(), {
998
1023
  pubkey: market.pubkey,
999
1024
  isWritable: true,
1000
1025
  isSigner: false,
1001
1026
  });
1002
- oracleAccountInfos.push({
1027
+ oracleAccountMap.set(market.amm.oracle.toString(), {
1003
1028
  pubkey: market.amm.oracle,
1004
1029
  isWritable: false,
1005
1030
  isSigner: false,
1006
1031
  });
1007
1032
  }
1008
1033
  }
1009
- const remainingAccounts = oracleAccountInfos.concat(
1010
- bankAccountInfos.concat(marketAccountInfos)
1011
- );
1034
+
1035
+ const remainingAccounts = [
1036
+ ...oracleAccountMap.values(),
1037
+ ...bankAccountMap.values(),
1038
+ ...marketAccountMap.values(),
1039
+ ];
1012
1040
 
1013
1041
  if (makerInfo) {
1014
1042
  remainingAccounts.push({
@@ -1020,6 +1048,7 @@ export class ClearingHouse {
1020
1048
 
1021
1049
  const orderId = order.orderId;
1022
1050
  const makerOrderId = makerInfo ? makerInfo.order.orderId : null;
1051
+
1023
1052
  return await this.program.instruction.fillOrder(orderId, makerOrderId, {
1024
1053
  accounts: {
1025
1054
  state: await this.getStatePublicKey(),
@@ -1054,48 +1083,64 @@ export class ClearingHouse {
1054
1083
  const marketIndex = order.marketIndex;
1055
1084
  const marketAccount = this.getMarketAccount(marketIndex);
1056
1085
 
1057
- const bankAccountInfos = [
1058
- {
1059
- pubkey: this.getQuoteAssetBankAccount().pubkey,
1060
- isSigner: false,
1061
- isWritable: true,
1062
- },
1063
- ];
1064
- const marketAccountInfos = [
1065
- {
1066
- pubkey: marketAccount.pubkey,
1067
- isWritable: true,
1068
- isSigner: false,
1069
- },
1070
- ];
1071
- const oracleAccountInfos = [
1072
- {
1073
- pubkey: marketAccount.amm.oracle,
1074
- isWritable: false,
1075
- isSigner: false,
1076
- },
1077
- ];
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
+
1078
1120
  for (const position of userAccount.positions) {
1079
1121
  if (
1080
1122
  !positionIsAvailable(position) &&
1081
1123
  !position.marketIndex.eq(order.marketIndex)
1082
1124
  ) {
1083
1125
  const market = this.getMarketAccount(position.marketIndex);
1084
- marketAccountInfos.push({
1126
+ marketAccountMap.set(position.marketIndex.toNumber(), {
1085
1127
  pubkey: market.pubkey,
1086
- isWritable: false,
1128
+ isWritable: true,
1087
1129
  isSigner: false,
1088
1130
  });
1089
- oracleAccountInfos.push({
1131
+ oracleAccountMap.set(market.amm.oracle.toString(), {
1090
1132
  pubkey: market.amm.oracle,
1091
1133
  isWritable: false,
1092
1134
  isSigner: false,
1093
1135
  });
1094
1136
  }
1095
1137
  }
1096
- const remainingAccounts = oracleAccountInfos.concat(
1097
- bankAccountInfos.concat(marketAccountInfos)
1098
- );
1138
+
1139
+ const remainingAccounts = [
1140
+ ...oracleAccountMap.values(),
1141
+ ...bankAccountMap.values(),
1142
+ ...marketAccountMap.values(),
1143
+ ];
1099
1144
 
1100
1145
  const orderId = order.orderId;
1101
1146
  return await this.program.instruction.triggerOrder(orderId, {
@@ -1110,7 +1155,7 @@ export class ClearingHouse {
1110
1155
  }
1111
1156
 
1112
1157
  public async placeAndTake(
1113
- orderParams: OrderParams,
1158
+ orderParams: OptionalOrderParams,
1114
1159
  makerInfo?: MakerInfo
1115
1160
  ): Promise<TransactionSignature> {
1116
1161
  const { txSig, slot } = await this.txSender.send(
@@ -1123,9 +1168,10 @@ export class ClearingHouse {
1123
1168
  }
1124
1169
 
1125
1170
  public async getPlaceAndTakeIx(
1126
- orderParams: OrderParams,
1171
+ orderParams: OptionalOrderParams,
1127
1172
  makerInfo?: MakerInfo
1128
1173
  ): Promise<TransactionInstruction> {
1174
+ orderParams = this.getOrderParams(orderParams);
1129
1175
  const userAccountPublicKey = await this.getUserAccountPublicKey();
1130
1176
 
1131
1177
  const remainingAccounts = this.getRemainingAccounts({
@@ -1157,6 +1203,55 @@ export class ClearingHouse {
1157
1203
  );
1158
1204
  }
1159
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,
1249
+ },
1250
+ remainingAccounts,
1251
+ }
1252
+ );
1253
+ }
1254
+
1160
1255
  /**
1161
1256
  * Close an entire position. If you want to reduce a position, use the {@link openPosition} method in the opposite direction of the current position.
1162
1257
  * @param marketIndex
@@ -1168,16 +1263,13 @@ export class ClearingHouse {
1168
1263
  throw Error(`No position in market ${marketIndex.toString()}`);
1169
1264
  }
1170
1265
 
1171
- return await this.placeAndTake(
1172
- getMarketOrderParams(
1173
- marketIndex,
1174
- findDirectionToClose(userPosition),
1175
- ZERO,
1176
- userPosition.baseAssetAmount,
1177
- true,
1178
- undefined
1179
- )
1180
- );
1266
+ return await this.placeAndTake({
1267
+ orderType: OrderType.MARKET,
1268
+ marketIndex,
1269
+ direction: findDirectionToClose(userPosition),
1270
+ baseAssetAmount: userPosition.baseAssetAmount,
1271
+ reduceOnly: true,
1272
+ });
1181
1273
  }
1182
1274
 
1183
1275
  public async settlePNLs(
@@ -1299,72 +1391,359 @@ export class ClearingHouse {
1299
1391
  });
1300
1392
  }
1301
1393
 
1302
- public async liquidate(
1303
- liquidateeUserAccountPublicKey: PublicKey
1394
+ public async liquidatePerp(
1395
+ userAccountPublicKey: PublicKey,
1396
+ userAccount: UserAccount,
1397
+ marketIndex: BN,
1398
+ maxBaseAssetAmount: BN
1304
1399
  ): Promise<TransactionSignature> {
1305
1400
  const { txSig } = await this.txSender.send(
1306
- wrapInTx(await this.getLiquidateIx(liquidateeUserAccountPublicKey)),
1401
+ wrapInTx(
1402
+ await this.getLiquidatePerpIx(
1403
+ userAccountPublicKey,
1404
+ userAccount,
1405
+ marketIndex,
1406
+ maxBaseAssetAmount
1407
+ )
1408
+ ),
1307
1409
  [],
1308
1410
  this.opts
1309
1411
  );
1310
1412
  return txSig;
1311
1413
  }
1312
1414
 
1313
- public async getLiquidateIx(
1314
- liquidateeUserAccountPublicKey: PublicKey
1415
+ public async getLiquidatePerpIx(
1416
+ userAccountPublicKey: PublicKey,
1417
+ userAccount: UserAccount,
1418
+ marketIndex: BN,
1419
+ maxBaseAssetAmount: BN
1315
1420
  ): Promise<TransactionInstruction> {
1316
- const userAccountPublicKey = await this.getUserAccountPublicKey();
1317
- const liquidateeUserAccount = (await this.program.account.user.fetch(
1318
- liquidateeUserAccountPublicKey
1319
- )) as UserAccount;
1421
+ const liquidatorPublicKey = await this.getUserAccountPublicKey();
1422
+
1423
+ const remainingAccounts = this.getRemainingAccountsForLiquidation({
1424
+ writableMarketIndex: marketIndex,
1425
+ userAccount,
1426
+ });
1320
1427
 
1321
- const bankAccountInfos = [
1428
+ return await this.program.instruction.liquidatePerp(
1429
+ marketIndex,
1430
+ maxBaseAssetAmount,
1322
1431
  {
1323
- pubkey: this.getQuoteAssetBankAccount().pubkey,
1324
- isSigner: false,
1325
- isWritable: true,
1326
- },
1327
- ];
1328
- const marketAccountInfos = [];
1329
- 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
+ }
1330
1632
  for (const position of liquidateeUserAccount.positions) {
1331
1633
  if (!positionIsAvailable(position)) {
1332
1634
  const market = this.getMarketAccount(position.marketIndex);
1333
- const marketPublicKey = await getMarketPublicKey(
1334
- this.program.programId,
1335
- position.marketIndex
1336
- );
1337
- marketAccountInfos.push({
1338
- pubkey: marketPublicKey,
1339
- isWritable: true,
1635
+ marketAccountMap.set(position.marketIndex.toNumber(), {
1636
+ pubkey: market.pubkey,
1637
+ isWritable: false,
1340
1638
  isSigner: false,
1341
1639
  });
1342
- oracleAccountInfos.push({
1640
+ oracleAccountMap.set(market.amm.oracle.toString(), {
1343
1641
  pubkey: market.amm.oracle,
1344
1642
  isWritable: false,
1345
1643
  isSigner: false,
1346
1644
  });
1347
1645
  }
1348
1646
  }
1349
- const remainingAccounts = oracleAccountInfos.concat(
1350
- bankAccountInfos.concat(marketAccountInfos)
1351
- );
1352
1647
 
1353
- const state = this.getStateAccount();
1354
- const quoteAssetBankAccount = this.getQuoteAssetBankAccount();
1355
- return await this.program.instruction.liquidate({
1356
- accounts: {
1357
- state: await this.getStatePublicKey(),
1358
- authority: this.wallet.publicKey,
1359
- user: liquidateeUserAccountPublicKey,
1360
- liquidator: userAccountPublicKey,
1361
- bankVault: quoteAssetBankAccount.vault,
1362
- bankVaultAuthority: quoteAssetBankAccount.vaultAuthority,
1363
- insuranceVault: state.insuranceVault,
1364
- tokenProgram: TOKEN_PROGRAM_ID,
1365
- },
1366
- remainingAccounts: remainingAccounts,
1367
- });
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
+ ];
1368
1747
  }
1369
1748
 
1370
1749
  public async updateFundingRate(