@drift-labs/sdk 2.145.0-beta.3 → 2.146.0-alpha.13

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 (99) hide show
  1. package/.env +4 -0
  2. package/VERSION +1 -1
  3. package/lib/browser/accounts/grpcMultiUserAccountSubscriber.js +8 -1
  4. package/lib/browser/accounts/webSocketProgramAccountSubscriberV2.d.ts +99 -7
  5. package/lib/browser/accounts/webSocketProgramAccountSubscriberV2.js +435 -144
  6. package/lib/browser/adminClient.d.ts +5 -1
  7. package/lib/browser/adminClient.js +57 -23
  8. package/lib/browser/constants/numericConstants.d.ts +2 -0
  9. package/lib/browser/constants/numericConstants.js +5 -1
  10. package/lib/browser/constants/perpMarkets.js +0 -2
  11. package/lib/browser/decode/user.js +4 -0
  12. package/lib/browser/driftClient.d.ts +25 -10
  13. package/lib/browser/driftClient.js +238 -41
  14. package/lib/browser/driftClientConfig.d.ts +7 -2
  15. package/lib/browser/idl/drift.json +245 -22
  16. package/lib/browser/index.d.ts +4 -0
  17. package/lib/browser/index.js +9 -1
  18. package/lib/browser/marginCalculation.d.ts +86 -0
  19. package/lib/browser/marginCalculation.js +209 -0
  20. package/lib/browser/math/margin.d.ts +1 -1
  21. package/lib/browser/math/margin.js +8 -1
  22. package/lib/browser/math/position.d.ts +1 -0
  23. package/lib/browser/math/position.js +10 -2
  24. package/lib/browser/math/spotPosition.d.ts +1 -1
  25. package/lib/browser/math/spotPosition.js +3 -2
  26. package/lib/browser/math/superStake.d.ts +3 -2
  27. package/lib/browser/types.d.ts +13 -0
  28. package/lib/browser/types.js +12 -1
  29. package/lib/browser/user.d.ts +59 -11
  30. package/lib/browser/user.js +348 -43
  31. package/lib/node/accounts/grpcMultiUserAccountSubscriber.d.ts.map +1 -1
  32. package/lib/node/accounts/grpcMultiUserAccountSubscriber.js +8 -1
  33. package/lib/node/accounts/webSocketProgramAccountSubscriberV2.d.ts +99 -7
  34. package/lib/node/accounts/webSocketProgramAccountSubscriberV2.d.ts.map +1 -1
  35. package/lib/node/accounts/webSocketProgramAccountSubscriberV2.js +435 -144
  36. package/lib/node/adminClient.d.ts +5 -1
  37. package/lib/node/adminClient.d.ts.map +1 -1
  38. package/lib/node/adminClient.js +57 -23
  39. package/lib/node/constants/numericConstants.d.ts +2 -0
  40. package/lib/node/constants/numericConstants.d.ts.map +1 -1
  41. package/lib/node/constants/numericConstants.js +5 -1
  42. package/lib/node/constants/perpMarkets.d.ts.map +1 -1
  43. package/lib/node/constants/perpMarkets.js +0 -2
  44. package/lib/node/decode/user.d.ts.map +1 -1
  45. package/lib/node/decode/user.js +4 -0
  46. package/lib/node/driftClient.d.ts +25 -10
  47. package/lib/node/driftClient.d.ts.map +1 -1
  48. package/lib/node/driftClient.js +238 -41
  49. package/lib/node/driftClientConfig.d.ts +7 -2
  50. package/lib/node/driftClientConfig.d.ts.map +1 -1
  51. package/lib/node/idl/drift.json +245 -22
  52. package/lib/node/index.d.ts +4 -0
  53. package/lib/node/index.d.ts.map +1 -1
  54. package/lib/node/index.js +9 -1
  55. package/lib/node/marginCalculation.d.ts +87 -0
  56. package/lib/node/marginCalculation.d.ts.map +1 -0
  57. package/lib/node/marginCalculation.js +209 -0
  58. package/lib/node/math/margin.d.ts +1 -1
  59. package/lib/node/math/margin.d.ts.map +1 -1
  60. package/lib/node/math/margin.js +8 -1
  61. package/lib/node/math/position.d.ts +1 -0
  62. package/lib/node/math/position.d.ts.map +1 -1
  63. package/lib/node/math/position.js +10 -2
  64. package/lib/node/math/spotPosition.d.ts +1 -1
  65. package/lib/node/math/spotPosition.d.ts.map +1 -1
  66. package/lib/node/math/spotPosition.js +3 -2
  67. package/lib/node/math/superStake.d.ts +3 -2
  68. package/lib/node/math/superStake.d.ts.map +1 -1
  69. package/lib/node/types.d.ts +13 -0
  70. package/lib/node/types.d.ts.map +1 -1
  71. package/lib/node/types.js +12 -1
  72. package/lib/node/user.d.ts +59 -11
  73. package/lib/node/user.d.ts.map +1 -1
  74. package/lib/node/user.js +348 -43
  75. package/package.json +1 -1
  76. package/scripts/deposit-isolated-positions.ts +110 -0
  77. package/scripts/single-grpc-client-test.ts +71 -21
  78. package/scripts/withdraw-isolated-positions.ts +174 -0
  79. package/src/accounts/grpcMultiUserAccountSubscriber.ts +8 -1
  80. package/src/accounts/webSocketProgramAccountSubscriberV2.ts +566 -167
  81. package/src/adminClient.ts +74 -25
  82. package/src/constants/numericConstants.ts +5 -0
  83. package/src/constants/perpMarkets.ts +0 -3
  84. package/src/decode/user.ts +7 -1
  85. package/src/driftClient.ts +465 -52
  86. package/src/driftClientConfig.ts +15 -8
  87. package/src/idl/drift.json +246 -23
  88. package/src/index.ts +4 -0
  89. package/src/margin/README.md +143 -0
  90. package/src/marginCalculation.ts +306 -0
  91. package/src/math/margin.ts +13 -1
  92. package/src/math/position.ts +12 -2
  93. package/src/math/spotPosition.ts +6 -2
  94. package/src/types.ts +16 -0
  95. package/src/user.ts +623 -81
  96. package/tests/amm/test.ts +1 -1
  97. package/tests/dlob/helpers.ts +6 -3
  98. package/tests/user/getMarginCalculation.ts +405 -0
  99. package/tests/user/test.ts +0 -7
