@metamask-previews/perps-controller 17.3.0-preview-d3f9964be → 17.4.0-preview-aecbce4ff

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 (59) hide show
  1. package/CHANGELOG.md +72 -1
  2. package/dist/PerpsController-method-action-types.d.ts +10 -3
  3. package/dist/PerpsController-method-action-types.d.ts.map +1 -1
  4. package/dist/PerpsController-method-action-types.js.map +1 -1
  5. package/dist/PerpsController.d.ts +10 -3
  6. package/dist/PerpsController.d.ts.map +1 -1
  7. package/dist/PerpsController.js +62 -8
  8. package/dist/PerpsController.js.map +1 -1
  9. package/dist/constants/perpsConfig.d.ts +55 -0
  10. package/dist/constants/perpsConfig.d.ts.map +1 -1
  11. package/dist/constants/perpsConfig.js +55 -0
  12. package/dist/constants/perpsConfig.js.map +1 -1
  13. package/dist/providers/AggregatedPerpsProvider.d.ts +19 -0
  14. package/dist/providers/AggregatedPerpsProvider.d.ts.map +1 -1
  15. package/dist/providers/AggregatedPerpsProvider.js +21 -0
  16. package/dist/providers/AggregatedPerpsProvider.js.map +1 -1
  17. package/dist/providers/HyperLiquidProvider.d.ts +7 -0
  18. package/dist/providers/HyperLiquidProvider.d.ts.map +1 -1
  19. package/dist/providers/HyperLiquidProvider.js +133 -34
  20. package/dist/providers/HyperLiquidProvider.js.map +1 -1
  21. package/dist/providers/LighterProvider.d.ts.map +1 -1
  22. package/dist/providers/LighterProvider.js +2 -0
  23. package/dist/providers/LighterProvider.js.map +1 -1
  24. package/dist/services/MarketDataService.d.ts.map +1 -1
  25. package/dist/services/MarketDataService.js +16 -4
  26. package/dist/services/MarketDataService.js.map +1 -1
  27. package/dist/services/RewardsIntegrationService.d.ts +47 -2
  28. package/dist/services/RewardsIntegrationService.d.ts.map +1 -1
  29. package/dist/services/RewardsIntegrationService.js +400 -19
  30. package/dist/services/RewardsIntegrationService.js.map +1 -1
  31. package/dist/services/ServiceContext.d.ts +9 -1
  32. package/dist/services/ServiceContext.d.ts.map +1 -1
  33. package/dist/services/ServiceContext.js.map +1 -1
  34. package/dist/services/TerminalMarketService.d.ts +1 -1
  35. package/dist/services/TerminalMarketService.d.ts.map +1 -1
  36. package/dist/services/TerminalMarketService.js +11 -2
  37. package/dist/services/TerminalMarketService.js.map +1 -1
  38. package/dist/services/TradingService.d.ts.map +1 -1
  39. package/dist/services/TradingService.js +291 -18
  40. package/dist/services/TradingService.js.map +1 -1
  41. package/dist/types/index.d.ts +65 -4
  42. package/dist/types/index.d.ts.map +1 -1
  43. package/dist/types/index.js.map +1 -1
  44. package/dist/types/messenger.d.ts +17 -1
  45. package/dist/types/messenger.d.ts.map +1 -1
  46. package/dist/types/messenger.js.map +1 -1
  47. package/dist/utils/hyperLiquidAdapter.d.ts +7 -0
  48. package/dist/utils/hyperLiquidAdapter.d.ts.map +1 -1
  49. package/dist/utils/hyperLiquidAdapter.js +1 -1
  50. package/dist/utils/hyperLiquidAdapter.js.map +1 -1
  51. package/dist/utils/index.d.ts +1 -0
  52. package/dist/utils/index.d.ts.map +1 -1
  53. package/dist/utils/index.js +1 -0
  54. package/dist/utils/index.js.map +1 -1
  55. package/dist/utils/subscriptionFeeWaiver.d.ts +201 -0
  56. package/dist/utils/subscriptionFeeWaiver.d.ts.map +1 -0
  57. package/dist/utils/subscriptionFeeWaiver.js +334 -0
  58. package/dist/utils/subscriptionFeeWaiver.js.map +1 -0
  59. package/package.json +11 -6
