@drift-labs/sdk 2.74.0-beta.1 → 2.74.0-beta.11

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 (53) hide show
  1. package/VERSION +1 -1
  2. package/lib/adminClient.d.ts +12 -3
  3. package/lib/adminClient.js +60 -32
  4. package/lib/blockhashSubscriber/BlockhashSubscriber.d.ts +21 -0
  5. package/lib/blockhashSubscriber/BlockhashSubscriber.js +73 -0
  6. package/lib/blockhashSubscriber/index.d.ts +1 -0
  7. package/lib/blockhashSubscriber/index.js +17 -0
  8. package/lib/blockhashSubscriber/types.d.ts +7 -0
  9. package/lib/blockhashSubscriber/types.js +2 -0
  10. package/lib/dlob/orderBookLevels.js +47 -12
  11. package/lib/driftClient.d.ts +5 -0
  12. package/lib/driftClient.js +17 -0
  13. package/lib/events/parse.d.ts +1 -1
  14. package/lib/events/parse.js +12 -12
  15. package/lib/idl/drift.json +85 -0
  16. package/lib/index.d.ts +1 -0
  17. package/lib/index.js +1 -0
  18. package/lib/math/funding.js +0 -6
  19. package/lib/math/oracles.js +1 -1
  20. package/lib/math/tiers.js +1 -1
  21. package/lib/oracles/prelaunchOracleClient.js +1 -0
  22. package/lib/oracles/types.d.ts +1 -0
  23. package/lib/tx/baseTxSender.d.ts +1 -1
  24. package/lib/tx/baseTxSender.js +9 -2
  25. package/lib/tx/fastSingleTxSender.d.ts +1 -1
  26. package/lib/tx/fastSingleTxSender.js +11 -3
  27. package/lib/tx/types.d.ts +1 -1
  28. package/lib/types.d.ts +3 -0
  29. package/lib/types.js +1 -0
  30. package/lib/user.d.ts +6 -4
  31. package/lib/user.js +24 -21
  32. package/package.json +1 -1
  33. package/src/adminClient.ts +297 -87
  34. package/src/blockhashSubscriber/BlockhashSubscriber.ts +108 -0
  35. package/src/blockhashSubscriber/index.ts +1 -0
  36. package/src/blockhashSubscriber/types.ts +8 -0
  37. package/src/dlob/orderBookLevels.ts +51 -15
  38. package/src/driftClient.ts +37 -0
  39. package/src/events/parse.ts +26 -12
  40. package/src/idl/drift.json +85 -0
  41. package/src/index.ts +1 -0
  42. package/src/math/funding.ts +0 -4
  43. package/src/math/oracles.ts +1 -1
  44. package/src/math/tiers.ts +1 -1
  45. package/src/oracles/prelaunchOracleClient.ts +1 -0
  46. package/src/oracles/types.ts +1 -0
  47. package/src/tx/baseTxSender.ts +12 -4
  48. package/src/tx/fastSingleTxSender.ts +13 -5
  49. package/src/tx/types.ts +2 -1
  50. package/src/types.ts +1 -0
  51. package/src/user.ts +32 -30
  52. package/tests/amm/test.ts +3 -1
  53. package/tests/dlob/test.ts +57 -0
@@ -1074,6 +1074,43 @@ export class DriftClient {
1074
1074
  return ix;
1075
1075
  }
1076
1076
 
