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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/README.md +1 -0
  2. package/VERSION +1 -1
  3. package/bun.lockb +0 -0
  4. package/lib/accounts/pollingDriftClientAccountSubscriber.d.ts +5 -3
  5. package/lib/accounts/pollingDriftClientAccountSubscriber.js +24 -1
  6. package/lib/accounts/types.d.ts +5 -8
  7. package/lib/accounts/types.js +7 -1
  8. package/lib/accounts/utils.d.ts +7 -0
  9. package/lib/accounts/utils.js +33 -1
  10. package/lib/accounts/webSocketAccountSubscriber.d.ts +1 -1
  11. package/lib/accounts/webSocketDriftClientAccountSubscriber.d.ts +8 -7
  12. package/lib/accounts/webSocketDriftClientAccountSubscriber.js +24 -1
  13. package/lib/accounts/webSocketProgramAccountSubscriber.d.ts +1 -1
  14. package/lib/config.d.ts +5 -1
  15. package/lib/config.js +9 -1
  16. package/lib/constants/perpMarkets.js +21 -0
  17. package/lib/constants/spotMarkets.js +12 -1
  18. package/lib/driftClient.d.ts +44 -9
  19. package/lib/driftClient.js +181 -61
  20. package/lib/driftClientConfig.d.ts +2 -6
  21. package/lib/events/eventSubscriber.js +9 -8
  22. package/lib/events/types.js +1 -5
  23. package/lib/idl/drift.json +169 -1
  24. package/lib/index.d.ts +1 -0
  25. package/lib/index.js +1 -0
  26. package/lib/math/margin.d.ts +16 -1
  27. package/lib/math/margin.js +67 -1
  28. package/lib/orderParams.js +8 -8
  29. package/lib/orderSubscriber/OrderSubscriber.d.ts +1 -2
  30. package/lib/orderSubscriber/OrderSubscriber.js +4 -19
  31. package/lib/orderSubscriber/types.d.ts +0 -9
  32. package/lib/tokenFaucet.js +2 -1
  33. package/lib/tx/baseTxSender.js +2 -2
  34. package/lib/tx/fastSingleTxSender.js +2 -2
  35. package/lib/tx/forwardOnlyTxSender.js +2 -2
  36. package/lib/tx/retryTxSender.js +2 -2
  37. package/lib/tx/txHandler.js +10 -7
  38. package/lib/tx/whileValidTxSender.d.ts +2 -4
  39. package/lib/tx/whileValidTxSender.js +16 -17
  40. package/lib/types.d.ts +21 -1
  41. package/lib/types.js +6 -1
  42. package/lib/user.d.ts +4 -1
  43. package/lib/user.js +13 -13
  44. package/lib/userConfig.d.ts +1 -6
  45. package/lib/userMap/userMap.js +0 -14
  46. package/lib/userMap/userMapConfig.d.ts +0 -7
  47. package/lib/userStatsConfig.d.ts +0 -6
  48. package/lib/util/TransactionConfirmationManager.d.ts +14 -0
  49. package/lib/util/TransactionConfirmationManager.js +96 -0
  50. package/package.json +4 -5
  51. package/src/accounts/pollingDriftClientAccountSubscriber.ts +41 -5
  52. package/src/accounts/types.ts +6 -9
  53. package/src/accounts/utils.ts +42 -0
  54. package/src/accounts/webSocketAccountSubscriber.ts +1 -1
  55. package/src/accounts/webSocketDriftClientAccountSubscriber.ts +43 -8
  56. package/src/accounts/webSocketProgramAccountSubscriber.ts +1 -1
  57. package/src/config.ts +15 -1
  58. package/src/constants/perpMarkets.ts +22 -0
  59. package/src/constants/spotMarkets.ts +14 -1
  60. package/src/driftClient.ts +423 -91
  61. package/src/driftClientConfig.ts +2 -7
  62. package/src/events/eventSubscriber.ts +18 -11
  63. package/src/events/types.ts +1 -5
  64. package/src/idl/drift.json +169 -1
  65. package/src/index.ts +1 -0
  66. package/src/math/margin.ts +137 -1
  67. package/src/orderParams.ts +20 -12
  68. package/src/orderSubscriber/OrderSubscriber.ts +1 -15
  69. package/src/orderSubscriber/types.ts +0 -10
  70. package/src/tokenFaucet.ts +2 -2
  71. package/src/tx/baseTxSender.ts +2 -2
  72. package/src/tx/fastSingleTxSender.ts +2 -2
  73. package/src/tx/forwardOnlyTxSender.ts +2 -2
  74. package/src/tx/retryTxSender.ts +2 -2
  75. package/src/tx/txHandler.ts +8 -2
  76. package/src/tx/whileValidTxSender.ts +23 -26
  77. package/src/types.ts +30 -1
  78. package/src/user.ts +35 -13
  79. package/src/userConfig.ts +1 -7
  80. package/src/userMap/userMap.ts +1 -17
  81. package/src/userMap/userMapConfig.ts +0 -8
  82. package/src/userStatsConfig.ts +0 -7
  83. package/src/util/TransactionConfirmationManager.ts +155 -0
  84. package/tests/ci/idl.ts +12 -3
  85. package/tests/ci/verifyConstants.ts +13 -0
  86. package/tests/tx/TransactionConfirmationManager.test.ts +286 -0
  87. package/lib/accounts/grpcAccountSubscriber.d.ts +0 -16
  88. package/lib/accounts/grpcAccountSubscriber.js +0 -155
  89. package/lib/accounts/grpcDriftClientAccountSubscriber.d.ts +0 -13
  90. package/lib/accounts/grpcDriftClientAccountSubscriber.js +0 -96
  91. package/lib/accounts/grpcInsuranceFundStakeAccountSubscriber.d.ts +0 -10
  92. package/lib/accounts/grpcInsuranceFundStakeAccountSubscriber.js +0 -30
  93. package/lib/accounts/grpcProgramAccountSubscriber.d.ts +0 -19
  94. package/lib/accounts/grpcProgramAccountSubscriber.js +0 -161
  95. package/lib/accounts/grpcUserAccountSubscriber.d.ts +0 -10
  96. package/lib/accounts/grpcUserAccountSubscriber.js +0 -28
  97. package/lib/accounts/grpcUserStatsAccountSubscriber.d.ts +0 -10
  98. package/lib/accounts/grpcUserStatsAccountSubscriber.js +0 -28
  99. package/lib/orderSubscriber/grpcSubscription.d.ts +0 -25
  100. package/lib/orderSubscriber/grpcSubscription.js +0 -68
  101. package/lib/userMap/grpcSubscription.d.ts +0 -26
  102. package/lib/userMap/grpcSubscription.js +0 -42
  103. package/src/accounts/grpcAccountSubscriber.ts +0 -158
  104. package/src/accounts/grpcDriftClientAccountSubscriber.ts +0 -196
  105. package/src/accounts/grpcInsuranceFundStakeAccountSubscriber.ts +0 -62
  106. package/src/accounts/grpcProgramAccountSubscriber.ts +0 -181
  107. package/src/accounts/grpcUserAccountSubscriber.ts +0 -48
  108. package/src/accounts/grpcUserStatsAccountSubscriber.ts +0 -51
  109. package/src/orderSubscriber/grpcSubscription.ts +0 -126
  110. package/src/userMap/grpcSubscription.ts +0 -83
