@metamask-previews/perps-controller 13.1.0-preview-8c2a2eeb2 → 13.1.0-preview-b13d916

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/CHANGELOG.md CHANGED
@@ -9,7 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
9
9
 
10
10
  ### Fixed
11
11
 
12
- - Stop reporting `TPSL_UPDATE_FAILED` from `updatePositionTPSL` when HyperLiquid accepts a trigger with `waitingForTrigger`; accepted triggers without response order IDs are reconciled before mixed-failure cleanup
12
+ - Prevent transient HyperLiquid WebSocket disconnects from failing the first TP/SL update by checking builder-fee approval over HTTP ([#9997](https://github.com/MetaMask/core/pull/9997))
13
+ - Stop reporting `TPSL_UPDATE_FAILED` from `updatePositionTPSL` when HyperLiquid accepts a trigger with `waitingForTrigger`; accepted triggers without response order IDs are reconciled before mixed-failure cleanup ([#9995](https://github.com/MetaMask/core/pull/9995))
13
14
 
14
15
  ## [13.1.0]
15
16
 
@@ -19,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
19
20
 
20
21
  ### Fixed
21
22
 
23
+ - Preserve trigger prices and normalized trigger order types in HyperLiquid historical orders while retaining their lifecycle and execution semantics ([#9982](https://github.com/MetaMask/core/pull/9982)).
22
24
  - Classify `xyz:CBRS` and `xyz:SPCX` as stocks in the Hyperliquid fallback market map ([#9988](https://github.com/MetaMask/core/pull/9988))
23
25
 
24
26
  ## [13.0.0]
@@ -18,6 +18,7 @@ const bignumber_js_1 = require("bignumber.js");
18
18
  const uuid_1 = require("uuid");
19
19
  const eventNames_js_1 = require("../constants/eventNames.cjs");
20
20
  const hyperLiquidConfig_js_1 = require("../constants/hyperLiquidConfig.cjs");
21
+ const orderTypes_js_1 = require("../constants/orderTypes.cjs");
21
22
  const perpsConfig_js_1 = require("../constants/perpsConfig.cjs");
22
23
  const transactionsHistoryConfig_js_1 = require("../constants/transactionsHistoryConfig.cjs");
23
24
  const perpsErrorCodes_js_1 = require("../perpsErrorCodes.cjs");
@@ -38,10 +39,18 @@ const idUtils_js_1 = require("../utils/idUtils.cjs");
38
39
  const marketDataTransform_js_1 = require("../utils/marketDataTransform.cjs");
39
40
  const marketUtils_js_1 = require("../utils/marketUtils.cjs");
40
41
  const orderCalculations_js_1 = require("../utils/orderCalculations.cjs");
41
- const orderTypes_js_1 = require("../utils/orderTypes.cjs");
42
+ const orderTypes_js_2 = require("../utils/orderTypes.cjs");
42
43
  const standaloneInfoClient_js_1 = require("../utils/standaloneInfoClient.cjs");
43
44
  const stringParseUtils_js_1 = require("../utils/stringParseUtils.cjs");
44
45
  // getStreamManagerInstance removed: use this.#deps.streamManager instead
46
+ const HISTORICAL_ORDER_TYPE_BY_DETAILED_TYPE = {
47
+ [orderTypes_js_1.DETAILED_ORDER_TYPES.LIMIT]: 'limit',
48
+ [orderTypes_js_1.DETAILED_ORDER_TYPES.MARKET]: 'market',
49
+ [orderTypes_js_1.DETAILED_ORDER_TYPES.STOP_LIMIT]: 'limit',
50
+ [orderTypes_js_1.DETAILED_ORDER_TYPES.STOP_MARKET]: 'market',
51
+ [orderTypes_js_1.DETAILED_ORDER_TYPES.TAKE_PROFIT_LIMIT]: 'limit',
52
+ [orderTypes_js_1.DETAILED_ORDER_TYPES.TAKE_PROFIT_MARKET]: 'market',
53
+ };
45
54
  /**
46
55
  * Type guard to check if a status is an object (not a string literal like "waitingForFill")
47
56
  * The SDK returns status as a union of object types and string literals.
@@ -349,11 +358,11 @@ function collectPositionTriggerOrders(params) {
349
358
  const triggerOrders = Array.from(byOrderId.values());
350
359
  const takeProfitOrders = triggerOrders.filter((order) => order.direction === 'take_profit');
351
360
  const stopLossOrders = triggerOrders.filter((order) => order.direction !== 'take_profit');
352
- const takeProfitSummaryPrice = (0, orderTypes_js_1.resolvePositionTriggerSummaryPrice)({
361
+ const takeProfitSummaryPrice = (0, orderTypes_js_2.resolvePositionTriggerSummaryPrice)({
353
362
  triggerOrders: takeProfitOrders,
354
363
  scannedPrice: takeProfitPrice,
355
364
  });
356
- const stopLossSummaryPrice = (0, orderTypes_js_1.resolvePositionTriggerSummaryPrice)({
365
+ const stopLossSummaryPrice = (0, orderTypes_js_2.resolvePositionTriggerSummaryPrice)({
357
366
  triggerOrders: stopLossOrders,
358
367
  scannedPrice: stopLossPrice,
359
368
  });
@@ -914,7 +923,7 @@ class HyperLiquidProvider {
914
923
  // single order, so they leave the shared path here — before the order
915
924
  // array, the HIP-3 transfer, and the atomic single-order submit, none of
916
925
  // which describe what a TWAP, a ladder, or a chase does.
917
- if ((0, orderTypes_js_1.isStrategyOrderType)(params.orderType)) {
926
+ if ((0, orderTypes_js_2.isStrategyOrderType)(params.orderType)) {
918
927
  return await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_placeStrategyOrder).call(this, params, params.orderType);
919
928
  }
920
929
  // Extract DEX name for API calls (main DEX = null)
@@ -1095,7 +1104,7 @@ class HyperLiquidProvider {
1095
1104
  if (isMinimumOrderError &&
1096
1105
  retryCount === 0 &&
1097
1106
  !params.reduceOnly &&
1098
- !(0, orderTypes_js_1.isStrategyOrderType)(params.orderType)) {
1107
+ !(0, orderTypes_js_2.isStrategyOrderType)(params.orderType)) {
1099
1108
  let adjustedUsdAmount;
1100
1109
  let originalValue;
1101
1110
  if (params.usdAmount) {
@@ -1325,7 +1334,7 @@ class HyperLiquidProvider {
1325
1334
  // `modify` rebuilds an order as a plain limit/market order, so a trigger
1326
1335
  // on either side of the edit would be silently dropped. Reject a resting
1327
1336
  // trigger order as well as an edit *into* one; cancel and re-place instead.
1328
- if ((0, orderTypes_js_1.isTriggerOrderType)(params.newOrder.orderType)) {
1337
+ if ((0, orderTypes_js_2.isTriggerOrderType)(params.newOrder.orderType)) {
1329
1338
  return {
1330
1339
  success: false,
1331
1340
  error: perpsErrorCodes_js_1.PERPS_ERROR_CODES.ORDER_EDIT_TRIGGER_UNSUPPORTED,
@@ -1336,7 +1345,7 @@ class HyperLiquidProvider {
1336
1345
  // FrontendMarket modification and quietly drop the TWAP schedule, the
1337
1346
  // ladder, or the chase loop the caller asked for. Cancel by the strategy
1338
1347
  // handle and place again.
1339
- if ((0, orderTypes_js_1.isStrategyOrderType)(params.newOrder.orderType)) {
1348
+ if ((0, orderTypes_js_2.isStrategyOrderType)(params.newOrder.orderType)) {
1340
1349
  return {
1341
1350
  success: false,
1342
1351
  error: perpsErrorCodes_js_1.PERPS_ERROR_CODES.ORDER_EDIT_STRATEGY_UNSUPPORTED,
@@ -1454,7 +1463,7 @@ class HyperLiquidProvider {
1454
1463
  // A limit order honours the caller's time in force; validation above has
1455
1464
  // already rejected one on any other order shape.
1456
1465
  t: params.newOrder.orderType === 'limit'
1457
- ? { limit: { tif: (0, orderTypes_js_1.toSDKTimeInForce)(params.newOrder.timeInForce) } }
1466
+ ? { limit: { tif: (0, orderTypes_js_2.toSDKTimeInForce)(params.newOrder.timeInForce) } }
1458
1467
  : { limit: { tif: 'FrontendMarket' } }, // True market order
1459
1468
  c: params.newOrder.clientOrderId
1460
1469
  ? params.newOrder.clientOrderId
@@ -1515,7 +1524,7 @@ class HyperLiquidProvider {
1515
1524
  // A strategy handle is not an exchange order ID, so it cannot go through
1516
1525
  // the single-order cancel below. Callers that omit `orderType` — every
1517
1526
  // existing one — keep the behaviour they have today.
1518
- if (params.orderType && (0, orderTypes_js_1.isStrategyOrderType)(params.orderType)) {
1527
+ if (params.orderType && (0, orderTypes_js_2.isStrategyOrderType)(params.orderType)) {
1519
1528
  return await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_cancelStrategyOrder).call(this, params);
1520
1529
  }
1521
1530
  // Resolve ownership before the first await. A reprice can change the child
@@ -2145,7 +2154,7 @@ class HyperLiquidProvider {
2145
2154
  orderType,
2146
2155
  triggerPrice,
2147
2156
  reduceOnly: true,
2148
- ...((0, orderTypes_js_1.isLimitExecutionOrderType)(orderType) && {
2157
+ ...((0, orderTypes_js_2.isLimitExecutionOrderType)(orderType) && {
2149
2158
  price: order.price,
2150
2159
  }),
2151
2160
  };
@@ -2159,9 +2168,9 @@ class HyperLiquidProvider {
2159
2168
  r: true,
2160
2169
  t: {
2161
2170
  trigger: {
2162
- isMarket: !(0, orderTypes_js_1.isLimitExecutionOrderType)(orderType),
2171
+ isMarket: !(0, orderTypes_js_2.isLimitExecutionOrderType)(orderType),
2163
2172
  triggerPx: triggerPrice,
2164
- tpsl: (0, orderTypes_js_1.getTriggerDirection)(orderType) === 'stop' ? 'sl' : 'tp',
2173
+ tpsl: (0, orderTypes_js_2.getTriggerDirection)(orderType) === 'stop' ? 'sl' : 'tp',
2165
2174
  },
2166
2175
  },
2167
2176
  },
@@ -3099,11 +3108,11 @@ class HyperLiquidProvider {
3099
3108
  });
3100
3109
  return {
3101
3110
  ...position,
3102
- takeProfitPrice: (0, orderTypes_js_1.resolvePositionTriggerSummaryPrice)({
3111
+ takeProfitPrice: (0, orderTypes_js_2.resolvePositionTriggerSummaryPrice)({
3103
3112
  triggerOrders: takeProfitOrders,
3104
3113
  scannedPrice: takeProfitPrice,
3105
3114
  }),
3106
- stopLossPrice: (0, orderTypes_js_1.resolvePositionTriggerSummaryPrice)({
3115
+ stopLossPrice: (0, orderTypes_js_2.resolvePositionTriggerSummaryPrice)({
3107
3116
  triggerOrders: stopLossOrders,
3108
3117
  scannedPrice: stopLossPrice,
3109
3118
  }),
@@ -3244,8 +3253,6 @@ class HyperLiquidProvider {
3244
3253
  // Transform HyperLiquid orders to abstract Order type
3245
3254
  const orders = (rawOrders || []).map((rawOrder) => {
3246
3255
  const { order, status, statusTimestamp } = rawOrder;
3247
- // Normalize side: HyperLiquid uses 'A' (Ask/Sell) and 'B' (Bid/Buy)
3248
- const normalizedSide = order.side === 'B' ? 'buy' : 'sell';
3249
3256
  // Normalize status
3250
3257
  let normalizedStatus;
3251
3258
  switch (status) {
@@ -3278,28 +3285,19 @@ class HyperLiquidProvider {
3278
3285
  default:
3279
3286
  normalizedStatus = 'queued';
3280
3287
  }
3281
- // Calculate filled and remaining size
3282
- const originalSize = parseFloat(order.origSz || order.sz);
3283
- const currentSize = parseFloat(order.sz);
3284
- const filledSize = originalSize - currentSize;
3288
+ const adaptedOrder = (0, hyperLiquidAdapter_js_1.adaptOrderFromSDK)(order, undefined);
3289
+ // limitPx is also populated as a slippage cap for market orders, so the
3290
+ // exchange's detailed type is the reliable execution-mode source.
3291
+ const historicalOrderType = (0, utils_1.hasProperty)(HISTORICAL_ORDER_TYPE_BY_DETAILED_TYPE, order.orderType)
3292
+ ? HISTORICAL_ORDER_TYPE_BY_DETAILED_TYPE[order.orderType]
3293
+ : 'market';
3285
3294
  return {
3286
- orderId: order.oid?.toString() || '',
3287
- symbol: order.coin,
3288
- side: normalizedSide,
3289
- orderType: order.orderType?.toLowerCase().includes('limit')
3290
- ? 'limit'
3291
- : 'market',
3292
- size: order.sz,
3293
- originalSize: order.origSz || order.sz,
3294
- price: order.limitPx || '0',
3295
- filledSize: filledSize.toString(),
3296
- remainingSize: currentSize.toString(),
3295
+ ...adaptedOrder,
3296
+ orderType: historicalOrderType,
3297
+ remainingSize: parseFloat(order.sz).toString(),
3297
3298
  status: normalizedStatus,
3298
3299
  timestamp: statusTimestamp,
3299
3300
  lastUpdated: statusTimestamp,
3300
- detailedOrderType: order.orderType, // Full order type from exchange (e.g., 'Take Profit Limit', 'Stop Market')
3301
- isTrigger: order.isTrigger,
3302
- reduceOnly: order.reduceOnly,
3303
3301
  };
3304
3302
  });
3305
3303
  return orders;
@@ -4166,7 +4164,7 @@ class HyperLiquidProvider {
4166
4164
  // fallback (plain limit, stop_limit, take_profit_limit)
4167
4165
  if (!priceForValidation &&
4168
4166
  params.price &&
4169
- (0, orderTypes_js_1.isLimitExecutionOrderType)(params.orderType)) {
4167
+ (0, orderTypes_js_2.isLimitExecutionOrderType)(params.orderType)) {
4170
4168
  priceForValidation = parseFloat(params.price);
4171
4169
  __classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('Using limit price for order validation (limit order):', {
4172
4170
  size,
@@ -4177,7 +4175,7 @@ class HyperLiquidProvider {
4177
4175
  // have no limit price; the trigger price is the best notional estimate.
4178
4176
  if (!priceForValidation &&
4179
4177
  params.triggerPrice &&
4180
- (0, orderTypes_js_1.isTriggerOrderType)(params.orderType)) {
4178
+ (0, orderTypes_js_2.isTriggerOrderType)(params.orderType)) {
4181
4179
  priceForValidation = parseFloat(params.triggerPrice);
4182
4180
  __classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('Using trigger price for order validation (trigger order):', {
4183
4181
  size,
@@ -5006,7 +5004,7 @@ class HyperLiquidProvider {
5006
5004
  // market order (taker), a stop_limit as a limit order, a scale ladder as the
5007
5005
  // resting limit orders it fans out into, and a TWAP as the marketable
5008
5006
  // suborders it crosses the book with.
5009
- const isMarketExecution = (0, orderTypes_js_1.getTriggerExecution)(orderType) === 'market';
5007
+ const isMarketExecution = (0, orderTypes_js_2.getTriggerExecution)(orderType) === 'market';
5010
5008
  // A chase is post-only by construction, so it can only ever fill as a maker.
5011
5009
  // Quoting it at the taker rate would overstate the fee whatever the caller
5012
5010
  // passes for `isMaker`.
@@ -6998,7 +6996,7 @@ async function _HyperLiquidProvider_queryUserDataAcrossDexs(baseParams, queryFn)
6998
6996
  * @returns Current max fee rate or null if not approved
6999
6997
  */
7000
6998
  async function _HyperLiquidProvider_checkBuilderFeeApproval(builder, userAddress) {
7001
- const infoClient = __classPrivateFieldGet(this, _HyperLiquidProvider_clientService, "f").getInfoClient();
6999
+ const infoClient = __classPrivateFieldGet(this, _HyperLiquidProvider_clientService, "f").getInfoClient({ useHttp: true });
7002
7000
  return infoClient.maxBuilderFee({
7003
7001
  user: userAddress,
7004
7002
  builder,