@metamask-previews/network-enablement-controller 3.1.0-preview-ac95634 → 3.1.0-preview-5460dce

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.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ### Added
11
+
12
+ - Add `ChainService` module for SLIP-44 coin type resolution ([#7034](https://github.com/MetaMask/core/pull/7034))
13
+ - Add `getSlip44ByChainId()` function to resolve SLIP-44 coin types from EVM chain IDs
14
+ - Add `getNativeCaip19()` function to generate CAIP-19 asset identifiers for native currencies
15
+ - Add support for fetching chain metadata from chainid.network with caching
16
+ - Add `@metamask/slip44` dependency for symbol-to-coin-type mapping
17
+
10
18
  ### Fixed
11
19
 
12
20
  - include additional popular networks now enabled by default ([#7014](https://github.com/MetaMask/core/pull/7014))
@@ -0,0 +1,128 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.getNativeCaip19 = exports.getSlip44ByChainId = void 0;
7
+ const controller_utils_1 = require("@metamask/controller-utils");
8
+ // @ts-expect-error - JSON imports need resolveJsonModule
9
+ const slip44_json_1 = __importDefault(require("@metamask/slip44/slip44.json"));
10
+ const utils_1 = require("@metamask/utils");
11
+ /** ---- Constants / Cache ---- */
12
+ const CHAINS_JSON_URL = 'https://chainid.network/chains.json';
13
+ const CACHE_DURATION_MS = 30 * 60 * 1000; // 30 min
14
+ let chainsCache = null;
15
+ let chainsFetchedAt = 0;
16
+ let inflight = null;
17
+ /** Build a quick symbol -> coinType map from slip44 dataset */
18
+ const SLIP44_BY_SYMBOL = (() => {
19
+ const map = {};
20
+ slip44_json_1.default.forEach((e) => {
21
+ const sym = e.symbol?.toUpperCase();
22
+ if (sym && typeof e.index === 'number' && !(sym in map)) {
23
+ map[sym] = e.index;
24
+ }
25
+ });
26
+ return map;
27
+ })();
28
+ /** Fallbacks for common native symbols */
29
+ const COMMON_SYMBOL_DEFAULTS = {
30
+ SOL: 501,
31
+ BTC: 0,
32
+ ETH: 60,
33
+ POL: 966,
34
+ MATIC: 966,
35
+ BNB: 714,
36
+ AVAX: 9000,
37
+ SEI: 19000118,
38
+ MON: 268435779,
39
+ FTM: 1007,
40
+ XDAI: 700,
41
+ };
42
+ /**
43
+ * Fetch with cache + in-flight dedupe.
44
+ *
45
+ * @returns The chains JSON data.
46
+ */
47
+ async function fetchChains() {
48
+ const now = Date.now();
49
+ if (chainsCache && now - chainsFetchedAt < CACHE_DURATION_MS) {
50
+ return chainsCache;
51
+ }
52
+ if (inflight) {
53
+ return inflight;
54
+ }
55
+ inflight = (async () => {
56
+ const data = (await (0, controller_utils_1.handleFetch)(CHAINS_JSON_URL));
57
+ if (!Array.isArray(data)) {
58
+ throw new Error('chains.json: unexpected shape');
59
+ }
60
+ // Minimal validation; keep what we need
61
+ const parsed = data
62
+ .filter((x) => x && typeof x === 'object')
63
+ .map((x) => {
64
+ const nativeCurrency = x.nativeCurrency;
65
+ return {
66
+ chainId: Number(x.chainId),
67
+ name: x.name,
68
+ nativeCurrency,
69
+ };
70
+ })
71
+ .filter((x) => Number.isFinite(x.chainId));
72
+ chainsCache = parsed;
73
+ chainsFetchedAt = now;
74
+ inflight = null; // clear in-flight marker
75
+ return parsed;
76
+ })();
77
+ try {
78
+ return await inflight;
79
+ }
80
+ catch (e) {
81
+ inflight = null;
82
+ throw e;
83
+ }
84
+ }
85
+ /**
86
+ * Resolve SLIP-44 coinType for a given chainId (EVM only).
87
+ *
88
+ * @param chainId - The chain ID to resolve.
89
+ * @returns The SLIP-44 coin type as a string, or null if not found.
90
+ */
91
+ async function getSlip44ByChainId(chainId) {
92
+ try {
93
+ // 1) Lookup by native symbol from chains.json
94
+ const chains = await fetchChains();
95
+ const chain = chains.find((c) => c.chainId === chainId);
96
+ const symbol = chain?.nativeCurrency?.symbol?.toUpperCase();
97
+ if (symbol) {
98
+ // Exact symbol match from slip44 dataset
99
+ const coinType = SLIP44_BY_SYMBOL[symbol] ?? COMMON_SYMBOL_DEFAULTS[symbol];
100
+ if (coinType !== undefined) {
101
+ return String(coinType);
102
+ }
103
+ }
104
+ // 2) Heuristic for ETH-based L2s: default to ETH coin type
105
+ // Many L2s (Arbitrum, Optimism, Base, Linea, Scroll, zkSync, etc.) use ETH as native
106
+ return '60';
107
+ }
108
+ catch (err) {
109
+ // Prefer null for ergonomics (callers can fallback silently)
110
+ console.error(`getSlip44ByChainId(${chainId}) failed:`, err);
111
+ return null;
112
+ }
113
+ }
114
+ exports.getSlip44ByChainId = getSlip44ByChainId;
115
+ /**
116
+ * Convenience: native CAIP-19 for an EVM chain.
117
+ *
118
+ * @param chainId - The chain ID to generate CAIP-19 for.
119
+ * @returns The CAIP-19 asset type string, or null if not found.
120
+ */
121
+ async function getNativeCaip19(chainId) {
122
+ const coinType = await getSlip44ByChainId(chainId);
123
+ return coinType
124
+ ? (0, utils_1.toCaipAssetType)('eip155', String(chainId), 'slip44', coinType)
125
+ : null;
126
+ }
127
+ exports.getNativeCaip19 = getNativeCaip19;
128
+ //# sourceMappingURL=ChainService.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChainService.cjs","sourceRoot":"","sources":["../src/ChainService.ts"],"names":[],"mappings":"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","sourcesContent":["import { handleFetch } from '@metamask/controller-utils';\n// @ts-expect-error - JSON imports need resolveJsonModule\nimport slip44 from '@metamask/slip44/slip44.json';\nimport { toCaipAssetType } from '@metamask/utils';\n\n/** ---- Types ---- */\ntype ChainsJsonItem = {\n chainId: number;\n name: string;\n nativeCurrency?: { symbol?: string; name?: string; decimals?: number };\n};\n\ntype Slip44Entry = {\n index: number; // the coin type (e.g., 60)\n symbol?: string; // e.g., 'ETH', 'BNB'\n name?: string; // e.g., 'Ethereum'\n};\n\n/** ---- Constants / Cache ---- */\nconst CHAINS_JSON_URL = 'https://chainid.network/chains.json';\nconst CACHE_DURATION_MS = 30 * 60 * 1000; // 30 min\n\nlet chainsCache: ChainsJsonItem[] | null = null;\nlet chainsFetchedAt = 0;\nlet inflight: Promise<ChainsJsonItem[]> | null = null;\n\n/** Build a quick symbol -> coinType map from slip44 dataset */\nconst SLIP44_BY_SYMBOL: Record<string, number> = (() => {\n const map: Record<string, number> = {};\n (slip44 as Slip44Entry[]).forEach((e) => {\n const sym = e.symbol?.toUpperCase();\n if (sym && typeof e.index === 'number' && !(sym in map)) {\n map[sym] = e.index;\n }\n });\n return map;\n})();\n\n/** Fallbacks for common native symbols */\nconst COMMON_SYMBOL_DEFAULTS: Record<string, number> = {\n SOL: 501,\n BTC: 0,\n ETH: 60,\n POL: 966,\n MATIC: 966,\n BNB: 714,\n AVAX: 9000,\n SEI: 19000118,\n MON: 268435779,\n FTM: 1007,\n XDAI: 700,\n};\n\n/**\n * Fetch with cache + in-flight dedupe.\n *\n * @returns The chains JSON data.\n */\nasync function fetchChains(): Promise<ChainsJsonItem[]> {\n const now = Date.now();\n if (chainsCache && now - chainsFetchedAt < CACHE_DURATION_MS) {\n return chainsCache;\n }\n if (inflight) {\n return inflight;\n }\n\n inflight = (async () => {\n const data = (await handleFetch(CHAINS_JSON_URL)) as unknown;\n if (!Array.isArray(data)) {\n throw new Error('chains.json: unexpected shape');\n }\n // Minimal validation; keep what we need\n const parsed = data\n .filter((x) => x && typeof x === 'object')\n .map((x: Record<string, unknown>): ChainsJsonItem => {\n const nativeCurrency = x.nativeCurrency as\n | { symbol?: string; name?: string; decimals?: number }\n | undefined;\n return {\n chainId: Number(x.chainId),\n name: x.name as string,\n nativeCurrency,\n };\n })\n .filter((x) => Number.isFinite(x.chainId));\n\n chainsCache = parsed;\n chainsFetchedAt = now;\n inflight = null; // clear in-flight marker\n return parsed;\n })();\n\n try {\n return await inflight;\n } catch (e) {\n inflight = null;\n throw e;\n }\n}\n\n/**\n * Resolve SLIP-44 coinType for a given chainId (EVM only).\n *\n * @param chainId - The chain ID to resolve.\n * @returns The SLIP-44 coin type as a string, or null if not found.\n */\nexport async function getSlip44ByChainId(\n chainId: number,\n): Promise<string | null> {\n try {\n // 1) Lookup by native symbol from chains.json\n const chains = await fetchChains();\n const chain = chains.find((c) => c.chainId === chainId);\n const symbol = chain?.nativeCurrency?.symbol?.toUpperCase();\n\n if (symbol) {\n // Exact symbol match from slip44 dataset\n const coinType =\n SLIP44_BY_SYMBOL[symbol] ?? COMMON_SYMBOL_DEFAULTS[symbol];\n if (coinType !== undefined) {\n return String(coinType);\n }\n }\n\n // 2) Heuristic for ETH-based L2s: default to ETH coin type\n // Many L2s (Arbitrum, Optimism, Base, Linea, Scroll, zkSync, etc.) use ETH as native\n return '60';\n } catch (err) {\n // Prefer null for ergonomics (callers can fallback silently)\n console.error(`getSlip44ByChainId(${chainId}) failed:`, err);\n return null;\n }\n}\n\n/**\n * Convenience: native CAIP-19 for an EVM chain.\n *\n * @param chainId - The chain ID to generate CAIP-19 for.\n * @returns The CAIP-19 asset type string, or null if not found.\n */\nexport async function getNativeCaip19(chainId: number): Promise<string | null> {\n const coinType = await getSlip44ByChainId(chainId);\n return coinType\n ? toCaipAssetType('eip155', String(chainId), 'slip44', coinType)\n : null;\n}\n"]}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Resolve SLIP-44 coinType for a given chainId (EVM only).
3
+ *
4
+ * @param chainId - The chain ID to resolve.
5
+ * @returns The SLIP-44 coin type as a string, or null if not found.
6
+ */
7
+ export declare function getSlip44ByChainId(chainId: number): Promise<string | null>;
8
+ /**
9
+ * Convenience: native CAIP-19 for an EVM chain.
10
+ *
11
+ * @param chainId - The chain ID to generate CAIP-19 for.
12
+ * @returns The CAIP-19 asset type string, or null if not found.
13
+ */
14
+ export declare function getNativeCaip19(chainId: number): Promise<string | null>;
15
+ //# sourceMappingURL=ChainService.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChainService.d.cts","sourceRoot":"","sources":["../src/ChainService.ts"],"names":[],"mappings":"AAqGA;;;;;GAKG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAwBxB;AAED;;;;;GAKG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAK7E"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Resolve SLIP-44 coinType for a given chainId (EVM only).
3
+ *
4
+ * @param chainId - The chain ID to resolve.
5
+ * @returns The SLIP-44 coin type as a string, or null if not found.
6
+ */
7
+ export declare function getSlip44ByChainId(chainId: number): Promise<string | null>;
8
+ /**
9
+ * Convenience: native CAIP-19 for an EVM chain.
10
+ *
11
+ * @param chainId - The chain ID to generate CAIP-19 for.
12
+ * @returns The CAIP-19 asset type string, or null if not found.
13
+ */
14
+ export declare function getNativeCaip19(chainId: number): Promise<string | null>;
15
+ //# sourceMappingURL=ChainService.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChainService.d.mts","sourceRoot":"","sources":["../src/ChainService.ts"],"names":[],"mappings":"AAqGA;;;;;GAKG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAwBxB;AAED;;;;;GAKG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAK7E"}
@@ -0,0 +1,120 @@
1
+ import { handleFetch } from "@metamask/controller-utils";
2
+ // @ts-expect-error - JSON imports need resolveJsonModule
3
+ import slip44 from "@metamask/slip44/slip44.json" assert { type: "json" };
4
+ import { toCaipAssetType } from "@metamask/utils";
5
+ /** ---- Constants / Cache ---- */
6
+ const CHAINS_JSON_URL = 'https://chainid.network/chains.json';
7
+ const CACHE_DURATION_MS = 30 * 60 * 1000; // 30 min
8
+ let chainsCache = null;
9
+ let chainsFetchedAt = 0;
10
+ let inflight = null;
11
+ /** Build a quick symbol -> coinType map from slip44 dataset */
12
+ const SLIP44_BY_SYMBOL = (() => {
13
+ const map = {};
14
+ slip44.forEach((e) => {
15
+ const sym = e.symbol?.toUpperCase();
16
+ if (sym && typeof e.index === 'number' && !(sym in map)) {
17
+ map[sym] = e.index;
18
+ }
19
+ });
20
+ return map;
21
+ })();
22
+ /** Fallbacks for common native symbols */
23
+ const COMMON_SYMBOL_DEFAULTS = {
24
+ SOL: 501,
25
+ BTC: 0,
26
+ ETH: 60,
27
+ POL: 966,
28
+ MATIC: 966,
29
+ BNB: 714,
30
+ AVAX: 9000,
31
+ SEI: 19000118,
32
+ MON: 268435779,
33
+ FTM: 1007,
34
+ XDAI: 700,
35
+ };
36
+ /**
37
+ * Fetch with cache + in-flight dedupe.
38
+ *
39
+ * @returns The chains JSON data.
40
+ */
41
+ async function fetchChains() {
42
+ const now = Date.now();
43
+ if (chainsCache && now - chainsFetchedAt < CACHE_DURATION_MS) {
44
+ return chainsCache;
45
+ }
46
+ if (inflight) {
47
+ return inflight;
48
+ }
49
+ inflight = (async () => {
50
+ const data = (await handleFetch(CHAINS_JSON_URL));
51
+ if (!Array.isArray(data)) {
52
+ throw new Error('chains.json: unexpected shape');
53
+ }
54
+ // Minimal validation; keep what we need
55
+ const parsed = data
56
+ .filter((x) => x && typeof x === 'object')
57
+ .map((x) => {
58
+ const nativeCurrency = x.nativeCurrency;
59
+ return {
60
+ chainId: Number(x.chainId),
61
+ name: x.name,
62
+ nativeCurrency,
63
+ };
64
+ })
65
+ .filter((x) => Number.isFinite(x.chainId));
66
+ chainsCache = parsed;
67
+ chainsFetchedAt = now;
68
+ inflight = null; // clear in-flight marker
69
+ return parsed;
70
+ })();
71
+ try {
72
+ return await inflight;
73
+ }
74
+ catch (e) {
75
+ inflight = null;
76
+ throw e;
77
+ }
78
+ }
79
+ /**
80
+ * Resolve SLIP-44 coinType for a given chainId (EVM only).
81
+ *
82
+ * @param chainId - The chain ID to resolve.
83
+ * @returns The SLIP-44 coin type as a string, or null if not found.
84
+ */
85
+ export async function getSlip44ByChainId(chainId) {
86
+ try {
87
+ // 1) Lookup by native symbol from chains.json
88
+ const chains = await fetchChains();
89
+ const chain = chains.find((c) => c.chainId === chainId);
90
+ const symbol = chain?.nativeCurrency?.symbol?.toUpperCase();
91
+ if (symbol) {
92
+ // Exact symbol match from slip44 dataset
93
+ const coinType = SLIP44_BY_SYMBOL[symbol] ?? COMMON_SYMBOL_DEFAULTS[symbol];
94
+ if (coinType !== undefined) {
95
+ return String(coinType);
96
+ }
97
+ }
98
+ // 2) Heuristic for ETH-based L2s: default to ETH coin type
99
+ // Many L2s (Arbitrum, Optimism, Base, Linea, Scroll, zkSync, etc.) use ETH as native
100
+ return '60';
101
+ }
102
+ catch (err) {
103
+ // Prefer null for ergonomics (callers can fallback silently)
104
+ console.error(`getSlip44ByChainId(${chainId}) failed:`, err);
105
+ return null;
106
+ }
107
+ }
108
+ /**
109
+ * Convenience: native CAIP-19 for an EVM chain.
110
+ *
111
+ * @param chainId - The chain ID to generate CAIP-19 for.
112
+ * @returns The CAIP-19 asset type string, or null if not found.
113
+ */
114
+ export async function getNativeCaip19(chainId) {
115
+ const coinType = await getSlip44ByChainId(chainId);
116
+ return coinType
117
+ ? toCaipAssetType('eip155', String(chainId), 'slip44', coinType)
118
+ : null;
119
+ }
120
+ //# sourceMappingURL=ChainService.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChainService.mjs","sourceRoot":"","sources":["../src/ChainService.ts"],"names":[],"mappings":"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","sourcesContent":["import { handleFetch } from '@metamask/controller-utils';\n// @ts-expect-error - JSON imports need resolveJsonModule\nimport slip44 from '@metamask/slip44/slip44.json';\nimport { toCaipAssetType } from '@metamask/utils';\n\n/** ---- Types ---- */\ntype ChainsJsonItem = {\n chainId: number;\n name: string;\n nativeCurrency?: { symbol?: string; name?: string; decimals?: number };\n};\n\ntype Slip44Entry = {\n index: number; // the coin type (e.g., 60)\n symbol?: string; // e.g., 'ETH', 'BNB'\n name?: string; // e.g., 'Ethereum'\n};\n\n/** ---- Constants / Cache ---- */\nconst CHAINS_JSON_URL = 'https://chainid.network/chains.json';\nconst CACHE_DURATION_MS = 30 * 60 * 1000; // 30 min\n\nlet chainsCache: ChainsJsonItem[] | null = null;\nlet chainsFetchedAt = 0;\nlet inflight: Promise<ChainsJsonItem[]> | null = null;\n\n/** Build a quick symbol -> coinType map from slip44 dataset */\nconst SLIP44_BY_SYMBOL: Record<string, number> = (() => {\n const map: Record<string, number> = {};\n (slip44 as Slip44Entry[]).forEach((e) => {\n const sym = e.symbol?.toUpperCase();\n if (sym && typeof e.index === 'number' && !(sym in map)) {\n map[sym] = e.index;\n }\n });\n return map;\n})();\n\n/** Fallbacks for common native symbols */\nconst COMMON_SYMBOL_DEFAULTS: Record<string, number> = {\n SOL: 501,\n BTC: 0,\n ETH: 60,\n POL: 966,\n MATIC: 966,\n BNB: 714,\n AVAX: 9000,\n SEI: 19000118,\n MON: 268435779,\n FTM: 1007,\n XDAI: 700,\n};\n\n/**\n * Fetch with cache + in-flight dedupe.\n *\n * @returns The chains JSON data.\n */\nasync function fetchChains(): Promise<ChainsJsonItem[]> {\n const now = Date.now();\n if (chainsCache && now - chainsFetchedAt < CACHE_DURATION_MS) {\n return chainsCache;\n }\n if (inflight) {\n return inflight;\n }\n\n inflight = (async () => {\n const data = (await handleFetch(CHAINS_JSON_URL)) as unknown;\n if (!Array.isArray(data)) {\n throw new Error('chains.json: unexpected shape');\n }\n // Minimal validation; keep what we need\n const parsed = data\n .filter((x) => x && typeof x === 'object')\n .map((x: Record<string, unknown>): ChainsJsonItem => {\n const nativeCurrency = x.nativeCurrency as\n | { symbol?: string; name?: string; decimals?: number }\n | undefined;\n return {\n chainId: Number(x.chainId),\n name: x.name as string,\n nativeCurrency,\n };\n })\n .filter((x) => Number.isFinite(x.chainId));\n\n chainsCache = parsed;\n chainsFetchedAt = now;\n inflight = null; // clear in-flight marker\n return parsed;\n })();\n\n try {\n return await inflight;\n } catch (e) {\n inflight = null;\n throw e;\n }\n}\n\n/**\n * Resolve SLIP-44 coinType for a given chainId (EVM only).\n *\n * @param chainId - The chain ID to resolve.\n * @returns The SLIP-44 coin type as a string, or null if not found.\n */\nexport async function getSlip44ByChainId(\n chainId: number,\n): Promise<string | null> {\n try {\n // 1) Lookup by native symbol from chains.json\n const chains = await fetchChains();\n const chain = chains.find((c) => c.chainId === chainId);\n const symbol = chain?.nativeCurrency?.symbol?.toUpperCase();\n\n if (symbol) {\n // Exact symbol match from slip44 dataset\n const coinType =\n SLIP44_BY_SYMBOL[symbol] ?? COMMON_SYMBOL_DEFAULTS[symbol];\n if (coinType !== undefined) {\n return String(coinType);\n }\n }\n\n // 2) Heuristic for ETH-based L2s: default to ETH coin type\n // Many L2s (Arbitrum, Optimism, Base, Linea, Scroll, zkSync, etc.) use ETH as native\n return '60';\n } catch (err) {\n // Prefer null for ergonomics (callers can fallback silently)\n console.error(`getSlip44ByChainId(${chainId}) failed:`, err);\n return null;\n }\n}\n\n/**\n * Convenience: native CAIP-19 for an EVM chain.\n *\n * @param chainId - The chain ID to generate CAIP-19 for.\n * @returns The CAIP-19 asset type string, or null if not found.\n */\nexport async function getNativeCaip19(chainId: number): Promise<string | null> {\n const coinType = await getSlip44ByChainId(chainId);\n return coinType\n ? toCaipAssetType('eip155', String(chainId), 'slip44', coinType)\n : null;\n}\n"]}
@@ -4,13 +4,20 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
4
4
  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");
5
5
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
6
6
  };
7
- var _NetworkEnablementController_instances, _NetworkEnablementController_ensureNamespaceBucket, _NetworkEnablementController_isInPopularNetworksMode, _NetworkEnablementController_removeNetworkEntry, _NetworkEnablementController_onAddNetwork;
7
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
8
+ if (kind === "m") throw new TypeError("Private method is not writable");
9
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
10
+ 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");
11
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
12
+ };
13
+ var _NetworkEnablementController_instances, _NetworkEnablementController_initialized, _NetworkEnablementController_ensureNamespaceBucket, _NetworkEnablementController_ensureSlip44NamespaceBucket, _NetworkEnablementController_isInPopularNetworksMode, _NetworkEnablementController_removeNetworkEntry, _NetworkEnablementController_onAddNetwork;
8
14
  Object.defineProperty(exports, "__esModule", { value: true });
9
15
  exports.NetworkEnablementController = void 0;
10
16
  const base_controller_1 = require("@metamask/base-controller");
11
17
  const controller_utils_1 = require("@metamask/controller-utils");
12
18
  const keyring_api_1 = require("@metamask/keyring-api");
13
19
  const utils_1 = require("@metamask/utils");
20
+ const ChainService_1 = require("./ChainService.cjs");
14
21
  const constants_1 = require("./constants.cjs");
15
22
  const utils_2 = require("./utils.cjs");
16
23
  const controllerName = 'NetworkEnablementController';
@@ -47,6 +54,33 @@ const getDefaultNetworkEnablementControllerState = () => ({
47
54
  [keyring_api_1.TrxScope.Shasta]: false,
48
55
  },
49
56
  },
57
+ slip44: {
58
+ [utils_1.KnownCaipNamespace.Eip155]: {
59
+ [controller_utils_1.ChainId[controller_utils_1.BuiltInNetworkName.Mainnet]]: '60',
60
+ [controller_utils_1.ChainId[controller_utils_1.BuiltInNetworkName.LineaMainnet]]: '69',
61
+ [controller_utils_1.ChainId[controller_utils_1.BuiltInNetworkName.BaseMainnet]]: '8453',
62
+ [controller_utils_1.ChainId[controller_utils_1.BuiltInNetworkName.ArbitrumOne]]: '42161',
63
+ [controller_utils_1.ChainId[controller_utils_1.BuiltInNetworkName.BscMainnet]]: '56',
64
+ [controller_utils_1.ChainId[controller_utils_1.BuiltInNetworkName.OptimismMainnet]]: '10',
65
+ [controller_utils_1.ChainId[controller_utils_1.BuiltInNetworkName.PolygonMainnet]]: '966',
66
+ [controller_utils_1.ChainId[controller_utils_1.BuiltInNetworkName.SeiMainnet]]: '19000118', // SEI
67
+ },
68
+ [utils_1.KnownCaipNamespace.Solana]: {
69
+ [keyring_api_1.SolScope.Mainnet]: '501',
70
+ [keyring_api_1.SolScope.Testnet]: '501',
71
+ [keyring_api_1.SolScope.Devnet]: '501', // SOL
72
+ },
73
+ [utils_1.KnownCaipNamespace.Bip122]: {
74
+ [keyring_api_1.BtcScope.Mainnet]: '0',
75
+ [keyring_api_1.BtcScope.Testnet]: '0',
76
+ [keyring_api_1.BtcScope.Signet]: '0', // BTC
77
+ },
78
+ [utils_1.KnownCaipNamespace.Tron]: {
79
+ [keyring_api_1.TrxScope.Mainnet]: '195',
80
+ [keyring_api_1.TrxScope.Nile]: '195',
81
+ [keyring_api_1.TrxScope.Shasta]: '195', // TRX
82
+ },
83
+ },
50
84
  });
