@metamask-previews/perps-controller 12.2.0-preview-e68ab1498 → 13.0.0-preview-56ae4c857
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 +15 -6
- package/dist/PerpsController-method-action-types.cjs.map +1 -1
- package/dist/PerpsController-method-action-types.d.cts +26 -3
- package/dist/PerpsController-method-action-types.d.cts.map +1 -1
- package/dist/PerpsController-method-action-types.d.mts +26 -3
- package/dist/PerpsController-method-action-types.d.mts.map +1 -1
- package/dist/PerpsController-method-action-types.mjs.map +1 -1
- package/dist/PerpsController.cjs +392 -102
- package/dist/PerpsController.cjs.map +1 -1
- package/dist/PerpsController.d.cts +26 -7
- package/dist/PerpsController.d.cts.map +1 -1
- package/dist/PerpsController.d.mts +26 -7
- package/dist/PerpsController.d.mts.map +1 -1
- package/dist/PerpsController.mjs +392 -102
- package/dist/PerpsController.mjs.map +1 -1
- package/dist/constants/eventNames.cjs +1 -1
- package/dist/constants/eventNames.cjs.map +1 -1
- package/dist/constants/eventNames.d.cts +1 -1
- package/dist/constants/eventNames.d.mts +1 -1
- package/dist/constants/eventNames.mjs +1 -1
- package/dist/constants/eventNames.mjs.map +1 -1
- package/dist/constants/hyperLiquidConfig.cjs +14 -2
- package/dist/constants/hyperLiquidConfig.cjs.map +1 -1
- package/dist/constants/hyperLiquidConfig.d.cts +10 -0
- package/dist/constants/hyperLiquidConfig.d.cts.map +1 -1
- package/dist/constants/hyperLiquidConfig.d.mts +10 -0
- package/dist/constants/hyperLiquidConfig.d.mts.map +1 -1
- package/dist/constants/hyperLiquidConfig.mjs +12 -0
- package/dist/constants/hyperLiquidConfig.mjs.map +1 -1
- package/dist/constants/perpsConfig.cjs +3 -4
- package/dist/constants/perpsConfig.cjs.map +1 -1
- package/dist/constants/perpsConfig.d.cts +2 -4
- package/dist/constants/perpsConfig.d.cts.map +1 -1
- package/dist/constants/perpsConfig.d.mts +2 -4
- package/dist/constants/perpsConfig.d.mts.map +1 -1
- package/dist/constants/perpsConfig.mjs +3 -4
- package/dist/constants/perpsConfig.mjs.map +1 -1
- package/dist/index.cjs +8 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -3
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +4 -3
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -0
- package/dist/index.mjs.map +1 -1
- package/dist/perpsErrorCodes.cjs +5 -1
- package/dist/perpsErrorCodes.cjs.map +1 -1
- package/dist/perpsErrorCodes.d.cts +4 -0
- package/dist/perpsErrorCodes.d.cts.map +1 -1
- package/dist/perpsErrorCodes.d.mts +4 -0
- package/dist/perpsErrorCodes.d.mts.map +1 -1
- package/dist/perpsErrorCodes.mjs +5 -1
- package/dist/perpsErrorCodes.mjs.map +1 -1
- package/dist/providers/AggregatedPerpsProvider.cjs +104 -14
- package/dist/providers/AggregatedPerpsProvider.cjs.map +1 -1
- package/dist/providers/AggregatedPerpsProvider.d.cts +34 -2
- package/dist/providers/AggregatedPerpsProvider.d.cts.map +1 -1
- package/dist/providers/AggregatedPerpsProvider.d.mts +34 -2
- package/dist/providers/AggregatedPerpsProvider.d.mts.map +1 -1
- package/dist/providers/AggregatedPerpsProvider.mjs +102 -13
- package/dist/providers/AggregatedPerpsProvider.mjs.map +1 -1
- package/dist/providers/HyperLiquidProvider.cjs +2141 -637
- package/dist/providers/HyperLiquidProvider.cjs.map +1 -1
- package/dist/providers/HyperLiquidProvider.d.cts +37 -17
- package/dist/providers/HyperLiquidProvider.d.cts.map +1 -1
- package/dist/providers/HyperLiquidProvider.d.mts +37 -17
- package/dist/providers/HyperLiquidProvider.d.mts.map +1 -1
- package/dist/providers/HyperLiquidProvider.mjs +2146 -642
- package/dist/providers/HyperLiquidProvider.mjs.map +1 -1
- package/dist/types/index.cjs.map +1 -1
- package/dist/types/index.d.cts +96 -3
- package/dist/types/index.d.cts.map +1 -1
- package/dist/types/index.d.mts +96 -3
- package/dist/types/index.d.mts.map +1 -1
- package/dist/types/index.mjs.map +1 -1
- package/dist/utils/capabilitySymbols.cjs +25 -0
- package/dist/utils/capabilitySymbols.cjs.map +1 -0
- package/dist/utils/capabilitySymbols.d.cts +15 -0
- package/dist/utils/capabilitySymbols.d.cts.map +1 -0
- package/dist/utils/capabilitySymbols.d.mts +15 -0
- package/dist/utils/capabilitySymbols.d.mts.map +1 -0
- package/dist/utils/capabilitySymbols.mjs +21 -0
- package/dist/utils/capabilitySymbols.mjs.map +1 -0
- package/dist/utils/hyperLiquidAdapter.cjs +20 -1
- package/dist/utils/hyperLiquidAdapter.cjs.map +1 -1
- package/dist/utils/hyperLiquidAdapter.d.cts +1 -0
- package/dist/utils/hyperLiquidAdapter.d.cts.map +1 -1
- package/dist/utils/hyperLiquidAdapter.d.mts +1 -0
- package/dist/utils/hyperLiquidAdapter.d.mts.map +1 -1
- package/dist/utils/hyperLiquidAdapter.mjs +19 -0
- package/dist/utils/hyperLiquidAdapter.mjs.map +1 -1
- package/dist/utils/hyperLiquidValidation.cjs +1 -1
- package/dist/utils/hyperLiquidValidation.cjs.map +1 -1
- package/dist/utils/hyperLiquidValidation.d.cts +1 -1
- package/dist/utils/hyperLiquidValidation.d.mts +1 -1
- package/dist/utils/hyperLiquidValidation.mjs +1 -1
- package/dist/utils/hyperLiquidValidation.mjs.map +1 -1
- package/dist/utils/stringParseUtils.cjs +17 -1
- package/dist/utils/stringParseUtils.cjs.map +1 -1
- package/dist/utils/stringParseUtils.d.cts +8 -0
- package/dist/utils/stringParseUtils.d.cts.map +1 -1
- package/dist/utils/stringParseUtils.d.mts +8 -0
- package/dist/utils/stringParseUtils.d.mts.map +1 -1
- package/dist/utils/stringParseUtils.mjs +15 -0
- package/dist/utils/stringParseUtils.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -9,12 +9,13 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
9
9
|
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");
|
|
10
10
|
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
11
11
|
};
|
|
12
|
-
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_subscriptionBuilderApprovalEpoch, _HyperLiquidProvider_approvedBuilderAddresses, _HyperLiquidProvider_compiledAllowlistPatterns, _HyperLiquidProvider_compiledBlocklistPatterns, _HyperLiquidProvider_userFeeDiscountBips, _HyperLiquidProvider_userFeeResolution, _HyperLiquidProvider_hip3Enabled, _HyperLiquidProvider_allowlistMarkets, _HyperLiquidProvider_blocklistMarkets, _HyperLiquidProvider_useUnifiedAccount, _HyperLiquidProvider_dexDiscoveryComplete, _HyperLiquidProvider_unifiedAccountSetupNeedsRetry, _HyperLiquidProvider_pendingValidatedDexsPromise, _HyperLiquidProvider_cachedUsdcTokenId, _HyperLiquidProvider_errorMappings, _HyperLiquidProvider_scaleOrderGroups, _HyperLiquidProvider_chaseSessions, _HyperLiquidProvider_chaseTerminations, _HyperLiquidProvider_terminatedChaseHandles, _HyperLiquidProvider_chasePlacementsInFlight, _HyperLiquidProvider_chasePlacementWaiters, _HyperLiquidProvider_chaseTickQueue,
|
|
13
|
-
import { hasProperty } from "@metamask/utils";
|
|
12
|
+
var _HyperLiquidProvider_instances, _a, _HyperLiquidProvider_deps, _HyperLiquidProvider_clientService, _HyperLiquidProvider_walletService, _HyperLiquidProvider_subscriptionService, _HyperLiquidProvider_symbolToAssetId, _HyperLiquidProvider_userFeeCache, _HyperLiquidProvider_maxLeverageCache, _HyperLiquidProvider_cachedMetaByDex, _HyperLiquidProvider_orderCapabilitiesMarketsByDex, _HyperLiquidProvider_orderCapabilitiesRefreshByDex, _HyperLiquidProvider_isDisconnected, _HyperLiquidProvider_disconnectOperationsInFlight, _HyperLiquidProvider_disconnectOperationPromise, _HyperLiquidProvider_lifecycleGeneration, _HyperLiquidProvider_cachedMarketDataWithPrices, _HyperLiquidProvider_cachedSpotMeta, _HyperLiquidProvider_dexDiscoveryCache, _HyperLiquidProvider_referralCheckCache, _HyperLiquidProvider_builderFeeCheckCache, _HyperLiquidProvider_ensureReadyPromise, _HyperLiquidProvider_pendingBuilderFeeApprovals, _HyperLiquidProvider_subscriptionBuilderApprovalEpoch, _HyperLiquidProvider_approvedBuilderAddresses, _HyperLiquidProvider_compiledAllowlistPatterns, _HyperLiquidProvider_compiledBlocklistPatterns, _HyperLiquidProvider_userFeeDiscountBips, _HyperLiquidProvider_userFeeResolution, _HyperLiquidProvider_hip3Enabled, _HyperLiquidProvider_allowlistMarkets, _HyperLiquidProvider_blocklistMarkets, _HyperLiquidProvider_useUnifiedAccount, _HyperLiquidProvider_dexDiscoveryComplete, _HyperLiquidProvider_unifiedAccountSetupNeedsRetry, _HyperLiquidProvider_pendingValidatedDexsPromise, _HyperLiquidProvider_cachedUsdcTokenId, _HyperLiquidProvider_errorMappings, _HyperLiquidProvider_scaleOrderGroups, _HyperLiquidProvider_cancelledScaleOrderGroups, _HyperLiquidProvider_trackedTwapOrdersByMessenger, _HyperLiquidProvider_trackedTwapOrders, _HyperLiquidProvider_chaseSessions, _HyperLiquidProvider_chaseTerminations, _HyperLiquidProvider_terminatedChaseHandles, _HyperLiquidProvider_chasePlacementsInFlight, _HyperLiquidProvider_chasePlacementWaiters, _HyperLiquidProvider_chaseTickQueue, _HyperLiquidProvider_strategyGeneration, _HyperLiquidProvider_chasePlacementBlockers, _HyperLiquidProvider_clientsInitialized, _HyperLiquidProvider_initializationPromise, _HyperLiquidProvider_messenger, _HyperLiquidProvider_builderAddressTestnet, _HyperLiquidProvider_builderAddressMainnet, _HyperLiquidProvider_subscriptionBuilderAddressTestnet, _HyperLiquidProvider_subscriptionBuilderAddressMainnet, _HyperLiquidProvider_priceDeviationLimit, _HyperLiquidProvider_onChaseOrderMaxDistanceReached, _HyperLiquidProvider_resolveOrderFeePolicy, _HyperLiquidProvider_assertProviderLifecycleCurrent, _HyperLiquidProvider_assertCacheWriteLifecycleCurrent, _HyperLiquidProvider_isCacheWriteLifecycleCurrent, _HyperLiquidProvider_getFreshOrderCapabilityMarkets, _HyperLiquidProvider_compilePatternsSafely, _HyperLiquidProvider_ensureClientsInitialized, _HyperLiquidProvider_isWalletOnHyperliquid, _HyperLiquidProvider_isHyperliquidMultiSigAccount, _HyperLiquidProvider_ensureUnifiedAccountEnabled, _HyperLiquidProvider_ensureReady, _HyperLiquidProvider_tradingSetupPromise, _HyperLiquidProvider_tradingSetupComplete, _HyperLiquidProvider_builderFeeSetupPromises, _HyperLiquidProvider_ensureBuilderFeeSetup, _HyperLiquidProvider_ensureReadyForTrading, _HyperLiquidProvider_getOrFetchPrice, _HyperLiquidProvider_filterFills, _HyperLiquidProvider_getAllAvailableDexs, _HyperLiquidProvider_getValidatedDexs, _HyperLiquidProvider_fetchValidatedDexsInternal, _HyperLiquidProvider_getCachedMeta, _HyperLiquidProvider_fetchMeta, _HyperLiquidProvider_cacheDexMetadata, _HyperLiquidProvider_cacheDexMetadataFromRead, _HyperLiquidProvider_backfillAssetMapForDex, _HyperLiquidProvider_getAssetIdWithRepair, _HyperLiquidProvider_getCachedSpotMeta, _HyperLiquidProvider_getCachedPerpDexs, _HyperLiquidProvider_calculateHip3FeeMultiplier, _HyperLiquidProvider_getCacheKey, _HyperLiquidProvider_getApprovedBuilderKey, _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_placeStrategyOrder, _HyperLiquidProvider_submitPreparedStrategy, _HyperLiquidProvider_prepareStrategyPlacement, _HyperLiquidProvider_getMinimumOrderSize, _HyperLiquidProvider_buildScaleLadder, _HyperLiquidProvider_placeTwapOrder, _HyperLiquidProvider_placeScaleOrder, _HyperLiquidProvider_startChaseSession, _HyperLiquidProvider_getChaseQuotePrice, _HyperLiquidProvider_restChaseOrder, _HyperLiquidProvider_scheduleChaseTick, _HyperLiquidProvider_pauseChaseTicksForPlacement, _HyperLiquidProvider_resumeChaseTicksAfterPlacement, _HyperLiquidProvider_recoverFailedChaseTick, _HyperLiquidProvider_runChaseTick, _HyperLiquidProvider_resolveOwnRestingSizes, _HyperLiquidProvider_readOrderRemainder, _HyperLiquidProvider_cancelChaseChild, _HyperLiquidProvider_retractOrphanedChaseOrder, _HyperLiquidProvider_stopChaseSession, _HyperLiquidProvider_rebalanceChaseSession, _HyperLiquidProvider_adaptTwapOrder, _HyperLiquidProvider_rebalanceTrackedTwapOrder, _HyperLiquidProvider_rebalanceTerminalHip3Twaps, _HyperLiquidProvider_cancelStrategyOrder, _HyperLiquidProvider_cancelTwapOrder, _HyperLiquidProvider_cancelScaleOrder, _HyperLiquidProvider_cancelChaseOrder, _HyperLiquidProvider_performChaseTermination, _HyperLiquidProvider_cancelOrderRequests, _HyperLiquidProvider_cancelOrderRequestBatch, _HyperLiquidProvider_readOrderPlacementOutcome, _HyperLiquidProvider_readOrderIdFromStatus, _HyperLiquidProvider_getDiscountedBuilderFee, _HyperLiquidProvider_getBuilderOrderContext, _HyperLiquidProvider_fetchOpenOrders, _HyperLiquidProvider_resolveReplacementOrderId, _HyperLiquidProvider_getStandaloneValidatedDexs, _HyperLiquidProvider_queryDexPositions, _HyperLiquidProvider_restoreScaleOrderGroups, _HyperLiquidProvider_getAllMids, _HyperLiquidProvider_fetchSingleDexFresh, _HyperLiquidProvider_excludeNonUsdcCollateralResults, _HyperLiquidProvider_mergeDexResultsInto, _HyperLiquidProvider_cacheFreshMarketDataSnapshot, _HyperLiquidProvider_getStaleMarketDataSnapshot, _HyperLiquidProvider_isFeeCacheValid, _HyperLiquidProvider_performDisconnect, _HyperLiquidProvider_getBuilderAddress, _HyperLiquidProvider_getSubscriptionBuilderAddress, _HyperLiquidProvider_getReferralCode, _HyperLiquidProvider_ensureReferralSet, _HyperLiquidProvider_isReferralCodeReady, _HyperLiquidProvider_checkReferralSet, _HyperLiquidProvider_setReferralCode;
|
|
13
|
+
import { hasProperty, isHexString } from "@metamask/utils";
|
|
14
|
+
import { BigNumber } from "bignumber.js";
|
|
14
15
|
import { v4 as uuidv4 } from "uuid";
|
|
15
16
|
import { PERPS_EVENT_PROPERTY, PERPS_EVENT_VALUE } from "../constants/eventNames.mjs";
|
|
16
|
-
import { BASIS_POINTS_DIVISOR, BUILDER_FEE_CONFIG, canonicalizeHyperLiquidDexes, FEE_RATES, getBridgeInfo, getChainId, HIP3_ASSET_MARKET_TYPES, HIP3_FEE_CONFIG, HIP3_MARGIN_CONFIG, HYPERLIQUID_ASSET_NAMES, HYPERLIQUID_CONFIG, HYPERLIQUID_WITHDRAWAL_MINUTES, REFERRAL_CONFIG, TRADING_DEFAULTS, USDC_DECIMALS, USDC_SYMBOL } from "../constants/hyperLiquidConfig.mjs";
|
|
17
|
-
import { CHASE_ORDER_CONFIG, CHASE_ORDER_STATUS, HYPERLIQUID_TWAP_LIMITS, ORDER_SLIPPAGE_CONFIG, PERFORMANCE_CONFIG, PERPS_CONSTANTS, TP_SL_CONFIG, WITHDRAWAL_CONSTANTS } from "../constants/perpsConfig.mjs";
|
|
17
|
+
import { BASIS_POINTS_DIVISOR, BUILDER_FEE_CONFIG, canonicalizeHyperLiquidDexes, FEE_RATES, HYPERLIQUID_ORDER_CAPABILITIES, getBridgeInfo, getChainId, HIP3_ASSET_MARKET_TYPES, HIP3_FEE_CONFIG, HIP3_MARGIN_CONFIG, HYPERLIQUID_ASSET_NAMES, HYPERLIQUID_CONFIG, HYPERLIQUID_WITHDRAWAL_MINUTES, REFERRAL_CONFIG, TRADING_DEFAULTS, USDC_DECIMALS, USDC_SYMBOL } from "../constants/hyperLiquidConfig.mjs";
|
|
18
|
+
import { CHASE_ORDER_CONFIG, CHASE_ORDER_STATUS, HYPERLIQUID_TWAP_LIMITS, ORDER_SLIPPAGE_CONFIG, PERFORMANCE_CONFIG, PERPS_CONSTANTS, PROVIDER_CONFIG, TP_SL_CONFIG, WITHDRAWAL_CONSTANTS } from "../constants/perpsConfig.mjs";
|
|
18
19
|
import { PERPS_TRANSACTIONS_HISTORY_CONSTANTS } from "../constants/transactionsHistoryConfig.mjs";
|
|
19
20
|
import { PERPS_ERROR_CODES } from "../perpsErrorCodes.mjs";
|
|
20
21
|
import { DexDiscoveryCacheManager } from "../services/DexDiscoveryCacheManager.mjs";
|
|
@@ -25,16 +26,18 @@ import { TradingReadinessCache, PerpsSigningCache } from "../services/TradingRea
|
|
|
25
26
|
import { HL_ABSTRACTION_WIRE, HL_UNIFIED_ACCOUNT_MODE, hyperLiquidModeFoldsSpot } from "../types/hyperliquid-types.mjs";
|
|
26
27
|
import { PerpsAnalyticsEvent } from "../types/index.mjs";
|
|
27
28
|
import { addSpotBalanceToAccountState, aggregateAccountStates } from "../utils/accountUtils.mjs";
|
|
29
|
+
import { isValidCapabilitySymbol } from "../utils/capabilitySymbols.mjs";
|
|
28
30
|
import { ensureError, isHyperLiquidMultiSigRequiredError, isHyperLiquidUserNotFoundError, isKeyringLockedError } from "../utils/errorUtils.mjs";
|
|
29
31
|
import { shouldDeferUnifiedAccountSetup } from "../utils/hyperLiquidAbstraction.mjs";
|
|
30
|
-
import { adaptAccountStateFromSDK, adaptHyperLiquidLedgerUpdateToUserHistoryItem, adaptMarketFromSDK, adaptOrderFromSDK, adaptPositionFromSDK, adaptPositionTriggerOrderFromSDK, adaptTpslLinkageToGrouping, buildAssetMapping, formatHyperLiquidPrice, formatHyperLiquidSize, parseAssetName } from "../utils/hyperLiquidAdapter.mjs";
|
|
32
|
+
import { adaptAccountStateFromSDK, adaptHyperLiquidLedgerUpdateToUserHistoryItem, adaptMarketFromSDK, adaptOrderFromSDK, adaptPositionFromSDK, adaptPositionTriggerOrderFromSDK, adaptTpslLinkageToGrouping, buildAssetMapping, formatHyperLiquidPrice, formatHyperLiquidSize, HYPERLIQUID_SCALE_CLOID_MARKER, parseAssetName } from "../utils/hyperLiquidAdapter.mjs";
|
|
31
33
|
import { createErrorResult, getMaxOrderValue, getSupportedPaths, validateAssetSupport, validateBalance, validateCoinExists, validateDepositParams, validateOrderParams, validateWithdrawalParams } from "../utils/hyperLiquidValidation.mjs";
|
|
32
34
|
import { generatePerpsId } from "../utils/idUtils.mjs";
|
|
33
35
|
import { transformMarketData } from "../utils/marketDataTransform.mjs";
|
|
34
36
|
import { compileMarketPattern, shouldIncludeMarket } from "../utils/marketUtils.mjs";
|
|
35
37
|
import { buildOrdersArray, calculateFinalPositionSize, calculateOrderPriceAndSize, computeChaseQuotePrice, computeScalePriceLadder, floorToSizeDecimals, formatPartialTpslSize, getPriceTick, splitScaleSizes, validateOrderPrecision } from "../utils/orderCalculations.mjs";
|
|
36
|
-
import { getTriggerExecution, isLimitExecutionOrderType, isStrategyOrderType, isTriggerOrderType, resolvePositionTriggerSummaryPrice, toSDKTimeInForce } from "../utils/orderTypes.mjs";
|
|
38
|
+
import { getTriggerDirection, getTriggerExecution, isLimitExecutionOrderType, isStrategyOrderType, isTriggerOrderType, resolvePositionTriggerSummaryPrice, toSDKTimeInForce } from "../utils/orderTypes.mjs";
|
|
37
39
|
import { createStandaloneInfoClient, queryStandaloneClearinghouseStates, queryStandaloneOpenOrders } from "../utils/standaloneInfoClient.mjs";
|
|
40
|
+
import { parseBoundedNonNegativeDecimal } from "../utils/stringParseUtils.mjs";
|
|
38
41
|
// getStreamManagerInstance removed: use this.#deps.streamManager instead
|
|
39
42
|
/**
|
|
40
43
|
* Type guard to check if a status is an object (not a string literal like "waitingForFill")
|
|
@@ -59,15 +62,54 @@ const ALREADY_GONE_CANCEL_MARKERS = [
|
|
|
59
62
|
'already canceled',
|
|
60
63
|
'already cancelled',
|
|
61
64
|
'order not found',
|
|
65
|
+
'twap not found',
|
|
62
66
|
];
|
|
63
67
|
const RETRYABLE_CHASE_PLACEMENT_MARKERS = [
|
|
64
68
|
'post only order would have immediately matched',
|
|
65
69
|
'price too far from oracle',
|
|
66
70
|
];
|
|
71
|
+
const CHASE_ORDER_STATUS_RETRY_COUNT = 2;
|
|
72
|
+
const CHASE_ORDER_STATUS_RETRY_DELAY_MS = 100;
|
|
73
|
+
const CHASE_ORDER_STATUS_UNAVAILABLE = 'status_unavailable';
|
|
74
|
+
class ChaseOrderStatusUnavailableError extends Error {
|
|
75
|
+
constructor() {
|
|
76
|
+
super('Chase order status unavailable');
|
|
77
|
+
this.name = 'ChaseOrderStatusUnavailableError';
|
|
78
|
+
}
|
|
79
|
+
}
|
|
67
80
|
const isRetryableChasePlacementError = (error) => {
|
|
68
81
|
const message = ensureError(error, 'HyperLiquidProvider.startChaseSession').message.toLowerCase();
|
|
69
82
|
return RETRYABLE_CHASE_PLACEMENT_MARKERS.some((marker) => message.includes(marker));
|
|
70
83
|
};
|
|
84
|
+
/**
|
|
85
|
+
* What happened to one order a cancel was asked to remove.
|
|
86
|
+
*
|
|
87
|
+
* `Cancelled` and `Gone` both mean nothing of it is resting, which is all a
|
|
88
|
+
* caller asked for; only `Refused` leaves an order behind.
|
|
89
|
+
*/
|
|
90
|
+
var CancelChildOutcome;
|
|
91
|
+
(function (CancelChildOutcome) {
|
|
92
|
+
CancelChildOutcome["Cancelled"] = "cancelled";
|
|
93
|
+
CancelChildOutcome["Gone"] = "gone";
|
|
94
|
+
CancelChildOutcome["Refused"] = "refused";
|
|
95
|
+
})(CancelChildOutcome || (CancelChildOutcome = {}));
|
|
96
|
+
var HyperLiquidTwapLifecycleStatus;
|
|
97
|
+
(function (HyperLiquidTwapLifecycleStatus) {
|
|
98
|
+
HyperLiquidTwapLifecycleStatus["Activated"] = "activated";
|
|
99
|
+
HyperLiquidTwapLifecycleStatus["Failed"] = "error";
|
|
100
|
+
HyperLiquidTwapLifecycleStatus["Finished"] = "finished";
|
|
101
|
+
HyperLiquidTwapLifecycleStatus["Stopped"] = "stopped";
|
|
102
|
+
HyperLiquidTwapLifecycleStatus["Terminated"] = "terminated";
|
|
103
|
+
HyperLiquidTwapLifecycleStatus["WaitingForTrigger"] = "waitingForTrigger";
|
|
104
|
+
})(HyperLiquidTwapLifecycleStatus || (HyperLiquidTwapLifecycleStatus = {}));
|
|
105
|
+
var PerpsTwapLifecycleStatus;
|
|
106
|
+
(function (PerpsTwapLifecycleStatus) {
|
|
107
|
+
PerpsTwapLifecycleStatus["Active"] = "active";
|
|
108
|
+
PerpsTwapLifecycleStatus["Canceled"] = "canceled";
|
|
109
|
+
PerpsTwapLifecycleStatus["Completed"] = "completed";
|
|
110
|
+
PerpsTwapLifecycleStatus["CompletedUnderfilled"] = "completed_underfilled";
|
|
111
|
+
PerpsTwapLifecycleStatus["Failed"] = "failed";
|
|
112
|
+
})(PerpsTwapLifecycleStatus || (PerpsTwapLifecycleStatus = {}));
|
|
71
113
|
/**
|
|
72
114
|
* Classify one entry of a cancel response.
|
|
73
115
|
*
|
|
@@ -76,14 +118,14 @@ const isRetryableChasePlacementError = (error) => {
|
|
|
76
118
|
*/
|
|
77
119
|
const classifyCancelStatus = (status) => {
|
|
78
120
|
if (status === 'success') {
|
|
79
|
-
return
|
|
121
|
+
return CancelChildOutcome.Cancelled;
|
|
80
122
|
}
|
|
81
123
|
const message = (isStatusObject(status) && typeof status.error === 'string'
|
|
82
124
|
? status.error
|
|
83
125
|
: '').toLowerCase();
|
|
84
126
|
return ALREADY_GONE_CANCEL_MARKERS.some((marker) => message.includes(marker))
|
|
85
|
-
?
|
|
86
|
-
:
|
|
127
|
+
? CancelChildOutcome.Gone
|
|
128
|
+
: CancelChildOutcome.Refused;
|
|
87
129
|
};
|
|
88
130
|
/**
|
|
89
131
|
* Read the best price on one side of the book, discounting the chase's own
|
|
@@ -185,6 +227,29 @@ const validateStrategyNotional = (params) => {
|
|
|
185
227
|
}
|
|
186
228
|
return { isValid: true };
|
|
187
229
|
};
|
|
230
|
+
/**
|
|
231
|
+
* Create one recoverable identity for a Scale ladder and one venue CLOID per
|
|
232
|
+
* rung. HyperLiquid requires every CLOID to be unique, so the last byte holds
|
|
233
|
+
* the rung index while the preceding 15 bytes identify the shared group.
|
|
234
|
+
*
|
|
235
|
+
* @param count - Number of ladder rungs.
|
|
236
|
+
* @returns The public group handle and venue client order IDs.
|
|
237
|
+
*/
|
|
238
|
+
const createScaleOrderIdentity = (count) => {
|
|
239
|
+
const groupKey = `${HYPERLIQUID_SCALE_CLOID_MARKER}${uuidv4()
|
|
240
|
+
.replace(/-/gu, '')
|
|
241
|
+
.slice(0, 22)}`;
|
|
242
|
+
const clientOrderIds = Array.from({ length: count }, (_, index) => {
|
|
243
|
+
const clientOrderId = `0x${groupKey}${index
|
|
244
|
+
.toString(16)
|
|
245
|
+
.padStart(2, '0')}`;
|
|
246
|
+
if (!isHexString(clientOrderId)) {
|
|
247
|
+
throw new Error('Failed to create Scale client order ID');
|
|
248
|
+
}
|
|
249
|
+
return clientOrderId;
|
|
250
|
+
});
|
|
251
|
+
return { groupId: `scale:${groupKey}`, clientOrderIds };
|
|
252
|
+
};
|
|
188
253
|
/**
|
|
189
254
|
* Collect the order IDs of every TP/SL child carried by a parent order.
|
|
190
255
|
*
|
|
@@ -296,6 +361,76 @@ function collectPositionTriggerOrders(params) {
|
|
|
296
361
|
...(stopLossSummaryPrice && { stopLossPrice: stopLossSummaryPrice }),
|
|
297
362
|
};
|
|
298
363
|
}
|
|
364
|
+
const MAX_API_FEE_RATE = 1;
|
|
365
|
+
const MAX_API_FEE_DISCOUNT = 1;
|
|
366
|
+
/**
|
|
367
|
+
* Fee applicability by canonical order type.
|
|
368
|
+
*
|
|
369
|
+
* The configuration type makes this map exhaustive, so a new order type cannot
|
|
370
|
+
* inherit a builder-fee policy accidentally. HyperLiquid's dedicated TWAP
|
|
371
|
+
* action has no builder field. Every other standalone placement uses an order
|
|
372
|
+
* action that can carry one builder context. This policy is deliberately not a
|
|
373
|
+
* constructor or order input. A future backend policy must be resolved inside
|
|
374
|
+
* the provider from its trusted source rather than accepted from a client.
|
|
375
|
+
*/
|
|
376
|
+
const HYPERLIQUID_ORDER_FEE_CONFIG = {
|
|
377
|
+
market: { chargesMetamaskBuilderFee: true },
|
|
378
|
+
limit: { chargesMetamaskBuilderFee: true },
|
|
379
|
+
stop_market: { chargesMetamaskBuilderFee: true },
|
|
380
|
+
stop_limit: { chargesMetamaskBuilderFee: true },
|
|
381
|
+
take_profit_market: { chargesMetamaskBuilderFee: true },
|
|
382
|
+
take_profit_limit: { chargesMetamaskBuilderFee: true },
|
|
383
|
+
twap: { chargesMetamaskBuilderFee: false },
|
|
384
|
+
scale: { chargesMetamaskBuilderFee: true },
|
|
385
|
+
chase: { chargesMetamaskBuilderFee: true },
|
|
386
|
+
};
|
|
387
|
+
const MILLISECONDS_PER_MINUTE = 60000;
|
|
388
|
+
const MILLISECONDS_PER_SECOND = 1000;
|
|
389
|
+
/**
|
|
390
|
+
* Normalize the TWAP history timestamp documented in seconds while tolerating
|
|
391
|
+
* an already-millisecond value from a future SDK response.
|
|
392
|
+
*
|
|
393
|
+
* @param timestamp - Venue timestamp in seconds or milliseconds.
|
|
394
|
+
* @returns Timestamp in milliseconds.
|
|
395
|
+
*/
|
|
396
|
+
const normalizeTwapHistoryTimestamp = (timestamp) => timestamp < 1000000000000
|
|
397
|
+
? timestamp * MILLISECONDS_PER_SECOND
|
|
398
|
+
: timestamp;
|
|
399
|
+
const getTwapOrderScopeKey = (params) => `${params.network}:${params.userAddress.toLowerCase()}:${params.orderId}`;
|
|
400
|
+
const resolveTwapOrderStatus = (historyEntry, executedSize, totalSize) => {
|
|
401
|
+
switch (historyEntry.status.status) {
|
|
402
|
+
case HyperLiquidTwapLifecycleStatus.Activated:
|
|
403
|
+
case HyperLiquidTwapLifecycleStatus.WaitingForTrigger:
|
|
404
|
+
return PerpsTwapLifecycleStatus.Active;
|
|
405
|
+
case HyperLiquidTwapLifecycleStatus.Finished:
|
|
406
|
+
return executedSize.isLessThan(totalSize)
|
|
407
|
+
? PerpsTwapLifecycleStatus.CompletedUnderfilled
|
|
408
|
+
: PerpsTwapLifecycleStatus.Completed;
|
|
409
|
+
case HyperLiquidTwapLifecycleStatus.Terminated:
|
|
410
|
+
case HyperLiquidTwapLifecycleStatus.Stopped:
|
|
411
|
+
return PerpsTwapLifecycleStatus.Canceled;
|
|
412
|
+
case HyperLiquidTwapLifecycleStatus.Failed:
|
|
413
|
+
return PerpsTwapLifecycleStatus.Failed;
|
|
414
|
+
default:
|
|
415
|
+
// A new venue status is not proof that a schedule stopped. Keep it
|
|
416
|
+
// non-terminal so collateral cannot be reclaimed from a live TWAP.
|
|
417
|
+
return PerpsTwapLifecycleStatus.Active;
|
|
418
|
+
}
|
|
419
|
+
};
|
|
420
|
+
const adaptTwapOrderFill = (entry) => ({
|
|
421
|
+
fillId: entry.fill.tid.toString(),
|
|
422
|
+
orderId: entry.fill.oid.toString(),
|
|
423
|
+
side: entry.fill.side === 'B' ? 'buy' : 'sell',
|
|
424
|
+
price: entry.fill.px,
|
|
425
|
+
size: entry.fill.sz,
|
|
426
|
+
fee: entry.fill.fee,
|
|
427
|
+
feeToken: entry.fill.feeToken,
|
|
428
|
+
...(entry.fill.builderFee === undefined
|
|
429
|
+
? {}
|
|
430
|
+
: { builderFee: entry.fill.builderFee }),
|
|
431
|
+
timestamp: entry.fill.time,
|
|
432
|
+
transactionHash: entry.fill.hash,
|
|
433
|
+
});
|
|
299
434
|
/**
|
|
300
435
|
* HyperLiquid provider implementation
|
|
301
436
|
*
|
|
@@ -310,7 +445,7 @@ function collectPositionTriggerOrders(params) {
|
|
|
310
445
|
export class HyperLiquidProvider {
|
|
311
446
|
constructor(options) {
|
|
312
447
|
_HyperLiquidProvider_instances.add(this);
|
|
313
|
-
this.protocolId =
|
|
448
|
+
this.protocolId = PROVIDER_CONFIG.DefaultProvider;
|
|
314
449
|
// Platform dependencies for logging and debugging
|
|
315
450
|
_HyperLiquidProvider_deps.set(this, void 0);
|
|
316
451
|
// Service instances
|
|
@@ -326,6 +461,16 @@ export class HyperLiquidProvider {
|
|
|
326
461
|
// Cache for raw meta responses (shared across methods to avoid redundant API calls)
|
|
327
462
|
// Filtering is applied on-demand (cheap array operations) - no need for separate processed cache
|
|
328
463
|
_HyperLiquidProvider_cachedMetaByDex.set(this, new Map());
|
|
464
|
+
// Fresh filtered markets used only for capability discovery. Each routed
|
|
465
|
+
// DEX owns its freshness window and coalesced refresh.
|
|
466
|
+
_HyperLiquidProvider_orderCapabilitiesMarketsByDex.set(this, new Map());
|
|
467
|
+
_HyperLiquidProvider_orderCapabilitiesRefreshByDex.set(this, new Map());
|
|
468
|
+
// Sticky for this instance. PerpsController discards disconnected providers
|
|
469
|
+
// during initialization; direct callers must call initialize() before reuse.
|
|
470
|
+
_HyperLiquidProvider_isDisconnected.set(this, false);
|
|
471
|
+
_HyperLiquidProvider_disconnectOperationsInFlight.set(this, 0);
|
|
472
|
+
_HyperLiquidProvider_disconnectOperationPromise.set(this, null);
|
|
473
|
+
_HyperLiquidProvider_lifecycleGeneration.set(this, 0);
|
|
329
474
|
// Last known-good market list for stale fallback when every enabled DEX fails in one fetch window.
|
|
330
475
|
_HyperLiquidProvider_cachedMarketDataWithPrices.set(this, null);
|
|
331
476
|
// Session cache for spot metadata (contains token info for HIP-3 collateral checks)
|
|
@@ -358,12 +503,9 @@ export class HyperLiquidProvider {
|
|
|
358
503
|
_HyperLiquidProvider_hip3Enabled.set(this, void 0);
|
|
359
504
|
_HyperLiquidProvider_allowlistMarkets.set(this, void 0);
|
|
360
505
|
_HyperLiquidProvider_blocklistMarkets.set(this, void 0);
|
|
361
|
-
//
|
|
362
|
-
//
|
|
363
|
-
//
|
|
364
|
-
// disable the migration via a hot-fix release if a regression surfaces
|
|
365
|
-
// in the wild — flipping this to false reverts to the legacy programmatic
|
|
366
|
-
// HIP-3 transfer path that already lives in the codebase.
|
|
506
|
+
// Legacy rollback switch. MetaMask production supports unified account and
|
|
507
|
+
// portfolio margin. The false branch and its manual HIP-3 transfers remain
|
|
508
|
+
// temporarily for rollback compatibility and will be removed separately.
|
|
367
509
|
_HyperLiquidProvider_useUnifiedAccount.set(this, void 0);
|
|
368
510
|
// True once DEX discovery has succeeded with real data (not a fallback).
|
|
369
511
|
// When false, #ensureReadyPromise is reset after each init so the next
|
|
@@ -390,6 +532,10 @@ export class HyperLiquidProvider {
|
|
|
390
532
|
// one cancel can reach every rung. Cleared on disconnect; the rung IDs are
|
|
391
533
|
// also returned to the caller as `OrderResult.childOrderIds`.
|
|
392
534
|
_HyperLiquidProvider_scaleOrderGroups.set(this, new Map());
|
|
535
|
+
// Prevent a stale open-order cache from recreating a group that this
|
|
536
|
+
// provider lifecycle already canceled successfully.
|
|
537
|
+
_HyperLiquidProvider_cancelledScaleOrderGroups.set(this, new Set());
|
|
538
|
+
_HyperLiquidProvider_trackedTwapOrders.set(this, void 0);
|
|
393
539
|
// Chase sessions running on this provider instance, keyed by session handle.
|
|
394
540
|
// Each owns a pending timer, so disconnect has to stop them.
|
|
395
541
|
_HyperLiquidProvider_chaseSessions.set(this, new Map());
|
|
@@ -412,11 +558,9 @@ export class HyperLiquidProvider {
|
|
|
412
558
|
// transport, so simultaneous cancel/replace ticks would create avoidable
|
|
413
559
|
// signed traffic and can starve a foreground placement.
|
|
414
560
|
_HyperLiquidProvider_chaseTickQueue.set(this, Promise.resolve());
|
|
415
|
-
//
|
|
416
|
-
//
|
|
417
|
-
|
|
418
|
-
// background timer against a provider that has already been torn down.
|
|
419
|
-
_HyperLiquidProvider_chaseGeneration.set(this, 0);
|
|
561
|
+
// Every strategy captures this before its round trips. A changed generation
|
|
562
|
+
// prevents a late response from registering handles after disconnect.
|
|
563
|
+
_HyperLiquidProvider_strategyGeneration.set(this, 0);
|
|
420
564
|
// Reference-counted because suspension and disconnect may overlap. One
|
|
421
565
|
// lifecycle owner finishing must not reopen placement while another still
|
|
422
566
|
// drains or tears down the provider.
|
|
@@ -431,27 +575,35 @@ export class HyperLiquidProvider {
|
|
|
431
575
|
_HyperLiquidProvider_subscriptionBuilderAddressTestnet.set(this, void 0);
|
|
432
576
|
_HyperLiquidProvider_subscriptionBuilderAddressMainnet.set(this, void 0);
|
|
433
577
|
_HyperLiquidProvider_priceDeviationLimit.set(this, void 0);
|
|
578
|
+
_HyperLiquidProvider_onChaseOrderMaxDistanceReached.set(this, void 0);
|
|
434
579
|
/**
|
|
435
580
|
* Ensure provider is ready for TRADING operations (signing required)
|
|
436
581
|
*
|
|
437
|
-
* This method performs
|
|
582
|
+
* This method performs shared setup that may require user signatures:
|
|
438
583
|
* - DEX abstraction enablement (for HIP-3 auto-transfers)
|
|
439
|
-
* - Builder fee approval (required for orders)
|
|
440
584
|
* - Referral code setup (attribution)
|
|
585
|
+
* - Builder fee approval when the whole action carries a builder context
|
|
441
586
|
*
|
|
442
587
|
* These operations are DEFERRED from ensureReady() to avoid hardware wallet prompt spam
|
|
443
588
|
* when users are just viewing the Perps section (critical for hardware wallets).
|
|
444
589
|
*
|
|
445
|
-
*
|
|
590
|
+
* General trading readiness is independent of builder approval. Native TWAP
|
|
591
|
+
* and cancellations call this with `requiresBuilderFee: false`.
|
|
446
592
|
*/
|
|
447
593
|
_HyperLiquidProvider_tradingSetupPromise.set(this, null);
|
|
448
594
|
_HyperLiquidProvider_tradingSetupComplete.set(this, false);
|
|
595
|
+
_HyperLiquidProvider_builderFeeSetupPromises.set(this, new Map());
|
|
449
596
|
__classPrivateFieldSet(this, _HyperLiquidProvider_deps, options.platformDependencies, "f");
|
|
450
597
|
__classPrivateFieldSet(this, _HyperLiquidProvider_messenger, options.messenger, "f");
|
|
598
|
+
const trackedTwapOrders = __classPrivateFieldGet(_a, _a, "f", _HyperLiquidProvider_trackedTwapOrdersByMessenger).get(__classPrivateFieldGet(this, _HyperLiquidProvider_messenger, "f")) ??
|
|
599
|
+
new Map();
|
|
600
|
+
__classPrivateFieldGet(_a, _a, "f", _HyperLiquidProvider_trackedTwapOrdersByMessenger).set(__classPrivateFieldGet(this, _HyperLiquidProvider_messenger, "f"), trackedTwapOrders);
|
|
601
|
+
__classPrivateFieldSet(this, _HyperLiquidProvider_trackedTwapOrders, trackedTwapOrders, "f");
|
|
451
602
|
__classPrivateFieldSet(this, _HyperLiquidProvider_builderAddressTestnet, options.builderAddressTestnet, "f");
|
|
452
603
|
__classPrivateFieldSet(this, _HyperLiquidProvider_builderAddressMainnet, options.builderAddressMainnet, "f");
|
|
453
604
|
__classPrivateFieldSet(this, _HyperLiquidProvider_subscriptionBuilderAddressTestnet, options.subscriptionBuilderAddressTestnet, "f");
|
|
454
605
|
__classPrivateFieldSet(this, _HyperLiquidProvider_subscriptionBuilderAddressMainnet, options.subscriptionBuilderAddressMainnet, "f");
|
|
606
|
+
__classPrivateFieldSet(this, _HyperLiquidProvider_onChaseOrderMaxDistanceReached, options.onChaseOrderMaxDistanceReached, "f");
|
|
455
607
|
__classPrivateFieldSet(this, _HyperLiquidProvider_priceDeviationLimit, options.priceDeviationLimit ??
|
|
456
608
|
HYPERLIQUID_CONFIG.OraclePriceDeviationLimit, "f");
|
|
457
609
|
const isTestnet = options.isTestnet ?? false;
|
|
@@ -501,6 +653,56 @@ export class HyperLiquidProvider {
|
|
|
501
653
|
isTestnet,
|
|
502
654
|
});
|
|
503
655
|
}
|
|
656
|
+
/**
|
|
657
|
+
* Return provider-owned strategy support for a routed market.
|
|
658
|
+
* HyperLiquid metadata has no per-market strategy flags. This provider
|
|
659
|
+
* advertises its implemented strategies uniformly after confirming the
|
|
660
|
+
* routed market exists.
|
|
661
|
+
*
|
|
662
|
+
* @param params - Required market route context.
|
|
663
|
+
* @returns Supported strategy order types.
|
|
664
|
+
*/
|
|
665
|
+
async getOrderCapabilities(params) {
|
|
666
|
+
if (!isValidCapabilitySymbol(params.symbol, { allowProviderRoute: true })) {
|
|
667
|
+
return {
|
|
668
|
+
status: 'unavailable',
|
|
669
|
+
providerId: this.protocolId,
|
|
670
|
+
reason: 'invalid_symbol',
|
|
671
|
+
};
|
|
672
|
+
}
|
|
673
|
+
const { dex } = parseAssetName(params.symbol);
|
|
674
|
+
if (__classPrivateFieldGet(this, _HyperLiquidProvider_isDisconnected, "f")) {
|
|
675
|
+
return {
|
|
676
|
+
status: 'unavailable',
|
|
677
|
+
providerId: this.protocolId,
|
|
678
|
+
reason: 'provider_unavailable',
|
|
679
|
+
};
|
|
680
|
+
}
|
|
681
|
+
const lifecycleGeneration = __classPrivateFieldGet(this, _HyperLiquidProvider_lifecycleGeneration, "f");
|
|
682
|
+
try {
|
|
683
|
+
const marketExists = (await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_getFreshOrderCapabilityMarkets).call(this, dex)).some((market) => market.name === params.symbol);
|
|
684
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_assertProviderLifecycleCurrent).call(this, lifecycleGeneration, 'Order capability read');
|
|
685
|
+
return marketExists
|
|
686
|
+
? HYPERLIQUID_ORDER_CAPABILITIES
|
|
687
|
+
: {
|
|
688
|
+
status: 'unavailable',
|
|
689
|
+
providerId: this.protocolId,
|
|
690
|
+
reason: 'market_not_found',
|
|
691
|
+
};
|
|
692
|
+
}
|
|
693
|
+
catch (error) {
|
|
694
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('HyperLiquid: Order capabilities unavailable', {
|
|
695
|
+
symbol: params.symbol,
|
|
696
|
+
error: ensureError(error, 'HyperLiquidProvider.getOrderCapabilities')
|
|
697
|
+
.message,
|
|
698
|
+
});
|
|
699
|
+
return {
|
|
700
|
+
status: 'unavailable',
|
|
701
|
+
providerId: this.protocolId,
|
|
702
|
+
reason: 'provider_unavailable',
|
|
703
|
+
};
|
|
704
|
+
}
|
|
705
|
+
}
|
|
504
706
|
/**
|
|
505
707
|
* Get fills using WebSocket cache first, falling back to REST API
|
|
506
708
|
* OPTIMIZATION: Uses cached fills when available (0 API weight), only calls REST on cache miss
|
|
@@ -759,7 +961,10 @@ export class HyperLiquidProvider {
|
|
|
759
961
|
// Ensure provider is ready for trading (includes signing operations).
|
|
760
962
|
// Kept after validation so invalid orders never trigger signature prompts
|
|
761
963
|
// (builder-fee approval, DEX abstraction enablement, etc.).
|
|
762
|
-
|
|
964
|
+
const { chargesMetamaskBuilderFee } = __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_resolveOrderFeePolicy).call(this, params);
|
|
965
|
+
const builderFeeSetupContext = await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_ensureReadyForTrading).call(this, {
|
|
966
|
+
requiresBuilderFee: chargesMetamaskBuilderFee,
|
|
967
|
+
});
|
|
763
968
|
// Debug: Log asset map state before order placement
|
|
764
969
|
const allMapKeys = Array.from(__classPrivateFieldGet(this, _HyperLiquidProvider_symbolToAssetId, "f").keys());
|
|
765
970
|
const hip3Keys = allMapKeys.filter((key) => key.includes(':'));
|
|
@@ -864,6 +1069,8 @@ export class HyperLiquidProvider {
|
|
|
864
1069
|
transferInfo,
|
|
865
1070
|
symbol: params.symbol,
|
|
866
1071
|
assetId,
|
|
1072
|
+
chargesMetamaskBuilderFee,
|
|
1073
|
+
builderFeeSetupContext,
|
|
867
1074
|
});
|
|
868
1075
|
}
|
|
869
1076
|
catch (error) {
|
|
@@ -930,6 +1137,54 @@ export class HyperLiquidProvider {
|
|
|
930
1137
|
});
|
|
931
1138
|
}
|
|
932
1139
|
}
|
|
1140
|
+
/**
|
|
1141
|
+
* Read native TWAP schedules and their slice fills from the venue.
|
|
1142
|
+
*
|
|
1143
|
+
* @returns Current and terminal TWAP lifecycle records, newest first.
|
|
1144
|
+
*/
|
|
1145
|
+
async getTwapOrders() {
|
|
1146
|
+
await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_ensureClientsInitialized).call(this);
|
|
1147
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_clientService, "f").ensureInitialized();
|
|
1148
|
+
const lifecycleGeneration = __classPrivateFieldGet(this, _HyperLiquidProvider_lifecycleGeneration, "f");
|
|
1149
|
+
const infoClient = __classPrivateFieldGet(this, _HyperLiquidProvider_clientService, "f").getInfoClient();
|
|
1150
|
+
const userAddress = await __classPrivateFieldGet(this, _HyperLiquidProvider_walletService, "f").getUserAddressWithDefault();
|
|
1151
|
+
const [history, sliceFills] = await Promise.all([
|
|
1152
|
+
infoClient.twapHistory({ user: userAddress }),
|
|
1153
|
+
infoClient.userTwapSliceFills({ user: userAddress }),
|
|
1154
|
+
]);
|
|
1155
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_assertProviderLifecycleCurrent).call(this, lifecycleGeneration, 'TWAP lifecycle read');
|
|
1156
|
+
const fillsByTwapId = new Map();
|
|
1157
|
+
for (const sliceFill of sliceFills) {
|
|
1158
|
+
const existing = fillsByTwapId.get(sliceFill.twapId) ?? [];
|
|
1159
|
+
existing.push(sliceFill);
|
|
1160
|
+
fillsByTwapId.set(sliceFill.twapId, existing);
|
|
1161
|
+
}
|
|
1162
|
+
const now = Date.now();
|
|
1163
|
+
const ordersById = new Map();
|
|
1164
|
+
for (const historyEntry of history) {
|
|
1165
|
+
const order = __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_adaptTwapOrder).call(this, {
|
|
1166
|
+
historyEntry,
|
|
1167
|
+
sliceFills: historyEntry.twapId === undefined
|
|
1168
|
+
? []
|
|
1169
|
+
: (fillsByTwapId.get(historyEntry.twapId) ?? []),
|
|
1170
|
+
now,
|
|
1171
|
+
});
|
|
1172
|
+
if (!order) {
|
|
1173
|
+
continue;
|
|
1174
|
+
}
|
|
1175
|
+
const existing = ordersById.get(order.orderId);
|
|
1176
|
+
if (!existing || order.lastUpdated >= existing.lastUpdated) {
|
|
1177
|
+
ordersById.set(order.orderId, order);
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
const orders = [...ordersById.values()].sort((left, right) => right.startedAt - left.startedAt);
|
|
1181
|
+
const network = __classPrivateFieldGet(this, _HyperLiquidProvider_clientService, "f").isTestnetMode() ? 'testnet' : 'mainnet';
|
|
1182
|
+
await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_rebalanceTerminalHip3Twaps).call(this, orders, {
|
|
1183
|
+
network,
|
|
1184
|
+
userAddress,
|
|
1185
|
+
});
|
|
1186
|
+
return orders;
|
|
1187
|
+
}
|
|
933
1188
|
/**
|
|
934
1189
|
* Return stable client-facing snapshots for every Chase session retained by
|
|
935
1190
|
* this provider instance. A read may refresh the current child order's
|
|
@@ -947,12 +1202,26 @@ export class HyperLiquidProvider {
|
|
|
947
1202
|
session.lastSnapshotSizeRefreshAt = now;
|
|
948
1203
|
const snapshotOrderId = session.orderId;
|
|
949
1204
|
try {
|
|
950
|
-
const liveRemainder = await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_readOrderRemainder).call(this, snapshotOrderId);
|
|
951
|
-
if (
|
|
952
|
-
session.orderId === snapshotOrderId &&
|
|
1205
|
+
const liveRemainder = await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_readOrderRemainder).call(this, snapshotOrderId, CHASE_ORDER_STATUS_RETRY_COUNT);
|
|
1206
|
+
if (session.orderId === snapshotOrderId &&
|
|
953
1207
|
session.pendingReplacement === null) {
|
|
954
|
-
|
|
955
|
-
|
|
1208
|
+
if (liveRemainder.terminalStatus === null) {
|
|
1209
|
+
if (liveRemainder.remainingSize !== null) {
|
|
1210
|
+
session.size = liveRemainder.remainingSize;
|
|
1211
|
+
snapshotRemainingSize = liveRemainder.remainingSize;
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
else {
|
|
1215
|
+
session.orderId = null;
|
|
1216
|
+
session.size = liveRemainder.remainingSize ?? '0';
|
|
1217
|
+
session.active = false;
|
|
1218
|
+
if (session.timer) {
|
|
1219
|
+
clearTimeout(session.timer);
|
|
1220
|
+
session.timer = null;
|
|
1221
|
+
}
|
|
1222
|
+
session.status = liveRemainder.terminalStatus;
|
|
1223
|
+
snapshotRemainingSize = session.size;
|
|
1224
|
+
}
|
|
956
1225
|
}
|
|
957
1226
|
}
|
|
958
1227
|
catch (error) {
|
|
@@ -963,6 +1232,12 @@ export class HyperLiquidProvider {
|
|
|
963
1232
|
});
|
|
964
1233
|
}
|
|
965
1234
|
}
|
|
1235
|
+
if (!session.active && session.hip3Transfer) {
|
|
1236
|
+
const rebalanced = await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_rebalanceChaseSession).call(this, session);
|
|
1237
|
+
if (rebalanced && session.removeAfterRebalance) {
|
|
1238
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_chaseSessions, "f").delete(handle);
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
966
1241
|
const arrival = Number.parseFloat(session.arrivalPrice);
|
|
967
1242
|
const resting = Number.parseFloat(session.restingPrice);
|
|
968
1243
|
const adverseDistance = session.isBuy
|
|
@@ -1000,9 +1275,6 @@ export class HyperLiquidProvider {
|
|
|
1000
1275
|
*/
|
|
1001
1276
|
async suspendChaseOrders() {
|
|
1002
1277
|
__classPrivateFieldSet(this, _HyperLiquidProvider_chasePlacementBlockers, __classPrivateFieldGet(this, _HyperLiquidProvider_chasePlacementBlockers, "f") + 1, "f");
|
|
1003
|
-
// Abort a placement that passed the blocker check but has not completed
|
|
1004
|
-
// its final generation check; the waiter below then drains its cleanup.
|
|
1005
|
-
__classPrivateFieldSet(this, _HyperLiquidProvider_chaseGeneration, __classPrivateFieldGet(this, _HyperLiquidProvider_chaseGeneration, "f") + 1, "f");
|
|
1006
1278
|
try {
|
|
1007
1279
|
await Promise.all([...__classPrivateFieldGet(this, _HyperLiquidProvider_chasePlacementWaiters, "f")]);
|
|
1008
1280
|
// Clear timers without deactivating sessions, then let any tick already
|
|
@@ -1185,10 +1457,10 @@ export class HyperLiquidProvider {
|
|
|
1185
1457
|
? params.newOrder.clientOrderId
|
|
1186
1458
|
: undefined,
|
|
1187
1459
|
};
|
|
1188
|
-
// Every refusal is behind us, so
|
|
1189
|
-
//
|
|
1190
|
-
//
|
|
1191
|
-
await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_ensureReadyForTrading).call(this);
|
|
1460
|
+
// Every refusal is behind us, so shared trading readiness can run now.
|
|
1461
|
+
// HyperLiquid's modify action has no builder field and must not request a
|
|
1462
|
+
// builder-fee approval.
|
|
1463
|
+
await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_ensureReadyForTrading).call(this, { requiresBuilderFee: false });
|
|
1192
1464
|
// Submit modification via SDK
|
|
1193
1465
|
const exchangeClient = __classPrivateFieldGet(this, _HyperLiquidProvider_clientService, "f").getExchangeClient();
|
|
1194
1466
|
const result = await exchangeClient.modify({
|
|
@@ -1253,24 +1525,25 @@ export class HyperLiquidProvider {
|
|
|
1253
1525
|
if (owningChase) {
|
|
1254
1526
|
__classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_stopChaseSession).call(this, owningChase[0]);
|
|
1255
1527
|
await __classPrivateFieldGet(this, _HyperLiquidProvider_chaseTickQueue, "f");
|
|
1256
|
-
|
|
1528
|
+
const result = await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_cancelChaseOrder).call(this, {
|
|
1257
1529
|
...params,
|
|
1258
1530
|
orderId: owningChase[0],
|
|
1259
1531
|
orderType: 'chase',
|
|
1260
1532
|
});
|
|
1533
|
+
return { ...result, orderId: params.orderId };
|
|
1261
1534
|
}
|
|
1262
1535
|
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('Canceling order:', params);
|
|
1263
1536
|
// Hydrate the asset map before coin validation so a cold start (e.g.
|
|
1264
1537
|
// service-worker restart with an empty prefetch map) can self-heal
|
|
1265
1538
|
// without signature prompts on invalid cancels. Trading setup (builder
|
|
1266
|
-
//
|
|
1267
|
-
// matching placeOrder / editOrder.
|
|
1539
|
+
// referral and unified-account setup runs only after validation passes,
|
|
1540
|
+
// matching placeOrder / editOrder. Cancel has no builder field.
|
|
1268
1541
|
await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_ensureReady).call(this);
|
|
1269
1542
|
const coinValidation = validateCoinExists(params.symbol, __classPrivateFieldGet(this, _HyperLiquidProvider_symbolToAssetId, "f"));
|
|
1270
1543
|
if (!coinValidation.isValid) {
|
|
1271
1544
|
throw new Error(coinValidation.error);
|
|
1272
1545
|
}
|
|
1273
|
-
await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_ensureReadyForTrading).call(this);
|
|
1546
|
+
await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_ensureReadyForTrading).call(this, { requiresBuilderFee: false });
|
|
1274
1547
|
const exchangeClient = __classPrivateFieldGet(this, _HyperLiquidProvider_clientService, "f").getExchangeClient();
|
|
1275
1548
|
const asset = await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_getAssetIdWithRepair).call(this, {
|
|
1276
1549
|
symbol: params.symbol,
|
|
@@ -1294,10 +1567,10 @@ export class HyperLiquidProvider {
|
|
|
1294
1567
|
// HyperLiquid usually rejects a cancel without throwing: the status entry
|
|
1295
1568
|
// carries the raw exchange string (e.g. "multi-sig required"). Map it the
|
|
1296
1569
|
// same way as a thrown rejection so callers get a standardized code
|
|
1297
|
-
// instead of a generic message.
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
'Order cancellation failed';
|
|
1570
|
+
// instead of a generic message.
|
|
1571
|
+
const rawError = isStatusObject(status) && typeof status.error === 'string'
|
|
1572
|
+
? status.error
|
|
1573
|
+
: 'Order cancellation failed';
|
|
1301
1574
|
return createErrorResult(__classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_mapError).call(this, new Error(rawError)), {
|
|
1302
1575
|
success: false,
|
|
1303
1576
|
orderId: params.orderId,
|
|
@@ -1309,7 +1582,10 @@ export class HyperLiquidProvider {
|
|
|
1309
1582
|
orderId: params.orderId,
|
|
1310
1583
|
coin: params.symbol,
|
|
1311
1584
|
}));
|
|
1312
|
-
return createErrorResult(mappedError, {
|
|
1585
|
+
return createErrorResult(mappedError, {
|
|
1586
|
+
success: false,
|
|
1587
|
+
orderId: params.orderId,
|
|
1588
|
+
});
|
|
1313
1589
|
}
|
|
1314
1590
|
}
|
|
1315
1591
|
/**
|
|
@@ -1320,87 +1596,126 @@ export class HyperLiquidProvider {
|
|
|
1320
1596
|
* @returns A promise that resolves to the result.
|
|
1321
1597
|
*/
|
|
1322
1598
|
async cancelOrders(params) {
|
|
1599
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('Batch canceling orders:', {
|
|
1600
|
+
count: params.length,
|
|
1601
|
+
});
|
|
1602
|
+
if (params.length === 0) {
|
|
1603
|
+
return {
|
|
1604
|
+
success: false,
|
|
1605
|
+
successCount: 0,
|
|
1606
|
+
failureCount: 0,
|
|
1607
|
+
results: [],
|
|
1608
|
+
};
|
|
1609
|
+
}
|
|
1610
|
+
const results = params.map((order) => ({
|
|
1611
|
+
orderId: order.orderId,
|
|
1612
|
+
symbol: order.symbol,
|
|
1613
|
+
success: false,
|
|
1614
|
+
error: PERPS_ERROR_CODES.BATCH_CANCEL_FAILED,
|
|
1615
|
+
}));
|
|
1616
|
+
// Resolve Chase ownership before the first await. A queued reprice may
|
|
1617
|
+
// replace the child ID, but the stable session handle still reaches it.
|
|
1618
|
+
const chaseSessions = [...__classPrivateFieldGet(this, _HyperLiquidProvider_chaseSessions, "f").entries()];
|
|
1619
|
+
const chaseRoutes = params.flatMap((order, index) => {
|
|
1620
|
+
const owner = chaseSessions.find(([, session]) => session.orderId === order.orderId ||
|
|
1621
|
+
session.replacingOrderId === order.orderId);
|
|
1622
|
+
return owner ? [{ handle: owner[0], index, order }] : [];
|
|
1623
|
+
});
|
|
1624
|
+
const chaseIndexes = new Set(chaseRoutes.map(({ index }) => index));
|
|
1323
1625
|
try {
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
});
|
|
1327
|
-
if (params.length === 0) {
|
|
1328
|
-
return {
|
|
1329
|
-
success: false,
|
|
1330
|
-
successCount: 0,
|
|
1331
|
-
failureCount: 0,
|
|
1332
|
-
results: [],
|
|
1333
|
-
};
|
|
1626
|
+
for (const handle of new Set(chaseRoutes.map((route) => route.handle))) {
|
|
1627
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_stopChaseSession).call(this, handle);
|
|
1334
1628
|
}
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1629
|
+
if (chaseRoutes.length > 0) {
|
|
1630
|
+
await __classPrivateFieldGet(this, _HyperLiquidProvider_chaseTickQueue, "f");
|
|
1631
|
+
}
|
|
1632
|
+
for (const { handle, index, order } of chaseRoutes) {
|
|
1633
|
+
const result = await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_cancelStrategyOrder).call(this, {
|
|
1634
|
+
orderId: handle,
|
|
1341
1635
|
symbol: order.symbol,
|
|
1342
|
-
|
|
1636
|
+
orderType: 'chase',
|
|
1343
1637
|
});
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1638
|
+
results[index] = {
|
|
1639
|
+
orderId: order.orderId,
|
|
1640
|
+
symbol: order.symbol,
|
|
1641
|
+
success: result.success,
|
|
1642
|
+
...(result.error === undefined ? {} : { error: result.error }),
|
|
1347
1643
|
};
|
|
1348
|
-
}
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
failureCount,
|
|
1361
|
-
results: statuses.map((status, index) => {
|
|
1362
|
-
// Map each per-status rejection the same way cancelOrder does, so a
|
|
1363
|
-
// batch cancel reports standardized codes rather than raw exchange
|
|
1364
|
-
// strings for the rejections this provider recognizes.
|
|
1365
|
-
const statusError = status?.error;
|
|
1644
|
+
}
|
|
1645
|
+
const ordinaryOrders = params.flatMap((order, index) => chaseIndexes.has(index) ? [] : [{ index, order }]);
|
|
1646
|
+
if (ordinaryOrders.length > 0) {
|
|
1647
|
+
// Cancellation carries no builder context, so it must not prompt for a
|
|
1648
|
+
// fee approval that the action cannot use.
|
|
1649
|
+
await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_ensureReadyForTrading).call(this, { requiresBuilderFee: false });
|
|
1650
|
+
const exchangeClient = __classPrivateFieldGet(this, _HyperLiquidProvider_clientService, "f").getExchangeClient();
|
|
1651
|
+
const cancelRequests = await Promise.all(ordinaryOrders.map(async ({ order }) => {
|
|
1652
|
+
const asset = await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_getAssetIdWithRepair).call(this, {
|
|
1653
|
+
symbol: order.symbol,
|
|
1654
|
+
dexName: parseAssetName(order.symbol).dex,
|
|
1655
|
+
});
|
|
1366
1656
|
return {
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
success: status === 'success',
|
|
1370
|
-
error: statusError
|
|
1371
|
-
? __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_mapError).call(this, new Error(statusError)).message
|
|
1372
|
-
: undefined,
|
|
1657
|
+
a: asset,
|
|
1658
|
+
o: parseInt(order.orderId, 10),
|
|
1373
1659
|
};
|
|
1374
|
-
})
|
|
1375
|
-
|
|
1660
|
+
}));
|
|
1661
|
+
const result = await exchangeClient.cancel({
|
|
1662
|
+
cancels: cancelRequests,
|
|
1663
|
+
});
|
|
1664
|
+
const statuses = result.response?.data?.statuses ?? [];
|
|
1665
|
+
if (result.status === 'ok' &&
|
|
1666
|
+
statuses.length === ordinaryOrders.length) {
|
|
1667
|
+
ordinaryOrders.forEach(({ index, order }, statusIndex) => {
|
|
1668
|
+
const status = statuses[statusIndex];
|
|
1669
|
+
const success = status === 'success';
|
|
1670
|
+
const statusError = isStatusObject(status) && typeof status.error === 'string'
|
|
1671
|
+
? status.error
|
|
1672
|
+
: undefined;
|
|
1673
|
+
results[index] = {
|
|
1674
|
+
orderId: order.orderId,
|
|
1675
|
+
symbol: order.symbol,
|
|
1676
|
+
success,
|
|
1677
|
+
...(success
|
|
1678
|
+
? {}
|
|
1679
|
+
: {
|
|
1680
|
+
error: statusError === undefined
|
|
1681
|
+
? PERPS_ERROR_CODES.BATCH_CANCEL_FAILED
|
|
1682
|
+
: __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_mapError).call(this, new Error(statusError)).message,
|
|
1683
|
+
}),
|
|
1684
|
+
};
|
|
1685
|
+
});
|
|
1686
|
+
}
|
|
1687
|
+
}
|
|
1376
1688
|
}
|
|
1377
1689
|
catch (error) {
|
|
1378
1690
|
const mappedError = __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_mapError).call(this, error);
|
|
1379
1691
|
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").logger.error(mappedError, await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_getTradingErrorContext).call(this, 'cancelOrders', mappedError, {
|
|
1380
1692
|
orderCount: params.length,
|
|
1381
1693
|
}));
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
error: error instanceof Error
|
|
1392
|
-
? mappedError.message
|
|
1393
|
-
: PERPS_ERROR_CODES.BATCH_CANCEL_FAILED,
|
|
1394
|
-
})),
|
|
1395
|
-
};
|
|
1694
|
+
for (const result of results) {
|
|
1695
|
+
if (!result.success &&
|
|
1696
|
+
result.error === PERPS_ERROR_CODES.BATCH_CANCEL_FAILED) {
|
|
1697
|
+
result.error =
|
|
1698
|
+
error instanceof Error
|
|
1699
|
+
? mappedError.message
|
|
1700
|
+
: PERPS_ERROR_CODES.BATCH_CANCEL_FAILED;
|
|
1701
|
+
}
|
|
1702
|
+
}
|
|
1396
1703
|
}
|
|
1704
|
+
const successCount = results.filter(({ success }) => success).length;
|
|
1705
|
+
return {
|
|
1706
|
+
success: successCount > 0,
|
|
1707
|
+
successCount,
|
|
1708
|
+
failureCount: results.length - successCount,
|
|
1709
|
+
results,
|
|
1710
|
+
};
|
|
1397
1711
|
}
|
|
1398
1712
|
async closePositions(params) {
|
|
1399
1713
|
// Declare outside try block so it's accessible in catch block
|
|
1400
1714
|
let positionsToClose = [];
|
|
1401
1715
|
try {
|
|
1402
|
-
//
|
|
1403
|
-
|
|
1716
|
+
// Batch preparation needs trading readiness but not builder approval yet.
|
|
1717
|
+
// The provider-owned market policy is resolved from the positions below.
|
|
1718
|
+
await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_ensureReadyForTrading).call(this, { requiresBuilderFee: false });
|
|
1404
1719
|
// Get all current positions from cache (avoids 429 rate limiting)
|
|
1405
1720
|
const positions = await this.getPositions();
|
|
1406
1721
|
// Filter positions based on params
|
|
@@ -1438,6 +1753,7 @@ export class HyperLiquidProvider {
|
|
|
1438
1753
|
// Build orders array, plus the positions each order closes so response
|
|
1439
1754
|
// statuses stay index-aligned when a position is skipped below
|
|
1440
1755
|
const orders = [];
|
|
1756
|
+
const feePolicyContexts = [];
|
|
1441
1757
|
const orderedPositions = [];
|
|
1442
1758
|
// Positions no order could be built for. Reported as failures so a caller
|
|
1443
1759
|
// cannot read "closed everything" from a result that left one open.
|
|
@@ -1512,6 +1828,13 @@ export class HyperLiquidProvider {
|
|
|
1512
1828
|
r: true, // reduceOnly
|
|
1513
1829
|
t: { limit: { tif: 'Ioc' } }, // Immediate or cancel for market-like execution
|
|
1514
1830
|
});
|
|
1831
|
+
feePolicyContexts.push({
|
|
1832
|
+
symbol: position.symbol,
|
|
1833
|
+
isBuy,
|
|
1834
|
+
size: formattedSize,
|
|
1835
|
+
orderType: 'market',
|
|
1836
|
+
reduceOnly: true,
|
|
1837
|
+
});
|
|
1515
1838
|
orderedPositions.push(position);
|
|
1516
1839
|
orderedHip3Transfers.push(hip3Transfer);
|
|
1517
1840
|
}
|
|
@@ -1526,11 +1849,19 @@ export class HyperLiquidProvider {
|
|
|
1526
1849
|
results: skippedResults,
|
|
1527
1850
|
};
|
|
1528
1851
|
}
|
|
1852
|
+
// HyperLiquid accepts one builder context for the whole close batch.
|
|
1853
|
+
// Resolve every position context and charge the batch when any included
|
|
1854
|
+
// policy charges, preserving deterministic behavior for future policies
|
|
1855
|
+
// that vary by market or route.
|
|
1856
|
+
const chargesMetamaskBuilderFee = feePolicyContexts.some((context) => __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_resolveOrderFeePolicy).call(this, context).chargesMetamaskBuilderFee);
|
|
1857
|
+
const builder = chargesMetamaskBuilderFee
|
|
1858
|
+
? await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_getBuilderOrderContext).call(this, await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_ensureReadyForTrading).call(this, { requiresBuilderFee: true }))
|
|
1859
|
+
: undefined;
|
|
1529
1860
|
// Single batch API call
|
|
1530
1861
|
const result = await exchangeClient.order({
|
|
1531
1862
|
orders,
|
|
1532
1863
|
grouping: 'na',
|
|
1533
|
-
builder
|
|
1864
|
+
...(builder && { builder }),
|
|
1534
1865
|
});
|
|
1535
1866
|
// Parse response statuses (one per order)
|
|
1536
1867
|
const { statuses } = result.response.data;
|
|
@@ -1644,11 +1975,11 @@ export class HyperLiquidProvider {
|
|
|
1644
1975
|
await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_ensureReady).call(this);
|
|
1645
1976
|
// Use live position (from WebSocket) if available, otherwise fetch via REST
|
|
1646
1977
|
// Preferring WebSocket data avoids rate limiting issues with the REST API
|
|
1647
|
-
let
|
|
1648
|
-
if (
|
|
1978
|
+
let fetchedPosition;
|
|
1979
|
+
if (livePosition) {
|
|
1649
1980
|
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('Using live position from WebSocket', {
|
|
1650
|
-
symbol:
|
|
1651
|
-
size:
|
|
1981
|
+
symbol: livePosition.symbol,
|
|
1982
|
+
size: livePosition.size,
|
|
1652
1983
|
});
|
|
1653
1984
|
}
|
|
1654
1985
|
else {
|
|
@@ -1664,8 +1995,9 @@ export class HyperLiquidProvider {
|
|
|
1664
1995
|
}));
|
|
1665
1996
|
throw error;
|
|
1666
1997
|
}
|
|
1667
|
-
|
|
1998
|
+
fetchedPosition = positions.find((pos) => pos.symbol === symbol);
|
|
1668
1999
|
}
|
|
2000
|
+
const position = livePosition ?? fetchedPosition;
|
|
1669
2001
|
if (!position) {
|
|
1670
2002
|
throw new Error(`No position found for ${symbol}`);
|
|
1671
2003
|
}
|
|
@@ -1729,17 +2061,117 @@ export class HyperLiquidProvider {
|
|
|
1729
2061
|
error: precision.error,
|
|
1730
2062
|
};
|
|
1731
2063
|
}
|
|
1732
|
-
// Everything is validated: only now run the trading setup that can prompt
|
|
1733
|
-
// for signatures and write the referral / builder-fee approvals.
|
|
1734
|
-
await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_ensureReadyForTrading).call(this);
|
|
1735
|
-
// Cancel existing TP/SL orders for this position
|
|
1736
|
-
// OPTIMIZATION: Use WebSocket cache first (0 weight), fall back to single-DEX REST (20 weight)
|
|
1737
|
-
// Previously: queryUserDataAcrossDexs queried ALL DEXs (20 weight × N DEXs = 40+ weight)
|
|
1738
2064
|
const assetId = await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_getAssetIdWithRepair).call(this, {
|
|
1739
2065
|
symbol,
|
|
1740
2066
|
dexName,
|
|
1741
2067
|
});
|
|
2068
|
+
const fullSize = TP_SL_CONFIG.UsePositionBoundTpsl && !isPartialTpsl
|
|
2069
|
+
? '0'
|
|
2070
|
+
: formatHyperLiquidSize({
|
|
2071
|
+
size: positionSize,
|
|
2072
|
+
szDecimals: assetInfo.szDecimals,
|
|
2073
|
+
});
|
|
2074
|
+
// Partial TP/SL orders carry their own size; the rest cover the position.
|
|
2075
|
+
// A partial size that rounds away at the asset precision is rejected
|
|
2076
|
+
// rather than sent as '0', which the exchange reads as whole-position.
|
|
2077
|
+
const resolveTpslSize = (tpslSize) => tpslSize === undefined
|
|
2078
|
+
? fullSize
|
|
2079
|
+
: formatPartialTpslSize({
|
|
2080
|
+
size: parseFloat(tpslSize),
|
|
2081
|
+
szDecimals: assetInfo.szDecimals,
|
|
2082
|
+
});
|
|
2083
|
+
// HyperLiquid accepts one builder context for the whole TP/SL action.
|
|
2084
|
+
// Position TP/SL has no parent policy to inherit, so resolve every
|
|
2085
|
+
// included trigger and charge the batch when any child policy charges.
|
|
2086
|
+
const feePolicyContexts = [
|
|
2087
|
+
...(takeProfitPrice
|
|
2088
|
+
? [
|
|
2089
|
+
{
|
|
2090
|
+
symbol,
|
|
2091
|
+
isBuy: !isLong,
|
|
2092
|
+
size: resolveTpslSize(takeProfitSize),
|
|
2093
|
+
orderType: 'take_profit_limit',
|
|
2094
|
+
price: takeProfitPrice,
|
|
2095
|
+
triggerPrice: takeProfitPrice,
|
|
2096
|
+
reduceOnly: true,
|
|
2097
|
+
},
|
|
2098
|
+
]
|
|
2099
|
+
: []),
|
|
2100
|
+
...(stopLossPrice
|
|
2101
|
+
? [
|
|
2102
|
+
{
|
|
2103
|
+
symbol,
|
|
2104
|
+
isBuy: !isLong,
|
|
2105
|
+
size: resolveTpslSize(stopLossSize),
|
|
2106
|
+
orderType: 'stop_market',
|
|
2107
|
+
triggerPrice: stopLossPrice,
|
|
2108
|
+
reduceOnly: true,
|
|
2109
|
+
},
|
|
2110
|
+
]
|
|
2111
|
+
: []),
|
|
2112
|
+
];
|
|
2113
|
+
const replacementChargesMetamaskBuilderFee = feePolicyContexts.some((feePolicyContext) => __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_resolveOrderFeePolicy).call(this, feePolicyContext)
|
|
2114
|
+
.chargesMetamaskBuilderFee);
|
|
2115
|
+
// Cancel existing TP/SL orders for this position
|
|
2116
|
+
// OPTIMIZATION: Use WebSocket cache first (0 weight), fall back to single-DEX REST (20 weight)
|
|
2117
|
+
// Previously: queryUserDataAcrossDexs queried ALL DEXs (20 weight × N DEXs = 40+ weight)
|
|
1742
2118
|
let cancelRequests = [];
|
|
2119
|
+
const restorablePositionTpslOrders = [];
|
|
2120
|
+
const restorableStandaloneTpslOrders = [];
|
|
2121
|
+
// Every replacement must cancel first so old and new reduce-only triggers
|
|
2122
|
+
// cannot execute together. Retain the exact trigger definitions so a
|
|
2123
|
+
// definitively failed replacement can restore confirmed cancellations.
|
|
2124
|
+
const captureRestorableTpslOrders = (tpslOrders) => {
|
|
2125
|
+
if (!takeProfitPrice && !stopLossPrice) {
|
|
2126
|
+
return;
|
|
2127
|
+
}
|
|
2128
|
+
tpslOrders.forEach((order) => {
|
|
2129
|
+
const orderId = Number(order.orderId);
|
|
2130
|
+
const orderType = order.triggerOrderType;
|
|
2131
|
+
const triggerPrice = order.triggerPrice ?? order.price;
|
|
2132
|
+
if (!Number.isSafeInteger(orderId) || !orderType || !triggerPrice) {
|
|
2133
|
+
throw new Error(PERPS_ERROR_CODES.TPSL_UPDATE_FAILED);
|
|
2134
|
+
}
|
|
2135
|
+
const size = order.isPositionTpsl ? '0' : order.remainingSize;
|
|
2136
|
+
const isBuy = order.side === 'buy';
|
|
2137
|
+
const feePolicyContext = {
|
|
2138
|
+
symbol,
|
|
2139
|
+
isBuy,
|
|
2140
|
+
size,
|
|
2141
|
+
orderType,
|
|
2142
|
+
triggerPrice,
|
|
2143
|
+
reduceOnly: true,
|
|
2144
|
+
...(isLimitExecutionOrderType(orderType) && {
|
|
2145
|
+
price: order.price,
|
|
2146
|
+
}),
|
|
2147
|
+
};
|
|
2148
|
+
const restorableOrder = {
|
|
2149
|
+
orderId,
|
|
2150
|
+
order: {
|
|
2151
|
+
a: assetId,
|
|
2152
|
+
b: isBuy,
|
|
2153
|
+
p: order.price,
|
|
2154
|
+
s: size,
|
|
2155
|
+
r: true,
|
|
2156
|
+
t: {
|
|
2157
|
+
trigger: {
|
|
2158
|
+
isMarket: !isLimitExecutionOrderType(orderType),
|
|
2159
|
+
triggerPx: triggerPrice,
|
|
2160
|
+
tpsl: getTriggerDirection(orderType) === 'stop' ? 'sl' : 'tp',
|
|
2161
|
+
},
|
|
2162
|
+
},
|
|
2163
|
+
},
|
|
2164
|
+
chargesMetamaskBuilderFee: __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_resolveOrderFeePolicy).call(this, feePolicyContext)
|
|
2165
|
+
.chargesMetamaskBuilderFee,
|
|
2166
|
+
};
|
|
2167
|
+
if (order.isPositionTpsl) {
|
|
2168
|
+
restorablePositionTpslOrders.push(restorableOrder);
|
|
2169
|
+
}
|
|
2170
|
+
else {
|
|
2171
|
+
restorableStandaloneTpslOrders.push(restorableOrder);
|
|
2172
|
+
}
|
|
2173
|
+
});
|
|
2174
|
+
};
|
|
1743
2175
|
// Use atomic getter to prevent race condition between check and get
|
|
1744
2176
|
const cachedOrders = __classPrivateFieldGet(this, _HyperLiquidProvider_subscriptionService, "f").getOrdersCacheIfInitialized();
|
|
1745
2177
|
// Replacing TP/SL has to consider standalone ('na' grouping) triggers —
|
|
@@ -1766,16 +2198,16 @@ export class HyperLiquidProvider {
|
|
|
1766
2198
|
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log(cachedOrders === null
|
|
1767
2199
|
? 'WebSocket cache not initialized, falling back to single-DEX REST query'
|
|
1768
2200
|
: 'TP/SL update needs parent/child order context: using single-DEX REST query', { dex: dexName ?? 'main', isPartialTpsl });
|
|
1769
|
-
const
|
|
2201
|
+
const openOrders = await infoClient.frontendOpenOrders({
|
|
1770
2202
|
user: userAddress,
|
|
1771
2203
|
dex: dexName ?? undefined,
|
|
1772
2204
|
});
|
|
1773
2205
|
// Orders that belong to a pending parent order (normalTpsl children) are
|
|
1774
2206
|
// also listed at the top level, so collect their IDs to exclude them:
|
|
1775
2207
|
// they protect that pending order, not this position.
|
|
1776
|
-
const childOrderIds = collectChildOrderIds(
|
|
2208
|
+
const childOrderIds = collectChildOrderIds(openOrders);
|
|
1777
2209
|
// Filter using raw SDK response properties
|
|
1778
|
-
const tpslOrders =
|
|
2210
|
+
const tpslOrders = openOrders.filter((order) => order.coin === symbol &&
|
|
1779
2211
|
order.reduceOnly &&
|
|
1780
2212
|
// Position-bound TP/SL always qualifies, and so do standalone
|
|
1781
2213
|
// triggers on this market (they belong to the position too, whether
|
|
@@ -1787,6 +2219,7 @@ export class HyperLiquidProvider {
|
|
|
1787
2219
|
order.isTrigger &&
|
|
1788
2220
|
(order.orderType.includes('Take Profit') ||
|
|
1789
2221
|
order.orderType.includes('Stop')));
|
|
2222
|
+
captureRestorableTpslOrders(tpslOrders.map((order) => adaptOrderFromSDK(order, position)));
|
|
1790
2223
|
cancelRequests = tpslOrders.map((order) => ({
|
|
1791
2224
|
a: assetId,
|
|
1792
2225
|
o: order.oid,
|
|
@@ -1810,36 +2243,14 @@ export class HyperLiquidProvider {
|
|
|
1810
2243
|
order.detailedOrderType &&
|
|
1811
2244
|
(order.detailedOrderType.includes('Take Profit') ||
|
|
1812
2245
|
order.detailedOrderType.includes('Stop')));
|
|
2246
|
+
captureRestorableTpslOrders(tpslOrders);
|
|
1813
2247
|
cancelRequests = tpslOrders.map((order) => ({
|
|
1814
2248
|
a: assetId,
|
|
1815
2249
|
o: parseInt(order.orderId, 10),
|
|
1816
2250
|
}));
|
|
1817
2251
|
}
|
|
1818
|
-
if (cancelRequests.length > 0) {
|
|
1819
|
-
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log(`Canceling ${cancelRequests.length} existing TP/SL orders for ${symbol}`);
|
|
1820
|
-
const cancelResult = await exchangeClient.cancel({
|
|
1821
|
-
cancels: cancelRequests,
|
|
1822
|
-
});
|
|
1823
|
-
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('Cancel result:', cancelResult);
|
|
1824
|
-
}
|
|
1825
|
-
// assetId already validated above when building cancelRequests
|
|
1826
2252
|
// Build orders array for TP/SL
|
|
1827
2253
|
const orders = [];
|
|
1828
|
-
const fullSize = TP_SL_CONFIG.UsePositionBoundTpsl && !isPartialTpsl
|
|
1829
|
-
? '0'
|
|
1830
|
-
: formatHyperLiquidSize({
|
|
1831
|
-
size: positionSize,
|
|
1832
|
-
szDecimals: assetInfo.szDecimals,
|
|
1833
|
-
});
|
|
1834
|
-
// Partial TP/SL orders carry their own size; the rest cover the position.
|
|
1835
|
-
// A partial size that rounds away at the asset precision is rejected
|
|
1836
|
-
// rather than sent as '0', which the exchange reads as whole-position.
|
|
1837
|
-
const resolveTpslSize = (tpslSize) => tpslSize === undefined
|
|
1838
|
-
? fullSize
|
|
1839
|
-
: formatPartialTpslSize({
|
|
1840
|
-
size: parseFloat(tpslSize),
|
|
1841
|
-
szDecimals: assetInfo.szDecimals,
|
|
1842
|
-
});
|
|
1843
2254
|
// Take Profit order
|
|
1844
2255
|
if (takeProfitPrice) {
|
|
1845
2256
|
const tpOrder = {
|
|
@@ -1888,24 +2299,184 @@ export class HyperLiquidProvider {
|
|
|
1888
2299
|
};
|
|
1889
2300
|
orders.push(slOrder);
|
|
1890
2301
|
}
|
|
1891
|
-
|
|
2302
|
+
const rollbackRequiresBuilderFee = [
|
|
2303
|
+
...restorablePositionTpslOrders,
|
|
2304
|
+
...restorableStandaloneTpslOrders,
|
|
2305
|
+
].some((order) => order.chargesMetamaskBuilderFee);
|
|
2306
|
+
const requiresBuilderFee = replacementChargesMetamaskBuilderFee || rollbackRequiresBuilderFee;
|
|
2307
|
+
// Approval and builder-context resolution both finish before the
|
|
2308
|
+
// pre-cancel. A failure here therefore leaves the old protection intact.
|
|
2309
|
+
const builderFeeSetupContext = requiresBuilderFee
|
|
2310
|
+
? await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_ensureReadyForTrading).call(this, {
|
|
2311
|
+
requiresBuilderFee: true,
|
|
2312
|
+
builderFeeApprovalFailureCode: PERPS_ERROR_CODES.TPSL_UPDATE_FAILED,
|
|
2313
|
+
})
|
|
2314
|
+
: await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_ensureReadyForTrading).call(this, { requiresBuilderFee: false });
|
|
2315
|
+
const builderOrderContext = builderFeeSetupContext
|
|
2316
|
+
? await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_getBuilderOrderContext).call(this, builderFeeSetupContext)
|
|
2317
|
+
: undefined;
|
|
2318
|
+
const restoreCancelledProtection = async (cancelledOrderIds) => {
|
|
2319
|
+
const restoredOrderIds = [];
|
|
2320
|
+
let success = true;
|
|
2321
|
+
const protectionGroups = [
|
|
2322
|
+
{
|
|
2323
|
+
grouping: 'positionTpsl',
|
|
2324
|
+
entries: restorablePositionTpslOrders,
|
|
2325
|
+
},
|
|
2326
|
+
{
|
|
2327
|
+
grouping: 'na',
|
|
2328
|
+
entries: restorableStandaloneTpslOrders,
|
|
2329
|
+
},
|
|
2330
|
+
];
|
|
2331
|
+
for (const protection of protectionGroups) {
|
|
2332
|
+
const entries = protection.entries.filter((entry) => cancelledOrderIds.has(entry.orderId));
|
|
2333
|
+
if (entries.length === 0) {
|
|
2334
|
+
continue;
|
|
2335
|
+
}
|
|
2336
|
+
try {
|
|
2337
|
+
const result = await exchangeClient.order({
|
|
2338
|
+
orders: entries.map((entry) => entry.order),
|
|
2339
|
+
grouping: protection.grouping,
|
|
2340
|
+
...(entries.some((entry) => entry.chargesMetamaskBuilderFee) &&
|
|
2341
|
+
builderOrderContext && { builder: builderOrderContext }),
|
|
2342
|
+
});
|
|
2343
|
+
const statuses = result.response?.data?.statuses ?? [];
|
|
2344
|
+
const outcomes = statuses
|
|
2345
|
+
.slice(0, entries.length)
|
|
2346
|
+
.map((status) => __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_readOrderPlacementOutcome).call(this, status));
|
|
2347
|
+
restoredOrderIds.push(...outcomes.flatMap((outcome) => outcome ? [outcome.orderId] : []));
|
|
2348
|
+
if (result.status !== 'ok' ||
|
|
2349
|
+
statuses.length !== entries.length ||
|
|
2350
|
+
outcomes.some((outcome) => outcome === undefined)) {
|
|
2351
|
+
success = false;
|
|
2352
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").logger.error(new Error(PERPS_ERROR_CODES.TPSL_UPDATE_FAILED), __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_getErrorContext).call(this, 'updatePositionTPSL > restoreCancelledProtection', { symbol, grouping: protection.grouping, statuses }));
|
|
2353
|
+
}
|
|
2354
|
+
}
|
|
2355
|
+
catch (error) {
|
|
2356
|
+
success = false;
|
|
2357
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").logger.error(ensureError(error, 'HyperLiquidProvider.updatePositionTPSL.restoreCancelledProtection'), __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_getErrorContext).call(this, 'updatePositionTPSL > restoreCancelledProtection', { symbol, grouping: protection.grouping }));
|
|
2358
|
+
}
|
|
2359
|
+
}
|
|
2360
|
+
return { restoredOrderIds, success };
|
|
2361
|
+
};
|
|
2362
|
+
const createProtectionLostResult = (survivingOrderIds) => createErrorResult(new Error(PERPS_ERROR_CODES.TPSL_PROTECTION_LOST), {
|
|
2363
|
+
success: false,
|
|
2364
|
+
childOrderIds: [...new Set(survivingOrderIds)],
|
|
2365
|
+
});
|
|
2366
|
+
// Clearing has no replacement batch to preserve. A partial cancellation
|
|
2367
|
+
// is reported so the caller can retry the same clear operation.
|
|
1892
2368
|
if (orders.length === 0) {
|
|
2369
|
+
const remainingOrderIds = await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_cancelOrderRequests).call(this, exchangeClient, cancelRequests);
|
|
2370
|
+
if (remainingOrderIds.length > 0) {
|
|
2371
|
+
throw new Error(PERPS_ERROR_CODES.TPSL_UPDATE_FAILED);
|
|
2372
|
+
}
|
|
1893
2373
|
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('No new TP/SL orders to place - existing ones cancelled');
|
|
1894
2374
|
return {
|
|
1895
2375
|
success: true,
|
|
1896
2376
|
// No orderId since we only cancelled orders, didn't place new ones
|
|
1897
2377
|
};
|
|
1898
2378
|
}
|
|
1899
|
-
//
|
|
1900
|
-
//
|
|
1901
|
-
//
|
|
1902
|
-
const
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
2379
|
+
// Cancel before placing for both position-bound and standalone partial
|
|
2380
|
+
// triggers. A place-first partial update leaves both trigger sets live
|
|
2381
|
+
// during the cancellation round trip and can reduce more than requested.
|
|
2382
|
+
const confirmedCancelledOldOrderIds = new Set();
|
|
2383
|
+
const oldCancellation = await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_cancelOrderRequestBatch).call(this, exchangeClient, cancelRequests);
|
|
2384
|
+
if (!oldCancellation.responseComplete) {
|
|
2385
|
+
const requestedOrderIds = new Set(cancelRequests.map((request) => request.o));
|
|
2386
|
+
let possiblyLiveOrderIds = Array.from(requestedOrderIds, String);
|
|
2387
|
+
try {
|
|
2388
|
+
const liveOrders = await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_fetchOpenOrders).call(this, { dexName });
|
|
2389
|
+
possiblyLiveOrderIds = liveOrders
|
|
2390
|
+
.filter((order) => requestedOrderIds.has(order.oid))
|
|
2391
|
+
.map((order) => String(order.oid));
|
|
2392
|
+
}
|
|
2393
|
+
catch (error) {
|
|
2394
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('Could not reconcile TP/SL protection after an incomplete cancel', {
|
|
2395
|
+
symbol,
|
|
2396
|
+
error: ensureError(error, 'HyperLiquidProvider.updatePositionTPSL.reconcileProtection').message,
|
|
2397
|
+
});
|
|
2398
|
+
}
|
|
2399
|
+
return createProtectionLostResult(possiblyLiveOrderIds);
|
|
2400
|
+
}
|
|
2401
|
+
oldCancellation.cancelledOrderIds.forEach((orderId) => confirmedCancelledOldOrderIds.add(orderId));
|
|
2402
|
+
if (oldCancellation.remainingOrderIds.length > 0) {
|
|
2403
|
+
const restoration = await restoreCancelledProtection(confirmedCancelledOldOrderIds);
|
|
2404
|
+
if (!restoration.success) {
|
|
2405
|
+
return createProtectionLostResult([
|
|
2406
|
+
...oldCancellation.remainingOrderIds.map(String),
|
|
2407
|
+
...restoration.restoredOrderIds,
|
|
2408
|
+
]);
|
|
2409
|
+
}
|
|
2410
|
+
const updateError = new Error(PERPS_ERROR_CODES.TPSL_UPDATE_FAILED);
|
|
2411
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").logger.error(updateError, __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_getErrorContext).call(this, 'updatePositionTPSL', {
|
|
2412
|
+
symbol: params.symbol,
|
|
2413
|
+
hasTakeProfit: params.takeProfitPrice !== undefined,
|
|
2414
|
+
hasStopLoss: params.stopLossPrice !== undefined,
|
|
2415
|
+
}));
|
|
2416
|
+
return createErrorResult(updateError, {
|
|
2417
|
+
success: false,
|
|
2418
|
+
childOrderIds: [
|
|
2419
|
+
...new Set([
|
|
2420
|
+
...oldCancellation.remainingOrderIds.map(String),
|
|
2421
|
+
...restoration.restoredOrderIds,
|
|
2422
|
+
]),
|
|
2423
|
+
],
|
|
2424
|
+
});
|
|
2425
|
+
}
|
|
2426
|
+
let result;
|
|
2427
|
+
try {
|
|
2428
|
+
result = await exchangeClient.order({
|
|
2429
|
+
orders,
|
|
2430
|
+
grouping: isPartialTpsl ? 'na' : 'positionTpsl',
|
|
2431
|
+
...(replacementChargesMetamaskBuilderFee &&
|
|
2432
|
+
builderOrderContext && { builder: builderOrderContext }),
|
|
2433
|
+
});
|
|
2434
|
+
}
|
|
2435
|
+
catch (error) {
|
|
2436
|
+
const restoration = await restoreCancelledProtection(confirmedCancelledOldOrderIds);
|
|
2437
|
+
if (!restoration.success) {
|
|
2438
|
+
return createProtectionLostResult(restoration.restoredOrderIds);
|
|
2439
|
+
}
|
|
2440
|
+
throw error;
|
|
2441
|
+
}
|
|
2442
|
+
const placementStatuses = result.response?.data?.statuses ?? [];
|
|
2443
|
+
const placementOutcomes = placementStatuses
|
|
2444
|
+
.slice(0, orders.length)
|
|
2445
|
+
.map((status) => __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_readOrderPlacementOutcome).call(this, status));
|
|
2446
|
+
const replacementOrderIds = placementOutcomes.flatMap((outcome) => outcome ? [outcome.orderId] : []);
|
|
2447
|
+
const restingReplacementOrderIds = placementOutcomes.flatMap((outcome) => outcome?.state === 'resting' ? [outcome.orderId] : []);
|
|
2448
|
+
const filledReplacementOrderIds = placementOutcomes.flatMap((outcome) => outcome?.state === 'filled' ? [outcome.orderId] : []);
|
|
2449
|
+
if (result.status !== 'ok' ||
|
|
2450
|
+
placementStatuses.length !== orders.length ||
|
|
2451
|
+
replacementOrderIds.length !== orders.length) {
|
|
2452
|
+
const remainingReplacementIds = await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_cancelOrderRequests).call(this, exchangeClient, restingReplacementOrderIds.map((orderId) => ({
|
|
2453
|
+
a: assetId,
|
|
2454
|
+
o: Number(orderId),
|
|
2455
|
+
})));
|
|
2456
|
+
const recoverableOrderIds = [
|
|
2457
|
+
...filledReplacementOrderIds,
|
|
2458
|
+
...remainingReplacementIds.map(String),
|
|
2459
|
+
];
|
|
2460
|
+
if (recoverableOrderIds.length === 0) {
|
|
2461
|
+
const restoration = await restoreCancelledProtection(confirmedCancelledOldOrderIds);
|
|
2462
|
+
if (!restoration.success) {
|
|
2463
|
+
return createProtectionLostResult(restoration.restoredOrderIds);
|
|
2464
|
+
}
|
|
2465
|
+
}
|
|
2466
|
+
// A filled replacement may have changed or closed the position, while
|
|
2467
|
+
// an uncancelled replacement may still protect it. Restoring the old
|
|
2468
|
+
// whole-position triggers in either case risks stale or duplicate
|
|
2469
|
+
// protection, so return those IDs for caller reconciliation instead.
|
|
2470
|
+
if (recoverableOrderIds.length > 0) {
|
|
2471
|
+
if (!isPartialTpsl) {
|
|
2472
|
+
return createProtectionLostResult(recoverableOrderIds);
|
|
2473
|
+
}
|
|
2474
|
+
return createErrorResult(new Error(PERPS_ERROR_CODES.TPSL_UPDATE_FAILED), {
|
|
2475
|
+
success: false,
|
|
2476
|
+
childOrderIds: recoverableOrderIds,
|
|
2477
|
+
});
|
|
2478
|
+
}
|
|
2479
|
+
throw new Error(PERPS_ERROR_CODES.TPSL_UPDATE_FAILED);
|
|
1909
2480
|
}
|
|
1910
2481
|
return {
|
|
1911
2482
|
success: true,
|
|
@@ -1933,8 +2504,9 @@ export class HyperLiquidProvider {
|
|
|
1933
2504
|
async closePosition(params) {
|
|
1934
2505
|
try {
|
|
1935
2506
|
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('Closing position:', params);
|
|
1936
|
-
//
|
|
1937
|
-
|
|
2507
|
+
// The delegated placeOrder call resolves the builder-fee policy for the
|
|
2508
|
+
// concrete close order after validation.
|
|
2509
|
+
await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_ensureReadyForTrading).call(this, { requiresBuilderFee: false });
|
|
1938
2510
|
// Use provided position (from WebSocket) or fetch from cache
|
|
1939
2511
|
// This avoids unnecessary API calls and prevents 429 rate limiting
|
|
1940
2512
|
let { position } = params;
|
|
@@ -2734,6 +3306,7 @@ export class HyperLiquidProvider {
|
|
|
2734
3306
|
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('Using cached open orders from WebSocket', {
|
|
2735
3307
|
count: cachedOrders.length,
|
|
2736
3308
|
});
|
|
3309
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_restoreScaleOrderGroups).call(this, cachedOrders);
|
|
2737
3310
|
return cachedOrders;
|
|
2738
3311
|
}
|
|
2739
3312
|
}
|
|
@@ -2764,6 +3337,7 @@ export class HyperLiquidProvider {
|
|
|
2764
3337
|
const position = positions.find((pos) => pos.symbol === order.coin);
|
|
2765
3338
|
return adaptOrderFromSDK(order, position);
|
|
2766
3339
|
});
|
|
3340
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_restoreScaleOrderGroups).call(this, orders);
|
|
2767
3341
|
return orders;
|
|
2768
3342
|
}
|
|
2769
3343
|
catch (error) {
|
|
@@ -3317,6 +3891,7 @@ export class HyperLiquidProvider {
|
|
|
3317
3891
|
* @returns A promise that resolves to the combined market data from all enabled DEXs.
|
|
3318
3892
|
*/
|
|
3319
3893
|
async getMarketDataWithPrices() {
|
|
3894
|
+
const lifecycleGeneration = __classPrivateFieldGet(this, _HyperLiquidProvider_lifecycleGeneration, "f");
|
|
3320
3895
|
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('Getting market data with prices via HyperLiquid SDK');
|
|
3321
3896
|
// Ensure asset mapping is built first (populates meta cache)
|
|
3322
3897
|
// This guarantees buildAssetMapping has run before we check cache,
|
|
@@ -3332,16 +3907,15 @@ export class HyperLiquidProvider {
|
|
|
3332
3907
|
// Check the meta cache first to avoid redundant API calls when buildAssetMapping
|
|
3333
3908
|
// has already populated it; on cache miss, delegate to #fetchSingleDexFresh.
|
|
3334
3909
|
const dexDataResults = await Promise.all(enabledDexs.map(async (dex) => {
|
|
3335
|
-
const dexKey = dex ?? '';
|
|
3336
3910
|
const dexParam = dex ?? '';
|
|
3337
|
-
const cachedMeta = __classPrivateFieldGet(this, _HyperLiquidProvider_cachedMetaByDex, "f").get(
|
|
3911
|
+
const cachedMeta = __classPrivateFieldGet(this, _HyperLiquidProvider_cachedMetaByDex, "f").get(dex);
|
|
3338
3912
|
if (!cachedMeta) {
|
|
3339
3913
|
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log(`[getMarketDataWithPrices] Cache miss for ${dex ?? 'main'}, fetching`);
|
|
3340
|
-
return __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_fetchSingleDexFresh).call(this, infoClient, dex);
|
|
3914
|
+
return __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_fetchSingleDexFresh).call(this, infoClient, dex, lifecycleGeneration);
|
|
3341
3915
|
}
|
|
3342
3916
|
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log(`[getMarketDataWithPrices] Using cached meta for ${dex ?? 'main'}`, { universeSize: cachedMeta.universe.length });
|
|
3343
3917
|
let metaForDex = cachedMeta;
|
|
3344
|
-
let assetCtxs = __classPrivateFieldGet(this, _HyperLiquidProvider_subscriptionService, "f").getDexAssetCtxsCache(
|
|
3918
|
+
let assetCtxs = __classPrivateFieldGet(this, _HyperLiquidProvider_subscriptionService, "f").getDexAssetCtxsCache(dexParam) ?? [];
|
|
3345
3919
|
let failedStep;
|
|
3346
3920
|
let errorMessage;
|
|
3347
3921
|
if (assetCtxs.length !== metaForDex.universe.length) {
|
|
@@ -3362,10 +3936,12 @@ export class HyperLiquidProvider {
|
|
|
3362
3936
|
}
|
|
3363
3937
|
metaForDex = freshMeta;
|
|
3364
3938
|
assetCtxs = freshAssetCtxs;
|
|
3365
|
-
__classPrivateFieldGet(this,
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3939
|
+
await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_cacheDexMetadataFromRead).call(this, {
|
|
3940
|
+
dex,
|
|
3941
|
+
meta: freshMeta,
|
|
3942
|
+
assetCtxs,
|
|
3943
|
+
lifecycleGeneration,
|
|
3944
|
+
});
|
|
3369
3945
|
}
|
|
3370
3946
|
catch (error) {
|
|
3371
3947
|
return {
|
|
@@ -3417,7 +3993,7 @@ export class HyperLiquidProvider {
|
|
|
3417
3993
|
const retryDelayMs = 2000;
|
|
3418
3994
|
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log(`[getMarketDataWithPrices] All DEXs returned empty, retrying in ${retryDelayMs}ms`);
|
|
3419
3995
|
await new Promise((resolve) => setTimeout(resolve, retryDelayMs));
|
|
3420
|
-
const retryResults = await Promise.all(enabledDexs.map((dex) => __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_fetchSingleDexFresh).call(this, infoClient, dex)));
|
|
3996
|
+
const retryResults = await Promise.all(enabledDexs.map((dex) => __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_fetchSingleDexFresh).call(this, infoClient, dex, lifecycleGeneration)));
|
|
3421
3997
|
const usdcFilteredRetryResults = await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_excludeNonUsdcCollateralResults).call(this, retryResults);
|
|
3422
3998
|
__classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_mergeDexResultsInto).call(this, usdcFilteredRetryResults, combinedUniverse, combinedAssetCtxs, combinedAllMids);
|
|
3423
3999
|
latestDexResults = usdcFilteredRetryResults;
|
|
@@ -3477,6 +4053,9 @@ export class HyperLiquidProvider {
|
|
|
3477
4053
|
assetCtxs: combinedAssetCtxs,
|
|
3478
4054
|
allMids: combinedAllMids,
|
|
3479
4055
|
}, __classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").marketDataFormatters, HIP3_ASSET_MARKET_TYPES, HYPERLIQUID_ASSET_NAMES);
|
|
4056
|
+
if (!__classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_isCacheWriteLifecycleCurrent).call(this, lifecycleGeneration, 'Market data cache write')) {
|
|
4057
|
+
return transformedMarketData;
|
|
4058
|
+
}
|
|
3480
4059
|
return __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_cacheFreshMarketDataSnapshot).call(this, transformedMarketData, latestDexResults);
|
|
3481
4060
|
}
|
|
3482
4061
|
/**
|
|
@@ -4131,23 +4710,28 @@ export class HyperLiquidProvider {
|
|
|
4131
4710
|
async toggleTestnet() {
|
|
4132
4711
|
try {
|
|
4133
4712
|
const newIsTestnet = !__classPrivateFieldGet(this, _HyperLiquidProvider_clientService, "f").isTestnetMode();
|
|
4134
|
-
|
|
4135
|
-
|
|
4136
|
-
|
|
4137
|
-
|
|
4138
|
-
|
|
4139
|
-
|
|
4140
|
-
|
|
4141
|
-
}
|
|
4142
|
-
catch {
|
|
4143
|
-
// Ignore - we're switching networks anyway
|
|
4144
|
-
}
|
|
4713
|
+
const disconnectResult = await this.disconnect();
|
|
4714
|
+
if (!disconnectResult.success) {
|
|
4715
|
+
// The network did not change. Keep the provider eligible for lazy
|
|
4716
|
+
// recovery instead of leaving this aborted toggle permanently sticky.
|
|
4717
|
+
__classPrivateFieldSet(this, _HyperLiquidProvider_isDisconnected, false, "f");
|
|
4718
|
+
throw new Error(disconnectResult.error ??
|
|
4719
|
+
'Failed to disconnect before network toggle');
|
|
4145
4720
|
}
|
|
4146
4721
|
// Update all services
|
|
4147
4722
|
__classPrivateFieldGet(this, _HyperLiquidProvider_clientService, "f").setTestnetMode(newIsTestnet);
|
|
4723
|
+
// Invalidate reads that started after disconnect completed but before
|
|
4724
|
+
// the clients switched networks.
|
|
4725
|
+
__classPrivateFieldSet(this, _HyperLiquidProvider_lifecycleGeneration, __classPrivateFieldGet(this, _HyperLiquidProvider_lifecycleGeneration, "f") + 1, "f");
|
|
4148
4726
|
__classPrivateFieldGet(this, _HyperLiquidProvider_walletService, "f").setTestnetMode(newIsTestnet);
|
|
4149
|
-
|
|
4150
|
-
|
|
4727
|
+
const initializeResult = await this.initialize();
|
|
4728
|
+
if (!initializeResult.success) {
|
|
4729
|
+
// The network switch is already committed. Leave the provider eligible
|
|
4730
|
+
// for lazy initialization so the next operation can retry the failed
|
|
4731
|
+
// client setup instead of remaining permanently disconnected.
|
|
4732
|
+
__classPrivateFieldSet(this, _HyperLiquidProvider_isDisconnected, false, "f");
|
|
4733
|
+
throw new Error(initializeResult.error ?? 'Failed to initialize after network toggle');
|
|
4734
|
+
}
|
|
4151
4735
|
return {
|
|
4152
4736
|
success: true,
|
|
4153
4737
|
isTestnet: newIsTestnet,
|
|
@@ -4166,9 +4750,18 @@ export class HyperLiquidProvider {
|
|
|
4166
4750
|
* @returns A promise that resolves to the result.
|
|
4167
4751
|
*/
|
|
4168
4752
|
async initialize() {
|
|
4753
|
+
const lifecycleGeneration = __classPrivateFieldGet(this, _HyperLiquidProvider_lifecycleGeneration, "f");
|
|
4169
4754
|
try {
|
|
4755
|
+
if (__classPrivateFieldGet(this, _HyperLiquidProvider_disconnectOperationsInFlight, "f") > 0) {
|
|
4756
|
+
throw new Error(PERPS_ERROR_CODES.PROVIDER_LIFECYCLE_STALE);
|
|
4757
|
+
}
|
|
4170
4758
|
// Ensure clients are initialized (lazy initialization)
|
|
4171
4759
|
await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_ensureClientsInitialized).call(this);
|
|
4760
|
+
if (__classPrivateFieldGet(this, _HyperLiquidProvider_disconnectOperationsInFlight, "f") > 0 ||
|
|
4761
|
+
lifecycleGeneration !== __classPrivateFieldGet(this, _HyperLiquidProvider_lifecycleGeneration, "f")) {
|
|
4762
|
+
throw new Error(PERPS_ERROR_CODES.PROVIDER_LIFECYCLE_STALE);
|
|
4763
|
+
}
|
|
4764
|
+
__classPrivateFieldSet(this, _HyperLiquidProvider_isDisconnected, false, "f");
|
|
4172
4765
|
return {
|
|
4173
4766
|
success: true,
|
|
4174
4767
|
chainId: getChainId(__classPrivateFieldGet(this, _HyperLiquidProvider_clientService, "f").isTestnetMode()),
|
|
@@ -4306,6 +4899,7 @@ export class HyperLiquidProvider {
|
|
|
4306
4899
|
* @returns A promise that resolves to the numeric result.
|
|
4307
4900
|
*/
|
|
4308
4901
|
async getMaxLeverage(asset) {
|
|
4902
|
+
const lifecycleGeneration = __classPrivateFieldGet(this, _HyperLiquidProvider_lifecycleGeneration, "f");
|
|
4309
4903
|
try {
|
|
4310
4904
|
// Check cache first
|
|
4311
4905
|
const cached = __classPrivateFieldGet(this, _HyperLiquidProvider_maxLeverageCache, "f").get(asset);
|
|
@@ -4338,11 +4932,12 @@ export class HyperLiquidProvider {
|
|
|
4338
4932
|
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log(`Asset ${asset} not found in universe, using default max leverage`);
|
|
4339
4933
|
return PERPS_CONSTANTS.DefaultMaxLeverage;
|
|
4340
4934
|
}
|
|
4341
|
-
|
|
4342
|
-
|
|
4343
|
-
|
|
4344
|
-
|
|
4345
|
-
|
|
4935
|
+
if (__classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_isCacheWriteLifecycleCurrent).call(this, lifecycleGeneration, 'Maximum leverage cache write')) {
|
|
4936
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_maxLeverageCache, "f").set(asset, {
|
|
4937
|
+
value: assetInfo.maxLeverage,
|
|
4938
|
+
timestamp: now,
|
|
4939
|
+
});
|
|
4940
|
+
}
|
|
4346
4941
|
return assetInfo.maxLeverage;
|
|
4347
4942
|
}
|
|
4348
4943
|
catch (error) {
|
|
@@ -4363,7 +4958,13 @@ export class HyperLiquidProvider {
|
|
|
4363
4958
|
* @returns A promise that resolves to the result.
|
|
4364
4959
|
*/
|
|
4365
4960
|
async calculateFees(params) {
|
|
4961
|
+
const lifecycleGeneration = __classPrivateFieldGet(this, _HyperLiquidProvider_lifecycleGeneration, "f");
|
|
4366
4962
|
const { orderType, isMaker = false, amount, symbol } = params;
|
|
4963
|
+
const numericAmount = amount === undefined ? undefined : Number.parseFloat(amount);
|
|
4964
|
+
const parsedAmount = numericAmount === undefined ||
|
|
4965
|
+
(Number.isFinite(numericAmount) && numericAmount >= 0)
|
|
4966
|
+
? numericAmount
|
|
4967
|
+
: 0;
|
|
4367
4968
|
// Every placement is charged as its execution kind: a stop_market fills as a
|
|
4368
4969
|
// market order (taker), a stop_limit as a limit order, a scale ladder as the
|
|
4369
4970
|
// resting limit orders it fans out into, and a TWAP as the marketable
|
|
@@ -4410,6 +5011,7 @@ export class HyperLiquidProvider {
|
|
|
4410
5011
|
// Try to get user-specific rates if wallet is connected
|
|
4411
5012
|
try {
|
|
4412
5013
|
const userAddress = await __classPrivateFieldGet(this, _HyperLiquidProvider_walletService, "f").getUserAddressWithDefault();
|
|
5014
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_assertProviderLifecycleCurrent).call(this, lifecycleGeneration, 'User fee cache read');
|
|
4413
5015
|
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('User Address Retrieved', {
|
|
4414
5016
|
userAddress,
|
|
4415
5017
|
network: __classPrivateFieldGet(this, _HyperLiquidProvider_clientService, "f").isTestnetMode() ? 'testnet' : 'mainnet',
|
|
@@ -4450,7 +5052,9 @@ export class HyperLiquidProvider {
|
|
|
4450
5052
|
});
|
|
4451
5053
|
// Fetch fresh rates from SDK
|
|
4452
5054
|
// Read-only operation: only need client initialization
|
|
5055
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_assertProviderLifecycleCurrent).call(this, lifecycleGeneration, 'User fee fetch');
|
|
4453
5056
|
await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_ensureClientsInitialized).call(this);
|
|
5057
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_assertProviderLifecycleCurrent).call(this, lifecycleGeneration, 'User fee fetch');
|
|
4454
5058
|
__classPrivateFieldGet(this, _HyperLiquidProvider_clientService, "f").ensureInitialized();
|
|
4455
5059
|
const infoClient = __classPrivateFieldGet(this, _HyperLiquidProvider_clientService, "f").getInfoClient();
|
|
4456
5060
|
const userFees = await infoClient.userFees({
|
|
@@ -4463,13 +5067,21 @@ export class HyperLiquidProvider {
|
|
|
4463
5067
|
activeStakingDiscount: userFees.activeStakingDiscount,
|
|
4464
5068
|
});
|
|
4465
5069
|
// Parse base user rates (these don't include discounts as expected)
|
|
4466
|
-
const baseUserTakerRate =
|
|
4467
|
-
const baseUserMakerRate =
|
|
4468
|
-
const baseUserSpotTakerRate =
|
|
4469
|
-
const baseUserSpotMakerRate =
|
|
5070
|
+
const baseUserTakerRate = parseBoundedNonNegativeDecimal(userFees.userCrossRate, MAX_API_FEE_RATE);
|
|
5071
|
+
const baseUserMakerRate = parseBoundedNonNegativeDecimal(userFees.userAddRate, MAX_API_FEE_RATE);
|
|
5072
|
+
const baseUserSpotTakerRate = parseBoundedNonNegativeDecimal(userFees.userSpotCrossRate, MAX_API_FEE_RATE);
|
|
5073
|
+
const baseUserSpotMakerRate = parseBoundedNonNegativeDecimal(userFees.userSpotAddRate, MAX_API_FEE_RATE);
|
|
4470
5074
|
// Apply discounts manually since HyperLiquid API doesn't apply them
|
|
4471
|
-
const referralDiscount =
|
|
4472
|
-
const stakingDiscount =
|
|
5075
|
+
const referralDiscount = parseBoundedNonNegativeDecimal(userFees.activeReferralDiscount || '0', MAX_API_FEE_DISCOUNT);
|
|
5076
|
+
const stakingDiscount = parseBoundedNonNegativeDecimal(userFees.activeStakingDiscount?.discount || '0', MAX_API_FEE_DISCOUNT);
|
|
5077
|
+
if (baseUserTakerRate === null ||
|
|
5078
|
+
baseUserMakerRate === null ||
|
|
5079
|
+
baseUserSpotTakerRate === null ||
|
|
5080
|
+
baseUserSpotMakerRate === null ||
|
|
5081
|
+
referralDiscount === null ||
|
|
5082
|
+
stakingDiscount === null) {
|
|
5083
|
+
throw new Error('Invalid fee rates received from API');
|
|
5084
|
+
}
|
|
4473
5085
|
// Calculate total discount (referral + staking, but not compounding)
|
|
4474
5086
|
const totalDiscount = Math.min(referralDiscount + stakingDiscount, 0.4); // Cap at 40%
|
|
4475
5087
|
// Apply discount to rates
|
|
@@ -4495,20 +5107,20 @@ export class HyperLiquidProvider {
|
|
|
4495
5107
|
},
|
|
4496
5108
|
});
|
|
4497
5109
|
// Validate all rates are valid numbers before caching
|
|
4498
|
-
if (
|
|
4499
|
-
|
|
4500
|
-
|
|
4501
|
-
|
|
5110
|
+
if (!Number.isFinite(perpsTakerRate) ||
|
|
5111
|
+
!Number.isFinite(perpsMakerRate) ||
|
|
5112
|
+
!Number.isFinite(spotTakerRate) ||
|
|
5113
|
+
!Number.isFinite(spotMakerRate) ||
|
|
4502
5114
|
perpsTakerRate < 0 ||
|
|
4503
5115
|
perpsMakerRate < 0 ||
|
|
4504
5116
|
spotTakerRate < 0 ||
|
|
4505
5117
|
spotMakerRate < 0) {
|
|
4506
5118
|
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('Fee Rate Validation Failed', {
|
|
4507
5119
|
validation: {
|
|
4508
|
-
perpsTakerValid:
|
|
4509
|
-
perpsMakerValid:
|
|
4510
|
-
spotTakerValid:
|
|
4511
|
-
spotMakerValid:
|
|
5120
|
+
perpsTakerValid: Number.isFinite(perpsTakerRate) && perpsTakerRate >= 0,
|
|
5121
|
+
perpsMakerValid: Number.isFinite(perpsMakerRate) && perpsMakerRate >= 0,
|
|
5122
|
+
spotTakerValid: Number.isFinite(spotTakerRate) && spotTakerRate >= 0,
|
|
5123
|
+
spotMakerValid: Number.isFinite(spotMakerRate) && spotMakerRate >= 0,
|
|
4512
5124
|
},
|
|
4513
5125
|
rawValues: {
|
|
4514
5126
|
perpsTakerRate,
|
|
@@ -4527,6 +5139,7 @@ export class HyperLiquidProvider {
|
|
|
4527
5139
|
timestamp: Date.now(),
|
|
4528
5140
|
ttl: 5 * 60 * 1000, // 5 minutes
|
|
4529
5141
|
};
|
|
5142
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_assertProviderLifecycleCurrent).call(this, lifecycleGeneration, 'User fee cache write');
|
|
4530
5143
|
__classPrivateFieldGet(this, _HyperLiquidProvider_userFeeCache, "f").set(userAddress, rates);
|
|
4531
5144
|
// Same maker/taker decision as the base rates above, chase included.
|
|
4532
5145
|
let userFeeRate = chargesMakerRate
|
|
@@ -4550,6 +5163,9 @@ export class HyperLiquidProvider {
|
|
|
4550
5163
|
catch (error) {
|
|
4551
5164
|
// Silently fall back to base rates
|
|
4552
5165
|
const safeError = ensureError(error, 'HyperLiquidProvider.getFeeSchedule');
|
|
5166
|
+
if (safeError.message === PERPS_ERROR_CODES.PROVIDER_LIFECYCLE_STALE) {
|
|
5167
|
+
throw safeError;
|
|
5168
|
+
}
|
|
4553
5169
|
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('Fee API Call Failed - Falling Back to Base Rates', {
|
|
4554
5170
|
error: safeError.message,
|
|
4555
5171
|
errorType: safeError.name,
|
|
@@ -4558,43 +5174,46 @@ export class HyperLiquidProvider {
|
|
|
4558
5174
|
userAddress: 'unknown',
|
|
4559
5175
|
});
|
|
4560
5176
|
}
|
|
4561
|
-
|
|
5177
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_assertProviderLifecycleCurrent).call(this, lifecycleGeneration, 'Fee calculation');
|
|
4562
5178
|
// Protocol base fee (HyperLiquid's fee)
|
|
4563
5179
|
const protocolFeeRate = feeRate;
|
|
4564
|
-
|
|
4565
|
-
|
|
4566
|
-
|
|
4567
|
-
}
|
|
4568
|
-
|
|
4569
|
-
|
|
4570
|
-
|
|
4571
|
-
|
|
4572
|
-
|
|
4573
|
-
|
|
4574
|
-
|
|
4575
|
-
|
|
4576
|
-
// Apply MetaMask reward discount if active
|
|
4577
|
-
if (__classPrivateFieldGet(this, _HyperLiquidProvider_userFeeDiscountBips, "f") !== undefined) {
|
|
4578
|
-
const discount = __classPrivateFieldGet(this, _HyperLiquidProvider_userFeeDiscountBips, "f") / BASIS_POINTS_DIVISOR; // Convert basis points to decimal
|
|
4579
|
-
metamaskFeeRate = BUILDER_FEE_CONFIG.MaxFeeDecimal * (1 - discount);
|
|
5180
|
+
const protocolFeeAmount = parsedAmount === undefined ? undefined : parsedAmount * protocolFeeRate;
|
|
5181
|
+
// The provider policy, not the client, owns whether this placement can
|
|
5182
|
+
// carry a builder fee. The dedicated TWAP action has no builder field.
|
|
5183
|
+
const { chargesMetamaskBuilderFee } = __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_resolveOrderFeePolicy).call(this, params);
|
|
5184
|
+
const baseMetamaskFeeRate = chargesMetamaskBuilderFee
|
|
5185
|
+
? BUILDER_FEE_CONFIG.MaxFeeDecimal
|
|
5186
|
+
: 0;
|
|
5187
|
+
const metamaskFeeDiscount = __classPrivateFieldGet(this, _HyperLiquidProvider_userFeeDiscountBips, "f") === undefined
|
|
5188
|
+
? 0
|
|
5189
|
+
: __classPrivateFieldGet(this, _HyperLiquidProvider_userFeeDiscountBips, "f") / BASIS_POINTS_DIVISOR;
|
|
5190
|
+
const metamaskFeeRate = baseMetamaskFeeRate * (1 - metamaskFeeDiscount);
|
|
5191
|
+
if (chargesMetamaskBuilderFee && __classPrivateFieldGet(this, _HyperLiquidProvider_userFeeDiscountBips, "f") !== undefined) {
|
|
4580
5192
|
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('HyperLiquid: Applied MetaMask fee discount', {
|
|
4581
|
-
originalRate:
|
|
5193
|
+
originalRate: baseMetamaskFeeRate,
|
|
4582
5194
|
discountBips: __classPrivateFieldGet(this, _HyperLiquidProvider_userFeeDiscountBips, "f"),
|
|
4583
5195
|
discountPercentage: __classPrivateFieldGet(this, _HyperLiquidProvider_userFeeDiscountBips, "f") / 100,
|
|
4584
5196
|
adjustedRate: metamaskFeeRate,
|
|
4585
|
-
discountAmount:
|
|
5197
|
+
discountAmount: baseMetamaskFeeRate * metamaskFeeDiscount,
|
|
4586
5198
|
});
|
|
4587
5199
|
}
|
|
4588
|
-
const
|
|
4589
|
-
? 0
|
|
4590
|
-
: parsedAmount * metamaskFeeRate;
|
|
4591
|
-
const metamaskFeeAmount = amount === undefined ? undefined : validAmountForMetamaskFee;
|
|
5200
|
+
const metamaskFeeAmount = parsedAmount === undefined ? undefined : parsedAmount * metamaskFeeRate;
|
|
4592
5201
|
// Total fees
|
|
4593
5202
|
const totalFeeRate = protocolFeeRate + metamaskFeeRate;
|
|
4594
|
-
const
|
|
4595
|
-
|
|
4596
|
-
|
|
4597
|
-
|
|
5203
|
+
const totalFeeAmount = parsedAmount === undefined ? undefined : parsedAmount * totalFeeRate;
|
|
5204
|
+
if (!Number.isFinite(protocolFeeRate) ||
|
|
5205
|
+
protocolFeeRate < 0 ||
|
|
5206
|
+
!Number.isFinite(metamaskFeeRate) ||
|
|
5207
|
+
metamaskFeeRate < 0 ||
|
|
5208
|
+
!Number.isFinite(totalFeeRate) ||
|
|
5209
|
+
totalFeeRate < 0 ||
|
|
5210
|
+
(protocolFeeAmount !== undefined &&
|
|
5211
|
+
!Number.isFinite(protocolFeeAmount)) ||
|
|
5212
|
+
(metamaskFeeAmount !== undefined &&
|
|
5213
|
+
!Number.isFinite(metamaskFeeAmount)) ||
|
|
5214
|
+
(totalFeeAmount !== undefined && !Number.isFinite(totalFeeAmount))) {
|
|
5215
|
+
throw new Error('Invalid fee calculation result');
|
|
5216
|
+
}
|
|
4598
5217
|
const result = {
|
|
4599
5218
|
// Total fees
|
|
4600
5219
|
feeRate: totalFeeRate,
|
|
@@ -4653,100 +5272,18 @@ export class HyperLiquidProvider {
|
|
|
4653
5272
|
* @returns A promise that resolves to the result.
|
|
4654
5273
|
*/
|
|
4655
5274
|
async disconnect() {
|
|
4656
|
-
|
|
4657
|
-
|
|
5275
|
+
if (__classPrivateFieldGet(this, _HyperLiquidProvider_disconnectOperationPromise, "f")) {
|
|
5276
|
+
return __classPrivateFieldGet(this, _HyperLiquidProvider_disconnectOperationPromise, "f");
|
|
5277
|
+
}
|
|
5278
|
+
const operation = __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_performDisconnect).call(this);
|
|
5279
|
+
__classPrivateFieldSet(this, _HyperLiquidProvider_disconnectOperationPromise, operation, "f");
|
|
4658
5280
|
try {
|
|
4659
|
-
|
|
4660
|
-
isTestnet: __classPrivateFieldGet(this, _HyperLiquidProvider_clientService, "f").isTestnetMode(),
|
|
4661
|
-
timestamp: new Date().toISOString(),
|
|
4662
|
-
});
|
|
4663
|
-
// Clear subscriptions through subscription service
|
|
4664
|
-
__classPrivateFieldGet(this, _HyperLiquidProvider_subscriptionService, "f").clearAll();
|
|
4665
|
-
// Stop every chase loop: each holds a pending timer, and re-pricing after
|
|
4666
|
-
// a disconnect would sign orders against a client that is being torn down.
|
|
4667
|
-
// Orders already resting are deliberately left alone — disconnecting is
|
|
4668
|
-
// not a request to cancel the user's positions or orders.
|
|
4669
|
-
// Invalidate placements first, then drain every mutation before clearing
|
|
4670
|
-
// the registry and tearing down the clients they use.
|
|
4671
|
-
for (const [sessionId, session] of __classPrivateFieldGet(this, _HyperLiquidProvider_chaseSessions, "f").entries()) {
|
|
4672
|
-
if (session.active) {
|
|
4673
|
-
__classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_stopChaseSession).call(this, sessionId);
|
|
4674
|
-
}
|
|
4675
|
-
}
|
|
4676
|
-
await Promise.all([...__classPrivateFieldGet(this, _HyperLiquidProvider_chasePlacementWaiters, "f")]);
|
|
4677
|
-
await __classPrivateFieldGet(this, _HyperLiquidProvider_chaseTickQueue, "f");
|
|
4678
|
-
await Promise.allSettled([...__classPrivateFieldGet(this, _HyperLiquidProvider_chaseTerminations, "f").values()]);
|
|
4679
|
-
__classPrivateFieldGet(this, _HyperLiquidProvider_chaseSessions, "f").clear();
|
|
4680
|
-
__classPrivateFieldGet(this, _HyperLiquidProvider_chaseTerminations, "f").clear();
|
|
4681
|
-
__classPrivateFieldGet(this, _HyperLiquidProvider_terminatedChaseHandles, "f").clear();
|
|
4682
|
-
__classPrivateFieldGet(this, _HyperLiquidProvider_scaleOrderGroups, "f").clear();
|
|
4683
|
-
// Clear fee cache
|
|
4684
|
-
this.clearFeeCache();
|
|
4685
|
-
// Clear session caches (ensures fresh state on reconnect/account switch)
|
|
4686
|
-
__classPrivateFieldGet(this, _HyperLiquidProvider_referralCheckCache, "f").clear();
|
|
4687
|
-
__classPrivateFieldGet(this, _HyperLiquidProvider_builderFeeCheckCache, "f").clear();
|
|
4688
|
-
__classPrivateFieldSet(this, _HyperLiquidProvider_subscriptionBuilderApprovalEpoch, __classPrivateFieldGet(this, _HyperLiquidProvider_subscriptionBuilderApprovalEpoch, "f") + 1, "f");
|
|
4689
|
-
__classPrivateFieldGet(this, _HyperLiquidProvider_approvedBuilderAddresses, "f").clear();
|
|
4690
|
-
__classPrivateFieldSet(this, _HyperLiquidProvider_userFeeResolution, undefined, "f");
|
|
4691
|
-
__classPrivateFieldSet(this, _HyperLiquidProvider_userFeeDiscountBips, undefined, "f");
|
|
4692
|
-
// NOTE: UnifiedAccountCache is global and NOT cleared on disconnect
|
|
4693
|
-
// to prevent repeated signing requests across reconnections
|
|
4694
|
-
__classPrivateFieldGet(this, _HyperLiquidProvider_cachedMetaByDex, "f").clear();
|
|
4695
|
-
__classPrivateFieldSet(this, _HyperLiquidProvider_cachedSpotMeta, null, "f");
|
|
4696
|
-
__classPrivateFieldGet(this, _HyperLiquidProvider_dexDiscoveryCache, "f").reset();
|
|
4697
|
-
__classPrivateFieldSet(this, _HyperLiquidProvider_dexDiscoveryComplete, false, "f");
|
|
4698
|
-
// Await pending initialization before clearing to prevent the IIFE from
|
|
4699
|
-
// setting clientsInitialized = true after disconnect completes
|
|
4700
|
-
const pendingInit = __classPrivateFieldGet(this, _HyperLiquidProvider_initializationPromise, "f");
|
|
4701
|
-
const pendingReady = __classPrivateFieldGet(this, _HyperLiquidProvider_ensureReadyPromise, "f");
|
|
4702
|
-
const pendingTradingSetup = __classPrivateFieldGet(this, _HyperLiquidProvider_tradingSetupPromise, "f");
|
|
4703
|
-
// Clear references first to prevent new callers from reusing
|
|
4704
|
-
__classPrivateFieldSet(this, _HyperLiquidProvider_initializationPromise, null, "f");
|
|
4705
|
-
__classPrivateFieldSet(this, _HyperLiquidProvider_ensureReadyPromise, null, "f");
|
|
4706
|
-
__classPrivateFieldSet(this, _HyperLiquidProvider_tradingSetupPromise, null, "f");
|
|
4707
|
-
__classPrivateFieldSet(this, _HyperLiquidProvider_tradingSetupComplete, false, "f");
|
|
4708
|
-
__classPrivateFieldGet(this, _HyperLiquidProvider_pendingBuilderFeeApprovals, "f").clear();
|
|
4709
|
-
// Wait for pending operations to complete (ignore errors)
|
|
4710
|
-
// This prevents IIFEs from setting state after disconnect completes
|
|
4711
|
-
if (pendingInit) {
|
|
4712
|
-
try {
|
|
4713
|
-
await pendingInit;
|
|
4714
|
-
}
|
|
4715
|
-
catch {
|
|
4716
|
-
// Ignore - we're disconnecting anyway
|
|
4717
|
-
}
|
|
4718
|
-
}
|
|
4719
|
-
if (pendingReady) {
|
|
4720
|
-
try {
|
|
4721
|
-
await pendingReady;
|
|
4722
|
-
}
|
|
4723
|
-
catch {
|
|
4724
|
-
// Ignore - we're disconnecting anyway
|
|
4725
|
-
}
|
|
4726
|
-
}
|
|
4727
|
-
if (pendingTradingSetup) {
|
|
4728
|
-
try {
|
|
4729
|
-
await pendingTradingSetup;
|
|
4730
|
-
}
|
|
4731
|
-
catch {
|
|
4732
|
-
// Ignore - we're disconnecting anyway
|
|
4733
|
-
}
|
|
4734
|
-
}
|
|
4735
|
-
// Reset client initialization flag so wallet adapter will be recreated with new account
|
|
4736
|
-
// This fixes account synchronization issue where old account's address persists in wallet adapter
|
|
4737
|
-
__classPrivateFieldSet(this, _HyperLiquidProvider_clientsInitialized, false, "f");
|
|
4738
|
-
// Disconnect client service
|
|
4739
|
-
await __classPrivateFieldGet(this, _HyperLiquidProvider_clientService, "f").disconnect();
|
|
4740
|
-
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('HyperLiquid: Provider fully disconnected', {
|
|
4741
|
-
timestamp: new Date().toISOString(),
|
|
4742
|
-
});
|
|
4743
|
-
return { success: true };
|
|
4744
|
-
}
|
|
4745
|
-
catch (error) {
|
|
4746
|
-
return createErrorResult(error, { success: false });
|
|
5281
|
+
return await operation;
|
|
4747
5282
|
}
|
|
4748
5283
|
finally {
|
|
4749
|
-
|
|
5284
|
+
if (__classPrivateFieldGet(this, _HyperLiquidProvider_disconnectOperationPromise, "f") === operation) {
|
|
5285
|
+
__classPrivateFieldSet(this, _HyperLiquidProvider_disconnectOperationPromise, null, "f");
|
|
5286
|
+
}
|
|
4750
5287
|
}
|
|
4751
5288
|
}
|
|
4752
5289
|
/**
|
|
@@ -4872,7 +5409,75 @@ export class HyperLiquidProvider {
|
|
|
4872
5409
|
return `${baseUrl}/explorer`;
|
|
4873
5410
|
}
|
|
4874
5411
|
}
|
|
4875
|
-
_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_subscriptionBuilderApprovalEpoch = new WeakMap(), _HyperLiquidProvider_approvedBuilderAddresses = new WeakMap(), _HyperLiquidProvider_compiledAllowlistPatterns = new WeakMap(), _HyperLiquidProvider_compiledBlocklistPatterns = new WeakMap(), _HyperLiquidProvider_userFeeDiscountBips = new WeakMap(), _HyperLiquidProvider_userFeeResolution = 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_scaleOrderGroups = new WeakMap(), _HyperLiquidProvider_chaseSessions = new WeakMap(), _HyperLiquidProvider_chaseTerminations = new WeakMap(), _HyperLiquidProvider_terminatedChaseHandles = new WeakMap(), _HyperLiquidProvider_chasePlacementsInFlight = new WeakMap(), _HyperLiquidProvider_chasePlacementWaiters = new WeakMap(), _HyperLiquidProvider_chaseTickQueue = new WeakMap(),
|
|
5412
|
+
_a = HyperLiquidProvider, _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_orderCapabilitiesMarketsByDex = new WeakMap(), _HyperLiquidProvider_orderCapabilitiesRefreshByDex = new WeakMap(), _HyperLiquidProvider_isDisconnected = new WeakMap(), _HyperLiquidProvider_disconnectOperationsInFlight = new WeakMap(), _HyperLiquidProvider_disconnectOperationPromise = new WeakMap(), _HyperLiquidProvider_lifecycleGeneration = 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_subscriptionBuilderApprovalEpoch = new WeakMap(), _HyperLiquidProvider_approvedBuilderAddresses = new WeakMap(), _HyperLiquidProvider_compiledAllowlistPatterns = new WeakMap(), _HyperLiquidProvider_compiledBlocklistPatterns = new WeakMap(), _HyperLiquidProvider_userFeeDiscountBips = new WeakMap(), _HyperLiquidProvider_userFeeResolution = 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_scaleOrderGroups = new WeakMap(), _HyperLiquidProvider_cancelledScaleOrderGroups = new WeakMap(), _HyperLiquidProvider_trackedTwapOrders = new WeakMap(), _HyperLiquidProvider_chaseSessions = new WeakMap(), _HyperLiquidProvider_chaseTerminations = new WeakMap(), _HyperLiquidProvider_terminatedChaseHandles = new WeakMap(), _HyperLiquidProvider_chasePlacementsInFlight = new WeakMap(), _HyperLiquidProvider_chasePlacementWaiters = new WeakMap(), _HyperLiquidProvider_chaseTickQueue = new WeakMap(), _HyperLiquidProvider_strategyGeneration = new WeakMap(), _HyperLiquidProvider_chasePlacementBlockers = new WeakMap(), _HyperLiquidProvider_clientsInitialized = new WeakMap(), _HyperLiquidProvider_initializationPromise = new WeakMap(), _HyperLiquidProvider_messenger = new WeakMap(), _HyperLiquidProvider_builderAddressTestnet = new WeakMap(), _HyperLiquidProvider_builderAddressMainnet = new WeakMap(), _HyperLiquidProvider_subscriptionBuilderAddressTestnet = new WeakMap(), _HyperLiquidProvider_subscriptionBuilderAddressMainnet = new WeakMap(), _HyperLiquidProvider_priceDeviationLimit = new WeakMap(), _HyperLiquidProvider_onChaseOrderMaxDistanceReached = new WeakMap(), _HyperLiquidProvider_tradingSetupPromise = new WeakMap(), _HyperLiquidProvider_tradingSetupComplete = new WeakMap(), _HyperLiquidProvider_builderFeeSetupPromises = new WeakMap(), _HyperLiquidProvider_instances = new WeakSet(), _HyperLiquidProvider_resolveOrderFeePolicy = function _HyperLiquidProvider_resolveOrderFeePolicy(params) {
|
|
5413
|
+
const fallbackPolicy = Object.prototype.hasOwnProperty.call(HYPERLIQUID_ORDER_FEE_CONFIG, params.orderType)
|
|
5414
|
+
? HYPERLIQUID_ORDER_FEE_CONFIG[params.orderType]
|
|
5415
|
+
: undefined;
|
|
5416
|
+
if (fallbackPolicy) {
|
|
5417
|
+
return fallbackPolicy;
|
|
5418
|
+
}
|
|
5419
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('HyperLiquid: Unknown order type used the safe builder-fee policy', { orderType: params.orderType });
|
|
5420
|
+
return HYPERLIQUID_ORDER_FEE_CONFIG.market;
|
|
5421
|
+
}, _HyperLiquidProvider_assertProviderLifecycleCurrent = function _HyperLiquidProvider_assertProviderLifecycleCurrent(generation, operation) {
|
|
5422
|
+
if (__classPrivateFieldGet(this, _HyperLiquidProvider_isDisconnected, "f") || generation !== __classPrivateFieldGet(this, _HyperLiquidProvider_lifecycleGeneration, "f")) {
|
|
5423
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('HyperLiquid: Provider operation became stale during disconnect', { operation });
|
|
5424
|
+
throw new Error(PERPS_ERROR_CODES.PROVIDER_LIFECYCLE_STALE);
|
|
5425
|
+
}
|
|
5426
|
+
}, _HyperLiquidProvider_assertCacheWriteLifecycleCurrent = function _HyperLiquidProvider_assertCacheWriteLifecycleCurrent(generation, operation) {
|
|
5427
|
+
if (!__classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_isCacheWriteLifecycleCurrent).call(this, generation, operation)) {
|
|
5428
|
+
throw new Error(PERPS_ERROR_CODES.PROVIDER_LIFECYCLE_STALE);
|
|
5429
|
+
}
|
|
5430
|
+
}, _HyperLiquidProvider_isCacheWriteLifecycleCurrent = function _HyperLiquidProvider_isCacheWriteLifecycleCurrent(generation, operation) {
|
|
5431
|
+
if (__classPrivateFieldGet(this, _HyperLiquidProvider_disconnectOperationsInFlight, "f") > 0 ||
|
|
5432
|
+
generation !== __classPrivateFieldGet(this, _HyperLiquidProvider_lifecycleGeneration, "f")) {
|
|
5433
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('HyperLiquid: Cache write became stale during disconnect', { operation });
|
|
5434
|
+
return false;
|
|
5435
|
+
}
|
|
5436
|
+
return true;
|
|
5437
|
+
}, _HyperLiquidProvider_getFreshOrderCapabilityMarkets =
|
|
5438
|
+
/**
|
|
5439
|
+
* Return filtered market metadata fresh enough for a capability query.
|
|
5440
|
+
* Each DEX shares one refresh across concurrent callers, and a failed
|
|
5441
|
+
* refresh remains immediately retryable.
|
|
5442
|
+
*
|
|
5443
|
+
* @param dex - Routed DEX, or null for the main DEX.
|
|
5444
|
+
* @returns Fresh filtered markets for that route.
|
|
5445
|
+
*/
|
|
5446
|
+
async function _HyperLiquidProvider_getFreshOrderCapabilityMarkets(dex) {
|
|
5447
|
+
const lifecycleGeneration = __classPrivateFieldGet(this, _HyperLiquidProvider_lifecycleGeneration, "f");
|
|
5448
|
+
const cachedMarkets = __classPrivateFieldGet(this, _HyperLiquidProvider_orderCapabilitiesMarketsByDex, "f").get(dex);
|
|
5449
|
+
if (cachedMarkets &&
|
|
5450
|
+
Date.now() - cachedMarkets.freshAt <
|
|
5451
|
+
PERFORMANCE_CONFIG.OrderCapabilitiesMetaFreshnessMs) {
|
|
5452
|
+
return cachedMarkets.markets;
|
|
5453
|
+
}
|
|
5454
|
+
let refreshPromise = __classPrivateFieldGet(this, _HyperLiquidProvider_orderCapabilitiesRefreshByDex, "f").get(dex);
|
|
5455
|
+
if (!refreshPromise) {
|
|
5456
|
+
refreshPromise = (async () => {
|
|
5457
|
+
const markets = await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_fetchMarketsForDex).call(this, {
|
|
5458
|
+
dex,
|
|
5459
|
+
skipCache: true,
|
|
5460
|
+
});
|
|
5461
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_assertProviderLifecycleCurrent).call(this, lifecycleGeneration, 'Order capability metadata refresh');
|
|
5462
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_orderCapabilitiesMarketsByDex, "f").set(dex, {
|
|
5463
|
+
markets,
|
|
5464
|
+
freshAt: Date.now(),
|
|
5465
|
+
});
|
|
5466
|
+
return markets;
|
|
5467
|
+
})();
|
|
5468
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_orderCapabilitiesRefreshByDex, "f").set(dex, refreshPromise);
|
|
5469
|
+
}
|
|
5470
|
+
try {
|
|
5471
|
+
const markets = await refreshPromise;
|
|
5472
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_assertProviderLifecycleCurrent).call(this, lifecycleGeneration, 'Order capability read');
|
|
5473
|
+
return markets;
|
|
5474
|
+
}
|
|
5475
|
+
finally {
|
|
5476
|
+
if (__classPrivateFieldGet(this, _HyperLiquidProvider_orderCapabilitiesRefreshByDex, "f").get(dex) === refreshPromise) {
|
|
5477
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_orderCapabilitiesRefreshByDex, "f").delete(dex);
|
|
5478
|
+
}
|
|
5479
|
+
}
|
|
5480
|
+
}, _HyperLiquidProvider_compilePatternsSafely = function _HyperLiquidProvider_compilePatternsSafely(patterns, listName) {
|
|
4876
5481
|
const compiled = [];
|
|
4877
5482
|
for (const pattern of patterns) {
|
|
4878
5483
|
try {
|
|
@@ -4895,6 +5500,9 @@ _HyperLiquidProvider_deps = new WeakMap(), _HyperLiquidProvider_clientService =
|
|
|
4895
5500
|
* the connection is fully established before marking initialization complete.
|
|
4896
5501
|
*/
|
|
4897
5502
|
async function _HyperLiquidProvider_ensureClientsInitialized() {
|
|
5503
|
+
if (__classPrivateFieldGet(this, _HyperLiquidProvider_disconnectOperationsInFlight, "f") > 0) {
|
|
5504
|
+
throw new Error(PERPS_ERROR_CODES.PROVIDER_LIFECYCLE_STALE);
|
|
5505
|
+
}
|
|
4898
5506
|
if (__classPrivateFieldGet(this, _HyperLiquidProvider_clientsInitialized, "f")) {
|
|
4899
5507
|
return; // Already initialized
|
|
4900
5508
|
}
|
|
@@ -4902,6 +5510,9 @@ async function _HyperLiquidProvider_ensureClientsInitialized() {
|
|
|
4902
5510
|
// This prevents race conditions when multiple methods call concurrently
|
|
4903
5511
|
if (__classPrivateFieldGet(this, _HyperLiquidProvider_initializationPromise, "f")) {
|
|
4904
5512
|
await __classPrivateFieldGet(this, _HyperLiquidProvider_initializationPromise, "f");
|
|
5513
|
+
if (__classPrivateFieldGet(this, _HyperLiquidProvider_disconnectOperationsInFlight, "f") > 0) {
|
|
5514
|
+
throw new Error(PERPS_ERROR_CODES.PROVIDER_LIFECYCLE_STALE);
|
|
5515
|
+
}
|
|
4905
5516
|
return;
|
|
4906
5517
|
}
|
|
4907
5518
|
// Create and cache the initialization promise
|
|
@@ -4910,8 +5521,14 @@ async function _HyperLiquidProvider_ensureClientsInitialized() {
|
|
|
4910
5521
|
if (__classPrivateFieldGet(this, _HyperLiquidProvider_clientsInitialized, "f")) {
|
|
4911
5522
|
return;
|
|
4912
5523
|
}
|
|
5524
|
+
if (__classPrivateFieldGet(this, _HyperLiquidProvider_disconnectOperationsInFlight, "f") > 0) {
|
|
5525
|
+
throw new Error(PERPS_ERROR_CODES.PROVIDER_LIFECYCLE_STALE);
|
|
5526
|
+
}
|
|
4913
5527
|
const wallet = __classPrivateFieldGet(this, _HyperLiquidProvider_walletService, "f").createWalletAdapter();
|
|
4914
5528
|
await __classPrivateFieldGet(this, _HyperLiquidProvider_clientService, "f").initialize(wallet);
|
|
5529
|
+
if (__classPrivateFieldGet(this, _HyperLiquidProvider_disconnectOperationsInFlight, "f") > 0) {
|
|
5530
|
+
throw new Error(PERPS_ERROR_CODES.PROVIDER_LIFECYCLE_STALE);
|
|
5531
|
+
}
|
|
4915
5532
|
// Set termination callback for logging when WebSocket terminates
|
|
4916
5533
|
// Note: Do NOT restore subscriptions here - termination means connection failed permanently
|
|
4917
5534
|
__classPrivateFieldGet(this, _HyperLiquidProvider_clientService, "f").setOnTerminateCallback((error) => {
|
|
@@ -5382,7 +5999,13 @@ async function _HyperLiquidProvider_ensureReady() {
|
|
|
5382
5999
|
// Await initialization - keep the promise so subsequent calls resolve immediately
|
|
5383
6000
|
// The promise is only reset in disconnect() for clean reconnection,
|
|
5384
6001
|
// or when DEX discovery was degraded so the next caller retries.
|
|
5385
|
-
|
|
6002
|
+
try {
|
|
6003
|
+
await __classPrivateFieldGet(this, _HyperLiquidProvider_ensureReadyPromise, "f");
|
|
6004
|
+
}
|
|
6005
|
+
catch (error) {
|
|
6006
|
+
__classPrivateFieldSet(this, _HyperLiquidProvider_ensureReadyPromise, null, "f");
|
|
6007
|
+
throw error;
|
|
6008
|
+
}
|
|
5386
6009
|
if (!__classPrivateFieldGet(this, _HyperLiquidProvider_dexDiscoveryComplete, "f")) {
|
|
5387
6010
|
// DEX discovery failed transiently — reset so next call retries.
|
|
5388
6011
|
// Trading still works (main DEX mapping is populated), but HIP-3 markets
|
|
@@ -5397,72 +6020,102 @@ async function _HyperLiquidProvider_ensureReady() {
|
|
|
5397
6020
|
__classPrivateFieldSet(this, _HyperLiquidProvider_ensureReadyPromise, null, "f");
|
|
5398
6021
|
}
|
|
5399
6022
|
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('[ensureReady] Initialization complete');
|
|
5400
|
-
},
|
|
6023
|
+
}, _HyperLiquidProvider_ensureBuilderFeeSetup =
|
|
6024
|
+
/**
|
|
6025
|
+
* Approve the standard builder fee once for the current account and network.
|
|
6026
|
+
* TWAP never calls this because its native action has no builder field.
|
|
6027
|
+
* Without an approval failure code, approval remains non-blocking and the
|
|
6028
|
+
* returned context describes attribution only; it does not prove approval.
|
|
6029
|
+
*
|
|
6030
|
+
* @param approvalFailureCode - Operation-specific error to throw when
|
|
6031
|
+
* approval is unavailable or fails.
|
|
6032
|
+
* @returns The account, network, and configured builder for the action.
|
|
6033
|
+
*/
|
|
6034
|
+
async function _HyperLiquidProvider_ensureBuilderFeeSetup(approvalFailureCode) {
|
|
6035
|
+
const isTestnet = __classPrivateFieldGet(this, _HyperLiquidProvider_clientService, "f").isTestnetMode();
|
|
6036
|
+
const network = isTestnet ? 'testnet' : 'mainnet';
|
|
6037
|
+
const userAddress = await __classPrivateFieldGet(this, _HyperLiquidProvider_walletService, "f").getUserAddressWithDefault();
|
|
6038
|
+
const cacheKey = __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_getCacheKey).call(this, network, userAddress);
|
|
6039
|
+
const builderAddress = __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_getBuilderAddress).call(this, isTestnet);
|
|
6040
|
+
const context = {
|
|
6041
|
+
network,
|
|
6042
|
+
userAddress,
|
|
6043
|
+
builderAddress,
|
|
6044
|
+
};
|
|
6045
|
+
const setupKey = __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_getApprovedBuilderKey).call(this, network, userAddress, builderAddress);
|
|
6046
|
+
if (__classPrivateFieldGet(this, _HyperLiquidProvider_builderFeeCheckCache, "f").has(cacheKey)) {
|
|
6047
|
+
return context;
|
|
6048
|
+
}
|
|
6049
|
+
let pendingApproval = __classPrivateFieldGet(this, _HyperLiquidProvider_builderFeeSetupPromises, "f").get(setupKey);
|
|
6050
|
+
if (!pendingApproval) {
|
|
6051
|
+
pendingApproval = __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_ensureBuilderFeeApproval).call(this, context);
|
|
6052
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_builderFeeSetupPromises, "f").set(setupKey, pendingApproval);
|
|
6053
|
+
}
|
|
6054
|
+
try {
|
|
6055
|
+
await pendingApproval;
|
|
6056
|
+
}
|
|
6057
|
+
catch (error) {
|
|
6058
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('[ensureBuilderFeeSetup] Builder fee approval failed', error);
|
|
6059
|
+
if (approvalFailureCode) {
|
|
6060
|
+
throw new Error(approvalFailureCode);
|
|
6061
|
+
}
|
|
6062
|
+
}
|
|
6063
|
+
finally {
|
|
6064
|
+
if (__classPrivateFieldGet(this, _HyperLiquidProvider_builderFeeSetupPromises, "f").get(setupKey) === pendingApproval) {
|
|
6065
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_builderFeeSetupPromises, "f").delete(setupKey);
|
|
6066
|
+
}
|
|
6067
|
+
}
|
|
6068
|
+
if (approvalFailureCode && !__classPrivateFieldGet(this, _HyperLiquidProvider_builderFeeCheckCache, "f").has(cacheKey)) {
|
|
6069
|
+
throw new Error(approvalFailureCode);
|
|
6070
|
+
}
|
|
6071
|
+
return context;
|
|
6072
|
+
}, _HyperLiquidProvider_ensureReadyForTrading = async function _HyperLiquidProvider_ensureReadyForTrading(options) {
|
|
5401
6073
|
// First ensure basic initialization is complete
|
|
5402
6074
|
await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_ensureReady).call(this);
|
|
5403
6075
|
// dexAbstraction users were deferred during init to avoid an EIP-712 prompt
|
|
5404
6076
|
// on Perps section open. Drive the migration here, gated by its own cache so
|
|
5405
6077
|
// already-migrated or already-rejected users are not re-prompted.
|
|
5406
6078
|
await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_ensureUnifiedAccountEnabled).call(this, { allowUserSigning: true });
|
|
5407
|
-
|
|
5408
|
-
|
|
5409
|
-
|
|
5410
|
-
|
|
5411
|
-
|
|
5412
|
-
|
|
5413
|
-
|
|
5414
|
-
|
|
5415
|
-
|
|
5416
|
-
|
|
6079
|
+
if (!__classPrivateFieldGet(this, _HyperLiquidProvider_tradingSetupComplete, "f") && !__classPrivateFieldGet(this, _HyperLiquidProvider_tradingSetupPromise, "f")) {
|
|
6080
|
+
const lifecycleGeneration = __classPrivateFieldGet(this, _HyperLiquidProvider_lifecycleGeneration, "f");
|
|
6081
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('[ensureReadyForTrading] Starting shared trading setup');
|
|
6082
|
+
__classPrivateFieldSet(this, _HyperLiquidProvider_tradingSetupPromise, (async () => {
|
|
6083
|
+
// Pre-fetch spotMeta for HIP-3 operations (non-blocking if it fails)
|
|
6084
|
+
// This ensures token info (e.g. USDC token index) is available during order placement
|
|
6085
|
+
if (__classPrivateFieldGet(this, _HyperLiquidProvider_hip3Enabled, "f")) {
|
|
6086
|
+
try {
|
|
6087
|
+
await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_getCachedSpotMeta).call(this);
|
|
6088
|
+
}
|
|
6089
|
+
catch (error) {
|
|
6090
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('[ensureReadyForTrading] spotMeta pre-fetch failed, will retry when needed', error);
|
|
6091
|
+
// Don't throw - spotMeta will be fetched on-demand if needed
|
|
6092
|
+
}
|
|
5417
6093
|
}
|
|
5418
|
-
|
|
5419
|
-
|
|
5420
|
-
|
|
6094
|
+
// Set up referral code independently from builder-fee applicability.
|
|
6095
|
+
await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_ensureReferralSet).call(this);
|
|
6096
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_assertProviderLifecycleCurrent).call(this, lifecycleGeneration, 'Trading setup completion');
|
|
6097
|
+
// Only mark complete if keyring was unlocked (signing could actually happen)
|
|
6098
|
+
if (__classPrivateFieldGet(this, _HyperLiquidProvider_walletService, "f").isKeyringUnlocked()) {
|
|
6099
|
+
__classPrivateFieldSet(this, _HyperLiquidProvider_tradingSetupComplete, true, "f");
|
|
5421
6100
|
}
|
|
5422
|
-
}
|
|
5423
|
-
return;
|
|
5424
|
-
}
|
|
5425
|
-
// If trading setup is in progress, wait for it
|
|
5426
|
-
if (__classPrivateFieldGet(this, _HyperLiquidProvider_tradingSetupPromise, "f")) {
|
|
5427
|
-
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('[ensureReadyForTrading] Waiting for in-progress trading setup');
|
|
5428
|
-
await __classPrivateFieldGet(this, _HyperLiquidProvider_tradingSetupPromise, "f");
|
|
5429
|
-
return;
|
|
6101
|
+
})(), "f");
|
|
5430
6102
|
}
|
|
5431
|
-
__classPrivateFieldGet(this,
|
|
5432
|
-
|
|
5433
|
-
// Pre-fetch spotMeta for HIP-3 operations (non-blocking if it fails)
|
|
5434
|
-
// This ensures token info (e.g. USDC token index) is available during order placement
|
|
5435
|
-
if (__classPrivateFieldGet(this, _HyperLiquidProvider_hip3Enabled, "f")) {
|
|
5436
|
-
try {
|
|
5437
|
-
await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_getCachedSpotMeta).call(this);
|
|
5438
|
-
}
|
|
5439
|
-
catch (error) {
|
|
5440
|
-
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('[ensureReadyForTrading] spotMeta pre-fetch failed, will retry when needed', error);
|
|
5441
|
-
// Don't throw - spotMeta will be fetched on-demand if needed
|
|
5442
|
-
}
|
|
5443
|
-
}
|
|
5444
|
-
// Set up builder fee approval
|
|
6103
|
+
const pendingSetup = __classPrivateFieldGet(this, _HyperLiquidProvider_tradingSetupPromise, "f");
|
|
6104
|
+
if (pendingSetup) {
|
|
5445
6105
|
try {
|
|
5446
|
-
await
|
|
5447
|
-
}
|
|
5448
|
-
catch (error) {
|
|
5449
|
-
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('HyperLiquidProvider: Builder fee approval failed', error);
|
|
5450
|
-
// Don't throw - let trading continue, will fail with clear error if needed
|
|
6106
|
+
await pendingSetup;
|
|
5451
6107
|
}
|
|
5452
|
-
|
|
5453
|
-
|
|
5454
|
-
|
|
5455
|
-
|
|
5456
|
-
__classPrivateFieldSet(this, _HyperLiquidProvider_tradingSetupComplete, true, "f");
|
|
6108
|
+
finally {
|
|
6109
|
+
if (__classPrivateFieldGet(this, _HyperLiquidProvider_tradingSetupPromise, "f") === pendingSetup) {
|
|
6110
|
+
__classPrivateFieldSet(this, _HyperLiquidProvider_tradingSetupPromise, null, "f");
|
|
6111
|
+
}
|
|
5457
6112
|
}
|
|
5458
|
-
})(), "f");
|
|
5459
|
-
try {
|
|
5460
|
-
await __classPrivateFieldGet(this, _HyperLiquidProvider_tradingSetupPromise, "f");
|
|
5461
|
-
}
|
|
5462
|
-
finally {
|
|
5463
|
-
__classPrivateFieldSet(this, _HyperLiquidProvider_tradingSetupPromise, null, "f");
|
|
5464
6113
|
}
|
|
6114
|
+
const builderFeeSetupContext = options.requiresBuilderFee
|
|
6115
|
+
? await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_ensureBuilderFeeSetup).call(this, options.builderFeeApprovalFailureCode)
|
|
6116
|
+
: undefined;
|
|
5465
6117
|
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('[ensureReadyForTrading] Trading setup complete');
|
|
6118
|
+
return builderFeeSetupContext;
|
|
5466
6119
|
}, _HyperLiquidProvider_getOrFetchPrice =
|
|
5467
6120
|
/**
|
|
5468
6121
|
* Get current price for a symbol using WebSocket cache first, REST API fallback
|
|
@@ -5525,6 +6178,7 @@ async function _HyperLiquidProvider_getOrFetchPrice(params) {
|
|
|
5525
6178
|
* @returns Array of all DEX names (null for main DEX, strings for HIP-3 DEXs)
|
|
5526
6179
|
*/
|
|
5527
6180
|
async function _HyperLiquidProvider_getAllAvailableDexs() {
|
|
6181
|
+
const lifecycleGeneration = __classPrivateFieldGet(this, _HyperLiquidProvider_lifecycleGeneration, "f");
|
|
5528
6182
|
// Use unified state if available
|
|
5529
6183
|
if (__classPrivateFieldGet(this, _HyperLiquidProvider_dexDiscoveryCache, "f").state) {
|
|
5530
6184
|
const availableHip3Dexs = [];
|
|
@@ -5542,6 +6196,7 @@ async function _HyperLiquidProvider_getAllAvailableDexs() {
|
|
|
5542
6196
|
if (!allDexs || !Array.isArray(allDexs)) {
|
|
5543
6197
|
return [null]; // Fallback to main DEX only
|
|
5544
6198
|
}
|
|
6199
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_assertCacheWriteLifecycleCurrent).call(this, lifecycleGeneration, 'DEX discovery cache write');
|
|
5545
6200
|
const state = __classPrivateFieldGet(this, _HyperLiquidProvider_dexDiscoveryCache, "f").update(allDexs);
|
|
5546
6201
|
const availableHip3Dexs = [];
|
|
5547
6202
|
state.raw.forEach((dex) => {
|
|
@@ -5588,14 +6243,17 @@ async function _HyperLiquidProvider_getValidatedDexs() {
|
|
|
5588
6243
|
return __classPrivateFieldGet(this, _HyperLiquidProvider_pendingValidatedDexsPromise, "f");
|
|
5589
6244
|
}
|
|
5590
6245
|
// Create and cache the pending promise for deduplication
|
|
5591
|
-
|
|
6246
|
+
const pendingPromise = __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_fetchValidatedDexsInternal).call(this);
|
|
6247
|
+
__classPrivateFieldSet(this, _HyperLiquidProvider_pendingValidatedDexsPromise, pendingPromise, "f");
|
|
5592
6248
|
try {
|
|
5593
|
-
const result = await
|
|
6249
|
+
const result = await pendingPromise;
|
|
5594
6250
|
return result;
|
|
5595
6251
|
}
|
|
5596
6252
|
finally {
|
|
5597
|
-
//
|
|
5598
|
-
|
|
6253
|
+
// A stale request must not clear a newer lifecycle's pending request.
|
|
6254
|
+
if (__classPrivateFieldGet(this, _HyperLiquidProvider_pendingValidatedDexsPromise, "f") === pendingPromise) {
|
|
6255
|
+
__classPrivateFieldSet(this, _HyperLiquidProvider_pendingValidatedDexsPromise, null, "f");
|
|
6256
|
+
}
|
|
5599
6257
|
}
|
|
5600
6258
|
}, _HyperLiquidProvider_fetchValidatedDexsInternal =
|
|
5601
6259
|
/**
|
|
@@ -5605,6 +6263,7 @@ async function _HyperLiquidProvider_getValidatedDexs() {
|
|
|
5605
6263
|
* @returns A promise that resolves to the result.
|
|
5606
6264
|
*/
|
|
5607
6265
|
async function _HyperLiquidProvider_fetchValidatedDexsInternal() {
|
|
6266
|
+
const lifecycleGeneration = __classPrivateFieldGet(this, _HyperLiquidProvider_lifecycleGeneration, "f");
|
|
5608
6267
|
// Kill switch: HIP-3 disabled, return main DEX only
|
|
5609
6268
|
if (!__classPrivateFieldGet(this, _HyperLiquidProvider_hip3Enabled, "f")) {
|
|
5610
6269
|
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('HyperLiquidProvider: HIP-3 disabled via hip3Enabled flag');
|
|
@@ -5634,6 +6293,7 @@ async function _HyperLiquidProvider_fetchValidatedDexsInternal() {
|
|
|
5634
6293
|
return [null];
|
|
5635
6294
|
}
|
|
5636
6295
|
// Atomically update unified state (raw + validated + timestamp)
|
|
6296
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_assertCacheWriteLifecycleCurrent).call(this, lifecycleGeneration, 'DEX discovery cache write');
|
|
5637
6297
|
const state = __classPrivateFieldGet(this, _HyperLiquidProvider_dexDiscoveryCache, "f").update(allDexs);
|
|
5638
6298
|
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('HyperLiquidProvider: Available DEXs (market filtering applied at data layer)', {
|
|
5639
6299
|
count: state.validated.filter((dex) => dex !== null).length,
|
|
@@ -5652,13 +6312,12 @@ async function _HyperLiquidProvider_fetchValidatedDexsInternal() {
|
|
|
5652
6312
|
* @throws Error if API returns invalid data
|
|
5653
6313
|
*/
|
|
5654
6314
|
async function _HyperLiquidProvider_getCachedMeta(params) {
|
|
6315
|
+
const lifecycleGeneration = __classPrivateFieldGet(this, _HyperLiquidProvider_lifecycleGeneration, "f");
|
|
5655
6316
|
const { dexName, skipCache } = params;
|
|
5656
|
-
|
|
5657
|
-
const dexKey = dexName ?? '';
|
|
5658
|
-
const dexDisplayName = dexKey || 'main';
|
|
6317
|
+
const dexDisplayName = dexName ?? 'main';
|
|
5659
6318
|
// Skip cache if requested (forces fresh fetch)
|
|
5660
6319
|
if (!skipCache) {
|
|
5661
|
-
const cached = __classPrivateFieldGet(this, _HyperLiquidProvider_cachedMetaByDex, "f").get(
|
|
6320
|
+
const cached = __classPrivateFieldGet(this, _HyperLiquidProvider_cachedMetaByDex, "f").get(dexName);
|
|
5662
6321
|
if (cached) {
|
|
5663
6322
|
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('[getCachedMeta] Using cached meta response', {
|
|
5664
6323
|
dex: dexDisplayName,
|
|
@@ -5668,6 +6327,29 @@ async function _HyperLiquidProvider_getCachedMeta(params) {
|
|
|
5668
6327
|
}
|
|
5669
6328
|
}
|
|
5670
6329
|
// Cache miss or skipCache=true - fetch from API.
|
|
6330
|
+
const meta = await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_fetchMeta).call(this, dexName);
|
|
6331
|
+
const cached = await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_cacheDexMetadataFromRead).call(this, {
|
|
6332
|
+
dex: dexName,
|
|
6333
|
+
meta,
|
|
6334
|
+
lifecycleGeneration,
|
|
6335
|
+
});
|
|
6336
|
+
if (cached) {
|
|
6337
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('[getCachedMeta] Fetched and cached meta response', {
|
|
6338
|
+
dex: dexDisplayName,
|
|
6339
|
+
universeSize: meta.universe.length,
|
|
6340
|
+
skipCache,
|
|
6341
|
+
});
|
|
6342
|
+
}
|
|
6343
|
+
return meta;
|
|
6344
|
+
}, _HyperLiquidProvider_fetchMeta =
|
|
6345
|
+
/**
|
|
6346
|
+
* Fetch and validate metadata without mutating provider caches.
|
|
6347
|
+
*
|
|
6348
|
+
* @param dexName - DEX name, or null for the main DEX.
|
|
6349
|
+
* @returns Validated metadata for the requested DEX.
|
|
6350
|
+
*/
|
|
6351
|
+
async function _HyperLiquidProvider_fetchMeta(dexName) {
|
|
6352
|
+
const dexDisplayName = dexName ?? 'main';
|
|
5671
6353
|
// Bring the SDK clients up first. This is the first client touch on the
|
|
5672
6354
|
// write path — placeOrder resolves asset info before it ensures trading
|
|
5673
6355
|
// readiness — so without it a cold start or a post-disconnect action fails
|
|
@@ -5679,20 +6361,54 @@ async function _HyperLiquidProvider_getCachedMeta(params) {
|
|
|
5679
6361
|
const infoClient = __classPrivateFieldGet(this, _HyperLiquidProvider_clientService, "f").getInfoClient({ useHttp: true });
|
|
5680
6362
|
// Pass dex only for HIP-3 DEXs; omit for main DEX (empty string).
|
|
5681
6363
|
// Testnet API returns null when dex="" is explicitly sent.
|
|
5682
|
-
const meta = await infoClient.meta(
|
|
6364
|
+
const meta = await infoClient.meta(dexName ? { dex: dexName } : undefined);
|
|
5683
6365
|
// Defensive validation before caching
|
|
5684
6366
|
if (!meta?.universe || !Array.isArray(meta.universe)) {
|
|
5685
6367
|
throw new Error(`[HyperLiquidProvider] Invalid meta response for DEX ${dexDisplayName}: universe is ${meta?.universe ? 'not an array' : 'missing'}`);
|
|
5686
6368
|
}
|
|
5687
|
-
// Store raw meta response for reuse
|
|
5688
|
-
__classPrivateFieldGet(this, _HyperLiquidProvider_cachedMetaByDex, "f").set(dexKey, meta);
|
|
5689
|
-
await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_backfillAssetMapForDex).call(this, dexName, meta);
|
|
5690
|
-
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('[getCachedMeta] Fetched and cached meta response', {
|
|
5691
|
-
dex: dexDisplayName,
|
|
5692
|
-
universeSize: meta.universe.length,
|
|
5693
|
-
skipCache,
|
|
5694
|
-
});
|
|
5695
6369
|
return meta;
|
|
6370
|
+
}, _HyperLiquidProvider_cacheDexMetadata = function _HyperLiquidProvider_cacheDexMetadata(params) {
|
|
6371
|
+
const { dex, meta, assetCtxs, lifecycleGeneration } = params;
|
|
6372
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_assertCacheWriteLifecycleCurrent).call(this, lifecycleGeneration, 'DEX metadata cache write');
|
|
6373
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_cachedMetaByDex, "f").set(dex, meta);
|
|
6374
|
+
if (assetCtxs) {
|
|
6375
|
+
const subscriptionDexKey = dex ?? '';
|
|
6376
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_subscriptionService, "f").setDexMetaCache(subscriptionDexKey, meta);
|
|
6377
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_subscriptionService, "f").setDexAssetCtxsCache(subscriptionDexKey, assetCtxs);
|
|
6378
|
+
}
|
|
6379
|
+
}, _HyperLiquidProvider_cacheDexMetadataFromRead =
|
|
6380
|
+
/**
|
|
6381
|
+
* Cache metadata fetched for a read without discarding a valid response.
|
|
6382
|
+
*
|
|
6383
|
+
* @param params - Metadata and lifecycle context.
|
|
6384
|
+
* @param params.dex - DEX name, or null for the main DEX.
|
|
6385
|
+
* @param params.meta - Validated DEX metadata.
|
|
6386
|
+
* @param params.assetCtxs - Asset contexts shared with subscriptions, if any.
|
|
6387
|
+
* @param params.lifecycleGeneration - Lifecycle that produced the metadata.
|
|
6388
|
+
* @returns Whether the fetched metadata was cached.
|
|
6389
|
+
*/
|
|
6390
|
+
async function _HyperLiquidProvider_cacheDexMetadataFromRead(params) {
|
|
6391
|
+
const { dex, meta, assetCtxs, lifecycleGeneration } = params;
|
|
6392
|
+
if (!__classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_isCacheWriteLifecycleCurrent).call(this, lifecycleGeneration, 'DEX metadata read cache write')) {
|
|
6393
|
+
return false;
|
|
6394
|
+
}
|
|
6395
|
+
try {
|
|
6396
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_cacheDexMetadata).call(this, {
|
|
6397
|
+
dex,
|
|
6398
|
+
meta,
|
|
6399
|
+
assetCtxs,
|
|
6400
|
+
lifecycleGeneration,
|
|
6401
|
+
});
|
|
6402
|
+
await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_backfillAssetMapForDex).call(this, dex, meta, lifecycleGeneration);
|
|
6403
|
+
return true;
|
|
6404
|
+
}
|
|
6405
|
+
catch (error) {
|
|
6406
|
+
if (ensureError(error, 'HyperLiquidProvider.cacheDexMetadataFromRead')
|
|
6407
|
+
.message === PERPS_ERROR_CODES.PROVIDER_LIFECYCLE_STALE) {
|
|
6408
|
+
return false;
|
|
6409
|
+
}
|
|
6410
|
+
throw error;
|
|
6411
|
+
}
|
|
5696
6412
|
}, _HyperLiquidProvider_backfillAssetMapForDex =
|
|
5697
6413
|
/**
|
|
5698
6414
|
* Backfill the asset ID map for a single DEX from a fresh meta response.
|
|
@@ -5700,9 +6416,11 @@ async function _HyperLiquidProvider_getCachedMeta(params) {
|
|
|
5700
6416
|
*
|
|
5701
6417
|
* @param dex - DEX name (null for main DEX).
|
|
5702
6418
|
* @param meta - Meta response containing the DEX universe.
|
|
6419
|
+
* @param lifecycleGeneration - Lifecycle that produced the metadata.
|
|
5703
6420
|
* @returns True if the mapping was rebuilt for the DEX.
|
|
5704
6421
|
*/
|
|
5705
|
-
async function _HyperLiquidProvider_backfillAssetMapForDex(dex, meta) {
|
|
6422
|
+
async function _HyperLiquidProvider_backfillAssetMapForDex(dex, meta, lifecycleGeneration) {
|
|
6423
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_assertCacheWriteLifecycleCurrent).call(this, lifecycleGeneration, 'Asset map backfill');
|
|
5706
6424
|
if (!meta?.universe || !Array.isArray(meta.universe)) {
|
|
5707
6425
|
return false;
|
|
5708
6426
|
}
|
|
@@ -5717,6 +6435,7 @@ async function _HyperLiquidProvider_backfillAssetMapForDex(dex, meta) {
|
|
|
5717
6435
|
});
|
|
5718
6436
|
}
|
|
5719
6437
|
}
|
|
6438
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_assertCacheWriteLifecycleCurrent).call(this, lifecycleGeneration, 'Asset map backfill');
|
|
5720
6439
|
const allPerpDexs = __classPrivateFieldGet(this, _HyperLiquidProvider_dexDiscoveryCache, "f").state?.raw ?? [null];
|
|
5721
6440
|
const perpDexIndex = allPerpDexs.findIndex((entry) => {
|
|
5722
6441
|
if (dex === null) {
|
|
@@ -5753,6 +6472,7 @@ async function _HyperLiquidProvider_backfillAssetMapForDex(dex, meta) {
|
|
|
5753
6472
|
* @returns The asset ID.
|
|
5754
6473
|
*/
|
|
5755
6474
|
async function _HyperLiquidProvider_getAssetIdWithRepair(params) {
|
|
6475
|
+
const lifecycleGeneration = __classPrivateFieldGet(this, _HyperLiquidProvider_lifecycleGeneration, "f");
|
|
5756
6476
|
const { symbol, dexName } = params;
|
|
5757
6477
|
const existingAssetId = __classPrivateFieldGet(this, _HyperLiquidProvider_symbolToAssetId, "f").get(symbol);
|
|
5758
6478
|
if (existingAssetId !== undefined) {
|
|
@@ -5761,7 +6481,7 @@ async function _HyperLiquidProvider_getAssetIdWithRepair(params) {
|
|
|
5761
6481
|
const meta = params.meta ?? (await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_getCachedMeta).call(this, { dexName: dexName ?? null }));
|
|
5762
6482
|
const assetExistsInMeta = meta.universe.some((asset) => asset.name === symbol);
|
|
5763
6483
|
if (assetExistsInMeta) {
|
|
5764
|
-
await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_backfillAssetMapForDex).call(this, dexName, meta);
|
|
6484
|
+
await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_backfillAssetMapForDex).call(this, dexName, meta, lifecycleGeneration);
|
|
5765
6485
|
const repairedAssetId = __classPrivateFieldGet(this, _HyperLiquidProvider_symbolToAssetId, "f").get(symbol);
|
|
5766
6486
|
if (repairedAssetId !== undefined) {
|
|
5767
6487
|
return repairedAssetId;
|
|
@@ -5792,13 +6512,16 @@ async function _HyperLiquidProvider_getCachedSpotMeta() {
|
|
|
5792
6512
|
});
|
|
5793
6513
|
return __classPrivateFieldGet(this, _HyperLiquidProvider_cachedSpotMeta, "f");
|
|
5794
6514
|
}
|
|
6515
|
+
const lifecycleGeneration = __classPrivateFieldGet(this, _HyperLiquidProvider_lifecycleGeneration, "f");
|
|
5795
6516
|
const infoClient = __classPrivateFieldGet(this, _HyperLiquidProvider_clientService, "f").getInfoClient();
|
|
5796
6517
|
const spotMeta = await infoClient.spotMeta();
|
|
5797
|
-
|
|
5798
|
-
|
|
5799
|
-
|
|
5800
|
-
|
|
5801
|
-
|
|
6518
|
+
if (__classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_isCacheWriteLifecycleCurrent).call(this, lifecycleGeneration, 'Spot metadata cache write')) {
|
|
6519
|
+
__classPrivateFieldSet(this, _HyperLiquidProvider_cachedSpotMeta, spotMeta, "f");
|
|
6520
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('[getCachedSpotMeta] Fetched and cached spotMeta', {
|
|
6521
|
+
tokensCount: spotMeta.tokens.length,
|
|
6522
|
+
universeCount: spotMeta.universe.length,
|
|
6523
|
+
});
|
|
6524
|
+
}
|
|
5802
6525
|
return spotMeta;
|
|
5803
6526
|
}, _HyperLiquidProvider_getCachedPerpDexs =
|
|
5804
6527
|
/**
|
|
@@ -5808,6 +6531,7 @@ async function _HyperLiquidProvider_getCachedSpotMeta() {
|
|
|
5808
6531
|
* @returns Array of ExtendedPerpDex objects (null entries represent main DEX)
|
|
5809
6532
|
*/
|
|
5810
6533
|
async function _HyperLiquidProvider_getCachedPerpDexs() {
|
|
6534
|
+
const lifecycleGeneration = __classPrivateFieldGet(this, _HyperLiquidProvider_lifecycleGeneration, "f");
|
|
5811
6535
|
const now = Date.now();
|
|
5812
6536
|
// Return cached data if still valid (uses unified state timestamp for TTL)
|
|
5813
6537
|
if (__classPrivateFieldGet(this, _HyperLiquidProvider_dexDiscoveryCache, "f").state &&
|
|
@@ -5825,17 +6549,19 @@ async function _HyperLiquidProvider_getCachedPerpDexs() {
|
|
|
5825
6549
|
await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_ensureClientsInitialized).call(this);
|
|
5826
6550
|
const infoClient = __classPrivateFieldGet(this, _HyperLiquidProvider_clientService, "f").getInfoClient();
|
|
5827
6551
|
const perpDexs = (await infoClient.perpDexs());
|
|
5828
|
-
|
|
5829
|
-
|
|
5830
|
-
|
|
5831
|
-
|
|
5832
|
-
|
|
5833
|
-
|
|
5834
|
-
|
|
5835
|
-
|
|
5836
|
-
|
|
5837
|
-
|
|
5838
|
-
|
|
6552
|
+
if (__classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_isCacheWriteLifecycleCurrent).call(this, lifecycleGeneration, 'Perp DEX cache write')) {
|
|
6553
|
+
// Atomically update unified state (raw + validated + timestamp)
|
|
6554
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_dexDiscoveryCache, "f").update(perpDexs);
|
|
6555
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('[getCachedPerpDexs] Fetched and cached perpDexs data', {
|
|
6556
|
+
count: perpDexs.length,
|
|
6557
|
+
dexes: perpDexs
|
|
6558
|
+
.filter((dex) => dex !== null)
|
|
6559
|
+
.map((dex) => ({
|
|
6560
|
+
name: dex.name,
|
|
6561
|
+
deployerFeeScale: dex.deployerFeeScale,
|
|
6562
|
+
})),
|
|
6563
|
+
});
|
|
6564
|
+
}
|
|
5839
6565
|
return perpDexs;
|
|
5840
6566
|
}, _HyperLiquidProvider_calculateHip3FeeMultiplier =
|
|
5841
6567
|
/**
|
|
@@ -6007,6 +6733,7 @@ async function _HyperLiquidProvider_isUsdcCollateralDex(dexName) {
|
|
|
6007
6733
|
* the asset ID lookup succeeds and the order routes to the correct DEX.
|
|
6008
6734
|
*/
|
|
6009
6735
|
async function _HyperLiquidProvider_buildAssetMapping() {
|
|
6736
|
+
const lifecycleGeneration = __classPrivateFieldGet(this, _HyperLiquidProvider_lifecycleGeneration, "f");
|
|
6010
6737
|
// Get feature-flag-validated DEXs to map (respects hip3Enabled and enabledDexs)
|
|
6011
6738
|
let dexsToMap;
|
|
6012
6739
|
try {
|
|
@@ -6037,14 +6764,14 @@ async function _HyperLiquidProvider_buildAssetMapping() {
|
|
|
6037
6764
|
// Extract HIP-3 DEX names (filter out null which represents main DEX)
|
|
6038
6765
|
const enabledDexs = dexsToMap.filter((dex) => dex !== null);
|
|
6039
6766
|
await __classPrivateFieldGet(this, _HyperLiquidProvider_subscriptionService, "f").updateFeatureFlags(__classPrivateFieldGet(this, _HyperLiquidProvider_hip3Enabled, "f"), enabledDexs, __classPrivateFieldGet(this, _HyperLiquidProvider_allowlistMarkets, "f"), __classPrivateFieldGet(this, _HyperLiquidProvider_blocklistMarkets, "f"));
|
|
6767
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_assertCacheWriteLifecycleCurrent).call(this, lifecycleGeneration, 'Asset mapping rebuild');
|
|
6040
6768
|
// Fetch metadata for each DEX in parallel using metaAndAssetCtxs
|
|
6041
6769
|
// Optimization: Check cache first - getMarketDataWithPrices may have already fetched
|
|
6042
6770
|
// If not cached, fetch via metaAndAssetCtxs and populate cache for other methods
|
|
6043
6771
|
const infoClient = __classPrivateFieldGet(this, _HyperLiquidProvider_clientService, "f").getInfoClient();
|
|
6044
6772
|
const allMetas = await Promise.allSettled(dexsToMap.map((dex) => {
|
|
6045
|
-
const dexKey = dex ?? '';
|
|
6046
6773
|
// Check if already cached (e.g., by getMarketDataWithPrices running in parallel)
|
|
6047
|
-
const cachedMeta = __classPrivateFieldGet(this, _HyperLiquidProvider_cachedMetaByDex, "f").get(
|
|
6774
|
+
const cachedMeta = __classPrivateFieldGet(this, _HyperLiquidProvider_cachedMetaByDex, "f").get(dex);
|
|
6048
6775
|
if (cachedMeta) {
|
|
6049
6776
|
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log(`[buildAssetMapping] Using cached meta for ${dex ?? 'main'}`, { universeSize: cachedMeta.universe.length });
|
|
6050
6777
|
return Promise.resolve({
|
|
@@ -6062,11 +6789,12 @@ async function _HyperLiquidProvider_buildAssetMapping() {
|
|
|
6062
6789
|
const assetCtxs = result?.[1] || [];
|
|
6063
6790
|
// Cache meta for later use by getCachedMeta
|
|
6064
6791
|
if (meta?.universe) {
|
|
6065
|
-
__classPrivateFieldGet(this,
|
|
6066
|
-
|
|
6067
|
-
|
|
6068
|
-
|
|
6069
|
-
|
|
6792
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_cacheDexMetadata).call(this, {
|
|
6793
|
+
dex,
|
|
6794
|
+
meta,
|
|
6795
|
+
assetCtxs,
|
|
6796
|
+
lifecycleGeneration,
|
|
6797
|
+
});
|
|
6070
6798
|
}
|
|
6071
6799
|
return { dex, meta, success: true };
|
|
6072
6800
|
})
|
|
@@ -6075,6 +6803,7 @@ async function _HyperLiquidProvider_buildAssetMapping() {
|
|
|
6075
6803
|
return { dex, meta: null, success: false };
|
|
6076
6804
|
});
|
|
6077
6805
|
}));
|
|
6806
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_assertCacheWriteLifecycleCurrent).call(this, lifecycleGeneration, 'Asset mapping rebuild');
|
|
6078
6807
|
// Build mapping with DEX prefixes for HIP-3 DEXs using the utility function
|
|
6079
6808
|
__classPrivateFieldGet(this, _HyperLiquidProvider_symbolToAssetId, "f").clear();
|
|
6080
6809
|
let dexDiscoveryComplete = __classPrivateFieldGet(this, _HyperLiquidProvider_dexDiscoveryCache, "f").state !== null;
|
|
@@ -6246,12 +6975,16 @@ async function _HyperLiquidProvider_checkBuilderFeeApproval(builder, userAddress
|
|
|
6246
6975
|
* This prevents repeated signing requests for hardware wallets.
|
|
6247
6976
|
*
|
|
6248
6977
|
* Note: This is network-specific - testnet and mainnet have separate builder fee states
|
|
6978
|
+
*
|
|
6979
|
+
* @param params - Builder fee setup context.
|
|
6980
|
+
* @param params.network - HyperLiquid network for the approval.
|
|
6981
|
+
* @param params.userAddress - Account that owns the approval.
|
|
6982
|
+
* @param params.builderAddress - Builder address being approved.
|
|
6249
6983
|
*/
|
|
6250
|
-
async function _HyperLiquidProvider_ensureBuilderFeeApproval() {
|
|
6251
|
-
const
|
|
6252
|
-
const network
|
|
6253
|
-
|
|
6254
|
-
const userAddress = await __classPrivateFieldGet(this, _HyperLiquidProvider_walletService, "f").getUserAddressWithDefault();
|
|
6984
|
+
async function _HyperLiquidProvider_ensureBuilderFeeApproval(params) {
|
|
6985
|
+
const lifecycleGeneration = __classPrivateFieldGet(this, _HyperLiquidProvider_lifecycleGeneration, "f");
|
|
6986
|
+
const { network, userAddress, builderAddress } = params;
|
|
6987
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_assertProviderLifecycleCurrent).call(this, lifecycleGeneration, 'Builder fee approval');
|
|
6255
6988
|
const cacheKey = __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_getCacheKey).call(this, network, userAddress);
|
|
6256
6989
|
// Check GLOBAL cache first to avoid repeated signing requests across reconnections
|
|
6257
6990
|
// This is CRITICAL for hardware wallets to prevent repeated signing prompts
|
|
@@ -6268,6 +7001,11 @@ async function _HyperLiquidProvider_ensureBuilderFeeApproval() {
|
|
|
6268
7001
|
if (inFlightPromise) {
|
|
6269
7002
|
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('[ensureBuilderFeeApproval] Global in-flight, waiting...', { network });
|
|
6270
7003
|
await inFlightPromise;
|
|
7004
|
+
const completedApproval = PerpsSigningCache.getBuilderFee(network, userAddress);
|
|
7005
|
+
if (completedApproval?.attempted && completedApproval.success) {
|
|
7006
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_builderFeeCheckCache, "f").set(cacheKey, true);
|
|
7007
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_approvedBuilderAddresses, "f").add(__classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_getApprovedBuilderKey).call(this, network, userAddress, builderAddress));
|
|
7008
|
+
}
|
|
6271
7009
|
return;
|
|
6272
7010
|
}
|
|
6273
7011
|
// Set global in-flight lock
|
|
@@ -6277,10 +7015,13 @@ async function _HyperLiquidProvider_ensureBuilderFeeApproval() {
|
|
|
6277
7015
|
const recheckCache = PerpsSigningCache.getBuilderFee(network, userAddress);
|
|
6278
7016
|
if (recheckCache?.attempted && recheckCache?.success) {
|
|
6279
7017
|
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('[ensureBuilderFeeApproval] Completed by another provider', { network });
|
|
7018
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_builderFeeCheckCache, "f").set(cacheKey, true);
|
|
7019
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_approvedBuilderAddresses, "f").add(__classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_getApprovedBuilderKey).call(this, network, userAddress, builderAddress));
|
|
6280
7020
|
completeInFlight();
|
|
6281
7021
|
return;
|
|
6282
7022
|
}
|
|
6283
7023
|
const { isApproved, requiredDecimal } = await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_checkBuilderFeeStatus).call(this, builderAddress, userAddress);
|
|
7024
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_assertProviderLifecycleCurrent).call(this, lifecycleGeneration, 'Builder fee approval');
|
|
6284
7025
|
if (isApproved) {
|
|
6285
7026
|
// User already has approval on-chain
|
|
6286
7027
|
PerpsSigningCache.setBuilderFee(network, userAddress, {
|
|
@@ -6305,11 +7046,15 @@ async function _HyperLiquidProvider_ensureBuilderFeeApproval() {
|
|
|
6305
7046
|
afterApprovalDecimal < requiredDecimal) {
|
|
6306
7047
|
throw new Error('[HyperLiquidProvider] Builder fee approval verification failed');
|
|
6307
7048
|
}
|
|
6308
|
-
//
|
|
7049
|
+
// The approval is already confirmed on-chain. Preserve that fact for
|
|
7050
|
+
// the next provider lifecycle even if this instance became stale while
|
|
7051
|
+
// the approval request was in flight.
|
|
6309
7052
|
PerpsSigningCache.setBuilderFee(network, userAddress, {
|
|
6310
7053
|
attempted: true,
|
|
6311
7054
|
success: true,
|
|
6312
7055
|
});
|
|
7056
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_assertProviderLifecycleCurrent).call(this, lifecycleGeneration, 'Builder fee approval');
|
|
7057
|
+
// Cache success in the current instance after the lifecycle check.
|
|
6313
7058
|
__classPrivateFieldGet(this, _HyperLiquidProvider_builderFeeCheckCache, "f").set(cacheKey, true);
|
|
6314
7059
|
__classPrivateFieldGet(this, _HyperLiquidProvider_approvedBuilderAddresses, "f").add(__classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_getApprovedBuilderKey).call(this, network, userAddress, builderAddress));
|
|
6315
7060
|
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('[ensureBuilderFeeApproval] Approval successful', {
|
|
@@ -6320,6 +7065,11 @@ async function _HyperLiquidProvider_ensureBuilderFeeApproval() {
|
|
|
6320
7065
|
completeInFlight();
|
|
6321
7066
|
}
|
|
6322
7067
|
catch (error) {
|
|
7068
|
+
if (ensureError(error, 'HyperLiquidProvider.ensureBuilderFeeApproval')
|
|
7069
|
+
.message === PERPS_ERROR_CODES.PROVIDER_LIFECYCLE_STALE) {
|
|
7070
|
+
completeInFlight();
|
|
7071
|
+
return;
|
|
7072
|
+
}
|
|
6323
7073
|
// HyperLiquid wraps wallet signing failures and preserves KEYRING_LOCKED
|
|
6324
7074
|
// in `cause`, so classify the full chain and leave retry caches empty.
|
|
6325
7075
|
if (isKeyringLockedError(error)) {
|
|
@@ -6657,6 +7407,7 @@ async function _HyperLiquidProvider_calculateHip3RequiredMargin(params) {
|
|
|
6657
7407
|
* @param params.transferInfo - The transfer information.
|
|
6658
7408
|
* @param params.transferInfo.amount - The amount value.
|
|
6659
7409
|
* @param params.transferInfo.sourceDex - The source DEX for the transfer.
|
|
7410
|
+
* @returns Whether the balance check and any required transfer succeeded.
|
|
6660
7411
|
* @private
|
|
6661
7412
|
*/
|
|
6662
7413
|
async function _HyperLiquidProvider_handleHip3PostOrderRebalance(params) {
|
|
@@ -6685,16 +7436,20 @@ async function _HyperLiquidProvider_handleHip3PostOrderRebalance(params) {
|
|
|
6685
7436
|
excessAmount: excessAmount.toFixed(2),
|
|
6686
7437
|
destinationDex: transferInfo.sourceDex,
|
|
6687
7438
|
});
|
|
6688
|
-
await this.transferBetweenDexs({
|
|
7439
|
+
const transferResult = await this.transferBetweenDexs({
|
|
6689
7440
|
sourceDex: dexName,
|
|
6690
7441
|
destinationDex: transferInfo.sourceDex,
|
|
6691
7442
|
amount: excessAmount.toFixed(USDC_DECIMALS),
|
|
6692
7443
|
});
|
|
7444
|
+
if (!transferResult.success) {
|
|
7445
|
+
throw new Error(transferResult.error ?? PERPS_ERROR_CODES.TRANSFER_FAILED);
|
|
7446
|
+
}
|
|
6693
7447
|
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('✅ HyperLiquidProvider: Auto-rebalance completed', {
|
|
6694
7448
|
transferredBack: excessAmount.toFixed(2),
|
|
6695
7449
|
from: dexName,
|
|
6696
7450
|
to: transferInfo.sourceDex,
|
|
6697
7451
|
});
|
|
7452
|
+
return true;
|
|
6698
7453
|
}
|
|
6699
7454
|
catch (rebalanceError) {
|
|
6700
7455
|
// Don't fail the order if rebalance fails (order already succeeded)
|
|
@@ -6703,6 +7458,7 @@ async function _HyperLiquidProvider_handleHip3PostOrderRebalance(params) {
|
|
|
6703
7458
|
excessAmount: excessAmount.toFixed(2),
|
|
6704
7459
|
note: 'Auto-rebalance failed - funds remain on HIP-3 DEX',
|
|
6705
7460
|
}));
|
|
7461
|
+
return false;
|
|
6706
7462
|
}
|
|
6707
7463
|
}
|
|
6708
7464
|
else {
|
|
@@ -6711,6 +7467,7 @@ async function _HyperLiquidProvider_handleHip3PostOrderRebalance(params) {
|
|
|
6711
7467
|
threshold: minimumTransferThreshold.toFixed(2),
|
|
6712
7468
|
note: 'Excess below minimum transfer threshold',
|
|
6713
7469
|
});
|
|
7470
|
+
return true;
|
|
6714
7471
|
}
|
|
6715
7472
|
}
|
|
6716
7473
|
catch (balanceCheckError) {
|
|
@@ -6719,6 +7476,7 @@ async function _HyperLiquidProvider_handleHip3PostOrderRebalance(params) {
|
|
|
6719
7476
|
dex: dexName,
|
|
6720
7477
|
note: 'Failed to verify post-order balance for auto-rebalance',
|
|
6721
7478
|
}));
|
|
7479
|
+
return false;
|
|
6722
7480
|
}
|
|
6723
7481
|
}, _HyperLiquidProvider_handleHip3OrderRollback =
|
|
6724
7482
|
/**
|
|
@@ -6902,7 +7660,10 @@ async function _HyperLiquidProvider_handleHip3PreOrder(params) {
|
|
|
6902
7660
|
async function _HyperLiquidProvider_submitOrderWithRollback(params) {
|
|
6903
7661
|
const { orders, grouping, isHip3Order, dexName, transferInfo, symbol } = params;
|
|
6904
7662
|
const exchangeClient = __classPrivateFieldGet(this, _HyperLiquidProvider_clientService, "f").getExchangeClient();
|
|
6905
|
-
const builder =
|
|
7663
|
+
const builder = params.chargesMetamaskBuilderFee
|
|
7664
|
+
? await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_getBuilderOrderContext).call(this, params.builderFeeSetupContext ??
|
|
7665
|
+
(await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_ensureBuilderFeeSetup).call(this)))
|
|
7666
|
+
: undefined;
|
|
6906
7667
|
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('Submitting order via asset ID routing', {
|
|
6907
7668
|
symbol,
|
|
6908
7669
|
assetId: orders[0].a,
|
|
@@ -6915,7 +7676,7 @@ async function _HyperLiquidProvider_submitOrderWithRollback(params) {
|
|
|
6915
7676
|
const result = await exchangeClient.order({
|
|
6916
7677
|
orders,
|
|
6917
7678
|
grouping,
|
|
6918
|
-
builder,
|
|
7679
|
+
...(builder && { builder }),
|
|
6919
7680
|
});
|
|
6920
7681
|
if (result.status !== 'ok') {
|
|
6921
7682
|
throw new Error(`Order failed: ${JSON.stringify(result)}`);
|
|
@@ -6990,18 +7751,22 @@ async function _HyperLiquidProvider_handleOrderError(params) {
|
|
|
6990
7751
|
async function _HyperLiquidProvider_placeStrategyOrder(params, orderType) {
|
|
6991
7752
|
// Captured before the shared preamble, not after it: that preamble awaits
|
|
6992
7753
|
// asset info, validation, trading readiness and a leverage update, and a
|
|
6993
|
-
// disconnect during any of them has to be seen
|
|
6994
|
-
|
|
7754
|
+
// disconnect during any of them has to be seen before or after submission.
|
|
7755
|
+
const generation = __classPrivateFieldGet(this, _HyperLiquidProvider_strategyGeneration, "f");
|
|
6995
7756
|
if (orderType !== 'chase') {
|
|
6996
7757
|
const context = await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_prepareStrategyPlacement).call(this, params);
|
|
6997
|
-
return
|
|
6998
|
-
|
|
6999
|
-
|
|
7758
|
+
return await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_submitPreparedStrategy).call(this, params, context, async () => {
|
|
7759
|
+
if (generation !== __classPrivateFieldGet(this, _HyperLiquidProvider_strategyGeneration, "f")) {
|
|
7760
|
+
throw new Error(PERPS_ERROR_CODES.PROVIDER_LIFECYCLE_STALE);
|
|
7761
|
+
}
|
|
7762
|
+
return orderType === 'twap'
|
|
7763
|
+
? await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_placeTwapOrder).call(this, params, context, generation)
|
|
7764
|
+
: await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_placeScaleOrder).call(this, params, context, generation);
|
|
7765
|
+
});
|
|
7000
7766
|
}
|
|
7001
7767
|
if (__classPrivateFieldGet(this, _HyperLiquidProvider_chasePlacementBlockers, "f") > 0) {
|
|
7002
7768
|
throw new Error(PERPS_ERROR_CODES.ORDER_CHASE_ABANDONED);
|
|
7003
7769
|
}
|
|
7004
|
-
const generation = __classPrivateFieldGet(this, _HyperLiquidProvider_chaseGeneration, "f");
|
|
7005
7770
|
// The chase slot is claimed before the preamble, not after it: the preamble
|
|
7006
7771
|
// completes the signing setup and can change the asset's leverage, and a
|
|
7007
7772
|
// request that is going to be refused for exceeding the venue's cap must
|
|
@@ -7023,7 +7788,7 @@ async function _HyperLiquidProvider_placeStrategyOrder(params, orderType) {
|
|
|
7023
7788
|
try {
|
|
7024
7789
|
await __classPrivateFieldGet(this, _HyperLiquidProvider_chaseTickQueue, "f");
|
|
7025
7790
|
const context = await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_prepareStrategyPlacement).call(this, params);
|
|
7026
|
-
return await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_startChaseSession).call(this, params, context, generation);
|
|
7791
|
+
return await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_submitPreparedStrategy).call(this, params, context, async () => await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_startChaseSession).call(this, params, context, generation));
|
|
7027
7792
|
}
|
|
7028
7793
|
finally {
|
|
7029
7794
|
settlePlacement?.();
|
|
@@ -7033,6 +7798,74 @@ async function _HyperLiquidProvider_placeStrategyOrder(params, orderType) {
|
|
|
7033
7798
|
__classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_resumeChaseTicksAfterPlacement).call(this);
|
|
7034
7799
|
}
|
|
7035
7800
|
}
|
|
7801
|
+
}, _HyperLiquidProvider_submitPreparedStrategy =
|
|
7802
|
+
/**
|
|
7803
|
+
* Submit a prepared strategy and settle any manual HIP-3 collateral move.
|
|
7804
|
+
*
|
|
7805
|
+
* Scale and Chase leave regular orders or fills on the venue, so the normal
|
|
7806
|
+
* HIP-3 post-order balance check can return any unreserved excess. Native
|
|
7807
|
+
* TWAP executes future slices, so its transferred collateral stays on the
|
|
7808
|
+
* routed DEX until cancellation confirms the schedule has stopped.
|
|
7809
|
+
*
|
|
7810
|
+
* @param params - Strategy order parameters.
|
|
7811
|
+
* @param context - Prepared placement and HIP-3 transfer context.
|
|
7812
|
+
* @param submit - Venue submission.
|
|
7813
|
+
* @returns The strategy placement result.
|
|
7814
|
+
*/
|
|
7815
|
+
async function _HyperLiquidProvider_submitPreparedStrategy(params, context, submit) {
|
|
7816
|
+
const { dexName, network, transferInfo, userAddress } = context;
|
|
7817
|
+
let result;
|
|
7818
|
+
try {
|
|
7819
|
+
result = await submit();
|
|
7820
|
+
}
|
|
7821
|
+
catch (error) {
|
|
7822
|
+
if (dexName && transferInfo) {
|
|
7823
|
+
await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_handleHip3OrderRollback).call(this, { dexName, transferInfo });
|
|
7824
|
+
}
|
|
7825
|
+
throw error;
|
|
7826
|
+
}
|
|
7827
|
+
const hasVenueExposure = result.success === true ||
|
|
7828
|
+
result.orderId !== undefined ||
|
|
7829
|
+
(result.childOrderIds?.length ?? 0) > 0;
|
|
7830
|
+
if (dexName && transferInfo && !hasVenueExposure) {
|
|
7831
|
+
await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_handleHip3OrderRollback).call(this, { dexName, transferInfo });
|
|
7832
|
+
return result;
|
|
7833
|
+
}
|
|
7834
|
+
if (params.orderType === 'twap' && result.orderId) {
|
|
7835
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_trackedTwapOrders, "f").set(getTwapOrderScopeKey({
|
|
7836
|
+
network,
|
|
7837
|
+
userAddress,
|
|
7838
|
+
orderId: result.orderId,
|
|
7839
|
+
}), {
|
|
7840
|
+
symbol: params.symbol,
|
|
7841
|
+
...(dexName && transferInfo
|
|
7842
|
+
? { hip3Transfer: { dexName, transferInfo } }
|
|
7843
|
+
: {}),
|
|
7844
|
+
});
|
|
7845
|
+
return result;
|
|
7846
|
+
}
|
|
7847
|
+
if (params.orderType === 'chase' &&
|
|
7848
|
+
result.success &&
|
|
7849
|
+
result.orderId &&
|
|
7850
|
+
dexName &&
|
|
7851
|
+
transferInfo) {
|
|
7852
|
+
const session = __classPrivateFieldGet(this, _HyperLiquidProvider_chaseSessions, "f").get(result.orderId);
|
|
7853
|
+
if (session) {
|
|
7854
|
+
session.hip3Transfer = { dexName, transferInfo };
|
|
7855
|
+
return result;
|
|
7856
|
+
}
|
|
7857
|
+
}
|
|
7858
|
+
if (params.orderType === 'chase' && hasVenueExposure) {
|
|
7859
|
+
// A stale placement can fail after its child rests and the best-effort
|
|
7860
|
+
// retraction is refused. With no session left to own that child, keep
|
|
7861
|
+
// manual HIP-3 collateral on its DEX until the caller cancels the
|
|
7862
|
+
// reported exchange ID.
|
|
7863
|
+
return result;
|
|
7864
|
+
}
|
|
7865
|
+
if (dexName && transferInfo) {
|
|
7866
|
+
await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_handleHip3PostOrderRebalance).call(this, { dexName, transferInfo });
|
|
7867
|
+
}
|
|
7868
|
+
return result;
|
|
7036
7869
|
}, _HyperLiquidProvider_prepareStrategyPlacement =
|
|
7037
7870
|
/**
|
|
7038
7871
|
* Run the shared preamble every strategy placement needs.
|
|
@@ -7046,13 +7879,6 @@ async function _HyperLiquidProvider_placeStrategyOrder(params, orderType) {
|
|
|
7046
7879
|
*/
|
|
7047
7880
|
async function _HyperLiquidProvider_prepareStrategyPlacement(params) {
|
|
7048
7881
|
const { dex: dexName } = parseAssetName(params.symbol);
|
|
7049
|
-
// A HIP-3 order is preceded by a margin transfer onto the builder DEX and
|
|
7050
|
-
// followed by a rebalance, both of which are wired into the single-order
|
|
7051
|
-
// submit path and its rollback. Rather than half-support that here, a
|
|
7052
|
-
// strategy placement on a HIP-3 market is refused outright.
|
|
7053
|
-
if (dexName !== null) {
|
|
7054
|
-
throw new Error(PERPS_ERROR_CODES.ORDER_STRATEGY_MARKET_UNSUPPORTED);
|
|
7055
|
-
}
|
|
7056
7882
|
const { assetInfo, currentPrice, meta } = await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_getAssetInfo).call(this, {
|
|
7057
7883
|
symbol: params.symbol,
|
|
7058
7884
|
dexName,
|
|
@@ -7113,7 +7939,10 @@ async function _HyperLiquidProvider_prepareStrategyPlacement(params) {
|
|
|
7113
7939
|
}
|
|
7114
7940
|
// Kept after validation so an invalid strategy order never triggers the
|
|
7115
7941
|
// signature prompts in trading setup — same ordering as `placeOrder`.
|
|
7116
|
-
|
|
7942
|
+
const { chargesMetamaskBuilderFee } = __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_resolveOrderFeePolicy).call(this, params);
|
|
7943
|
+
const builderFeeSetupContext = chargesMetamaskBuilderFee
|
|
7944
|
+
? await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_ensureReadyForTrading).call(this, { requiresBuilderFee: true })
|
|
7945
|
+
: await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_ensureReadyForTrading).call(this, { requiresBuilderFee: false });
|
|
7117
7946
|
const assetId = await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_getAssetIdWithRepair).call(this, {
|
|
7118
7947
|
symbol: params.symbol,
|
|
7119
7948
|
dexName,
|
|
@@ -7124,12 +7953,38 @@ async function _HyperLiquidProvider_prepareStrategyPlacement(params) {
|
|
|
7124
7953
|
assetId,
|
|
7125
7954
|
leverage: params.leverage,
|
|
7126
7955
|
});
|
|
7956
|
+
const builder = builderFeeSetupContext
|
|
7957
|
+
? await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_getBuilderOrderContext).call(this, builderFeeSetupContext)
|
|
7958
|
+
: undefined;
|
|
7959
|
+
const network = __classPrivateFieldGet(this, _HyperLiquidProvider_clientService, "f").isTestnetMode() ? 'testnet' : 'mainnet';
|
|
7960
|
+
const userAddress = builderFeeSetupContext?.userAddress ??
|
|
7961
|
+
(await __classPrivateFieldGet(this, _HyperLiquidProvider_walletService, "f").getUserAddressWithDefault());
|
|
7962
|
+
let transferInfo = null;
|
|
7963
|
+
if (dexName) {
|
|
7964
|
+
const orderPrice = ladder === undefined
|
|
7965
|
+
? effectivePrice
|
|
7966
|
+
: Math.max(...ladder.prices.map(Number.parseFloat));
|
|
7967
|
+
const hip3Result = await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_handleHip3PreOrder).call(this, {
|
|
7968
|
+
dexName,
|
|
7969
|
+
symbol: params.symbol,
|
|
7970
|
+
orderPrice,
|
|
7971
|
+
positionSize: parseFloat(formattedSize),
|
|
7972
|
+
leverage: params.leverage ?? assetInfo.maxLeverage ?? 1,
|
|
7973
|
+
isBuy: params.isBuy,
|
|
7974
|
+
maxLeverage: assetInfo.maxLeverage,
|
|
7975
|
+
});
|
|
7976
|
+
transferInfo = hip3Result.transferInfo;
|
|
7977
|
+
}
|
|
7127
7978
|
return {
|
|
7128
7979
|
assetId,
|
|
7129
7980
|
szDecimals: assetInfo.szDecimals,
|
|
7130
|
-
finalPositionSize,
|
|
7131
7981
|
formattedSize,
|
|
7132
7982
|
ladder,
|
|
7983
|
+
builder,
|
|
7984
|
+
dexName,
|
|
7985
|
+
network,
|
|
7986
|
+
transferInfo,
|
|
7987
|
+
userAddress,
|
|
7133
7988
|
};
|
|
7134
7989
|
}, _HyperLiquidProvider_getMinimumOrderSize = function _HyperLiquidProvider_getMinimumOrderSize() {
|
|
7135
7990
|
return __classPrivateFieldGet(this, _HyperLiquidProvider_clientService, "f").isTestnetMode()
|
|
@@ -7137,11 +7992,17 @@ async function _HyperLiquidProvider_prepareStrategyPlacement(params) {
|
|
|
7137
7992
|
: TRADING_DEFAULTS.amount.mainnet;
|
|
7138
7993
|
}, _HyperLiquidProvider_buildScaleLadder = function _HyperLiquidProvider_buildScaleLadder(options) {
|
|
7139
7994
|
const { params, szDecimals, finalPositionSize } = options;
|
|
7140
|
-
const
|
|
7995
|
+
const { scaleMinPrice, scaleMaxPrice, scaleNumOrders } = params;
|
|
7996
|
+
if (scaleMinPrice === undefined || scaleMaxPrice === undefined) {
|
|
7997
|
+
throw new Error(PERPS_ERROR_CODES.ORDER_SCALE_RANGE_REQUIRED);
|
|
7998
|
+
}
|
|
7999
|
+
if (scaleNumOrders === undefined) {
|
|
8000
|
+
throw new Error(PERPS_ERROR_CODES.ORDER_SCALE_COUNT_INVALID);
|
|
8001
|
+
}
|
|
7141
8002
|
const prices = computeScalePriceLadder({
|
|
7142
|
-
minPrice: parseFloat(
|
|
7143
|
-
maxPrice: parseFloat(
|
|
7144
|
-
count,
|
|
8003
|
+
minPrice: parseFloat(scaleMinPrice),
|
|
8004
|
+
maxPrice: parseFloat(scaleMaxPrice),
|
|
8005
|
+
count: scaleNumOrders,
|
|
7145
8006
|
}).map((price) => formatHyperLiquidPrice({ price, szDecimals }));
|
|
7146
8007
|
if (new Set(prices).size !== prices.length) {
|
|
7147
8008
|
throw new Error(PERPS_ERROR_CODES.ORDER_SCALE_RANGE_INVALID);
|
|
@@ -7150,7 +8011,7 @@ async function _HyperLiquidProvider_prepareStrategyPlacement(params) {
|
|
|
7150
8011
|
// which a skew weighted far enough from even can do on its own.
|
|
7151
8012
|
const sizes = splitScaleSizes({
|
|
7152
8013
|
totalSize: finalPositionSize,
|
|
7153
|
-
count,
|
|
8014
|
+
count: scaleNumOrders,
|
|
7154
8015
|
szDecimals,
|
|
7155
8016
|
skew: params.scaleSkew,
|
|
7156
8017
|
});
|
|
@@ -7170,16 +8031,26 @@ async function _HyperLiquidProvider_prepareStrategyPlacement(params) {
|
|
|
7170
8031
|
*
|
|
7171
8032
|
* @param params - Order parameters.
|
|
7172
8033
|
* @param context - Prepared asset and sizing context.
|
|
8034
|
+
* @param generation - Teardown generation captured before preparation.
|
|
7173
8035
|
* @returns A promise that resolves to the result.
|
|
7174
8036
|
*/
|
|
7175
|
-
async function _HyperLiquidProvider_placeTwapOrder(params, context) {
|
|
8037
|
+
async function _HyperLiquidProvider_placeTwapOrder(params, context, generation) {
|
|
7176
8038
|
const { assetId, formattedSize } = context;
|
|
8039
|
+
const durationMinutes = params.twapDuration;
|
|
8040
|
+
if (durationMinutes === undefined) {
|
|
8041
|
+
throw new Error(PERPS_ERROR_CODES.ORDER_TWAP_DURATION_REQUIRED);
|
|
8042
|
+
}
|
|
8043
|
+
if (!Number.isSafeInteger(durationMinutes) ||
|
|
8044
|
+
durationMinutes < HYPERLIQUID_TWAP_LIMITS.MinDurationMinutes ||
|
|
8045
|
+
durationMinutes > HYPERLIQUID_TWAP_LIMITS.MaxDurationMinutes) {
|
|
8046
|
+
throw new Error(PERPS_ERROR_CODES.ORDER_TWAP_DURATION_INVALID);
|
|
8047
|
+
}
|
|
7177
8048
|
const exchangeClient = __classPrivateFieldGet(this, _HyperLiquidProvider_clientService, "f").getExchangeClient();
|
|
7178
8049
|
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('Submitting TWAP order', {
|
|
7179
8050
|
symbol: params.symbol,
|
|
7180
8051
|
assetId,
|
|
7181
8052
|
size: formattedSize,
|
|
7182
|
-
durationMinutes
|
|
8053
|
+
durationMinutes,
|
|
7183
8054
|
randomize: params.twapRandomize ?? false,
|
|
7184
8055
|
});
|
|
7185
8056
|
const result = await exchangeClient.twapOrder({
|
|
@@ -7188,9 +8059,7 @@ async function _HyperLiquidProvider_placeTwapOrder(params, context) {
|
|
|
7188
8059
|
b: params.isBuy,
|
|
7189
8060
|
s: formattedSize,
|
|
7190
8061
|
r: params.reduceOnly ?? false,
|
|
7191
|
-
|
|
7192
|
-
// inside the venue's bounds.
|
|
7193
|
-
m: params.twapDuration,
|
|
8062
|
+
m: durationMinutes,
|
|
7194
8063
|
t: params.twapRandomize ?? false,
|
|
7195
8064
|
},
|
|
7196
8065
|
});
|
|
@@ -7198,15 +8067,44 @@ async function _HyperLiquidProvider_placeTwapOrder(params, context) {
|
|
|
7198
8067
|
throw new Error(`TWAP order failed: ${JSON.stringify(result)}`);
|
|
7199
8068
|
}
|
|
7200
8069
|
const status = result.response?.data?.status;
|
|
7201
|
-
if (!isStatusObject(status)
|
|
7202
|
-
|
|
7203
|
-
|
|
8070
|
+
if (!isStatusObject(status)) {
|
|
8071
|
+
throw new Error('TWAP order rejected');
|
|
8072
|
+
}
|
|
8073
|
+
const rawError = typeof status.error === 'string' ? status.error : 'TWAP order rejected';
|
|
8074
|
+
const { running } = status;
|
|
8075
|
+
if (!isStatusObject(running) ||
|
|
8076
|
+
typeof running.twapId !== 'number' ||
|
|
8077
|
+
!Number.isSafeInteger(running.twapId) ||
|
|
8078
|
+
running.twapId < 0) {
|
|
7204
8079
|
throw new Error(rawError);
|
|
7205
8080
|
}
|
|
7206
|
-
const
|
|
8081
|
+
const orderId = running.twapId.toString();
|
|
8082
|
+
if (generation !== __classPrivateFieldGet(this, _HyperLiquidProvider_strategyGeneration, "f")) {
|
|
8083
|
+
let remainsLive = true;
|
|
8084
|
+
try {
|
|
8085
|
+
const cancelResult = await exchangeClient.twapCancel({
|
|
8086
|
+
a: assetId,
|
|
8087
|
+
t: running.twapId,
|
|
8088
|
+
});
|
|
8089
|
+
remainsLive =
|
|
8090
|
+
classifyCancelStatus(cancelResult.response?.data?.status) ===
|
|
8091
|
+
CancelChildOutcome.Refused;
|
|
8092
|
+
}
|
|
8093
|
+
catch (error) {
|
|
8094
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('Stale TWAP placement could not be retracted', {
|
|
8095
|
+
error: ensureError(error, 'HyperLiquidProvider.placeTwapOrder.retract').message,
|
|
8096
|
+
orderId,
|
|
8097
|
+
});
|
|
8098
|
+
}
|
|
8099
|
+
return createErrorResult(new Error(PERPS_ERROR_CODES.PROVIDER_LIFECYCLE_STALE), {
|
|
8100
|
+
success: false,
|
|
8101
|
+
submittedSize: formattedSize,
|
|
8102
|
+
...(remainsLive && { orderId }),
|
|
8103
|
+
});
|
|
8104
|
+
}
|
|
7207
8105
|
return {
|
|
7208
8106
|
success: true,
|
|
7209
|
-
orderId
|
|
8107
|
+
orderId,
|
|
7210
8108
|
submittedSize: formattedSize,
|
|
7211
8109
|
};
|
|
7212
8110
|
}, _HyperLiquidProvider_placeScaleOrder =
|
|
@@ -7215,22 +8113,25 @@ async function _HyperLiquidProvider_placeTwapOrder(params, context) {
|
|
|
7215
8113
|
*
|
|
7216
8114
|
* The whole ladder goes in a single `order` action, which is one round trip
|
|
7217
8115
|
* and one signature rather than one per rung. It is **not** atomic: an `na`
|
|
7218
|
-
* grouping evaluates each entry independently
|
|
7219
|
-
*
|
|
7220
|
-
* such — `submittedSize` covers only the rungs that actually rested — rather
|
|
7221
|
-
* than as a full submission.
|
|
8116
|
+
* grouping evaluates each entry independently. Every rung must either rest
|
|
8117
|
+
* or fill; otherwise all known resting rungs are retracted before failure.
|
|
7222
8118
|
*
|
|
7223
8119
|
* @param params - Order parameters.
|
|
7224
8120
|
* @param context - Prepared asset and sizing context.
|
|
8121
|
+
* @param generation - Teardown generation captured before preparation.
|
|
7225
8122
|
* @returns A promise that resolves to the result.
|
|
7226
8123
|
*/
|
|
7227
|
-
async function _HyperLiquidProvider_placeScaleOrder(params, context) {
|
|
7228
|
-
const { assetId,
|
|
8124
|
+
async function _HyperLiquidProvider_placeScaleOrder(params, context, generation) {
|
|
8125
|
+
const { assetId, formattedSize, ladder, builder } = context;
|
|
7229
8126
|
// Built and validated in `#prepareStrategyPlacement`, before anything was
|
|
7230
8127
|
// signed, so what is submitted here is exactly what the minimums were
|
|
7231
8128
|
// applied to.
|
|
8129
|
+
if (!ladder) {
|
|
8130
|
+
throw new Error(PERPS_ERROR_CODES.ORDER_SCALE_RANGE_REQUIRED);
|
|
8131
|
+
}
|
|
7232
8132
|
const { prices, sizes } = ladder;
|
|
7233
8133
|
const count = prices.length;
|
|
8134
|
+
const { groupId, clientOrderIds } = createScaleOrderIdentity(count);
|
|
7234
8135
|
const orders = prices.map((price, index) => ({
|
|
7235
8136
|
a: assetId,
|
|
7236
8137
|
b: params.isBuy,
|
|
@@ -7238,6 +8139,7 @@ async function _HyperLiquidProvider_placeScaleOrder(params, context) {
|
|
|
7238
8139
|
s: sizes[index],
|
|
7239
8140
|
r: params.reduceOnly ?? false,
|
|
7240
8141
|
t: { limit: { tif: 'Gtc' } },
|
|
8142
|
+
c: clientOrderIds[index],
|
|
7241
8143
|
}));
|
|
7242
8144
|
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('Submitting scale ladder', {
|
|
7243
8145
|
symbol: params.symbol,
|
|
@@ -7250,50 +8152,81 @@ async function _HyperLiquidProvider_placeScaleOrder(params, context) {
|
|
|
7250
8152
|
const result = await exchangeClient.order({
|
|
7251
8153
|
orders,
|
|
7252
8154
|
grouping: 'na',
|
|
7253
|
-
builder
|
|
8155
|
+
...(builder && { builder }),
|
|
7254
8156
|
});
|
|
7255
|
-
if (result.status !== 'ok') {
|
|
7256
|
-
throw new Error(`Scale order failed: ${JSON.stringify(result)}`);
|
|
7257
|
-
}
|
|
7258
8157
|
const statuses = result.response?.data?.statuses ?? [];
|
|
7259
|
-
|
|
7260
|
-
|
|
7261
|
-
|
|
7262
|
-
|
|
7263
|
-
|
|
7264
|
-
|
|
7265
|
-
|
|
7266
|
-
.
|
|
7267
|
-
|
|
7268
|
-
|
|
7269
|
-
|
|
7270
|
-
|
|
7271
|
-
|
|
7272
|
-
|
|
7273
|
-
|
|
7274
|
-
|
|
7275
|
-
|
|
7276
|
-
|
|
7277
|
-
|
|
7278
|
-
|
|
7279
|
-
|
|
7280
|
-
|
|
7281
|
-
|
|
7282
|
-
|
|
8158
|
+
const outcomes = statuses
|
|
8159
|
+
.slice(0, count)
|
|
8160
|
+
.map((status) => __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_readOrderPlacementOutcome).call(this, status));
|
|
8161
|
+
const acceptedCount = outcomes.filter((outcome) => outcome !== undefined).length;
|
|
8162
|
+
const restingChildOrderIds = outcomes.flatMap((outcome) => outcome?.state === 'resting' ? [outcome.orderId] : []);
|
|
8163
|
+
const filledChildOrderIds = outcomes.flatMap((outcome) => outcome?.state === 'filled' ? [outcome.orderId] : []);
|
|
8164
|
+
if (generation !== __classPrivateFieldGet(this, _HyperLiquidProvider_strategyGeneration, "f")) {
|
|
8165
|
+
const remainingOrderIds = await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_cancelOrderRequests).call(this, exchangeClient, restingChildOrderIds.map((orderId) => ({
|
|
8166
|
+
a: assetId,
|
|
8167
|
+
o: Number(orderId),
|
|
8168
|
+
})));
|
|
8169
|
+
const recoverableOrderIds = [
|
|
8170
|
+
...filledChildOrderIds,
|
|
8171
|
+
...remainingOrderIds.map(String),
|
|
8172
|
+
];
|
|
8173
|
+
return createErrorResult(new Error(PERPS_ERROR_CODES.PROVIDER_LIFECYCLE_STALE), {
|
|
8174
|
+
success: false,
|
|
8175
|
+
submittedSize: formattedSize,
|
|
8176
|
+
...(recoverableOrderIds.length > 0 && {
|
|
8177
|
+
childOrderIds: recoverableOrderIds,
|
|
8178
|
+
}),
|
|
8179
|
+
});
|
|
8180
|
+
}
|
|
8181
|
+
if (result.status !== 'ok' ||
|
|
8182
|
+
statuses.length !== count ||
|
|
8183
|
+
acceptedCount !== count) {
|
|
8184
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('Scale ladder was not fully accepted', {
|
|
8185
|
+
accepted: acceptedCount,
|
|
8186
|
+
filled: filledChildOrderIds.length,
|
|
8187
|
+
resting: restingChildOrderIds.length,
|
|
7283
8188
|
requested: count,
|
|
7284
8189
|
statuses,
|
|
7285
8190
|
});
|
|
8191
|
+
const remainingOrderIds = await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_cancelOrderRequests).call(this, exchangeClient, restingChildOrderIds.map((orderId) => ({
|
|
8192
|
+
a: assetId,
|
|
8193
|
+
o: Number(orderId),
|
|
8194
|
+
})));
|
|
8195
|
+
const recoverableOrderIds = [
|
|
8196
|
+
...filledChildOrderIds,
|
|
8197
|
+
...remainingOrderIds.map(String),
|
|
8198
|
+
];
|
|
8199
|
+
if (remainingOrderIds.length > 0) {
|
|
8200
|
+
const remainingRestingOrderIds = remainingOrderIds.map(String);
|
|
8201
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_scaleOrderGroups, "f").set(groupId, {
|
|
8202
|
+
symbol: params.symbol,
|
|
8203
|
+
orderIds: remainingRestingOrderIds,
|
|
8204
|
+
});
|
|
8205
|
+
return createErrorResult(new Error(PERPS_ERROR_CODES.ORDER_STRATEGY_CANCEL_INCOMPLETE), {
|
|
8206
|
+
success: false,
|
|
8207
|
+
orderId: groupId,
|
|
8208
|
+
childOrderIds: recoverableOrderIds,
|
|
8209
|
+
submittedSize: formattedSize,
|
|
8210
|
+
});
|
|
8211
|
+
}
|
|
8212
|
+
if (recoverableOrderIds.length > 0) {
|
|
8213
|
+
return createErrorResult(new Error(PERPS_ERROR_CODES.ORDER_REJECTED), {
|
|
8214
|
+
success: false,
|
|
8215
|
+
childOrderIds: recoverableOrderIds,
|
|
8216
|
+
submittedSize: formattedSize,
|
|
8217
|
+
});
|
|
8218
|
+
}
|
|
8219
|
+
throw new Error(PERPS_ERROR_CODES.ORDER_REJECTED);
|
|
7286
8220
|
}
|
|
8221
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_scaleOrderGroups, "f").set(groupId, {
|
|
8222
|
+
symbol: params.symbol,
|
|
8223
|
+
orderIds: restingChildOrderIds,
|
|
8224
|
+
});
|
|
7287
8225
|
return {
|
|
7288
8226
|
success: true,
|
|
7289
8227
|
orderId: groupId,
|
|
7290
|
-
childOrderIds,
|
|
7291
|
-
submittedSize:
|
|
7292
|
-
? formatHyperLiquidSize({
|
|
7293
|
-
size: restedRungs.reduce((total, rung) => total + parseFloat(rung.size), 0),
|
|
7294
|
-
szDecimals,
|
|
7295
|
-
})
|
|
7296
|
-
: formattedSize,
|
|
8228
|
+
childOrderIds: restingChildOrderIds,
|
|
8229
|
+
submittedSize: formattedSize,
|
|
7297
8230
|
};
|
|
7298
8231
|
}, _HyperLiquidProvider_startChaseSession =
|
|
7299
8232
|
/**
|
|
@@ -7308,12 +8241,12 @@ async function _HyperLiquidProvider_placeScaleOrder(params, context) {
|
|
|
7308
8241
|
* @returns A promise that resolves to the result.
|
|
7309
8242
|
*/
|
|
7310
8243
|
async function _HyperLiquidProvider_startChaseSession(params, context, generation) {
|
|
7311
|
-
const { assetId, szDecimals, formattedSize } = context;
|
|
8244
|
+
const { assetId, szDecimals, formattedSize, builder } = context;
|
|
7312
8245
|
// The preamble is several round trips long. A disconnect during it has
|
|
7313
8246
|
// already torn down everything this session would run on, so the chase
|
|
7314
8247
|
// stops here rather than reading the book and putting a fresh order on it
|
|
7315
8248
|
// for a provider that no longer exists.
|
|
7316
|
-
if (generation !== __classPrivateFieldGet(this,
|
|
8249
|
+
if (generation !== __classPrivateFieldGet(this, _HyperLiquidProvider_strategyGeneration, "f")) {
|
|
7317
8250
|
throw new Error(PERPS_ERROR_CODES.ORDER_CHASE_ABANDONED);
|
|
7318
8251
|
}
|
|
7319
8252
|
let quotePrice = await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_getChaseQuotePrice).call(this, {
|
|
@@ -7321,8 +8254,9 @@ async function _HyperLiquidProvider_startChaseSession(params, context, generatio
|
|
|
7321
8254
|
isBuy: params.isBuy,
|
|
7322
8255
|
szDecimals,
|
|
7323
8256
|
});
|
|
7324
|
-
if (quotePrice ===
|
|
7325
|
-
|
|
8257
|
+
if (quotePrice === CHASE_ORDER_STATUS_UNAVAILABLE ||
|
|
8258
|
+
typeof quotePrice !== 'string') {
|
|
8259
|
+
// A first placement has no child whose terminal state could be read.
|
|
7326
8260
|
throw new Error(PERPS_ERROR_CODES.ORDER_CHASE_TOUCH_UNAVAILABLE);
|
|
7327
8261
|
}
|
|
7328
8262
|
// The book read is a round trip of its own, so the check above does not
|
|
@@ -7331,12 +8265,7 @@ async function _HyperLiquidProvider_startChaseSession(params, context, generatio
|
|
|
7331
8265
|
// before the next one, leaving one window that cannot be closed from here
|
|
7332
8266
|
// — a disconnect arriving while the submission itself is in flight, which
|
|
7333
8267
|
// the check after it handles.
|
|
7334
|
-
if (generation !== __classPrivateFieldGet(this,
|
|
7335
|
-
throw new Error(PERPS_ERROR_CODES.ORDER_CHASE_ABANDONED);
|
|
7336
|
-
}
|
|
7337
|
-
// Read once, while the caller's fee-source context is still set.
|
|
7338
|
-
const builder = await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_getBuilderOrderContext).call(this);
|
|
7339
|
-
if (generation !== __classPrivateFieldGet(this, _HyperLiquidProvider_chaseGeneration, "f")) {
|
|
8268
|
+
if (generation !== __classPrivateFieldGet(this, _HyperLiquidProvider_strategyGeneration, "f")) {
|
|
7340
8269
|
throw new Error(PERPS_ERROR_CODES.ORDER_CHASE_ABANDONED);
|
|
7341
8270
|
}
|
|
7342
8271
|
// Held onto rather than looked up again after the submission returns.
|
|
@@ -7354,8 +8283,7 @@ async function _HyperLiquidProvider_startChaseSession(params, context, generatio
|
|
|
7354
8283
|
price: quotePrice,
|
|
7355
8284
|
size: formattedSize,
|
|
7356
8285
|
reduceOnly: params.reduceOnly ?? false,
|
|
7357
|
-
|
|
7358
|
-
builderAddress: builder.b,
|
|
8286
|
+
builder,
|
|
7359
8287
|
exchangeClient: placingClient,
|
|
7360
8288
|
});
|
|
7361
8289
|
break;
|
|
@@ -7365,7 +8293,7 @@ async function _HyperLiquidProvider_startChaseSession(params, context, generatio
|
|
|
7365
8293
|
!isRetryableChasePlacementError(error)) {
|
|
7366
8294
|
throw error;
|
|
7367
8295
|
}
|
|
7368
|
-
if (generation !== __classPrivateFieldGet(this,
|
|
8296
|
+
if (generation !== __classPrivateFieldGet(this, _HyperLiquidProvider_strategyGeneration, "f")) {
|
|
7369
8297
|
throw new Error(PERPS_ERROR_CODES.ORDER_CHASE_ABANDONED);
|
|
7370
8298
|
}
|
|
7371
8299
|
const refreshedQuote = await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_getChaseQuotePrice).call(this, {
|
|
@@ -7373,10 +8301,11 @@ async function _HyperLiquidProvider_startChaseSession(params, context, generatio
|
|
|
7373
8301
|
isBuy: params.isBuy,
|
|
7374
8302
|
szDecimals,
|
|
7375
8303
|
});
|
|
7376
|
-
if (refreshedQuote ===
|
|
8304
|
+
if (refreshedQuote === CHASE_ORDER_STATUS_UNAVAILABLE ||
|
|
8305
|
+
typeof refreshedQuote !== 'string') {
|
|
7377
8306
|
throw new Error(PERPS_ERROR_CODES.ORDER_CHASE_TOUCH_UNAVAILABLE);
|
|
7378
8307
|
}
|
|
7379
|
-
if (generation !== __classPrivateFieldGet(this,
|
|
8308
|
+
if (generation !== __classPrivateFieldGet(this, _HyperLiquidProvider_strategyGeneration, "f")) {
|
|
7380
8309
|
throw new Error(PERPS_ERROR_CODES.ORDER_CHASE_ABANDONED);
|
|
7381
8310
|
}
|
|
7382
8311
|
quotePrice = refreshedQuote;
|
|
@@ -7404,11 +8333,11 @@ async function _HyperLiquidProvider_startChaseSession(params, context, generatio
|
|
|
7404
8333
|
maxDistanceBps: params.chaseMaxDistanceBps,
|
|
7405
8334
|
intervalMs,
|
|
7406
8335
|
lastSnapshotSizeRefreshAt: 0,
|
|
7407
|
-
|
|
7408
|
-
|
|
7409
|
-
|
|
7410
|
-
(params.chaseMaxDurationMs
|
|
7411
|
-
maxRepricings: params.chaseMaxRepricings ??
|
|
8336
|
+
builder,
|
|
8337
|
+
deadline: params.chaseMaxDurationMs === undefined
|
|
8338
|
+
? Number.POSITIVE_INFINITY
|
|
8339
|
+
: Date.now() + params.chaseMaxDurationMs,
|
|
8340
|
+
maxRepricings: params.chaseMaxRepricings ?? Number.POSITIVE_INFINITY,
|
|
7412
8341
|
repricings: 0,
|
|
7413
8342
|
timer: null,
|
|
7414
8343
|
active: true,
|
|
@@ -7427,7 +8356,7 @@ async function _HyperLiquidProvider_startChaseSession(params, context, generatio
|
|
|
7427
8356
|
// and the failure is then true of the venue as well as of this provider.
|
|
7428
8357
|
// Best-effort still: the transport underneath that client may already be
|
|
7429
8358
|
// closing, so a cancel that does not land falls back to reporting the id.
|
|
7430
|
-
if (generation !== __classPrivateFieldGet(this,
|
|
8359
|
+
if (generation !== __classPrivateFieldGet(this, _HyperLiquidProvider_strategyGeneration, "f")) {
|
|
7431
8360
|
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('Chase placement outlived its provider', {
|
|
7432
8361
|
orderId,
|
|
7433
8362
|
restingPrice: quotePrice,
|
|
@@ -7439,7 +8368,9 @@ async function _HyperLiquidProvider_startChaseSession(params, context, generatio
|
|
|
7439
8368
|
// that is no longer on the book would send the caller to cancel
|
|
7440
8369
|
// something already gone; naming one that still rests is the only
|
|
7441
8370
|
// route left to it.
|
|
7442
|
-
...(outcome ===
|
|
8371
|
+
...(outcome === CancelChildOutcome.Refused
|
|
8372
|
+
? { childOrderIds: [orderId] }
|
|
8373
|
+
: {}),
|
|
7443
8374
|
submittedSize: formattedSize,
|
|
7444
8375
|
});
|
|
7445
8376
|
}
|
|
@@ -7475,8 +8406,8 @@ async function _HyperLiquidProvider_startChaseSession(params, context, generatio
|
|
|
7475
8406
|
* @param params.own.orderId - Exchange ID of that order.
|
|
7476
8407
|
* @param params.own.price - Price that order rests at.
|
|
7477
8408
|
* @param params.own.size - Size it was last placed for.
|
|
7478
|
-
* @returns The formatted price
|
|
7479
|
-
*
|
|
8409
|
+
* @returns The formatted price, a terminal child result, or the status
|
|
8410
|
+
* unavailable sentinel when the child may still be live.
|
|
7480
8411
|
*/
|
|
7481
8412
|
async function _HyperLiquidProvider_getChaseQuotePrice(params) {
|
|
7482
8413
|
const { symbol, isBuy, szDecimals, own } = params;
|
|
@@ -7491,8 +8422,9 @@ async function _HyperLiquidProvider_getChaseQuotePrice(params) {
|
|
|
7491
8422
|
callerOrderId: own?.orderId,
|
|
7492
8423
|
ownSide,
|
|
7493
8424
|
});
|
|
7494
|
-
if (netting ===
|
|
7495
|
-
|
|
8425
|
+
if (netting === CHASE_ORDER_STATUS_UNAVAILABLE ||
|
|
8426
|
+
!(netting instanceof Map)) {
|
|
8427
|
+
return netting;
|
|
7496
8428
|
}
|
|
7497
8429
|
const bestBid = readBestExternalPrice(book?.levels?.[0], netting);
|
|
7498
8430
|
const bestAsk = readBestExternalPrice(book?.levels?.[1], netting);
|
|
@@ -7510,9 +8442,7 @@ async function _HyperLiquidProvider_getChaseQuotePrice(params) {
|
|
|
7510
8442
|
* @param params.price - Formatted limit price.
|
|
7511
8443
|
* @param params.size - Formatted size.
|
|
7512
8444
|
* @param params.reduceOnly - Whether the order may only reduce a position.
|
|
7513
|
-
* @param params.
|
|
7514
|
-
* when the session started so replacements keep the rate they were quoted at.
|
|
7515
|
-
* @param params.builderAddress - Builder address captured with the fee.
|
|
8445
|
+
* @param params.builder - Builder context captured when the session started.
|
|
7516
8446
|
* @param params.exchangeClient - Client to submit through. Passed in rather
|
|
7517
8447
|
* than looked up here so a first placement can keep the instance it signed
|
|
7518
8448
|
* with, which is the only one that can take the order back once `disconnect`
|
|
@@ -7534,10 +8464,7 @@ async function _HyperLiquidProvider_restChaseOrder(params) {
|
|
|
7534
8464
|
},
|
|
7535
8465
|
],
|
|
7536
8466
|
grouping: 'na',
|
|
7537
|
-
builder:
|
|
7538
|
-
b: params.builderAddress,
|
|
7539
|
-
f: params.builderFee,
|
|
7540
|
-
},
|
|
8467
|
+
...(params.builder && { builder: params.builder }),
|
|
7541
8468
|
});
|
|
7542
8469
|
if (result.status !== 'ok') {
|
|
7543
8470
|
throw new Error(`Chase order failed: ${JSON.stringify(result)}`);
|
|
@@ -7549,25 +8476,21 @@ async function _HyperLiquidProvider_restChaseOrder(params) {
|
|
|
7549
8476
|
return orderId;
|
|
7550
8477
|
}, _HyperLiquidProvider_scheduleChaseTick = function _HyperLiquidProvider_scheduleChaseTick(sessionId) {
|
|
7551
8478
|
const session = __classPrivateFieldGet(this, _HyperLiquidProvider_chaseSessions, "f").get(sessionId);
|
|
7552
|
-
if (!session?.active) {
|
|
8479
|
+
if (!session?.active || session.timer !== null) {
|
|
7553
8480
|
return;
|
|
7554
8481
|
}
|
|
7555
8482
|
const timer = setTimeout(() => {
|
|
7556
8483
|
session.timer = null;
|
|
7557
|
-
__classPrivateFieldSet(this, _HyperLiquidProvider_chaseTickQueue, __classPrivateFieldGet(this, _HyperLiquidProvider_chaseTickQueue, "f")
|
|
7558
|
-
|
|
7559
|
-
|
|
7560
|
-
|
|
7561
|
-
|
|
7562
|
-
|
|
7563
|
-
|
|
7564
|
-
|
|
7565
|
-
|
|
7566
|
-
|
|
7567
|
-
});
|
|
7568
|
-
__classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_recoverFailedChaseTick).call(this, sessionId);
|
|
7569
|
-
}
|
|
7570
|
-
return undefined;
|
|
8484
|
+
__classPrivateFieldSet(this, _HyperLiquidProvider_chaseTickQueue, __classPrivateFieldGet(this, _HyperLiquidProvider_chaseTickQueue, "f")
|
|
8485
|
+
.then(() => __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_runChaseTick).call(this, sessionId))
|
|
8486
|
+
.catch((error) => {
|
|
8487
|
+
// Resolve the shared queue after every failure. Otherwise one
|
|
8488
|
+
// rejected tick prevents all later ticks and teardown from running.
|
|
8489
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('Chase tick failed', {
|
|
8490
|
+
sessionId,
|
|
8491
|
+
error: ensureError(error, 'HyperLiquidProvider.chaseTick').message,
|
|
8492
|
+
});
|
|
8493
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_recoverFailedChaseTick).call(this, sessionId);
|
|
7571
8494
|
}), "f");
|
|
7572
8495
|
}, session.intervalMs);
|
|
7573
8496
|
// Node keeps the process alive for a pending timer; a background chase is
|
|
@@ -7599,6 +8522,12 @@ async function _HyperLiquidProvider_restChaseOrder(params) {
|
|
|
7599
8522
|
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('Chase ended with nothing resting', {
|
|
7600
8523
|
sessionId,
|
|
7601
8524
|
});
|
|
8525
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_rebalanceChaseSession).call(this, session).catch((error) => {
|
|
8526
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('Chase collateral cleanup failed', {
|
|
8527
|
+
sessionId,
|
|
8528
|
+
error: ensureError(error, 'HyperLiquidProvider.recoverFailedChaseTick').message,
|
|
8529
|
+
});
|
|
8530
|
+
});
|
|
7602
8531
|
return;
|
|
7603
8532
|
}
|
|
7604
8533
|
__classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_scheduleChaseTick).call(this, sessionId);
|
|
@@ -7632,6 +8561,7 @@ async function _HyperLiquidProvider_runChaseTick(sessionId) {
|
|
|
7632
8561
|
session.active = false;
|
|
7633
8562
|
session.status = CHASE_ORDER_STATUS.DurationReached;
|
|
7634
8563
|
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('Chase session window closed', { sessionId });
|
|
8564
|
+
await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_rebalanceChaseSession).call(this, session);
|
|
7635
8565
|
return;
|
|
7636
8566
|
}
|
|
7637
8567
|
const rawQuotePrice = await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_getChaseQuotePrice).call(this, {
|
|
@@ -7646,16 +8576,29 @@ async function _HyperLiquidProvider_runChaseTick(sessionId) {
|
|
|
7646
8576
|
}
|
|
7647
8577
|
: undefined,
|
|
7648
8578
|
});
|
|
7649
|
-
if (rawQuotePrice
|
|
7650
|
-
// The order left the book without the loop seeing it
|
|
7651
|
-
//
|
|
7652
|
-
// holding one until the window closes.
|
|
8579
|
+
if (typeof rawQuotePrice !== 'string') {
|
|
8580
|
+
// The order left the book without the loop seeing it. Preserve the venue
|
|
8581
|
+
// status so an external cancellation is not reported as a fill.
|
|
7653
8582
|
session.orderId = null;
|
|
7654
8583
|
session.active = false;
|
|
7655
|
-
session.status =
|
|
7656
|
-
|
|
8584
|
+
session.status = rawQuotePrice.status;
|
|
8585
|
+
session.size = rawQuotePrice.remainingSize ?? '0';
|
|
8586
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('Chase order ended between ticks', {
|
|
8587
|
+
sessionId,
|
|
8588
|
+
status: session.status,
|
|
8589
|
+
});
|
|
8590
|
+
await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_rebalanceChaseSession).call(this, session);
|
|
8591
|
+
return;
|
|
8592
|
+
}
|
|
8593
|
+
if (rawQuotePrice === CHASE_ORDER_STATUS_UNAVAILABLE) {
|
|
8594
|
+
// The child may still be live. Leave it untouched and retry on the next
|
|
8595
|
+
// normal interval instead of permanently stranding the Chase after one
|
|
8596
|
+
// short status outage.
|
|
8597
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('Chase order status unavailable', {
|
|
7657
8598
|
sessionId,
|
|
8599
|
+
orderId: session.orderId,
|
|
7658
8600
|
});
|
|
8601
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_scheduleChaseTick).call(this, sessionId);
|
|
7659
8602
|
return;
|
|
7660
8603
|
}
|
|
7661
8604
|
const arrivalPrice = Number.parseFloat(session.arrivalPrice);
|
|
@@ -7713,12 +8656,12 @@ async function _HyperLiquidProvider_runChaseTick(sessionId) {
|
|
|
7713
8656
|
// A refused cancel leaves the child resting. Preserve its ID so the
|
|
7714
8657
|
// caller that stopped this session can report an incomplete cancel and
|
|
7715
8658
|
// retry it instead of orphaning the order.
|
|
7716
|
-
if (outcome !==
|
|
8659
|
+
if (outcome !== CancelChildOutcome.Refused) {
|
|
7717
8660
|
session.orderId = null;
|
|
7718
8661
|
}
|
|
7719
8662
|
return;
|
|
7720
8663
|
}
|
|
7721
|
-
if (outcome ===
|
|
8664
|
+
if (outcome === CancelChildOutcome.Refused) {
|
|
7722
8665
|
// The exchange kept the order, so it is still resting at the old price.
|
|
7723
8666
|
// Placing the replacement anyway would double the position. Leave it
|
|
7724
8667
|
// alone and try again on the next tick, which is why this falls through
|
|
@@ -7729,25 +8672,36 @@ async function _HyperLiquidProvider_runChaseTick(sessionId) {
|
|
|
7729
8672
|
});
|
|
7730
8673
|
reachedMaxDistance = false;
|
|
7731
8674
|
}
|
|
7732
|
-
else if (outcome ===
|
|
8675
|
+
else if (outcome === CancelChildOutcome.Gone) {
|
|
7733
8676
|
// The order left the book between ticks: it filled, or something else
|
|
7734
8677
|
// cancelled it. There is nothing left to chase.
|
|
7735
8678
|
const goneOrderId = session.orderId;
|
|
7736
|
-
const remaining = await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_readOrderRemainder).call(this, goneOrderId);
|
|
7737
8679
|
session.orderId = null;
|
|
7738
8680
|
session.active = false;
|
|
7739
|
-
|
|
7740
|
-
|
|
7741
|
-
|
|
7742
|
-
|
|
7743
|
-
|
|
7744
|
-
session.
|
|
8681
|
+
let remainder;
|
|
8682
|
+
try {
|
|
8683
|
+
remainder = await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_readOrderRemainder).call(this, goneOrderId, CHASE_ORDER_STATUS_RETRY_COUNT);
|
|
8684
|
+
}
|
|
8685
|
+
catch (error) {
|
|
8686
|
+
session.status = CHASE_ORDER_STATUS.Failed;
|
|
8687
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('Chase order status unavailable', {
|
|
8688
|
+
sessionId,
|
|
8689
|
+
orderId: goneOrderId,
|
|
8690
|
+
error: ensureError(error, 'HyperLiquidProvider.chaseGoneOrderStatus').message,
|
|
8691
|
+
});
|
|
8692
|
+
await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_rebalanceChaseSession).call(this, session);
|
|
8693
|
+
return;
|
|
8694
|
+
}
|
|
8695
|
+
session.status = remainder.terminalStatus ?? CHASE_ORDER_STATUS.Failed;
|
|
8696
|
+
if (remainder.remainingSize !== null) {
|
|
8697
|
+
session.size = remainder.remainingSize;
|
|
7745
8698
|
}
|
|
7746
8699
|
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('Chase order no longer resting', {
|
|
7747
8700
|
sessionId,
|
|
7748
8701
|
status: session.status,
|
|
7749
8702
|
remainingSize: session.size,
|
|
7750
8703
|
});
|
|
8704
|
+
await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_rebalanceChaseSession).call(this, session);
|
|
7751
8705
|
return;
|
|
7752
8706
|
}
|
|
7753
8707
|
else {
|
|
@@ -7761,25 +8715,44 @@ async function _HyperLiquidProvider_runChaseTick(sessionId) {
|
|
|
7761
8715
|
// No further fills can reach a cancelled order, so what it did not fill
|
|
7762
8716
|
// is now fixed. Sampling before the cancel would have left a window in
|
|
7763
8717
|
// which a fill lands and is then re-placed.
|
|
7764
|
-
|
|
8718
|
+
let remainder;
|
|
8719
|
+
try {
|
|
8720
|
+
remainder = await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_readOrderRemainder).call(this, cancelledOrderId, CHASE_ORDER_STATUS_RETRY_COUNT);
|
|
8721
|
+
}
|
|
8722
|
+
catch (error) {
|
|
8723
|
+
session.replacingOrderId = null;
|
|
8724
|
+
throw error;
|
|
8725
|
+
}
|
|
7765
8726
|
// Another round trip, another window for a cancel to land. By now the
|
|
7766
8727
|
// old order is already off the book, so a cancel that arrived during it
|
|
7767
8728
|
// found nothing to cancel and reported success — resting a replacement
|
|
7768
8729
|
// after that would put an order on the book the caller believes is gone
|
|
7769
8730
|
// and has no handle for.
|
|
7770
8731
|
if (!session.active || !__classPrivateFieldGet(this, _HyperLiquidProvider_chaseSessions, "f").has(sessionId)) {
|
|
8732
|
+
session.replacingOrderId = null;
|
|
7771
8733
|
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('Chase cancelled mid-reprice', {
|
|
7772
8734
|
sessionId,
|
|
7773
8735
|
});
|
|
7774
8736
|
return;
|
|
7775
8737
|
}
|
|
7776
|
-
if (
|
|
7777
|
-
|
|
8738
|
+
if (remainder.remainingSize === null ||
|
|
8739
|
+
(remainder.terminalStatus !== null &&
|
|
8740
|
+
remainder.terminalStatus !== CHASE_ORDER_STATUS.Canceled)) {
|
|
8741
|
+
// Nothing is left to chase. Preserve whether the venue actually
|
|
8742
|
+
// confirmed a fill rather than labelling every zero remainder filled.
|
|
8743
|
+
session.replacingOrderId = null;
|
|
7778
8744
|
session.active = false;
|
|
7779
|
-
session.
|
|
7780
|
-
|
|
8745
|
+
session.size = remainder.remainingSize ?? '0';
|
|
8746
|
+
session.status =
|
|
8747
|
+
remainder.terminalStatus ?? CHASE_ORDER_STATUS.Failed;
|
|
8748
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('Chase order has no remainder', {
|
|
8749
|
+
sessionId,
|
|
8750
|
+
status: session.status,
|
|
8751
|
+
});
|
|
8752
|
+
await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_rebalanceChaseSession).call(this, session);
|
|
7781
8753
|
return;
|
|
7782
8754
|
}
|
|
8755
|
+
const remaining = remainder.remainingSize;
|
|
7783
8756
|
// Published *before* the placement starts, not from the promise it
|
|
7784
8757
|
// returns: the window opens the moment `#restChaseOrder` is entered, so
|
|
7785
8758
|
// deriving the marker from its return value would leave the synchronous
|
|
@@ -7798,8 +8771,7 @@ async function _HyperLiquidProvider_runChaseTick(sessionId) {
|
|
|
7798
8771
|
price: quotePrice,
|
|
7799
8772
|
size: remaining,
|
|
7800
8773
|
reduceOnly: session.reduceOnly,
|
|
7801
|
-
|
|
7802
|
-
builderAddress: session.builderAddress,
|
|
8774
|
+
builder: session.builder,
|
|
7803
8775
|
// A running session is on a live provider, so the current client is
|
|
7804
8776
|
// the right one; only the first placement has a teardown to survive.
|
|
7805
8777
|
exchangeClient: __classPrivateFieldGet(this, _HyperLiquidProvider_clientService, "f").getExchangeClient(),
|
|
@@ -7837,7 +8809,7 @@ async function _HyperLiquidProvider_runChaseTick(sessionId) {
|
|
|
7837
8809
|
}
|
|
7838
8810
|
}
|
|
7839
8811
|
}
|
|
7840
|
-
if (reachedMaxDistance) {
|
|
8812
|
+
if (reachedMaxDistance && session.maxDistanceBps !== undefined) {
|
|
7841
8813
|
session.active = false;
|
|
7842
8814
|
session.status = CHASE_ORDER_STATUS.MaxDistanceReached;
|
|
7843
8815
|
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('Chase max distance reached', {
|
|
@@ -7845,12 +8817,24 @@ async function _HyperLiquidProvider_runChaseTick(sessionId) {
|
|
|
7845
8817
|
restingPrice: session.restingPrice,
|
|
7846
8818
|
maxDistanceBps: session.maxDistanceBps,
|
|
7847
8819
|
});
|
|
8820
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_onChaseOrderMaxDistanceReached, "f")?.call(this, {
|
|
8821
|
+
handle: sessionId,
|
|
8822
|
+
symbol: session.symbol,
|
|
8823
|
+
side: session.isBuy ? 'buy' : 'sell',
|
|
8824
|
+
restingOrderId: session.orderId,
|
|
8825
|
+
restingPrice: session.restingPrice,
|
|
8826
|
+
maxDistanceBps: session.maxDistanceBps,
|
|
8827
|
+
timestamp: Date.now(),
|
|
8828
|
+
providerId: PROVIDER_CONFIG.DefaultProvider,
|
|
8829
|
+
});
|
|
8830
|
+
await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_rebalanceChaseSession).call(this, session);
|
|
7848
8831
|
return;
|
|
7849
8832
|
}
|
|
7850
8833
|
if (session.repricings >= session.maxRepricings) {
|
|
7851
8834
|
session.active = false;
|
|
7852
8835
|
session.status = CHASE_ORDER_STATUS.RepricingLimitReached;
|
|
7853
8836
|
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('Chase repricing cap reached', { sessionId });
|
|
8837
|
+
await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_rebalanceChaseSession).call(this, session);
|
|
7854
8838
|
return;
|
|
7855
8839
|
}
|
|
7856
8840
|
__classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_scheduleChaseTick).call(this, sessionId);
|
|
@@ -7873,21 +8857,24 @@ async function _HyperLiquidProvider_runChaseTick(sessionId) {
|
|
|
7873
8857
|
* @param params.isBuy - Side being quoted.
|
|
7874
8858
|
* @param params.callerOrderId - The asking session's order, if it has one.
|
|
7875
8859
|
* @param params.ownSide - The side of the book those orders rest on.
|
|
7876
|
-
* @returns Our resting size at each price,
|
|
7877
|
-
*
|
|
8860
|
+
* @returns Our resting size at each price, the asking child's terminal
|
|
8861
|
+
* result, or the unavailable sentinel.
|
|
7878
8862
|
*/
|
|
7879
8863
|
async function _HyperLiquidProvider_resolveOwnRestingSizes(params) {
|
|
7880
8864
|
const { symbol, isBuy, callerOrderId, ownSide } = params;
|
|
7881
8865
|
// Every chase this provider is running on this side, the caller included.
|
|
7882
|
-
const ourOrders = [
|
|
7883
|
-
|
|
7884
|
-
session.
|
|
7885
|
-
|
|
7886
|
-
|
|
7887
|
-
|
|
7888
|
-
|
|
7889
|
-
|
|
7890
|
-
|
|
8866
|
+
const ourOrders = [];
|
|
8867
|
+
for (const session of __classPrivateFieldGet(this, _HyperLiquidProvider_chaseSessions, "f").values()) {
|
|
8868
|
+
if (session.orderId !== null &&
|
|
8869
|
+
session.symbol === symbol &&
|
|
8870
|
+
session.isBuy === isBuy) {
|
|
8871
|
+
ourOrders.push({
|
|
8872
|
+
orderId: session.orderId,
|
|
8873
|
+
price: session.restingPrice,
|
|
8874
|
+
size: session.size,
|
|
8875
|
+
});
|
|
8876
|
+
}
|
|
8877
|
+
}
|
|
7891
8878
|
if (ourOrders.length === 0) {
|
|
7892
8879
|
return new Map();
|
|
7893
8880
|
}
|
|
@@ -7904,27 +8891,44 @@ async function _HyperLiquidProvider_resolveOwnRestingSizes(params) {
|
|
|
7904
8891
|
const level = ownSide?.find((entry) => entry.px === order.price);
|
|
7905
8892
|
return !level || parseFloat(level.sz) <= (recorded.get(order.price) ?? 0);
|
|
7906
8893
|
});
|
|
7907
|
-
let
|
|
7908
|
-
|
|
7909
|
-
|
|
7910
|
-
|
|
8894
|
+
let callerTerminalResult = null;
|
|
8895
|
+
let callerStatusUnavailable = false;
|
|
8896
|
+
const resolutions = await Promise.allSettled(ambiguous.map((order) => __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_readOrderRemainder).call(this, order.orderId, CHASE_ORDER_STATUS_RETRY_COUNT)));
|
|
8897
|
+
resolutions.forEach((resolution, index) => {
|
|
8898
|
+
const order = ambiguous[index];
|
|
8899
|
+
if (resolution.status === 'fulfilled') {
|
|
8900
|
+
const { remainingSize: live, terminalStatus } = resolution.value;
|
|
7911
8901
|
const delta = (live === null ? 0 : parseFloat(live)) - parseFloat(order.size);
|
|
7912
8902
|
recorded.set(order.price, Math.max(0, (recorded.get(order.price) ?? 0) + delta));
|
|
7913
|
-
if (
|
|
7914
|
-
|
|
8903
|
+
if (terminalStatus !== null && order.orderId === callerOrderId) {
|
|
8904
|
+
callerTerminalResult = {
|
|
8905
|
+
remainingSize: live,
|
|
8906
|
+
status: terminalStatus,
|
|
8907
|
+
};
|
|
7915
8908
|
}
|
|
7916
8909
|
}
|
|
7917
|
-
|
|
7918
|
-
|
|
7919
|
-
|
|
7920
|
-
|
|
7921
|
-
|
|
7922
|
-
|
|
7923
|
-
|
|
7924
|
-
|
|
8910
|
+
else {
|
|
8911
|
+
const { reason: error } = resolution;
|
|
8912
|
+
if (error instanceof ChaseOrderStatusUnavailableError &&
|
|
8913
|
+
order.orderId === callerOrderId) {
|
|
8914
|
+
callerStatusUnavailable = true;
|
|
8915
|
+
}
|
|
8916
|
+
else {
|
|
8917
|
+
// A failed lookup must not stop the chase. Keeping the recorded size
|
|
8918
|
+
// can only over-subtract, which quotes a level deeper rather than
|
|
8919
|
+
// leaving the order stranded at a stale price.
|
|
8920
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('Chase own-size lookup failed', {
|
|
8921
|
+
orderId: order.orderId,
|
|
8922
|
+
error: ensureError(error, 'HyperLiquidProvider.chaseOwnSize')
|
|
8923
|
+
.message,
|
|
8924
|
+
});
|
|
8925
|
+
}
|
|
7925
8926
|
}
|
|
8927
|
+
});
|
|
8928
|
+
if (callerStatusUnavailable) {
|
|
8929
|
+
return CHASE_ORDER_STATUS_UNAVAILABLE;
|
|
7926
8930
|
}
|
|
7927
|
-
return
|
|
8931
|
+
return callerTerminalResult ?? recorded;
|
|
7928
8932
|
}, _HyperLiquidProvider_readOrderRemainder =
|
|
7929
8933
|
/**
|
|
7930
8934
|
* Read what a chase's just-cancelled order left unfilled.
|
|
@@ -7940,19 +8944,51 @@ async function _HyperLiquidProvider_resolveOwnRestingSizes(params) {
|
|
|
7940
8944
|
* window in which a fill lands and is then re-placed on top.
|
|
7941
8945
|
*
|
|
7942
8946
|
* @param orderId - Exchange ID of the order that was just cancelled.
|
|
7943
|
-
* @
|
|
8947
|
+
* @param unknownStatusRetries - Remaining retries for a transient unknown ID.
|
|
8948
|
+
* @returns The unfilled size and exact terminal status, when present.
|
|
7944
8949
|
*/
|
|
7945
|
-
async function _HyperLiquidProvider_readOrderRemainder(orderId) {
|
|
8950
|
+
async function _HyperLiquidProvider_readOrderRemainder(orderId, unknownStatusRetries = 0) {
|
|
7946
8951
|
const userAddress = await __classPrivateFieldGet(this, _HyperLiquidProvider_walletService, "f").getUserAddressWithDefault();
|
|
7947
8952
|
const status = await __classPrivateFieldGet(this, _HyperLiquidProvider_clientService, "f").getInfoClient().orderStatus({
|
|
7948
8953
|
user: userAddress,
|
|
7949
8954
|
oid: parseInt(orderId, 10),
|
|
7950
8955
|
});
|
|
7951
8956
|
if (status.status !== 'order') {
|
|
7952
|
-
|
|
8957
|
+
// unknownOid can be transient while the order-status index catches up.
|
|
8958
|
+
// Treating it as a fill would drop the only handle to a live order.
|
|
8959
|
+
if (unknownStatusRetries > 0) {
|
|
8960
|
+
await new Promise((resolve) => setTimeout(resolve, CHASE_ORDER_STATUS_RETRY_DELAY_MS));
|
|
8961
|
+
return await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_readOrderRemainder).call(this, orderId, unknownStatusRetries - 1);
|
|
8962
|
+
}
|
|
8963
|
+
throw new ChaseOrderStatusUnavailableError();
|
|
7953
8964
|
}
|
|
7954
8965
|
const remaining = String(status.order.order.sz);
|
|
7955
|
-
|
|
8966
|
+
const remainingSize = parseFloat(remaining) > 0 ? remaining : null;
|
|
8967
|
+
switch (status.order.status) {
|
|
8968
|
+
case 'open':
|
|
8969
|
+
case 'triggered':
|
|
8970
|
+
return { remainingSize, terminalStatus: null };
|
|
8971
|
+
case 'filled':
|
|
8972
|
+
return {
|
|
8973
|
+
remainingSize: null,
|
|
8974
|
+
terminalStatus: CHASE_ORDER_STATUS.Filled,
|
|
8975
|
+
};
|
|
8976
|
+
case 'canceled':
|
|
8977
|
+
return remainingSize === null
|
|
8978
|
+
? {
|
|
8979
|
+
remainingSize: null,
|
|
8980
|
+
terminalStatus: CHASE_ORDER_STATUS.Filled,
|
|
8981
|
+
}
|
|
8982
|
+
: {
|
|
8983
|
+
remainingSize,
|
|
8984
|
+
terminalStatus: CHASE_ORDER_STATUS.Canceled,
|
|
8985
|
+
};
|
|
8986
|
+
default:
|
|
8987
|
+
return {
|
|
8988
|
+
remainingSize,
|
|
8989
|
+
terminalStatus: CHASE_ORDER_STATUS.Failed,
|
|
8990
|
+
};
|
|
8991
|
+
}
|
|
7956
8992
|
}, _HyperLiquidProvider_cancelChaseChild =
|
|
7957
8993
|
/**
|
|
7958
8994
|
* Cancel the order a chase session currently has resting.
|
|
@@ -7965,7 +9001,7 @@ async function _HyperLiquidProvider_readOrderRemainder(orderId) {
|
|
|
7965
9001
|
*/
|
|
7966
9002
|
async function _HyperLiquidProvider_cancelChaseChild(session, placingClient) {
|
|
7967
9003
|
if (!session.orderId) {
|
|
7968
|
-
return
|
|
9004
|
+
return CancelChildOutcome.Gone;
|
|
7969
9005
|
}
|
|
7970
9006
|
// Looked up only once there is something to cancel, so a session with
|
|
7971
9007
|
// nothing resting still answers on a provider whose client is already gone.
|
|
@@ -7979,7 +9015,7 @@ async function _HyperLiquidProvider_cancelChaseChild(session, placingClient) {
|
|
|
7979
9015
|
catch (error) {
|
|
7980
9016
|
const message = ensureError(error, 'HyperLiquidProvider.cancelChaseChild').message.toLowerCase();
|
|
7981
9017
|
if (ALREADY_GONE_CANCEL_MARKERS.some((marker) => message.includes(marker))) {
|
|
7982
|
-
return
|
|
9018
|
+
return CancelChildOutcome.Gone;
|
|
7983
9019
|
}
|
|
7984
9020
|
throw error;
|
|
7985
9021
|
}
|
|
@@ -8014,19 +9050,194 @@ async function _HyperLiquidProvider_retractOrphanedChaseOrder(session, placingCl
|
|
|
8014
9050
|
error: ensureError(error, 'HyperLiquidProvider.startChaseSession')
|
|
8015
9051
|
.message,
|
|
8016
9052
|
});
|
|
8017
|
-
return
|
|
9053
|
+
return CancelChildOutcome.Refused;
|
|
8018
9054
|
}
|
|
8019
9055
|
}, _HyperLiquidProvider_stopChaseSession = function _HyperLiquidProvider_stopChaseSession(sessionId) {
|
|
8020
9056
|
const session = __classPrivateFieldGet(this, _HyperLiquidProvider_chaseSessions, "f").get(sessionId);
|
|
8021
9057
|
if (!session) {
|
|
8022
9058
|
return;
|
|
8023
9059
|
}
|
|
9060
|
+
const wasActive = session.active;
|
|
8024
9061
|
session.active = false;
|
|
8025
|
-
|
|
9062
|
+
if (wasActive) {
|
|
9063
|
+
session.status = CHASE_ORDER_STATUS.TerminationPending;
|
|
9064
|
+
}
|
|
8026
9065
|
if (session.timer) {
|
|
8027
9066
|
clearTimeout(session.timer);
|
|
8028
9067
|
session.timer = null;
|
|
8029
9068
|
}
|
|
9069
|
+
}, _HyperLiquidProvider_rebalanceChaseSession =
|
|
9070
|
+
/**
|
|
9071
|
+
* Rebalance one Chase session at most once at a time. Failed cleanup keeps
|
|
9072
|
+
* the transfer context so a later lifecycle read or cancel can retry it.
|
|
9073
|
+
*
|
|
9074
|
+
* @param session - Chase session whose manual HIP-3 transfer is complete.
|
|
9075
|
+
* @returns Whether no collateral cleanup remains for the session.
|
|
9076
|
+
*/
|
|
9077
|
+
async function _HyperLiquidProvider_rebalanceChaseSession(session) {
|
|
9078
|
+
const { hip3Transfer } = session;
|
|
9079
|
+
if (!hip3Transfer) {
|
|
9080
|
+
return true;
|
|
9081
|
+
}
|
|
9082
|
+
if (session.orderId !== null ||
|
|
9083
|
+
session.replacingOrderId !== null ||
|
|
9084
|
+
session.pendingReplacement !== null) {
|
|
9085
|
+
return false;
|
|
9086
|
+
}
|
|
9087
|
+
let rebalancePromise = session.hip3RebalancePromise;
|
|
9088
|
+
if (!rebalancePromise) {
|
|
9089
|
+
rebalancePromise = __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_handleHip3PostOrderRebalance).call(this, hip3Transfer);
|
|
9090
|
+
session.hip3RebalancePromise = rebalancePromise;
|
|
9091
|
+
}
|
|
9092
|
+
try {
|
|
9093
|
+
const success = await rebalancePromise;
|
|
9094
|
+
if (success && session.hip3Transfer === hip3Transfer) {
|
|
9095
|
+
delete session.hip3Transfer;
|
|
9096
|
+
}
|
|
9097
|
+
return success;
|
|
9098
|
+
}
|
|
9099
|
+
finally {
|
|
9100
|
+
if (session.hip3RebalancePromise === rebalancePromise) {
|
|
9101
|
+
delete session.hip3RebalancePromise;
|
|
9102
|
+
}
|
|
9103
|
+
}
|
|
9104
|
+
}, _HyperLiquidProvider_adaptTwapOrder = function _HyperLiquidProvider_adaptTwapOrder(params) {
|
|
9105
|
+
const { historyEntry, now } = params;
|
|
9106
|
+
const { state, twapId } = historyEntry;
|
|
9107
|
+
if (twapId === undefined || !Number.isSafeInteger(twapId) || twapId < 0) {
|
|
9108
|
+
return null;
|
|
9109
|
+
}
|
|
9110
|
+
const totalSize = new BigNumber(state.sz);
|
|
9111
|
+
const executedSize = new BigNumber(state.executedSz);
|
|
9112
|
+
const executedNotional = new BigNumber(state.executedNtl);
|
|
9113
|
+
if (!totalSize.isFinite() ||
|
|
9114
|
+
!executedSize.isFinite() ||
|
|
9115
|
+
!executedNotional.isFinite() ||
|
|
9116
|
+
!Number.isFinite(state.timestamp) ||
|
|
9117
|
+
!Number.isFinite(historyEntry.time) ||
|
|
9118
|
+
!Number.isFinite(state.minutes)) {
|
|
9119
|
+
return null;
|
|
9120
|
+
}
|
|
9121
|
+
let boundedExecutedSize = executedSize;
|
|
9122
|
+
if (executedSize.isLessThan(0)) {
|
|
9123
|
+
boundedExecutedSize = new BigNumber(0);
|
|
9124
|
+
}
|
|
9125
|
+
else if (executedSize.isGreaterThan(totalSize)) {
|
|
9126
|
+
boundedExecutedSize = totalSize;
|
|
9127
|
+
}
|
|
9128
|
+
const rawRemainingSize = totalSize.minus(boundedExecutedSize);
|
|
9129
|
+
const remainingSize = rawRemainingSize.isGreaterThan(0)
|
|
9130
|
+
? rawRemainingSize
|
|
9131
|
+
: new BigNumber(0);
|
|
9132
|
+
const status = resolveTwapOrderStatus(historyEntry, boundedExecutedSize, totalSize);
|
|
9133
|
+
const fills = params.sliceFills
|
|
9134
|
+
.map(adaptTwapOrderFill)
|
|
9135
|
+
.sort((left, right) => left.timestamp - right.timestamp);
|
|
9136
|
+
const startedAt = state.timestamp;
|
|
9137
|
+
const historyUpdatedAt = normalizeTwapHistoryTimestamp(historyEntry.time);
|
|
9138
|
+
const lastUpdated = Math.max(startedAt, historyUpdatedAt, ...fills.map((fill) => fill.timestamp));
|
|
9139
|
+
const durationMilliseconds = state.minutes * MILLISECONDS_PER_MINUTE;
|
|
9140
|
+
const elapsedTimeMilliseconds = Math.min(durationMilliseconds, Math.max(0, (status === PerpsTwapLifecycleStatus.Active ? now : lastUpdated) -
|
|
9141
|
+
startedAt));
|
|
9142
|
+
const fillProgressBps = totalSize.isGreaterThan(0)
|
|
9143
|
+
? boundedExecutedSize
|
|
9144
|
+
.dividedBy(totalSize)
|
|
9145
|
+
.multipliedBy(BASIS_POINTS_DIVISOR)
|
|
9146
|
+
.integerValue(BigNumber.ROUND_FLOOR)
|
|
9147
|
+
.toNumber()
|
|
9148
|
+
: 0;
|
|
9149
|
+
const timeProgressBps = durationMilliseconds > 0
|
|
9150
|
+
? Math.floor((elapsedTimeMilliseconds / durationMilliseconds) *
|
|
9151
|
+
BASIS_POINTS_DIVISOR)
|
|
9152
|
+
: 0;
|
|
9153
|
+
return {
|
|
9154
|
+
orderId: twapId.toString(),
|
|
9155
|
+
symbol: state.coin,
|
|
9156
|
+
side: state.side === 'B' ? 'buy' : 'sell',
|
|
9157
|
+
size: state.sz,
|
|
9158
|
+
executedSize: boundedExecutedSize.toString(),
|
|
9159
|
+
remainingSize: remainingSize.toFixed(),
|
|
9160
|
+
executedNotional: state.executedNtl,
|
|
9161
|
+
...(executedSize.isGreaterThan(0) && {
|
|
9162
|
+
averagePrice: executedNotional.dividedBy(executedSize).toFixed(),
|
|
9163
|
+
}),
|
|
9164
|
+
fillProgressBps,
|
|
9165
|
+
timeProgressBps,
|
|
9166
|
+
elapsedTimeMilliseconds,
|
|
9167
|
+
durationMinutes: state.minutes,
|
|
9168
|
+
randomize: state.randomize,
|
|
9169
|
+
reduceOnly: state.reduceOnly,
|
|
9170
|
+
status,
|
|
9171
|
+
startedAt,
|
|
9172
|
+
lastUpdated,
|
|
9173
|
+
...(historyEntry.status.status ===
|
|
9174
|
+
HyperLiquidTwapLifecycleStatus.Failed && {
|
|
9175
|
+
error: historyEntry.status.description,
|
|
9176
|
+
}),
|
|
9177
|
+
fills,
|
|
9178
|
+
};
|
|
9179
|
+
}, _HyperLiquidProvider_rebalanceTrackedTwapOrder =
|
|
9180
|
+
/**
|
|
9181
|
+
* Rebalance one tracked TWAP at most once at a time. A failed transfer keeps
|
|
9182
|
+
* the tracking entry so the next lifecycle read or cancel can retry it.
|
|
9183
|
+
*
|
|
9184
|
+
* @param trackingKey - Account-scoped TWAP tracking key.
|
|
9185
|
+
* @returns Whether no cleanup remains for the entry.
|
|
9186
|
+
*/
|
|
9187
|
+
async function _HyperLiquidProvider_rebalanceTrackedTwapOrder(trackingKey) {
|
|
9188
|
+
const trackedOrder = __classPrivateFieldGet(this, _HyperLiquidProvider_trackedTwapOrders, "f").get(trackingKey);
|
|
9189
|
+
if (!trackedOrder) {
|
|
9190
|
+
return true;
|
|
9191
|
+
}
|
|
9192
|
+
if (!trackedOrder.hip3Transfer) {
|
|
9193
|
+
if (__classPrivateFieldGet(this, _HyperLiquidProvider_trackedTwapOrders, "f").get(trackingKey) === trackedOrder) {
|
|
9194
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_trackedTwapOrders, "f").delete(trackingKey);
|
|
9195
|
+
}
|
|
9196
|
+
return true;
|
|
9197
|
+
}
|
|
9198
|
+
let { rebalancePromise } = trackedOrder;
|
|
9199
|
+
if (!rebalancePromise) {
|
|
9200
|
+
rebalancePromise = __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_handleHip3PostOrderRebalance).call(this, trackedOrder.hip3Transfer);
|
|
9201
|
+
trackedOrder.rebalancePromise = rebalancePromise;
|
|
9202
|
+
}
|
|
9203
|
+
try {
|
|
9204
|
+
const success = await rebalancePromise;
|
|
9205
|
+
if (success &&
|
|
9206
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_trackedTwapOrders, "f").get(trackingKey) === trackedOrder) {
|
|
9207
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_trackedTwapOrders, "f").delete(trackingKey);
|
|
9208
|
+
}
|
|
9209
|
+
return success;
|
|
9210
|
+
}
|
|
9211
|
+
finally {
|
|
9212
|
+
if (__classPrivateFieldGet(this, _HyperLiquidProvider_trackedTwapOrders, "f").get(trackingKey) === trackedOrder &&
|
|
9213
|
+
trackedOrder.rebalancePromise === rebalancePromise) {
|
|
9214
|
+
delete trackedOrder.rebalancePromise;
|
|
9215
|
+
}
|
|
9216
|
+
}
|
|
9217
|
+
}, _HyperLiquidProvider_rebalanceTerminalHip3Twaps =
|
|
9218
|
+
/**
|
|
9219
|
+
* Return manually moved HIP-3 collateral after a TWAP reaches a terminal
|
|
9220
|
+
* venue state. The scoped registry survives reconnects so an active schedule
|
|
9221
|
+
* cannot lose its eventual cleanup path.
|
|
9222
|
+
*
|
|
9223
|
+
* @param orders - Current and historical TWAP lifecycle records.
|
|
9224
|
+
* @param scope - Current account and network identity.
|
|
9225
|
+
*/
|
|
9226
|
+
async function _HyperLiquidProvider_rebalanceTerminalHip3Twaps(orders, scope) {
|
|
9227
|
+
for (const order of orders) {
|
|
9228
|
+
if (order.status === PerpsTwapLifecycleStatus.Active) {
|
|
9229
|
+
continue;
|
|
9230
|
+
}
|
|
9231
|
+
const trackingKey = getTwapOrderScopeKey({
|
|
9232
|
+
...scope,
|
|
9233
|
+
orderId: order.orderId,
|
|
9234
|
+
});
|
|
9235
|
+
const trackedOrder = __classPrivateFieldGet(this, _HyperLiquidProvider_trackedTwapOrders, "f").get(trackingKey);
|
|
9236
|
+
if (trackedOrder?.symbol !== order.symbol) {
|
|
9237
|
+
continue;
|
|
9238
|
+
}
|
|
9239
|
+
await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_rebalanceTrackedTwapOrder).call(this, trackingKey);
|
|
9240
|
+
}
|
|
8030
9241
|
}, _HyperLiquidProvider_cancelStrategyOrder =
|
|
8031
9242
|
/**
|
|
8032
9243
|
* Cancel a strategy placement by its handle.
|
|
@@ -8073,21 +9284,64 @@ async function _HyperLiquidProvider_cancelTwapOrder(params) {
|
|
|
8073
9284
|
if (!coinValidation.isValid) {
|
|
8074
9285
|
throw new Error(coinValidation.error);
|
|
8075
9286
|
}
|
|
8076
|
-
|
|
9287
|
+
const twapId = Number(params.orderId);
|
|
9288
|
+
if (!/^\d+$/u.test(params.orderId) || !Number.isSafeInteger(twapId)) {
|
|
9289
|
+
throw new Error(PERPS_ERROR_CODES.ORDER_STRATEGY_HANDLE_UNKNOWN);
|
|
9290
|
+
}
|
|
9291
|
+
await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_ensureReadyForTrading).call(this, { requiresBuilderFee: false });
|
|
9292
|
+
const network = __classPrivateFieldGet(this, _HyperLiquidProvider_clientService, "f").isTestnetMode() ? 'testnet' : 'mainnet';
|
|
9293
|
+
const userAddress = await __classPrivateFieldGet(this, _HyperLiquidProvider_walletService, "f").getUserAddressWithDefault();
|
|
9294
|
+
const trackingKey = getTwapOrderScopeKey({
|
|
9295
|
+
network,
|
|
9296
|
+
userAddress,
|
|
9297
|
+
orderId: params.orderId,
|
|
9298
|
+
});
|
|
9299
|
+
const trackedOrder = __classPrivateFieldGet(this, _HyperLiquidProvider_trackedTwapOrders, "f").get(trackingKey);
|
|
9300
|
+
let ownershipAuthenticated = trackedOrder !== undefined;
|
|
9301
|
+
if (trackedOrder) {
|
|
9302
|
+
if (trackedOrder.symbol !== params.symbol) {
|
|
9303
|
+
throw new Error(PERPS_ERROR_CODES.ORDER_STRATEGY_HANDLE_UNKNOWN);
|
|
9304
|
+
}
|
|
9305
|
+
}
|
|
9306
|
+
else {
|
|
9307
|
+
let venueOrders = null;
|
|
9308
|
+
try {
|
|
9309
|
+
venueOrders = await this.getTwapOrders();
|
|
9310
|
+
}
|
|
9311
|
+
catch (error) {
|
|
9312
|
+
// History is an ownership check, not the cancellation transport. A
|
|
9313
|
+
// transient read failure must not make a live TWAP impossible to stop.
|
|
9314
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('TWAP history unavailable before cancellation', {
|
|
9315
|
+
orderId: params.orderId,
|
|
9316
|
+
error: ensureError(error, 'HyperLiquidProvider.cancelTwapOrderHistory').message,
|
|
9317
|
+
});
|
|
9318
|
+
}
|
|
9319
|
+
const venueOrder = venueOrders?.find((order) => order.orderId === params.orderId);
|
|
9320
|
+
if (venueOrders !== null && venueOrder?.symbol !== params.symbol) {
|
|
9321
|
+
throw new Error(PERPS_ERROR_CODES.ORDER_STRATEGY_HANDLE_UNKNOWN);
|
|
9322
|
+
}
|
|
9323
|
+
ownershipAuthenticated = venueOrder !== undefined;
|
|
9324
|
+
}
|
|
8077
9325
|
const assetId = await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_getAssetIdWithRepair).call(this, {
|
|
8078
9326
|
symbol: params.symbol,
|
|
8079
9327
|
dexName: parseAssetName(params.symbol).dex,
|
|
8080
9328
|
});
|
|
8081
9329
|
const result = await __classPrivateFieldGet(this, _HyperLiquidProvider_clientService, "f").getExchangeClient().twapCancel({
|
|
8082
9330
|
a: assetId,
|
|
8083
|
-
t:
|
|
9331
|
+
t: twapId,
|
|
8084
9332
|
});
|
|
8085
9333
|
const status = result.response?.data?.status;
|
|
8086
|
-
|
|
9334
|
+
const cancelOutcome = classifyCancelStatus(status);
|
|
9335
|
+
if (cancelOutcome === CancelChildOutcome.Gone && !ownershipAuthenticated) {
|
|
9336
|
+
throw new Error(PERPS_ERROR_CODES.ORDER_STRATEGY_HANDLE_UNKNOWN);
|
|
9337
|
+
}
|
|
9338
|
+
if (cancelOutcome !== CancelChildOutcome.Refused) {
|
|
9339
|
+
await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_rebalanceTrackedTwapOrder).call(this, trackingKey);
|
|
8087
9340
|
return { success: true, orderId: params.orderId };
|
|
8088
9341
|
}
|
|
8089
|
-
const rawError = status
|
|
8090
|
-
|
|
9342
|
+
const rawError = isStatusObject(status) && typeof status.error === 'string'
|
|
9343
|
+
? status.error
|
|
9344
|
+
: 'TWAP cancellation failed';
|
|
8091
9345
|
return createErrorResult(__classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_mapError).call(this, new Error(rawError)), {
|
|
8092
9346
|
success: false,
|
|
8093
9347
|
orderId: params.orderId,
|
|
@@ -8104,19 +9358,25 @@ async function _HyperLiquidProvider_cancelScaleOrder(params) {
|
|
|
8104
9358
|
if (!group) {
|
|
8105
9359
|
throw new Error(PERPS_ERROR_CODES.ORDER_STRATEGY_HANDLE_UNKNOWN);
|
|
8106
9360
|
}
|
|
8107
|
-
await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_ensureReadyForTrading).call(this);
|
|
8108
|
-
const
|
|
8109
|
-
|
|
8110
|
-
|
|
8111
|
-
o: parseInt(orderId, 10),
|
|
8112
|
-
})),
|
|
9361
|
+
await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_ensureReadyForTrading).call(this, { requiresBuilderFee: false });
|
|
9362
|
+
const assetId = await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_getAssetIdWithRepair).call(this, {
|
|
9363
|
+
symbol: group.symbol,
|
|
9364
|
+
dexName: parseAssetName(group.symbol).dex,
|
|
8113
9365
|
});
|
|
8114
|
-
const
|
|
8115
|
-
|
|
8116
|
-
|
|
8117
|
-
|
|
8118
|
-
|
|
9366
|
+
const exchangeClient = __classPrivateFieldGet(this, _HyperLiquidProvider_clientService, "f").getExchangeClient();
|
|
9367
|
+
const cancelRequests = group.orderIds.map((orderId) => ({
|
|
9368
|
+
a: assetId,
|
|
9369
|
+
o: Number(orderId),
|
|
9370
|
+
}));
|
|
9371
|
+
const remaining = (await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_cancelOrderRequests).call(this, exchangeClient, cancelRequests)).map(String);
|
|
9372
|
+
/*
|
|
9373
|
+
* A rung that filled or was cancelled individually comes back as a
|
|
9374
|
+
* rejection, but nothing of it is resting. The shared cancellation helper
|
|
9375
|
+
* distinguishes that result from a refusal while retaining every child
|
|
9376
|
+
* after a malformed or non-ok batch response.
|
|
9377
|
+
*/
|
|
8119
9378
|
if (remaining.length === 0) {
|
|
9379
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_cancelledScaleOrderGroups, "f").add(params.orderId);
|
|
8120
9380
|
__classPrivateFieldGet(this, _HyperLiquidProvider_scaleOrderGroups, "f").delete(params.orderId);
|
|
8121
9381
|
return { success: true, orderId: params.orderId };
|
|
8122
9382
|
}
|
|
@@ -8124,7 +9384,7 @@ async function _HyperLiquidProvider_cancelScaleOrder(params) {
|
|
|
8124
9384
|
// handle registered against what is left means the caller can retry the
|
|
8125
9385
|
// same cancel rather than being told the ladder is gone when it is not.
|
|
8126
9386
|
__classPrivateFieldGet(this, _HyperLiquidProvider_scaleOrderGroups, "f").set(params.orderId, {
|
|
8127
|
-
|
|
9387
|
+
symbol: group.symbol,
|
|
8128
9388
|
orderIds: remaining,
|
|
8129
9389
|
});
|
|
8130
9390
|
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('Scale group cancel left children resting', {
|
|
@@ -8185,39 +9445,120 @@ async function _HyperLiquidProvider_performChaseTermination(params) {
|
|
|
8185
9445
|
// Nothing rests: the order filled, something else cancelled it, or a
|
|
8186
9446
|
// replacement failed to go up. Either way the session ends cleanly.
|
|
8187
9447
|
if (session.orderId === null) {
|
|
8188
|
-
|
|
9448
|
+
session.removeAfterRebalance = true;
|
|
9449
|
+
if (await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_rebalanceChaseSession).call(this, session)) {
|
|
9450
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_chaseSessions, "f").delete(params.orderId);
|
|
9451
|
+
}
|
|
8189
9452
|
return { success: true, orderId: params.orderId };
|
|
8190
9453
|
}
|
|
8191
|
-
await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_ensureReadyForTrading).call(this);
|
|
9454
|
+
await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_ensureReadyForTrading).call(this, { requiresBuilderFee: false });
|
|
8192
9455
|
// Only a refusal leaves an order behind. A child that had already filled or
|
|
8193
9456
|
// been cancelled is reported as a rejection too, but nothing of it is
|
|
8194
9457
|
// resting — treating that as a failure would pin the handle open forever on
|
|
8195
9458
|
// a chase that is entirely finished.
|
|
8196
9459
|
const cancelOutcome = await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_cancelChaseChild).call(this, session);
|
|
8197
|
-
if (cancelOutcome ===
|
|
9460
|
+
if (cancelOutcome === CancelChildOutcome.Refused) {
|
|
8198
9461
|
// The order is still resting. The session stays registered — stopped, but
|
|
8199
9462
|
// still cancellable — so the caller can retry with the same handle.
|
|
8200
9463
|
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('Chase cancel left its order resting', {
|
|
8201
9464
|
sessionId: params.orderId,
|
|
8202
9465
|
orderId: session.orderId,
|
|
8203
9466
|
});
|
|
9467
|
+
await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_rebalanceChaseSession).call(this, session);
|
|
8204
9468
|
return createErrorResult(new Error(PERPS_ERROR_CODES.ORDER_STRATEGY_CANCEL_INCOMPLETE), { success: false, orderId: params.orderId });
|
|
8205
9469
|
}
|
|
8206
|
-
|
|
9470
|
+
session.orderId = null;
|
|
9471
|
+
session.status = CHASE_ORDER_STATUS.Canceled;
|
|
9472
|
+
session.removeAfterRebalance = true;
|
|
9473
|
+
if (await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_rebalanceChaseSession).call(this, session)) {
|
|
9474
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_chaseSessions, "f").delete(params.orderId);
|
|
9475
|
+
}
|
|
8207
9476
|
return { success: true, orderId: params.orderId };
|
|
8208
|
-
},
|
|
9477
|
+
}, _HyperLiquidProvider_cancelOrderRequests =
|
|
9478
|
+
/**
|
|
9479
|
+
* Cancel a batch and retain every order that may still be live.
|
|
9480
|
+
*
|
|
9481
|
+
* A malformed, truncated, or rejected response cannot prove which requests
|
|
9482
|
+
* reached the venue, so every requested ID remains recoverable for a retry.
|
|
9483
|
+
*
|
|
9484
|
+
* @param exchangeClient - Client that owns the orders.
|
|
9485
|
+
* @param requests - Venue cancel requests.
|
|
9486
|
+
* @returns Order IDs that may still be resting.
|
|
9487
|
+
*/
|
|
9488
|
+
async function _HyperLiquidProvider_cancelOrderRequests(exchangeClient, requests) {
|
|
9489
|
+
return (await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_cancelOrderRequestBatch).call(this, exchangeClient, requests))
|
|
9490
|
+
.remainingOrderIds;
|
|
9491
|
+
}, _HyperLiquidProvider_cancelOrderRequestBatch =
|
|
9492
|
+
/**
|
|
9493
|
+
* Cancel a batch while distinguishing confirmed cancellations from orders
|
|
9494
|
+
* that were already gone. Replacement rollback may only restore the former.
|
|
9495
|
+
*
|
|
9496
|
+
* @param exchangeClient - Client that owns the orders.
|
|
9497
|
+
* @param requests - Venue cancel requests.
|
|
9498
|
+
* @returns IDs that may still rest and IDs this call confirmed it cancelled.
|
|
9499
|
+
*/
|
|
9500
|
+
async function _HyperLiquidProvider_cancelOrderRequestBatch(exchangeClient, requests) {
|
|
9501
|
+
if (requests.length === 0) {
|
|
9502
|
+
return {
|
|
9503
|
+
remainingOrderIds: [],
|
|
9504
|
+
cancelledOrderIds: [],
|
|
9505
|
+
responseComplete: true,
|
|
9506
|
+
};
|
|
9507
|
+
}
|
|
9508
|
+
try {
|
|
9509
|
+
const result = await exchangeClient.cancel({ cancels: requests });
|
|
9510
|
+
const statuses = result.response?.data?.statuses ?? [];
|
|
9511
|
+
if (result.status !== 'ok' || statuses.length !== requests.length) {
|
|
9512
|
+
return {
|
|
9513
|
+
remainingOrderIds: requests.map((request) => request.o),
|
|
9514
|
+
cancelledOrderIds: [],
|
|
9515
|
+
responseComplete: false,
|
|
9516
|
+
};
|
|
9517
|
+
}
|
|
9518
|
+
const remainingOrderIds = [];
|
|
9519
|
+
const cancelledOrderIds = [];
|
|
9520
|
+
requests.forEach((request, index) => {
|
|
9521
|
+
const outcome = classifyCancelStatus(statuses[index]);
|
|
9522
|
+
if (outcome === CancelChildOutcome.Refused) {
|
|
9523
|
+
remainingOrderIds.push(request.o);
|
|
9524
|
+
}
|
|
9525
|
+
else if (outcome === CancelChildOutcome.Cancelled) {
|
|
9526
|
+
cancelledOrderIds.push(request.o);
|
|
9527
|
+
}
|
|
9528
|
+
});
|
|
9529
|
+
return { remainingOrderIds, cancelledOrderIds, responseComplete: true };
|
|
9530
|
+
}
|
|
9531
|
+
catch (error) {
|
|
9532
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('Order cancellation batch failed', {
|
|
9533
|
+
error: ensureError(error, 'HyperLiquidProvider.cancelOrderRequests')
|
|
9534
|
+
.message,
|
|
9535
|
+
orderIds: requests.map((request) => request.o),
|
|
9536
|
+
});
|
|
9537
|
+
return {
|
|
9538
|
+
remainingOrderIds: requests.map((request) => request.o),
|
|
9539
|
+
cancelledOrderIds: [],
|
|
9540
|
+
responseComplete: false,
|
|
9541
|
+
};
|
|
9542
|
+
}
|
|
9543
|
+
}, _HyperLiquidProvider_readOrderPlacementOutcome = function _HyperLiquidProvider_readOrderPlacementOutcome(status) {
|
|
8209
9544
|
if (!isStatusObject(status)) {
|
|
8210
9545
|
return undefined;
|
|
8211
9546
|
}
|
|
8212
|
-
|
|
8213
|
-
|
|
8214
|
-
|
|
8215
|
-
|
|
8216
|
-
|
|
8217
|
-
|
|
8218
|
-
|
|
8219
|
-
|
|
8220
|
-
|
|
9547
|
+
for (const state of ['resting', 'filled']) {
|
|
9548
|
+
if (!hasProperty(status, state)) {
|
|
9549
|
+
continue;
|
|
9550
|
+
}
|
|
9551
|
+
const order = status[state];
|
|
9552
|
+
if (isStatusObject(order) &&
|
|
9553
|
+
typeof order.oid === 'number' &&
|
|
9554
|
+
Number.isSafeInteger(order.oid) &&
|
|
9555
|
+
order.oid >= 0) {
|
|
9556
|
+
return { orderId: order.oid.toString(), state };
|
|
9557
|
+
}
|
|
9558
|
+
}
|
|
9559
|
+
return undefined;
|
|
9560
|
+
}, _HyperLiquidProvider_readOrderIdFromStatus = function _HyperLiquidProvider_readOrderIdFromStatus(status) {
|
|
9561
|
+
return __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_readOrderPlacementOutcome).call(this, status)?.orderId;
|
|
8221
9562
|
}, _HyperLiquidProvider_getDiscountedBuilderFee = function _HyperLiquidProvider_getDiscountedBuilderFee() {
|
|
8222
9563
|
if (__classPrivateFieldGet(this, _HyperLiquidProvider_userFeeDiscountBips, "f") === undefined) {
|
|
8223
9564
|
return BUILDER_FEE_CONFIG.MaxFeeTenthsBps;
|
|
@@ -8232,15 +9573,14 @@ async function _HyperLiquidProvider_performChaseTermination(params) {
|
|
|
8232
9573
|
* cached for this provider/account session. Until then, the ordinary builder
|
|
8233
9574
|
* and standard fee keep the trade attributable and non-blocking.
|
|
8234
9575
|
*
|
|
9576
|
+
* @param setupContext - Account, network, and builder approved for the order.
|
|
8235
9577
|
* @returns HyperLiquid builder address and fee payload.
|
|
8236
9578
|
*/
|
|
8237
|
-
async function _HyperLiquidProvider_getBuilderOrderContext() {
|
|
8238
|
-
const
|
|
8239
|
-
const
|
|
8240
|
-
const defaultBuilder = __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_getBuilderAddress).call(this, isTestnet);
|
|
9579
|
+
async function _HyperLiquidProvider_getBuilderOrderContext(setupContext) {
|
|
9580
|
+
const { network, userAddress, builderAddress: defaultBuilder, } = setupContext;
|
|
9581
|
+
const isTestnet = network === 'testnet';
|
|
8241
9582
|
if (__classPrivateFieldGet(this, _HyperLiquidProvider_userFeeResolution, "f")?.source === 'subscription') {
|
|
8242
9583
|
const subscriptionBuilder = __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_getSubscriptionBuilderAddress).call(this, isTestnet);
|
|
8243
|
-
const userAddress = await __classPrivateFieldGet(this, _HyperLiquidProvider_walletService, "f").getUserAddressWithDefault();
|
|
8244
9584
|
if (subscriptionBuilder &&
|
|
8245
9585
|
__classPrivateFieldGet(this, _HyperLiquidProvider_approvedBuilderAddresses, "f").has(__classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_getApprovedBuilderKey).call(this, network, userAddress, subscriptionBuilder))) {
|
|
8246
9586
|
return { b: subscriptionBuilder, f: 0 };
|
|
@@ -8390,6 +9730,41 @@ async function _HyperLiquidProvider_queryDexPositions(dexName) {
|
|
|
8390
9730
|
});
|
|
8391
9731
|
return { answered: false, positions: [] };
|
|
8392
9732
|
}
|
|
9733
|
+
}, _HyperLiquidProvider_restoreScaleOrderGroups = function _HyperLiquidProvider_restoreScaleOrderGroups(orders) {
|
|
9734
|
+
const recovered = new Map();
|
|
9735
|
+
for (const order of orders) {
|
|
9736
|
+
if (!order.strategyGroupId) {
|
|
9737
|
+
continue;
|
|
9738
|
+
}
|
|
9739
|
+
const group = recovered.get(order.strategyGroupId) ?? {
|
|
9740
|
+
symbol: order.symbol,
|
|
9741
|
+
orderIds: [],
|
|
9742
|
+
};
|
|
9743
|
+
group.orderIds.push(order.orderId);
|
|
9744
|
+
recovered.set(order.strategyGroupId, group);
|
|
9745
|
+
}
|
|
9746
|
+
for (const [groupId, group] of recovered) {
|
|
9747
|
+
if (__classPrivateFieldGet(this, _HyperLiquidProvider_cancelledScaleOrderGroups, "f").has(groupId)) {
|
|
9748
|
+
continue;
|
|
9749
|
+
}
|
|
9750
|
+
const existingGroup = __classPrivateFieldGet(this, _HyperLiquidProvider_scaleOrderGroups, "f").get(groupId);
|
|
9751
|
+
if (!existingGroup) {
|
|
9752
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_scaleOrderGroups, "f").set(groupId, group);
|
|
9753
|
+
continue;
|
|
9754
|
+
}
|
|
9755
|
+
if (existingGroup.symbol === group.symbol) {
|
|
9756
|
+
existingGroup.orderIds = [
|
|
9757
|
+
...new Set([...existingGroup.orderIds, ...group.orderIds]),
|
|
9758
|
+
];
|
|
9759
|
+
}
|
|
9760
|
+
else {
|
|
9761
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('Scale group symbol mismatch during recovery', {
|
|
9762
|
+
groupId,
|
|
9763
|
+
existingSymbol: existingGroup.symbol,
|
|
9764
|
+
recoveredSymbol: group.symbol,
|
|
9765
|
+
});
|
|
9766
|
+
}
|
|
9767
|
+
}
|
|
8393
9768
|
}, _HyperLiquidProvider_getAllMids =
|
|
8394
9769
|
/**
|
|
8395
9770
|
* Get allMids for a DEX — uses WS snapshot as primary source, REST as fallback.
|
|
@@ -8406,7 +9781,7 @@ async function _HyperLiquidProvider_getAllMids(infoClient, dexParam) {
|
|
|
8406
9781
|
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('[getMarketDataWithPrices] No WS allMids snapshot, falling back to REST', { dexParam: dexParam ?? 'main' });
|
|
8407
9782
|
const mids = await infoClient.allMids(dexParam ? { dex: dexParam } : undefined);
|
|
8408
9783
|
return mids ?? {};
|
|
8409
|
-
}, _HyperLiquidProvider_fetchSingleDexFresh = async function _HyperLiquidProvider_fetchSingleDexFresh(infoClient, dex) {
|
|
9784
|
+
}, _HyperLiquidProvider_fetchSingleDexFresh = async function _HyperLiquidProvider_fetchSingleDexFresh(infoClient, dex, lifecycleGeneration) {
|
|
8410
9785
|
const dexParam = dex ?? '';
|
|
8411
9786
|
let metaAndCtxs = null;
|
|
8412
9787
|
try {
|
|
@@ -8434,10 +9809,12 @@ async function _HyperLiquidProvider_getAllMids(infoClient, dexParam) {
|
|
|
8434
9809
|
allMidsErrorMessage = ensureError(error, 'HyperLiquidProvider.getMarketDataWithPrices.allMids').message;
|
|
8435
9810
|
}
|
|
8436
9811
|
if (meta?.universe) {
|
|
8437
|
-
__classPrivateFieldGet(this,
|
|
8438
|
-
|
|
8439
|
-
|
|
8440
|
-
|
|
9812
|
+
await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_cacheDexMetadataFromRead).call(this, {
|
|
9813
|
+
dex,
|
|
9814
|
+
meta,
|
|
9815
|
+
assetCtxs,
|
|
9816
|
+
lifecycleGeneration,
|
|
9817
|
+
});
|
|
8441
9818
|
}
|
|
8442
9819
|
return {
|
|
8443
9820
|
dex,
|
|
@@ -8540,6 +9917,128 @@ async function _HyperLiquidProvider_excludeNonUsdcCollateralResults(results) {
|
|
|
8540
9917
|
return false;
|
|
8541
9918
|
}
|
|
8542
9919
|
return Date.now() - cached.timestamp < cached.ttl;
|
|
9920
|
+
}, _HyperLiquidProvider_performDisconnect =
|
|
9921
|
+
/**
|
|
9922
|
+
* Perform one provider teardown shared by every concurrent caller.
|
|
9923
|
+
*
|
|
9924
|
+
* @returns A promise that resolves to the result.
|
|
9925
|
+
*/
|
|
9926
|
+
async function _HyperLiquidProvider_performDisconnect() {
|
|
9927
|
+
__classPrivateFieldSet(this, _HyperLiquidProvider_chasePlacementBlockers, __classPrivateFieldGet(this, _HyperLiquidProvider_chasePlacementBlockers, "f") + 1, "f");
|
|
9928
|
+
__classPrivateFieldSet(this, _HyperLiquidProvider_strategyGeneration, __classPrivateFieldGet(this, _HyperLiquidProvider_strategyGeneration, "f") + 1, "f");
|
|
9929
|
+
__classPrivateFieldSet(this, _HyperLiquidProvider_disconnectOperationsInFlight, __classPrivateFieldGet(this, _HyperLiquidProvider_disconnectOperationsInFlight, "f") + 1, "f");
|
|
9930
|
+
__classPrivateFieldSet(this, _HyperLiquidProvider_isDisconnected, true, "f");
|
|
9931
|
+
__classPrivateFieldSet(this, _HyperLiquidProvider_lifecycleGeneration, __classPrivateFieldGet(this, _HyperLiquidProvider_lifecycleGeneration, "f") + 1, "f");
|
|
9932
|
+
try {
|
|
9933
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('HyperLiquid: Disconnecting provider', {
|
|
9934
|
+
isTestnet: __classPrivateFieldGet(this, _HyperLiquidProvider_clientService, "f").isTestnetMode(),
|
|
9935
|
+
timestamp: new Date().toISOString(),
|
|
9936
|
+
});
|
|
9937
|
+
// Clear subscriptions through subscription service
|
|
9938
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_subscriptionService, "f").clearAll();
|
|
9939
|
+
// Stop every chase loop: each holds a pending timer, and re-pricing after
|
|
9940
|
+
// a disconnect would sign orders against a client that is being torn down.
|
|
9941
|
+
// Orders already resting are deliberately left alone — disconnecting is
|
|
9942
|
+
// not a request to cancel the user's positions or orders.
|
|
9943
|
+
// Invalidate placements first, then drain every mutation before clearing
|
|
9944
|
+
// the registry and tearing down the clients they use.
|
|
9945
|
+
for (const [sessionId, session] of __classPrivateFieldGet(this, _HyperLiquidProvider_chaseSessions, "f").entries()) {
|
|
9946
|
+
if (session.active) {
|
|
9947
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_stopChaseSession).call(this, sessionId);
|
|
9948
|
+
}
|
|
9949
|
+
}
|
|
9950
|
+
await Promise.all([...__classPrivateFieldGet(this, _HyperLiquidProvider_chasePlacementWaiters, "f")]);
|
|
9951
|
+
await __classPrivateFieldGet(this, _HyperLiquidProvider_chaseTickQueue, "f");
|
|
9952
|
+
await Promise.allSettled([...__classPrivateFieldGet(this, _HyperLiquidProvider_chaseTerminations, "f").values()]);
|
|
9953
|
+
await Promise.all([...__classPrivateFieldGet(this, _HyperLiquidProvider_chaseSessions, "f").values()].map(async (session) => await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_rebalanceChaseSession).call(this, session)));
|
|
9954
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_chaseSessions, "f").clear();
|
|
9955
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_chaseTerminations, "f").clear();
|
|
9956
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_terminatedChaseHandles, "f").clear();
|
|
9957
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_scaleOrderGroups, "f").clear();
|
|
9958
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_cancelledScaleOrderGroups, "f").clear();
|
|
9959
|
+
// Clear session caches. Capability and fee reads use the lifecycle
|
|
9960
|
+
// generation above to discard any old response that resolves later.
|
|
9961
|
+
this.clearFeeCache();
|
|
9962
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_referralCheckCache, "f").clear();
|
|
9963
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_builderFeeCheckCache, "f").clear();
|
|
9964
|
+
__classPrivateFieldSet(this, _HyperLiquidProvider_subscriptionBuilderApprovalEpoch, __classPrivateFieldGet(this, _HyperLiquidProvider_subscriptionBuilderApprovalEpoch, "f") + 1, "f");
|
|
9965
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_approvedBuilderAddresses, "f").clear();
|
|
9966
|
+
__classPrivateFieldSet(this, _HyperLiquidProvider_userFeeResolution, undefined, "f");
|
|
9967
|
+
__classPrivateFieldSet(this, _HyperLiquidProvider_userFeeDiscountBips, undefined, "f");
|
|
9968
|
+
// UnifiedAccountCache stays global to avoid repeated signing requests.
|
|
9969
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_cachedMetaByDex, "f").clear();
|
|
9970
|
+
__classPrivateFieldSet(this, _HyperLiquidProvider_pendingValidatedDexsPromise, null, "f");
|
|
9971
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_orderCapabilitiesMarketsByDex, "f").clear();
|
|
9972
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_orderCapabilitiesRefreshByDex, "f").clear();
|
|
9973
|
+
__classPrivateFieldSet(this, _HyperLiquidProvider_cachedSpotMeta, null, "f");
|
|
9974
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_dexDiscoveryCache, "f").reset();
|
|
9975
|
+
__classPrivateFieldSet(this, _HyperLiquidProvider_dexDiscoveryComplete, false, "f");
|
|
9976
|
+
// Await pending initialization before clearing to prevent the IIFE from
|
|
9977
|
+
// setting clientsInitialized = true after disconnect completes
|
|
9978
|
+
const pendingInit = __classPrivateFieldGet(this, _HyperLiquidProvider_initializationPromise, "f");
|
|
9979
|
+
const pendingReady = __classPrivateFieldGet(this, _HyperLiquidProvider_ensureReadyPromise, "f");
|
|
9980
|
+
const pendingTradingSetup = __classPrivateFieldGet(this, _HyperLiquidProvider_tradingSetupPromise, "f");
|
|
9981
|
+
const pendingBuilderFeeSetups = [
|
|
9982
|
+
...__classPrivateFieldGet(this, _HyperLiquidProvider_builderFeeSetupPromises, "f").values(),
|
|
9983
|
+
];
|
|
9984
|
+
// Clear references first to prevent new callers from reusing
|
|
9985
|
+
__classPrivateFieldSet(this, _HyperLiquidProvider_initializationPromise, null, "f");
|
|
9986
|
+
__classPrivateFieldSet(this, _HyperLiquidProvider_ensureReadyPromise, null, "f");
|
|
9987
|
+
__classPrivateFieldSet(this, _HyperLiquidProvider_tradingSetupPromise, null, "f");
|
|
9988
|
+
__classPrivateFieldSet(this, _HyperLiquidProvider_tradingSetupComplete, false, "f");
|
|
9989
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_builderFeeSetupPromises, "f").clear();
|
|
9990
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_pendingBuilderFeeApprovals, "f").clear();
|
|
9991
|
+
// Wait for pending operations to complete (ignore errors)
|
|
9992
|
+
// This prevents IIFEs from setting state after disconnect completes
|
|
9993
|
+
if (pendingInit) {
|
|
9994
|
+
try {
|
|
9995
|
+
await pendingInit;
|
|
9996
|
+
}
|
|
9997
|
+
catch {
|
|
9998
|
+
// Ignore - we're disconnecting anyway
|
|
9999
|
+
}
|
|
10000
|
+
}
|
|
10001
|
+
if (pendingReady) {
|
|
10002
|
+
try {
|
|
10003
|
+
await pendingReady;
|
|
10004
|
+
}
|
|
10005
|
+
catch {
|
|
10006
|
+
// Ignore - we're disconnecting anyway
|
|
10007
|
+
}
|
|
10008
|
+
}
|
|
10009
|
+
if (pendingTradingSetup) {
|
|
10010
|
+
try {
|
|
10011
|
+
await pendingTradingSetup;
|
|
10012
|
+
}
|
|
10013
|
+
catch {
|
|
10014
|
+
// Ignore - we're disconnecting anyway
|
|
10015
|
+
}
|
|
10016
|
+
}
|
|
10017
|
+
for (const pendingBuilderFeeSetup of pendingBuilderFeeSetups) {
|
|
10018
|
+
try {
|
|
10019
|
+
await pendingBuilderFeeSetup;
|
|
10020
|
+
}
|
|
10021
|
+
catch {
|
|
10022
|
+
// Ignore - we're disconnecting anyway
|
|
10023
|
+
}
|
|
10024
|
+
}
|
|
10025
|
+
// Reset client initialization flag so wallet adapter will be recreated with new account
|
|
10026
|
+
// This fixes account synchronization issue where old account's address persists in wallet adapter
|
|
10027
|
+
__classPrivateFieldSet(this, _HyperLiquidProvider_clientsInitialized, false, "f");
|
|
10028
|
+
// Disconnect client service
|
|
10029
|
+
await __classPrivateFieldGet(this, _HyperLiquidProvider_clientService, "f").disconnect();
|
|
10030
|
+
__classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('HyperLiquid: Provider fully disconnected', {
|
|
10031
|
+
timestamp: new Date().toISOString(),
|
|
10032
|
+
});
|
|
10033
|
+
return { success: true };
|
|
10034
|
+
}
|
|
10035
|
+
catch (error) {
|
|
10036
|
+
return createErrorResult(error, { success: false });
|
|
10037
|
+
}
|
|
10038
|
+
finally {
|
|
10039
|
+
__classPrivateFieldSet(this, _HyperLiquidProvider_chasePlacementBlockers, __classPrivateFieldGet(this, _HyperLiquidProvider_chasePlacementBlockers, "f") - 1, "f");
|
|
10040
|
+
__classPrivateFieldSet(this, _HyperLiquidProvider_disconnectOperationsInFlight, __classPrivateFieldGet(this, _HyperLiquidProvider_disconnectOperationsInFlight, "f") - 1, "f");
|
|
10041
|
+
}
|
|
8543
10042
|
}, _HyperLiquidProvider_getBuilderAddress = function _HyperLiquidProvider_getBuilderAddress(isTestnet) {
|
|
8544
10043
|
// || intentional: env vars default to '' which must fall through to the hardcoded default
|
|
8545
10044
|
if (isTestnet) {
|
|
@@ -8793,4 +10292,9 @@ async function _HyperLiquidProvider_setReferralCode() {
|
|
|
8793
10292
|
throw error;
|
|
8794
10293
|
}
|
|
8795
10294
|
};
|
|
10295
|
+
// Native TWAP IDs and any manual HIP-3 collateral transfer must outlive one
|
|
10296
|
+
// provider instance: controller disconnect/reinitialization replaces the
|
|
10297
|
+
// provider while the venue schedule keeps running. Each controller messenger
|
|
10298
|
+
// owns one registry, so separate controller lifecycles never share handles.
|
|
10299
|
+
_HyperLiquidProvider_trackedTwapOrdersByMessenger = { value: new WeakMap() };
|
|
8796
10300
|
//# sourceMappingURL=HyperLiquidProvider.mjs.map
|