@@ -1 +1 @@
1
- {"version":3,"file":"RewardsIntegrationService.js","sourceRoot":"","sources":["../../src/services/RewardsIntegrationService.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n BASIS_POINTS_DIVISOR,\n BUILDER_FEE_CONFIG,\n} from '../constants/hyperLiquidConfig.js';\nimport {\n PERPS_CONSTANTS,\n SUBSCRIPTION_BENEFITS_CACHE,\n} from '../constants/perpsConfig.js';\nimport type {\n PerpsFeeResolution,\n PerpsFeeSource,\n PerpsPlatformDependencies,\n PerpsSubscriptionBenefits,\n PerpsSubscriptionFeeWaiverStatus,\n} from '../types/index.js';\nimport type { PerpsControllerMessengerBase } from '../types/messenger.js';\nimport { getSelectedEvmAccountFromMessenger } from '../utils/accountUtils.js';\nimport { ensureError } from '../utils/errorUtils.js';\nimport { formatAccountToCaipAccountId } from '../utils/rewardsUtils.js';\n\n/**\n * Default MetaMask builder fee, in basis points.\n * This is the fee every user pays when no cheaper source applies.\n */\nconst DEFAULT_FEE_BIPS =\n BUILDER_FEE_CONFIG.MaxFeeDecimal * BASIS_POINTS_DIVISOR;\n\n/**\n * Cached subscription benefits plus the time they were read.\n */\ntype BenefitsSnapshot = {\n benefits: PerpsSubscriptionBenefits | null;\n fetchedAt: number;\n};\n\n/**\n * RewardsIntegrationService\n *\n * Owns the unified perps fee resolver: it considers every fee source and\n * returns the lowest fee, expressed as the discount bips providers consume.\n *\n * Sources, all in fee basis points (lowest wins):\n * - `default` — {@link BUILDER_FEE_CONFIG}, the fee with no reductions.\n * - `rewards` — VIP and season, collapsed into one discount by\n * `RewardsController` (`rewards.getPerpsDiscountForAccount`), so this service\n * does not re-derive the VIP/season split.\n * - `subscription` — `0` bips, but only when the eligibility gate passes on a\n * cached read of the profile's benefits.\n *\n * On a tie the cheaper-to-explain source wins, in the order\n * `subscription` > `rewards` > `default`.\n *\n * The benefits cache is stale-while-revalidate: fee resolution is a pure read\n * of the cached snapshot, while preview and lifecycle callers refresh it\n * explicitly. Nothing is reserved or committed client-side, so backend\n * exhaustion needs no release logic — the next refresh simply stops passing\n * the gate.\n *\n * Instance-based service with constructor injection of platform dependencies.\n */\nexport class RewardsIntegrationService {\n readonly #deps: PerpsPlatformDependencies;\n\n readonly #messenger: PerpsControllerMessengerBase;\n\n /** Last successful benefits read, or undefined before the first one. */\n #benefitsSnapshot: BenefitsSnapshot | undefined;\n\n /**\n * When the last benefits read finished, successful or not.\n *\n * Separate from `#benefitsSnapshot.fetchedAt`, which only advances on\n * success: a failing read must still throttle the next preview refresh,\n * otherwise an outage turns every fee preview into a new request.\n */\n #lastAttemptAt: number | undefined;\n\n /** In-flight refresh, deduped so only one runs at a time. */\n #benefitsRefresh: Promise<void> | undefined;\n\n /**\n * Identity generation for the cached benefits.\n *\n * Bumped by {@link invalidateSubscriptionBenefits}; a read that resolves\n * against a superseded epoch is discarded rather than written back, so a\n * refresh issued for the previous profile cannot repopulate the cache after\n * a sign-out or profile switch.\n */\n #benefitsEpoch = 0;\n\n /**\n * Create a new RewardsIntegrationService instance\n *\n * @param deps - Platform dependencies for logging, metrics, etc.\n * @param messenger - Controller messenger for cross-controller communication.\n */\n constructor(\n deps: PerpsPlatformDependencies,\n messenger: PerpsControllerMessengerBase,\n ) {\n this.#deps = deps;\n this.#messenger = messenger;\n }\n\n /**\n * Get chain ID for a network client via DI network controller\n *\n * @param networkClientId - The network client identifier to look up.\n * @returns The chain ID string, or undefined if the network client is not found.\n */\n #getChainIdForNetwork(networkClientId: string): string | undefined {\n try {\n const networkClient = this.#messenger.call(\n 'NetworkController:getNetworkClientById',\n networkClientId,\n );\n return networkClient.configuration.chainId;\n } catch {\n // Network client may not exist\n return undefined;\n }\n }\n\n /**\n * Calculate user fee discount from the unified fee resolver.\n * Returns discount in basis points (e.g., 6500 = 65% discount)\n *\n * @returns The fee discount in basis points, or undefined if no source resolved.\n */\n async calculateUserFeeDiscount(): Promise<number | undefined> {\n const resolution = await this.resolveFee();\n return resolution.discountBips;\n }\n\n /**\n * Resolve the MetaMask builder fee across every source and return the lowest.\n *\n * Never throws and never starts a subscription benefits read: a failing or\n * unresolved cached source simply drops out of the comparison, so the worst\n * case is the default fee rather than an error or an over-granted waiver.\n *\n * @returns The winning fee, its source, and the subscription gate outcome.\n */\n async resolveFee(): Promise<PerpsFeeResolution> {\n const rewardsDiscountBips = await this.#calculateRewardsDiscount();\n // Pure cache read: subscription benefits must never start a network request\n // while an order is being prepared for signing.\n const subscription = this.getSubscriptionFeeWaiverStatus();\n\n let feeBips = DEFAULT_FEE_BIPS;\n let source: PerpsFeeSource = 'default';\n\n if (rewardsDiscountBips !== undefined) {\n const rewardsFeeBips =\n DEFAULT_FEE_BIPS * (1 - rewardsDiscountBips / BASIS_POINTS_DIVISOR);\n // `<=` so an equal rewards fee still reports the rewards source, keeping\n // a resolved 0% discount distinguishable from an unresolved one.\n if (rewardsFeeBips <= feeBips) {\n feeBips = rewardsFeeBips;\n source = 'rewards';\n }\n }\n\n // Nothing can undercut a waived fee, so the gate passing always wins.\n if (subscription.eligible) {\n feeBips = 0;\n source = 'subscription';\n }\n\n const discountBips =\n source === 'default'\n ? undefined\n : Math.round((1 - feeBips / DEFAULT_FEE_BIPS) * BASIS_POINTS_DIVISOR);\n\n this.#deps.debugLogger.log('RewardsIntegrationService: Fee resolved', {\n source,\n feeBips,\n discountBips,\n defaultFeeBips: DEFAULT_FEE_BIPS,\n rewardsDiscountBips,\n subscriptionEligible: subscription.eligible,\n subscriptionReason: subscription.reason,\n });\n\n return { feeBips, discountBips, source, subscription };\n }\n\n /**\n * Read the subscription fee-waiver gate from the cached benefits snapshot.\n *\n * Synchronous and side-effect free. The returned value always comes from\n * what is already cached; preview and lifecycle callers own hydration.\n *\n * @returns Whether the waiver applies, why, and the remaining notional.\n */\n getSubscriptionFeeWaiverStatus(): PerpsSubscriptionFeeWaiverStatus {\n if (!this.#deps.subscription) {\n return { eligible: false, reason: 'no-source' };\n }\n\n const now = Date.now();\n const snapshot = this.#benefitsSnapshot;\n const age = snapshot ? now - snapshot.fetchedAt : Infinity;\n if (!snapshot) {\n return { eligible: false, reason: 'not-hydrated' };\n }\n\n if (age > SUBSCRIPTION_BENEFITS_CACHE.MaxStaleMs) {\n // Past the ceiling we cannot tell whether the cap is still available, so\n // fall back to the next-lowest source rather than over-granting.\n return { eligible: false, reason: 'stale' };\n }\n\n return evaluateFeeWaiverGate(snapshot.benefits);\n }\n\n /**\n * Refresh the cached subscription benefits snapshot.\n *\n * Deduped: concurrent callers share the in-flight request. Rejections are\n * logged and swallowed, leaving the previous snapshot in place. Preview and\n * lifecycle callers invoke this outside order submission.\n *\n * @returns A promise that settles when the refresh completes.\n */\n async refreshSubscriptionBenefits(): Promise<void> {\n const source = this.#deps.subscription;\n if (!source) {\n return;\n }\n\n if (this.#benefitsRefresh) {\n await this.#benefitsRefresh;\n return;\n }\n\n const now = Date.now();\n const snapshotAge = this.#benefitsSnapshot\n ? now - this.#benefitsSnapshot.fetchedAt\n : Infinity;\n const sinceAttempt =\n this.#lastAttemptAt === undefined ? Infinity : now - this.#lastAttemptAt;\n if (\n snapshotAge < SUBSCRIPTION_BENEFITS_CACHE.FreshMs ||\n sinceAttempt < SUBSCRIPTION_BENEFITS_CACHE.FreshMs\n ) {\n return;\n }\n\n const refresh = this.#readSubscriptionBenefits(source);\n this.#benefitsRefresh = refresh;\n // `finally` always defers, so this never clears the handle we just set.\n refresh\n .finally(() => {\n if (this.#benefitsRefresh === refresh) {\n this.#benefitsRefresh = undefined;\n }\n })\n .catch(() => undefined);\n\n await refresh;\n }\n\n /**\n * Drop the cached benefits snapshot.\n *\n * Call this when the identity behind the benefits changes — sign-out, or a\n * profile switch — since the snapshot carries no profile identity of its own\n * and would otherwise keep answering for the previous profile until the next\n * successful refresh. The next status read reports `not-hydrated`, so the\n * waiver is withheld until a preview or lifecycle caller hydrates it.\n */\n invalidateSubscriptionBenefits(): void {\n this.#benefitsSnapshot = undefined;\n this.#lastAttemptAt = undefined;\n // Fence any in-flight read: it was issued for the previous identity, so its\n // result must not repopulate the cache after this point.\n this.#benefitsEpoch += 1;\n // Drop the dedupe handle too. The fenced read can only be discarded, so\n // leaving it in place would make the next refresh await it instead of\n // fetching for the new identity. Its `finally` guard compares against the\n // current handle, so it will not clear whatever replaces it here.\n this.#benefitsRefresh = undefined;\n\n this.#deps.debugLogger.log(\n 'RewardsIntegrationService: Subscription benefits cache invalidated',\n );\n }\n\n /**\n * Perform one benefits read and store it, keeping the previous snapshot on\n * error. Never rejects, so callers cannot produce an unhandled rejection.\n *\n * @param source - The injected subscription benefits source.\n */\n async #readSubscriptionBenefits(\n source: NonNullable<PerpsPlatformDependencies['subscription']>,\n ): Promise<void> {\n const epoch = this.#benefitsEpoch;\n\n try {\n const benefits = await source.getPerpsBenefits();\n\n if (epoch !== this.#benefitsEpoch) {\n // Invalidated while this read was in flight: it belongs to a previous\n // identity, so discarding it is the only safe outcome.\n this.#deps.debugLogger.log(\n 'RewardsIntegrationService: Discarding benefits read from a previous identity',\n );\n return;\n }\n\n this.#benefitsSnapshot = { benefits, fetchedAt: Date.now() };\n\n this.#deps.debugLogger.log(\n 'RewardsIntegrationService: Subscription benefits refreshed',\n {\n status: benefits?.status,\n entitled: benefits?.perpsFeeWaiver?.entitled,\n usage: benefits?.perpsFeeWaiver?.usage,\n exhausted: benefits?.perpsFeeWaiver?.exhausted,\n },\n );\n } catch (error) {\n // Keep the previous snapshot: an unreachable benefits endpoint must not\n // erase a valid cache, and it must never grant the waiver either.\n this.#deps.logger.error(\n ensureError(\n error,\n 'RewardsIntegrationService.refreshSubscriptionBenefits',\n ),\n {\n tags: { feature: PERPS_CONSTANTS.FeatureName },\n context: {\n name: 'RewardsIntegrationService.refreshSubscriptionBenefits',\n data: {},\n },\n },\n );\n } finally {\n // Recorded on failure too — this is what throttles the retry loop. Not\n // recorded for a fenced read: that attempt belongs to a previous\n // identity, and letting it throttle would delay the new identity's first\n // fetch by a whole freshness window.\n if (epoch === this.#benefitsEpoch) {\n this.#lastAttemptAt = Date.now();\n }\n }\n }\n\n /**\n * Resolve the rewards (VIP + season) discount for the selected account.\n *\n * @returns The discount in basis points, or undefined when unavailable.\n */\n async #calculateRewardsDiscount(): Promise<number | undefined> {\n try {\n const evmAccount = getSelectedEvmAccountFromMessenger(this.#messenger);\n\n if (!evmAccount) {\n this.#deps.debugLogger.log(\n 'RewardsIntegrationService: No EVM account found for fee discount',\n );\n return undefined;\n }\n\n // Get the chain ID via DI network controller\n const networkState = this.#messenger.call('NetworkController:getState');\n const { selectedNetworkClientId } = networkState;\n const chainId = this.#getChainIdForNetwork(selectedNetworkClientId);\n\n if (!chainId) {\n this.#deps.logger.error(\n new Error('Chain ID not found for fee discount calculation'),\n {\n tags: { feature: PERPS_CONSTANTS.FeatureName },\n context: {\n name: 'RewardsIntegrationService.calculateUserFeeDiscount',\n data: {\n selectedNetworkClientId,\n },\n },\n },\n );\n return undefined;\n }\n\n // Use pure utility function for CAIP formatting (pass logger for error reporting)\n const caipAccountId = formatAccountToCaipAccountId(\n evmAccount.address,\n chainId,\n this.#deps.logger,\n );\n\n if (!caipAccountId) {\n this.#deps.logger.error(\n new Error('Failed to format CAIP account ID for fee discount'),\n {\n tags: { feature: PERPS_CONSTANTS.FeatureName },\n context: {\n name: 'RewardsIntegrationService.calculateUserFeeDiscount',\n data: {\n address: evmAccount.address,\n chainId,\n selectedNetworkClientId,\n },\n },\n },\n );\n return undefined;\n }\n\n // Use rewards via DI (no RewardsController in Core yet).\n // The rewards controller needs the perps MetaMask builder base fee in\n // bips to convert an absolute VIP fee into a discount fraction.\n const discountBips = await this.#deps.rewards.getPerpsDiscountForAccount(\n caipAccountId,\n DEFAULT_FEE_BIPS,\n );\n\n // null = subscription state not hydrated yet; surface as undefined so\n // callers don't treat it as a definitive \"no discount\" answer.\n if (discountBips === null) {\n this.#deps.debugLogger.log(\n 'RewardsIntegrationService: Fee discount unavailable (subscription state not hydrated)',\n { address: evmAccount.address, caipAccountId },\n );\n return undefined;\n }\n\n this.#deps.debugLogger.log(\n 'RewardsIntegrationService: Fee discount calculated',\n {\n address: evmAccount.address,\n caipAccountId,\n discountBips,\n discountPercentage: discountBips / 100,\n },\n );\n\n return discountBips;\n } catch (error) {\n this.#deps.logger.error(\n ensureError(\n error,\n 'RewardsIntegrationService.calculateUserFeeDiscount',\n ),\n {\n tags: { feature: PERPS_CONSTANTS.FeatureName },\n context: {\n name: 'RewardsIntegrationService.calculateUserFeeDiscount',\n data: {},\n },\n },\n );\n return undefined;\n }\n }\n}\n\n/**\n * Evaluate the perps fee-waiver eligibility gate against a benefits snapshot.\n *\n * The gate is `status=active` AND `perpsFeeWaiver` entitled AND\n * `usage=available`. A backend `exhausted` flag (or an `exhausted` usage) fails\n * the gate on its own; anything short of an affirmative `available` is treated\n * as not entitled, because the waiver is only granted on positive evidence.\n * A `null` payload means there is no subscription at all, which is reported\n * separately from a subscription that exists but is not active.\n *\n * @param benefits - The cached benefits payload, or null when there is none.\n * @returns The gate outcome plus the remaining notional when reported.\n */\nfunction evaluateFeeWaiverGate(\n benefits: PerpsSubscriptionBenefits | null,\n): PerpsSubscriptionFeeWaiverStatus {\n const waiver = benefits?.perpsFeeWaiver;\n const { remainingNotionalUsd } = waiver ?? {};\n\n // `null` is the DI contract's \"nothing to report\" (signed out, no profile),\n // which is distinct from a subscription that exists but is not active.\n if (benefits === null) {\n return { eligible: false, reason: 'no-subscription' };\n }\n\n if (benefits.status !== 'active') {\n return { eligible: false, reason: 'inactive', remainingNotionalUsd };\n }\n\n if (waiver?.entitled !== true) {\n return { eligible: false, reason: 'not-entitled', remainingNotionalUsd };\n }\n\n if (waiver.exhausted === true || waiver.usage === 'exhausted') {\n return { eligible: false, reason: 'exhausted', remainingNotionalUsd };\n }\n\n if (waiver.usage !== 'available') {\n return { eligible: false, reason: 'not-entitled', remainingNotionalUsd };\n }\n\n return { eligible: true, reason: 'eligible', remainingNotionalUsd };\n}\n"]}
1
+ {"version":3,"file":"RewardsIntegrationService.js","sourceRoot":"","sources":["../../src/services/RewardsIntegrationService.ts"],"names":[],"mappings":"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","sourcesContent":["import type { SubscriptionBenefitsResponse } from '@metamask/subscription-controller';\n\nimport {\n BASIS_POINTS_DIVISOR,\n BUILDER_FEE_CONFIG,\n HYPERLIQUID_MAINNET_CHAIN_ID,\n HYPERLIQUID_TESTNET_CHAIN_ID,\n} from '../constants/hyperLiquidConfig.js';\nimport {\n PERPS_CONSTANTS,\n SUBSCRIPTION_BENEFITS_CACHE,\n SUBSCRIPTION_FEE_WAIVER_FLAG,\n} from '../constants/perpsConfig.js';\nimport type {\n PerpsFeeResolution,\n PerpsFeeSource,\n PerpsPlatformDependencies,\n PerpsSubscriptionBenefits,\n PerpsSubscriptionFeeWaiverStatus,\n} from '../types/index.js';\nimport type { PerpsControllerMessengerBase } from '../types/messenger.js';\nimport { getSelectedEvmAccountFromMessenger } from '../utils/accountUtils.js';\nimport { ensureError } from '../utils/errorUtils.js';\nimport { formatAccountToCaipAccountId } from '../utils/rewardsUtils.js';\nimport {\n quantizeBuilderFeeTenthsBps,\n resolveSubscriptionWaiverRate,\n} from '../utils/subscriptionFeeWaiver.js';\n\n/**\n * Default MetaMask builder fee, in basis points.\n * This is the fee every user pays when no cheaper source applies.\n */\nconst DEFAULT_FEE_BIPS =\n BUILDER_FEE_CONFIG.MaxFeeDecimal * BASIS_POINTS_DIVISOR;\n\n/** `SubscriptionController` reports allowances in micro-USD. */\nconst MICRO_USD_PER_USD = 1_000_000;\n\n/**\n * Cached subscription benefits plus the time they were read.\n */\ntype BenefitsSnapshot = {\n benefits: PerpsSubscriptionBenefits | null;\n fetchedAt: number;\n};\n\n/**\n * RewardsIntegrationService\n *\n * Owns the unified perps fee resolver: it considers every fee source and\n * returns the lowest fee, expressed as the discount bips providers consume.\n *\n * Sources, all in fee basis points (lowest wins):\n * - `default` — {@link BUILDER_FEE_CONFIG}, the fee with no reductions.\n * - `rewards` — VIP and season, collapsed into one discount by\n * `RewardsController` (`rewards.getPerpsDiscountForAccount`), so this service\n * does not re-derive the VIP/season split.\n * - `subscription` — `0` bips, but only when the eligibility gate passes on a\n * cached read of the profile's benefits.\n *\n * On a tie the cheaper-to-explain source wins, in the order\n * `subscription` > `rewards` > `default`.\n *\n * The benefits cache is stale-while-revalidate: fee resolution is a pure read\n * of the cached snapshot, while preview and lifecycle callers refresh it\n * explicitly. Nothing is reserved or committed client-side, so backend\n * exhaustion needs no release logic — the next refresh simply stops passing\n * the gate.\n *\n * Instance-based service with constructor injection of platform dependencies.\n */\nexport class RewardsIntegrationService {\n readonly #deps: PerpsPlatformDependencies;\n\n readonly #messenger: PerpsControllerMessengerBase;\n\n /** Last successful benefits read, or undefined before the first one. */\n #benefitsSnapshot: BenefitsSnapshot | undefined;\n\n /**\n * When the last benefits read finished, successful or not.\n *\n * Separate from `#benefitsSnapshot.fetchedAt`, which only advances on\n * success: a failing read must still throttle the next preview refresh,\n * otherwise an outage turns every fee preview into a new request.\n */\n #lastAttemptAt: number | undefined;\n\n /** In-flight refresh, deduped so only one runs at a time. */\n #benefitsRefresh: Promise<void> | undefined;\n\n /**\n * Identity generation for the cached benefits.\n *\n * Bumped by {@link invalidateSubscriptionBenefits}; a read that resolves\n * against a superseded epoch is discarded rather than written back, so a\n * refresh issued for the previous profile cannot repopulate the cache after\n * a sign-out or profile switch.\n */\n #benefitsEpoch = 0;\n\n /**\n * CAIP-10 addresses already registered with the subscription profile this\n * session, so preview does not re-send the same registration on every\n * keystroke. Cleared on account switch and on cache invalidation.\n */\n readonly #registeredTradingAddresses = new Set<string>();\n\n /**\n * Whether `SubscriptionController:getBenefits` has ever answered on this\n * messenger. Action registration can be delegated, in which case it does not\n * appear in `getRegisteredActionTypes`, so an answered call is the only\n * reliable proof that the messenger route is available.\n */\n #messengerBenefitsAnswered = false;\n\n /**\n * Create a new RewardsIntegrationService instance\n *\n * @param deps - Platform dependencies for logging, metrics, etc.\n * @param messenger - Controller messenger for cross-controller communication.\n */\n constructor(\n deps: PerpsPlatformDependencies,\n messenger: PerpsControllerMessengerBase,\n ) {\n this.#deps = deps;\n this.#messenger = messenger;\n }\n\n /**\n * Get chain ID for a network client via DI network controller\n *\n * @param networkClientId - The network client identifier to look up.\n * @returns The chain ID string, or undefined if the network client is not found.\n */\n #getChainIdForNetwork(networkClientId: string): string | undefined {\n try {\n const networkClient = this.#messenger.call(\n 'NetworkController:getNetworkClientById',\n networkClientId,\n );\n return networkClient.configuration.chainId;\n } catch {\n // Network client may not exist\n return undefined;\n }\n }\n\n /**\n * Calculate user fee discount from the unified fee resolver.\n * Returns discount in basis points (e.g., 6500 = 65% discount)\n *\n * @param orderNotionalUsd - Order notional (USD), when the caller knows it.\n * @returns The fee discount in basis points, or undefined if no source resolved.\n */\n async calculateUserFeeDiscount(\n orderNotionalUsd?: number,\n ): Promise<number | undefined> {\n const resolution = await this.resolveFee(orderNotionalUsd);\n return resolution.discountBips;\n }\n\n /**\n * Resolve the MetaMask builder fee across every source and return the lowest.\n *\n * Never throws and never starts a subscription benefits read: a failing or\n * unresolved cached source simply drops out of the comparison, so the worst\n * case is the default fee rather than an error or an over-granted waiver.\n *\n * The subscription source contributes an effective rate rather than a flat\n * zero (ADR 0064): the allowance may cover only part of the order, and the\n * blend that results has to be able to lose to a deeper VIP or season\n * discount. Passing the order notional is what makes that blend possible.\n *\n * Without a notional the outcome depends on whether the backend bounded the\n * allowance. An unbounded allowance still resolves to the full waiver — there\n * is no cap to over-consume. A *bounded* one is withheld entirely rather than\n * quoted as a full waiver, because charging nothing on an order of unknown\n * size would silently spend the cap; such a caller gets the next-lowest\n * source instead.\n *\n * @param orderNotionalUsd - Order notional (USD), when the caller knows it.\n * @returns The winning fee, its source, and the subscription gate outcome.\n */\n async resolveFee(orderNotionalUsd?: number): Promise<PerpsFeeResolution> {\n const rewardsDiscountBips = await this.#calculateRewardsDiscount();\n // Pure cache read: subscription benefits must never start a network request\n // while an order is being prepared for signing.\n const subscription = this.getSubscriptionFeeWaiverStatus();\n\n let feeBips = DEFAULT_FEE_BIPS;\n let source: PerpsFeeSource = 'default';\n\n if (rewardsDiscountBips !== undefined) {\n const rewardsFeeBips =\n DEFAULT_FEE_BIPS * (1 - rewardsDiscountBips / BASIS_POINTS_DIVISOR);\n // `<=` so an equal rewards fee still reports the rewards source, keeping\n // a resolved 0% discount distinguishable from an unresolved one.\n if (rewardsFeeBips <= feeBips) {\n feeBips = rewardsFeeBips;\n source = 'rewards';\n }\n }\n\n const waiver = resolveSubscriptionWaiverRate({\n status: subscription,\n maxFeeBips: DEFAULT_FEE_BIPS,\n orderNotionalUsd,\n });\n\n let subscriptionWaiverKind: PerpsFeeResolution['subscriptionWaiverKind'];\n let subscriptionCoveredNotionalUsd: number | undefined;\n\n // The waiver competes like any other source. A full waiver still wins on\n // `<=`, but a partial blend only wins when it is genuinely cheaper than the\n // rewards discount — the ADR's requirement that subscription be able to\n // lose.\n //\n // Compared *after* venue quantization, because that is the fee the order\n // pays. The builder fee is submitted in integer tenths of a basis point, so\n // a blend like 9.9999 bips is cheaper than the 10-bip default in arithmetic\n // and identical to it on the wire. Selecting subscription on the raw number\n // would label such an order `source: 'subscription'` with a 0-bip discount\n // while it pays full price, and would disagree with the cloid marker, which\n // already gates on the quantized fee.\n const toTenthsBps = (bips: number): number =>\n quantizeBuilderFeeTenthsBps(\n Math.round((1 - bips / DEFAULT_FEE_BIPS) * BASIS_POINTS_DIVISOR),\n );\n\n // Subscription must be strictly cheaper on the wire to claim the order. A\n // tie buys the user nothing — the same rate is already available from the\n // source that won — while claiming it marks the cloid and spends the\n // remaining allowance, so a tie is left to the other source. That holds at\n // any rate, including a rewards discount that already reaches 0 bips.\n const waiverSurvivesQuantization =\n toTenthsBps(waiver.feeBips) < toTenthsBps(feeBips);\n\n if (\n waiver.applies &&\n waiver.feeBips <= feeBips &&\n waiverSurvivesQuantization\n ) {\n feeBips = waiver.feeBips;\n source = 'subscription';\n subscriptionWaiverKind = waiver.kind === 'partial' ? 'partial' : 'full';\n subscriptionCoveredNotionalUsd = waiver.coveredNotionalUsd;\n }\n\n const discountBips =\n source === 'default'\n ? undefined\n : Math.round((1 - feeBips / DEFAULT_FEE_BIPS) * BASIS_POINTS_DIVISOR);\n\n this.#deps.debugLogger.log('RewardsIntegrationService: Fee resolved', {\n source,\n feeBips,\n discountBips,\n defaultFeeBips: DEFAULT_FEE_BIPS,\n rewardsDiscountBips,\n orderNotionalUsd,\n subscriptionEligible: subscription.eligible,\n subscriptionReason: subscription.reason,\n subscriptionWaiverKind,\n subscriptionCoveredNotionalUsd,\n });\n\n return {\n feeBips,\n discountBips,\n source,\n subscription,\n subscriptionWaiverKind,\n subscriptionCoveredNotionalUsd,\n };\n }\n\n /**\n * Read the subscription fee-waiver gate from the cached benefits snapshot.\n *\n * Synchronous and side-effect free. The returned value always comes from\n * what is already cached; preview and lifecycle callers own hydration.\n *\n * @returns Whether the waiver applies, why, and the remaining notional.\n */\n getSubscriptionFeeWaiverStatus(): PerpsSubscriptionFeeWaiverStatus {\n if (!this.#hasSubscriptionSource()) {\n return { eligible: false, reason: 'no-source' };\n }\n\n // ADR 0064 Milestone 8: the subscription source has to be killable on its\n // own. Reported as `no-source` so a disabled flag is indistinguishable from\n // an unwired client downstream — both mean \"subscription contributes\n // nothing\", and neither touches rewards or the default fee.\n if (!this.#isSubscriptionFeeWaiverEnabled()) {\n return { eligible: false, reason: 'no-source' };\n }\n\n const now = Date.now();\n const snapshot = this.#benefitsSnapshot;\n const age = snapshot ? now - snapshot.fetchedAt : Infinity;\n if (!snapshot) {\n return { eligible: false, reason: 'not-hydrated' };\n }\n\n if (age > SUBSCRIPTION_BENEFITS_CACHE.MaxStaleMs) {\n // Past the ceiling we cannot tell whether the cap is still available, so\n // fall back to the next-lowest source rather than over-granting.\n return { eligible: false, reason: 'stale' };\n }\n\n return evaluateFeeWaiverGate(snapshot.benefits);\n }\n\n /**\n * Whether this client has any way to read subscription benefits.\n *\n * Either wiring counts: a registered `SubscriptionController:getBenefits`\n * action (the ADR 0064 target) or the legacy injected `subscription` callback.\n * Requiring the injected one would make the messenger path unreachable on\n * exactly the configuration it was added for, so the messenger is probed by\n * asking whether an action handler exists rather than by calling it — this\n * runs on the synchronous status read and must not start work.\n *\n * @returns True when benefits can be read by some route.\n */\n #hasSubscriptionSource(): boolean {\n if (this.#deps.subscription || this.#messengerBenefitsAnswered) {\n return true;\n }\n\n try {\n // `getRegisteredActionTypes` reports this messenger's own registrations.\n // A delegated action does not appear there, which is why it is only a\n // positive signal — `refreshSubscriptionBenefits` still attempts the call\n // regardless, and an answer sets `#messengerBenefitsAnswered` above.\n return this.#messenger\n .getRegisteredActionTypes()\n .includes('SubscriptionController:getBenefits');\n } catch {\n return false;\n }\n }\n\n /**\n * Whether the subscription fee-waiver source is enabled remotely.\n *\n * Fails open: an absent flag, a malformed value, or an unreachable flag\n * controller all read as enabled, because silently dropping a benefit the\n * user pays for is worse than serving it one release too long. The kill\n * switch is an explicit `false`.\n *\n * @returns True unless the remote flag explicitly disables the source.\n */\n #isSubscriptionFeeWaiverEnabled(): boolean {\n try {\n const { remoteFeatureFlags } = this.#messenger.call(\n 'RemoteFeatureFlagController:getState',\n );\n const flag = remoteFeatureFlags?.[SUBSCRIPTION_FEE_WAIVER_FLAG];\n return flag !== false;\n } catch {\n // No flag controller registered, or the read threw: keep the source.\n return true;\n }\n }\n\n /**\n * Refresh the cached subscription benefits snapshot.\n *\n * Deduped: concurrent callers share the in-flight request. Rejections are\n * logged and swallowed, leaving the previous snapshot in place. Preview and\n * lifecycle callers invoke this outside order submission.\n *\n * @returns A promise that settles when the refresh completes.\n */\n async refreshSubscriptionBenefits(): Promise<void> {\n // Deliberately not gated on `#hasSubscriptionSource`: a delegated action is\n // invisible to `getRegisteredActionTypes`, so the first call is what proves\n // it is reachable. A client with no source at all reads `null` here, which\n // costs one no-op call per freshness window and leaves the gate closed.\n\n if (this.#benefitsRefresh) {\n await this.#benefitsRefresh;\n return;\n }\n\n const now = Date.now();\n const snapshotAge = this.#benefitsSnapshot\n ? now - this.#benefitsSnapshot.fetchedAt\n : Infinity;\n const sinceAttempt =\n this.#lastAttemptAt === undefined ? Infinity : now - this.#lastAttemptAt;\n if (\n snapshotAge < SUBSCRIPTION_BENEFITS_CACHE.FreshMs ||\n sinceAttempt < SUBSCRIPTION_BENEFITS_CACHE.FreshMs\n ) {\n return;\n }\n\n const refresh = this.#readSubscriptionBenefits();\n this.#benefitsRefresh = refresh;\n // `finally` always defers, so this never clears the handle we just set.\n refresh\n .finally(() => {\n if (this.#benefitsRefresh === refresh) {\n this.#benefitsRefresh = undefined;\n }\n })\n .catch(() => undefined);\n\n await refresh;\n }\n\n /**\n * Drop the cached benefits snapshot.\n *\n * Call this when the identity behind the benefits changes — sign-out, or a\n * profile switch — since the snapshot carries no profile identity of its own\n * and would otherwise keep answering for the previous profile until the next\n * successful refresh. The next status read reports `not-hydrated`, so the\n * waiver is withheld until a preview or lifecycle caller hydrates it.\n */\n invalidateSubscriptionBenefits(): void {\n this.#benefitsSnapshot = undefined;\n this.#lastAttemptAt = undefined;\n // Fence any in-flight read: it was issued for the previous identity, so its\n // result must not repopulate the cache after this point.\n this.#benefitsEpoch += 1;\n // Drop the dedupe handle too. The fenced read can only be discarded, so\n // leaving it in place would make the next refresh await it instead of\n // fetching for the new identity. Its `finally` guard compares against the\n // current handle, so it will not clear whatever replaces it here.\n this.#benefitsRefresh = undefined;\n // The identity behind the registration changed too, so the new one has to\n // announce itself rather than inherit the previous profile's registration.\n this.#registeredTradingAddresses.clear();\n\n this.#deps.debugLogger.log(\n 'RewardsIntegrationService: Subscription benefits cache invalidated',\n );\n }\n\n /**\n * Perform one benefits read and store it, keeping the previous snapshot on\n * error. Never rejects, so callers cannot produce an unhandled rejection.\n */\n async #readSubscriptionBenefits(): Promise<void> {\n const epoch = this.#benefitsEpoch;\n\n try {\n const benefits = await this.#getPerpsBenefits();\n\n if (epoch !== this.#benefitsEpoch) {\n // Invalidated while this read was in flight: it belongs to a previous\n // identity, so discarding it is the only safe outcome.\n this.#deps.debugLogger.log(\n 'RewardsIntegrationService: Discarding benefits read from a previous identity',\n );\n return;\n }\n\n this.#benefitsSnapshot = { benefits, fetchedAt: Date.now() };\n\n this.#deps.debugLogger.log(\n 'RewardsIntegrationService: Subscription benefits refreshed',\n {\n status: benefits?.status,\n entitled: benefits?.perpsFeeWaiver?.entitled,\n usage: benefits?.perpsFeeWaiver?.usage,\n exhausted: benefits?.perpsFeeWaiver?.exhausted,\n },\n );\n } catch (error) {\n // Keep the previous snapshot: an unreachable benefits endpoint must not\n // erase a valid cache, and it must never grant the waiver either.\n this.#deps.logger.error(\n ensureError(\n error,\n 'RewardsIntegrationService.refreshSubscriptionBenefits',\n ),\n {\n tags: { feature: PERPS_CONSTANTS.FeatureName },\n context: {\n name: 'RewardsIntegrationService.refreshSubscriptionBenefits',\n data: {},\n },\n },\n );\n } finally {\n // Recorded on failure too — this is what throttles the retry loop. Not\n // recorded for a fenced read: that attempt belongs to a previous\n // identity, and letting it throttle would delay the new identity's first\n // fetch by a whole freshness window.\n if (epoch === this.#benefitsEpoch) {\n this.#lastAttemptAt = Date.now();\n }\n }\n }\n\n /**\n * Read subscription benefits, preferring the messenger over the DI callback.\n *\n * ADR 0064 moves hydration onto `SubscriptionController`. The messenger is\n * tried first and the injected `subscription` dependency is only a fallback,\n * so a client that ships `SubscriptionController` without the legacy callback\n * hydrates normally — that configuration is the ADR's target, not an edge\n * case. A client with neither gets `null`, which reads as \"no subscription\".\n *\n * @returns The benefits payload, or null when there is none to report.\n */\n async #getPerpsBenefits(): Promise<PerpsSubscriptionBenefits | null> {\n let pending: Promise<PerpsSubscriptionBenefits | null> | undefined;\n try {\n // Called without awaiting so the fallback stays synchronous when no\n // handler is registered: the DI read must start in the same tick, or a\n // caller that inspects the in-flight state sees an idle service.\n const result = this.#messenger.call('SubscriptionController:getBenefits');\n // `undefined` means nothing handled the action, which is the fallback\n // case rather than an answer.\n if (result !== undefined) {\n this.#messengerBenefitsAnswered = true;\n pending = Promise.resolve(result).then(adaptSubscriptionBenefits);\n }\n } catch (error) {\n // A handler that has answered before exists, so a synchronous throw is a\n // real failure rather than an unregistered action. Treating it as the\n // latter would fall through to `null` and erase a valid cached snapshot,\n // exactly as an asynchronous rejection would.\n if (isNotSubscribedRejection(error)) {\n // Definitive \"not entitled\", even thrown synchronously.\n return null;\n }\n // An unregistered action throws a recognisable \"no handler\" error; any\n // other synchronous throw came from a handler that exists and failed, so\n // it must not be mistaken for an absent action and cached as `null` —\n // including on the very first call, before one has ever answered.\n if (!isUnregisteredActionError(error) && !this.#deps.subscription) {\n throw error;\n }\n // Otherwise: unregistered action, or a throw with a DI source to fall\n // back to.\n }\n\n const fallback = this.#deps.subscription;\n\n if (pending) {\n try {\n return await pending;\n } catch (error) {\n if (isNotSubscribedRejection(error)) {\n // A definitive answer, not a failed read: the profile is not\n // entitled. Returning `null` replaces the cached snapshot, which is\n // the point — preserving it would keep granting the waiver for the\n // rest of the staleness window after entitlement ended.\n return null;\n }\n if (!fallback) {\n // Nothing else can answer, so this rejection is the whole result.\n // Returning `null` here would be stored as a successful \"no\n // subscription\" snapshot and silently erase a valid cached one; let\n // it reach the refresh handler, which keeps the previous snapshot.\n throw error;\n }\n // A registered handler that rejects still falls back to the injected\n // source rather than erasing the cached snapshot.\n }\n }\n\n if (!fallback) {\n // No handler answered and no injected source: genuinely nothing to\n // report, which is a real `null` rather than a swallowed failure.\n return null;\n }\n\n return await fallback.getPerpsBenefits();\n }\n\n /**\n * Register the current HyperLiquid trading address with the subscription\n * profile, so a later fill decoded off the HL fan-out can be attributed.\n *\n * ADR 0064 calls for this at preview time and again whenever the selected\n * account changes. Registration is idempotent backend-side and deliberately\n * never throws: it is observability plumbing, and a failure here must not\n * block a fee preview.\n *\n * New clients may route this through the structural\n * `SubscriptionController:registerAddress` action. Older clients continue to\n * use the injected `subscription` hook as a compatibility fallback.\n *\n * @param address - The EVM trading address to register.\n * @param options - Registration options.\n * @param options.isTestnet - Whether the caller is trading against the\n * HyperLiquid testnet. Selects which HyperLiquid chain the CAIP-10 names;\n * defaults to mainnet.\n * @returns A promise that resolves once the attempt settles.\n */\n async registerTradingAddress(\n address: string,\n options?: { isTestnet?: boolean },\n ): Promise<void> {\n try {\n // HyperLiquid's own chain, not the wallet's selected network. The address\n // is being announced so a fill decoded off the HyperLiquid fan-out can be\n // matched back to a profile, and that fill names `eip155:999` /\n // `eip155:998`. Registering under whatever network the wallet happened to\n // have selected produces an identifier no fill will ever match.\n const chainId = options?.isTestnet\n ? HYPERLIQUID_TESTNET_CHAIN_ID\n : HYPERLIQUID_MAINNET_CHAIN_ID;\n\n const caipAccountId = formatAccountToCaipAccountId(\n address,\n chainId,\n this.#deps.logger,\n );\n\n if (!caipAccountId) {\n return;\n }\n\n // Skip the round trip when this address was already registered for this\n // session: preview runs on every keystroke in the order form.\n if (this.#registeredTradingAddresses.has(caipAccountId)) {\n return;\n }\n\n // Capture the identity before invoking either integration. If profile\n // invalidation happens while the handler is pending, its completion must\n // not repopulate the registration cache for the old profile.\n const epoch = this.#benefitsEpoch;\n\n // New clients can route registration through SubscriptionController.\n // Older clients do not expose this action and continue using the\n // injected hook below.\n let registration: Promise<void> | undefined;\n try {\n const result = this.#messenger.call(\n 'SubscriptionController:registerAddress',\n caipAccountId,\n );\n if (result !== undefined) {\n registration = Promise.resolve(result);\n }\n } catch (error) {\n if (!isUnregisteredActionError(error) && !this.#deps.subscription) {\n throw error;\n }\n }\n\n if (!registration) {\n const source = this.#deps.subscription;\n if (!source?.registerTradingAddress) {\n // Visible rather than silent: a client wired only to benefits has no\n // way to register, and a missing registration is otherwise\n // indetectable until fills arrive unattributed.\n this.#deps.debugLogger.log(\n 'RewardsIntegrationService: No trading-address registration hook wired; fills will be unattributed',\n { address },\n );\n return;\n }\n registration = source.registerTradingAddress(caipAccountId);\n }\n\n // Fence the completion against an invalidation that lands while this\n // registration is in flight. `invalidateSubscriptionBenefits` clears the\n // registered set because the profile behind it changed; writing back\n // afterwards would re-add an address registered for the *previous*\n // profile, and the new profile would then skip its own registration and\n // leave its fills unattributable.\n await registration;\n if (epoch !== this.#benefitsEpoch) {\n this.#deps.debugLogger.log(\n 'RewardsIntegrationService: Trading address registration superseded',\n { caipAccountId },\n );\n return;\n }\n this.#registeredTradingAddresses.add(caipAccountId);\n\n this.#deps.debugLogger.log(\n 'RewardsIntegrationService: Trading address registered',\n { caipAccountId },\n );\n } catch (error) {\n // An offline client or a backend refusal both land here. Neither is a\n // reason to fail a fee preview.\n this.#deps.debugLogger.log(\n 'RewardsIntegrationService: Trading address registration skipped',\n {\n address,\n error: ensureError(\n error,\n 'RewardsIntegrationService.registerTradingAddress',\n ).message,\n },\n );\n }\n }\n\n /**\n * Forget which trading addresses were registered this session.\n *\n * Called when the selected account changes, so the next preview re-sends the\n * registration for the new address rather than assuming the previous one\n * still stands.\n */\n resetRegisteredTradingAddresses(): void {\n this.#registeredTradingAddresses.clear();\n }\n\n /**\n * Resolve the rewards (VIP + season) discount for the selected account.\n *\n * @returns The discount in basis points, or undefined when unavailable.\n */\n async #calculateRewardsDiscount(): Promise<number | undefined> {\n try {\n const evmAccount = getSelectedEvmAccountFromMessenger(this.#messenger);\n\n if (!evmAccount) {\n this.#deps.debugLogger.log(\n 'RewardsIntegrationService: No EVM account found for fee discount',\n );\n return undefined;\n }\n\n // Get the chain ID via DI network controller\n const networkState = this.#messenger.call('NetworkController:getState');\n const { selectedNetworkClientId } = networkState;\n const chainId = this.#getChainIdForNetwork(selectedNetworkClientId);\n\n if (!chainId) {\n this.#deps.logger.error(\n new Error('Chain ID not found for fee discount calculation'),\n {\n tags: { feature: PERPS_CONSTANTS.FeatureName },\n context: {\n name: 'RewardsIntegrationService.calculateUserFeeDiscount',\n data: {\n selectedNetworkClientId,\n },\n },\n },\n );\n return undefined;\n }\n\n // Use pure utility function for CAIP formatting (pass logger for error reporting)\n const caipAccountId = formatAccountToCaipAccountId(\n evmAccount.address,\n chainId,\n this.#deps.logger,\n );\n\n if (!caipAccountId) {\n this.#deps.logger.error(\n new Error('Failed to format CAIP account ID for fee discount'),\n {\n tags: { feature: PERPS_CONSTANTS.FeatureName },\n context: {\n name: 'RewardsIntegrationService.calculateUserFeeDiscount',\n data: {\n address: evmAccount.address,\n chainId,\n selectedNetworkClientId,\n },\n },\n },\n );\n return undefined;\n }\n\n // Use rewards via DI (no RewardsController in Core yet).\n // The rewards controller needs the perps MetaMask builder base fee in\n // bips to convert an absolute VIP fee into a discount fraction.\n const discountBips = await this.#deps.rewards.getPerpsDiscountForAccount(\n caipAccountId,\n DEFAULT_FEE_BIPS,\n );\n\n // null = subscription state not hydrated yet; surface as undefined so\n // callers don't treat it as a definitive \"no discount\" answer.\n if (discountBips === null) {\n this.#deps.debugLogger.log(\n 'RewardsIntegrationService: Fee discount unavailable (subscription state not hydrated)',\n { address: evmAccount.address, caipAccountId },\n );\n return undefined;\n }\n\n this.#deps.debugLogger.log(\n 'RewardsIntegrationService: Fee discount calculated',\n {\n address: evmAccount.address,\n caipAccountId,\n discountBips,\n discountPercentage: discountBips / 100,\n },\n );\n\n return discountBips;\n } catch (error) {\n this.#deps.logger.error(\n ensureError(\n error,\n 'RewardsIntegrationService.calculateUserFeeDiscount',\n ),\n {\n tags: { feature: PERPS_CONSTANTS.FeatureName },\n context: {\n name: 'RewardsIntegrationService.calculateUserFeeDiscount',\n data: {},\n },\n },\n );\n return undefined;\n }\n }\n}\n\n/**\n * Whether a messenger call failed because no handler is registered.\n *\n * `Messenger.call` reports an unregistered action with a distinctive message.\n * Anything else thrown synchronously came from a handler that does exist, and\n * conflating the two would cache a real failure as \"no subscription\".\n *\n * @param error - The error thrown by the messenger call.\n * @returns True when the action has no registered handler.\n */\nfunction isUnregisteredActionError(error: unknown): boolean {\n return (\n error instanceof Error &&\n /handler.*not.*registered|no.*handler.*registered|A handler for .* has not been registered/iu.test(\n error.message,\n )\n );\n}\n\n/**\n * Whether a benefits rejection definitively means \"this profile is not\n * entitled\", as opposed to \"the read failed\".\n *\n * `SubscriptionController.getBenefits` throws `UserNotSubscribed` when the\n * subscription is inactive or the response reports ineligibility, and clears\n * its own benefits state on that path. Treating it as a transport failure would\n * keep serving a cached waiver for the rest of the staleness window — up to ten\n * minutes of free trading after entitlement ended — so it is converted to a\n * real `null` answer instead.\n *\n * Matched on the message because the controller throws a plain `Error`; any\n * other failure stays a failure and preserves the cached snapshot.\n *\n * @param error - The rejection from the benefits read.\n * @returns True when the rejection means the profile is not entitled.\n */\nfunction isNotSubscribedRejection(error: unknown): boolean {\n return (\n error instanceof Error && error.message.includes('User is not subscribed')\n );\n}\n\n/**\n * Convert `SubscriptionController`'s benefits response into the shape the\n * waiver gate reads.\n *\n * The controller reports allowances in **micro-USD** (`remainingMicroUsd`) and\n * carries no `status`/`entitled` fields: eligibility is the response's own\n * `eligible` flag, and the perps product block holds the cap. Converting once,\n * here at the boundary, keeps the gate and the blended-rate formula working in\n * whole USD.\n *\n * A `remainingMicroUsd` of `null` means the backend reported no bound, which\n * stays an unbounded allowance rather than a spent one.\n *\n * @param response - The controller's benefits response.\n * @returns The internal benefits shape, or null when nothing is entitled.\n */\nfunction adaptSubscriptionBenefits(\n response: SubscriptionBenefitsResponse | null | undefined,\n): PerpsSubscriptionBenefits | null {\n if (!response) {\n return null;\n }\n\n const perps = response.products?.perps;\n // `products.perps` is always present on the response, so its existence proves\n // nothing. Entitlement is the response-level `eligible` flag plus positive\n // evidence from the perps block itself: a builder fee rate to apply, or a\n // reported allowance to spend. A block carrying neither describes a profile\n // with no perps benefit, whatever the other products say.\n const hasPerpsBenefit = Boolean(\n perps &&\n (perps.builderFeeBips !== null ||\n perps.remainingMicroUsd !== null ||\n perps.capMicroUsd !== undefined),\n );\n\n return {\n status: response.eligible ? 'active' : 'inactive',\n perpsFeeWaiver: {\n entitled: response.eligible && hasPerpsBenefit,\n usage: perps?.exhausted ? 'exhausted' : 'available',\n exhausted: perps?.exhausted,\n remainingNotionalUsd:\n perps?.remainingMicroUsd === null ||\n perps?.remainingMicroUsd === undefined\n ? undefined\n : perps.remainingMicroUsd / MICRO_USD_PER_USD,\n },\n };\n}\n\n/**\n * Evaluate the perps fee-waiver eligibility gate against a benefits snapshot.\n *\n * The gate is `status=active` AND `perpsFeeWaiver` entitled AND\n * `usage=available`. A backend `exhausted` flag (or an `exhausted` usage) fails\n * the gate on its own; anything short of an affirmative `available` is treated\n * as not entitled, because the waiver is only granted on positive evidence.\n * A `null` payload means there is no subscription at all, which is reported\n * separately from a subscription that exists but is not active.\n *\n * @param benefits - The cached benefits payload, or null when there is none.\n * @returns The gate outcome plus the remaining notional when reported.\n */\nfunction evaluateFeeWaiverGate(\n benefits: PerpsSubscriptionBenefits | null,\n): PerpsSubscriptionFeeWaiverStatus {\n const waiver = benefits?.perpsFeeWaiver;\n const { remainingNotionalUsd } = waiver ?? {};\n\n // `null` is the DI contract's \"nothing to report\" (signed out, no profile),\n // which is distinct from a subscription that exists but is not active.\n if (benefits === null) {\n return { eligible: false, reason: 'no-subscription' };\n }\n\n if (benefits.status !== 'active') {\n return { eligible: false, reason: 'inactive', remainingNotionalUsd };\n }\n\n if (waiver?.entitled !== true) {\n return { eligible: false, reason: 'not-entitled', remainingNotionalUsd };\n }\n\n if (waiver.exhausted === true || waiver.usage === 'exhausted') {\n return { eligible: false, reason: 'exhausted', remainingNotionalUsd };\n }\n\n if (waiver.usage !== 'available') {\n return { eligible: false, reason: 'not-entitled', remainingNotionalUsd };\n }\n\n return { eligible: true, reason: 'eligible', remainingNotionalUsd };\n}\n"]}
@@ -1,5 +1,5 @@
1
1
  import type { PerpsControllerState } from '../PerpsController.js';