1077
+ public async updateUserReduceOnly(
1078
+ updates: { reduceOnly: boolean; subAccountId: number }[]
1079
+ ): Promise<TransactionSignature> {
1080
+ const ixs = await Promise.all(
1081
+ updates.map(async ({ reduceOnly, subAccountId }) => {
1082
+ return await this.getUpdateUserReduceOnlyIx(reduceOnly, subAccountId);
1083
+ })
1084
+ );
1085
+
1086
+ const tx = await this.buildTransaction(ixs, this.txParams);
1087
+
1088
+ const { txSig } = await this.sendTransaction(tx, [], this.opts);
1089
+ return txSig;
1090
+ }
1091
+
1092
+ public async getUpdateUserReduceOnlyIx(
1093
+ reduceOnly: boolean,
1094
+ subAccountId: number
1095
+ ) {
1096
+ const ix = await this.program.instruction.updateUserReduceOnly(
1097
+ subAccountId,
1098
+ reduceOnly,
1099
+ {
1100
+ accounts: {
1101
+ user: getUserAccountPublicKeySync(
1102
+ this.program.programId,
1103
+ this.wallet.publicKey,
1104
+ subAccountId
1105
+ ),
1106
+ authority: this.wallet.publicKey,
1107
+ },
1108
+ }
1109
+ );
1110
+
1111
+ return ix;
1112
+ }
1113
+
1077
1114
  public async fetchAllUserAccounts(
1078
1115
  includeIdle = true
1079
1116
  ): Promise<ProgramAccount<UserAccount>[]> {
@@ -1,13 +1,16 @@
1
1
  import { Program, Event } from '@coral-xyz/anchor';
2
2
 
3
3
  const driftProgramId = 'dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH';
4
- const driftProgramStart = `Program ${driftProgramId} invoke`;
5
4
  const PROGRAM_LOG = 'Program log: ';
6
5
  const PROGRAM_DATA = 'Program data: ';
7
6
  const PROGRAM_LOG_START_INDEX = PROGRAM_LOG.length;
8
7
  const PROGRAM_DATA_START_INDEX = PROGRAM_DATA.length;
9
8
 
10
- export function parseLogs(program: Program, logs: string[]): Event[] {
9
+ export function parseLogs(
10
+ program: Program,
11
+ logs: string[],
12
+ programId = driftProgramId
13
+ ): Event[] {
11
14
  const events = [];
12
15
  const execution = new ExecutionContext();
13
16
  for (const log of logs) {
@@ -15,7 +18,12 @@ export function parseLogs(program: Program, logs: string[]): Event[] {
15
18
  break;
16
19
  }
17
20
 
18
- const [event, newProgram, didPop] = handleLog(execution, log, program);
21
+ const [event, newProgram, didPop] = handleLog(
22
+ execution,
23
+ log,
24
+ program,
25
+ programId
26
+ );
19
27
  if (event) {
20
28
  events.push(event);
21
29
  }
@@ -32,22 +40,24 @@ export function parseLogs(program: Program, logs: string[]): Event[] {
32
40
  function handleLog(
33
41
  execution: ExecutionContext,
34
42
  log: string,
35
- program: Program
43
+ program: Program,
44
+ programId = driftProgramId
36
45
  ): [Event | null, string | null, boolean] {
37
46
  // Executing program is drift program.
38
- if (execution.stack.length > 0 && execution.program() === driftProgramId) {
39
- return handleProgramLog(log, program);
47
+ if (execution.stack.length > 0 && execution.program() === programId) {
48
+ return handleProgramLog(log, program, programId);
40
49
  }
41
50
  // Executing program is not drift program.
42
51
  else {
43
- return [null, ...handleSystemLog(log)];
52
+ return [null, ...handleSystemLog(log, programId)];
44
53
  }
45
54
  }
46
55
 
47
56
  // Handles logs from *drift* program.
48
57
  function handleProgramLog(
49
58
  log: string,
50
- program: Program
59
+ program: Program,
60
+ programId = driftProgramId
51
61
  ): [Event | null, string | null, boolean] {
52
62
  // This is a `msg!` log or a `sol_log_data` log.
53
63
  if (log.startsWith(PROGRAM_LOG)) {
@@ -59,21 +69,25 @@ function handleProgramLog(
59
69
  const event = program.coder.events.decode(logStr);
60
70
  return [event, null, false];
61
71
  } else {
62
- return [null, ...handleSystemLog(log)];
72
+ return [null, ...handleSystemLog(log, programId)];
63
73
  }
64
74
  }
65
75
 
66
76
  // Handles logs when the current program being executing is *not* drift.
67
- function handleSystemLog(log: string): [string | null, boolean] {
77
+ function handleSystemLog(
78
+ log: string,
79
+ programId = driftProgramId
80
+ ): [string | null, boolean] {
68
81
  // System component.
69
82
  const logStart = log.split(':')[0];
83
+ const programStart = `Program ${programId} invoke`;
70
84
 
71
85
  // Did the program finish executing?
72
86
  if (logStart.match(/^Program (.*) success/g) !== null) {
73
87
  return [null, true];
74
88
  // Recursive call.
75
- } else if (logStart.startsWith(driftProgramStart)) {
76
- return [driftProgramId, false];
89
+ } else if (logStart.startsWith(programStart)) {
90
+ return [programId, false];
77
91
  }
78
92
  // CPI call.
79
93
  else if (logStart.includes('invoke')) {
@@ -2651,10 +2651,40 @@
2651
2651
  "name": "liquidatorFee",
2652
2652
  "type": "u32"
2653
2653
  },
2654
+ {
2655
+ "name": "ifLiquidationFee",
2656
+ "type": "u32"
2657
+ },
2654
2658
  {
2655
2659
  "name": "activeStatus",
2656
2660
  "type": "bool"
2657
2661
  },
2662
+ {
2663
+ "name": "assetTier",
2664
+ "type": {
2665
+ "defined": "AssetTier"
2666
+ }
2667
+ },
2668
+ {
2669
+ "name": "scaleInitialAssetWeightStart",
2670
+ "type": "u64"
2671
+ },
2672
+ {
2673
+ "name": "withdrawGuardThreshold",
2674
+ "type": "u64"
2675
+ },
2676
+ {
2677
+ "name": "orderTickSize",
2678
+ "type": "u64"
2679
+ },
2680
+ {
2681
+ "name": "orderStepSize",
2682
+ "type": "u64"
2683
+ },
2684
+ {
2685
+ "name": "ifTotalFactor",
2686
+ "type": "u32"
2687
+ },
2658
2688
  {
2659
2689
  "name": "name",
2660
2690
  "type": {
@@ -2943,10 +2973,62 @@
2943
2973
  "name": "liquidatorFee",
2944
2974
  "type": "u32"
2945
2975
  },
2976
+ {
2977
+ "name": "ifLiquidationFee",
2978
+ "type": "u32"
2979
+ },
2980
+ {
2981
+ "name": "imfFactor",
2982
+ "type": "u32"
2983
+ },
2946
2984
  {
2947
2985
  "name": "activeStatus",
2948
2986
  "type": "bool"
2949
2987
  },
2988
+ {
2989
+ "name": "baseSpread",
2990
+ "type": "u32"
2991
+ },
2992
+ {
2993
+ "name": "maxSpread",
2994
+ "type": "u32"
2995
+ },
2996
+ {
2997
+ "name": "maxOpenInterest",
2998
+ "type": "u128"
2999
+ },
3000
+ {
3001
+ "name": "maxRevenueWithdrawPerPeriod",
3002
+ "type": "u64"
3003
+ },
3004
+ {
3005
+ "name": "quoteMaxInsurance",
3006
+ "type": "u64"
3007
+ },
3008
+ {
3009
+ "name": "orderStepSize",
3010
+ "type": "u64"
3011
+ },
3012
+ {
3013
+ "name": "orderTickSize",
3014
+ "type": "u64"
3015
+ },
3016
+ {
3017
+ "name": "minOrderSize",
3018
+ "type": "u64"
3019
+ },
3020
+ {
3021
+ "name": "concentrationCoefScale",
3022
+ "type": "u128"
3023
+ },
3024
+ {
3025
+ "name": "curveUpdateIntensity",
3026
+ "type": "u8"
3027
+ },
3028
+ {
3029
+ "name": "ammJitIntensity",
3030
+ "type": "u8"
3031
+ },
2950
3032
  {
2951
3033
  "name": "name",
2952
3034
  "type": {
@@ -9119,6 +9201,9 @@
9119
9201
  {
9120
9202
  "name": "Speculative"
9121
9203
  },
9204
+ {
9205
+ "name": "HighlySpeculative"
9206
+ },
9122
9207
  {
9123
9208
  "name": "Isolated"
9124
9209
  }
package/src/index.ts CHANGED
@@ -99,5 +99,6 @@ export * from './auctionSubscriber';
99
99
  export * from './auctionSubscriber/types';
100
100
  export * from './memcmp';
101
101
  export * from './decode/user';
102
+ export * from './blockhashSubscriber';
102
103
 
103
104
  export { BN, PublicKey, pyth };
@@ -252,10 +252,6 @@ function getMaxPriceDivergenceForFundingRate(
252
252
  return oracleTwap.divn(33);
253
253
  } else if (isVariant(market.contractTier, 'c')) {
254
254
  return oracleTwap.divn(20);
255
- } else if (isVariant(market.contractTier, 'speculative')) {
256
- return oracleTwap.divn(10);
257
- } else if (isVariant(market.contractTier, 'isolated')) {
258
- return oracleTwap.divn(10);
259
255
  } else {
260
256
  return oracleTwap.divn(10);
261
257
  }
@@ -39,7 +39,7 @@ export function getMaxConfidenceIntervalMultiplier(
39
39
  maxConfidenceIntervalMultiplier = new BN(2);
40
40
  } else if (isVariant(market.contractTier, 'speculative')) {
41
41
  maxConfidenceIntervalMultiplier = new BN(10);
42
- } else if (isVariant(market.contractTier, 'isolated')) {
42
+ } else {
43
43
  maxConfidenceIntervalMultiplier = new BN(50);
44
44
  }
45
45
  return maxConfidenceIntervalMultiplier;
package/src/math/tiers.ts CHANGED
@@ -9,7 +9,7 @@ export function getPerpMarketTierNumber(perpMarket: PerpMarketAccount): number {
9
9
  return 2;
10
10
  } else if (isVariant(perpMarket.contractTier, 'speculative')) {
11
11
  return 3;
12
- } else if (isVariant(perpMarket.contractTier, 'isolated')) {
12
+ } else if (isVariant(perpMarket.contractTier, 'highlySpeculative')) {
13
13
  return 4;
14
14
  } else {
15
15
  return 5;
@@ -31,6 +31,7 @@ export class PrelaunchOracleClient implements OracleClient {
31
31
  slot: prelaunchOracle.ammLastUpdateSlot,
32
32
  confidence: prelaunchOracle.confidence,
33
33
  hasSufficientNumberOfDataPoints: true,
34
+ maxPrice: prelaunchOracle.maxPrice,
34
35
  };
35
36
  }
36
37
  }
@@ -9,6 +9,7 @@ export type OraclePriceData = {
9
9
  hasSufficientNumberOfDataPoints: boolean;
10
10
  twap?: BN;
11
11
  twapConfidence?: BN;
12
+ maxPrice?: BN; // pre-launch markets only
12
13
  };
13
14
 
14
15
  export type OracleInfo = {
@@ -112,7 +112,8 @@ export abstract class BaseTxSender implements TxSender {
112
112
  ixs: TransactionInstruction[],
113
113
  lookupTableAccounts: AddressLookupTableAccount[],
114
114
  additionalSigners?: Array<Signer>,
115
- opts?: ConfirmOptions
115
+ opts?: ConfirmOptions,
116
+ blockhash?: string
116
117
  ): Promise<VersionedTransaction> {
117
118
  if (additionalSigners === undefined) {
118
119
  additionalSigners = [];
@@ -121,11 +122,18 @@ export abstract class BaseTxSender implements TxSender {
121
122
  opts = this.opts;
122
123
  }
123
124
 
125
+ let recentBlockhash = '';
126
+ if (blockhash) {
127
+ recentBlockhash = blockhash;
128
+ } else {
129
+ recentBlockhash = (
130
+ await this.connection.getLatestBlockhash(opts.preflightCommitment)
131
+ ).blockhash;
132
+ }
133
+
124
134
  const message = new TransactionMessage({
125
135
  payerKey: this.wallet.publicKey,
126
- recentBlockhash: (
127
- await this.connection.getLatestBlockhash(opts.preflightCommitment)
128
- ).blockhash,
136
+ recentBlockhash,
129
137
  instructions: ixs,
130
138
  }).compileToV0Message(lookupTableAccounts);
131
139
 
@@ -109,7 +109,8 @@ export class FastSingleTxSender extends BaseTxSender {
109
109
  ixs: TransactionInstruction[],
110
110
  lookupTableAccounts: AddressLookupTableAccount[],
111
111
  additionalSigners?: Array<Signer>,
112
- opts?: ConfirmOptions
112
+ opts?: ConfirmOptions,
113
+ blockhash?: string
113
114
  ): Promise<VersionedTransaction> {
114
115
  if (additionalSigners === undefined) {
115
116
  additionalSigners = [];
@@ -118,12 +119,19 @@ export class FastSingleTxSender extends BaseTxSender {
118
119
  opts = this.opts;
119
120
  }
120
121
 
121
- const message = new TransactionMessage({
122
- payerKey: this.wallet.publicKey,
123
- recentBlockhash:
122
+ let recentBlockhash = '';
123
+ if (blockhash) {
124
+ recentBlockhash = blockhash;
125
+ } else {
126
+ recentBlockhash =
124
127
  this.recentBlockhash ??
125
128
  (await this.connection.getLatestBlockhash(opts.preflightCommitment))
126
- .blockhash,
129
+ .blockhash;
130
+ }
131
+
132
+ const message = new TransactionMessage({
133
+ payerKey: this.wallet.publicKey,
134
+ recentBlockhash,
127
135
  instructions: ixs,
128
136
  }).compileToV0Message(lookupTableAccounts);
129
137
 
package/src/tx/types.ts CHANGED
@@ -47,7 +47,8 @@ export interface TxSender {
47
47
  ixs: TransactionInstruction[],
48
48
  lookupTableAccounts: AddressLookupTableAccount[],
49
49
  additionalSigners?: Array<Signer>,
50
- opts?: ConfirmOptions
50
+ opts?: ConfirmOptions,
51
+ blockhash?: string
51
52
  ): Promise<VersionedTransaction>;
52
53
 
53
54
  sendRawTransaction(
package/src/types.ts CHANGED
@@ -58,6 +58,7 @@ export class ContractTier {
58
58
  static readonly B = { b: {} };
59
59
  static readonly C = { c: {} };
60
60
  static readonly SPECULATIVE = { speculative: {} };
61
+ static readonly HIGHLY_SPECULATIVE = { highlySpeculative: {} };
61
62
  static readonly ISOLATED = { isolated: {} };
62
63
  }
63
64
 
package/src/user.ts CHANGED
@@ -1975,21 +1975,25 @@ export class User {
1975
1975
  /**
1976
1976
  * Calculate the liquidation price of a perp position, with optional parameter to calculate the liquidation price after a trade
1977
1977
  * @param marketIndex
1978
- * @param positionBaseSizeChange // change in position size to calculate liquidation price for : Precision 10^13
1978
+ * @param positionBaseSizeChange // change in position size to calculate liquidation price for : Precision 10^9
1979
+ * @param estimatedEntryPrice
1979
1980
  * @param marginCategory // allow Initial to be passed in if we are trying to calculate price for DLP de-risking
1981
+ * @param includeOpenOrders
1980
1982
  * @returns Precision : PRICE_PRECISION
1981
1983
  */
1982
1984
  public liquidationPrice(
1983
1985
  marketIndex: number,
1984
1986
  positionBaseSizeChange: BN = ZERO,
1985
1987
  estimatedEntryPrice: BN = ZERO,
1986
- marginCategory: MarginCategory = 'Maintenance'
1988
+ marginCategory: MarginCategory = 'Maintenance',
1989
+ includeOpenOrders = false
1987
1990
  ): BN {
1988
1991
  const totalCollateral = this.getTotalCollateral(marginCategory);
1989
1992
  const marginRequirement = this.getMarginRequirement(
1990
1993
  marginCategory,
1991
1994
  undefined,
1992
- false
1995
+ false,
1996
+ includeOpenOrders
1993
1997
  );
1994
1998
  let freeCollateral = BN.max(ZERO, totalCollateral.sub(marginRequirement));
1995
1999
 
@@ -2015,7 +2019,8 @@ export class User {
2015
2019
  oraclePrice,
2016
2020
  currentPerpPosition,
2017
2021
  positionBaseSizeChange,
2018
- estimatedEntryPrice
2022
+ estimatedEntryPrice,
2023
+ includeOpenOrders
2019
2024
  );
2020
2025
 
2021
2026
  freeCollateral = freeCollateral.add(freeCollateralChangeFromNewPosition);
@@ -2024,7 +2029,8 @@ export class User {
2024
2029
  market,
2025
2030
  currentPerpPosition,
2026
2031
  positionBaseSizeChange,
2027
- marginCategory
2032
+ marginCategory,
2033
+ includeOpenOrders
2028
2034
  );
2029
2035
 
2030
2036
  if (!freeCollateralDelta) {
@@ -2082,7 +2088,8 @@ export class User {
2082
2088
  oraclePrice: BN,
2083
2089
  perpPosition: PerpPosition,
2084
2090
  positionBaseSizeChange: BN,
2085
- estimatedEntryPrice: BN
2091
+ estimatedEntryPrice: BN,
2092
+ includeOpenOrders: boolean
2086
2093
  ): BN {
2087
2094
  let freeCollateralChange = ZERO;
2088
2095
 
@@ -2097,8 +2104,6 @@ export class User {
2097
2104
  if (positionBaseSizeChange.gt(ZERO)) {
2098
2105
  freeCollateralChange = costBasis.sub(newPositionValue);
2099
2106
  } else {
2100
- console.log('newPositionValue', newPositionValue.toString());
2101
- console.log('costBasis', costBasis.toString());
2102
2107
  freeCollateralChange = newPositionValue.sub(costBasis);
2103
2108
  }
2104
2109
 
@@ -2111,24 +2116,23 @@ export class User {
2111
2116
  freeCollateralChange = freeCollateralChange.sub(takerFee);
2112
2117
  }
2113
2118
 
2114
- const worstCaseBaseAssetAmount =
2115
- calculateWorstCaseBaseAssetAmount(perpPosition);
2119
+ const baseAssetAmount = includeOpenOrders
2120
+ ? calculateWorstCaseBaseAssetAmount(perpPosition)
2121
+ : perpPosition.baseAssetAmount;
2116
2122
 
2117
- const newWorstCaseBaseAssetAmount = worstCaseBaseAssetAmount.add(
2118
- positionBaseSizeChange
2119
- );
2123
+ const newBaseAssetAmount = baseAssetAmount.add(positionBaseSizeChange);
2120
2124
 
2121
2125
  const newMarginRatio = calculateMarketMarginRatio(
2122
2126
  market,
2123
- newWorstCaseBaseAssetAmount.abs(),
2127
+ newBaseAssetAmount.abs(),
2124
2128
  'Maintenance'
2125
2129
  );
2126
2130
 
2127
2131
  // update free collateral to account for new margin requirement from position change
2128
2132
  freeCollateralChange = freeCollateralChange.sub(
2129
- newWorstCaseBaseAssetAmount
2133
+ newBaseAssetAmount
2130
2134
  .abs()
2131
- .sub(worstCaseBaseAssetAmount.abs())
2135
+ .sub(baseAssetAmount.abs())
2132
2136
  .mul(oraclePrice)
2133
2137
  .div(BASE_PRECISION)
2134
2138
  .mul(new BN(newMarginRatio))
@@ -2142,25 +2146,23 @@ export class User {
2142
2146
  market: PerpMarketAccount,
2143
2147
  perpPosition: PerpPosition,
2144
2148
  positionBaseSizeChange: BN,
2145
- marginCategory: MarginCategory = 'Maintenance'
2149
+ marginCategory: MarginCategory = 'Maintenance',
2150
+ includeOpenOrders = false
2146
2151
  ): BN | undefined {
2147
- const currentBaseAssetAmount = perpPosition.baseAssetAmount;
2152
+ const baseAssetAmount = includeOpenOrders
2153
+ ? calculateWorstCaseBaseAssetAmount(perpPosition)
2154
+ : perpPosition.baseAssetAmount;
2148
2155
 
2149
- const worstCaseBaseAssetAmount =
2150
- calculateWorstCaseBaseAssetAmount(perpPosition);
2151
- const orderBaseAssetAmount = worstCaseBaseAssetAmount.sub(
2152
- currentBaseAssetAmount
2153
- );
2154
- const proposedBaseAssetAmount = currentBaseAssetAmount.add(
2155
- positionBaseSizeChange
2156
- );
2157
- const proposedWorstCaseBaseAssetAmount = worstCaseBaseAssetAmount.add(
2158
- positionBaseSizeChange
2156
+ // zero if include orders == false
2157
+ const orderBaseAssetAmount = baseAssetAmount.sub(
2158
+ perpPosition.baseAssetAmount
2159
2159
  );
2160
2160
 
2161
+ const proposedBaseAssetAmount = baseAssetAmount.add(positionBaseSizeChange);
2162
+
2161
2163
  const marginRatio = calculateMarketMarginRatio(
2162
2164
  market,
2163
- proposedWorstCaseBaseAssetAmount.abs(),
2165
+ proposedBaseAssetAmount.abs(),
2164
2166
  marginCategory,
2165
2167
  this.getUserAccount().maxMarginRatio
2166
2168
  );
@@ -2168,7 +2170,7 @@ export class User {
2168
2170
  .mul(QUOTE_PRECISION)
2169
2171
  .div(MARGIN_PRECISION);
2170
2172
 
2171
- if (proposedWorstCaseBaseAssetAmount.eq(ZERO)) {
2173
+ if (proposedBaseAssetAmount.eq(ZERO)) {
2172
2174
  return undefined;
2173
2175
  }
2174
2176
 
package/tests/amm/test.ts CHANGED
@@ -1193,7 +1193,9 @@ describe('AMM Tests', () => {
1193
1193
  };
1194
1194
 
1195
1195
  // good oracle
1196
- assert(isOracleValid(mockMarket1, oraclePriceData, oracleGuardRails, slot + 5));
1196
+ assert(
1197
+ isOracleValid(mockMarket1, oraclePriceData, oracleGuardRails, slot + 5)
1198
+ );
1197
1199
 
1198
1200
  // conf too high
1199
1201
  assert(
@@ -6614,6 +6614,63 @@ describe('Uncross L2', () => {
6614
6614
  );
6615
6615
  });
6616
6616
 
6617
+ it('Handles user crossing bid in second level', () => {
6618
+ const oraclePrice = new BN(190.3843 * PRICE_PRECISION.toNumber());
6619
+ const bids = [
6620
+ [190.59, 2],
6621
+ [190.588, 58.3],
6622
+ [190.5557, 5],
6623
+ [190.5547, 5],
6624
+ [190.5508, 5],
6625
+ [190.541, 2],
6626
+ [190.5099, 49.1],
6627
+ [190.5, 60],
6628
+ ].map(([price, size]) => ({
6629
+ price: new BN(price * PRICE_PRECISION.toNumber()),
6630
+ size: new BN(size * BASE_PRECISION.toNumber()),
6631
+ sources: { vamm: new BN(size * BASE_PRECISION.toNumber()) },
6632
+ }));
6633
+
6634
+ const asks = [
6635
+ [190.5, 86.5],
6636
+ [190.6159, 1],
6637
+ [190.656, 10.5],
6638
+ [190.6561, 1],
6639
+ [190.6585, 5],
6640
+ [190.6595, 5],
6641
+ [190.6596, 5],
6642
+ ].map(([price, size]) => ({
6643
+ price: new BN(price * PRICE_PRECISION.toNumber()),
6644
+ size: new BN(size * BASE_PRECISION.toNumber()),
6645
+ sources: { vamm: new BN(size * BASE_PRECISION.toNumber()) },
6646
+ }));
6647
+
6648
+ expect(asksAreSortedAsc(asks), 'Input asks are ascending').to.be.true;
6649
+ expect(bidsAreSortedDesc(bids), 'Input bids are descending').to.be.true;
6650
+
6651
+ const groupingSize = new BN('100');
6652
+
6653
+ const userBidPrice = new BN(190.588 * PRICE_PRECISION.toNumber());
6654
+ const userBids = new Set<string>([userBidPrice.toString()]);
6655
+
6656
+ const { bids: newBids, asks: newAsks } = uncrossL2(
6657
+ bids,
6658
+ asks,
6659
+ oraclePrice,
6660
+ oraclePrice,
6661
+ oraclePrice,
6662
+ groupingSize,
6663
+ userBids,
6664
+ new Set<string>()
6665
+ );
6666
+
6667
+ expect(asksAreSortedAsc(newAsks), 'Uncrossed asks are ascending').to.be
6668
+ .true;
6669
+ expect(bidsAreSortedDesc(newBids), 'Uncrossed bids are descending').to.be
6670
+ .true;
6671
+ expect(newBids[0].price.toString()).to.equal(userBidPrice.toString());
6672
+ });
6673
+
6617
6674
  it('Handles edge case bide and asks with large cross and an overlapping level', () => {
6618
6675
  const bids = [
6619
6676
  '104411000',