@metamask-previews/perps-controller 4.0.0-preview-e0eba6dbb → 4.0.0-preview-56dd1249f

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 (64) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/constants/eventNames.cjs +5 -0
  3. package/dist/constants/eventNames.cjs.map +1 -1
  4. package/dist/constants/eventNames.d.cts +4 -0
  5. package/dist/constants/eventNames.d.cts.map +1 -1
  6. package/dist/constants/eventNames.d.mts +4 -0
  7. package/dist/constants/eventNames.d.mts.map +1 -1
  8. package/dist/constants/eventNames.mjs +5 -0
  9. package/dist/constants/eventNames.mjs.map +1 -1
  10. package/dist/providers/HyperLiquidProvider.cjs +230 -57
  11. package/dist/providers/HyperLiquidProvider.cjs.map +1 -1
  12. package/dist/providers/HyperLiquidProvider.d.cts +1 -1
  13. package/dist/providers/HyperLiquidProvider.d.cts.map +1 -1
  14. package/dist/providers/HyperLiquidProvider.d.mts +1 -1
  15. package/dist/providers/HyperLiquidProvider.d.mts.map +1 -1
  16. package/dist/providers/HyperLiquidProvider.mjs +230 -57
  17. package/dist/providers/HyperLiquidProvider.mjs.map +1 -1
  18. package/dist/services/HyperLiquidSubscriptionService.cjs +108 -20
  19. package/dist/services/HyperLiquidSubscriptionService.cjs.map +1 -1
  20. package/dist/services/HyperLiquidSubscriptionService.d.cts +19 -0
  21. package/dist/services/HyperLiquidSubscriptionService.d.cts.map +1 -1
  22. package/dist/services/HyperLiquidSubscriptionService.d.mts +19 -0
  23. package/dist/services/HyperLiquidSubscriptionService.d.mts.map +1 -1
  24. package/dist/services/HyperLiquidSubscriptionService.mjs +108 -20
  25. package/dist/services/HyperLiquidSubscriptionService.mjs.map +1 -1
  26. package/dist/services/HyperLiquidWalletService.cjs +20 -0
  27. package/dist/services/HyperLiquidWalletService.cjs.map +1 -1
  28. package/dist/services/HyperLiquidWalletService.d.cts +6 -0
  29. package/dist/services/HyperLiquidWalletService.d.cts.map +1 -1
  30. package/dist/services/HyperLiquidWalletService.d.mts +6 -0
  31. package/dist/services/HyperLiquidWalletService.d.mts.map +1 -1
  32. package/dist/services/HyperLiquidWalletService.mjs +22 -2
  33. package/dist/services/HyperLiquidWalletService.mjs.map +1 -1
  34. package/dist/services/TradingReadinessCache.cjs +16 -16
  35. package/dist/services/TradingReadinessCache.cjs.map +1 -1
  36. package/dist/services/TradingReadinessCache.d.cts +12 -12
  37. package/dist/services/TradingReadinessCache.d.mts +12 -12
  38. package/dist/services/TradingReadinessCache.mjs +16 -16
  39. package/dist/services/TradingReadinessCache.mjs.map +1 -1
  40. package/dist/types/hyperliquid-types.cjs +39 -0
  41. package/dist/types/hyperliquid-types.cjs.map +1 -1
  42. package/dist/types/hyperliquid-types.d.cts +34 -2
  43. package/dist/types/hyperliquid-types.d.cts.map +1 -1
  44. package/dist/types/hyperliquid-types.d.mts +34 -2
  45. package/dist/types/hyperliquid-types.d.mts.map +1 -1
  46. package/dist/types/hyperliquid-types.mjs +37 -1
  47. package/dist/types/hyperliquid-types.mjs.map +1 -1
  48. package/dist/types/index.cjs +1 -0
  49. package/dist/types/index.cjs.map +1 -1
  50. package/dist/types/index.d.cts +2 -1
  51. package/dist/types/index.d.cts.map +1 -1
  52. package/dist/types/index.d.mts +2 -1
  53. package/dist/types/index.d.mts.map +1 -1
  54. package/dist/types/index.mjs +1 -0
  55. package/dist/types/index.mjs.map +1 -1
  56. package/dist/utils/accountUtils.cjs +20 -8
  57. package/dist/utils/accountUtils.cjs.map +1 -1
  58. package/dist/utils/accountUtils.d.cts +9 -5
  59. package/dist/utils/accountUtils.d.cts.map +1 -1
  60. package/dist/utils/accountUtils.d.mts +9 -5
  61. package/dist/utils/accountUtils.d.mts.map +1 -1
  62. package/dist/utils/accountUtils.mjs +20 -8
  63. package/dist/utils/accountUtils.mjs.map +1 -1
  64. package/package.json +3 -3
@@ -17,6 +17,12 @@ const utils_1 = require("@metamask/utils");
17
17
  const hyperLiquidConfig_1 = require("../constants/hyperLiquidConfig.cjs");
18
18
  const perpsErrorCodes_1 = require("../perpsErrorCodes.cjs");
19
19
  const accountUtils_1 = require("../utils/accountUtils.cjs");
