@metamask-previews/perps-controller 9.3.0-preview-02ebbf830 → 9.3.0-preview-acc8294b8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/dist/constants/hyperLiquidConfig.cjs +2 -18
  3. package/dist/constants/hyperLiquidConfig.cjs.map +1 -1
  4. package/dist/constants/hyperLiquidConfig.d.cts +0 -16
  5. package/dist/constants/hyperLiquidConfig.d.cts.map +1 -1
  6. package/dist/constants/hyperLiquidConfig.d.mts +0 -16
  7. package/dist/constants/hyperLiquidConfig.d.mts.map +1 -1
  8. package/dist/constants/hyperLiquidConfig.mjs +0 -16
  9. package/dist/constants/hyperLiquidConfig.mjs.map +1 -1
  10. package/dist/index.cjs +4 -5
  11. package/dist/index.cjs.map +1 -1
  12. package/dist/index.d.cts +1 -1
  13. package/dist/index.d.cts.map +1 -1
  14. package/dist/index.d.mts +1 -1
  15. package/dist/index.d.mts.map +1 -1
  16. package/dist/index.mjs +1 -1
  17. package/dist/index.mjs.map +1 -1
  18. package/dist/perpsErrorCodes.cjs +2 -0
  19. package/dist/perpsErrorCodes.cjs.map +1 -1
  20. package/dist/perpsErrorCodes.d.cts +1 -0
  21. package/dist/perpsErrorCodes.d.cts.map +1 -1
  22. package/dist/perpsErrorCodes.d.mts +1 -0
  23. package/dist/perpsErrorCodes.d.mts.map +1 -1
  24. package/dist/perpsErrorCodes.mjs +2 -0
  25. package/dist/perpsErrorCodes.mjs.map +1 -1
  26. package/dist/providers/HyperLiquidProvider.cjs +96 -294
  27. package/dist/providers/HyperLiquidProvider.cjs.map +1 -1
  28. package/dist/providers/HyperLiquidProvider.d.cts.map +1 -1
  29. package/dist/providers/HyperLiquidProvider.d.mts.map +1 -1
  30. package/dist/providers/HyperLiquidProvider.mjs +97 -295
  31. package/dist/providers/HyperLiquidProvider.mjs.map +1 -1
  32. package/dist/services/HyperLiquidSubscriptionService.cjs +171 -19
  33. package/dist/services/HyperLiquidSubscriptionService.cjs.map +1 -1
  34. package/dist/services/HyperLiquidSubscriptionService.d.cts.map +1 -1
  35. package/dist/services/HyperLiquidSubscriptionService.d.mts.map +1 -1
  36. package/dist/services/HyperLiquidSubscriptionService.mjs +171 -19
  37. package/dist/services/HyperLiquidSubscriptionService.mjs.map +1 -1
  38. package/package.json +1 -1
@@ -10,7 +10,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
10
10
  if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
11
11
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
12
12
  };
