@drift-labs/sdk 0.2.0-master.35 → 0.2.0-master.37

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.
@@ -38,7 +38,7 @@ async function getUserAccountPublicKeyAndNonce(programId, authority, subAccountI
38
38
  return web3_js_1.PublicKey.findProgramAddress([
39
39
  Buffer.from(anchor.utils.bytes.utf8.encode('user')),
40
40
  authority.toBuffer(),
41
- Uint8Array.from([subAccountId]),
41
+ new anchor.BN(subAccountId).toArrayLike(Buffer, 'le', 2),
42
42
  ], programId);
43
43
  }
44
44
  exports.getUserAccountPublicKeyAndNonce = getUserAccountPublicKeyAndNonce;
@@ -50,7 +50,7 @@ function getUserAccountPublicKeySync(programId, authority, subAccountId = 0) {
50
50
  return web3_js_1.PublicKey.findProgramAddressSync([
51
51
  Buffer.from(anchor.utils.bytes.utf8.encode('user')),
52
52
  authority.toBuffer(),
53
- Uint8Array.from([subAccountId]),
53
+ new anchor.BN(subAccountId).toArrayLike(Buffer, 'le', 2),
54
54
  ], programId)[0];
55
55
  }
56
56
  exports.getUserAccountPublicKeySync = getUserAccountPublicKeySync;
package/lib/admin.d.ts CHANGED
@@ -5,9 +5,9 @@ import { BN } from '@project-serum/anchor';
5
5
  import { ClearingHouse } from './clearingHouse';
