@drift-labs/sdk 2.38.1-beta.8 → 2.39.1-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.
@@ -113,7 +113,12 @@ import { isSpotPositionAvailable } from './math/spotPosition';
113
113
  import { calculateMarketMaxAvailableInsurance } from './math/market';
114
114
  import { fetchUserStatsAccount } from './accounts/fetch';
115
115
  import { castNumberToSpotPrecision } from './math/spotMarket';
116
- import { JupiterClient, Route, SwapMode } from './jupiter/jupiterClient';
116
+ import {
117
+ JupiterClient,
118
+ QuoteResponse,
119
+ Route,
120
+ SwapMode,
121
+ } from './jupiter/jupiterClient';
117
122
  import { getNonIdleUserFilter } from './memcmp';
118
123
  import { UserStatsSubscriptionConfig } from './userStatsConfig';
119
124
  import { getMarinadeDepositIx, getMarinadeFinanceProgram } from './marinade';
@@ -3418,6 +3423,7 @@ export class DriftClient {
3418
3423
  route,
3419
3424
  reduceOnly,
3420
3425
  txParams,
3426
+ v6,
3421
3427
  }: {
3422
3428
  jupiterClient: JupiterClient;
3423
3429
  outMarketIndex: number;
@@ -3430,19 +3436,44 @@ export class DriftClient {
3430
3436
  route?: Route;
3431
3437
  reduceOnly?: SwapReduceOnly;
3432
3438
  txParams?: TxParams;
3439
+ v6?: {
3440
+ quote?: QuoteResponse;
3441
+ };
3433
3442
  }): Promise<TransactionSignature> {
3434
- const { ixs, lookupTables } = await this.getJupiterSwapIx({
3435
- jupiterClient,
3436
- outMarketIndex,
3437
- inMarketIndex,
3438
- outAssociatedTokenAccount,
3439
- inAssociatedTokenAccount,
3440
- amount,
3441
- slippageBps,
3442
- swapMode,
3443
- route,
3444
- reduceOnly,
3445
- });
3443
+ let ixs: anchor.web3.TransactionInstruction[];
3444
+ let lookupTables: anchor.web3.AddressLookupTableAccount[];
3445
+
3446
+ if (v6) {
3447
+ const res = await this.getJupiterSwapIxV6({
3448
+ jupiterClient,
3449
+ outMarketIndex,
3450
+ inMarketIndex,
3451
+ outAssociatedTokenAccount,
3452
+ inAssociatedTokenAccount,
3453
+ amount,
3454
+ slippageBps,
3455
+ swapMode,
3456
+ quote: v6.quote,
3457
+ reduceOnly,
3458
+ });
3459
+ ixs = res.ixs;
3460
+ lookupTables = res.lookupTables;
3461
+ } else {
3462
+ const res = await this.getJupiterSwapIx({
3463
+ jupiterClient,
3464
+ outMarketIndex,
3465
+ inMarketIndex,
3466
+ outAssociatedTokenAccount,
3467
+ inAssociatedTokenAccount,
3468
+ amount,
3469
+ slippageBps,
3470
+ swapMode,
3471
+ route,
3472
+ reduceOnly,
3473
+ });
3474
+ ixs = res.ixs;
3475
+ lookupTables = res.lookupTables;
3476
+ }
3446
3477
 
3447
3478
  const tx = (await this.buildTransaction(
3448
3479
  ixs,
@@ -3588,6 +3619,132 @@ export class DriftClient {
3588
3619
  return { ixs, lookupTables };
3589
3620
  }
3590
3621
 
3622
+ public async getJupiterSwapIxV6({
3623
+ jupiterClient,
3624
+ outMarketIndex,
3625
+ inMarketIndex,
3626
+ outAssociatedTokenAccount,
3627
+ inAssociatedTokenAccount,
3628
+ amount,
3629
+ slippageBps,
3630
+ swapMode,
3631
+ onlyDirectRoutes,
3632
+ quote,
3633
+ reduceOnly,
3634
+ userAccountPublicKey,
3635
+ }: {
3636
+ jupiterClient: JupiterClient;
3637
+ outMarketIndex: number;
3638
+ inMarketIndex: number;
3639
+ outAssociatedTokenAccount?: PublicKey;
3640
+ inAssociatedTokenAccount?: PublicKey;
3641
+ amount: BN;
3642
+ slippageBps?: number;
3643
+ swapMode?: SwapMode;
3644
+ onlyDirectRoutes?: boolean;
3645
+ quote?: QuoteResponse;
3646
+ reduceOnly?: SwapReduceOnly;
3647
+ userAccountPublicKey?: PublicKey;
3648
+ }): Promise<{
3649
+ ixs: TransactionInstruction[];
3650
+ lookupTables: AddressLookupTableAccount[];
3651
+ }> {
3652
+ const outMarket = this.getSpotMarketAccount(outMarketIndex);
3653
+ const inMarket = this.getSpotMarketAccount(inMarketIndex);
3654
+
3655
+ if (!quote) {
3656
+ const fetchedQuote = await jupiterClient.getQuote({
3657
+ inputMint: inMarket.mint,
3658
+ outputMint: outMarket.mint,
3659
+ amount,
3660
+ slippageBps,
3661
+ swapMode,
3662
+ onlyDirectRoutes,
3663
+ });
3664
+
3665
+ quote = fetchedQuote;
3666
+ }
3667
+
3668
+ const transaction = await jupiterClient.getSwap({
3669
+ quote,
3670
+ userPublicKey: this.provider.wallet.publicKey,
3671
+ slippageBps,
3672
+ });
3673
+
3674
+ const { transactionMessage, lookupTables } =
3675
+ await jupiterClient.getTransactionMessageAndLookupTables({
3676
+ transaction,
3677
+ });
3678
+
3679
+ const jupiterInstructions = jupiterClient.getJupiterInstructions({
3680
+ transactionMessage,
3681
+ inputMint: inMarket.mint,
3682
+ outputMint: outMarket.mint,
3683
+ });
3684
+
3685
+ const preInstructions = [];
3686
+ if (!outAssociatedTokenAccount) {
3687
+ outAssociatedTokenAccount = await this.getAssociatedTokenAccount(
3688
+ outMarket.marketIndex,
3689
+ false
3690
+ );
3691
+
3692
+ const accountInfo = await this.connection.getAccountInfo(
3693
+ outAssociatedTokenAccount
3694
+ );
3695
+ if (!accountInfo) {
3696
+ preInstructions.push(
3697
+ this.createAssociatedTokenAccountIdempotentInstruction(
3698
+ outAssociatedTokenAccount,
3699
+ this.provider.wallet.publicKey,
3700
+ this.provider.wallet.publicKey,
3701
+ outMarket.mint
3702
+ )
3703
+ );
3704
+ }
3705
+ }
3706
+
3707
+ if (!inAssociatedTokenAccount) {
3708
+ inAssociatedTokenAccount = await this.getAssociatedTokenAccount(
3709
+ inMarket.marketIndex,
3710
+ false
3711
+ );
3712
+
3713
+ const accountInfo = await this.connection.getAccountInfo(
3714
+ inAssociatedTokenAccount
3715
+ );
3716
+ if (!accountInfo) {
3717
+ preInstructions.push(
3718
+ this.createAssociatedTokenAccountIdempotentInstruction(
3719
+ inAssociatedTokenAccount,
3720
+ this.provider.wallet.publicKey,
3721
+ this.provider.wallet.publicKey,
3722
+ inMarket.mint
3723
+ )
3724
+ );
3725
+ }
3726
+ }
3727
+
3728
+ const { beginSwapIx, endSwapIx } = await this.getSwapIx({
3729
+ outMarketIndex,
3730
+ inMarketIndex,
3731
+ amountIn: amount,
3732
+ inTokenAccount: inAssociatedTokenAccount,
3733
+ outTokenAccount: outAssociatedTokenAccount,
3734
+ reduceOnly,
3735
+ userAccountPublicKey,
3736
+ });
3737
+
3738
+ const ixs = [
3739
+ ...preInstructions,
3740
+ beginSwapIx,
3741
+ ...jupiterInstructions,
3742
+ endSwapIx,
3743
+ ];
3744
+
3745
+ return { ixs, lookupTables };
3746
+ }
3747
+
3591
3748
  /**
3592
3749
  * Get the drift begin_swap and end_swap instructions
3593
3750
  *
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "2.38.0",
2
+ "version": "2.39.0",
3
3
  "name": "drift",
4
4
  "instructions": [
5
5
  {
@@ -3538,6 +3538,32 @@
3538
3538
  }
3539
3539
  ]
3540
3540
  },
3541
+ {
3542
+ "name": "updateSpotMarketScaleInitialAssetWeightStart",
3543
+ "accounts": [
3544
+ {
3545
+ "name": "admin",
3546
+ "isMut": false,
3547
+ "isSigner": true
3548
+ },
3549
+ {
3550
+ "name": "state",
3551
+ "isMut": false,
3552
+ "isSigner": false
3553
+ },
3554
+ {
3555
+ "name": "spotMarket",
3556
+ "isMut": true,
3557
+ "isSigner": false
3558
+ }
3559
+ ],
3560
+ "args": [
3561
+ {
3562
+ "name": "scaleInitialAssetWeightStart",
3563
+ "type": "u64"
3564
+ }
3565
+ ]
3566
+ },
3541
3567
  {
3542
3568
  "name": "updateSpotMarketOracle",
3543
3569
  "accounts": [
@@ -5357,12 +5383,21 @@
5357
5383
  ],
5358
5384
  "type": "u64"
5359
5385
  },
5386
+ {
5387
+ "name": "scaleInitialAssetWeightStart",
5388
+ "docs": [
5389
+ "When to begin scaling down the initial asset weight",
5390
+ "disabled when 0",
5391
+ "precision: QUOTE_PRECISION"
5392
+ ],
5393
+ "type": "u64"
5394
+ },
5360
5395
  {
5361
5396
  "name": "padding",
5362
5397
  "type": {
5363
5398
  "array": [
5364
5399
  "u8",
5365
- 56
5400
+ 48
5366
5401
  ]
5367
5402
  }
5368
5403
  }
package/src/index.ts CHANGED
@@ -5,6 +5,7 @@ import pyth from '@pythnetwork/client';
5
5
  export * from './tokenFaucet';
6
6
  export * from './oracles/types';
7
7
  export * from './oracles/pythClient';
8
+ export * from './oracles/strictOraclePrice';
8
9
  export * from './types';
9
10
  export * from './constants/perpMarkets';
10
11
  export * from './accounts/fetch';
@@ -49,6 +50,7 @@ export * from './math/repeg';
49
50
  export * from './math/margin';
50
51
  export * from './math/insurance';
51
52
  export * from './math/superStake';
53
+ export * from './math/spotPosition';
52
54
  export * from './marinade';
53
55
  export * from './orderParams';
54
56
  export * from './slot/SlotSubscriber';
@@ -64,6 +66,7 @@ export * from './priorityFee/priorityFeeSubscriber';
64
66
  export * from './phoenix/phoenixFulfillmentConfigMap';
65
67
  export * from './tx/fastSingleTxSender';
66
68
  export * from './tx/retryTxSender';
69
+ export * from './tx/priorityFeeCalculator';
67
70
  export * from './tx/types';
68
71
  export * from './util/computeUnits';
69
72
  export * from './util/tps';
@@ -22,6 +22,7 @@ import {
22
22
  import { OraclePriceData } from '../oracles/types';
23
23
  import { PERCENTAGE_PRECISION } from '../constants/numericConstants';
24
24
  import { divCeil } from './utils';
25
+ import { StrictOraclePrice } from '../oracles/strictOraclePrice';
25
26
 
26
27
  /**
27
28
  * Calculates the balance of a given token amount including any accumulated interest. This
@@ -102,25 +103,23 @@ export function getSignedTokenAmount(
102
103
  *
103
104
  * @param {BN} tokenAmount - The amount of tokens to calculate the value for (from `getTokenAmount`)
104
105
  * @param {number} spotDecimals - The number of decimals in the token.
105
- * @param {OraclePriceData} oraclePriceData - The oracle price data (typically a token/USD oracle).
106
- * @param {BN} oraclePriceTwap - The Time-Weighted Average Price of the oracle.
106
+ * @param {StrictOraclePrice} strictOraclePrice - Contains oracle price and 5min twap.
107
107
  * @return {BN} The calculated value of the given token amount, scaled by `PRICE_PRECISION`
108
108
  */
109
109
  export function getStrictTokenValue(
110
110
  tokenAmount: BN,
111
111
  spotDecimals: number,
112
- oraclePriceData: OraclePriceData,
113
- oraclePriceTwap: BN
112
+ strictOraclePrice: StrictOraclePrice
114
113
  ): BN {
115
114
  if (tokenAmount.eq(ZERO)) {
116
115
  return ZERO;
117
116
  }
118
117
 
119
- let price = oraclePriceData.price;
120
- if (tokenAmount.gt(ZERO)) {
121
- price = BN.min(oraclePriceData.price, oraclePriceTwap);
118
+ let price;
119
+ if (tokenAmount.gte(ZERO)) {
120
+ price = strictOraclePrice.min();
122
121
  } else {
123
- price = BN.max(oraclePriceData.price, oraclePriceTwap);
122
+ price = strictOraclePrice.max();
124
123
  }
125
124
 
126
125
  const precisionDecrease = TEN.pow(new BN(spotDecimals));
@@ -139,7 +138,7 @@ export function getStrictTokenValue(
139
138
  export function getTokenValue(
140
139
  tokenAmount: BN,
141
140
  spotDecimals: number,
142
- oraclePriceData: OraclePriceData
141
+ oraclePriceData: Pick<OraclePriceData, 'price'>
143
142
  ): BN {
144
143
  if (tokenAmount.eq(ZERO)) {
145
144
  return ZERO;
@@ -152,6 +151,7 @@ export function getTokenValue(
152
151
 
153
152
  export function calculateAssetWeight(
154
153
  balanceAmount: BN,
154
+ oraclePrice: BN,
155
155
  spotMarket: SpotMarketAccount,
156
156
  marginCategory: MarginCategory
157
157
  ): BN {
@@ -174,7 +174,7 @@ export function calculateAssetWeight(
174
174
  assetWeight = calculateSizeDiscountAssetWeight(
175
175
  sizeInAmmReservePrecision,
176
176
  new BN(spotMarket.imfFactor),
177
- new BN(spotMarket.initialAssetWeight)
177
+ calculateScaledInitialAssetWeight(spotMarket, oraclePrice)
178
178
  );
179
179
  break;
180
180
  case 'Maintenance':
@@ -185,13 +185,39 @@ export function calculateAssetWeight(
185
185
  );
186
186
  break;
187
187
  default:
188
- assetWeight = new BN(spotMarket.initialAssetWeight);
188
+ assetWeight = calculateScaledInitialAssetWeight(spotMarket, oraclePrice);
189
189
  break;
190
190
  }
191
191
 
192
192
  return assetWeight;
193
193
  }
194
194
 
195
+ export function calculateScaledInitialAssetWeight(
196
+ spotMarket: SpotMarketAccount,
197
+ oraclePrice: BN
198
+ ): BN {
199
+ if (spotMarket.scaleInitialAssetWeightStart.eq(ZERO)) {
200
+ return new BN(spotMarket.initialAssetWeight);
201
+ }
202
+
203
+ const deposits = getTokenAmount(
204
+ spotMarket.depositBalance,
205
+ spotMarket,
206
+ SpotBalanceType.DEPOSIT
207
+ );
208
+ const depositsValue = getTokenValue(deposits, spotMarket.decimals, {
209
+ price: oraclePrice,
210
+ });
211
+
212
+ if (depositsValue.lt(spotMarket.scaleInitialAssetWeightStart)) {
213
+ return new BN(spotMarket.initialAssetWeight);
214
+ } else {
215
+ return new BN(spotMarket.initialAssetWeight)
216
+ .mul(spotMarket.scaleInitialAssetWeightStart)
217
+ .div(depositsValue);
218
+ }
219
+ }
220
+
195
221
  export function calculateLiabilityWeight(
196
222
  size: BN,
197
223
  spotMarket: SpotMarketAccount,
@@ -229,7 +255,7 @@ export function calculateLiabilityWeight(
229
255
  );
230
256
  break;
231
257
  default:
232
- liabilityWeight = spotMarket.initialLiabilityWeight;
258
+ liabilityWeight = new BN(spotMarket.initialLiabilityWeight);
233
259
  break;
234
260
  }
235
261
 
@@ -21,12 +21,18 @@ export function castNumberToSpotPrecision(
21
21
 
22
22
  export function calculateSpotMarketMarginRatio(
23
23
  market: SpotMarketAccount,
24
+ oraclePrice: BN,
24
25
  marginCategory: MarginCategory,
25
26
  size: BN,
26
27
  balanceType: SpotBalanceType
27
28
  ): number {
28
29
  if (isVariant(balanceType, 'deposit')) {
29
- const assetWeight = calculateAssetWeight(size, market, marginCategory);
30
+ const assetWeight = calculateAssetWeight(
31
+ size,
32
+ oraclePrice,
33
+ market,
34
+ marginCategory
35
+ );
30
36
  return MARGIN_PRECISION.sub(assetWeight).toNumber();
31
37
  } else {
32
38
  const liabilityWeight = calculateLiabilityWeight(
@@ -1,22 +1,38 @@
1
- import { SpotMarketAccount, SpotPosition } from '../types';
2
- import { ZERO } from '../constants/numericConstants';
1
+ import { MarginCategory, SpotMarketAccount, SpotPosition } from '../types';
2
+ import {
3
+ SPOT_MARKET_WEIGHT_PRECISION,
4
+ ZERO,
5
+ } from '../constants/numericConstants';
3
6
  import { BN } from '@coral-xyz/anchor';
4
7
  import {
8
+ calculateAssetWeight,
9
+ calculateLiabilityWeight,
5
10
  getSignedTokenAmount,
11
+ getStrictTokenValue,
6
12
  getTokenAmount,
7
13
  getTokenValue,
8
14
  } from './spotBalance';
9
- import { OraclePriceData } from '../oracles/types';
15
+ import { StrictOraclePrice } from '../oracles/strictOraclePrice';
10
16
 
11
17
  export function isSpotPositionAvailable(position: SpotPosition): boolean {
12
18
  return position.scaledBalance.eq(ZERO) && position.openOrders === 0;
13
19
  }
14
20
 
21
+ export type OrderFillSimulation = {
22
+ tokenAmount: BN;
23
+ ordersValue: BN;
24
+ tokenValue: BN;
25
+ weight: BN;
26
+ weightedTokenValue: BN;
27
+ freeCollateralContribution;
28
+ };
29
+
15
30
  export function getWorstCaseTokenAmounts(
16
31
  spotPosition: SpotPosition,
17
32
  spotMarketAccount: SpotMarketAccount,
18
- oraclePriceData: OraclePriceData
19
- ): [BN, BN] {
33
+ strictOraclePrice: StrictOraclePrice,
34
+ marginCategory: MarginCategory
35
+ ): OrderFillSimulation {
20
36
  const tokenAmount = getSignedTokenAmount(
21
37
  getTokenAmount(
22
38
  spotPosition.scaledBalance,
@@ -26,22 +42,121 @@ export function getWorstCaseTokenAmounts(
26
42
  spotPosition.balanceType
27
43
  );
28
44
 
29
- const tokenAmountAllBidsFill = tokenAmount.add(spotPosition.openBids);
30
- const tokenAmountAllAsksFill = tokenAmount.add(spotPosition.openAsks);
45
+ const tokenValue = getStrictTokenValue(
46
+ tokenAmount,
47
+ spotMarketAccount.decimals,
48
+ strictOraclePrice
49
+ );
31
50
 
32
- if (tokenAmountAllBidsFill.abs().gt(tokenAmountAllAsksFill.abs())) {
33
- const worstCaseQuoteTokenAmount = getTokenValue(
34
- spotPosition.openBids.neg(),
35
- spotMarketAccount.decimals,
36
- oraclePriceData
51
+ if (spotPosition.openBids.eq(ZERO) && spotPosition.openAsks.eq(ZERO)) {
52
+ const { weight, weightedTokenValue } = calculateWeightedTokenValue(
53
+ tokenAmount,
54
+ tokenValue,
55
+ strictOraclePrice.current,
56
+ spotMarketAccount,
57
+ marginCategory
37
58
  );
38
- return [tokenAmountAllBidsFill, worstCaseQuoteTokenAmount];
59
+ return {
60
+ tokenAmount,
61
+ ordersValue: ZERO,
62
+ tokenValue,
63
+ weight,
64
+ weightedTokenValue,
65
+ freeCollateralContribution: weightedTokenValue,
66
+ };
67
+ }
68
+
69
+ const bidsSimulation = simulateOrderFill(
70
+ tokenAmount,
71
+ tokenValue,
72
+ spotPosition.openBids,
73
+ strictOraclePrice,
74
+ spotMarketAccount,
75
+ marginCategory
76
+ );
77
+ const asksSimulation = simulateOrderFill(
78
+ tokenAmount,
79
+ tokenValue,
80
+ spotPosition.openAsks,
81
+ strictOraclePrice,
82
+ spotMarketAccount,
83
+ marginCategory
84
+ );
85
+
86
+ if (
87
+ asksSimulation.freeCollateralContribution.lt(
88
+ bidsSimulation.freeCollateralContribution
89
+ )
90
+ ) {
91
+ return asksSimulation;
39
92
  } else {
40
- const worstCaseQuoteTokenAmount = getTokenValue(
41
- spotPosition.openAsks.neg(),
42
- spotMarketAccount.decimals,
43
- oraclePriceData
93
+ return bidsSimulation;
94
+ }
95
+ }
96
+
97
+ export function calculateWeightedTokenValue(
98
+ tokenAmount: BN,
99
+ tokenValue: BN,
100
+ oraclePrice: BN,
101
+ spotMarket: SpotMarketAccount,
102
+ marginCategory: MarginCategory
103
+ ): { weight: BN; weightedTokenValue: BN } {
104
+ let weight: BN;
105
+ if (tokenValue.gte(ZERO)) {
106
+ weight = calculateAssetWeight(
107
+ tokenAmount,
108
+ oraclePrice,
109
+ spotMarket,
110
+ marginCategory
111
+ );
112
+ } else {
113
+ weight = calculateLiabilityWeight(
114
+ tokenAmount.abs(),
115
+ spotMarket,
116
+ marginCategory
44
117
  );
45
- return [tokenAmountAllAsksFill, worstCaseQuoteTokenAmount];
46
118
  }
119
+
120
+ return {
121
+ weight: weight,
122
+ weightedTokenValue: tokenValue
123
+ .mul(weight)
124
+ .div(SPOT_MARKET_WEIGHT_PRECISION),
125
+ };
126
+ }
127
+
128
+ export function simulateOrderFill(
129
+ tokenAmount: BN,
130
+ tokenValue: BN,
131
+ openOrders: BN,
132
+ strictOraclePrice: StrictOraclePrice,
133
+ spotMarket: SpotMarketAccount,
134
+ marginCategory: MarginCategory
135
+ ): OrderFillSimulation {
136
+ const ordersValue = getTokenValue(openOrders.neg(), spotMarket.decimals, {
137
+ price: strictOraclePrice.max(),
138
+ });
139
+ const tokenAmountAfterFill = tokenAmount.add(openOrders);
140
+ const tokenValueAfterFill = tokenValue.add(ordersValue.neg());
141
+
142
+ const { weight, weightedTokenValue: weightedTokenValueAfterFill } =
143
+ calculateWeightedTokenValue(
144
+ tokenAmountAfterFill,
145
+ tokenValueAfterFill,
146
+ strictOraclePrice.current,
147
+ spotMarket,
148
+ marginCategory
149
+ );
150
+
151
+ const freeCollateralContribution =
152
+ weightedTokenValueAfterFill.add(ordersValue);
153
+
154
+ return {
155
+ tokenAmount: tokenAmountAfterFill,
156
+ ordersValue: ordersValue,
157
+ tokenValue: tokenValueAfterFill,
158
+ weight,
159
+ weightedTokenValue: weightedTokenValueAfterFill,
160
+ freeCollateralContribution,
161
+ };
47
162
  }
@@ -0,0 +1,19 @@
1
+ import { BN } from '@coral-xyz/anchor';
2
+
3
+ export class StrictOraclePrice {
4
+ current: BN;
5
+ twap?: BN;
6
+
7
+ constructor(current: BN, twap?: BN) {
8
+ this.current = current;
9
+ this.twap = twap;
10
+ }
11
+
12
+ public max(): BN {
13
+ return this.twap ? BN.max(this.twap, this.current) : this.current;
14
+ }
15
+
16
+ public min(): BN {
17
+ return this.twap ? BN.min(this.twap, this.current) : this.current;
18
+ }
19
+ }