@metamask-previews/perps-controller 11.0.0-preview-9c0f91815 → 11.0.0-preview-e57e5c3dc
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 +0 -49
- package/dist/PerpsController.cjs.map +1 -1
- package/dist/PerpsController.d.cts +1 -1
- package/dist/PerpsController.d.cts.map +1 -1
- package/dist/PerpsController.d.mts +1 -1
- package/dist/PerpsController.d.mts.map +1 -1
- package/dist/PerpsController.mjs.map +1 -1
- package/dist/constants/eventNames.cjs +0 -4
- package/dist/constants/eventNames.cjs.map +1 -1
- package/dist/constants/eventNames.d.cts +0 -3
- package/dist/constants/eventNames.d.cts.map +1 -1
- package/dist/constants/eventNames.d.mts +0 -3
- package/dist/constants/eventNames.d.mts.map +1 -1
- package/dist/constants/eventNames.mjs +0 -4
- package/dist/constants/eventNames.mjs.map +1 -1
- package/dist/constants/perpsConfig.cjs +1 -54
- package/dist/constants/perpsConfig.cjs.map +1 -1
- package/dist/constants/perpsConfig.d.cts +2 -71
- package/dist/constants/perpsConfig.d.cts.map +1 -1
- package/dist/constants/perpsConfig.d.mts +2 -71
- package/dist/constants/perpsConfig.d.mts.map +1 -1
- package/dist/constants/perpsConfig.mjs +0 -53
- package/dist/constants/perpsConfig.mjs.map +1 -1
- package/dist/index.cjs +3 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -4
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +4 -4
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +2 -2
- package/dist/index.mjs.map +1 -1
- package/dist/perpsErrorCodes.cjs +0 -20
- package/dist/perpsErrorCodes.cjs.map +1 -1
- package/dist/perpsErrorCodes.d.cts +0 -19
- package/dist/perpsErrorCodes.d.cts.map +1 -1
- package/dist/perpsErrorCodes.d.mts +0 -19
- package/dist/perpsErrorCodes.d.mts.map +1 -1
- package/dist/perpsErrorCodes.mjs +0 -20
- package/dist/perpsErrorCodes.mjs.map +1 -1
- package/dist/providers/HyperLiquidProvider.cjs +14 -1276
- package/dist/providers/HyperLiquidProvider.cjs.map +1 -1
- package/dist/providers/HyperLiquidProvider.d.cts.map +1 -1
- package/dist/providers/HyperLiquidProvider.d.mts.map +1 -1
- package/dist/providers/HyperLiquidProvider.mjs +17 -1279
- package/dist/providers/HyperLiquidProvider.mjs.map +1 -1
- package/dist/types/index.cjs.map +1 -1
- package/dist/types/index.d.cts +2 -40
- package/dist/types/index.d.cts.map +1 -1
- package/dist/types/index.d.mts +2 -40
- package/dist/types/index.d.mts.map +1 -1
- package/dist/types/index.mjs.map +1 -1
- package/dist/types/perps-types.cjs.map +1 -1
- package/dist/types/perps-types.d.cts +2 -33
- package/dist/types/perps-types.d.cts.map +1 -1
- package/dist/types/perps-types.d.mts +2 -33
- package/dist/types/perps-types.d.mts.map +1 -1
- package/dist/types/perps-types.mjs.map +1 -1
- package/dist/utils/hyperLiquidValidation.cjs +3 -192
- package/dist/utils/hyperLiquidValidation.cjs.map +1 -1
- package/dist/utils/hyperLiquidValidation.d.cts +3 -30
- package/dist/utils/hyperLiquidValidation.d.cts.map +1 -1
- package/dist/utils/hyperLiquidValidation.d.mts +3 -30
- package/dist/utils/hyperLiquidValidation.d.mts.map +1 -1
- package/dist/utils/hyperLiquidValidation.mjs +5 -194
- package/dist/utils/hyperLiquidValidation.mjs.map +1 -1
- package/dist/utils/orderCalculations.cjs +1 -131
- package/dist/utils/orderCalculations.cjs.map +1 -1
- package/dist/utils/orderCalculations.d.cts +3 -88
- package/dist/utils/orderCalculations.d.cts.map +1 -1
- package/dist/utils/orderCalculations.d.mts +3 -88
- package/dist/utils/orderCalculations.d.mts.map +1 -1
- package/dist/utils/orderCalculations.mjs +2 -128
- package/dist/utils/orderCalculations.mjs.map +1 -1
- package/dist/utils/orderTypes.cjs +4 -56
- package/dist/utils/orderTypes.cjs.map +1 -1
- package/dist/utils/orderTypes.d.cts +3 -32
- package/dist/utils/orderTypes.d.cts.map +1 -1
- package/dist/utils/orderTypes.d.mts +3 -32
- package/dist/utils/orderTypes.d.mts.map +1 -1
- package/dist/utils/orderTypes.mjs +3 -54
- package/dist/utils/orderTypes.mjs.map +1 -1
- package/package.json +2 -2
|
@@ -10,7 +10,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
10
10
|
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
11
11
|
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
12
12
|
};
|
|
13
|
-
var _HyperLiquidProvider_instances, _HyperLiquidProvider_deps, _HyperLiquidProvider_clientService, _HyperLiquidProvider_walletService, _HyperLiquidProvider_subscriptionService, _HyperLiquidProvider_symbolToAssetId, _HyperLiquidProvider_userFeeCache, _HyperLiquidProvider_maxLeverageCache, _HyperLiquidProvider_cachedMetaByDex, _HyperLiquidProvider_cachedMarketDataWithPrices, _HyperLiquidProvider_cachedSpotMeta, _HyperLiquidProvider_dexDiscoveryCache, _HyperLiquidProvider_referralCheckCache, _HyperLiquidProvider_builderFeeCheckCache, _HyperLiquidProvider_ensureReadyPromise, _HyperLiquidProvider_pendingBuilderFeeApprovals, _HyperLiquidProvider_compiledAllowlistPatterns, _HyperLiquidProvider_compiledBlocklistPatterns, _HyperLiquidProvider_userFeeDiscountBips, _HyperLiquidProvider_hip3Enabled, _HyperLiquidProvider_allowlistMarkets, _HyperLiquidProvider_blocklistMarkets, _HyperLiquidProvider_useUnifiedAccount, _HyperLiquidProvider_dexDiscoveryComplete, _HyperLiquidProvider_unifiedAccountSetupNeedsRetry, _HyperLiquidProvider_pendingValidatedDexsPromise, _HyperLiquidProvider_cachedUsdcTokenId, _HyperLiquidProvider_errorMappings,
|
|
13
|
+
var _HyperLiquidProvider_instances, _HyperLiquidProvider_deps, _HyperLiquidProvider_clientService, _HyperLiquidProvider_walletService, _HyperLiquidProvider_subscriptionService, _HyperLiquidProvider_symbolToAssetId, _HyperLiquidProvider_userFeeCache, _HyperLiquidProvider_maxLeverageCache, _HyperLiquidProvider_cachedMetaByDex, _HyperLiquidProvider_cachedMarketDataWithPrices, _HyperLiquidProvider_cachedSpotMeta, _HyperLiquidProvider_dexDiscoveryCache, _HyperLiquidProvider_referralCheckCache, _HyperLiquidProvider_builderFeeCheckCache, _HyperLiquidProvider_ensureReadyPromise, _HyperLiquidProvider_pendingBuilderFeeApprovals, _HyperLiquidProvider_compiledAllowlistPatterns, _HyperLiquidProvider_compiledBlocklistPatterns, _HyperLiquidProvider_userFeeDiscountBips, _HyperLiquidProvider_hip3Enabled, _HyperLiquidProvider_allowlistMarkets, _HyperLiquidProvider_blocklistMarkets, _HyperLiquidProvider_useUnifiedAccount, _HyperLiquidProvider_dexDiscoveryComplete, _HyperLiquidProvider_unifiedAccountSetupNeedsRetry, _HyperLiquidProvider_pendingValidatedDexsPromise, _HyperLiquidProvider_cachedUsdcTokenId, _HyperLiquidProvider_errorMappings, _HyperLiquidProvider_clientsInitialized, _HyperLiquidProvider_initializationPromise, _HyperLiquidProvider_messenger, _HyperLiquidProvider_builderAddressTestnet, _HyperLiquidProvider_builderAddressMainnet, _HyperLiquidProvider_priceDeviationLimit, _HyperLiquidProvider_compilePatternsSafely, _HyperLiquidProvider_ensureClientsInitialized, _HyperLiquidProvider_isWalletOnHyperliquid, _HyperLiquidProvider_isHyperliquidMultiSigAccount, _HyperLiquidProvider_ensureUnifiedAccountEnabled, _HyperLiquidProvider_ensureReady, _HyperLiquidProvider_tradingSetupPromise, _HyperLiquidProvider_tradingSetupComplete, _HyperLiquidProvider_ensureReadyForTrading, _HyperLiquidProvider_getOrFetchPrice, _HyperLiquidProvider_filterFills, _HyperLiquidProvider_getAllAvailableDexs, _HyperLiquidProvider_getValidatedDexs, _HyperLiquidProvider_fetchValidatedDexsInternal, _HyperLiquidProvider_getCachedMeta, _HyperLiquidProvider_backfillAssetMapForDex, _HyperLiquidProvider_getAssetIdWithRepair, _HyperLiquidProvider_getCachedSpotMeta, _HyperLiquidProvider_getCachedPerpDexs, _HyperLiquidProvider_calculateHip3FeeMultiplier, _HyperLiquidProvider_getCacheKey, _HyperLiquidProvider_fetchMarketsForDex, _HyperLiquidProvider_getUsdcTokenId, _HyperLiquidProvider_isUsdcCollateralDex, _HyperLiquidProvider_buildAssetMapping, _HyperLiquidProvider_queryUserDataAcrossDexs, _HyperLiquidProvider_mapError, _HyperLiquidProvider_getErrorContext, _HyperLiquidProvider_isMappedAccountModeExchangeError, _HyperLiquidProvider_getTradingErrorContext, _HyperLiquidProvider_checkBuilderFeeApproval, _HyperLiquidProvider_ensureBuilderFeeApproval, _HyperLiquidProvider_checkBuilderFeeStatus, _HyperLiquidProvider_getBalanceForDex, _HyperLiquidProvider_findSourceDexWithBalance, _HyperLiquidProvider_autoTransferForHip3Order, _HyperLiquidProvider_autoTransferBackAfterClose, _HyperLiquidProvider_calculateHip3RequiredMargin, _HyperLiquidProvider_handleHip3PostOrderRebalance, _HyperLiquidProvider_handleHip3OrderRollback, _HyperLiquidProvider_validateOrderBeforePlacement, _HyperLiquidProvider_getAssetInfo, _HyperLiquidProvider_prepareAssetForTrading, _HyperLiquidProvider_handleHip3PreOrder, _HyperLiquidProvider_submitOrderWithRollback, _HyperLiquidProvider_handleOrderError, _HyperLiquidProvider_fetchOpenOrders, _HyperLiquidProvider_resolveReplacementOrderId, _HyperLiquidProvider_getStandaloneValidatedDexs, _HyperLiquidProvider_queryDexPositions, _HyperLiquidProvider_getAllMids, _HyperLiquidProvider_fetchSingleDexFresh, _HyperLiquidProvider_excludeNonUsdcCollateralResults, _HyperLiquidProvider_mergeDexResultsInto, _HyperLiquidProvider_cacheFreshMarketDataSnapshot, _HyperLiquidProvider_getStaleMarketDataSnapshot, _HyperLiquidProvider_isFeeCacheValid, _HyperLiquidProvider_getBuilderAddress, _HyperLiquidProvider_getReferralCode, _HyperLiquidProvider_ensureReferralSet, _HyperLiquidProvider_isReferralCodeReady, _HyperLiquidProvider_checkReferralSet, _HyperLiquidProvider_setReferralCode;
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
15
|
exports.HyperLiquidProvider = void 0;
|
|
16
16
|
const utils_1 = require("@metamask/utils");
|
|
@@ -32,7 +32,6 @@ const errorUtils_js_1 = require("../utils/errorUtils.cjs");
|
|
|
32
32
|
const hyperLiquidAbstraction_js_1 = require("../utils/hyperLiquidAbstraction.cjs");
|
|
33
33
|
const hyperLiquidAdapter_js_1 = require("../utils/hyperLiquidAdapter.cjs");
|
|
34
34
|
const hyperLiquidValidation_js_1 = require("../utils/hyperLiquidValidation.cjs");
|
|
35
|
-
const idUtils_js_1 = require("../utils/idUtils.cjs");
|
|
36
35
|
const marketDataTransform_js_1 = require("../utils/marketDataTransform.cjs");
|
|
37
36
|
const marketUtils_js_1 = require("../utils/marketUtils.cjs");
|
|
38
37
|
const orderCalculations_js_1 = require("../utils/orderCalculations.cjs");
|
|
@@ -47,137 +46,6 @@ const standaloneInfoClient_js_1 = require("../utils/standaloneInfoClient.cjs");
|
|
|
47
46
|
* @returns The result of the operation.
|
|
48
47
|
*/
|
|
49
48
|
const isStatusObject = (status) => typeof status === 'object' && status !== null;
|
|
50
|
-
/**
|
|
51
|
-
* Exchange messages that mean a cancel was refused because the order is not on
|
|
52
|
-
* the book any more.
|
|
53
|
-
*
|
|
54
|
-
* HyperLiquid answers a cancel it cannot match with "Order was never placed,
|
|
55
|
-
* already canceled, or filled." That is a rejection of the request but a
|
|
56
|
-
* confirmation of what the caller wanted — nothing of that order is resting.
|
|
57
|
-
* Every other rejection (multi-sig, a stale nonce, a rate limit) leaves the
|
|
58
|
-
* order exactly where it was, which is a materially different outcome.
|
|
59
|
-
*/
|
|
60
|
-
const ALREADY_GONE_CANCEL_MARKERS = [
|
|
61
|
-
'never placed',
|
|
62
|
-
'already canceled',
|
|
63
|
-
'already cancelled',
|
|
64
|
-
'order not found',
|
|
65
|
-
];
|
|
66
|
-
/**
|
|
67
|
-
* Classify one entry of a cancel response.
|
|
68
|
-
*
|
|
69
|
-
* @param status - A single status from the exchange's cancel response.
|
|
70
|
-
* @returns Whether the order was cancelled, was already gone, or still rests.
|
|
71
|
-
*/
|
|
72
|
-
const classifyCancelStatus = (status) => {
|
|
73
|
-
if (status === 'success') {
|
|
74
|
-
return 'cancelled';
|
|
75
|
-
}
|
|
76
|
-
const message = (isStatusObject(status) && typeof status.error === 'string'
|
|
77
|
-
? status.error
|
|
78
|
-
: '').toLowerCase();
|
|
79
|
-
return ALREADY_GONE_CANCEL_MARKERS.some((marker) => message.includes(marker))
|
|
80
|
-
? 'gone'
|
|
81
|
-
: 'refused';
|
|
82
|
-
};
|
|
83
|
-
/**
|
|
84
|
-
* Read the best price on one side of the book, discounting the chase's own
|
|
85
|
-
* order.
|
|
86
|
-
*
|
|
87
|
-
* A chase rests one tick inside the spread, so its own order *is* the best
|
|
88
|
-
* price on its side. Reading the raw book would therefore see the chase's own
|
|
89
|
-
* quote every tick, conclude the touch has not moved, and never re-price —
|
|
90
|
-
* masking exactly the adverse moves the strategy exists to follow. Levels this
|
|
91
|
-
* provider occupies are netted down by what it holds there, and are skipped
|
|
92
|
-
* entirely when nothing else is left on them.
|
|
93
|
-
*
|
|
94
|
-
* "Own" means *every* chase this provider is running on that side, not just the
|
|
95
|
-
* one asking. Two chases on the same side each netting only themselves would
|
|
96
|
-
* read the other as the external touch and improve on it, then improve on the
|
|
97
|
-
* improvement — walking each other toward the opposite side of an unchanged
|
|
98
|
-
* market.
|
|
99
|
-
*
|
|
100
|
-
* The sizes must be what the orders still have resting, not what they were
|
|
101
|
-
* placed for: an order netted at its original size subtracts more than it
|
|
102
|
-
* occupies, which can hide external liquidity sharing the level.
|
|
103
|
-
*
|
|
104
|
-
* @param levels - One side of the book, best-first.
|
|
105
|
-
* @param ownByPrice - This provider's own resting size at each price.
|
|
106
|
-
* @returns The best price other participants are showing, or null.
|
|
107
|
-
*/
|
|
108
|
-
const readBestExternalPrice = (levels, ownByPrice) => {
|
|
109
|
-
for (const level of levels ?? []) {
|
|
110
|
-
const price = parseFloat(level.px);
|
|
111
|
-
if (!Number.isFinite(price) || price <= 0) {
|
|
112
|
-
continue;
|
|
113
|
-
}
|
|
114
|
-
const size = parseFloat(level.sz) - (ownByPrice.get(level.px) ?? 0);
|
|
115
|
-
if (size > 0) {
|
|
116
|
-
return price;
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
return null;
|
|
120
|
-
};
|
|
121
|
-
/**
|
|
122
|
-
* Narrow order params down to their strategy fields.
|
|
123
|
-
*
|
|
124
|
-
* Both validation entry points forward exactly this group, so a field added to
|
|
125
|
-
* `StrategyOrderValidationParams` reaches both of them or neither.
|
|
126
|
-
*
|
|
127
|
-
* @param params - Order parameters.
|
|
128
|
-
* @returns The strategy fields, as validation takes them.
|
|
129
|
-
*/
|
|
130
|
-
const pickStrategyParams = (params) => ({
|
|
131
|
-
twapDuration: params.twapDuration,
|
|
132
|
-
twapRandomize: params.twapRandomize,
|
|
133
|
-
scaleMinPrice: params.scaleMinPrice,
|
|
134
|
-
scaleMaxPrice: params.scaleMaxPrice,
|
|
135
|
-
scaleNumOrders: params.scaleNumOrders,
|
|
136
|
-
chaseIntervalMs: params.chaseIntervalMs,
|
|
137
|
-
chaseMaxDurationMs: params.chaseMaxDurationMs,
|
|
138
|
-
chaseMaxRepricings: params.chaseMaxRepricings,
|
|
139
|
-
});
|
|
140
|
-
/**
|
|
141
|
-
* Check a strategy placement's notional against the minimum the venue will
|
|
142
|
-
* actually apply to it.
|
|
143
|
-
*
|
|
144
|
-
* The per-order minimum is charged against each order the venue receives, and a
|
|
145
|
-
* strategy placement does not send one order:
|
|
146
|
-
*
|
|
147
|
-
* A `twap` is a single instruction the venue slices itself, so it is bounded by
|
|
148
|
-
* the venue's own documented minimum for a TWAP rather than by the per-order
|
|
149
|
-
* minimum that `validateOrder` has already applied.
|
|
150
|
-
*
|
|
151
|
-
* `scale` is deliberately absent. Its rungs are the orders the venue charges
|
|
152
|
-
* the minimum against, and their sizes come from flooring onto the asset's size
|
|
153
|
-
* grid with the remainder landing on the first rung — none of which is knowable
|
|
154
|
-
* without `szDecimals`, which this check does not have. Every approximation
|
|
155
|
-
* available here is unsound in one direction or the other, and the one that was
|
|
156
|
-
* here rejected ladders whose submitted rungs all cleared the minimum.
|
|
157
|
-
* `#buildScaleLadder` applies the exact per-rung check against the real grid
|
|
158
|
-
* sizes, and it runs before anything is signed, so nothing is lost by leaving
|
|
159
|
-
* the question to it.
|
|
160
|
-
*
|
|
161
|
-
* `chase` is absent too: it rests one order at a time, so the per-order minimum
|
|
162
|
-
* the caller has already been checked against is the right bound.
|
|
163
|
-
*
|
|
164
|
-
* @param params - Notional parameters.
|
|
165
|
-
* @param params.orderType - The order placement type.
|
|
166
|
-
* @param params.orderValueUSD - Total notional of the placement, in USD.
|
|
167
|
-
* @returns Validation result with isValid flag and optional error message.
|
|
168
|
-
*/
|
|
169
|
-
const validateStrategyNotional = (params) => {
|
|
170
|
-
const { orderType, orderValueUSD } = params;
|
|
171
|
-
if (orderType === 'twap') {
|
|
172
|
-
return orderValueUSD < perpsConfig_js_1.HYPERLIQUID_TWAP_LIMITS.MinNotionalUsd
|
|
173
|
-
? {
|
|
174
|
-
isValid: false,
|
|
175
|
-
error: perpsErrorCodes_js_1.PERPS_ERROR_CODES.ORDER_TWAP_NOTIONAL_TOO_SMALL,
|
|
176
|
-
}
|
|
177
|
-
: { isValid: true };
|
|
178
|
-
}
|
|
179
|
-
return { isValid: true };
|
|
180
|
-
};
|
|
181
49
|
/**
|
|
182
50
|
* Collect the order IDs of every TP/SL child carried by a parent order.
|
|
183
51
|
*
|
|
@@ -338,23 +206,6 @@ class HyperLiquidProvider {
|
|
|
338
206
|
'multi-sig required': perpsErrorCodes_js_1.PERPS_ERROR_CODES.EXCHANGE_MULTI_SIG_REQUIRED,
|
|
339
207
|
'invalid nonce': perpsErrorCodes_js_1.PERPS_ERROR_CODES.EXCHANGE_INVALID_NONCE,
|
|
340
208
|
});
|
|
341
|
-
// Scale ladders placed by this provider instance, keyed by group handle, so
|
|
342
|
-
// one cancel can reach every rung. Cleared on disconnect; the rung IDs are
|
|
343
|
-
// also returned to the caller as `OrderResult.childOrderIds`.
|
|
344
|
-
_HyperLiquidProvider_scaleOrderGroups.set(this, new Map());
|
|
345
|
-
// Chase sessions running on this provider instance, keyed by session handle.
|
|
346
|
-
// Each owns a pending timer, so disconnect has to stop them.
|
|
347
|
-
_HyperLiquidProvider_chaseSessions.set(this, new Map());
|
|
348
|
-
// Chase placements that have reserved a slot against the venue's concurrency
|
|
349
|
-
// cap but have not registered their session yet. Two round trips separate the
|
|
350
|
-
// two, and a reservation is what keeps concurrent placements from both
|
|
351
|
-
// passing the check.
|
|
352
|
-
_HyperLiquidProvider_chasePlacementsInFlight.set(this, 0);
|
|
353
|
-
// Bumped by every teardown that clears the chase registry. A placement
|
|
354
|
-
// captures it before its round trips and refuses to register afterwards if it
|
|
355
|
-
// has moved: a session registered after a disconnect would schedule a
|
|
356
|
-
// background timer against a provider that has already been torn down.
|
|
357
|
-
_HyperLiquidProvider_chaseGeneration.set(this, 0);
|
|
358
209
|
// Track whether clients have been initialized (lazy initialization)
|
|
359
210
|
_HyperLiquidProvider_clientsInitialized.set(this, false);
|
|
360
211
|
// Promise-based lock to prevent race conditions in concurrent initialization
|
|
@@ -532,19 +383,10 @@ class HyperLiquidProvider {
|
|
|
532
383
|
tpslLinkage: params.tpslLinkage,
|
|
533
384
|
grouping: params.grouping,
|
|
534
385
|
timeInForce: params.timeInForce,
|
|
535
|
-
clientOrderId: params.clientOrderId,
|
|
536
|
-
...pickStrategyParams(params),
|
|
537
386
|
});
|
|
538
387
|
if (!validation.isValid) {
|
|
539
388
|
throw new Error(validation.error);
|
|
540
389
|
}
|
|
541
|
-
// Strategy placements expand into an execution schedule rather than a
|
|
542
|
-
// single order, so they leave the shared path here — before the order
|
|
543
|
-
// array, the HIP-3 transfer, and the atomic single-order submit, none of
|
|
544
|
-
// which describe what a TWAP, a ladder, or a chase does.
|
|
545
|
-
if ((0, orderTypes_js_1.isStrategyOrderType)(params.orderType)) {
|
|
546
|
-
return await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_placeStrategyOrder).call(this, params, params.orderType);
|
|
547
|
-
}
|
|
548
390
|
// Extract DEX name for API calls (main DEX = null)
|
|
549
391
|
const { dex: dexName } = (0, hyperLiquidAdapter_js_1.parseAssetName)(params.symbol);
|
|
550
392
|
// 1. Get asset info and current price before validation so price-less
|
|
@@ -711,14 +553,7 @@ class HyperLiquidProvider {
|
|
|
711
553
|
// close, so the retry would either be rejected as "Reduce only order would
|
|
712
554
|
// increase position" or resubmit an identical order. Surfacing the
|
|
713
555
|
// minimum-value error names the real problem instead.
|
|
714
|
-
|
|
715
|
-
// Strategy placements are excluded for the same reason in a different
|
|
716
|
-
// shape: the minimum applies to each TWAP slice and each ladder rung, not
|
|
717
|
-
// to the total, so growing the total by 1.5% does not clear it.
|
|
718
|
-
if (isMinimumOrderError &&
|
|
719
|
-
retryCount === 0 &&
|
|
720
|
-
!params.reduceOnly &&
|
|
721
|
-
!(0, orderTypes_js_1.isStrategyOrderType)(params.orderType)) {
|
|
556
|
+
if (isMinimumOrderError && retryCount === 0 && !params.reduceOnly) {
|
|
722
557
|
let adjustedUsdAmount;
|
|
723
558
|
let originalValue;
|
|
724
559
|
if (params.usdAmount) {
|
|
@@ -794,17 +629,6 @@ class HyperLiquidProvider {
|
|
|
794
629
|
error: perpsErrorCodes_js_1.PERPS_ERROR_CODES.ORDER_EDIT_TRIGGER_UNSUPPORTED,
|
|
795
630
|
};
|
|
796
631
|
}
|
|
797
|
-
// A strategy placement is not a single resting order, so there is nothing
|
|
798
|
-
// for `modify` to rewrite: it would submit the edit as an ordinary
|
|
799
|
-
// FrontendMarket modification and quietly drop the TWAP schedule, the
|
|
800
|
-
// ladder, or the chase loop the caller asked for. Cancel by the strategy
|
|
801
|
-
// handle and place again.
|
|
802
|
-
if ((0, orderTypes_js_1.isStrategyOrderType)(params.newOrder.orderType)) {
|
|
803
|
-
return {
|
|
804
|
-
success: false,
|
|
805
|
-
error: perpsErrorCodes_js_1.PERPS_ERROR_CODES.ORDER_EDIT_STRATEGY_UNSUPPORTED,
|
|
806
|
-
};
|
|
807
|
-
}
|
|
808
632
|
// The WebSocket order cache is the cheap source for the resting order's
|
|
809
633
|
// placement type, but it may be cold or stale.
|
|
810
634
|
const cachedRestingOrder = __classPrivateFieldGet(this, _HyperLiquidProvider_subscriptionService, "f")
|
|
@@ -975,12 +799,6 @@ class HyperLiquidProvider {
|
|
|
975
799
|
* @returns A promise that resolves to the result.
|
|
976
800
|
*/
|
|
977
801
|
async cancelOrder(params) {
|
|
978
|
-
// A strategy handle is not an exchange order ID, so it cannot go through
|
|
979
|
-
// the single-order cancel below. Callers that omit `orderType` — every
|
|
980
|
-
// existing one — keep the behaviour they have today.
|
|
981
|
-
if (params.orderType && (0, orderTypes_js_1.isStrategyOrderType)(params.orderType)) {
|
|
982
|
-
return await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_cancelStrategyOrder).call(this, params);
|
|
983
|
-
}
|
|
984
802
|
try {
|
|
985
803
|
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('Canceling order:', params);
|
|
986
804
|
// Hydrate the asset map before coin validation so a cold start (e.g.
|
|
@@ -3136,8 +2954,6 @@ class HyperLiquidProvider {
|
|
|
3136
2954
|
tpslLinkage: params.tpslLinkage,
|
|
3137
2955
|
grouping: params.grouping,
|
|
3138
2956
|
timeInForce: params.timeInForce,
|
|
3139
|
-
clientOrderId: params.clientOrderId,
|
|
3140
|
-
...pickStrategyParams(params),
|
|
3141
2957
|
});
|
|
3142
2958
|
if (!basicValidation.isValid) {
|
|
3143
2959
|
return basicValidation;
|
|
@@ -3210,15 +3026,6 @@ class HyperLiquidProvider {
|
|
|
3210
3026
|
error: perpsErrorCodes_js_1.PERPS_ERROR_CODES.ORDER_SIZE_MIN,
|
|
3211
3027
|
};
|
|
3212
3028
|
}
|
|
3213
|
-
// A strategy placement's total clearing the per-order minimum does not
|
|
3214
|
-
// mean the orders it expands into will.
|
|
3215
|
-
const strategyMinimum = validateStrategyNotional({
|
|
3216
|
-
orderType: params.orderType,
|
|
3217
|
-
orderValueUSD,
|
|
3218
|
-
});
|
|
3219
|
-
if (!strategyMinimum.isValid) {
|
|
3220
|
-
return strategyMinimum;
|
|
3221
|
-
}
|
|
3222
3029
|
}
|
|
3223
3030
|
// Asset-specific leverage validation
|
|
3224
3031
|
if (params.leverage && params.symbol) {
|
|
@@ -3984,17 +3791,11 @@ class HyperLiquidProvider {
|
|
|
3984
3791
|
*/
|
|
3985
3792
|
async calculateFees(params) {
|
|
3986
3793
|
const { orderType, isMaker = false, amount, symbol } = params;
|
|
3987
|
-
//
|
|
3988
|
-
// market order (taker), a stop_limit as a limit order
|
|
3989
|
-
// resting limit orders it fans out into, and a TWAP as the marketable
|
|
3990
|
-
// suborders it crosses the book with.
|
|
3794
|
+
// Trigger placements are charged as their execution kind: a stop_market fills
|
|
3795
|
+
// as a market order (taker), a stop_limit as a limit order.
|
|
3991
3796
|
const isMarketExecution = (0, orderTypes_js_1.getTriggerExecution)(orderType) === 'market';
|
|
3992
|
-
// A chase is post-only by construction, so it can only ever fill as a maker.
|
|
3993
|
-
// Quoting it at the taker rate would overstate the fee whatever the caller
|
|
3994
|
-
// passes for `isMaker`.
|
|
3995
|
-
const chargesMakerRate = orderType === 'chase' || (!isMarketExecution && isMaker);
|
|
3996
3797
|
// Start with base rates from config
|
|
3997
|
-
let feeRate =
|
|
3798
|
+
let feeRate = isMarketExecution || !isMaker ? hyperLiquidConfig_js_1.FEE_RATES.taker : hyperLiquidConfig_js_1.FEE_RATES.maker;
|
|
3998
3799
|
// Parse symbol to detect HIP-3 DEX (e.g., "xyz:TSLA" → dex="xyz", parsedSymbol="TSLA")
|
|
3999
3800
|
const { dex, symbol: parsedSymbol } = (0, hyperLiquidAdapter_js_1.parseAssetName)(symbol);
|
|
4000
3801
|
const isHip3Asset = dex !== null;
|
|
@@ -4038,12 +3839,10 @@ class HyperLiquidProvider {
|
|
|
4038
3839
|
if (__classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_isFeeCacheValid).call(this, userAddress)) {
|
|
4039
3840
|
const cached = __classPrivateFieldGet(this, _HyperLiquidProvider_userFeeCache, "f").get(userAddress);
|
|
4040
3841
|
if (cached) {
|
|
4041
|
-
//
|
|
4042
|
-
|
|
4043
|
-
|
|
4044
|
-
|
|
4045
|
-
? cached.perpsMakerRate
|
|
4046
|
-
: cached.perpsTakerRate;
|
|
3842
|
+
// Market orders always use taker rate, limit orders check isMaker
|
|
3843
|
+
let userFeeRate = isMarketExecution || !isMaker
|
|
3844
|
+
? cached.perpsTakerRate
|
|
3845
|
+
: cached.perpsMakerRate;
|
|
4047
3846
|
// Apply HIP-3 dynamic multiplier to user-specific rates (includes Growth Mode)
|
|
4048
3847
|
if (isHip3Asset && hip3Multiplier > 0) {
|
|
4049
3848
|
userFeeRate *= hip3Multiplier;
|
|
@@ -4148,10 +3947,10 @@ class HyperLiquidProvider {
|
|
|
4148
3947
|
ttl: 5 * 60 * 1000, // 5 minutes
|
|
4149
3948
|
};
|
|
4150
3949
|
__classPrivateFieldGet(this, _HyperLiquidProvider_userFeeCache, "f").set(userAddress, rates);
|
|
4151
|
-
//
|
|
4152
|
-
let userFeeRate =
|
|
4153
|
-
? rates.
|
|
4154
|
-
: rates.
|
|
3950
|
+
// Market orders always use taker rate, limit orders check isMaker
|
|
3951
|
+
let userFeeRate = isMarketExecution || !isMaker
|
|
3952
|
+
? rates.perpsTakerRate
|
|
3953
|
+
: rates.perpsMakerRate;
|
|
4155
3954
|
// Apply HIP-3 dynamic multiplier to API-fetched rates (includes Growth Mode)
|
|
4156
3955
|
if (isHip3Asset && hip3Multiplier > 0) {
|
|
4157
3956
|
userFeeRate *= hip3Multiplier;
|
|
@@ -4280,17 +4079,6 @@ class HyperLiquidProvider {
|
|
|
4280
4079
|
});
|
|
4281
4080
|
// Clear subscriptions through subscription service
|
|
4282
4081
|
__classPrivateFieldGet(this, _HyperLiquidProvider_subscriptionService, "f").clearAll();
|
|
4283
|
-
// Stop every chase loop: each holds a pending timer, and re-pricing after
|
|
4284
|
-
// a disconnect would sign orders against a client that is being torn down.
|
|
4285
|
-
// Orders already resting are deliberately left alone — disconnecting is
|
|
4286
|
-
// not a request to cancel the user's positions or orders.
|
|
4287
|
-
for (const sessionId of __classPrivateFieldGet(this, _HyperLiquidProvider_chaseSessions, "f").keys()) {
|
|
4288
|
-
__classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_stopChaseSession).call(this, sessionId);
|
|
4289
|
-
}
|
|
4290
|
-
__classPrivateFieldGet(this, _HyperLiquidProvider_chaseSessions, "f").clear();
|
|
4291
|
-
__classPrivateFieldGet(this, _HyperLiquidProvider_scaleOrderGroups, "f").clear();
|
|
4292
|
-
// Placements still mid-flight check this before registering.
|
|
4293
|
-
__classPrivateFieldSet(this, _HyperLiquidProvider_chaseGeneration, __classPrivateFieldGet(this, _HyperLiquidProvider_chaseGeneration, "f") + 1, "f");
|
|
4294
4082
|
// Clear fee cache
|
|
4295
4083
|
this.clearFeeCache();
|
|
4296
4084
|
// Clear session caches (ensures fresh state on reconnect/account switch)
|
|
@@ -4477,7 +4265,7 @@ class HyperLiquidProvider {
|
|
|
4477
4265
|
}
|
|
4478
4266
|
}
|
|
4479
4267
|
exports.HyperLiquidProvider = HyperLiquidProvider;
|
|
4480
|
-
_HyperLiquidProvider_deps = new WeakMap(), _HyperLiquidProvider_clientService = new WeakMap(), _HyperLiquidProvider_walletService = new WeakMap(), _HyperLiquidProvider_subscriptionService = new WeakMap(), _HyperLiquidProvider_symbolToAssetId = new WeakMap(), _HyperLiquidProvider_userFeeCache = new WeakMap(), _HyperLiquidProvider_maxLeverageCache = new WeakMap(), _HyperLiquidProvider_cachedMetaByDex = new WeakMap(), _HyperLiquidProvider_cachedMarketDataWithPrices = new WeakMap(), _HyperLiquidProvider_cachedSpotMeta = new WeakMap(), _HyperLiquidProvider_dexDiscoveryCache = new WeakMap(), _HyperLiquidProvider_referralCheckCache = new WeakMap(), _HyperLiquidProvider_builderFeeCheckCache = new WeakMap(), _HyperLiquidProvider_ensureReadyPromise = new WeakMap(), _HyperLiquidProvider_pendingBuilderFeeApprovals = new WeakMap(), _HyperLiquidProvider_compiledAllowlistPatterns = new WeakMap(), _HyperLiquidProvider_compiledBlocklistPatterns = new WeakMap(), _HyperLiquidProvider_userFeeDiscountBips = new WeakMap(), _HyperLiquidProvider_hip3Enabled = new WeakMap(), _HyperLiquidProvider_allowlistMarkets = new WeakMap(), _HyperLiquidProvider_blocklistMarkets = new WeakMap(), _HyperLiquidProvider_useUnifiedAccount = new WeakMap(), _HyperLiquidProvider_dexDiscoveryComplete = new WeakMap(), _HyperLiquidProvider_unifiedAccountSetupNeedsRetry = new WeakMap(), _HyperLiquidProvider_pendingValidatedDexsPromise = new WeakMap(), _HyperLiquidProvider_cachedUsdcTokenId = new WeakMap(), _HyperLiquidProvider_errorMappings = new WeakMap(),
|
|
4268
|
+
_HyperLiquidProvider_deps = new WeakMap(), _HyperLiquidProvider_clientService = new WeakMap(), _HyperLiquidProvider_walletService = new WeakMap(), _HyperLiquidProvider_subscriptionService = new WeakMap(), _HyperLiquidProvider_symbolToAssetId = new WeakMap(), _HyperLiquidProvider_userFeeCache = new WeakMap(), _HyperLiquidProvider_maxLeverageCache = new WeakMap(), _HyperLiquidProvider_cachedMetaByDex = new WeakMap(), _HyperLiquidProvider_cachedMarketDataWithPrices = new WeakMap(), _HyperLiquidProvider_cachedSpotMeta = new WeakMap(), _HyperLiquidProvider_dexDiscoveryCache = new WeakMap(), _HyperLiquidProvider_referralCheckCache = new WeakMap(), _HyperLiquidProvider_builderFeeCheckCache = new WeakMap(), _HyperLiquidProvider_ensureReadyPromise = new WeakMap(), _HyperLiquidProvider_pendingBuilderFeeApprovals = new WeakMap(), _HyperLiquidProvider_compiledAllowlistPatterns = new WeakMap(), _HyperLiquidProvider_compiledBlocklistPatterns = new WeakMap(), _HyperLiquidProvider_userFeeDiscountBips = new WeakMap(), _HyperLiquidProvider_hip3Enabled = new WeakMap(), _HyperLiquidProvider_allowlistMarkets = new WeakMap(), _HyperLiquidProvider_blocklistMarkets = new WeakMap(), _HyperLiquidProvider_useUnifiedAccount = new WeakMap(), _HyperLiquidProvider_dexDiscoveryComplete = new WeakMap(), _HyperLiquidProvider_unifiedAccountSetupNeedsRetry = new WeakMap(), _HyperLiquidProvider_pendingValidatedDexsPromise = new WeakMap(), _HyperLiquidProvider_cachedUsdcTokenId = new WeakMap(), _HyperLiquidProvider_errorMappings = new WeakMap(), _HyperLiquidProvider_clientsInitialized = new WeakMap(), _HyperLiquidProvider_initializationPromise = new WeakMap(), _HyperLiquidProvider_messenger = new WeakMap(), _HyperLiquidProvider_builderAddressTestnet = new WeakMap(), _HyperLiquidProvider_builderAddressMainnet = new WeakMap(), _HyperLiquidProvider_priceDeviationLimit = new WeakMap(), _HyperLiquidProvider_tradingSetupPromise = new WeakMap(), _HyperLiquidProvider_tradingSetupComplete = new WeakMap(), _HyperLiquidProvider_instances = new WeakSet(), _HyperLiquidProvider_compilePatternsSafely = function _HyperLiquidProvider_compilePatternsSafely(patterns, listName) {
|
|
4481
4269
|
const compiled = [];
|
|
4482
4270
|
for (const pattern of patterns) {
|
|
4483
4271
|
try {
|
|
@@ -6581,1056 +6369,6 @@ async function _HyperLiquidProvider_handleOrderError(params) {
|
|
|
6581
6369
|
}));
|
|
6582
6370
|
}
|
|
6583
6371
|
return (0, hyperLiquidValidation_js_1.createErrorResult)(mappedError, { success: false });
|
|
6584
|
-
}, _HyperLiquidProvider_placeStrategyOrder =
|
|
6585
|
-
/**
|
|
6586
|
-
* Dispatch a strategy placement to the handler that owns it.
|
|
6587
|
-
*
|
|
6588
|
-
* @param params - Order parameters, already validated.
|
|
6589
|
-
* @param orderType - The strategy type, narrowed by the caller.
|
|
6590
|
-
* @returns A promise that resolves to the result.
|
|
6591
|
-
*/
|
|
6592
|
-
async function _HyperLiquidProvider_placeStrategyOrder(params, orderType) {
|
|
6593
|
-
// Captured before the shared preamble, not after it: that preamble awaits
|
|
6594
|
-
// asset info, validation, trading readiness and a leverage update, and a
|
|
6595
|
-
// disconnect during any of them has to be seen by the chase registration
|
|
6596
|
-
// at the end.
|
|
6597
|
-
const generation = __classPrivateFieldGet(this, _HyperLiquidProvider_chaseGeneration, "f");
|
|
6598
|
-
if (orderType !== 'chase') {
|
|
6599
|
-
const context = await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_prepareStrategyPlacement).call(this, params);
|
|
6600
|
-
return orderType === 'twap'
|
|
6601
|
-
? await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_placeTwapOrder).call(this, params, context)
|
|
6602
|
-
: await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_placeScaleOrder).call(this, params, context);
|
|
6603
|
-
}
|
|
6604
|
-
// The chase slot is claimed before the preamble, not after it: the preamble
|
|
6605
|
-
// completes the signing setup and can change the asset's leverage, and a
|
|
6606
|
-
// request that is going to be refused for exceeding the venue's cap must
|
|
6607
|
-
// not cost either. Reserved in the same synchronous step it is checked, so
|
|
6608
|
-
// concurrent placements cannot both see room during the round trips that
|
|
6609
|
-
// follow.
|
|
6610
|
-
const activeChases = [...__classPrivateFieldGet(this, _HyperLiquidProvider_chaseSessions, "f").values()].filter((session) => session.active).length;
|
|
6611
|
-
if (activeChases + __classPrivateFieldGet(this, _HyperLiquidProvider_chasePlacementsInFlight, "f") >=
|
|
6612
|
-
perpsConfig_js_1.CHASE_ORDER_CONFIG.MaxActiveSessions) {
|
|
6613
|
-
throw new Error(perpsErrorCodes_js_1.PERPS_ERROR_CODES.ORDER_CHASE_LIMIT_REACHED);
|
|
6614
|
-
}
|
|
6615
|
-
__classPrivateFieldSet(this, _HyperLiquidProvider_chasePlacementsInFlight, __classPrivateFieldGet(this, _HyperLiquidProvider_chasePlacementsInFlight, "f") + 1, "f");
|
|
6616
|
-
try {
|
|
6617
|
-
const context = await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_prepareStrategyPlacement).call(this, params);
|
|
6618
|
-
return await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_startChaseSession).call(this, params, context, generation);
|
|
6619
|
-
}
|
|
6620
|
-
finally {
|
|
6621
|
-
__classPrivateFieldSet(this, _HyperLiquidProvider_chasePlacementsInFlight, __classPrivateFieldGet(this, _HyperLiquidProvider_chasePlacementsInFlight, "f") - 1, "f");
|
|
6622
|
-
}
|
|
6623
|
-
}, _HyperLiquidProvider_prepareStrategyPlacement =
|
|
6624
|
-
/**
|
|
6625
|
-
* Run the shared preamble every strategy placement needs.
|
|
6626
|
-
*
|
|
6627
|
-
* Mirrors the single-order path's own preamble — validate against a live
|
|
6628
|
-
* price, complete the signing setup, resolve the asset ID, apply leverage —
|
|
6629
|
-
* so a strategy order is held to the same rules as an ordinary one.
|
|
6630
|
-
*
|
|
6631
|
-
* @param params - Order parameters.
|
|
6632
|
-
* @returns The asset and sizing context the strategy handlers submit with.
|
|
6633
|
-
*/
|
|
6634
|
-
async function _HyperLiquidProvider_prepareStrategyPlacement(params) {
|
|
6635
|
-
const { dex: dexName } = (0, hyperLiquidAdapter_js_1.parseAssetName)(params.symbol);
|
|
6636
|
-
// A HIP-3 order is preceded by a margin transfer onto the builder DEX and
|
|
6637
|
-
// followed by a rebalance, both of which are wired into the single-order
|
|
6638
|
-
// submit path and its rollback. Rather than half-support that here, a
|
|
6639
|
-
// strategy placement on a HIP-3 market is refused outright.
|
|
6640
|
-
if (dexName !== null) {
|
|
6641
|
-
throw new Error(perpsErrorCodes_js_1.PERPS_ERROR_CODES.ORDER_STRATEGY_MARKET_UNSUPPORTED);
|
|
6642
|
-
}
|
|
6643
|
-
const { assetInfo, currentPrice, meta } = await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_getAssetInfo).call(this, {
|
|
6644
|
-
symbol: params.symbol,
|
|
6645
|
-
dexName,
|
|
6646
|
-
});
|
|
6647
|
-
const effectivePrice = params.currentPrice && params.currentPrice > 0
|
|
6648
|
-
? params.currentPrice
|
|
6649
|
-
: currentPrice;
|
|
6650
|
-
await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_validateOrderBeforePlacement).call(this, {
|
|
6651
|
-
...params,
|
|
6652
|
-
currentPrice: effectivePrice,
|
|
6653
|
-
});
|
|
6654
|
-
const normalizedMaxSlippageBps = params.maxSlippageBps ??
|
|
6655
|
-
(typeof params.slippage === 'number'
|
|
6656
|
-
? Math.round(params.slippage * hyperLiquidConfig_js_1.BASIS_POINTS_DIVISOR)
|
|
6657
|
-
: undefined);
|
|
6658
|
-
const { finalPositionSize } = (0, orderCalculations_js_1.calculateFinalPositionSize)({
|
|
6659
|
-
usdAmount: params.usdAmount,
|
|
6660
|
-
size: params.size,
|
|
6661
|
-
currentPrice: effectivePrice,
|
|
6662
|
-
priceAtCalculation: params.priceAtCalculation,
|
|
6663
|
-
maxSlippageBps: normalizedMaxSlippageBps,
|
|
6664
|
-
szDecimals: assetInfo.szDecimals,
|
|
6665
|
-
leverage: params.leverage,
|
|
6666
|
-
reduceOnly: params.reduceOnly,
|
|
6667
|
-
});
|
|
6668
|
-
const formattedSize = (0, hyperLiquidAdapter_js_1.formatHyperLiquidSize)({
|
|
6669
|
-
size: finalPositionSize,
|
|
6670
|
-
szDecimals: assetInfo.szDecimals,
|
|
6671
|
-
});
|
|
6672
|
-
// Everything below is checked here, before anything is signed: it needs
|
|
6673
|
-
// `szDecimals`, which only arrives with the asset info above, and these are
|
|
6674
|
-
// caller mistakes that must not cost a leverage change or a signing prompt
|
|
6675
|
-
// first.
|
|
6676
|
-
const ladder = params.orderType === 'scale'
|
|
6677
|
-
? __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_buildScaleLadder).call(this, {
|
|
6678
|
-
params,
|
|
6679
|
-
szDecimals: assetInfo.szDecimals,
|
|
6680
|
-
finalPositionSize,
|
|
6681
|
-
})
|
|
6682
|
-
: undefined;
|
|
6683
|
-
// `validateOrder` applied the venue minimum to the notional the caller
|
|
6684
|
-
// asked for; this applies it to the size actually being submitted. The two
|
|
6685
|
-
// differ whenever sizing floors onto the grid rather than rounding up to
|
|
6686
|
-
// meet the requested USD — which is exactly what a reduce-only order does,
|
|
6687
|
-
// since the venue rejects a close larger than the position. A boundary
|
|
6688
|
-
// amount can therefore clear the check the caller sees and still arrive
|
|
6689
|
-
// under the minimum the venue charges against what it receives.
|
|
6690
|
-
if (params.orderType === 'twap' || params.orderType === 'chase') {
|
|
6691
|
-
const submittedNotional = parseFloat(formattedSize) * effectivePrice;
|
|
6692
|
-
const minimumNotional = params.orderType === 'twap'
|
|
6693
|
-
? perpsConfig_js_1.HYPERLIQUID_TWAP_LIMITS.MinNotionalUsd
|
|
6694
|
-
: __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_getMinimumOrderSize).call(this);
|
|
6695
|
-
if (submittedNotional < minimumNotional) {
|
|
6696
|
-
throw new Error(params.orderType === 'twap'
|
|
6697
|
-
? perpsErrorCodes_js_1.PERPS_ERROR_CODES.ORDER_TWAP_NOTIONAL_TOO_SMALL
|
|
6698
|
-
: perpsErrorCodes_js_1.PERPS_ERROR_CODES.ORDER_SIZE_MIN);
|
|
6699
|
-
}
|
|
6700
|
-
}
|
|
6701
|
-
// Kept after validation so an invalid strategy order never triggers the
|
|
6702
|
-
// signature prompts in trading setup — same ordering as `placeOrder`.
|
|
6703
|
-
await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_ensureReadyForTrading).call(this);
|
|
6704
|
-
const assetId = await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_getAssetIdWithRepair).call(this, {
|
|
6705
|
-
symbol: params.symbol,
|
|
6706
|
-
dexName,
|
|
6707
|
-
meta,
|
|
6708
|
-
});
|
|
6709
|
-
await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_prepareAssetForTrading).call(this, {
|
|
6710
|
-
symbol: params.symbol,
|
|
6711
|
-
assetId,
|
|
6712
|
-
leverage: params.leverage,
|
|
6713
|
-
});
|
|
6714
|
-
return {
|
|
6715
|
-
assetId,
|
|
6716
|
-
szDecimals: assetInfo.szDecimals,
|
|
6717
|
-
finalPositionSize,
|
|
6718
|
-
formattedSize,
|
|
6719
|
-
ladder,
|
|
6720
|
-
};
|
|
6721
|
-
}, _HyperLiquidProvider_getMinimumOrderSize = function _HyperLiquidProvider_getMinimumOrderSize() {
|
|
6722
|
-
return __classPrivateFieldGet(this, _HyperLiquidProvider_clientService, "f").isTestnetMode()
|
|
6723
|
-
? hyperLiquidConfig_js_1.TRADING_DEFAULTS.amount.testnet
|
|
6724
|
-
: hyperLiquidConfig_js_1.TRADING_DEFAULTS.amount.mainnet;
|
|
6725
|
-
}, _HyperLiquidProvider_buildScaleLadder = function _HyperLiquidProvider_buildScaleLadder(options) {
|
|
6726
|
-
const { params, szDecimals, finalPositionSize } = options;
|
|
6727
|
-
const count = params.scaleNumOrders;
|
|
6728
|
-
const prices = (0, orderCalculations_js_1.computeScalePriceLadder)({
|
|
6729
|
-
minPrice: parseFloat(params.scaleMinPrice),
|
|
6730
|
-
maxPrice: parseFloat(params.scaleMaxPrice),
|
|
6731
|
-
count,
|
|
6732
|
-
}).map((price) => (0, hyperLiquidAdapter_js_1.formatHyperLiquidPrice)({ price, szDecimals }));
|
|
6733
|
-
if (new Set(prices).size !== prices.length) {
|
|
6734
|
-
throw new Error(perpsErrorCodes_js_1.PERPS_ERROR_CODES.ORDER_SCALE_RANGE_INVALID);
|
|
6735
|
-
}
|
|
6736
|
-
// Throws ORDER_SCALE_SIZE_TOO_SMALL when a rung would round to nothing.
|
|
6737
|
-
const sizes = (0, orderCalculations_js_1.splitScaleSizes)({
|
|
6738
|
-
totalSize: finalPositionSize,
|
|
6739
|
-
count,
|
|
6740
|
-
szDecimals,
|
|
6741
|
-
});
|
|
6742
|
-
const minimumOrderSize = __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_getMinimumOrderSize).call(this);
|
|
6743
|
-
const cheapestRungNotional = Math.min(...sizes.map((size, index) => parseFloat(size) * parseFloat(prices[index])));
|
|
6744
|
-
if (cheapestRungNotional < minimumOrderSize) {
|
|
6745
|
-
throw new Error(perpsErrorCodes_js_1.PERPS_ERROR_CODES.ORDER_SCALE_NOTIONAL_TOO_SMALL);
|
|
6746
|
-
}
|
|
6747
|
-
return { prices, sizes };
|
|
6748
|
-
}, _HyperLiquidProvider_placeTwapOrder =
|
|
6749
|
-
/**
|
|
6750
|
-
* Submit a TWAP through HyperLiquid's dedicated TWAP action.
|
|
6751
|
-
*
|
|
6752
|
-
* A TWAP is not an order on the book, so it does not go through the `order`
|
|
6753
|
-
* action, carries no builder fee, and comes back identified by a `twapId`
|
|
6754
|
-
* rather than an `oid`. That ID is the handle `cancelOrder` needs.
|
|
6755
|
-
*
|
|
6756
|
-
* @param params - Order parameters.
|
|
6757
|
-
* @param context - Prepared asset and sizing context.
|
|
6758
|
-
* @returns A promise that resolves to the result.
|
|
6759
|
-
*/
|
|
6760
|
-
async function _HyperLiquidProvider_placeTwapOrder(params, context) {
|
|
6761
|
-
const { assetId, formattedSize } = context;
|
|
6762
|
-
const exchangeClient = __classPrivateFieldGet(this, _HyperLiquidProvider_clientService, "f").getExchangeClient();
|
|
6763
|
-
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('Submitting TWAP order', {
|
|
6764
|
-
symbol: params.symbol,
|
|
6765
|
-
assetId,
|
|
6766
|
-
size: formattedSize,
|
|
6767
|
-
durationMinutes: params.twapDuration,
|
|
6768
|
-
randomize: params.twapRandomize ?? false,
|
|
6769
|
-
});
|
|
6770
|
-
const result = await exchangeClient.twapOrder({
|
|
6771
|
-
twap: {
|
|
6772
|
-
a: assetId,
|
|
6773
|
-
b: params.isBuy,
|
|
6774
|
-
s: formattedSize,
|
|
6775
|
-
r: params.reduceOnly ?? false,
|
|
6776
|
-
// Validation has already established this is a whole number of minutes
|
|
6777
|
-
// inside the venue's bounds.
|
|
6778
|
-
m: params.twapDuration,
|
|
6779
|
-
t: params.twapRandomize ?? false,
|
|
6780
|
-
},
|
|
6781
|
-
});
|
|
6782
|
-
if (result.status !== 'ok') {
|
|
6783
|
-
throw new Error(`TWAP order failed: ${JSON.stringify(result)}`);
|
|
6784
|
-
}
|
|
6785
|
-
const status = result.response?.data?.status;
|
|
6786
|
-
if (!isStatusObject(status) || !(0, utils_1.hasProperty)(status, 'running')) {
|
|
6787
|
-
const rawError = status?.error ??
|
|
6788
|
-
'TWAP order rejected';
|
|
6789
|
-
throw new Error(rawError);
|
|
6790
|
-
}
|
|
6791
|
-
const running = status.running;
|
|
6792
|
-
return {
|
|
6793
|
-
success: true,
|
|
6794
|
-
orderId: running.twapId.toString(),
|
|
6795
|
-
submittedSize: formattedSize,
|
|
6796
|
-
};
|
|
6797
|
-
}, _HyperLiquidProvider_placeScaleOrder =
|
|
6798
|
-
/**
|
|
6799
|
-
* Fan a scale placement out into one batch of resting limit orders.
|
|
6800
|
-
*
|
|
6801
|
-
* The whole ladder goes in a single `order` action, which is one round trip
|
|
6802
|
-
* and one signature rather than one per rung. It is **not** atomic: an `na`
|
|
6803
|
-
* grouping evaluates each entry independently, so the response can report
|
|
6804
|
-
* some rungs resting and others rejected. A partial ladder is reported as
|
|
6805
|
-
* such — `submittedSize` covers only the rungs that actually rested — rather
|
|
6806
|
-
* than as a full submission.
|
|
6807
|
-
*
|
|
6808
|
-
* @param params - Order parameters.
|
|
6809
|
-
* @param context - Prepared asset and sizing context.
|
|
6810
|
-
* @returns A promise that resolves to the result.
|
|
6811
|
-
*/
|
|
6812
|
-
async function _HyperLiquidProvider_placeScaleOrder(params, context) {
|
|
6813
|
-
const { assetId, szDecimals, formattedSize, ladder } = context;
|
|
6814
|
-
// Built and validated in `#prepareStrategyPlacement`, before anything was
|
|
6815
|
-
// signed, so what is submitted here is exactly what the minimums were
|
|
6816
|
-
// applied to.
|
|
6817
|
-
const { prices, sizes } = ladder;
|
|
6818
|
-
const count = prices.length;
|
|
6819
|
-
const orders = prices.map((price, index) => ({
|
|
6820
|
-
a: assetId,
|
|
6821
|
-
b: params.isBuy,
|
|
6822
|
-
p: price,
|
|
6823
|
-
s: sizes[index],
|
|
6824
|
-
r: params.reduceOnly ?? false,
|
|
6825
|
-
t: { limit: { tif: 'Gtc' } },
|
|
6826
|
-
}));
|
|
6827
|
-
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('Submitting scale ladder', {
|
|
6828
|
-
symbol: params.symbol,
|
|
6829
|
-
assetId,
|
|
6830
|
-
count,
|
|
6831
|
-
prices: orders.map((order) => order.p),
|
|
6832
|
-
sizes,
|
|
6833
|
-
});
|
|
6834
|
-
const exchangeClient = __classPrivateFieldGet(this, _HyperLiquidProvider_clientService, "f").getExchangeClient();
|
|
6835
|
-
const result = await exchangeClient.order({
|
|
6836
|
-
orders,
|
|
6837
|
-
grouping: 'na',
|
|
6838
|
-
builder: {
|
|
6839
|
-
b: __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_getBuilderAddress).call(this, __classPrivateFieldGet(this, _HyperLiquidProvider_clientService, "f").isTestnetMode()),
|
|
6840
|
-
f: __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_getDiscountedBuilderFee).call(this),
|
|
6841
|
-
},
|
|
6842
|
-
});
|
|
6843
|
-
if (result.status !== 'ok') {
|
|
6844
|
-
throw new Error(`Scale order failed: ${JSON.stringify(result)}`);
|
|
6845
|
-
}
|
|
6846
|
-
const statuses = result.response?.data?.statuses ?? [];
|
|
6847
|
-
// Which rung each ID came from matters: `submittedSize` has to add up the
|
|
6848
|
-
// slices that actually rested, not the total that was asked for.
|
|
6849
|
-
const restedRungs = statuses
|
|
6850
|
-
.map((status, index) => ({
|
|
6851
|
-
orderId: __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_readOrderIdFromStatus).call(this, status),
|
|
6852
|
-
size: sizes[index],
|
|
6853
|
-
}))
|
|
6854
|
-
.filter((rung) => rung.orderId !== undefined);
|
|
6855
|
-
// A ladder that came back with no IDs at all rested nothing; reporting it
|
|
6856
|
-
// as a success would hand the caller a handle that cancels nothing.
|
|
6857
|
-
if (restedRungs.length === 0) {
|
|
6858
|
-
throw new Error(`Scale order rejected: ${JSON.stringify(result.response?.data)}`);
|
|
6859
|
-
}
|
|
6860
|
-
const childOrderIds = restedRungs.map((rung) => rung.orderId);
|
|
6861
|
-
const groupId = (0, idUtils_js_1.generatePerpsId)('scale');
|
|
6862
|
-
__classPrivateFieldGet(this, _HyperLiquidProvider_scaleOrderGroups, "f").set(groupId, { assetId, orderIds: childOrderIds });
|
|
6863
|
-
// The batch is not atomic, so a ladder can come back part-rested. Reporting
|
|
6864
|
-
// the requested total as submitted would tell the caller they are exposed
|
|
6865
|
-
// for more than they are.
|
|
6866
|
-
const isPartial = restedRungs.length < count;
|
|
6867
|
-
if (isPartial) {
|
|
6868
|
-
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('Scale ladder only partly rested', {
|
|
6869
|
-
groupId,
|
|
6870
|
-
rested: restedRungs.length,
|
|
6871
|
-
requested: count,
|
|
6872
|
-
statuses,
|
|
6873
|
-
});
|
|
6874
|
-
}
|
|
6875
|
-
return {
|
|
6876
|
-
success: true,
|
|
6877
|
-
orderId: groupId,
|
|
6878
|
-
childOrderIds,
|
|
6879
|
-
submittedSize: isPartial
|
|
6880
|
-
? (0, hyperLiquidAdapter_js_1.formatHyperLiquidSize)({
|
|
6881
|
-
size: restedRungs.reduce((total, rung) => total + parseFloat(rung.size), 0),
|
|
6882
|
-
szDecimals,
|
|
6883
|
-
})
|
|
6884
|
-
: formattedSize,
|
|
6885
|
-
};
|
|
6886
|
-
}, _HyperLiquidProvider_startChaseSession =
|
|
6887
|
-
/**
|
|
6888
|
-
* Place a chase's first order and register the session that follows it.
|
|
6889
|
-
*
|
|
6890
|
-
* Split from the routing above so the concurrency reservation there wraps
|
|
6891
|
-
* every await in one `try`/`finally`.
|
|
6892
|
-
*
|
|
6893
|
-
* @param params - Order parameters.
|
|
6894
|
-
* @param context - Prepared asset and sizing context.
|
|
6895
|
-
* @param generation - Teardown generation captured before any await.
|
|
6896
|
-
* @returns A promise that resolves to the result.
|
|
6897
|
-
*/
|
|
6898
|
-
async function _HyperLiquidProvider_startChaseSession(params, context, generation) {
|
|
6899
|
-
const { assetId, szDecimals, formattedSize } = context;
|
|
6900
|
-
// The preamble is several round trips long. A disconnect during it has
|
|
6901
|
-
// already torn down everything this session would run on, so the chase
|
|
6902
|
-
// stops here rather than reading the book and putting a fresh order on it
|
|
6903
|
-
// for a provider that no longer exists.
|
|
6904
|
-
if (generation !== __classPrivateFieldGet(this, _HyperLiquidProvider_chaseGeneration, "f")) {
|
|
6905
|
-
throw new Error(perpsErrorCodes_js_1.PERPS_ERROR_CODES.ORDER_CHASE_ABANDONED);
|
|
6906
|
-
}
|
|
6907
|
-
const quotePrice = await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_getChaseQuotePrice).call(this, {
|
|
6908
|
-
symbol: params.symbol,
|
|
6909
|
-
isBuy: params.isBuy,
|
|
6910
|
-
szDecimals,
|
|
6911
|
-
});
|
|
6912
|
-
if (quotePrice === 'gone') {
|
|
6913
|
-
// No own order to be gone on a first placement; narrows the union.
|
|
6914
|
-
throw new Error(perpsErrorCodes_js_1.PERPS_ERROR_CODES.ORDER_CHASE_TOUCH_UNAVAILABLE);
|
|
6915
|
-
}
|
|
6916
|
-
// The book read is a round trip of its own, so the check above does not
|
|
6917
|
-
// cover it. Re-checked here, immediately before the only statement that
|
|
6918
|
-
// signs anything: every await on this path is now followed by a refusal
|
|
6919
|
-
// before the next one, leaving one window that cannot be closed from here
|
|
6920
|
-
// — a disconnect arriving while the submission itself is in flight, which
|
|
6921
|
-
// the check after it handles.
|
|
6922
|
-
if (generation !== __classPrivateFieldGet(this, _HyperLiquidProvider_chaseGeneration, "f")) {
|
|
6923
|
-
throw new Error(perpsErrorCodes_js_1.PERPS_ERROR_CODES.ORDER_CHASE_ABANDONED);
|
|
6924
|
-
}
|
|
6925
|
-
// Read once, while the caller's discount context is still set.
|
|
6926
|
-
const builderFee = __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_getDiscountedBuilderFee).call(this);
|
|
6927
|
-
// Held onto rather than looked up again after the submission returns.
|
|
6928
|
-
// `disconnect` drops the service's client reference synchronously, so a
|
|
6929
|
-
// retraction that asked for a client after the fact would be refused one
|
|
6930
|
-
// and leave the order resting under the account that placed it. This
|
|
6931
|
-
// instance signed the order and can still cancel it as that account.
|
|
6932
|
-
const placingClient = __classPrivateFieldGet(this, _HyperLiquidProvider_clientService, "f").getExchangeClient();
|
|
6933
|
-
const orderId = await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_restChaseOrder).call(this, {
|
|
6934
|
-
assetId,
|
|
6935
|
-
isBuy: params.isBuy,
|
|
6936
|
-
price: quotePrice,
|
|
6937
|
-
size: formattedSize,
|
|
6938
|
-
reduceOnly: params.reduceOnly ?? false,
|
|
6939
|
-
builderFee,
|
|
6940
|
-
exchangeClient: placingClient,
|
|
6941
|
-
});
|
|
6942
|
-
const intervalMs = params.chaseIntervalMs ?? perpsConfig_js_1.CHASE_ORDER_CONFIG.DefaultIntervalMs;
|
|
6943
|
-
const sessionId = (0, idUtils_js_1.generatePerpsId)('chase');
|
|
6944
|
-
const session = {
|
|
6945
|
-
symbol: params.symbol,
|
|
6946
|
-
assetId,
|
|
6947
|
-
isBuy: params.isBuy,
|
|
6948
|
-
size: formattedSize,
|
|
6949
|
-
szDecimals,
|
|
6950
|
-
reduceOnly: params.reduceOnly ?? false,
|
|
6951
|
-
orderId,
|
|
6952
|
-
pendingReplacement: null,
|
|
6953
|
-
restingPrice: quotePrice,
|
|
6954
|
-
intervalMs,
|
|
6955
|
-
builderFee,
|
|
6956
|
-
deadline: Date.now() +
|
|
6957
|
-
(params.chaseMaxDurationMs ?? perpsConfig_js_1.CHASE_ORDER_CONFIG.DefaultMaxDurationMs),
|
|
6958
|
-
maxRepricings: params.chaseMaxRepricings ?? perpsConfig_js_1.CHASE_ORDER_CONFIG.DefaultMaxRepricings,
|
|
6959
|
-
repricings: 0,
|
|
6960
|
-
timer: null,
|
|
6961
|
-
active: true,
|
|
6962
|
-
};
|
|
6963
|
-
// A disconnect landed while the submission was in flight — the one window
|
|
6964
|
-
// the checks above cannot close. The order rested, but no strategy is
|
|
6965
|
-
// running behind it and no handle names it, so this placement is reported
|
|
6966
|
-
// as a failure. That makes the resting order an orphan: the caller is told
|
|
6967
|
-
// the chase failed, nothing refreshes the caches a successful placement
|
|
6968
|
-
// would have invalidated, and the exchange id is only usable for as long as
|
|
6969
|
-
// the provider stays pointed at the account that placed it — a disconnect
|
|
6970
|
-
// is usually followed by an account or network switch, after which handing
|
|
6971
|
-
// the id back is no remedy at all. So it is cancelled here, through the
|
|
6972
|
-
// client that signed it — the account switch cannot reach that instance —
|
|
6973
|
-
// and the failure is then true of the venue as well as of this provider.
|
|
6974
|
-
// Best-effort still: the transport underneath that client may already be
|
|
6975
|
-
// closing, so a cancel that does not land falls back to reporting the id.
|
|
6976
|
-
if (generation !== __classPrivateFieldGet(this, _HyperLiquidProvider_chaseGeneration, "f")) {
|
|
6977
|
-
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('Chase placement outlived its provider', {
|
|
6978
|
-
orderId,
|
|
6979
|
-
restingPrice: quotePrice,
|
|
6980
|
-
});
|
|
6981
|
-
const outcome = await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_retractOrphanedChaseOrder).call(this, session, placingClient);
|
|
6982
|
-
return (0, hyperLiquidValidation_js_1.createErrorResult)(new Error(perpsErrorCodes_js_1.PERPS_ERROR_CODES.ORDER_CHASE_ABANDONED), {
|
|
6983
|
-
success: false,
|
|
6984
|
-
// Reported only when the retraction did not take. Naming an order
|
|
6985
|
-
// that is no longer on the book would send the caller to cancel
|
|
6986
|
-
// something already gone; naming one that still rests is the only
|
|
6987
|
-
// route left to it.
|
|
6988
|
-
...(outcome === 'refused' ? { childOrderIds: [orderId] } : {}),
|
|
6989
|
-
submittedSize: formattedSize,
|
|
6990
|
-
});
|
|
6991
|
-
}
|
|
6992
|
-
__classPrivateFieldGet(this, _HyperLiquidProvider_chaseSessions, "f").set(sessionId, session);
|
|
6993
|
-
__classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_scheduleChaseTick).call(this, sessionId);
|
|
6994
|
-
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('Chase session started', {
|
|
6995
|
-
sessionId,
|
|
6996
|
-
symbol: params.symbol,
|
|
6997
|
-
orderId,
|
|
6998
|
-
restingPrice: quotePrice,
|
|
6999
|
-
intervalMs,
|
|
7000
|
-
});
|
|
7001
|
-
return {
|
|
7002
|
-
success: true,
|
|
7003
|
-
orderId: sessionId,
|
|
7004
|
-
childOrderIds: [orderId],
|
|
7005
|
-
submittedSize: formattedSize,
|
|
7006
|
-
};
|
|
7007
|
-
}, _HyperLiquidProvider_getChaseQuotePrice =
|
|
7008
|
-
/**
|
|
7009
|
-
* Read the price a chase must rest at: the best bid for a buy, the best ask
|
|
7010
|
-
* for a sell.
|
|
7011
|
-
*
|
|
7012
|
-
* The touch, not the mid: a post-only order priced at the mid would either
|
|
7013
|
-
* cross or be rejected, and a chase that never rests at the front of the
|
|
7014
|
-
* queue is not chasing anything.
|
|
7015
|
-
*
|
|
7016
|
-
* @param params - The lookup parameters.
|
|
7017
|
-
* @param params.symbol - Market to read.
|
|
7018
|
-
* @param params.isBuy - Which side the chase rests on.
|
|
7019
|
-
* @param params.szDecimals - Asset size precision, for price formatting.
|
|
7020
|
-
* @param params.own - The chase's own resting order, excluded from the book.
|
|
7021
|
-
* @param params.own.orderId - Exchange ID of that order.
|
|
7022
|
-
* @param params.own.price - Price that order rests at.
|
|
7023
|
-
* @param params.own.size - Size it was last placed for.
|
|
7024
|
-
* @returns The formatted price the chase should rest at, or `'gone'` when the
|
|
7025
|
-
* order it was chasing is no longer live.
|
|
7026
|
-
*/
|
|
7027
|
-
async function _HyperLiquidProvider_getChaseQuotePrice(params) {
|
|
7028
|
-
const { symbol, isBuy, szDecimals, own } = params;
|
|
7029
|
-
const book = await __classPrivateFieldGet(this, _HyperLiquidProvider_clientService, "f").getInfoClient().l2Book({
|
|
7030
|
-
coin: symbol,
|
|
7031
|
-
});
|
|
7032
|
-
// `levels` is [bids, asks], each best-first.
|
|
7033
|
-
const ownSide = book?.levels?.[isBuy ? 0 : 1];
|
|
7034
|
-
const netting = await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_resolveOwnRestingSizes).call(this, {
|
|
7035
|
-
symbol,
|
|
7036
|
-
isBuy,
|
|
7037
|
-
callerOrderId: own?.orderId,
|
|
7038
|
-
ownSide,
|
|
7039
|
-
});
|
|
7040
|
-
if (netting === 'gone') {
|
|
7041
|
-
return 'gone';
|
|
7042
|
-
}
|
|
7043
|
-
const bestBid = readBestExternalPrice(book?.levels?.[0], netting);
|
|
7044
|
-
const bestAsk = readBestExternalPrice(book?.levels?.[1], netting);
|
|
7045
|
-
if (bestBid === null || bestAsk === null) {
|
|
7046
|
-
throw new Error(perpsErrorCodes_js_1.PERPS_ERROR_CODES.ORDER_CHASE_TOUCH_UNAVAILABLE);
|
|
7047
|
-
}
|
|
7048
|
-
return (0, orderCalculations_js_1.computeChaseQuotePrice)({ bestBid, bestAsk, isBuy, szDecimals });
|
|
7049
|
-
}, _HyperLiquidProvider_restChaseOrder =
|
|
7050
|
-
/**
|
|
7051
|
-
* Rest one post-only order for a chase and return its exchange ID.
|
|
7052
|
-
*
|
|
7053
|
-
* @param params - The placement parameters.
|
|
7054
|
-
* @param params.assetId - Resolved asset ID.
|
|
7055
|
-
* @param params.isBuy - Order side.
|
|
7056
|
-
* @param params.price - Formatted limit price.
|
|
7057
|
-
* @param params.size - Formatted size.
|
|
7058
|
-
* @param params.reduceOnly - Whether the order may only reduce a position.
|
|
7059
|
-
* @param params.builderFee - Builder fee, in tenths of a basis point, captured
|
|
7060
|
-
* when the session started so replacements keep the rate they were quoted at.
|
|
7061
|
-
* @param params.exchangeClient - Client to submit through. Passed in rather
|
|
7062
|
-
* than looked up here so a first placement can keep the instance it signed
|
|
7063
|
-
* with, which is the only one that can take the order back once `disconnect`
|
|
7064
|
-
* has dropped the service's reference.
|
|
7065
|
-
* @returns The resting order's exchange ID.
|
|
7066
|
-
*/
|
|
7067
|
-
async function _HyperLiquidProvider_restChaseOrder(params) {
|
|
7068
|
-
const result = await params.exchangeClient.order({
|
|
7069
|
-
orders: [
|
|
7070
|
-
{
|
|
7071
|
-
a: params.assetId,
|
|
7072
|
-
b: params.isBuy,
|
|
7073
|
-
p: params.price,
|
|
7074
|
-
s: params.size,
|
|
7075
|
-
r: params.reduceOnly,
|
|
7076
|
-
// Post-only: a chase adds liquidity at the touch. Crossing would end
|
|
7077
|
-
// the chase on its first tick at a worse price than resting does.
|
|
7078
|
-
t: { limit: { tif: 'Alo' } },
|
|
7079
|
-
},
|
|
7080
|
-
],
|
|
7081
|
-
grouping: 'na',
|
|
7082
|
-
builder: {
|
|
7083
|
-
b: __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_getBuilderAddress).call(this, __classPrivateFieldGet(this, _HyperLiquidProvider_clientService, "f").isTestnetMode()),
|
|
7084
|
-
f: params.builderFee,
|
|
7085
|
-
},
|
|
7086
|
-
});
|
|
7087
|
-
if (result.status !== 'ok') {
|
|
7088
|
-
throw new Error(`Chase order failed: ${JSON.stringify(result)}`);
|
|
7089
|
-
}
|
|
7090
|
-
const orderId = __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_readOrderIdFromStatus).call(this, result.response?.data?.statuses?.[0]);
|
|
7091
|
-
if (!orderId) {
|
|
7092
|
-
throw new Error(`Chase order rejected: ${JSON.stringify(result.response?.data)}`);
|
|
7093
|
-
}
|
|
7094
|
-
return orderId;
|
|
7095
|
-
}, _HyperLiquidProvider_scheduleChaseTick = function _HyperLiquidProvider_scheduleChaseTick(sessionId) {
|
|
7096
|
-
const session = __classPrivateFieldGet(this, _HyperLiquidProvider_chaseSessions, "f").get(sessionId);
|
|
7097
|
-
if (!session?.active) {
|
|
7098
|
-
return;
|
|
7099
|
-
}
|
|
7100
|
-
const timer = setTimeout(() => {
|
|
7101
|
-
// A rejected tick must not stop the session from being cancellable, and
|
|
7102
|
-
// must not surface as an unhandled rejection either.
|
|
7103
|
-
__classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_runChaseTick).call(this, sessionId).catch((error) => {
|
|
7104
|
-
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('Chase tick failed', {
|
|
7105
|
-
sessionId,
|
|
7106
|
-
error: (0, errorUtils_js_1.ensureError)(error, 'HyperLiquidProvider.chaseTick').message,
|
|
7107
|
-
});
|
|
7108
|
-
__classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_recoverFailedChaseTick).call(this, sessionId);
|
|
7109
|
-
});
|
|
7110
|
-
}, session.intervalMs);
|
|
7111
|
-
// Node keeps the process alive for a pending timer; a background chase is
|
|
7112
|
-
// not a reason to hold an exiting process open. React Native's timers have
|
|
7113
|
-
// no `unref`, hence the guard.
|
|
7114
|
-
timer.unref?.();
|
|
7115
|
-
session.timer = timer;
|
|
7116
|
-
}, _HyperLiquidProvider_recoverFailedChaseTick = function _HyperLiquidProvider_recoverFailedChaseTick(sessionId) {
|
|
7117
|
-
const session = __classPrivateFieldGet(this, _HyperLiquidProvider_chaseSessions, "f").get(sessionId);
|
|
7118
|
-
if (!session?.active) {
|
|
7119
|
-
return;
|
|
7120
|
-
}
|
|
7121
|
-
if (session.orderId === null) {
|
|
7122
|
-
session.active = false;
|
|
7123
|
-
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('Chase ended with nothing resting', {
|
|
7124
|
-
sessionId,
|
|
7125
|
-
});
|
|
7126
|
-
return;
|
|
7127
|
-
}
|
|
7128
|
-
__classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_scheduleChaseTick).call(this, sessionId);
|
|
7129
|
-
}, _HyperLiquidProvider_runChaseTick =
|
|
7130
|
-
/**
|
|
7131
|
-
* Advance one chase session: re-price if the touch has moved, stop if the
|
|
7132
|
-
* window has closed.
|
|
7133
|
-
*
|
|
7134
|
-
* @param sessionId - Session to advance.
|
|
7135
|
-
*/
|
|
7136
|
-
async function _HyperLiquidProvider_runChaseTick(sessionId) {
|
|
7137
|
-
const session = __classPrivateFieldGet(this, _HyperLiquidProvider_chaseSessions, "f").get(sessionId);
|
|
7138
|
-
if (!session?.active) {
|
|
7139
|
-
return;
|
|
7140
|
-
}
|
|
7141
|
-
if (Date.now() >= session.deadline) {
|
|
7142
|
-
// The window closed. The last order stays resting as an ordinary limit
|
|
7143
|
-
// order and the session stays registered, so cancelling by its handle
|
|
7144
|
-
// still reaches that order.
|
|
7145
|
-
session.active = false;
|
|
7146
|
-
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('Chase session window closed', { sessionId });
|
|
7147
|
-
return;
|
|
7148
|
-
}
|
|
7149
|
-
const quotePrice = await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_getChaseQuotePrice).call(this, {
|
|
7150
|
-
symbol: session.symbol,
|
|
7151
|
-
isBuy: session.isBuy,
|
|
7152
|
-
szDecimals: session.szDecimals,
|
|
7153
|
-
own: session.orderId
|
|
7154
|
-
? {
|
|
7155
|
-
orderId: session.orderId,
|
|
7156
|
-
price: session.restingPrice,
|
|
7157
|
-
size: session.size,
|
|
7158
|
-
}
|
|
7159
|
-
: undefined,
|
|
7160
|
-
});
|
|
7161
|
-
if (quotePrice === 'gone') {
|
|
7162
|
-
// The order left the book without the loop seeing it — it filled. Ending
|
|
7163
|
-
// here releases its slot against the venue's concurrency cap instead of
|
|
7164
|
-
// holding one until the window closes.
|
|
7165
|
-
session.orderId = null;
|
|
7166
|
-
session.active = false;
|
|
7167
|
-
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('Chase order filled between ticks', {
|
|
7168
|
-
sessionId,
|
|
7169
|
-
});
|
|
7170
|
-
return;
|
|
7171
|
-
}
|
|
7172
|
-
// The book read is a round trip, and a cancel arriving during it stops the
|
|
7173
|
-
// session. Without this re-check the tick would cancel and re-place the very
|
|
7174
|
-
// order the caller just asked to be rid of.
|
|
7175
|
-
if (!session.active) {
|
|
7176
|
-
return;
|
|
7177
|
-
}
|
|
7178
|
-
if (quotePrice !== session.restingPrice && session.orderId) {
|
|
7179
|
-
const outcome = await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_cancelChaseChild).call(this, session);
|
|
7180
|
-
// The cancel is a second round trip, and this is the window where a
|
|
7181
|
-
// caller's `cancelOrder` does the most damage: it would cancel the order
|
|
7182
|
-
// this tick has already cancelled, report the cancel incomplete, and then
|
|
7183
|
-
// this tick would rest a replacement the caller has no idea exists.
|
|
7184
|
-
// Stopping here leaves nothing on the book.
|
|
7185
|
-
if (!session.active) {
|
|
7186
|
-
// A refused cancel leaves the child resting. Preserve its ID so the
|
|
7187
|
-
// caller that stopped this session can report an incomplete cancel and
|
|
7188
|
-
// retry it instead of orphaning the order.
|
|
7189
|
-
if (outcome !== 'refused') {
|
|
7190
|
-
session.orderId = null;
|
|
7191
|
-
}
|
|
7192
|
-
return;
|
|
7193
|
-
}
|
|
7194
|
-
if (outcome === 'refused') {
|
|
7195
|
-
// The exchange kept the order, so it is still resting at the old price.
|
|
7196
|
-
// Placing the replacement anyway would double the position. Leave it
|
|
7197
|
-
// alone and try again on the next tick, which is why this falls through
|
|
7198
|
-
// to the reschedule below rather than ending the session.
|
|
7199
|
-
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('Chase reprice cancel refused', {
|
|
7200
|
-
sessionId,
|
|
7201
|
-
orderId: session.orderId,
|
|
7202
|
-
});
|
|
7203
|
-
}
|
|
7204
|
-
else if (outcome === 'gone') {
|
|
7205
|
-
// The order left the book between ticks: it filled, or something else
|
|
7206
|
-
// cancelled it. There is nothing left to chase.
|
|
7207
|
-
session.orderId = null;
|
|
7208
|
-
session.active = false;
|
|
7209
|
-
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('Chase order no longer resting', {
|
|
7210
|
-
sessionId,
|
|
7211
|
-
});
|
|
7212
|
-
return;
|
|
7213
|
-
}
|
|
7214
|
-
else {
|
|
7215
|
-
// The cancel is confirmed, so the session owns nothing from here on.
|
|
7216
|
-
// Recorded before the read rather than after it: a read that rejects
|
|
7217
|
-
// would otherwise leave the session naming an order that is already off
|
|
7218
|
-
// the book, and recovery would reschedule a chase with nothing resting.
|
|
7219
|
-
const cancelledOrderId = session.orderId;
|
|
7220
|
-
session.orderId = null;
|
|
7221
|
-
// No further fills can reach a cancelled order, so what it did not fill
|
|
7222
|
-
// is now fixed. Sampling before the cancel would have left a window in
|
|
7223
|
-
// which a fill lands and is then re-placed.
|
|
7224
|
-
const remaining = await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_readOrderRemainder).call(this, cancelledOrderId);
|
|
7225
|
-
// Another round trip, another window for a cancel to land. By now the
|
|
7226
|
-
// old order is already off the book, so a cancel that arrived during it
|
|
7227
|
-
// found nothing to cancel and reported success — resting a replacement
|
|
7228
|
-
// after that would put an order on the book the caller believes is gone
|
|
7229
|
-
// and has no handle for.
|
|
7230
|
-
if (!session.active || !__classPrivateFieldGet(this, _HyperLiquidProvider_chaseSessions, "f").has(sessionId)) {
|
|
7231
|
-
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('Chase cancelled mid-reprice', {
|
|
7232
|
-
sessionId,
|
|
7233
|
-
});
|
|
7234
|
-
return;
|
|
7235
|
-
}
|
|
7236
|
-
if (remaining === null) {
|
|
7237
|
-
// It filled completely between ticks; there is nothing left to chase.
|
|
7238
|
-
session.active = false;
|
|
7239
|
-
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('Chase order fully filled', { sessionId });
|
|
7240
|
-
return;
|
|
7241
|
-
}
|
|
7242
|
-
// Published *before* the placement starts, not from the promise it
|
|
7243
|
-
// returns: the window opens the moment `#restChaseOrder` is entered, so
|
|
7244
|
-
// deriving the marker from its return value would leave the synchronous
|
|
7245
|
-
// prefix of that call unguarded. A cancel arriving anywhere inside the
|
|
7246
|
-
// round trip now waits for the replacement to land instead of reading
|
|
7247
|
-
// the null `orderId` as "nothing rests", reporting success, and dropping
|
|
7248
|
-
// the handle while this call is still putting an order on the book.
|
|
7249
|
-
let settleReplacement;
|
|
7250
|
-
session.pendingReplacement = new Promise((resolve) => {
|
|
7251
|
-
settleReplacement = resolve;
|
|
7252
|
-
});
|
|
7253
|
-
try {
|
|
7254
|
-
session.orderId = await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_restChaseOrder).call(this, {
|
|
7255
|
-
assetId: session.assetId,
|
|
7256
|
-
isBuy: session.isBuy,
|
|
7257
|
-
price: quotePrice,
|
|
7258
|
-
size: remaining,
|
|
7259
|
-
reduceOnly: session.reduceOnly,
|
|
7260
|
-
builderFee: session.builderFee,
|
|
7261
|
-
// A running session is on a live provider, so the current client is
|
|
7262
|
-
// the right one; only the first placement has a teardown to survive.
|
|
7263
|
-
exchangeClient: __classPrivateFieldGet(this, _HyperLiquidProvider_clientService, "f").getExchangeClient(),
|
|
7264
|
-
});
|
|
7265
|
-
session.size = remaining;
|
|
7266
|
-
}
|
|
7267
|
-
finally {
|
|
7268
|
-
// Cleared before the waiters wake, so they see the settled session.
|
|
7269
|
-
session.pendingReplacement = null;
|
|
7270
|
-
settleReplacement?.();
|
|
7271
|
-
}
|
|
7272
|
-
session.restingPrice = quotePrice;
|
|
7273
|
-
session.repricings += 1;
|
|
7274
|
-
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('Chase re-priced', {
|
|
7275
|
-
sessionId,
|
|
7276
|
-
orderId: session.orderId,
|
|
7277
|
-
restingPrice: quotePrice,
|
|
7278
|
-
size: session.size,
|
|
7279
|
-
repricings: session.repricings,
|
|
7280
|
-
});
|
|
7281
|
-
// A cancel that arrived during the round trip is now waiting on this
|
|
7282
|
-
// session; it cancels the order just recorded above. A `disconnect`
|
|
7283
|
-
// deregisters the session instead, and deliberately leaves resting
|
|
7284
|
-
// orders alone — logged with its ID so it stays traceable.
|
|
7285
|
-
if (!__classPrivateFieldGet(this, _HyperLiquidProvider_chaseSessions, "f").has(sessionId)) {
|
|
7286
|
-
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('Chase replacement outlived its session', {
|
|
7287
|
-
sessionId,
|
|
7288
|
-
orderId: session.orderId,
|
|
7289
|
-
});
|
|
7290
|
-
return;
|
|
7291
|
-
}
|
|
7292
|
-
if (!session.active) {
|
|
7293
|
-
return;
|
|
7294
|
-
}
|
|
7295
|
-
}
|
|
7296
|
-
}
|
|
7297
|
-
if (session.repricings >= session.maxRepricings) {
|
|
7298
|
-
session.active = false;
|
|
7299
|
-
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('Chase repricing cap reached', { sessionId });
|
|
7300
|
-
return;
|
|
7301
|
-
}
|
|
7302
|
-
__classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_scheduleChaseTick).call(this, sessionId);
|
|
7303
|
-
}, _HyperLiquidProvider_resolveOwnRestingSizes =
|
|
7304
|
-
/**
|
|
7305
|
-
* Resolve how much of the chase's own order is really sitting on its level.
|
|
7306
|
-
*
|
|
7307
|
-
* The session tracks what its order was last *placed* for, which overstates
|
|
7308
|
-
* the order once it partially fills. Over-subtracting only matters when it
|
|
7309
|
-
* would net the level away entirely — while the level still shows more size
|
|
7310
|
-
* than the order could possibly hold, the level has external liquidity either
|
|
7311
|
-
* way and the stale figure is good enough.
|
|
7312
|
-
*
|
|
7313
|
-
* So the live size is fetched only in the ambiguous case, which keeps the
|
|
7314
|
-
* common tick to a single book read rather than doubling the request rate
|
|
7315
|
-
* against the venue for every chase on every interval.
|
|
7316
|
-
*
|
|
7317
|
-
* @param params - Netting parameters.
|
|
7318
|
-
* @param params.symbol - Market being quoted.
|
|
7319
|
-
* @param params.isBuy - Side being quoted.
|
|
7320
|
-
* @param params.callerOrderId - The asking session's order, if it has one.
|
|
7321
|
-
* @param params.ownSide - The side of the book those orders rest on.
|
|
7322
|
-
* @returns Our resting size at each price, or `'gone'` when the asking
|
|
7323
|
-
* session's own order is no longer live.
|
|
7324
|
-
*/
|
|
7325
|
-
async function _HyperLiquidProvider_resolveOwnRestingSizes(params) {
|
|
7326
|
-
const { symbol, isBuy, callerOrderId, ownSide } = params;
|
|
7327
|
-
// Every chase this provider is running on this side, the caller included.
|
|
7328
|
-
const ourOrders = [...__classPrivateFieldGet(this, _HyperLiquidProvider_chaseSessions, "f").values()]
|
|
7329
|
-
.filter((session) => session.orderId !== null &&
|
|
7330
|
-
session.symbol === symbol &&
|
|
7331
|
-
session.isBuy === isBuy)
|
|
7332
|
-
.map((session) => ({
|
|
7333
|
-
orderId: session.orderId,
|
|
7334
|
-
price: session.restingPrice,
|
|
7335
|
-
size: session.size,
|
|
7336
|
-
}));
|
|
7337
|
-
if (ourOrders.length === 0) {
|
|
7338
|
-
return new Map();
|
|
7339
|
-
}
|
|
7340
|
-
const recorded = new Map();
|
|
7341
|
-
for (const order of ourOrders) {
|
|
7342
|
-
recorded.set(order.price, (recorded.get(order.price) ?? 0) + parseFloat(order.size));
|
|
7343
|
-
}
|
|
7344
|
-
// A level holding more than we possibly could has external size regardless,
|
|
7345
|
-
// so the recorded figures cannot change the answer there and are left as
|
|
7346
|
-
// they are. Elsewhere the recorded sizes may overstate what is really
|
|
7347
|
-
// resting — an order partially fills without the loop seeing it — and only
|
|
7348
|
-
// then is a lookup worth a round trip.
|
|
7349
|
-
const ambiguous = ourOrders.filter((order) => {
|
|
7350
|
-
const level = ownSide?.find((entry) => entry.px === order.price);
|
|
7351
|
-
return !level || parseFloat(level.sz) <= (recorded.get(order.price) ?? 0);
|
|
7352
|
-
});
|
|
7353
|
-
let callerIsGone = false;
|
|
7354
|
-
for (const order of ambiguous) {
|
|
7355
|
-
try {
|
|
7356
|
-
const live = await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_readOrderRemainder).call(this, order.orderId);
|
|
7357
|
-
const delta = (live === null ? 0 : parseFloat(live)) - parseFloat(order.size);
|
|
7358
|
-
recorded.set(order.price, Math.max(0, (recorded.get(order.price) ?? 0) + delta));
|
|
7359
|
-
if (live === null && order.orderId === callerOrderId) {
|
|
7360
|
-
callerIsGone = true;
|
|
7361
|
-
}
|
|
7362
|
-
}
|
|
7363
|
-
catch (error) {
|
|
7364
|
-
// A failed lookup must not stop the chase. Keeping the recorded size can
|
|
7365
|
-
// only over-subtract, which quotes a level deeper rather than leaving
|
|
7366
|
-
// the order stranded at a stale price.
|
|
7367
|
-
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('Chase own-size lookup failed', {
|
|
7368
|
-
orderId: order.orderId,
|
|
7369
|
-
error: (0, errorUtils_js_1.ensureError)(error, 'HyperLiquidProvider.chaseOwnSize').message,
|
|
7370
|
-
});
|
|
7371
|
-
}
|
|
7372
|
-
}
|
|
7373
|
-
return callerIsGone ? 'gone' : recorded;
|
|
7374
|
-
}, _HyperLiquidProvider_readOrderRemainder =
|
|
7375
|
-
/**
|
|
7376
|
-
* Read what a chase's just-cancelled order left unfilled.
|
|
7377
|
-
*
|
|
7378
|
-
* A chase re-prices by cancelling and re-placing, and the order it cancels
|
|
7379
|
-
* may have partially filled first; re-placing the session's original size
|
|
7380
|
-
* would execute more than the caller asked for.
|
|
7381
|
-
*
|
|
7382
|
-
* Read *after* the cancel on purpose. The order's status endpoint answers for
|
|
7383
|
-
* an order that is no longer on the book, and once the cancel has landed no
|
|
7384
|
-
* further fills can reach it — so the unfilled size it reports is final.
|
|
7385
|
-
* Sampling the open-order book beforehand would instead leave a one-round-trip
|
|
7386
|
-
* window in which a fill lands and is then re-placed on top.
|
|
7387
|
-
*
|
|
7388
|
-
* @param orderId - Exchange ID of the order that was just cancelled.
|
|
7389
|
-
* @returns The unfilled size, or null when nothing is left to chase.
|
|
7390
|
-
*/
|
|
7391
|
-
async function _HyperLiquidProvider_readOrderRemainder(orderId) {
|
|
7392
|
-
const userAddress = await __classPrivateFieldGet(this, _HyperLiquidProvider_walletService, "f").getUserAddressWithDefault();
|
|
7393
|
-
const status = await __classPrivateFieldGet(this, _HyperLiquidProvider_clientService, "f").getInfoClient().orderStatus({
|
|
7394
|
-
user: userAddress,
|
|
7395
|
-
oid: parseInt(orderId, 10),
|
|
7396
|
-
});
|
|
7397
|
-
if (status.status !== 'order') {
|
|
7398
|
-
return null;
|
|
7399
|
-
}
|
|
7400
|
-
const remaining = String(status.order.order.sz);
|
|
7401
|
-
return parseFloat(remaining) > 0 ? remaining : null;
|
|
7402
|
-
}, _HyperLiquidProvider_cancelChaseChild =
|
|
7403
|
-
/**
|
|
7404
|
-
* Cancel the order a chase session currently has resting.
|
|
7405
|
-
*
|
|
7406
|
-
* @param session - The session whose child to cancel.
|
|
7407
|
-
* @param placingClient - Client to cancel through. Omitted by every cancel on
|
|
7408
|
-
* a live provider, which wants the current one; an abandoned placement passes
|
|
7409
|
-
* the client it signed with, the only one still able to reach its order.
|
|
7410
|
-
* @returns Whether the order was cancelled, was already gone, or still rests.
|
|
7411
|
-
*/
|
|
7412
|
-
async function _HyperLiquidProvider_cancelChaseChild(session, placingClient) {
|
|
7413
|
-
if (!session.orderId) {
|
|
7414
|
-
return 'gone';
|
|
7415
|
-
}
|
|
7416
|
-
// Looked up only once there is something to cancel, so a session with
|
|
7417
|
-
// nothing resting still answers on a provider whose client is already gone.
|
|
7418
|
-
const exchangeClient = placingClient ?? __classPrivateFieldGet(this, _HyperLiquidProvider_clientService, "f").getExchangeClient();
|
|
7419
|
-
const result = await exchangeClient.cancel({
|
|
7420
|
-
cancels: [{ a: session.assetId, o: parseInt(session.orderId, 10) }],
|
|
7421
|
-
});
|
|
7422
|
-
return classifyCancelStatus(result.response?.data?.statuses?.[0]);
|
|
7423
|
-
}, _HyperLiquidProvider_retractOrphanedChaseOrder =
|
|
7424
|
-
/**
|
|
7425
|
-
* Take back the order of a chase that was abandoned before it registered.
|
|
7426
|
-
*
|
|
7427
|
-
* Best-effort by construction: the provider is already being torn down, so a
|
|
7428
|
-
* cancel that fails outright is reported rather than retried or thrown. The
|
|
7429
|
-
* caller reports the placement as a failure either way — the outcome only
|
|
7430
|
-
* decides whether an exchange id is worth handing back with it.
|
|
7431
|
-
*
|
|
7432
|
-
* @param session - The unregistered session holding the resting order.
|
|
7433
|
-
* @param placingClient - The client the order was signed with. The service's
|
|
7434
|
-
* own reference is already cleared by the time this runs, so asking it for a
|
|
7435
|
-
* client here would fail before a cancel was ever sent.
|
|
7436
|
-
* @returns Whether the order was cancelled, was already gone, or still rests.
|
|
7437
|
-
*/
|
|
7438
|
-
async function _HyperLiquidProvider_retractOrphanedChaseOrder(session, placingClient) {
|
|
7439
|
-
try {
|
|
7440
|
-
const outcome = await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_cancelChaseChild).call(this, session, placingClient);
|
|
7441
|
-
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('Retracted abandoned chase order', {
|
|
7442
|
-
orderId: session.orderId,
|
|
7443
|
-
outcome,
|
|
7444
|
-
});
|
|
7445
|
-
return outcome;
|
|
7446
|
-
}
|
|
7447
|
-
catch (error) {
|
|
7448
|
-
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('Could not retract abandoned chase order', {
|
|
7449
|
-
orderId: session.orderId,
|
|
7450
|
-
error: (0, errorUtils_js_1.ensureError)(error, 'HyperLiquidProvider.startChaseSession')
|
|
7451
|
-
.message,
|
|
7452
|
-
});
|
|
7453
|
-
return 'refused';
|
|
7454
|
-
}
|
|
7455
|
-
}, _HyperLiquidProvider_stopChaseSession = function _HyperLiquidProvider_stopChaseSession(sessionId) {
|
|
7456
|
-
const session = __classPrivateFieldGet(this, _HyperLiquidProvider_chaseSessions, "f").get(sessionId);
|
|
7457
|
-
if (!session) {
|
|
7458
|
-
return;
|
|
7459
|
-
}
|
|
7460
|
-
session.active = false;
|
|
7461
|
-
if (session.timer) {
|
|
7462
|
-
clearTimeout(session.timer);
|
|
7463
|
-
session.timer = null;
|
|
7464
|
-
}
|
|
7465
|
-
}, _HyperLiquidProvider_cancelStrategyOrder =
|
|
7466
|
-
/**
|
|
7467
|
-
* Cancel a strategy placement by its handle.
|
|
7468
|
-
*
|
|
7469
|
-
* @param params - Cancellation parameters.
|
|
7470
|
-
* @returns A promise that resolves to the result.
|
|
7471
|
-
*/
|
|
7472
|
-
async function _HyperLiquidProvider_cancelStrategyOrder(params) {
|
|
7473
|
-
try {
|
|
7474
|
-
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('Canceling strategy order:', params);
|
|
7475
|
-
if (params.orderType === 'twap') {
|
|
7476
|
-
return await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_cancelTwapOrder).call(this, params);
|
|
7477
|
-
}
|
|
7478
|
-
if (params.orderType === 'scale') {
|
|
7479
|
-
return await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_cancelScaleOrder).call(this, params);
|
|
7480
|
-
}
|
|
7481
|
-
return await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_cancelChaseOrder).call(this, params);
|
|
7482
|
-
}
|
|
7483
|
-
catch (error) {
|
|
7484
|
-
const mappedError = __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_mapError).call(this, error);
|
|
7485
|
-
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").logger.error(mappedError, await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_getTradingErrorContext).call(this, 'cancelOrder', mappedError, {
|
|
7486
|
-
orderId: params.orderId,
|
|
7487
|
-
coin: params.symbol,
|
|
7488
|
-
orderType: params.orderType,
|
|
7489
|
-
}));
|
|
7490
|
-
return (0, hyperLiquidValidation_js_1.createErrorResult)(mappedError, {
|
|
7491
|
-
success: false,
|
|
7492
|
-
orderId: params.orderId,
|
|
7493
|
-
});
|
|
7494
|
-
}
|
|
7495
|
-
}, _HyperLiquidProvider_cancelTwapOrder =
|
|
7496
|
-
/**
|
|
7497
|
-
* Cancel a running TWAP through the venue's TWAP cancel action.
|
|
7498
|
-
*
|
|
7499
|
-
* A TWAP never rested on the book, so the ordinary `cancel` action has no
|
|
7500
|
-
* order ID to match and would reject it.
|
|
7501
|
-
*
|
|
7502
|
-
* @param params - Cancellation parameters, with `orderId` carrying the TWAP ID.
|
|
7503
|
-
* @returns A promise that resolves to the result.
|
|
7504
|
-
*/
|
|
7505
|
-
async function _HyperLiquidProvider_cancelTwapOrder(params) {
|
|
7506
|
-
await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_ensureReady).call(this);
|
|
7507
|
-
const coinValidation = (0, hyperLiquidValidation_js_1.validateCoinExists)(params.symbol, __classPrivateFieldGet(this, _HyperLiquidProvider_symbolToAssetId, "f"));
|
|
7508
|
-
if (!coinValidation.isValid) {
|
|
7509
|
-
throw new Error(coinValidation.error);
|
|
7510
|
-
}
|
|
7511
|
-
await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_ensureReadyForTrading).call(this);
|
|
7512
|
-
const assetId = await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_getAssetIdWithRepair).call(this, {
|
|
7513
|
-
symbol: params.symbol,
|
|
7514
|
-
dexName: (0, hyperLiquidAdapter_js_1.parseAssetName)(params.symbol).dex,
|
|
7515
|
-
});
|
|
7516
|
-
const result = await __classPrivateFieldGet(this, _HyperLiquidProvider_clientService, "f").getExchangeClient().twapCancel({
|
|
7517
|
-
a: assetId,
|
|
7518
|
-
t: parseInt(params.orderId, 10),
|
|
7519
|
-
});
|
|
7520
|
-
const status = result.response?.data?.status;
|
|
7521
|
-
if (status === 'success') {
|
|
7522
|
-
return { success: true, orderId: params.orderId };
|
|
7523
|
-
}
|
|
7524
|
-
const rawError = status?.error ??
|
|
7525
|
-
'TWAP cancellation failed';
|
|
7526
|
-
return (0, hyperLiquidValidation_js_1.createErrorResult)(__classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_mapError).call(this, new Error(rawError)), {
|
|
7527
|
-
success: false,
|
|
7528
|
-
orderId: params.orderId,
|
|
7529
|
-
});
|
|
7530
|
-
}, _HyperLiquidProvider_cancelScaleOrder =
|
|
7531
|
-
/**
|
|
7532
|
-
* Cancel every child of a scale ladder in one batch.
|
|
7533
|
-
*
|
|
7534
|
-
* @param params - Cancellation parameters, with `orderId` carrying the group handle.
|
|
7535
|
-
* @returns A promise that resolves to the result.
|
|
7536
|
-
*/
|
|
7537
|
-
async function _HyperLiquidProvider_cancelScaleOrder(params) {
|
|
7538
|
-
const group = __classPrivateFieldGet(this, _HyperLiquidProvider_scaleOrderGroups, "f").get(params.orderId);
|
|
7539
|
-
if (!group) {
|
|
7540
|
-
throw new Error(perpsErrorCodes_js_1.PERPS_ERROR_CODES.ORDER_STRATEGY_HANDLE_UNKNOWN);
|
|
7541
|
-
}
|
|
7542
|
-
await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_ensureReadyForTrading).call(this);
|
|
7543
|
-
const result = await __classPrivateFieldGet(this, _HyperLiquidProvider_clientService, "f").getExchangeClient().cancel({
|
|
7544
|
-
cancels: group.orderIds.map((orderId) => ({
|
|
7545
|
-
a: group.assetId,
|
|
7546
|
-
o: parseInt(orderId, 10),
|
|
7547
|
-
})),
|
|
7548
|
-
});
|
|
7549
|
-
const statuses = result.response?.data?.statuses ?? [];
|
|
7550
|
-
// A rung that filled or was cancelled individually comes back as a
|
|
7551
|
-
// rejection, but nothing of it is resting — counting it as still live would
|
|
7552
|
-
// pin the handle open for a ladder that is entirely off the book.
|
|
7553
|
-
const remaining = group.orderIds.filter((_unused, index) => classifyCancelStatus(statuses[index]) === 'refused');
|
|
7554
|
-
if (remaining.length === 0) {
|
|
7555
|
-
__classPrivateFieldGet(this, _HyperLiquidProvider_scaleOrderGroups, "f").delete(params.orderId);
|
|
7556
|
-
return { success: true, orderId: params.orderId };
|
|
7557
|
-
}
|
|
7558
|
-
// A rung the exchange refused to cancel may still be resting. Keeping the
|
|
7559
|
-
// handle registered against what is left means the caller can retry the
|
|
7560
|
-
// same cancel rather than being told the ladder is gone when it is not.
|
|
7561
|
-
__classPrivateFieldGet(this, _HyperLiquidProvider_scaleOrderGroups, "f").set(params.orderId, {
|
|
7562
|
-
assetId: group.assetId,
|
|
7563
|
-
orderIds: remaining,
|
|
7564
|
-
});
|
|
7565
|
-
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('Scale group cancel left children resting', {
|
|
7566
|
-
groupId: params.orderId,
|
|
7567
|
-
remaining: remaining.length,
|
|
7568
|
-
total: group.orderIds.length,
|
|
7569
|
-
});
|
|
7570
|
-
return (0, hyperLiquidValidation_js_1.createErrorResult)(new Error(perpsErrorCodes_js_1.PERPS_ERROR_CODES.ORDER_STRATEGY_CANCEL_INCOMPLETE), { success: false, orderId: params.orderId });
|
|
7571
|
-
}, _HyperLiquidProvider_cancelChaseOrder =
|
|
7572
|
-
/**
|
|
7573
|
-
* Stop a chase session and cancel whatever it still has resting.
|
|
7574
|
-
*
|
|
7575
|
-
* @param params - Cancellation parameters, with `orderId` carrying the session handle.
|
|
7576
|
-
* @returns A promise that resolves to the result.
|
|
7577
|
-
*/
|
|
7578
|
-
async function _HyperLiquidProvider_cancelChaseOrder(params) {
|
|
7579
|
-
const session = __classPrivateFieldGet(this, _HyperLiquidProvider_chaseSessions, "f").get(params.orderId);
|
|
7580
|
-
if (!session) {
|
|
7581
|
-
throw new Error(perpsErrorCodes_js_1.PERPS_ERROR_CODES.ORDER_STRATEGY_HANDLE_UNKNOWN);
|
|
7582
|
-
}
|
|
7583
|
-
// Stopped first: a tick that fired between here and the cancel below would
|
|
7584
|
-
// otherwise re-place the very order being cancelled.
|
|
7585
|
-
__classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_stopChaseSession).call(this, params.orderId);
|
|
7586
|
-
// A replacement already in flight cannot be called off, so the cancel waits
|
|
7587
|
-
// for it and then cancels whatever it rested. Deciding without waiting would
|
|
7588
|
-
// read the session's null `orderId` as "nothing rests" and report a clean
|
|
7589
|
-
// cancellation over an order that was about to appear on the book.
|
|
7590
|
-
if (session.pendingReplacement) {
|
|
7591
|
-
await session.pendingReplacement;
|
|
7592
|
-
}
|
|
7593
|
-
// Nothing rests: the order filled, something else cancelled it, or a
|
|
7594
|
-
// replacement failed to go up. Either way the session ends cleanly.
|
|
7595
|
-
if (session.orderId === null) {
|
|
7596
|
-
__classPrivateFieldGet(this, _HyperLiquidProvider_chaseSessions, "f").delete(params.orderId);
|
|
7597
|
-
return { success: true, orderId: params.orderId };
|
|
7598
|
-
}
|
|
7599
|
-
await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_ensureReadyForTrading).call(this);
|
|
7600
|
-
// Only a refusal leaves an order behind. A child that had already filled or
|
|
7601
|
-
// been cancelled is reported as a rejection too, but nothing of it is
|
|
7602
|
-
// resting — treating that as a failure would pin the handle open forever on
|
|
7603
|
-
// a chase that is entirely finished.
|
|
7604
|
-
if ((await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_cancelChaseChild).call(this, session)) === 'refused') {
|
|
7605
|
-
// The order is still resting. The session stays registered — stopped, but
|
|
7606
|
-
// still cancellable — so the caller can retry with the same handle.
|
|
7607
|
-
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('Chase cancel left its order resting', {
|
|
7608
|
-
sessionId: params.orderId,
|
|
7609
|
-
orderId: session.orderId,
|
|
7610
|
-
});
|
|
7611
|
-
return (0, hyperLiquidValidation_js_1.createErrorResult)(new Error(perpsErrorCodes_js_1.PERPS_ERROR_CODES.ORDER_STRATEGY_CANCEL_INCOMPLETE), { success: false, orderId: params.orderId });
|
|
7612
|
-
}
|
|
7613
|
-
__classPrivateFieldGet(this, _HyperLiquidProvider_chaseSessions, "f").delete(params.orderId);
|
|
7614
|
-
return { success: true, orderId: params.orderId };
|
|
7615
|
-
}, _HyperLiquidProvider_readOrderIdFromStatus = function _HyperLiquidProvider_readOrderIdFromStatus(status) {
|
|
7616
|
-
if (!isStatusObject(status)) {
|
|
7617
|
-
return undefined;
|
|
7618
|
-
}
|
|
7619
|
-
// `hasProperty` types the property as `unknown`, so each branch is cast to
|
|
7620
|
-
// the only shape the exchange puts there.
|
|
7621
|
-
const restingOrder = (0, utils_1.hasProperty)(status, 'resting')
|
|
7622
|
-
? status.resting
|
|
7623
|
-
: undefined;
|
|
7624
|
-
const filledOrder = (0, utils_1.hasProperty)(status, 'filled')
|
|
7625
|
-
? status.filled
|
|
7626
|
-
: undefined;
|
|
7627
|
-
return (restingOrder?.oid?.toString() ?? filledOrder?.oid?.toString() ?? undefined);
|
|
7628
|
-
}, _HyperLiquidProvider_getDiscountedBuilderFee = function _HyperLiquidProvider_getDiscountedBuilderFee() {
|
|
7629
|
-
if (__classPrivateFieldGet(this, _HyperLiquidProvider_userFeeDiscountBips, "f") === undefined) {
|
|
7630
|
-
return hyperLiquidConfig_js_1.BUILDER_FEE_CONFIG.MaxFeeTenthsBps;
|
|
7631
|
-
}
|
|
7632
|
-
return Math.floor(hyperLiquidConfig_js_1.BUILDER_FEE_CONFIG.MaxFeeTenthsBps *
|
|
7633
|
-
(1 - __classPrivateFieldGet(this, _HyperLiquidProvider_userFeeDiscountBips, "f") / hyperLiquidConfig_js_1.BASIS_POINTS_DIVISOR));
|
|
7634
6372
|
}, _HyperLiquidProvider_fetchOpenOrders =
|
|
7635
6373
|
/**
|
|
7636
6374
|
* Read the account's currently resting orders.
|