13
- var _HyperLiquidProvider_instances, _HyperLiquidProvider_deps, _HyperLiquidProvider_clientService, _HyperLiquidProvider_walletService, _HyperLiquidProvider_subscriptionService, _HyperLiquidProvider_symbolToAssetId, _HyperLiquidProvider_userFeeCache, _HyperLiquidProvider_maxLeverageCache, _HyperLiquidProvider_cachedMetaByDex, _HyperLiquidProvider_cachedMarketDataWithPrices, _HyperLiquidProvider_cachedSpotMeta, _HyperLiquidProvider_dexDiscoveryCache, _HyperLiquidProvider_referralCheckCache, _HyperLiquidProvider_builderFeeCheckCache, _HyperLiquidProvider_ensureReadyPromise, _HyperLiquidProvider_pendingBuilderFeeApprovals, _HyperLiquidProvider_compiledAllowlistPatterns, _HyperLiquidProvider_compiledBlocklistPatterns, _HyperLiquidProvider_userFeeDiscountBips, _HyperLiquidProvider_hip3Enabled, _HyperLiquidProvider_allowlistMarkets, _HyperLiquidProvider_blocklistMarkets, _HyperLiquidProvider_useUnifiedAccount, _HyperLiquidProvider_dexDiscoveryComplete, _HyperLiquidProvider_unifiedAccountSetupNeedsRetry, _HyperLiquidProvider_pendingValidatedDexsPromise, _HyperLiquidProvider_cachedUsdcTokenId, _HyperLiquidProvider_errorMappings, _HyperLiquidProvider_clientsInitialized, _HyperLiquidProvider_initializationPromise, _HyperLiquidProvider_messenger, _HyperLiquidProvider_builderAddressTestnet, _HyperLiquidProvider_builderAddressMainnet, _HyperLiquidProvider_priceDeviationLimit, _HyperLiquidProvider_compilePatternsSafely, _HyperLiquidProvider_ensureClientsInitialized, _HyperLiquidProvider_isWalletOnHyperliquid, _HyperLiquidProvider_ensureUnifiedAccountEnabled, _HyperLiquidProvider_ensureReady, _HyperLiquidProvider_tradingSetupPromise, _HyperLiquidProvider_tradingSetupComplete, _HyperLiquidProvider_ensureReadyForTrading, _HyperLiquidProvider_getOrFetchPrice, _HyperLiquidProvider_filterFills, _HyperLiquidProvider_getAllAvailableDexs, _HyperLiquidProvider_getValidatedDexs, _HyperLiquidProvider_fetchValidatedDexsInternal, _HyperLiquidProvider_getCachedMeta, _HyperLiquidProvider_backfillAssetMapForDex, _HyperLiquidProvider_getAssetIdWithRepair, _HyperLiquidProvider_getCachedSpotMeta, _HyperLiquidProvider_getCachedPerpDexs, _HyperLiquidProvider_calculateHip3FeeMultiplier, _HyperLiquidProvider_getCacheKey, _HyperLiquidProvider_fetchMarketsForDex, _HyperLiquidProvider_getUsdcTokenId, _HyperLiquidProvider_isUsdhCollateralDex, _HyperLiquidProvider_getSpotUsdhBalance, _HyperLiquidProvider_getSpotUsdcBalance, _HyperLiquidProvider_transferUsdcToSpot, _HyperLiquidProvider_swapUsdcToUsdh, _HyperLiquidProvider_ensureUsdhCollateralForOrder, _HyperLiquidProvider_buildAssetMapping, _HyperLiquidProvider_queryUserDataAcrossDexs, _HyperLiquidProvider_mapError, _HyperLiquidProvider_getErrorContext, _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_getStandaloneValidatedDexs, _HyperLiquidProvider_getAllMids, _HyperLiquidProvider_fetchSingleDexFresh, _HyperLiquidProvider_mergeDexResultsInto, _HyperLiquidProvider_cacheFreshMarketDataSnapshot, _HyperLiquidProvider_getStaleMarketDataSnapshot, _HyperLiquidProvider_isFeeCacheValid, _HyperLiquidProvider_getBuilderAddress, _HyperLiquidProvider_getReferralCode, _HyperLiquidProvider_ensureReferralSet, _HyperLiquidProvider_isReferralCodeReady, _HyperLiquidProvider_checkReferralSet, _HyperLiquidProvider_setReferralCode;
13
+ var _HyperLiquidProvider_instances, _HyperLiquidProvider_deps, _HyperLiquidProvider_clientService, _HyperLiquidProvider_walletService, _HyperLiquidProvider_subscriptionService, _HyperLiquidProvider_symbolToAssetId, _HyperLiquidProvider_userFeeCache, _HyperLiquidProvider_maxLeverageCache, _HyperLiquidProvider_cachedMetaByDex, _HyperLiquidProvider_cachedMarketDataWithPrices, _HyperLiquidProvider_cachedSpotMeta, _HyperLiquidProvider_dexDiscoveryCache, _HyperLiquidProvider_referralCheckCache, _HyperLiquidProvider_builderFeeCheckCache, _HyperLiquidProvider_ensureReadyPromise, _HyperLiquidProvider_pendingBuilderFeeApprovals, _HyperLiquidProvider_compiledAllowlistPatterns, _HyperLiquidProvider_compiledBlocklistPatterns, _HyperLiquidProvider_userFeeDiscountBips, _HyperLiquidProvider_hip3Enabled, _HyperLiquidProvider_allowlistMarkets, _HyperLiquidProvider_blocklistMarkets, _HyperLiquidProvider_useUnifiedAccount, _HyperLiquidProvider_dexDiscoveryComplete, _HyperLiquidProvider_unifiedAccountSetupNeedsRetry, _HyperLiquidProvider_pendingValidatedDexsPromise, _HyperLiquidProvider_cachedUsdcTokenId, _HyperLiquidProvider_errorMappings, _HyperLiquidProvider_clientsInitialized, _HyperLiquidProvider_initializationPromise, _HyperLiquidProvider_messenger, _HyperLiquidProvider_builderAddressTestnet, _HyperLiquidProvider_builderAddressMainnet, _HyperLiquidProvider_priceDeviationLimit, _HyperLiquidProvider_compilePatternsSafely, _HyperLiquidProvider_ensureClientsInitialized, _HyperLiquidProvider_isWalletOnHyperliquid, _HyperLiquidProvider_ensureUnifiedAccountEnabled, _HyperLiquidProvider_ensureReady, _HyperLiquidProvider_tradingSetupPromise, _HyperLiquidProvider_tradingSetupComplete, _HyperLiquidProvider_ensureReadyForTrading, _HyperLiquidProvider_getOrFetchPrice, _HyperLiquidProvider_filterFills, _HyperLiquidProvider_getAllAvailableDexs, _HyperLiquidProvider_getValidatedDexs, _HyperLiquidProvider_fetchValidatedDexsInternal, _HyperLiquidProvider_getCachedMeta, _HyperLiquidProvider_backfillAssetMapForDex, _HyperLiquidProvider_getAssetIdWithRepair, _HyperLiquidProvider_getCachedSpotMeta, _HyperLiquidProvider_getCachedPerpDexs, _HyperLiquidProvider_calculateHip3FeeMultiplier, _HyperLiquidProvider_getCacheKey, _HyperLiquidProvider_fetchMarketsForDex, _HyperLiquidProvider_getUsdcTokenId, _HyperLiquidProvider_isUsdcCollateralDex, _HyperLiquidProvider_buildAssetMapping, _HyperLiquidProvider_queryUserDataAcrossDexs, _HyperLiquidProvider_mapError, _HyperLiquidProvider_getErrorContext, _HyperLiquidProvider_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_getStandaloneValidatedDexs, _HyperLiquidProvider_getAllMids, _HyperLiquidProvider_fetchSingleDexFresh, _HyperLiquidProvider_excludeNonUsdcCollateralResults, _HyperLiquidProvider_mergeDexResultsInto, _HyperLiquidProvider_cacheFreshMarketDataSnapshot, _HyperLiquidProvider_getStaleMarketDataSnapshot, _HyperLiquidProvider_isFeeCacheValid, _HyperLiquidProvider_getBuilderAddress, _HyperLiquidProvider_getReferralCode, _HyperLiquidProvider_ensureReferralSet, _HyperLiquidProvider_isReferralCodeReady, _HyperLiquidProvider_checkReferralSet, _HyperLiquidProvider_setReferralCode;
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.HyperLiquidProvider = void 0;
16
16
  const utils_1 = require("@metamask/utils");