51
85
  // Metadata for the controller state
52
86
  const metadata = {
@@ -56,6 +90,12 @@ const metadata = {
56
90
  includeInDebugSnapshot: true,
57
91
  usedInUi: true,
58
92
  },
93
+ slip44: {
94
+ includeInStateLogs: true,
95
+ persist: true,
96
+ includeInDebugSnapshot: true,
97
+ usedInUi: true,
98
+ },
59
99
  };
60
100
  /**
61
101
  * Controller responsible for managing network enablement state across different blockchain networks.
@@ -85,8 +125,12 @@ class NetworkEnablementController extends base_controller_1.BaseController {
85
125
  },
86
126
  });
87
127
  _NetworkEnablementController_instances.add(this);
128
+ _NetworkEnablementController_initialized.set(this, false);
88
129
  messenger.subscribe('NetworkController:networkAdded', ({ chainId }) => {
89
- __classPrivateFieldGet(this, _NetworkEnablementController_instances, "m", _NetworkEnablementController_onAddNetwork).call(this, chainId);
130
+ // Handle async network addition
131
+ __classPrivateFieldGet(this, _NetworkEnablementController_instances, "m", _NetworkEnablementController_onAddNetwork).call(this, chainId).catch((error) => {
132
+ console.error('Error adding network:', error);
133
+ });
90
134
  });
91
135
  messenger.subscribe('NetworkController:networkRemoved', ({ chainId }) => {
92
136
  __classPrivateFieldGet(this, _NetworkEnablementController_instances, "m", _NetworkEnablementController_removeNetworkEntry).call(this, chainId);
@@ -149,8 +193,9 @@ class NetworkEnablementController extends base_controller_1.BaseController {
149
193
  throw new Error(`Chain ID ${chainId} belongs to namespace ${derivedNamespace}, but namespace ${namespace} was specified`);
150
194
  }
151
195
  this.update((s) => {
152
- // Ensure the namespace bucket exists
196
+ // Ensure the namespace buckets exist
153
197
  __classPrivateFieldGet(this, _NetworkEnablementController_instances, "m", _NetworkEnablementController_ensureNamespaceBucket).call(this, s, namespace);
198
+ __classPrivateFieldGet(this, _NetworkEnablementController_instances, "m", _NetworkEnablementController_ensureSlip44NamespaceBucket).call(this, s, namespace);
154
199
  // Disable all networks in the specified namespace first
155
200
  if (s.enabledNetworkMap[namespace]) {
156
201
  Object.keys(s.enabledNetworkMap[namespace]).forEach((key) => {
@@ -187,8 +232,9 @@ class NetworkEnablementController extends base_controller_1.BaseController {
187
232
  const { namespace, storageKey } = (0, utils_2.deriveKeys)(chainId);
188
233
  // Check if network exists in NetworkController configurations
189
234
  if (networkControllerState.networkConfigurationsByChainId[chainId]) {
190
- // Ensure namespace bucket exists
235
+ // Ensure namespace buckets exist
191
236
  __classPrivateFieldGet(this, _NetworkEnablementController_instances, "m", _NetworkEnablementController_ensureNamespaceBucket).call(this, s, namespace);
237
+ __classPrivateFieldGet(this, _NetworkEnablementController_instances, "m", _NetworkEnablementController_ensureSlip44NamespaceBucket).call(this, s, namespace);
192
238
  // Enable the network
193
239
  s.enabledNetworkMap[namespace][storageKey] = true;
194
240
  }
@@ -196,16 +242,18 @@ class NetworkEnablementController extends base_controller_1.BaseController {
196
242
  // Enable Solana mainnet if it exists in MultichainNetworkController configurations
197
243
  const solanaKeys = (0, utils_2.deriveKeys)(keyring_api_1.SolScope.Mainnet);
198
244
  if (multichainState.multichainNetworkConfigurationsByChainId[keyring_api_1.SolScope.Mainnet]) {
199
- // Ensure namespace bucket exists
245
+ // Ensure namespace buckets exist
200
246
  __classPrivateFieldGet(this, _NetworkEnablementController_instances, "m", _NetworkEnablementController_ensureNamespaceBucket).call(this, s, solanaKeys.namespace);
247
+ __classPrivateFieldGet(this, _NetworkEnablementController_instances, "m", _NetworkEnablementController_ensureSlip44NamespaceBucket).call(this, s, solanaKeys.namespace);
201
248
  // Enable Solana mainnet
202
249
  s.enabledNetworkMap[solanaKeys.namespace][solanaKeys.storageKey] = true;
203
250
  }
204
251
  // Enable Bitcoin mainnet if it exists in MultichainNetworkController configurations
205
252
  const bitcoinKeys = (0, utils_2.deriveKeys)(keyring_api_1.BtcScope.Mainnet);
206
253
  if (multichainState.multichainNetworkConfigurationsByChainId[keyring_api_1.BtcScope.Mainnet]) {
207
- // Ensure namespace bucket exists
254
+ // Ensure namespace buckets exist
208
255
  __classPrivateFieldGet(this, _NetworkEnablementController_instances, "m", _NetworkEnablementController_ensureNamespaceBucket).call(this, s, bitcoinKeys.namespace);
256
+ __classPrivateFieldGet(this, _NetworkEnablementController_instances, "m", _NetworkEnablementController_ensureSlip44NamespaceBucket).call(this, s, bitcoinKeys.namespace);
209
257
  // Enable Bitcoin mainnet
210
258
  s.enabledNetworkMap[bitcoinKeys.namespace][bitcoinKeys.storageKey] =
211
259
  true;
@@ -213,8 +261,9 @@ class NetworkEnablementController extends base_controller_1.BaseController {
213
261
  // Enable Tron mainnet if it exists in MultichainNetworkController configurations
214
262
  const tronKeys = (0, utils_2.deriveKeys)(keyring_api_1.TrxScope.Mainnet);
215
263
  if (multichainState.multichainNetworkConfigurationsByChainId[keyring_api_1.TrxScope.Mainnet]) {
216
- // Ensure namespace bucket exists
264
+ // Ensure namespace buckets exist
217
265
  __classPrivateFieldGet(this, _NetworkEnablementController_instances, "m", _NetworkEnablementController_ensureNamespaceBucket).call(this, s, tronKeys.namespace);
266
+ __classPrivateFieldGet(this, _NetworkEnablementController_instances, "m", _NetworkEnablementController_ensureSlip44NamespaceBucket).call(this, s, tronKeys.namespace);
218
267
  // Enable Tron mainnet
219
268
  s.enabledNetworkMap[tronKeys.namespace][tronKeys.storageKey] = true;
220
269
  }
@@ -227,20 +276,29 @@ class NetworkEnablementController extends base_controller_1.BaseController {
227
276
  * and MultichainNetworkController and syncs the enabled network map accordingly.
228
277
  * It ensures proper namespace buckets exist for all configured networks and only
229
278
  * adds missing networks with a default value of false, preserving existing user settings.
279
+ * Additionally, it fetches slip44 values for EIP-155 networks that don't have them.
280
+ *
281
+ * This method only runs once per controller instance to avoid unnecessary API calls.
282
+ * Subsequent calls will return immediately without performing any operations.
283
+ * Use `reinit()` if you need to force re-initialization.
230
284
  *
231
285
  * This method should be called after the NetworkController and MultichainNetworkController
232
286
  * have been initialized and their configurations are available.
233
287
  */
