@openzeppelin/ui-react 3.1.0 → 3.2.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.2.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
  /**
@@ -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",
@@ -1387,8 +1452,10 @@ const EMPTY_RESOLVER = {};
1387
1452
  function useRuntimeNameResolver() {
1388
1453
  const walletState = (0, react.useContext)(WalletStateContext);
1389
1454
  const { queryClient, config } = useNameResolutionContext();
1390
- const capability = walletState?.activeRuntime?.nameResolution;
1455
+ const activeRuntime = walletState?.activeRuntime ?? null;
1456
+ const capability = activeRuntime?.nameResolution;
1391
1457
  const networkId = walletState?.activeNetworkId ?? "";
1458
+ const runtimeInstanceId = getRuntimeInstanceId(activeRuntime);
1392
1459
  return (0, react.useMemo)(() => {
1393
1460
  if (!capability) return EMPTY_RESOLVER;
1394
1461
  const resolveNameMethod = capability.resolveName;
@@ -1400,7 +1467,7 @@ function useRuntimeNameResolver() {
1400
1467
  return {
1401
1468
  ok: true,
1402
1469
  value: await queryClient.fetchQuery({
1403
- queryKey: buildResolutionKey("name", networkId, normalized),
1470
+ queryKey: buildResolutionKey("name", networkId, normalized, runtimeInstanceId),
1404
1471
  queryFn: async () => {
1405
1472
  let result;
1406
1473
  try {
@@ -1432,7 +1499,73 @@ function useRuntimeNameResolver() {
1432
1499
  capability,
1433
1500
  queryClient,
1434
1501
  config,
1435
- networkId
1502
+ networkId,
1503
+ runtimeInstanceId
1504
+ ]);
1505
+ }
1506
+
1507
+ //#endregion
1508
+ //#region src/hooks/runtime/runtimeCreationConfig.ts
1509
+ /** Safe default: miss-fallback OFF; no uiKit override. INV-204 */
1510
+ const DEFAULT_RUNTIME_CREATION_CONFIG = {};
1511
+ /**
1512
+ * Normalizes the opt-in flag to the adapter's strict-enable contract.
1513
+ * UIKit uses this at the threading boundary so `false` and `undefined` never
1514
+ * emit `enableMainnetL1MissFallback: false` into adapter options (absent = OFF).
1515
+ */
1516
+ function isMainnetL1MissFallbackEnabled(options) {
1517
+ return options?.enableMainnetL1MissFallback === true;
1518
+ }
1519
+ /**
1520
+ * Builds the `createRuntime` options bag for adapter threading.
1521
+ * When OFF, `nameResolution` is omitted (INV-207). When ON, spreads only
1522
+ * `{ enableMainnetL1MissFallback: true }` (INV-208).
1523
+ */
1524
+ function buildCreateRuntimeOptions(options) {
1525
+ return {
1526
+ ...options?.uiKit !== void 0 ? { uiKit: options.uiKit } : {},
1527
+ ...isMainnetL1MissFallbackEnabled(options?.nameResolution) ? { nameResolution: { enableMainnetL1MissFallback: true } } : {}
1528
+ };
1529
+ }
1530
+
1531
+ //#endregion
1532
+ //#region src/hooks/runtime/createResolveRuntime.ts
1533
+ /**
1534
+ * Build a `resolveRuntime` callback for {@link RuntimeProvider} that forwards
1535
+ * `CreateRuntimeOptions` to `ecosystemDefinition.createRuntime`.
1536
+ *
1537
+ * Does not cache — {@link RuntimeProvider} owns the per-network singleton registry.
1538
+ */
1539
+ function createResolveRuntime(ecosystemDefinition, params) {
1540
+ const { profile, options } = params;
1541
+ const mergedOptions = buildCreateRuntimeOptions(options);
1542
+ return (networkConfig) => Promise.resolve(ecosystemDefinition.createRuntime(profile, networkConfig, mergedOptions));
1543
+ }
1544
+
1545
+ //#endregion
1546
+ //#region src/hooks/runtime/useResolveRuntime.ts
1547
+ /**
1548
+ * Memoized {@link createResolveRuntime} for React apps. Recompute when
1549
+ * `ecosystemDefinition`, `profile`, or opt-in posture change (INV-217, INV-222).
1550
+ *
1551
+ * Changing the returned function identity MUST trigger runtime registry flush
1552
+ * in {@link RuntimeProvider} (INV-218).
1553
+ */
1554
+ function useResolveRuntime(ecosystemDefinition, params) {
1555
+ const { profile, options } = params;
1556
+ const uiKit = options?.uiKit;
1557
+ const enableMainnetL1MissFallback = options?.nameResolution?.enableMainnetL1MissFallback;
1558
+ return (0, react.useMemo)(() => createResolveRuntime(ecosystemDefinition, {
1559
+ profile,
1560
+ options: {
1561
+ ...uiKit !== void 0 ? { uiKit } : {},
1562
+ ...enableMainnetL1MissFallback === true ? { nameResolution: { enableMainnetL1MissFallback: true } } : {}
1563
+ }
1564
+ }), [
1565
+ ecosystemDefinition,
1566
+ profile,
1567
+ uiKit,
1568
+ enableMainnetL1MissFallback
1436
1569
  ]);
1437
1570
  }
1438
1571
 
@@ -1646,6 +1779,7 @@ const NetworkSwitchManager = ({ wallet, networkCatalog, targetNetworkId, onNetwo
1646
1779
  exports.AnalyticsContext = AnalyticsContext;
1647
1780
  exports.AnalyticsProvider = AnalyticsProvider;
1648
1781
  exports.DEFAULT_CONFIG = DEFAULT_CONFIG;
1782
+ exports.DEFAULT_RUNTIME_CREATION_CONFIG = DEFAULT_RUNTIME_CREATION_CONFIG;
1649
1783
  exports.NameResolutionContext = NameResolutionContext;
1650
1784
  exports.NameResolutionProvider = NameResolutionProvider;
1651
1785
  exports.NetworkSwitchManager = NetworkSwitchManager;
@@ -1656,6 +1790,8 @@ exports.WalletConnectionHeader = WalletConnectionHeader;
1656
1790
  exports.WalletConnectionUI = WalletConnectionUI;
1657
1791
  exports.WalletStateContext = WalletStateContext;
1658
1792
  exports.WalletStateProvider = WalletStateProvider;
1793
+ exports.createResolveRuntime = createResolveRuntime;
1794
+ exports.isMainnetL1MissFallbackEnabled = isMainnetL1MissFallbackEnabled;
1659
1795
  exports.useAnalytics = useAnalytics;
1660
1796
  exports.useDerivedAccountStatus = useDerivedAccountStatus;
1661
1797
  exports.useDerivedChainInfo = useDerivedChainInfo;
@@ -1665,6 +1801,7 @@ exports.useDerivedSwitchChainStatus = useDerivedSwitchChainStatus;
1665
1801
  exports.useNameResolutionContext = useNameResolutionContext;
1666
1802
  exports.useResolveAddress = useResolveAddress;
1667
1803
  exports.useResolveName = useResolveName;
1804
+ exports.useResolveRuntime = useResolveRuntime;
1668
1805
  exports.useRuntimeContext = useRuntimeContext;
1669
1806
  exports.useRuntimeNameResolver = useRuntimeNameResolver;
1670
1807
  exports.useWalletComponents = useWalletComponents;