@@ -77,7 +77,7 @@ class HyperLiquidProvider {
77
77
  _HyperLiquidProvider_cachedMetaByDex.set(this, new Map());
78
78
  // Last known-good market list for stale fallback when every enabled DEX fails in one fetch window.
79
79
  _HyperLiquidProvider_cachedMarketDataWithPrices.set(this, null);
80
- // Session cache for spot metadata (contains USDC/USDH token info for HIP-3 collateral checks)
80
+ // Session cache for spot metadata (contains token info for HIP-3 collateral checks)
81
81
  // Pre-fetched in ensureReadyForTrading() to avoid API failures during order placement
82
82
  _HyperLiquidProvider_cachedSpotMeta.set(this, null);
83
83
  // Unified DEX discovery cache — single source of truth for all perpDexs() derivatives.
@@ -2355,12 +2355,17 @@ class HyperLiquidProvider {
2355
2355
  errorMessage,
2356
2356
  };
2357
2357
  }));
2358
+ // TAT-3304: Exclude non-USDC-collateral HIP-3 DEXs before merging, so
2359
+ // getMarketDataWithPrices (and the stale snapshot #cacheFreshMarketDataSnapshot
2360
+ // derives from it) enforces the same USDC-only policy as market discovery
2361
+ // and order placement.
2362
+ const usdcFilteredDexDataResults = await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_excludeNonUsdcCollateralResults).call(this, dexDataResults);
2358
2363
  // Combine universe, assetCtxs, and allMids from all DEXs
2359
2364
  const combinedUniverse = [];
2360
2365
  const combinedAssetCtxs = [];
2361
2366
  const combinedAllMids = {};
