@metamask-previews/perps-controller 0.0.0-preview-ae46ab603 → 0.0.0-preview-e4aa1532

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 (498) hide show
  1. package/CHANGELOG.md +11 -1
  2. package/dist/PerpsController.cjs +2224 -66
  3. package/dist/PerpsController.cjs.map +1 -1
  4. package/dist/PerpsController.d.cts +933 -79
  5. package/dist/PerpsController.d.cts.map +1 -1
  6. package/dist/PerpsController.d.mts +933 -79
  7. package/dist/PerpsController.d.mts.map +1 -1
  8. package/dist/PerpsController.mjs +2221 -64
  9. package/dist/PerpsController.mjs.map +1 -1
  10. package/dist/__mocks__/hyperliquidMock.cjs +68 -0
  11. package/dist/__mocks__/hyperliquidMock.cjs.map +1 -0
  12. package/dist/__mocks__/hyperliquidMock.d.cts +15 -0
  13. package/dist/__mocks__/hyperliquidMock.d.cts.map +1 -0
  14. package/dist/__mocks__/hyperliquidMock.d.mts +15 -0
  15. package/dist/__mocks__/hyperliquidMock.d.mts.map +1 -0
  16. package/dist/__mocks__/hyperliquidMock.mjs +65 -0
  17. package/dist/__mocks__/hyperliquidMock.mjs.map +1 -0
  18. package/dist/__mocks__/providerMocks.cjs +144 -0
  19. package/dist/__mocks__/providerMocks.cjs.map +1 -0
  20. package/dist/__mocks__/providerMocks.d.cts +44 -0
  21. package/dist/__mocks__/providerMocks.d.cts.map +1 -0
  22. package/dist/__mocks__/providerMocks.d.mts +44 -0
  23. package/dist/__mocks__/providerMocks.d.mts.map +1 -0
  24. package/dist/__mocks__/providerMocks.mjs +136 -0
  25. package/dist/__mocks__/providerMocks.mjs.map +1 -0
  26. package/dist/__mocks__/serviceMocks.cjs +187 -0
  27. package/dist/__mocks__/serviceMocks.cjs.map +1 -0
  28. package/dist/__mocks__/serviceMocks.d.cts +58 -0
  29. package/dist/__mocks__/serviceMocks.d.cts.map +1 -0
  30. package/dist/__mocks__/serviceMocks.d.mts +58 -0
  31. package/dist/__mocks__/serviceMocks.d.mts.map +1 -0
  32. package/dist/__mocks__/serviceMocks.mjs +180 -0
  33. package/dist/__mocks__/serviceMocks.mjs.map +1 -0
  34. package/dist/aggregation/SubscriptionMultiplexer.cjs +459 -0
  35. package/dist/aggregation/SubscriptionMultiplexer.cjs.map +1 -0
  36. package/dist/aggregation/SubscriptionMultiplexer.d.cts +222 -0
  37. package/dist/aggregation/SubscriptionMultiplexer.d.cts.map +1 -0
  38. package/dist/aggregation/SubscriptionMultiplexer.d.mts +222 -0
  39. package/dist/aggregation/SubscriptionMultiplexer.d.mts.map +1 -0
  40. package/dist/aggregation/SubscriptionMultiplexer.mjs +455 -0
  41. package/dist/aggregation/SubscriptionMultiplexer.mjs.map +1 -0
  42. package/dist/aggregation/index.cjs +9 -0
  43. package/dist/aggregation/index.cjs.map +1 -0
  44. package/dist/aggregation/index.d.cts +6 -0
  45. package/dist/aggregation/index.d.cts.map +1 -0
  46. package/dist/aggregation/index.d.mts +6 -0
  47. package/dist/aggregation/index.d.mts.map +1 -0
  48. package/dist/aggregation/index.mjs +5 -0
  49. package/dist/aggregation/index.mjs.map +1 -0
  50. package/dist/constants/eventNames.cjs +117 -0
  51. package/dist/constants/eventNames.cjs.map +1 -0
  52. package/dist/constants/eventNames.d.cts +106 -0
  53. package/dist/constants/eventNames.d.cts.map +1 -0
  54. package/dist/constants/eventNames.d.mts +106 -0
  55. package/dist/constants/eventNames.d.mts.map +1 -0
  56. package/dist/constants/eventNames.mjs +114 -0
  57. package/dist/constants/eventNames.mjs.map +1 -0
  58. package/dist/constants/hyperLiquidConfig.cjs +366 -0
  59. package/dist/constants/hyperLiquidConfig.cjs.map +1 -0
  60. package/dist/constants/hyperLiquidConfig.d.cts +276 -0
  61. package/dist/constants/hyperLiquidConfig.d.cts.map +1 -0
  62. package/dist/constants/hyperLiquidConfig.d.mts +276 -0
  63. package/dist/constants/hyperLiquidConfig.d.mts.map +1 -0
  64. package/dist/constants/hyperLiquidConfig.mjs +357 -0
  65. package/dist/constants/hyperLiquidConfig.mjs.map +1 -0
  66. package/dist/constants/index.cjs +23 -0
  67. package/dist/constants/index.cjs.map +1 -0
  68. package/dist/constants/index.d.cts +7 -0
  69. package/dist/constants/index.d.cts.map +1 -0
  70. package/dist/constants/index.d.mts +7 -0
  71. package/dist/constants/index.d.mts.map +1 -0
  72. package/dist/constants/index.mjs +7 -0
  73. package/dist/constants/index.mjs.map +1 -0
  74. package/dist/constants/orderTypes.cjs +31 -0
  75. package/dist/constants/orderTypes.cjs.map +1 -0
  76. package/dist/constants/orderTypes.d.cts +19 -0
  77. package/dist/constants/orderTypes.d.cts.map +1 -0
  78. package/dist/constants/orderTypes.d.mts +19 -0
  79. package/dist/constants/orderTypes.d.mts.map +1 -0
  80. package/dist/constants/orderTypes.mjs +27 -0
  81. package/dist/constants/orderTypes.mjs.map +1 -0
  82. package/dist/constants/performanceMetrics.cjs +61 -0
  83. package/dist/constants/performanceMetrics.cjs.map +1 -0
  84. package/dist/constants/performanceMetrics.d.cts +44 -0
  85. package/dist/constants/performanceMetrics.d.cts.map +1 -0
  86. package/dist/constants/performanceMetrics.d.mts +44 -0
  87. package/dist/constants/performanceMetrics.d.mts.map +1 -0
  88. package/dist/constants/performanceMetrics.mjs +58 -0
  89. package/dist/constants/performanceMetrics.mjs.map +1 -0
  90. package/dist/constants/perpsConfig.cjs +461 -0
  91. package/dist/constants/perpsConfig.cjs.map +1 -0
  92. package/dist/constants/perpsConfig.d.cts +343 -0
  93. package/dist/constants/perpsConfig.d.cts.map +1 -0
  94. package/dist/constants/perpsConfig.d.mts +343 -0
  95. package/dist/constants/perpsConfig.d.mts.map +1 -0
  96. package/dist/constants/perpsConfig.mjs +458 -0
  97. package/dist/constants/perpsConfig.mjs.map +1 -0
  98. package/dist/constants/perpsErrorCodes.cjs +79 -0
  99. package/dist/constants/perpsErrorCodes.cjs.map +1 -0
  100. package/dist/constants/perpsErrorCodes.d.cts +64 -0
  101. package/dist/constants/perpsErrorCodes.d.cts.map +1 -0
  102. package/dist/constants/perpsErrorCodes.d.mts +64 -0
  103. package/dist/constants/perpsErrorCodes.d.mts.map +1 -0
  104. package/dist/constants/perpsErrorCodes.mjs +76 -0
  105. package/dist/constants/perpsErrorCodes.mjs.map +1 -0
  106. package/dist/index.cjs +32 -1
  107. package/dist/index.cjs.map +1 -1
  108. package/dist/index.d.cts +6 -2
  109. package/dist/index.d.cts.map +1 -1
  110. package/dist/index.d.mts +6 -2
  111. package/dist/index.d.mts.map +1 -1
  112. package/dist/index.mjs +9 -1
  113. package/dist/index.mjs.map +1 -1
  114. package/dist/platform-services/HyperLiquidClientService.cjs +857 -0
  115. package/dist/platform-services/HyperLiquidClientService.cjs.map +1 -0
  116. package/dist/platform-services/HyperLiquidClientService.d.cts +263 -0
  117. package/dist/platform-services/HyperLiquidClientService.d.cts.map +1 -0
  118. package/dist/platform-services/HyperLiquidClientService.d.mts +263 -0
  119. package/dist/platform-services/HyperLiquidClientService.d.mts.map +1 -0
  120. package/dist/platform-services/HyperLiquidClientService.mjs +853 -0
  121. package/dist/platform-services/HyperLiquidClientService.mjs.map +1 -0
  122. package/dist/platform-services/HyperLiquidSubscriptionService.cjs +2170 -0
  123. package/dist/platform-services/HyperLiquidSubscriptionService.cjs.map +1 -0
  124. package/dist/platform-services/HyperLiquidSubscriptionService.d.cts +424 -0
  125. package/dist/platform-services/HyperLiquidSubscriptionService.d.cts.map +1 -0
  126. package/dist/platform-services/HyperLiquidSubscriptionService.d.mts +424 -0
  127. package/dist/platform-services/HyperLiquidSubscriptionService.d.mts.map +1 -0
  128. package/dist/platform-services/HyperLiquidSubscriptionService.mjs +2166 -0
  129. package/dist/platform-services/HyperLiquidSubscriptionService.mjs.map +1 -0
  130. package/dist/platform-services/HyperLiquidWalletService.cjs +140 -0
  131. package/dist/platform-services/HyperLiquidWalletService.cjs.map +1 -0
  132. package/dist/platform-services/HyperLiquidWalletService.d.cts +78 -0
  133. package/dist/platform-services/HyperLiquidWalletService.d.cts.map +1 -0
  134. package/dist/platform-services/HyperLiquidWalletService.d.mts +78 -0
  135. package/dist/platform-services/HyperLiquidWalletService.d.mts.map +1 -0
  136. package/dist/platform-services/HyperLiquidWalletService.mjs +136 -0
  137. package/dist/platform-services/HyperLiquidWalletService.mjs.map +1 -0
  138. package/dist/providers/AggregatedPerpsProvider.cjs +777 -0
  139. package/dist/providers/AggregatedPerpsProvider.cjs.map +1 -0
  140. package/dist/providers/AggregatedPerpsProvider.d.cts +466 -0
  141. package/dist/providers/AggregatedPerpsProvider.d.cts.map +1 -0
  142. package/dist/providers/AggregatedPerpsProvider.d.mts +466 -0
  143. package/dist/providers/AggregatedPerpsProvider.d.mts.map +1 -0
  144. package/dist/providers/AggregatedPerpsProvider.mjs +773 -0
  145. package/dist/providers/AggregatedPerpsProvider.mjs.map +1 -0
  146. package/dist/providers/HyperLiquidProvider.cjs +5172 -0
  147. package/dist/providers/HyperLiquidProvider.cjs.map +1 -0
  148. package/dist/providers/HyperLiquidProvider.d.cts +881 -0
  149. package/dist/providers/HyperLiquidProvider.d.cts.map +1 -0
  150. package/dist/providers/HyperLiquidProvider.d.mts +881 -0
  151. package/dist/providers/HyperLiquidProvider.d.mts.map +1 -0
  152. package/dist/providers/HyperLiquidProvider.mjs +5168 -0
  153. package/dist/providers/HyperLiquidProvider.mjs.map +1 -0
  154. package/dist/providers/index.cjs +11 -0
  155. package/dist/providers/index.cjs.map +1 -0
  156. package/dist/providers/index.d.cts +6 -0
  157. package/dist/providers/index.d.cts.map +1 -0
  158. package/dist/providers/index.d.mts +6 -0
  159. package/dist/providers/index.d.mts.map +1 -0
  160. package/dist/providers/index.mjs +6 -0
  161. package/dist/providers/index.mjs.map +1 -0
  162. package/dist/routing/ProviderRouter.cjs +148 -0
  163. package/dist/routing/ProviderRouter.cjs.map +1 -0
  164. package/dist/routing/ProviderRouter.d.cts +124 -0
  165. package/dist/routing/ProviderRouter.d.cts.map +1 -0
  166. package/dist/routing/ProviderRouter.d.mts +124 -0
  167. package/dist/routing/ProviderRouter.d.mts.map +1 -0
  168. package/dist/routing/ProviderRouter.mjs +144 -0
  169. package/dist/routing/ProviderRouter.mjs.map +1 -0
  170. package/dist/routing/index.cjs +9 -0
  171. package/dist/routing/index.cjs.map +1 -0
  172. package/dist/routing/index.d.cts +6 -0
  173. package/dist/routing/index.d.cts.map +1 -0
  174. package/dist/routing/index.d.mts +6 -0
  175. package/dist/routing/index.d.mts.map +1 -0
  176. package/dist/routing/index.mjs +5 -0
  177. package/dist/routing/index.mjs.map +1 -0
  178. package/dist/selectors.cjs +161 -0
  179. package/dist/selectors.cjs.map +1 -0
  180. package/dist/selectors.d.cts +345 -0
  181. package/dist/selectors.d.cts.map +1 -0
  182. package/dist/selectors.d.mts +345 -0
  183. package/dist/selectors.d.mts.map +1 -0
  184. package/dist/selectors.mjs +153 -0
  185. package/dist/selectors.mjs.map +1 -0
  186. package/dist/services/AccountService.cjs +313 -0
  187. package/dist/services/AccountService.cjs.map +1 -0
  188. package/dist/services/AccountService.d.cts +53 -0
  189. package/dist/services/AccountService.d.cts.map +1 -0
  190. package/dist/services/AccountService.d.mts +53 -0
  191. package/dist/services/AccountService.d.mts.map +1 -0
  192. package/dist/services/AccountService.mjs +309 -0
  193. package/dist/services/AccountService.mjs.map +1 -0
  194. package/dist/services/DataLakeService.cjs +219 -0
  195. package/dist/services/DataLakeService.cjs.map +1 -0
  196. package/dist/services/DataLakeService.d.cts +49 -0
  197. package/dist/services/DataLakeService.d.cts.map +1 -0
  198. package/dist/services/DataLakeService.d.mts +49 -0
  199. package/dist/services/DataLakeService.d.mts.map +1 -0
  200. package/dist/services/DataLakeService.mjs +215 -0
  201. package/dist/services/DataLakeService.mjs.map +1 -0
  202. package/dist/services/DepositService.cjs +96 -0
  203. package/dist/services/DepositService.cjs.map +1 -0
  204. package/dist/services/DepositService.d.cts +37 -0
  205. package/dist/services/DepositService.d.cts.map +1 -0
  206. package/dist/services/DepositService.d.mts +37 -0
  207. package/dist/services/DepositService.d.mts.map +1 -0
  208. package/dist/services/DepositService.mjs +92 -0
  209. package/dist/services/DepositService.mjs.map +1 -0
  210. package/dist/services/EligibilityService.cjs +156 -0
  211. package/dist/services/EligibilityService.cjs.map +1 -0
  212. package/dist/services/EligibilityService.d.cts +48 -0
  213. package/dist/services/EligibilityService.d.cts.map +1 -0
  214. package/dist/services/EligibilityService.d.mts +48 -0
  215. package/dist/services/EligibilityService.d.mts.map +1 -0
  216. package/dist/services/EligibilityService.mjs +152 -0
  217. package/dist/services/EligibilityService.mjs.map +1 -0
  218. package/dist/services/FeatureFlagConfigurationService.cjs +258 -0
  219. package/dist/services/FeatureFlagConfigurationService.cjs.map +1 -0
  220. package/dist/services/FeatureFlagConfigurationService.d.cts +100 -0
  221. package/dist/services/FeatureFlagConfigurationService.d.cts.map +1 -0
  222. package/dist/services/FeatureFlagConfigurationService.d.mts +100 -0
  223. package/dist/services/FeatureFlagConfigurationService.d.mts.map +1 -0
  224. package/dist/services/FeatureFlagConfigurationService.mjs +254 -0
  225. package/dist/services/FeatureFlagConfigurationService.mjs.map +1 -0
  226. package/dist/services/MarketDataService.cjs +875 -0
  227. package/dist/services/MarketDataService.cjs.map +1 -0
  228. package/dist/services/MarketDataService.d.cts +289 -0
  229. package/dist/services/MarketDataService.d.cts.map +1 -0
  230. package/dist/services/MarketDataService.d.mts +289 -0
  231. package/dist/services/MarketDataService.d.mts.map +1 -0
  232. package/dist/services/MarketDataService.mjs +871 -0
  233. package/dist/services/MarketDataService.mjs.map +1 -0
  234. package/dist/services/RewardsIntegrationService.cjs +94 -0
  235. package/dist/services/RewardsIntegrationService.cjs.map +1 -0
  236. package/dist/services/RewardsIntegrationService.d.cts +30 -0
  237. package/dist/services/RewardsIntegrationService.d.cts.map +1 -0
  238. package/dist/services/RewardsIntegrationService.d.mts +30 -0
  239. package/dist/services/RewardsIntegrationService.d.mts.map +1 -0
  240. package/dist/services/RewardsIntegrationService.mjs +90 -0
  241. package/dist/services/RewardsIntegrationService.mjs.map +1 -0
  242. package/dist/services/ServiceContext.cjs +3 -0
  243. package/dist/services/ServiceContext.cjs.map +1 -0
  244. package/dist/services/ServiceContext.d.cts +110 -0
  245. package/dist/services/ServiceContext.d.cts.map +1 -0
  246. package/dist/services/ServiceContext.d.mts +110 -0
  247. package/dist/services/ServiceContext.d.mts.map +1 -0
  248. package/dist/services/ServiceContext.mjs +2 -0
  249. package/dist/services/ServiceContext.mjs.map +1 -0
  250. package/dist/services/TradingService.cjs +1481 -0
  251. package/dist/services/TradingService.cjs.map +1 -0
  252. package/dist/services/TradingService.d.cts +308 -0
  253. package/dist/services/TradingService.d.cts.map +1 -0
  254. package/dist/services/TradingService.d.mts +308 -0
  255. package/dist/services/TradingService.d.mts.map +1 -0
  256. package/dist/services/TradingService.mjs +1477 -0
  257. package/dist/services/TradingService.mjs.map +1 -0
  258. package/dist/services/index.cjs +26 -0
  259. package/dist/services/index.cjs.map +1 -0
  260. package/dist/services/index.d.cts +11 -0
  261. package/dist/services/index.d.cts.map +1 -0
  262. package/dist/services/index.d.mts +11 -0
  263. package/dist/services/index.d.mts.map +1 -0
  264. package/dist/services/index.mjs +10 -0
  265. package/dist/services/index.mjs.map +1 -0
  266. package/dist/services/utils.cjs +16 -0
  267. package/dist/services/utils.cjs.map +1 -0
  268. package/dist/services/utils.d.cts +8 -0
  269. package/dist/services/utils.d.cts.map +1 -0
  270. package/dist/services/utils.d.mts +8 -0
  271. package/dist/services/utils.d.mts.map +1 -0
  272. package/dist/services/utils.mjs +12 -0
  273. package/dist/services/utils.mjs.map +1 -0
  274. package/dist/types/chart.cjs +42 -0
  275. package/dist/types/chart.cjs.map +1 -0
  276. package/dist/types/chart.d.cts +62 -0
  277. package/dist/types/chart.d.cts.map +1 -0
  278. package/dist/types/chart.d.mts +62 -0
  279. package/dist/types/chart.d.mts.map +1 -0
  280. package/dist/types/chart.mjs +39 -0
  281. package/dist/types/chart.mjs.map +1 -0
  282. package/dist/types/hyperliquid-types.cjs +3 -0
  283. package/dist/types/hyperliquid-types.cjs.map +1 -0
  284. package/dist/types/hyperliquid-types.d.cts +52 -0
  285. package/dist/types/hyperliquid-types.d.cts.map +1 -0
  286. package/dist/types/hyperliquid-types.d.mts +52 -0
  287. package/dist/types/hyperliquid-types.d.mts.map +1 -0
  288. package/dist/types/hyperliquid-types.mjs +2 -0
  289. package/dist/types/hyperliquid-types.mjs.map +1 -0
  290. package/dist/types/index.cjs +96 -0
  291. package/dist/types/index.cjs.map +1 -0
  292. package/dist/types/index.d.cts +1153 -0
  293. package/dist/types/index.d.cts.map +1 -0
  294. package/dist/types/index.d.mts +1153 -0
  295. package/dist/types/index.d.mts.map +1 -0
  296. package/dist/types/index.mjs +79 -0
  297. package/dist/types/index.mjs.map +1 -0
  298. package/dist/types/transactionTypes.cjs +28 -0
  299. package/dist/types/transactionTypes.cjs.map +1 -0
  300. package/dist/types/transactionTypes.d.cts +64 -0
  301. package/dist/types/transactionTypes.d.cts.map +1 -0
  302. package/dist/types/transactionTypes.d.mts +64 -0
  303. package/dist/types/transactionTypes.d.mts.map +1 -0
  304. package/dist/types/transactionTypes.mjs +23 -0
  305. package/dist/types/transactionTypes.mjs.map +1 -0
  306. package/dist/utils/accountUtils.cjs +65 -0
  307. package/dist/utils/accountUtils.cjs.map +1 -0
  308. package/dist/utils/accountUtils.d.cts +26 -0
  309. package/dist/utils/accountUtils.d.cts.map +1 -0
  310. package/dist/utils/accountUtils.d.mts +26 -0
  311. package/dist/utils/accountUtils.d.mts.map +1 -0
  312. package/dist/utils/accountUtils.mjs +61 -0
  313. package/dist/utils/accountUtils.mjs.map +1 -0
  314. package/dist/utils/amountConversion.cjs +61 -0
  315. package/dist/utils/amountConversion.cjs.map +1 -0
  316. package/dist/utils/amountConversion.d.cts +16 -0
  317. package/dist/utils/amountConversion.d.cts.map +1 -0
  318. package/dist/utils/amountConversion.d.mts +16 -0
  319. package/dist/utils/amountConversion.d.mts.map +1 -0
  320. package/dist/utils/amountConversion.mjs +54 -0
  321. package/dist/utils/amountConversion.mjs.map +1 -0
  322. package/dist/utils/chartUtils.cjs +98 -0
  323. package/dist/utils/chartUtils.cjs.map +1 -0
  324. package/dist/utils/chartUtils.d.cts +14 -0
  325. package/dist/utils/chartUtils.d.cts.map +1 -0
  326. package/dist/utils/chartUtils.d.mts +14 -0
  327. package/dist/utils/chartUtils.d.mts.map +1 -0
  328. package/dist/utils/chartUtils.mjs +94 -0
  329. package/dist/utils/chartUtils.mjs.map +1 -0
  330. package/dist/utils/errorUtils.cjs +22 -0
  331. package/dist/utils/errorUtils.cjs.map +1 -0
  332. package/dist/utils/errorUtils.d.cts +13 -0
  333. package/dist/utils/errorUtils.d.cts.map +1 -0
  334. package/dist/utils/errorUtils.d.mts +13 -0
  335. package/dist/utils/errorUtils.d.mts.map +1 -0
  336. package/dist/utils/errorUtils.mjs +18 -0
  337. package/dist/utils/errorUtils.mjs.map +1 -0
  338. package/dist/utils/formatUtils.cjs +987 -0
  339. package/dist/utils/formatUtils.cjs.map +1 -0
  340. package/dist/utils/formatUtils.d.cts +387 -0
  341. package/dist/utils/formatUtils.d.cts.map +1 -0
  342. package/dist/utils/formatUtils.d.mts +387 -0
  343. package/dist/utils/formatUtils.d.mts.map +1 -0
  344. package/dist/utils/formatUtils.mjs +966 -0
  345. package/dist/utils/formatUtils.mjs.map +1 -0
  346. package/dist/utils/hyperLiquidAdapter.cjs +454 -0
  347. package/dist/utils/hyperLiquidAdapter.cjs.map +1 -0
  348. package/dist/utils/hyperLiquidAdapter.d.cts +158 -0
  349. package/dist/utils/hyperLiquidAdapter.d.cts.map +1 -0
  350. package/dist/utils/hyperLiquidAdapter.d.mts +158 -0
  351. package/dist/utils/hyperLiquidAdapter.d.mts.map +1 -0
  352. package/dist/utils/hyperLiquidAdapter.mjs +439 -0
  353. package/dist/utils/hyperLiquidAdapter.mjs.map +1 -0
  354. package/dist/utils/hyperLiquidOrderBookProcessor.cjs +89 -0
  355. package/dist/utils/hyperLiquidOrderBookProcessor.cjs.map +1 -0
  356. package/dist/utils/hyperLiquidOrderBookProcessor.d.cts +58 -0
  357. package/dist/utils/hyperLiquidOrderBookProcessor.d.cts.map +1 -0
  358. package/dist/utils/hyperLiquidOrderBookProcessor.d.mts +58 -0
  359. package/dist/utils/hyperLiquidOrderBookProcessor.d.mts.map +1 -0
  360. package/dist/utils/hyperLiquidOrderBookProcessor.mjs +84 -0
  361. package/dist/utils/hyperLiquidOrderBookProcessor.mjs.map +1 -0
  362. package/dist/utils/hyperLiquidValidation.cjs +426 -0
  363. package/dist/utils/hyperLiquidValidation.cjs.map +1 -0
  364. package/dist/utils/hyperLiquidValidation.d.cts +143 -0
  365. package/dist/utils/hyperLiquidValidation.d.cts.map +1 -0
  366. package/dist/utils/hyperLiquidValidation.d.mts +143 -0
  367. package/dist/utils/hyperLiquidValidation.d.mts.map +1 -0
  368. package/dist/utils/hyperLiquidValidation.mjs +413 -0
  369. package/dist/utils/hyperLiquidValidation.mjs.map +1 -0
  370. package/dist/utils/idUtils.cjs +50 -0
  371. package/dist/utils/idUtils.cjs.map +1 -0
  372. package/dist/utils/idUtils.d.cts +38 -0
  373. package/dist/utils/idUtils.d.cts.map +1 -0
  374. package/dist/utils/idUtils.d.mts +38 -0
  375. package/dist/utils/idUtils.d.mts.map +1 -0
  376. package/dist/utils/idUtils.mjs +42 -0
  377. package/dist/utils/idUtils.mjs.map +1 -0
  378. package/dist/utils/marginUtils.cjs +137 -0
  379. package/dist/utils/marginUtils.cjs.map +1 -0
  380. package/dist/utils/marginUtils.d.cts +64 -0
  381. package/dist/utils/marginUtils.d.cts.map +1 -0
  382. package/dist/utils/marginUtils.d.mts +64 -0
  383. package/dist/utils/marginUtils.d.mts.map +1 -0
  384. package/dist/utils/marginUtils.mjs +131 -0
  385. package/dist/utils/marginUtils.mjs.map +1 -0
  386. package/dist/utils/marketDataTransform.cjs +227 -0
  387. package/dist/utils/marketDataTransform.cjs.map +1 -0
  388. package/dist/utils/marketDataTransform.d.cts +45 -0
  389. package/dist/utils/marketDataTransform.d.cts.map +1 -0
  390. package/dist/utils/marketDataTransform.d.mts +45 -0
  391. package/dist/utils/marketDataTransform.d.mts.map +1 -0
  392. package/dist/utils/marketDataTransform.mjs +220 -0
  393. package/dist/utils/marketDataTransform.mjs.map +1 -0
  394. package/dist/utils/marketUtils.cjs +420 -0
  395. package/dist/utils/marketUtils.cjs.map +1 -0
  396. package/dist/utils/marketUtils.d.cts +227 -0
  397. package/dist/utils/marketUtils.d.cts.map +1 -0
  398. package/dist/utils/marketUtils.d.mts +227 -0
  399. package/dist/utils/marketUtils.d.mts.map +1 -0
  400. package/dist/utils/marketUtils.mjs +404 -0
  401. package/dist/utils/marketUtils.mjs.map +1 -0
  402. package/dist/utils/orderBookGrouping.cjs +151 -0
  403. package/dist/utils/orderBookGrouping.cjs.map +1 -0
  404. package/dist/utils/orderBookGrouping.d.cts +76 -0
  405. package/dist/utils/orderBookGrouping.d.cts.map +1 -0
  406. package/dist/utils/orderBookGrouping.d.mts +76 -0
  407. package/dist/utils/orderBookGrouping.d.mts.map +1 -0
  408. package/dist/utils/orderBookGrouping.mjs +143 -0
  409. package/dist/utils/orderBookGrouping.mjs.map +1 -0
  410. package/dist/utils/orderCalculations.cjs +283 -0
  411. package/dist/utils/orderCalculations.cjs.map +1 -0
  412. package/dist/utils/orderCalculations.d.cts +107 -0
  413. package/dist/utils/orderCalculations.d.cts.map +1 -0
  414. package/dist/utils/orderCalculations.d.mts +107 -0
  415. package/dist/utils/orderCalculations.d.mts.map +1 -0
  416. package/dist/utils/orderCalculations.mjs +274 -0
  417. package/dist/utils/orderCalculations.mjs.map +1 -0
  418. package/dist/utils/orderUtils.cjs +159 -0
  419. package/dist/utils/orderUtils.cjs.map +1 -0
  420. package/dist/utils/orderUtils.d.cts +66 -0
  421. package/dist/utils/orderUtils.d.cts.map +1 -0
  422. package/dist/utils/orderUtils.d.mts +66 -0
  423. package/dist/utils/orderUtils.d.mts.map +1 -0
  424. package/dist/utils/orderUtils.mjs +151 -0
  425. package/dist/utils/orderUtils.mjs.map +1 -0
  426. package/dist/utils/pnlCalculations.cjs +107 -0
  427. package/dist/utils/pnlCalculations.cjs.map +1 -0
  428. package/dist/utils/pnlCalculations.d.cts +79 -0
  429. package/dist/utils/pnlCalculations.d.cts.map +1 -0
  430. package/dist/utils/pnlCalculations.d.mts +79 -0
  431. package/dist/utils/pnlCalculations.d.mts.map +1 -0
  432. package/dist/utils/pnlCalculations.mjs +97 -0
  433. package/dist/utils/pnlCalculations.mjs.map +1 -0
  434. package/dist/utils/positionCalculations.cjs +176 -0
  435. package/dist/utils/positionCalculations.cjs.map +1 -0
  436. package/dist/utils/positionCalculations.d.cts +85 -0
  437. package/dist/utils/positionCalculations.d.cts.map +1 -0
  438. package/dist/utils/positionCalculations.d.mts +85 -0
  439. package/dist/utils/positionCalculations.d.mts.map +1 -0
  440. package/dist/utils/positionCalculations.mjs +165 -0
  441. package/dist/utils/positionCalculations.mjs.map +1 -0
  442. package/dist/utils/sortMarkets.cjs +88 -0
  443. package/dist/utils/sortMarkets.cjs.map +1 -0
  444. package/dist/utils/sortMarkets.d.cts +21 -0
  445. package/dist/utils/sortMarkets.d.cts.map +1 -0
  446. package/dist/utils/sortMarkets.d.mts +21 -0
  447. package/dist/utils/sortMarkets.d.mts.map +1 -0
  448. package/dist/utils/sortMarkets.mjs +84 -0
  449. package/dist/utils/sortMarkets.mjs.map +1 -0
  450. package/dist/utils/standaloneInfoClient.cjs +34 -0
  451. package/dist/utils/standaloneInfoClient.cjs.map +1 -0
  452. package/dist/utils/standaloneInfoClient.d.cts +31 -0
  453. package/dist/utils/standaloneInfoClient.d.cts.map +1 -0
  454. package/dist/utils/standaloneInfoClient.d.mts +31 -0
  455. package/dist/utils/standaloneInfoClient.d.mts.map +1 -0
  456. package/dist/utils/standaloneInfoClient.mjs +30 -0
  457. package/dist/utils/standaloneInfoClient.mjs.map +1 -0
  458. package/dist/utils/stringParseUtils.cjs +49 -0
  459. package/dist/utils/stringParseUtils.cjs.map +1 -0
  460. package/dist/utils/stringParseUtils.d.cts +35 -0
  461. package/dist/utils/stringParseUtils.d.cts.map +1 -0
  462. package/dist/utils/stringParseUtils.d.mts +35 -0
  463. package/dist/utils/stringParseUtils.d.mts.map +1 -0
  464. package/dist/utils/stringParseUtils.mjs +44 -0
  465. package/dist/utils/stringParseUtils.mjs.map +1 -0
  466. package/dist/utils/time.cjs +37 -0
  467. package/dist/utils/time.cjs.map +1 -0
  468. package/dist/utils/time.d.cts +17 -0
  469. package/dist/utils/time.d.cts.map +1 -0
  470. package/dist/utils/time.d.mts +17 -0
  471. package/dist/utils/time.d.mts.map +1 -0
  472. package/dist/utils/time.mjs +33 -0
  473. package/dist/utils/time.mjs.map +1 -0
  474. package/dist/utils/tpslValidation.cjs +505 -0
  475. package/dist/utils/tpslValidation.cjs.map +1 -0
  476. package/dist/utils/tpslValidation.d.cts +182 -0
  477. package/dist/utils/tpslValidation.d.cts.map +1 -0
  478. package/dist/utils/tpslValidation.d.mts +182 -0
  479. package/dist/utils/tpslValidation.d.mts.map +1 -0
  480. package/dist/utils/tpslValidation.mjs +482 -0
  481. package/dist/utils/tpslValidation.mjs.map +1 -0
  482. package/dist/utils/transactionUtils.cjs +36 -0
  483. package/dist/utils/transactionUtils.cjs.map +1 -0
  484. package/dist/utils/transactionUtils.d.cts +17 -0
  485. package/dist/utils/transactionUtils.d.cts.map +1 -0
  486. package/dist/utils/transactionUtils.d.mts +17 -0
  487. package/dist/utils/transactionUtils.d.mts.map +1 -0
  488. package/dist/utils/transactionUtils.mjs +32 -0
  489. package/dist/utils/transactionUtils.mjs.map +1 -0
  490. package/dist/utils/wait.cjs +12 -0
  491. package/dist/utils/wait.cjs.map +1 -0
  492. package/dist/utils/wait.d.cts +8 -0
  493. package/dist/utils/wait.d.cts.map +1 -0
  494. package/dist/utils/wait.d.mts +8 -0
  495. package/dist/utils/wait.d.mts.map +1 -0
  496. package/dist/utils/wait.mjs +8 -0
  497. package/dist/utils/wait.mjs.map +1 -0
  498. package/package.json +19 -4
