@metamask/assets-controller 0.1.0

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 (252) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/LICENSE +20 -0
  3. package/README.md +15 -0
  4. package/dist/AssetsController.cjs +1057 -0
  5. package/dist/AssetsController.cjs.map +1 -0
  6. package/dist/AssetsController.d.cts +260 -0
  7. package/dist/AssetsController.d.cts.map +1 -0
  8. package/dist/AssetsController.d.mts +260 -0
  9. package/dist/AssetsController.d.mts.map +1 -0
  10. package/dist/AssetsController.mjs +1050 -0
  11. package/dist/AssetsController.mjs.map +1 -0
  12. package/dist/data-sources/AbstractDataSource.cjs +103 -0
  13. package/dist/data-sources/AbstractDataSource.cjs.map +1 -0
  14. package/dist/data-sources/AbstractDataSource.d.cts +92 -0
  15. package/dist/data-sources/AbstractDataSource.d.cts.map +1 -0
  16. package/dist/data-sources/AbstractDataSource.d.mts +92 -0
  17. package/dist/data-sources/AbstractDataSource.d.mts.map +1 -0
  18. package/dist/data-sources/AbstractDataSource.mjs +99 -0
  19. package/dist/data-sources/AbstractDataSource.mjs.map +1 -0
  20. package/dist/data-sources/AccountsApiDataSource.cjs +421 -0
  21. package/dist/data-sources/AccountsApiDataSource.cjs.map +1 -0
  22. package/dist/data-sources/AccountsApiDataSource.d.cts +103 -0
  23. package/dist/data-sources/AccountsApiDataSource.d.cts.map +1 -0
  24. package/dist/data-sources/AccountsApiDataSource.d.mts +103 -0
  25. package/dist/data-sources/AccountsApiDataSource.d.mts.map +1 -0
  26. package/dist/data-sources/AccountsApiDataSource.mjs +416 -0
  27. package/dist/data-sources/AccountsApiDataSource.mjs.map +1 -0
  28. package/dist/data-sources/BackendWebsocketDataSource.cjs +388 -0
  29. package/dist/data-sources/BackendWebsocketDataSource.cjs.map +1 -0
  30. package/dist/data-sources/BackendWebsocketDataSource.d.cts +95 -0
  31. package/dist/data-sources/BackendWebsocketDataSource.d.cts.map +1 -0
  32. package/dist/data-sources/BackendWebsocketDataSource.d.mts +95 -0
  33. package/dist/data-sources/BackendWebsocketDataSource.d.mts.map +1 -0
  34. package/dist/data-sources/BackendWebsocketDataSource.mjs +383 -0
  35. package/dist/data-sources/BackendWebsocketDataSource.mjs.map +1 -0
  36. package/dist/data-sources/PriceDataSource.cjs +343 -0
  37. package/dist/data-sources/PriceDataSource.cjs.map +1 -0
  38. package/dist/data-sources/PriceDataSource.d.cts +153 -0
  39. package/dist/data-sources/PriceDataSource.d.cts.map +1 -0
  40. package/dist/data-sources/PriceDataSource.d.mts +153 -0
  41. package/dist/data-sources/PriceDataSource.d.mts.map +1 -0
  42. package/dist/data-sources/PriceDataSource.mjs +339 -0
  43. package/dist/data-sources/PriceDataSource.mjs.map +1 -0
  44. package/dist/data-sources/RpcDataSource.cjs +829 -0
  45. package/dist/data-sources/RpcDataSource.cjs.map +1 -0
  46. package/dist/data-sources/RpcDataSource.d.cts +243 -0
  47. package/dist/data-sources/RpcDataSource.d.cts.map +1 -0
  48. package/dist/data-sources/RpcDataSource.d.mts +243 -0
  49. package/dist/data-sources/RpcDataSource.d.mts.map +1 -0
  50. package/dist/data-sources/RpcDataSource.mjs +820 -0
  51. package/dist/data-sources/RpcDataSource.mjs.map +1 -0
  52. package/dist/data-sources/SnapDataSource.cjs +463 -0
  53. package/dist/data-sources/SnapDataSource.cjs.map +1 -0
  54. package/dist/data-sources/SnapDataSource.d.cts +155 -0
  55. package/dist/data-sources/SnapDataSource.d.cts.map +1 -0
  56. package/dist/data-sources/SnapDataSource.d.mts +155 -0
  57. package/dist/data-sources/SnapDataSource.d.mts.map +1 -0
  58. package/dist/data-sources/SnapDataSource.mjs +456 -0
  59. package/dist/data-sources/SnapDataSource.mjs.map +1 -0
  60. package/dist/data-sources/TokenDataSource.cjs +211 -0
  61. package/dist/data-sources/TokenDataSource.cjs.map +1 -0
  62. package/dist/data-sources/TokenDataSource.d.cts +62 -0
  63. package/dist/data-sources/TokenDataSource.d.cts.map +1 -0
  64. package/dist/data-sources/TokenDataSource.d.mts +62 -0
  65. package/dist/data-sources/TokenDataSource.d.mts.map +1 -0
  66. package/dist/data-sources/TokenDataSource.mjs +207 -0
  67. package/dist/data-sources/TokenDataSource.mjs.map +1 -0
  68. package/dist/data-sources/evm-rpc-services/clients/MulticallClient.cjs +638 -0
  69. package/dist/data-sources/evm-rpc-services/clients/MulticallClient.cjs.map +1 -0
  70. package/dist/data-sources/evm-rpc-services/clients/MulticallClient.d.cts +47 -0
  71. package/dist/data-sources/evm-rpc-services/clients/MulticallClient.d.cts.map +1 -0
  72. package/dist/data-sources/evm-rpc-services/clients/MulticallClient.d.mts +47 -0
  73. package/dist/data-sources/evm-rpc-services/clients/MulticallClient.d.mts.map +1 -0
  74. package/dist/data-sources/evm-rpc-services/clients/MulticallClient.mjs +632 -0
  75. package/dist/data-sources/evm-rpc-services/clients/MulticallClient.mjs.map +1 -0
  76. package/dist/data-sources/evm-rpc-services/clients/index.cjs +8 -0
  77. package/dist/data-sources/evm-rpc-services/clients/index.cjs.map +1 -0
  78. package/dist/data-sources/evm-rpc-services/clients/index.d.cts +3 -0
  79. package/dist/data-sources/evm-rpc-services/clients/index.d.cts.map +1 -0
  80. package/dist/data-sources/evm-rpc-services/clients/index.d.mts +3 -0
  81. package/dist/data-sources/evm-rpc-services/clients/index.d.mts.map +1 -0
  82. package/dist/data-sources/evm-rpc-services/clients/index.mjs +2 -0
  83. package/dist/data-sources/evm-rpc-services/clients/index.mjs.map +1 -0
  84. package/dist/data-sources/evm-rpc-services/index.cjs +12 -0
  85. package/dist/data-sources/evm-rpc-services/index.cjs.map +1 -0
  86. package/dist/data-sources/evm-rpc-services/index.d.cts +5 -0
  87. package/dist/data-sources/evm-rpc-services/index.d.cts.map +1 -0
  88. package/dist/data-sources/evm-rpc-services/index.d.mts +5 -0
  89. package/dist/data-sources/evm-rpc-services/index.d.mts.map +1 -0
  90. package/dist/data-sources/evm-rpc-services/index.mjs +4 -0
  91. package/dist/data-sources/evm-rpc-services/index.mjs.map +1 -0
  92. package/dist/data-sources/evm-rpc-services/services/BalanceFetcher.cjs +202 -0
  93. package/dist/data-sources/evm-rpc-services/services/BalanceFetcher.cjs.map +1 -0
  94. package/dist/data-sources/evm-rpc-services/services/BalanceFetcher.d.cts +75 -0
  95. package/dist/data-sources/evm-rpc-services/services/BalanceFetcher.d.cts.map +1 -0
  96. package/dist/data-sources/evm-rpc-services/services/BalanceFetcher.d.mts +75 -0
  97. package/dist/data-sources/evm-rpc-services/services/BalanceFetcher.d.mts.map +1 -0
  98. package/dist/data-sources/evm-rpc-services/services/BalanceFetcher.mjs +198 -0
  99. package/dist/data-sources/evm-rpc-services/services/BalanceFetcher.mjs.map +1 -0
  100. package/dist/data-sources/evm-rpc-services/services/TokenDetector.cjs +214 -0
  101. package/dist/data-sources/evm-rpc-services/services/TokenDetector.cjs.map +1 -0
  102. package/dist/data-sources/evm-rpc-services/services/TokenDetector.d.cts +71 -0
  103. package/dist/data-sources/evm-rpc-services/services/TokenDetector.d.cts.map +1 -0
  104. package/dist/data-sources/evm-rpc-services/services/TokenDetector.d.mts +71 -0
  105. package/dist/data-sources/evm-rpc-services/services/TokenDetector.d.mts.map +1 -0
  106. package/dist/data-sources/evm-rpc-services/services/TokenDetector.mjs +210 -0
  107. package/dist/data-sources/evm-rpc-services/services/TokenDetector.mjs.map +1 -0
  108. package/dist/data-sources/evm-rpc-services/services/index.cjs +8 -0
  109. package/dist/data-sources/evm-rpc-services/services/index.cjs.map +1 -0
  110. package/dist/data-sources/evm-rpc-services/services/index.d.cts +3 -0
  111. package/dist/data-sources/evm-rpc-services/services/index.d.cts.map +1 -0
  112. package/dist/data-sources/evm-rpc-services/services/index.d.mts +3 -0
  113. package/dist/data-sources/evm-rpc-services/services/index.d.mts.map +1 -0
  114. package/dist/data-sources/evm-rpc-services/services/index.mjs +3 -0
  115. package/dist/data-sources/evm-rpc-services/services/index.mjs.map +1 -0
  116. package/dist/data-sources/evm-rpc-services/types/assets.cjs +3 -0
  117. package/dist/data-sources/evm-rpc-services/types/assets.cjs.map +1 -0
  118. package/dist/data-sources/evm-rpc-services/types/assets.d.cts +53 -0
  119. package/dist/data-sources/evm-rpc-services/types/assets.d.cts.map +1 -0
  120. package/dist/data-sources/evm-rpc-services/types/assets.d.mts +53 -0
  121. package/dist/data-sources/evm-rpc-services/types/assets.d.mts.map +1 -0
  122. package/dist/data-sources/evm-rpc-services/types/assets.mjs +2 -0
  123. package/dist/data-sources/evm-rpc-services/types/assets.mjs.map +1 -0
  124. package/dist/data-sources/evm-rpc-services/types/config.cjs +3 -0
  125. package/dist/data-sources/evm-rpc-services/types/config.cjs.map +1 -0
  126. package/dist/data-sources/evm-rpc-services/types/config.d.cts +18 -0
  127. package/dist/data-sources/evm-rpc-services/types/config.d.cts.map +1 -0
  128. package/dist/data-sources/evm-rpc-services/types/config.d.mts +18 -0
  129. package/dist/data-sources/evm-rpc-services/types/config.d.mts.map +1 -0
  130. package/dist/data-sources/evm-rpc-services/types/config.mjs +2 -0
  131. package/dist/data-sources/evm-rpc-services/types/config.mjs.map +1 -0
  132. package/dist/data-sources/evm-rpc-services/types/core.cjs +3 -0
  133. package/dist/data-sources/evm-rpc-services/types/core.cjs.map +1 -0
  134. package/dist/data-sources/evm-rpc-services/types/core.d.cts +19 -0
  135. package/dist/data-sources/evm-rpc-services/types/core.d.cts.map +1 -0
  136. package/dist/data-sources/evm-rpc-services/types/core.d.mts +19 -0
  137. package/dist/data-sources/evm-rpc-services/types/core.d.mts.map +1 -0
  138. package/dist/data-sources/evm-rpc-services/types/core.mjs +2 -0
  139. package/dist/data-sources/evm-rpc-services/types/core.mjs.map +1 -0
  140. package/dist/data-sources/evm-rpc-services/types/index.cjs +3 -0
  141. package/dist/data-sources/evm-rpc-services/types/index.cjs.map +1 -0
  142. package/dist/data-sources/evm-rpc-services/types/index.d.cts +7 -0
  143. package/dist/data-sources/evm-rpc-services/types/index.d.cts.map +1 -0
  144. package/dist/data-sources/evm-rpc-services/types/index.d.mts +7 -0
  145. package/dist/data-sources/evm-rpc-services/types/index.d.mts.map +1 -0
  146. package/dist/data-sources/evm-rpc-services/types/index.mjs +2 -0
  147. package/dist/data-sources/evm-rpc-services/types/index.mjs.map +1 -0
  148. package/dist/data-sources/evm-rpc-services/types/multicall.cjs +3 -0
  149. package/dist/data-sources/evm-rpc-services/types/multicall.cjs.map +1 -0
  150. package/dist/data-sources/evm-rpc-services/types/multicall.d.cts +24 -0
  151. package/dist/data-sources/evm-rpc-services/types/multicall.d.cts.map +1 -0
  152. package/dist/data-sources/evm-rpc-services/types/multicall.d.mts +24 -0
  153. package/dist/data-sources/evm-rpc-services/types/multicall.d.mts.map +1 -0
  154. package/dist/data-sources/evm-rpc-services/types/multicall.mjs +2 -0
  155. package/dist/data-sources/evm-rpc-services/types/multicall.mjs.map +1 -0
  156. package/dist/data-sources/evm-rpc-services/types/services.cjs +3 -0
  157. package/dist/data-sources/evm-rpc-services/types/services.cjs.map +1 -0
  158. package/dist/data-sources/evm-rpc-services/types/services.d.cts +74 -0
  159. package/dist/data-sources/evm-rpc-services/types/services.d.cts.map +1 -0
  160. package/dist/data-sources/evm-rpc-services/types/services.d.mts +74 -0
  161. package/dist/data-sources/evm-rpc-services/types/services.d.mts.map +1 -0
  162. package/dist/data-sources/evm-rpc-services/types/services.mjs +2 -0
  163. package/dist/data-sources/evm-rpc-services/types/services.mjs.map +1 -0
  164. package/dist/data-sources/evm-rpc-services/types/state.cjs +3 -0
  165. package/dist/data-sources/evm-rpc-services/types/state.cjs.map +1 -0
  166. package/dist/data-sources/evm-rpc-services/types/state.d.cts +52 -0
  167. package/dist/data-sources/evm-rpc-services/types/state.d.cts.map +1 -0
  168. package/dist/data-sources/evm-rpc-services/types/state.d.mts +52 -0
  169. package/dist/data-sources/evm-rpc-services/types/state.d.mts.map +1 -0
  170. package/dist/data-sources/evm-rpc-services/types/state.mjs +2 -0
  171. package/dist/data-sources/evm-rpc-services/types/state.mjs.map +1 -0
  172. package/dist/data-sources/evm-rpc-services/utils/batch.cjs +45 -0
  173. package/dist/data-sources/evm-rpc-services/utils/batch.cjs.map +1 -0
  174. package/dist/data-sources/evm-rpc-services/utils/batch.d.cts +28 -0
  175. package/dist/data-sources/evm-rpc-services/utils/batch.d.cts.map +1 -0
  176. package/dist/data-sources/evm-rpc-services/utils/batch.d.mts +28 -0
  177. package/dist/data-sources/evm-rpc-services/utils/batch.d.mts.map +1 -0
  178. package/dist/data-sources/evm-rpc-services/utils/batch.mjs +40 -0
  179. package/dist/data-sources/evm-rpc-services/utils/batch.mjs.map +1 -0
  180. package/dist/data-sources/evm-rpc-services/utils/index.cjs +7 -0
  181. package/dist/data-sources/evm-rpc-services/utils/index.cjs.map +1 -0
  182. package/dist/data-sources/evm-rpc-services/utils/index.d.cts +2 -0
  183. package/dist/data-sources/evm-rpc-services/utils/index.d.cts.map +1 -0
  184. package/dist/data-sources/evm-rpc-services/utils/index.d.mts +2 -0
  185. package/dist/data-sources/evm-rpc-services/utils/index.d.mts.map +1 -0
  186. package/dist/data-sources/evm-rpc-services/utils/index.mjs +2 -0
  187. package/dist/data-sources/evm-rpc-services/utils/index.mjs.map +1 -0
  188. package/dist/data-sources/index.cjs +33 -0
  189. package/dist/data-sources/index.cjs.map +1 -0
  190. package/dist/data-sources/index.d.cts +9 -0
  191. package/dist/data-sources/index.d.cts.map +1 -0
  192. package/dist/data-sources/index.d.mts +9 -0
  193. package/dist/data-sources/index.d.mts.map +1 -0
  194. package/dist/data-sources/index.mjs +15 -0
  195. package/dist/data-sources/index.mjs.map +1 -0
  196. package/dist/data-sources/initDataSources.cjs +215 -0
  197. package/dist/data-sources/initDataSources.cjs.map +1 -0
  198. package/dist/data-sources/initDataSources.d.cts +140 -0
  199. package/dist/data-sources/initDataSources.d.cts.map +1 -0
  200. package/dist/data-sources/initDataSources.d.mts +140 -0
  201. package/dist/data-sources/initDataSources.d.mts.map +1 -0
  202. package/dist/data-sources/initDataSources.mjs +210 -0
  203. package/dist/data-sources/initDataSources.mjs.map +1 -0
  204. package/dist/index.cjs +47 -0
  205. package/dist/index.cjs.map +1 -0
  206. package/dist/index.d.cts +21 -0
  207. package/dist/index.d.cts.map +1 -0
  208. package/dist/index.d.mts +21 -0
  209. package/dist/index.d.mts.map +1 -0
  210. package/dist/index.mjs +25 -0
  211. package/dist/index.mjs.map +1 -0
  212. package/dist/logger.cjs +7 -0
  213. package/dist/logger.cjs.map +1 -0
  214. package/dist/logger.d.cts +5 -0
  215. package/dist/logger.d.cts.map +1 -0
  216. package/dist/logger.d.mts +5 -0
  217. package/dist/logger.d.mts.map +1 -0
  218. package/dist/logger.mjs +4 -0
  219. package/dist/logger.mjs.map +1 -0
  220. package/dist/middlewares/DetectionMiddleware.cjs +101 -0
  221. package/dist/middlewares/DetectionMiddleware.cjs.map +1 -0
  222. package/dist/middlewares/DetectionMiddleware.d.cts +54 -0
  223. package/dist/middlewares/DetectionMiddleware.d.cts.map +1 -0
  224. package/dist/middlewares/DetectionMiddleware.d.mts +54 -0
  225. package/dist/middlewares/DetectionMiddleware.d.mts.map +1 -0
  226. package/dist/middlewares/DetectionMiddleware.mjs +97 -0
  227. package/dist/middlewares/DetectionMiddleware.mjs.map +1 -0
  228. package/dist/middlewares/index.cjs +6 -0
  229. package/dist/middlewares/index.cjs.map +1 -0
  230. package/dist/middlewares/index.d.cts +2 -0
  231. package/dist/middlewares/index.d.cts.map +1 -0
  232. package/dist/middlewares/index.d.mts +2 -0
  233. package/dist/middlewares/index.d.mts.map +1 -0
  234. package/dist/middlewares/index.mjs +2 -0
  235. package/dist/middlewares/index.mjs.map +1 -0
  236. package/dist/types.cjs +39 -0
  237. package/dist/types.cjs.map +1 -0
  238. package/dist/types.d.cts +433 -0
  239. package/dist/types.d.cts.map +1 -0
  240. package/dist/types.d.mts +433 -0
  241. package/dist/types.d.mts.map +1 -0
  242. package/dist/types.mjs +35 -0
  243. package/dist/types.mjs.map +1 -0
  244. package/dist/utils.cjs +28 -0
  245. package/dist/utils.cjs.map +1 -0
  246. package/dist/utils.d.cts +13 -0
  247. package/dist/utils.d.cts.map +1 -0
  248. package/dist/utils.d.mts +13 -0
  249. package/dist/utils.d.mts.map +1 -0
  250. package/dist/utils.mjs +24 -0
  251. package/dist/utils.mjs.map +1 -0
  252. package/package.json +93 -0