2362
- let latestDexResults = dexDataResults;
2363
- __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_mergeDexResultsInto).call(this, dexDataResults, combinedUniverse, combinedAssetCtxs, combinedAllMids);
2367
+ let latestDexResults = usdcFilteredDexDataResults;
2368
+ __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_mergeDexResultsInto).call(this, usdcFilteredDexDataResults, combinedUniverse, combinedAssetCtxs, combinedAllMids);
2364
2369
  if (combinedUniverse.length === 0) {
2365
2370
  combinedUniverse.length = 0;
2366
2371
  combinedAssetCtxs.length = 0;
@@ -2371,8 +2376,9 @@ class HyperLiquidProvider {
2371
2376
  __classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log(`[getMarketDataWithPrices] All DEXs returned empty, retrying in ${retryDelayMs}ms`);
2372
2377
  await new Promise((resolve) => setTimeout(resolve, retryDelayMs));
2373
2378
  const retryResults = await Promise.all(enabledDexs.map((dex) => __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_fetchSingleDexFresh).call(this, infoClient, dex)));
2374
- __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_mergeDexResultsInto).call(this, retryResults, combinedUniverse, combinedAssetCtxs, combinedAllMids);
2375
- latestDexResults = retryResults;
2379
+ const usdcFilteredRetryResults = await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_excludeNonUsdcCollateralResults).call(this, retryResults);
2380
+ __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_mergeDexResultsInto).call(this, usdcFilteredRetryResults, combinedUniverse, combinedAssetCtxs, combinedAllMids);
2381
+ latestDexResults = usdcFilteredRetryResults;
2376
2382
  if (combinedUniverse.length === 0) {
2377
2383
  const failedDexs = retryResults
2378
2384
  .filter((result) => !result.success)
@@ -4237,7 +4243,7 @@ async function _HyperLiquidProvider_ensureReady() {
4237
4243
  __classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('[ensureReadyForTrading] Starting trading setup (may require signatures)');
4238
4244
  __classPrivateFieldSet(this, _HyperLiquidProvider_tradingSetupPromise, (async () => {
4239
4245
  // Pre-fetch spotMeta for HIP-3 operations (non-blocking if it fails)
4240
- // This ensures token info (USDC/USDH indices) is available during order placement
4246
+ // This ensures token info (e.g. USDC token index) is available during order placement
4241
4247
  if (__classPrivateFieldGet(this, _HyperLiquidProvider_hip3Enabled, "f")) {
4242
4248
  try {
4243
4249
  await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_getCachedSpotMeta).call(this);
@@ -4577,7 +4583,7 @@ async function _HyperLiquidProvider_getAssetIdWithRepair(params) {
4577
4583
  }, _HyperLiquidProvider_getCachedSpotMeta =
4578
4584
  /**
4579
4585
  * Fetch spot metadata with session-based caching
4580
- * Contains token info (USDC, USDH indices) needed for HIP-3 collateral checks
4586
+ * Contains token info (e.g. USDC token index) needed for HIP-3 collateral checks
4581
4587
  * Pre-fetched in ensureReadyForTrading() to ensure availability during order placement
4582
4588
  *
4583
4589
  * @returns SpotMetaResponse with tokens and universe data
@@ -4714,6 +4720,14 @@ async function _HyperLiquidProvider_fetchMarketsForDex(params) {
4714
4720
  __classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log(`HyperLiquidProvider: Invalid universe data for DEX ${dex ?? 'main'}`);
4715
4721
  return [];
4716
4722
  }
4723
+ // TAT-3304: Following the USDH sunset, only USDC-collateral HIP-3 DEXs
4724
+ // are supported for trading. Gate non-USDC-collateral DEXs out of market
4725
+ // discovery entirely (regardless of skipFilters) so their markets can
4726
+ // never be surfaced to trade, even via an allowlist entry naming the DEX.
4727
+ if (dex !== null && !(await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_isUsdcCollateralDex).call(this, dex))) {
4728
+ __classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('HyperLiquidProvider: Filtering out non-USDC-collateral HIP-3 DEX from market discovery', { dex });
4729
+ return [];
4730
+ }
4717
4731
  // Transform to MarketInfo format
4718
4732
  const markets = meta.universe.map((asset) => (0, hyperLiquidAdapter_1.adaptMarketFromSDK)(asset));
4719
4733
  // Apply HIP-3 filtering on-demand (cheap array operation)
@@ -4750,288 +4764,37 @@ async function _HyperLiquidProvider_getUsdcTokenId() {
4750
4764
  tokenId: __classPrivateFieldGet(this, _HyperLiquidProvider_cachedUsdcTokenId, "f"),
4751
4765
  });
4752
4766
  return __classPrivateFieldGet(this, _HyperLiquidProvider_cachedUsdcTokenId, "f");
4753
- }, _HyperLiquidProvider_isUsdhCollateralDex =
4767
+ }, _HyperLiquidProvider_isUsdcCollateralDex =
4754
4768
  /**
4755
- * Check if a HIP-3 DEX uses USDH as collateral (vs USDC)
4756
- * Per HyperLiquid docs: USDH DEXs pull collateral from spot balance automatically
4769
+ * Check if a HIP-3 DEX uses USDC as its collateral token
4770
+ *
4771
+ * TAT-3304: Following the USDH sunset, only USDC-collateral DEXs are
4772
+ * supported for trading. This gate filters non-USDC-collateral DEXs
4773
+ * (e.g. the now-sunset USDH) out of market discovery and blocks order
4774
+ * placement, replacing the previous USDH-specific auto-swap path.
4775
+ *
4776
+ * Fails closed: only returns true when the collateral token index
4777
+ * positively resolves to USDC against spot metadata. If the token can't
4778
+ * be resolved (e.g. missing/stale spot metadata), the DEX is treated as
4779
+ * non-USDC and gated out, since we can't otherwise verify the USDC-only
4780
+ * requirement. This only affects HIP-3 DEXs — main-DEX trading (dex ===
4781
+ * null) never calls this gate.
4757
4782
  *
4758
4783
  * @param dexName - The DEX identifier (empty string for main DEX).
4759
4784
  * @returns A promise that resolves to the boolean result.
4760
4785
  */
4761
- async function _HyperLiquidProvider_isUsdhCollateralDex(dexName) {
4786
+ async function _HyperLiquidProvider_isUsdcCollateralDex(dexName) {
4762
4787
  const meta = await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_getCachedMeta).call(this, { dexName });
4763
4788
  const spotMeta = await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_getCachedSpotMeta).call(this);
4764
4789
  const collateralToken = spotMeta.tokens.find((tok) => tok.index === meta.collateralToken);
4765
- const isUsdh = collateralToken?.name === hyperLiquidConfig_1.USDH_CONFIG.TokenName;
4790
+ const isUsdc = collateralToken?.name === hyperLiquidConfig_1.USDC_SYMBOL;
4766
4791
  __classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('HyperLiquidProvider: Checked DEX collateral type', {
4767
4792
  dexName,
4768
4793
  collateralTokenIndex: meta.collateralToken,
4769
4794
  collateralTokenName: collateralToken?.name,
4770
- isUsdh,
4771
- });
4772
- return isUsdh;
4773
- }, _HyperLiquidProvider_getSpotUsdhBalance =
4774
- /**
4775
- * Get user's USDH balance in spot wallet
4776
- *
4777
- * @returns A promise that resolves to the numeric result.
4778
- */
4779
- async function _HyperLiquidProvider_getSpotUsdhBalance() {
4780
- const infoClient = __classPrivateFieldGet(this, _HyperLiquidProvider_clientService, "f").getInfoClient();
4781
- const userAddress = await __classPrivateFieldGet(this, _HyperLiquidProvider_walletService, "f").getUserAddressWithDefault();
4782
- const spotState = await infoClient.spotClearinghouseState({
4783
- user: userAddress,
4784
- });
4785
- const usdhBalance = spotState.balances.find((b) => b.coin === hyperLiquidConfig_1.USDH_CONFIG.TokenName);
4786
- const balance = usdhBalance ? parseFloat(usdhBalance.total) : 0;
4787
- __classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('HyperLiquidProvider: Spot USDH balance', {
4788
- balance,
4789
- userAddress,
4790
- });
4791
- return balance;
4792
- }, _HyperLiquidProvider_getSpotUsdcBalance =
4793
- /**
4794
- * Get user's USDC balance in spot wallet
4795
- * Required for USDH DEX orders - need USDC in spot to swap to USDH
4796
- *
4797
- * @returns A promise that resolves to the numeric result.
4798
- */
4799
- async function _HyperLiquidProvider_getSpotUsdcBalance() {
4800
- const infoClient = __classPrivateFieldGet(this, _HyperLiquidProvider_clientService, "f").getInfoClient();
4801
- const userAddress = await __classPrivateFieldGet(this, _HyperLiquidProvider_walletService, "f").getUserAddressWithDefault();
4802
- const spotState = await infoClient.spotClearinghouseState({
4803
- user: userAddress,
4804
- });
4805
- const usdcBalance = spotState.balances.find((b) => b.coin === 'USDC');
4806
- const balance = usdcBalance ? parseFloat(usdcBalance.total) : 0;
4807
- __classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('HyperLiquidProvider: Spot USDC balance', {
4808
- balance,
4809
- userAddress,
4810
- });
4811
- return balance;
4812
- }, _HyperLiquidProvider_transferUsdcToSpot =
4813
- /**
4814
- * Transfer USDC from main perps wallet to spot wallet
4815
- * Required before swapping USDC→USDH for USDH DEX orders
4816
- *
4817
- * @param amount - The amount value.
4818
- * @returns A promise that resolves to the result.
4819
- */
4820
- async function _HyperLiquidProvider_transferUsdcToSpot(amount) {
4821
- const exchangeClient = __classPrivateFieldGet(this, _HyperLiquidProvider_clientService, "f").getExchangeClient();
4822
- const userAddress = await __classPrivateFieldGet(this, _HyperLiquidProvider_walletService, "f").getUserAddressWithDefault();
4823
- __classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('HyperLiquidProvider: Transferring USDC to spot', {
4824
- amount,
4825
- userAddress,
4826
- });
4827
- try {
4828
- const result = await exchangeClient.sendAsset({
4829
- destination: userAddress,
4830
- sourceDex: '', // Main perps DEX (empty string)
4831
- destinationDex: 'spot',
4832
- token: await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_getUsdcTokenId).call(this),
4833
- amount: amount.toString(),
4834
- });
4835
- if (result.status === 'ok') {
4836
- __classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('HyperLiquidProvider: USDC transferred to spot', {
4837
- amount,
4838
- });
4839
- return { success: true };
4840
- }
4841
- return { success: false, error: perpsErrorCodes_1.PERPS_ERROR_CODES.TRANSFER_FAILED };
4842
- }
4843
- catch (error) {
4844
- const errorMsg = (0, errorUtils_1.ensureError)(error, 'HyperLiquidProvider.transferUSDCToPerps').message;
4845
- __classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('HyperLiquidProvider: USDC transfer to spot failed', {
4846
- error: errorMsg,
4847
- });
4848
- return { success: false, error: errorMsg };
4849
- }
4850
- }, _HyperLiquidProvider_swapUsdcToUsdh =
4851
- /**
4852
- * Swap USDC to USDH on spot market
4853
- * Returns the result of the swap including filled size
4854
- *
4855
- * @param amount - The amount value.
4856
- * @returns A promise that resolves to the result.
4857
- */
4858
- async function _HyperLiquidProvider_swapUsdcToUsdh(amount) {
4859
- const spotMeta = await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_getCachedSpotMeta).call(this);
4860
- // Find USDH and USDC tokens by name
4861
- const usdhToken = spotMeta.tokens.find((tok) => tok.name === hyperLiquidConfig_1.USDH_CONFIG.TokenName);
4862
- const usdcToken = spotMeta.tokens.find((tok) => tok.name === 'USDC');
4863
- if (!usdhToken || !usdcToken) {
4864
- return {
4865
- success: false,
4866
- error: perpsErrorCodes_1.PERPS_ERROR_CODES.SPOT_PAIR_NOT_FOUND,
4867
- };
4868
- }
4869
- // Find USDH/USDC pair by token indices (NOT by name - name is @230)
4870
- const usdhUsdcPair = spotMeta.universe.find((univ) => univ.tokens.includes(usdhToken.index) &&
4871
- univ.tokens.includes(usdcToken.index));
4872
- if (!usdhUsdcPair) {
4873
- return { success: false, error: perpsErrorCodes_1.PERPS_ERROR_CODES.SPOT_PAIR_NOT_FOUND };
4874
- }
4875
- const spotAssetId = hyperLiquidConfig_1.SPOT_ASSET_ID_OFFSET + usdhUsdcPair.index;
4876
- __classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('HyperLiquidProvider: Found USDH/USDC spot pair', {
4877
- pairIndex: usdhUsdcPair.index,
4878
- pairName: usdhUsdcPair.name,
4879
- spotAssetId,
4880
- usdhTokenIndex: usdhToken.index,
4881
- usdcTokenIndex: usdcToken.index,
4882
- });
4883
- // Get current mid price
4884
- const infoClient = __classPrivateFieldGet(this, _HyperLiquidProvider_clientService, "f").getInfoClient();
4885
- const allMids = await infoClient.allMids();
4886
- const pairKey = `@${usdhUsdcPair.index}`;
4887
- const usdhPrice = parseFloat(allMids[pairKey] || '1');
4888
- if (usdhPrice === 0) {
4889
- return {
4890
- success: false,
4891
- error: perpsErrorCodes_1.PERPS_ERROR_CODES.PRICE_UNAVAILABLE,
4892
- };
4893
- }
4894
- // Calculate order parameters
4895
- // USDH is pegged 1:1 to USDC, add small slippage buffer
4896
- const slippageMultiplier = 1 + hyperLiquidConfig_1.USDH_CONFIG.SwapSlippageBps / hyperLiquidConfig_1.BASIS_POINTS_DIVISOR;
4897
- const maxPrice = usdhPrice * slippageMultiplier;
4898
- // Size in USDH = amount / price (since we're buying USDH with USDC)
4899
- let sizeInUsdh = amount / usdhPrice;
4900
- // Format size according to HyperLiquid requirements
4901
- let formattedSize = sizeInUsdh.toFixed(usdhToken.szDecimals);
4902
- // CRITICAL: Ensure USDC cost meets $10 minimum after rounding
4903
- // At price ~0.999995, buying 10.00 USDH costs 9.99995 USDC (under minimum)
4904
- // Bump up by one increment if needed to meet minimum
4905
- const minSpotOrderValue = hyperLiquidConfig_1.TRADING_DEFAULTS.amount.mainnet;
4906
- const estimatedCost = parseFloat(formattedSize) * usdhPrice;
4907
- if (estimatedCost < minSpotOrderValue) {
4908
- const increment = Math.pow(10, -usdhToken.szDecimals); // 0.01 for szDecimals=2
4909
- sizeInUsdh = parseFloat(formattedSize) + increment;
4910
- formattedSize = sizeInUsdh.toFixed(usdhToken.szDecimals);
4911
- }
4912
- // Format price according to HyperLiquid requirements
4913
- const formattedPrice = (0, hyperLiquidAdapter_1.formatHyperLiquidPrice)({
4914
- price: maxPrice,
4915
- szDecimals: usdhToken.szDecimals,
4916
- });
4917
- __classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('HyperLiquidProvider: Placing USDC→USDH swap order', {
4918
- usdcAmount: amount,
4919
- usdhPrice,
4920
- maxPrice: formattedPrice,
4921
- size: formattedSize,
4922
- szDecimals: usdhToken.szDecimals,
4923
- });
4924
- try {
4925
- const exchangeClient = __classPrivateFieldGet(this, _HyperLiquidProvider_clientService, "f").getExchangeClient();
4926
- const result = await exchangeClient.order({
4927
- orders: [
4928
- {
4929
- a: spotAssetId,
4930
- b: true, // Buy USDH
4931
- p: formattedPrice,
4932
- s: formattedSize,
4933
- r: false, // Not reduce-only
4934
- t: { limit: { tif: 'Ioc' } }, // Immediate-or-cancel
4935
- },
4936
- ],
4937
- grouping: 'na',
4938
- });
4939
- if (result.status !== 'ok') {
4940
- return {
4941
- success: false,
4942
- error: perpsErrorCodes_1.PERPS_ERROR_CODES.SWAP_FAILED,
4943
- };
4944
- }
4945
- // Check order status
4946
- const status = result.response?.data?.statuses?.[0];
4947
- if (isStatusObject(status) && (0, utils_1.hasProperty)(status, 'error')) {
4948
- return { success: false, error: String(status.error) };
4949
- }
4950
- // Note: `in` narrows the HyperLiquid SDK discriminated union to the
4951
- // branch that has `filled`; `hasProperty` only narrows the key and
4952
- // types `status.filled` as `unknown`, which loses access to `.totalSz`.
4953
- /* eslint-disable no-restricted-syntax */
4954
- const filledSize = isStatusObject(status) && 'filled' in status
4955
- ? parseFloat(status.filled?.totalSz ?? '0')
4956
- : 0;
4957
- /* eslint-enable no-restricted-syntax */
4958
- __classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('HyperLiquidProvider: USDC→USDH swap completed', {
4959
- success: true,
4960
- filledSize,
4961
- requestedSize: formattedSize,
4962
- });
4963
- return { success: true, filledSize };
4964
- }
4965
- catch (error) {
4966
- const errorMsg = (0, errorUtils_1.ensureError)(error, 'HyperLiquidProvider.swapUSDCToUSDH').message;
4967
- __classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('HyperLiquidProvider: USDC→USDH swap error', {
4968
- error: errorMsg,
4969
- });
4970
- return { success: false, error: errorMsg };
4971
- }
4972
- }, _HyperLiquidProvider_ensureUsdhCollateralForOrder =
4973
- /**
4974
- * Ensure sufficient USDH collateral in spot for HIP-3 DEX order
4975
- * If user lacks USDH, auto-swap from USDC
4976
- *
4977
- * @param dexName - The DEX identifier (empty string for main DEX).
4978
- * @param requiredMargin - The required margin amount.
4979
- */
4980
- async function _HyperLiquidProvider_ensureUsdhCollateralForOrder(dexName, requiredMargin) {
4981
- const spotUsdhBalance = await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_getSpotUsdhBalance).call(this);
4982
- __classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('HyperLiquidProvider: Checking USDH collateral', {
4983
- dexName,
4984
- requiredMargin,
4985
- spotUsdhBalance,
4986
- });
4987
- if (spotUsdhBalance >= requiredMargin) {
4988
- __classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('HyperLiquidProvider: Sufficient USDH in spot');
4989
- return;
4990
- }
4991
- const shortfall = requiredMargin - spotUsdhBalance;
4992
- // HyperLiquid spot has $10 minimum order value
4993
- const minSpotOrderValue = hyperLiquidConfig_1.TRADING_DEFAULTS.amount.mainnet;
4994
- // If user has some USDH already, we can swap just the shortfall (if >= $10)
4995
- // If user has zero USDH, they need at least $10 for first swap
4996
- const swapAmount = spotUsdhBalance > 0 && shortfall >= minSpotOrderValue
4997
- ? shortfall
4998
- : Math.max(shortfall, minSpotOrderValue);
4999
- // Step 1: Check spot USDC balance
5000
- const spotUsdcBalance = await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_getSpotUsdcBalance).call(this);
5001
- // Calculate total available USDC (spot + what we can transfer from perps)
5002
- // For now, check if we have enough in spot first
5003
- const totalUsdcNeeded = swapAmount - spotUsdcBalance;
5004
- // Step 2: If insufficient USDC in spot, transfer from main perps
5005
- if (spotUsdcBalance < swapAmount) {
5006
- const transferAmount = totalUsdcNeeded;
5007
- __classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('HyperLiquidProvider: Transferring USDC to spot for swap', {
5008
- spotUsdcBalance,
5009
- swapAmount,
5010
- transferAmount,
5011
- });
5012
- const transferResult = await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_transferUsdcToSpot).call(this, transferAmount);
5013
- if (!transferResult.success) {
5014
- // Provide user-friendly error for insufficient funds
5015
- if (transferResult.error?.includes('Insufficient balance')) {
5016
- throw new Error(`Insufficient USDC balance. Need $${swapAmount.toFixed(2)} for USDH swap but transfer failed. Please deposit more USDC to your HyperLiquid account.`);
5017
- }
5018
- throw new Error(`Failed to transfer USDC to spot: ${transferResult.error}`);
5019
- }
5020
- }
5021
- // Step 3: Swap USDC → USDH
5022
- __classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('HyperLiquidProvider: Swapping USDC→USDH for collateral', {
5023
- shortfall,
5024
- swapAmount,
5025
- minOrderValue: minSpotOrderValue,
5026
- });
5027
- const swapResult = await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_swapUsdcToUsdh).call(this, swapAmount);
5028
- if (!swapResult.success) {
5029
- throw new Error(`Failed to acquire USDH collateral for ${dexName}: ${swapResult.error}`);
5030
- }
5031
- __classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('HyperLiquidProvider: USDH collateral acquired', {
5032
- dexName,
5033
- filledSize: swapResult.filledSize,
4795
+ isUsdc,
5034
4796
  });
