@drift-labs/sdk 2.77.0-beta.4 → 2.79.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/VERSION CHANGED
@@ -1 +1 @@
1
- 2.77.0-beta.4
1
+ 2.79.0-beta.0
@@ -64,3 +64,4 @@ export declare const DEFAULT_REVENUE_SINCE_LAST_FUNDING_SPREAD_RETREAT: BN;
64
64
  export declare const ACCOUNT_AGE_DELETION_CUTOFF_SECONDS: number;
65
65
  export declare const IDLE_TIME_SLOTS = 9000;
66
66
  export declare const SLOT_TIME_ESTIMATE_MS = 400;
67
+ export declare const DUST_POSITION_SIZE: BN;
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.MARGIN_PRECISION = exports.AMM_TIMES_PEG_TO_QUOTE_PRECISION_RATIO = exports.PRICE_TO_QUOTE_PRECISION = exports.PRICE_DIV_PEG = exports.AMM_TO_QUOTE_PRECISION_RATIO = exports.BASE_PRECISION_EXP = exports.BASE_PRECISION = exports.AMM_RESERVE_PRECISION = exports.PEG_PRECISION = exports.FUNDING_RATE_BUFFER_PRECISION = exports.FUNDING_RATE_PRECISION = exports.PRICE_PRECISION = exports.QUOTE_PRECISION = exports.LIQUIDATION_FEE_PRECISION = exports.SPOT_MARKET_IMF_PRECISION = exports.SPOT_MARKET_IMF_PRECISION_EXP = exports.SPOT_MARKET_BALANCE_PRECISION = exports.SPOT_MARKET_BALANCE_PRECISION_EXP = exports.SPOT_MARKET_WEIGHT_PRECISION = exports.SPOT_MARKET_UTILIZATION_PRECISION = exports.SPOT_MARKET_UTILIZATION_PRECISION_EXP = exports.SPOT_MARKET_CUMULATIVE_INTEREST_PRECISION = exports.SPOT_MARKET_CUMULATIVE_INTEREST_PRECISION_EXP = exports.SPOT_MARKET_RATE_PRECISION = exports.SPOT_MARKET_RATE_PRECISION_EXP = exports.AMM_RESERVE_PRECISION_EXP = exports.PEG_PRECISION_EXP = exports.FUNDING_RATE_PRECISION_EXP = exports.PRICE_PRECISION_EXP = exports.FUNDING_RATE_BUFFER_PRECISION_EXP = exports.QUOTE_PRECISION_EXP = exports.CONCENTRATION_PRECISION = exports.PERCENTAGE_PRECISION = exports.PERCENTAGE_PRECISION_EXP = exports.MAX_LEVERAGE_ORDER_SIZE = exports.MAX_LEVERAGE = exports.TEN_MILLION = exports.BN_MAX = exports.TEN_THOUSAND = exports.TEN = exports.NINE = exports.EIGHT = exports.SEVEN = exports.SIX = exports.FIVE = exports.FOUR = exports.THREE = exports.TWO = exports.ONE = exports.ZERO = void 0;
4
- exports.SLOT_TIME_ESTIMATE_MS = exports.IDLE_TIME_SLOTS = exports.ACCOUNT_AGE_DELETION_CUTOFF_SECONDS = exports.DEFAULT_REVENUE_SINCE_LAST_FUNDING_SPREAD_RETREAT = exports.OPEN_ORDER_MARGIN_REQUIREMENT = exports.LAMPORTS_EXP = exports.LAMPORTS_PRECISION = exports.QUOTE_SPOT_MARKET_INDEX = exports.ONE_YEAR = exports.ONE_HOUR = exports.FIVE_MINUTE = exports.FUNDING_RATE_OFFSET_DENOMINATOR = exports.LIQUIDATION_PCT_PRECISION = exports.BID_ASK_SPREAD_PRECISION = void 0;
4
+ exports.DUST_POSITION_SIZE = exports.SLOT_TIME_ESTIMATE_MS = exports.IDLE_TIME_SLOTS = exports.ACCOUNT_AGE_DELETION_CUTOFF_SECONDS = exports.DEFAULT_REVENUE_SINCE_LAST_FUNDING_SPREAD_RETREAT = exports.OPEN_ORDER_MARGIN_REQUIREMENT = exports.LAMPORTS_EXP = exports.LAMPORTS_PRECISION = exports.QUOTE_SPOT_MARKET_INDEX = exports.ONE_YEAR = exports.ONE_HOUR = exports.FIVE_MINUTE = exports.FUNDING_RATE_OFFSET_DENOMINATOR = exports.LIQUIDATION_PCT_PRECISION = exports.BID_ASK_SPREAD_PRECISION = void 0;
5
5
  const web3_js_1 = require("@solana/web3.js");
6
6
  const __1 = require("../");
7
7
  exports.ZERO = new __1.BN(0);