234
- init() {
288
+ async init() {
289
+ if (__classPrivateFieldGet(this, _NetworkEnablementController_initialized, "f")) {
290
+ return;
291
+ }
292
+ // Get network configurations
293
+ const networkControllerState = this.messenger.call('NetworkController:getState');
294
+ const multichainState = this.messenger.call('MultichainNetworkController:getState');
295
+ // First, initialize the state synchronously
235
296
  this.update((s) => {
236
- // Get network configurations from NetworkController (EVM networks)
237
- const networkControllerState = this.messenger.call('NetworkController:getState');
238
- // Get network configurations from MultichainNetworkController (all networks)
239
- const multichainState = this.messenger.call('MultichainNetworkController:getState');
240
297
  // Initialize namespace buckets for EVM networks from NetworkController
241
298
  Object.keys(networkControllerState.networkConfigurationsByChainId).forEach((chainId) => {
242
299
  const { namespace, storageKey } = (0, utils_2.deriveKeys)(chainId);
243
300
  __classPrivateFieldGet(this, _NetworkEnablementController_instances, "m", _NetworkEnablementController_ensureNamespaceBucket).call(this, s, namespace);
301
+ __classPrivateFieldGet(this, _NetworkEnablementController_instances, "m", _NetworkEnablementController_ensureSlip44NamespaceBucket).call(this, s, namespace);
244
302
  // Only add network if it doesn't already exist in state (preserves user settings)
245
303
  if (s.enabledNetworkMap[namespace][storageKey] === undefined) {
246
304
  s.enabledNetworkMap[namespace][storageKey] = false;
@@ -250,12 +308,63 @@ class NetworkEnablementController extends base_controller_1.BaseController {
250
308
  Object.keys(multichainState.multichainNetworkConfigurationsByChainId).forEach((chainId) => {
251
309
  const { namespace, storageKey } = (0, utils_2.deriveKeys)(chainId);
252
310
  __classPrivateFieldGet(this, _NetworkEnablementController_instances, "m", _NetworkEnablementController_ensureNamespaceBucket).call(this, s, namespace);
311
+ __classPrivateFieldGet(this, _NetworkEnablementController_instances, "m", _NetworkEnablementController_ensureSlip44NamespaceBucket).call(this, s, namespace);
253
312
  // Only add network if it doesn't already exist in state (preserves user settings)
254
313
  if (s.enabledNetworkMap[namespace][storageKey] === undefined) {
255
314
  s.enabledNetworkMap[namespace][storageKey] = false;
256
315
  }
257
316
  });
258
317
  });
318
+ // Collect EIP-155 networks that need slip44 values
319
+ const networksToFetch = [];
320
+ Object.keys(networkControllerState.networkConfigurationsByChainId).forEach((chainId) => {
321
+ const { namespace, storageKey, reference } = (0, utils_2.deriveKeys)(chainId);
322
+ if (namespace === 'eip155') {
323
+ // Check if slip44 value already exists in state
324
+ const existingSlip44 = this.state.slip44[namespace]?.[storageKey];
325
+ if (!existingSlip44) {
326
+ const numericChainId = parseInt(reference, 16);
327
+ networksToFetch.push({ chainId, storageKey, numericChainId });
328
+ }
329
+ }
330
+ });
331
+ // Fetch slip44 values for networks that don't have them
332
+ if (networksToFetch.length > 0) {
333
+ const slip44Promises = networksToFetch.map(async ({ chainId, storageKey, numericChainId }) => {
334
+ try {
335
+ const slip44Value = await (0, ChainService_1.getSlip44ByChainId)(numericChainId);
336
+ return { chainId, storageKey, slip44Value };
337
+ }
338
+ catch (error) {
339
+ console.error(`Failed to fetch slip44 for chainId ${chainId}:`, error);
340
+ return { chainId, storageKey, slip44Value: null };
341
+ }
342
+ });
343
+ const results = await Promise.all(slip44Promises);
344
+ // Update state with fetched slip44 values
345
+ this.update((s) => {
346
+ results.forEach(({ storageKey, slip44Value }) => {
347
+ if (slip44Value !== null) {
348
+ // Ensure namespace exists (should already exist from above)
349
+ __classPrivateFieldGet(this, _NetworkEnablementController_instances, "m", _NetworkEnablementController_ensureSlip44NamespaceBucket).call(this, s, 'eip155');
350
+ // @ts-expect-error - TypeScript doesn't recognize the dynamic namespace access
351
+ s.slip44.eip155[storageKey] = slip44Value;
352
+ }
353
+ });
354
+ });
355
+ }
356
+ __classPrivateFieldSet(this, _NetworkEnablementController_initialized, true, "f");
357
+ }
358
+ /**
359
+ * Re-initializes the controller's state.
360
+ *
361
+ * This method forces a fresh initialization even if the controller has already been initialized.
362
+ * It will re-fetch slip44 values for all EIP-155 networks and re-sync the network state.
363
+ * Use this when you need to force a full re-initialization.
364
+ */
365
+ async reinit() {
366
+ __classPrivateFieldSet(this, _NetworkEnablementController_initialized, false, "f");
367
+ await this.init();
259
368
  }
260
369
  /**
261
370
  * Disables a network for the user.
@@ -293,10 +402,14 @@ class NetworkEnablementController extends base_controller_1.BaseController {
293
402
  }
294
403
  }
295
404
  exports.NetworkEnablementController = NetworkEnablementController;
296
- _NetworkEnablementController_instances = new WeakSet(), _NetworkEnablementController_ensureNamespaceBucket = function _NetworkEnablementController_ensureNamespaceBucket(state, ns) {
405
+ _NetworkEnablementController_initialized = new WeakMap(), _NetworkEnablementController_instances = new WeakSet(), _NetworkEnablementController_ensureNamespaceBucket = function _NetworkEnablementController_ensureNamespaceBucket(state, ns) {
297
406
  if (!state.enabledNetworkMap[ns]) {
298
407
  state.enabledNetworkMap[ns] = {};
299
408
  }
409
+ }, _NetworkEnablementController_ensureSlip44NamespaceBucket = function _NetworkEnablementController_ensureSlip44NamespaceBucket(state, ns) {
410
+ if (!state.slip44[ns]) {
411
+ state.slip44[ns] = {};
412
+ }
300
413
  }, _NetworkEnablementController_isInPopularNetworksMode = function _NetworkEnablementController_isInPopularNetworksMode() {
301
414
  // Get current network configurations to check which popular networks exist
302
415
  const networkControllerState = this.messenger.call('NetworkController:getState');
@@ -325,11 +438,41 @@ _NetworkEnablementController_instances = new WeakSet(), _NetworkEnablementContro
325
438
  delete s.enabledNetworkMap[namespace][storageKey];
326
439
  }
327
440
  });
328
- }, _NetworkEnablementController_onAddNetwork = function _NetworkEnablementController_onAddNetwork(chainId) {
441
+ }, _NetworkEnablementController_onAddNetwork =
442
+ /**
443
+ * Handles the addition of a new network to the controller.
444
+ *
445
+ * @param chainId - The chain ID to add (Hex or CAIP-2 format)
446
+ *
447
+ * @description
448
+ * - If in popular networks mode (>2 popular networks enabled) AND adding a popular network:
449
+ * - Keep current selection (add but don't enable the new network)
450
+ * - Otherwise:
451
+ * - Switch to the newly added network (disable all others, enable this one)
452
+ * - Fetches and stores slip44 value for EIP-155 networks
453
+ */
454
+ async function _NetworkEnablementController_onAddNetwork(chainId) {
329
455
  const { namespace, storageKey, reference } = (0, utils_2.deriveKeys)(chainId);
456
+ // Fetch slip44 for EIP-155 networks
457
+ let slip44Value = null;
458
+ if (namespace === 'eip155') {
459
+ try {
460
+ // Convert hex chainId to decimal for the API call
461
+ const numericChainId = parseInt(reference);
462
+ slip44Value = await (0, ChainService_1.getSlip44ByChainId)(numericChainId);
463
+ }
464
+ catch (error) {
465
+ console.error(`Failed to fetch slip44 for chainId ${chainId}:`, error);
466
+ }
467
+ }
330
468
  this.update((s) => {
331
- // Ensure the namespace bucket exists
469
+ // Ensure the namespace buckets exist
332
470
  __classPrivateFieldGet(this, _NetworkEnablementController_instances, "m", _NetworkEnablementController_ensureNamespaceBucket).call(this, s, namespace);
471
+ __classPrivateFieldGet(this, _NetworkEnablementController_instances, "m", _NetworkEnablementController_ensureSlip44NamespaceBucket).call(this, s, namespace);
472
+ // Add slip44 value if fetched successfully
473
+ if (slip44Value !== null) {
474
+ s.slip44[namespace][storageKey] = slip44Value;
475
+ }
333
476
  // Check if popular networks mode is active (>2 popular networks enabled)
334
477
  const inPopularNetworksMode = __classPrivateFieldGet(this, _NetworkEnablementController_instances, "m", _NetworkEnablementController_isInPopularNetworksMode).call(this);
335
478
  // Check if the network being added is a popular network