@drift-labs/sdk-browser 2.155.0-beta.3 → 2.155.0-beta.5

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 (57) hide show
  1. package/VERSION +1 -1
  2. package/lib/browser/decode/user.js +8 -5
  3. package/lib/browser/driftClient.d.ts +15 -10
  4. package/lib/browser/driftClient.js +137 -23
  5. package/lib/browser/marginCalculation.d.ts +0 -12
  6. package/lib/browser/marginCalculation.js +0 -20
  7. package/lib/browser/math/margin.js +1 -0
  8. package/lib/browser/math/position.d.ts +1 -0
  9. package/lib/browser/math/position.js +10 -2
  10. package/lib/browser/swap/UnifiedSwapClient.js +1 -10
  11. package/lib/browser/titan/titanClient.d.ts +4 -5
  12. package/lib/browser/titan/titanClient.js +2 -16
  13. package/lib/browser/types.d.ts +9 -6
  14. package/lib/browser/types.js +11 -7
  15. package/lib/browser/user.js +13 -7
  16. package/lib/node/decode/user.d.ts.map +1 -1
  17. package/lib/node/decode/user.js +8 -5
  18. package/lib/node/driftClient.d.ts +15 -10
  19. package/lib/node/driftClient.d.ts.map +1 -1
  20. package/lib/node/driftClient.js +137 -23
  21. package/lib/node/marginCalculation.d.ts +0 -12
  22. package/lib/node/marginCalculation.d.ts.map +1 -1
  23. package/lib/node/marginCalculation.js +0 -20
  24. package/lib/node/math/margin.d.ts.map +1 -1
  25. package/lib/node/math/margin.js +1 -0
  26. package/lib/node/math/position.d.ts +1 -0
  27. package/lib/node/math/position.d.ts.map +1 -1
  28. package/lib/node/math/position.js +10 -2
  29. package/lib/node/math/spotBalance.d.ts.map +1 -1
  30. package/lib/node/swap/UnifiedSwapClient.d.ts.map +1 -1
  31. package/lib/node/swap/UnifiedSwapClient.js +1 -10
  32. package/lib/node/titan/titanClient.d.ts +4 -5
  33. package/lib/node/titan/titanClient.d.ts.map +1 -1
  34. package/lib/node/titan/titanClient.js +2 -16
  35. package/lib/node/types.d.ts +9 -6
  36. package/lib/node/types.d.ts.map +1 -1
  37. package/lib/node/types.js +11 -7
  38. package/lib/node/user.d.ts.map +1 -1
  39. package/lib/node/user.js +13 -7
  40. package/package.json +1 -1
  41. package/scripts/deposit-isolated-positions.ts +110 -0
  42. package/scripts/find-flagged-users.ts +216 -0
  43. package/scripts/single-grpc-client-test.ts +71 -21
  44. package/scripts/withdraw-isolated-positions.ts +174 -0
  45. package/src/decode/user.ts +14 -6
  46. package/src/driftClient.ts +297 -65
  47. package/src/margin/README.md +139 -0
  48. package/src/marginCalculation.ts +0 -32
  49. package/src/math/margin.ts +2 -3
  50. package/src/math/position.ts +12 -2
  51. package/src/math/spotBalance.ts +0 -1
  52. package/src/swap/UnifiedSwapClient.ts +2 -13
  53. package/src/titan/titanClient.ts +4 -28
  54. package/src/types.ts +11 -7
  55. package/src/user.ts +17 -8
  56. package/tests/dlob/helpers.ts +1 -1
  57. package/tests/user/test.ts +1 -1