@@ -68,3 +68,4 @@ exports.DEFAULT_REVENUE_SINCE_LAST_FUNDING_SPREAD_RETREAT = new __1.BN(-25).mul(
68
68
  exports.ACCOUNT_AGE_DELETION_CUTOFF_SECONDS = 60 * 60 * 24 * 13; // 13 days
69
69
  exports.IDLE_TIME_SLOTS = 9000;
70
70
  exports.SLOT_TIME_ESTIMATE_MS = 400;
71
+ exports.DUST_POSITION_SIZE = exports.QUOTE_PRECISION.divn(100); // Dust position is any position smaller than 1c
@@ -251,6 +251,7 @@ export declare class DriftClient {
251
251
  */
252
252
  initializeUserAccountAndDepositCollateral(amount: BN, userTokenAccount: PublicKey, marketIndex?: number, subAccountId?: number, name?: string, fromSubAccountId?: number, referrerInfo?: ReferrerInfo, donateAmount?: BN, txParams?: TxParams, customMaxMarginRatio?: number): Promise<[TransactionSignature, PublicKey]>;
253
253
  initializeUserAccountForDevnet(subAccountId: number, name: string, marketIndex: number, tokenFaucet: TokenFaucet, amount: BN, referrerInfo?: ReferrerInfo, txParams?: TxParams): Promise<[TransactionSignature, PublicKey]>;
254
+ private getWithdrawalIxs;
254
255
  /**
255
256
  * Withdraws from a user account. If deposit doesn't already exist, creates a borrow
256
257
  * @param amount
@@ -259,6 +260,9 @@ export declare class DriftClient {
259
260
  * @param reduceOnly
260
261
  */
261
262
  withdraw(amount: BN, marketIndex: number, associatedTokenAddress: PublicKey, reduceOnly?: boolean, subAccountId?: number, txParams?: TxParams): Promise<TransactionSignature>;
263
+ withdrawAllDustPositions(subAccountId?: number, txParams?: TxParams, opts?: {
264
+ dustPositionCountCallback?: (count: number) => void;
265
+ }): Promise<TransactionSignature | undefined>;
262
266
  getWithdrawIx(amount: BN, marketIndex: number, userTokenAccount: PublicKey, reduceOnly?: boolean, subAccountId?: number): Promise<TransactionInstruction>;
263
267
  /**
264
268
  * Withdraws from the fromSubAccount and deposits into the toSubAccount
@@ -585,7 +589,9 @@ export declare class DriftClient {
585
589
  settlePNLs(users: {
586
590
  settleeUserAccountPublicKey: PublicKey;
587
591
  settleeUserAccount: UserAccount;
588
- }[], marketIndexes: number[]): Promise<TransactionSignature>;
592
+ }[], marketIndexes: number[], opts?: {
593
+ filterInvalidMarkets?: boolean;
594
+ }): Promise<TransactionSignature>;
589
595
  getSettlePNLsIxs(users: {
590
596
  settleeUserAccountPublicKey: PublicKey;
591
597
  settleeUserAccount: UserAccount;
@@ -57,6 +57,7 @@ const marinade_1 = require("./marinade");
57
57
  const orderParams_1 = require("./orderParams");
58
58
  const utils_2 = require("./math/utils");
59
59
  const txParamProcessor_1 = require("./tx/txParamProcessor");
60
+ const oracles_1 = require("./math/oracles");
60
61
  /**
61
62
  * # DriftClient
62
63
  * This class is the main way to interact with Drift Protocol. It allows you to subscribe to the various accounts where the Market's state is stored, as well as: opening positions, liquidating, settling funding, depositing & withdrawing, and more.
@@ -69,7 +70,7 @@ class DriftClient {
69
70
  this._isSubscribed = val;
70
71
  }
71
72
  constructor(config) {
72
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0;
73
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2;
73
74
  this.users = new Map();
74
75
  this._isSubscribed = false;
75
76
  this.perpMarketLastSlotCache = new Map();
@@ -78,18 +79,22 @@ class DriftClient {
78
79
  this.mustIncludeSpotMarketIndexes = new Set();
79
80
  this.connection = config.connection;
80
81
  this.wallet = config.wallet;
81
- this.opts = config.opts || anchor_1.AnchorProvider.defaultOptions();
82
+ this.opts = config.opts || {
83
+ ...anchor_1.AnchorProvider.defaultOptions(),
84
+ commitment: (_a = config === null || config === void 0 ? void 0 : config.connection) === null || _a === void 0 ? void 0 : _a.commitment,
85
+ preflightCommitment: (_b = config === null || config === void 0 ? void 0 : config.connection) === null || _b === void 0 ? void 0 : _b.commitment, // At the moment this ensures that our transaction simulations (which use Connection object) will use the same commitment level as our Transaction blockhashes (which use these opts)
86
+ };
82
87
  this.provider = new anchor_1.AnchorProvider(config.connection,
83
88
  // @ts-ignore
84
89
  config.wallet, this.opts);
85
- this.program = new anchor_1.Program(drift_json_1.default, (_a = config.programID) !== null && _a !== void 0 ? _a : new web3_js_1.PublicKey(config_1.DRIFT_PROGRAM_ID), this.provider);
86
- this.authority = (_b = config.authority) !== null && _b !== void 0 ? _b : this.wallet.publicKey;
87
- this.activeSubAccountId = (_c = config.activeSubAccountId) !== null && _c !== void 0 ? _c : 0;
88
- this.skipLoadUsers = (_d = config.skipLoadUsers) !== null && _d !== void 0 ? _d : false;
89
- this.txVersion = (_e = config.txVersion) !== null && _e !== void 0 ? _e : 'legacy';
90
+ this.program = new anchor_1.Program(drift_json_1.default, (_c = config.programID) !== null && _c !== void 0 ? _c : new web3_js_1.PublicKey(config_1.DRIFT_PROGRAM_ID), this.provider);
91
+ this.authority = (_d = config.authority) !== null && _d !== void 0 ? _d : this.wallet.publicKey;
92
+ this.activeSubAccountId = (_e = config.activeSubAccountId) !== null && _e !== void 0 ? _e : 0;
93
+ this.skipLoadUsers = (_f = config.skipLoadUsers) !== null && _f !== void 0 ? _f : false;
94
+ this.txVersion = (_g = config.txVersion) !== null && _g !== void 0 ? _g : 'legacy';
90
95
  this.txParams = {
91
- computeUnits: (_g = (_f = config.txParams) === null || _f === void 0 ? void 0 : _f.computeUnits) !== null && _g !== void 0 ? _g : 600000,
92
- computeUnitsPrice: (_j = (_h = config.txParams) === null || _h === void 0 ? void 0 : _h.computeUnitsPrice) !== null && _j !== void 0 ? _j : 0,
96
+ computeUnits: (_j = (_h = config.txParams) === null || _h === void 0 ? void 0 : _h.computeUnits) !== null && _j !== void 0 ? _j : 600000,
97
+ computeUnitsPrice: (_l = (_k = config.txParams) === null || _k === void 0 ? void 0 : _k.computeUnitsPrice) !== null && _l !== void 0 ? _l : 0,
93
98
  };
94
99
  if (config.includeDelegates && config.subAccountIds) {
95
100
  throw new Error('Can only pass one of includeDelegates or subAccountIds. If you want to specify subaccount ids for multiple authorities, pass authoritySubaccountMap instead');
@@ -105,8 +110,8 @@ class DriftClient {
105
110
  : config.subAccountIds
106
111
  ? new Map([[this.authority.toString(), config.subAccountIds]])
107
112
  : new Map();
108
- this.includeDelegates = (_k = config.includeDelegates) !== null && _k !== void 0 ? _k : false;
109
- if (((_l = config.accountSubscription) === null || _l === void 0 ? void 0 : _l.type) === 'polling') {
113
+ this.includeDelegates = (_m = config.includeDelegates) !== null && _m !== void 0 ? _m : false;
114
+ if (((_o = config.accountSubscription) === null || _o === void 0 ? void 0 : _o.type) === 'polling') {
110
115
  this.userAccountSubscriptionConfig = {
111
116
  type: 'polling',
112
117
  accountLoader: config.accountSubscription.accountLoader,
@@ -119,13 +124,13 @@ class DriftClient {
119
124
  else {
120
125
  this.userAccountSubscriptionConfig = {
121
126
  type: 'websocket',
122
- resubTimeoutMs: (_m = config.accountSubscription) === null || _m === void 0 ? void 0 : _m.resubTimeoutMs,
123
- commitment: (_o = config.accountSubscription) === null || _o === void 0 ? void 0 : _o.commitment,
127
+ resubTimeoutMs: (_p = config.accountSubscription) === null || _p === void 0 ? void 0 : _p.resubTimeoutMs,
128
+ commitment: (_q = config.accountSubscription) === null || _q === void 0 ? void 0 : _q.commitment,
124
129
  };
125
130
  this.userStatsAccountSubscriptionConfig = {
126
131
  type: 'websocket',
127
- resubTimeoutMs: (_p = config.accountSubscription) === null || _p === void 0 ? void 0 : _p.resubTimeoutMs,
128
- commitment: (_q = config.accountSubscription) === null || _q === void 0 ? void 0 : _q.commitment,
132
+ resubTimeoutMs: (_r = config.accountSubscription) === null || _r === void 0 ? void 0 : _r.resubTimeoutMs,
133
+ commitment: (_s = config.accountSubscription) === null || _s === void 0 ? void 0 : _s.commitment,
129
134
  };
130
135
  }
131
136
  if (config.userStats) {
@@ -142,11 +147,11 @@ class DriftClient {
142
147
  const noMarketsAndOraclesSpecified = config.perpMarketIndexes === undefined &&
143
148
  config.spotMarketIndexes === undefined &&
144
149
  config.oracleInfos === undefined;
145
- if (((_r = config.accountSubscription) === null || _r === void 0 ? void 0 : _r.type) === 'polling') {
146
- this.accountSubscriber = new pollingDriftClientAccountSubscriber_1.PollingDriftClientAccountSubscriber(this.program, config.accountSubscription.accountLoader, (_s = config.perpMarketIndexes) !== null && _s !== void 0 ? _s : [], (_t = config.spotMarketIndexes) !== null && _t !== void 0 ? _t : [], (_u = config.oracleInfos) !== null && _u !== void 0 ? _u : [], noMarketsAndOraclesSpecified);
150
+ if (((_t = config.accountSubscription) === null || _t === void 0 ? void 0 : _t.type) === 'polling') {
151
+ this.accountSubscriber = new pollingDriftClientAccountSubscriber_1.PollingDriftClientAccountSubscriber(this.program, config.accountSubscription.accountLoader, (_u = config.perpMarketIndexes) !== null && _u !== void 0 ? _u : [], (_v = config.spotMarketIndexes) !== null && _v !== void 0 ? _v : [], (_w = config.oracleInfos) !== null && _w !== void 0 ? _w : [], noMarketsAndOraclesSpecified);
147
152
  }
148
153
  else {
149
- this.accountSubscriber = new webSocketDriftClientAccountSubscriber_1.WebSocketDriftClientAccountSubscriber(this.program, (_v = config.perpMarketIndexes) !== null && _v !== void 0 ? _v : [], (_w = config.spotMarketIndexes) !== null && _w !== void 0 ? _w : [], (_x = config.oracleInfos) !== null && _x !== void 0 ? _x : [], noMarketsAndOraclesSpecified, (_y = config.accountSubscription) === null || _y === void 0 ? void 0 : _y.resubTimeoutMs, (_z = config.accountSubscription) === null || _z === void 0 ? void 0 : _z.commitment);
154
+ this.accountSubscriber = new webSocketDriftClientAccountSubscriber_1.WebSocketDriftClientAccountSubscriber(this.program, (_x = config.perpMarketIndexes) !== null && _x !== void 0 ? _x : [], (_y = config.spotMarketIndexes) !== null && _y !== void 0 ? _y : [], (_z = config.oracleInfos) !== null && _z !== void 0 ? _z : [], noMarketsAndOraclesSpecified, (_0 = config.accountSubscription) === null || _0 === void 0 ? void 0 : _0.resubTimeoutMs, (_1 = config.accountSubscription) === null || _1 === void 0 ? void 0 : _1.commitment);
150
155
  }
151
156
  this.eventEmitter = this.accountSubscriber.eventEmitter;
152
157
  if (config.enableMetricsEvents) {
@@ -154,7 +159,7 @@ class DriftClient {
154
159
  this.metricsEventEmitter = new events_1.EventEmitter();
155
160
  }
156
161
  this.txSender =
157
- (_0 = config.txSender) !== null && _0 !== void 0 ? _0 : new retryTxSender_1.RetryTxSender({
162
+ (_2 = config.txSender) !== null && _2 !== void 0 ? _2 : new retryTxSender_1.RetryTxSender({
158
163
  connection: this.connection,
159
164
  wallet: this.wallet,
160
165
  opts: this.opts,
@@ -1233,16 +1238,8 @@ class DriftClient {
1233
1238
  await this.addUser(subAccountId);
1234
1239
  return [txSig, userAccountPublicKey];
1235
1240
  }
1236
- /**
1237
- * Withdraws from a user account. If deposit doesn't already exist, creates a borrow
1238
- * @param amount
1239
- * @param marketIndex
1240
- * @param associatedTokenAddress - the token account to withdraw to. can be the wallet public key if using native sol
1241
- * @param reduceOnly
1242
- */
1243
- async withdraw(amount, marketIndex, associatedTokenAddress, reduceOnly = false, subAccountId, txParams) {
1241
+ async getWithdrawalIxs(amount, marketIndex, associatedTokenAddress, reduceOnly = false, subAccountId) {
1244
1242
  const withdrawIxs = [];
1245
- const additionalSigners = [];
1246
1243
  const spotMarketAccount = this.getSpotMarketAccount(marketIndex);
1247
1244
  const isSolMarket = spotMarketAccount.mint.equals(spotMarkets_1.WRAPPED_SOL_MINT);
1248
1245
  const authority = this.wallet.publicKey;
@@ -1265,11 +1262,46 @@ class DriftClient {
1265
1262
  if (createWSOLTokenAccount) {
1266
1263
  withdrawIxs.push((0, spl_token_1.createCloseAccountInstruction)(associatedTokenAddress, authority, authority, []));
1267
1264
  }
1265
+ return withdrawIxs;
1266
+ }
1267
+ /**
1268
+ * Withdraws from a user account. If deposit doesn't already exist, creates a borrow
1269
+ * @param amount
1270
+ * @param marketIndex
1271
+ * @param associatedTokenAddress - the token account to withdraw to. can be the wallet public key if using native sol
1272
+ * @param reduceOnly
1273
+ */
1274
+ async withdraw(amount, marketIndex, associatedTokenAddress, reduceOnly = false, subAccountId, txParams) {
1275
+ const additionalSigners = [];
1276
+ const withdrawIxs = await this.getWithdrawalIxs(amount, marketIndex, associatedTokenAddress, reduceOnly, subAccountId);
1268
1277
  const tx = await this.buildTransaction(withdrawIxs, txParams !== null && txParams !== void 0 ? txParams : this.txParams);
1269
1278
  const { txSig, slot } = await this.sendTransaction(tx, additionalSigners, this.opts);
1270
1279
  this.spotMarketLastSlotCache.set(marketIndex, slot);
1271
1280
  return txSig;
1272
1281
  }
1282
+ async withdrawAllDustPositions(subAccountId, txParams, opts) {
1283
+ var _a, _b;
1284
+ const user = this.getUser(subAccountId);
1285
+ const dustPositionSpotMarketAccounts = user.getSpotMarketAccountsWithDustPosition();
1286
+ if (!dustPositionSpotMarketAccounts ||
1287
+ dustPositionSpotMarketAccounts.length === 0) {
1288
+ (_a = opts === null || opts === void 0 ? void 0 : opts.dustPositionCountCallback) === null || _a === void 0 ? void 0 : _a.call(opts, 0);
1289
+ return undefined;
1290
+ }
1291
+ (_b = opts === null || opts === void 0 ? void 0 : opts.dustPositionCountCallback) === null || _b === void 0 ? void 0 : _b.call(opts, dustPositionSpotMarketAccounts.length);
1292
+ let allWithdrawIxs = [];
1293
+ for (const position of dustPositionSpotMarketAccounts) {
1294
+ const tokenAccount = await (0, spl_token_1.getAssociatedTokenAddress)(position.mint, this.wallet.publicKey);
1295
+ const tokenAmount = await user.getTokenAmount(position.marketIndex);
1296
+ const withdrawIxs = await this.getWithdrawalIxs(tokenAmount.muln(2), // 2x to ensure all dust is withdrawn
1297
+ position.marketIndex, tokenAccount, true, // reduce-only true to ensure all dust is withdrawn
1298
+ subAccountId);
1299
+ allWithdrawIxs = allWithdrawIxs.concat(withdrawIxs);
1300
+ }
1301
+ const tx = await this.buildTransaction(allWithdrawIxs, txParams !== null && txParams !== void 0 ? txParams : this.txParams);
1302
+ const { txSig } = await this.sendTransaction(tx, [], this.opts);
1303
+ return txSig;
1304
+ }
1273
1305
  async getWithdrawIx(amount, marketIndex, userTokenAccount, reduceOnly = false, subAccountId) {
1274
1306
  const user = await this.getUserAccountPublicKey(subAccountId);
1275
1307
  const remainingAccounts = this.getRemainingAccounts({
@@ -2524,9 +2556,13 @@ class DriftClient {
2524
2556
  };
2525
2557
  let placeAndTakeTx = await this.buildTransaction(ixs, txParamsWithoutImplicitSimulation);
2526
2558
  if (shouldUseSimulationComputeUnits || shouldExitIfSimulationFails) {
2527
- const versionedPlaceAndTakeTx = this.isVersionedTransaction(placeAndTakeTx)
2528
- ? placeAndTakeTx
2529
- : (await this.buildTransaction(ixs, txParamsWithoutImplicitSimulation, undefined, undefined, true));
2559
+ let versionedPlaceAndTakeTx;
2560
+ if (this.isVersionedTransaction(placeAndTakeTx)) {
2561
+ versionedPlaceAndTakeTx = placeAndTakeTx;
2562
+ }
2563
+ else {
2564
+ versionedPlaceAndTakeTx = (await this.buildTransaction(ixs, txParamsWithoutImplicitSimulation, undefined, undefined, true));
2565
+ }
2530
2566
  const simulationResult = await txParamProcessor_1.TransactionProcessor.getTxSimComputeUnits(versionedPlaceAndTakeTx, this.connection);
2531
2567
  if (shouldExitIfSimulationFails && !simulationResult.success) {
2532
2568
  return;
@@ -2933,9 +2969,29 @@ class DriftClient {
2933
2969
  remainingAccounts,
2934
2970
  });
2935
2971
  }
2936
- async settlePNLs(users, marketIndexes) {
2937
- const ixs = await this.getSettlePNLsIxs(users, marketIndexes);
2938
- const tx = await this.buildTransaction(ixs, { computeUnits: 1000000 });
2972
+ async settlePNLs(users, marketIndexes, opts) {
2973
+ const filterInvalidMarkets = opts === null || opts === void 0 ? void 0 : opts.filterInvalidMarkets;
2974
+ // # Filter market indexes by markets with valid oracle
2975
+ const marketIndexToSettle = filterInvalidMarkets
2976
+ ? []
2977
+ : marketIndexes;
2978
+ if (filterInvalidMarkets) {
2979
+ for (const marketIndex of marketIndexes) {
2980
+ const perpMarketAccount = this.getPerpMarketAccount(marketIndex);
2981
+ const oraclePriceData = this.getOracleDataForPerpMarket(marketIndex);
2982
+ const stateAccountAndSlot = this.accountSubscriber.getStateAccountAndSlot();
2983
+ const oracleGuardRails = stateAccountAndSlot.data.oracleGuardRails;
2984
+ const isValid = (0, oracles_1.isOracleValid)(perpMarketAccount, oraclePriceData, oracleGuardRails, stateAccountAndSlot.slot);
2985
+ if (isValid) {
2986
+ marketIndexToSettle.push(marketIndex);
2987
+ }
2988
+ }
2989
+ }
2990
+ // # Settle filtered market indexes
2991
+ const ixs = await this.getSettlePNLsIxs(users, marketIndexToSettle);
2992
+ const tx = await this.buildTransaction(ixs, {
2993
+ computeUnits: 1400000,
2994
+ });
2939
2995
  const { txSig } = await this.sendTransaction(tx, [], this.opts);
2940
2996
  return txSig;
2941
2997
  }
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "2.76.0",
2
+ "version": "2.78.0",
3
3
  "name": "drift",
4
4
  "instructions": [
5
5
  {
@@ -47,20 +47,23 @@ class TransactionProcessor {
47
47
  }
48
48
  // # Setup
49
49
  const { txProps: txProps, txBuilder: txBuilder, processConfig, processParams: processProps, } = props;
50
- const baseTransaction = await txBuilder(txProps);
51
50
  const finalTxProps = {
52
51
  ...txProps,
53
52
  };
54
53
  // # Run Processes
55
54
  if (processConfig.useSimulatedComputeUnits) {
56
- const txSimComputeUnitsResult = await this.getTxSimComputeUnits(baseTransaction, processProps.connection);
55
+ const txToSim = await txBuilder({
56
+ ...txProps,
57
+ txParams: { ...txProps.txParams, computeUnits: 1400000 },
58
+ });
59
+ const txSimComputeUnitsResult = await this.getTxSimComputeUnits(txToSim, processProps.connection);
57
60
  if (txSimComputeUnitsResult.success) {
58
61
  const bufferedComputeUnits = txSimComputeUnitsResult.computeUnits *
59
62
  ((_a = processConfig === null || processConfig === void 0 ? void 0 : processConfig.computeUnitsBufferMultiplier) !== null && _a !== void 0 ? _a : COMPUTE_UNIT_BUFFER_FACTOR);
60
63
  // Adjust the transaction based on the simulated compute units
61
64
  finalTxProps.txParams = {
62
65
  ...txProps.txParams,
63
- computeUnits: bufferedComputeUnits,
66
+ computeUnits: Math.ceil(bufferedComputeUnits), // Round the compute units to a whole number
64
67
  };
65
68
  }
66
69
  }
package/lib/user.d.ts CHANGED
@@ -195,6 +195,8 @@ export declare class User {
195
195
  spotAssetValue: BN;
196
196
  spotLiabilityValue: BN;
197
197
  };
198
+ isDustDepositPosition(spotMarketAccount: SpotMarketAccount): boolean;
199
+ getSpotMarketAccountsWithDustPosition(): SpotMarketAccount[];
198
200
  getTotalLiabilityValue(marginCategory?: MarginCategory): BN;
199
201
  getTotalAssetValue(marginCategory?: MarginCategory): BN;
200
202
  getNetUsdValue(): BN;
package/lib/user.js CHANGED
@@ -848,6 +848,35 @@ class User {
848
848
  spotLiabilityValue,
849
849
  };
850
850
  }
851
+ isDustDepositPosition(spotMarketAccount) {
852
+ const marketIndex = spotMarketAccount.marketIndex;
853
+ const spotPosition = this.getSpotPosition(spotMarketAccount.marketIndex);
854
+ if ((0, spotPosition_1.isSpotPositionAvailable)(spotPosition)) {
855
+ return false;
856
+ }
857
+ const depositAmount = this.getTokenAmount(spotMarketAccount.marketIndex);
858
+ if (depositAmount.lte(numericConstants_1.ZERO)) {
859
+ return false;
860
+ }
861
+ const oraclePriceData = this.getOracleDataForSpotMarket(marketIndex);
862
+ const strictOraclePrice = new strictOraclePrice_1.StrictOraclePrice(oraclePriceData.price, oraclePriceData.twap);
863
+ const balanceValue = this.getSpotAssetValue(depositAmount, strictOraclePrice, spotMarketAccount);
864
+ if (balanceValue.lt(numericConstants_1.DUST_POSITION_SIZE)) {
865
+ return true;
866
+ }
867
+ return false;
868
+ }
869
+ getSpotMarketAccountsWithDustPosition() {
870
+ const spotMarketAccounts = this.driftClient.getSpotMarketAccounts();
871
+ const dustPositionAccounts = [];
872
+ for (const spotMarketAccount of spotMarketAccounts) {
873
+ const isDust = this.isDustDepositPosition(spotMarketAccount);
874
+ if (isDust) {
875
+ dustPositionAccounts.push(spotMarketAccount);
876
+ }
877
+ }
878
+ return dustPositionAccounts;
879
+ }
851
880
  getTotalLiabilityValue(marginCategory) {
852
881
  return this.getTotalPerpPositionValue(marginCategory, undefined, true).add(this.getSpotMarketLiabilityValue(undefined, marginCategory, undefined, true));
853
882
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drift-labs/sdk",
3
- "version": "2.77.0-beta.4",
3
+ "version": "2.79.0-beta.0",
4
4
  "main": "lib/index.js",
5
5
  "types": "lib/index.d.ts",
6
6
  "author": "crispheaney",
@@ -103,3 +103,5 @@ export const DEFAULT_REVENUE_SINCE_LAST_FUNDING_SPREAD_RETREAT = new BN(
103
103
  export const ACCOUNT_AGE_DELETION_CUTOFF_SECONDS = 60 * 60 * 24 * 13; // 13 days
104
104
  export const IDLE_TIME_SLOTS = 9000;
105
105
  export const SLOT_TIME_ESTIMATE_MS = 400;
106
+
107
+ export const DUST_POSITION_SIZE = QUOTE_PRECISION.divn(100); // Dust position is any position smaller than 1c
@@ -128,6 +128,7 @@ import { getMarinadeDepositIx, getMarinadeFinanceProgram } from './marinade';
128
128
  import { getOrderParams } from './orderParams';
129
129
  import { numberToSafeBN } from './math/utils';
130
130
  import { TransactionProcessor as TransactionParamProcessor } from './tx/txParamProcessor';
131
+ import { isOracleValid } from './math/oracles';
131
132
 
132
133
  type RemainingAccountParams = {
133
134
  userAccounts: UserAccount[];
@@ -186,7 +187,11 @@ export class DriftClient {
186
187
  public constructor(config: DriftClientConfig) {
187
188
  this.connection = config.connection;
188
189
  this.wallet = config.wallet;
189
- this.opts = config.opts || AnchorProvider.defaultOptions();
190
+ this.opts = config.opts || {
191
+ ...AnchorProvider.defaultOptions(),
192
+ commitment: config?.connection?.commitment,
193
+ preflightCommitment: config?.connection?.commitment, // At the moment this ensures that our transaction simulations (which use Connection object) will use the same commitment level as our Transaction blockhashes (which use these opts)
194
+ };
190
195
  this.provider = new AnchorProvider(
191
196
  config.connection,
192
197
  // @ts-ignore
@@ -2183,24 +2188,14 @@ export class DriftClient {
2183
2188
  return [txSig, userAccountPublicKey];
2184
2189
  }
2185
2190
 
2186
- /**
2187
- * Withdraws from a user account. If deposit doesn't already exist, creates a borrow
2188
- * @param amount
2189
- * @param marketIndex
2190
- * @param associatedTokenAddress - the token account to withdraw to. can be the wallet public key if using native sol
2191
- * @param reduceOnly
2192
- */
2193
- public async withdraw(
2191
+ private async getWithdrawalIxs(
2194
2192
  amount: BN,
2195
2193
  marketIndex: number,
2196
2194
  associatedTokenAddress: PublicKey,
2197
2195
  reduceOnly = false,
2198
- subAccountId?: number,
2199
- txParams?: TxParams
2200
- ): Promise<TransactionSignature> {
2201
- const withdrawIxs = [];
2202
-
2203
- const additionalSigners: Array<Signer> = [];
2196
+ subAccountId?: number
2197
+ ) {
2198
+ const withdrawIxs: anchor.web3.TransactionInstruction[] = [];
2204
2199
 
2205
2200
  const spotMarketAccount = this.getSpotMarketAccount(marketIndex);
2206
2201
 
@@ -2258,6 +2253,34 @@ export class DriftClient {
2258
2253
  );
2259
2254
  }
2260
2255
 
2256
+ return withdrawIxs;
2257
+ }
2258
+
2259
+ /**
2260
+ * Withdraws from a user account. If deposit doesn't already exist, creates a borrow
2261
+ * @param amount
2262
+ * @param marketIndex
2263
+ * @param associatedTokenAddress - the token account to withdraw to. can be the wallet public key if using native sol
2264
+ * @param reduceOnly
2265
+ */
2266
+ public async withdraw(
2267
+ amount: BN,
2268
+ marketIndex: number,
2269
+ associatedTokenAddress: PublicKey,
2270
+ reduceOnly = false,
2271
+ subAccountId?: number,
2272
+ txParams?: TxParams
2273
+ ): Promise<TransactionSignature> {
2274
+ const additionalSigners: Array<Signer> = [];
2275
+
2276
+ const withdrawIxs = await this.getWithdrawalIxs(
2277
+ amount,
2278
+ marketIndex,
2279
+ associatedTokenAddress,
2280
+ reduceOnly,
2281
+ subAccountId
2282
+ );
2283
+
2261
2284
  const tx = await this.buildTransaction(
2262
2285
  withdrawIxs,
2263
2286
  txParams ?? this.txParams
@@ -2272,6 +2295,59 @@ export class DriftClient {
2272
2295
  return txSig;
2273
2296
  }
2274
2297
 
2298
+ public async withdrawAllDustPositions(
2299
+ subAccountId?: number,
2300
+ txParams?: TxParams,
2301
+ opts?: {
2302
+ dustPositionCountCallback?: (count: number) => void;
2303
+ }
2304
+ ): Promise<TransactionSignature | undefined> {
2305
+ const user = this.getUser(subAccountId);
2306
+
2307
+ const dustPositionSpotMarketAccounts =
2308
+ user.getSpotMarketAccountsWithDustPosition();
2309
+
2310
+ if (
2311
+ !dustPositionSpotMarketAccounts ||
2312
+ dustPositionSpotMarketAccounts.length === 0
2313
+ ) {
2314
+ opts?.dustPositionCountCallback?.(0);
2315
+ return undefined;
2316
+ }
2317
+
2318
+ opts?.dustPositionCountCallback?.(dustPositionSpotMarketAccounts.length);
2319
+
2320
+ let allWithdrawIxs: anchor.web3.TransactionInstruction[] = [];
2321
+
2322
+ for (const position of dustPositionSpotMarketAccounts) {
2323
+ const tokenAccount = await getAssociatedTokenAddress(
2324
+ position.mint,
2325
+ this.wallet.publicKey
2326
+ );
2327
+
2328
+ const tokenAmount = await user.getTokenAmount(position.marketIndex);
2329
+
2330
+ const withdrawIxs = await this.getWithdrawalIxs(
2331
+ tokenAmount.muln(2), // 2x to ensure all dust is withdrawn
2332
+ position.marketIndex,
2333
+ tokenAccount,
2334
+ true, // reduce-only true to ensure all dust is withdrawn
2335
+ subAccountId
2336
+ );
2337
+
2338
+ allWithdrawIxs = allWithdrawIxs.concat(withdrawIxs);
2339
+ }
2340
+
2341
+ const tx = await this.buildTransaction(
2342
+ allWithdrawIxs,
2343
+ txParams ?? this.txParams
2344
+ );
2345
+
2346
+ const { txSig } = await this.sendTransaction(tx, [], this.opts);
2347
+
2348
+ return txSig;
2349
+ }
2350
+
2275
2351
  public async getWithdrawIx(
2276
2352
  amount: BN,
2277
2353
  marketIndex: number,
@@ -4577,17 +4653,19 @@ export class DriftClient {
4577
4653
  );
4578
4654
 
4579
4655
  if (shouldUseSimulationComputeUnits || shouldExitIfSimulationFails) {
4580
- const versionedPlaceAndTakeTx = this.isVersionedTransaction(
4581
- placeAndTakeTx
4582
- )
4583
- ? (placeAndTakeTx as VersionedTransaction)
4584
- : ((await this.buildTransaction(
4585
- ixs,
4586
- txParamsWithoutImplicitSimulation,
4587
- undefined,
4588
- undefined,
4589
- true
4590
- )) as VersionedTransaction);
4656
+ let versionedPlaceAndTakeTx: VersionedTransaction;
4657
+
4658
+ if (this.isVersionedTransaction(placeAndTakeTx)) {
4659
+ versionedPlaceAndTakeTx = placeAndTakeTx as VersionedTransaction;
4660
+ } else {
4661
+ versionedPlaceAndTakeTx = (await this.buildTransaction(
4662
+ ixs,
4663
+ txParamsWithoutImplicitSimulation,
4664
+ undefined,
4665
+ undefined,
4666
+ true
4667
+ )) as VersionedTransaction;
4668
+ }
4591
4669
 
4592
4670
  const simulationResult =
4593
4671
  await TransactionParamProcessor.getTxSimComputeUnits(
@@ -5346,11 +5424,45 @@ export class DriftClient {
5346
5424
  settleeUserAccountPublicKey: PublicKey;
5347
5425
  settleeUserAccount: UserAccount;
5348
5426
  }[],
5349
- marketIndexes: number[]
5427
+ marketIndexes: number[],
5428
+ opts?: {
5429
+ filterInvalidMarkets?: boolean;
5430
+ }
5350
5431
  ): Promise<TransactionSignature> {
5351
- const ixs = await this.getSettlePNLsIxs(users, marketIndexes);
5432
+ const filterInvalidMarkets = opts?.filterInvalidMarkets;
5433
+
5434
+ // # Filter market indexes by markets with valid oracle
5435
+ const marketIndexToSettle: number[] = filterInvalidMarkets
5436
+ ? []
5437
+ : marketIndexes;
5438
+
5439
+ if (filterInvalidMarkets) {
5440
+ for (const marketIndex of marketIndexes) {
5441
+ const perpMarketAccount = this.getPerpMarketAccount(marketIndex);
5442
+ const oraclePriceData = this.getOracleDataForPerpMarket(marketIndex);
5443
+ const stateAccountAndSlot =
5444
+ this.accountSubscriber.getStateAccountAndSlot();
5445
+ const oracleGuardRails = stateAccountAndSlot.data.oracleGuardRails;
5446
+
5447
+ const isValid = isOracleValid(
5448
+ perpMarketAccount,
5449
+ oraclePriceData,
5450
+ oracleGuardRails,
5451
+ stateAccountAndSlot.slot
5452
+ );
5352
5453
 
5353
- const tx = await this.buildTransaction(ixs, { computeUnits: 1_000_000 });
5454
+ if (isValid) {
5455
+ marketIndexToSettle.push(marketIndex);
5456
+ }
5457
+ }
5458
+ }
5459
+
5460
+ // # Settle filtered market indexes
5461
+ const ixs = await this.getSettlePNLsIxs(users, marketIndexToSettle);
5462
+
5463
+ const tx = await this.buildTransaction(ixs, {
5464
+ computeUnits: 1_400_000,
5465
+ });
5354
5466
 
5355
5467
  const { txSig } = await this.sendTransaction(tx, [], this.opts);
5356
5468
  return txSig;
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "2.76.0",
2
+ "version": "2.78.0",
3
3
  "name": "drift",
4
4
  "instructions": [
5
5
  {
@@ -93,16 +93,19 @@ export class TransactionProcessor {
93
93
  processParams: processProps,
94
94
  } = props;
95
95
 
96
- const baseTransaction = await txBuilder(txProps);
97
-
98
96
  const finalTxProps = {
99
97
  ...txProps,
100
98
  };
101
99
 
102
100
  // # Run Processes
103
101
  if (processConfig.useSimulatedComputeUnits) {
102
+ const txToSim = await txBuilder({
103
+ ...txProps,
104
+ txParams: { ...txProps.txParams, computeUnits: 1_400_000 },
105
+ });
106
+
104
107
  const txSimComputeUnitsResult = await this.getTxSimComputeUnits(
105
- baseTransaction,
108
+ txToSim,
106
109
  processProps.connection
107
110
  );
108
111
 
@@ -115,7 +118,7 @@ export class TransactionProcessor {
115
118
  // Adjust the transaction based on the simulated compute units
116
119
  finalTxProps.txParams = {
117
120
  ...txProps.txParams,
118
- computeUnits: bufferedComputeUnits,
121
+ computeUnits: Math.ceil(bufferedComputeUnits), // Round the compute units to a whole number
119
122
  };
120
123
  }
121
124
  }
package/src/user.ts CHANGED
@@ -21,6 +21,7 @@ import {
21
21
  AMM_TO_QUOTE_PRECISION_RATIO,
22
22
  BASE_PRECISION,
23
23
  BN_MAX,
24
+ DUST_POSITION_SIZE,
24
25
  FIVE_MINUTE,
25
26
  MARGIN_PRECISION,
26
27
  ONE,
@@ -1563,6 +1564,56 @@ export class User {
1563
1564
  };
1564
1565
  }
1565
1566
 
1567
+ isDustDepositPosition(spotMarketAccount: SpotMarketAccount): boolean {
1568
+ const marketIndex = spotMarketAccount.marketIndex;
1569
+
1570
+ const spotPosition = this.getSpotPosition(spotMarketAccount.marketIndex);
1571
+
1572
+ if (isSpotPositionAvailable(spotPosition)) {
1573
+ return false;
1574
+ }
1575
+
1576
+ const depositAmount = this.getTokenAmount(spotMarketAccount.marketIndex);
1577
+
1578
+ if (depositAmount.lte(ZERO)) {
1579
+ return false;
1580
+ }
1581
+
1582
+ const oraclePriceData = this.getOracleDataForSpotMarket(marketIndex);
1583
+
1584
+ const strictOraclePrice = new StrictOraclePrice(
1585
+ oraclePriceData.price,
1586
+ oraclePriceData.twap
1587
+ );
1588
+
1589
+ const balanceValue = this.getSpotAssetValue(
1590
+ depositAmount,
1591
+ strictOraclePrice,
1592
+ spotMarketAccount
1593
+ );
1594
+
1595
+ if (balanceValue.lt(DUST_POSITION_SIZE)) {
1596
+ return true;
1597
+ }
1598
+
1599
+ return false;
1600
+ }
1601
+
1602
+ getSpotMarketAccountsWithDustPosition() {
1603
+ const spotMarketAccounts = this.driftClient.getSpotMarketAccounts();
1604
+
1605
+ const dustPositionAccounts: SpotMarketAccount[] = [];
1606
+
1607
+ for (const spotMarketAccount of spotMarketAccounts) {
1608
+ const isDust = this.isDustDepositPosition(spotMarketAccount);
1609
+ if (isDust) {
1610
+ dustPositionAccounts.push(spotMarketAccount);
1611
+ }
1612
+ }
1613
+
1614
+ return dustPositionAccounts;
1615
+ }
1616
+
1566
1617
  getTotalLiabilityValue(marginCategory?: MarginCategory): BN {
1567
1618
  return this.getTotalPerpPositionValue(marginCategory, undefined, true).add(
1568
1619
  this.getSpotMarketLiabilityValue(