20
+ // Mirrors KeyringTypes from @metamask/keyring-controller. Inlined to keep this
21
+ // service portable between mobile and the core monorepo.
22
+ const HARDWARE_KEYRING_TYPES = new Set([
23
+ 'Ledger Hardware',
24
+ 'QR Hardware Wallet Device',
25
+ ]);
20
26
  /**
21
27
  * Service for MetaMask wallet integration with HyperLiquid SDK
22
28
  * Provides wallet adapter that implements AbstractWindowEthereum interface
@@ -40,6 +46,20 @@ class HyperLiquidWalletService {
40
46
  isKeyringUnlocked() {
41
47
  return __classPrivateFieldGet(this, _HyperLiquidWalletService_messenger, "f").call('KeyringController:getState').isUnlocked;
42
48
  }
49
+ /**
50
+ * Check whether the selected EVM account is backed by hardware.
51
+ *
52
+ * @returns True for Ledger / QR hardware keyrings; false for software accounts.
53
+ */
54
+ isSelectedHardwareWallet() {
55
+ const selectedEvmAccount = (0, accountUtils_1.findEvmAccount)(__classPrivateFieldGet(this, _HyperLiquidWalletService_messenger, "f").call('AccountTreeController:getAccountsFromSelectedAccountGroup'));
56
+ if (!selectedEvmAccount || !(0, utils_1.hasProperty)(selectedEvmAccount, 'metadata')) {
57
+ return false;
58
+ }
59
+ const metadata = selectedEvmAccount.metadata;
60
+ const keyringType = metadata?.keyring?.type;
61
+ return Boolean(keyringType && HARDWARE_KEYRING_TYPES.has(keyringType));
62
+ }
43
63
  /**
44
64
  * Create wallet adapter that implements AbstractViemJsonRpcAccount interface
45
65
  * Required by @nktkas/hyperliquid SDK for signing transactions
@@ -1 +1 @@
1
- {"version":3,"file":"HyperLiquidWalletService.cjs","sourceRoot":"","sources":["../../src/services/HyperLiquidWalletService.ts"],"names":[],"mappings":"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","sourcesContent":["import { parseCaipAccountId, isValidHexAddress } from '@metamask/utils';\nimport type { CaipAccountId, Hex } from '@metamask/utils';\n\nimport { getChainId } from '../constants/hyperLiquidConfig';\nimport { PERPS_ERROR_CODES } from '../perpsErrorCodes';\nimport type {\n PerpsPlatformDependencies,\n PerpsTypedMessageParams,\n} from '../types';\nimport type { PerpsControllerMessengerBase } from '../types/messenger';\nimport { getSelectedEvmAccount } from '../utils/accountUtils';\n\n/**\n * Service for MetaMask wallet integration with HyperLiquid SDK\n * Provides wallet adapter that implements AbstractWindowEthereum interface\n */\nexport class HyperLiquidWalletService {\n #isTestnet: boolean;\n\n // Platform dependencies for observability\n readonly #deps: PerpsPlatformDependencies;\n\n readonly #messenger: PerpsControllerMessengerBase;\n\n constructor(\n deps: PerpsPlatformDependencies,\n messenger: PerpsControllerMessengerBase,\n options: { isTestnet?: boolean } = {},\n ) {\n this.#deps = deps;\n this.#messenger = messenger;\n this.#isTestnet = options.isTestnet ?? false;\n }\n\n /**\n * Check if the keyring is currently unlocked\n *\n * @returns True if the keyring is unlocked and available for signing.\n */\n public isKeyringUnlocked(): boolean {\n return this.#messenger.call('KeyringController:getState').isUnlocked;\n }\n\n /**\n * Sign typed data via DI keyring controller\n *\n * @param msgParams - The typed message parameters including data and sender address.\n * @returns The signature string.\n */\n async #signTypedMessage(msgParams: PerpsTypedMessageParams): Promise<string> {\n if (!this.isKeyringUnlocked()) {\n throw new Error(PERPS_ERROR_CODES.KEYRING_LOCKED);\n }\n // Cast needed: PerpsTypedMessageParams uses loose `data: unknown` type\n // while KeyringController uses strict TypedMessageParams / SignTypedDataVersion\n return this.#messenger.call(\n 'KeyringController:signTypedMessage',\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n msgParams as any,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n 'V4' as any,\n );\n }\n\n /**\n * Create wallet adapter that implements AbstractViemJsonRpcAccount interface\n * Required by @nktkas/hyperliquid SDK for signing transactions\n *\n * @returns The wallet adapter with address, signTypedData, and getChainId methods.\n */\n public createWalletAdapter(): {\n address: Hex;\n signTypedData: (params: {\n domain: {\n name: string;\n version: string;\n chainId: number;\n verifyingContract: Hex;\n };\n types: {\n [key: string]: { name: string; type: string }[];\n };\n primaryType: string;\n message: Record<string, unknown>;\n }) => Promise<Hex>;\n getChainId?: () => Promise<number>;\n } {\n // Get current EVM account via DI accountTree\n const evmAccount = getSelectedEvmAccount(\n this.#messenger.call(\n 'AccountTreeController:getAccountsFromSelectedAccountGroup',\n ),\n );\n\n if (!evmAccount?.address) {\n throw new Error(PERPS_ERROR_CODES.NO_ACCOUNT_SELECTED);\n }\n\n const address = evmAccount.address as Hex;\n\n return {\n address,\n signTypedData: async (params: {\n domain: {\n name: string;\n version: string;\n chainId: number;\n verifyingContract: Hex;\n };\n types: {\n [key: string]: { name: string; type: string }[];\n };\n primaryType: string;\n message: Record<string, unknown>;\n }): Promise<Hex> => {\n // Get FRESH account on every sign to handle account switches\n // This prevents race conditions where wallet adapter was created with old account\n const currentEvmAccount = getSelectedEvmAccount(\n this.#messenger.call(\n 'AccountTreeController:getAccountsFromSelectedAccountGroup',\n ),\n );\n\n if (!currentEvmAccount?.address) {\n throw new Error(PERPS_ERROR_CODES.NO_ACCOUNT_SELECTED);\n }\n\n const currentAddress = currentEvmAccount.address as Hex;\n\n // Construct EIP-712 typed data\n const typedData = {\n domain: params.domain,\n types: params.types,\n primaryType: params.primaryType,\n message: params.message,\n };\n\n this.#deps.debugLogger.log(\n 'HyperLiquidWalletService: Signing typed data',\n {\n address: currentAddress,\n primaryType: params.primaryType,\n domain: params.domain,\n },\n );\n\n // Use messenger to sign typed data\n const signature = await this.#signTypedMessage({\n from: currentAddress,\n data: typedData,\n });\n\n return signature as Hex;\n },\n getChainId: async (): Promise<number> =>\n parseInt(getChainId(this.#isTestnet), 10),\n };\n }\n\n /**\n * Get current account ID using messenger\n *\n * @returns The CAIP account ID for the current EVM account.\n */\n public async getCurrentAccountId(): Promise<CaipAccountId> {\n const evmAccount = getSelectedEvmAccount(\n this.#messenger.call(\n 'AccountTreeController:getAccountsFromSelectedAccountGroup',\n ),\n );\n\n if (!evmAccount?.address) {\n throw new Error(PERPS_ERROR_CODES.NO_ACCOUNT_SELECTED);\n }\n\n const chainId = getChainId(this.#isTestnet);\n const caipAccountId: CaipAccountId = `eip155:${chainId}:${evmAccount.address}`;\n\n return caipAccountId;\n }\n\n /**\n * Get validated user address as Hex from account ID\n *\n * @param accountId - The CAIP account ID to extract the address from.\n * @returns The validated hex address.\n */\n public getUserAddress(accountId: CaipAccountId): Hex {\n const parsed = parseCaipAccountId(accountId);\n const address = parsed.address as Hex;\n\n if (!isValidHexAddress(address)) {\n throw new Error(PERPS_ERROR_CODES.INVALID_ADDRESS_FORMAT);\n }\n\n return address;\n }\n\n /**\n * Get user address with default fallback to current account\n *\n * @param accountId - Optional CAIP account ID; defaults to current account if omitted.\n * @returns The validated hex address.\n */\n public async getUserAddressWithDefault(\n accountId?: CaipAccountId,\n ): Promise<Hex> {\n const id = accountId ?? (await this.getCurrentAccountId());\n return this.getUserAddress(id);\n }\n\n /**\n * Update testnet mode\n *\n * @param isTestnet - Whether to enable testnet mode.\n */\n public setTestnetMode(isTestnet: boolean): void {\n this.#isTestnet = isTestnet;\n }\n\n /**\n * Check if running on testnet\n *\n * @returns True if the service is in testnet mode.\n */\n public isTestnetMode(): boolean {\n return this.#isTestnet;\n }\n}\n"]}
1
+ {"version":3,"file":"HyperLiquidWalletService.cjs","sourceRoot":"","sources":["../../src/services/HyperLiquidWalletService.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n hasProperty,\n isValidHexAddress,\n parseCaipAccountId,\n} from '@metamask/utils';\nimport type { CaipAccountId, Hex } from '@metamask/utils';\n\nimport { getChainId } from '../constants/hyperLiquidConfig';\nimport { PERPS_ERROR_CODES } from '../perpsErrorCodes';\nimport type {\n PerpsPlatformDependencies,\n PerpsTypedMessageParams,\n} from '../types';\nimport type { PerpsControllerMessengerBase } from '../types/messenger';\nimport { findEvmAccount, getSelectedEvmAccount } from '../utils/accountUtils';\n\n// Mirrors KeyringTypes from @metamask/keyring-controller. Inlined to keep this\n// service portable between mobile and the core monorepo.\nconst HARDWARE_KEYRING_TYPES = new Set<string>([\n 'Ledger Hardware',\n 'QR Hardware Wallet Device',\n]);\n\n/**\n * Service for MetaMask wallet integration with HyperLiquid SDK\n * Provides wallet adapter that implements AbstractWindowEthereum interface\n */\nexport class HyperLiquidWalletService {\n #isTestnet: boolean;\n\n // Platform dependencies for observability\n readonly #deps: PerpsPlatformDependencies;\n\n readonly #messenger: PerpsControllerMessengerBase;\n\n constructor(\n deps: PerpsPlatformDependencies,\n messenger: PerpsControllerMessengerBase,\n options: { isTestnet?: boolean } = {},\n ) {\n this.#deps = deps;\n this.#messenger = messenger;\n this.#isTestnet = options.isTestnet ?? false;\n }\n\n /**\n * Check if the keyring is currently unlocked\n *\n * @returns True if the keyring is unlocked and available for signing.\n */\n public isKeyringUnlocked(): boolean {\n return this.#messenger.call('KeyringController:getState').isUnlocked;\n }\n\n /**\n * Check whether the selected EVM account is backed by hardware.\n *\n * @returns True for Ledger / QR hardware keyrings; false for software accounts.\n */\n public isSelectedHardwareWallet(): boolean {\n const selectedEvmAccount = findEvmAccount(\n this.#messenger.call(\n 'AccountTreeController:getAccountsFromSelectedAccountGroup',\n ),\n );\n if (!selectedEvmAccount || !hasProperty(selectedEvmAccount, 'metadata')) {\n return false;\n }\n\n const metadata = selectedEvmAccount.metadata as\n | { keyring?: { type?: string } }\n | undefined;\n const keyringType = metadata?.keyring?.type;\n\n return Boolean(keyringType && HARDWARE_KEYRING_TYPES.has(keyringType));\n }\n\n /**\n * Sign typed data via DI keyring controller\n *\n * @param msgParams - The typed message parameters including data and sender address.\n * @returns The signature string.\n */\n async #signTypedMessage(msgParams: PerpsTypedMessageParams): Promise<string> {\n if (!this.isKeyringUnlocked()) {\n throw new Error(PERPS_ERROR_CODES.KEYRING_LOCKED);\n }\n // Cast needed: PerpsTypedMessageParams uses loose `data: unknown` type\n // while KeyringController uses strict TypedMessageParams / SignTypedDataVersion\n return this.#messenger.call(\n 'KeyringController:signTypedMessage',\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n msgParams as any,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n 'V4' as any,\n );\n }\n\n /**\n * Create wallet adapter that implements AbstractViemJsonRpcAccount interface\n * Required by @nktkas/hyperliquid SDK for signing transactions\n *\n * @returns The wallet adapter with address, signTypedData, and getChainId methods.\n */\n public createWalletAdapter(): {\n address: Hex;\n signTypedData: (params: {\n domain: {\n name: string;\n version: string;\n chainId: number;\n verifyingContract: Hex;\n };\n types: {\n [key: string]: { name: string; type: string }[];\n };\n primaryType: string;\n message: Record<string, unknown>;\n }) => Promise<Hex>;\n getChainId?: () => Promise<number>;\n } {\n // Get current EVM account via DI accountTree\n const evmAccount = getSelectedEvmAccount(\n this.#messenger.call(\n 'AccountTreeController:getAccountsFromSelectedAccountGroup',\n ),\n );\n\n if (!evmAccount?.address) {\n throw new Error(PERPS_ERROR_CODES.NO_ACCOUNT_SELECTED);\n }\n\n const address = evmAccount.address as Hex;\n\n return {\n address,\n signTypedData: async (params: {\n domain: {\n name: string;\n version: string;\n chainId: number;\n verifyingContract: Hex;\n };\n types: {\n [key: string]: { name: string; type: string }[];\n };\n primaryType: string;\n message: Record<string, unknown>;\n }): Promise<Hex> => {\n // Get FRESH account on every sign to handle account switches\n // This prevents race conditions where wallet adapter was created with old account\n const currentEvmAccount = getSelectedEvmAccount(\n this.#messenger.call(\n 'AccountTreeController:getAccountsFromSelectedAccountGroup',\n ),\n );\n\n if (!currentEvmAccount?.address) {\n throw new Error(PERPS_ERROR_CODES.NO_ACCOUNT_SELECTED);\n }\n\n const currentAddress = currentEvmAccount.address as Hex;\n\n // Construct EIP-712 typed data\n const typedData = {\n domain: params.domain,\n types: params.types,\n primaryType: params.primaryType,\n message: params.message,\n };\n\n this.#deps.debugLogger.log(\n 'HyperLiquidWalletService: Signing typed data',\n {\n address: currentAddress,\n primaryType: params.primaryType,\n domain: params.domain,\n },\n );\n\n // Use messenger to sign typed data\n const signature = await this.#signTypedMessage({\n from: currentAddress,\n data: typedData,\n });\n\n return signature as Hex;\n },\n getChainId: async (): Promise<number> =>\n parseInt(getChainId(this.#isTestnet), 10),\n };\n }\n\n /**\n * Get current account ID using messenger\n *\n * @returns The CAIP account ID for the current EVM account.\n */\n public async getCurrentAccountId(): Promise<CaipAccountId> {\n const evmAccount = getSelectedEvmAccount(\n this.#messenger.call(\n 'AccountTreeController:getAccountsFromSelectedAccountGroup',\n ),\n );\n\n if (!evmAccount?.address) {\n throw new Error(PERPS_ERROR_CODES.NO_ACCOUNT_SELECTED);\n }\n\n const chainId = getChainId(this.#isTestnet);\n const caipAccountId: CaipAccountId = `eip155:${chainId}:${evmAccount.address}`;\n\n return caipAccountId;\n }\n\n /**\n * Get validated user address as Hex from account ID\n *\n * @param accountId - The CAIP account ID to extract the address from.\n * @returns The validated hex address.\n */\n public getUserAddress(accountId: CaipAccountId): Hex {\n const parsed = parseCaipAccountId(accountId);\n const address = parsed.address as Hex;\n\n if (!isValidHexAddress(address)) {\n throw new Error(PERPS_ERROR_CODES.INVALID_ADDRESS_FORMAT);\n }\n\n return address;\n }\n\n /**\n * Get user address with default fallback to current account\n *\n * @param accountId - Optional CAIP account ID; defaults to current account if omitted.\n * @returns The validated hex address.\n */\n public async getUserAddressWithDefault(\n accountId?: CaipAccountId,\n ): Promise<Hex> {\n const id = accountId ?? (await this.getCurrentAccountId());\n return this.getUserAddress(id);\n }\n\n /**\n * Update testnet mode\n *\n * @param isTestnet - Whether to enable testnet mode.\n */\n public setTestnetMode(isTestnet: boolean): void {\n this.#isTestnet = isTestnet;\n }\n\n /**\n * Check if running on testnet\n *\n * @returns True if the service is in testnet mode.\n */\n public isTestnetMode(): boolean {\n return this.#isTestnet;\n }\n}\n"]}
@@ -16,6 +16,12 @@ export declare class HyperLiquidWalletService {
16
16
  * @returns True if the keyring is unlocked and available for signing.
17
17
  */
18
18
  isKeyringUnlocked(): boolean;
19
+ /**
20
+ * Check whether the selected EVM account is backed by hardware.
21
+ *
22
+ * @returns True for Ledger / QR hardware keyrings; false for software accounts.
23
+ */
24
+ isSelectedHardwareWallet(): boolean;
19
25
  /**
20
26
  * Create wallet adapter that implements AbstractViemJsonRpcAccount interface
21
27
  * Required by @nktkas/hyperliquid SDK for signing transactions
@@ -1 +1 @@
1
- {"version":3,"file":"HyperLiquidWalletService.d.cts","sourceRoot":"","sources":["../../src/services/HyperLiquidWalletService.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,GAAG,EAAE,wBAAwB;AAI1D,OAAO,KAAK,EACV,yBAAyB,EAE1B,2BAAiB;AAClB,OAAO,KAAK,EAAE,4BAA4B,EAAE,+BAA2B;AAGvE;;;GAGG;AACH,qBAAa,wBAAwB;;gBASjC,IAAI,EAAE,yBAAyB,EAC/B,SAAS,EAAE,4BAA4B,EACvC,OAAO,GAAE;QAAE,SAAS,CAAC,EAAE,OAAO,CAAA;KAAO;IAOvC;;;;OAIG;IACI,iBAAiB,IAAI,OAAO;IAyBnC;;;;;OAKG;IACI,mBAAmB,IAAI;QAC5B,OAAO,EAAE,GAAG,CAAC;QACb,aAAa,EAAE,CAAC,MAAM,EAAE;YACtB,MAAM,EAAE;gBACN,IAAI,EAAE,MAAM,CAAC;gBACb,OAAO,EAAE,MAAM,CAAC;gBAChB,OAAO,EAAE,MAAM,CAAC;gBAChB,iBAAiB,EAAE,GAAG,CAAC;aACxB,CAAC;YACF,KAAK,EAAE;gBACL,CAAC,GAAG,EAAE,MAAM,GAAG;oBAAE,IAAI,EAAE,MAAM,CAAC;oBAAC,IAAI,EAAE,MAAM,CAAA;iBAAE,EAAE,CAAC;aACjD,CAAC;YACF,WAAW,EAAE,MAAM,CAAC;YACpB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;SAClC,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC;QACnB,UAAU,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;KACpC;IAyED;;;;OAIG;IACU,mBAAmB,IAAI,OAAO,CAAC,aAAa,CAAC;IAiB1D;;;;;OAKG;IACI,cAAc,CAAC,SAAS,EAAE,aAAa,GAAG,GAAG;IAWpD;;;;;OAKG;IACU,yBAAyB,CACpC,SAAS,CAAC,EAAE,aAAa,GACxB,OAAO,CAAC,GAAG,CAAC;IAKf;;;;OAIG;IACI,cAAc,CAAC,SAAS,EAAE,OAAO,GAAG,IAAI;IAI/C;;;;OAIG;IACI,aAAa,IAAI,OAAO;CAGhC"}
1
+ {"version":3,"file":"HyperLiquidWalletService.d.cts","sourceRoot":"","sources":["../../src/services/HyperLiquidWalletService.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,aAAa,EAAE,GAAG,EAAE,wBAAwB;AAI1D,OAAO,KAAK,EACV,yBAAyB,EAE1B,2BAAiB;AAClB,OAAO,KAAK,EAAE,4BAA4B,EAAE,+BAA2B;AAUvE;;;GAGG;AACH,qBAAa,wBAAwB;;gBASjC,IAAI,EAAE,yBAAyB,EAC/B,SAAS,EAAE,4BAA4B,EACvC,OAAO,GAAE;QAAE,SAAS,CAAC,EAAE,OAAO,CAAA;KAAO;IAOvC;;;;OAIG;IACI,iBAAiB,IAAI,OAAO;IAInC;;;;OAIG;IACI,wBAAwB,IAAI,OAAO;IAuC1C;;;;;OAKG;IACI,mBAAmB,IAAI;QAC5B,OAAO,EAAE,GAAG,CAAC;QACb,aAAa,EAAE,CAAC,MAAM,EAAE;YACtB,MAAM,EAAE;gBACN,IAAI,EAAE,MAAM,CAAC;gBACb,OAAO,EAAE,MAAM,CAAC;gBAChB,OAAO,EAAE,MAAM,CAAC;gBAChB,iBAAiB,EAAE,GAAG,CAAC;aACxB,CAAC;YACF,KAAK,EAAE;gBACL,CAAC,GAAG,EAAE,MAAM,GAAG;oBAAE,IAAI,EAAE,MAAM,CAAC;oBAAC,IAAI,EAAE,MAAM,CAAA;iBAAE,EAAE,CAAC;aACjD,CAAC;YACF,WAAW,EAAE,MAAM,CAAC;YACpB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;SAClC,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC;QACnB,UAAU,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;KACpC;IAyED;;;;OAIG;IACU,mBAAmB,IAAI,OAAO,CAAC,aAAa,CAAC;IAiB1D;;;;;OAKG;IACI,cAAc,CAAC,SAAS,EAAE,aAAa,GAAG,GAAG;IAWpD;;;;;OAKG;IACU,yBAAyB,CACpC,SAAS,CAAC,EAAE,aAAa,GACxB,OAAO,CAAC,GAAG,CAAC;IAKf;;;;OAIG;IACI,cAAc,CAAC,SAAS,EAAE,OAAO,GAAG,IAAI;IAI/C;;;;OAIG;IACI,aAAa,IAAI,OAAO;CAGhC"}
@@ -16,6 +16,12 @@ export declare class HyperLiquidWalletService {
16
16
  * @returns True if the keyring is unlocked and available for signing.
17
17
  */
18
18
  isKeyringUnlocked(): boolean;
19
+ /**
20
+ * Check whether the selected EVM account is backed by hardware.
21
+ *
22
+ * @returns True for Ledger / QR hardware keyrings; false for software accounts.
23
+ */
24
+ isSelectedHardwareWallet(): boolean;
19
25
  /**
20
26
  * Create wallet adapter that implements AbstractViemJsonRpcAccount interface
21
27
  * Required by @nktkas/hyperliquid SDK for signing transactions
@@ -1 +1 @@
1
- {"version":3,"file":"HyperLiquidWalletService.d.mts","sourceRoot":"","sources":["../../src/services/HyperLiquidWalletService.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,GAAG,EAAE,wBAAwB;AAI1D,OAAO,KAAK,EACV,yBAAyB,EAE1B,2BAAiB;AAClB,OAAO,KAAK,EAAE,4BAA4B,EAAE,+BAA2B;AAGvE;;;GAGG;AACH,qBAAa,wBAAwB;;gBASjC,IAAI,EAAE,yBAAyB,EAC/B,SAAS,EAAE,4BAA4B,EACvC,OAAO,GAAE;QAAE,SAAS,CAAC,EAAE,OAAO,CAAA;KAAO;IAOvC;;;;OAIG;IACI,iBAAiB,IAAI,OAAO;IAyBnC;;;;;OAKG;IACI,mBAAmB,IAAI;QAC5B,OAAO,EAAE,GAAG,CAAC;QACb,aAAa,EAAE,CAAC,MAAM,EAAE;YACtB,MAAM,EAAE;gBACN,IAAI,EAAE,MAAM,CAAC;gBACb,OAAO,EAAE,MAAM,CAAC;gBAChB,OAAO,EAAE,MAAM,CAAC;gBAChB,iBAAiB,EAAE,GAAG,CAAC;aACxB,CAAC;YACF,KAAK,EAAE;gBACL,CAAC,GAAG,EAAE,MAAM,GAAG;oBAAE,IAAI,EAAE,MAAM,CAAC;oBAAC,IAAI,EAAE,MAAM,CAAA;iBAAE,EAAE,CAAC;aACjD,CAAC;YACF,WAAW,EAAE,MAAM,CAAC;YACpB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;SAClC,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC;QACnB,UAAU,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;KACpC;IAyED;;;;OAIG;IACU,mBAAmB,IAAI,OAAO,CAAC,aAAa,CAAC;IAiB1D;;;;;OAKG;IACI,cAAc,CAAC,SAAS,EAAE,aAAa,GAAG,GAAG;IAWpD;;;;;OAKG;IACU,yBAAyB,CACpC,SAAS,CAAC,EAAE,aAAa,GACxB,OAAO,CAAC,GAAG,CAAC;IAKf;;;;OAIG;IACI,cAAc,CAAC,SAAS,EAAE,OAAO,GAAG,IAAI;IAI/C;;;;OAIG;IACI,aAAa,IAAI,OAAO;CAGhC"}
1
+ {"version":3,"file":"HyperLiquidWalletService.d.mts","sourceRoot":"","sources":["../../src/services/HyperLiquidWalletService.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,aAAa,EAAE,GAAG,EAAE,wBAAwB;AAI1D,OAAO,KAAK,EACV,yBAAyB,EAE1B,2BAAiB;AAClB,OAAO,KAAK,EAAE,4BAA4B,EAAE,+BAA2B;AAUvE;;;GAGG;AACH,qBAAa,wBAAwB;;gBASjC,IAAI,EAAE,yBAAyB,EAC/B,SAAS,EAAE,4BAA4B,EACvC,OAAO,GAAE;QAAE,SAAS,CAAC,EAAE,OAAO,CAAA;KAAO;IAOvC;;;;OAIG;IACI,iBAAiB,IAAI,OAAO;IAInC;;;;OAIG;IACI,wBAAwB,IAAI,OAAO;IAuC1C;;;;;OAKG;IACI,mBAAmB,IAAI;QAC5B,OAAO,EAAE,GAAG,CAAC;QACb,aAAa,EAAE,CAAC,MAAM,EAAE;YACtB,MAAM,EAAE;gBACN,IAAI,EAAE,MAAM,CAAC;gBACb,OAAO,EAAE,MAAM,CAAC;gBAChB,OAAO,EAAE,MAAM,CAAC;gBAChB,iBAAiB,EAAE,GAAG,CAAC;aACxB,CAAC;YACF,KAAK,EAAE;gBACL,CAAC,GAAG,EAAE,MAAM,GAAG;oBAAE,IAAI,EAAE,MAAM,CAAC;oBAAC,IAAI,EAAE,MAAM,CAAA;iBAAE,EAAE,CAAC;aACjD,CAAC;YACF,WAAW,EAAE,MAAM,CAAC;YACpB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;SAClC,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC;QACnB,UAAU,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;KACpC;IAyED;;;;OAIG;IACU,mBAAmB,IAAI,OAAO,CAAC,aAAa,CAAC;IAiB1D;;;;;OAKG;IACI,cAAc,CAAC,SAAS,EAAE,aAAa,GAAG,GAAG;IAWpD;;;;;OAKG;IACU,yBAAyB,CACpC,SAAS,CAAC,EAAE,aAAa,GACxB,OAAO,CAAC,GAAG,CAAC;IAKf;;;;OAIG;IACI,cAAc,CAAC,SAAS,EAAE,OAAO,GAAG,IAAI;IAI/C;;;;OAIG;IACI,aAAa,IAAI,OAAO;CAGhC"}
@@ -10,10 +10,16 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
10
10
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
11
11
  };
12
12
  var _HyperLiquidWalletService_instances, _HyperLiquidWalletService_isTestnet, _HyperLiquidWalletService_deps, _HyperLiquidWalletService_messenger, _HyperLiquidWalletService_signTypedMessage;
13
- import { parseCaipAccountId, isValidHexAddress } from "@metamask/utils";
13
+ import { hasProperty, isValidHexAddress, parseCaipAccountId } from "@metamask/utils";
14
14
  import { getChainId } from "../constants/hyperLiquidConfig.mjs";
15
15
  import { PERPS_ERROR_CODES } from "../perpsErrorCodes.mjs";
16
- import { getSelectedEvmAccount } from "../utils/accountUtils.mjs";
16
+ import { findEvmAccount, getSelectedEvmAccount } from "../utils/accountUtils.mjs";
17
+ // Mirrors KeyringTypes from @metamask/keyring-controller. Inlined to keep this
18
+ // service portable between mobile and the core monorepo.
19
+ const HARDWARE_KEYRING_TYPES = new Set([
20
+ 'Ledger Hardware',
21
+ 'QR Hardware Wallet Device',
22
+ ]);
17
23
  /**
18
24
  * Service for MetaMask wallet integration with HyperLiquid SDK
19
25
  * Provides wallet adapter that implements AbstractWindowEthereum interface
@@ -37,6 +43,20 @@ export class HyperLiquidWalletService {
37
43
  isKeyringUnlocked() {
38
44
  return __classPrivateFieldGet(this, _HyperLiquidWalletService_messenger, "f").call('KeyringController:getState').isUnlocked;
39
45
  }
46
+ /**
47
+ * Check whether the selected EVM account is backed by hardware.
48
+ *
49
+ * @returns True for Ledger / QR hardware keyrings; false for software accounts.
50
+ */
51
+ isSelectedHardwareWallet() {
52
+ const selectedEvmAccount = findEvmAccount(__classPrivateFieldGet(this, _HyperLiquidWalletService_messenger, "f").call('AccountTreeController:getAccountsFromSelectedAccountGroup'));
53
+ if (!selectedEvmAccount || !hasProperty(selectedEvmAccount, 'metadata')) {
54
+ return false;
55
+ }
56
+ const metadata = selectedEvmAccount.metadata;
57
+ const keyringType = metadata?.keyring?.type;
58
+ return Boolean(keyringType && HARDWARE_KEYRING_TYPES.has(keyringType));
59
+ }
40
60
  /**
41
61
  * Create wallet adapter that implements AbstractViemJsonRpcAccount interface
42
62
  * Required by @nktkas/hyperliquid SDK for signing transactions
@@ -1 +1 @@
1
- {"version":3,"file":"HyperLiquidWalletService.mjs","sourceRoot":"","sources":["../../src/services/HyperLiquidWalletService.ts"],"names":[],"mappings":"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","sourcesContent":["import { parseCaipAccountId, isValidHexAddress } from '@metamask/utils';\nimport type { CaipAccountId, Hex } from '@metamask/utils';\n\nimport { getChainId } from '../constants/hyperLiquidConfig';\nimport { PERPS_ERROR_CODES } from '../perpsErrorCodes';\nimport type {\n PerpsPlatformDependencies,\n PerpsTypedMessageParams,\n} from '../types';\nimport type { PerpsControllerMessengerBase } from '../types/messenger';\nimport { getSelectedEvmAccount } from '../utils/accountUtils';\n\n/**\n * Service for MetaMask wallet integration with HyperLiquid SDK\n * Provides wallet adapter that implements AbstractWindowEthereum interface\n */\nexport class HyperLiquidWalletService {\n #isTestnet: boolean;\n\n // Platform dependencies for observability\n readonly #deps: PerpsPlatformDependencies;\n\n readonly #messenger: PerpsControllerMessengerBase;\n\n constructor(\n deps: PerpsPlatformDependencies,\n messenger: PerpsControllerMessengerBase,\n options: { isTestnet?: boolean } = {},\n ) {\n this.#deps = deps;\n this.#messenger = messenger;\n this.#isTestnet = options.isTestnet ?? false;\n }\n\n /**\n * Check if the keyring is currently unlocked\n *\n * @returns True if the keyring is unlocked and available for signing.\n */\n public isKeyringUnlocked(): boolean {\n return this.#messenger.call('KeyringController:getState').isUnlocked;\n }\n\n /**\n * Sign typed data via DI keyring controller\n *\n * @param msgParams - The typed message parameters including data and sender address.\n * @returns The signature string.\n */\n async #signTypedMessage(msgParams: PerpsTypedMessageParams): Promise<string> {\n if (!this.isKeyringUnlocked()) {\n throw new Error(PERPS_ERROR_CODES.KEYRING_LOCKED);\n }\n // Cast needed: PerpsTypedMessageParams uses loose `data: unknown` type\n // while KeyringController uses strict TypedMessageParams / SignTypedDataVersion\n return this.#messenger.call(\n 'KeyringController:signTypedMessage',\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n msgParams as any,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n 'V4' as any,\n );\n }\n\n /**\n * Create wallet adapter that implements AbstractViemJsonRpcAccount interface\n * Required by @nktkas/hyperliquid SDK for signing transactions\n *\n * @returns The wallet adapter with address, signTypedData, and getChainId methods.\n */\n public createWalletAdapter(): {\n address: Hex;\n signTypedData: (params: {\n domain: {\n name: string;\n version: string;\n chainId: number;\n verifyingContract: Hex;\n };\n types: {\n [key: string]: { name: string; type: string }[];\n };\n primaryType: string;\n message: Record<string, unknown>;\n }) => Promise<Hex>;\n getChainId?: () => Promise<number>;\n } {\n // Get current EVM account via DI accountTree\n const evmAccount = getSelectedEvmAccount(\n this.#messenger.call(\n 'AccountTreeController:getAccountsFromSelectedAccountGroup',\n ),\n );\n\n if (!evmAccount?.address) {\n throw new Error(PERPS_ERROR_CODES.NO_ACCOUNT_SELECTED);\n }\n\n const address = evmAccount.address as Hex;\n\n return {\n address,\n signTypedData: async (params: {\n domain: {\n name: string;\n version: string;\n chainId: number;\n verifyingContract: Hex;\n };\n types: {\n [key: string]: { name: string; type: string }[];\n };\n primaryType: string;\n message: Record<string, unknown>;\n }): Promise<Hex> => {\n // Get FRESH account on every sign to handle account switches\n // This prevents race conditions where wallet adapter was created with old account\n const currentEvmAccount = getSelectedEvmAccount(\n this.#messenger.call(\n 'AccountTreeController:getAccountsFromSelectedAccountGroup',\n ),\n );\n\n if (!currentEvmAccount?.address) {\n throw new Error(PERPS_ERROR_CODES.NO_ACCOUNT_SELECTED);\n }\n\n const currentAddress = currentEvmAccount.address as Hex;\n\n // Construct EIP-712 typed data\n const typedData = {\n domain: params.domain,\n types: params.types,\n primaryType: params.primaryType,\n message: params.message,\n };\n\n this.#deps.debugLogger.log(\n 'HyperLiquidWalletService: Signing typed data',\n {\n address: currentAddress,\n primaryType: params.primaryType,\n domain: params.domain,\n },\n );\n\n // Use messenger to sign typed data\n const signature = await this.#signTypedMessage({\n from: currentAddress,\n data: typedData,\n });\n\n return signature as Hex;\n },\n getChainId: async (): Promise<number> =>\n parseInt(getChainId(this.#isTestnet), 10),\n };\n }\n\n /**\n * Get current account ID using messenger\n *\n * @returns The CAIP account ID for the current EVM account.\n */\n public async getCurrentAccountId(): Promise<CaipAccountId> {\n const evmAccount = getSelectedEvmAccount(\n this.#messenger.call(\n 'AccountTreeController:getAccountsFromSelectedAccountGroup',\n ),\n );\n\n if (!evmAccount?.address) {\n throw new Error(PERPS_ERROR_CODES.NO_ACCOUNT_SELECTED);\n }\n\n const chainId = getChainId(this.#isTestnet);\n const caipAccountId: CaipAccountId = `eip155:${chainId}:${evmAccount.address}`;\n\n return caipAccountId;\n }\n\n /**\n * Get validated user address as Hex from account ID\n *\n * @param accountId - The CAIP account ID to extract the address from.\n * @returns The validated hex address.\n */\n public getUserAddress(accountId: CaipAccountId): Hex {\n const parsed = parseCaipAccountId(accountId);\n const address = parsed.address as Hex;\n\n if (!isValidHexAddress(address)) {\n throw new Error(PERPS_ERROR_CODES.INVALID_ADDRESS_FORMAT);\n }\n\n return address;\n }\n\n /**\n * Get user address with default fallback to current account\n *\n * @param accountId - Optional CAIP account ID; defaults to current account if omitted.\n * @returns The validated hex address.\n */\n public async getUserAddressWithDefault(\n accountId?: CaipAccountId,\n ): Promise<Hex> {\n const id = accountId ?? (await this.getCurrentAccountId());\n return this.getUserAddress(id);\n }\n\n /**\n * Update testnet mode\n *\n * @param isTestnet - Whether to enable testnet mode.\n */\n public setTestnetMode(isTestnet: boolean): void {\n this.#isTestnet = isTestnet;\n }\n\n /**\n * Check if running on testnet\n *\n * @returns True if the service is in testnet mode.\n */\n public isTestnetMode(): boolean {\n return this.#isTestnet;\n }\n}\n"]}
1
+ {"version":3,"file":"HyperLiquidWalletService.mjs","sourceRoot":"","sources":["../../src/services/HyperLiquidWalletService.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n hasProperty,\n isValidHexAddress,\n parseCaipAccountId,\n} from '@metamask/utils';\nimport type { CaipAccountId, Hex } from '@metamask/utils';\n\nimport { getChainId } from '../constants/hyperLiquidConfig';\nimport { PERPS_ERROR_CODES } from '../perpsErrorCodes';\nimport type {\n PerpsPlatformDependencies,\n PerpsTypedMessageParams,\n} from '../types';\nimport type { PerpsControllerMessengerBase } from '../types/messenger';\nimport { findEvmAccount, getSelectedEvmAccount } from '../utils/accountUtils';\n\n// Mirrors KeyringTypes from @metamask/keyring-controller. Inlined to keep this\n// service portable between mobile and the core monorepo.\nconst HARDWARE_KEYRING_TYPES = new Set<string>([\n 'Ledger Hardware',\n 'QR Hardware Wallet Device',\n]);\n\n/**\n * Service for MetaMask wallet integration with HyperLiquid SDK\n * Provides wallet adapter that implements AbstractWindowEthereum interface\n */\nexport class HyperLiquidWalletService {\n #isTestnet: boolean;\n\n // Platform dependencies for observability\n readonly #deps: PerpsPlatformDependencies;\n\n readonly #messenger: PerpsControllerMessengerBase;\n\n constructor(\n deps: PerpsPlatformDependencies,\n messenger: PerpsControllerMessengerBase,\n options: { isTestnet?: boolean } = {},\n ) {\n this.#deps = deps;\n this.#messenger = messenger;\n this.#isTestnet = options.isTestnet ?? false;\n }\n\n /**\n * Check if the keyring is currently unlocked\n *\n * @returns True if the keyring is unlocked and available for signing.\n */\n public isKeyringUnlocked(): boolean {\n return this.#messenger.call('KeyringController:getState').isUnlocked;\n }\n\n /**\n * Check whether the selected EVM account is backed by hardware.\n *\n * @returns True for Ledger / QR hardware keyrings; false for software accounts.\n */\n public isSelectedHardwareWallet(): boolean {\n const selectedEvmAccount = findEvmAccount(\n this.#messenger.call(\n 'AccountTreeController:getAccountsFromSelectedAccountGroup',\n ),\n );\n if (!selectedEvmAccount || !hasProperty(selectedEvmAccount, 'metadata')) {\n return false;\n }\n\n const metadata = selectedEvmAccount.metadata as\n | { keyring?: { type?: string } }\n | undefined;\n const keyringType = metadata?.keyring?.type;\n\n return Boolean(keyringType && HARDWARE_KEYRING_TYPES.has(keyringType));\n }\n\n /**\n * Sign typed data via DI keyring controller\n *\n * @param msgParams - The typed message parameters including data and sender address.\n * @returns The signature string.\n */\n async #signTypedMessage(msgParams: PerpsTypedMessageParams): Promise<string> {\n if (!this.isKeyringUnlocked()) {\n throw new Error(PERPS_ERROR_CODES.KEYRING_LOCKED);\n }\n // Cast needed: PerpsTypedMessageParams uses loose `data: unknown` type\n // while KeyringController uses strict TypedMessageParams / SignTypedDataVersion\n return this.#messenger.call(\n 'KeyringController:signTypedMessage',\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n msgParams as any,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n 'V4' as any,\n );\n }\n\n /**\n * Create wallet adapter that implements AbstractViemJsonRpcAccount interface\n * Required by @nktkas/hyperliquid SDK for signing transactions\n *\n * @returns The wallet adapter with address, signTypedData, and getChainId methods.\n */\n public createWalletAdapter(): {\n address: Hex;\n signTypedData: (params: {\n domain: {\n name: string;\n version: string;\n chainId: number;\n verifyingContract: Hex;\n };\n types: {\n [key: string]: { name: string; type: string }[];\n };\n primaryType: string;\n message: Record<string, unknown>;\n }) => Promise<Hex>;\n getChainId?: () => Promise<number>;\n } {\n // Get current EVM account via DI accountTree\n const evmAccount = getSelectedEvmAccount(\n this.#messenger.call(\n 'AccountTreeController:getAccountsFromSelectedAccountGroup',\n ),\n );\n\n if (!evmAccount?.address) {\n throw new Error(PERPS_ERROR_CODES.NO_ACCOUNT_SELECTED);\n }\n\n const address = evmAccount.address as Hex;\n\n return {\n address,\n signTypedData: async (params: {\n domain: {\n name: string;\n version: string;\n chainId: number;\n verifyingContract: Hex;\n };\n types: {\n [key: string]: { name: string; type: string }[];\n };\n primaryType: string;\n message: Record<string, unknown>;\n }): Promise<Hex> => {\n // Get FRESH account on every sign to handle account switches\n // This prevents race conditions where wallet adapter was created with old account\n const currentEvmAccount = getSelectedEvmAccount(\n this.#messenger.call(\n 'AccountTreeController:getAccountsFromSelectedAccountGroup',\n ),\n );\n\n if (!currentEvmAccount?.address) {\n throw new Error(PERPS_ERROR_CODES.NO_ACCOUNT_SELECTED);\n }\n\n const currentAddress = currentEvmAccount.address as Hex;\n\n // Construct EIP-712 typed data\n const typedData = {\n domain: params.domain,\n types: params.types,\n primaryType: params.primaryType,\n message: params.message,\n };\n\n this.#deps.debugLogger.log(\n 'HyperLiquidWalletService: Signing typed data',\n {\n address: currentAddress,\n primaryType: params.primaryType,\n domain: params.domain,\n },\n );\n\n // Use messenger to sign typed data\n const signature = await this.#signTypedMessage({\n from: currentAddress,\n data: typedData,\n });\n\n return signature as Hex;\n },\n getChainId: async (): Promise<number> =>\n parseInt(getChainId(this.#isTestnet), 10),\n };\n }\n\n /**\n * Get current account ID using messenger\n *\n * @returns The CAIP account ID for the current EVM account.\n */\n public async getCurrentAccountId(): Promise<CaipAccountId> {\n const evmAccount = getSelectedEvmAccount(\n this.#messenger.call(\n 'AccountTreeController:getAccountsFromSelectedAccountGroup',\n ),\n );\n\n if (!evmAccount?.address) {\n throw new Error(PERPS_ERROR_CODES.NO_ACCOUNT_SELECTED);\n }\n\n const chainId = getChainId(this.#isTestnet);\n const caipAccountId: CaipAccountId = `eip155:${chainId}:${evmAccount.address}`;\n\n return caipAccountId;\n }\n\n /**\n * Get validated user address as Hex from account ID\n *\n * @param accountId - The CAIP account ID to extract the address from.\n * @returns The validated hex address.\n */\n public getUserAddress(accountId: CaipAccountId): Hex {\n const parsed = parseCaipAccountId(accountId);\n const address = parsed.address as Hex;\n\n if (!isValidHexAddress(address)) {\n throw new Error(PERPS_ERROR_CODES.INVALID_ADDRESS_FORMAT);\n }\n\n return address;\n }\n\n /**\n * Get user address with default fallback to current account\n *\n * @param accountId - Optional CAIP account ID; defaults to current account if omitted.\n * @returns The validated hex address.\n */\n public async getUserAddressWithDefault(\n accountId?: CaipAccountId,\n ): Promise<Hex> {\n const id = accountId ?? (await this.getCurrentAccountId());\n return this.getUserAddress(id);\n }\n\n /**\n * Update testnet mode\n *\n * @param isTestnet - Whether to enable testnet mode.\n */\n public setTestnetMode(isTestnet: boolean): void {\n this.#isTestnet = isTestnet;\n }\n\n /**\n * Check if running on testnet\n *\n * @returns True if the service is in testnet mode.\n */\n public isTestnetMode(): boolean {\n return this.#isTestnet;\n }\n}\n"]}
@@ -9,13 +9,13 @@
9
9
  * are recreated on account/network changes, which would reset instance-level caches.
10
10
  *
11
11
  * Tracks three signing operations:
12
- * 1. DEX Abstraction enablement (one-time, irreversible)
12
+ * 1. Unified Account enablement (one-time, replaces deprecated DEX abstraction)
13
13
  * 2. Builder Fee approval (required for trading)
14
14
  * 3. Referral code setup (one-time per account)
15
15
  *
16
16
  * Cache Structure:
17
17
  * - Key: `network:userAddress` (e.g., "mainnet:0x123...")
18
- * - Value: { dexAbstraction, builderFee, referral, timestamp }
18
+ * - Value: { unifiedAccount, builderFee, referral, timestamp }
19
19
  *
20
20
  * Lifecycle:
21
21
  * - Cache persists throughout app session
@@ -85,9 +85,9 @@ class PerpsSigningCacheManager {
85
85
  resolvePromise();
86
86
  };
87
87
  }
88
- // ===== DEX Abstraction Methods =====
88
+ // ===== Unified Account Methods =====
89
89
  /**
90
- * Get DEX abstraction cache entry (legacy compatibility)
90
+ * Get unified account cache entry (legacy compatibility)
91
91
  *
92
92
  * @param network - The network environment.
93
93
  * @param userAddress - The user's wallet address.
@@ -100,13 +100,13 @@ class PerpsSigningCacheManager {
100
100
  return undefined;
101
101
  }
102
102
  return {
103
- attempted: entry.dexAbstraction.attempted,
104
- enabled: entry.dexAbstraction.success,
103
+ attempted: entry.unifiedAccount.attempted,
104
+ enabled: entry.unifiedAccount.success,
105
105
  timestamp: entry.timestamp,
106
106
  };
107
107
  }
108
108
  /**
109
- * Set DEX abstraction cache entry (legacy compatibility)
109
+ * Set unified account cache entry (legacy compatibility)
110
110
  *
111
111
  * @param network - The network environment.
112
112
  * @param userAddress - The user's wallet address.
@@ -116,7 +116,7 @@ class PerpsSigningCacheManager {
116
116
  */
117
117
  set(network, userAddress, data) {
118
118
  const entry = __classPrivateFieldGet(this, _PerpsSigningCacheManager_instances, "m", _PerpsSigningCacheManager_getOrCreateEntry).call(this, network, userAddress);
119
- entry.dexAbstraction = { attempted: data.attempted, success: data.enabled };
119
+ entry.unifiedAccount = { attempted: data.attempted, success: data.enabled };
120
120
  entry.timestamp = Date.now();
121
121
  }
122
122
  // ===== Builder Fee Methods =====
@@ -171,23 +171,23 @@ class PerpsSigningCacheManager {
171
171
  }
172
172
  // ===== General Methods =====
173
173
  /**
174
- * Clear only DEX abstraction state for a specific network and user address
174
+ * Clear only unified account state for a specific network and user address
175
175
  * This preserves builder fee and referral states
176
176
  *
177
177
  * @param network - The network environment.
178
178
  * @param userAddress - The user's wallet address.
179
179
  */
180
- clearDexAbstraction(network, userAddress) {
180
+ clearUnifiedAccount(network, userAddress) {
181
181
  const key = __classPrivateFieldGet(this, _PerpsSigningCacheManager_instances, "m", _PerpsSigningCacheManager_getCacheKey).call(this, network, userAddress);
182
182
  const entry = __classPrivateFieldGet(this, _PerpsSigningCacheManager_cache, "f").get(key);
183
183
  if (entry) {
184
- entry.dexAbstraction = { attempted: false, success: false };
184
+ entry.unifiedAccount = { attempted: false, success: false };
185
185
  entry.timestamp = Date.now();
186
186
  }
187
187
  }
188
188
  /**
189
189
  * Clear only builder fee state for a specific network and user address
190
- * This preserves DEX abstraction and referral states
190
+ * This preserves unified account and referral states
191
191
  *
192
192
  * @param network - The network environment.
193
193
  * @param userAddress - The user's wallet address.
@@ -202,7 +202,7 @@ class PerpsSigningCacheManager {
202
202
  }
203
203
  /**
204
204
  * Clear only referral state for a specific network and user address
205
- * This preserves DEX abstraction and builder fee states
205
+ * This preserves unified account and builder fee states
206
206
  *
207
207
  * @param network - The network environment.
208
208
  * @param userAddress - The user's wallet address.
@@ -217,7 +217,7 @@ class PerpsSigningCacheManager {
217
217
  }
218
218
  /**
219
219
  * Clear entire cache entry for a specific network and user address
220
- * WARNING: This clears ALL signing operation states (dexAbstraction, builderFee, referral)
220
+ * WARNING: This clears ALL signing operation states (unifiedAccount, builderFee, referral)
221
221
  *
222
222
  * @param network - The network environment.
223
223
  * @param userAddress - The user's wallet address.
@@ -257,7 +257,7 @@ class PerpsSigningCacheManager {
257
257
  debugState() {
258
258
  const entries = [];
259
259
  __classPrivateFieldGet(this, _PerpsSigningCacheManager_cache, "f").forEach((entry, key) => {
260
- entries.push(`${key}: dex=${entry.dexAbstraction.attempted}/${entry.dexAbstraction.success}, ` +
260
+ entries.push(`${key}: unified=${entry.unifiedAccount.attempted}/${entry.unifiedAccount.success}, ` +
261
261
  `builder=${entry.builderFee.attempted}/${entry.builderFee.success}, ` +
262
262
  `referral=${entry.referral.attempted}/${entry.referral.success}`);
263
263
  });
@@ -271,7 +271,7 @@ _a = PerpsSigningCacheManager, _PerpsSigningCacheManager_cache = new WeakMap(),
271
271
  let entry = __classPrivateFieldGet(this, _PerpsSigningCacheManager_cache, "f").get(key);
272
272
  if (!entry) {
273
273
  entry = {
274
- dexAbstraction: { attempted: false, success: false },
274
+ unifiedAccount: { attempted: false, success: false },
275
275
  builderFee: { attempted: false, success: false },
276
276
  referral: { attempted: false, success: false },
277
277
  timestamp: Date.now(),
@@ -1 +1 @@
1
- {"version":3,"file":"TradingReadinessCache.cjs","sourceRoot":"","sources":["../../src/services/TradingReadinessCache.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Global singleton cache for Perps signing operations\n *\n * This cache persists across provider reconnections to prevent repeated\n * signing requests for hardware wallets. Critical for preventing QR popup spam.\n *\n * Cache is intentionally kept separate from provider instances because providers\n * are recreated on account/network changes, which would reset instance-level caches.\n *\n * Tracks three signing operations:\n * 1. DEX Abstraction enablement (one-time, irreversible)\n * 2. Builder Fee approval (required for trading)\n * 3. Referral code setup (one-time per account)\n *\n * Cache Structure:\n * - Key: `network:userAddress` (e.g., \"mainnet:0x123...\")\n * - Value: { dexAbstraction, builderFee, referral, timestamp }\n *\n * Lifecycle:\n * - Cache persists throughout app session\n * - Individual entries can be cleared per user/network\n * - Full cache can be cleared on app restart or explicit user action\n */\n\ntype SigningOperationState = {\n attempted: boolean; // Whether we've attempted this operation\n success: boolean; // Whether it succeeded (only valid if attempted=true)\n};\n\ntype PerpsSigningCacheEntry = {\n dexAbstraction: SigningOperationState;\n builderFee: SigningOperationState;\n referral: SigningOperationState;\n timestamp: number; // When this entry was last updated\n};\n\n// Legacy interface for backward compatibility\ntype TradingReadinessCacheEntry = {\n attempted: boolean;\n enabled: boolean;\n timestamp: number;\n};\n\nclass PerpsSigningCacheManager {\n static #instance: PerpsSigningCacheManager;\n\n readonly #cache: Map<string, PerpsSigningCacheEntry> = new Map();\n\n // Global in-flight locks to prevent concurrent signing attempts across providers\n // Key: operationType:network:userAddress, Value: Promise that resolves when operation completes\n readonly #inFlightOperations: Map<string, Promise<void>> = new Map();\n\n // Singleton: use getInstance() instead of new\n protected constructor() {\n // Protected constructor for singleton\n }\n\n public static getInstance(): PerpsSigningCacheManager {\n PerpsSigningCacheManager.#instance ??= new PerpsSigningCacheManager();\n return PerpsSigningCacheManager.#instance;\n }\n\n // ===== In-Flight Lock Methods =====\n\n /**\n * Check if an operation is currently in-flight for this user/network\n *\n * @param operationType - The type of operation being performed.\n * @param network - The network environment.\n * @param userAddress - The user's wallet address.\n * @returns The resulting string value.\n */\n public isInFlight(\n operationType: 'dexAbstraction' | 'builderFee' | 'referral',\n network: 'mainnet' | 'testnet',\n userAddress: string,\n ): Promise<void> | undefined {\n const key = `${operationType}:${network}:${userAddress.toLowerCase()}`;\n return this.#inFlightOperations.get(key);\n }\n\n /**\n * Set an operation as in-flight\n * Returns a function to call when operation completes\n *\n * @param operationType - The type of operation being performed.\n * @param network - The network environment.\n * @param userAddress - The user's wallet address.\n * @returns The resulting string value.\n */\n public setInFlight(\n operationType: 'dexAbstraction' | 'builderFee' | 'referral',\n network: 'mainnet' | 'testnet',\n userAddress: string,\n ): () => void {\n const key = `${operationType}:${network}:${userAddress.toLowerCase()}`;\n let resolvePromise: () => void;\n const promise = new Promise<void>((resolve) => {\n resolvePromise = resolve;\n });\n this.#inFlightOperations.set(key, promise);\n return () => {\n this.#inFlightOperations.delete(key);\n resolvePromise();\n };\n }\n\n #getCacheKey(network: 'mainnet' | 'testnet', userAddress: string): string {\n return `${network}:${userAddress.toLowerCase()}`;\n }\n\n #getOrCreateEntry(\n network: 'mainnet' | 'testnet',\n userAddress: string,\n ): PerpsSigningCacheEntry {\n const key = this.#getCacheKey(network, userAddress);\n let entry = this.#cache.get(key);\n if (!entry) {\n entry = {\n dexAbstraction: { attempted: false, success: false },\n builderFee: { attempted: false, success: false },\n referral: { attempted: false, success: false },\n timestamp: Date.now(),\n };\n this.#cache.set(key, entry);\n }\n return entry;\n }\n\n // ===== DEX Abstraction Methods =====\n\n /**\n * Get DEX abstraction cache entry (legacy compatibility)\n *\n * @param network - The network environment.\n * @param userAddress - The user's wallet address.\n * @returns The resulting string value.\n */\n public get(\n network: 'mainnet' | 'testnet',\n userAddress: string,\n ): TradingReadinessCacheEntry | undefined {\n const key = this.#getCacheKey(network, userAddress);\n const entry = this.#cache.get(key);\n if (!entry) {\n return undefined;\n }\n return {\n attempted: entry.dexAbstraction.attempted,\n enabled: entry.dexAbstraction.success,\n timestamp: entry.timestamp,\n };\n }\n\n /**\n * Set DEX abstraction cache entry (legacy compatibility)\n *\n * @param network - The network environment.\n * @param userAddress - The user's wallet address.\n * @param data - The transaction data payload.\n * @param data.attempted - Whether the operation was attempted.\n * @param data.enabled - Whether the feature is enabled.\n */\n public set(\n network: 'mainnet' | 'testnet',\n userAddress: string,\n data: { attempted: boolean; enabled: boolean },\n ): void {\n const entry = this.#getOrCreateEntry(network, userAddress);\n entry.dexAbstraction = { attempted: data.attempted, success: data.enabled };\n entry.timestamp = Date.now();\n }\n\n // ===== Builder Fee Methods =====\n\n /**\n * Check if builder fee approval was attempted\n *\n * @param network - The network environment.\n * @param userAddress - The user's wallet address.\n * @returns The resulting string value.\n */\n public getBuilderFee(\n network: 'mainnet' | 'testnet',\n userAddress: string,\n ): SigningOperationState | undefined {\n const key = this.#getCacheKey(network, userAddress);\n const entry = this.#cache.get(key);\n return entry?.builderFee;\n }\n\n /**\n * Set builder fee approval state\n *\n * @param network - The network environment.\n * @param userAddress - The user's wallet address.\n * @param state - The current state.\n */\n public setBuilderFee(\n network: 'mainnet' | 'testnet',\n userAddress: string,\n state: SigningOperationState,\n ): void {\n const entry = this.#getOrCreateEntry(network, userAddress);\n entry.builderFee = state;\n entry.timestamp = Date.now();\n }\n\n // ===== Referral Methods =====\n\n /**\n * Check if referral setup was attempted\n *\n * @param network - The network environment.\n * @param userAddress - The user's wallet address.\n * @returns The resulting string value.\n */\n public getReferral(\n network: 'mainnet' | 'testnet',\n userAddress: string,\n ): SigningOperationState | undefined {\n const key = this.#getCacheKey(network, userAddress);\n const entry = this.#cache.get(key);\n return entry?.referral;\n }\n\n /**\n * Set referral setup state\n *\n * @param network - The network environment.\n * @param userAddress - The user's wallet address.\n * @param state - The current state.\n */\n public setReferral(\n network: 'mainnet' | 'testnet',\n userAddress: string,\n state: SigningOperationState,\n ): void {\n const entry = this.#getOrCreateEntry(network, userAddress);\n entry.referral = state;\n entry.timestamp = Date.now();\n }\n\n // ===== General Methods =====\n\n /**\n * Clear only DEX abstraction state for a specific network and user address\n * This preserves builder fee and referral states\n *\n * @param network - The network environment.\n * @param userAddress - The user's wallet address.\n */\n public clearDexAbstraction(\n network: 'mainnet' | 'testnet',\n userAddress: string,\n ): void {\n const key = this.#getCacheKey(network, userAddress);\n const entry = this.#cache.get(key);\n if (entry) {\n entry.dexAbstraction = { attempted: false, success: false };\n entry.timestamp = Date.now();\n }\n }\n\n /**\n * Clear only builder fee state for a specific network and user address\n * This preserves DEX abstraction and referral states\n *\n * @param network - The network environment.\n * @param userAddress - The user's wallet address.\n */\n public clearBuilderFee(\n network: 'mainnet' | 'testnet',\n userAddress: string,\n ): void {\n const key = this.#getCacheKey(network, userAddress);\n const entry = this.#cache.get(key);\n if (entry) {\n entry.builderFee = { attempted: false, success: false };\n entry.timestamp = Date.now();\n }\n }\n\n /**\n * Clear only referral state for a specific network and user address\n * This preserves DEX abstraction and builder fee states\n *\n * @param network - The network environment.\n * @param userAddress - The user's wallet address.\n */\n public clearReferral(\n network: 'mainnet' | 'testnet',\n userAddress: string,\n ): void {\n const key = this.#getCacheKey(network, userAddress);\n const entry = this.#cache.get(key);\n if (entry) {\n entry.referral = { attempted: false, success: false };\n entry.timestamp = Date.now();\n }\n }\n\n /**\n * Clear entire cache entry for a specific network and user address\n * WARNING: This clears ALL signing operation states (dexAbstraction, builderFee, referral)\n *\n * @param network - The network environment.\n * @param userAddress - The user's wallet address.\n */\n public clear(network: 'mainnet' | 'testnet', userAddress: string): void {\n const key = this.#getCacheKey(network, userAddress);\n this.#cache.delete(key);\n }\n\n /**\n * Clear all cache entries\n * WARNING: This clears ALL signing operation states for ALL users\n */\n public clearAll(): void {\n this.#cache.clear();\n }\n\n /**\n * Get all cache entries (for debugging)\n *\n * @returns The result of the operation.\n */\n public getAll(): Map<string, PerpsSigningCacheEntry> {\n return new Map(this.#cache);\n }\n\n /**\n * Get cache size (for debugging)\n *\n * @returns The resulting numeric value.\n */\n public size(): number {\n return this.#cache.size;\n }\n\n /**\n * Get full cache state for debugging\n *\n * @returns The resulting string value.\n */\n public debugState(): string {\n const entries: string[] = [];\n this.#cache.forEach((entry, key) => {\n entries.push(\n `${key}: dex=${entry.dexAbstraction.attempted}/${entry.dexAbstraction.success}, ` +\n `builder=${entry.builderFee.attempted}/${entry.builderFee.success}, ` +\n `referral=${entry.referral.attempted}/${entry.referral.success}`,\n );\n });\n return entries.join('\\n') || '(empty)';\n }\n}\n\n// Export singleton instance with backward-compatible name\nexport const TradingReadinessCache = PerpsSigningCacheManager.getInstance();\n\n// Export with new name for clarity\nexport const PerpsSigningCache = PerpsSigningCacheManager.getInstance();\n"]}
1
+ {"version":3,"file":"TradingReadinessCache.cjs","sourceRoot":"","sources":["../../src/services/TradingReadinessCache.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Global singleton cache for Perps signing operations\n *\n * This cache persists across provider reconnections to prevent repeated\n * signing requests for hardware wallets. Critical for preventing QR popup spam.\n *\n * Cache is intentionally kept separate from provider instances because providers\n * are recreated on account/network changes, which would reset instance-level caches.\n *\n * Tracks three signing operations:\n * 1. Unified Account enablement (one-time, replaces deprecated DEX abstraction)\n * 2. Builder Fee approval (required for trading)\n * 3. Referral code setup (one-time per account)\n *\n * Cache Structure:\n * - Key: `network:userAddress` (e.g., \"mainnet:0x123...\")\n * - Value: { unifiedAccount, builderFee, referral, timestamp }\n *\n * Lifecycle:\n * - Cache persists throughout app session\n * - Individual entries can be cleared per user/network\n * - Full cache can be cleared on app restart or explicit user action\n */\n\ntype SigningOperationState = {\n attempted: boolean; // Whether we've attempted this operation\n success: boolean; // Whether it succeeded (only valid if attempted=true)\n};\n\ntype PerpsSigningCacheEntry = {\n unifiedAccount: SigningOperationState;\n builderFee: SigningOperationState;\n referral: SigningOperationState;\n timestamp: number; // When this entry was last updated\n};\n\n// Legacy interface for backward compatibility\ntype TradingReadinessCacheEntry = {\n attempted: boolean;\n enabled: boolean;\n timestamp: number;\n};\n\nclass PerpsSigningCacheManager {\n static #instance: PerpsSigningCacheManager;\n\n readonly #cache: Map<string, PerpsSigningCacheEntry> = new Map();\n\n // Global in-flight locks to prevent concurrent signing attempts across providers\n // Key: operationType:network:userAddress, Value: Promise that resolves when operation completes\n readonly #inFlightOperations: Map<string, Promise<void>> = new Map();\n\n // Singleton: use getInstance() instead of new\n protected constructor() {\n // Protected constructor for singleton\n }\n\n public static getInstance(): PerpsSigningCacheManager {\n PerpsSigningCacheManager.#instance ??= new PerpsSigningCacheManager();\n return PerpsSigningCacheManager.#instance;\n }\n\n // ===== In-Flight Lock Methods =====\n\n /**\n * Check if an operation is currently in-flight for this user/network\n *\n * @param operationType - The type of operation being performed.\n * @param network - The network environment.\n * @param userAddress - The user's wallet address.\n * @returns The resulting string value.\n */\n public isInFlight(\n operationType: 'unifiedAccount' | 'builderFee' | 'referral',\n network: 'mainnet' | 'testnet',\n userAddress: string,\n ): Promise<void> | undefined {\n const key = `${operationType}:${network}:${userAddress.toLowerCase()}`;\n return this.#inFlightOperations.get(key);\n }\n\n /**\n * Set an operation as in-flight\n * Returns a function to call when operation completes\n *\n * @param operationType - The type of operation being performed.\n * @param network - The network environment.\n * @param userAddress - The user's wallet address.\n * @returns The resulting string value.\n */\n public setInFlight(\n operationType: 'unifiedAccount' | 'builderFee' | 'referral',\n network: 'mainnet' | 'testnet',\n userAddress: string,\n ): () => void {\n const key = `${operationType}:${network}:${userAddress.toLowerCase()}`;\n let resolvePromise: () => void;\n const promise = new Promise<void>((resolve) => {\n resolvePromise = resolve;\n });\n this.#inFlightOperations.set(key, promise);\n return () => {\n this.#inFlightOperations.delete(key);\n resolvePromise();\n };\n }\n\n #getCacheKey(network: 'mainnet' | 'testnet', userAddress: string): string {\n return `${network}:${userAddress.toLowerCase()}`;\n }\n\n #getOrCreateEntry(\n network: 'mainnet' | 'testnet',\n userAddress: string,\n ): PerpsSigningCacheEntry {\n const key = this.#getCacheKey(network, userAddress);\n let entry = this.#cache.get(key);\n if (!entry) {\n entry = {\n unifiedAccount: { attempted: false, success: false },\n builderFee: { attempted: false, success: false },\n referral: { attempted: false, success: false },\n timestamp: Date.now(),\n };\n this.#cache.set(key, entry);\n }\n return entry;\n }\n\n // ===== Unified Account Methods =====\n\n /**\n * Get unified account cache entry (legacy compatibility)\n *\n * @param network - The network environment.\n * @param userAddress - The user's wallet address.\n * @returns The resulting string value.\n */\n public get(\n network: 'mainnet' | 'testnet',\n userAddress: string,\n ): TradingReadinessCacheEntry | undefined {\n const key = this.#getCacheKey(network, userAddress);\n const entry = this.#cache.get(key);\n if (!entry) {\n return undefined;\n }\n return {\n attempted: entry.unifiedAccount.attempted,\n enabled: entry.unifiedAccount.success,\n timestamp: entry.timestamp,\n };\n }\n\n /**\n * Set unified account cache entry (legacy compatibility)\n *\n * @param network - The network environment.\n * @param userAddress - The user's wallet address.\n * @param data - The transaction data payload.\n * @param data.attempted - Whether the operation was attempted.\n * @param data.enabled - Whether the feature is enabled.\n */\n public set(\n network: 'mainnet' | 'testnet',\n userAddress: string,\n data: { attempted: boolean; enabled: boolean },\n ): void {\n const entry = this.#getOrCreateEntry(network, userAddress);\n entry.unifiedAccount = { attempted: data.attempted, success: data.enabled };\n entry.timestamp = Date.now();\n }\n\n // ===== Builder Fee Methods =====\n\n /**\n * Check if builder fee approval was attempted\n *\n * @param network - The network environment.\n * @param userAddress - The user's wallet address.\n * @returns The resulting string value.\n */\n public getBuilderFee(\n network: 'mainnet' | 'testnet',\n userAddress: string,\n ): SigningOperationState | undefined {\n const key = this.#getCacheKey(network, userAddress);\n const entry = this.#cache.get(key);\n return entry?.builderFee;\n }\n\n /**\n * Set builder fee approval state\n *\n * @param network - The network environment.\n * @param userAddress - The user's wallet address.\n * @param state - The current state.\n */\n public setBuilderFee(\n network: 'mainnet' | 'testnet',\n userAddress: string,\n state: SigningOperationState,\n ): void {\n const entry = this.#getOrCreateEntry(network, userAddress);\n entry.builderFee = state;\n entry.timestamp = Date.now();\n }\n\n // ===== Referral Methods =====\n\n /**\n * Check if referral setup was attempted\n *\n * @param network - The network environment.\n * @param userAddress - The user's wallet address.\n * @returns The resulting string value.\n */\n public getReferral(\n network: 'mainnet' | 'testnet',\n userAddress: string,\n ): SigningOperationState | undefined {\n const key = this.#getCacheKey(network, userAddress);\n const entry = this.#cache.get(key);\n return entry?.referral;\n }\n\n /**\n * Set referral setup state\n *\n * @param network - The network environment.\n * @param userAddress - The user's wallet address.\n * @param state - The current state.\n */\n public setReferral(\n network: 'mainnet' | 'testnet',\n userAddress: string,\n state: SigningOperationState,\n ): void {\n const entry = this.#getOrCreateEntry(network, userAddress);\n entry.referral = state;\n entry.timestamp = Date.now();\n }\n\n // ===== General Methods =====\n\n /**\n * Clear only unified account state for a specific network and user address\n * This preserves builder fee and referral states\n *\n * @param network - The network environment.\n * @param userAddress - The user's wallet address.\n */\n public clearUnifiedAccount(\n network: 'mainnet' | 'testnet',\n userAddress: string,\n ): void {\n const key = this.#getCacheKey(network, userAddress);\n const entry = this.#cache.get(key);\n if (entry) {\n entry.unifiedAccount = { attempted: false, success: false };\n entry.timestamp = Date.now();\n }\n }\n\n /**\n * Clear only builder fee state for a specific network and user address\n * This preserves unified account and referral states\n *\n * @param network - The network environment.\n * @param userAddress - The user's wallet address.\n */\n public clearBuilderFee(\n network: 'mainnet' | 'testnet',\n userAddress: string,\n ): void {\n const key = this.#getCacheKey(network, userAddress);\n const entry = this.#cache.get(key);\n if (entry) {\n entry.builderFee = { attempted: false, success: false };\n entry.timestamp = Date.now();\n }\n }\n\n /**\n * Clear only referral state for a specific network and user address\n * This preserves unified account and builder fee states\n *\n * @param network - The network environment.\n * @param userAddress - The user's wallet address.\n */\n public clearReferral(\n network: 'mainnet' | 'testnet',\n userAddress: string,\n ): void {\n const key = this.#getCacheKey(network, userAddress);\n const entry = this.#cache.get(key);\n if (entry) {\n entry.referral = { attempted: false, success: false };\n entry.timestamp = Date.now();\n }\n }\n\n /**\n * Clear entire cache entry for a specific network and user address\n * WARNING: This clears ALL signing operation states (unifiedAccount, builderFee, referral)\n *\n * @param network - The network environment.\n * @param userAddress - The user's wallet address.\n */\n public clear(network: 'mainnet' | 'testnet', userAddress: string): void {\n const key = this.#getCacheKey(network, userAddress);\n this.#cache.delete(key);\n }\n\n /**\n * Clear all cache entries\n * WARNING: This clears ALL signing operation states for ALL users\n */\n public clearAll(): void {\n this.#cache.clear();\n }\n\n /**\n * Get all cache entries (for debugging)\n *\n * @returns The result of the operation.\n */\n public getAll(): Map<string, PerpsSigningCacheEntry> {\n return new Map(this.#cache);\n }\n\n /**\n * Get cache size (for debugging)\n *\n * @returns The resulting numeric value.\n */\n public size(): number {\n return this.#cache.size;\n }\n\n /**\n * Get full cache state for debugging\n *\n * @returns The resulting string value.\n */\n public debugState(): string {\n const entries: string[] = [];\n this.#cache.forEach((entry, key) => {\n entries.push(\n `${key}: unified=${entry.unifiedAccount.attempted}/${entry.unifiedAccount.success}, ` +\n `builder=${entry.builderFee.attempted}/${entry.builderFee.success}, ` +\n `referral=${entry.referral.attempted}/${entry.referral.success}`,\n );\n });\n return entries.join('\\n') || '(empty)';\n }\n}\n\n// Export singleton instance with backward-compatible name\nexport const TradingReadinessCache = PerpsSigningCacheManager.getInstance();\n\n// Export with new name for clarity\nexport const PerpsSigningCache = PerpsSigningCacheManager.getInstance();\n"]}
@@ -8,13 +8,13 @@
8
8
  * are recreated on account/network changes, which would reset instance-level caches.
9
9
  *
10
10
  * Tracks three signing operations:
11
- * 1. DEX Abstraction enablement (one-time, irreversible)
11
+ * 1. Unified Account enablement (one-time, replaces deprecated DEX abstraction)
12
12
  * 2. Builder Fee approval (required for trading)
13
13
  * 3. Referral code setup (one-time per account)
14
14
  *
15
15
  * Cache Structure:
16
16
  * - Key: `network:userAddress` (e.g., "mainnet:0x123...")
17
- * - Value: { dexAbstraction, builderFee, referral, timestamp }
17
+ * - Value: { unifiedAccount, builderFee, referral, timestamp }
18
18
  *
19
19
  * Lifecycle:
20
20
  * - Cache persists throughout app session
@@ -26,7 +26,7 @@ type SigningOperationState = {
26
26
  success: boolean;
27
27
  };
28
28
  type PerpsSigningCacheEntry = {
29
- dexAbstraction: SigningOperationState;
29
+ unifiedAccount: SigningOperationState;
30
30
  builderFee: SigningOperationState;
31
31
  referral: SigningOperationState;
32
32
  timestamp: number;
@@ -48,7 +48,7 @@ declare class PerpsSigningCacheManager {
48
48
  * @param userAddress - The user's wallet address.
49
49
  * @returns The resulting string value.
50
50
  */
51
- isInFlight(operationType: 'dexAbstraction' | 'builderFee' | 'referral', network: 'mainnet' | 'testnet', userAddress: string): Promise<void> | undefined;
51
+ isInFlight(operationType: 'unifiedAccount' | 'builderFee' | 'referral', network: 'mainnet' | 'testnet', userAddress: string): Promise<void> | undefined;
52
52
  /**
53
53
  * Set an operation as in-flight
54
54
  * Returns a function to call when operation completes
@@ -58,9 +58,9 @@ declare class PerpsSigningCacheManager {
58
58
  * @param userAddress - The user's wallet address.
59
59
  * @returns The resulting string value.
60
60
  */
61
- setInFlight(operationType: 'dexAbstraction' | 'builderFee' | 'referral', network: 'mainnet' | 'testnet', userAddress: string): () => void;
61
+ setInFlight(operationType: 'unifiedAccount' | 'builderFee' | 'referral', network: 'mainnet' | 'testnet', userAddress: string): () => void;
62
62
  /**
63
- * Get DEX abstraction cache entry (legacy compatibility)
63
+ * Get unified account cache entry (legacy compatibility)
64
64
  *
65
65
  * @param network - The network environment.
66
66
  * @param userAddress - The user's wallet address.
@@ -68,7 +68,7 @@ declare class PerpsSigningCacheManager {
68
68
  */
69
69
  get(network: 'mainnet' | 'testnet', userAddress: string): TradingReadinessCacheEntry | undefined;
70
70
  /**
71
- * Set DEX abstraction cache entry (legacy compatibility)
71
+ * Set unified account cache entry (legacy compatibility)
72
72
  *
73
73
  * @param network - The network environment.
74
74
  * @param userAddress - The user's wallet address.
@@ -113,16 +113,16 @@ declare class PerpsSigningCacheManager {
113
113
  */
114
114
  setReferral(network: 'mainnet' | 'testnet', userAddress: string, state: SigningOperationState): void;
115
115
  /**
116
- * Clear only DEX abstraction state for a specific network and user address
116
+ * Clear only unified account state for a specific network and user address
117
117
  * This preserves builder fee and referral states
118
118
  *
119
119
  * @param network - The network environment.
120
120
  * @param userAddress - The user's wallet address.
121
121
  */
122
- clearDexAbstraction(network: 'mainnet' | 'testnet', userAddress: string): void;
122
+ clearUnifiedAccount(network: 'mainnet' | 'testnet', userAddress: string): void;
123
123
  /**
124
124
  * Clear only builder fee state for a specific network and user address
125
- * This preserves DEX abstraction and referral states
125
+ * This preserves unified account and referral states
126
126
  *
127
127
  * @param network - The network environment.
128
128
  * @param userAddress - The user's wallet address.
@@ -130,7 +130,7 @@ declare class PerpsSigningCacheManager {
130
130
  clearBuilderFee(network: 'mainnet' | 'testnet', userAddress: string): void;
131
131
  /**
132
132
  * Clear only referral state for a specific network and user address
133
- * This preserves DEX abstraction and builder fee states
133
+ * This preserves unified account and builder fee states
134
134
  *
135
135
  * @param network - The network environment.
136
136
  * @param userAddress - The user's wallet address.
@@ -138,7 +138,7 @@ declare class PerpsSigningCacheManager {
138
138
  clearReferral(network: 'mainnet' | 'testnet', userAddress: string): void;
139
139
  /**
140
140
  * Clear entire cache entry for a specific network and user address
141
- * WARNING: This clears ALL signing operation states (dexAbstraction, builderFee, referral)
141
+ * WARNING: This clears ALL signing operation states (unifiedAccount, builderFee, referral)
142
142
  *
143
143
  * @param network - The network environment.
144
144
  * @param userAddress - The user's wallet address.
@@ -8,13 +8,13 @@
8
8
  * are recreated on account/network changes, which would reset instance-level caches.
9
9
  *
10
10
  * Tracks three signing operations:
11
- * 1. DEX Abstraction enablement (one-time, irreversible)
11
+ * 1. Unified Account enablement (one-time, replaces deprecated DEX abstraction)
12
12
  * 2. Builder Fee approval (required for trading)
13
13
  * 3. Referral code setup (one-time per account)
14
14
  *
15
15
  * Cache Structure:
16
16
  * - Key: `network:userAddress` (e.g., "mainnet:0x123...")
17
- * - Value: { dexAbstraction, builderFee, referral, timestamp }
17
+ * - Value: { unifiedAccount, builderFee, referral, timestamp }
18
18
  *
19
19
  * Lifecycle:
20
20
  * - Cache persists throughout app session
@@ -26,7 +26,7 @@ type SigningOperationState = {
26
26
  success: boolean;
27
27
  };
28
28
  type PerpsSigningCacheEntry = {
29
- dexAbstraction: SigningOperationState;
29
+ unifiedAccount: SigningOperationState;
30
30
  builderFee: SigningOperationState;
31
31
  referral: SigningOperationState;
32
32
  timestamp: number;
@@ -48,7 +48,7 @@ declare class PerpsSigningCacheManager {
48
48
  * @param userAddress - The user's wallet address.
49
49
  * @returns The resulting string value.
50
50
  */
51
- isInFlight(operationType: 'dexAbstraction' | 'builderFee' | 'referral', network: 'mainnet' | 'testnet', userAddress: string): Promise<void> | undefined;
51
+ isInFlight(operationType: 'unifiedAccount' | 'builderFee' | 'referral', network: 'mainnet' | 'testnet', userAddress: string): Promise<void> | undefined;
52
52
  /**
53
53
  * Set an operation as in-flight
54
54
  * Returns a function to call when operation completes
@@ -58,9 +58,9 @@ declare class PerpsSigningCacheManager {
58
58
  * @param userAddress - The user's wallet address.
59
59
  * @returns The resulting string value.
60
60
  */
61
- setInFlight(operationType: 'dexAbstraction' | 'builderFee' | 'referral', network: 'mainnet' | 'testnet', userAddress: string): () => void;
61
+ setInFlight(operationType: 'unifiedAccount' | 'builderFee' | 'referral', network: 'mainnet' | 'testnet', userAddress: string): () => void;
62
62
  /**
63
- * Get DEX abstraction cache entry (legacy compatibility)
63
+ * Get unified account cache entry (legacy compatibility)
64
64
  *
65
65
  * @param network - The network environment.
66
66
  * @param userAddress - The user's wallet address.
@@ -68,7 +68,7 @@ declare class PerpsSigningCacheManager {
68
68
  */
69
69
  get(network: 'mainnet' | 'testnet', userAddress: string): TradingReadinessCacheEntry | undefined;
70
70
  /**
71
- * Set DEX abstraction cache entry (legacy compatibility)
71
+ * Set unified account cache entry (legacy compatibility)
72
72
  *
73
73
  * @param network - The network environment.
74
74
  * @param userAddress - The user's wallet address.
@@ -113,16 +113,16 @@ declare class PerpsSigningCacheManager {
113
113
  */
114
114
  setReferral(network: 'mainnet' | 'testnet', userAddress: string, state: SigningOperationState): void;
115
115
  /**
116
- * Clear only DEX abstraction state for a specific network and user address
116
+ * Clear only unified account state for a specific network and user address
117
117
  * This preserves builder fee and referral states
118
118
  *
119
119
  * @param network - The network environment.
120
120
  * @param userAddress - The user's wallet address.
121
121
  */
122
- clearDexAbstraction(network: 'mainnet' | 'testnet', userAddress: string): void;
122
+ clearUnifiedAccount(network: 'mainnet' | 'testnet', userAddress: string): void;
123
123
  /**
124
124
  * Clear only builder fee state for a specific network and user address
125
- * This preserves DEX abstraction and referral states
125
+ * This preserves unified account and referral states
126
126
  *
127
127
  * @param network - The network environment.
128
128
  * @param userAddress - The user's wallet address.
@@ -130,7 +130,7 @@ declare class PerpsSigningCacheManager {
130
130
  clearBuilderFee(network: 'mainnet' | 'testnet', userAddress: string): void;
131
131
  /**
132
132
  * Clear only referral state for a specific network and user address
133
- * This preserves DEX abstraction and builder fee states
133
+ * This preserves unified account and builder fee states
134
134
  *
135
135
  * @param network - The network environment.
136
136
  * @param userAddress - The user's wallet address.
@@ -138,7 +138,7 @@ declare class PerpsSigningCacheManager {
138
138
  clearReferral(network: 'mainnet' | 'testnet', userAddress: string): void;
139
139
  /**
140
140
  * Clear entire cache entry for a specific network and user address
141
- * WARNING: This clears ALL signing operation states (dexAbstraction, builderFee, referral)
141
+ * WARNING: This clears ALL signing operation states (unifiedAccount, builderFee, referral)
142
142
  *
143
143
  * @param network - The network environment.
144
144
  * @param userAddress - The user's wallet address.