@@ -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,
@@ -140,6 +139,7 @@ import {
140
139
  BASE_PRECISION,
141
140
  GOV_SPOT_MARKET_INDEX,
142
141
  MARGIN_PRECISION,
142
+ MIN_I64,
143
143
  ONE,
144
144
  PERCENTAGE_PRECISION,
145
145
  PRICE_PRECISION,
@@ -147,7 +147,11 @@ import {
147
147
  QUOTE_SPOT_MARKET_INDEX,
148
148
  ZERO,
149
149
  } from './constants/numericConstants';
150
- import { findDirectionToClose, positionIsAvailable } from './math/position';
150
+ import {
151
+ calculateClaimablePnl,
152
+ findDirectionToClose,
153
+ positionIsAvailable,
154
+ } from './math/position';
151
155
  import { getSignedTokenAmount, getTokenAmount } from './math/spotBalance';
152
156
  import { decodeName, DEFAULT_USER_NAME, encodeName } from './userName';
153
157
  import { MMOraclePriceData, OraclePriceData } from './oracles/types';
@@ -200,6 +204,8 @@ import nacl from 'tweetnacl';
200
204
  import { Slothash } from './slot/SlothashSubscriber';
201
205
  import { getOracleId } from './oracles/oracleId';
202
206
  import { SignedMsgOrderParams } from './types';
207
+ import { TakerInfo } from './types';
208
+ // BN is already imported globally in this file via other imports
203
209
  import { sha256 } from '@noble/hashes/sha256';
204
210
  import { getOracleConfidenceFromMMOracleData } from './oracles/utils';
205
211
  import { ConstituentMap } from './constituentMap/constituentMap';
@@ -293,6 +299,46 @@ export class DriftClient {
293
299
  return this._isSubscribed && this.accountSubscriber.isSubscribed;
294
300
  }
295
301
 
302
+ private async getPrePlaceOrderIxs(
303
+ orderParams: OptionalOrderParams,
304
+ userAccount: UserAccount,
305
+ options?: { positionMaxLev?: number; isolatedPositionDepositAmount?: BN }
306
+ ): Promise<TransactionInstruction[]> {
307
+ const preIxs: TransactionInstruction[] = [];
308
+
309
+ if (isVariant(orderParams.marketType, 'perp')) {
310
+ const { positionMaxLev, isolatedPositionDepositAmount } = options ?? {};
311
+
312
+ if (
313
+ isolatedPositionDepositAmount?.gt?.(ZERO) &&
314
+ this.isOrderIncreasingPosition(orderParams, userAccount.subAccountId)
315
+ ) {
316
+ preIxs.push(
317
+ await this.getTransferIsolatedPerpPositionDepositIx(
318
+ isolatedPositionDepositAmount as BN,
319
+ orderParams.marketIndex,
320
+ userAccount.subAccountId
321
+ )
322
+ );
323
+ }
324
+
325
+ if (positionMaxLev) {
326
+ const marginRatio = Math.floor(
327
+ (1 / positionMaxLev) * MARGIN_PRECISION.toNumber()
328
+ );
329
+ preIxs.push(
330
+ await this.getUpdateUserPerpPositionCustomMarginRatioIx(
331
+ orderParams.marketIndex,
332
+ marginRatio,
333
+ userAccount.subAccountId
334
+ )
335
+ );
336
+ }
337
+ }
338
+
339
+ return preIxs;
340
+ }
341
+
296
342
  public set isSubscribed(val: boolean) {
297
343
  this._isSubscribed = val;
298
344
  }
@@ -766,7 +812,6 @@ export class DriftClient {
766
812
 
767
813
  return lookupTableAccount;
768
814
  }
769
-
770
815
  public async fetchAllLookupTableAccounts(): Promise<
771
816
  AddressLookupTableAccount[]
772
817
  > {
@@ -1771,7 +1816,6 @@ export class DriftClient {
1771
1816
  const { txSig } = await this.sendTransaction(tx, [], this.opts);
1772
1817
  return txSig;
1773
1818
  }
1774
-
1775
1819
  public async getUpdateUserCustomMarginRatioIx(
1776
1820
  marginRatio: number,
1777
1821
  subAccountId = 0
@@ -2580,7 +2624,6 @@ export class DriftClient {
2580
2624
  this.mustIncludeSpotMarketIndexes.add(spotMarketIndex);
2581
2625
  });
2582
2626
  }
2583
-
2584
2627
  getRemainingAccounts(params: RemainingAccountParams): AccountMeta[] {
2585
2628
  const { oracleAccountMap, spotMarketAccountMap, perpMarketAccountMap } =
2586
2629
  this.getRemainingAccountMapsForUsers(params.userAccounts);
@@ -3508,7 +3551,6 @@ export class DriftClient {
3508
3551
  userAccountPublicKey,
3509
3552
  };
3510
3553
  }
3511
-
3512
3554
  public async createInitializeUserAccountAndDepositCollateral(
3513
3555
  amount: BN,
3514
3556
  userTokenAccount: PublicKey,
@@ -4236,27 +4278,52 @@ export class DriftClient {
4236
4278
  amount: BN,
4237
4279
  perpMarketIndex: number,
4238
4280
  subAccountId?: number,
4239
- txParams?: TxParams
4281
+ txParams?: TxParams,
4282
+ trySettle?: boolean,
4283
+ noBuffer?: boolean
4240
4284
  ): Promise<TransactionSignature> {
4241
- const { txSig } = await this.sendTransaction(
4242
- await this.buildTransaction(
4243
- await this.getTransferIsolatedPerpPositionDepositIx(
4244
- amount,
4245
- perpMarketIndex,
4246
- subAccountId
4247
- ),
4248
- txParams
4249
- ),
4250
- [],
4251
- this.opts
4285
+ const ixs = [];
4286
+ const tokenAmountDeposited =
4287
+ this.getIsolatedPerpPositionTokenAmount(perpMarketIndex);
4288
+ const transferIx = await this.getTransferIsolatedPerpPositionDepositIx(
4289
+ amount,
4290
+ perpMarketIndex,
4291
+ subAccountId,
4292
+ noBuffer
4252
4293
  );
4294
+
4295
+ const needsToSettle =
4296
+ amount.lt(tokenAmountDeposited.neg()) || amount.eq(MIN_I64) || trySettle;
4297
+ if (needsToSettle) {
4298
+ const settleIx = await this.settleMultiplePNLsIx(
4299
+ await getUserAccountPublicKey(
4300
+ this.program.programId,
4301
+ this.authority,
4302
+ subAccountId ?? this.activeSubAccountId
4303
+ ),
4304
+ this.getUserAccount(subAccountId),
4305
+ [perpMarketIndex],
4306
+ SettlePnlMode.TRY_SETTLE
4307
+ );
4308
+ ixs.push(settleIx);
4309
+ }
4310
+
4311
+ ixs.push(transferIx);
4312
+
4313
+ const tx = await this.buildTransaction(ixs, txParams);
4314
+ const { txSig } = await this.sendTransaction(tx, [], {
4315
+ ...this.opts,
4316
+ skipPreflight: true,
4317
+ });
4253
4318
  return txSig;
4254
4319
  }
4255
4320
 
4256
4321
  public async getTransferIsolatedPerpPositionDepositIx(
4257
4322
  amount: BN,
4258
4323
  perpMarketIndex: number,
4259
- subAccountId?: number
4324
+ subAccountId?: number,
4325
+ noAmountBuffer?: boolean,
4326
+ signingAuthority?: PublicKey
4260
4327
  ): Promise<TransactionInstruction> {
4261
4328
  const userAccountPublicKey = await getUserAccountPublicKey(
4262
4329
  this.program.programId,
@@ -4274,17 +4341,22 @@ export class DriftClient {
4274
4341
  readablePerpMarketIndex: [perpMarketIndex],
4275
4342
  });
4276
4343
 
4344
+ const amountWithBuffer =
4345
+ noAmountBuffer || amount.eq(MIN_I64)
4346
+ ? amount
4347
+ : amount.add(amount.div(new BN(200))); // .5% buffer
4348
+
4277
4349
  return await this.program.instruction.transferIsolatedPerpPositionDeposit(
4278
4350
  spotMarketIndex,
4279
4351
  perpMarketIndex,
4280
- amount,
4352
+ amountWithBuffer,
4281
4353
  {
4282
4354
  accounts: {
4283
4355
  state: await this.getStatePublicKey(),
4284
4356
  spotMarketVault: spotMarketAccount.vault,
4285
4357
  user: userAccountPublicKey,
4286
4358
  userStats: this.getUserStatsAccountPublicKey(),
4287
- authority: this.wallet.publicKey,
4359
+ authority: signingAuthority ?? this.wallet.publicKey,
4288
4360
  },
4289
4361
  remainingAccounts,
4290
4362
  }
@@ -4298,20 +4370,83 @@ export class DriftClient {
4298
4370
  subAccountId?: number,
4299
4371
  txParams?: TxParams
4300
4372
  ): Promise<TransactionSignature> {
4373
+ const instructions =
4374
+ await this.getWithdrawFromIsolatedPerpPositionIxsBundle(
4375
+ amount,
4376
+ perpMarketIndex,
4377
+ subAccountId,
4378
+ userTokenAccount
4379
+ );
4301
4380
  const { txSig } = await this.sendTransaction(
4302
- await this.buildTransaction(
4303
- await this.getWithdrawFromIsolatedPerpPositionIx(
4304
- amount,
4305
- perpMarketIndex,
4306
- userTokenAccount,
4307
- subAccountId
4308
- ),
4309
- txParams
4310
- )
4381
+ await this.buildTransaction(instructions, txParams)
4311
4382
  );
4312
4383
  return txSig;
4313
4384
  }
4314
4385
 
4386
+ public async getWithdrawFromIsolatedPerpPositionIxsBundle(
4387
+ amount: BN,
4388
+ perpMarketIndex: number,
4389
+ subAccountId?: number,
4390
+ userTokenAccount?: PublicKey
4391
+ ): Promise<TransactionInstruction[]> {
4392
+ const userAccountPublicKey = await getUserAccountPublicKey(
4393
+ this.program.programId,
4394
+ this.authority,
4395
+ subAccountId ?? this.activeSubAccountId
4396
+ );
4397
+ const userAccount = this.getUserAccount(subAccountId);
4398
+
4399
+ const tokenAmountDeposited =
4400
+ this.getIsolatedPerpPositionTokenAmount(perpMarketIndex);
4401
+ const isolatedPositionUnrealizedPnl = calculateClaimablePnl(
4402
+ this.getPerpMarketAccount(perpMarketIndex),
4403
+ this.getSpotMarketAccount(
4404
+ this.getPerpMarketAccount(perpMarketIndex).quoteSpotMarketIndex
4405
+ ),
4406
+ userAccount.perpPositions.find((p) => p.marketIndex === perpMarketIndex),
4407
+ this.getOracleDataForSpotMarket(
4408
+ this.getPerpMarketAccount(perpMarketIndex).quoteSpotMarketIndex
4409
+ )
4410
+ );
4411
+
4412
+ const depositAmountPlusUnrealizedPnl = tokenAmountDeposited.add(
4413
+ isolatedPositionUnrealizedPnl
4414
+ );
4415
+
4416
+ const amountToWithdraw = amount.gt(depositAmountPlusUnrealizedPnl)
4417
+ ? MIN_I64 // min i64
4418
+ : amount;
4419
+ let associatedTokenAccount = userTokenAccount;
4420
+ if (!associatedTokenAccount) {
4421
+ const perpMarketAccount = this.getPerpMarketAccount(perpMarketIndex);
4422
+ const quoteSpotMarketIndex = perpMarketAccount.quoteSpotMarketIndex;
4423
+ associatedTokenAccount = await this.getAssociatedTokenAccount(
4424
+ quoteSpotMarketIndex
4425
+ );
4426
+ }
4427
+
4428
+ const withdrawIx = await this.getWithdrawFromIsolatedPerpPositionIx(
4429
+ amountToWithdraw,
4430
+ perpMarketIndex,
4431
+ associatedTokenAccount,
4432
+ subAccountId
4433
+ );
4434
+ const ixs = [withdrawIx];
4435
+
4436
+ const needsToSettle =
4437
+ amount.gt(tokenAmountDeposited) && isolatedPositionUnrealizedPnl.gt(ZERO);
4438
+ if (needsToSettle) {
4439
+ const settleIx = await this.settleMultiplePNLsIx(
4440
+ userAccountPublicKey,
4441
+ userAccount,
4442
+ [perpMarketIndex],
4443
+ SettlePnlMode.TRY_SETTLE
4444
+ );
4445
+ ixs.push(settleIx);
4446
+ }
4447
+ return ixs;
4448
+ }
4449
+
4315
4450
  public async getWithdrawFromIsolatedPerpPositionIx(
4316
4451
  amount: BN,
4317
4452
  perpMarketIndex: number,
@@ -4495,7 +4630,6 @@ export class DriftClient {
4495
4630
  }
4496
4631
  );
4497
4632
  }
4498
-
4499
4633
  public async getRemovePerpLpSharesIx(
4500
4634
  marketIndex: number,
4501
4635
  sharesToBurn?: BN,
@@ -4652,7 +4786,8 @@ export class DriftClient {
4652
4786
  referrerInfo?: ReferrerInfo,
4653
4787
  cancelExistingOrders?: boolean,
4654
4788
  settlePnl?: boolean,
4655
- positionMaxLev?: number
4789
+ positionMaxLev?: number,
4790
+ isolatedPositionDepositAmount?: BN
4656
4791
  ): Promise<{
4657
4792
  cancelExistingOrdersTx?: Transaction | VersionedTransaction;
4658
4793
  settlePnlTx?: Transaction | VersionedTransaction;
@@ -4680,18 +4815,25 @@ export class DriftClient {
4680
4815
 
4681
4816
  const txKeys = Object.keys(ixPromisesForTxs);
4682
4817
 
4683
- const marketOrderTxIxs = positionMaxLev
4684
- ? this.getPlaceOrdersAndSetPositionMaxLevIx(
4685
- [orderParams, ...bracketOrdersParams],
4686
- positionMaxLev,
4687
- userAccount.subAccountId
4688
- )
4689
- : this.getPlaceOrdersIx(
4690
- [orderParams, ...bracketOrdersParams],
4691
- userAccount.subAccountId
4692
- );
4818
+ const preIxs: TransactionInstruction[] = await this.getPrePlaceOrderIxs(
4819
+ orderParams,
4820
+ userAccount,
4821
+ {
4822
+ positionMaxLev,
4823
+ isolatedPositionDepositAmount,
4824
+ }
4825
+ );
4693
4826
 
4694
- ixPromisesForTxs.marketOrderTx = marketOrderTxIxs;
4827
+ ixPromisesForTxs.marketOrderTx = (async () => {
4828
+ const placeOrdersIx = await this.getPlaceOrdersIx(
4829
+ [orderParams, ...bracketOrdersParams],
4830
+ userAccount.subAccountId
4831
+ );
4832
+ if (preIxs.length) {
4833
+ return [...preIxs, placeOrdersIx] as unknown as TransactionInstruction;
4834
+ }
4835
+ return placeOrdersIx;
4836
+ })();
4695
4837
 
4696
4838
  /* Cancel open orders in market if requested */
4697
4839
  if (cancelExistingOrders && isVariant(orderParams.marketType, 'perp')) {
@@ -4809,12 +4951,32 @@ export class DriftClient {
4809
4951
  public async placePerpOrder(
4810
4952
  orderParams: OptionalOrderParams,
4811
4953
  txParams?: TxParams,
4812
- subAccountId?: number
4954
+ subAccountId?: number,
4955
+ isolatedPositionDepositAmount?: BN
4813
4956
  ): Promise<TransactionSignature> {
4957
+ const preIxs: TransactionInstruction[] = [];
4958
+ if (
4959
+ isolatedPositionDepositAmount?.gt?.(ZERO) &&
4960
+ this.isOrderIncreasingPosition(orderParams, subAccountId)
4961
+ ) {
4962
+ preIxs.push(
4963
+ await this.getTransferIsolatedPerpPositionDepositIx(
4964
+ isolatedPositionDepositAmount as BN,
4965
+ orderParams.marketIndex,
4966
+ subAccountId
4967
+ )
4968
+ );
4969
+ }
4970
+
4814
4971
  const { txSig, slot } = await this.sendTransaction(
4815
4972
  await this.buildTransaction(
4816
4973
  await this.getPlacePerpOrderIx(orderParams, subAccountId),
4817
- txParams
4974
+ txParams,
4975
+ undefined,
4976
+ undefined,
4977
+ undefined,
4978
+ undefined,
4979
+ preIxs
4818
4980
  ),
4819
4981
  [],
4820
4982
  this.opts
@@ -5002,13 +5164,31 @@ export class DriftClient {
5002
5164
  public async cancelOrder(
5003
5165
  orderId?: number,
5004
5166
  txParams?: TxParams,
5005
- subAccountId?: number
5167
+ subAccountId?: number,
5168
+ overrides?: { withdrawIsolatedDepositAmount?: BN }
5006
5169
  ): Promise<TransactionSignature> {
5170
+ const cancelIx = await this.getCancelOrderIx(orderId, subAccountId);
5171
+
5172
+ const instructions: TransactionInstruction[] = [cancelIx];
5173
+
5174
+ if (overrides?.withdrawIsolatedDepositAmount !== undefined) {
5175
+ const order = this.getOrder(orderId, subAccountId);
5176
+ const perpMarketIndex = order?.marketIndex;
5177
+ const withdrawAmount = overrides.withdrawIsolatedDepositAmount;
5178
+
5179
+ if (withdrawAmount.gt(ZERO)) {
5180
+ const withdrawIxs =
5181
+ await this.getWithdrawFromIsolatedPerpPositionIxsBundle(
5182
+ withdrawAmount,
5183
+ perpMarketIndex,
5184
+ subAccountId
5185
+ );
5186
+ instructions.push(...withdrawIxs);
5187
+ }
5188
+ }
5189
+
5007
5190
  const { txSig } = await this.sendTransaction(
5008
- await this.buildTransaction(
5009
- await this.getCancelOrderIx(orderId, subAccountId),
5010
- txParams
5011
- ),
5191
+ await this.buildTransaction(instructions, txParams),
5012
5192
  [],
5013
5193
  this.opts
5014
5194
  );
@@ -5242,7 +5422,8 @@ export class DriftClient {
5242
5422
  params: OrderParams[],
5243
5423
  txParams?: TxParams,
5244
5424
  subAccountId?: number,
5245
- optionalIxs?: TransactionInstruction[]
5425
+ optionalIxs?: TransactionInstruction[],
5426
+ isolatedPositionDepositAmount?: BN
5246
5427
  ): Promise<TransactionSignature> {
5247
5428
  const { txSig } = await this.sendTransaction(
5248
5429
  (
@@ -5250,7 +5431,8 @@ export class DriftClient {
5250
5431
  params,
5251
5432
  txParams,
5252
5433
  subAccountId,
5253
- optionalIxs
5434
+ optionalIxs,
5435
+ isolatedPositionDepositAmount
5254
5436
  )
5255
5437
  ).placeOrdersTx,
5256
5438
  [],
@@ -5264,10 +5446,29 @@ export class DriftClient {
5264
5446
  params: OrderParams[],
5265
5447
  txParams?: TxParams,
5266
5448
  subAccountId?: number,
5267
- optionalIxs?: TransactionInstruction[]
5449
+ optionalIxs?: TransactionInstruction[],
5450
+ isolatedPositionDepositAmount?: BN
5268
5451
  ) {
5269
5452
  const lookupTableAccounts = await this.fetchAllLookupTableAccounts();
5270
5453
 
5454
+ const preIxs: TransactionInstruction[] = [];
5455
+ if (params?.length === 1) {
5456
+ const p = params[0];
5457
+ if (
5458
+ isVariant(p.marketType, 'perp') &&
5459
+ isolatedPositionDepositAmount?.gt?.(ZERO) &&
5460
+ this.isOrderIncreasingPosition(p, subAccountId)
5461
+ ) {
5462
+ preIxs.push(
5463
+ await this.getTransferIsolatedPerpPositionDepositIx(
5464
+ isolatedPositionDepositAmount as BN,
5465
+ p.marketIndex,
5466
+ subAccountId
5467
+ )
5468
+ );
5469
+ }
5470
+ }
5471
+
5271
5472
  const tx = await this.buildTransaction(
5272
5473
  await this.getPlaceOrdersIx(params, subAccountId),
5273
5474
  txParams,
@@ -5275,14 +5476,13 @@ export class DriftClient {
5275
5476
  lookupTableAccounts,
5276
5477
  undefined,
5277
5478
  undefined,
5278
- optionalIxs
5479
+ [...preIxs, ...(optionalIxs ?? [])]
5279
5480
  );
5280
5481
 
5281
5482
  return {
5282
5483
  placeOrdersTx: tx,
5283
5484
  };
5284
5485
  }
5285
-
5286
5486
  public async getPlaceOrdersIx(
5287
5487
  params: OptionalOrderParams[],
5288
5488
  subAccountId?: number,
@@ -5391,8 +5591,7 @@ export class DriftClient {
5391
5591
  const marginRatio = Math.floor(
5392
5592
  (1 / positionMaxLev) * MARGIN_PRECISION.toNumber()
5393
5593
  );
5394
-
5395
- // TODO: Handle multiple markets?
5594
+ // Keep existing behavior but note: prefer using getPostPlaceOrderIxs path
5396
5595
  const setPositionMaxLevIxs =
5397
5596
  await this.getUpdateUserPerpPositionCustomMarginRatioIx(
5398
5597
  readablePerpMarketIndex[0],
@@ -7110,7 +7309,6 @@ export class DriftClient {
7110
7309
  this.perpMarketLastSlotCache.set(orderParams.marketIndex, slot);
7111
7310
  return txSig;
7112
7311
  }
7113
-
7114
7312
  public async preparePlaceAndTakePerpOrderWithAdditionalOrders(
7115
7313
  orderParams: OptionalOrderParams,
7116
7314
  makerInfo?: MakerInfo | MakerInfo[],
@@ -7122,7 +7320,8 @@ export class DriftClient {
7122
7320
  settlePnl?: boolean,
7123
7321
  exitEarlyIfSimFails?: boolean,
7124
7322
  auctionDurationPercentage?: number,
7125
- optionalIxs?: TransactionInstruction[]
7323
+ optionalIxs?: TransactionInstruction[],
7324
+ isolatedPositionDepositAmount?: BN
7126
7325
  ): Promise<{
7127
7326
  placeAndTakeTx: Transaction | VersionedTransaction;
7128
7327
  cancelExistingOrdersTx: Transaction | VersionedTransaction;
@@ -7156,6 +7355,20 @@ export class DriftClient {
7156
7355
  subAccountId
7157
7356
  );
7158
7357
 
7358
+ if (
7359
+ isVariant(orderParams.marketType, 'perp') &&
7360
+ isolatedPositionDepositAmount?.gt?.(ZERO) &&
7361
+ this.isOrderIncreasingPosition(orderParams, subAccountId)
7362
+ ) {
7363
+ placeAndTakeIxs.push(
7364
+ await this.getTransferIsolatedPerpPositionDepositIx(
7365
+ isolatedPositionDepositAmount as BN,
7366
+ orderParams.marketIndex,
7367
+ subAccountId
7368
+ )
7369
+ );
7370
+ }
7371
+
7159
7372
  placeAndTakeIxs.push(placeAndTakeIx);
7160
7373
 
7161
7374
  if (bracketOrdersParams.length > 0) {
@@ -7166,6 +7379,11 @@ export class DriftClient {
7166
7379
  placeAndTakeIxs.push(bracketOrdersIx);
7167
7380
  }
7168
7381
 
7382
+ // Optional extra ixs can be appended at the front
7383
+ if (optionalIxs?.length) {
7384
+ placeAndTakeIxs.unshift(...optionalIxs);
7385
+ }
7386
+
7169
7387
  const shouldUseSimulationComputeUnits =
7170
7388
  txParams?.useSimulatedComputeUnits;
7171
7389
  const shouldExitIfSimulationFails = exitEarlyIfSimFails;
@@ -7973,7 +8191,6 @@ export class DriftClient {
7973
8191
  this.spotMarketLastSlotCache.set(QUOTE_SPOT_MARKET_INDEX, slot);
7974
8192
  return txSig;
7975
8193
  }
7976
-
7977
8194
  public async getPlaceAndTakeSpotOrderIx(
7978
8195
  orderParams: OptionalOrderParams,
7979
8196
  fulfillmentConfig?: SerumV3FulfillmentConfigAccount,
@@ -8436,7 +8653,6 @@ export class DriftClient {
8436
8653
  bitFlags?: number;
8437
8654
  policy?: ModifyOrderPolicy;
8438
8655
  maxTs?: BN;
8439
- txParams?: TxParams;
8440
8656
  },
8441
8657
  subAccountId?: number
8442
8658
  ): Promise<TransactionInstruction> {
@@ -8962,7 +9178,6 @@ export class DriftClient {
8962
9178
  this.perpMarketLastSlotCache.set(marketIndex, slot);
8963
9179
  return txSig;
8964
9180
  }
8965
-
8966
9181
  public async getLiquidatePerpIx(
8967
9182
  userAccountPublicKey: PublicKey,
8968
9183
  userAccount: UserAccount,
@@ -9753,7 +9968,6 @@ export class DriftClient {
9753
9968
  }
9754
9969
  );
9755
9970
  }
9756
-
9757
9971
  public async resolveSpotBankruptcy(
9758
9972
  userAccountPublicKey: PublicKey,
9759
9973
  userAccount: UserAccount,
@@ -10590,7 +10804,6 @@ export class DriftClient {
10590
10804
  const { txSig } = await this.sendTransaction(tx, [], this.opts);
10591
10805
  return txSig;
10592
10806
  }
10593
-
10594
10807
  public async getSettleRevenueToInsuranceFundIx(
10595
10808
  spotMarketIndex: number
10596
10809
  ): Promise<TransactionInstruction> {
@@ -11394,7 +11607,6 @@ export class DriftClient {
11394
11607
  );
11395
11608
  return config as ProtectedMakerModeConfig;
11396
11609
  }
11397
-
11398
11610
  public async updateUserProtectedMakerOrders(
11399
11611
  subAccountId: number,
11400
11612
  protectedOrders: boolean,
@@ -12788,4 +13000,24 @@ export class DriftClient {
12788
13000
  forceVersionedTransaction,
12789
13001
  });
12790
13002
  }
13003
+
13004
+ isOrderIncreasingPosition(
13005
+ orderParams: OptionalOrderParams,
13006
+ subAccountId: number
13007
+ ): boolean {
13008
+ const userAccount = this.getUserAccount(subAccountId);
13009
+ const perpPosition = userAccount.perpPositions.find(
13010
+ (p) => p.marketIndex === orderParams.marketIndex
13011
+ );
13012
+ if (!perpPosition) return true;
13013
+
13014
+ const currentBase = perpPosition.baseAssetAmount;
13015
+ if (currentBase.eq(ZERO)) return true;
13016
+
13017
+ const orderBaseAmount = isVariant(orderParams.direction, 'long')
13018
+ ? orderParams.baseAssetAmount
13019
+ : orderParams.baseAssetAmount.neg();
13020
+
13021
+ return currentBase.add(orderBaseAmount).abs().gt(currentBase.abs());
13022
+ }
12791
13023
  }