@drift-labs/sdk 2.31.1-beta.8 → 2.32.0

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 (42) hide show
  1. package/VERSION +1 -1
  2. package/lib/constants/perpMarkets.js +20 -0
  3. package/lib/dlob/orderBookLevels.js +2 -2
  4. package/lib/driftClient.d.ts +53 -5
  5. package/lib/driftClient.js +203 -196
  6. package/lib/idl/drift.json +31 -1
  7. package/lib/index.d.ts +3 -0
  8. package/lib/index.js +3 -0
  9. package/lib/marinade/index.d.ts +11 -0
  10. package/lib/marinade/index.js +36 -0
  11. package/lib/marinade/types.d.ts +1963 -0
  12. package/lib/marinade/types.js +1965 -0
  13. package/lib/math/spotBalance.d.ts +9 -2
  14. package/lib/math/spotBalance.js +54 -6
  15. package/lib/math/superStake.d.ts +22 -0
  16. package/lib/math/superStake.js +108 -0
  17. package/lib/math/utils.d.ts +1 -0
  18. package/lib/math/utils.js +5 -1
  19. package/lib/orderParams.d.ts +18 -5
  20. package/lib/orderParams.js +17 -1
  21. package/lib/user.d.ts +45 -1
  22. package/lib/user.js +227 -9
  23. package/package.json +1 -1
  24. package/src/assert/assert.js +9 -0
  25. package/src/constants/perpMarkets.ts +20 -0
  26. package/src/dlob/orderBookLevels.ts +3 -2
  27. package/src/driftClient.ts +383 -225
  28. package/src/idl/drift.json +31 -1
  29. package/src/index.ts +3 -0
  30. package/src/marinade/idl/idl.json +1962 -0
  31. package/src/marinade/index.ts +64 -0
  32. package/src/marinade/types.ts +3925 -0
  33. package/src/math/spotBalance.ts +83 -5
  34. package/src/math/superStake.ts +148 -0
  35. package/src/math/utils.ts +4 -0
  36. package/src/orderParams.ts +35 -5
  37. package/src/token/index.js +38 -0
  38. package/src/user.ts +453 -15
  39. package/src/util/computeUnits.js +27 -0
  40. package/src/util/promiseTimeout.js +14 -0
  41. package/src/util/tps.js +27 -0
  42. package/tests/spot/test.ts +156 -0