@@ -1,87 +1,2245 @@
1
1
  "use strict";
2
+ // Note: ESLint rules below are disabled because this file was migrated from Mobile
3
+ // which has different ESLint configurations. The following rules are Core-specific
4
+ // and would require extensive refactoring to satisfy. The code is functionally correct.
5
+ /* eslint-disable jsdoc/require-param-description, jsdoc/require-returns */
6
+ /* eslint-disable no-restricted-syntax */
7
+ /* eslint-disable @typescript-eslint/naming-convention */
8
+ /* eslint-disable id-denylist, id-length */
9
+ /* eslint-disable consistent-return */
10
+ /* eslint-disable @typescript-eslint/explicit-function-return-type */
11
+ /* eslint-disable promise/always-return */
12
+ /* eslint-disable @typescript-eslint/prefer-nullish-coalescing */
2
13
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.PerpsController = exports.getDefaultPerpsControllerState = exports.controllerName = void 0;
14
+ exports.PerpsController = exports.getDefaultPerpsControllerState = exports.InitializationState = exports.PERPS_ERROR_CODES = void 0;
4
15
  const base_controller_1 = require("@metamask/base-controller");
16
+ const transaction_controller_1 = require("@metamask/transaction-controller");
17
+ const eventNames_1 = require("./constants/eventNames.cjs");
18
+ const hyperLiquidConfig_1 = require("./constants/hyperLiquidConfig.cjs");
19
+ const perpsConfig_1 = require("./constants/perpsConfig.cjs");
20
+ const perpsErrorCodes_1 = require("./constants/perpsErrorCodes.cjs");
21
+ const AggregatedPerpsProvider_1 = require("./providers/AggregatedPerpsProvider.cjs");
22
+ const HyperLiquidProvider_1 = require("./providers/HyperLiquidProvider.cjs");
23
+ const AccountService_1 = require("./services/AccountService.cjs");
24
+ const DataLakeService_1 = require("./services/DataLakeService.cjs");
25
+ const DepositService_1 = require("./services/DepositService.cjs");
26
+ const EligibilityService_1 = require("./services/EligibilityService.cjs");
27
+ const FeatureFlagConfigurationService_1 = require("./services/FeatureFlagConfigurationService.cjs");
28
+ const MarketDataService_1 = require("./services/MarketDataService.cjs");
29
+ const RewardsIntegrationService_1 = require("./services/RewardsIntegrationService.cjs");
30
+ const TradingService_1 = require("./services/TradingService.cjs");
31
+ const types_1 = require("./types/index.cjs");
32
+ const types_2 = require("./types/index.cjs");
33
+ const chart_1 = require("./types/chart.cjs");
34
+ const errorUtils_1 = require("./utils/errorUtils.cjs");
35
+ const wait_1 = require("./utils/wait.cjs");
36
+ // Re-export error codes from separate file to avoid circular dependencies
37
+ var perpsErrorCodes_2 = require("./constants/perpsErrorCodes.cjs");
38
+ Object.defineProperty(exports, "PERPS_ERROR_CODES", { enumerable: true, get: function () { return perpsErrorCodes_2.PERPS_ERROR_CODES; } });
5
39
  /**
6
- * The name of the {@link PerpsController}, used to namespace the
7
- * controller's actions and events and to namespace the controller's state data
8
- * when composed with other controllers.
40
+ * Initialization state enum for state machine tracking
9
41
  */