@@ -4,11 +4,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.WhileValidTxSender = void 0;
7
- const anchor_1 = require("@coral-xyz/anchor");
7
+ const web3_js_1 = require("@solana/web3.js");
8
8
  const baseTxSender_1 = require("./baseTxSender");
9
9
  const bs58_1 = __importDefault(require("bs58"));
10
+ const config_1 = require("../config");
10
11
  const DEFAULT_RETRY = 2000;
11
- const VALID_BLOCK_HEIGHT_OFFSET = -150; // This is a bit of weirdness but the lastValidBlockHeight value returned from connection.getLatestBlockhash is always 300 blocks ahead of the current block, even though the transaction actually expires after 150 blocks. This accounts for that so that we can at least accuractely estimate the transaction expiry.
12
12
  class WhileValidTxSender extends baseTxSender_1.BaseTxSender {
13
13
  async checkAndSetUseBlockHeightOffset() {
14
14
  this.connection.getVersion().then((version) => {
@@ -29,7 +29,7 @@ class WhileValidTxSender extends baseTxSender_1.BaseTxSender {
29
29
  }
30
30
  });
31
31
  }
32
- constructor({ connection, wallet, opts = { ...anchor_1.AnchorProvider.defaultOptions(), maxRetries: 0 }, retrySleep = DEFAULT_RETRY, additionalConnections = new Array(), additionalTxSenderCallbacks = [], blockhashCommitment = 'finalized', txHandler, trackTxLandRate, txLandRateLookbackWindowMinutes, landRateToFeeFunc, }) {
32
+ constructor({ connection, wallet, opts = { ...config_1.DEFAULT_CONFIRMATION_OPTS, maxRetries: 0 }, retrySleep = DEFAULT_RETRY, additionalConnections = new Array(), additionalTxSenderCallbacks = [], txHandler, trackTxLandRate, txLandRateLookbackWindowMinutes, landRateToFeeFunc, }) {
33
33
  super({
34
34
  connection,
35
35
  wallet,
@@ -45,7 +45,6 @@ class WhileValidTxSender extends baseTxSender_1.BaseTxSender {
45
45
  this.untilValid = new Map();
46
46
  this.useBlockHeightOffset = true;
47
47
  this.retrySleep = retrySleep;
48
- this.blockhashCommitment = blockhashCommitment;
49
48
  this.checkAndSetUseBlockHeightOffset();
50
49
  }
51
50
  async sleep(reference) {
@@ -69,7 +68,7 @@ class WhileValidTxSender extends baseTxSender_1.BaseTxSender {
69
68
  latestBlockhash = tx.SIGNATURE_BLOCK_AND_EXPIRY;
70
69
  }
71
70
  // handle subclass-specific side effects
72
- const txSig = bs58_1.default.encode(((_a = signedTx.signatures[0]) === null || _a === void 0 ? void 0 : _a.signature) || signedTx.signatures[0]);
71
+ const txSig = bs58_1.default.encode((signedTx === null || signedTx === void 0 ? void 0 : signedTx.signature) || ((_a = signedTx.signatures[0]) === null || _a === void 0 ? void 0 : _a.signature));
73
72
  this.untilValid.set(txSig, latestBlockhash);
74
73
  return signedTx;
75
74
  }
@@ -104,11 +103,13 @@ class WhileValidTxSender extends baseTxSender_1.BaseTxSender {
104
103
  }
105
104
  const txSig = bs58_1.default.encode(signedTx.signatures[0]);
106
105
  this.untilValid.set(txSig, latestBlockhash);
106
+ console.debug(`preflight_commitment`, `sending_tx_with_preflight_commitment::${opts === null || opts === void 0 ? void 0 : opts.preflightCommitment}`);
107
107
  return this.sendRawTransaction(signedTx.serialize(), opts);
108
108
  }
109
109
  async sendRawTransaction(rawTransaction, opts) {
110
- var _a, _b;
110
+ var _a, _b, _c;
111
111
  const startTime = this.getTimestamp();
112
+ console.debug(`preflight_commitment`, `sending_tx_with_preflight_commitment::${opts === null || opts === void 0 ? void 0 : opts.preflightCommitment}`);
112
113
  const txid = await this.connection.sendRawTransaction(rawTransaction, opts);
113
114
  (_a = this.txSigCache) === null || _a === void 0 ? void 0 : _a.set(txid, false);
114
115
  this.sendToAdditionalConnections(rawTransaction, opts);
@@ -138,19 +139,17 @@ class WhileValidTxSender extends baseTxSender_1.BaseTxSender {
138
139
  })();
139
140
  let slot;
140
141
  try {
141
- const { blockhash, lastValidBlockHeight } = this.untilValid.get(txid);
142
- const result = await this.connection.confirmTransaction({
143
- signature: txid,
144
- blockhash,
145
- lastValidBlockHeight: this.useBlockHeightOffset
146
- ? lastValidBlockHeight + VALID_BLOCK_HEIGHT_OFFSET
147
- : lastValidBlockHeight,
148
- }, opts === null || opts === void 0 ? void 0 : opts.commitment);
149
- if (!result) {
150
- throw new Error(`Couldn't get signature status for txid: ${txid}`);
151
- }
142
+ const result = await this.confirmTransaction(txid, opts.commitment);
152
143
  (_b = this.txSigCache) === null || _b === void 0 ? void 0 : _b.set(txid, true);
153
144
  await this.checkConfirmationResultForError(txid, result.value);
145
+ if ((_c = result === null || result === void 0 ? void 0 : result.value) === null || _c === void 0 ? void 0 : _c.err) {
146
+ // Fallback error handling if there's a problem reporting the error in checkConfirmationResultForError
147
+ throw new web3_js_1.SendTransactionError({
148
+ action: 'send',
149
+ signature: txid,
150
+ transactionMessage: `Transaction Failed`,
151
+ });
152
+ }
154
153
  slot = result.context.slot;
155
154
  // eslint-disable-next-line no-useless-catch
156
155
  }
package/lib/types.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /// <reference types="bn.js" />
2
- import { PublicKey, Transaction, VersionedTransaction } from '@solana/web3.js';
2
+ import { Keypair, PublicKey, Transaction, VersionedTransaction } from '@solana/web3.js';
3
3
  import { BN } from '.';
4
4
  export type MappedRecord<A extends Record<string, unknown>, B> = {
5
5
  [K in keyof A]: B;
@@ -1096,6 +1096,20 @@ export declare class ModifyOrderPolicy {
1096
1096
  };
1097
1097
  }
1098
1098
  export declare const DefaultOrderParams: OrderParams;
1099
+ export type SwiftServerMessage = {
1100
+ slot: BN;
1101
+ swiftOrderSignature: Uint8Array;
1102
+ };
1103
+ export type SwiftOrderParamsMessage = {
1104
+ swiftOrderParams: OptionalOrderParams;
1105
+ expectedOrderId: number;
1106
+ takeProfitOrderParams: SwiftTriggerOrderParams | null;
1107
+ stopLossOrderParams: SwiftTriggerOrderParams | null;
1108
+ };
1109
+ export type SwiftTriggerOrderParams = {
1110
+ triggerPrice: BN;
1111
+ baseAssetAmount: BN;
1112
+ };
1099
1113
  export type MakerInfo = {
1100
1114
  maker: PublicKey;
1101
1115
  makerStats: PublicKey;
@@ -1112,6 +1126,10 @@ export type ReferrerInfo = {
1112
1126
  referrer: PublicKey;
1113
1127
  referrerStats: PublicKey;
1114
1128
  };
1129
+ export declare enum PlaceAndTakeOrderSuccessCondition {
1130
+ PartialFill = 1,
1131
+ FullFill = 2
1132
+ }
1115
1133
  type ExactType<T> = Pick<T, keyof T>;
1116
1134
  export type BaseTxParams = ExactType<{
1117
1135
  computeUnits?: number;
@@ -1137,11 +1155,13 @@ export interface IWallet {
1137
1155
  signTransaction(tx: Transaction): Promise<Transaction>;
1138
1156
  signAllTransactions(txs: Transaction[]): Promise<Transaction[]>;
1139
1157
  publicKey: PublicKey;
1158
+ payer?: Keypair;
1140
1159
  }
1141
1160
  export interface IVersionedWallet {
1142
1161
  signVersionedTransaction(tx: VersionedTransaction): Promise<VersionedTransaction>;
1143
1162
  signAllVersionedTransactions(txs: VersionedTransaction[]): Promise<VersionedTransaction[]>;
1144
1163
  publicKey: PublicKey;
1164
+ payer?: Keypair;
1145
1165
  }
1146
1166
  export type FeeStructure = {
1147
1167
  feeTiers: FeeTier[];
package/lib/types.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.SwapReduceOnly = exports.DefaultOrderParams = exports.ModifyOrderPolicy = exports.PostOnlyParams = exports.LiquidationType = exports.LPAction = exports.TradeSide = exports.getVariant = exports.isOneOfVariant = exports.isVariant = exports.SettlePnlMode = exports.StakeAction = exports.SpotFulfillmentConfigStatus = exports.SettlePnlExplanation = exports.DepositExplanation = exports.SpotFulfillmentStatus = exports.SpotFulfillmentType = exports.OrderTriggerCondition = exports.OrderActionExplanation = exports.OrderAction = exports.OrderStatus = exports.MarketType = exports.OrderType = exports.OracleSource = exports.DepositDirection = exports.PositionDirection = exports.SpotBalanceType = exports.SwapDirection = exports.AssetTier = exports.ContractTier = exports.ContractType = exports.UserStatus = exports.InsuranceFundOperation = exports.SpotOperation = exports.PerpOperation = exports.MarketStatus = exports.ExchangeStatus = void 0;
3
+ exports.SwapReduceOnly = exports.PlaceAndTakeOrderSuccessCondition = exports.DefaultOrderParams = exports.ModifyOrderPolicy = exports.PostOnlyParams = exports.LiquidationType = exports.LPAction = exports.TradeSide = exports.getVariant = exports.isOneOfVariant = exports.isVariant = exports.SettlePnlMode = exports.StakeAction = exports.SpotFulfillmentConfigStatus = exports.SettlePnlExplanation = exports.DepositExplanation = exports.SpotFulfillmentStatus = exports.SpotFulfillmentType = exports.OrderTriggerCondition = exports.OrderActionExplanation = exports.OrderAction = exports.OrderStatus = exports.MarketType = exports.OrderType = exports.OracleSource = exports.DepositDirection = exports.PositionDirection = exports.SpotBalanceType = exports.SwapDirection = exports.AssetTier = exports.ContractTier = exports.ContractType = exports.UserStatus = exports.InsuranceFundOperation = exports.SpotOperation = exports.PerpOperation = exports.MarketStatus = exports.ExchangeStatus = void 0;
4
4
  const _1 = require(".");
5
5
  // # Utility Types / Enums / Constants
6
6
  var ExchangeStatus;
@@ -325,6 +325,11 @@ exports.DefaultOrderParams = {
325
325
  auctionStartPrice: null,
326
326
  auctionEndPrice: null,
327
327
  };
328
+ var PlaceAndTakeOrderSuccessCondition;
329
+ (function (PlaceAndTakeOrderSuccessCondition) {
330
+ PlaceAndTakeOrderSuccessCondition[PlaceAndTakeOrderSuccessCondition["PartialFill"] = 1] = "PartialFill";
331
+ PlaceAndTakeOrderSuccessCondition[PlaceAndTakeOrderSuccessCondition["FullFill"] = 2] = "FullFill";
332
+ })(PlaceAndTakeOrderSuccessCondition = exports.PlaceAndTakeOrderSuccessCondition || (exports.PlaceAndTakeOrderSuccessCondition = {}));
328
333
  class SwapReduceOnly {
329
334
  }
330
335
  exports.SwapReduceOnly = SwapReduceOnly;
package/lib/user.d.ts CHANGED
@@ -265,9 +265,10 @@ export declare class User {
265
265
  * @param estimatedEntryPrice
266
266
  * @param marginCategory // allow Initial to be passed in if we are trying to calculate price for DLP de-risking
267
267
  * @param includeOpenOrders
268
+ * @param offsetCollateral // allows calculating the liquidation price after this offset collateral is added to the user's account (e.g. : what will the liquidation price be for this position AFTER I deposit $x worth of collateral)
268
269
  * @returns Precision : PRICE_PRECISION
269
270
  */
270
- liquidationPrice(marketIndex: number, positionBaseSizeChange?: BN, estimatedEntryPrice?: BN, marginCategory?: MarginCategory, includeOpenOrders?: boolean): BN;
271
+ liquidationPrice(marketIndex: number, positionBaseSizeChange?: BN, estimatedEntryPrice?: BN, marginCategory?: MarginCategory, includeOpenOrders?: boolean, offsetCollateral?: BN): BN;
271
272
  calculateEntriesEffectOnFreeCollateral(market: PerpMarketAccount, oraclePrice: BN, perpPosition: PerpPosition, positionBaseSizeChange: BN, estimatedEntryPrice: BN, includeOpenOrders: boolean): BN;
272
273
  calculateFreeCollateralDeltaForPerp(market: PerpMarketAccount, perpPosition: PerpPosition, positionBaseSizeChange: BN, oraclePrice: BN, marginCategory?: MarginCategory, includeOpenOrders?: boolean): BN | undefined;
273
274
  calculateFreeCollateralDeltaForSpot(market: SpotMarketAccount, signedTokenAmount: BN, marginCategory?: MarginCategory): BN;
@@ -278,6 +279,8 @@ export declare class User {
278
279
  * @returns : Precision PRICE_PRECISION
279
280
  */
280
281
  liquidationPriceAfterClose(positionMarketIndex: number, closeQuoteAmount: BN, estimatedEntryPrice?: BN): BN;
282
+ getMarginUSDCRequiredForTrade(targetMarketIndex: number, baseSize: BN): BN;
283
+ getCollateralDepositRequiredForTrade(targetMarketIndex: number, baseSize: BN, collateralIndex: number): BN;
281
284
  /**
282
285
  * Get the maximum trade size for a given market, taking into account the user's current leverage, positions, collateral, etc.
283
286
  *
package/lib/user.js CHANGED
@@ -15,7 +15,6 @@ const oracles_1 = require("./math/oracles");
15
15
  const tiers_1 = require("./math/tiers");
16
16
  const strictOraclePrice_1 = require("./oracles/strictOraclePrice");
17
17
  const fuel_1 = require("./math/fuel");
18
- const grpcUserAccountSubscriber_1 = require("./accounts/grpcUserAccountSubscriber");
19
18
  class User {
20
19
  get isSubscribed() {
21
20
  return this._isSubscribed && this.accountSubscriber.isSubscribed;
@@ -24,7 +23,7 @@ class User {
24
23
  this._isSubscribed = val;
25
24
  }
26
25
  constructor(config) {
27
- var _a, _b, _c, _d, _e, _f, _g, _h;
26
+ var _a, _b, _c, _d, _e;
28
27
  this._isSubscribed = false;
29
28
  this.driftClient = config.driftClient;
30
29
  this.userAccountPublicKey = config.userAccountPublicKey;
@@ -34,17 +33,11 @@ class User {
34
33
  else if (((_b = config.accountSubscription) === null || _b === void 0 ? void 0 : _b.type) === 'custom') {
35
34
  this.accountSubscriber = config.accountSubscription.userAccountSubscriber;
36
35
  }
37
- else if (((_c = config.accountSubscription) === null || _c === void 0 ? void 0 : _c.type) === 'grpc') {
38
- this.accountSubscriber = new grpcUserAccountSubscriber_1.grpcUserAccountSubscriber(config.accountSubscription.configs, config.driftClient.program, config.userAccountPublicKey, {
39
- resubTimeoutMs: (_d = config.accountSubscription) === null || _d === void 0 ? void 0 : _d.resubTimeoutMs,
40
- logResubMessages: (_e = config.accountSubscription) === null || _e === void 0 ? void 0 : _e.logResubMessages,
41
- });
42
- }
43
36
  else {
44
37
  this.accountSubscriber = new webSocketUserAccountSubscriber_1.WebSocketUserAccountSubscriber(config.driftClient.program, config.userAccountPublicKey, {
45
- resubTimeoutMs: (_f = config.accountSubscription) === null || _f === void 0 ? void 0 : _f.resubTimeoutMs,
46
- logResubMessages: (_g = config.accountSubscription) === null || _g === void 0 ? void 0 : _g.logResubMessages,
47
- }, (_h = config.accountSubscription) === null || _h === void 0 ? void 0 : _h.commitment);
38
+ resubTimeoutMs: (_c = config.accountSubscription) === null || _c === void 0 ? void 0 : _c.resubTimeoutMs,
39
+ logResubMessages: (_d = config.accountSubscription) === null || _d === void 0 ? void 0 : _d.logResubMessages,
40
+ }, (_e = config.accountSubscription) === null || _e === void 0 ? void 0 : _e.commitment);
48
41
  }
49
42
  this.eventEmitter = this.accountSubscriber.eventEmitter;
50
43
  }
@@ -1209,12 +1202,13 @@ class User {
1209
1202
  * @param estimatedEntryPrice
1210
1203
  * @param marginCategory // allow Initial to be passed in if we are trying to calculate price for DLP de-risking
1211
1204
  * @param includeOpenOrders
1205
+ * @param offsetCollateral // allows calculating the liquidation price after this offset collateral is added to the user's account (e.g. : what will the liquidation price be for this position AFTER I deposit $x worth of collateral)
1212
1206
  * @returns Precision : PRICE_PRECISION
1213
1207
  */
1214
- liquidationPrice(marketIndex, positionBaseSizeChange = numericConstants_1.ZERO, estimatedEntryPrice = numericConstants_1.ZERO, marginCategory = 'Maintenance', includeOpenOrders = false) {
1208
+ liquidationPrice(marketIndex, positionBaseSizeChange = numericConstants_1.ZERO, estimatedEntryPrice = numericConstants_1.ZERO, marginCategory = 'Maintenance', includeOpenOrders = false, offsetCollateral = numericConstants_1.ZERO) {
1215
1209
  const totalCollateral = this.getTotalCollateral(marginCategory);
1216
1210
  const marginRequirement = this.getMarginRequirement(marginCategory, undefined, false, includeOpenOrders);
1217
- let freeCollateral = _1.BN.max(numericConstants_1.ZERO, totalCollateral.sub(marginRequirement));
1211
+ let freeCollateral = _1.BN.max(numericConstants_1.ZERO, totalCollateral.sub(marginRequirement)).add(offsetCollateral);
1218
1212
  const oracle = this.driftClient.getPerpMarketAccount(marketIndex).amm.oracle;
1219
1213
  const oraclePrice = this.driftClient.getOracleDataForPerpMarket(marketIndex).price;
1220
1214
  const market = this.driftClient.getPerpMarketAccount(marketIndex);
@@ -1374,6 +1368,12 @@ class User {
1374
1368
  .neg();
1375
1369
  return this.liquidationPrice(positionMarketIndex, closeBaseAmount, estimatedEntryPrice);
1376
1370
  }
1371
+ getMarginUSDCRequiredForTrade(targetMarketIndex, baseSize) {
1372
+ return (0, margin_1.calculateMarginUSDCRequiredForTrade)(this.driftClient, targetMarketIndex, baseSize, this.getUserAccount().maxMarginRatio);
1373
+ }
1374
+ getCollateralDepositRequiredForTrade(targetMarketIndex, baseSize, collateralIndex) {
1375
+ return (0, margin_1.calculateCollateralDepositRequiredForTrade)(this.driftClient, targetMarketIndex, baseSize, collateralIndex, this.getUserAccount().maxMarginRatio);
1376
+ }
1377
1377
  /**
1378
1378
  * Get the maximum trade size for a given market, taking into account the user's current leverage, positions, collateral, etc.
1379
1379
  *
@@ -1,7 +1,7 @@
1
1
  import { DriftClient } from './driftClient';
2
2
  import { Commitment, PublicKey } from '@solana/web3.js';
3
3
  import { BulkAccountLoader } from './accounts/bulkAccountLoader';
4
- import { GrpcConfigs, UserAccountSubscriber } from './accounts/types';
4
+ import { UserAccountSubscriber } from './accounts/types';
5
5
  export type UserConfig = {
6
6
  accountSubscription?: UserSubscriptionConfig;
7
7
  driftClient: DriftClient;
@@ -18,9 +18,4 @@ export type UserSubscriptionConfig = {
18
18
  } | {
19
19
  type: 'custom';
20
20
  userAccountSubscriber: UserAccountSubscriber;
21
- } | {
22
- type: 'grpc';
23
- resubTimeoutMs?: number;
24
- logResubMessages?: boolean;
25
- configs: GrpcConfigs;
26
21
  };
@@ -9,7 +9,6 @@ const memcmp_1 = require("../memcmp");
9
9
  const WebsocketSubscription_1 = require("./WebsocketSubscription");
10
10
  const PollingSubscription_1 = require("./PollingSubscription");
11
11
  const user_1 = require("../decode/user");
12
- const grpcSubscription_1 = require("./grpcSubscription");
13
12
  const MAX_USER_ACCOUNT_SIZE_BYTES = 4376;
14
13
  class UserMap {
15
14
  /**
@@ -53,19 +52,6 @@ class UserMap {
53
52
  skipInitialLoad: config.skipInitialLoad,
54
53
  });
55
54
  }
56
- else if (config.subscriptionConfig.type === 'grpc') {
57
- this.subscription = new grpcSubscription_1.grpcSubscription({
58
- configs: config.subscriptionConfig.configs,
59
- userMap: this,
60
- commitment: this.commitment,
61
- resubOpts: {
62
- resubTimeoutMs: config.subscriptionConfig.resubTimeoutMs,
63
- logResubMessages: config.subscriptionConfig.logResubMessages,
64
- },
65
- skipInitialLoad: config.skipInitialLoad,
66
- decodeFn,
67
- });
68
- }
69
55
  else {
70
56
  this.subscription = new WebsocketSubscription_1.WebsocketSubscription({
71
57
  userMap: this,
@@ -1,6 +1,5 @@
1
1
  import { Commitment, Connection } from '@solana/web3.js';
2
2
  import { DriftClient } from '../driftClient';
3
- import { GrpcConfigs } from '../accounts/types';
4
3
  export type UserAccountFilterCriteria = {
5
4
  hasOpenOrders: boolean;
6
5
  };
@@ -18,12 +17,6 @@ export type UserMapConfig = {
18
17
  type: 'polling';
19
18
  frequency: number;
20
19
  commitment?: Commitment;
21
- } | {
22
- type: 'grpc';
23
- configs: GrpcConfigs;
24
- resubTimeoutMs?: number;
25
- logResubMessages?: boolean;
26
- commitment?: Commitment;
27
20
  } | {
28
21
  type: 'websocket';
29
22
  resubTimeoutMs?: number;
@@ -1,7 +1,6 @@
1
1
  import { DriftClient } from './driftClient';
2
2
  import { Commitment, PublicKey } from '@solana/web3.js';
3
3
  import { BulkAccountLoader } from './accounts/bulkAccountLoader';
4
- import { GrpcConfigs } from './accounts/types';
5
4
  export type UserStatsConfig = {
6
5
  accountSubscription?: UserStatsSubscriptionConfig;
7
6
  driftClient: DriftClient;
@@ -17,9 +16,4 @@ export type UserStatsSubscriptionConfig = {
17
16
  accountLoader: BulkAccountLoader;
18
17
  } | {
19
18
  type: 'custom';
20
- } | {
21
- type: 'grpc';
22
- resubTimeoutMs?: number;
23
- logResubMessages?: boolean;
24
- configs: GrpcConfigs;
25
19
  };
@@ -0,0 +1,14 @@
1
+ import { Connection, SignatureStatus, TransactionConfirmationStatus } from '@solana/web3.js';
2
+ /**
3
+ * Class to await for transaction confirmations in an optimised manner. It tracks a shared list of all pending transactions and fetches them in bulk in a shared RPC request whenever they have an "overlapping" polling interval. E.g. tx1 with an interval of 200ms and tx2 with an interval of 300ms (if sent at the same time) will be fetched together at at 600ms, 1200ms, 1800ms, etc.
4
+ */
5
+ export declare class TransactionConfirmationManager {
6
+ private connection;
7
+ private pendingConfirmations;
8
+ private intervalId;
9
+ constructor(connection: Connection);
10
+ confirmTransaction(txSig: string, desiredConfirmationStatus?: TransactionConfirmationStatus, timeout?: number, pollInterval?: number, searchTransactionHistory?: boolean): Promise<SignatureStatus>;
11
+ private startConfirmationLoop;
12
+ private checkPendingConfirmations;
13
+ private checkTransactionStatuses;
14
+ }
@@ -0,0 +1,96 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TransactionConfirmationManager = void 0;
4
+ const config_1 = require("../config");
5
+ const confirmationStatusValues = {
6
+ processed: 0,
7
+ confirmed: 1,
8
+ finalized: 2,
9
+ };
10
+ /**
11
+ * Class to await for transaction confirmations in an optimised manner. It tracks a shared list of all pending transactions and fetches them in bulk in a shared RPC request whenever they have an "overlapping" polling interval. E.g. tx1 with an interval of 200ms and tx2 with an interval of 300ms (if sent at the same time) will be fetched together at at 600ms, 1200ms, 1800ms, etc.
12
+ */
13
+ class TransactionConfirmationManager {
14
+ constructor(connection) {
15
+ this.pendingConfirmations = new Map();
16
+ this.intervalId = null;
17
+ this.connection = connection;
18
+ }
19
+ async confirmTransaction(txSig, desiredConfirmationStatus = config_1.DEFAULT_CONFIRMATION_OPTS.commitment, timeout = 30000, pollInterval = 1000, searchTransactionHistory = false) {
20
+ // Interval must be > 400ms and a multiple of 100ms
21
+ if (pollInterval < 400 || pollInterval % 100 !== 0) {
22
+ throw new Error('Transaction confirmation polling interval must be at least 400ms and a multiple of 100ms');
23
+ }
24
+ return new Promise((resolve, reject) => {
25
+ this.pendingConfirmations.set(txSig, {
26
+ txSig,
27
+ desiredConfirmationStatus,
28
+ timeout,
29
+ pollInterval,
30
+ searchTransactionHistory,
31
+ startTime: Date.now(),
32
+ resolve,
33
+ reject,
34
+ });
35
+ if (!this.intervalId) {
36
+ this.startConfirmationLoop();
37
+ }
38
+ });
39
+ }
40
+ startConfirmationLoop() {
41
+ this.intervalId = setInterval(() => this.checkPendingConfirmations(), 100);
42
+ }
43
+ async checkPendingConfirmations() {
44
+ const now = Date.now();
45
+ const transactionsToCheck = [];
46
+ for (const [txSig, request] of this.pendingConfirmations.entries()) {
47
+ if (now - request.startTime >= request.timeout) {
48
+ request.reject(new Error(`Transaction confirmation timeout after ${request.timeout}ms`));
49
+ this.pendingConfirmations.delete(txSig);
50
+ }
51
+ else if ((now - request.startTime) % request.pollInterval < 100) {
52
+ transactionsToCheck.push(request);
53
+ }
54
+ }
55
+ if (transactionsToCheck.length > 0) {
56
+ await this.checkTransactionStatuses(transactionsToCheck);
57
+ }
58
+ if (this.pendingConfirmations.size === 0 && this.intervalId) {
59
+ clearInterval(this.intervalId);
60
+ this.intervalId = null;
61
+ }
62
+ }
63
+ async checkTransactionStatuses(requests) {
64
+ const txSigs = requests.map((request) => request.txSig);
65
+ const { value: statuses } = await this.connection.getSignatureStatuses(txSigs, {
66
+ searchTransactionHistory: requests.some((req) => req.searchTransactionHistory),
67
+ });
68
+ if (!statuses || statuses.length !== txSigs.length) {
69
+ throw new Error('Failed to get signature statuses');
70
+ }
71
+ for (let i = 0; i < statuses.length; i++) {
72
+ const status = statuses[i];
73
+ const request = requests[i];
74
+ if (status === null) {
75
+ continue;
76
+ }
77
+ if (status.err) {
78
+ request.reject(new Error(`Transaction failed: ${JSON.stringify(status.err)}`));
79
+ this.pendingConfirmations.delete(request.txSig);
80
+ continue;
81
+ }
82
+ if (confirmationStatusValues[status.confirmationStatus] === undefined ||
83
+ confirmationStatusValues[request.desiredConfirmationStatus] ===
84
+ undefined) {
85
+ throw new Error(`Invalid confirmation status when awaiting confirmation: ${status.confirmationStatus}`);
86
+ }
87
+ if (status.confirmationStatus &&
88
+ confirmationStatusValues[status.confirmationStatus] >=
89
+ confirmationStatusValues[request.desiredConfirmationStatus]) {
90
+ request.resolve(status);
91
+ this.pendingConfirmations.delete(request.txSig);
92
+ }
93
+ }
94
+ }
95
+ }
96
+ exports.TransactionConfirmationManager = TransactionConfirmationManager;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drift-labs/sdk",
3
- "version": "2.96.0-beta.2",
3
+ "version": "2.96.0-beta.20",
4
4
  "main": "lib/index.js",
5
5
  "types": "lib/index.d.ts",
6
6
  "author": "crispheaney",
@@ -37,16 +37,14 @@
37
37
  "@coral-xyz/anchor": "0.28.0",
38
38
  "@coral-xyz/anchor-30": "npm:@coral-xyz/anchor@0.30.1",
39
39
  "@ellipsis-labs/phoenix-sdk": "^1.4.2",
40
- "@grpc/grpc-js": "^1.11.2",
41
- "@openbook-dex/openbook-v2": "^0.2.10",
40
+ "@openbook-dex/openbook-v2": "0.2.10",
42
41
  "@project-serum/serum": "^0.13.38",
43
42
  "@pythnetwork/client": "2.5.3",
44
43
  "@pythnetwork/price-service-sdk": "^1.7.1",
45
44
  "@pythnetwork/pyth-solana-receiver": "^0.7.0",
46
45
  "@solana/spl-token": "0.3.7",
47
46
  "@solana/web3.js": "1.92.3",
48
- "@switchboard-xyz/on-demand": "1.2.32",
49
- "@triton-one/yellowstone-grpc": "^0.6.0",
47
+ "@switchboard-xyz/on-demand": "1.2.42",
50
48
  "anchor-bankrun": "^0.3.0",
51
49
  "node-cache": "^5.1.2",
52
50
  "rpc-websockets": "7.5.1",
@@ -58,6 +56,7 @@
58
56
  "devDependencies": {
59
57
  "@types/big.js": "^6.2.0",
60
58
  "@types/bn.js": "^5.1.3",
59
+ "@types/bs58": "^4.0.4",
61
60
  "@types/chai": "^4.3.1",
62
61
  "@types/jest": "^28.1.3",
63
62
  "@types/mocha": "^9.1.1",
@@ -1,6 +1,7 @@
1
1
  import {
2
- DataAndSlot,
3
2
  AccountToPoll,
3
+ DataAndSlot,
4
+ DelistedMarketSetting,
4
5
  DriftClientAccountEvents,
5
6
  DriftClientAccountSubscriber,
6
7
  NotSubscribedError,
@@ -10,18 +11,18 @@ import { Program } from '@coral-xyz/anchor';
10
11
  import StrictEventEmitter from 'strict-event-emitter-types';
11
12
  import { EventEmitter } from 'events';
12
13
  import {
13
- SpotMarketAccount,
14
14
  PerpMarketAccount,
15
+ SpotMarketAccount,
15
16
  StateAccount,
16
17
  UserAccount,
17
18
  } from '../types';
18
19
  import {
19
20
  getDriftStateAccountPublicKey,
20
- getSpotMarketPublicKey,
21
21
  getPerpMarketPublicKey,
22
+ getSpotMarketPublicKey,
22
23
  } from '../addresses/pda';
23
24
  import { BulkAccountLoader } from './bulkAccountLoader';
24
- import { capitalize } from './utils';
25
+ import { capitalize, findDelistedPerpMarketsAndOracles } from './utils';
25
26
  import { PublicKey } from '@solana/web3.js';
26
27
  import { OracleInfo, OraclePriceData } from '../oracles/types';
27
28
  import { OracleClientCache } from '../oracles/oracleClientCache';
@@ -58,6 +59,7 @@ export class PollingDriftClientAccountSubscriber
58
59
  spotOracleStringMap = new Map<number, string>();
59
60
  oracles = new Map<string, DataAndSlot<OraclePriceData>>();
60
61
  user?: DataAndSlot<UserAccount>;
62
+ delistedMarketSetting: DelistedMarketSetting;
61
63
 
62
64
  private isSubscribing = false;
63
65
  private subscriptionPromise: Promise<boolean>;
@@ -69,7 +71,8 @@ export class PollingDriftClientAccountSubscriber
69
71
  perpMarketIndexes: number[],
70
72
  spotMarketIndexes: number[],
71
73
  oracleInfos: OracleInfo[],
72
- shouldFindAllMarketsAndOracles: boolean
74
+ shouldFindAllMarketsAndOracles: boolean,
75
+ delistedMarketSetting: DelistedMarketSetting
73
76
  ) {
74
77
  this.isSubscribed = false;
75
78
  this.program = program;
@@ -79,6 +82,7 @@ export class PollingDriftClientAccountSubscriber
79
82
  this.spotMarketIndexes = spotMarketIndexes;
80
83
  this.oracleInfos = oracleInfos;
81
84
  this.shouldFindAllMarketsAndOracles = shouldFindAllMarketsAndOracles;
85
+ this.delistedMarketSetting = delistedMarketSetting;
82
86
  }
83
87
 
84
88
  public async subscribe(): Promise<boolean> {
@@ -120,6 +124,8 @@ export class PollingDriftClientAccountSubscriber
120
124
  this.eventEmitter.emit('update');
121
125
  }
122
126
 
127
+ this.handleDelistedMarkets();
128
+
123
129
  await Promise.all([this.setPerpOracleMap(), this.setSpotOracleMap()]);
124
130
 
125
131
  this.isSubscribing = false;
@@ -500,6 +506,36 @@ export class PollingDriftClientAccountSubscriber
500
506
  await Promise.all(oraclePromises);
501
507
  }
502
508
 
509
+ handleDelistedMarkets(): void {
510
+ if (this.delistedMarketSetting === DelistedMarketSetting.Subscribe) {
511
+ return;
512
+ }
513
+
514
+ const { perpMarketIndexes, oracles } = findDelistedPerpMarketsAndOracles(
515
+ this.getMarketAccountsAndSlots(),
516
+ this.getSpotMarketAccountsAndSlots()
517
+ );
518
+
519
+ for (const perpMarketIndex of perpMarketIndexes) {
520
+ const perpMarketPubkey = this.perpMarket.get(perpMarketIndex).data.pubkey;
521
+ const callbackId = this.accountsToPoll.get(
522
+ perpMarketPubkey.toBase58()
523
+ ).callbackId;
524
+ this.accountLoader.removeAccount(perpMarketPubkey, callbackId);
525
+ if (this.delistedMarketSetting === DelistedMarketSetting.Discard) {
526
+ this.perpMarket.delete(perpMarketIndex);
527
+ }
528
+ }
529
+
530
+ for (const oracle of oracles) {
531
+ const callbackId = this.oraclesToPoll.get(oracle.toBase58()).callbackId;
532
+ this.accountLoader.removeAccount(oracle, callbackId);
533
+ if (this.delistedMarketSetting === DelistedMarketSetting.Discard) {
534
+ this.oracles.delete(oracle.toBase58());
535
+ }
536
+ }
537
+ }
538
+
503
539
  assertIsSubscribed(): void {
504
540
  if (!this.isSubscribed) {
505
541
  throw new NotSubscribedError(
@@ -12,8 +12,6 @@ import { EventEmitter } from 'events';
12
12
  import { Context, PublicKey } from '@solana/web3.js';
13
13
  import { Account } from '@solana/spl-token';
14
14
  import { OracleInfo, OraclePriceData } from '..';
15
- import { CommitmentLevel } from '@triton-one/yellowstone-grpc';
16
- import { ChannelOptions } from '@grpc/grpc-js';
17
15
 
18
16
  export interface AccountSubscriber<T> {
19
17
  dataAndSlot?: DataAndSlot<T>;
@@ -86,6 +84,12 @@ export interface DriftClientAccountSubscriber {
86
84
  updateAccountLoaderPollingFrequency?: (pollingFrequency: number) => void;
87
85
  }
88
86
 
87
+ export enum DelistedMarketSetting {
88
+ Unsubscribe,
89
+ Subscribe,
90
+ Discard,
91
+ }
92
+
89
93
  export interface UserAccountEvents {
90
94
  userAccountUpdate: (payload: UserAccount) => void;
91
95
  update: void;
@@ -203,10 +207,3 @@ export interface UserStatsAccountSubscriber {
203
207
 
204
208
  getUserStatsAccountAndSlot(): DataAndSlot<UserStatsAccount>;
205
209
  }
206
-
207
- export type GrpcConfigs = {
208
- endpoint: string;
209
- token: string;
210
- commitmentLevel?: CommitmentLevel;
211
- channelOptions?: ChannelOptions;
212
- };
@@ -1,3 +1,45 @@
1
+ import { PublicKey } from '@solana/web3.js';
2
+ import { DataAndSlot } from './types';
3
+ import { isVariant, PerpMarketAccount, SpotMarketAccount } from '../types';
4
+
1
5
  export function capitalize(value: string): string {
2
6
  return value[0].toUpperCase() + value.slice(1);
3
7
  }
8
+
9
+ export function findDelistedPerpMarketsAndOracles(
10
+ perpMarkets: DataAndSlot<PerpMarketAccount>[],
11
+ spotMarkets: DataAndSlot<SpotMarketAccount>[]
12
+ ): { perpMarketIndexes: number[]; oracles: PublicKey[] } {
13
+ const delistedPerpMarketIndexes = [];
14
+ const delistedOracles = [];
15
+ for (const perpMarket of perpMarkets) {
16
+ if (!perpMarket.data) {
17
+ continue;
18
+ }
19
+
20
+ if (isVariant(perpMarket.data.status, 'delisted')) {
21
+ delistedPerpMarketIndexes.push(perpMarket.data.marketIndex);
22
+ delistedOracles.push(perpMarket.data.amm.oracle);
23
+ }
24
+ }
25
+
26
+ // make sure oracle isn't used by spot market
27
+ const filteredDelistedOracles = [];
28
+ for (const delistedOracle of delistedOracles) {
29
+ for (const spotMarket of spotMarkets) {
30
+ if (!spotMarket.data) {
31
+ continue;
32
+ }
33
+
34
+ if (spotMarket.data.oracle.equals(delistedOracle)) {
35
+ break;
36
+ }
37
+ }
38
+ filteredDelistedOracles.push(delistedOracle);
39
+ }
40
+
41
+ return {
42
+ perpMarketIndexes: delistedPerpMarketIndexes,
43
+ oracles: filteredDelistedOracles,
44
+ };
45
+ }