@drift-labs/sdk 2.31.1-beta.2 → 2.31.1-beta.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/VERSION +1 -1
  2. package/lib/accounts/mockUserAccountSubscriber.d.ts +23 -0
  3. package/lib/accounts/mockUserAccountSubscriber.js +31 -0
  4. package/lib/constants/perpMarkets.js +20 -0
  5. package/lib/dlob/orderBookLevels.js +2 -2
  6. package/lib/driftClient.d.ts +57 -4
  7. package/lib/driftClient.js +244 -205
  8. package/lib/driftClientConfig.d.ts +2 -1
  9. package/lib/idl/drift.json +31 -1
  10. package/lib/index.d.ts +2 -0
  11. package/lib/index.js +2 -0
  12. package/lib/marinade/index.d.ts +11 -0
  13. package/lib/marinade/index.js +36 -0
  14. package/lib/marinade/types.d.ts +1963 -0
  15. package/lib/marinade/types.js +1965 -0
  16. package/lib/math/spotBalance.d.ts +9 -2
  17. package/lib/math/spotBalance.js +54 -6
  18. package/lib/math/superStake.d.ts +21 -0
  19. package/lib/math/superStake.js +100 -0
  20. package/lib/math/tiers.d.ts +4 -0
  21. package/lib/math/tiers.js +52 -0
  22. package/lib/tx/retryTxSender.d.ts +12 -3
  23. package/lib/tx/retryTxSender.js +22 -22
  24. package/lib/tx/types.d.ts +2 -2
  25. package/lib/user.d.ts +10 -1
  26. package/lib/user.js +39 -8
  27. package/lib/userConfig.d.ts +4 -0
  28. package/lib/userStats.js +4 -1
  29. package/lib/userStatsConfig.d.ts +2 -0
  30. package/package.json +1 -1
  31. package/src/accounts/mockUserAccountSubscriber.ts +53 -0
  32. package/src/config.ts +2 -2
  33. package/src/constants/perpMarkets.ts +20 -0
  34. package/src/dlob/orderBookLevels.ts +3 -2
  35. package/src/driftClient.ts +440 -224
  36. package/src/driftClientConfig.ts +2 -1
  37. package/src/idl/drift.json +31 -1
  38. package/src/index.ts +2 -0
  39. package/src/marinade/idl/idl.json +1962 -0
  40. package/src/marinade/index.ts +64 -0
  41. package/src/marinade/types.ts +3925 -0
  42. package/src/math/spotBalance.ts +83 -5
  43. package/src/math/superStake.ts +133 -0
  44. package/src/math/tiers.ts +44 -0
  45. package/src/tx/retryTxSender.ts +39 -35
  46. package/src/tx/types.ts +2 -2
  47. package/src/user.ts +63 -12
  48. package/src/userConfig.ts +5 -0
  49. package/src/userStats.ts +4 -0
  50. package/src/userStatsConfig.ts +3 -0
  51. package/tests/spot/test.ts +156 -0
@@ -95,6 +95,7 @@ import {
95
95
  PRICE_PRECISION,
96
96
  QUOTE_SPOT_MARKET_INDEX,
97
97
  ZERO,
98
+ QUOTE_PRECISION,
98
99
  } from './constants/numericConstants';
99
100
  import { findDirectionToClose, positionIsAvailable } from './math/position';
100
101
  import { getSignedTokenAmount, getTokenAmount } from './math/spotBalance';
@@ -115,12 +116,14 @@ import { fetchUserStatsAccount } from './accounts/fetch';
115
116
  import { castNumberToSpotPrecision } from './math/spotMarket';
116
117
  import { JupiterClient, Route, SwapMode } from './jupiter/jupiterClient';
117
118
  import { getNonIdleUserFilter } from './memcmp';
119
+ import { UserStatsSubscriptionConfig } from './userStatsConfig';
120
+ import { getMarinadeDepositIx, getMarinadeFinanceProgram } from './marinade';
118
121
 
119
122
  type RemainingAccountParams = {
120
123
  userAccounts: UserAccount[];
121
124
  writablePerpMarketIndexes?: number[];
122
125
  writableSpotMarketIndexes?: number[];
123
- readablePerpMarketIndex?: number;
126
+ readablePerpMarketIndex?: number | number[];
124
127
  readableSpotMarketIndexes?: number[];
125
128
  useMarketLastSlotCache?: boolean;
126
129
  };