@@ -0,0 +1,95 @@
1
+ import type { BackendWebSocketServiceActions, BackendWebSocketServiceEvents } from "@metamask/core-backend";
2
+ import type { Messenger } from "@metamask/messenger";
3
+ import { AbstractDataSource } from "./AbstractDataSource.mjs";
4
+ import type { DataSourceState, SubscriptionRequest } from "./AbstractDataSource.mjs";
5
+ import type { ChainId, DataResponse } from "../types.mjs";
6
+ declare const CONTROLLER_NAME = "BackendWebsocketDataSource";
7
+ export type BackendWebsocketDataSourceGetActiveChainsAction = {
8
+ type: 'BackendWebsocketDataSource:getActiveChains';
9
+ handler: () => Promise<ChainId[]>;
10
+ };
11
+ export type BackendWebsocketDataSourceSubscribeAction = {
12
+ type: 'BackendWebsocketDataSource:subscribe';
13
+ handler: (request: SubscriptionRequest) => Promise<void>;
14
+ };
15
+ export type BackendWebsocketDataSourceUnsubscribeAction = {
16
+ type: 'BackendWebsocketDataSource:unsubscribe';
17
+ handler: (subscriptionId: string) => Promise<void>;
18
+ };
19
+ export type BackendWebsocketDataSourceActions = BackendWebsocketDataSourceGetActiveChainsAction | BackendWebsocketDataSourceSubscribeAction | BackendWebsocketDataSourceUnsubscribeAction;
20
+ export type BackendWebsocketDataSourceActiveChainsChangedEvent = {
21
+ type: 'BackendWebsocketDataSource:activeChainsUpdated';
22
+ payload: [ChainId[]];
23
+ };
24
+ export type BackendWebsocketDataSourceAssetsUpdatedEvent = {
25
+ type: 'BackendWebsocketDataSource:assetsUpdated';
26
+ payload: [DataResponse, string | undefined];
27
+ };
28
+ export type BackendWebsocketDataSourceEvents = BackendWebsocketDataSourceActiveChainsChangedEvent | BackendWebsocketDataSourceAssetsUpdatedEvent;
29
+ type AssetsControllerActiveChainsUpdateAction = {
30
+ type: 'AssetsController:activeChainsUpdate';
31
+ handler: (dataSourceId: string, activeChains: ChainId[]) => void;
32
+ };
33
+ type AssetsControllerAssetsUpdateAction = {
34
+ type: 'AssetsController:assetsUpdate';
35
+ handler: (response: DataResponse, sourceId: string) => Promise<void>;
36
+ };
37
+ export type BackendWebsocketDataSourceAllowedActions = BackendWebSocketServiceActions | AssetsControllerActiveChainsUpdateAction | AssetsControllerAssetsUpdateAction;
38
+ export type AccountsApiDataSourceActiveChainsChangedEvent = {
39
+ type: 'AccountsApiDataSource:activeChainsUpdated';
40
+ payload: [ChainId[]];
41
+ };
42
+ export type BackendWebsocketDataSourceAllowedEvents = BackendWebSocketServiceEvents | AccountsApiDataSourceActiveChainsChangedEvent;
43
+ export type BackendWebsocketDataSourceMessenger = Messenger<typeof CONTROLLER_NAME, BackendWebsocketDataSourceActions | BackendWebsocketDataSourceAllowedActions, BackendWebsocketDataSourceEvents | BackendWebsocketDataSourceAllowedEvents>;
44
+ export type BackendWebsocketDataSourceState = DataSourceState;
45
+ export type BackendWebsocketDataSourceOptions = {
46
+ messenger: BackendWebsocketDataSourceMessenger;
47
+ state?: Partial<BackendWebsocketDataSourceState>;
48
+ };
49
+ /**
50
+ * Data source for receiving real-time balance updates via WebSocket.
51
+ *
52
+ * This data source connects directly to BackendWebSocketService to receive
53
+ * push notifications for account balance changes. Unlike AccountsApiDataSource
54
+ * which polls for data, this provides instant updates.
55
+ *
56
+ * Uses Messenger pattern for all interactions:
57
+ * - Calls BackendWebSocketService methods via messenger actions
58
+ * - Exposes its own actions for AssetsController to call
59
+ * - Publishes events for AssetsController to subscribe to
60
+ *
61
+ * Actions exposed:
62
+ * - BackendWebsocketDataSource:getActiveChains
63
+ * - BackendWebsocketDataSource:subscribe
64
+ * - BackendWebsocketDataSource:unsubscribe
65
+ *
66
+ * Events published:
67
+ * - BackendWebsocketDataSource:activeChainsUpdated
68
+ * - BackendWebsocketDataSource:assetsUpdated
69
+ *
70
+ * Actions called (from BackendWebSocketService):
71
+ * - BackendWebSocketService:subscribe
72
+ * - BackendWebSocketService:getConnectionInfo
73
+ * - BackendWebSocketService:findSubscriptionsByChannelPrefix
74
+ */
75
+ export declare class BackendWebsocketDataSource extends AbstractDataSource<typeof CONTROLLER_NAME, BackendWebsocketDataSourceState> {
76
+ #private;
77
+ constructor(options: BackendWebsocketDataSourceOptions);
78
+ /**
79
+ * Update active chains when AccountsApiDataSource reports new supported chains.
80
+ *
81
+ * @param chains - Array of supported chain IDs.
82
+ */
83
+ updateSupportedChains(chains: ChainId[]): void;
84
+ subscribe(subscriptionRequest: SubscriptionRequest): Promise<void>;
85
+ destroy(): void;
86
+ }
87
+ /**
88
+ * Creates a BackendWebsocketDataSource instance.
89
+ *
90
+ * @param options - Configuration options for the data source.
91
+ * @returns A new BackendWebsocketDataSource instance.
92
+ */
93
+ export declare function createBackendWebsocketDataSource(options: BackendWebsocketDataSourceOptions): BackendWebsocketDataSource;
94
+ export {};
95
+ //# sourceMappingURL=BackendWebsocketDataSource.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BackendWebsocketDataSource.d.mts","sourceRoot":"","sources":["../../src/data-sources/BackendWebsocketDataSource.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,383 @@
1
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
2
+ if (kind === "m") throw new TypeError("Private method is not writable");
3
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
4
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
5
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
6
+ };
7
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
8
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
9
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
10
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
11
+ };
12
+ var _BackendWebsocketDataSource_instances, _BackendWebsocketDataSource_messenger, _BackendWebsocketDataSource_wsSubscriptions, _BackendWebsocketDataSource_pendingSubscriptions, _BackendWebsocketDataSource_subscriptionRequests, _BackendWebsocketDataSource_registerActionHandlers, _BackendWebsocketDataSource_subscribeToEvents, _BackendWebsocketDataSource_handleDisconnect, _BackendWebsocketDataSource_processPendingSubscriptions, _BackendWebsocketDataSource_handleNotification, _BackendWebsocketDataSource_processBalanceUpdates;
13
+ import { AbstractDataSource } from "./AbstractDataSource.mjs";
14
+ import { projectLogger, createModuleLogger } from "../logger.mjs";
15
+ // ============================================================================
16
+ // CONSTANTS
17
+ // ============================================================================
18
+ const CONTROLLER_NAME = 'BackendWebsocketDataSource';
19
+ const CHANNEL_TYPE = 'account-activity.v1';
20
+ const log = createModuleLogger(projectLogger, CONTROLLER_NAME);
21
+ const defaultState = {
22
+ activeChains: [],
23
+ };
24
+ // ============================================================================
25
+ // HELPER FUNCTIONS
26
+ // ============================================================================
27
+ /**
28
+ * Extract namespace from a CAIP-2 chain ID.
29
+ * E.g., "eip155:1" -> "eip155", "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp" -> "solana"
30
+ *
31
+ * @param chainId - The CAIP-2 chain ID to extract namespace from.
32
+ * @returns The namespace portion of the chain ID.
33
+ */
34
+ function extractNamespace(chainId) {
35
+ const [namespace] = chainId.split(':');
36
+ return namespace;
37
+ }
38
+ /**
39
+ * Get unique namespaces from chain IDs.
40
+ *
41
+ * @param chainIds - Array of CAIP-2 chain IDs.
42
+ * @returns Array of unique namespaces.
43
+ */
44
+ function getUniqueNamespaces(chainIds) {
45
+ const namespaces = new Set();
46
+ for (const chainId of chainIds) {
47
+ namespaces.add(extractNamespace(chainId));
48
+ }
49
+ return Array.from(namespaces);
50
+ }
51
+ /**
52
+ * Build WebSocket channel name for account activity using CAIP-10 wildcard format.
53
+ * Uses 0 as the chain reference to subscribe to all chains in the namespace.
54
+ * Format: account-activity.v1.eip155:0:0x1234... (all EVM chains)
55
+ * Format: account-activity.v1.solana:0:ABC123... (all Solana chains)
56
+ *
57
+ * @param namespace - The chain namespace (e.g., "eip155", "solana").
58
+ * @param address - The account address.
59
+ * @returns The WebSocket channel name.
60
+ */
61
+ function buildAccountActivityChannel(namespace, address) {
62
+ return `${CHANNEL_TYPE}.${namespace}:0:${address.toLowerCase()}`;
63
+ }
64
+ // Note: AccountActivityMessage and BalanceUpdate types are imported from @metamask/core-backend
65
+ // ============================================================================
66
+ // BACKEND WEBSOCKET DATA SOURCE
67
+ // ============================================================================
68
+ /**
69
+ * Data source for receiving real-time balance updates via WebSocket.
70
+ *
71
+ * This data source connects directly to BackendWebSocketService to receive
72
+ * push notifications for account balance changes. Unlike AccountsApiDataSource
73
+ * which polls for data, this provides instant updates.
74
+ *
75
+ * Uses Messenger pattern for all interactions:
76
+ * - Calls BackendWebSocketService methods via messenger actions
77
+ * - Exposes its own actions for AssetsController to call
78
+ * - Publishes events for AssetsController to subscribe to
79
+ *
80
+ * Actions exposed:
81
+ * - BackendWebsocketDataSource:getActiveChains
82
+ * - BackendWebsocketDataSource:subscribe
83
+ * - BackendWebsocketDataSource:unsubscribe
84
+ *
85
+ * Events published:
86
+ * - BackendWebsocketDataSource:activeChainsUpdated
87
+ * - BackendWebsocketDataSource:assetsUpdated
88
+ *
89
+ * Actions called (from BackendWebSocketService):
90
+ * - BackendWebSocketService:subscribe
91
+ * - BackendWebSocketService:getConnectionInfo
92
+ * - BackendWebSocketService:findSubscriptionsByChannelPrefix
93
+ */
94
+ export class BackendWebsocketDataSource extends AbstractDataSource {
95
+ constructor(options) {
96
+ super(CONTROLLER_NAME, {
97
+ ...defaultState,
98
+ ...options.state,
99
+ });
100
+ _BackendWebsocketDataSource_instances.add(this);
101
+ _BackendWebsocketDataSource_messenger.set(this, void 0);
102
+ /** WebSocket subscriptions by our internal subscription ID */
103
+ _BackendWebsocketDataSource_wsSubscriptions.set(this, new Map());
104
+ /** Pending subscription requests to process when WebSocket connects */
105
+ _BackendWebsocketDataSource_pendingSubscriptions.set(this, new Map());
106
+ /** Store original subscription requests for reconnection */
107
+ _BackendWebsocketDataSource_subscriptionRequests.set(this, new Map());
108
+ __classPrivateFieldSet(this, _BackendWebsocketDataSource_messenger, options.messenger, "f");
109
+ __classPrivateFieldGet(this, _BackendWebsocketDataSource_instances, "m", _BackendWebsocketDataSource_registerActionHandlers).call(this);
110
+ __classPrivateFieldGet(this, _BackendWebsocketDataSource_instances, "m", _BackendWebsocketDataSource_subscribeToEvents).call(this);
111
+ }
112
+ // ============================================================================
113
+ // ACTIVE CHAINS
114
+ // ============================================================================
115
+ /**
116
+ * Update active chains when AccountsApiDataSource reports new supported chains.
117
+ *
118
+ * @param chains - Array of supported chain IDs.
119
+ */
120
+ updateSupportedChains(chains) {
121
+ this.updateActiveChains(chains, (updatedChains) => __classPrivateFieldGet(this, _BackendWebsocketDataSource_messenger, "f").call('AssetsController:activeChainsUpdate', CONTROLLER_NAME, updatedChains));
122
+ }
123
+ // ============================================================================
124
+ // SUBSCRIBE
125
+ // ============================================================================
126
+ async subscribe(subscriptionRequest) {
127
+ const { request, subscriptionId, isUpdate } = subscriptionRequest;
128
+ // Filter to active chains only
129
+ const chainsToSubscribe = request.chainIds.filter((chainId) => this.state.activeChains.includes(chainId));
130
+ const addresses = request.accounts.map((a) => a.address);
131
+ if (chainsToSubscribe.length === 0) {
132
+ return;
133
+ }
134
+ // Check WebSocket connection status
135
+ try {
136
+ const connectionInfo = __classPrivateFieldGet(this, _BackendWebsocketDataSource_messenger, "f").call('BackendWebSocketService:getConnectionInfo');
137
+ if (connectionInfo.state !== 'connected') {
138
+ // Store the subscription request to process when WebSocket connects
139
+ __classPrivateFieldGet(this, _BackendWebsocketDataSource_pendingSubscriptions, "f").set(subscriptionId, subscriptionRequest);
140
+ return;
141
+ }
142
+ }
143
+ catch {
144
+ // Store anyway - will be processed when we can connect
145
+ __classPrivateFieldGet(this, _BackendWebsocketDataSource_pendingSubscriptions, "f").set(subscriptionId, subscriptionRequest);
146
+ return;
147
+ }
148
+ // Remove from pending if it was there (we're processing it now)
149
+ __classPrivateFieldGet(this, _BackendWebsocketDataSource_pendingSubscriptions, "f").delete(subscriptionId);
150
+ // Handle subscription update
151
+ if (isUpdate) {
152
+ const existing = this.activeSubscriptions.get(subscriptionId);
153
+ if (existing) {
154
+ // Check if accounts changed - if so, we need to re-subscribe to different channels
155
+ const existingAddresses = existing.addresses ?? [];
156
+ const addressesChanged = addresses.length !== existingAddresses.length ||
157
+ addresses.some((addr) => !existingAddresses.includes(addr));
158
+ if (!addressesChanged) {
159
+ // Only chains changed - just update chains and return
160
+ existing.chains = chainsToSubscribe;
161
+ return;
162
+ }
163
+ // Accounts changed - fall through to re-subscribe with new channels
164
+ }
165
+ }
166
+ // Clean up existing subscription if any
167
+ await this.unsubscribe(subscriptionId);
168
+ // Extract unique namespaces from chains (e.g., eip155, solana)
169
+ const namespaces = getUniqueNamespaces(chainsToSubscribe);
170
+ // Build channel names using CAIP-10 wildcard format
171
+ const channels = [];
172
+ for (const namespace of namespaces) {
173
+ for (const address of addresses) {
174
+ channels.push(buildAccountActivityChannel(namespace, address));
175
+ }
176
+ }
177
+ try {
178
+ // Create WebSocket subscription
179
+ const wsSubscription = await __classPrivateFieldGet(this, _BackendWebsocketDataSource_messenger, "f").call('BackendWebSocketService:subscribe', {
180
+ channels,
181
+ channelType: CHANNEL_TYPE,
182
+ callback: (notification) => {
183
+ __classPrivateFieldGet(this, _BackendWebsocketDataSource_instances, "m", _BackendWebsocketDataSource_handleNotification).call(this, notification, request);
184
+ },
185
+ });
186
+ // Store WebSocket subscription
187
+ __classPrivateFieldGet(this, _BackendWebsocketDataSource_wsSubscriptions, "f").set(subscriptionId, wsSubscription);
188
+ // Store in abstract class tracking
189
+ this.activeSubscriptions.set(subscriptionId, {
190
+ cleanup: () => {
191
+ const wsSub = __classPrivateFieldGet(this, _BackendWebsocketDataSource_wsSubscriptions, "f").get(subscriptionId);
192
+ if (wsSub) {
193
+ wsSub.unsubscribe().catch((unsubErr) => {
194
+ log('Error unsubscribing', { subscriptionId, error: unsubErr });
195
+ });
196
+ __classPrivateFieldGet(this, _BackendWebsocketDataSource_wsSubscriptions, "f").delete(subscriptionId);
197
+ }
198
+ // Also clean up the stored request
199
+ __classPrivateFieldGet(this, _BackendWebsocketDataSource_subscriptionRequests, "f").delete(subscriptionId);
200
+ },
201
+ chains: chainsToSubscribe,
202
+ addresses,
203
+ });
204
+ // Store original request for reconnection
205
+ __classPrivateFieldGet(this, _BackendWebsocketDataSource_subscriptionRequests, "f").set(subscriptionId, subscriptionRequest);
206
+ }
207
+ catch (error) {
208
+ log('WebSocket subscription FAILED', {
209
+ subscriptionId,
210
+ error,
211
+ chains: chainsToSubscribe,
212
+ });
213
+ }
214
+ }
215
+ // ============================================================================
216
+ // CLEANUP
217
+ // ============================================================================
218
+ destroy() {
219
+ // Clean up WebSocket subscriptions
220
+ // Convert to array first to avoid modifying map during iteration
221
+ const subscriptions = [...__classPrivateFieldGet(this, _BackendWebsocketDataSource_wsSubscriptions, "f").values()];
222
+ for (const wsSub of subscriptions) {
223
+ try {
224
+ // Fire and forget - don't await in destroy
225
+ wsSub.unsubscribe().catch(() => {
226
+ // Ignore errors during cleanup
227
+ });
228
+ }
229
+ catch {
230
+ // Ignore errors during cleanup
231
+ }
232
+ }
233
+ __classPrivateFieldGet(this, _BackendWebsocketDataSource_wsSubscriptions, "f").clear();
234
+ // Clean up base class subscriptions
235
+ super.destroy();
236
+ }
237
+ }
238
+ _BackendWebsocketDataSource_messenger = new WeakMap(), _BackendWebsocketDataSource_wsSubscriptions = new WeakMap(), _BackendWebsocketDataSource_pendingSubscriptions = new WeakMap(), _BackendWebsocketDataSource_subscriptionRequests = new WeakMap(), _BackendWebsocketDataSource_instances = new WeakSet(), _BackendWebsocketDataSource_registerActionHandlers = function _BackendWebsocketDataSource_registerActionHandlers() {
239
+ const getActiveChainsHandler = async () => this.getActiveChains();
240
+ const subscribeHandler = async (request) => this.subscribe(request);
241
+ const unsubscribeHandler = async (subscriptionId) => this.unsubscribe(subscriptionId);
242
+ __classPrivateFieldGet(this, _BackendWebsocketDataSource_messenger, "f").registerActionHandler('BackendWebsocketDataSource:getActiveChains', getActiveChainsHandler);
243
+ __classPrivateFieldGet(this, _BackendWebsocketDataSource_messenger, "f").registerActionHandler('BackendWebsocketDataSource:subscribe', subscribeHandler);
244
+ __classPrivateFieldGet(this, _BackendWebsocketDataSource_messenger, "f").registerActionHandler('BackendWebsocketDataSource:unsubscribe', unsubscribeHandler);
245
+ }, _BackendWebsocketDataSource_subscribeToEvents = function _BackendWebsocketDataSource_subscribeToEvents() {
246
+ // Listen for WebSocket connection state changes
247
+ __classPrivateFieldGet(this, _BackendWebsocketDataSource_messenger, "f").subscribe('BackendWebSocketService:connectionStateChanged', (connectionInfo) => {
248
+ if (connectionInfo.state === 'connected') {
249
+ // WebSocket connected - process any pending subscriptions
250
+ __classPrivateFieldGet(this, _BackendWebsocketDataSource_instances, "m", _BackendWebsocketDataSource_processPendingSubscriptions).call(this).catch(console.error);
251
+ }
252
+ else if (connectionInfo.state === 'disconnected') {
253
+ // When disconnected, all subscriptions are cleared server-side
254
+ // Move active subscriptions to pending for re-subscription on reconnect
255
+ __classPrivateFieldGet(this, _BackendWebsocketDataSource_instances, "m", _BackendWebsocketDataSource_handleDisconnect).call(this);
256
+ }
257
+ });
258
+ // Listen for AccountsApiDataSource active chains changes
259
+ // This keeps BackendWebsocketDataSource in sync with the supported chains
260
+ // since both use the same backend infrastructure
261
+ __classPrivateFieldGet(this, _BackendWebsocketDataSource_messenger, "f").subscribe('AccountsApiDataSource:activeChainsUpdated', (chains) => {
262
+ this.updateActiveChains(chains, (updatedChains) => __classPrivateFieldGet(this, _BackendWebsocketDataSource_messenger, "f").call('AssetsController:activeChainsUpdate', CONTROLLER_NAME, updatedChains));
263
+ });
264
+ }, _BackendWebsocketDataSource_handleDisconnect = function _BackendWebsocketDataSource_handleDisconnect() {
265
+ log('WebSocket disconnected, preserving subscriptions for reconnect', {
266
+ activeSubscriptionCount: this.activeSubscriptions.size,
267
+ wsSubscriptionCount: __classPrivateFieldGet(this, _BackendWebsocketDataSource_wsSubscriptions, "f").size,
268
+ });
269
+ // Move active subscriptions to pending for re-subscription
270
+ for (const [subscriptionId] of this.activeSubscriptions) {
271
+ const originalRequest = __classPrivateFieldGet(this, _BackendWebsocketDataSource_subscriptionRequests, "f").get(subscriptionId);
272
+ if (originalRequest) {
273
+ // Mark as update since it was previously active
274
+ __classPrivateFieldGet(this, _BackendWebsocketDataSource_pendingSubscriptions, "f").set(subscriptionId, {
275
+ ...originalRequest,
276
+ isUpdate: false, // Treat as new subscription since server cleared it
277
+ });
278
+ }
279
+ }
280
+ // Clear WebSocket subscriptions (server-side already cleared)
281
+ __classPrivateFieldGet(this, _BackendWebsocketDataSource_wsSubscriptions, "f").clear();
282
+ // Clear active subscriptions (they're no longer valid)
283
+ this.activeSubscriptions.clear();
284
+ }, _BackendWebsocketDataSource_processPendingSubscriptions =
285
+ /**
286
+ * Process any pending subscriptions that were queued while WebSocket was disconnected.
287
+ */
288
+ async function _BackendWebsocketDataSource_processPendingSubscriptions() {
289
+ if (__classPrivateFieldGet(this, _BackendWebsocketDataSource_pendingSubscriptions, "f").size === 0) {
290
+ return;
291
+ }
292
+ // Process all pending subscriptions
293
+ const pendingEntries = Array.from(__classPrivateFieldGet(this, _BackendWebsocketDataSource_pendingSubscriptions, "f").entries());
294
+ for (const [subscriptionId, request] of pendingEntries) {
295
+ try {
296
+ // Remove from pending before processing to avoid infinite loop
297
+ __classPrivateFieldGet(this, _BackendWebsocketDataSource_pendingSubscriptions, "f").delete(subscriptionId);
298
+ await this.subscribe(request);
299
+ }
300
+ catch (error) {
301
+ log('Failed to process pending subscription', {
302
+ subscriptionId,
303
+ error,
304
+ });
305
+ }
306
+ }
307
+ }, _BackendWebsocketDataSource_handleNotification = function _BackendWebsocketDataSource_handleNotification(notification, request) {
308
+ try {
309
+ const activityMessage = notification.data;
310
+ const { address, tx, updates } = activityMessage;
311
+ if (!address || !tx || !updates) {
312
+ return;
313
+ }
314
+ // Extract chain ID from transaction (CAIP-2 format, e.g., "eip155:8453")
315
+ const chainId = tx.chain;
316
+ // Find matching account in request
317
+ const account = request.accounts.find((a) => a.address.toLowerCase() === address.toLowerCase());
318
+ if (!account) {
319
+ return;
320
+ }
321
+ const accountId = account.id;
322
+ // Process all balance updates from the activity message
323
+ const response = __classPrivateFieldGet(this, _BackendWebsocketDataSource_instances, "m", _BackendWebsocketDataSource_processBalanceUpdates).call(this, updates, chainId, accountId);
324
+ if (Object.keys(response).length > 0) {
325
+ // Report update to AssetsController
326
+ __classPrivateFieldGet(this, _BackendWebsocketDataSource_messenger, "f")
327
+ .call('AssetsController:assetsUpdate', response, CONTROLLER_NAME)
328
+ .catch(console.error);
329
+ }
330
+ }
331
+ catch (error) {
332
+ log('Error handling notification', error);
333
+ }
334
+ }, _BackendWebsocketDataSource_processBalanceUpdates = function _BackendWebsocketDataSource_processBalanceUpdates(updates, _chainId, accountId) {
335
+ const assetsBalance = {
336
+ [accountId]: {},
337
+ };
338
+ const assetsMetadata = {};
339
+ for (const update of updates) {
340
+ const { asset, postBalance } = update;
341
+ if (!asset || !postBalance) {
342
+ continue;
343
+ }
344
+ // Asset type is in CAIP format: "eip155:1/erc20:0x..." or "eip155:1/slip44:60"
345
+ // We can use it directly as the asset ID
346
+ const assetId = asset.type;
347
+ // Determine token type from asset type string
348
+ const isNative = asset.type.includes('/slip44:');
349
+ const tokenType = isNative ? 'native' : 'erc20';
350
+ // Parse balance amount (already in hex format like "0xc350")
351
+ const balanceAmount = postBalance.amount.startsWith('0x')
352
+ ? BigInt(postBalance.amount).toString()
353
+ : postBalance.amount;
354
+ assetsBalance[accountId][assetId] = {
355
+ amount: balanceAmount,
356
+ };
357
+ assetsMetadata[assetId] = {
358
+ type: tokenType,
359
+ symbol: asset.unit,
360
+ name: asset.unit, // Use unit as name (actual name may not be in the message)
361
+ decimals: asset.decimals,
362
+ };
363
+ }
364
+ const response = {};
365
+ if (Object.keys(assetsBalance[accountId]).length > 0) {
366
+ response.assetsBalance = assetsBalance;
367
+ response.assetsMetadata = assetsMetadata;
368
+ }
369
+ return response;
370
+ };
371
+ // ============================================================================
372
+ // FACTORY FUNCTION
373
+ // ============================================================================
374
+ /**
375
+ * Creates a BackendWebsocketDataSource instance.
376
+ *
377
+ * @param options - Configuration options for the data source.
378
+ * @returns A new BackendWebsocketDataSource instance.
379
+ */
380
+ export function createBackendWebsocketDataSource(options) {
381
+ return new BackendWebsocketDataSource(options);
382
+ }
383
+ //# sourceMappingURL=BackendWebsocketDataSource.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BackendWebsocketDataSource.mjs","sourceRoot":"","sources":["../../src/data-sources/BackendWebsocketDataSource.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n BackendWebSocketServiceActions,\n BackendWebSocketServiceEvents,\n ServerNotificationMessage,\n WebSocketSubscription,\n WebSocketState,\n AccountActivityMessage,\n BalanceUpdate,\n} from '@metamask/core-backend';\nimport type { Messenger } from '@metamask/messenger';\n\nimport { AbstractDataSource } from './AbstractDataSource';\nimport type {\n DataSourceState,\n SubscriptionRequest,\n} from './AbstractDataSource';\nimport { projectLogger, createModuleLogger } from '../logger';\nimport type {\n ChainId,\n Caip19AssetId,\n AssetMetadata,\n AssetBalance,\n DataRequest,\n DataResponse,\n} from '../types';\n\n// ============================================================================\n// CONSTANTS\n// ============================================================================\n\nconst CONTROLLER_NAME = 'BackendWebsocketDataSource';\nconst CHANNEL_TYPE = 'account-activity.v1';\n\nconst log = createModuleLogger(projectLogger, CONTROLLER_NAME);\n\n// ============================================================================\n// MESSENGER TYPES\n// ============================================================================\n\n// Action types that BackendWebsocketDataSource exposes\nexport type BackendWebsocketDataSourceGetActiveChainsAction = {\n type: 'BackendWebsocketDataSource:getActiveChains';\n handler: () => Promise<ChainId[]>;\n};\n\nexport type BackendWebsocketDataSourceSubscribeAction = {\n type: 'BackendWebsocketDataSource:subscribe';\n handler: (request: SubscriptionRequest) => Promise<void>;\n};\n\nexport type BackendWebsocketDataSourceUnsubscribeAction = {\n type: 'BackendWebsocketDataSource:unsubscribe';\n handler: (subscriptionId: string) => Promise<void>;\n};\n\nexport type BackendWebsocketDataSourceActions =\n | BackendWebsocketDataSourceGetActiveChainsAction\n | BackendWebsocketDataSourceSubscribeAction\n | BackendWebsocketDataSourceUnsubscribeAction;\n\n// Event types that BackendWebsocketDataSource publishes\nexport type BackendWebsocketDataSourceActiveChainsChangedEvent = {\n type: 'BackendWebsocketDataSource:activeChainsUpdated';\n payload: [ChainId[]];\n};\n\nexport type BackendWebsocketDataSourceAssetsUpdatedEvent = {\n type: 'BackendWebsocketDataSource:assetsUpdated';\n payload: [DataResponse, string | undefined];\n};\n\nexport type BackendWebsocketDataSourceEvents =\n | BackendWebsocketDataSourceActiveChainsChangedEvent\n | BackendWebsocketDataSourceAssetsUpdatedEvent;\n\n// Actions to report to AssetsController\ntype AssetsControllerActiveChainsUpdateAction = {\n type: 'AssetsController:activeChainsUpdate';\n handler: (dataSourceId: string, activeChains: ChainId[]) => void;\n};\n\ntype AssetsControllerAssetsUpdateAction = {\n type: 'AssetsController:assetsUpdate';\n handler: (response: DataResponse, sourceId: string) => Promise<void>;\n};\n\n// Allowed actions that BackendWebsocketDataSource can call\nexport type BackendWebsocketDataSourceAllowedActions =\n | BackendWebSocketServiceActions\n | AssetsControllerActiveChainsUpdateAction\n | AssetsControllerAssetsUpdateAction;\n\n// Event type from AccountsApiDataSource that we subscribe to\nexport type AccountsApiDataSourceActiveChainsChangedEvent = {\n type: 'AccountsApiDataSource:activeChainsUpdated';\n payload: [ChainId[]];\n};\n\n// Allowed events that BackendWebsocketDataSource can subscribe to\nexport type BackendWebsocketDataSourceAllowedEvents =\n | BackendWebSocketServiceEvents\n | AccountsApiDataSourceActiveChainsChangedEvent;\n\nexport type BackendWebsocketDataSourceMessenger = Messenger<\n typeof CONTROLLER_NAME,\n BackendWebsocketDataSourceActions | BackendWebsocketDataSourceAllowedActions,\n BackendWebsocketDataSourceEvents | BackendWebsocketDataSourceAllowedEvents\n>;\n\n// ============================================================================\n// STATE\n// ============================================================================\n\nexport type BackendWebsocketDataSourceState = DataSourceState;\n\nconst defaultState: BackendWebsocketDataSourceState = {\n activeChains: [],\n};\n\n// ============================================================================\n// OPTIONS\n// ============================================================================\n\nexport type BackendWebsocketDataSourceOptions = {\n messenger: BackendWebsocketDataSourceMessenger;\n state?: Partial<BackendWebsocketDataSourceState>;\n};\n\n// ============================================================================\n// HELPER FUNCTIONS\n// ============================================================================\n\n/**\n * Extract namespace from a CAIP-2 chain ID.\n * E.g., \"eip155:1\" -> \"eip155\", \"solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp\" -> \"solana\"\n *\n * @param chainId - The CAIP-2 chain ID to extract namespace from.\n * @returns The namespace portion of the chain ID.\n */\nfunction extractNamespace(chainId: ChainId): string {\n const [namespace] = chainId.split(':');\n return namespace;\n}\n\n/**\n * Get unique namespaces from chain IDs.\n *\n * @param chainIds - Array of CAIP-2 chain IDs.\n * @returns Array of unique namespaces.\n */\nfunction getUniqueNamespaces(chainIds: ChainId[]): string[] {\n const namespaces = new Set<string>();\n for (const chainId of chainIds) {\n namespaces.add(extractNamespace(chainId));\n }\n return Array.from(namespaces);\n}\n\n/**\n * Build WebSocket channel name for account activity using CAIP-10 wildcard format.\n * Uses 0 as the chain reference to subscribe to all chains in the namespace.\n * Format: account-activity.v1.eip155:0:0x1234... (all EVM chains)\n * Format: account-activity.v1.solana:0:ABC123... (all Solana chains)\n *\n * @param namespace - The chain namespace (e.g., \"eip155\", \"solana\").\n * @param address - The account address.\n * @returns The WebSocket channel name.\n */\nfunction buildAccountActivityChannel(\n namespace: string,\n address: string,\n): string {\n return `${CHANNEL_TYPE}.${namespace}:0:${address.toLowerCase()}`;\n}\n\n// Note: AccountActivityMessage and BalanceUpdate types are imported from @metamask/core-backend\n\n// ============================================================================\n// BACKEND WEBSOCKET DATA SOURCE\n// ============================================================================\n\n/**\n * Data source for receiving real-time balance updates via WebSocket.\n *\n * This data source connects directly to BackendWebSocketService to receive\n * push notifications for account balance changes. Unlike AccountsApiDataSource\n * which polls for data, this provides instant updates.\n *\n * Uses Messenger pattern for all interactions:\n * - Calls BackendWebSocketService methods via messenger actions\n * - Exposes its own actions for AssetsController to call\n * - Publishes events for AssetsController to subscribe to\n *\n * Actions exposed:\n * - BackendWebsocketDataSource:getActiveChains\n * - BackendWebsocketDataSource:subscribe\n * - BackendWebsocketDataSource:unsubscribe\n *\n * Events published:\n * - BackendWebsocketDataSource:activeChainsUpdated\n * - BackendWebsocketDataSource:assetsUpdated\n *\n * Actions called (from BackendWebSocketService):\n * - BackendWebSocketService:subscribe\n * - BackendWebSocketService:getConnectionInfo\n * - BackendWebSocketService:findSubscriptionsByChannelPrefix\n */\nexport class BackendWebsocketDataSource extends AbstractDataSource<\n typeof CONTROLLER_NAME,\n BackendWebsocketDataSourceState\n> {\n readonly #messenger: BackendWebsocketDataSourceMessenger;\n\n /** WebSocket subscriptions by our internal subscription ID */\n readonly #wsSubscriptions: Map<string, WebSocketSubscription> = new Map();\n\n /** Pending subscription requests to process when WebSocket connects */\n readonly #pendingSubscriptions: Map<string, SubscriptionRequest> = new Map();\n\n /** Store original subscription requests for reconnection */\n readonly #subscriptionRequests: Map<string, SubscriptionRequest> = new Map();\n\n constructor(options: BackendWebsocketDataSourceOptions) {\n super(CONTROLLER_NAME, {\n ...defaultState,\n ...options.state,\n });\n\n this.#messenger = options.messenger;\n\n this.#registerActionHandlers();\n this.#subscribeToEvents();\n }\n\n // ============================================================================\n // INITIALIZATION\n // ============================================================================\n\n #registerActionHandlers(): void {\n const getActiveChainsHandler: BackendWebsocketDataSourceGetActiveChainsAction['handler'] =\n async () => this.getActiveChains();\n\n const subscribeHandler: BackendWebsocketDataSourceSubscribeAction['handler'] =\n async (request) => this.subscribe(request);\n\n const unsubscribeHandler: BackendWebsocketDataSourceUnsubscribeAction['handler'] =\n async (subscriptionId) => this.unsubscribe(subscriptionId);\n\n this.#messenger.registerActionHandler(\n 'BackendWebsocketDataSource:getActiveChains',\n getActiveChainsHandler,\n );\n\n this.#messenger.registerActionHandler(\n 'BackendWebsocketDataSource:subscribe',\n subscribeHandler,\n );\n\n this.#messenger.registerActionHandler(\n 'BackendWebsocketDataSource:unsubscribe',\n unsubscribeHandler,\n );\n }\n\n #subscribeToEvents(): void {\n // Listen for WebSocket connection state changes\n this.#messenger.subscribe(\n 'BackendWebSocketService:connectionStateChanged',\n (connectionInfo) => {\n if (connectionInfo.state === ('connected' as WebSocketState)) {\n // WebSocket connected - process any pending subscriptions\n this.#processPendingSubscriptions().catch(console.error);\n } else if (\n connectionInfo.state === ('disconnected' as WebSocketState)\n ) {\n // When disconnected, all subscriptions are cleared server-side\n // Move active subscriptions to pending for re-subscription on reconnect\n this.#handleDisconnect();\n }\n },\n );\n\n // Listen for AccountsApiDataSource active chains changes\n // This keeps BackendWebsocketDataSource in sync with the supported chains\n // since both use the same backend infrastructure\n this.#messenger.subscribe(\n 'AccountsApiDataSource:activeChainsUpdated',\n (chains: ChainId[]) => {\n this.updateActiveChains(chains, (updatedChains) =>\n this.#messenger.call(\n 'AssetsController:activeChainsUpdate',\n CONTROLLER_NAME,\n updatedChains,\n ),\n );\n },\n );\n }\n\n /**\n * Handle WebSocket disconnection.\n * Moves all active subscriptions to pending for re-subscription on reconnect.\n */\n #handleDisconnect(): void {\n log('WebSocket disconnected, preserving subscriptions for reconnect', {\n activeSubscriptionCount: this.activeSubscriptions.size,\n wsSubscriptionCount: this.#wsSubscriptions.size,\n });\n\n // Move active subscriptions to pending for re-subscription\n for (const [subscriptionId] of this.activeSubscriptions) {\n const originalRequest = this.#subscriptionRequests.get(subscriptionId);\n if (originalRequest) {\n // Mark as update since it was previously active\n this.#pendingSubscriptions.set(subscriptionId, {\n ...originalRequest,\n isUpdate: false, // Treat as new subscription since server cleared it\n });\n }\n }\n\n // Clear WebSocket subscriptions (server-side already cleared)\n this.#wsSubscriptions.clear();\n\n // Clear active subscriptions (they're no longer valid)\n this.activeSubscriptions.clear();\n }\n\n /**\n * Process any pending subscriptions that were queued while WebSocket was disconnected.\n */\n async #processPendingSubscriptions(): Promise<void> {\n if (this.#pendingSubscriptions.size === 0) {\n return;\n }\n\n // Process all pending subscriptions\n const pendingEntries = Array.from(this.#pendingSubscriptions.entries());\n\n for (const [subscriptionId, request] of pendingEntries) {\n try {\n // Remove from pending before processing to avoid infinite loop\n this.#pendingSubscriptions.delete(subscriptionId);\n await this.subscribe(request);\n } catch (error) {\n log('Failed to process pending subscription', {\n subscriptionId,\n error,\n });\n }\n }\n }\n\n // ============================================================================\n // ACTIVE CHAINS\n // ============================================================================\n\n /**\n * Update active chains when AccountsApiDataSource reports new supported chains.\n *\n * @param chains - Array of supported chain IDs.\n */\n updateSupportedChains(chains: ChainId[]): void {\n this.updateActiveChains(chains, (updatedChains) =>\n this.#messenger.call(\n 'AssetsController:activeChainsUpdate',\n CONTROLLER_NAME,\n updatedChains,\n ),\n );\n }\n\n // ============================================================================\n // SUBSCRIBE\n // ============================================================================\n\n async subscribe(subscriptionRequest: SubscriptionRequest): Promise<void> {\n const { request, subscriptionId, isUpdate } = subscriptionRequest;\n\n // Filter to active chains only\n const chainsToSubscribe = request.chainIds.filter((chainId) =>\n this.state.activeChains.includes(chainId),\n );\n\n const addresses = request.accounts.map((a) => a.address);\n\n if (chainsToSubscribe.length === 0) {\n return;\n }\n\n // Check WebSocket connection status\n try {\n const connectionInfo = this.#messenger.call(\n 'BackendWebSocketService:getConnectionInfo',\n );\n if (connectionInfo.state !== ('connected' as WebSocketState)) {\n // Store the subscription request to process when WebSocket connects\n this.#pendingSubscriptions.set(subscriptionId, subscriptionRequest);\n return;\n }\n } catch {\n // Store anyway - will be processed when we can connect\n this.#pendingSubscriptions.set(subscriptionId, subscriptionRequest);\n return;\n }\n\n // Remove from pending if it was there (we're processing it now)\n this.#pendingSubscriptions.delete(subscriptionId);\n\n // Handle subscription update\n if (isUpdate) {\n const existing = this.activeSubscriptions.get(subscriptionId);\n if (existing) {\n // Check if accounts changed - if so, we need to re-subscribe to different channels\n const existingAddresses = existing.addresses ?? [];\n const addressesChanged =\n addresses.length !== existingAddresses.length ||\n addresses.some((addr) => !existingAddresses.includes(addr));\n\n if (!addressesChanged) {\n // Only chains changed - just update chains and return\n existing.chains = chainsToSubscribe;\n return;\n }\n // Accounts changed - fall through to re-subscribe with new channels\n }\n }\n\n // Clean up existing subscription if any\n await this.unsubscribe(subscriptionId);\n\n // Extract unique namespaces from chains (e.g., eip155, solana)\n const namespaces = getUniqueNamespaces(chainsToSubscribe);\n\n // Build channel names using CAIP-10 wildcard format\n const channels: string[] = [];\n for (const namespace of namespaces) {\n for (const address of addresses) {\n channels.push(buildAccountActivityChannel(namespace, address));\n }\n }\n\n try {\n // Create WebSocket subscription\n const wsSubscription = await this.#messenger.call(\n 'BackendWebSocketService:subscribe',\n {\n channels,\n channelType: CHANNEL_TYPE,\n callback: (notification: ServerNotificationMessage) => {\n this.#handleNotification(notification, request);\n },\n },\n );\n\n // Store WebSocket subscription\n this.#wsSubscriptions.set(subscriptionId, wsSubscription);\n\n // Store in abstract class tracking\n this.activeSubscriptions.set(subscriptionId, {\n cleanup: () => {\n const wsSub = this.#wsSubscriptions.get(subscriptionId);\n if (wsSub) {\n wsSub.unsubscribe().catch((unsubErr) => {\n log('Error unsubscribing', { subscriptionId, error: unsubErr });\n });\n this.#wsSubscriptions.delete(subscriptionId);\n }\n // Also clean up the stored request\n this.#subscriptionRequests.delete(subscriptionId);\n },\n chains: chainsToSubscribe,\n addresses,\n });\n\n // Store original request for reconnection\n this.#subscriptionRequests.set(subscriptionId, subscriptionRequest);\n } catch (error) {\n log('WebSocket subscription FAILED', {\n subscriptionId,\n error,\n chains: chainsToSubscribe,\n });\n }\n }\n\n // ============================================================================\n // NOTIFICATION HANDLING\n // ============================================================================\n\n #handleNotification(\n notification: ServerNotificationMessage,\n request: DataRequest,\n ): void {\n try {\n const activityMessage =\n notification.data as unknown as AccountActivityMessage;\n const { address, tx, updates } = activityMessage;\n\n if (!address || !tx || !updates) {\n return;\n }\n\n // Extract chain ID from transaction (CAIP-2 format, e.g., \"eip155:8453\")\n const chainId = tx.chain as ChainId;\n\n // Find matching account in request\n const account = request.accounts.find(\n (a) => a.address.toLowerCase() === address.toLowerCase(),\n );\n if (!account) {\n return;\n }\n const accountId = account.id;\n\n // Process all balance updates from the activity message\n const response = this.#processBalanceUpdates(updates, chainId, accountId);\n\n if (Object.keys(response).length > 0) {\n // Report update to AssetsController\n this.#messenger\n .call('AssetsController:assetsUpdate', response, CONTROLLER_NAME)\n .catch(console.error);\n }\n } catch (error) {\n log('Error handling notification', error);\n }\n }\n\n /**\n * Process balance updates from AccountActivityMessage.\n * Each update contains asset info, post-transaction balance, and transfer details.\n *\n * @param updates - Array of balance updates from the activity message.\n * @param _chainId - The chain ID (unused but kept for context).\n * @param accountId - The account ID to process updates for.\n * @returns DataResponse containing processed balance and metadata.\n */\n #processBalanceUpdates(\n updates: BalanceUpdate[],\n _chainId: ChainId,\n accountId: string,\n ): DataResponse {\n const assetsBalance: Record<string, Record<Caip19AssetId, AssetBalance>> = {\n [accountId]: {},\n };\n const assetsMetadata: Record<Caip19AssetId, AssetMetadata> = {};\n\n for (const update of updates) {\n const { asset, postBalance } = update;\n\n if (!asset || !postBalance) {\n continue;\n }\n\n // Asset type is in CAIP format: \"eip155:1/erc20:0x...\" or \"eip155:1/slip44:60\"\n // We can use it directly as the asset ID\n const assetId = asset.type as Caip19AssetId;\n\n // Determine token type from asset type string\n const isNative = asset.type.includes('/slip44:');\n const tokenType = isNative ? 'native' : 'erc20';\n\n // Parse balance amount (already in hex format like \"0xc350\")\n const balanceAmount = postBalance.amount.startsWith('0x')\n ? BigInt(postBalance.amount).toString()\n : postBalance.amount;\n\n assetsBalance[accountId][assetId] = {\n amount: balanceAmount,\n };\n\n assetsMetadata[assetId] = {\n type: tokenType,\n symbol: asset.unit,\n name: asset.unit, // Use unit as name (actual name may not be in the message)\n decimals: asset.decimals,\n };\n }\n\n const response: DataResponse = {};\n if (Object.keys(assetsBalance[accountId]).length > 0) {\n response.assetsBalance = assetsBalance;\n response.assetsMetadata = assetsMetadata;\n }\n\n return response;\n }\n\n // ============================================================================\n // CLEANUP\n // ============================================================================\n\n destroy(): void {\n // Clean up WebSocket subscriptions\n // Convert to array first to avoid modifying map during iteration\n const subscriptions = [...this.#wsSubscriptions.values()];\n for (const wsSub of subscriptions) {\n try {\n // Fire and forget - don't await in destroy\n wsSub.unsubscribe().catch(() => {\n // Ignore errors during cleanup\n });\n } catch {\n // Ignore errors during cleanup\n }\n }\n this.#wsSubscriptions.clear();\n\n // Clean up base class subscriptions\n super.destroy();\n }\n}\n\n// ============================================================================\n// FACTORY FUNCTION\n// ============================================================================\n\n/**\n * Creates a BackendWebsocketDataSource instance.\n *\n * @param options - Configuration options for the data source.\n * @returns A new BackendWebsocketDataSource instance.\n */\nexport function createBackendWebsocketDataSource(\n options: BackendWebsocketDataSourceOptions,\n): BackendWebsocketDataSource {\n return new BackendWebsocketDataSource(options);\n}\n"]}