@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,211 @@
1
+ "use strict";
2
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
3
+ if (kind === "m") throw new TypeError("Private method is not writable");
4
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
5
+ 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");
6
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
7
+ };
8
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
9
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
10
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
11
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
12
+ };
13
+ var _TokenDataSource_instances, _TokenDataSource_messenger, _TokenDataSource_apiClient, _TokenDataSource_getSupportedNetworks, _TokenDataSource_filterAssetsByNetwork, _TokenDataSource_registerActionHandlers;
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.TokenDataSource = void 0;
16
+ const core_backend_1 = require("@metamask/core-backend");
17
+ const utils_1 = require("@metamask/utils");
18
+ const logger_1 = require("../logger.cjs");
19
+ const types_1 = require("../types.cjs");
20
+ // ============================================================================
21
+ // CONSTANTS
22
+ // ============================================================================
23
+ const CONTROLLER_NAME = 'TokenDataSource';
24
+ const log = (0, logger_1.createModuleLogger)(logger_1.projectLogger, CONTROLLER_NAME);
25
+ // ============================================================================
26
+ // HELPER FUNCTIONS
27
+ // ============================================================================
28
+ /**
29
+ * Transform V3 API response to FungibleAssetMetadata for state storage.
30
+ *
31
+ * Mapping:
32
+ * - assetId → used to derive `type` (native/erc20/spl)
33
+ * - iconUrl → image
34
+ * - All other fields map directly
35
+ *
36
+ * @param assetId - CAIP-19 asset ID used to derive token type.
37
+ * @param assetData - V3 API response data.
38
+ * @returns FungibleAssetMetadata for state storage.
39
+ */
40
+ function transformV3AssetResponseToMetadata(assetId, assetData) {
41
+ const parsed = (0, utils_1.parseCaipAssetType)(assetId);
42
+ let tokenType = 'erc20';
43
+ if (parsed.assetNamespace === 'slip44') {
44
+ tokenType = 'native';
45
+ }
46
+ else if (parsed.assetNamespace === 'spl') {
47
+ tokenType = 'spl';
48
+ }
49
+ const metadata = {
50
+ // Type derived from assetId
51
+ type: tokenType,
52
+ // BaseAssetMetadata fields
53
+ name: assetData.name,
54
+ symbol: assetData.symbol,
55
+ decimals: assetData.decimals,
56
+ image: assetData.iconUrl,
57
+ // Direct mapping fields
58
+ coingeckoId: assetData.coingeckoId,
59
+ occurrences: assetData.occurrences,
60
+ aggregators: assetData.aggregators,
61
+ labels: assetData.labels,
62
+ erc20Permit: assetData.erc20Permit,
63
+ fees: assetData.fees,
64
+ honeypotStatus: assetData.honeypotStatus,
65
+ storage: assetData.storage,
66
+ isContractVerified: assetData.isContractVerified,
67
+ description: assetData.description,
68
+ };
69
+ return metadata;
70
+ }
71
+ // ============================================================================
72
+ // TOKEN DATA SOURCE
73
+ // ============================================================================
74
+ /**
75
+ * TokenDataSource enriches responses with token metadata from the Tokens API.
76
+ *
77
+ * This middleware-based data source:
78
+ * - Checks detected assets for missing metadata/images
79
+ * - Fetches metadata from Tokens API v3 for assets needing enrichment
80
+ * - Merges fetched metadata into the response
81
+ *
82
+ * Usage:
83
+ * ```typescript
84
+ * // Create and initialize (registers messenger actions)
85
+ * const tokenDataSource = new TokenDataSource({ messenger, queryApiClient });
86
+ *
87
+ * // Later, get middleware via messenger
88
+ * const middleware = messenger.call('TokenDataSource:getAssetsMiddleware');
89
+ * ```
90
+ */
91
+ class TokenDataSource {
92
+ constructor(options) {
93
+ _TokenDataSource_instances.add(this);
94
+ this.name = CONTROLLER_NAME;
95
+ _TokenDataSource_messenger.set(this, void 0);
96
+ /** ApiPlatformClient for cached API calls */
97
+ _TokenDataSource_apiClient.set(this, void 0);
98
+ __classPrivateFieldSet(this, _TokenDataSource_messenger, options.messenger, "f");
99
+ __classPrivateFieldSet(this, _TokenDataSource_apiClient, options.queryApiClient, "f");
100
+ __classPrivateFieldGet(this, _TokenDataSource_instances, "m", _TokenDataSource_registerActionHandlers).call(this);
101
+ }
102
+ /**
103
+ * Get the middleware for enriching responses with token metadata.
104
+ *
105
+ * This middleware:
106
+ * 1. Extracts the response from context
107
+ * 2. Fetches metadata for detected assets (assets without metadata)
108
+ * 3. Enriches the response with fetched metadata
109
+ * 4. Calls next() at the end to continue the middleware chain
110
+ *
111
+ * @returns The middleware function for the assets pipeline.
112
+ */
113
+ get assetsMiddleware() {
114
+ return (0, types_1.forDataTypes)(['metadata'], async (ctx, next) => {
115
+ // Extract response from context
116
+ const { response } = ctx;
117
+ // Only fetch metadata for detected assets (assets without metadata)
118
+ if (!response.detectedAssets) {
119
+ return next(ctx);
120
+ }
121
+ const { assetsMetadata: stateMetadata } = ctx.getAssetsState();
122
+ const assetIdsNeedingMetadata = new Set();
123
+ for (const detectedIds of Object.values(response.detectedAssets)) {
124
+ for (const assetId of detectedIds) {
125
+ // Skip if response already has metadata with image
126
+ const responseMetadata = response.assetsMetadata?.[assetId];
127
+ if (responseMetadata?.image) {
128
+ continue;
129
+ }
130
+ // Skip if state already has metadata with image
131
+ const existingMetadata = stateMetadata[assetId];
132
+ if (existingMetadata?.image) {
133
+ continue;
134
+ }
135
+ assetIdsNeedingMetadata.add(assetId);
136
+ }
137
+ }
138
+ if (assetIdsNeedingMetadata.size === 0) {
139
+ return next(ctx);
140
+ }
141
+ // Filter asset IDs to only include supported networks
142
+ const supportedNetworks = await __classPrivateFieldGet(this, _TokenDataSource_instances, "m", _TokenDataSource_getSupportedNetworks).call(this);
143
+ const supportedAssetIds = __classPrivateFieldGet(this, _TokenDataSource_instances, "m", _TokenDataSource_filterAssetsByNetwork).call(this, [...assetIdsNeedingMetadata], supportedNetworks);
144
+ if (supportedAssetIds.length === 0) {
145
+ return next(ctx);
146
+ }
147
+ try {
148
+ // Use ApiPlatformClient for fetching asset metadata
149
+ // API returns an array with assetId as a property on each item
150
+ const metadataResponse = await __classPrivateFieldGet(this, _TokenDataSource_apiClient, "f").tokens.fetchV3Assets(supportedAssetIds, {
151
+ includeIconUrl: true,
152
+ includeMarketData: true,
153
+ includeMetadata: true,
154
+ includeLabels: true,
155
+ includeRwaData: true,
156
+ });
157
+ response.assetsMetadata ?? (response.assetsMetadata = {});
158
+ for (const assetData of metadataResponse) {
159
+ const caipAssetId = assetData.assetId;
160
+ response.assetsMetadata[caipAssetId] =
161
+ transformV3AssetResponseToMetadata(assetData.assetId, assetData);
162
+ }
163
+ }
164
+ catch (error) {
165
+ log('Failed to fetch metadata', { error });
166
+ }
167
+ // Call next() at the end to continue the middleware chain
168
+ return next(ctx);
169
+ });
170
+ }
171
+ }
172
+ exports.TokenDataSource = TokenDataSource;
173
+ _TokenDataSource_messenger = new WeakMap(), _TokenDataSource_apiClient = new WeakMap(), _TokenDataSource_instances = new WeakSet(), _TokenDataSource_getSupportedNetworks =
174
+ /**
175
+ * Gets the supported networks from the API.
176
+ * Caching is handled by ApiPlatformClient.
177
+ *
178
+ * @returns Set of supported chain IDs in CAIP format
179
+ */
180
+ async function _TokenDataSource_getSupportedNetworks() {
181
+ try {
182
+ // Use v2/supportedNetworks which returns CAIP chain IDs
183
+ // ApiPlatformClient handles caching
184
+ const response = await __classPrivateFieldGet(this, _TokenDataSource_apiClient, "f").tokens.fetchTokenV2SupportedNetworks();
185
+ // Combine full and partial support networks
186
+ const allNetworks = [...response.fullSupport, ...response.partialSupport];
187
+ return new Set(allNetworks);
188
+ }
189
+ catch (error) {
190
+ log('Failed to fetch supported networks', { error });
191
+ return new Set();
192
+ }
193
+ }, _TokenDataSource_filterAssetsByNetwork = function _TokenDataSource_filterAssetsByNetwork(assetIds, supportedNetworks) {
194
+ return assetIds.filter((assetId) => {
195
+ try {
196
+ const parsed = (0, utils_1.parseCaipAssetType)(assetId);
197
+ // chainId is in format "eip155:1" or "tron:728126428"
198
+ // parsed.chain has namespace and reference properties
199
+ const chainId = `${parsed.chain.namespace}:${parsed.chain.reference}`;
200
+ return supportedNetworks.has(chainId);
201
+ }
202
+ catch {
203
+ // If we can't parse the asset ID, filter it out
204
+ return false;
205
+ }
206
+ });
207
+ }, _TokenDataSource_registerActionHandlers = function _TokenDataSource_registerActionHandlers() {
208
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
209
+ __classPrivateFieldGet(this, _TokenDataSource_messenger, "f").registerActionHandler('TokenDataSource:getAssetsMiddleware', () => this.assetsMiddleware);
210
+ };
211
+ //# sourceMappingURL=TokenDataSource.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TokenDataSource.cjs","sourceRoot":"","sources":["../../src/data-sources/TokenDataSource.ts"],"names":[],"mappings":"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","sourcesContent":["import type { V3AssetResponse } from '@metamask/core-backend';\nimport { ApiPlatformClient } from '@metamask/core-backend';\nimport type { Messenger } from '@metamask/messenger';\nimport { parseCaipAssetType } from '@metamask/utils';\nimport type { CaipAssetType } from '@metamask/utils';\n\nimport { projectLogger, createModuleLogger } from '../logger';\nimport { forDataTypes } from '../types';\nimport type {\n Caip19AssetId,\n AssetMetadata,\n Middleware,\n FungibleAssetMetadata,\n} from '../types';\n\n// ============================================================================\n// CONSTANTS\n// ============================================================================\n\nconst CONTROLLER_NAME = 'TokenDataSource';\n\nconst log = createModuleLogger(projectLogger, CONTROLLER_NAME);\n\n// ============================================================================\n// MESSENGER TYPES\n// ============================================================================\n\n/**\n * Action to get the TokenDataSource middleware.\n */\nexport type TokenDataSourceGetAssetsMiddlewareAction = {\n type: `${typeof CONTROLLER_NAME}:getAssetsMiddleware`;\n handler: () => Middleware;\n};\n\n/**\n * All actions exposed by TokenDataSource.\n */\nexport type TokenDataSourceActions = TokenDataSourceGetAssetsMiddlewareAction;\n\n/**\n * TokenDataSource no longer needs external BackendApiClient actions.\n * It uses ApiPlatformClient directly.\n */\nexport type TokenDataSourceAllowedActions = never;\n\nexport type TokenDataSourceMessenger = Messenger<\n typeof CONTROLLER_NAME,\n TokenDataSourceActions,\n never\n>;\n\n// ============================================================================\n// OPTIONS\n// ============================================================================\n\nexport type TokenDataSourceOptions = {\n messenger: TokenDataSourceMessenger;\n /** ApiPlatformClient for API calls with caching */\n queryApiClient: ApiPlatformClient;\n};\n\n// ============================================================================\n// HELPER FUNCTIONS\n// ============================================================================\n\n/**\n * Transform V3 API response to FungibleAssetMetadata for state storage.\n *\n * Mapping:\n * - assetId → used to derive `type` (native/erc20/spl)\n * - iconUrl → image\n * - All other fields map directly\n *\n * @param assetId - CAIP-19 asset ID used to derive token type.\n * @param assetData - V3 API response data.\n * @returns FungibleAssetMetadata for state storage.\n */\nfunction transformV3AssetResponseToMetadata(\n assetId: string,\n assetData: V3AssetResponse,\n): AssetMetadata {\n const parsed = parseCaipAssetType(assetId as CaipAssetType);\n let tokenType: 'native' | 'erc20' | 'spl' = 'erc20';\n\n if (parsed.assetNamespace === 'slip44') {\n tokenType = 'native';\n } else if (parsed.assetNamespace === 'spl') {\n tokenType = 'spl';\n }\n\n const metadata: FungibleAssetMetadata = {\n // Type derived from assetId\n type: tokenType,\n // BaseAssetMetadata fields\n name: assetData.name,\n symbol: assetData.symbol,\n decimals: assetData.decimals,\n image: assetData.iconUrl,\n // Direct mapping fields\n coingeckoId: assetData.coingeckoId,\n occurrences: assetData.occurrences,\n aggregators: assetData.aggregators,\n labels: assetData.labels,\n erc20Permit: assetData.erc20Permit,\n fees: assetData.fees,\n honeypotStatus: assetData.honeypotStatus,\n storage: assetData.storage,\n isContractVerified: assetData.isContractVerified,\n description: assetData.description,\n };\n\n return metadata;\n}\n\n// ============================================================================\n// TOKEN DATA SOURCE\n// ============================================================================\n\n/**\n * TokenDataSource enriches responses with token metadata from the Tokens API.\n *\n * This middleware-based data source:\n * - Checks detected assets for missing metadata/images\n * - Fetches metadata from Tokens API v3 for assets needing enrichment\n * - Merges fetched metadata into the response\n *\n * Usage:\n * ```typescript\n * // Create and initialize (registers messenger actions)\n * const tokenDataSource = new TokenDataSource({ messenger, queryApiClient });\n *\n * // Later, get middleware via messenger\n * const middleware = messenger.call('TokenDataSource:getAssetsMiddleware');\n * ```\n */\nexport class TokenDataSource {\n readonly name = CONTROLLER_NAME;\n\n readonly #messenger: TokenDataSourceMessenger;\n\n /** ApiPlatformClient for cached API calls */\n readonly #apiClient: ApiPlatformClient;\n\n constructor(options: TokenDataSourceOptions) {\n this.#messenger = options.messenger;\n this.#apiClient = options.queryApiClient;\n this.#registerActionHandlers();\n }\n\n /**\n * Gets the supported networks from the API.\n * Caching is handled by ApiPlatformClient.\n *\n * @returns Set of supported chain IDs in CAIP format\n */\n async #getSupportedNetworks(): Promise<Set<string>> {\n try {\n // Use v2/supportedNetworks which returns CAIP chain IDs\n // ApiPlatformClient handles caching\n const response =\n await this.#apiClient.tokens.fetchTokenV2SupportedNetworks();\n\n // Combine full and partial support networks\n const allNetworks = [...response.fullSupport, ...response.partialSupport];\n\n return new Set(allNetworks);\n } catch (error) {\n log('Failed to fetch supported networks', { error });\n return new Set();\n }\n }\n\n /**\n * Filters asset IDs to only include those from supported networks.\n *\n * @param assetIds - Array of CAIP-19 asset IDs\n * @param supportedNetworks - Set of supported chain IDs\n * @returns Array of asset IDs from supported networks\n */\n #filterAssetsByNetwork(\n assetIds: string[],\n supportedNetworks: Set<string>,\n ): string[] {\n return assetIds.filter((assetId) => {\n try {\n const parsed = parseCaipAssetType(assetId as CaipAssetType);\n // chainId is in format \"eip155:1\" or \"tron:728126428\"\n // parsed.chain has namespace and reference properties\n const chainId = `${parsed.chain.namespace}:${parsed.chain.reference}`;\n return supportedNetworks.has(chainId);\n } catch {\n // If we can't parse the asset ID, filter it out\n return false;\n }\n });\n }\n\n #registerActionHandlers(): void {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (this.#messenger as any).registerActionHandler(\n 'TokenDataSource:getAssetsMiddleware',\n () => this.assetsMiddleware,\n );\n }\n\n /**\n * Get the middleware for enriching responses with token metadata.\n *\n * This middleware:\n * 1. Extracts the response from context\n * 2. Fetches metadata for detected assets (assets without metadata)\n * 3. Enriches the response with fetched metadata\n * 4. Calls next() at the end to continue the middleware chain\n *\n * @returns The middleware function for the assets pipeline.\n */\n get assetsMiddleware(): Middleware {\n return forDataTypes(['metadata'], async (ctx, next) => {\n // Extract response from context\n const { response } = ctx;\n\n // Only fetch metadata for detected assets (assets without metadata)\n if (!response.detectedAssets) {\n return next(ctx);\n }\n\n const { assetsMetadata: stateMetadata } = ctx.getAssetsState();\n const assetIdsNeedingMetadata = new Set<string>();\n\n for (const detectedIds of Object.values(response.detectedAssets)) {\n for (const assetId of detectedIds) {\n // Skip if response already has metadata with image\n const responseMetadata = response.assetsMetadata?.[assetId];\n if (responseMetadata?.image) {\n continue;\n }\n\n // Skip if state already has metadata with image\n const existingMetadata = stateMetadata[assetId];\n if (existingMetadata?.image) {\n continue;\n }\n\n assetIdsNeedingMetadata.add(assetId);\n }\n }\n\n if (assetIdsNeedingMetadata.size === 0) {\n return next(ctx);\n }\n\n // Filter asset IDs to only include supported networks\n const supportedNetworks = await this.#getSupportedNetworks();\n const supportedAssetIds = this.#filterAssetsByNetwork(\n [...assetIdsNeedingMetadata],\n supportedNetworks,\n );\n\n if (supportedAssetIds.length === 0) {\n return next(ctx);\n }\n\n try {\n // Use ApiPlatformClient for fetching asset metadata\n // API returns an array with assetId as a property on each item\n const metadataResponse = await this.#apiClient.tokens.fetchV3Assets(\n supportedAssetIds,\n {\n includeIconUrl: true,\n includeMarketData: true,\n includeMetadata: true,\n includeLabels: true,\n includeRwaData: true,\n },\n );\n\n response.assetsMetadata ??= {};\n\n for (const assetData of metadataResponse) {\n const caipAssetId = assetData.assetId as Caip19AssetId;\n response.assetsMetadata[caipAssetId] =\n transformV3AssetResponseToMetadata(assetData.assetId, assetData);\n }\n } catch (error) {\n log('Failed to fetch metadata', { error });\n }\n\n // Call next() at the end to continue the middleware chain\n return next(ctx);\n });\n }\n}\n"]}
@@ -0,0 +1,62 @@
1
+ import { ApiPlatformClient } from "@metamask/core-backend";
2
+ import type { Messenger } from "@metamask/messenger";
3
+ import type { Middleware } from "../types.cjs";
4
+ declare const CONTROLLER_NAME = "TokenDataSource";
5
+ /**
6
+ * Action to get the TokenDataSource middleware.
7
+ */
8
+ export type TokenDataSourceGetAssetsMiddlewareAction = {
9
+ type: `${typeof CONTROLLER_NAME}:getAssetsMiddleware`;
10
+ handler: () => Middleware;
11
+ };
12
+ /**
13
+ * All actions exposed by TokenDataSource.
14
+ */
15
+ export type TokenDataSourceActions = TokenDataSourceGetAssetsMiddlewareAction;
16
+ /**
17
+ * TokenDataSource no longer needs external BackendApiClient actions.
18
+ * It uses ApiPlatformClient directly.
19
+ */
20
+ export type TokenDataSourceAllowedActions = never;
21
+ export type TokenDataSourceMessenger = Messenger<typeof CONTROLLER_NAME, TokenDataSourceActions, never>;
22
+ export type TokenDataSourceOptions = {
23
+ messenger: TokenDataSourceMessenger;
24
+ /** ApiPlatformClient for API calls with caching */
25
+ queryApiClient: ApiPlatformClient;
26
+ };
27
+ /**
28
+ * TokenDataSource enriches responses with token metadata from the Tokens API.
29
+ *
30
+ * This middleware-based data source:
31
+ * - Checks detected assets for missing metadata/images
32
+ * - Fetches metadata from Tokens API v3 for assets needing enrichment
33
+ * - Merges fetched metadata into the response
34
+ *
35
+ * Usage:
36
+ * ```typescript
37
+ * // Create and initialize (registers messenger actions)
38
+ * const tokenDataSource = new TokenDataSource({ messenger, queryApiClient });
39
+ *
40
+ * // Later, get middleware via messenger
41
+ * const middleware = messenger.call('TokenDataSource:getAssetsMiddleware');
42
+ * ```
43
+ */
44
+ export declare class TokenDataSource {
45
+ #private;
46
+ readonly name = "TokenDataSource";
47
+ constructor(options: TokenDataSourceOptions);
48
+ /**
49
+ * Get the middleware for enriching responses with token metadata.
50
+ *
51
+ * This middleware:
52
+ * 1. Extracts the response from context
53
+ * 2. Fetches metadata for detected assets (assets without metadata)
54
+ * 3. Enriches the response with fetched metadata
55
+ * 4. Calls next() at the end to continue the middleware chain
56
+ *
57
+ * @returns The middleware function for the assets pipeline.
58
+ */
59
+ get assetsMiddleware(): Middleware;
60
+ }
61
+ export {};
62
+ //# sourceMappingURL=TokenDataSource.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TokenDataSource.d.cts","sourceRoot":"","sources":["../../src/data-sources/TokenDataSource.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,+BAA+B;AAC3D,OAAO,KAAK,EAAE,SAAS,EAAE,4BAA4B;AAMrD,OAAO,KAAK,EAGV,UAAU,EAEX,qBAAiB;AAMlB,QAAA,MAAM,eAAe,oBAAoB,CAAC;AAQ1C;;GAEG;AACH,MAAM,MAAM,wCAAwC,GAAG;IACrD,IAAI,EAAE,GAAG,OAAO,eAAe,sBAAsB,CAAC;IACtD,OAAO,EAAE,MAAM,UAAU,CAAC;CAC3B,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,sBAAsB,GAAG,wCAAwC,CAAC;AAE9E;;;GAGG;AACH,MAAM,MAAM,6BAA6B,GAAG,KAAK,CAAC;AAElD,MAAM,MAAM,wBAAwB,GAAG,SAAS,CAC9C,OAAO,eAAe,EACtB,sBAAsB,EACtB,KAAK,CACN,CAAC;AAMF,MAAM,MAAM,sBAAsB,GAAG;IACnC,SAAS,EAAE,wBAAwB,CAAC;IACpC,mDAAmD;IACnD,cAAc,EAAE,iBAAiB,CAAC;CACnC,CAAC;AA2DF;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,eAAe;;IAC1B,QAAQ,CAAC,IAAI,qBAAmB;gBAOpB,OAAO,EAAE,sBAAsB;IA8D3C;;;;;;;;;;OAUG;IACH,IAAI,gBAAgB,IAAI,UAAU,CA0EjC;CACF"}
@@ -0,0 +1,62 @@
1
+ import { ApiPlatformClient } from "@metamask/core-backend";
2
+ import type { Messenger } from "@metamask/messenger";
3
+ import type { Middleware } from "../types.mjs";
4
+ declare const CONTROLLER_NAME = "TokenDataSource";
5
+ /**
6
+ * Action to get the TokenDataSource middleware.
7
+ */
8
+ export type TokenDataSourceGetAssetsMiddlewareAction = {
9
+ type: `${typeof CONTROLLER_NAME}:getAssetsMiddleware`;
10
+ handler: () => Middleware;
11
+ };
12
+ /**
13
+ * All actions exposed by TokenDataSource.
14
+ */
15
+ export type TokenDataSourceActions = TokenDataSourceGetAssetsMiddlewareAction;
16
+ /**
17
+ * TokenDataSource no longer needs external BackendApiClient actions.
18
+ * It uses ApiPlatformClient directly.
19
+ */
20
+ export type TokenDataSourceAllowedActions = never;
21
+ export type TokenDataSourceMessenger = Messenger<typeof CONTROLLER_NAME, TokenDataSourceActions, never>;
22
+ export type TokenDataSourceOptions = {
23
+ messenger: TokenDataSourceMessenger;
24
+ /** ApiPlatformClient for API calls with caching */
25
+ queryApiClient: ApiPlatformClient;
26
+ };
27
+ /**
28
+ * TokenDataSource enriches responses with token metadata from the Tokens API.
29
+ *
30
+ * This middleware-based data source:
31
+ * - Checks detected assets for missing metadata/images
32
+ * - Fetches metadata from Tokens API v3 for assets needing enrichment
33
+ * - Merges fetched metadata into the response
34
+ *
35
+ * Usage:
36
+ * ```typescript
37
+ * // Create and initialize (registers messenger actions)
38
+ * const tokenDataSource = new TokenDataSource({ messenger, queryApiClient });
39
+ *
40
+ * // Later, get middleware via messenger
41
+ * const middleware = messenger.call('TokenDataSource:getAssetsMiddleware');
42
+ * ```
43
+ */
44
+ export declare class TokenDataSource {
45
+ #private;
46
+ readonly name = "TokenDataSource";
47
+ constructor(options: TokenDataSourceOptions);
48
+ /**
49
+ * Get the middleware for enriching responses with token metadata.
50
+ *
51
+ * This middleware:
52
+ * 1. Extracts the response from context
53
+ * 2. Fetches metadata for detected assets (assets without metadata)
54
+ * 3. Enriches the response with fetched metadata
55
+ * 4. Calls next() at the end to continue the middleware chain
56
+ *
57
+ * @returns The middleware function for the assets pipeline.
58
+ */
59
+ get assetsMiddleware(): Middleware;
60
+ }
61
+ export {};
62
+ //# sourceMappingURL=TokenDataSource.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TokenDataSource.d.mts","sourceRoot":"","sources":["../../src/data-sources/TokenDataSource.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,+BAA+B;AAC3D,OAAO,KAAK,EAAE,SAAS,EAAE,4BAA4B;AAMrD,OAAO,KAAK,EAGV,UAAU,EAEX,qBAAiB;AAMlB,QAAA,MAAM,eAAe,oBAAoB,CAAC;AAQ1C;;GAEG;AACH,MAAM,MAAM,wCAAwC,GAAG;IACrD,IAAI,EAAE,GAAG,OAAO,eAAe,sBAAsB,CAAC;IACtD,OAAO,EAAE,MAAM,UAAU,CAAC;CAC3B,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,sBAAsB,GAAG,wCAAwC,CAAC;AAE9E;;;GAGG;AACH,MAAM,MAAM,6BAA6B,GAAG,KAAK,CAAC;AAElD,MAAM,MAAM,wBAAwB,GAAG,SAAS,CAC9C,OAAO,eAAe,EACtB,sBAAsB,EACtB,KAAK,CACN,CAAC;AAMF,MAAM,MAAM,sBAAsB,GAAG;IACnC,SAAS,EAAE,wBAAwB,CAAC;IACpC,mDAAmD;IACnD,cAAc,EAAE,iBAAiB,CAAC;CACnC,CAAC;AA2DF;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,eAAe;;IAC1B,QAAQ,CAAC,IAAI,qBAAmB;gBAOpB,OAAO,EAAE,sBAAsB;IA8D3C;;;;;;;;;;OAUG;IACH,IAAI,gBAAgB,IAAI,UAAU,CA0EjC;CACF"}
@@ -0,0 +1,207 @@
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 _TokenDataSource_instances, _TokenDataSource_messenger, _TokenDataSource_apiClient, _TokenDataSource_getSupportedNetworks, _TokenDataSource_filterAssetsByNetwork, _TokenDataSource_registerActionHandlers;
13
+ import { ApiPlatformClient } from "@metamask/core-backend";
14
+ import { parseCaipAssetType } from "@metamask/utils";
15
+ import { projectLogger, createModuleLogger } from "../logger.mjs";
16
+ import { forDataTypes } from "../types.mjs";
17
+ // ============================================================================
18
+ // CONSTANTS
19
+ // ============================================================================
20
+ const CONTROLLER_NAME = 'TokenDataSource';
21
+ const log = createModuleLogger(projectLogger, CONTROLLER_NAME);
22
+ // ============================================================================
23
+ // HELPER FUNCTIONS
24
+ // ============================================================================
25
+ /**
26
+ * Transform V3 API response to FungibleAssetMetadata for state storage.
27
+ *
28
+ * Mapping:
29
+ * - assetId → used to derive `type` (native/erc20/spl)
30
+ * - iconUrl → image
31
+ * - All other fields map directly
32
+ *
33
+ * @param assetId - CAIP-19 asset ID used to derive token type.
34
+ * @param assetData - V3 API response data.
35
+ * @returns FungibleAssetMetadata for state storage.
36
+ */
37
+ function transformV3AssetResponseToMetadata(assetId, assetData) {
38
+ const parsed = parseCaipAssetType(assetId);
39
+ let tokenType = 'erc20';
40
+ if (parsed.assetNamespace === 'slip44') {
41
+ tokenType = 'native';
42
+ }
43
+ else if (parsed.assetNamespace === 'spl') {
44
+ tokenType = 'spl';
45
+ }
46
+ const metadata = {
47
+ // Type derived from assetId
48
+ type: tokenType,
49
+ // BaseAssetMetadata fields
50
+ name: assetData.name,
51
+ symbol: assetData.symbol,
52
+ decimals: assetData.decimals,
53
+ image: assetData.iconUrl,
54
+ // Direct mapping fields
55
+ coingeckoId: assetData.coingeckoId,
56
+ occurrences: assetData.occurrences,
57
+ aggregators: assetData.aggregators,
58
+ labels: assetData.labels,
59
+ erc20Permit: assetData.erc20Permit,
60
+ fees: assetData.fees,
61
+ honeypotStatus: assetData.honeypotStatus,
62
+ storage: assetData.storage,
63
+ isContractVerified: assetData.isContractVerified,
64
+ description: assetData.description,
65
+ };
66
+ return metadata;
67
+ }
68
+ // ============================================================================
69
+ // TOKEN DATA SOURCE
70
+ // ============================================================================
71
+ /**
72
+ * TokenDataSource enriches responses with token metadata from the Tokens API.
73
+ *
74
+ * This middleware-based data source:
75
+ * - Checks detected assets for missing metadata/images
76
+ * - Fetches metadata from Tokens API v3 for assets needing enrichment
77
+ * - Merges fetched metadata into the response
78
+ *
79
+ * Usage:
80
+ * ```typescript
81
+ * // Create and initialize (registers messenger actions)
82
+ * const tokenDataSource = new TokenDataSource({ messenger, queryApiClient });
83
+ *
84
+ * // Later, get middleware via messenger
85
+ * const middleware = messenger.call('TokenDataSource:getAssetsMiddleware');
86
+ * ```
87
+ */
88
+ export class TokenDataSource {
89
+ constructor(options) {
90
+ _TokenDataSource_instances.add(this);
91
+ this.name = CONTROLLER_NAME;
92
+ _TokenDataSource_messenger.set(this, void 0);
93
+ /** ApiPlatformClient for cached API calls */
94
+ _TokenDataSource_apiClient.set(this, void 0);
95
+ __classPrivateFieldSet(this, _TokenDataSource_messenger, options.messenger, "f");
96
+ __classPrivateFieldSet(this, _TokenDataSource_apiClient, options.queryApiClient, "f");
97
+ __classPrivateFieldGet(this, _TokenDataSource_instances, "m", _TokenDataSource_registerActionHandlers).call(this);
98
+ }
99
+ /**
100
+ * Get the middleware for enriching responses with token metadata.
101
+ *
102
+ * This middleware:
103
+ * 1. Extracts the response from context
104
+ * 2. Fetches metadata for detected assets (assets without metadata)
105
+ * 3. Enriches the response with fetched metadata
106
+ * 4. Calls next() at the end to continue the middleware chain
107
+ *
108
+ * @returns The middleware function for the assets pipeline.
109
+ */
110
+ get assetsMiddleware() {
111
+ return forDataTypes(['metadata'], async (ctx, next) => {
112
+ // Extract response from context
113
+ const { response } = ctx;
114
+ // Only fetch metadata for detected assets (assets without metadata)
115
+ if (!response.detectedAssets) {
116
+ return next(ctx);
117
+ }
118
+ const { assetsMetadata: stateMetadata } = ctx.getAssetsState();
119
+ const assetIdsNeedingMetadata = new Set();
120
+ for (const detectedIds of Object.values(response.detectedAssets)) {
121
+ for (const assetId of detectedIds) {
122
+ // Skip if response already has metadata with image
123
+ const responseMetadata = response.assetsMetadata?.[assetId];
124
+ if (responseMetadata?.image) {
125
+ continue;
126
+ }
127
+ // Skip if state already has metadata with image
128
+ const existingMetadata = stateMetadata[assetId];
129
+ if (existingMetadata?.image) {
130
+ continue;
131
+ }
132
+ assetIdsNeedingMetadata.add(assetId);
133
+ }
134
+ }
135
+ if (assetIdsNeedingMetadata.size === 0) {
136
+ return next(ctx);
137
+ }
138
+ // Filter asset IDs to only include supported networks
139
+ const supportedNetworks = await __classPrivateFieldGet(this, _TokenDataSource_instances, "m", _TokenDataSource_getSupportedNetworks).call(this);
140
+ const supportedAssetIds = __classPrivateFieldGet(this, _TokenDataSource_instances, "m", _TokenDataSource_filterAssetsByNetwork).call(this, [...assetIdsNeedingMetadata], supportedNetworks);
141
+ if (supportedAssetIds.length === 0) {
142
+ return next(ctx);
143
+ }
144
+ try {
145
+ // Use ApiPlatformClient for fetching asset metadata
146
+ // API returns an array with assetId as a property on each item
147
+ const metadataResponse = await __classPrivateFieldGet(this, _TokenDataSource_apiClient, "f").tokens.fetchV3Assets(supportedAssetIds, {
148
+ includeIconUrl: true,
149
+ includeMarketData: true,
150
+ includeMetadata: true,
151
+ includeLabels: true,
152
+ includeRwaData: true,
153
+ });
154
+ response.assetsMetadata ?? (response.assetsMetadata = {});
155
+ for (const assetData of metadataResponse) {
156
+ const caipAssetId = assetData.assetId;
157
+ response.assetsMetadata[caipAssetId] =
158
+ transformV3AssetResponseToMetadata(assetData.assetId, assetData);
159
+ }
160
+ }
161
+ catch (error) {
162
+ log('Failed to fetch metadata', { error });
163
+ }
164
+ // Call next() at the end to continue the middleware chain
165
+ return next(ctx);
166
+ });
167
+ }
168
+ }
169
+ _TokenDataSource_messenger = new WeakMap(), _TokenDataSource_apiClient = new WeakMap(), _TokenDataSource_instances = new WeakSet(), _TokenDataSource_getSupportedNetworks =
170
+ /**
171
+ * Gets the supported networks from the API.
172
+ * Caching is handled by ApiPlatformClient.
173
+ *
174
+ * @returns Set of supported chain IDs in CAIP format
175
+ */
176
+ async function _TokenDataSource_getSupportedNetworks() {
177
+ try {
178
+ // Use v2/supportedNetworks which returns CAIP chain IDs
179
+ // ApiPlatformClient handles caching
180
+ const response = await __classPrivateFieldGet(this, _TokenDataSource_apiClient, "f").tokens.fetchTokenV2SupportedNetworks();
181
+ // Combine full and partial support networks
182
+ const allNetworks = [...response.fullSupport, ...response.partialSupport];
183
+ return new Set(allNetworks);
184
+ }
185
+ catch (error) {
186
+ log('Failed to fetch supported networks', { error });
187
+ return new Set();
188
+ }
189
+ }, _TokenDataSource_filterAssetsByNetwork = function _TokenDataSource_filterAssetsByNetwork(assetIds, supportedNetworks) {
190
+ return assetIds.filter((assetId) => {
191
+ try {
192
+ const parsed = parseCaipAssetType(assetId);
193
+ // chainId is in format "eip155:1" or "tron:728126428"
194
+ // parsed.chain has namespace and reference properties
195
+ const chainId = `${parsed.chain.namespace}:${parsed.chain.reference}`;
196
+ return supportedNetworks.has(chainId);
197
+ }
198
+ catch {
199
+ // If we can't parse the asset ID, filter it out
200
+ return false;
201
+ }
202
+ });
203
+ }, _TokenDataSource_registerActionHandlers = function _TokenDataSource_registerActionHandlers() {
204
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
205
+ __classPrivateFieldGet(this, _TokenDataSource_messenger, "f").registerActionHandler('TokenDataSource:getAssetsMiddleware', () => this.assetsMiddleware);
206
+ };
207
+ //# sourceMappingURL=TokenDataSource.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TokenDataSource.mjs","sourceRoot":"","sources":["../../src/data-sources/TokenDataSource.ts"],"names":[],"mappings":"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","sourcesContent":["import type { V3AssetResponse } from '@metamask/core-backend';\nimport { ApiPlatformClient } from '@metamask/core-backend';\nimport type { Messenger } from '@metamask/messenger';\nimport { parseCaipAssetType } from '@metamask/utils';\nimport type { CaipAssetType } from '@metamask/utils';\n\nimport { projectLogger, createModuleLogger } from '../logger';\nimport { forDataTypes } from '../types';\nimport type {\n Caip19AssetId,\n AssetMetadata,\n Middleware,\n FungibleAssetMetadata,\n} from '../types';\n\n// ============================================================================\n// CONSTANTS\n// ============================================================================\n\nconst CONTROLLER_NAME = 'TokenDataSource';\n\nconst log = createModuleLogger(projectLogger, CONTROLLER_NAME);\n\n// ============================================================================\n// MESSENGER TYPES\n// ============================================================================\n\n/**\n * Action to get the TokenDataSource middleware.\n */\nexport type TokenDataSourceGetAssetsMiddlewareAction = {\n type: `${typeof CONTROLLER_NAME}:getAssetsMiddleware`;\n handler: () => Middleware;\n};\n\n/**\n * All actions exposed by TokenDataSource.\n */\nexport type TokenDataSourceActions = TokenDataSourceGetAssetsMiddlewareAction;\n\n/**\n * TokenDataSource no longer needs external BackendApiClient actions.\n * It uses ApiPlatformClient directly.\n */\nexport type TokenDataSourceAllowedActions = never;\n\nexport type TokenDataSourceMessenger = Messenger<\n typeof CONTROLLER_NAME,\n TokenDataSourceActions,\n never\n>;\n\n// ============================================================================\n// OPTIONS\n// ============================================================================\n\nexport type TokenDataSourceOptions = {\n messenger: TokenDataSourceMessenger;\n /** ApiPlatformClient for API calls with caching */\n queryApiClient: ApiPlatformClient;\n};\n\n// ============================================================================\n// HELPER FUNCTIONS\n// ============================================================================\n\n/**\n * Transform V3 API response to FungibleAssetMetadata for state storage.\n *\n * Mapping:\n * - assetId → used to derive `type` (native/erc20/spl)\n * - iconUrl → image\n * - All other fields map directly\n *\n * @param assetId - CAIP-19 asset ID used to derive token type.\n * @param assetData - V3 API response data.\n * @returns FungibleAssetMetadata for state storage.\n */\nfunction transformV3AssetResponseToMetadata(\n assetId: string,\n assetData: V3AssetResponse,\n): AssetMetadata {\n const parsed = parseCaipAssetType(assetId as CaipAssetType);\n let tokenType: 'native' | 'erc20' | 'spl' = 'erc20';\n\n if (parsed.assetNamespace === 'slip44') {\n tokenType = 'native';\n } else if (parsed.assetNamespace === 'spl') {\n tokenType = 'spl';\n }\n\n const metadata: FungibleAssetMetadata = {\n // Type derived from assetId\n type: tokenType,\n // BaseAssetMetadata fields\n name: assetData.name,\n symbol: assetData.symbol,\n decimals: assetData.decimals,\n image: assetData.iconUrl,\n // Direct mapping fields\n coingeckoId: assetData.coingeckoId,\n occurrences: assetData.occurrences,\n aggregators: assetData.aggregators,\n labels: assetData.labels,\n erc20Permit: assetData.erc20Permit,\n fees: assetData.fees,\n honeypotStatus: assetData.honeypotStatus,\n storage: assetData.storage,\n isContractVerified: assetData.isContractVerified,\n description: assetData.description,\n };\n\n return metadata;\n}\n\n// ============================================================================\n// TOKEN DATA SOURCE\n// ============================================================================\n\n/**\n * TokenDataSource enriches responses with token metadata from the Tokens API.\n *\n * This middleware-based data source:\n * - Checks detected assets for missing metadata/images\n * - Fetches metadata from Tokens API v3 for assets needing enrichment\n * - Merges fetched metadata into the response\n *\n * Usage:\n * ```typescript\n * // Create and initialize (registers messenger actions)\n * const tokenDataSource = new TokenDataSource({ messenger, queryApiClient });\n *\n * // Later, get middleware via messenger\n * const middleware = messenger.call('TokenDataSource:getAssetsMiddleware');\n * ```\n */\nexport class TokenDataSource {\n readonly name = CONTROLLER_NAME;\n\n readonly #messenger: TokenDataSourceMessenger;\n\n /** ApiPlatformClient for cached API calls */\n readonly #apiClient: ApiPlatformClient;\n\n constructor(options: TokenDataSourceOptions) {\n this.#messenger = options.messenger;\n this.#apiClient = options.queryApiClient;\n this.#registerActionHandlers();\n }\n\n /**\n * Gets the supported networks from the API.\n * Caching is handled by ApiPlatformClient.\n *\n * @returns Set of supported chain IDs in CAIP format\n */\n async #getSupportedNetworks(): Promise<Set<string>> {\n try {\n // Use v2/supportedNetworks which returns CAIP chain IDs\n // ApiPlatformClient handles caching\n const response =\n await this.#apiClient.tokens.fetchTokenV2SupportedNetworks();\n\n // Combine full and partial support networks\n const allNetworks = [...response.fullSupport, ...response.partialSupport];\n\n return new Set(allNetworks);\n } catch (error) {\n log('Failed to fetch supported networks', { error });\n return new Set();\n }\n }\n\n /**\n * Filters asset IDs to only include those from supported networks.\n *\n * @param assetIds - Array of CAIP-19 asset IDs\n * @param supportedNetworks - Set of supported chain IDs\n * @returns Array of asset IDs from supported networks\n */\n #filterAssetsByNetwork(\n assetIds: string[],\n supportedNetworks: Set<string>,\n ): string[] {\n return assetIds.filter((assetId) => {\n try {\n const parsed = parseCaipAssetType(assetId as CaipAssetType);\n // chainId is in format \"eip155:1\" or \"tron:728126428\"\n // parsed.chain has namespace and reference properties\n const chainId = `${parsed.chain.namespace}:${parsed.chain.reference}`;\n return supportedNetworks.has(chainId);\n } catch {\n // If we can't parse the asset ID, filter it out\n return false;\n }\n });\n }\n\n #registerActionHandlers(): void {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (this.#messenger as any).registerActionHandler(\n 'TokenDataSource:getAssetsMiddleware',\n () => this.assetsMiddleware,\n );\n }\n\n /**\n * Get the middleware for enriching responses with token metadata.\n *\n * This middleware:\n * 1. Extracts the response from context\n * 2. Fetches metadata for detected assets (assets without metadata)\n * 3. Enriches the response with fetched metadata\n * 4. Calls next() at the end to continue the middleware chain\n *\n * @returns The middleware function for the assets pipeline.\n */\n get assetsMiddleware(): Middleware {\n return forDataTypes(['metadata'], async (ctx, next) => {\n // Extract response from context\n const { response } = ctx;\n\n // Only fetch metadata for detected assets (assets without metadata)\n if (!response.detectedAssets) {\n return next(ctx);\n }\n\n const { assetsMetadata: stateMetadata } = ctx.getAssetsState();\n const assetIdsNeedingMetadata = new Set<string>();\n\n for (const detectedIds of Object.values(response.detectedAssets)) {\n for (const assetId of detectedIds) {\n // Skip if response already has metadata with image\n const responseMetadata = response.assetsMetadata?.[assetId];\n if (responseMetadata?.image) {\n continue;\n }\n\n // Skip if state already has metadata with image\n const existingMetadata = stateMetadata[assetId];\n if (existingMetadata?.image) {\n continue;\n }\n\n assetIdsNeedingMetadata.add(assetId);\n }\n }\n\n if (assetIdsNeedingMetadata.size === 0) {\n return next(ctx);\n }\n\n // Filter asset IDs to only include supported networks\n const supportedNetworks = await this.#getSupportedNetworks();\n const supportedAssetIds = this.#filterAssetsByNetwork(\n [...assetIdsNeedingMetadata],\n supportedNetworks,\n );\n\n if (supportedAssetIds.length === 0) {\n return next(ctx);\n }\n\n try {\n // Use ApiPlatformClient for fetching asset metadata\n // API returns an array with assetId as a property on each item\n const metadataResponse = await this.#apiClient.tokens.fetchV3Assets(\n supportedAssetIds,\n {\n includeIconUrl: true,\n includeMarketData: true,\n includeMetadata: true,\n includeLabels: true,\n includeRwaData: true,\n },\n );\n\n response.assetsMetadata ??= {};\n\n for (const assetData of metadataResponse) {\n const caipAssetId = assetData.assetId as Caip19AssetId;\n response.assetsMetadata[caipAssetId] =\n transformV3AssetResponseToMetadata(assetData.assetId, assetData);\n }\n } catch (error) {\n log('Failed to fetch metadata', { error });\n }\n\n // Call next() at the end to continue the middleware chain\n return next(ctx);\n });\n }\n}\n"]}