6
6
  export declare class Admin extends ClearingHouse {
7
7
  initialize(usdcMint: PublicKey, _adminControlsPrices: boolean): Promise<[TransactionSignature]>;
8
- initializeSpotMarket(mint: PublicKey, optimalUtilization: number, optimalRate: number, maxRate: number, oracle: PublicKey, oracleSource: OracleSource, initialAssetWeight: BN, maintenanceAssetWeight: BN, initialLiabilityWeight: BN, maintenanceLiabilityWeight: BN, imfFactor?: BN, liquidationFee?: BN, activeStatus?: boolean): Promise<TransactionSignature>;
8
+ initializeSpotMarket(mint: PublicKey, optimalUtilization: number, optimalRate: number, maxRate: number, oracle: PublicKey, oracleSource: OracleSource, initialAssetWeight: number, maintenanceAssetWeight: number, initialLiabilityWeight: number, maintenanceLiabilityWeight: number, imfFactor?: number, liquidationFee?: number, activeStatus?: boolean): Promise<TransactionSignature>;
9
9
  initializeSerumFulfillmentConfig(marketIndex: number, serumMarket: PublicKey, serumProgram: PublicKey): Promise<TransactionSignature>;
10
- initializePerpMarket(priceOracle: PublicKey, baseAssetReserve: BN, quoteAssetReserve: BN, periodicity: BN, pegMultiplier?: BN, oracleSource?: OracleSource, marginRatioInitial?: number, marginRatioMaintenance?: number, liquidationFee?: BN, activeStatus?: boolean, name?: string): Promise<TransactionSignature>;
10
+ initializePerpMarket(priceOracle: PublicKey, baseAssetReserve: BN, quoteAssetReserve: BN, periodicity: BN, pegMultiplier?: BN, oracleSource?: OracleSource, marginRatioInitial?: number, marginRatioMaintenance?: number, liquidationFee?: number, activeStatus?: boolean, name?: string): Promise<TransactionSignature>;
11
11
  moveAmmPrice(perpMarketIndex: number, baseAssetReserve: BN, quoteAssetReserve: BN, sqrtK?: BN): Promise<TransactionSignature>;
12
12
  updateK(perpMarketIndex: number, sqrtK: BN): Promise<TransactionSignature>;
13
13
  updatePerpMarketConcentrationScale(perpMarketIndex: number, concentrationScale: BN): Promise<TransactionSignature>;
@@ -41,7 +41,7 @@ export declare class Admin extends ClearingHouse {
41
41
  updateSpotMarketExpiry(spotMarketIndex: number, expiryTs: BN): Promise<TransactionSignature>;
42
42
  updateWhitelistMint(whitelistMint?: PublicKey): Promise<TransactionSignature>;
43
43
  updateDiscountMint(discountMint: PublicKey): Promise<TransactionSignature>;
44
- updateSpotMarketMarginWeights(spotMarketIndex: number, initialAssetWeight: BN, maintenanceAssetWeight: BN, initialLiabilityWeight: BN, maintenanceLiabilityWeight: BN, imfFactor?: BN): Promise<TransactionSignature>;
44
+ updateSpotMarketMarginWeights(spotMarketIndex: number, initialAssetWeight: number, maintenanceAssetWeight: number, initialLiabilityWeight: number, maintenanceLiabilityWeight: number, imfFactor?: number): Promise<TransactionSignature>;
45
45
  updateSpotMarketAssetTier(spotMarketIndex: number, assetTier: AssetTier): Promise<TransactionSignature>;
46
46
  updateSpotMarketStatus(spotMarketIndex: number, marketStatus: MarketStatus): Promise<TransactionSignature>;
47
47
  updatePerpMarketStatus(perpMarketIndex: number, marketStatus: MarketStatus): Promise<TransactionSignature>;
package/lib/admin.js CHANGED
@@ -57,7 +57,7 @@ class Admin extends clearingHouse_1.ClearingHouse {
57
57
  const { txSig: initializeTxSig } = await this.txSender.send(initializeTx, [], this.opts);
58
58
  return [initializeTxSig];
59
59
  }
60
- async initializeSpotMarket(mint, optimalUtilization, optimalRate, maxRate, oracle, oracleSource, initialAssetWeight, maintenanceAssetWeight, initialLiabilityWeight, maintenanceLiabilityWeight, imfFactor = new anchor_1.BN(0), liquidationFee = numericConstants_1.ZERO, activeStatus = true) {
60
+ async initializeSpotMarket(mint, optimalUtilization, optimalRate, maxRate, oracle, oracleSource, initialAssetWeight, maintenanceAssetWeight, initialLiabilityWeight, maintenanceLiabilityWeight, imfFactor = 0, liquidationFee = 0, activeStatus = true) {
61
61
  const spotMarketIndex = this.getStateAccount().numberOfSpotMarkets;
62
62
  const spotMarket = await (0, pda_1.getSpotMarketPublicKey)(this.program.programId, spotMarketIndex);
63
63
  const spotMarketVault = await (0, pda_1.getSpotMarketVaultPublicKey)(this.program.programId, spotMarketIndex);
@@ -104,7 +104,7 @@ class Admin extends clearingHouse_1.ClearingHouse {
104
104
  },
105
105
  });
106
106
  }
107
- async initializePerpMarket(priceOracle, baseAssetReserve, quoteAssetReserve, periodicity, pegMultiplier = numericConstants_1.PEG_PRECISION, oracleSource = types_1.OracleSource.PYTH, marginRatioInitial = 2000, marginRatioMaintenance = 500, liquidationFee = numericConstants_1.ZERO, activeStatus = true, name = userName_1.DEFAULT_MARKET_NAME) {
107
+ async initializePerpMarket(priceOracle, baseAssetReserve, quoteAssetReserve, periodicity, pegMultiplier = numericConstants_1.PEG_PRECISION, oracleSource = types_1.OracleSource.PYTH, marginRatioInitial = 2000, marginRatioMaintenance = 500, liquidationFee = 0, activeStatus = true, name = userName_1.DEFAULT_MARKET_NAME) {
108
108
  const perpMarketPublicKey = await (0, pda_1.getPerpMarketPublicKey)(this.program.programId, this.getStateAccount().numberOfMarkets);
109
109
  const nameBuffer = (0, userName_1.encodeName)(name);
110
110
  const initializeMarketTx = await this.program.transaction.initializePerpMarket(baseAssetReserve, quoteAssetReserve, periodicity, pegMultiplier, oracleSource, marginRatioInitial, marginRatioMaintenance, liquidationFee, activeStatus, nameBuffer, {
@@ -445,7 +445,7 @@ class Admin extends clearingHouse_1.ClearingHouse {
445
445
  },
446
446
  });
447
447
  }
448
- async updateSpotMarketMarginWeights(spotMarketIndex, initialAssetWeight, maintenanceAssetWeight, initialLiabilityWeight, maintenanceLiabilityWeight, imfFactor = new anchor_1.BN(0)) {
448
+ async updateSpotMarketMarginWeights(spotMarketIndex, initialAssetWeight, maintenanceAssetWeight, initialLiabilityWeight, maintenanceLiabilityWeight, imfFactor = 0) {
449
449
  return await this.program.rpc.updateSpotMarketMarginWeights(initialAssetWeight, maintenanceAssetWeight, initialLiabilityWeight, maintenanceLiabilityWeight, imfFactor, {
450
450
  accounts: {
451
451
  admin: this.wallet.publicKey,
@@ -19,7 +19,7 @@ declare type RemainingAccountParams = {
19
19
  writablePerpMarketIndexes?: number[];
20
20
  writableSpotMarketIndexes?: number[];
21
21
  readablePerpMarketIndex?: number;
22
- readableSpotMarketIndex?: number;
22
+ readableSpotMarketIndexes?: number[];
23
23
  useMarketLastSlotCache?: boolean;
24
24
  };
25
25
  /**
@@ -40,7 +40,8 @@ export declare class ClearingHouse {
40
40
  eventEmitter: StrictEventEmitter<EventEmitter, ClearingHouseAccountEvents>;
41
41
  _isSubscribed: boolean;
42
42
  txSender: TxSender;
43
- marketLastSlotCache: Map<number, number>;
43
+ perpMarketLastSlotCache: Map<number, number>;
44
+ spotMarketLastSlotCache: Map<number, number>;
44
45
  authority: PublicKey;
45
46
  get isSubscribed(): boolean;
46
47
  set isSubscribed(val: boolean);
@@ -176,22 +177,31 @@ export declare class ClearingHouse {
176
177
  getPlaceAndMakePerpOrderIx(orderParams: OptionalOrderParams, takerInfo: TakerInfo, referrerInfo?: ReferrerInfo): Promise<TransactionInstruction>;
177
178
  placeAndTakeSpotOrder(orderParams: OptionalOrderParams, fulfillmentConfig?: SerumV3FulfillmentConfigAccount, makerInfo?: MakerInfo, referrerInfo?: ReferrerInfo): Promise<TransactionSignature>;
178
179
  getPlaceAndTakeSpotOrderIx(orderParams: OptionalOrderParams, fulfillmentConfig?: SerumV3FulfillmentConfigAccount, makerInfo?: MakerInfo, referrerInfo?: ReferrerInfo): Promise<TransactionInstruction>;
179
- placeAndMakeSpotOrder(orderParams: OptionalOrderParams, takerInfo: TakerInfo, referrerInfo?: ReferrerInfo): Promise<TransactionSignature>;
180
- getPlaceAndMakeSpotOrderIx(orderParams: OptionalOrderParams, takerInfo: TakerInfo, referrerInfo?: ReferrerInfo): Promise<TransactionInstruction>;
180
+ placeAndMakeSpotOrder(orderParams: OptionalOrderParams, takerInfo: TakerInfo, fulfillmentConfig?: SerumV3FulfillmentConfigAccount, referrerInfo?: ReferrerInfo): Promise<TransactionSignature>;
181
+ getPlaceAndMakeSpotOrderIx(orderParams: OptionalOrderParams, takerInfo: TakerInfo, fulfillmentConfig?: SerumV3FulfillmentConfigAccount, referrerInfo?: ReferrerInfo): Promise<TransactionInstruction>;
181
182
  /**
182
183
  * Close an entire position. If you want to reduce a position, use the {@link openPosition} method in the opposite direction of the current position.
183
184
  * @param marketIndex
184
185
  * @returns
185
186
  */
186
187
  closePosition(marketIndex: number, limitPrice?: BN): Promise<TransactionSignature>;
188
+ /**
189
+ * Modifies an open order by closing it and replacing it with a new order.
190
+ * @param orderId: The open order to modify
191
+ * @param newBaseAmount: The new base amount for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
192
+ * @param newLimitPice: The new limit price for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
193
+ * @param newOraclePriceOffset: The new oracle price offset for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
194
+ * @returns
195
+ */
196
+ modifyPerpOrder(orderId: number, newBaseAmount?: BN, newLimitPrice?: BN, newOraclePriceOffset?: number): Promise<TransactionSignature>;
187
197
  settlePNLs(users: {
188
198
  settleeUserAccountPublicKey: PublicKey;
189
199
  settleeUserAccount: UserAccount;
190
200
  }[], marketIndex: number): Promise<TransactionSignature>;
191
201
  settlePNL(settleeUserAccountPublicKey: PublicKey, settleeUserAccount: UserAccount, marketIndex: number): Promise<TransactionSignature>;
192
202
  settlePNLIx(settleeUserAccountPublicKey: PublicKey, settleeUserAccount: UserAccount, marketIndex: number): Promise<TransactionInstruction>;
193
- liquidatePerp(userAccountPublicKey: PublicKey, userAccount: UserAccount, marketIndex: number, maxBaseAssetAmount: BN): Promise<TransactionSignature>;
194
- getLiquidatePerpIx(userAccountPublicKey: PublicKey, userAccount: UserAccount, marketIndex: number, maxBaseAssetAmount: BN): Promise<TransactionInstruction>;
203
+ liquidatePerp(userAccountPublicKey: PublicKey, userAccount: UserAccount, marketIndex: number, maxBaseAssetAmount: BN, limitPrice?: BN): Promise<TransactionSignature>;
204
+ getLiquidatePerpIx(userAccountPublicKey: PublicKey, userAccount: UserAccount, marketIndex: number, maxBaseAssetAmount: BN, limitPrice?: BN): Promise<TransactionInstruction>;
195
205
  liquidateSpot(userAccountPublicKey: PublicKey, userAccount: UserAccount, assetMarketIndex: number, liabilityMarketIndex: number, maxLiabilityTransfer: BN): Promise<TransactionSignature>;
196
206
  getLiquidateSpotIx(userAccountPublicKey: PublicKey, userAccount: UserAccount, assetMarketIndex: number, liabilityMarketIndex: number, maxLiabilityTransfer: BN): Promise<TransactionInstruction>;
197
207
  liquidateBorrowForPerpPnl(userAccountPublicKey: PublicKey, userAccount: UserAccount, perpMarketIndex: number, liabilityMarketIndex: number, maxLiabilityTransfer: BN): Promise<TransactionSignature>;
@@ -57,7 +57,8 @@ class ClearingHouse {
57
57
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
58
58
  this.users = new Map();
59
59
  this._isSubscribed = false;
60
- this.marketLastSlotCache = new Map();
60
+ this.perpMarketLastSlotCache = new Map();
61
+ this.spotMarketLastSlotCache = new Map();
61
62
  this.connection = config.connection;
62
63
  this.wallet = config.wallet;
63
64
  this.opts = config.opts || anchor_1.AnchorProvider.defaultOptions();
@@ -404,11 +405,11 @@ class ClearingHouse {
404
405
  var _a;
405
406
  const { oracleAccountMap, spotMarketAccountMap, perpMarketAccountMap } = this.getRemainingAccountMapsForUsers(params.userAccounts);
406
407
  if (params.useMarketLastSlotCache) {
407
- const lastUserPositionsSlot = (_a = this.getUserAccountAndSlot()) === null || _a === void 0 ? void 0 : _a.slot;
408
- for (const [marketIndex, slot] of this.marketLastSlotCache.entries()) {
408
+ const lastUserSlot = (_a = this.getUserAccountAndSlot()) === null || _a === void 0 ? void 0 : _a.slot;
409
+ for (const [marketIndex, slot,] of this.perpMarketLastSlotCache.entries()) {
409
410
  // if cache has more recent slot than user positions account slot, add market to remaining accounts
410
411
  // otherwise remove from slot
411
- if (slot > lastUserPositionsSlot) {
412
+ if (slot > lastUserSlot) {
412
413
  const marketAccount = this.getPerpMarketAccount(marketIndex);
413
414
  perpMarketAccountMap.set(marketIndex, {
414
415
  pubkey: marketAccount.pubkey,
@@ -422,7 +423,29 @@ class ClearingHouse {
422
423
  });
423
424
  }
424
425
  else {
425
- this.marketLastSlotCache.delete(marketIndex);
426
+ this.perpMarketLastSlotCache.delete(marketIndex);
427
+ }
428
+ }
429
+ for (const [marketIndex, slot,] of this.spotMarketLastSlotCache.entries()) {
430
+ // if cache has more recent slot than user positions account slot, add market to remaining accounts
431
+ // otherwise remove from slot
432
+ if (slot > lastUserSlot) {
433
+ const marketAccount = this.getSpotMarketAccount(marketIndex);
434
+ spotMarketAccountMap.set(marketIndex, {
435
+ pubkey: marketAccount.pubkey,
436
+ isSigner: false,
437
+ isWritable: false,
438
+ });
439
+ if (!marketAccount.oracle.equals(web3_js_1.PublicKey.default)) {
440
+ oracleAccountMap.set(marketAccount.oracle.toString(), {
441
+ pubkey: marketAccount.oracle,
442
+ isSigner: false,
443
+ isWritable: false,
444
+ });
445
+ }
446
+ }
447
+ else {
448
+ this.spotMarketLastSlotCache.delete(marketIndex);
426
449
  }
427
450
  }
428
451
  }
@@ -454,19 +477,21 @@ class ClearingHouse {
454
477
  });
455
478
  }
456
479
  }
457
- if (params.readableSpotMarketIndex !== undefined) {
458
- const spotMarketAccount = this.getSpotMarketAccount(params.readableSpotMarketIndex);
459
- spotMarketAccountMap.set(params.readableSpotMarketIndex, {
460
- pubkey: spotMarketAccount.pubkey,
461
- isSigner: false,
462
- isWritable: false,
463
- });
464
- if (spotMarketAccount.marketIndex !== 0) {
465
- oracleAccountMap.set(spotMarketAccount.oracle.toString(), {
466
- pubkey: spotMarketAccount.oracle,
480
+ if (params.readableSpotMarketIndexes !== undefined) {
481
+ for (const readableSpotMarketIndex of params.readableSpotMarketIndexes) {
482
+ const spotMarketAccount = this.getSpotMarketAccount(readableSpotMarketIndex);
483
+ spotMarketAccountMap.set(readableSpotMarketIndex, {
484
+ pubkey: spotMarketAccount.pubkey,
467
485
  isSigner: false,
468
486
  isWritable: false,
469
487
  });
488
+ if (spotMarketAccount.marketIndex !== 0) {
489
+ oracleAccountMap.set(spotMarketAccount.oracle.toString(), {
490
+ pubkey: spotMarketAccount.oracle,
491
+ isSigner: false,
492
+ isWritable: false,
493
+ });
494
+ }
470
495
  }
471
496
  }
472
497
  if (params.writableSpotMarketIndexes !== undefined) {
@@ -512,6 +537,14 @@ class ClearingHouse {
512
537
  isWritable: false,
513
538
  });
514
539
  }
540
+ if (!spotPosition.openAsks.eq(numericConstants_1.ZERO) ||
541
+ !spotPosition.openBids.eq(numericConstants_1.ZERO)) {
542
+ spotMarketAccountMap.set(spotPosition.marketIndex, {
543
+ pubkey: this.getQuoteSpotMarketAccount().pubkey,
544
+ isSigner: false,
545
+ isWritable: false,
546
+ });
547
+ }
515
548
  }
516
549
  }
517
550
  for (const position of userAccount.perpPositions) {
@@ -569,7 +602,8 @@ class ClearingHouse {
569
602
  if (createWSOLTokenAccount) {
570
603
  tx.add(spl_token_1.Token.createCloseAccountInstruction(spl_token_1.TOKEN_PROGRAM_ID, collateralAccountPublicKey, authority, authority, []));
571
604
  }
572
- const { txSig } = await this.txSender.send(tx, additionalSigners, this.opts);
605
+ const { txSig, slot } = await this.txSender.send(tx, additionalSigners, this.opts);
606
+ this.spotMarketLastSlotCache.set(marketIndex, slot);
573
607
  return txSig;
574
608
  }
575
609
  async getDepositInstruction(amount, marketIndex, userTokenAccount, subAccountId, reduceOnly = false, userInitialized = true) {
@@ -679,7 +713,8 @@ class ClearingHouse {
679
713
  if (createWSOLTokenAccount) {
680
714
  tx.add(spl_token_1.Token.createCloseAccountInstruction(spl_token_1.TOKEN_PROGRAM_ID, userTokenAccount, authority, authority, []));
681
715
  }
682
- const { txSig } = await this.txSender.send(tx, additionalSigners, this.opts);
716
+ const { txSig, slot } = await this.txSender.send(tx, additionalSigners, this.opts);
717
+ this.spotMarketLastSlotCache.set(marketIndex, slot);
683
718
  return [txSig, userAccountPublicKey];
684
719
  }
685
720
  async initializeUserAccountForDevnet(subAccountId = 0, name = userName_1.DEFAULT_USER_NAME, marketIndex, tokenFaucet, amount, referrerInfo) {
@@ -726,7 +761,8 @@ class ClearingHouse {
726
761
  if (createWSOLTokenAccount) {
727
762
  tx.add(spl_token_1.Token.createCloseAccountInstruction(spl_token_1.TOKEN_PROGRAM_ID, userTokenAccount, authority, authority, []));
728
763
  }
729
- const { txSig } = await this.txSender.send(tx, additionalSigners, this.opts);
764
+ const { txSig, slot } = await this.txSender.send(tx, additionalSigners, this.opts);
765
+ this.spotMarketLastSlotCache.set(marketIndex, slot);
730
766
  return txSig;
731
767
  }
732
768
  async getWithdrawIx(amount, marketIndex, userTokenAccount, reduceOnly = false) {
@@ -735,6 +771,7 @@ class ClearingHouse {
735
771
  userAccounts: [this.getUserAccount()],
736
772
  useMarketLastSlotCache: true,
737
773
  writableSpotMarketIndexes: [marketIndex],
774
+ readableSpotMarketIndexes: [numericConstants_1.QUOTE_SPOT_MARKET_INDEX],
738
775
  });
739
776
  const spotMarketAccount = this.getSpotMarketAccount(marketIndex);
740
777
  return await this.program.instruction.withdraw(marketIndex, amount, reduceOnly, {
@@ -753,7 +790,11 @@ class ClearingHouse {
753
790
  });
754
791
  }
755
792
  async transferDeposit(amount, marketIndex, fromSubAccountId, toSubAccountId) {
756
- const { txSig } = await this.txSender.send((0, utils_1.wrapInTx)(await this.getTransferDepositIx(amount, marketIndex, fromSubAccountId, toSubAccountId)), [], this.opts);
793
+ const { txSig, slot } = await this.txSender.send((0, utils_1.wrapInTx)(await this.getTransferDepositIx(amount, marketIndex, fromSubAccountId, toSubAccountId)), [], this.opts);
794
+ if (fromSubAccountId === this.activeSubAccountId ||
795
+ toSubAccountId === this.activeSubAccountId) {
796
+ this.spotMarketLastSlotCache.set(marketIndex, slot);
797
+ }
757
798
  return txSig;
758
799
  }
759
800
  async getTransferDepositIx(amount, marketIndex, fromSubAccountId, toSubAccountId) {
@@ -783,6 +824,7 @@ class ClearingHouse {
783
824
  toUser,
784
825
  userStats: this.getUserStatsAccountPublicKey(),
785
826
  state: await this.getStatePublicKey(),
827
+ spotMarketVault: this.getQuoteSpotMarketAccount().vault,
786
828
  },
787
829
  remainingAccounts,
788
830
  });
@@ -871,7 +913,7 @@ class ClearingHouse {
871
913
  }
872
914
  async addPerpLpShares(amount, marketIndex) {
873
915
  const { txSig, slot } = await this.txSender.send((0, utils_1.wrapInTx)(await this.getAddPerpLpSharesIx(amount, marketIndex)), [], this.opts);
874
- this.marketLastSlotCache.set(marketIndex, slot);
916
+ this.perpMarketLastSlotCache.set(marketIndex, slot);
875
917
  return txSig;
876
918
  }
877
919
  async getAddPerpLpSharesIx(amount, marketIndex) {
@@ -926,12 +968,12 @@ class ClearingHouse {
926
968
  fillTx.feePayer = userAccount.authority;
927
969
  const [signedMarketOrderTx, signedFillTx] = await this.provider.wallet.signAllTransactions([marketOrderTx, fillTx]);
928
970
  const { txSig, slot } = await this.txSender.send(signedMarketOrderTx, [], this.opts, true);
929
- this.marketLastSlotCache.set(orderParams.marketIndex, slot);
971
+ this.perpMarketLastSlotCache.set(orderParams.marketIndex, slot);
930
972
  return { txSig, signedFillTx };
931
973
  }
932
974
  async placePerpOrder(orderParams) {
933
975
  const { txSig, slot } = await this.txSender.send((0, utils_1.wrapInTx)(await this.getPlacePerpOrderIx(orderParams)), [], this.opts);
934
- this.marketLastSlotCache.set(orderParams.marketIndex, slot);
976
+ this.perpMarketLastSlotCache.set(orderParams.marketIndex, slot);
935
977
  return txSig;
936
978
  }
937
979
  getOrderParams(optionalOrderParams, marketType) {
@@ -1158,7 +1200,9 @@ class ClearingHouse {
1158
1200
  });
1159
1201
  }
1160
1202
  async placeSpotOrder(orderParams) {
1161
- const { txSig } = await this.txSender.send((0, utils_1.wrapInTx)(await this.getPlaceSpotOrderIx(orderParams)), [], this.opts);
1203
+ const { txSig, slot } = await this.txSender.send((0, utils_1.wrapInTx)(await this.getPlaceSpotOrderIx(orderParams)), [], this.opts);
1204
+ this.spotMarketLastSlotCache.set(orderParams.marketIndex, slot);
1205
+ this.spotMarketLastSlotCache.set(numericConstants_1.QUOTE_SPOT_MARKET_INDEX, slot);
1162
1206
  return txSig;
1163
1207
  }
1164
1208
  async getPlaceSpotOrderIx(orderParams) {
@@ -1167,7 +1211,10 @@ class ClearingHouse {
1167
1211
  const remainingAccounts = this.getRemainingAccounts({
1168
1212
  userAccounts: [this.getUserAccount()],
1169
1213
  useMarketLastSlotCache: true,
1170
- readableSpotMarketIndex: orderParams.marketIndex,
1214
+ readableSpotMarketIndexes: [
1215
+ orderParams.marketIndex,
1216
+ numericConstants_1.QUOTE_SPOT_MARKET_INDEX,
1217
+ ],
1171
1218
  });
1172
1219
  return await this.program.instruction.placeSpotOrder(orderParams, {
1173
1220
  accounts: {
@@ -1354,7 +1401,7 @@ class ClearingHouse {
1354
1401
  }
1355
1402
  async placeAndTakePerpOrder(orderParams, makerInfo, referrerInfo) {
1356
1403
  const { txSig, slot } = await this.txSender.send((0, utils_1.wrapInTx)(await this.getPlaceAndTakePerpOrderIx(orderParams, makerInfo, referrerInfo)), [], this.opts);
1357
- this.marketLastSlotCache.set(orderParams.marketIndex, slot);
1404
+ this.perpMarketLastSlotCache.set(orderParams.marketIndex, slot);
1358
1405
  return txSig;
1359
1406
  }
1360
1407
  async getPlaceAndTakePerpOrderIx(orderParams, makerInfo, referrerInfo) {
@@ -1408,7 +1455,7 @@ class ClearingHouse {
1408
1455
  }
1409
1456
  async placeAndMakePerpOrder(orderParams, takerInfo, referrerInfo) {
1410
1457
  const { txSig, slot } = await this.txSender.send((0, utils_1.wrapInTx)(await this.getPlaceAndMakePerpOrderIx(orderParams, takerInfo, referrerInfo)), [], this.opts);
1411
- this.marketLastSlotCache.set(orderParams.marketIndex, slot);
1458
+ this.perpMarketLastSlotCache.set(orderParams.marketIndex, slot);
1412
1459
  return txSig;
1413
1460
  }
1414
1461
  async getPlaceAndMakePerpOrderIx(orderParams, takerInfo, referrerInfo) {
@@ -1446,7 +1493,9 @@ class ClearingHouse {
1446
1493
  });
1447
1494
  }
1448
1495
  async placeAndTakeSpotOrder(orderParams, fulfillmentConfig, makerInfo, referrerInfo) {
1449
- const { txSig } = await this.txSender.send((0, utils_1.wrapInTx)(await this.getPlaceAndTakeSpotOrderIx(orderParams, fulfillmentConfig, makerInfo, referrerInfo)), [], this.opts);
1496
+ const { txSig, slot } = await this.txSender.send((0, utils_1.wrapInTx)(await this.getPlaceAndTakeSpotOrderIx(orderParams, fulfillmentConfig, makerInfo, referrerInfo)), [], this.opts);
1497
+ this.spotMarketLastSlotCache.set(orderParams.marketIndex, slot);
1498
+ this.spotMarketLastSlotCache.set(numericConstants_1.QUOTE_SPOT_MARKET_INDEX, slot);
1450
1499
  return txSig;
1451
1500
  }
1452
1501
  async getPlaceAndTakeSpotOrderIx(orderParams, fulfillmentConfig, makerInfo, referrerInfo) {
@@ -1504,11 +1553,13 @@ class ClearingHouse {
1504
1553
  remainingAccounts,
1505
1554
  });
1506
1555
  }
1507
- async placeAndMakeSpotOrder(orderParams, takerInfo, referrerInfo) {
1508
- const { txSig } = await this.txSender.send((0, utils_1.wrapInTx)(await this.getPlaceAndMakeSpotOrderIx(orderParams, takerInfo, referrerInfo)), [], this.opts);
1556
+ async placeAndMakeSpotOrder(orderParams, takerInfo, fulfillmentConfig, referrerInfo) {
1557
+ const { txSig, slot } = await this.txSender.send((0, utils_1.wrapInTx)(await this.getPlaceAndMakeSpotOrderIx(orderParams, takerInfo, fulfillmentConfig, referrerInfo)), [], this.opts);
1558
+ this.spotMarketLastSlotCache.set(orderParams.marketIndex, slot);
1559
+ this.spotMarketLastSlotCache.set(numericConstants_1.QUOTE_SPOT_MARKET_INDEX, slot);
1509
1560
  return txSig;
1510
1561
  }
1511
- async getPlaceAndMakeSpotOrderIx(orderParams, takerInfo, referrerInfo) {
1562
+ async getPlaceAndMakeSpotOrderIx(orderParams, takerInfo, fulfillmentConfig, referrerInfo) {
1512
1563
  orderParams = this.getOrderParams(orderParams, types_1.MarketType.SPOT);
1513
1564
  const userStatsPublicKey = this.getUserStatsAccountPublicKey();
1514
1565
  const userAccountPublicKey = await this.getUserAccountPublicKey();
@@ -1532,8 +1583,11 @@ class ClearingHouse {
1532
1583
  isSigner: false,
1533
1584
  });
1534
1585
  }
1586
+ if (fulfillmentConfig) {
1587
+ this.addSerumRemainingAccounts(orderParams.marketIndex, remainingAccounts, fulfillmentConfig);
1588
+ }
1535
1589
  const takerOrderId = takerInfo.order.orderId;
1536
- return await this.program.instruction.placeAndMakeSpotOrder(orderParams, takerOrderId, {
1590
+ return await this.program.instruction.placeAndMakeSpotOrder(orderParams, takerOrderId, fulfillmentConfig ? fulfillmentConfig.fulfillmentType : null, {
1537
1591
  accounts: {
1538
1592
  state: await this.getStatePublicKey(),
1539
1593
  user: userAccountPublicKey,
@@ -1564,6 +1618,53 @@ class ClearingHouse {
1564
1618
  price: limitPrice,
1565
1619
  });
1566
1620
  }
1621
+ /**
1622
+ * Modifies an open order by closing it and replacing it with a new order.
1623
+ * @param orderId: The open order to modify
1624
+ * @param newBaseAmount: The new base amount for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
1625
+ * @param newLimitPice: The new limit price for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
1626
+ * @param newOraclePriceOffset: The new oracle price offset for the order. One of [newBaseAmount|newLimitPrice|newOraclePriceOffset] must be provided.
1627
+ * @returns
1628
+ */
1629
+ async modifyPerpOrder(orderId, newBaseAmount, newLimitPrice, newOraclePriceOffset) {
1630
+ if (!newBaseAmount && !newLimitPrice && !newOraclePriceOffset) {
1631
+ throw new Error(`Must provide newBaseAmount or newLimitPrice or newOraclePriceOffset to modify order`);
1632
+ }
1633
+ const openOrder = this.getUser().getOrder(orderId);
1634
+ if (!openOrder) {
1635
+ throw new Error(`No open order with id ${orderId.toString()}`);
1636
+ }
1637
+ const cancelOrderIx = await this.getCancelOrderIx(orderId);
1638
+ const newOrderParams = {
1639
+ orderType: openOrder.orderType,
1640
+ marketType: openOrder.marketType,
1641
+ direction: openOrder.direction,
1642
+ baseAssetAmount: newBaseAmount || openOrder.baseAssetAmount,
1643
+ price: newLimitPrice || openOrder.price,
1644
+ marketIndex: openOrder.marketIndex,
1645
+ reduceOnly: openOrder.reduceOnly,
1646
+ postOnly: openOrder.postOnly,
1647
+ immediateOrCancel: openOrder.immediateOrCancel,
1648
+ triggerPrice: openOrder.triggerPrice,
1649
+ triggerCondition: openOrder.triggerCondition,
1650
+ oraclePriceOffset: newOraclePriceOffset || openOrder.oraclePriceOffset,
1651
+ auctionDuration: openOrder.auctionDuration,
1652
+ maxTs: openOrder.maxTs,
1653
+ auctionStartPrice: openOrder.auctionStartPrice,
1654
+ auctionEndPrice: openOrder.auctionEndPrice,
1655
+ };
1656
+ const placeOrderIx = await this.getPlacePerpOrderIx(newOrderParams);
1657
+ const tx = new web3_js_1.Transaction();
1658
+ tx.add(web3_js_1.ComputeBudgetProgram.requestUnits({
1659
+ units: 1000000,
1660
+ additionalFee: 0,
1661
+ }));
1662
+ tx.add(cancelOrderIx);
1663
+ tx.add(placeOrderIx);
1664
+ const { txSig, slot } = await this.txSender.send(tx, [], this.opts);
1665
+ this.perpMarketLastSlotCache.set(newOrderParams.marketIndex, slot);
1666
+ return txSig;
1667
+ }
1567
1668
  async settlePNLs(users, marketIndex) {
1568
1669
  const ixs = [];
1569
1670
  for (const { settleeUserAccountPublicKey, settleeUserAccount } of users) {
@@ -1593,16 +1694,17 @@ class ClearingHouse {
1593
1694
  state: await this.getStatePublicKey(),
1594
1695
  authority: this.wallet.publicKey,
1595
1696
  user: settleeUserAccountPublicKey,
1697
+ spotMarketVault: this.getQuoteSpotMarketAccount().vault,
1596
1698
  },
1597
1699
  remainingAccounts: remainingAccounts,
1598
1700
  });
1599
1701
  }
1600
- async liquidatePerp(userAccountPublicKey, userAccount, marketIndex, maxBaseAssetAmount) {
1601
- const { txSig, slot } = await this.txSender.send((0, utils_1.wrapInTx)(await this.getLiquidatePerpIx(userAccountPublicKey, userAccount, marketIndex, maxBaseAssetAmount)), [], this.opts);
1602
- this.marketLastSlotCache.set(marketIndex, slot);
1702
+ async liquidatePerp(userAccountPublicKey, userAccount, marketIndex, maxBaseAssetAmount, limitPrice) {
1703
+ const { txSig, slot } = await this.txSender.send((0, utils_1.wrapInTx)(await this.getLiquidatePerpIx(userAccountPublicKey, userAccount, marketIndex, maxBaseAssetAmount, limitPrice)), [], this.opts);
1704
+ this.perpMarketLastSlotCache.set(marketIndex, slot);
1603
1705
  return txSig;
1604
1706
  }
1605
- async getLiquidatePerpIx(userAccountPublicKey, userAccount, marketIndex, maxBaseAssetAmount) {
1707
+ async getLiquidatePerpIx(userAccountPublicKey, userAccount, marketIndex, maxBaseAssetAmount, limitPrice) {
1606
1708
  const userStatsPublicKey = (0, pda_1.getUserStatsAccountPublicKey)(this.program.programId, userAccount.authority);
1607
1709
  const liquidatorPublicKey = await this.getUserAccountPublicKey();
1608
1710
  const liquidatorStatsPublicKey = this.getUserStatsAccountPublicKey();
@@ -1611,7 +1713,7 @@ class ClearingHouse {
1611
1713
  useMarketLastSlotCache: true,
1612
1714
  writablePerpMarketIndexes: [marketIndex],
1613
1715
  });
1614
- return await this.program.instruction.liquidatePerp(marketIndex, maxBaseAssetAmount, {
1716
+ return await this.program.instruction.liquidatePerp(marketIndex, maxBaseAssetAmount, limitPrice !== null && limitPrice !== void 0 ? limitPrice : null, {
1615
1717
  accounts: {
1616
1718
  state: await this.getStatePublicKey(),
1617
1719
  authority: this.wallet.publicKey,
@@ -1624,7 +1726,9 @@ class ClearingHouse {
1624
1726
  });
1625
1727
  }
1626
1728
  async liquidateSpot(userAccountPublicKey, userAccount, assetMarketIndex, liabilityMarketIndex, maxLiabilityTransfer) {
1627
- const { txSig } = await this.txSender.send((0, utils_1.wrapInTx)(await this.getLiquidateSpotIx(userAccountPublicKey, userAccount, assetMarketIndex, liabilityMarketIndex, maxLiabilityTransfer)), [], this.opts);
1729
+ const { txSig, slot } = await this.txSender.send((0, utils_1.wrapInTx)(await this.getLiquidateSpotIx(userAccountPublicKey, userAccount, assetMarketIndex, liabilityMarketIndex, maxLiabilityTransfer)), [], this.opts);
1730
+ this.spotMarketLastSlotCache.set(assetMarketIndex, slot);
1731
+ this.spotMarketLastSlotCache.set(liabilityMarketIndex, slot);
1628
1732
  return txSig;
1629
1733
  }
1630
1734
  async getLiquidateSpotIx(userAccountPublicKey, userAccount, assetMarketIndex, liabilityMarketIndex, maxLiabilityTransfer) {
@@ -1650,7 +1754,8 @@ class ClearingHouse {
1650
1754
  }
1651
1755
  async liquidateBorrowForPerpPnl(userAccountPublicKey, userAccount, perpMarketIndex, liabilityMarketIndex, maxLiabilityTransfer) {
1652
1756
  const { txSig, slot } = await this.txSender.send((0, utils_1.wrapInTx)(await this.getLiquidateBorrowForPerpPnlIx(userAccountPublicKey, userAccount, perpMarketIndex, liabilityMarketIndex, maxLiabilityTransfer)), [], this.opts);
1653
- this.marketLastSlotCache.set(perpMarketIndex, slot);
1757
+ this.perpMarketLastSlotCache.set(perpMarketIndex, slot);
1758
+ this.spotMarketLastSlotCache.set(liabilityMarketIndex, slot);
1654
1759
  return txSig;
1655
1760
  }
1656
1761
  async getLiquidateBorrowForPerpPnlIx(userAccountPublicKey, userAccount, perpMarketIndex, liabilityMarketIndex, maxLiabilityTransfer) {
@@ -1676,7 +1781,8 @@ class ClearingHouse {
1676
1781
  }
1677
1782
  async liquidatePerpPnlForDeposit(userAccountPublicKey, userAccount, perpMarketIndex, assetMarketIndex, maxPnlTransfer) {
1678
1783
  const { txSig, slot } = await this.txSender.send((0, utils_1.wrapInTx)(await this.getLiquidatePerpPnlForDepositIx(userAccountPublicKey, userAccount, perpMarketIndex, assetMarketIndex, maxPnlTransfer)), [], this.opts);
1679
- this.marketLastSlotCache.set(perpMarketIndex, slot);
1784
+ this.perpMarketLastSlotCache.set(perpMarketIndex, slot);
1785
+ this.spotMarketLastSlotCache.set(assetMarketIndex, slot);
1680
1786
  return txSig;
1681
1787
  }
1682
1788
  async getLiquidatePerpPnlForDepositIx(userAccountPublicKey, userAccount, perpMarketIndex, assetMarketIndex, maxPnlTransfer) {
@@ -71,6 +71,7 @@ class ClearingHouseUser {
71
71
  marketIndex,
72
72
  quoteAssetAmount: numericConstants_1.ZERO,
73
73
  quoteEntryAmount: numericConstants_1.ZERO,
74
+ quoteBreakEvenAmount: numericConstants_1.ZERO,
74
75
  openOrders: 0,
75
76
  openBids: numericConstants_1.ZERO,
76
77
  openAsks: numericConstants_1.ZERO,
@@ -443,30 +444,9 @@ class ClearingHouseUser {
443
444
  perpPosition.baseAssetAmount =
444
445
  settledPosition.baseAssetAmount.add(dustBaa);
445
446
  perpPosition.quoteAssetAmount = settledPosition.quoteAssetAmount;
446
- // open orders
447
- let openAsks;
448
- if (market.amm.maxBaseAssetReserve > market.amm.baseAssetReserve) {
449
- openAsks = market.amm.maxBaseAssetReserve
450
- .sub(market.amm.baseAssetReserve)
451
- .mul(perpPosition.lpShares)
452
- .div(market.amm.sqrtK)
453
- .mul(new _1.BN(-1));
454
- }
455
- else {
456
- openAsks = numericConstants_1.ZERO;
457
- }
458
- let openBids;
459
- if (market.amm.minBaseAssetReserve < market.amm.baseAssetReserve) {
460
- openBids = market.amm.baseAssetReserve
461
- .sub(market.amm.minBaseAssetReserve)
462
- .mul(perpPosition.lpShares)
463
- .div(market.amm.sqrtK);
464
- }
465
- else {
466
- openBids = numericConstants_1.ZERO;
467
- }
468
- perpPosition.openAsks = perpPosition.openAsks.add(openAsks);
469
- perpPosition.openBids = perpPosition.openBids.add(openBids);
447
+ const [totalOpenBids, totalOpenAsks] = this.getPerpBidAsks(market.marketIndex);
448
+ perpPosition.openAsks = totalOpenAsks;
449
+ perpPosition.openBids = totalOpenBids;
470
450
  }
471
451
  let valuationPrice = this.getOracleDataForPerpMarket(market.marketIndex).price;
472
452
  if ((0, types_1.isVariant)(market.status, 'settlement')) {
@@ -673,6 +653,7 @@ class ClearingHouseUser {
673
653
  remainderBaseAssetAmount: 0,
674
654
  quoteAssetAmount: new _1.BN(0),
675
655
  lastCumulativeFundingRate: numericConstants_1.ZERO,
656
+ quoteBreakEvenAmount: new _1.BN(0),
676
657
  quoteEntryAmount: new _1.BN(0),
677
658
  openOrders: 0,
678
659
  openBids: new _1.BN(0),
@@ -903,7 +884,7 @@ class ClearingHouseUser {
903
884
  const precisionIncrease = numericConstants_1.TEN.pow(new _1.BN(spotMarket.decimals - 6));
904
885
  const amountWithdrawable = freeCollateral
905
886
  .mul(numericConstants_1.MARGIN_PRECISION)
906
- .div(spotMarket.initialAssetWeight)
887
+ .div(new _1.BN(spotMarket.initialAssetWeight))
907
888
  .mul(numericConstants_1.PRICE_PRECISION)
908
889
  .div(oracleData.price)
909
890
  .mul(precisionIncrease);
@@ -923,7 +904,7 @@ class ClearingHouseUser {
923
904
  : freeCollateral;
924
905
  const maxLiabilityAllowed = freeCollatAfterWithdraw
925
906
  .mul(numericConstants_1.MARGIN_PRECISION)
926
- .div(spotMarket.initialLiabilityWeight)
907
+ .div(new _1.BN(spotMarket.initialLiabilityWeight))
927
908
  .mul(numericConstants_1.PRICE_PRECISION)
928
909
  .div(oracleData.price)
929
910
  .mul(precisionIncrease);
package/lib/config.js CHANGED
@@ -7,7 +7,7 @@ exports.configs = {
7
7
  devnet: {
8
8
  ENV: 'devnet',
9
9
  PYTH_ORACLE_MAPPING_ADDRESS: 'BmA9Z6FjioHJPpjT39QazZyhDRUdZy2ezwx4GiDdE2u2',
10
- CLEARING_HOUSE_PROGRAM_ID: 'BJG3o4CURrokB7huNyiHptYepAtHXj4YfefJiPgzUULV',
10
+ CLEARING_HOUSE_PROGRAM_ID: 'jAEeKs9twxAJmXZHqS2p459xW7FMDjoyvuqthRo9qGS',
11
11
  USDC_MINT_ADDRESS: '8zGuJQqwhZafTah7Uc7Z4tXRnguqkn5KLFAP8oV6PHe2',
12
12
  SERUM_V3: 'DESVgJVGajEgKGXhb6XmqDHGz3VjdgP7rEVESBgxmroY',
13
13
  V2_ALPHA_TICKET_MINT_ADDRESS: 'DeEiGWfCMP9psnLGkxGrBBMEAW5Jv8bBGMN8DCtFRCyB',
@@ -5,6 +5,7 @@ export declare class BigNum {
5
5
  precision: BN;
6
6
  static delim: string;
7
7
  static spacer: string;
8
+ static setLocale(locale: string): void;
8
9
  constructor(val: BN | number | string, precisionVal?: BN | number | string);
9
10
  private bigNumFromParam;
10
11
  add(bn: BigNum): BigNum;
@@ -10,6 +10,10 @@ class BigNum {
10
10
  this.val = new anchor_1.BN(val);
11
11
  this.precision = new anchor_1.BN(precisionVal);
12
12
  }
13
+ static setLocale(locale) {
14
+ BigNum.delim = (1.1).toLocaleString(locale).slice(1, 2) || '.';
15
+ BigNum.spacer = (1000).toLocaleString(locale).slice(1, 2) || ',';
16
+ }
13
17
  bigNumFromParam(bn) {
14
18
  return anchor_1.BN.isBN(bn) ? BigNum.from(bn) : bn;
15
19
  }
@@ -381,7 +385,21 @@ class BigNum {
381
385
  * @returns
382
386
  */
383
387
  toNum() {
384
- return parseFloat(this.print());
388
+ let printedValue = this.print();
389
+ // Must convert any non-US delimiters and spacers to US format before using parseFloat
390
+ if (BigNum.delim !== '.' || BigNum.spacer !== ',') {
391
+ printedValue = printedValue
392
+ .split('')
393
+ .map((char) => {
394
+ if (char === BigNum.delim)
395
+ return '.';
396
+ if (char === BigNum.spacer)
397
+ return ',';
398
+ return char;
399
+ })
400
+ .join('');
401
+ }
402
+ return parseFloat(printedValue);
385
403
  }
386
404
  static fromJSON(json) {
387
405
  return BigNum.from(new anchor_1.BN(json.val), new anchor_1.BN(json.precision));
@@ -407,10 +425,14 @@ class BigNum {
407
425
  // Handle empty number edge cases
408
426
  if (!val)
409
427
  return BigNum.from(numericConstants_1.ZERO, precisionShift);
410
- if (!val.replace(BigNum.delim, ''))
428
+ if (!val.replace(BigNum.delim, '')) {
411
429
  return BigNum.from(numericConstants_1.ZERO, precisionShift);
412
- const [leftSide, rightSide] = val.split(BigNum.delim);
413
- const rawBn = new anchor_1.BN(`${leftSide !== null && leftSide !== void 0 ? leftSide : ''}${rightSide !== null && rightSide !== void 0 ? rightSide : ''}`);
430
+ }
431
+ const sides = val.split(BigNum.delim);
432
+ const rightSide = sides[1];
433
+ const leftSide = sides[0].replace(/\D/g, '');
434
+ const bnInput = `${leftSide !== null && leftSide !== void 0 ? leftSide : ''}${rightSide !== null && rightSide !== void 0 ? rightSide : ''}`;
435
+ const rawBn = new anchor_1.BN(bnInput);
414
436
  const rightSideLength = (_a = rightSide === null || rightSide === void 0 ? void 0 : rightSide.length) !== null && _a !== void 0 ? _a : 0;
415
437
  const totalShift = precisionShift
416
438
  ? precisionShift.sub(new anchor_1.BN(rightSideLength))