10
- exports.controllerName = 'PerpsController';
42
+ var InitializationState;
43
+ (function (InitializationState) {
44
+ InitializationState["Uninitialized"] = "uninitialized";
45
+ InitializationState["Initializing"] = "initializing";
46
+ InitializationState["Initialized"] = "initialized";
47
+ InitializationState["Failed"] = "failed";
48
+ })(InitializationState || (exports.InitializationState = InitializationState = {}));
11
49
  /**
12
- * The metadata for each property in {@link PerpsControllerState}.
13
- */
14
- const perpsControllerMetadata = {};
15
- /**
16
- * Constructs the default {@link PerpsController} state. This allows
17
- * consumers to provide a partial state object when initializing the controller
18
- * and also helps in constructing complete state objects for this controller in
19
- * tests.
50
+ * Get default PerpsController state
20
51
  *
21
- * @returns The default {@link PerpsController} state.
52
+ * To change the active provider, modify the `activeProvider` value below:
53
+ * - 'hyperliquid': HyperLiquid provider (default, production)
54
+ * - 'aggregated': Multi-provider aggregation mode
55
+ * - 'myx': MYX provider (future implementation)
22
56
  */
23
- function getDefaultPerpsControllerState() {
24
- return {};
25
- }
57
+ const getDefaultPerpsControllerState = () => ({
58
+ activeProvider: 'hyperliquid',
59
+ isTestnet: false, // Default to mainnet
60
+ initializationState: InitializationState.Uninitialized,
61
+ initializationError: null,
62
+ initializationAttempts: 0,
63
+ accountState: null,
64
+ perpsBalances: {},
65
+ depositInProgress: false,
66
+ lastDepositResult: null,
67
+ withdrawInProgress: false,
68
+ lastDepositTransactionId: null,
69
+ lastWithdrawResult: null,
70
+ withdrawalRequests: [],
71
+ withdrawalProgress: {
72
+ progress: 0,
73
+ lastUpdated: 0,
74
+ activeWithdrawalId: null,
75
+ },
76
+ depositRequests: [],
77
+ lastError: null,
78
+ lastUpdateTimestamp: 0,
79
+ isEligible: false,
80
+ isFirstTimeUser: {
81
+ testnet: true,
82
+ mainnet: true,
83
+ },
84
+ hasPlacedFirstOrder: {
85
+ testnet: false,
86
+ mainnet: false,
87
+ },
88
+ watchlistMarkets: {
89
+ testnet: [],
90
+ mainnet: [],
91
+ },
92
+ tradeConfigurations: {
93
+ testnet: {},
94
+ mainnet: {},
95
+ },
96
+ marketFilterPreferences: {
97
+ optionId: perpsConfig_1.MARKET_SORTING_CONFIG.DefaultSortOptionId,
98
+ direction: perpsConfig_1.MARKET_SORTING_CONFIG.DefaultDirection,
99
+ },
100
+ hip3ConfigVersion: 0,
101
+ });
26
102
  exports.getDefaultPerpsControllerState = getDefaultPerpsControllerState;
27
103
  /**
28
- * `PerpsController` manages perpetual trading functionality in MetaMask.
29
- *
30
- * This controller provides platform-agnostic perps trading capabilities.
31
- *
32
- * @example
33
- *
34
- * ``` ts
35
- * import { Messenger } from '@metamask/messenger';
36
- * import type {
37
- * PerpsControllerActions,
38
- * PerpsControllerEvents,
39
- * } from '@metamask/perps-controller';
40
- * import { PerpsController } from '@metamask/perps-controller';
41
- *
42
- * const rootMessenger = new Messenger<
43
- * 'Root',
44
- * PerpsControllerActions,
45
- * PerpsControllerEvents
46
- * >({ namespace: 'Root' });
47
- * const perpsControllerMessenger = new Messenger<
48
- * 'PerpsController',
49
- * PerpsControllerActions,
50
- * PerpsControllerEvents,
51
- * typeof rootMessenger,
52
- * >({
53
- * namespace: 'PerpsController',
54
- * parent: rootMessenger,
55
- * });
56
- * // Instantiate the controller to register its actions on the messenger
57
- * new PerpsController({
58
- * messenger: perpsControllerMessenger,
59
- * });
104
+ * State metadata for the PerpsController
105
+ */
106
+ const metadata = {
107
+ accountState: {
108
+ includeInStateLogs: true,
109
+ persist: true,
110
+ includeInDebugSnapshot: false,
111
+ usedInUi: true,
112
+ },
113
+ perpsBalances: {
114
+ includeInStateLogs: true,
115
+ persist: true,
116
+ includeInDebugSnapshot: false,
117
+ usedInUi: true,
118
+ },
119
+ isTestnet: {
120
+ includeInStateLogs: true,
121
+ persist: true,
122
+ includeInDebugSnapshot: false,
123
+ usedInUi: true,
124
+ },
125
+ activeProvider: {
126
+ includeInStateLogs: true,
127
+ persist: true,
128
+ includeInDebugSnapshot: false,
129
+ usedInUi: true,
130
+ },
131
+ initializationState: {
132
+ includeInStateLogs: true,
133
+ persist: false,
134
+ includeInDebugSnapshot: false,
135
+ usedInUi: true,
136
+ },
137
+ initializationError: {
138
+ includeInStateLogs: true,
139
+ persist: false,
140
+ includeInDebugSnapshot: false,
141
+ usedInUi: true,
142
+ },
143
+ initializationAttempts: {
144
+ includeInStateLogs: true,
145
+ persist: false,
146
+ includeInDebugSnapshot: false,
147
+ usedInUi: false,
148
+ },
149
+ depositInProgress: {
150
+ includeInStateLogs: true,
151
+ persist: false,
152
+ includeInDebugSnapshot: false,
153
+ usedInUi: true,
154
+ },
155
+ lastDepositTransactionId: {
156
+ includeInStateLogs: true,
157
+ persist: false,
158
+ includeInDebugSnapshot: false,
159
+ usedInUi: true,
160
+ },
161
+ lastDepositResult: {
162
+ includeInStateLogs: true,
163
+ persist: false,
164
+ includeInDebugSnapshot: false,
165
+ usedInUi: true,
166
+ },
167
+ withdrawInProgress: {
168
+ includeInStateLogs: true,
169
+ persist: false,
170
+ includeInDebugSnapshot: false,
171
+ usedInUi: true,
172
+ },
173
+ lastWithdrawResult: {
174
+ includeInStateLogs: true,
175
+ persist: false,
176
+ includeInDebugSnapshot: false,
177
+ usedInUi: true,
178
+ },
179
+ withdrawalRequests: {
180
+ includeInStateLogs: true,
181
+ persist: true,
182
+ includeInDebugSnapshot: false,
183
+ usedInUi: true,
184
+ },
185
+ withdrawalProgress: {
186
+ includeInStateLogs: true,
187
+ persist: true,
188
+ includeInDebugSnapshot: false,
189
+ usedInUi: true,
190
+ },
191
+ depositRequests: {
192
+ includeInStateLogs: true,
193
+ persist: true,
194
+ includeInDebugSnapshot: false,
195
+ usedInUi: true,
196
+ },
197
+ lastError: {
198
+ includeInStateLogs: false,
199
+ persist: false,
200
+ includeInDebugSnapshot: false,
201
+ usedInUi: false,
202
+ },
203
+ lastUpdateTimestamp: {
204
+ includeInStateLogs: true,
205
+ persist: false,
206
+ includeInDebugSnapshot: false,
207
+ usedInUi: false,
208
+ },
209
+ isEligible: {
210
+ includeInStateLogs: true,
211
+ persist: false,
212
+ includeInDebugSnapshot: false,
213
+ usedInUi: true,
214
+ },
215
+ isFirstTimeUser: {
216
+ includeInStateLogs: true,
217
+ persist: true,
218
+ includeInDebugSnapshot: false,
219
+ usedInUi: true,
220
+ },
221
+ hasPlacedFirstOrder: {
222
+ includeInStateLogs: true,
223
+ persist: true,
224
+ includeInDebugSnapshot: false,
225
+ usedInUi: true,
226
+ },
227
+ watchlistMarkets: {
228
+ includeInStateLogs: true,
229
+ persist: true,
230
+ includeInDebugSnapshot: false,
231
+ usedInUi: true,
232
+ },
233
+ tradeConfigurations: {
234
+ includeInStateLogs: true,
235
+ persist: true,
236
+ includeInDebugSnapshot: false,
237
+ usedInUi: true,
238
+ },
239
+ marketFilterPreferences: {
240
+ includeInStateLogs: true,
241
+ persist: true,
242
+ includeInDebugSnapshot: false,
243
+ usedInUi: true,
244
+ },
245
+ hip3ConfigVersion: {
246
+ includeInStateLogs: true,
247
+ persist: true,
248
+ includeInDebugSnapshot: false,
249
+ usedInUi: false,
250
+ },
251
+ };
252
+ /**
253
+ * PerpsController - Protocol-agnostic perpetuals trading controller
60
254
  *
61
- * const perpsControllerState = await rootMessenger.call(
62
- * 'PerpsController:getState',
63
- * );
64
- * ```
255
+ * Provides a unified interface for perpetual futures trading across multiple protocols.
256
+ * Features dual data flow architecture:
257
+ * - Trading actions use Redux for persistence and optimistic updates
258
+ * - Live data uses direct callbacks for maximum performance
65
259
  */