4797
+ return isUsdc;
5035
4798
  }, _HyperLiquidProvider_buildAssetMapping =
5036
4799
  /**
5037
4800
  * Build asset ID mapping from market metadata
@@ -5851,26 +5614,19 @@ async function _HyperLiquidProvider_prepareAssetForTrading(params) {
5851
5614
  */
5852
5615
  async function _HyperLiquidProvider_handleHip3PreOrder(params) {
5853
5616
  const { dexName, symbol, orderPrice, positionSize, leverage, isBuy } = params;
5854
- // Check if this DEX uses USDH collateral (vs USDC)
5855
- // For USDH DEXs, HyperLiquid automatically pulls from spot balance
5856
- const isUsdhDex = await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_isUsdhCollateralDex).call(this, dexName);
5857
- if (isUsdhDex) {
5858
- __classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('HyperLiquidProvider: USDH-collateralized DEX detected', {
5617
+ // TAT-3304: Only USDC-collateral HIP-3 DEXs are supported for trading.
5618
+ // Following the USDH sunset, reject orders on any non-USDC-collateral
5619
+ // DEX here instead of attempting the (now-removed) USDH auto-swap path.
5620
+ // Market discovery (#fetchMarketsForDex) already filters such DEXs out,
5621
+ // so this is a defense-in-depth check against stale caches or a
5622
+ // misconfigured allowlist entry.
5623
+ const isUsdcDex = await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_isUsdcCollateralDex).call(this, dexName);
5624
+ if (!isUsdcDex) {
5625
+ __classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('HyperLiquidProvider: Rejecting order for non-USDC-collateral DEX', {
5859
5626
  dexName,
5860
5627
  symbol,
5861
5628
  });
5862
- // Calculate required margin and ensure USDH is in spot
5863
- const requiredMargin = await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_calculateHip3RequiredMargin).call(this, {
5864
- symbol,
5865
- dexName,
5866
- positionSize,
5867
- orderPrice,
5868
- leverage,
5869
- isBuy,
5870
- });
5871
- await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_ensureUsdhCollateralForOrder).call(this, dexName, requiredMargin);
5872
- // Unified Account will pull USDH from spot automatically
5873
- return { transferInfo: null };
5629
+ throw new Error(perpsErrorCodes_1.PERPS_ERROR_CODES.UNSUPPORTED_COLLATERAL);
5874
5630
  }