@@ -57,7 +57,6 @@ import {
57
57
  StateAccount,
58
58
  SwapReduceOnly,
59
59
  SignedMsgOrderParamsMessage,
60
- TakerInfo,
61
60
  TxParams,
62
61
  UserAccount,
63
62
  UserStatsAccount,
@@ -147,7 +146,11 @@ import {
147
146
  QUOTE_SPOT_MARKET_INDEX,
148
147
  ZERO,
149
148
  } from './constants/numericConstants';
150
- import { findDirectionToClose, positionIsAvailable } from './math/position';
149
+ import {
150
+ calculateClaimablePnl,
151
+ findDirectionToClose,
152
+ positionIsAvailable,
153
+ } from './math/position';
151
154
  import { getSignedTokenAmount, getTokenAmount } from './math/spotBalance';
152
155
  import { decodeName, DEFAULT_USER_NAME, encodeName } from './userName';
153
156
  import { MMOraclePriceData, OraclePriceData } from './oracles/types';
@@ -210,6 +213,8 @@ import nacl from 'tweetnacl';
210
213
  import { Slothash } from './slot/SlothashSubscriber';
211
214
  import { getOracleId } from './oracles/oracleId';
212
215
  import { SignedMsgOrderParams } from './types';
216
+ import { TakerInfo } from './types';
217
+ // BN is already imported globally in this file via other imports
213
218
  import { sha256 } from '@noble/hashes/sha256';
214
219
  import { getOracleConfidenceFromMMOracleData } from './oracles/utils';
215
220
  import { ConstituentMap } from './constituentMap/constituentMap';
@@ -219,6 +224,7 @@ import {
219
224
  isBuilderOrderReferral,
220
225
  isBuilderOrderCompleted,
221
226
  } from './math/builder';
227
+ import { BigNum } from './factory/bigNum';
222
228
  import { TitanClient, SwapMode as TitanSwapMode } from './titan/titanClient';
223
229
  import { UnifiedSwapClient } from './swap/UnifiedSwapClient';
224
230
 
@@ -295,6 +301,46 @@ export class DriftClient {
295
301
  return this._isSubscribed && this.accountSubscriber.isSubscribed;
296
302
  }
297
303
 
304
+ private async getPrePlaceOrderIxs(
305
+ orderParams: OptionalOrderParams,
306
+ userAccount: UserAccount,
307
+ options?: { positionMaxLev?: number; isolatedPositionDepositAmount?: BN }
308
+ ): Promise<TransactionInstruction[]> {
309
+ const preIxs: TransactionInstruction[] = [];
310
+
311
+ if (isVariant(orderParams.marketType, 'perp')) {
312
+ const { positionMaxLev, isolatedPositionDepositAmount } = options ?? {};
313
+
314
+ if (
315
+ isolatedPositionDepositAmount?.gt?.(ZERO) &&
316
+ this.isOrderIncreasingPosition(orderParams, userAccount)
317
+ ) {
318
+ preIxs.push(
319
+ await this.getTransferIsolatedPerpPositionDepositIx(
320
+ isolatedPositionDepositAmount as BN,
321
+ orderParams.marketIndex,
322
+ userAccount.subAccountId
323
+ )
324
+ );
325
+ }
326
+
327
+ if (positionMaxLev) {
328
+ const marginRatio = Math.floor(
329
+ (1 / positionMaxLev) * MARGIN_PRECISION.toNumber()
330
+ );
331
+ preIxs.push(
332
+ await this.getUpdateUserPerpPositionCustomMarginRatioIx(
333
+ orderParams.marketIndex,
334
+ marginRatio,
335
+ userAccount.subAccountId
336
+ )
337
+ );
338
+ }
339
+ }
340
+
341
+ return preIxs;
342
+ }
343
+
298
344
  public set isSubscribed(val: boolean) {
299
345
  this._isSubscribed = val;
300
346
  }
@@ -406,6 +452,8 @@ export class DriftClient {
406
452
  resubTimeoutMs: config.accountSubscription?.resubTimeoutMs,
407
453
  logResubMessages: config.accountSubscription?.logResubMessages,
408
454
  commitment: config.accountSubscription?.commitment,
455
+ programUserAccountSubscriber:
456
+ config.accountSubscription?.programUserAccountSubscriber,
409
457
  };
410
458
  this.userStatsAccountSubscriptionConfig = {
411
459
  type: 'websocket',
@@ -474,7 +522,10 @@ export class DriftClient {
474
522
  }
475
523
  );
476
524
  } else {
477
- this.accountSubscriber = new WebSocketDriftClientAccountSubscriber(
525
+ const accountSubscriberClass =
526
+ config.accountSubscription?.driftClientAccountSubscriber ??
527
+ WebSocketDriftClientAccountSubscriber;
528
+ this.accountSubscriber = new accountSubscriberClass(
478
529
  this.program,
479
530
  config.perpMarketIndexes ?? [],
480
531
  config.spotMarketIndexes ?? [],
@@ -485,9 +536,7 @@ export class DriftClient {
485
536
  resubTimeoutMs: config.accountSubscription?.resubTimeoutMs,
486
537
  logResubMessages: config.accountSubscription?.logResubMessages,
487
538
  },
488
- config.accountSubscription?.commitment,
489
- config.accountSubscription?.perpMarketAccountSubscriber,
490
- config.accountSubscription?.oracleAccountSubscriber
539
+ config.accountSubscription?.commitment
491
540
  );
492
541
  }
493
542
  this.eventEmitter = this.accountSubscriber.eventEmitter;
@@ -765,7 +814,6 @@ export class DriftClient {
765
814
 
766
815
  return lookupTableAccount;
767
816
  }
768
-
769
817
  public async fetchAllLookupTableAccounts(): Promise<
770
818
  AddressLookupTableAccount[]
771
819
  > {
@@ -1743,7 +1791,6 @@ export class DriftClient {
1743
1791
  const { txSig } = await this.sendTransaction(tx, [], this.opts);
1744
1792
  return txSig;
1745
1793
  }
1746
-
1747
1794
  public async getUpdateUserCustomMarginRatioIx(
1748
1795
  marginRatio: number,
1749
1796
  subAccountId = 0
@@ -2457,6 +2504,15 @@ export class DriftClient {
2457
2504
  return this.getTokenAmount(QUOTE_SPOT_MARKET_INDEX);
2458
2505
  }
2459
2506
 
2507
+ public getIsolatedPerpPositionTokenAmount(
2508
+ perpMarketIndex: number,
2509
+ subAccountId?: number
2510
+ ): BN {
2511
+ return this.getUser(subAccountId).getIsolatePerpPositionTokenAmount(
2512
+ perpMarketIndex
2513
+ );
2514
+ }
2515
+
2460
2516
  /**
2461
2517
  * Returns the token amount for a given market. The spot market precision is based on the token mint decimals.
2462
2518
  * Positive if it is a deposit, negative if it is a borrow.
@@ -2534,7 +2590,6 @@ export class DriftClient {
2534
2590
  this.mustIncludeSpotMarketIndexes.add(spotMarketIndex);
2535
2591
  });
2536
2592
  }
2537
-
2538
2593
  getRemainingAccounts(params: RemainingAccountParams): AccountMeta[] {
2539
2594
  const { oracleAccountMap, spotMarketAccountMap, perpMarketAccountMap } =
2540
2595
  this.getRemainingAccountMapsForUsers(params.userAccounts);
@@ -3399,7 +3454,6 @@ export class DriftClient {
3399
3454
  userAccountPublicKey,
3400
3455
  };
3401
3456
  }
3402
-
3403
3457
  public async createInitializeUserAccountAndDepositCollateral(
3404
3458
  amount: BN,
3405
3459
  userTokenAccount: PublicKey,
@@ -4056,6 +4110,263 @@ export class DriftClient {
4056
4110
  );
4057
4111
  }
4058
4112
 
4113
+ async depositIntoIsolatedPerpPosition(
4114
+ amount: BN,
4115
+ perpMarketIndex: number,
4116
+ userTokenAccount: PublicKey,
4117
+ subAccountId?: number,
4118
+ txParams?: TxParams
4119
+ ): Promise<TransactionSignature> {
4120
+ const { txSig } = await this.sendTransaction(
4121
+ await this.buildTransaction(
4122
+ await this.getDepositIntoIsolatedPerpPositionIx(
4123
+ amount,
4124
+ perpMarketIndex,
4125
+ userTokenAccount,
4126
+ subAccountId
4127
+ ),
4128
+ txParams
4129
+ ),
4130
+ [],
4131
+ this.opts
4132
+ );
4133
+ return txSig;
4134
+ }
4135
+
4136
+ async getDepositIntoIsolatedPerpPositionIx(
4137
+ amount: BN,
4138
+ perpMarketIndex: number,
4139
+ userTokenAccount: PublicKey,
4140
+ subAccountId?: number
4141
+ ): Promise<TransactionInstruction> {
4142
+ const userAccountPublicKey = await getUserAccountPublicKey(
4143
+ this.program.programId,
4144
+ this.authority,
4145
+ subAccountId ?? this.activeSubAccountId
4146
+ );
4147
+
4148
+ const perpMarketAccount = this.getPerpMarketAccount(perpMarketIndex);
4149
+ const spotMarketIndex = perpMarketAccount.quoteSpotMarketIndex;
4150
+ const spotMarketAccount = this.getSpotMarketAccount(spotMarketIndex);
4151
+
4152
+ const remainingAccounts = this.getRemainingAccounts({
4153
+ userAccounts: [],
4154
+ writableSpotMarketIndexes: [spotMarketIndex],
4155
+ readablePerpMarketIndex: [perpMarketIndex],
4156
+ });
4157
+
4158
+ const tokenProgram = this.getTokenProgramForSpotMarket(spotMarketAccount);
4159
+ return await this.program.instruction.depositIntoIsolatedPerpPosition(
4160
+ spotMarketIndex,
4161
+ perpMarketIndex,
4162
+ amount,
4163
+ {
4164
+ accounts: {
4165
+ state: await this.getStatePublicKey(),
4166
+ spotMarketVault: spotMarketAccount.vault,
4167
+ user: userAccountPublicKey,
4168
+ userStats: this.getUserStatsAccountPublicKey(),
4169
+ userTokenAccount: userTokenAccount,
4170
+ authority: this.wallet.publicKey,
4171
+ tokenProgram,
4172
+ },
4173
+ remainingAccounts,
4174
+ }
4175
+ );
4176
+ }
4177
+
4178
+ public async transferIsolatedPerpPositionDeposit(
4179
+ amount: BN,
4180
+ perpMarketIndex: number,
4181
+ subAccountId?: number,
4182
+ txParams?: TxParams
4183
+ ): Promise<TransactionSignature> {
4184
+ const tx = await this.buildTransaction(
4185
+ await this.getTransferIsolatedPerpPositionDepositIx(
4186
+ amount,
4187
+ perpMarketIndex,
4188
+ subAccountId
4189
+ ),
4190
+ txParams
4191
+ );
4192
+ const { txSig } = await this.sendTransaction(tx, [], {
4193
+ ...this.opts,
4194
+ skipPreflight: true,
4195
+ });
4196
+ return txSig;
4197
+ }
4198
+
4199
+ public async getTransferIsolatedPerpPositionDepositIx(
4200
+ amount: BN,
4201
+ perpMarketIndex: number,
4202
+ subAccountId?: number,
4203
+ noAmountBuffer?: boolean
4204
+ ): Promise<TransactionInstruction> {
4205
+ const userAccountPublicKey = await getUserAccountPublicKey(
4206
+ this.program.programId,
4207
+ this.authority,
4208
+ subAccountId ?? this.activeSubAccountId
4209
+ );
4210
+
4211
+ const perpMarketAccount = this.getPerpMarketAccount(perpMarketIndex);
4212
+ const spotMarketIndex = perpMarketAccount.quoteSpotMarketIndex;
4213
+ const spotMarketAccount = this.getSpotMarketAccount(spotMarketIndex);
4214
+ const user = await this.getUserAccount(subAccountId);
4215
+ const remainingAccounts = this.getRemainingAccounts({
4216
+ userAccounts: [user],
4217
+ writableSpotMarketIndexes: [spotMarketIndex],
4218
+ readablePerpMarketIndex: [perpMarketIndex],
4219
+ });
4220
+
4221
+ const amountWithBuffer =
4222
+ noAmountBuffer || amount.eq(BigNum.fromPrint('-9223372036854775808').val)
4223
+ ? amount
4224
+ : amount.add(amount.div(new BN(1000))); // .1% buffer
4225
+
4226
+ return await this.program.instruction.transferIsolatedPerpPositionDeposit(
4227
+ spotMarketIndex,
4228
+ perpMarketIndex,
4229
+ amountWithBuffer,
4230
+ {
4231
+ accounts: {
4232
+ state: await this.getStatePublicKey(),
4233
+ spotMarketVault: spotMarketAccount.vault,
4234
+ user: userAccountPublicKey,
4235
+ userStats: this.getUserStatsAccountPublicKey(),
4236
+ authority: this.wallet.publicKey,
4237
+ },
4238
+ remainingAccounts,
4239
+ }
4240
+ );
4241
+ }
4242
+
4243
+ public async withdrawFromIsolatedPerpPosition(
4244
+ amount: BN,
4245
+ perpMarketIndex: number,
4246
+ userTokenAccount: PublicKey,
4247
+ subAccountId?: number,
4248
+ txParams?: TxParams
4249
+ ): Promise<TransactionSignature> {
4250
+ const instructions =
4251
+ await this.getWithdrawFromIsolatedPerpPositionIxsBundle(
4252
+ amount,
4253
+ perpMarketIndex,
4254
+ subAccountId,
4255
+ userTokenAccount
4256
+ );
4257
+ const { txSig } = await this.sendTransaction(
4258
+ await this.buildTransaction(instructions, txParams)
4259
+ );
4260
+ return txSig;
4261
+ }
4262
+
4263
+ public async getWithdrawFromIsolatedPerpPositionIxsBundle(
4264
+ amount: BN,
4265
+ perpMarketIndex: number,
4266
+ subAccountId?: number,
4267
+ userTokenAccount?: PublicKey
4268
+ ): Promise<TransactionInstruction[]> {
4269
+ const userAccountPublicKey = await getUserAccountPublicKey(
4270
+ this.program.programId,
4271
+ this.authority,
4272
+ subAccountId ?? this.activeSubAccountId
4273
+ );
4274
+ const userAccount = this.getUserAccount(subAccountId);
4275
+
4276
+ const tokenAmountDeposited =
4277
+ this.getIsolatedPerpPositionTokenAmount(perpMarketIndex);
4278
+ const isolatedPositionUnrealizedPnl = calculateClaimablePnl(
4279
+ this.getPerpMarketAccount(perpMarketIndex),
4280
+ this.getSpotMarketAccount(
4281
+ this.getPerpMarketAccount(perpMarketIndex).quoteSpotMarketIndex
4282
+ ),
4283
+ userAccount.perpPositions.find((p) => p.marketIndex === perpMarketIndex),
4284
+ this.getOracleDataForSpotMarket(
4285
+ this.getPerpMarketAccount(perpMarketIndex).quoteSpotMarketIndex
4286
+ )
4287
+ );
4288
+
4289
+ const depositAmountPlusUnrealizedPnl = tokenAmountDeposited.add(
4290
+ isolatedPositionUnrealizedPnl
4291
+ );
4292
+
4293
+ const amountToWithdraw = amount.gt(depositAmountPlusUnrealizedPnl)
4294
+ ? BigNum.fromPrint('-9223372036854775808').val // min i64
4295
+ : amount;
4296
+ console.log('amountToWithdraw', amountToWithdraw.toString());
4297
+ console.log('amount', amount.toString());
4298
+
4299
+ let associatedTokenAccount = userTokenAccount;
4300
+ if (!associatedTokenAccount) {
4301
+ const perpMarketAccount = this.getPerpMarketAccount(perpMarketIndex);
4302
+ const quoteSpotMarketIndex = perpMarketAccount.quoteSpotMarketIndex;
4303
+ associatedTokenAccount = await this.getAssociatedTokenAccount(
4304
+ quoteSpotMarketIndex
4305
+ );
4306
+ }
4307
+
4308
+ const withdrawIx = await this.getWithdrawFromIsolatedPerpPositionIx(
4309
+ amountToWithdraw,
4310
+ perpMarketIndex,
4311
+ associatedTokenAccount,
4312
+ subAccountId
4313
+ );
4314
+ const ixs = [withdrawIx];
4315
+
4316
+ const needsToSettle =
4317
+ amount.gt(tokenAmountDeposited) && isolatedPositionUnrealizedPnl.gt(ZERO);
4318
+ if (needsToSettle) {
4319
+ const settleIx = await this.settleMultiplePNLsIx(
4320
+ userAccountPublicKey,
4321
+ userAccount,
4322
+ [perpMarketIndex],
4323
+ SettlePnlMode.TRY_SETTLE
4324
+ );
4325
+ ixs.push(settleIx);
4326
+ }
4327
+ return ixs;
4328
+ }
4329
+
4330
+ public async getWithdrawFromIsolatedPerpPositionIx(
4331
+ amount: BN,
4332
+ perpMarketIndex: number,
4333
+ userTokenAccount: PublicKey,
4334
+ subAccountId?: number
4335
+ ): Promise<TransactionInstruction> {
4336
+ const userAccountPublicKey = await getUserAccountPublicKey(
4337
+ this.program.programId,
4338
+ this.authority,
4339
+ subAccountId ?? this.activeSubAccountId
4340
+ );
4341
+ const perpMarketAccount = this.getPerpMarketAccount(perpMarketIndex);
4342
+ const spotMarketIndex = perpMarketAccount.quoteSpotMarketIndex;
4343
+ const spotMarketAccount = this.getSpotMarketAccount(spotMarketIndex);
4344
+ const remainingAccounts = this.getRemainingAccounts({
4345
+ userAccounts: [this.getUserAccount(subAccountId)],
4346
+ writableSpotMarketIndexes: [spotMarketIndex],
4347
+ readablePerpMarketIndex: [perpMarketIndex],
4348
+ });
4349
+
4350
+ return await this.program.instruction.withdrawFromIsolatedPerpPosition(
4351
+ spotMarketIndex,
4352
+ perpMarketIndex,
4353
+ amount,
4354
+ {
4355
+ accounts: {
4356
+ state: await this.getStatePublicKey(),
4357
+ spotMarketVault: spotMarketAccount.vault,
4358
+ user: userAccountPublicKey,
4359
+ userStats: this.getUserStatsAccountPublicKey(),
4360
+ authority: this.wallet.publicKey,
4361
+ userTokenAccount: userTokenAccount,
4362
+ tokenProgram: this.getTokenProgramForSpotMarket(spotMarketAccount),
4363
+ driftSigner: this.getSignerPublicKey(),
4364
+ },
4365
+ remainingAccounts,
4366
+ }
4367
+ );
4368
+ }
4369
+
4059
4370
  public async updateSpotMarketCumulativeInterest(
4060
4371
  marketIndex: number,
4061
4372
  txParams?: TxParams
@@ -4199,7 +4510,6 @@ export class DriftClient {
4199
4510
  }
4200
4511
  );
4201
4512
  }
4202
-
4203
4513
  public async getRemovePerpLpSharesIx(
4204
4514
  marketIndex: number,
4205
4515
  sharesToBurn?: BN,
@@ -4356,7 +4666,8 @@ export class DriftClient {
4356
4666
  referrerInfo?: ReferrerInfo,
4357
4667
  cancelExistingOrders?: boolean,
4358
4668
  settlePnl?: boolean,
4359
- positionMaxLev?: number
4669
+ positionMaxLev?: number,
4670
+ isolatedPositionDepositAmount?: BN
4360
4671
  ): Promise<{
4361
4672
  cancelExistingOrdersTx?: Transaction | VersionedTransaction;
4362
4673
  settlePnlTx?: Transaction | VersionedTransaction;
@@ -4384,18 +4695,25 @@ export class DriftClient {
4384
4695
 
4385
4696
  const txKeys = Object.keys(ixPromisesForTxs);
4386
4697
 
4387
- const marketOrderTxIxs = positionMaxLev
4388
- ? this.getPlaceOrdersAndSetPositionMaxLevIx(
4389
- [orderParams, ...bracketOrdersParams],
4390
- positionMaxLev,
4391
- userAccount.subAccountId
4392
- )
4393
- : this.getPlaceOrdersIx(
4394
- [orderParams, ...bracketOrdersParams],
4395
- userAccount.subAccountId
4396
- );
4698
+ const preIxs: TransactionInstruction[] = await this.getPrePlaceOrderIxs(
4699
+ orderParams,
4700
+ userAccount,
4701
+ {
4702
+ positionMaxLev,
4703
+ isolatedPositionDepositAmount,
4704
+ }
4705
+ );
4397
4706
 
4398
- ixPromisesForTxs.marketOrderTx = marketOrderTxIxs;
4707
+ ixPromisesForTxs.marketOrderTx = (async () => {
4708
+ const placeOrdersIx = await this.getPlaceOrdersIx(
4709
+ [orderParams, ...bracketOrdersParams],
4710
+ userAccount.subAccountId
4711
+ );
4712
+ if (preIxs.length) {
4713
+ return [...preIxs, placeOrdersIx] as unknown as TransactionInstruction;
4714
+ }
4715
+ return placeOrdersIx;
4716
+ })();
4399
4717
 
4400
4718
  /* Cancel open orders in market if requested */
4401
4719
  if (cancelExistingOrders && isVariant(orderParams.marketType, 'perp')) {
@@ -4513,12 +4831,29 @@ export class DriftClient {
4513
4831
  public async placePerpOrder(
4514
4832
  orderParams: OptionalOrderParams,
4515
4833
  txParams?: TxParams,
4516
- subAccountId?: number
4834
+ subAccountId?: number,
4835
+ isolatedPositionDepositAmount?: BN
4517
4836
  ): Promise<TransactionSignature> {
4837
+ const preIxs: TransactionInstruction[] = [];
4838
+ if (isolatedPositionDepositAmount?.gt?.(ZERO)) {
4839
+ preIxs.push(
4840
+ await this.getTransferIsolatedPerpPositionDepositIx(
4841
+ isolatedPositionDepositAmount as BN,
4842
+ orderParams.marketIndex,
4843
+ subAccountId
4844
+ )
4845
+ );
4846
+ }
4847
+
4518
4848
  const { txSig, slot } = await this.sendTransaction(
4519
4849
  await this.buildTransaction(
4520
4850
  await this.getPlacePerpOrderIx(orderParams, subAccountId),
4521
- txParams
4851
+ txParams,
4852
+ undefined,
4853
+ undefined,
4854
+ undefined,
4855
+ undefined,
4856
+ preIxs
4522
4857
  ),
4523
4858
  [],
4524
4859
  this.opts
@@ -4706,13 +5041,31 @@ export class DriftClient {
4706
5041
  public async cancelOrder(
4707
5042
  orderId?: number,
4708
5043
  txParams?: TxParams,
4709
- subAccountId?: number
5044
+ subAccountId?: number,
5045
+ overrides?: { withdrawIsolatedDepositAmount?: BN }
4710
5046
  ): Promise<TransactionSignature> {
5047
+ const cancelIx = await this.getCancelOrderIx(orderId, subAccountId);
5048
+
5049
+ const instructions: TransactionInstruction[] = [cancelIx];
5050
+
5051
+ if (overrides?.withdrawIsolatedDepositAmount !== undefined) {
5052
+ const order = this.getOrder(orderId, subAccountId);
5053
+ const perpMarketIndex = order?.marketIndex;
5054
+ const withdrawAmount = overrides.withdrawIsolatedDepositAmount;
5055
+
5056
+ if (withdrawAmount.gt(ZERO)) {
5057
+ const withdrawIxs =
5058
+ await this.getWithdrawFromIsolatedPerpPositionIxsBundle(
5059
+ withdrawAmount,
5060
+ perpMarketIndex,
5061
+ subAccountId
5062
+ );
5063
+ instructions.push(...withdrawIxs);
5064
+ }
5065
+ }
5066
+
4711
5067
  const { txSig } = await this.sendTransaction(
4712
- await this.buildTransaction(
4713
- await this.getCancelOrderIx(orderId, subAccountId),
4714
- txParams
4715
- ),
5068
+ await this.buildTransaction(instructions, txParams),
4716
5069
  [],
4717
5070
  this.opts
4718
5071
  );
@@ -4946,7 +5299,8 @@ export class DriftClient {
4946
5299
  params: OrderParams[],
4947
5300
  txParams?: TxParams,
4948
5301
  subAccountId?: number,
4949
- optionalIxs?: TransactionInstruction[]
5302
+ optionalIxs?: TransactionInstruction[],
5303
+ isolatedPositionDepositAmount?: BN
4950
5304
  ): Promise<TransactionSignature> {
4951
5305
  const { txSig } = await this.sendTransaction(
4952
5306
  (
@@ -4954,7 +5308,8 @@ export class DriftClient {
4954
5308
  params,
4955
5309
  txParams,
4956
5310
  subAccountId,
4957
- optionalIxs
5311
+ optionalIxs,
5312
+ isolatedPositionDepositAmount
4958
5313
  )
4959
5314
  ).placeOrdersTx,
4960
5315
  [],
@@ -4968,10 +5323,28 @@ export class DriftClient {
4968
5323
  params: OrderParams[],
4969
5324
  txParams?: TxParams,
4970
5325
  subAccountId?: number,
4971
- optionalIxs?: TransactionInstruction[]
5326
+ optionalIxs?: TransactionInstruction[],
5327
+ isolatedPositionDepositAmount?: BN
4972
5328
  ) {
4973
5329
  const lookupTableAccounts = await this.fetchAllLookupTableAccounts();
4974
5330
 
5331
+ const preIxs: TransactionInstruction[] = [];
5332
+ if (params?.length === 1) {
5333
+ const p = params[0];
5334
+ if (
5335
+ isVariant(p.marketType, 'perp') &&
5336
+ isolatedPositionDepositAmount?.gt?.(ZERO)
5337
+ ) {
5338
+ preIxs.push(
5339
+ await this.getTransferIsolatedPerpPositionDepositIx(
5340
+ isolatedPositionDepositAmount as BN,
5341
+ p.marketIndex,
5342
+ subAccountId
5343
+ )
5344
+ );
5345
+ }
5346
+ }
5347
+
4975
5348
  const tx = await this.buildTransaction(
4976
5349
  await this.getPlaceOrdersIx(params, subAccountId),
4977
5350
  txParams,
@@ -4979,14 +5352,13 @@ export class DriftClient {
4979
5352
  lookupTableAccounts,
4980
5353
  undefined,
4981
5354
  undefined,
4982
- optionalIxs
5355
+ [...preIxs, ...(optionalIxs ?? [])]
4983
5356
  );
4984
5357
 
4985
5358
  return {
4986
5359
  placeOrdersTx: tx,
4987
5360
  };
4988
5361
  }
4989
-
4990
5362
  public async getPlaceOrdersIx(
4991
5363
  params: OptionalOrderParams[],
4992
5364
  subAccountId?: number,
@@ -5095,8 +5467,7 @@ export class DriftClient {
5095
5467
  const marginRatio = Math.floor(
5096
5468
  (1 / positionMaxLev) * MARGIN_PRECISION.toNumber()
5097
5469
  );
5098
-
5099
- // TODO: Handle multiple markets?
5470
+ // Keep existing behavior but note: prefer using getPostPlaceOrderIxs path
5100
5471
  const setPositionMaxLevIxs =
5101
5472
  await this.getUpdateUserPerpPositionCustomMarginRatioIx(
5102
5473
  readablePerpMarketIndex[0],
@@ -5753,6 +6124,7 @@ export class DriftClient {
5753
6124
  /**
5754
6125
  * Swap tokens in drift account using titan or jupiter
5755
6126
  * @param swapClient swap client to find routes and instructions (Titan or Jupiter)
6127
+ * @param jupiterClient @deprecated Use swapClient instead. Legacy parameter for backward compatibility
5756
6128
  * @param outMarketIndex the market index of the token you're buying
5757
6129
  * @param inMarketIndex the market index of the token you're selling
5758
6130
  * @param outAssociatedTokenAccount the token account to receive the token being sold on titan or jupiter
@@ -5768,6 +6140,7 @@ export class DriftClient {
5768
6140
  */
5769
6141
  public async swap({
5770
6142
  swapClient,
6143
+ jupiterClient,
5771
6144
  outMarketIndex,
5772
6145
  inMarketIndex,
5773
6146
  outAssociatedTokenAccount,
@@ -5781,7 +6154,9 @@ export class DriftClient {
5781
6154
  quote,
5782
6155
  onlyDirectRoutes = false,
5783
6156
  }: {
5784
- swapClient: UnifiedSwapClient | SwapClient;
6157
+ swapClient?: UnifiedSwapClient | SwapClient;
6158
+ /** @deprecated Use swapClient instead. Legacy parameter for backward compatibility */
6159
+ jupiterClient?: JupiterClient;
5785
6160
  outMarketIndex: number;
5786
6161
  inMarketIndex: number;
5787
6162
  outAssociatedTokenAccount?: PublicKey;
@@ -5797,15 +6172,22 @@ export class DriftClient {
5797
6172
  };
5798
6173
  quote?: QuoteResponse;
5799
6174
  }): Promise<TransactionSignature> {
6175
+ // Handle backward compatibility: use jupiterClient if swapClient is not provided
6176
+ const clientToUse = swapClient || jupiterClient;
6177
+
6178
+ if (!clientToUse) {
6179
+ throw new Error('Either swapClient or jupiterClient must be provided');
6180
+ }
6181
+
5800
6182
  let res: {
5801
6183
  ixs: TransactionInstruction[];
5802
6184
  lookupTables: AddressLookupTableAccount[];
5803
6185
  };
5804
6186
 
5805
6187
  // Use unified SwapClient if available
5806
- if (swapClient instanceof UnifiedSwapClient) {
6188
+ if (clientToUse instanceof UnifiedSwapClient) {
5807
6189
  res = await this.getSwapIxV2({
5808
- swapClient,
6190
+ swapClient: clientToUse,
5809
6191
  outMarketIndex,
5810
6192
  inMarketIndex,
5811
6193
  outAssociatedTokenAccount,
@@ -5818,9 +6200,9 @@ export class DriftClient {
5818
6200
  quote,
5819
6201
  v6,
5820
6202
  });
5821
- } else if (swapClient instanceof TitanClient) {
6203
+ } else if (clientToUse instanceof TitanClient) {
5822
6204
  res = await this.getTitanSwapIx({
5823
- titanClient: swapClient,
6205
+ titanClient: clientToUse,
5824
6206
  outMarketIndex,
5825
6207
  inMarketIndex,
5826
6208
  outAssociatedTokenAccount,
@@ -5831,10 +6213,10 @@ export class DriftClient {
5831
6213
  onlyDirectRoutes,
5832
6214
  reduceOnly,
5833
6215
  });
5834
- } else if (swapClient instanceof JupiterClient) {
6216
+ } else if (clientToUse instanceof JupiterClient) {
5835
6217
  const quoteToUse = quote ?? v6?.quote;
5836
6218
  res = await this.getJupiterSwapIxV6({
5837
- jupiterClient: swapClient,
6219
+ jupiterClient: clientToUse,
5838
6220
  outMarketIndex,
5839
6221
  inMarketIndex,
5840
6222
  outAssociatedTokenAccount,
@@ -6786,7 +7168,6 @@ export class DriftClient {
6786
7168
  this.perpMarketLastSlotCache.set(orderParams.marketIndex, slot);
6787
7169
  return txSig;
6788
7170
  }
6789
-
6790
7171
  public async preparePlaceAndTakePerpOrderWithAdditionalOrders(
6791
7172
  orderParams: OptionalOrderParams,
6792
7173
  makerInfo?: MakerInfo | MakerInfo[],
@@ -6798,7 +7179,8 @@ export class DriftClient {
6798
7179
  settlePnl?: boolean,
6799
7180
  exitEarlyIfSimFails?: boolean,
6800
7181
  auctionDurationPercentage?: number,
6801
- optionalIxs?: TransactionInstruction[]
7182
+ optionalIxs?: TransactionInstruction[],
7183
+ isolatedPositionDepositAmount?: BN
6802
7184
  ): Promise<{
6803
7185
  placeAndTakeTx: Transaction | VersionedTransaction;
6804
7186
  cancelExistingOrdersTx: Transaction | VersionedTransaction;
@@ -6832,6 +7214,19 @@ export class DriftClient {
6832
7214
  subAccountId
6833
7215
  );
6834
7216
 
7217
+ if (
7218
+ isVariant(orderParams.marketType, 'perp') &&
7219
+ isolatedPositionDepositAmount?.gt?.(ZERO)
7220
+ ) {
7221
+ placeAndTakeIxs.push(
7222
+ await this.getTransferIsolatedPerpPositionDepositIx(
7223
+ isolatedPositionDepositAmount as BN,
7224
+ orderParams.marketIndex,
7225
+ subAccountId
7226
+ )
7227
+ );
7228
+ }
7229
+
6835
7230
  placeAndTakeIxs.push(placeAndTakeIx);
6836
7231
 
6837
7232
  if (bracketOrdersParams.length > 0) {
@@ -6842,6 +7237,11 @@ export class DriftClient {
6842
7237
  placeAndTakeIxs.push(bracketOrdersIx);
6843
7238
  }
6844
7239
 
7240
+ // Optional extra ixs can be appended at the front
7241
+ if (optionalIxs?.length) {
7242
+ placeAndTakeIxs.unshift(...optionalIxs);
7243
+ }
7244
+
6845
7245
  const shouldUseSimulationComputeUnits =
6846
7246
  txParams?.useSimulatedComputeUnits;
6847
7247
  const shouldExitIfSimulationFails = exitEarlyIfSimFails;
@@ -7649,7 +8049,6 @@ export class DriftClient {
7649
8049
  this.spotMarketLastSlotCache.set(QUOTE_SPOT_MARKET_INDEX, slot);
7650
8050
  return txSig;
7651
8051
  }
7652
-
7653
8052
  public async getPlaceAndTakeSpotOrderIx(
7654
8053
  orderParams: OptionalOrderParams,
7655
8054
  fulfillmentConfig?: SerumV3FulfillmentConfigAccount,
@@ -8112,7 +8511,6 @@ export class DriftClient {
8112
8511
  bitFlags?: number;
8113
8512
  policy?: ModifyOrderPolicy;
8114
8513
  maxTs?: BN;
8115
- txParams?: TxParams;
8116
8514
  },
8117
8515
  subAccountId?: number
8118
8516
  ): Promise<TransactionInstruction> {
@@ -8638,7 +9036,6 @@ export class DriftClient {
8638
9036
  this.perpMarketLastSlotCache.set(marketIndex, slot);
8639
9037
  return txSig;
8640
9038
  }
8641
-
8642
9039
  public async getLiquidatePerpIx(
8643
9040
  userAccountPublicKey: PublicKey,
8644
9041
  userAccount: UserAccount,
@@ -9429,7 +9826,6 @@ export class DriftClient {
9429
9826
  }
9430
9827
  );
9431
9828
  }
9432
-
9433
9829
  public async resolveSpotBankruptcy(
9434
9830
  userAccountPublicKey: PublicKey,
9435
9831
  userAccount: UserAccount,
@@ -10266,7 +10662,6 @@ export class DriftClient {
10266
10662
  const { txSig } = await this.sendTransaction(tx, [], this.opts);
10267
10663
  return txSig;
10268
10664
  }
10269
-
10270
10665
  public async getSettleRevenueToInsuranceFundIx(
10271
10666
  spotMarketIndex: number
10272
10667
  ): Promise<TransactionInstruction> {
@@ -11061,7 +11456,6 @@ export class DriftClient {
11061
11456
  );
11062
11457
  return config as ProtectedMakerModeConfig;
11063
11458
  }
11064
-
11065
11459
  public async updateUserProtectedMakerOrders(
11066
11460
  subAccountId: number,
11067
11461
  protectedOrders: boolean,
@@ -12455,4 +12849,23 @@ export class DriftClient {
12455
12849
  forceVersionedTransaction,
12456
12850
  });
12457
12851
  }
12852
+
12853
+ isOrderIncreasingPosition(
12854
+ orderParams: OptionalOrderParams,
12855
+ userAccount: UserAccount
12856
+ ): boolean {
12857
+ const perpPosition = userAccount.perpPositions.find(
12858
+ (p) => p.marketIndex === orderParams.marketIndex
12859
+ );
12860
+ if (!perpPosition) return true;
12861
+
12862
+ const currentBase = perpPosition.baseAssetAmount;
12863
+ if (currentBase.eq(ZERO)) return true;
12864
+
12865
+ const orderBaseAmount = isVariant(orderParams.direction, 'long')
12866
+ ? orderParams.baseAssetAmount
12867
+ : orderParams.baseAssetAmount.neg();
12868
+
12869
+ return currentBase.add(orderBaseAmount).abs().gt(currentBase.abs());
12870
+ }
12458
12871
  }