@@ -139,12 +142,15 @@ export class DriftClient {
139
142
  userStats?: UserStats;
140
143
  activeSubAccountId: number;
141
144
  userAccountSubscriptionConfig: UserSubscriptionConfig;
145
+ userStatsAccountSubscriptionConfig: UserStatsSubscriptionConfig;
142
146
  accountSubscriber: DriftClientAccountSubscriber;
143
147
  eventEmitter: StrictEventEmitter<EventEmitter, DriftClientAccountEvents>;
144
148
  _isSubscribed = false;
145
149
  txSender: TxSender;
146
150
  perpMarketLastSlotCache = new Map<number, number>();
147
151
  spotMarketLastSlotCache = new Map<number, number>();
152
+ mustIncludePerpMarketIndexes = new Set<number>();
153
+ mustIncludeSpotMarketIndexes = new Set<number>();
148
154
  authority: PublicKey;
149
155
  marketLookupTable: PublicKey;
150
156
  lookupTableAccount: AddressLookupTableAccount;
@@ -152,6 +158,7 @@ export class DriftClient {
152
158
  authoritySubAccountMap?: Map<string, number[]>;
153
159
  skipLoadUsers?: boolean;
154
160
  txVersion: TransactionVersion;
161
+ txParams: TxParams;
155
162
 
156
163
  public get isSubscribed() {
157
164
  return this._isSubscribed && this.accountSubscriber.isSubscribed;
@@ -180,6 +187,10 @@ export class DriftClient {
180
187
  this.activeSubAccountId = config.activeSubAccountId ?? 0;
181
188
  this.skipLoadUsers = config.skipLoadUsers ?? false;
182
189
  this.txVersion = config.txVersion ?? 'legacy';
190
+ this.txParams = {
191
+ computeUnits: config.txParams?.computeUnits ?? 600_000,
192
+ computeUnitsPrice: config.txParams?.computeUnitsPrice ?? 0,
193
+ };
183
194
 
184
195
  if (config.includeDelegates && config.subAccountIds) {
185
196
  throw new Error(
@@ -206,15 +217,23 @@ export class DriftClient {
206
217
  : new Map<string, number[]>();
207
218
 
208
219
  this.includeDelegates = config.includeDelegates ?? false;
209
- this.userAccountSubscriptionConfig =
210
- config.accountSubscription?.type === 'polling'
211
- ? {
212
- type: 'polling',
213
- accountLoader: config.accountSubscription.accountLoader,
214
- }
215
- : {
216
- type: 'websocket',
217
- };
220
+ if (config.accountSubscription?.type === 'polling') {
221
+ this.userAccountSubscriptionConfig = {
222
+ type: 'polling',
223
+ accountLoader: config.accountSubscription.accountLoader,
224
+ };
225
+ this.userStatsAccountSubscriptionConfig = {
226
+ type: 'polling',
227
+ accountLoader: config.accountSubscription.accountLoader,
228
+ };
229
+ } else {
230
+ this.userAccountSubscriptionConfig = {
231
+ type: 'websocket',
232
+ };
233
+ this.userStatsAccountSubscriptionConfig = {
234
+ type: 'websocket',
235
+ };
236
+ }
218
237
 
219
238
  if (config.userStats) {
220
239
  this.userStats = new UserStats({
@@ -257,7 +276,13 @@ export class DriftClient {
257
276
  );
258
277
  }
259
278
  this.eventEmitter = this.accountSubscriber.eventEmitter;
260
- this.txSender = config.txSender ?? new RetryTxSender(this.provider);
279
+ this.txSender =
280
+ config.txSender ??
281
+ new RetryTxSender({
282
+ connection: this.connection,
283
+ wallet: this.wallet,
284
+ opts: this.opts,
285
+ });
261
286
  }
262
287
 
263
288
  public getUserMapKey(subAccountId: number, authority: PublicKey): string {
@@ -498,7 +523,7 @@ export class DriftClient {
498
523
 
499
524
  this.skipLoadUsers = false;
500
525
  // Update provider for txSender with new wallet details
501
- this.txSender.provider = newProvider;
526
+ this.txSender.wallet = newWallet;
502
527
  this.wallet = newWallet;
503
528
  this.provider = newProvider;
504
529
  this.program = newProgram;
@@ -542,7 +567,7 @@ export class DriftClient {
542
567
  this.userStats = new UserStats({
543
568
  driftClient: this,
544
569
  userStatsAccountPublicKey: this.getUserStatsAccountPublicKey(),
545
- accountSubscription: this.userAccountSubscriptionConfig,
570
+ accountSubscription: this.userStatsAccountSubscriptionConfig,
546
571
  });
547
572
 
548
573
  await this.userStats.subscribe();
@@ -838,6 +863,43 @@ export class DriftClient {
838
863
  return txSig;
839
864
  }
840
865
 
866
+ public async getUpdateUserMarginTradingEnabledIx(
867
+ marginTradingEnabled: boolean,
868
+ subAccountId = 0,
869
+ userAccountPublicKey?: PublicKey
870
+ ): Promise<TransactionInstruction> {
871
+ const userAccountPublicKeyToUse =
872
+ userAccountPublicKey ||
873
+ getUserAccountPublicKeySync(
874
+ this.program.programId,
875
+ this.wallet.publicKey,
876
+ subAccountId
877
+ );
878
+
879
+ await this.addUser(subAccountId, this.wallet.publicKey);
880
+
881
+ let remainingAccounts;
882
+ try {
883
+ remainingAccounts = this.getRemainingAccounts({
884
+ userAccounts: [this.getUserAccount(subAccountId)],
885
+ });
886
+ } catch (err) {
887
+ remainingAccounts = [];
888
+ }
889
+
890
+ return await this.program.instruction.updateUserMarginTradingEnabled(
891
+ subAccountId,
892
+ marginTradingEnabled,
893
+ {
894
+ accounts: {
895
+ user: userAccountPublicKeyToUse,
896
+ authority: this.wallet.publicKey,
897
+ },
898
+ remainingAccounts,
899
+ }
900
+ );
901
+ }
902
+
841
903
  public async updateUserMarginTradingEnabled(
842
904
  marginTradingEnabled: boolean,
843
905
  subAccountId = 0
@@ -1028,11 +1090,19 @@ export class DriftClient {
1028
1090
  const userMapKey = this.getUserMapKey(subAccountId, authority);
1029
1091
 
1030
1092
  if (!this.users.has(userMapKey)) {
1031
- throw new Error(`Clearing House has no user for user id ${userMapKey}`);
1093
+ throw new Error(`DriftClient has no user for user id ${userMapKey}`);
1032
1094
  }
1033
1095
  return this.users.get(userMapKey);
1034
1096
  }
1035
1097
 
1098
+ public hasUser(subAccountId?: number, authority?: PublicKey): boolean {
1099
+ subAccountId = subAccountId ?? this.activeSubAccountId;
1100
+ authority = authority ?? this.authority;
1101
+ const userMapKey = this.getUserMapKey(subAccountId, authority);
1102
+
1103
+ return this.users.has(userMapKey);
1104
+ }
1105
+
1036
1106
  public getUsers(): User[] {
1037
1107
  // delegate users get added to the end
1038
1108
  return [...this.users.values()]
@@ -1167,6 +1237,29 @@ export class DriftClient {
1167
1237
  return amount.mul(PRICE_PRECISION);
1168
1238
  }
1169
1239
 
1240
+ /**
1241
+ * Each drift instruction must include perp and sport market accounts in the ix remaining accounts.
1242
+ * Use this function to force a subset of markets to be included in the remaining accounts for every ix
1243
+ *
1244
+ * @param perpMarketIndexes
1245
+ * @param spotMarketIndexes
1246
+ */
1247
+ public mustIncludeMarketsInIx({
1248
+ perpMarketIndexes,
1249
+ spotMarketIndexes,
1250
+ }: {
1251
+ perpMarketIndexes: number[];
1252
+ spotMarketIndexes: number[];
1253
+ }): void {
1254
+ perpMarketIndexes.forEach((perpMarketIndex) => {
1255
+ this.mustIncludePerpMarketIndexes.add(perpMarketIndex);
1256
+ });
1257
+
1258
+ spotMarketIndexes.forEach((spotMarketIndex) => {
1259
+ this.mustIncludeSpotMarketIndexes.add(spotMarketIndex);
1260
+ });
1261
+ }
1262
+
1170
1263
  getRemainingAccounts(params: RemainingAccountParams): AccountMeta[] {
1171
1264
  const { oracleAccountMap, spotMarketAccountMap, perpMarketAccountMap } =
1172
1265
  this.getRemainingAccountMapsForUsers(params.userAccounts);
@@ -1180,32 +1273,13 @@ export class DriftClient {
1180
1273
  // if cache has more recent slot than user positions account slot, add market to remaining accounts
1181
1274
  // otherwise remove from slot
1182
1275
  if (slot > lastUserSlot) {
1183
- const perpMarketAccount = this.getPerpMarketAccount(marketIndex);
1184
- perpMarketAccountMap.set(marketIndex, {
1185
- pubkey: perpMarketAccount.pubkey,
1186
- isSigner: false,
1187
- isWritable: false,
1188
- });
1189
- oracleAccountMap.set(perpMarketAccount.amm.oracle.toString(), {
1190
- pubkey: perpMarketAccount.amm.oracle,
1191
- isSigner: false,
1192
- isWritable: false,
1193
- });
1194
- const spotMarketAccount = this.getSpotMarketAccount(
1195
- perpMarketAccount.quoteSpotMarketIndex
1276
+ this.addPerpMarketToRemainingAccountMaps(
1277
+ marketIndex,
1278
+ false,
1279
+ oracleAccountMap,
1280
+ spotMarketAccountMap,
1281
+ perpMarketAccountMap
1196
1282
  );
1197
- spotMarketAccountMap.set(perpMarketAccount.quoteSpotMarketIndex, {
1198
- pubkey: spotMarketAccount.pubkey,
1199
- isSigner: false,
1200
- isWritable: false,
1201
- });
1202
- if (!spotMarketAccount.oracle.equals(PublicKey.default)) {
1203
- oracleAccountMap.set(spotMarketAccount.oracle.toString(), {
1204
- pubkey: spotMarketAccount.oracle,
1205
- isSigner: false,
1206
- isWritable: false,
1207
- });
1208
- }
1209
1283
  } else {
1210
1284
  this.perpMarketLastSlotCache.delete(marketIndex);
1211
1285
  }
@@ -1218,19 +1292,12 @@ export class DriftClient {
1218
1292
  // if cache has more recent slot than user positions account slot, add market to remaining accounts
1219
1293
  // otherwise remove from slot
1220
1294
  if (slot > lastUserSlot) {
1221
- const spotMarketAccount = this.getSpotMarketAccount(marketIndex);
1222
- spotMarketAccountMap.set(marketIndex, {
1223
- pubkey: spotMarketAccount.pubkey,
1224
- isSigner: false,
1225
- isWritable: false,
1226
- });
1227
- if (!spotMarketAccount.oracle.equals(PublicKey.default)) {
1228
- oracleAccountMap.set(spotMarketAccount.oracle.toString(), {
1229
- pubkey: spotMarketAccount.oracle,
1230
- isSigner: false,
1231
- isWritable: false,
1232
- });
1233
- }
1295
+ this.addSpotMarketToRemainingAccountMaps(
1296
+ marketIndex,
1297
+ false,
1298
+ oracleAccountMap,
1299
+ spotMarketAccountMap
1300
+ );
1234
1301
  } else {
1235
1302
  this.spotMarketLastSlotCache.delete(marketIndex);
1236
1303
  }
@@ -1238,106 +1305,72 @@ export class DriftClient {
1238
1305
  }
1239
1306
 
1240
1307
  if (params.readablePerpMarketIndex !== undefined) {
1241
- const perpMarketAccount = this.getPerpMarketAccount(
1308
+ const readablePerpMarketIndexes = Array.isArray(
1242
1309
  params.readablePerpMarketIndex
1243
- );
1244
- perpMarketAccountMap.set(params.readablePerpMarketIndex, {
1245
- pubkey: perpMarketAccount.pubkey,
1246
- isSigner: false,
1247
- isWritable: false,
1248
- });
1249
- oracleAccountMap.set(perpMarketAccount.amm.oracle.toString(), {
1250
- pubkey: perpMarketAccount.amm.oracle,
1251
- isSigner: false,
1252
- isWritable: false,
1253
- });
1254
- const spotMarketAccount = this.getSpotMarketAccount(
1255
- perpMarketAccount.quoteSpotMarketIndex
1256
- );
1257
- spotMarketAccountMap.set(perpMarketAccount.quoteSpotMarketIndex, {
1258
- pubkey: spotMarketAccount.pubkey,
1259
- isSigner: false,
1260
- isWritable: false,
1261
- });
1262
- if (!spotMarketAccount.oracle.equals(PublicKey.default)) {
1263
- oracleAccountMap.set(spotMarketAccount.oracle.toString(), {
1264
- pubkey: spotMarketAccount.oracle,
1265
- isSigner: false,
1266
- isWritable: false,
1267
- });
1310
+ )
1311
+ ? params.readablePerpMarketIndex
1312
+ : [params.readablePerpMarketIndex];
1313
+ for (const marketIndex of readablePerpMarketIndexes) {
1314
+ this.addPerpMarketToRemainingAccountMaps(
1315
+ marketIndex,
1316
+ false,
1317
+ oracleAccountMap,
1318
+ spotMarketAccountMap,
1319
+ perpMarketAccountMap
1320
+ );
1268
1321
  }
1269
1322
  }
1270
1323
 
1324
+ for (const perpMarketIndex of this.mustIncludePerpMarketIndexes.values()) {
1325
+ this.addPerpMarketToRemainingAccountMaps(
1326
+ perpMarketIndex,
1327
+ false,
1328
+ oracleAccountMap,
1329
+ spotMarketAccountMap,
1330
+ perpMarketAccountMap
1331
+ );
1332
+ }
1333
+
1271
1334
  if (params.readableSpotMarketIndexes !== undefined) {
1272
1335
  for (const readableSpotMarketIndex of params.readableSpotMarketIndexes) {
1273
- const spotMarketAccount = this.getSpotMarketAccount(
1274
- readableSpotMarketIndex
1336
+ this.addSpotMarketToRemainingAccountMaps(
1337
+ readableSpotMarketIndex,
1338
+ false,
1339
+ oracleAccountMap,
1340
+ spotMarketAccountMap
1275
1341
  );
1276
- spotMarketAccountMap.set(readableSpotMarketIndex, {
1277
- pubkey: spotMarketAccount.pubkey,
1278
- isSigner: false,
1279
- isWritable: false,
1280
- });
1281
- if (!spotMarketAccount.oracle.equals(PublicKey.default)) {
1282
- oracleAccountMap.set(spotMarketAccount.oracle.toString(), {
1283
- pubkey: spotMarketAccount.oracle,
1284
- isSigner: false,
1285
- isWritable: false,
1286
- });
1287
- }
1288
1342
  }
1289
1343
  }
1290
1344
 
1345
+ for (const spotMarketIndex of this.mustIncludeSpotMarketIndexes.values()) {
1346
+ this.addSpotMarketToRemainingAccountMaps(
1347
+ spotMarketIndex,
1348
+ false,
1349
+ oracleAccountMap,
1350
+ spotMarketAccountMap
1351
+ );
1352
+ }
1353
+
1291
1354
  if (params.writablePerpMarketIndexes !== undefined) {
1292
1355
  for (const writablePerpMarketIndex of params.writablePerpMarketIndexes) {
1293
- const perpMarketAccount = this.getPerpMarketAccount(
1294
- writablePerpMarketIndex
1356
+ this.addPerpMarketToRemainingAccountMaps(
1357
+ writablePerpMarketIndex,
1358
+ true,
1359
+ oracleAccountMap,
1360
+ spotMarketAccountMap,
1361
+ perpMarketAccountMap
1295
1362
  );
1296
- perpMarketAccountMap.set(writablePerpMarketIndex, {
1297
- pubkey: perpMarketAccount.pubkey,
1298
- isSigner: false,
1299
- isWritable: true,
1300
- });
1301
- oracleAccountMap.set(perpMarketAccount.amm.oracle.toString(), {
1302
- pubkey: perpMarketAccount.amm.oracle,
1303
- isSigner: false,
1304
- isWritable: false,
1305
- });
1306
- const spotMarketAccount = this.getSpotMarketAccount(
1307
- perpMarketAccount.quoteSpotMarketIndex
1308
- );
1309
- spotMarketAccountMap.set(perpMarketAccount.quoteSpotMarketIndex, {
1310
- pubkey: spotMarketAccount.pubkey,
1311
- isSigner: false,
1312
- isWritable: false,
1313
- });
1314
- if (!spotMarketAccount.oracle.equals(PublicKey.default)) {
1315
- oracleAccountMap.set(spotMarketAccount.oracle.toString(), {
1316
- pubkey: spotMarketAccount.oracle,
1317
- isSigner: false,
1318
- isWritable: false,
1319
- });
1320
- }
1321
1363
  }
1322
1364
  }
1323
1365
 
1324
1366
  if (params.writableSpotMarketIndexes !== undefined) {
1325
1367
  for (const writableSpotMarketIndex of params.writableSpotMarketIndexes) {
1326
- const spotMarketAccount = this.getSpotMarketAccount(
1327
- writableSpotMarketIndex
1368
+ this.addSpotMarketToRemainingAccountMaps(
1369
+ writableSpotMarketIndex,
1370
+ true,
1371
+ oracleAccountMap,
1372
+ spotMarketAccountMap
1328
1373
  );
1329
- spotMarketAccountMap.set(spotMarketAccount.marketIndex, {
1330
- pubkey: spotMarketAccount.pubkey,
1331
- isSigner: false,
1332
- isWritable: true,
1333
- });
1334
- if (!spotMarketAccount.oracle.equals(PublicKey.default)) {
1335
- oracleAccountMap.set(spotMarketAccount.oracle.toString(), {
1336
- pubkey: spotMarketAccount.oracle,
1337
- isSigner: false,
1338
- isWritable: false,
1339
- });
1340
- }
1341
1374
  }
1342
1375
  }
1343
1376
 
@@ -1348,6 +1381,53 @@ export class DriftClient {
1348
1381
  ];
1349
1382
  }
1350
1383
 
1384
+ addPerpMarketToRemainingAccountMaps(
1385
+ marketIndex: number,
1386
+ writable: boolean,
1387
+ oracleAccountMap: Map<string, AccountMeta>,
1388
+ spotMarketAccountMap: Map<number, AccountMeta>,
1389
+ perpMarketAccountMap: Map<number, AccountMeta>
1390
+ ): void {
1391
+ const perpMarketAccount = this.getPerpMarketAccount(marketIndex);
1392
+ perpMarketAccountMap.set(marketIndex, {
1393
+ pubkey: perpMarketAccount.pubkey,
1394
+ isSigner: false,
1395
+ isWritable: writable,
1396
+ });
1397
+ oracleAccountMap.set(perpMarketAccount.amm.oracle.toString(), {
1398
+ pubkey: perpMarketAccount.amm.oracle,
1399
+ isSigner: false,
1400
+ isWritable: false,
1401
+ });
1402
+ this.addSpotMarketToRemainingAccountMaps(
1403
+ perpMarketAccount.quoteSpotMarketIndex,
1404
+ false,
1405
+ oracleAccountMap,
1406
+ spotMarketAccountMap
1407
+ );
1408
+ }
1409
+
1410
+ addSpotMarketToRemainingAccountMaps(
1411
+ marketIndex: number,
1412
+ writable: boolean,
1413
+ oracleAccountMap: Map<string, AccountMeta>,
1414
+ spotMarketAccountMap: Map<number, AccountMeta>
1415
+ ): void {
1416
+ const spotMarketAccount = this.getSpotMarketAccount(marketIndex);
1417
+ spotMarketAccountMap.set(spotMarketAccount.marketIndex, {
1418
+ pubkey: spotMarketAccount.pubkey,
1419
+ isSigner: false,
1420
+ isWritable: writable,
1421
+ });
1422
+ if (!spotMarketAccount.oracle.equals(PublicKey.default)) {
1423
+ oracleAccountMap.set(spotMarketAccount.oracle.toString(), {
1424
+ pubkey: spotMarketAccount.oracle,
1425
+ isSigner: false,
1426
+ isWritable: false,
1427
+ });
1428
+ }
1429
+ }
1430
+
1351
1431
  getRemainingAccountMapsForUsers(userAccounts: UserAccount[]): {
1352
1432
  oracleAccountMap: Map<string, AccountMeta>;
1353
1433
  spotMarketAccountMap: Map<number, AccountMeta>;
@@ -1360,73 +1440,35 @@ export class DriftClient {
1360
1440
  for (const userAccount of userAccounts) {
1361
1441
  for (const spotPosition of userAccount.spotPositions) {
1362
1442
  if (!isSpotPositionAvailable(spotPosition)) {
1363
- const spotMarket = this.getSpotMarketAccount(
1364
- spotPosition.marketIndex
1443
+ this.addSpotMarketToRemainingAccountMaps(
1444
+ spotPosition.marketIndex,
1445
+ false,
1446
+ oracleAccountMap,
1447
+ spotMarketAccountMap
1365
1448
  );
1366
- spotMarketAccountMap.set(spotPosition.marketIndex, {
1367
- pubkey: spotMarket.pubkey,
1368
- isSigner: false,
1369
- isWritable: false,
1370
- });
1371
-
1372
- if (!spotMarket.oracle.equals(PublicKey.default)) {
1373
- oracleAccountMap.set(spotMarket.oracle.toString(), {
1374
- pubkey: spotMarket.oracle,
1375
- isSigner: false,
1376
- isWritable: false,
1377
- });
1378
- }
1379
1449
 
1380
1450
  if (
1381
1451
  !spotPosition.openAsks.eq(ZERO) ||
1382
1452
  !spotPosition.openBids.eq(ZERO)
1383
1453
  ) {
1384
- const quoteSpotMarket = this.getQuoteSpotMarketAccount();
1385
- spotMarketAccountMap.set(QUOTE_SPOT_MARKET_INDEX, {
1386
- pubkey: quoteSpotMarket.pubkey,
1387
- isSigner: false,
1388
- isWritable: false,
1389
- });
1390
- if (!quoteSpotMarket.oracle.equals(PublicKey.default)) {
1391
- oracleAccountMap.set(quoteSpotMarket.oracle.toString(), {
1392
- pubkey: quoteSpotMarket.oracle,
1393
- isSigner: false,
1394
- isWritable: false,
1395
- });
1396
- }
1454
+ this.addSpotMarketToRemainingAccountMaps(
1455
+ QUOTE_SPOT_MARKET_INDEX,
1456
+ false,
1457
+ oracleAccountMap,
1458
+ spotMarketAccountMap
1459
+ );
1397
1460
  }
1398
1461
  }
1399
1462
  }
1400
1463
  for (const position of userAccount.perpPositions) {
1401
1464
  if (!positionIsAvailable(position)) {
1402
- const perpMarketAccount = this.getPerpMarketAccount(
1403
- position.marketIndex
1404
- );
1405
- perpMarketAccountMap.set(position.marketIndex, {
1406
- pubkey: perpMarketAccount.pubkey,
1407
- isWritable: false,
1408
- isSigner: false,
1409
- });
1410
- oracleAccountMap.set(perpMarketAccount.amm.oracle.toString(), {
1411
- pubkey: perpMarketAccount.amm.oracle,
1412
- isWritable: false,
1413
- isSigner: false,
1414
- });
1415
- const spotMarketAccount = this.getSpotMarketAccount(
1416
- perpMarketAccount.quoteSpotMarketIndex
1465
+ this.addPerpMarketToRemainingAccountMaps(
1466
+ position.marketIndex,
1467
+ false,
1468
+ oracleAccountMap,
1469
+ spotMarketAccountMap,
1470
+ perpMarketAccountMap
1417
1471
  );
1418
- spotMarketAccountMap.set(perpMarketAccount.quoteSpotMarketIndex, {
1419
- pubkey: spotMarketAccount.pubkey,
1420
- isSigner: false,
1421
- isWritable: false,
1422
- });
1423
- if (!spotMarketAccount.oracle.equals(PublicKey.default)) {
1424
- oracleAccountMap.set(spotMarketAccount.oracle.toString(), {
1425
- pubkey: spotMarketAccount.oracle,
1426
- isSigner: false,
1427
- isWritable: false,
1428
- });
1429
- }
1430
1472
  }
1431
1473
  }
1432
1474
  }
@@ -1630,7 +1672,7 @@ export class DriftClient {
1630
1672
  }
1631
1673
  }
1632
1674
 
1633
- private async getWrappedSolAccountCreationIxs(
1675
+ public async getWrappedSolAccountCreationIxs(
1634
1676
  amount: BN,
1635
1677
  includeRent?: boolean
1636
1678
  ): Promise<{
@@ -1690,7 +1732,7 @@ export class DriftClient {
1690
1732
  }
1691
1733
 
1692
1734
  /**
1693
- * Creates the Clearing House User account for a user, and deposits some initial collateral
1735
+ * Creates the User account for a user, and deposits some initial collateral
1694
1736
  * @param amount
1695
1737
  * @param userTokenAccount
1696
1738
  * @param marketIndex
@@ -2291,6 +2333,35 @@ export class DriftClient {
2291
2333
  });
2292
2334
  }
2293
2335
 
2336
+ public getQuoteValuePerLpShare(marketIndex: number): BN {
2337
+ const perpMarketAccount = this.getPerpMarketAccount(marketIndex);
2338
+
2339
+ const openBids = BN.max(
2340
+ perpMarketAccount.amm.baseAssetReserve.sub(
2341
+ perpMarketAccount.amm.minBaseAssetReserve
2342
+ ),
2343
+ ZERO
2344
+ );
2345
+
2346
+ const openAsks = BN.max(
2347
+ perpMarketAccount.amm.maxBaseAssetReserve.sub(
2348
+ perpMarketAccount.amm.baseAssetReserve
2349
+ ),
2350
+ ZERO
2351
+ );
2352
+
2353
+ const oraclePriceData = this.getOracleDataForPerpMarket(marketIndex);
2354
+
2355
+ const maxOpenBidsAsks = BN.max(openBids, openAsks);
2356
+ const quoteValuePerLpShare = maxOpenBidsAsks
2357
+ .mul(oraclePriceData.price)
2358
+ .mul(QUOTE_PRECISION)
2359
+ .div(PRICE_PRECISION)
2360
+ .div(perpMarketAccount.amm.sqrtK);
2361
+
2362
+ return quoteValuePerLpShare;
2363
+ }
2364
+
2294
2365
  /**
2295
2366
  * @deprecated use {@link placePerpOrder} or {@link placeAndTakePerpOrder} instead
2296
2367
  */
@@ -2756,32 +2827,65 @@ export class DriftClient {
2756
2827
  placeOrderParams: OrderParams[],
2757
2828
  txParams?: TxParams
2758
2829
  ): Promise<TransactionSignature> {
2759
- const tx = wrapInTx(
2830
+ const ixs = [
2760
2831
  await this.getCancelOrdersIx(
2761
2832
  cancelOrderParams.marketType,
2762
2833
  cancelOrderParams.marketIndex,
2763
2834
  cancelOrderParams.direction
2764
2835
  ),
2765
- txParams?.computeUnits,
2766
- txParams?.computeUnitsPrice
2836
+ await this.getPlaceOrdersIx(placeOrderParams),
2837
+ ];
2838
+ const tx = await this.buildTransaction(ixs, txParams);
2839
+ const { txSig } = await this.sendTransaction(tx, [], this.opts);
2840
+ return txSig;
2841
+ }
2842
+
2843
+ public async placeOrders(
2844
+ params: OrderParams[],
2845
+ txParams?: TxParams
2846
+ ): Promise<TransactionSignature> {
2847
+ const { txSig } = await this.sendTransaction(
2848
+ await this.buildTransaction(
2849
+ await this.getPlaceOrdersIx(params),
2850
+ txParams
2851
+ ),
2852
+ [],
2853
+ this.opts
2767
2854
  );
2855
+ return txSig;
2856
+ }
2768
2857
 
2769
- for (const placeOrderParam of placeOrderParams) {
2770
- const marketType = placeOrderParam.marketType;
2771
- if (!marketType) {
2772
- throw new Error('marketType must be set on placeOrderParams');
2773
- }
2774
- let ix;
2775
- if (isVariant(marketType, 'perp')) {
2776
- ix = this.getPlacePerpOrderIx(placeOrderParam);
2858
+ public async getPlaceOrdersIx(
2859
+ params: OrderParams[]
2860
+ ): Promise<TransactionInstruction> {
2861
+ const userAccountPublicKey = await this.getUserAccountPublicKey();
2862
+
2863
+ const readablePerpMarketIndex: number[] = [];
2864
+ const readableSpotMarketIndexes: number[] = [];
2865
+ for (const param of params) {
2866
+ if (isVariant(param.marketType, 'perp')) {
2867
+ readablePerpMarketIndex.push(param.marketIndex);
2777
2868
  } else {
2778
- ix = this.getPlaceSpotOrderIx(placeOrderParam);
2869
+ readableSpotMarketIndexes.push(param.marketIndex);
2779
2870
  }
2780
- tx.add(ix);
2781
2871
  }
2782
2872
 
2783
- const { txSig } = await this.sendTransaction(tx, [], this.opts);
2784
- return txSig;
2873
+ const remainingAccounts = this.getRemainingAccounts({
2874
+ userAccounts: [this.getUserAccount()],
2875
+ readablePerpMarketIndex,
2876
+ readableSpotMarketIndexes,
2877
+ useMarketLastSlotCache: true,
2878
+ });
2879
+
2880
+ return await this.program.instruction.placeOrders(params, {
2881
+ accounts: {
2882
+ state: await this.getStatePublicKey(),
2883
+ user: userAccountPublicKey,
2884
+ userStats: this.getUserStatsAccountPublicKey(),
2885
+ authority: this.wallet.publicKey,
2886
+ },
2887
+ remainingAccounts,
2888
+ });
2785
2889
  }
2786
2890
 
2787
2891
  public async fillPerpOrder(
@@ -3284,6 +3388,61 @@ export class DriftClient {
3284
3388
  reduceOnly?: SwapReduceOnly;
3285
3389
  txParams?: TxParams;
3286
3390
  }): Promise<TransactionSignature> {
3391
+ const { ixs, lookupTables } = await this.getJupiterSwapIx({
3392
+ jupiterClient,
3393
+ outMarketIndex,
3394
+ inMarketIndex,
3395
+ outAssociatedTokenAccount,
3396
+ inAssociatedTokenAccount,
3397
+ amount,
3398
+ slippageBps,
3399
+ swapMode,
3400
+ route,
3401
+ reduceOnly,
3402
+ });
3403
+
3404
+ const tx = (await this.buildTransaction(
3405
+ ixs,
3406
+ txParams,
3407
+ 0,
3408
+ lookupTables
3409
+ )) as VersionedTransaction;
3410
+
3411
+ const { txSig, slot } = await this.sendTransaction(tx);
3412
+ this.spotMarketLastSlotCache.set(outMarketIndex, slot);
3413
+ this.spotMarketLastSlotCache.set(inMarketIndex, slot);
3414
+
3415
+ return txSig;
3416
+ }
3417
+
3418
+ public async getJupiterSwapIx({
3419
+ jupiterClient,
3420
+ outMarketIndex,
3421
+ inMarketIndex,
3422
+ outAssociatedTokenAccount,
3423
+ inAssociatedTokenAccount,
3424
+ amount,
3425
+ slippageBps,
3426
+ swapMode,
3427
+ route,
3428
+ reduceOnly,
3429
+ userAccountPublicKey,
3430
+ }: {
3431
+ jupiterClient: JupiterClient;
3432
+ outMarketIndex: number;
3433
+ inMarketIndex: number;
3434
+ outAssociatedTokenAccount?: PublicKey;
3435
+ inAssociatedTokenAccount?: PublicKey;
3436
+ amount: BN;
3437
+ slippageBps?: number;
3438
+ swapMode?: SwapMode;
3439
+ route?: Route;
3440
+ reduceOnly?: SwapReduceOnly;
3441
+ userAccountPublicKey?: PublicKey;
3442
+ }): Promise<{
3443
+ ixs: TransactionInstruction[];
3444
+ lookupTables: AddressLookupTableAccount[];
3445
+ }> {
3287
3446
  const outMarket = this.getSpotMarketAccount(outMarketIndex);
3288
3447
  const inMarket = this.getSpotMarketAccount(inMarketIndex);
3289
3448
 
@@ -3370,27 +3529,17 @@ export class DriftClient {
3370
3529
  inTokenAccount: inAssociatedTokenAccount,
3371
3530
  outTokenAccount: outAssociatedTokenAccount,
3372
3531
  reduceOnly,
3532
+ userAccountPublicKey,
3373
3533
  });
3374
3534
 
3375
- const instructions = [
3535
+ const ixs = [
3376
3536
  ...preInstructions,
3377
3537
  beginSwapIx,
3378
3538
  ...jupiterInstructions,
3379
3539
  endSwapIx,
3380
3540
  ];
3381
3541
 
3382
- const tx = (await this.buildTransaction(
3383
- instructions,
3384
- txParams,
3385
- 0,
3386
- lookupTables
3387
- )) as VersionedTransaction;
3388
-
3389
- const { txSig, slot } = await this.sendTransaction(tx);
3390
- this.spotMarketLastSlotCache.set(outMarketIndex, slot);
3391
- this.spotMarketLastSlotCache.set(inMarketIndex, slot);
3392
-
3393
- return txSig;
3542
+ return { ixs, lookupTables };
3394
3543
  }
3395
3544
 
3396
3545
  /**
@@ -3402,6 +3551,8 @@ export class DriftClient {
3402
3551
  * @param inTokenAccount the token account to move the tokens being sold
3403
3552
  * @param outTokenAccount the token account to receive the tokens being bought
3404
3553
  * @param limitPrice the limit price of the swap
3554
+ * @param reduceOnly
3555
+ * @param userAccountPublicKey optional, specify a custom userAccountPublicKey to use instead of getting the current user account; can be helpful if the account is being created within the current tx
3405
3556
  */
3406
3557
  public async getSwapIx({
3407
3558
  outMarketIndex,
@@ -3411,6 +3562,7 @@ export class DriftClient {
3411
3562
  outTokenAccount,
3412
3563
  limitPrice,
3413
3564
  reduceOnly,
3565
+ userAccountPublicKey,
3414
3566
  }: {
3415
3567
  outMarketIndex: number;
3416
3568
  inMarketIndex: number;
@@ -3419,14 +3571,17 @@ export class DriftClient {
3419
3571
  outTokenAccount: PublicKey;
3420
3572
  limitPrice?: BN;
3421
3573
  reduceOnly?: SwapReduceOnly;
3574
+ userAccountPublicKey?: PublicKey;
3422
3575
  }): Promise<{
3423
3576
  beginSwapIx: TransactionInstruction;
3424
3577
  endSwapIx: TransactionInstruction;
3425
3578
  }> {
3426
- const userAccountPublicKey = await this.getUserAccountPublicKey();
3579
+ const userAccountPublicKeyToUse =
3580
+ userAccountPublicKey || (await this.getUserAccountPublicKey());
3427
3581
 
3582
+ const userAccounts = this.hasUser() ? [this.getUserAccount()] : [];
3428
3583
  const remainingAccounts = this.getRemainingAccounts({
3429
- userAccounts: [this.getUserAccount()],
3584
+ userAccounts,
3430
3585
  writableSpotMarketIndexes: [outMarketIndex, inMarketIndex],
3431
3586
  });
3432
3587
 
@@ -3440,7 +3595,7 @@ export class DriftClient {
3440
3595
  {
3441
3596
  accounts: {
3442
3597
  state: await this.getStatePublicKey(),
3443
- user: userAccountPublicKey,
3598
+ user: userAccountPublicKeyToUse,
3444
3599
  userStats: this.getUserStatsAccountPublicKey(),
3445
3600
  authority: this.authority,
3446
3601
  outSpotMarketVault: outSpotMarket.vault,
@@ -3463,7 +3618,7 @@ export class DriftClient {
3463
3618
  {
3464
3619
  accounts: {
3465
3620
  state: await this.getStatePublicKey(),
3466
- user: userAccountPublicKey,
3621
+ user: userAccountPublicKeyToUse,
3467
3622
  userStats: this.getUserStatsAccountPublicKey(),
3468
3623
  authority: this.authority,
3469
3624
  outSpotMarketVault: outSpotMarket.vault,
@@ -3481,6 +3636,66 @@ export class DriftClient {
3481
3636
  return { beginSwapIx, endSwapIx };
3482
3637
  }
3483
3638
 
3639
+ public async stakeForMSOL({ amount }: { amount: BN }): Promise<TxSigAndSlot> {
3640
+ const ixs = await this.getStakeForMSOLIx({ amount });
3641
+ const tx = await this.buildTransaction(ixs);
3642
+ return this.sendTransaction(tx);
3643
+ }
3644
+
3645
+ public async getStakeForMSOLIx({
3646
+ amount,
3647
+ userAccountPublicKey,
3648
+ }: {
3649
+ amount: BN;
3650
+ userAccountPublicKey?: PublicKey;
3651
+ }): Promise<TransactionInstruction[]> {
3652
+ const wSOLMint = this.getSpotMarketAccount(1).mint;
3653
+ const mSOLAccount = await this.getAssociatedTokenAccount(2);
3654
+ const wSOLAccount = await this.getAssociatedTokenAccount(1, false);
3655
+
3656
+ const wSOLAccountExists = await this.checkIfAccountExists(wSOLAccount);
3657
+
3658
+ const closeWSOLIx = createCloseAccountInstruction(
3659
+ wSOLAccount,
3660
+ this.wallet.publicKey,
3661
+ this.wallet.publicKey
3662
+ );
3663
+
3664
+ const createWSOLIx =
3665
+ await this.createAssociatedTokenAccountIdempotentInstruction(
3666
+ wSOLAccount,
3667
+ this.wallet.publicKey,
3668
+ this.wallet.publicKey,
3669
+ wSOLMint
3670
+ );
3671
+
3672
+ const { beginSwapIx, endSwapIx } = await this.getSwapIx({
3673
+ inMarketIndex: 1,
3674
+ outMarketIndex: 2,
3675
+ amountIn: amount,
3676
+ inTokenAccount: wSOLAccount,
3677
+ outTokenAccount: mSOLAccount,
3678
+ userAccountPublicKey,
3679
+ });
3680
+
3681
+ const program = getMarinadeFinanceProgram(this.provider);
3682
+ const depositIx = await getMarinadeDepositIx({
3683
+ program,
3684
+ mSOLAccount: mSOLAccount,
3685
+ transferFrom: this.wallet.publicKey,
3686
+ amount,
3687
+ });
3688
+
3689
+ const ixs = [];
3690
+
3691
+ if (!wSOLAccountExists) {
3692
+ ixs.push(createWSOLIx);
3693
+ }
3694
+ ixs.push(beginSwapIx, closeWSOLIx, depositIx, createWSOLIx, endSwapIx);
3695
+
3696
+ return ixs;
3697
+ }
3698
+
3484
3699
  public async triggerOrder(
3485
3700
  userAccountPublicKey: PublicKey,
3486
3701
  user: UserAccount,
@@ -5361,7 +5576,7 @@ export class DriftClient {
5361
5576
  lookupTables?: AddressLookupTableAccount[]
5362
5577
  ): Promise<Transaction | VersionedTransaction> {
5363
5578
  const allIx = [];
5364
- const computeUnits = txParams?.computeUnits ?? 600_000;
5579
+ const computeUnits = txParams?.computeUnits ?? this.txParams.computeUnits;
5365
5580
  if (computeUnits !== 200_000) {
5366
5581
  allIx.push(
5367
5582
  ComputeBudgetProgram.setComputeUnitLimit({
@@ -5369,7 +5584,8 @@ export class DriftClient {
5369
5584
  })
5370
5585
  );
5371
5586
  }
5372
- const computeUnitsPrice = txParams?.computeUnitsPrice ?? 0;
5587
+ const computeUnitsPrice =
5588
+ txParams?.computeUnitsPrice ?? this.txParams.computeUnitsPrice;
5373
5589
  if (computeUnitsPrice !== 0) {
5374
5590
  allIx.push(
5375
5591
  ComputeBudgetProgram.setComputeUnitPrice({