5875
5631
  if (__classPrivateFieldGet(this, _HyperLiquidProvider_useUnifiedAccount, "f")) {
5876
5632
  __classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('Using Unified Account (no manual transfer)', {
@@ -6087,6 +5843,52 @@ async function _HyperLiquidProvider_getAllMids(infoClient, dexParam) {
6087
5843
  failedStep: allMidsErrorMessage ? 'allMids' : undefined,
6088
5844
  errorMessage: allMidsErrorMessage,
6089
5845
  };
5846
+ }, _HyperLiquidProvider_excludeNonUsdcCollateralResults =
5847
+ /**
5848
+ * Filter out successful HIP-3 DexFetchResults whose collateral token is
5849
+ * not USDC, so getMarketDataWithPrices enforces the same USDC-only policy
5850
+ * as market discovery (#fetchMarketsForDex) and order placement
5851
+ * (#handleHip3PreOrder) — otherwise a non-USDC HIP-3 market could appear
5852
+ * in overview data (and the cached stale snapshot derived from it) while
5853
+ * order placement rejects it (TAT-3304).
5854
+ *
5855
+ * Main-DEX results (dex === null) and already-failed results pass through
5856
+ * unchanged. #isUsdcCollateralDex fails closed, and on an unexpected
5857
+ * error checking it (e.g. a spotMeta fetch failure) this also fails
5858
+ * closed by dropping the DEX's result, rather than failing the whole
5859
+ * method and losing main-DEX overview data.
5860
+ *
5861
+ * @param results - The DEX fetch results to filter.
5862
+ * @returns A promise that resolves to the filtered results.
5863
+ */
5864
+ async function _HyperLiquidProvider_excludeNonUsdcCollateralResults(results) {
5865
+ return Promise.all(results.map(async (result) => {
5866
+ if (result.dex === null || !result.success) {
5867
+ return result;
5868
+ }
5869
+ let isUsdcDex;
5870
+ try {
5871
+ isUsdcDex = await __classPrivateFieldGet(this, _HyperLiquidProvider_instances, "m", _HyperLiquidProvider_isUsdcCollateralDex).call(this, result.dex);
5872
+ }
5873
+ catch (error) {
5874
+ __classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('HyperLiquidProvider: Failed to check collateral type for HIP-3 DEX; excluding from market data', {
5875
+ dex: result.dex,
5876
+ error: (0, errorUtils_1.ensureError)(error, 'HyperLiquidProvider.excludeNonUsdcCollateralResults').message,
5877
+ });
5878
+ isUsdcDex = false;
5879
+ }
5880
+ if (isUsdcDex) {
5881
+ return result;
5882
+ }
5883
+ __classPrivateFieldGet(this, _HyperLiquidProvider_deps, "f").debugLogger.log('HyperLiquidProvider: Excluding non-USDC-collateral HIP-3 DEX from market data', { dex: result.dex });
5884
+ return {
5885
+ ...result,
5886
+ meta: null,
5887
+ assetCtxs: [],
5888
+ allMids: {},
5889
+ success: false,
5890
+ };
5891
+ }));
6090
5892
  }, _HyperLiquidProvider_mergeDexResultsInto = function _HyperLiquidProvider_mergeDexResultsInto(results, combinedUniverse, combinedAssetCtxs, combinedAllMids) {
6091
5893
  results.forEach((result) => {
6092
5894
  if (result.success && result.meta?.universe) {