@@ -27,7 +27,6 @@ import {
27
27
  MakerInfo,
28
28
  TakerInfo,
29
29
  OptionalOrderParams,
30
- DefaultOrderParams,
31
30
  OrderType,
32
31
  ReferrerInfo,
33
32
  MarketType,
@@ -117,12 +116,14 @@ import { castNumberToSpotPrecision } from './math/spotMarket';
117
116
  import { JupiterClient, Route, SwapMode } from './jupiter/jupiterClient';
118
117
  import { getNonIdleUserFilter } from './memcmp';
119
118
  import { UserStatsSubscriptionConfig } from './userStatsConfig';
119
+ import { getMarinadeDepositIx, getMarinadeFinanceProgram } from './marinade';
120
+ import { getOrderParams } from './orderParams';
120
121
 
121
122
  type RemainingAccountParams = {
122
123
  userAccounts: UserAccount[];
123
124
  writablePerpMarketIndexes?: number[];
124
125
  writableSpotMarketIndexes?: number[];
125
- readablePerpMarketIndex?: number;
126
+ readablePerpMarketIndex?: number | number[];
126
127
  readableSpotMarketIndexes?: number[];
127
128
  useMarketLastSlotCache?: boolean;
128
129
  };
@@ -148,6 +149,8 @@ export class DriftClient {
148
149
  txSender: TxSender;
149
150
  perpMarketLastSlotCache = new Map<number, number>();
150
151
  spotMarketLastSlotCache = new Map<number, number>();
152
+ mustIncludePerpMarketIndexes = new Set<number>();
153
+ mustIncludeSpotMarketIndexes = new Set<number>();
151
154
  authority: PublicKey;
152
155
  marketLookupTable: PublicKey;
153
156
  lookupTableAccount: AddressLookupTableAccount;
@@ -860,6 +863,43 @@ export class DriftClient {
860
863
  return txSig;
861
864
  }
862
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
+
863
903
  public async updateUserMarginTradingEnabled(
864
904
  marginTradingEnabled: boolean,
865
905
  subAccountId = 0
@@ -1050,11 +1090,19 @@ export class DriftClient {
1050
1090
  const userMapKey = this.getUserMapKey(subAccountId, authority);
1051
1091
 
1052
1092
  if (!this.users.has(userMapKey)) {
1053
- throw new Error(`Clearing House has no user for user id ${userMapKey}`);
1093
+ throw new Error(`DriftClient has no user for user id ${userMapKey}`);
1054
1094
  }
1055
1095
  return this.users.get(userMapKey);
1056
1096
  }
1057
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
+
1058
1106
  public getUsers(): User[] {
1059
1107
  // delegate users get added to the end
1060
1108
  return [...this.users.values()]
@@ -1189,6 +1237,29 @@ export class DriftClient {
1189
1237
  return amount.mul(PRICE_PRECISION);
1190
1238
  }
1191
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
+
1192
1263
  getRemainingAccounts(params: RemainingAccountParams): AccountMeta[] {
1193
1264
  const { oracleAccountMap, spotMarketAccountMap, perpMarketAccountMap } =
1194
1265
  this.getRemainingAccountMapsForUsers(params.userAccounts);
@@ -1202,32 +1273,13 @@ export class DriftClient {
1202
1273
  // if cache has more recent slot than user positions account slot, add market to remaining accounts
1203
1274
  // otherwise remove from slot
1204
1275
  if (slot > lastUserSlot) {
1205
- const perpMarketAccount = this.getPerpMarketAccount(marketIndex);
1206
- perpMarketAccountMap.set(marketIndex, {
1207
- pubkey: perpMarketAccount.pubkey,
1208
- isSigner: false,
1209
- isWritable: false,
1210
- });
1211
- oracleAccountMap.set(perpMarketAccount.amm.oracle.toString(), {
1212
- pubkey: perpMarketAccount.amm.oracle,
1213
- isSigner: false,
1214
- isWritable: false,
1215
- });
1216
- const spotMarketAccount = this.getSpotMarketAccount(
1217
- perpMarketAccount.quoteSpotMarketIndex
1276
+ this.addPerpMarketToRemainingAccountMaps(
1277
+ marketIndex,
1278
+ false,
1279
+ oracleAccountMap,
1280
+ spotMarketAccountMap,
1281
+ perpMarketAccountMap
1218
1282
  );
1219
- spotMarketAccountMap.set(perpMarketAccount.quoteSpotMarketIndex, {
1220
- pubkey: spotMarketAccount.pubkey,
1221
- isSigner: false,
1222
- isWritable: false,
1223
- });
1224
- if (!spotMarketAccount.oracle.equals(PublicKey.default)) {
1225
- oracleAccountMap.set(spotMarketAccount.oracle.toString(), {
1226
- pubkey: spotMarketAccount.oracle,
1227
- isSigner: false,
1228
- isWritable: false,
1229
- });
1230
- }
1231
1283
  } else {
1232
1284
  this.perpMarketLastSlotCache.delete(marketIndex);
1233
1285
  }
@@ -1240,19 +1292,12 @@ export class DriftClient {
1240
1292
  // if cache has more recent slot than user positions account slot, add market to remaining accounts
1241
1293
  // otherwise remove from slot
1242
1294
  if (slot > lastUserSlot) {
1243
- const spotMarketAccount = this.getSpotMarketAccount(marketIndex);
1244
- spotMarketAccountMap.set(marketIndex, {
1245
- pubkey: spotMarketAccount.pubkey,
1246
- isSigner: false,
1247
- isWritable: false,
1248
- });
1249
- if (!spotMarketAccount.oracle.equals(PublicKey.default)) {
1250
- oracleAccountMap.set(spotMarketAccount.oracle.toString(), {
1251
- pubkey: spotMarketAccount.oracle,
1252
- isSigner: false,
1253
- isWritable: false,
1254
- });
1255
- }
1295
+ this.addSpotMarketToRemainingAccountMaps(
1296
+ marketIndex,
1297
+ false,
1298
+ oracleAccountMap,
1299
+ spotMarketAccountMap
1300
+ );
1256
1301
  } else {
1257
1302
  this.spotMarketLastSlotCache.delete(marketIndex);
1258
1303
  }
@@ -1260,106 +1305,72 @@ export class DriftClient {
1260
1305
  }
1261
1306
 
1262
1307
  if (params.readablePerpMarketIndex !== undefined) {
1263
- const perpMarketAccount = this.getPerpMarketAccount(
1308
+ const readablePerpMarketIndexes = Array.isArray(
1264
1309
  params.readablePerpMarketIndex
1265
- );
1266
- perpMarketAccountMap.set(params.readablePerpMarketIndex, {
1267
- pubkey: perpMarketAccount.pubkey,
1268
- isSigner: false,
1269
- isWritable: false,
1270
- });
1271
- oracleAccountMap.set(perpMarketAccount.amm.oracle.toString(), {
1272
- pubkey: perpMarketAccount.amm.oracle,
1273
- isSigner: false,
1274
- isWritable: false,
1275
- });
1276
- const spotMarketAccount = this.getSpotMarketAccount(
1277
- perpMarketAccount.quoteSpotMarketIndex
1278
- );
1279
- spotMarketAccountMap.set(perpMarketAccount.quoteSpotMarketIndex, {
1280
- pubkey: spotMarketAccount.pubkey,
1281
- isSigner: false,
1282
- isWritable: false,
1283
- });
1284
- if (!spotMarketAccount.oracle.equals(PublicKey.default)) {
1285
- oracleAccountMap.set(spotMarketAccount.oracle.toString(), {
1286
- pubkey: spotMarketAccount.oracle,
1287
- isSigner: false,
1288
- isWritable: false,
1289
- });
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
+ );
1290
1321
  }
1291
1322
  }
1292
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
+
1293
1334
  if (params.readableSpotMarketIndexes !== undefined) {
1294
1335
  for (const readableSpotMarketIndex of params.readableSpotMarketIndexes) {
1295
- const spotMarketAccount = this.getSpotMarketAccount(
1296
- readableSpotMarketIndex
1336
+ this.addSpotMarketToRemainingAccountMaps(
1337
+ readableSpotMarketIndex,
1338
+ false,
1339
+ oracleAccountMap,
1340
+ spotMarketAccountMap
1297
1341
  );
1298
- spotMarketAccountMap.set(readableSpotMarketIndex, {
1299
- pubkey: spotMarketAccount.pubkey,
1300
- isSigner: false,
1301
- isWritable: false,
1302
- });
1303
- if (!spotMarketAccount.oracle.equals(PublicKey.default)) {
1304
- oracleAccountMap.set(spotMarketAccount.oracle.toString(), {
1305
- pubkey: spotMarketAccount.oracle,
1306
- isSigner: false,
1307
- isWritable: false,
1308
- });
1309
- }
1310
1342
  }
1311
1343
  }
1312
1344
 
1345
+ for (const spotMarketIndex of this.mustIncludeSpotMarketIndexes.values()) {
1346
+ this.addSpotMarketToRemainingAccountMaps(
1347
+ spotMarketIndex,
1348
+ false,
1349
+ oracleAccountMap,
1350
+ spotMarketAccountMap
1351
+ );
1352
+ }
1353
+
1313
1354
  if (params.writablePerpMarketIndexes !== undefined) {
1314
1355
  for (const writablePerpMarketIndex of params.writablePerpMarketIndexes) {
1315
- const perpMarketAccount = this.getPerpMarketAccount(
1316
- writablePerpMarketIndex
1356
+ this.addPerpMarketToRemainingAccountMaps(
1357
+ writablePerpMarketIndex,
1358
+ true,
1359
+ oracleAccountMap,
1360
+ spotMarketAccountMap,
1361
+ perpMarketAccountMap
1317
1362
  );
1318
- perpMarketAccountMap.set(writablePerpMarketIndex, {
1319
- pubkey: perpMarketAccount.pubkey,
1320
- isSigner: false,
1321
- isWritable: true,
1322
- });
1323
- oracleAccountMap.set(perpMarketAccount.amm.oracle.toString(), {
1324
- pubkey: perpMarketAccount.amm.oracle,
1325
- isSigner: false,
1326
- isWritable: false,
1327
- });
1328
- const spotMarketAccount = this.getSpotMarketAccount(
1329
- perpMarketAccount.quoteSpotMarketIndex
1330
- );
1331
- spotMarketAccountMap.set(perpMarketAccount.quoteSpotMarketIndex, {
1332
- pubkey: spotMarketAccount.pubkey,
1333
- isSigner: false,
1334
- isWritable: false,
1335
- });
1336
- if (!spotMarketAccount.oracle.equals(PublicKey.default)) {
1337
- oracleAccountMap.set(spotMarketAccount.oracle.toString(), {
1338
- pubkey: spotMarketAccount.oracle,
1339
- isSigner: false,
1340
- isWritable: false,
1341
- });
1342
- }
1343
1363
  }
1344
1364
  }
1345
1365
 
1346
1366
  if (params.writableSpotMarketIndexes !== undefined) {
1347
1367
  for (const writableSpotMarketIndex of params.writableSpotMarketIndexes) {
1348
- const spotMarketAccount = this.getSpotMarketAccount(
1349
- writableSpotMarketIndex
1368
+ this.addSpotMarketToRemainingAccountMaps(
1369
+ writableSpotMarketIndex,
1370
+ true,
1371
+ oracleAccountMap,
1372
+ spotMarketAccountMap
1350
1373
  );
1351
- spotMarketAccountMap.set(spotMarketAccount.marketIndex, {
1352
- pubkey: spotMarketAccount.pubkey,
1353
- isSigner: false,
1354
- isWritable: true,
1355
- });
1356
- if (!spotMarketAccount.oracle.equals(PublicKey.default)) {
1357
- oracleAccountMap.set(spotMarketAccount.oracle.toString(), {
1358
- pubkey: spotMarketAccount.oracle,
1359
- isSigner: false,
1360
- isWritable: false,
1361
- });
1362
- }
1363
1374
  }
1364
1375
  }
1365
1376
 
@@ -1370,6 +1381,53 @@ export class DriftClient {
1370
1381
  ];
1371
1382
  }
1372
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
+
1373
1431
  getRemainingAccountMapsForUsers(userAccounts: UserAccount[]): {
1374
1432
  oracleAccountMap: Map<string, AccountMeta>;
1375
1433
  spotMarketAccountMap: Map<number, AccountMeta>;
@@ -1382,73 +1440,35 @@ export class DriftClient {
1382
1440
  for (const userAccount of userAccounts) {
1383
1441
  for (const spotPosition of userAccount.spotPositions) {
1384
1442
  if (!isSpotPositionAvailable(spotPosition)) {
1385
- const spotMarket = this.getSpotMarketAccount(
1386
- spotPosition.marketIndex
1443
+ this.addSpotMarketToRemainingAccountMaps(
1444
+ spotPosition.marketIndex,
1445
+ false,
1446
+ oracleAccountMap,
1447
+ spotMarketAccountMap
1387
1448
  );
1388
- spotMarketAccountMap.set(spotPosition.marketIndex, {
1389
- pubkey: spotMarket.pubkey,
1390
- isSigner: false,
1391
- isWritable: false,
1392
- });
1393
-
1394
- if (!spotMarket.oracle.equals(PublicKey.default)) {
1395
- oracleAccountMap.set(spotMarket.oracle.toString(), {
1396
- pubkey: spotMarket.oracle,
1397
- isSigner: false,
1398
- isWritable: false,
1399
- });
1400
- }
1401
1449
 
1402
1450
  if (
1403
1451
  !spotPosition.openAsks.eq(ZERO) ||
1404
1452
  !spotPosition.openBids.eq(ZERO)
1405
1453
  ) {
1406
- const quoteSpotMarket = this.getQuoteSpotMarketAccount();
1407
- spotMarketAccountMap.set(QUOTE_SPOT_MARKET_INDEX, {
1408
- pubkey: quoteSpotMarket.pubkey,
1409
- isSigner: false,
1410
- isWritable: false,
1411
- });
1412
- if (!quoteSpotMarket.oracle.equals(PublicKey.default)) {
1413
- oracleAccountMap.set(quoteSpotMarket.oracle.toString(), {
1414
- pubkey: quoteSpotMarket.oracle,
1415
- isSigner: false,
1416
- isWritable: false,
1417
- });
1418
- }
1454
+ this.addSpotMarketToRemainingAccountMaps(
1455
+ QUOTE_SPOT_MARKET_INDEX,
1456
+ false,
1457
+ oracleAccountMap,
1458
+ spotMarketAccountMap
1459
+ );
1419
1460
  }
1420
1461
  }
1421
1462
  }
1422
1463
  for (const position of userAccount.perpPositions) {
1423
1464
  if (!positionIsAvailable(position)) {
1424
- const perpMarketAccount = this.getPerpMarketAccount(
1425
- position.marketIndex
1426
- );
1427
- perpMarketAccountMap.set(position.marketIndex, {
1428
- pubkey: perpMarketAccount.pubkey,
1429
- isWritable: false,
1430
- isSigner: false,
1431
- });
1432
- oracleAccountMap.set(perpMarketAccount.amm.oracle.toString(), {
1433
- pubkey: perpMarketAccount.amm.oracle,
1434
- isWritable: false,
1435
- isSigner: false,
1436
- });
1437
- const spotMarketAccount = this.getSpotMarketAccount(
1438
- perpMarketAccount.quoteSpotMarketIndex
1465
+ this.addPerpMarketToRemainingAccountMaps(
1466
+ position.marketIndex,
1467
+ false,
1468
+ oracleAccountMap,
1469
+ spotMarketAccountMap,
1470
+ perpMarketAccountMap
1439
1471
  );
1440
- spotMarketAccountMap.set(perpMarketAccount.quoteSpotMarketIndex, {
1441
- pubkey: spotMarketAccount.pubkey,
1442
- isSigner: false,
1443
- isWritable: false,
1444
- });
1445
- if (!spotMarketAccount.oracle.equals(PublicKey.default)) {
1446
- oracleAccountMap.set(spotMarketAccount.oracle.toString(), {
1447
- pubkey: spotMarketAccount.oracle,
1448
- isSigner: false,
1449
- isWritable: false,
1450
- });
1451
- }
1452
1472
  }
1453
1473
  }
1454
1474
  }
@@ -1652,7 +1672,7 @@ export class DriftClient {
1652
1672
  }
1653
1673
  }
1654
1674
 
1655
- private async getWrappedSolAccountCreationIxs(
1675
+ public async getWrappedSolAccountCreationIxs(
1656
1676
  amount: BN,
1657
1677
  includeRent?: boolean
1658
1678
  ): Promise<{
@@ -1712,7 +1732,7 @@ export class DriftClient {
1712
1732
  }
1713
1733
 
1714
1734
  /**
1715
- * 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
1716
1736
  * @param amount
1717
1737
  * @param userTokenAccount
1718
1738
  * @param marketIndex
@@ -2502,19 +2522,10 @@ export class DriftClient {
2502
2522
  return txSig;
2503
2523
  }
2504
2524
 
2505
- getOrderParams(
2506
- optionalOrderParams: OptionalOrderParams,
2507
- marketType: MarketType
2508
- ): OrderParams {
2509
- return Object.assign({}, DefaultOrderParams, optionalOrderParams, {
2510
- marketType,
2511
- });
2512
- }
2513
-
2514
2525
  public async getPlacePerpOrderIx(
2515
2526
  orderParams: OptionalOrderParams
2516
2527
  ): Promise<TransactionInstruction> {
2517
- orderParams = this.getOrderParams(orderParams, MarketType.PERP);
2528
+ orderParams = getOrderParams(orderParams, { marketType: MarketType.PERP });
2518
2529
  const userAccountPublicKey = await this.getUserAccountPublicKey();
2519
2530
 
2520
2531
  const remainingAccounts = this.getRemainingAccounts({
@@ -2807,32 +2818,68 @@ export class DriftClient {
2807
2818
  placeOrderParams: OrderParams[],
2808
2819
  txParams?: TxParams
2809
2820
  ): Promise<TransactionSignature> {
2810
- const tx = wrapInTx(
2821
+ const ixs = [
2811
2822
  await this.getCancelOrdersIx(
2812
2823
  cancelOrderParams.marketType,
2813
2824
  cancelOrderParams.marketIndex,
2814
2825
  cancelOrderParams.direction
2815
2826
  ),
2816
- txParams?.computeUnits,
2817
- txParams?.computeUnitsPrice
2827
+ await this.getPlaceOrdersIx(placeOrderParams),
2828
+ ];
2829
+ const tx = await this.buildTransaction(ixs, txParams);
2830
+ const { txSig } = await this.sendTransaction(tx, [], this.opts);
2831
+ return txSig;
2832
+ }
2833
+
2834
+ public async placeOrders(
2835
+ params: OrderParams[],
2836
+ txParams?: TxParams
2837
+ ): Promise<TransactionSignature> {
2838
+ const { txSig } = await this.sendTransaction(
2839
+ await this.buildTransaction(
2840
+ await this.getPlaceOrdersIx(params),
2841
+ txParams
2842
+ ),
2843
+ [],
2844
+ this.opts
2818
2845
  );
2846
+ return txSig;
2847
+ }
2819
2848
 
2820
- for (const placeOrderParam of placeOrderParams) {
2821
- const marketType = placeOrderParam.marketType;
2822
- if (!marketType) {
2823
- throw new Error('marketType must be set on placeOrderParams');
2849
+ public async getPlaceOrdersIx(
2850
+ params: OrderParams[]
2851
+ ): Promise<TransactionInstruction> {
2852
+ const userAccountPublicKey = await this.getUserAccountPublicKey();
2853
+
2854
+ const readablePerpMarketIndex: number[] = [];
2855
+ const readableSpotMarketIndexes: number[] = [];
2856
+ for (const param of params) {
2857
+ if (!param.marketType) {
2858
+ throw new Error('must set param.marketType');
2824
2859
  }
2825
- let ix;
2826
- if (isVariant(marketType, 'perp')) {
2827
- ix = this.getPlacePerpOrderIx(placeOrderParam);
2860
+ if (isVariant(param.marketType, 'perp')) {
2861
+ readablePerpMarketIndex.push(param.marketIndex);
2828
2862
  } else {
2829
- ix = this.getPlaceSpotOrderIx(placeOrderParam);
2863
+ readableSpotMarketIndexes.push(param.marketIndex);
2830
2864
  }
2831
- tx.add(ix);
2832
2865
  }
2833
2866
 
2834
- const { txSig } = await this.sendTransaction(tx, [], this.opts);
2835
- return txSig;
2867
+ const remainingAccounts = this.getRemainingAccounts({
2868
+ userAccounts: [this.getUserAccount()],
2869
+ readablePerpMarketIndex,
2870
+ readableSpotMarketIndexes,
2871
+ useMarketLastSlotCache: true,
2872
+ });
2873
+
2874
+ return await this.program.instruction.placeOrders(params, {
2875
+ accounts: {
2876
+ state: await this.getStatePublicKey(),
2877
+ user: userAccountPublicKey,
2878
+ userStats: this.getUserStatsAccountPublicKey(),
2879
+ authority: this.wallet.publicKey,
2880
+ },
2881
+ remainingAccounts,
2882
+ });
2836
2883
  }
2837
2884
 
2838
2885
  public async fillPerpOrder(
@@ -2975,7 +3022,7 @@ export class DriftClient {
2975
3022
  public async getPlaceSpotOrderIx(
2976
3023
  orderParams: OptionalOrderParams
2977
3024
  ): Promise<TransactionInstruction> {
2978
- orderParams = this.getOrderParams(orderParams, MarketType.SPOT);
3025
+ orderParams = getOrderParams(orderParams, { marketType: MarketType.SPOT });
2979
3026
  const userAccountPublicKey = await this.getUserAccountPublicKey();
2980
3027
 
2981
3028
  const remainingAccounts = this.getRemainingAccounts({
@@ -3335,6 +3382,61 @@ export class DriftClient {
3335
3382
  reduceOnly?: SwapReduceOnly;
3336
3383
  txParams?: TxParams;
3337
3384
  }): Promise<TransactionSignature> {
3385
+ const { ixs, lookupTables } = await this.getJupiterSwapIx({
3386
+ jupiterClient,
3387
+ outMarketIndex,
3388
+ inMarketIndex,
3389
+ outAssociatedTokenAccount,
3390
+ inAssociatedTokenAccount,
3391
+ amount,
3392
+ slippageBps,
3393
+ swapMode,
3394
+ route,
3395
+ reduceOnly,
3396
+ });
3397
+
3398
+ const tx = (await this.buildTransaction(
3399
+ ixs,
3400
+ txParams,
3401
+ 0,
3402
+ lookupTables
3403
+ )) as VersionedTransaction;
3404
+
3405
+ const { txSig, slot } = await this.sendTransaction(tx);
3406
+ this.spotMarketLastSlotCache.set(outMarketIndex, slot);
3407
+ this.spotMarketLastSlotCache.set(inMarketIndex, slot);
3408
+
3409
+ return txSig;
3410
+ }
3411
+
3412
+ public async getJupiterSwapIx({
3413
+ jupiterClient,
3414
+ outMarketIndex,
3415
+ inMarketIndex,
3416
+ outAssociatedTokenAccount,
3417
+ inAssociatedTokenAccount,
3418
+ amount,
3419
+ slippageBps,
3420
+ swapMode,
3421
+ route,
3422
+ reduceOnly,
3423
+ userAccountPublicKey,
3424
+ }: {
3425
+ jupiterClient: JupiterClient;
3426
+ outMarketIndex: number;
3427
+ inMarketIndex: number;
3428
+ outAssociatedTokenAccount?: PublicKey;
3429
+ inAssociatedTokenAccount?: PublicKey;
3430
+ amount: BN;
3431
+ slippageBps?: number;
3432
+ swapMode?: SwapMode;
3433
+ route?: Route;
3434
+ reduceOnly?: SwapReduceOnly;
3435
+ userAccountPublicKey?: PublicKey;
3436
+ }): Promise<{
3437
+ ixs: TransactionInstruction[];
3438
+ lookupTables: AddressLookupTableAccount[];
3439
+ }> {
3338
3440
  const outMarket = this.getSpotMarketAccount(outMarketIndex);
3339
3441
  const inMarket = this.getSpotMarketAccount(inMarketIndex);
3340
3442
 
@@ -3421,27 +3523,17 @@ export class DriftClient {
3421
3523
  inTokenAccount: inAssociatedTokenAccount,
3422
3524
  outTokenAccount: outAssociatedTokenAccount,
3423
3525
  reduceOnly,
3526
+ userAccountPublicKey,
3424
3527
  });
3425
3528
 
3426
- const instructions = [
3529
+ const ixs = [
3427
3530
  ...preInstructions,
3428
3531
  beginSwapIx,
3429
3532
  ...jupiterInstructions,
3430
3533
  endSwapIx,
3431
3534
  ];
3432
3535
 
3433
- const tx = (await this.buildTransaction(
3434
- instructions,
3435
- txParams,
3436
- 0,
3437
- lookupTables
3438
- )) as VersionedTransaction;
3439
-
3440
- const { txSig, slot } = await this.sendTransaction(tx);
3441
- this.spotMarketLastSlotCache.set(outMarketIndex, slot);
3442
- this.spotMarketLastSlotCache.set(inMarketIndex, slot);
3443
-
3444
- return txSig;
3536
+ return { ixs, lookupTables };
3445
3537
  }
3446
3538
 
3447
3539
  /**
@@ -3453,6 +3545,8 @@ export class DriftClient {
3453
3545
  * @param inTokenAccount the token account to move the tokens being sold
3454
3546
  * @param outTokenAccount the token account to receive the tokens being bought
3455
3547
  * @param limitPrice the limit price of the swap
3548
+ * @param reduceOnly
3549
+ * @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
3456
3550
  */
3457
3551
  public async getSwapIx({
3458
3552
  outMarketIndex,
@@ -3462,6 +3556,7 @@ export class DriftClient {
3462
3556
  outTokenAccount,
3463
3557
  limitPrice,
3464
3558
  reduceOnly,
3559
+ userAccountPublicKey,
3465
3560
  }: {
3466
3561
  outMarketIndex: number;
3467
3562
  inMarketIndex: number;
@@ -3470,14 +3565,17 @@ export class DriftClient {
3470
3565
  outTokenAccount: PublicKey;
3471
3566
  limitPrice?: BN;
3472
3567
  reduceOnly?: SwapReduceOnly;
3568
+ userAccountPublicKey?: PublicKey;
3473
3569
  }): Promise<{
3474
3570
  beginSwapIx: TransactionInstruction;
3475
3571
  endSwapIx: TransactionInstruction;
3476
3572
  }> {
3477
- const userAccountPublicKey = await this.getUserAccountPublicKey();
3573
+ const userAccountPublicKeyToUse =
3574
+ userAccountPublicKey || (await this.getUserAccountPublicKey());
3478
3575
 
3576
+ const userAccounts = this.hasUser() ? [this.getUserAccount()] : [];
3479
3577
  const remainingAccounts = this.getRemainingAccounts({
3480
- userAccounts: [this.getUserAccount()],
3578
+ userAccounts,
3481
3579
  writableSpotMarketIndexes: [outMarketIndex, inMarketIndex],
3482
3580
  });
3483
3581
 
@@ -3491,7 +3589,7 @@ export class DriftClient {
3491
3589
  {
3492
3590
  accounts: {
3493
3591
  state: await this.getStatePublicKey(),
3494
- user: userAccountPublicKey,
3592
+ user: userAccountPublicKeyToUse,
3495
3593
  userStats: this.getUserStatsAccountPublicKey(),
3496
3594
  authority: this.authority,
3497
3595
  outSpotMarketVault: outSpotMarket.vault,
@@ -3514,7 +3612,7 @@ export class DriftClient {
3514
3612
  {
3515
3613
  accounts: {
3516
3614
  state: await this.getStatePublicKey(),
3517
- user: userAccountPublicKey,
3615
+ user: userAccountPublicKeyToUse,
3518
3616
  userStats: this.getUserStatsAccountPublicKey(),
3519
3617
  authority: this.authority,
3520
3618
  outSpotMarketVault: outSpotMarket.vault,
@@ -3532,6 +3630,66 @@ export class DriftClient {
3532
3630
  return { beginSwapIx, endSwapIx };
3533
3631
  }
3534
3632
 
3633
+ public async stakeForMSOL({ amount }: { amount: BN }): Promise<TxSigAndSlot> {
3634
+ const ixs = await this.getStakeForMSOLIx({ amount });
3635
+ const tx = await this.buildTransaction(ixs);
3636
+ return this.sendTransaction(tx);
3637
+ }
3638
+
3639
+ public async getStakeForMSOLIx({
3640
+ amount,
3641
+ userAccountPublicKey,
3642
+ }: {
3643
+ amount: BN;
3644
+ userAccountPublicKey?: PublicKey;
3645
+ }): Promise<TransactionInstruction[]> {
3646
+ const wSOLMint = this.getSpotMarketAccount(1).mint;
3647
+ const mSOLAccount = await this.getAssociatedTokenAccount(2);
3648
+ const wSOLAccount = await this.getAssociatedTokenAccount(1, false);
3649
+
3650
+ const wSOLAccountExists = await this.checkIfAccountExists(wSOLAccount);
3651
+
3652
+ const closeWSOLIx = createCloseAccountInstruction(
3653
+ wSOLAccount,
3654
+ this.wallet.publicKey,
3655
+ this.wallet.publicKey
3656
+ );
3657
+
3658
+ const createWSOLIx =
3659
+ await this.createAssociatedTokenAccountIdempotentInstruction(
3660
+ wSOLAccount,
3661
+ this.wallet.publicKey,
3662
+ this.wallet.publicKey,
3663
+ wSOLMint
3664
+ );
3665
+
3666
+ const { beginSwapIx, endSwapIx } = await this.getSwapIx({
3667
+ inMarketIndex: 1,
3668
+ outMarketIndex: 2,
3669
+ amountIn: amount,
3670
+ inTokenAccount: wSOLAccount,
3671
+ outTokenAccount: mSOLAccount,
3672
+ userAccountPublicKey,
3673
+ });
3674
+
3675
+ const program = getMarinadeFinanceProgram(this.provider);
3676
+ const depositIx = await getMarinadeDepositIx({
3677
+ program,
3678
+ mSOLAccount: mSOLAccount,
3679
+ transferFrom: this.wallet.publicKey,
3680
+ amount,
3681
+ });
3682
+
3683
+ const ixs = [];
3684
+
3685
+ if (!wSOLAccountExists) {
3686
+ ixs.push(createWSOLIx);
3687
+ }
3688
+ ixs.push(beginSwapIx, closeWSOLIx, depositIx, createWSOLIx, endSwapIx);
3689
+
3690
+ return ixs;
3691
+ }
3692
+
3535
3693
  public async triggerOrder(
3536
3694
  userAccountPublicKey: PublicKey,
3537
3695
  user: UserAccount,
@@ -3724,7 +3882,7 @@ export class DriftClient {
3724
3882
  makerInfo?: MakerInfo | MakerInfo[],
3725
3883
  referrerInfo?: ReferrerInfo
3726
3884
  ): Promise<TransactionInstruction> {
3727
- orderParams = this.getOrderParams(orderParams, MarketType.PERP);
3885
+ orderParams = getOrderParams(orderParams, { marketType: MarketType.PERP });
3728
3886
  const userStatsPublicKey = await this.getUserStatsAccountPublicKey();
3729
3887
  const userAccountPublicKey = await this.getUserAccountPublicKey();
3730
3888
 
@@ -3820,7 +3978,7 @@ export class DriftClient {
3820
3978
  takerInfo: TakerInfo,
3821
3979
  referrerInfo?: ReferrerInfo
3822
3980
  ): Promise<TransactionInstruction> {
3823
- orderParams = this.getOrderParams(orderParams, MarketType.PERP);
3981
+ orderParams = getOrderParams(orderParams, { marketType: MarketType.PERP });
3824
3982
  const userStatsPublicKey = this.getUserStatsAccountPublicKey();
3825
3983
  const userAccountPublicKey = await this.getUserAccountPublicKey();
3826
3984
 
@@ -3892,7 +4050,7 @@ export class DriftClient {
3892
4050
  makerInfo?: MakerInfo,
3893
4051
  referrerInfo?: ReferrerInfo
3894
4052
  ): Promise<TransactionInstruction> {
3895
- orderParams = this.getOrderParams(orderParams, MarketType.SPOT);
4053
+ orderParams = getOrderParams(orderParams, { marketType: MarketType.SPOT });
3896
4054
  const userStatsPublicKey = await this.getUserStatsAccountPublicKey();
3897
4055
  const userAccountPublicKey = await this.getUserAccountPublicKey();
3898
4056
 
@@ -3990,7 +4148,7 @@ export class DriftClient {
3990
4148
  fulfillmentConfig?: SerumV3FulfillmentConfigAccount,
3991
4149
  referrerInfo?: ReferrerInfo
3992
4150
  ): Promise<TransactionInstruction> {
3993
- orderParams = this.getOrderParams(orderParams, MarketType.SPOT);
4151
+ orderParams = getOrderParams(orderParams, { marketType: MarketType.SPOT });
3994
4152
  const userStatsPublicKey = this.getUserStatsAccountPublicKey();
3995
4153
  const userAccountPublicKey = await this.getUserAccountPublicKey();
3996
4154