2
- import type { Order, PerpsGlobalSnapshotRequest, PerpsSubscriptionFeeWaiverStatus, Position } from '../types/index.js';
2
+ import type { Order, PerpsFeeResolution, PerpsGlobalSnapshotRequest, PerpsSubscriptionFeeWaiverStatus, Position } from '../types/index.js';
3
3
  /**
4
4
  * ServiceContext
5
5
  *
@@ -71,6 +71,14 @@ export type ServiceContext = {
71
71
  * subscription source is wired.
72
72
  */
73
73
  subscriptionFeeWaiver?: PerpsSubscriptionFeeWaiverStatus;
74
+ /**
75
+ * Unified fee resolution for the quote being previewed.
76
+ *
77
+ * Carries the same blended subscription rate the submit path will charge, so
78
+ * a preview quotes what the order actually pays rather than the undiscounted
79
+ * builder fee. Omitted when no resolution was computed.
80
+ */
81
+ feeResolution?: PerpsFeeResolution;
74
82
  /**
75
83
  * Callback functions for controller-specific operations
76
84
  */
@@ -1 +1 @@
1
- {"version":3,"file":"ServiceContext.d.ts","sourceRoot":"","sources":["../../src/services/ServiceContext.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAClE,OAAO,KAAK,EACV,KAAK,EACL,0BAA0B,EAC1B,gCAAgC,EAChC,QAAQ,EACT,MAAM,mBAAmB,CAAC;AAE3B;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B;;;OAGG;IACH,cAAc,EAAE;QACd,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,OAAO,CAAC;KACpB,CAAC;IAEF;;;OAGG;IACH,YAAY,EAAE;QACZ,UAAU,EAAE,MAAM,CAAC;QACnB,MAAM,EAAE,MAAM,CAAC;QACf,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACjC,CAAC;IAEF;;;;OAIG;IACH,YAAY,CAAC,EAAE;QACb,MAAM,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,oBAAoB,KAAK,IAAI,KAAK,IAAI,CAAC;QACjE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;KACtC,CAAC;IAEF;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;IACvC,YAAY,CAAC,EAAE,MAAM,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;IAEzC;;;OAGG;IACH,cAAc,CAAC,EAAE;QACf,OAAO,EAAE,0BAA0B,CAAC;QACpC,SAAS,EAAE,MAAM,OAAO,CAAC;QACzB,eAAe,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC;KAC9C,CAAC;IAEF;;;;;OAKG;IACH,qBAAqB,CAAC,EAAE,gCAAgC,CAAC;IAEzD;;OAEG;IACH,sBAAsB,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAEpE;;;OAGG;IACH,oBAAoB,CAAC,EAAE,MAAM;QAC3B,IAAI,EAAE,MAAM,EAAE,CAAC;QACf,MAAM,EAAE,QAAQ,GAAG,UAAU,CAAC;KAC/B,CAAC;IACF,oBAAoB,CAAC,EAAE,CACrB,IAAI,EAAE,MAAM,EAAE,EACd,MAAM,EAAE,QAAQ,GAAG,UAAU,KAC1B,IAAI,CAAC;IACV,aAAa,CAAC,EAAE,MAAM;QACpB,OAAO,EAAE,OAAO,CAAC;QACjB,gBAAgB,EAAE,MAAM,EAAE,CAAC;QAC3B,gBAAgB,EAAE,MAAM,EAAE,CAAC;QAC3B,MAAM,EAAE,QAAQ,GAAG,UAAU,CAAC;KAC/B,CAAC;IACF,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE;QACvB,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;QAC5B,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;QAC5B,MAAM,EAAE,QAAQ,GAAG,UAAU,CAAC;KAC/B,KAAK,IAAI,CAAC;IACX,0BAA0B,CAAC,EAAE,MAAM,MAAM,CAAC;IAC1C,kBAAkB,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1C,CAAC"}
1
+ {"version":3,"file":"ServiceContext.d.ts","sourceRoot":"","sources":["../../src/services/ServiceContext.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAClE,OAAO,KAAK,EACV,KAAK,EACL,kBAAkB,EAClB,0BAA0B,EAC1B,gCAAgC,EAChC,QAAQ,EACT,MAAM,mBAAmB,CAAC;AAE3B;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B;;;OAGG;IACH,cAAc,EAAE;QACd,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,OAAO,CAAC;KACpB,CAAC;IAEF;;;OAGG;IACH,YAAY,EAAE;QACZ,UAAU,EAAE,MAAM,CAAC;QACnB,MAAM,EAAE,MAAM,CAAC;QACf,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACjC,CAAC;IAEF;;;;OAIG;IACH,YAAY,CAAC,EAAE;QACb,MAAM,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,oBAAoB,KAAK,IAAI,KAAK,IAAI,CAAC;QACjE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;KACtC,CAAC;IAEF;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;IACvC,YAAY,CAAC,EAAE,MAAM,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;IAEzC;;;OAGG;IACH,cAAc,CAAC,EAAE;QACf,OAAO,EAAE,0BAA0B,CAAC;QACpC,SAAS,EAAE,MAAM,OAAO,CAAC;QACzB,eAAe,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC;KAC9C,CAAC;IAEF;;;;;OAKG;IACH,qBAAqB,CAAC,EAAE,gCAAgC,CAAC;IAEzD;;;;;;OAMG;IACH,aAAa,CAAC,EAAE,kBAAkB,CAAC;IAEnC;;OAEG;IACH,sBAAsB,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAEpE;;;OAGG;IACH,oBAAoB,CAAC,EAAE,MAAM;QAC3B,IAAI,EAAE,MAAM,EAAE,CAAC;QACf,MAAM,EAAE,QAAQ,GAAG,UAAU,CAAC;KAC/B,CAAC;IACF,oBAAoB,CAAC,EAAE,CACrB,IAAI,EAAE,MAAM,EAAE,EACd,MAAM,EAAE,QAAQ,GAAG,UAAU,KAC1B,IAAI,CAAC;IACV,aAAa,CAAC,EAAE,MAAM;QACpB,OAAO,EAAE,OAAO,CAAC;QACjB,gBAAgB,EAAE,MAAM,EAAE,CAAC;QAC3B,gBAAgB,EAAE,MAAM,EAAE,CAAC;QAC3B,MAAM,EAAE,QAAQ,GAAG,UAAU,CAAC;KAC/B,CAAC;IACF,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE;QACvB,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;QAC5B,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;QAC5B,MAAM,EAAE,QAAQ,GAAG,UAAU,CAAC;KAC/B,KAAK,IAAI,CAAC;IACX,0BAA0B,CAAC,EAAE,MAAM,MAAM,CAAC;IAC1C,kBAAkB,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1C,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"ServiceContext.js","sourceRoot":"","sources":["../../src/services/ServiceContext.ts"],"names":[],"mappings":"","sourcesContent":["import type { PerpsControllerState } from '../PerpsController.js';\nimport type {\n Order,\n PerpsGlobalSnapshotRequest,\n PerpsSubscriptionFeeWaiverStatus,\n Position,\n} from '../types/index.js';\n\n/**\n * ServiceContext\n *\n * Lightweight per-call context for Perps services.\n * Contains ONLY data that varies per operation:\n * - Tracing context (provider, network)\n * - Error context (controller, method)\n * - State management callbacks\n * - Query/action callbacks specific to the operation\n *\n * Platform dependencies (logging, metrics, tracing) are injected into service\n * instances via constructor, not passed per-call.\n *\n * Controller-level singletons (RewardsController, NetworkController, messenger)\n * are also injected into services that need them, not passed per-call.\n *\n * This enables:\n * - Clean method signatures (no verbose dependency passing)\n * - Fat services with complete orchestration\n * - Thin controller with pure delegation\n * - Easy testing through mock contexts and constructor injection\n */\nexport type ServiceContext = {\n /**\n * Tracing context for performance monitoring\n * Used in trace() calls to tag operations\n */\n tracingContext: {\n provider: string;\n isTestnet: boolean;\n };\n\n /**\n * Error logging context\n * Provides consistent error logging across services\n */\n errorContext: {\n controller: string;\n method: string;\n extra?: Record<string, unknown>;\n };\n\n /**\n * State management functions (optional)\n * Only provided for operations that need to mutate controller state\n * Example: Trading operations that update lastTransaction\n */\n stateManager?: {\n update: (updater: (state: PerpsControllerState) => void) => void;\n getState: () => PerpsControllerState;\n };\n\n /**\n * Query functions for dependent data\n * Required by: Operations that need to fetch related data\n */\n getOpenOrders?: () => Promise<Order[]>;\n getPositions?: () => Promise<Position[]>;\n\n /**\n * Exact per-call identity and guards for adopting a global market snapshot.\n * Omitted when the active provider or DEX configuration is not static.\n */\n globalSnapshot?: {\n request: PerpsGlobalSnapshotRequest;\n isCurrent: () => boolean;\n isMarketAllowed: (symbol: string) => boolean;\n };\n\n /**\n * Cached subscription fee-waiver status for read-only fee previews.\n * Read by the controller from `RewardsIntegrationService` — the same cached\n * benefits snapshot the fee resolver uses — and omitted entirely when no\n * subscription source is wired.\n */\n subscriptionFeeWaiver?: PerpsSubscriptionFeeWaiverStatus;\n\n /**\n * Callback functions for controller-specific operations\n */\n saveTradeConfiguration?: (symbol: string, leverage: number) => void;\n\n /**\n * Feature flag configuration callbacks\n * Required by: FeatureFlagConfigurationService\n */\n getBlockedRegionList?: () => {\n list: string[];\n source: 'remote' | 'fallback';\n };\n setBlockedRegionList?: (\n list: string[],\n source: 'remote' | 'fallback',\n ) => void;\n getHip3Config?: () => {\n enabled: boolean;\n allowlistMarkets: string[];\n blocklistMarkets: string[];\n source: 'remote' | 'fallback';\n };\n setHip3Config?: (config: {\n enabled?: boolean;\n allowlistMarkets?: string[];\n blocklistMarkets?: string[];\n source: 'remote' | 'fallback';\n }) => void;\n incrementHip3ConfigVersion?: () => number;\n refreshEligibility?: () => Promise<void>;\n};\n"]}
1
+ {"version":3,"file":"ServiceContext.js","sourceRoot":"","sources":["../../src/services/ServiceContext.ts"],"names":[],"mappings":"","sourcesContent":["import type { PerpsControllerState } from '../PerpsController.js';\nimport type {\n Order,\n PerpsFeeResolution,\n PerpsGlobalSnapshotRequest,\n PerpsSubscriptionFeeWaiverStatus,\n Position,\n} from '../types/index.js';\n\n/**\n * ServiceContext\n *\n * Lightweight per-call context for Perps services.\n * Contains ONLY data that varies per operation:\n * - Tracing context (provider, network)\n * - Error context (controller, method)\n * - State management callbacks\n * - Query/action callbacks specific to the operation\n *\n * Platform dependencies (logging, metrics, tracing) are injected into service\n * instances via constructor, not passed per-call.\n *\n * Controller-level singletons (RewardsController, NetworkController, messenger)\n * are also injected into services that need them, not passed per-call.\n *\n * This enables:\n * - Clean method signatures (no verbose dependency passing)\n * - Fat services with complete orchestration\n * - Thin controller with pure delegation\n * - Easy testing through mock contexts and constructor injection\n */\nexport type ServiceContext = {\n /**\n * Tracing context for performance monitoring\n * Used in trace() calls to tag operations\n */\n tracingContext: {\n provider: string;\n isTestnet: boolean;\n };\n\n /**\n * Error logging context\n * Provides consistent error logging across services\n */\n errorContext: {\n controller: string;\n method: string;\n extra?: Record<string, unknown>;\n };\n\n /**\n * State management functions (optional)\n * Only provided for operations that need to mutate controller state\n * Example: Trading operations that update lastTransaction\n */\n stateManager?: {\n update: (updater: (state: PerpsControllerState) => void) => void;\n getState: () => PerpsControllerState;\n };\n\n /**\n * Query functions for dependent data\n * Required by: Operations that need to fetch related data\n */\n getOpenOrders?: () => Promise<Order[]>;\n getPositions?: () => Promise<Position[]>;\n\n /**\n * Exact per-call identity and guards for adopting a global market snapshot.\n * Omitted when the active provider or DEX configuration is not static.\n */\n globalSnapshot?: {\n request: PerpsGlobalSnapshotRequest;\n isCurrent: () => boolean;\n isMarketAllowed: (symbol: string) => boolean;\n };\n\n /**\n * Cached subscription fee-waiver status for read-only fee previews.\n * Read by the controller from `RewardsIntegrationService` — the same cached\n * benefits snapshot the fee resolver uses — and omitted entirely when no\n * subscription source is wired.\n */\n subscriptionFeeWaiver?: PerpsSubscriptionFeeWaiverStatus;\n\n /**\n * Unified fee resolution for the quote being previewed.\n *\n * Carries the same blended subscription rate the submit path will charge, so\n * a preview quotes what the order actually pays rather than the undiscounted\n * builder fee. Omitted when no resolution was computed.\n */\n feeResolution?: PerpsFeeResolution;\n\n /**\n * Callback functions for controller-specific operations\n */\n saveTradeConfiguration?: (symbol: string, leverage: number) => void;\n\n /**\n * Feature flag configuration callbacks\n * Required by: FeatureFlagConfigurationService\n */\n getBlockedRegionList?: () => {\n list: string[];\n source: 'remote' | 'fallback';\n };\n setBlockedRegionList?: (\n list: string[],\n source: 'remote' | 'fallback',\n ) => void;\n getHip3Config?: () => {\n enabled: boolean;\n allowlistMarkets: string[];\n blocklistMarkets: string[];\n source: 'remote' | 'fallback';\n };\n setHip3Config?: (config: {\n enabled?: boolean;\n allowlistMarkets?: string[];\n blocklistMarkets?: string[];\n source: 'remote' | 'fallback';\n }) => void;\n incrementHip3ConfigVersion?: () => number;\n refreshEligibility?: () => Promise<void>;\n};\n"]}
@@ -22,7 +22,7 @@ export declare class TerminalMarketService {
22
22
  metadata: Map<string, TerminalAssetMetadata>;
23
23
  }>;
24
24
  /**
25
- * Fetch, authenticate by exact identity, and map a schema-v2 atomic market
25
+ * Fetch, authenticate by exact identity, and map a schema-v3 atomic market
26
26
  * snapshot. Accepted entries remain inside the source freshness window;
27
27
  * rejected responses are never cached.
28
28
  *
@@ -1 +1 @@
1
- {"version":3,"file":"TerminalMarketService.d.ts","sourceRoot":"","sources":["../../src/services/TerminalMarketService.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EACV,UAAU,EACV,0BAA0B,EAC1B,yBAAyB,EAEzB,yBAAyB,EACzB,qBAAqB,EACtB,MAAM,mBAAmB,CAAC;AAkG3B;;;;;;;;GAQG;AACH,qBAAa,qBAAqB;;IAchC,YAAY,IAAI,EAAE,yBAAyB,EAE1C;IAED;;;;;OAKG;IACG,YAAY,IAAI,OAAO,CAAC;QAC5B,OAAO,EAAE,UAAU,EAAE,CAAC;QACtB,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAC;KAC9C,CAAC,CAqDD;IAED;;;;;;;OAOG;IACG,mBAAmB,CACvB,OAAO,EAAE,0BAA0B,GAClC,OAAO,CAAC,yBAAyB,CAAC,CAiDpC;IA6YD;;OAEG;IACH,UAAU,IAAI,IAAI,CAKjB;IA+HD;;;;;OAKG;IACH,QAAQ,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAgB7C;CACF"}
1
+ {"version":3,"file":"TerminalMarketService.d.ts","sourceRoot":"","sources":["../../src/services/TerminalMarketService.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EACV,UAAU,EACV,0BAA0B,EAC1B,yBAAyB,EAEzB,yBAAyB,EACzB,qBAAqB,EACtB,MAAM,mBAAmB,CAAC;AAoG3B;;;;;;;;GAQG;AACH,qBAAa,qBAAqB;;IAchC,YAAY,IAAI,EAAE,yBAAyB,EAE1C;IAED;;;;;OAKG;IACG,YAAY,IAAI,OAAO,CAAC;QAC5B,OAAO,EAAE,UAAU,EAAE,CAAC;QACtB,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAC;KAC9C,CAAC,CAqDD;IAED;;;;;;;OAOG;IACG,mBAAmB,CACvB,OAAO,EAAE,0BAA0B,GAClC,OAAO,CAAC,yBAAyB,CAAC,CAiDpC;IAoZD;;OAEG;IACH,UAAU,IAAI,IAAI,CAKjB;IA+HD;;;;;OAKG;IACH,QAAQ,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAgB7C;CACF"}
@@ -7,7 +7,7 @@ import { ensureError } from '../utils/errorUtils.js';
7
7
  import { formatChange } from '../utils/marketDataTransform.js';
8
8
  import { clonePerpsMarketData } from '../utils/marketUtils.js';
9
9
  const VALID_MARKET_TYPES = new Set(Object.values(MarketCategory));
10
- const GLOBAL_SNAPSHOT_SCHEMA_VERSION = 2;
10
+ const GLOBAL_SNAPSHOT_SCHEMA_VERSION = 3;
11
11
  const GLOBAL_SNAPSHOT_CONSUMER_MAX_AGE_MS = 30_000;
12
12
  const GLOBAL_SNAPSHOT_MAX_PAYLOAD_BYTES = 1_048_576;
13
13
  const GLOBAL_SNAPSHOT_PERCENT_TOLERANCE = 0.01;
@@ -17,7 +17,9 @@ const DECIMAL_PATTERN = /^-?(?:0|[1-9]\d*)(?:\.\d+)?$/u;
17
17
  const NON_NEGATIVE_DECIMAL_PATTERN = /^(?:0|[1-9]\d*)(?:\.\d+)?$/u;
18
18
  const DEX_PATTERN = /^(?:main|[a-z0-9][a-z0-9-]*)$/u;
19
19
  const GlobalSnapshotMarketStruct = object({
20
+ id: string(),
20
21
  symbol: string(),
22
+ network: string(),
21
23
  provider: string(),
22
24
  dex: string(),
23
25
  name: nullable(string()),
@@ -145,7 +147,7 @@ export class TerminalMarketService {
145
147
  return { markets, metadata };
146
148
  }
147
149
  /**
148
- * Fetch, authenticate by exact identity, and map a schema-v2 atomic market
150
+ * Fetch, authenticate by exact identity, and map a schema-v3 atomic market
149
151
  * snapshot. Accepted entries remain inside the source freshness window;
150
152
  * rejected responses are never cached.
151
153
  *
@@ -345,6 +347,12 @@ export class TerminalMarketService {
345
347
  }
346
348
  #validateSnapshotMarket(market, identity, index, generatedAt) {
347
349
  const invalid = (field) => new Error(`Terminal global snapshot market ${String(index)} has invalid ${field}`);
350
+ if (market.id.length === 0) {
351
+ throw invalid('id');
352
+ }
353
+ if (market.network !== identity.network) {
354
+ throw invalid('network');
355
+ }
348
356
  if (!identity.enabledDexes.includes(market.dex)) {
349
357
  throw invalid('dex');
350
358
  }
@@ -452,6 +460,7 @@ export class TerminalMarketService {
452
460
  const isHip3 = market.dex !== 'main';
453
461
  const marketType = this.#marketTypeFor(market.dex, market.category);
454
462
  return {
463
+ id: market.id,
455
464
  symbol: market.symbol,
456
465
  name: market.name ?? market.symbol,
457
466
  ...(market.description !== null && {
@@ -1 +1 @@
1
- {"version":3,"file":"TerminalMarketService.js","sourceRoot":"","sources":["../../src/services/TerminalMarketService.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Infer } from '@metamask/superstruct';\nimport {\n array,\n boolean,\n is,\n nullable,\n number,\n object,\n optional,\n string,\n tuple,\n type,\n union,\n} from '@metamask/superstruct';\nimport { bytesToHex, sha256, stringToBytes } from '@metamask/utils';\n\nimport { canonicalizeHyperLiquidDexes } from '../constants/hyperLiquidConfig.js';\nimport {\n PERPS_CONSTANTS,\n TERMINAL_API_CONFIG,\n} from '../constants/perpsConfig.js';\nimport type {\n MarketInfo,\n PerpsGlobalSnapshotRequest,\n PerpsGlobalSnapshotResult,\n PerpsMarketData,\n PerpsPlatformDependencies,\n TerminalAssetMetadata,\n} from '../types/index.js';\nimport { MarketCategory } from '../types/index.js';\nimport { ensureError } from '../utils/errorUtils.js';\nimport { formatChange } from '../utils/marketDataTransform.js';\nimport { clonePerpsMarketData } from '../utils/marketUtils.js';\n\nconst VALID_MARKET_TYPES = new Set<string>(Object.values(MarketCategory));\nconst GLOBAL_SNAPSHOT_SCHEMA_VERSION = 2;\nconst GLOBAL_SNAPSHOT_CONSUMER_MAX_AGE_MS = 30_000;\nconst GLOBAL_SNAPSHOT_MAX_PAYLOAD_BYTES = 1_048_576;\nconst GLOBAL_SNAPSHOT_PERCENT_TOLERANCE = 0.01;\nconst GLOBAL_SNAPSHOT_MAX_FUTURE_CLOCK_SKEW_MS = 5_000;\nconst MINIMUM_EPOCH_MILLISECONDS = Date.UTC(2000, 0, 1);\nconst DECIMAL_PATTERN = /^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$/u;\nconst NON_NEGATIVE_DECIMAL_PATTERN = /^(?:0|[1-9]\\d*)(?:\\.\\d+)?$/u;\nconst DEX_PATTERN = /^(?:main|[a-z0-9][a-z0-9-]*)$/u;\n\nconst GlobalSnapshotMarketStruct = object({\n symbol: string(),\n provider: string(),\n dex: string(),\n name: nullable(string()),\n description: nullable(string()),\n iconUrl: nullable(string()),\n szDecimals: number(),\n maxLeverage: number(),\n markPrice: string(),\n price: string(),\n midPrice: nullable(string()),\n oraclePrice: string(),\n change24h: string(),\n changePercent24h: number(),\n funding: string(),\n volume24h: string(),\n openInterest: string(),\n category: nullable(string()),\n keywords: nullable(array(string())),\n tags: nullable(array(string())),\n listedAt: nullable(number()),\n trend: array(tuple([number(), string()])),\n});\n\nconst GlobalSnapshotStruct = object({\n schemaVersion: number(),\n provider: string(),\n network: string(),\n enabledDexes: array(string()),\n fingerprint: string(),\n generatedAt: number(),\n receivedAt: number(),\n maxAgeMs: number(),\n complete: boolean(),\n perDexErrors: array(\n object({\n dex: string(),\n error: string(),\n }),\n ),\n markets: array(GlobalSnapshotMarketStruct),\n});\n\ntype GlobalSnapshotMarket = Infer<typeof GlobalSnapshotMarketStruct>;\ntype GlobalSnapshot = Infer<typeof GlobalSnapshotStruct>;\n\n/**\n * Runtime validation schema for a single market item returned by\n * `GET {terminalApi.marketDataUrl}`.\n *\n * Uses `type()` (loose object matching) so that extra fields the API sends\n * (e.g. `price`, `iconUrl`, `trend`) are silently accepted.\n * Each item is individually validated; items that fail validation are\n * filtered out and logged rather than rejecting the entire response.\n */\nconst TerminalPerpetualItemStruct = type({\n symbol: string(),\n name: optional(nullable(string())),\n description: optional(nullable(string())),\n szDecimals: optional(number()),\n maxLeverage: optional(number()),\n marginTableId: optional(number()),\n onlyIsolated: optional(boolean()),\n isDelisted: optional(boolean()),\n minimumOrderSize: optional(number()),\n keywords: optional(nullable(array(string()))),\n tags: optional(nullable(array(string()))),\n categories: optional(nullable(array(string()))),\n marketType: optional(nullable(string())),\n listedAt: optional(nullable(union([number(), string()]))),\n});\n\ntype TerminalPerpetualItem = Infer<typeof TerminalPerpetualItemStruct>;\n\ntype CacheEntry = {\n markets: MarketInfo[];\n metadata: Map<string, TerminalAssetMetadata>;\n timestamp: number;\n};\n\n/**\n * TerminalMarketService\n *\n * Fetches structured market metadata from the MetaMask Terminal API.\n * Caches responses for {@link TERMINAL_API_CONFIG.CacheTtlMs} to avoid\n * redundant network calls across polling cycles.\n *\n * Instance-based service with constructor injection of platform dependencies.\n */\nexport class TerminalMarketService {\n readonly #deps: PerpsPlatformDependencies;\n\n #cache: CacheEntry | null = null;\n\n readonly #globalSnapshotCache = new Map<string, PerpsGlobalSnapshotResult>();\n\n readonly #globalSnapshotInFlight = new Map<\n string,\n Promise<PerpsGlobalSnapshotResult>\n >();\n\n #globalSnapshotGeneration = 0;\n\n constructor(deps: PerpsPlatformDependencies) {\n this.#deps = deps;\n }\n\n /**\n * Fetch markets from the Terminal API.\n * Returns cached data when available and within TTL.\n *\n * @returns Object with mapped MarketInfo array and per-symbol metadata.\n */\n async fetchMarkets(): Promise<{\n markets: MarketInfo[];\n metadata: Map<string, TerminalAssetMetadata>;\n }> {\n if (\n this.#cache &&\n Date.now() - this.#cache.timestamp < TERMINAL_API_CONFIG.CacheTtlMs\n ) {\n return {\n markets: this.#cache.markets,\n metadata: this.#cache.metadata,\n };\n }\n\n const marketDataUrl =\n this.#deps.terminalApi?.marketDataUrl ?? this.#deps.terminalApiUrl;\n if (!marketDataUrl) {\n throw new Error('Terminal API market-data URL not configured');\n }\n\n const url = marketDataUrl;\n const controller = new AbortController();\n const timeoutId = setTimeout(\n () => controller.abort(new Error('Terminal API fetch timed out')),\n TERMINAL_API_CONFIG.FetchTimeoutMs,\n );\n\n let response: Response;\n try {\n response = await fetch(url, {\n method: 'GET',\n headers: { 'Content-Type': 'application/json' },\n signal: controller.signal,\n });\n } finally {\n clearTimeout(timeoutId);\n }\n\n if (!response.ok) {\n throw new Error(\n `Terminal API returned ${String(response.status)}: ${response.statusText}`,\n );\n }\n\n const body: unknown = await response.json();\n\n if (!Array.isArray(body)) {\n throw new Error(`Terminal API returned non-array body: ${typeof body}`);\n }\n\n const items = this.#validateItems(body);\n const markets = this.#mapToMarketInfo(items);\n const metadata = this.#extractMetadata(items);\n\n this.#cache = { markets, metadata, timestamp: Date.now() };\n return { markets, metadata };\n }\n\n /**\n * Fetch, authenticate by exact identity, and map a schema-v2 atomic market\n * snapshot. Accepted entries remain inside the source freshness window;\n * rejected responses are never cached.\n *\n * @param request - Exact provider/network/DEX identity expected by the client.\n * @returns UI-ready market data and its source-bounded expiry.\n */\n async fetchGlobalSnapshot(\n request: PerpsGlobalSnapshotRequest,\n ): Promise<PerpsGlobalSnapshotResult> {\n const identity = this.#validateRequestedIdentity(request);\n if (!this.#deps.terminalApi?.globalSnapshotUrl) {\n throw new Error('Terminal global snapshot URL not configured');\n }\n\n const url = this.#buildGlobalSnapshotUrl(\n this.#deps.terminalApi.globalSnapshotUrl,\n identity,\n );\n const cacheKey = [\n url,\n String(GLOBAL_SNAPSHOT_SCHEMA_VERSION),\n identity.provider,\n identity.network,\n identity.enabledDexes.join(','),\n ].join('|');\n const now = Date.now();\n const cached = this.#globalSnapshotCache.get(cacheKey);\n if (cached && now < cached.expiresAt) {\n return this.#cloneGlobalSnapshotResult(cached);\n }\n if (cached) {\n this.#globalSnapshotCache.delete(cacheKey);\n }\n\n const existing = this.#globalSnapshotInFlight.get(cacheKey);\n if (existing) {\n return this.#cloneGlobalSnapshotResult(await existing);\n }\n\n const generation = this.#globalSnapshotGeneration;\n const pending = this.#fetchAndValidateGlobalSnapshot(identity, url).then(\n (result) => {\n if (this.#globalSnapshotGeneration !== generation) {\n return result;\n }\n this.#globalSnapshotCache.set(cacheKey, result);\n return result;\n },\n );\n this.#globalSnapshotInFlight.set(cacheKey, pending);\n try {\n return this.#cloneGlobalSnapshotResult(await pending);\n } finally {\n if (this.#globalSnapshotInFlight.get(cacheKey) === pending) {\n this.#globalSnapshotInFlight.delete(cacheKey);\n }\n }\n }\n\n async #fetchAndValidateGlobalSnapshot(\n identity: PerpsGlobalSnapshotRequest,\n url: string,\n ): Promise<PerpsGlobalSnapshotResult> {\n const controller = new AbortController();\n const timeoutId = setTimeout(\n () => controller.abort(new Error('Terminal global snapshot timed out')),\n TERMINAL_API_CONFIG.FetchTimeoutMs,\n );\n\n try {\n const response = await fetch(url, {\n method: 'GET',\n headers: { 'Content-Type': 'application/json' },\n signal: controller.signal,\n });\n\n if (!response.ok) {\n throw new Error(\n `Terminal global snapshot returned ${String(response.status)}: ${response.statusText}`,\n );\n }\n\n const declaredLength = response.headers?.get('content-length');\n if (\n declaredLength !== null &&\n declaredLength !== undefined &&\n /^\\d+$/u.test(declaredLength) &&\n Number(declaredLength) > GLOBAL_SNAPSHOT_MAX_PAYLOAD_BYTES\n ) {\n throw new Error('Terminal global snapshot payload exceeds 1 MiB');\n }\n // React Native fetch does not consistently expose a streaming reader.\n // Reject declared oversize bodies before allocation, then enforce the same\n // byte cap after text() for servers that omit Content-Length.\n const text = await response.text();\n if (stringToBytes(text).byteLength > GLOBAL_SNAPSHOT_MAX_PAYLOAD_BYTES) {\n throw new Error('Terminal global snapshot payload exceeds 1 MiB');\n }\n let body: unknown;\n try {\n body = JSON.parse(text) as unknown;\n } catch {\n throw new Error('Terminal global snapshot returned invalid JSON');\n }\n if (!is(body, GlobalSnapshotStruct)) {\n throw new Error('Terminal global snapshot failed schema validation');\n }\n return this.#validateAndMapGlobalSnapshot(body, identity, Date.now());\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n async #validateAndMapGlobalSnapshot(\n snapshot: GlobalSnapshot,\n identity: PerpsGlobalSnapshotRequest,\n now: number,\n ): Promise<PerpsGlobalSnapshotResult> {\n if (snapshot.schemaVersion !== GLOBAL_SNAPSHOT_SCHEMA_VERSION) {\n throw new Error('Terminal global snapshot schema version mismatch');\n }\n if (\n snapshot.provider !== identity.provider ||\n snapshot.network !== identity.network\n ) {\n throw new Error('Terminal global snapshot identity mismatch');\n }\n\n const responseDexes = this.#normalizeDexes(snapshot.enabledDexes);\n if (\n responseDexes.length !== identity.enabledDexes.length ||\n responseDexes.some((dex, index) => dex !== identity.enabledDexes[index])\n ) {\n throw new Error('Terminal global snapshot DEX mismatch');\n }\n const expectedFingerprint = await this.#createFingerprint(identity);\n if (snapshot.fingerprint !== expectedFingerprint) {\n throw new Error('Terminal global snapshot fingerprint mismatch');\n }\n if (!snapshot.complete || snapshot.perDexErrors.length > 0) {\n throw new Error('Terminal global snapshot is incomplete');\n }\n if (\n !this.#isNonNegativeSafeInteger(snapshot.generatedAt) ||\n !this.#isNonNegativeSafeInteger(snapshot.receivedAt) ||\n !this.#isPositiveSafeInteger(snapshot.maxAgeMs) ||\n snapshot.receivedAt > snapshot.generatedAt ||\n snapshot.generatedAt > now + GLOBAL_SNAPSHOT_MAX_FUTURE_CLOCK_SKEW_MS ||\n snapshot.receivedAt > now + GLOBAL_SNAPSHOT_MAX_FUTURE_CLOCK_SKEW_MS\n ) {\n throw new Error('Terminal global snapshot has invalid timestamps');\n }\n\n const trustedMaxAgeMs = Math.min(\n snapshot.maxAgeMs,\n GLOBAL_SNAPSHOT_CONSUMER_MAX_AGE_MS,\n );\n const expiresAt = snapshot.receivedAt + trustedMaxAgeMs;\n if (now >= expiresAt) {\n throw new Error('Terminal global snapshot is stale');\n }\n if (snapshot.markets.length === 0) {\n throw new Error('Terminal global snapshot has no markets');\n }\n\n const marketKeys = new Set<string>();\n const representedDexes = new Set<string>();\n const markets = snapshot.markets\n .map((market, index) => {\n this.#validateSnapshotMarket(\n market,\n identity,\n index,\n snapshot.generatedAt,\n );\n const key = `${market.dex}:${market.symbol}`;\n if (marketKeys.has(key)) {\n throw new Error(`Terminal global snapshot duplicates market ${key}`);\n }\n marketKeys.add(key);\n representedDexes.add(market.dex);\n return market;\n })\n .map((market) => this.#mapSnapshotMarket(market, expiresAt));\n if (identity.enabledDexes.some((dex) => !representedDexes.has(dex))) {\n throw new Error('Terminal global snapshot is missing a requested DEX');\n }\n if (markets.length === 0) {\n throw new Error('Terminal global snapshot has no tradable markets');\n }\n\n return { markets, expiresAt };\n }\n\n #validateRequestedIdentity(\n request: PerpsGlobalSnapshotRequest,\n ): PerpsGlobalSnapshotRequest {\n if (request.provider !== 'hyperliquid') {\n throw new Error('Terminal global snapshot provider is unsupported');\n }\n if (request.network !== 'mainnet' && request.network !== 'testnet') {\n throw new Error('Terminal global snapshot network is unsupported');\n }\n return {\n provider: request.provider,\n network: request.network,\n enabledDexes: this.#normalizeDexes(request.enabledDexes),\n };\n }\n\n #buildGlobalSnapshotUrl(\n baseUrl: string,\n identity: PerpsGlobalSnapshotRequest,\n ): string {\n const query = new URLSearchParams({\n provider: identity.provider,\n network: identity.network,\n dexes: identity.enabledDexes.join(','),\n });\n return `${baseUrl}${baseUrl.includes('?') ? '&' : '?'}${query.toString()}`;\n }\n\n #normalizeDexes(dexes: string[]): string[] {\n if (!Array.isArray(dexes) || dexes.length === 0) {\n throw new Error('Terminal global snapshot requires at least one DEX');\n }\n const normalized = dexes.map((dex) => {\n if (typeof dex !== 'string' || !DEX_PATTERN.test(dex)) {\n throw new Error('Terminal global snapshot contains an invalid DEX');\n }\n return dex;\n });\n if (new Set(normalized).size !== normalized.length) {\n throw new Error('Terminal global snapshot contains duplicate DEXes');\n }\n if (!normalized.includes('main')) {\n throw new Error('Terminal global snapshot requires the main DEX');\n }\n return canonicalizeHyperLiquidDexes(normalized);\n }\n\n async #createFingerprint(\n identity: PerpsGlobalSnapshotRequest,\n ): Promise<string> {\n const canonicalIdentity = JSON.stringify({\n provider: identity.provider,\n network: identity.network,\n enabledDexes: identity.enabledDexes,\n });\n const digest = await sha256(stringToBytes(canonicalIdentity));\n return `sha256:${bytesToHex(digest).slice(2)}`;\n }\n\n #validateSnapshotMarket(\n market: GlobalSnapshotMarket,\n identity: PerpsGlobalSnapshotRequest,\n index: number,\n generatedAt: number,\n ): void {\n const invalid = (field: string): Error =>\n new Error(\n `Terminal global snapshot market ${String(index)} has invalid ${field}`,\n );\n if (!identity.enabledDexes.includes(market.dex)) {\n throw invalid('dex');\n }\n const expectedProvider = market.dex === 'main' ? 'hyperliquid' : market.dex;\n if (market.provider !== expectedProvider) {\n throw invalid('provider');\n }\n const expectedPrefix = market.dex === 'main' ? '' : `${market.dex}:`;\n if (\n market.symbol.length === 0 ||\n (expectedPrefix\n ? !market.symbol.startsWith(expectedPrefix)\n : market.symbol.includes(':'))\n ) {\n throw invalid('symbol');\n }\n if (\n !this.#isNonNegativeSafeInteger(market.szDecimals) ||\n !this.#isPositiveSafeInteger(market.maxLeverage) ||\n (market.listedAt !== null &&\n (!this.#isNonNegativeSafeInteger(market.listedAt) ||\n market.listedAt < MINIMUM_EPOCH_MILLISECONDS ||\n market.listedAt > generatedAt))\n ) {\n throw invalid('integer field');\n }\n\n const decimalFields: [string, string, boolean][] = [\n ['markPrice', market.markPrice, true],\n ['price', market.price, true],\n ['oraclePrice', market.oraclePrice, true],\n ['change24h', market.change24h, false],\n ['volume24h', market.volume24h, true],\n ['openInterest', market.openInterest, true],\n ['funding', market.funding, false],\n ];\n if (market.midPrice !== null) {\n decimalFields.push(['midPrice', market.midPrice, true]);\n }\n for (const [field, value, nonNegative] of decimalFields) {\n const pattern = nonNegative\n ? NON_NEGATIVE_DECIMAL_PATTERN\n : DECIMAL_PATTERN;\n if (!pattern.test(value) || !Number.isFinite(Number(value))) {\n throw invalid(field);\n }\n }\n if (market.price !== market.markPrice) {\n throw invalid('deprecated price alias');\n }\n if (\n Number(market.oraclePrice) <= 0 ||\n (market.midPrice !== null && Number(market.midPrice) <= 0)\n ) {\n throw invalid('reference price');\n }\n const price = Number(market.markPrice);\n const change24h = Number(market.change24h);\n const previousPrice = price - change24h;\n if (price <= 0 || previousPrice <= 0 || !Number.isFinite(previousPrice)) {\n throw invalid('mark/change coherence');\n }\n const derivedPercent = (change24h / previousPrice) * 100;\n if (\n !Number.isFinite(derivedPercent) ||\n !Number.isFinite(market.changePercent24h) ||\n Math.abs(market.changePercent24h - derivedPercent) >\n GLOBAL_SNAPSHOT_PERCENT_TOLERANCE\n ) {\n throw invalid('changePercent24h coherence');\n }\n for (const [field, values] of [\n ['keywords', market.keywords],\n ['tags', market.tags],\n ] as const) {\n if (\n values !== null &&\n (values.some((value) => value.length === 0) ||\n new Set(values).size !== values.length)\n ) {\n throw invalid(field);\n }\n }\n for (const [field, value] of [\n ['name', market.name],\n ['description', market.description],\n ['iconUrl', market.iconUrl],\n ['category', market.category],\n ] as const) {\n if (value !== null && value.length === 0) {\n throw invalid(field);\n }\n }\n let previousTrendTimestamp = -1;\n for (const [timestamp, trendPrice] of market.trend) {\n if (\n !this.#isNonNegativeSafeInteger(timestamp) ||\n timestamp < MINIMUM_EPOCH_MILLISECONDS ||\n timestamp > generatedAt ||\n timestamp <= previousTrendTimestamp ||\n !NON_NEGATIVE_DECIMAL_PATTERN.test(trendPrice) ||\n !Number.isFinite(Number(trendPrice)) ||\n Number(trendPrice) <= 0\n ) {\n throw invalid('trend');\n }\n previousTrendTimestamp = timestamp;\n }\n }\n\n #mapSnapshotMarket(\n market: GlobalSnapshotMarket,\n sourceExpiresAt: number,\n ): PerpsMarketData {\n const formatters = this.#deps.marketDataFormatters;\n // Keep both provider price semantics explicit in the wire contract. Core\n // maps markPrice to its UI price while retaining validation of midPrice.\n const price = Number(market.markPrice);\n const change24h = Number(market.change24h);\n const volume = Number(market.volume24h);\n const openInterest = Number(market.openInterest);\n const isHip3 = market.dex !== 'main';\n const marketType = this.#marketTypeFor(market.dex, market.category);\n\n return {\n symbol: market.symbol,\n name: market.name ?? market.symbol,\n ...(market.description !== null && {\n description: market.description,\n }),\n maxLeverage: `${String(market.maxLeverage)}x`,\n price: formatters.formatPerpsFiat(price, {\n ranges: formatters.priceRangesUniversal,\n }),\n change24h: formatChange(change24h, formatters),\n change24hPercent: formatters.formatPercentage(market.changePercent24h),\n volume: formatters.formatVolume(volume),\n openInterest: formatters.formatVolume(openInterest),\n fundingRate: Number(market.funding),\n marketSource: isHip3 ? market.dex : undefined,\n marketType,\n isHip3,\n isNewMarket: isHip3 && marketType === undefined,\n ...(market.keywords && { keywords: market.keywords }),\n ...(market.tags && { tags: market.tags }),\n ...(market.category && { categories: [market.category] }),\n ...(market.listedAt !== null && { listedAt: market.listedAt }),\n trend: market.trend,\n dataSource: 'terminal-global-snapshot-mark',\n sourceExpiresAt,\n };\n }\n\n #marketTypeFor(\n dex: string,\n category: string | null,\n ): TerminalAssetMetadata['marketType'] | undefined {\n if (dex === 'main') {\n return MarketCategory.CryptoCurrency;\n }\n if (category === 'stocks') {\n return MarketCategory.Stock;\n }\n if (category === 'pre_ipo') {\n return MarketCategory.PreIpo;\n }\n if (category && VALID_MARKET_TYPES.has(category)) {\n return category as TerminalAssetMetadata['marketType'];\n }\n return undefined;\n }\n\n #cloneGlobalSnapshotResult(\n result: PerpsGlobalSnapshotResult,\n ): PerpsGlobalSnapshotResult {\n return {\n expiresAt: result.expiresAt,\n markets: clonePerpsMarketData(result.markets),\n };\n }\n\n #isNonNegativeSafeInteger(value: unknown): value is number {\n return (\n typeof value === 'number' && Number.isSafeInteger(value) && value >= 0\n );\n }\n\n #isPositiveSafeInteger(value: unknown): value is number {\n return this.#isNonNegativeSafeInteger(value) && value > 0;\n }\n\n /**\n * Invalidate the internal cache so the next fetch hits the network.\n */\n clearCache(): void {\n this.#cache = null;\n this.#globalSnapshotGeneration += 1;\n this.#globalSnapshotCache.clear();\n this.#globalSnapshotInFlight.clear();\n }\n\n /**\n * Validate raw API response items against the expected schema.\n * Items that fail validation are filtered out and logged rather than\n * rejecting the entire response.\n *\n * @param raw - The raw array from the API response body.\n * @returns Array of validated items.\n */\n #validateItems(raw: unknown[]): TerminalPerpetualItem[] {\n const valid: TerminalPerpetualItem[] = [];\n for (const item of raw) {\n if (is(item, TerminalPerpetualItemStruct)) {\n valid.push(item);\n } else {\n this.#deps.logger.error(\n ensureError(\n new Error('Terminal API item failed schema validation'),\n 'TerminalMarketService.validateItems',\n ),\n {\n tags: {\n feature: PERPS_CONSTANTS.FeatureName,\n source: 'terminal-api',\n },\n context: {\n name: 'TerminalMarketService.validateItems',\n data: {\n symbol:\n typeof item === 'object' &&\n item !== null &&\n Object.prototype.hasOwnProperty.call(item, 'symbol')\n ? (item as Record<string, unknown>).symbol\n : undefined,\n },\n },\n },\n );\n }\n }\n return valid;\n }\n\n /**\n * Map Terminal API items to the protocol-agnostic MarketInfo shape.\n *\n * @param items - Raw items from the API response.\n * @returns Array of MarketInfo objects.\n */\n #mapToMarketInfo(items: TerminalPerpetualItem[]): MarketInfo[] {\n return items\n .filter(\n (item) => typeof item.symbol === 'string' && item.symbol.length > 0,\n )\n .map((item) => ({\n name: item.symbol,\n szDecimals: item.szDecimals ?? 0,\n maxLeverage: item.maxLeverage ?? 1,\n marginTableId: item.marginTableId ?? 0,\n ...(item.onlyIsolated === true && { onlyIsolated: true as const }),\n ...(item.isDelisted === true && { isDelisted: true as const }),\n ...(item.minimumOrderSize !== undefined && {\n minimumOrderSize: item.minimumOrderSize,\n }),\n }));\n }\n\n /**\n * Extract per-symbol metadata for downstream merge into PerpsMarketData.\n *\n * @param items - Raw items from the API response.\n * @returns Map keyed by symbol with enrichment metadata.\n */\n #extractMetadata(\n items: TerminalPerpetualItem[],\n ): Map<string, TerminalAssetMetadata> {\n const map = new Map<string, TerminalAssetMetadata>();\n\n for (const item of items) {\n if (typeof item.symbol !== 'string' || item.symbol.length === 0) {\n continue;\n }\n\n const entry: TerminalAssetMetadata = {};\n\n if (typeof item.name === 'string' && item.name.length > 0) {\n entry.name = item.name;\n }\n\n if (typeof item.description === 'string' && item.description.length > 0) {\n entry.description = item.description;\n }\n\n if (Array.isArray(item.keywords) && item.keywords.length > 0) {\n entry.keywords = item.keywords;\n }\n if (Array.isArray(item.tags) && item.tags.length > 0) {\n entry.tags = item.tags;\n }\n if (Array.isArray(item.categories) && item.categories.length > 0) {\n entry.categories = item.categories;\n }\n if (\n typeof item.marketType === 'string' &&\n VALID_MARKET_TYPES.has(item.marketType)\n ) {\n entry.marketType =\n item.marketType as TerminalAssetMetadata['marketType'];\n }\n\n if (item.listedAt !== null && item.listedAt !== undefined) {\n const listedAtMs =\n typeof item.listedAt === 'number'\n ? item.listedAt\n : Date.parse(item.listedAt);\n if (isFinite(listedAtMs)) {\n entry.listedAt = listedAtMs;\n }\n }\n\n map.set(item.symbol, entry);\n }\n\n return map;\n }\n\n /**\n * Log a Terminal API error to Sentry without surfacing it to the user.\n *\n * @param error - The caught error.\n * @param method - The calling method name for context.\n */\n logError(error: unknown, method: string): void {\n this.#deps.logger.error(\n ensureError(error, `TerminalMarketService.${method}`),\n {\n tags: { feature: PERPS_CONSTANTS.FeatureName, source: 'terminal-api' },\n context: {\n name: `TerminalMarketService.${method}`,\n data: {\n url: method.includes('globalSnapshot')\n ? this.#deps.terminalApi?.globalSnapshotUrl\n : (this.#deps.terminalApi?.marketDataUrl ??\n this.#deps.terminalApiUrl),\n },\n },\n },\n );\n }\n}\n"]}
1
+ {"version":3,"file":"TerminalMarketService.js","sourceRoot":"","sources":["../../src/services/TerminalMarketService.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Infer } from '@metamask/superstruct';\nimport {\n array,\n boolean,\n is,\n nullable,\n number,\n object,\n optional,\n string,\n tuple,\n type,\n union,\n} from '@metamask/superstruct';\nimport { bytesToHex, sha256, stringToBytes } from '@metamask/utils';\n\nimport { canonicalizeHyperLiquidDexes } from '../constants/hyperLiquidConfig.js';\nimport {\n PERPS_CONSTANTS,\n TERMINAL_API_CONFIG,\n} from '../constants/perpsConfig.js';\nimport type {\n MarketInfo,\n PerpsGlobalSnapshotRequest,\n PerpsGlobalSnapshotResult,\n PerpsMarketData,\n PerpsPlatformDependencies,\n TerminalAssetMetadata,\n} from '../types/index.js';\nimport { MarketCategory } from '../types/index.js';\nimport { ensureError } from '../utils/errorUtils.js';\nimport { formatChange } from '../utils/marketDataTransform.js';\nimport { clonePerpsMarketData } from '../utils/marketUtils.js';\n\nconst VALID_MARKET_TYPES = new Set<string>(Object.values(MarketCategory));\nconst GLOBAL_SNAPSHOT_SCHEMA_VERSION = 3;\nconst GLOBAL_SNAPSHOT_CONSUMER_MAX_AGE_MS = 30_000;\nconst GLOBAL_SNAPSHOT_MAX_PAYLOAD_BYTES = 1_048_576;\nconst GLOBAL_SNAPSHOT_PERCENT_TOLERANCE = 0.01;\nconst GLOBAL_SNAPSHOT_MAX_FUTURE_CLOCK_SKEW_MS = 5_000;\nconst MINIMUM_EPOCH_MILLISECONDS = Date.UTC(2000, 0, 1);\nconst DECIMAL_PATTERN = /^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$/u;\nconst NON_NEGATIVE_DECIMAL_PATTERN = /^(?:0|[1-9]\\d*)(?:\\.\\d+)?$/u;\nconst DEX_PATTERN = /^(?:main|[a-z0-9][a-z0-9-]*)$/u;\n\nconst GlobalSnapshotMarketStruct = object({\n id: string(),\n symbol: string(),\n network: string(),\n provider: string(),\n dex: string(),\n name: nullable(string()),\n description: nullable(string()),\n iconUrl: nullable(string()),\n szDecimals: number(),\n maxLeverage: number(),\n markPrice: string(),\n price: string(),\n midPrice: nullable(string()),\n oraclePrice: string(),\n change24h: string(),\n changePercent24h: number(),\n funding: string(),\n volume24h: string(),\n openInterest: string(),\n category: nullable(string()),\n keywords: nullable(array(string())),\n tags: nullable(array(string())),\n listedAt: nullable(number()),\n trend: array(tuple([number(), string()])),\n});\n\nconst GlobalSnapshotStruct = object({\n schemaVersion: number(),\n provider: string(),\n network: string(),\n enabledDexes: array(string()),\n fingerprint: string(),\n generatedAt: number(),\n receivedAt: number(),\n maxAgeMs: number(),\n complete: boolean(),\n perDexErrors: array(\n object({\n dex: string(),\n error: string(),\n }),\n ),\n markets: array(GlobalSnapshotMarketStruct),\n});\n\ntype GlobalSnapshotMarket = Infer<typeof GlobalSnapshotMarketStruct>;\ntype GlobalSnapshot = Infer<typeof GlobalSnapshotStruct>;\n\n/**\n * Runtime validation schema for a single market item returned by\n * `GET {terminalApi.marketDataUrl}`.\n *\n * Uses `type()` (loose object matching) so that extra fields the API sends\n * (e.g. `price`, `iconUrl`, `trend`) are silently accepted.\n * Each item is individually validated; items that fail validation are\n * filtered out and logged rather than rejecting the entire response.\n */\nconst TerminalPerpetualItemStruct = type({\n symbol: string(),\n name: optional(nullable(string())),\n description: optional(nullable(string())),\n szDecimals: optional(number()),\n maxLeverage: optional(number()),\n marginTableId: optional(number()),\n onlyIsolated: optional(boolean()),\n isDelisted: optional(boolean()),\n minimumOrderSize: optional(number()),\n keywords: optional(nullable(array(string()))),\n tags: optional(nullable(array(string()))),\n categories: optional(nullable(array(string()))),\n marketType: optional(nullable(string())),\n listedAt: optional(nullable(union([number(), string()]))),\n});\n\ntype TerminalPerpetualItem = Infer<typeof TerminalPerpetualItemStruct>;\n\ntype CacheEntry = {\n markets: MarketInfo[];\n metadata: Map<string, TerminalAssetMetadata>;\n timestamp: number;\n};\n\n/**\n * TerminalMarketService\n *\n * Fetches structured market metadata from the MetaMask Terminal API.\n * Caches responses for {@link TERMINAL_API_CONFIG.CacheTtlMs} to avoid\n * redundant network calls across polling cycles.\n *\n * Instance-based service with constructor injection of platform dependencies.\n */\nexport class TerminalMarketService {\n readonly #deps: PerpsPlatformDependencies;\n\n #cache: CacheEntry | null = null;\n\n readonly #globalSnapshotCache = new Map<string, PerpsGlobalSnapshotResult>();\n\n readonly #globalSnapshotInFlight = new Map<\n string,\n Promise<PerpsGlobalSnapshotResult>\n >();\n\n #globalSnapshotGeneration = 0;\n\n constructor(deps: PerpsPlatformDependencies) {\n this.#deps = deps;\n }\n\n /**\n * Fetch markets from the Terminal API.\n * Returns cached data when available and within TTL.\n *\n * @returns Object with mapped MarketInfo array and per-symbol metadata.\n */\n async fetchMarkets(): Promise<{\n markets: MarketInfo[];\n metadata: Map<string, TerminalAssetMetadata>;\n }> {\n if (\n this.#cache &&\n Date.now() - this.#cache.timestamp < TERMINAL_API_CONFIG.CacheTtlMs\n ) {\n return {\n markets: this.#cache.markets,\n metadata: this.#cache.metadata,\n };\n }\n\n const marketDataUrl =\n this.#deps.terminalApi?.marketDataUrl ?? this.#deps.terminalApiUrl;\n if (!marketDataUrl) {\n throw new Error('Terminal API market-data URL not configured');\n }\n\n const url = marketDataUrl;\n const controller = new AbortController();\n const timeoutId = setTimeout(\n () => controller.abort(new Error('Terminal API fetch timed out')),\n TERMINAL_API_CONFIG.FetchTimeoutMs,\n );\n\n let response: Response;\n try {\n response = await fetch(url, {\n method: 'GET',\n headers: { 'Content-Type': 'application/json' },\n signal: controller.signal,\n });\n } finally {\n clearTimeout(timeoutId);\n }\n\n if (!response.ok) {\n throw new Error(\n `Terminal API returned ${String(response.status)}: ${response.statusText}`,\n );\n }\n\n const body: unknown = await response.json();\n\n if (!Array.isArray(body)) {\n throw new Error(`Terminal API returned non-array body: ${typeof body}`);\n }\n\n const items = this.#validateItems(body);\n const markets = this.#mapToMarketInfo(items);\n const metadata = this.#extractMetadata(items);\n\n this.#cache = { markets, metadata, timestamp: Date.now() };\n return { markets, metadata };\n }\n\n /**\n * Fetch, authenticate by exact identity, and map a schema-v3 atomic market\n * snapshot. Accepted entries remain inside the source freshness window;\n * rejected responses are never cached.\n *\n * @param request - Exact provider/network/DEX identity expected by the client.\n * @returns UI-ready market data and its source-bounded expiry.\n */\n async fetchGlobalSnapshot(\n request: PerpsGlobalSnapshotRequest,\n ): Promise<PerpsGlobalSnapshotResult> {\n const identity = this.#validateRequestedIdentity(request);\n if (!this.#deps.terminalApi?.globalSnapshotUrl) {\n throw new Error('Terminal global snapshot URL not configured');\n }\n\n const url = this.#buildGlobalSnapshotUrl(\n this.#deps.terminalApi.globalSnapshotUrl,\n identity,\n );\n const cacheKey = [\n url,\n String(GLOBAL_SNAPSHOT_SCHEMA_VERSION),\n identity.provider,\n identity.network,\n identity.enabledDexes.join(','),\n ].join('|');\n const now = Date.now();\n const cached = this.#globalSnapshotCache.get(cacheKey);\n if (cached && now < cached.expiresAt) {\n return this.#cloneGlobalSnapshotResult(cached);\n }\n if (cached) {\n this.#globalSnapshotCache.delete(cacheKey);\n }\n\n const existing = this.#globalSnapshotInFlight.get(cacheKey);\n if (existing) {\n return this.#cloneGlobalSnapshotResult(await existing);\n }\n\n const generation = this.#globalSnapshotGeneration;\n const pending = this.#fetchAndValidateGlobalSnapshot(identity, url).then(\n (result) => {\n if (this.#globalSnapshotGeneration !== generation) {\n return result;\n }\n this.#globalSnapshotCache.set(cacheKey, result);\n return result;\n },\n );\n this.#globalSnapshotInFlight.set(cacheKey, pending);\n try {\n return this.#cloneGlobalSnapshotResult(await pending);\n } finally {\n if (this.#globalSnapshotInFlight.get(cacheKey) === pending) {\n this.#globalSnapshotInFlight.delete(cacheKey);\n }\n }\n }\n\n async #fetchAndValidateGlobalSnapshot(\n identity: PerpsGlobalSnapshotRequest,\n url: string,\n ): Promise<PerpsGlobalSnapshotResult> {\n const controller = new AbortController();\n const timeoutId = setTimeout(\n () => controller.abort(new Error('Terminal global snapshot timed out')),\n TERMINAL_API_CONFIG.FetchTimeoutMs,\n );\n\n try {\n const response = await fetch(url, {\n method: 'GET',\n headers: { 'Content-Type': 'application/json' },\n signal: controller.signal,\n });\n\n if (!response.ok) {\n throw new Error(\n `Terminal global snapshot returned ${String(response.status)}: ${response.statusText}`,\n );\n }\n\n const declaredLength = response.headers?.get('content-length');\n if (\n declaredLength !== null &&\n declaredLength !== undefined &&\n /^\\d+$/u.test(declaredLength) &&\n Number(declaredLength) > GLOBAL_SNAPSHOT_MAX_PAYLOAD_BYTES\n ) {\n throw new Error('Terminal global snapshot payload exceeds 1 MiB');\n }\n // React Native fetch does not consistently expose a streaming reader.\n // Reject declared oversize bodies before allocation, then enforce the same\n // byte cap after text() for servers that omit Content-Length.\n const text = await response.text();\n if (stringToBytes(text).byteLength > GLOBAL_SNAPSHOT_MAX_PAYLOAD_BYTES) {\n throw new Error('Terminal global snapshot payload exceeds 1 MiB');\n }\n let body: unknown;\n try {\n body = JSON.parse(text) as unknown;\n } catch {\n throw new Error('Terminal global snapshot returned invalid JSON');\n }\n if (!is(body, GlobalSnapshotStruct)) {\n throw new Error('Terminal global snapshot failed schema validation');\n }\n return this.#validateAndMapGlobalSnapshot(body, identity, Date.now());\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n async #validateAndMapGlobalSnapshot(\n snapshot: GlobalSnapshot,\n identity: PerpsGlobalSnapshotRequest,\n now: number,\n ): Promise<PerpsGlobalSnapshotResult> {\n if (snapshot.schemaVersion !== GLOBAL_SNAPSHOT_SCHEMA_VERSION) {\n throw new Error('Terminal global snapshot schema version mismatch');\n }\n if (\n snapshot.provider !== identity.provider ||\n snapshot.network !== identity.network\n ) {\n throw new Error('Terminal global snapshot identity mismatch');\n }\n\n const responseDexes = this.#normalizeDexes(snapshot.enabledDexes);\n if (\n responseDexes.length !== identity.enabledDexes.length ||\n responseDexes.some((dex, index) => dex !== identity.enabledDexes[index])\n ) {\n throw new Error('Terminal global snapshot DEX mismatch');\n }\n const expectedFingerprint = await this.#createFingerprint(identity);\n if (snapshot.fingerprint !== expectedFingerprint) {\n throw new Error('Terminal global snapshot fingerprint mismatch');\n }\n if (!snapshot.complete || snapshot.perDexErrors.length > 0) {\n throw new Error('Terminal global snapshot is incomplete');\n }\n if (\n !this.#isNonNegativeSafeInteger(snapshot.generatedAt) ||\n !this.#isNonNegativeSafeInteger(snapshot.receivedAt) ||\n !this.#isPositiveSafeInteger(snapshot.maxAgeMs) ||\n snapshot.receivedAt > snapshot.generatedAt ||\n snapshot.generatedAt > now + GLOBAL_SNAPSHOT_MAX_FUTURE_CLOCK_SKEW_MS ||\n snapshot.receivedAt > now + GLOBAL_SNAPSHOT_MAX_FUTURE_CLOCK_SKEW_MS\n ) {\n throw new Error('Terminal global snapshot has invalid timestamps');\n }\n\n const trustedMaxAgeMs = Math.min(\n snapshot.maxAgeMs,\n GLOBAL_SNAPSHOT_CONSUMER_MAX_AGE_MS,\n );\n const expiresAt = snapshot.receivedAt + trustedMaxAgeMs;\n if (now >= expiresAt) {\n throw new Error('Terminal global snapshot is stale');\n }\n if (snapshot.markets.length === 0) {\n throw new Error('Terminal global snapshot has no markets');\n }\n\n const marketKeys = new Set<string>();\n const representedDexes = new Set<string>();\n const markets = snapshot.markets\n .map((market, index) => {\n this.#validateSnapshotMarket(\n market,\n identity,\n index,\n snapshot.generatedAt,\n );\n const key = `${market.dex}:${market.symbol}`;\n if (marketKeys.has(key)) {\n throw new Error(`Terminal global snapshot duplicates market ${key}`);\n }\n marketKeys.add(key);\n representedDexes.add(market.dex);\n return market;\n })\n .map((market) => this.#mapSnapshotMarket(market, expiresAt));\n if (identity.enabledDexes.some((dex) => !representedDexes.has(dex))) {\n throw new Error('Terminal global snapshot is missing a requested DEX');\n }\n if (markets.length === 0) {\n throw new Error('Terminal global snapshot has no tradable markets');\n }\n\n return { markets, expiresAt };\n }\n\n #validateRequestedIdentity(\n request: PerpsGlobalSnapshotRequest,\n ): PerpsGlobalSnapshotRequest {\n if (request.provider !== 'hyperliquid') {\n throw new Error('Terminal global snapshot provider is unsupported');\n }\n if (request.network !== 'mainnet' && request.network !== 'testnet') {\n throw new Error('Terminal global snapshot network is unsupported');\n }\n return {\n provider: request.provider,\n network: request.network,\n enabledDexes: this.#normalizeDexes(request.enabledDexes),\n };\n }\n\n #buildGlobalSnapshotUrl(\n baseUrl: string,\n identity: PerpsGlobalSnapshotRequest,\n ): string {\n const query = new URLSearchParams({\n provider: identity.provider,\n network: identity.network,\n dexes: identity.enabledDexes.join(','),\n });\n return `${baseUrl}${baseUrl.includes('?') ? '&' : '?'}${query.toString()}`;\n }\n\n #normalizeDexes(dexes: string[]): string[] {\n if (!Array.isArray(dexes) || dexes.length === 0) {\n throw new Error('Terminal global snapshot requires at least one DEX');\n }\n const normalized = dexes.map((dex) => {\n if (typeof dex !== 'string' || !DEX_PATTERN.test(dex)) {\n throw new Error('Terminal global snapshot contains an invalid DEX');\n }\n return dex;\n });\n if (new Set(normalized).size !== normalized.length) {\n throw new Error('Terminal global snapshot contains duplicate DEXes');\n }\n if (!normalized.includes('main')) {\n throw new Error('Terminal global snapshot requires the main DEX');\n }\n return canonicalizeHyperLiquidDexes(normalized);\n }\n\n async #createFingerprint(\n identity: PerpsGlobalSnapshotRequest,\n ): Promise<string> {\n const canonicalIdentity = JSON.stringify({\n provider: identity.provider,\n network: identity.network,\n enabledDexes: identity.enabledDexes,\n });\n const digest = await sha256(stringToBytes(canonicalIdentity));\n return `sha256:${bytesToHex(digest).slice(2)}`;\n }\n\n #validateSnapshotMarket(\n market: GlobalSnapshotMarket,\n identity: PerpsGlobalSnapshotRequest,\n index: number,\n generatedAt: number,\n ): void {\n const invalid = (field: string): Error =>\n new Error(\n `Terminal global snapshot market ${String(index)} has invalid ${field}`,\n );\n if (market.id.length === 0) {\n throw invalid('id');\n }\n if (market.network !== identity.network) {\n throw invalid('network');\n }\n if (!identity.enabledDexes.includes(market.dex)) {\n throw invalid('dex');\n }\n const expectedProvider = market.dex === 'main' ? 'hyperliquid' : market.dex;\n if (market.provider !== expectedProvider) {\n throw invalid('provider');\n }\n const expectedPrefix = market.dex === 'main' ? '' : `${market.dex}:`;\n if (\n market.symbol.length === 0 ||\n (expectedPrefix\n ? !market.symbol.startsWith(expectedPrefix)\n : market.symbol.includes(':'))\n ) {\n throw invalid('symbol');\n }\n if (\n !this.#isNonNegativeSafeInteger(market.szDecimals) ||\n !this.#isPositiveSafeInteger(market.maxLeverage) ||\n (market.listedAt !== null &&\n (!this.#isNonNegativeSafeInteger(market.listedAt) ||\n market.listedAt < MINIMUM_EPOCH_MILLISECONDS ||\n market.listedAt > generatedAt))\n ) {\n throw invalid('integer field');\n }\n\n const decimalFields: [string, string, boolean][] = [\n ['markPrice', market.markPrice, true],\n ['price', market.price, true],\n ['oraclePrice', market.oraclePrice, true],\n ['change24h', market.change24h, false],\n ['volume24h', market.volume24h, true],\n ['openInterest', market.openInterest, true],\n ['funding', market.funding, false],\n ];\n if (market.midPrice !== null) {\n decimalFields.push(['midPrice', market.midPrice, true]);\n }\n for (const [field, value, nonNegative] of decimalFields) {\n const pattern = nonNegative\n ? NON_NEGATIVE_DECIMAL_PATTERN\n : DECIMAL_PATTERN;\n if (!pattern.test(value) || !Number.isFinite(Number(value))) {\n throw invalid(field);\n }\n }\n if (market.price !== market.markPrice) {\n throw invalid('deprecated price alias');\n }\n if (\n Number(market.oraclePrice) <= 0 ||\n (market.midPrice !== null && Number(market.midPrice) <= 0)\n ) {\n throw invalid('reference price');\n }\n const price = Number(market.markPrice);\n const change24h = Number(market.change24h);\n const previousPrice = price - change24h;\n if (price <= 0 || previousPrice <= 0 || !Number.isFinite(previousPrice)) {\n throw invalid('mark/change coherence');\n }\n const derivedPercent = (change24h / previousPrice) * 100;\n if (\n !Number.isFinite(derivedPercent) ||\n !Number.isFinite(market.changePercent24h) ||\n Math.abs(market.changePercent24h - derivedPercent) >\n GLOBAL_SNAPSHOT_PERCENT_TOLERANCE\n ) {\n throw invalid('changePercent24h coherence');\n }\n for (const [field, values] of [\n ['keywords', market.keywords],\n ['tags', market.tags],\n ] as const) {\n if (\n values !== null &&\n (values.some((value) => value.length === 0) ||\n new Set(values).size !== values.length)\n ) {\n throw invalid(field);\n }\n }\n for (const [field, value] of [\n ['name', market.name],\n ['description', market.description],\n ['iconUrl', market.iconUrl],\n ['category', market.category],\n ] as const) {\n if (value !== null && value.length === 0) {\n throw invalid(field);\n }\n }\n let previousTrendTimestamp = -1;\n for (const [timestamp, trendPrice] of market.trend) {\n if (\n !this.#isNonNegativeSafeInteger(timestamp) ||\n timestamp < MINIMUM_EPOCH_MILLISECONDS ||\n timestamp > generatedAt ||\n timestamp <= previousTrendTimestamp ||\n !NON_NEGATIVE_DECIMAL_PATTERN.test(trendPrice) ||\n !Number.isFinite(Number(trendPrice)) ||\n Number(trendPrice) <= 0\n ) {\n throw invalid('trend');\n }\n previousTrendTimestamp = timestamp;\n }\n }\n\n #mapSnapshotMarket(\n market: GlobalSnapshotMarket,\n sourceExpiresAt: number,\n ): PerpsMarketData {\n const formatters = this.#deps.marketDataFormatters;\n // Keep both provider price semantics explicit in the wire contract. Core\n // maps markPrice to its UI price while retaining validation of midPrice.\n const price = Number(market.markPrice);\n const change24h = Number(market.change24h);\n const volume = Number(market.volume24h);\n const openInterest = Number(market.openInterest);\n const isHip3 = market.dex !== 'main';\n const marketType = this.#marketTypeFor(market.dex, market.category);\n\n return {\n id: market.id,\n symbol: market.symbol,\n name: market.name ?? market.symbol,\n ...(market.description !== null && {\n description: market.description,\n }),\n maxLeverage: `${String(market.maxLeverage)}x`,\n price: formatters.formatPerpsFiat(price, {\n ranges: formatters.priceRangesUniversal,\n }),\n change24h: formatChange(change24h, formatters),\n change24hPercent: formatters.formatPercentage(market.changePercent24h),\n volume: formatters.formatVolume(volume),\n openInterest: formatters.formatVolume(openInterest),\n fundingRate: Number(market.funding),\n marketSource: isHip3 ? market.dex : undefined,\n marketType,\n isHip3,\n isNewMarket: isHip3 && marketType === undefined,\n ...(market.keywords && { keywords: market.keywords }),\n ...(market.tags && { tags: market.tags }),\n ...(market.category && { categories: [market.category] }),\n ...(market.listedAt !== null && { listedAt: market.listedAt }),\n trend: market.trend,\n dataSource: 'terminal-global-snapshot-mark',\n sourceExpiresAt,\n };\n }\n\n #marketTypeFor(\n dex: string,\n category: string | null,\n ): TerminalAssetMetadata['marketType'] | undefined {\n if (dex === 'main') {\n return MarketCategory.CryptoCurrency;\n }\n if (category === 'stocks') {\n return MarketCategory.Stock;\n }\n if (category === 'pre_ipo') {\n return MarketCategory.PreIpo;\n }\n if (category && VALID_MARKET_TYPES.has(category)) {\n return category as TerminalAssetMetadata['marketType'];\n }\n return undefined;\n }\n\n #cloneGlobalSnapshotResult(\n result: PerpsGlobalSnapshotResult,\n ): PerpsGlobalSnapshotResult {\n return {\n expiresAt: result.expiresAt,\n markets: clonePerpsMarketData(result.markets),\n };\n }\n\n #isNonNegativeSafeInteger(value: unknown): value is number {\n return (\n typeof value === 'number' && Number.isSafeInteger(value) && value >= 0\n );\n }\n\n #isPositiveSafeInteger(value: unknown): value is number {\n return this.#isNonNegativeSafeInteger(value) && value > 0;\n }\n\n /**\n * Invalidate the internal cache so the next fetch hits the network.\n */\n clearCache(): void {\n this.#cache = null;\n this.#globalSnapshotGeneration += 1;\n this.#globalSnapshotCache.clear();\n this.#globalSnapshotInFlight.clear();\n }\n\n /**\n * Validate raw API response items against the expected schema.\n * Items that fail validation are filtered out and logged rather than\n * rejecting the entire response.\n *\n * @param raw - The raw array from the API response body.\n * @returns Array of validated items.\n */\n #validateItems(raw: unknown[]): TerminalPerpetualItem[] {\n const valid: TerminalPerpetualItem[] = [];\n for (const item of raw) {\n if (is(item, TerminalPerpetualItemStruct)) {\n valid.push(item);\n } else {\n this.#deps.logger.error(\n ensureError(\n new Error('Terminal API item failed schema validation'),\n 'TerminalMarketService.validateItems',\n ),\n {\n tags: {\n feature: PERPS_CONSTANTS.FeatureName,\n source: 'terminal-api',\n },\n context: {\n name: 'TerminalMarketService.validateItems',\n data: {\n symbol:\n typeof item === 'object' &&\n item !== null &&\n Object.prototype.hasOwnProperty.call(item, 'symbol')\n ? (item as Record<string, unknown>).symbol\n : undefined,\n },\n },\n },\n );\n }\n }\n return valid;\n }\n\n /**\n * Map Terminal API items to the protocol-agnostic MarketInfo shape.\n *\n * @param items - Raw items from the API response.\n * @returns Array of MarketInfo objects.\n */\n #mapToMarketInfo(items: TerminalPerpetualItem[]): MarketInfo[] {\n return items\n .filter(\n (item) => typeof item.symbol === 'string' && item.symbol.length > 0,\n )\n .map((item) => ({\n name: item.symbol,\n szDecimals: item.szDecimals ?? 0,\n maxLeverage: item.maxLeverage ?? 1,\n marginTableId: item.marginTableId ?? 0,\n ...(item.onlyIsolated === true && { onlyIsolated: true as const }),\n ...(item.isDelisted === true && { isDelisted: true as const }),\n ...(item.minimumOrderSize !== undefined && {\n minimumOrderSize: item.minimumOrderSize,\n }),\n }));\n }\n\n /**\n * Extract per-symbol metadata for downstream merge into PerpsMarketData.\n *\n * @param items - Raw items from the API response.\n * @returns Map keyed by symbol with enrichment metadata.\n */\n #extractMetadata(\n items: TerminalPerpetualItem[],\n ): Map<string, TerminalAssetMetadata> {\n const map = new Map<string, TerminalAssetMetadata>();\n\n for (const item of items) {\n if (typeof item.symbol !== 'string' || item.symbol.length === 0) {\n continue;\n }\n\n const entry: TerminalAssetMetadata = {};\n\n if (typeof item.name === 'string' && item.name.length > 0) {\n entry.name = item.name;\n }\n\n if (typeof item.description === 'string' && item.description.length > 0) {\n entry.description = item.description;\n }\n\n if (Array.isArray(item.keywords) && item.keywords.length > 0) {\n entry.keywords = item.keywords;\n }\n if (Array.isArray(item.tags) && item.tags.length > 0) {\n entry.tags = item.tags;\n }\n if (Array.isArray(item.categories) && item.categories.length > 0) {\n entry.categories = item.categories;\n }\n if (\n typeof item.marketType === 'string' &&\n VALID_MARKET_TYPES.has(item.marketType)\n ) {\n entry.marketType =\n item.marketType as TerminalAssetMetadata['marketType'];\n }\n\n if (item.listedAt !== null && item.listedAt !== undefined) {\n const listedAtMs =\n typeof item.listedAt === 'number'\n ? item.listedAt\n : Date.parse(item.listedAt);\n if (isFinite(listedAtMs)) {\n entry.listedAt = listedAtMs;\n }\n }\n\n map.set(item.symbol, entry);\n }\n\n return map;\n }\n\n /**\n * Log a Terminal API error to Sentry without surfacing it to the user.\n *\n * @param error - The caught error.\n * @param method - The calling method name for context.\n */\n logError(error: unknown, method: string): void {\n this.#deps.logger.error(\n ensureError(error, `TerminalMarketService.${method}`),\n {\n tags: { feature: PERPS_CONSTANTS.FeatureName, source: 'terminal-api' },\n context: {\n name: `TerminalMarketService.${method}`,\n data: {\n url: method.includes('globalSnapshot')\n ? this.#deps.terminalApi?.globalSnapshotUrl\n : (this.#deps.terminalApi?.marketDataUrl ??\n this.#deps.terminalApiUrl),\n },\n },\n },\n );\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"TradingService.d.ts","sourceRoot":"","sources":["../../src/services/TradingService.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"TradingService.d.ts","sourceRoot":"","sources":["../../src/services/TradingService.ts"],"names":[],"mappings":"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"}