@openzeppelin/ui-react 3.1.0 → 3.3.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.
package/dist/index.cjs CHANGED
@@ -33,7 +33,7 @@ let _tanstack_react_query = require("@tanstack/react-query");
33
33
  let _openzeppelin_ui_components = require("@openzeppelin/ui-components");
34
34
 
35
35
  //#region src/version.ts
36
- const VERSION = "3.1.0";
36
+ const VERSION = "3.3.0";
37
37
 
38
38
  //#endregion
39
39
  //#region src/hooks/AdapterContext.tsx
@@ -86,11 +86,31 @@ function RuntimeProvider({ children, resolveRuntime }) {
86
86
  const [loadingNetworks, setLoadingNetworks] = (0, react.useState)(/* @__PURE__ */ new Set());
87
87
  const runtimeRegistryRef = (0, react.useRef)(runtimeRegistry);
88
88
  const failedNetworksRef = (0, react.useRef)(/* @__PURE__ */ new Set());
89
+ const resolverGenerationRef = (0, react.useRef)(0);
90
+ const resolverInitializedRef = (0, react.useRef)(false);
89
91
  (0, react.useEffect)(() => {
90
92
  runtimeRegistryRef.current = runtimeRegistry;
91
93
  }, [runtimeRegistry]);
92
94
  (0, react.useEffect)(() => {
95
+ if (resolverInitializedRef.current) resolverGenerationRef.current += 1;
96
+ else resolverInitializedRef.current = true;
97
+ const registry = runtimeRegistryRef.current;
98
+ const runtimes = Object.values(registry);
99
+ const hadCachedRuntimes = runtimes.length > 0;
93
100
  failedNetworksRef.current = /* @__PURE__ */ new Set();
101
+ setLoadingNetworks(/* @__PURE__ */ new Set());
102
+ if (hadCachedRuntimes) {
103
+ setRuntimeRegistry({});
104
+ setTimeout(() => {
105
+ runtimes.forEach((runtime) => {
106
+ try {
107
+ runtime.dispose();
108
+ } catch (error) {
109
+ _openzeppelin_ui_utils.logger.error("RuntimeProvider", "Error disposing runtime during registry flush:", error);
110
+ }
111
+ });
112
+ }, 0);
113
+ }
94
114
  }, [resolveRuntime]);
95
115
  (0, react.useEffect)(() => {
96
116
  return () => {
@@ -178,7 +198,23 @@ function RuntimeProvider({ children, resolveRuntime }) {
178
198
  return newSet;
179
199
  });
180
200
  _openzeppelin_ui_utils.logger.info("RuntimeProvider", `Starting runtime initialization for network ${networkId} (${networkConfig.name})`);
201
+ const generation = resolverGenerationRef.current;
181
202
  resolveRuntime(networkConfig).then((runtime) => {
203
+ if (generation !== resolverGenerationRef.current) {
204
+ setTimeout(() => {
205
+ try {
206
+ runtime.dispose();
207
+ } catch (error) {
208
+ _openzeppelin_ui_utils.logger.error("RuntimeProvider", `Error disposing stale runtime for network ${networkId}:`, error);
209
+ }
210
+ }, 0);
211
+ setLoadingNetworks((prev) => {
212
+ const newSet = new Set(prev);
213
+ newSet.delete(networkId);
214
+ return newSet;
215
+ });
216
+ return;
217
+ }
182
218
  _openzeppelin_ui_utils.logger.info("RuntimeProvider", `Runtime for network ${networkId} loaded successfully`, {
183
219
  type: runtime.constructor.name,
184
220
  objectId: Object.prototype.toString.call(runtime)
@@ -193,6 +229,14 @@ function RuntimeProvider({ children, resolveRuntime }) {
193
229
  return newSet;
194
230
  });
195
231
  }).catch((error) => {
232
+ if (generation !== resolverGenerationRef.current) {
233
+ setLoadingNetworks((prev) => {
234
+ const newSet = new Set(prev);
235
+ newSet.delete(networkId);
236
+ return newSet;
237
+ });
238
+ return;
239
+ }
196
240
  _openzeppelin_ui_utils.logger.error("RuntimeProvider", `Error loading runtime for network ${networkId}:`, error);
197
241
  failedNetworksRef.current.add(networkId);
198
242
  setLoadingNetworks((prev) => {
@@ -921,22 +965,42 @@ const DEFAULT_CONFIG = {
921
965
  };
922
966
  /** Stable prefix for every resolution cache key — namespaces this package's keys. */
923
967
  const RESOLUTION_KEY_PREFIX = "oz-name-resolution";
968
+ const runtimeInstanceIds = /* @__PURE__ */ new WeakMap();
969
+ let nextRuntimeInstanceId = 1;
970
+ /**
971
+ * Assign a stable, serializable id to each {@link EcosystemRuntime} object identity.
972
+ * A disposed-and-recreated runtime (e.g. SF-4 opt-in toggle after INV-218 flush) gets
973
+ * a new id so resolution cache keys miss and results refresh (INV-230).
974
+ */
975
+ function getRuntimeInstanceId(runtime) {
976
+ if (!runtime) return 0;
977
+ let id = runtimeInstanceIds.get(runtime);
978
+ if (id === void 0) {
979
+ id = nextRuntimeInstanceId;
980
+ nextRuntimeInstanceId += 1;
981
+ runtimeInstanceIds.set(runtime, id);
982
+ }
983
+ return id;
984
+ }
924
985
  /**
925
986
  * Build a network-scoped, namespace-separated cache key (INV-40). A name resolves
926
987
  * differently per network and provenance can be chain-scoped, so `networkId` is
927
- * part of the key; `namespace` keeps forward and reverse lookups disjoint.
988
+ * part of the key; `namespace` keeps forward and reverse lookups disjoint;
989
+ * `runtimeInstanceId` scopes entries to the active capability instance (INV-230).
928
990
  *
929
- * @param namespace - `'name'` (forward) or `'addr'` (reverse).
930
- * @param networkId - Active network id (`''` when no network is selected).
931
- * @param normalizedInput - Trimmed + lowercased input (INV-26).
991
+ * @param namespace - `'name'` (forward) or `'addr'` (reverse).
992
+ * @param networkId - Active network id (`''` when no network is selected).
993
+ * @param normalizedInput - Trimmed + lowercased input (INV-26).
994
+ * @param runtimeInstanceId - Active runtime instance discriminator (`0` when absent).
932
995
  * @returns A readonly tuple suitable for a TanStack Query `queryKey`.
933
996
  */
934
- function buildResolutionKey(namespace, networkId, normalizedInput) {
997
+ function buildResolutionKey(namespace, networkId, normalizedInput, runtimeInstanceId = 0) {
935
998
  return [
936
999
  RESOLUTION_KEY_PREFIX,
937
1000
  namespace,
938
1001
  networkId,
939
- normalizedInput
1002
+ normalizedInput,
1003
+ runtimeInstanceId
940
1004
  ];
941
1005
  }
942
1006
  /**
@@ -1109,11 +1173,11 @@ const NOOP_RETRY = () => {};
1109
1173
  * {@link EngineResult}. Not exported from the package.
1110
1174
  */
1111
1175
  function useResolutionEngine(params) {
1112
- const { input, namespace, debounceMs, enabled, shouldAttempt, getMethod } = params;
1176
+ const { input, namespace, debounceMs, enabled, shouldAttempt, getMethod, runtimeSource } = params;
1113
1177
  const walletState = (0, react.useContext)(WalletStateContext);
1114
- const activeRuntime = walletState?.activeRuntime ?? null;
1115
- const activeNetworkId = walletState?.activeNetworkId ?? null;
1116
- const isRuntimeLoading = walletState?.isRuntimeLoading ?? false;
1178
+ const activeRuntime = runtimeSource ? runtimeSource.runtime : walletState?.activeRuntime ?? null;
1179
+ const activeNetworkId = runtimeSource ? runtimeSource.networkId : walletState?.activeNetworkId ?? null;
1180
+ const isRuntimeLoading = runtimeSource ? runtimeSource.isRuntimeLoading : walletState?.isRuntimeLoading ?? false;
1117
1181
  const { queryClient, config } = useNameResolutionContext();
1118
1182
  const trimmedLive = (input ?? "").trim();
1119
1183
  const normalizedLive = trimmedLive.toLowerCase();
@@ -1135,12 +1199,13 @@ function useResolutionEngine(params) {
1135
1199
  const normalizedKey = effectiveTrimmed.toLowerCase();
1136
1200
  const capability = activeRuntime?.nameResolution;
1137
1201
  const networkId = activeNetworkId ?? "";
1202
+ const runtimeInstanceId = getRuntimeInstanceId(activeRuntime);
1138
1203
  const method = capability ? getMethod(capability) : void 0;
1139
1204
  const liveAttemptable = capability ? shouldAttempt(capability, normalizedLive) : false;
1140
1205
  const keyAttemptable = capability ? shouldAttempt(capability, normalizedKey) : false;
1141
1206
  const queryEnabled = enabled && trimmedLive !== "" && capability != null && method != null && normalizedKey !== "" && keyAttemptable;
1142
1207
  const query = (0, _tanstack_react_query.useQuery)({
1143
- queryKey: buildResolutionKey(namespace, networkId, normalizedKey),
1208
+ queryKey: buildResolutionKey(namespace, networkId, normalizedKey, runtimeInstanceId),
1144
1209
  queryFn: async () => {
1145
1210
  if (!method) throw new ResolutionQueryError({
1146
1211
  code: "ADAPTER_ERROR",
@@ -1267,40 +1332,13 @@ function toNameResult(engine) {
1267
1332
  }
1268
1333
 
1269
1334
  //#endregion
1270
- //#region src/hooks/nameResolution/useResolveAddress.ts
1271
- /**
1272
- * Reverse-resolve an address to a name. Soft-reads the active runtime from
1273
- * `WalletStateContext` (never throws when no provider is mounted — degrades to
1274
- * idle / UNSUPPORTED_NETWORK) and calls `runtime.nameResolution?.resolveAddress`.
1275
- * Caches per (network, address) via the owned QueryClient. Not debounced by
1276
- * default — addresses are pasted, not typed char-by-char.
1277
- *
1278
- * Applies no client-side address-shape check (INV-32): resolution is attempted on
1279
- * any non-empty input and malformed addresses are rejected via the adapter's typed
1280
- * error union. Address-shape validation is SF-3's concern.
1281
- *
1282
- * @param address - The address to reverse-resolve. `null` / empty yields
1283
- * `status: 'idle'`.
1284
- * @param options - `debounceMs` / `enabled` overrides.
1285
- * @returns The current {@link UseResolveAddressResult}.
1286
- */
1287
- function useResolveAddress(address, options) {
1288
- const { config } = useNameResolutionContext();
1289
- return toAddressResult(useResolutionEngine({
1290
- input: address,
1291
- namespace: "addr",
1292
- debounceMs: options?.debounceMs ?? config.reverseDebounceMs,
1293
- enabled: options?.enabled ?? true,
1294
- shouldAttempt: () => true,
1295
- getMethod: (cap) => cap.resolveAddress
1296
- }));
1297
- }
1335
+ //#region src/hooks/nameResolution/mapAddressEngineResult.ts
1298
1336
  /**
1299
1337
  * Remap the engine's generic `input` to `address` (INV-24). A `debouncing` arm —
1300
1338
  * only reachable when a caller passes a non-zero reverse `debounceMs` — collapses
1301
1339
  * to `loading` so {@link UseResolveAddressResult} keeps no `debouncing` variant.
1302
1340
  */
1303
- function toAddressResult(engine) {
1341
+ function mapAddressEngineResult(engine) {
1304
1342
  switch (engine.status) {
1305
1343
  case "idle": return { status: "idle" };
1306
1344
  case "debouncing":
@@ -1322,6 +1360,71 @@ function toAddressResult(engine) {
1322
1360
  }
1323
1361
  }
1324
1362
 
1363
+ //#endregion
1364
+ //#region src/hooks/nameResolution/useNetworkRuntimeSource.ts
1365
+ /**
1366
+ * Soft-reads {@link RuntimeContext} (never throws) and requests the registry
1367
+ * runtime for `network`. Call during render so loading state updates propagate.
1368
+ *
1369
+ * Does not mutate {@link WalletStateProvider}'s active network — wallet-global
1370
+ * runtime is never consulted when a scoped network is supplied (INV-154).
1371
+ */
1372
+ function useNetworkRuntimeSource(network) {
1373
+ const runtimeContext = (0, react.useContext)(RuntimeContext);
1374
+ if (!network) return {
1375
+ runtime: null,
1376
+ networkId: "",
1377
+ isRuntimeLoading: false
1378
+ };
1379
+ if (!runtimeContext) return {
1380
+ runtime: null,
1381
+ networkId: network.id,
1382
+ isRuntimeLoading: false
1383
+ };
1384
+ const { runtime, isLoading } = runtimeContext.getRuntimeForNetwork(network);
1385
+ return {
1386
+ runtime,
1387
+ networkId: network.id,
1388
+ isRuntimeLoading: isLoading
1389
+ };
1390
+ }
1391
+
1392
+ //#endregion
1393
+ //#region src/hooks/nameResolution/useResolveAddress.ts
1394
+ /**
1395
+ * Reverse-resolve an address to a name. Soft-reads the active runtime from
1396
+ * `WalletStateContext` (never throws when no provider is mounted — degrades to
1397
+ * idle / UNSUPPORTED_NETWORK) and calls `runtime.nameResolution?.resolveAddress`.
1398
+ * Pass `options.network` to scope resolution to a specific network without
1399
+ * changing the wallet's active network. Caches per (network, address) via the
1400
+ * owned QueryClient. Not debounced by default — addresses are pasted, not
1401
+ * typed char-by-char.
1402
+ *
1403
+ * Applies no client-side address-shape check (INV-32): resolution is attempted on
1404
+ * any non-empty input and malformed addresses are rejected via the adapter's typed
1405
+ * error union. Address-shape validation is SF-3's concern.
1406
+ *
1407
+ * @param address - The address to reverse-resolve. `null` / empty yields
1408
+ * `status: 'idle'`.
1409
+ * @param options - `debounceMs` / `enabled` / `network` overrides.
1410
+ * @returns The current {@link UseResolveAddressResult}.
1411
+ */
1412
+ function useResolveAddress(address, options) {
1413
+ const { config } = useNameResolutionContext();
1414
+ const debounceMs = options?.debounceMs ?? config.reverseDebounceMs;
1415
+ const enabled = options?.enabled ?? true;
1416
+ const networkSource = useNetworkRuntimeSource(options?.network ?? null);
1417
+ return mapAddressEngineResult(useResolutionEngine({
1418
+ input: address,
1419
+ namespace: "addr",
1420
+ debounceMs,
1421
+ enabled,
1422
+ shouldAttempt: () => true,
1423
+ getMethod: (cap) => cap.resolveAddress,
1424
+ runtimeSource: options?.network != null ? networkSource : void 0
1425
+ }));
1426
+ }
1427
+
1325
1428
  //#endregion
1326
1429
  //#region src/hooks/nameResolution/NameResolutionProvider.tsx
1327
1430
  /**
@@ -1347,6 +1450,54 @@ function NameResolutionProvider({ children, config, queryClient }) {
1347
1450
  });
1348
1451
  }
1349
1452
 
1453
+ //#endregion
1454
+ //#region src/hooks/nameResolution/createNameResolver.ts
1455
+ const EMPTY_RESOLVER$1 = {};
1456
+ /**
1457
+ * Projects a {@link NameResolutionCapability} into the SF-3 {@link NameResolver}
1458
+ * seam with SF-2 cache-key parity (INV-119).
1459
+ */
1460
+ function createNameResolverFromCapability(capability, networkId, runtimeInstanceId, queryClient, config) {
1461
+ if (!capability) return EMPTY_RESOLVER$1;
1462
+ const resolveNameMethod = capability.resolveName;
1463
+ return {
1464
+ isValidName: (name) => capability.isValidName(name),
1465
+ resolveName: resolveNameMethod ? async (name) => {
1466
+ const normalized = name.trim().toLowerCase();
1467
+ try {
1468
+ return {
1469
+ ok: true,
1470
+ value: await queryClient.fetchQuery({
1471
+ queryKey: buildResolutionKey("name", networkId, normalized, runtimeInstanceId),
1472
+ queryFn: async () => {
1473
+ let result;
1474
+ try {
1475
+ result = await resolveNameMethod(normalized);
1476
+ } catch (cause) {
1477
+ throw new ResolutionQueryError({
1478
+ code: "ADAPTER_ERROR",
1479
+ message: cause instanceof Error ? cause.message : String(cause),
1480
+ cause
1481
+ });
1482
+ }
1483
+ if (!result.ok) throw new ResolutionQueryError(result.error);
1484
+ return result.value;
1485
+ },
1486
+ staleTime: config.staleTimeMs,
1487
+ gcTime: config.gcTimeMs,
1488
+ retry: (failureCount, error) => (error instanceof ResolutionQueryError ? isTransientError(error.resolutionError) : true) && failureCount < config.transientRetryCount
1489
+ })
1490
+ };
1491
+ } catch (error) {
1492
+ return {
1493
+ ok: false,
1494
+ error: toNameResolutionError(error)
1495
+ };
1496
+ }
1497
+ } : void 0
1498
+ };
1499
+ }
1500
+
1350
1501
  //#endregion
1351
1502
  //#region src/hooks/nameResolution/useRuntimeNameResolver.ts
1352
1503
  /**
@@ -1356,7 +1507,7 @@ function NameResolutionProvider({ children, config, queryClient }) {
1356
1507
  */
1357
1508
  const EMPTY_RESOLVER = {};
1358
1509
  /**
1359
- * Project the active runtime's `NameResolutionCapability` into the injected
1510
+ * Project a runtime's `NameResolutionCapability` into the injected
1360
1511
  * {@link NameResolver} seam consumed by `@openzeppelin/ui-components` (SF-3, D3).
1361
1512
  * Mounted ambiently by the renderer:
1362
1513
  *
@@ -1364,6 +1515,10 @@ const EMPTY_RESOLVER = {};
1364
1515
  * <NameResolverProvider {...useRuntimeNameResolver()}>{form}</NameResolverProvider>
1365
1516
  * ```
1366
1517
  *
1518
+ * Pass `scopedNetwork` to resolve against a specific network via
1519
+ * {@link RuntimeProvider}'s registry without changing {@link WalletStateProvider}'s
1520
+ * active network (e.g. address-book Add Alias dropdown).
1521
+ *
1367
1522
  * Each imperative call is backed by SF-2's **owned** resolution `QueryClient`
1368
1523
  * via `fetchQuery`, reusing SF-2's exact query-key convention
1369
1524
  * (`buildResolutionKey('name', networkId, normalizedName)`) — so a name
@@ -1372,9 +1527,10 @@ const EMPTY_RESOLVER = {};
1372
1527
  * out-of-order safety, never a parallel cache (INV-119).
1373
1528
  *
1374
1529
  * Degradation, in order (all method-omission, never a throw):
1375
- * - No `WalletStateProvider` mounted empty resolver. The context is read
1376
- * directly (not via `useWalletState()`, which throws) so the ambient
1377
- * renderer mount stays safe for wallet-less usage.
1530
+ * - No `WalletStateProvider` mounted (wallet path) or no `RuntimeProvider`
1531
+ * (network path) empty resolver. The context is read directly (not via
1532
+ * `useWalletState()`, which throws) so the ambient renderer mount stays safe
1533
+ * for wallet-less usage.
1378
1534
  * - No active runtime, or runtime without the capability → empty resolver.
1379
1535
  * - Capability without `resolveName` → `isValidName` only; forward unsupported.
1380
1536
  *
@@ -1384,55 +1540,111 @@ const EMPTY_RESOLVER = {};
1384
1540
  *
1385
1541
  * @returns A referentially stable {@link NameResolver} for the active runtime.
1386
1542
  */
1387
- function useRuntimeNameResolver() {
1543
+ function useRuntimeNameResolver(scopedNetwork) {
1388
1544
  const walletState = (0, react.useContext)(WalletStateContext);
1389
1545
  const { queryClient, config } = useNameResolutionContext();
1390
- const capability = walletState?.activeRuntime?.nameResolution;
1391
- const networkId = walletState?.activeNetworkId ?? "";
1546
+ const networkSource = useNetworkRuntimeSource(scopedNetwork ?? null);
1547
+ const useNetworkScoped = scopedNetwork != null;
1548
+ const activeRuntime = useNetworkScoped ? networkSource.runtime : walletState?.activeRuntime ?? null;
1549
+ const capability = activeRuntime?.nameResolution;
1550
+ const networkId = useNetworkScoped ? networkSource.networkId : walletState?.activeNetworkId ?? "";
1551
+ const runtimeInstanceId = getRuntimeInstanceId(activeRuntime);
1392
1552
  return (0, react.useMemo)(() => {
1393
1553
  if (!capability) return EMPTY_RESOLVER;
1394
- const resolveNameMethod = capability.resolveName;
1395
- return {
1396
- isValidName: (name) => capability.isValidName(name),
1397
- resolveName: resolveNameMethod ? async (name) => {
1398
- const normalized = name.trim().toLowerCase();
1399
- try {
1400
- return {
1401
- ok: true,
1402
- value: await queryClient.fetchQuery({
1403
- queryKey: buildResolutionKey("name", networkId, normalized),
1404
- queryFn: async () => {
1405
- let result;
1406
- try {
1407
- result = await resolveNameMethod(normalized);
1408
- } catch (cause) {
1409
- throw new ResolutionQueryError({
1410
- code: "ADAPTER_ERROR",
1411
- message: cause instanceof Error ? cause.message : String(cause),
1412
- cause
1413
- });
1414
- }
1415
- if (!result.ok) throw new ResolutionQueryError(result.error);
1416
- return result.value;
1417
- },
1418
- staleTime: config.staleTimeMs,
1419
- gcTime: config.gcTimeMs,
1420
- retry: (failureCount, error) => (error instanceof ResolutionQueryError ? isTransientError(error.resolutionError) : true) && failureCount < config.transientRetryCount
1421
- })
1422
- };
1423
- } catch (error) {
1424
- return {
1425
- ok: false,
1426
- error: toNameResolutionError(error)
1427
- };
1428
- }
1429
- } : void 0
1430
- };
1554
+ return createNameResolverFromCapability(capability, networkId, runtimeInstanceId, queryClient, config);
1431
1555
  }, [
1432
1556
  capability,
1557
+ networkId,
1558
+ runtimeInstanceId,
1433
1559
  queryClient,
1434
1560
  config,
1435
- networkId
1561
+ useNetworkScoped ? networkSource.isRuntimeLoading : walletState?.isRuntimeLoading ?? false
1562
+ ]);
1563
+ }
1564
+
1565
+ //#endregion
1566
+ //#region src/hooks/nameResolution/useNetworkNameResolver.ts
1567
+ /**
1568
+ * @deprecated Pass the network to {@link useRuntimeNameResolver} instead.
1569
+ */
1570
+ function useNetworkNameResolver(network) {
1571
+ return useRuntimeNameResolver(network ?? void 0);
1572
+ }
1573
+
1574
+ //#endregion
1575
+ //#region src/hooks/nameResolution/useNetworkResolveAddress.ts
1576
+ /**
1577
+ * @deprecated Pass `network` in {@link UseResolveAddressOptions} to {@link useResolveAddress}.
1578
+ */
1579
+ function useNetworkResolveAddress(address, network, options) {
1580
+ return useResolveAddress(address, network != null ? {
1581
+ ...options,
1582
+ network
1583
+ } : options);
1584
+ }
1585
+
1586
+ //#endregion
1587
+ //#region src/hooks/runtime/runtimeCreationConfig.ts
1588
+ /** Safe default: miss-fallback OFF; no uiKit override. INV-204 */
1589
+ const DEFAULT_RUNTIME_CREATION_CONFIG = {};
1590
+ /**
1591
+ * Normalizes the opt-in flag to the adapter's strict-enable contract.
1592
+ * UIKit uses this at the threading boundary so `false` and `undefined` never
1593
+ * emit `enableMainnetL1MissFallback: false` into adapter options (absent = OFF).
1594
+ */
1595
+ function isMainnetL1MissFallbackEnabled(options) {
1596
+ return options?.enableMainnetL1MissFallback === true;
1597
+ }
1598
+ /**
1599
+ * Builds the `createRuntime` options bag for adapter threading.
1600
+ * When OFF, `nameResolution` is omitted (INV-207). When ON, spreads only
1601
+ * `{ enableMainnetL1MissFallback: true }` (INV-208).
1602
+ */
1603
+ function buildCreateRuntimeOptions(options) {
1604
+ return {
1605
+ ...options?.uiKit !== void 0 ? { uiKit: options.uiKit } : {},
1606
+ ...isMainnetL1MissFallbackEnabled(options?.nameResolution) ? { nameResolution: { enableMainnetL1MissFallback: true } } : {}
1607
+ };
1608
+ }
1609
+
1610
+ //#endregion
1611
+ //#region src/hooks/runtime/createResolveRuntime.ts
1612
+ /**
1613
+ * Build a `resolveRuntime` callback for {@link RuntimeProvider} that forwards
1614
+ * `CreateRuntimeOptions` to `ecosystemDefinition.createRuntime`.
1615
+ *
1616
+ * Does not cache — {@link RuntimeProvider} owns the per-network singleton registry.
1617
+ */
1618
+ function createResolveRuntime(ecosystemDefinition, params) {
1619
+ const { profile, options } = params;
1620
+ const mergedOptions = buildCreateRuntimeOptions(options);
1621
+ return (networkConfig) => Promise.resolve(ecosystemDefinition.createRuntime(profile, networkConfig, mergedOptions));
1622
+ }
1623
+
1624
+ //#endregion
1625
+ //#region src/hooks/runtime/useResolveRuntime.ts
1626
+ /**
1627
+ * Memoized {@link createResolveRuntime} for React apps. Recompute when
1628
+ * `ecosystemDefinition`, `profile`, or opt-in posture change (INV-217, INV-222).
1629
+ *
1630
+ * Changing the returned function identity MUST trigger runtime registry flush
1631
+ * in {@link RuntimeProvider} (INV-218).
1632
+ */
1633
+ function useResolveRuntime(ecosystemDefinition, params) {
1634
+ const { profile, options } = params;
1635
+ const uiKit = options?.uiKit;
1636
+ const enableMainnetL1MissFallback = options?.nameResolution?.enableMainnetL1MissFallback;
1637
+ return (0, react.useMemo)(() => createResolveRuntime(ecosystemDefinition, {
1638
+ profile,
1639
+ options: {
1640
+ ...uiKit !== void 0 ? { uiKit } : {},
1641
+ ...enableMainnetL1MissFallback === true ? { nameResolution: { enableMainnetL1MissFallback: true } } : {}
1642
+ }
1643
+ }), [
1644
+ ecosystemDefinition,
1645
+ profile,
1646
+ uiKit,
1647
+ enableMainnetL1MissFallback
1436
1648
  ]);
1437
1649
  }
1438
1650
 
@@ -1646,6 +1858,7 @@ const NetworkSwitchManager = ({ wallet, networkCatalog, targetNetworkId, onNetwo
1646
1858
  exports.AnalyticsContext = AnalyticsContext;
1647
1859
  exports.AnalyticsProvider = AnalyticsProvider;
1648
1860
  exports.DEFAULT_CONFIG = DEFAULT_CONFIG;
1861
+ exports.DEFAULT_RUNTIME_CREATION_CONFIG = DEFAULT_RUNTIME_CREATION_CONFIG;
1649
1862
  exports.NameResolutionContext = NameResolutionContext;
1650
1863
  exports.NameResolutionProvider = NameResolutionProvider;
1651
1864
  exports.NetworkSwitchManager = NetworkSwitchManager;
@@ -1656,6 +1869,8 @@ exports.WalletConnectionHeader = WalletConnectionHeader;
1656
1869
  exports.WalletConnectionUI = WalletConnectionUI;
1657
1870
  exports.WalletStateContext = WalletStateContext;
1658
1871
  exports.WalletStateProvider = WalletStateProvider;
1872
+ exports.createResolveRuntime = createResolveRuntime;
1873
+ exports.isMainnetL1MissFallbackEnabled = isMainnetL1MissFallbackEnabled;
1659
1874
  exports.useAnalytics = useAnalytics;
1660
1875
  exports.useDerivedAccountStatus = useDerivedAccountStatus;
1661
1876
  exports.useDerivedChainInfo = useDerivedChainInfo;
@@ -1663,8 +1878,11 @@ exports.useDerivedConnectStatus = useDerivedConnectStatus;
1663
1878
  exports.useDerivedDisconnect = useDerivedDisconnect;
1664
1879
  exports.useDerivedSwitchChainStatus = useDerivedSwitchChainStatus;
1665
1880
  exports.useNameResolutionContext = useNameResolutionContext;
1881
+ exports.useNetworkNameResolver = useNetworkNameResolver;
1882
+ exports.useNetworkResolveAddress = useNetworkResolveAddress;
1666
1883
  exports.useResolveAddress = useResolveAddress;
1667
1884
  exports.useResolveName = useResolveName;
1885
+ exports.useResolveRuntime = useResolveRuntime;
1668
1886
  exports.useRuntimeContext = useRuntimeContext;
1669
1887
  exports.useRuntimeNameResolver = useRuntimeNameResolver;
1670
1888
  exports.useWalletComponents = useWalletComponents;