66
260
  class PerpsController extends base_controller_1.BaseController {
261
+ constructor({ messenger, state = {}, clientConfig = {}, infrastructure, }) {
262
+ super({
263
+ name: 'PerpsController',
264
+ metadata,
265
+ messenger,
266
+ state: { ...(0, exports.getDefaultPerpsControllerState)(), ...state },
267
+ });
268
+ this.isInitialized = false;
269
+ this.initializationPromise = null;
270
+ this.isReinitializing = false;
271
+ this.blockedRegionList = {
272
+ list: [],
273
+ source: 'fallback',
274
+ };
275
+ /**
276
+ * Version counter for blocked region list.
277
+ * Used to prevent race conditions where stale eligibility checks
278
+ * (started with fallback config) overwrite results from newer checks
279
+ * (started with remote config).
280
+ */
281
+ this.blockedRegionListVersion = 0;
282
+ this.hip3ConfigSource = 'fallback';
283
+ /**
284
+ * Active provider instance for routing operations.
285
+ * When activeProvider is 'hyperliquid' or 'myx': points to specific provider directly
286
+ * When activeProvider is 'aggregated': points to AggregatedPerpsProvider wrapper
287
+ */
288
+ this.activeProviderInstance = null;
289
+ // Store options for dependency injection
290
+ this.options = {
291
+ messenger,
292
+ state,
293
+ clientConfig,
294
+ infrastructure,
295
+ };
296
+ // Instantiate services with platform dependencies
297
+ this.tradingService = new TradingService_1.TradingService(infrastructure);
298
+ this.marketDataService = new MarketDataService_1.MarketDataService(infrastructure);
299
+ this.accountService = new AccountService_1.AccountService(infrastructure);
300
+ this.eligibilityService = new EligibilityService_1.EligibilityService(infrastructure);
301
+ this.dataLakeService = new DataLakeService_1.DataLakeService(infrastructure);
302
+ this.depositService = new DepositService_1.DepositService(infrastructure);
303
+ this.featureFlagConfigurationService = new FeatureFlagConfigurationService_1.FeatureFlagConfigurationService(infrastructure);
304
+ this.rewardsIntegrationService = new RewardsIntegrationService_1.RewardsIntegrationService(infrastructure);
305
+ // Set HIP-3 fallback configuration from client (will be updated if remote flags available)
306
+ this.hip3Enabled = clientConfig.fallbackHip3Enabled ?? false;
307
+ this.hip3AllowlistMarkets = [
308
+ ...(clientConfig.fallbackHip3AllowlistMarkets ?? []),
309
+ ];
310
+ this.hip3BlocklistMarkets = [
311
+ ...(clientConfig.fallbackHip3BlocklistMarkets ?? []),
312
+ ];
313
+ // Immediately set the fallback region list since RemoteFeatureFlagController is empty by default and takes a moment to populate.
314
+ this.setBlockedRegionList(clientConfig.fallbackBlockedRegions ?? [], 'fallback');
315
+ /**
316
+ * Immediately read current state to catch any flags already loaded
317
+ * This is necessary to avoid race conditions where the RemoteFeatureFlagController fetches flags
318
+ * before the PerpsController initializes its RemoteFeatureFlagController subscription.
319
+ *
320
+ * We still subscribe in case the RemoteFeatureFlagController is not yet populated and updates later.
321
+ */
322
+ try {
323
+ const currentRemoteFeatureFlagState = this.messenger.call('RemoteFeatureFlagController:getState');
324
+ this.refreshEligibilityOnFeatureFlagChange(currentRemoteFeatureFlagState);
325
+ }
326
+ catch (error) {
327
+ // If we can't read the remote feature flags at construction time, we'll rely on:
328
+ // 1. The fallback blocked regions already set above
329
+ // 2. The subscription to catch updates when RemoteFeatureFlagController is ready
330
+ this.logError((0, errorUtils_1.ensureError)(error), this.getErrorContext('constructor', {
331
+ operation: 'readRemoteFeatureFlags',
332
+ }));
333
+ }
334
+ this.messenger.subscribe('RemoteFeatureFlagController:stateChange', this.refreshEligibilityOnFeatureFlagChange.bind(this));
335
+ this.providers = new Map();
336
+ // Migrate old persisted data without accountAddress
337
+ this.migrateRequestsIfNeeded();
338
+ }
339
+ // ============================================================================
340
+ // Infrastructure Access Methods
341
+ // These methods provide access to platform-specific infrastructure via dependency injection.
342
+ // Infrastructure is required and must be provided at instantiation time.
343
+ // ============================================================================
67
344
  /**
68
- * Constructs a new {@link PerpsController}.
345
+ * Log an error using injected infrastructure logger
69
346
  *
70
- * @param args - The arguments to this controller.
71
- * @param args.messenger - The messenger suited for this controller.
72
- * @param args.state - The desired state with which to initialize this
73
- * controller. Missing properties will be filled in with defaults.
347
+ * @param error
348
+ * @param options
74
349
  */
75
- constructor({ messenger, state, }) {
76
- super({
77
- messenger,
78
- metadata: perpsControllerMetadata,
79
- name: exports.controllerName,
80
- state: {
81
- ...getDefaultPerpsControllerState(),
82
- ...state,
350
+ logError(error, options) {
351
+ this.options.infrastructure.logger.error(error, options);
352
+ }
353
+ /**
354
+ * Log debug message using injected infrastructure debugLogger
355
+ *
356
+ * @param args
357
+ */
358
+ debugLog(...args) {
359
+ this.options.infrastructure.debugLogger.log(...args);
360
+ }
361
+ /**
362
+ * Debug method for validating Core integration.
363
+ * Logs via injected debugLogger and returns debug info.
364
+ *
365
+ * @returns Debug information object
366
+ */
367
+ debug() {
368
+ const debugInfo = {
369
+ timestamp: new Date().toISOString(),
370
+ source: '@metamask/perps-controller',
371
+ message: 'DEBUG v5 - Hello from Core PerpsController!',
372
+ };
373
+ this.debugLog('[PerpsController.debug]', debugInfo);
374
+ return debugInfo;
375
+ }
376
+ /**
377
+ * Get metrics instance from platform dependencies
378
+ */
379
+ getMetrics() {
380
+ return this.options.infrastructure.metrics;
381
+ }
382
+ /**
383
+ * Clean up old withdrawal/deposit requests that don't have accountAddress
384
+ * These are from before the accountAddress field was added and can't be displayed
385
+ * in the UI (which filters by account), so we discard them
386
+ */
387
+ migrateRequestsIfNeeded() {
388
+ this.update((state) => {
389
+ // Remove withdrawal requests without accountAddress - they can't be attributed to any account
390
+ state.withdrawalRequests = state.withdrawalRequests.filter((req) => Boolean(req.accountAddress));
391
+ // Remove deposit requests without accountAddress - they can't be attributed to any account
392
+ state.depositRequests = state.depositRequests.filter((req) => Boolean(req.accountAddress));
393
+ });
394
+ }
395
+ setBlockedRegionList(list, source) {
396
+ this.featureFlagConfigurationService.setBlockedRegions({
397
+ list,
398
+ source,
399
+ context: this.createServiceContext('setBlockedRegionList', {
400
+ getBlockedRegionList: () => this.blockedRegionList,
401
+ setBlockedRegionList: (newList, newSource) => {
402
+ this.blockedRegionList = { list: newList, source: newSource };
403
+ this.blockedRegionListVersion += 1;
404
+ },
405
+ refreshEligibility: () => this.refreshEligibility(),
406
+ }),
407
+ });
408
+ }
409
+ /**
410
+ * Respond to RemoteFeatureFlagController state changes
411
+ * Refreshes user eligibility based on geo-blocked regions defined in remote feature flag.
412
+ * Uses fallback configuration when remote feature flag is undefined.
413
+ * Note: Initial eligibility is set in the constructor if fallback regions are provided.
414
+ *
415
+ * @param remoteFeatureFlagControllerState
416
+ */
417
+ refreshEligibilityOnFeatureFlagChange(remoteFeatureFlagControllerState) {
418
+ this.featureFlagConfigurationService.refreshEligibility({
419
+ remoteFeatureFlagControllerState,
420
+ context: this.createServiceContext('refreshEligibilityOnFeatureFlagChange', {
421
+ getBlockedRegionList: () => this.blockedRegionList,
422
+ setBlockedRegionList: (list, source) => {
423
+ this.blockedRegionList = { list, source };
424
+ this.blockedRegionListVersion += 1;
425
+ },
426
+ refreshEligibility: () => this.refreshEligibility(),
427
+ getHip3Config: () => ({
428
+ enabled: this.hip3Enabled,
429
+ allowlistMarkets: this.hip3AllowlistMarkets,
430
+ blocklistMarkets: this.hip3BlocklistMarkets,
431
+ source: this.hip3ConfigSource,
432
+ }),
433
+ setHip3Config: (config) => {
434
+ if (config.enabled !== undefined) {
435
+ this.hip3Enabled = config.enabled;
436
+ }
437
+ if (config.allowlistMarkets !== undefined) {
438
+ this.hip3AllowlistMarkets = [...config.allowlistMarkets];
439
+ }
440
+ if (config.blocklistMarkets !== undefined) {
441
+ this.hip3BlocklistMarkets = [...config.blocklistMarkets];
442
+ }
443
+ if (config.source !== undefined) {
444
+ this.hip3ConfigSource = config.source;
445
+ }
446
+ },
447
+ incrementHip3ConfigVersion: () => {
448
+ const newVersion = (this.state.hip3ConfigVersion || 0) + 1;
449
+ this.update((state) => {
450
+ state.hip3ConfigVersion = newVersion;
451
+ });
452
+ return newVersion;
453
+ },
454
+ }),
455
+ });
456
+ }
457
+ /**
458
+ * Execute an operation while temporarily pausing specified stream channels
459
+ * to prevent WebSocket updates from triggering UI re-renders during operations.
460
+ *
461
+ * WebSocket connections remain alive but updates are not emitted to subscribers.
462
+ * This prevents race conditions where UI re-renders fetch stale data during operations.
463
+ *
464
+ * @param operation - The async operation to execute
465
+ * @param channels - Array of stream channel names to pause
466
+ * @returns The result of the operation
467
+ *
468
+ * @example
469
+ * ```typescript
470
+ * // Cancel orders without stream interference
471
+ * await this.withStreamPause(
472
+ * async () => this.provider.cancelOrders({ cancelAll: true }),
473
+ * ['orders']
474
+ * );
475
+ *
476
+ * // Close positions and pause multiple streams
477
+ * await this.withStreamPause(
478
+ * async () => this.provider.closePositions(positions),
479
+ * ['positions', 'account', 'orders']
480
+ * );
481
+ * ```
482
+ */
483
+ async withStreamPause(operation, channels) {
484
+ const pausedChannels = [];
485
+ const { streamManager } = this.options.infrastructure;
486
+ // Pause emission on specified channels (WebSocket stays connected)
487
+ // Track which channels successfully paused to ensure proper cleanup
488
+ for (const channel of channels) {
489
+ try {
490
+ streamManager.pauseChannel(channel);
491
+ pausedChannels.push(channel);
492
+ }
493
+ catch (err) {
494
+ // Log error to Sentry but continue pausing remaining channels
495
+ this.logError((0, errorUtils_1.ensureError)(err), this.getErrorContext('withStreamPause', {
496
+ operation: 'pause',
497
+ channel: String(channel),
498
+ pausedChannels: pausedChannels.join(','),
499
+ }));
500
+ }
501
+ }
502
+ try {
503
+ // Execute operation without stream interference
504
+ return await operation();
505
+ }
506
+ finally {
507
+ // Resume only channels that were successfully paused
508
+ for (const channel of pausedChannels) {
509
+ try {
510
+ streamManager.resumeChannel(channel);
511
+ }
512
+ catch (err) {
513
+ // Log error to Sentry but continue resuming remaining channels
514
+ this.logError((0, errorUtils_1.ensureError)(err), this.getErrorContext('withStreamPause', {
515
+ operation: 'resume',
516
+ channel: String(channel),
517
+ pausedChannels: pausedChannels.join(','),
518
+ }));
519
+ }
520
+ }
521
+ }
522
+ }
523
+ /**
524
+ * Initialize the PerpsController providers
525
+ * Must be called before using any other methods
526
+ * Prevents double initialization with promise caching
527
+ */
528
+ async init() {
529
+ if (this.isInitialized) {
530
+ return;
531
+ }
532
+ if (this.initializationPromise) {
533
+ return this.initializationPromise;
534
+ }
535
+ this.initializationPromise = this.performInitialization();
536
+ return this.initializationPromise;
537
+ }
538
+ /**
539
+ * Actual initialization implementation with retry logic
540
+ */
541
+ async performInitialization() {
542
+ const maxAttempts = 3;
543
+ const baseDelay = 1000;
544
+ this.update((state) => {
545
+ state.initializationState = InitializationState.Initializing;
546
+ state.initializationError = null;
547
+ state.initializationAttempts = 0;
548
+ });
549
+ this.debugLog('PerpsController: Initializing providers', {
550
+ currentNetwork: this.state.isTestnet ? 'testnet' : 'mainnet',
551
+ existingProviders: Array.from(this.providers.keys()),
552
+ timestamp: new Date().toISOString(),
553
+ });
554
+ let lastError = null;
555
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
556
+ try {
557
+ this.update((state) => {
558
+ state.initializationAttempts = attempt;
559
+ });
560
+ // Disconnect existing providers to close WebSocket connections
561
+ const existingProviders = Array.from(this.providers.values());
562
+ if (existingProviders.length > 0) {
563
+ this.debugLog('PerpsController: Disconnecting existing providers', {
564
+ count: existingProviders.length,
565
+ timestamp: new Date().toISOString(),
566
+ });
567
+ await Promise.all(existingProviders.map((provider) => provider.disconnect()));
568
+ }
569
+ this.providers.clear();
570
+ const { activeProvider } = this.state;
571
+ this.debugLog('PerpsController: Creating provider with HIP-3 configuration', {
572
+ hip3Enabled: this.hip3Enabled,
573
+ hip3AllowlistMarkets: this.hip3AllowlistMarkets,
574
+ hip3BlocklistMarkets: this.hip3BlocklistMarkets,
575
+ hip3ConfigSource: this.hip3ConfigSource,
576
+ isTestnet: this.state.isTestnet,
577
+ activeProvider,
578
+ });
579
+ // Always create HyperLiquid provider as the base provider
580
+ const hyperLiquidProvider = new HyperLiquidProvider_1.HyperLiquidProvider({
581
+ isTestnet: this.state.isTestnet,
582
+ hip3Enabled: this.hip3Enabled,
583
+ allowlistMarkets: this.hip3AllowlistMarkets,
584
+ blocklistMarkets: this.hip3BlocklistMarkets,
585
+ platformDependencies: this.options.infrastructure,
586
+ });
587
+ this.providers.set('hyperliquid', hyperLiquidProvider);
588
+ // Set up active provider based on activeProvider value in state
589
+ // 'aggregated' is treated as just another provider that wraps others
590
+ if (activeProvider === 'aggregated') {
591
+ // Aggregated mode: wrap in AggregatedPerpsProvider for multi-provider support
592
+ // Future: register additional providers here (MYX, etc.)
593
+ this.activeProviderInstance = new AggregatedPerpsProvider_1.AggregatedPerpsProvider({
594
+ providers: this.providers,
595
+ defaultProvider: 'hyperliquid',
596
+ infrastructure: this.options.infrastructure,
597
+ });
598
+ this.debugLog('PerpsController: Using aggregated provider (multi-provider)', { registeredProviders: Array.from(this.providers.keys()) });
599
+ }
600
+ else if (activeProvider === 'hyperliquid') {
601
+ // Direct provider mode: use HyperLiquid provider directly
602
+ this.activeProviderInstance = hyperLiquidProvider;
603
+ this.debugLog(`PerpsController: Using direct provider (${activeProvider})`);
604
+ }
605
+ else {
606
+ // Unsupported provider - throw error to prevent silent misconfiguration
607
+ throw new Error(`Unsupported provider: ${activeProvider}. Currently only 'hyperliquid' and 'aggregated' are supported.`);
608
+ }
609
+ // Future providers can be added here with their own authentication patterns:
610
+ // - Some might use API keys: new BinanceProvider({ apiKey, apiSecret })
611
+ // - Some might use different wallet patterns: new GMXProvider({ signer })
612
+ // - Some might not need auth at all: new DydxProvider()
613
+ // Wait for WebSocket transport to be ready before marking as initialized
614
+ await (0, wait_1.wait)(perpsConfig_1.PERPS_CONSTANTS.ReconnectionCleanupDelayMs);
615
+ this.isInitialized = true;
616
+ this.update((state) => {
617
+ state.initializationState = InitializationState.Initialized;
618
+ state.initializationError = null;
619
+ });
620
+ this.debugLog('PerpsController: Providers initialized successfully', {
621
+ providerCount: this.providers.size,
622
+ activeProvider,
623
+ timestamp: new Date().toISOString(),
624
+ attempts: attempt,
625
+ });
626
+ return; // Exit retry loop on success
627
+ }
628
+ catch (error) {
629
+ lastError = (0, errorUtils_1.ensureError)(error);
630
+ this.logError(lastError, this.getErrorContext('performInitialization', {
631
+ attempt,
632
+ maxAttempts,
633
+ }));
634
+ // If not the last attempt, wait before retrying (exponential backoff)
635
+ if (attempt < maxAttempts) {
636
+ const delay = baseDelay * Math.pow(2, attempt - 1); // 1s, 2s, 4s
637
+ this.debugLog(`PerpsController: Retrying initialization in ${delay}ms`, {
638
+ attempt,
639
+ maxAttempts,
640
+ error: lastError.message,
641
+ });
642
+ await (0, wait_1.wait)(delay);
643
+ }
644
+ }
645
+ }
646
+ this.isInitialized = false;
647
+ this.update((state) => {
648
+ state.initializationState = InitializationState.Failed;
649
+ state.initializationError = lastError?.message ?? 'Unknown error';
650
+ });
651
+ this.initializationPromise = null; // Clear promise to allow retry
652
+ this.debugLog('PerpsController: Initialization failed', {
653
+ error: lastError?.message,
654
+ attempts: maxAttempts,
655
+ timestamp: new Date().toISOString(),
656
+ });
657
+ }
658
+ /**
659
+ * Generate standard error context for Logger.error calls with searchable tags and context.
660
+ * Enables Sentry dashboard filtering by feature, provider, and network.
661
+ *
662
+ * @param method - The method name where the error occurred
663
+ * @param extra - Optional additional context fields (becomes searchable context data)
664
+ * @returns PerpsLoggerOptions with tags (searchable) and context (searchable)
665
+ * @private
666
+ *
667
+ * @example
668
+ * this.logError(error, this.getErrorContext('placeOrder', { symbol: 'BTC', operation: 'validate' }));
669
+ * // Creates searchable tags: feature:perps, provider:hyperliquid, network:mainnet
670
+ * // Creates searchable context: perps_controller.method:placeOrder, perps_controller.symbol:BTC, perps_controller.operation:validate
671
+ */
672
+ getErrorContext(method, extra) {
673
+ return {
674
+ tags: {
675
+ feature: perpsConfig_1.PERPS_CONSTANTS.FeatureName,
676
+ provider: this.state.activeProvider,
677
+ network: this.state.isTestnet ? 'testnet' : 'mainnet',
678
+ },
679
+ context: {
680
+ name: 'PerpsController',
681
+ data: {
682
+ method,
683
+ ...extra,
684
+ },
685
+ },
686
+ };
687
+ }
688
+ /**
689
+ * Create a ServiceContext for dependency injection into services
690
+ * Provides all orchestration dependencies (tracing, analytics, state management)
691
+ *
692
+ * @param method - Method name for error context
693
+ * @param additionalContext - Optional additional context (e.g., rewardsController, streamManager)
694
+ * @returns ServiceContext with all required dependencies
695
+ */
696
+ createServiceContext(method, additionalContext) {
697
+ return {
698
+ tracingContext: {
699
+ provider: this.state.activeProvider,
700
+ isTestnet: this.state.isTestnet,
701
+ },
702
+ errorContext: {
703
+ controller: 'PerpsController',
704
+ method,
705
+ },
706
+ stateManager: {
707
+ update: (updater) => this.update(updater),
708
+ getState: () => this.state,
83
709
  },
710
+ ...additionalContext,
711
+ };
712
+ }
713
+ /**
714
+ * Ensure TradingService has controller dependencies set.
715
+ * Uses injectable dependencies from infrastructure for core migration compatibility.
716
+ */
717
+ ensureTradingServiceDeps() {
718
+ const { controllers } = this.options.infrastructure;
719
+ this.tradingService.setControllerDependencies({
720
+ controllers,
721
+ messenger: this.messenger,
722
+ rewardsIntegrationService: this.rewardsIntegrationService,
723
+ });
724
+ }
725
+ /**
726
+ * Get the currently active provider.
727
+ * In aggregated mode, returns AggregatedPerpsProvider which routes to underlying providers.
728
+ * In single provider mode, returns HyperLiquidProvider directly.
729
+ *
730
+ * @returns The active provider (aggregated wrapper or direct provider based on mode)
731
+ * @throws Error if provider is not initialized or reinitializing
732
+ */
733
+ getActiveProvider() {
734
+ // Check if we're in the middle of reinitializing
735
+ if (this.isReinitializing) {
736
+ this.update((state) => {
737
+ state.lastError = perpsErrorCodes_1.PERPS_ERROR_CODES.CLIENT_REINITIALIZING;
738
+ state.lastUpdateTimestamp = Date.now();
739
+ });
740
+ throw new Error(perpsErrorCodes_1.PERPS_ERROR_CODES.CLIENT_REINITIALIZING);
741
+ }
742
+ // Check if not initialized
743
+ if (this.state.initializationState !== InitializationState.Initialized ||
744
+ !this.isInitialized) {
745
+ const errorMessage = this.state.initializationState === InitializationState.Failed
746
+ ? `${perpsErrorCodes_1.PERPS_ERROR_CODES.CLIENT_NOT_INITIALIZED}: ${this.state.initializationError || 'Initialization failed'}`
747
+ : perpsErrorCodes_1.PERPS_ERROR_CODES.CLIENT_NOT_INITIALIZED;
748
+ this.update((state) => {
749
+ state.lastError = errorMessage;
750
+ state.lastUpdateTimestamp = Date.now();
751
+ });
752
+ throw new Error(errorMessage);
753
+ }
754
+ // Return the active provider instance (set during initialization based on providerMode)
755
+ if (!this.activeProviderInstance) {
756
+ this.update((state) => {
757
+ state.lastError = perpsErrorCodes_1.PERPS_ERROR_CODES.PROVIDER_NOT_AVAILABLE;
758
+ state.lastUpdateTimestamp = Date.now();
759
+ });
760
+ throw new Error(perpsErrorCodes_1.PERPS_ERROR_CODES.PROVIDER_NOT_AVAILABLE);
761
+ }
762
+ return this.activeProviderInstance;
763
+ }
764
+ /**
765
+ * Get the currently active provider, returning null if not available
766
+ * Use this method when the caller can gracefully handle a missing provider
767
+ * (e.g., UI components during initialization or reconnection)
768
+ *
769
+ * @returns The active provider, or null if not initialized/reinitializing
770
+ */
771
+ getActiveProviderOrNull() {
772
+ // Return null during reinitialization
773
+ if (this.isReinitializing) {
774
+ return null;
775
+ }
776
+ // Return null if not initialized
777
+ if (this.state.initializationState !== InitializationState.Initialized ||
778
+ !this.isInitialized) {
779
+ return null;
780
+ }
781
+ // Return the active provider instance or null if not found
782
+ return this.activeProviderInstance || null;
783
+ }
784
+ /**
785
+ * Place a new order
786
+ * Thin delegation to TradingService
787
+ *
788
+ * @param params
789
+ */
790
+ async placeOrder(params) {
791
+ const provider = this.getActiveProvider();
792
+ this.ensureTradingServiceDeps();
793
+ return this.tradingService.placeOrder({
794
+ provider,
795
+ params,
796
+ context: this.createServiceContext('placeOrder', {
797
+ saveTradeConfiguration: (symbol, leverage) => this.saveTradeConfiguration(symbol, leverage),
798
+ }),
799
+ reportOrderToDataLake: (dataLakeParams) => this.reportOrderToDataLake(dataLakeParams),
800
+ });
801
+ }
802
+ /**
803
+ * Edit an existing order
804
+ * Thin delegation to TradingService
805
+ *
806
+ * @param params
807
+ */
808
+ async editOrder(params) {
809
+ const provider = this.getActiveProvider();
810
+ this.ensureTradingServiceDeps();
811
+ return this.tradingService.editOrder({
812
+ provider,
813
+ params,
814
+ context: this.createServiceContext('editOrder'),
815
+ });
816
+ }
817
+ /**
818
+ * Cancel an existing order
819
+ *
820
+ * @param params
821
+ */
822
+ async cancelOrder(params) {
823
+ const provider = this.getActiveProvider();
824
+ return this.tradingService.cancelOrder({
825
+ provider,
826
+ params,
827
+ context: this.createServiceContext('cancelOrder'),
828
+ });
829
+ }
830
+ /**
831
+ * Cancel multiple orders in parallel
832
+ * Batch version of cancelOrder() that cancels multiple orders simultaneously
833
+ *
834
+ * @param params
835
+ */
836
+ async cancelOrders(params) {
837
+ const provider = this.getActiveProvider();
838
+ return this.tradingService.cancelOrders({
839
+ provider,
840
+ params,
841
+ context: this.createServiceContext('cancelOrders', {
842
+ getOpenOrders: () => this.getOpenOrders(),
843
+ }),
844
+ withStreamPause: (operation, channels) => this.withStreamPause(operation, channels),
845
+ });
846
+ }
847
+ /**
848
+ * Close a position (partial or full)
849
+ * Thin delegation to TradingService
850
+ *
851
+ * @param params
852
+ */
853
+ async closePosition(params) {
854
+ const provider = this.getActiveProvider();
855
+ this.ensureTradingServiceDeps();
856
+ return this.tradingService.closePosition({
857
+ provider,
858
+ params,
859
+ context: this.createServiceContext('closePosition', {
860
+ getPositions: () => this.getPositions(),
861
+ }),
862
+ reportOrderToDataLake: (dataLakeParams) => this.reportOrderToDataLake(dataLakeParams),
863
+ });
864
+ }
865
+ /**
866
+ * Close multiple positions in parallel
867
+ * Batch version of closePosition() that closes multiple positions simultaneously
868
+ *
869
+ * @param params
870
+ */
871
+ async closePositions(params) {
872
+ const provider = this.getActiveProvider();
873
+ this.ensureTradingServiceDeps();
874
+ return this.tradingService.closePositions({
875
+ provider,
876
+ params,
877
+ context: this.createServiceContext('closePositions', {
878
+ getPositions: () => this.getPositions(),
879
+ }),
880
+ });
881
+ }
882
+ /**
883
+ * Update TP/SL for an existing position
884
+ *
885
+ * @param params
886
+ */
887
+ async updatePositionTPSL(params) {
888
+ const provider = this.getActiveProvider();
889
+ this.ensureTradingServiceDeps();
890
+ return this.tradingService.updatePositionTPSL({
891
+ provider,
892
+ params,
893
+ context: this.createServiceContext('updatePositionTPSL'),
894
+ });
895
+ }
896
+ /**
897
+ * Update margin for an existing position (add or remove)
898
+ *
899
+ * @param params
900
+ */
901
+ async updateMargin(params) {
902
+ const provider = this.getActiveProvider();
903
+ this.ensureTradingServiceDeps();
904
+ return this.tradingService.updateMargin({
905
+ provider,
906
+ symbol: params.symbol,
907
+ amount: params.amount,
908
+ context: this.createServiceContext('updateMargin'),
909
+ });
910
+ }
911
+ /**
912
+ * Flip position (reverse direction while keeping size and leverage)
913
+ *
914
+ * @param params
915
+ */
916
+ async flipPosition(params) {
917
+ const provider = this.getActiveProvider();
918
+ this.ensureTradingServiceDeps();
919
+ return this.tradingService.flipPosition({
920
+ provider,
921
+ position: params.position,
922
+ context: this.createServiceContext('flipPosition'),
923
+ });
924
+ }
925
+ /**
926
+ * Simplified deposit method that prepares transaction for confirmation screen
927
+ * No complex state tracking - just sets a loading flag
928
+ *
929
+ * @param amount
930
+ */
931
+ async depositWithConfirmation(amount) {
932
+ const { controllers } = this.options.infrastructure;
933
+ try {
934
+ // Clear any stale results when starting a new deposit flow
935
+ // Don't set depositInProgress yet - wait until user confirms
936
+ // Prepare deposit transaction using DepositService
937
+ const provider = this.getActiveProvider();
938
+ const { transaction, assetChainId, currentDepositId } = await this.depositService.prepareTransaction({ provider });
939
+ this.update((state) => {
940
+ state.lastDepositResult = null;
941
+ // Get current account address via infrastructure
942
+ const evmAccount = this.options.infrastructure.controllers.accounts.getSelectedEvmAccount();
943
+ const accountAddress = evmAccount?.address || 'unknown';
944
+ // Add deposit request to tracking
945
+ const depositRequest = {
946
+ id: currentDepositId,
947
+ timestamp: Date.now(),
948
+ amount: amount || '0', // Use provided amount or default to '0'
949
+ asset: hyperLiquidConfig_1.USDC_SYMBOL,
950
+ accountAddress, // Track which account initiated deposit
951
+ success: false, // Will be updated when transaction completes
952
+ txHash: undefined,
953
+ status: 'pending',
954
+ source: undefined,
955
+ transactionId: undefined, // Will be set to depositId when available
956
+ };
957
+ state.depositRequests.unshift(depositRequest); // Add to beginning of array
958
+ });
959
+ const networkClientId = controllers.network.findNetworkClientIdForChain(assetChainId);
960
+ if (!networkClientId) {
961
+ throw new Error(`No network client found for chain ${assetChainId}. Please add the network first.`);
962
+ }
963
+ const gasFeeToken = transaction.to &&
964
+ assetChainId.toLowerCase() === hyperLiquidConfig_1.ARBITRUM_MAINNET_CHAIN_ID_HEX &&
965
+ transaction.to.toLowerCase() ===
966
+ hyperLiquidConfig_1.USDC_ARBITRUM_MAINNET_ADDRESS.toLowerCase()
967
+ ? transaction.to
968
+ : undefined;
969
+ // submit shows the confirmation screen and returns a promise
970
+ // The promise will resolve when transaction completes or reject if cancelled/failed
971
+ const { result, transactionMeta } = await controllers.transaction.submit(transaction, {
972
+ networkClientId,
973
+ origin: 'metamask',
974
+ type: transaction_controller_1.TransactionType.perpsDeposit,
975
+ skipInitialGasEstimate: true,
976
+ gasFeeToken,
977
+ });
978
+ // Store the transaction ID and try to get amount from transaction
979
+ this.update((state) => {
980
+ state.lastDepositTransactionId = transactionMeta.id;
981
+ });
982
+ // At this point, the confirmation modal is shown to the user
983
+ // The result promise will resolve/reject based on user action and transaction outcome
984
+ // Track the transaction lifecycle
985
+ // The result promise will resolve/reject based on user action and transaction outcome
986
+ // Note: We intentionally don't set depositInProgress immediately to avoid
987
+ // showing toasts before the user confirms the transaction
988
+ // TODO: @abretonc7s Find a better way to trigger our custom toast notification then having to toggle the state
989
+ // How to replace the system notifications?
990
+ result
991
+ .then((actualTxHash) => {
992
+ // Transaction was successfully completed
993
+ // Set depositInProgress to true temporarily to show success
994
+ this.update((state) => {
995
+ state.depositInProgress = true;
996
+ state.lastDepositResult = {
997
+ success: true,
998
+ txHash: actualTxHash,
999
+ amount: amount || '0',
1000
+ asset: hyperLiquidConfig_1.USDC_SYMBOL, // Default asset for deposits
1001
+ timestamp: Date.now(),
1002
+ error: '',
1003
+ };
1004
+ // Update the deposit request by request ID to avoid race conditions
1005
+ if (state.depositRequests.length > 0) {
1006
+ const requestToUpdate = state.depositRequests.find((req) => req.id === currentDepositId);
1007
+ if (requestToUpdate) {
1008
+ // For deposits, we have a txHash immediately, so mark as completed
1009
+ // (the transaction hash means the deposit was successful)
1010
+ requestToUpdate.status = 'completed';
1011
+ requestToUpdate.success = true;
1012
+ requestToUpdate.txHash = actualTxHash;
1013
+ }
1014
+ }
1015
+ });
1016
+ // Clear depositInProgress after a short delay
1017
+ setTimeout(() => {
1018
+ this.update((state) => {
1019
+ state.depositInProgress = false;
1020
+ state.lastDepositTransactionId = null;
1021
+ });
1022
+ }, 100);
1023
+ })
1024
+ .catch((error) => {
1025
+ // Check if user denied/cancelled the transaction
1026
+ const errorMessage = error instanceof Error ? error.message : String(error);
1027
+ const userCancelled = errorMessage.includes('User denied') ||
1028
+ errorMessage.includes('User rejected') ||
1029
+ errorMessage.includes('User cancelled') ||
1030
+ errorMessage.includes('User canceled');
1031
+ if (userCancelled) {
1032
+ // User cancelled - clear any state, no toast
1033
+ this.update((state) => {
1034
+ state.depositInProgress = false;
1035
+ state.lastDepositTransactionId = null;
1036
+ // Don't set lastDepositResult - no toast needed
1037
+ });
1038
+ }
1039
+ else {
1040
+ // Transaction failed after confirmation - show error toast
1041
+ this.update((state) => {
1042
+ state.depositInProgress = false;
1043
+ state.lastDepositTransactionId = null;
1044
+ state.lastDepositResult = {
1045
+ success: false,
1046
+ error: errorMessage,
1047
+ amount: amount || '0',
1048
+ asset: hyperLiquidConfig_1.USDC_SYMBOL, // Default asset for deposits
1049
+ timestamp: Date.now(),
1050
+ txHash: '',
1051
+ };
1052
+ // Update the deposit request by request ID to avoid race conditions
1053
+ if (state.depositRequests.length > 0) {
1054
+ const requestToUpdate = state.depositRequests.find((req) => req.id === currentDepositId);
1055
+ if (requestToUpdate) {
1056
+ requestToUpdate.status = 'failed';
1057
+ requestToUpdate.success = false;
1058
+ }
1059
+ }
1060
+ });
1061
+ }
1062
+ });
1063
+ return {
1064
+ result,
1065
+ };
1066
+ }
1067
+ catch (error) {
1068
+ // Check if user denied/cancelled the transaction
1069
+ const errorMessage = error instanceof Error ? error.message : String(error);
1070
+ const userCancelled = errorMessage.includes('User denied') ||
1071
+ errorMessage.includes('User rejected') ||
1072
+ errorMessage.includes('User cancelled') ||
1073
+ errorMessage.includes('User canceled');
1074
+ if (!userCancelled) {
1075
+ // Only track actual errors, not user cancellations
1076
+ this.update((state) => {
1077
+ state.lastDepositTransactionId = null;
1078
+ // Note: lastDepositResult is already set in the catch block above
1079
+ });
1080
+ }
1081
+ throw error;
1082
+ }
1083
+ }
1084
+ /**
1085
+ * Clear the last deposit result after it has been shown to the user
1086
+ */
1087
+ clearDepositResult() {
1088
+ this.update((state) => {
1089
+ state.lastDepositResult = null;
1090
+ });
1091
+ }
1092
+ clearWithdrawResult() {
1093
+ this.update((state) => {
1094
+ state.lastWithdrawResult = null;
1095
+ });
1096
+ }
1097
+ /**
1098
+ * Update withdrawal request status when it completes
1099
+ * This is called when a withdrawal is matched with a completed withdrawal from the API
1100
+ *
1101
+ * @param withdrawalId
1102
+ * @param status
1103
+ * @param txHash
1104
+ */
1105
+ updateWithdrawalStatus(withdrawalId, status, txHash) {
1106
+ let withdrawalAmount;
1107
+ this.update((state) => {
1108
+ const withdrawalIndex = state.withdrawalRequests.findIndex((request) => request.id === withdrawalId);
1109
+ if (withdrawalIndex >= 0) {
1110
+ const request = state.withdrawalRequests[withdrawalIndex];
1111
+ withdrawalAmount = request.amount;
1112
+ const originalStatus = request.status;
1113
+ request.status = status;
1114
+ request.success = status === 'completed';
1115
+ if (txHash) {
1116
+ request.txHash = txHash;
1117
+ }
1118
+ // Clear withdrawal progress when withdrawal completes
1119
+ if (status === 'completed' || status === 'failed') {
1120
+ state.withdrawalProgress = {
1121
+ progress: 0,
1122
+ lastUpdated: Date.now(),
1123
+ activeWithdrawalId: null,
1124
+ };
1125
+ }
1126
+ // Track withdrawal transaction completed/failed (confirmed via HyperLiquid API)
1127
+ if (withdrawalAmount !== undefined && originalStatus !== status) {
1128
+ this.getMetrics().trackPerpsEvent(types_2.PerpsAnalyticsEvent.WithdrawalTransaction, {
1129
+ [eventNames_1.PerpsEventProperties.STATUS]: status === 'completed'
1130
+ ? eventNames_1.PerpsEventValues.STATUS.COMPLETED
1131
+ : eventNames_1.PerpsEventValues.STATUS.FAILED,
1132
+ [eventNames_1.PerpsEventProperties.WITHDRAWAL_AMOUNT]: Number.parseFloat(withdrawalAmount),
1133
+ });
1134
+ }
1135
+ this.debugLog('PerpsController: Updated withdrawal status', {
1136
+ withdrawalId,
1137
+ status,
1138
+ txHash,
1139
+ });
1140
+ }
1141
+ });
1142
+ }
1143
+ /**
1144
+ * Update withdrawal progress (persistent across navigation)
1145
+ *
1146
+ * @param progress
1147
+ * @param activeWithdrawalId
1148
+ */
1149
+ updateWithdrawalProgress(progress, activeWithdrawalId = null) {
1150
+ this.update((state) => {
1151
+ state.withdrawalProgress = {
1152
+ progress,
1153
+ lastUpdated: Date.now(),
1154
+ activeWithdrawalId,
1155
+ };
1156
+ });
1157
+ }
1158
+ /**
1159
+ * Get current withdrawal progress
1160
+ */
1161
+ getWithdrawalProgress() {
1162
+ return this.state.withdrawalProgress;
1163
+ }
1164
+ /**
1165
+ * Withdraw funds from trading account
1166
+ *
1167
+ * The withdrawal process varies by provider and may involve:
1168
+ * - Direct on-chain transfers
1169
+ * - Bridge operations
1170
+ * - Multi-step validation processes
1171
+ *
1172
+ * Check the specific provider documentation for detailed withdrawal flows.
1173
+ *
1174
+ * @param params Withdrawal parameters
1175
+ * @returns WithdrawResult with withdrawal ID and tracking info
1176
+ */
1177
+ async withdraw(params) {
1178
+ const provider = this.getActiveProvider();
1179
+ return this.accountService.withdraw({
1180
+ provider,
1181
+ params,
1182
+ context: this.createServiceContext('withdraw'),
1183
+ refreshAccountState: async () => {
1184
+ await this.getAccountState({ source: 'post_withdrawal' });
1185
+ },
1186
+ });
1187
+ }
1188
+ /**
1189
+ * Get current positions
1190
+ * Thin delegation to MarketDataService
1191
+ *
1192
+ * @param params
1193
+ */
1194
+ async getPositions(params) {
1195
+ const provider = this.getActiveProvider();
1196
+ return this.marketDataService.getPositions({
1197
+ provider,
1198
+ params,
1199
+ context: this.createServiceContext('getPositions'),
1200
+ });
1201
+ }
1202
+ /**
1203
+ * Get historical user fills (trade executions)
1204
+ * Thin delegation to MarketDataService
1205
+ *
1206
+ * @param params
1207
+ */
1208
+ async getOrderFills(params) {
1209
+ const provider = this.getActiveProvider();
1210
+ return this.marketDataService.getOrderFills({
1211
+ provider,
1212
+ params,
1213
+ context: this.createServiceContext('getOrderFills'),
1214
+ });
1215
+ }
1216
+ /**
1217
+ * Get historical user orders (order lifecycle)
1218
+ * Thin delegation to MarketDataService
1219
+ *
1220
+ * @param params
1221
+ */
1222
+ async getOrders(params) {
1223
+ const provider = this.getActiveProvider();
1224
+ return this.marketDataService.getOrders({
1225
+ provider,
1226
+ params,
1227
+ context: this.createServiceContext('getOrders'),
1228
+ });
1229
+ }
1230
+ /**
1231
+ * Get currently open orders (real-time status)
1232
+ * Thin delegation to MarketDataService
1233
+ *
1234
+ * @param params
1235
+ */
1236
+ async getOpenOrders(params) {
1237
+ const provider = this.getActiveProvider();
1238
+ return this.marketDataService.getOpenOrders({
1239
+ provider,
1240
+ params,
1241
+ context: this.createServiceContext('getOpenOrders'),
1242
+ });
1243
+ }
1244
+ /**
1245
+ * Get historical user funding history (funding payments)
1246
+ * Thin delegation to MarketDataService
1247
+ *
1248
+ * @param params
1249
+ */
1250
+ async getFunding(params) {
1251
+ const provider = this.getActiveProvider();
1252
+ return this.marketDataService.getFunding({
1253
+ provider,
1254
+ params,
1255
+ context: this.createServiceContext('getFunding'),
1256
+ });
1257
+ }
1258
+ /**
1259
+ * Get account state (balances, etc.)
1260
+ * Thin delegation to MarketDataService
1261
+ *
1262
+ * @param params
1263
+ */
1264
+ async getAccountState(params) {
1265
+ const provider = this.getActiveProvider();
1266
+ return this.marketDataService.getAccountState({
1267
+ provider,
1268
+ params,
1269
+ context: this.createServiceContext('getAccountState'),
1270
+ });
1271
+ }
1272
+ /**
1273
+ * Get historical portfolio data
1274
+ * Thin delegation to MarketDataService
1275
+ *
1276
+ * @param params
1277
+ */
1278
+ async getHistoricalPortfolio(params) {
1279
+ const provider = this.getActiveProvider();
1280
+ return this.marketDataService.getHistoricalPortfolio({
1281
+ provider,
1282
+ params,
1283
+ context: this.createServiceContext('getHistoricalPortfolio'),
1284
+ });
1285
+ }
1286
+ /**
1287
+ * Get available markets with optional filtering
1288
+ * Thin delegation to MarketDataService
1289
+ *
1290
+ * For readOnly mode, bypasses getActiveProvider() to allow market discovery
1291
+ * without full perps initialization (e.g., for discovery banners on spot screens)
1292
+ *
1293
+ * @param params
1294
+ */
1295
+ async getMarkets(params) {
1296
+ // For readOnly mode, access provider directly without initialization check
1297
+ // This allows discovery use cases (checking if market exists) without full perps setup
1298
+ if (params?.readOnly) {
1299
+ // Try to get existing provider, or create a temporary one for readOnly queries
1300
+ // Note: 'aggregated' mode uses activeProviderInstance directly, not the providers map
1301
+ const { activeProvider } = this.state;
1302
+ let provider = activeProvider === 'aggregated'
1303
+ ? undefined
1304
+ : this.providers.get(activeProvider);
1305
+ // Create a temporary provider instance for readOnly queries
1306
+ // The readOnly path in provider creates a standalone InfoClient without full init
1307
+ provider ?? (provider = new HyperLiquidProvider_1.HyperLiquidProvider({
1308
+ isTestnet: this.state.isTestnet,
1309
+ hip3Enabled: this.hip3Enabled,
1310
+ allowlistMarkets: this.hip3AllowlistMarkets,
1311
+ blocklistMarkets: this.hip3BlocklistMarkets,
1312
+ platformDependencies: this.options.infrastructure,
1313
+ }));
1314
+ return provider.getMarkets(params);
1315
+ }
1316
+ const provider = this.getActiveProvider();
1317
+ return this.marketDataService.getMarkets({
1318
+ provider,
1319
+ params,
1320
+ context: this.createServiceContext('getMarkets'),
1321
+ });
1322
+ }
1323
+ /**
1324
+ * Get list of available HIP-3 builder-deployed DEXs
1325
+ *
1326
+ * @param params - Optional parameters for filtering
1327
+ * @returns Array of DEX names
1328
+ */
1329
+ async getAvailableDexs(params) {
1330
+ const provider = this.getActiveProvider();
1331
+ const context = this.createServiceContext('getAvailableDexs');
1332
+ return this.marketDataService.getAvailableDexs({
1333
+ provider,
1334
+ params,
1335
+ context,
1336
+ });
1337
+ }
1338
+ /**
1339
+ * Fetch historical candle data
1340
+ * Thin delegation to MarketDataService
1341
+ *
1342
+ * @param symbol
1343
+ * @param interval
1344
+ * @param limit
1345
+ * @param endTime
1346
+ */
1347
+ async fetchHistoricalCandles(symbol, interval, limit = 100, endTime) {
1348
+ const provider = this.getActiveProvider();
1349
+ return this.marketDataService.fetchHistoricalCandles({
1350
+ provider,
1351
+ symbol,
1352
+ interval,
1353
+ limit,
1354
+ endTime,
1355
+ context: this.createServiceContext('fetchHistoricalCandles'),
1356
+ });
1357
+ }
1358
+ /**
1359
+ * Calculate liquidation price for a position
1360
+ * Uses provider-specific formulas based on protocol rules
1361
+ *
1362
+ * @param params
1363
+ */
1364
+ async calculateLiquidationPrice(params) {
1365
+ const provider = this.getActiveProvider();
1366
+ const context = this.createServiceContext('calculateLiquidationPrice');
1367
+ return this.marketDataService.calculateLiquidationPrice({
1368
+ provider,
1369
+ params,
1370
+ context,
1371
+ });
1372
+ }
1373
+ /**
1374
+ * Calculate maintenance margin for a specific asset
1375
+ * Returns a percentage (e.g., 0.0125 for 1.25%)
1376
+ *
1377
+ * @param params
1378
+ */
1379
+ async calculateMaintenanceMargin(params) {
1380
+ const provider = this.getActiveProvider();
1381
+ const context = this.createServiceContext('calculateMaintenanceMargin');
1382
+ return this.marketDataService.calculateMaintenanceMargin({
1383
+ provider,
1384
+ params,
1385
+ context,
1386
+ });
1387
+ }
1388
+ /**
1389
+ * Get maximum leverage allowed for an asset
1390
+ *
1391
+ * @param asset
1392
+ */
1393
+ async getMaxLeverage(asset) {
1394
+ const provider = this.getActiveProvider();
1395
+ const context = this.createServiceContext('getMaxLeverage');
1396
+ return this.marketDataService.getMaxLeverage({ provider, asset, context });
1397
+ }
1398
+ /**
1399
+ * Validate order parameters according to protocol-specific rules
1400
+ *
1401
+ * @param params
1402
+ */
1403
+ async validateOrder(params) {
1404
+ const provider = this.getActiveProvider();
1405
+ const context = this.createServiceContext('validateOrder');
1406
+ return this.marketDataService.validateOrder({ provider, params, context });
1407
+ }
1408
+ /**
1409
+ * Validate close position parameters according to protocol-specific rules
1410
+ *
1411
+ * @param params
1412
+ */
1413
+ async validateClosePosition(params) {
1414
+ const provider = this.getActiveProvider();
1415
+ const context = this.createServiceContext('validateClosePosition');
1416
+ return this.marketDataService.validateClosePosition({
1417
+ provider,
1418
+ params,
1419
+ context,
1420
+ });
1421
+ }
1422
+ /**
1423
+ * Validate withdrawal parameters according to protocol-specific rules
1424
+ *
1425
+ * @param params
1426
+ */
1427
+ async validateWithdrawal(params) {
1428
+ const provider = this.getActiveProvider();
1429
+ return this.accountService.validateWithdrawal({ provider, params });
1430
+ }
1431
+ /**
1432
+ * Get supported withdrawal routes - returns complete asset and routing information
1433
+ */
1434
+ getWithdrawalRoutes() {
1435
+ try {
1436
+ const provider = this.getActiveProvider();
1437
+ return this.marketDataService.getWithdrawalRoutes({ provider });
1438
+ }
1439
+ catch (error) {
1440
+ this.logError((0, errorUtils_1.ensureError)(error), this.getErrorContext('getWithdrawalRoutes'));
1441
+ // Return empty array if provider is not available
1442
+ return [];
1443
+ }
1444
+ }
1445
+ /**
1446
+ * Toggle between testnet and mainnet
1447
+ */
1448
+ async toggleTestnet() {
1449
+ // Prevent concurrent reinitializations
1450
+ if (this.isReinitializing) {
1451
+ this.debugLog('PerpsController: Already reinitializing, skipping toggle', {
1452
+ timestamp: new Date().toISOString(),
1453
+ });
1454
+ return {
1455
+ success: false,
1456
+ isTestnet: this.state.isTestnet,
1457
+ error: perpsErrorCodes_1.PERPS_ERROR_CODES.CLIENT_REINITIALIZING,
1458
+ };
1459
+ }
1460
+ this.isReinitializing = true;
1461
+ try {
1462
+ const previousNetwork = this.state.isTestnet ? 'testnet' : 'mainnet';
1463
+ this.update((state) => {
1464
+ state.isTestnet = !state.isTestnet;
1465
+ });
1466
+ const newNetwork = this.state.isTestnet ? 'testnet' : 'mainnet';
1467
+ this.debugLog('PerpsController: Network toggle initiated', {
1468
+ from: previousNetwork,
1469
+ to: newNetwork,
1470
+ timestamp: new Date().toISOString(),
1471
+ });
1472
+ // Reset initialization state and reinitialize provider with new testnet setting
1473
+ this.isInitialized = false;
1474
+ this.initializationPromise = null;
1475
+ await this.init();
1476
+ this.debugLog('PerpsController: Network toggle completed', {
1477
+ newNetwork,
1478
+ isTestnet: this.state.isTestnet,
1479
+ timestamp: new Date().toISOString(),
1480
+ });
1481
+ return { success: true, isTestnet: this.state.isTestnet };
1482
+ }
1483
+ catch (error) {
1484
+ return {
1485
+ success: false,
1486
+ isTestnet: this.state.isTestnet,
1487
+ error: error instanceof Error
1488
+ ? error.message
1489
+ : perpsErrorCodes_1.PERPS_ERROR_CODES.UNKNOWN_ERROR,
1490
+ };
1491
+ }
1492
+ finally {
1493
+ this.isReinitializing = false;
1494
+ }
1495
+ }
1496
+ /**
1497
+ * Switch to a different provider
1498
+ *
1499
+ * @param providerId
1500
+ */
1501
+ async switchProvider(providerId) {
1502
+ // 'aggregated' is always valid, individual providers must exist in the map
1503
+ const isValidProvider = providerId === 'aggregated' || this.providers.has(providerId);
1504
+ if (!isValidProvider) {
1505
+ return {
1506
+ success: false,
1507
+ providerId: this.state.activeProvider,
1508
+ error: `Provider ${providerId} not available`,
1509
+ };
1510
+ }
1511
+ this.update((state) => {
1512
+ state.activeProvider = providerId;
1513
+ });
1514
+ return { success: true, providerId };
1515
+ }
1516
+ /**
1517
+ * Get current network (mainnet/testnet)
1518
+ */
1519
+ getCurrentNetwork() {
1520
+ return this.state.isTestnet ? 'testnet' : 'mainnet';
1521
+ }
1522
+ /**
1523
+ * Get the current WebSocket connection state from the active provider.
1524
+ * Used by the UI to monitor connection health and show notifications.
1525
+ *
1526
+ * @returns The current WebSocket connection state, or DISCONNECTED if not supported
1527
+ */
1528
+ getWebSocketConnectionState() {
1529
+ try {
1530
+ const provider = this.getActiveProvider();
1531
+ if (provider.getWebSocketConnectionState) {
1532
+ return provider.getWebSocketConnectionState();
1533
+ }
1534
+ // Fallback for providers that don't support this method
1535
+ return types_1.WebSocketConnectionState.Disconnected;
1536
+ }
1537
+ catch {
1538
+ // If no provider is active, return disconnected
1539
+ return types_1.WebSocketConnectionState.Disconnected;
1540
+ }
1541
+ }
1542
+ /**
1543
+ * Subscribe to WebSocket connection state changes from the active provider.
1544
+ * The listener will be called immediately with the current state and whenever the state changes.
1545
+ *
1546
+ * @param listener - Callback function that receives the new connection state and reconnection attempt
1547
+ * @returns Unsubscribe function to remove the listener, or no-op if not supported
1548
+ */
1549
+ subscribeToConnectionState(listener) {
1550
+ try {
1551
+ const provider = this.getActiveProvider();
1552
+ if (provider.subscribeToConnectionState) {
1553
+ return provider.subscribeToConnectionState(listener);
1554
+ }
1555
+ // Fallback: immediately call with current state and return no-op unsubscribe
1556
+ listener(this.getWebSocketConnectionState(), 0);
1557
+ return () => {
1558
+ // No-op
1559
+ };
1560
+ }
1561
+ catch {
1562
+ // If no provider is active, call with disconnected and return no-op
1563
+ listener(types_1.WebSocketConnectionState.Disconnected, 0);
1564
+ return () => {
1565
+ // No-op
1566
+ };
1567
+ }
1568
+ }
1569
+ /**
1570
+ * Manually trigger a WebSocket reconnection attempt.
1571
+ * Used by the UI retry button when connection is lost.
1572
+ */
1573
+ async reconnect() {
1574
+ this.debugLog('[PerpsController] reconnect() called');
1575
+ try {
1576
+ const provider = this.getActiveProvider();
1577
+ if (provider.reconnect) {
1578
+ this.debugLog('[PerpsController] Delegating to provider.reconnect()');
1579
+ await provider.reconnect();
1580
+ this.debugLog('[PerpsController] provider.reconnect() completed');
1581
+ }
1582
+ else {
1583
+ this.debugLog('[PerpsController] Provider does not support reconnect()');
1584
+ }
1585
+ }
1586
+ catch (error) {
1587
+ this.logError((0, errorUtils_1.ensureError)(error), this.getErrorContext('reconnect', {
1588
+ operation: 'websocket_reconnect',
1589
+ }));
1590
+ }
1591
+ }
1592
+ // Live data delegation (NO Redux) - delegates to active provider
1593
+ /**
1594
+ * Subscribe to live price updates
1595
+ *
1596
+ * @param params
1597
+ */
1598
+ subscribeToPrices(params) {
1599
+ try {
1600
+ const provider = this.getActiveProvider();
1601
+ return provider.subscribeToPrices(params);
1602
+ }
1603
+ catch (error) {
1604
+ this.logError((0, errorUtils_1.ensureError)(error), this.getErrorContext('subscribeToPrices', {
1605
+ symbols: params.symbols?.join(','),
1606
+ }));
1607
+ // Return a no-op unsubscribe function
1608
+ return () => {
1609
+ // No-op: Provider not initialized
1610
+ };
1611
+ }
1612
+ }
1613
+ /**
1614
+ * Subscribe to live position updates
1615
+ *
1616
+ * @param params
1617
+ */
1618
+ subscribeToPositions(params) {
1619
+ try {
1620
+ const provider = this.getActiveProvider();
1621
+ return provider.subscribeToPositions(params);
1622
+ }
1623
+ catch (error) {
1624
+ this.logError((0, errorUtils_1.ensureError)(error), this.getErrorContext('subscribeToPositions', {
1625
+ accountId: params.accountId,
1626
+ }));
1627
+ // Return a no-op unsubscribe function
1628
+ return () => {
1629
+ // No-op: Provider not initialized
1630
+ };
1631
+ }
1632
+ }
1633
+ /**
1634
+ * Subscribe to live order fill updates
1635
+ *
1636
+ * @param params
1637
+ */
1638
+ subscribeToOrderFills(params) {
1639
+ try {
1640
+ const provider = this.getActiveProvider();
1641
+ return provider.subscribeToOrderFills(params);
1642
+ }
1643
+ catch (error) {
1644
+ this.logError((0, errorUtils_1.ensureError)(error), this.getErrorContext('subscribeToOrderFills', {
1645
+ accountId: params.accountId,
1646
+ }));
1647
+ // Return a no-op unsubscribe function
1648
+ return () => {
1649
+ // No-op: Provider not initialized
1650
+ };
1651
+ }
1652
+ }
1653
+ /**
1654
+ * Subscribe to live order updates
1655
+ *
1656
+ * @param params
1657
+ */
1658
+ subscribeToOrders(params) {
1659
+ try {
1660
+ const provider = this.getActiveProvider();
1661
+ return provider.subscribeToOrders(params);
1662
+ }
1663
+ catch (error) {
1664
+ this.logError((0, errorUtils_1.ensureError)(error), this.getErrorContext('subscribeToOrders', {
1665
+ accountId: params.accountId,
1666
+ }));
1667
+ // Return a no-op unsubscribe function
1668
+ return () => {
1669
+ // No-op: Provider not initialized
1670
+ };
1671
+ }
1672
+ }
1673
+ /**
1674
+ * Subscribe to live account updates
1675
+ *
1676
+ * @param params
1677
+ */
1678
+ subscribeToAccount(params) {
1679
+ try {
1680
+ const provider = this.getActiveProvider();
1681
+ return provider.subscribeToAccount(params);
1682
+ }
1683
+ catch (error) {
1684
+ this.logError((0, errorUtils_1.ensureError)(error), this.getErrorContext('subscribeToAccount', {
1685
+ accountId: params.accountId,
1686
+ }));
1687
+ // Return a no-op unsubscribe function
1688
+ return () => {
1689
+ // No-op: Provider not initialized
1690
+ };
1691
+ }
1692
+ }
1693
+ /**
1694
+ * Subscribe to full order book updates with multiple depth levels
1695
+ * Creates a dedicated L2Book subscription for real-time order book data
1696
+ *
1697
+ * @param params
1698
+ */
1699
+ subscribeToOrderBook(params) {
1700
+ try {
1701
+ const provider = this.getActiveProvider();
1702
+ return provider.subscribeToOrderBook(params);
1703
+ }
1704
+ catch (error) {
1705
+ this.logError((0, errorUtils_1.ensureError)(error), this.getErrorContext('subscribeToOrderBook', {
1706
+ symbol: params.symbol,
1707
+ levels: params.levels,
1708
+ }));
1709
+ // Return a no-op unsubscribe function
1710
+ return () => {
1711
+ // No-op: Provider not initialized
1712
+ };
1713
+ }
1714
+ }
1715
+ /**
1716
+ * Subscribe to live candle updates
1717
+ *
1718
+ * @param params
1719
+ */
1720
+ subscribeToCandles(params) {
1721
+ try {
1722
+ const provider = this.getActiveProvider();
1723
+ return provider.subscribeToCandles(params);
1724
+ }
1725
+ catch (error) {
1726
+ this.logError((0, errorUtils_1.ensureError)(error), this.getErrorContext('subscribeToCandles', {
1727
+ symbol: params.symbol,
1728
+ interval: params.interval,
1729
+ duration: params.duration,
1730
+ }));
1731
+ // Return a no-op unsubscribe function
1732
+ return () => {
1733
+ // No-op: Provider not initialized
1734
+ };
1735
+ }
1736
+ }
1737
+ /**
1738
+ * Subscribe to open interest cap updates
1739
+ * Zero additional network overhead - data comes from existing webData3 subscription
1740
+ *
1741
+ * @param params
1742
+ */
1743
+ subscribeToOICaps(params) {
1744
+ try {
1745
+ const provider = this.getActiveProvider();
1746
+ return provider.subscribeToOICaps(params);
1747
+ }
1748
+ catch (error) {
1749
+ this.logError((0, errorUtils_1.ensureError)(error), this.getErrorContext('subscribeToOICaps', {
1750
+ accountId: params.accountId,
1751
+ }));
1752
+ // Return a no-op unsubscribe function
1753
+ return () => {
1754
+ // No-op: Provider not initialized
1755
+ };
1756
+ }
1757
+ }
1758
+ /**
1759
+ * Configure live data throttling
1760
+ *
1761
+ * @param config
1762
+ */
1763
+ setLiveDataConfig(config) {
1764
+ try {
1765
+ const provider = this.getActiveProvider();
1766
+ provider.setLiveDataConfig(config);
1767
+ }
1768
+ catch (error) {
1769
+ this.logError((0, errorUtils_1.ensureError)(error), this.getErrorContext('setLiveDataConfig'));
1770
+ }
1771
+ }
1772
+ /**
1773
+ * Calculate trading fees for the active provider
1774
+ * Each provider implements its own fee structure
1775
+ *
1776
+ * @param params
1777
+ */
1778
+ async calculateFees(params) {
1779
+ const provider = this.getActiveProvider();
1780
+ const context = this.createServiceContext('calculateFees');
1781
+ return this.marketDataService.calculateFees({ provider, params, context });
1782
+ }
1783
+ /**
1784
+ * Disconnect provider and cleanup subscriptions
1785
+ * Call this when navigating away from Perps screens to prevent battery drain
1786
+ */
1787
+ async disconnect() {
1788
+ this.debugLog('PerpsController: Disconnecting provider to cleanup subscriptions', {
1789
+ timestamp: new Date().toISOString(),
84
1790
  });
1791
+ // Only disconnect the provider if we're initialized
1792
+ if (this.isInitialized && !this.isReinitializing) {
1793
+ try {
1794
+ const provider = this.getActiveProvider();
1795
+ await provider.disconnect();
1796
+ }
1797
+ catch (error) {
1798
+ this.logError((0, errorUtils_1.ensureError)(error), this.getErrorContext('disconnect'));
1799
+ }
1800
+ }
1801
+ // Reset initialization state to ensure proper reconnection
1802
+ this.isInitialized = false;
1803
+ this.initializationPromise = null;
1804
+ }
1805
+ /**
1806
+ * Eligibility (Geo-Blocking)
1807
+ */
1808
+ /**
1809
+ * Fetch geo location
1810
+ *
1811
+ * Returned in Country or Country-Region format
1812
+ * Example: FR, DE, US-MI, CA-ON
1813
+ */
1814
+ /**
1815
+ * Refresh eligibility status
1816
+ */
1817
+ async refreshEligibility() {
1818
+ // Capture the current version before starting the async operation.
1819
+ // This prevents race conditions where stale eligibility checks
1820
+ // (started with fallback config) overwrite results from newer checks
1821
+ // (started with remote config after it was fetched).
1822
+ const versionAtStart = this.blockedRegionListVersion;
1823
+ try {
1824
+ // TODO: It would be good to have this location before we call this async function to avoid the race condition
1825
+ const isEligible = await this.eligibilityService.checkEligibility(this.blockedRegionList.list);
1826
+ // Only update state if the blocked region list hasn't changed while we were awaiting.
1827
+ // This prevents stale fallback-based eligibility checks from overwriting
1828
+ // results from remote-based checks.
1829
+ if (this.blockedRegionListVersion !== versionAtStart) {
1830
+ return;
1831
+ }
1832
+ this.update((state) => {
1833
+ state.isEligible = isEligible;
1834
+ });
1835
+ }
1836
+ catch (error) {
1837
+ this.logError((0, errorUtils_1.ensureError)(error), this.getErrorContext('refreshEligibility'));
1838
+ // Only update on error if version is still current
1839
+ if (this.blockedRegionListVersion === versionAtStart) {
1840
+ // Default to eligible on error
1841
+ this.update((state) => {
1842
+ state.isEligible = true;
1843
+ });
1844
+ }
1845
+ }
1846
+ }
1847
+ /**
1848
+ * Get block explorer URL for an address or just the base URL
1849
+ *
1850
+ * @param address - Optional address to append to the base URL
1851
+ * @returns Block explorer URL
1852
+ */
1853
+ getBlockExplorerUrl(address) {
1854
+ const provider = this.getActiveProvider();
1855
+ return this.marketDataService.getBlockExplorerUrl({ provider, address });
1856
+ }
1857
+ /**
1858
+ * Check if user is first-time for the current network
1859
+ */
1860
+ isFirstTimeUserOnCurrentNetwork() {
1861
+ const currentNetwork = this.state.isTestnet ? 'testnet' : 'mainnet';
1862
+ return this.state.isFirstTimeUser[currentNetwork];
1863
+ }
1864
+ /**
1865
+ * Mark that the user has completed the tutorial/onboarding
1866
+ * This prevents the tutorial from showing again
1867
+ */
1868
+ markTutorialCompleted() {
1869
+ const currentNetwork = this.state.isTestnet ? 'testnet' : 'mainnet';
1870
+ this.debugLog('PerpsController: Marking tutorial as completed', {
1871
+ timestamp: new Date().toISOString(),
1872
+ network: currentNetwork,
1873
+ });
1874
+ this.update((state) => {
1875
+ state.isFirstTimeUser[currentNetwork] = false;
1876
+ });
1877
+ }
1878
+ /*
1879
+ * Mark that user has placed their first successful order
1880
+ * This prevents the notification tooltip from showing again
1881
+ */
1882
+ markFirstOrderCompleted() {
1883
+ const currentNetwork = this.state.isTestnet ? 'testnet' : 'mainnet';
1884
+ this.debugLog('PerpsController: Marking first order completed', {
1885
+ timestamp: new Date().toISOString(),
1886
+ network: currentNetwork,
1887
+ });
1888
+ this.update((state) => {
1889
+ state.hasPlacedFirstOrder[currentNetwork] = true;
1890
+ });
1891
+ }
1892
+ /**
1893
+ * Reset first-time user state for both networks
1894
+ * This is useful for testing the tutorial flow
1895
+ * Called by Reset Account feature in settings
1896
+ */
1897
+ resetFirstTimeUserState() {
1898
+ this.debugLog('PerpsController: Resetting first-time user state', {
1899
+ timestamp: new Date().toISOString(),
1900
+ previousState: this.state.isFirstTimeUser,
1901
+ });
1902
+ this.update((state) => {
1903
+ state.isFirstTimeUser = {
1904
+ testnet: true,
1905
+ mainnet: true,
1906
+ };
1907
+ state.hasPlacedFirstOrder = {
1908
+ testnet: false,
1909
+ mainnet: false,
1910
+ };
1911
+ });
1912
+ }
1913
+ /**
1914
+ * Clear pending/bridging withdrawal and deposit requests
1915
+ * This is useful when users want to clear stuck pending indicators
1916
+ * Called by Reset Account feature in settings
1917
+ */
1918
+ clearPendingTransactionRequests() {
1919
+ this.debugLog('PerpsController: Clearing pending transaction requests', {
1920
+ timestamp: new Date().toISOString(),
1921
+ });
1922
+ this.update((state) => {
1923
+ // Filter out pending/bridging withdrawals, keep completed/failed for history
1924
+ state.withdrawalRequests = state.withdrawalRequests.filter((req) => req.status !== 'pending' && req.status !== 'bridging');
1925
+ // Filter out pending deposits, keep completed/failed for history
1926
+ state.depositRequests = state.depositRequests.filter((req) => req.status !== 'pending' && req.status !== 'bridging');
1927
+ // Reset withdrawal progress
1928
+ state.withdrawalProgress = {
1929
+ progress: 0,
1930
+ lastUpdated: Date.now(),
1931
+ activeWithdrawalId: null,
1932
+ };
1933
+ });
1934
+ }
1935
+ /**
1936
+ * Get saved trade configuration for a market
1937
+ *
1938
+ * @param symbol
1939
+ */
1940
+ getTradeConfiguration(symbol) {
1941
+ const network = this.state.isTestnet ? 'testnet' : 'mainnet';
1942
+ const config = this.state.tradeConfigurations[network]?.[symbol];
1943
+ if (!config?.leverage) {
1944
+ return undefined;
1945
+ }
1946
+ this.debugLog('PerpsController: Retrieved trade config', {
1947
+ symbol,
1948
+ network,
1949
+ leverage: config.leverage,
1950
+ });
1951
+ return { leverage: config.leverage };
1952
+ }
1953
+ /**
1954
+ * Save trade configuration for a market
1955
+ *
1956
+ * @param symbol - Market symbol
1957
+ * @param leverage - Leverage value
1958
+ */
1959
+ saveTradeConfiguration(symbol, leverage) {
1960
+ const network = this.state.isTestnet ? 'testnet' : 'mainnet';
1961
+ this.debugLog('PerpsController: Saving trade configuration', {
1962
+ symbol,
1963
+ network,
1964
+ leverage,
1965
+ timestamp: new Date().toISOString(),
1966
+ });
1967
+ this.update((state) => {
1968
+ if (!state.tradeConfigurations[network]) {
1969
+ state.tradeConfigurations[network] = {};
1970
+ }
1971
+ const existingConfig = state.tradeConfigurations[network][symbol] || {};
1972
+ state.tradeConfigurations[network][symbol] = {
1973
+ ...existingConfig,
1974
+ leverage,
1975
+ };
1976
+ });
1977
+ }
1978
+ /**
1979
+ * Save pending trade configuration for a market
1980
+ * This is a temporary configuration that expires after 5 minutes
1981
+ *
1982
+ * @param symbol - Market symbol
1983
+ * @param config - Pending trade configuration
1984
+ * @param config.amount
1985
+ * @param config.leverage
1986
+ * @param config.takeProfitPrice
1987
+ * @param config.stopLossPrice
1988
+ * @param config.limitPrice
1989
+ * @param config.orderType
1990
+ */
1991
+ savePendingTradeConfiguration(symbol, config) {
1992
+ const network = this.state.isTestnet ? 'testnet' : 'mainnet';
1993
+ this.debugLog('PerpsController: Saving pending trade configuration', {
1994
+ symbol,
1995
+ network,
1996
+ config,
1997
+ timestamp: new Date().toISOString(),
1998
+ });
1999
+ this.update((state) => {
2000
+ if (!state.tradeConfigurations[network]) {
2001
+ state.tradeConfigurations[network] = {};
2002
+ }
2003
+ const existingConfig = state.tradeConfigurations[network][symbol] || {};
2004
+ state.tradeConfigurations[network][symbol] = {
2005
+ ...existingConfig,
2006
+ pendingConfig: {
2007
+ ...config,
2008
+ timestamp: Date.now(),
2009
+ },
2010
+ };
2011
+ });
2012
+ }
2013
+ /**
2014
+ * Get pending trade configuration for a market
2015
+ * Returns undefined if config doesn't exist or has expired (more than 5 minutes old)
2016
+ *
2017
+ * @param symbol - Market symbol
2018
+ * @returns Pending trade configuration or undefined
2019
+ */
2020
+ getPendingTradeConfiguration(symbol) {
2021
+ const network = this.state.isTestnet ? 'testnet' : 'mainnet';
2022
+ const config = this.state.tradeConfigurations[network]?.[symbol]?.pendingConfig;
2023
+ if (!config) {
2024
+ return undefined;
2025
+ }
2026
+ // Check if config has expired (5 minutes = 300,000 milliseconds)
2027
+ const FIVE_MINUTES_MS = 5 * 60 * 1000;
2028
+ const now = Date.now();
2029
+ const age = now - config.timestamp;
2030
+ if (age > FIVE_MINUTES_MS) {
2031
+ this.debugLog('PerpsController: Pending trade config expired', {
2032
+ symbol,
2033
+ network,
2034
+ age,
2035
+ timestamp: config.timestamp,
2036
+ });
2037
+ // Clear expired config
2038
+ this.update((state) => {
2039
+ if (state.tradeConfigurations[network]?.[symbol]?.pendingConfig) {
2040
+ delete state.tradeConfigurations[network][symbol].pendingConfig;
2041
+ }
2042
+ });
2043
+ return undefined;
2044
+ }
2045
+ this.debugLog('PerpsController: Retrieved pending trade config', {
2046
+ symbol,
2047
+ network,
2048
+ config,
2049
+ age,
2050
+ });
2051
+ // Return config without timestamp
2052
+ const { timestamp, ...configWithoutTimestamp } = config;
2053
+ return configWithoutTimestamp;
2054
+ }
2055
+ /**
2056
+ * Clear pending trade configuration for a market
2057
+ *
2058
+ * @param symbol - Market symbol
2059
+ */
2060
+ clearPendingTradeConfiguration(symbol) {
2061
+ const network = this.state.isTestnet ? 'testnet' : 'mainnet';
2062
+ this.debugLog('PerpsController: Clearing pending trade configuration', {
2063
+ symbol,
2064
+ network,
2065
+ timestamp: new Date().toISOString(),
2066
+ });
2067
+ this.update((state) => {
2068
+ if (state.tradeConfigurations[network]?.[symbol]?.pendingConfig) {
2069
+ delete state.tradeConfigurations[network][symbol].pendingConfig;
2070
+ }
2071
+ });
2072
+ }
2073
+ /**
2074
+ * Get saved market filter preferences
2075
+ * Handles backward compatibility with legacy string format
2076
+ */
2077
+ getMarketFilterPreferences() {
2078
+ const pref = this.state.marketFilterPreferences;
2079
+ // Handle legacy string format (backward compatibility)
2080
+ if (typeof pref === 'string') {
2081
+ // Map legacy compound IDs to new format
2082
+ // Old format: 'priceChange-desc' or 'priceChange-asc'
2083
+ // New format: { optionId: 'priceChange', direction: 'desc'/'asc' }
2084
+ if (pref === 'priceChange-desc') {
2085
+ return {
2086
+ optionId: 'priceChange',
2087
+ direction: 'desc',
2088
+ };
2089
+ }
2090
+ if (pref === 'priceChange-asc') {
2091
+ return {
2092
+ optionId: 'priceChange',
2093
+ direction: 'asc',
2094
+ };
2095
+ }
2096
+ // Handle other simple legacy strings (e.g., 'volume', 'openInterest', etc.)
2097
+ return {
2098
+ optionId: pref,
2099
+ direction: perpsConfig_1.MARKET_SORTING_CONFIG.DefaultDirection,
2100
+ };
2101
+ }
2102
+ // Return new object format or default
2103
+ return (pref ?? {
2104
+ optionId: perpsConfig_1.MARKET_SORTING_CONFIG.DefaultSortOptionId,
2105
+ direction: perpsConfig_1.MARKET_SORTING_CONFIG.DefaultDirection,
2106
+ });
2107
+ }
2108
+ /**
2109
+ * Save market filter preferences
2110
+ *
2111
+ * @param optionId - Sort/filter option ID
2112
+ * @param direction - Sort direction ('asc' or 'desc')
2113
+ */
2114
+ saveMarketFilterPreferences(optionId, direction) {
2115
+ this.debugLog('PerpsController: Saving market filter preferences', {
2116
+ optionId,
2117
+ direction,
2118
+ timestamp: new Date().toISOString(),
2119
+ });
2120
+ this.update((state) => {
2121
+ state.marketFilterPreferences = { optionId, direction };
2122
+ });
2123
+ }
2124
+ /**
2125
+ * Get saved order book grouping for a market
2126
+ *
2127
+ * @param symbol - Market symbol
2128
+ * @returns The saved grouping value or undefined if not set
2129
+ */
2130
+ getOrderBookGrouping(symbol) {
2131
+ const network = this.state.isTestnet ? 'testnet' : 'mainnet';
2132
+ const grouping = this.state.tradeConfigurations[network]?.[symbol]?.orderBookGrouping;
2133
+ if (grouping !== undefined) {
2134
+ this.debugLog('PerpsController: Retrieved order book grouping', {
2135
+ symbol,
2136
+ network,
2137
+ grouping,
2138
+ });
2139
+ }
2140
+ return grouping;
2141
+ }
2142
+ /**
2143
+ * Save order book grouping for a market
2144
+ *
2145
+ * @param symbol - Market symbol
2146
+ * @param grouping - Price grouping value
2147
+ */
2148
+ saveOrderBookGrouping(symbol, grouping) {
2149
+ const network = this.state.isTestnet ? 'testnet' : 'mainnet';
2150
+ this.debugLog('PerpsController: Saving order book grouping', {
2151
+ symbol,
2152
+ network,
2153
+ grouping,
2154
+ timestamp: new Date().toISOString(),
2155
+ });
2156
+ this.update((state) => {
2157
+ if (!state.tradeConfigurations[network]) {
2158
+ state.tradeConfigurations[network] = {};
2159
+ }
2160
+ const existingConfig = state.tradeConfigurations[network][symbol] || {};
2161
+ state.tradeConfigurations[network][symbol] = {
2162
+ ...existingConfig,
2163
+ orderBookGrouping: grouping,
2164
+ };
2165
+ });
2166
+ }
2167
+ /**
2168
+ * Toggle watchlist status for a market
2169
+ * Watchlist markets are stored per network (testnet/mainnet)
2170
+ *
2171
+ * @param symbol
2172
+ */
2173
+ toggleWatchlistMarket(symbol) {
2174
+ const currentNetwork = this.state.isTestnet ? 'testnet' : 'mainnet';
2175
+ const currentWatchlist = this.state.watchlistMarkets[currentNetwork];
2176
+ const isWatchlisted = currentWatchlist.includes(symbol);
2177
+ this.debugLog('PerpsController: Toggling watchlist market', {
2178
+ timestamp: new Date().toISOString(),
2179
+ network: currentNetwork,
2180
+ symbol,
2181
+ action: isWatchlisted ? 'remove' : 'add',
2182
+ });
2183
+ this.update((state) => {
2184
+ if (isWatchlisted) {
2185
+ // Remove from watchlist
2186
+ state.watchlistMarkets[currentNetwork] = currentWatchlist.filter((s) => s !== symbol);
2187
+ }
2188
+ else {
2189
+ // Add to watchlist
2190
+ state.watchlistMarkets[currentNetwork] = [...currentWatchlist, symbol];
2191
+ }
2192
+ });
2193
+ }
2194
+ /**
2195
+ * Check if a market is in the watchlist on the current network
2196
+ *
2197
+ * @param symbol
2198
+ */
2199
+ isWatchlistMarket(symbol) {
2200
+ const currentNetwork = this.state.isTestnet ? 'testnet' : 'mainnet';
2201
+ return this.state.watchlistMarkets[currentNetwork].includes(symbol);
2202
+ }
2203
+ /**
2204
+ * Get all watchlist markets for the current network
2205
+ */
2206
+ getWatchlistMarkets() {
2207
+ const currentNetwork = this.state.isTestnet ? 'testnet' : 'mainnet';
2208
+ return this.state.watchlistMarkets[currentNetwork];
2209
+ }
2210
+ /**
2211
+ * Report order events to data lake API with retry (non-blocking)
2212
+ * Thin delegation to DataLakeService
2213
+ *
2214
+ * @param params
2215
+ * @param params.action
2216
+ * @param params.symbol
2217
+ * @param params.sl_price
2218
+ * @param params.tp_price
2219
+ * @param params.retryCount
2220
+ * @param params._traceId
2221
+ */
2222
+ async reportOrderToDataLake(params) {
2223
+ return this.dataLakeService.reportOrder({
2224
+ action: params.action,
2225
+ symbol: params.symbol,
2226
+ sl_price: params.sl_price,
2227
+ tp_price: params.tp_price,
2228
+ isTestnet: this.state.isTestnet,
2229
+ context: this.createServiceContext('reportOrderToDataLake', {
2230
+ messenger: this.messenger,
2231
+ }),
2232
+ retryCount: params.retryCount,
2233
+ _traceId: params._traceId,
2234
+ });
2235
+ }
2236
+ /**
2237
+ * Check if the controller is currently reinitializing
2238
+ *
2239
+ * @returns true if providers are being reinitialized
2240
+ */
2241
+ isCurrentlyReinitializing() {
2242
+ return this.isReinitializing;
85
2243
  }
86
2244
  }
87
2245
  exports.PerpsController = PerpsController;