@openzeppelin/ui-react 3.2.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.
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["getOrCreateSharedContext"],"sources":["../src/version.ts","../src/hooks/AdapterContext.tsx","../src/hooks/AdapterProvider.tsx","../src/hooks/WalletStateContext.ts","../src/hooks/useAdapterContext.ts","../src/hooks/walletSessionRegistry.ts","../src/hooks/WalletStateProvider.tsx","../src/hooks/AnalyticsContext.tsx","../src/hooks/AnalyticsProvider.tsx","../src/hooks/useAnalytics.ts","../src/hooks/useWalletComponents.ts","../src/hooks/useDerivedAccountStatus.ts","../src/hooks/useDerivedSwitchChainStatus.ts","../src/hooks/useDerivedChainInfo.ts","../src/hooks/useDerivedConnectStatus.ts","../src/hooks/useDerivedDisconnect.ts","../src/hooks/useWalletReconnectionHandler.ts","../src/hooks/nameResolution/resolutionConfig.ts","../src/hooks/nameResolution/NameResolutionContext.ts","../src/hooks/nameResolution/resolutionState.ts","../src/hooks/nameResolution/useResolutionEngine.ts","../src/hooks/nameResolution/useResolveName.ts","../src/hooks/nameResolution/useResolveAddress.ts","../src/hooks/nameResolution/NameResolutionProvider.tsx","../src/hooks/nameResolution/useRuntimeNameResolver.ts","../src/hooks/runtime/runtimeCreationConfig.ts","../src/hooks/runtime/createResolveRuntime.ts","../src/hooks/runtime/useResolveRuntime.ts","../src/components/WalletConnectionUI.tsx","../src/components/WalletConnectionHeader.tsx","../src/components/NetworkSwitchManager.tsx"],"sourcesContent":["declare const __PACKAGE_VERSION__: string;\nexport const VERSION: string = __PACKAGE_VERSION__;\n","/**\n * AdapterContext.tsx\n *\n * This file defines the React Context used for the runtime singleton pattern.\n * It provides types and the context definition, but the actual implementation\n * is in the `RuntimeProvider` component.\n *\n * The runtime singleton pattern ensures that only one runtime instance exists\n * per network configuration, which is critical for consistent wallet connection\n * state across the application.\n */\nimport { createContext } from 'react';\n\nimport type { EcosystemRuntime, NetworkConfig } from '@openzeppelin/ui-types';\n\n/**\n * Registry type that maps network IDs to their corresponding runtime instances.\n */\nexport interface RuntimeRegistry {\n [networkId: string]: EcosystemRuntime;\n}\n\n/**\n * Context value interface defining what's provided through the context.\n * The main functionality is `getRuntimeForNetwork`, which either returns an existing\n * runtime or initiates loading of a new one.\n */\nexport interface RuntimeContextValue {\n getRuntimeForNetwork: (networkConfig: NetworkConfig | null) => {\n runtime: EcosystemRuntime | null;\n isLoading: boolean;\n };\n /**\n * Evicts a runtime from the registry and calls `dispose()` on it.\n * Used by WalletStateProvider to release superseded runtimes after a safe handoff.\n */\n releaseRuntime: (networkId: string) => void;\n}\n\n/**\n * The React Context that provides runtime registry access throughout the app.\n * Components can access this through the `useRuntimeContext` hook.\n */\nexport const RuntimeContext = createContext<RuntimeContextValue | null>(null);\n","/**\n * RuntimeProvider.tsx\n *\n * This file implements the Runtime Provider component which manages a registry of\n * runtime instances. It's a key part of the runtime singleton pattern which ensures\n * that only one runtime instance exists per network configuration.\n *\n * The runtime registry is shared across the application via React Context, allowing\n * components to access the same runtime instances and maintain consistent wallet\n * connection state.\n *\n * IMPORTANT: This implementation needs special care to avoid React state update errors\n * during component rendering. Direct state updates during render are not allowed, which\n * is why runtime loading is controlled carefully.\n */\nimport { ReactNode, useCallback, useEffect, useMemo, useRef, useState } from 'react';\n\nimport type { EcosystemRuntime, NetworkConfig } from '@openzeppelin/ui-types';\nimport { logger } from '@openzeppelin/ui-utils';\n\nimport { RuntimeContext, RuntimeContextValue, RuntimeRegistry } from './AdapterContext';\n\nexport interface RuntimeProviderProps {\n children: ReactNode;\n /** Function to resolve/create a runtime instance for a given NetworkConfig. */\n resolveRuntime: (networkConfig: NetworkConfig) => Promise<EcosystemRuntime>;\n}\n\n/**\n * Provider component that manages runtime instances centrally\n * to avoid creating multiple instances of the same runtime.\n *\n * This component:\n * 1. Maintains a registry of runtime instances by network ID\n * 2. Tracks loading states for runtimes being initialized\n * 3. Provides a function to get or load runtimes for specific networks\n * 4. Ensures runtime instances are reused when possible\n */\nexport function RuntimeProvider({ children, resolveRuntime }: RuntimeProviderProps) {\n // Registry to store runtime instances by network ID\n const [runtimeRegistry, setRuntimeRegistry] = useState<RuntimeRegistry>({});\n\n // Track loading states by network ID\n const [loadingNetworks, setLoadingNetworks] = useState<Set<string>>(new Set());\n const runtimeRegistryRef = useRef(runtimeRegistry);\n\n // Track networks whose runtime failed to load so we don't retry infinitely.\n // Cleared when resolveRuntime changes (e.g. the host app fixes the issue).\n const failedNetworksRef = useRef<Set<string>>(new Set());\n\n // Bumped when resolveRuntime identity changes so in-flight resolves from a\n // prior resolver cannot repopulate the registry with stale runtimes.\n const resolverGenerationRef = useRef(0);\n const resolverInitializedRef = useRef(false);\n\n useEffect(() => {\n runtimeRegistryRef.current = runtimeRegistry;\n }, [runtimeRegistry]);\n\n // Reset failure tracking and evict all cached runtimes when the resolver\n // function changes (e.g. opt-in toggle). INV-218: full registry disposal —\n // not only failedNetworksRef — so ctor-frozen capabilities cannot go stale.\n useEffect(() => {\n if (resolverInitializedRef.current) {\n resolverGenerationRef.current += 1;\n } else {\n resolverInitializedRef.current = true;\n }\n\n const registry = runtimeRegistryRef.current;\n const runtimes = Object.values(registry);\n const hadCachedRuntimes = runtimes.length > 0;\n\n failedNetworksRef.current = new Set();\n setLoadingNetworks(new Set());\n\n if (hadCachedRuntimes) {\n setRuntimeRegistry({});\n\n // INV-223: deferred disposal — same macrotask discipline as releaseRuntime.\n setTimeout(() => {\n runtimes.forEach((runtime) => {\n try {\n runtime.dispose();\n } catch (error) {\n logger.error(\n 'RuntimeProvider',\n 'Error disposing runtime during registry flush:',\n error\n );\n }\n });\n }, 0);\n }\n }, [resolveRuntime]);\n\n useEffect(() => {\n return () => {\n Object.values(runtimeRegistryRef.current).forEach((runtime) => {\n runtime.dispose();\n });\n };\n }, []);\n\n // Log registry status on changes\n useEffect(() => {\n const runtimeCount = Object.keys(runtimeRegistry).length;\n if (runtimeCount > 0) {\n logger.info('RuntimeProvider', `Registry contains ${runtimeCount} runtimes:`, {\n networkIds: Object.keys(runtimeRegistry),\n loadingCount: loadingNetworks.size,\n loadingNetworkIds: Array.from(loadingNetworks),\n });\n }\n }, [runtimeRegistry, loadingNetworks]);\n\n /**\n * Evicts a runtime from the registry by network ID and disposes it.\n * This is the safe counterpart to `getRuntimeForNetwork`: callers should only\n * release a runtime after they have already promoted its replacement.\n *\n * Disposal is deferred to the next macrotask so React's commit phase\n * (including development-mode prop diffing) can finish reading the old\n * runtime's properties without hitting the RuntimeDisposedError proxy trap.\n */\n const releaseRuntime = useCallback((networkId: string) => {\n const runtime = runtimeRegistryRef.current[networkId];\n if (!runtime) {\n return;\n }\n\n logger.info('RuntimeProvider', `Releasing runtime for network ${networkId}`);\n\n setRuntimeRegistry((prev) => {\n const next = { ...prev };\n delete next[networkId];\n return next;\n });\n\n setTimeout(() => {\n try {\n runtime.dispose();\n } catch (error) {\n logger.error('RuntimeProvider', `Error disposing runtime for network ${networkId}:`, error);\n }\n }, 0);\n }, []);\n\n /**\n * Function to get or create a runtime for a network\n *\n * IMPORTANT: Runtime loading is coordinated carefully to avoid React state updates\n * during render, which would cause errors.\n *\n * This function:\n * 1. Returns existing runtimes immediately if available\n * 2. Reports loading state for runtimes being initialized\n * 3. Initiates runtime loading when needed\n */\n const getRuntimeForNetwork = useCallback(\n (networkConfig: NetworkConfig | null) => {\n if (!networkConfig) {\n return { runtime: null, isLoading: false };\n }\n\n const networkId = networkConfig.id;\n\n // Debug log to track runtime requests\n logger.debug('RuntimeProvider', `Runtime requested for network ${networkId}`);\n\n // If we already have this runtime, return it\n if (runtimeRegistry[networkId]) {\n logger.debug('RuntimeProvider', `Using existing runtime for network ${networkId}`);\n return {\n runtime: runtimeRegistry[networkId],\n isLoading: false,\n };\n }\n\n // If we're already loading this runtime, indicate loading\n if (loadingNetworks.has(networkId)) {\n logger.debug('RuntimeProvider', `Runtime for network ${networkId} is currently loading`);\n return {\n runtime: null,\n isLoading: true,\n };\n }\n\n // If this network previously failed to load, don't retry.\n if (failedNetworksRef.current.has(networkId)) {\n logger.debug(\n 'RuntimeProvider',\n `Runtime for network ${networkId} previously failed; skipping retry`\n );\n return {\n runtime: null,\n isLoading: false,\n };\n }\n\n // Start loading the runtime.\n setLoadingNetworks((prev) => {\n const newSet = new Set(prev);\n newSet.add(networkId);\n return newSet;\n });\n\n logger.info(\n 'RuntimeProvider',\n `Starting runtime initialization for network ${networkId} (${networkConfig.name})`\n );\n\n const generation = resolverGenerationRef.current;\n\n // Use the passed-in resolveRuntime function\n void resolveRuntime(networkConfig)\n .then((runtime) => {\n if (generation !== resolverGenerationRef.current) {\n // Stale resolver — do not add to registry; dispose the orphan.\n setTimeout(() => {\n try {\n runtime.dispose();\n } catch (error) {\n logger.error(\n 'RuntimeProvider',\n `Error disposing stale runtime for network ${networkId}:`,\n error\n );\n }\n }, 0);\n\n setLoadingNetworks((prev) => {\n const newSet = new Set(prev);\n newSet.delete(networkId);\n return newSet;\n });\n return;\n }\n\n logger.info('RuntimeProvider', `Runtime for network ${networkId} loaded successfully`, {\n type: runtime.constructor.name,\n objectId: Object.prototype.toString.call(runtime),\n });\n\n // Update registry with new runtime\n setRuntimeRegistry((prev) => ({\n ...prev,\n [networkId]: runtime,\n }));\n\n // Remove from loading networks\n setLoadingNetworks((prev) => {\n const newSet = new Set(prev);\n newSet.delete(networkId);\n return newSet;\n });\n })\n .catch((error) => {\n if (generation !== resolverGenerationRef.current) {\n setLoadingNetworks((prev) => {\n const newSet = new Set(prev);\n newSet.delete(networkId);\n return newSet;\n });\n return;\n }\n\n logger.error('RuntimeProvider', `Error loading runtime for network ${networkId}:`, error);\n\n failedNetworksRef.current.add(networkId);\n\n setLoadingNetworks((prev) => {\n const newSet = new Set(prev);\n newSet.delete(networkId);\n return newSet;\n });\n });\n\n return {\n runtime: null,\n isLoading: true,\n };\n },\n [runtimeRegistry, loadingNetworks, resolveRuntime]\n );\n\n // Memoize context value to prevent unnecessary re-renders\n const contextValue = useMemo<RuntimeContextValue>(\n () => ({\n getRuntimeForNetwork,\n releaseRuntime,\n }),\n [getRuntimeForNetwork, releaseRuntime]\n );\n\n return <RuntimeContext.Provider value={contextValue}>{children}</RuntimeContext.Provider>;\n}\n","import React, { createContext } from 'react';\n\nimport type {\n EcosystemRuntime,\n EcosystemSpecificReactHooks,\n NetworkConfig,\n UiKitConfiguration,\n} from '@openzeppelin/ui-types';\n\nexport interface WalletStateContextValue {\n // Globally selected network state\n activeNetworkId: string | null;\n setActiveNetworkId: (networkId: string | null) => void;\n activeNetworkConfig: NetworkConfig | null;\n\n // Active runtime state\n activeRuntime: EcosystemRuntime | null;\n isRuntimeLoading: boolean;\n\n // Facade hooks object from the active runtime's UI kit\n // Consumers will call these hooks (e.g., walletFacadeHooks.useAccount())\n walletFacadeHooks: EcosystemSpecificReactHooks | null;\n reconfigureActiveUiKit: (uiKitConfig?: Partial<UiKitConfiguration>) => void;\n}\n\n/**\n * Shared Global Context Pattern\n * =============================\n *\n * WHY THIS EXISTS:\n * When bundlers (like Vite's optimizeDeps with esbuild) pre-bundle dependencies,\n * they may inline transitive dependencies like @openzeppelin/ui-react into the\n * consuming package's bundle. This creates MULTIPLE instances of this module:\n *\n * 1. The app's direct import → packages/react/dist/index.js\n * 2. The adapter package's inlined copy → .vite/deps/@openzeppelin_adapter_evm.js\n *\n * Since React contexts use referential identity, these two module instances have\n * DIFFERENT context objects. When the adapter's components call useWalletState(),\n * they look for a context that was never provided (because WalletStateProvider\n * uses the app's context, not the adapter's inlined copy).\n *\n * SOLUTION:\n * Store the context object on globalThis so ALL module instances share the same\n * React context, regardless of how they were loaded or bundled.\n *\n * WHEN IS THIS NEEDED:\n * - Development with Vite's dependency pre-bundling (optimizeDeps)\n * - When adapters are installed from npm (not workspace-linked with same bundler)\n * - Any scenario where @openzeppelin/ui-react might be duplicated\n *\n * PRODUCTION APPS:\n * In production builds where the app and adapters are bundled together with proper\n * deduplication (e.g., via bundler configuration or peer dependencies), this\n * workaround may not be strictly necessary. However, it provides a safety net\n * and has minimal overhead.\n */\n\n/**\n * Use Symbol.for() to create a globally unique key that is consistent across\n * all module instances. Unlike Symbol(), Symbol.for() returns the same symbol\n * for the same key string, which is essential for cross-module sharing.\n */\nconst WALLET_STATE_CONTEXT_KEY = Symbol.for('@openzeppelin/ui-react/WalletStateContext');\n\n/**\n * Type-safe interface for the global object extension.\n * This provides better type safety than using Record<string, unknown>.\n */\ninterface GlobalWithWalletContext {\n [WALLET_STATE_CONTEXT_KEY]?: React.Context<WalletStateContextValue | undefined>;\n}\n\n/**\n * Retrieves or creates the shared WalletStateContext.\n *\n * NOTE ON ATOMICITY:\n * The check-then-set pattern here is not atomic, but this is acceptable because:\n * 1. JavaScript is single-threaded; module initialization is synchronous\n * 2. Even if multiple modules initialize \"simultaneously\" during parallel loading,\n * they execute sequentially on the main thread\n * 3. The worst case (two contexts created) would only happen if the check and set\n * were somehow interleaved, which cannot occur in JS's execution model\n * 4. For React contexts specifically, having the same context object is what matters,\n * and this pattern guarantees that after initialization\n */\nfunction getOrCreateSharedContext(): React.Context<WalletStateContextValue | undefined> {\n const global = globalThis as GlobalWithWalletContext;\n\n if (!global[WALLET_STATE_CONTEXT_KEY]) {\n global[WALLET_STATE_CONTEXT_KEY] = createContext<WalletStateContextValue | undefined>(\n undefined\n );\n }\n\n return global[WALLET_STATE_CONTEXT_KEY];\n}\n\nexport const WalletStateContext = getOrCreateSharedContext();\n\n/**\n * Hook to access wallet state from WalletStateProvider.\n * @throws Error if used outside of WalletStateProvider\n */\nexport function useWalletState(): WalletStateContextValue {\n const context = React.useContext(WalletStateContext);\n if (context === undefined) {\n throw new Error('useWalletState must be used within a WalletStateProvider');\n }\n return context;\n}\n","/**\n * useAdapterContext.ts\n *\n * This file provides a hook to access the runtime context throughout the application.\n * It's a critical part of the runtime singleton pattern, allowing components to\n * access the centralized runtime registry.\n *\n * The runtime singleton pattern ensures:\n * - Only one runtime instance exists per network\n * - Wallet connection state is consistent across the app\n * - Better performance by eliminating redundant runtime initialization\n */\nimport { useContext } from 'react';\n\nimport { RuntimeContext, RuntimeContextValue } from './AdapterContext';\n\n/**\n * Hook to access the runtime context\n *\n * This hook provides access to the `getRuntimeForNetwork` function which\n * retrieves or creates runtime instances from the singleton registry.\n *\n * Components should typically use the higher-level wallet/runtime hooks instead\n * of this hook directly, as it handles React state update timing properly.\n *\n * @throws Error if used outside of a RuntimeProvider context\n * @returns The runtime context value\n */\nexport function useRuntimeContext(): RuntimeContextValue {\n const context = useContext(RuntimeContext);\n\n if (!context) {\n throw new Error('useRuntimeContext must be used within a RuntimeProvider');\n }\n\n return context;\n}\n","import type React from 'react';\n\nimport type {\n EcosystemReactUiProviderProps,\n EcosystemSpecificReactHooks,\n} from '@openzeppelin/ui-types';\n\n/**\n * Cached wallet session artifacts for a single ecosystem.\n *\n * This keeps the wallet provider and facade hooks stable while network-scoped\n * runtimes are recreated underneath the session.\n */\nexport interface WalletSessionEntry {\n /** Ecosystem identifier, e.g. `evm` or `stellar`. */\n ecosystem: string;\n /** The network whose runtime most recently configured this session. */\n lastConfiguredNetworkId: string;\n /** Ecosystem-scoped React provider root for wallet libraries such as wagmi. */\n providerComponent: React.ComponentType<EcosystemReactUiProviderProps> | null;\n /** Facade hooks resolved from the ecosystem session. */\n hooks: EcosystemSpecificReactHooks | null;\n}\n\n/**\n * Internal registry of dormant and active wallet sessions keyed by ecosystem.\n */\nexport type WalletSessionRegistry = Record<string, WalletSessionEntry>;\n\n/**\n * Returns the cached wallet session for an ecosystem, if present.\n *\n * @param registry - Current internal wallet session registry.\n * @param ecosystem - Ecosystem key to resolve.\n * @returns The cached session entry or `null` when the ecosystem has not been configured yet.\n */\nexport function getWalletSession(\n registry: WalletSessionRegistry,\n ecosystem: string | null | undefined\n): WalletSessionEntry | null {\n if (!ecosystem) {\n return null;\n }\n\n return registry[ecosystem] ?? null;\n}\n\n/**\n * Inserts or replaces the cached wallet session for a single ecosystem.\n *\n * @param registry - Current internal wallet session registry.\n * @param session - Session entry to cache.\n * @returns A new registry object containing the upserted ecosystem session.\n */\nexport function upsertWalletSession(\n registry: WalletSessionRegistry,\n session: WalletSessionEntry\n): WalletSessionRegistry {\n return {\n ...registry,\n [session.ecosystem]: session,\n };\n}\n","import React, { ReactNode, useCallback, useEffect, useMemo, useRef, useState } from 'react';\n\nimport type {\n EcosystemReactUiProviderProps,\n EcosystemRuntime,\n EcosystemSpecificReactHooks,\n NativeConfigLoader,\n NetworkConfig,\n UiKitConfiguration,\n} from '@openzeppelin/ui-types';\nimport { logger } from '@openzeppelin/ui-utils';\n\nimport { useRuntimeContext } from './useAdapterContext';\nimport {\n getWalletSession,\n upsertWalletSession,\n type WalletSessionRegistry,\n} from './walletSessionRegistry';\nimport { WalletStateContext, type WalletStateContextValue } from './WalletStateContext';\n\nexport interface WalletStateProviderProps {\n children: ReactNode;\n /** Optional initial network ID to set as active when the provider mounts. */\n initialNetworkId?: string | null;\n /** Function to retrieve a NetworkConfig object by its ID. */\n getNetworkConfigById: (\n networkId: string\n ) => Promise<NetworkConfig | null | undefined> | NetworkConfig | null | undefined;\n /**\n * Optional generic function to load configuration modules by relative path.\n * The adapter is responsible for constructing the conventional path (e.g., './config/wallet/[kitName].config').\n * @param relativePath The conventional relative path to the configuration module.\n * @returns A Promise resolving to the configuration object (expected to have a default export) or null.\n */\n loadConfigModule?: NativeConfigLoader;\n}\n\n/**\n * Configures the runtime's UI kit capability and returns the ecosystem session artifacts.\n */\nasync function configureRuntimeUiKit(\n runtime: EcosystemRuntime,\n loadConfigModule?: (relativePath: string) => Promise<Record<string, unknown> | null>,\n programmaticOverrides: Partial<UiKitConfiguration> = {}\n): Promise<{\n providerComponent: React.ComponentType<EcosystemReactUiProviderProps> | null;\n hooks: EcosystemSpecificReactHooks | null;\n}> {\n const uiKit = runtime.uiKit;\n if (!uiKit) {\n return { providerComponent: null, hooks: null };\n }\n\n try {\n const hasUiKitOverride = Object.keys(programmaticOverrides).length > 0;\n\n // Always initialize the runtime UI kit so adapter-managed providers (wagmi, Stellar kit, etc.)\n // can hydrate their internal state from app-config defaults even without explicit overrides.\n if (typeof uiKit.configureUiKit === 'function') {\n const nextUiKitConfig = { ...programmaticOverrides };\n logger.info(\n '[WSP configureRuntimeUiKit]',\n hasUiKitOverride\n ? `Applying explicit UI kit overrides for runtime: ${runtime.networkConfig.id}`\n : `Initializing runtime UI kit from adapter/app defaults for runtime: ${runtime.networkConfig.id}`\n );\n await uiKit.configureUiKit(nextUiKitConfig, {\n loadUiKitNativeConfig: loadConfigModule,\n });\n logger.info(\n '[WSP configureRuntimeUiKit] configureUiKit completed for runtime:',\n runtime.networkConfig.id\n );\n }\n\n const providerComponent = uiKit.getEcosystemReactUiContextProvider?.() || null;\n const hooks = uiKit.getEcosystemReactHooks?.() || null;\n\n logger.info('[WSP configureRuntimeUiKit]', 'UI provider and hooks retrieved successfully.');\n\n return { providerComponent, hooks };\n } catch (error) {\n logger.error('[WSP configureRuntimeUiKit]', 'Error during runtime UI setup:', error);\n throw error; // Re-throw to be handled by caller\n }\n}\n\n/**\n * @name WalletStateProvider\n * @description This provider is a central piece of the application's state management for wallet and network interactions.\n * It is responsible for:\n * 1. Managing the globally selected active network ID (`activeNetworkId`).\n * 2. Deriving the full `NetworkConfig` object (`activeNetworkConfig`) for the active network.\n * 3. Fetching and providing the corresponding `EcosystemRuntime` (`activeRuntime`) for the active network,\n * leveraging the `RuntimeProvider` to ensure runtime singletons.\n * 4. Caching ecosystem-scoped wallet session artifacts (provider roots and facade hooks)\n * independently from the network-scoped runtime.\n * 5. Rendering the active ecosystem wallet provider (e.g., WagmiProvider for EVM) around its\n * children, which is essential for the facade hooks to function correctly.\n * 6. Providing a function (`setActiveNetworkId`) to change the globally active network.\n *\n * Consumers use the `useWalletState()` hook to access this global state.\n * It should be placed high in the component tree, inside a `<RuntimeProvider>`.\n */\nexport function WalletStateProvider({\n children,\n initialNetworkId = null,\n getNetworkConfigById,\n loadConfigModule,\n}: WalletStateProviderProps) {\n // State for the ID of the globally selected network.\n const [currentGlobalNetworkId, setCurrentGlobalNetworkIdState] = useState<string | null>(\n initialNetworkId\n );\n // State for the full NetworkConfig object of the globally selected network.\n const [currentGlobalNetworkConfig, setCurrentGlobalNetworkConfig] =\n useState<NetworkConfig | null>(null);\n\n // State for the active EcosystemRuntime corresponding to the currentGlobalNetworkConfig.\n const [globalActiveRuntime, setGlobalActiveRuntime] = useState<EcosystemRuntime | null>(null);\n // Loading state for the globalActiveRuntime.\n const [isGlobalRuntimeLoading, setIsGlobalRuntimeLoading] = useState<boolean>(false);\n // Cache one wallet provider/hooks pair per ecosystem so same-ecosystem network switches do not\n // remount the wallet provider. The active runtime remains network-scoped and disposable.\n const [walletSessionRegistry, setWalletSessionRegistry] = useState<WalletSessionRegistry>({});\n const [activeWalletSessionEcosystem, setActiveWalletSessionEcosystem] = useState<string | null>(\n null\n );\n\n // New state to act as a manual trigger for re-configuring the UI kit.\n const [uiKitConfigVersion, setUiKitConfigVersion] = useState(0);\n // State to hold programmatic overrides for the next reconfiguration.\n const [programmaticUiKitConfig, setProgrammaticUiKitConfig] = useState<\n Partial<UiKitConfiguration> | undefined\n >(undefined);\n\n // Consume RuntimeContext to get the function for fetching runtime instances.\n const { getRuntimeForNetwork, releaseRuntime } = useRuntimeContext();\n\n // Track the network ID of the currently promoted runtime so we can release it\n // after a successful handoff to the next runtime.\n const promotedNetworkIdRef = useRef<string | null>(null);\n\n // Effect to derive the full NetworkConfig object when currentGlobalNetworkId changes.\n useEffect(() => {\n const abortController = new AbortController();\n\n async function fetchNetworkConfig() {\n if (!currentGlobalNetworkId) {\n // If currentGlobalNetworkId is null, clear the config.\n if (!abortController.signal.aborted) {\n setCurrentGlobalNetworkConfig(null);\n }\n return;\n }\n\n try {\n const config = await Promise.resolve(getNetworkConfigById(currentGlobalNetworkId));\n if (!abortController.signal.aborted) {\n setCurrentGlobalNetworkConfig(config || null);\n }\n } catch (error) {\n if (!abortController.signal.aborted) {\n logger.error('[WSP fetchNetworkConfig]', 'Failed to fetch network config:', error);\n setCurrentGlobalNetworkConfig(null);\n }\n }\n }\n\n void fetchNetworkConfig();\n return () => abortController.abort();\n }, [currentGlobalNetworkId, getNetworkConfigById]);\n\n // Effect to load the active runtime and its UI capabilities when currentGlobalNetworkConfig changes.\n // Implements a safe handoff: the previous runtime stays active while the replacement loads and\n // configures its UI kit. Only after the new runtime is promoted does the old one get released.\n useEffect(() => {\n const abortController = new AbortController();\n\n async function loadRuntimeAndConfigureUi() {\n if (!currentGlobalNetworkConfig) {\n if (!abortController.signal.aborted) {\n const prevNetworkId = promotedNetworkIdRef.current;\n setGlobalActiveRuntime(null);\n setIsGlobalRuntimeLoading(false);\n setActiveWalletSessionEcosystem(null);\n\n if (prevNetworkId) {\n releaseRuntime(prevNetworkId);\n promotedNetworkIdRef.current = null;\n }\n }\n return;\n }\n\n const { runtime: newRuntime, isLoading: newIsLoading } = getRuntimeForNetwork(\n currentGlobalNetworkConfig\n ) as { runtime: EcosystemRuntime | null; isLoading: boolean };\n\n if (abortController.signal.aborted) return;\n\n setIsGlobalRuntimeLoading(newIsLoading);\n\n if (newRuntime && !newIsLoading) {\n try {\n const { providerComponent, hooks } = await configureRuntimeUiKit(\n newRuntime,\n loadConfigModule,\n programmaticUiKitConfig\n );\n\n if (!abortController.signal.aborted) {\n const ecosystem = newRuntime.networkConfig.ecosystem;\n const prevNetworkId = promotedNetworkIdRef.current;\n const nextNetworkId = newRuntime.networkConfig.id;\n\n setWalletSessionRegistry((prevRegistry) =>\n upsertWalletSession(prevRegistry, {\n ecosystem,\n lastConfiguredNetworkId: nextNetworkId,\n providerComponent,\n hooks,\n })\n );\n setGlobalActiveRuntime(newRuntime);\n setActiveWalletSessionEcosystem(ecosystem);\n promotedNetworkIdRef.current = nextNetworkId;\n\n // Release the superseded runtime now that the replacement is promoted.\n // Skip when the network ID hasn't changed (e.g. UI-kit reconfiguration).\n if (prevNetworkId && prevNetworkId !== nextNetworkId) {\n releaseRuntime(prevNetworkId);\n }\n }\n } catch (error) {\n if (!abortController.signal.aborted) {\n logger.error(\n '[WSP loadRuntimeAndConfigureUi]',\n 'Error during runtime UI setup:',\n error\n );\n }\n }\n } else if (!newRuntime && !newIsLoading) {\n if (!abortController.signal.aborted) {\n const prevNetworkId = promotedNetworkIdRef.current;\n setGlobalActiveRuntime(null);\n setActiveWalletSessionEcosystem(null);\n\n if (prevNetworkId) {\n releaseRuntime(prevNetworkId);\n promotedNetworkIdRef.current = null;\n }\n }\n }\n // If newIsLoading is true, retain the active wallet session so same-ecosystem switches do\n // not tear down the connected provider while the target runtime is still loading.\n }\n\n void loadRuntimeAndConfigureUi();\n return () => abortController.abort();\n }, [\n currentGlobalNetworkConfig,\n getRuntimeForNetwork,\n releaseRuntime,\n loadConfigModule,\n uiKitConfigVersion,\n programmaticUiKitConfig,\n ]);\n\n /**\n * Callback to set the globally active network ID.\n * Also clears dependent states if the network ID is cleared.\n */\n const setActiveNetworkIdCallback = useCallback((networkId: string | null) => {\n logger.info('WalletStateProvider', `Setting global network ID to: ${networkId}`);\n setCurrentGlobalNetworkIdState(networkId); // This will trigger the fetchNetworkConfig effect.\n if (!networkId) {\n // If clearing the network, proactively clear downstream states.\n // The effects above will also clear them, but this is more immediate.\n setCurrentGlobalNetworkConfig(null);\n setGlobalActiveRuntime(null);\n setIsGlobalRuntimeLoading(false);\n setActiveWalletSessionEcosystem(null);\n }\n }, []); // Empty dependency array as it only uses setters from useState.\n\n /**\n * Callback to explicitly trigger a re-configuration of the active runtime's UI kit.\n * This is useful when a UI kit setting changes (e.g., via a wizard) without a network change.\n */\n const reconfigureActiveUiKit = useCallback(\n (uiKitConfig?: Partial<UiKitConfiguration>) => {\n logger.info(\n 'WalletStateProvider',\n 'Explicitly triggering UI kit re-configuration by bumping version.',\n uiKitConfig\n );\n setProgrammaticUiKitConfig(uiKitConfig);\n setUiKitConfigVersion((v) => v + 1);\n },\n [setProgrammaticUiKitConfig, setUiKitConfigVersion]\n );\n\n const activeWalletSession = useMemo(\n () => getWalletSession(walletSessionRegistry, activeWalletSessionEcosystem),\n [walletSessionRegistry, activeWalletSessionEcosystem]\n );\n\n const walletFacadeHooks: EcosystemSpecificReactHooks | null = activeWalletSession?.hooks ?? null;\n\n // The context value exposes the active network-scoped runtime and the active ecosystem-scoped\n // wallet session hooks. Consumers continue to access the same public fields.\n const contextValue = useMemo<WalletStateContextValue>(\n () => ({\n activeNetworkId: currentGlobalNetworkId,\n setActiveNetworkId: setActiveNetworkIdCallback,\n activeNetworkConfig: currentGlobalNetworkConfig,\n activeRuntime: globalActiveRuntime,\n isRuntimeLoading: isGlobalRuntimeLoading,\n walletFacadeHooks,\n reconfigureActiveUiKit,\n }),\n [\n currentGlobalNetworkId,\n setActiveNetworkIdCallback,\n currentGlobalNetworkConfig,\n globalActiveRuntime,\n isGlobalRuntimeLoading,\n walletFacadeHooks,\n reconfigureActiveUiKit,\n ]\n );\n\n const ActualProviderToRender = activeWalletSession?.providerComponent ?? null;\n let childrenToRender: ReactNode;\n\n if (ActualProviderToRender) {\n // Key the provider by ecosystem instead of network so same-ecosystem network switches keep\n // the wallet provider mounted. Switching ecosystems still remounts the provider cleanly.\n const key = activeWalletSessionEcosystem || 'unknown';\n\n // Runtime-provided UI roots manage their own configuration internally via the UI kit capability.\n logger.debug(\n '[WSP RENDER]',\n 'Rendering runtime-provided UI context provider:',\n ActualProviderToRender.displayName || ActualProviderToRender.name || 'UnknownComponent',\n 'with key:',\n key\n );\n childrenToRender = <ActualProviderToRender key={key}>{children}</ActualProviderToRender>;\n } else {\n logger.debug(\n '[WSP RENDER]',\n 'No runtime UI context provider to render. Rendering direct children.'\n );\n childrenToRender = children;\n }\n\n return (\n <WalletStateContext.Provider value={contextValue}>\n {childrenToRender}\n </WalletStateContext.Provider>\n );\n}\n","import { createContext, useContext } from 'react';\n\n/**\n * Analytics context value interface.\n * Provides access to analytics functionality throughout the React component tree.\n */\nexport interface AnalyticsContextValue {\n /** Google Analytics tag ID */\n tagId?: string;\n /**\n * Check if analytics is enabled via feature flag.\n * Returns fresh state on each call to handle dynamic feature flag changes.\n */\n isEnabled: () => boolean;\n /** Initialize analytics with optional tag ID override */\n initialize: (tagIdOverride?: string) => void;\n /**\n * Track a generic event with custom parameters.\n * Use this for app-specific events.\n *\n * @example\n * ```typescript\n * trackEvent('button_clicked', { button_name: 'submit' });\n * ```\n */\n trackEvent: (eventName: string, parameters: Record<string, string | number>) => void;\n /**\n * Track page view event.\n *\n * @example\n * ```typescript\n * trackPageView('Dashboard', '/dashboard');\n * ```\n */\n trackPageView: (pageName: string, pagePath: string) => void;\n /**\n * Track network selection event.\n *\n * @example\n * ```typescript\n * trackNetworkSelection('ethereum-mainnet', 'evm');\n * ```\n */\n trackNetworkSelection: (networkId: string, ecosystem: string) => void;\n}\n\n/**\n * Analytics context for providing analytics functionality to React components.\n * Must be used within an AnalyticsProvider.\n */\nexport const AnalyticsContext = createContext<AnalyticsContextValue | null>(null);\n\n/**\n * Internal hook to access analytics context.\n * Throws an error if used outside of an AnalyticsProvider.\n *\n * @internal\n * @throws Error if used outside of AnalyticsProvider\n */\nexport const useAnalyticsContext = (): AnalyticsContextValue => {\n const context = useContext(AnalyticsContext);\n if (!context) {\n throw new Error('useAnalyticsContext must be used within an AnalyticsProvider');\n }\n return context;\n};\n","import React, { ReactNode, useEffect, useMemo } from 'react';\n\nimport { AnalyticsService, logger } from '@openzeppelin/ui-utils';\n\nimport { AnalyticsContext, AnalyticsContextValue } from './AnalyticsContext';\n\n/**\n * Props for the AnalyticsProvider component\n */\nexport interface AnalyticsProviderProps {\n /** Google Analytics tag ID (e.g., 'G-XXXXXXXXXX') */\n tagId?: string;\n /** Whether to automatically initialize analytics on mount (default: true) */\n autoInit?: boolean;\n /** Child components */\n children: ReactNode;\n}\n\n/**\n * Analytics Provider component.\n * Provides analytics functionality throughout the React component tree.\n *\n * @example\n * ```tsx\n * function App() {\n * return (\n * <AnalyticsProvider tagId={import.meta.env.VITE_GA_TAG_ID} autoInit={true}>\n * <YourApp />\n * </AnalyticsProvider>\n * );\n * }\n * ```\n */\nexport const AnalyticsProvider: React.FC<AnalyticsProviderProps> = ({\n tagId,\n autoInit = true,\n children,\n}) => {\n useEffect(() => {\n if (autoInit && tagId) {\n AnalyticsService.initialize(tagId);\n }\n }, [tagId, autoInit]);\n\n const contextValue: AnalyticsContextValue = useMemo(\n () => ({\n tagId,\n isEnabled: () => AnalyticsService.isEnabled(),\n initialize: (tagIdOverride?: string) => {\n const effectiveTagId = tagIdOverride || tagId;\n if (effectiveTagId) {\n AnalyticsService.initialize(effectiveTagId);\n }\n },\n trackEvent: (eventName: string, parameters: Record<string, string | number>) => {\n try {\n AnalyticsService.trackEvent(eventName, parameters);\n } catch (error) {\n logger.error('AnalyticsProvider', 'Error tracking event:', error);\n }\n },\n trackPageView: (pageName: string, pagePath: string) => {\n try {\n AnalyticsService.trackPageView(pageName, pagePath);\n } catch (error) {\n logger.error('AnalyticsProvider', 'Error tracking page view:', error);\n }\n },\n trackNetworkSelection: (networkId: string, ecosystem: string) => {\n try {\n AnalyticsService.trackNetworkSelection(networkId, ecosystem);\n } catch (error) {\n logger.error('AnalyticsProvider', 'Error tracking network selection:', error);\n }\n },\n }),\n [tagId]\n );\n\n return <AnalyticsContext.Provider value={contextValue}>{children}</AnalyticsContext.Provider>;\n};\n","import { useAnalyticsContext } from './AnalyticsContext';\n\n/**\n * Custom hook for accessing analytics functionality.\n *\n * This hook provides a convenient interface for tracking user interactions\n * throughout the application. It must be used within an AnalyticsProvider.\n *\n * For app-specific tracking methods, create a wrapper hook that uses this\n * hook and adds your custom tracking functions.\n *\n * @example\n * ```tsx\n * // Basic usage\n * function MyComponent() {\n * const { trackEvent, trackPageView, isEnabled } = useAnalytics();\n *\n * const handleClick = () => {\n * trackEvent('button_clicked', { button_name: 'submit' });\n * };\n *\n * return (\n * <div>\n * Analytics enabled: {isEnabled().toString()}\n * <button onClick={handleClick}>Submit</button>\n * </div>\n * );\n * }\n * ```\n *\n * @example\n * ```tsx\n * // Creating app-specific wrapper hook\n * function useMyAppAnalytics() {\n * const analytics = useAnalytics();\n *\n * return {\n * ...analytics,\n * trackFormSubmit: (formName: string) => {\n * analytics.trackEvent('form_submitted', { form_name: formName });\n * },\n * };\n * }\n * ```\n *\n * @returns Analytics context with tracking methods and state\n * @throws Error if used outside of AnalyticsProvider\n */\nexport const useAnalytics = () => {\n try {\n return useAnalyticsContext();\n } catch {\n throw new Error('useAnalytics must be used within an AnalyticsProvider');\n }\n};\n","import type { EcosystemWalletComponents } from '@openzeppelin/ui-types';\n\nimport { useWalletState } from './WalletStateContext';\n\n/**\n * Hook that provides direct access to wallet UI components from the active runtime.\n *\n * Use this hook when you need full control over the layout and composition of\n * wallet components. For standard layouts, prefer using `WalletConnectionUI`\n * with its props forwarding capabilities.\n *\n * @returns The wallet components object, or null if no runtime is active or\n * the runtime doesn't provide wallet components.\n *\n * @example\n * ```tsx\n * import { useWalletComponents } from '@openzeppelin/ui-react';\n *\n * function CustomWalletSection() {\n * const walletComponents = useWalletComponents();\n *\n * if (!walletComponents) {\n * return <p>Loading wallet...</p>;\n * }\n *\n * const { ConnectButton, NetworkSwitcher, AccountDisplay } = walletComponents;\n *\n * return (\n * <div className=\"flex flex-col gap-4\">\n * {ConnectButton && (\n * <ConnectButton\n * size=\"xl\"\n * variant=\"outline\"\n * fullWidth\n * className=\"font-semibold\"\n * />\n * )}\n * <div className=\"flex gap-2\">\n * {NetworkSwitcher && <NetworkSwitcher size=\"sm\" />}\n * {AccountDisplay && <AccountDisplay size=\"sm\" />}\n * </div>\n * </div>\n * );\n * }\n * ```\n */\nexport function useWalletComponents(): EcosystemWalletComponents | null {\n const { activeNetworkConfig, activeRuntime, isRuntimeLoading } = useWalletState();\n const activeUiKit = activeRuntime?.uiKit;\n const isCrossEcosystemTransition = !!(\n isRuntimeLoading &&\n activeRuntime?.networkConfig?.ecosystem &&\n activeNetworkConfig?.ecosystem &&\n activeRuntime.networkConfig.ecosystem !== activeNetworkConfig.ecosystem\n );\n\n if (\n isCrossEcosystemTransition ||\n !activeUiKit ||\n typeof activeUiKit.getEcosystemWalletComponents !== 'function'\n ) {\n return null;\n }\n\n try {\n return activeUiKit.getEcosystemWalletComponents() ?? null;\n } catch {\n return null;\n }\n}\n","// Assumes WalletStateContext exports useWalletState\nimport { isRecordWithProperties } from '@openzeppelin/ui-utils';\n\nimport { useWalletState } from './WalletStateContext';\n\nexport interface DerivedAccountStatus {\n isConnected: boolean;\n isConnecting: boolean;\n isDisconnected: boolean;\n isReconnecting: boolean;\n status: string;\n address?: string;\n chainId?: number;\n // Potentially add other commonly used and safely extracted properties from useAccount's result\n}\n\nconst defaultAccountStatus: DerivedAccountStatus = {\n isConnected: false,\n isConnecting: false,\n isDisconnected: true,\n isReconnecting: false,\n status: 'disconnected',\n address: undefined,\n chainId: undefined,\n};\n\n/**\n * A custom hook that consumes useWalletState to get the walletFacadeHooks,\n * then calls the useAccount facade hook (if available) and returns a structured,\n * safely-accessed account status (isConnected, address, chainId).\n * Provides default values if the hook or its properties are unavailable.\n */\nexport function useDerivedAccountStatus(): DerivedAccountStatus {\n const { walletFacadeHooks } = useWalletState();\n\n // Call the useAccount hook from the facade\n const accountHookOutput = walletFacadeHooks?.useAccount\n ? walletFacadeHooks.useAccount()\n : undefined;\n\n if (isRecordWithProperties(accountHookOutput)) {\n const isConnected =\n 'isConnected' in accountHookOutput && typeof accountHookOutput.isConnected === 'boolean'\n ? accountHookOutput.isConnected\n : defaultAccountStatus.isConnected;\n const isConnecting =\n 'isConnecting' in accountHookOutput && typeof accountHookOutput.isConnecting === 'boolean'\n ? accountHookOutput.isConnecting\n : defaultAccountStatus.isConnecting;\n const isDisconnected =\n 'isDisconnected' in accountHookOutput && typeof accountHookOutput.isDisconnected === 'boolean'\n ? accountHookOutput.isDisconnected\n : defaultAccountStatus.isDisconnected;\n const isReconnecting =\n 'isReconnecting' in accountHookOutput && typeof accountHookOutput.isReconnecting === 'boolean'\n ? accountHookOutput.isReconnecting\n : defaultAccountStatus.isReconnecting;\n const status =\n 'status' in accountHookOutput && typeof accountHookOutput.status === 'string'\n ? accountHookOutput.status\n : defaultAccountStatus.status;\n const address =\n 'address' in accountHookOutput && typeof accountHookOutput.address === 'string'\n ? accountHookOutput.address\n : defaultAccountStatus.address;\n const chainId =\n 'chainId' in accountHookOutput && typeof accountHookOutput.chainId === 'number'\n ? accountHookOutput.chainId\n : defaultAccountStatus.chainId;\n return {\n isConnected,\n isConnecting,\n isDisconnected,\n isReconnecting,\n status,\n address,\n chainId,\n };\n }\n return defaultAccountStatus;\n}\n","import { isRecordWithProperties } from '@openzeppelin/ui-utils';\n\nimport { useWalletState } from './WalletStateContext';\n\n// Define the expected return shape for the derived hook\nexport interface DerivedSwitchChainStatus {\n /** Function to initiate a network switch. Undefined if not available. */\n switchChain?: (args: { chainId: number }) => void;\n /** True if a network switch is currently in progress. */\n isSwitching: boolean;\n /** Error object if the last switch attempt failed, otherwise null. */\n error: Error | null;\n}\n\nconst defaultSwitchChainStatus: DerivedSwitchChainStatus = {\n switchChain: undefined,\n isSwitching: false,\n error: null,\n};\n\n/**\n * A custom hook that consumes `useWalletState` to get `walletFacadeHooks`,\n * then calls the `useSwitchChain` facade hook (if available) and returns a structured,\n * safely-accessed status and control function for network switching.\n * Provides default values if the hook or its properties are unavailable.\n */\nexport function useDerivedSwitchChainStatus(): DerivedSwitchChainStatus {\n const { walletFacadeHooks } = useWalletState();\n\n const switchChainHookOutput = walletFacadeHooks?.useSwitchChain\n ? walletFacadeHooks.useSwitchChain()\n : undefined;\n\n if (isRecordWithProperties(switchChainHookOutput)) {\n const execSwitchFn =\n 'switchChain' in switchChainHookOutput &&\n typeof switchChainHookOutput.switchChain === 'function'\n ? (switchChainHookOutput.switchChain as (args: { chainId: number }) => void)\n : defaultSwitchChainStatus.switchChain;\n\n const isPending =\n 'isPending' in switchChainHookOutput && typeof switchChainHookOutput.isPending === 'boolean'\n ? switchChainHookOutput.isPending\n : defaultSwitchChainStatus.isSwitching;\n\n const err =\n 'error' in switchChainHookOutput && switchChainHookOutput.error instanceof Error\n ? switchChainHookOutput.error\n : defaultSwitchChainStatus.error;\n\n return { switchChain: execSwitchFn, isSwitching: isPending, error: err };\n }\n\n return defaultSwitchChainStatus;\n}\n","import { logger } from '@openzeppelin/ui-utils';\n\nimport { useWalletState } from './WalletStateContext';\n\nexport interface DerivedChainInfo {\n /** The current chain ID reported by the wallet's active connection, if available. */\n currentChainId?: number;\n /** Array of chains configured in the underlying wallet library (e.g., wagmi). Type is any[] for generic compatibility. */\n availableChains: unknown[];\n}\n\nconst defaultChainInfo: DerivedChainInfo = {\n currentChainId: undefined,\n availableChains: [],\n};\n\n/**\n * A custom hook that consumes `useWalletState` to get `walletFacadeHooks`,\n * then calls the `useChainId` and `useChains` facade hooks (if available)\n * and returns a structured object with this information.\n * Provides default values if the hooks or their properties are unavailable.\n */\nexport function useDerivedChainInfo(): DerivedChainInfo {\n const { walletFacadeHooks } = useWalletState();\n\n let chainIdToReturn: number | undefined = defaultChainInfo.currentChainId;\n const chainIdHookOutput = walletFacadeHooks?.useChainId\n ? walletFacadeHooks.useChainId()\n : undefined;\n // The useChainId hook from wagmi directly returns the number or undefined\n if (typeof chainIdHookOutput === 'number') {\n chainIdToReturn = chainIdHookOutput;\n } else if (chainIdHookOutput !== undefined) {\n // If it's not a number but not undefined, log a warning but use default. Could be an adapter returning unexpected type.\n logger.warn(\n 'useDerivedChainInfo',\n 'useChainId facade hook returned non-numeric value:',\n chainIdHookOutput\n );\n }\n\n let chainsToReturn: unknown[] = defaultChainInfo.availableChains;\n const chainsHookOutput = walletFacadeHooks?.useChains ? walletFacadeHooks.useChains() : undefined;\n // The useChains hook from wagmi directly returns an array of Chain objects\n if (Array.isArray(chainsHookOutput)) {\n chainsToReturn = chainsHookOutput;\n } else if (chainsHookOutput !== undefined) {\n logger.warn(\n 'useDerivedChainInfo',\n 'useChains facade hook returned non-array value:',\n chainsHookOutput\n );\n }\n\n return { currentChainId: chainIdToReturn, availableChains: chainsToReturn };\n}\n","import type { Connector } from '@openzeppelin/ui-types';\nimport { isRecordWithProperties } from '@openzeppelin/ui-utils';\n\nimport { useWalletState } from './WalletStateContext';\n\n// Assuming Connector type is available\n\nexport interface DerivedConnectStatus {\n /** Function to initiate a connection, usually takes a connector. Undefined if not available. */\n connect?: (args?: { connector?: Connector /* or string for id */ }) => void;\n /** Array of available connectors. Type is any[] for broad compatibility until Connector type is fully generic here. */\n connectors: Connector[]; // Or any[] if Connector type from types pkg is too specific for generic hook here\n /** True if a connection attempt is in progress. */\n isConnecting: boolean;\n /** Error object if the last connection attempt failed, otherwise null. */\n error: Error | null;\n /** The connector a connection is pending for, if any. */\n pendingConnector?: Connector; // Or any\n}\n\nconst defaultConnectStatus: DerivedConnectStatus = {\n connect: undefined,\n connectors: [],\n isConnecting: false,\n error: null,\n pendingConnector: undefined,\n};\n\n/**\n * A custom hook that consumes `useWalletState` to get `walletFacadeHooks`,\n * then calls the `useConnect` facade hook (if available) and returns a structured,\n * safely-accessed status and control functions for wallet connection.\n */\nexport function useDerivedConnectStatus(): DerivedConnectStatus {\n const { walletFacadeHooks } = useWalletState();\n\n const connectHookOutput = walletFacadeHooks?.useConnect\n ? walletFacadeHooks.useConnect()\n : undefined;\n\n if (isRecordWithProperties(connectHookOutput)) {\n const connectFn =\n 'connect' in connectHookOutput && typeof connectHookOutput.connect === 'function'\n ? (connectHookOutput.connect as (args?: { connector?: Connector }) => void)\n : defaultConnectStatus.connect;\n\n const conns =\n 'connectors' in connectHookOutput && Array.isArray(connectHookOutput.connectors)\n ? (connectHookOutput.connectors as Connector[])\n : defaultConnectStatus.connectors;\n\n const isPending =\n 'isPending' in connectHookOutput && typeof connectHookOutput.isPending === 'boolean'\n ? connectHookOutput.isPending\n : 'isLoading' in connectHookOutput && typeof connectHookOutput.isLoading === 'boolean'\n ? connectHookOutput.isLoading\n : defaultConnectStatus.isConnecting;\n\n const err =\n 'error' in connectHookOutput && connectHookOutput.error instanceof Error\n ? connectHookOutput.error\n : defaultConnectStatus.error;\n\n const pendingConn =\n 'pendingConnector' in connectHookOutput &&\n typeof connectHookOutput.pendingConnector === 'object' // Assuming Connector is an object\n ? (connectHookOutput.pendingConnector as Connector)\n : defaultConnectStatus.pendingConnector;\n\n return {\n connect: connectFn,\n connectors: conns,\n isConnecting: isPending,\n error: err,\n pendingConnector: pendingConn,\n };\n }\n\n return defaultConnectStatus;\n}\n","import { isRecordWithProperties } from '@openzeppelin/ui-utils';\n\nimport { useWalletState } from './WalletStateContext';\n\nexport interface DerivedDisconnectStatus {\n /** Function to initiate disconnection. Undefined if not available. */\n disconnect?: () => void | Promise<void>; // Can be sync or async\n /** True if a disconnection attempt is in progress (if hook provides this). */\n isDisconnecting: boolean;\n /** Error object if the last disconnection attempt failed (if hook provides this). */\n error: Error | null;\n}\n\nconst defaultDisconnectStatus: DerivedDisconnectStatus = {\n disconnect: undefined,\n isDisconnecting: false,\n error: null,\n};\n\n/**\n * A custom hook that consumes `useWalletState` to get `walletFacadeHooks`,\n * then calls the `useDisconnect` facade hook (if available) and returns a structured,\n * safely-accessed status and control function for wallet disconnection.\n */\nexport function useDerivedDisconnect(): DerivedDisconnectStatus {\n const { walletFacadeHooks } = useWalletState();\n\n const disconnectHookOutput = walletFacadeHooks?.useDisconnect\n ? walletFacadeHooks.useDisconnect()\n : undefined;\n\n if (isRecordWithProperties(disconnectHookOutput)) {\n const disconnectFn =\n 'disconnect' in disconnectHookOutput && typeof disconnectHookOutput.disconnect === 'function'\n ? (disconnectHookOutput.disconnect as () => void | Promise<void>)\n : defaultDisconnectStatus.disconnect;\n\n // wagmi's useDisconnect doesn't have isPending/isLoading directly, but has error and variables (which is the connector it disconnected)\n // We will assume a simple isDisconnecting is not provided by current wagmi facade, but include for future flexibility\n const isPending =\n 'isPending' in disconnectHookOutput && typeof disconnectHookOutput.isPending === 'boolean'\n ? disconnectHookOutput.isPending\n : 'isLoading' in disconnectHookOutput && typeof disconnectHookOutput.isLoading === 'boolean'\n ? disconnectHookOutput.isLoading\n : defaultDisconnectStatus.isDisconnecting;\n\n const err =\n 'error' in disconnectHookOutput && disconnectHookOutput.error instanceof Error\n ? disconnectHookOutput.error\n : defaultDisconnectStatus.error;\n\n return { disconnect: disconnectFn, isDisconnecting: isPending, error: err };\n }\n\n return defaultDisconnectStatus;\n}\n","import { useEffect, useRef } from 'react';\n\nimport type { RuntimeCapability } from '@openzeppelin/ui-types';\nimport { logger } from '@openzeppelin/ui-utils';\n\nimport { useDerivedAccountStatus } from './useDerivedAccountStatus';\n\n/**\n * Hook that detects wallet reconnection and re-queues network switch if needed.\n *\n * When a user disconnects their wallet and then reconnects in the same session,\n * the wallet may connect to a different chain than what's selected in the app.\n * This hook detects that scenario and invokes a callback to re-queue the network switch.\n *\n * @param selectedNetworkConfigId - Currently selected network in the app\n * @param selectedCapability - Currently active wallet capability instance\n * @param networkToSwitchTo - Current network switch queue state (null if no switch pending)\n * @param onRequeueSwitch - Callback invoked when a network switch should be re-queued\n */\nexport function useWalletReconnectionHandler(\n selectedNetworkConfigId: string | null,\n selectedCapability: RuntimeCapability | null,\n networkToSwitchTo: string | null,\n onRequeueSwitch: (networkId: string) => void\n): void {\n const { isConnected, chainId: walletChainId } = useDerivedAccountStatus();\n const prevConnectedRef = useRef(isConnected);\n\n useEffect(() => {\n const wasDisconnected = !prevConnectedRef.current;\n const isNowConnected = isConnected;\n const isReconnection = wasDisconnected && isNowConnected;\n\n // Update ref for next render\n prevConnectedRef.current = isConnected;\n\n if (!isReconnection || !selectedNetworkConfigId || !selectedCapability) {\n return;\n }\n\n // Skip if already queued\n if (networkToSwitchTo === selectedNetworkConfigId) {\n return;\n }\n\n // Check if the selected capability config has chainId (only EVM chains support network switching)\n const selectedNetworkConfig = selectedCapability.networkConfig;\n if (!('chainId' in selectedNetworkConfig) || !walletChainId) {\n return;\n }\n\n const targetChainId = Number(selectedNetworkConfig.chainId);\n if (walletChainId !== targetChainId) {\n logger.info(\n 'useWalletReconnectionHandler',\n `Wallet reconnected with chain ${walletChainId}, but selected network requires ${targetChainId}. Re-queueing switch.`\n );\n onRequeueSwitch(selectedNetworkConfigId);\n }\n }, [\n isConnected,\n walletChainId,\n selectedNetworkConfigId,\n selectedCapability,\n networkToSwitchTo,\n onRequeueSwitch,\n ]);\n}\n","import { QueryClient } from '@tanstack/react-query';\n\nimport type { EcosystemRuntime, NameResolutionError } from '@openzeppelin/ui-types';\n\n/**\n * Cache namespace for a resolution query key. Keeps a forward (name → address)\n * lookup and a reverse (address → name) lookup in disjoint cache entries even\n * when the raw input strings collide. See {@link buildResolutionKey}.\n */\nexport type ResolutionNamespace = 'name' | 'addr';\n\n/**\n * Tunable knobs for the resolution hooks, overridable per-app via\n * `NameResolutionProvider`'s `config` prop and merged over {@link DEFAULT_CONFIG}.\n */\nexport interface ResolutionConfig {\n /** Time a resolved value is considered fresh (no refetch). Maps to react-query `staleTime`. */\n readonly staleTimeMs: number;\n /** Time an unobserved cache entry is retained before GC. Maps to react-query `gcTime`. */\n readonly gcTimeMs: number;\n /** Debounce window (ms) for the forward hook before a settled input becomes the query key. */\n readonly forwardDebounceMs: number;\n /** Debounce window (ms) for the reverse hook. Addresses are pasted/stable, so this defaults to 0. */\n readonly reverseDebounceMs: number;\n /** Max retries for TRANSIENT errors. Definitive negatives are never retried. */\n readonly transientRetryCount: number;\n}\n\n/**\n * Default resolution config. Values sit within the spec's \"seconds-to-minutes\"\n * caching guidance; forward debounce is 300ms (typed char-by-char), reverse is 0.\n */\nexport const DEFAULT_CONFIG: ResolutionConfig = {\n staleTimeMs: 60_000,\n gcTimeMs: 300_000,\n forwardDebounceMs: 300,\n reverseDebounceMs: 0,\n transientRetryCount: 2,\n};\n\n/** Stable prefix for every resolution cache key — namespaces this package's keys. */\nexport const RESOLUTION_KEY_PREFIX = 'oz-name-resolution' as const;\n\n/** `0` when no runtime is mounted; positive ids are assigned per {@link EcosystemRuntime} instance. */\nexport type RuntimeInstanceId = number;\n\nconst runtimeInstanceIds = new WeakMap<object, RuntimeInstanceId>();\nlet nextRuntimeInstanceId = 1;\n\n/**\n * Assign a stable, serializable id to each {@link EcosystemRuntime} object identity.\n * A disposed-and-recreated runtime (e.g. SF-4 opt-in toggle after INV-218 flush) gets\n * a new id so resolution cache keys miss and results refresh (INV-230).\n */\nexport function getRuntimeInstanceId(\n runtime: EcosystemRuntime | null | undefined\n): RuntimeInstanceId {\n if (!runtime) {\n return 0;\n }\n let id = runtimeInstanceIds.get(runtime);\n if (id === undefined) {\n id = nextRuntimeInstanceId;\n nextRuntimeInstanceId += 1;\n runtimeInstanceIds.set(runtime, id);\n }\n return id;\n}\n\n/**\n * Build a network-scoped, namespace-separated cache key (INV-40). A name resolves\n * differently per network and provenance can be chain-scoped, so `networkId` is\n * part of the key; `namespace` keeps forward and reverse lookups disjoint;\n * `runtimeInstanceId` scopes entries to the active capability instance (INV-230).\n *\n * @param namespace - `'name'` (forward) or `'addr'` (reverse).\n * @param networkId - Active network id (`''` when no network is selected).\n * @param normalizedInput - Trimmed + lowercased input (INV-26).\n * @param runtimeInstanceId - Active runtime instance discriminator (`0` when absent).\n * @returns A readonly tuple suitable for a TanStack Query `queryKey`.\n */\nexport function buildResolutionKey(\n namespace: ResolutionNamespace,\n networkId: string,\n normalizedInput: string,\n runtimeInstanceId: RuntimeInstanceId = 0\n): readonly unknown[] {\n return [RESOLUTION_KEY_PREFIX, namespace, networkId, normalizedInput, runtimeInstanceId];\n}\n\n/**\n * Whether a resolution error is transient (worth retrying) versus a definitive,\n * stable answer (INV-47). Exhaustive over SF-1's closed error union with no\n * `default`: adding a new error code to `@openzeppelin/ui-types` will fail to\n * compile here until it is classified — deliberately coupling SF-2 to SF-1 INV-7.\n *\n * @param error - A typed {@link NameResolutionError}.\n * @returns `true` for transient failures (timeout / gateway / adapter), `false`\n * for definitive negatives and unsupported-network answers.\n */\nexport function isTransientError(error: NameResolutionError): boolean {\n switch (error.code) {\n case 'RESOLUTION_TIMEOUT':\n case 'EXTERNAL_GATEWAY_ERROR':\n case 'ADAPTER_ERROR':\n return true;\n case 'NAME_NOT_FOUND':\n case 'ADDRESS_NOT_FOUND':\n case 'UNSUPPORTED_NAME':\n case 'UNSUPPORTED_NETWORK':\n return false;\n }\n}\n\n/**\n * Construct a QueryClient tuned for name resolution: ambient refetch triggers are\n * disabled at the client level (INV-41) so a resolved value never silently\n * changes under the user on window focus or network reconnect.\n */\nexport function createResolutionQueryClient(): QueryClient {\n return new QueryClient({\n defaultOptions: {\n queries: {\n refetchOnWindowFocus: false,\n refetchOnReconnect: false,\n },\n },\n });\n}\n\n/**\n * Process-global key for the default resolution QueryClient. Uses `Symbol.for`\n * so every duplicated module instance (Vite pre-bundling, npm-installed adapters)\n * shares ONE client — the same cross-bundle-singleton pattern as\n * `WalletStateContext`. This is what makes the cache shared with zero wiring\n * (SC-001) and enables cross-instance dedup / warm-cache reuse (INV-33 / INV-37 / INV-48).\n */\nconst DEFAULT_CLIENT_KEY = Symbol.for('@openzeppelin/ui-react/NameResolutionQueryClient');\n\ninterface GlobalWithResolutionClient {\n [DEFAULT_CLIENT_KEY]?: QueryClient;\n}\n\n/**\n * Lazily create (exactly once) and return the process-global resolution\n * QueryClient used when no `NameResolutionProvider` is mounted (INV-48).\n *\n * @returns The shared default resolution QueryClient (stable across calls).\n */\nexport function getDefaultResolutionQueryClient(): QueryClient {\n const globalScope = globalThis as GlobalWithResolutionClient;\n if (!globalScope[DEFAULT_CLIENT_KEY]) {\n globalScope[DEFAULT_CLIENT_KEY] = createResolutionQueryClient();\n }\n return globalScope[DEFAULT_CLIENT_KEY];\n}\n","import type { QueryClient } from '@tanstack/react-query';\nimport { createContext, useContext, type Context } from 'react';\n\nimport {\n DEFAULT_CONFIG,\n getDefaultResolutionQueryClient,\n type ResolutionConfig,\n} from './resolutionConfig';\n\n/**\n * Value carried by {@link NameResolutionContext}: the owned QueryClient (passed\n * explicitly to `useQuery`, never via an ambient `QueryClientProvider`) and the\n * merged resolution config.\n */\nexport interface NameResolutionContextValue {\n /** OWNED resolution QueryClient, passed explicitly to `useQuery` (INV-48). */\n readonly queryClient: QueryClient;\n /** Effective config (Provider overrides merged over {@link DEFAULT_CONFIG}). */\n readonly config: ResolutionConfig;\n}\n\n/**\n * Process-global key for the context object. Uses `Symbol.for` so all duplicated\n * module instances share ONE React context — the same cross-bundle-singleton\n * pattern as `WalletStateContext` (bundler pre-bundling / npm-installed adapters).\n */\nconst CONTEXT_KEY = Symbol.for('@openzeppelin/ui-react/NameResolutionContext');\n\ninterface GlobalWithResolutionContext {\n [CONTEXT_KEY]?: Context<NameResolutionContextValue | null>;\n}\n\nfunction getOrCreateSharedContext(): Context<NameResolutionContextValue | null> {\n const globalScope = globalThis as GlobalWithResolutionContext;\n if (!globalScope[CONTEXT_KEY]) {\n globalScope[CONTEXT_KEY] = createContext<NameResolutionContextValue | null>(null);\n }\n return globalScope[CONTEXT_KEY];\n}\n\n/**\n * React context for resolution. `null` default triggers the module-singleton\n * fallback in {@link useNameResolutionContext} — no Provider is required (INV-48).\n */\nexport const NameResolutionContext = getOrCreateSharedContext();\n\n/**\n * Stable fallback value used when no Provider is mounted. Cached per module so the\n * returned reference is referentially stable across renders (INV-38); it wraps the\n * process-global default client, so caches are still shared even across bundles.\n */\nlet fallbackValue: NameResolutionContextValue | undefined;\n\nfunction getFallbackContextValue(): NameResolutionContextValue {\n if (!fallbackValue) {\n fallbackValue = {\n queryClient: getDefaultResolutionQueryClient(),\n config: DEFAULT_CONFIG,\n };\n }\n return fallbackValue;\n}\n\n/**\n * Read the resolution context, falling back to the zero-wiring default (global\n * singleton client + {@link DEFAULT_CONFIG}) when no `NameResolutionProvider` is\n * mounted (INV-48). Never throws for a missing provider.\n *\n * @returns The active {@link NameResolutionContextValue}.\n */\nexport function useNameResolutionContext(): NameResolutionContextValue {\n return useContext(NameResolutionContext) ?? getFallbackContextValue();\n}\n","import type { NameResolutionError, ResolvedAddress, ResolvedName } from '@openzeppelin/ui-types';\n\n/**\n * Lifecycle status shared by both resolution hooks. `debouncing` is distinct from\n * `loading` so an input field can show a subtle \"typing…\" state separate from an\n * in-flight call (the reverse hook omits `debouncing`; see {@link UseResolveAddressResult}).\n */\nexport type NameResolutionStatus = 'idle' | 'debouncing' | 'loading' | 'resolved' | 'error';\n\n/**\n * Forward-resolution (`useResolveName`) result. A discriminated union keyed on\n * `status` so illegal field combinations — e.g. holding both `data` and `error`,\n * or reading `.data` without narrowing — are unrepresentable (INV-23). This is the\n * component-boundary shadow of SC-004: an unresolved name can never be read as a\n * resolved address.\n */\nexport type UseResolveNameResult =\n | { readonly status: 'idle' }\n | { readonly status: 'debouncing'; readonly name: string }\n | { readonly status: 'loading'; readonly name: string }\n | { readonly status: 'resolved'; readonly name: string; readonly data: ResolvedAddress }\n | {\n readonly status: 'error';\n readonly name: string;\n readonly error: NameResolutionError;\n readonly retry: () => void;\n };\n\n/**\n * Reverse-resolution (`useResolveAddress`) result. No `debouncing` arm — reverse\n * debounce defaults to 0 (addresses are pasted, not typed). A caller-supplied\n * non-zero `debounceMs` surfaces as `loading` rather than widening this type.\n */\nexport type UseResolveAddressResult =\n | { readonly status: 'idle' }\n | { readonly status: 'loading'; readonly address: string }\n | { readonly status: 'resolved'; readonly address: string; readonly data: ResolvedName }\n | {\n readonly status: 'error';\n readonly address: string;\n readonly error: NameResolutionError;\n readonly retry: () => void;\n };\n\n/**\n * Internal, direction-agnostic result the shared engine produces. The public\n * hooks remap the generic `input` field to `name` / `address` (INV-24: the remap\n * keys off the debounced input carried here, never the live prop).\n */\nexport type EngineResult<T> =\n | { readonly status: 'idle' }\n | { readonly status: 'debouncing'; readonly input: string }\n | { readonly status: 'loading'; readonly input: string }\n | { readonly status: 'resolved'; readonly input: string; readonly data: T }\n | {\n readonly status: 'error';\n readonly input: string;\n readonly error: NameResolutionError;\n readonly retry: () => void;\n };\n\n/**\n * Internal error used to bridge SF-1's `ok: false` results (and defensively-caught\n * adapter throws) into TanStack Query's thrown-error channel, carrying the typed\n * {@link NameResolutionError} through so the mapping step can surface it unchanged\n * (INV-43). Never leaks past the hook boundary.\n */\nexport class ResolutionQueryError extends Error {\n readonly resolutionError: NameResolutionError;\n\n /** @param resolutionError - The typed error to carry through react-query. */\n constructor(resolutionError: NameResolutionError) {\n super(`name resolution failed: ${resolutionError.code}`);\n this.name = 'ResolutionQueryError';\n this.resolutionError = resolutionError;\n }\n}\n\n/**\n * Convert any value from react-query's error channel into a typed, closed-union\n * {@link NameResolutionError} (INV-43). A {@link ResolutionQueryError} is\n * unwrapped verbatim; anything else (a react-query internal error) is mapped to\n * `ADAPTER_ERROR` so no untyped throw reaches a component.\n *\n * @param error - The raw `query.error` value (`unknown`).\n * @returns A typed error drawn only from SF-1's closed union.\n */\nexport function toNameResolutionError(error: unknown): NameResolutionError {\n if (error instanceof ResolutionQueryError) {\n return error.resolutionError;\n }\n const message = error instanceof Error ? error.message : String(error);\n return { code: 'ADAPTER_ERROR', message, cause: error };\n}\n\n/**\n * Minimal, React-free view of a settled query's state — the subset the mapping\n * step reads. Keeps {@link mapSettledQuery} unit-testable without react-query.\n */\nexport interface MappableQueryState<T> {\n readonly isSuccess: boolean;\n readonly isError: boolean;\n readonly data: T | undefined;\n readonly error: unknown;\n}\n\n/**\n * Map a settled query's state to exactly one non-gate {@link EngineResult} arm\n * (INV-42, INV-44). The `input` echoed here is the debounced value the query was\n * keyed on, so `data`/`error` are always paired with the input that produced them\n * (INV-24). Success without data degrades to `loading` rather than a resolved arm\n * missing its payload.\n *\n * @param input - The debounced, normalized input keying this query.\n * @param query - The settled query state.\n * @param retry - Bound `refetch` for the `error` arm (INV-34).\n * @returns The `resolved`, `error`, or `loading` arm.\n */\nexport function mapSettledQuery<T>(\n input: string,\n query: MappableQueryState<T>,\n retry: () => void\n): EngineResult<T> {\n if (query.isSuccess && query.data !== undefined) {\n return { status: 'resolved', input, data: query.data };\n }\n if (query.isError) {\n return { status: 'error', input, error: toNameResolutionError(query.error), retry };\n }\n return { status: 'loading', input };\n}\n","import { useQuery } from '@tanstack/react-query';\nimport { useContext, useEffect, useRef, useState } from 'react';\n\nimport type { NameResolutionCapability, ResolutionResult } from '@openzeppelin/ui-types';\nimport { logger } from '@openzeppelin/ui-utils';\n\nimport { WalletStateContext } from '../WalletStateContext';\nimport { useNameResolutionContext } from './NameResolutionContext';\nimport {\n buildResolutionKey,\n getRuntimeInstanceId,\n isTransientError,\n type ResolutionNamespace,\n} from './resolutionConfig';\nimport { mapSettledQuery, ResolutionQueryError, type EngineResult } from './resolutionState';\n\nconst LOG_SCOPE = 'useResolutionEngine';\n\n/** No-op retry for synthesized errors that never entered the query path (INV-45). */\nconst NOOP_RETRY = (): void => {};\n\n/**\n * Parameters for {@link useResolutionEngine}. The public hooks bind these to select\n * direction, debounce default, cache namespace, and the capability method.\n */\nexport interface ResolutionEngineParams<T> {\n /** Raw input; `null` / `undefined` / empty / whitespace-only gate to `idle` (INV-27). */\n readonly input: string | null | undefined;\n /** Cache namespace — keeps forward and reverse lookups disjoint (INV-40). */\n readonly namespace: ResolutionNamespace;\n /** Concrete debounce window (ms); `<= 0` disables debouncing (INV-29). */\n readonly debounceMs: number;\n /** Concrete enabled flag; `false` forces `idle` with no resolution (INV-28). */\n readonly enabled: boolean;\n /**\n * Whether to attempt resolution for a given normalized input. Forward binds\n * `cap.isValidName` → `idle` (never `error`) for non-names (INV-32); reverse\n * binds `() => true`.\n */\n readonly shouldAttempt: (cap: NameResolutionCapability, normalizedInput: string) => boolean;\n /** The directional method, or `undefined` when the adapter omits it (→ INV-45). */\n readonly getMethod: (\n cap: NameResolutionCapability\n ) => ((input: string) => Promise<ResolutionResult<T>>) | undefined;\n}\n\n/**\n * Shared engine behind `useResolveName` / `useResolveAddress`. Owns input\n * normalization, debouncing, the `useQuery` wiring (explicit-client form), the\n * capability-absence gate, and the mapping to a direction-agnostic\n * {@link EngineResult}. Not exported from the package.\n */\nexport function useResolutionEngine<T>(params: ResolutionEngineParams<T>): EngineResult<T> {\n const { input, namespace, debounceMs, enabled, shouldAttempt, getMethod } = params;\n\n // Soft read — never throw. Mirrors `useRuntimeNameResolver`: read the context\n // directly (not `useWalletState()`, which throws) so wallet-less consumers\n // degrade to idle / UNSUPPORTED_NETWORK instead of crashing the tree.\n const walletState = useContext(WalletStateContext);\n const activeRuntime = walletState?.activeRuntime ?? null;\n const activeNetworkId = walletState?.activeNetworkId ?? null;\n const isRuntimeLoading = walletState?.isRuntimeLoading ?? false;\n const { queryClient, config } = useNameResolutionContext();\n\n // INV-26: normalize exactly once (trim, then lowercase). The trimmed original-case\n // form is retained for the reverse adapter argument (checksum preservation); the\n // lowercased form is the single value used by the empty gate, the attempt gate,\n // and the cache key.\n const trimmedLive = (input ?? '').trim();\n const normalizedLive = trimmedLive.toLowerCase();\n\n // INV-35 + INV-37: seed the debounced copy to the initial value on mount (a warm\n // cache is not gated behind a debounce window — SC-002), then debounce only\n // subsequent changes; the effect cleanup clears a pending timer on change/unmount.\n const [debouncedTrimmed, setDebouncedTrimmed] = useState(trimmedLive);\n const seededRef = useRef(false);\n\n useEffect(() => {\n if (!seededRef.current) {\n seededRef.current = true;\n return; // mount value already seeded via useState — no timer, no debouncing window\n }\n if (debounceMs <= 0) {\n setDebouncedTrimmed(trimmedLive);\n return;\n }\n const timer = setTimeout(() => setDebouncedTrimmed(trimmedLive), debounceMs);\n return () => clearTimeout(timer);\n }, [trimmedLive, debounceMs]);\n\n const effectiveTrimmed = debounceMs <= 0 ? trimmedLive : debouncedTrimmed;\n const normalizedKey = effectiveTrimmed.toLowerCase();\n\n const capability = activeRuntime?.nameResolution;\n // Canonical selected-network id (INV-40 key scoping, INV-49 UNSUPPORTED_NETWORK payload).\n // `''` when no network is selected — a valid closed-union payload (INV-49).\n const networkId = activeNetworkId ?? '';\n const runtimeInstanceId = getRuntimeInstanceId(activeRuntime);\n const method = capability ? getMethod(capability) : undefined;\n\n const liveAttemptable = capability ? shouldAttempt(capability, normalizedLive) : false;\n const keyAttemptable = capability ? shouldAttempt(capability, normalizedKey) : false;\n\n // The query is enabled only for a settled, attemptable, resolvable input on a\n // capability that exposes the directional method.\n const queryEnabled =\n enabled &&\n trimmedLive !== '' &&\n capability != null &&\n method != null &&\n normalizedKey !== '' &&\n keyAttemptable;\n\n const query = useQuery<T, Error>(\n {\n queryKey: buildResolutionKey(namespace, networkId, normalizedKey, runtimeInstanceId),\n queryFn: async (): Promise<T> => {\n if (!method) {\n // Unreachable: queryEnabled requires a defined method. Guarded (no `!`).\n throw new ResolutionQueryError({\n code: 'ADAPTER_ERROR',\n message: 'resolution method unexpectedly missing',\n });\n }\n // INV-26 (reverse checksum): forward sends the lowercased name; reverse sends\n // the trimmed original-case address while the cache key stays lowercased.\n const adapterArg = namespace === 'addr' ? effectiveTrimmed : normalizedKey;\n\n let result: ResolutionResult<T>;\n try {\n result = await method(adapterArg);\n } catch (cause) {\n // INV-43 backstop: an adapter that throws instead of returning ok:false\n // (SF-1 INV-8 violation). Log once at the throw site, then convert to the\n // closed-union ADAPTER_ERROR so nothing escapes into a render / error boundary.\n logger.warn(LOG_SCOPE, 'resolution method threw instead of returning ok:false', cause);\n throw new ResolutionQueryError({\n code: 'ADAPTER_ERROR',\n message: cause instanceof Error ? cause.message : String(cause),\n cause,\n });\n }\n if (!result.ok) {\n // INV-43: bridge SF-1's ok:false into react-query's thrown-error channel.\n throw new ResolutionQueryError(result.error);\n }\n return result.value;\n },\n enabled: queryEnabled,\n staleTime: config.staleTimeMs, // INV-50 / SC-002\n gcTime: config.gcTimeMs, // INV-39\n // INV-41: resolution never fires from ambient events (belt-and-suspenders with\n // the client-level defaults, in case a consumer injects their own client).\n refetchOnWindowFocus: false,\n refetchOnReconnect: false,\n // INV-47: transient errors retry up to the cap; definitive negatives never retry.\n // failureCount is 0-based at this call site, so `< transientRetryCount` yields\n // exactly `1 + transientRetryCount` attempts.\n retry: (failureCount, error) =>\n (error instanceof ResolutionQueryError ? isTransientError(error.resolutionError) : true) &&\n failureCount < config.transientRetryCount,\n },\n queryClient // INV-48: explicit-client form — no ambient QueryClientProvider required.\n );\n\n // --- Gate → EngineResult (a total function of gate state × query state, INV-31) ---\n\n if (!enabled) {\n return { status: 'idle' }; // INV-28\n }\n if (trimmedLive === '') {\n return { status: 'idle' }; // INV-27\n }\n\n if (capability) {\n if (!liveAttemptable) {\n return { status: 'idle' }; // INV-32: forward non-name → idle, never error\n }\n if (!method) {\n // INV-45: capability present but directional method absent → UNSUPPORTED_NETWORK.\n return unsupportedNetwork(normalizedLive, networkId);\n }\n if (normalizedKey !== normalizedLive) {\n return { status: 'debouncing', input: normalizedLive }; // INV-31: pending debounce window\n }\n } else {\n // INV-46: runtime still loading with a resolvable input → loading, not an error flash.\n if (isRuntimeLoading) {\n return { status: 'loading', input: normalizedLive };\n }\n // INV-45 / INV-49: no runtime (settled) → UNSUPPORTED_NETWORK (networkId may be '').\n return unsupportedNetwork(normalizedLive, networkId);\n }\n\n // Settled and keyed on the debounced input — surface the query's state (INV-42, INV-44).\n const retry = (): void => {\n void query.refetch(); // INV-34: one refetch per call, current input, ignores staleTime\n };\n return mapSettledQuery(normalizedKey, query, retry);\n}\n\n/**\n * Synthesize an `UNSUPPORTED_NETWORK` error arm without touching react-query\n * (INV-45). Drawn from SF-1's closed union so it is indistinguishable from an\n * adapter-returned `UNSUPPORTED_NETWORK`; `networkId` may be `''` (INV-49). Retry\n * is a no-op — there is no query to refetch.\n */\nfunction unsupportedNetwork<T>(input: string, networkId: string): EngineResult<T> {\n return {\n status: 'error',\n input,\n error: { code: 'UNSUPPORTED_NETWORK', networkId },\n retry: NOOP_RETRY,\n };\n}\n","import type { ResolvedAddress } from '@openzeppelin/ui-types';\n\nimport { useNameResolutionContext } from './NameResolutionContext';\nimport { type EngineResult, type UseResolveNameResult } from './resolutionState';\nimport { useResolutionEngine } from './useResolutionEngine';\n\n/** Options for {@link useResolveName}. */\nexport interface UseResolveNameOptions {\n /** Override the forward debounce window (ms). Default: `config.forwardDebounceMs` (300). */\n readonly debounceMs?: number;\n /** When `false`, the hook stays `idle` and issues no resolution. Default `true`. */\n readonly enabled?: boolean;\n}\n\n/**\n * Forward-resolve a name to an address. Soft-reads the active runtime from\n * `WalletStateContext` (never throws when no provider is mounted — degrades to\n * idle / UNSUPPORTED_NETWORK) and calls `runtime.nameResolution?.resolveName`.\n * Debounces `name`, caches per (network, name) via the owned QueryClient, and is\n * protected against out-of-order responses (distinct inputs → distinct query keys).\n *\n * Returns a discriminated union keyed on `status`; it never throws for expected\n * failures (they surface in the `error` arm) and never pairs a name with a\n * different name's resolved address (INV-24).\n *\n * @param name - The name to resolve. `null` / empty / a value failing the\n * adapter's `isValidName` yields `status: 'idle'` (never `error`).\n * @param options - `debounceMs` / `enabled` overrides.\n * @returns The current {@link UseResolveNameResult}.\n */\nexport function useResolveName(\n name: string | null | undefined,\n options?: UseResolveNameOptions\n): UseResolveNameResult {\n const { config } = useNameResolutionContext();\n // INV-29: resolve option defaults at the hook boundary — the engine sees concrete values.\n const debounceMs = options?.debounceMs ?? config.forwardDebounceMs;\n const enabled = options?.enabled ?? true;\n\n const engine = useResolutionEngine<ResolvedAddress>({\n input: name,\n namespace: 'name',\n debounceMs,\n enabled,\n shouldAttempt: (cap, normalized) => cap.isValidName(normalized), // INV-32\n getMethod: (cap) => cap.resolveName,\n });\n\n return toNameResult(engine);\n}\n\n/**\n * Remap the engine's generic `input` to `name` (INV-24: keyed off the debounced\n * input carried in the engine result, not the live prop). Exhaustive over the\n * union so a new status cannot silently fall through (INV-23 / INV-42).\n */\nfunction toNameResult(engine: EngineResult<ResolvedAddress>): UseResolveNameResult {\n switch (engine.status) {\n case 'idle':\n return { status: 'idle' };\n case 'debouncing':\n return { status: 'debouncing', name: engine.input };\n case 'loading':\n return { status: 'loading', name: engine.input };\n case 'resolved':\n return { status: 'resolved', name: engine.input, data: engine.data };\n case 'error':\n return { status: 'error', name: engine.input, error: engine.error, retry: engine.retry };\n }\n}\n","import type { ResolvedName } from '@openzeppelin/ui-types';\n\nimport { useNameResolutionContext } from './NameResolutionContext';\nimport { type EngineResult, type UseResolveAddressResult } from './resolutionState';\nimport { useResolutionEngine } from './useResolutionEngine';\n\n/** Options for {@link useResolveAddress}. */\nexport interface UseResolveAddressOptions {\n /** Override the reverse debounce window (ms). Default: `config.reverseDebounceMs` (0). */\n readonly debounceMs?: number;\n /** When `false`, the hook stays `idle` and issues no resolution. Default `true`. */\n readonly enabled?: boolean;\n}\n\n/**\n * Reverse-resolve an address to a name. Soft-reads the active runtime from\n * `WalletStateContext` (never throws when no provider is mounted — degrades to\n * idle / UNSUPPORTED_NETWORK) and calls `runtime.nameResolution?.resolveAddress`.\n * Caches per (network, address) via the owned QueryClient. Not debounced by\n * default — addresses are pasted, not typed char-by-char.\n *\n * Applies no client-side address-shape check (INV-32): resolution is attempted on\n * any non-empty input and malformed addresses are rejected via the adapter's typed\n * error union. Address-shape validation is SF-3's concern.\n *\n * @param address - The address to reverse-resolve. `null` / empty yields\n * `status: 'idle'`.\n * @param options - `debounceMs` / `enabled` overrides.\n * @returns The current {@link UseResolveAddressResult}.\n */\nexport function useResolveAddress(\n address: string | null | undefined,\n options?: UseResolveAddressOptions\n): UseResolveAddressResult {\n const { config } = useNameResolutionContext();\n // INV-29: resolve option defaults at the hook boundary — the engine sees concrete values.\n const debounceMs = options?.debounceMs ?? config.reverseDebounceMs;\n const enabled = options?.enabled ?? true;\n\n const engine = useResolutionEngine<ResolvedName>({\n input: address,\n namespace: 'addr',\n debounceMs,\n enabled,\n shouldAttempt: () => true, // INV-32: reverse attempts on any non-empty input\n getMethod: (cap) => cap.resolveAddress,\n });\n\n return toAddressResult(engine);\n}\n\n/**\n * Remap the engine's generic `input` to `address` (INV-24). A `debouncing` arm —\n * only reachable when a caller passes a non-zero reverse `debounceMs` — collapses\n * to `loading` so {@link UseResolveAddressResult} keeps no `debouncing` variant.\n */\nfunction toAddressResult(engine: EngineResult<ResolvedName>): UseResolveAddressResult {\n switch (engine.status) {\n case 'idle':\n return { status: 'idle' };\n case 'debouncing':\n case 'loading':\n return { status: 'loading', address: engine.input };\n case 'resolved':\n return { status: 'resolved', address: engine.input, data: engine.data };\n case 'error':\n return { status: 'error', address: engine.input, error: engine.error, retry: engine.retry };\n }\n}\n","import type { QueryClient } from '@tanstack/react-query';\nimport { useMemo, type ReactNode } from 'react';\n\nimport { NameResolutionContext, type NameResolutionContextValue } from './NameResolutionContext';\nimport {\n DEFAULT_CONFIG,\n getDefaultResolutionQueryClient,\n type ResolutionConfig,\n} from './resolutionConfig';\n\n/**\n * Props for {@link NameResolutionProvider}. Mounting the Provider is OPTIONAL —\n * absent it, hooks use the module-singleton client and {@link DEFAULT_CONFIG}\n * (INV-48). Mount it only to override config or share the cache with an existing\n * QueryClient.\n */\nexport interface NameResolutionProviderProps {\n readonly children: ReactNode;\n /** Partial override of the default config (TTLs, debounce, retry count). Merged per-field. */\n readonly config?: Partial<ResolutionConfig>;\n /**\n * Inject a QueryClient to SHARE the resolution cache with the app / adapter\n * client (unified devtools / persistence). Omit for an isolated owned client.\n */\n readonly queryClient?: QueryClient;\n}\n\n/**\n * Optional provider that overrides resolution config and/or the QueryClient for\n * its subtree. It does NOT render a `QueryClientProvider`: the client is passed\n * explicitly to `useQuery` (INV-48), so no ambient react-query provider is needed.\n *\n * Config is merged field-by-field over {@link DEFAULT_CONFIG} (INV-30), and the\n * context value is memoized so a Provider re-render does not cascade to every hook\n * (INV-38).\n */\nexport function NameResolutionProvider({\n children,\n config,\n queryClient,\n}: NameResolutionProviderProps): ReactNode {\n const value = useMemo<NameResolutionContextValue>(\n () => ({\n queryClient: queryClient ?? getDefaultResolutionQueryClient(),\n // INV-30: shallow per-field merge — a partial override never nulls unset knobs.\n config: { ...DEFAULT_CONFIG, ...config },\n }),\n [queryClient, config]\n );\n\n return <NameResolutionContext.Provider value={value}>{children}</NameResolutionContext.Provider>;\n}\n","import { useContext, useMemo } from 'react';\n\nimport type { NameResolver, ResolutionResult, ResolvedAddress } from '@openzeppelin/ui-types';\n\nimport { WalletStateContext } from '../WalletStateContext';\nimport { useNameResolutionContext } from './NameResolutionContext';\nimport { buildResolutionKey, getRuntimeInstanceId, isTransientError } from './resolutionConfig';\nimport { ResolutionQueryError, toNameResolutionError } from './resolutionState';\n\n/**\n * Stable empty resolver returned when no runtime / capability is available.\n * No methods ⇒ the consuming field treats a typed name as unsupported and\n * surfaces `UNSUPPORTED_NETWORK` (INV-119) while hex behavior stays legacy.\n */\nconst EMPTY_RESOLVER: NameResolver = {};\n\n/**\n * Project the active runtime's `NameResolutionCapability` into the injected\n * {@link NameResolver} seam consumed by `@openzeppelin/ui-components` (SF-3, D3).\n * Mounted ambiently by the renderer:\n *\n * ```tsx\n * <NameResolverProvider {...useRuntimeNameResolver()}>{form}</NameResolverProvider>\n * ```\n *\n * Each imperative call is backed by SF-2's **owned** resolution `QueryClient`\n * via `fetchQuery`, reusing SF-2's exact query-key convention\n * (`buildResolutionKey('name', networkId, normalizedName)`) — so a name\n * resolved through the field and the same name resolved by a bare\n * `useResolveName` hit the SAME cache entry: shared cache, dedupe, and\n * out-of-order safety, never a parallel cache (INV-119).\n *\n * Degradation, in order (all method-omission, never a throw):\n * - No `WalletStateProvider` mounted → empty resolver. The context is read\n * directly (not via `useWalletState()`, which throws) so the ambient\n * renderer mount stays safe for wallet-less usage.\n * - No active runtime, or runtime without the capability → empty resolver.\n * - Capability without `resolveName` → `isValidName` only; forward unsupported.\n *\n * When the capability is present but the network itself is unsupported, the\n * adapter's `resolveName` resolves `{ ok: false, error: UNSUPPORTED_NETWORK }`\n * and that result passes through unchanged.\n *\n * @returns A referentially stable {@link NameResolver} for the active runtime.\n */\nexport function useRuntimeNameResolver(): NameResolver {\n const walletState = useContext(WalletStateContext);\n const { queryClient, config } = useNameResolutionContext();\n\n const activeRuntime = walletState?.activeRuntime ?? null;\n const capability = activeRuntime?.nameResolution;\n // Canonical selected-network id — part of the shared cache key (INV-119);\n // '' when no network is selected, mirroring the SF-2 engine.\n const networkId = walletState?.activeNetworkId ?? '';\n const runtimeInstanceId = getRuntimeInstanceId(activeRuntime);\n\n return useMemo<NameResolver>(() => {\n if (!capability) {\n return EMPTY_RESOLVER;\n }\n\n const resolveNameMethod = capability.resolveName;\n\n return {\n isValidName: (name: string): boolean => capability.isValidName(name),\n\n resolveName: resolveNameMethod\n ? async (name: string): Promise<ResolutionResult<ResolvedAddress>> => {\n // Normalize exactly as the SF-2 engine (trim, then lowercase) so the\n // cache key — and the echoed `value.name` — match the hook path (INV-119).\n const normalized = name.trim().toLowerCase();\n try {\n const value = await queryClient.fetchQuery<ResolvedAddress, Error>({\n queryKey: buildResolutionKey('name', networkId, normalized, runtimeInstanceId),\n queryFn: async (): Promise<ResolvedAddress> => {\n // Same ok:false → thrown-error bridge as SF-2's engine, so a\n // field-initiated fetch and a hook-initiated fetch populate the\n // cache identically.\n let result: ResolutionResult<ResolvedAddress>;\n try {\n result = await resolveNameMethod(normalized);\n } catch (cause) {\n // Backstop for an adapter that throws instead of returning\n // ok:false (SF-1 INV-8 violation) — keep the no-throw contract.\n throw new ResolutionQueryError({\n code: 'ADAPTER_ERROR',\n message: cause instanceof Error ? cause.message : String(cause),\n cause,\n });\n }\n if (!result.ok) {\n throw new ResolutionQueryError(result.error);\n }\n return result.value;\n },\n staleTime: config.staleTimeMs,\n gcTime: config.gcTimeMs,\n // Mirror SF-2's retry policy: transient errors up to the cap,\n // definitive negatives never.\n retry: (failureCount, error) =>\n (error instanceof ResolutionQueryError\n ? isTransientError(error.resolutionError)\n : true) && failureCount < config.transientRetryCount,\n });\n return { ok: true, value };\n } catch (error) {\n // Un-bridge back into the seam's no-throw ResolutionResult contract.\n return { ok: false, error: toNameResolutionError(error) };\n }\n }\n : undefined,\n };\n }, [capability, queryClient, config, networkId, runtimeInstanceId]);\n}\n","import type { CreateRuntimeOptions, NameResolutionRuntimeOptions } from '@openzeppelin/ui-types';\n\n/** Safe default: miss-fallback OFF; no uiKit override. INV-204 */\nexport const DEFAULT_RUNTIME_CREATION_CONFIG: CreateRuntimeOptions = {};\n\n/**\n * Normalizes the opt-in flag to the adapter's strict-enable contract.\n * UIKit uses this at the threading boundary so `false` and `undefined` never\n * emit `enableMainnetL1MissFallback: false` into adapter options (absent = OFF).\n */\nexport function isMainnetL1MissFallbackEnabled(options?: NameResolutionRuntimeOptions): boolean {\n return options?.enableMainnetL1MissFallback === true; // INV-206\n}\n\n/**\n * Builds the `createRuntime` options bag for adapter threading.\n * When OFF, `nameResolution` is omitted (INV-207). When ON, spreads only\n * `{ enableMainnetL1MissFallback: true }` (INV-208).\n */\nexport function buildCreateRuntimeOptions(options?: CreateRuntimeOptions): CreateRuntimeOptions {\n return {\n ...(options?.uiKit !== undefined ? { uiKit: options.uiKit } : {}),\n ...(isMainnetL1MissFallbackEnabled(options?.nameResolution)\n ? { nameResolution: { enableMainnetL1MissFallback: true as const } }\n : {}),\n };\n}\n","import type {\n CreateRuntimeOptions,\n EcosystemExport,\n EcosystemRuntime,\n NetworkConfig,\n ProfileName,\n} from '@openzeppelin/ui-types';\n\nimport { buildCreateRuntimeOptions } from './runtimeCreationConfig';\n\nexport interface ResolveRuntimeParams {\n readonly profile: ProfileName;\n /** Merged over {@link DEFAULT_RUNTIME_CREATION_CONFIG} per field. */\n readonly options?: CreateRuntimeOptions;\n}\n\nexport type ResolveRuntimeFn = (networkConfig: NetworkConfig) => Promise<EcosystemRuntime>;\n\n/**\n * Build a `resolveRuntime` callback for {@link RuntimeProvider} that forwards\n * `CreateRuntimeOptions` to `ecosystemDefinition.createRuntime`.\n *\n * Does not cache — {@link RuntimeProvider} owns the per-network singleton registry.\n */\nexport function createResolveRuntime(\n ecosystemDefinition: EcosystemExport,\n params: ResolveRuntimeParams\n): ResolveRuntimeFn {\n const { profile, options } = params;\n const mergedOptions = buildCreateRuntimeOptions(options);\n\n return (networkConfig: NetworkConfig) =>\n Promise.resolve(ecosystemDefinition.createRuntime(profile, networkConfig, mergedOptions));\n}\n","import { useMemo } from 'react';\n\nimport type { EcosystemExport } from '@openzeppelin/ui-types';\n\nimport {\n createResolveRuntime,\n type ResolveRuntimeFn,\n type ResolveRuntimeParams,\n} from './createResolveRuntime';\n\n/**\n * Memoized {@link createResolveRuntime} for React apps. Recompute when\n * `ecosystemDefinition`, `profile`, or opt-in posture change (INV-217, INV-222).\n *\n * Changing the returned function identity MUST trigger runtime registry flush\n * in {@link RuntimeProvider} (INV-218).\n */\nexport function useResolveRuntime(\n ecosystemDefinition: EcosystemExport,\n params: ResolveRuntimeParams\n): ResolveRuntimeFn {\n const { profile, options } = params;\n const uiKit = options?.uiKit;\n const enableMainnetL1MissFallback = options?.nameResolution?.enableMainnetL1MissFallback;\n\n return useMemo(\n () =>\n createResolveRuntime(ecosystemDefinition, {\n profile,\n options: {\n ...(uiKit !== undefined ? { uiKit } : {}),\n ...(enableMainnetL1MissFallback === true\n ? { nameResolution: { enableMainnetL1MissFallback: true } }\n : {}),\n },\n }),\n [ecosystemDefinition, profile, uiKit, enableMainnetL1MissFallback]\n );\n}\n","import React, { useState } from 'react';\n\nimport { Button } from '@openzeppelin/ui-components';\nimport type { BaseComponentProps } from '@openzeppelin/ui-types';\nimport { cn, logger } from '@openzeppelin/ui-utils';\n\nimport { useWalletState } from '../hooks/WalletStateContext';\n\n/**\n * Props for the WalletConnectionUI component.\n */\nexport interface WalletConnectionUIProps {\n /** Additional CSS classes to apply to the wrapper container */\n className?: string;\n /** Props forwarded to the ConnectButton component */\n connectButtonProps?: BaseComponentProps;\n /** Props forwarded to the AccountDisplay component */\n accountDisplayProps?: BaseComponentProps;\n /** Props forwarded to the NetworkSwitcher component */\n networkSwitcherProps?: BaseComponentProps;\n}\n\n/**\n * Component that displays wallet connection UI components\n * provided by the active adapter.\n *\n * @example\n * ```tsx\n * // Basic usage\n * <WalletConnectionUI />\n *\n * // With custom styling for the connect button\n * <WalletConnectionUI\n * connectButtonProps={{ size: \"lg\", variant: \"outline\", fullWidth: true }}\n * />\n *\n * // Customizing all components\n * <WalletConnectionUI\n * connectButtonProps={{ size: \"lg\" }}\n * accountDisplayProps={{ size: \"lg\" }}\n * networkSwitcherProps={{ size: \"lg\" }}\n * />\n * ```\n */\nexport const WalletConnectionUI: React.FC<WalletConnectionUIProps> = ({\n className,\n connectButtonProps,\n accountDisplayProps,\n networkSwitcherProps,\n}) => {\n const [isError, setIsError] = useState(false);\n const { activeNetworkConfig, activeRuntime, isRuntimeLoading } = useWalletState();\n const activeUiKit = activeRuntime?.uiKit;\n const isCrossEcosystemTransition = !!(\n isRuntimeLoading &&\n activeRuntime?.networkConfig?.ecosystem &&\n activeNetworkConfig?.ecosystem &&\n activeRuntime.networkConfig.ecosystem !== activeNetworkConfig.ecosystem\n );\n\n if (isCrossEcosystemTransition) {\n return null;\n }\n\n const walletComponents = (() => {\n if (!activeUiKit || typeof activeUiKit.getEcosystemWalletComponents !== 'function') {\n return null;\n }\n\n try {\n return activeUiKit.getEcosystemWalletComponents();\n } catch (error) {\n logger.error('WalletConnectionUI', 'Error getting wallet components:', error);\n setIsError(true);\n return null;\n }\n })();\n\n if (!walletComponents) {\n return null;\n }\n\n const { ConnectButton, AccountDisplay, NetworkSwitcher } = walletComponents;\n\n if (isError) {\n return (\n <div className={cn('flex items-center gap-4', className)}>\n <Button variant=\"destructive\" size=\"sm\" onClick={() => window.location.reload()}>\n Wallet Error - Retry\n </Button>\n </div>\n );\n }\n\n return (\n <div className={cn('flex items-center gap-4', className)}>\n {NetworkSwitcher && <NetworkSwitcher {...networkSwitcherProps} />}\n {AccountDisplay && <AccountDisplay {...accountDisplayProps} />}\n {ConnectButton && <ConnectButton {...connectButtonProps} />}\n </div>\n );\n};\n","import React from 'react';\n\nimport { useWalletState } from '../hooks/WalletStateContext';\nimport { WalletConnectionUI } from './WalletConnectionUI';\n\n/**\n * Component that renders the wallet connection UI.\n * Uses useWalletState to get its data.\n */\nexport const WalletConnectionHeader: React.FC = () => {\n const { isRuntimeLoading } = useWalletState();\n\n if (isRuntimeLoading) {\n return <div className=\"h-9 w-28 animate-pulse rounded bg-muted\"></div>;\n }\n\n return <WalletConnectionUI />;\n};\n","import React, { useEffect, useRef, useState } from 'react';\n\nimport { NetworkCatalogCapability, WalletCapability } from '@openzeppelin/ui-types';\nimport { logger } from '@openzeppelin/ui-utils';\n\nimport { useDerivedAccountStatus } from '../hooks/useDerivedAccountStatus';\nimport { useDerivedSwitchChainStatus } from '../hooks/useDerivedSwitchChainStatus';\n\n/**\n * Props for the NetworkSwitchManager component.\n */\nexport interface NetworkSwitchManagerProps {\n /** Wallet capability for the target network */\n wallet: WalletCapability;\n /** Network catalog capability used to validate the target network id */\n networkCatalog: NetworkCatalogCapability;\n /** The network ID we want to switch to */\n targetNetworkId: string;\n /** Callback when network switch completes (success or error) */\n onNetworkSwitchComplete?: () => void;\n}\n\n/**\n * Component that handles wallet network switching based on the selected network.\n *\n * This component manages the lifecycle of network switching operations,\n * coordinating between the wallet's current chain state and the target network.\n * It's designed to be used in any application that needs seamless wallet network switching.\n *\n * Features:\n * - Automatically initiates network switch when mounted with a target network\n * - Handles EVM chain switching gracefully\n * - No-ops for non-EVM networks that don't support chain switching\n * - Tracks switch attempts to prevent duplicate operations\n * - Provides completion callback for parent components to handle state cleanup\n */\nexport const NetworkSwitchManager: React.FC<NetworkSwitchManagerProps> = ({\n wallet,\n networkCatalog,\n targetNetworkId,\n onNetworkSwitchComplete,\n}) => {\n const isMountedRef = useRef(true);\n const [hasAttemptedSwitch, setHasAttemptedSwitch] = useState(false);\n\n const { isConnected, chainId: currentChainIdFromHook } = useDerivedAccountStatus();\n const {\n switchChain: execSwitchNetwork,\n isSwitching: isSwitchingNetworkViaHook,\n error: switchNetworkError,\n } = useDerivedSwitchChainStatus();\n\n useEffect(() => {\n isMountedRef.current = true;\n logger.info(\n 'NetworkSwitchManager',\n `Mounted with target: ${targetNetworkId}, current attempt status: ${hasAttemptedSwitch}`\n );\n setHasAttemptedSwitch(false);\n return () => {\n logger.info('NetworkSwitchManager', `Unmounting, was for target: ${targetNetworkId}`);\n isMountedRef.current = false;\n };\n // hasAttemptedSwitch is intentionally omitted from deps: we only want to reset when targetNetworkId changes,\n // not when hasAttemptedSwitch itself changes (which would cause unnecessary re-renders)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [targetNetworkId]);\n\n useEffect(() => {\n logger.info('NetworkSwitchManager', 'State Update:', {\n target: targetNetworkId,\n walletNetwork: wallet.networkConfig.id,\n isSwitching: isSwitchingNetworkViaHook,\n hookError: !!switchNetworkError,\n canExec: !!execSwitchNetwork,\n connected: isConnected,\n walletChain: currentChainIdFromHook,\n attempted: hasAttemptedSwitch,\n });\n }, [\n wallet,\n targetNetworkId,\n isSwitchingNetworkViaHook,\n switchNetworkError,\n execSwitchNetwork,\n isConnected,\n currentChainIdFromHook,\n hasAttemptedSwitch,\n ]);\n\n // Main Orchestration & Pre-flight Effect\n useEffect(() => {\n const completeOperation = (\n logMessage?: string,\n options: { notifyComplete?: boolean } = { notifyComplete: true }\n ) => {\n if (logMessage) logger.info('NetworkSwitchManager', logMessage);\n if (options.notifyComplete && isMountedRef.current && onNetworkSwitchComplete)\n onNetworkSwitchComplete();\n if (isMountedRef.current) setHasAttemptedSwitch(false);\n };\n\n if (!execSwitchNetwork) {\n completeOperation('No switchChain function available from hook. Operation halted.', {\n notifyComplete: false,\n });\n return;\n }\n\n if (isSwitchingNetworkViaHook && hasAttemptedSwitch) {\n // If hook is pending AND we initiated this attempt, let completion effect handle it.\n logger.info(\n 'NetworkSwitchManager',\n 'Hook reports switch in progress for current attempt. Waiting...'\n );\n return;\n }\n\n // If hasAttemptedSwitch is true here, but hook is NOT pending, means it completed/errored very fast.\n // The completion effect should pick this up.\n if (hasAttemptedSwitch && !isSwitchingNetworkViaHook) {\n logger.info(\n 'NetworkSwitchManager',\n 'Previous switch attempt concluded. Deferring to completion effect.'\n );\n return;\n }\n\n // If we are here, no attempt has been made for the current targetNetworkId yet, or a previous attempt on a *different* target completed.\n // OR, the hook is not pending from a previous unrelated call.\n // Reset attempt flag for a fresh try if it was from a different context/target.\n // Note: setHasAttemptedSwitch(false) is in mount effect for targetNetworkId change.\n\n // === Pre-flight checks for the current targetNetworkId ===\n const targetNetwork = networkCatalog\n .getNetworks()\n .find((network) => network.id === targetNetworkId);\n if (!targetNetwork) {\n completeOperation(\n `Target network ${targetNetworkId} is not present in the network catalog.`,\n {\n notifyComplete: false,\n }\n );\n return;\n }\n if (wallet.networkConfig.id !== targetNetworkId) {\n completeOperation(\n `CRITICAL: Wallet capability (${wallet.networkConfig.id}) vs Target (${targetNetworkId}) mismatch. Operation halted.`,\n {\n notifyComplete: false,\n }\n );\n return;\n }\n if (!isConnected) {\n completeOperation('Wallet not connected (derived status). Awaiting connection.', {\n notifyComplete: false,\n });\n return;\n }\n if (!('chainId' in wallet.networkConfig)) {\n completeOperation(\n 'Network does not support chain switching (non-EVM). Operation complete (no-op).'\n );\n return;\n }\n const targetChainToBeSwitchedTo = Number(wallet.networkConfig.chainId);\n if (currentChainIdFromHook === targetChainToBeSwitchedTo) {\n completeOperation('Already on correct chain (derived status). Operation complete.');\n return;\n }\n\n const performSwitchActual = () => {\n if (!isMountedRef.current || isSwitchingNetworkViaHook || hasAttemptedSwitch) {\n // If hook became pending, or an attempt was already made and concluded, don't re-issue.\n logger.info(\n 'NetworkSwitchManager',\n `Switch attempt aborted in timeout or already handled. Conditions: isSwitching: ${isSwitchingNetworkViaHook}, hasAttempted: ${hasAttemptedSwitch}`\n );\n return;\n }\n logger.info(\n 'NetworkSwitchManager',\n `Attempting switch to ${targetChainToBeSwitchedTo} via derived hook.`\n );\n setHasAttemptedSwitch(true); // Mark that this specific attempt for this target is now starting\n execSwitchNetwork({ chainId: targetChainToBeSwitchedTo });\n };\n\n const timeoutId = setTimeout(performSwitchActual, 100);\n return () => clearTimeout(timeoutId);\n }, [\n wallet,\n networkCatalog,\n targetNetworkId,\n execSwitchNetwork,\n isSwitchingNetworkViaHook,\n onNetworkSwitchComplete,\n isConnected,\n currentChainIdFromHook,\n hasAttemptedSwitch,\n ]);\n\n // Completion/Error Effect (handles outcomes of an initiated execSwitchNetwork call)\n useEffect(() => {\n if (!isMountedRef.current || !execSwitchNetwork || !hasAttemptedSwitch) return;\n\n // Only act if the hook is NOT pending AND an attempt was made\n if (!isSwitchingNetworkViaHook) {\n let completionMessage = 'Switch hook operation concluded.';\n if (switchNetworkError) {\n logger.error('NetworkSwitchManager', 'Error from derived switch hook:', switchNetworkError);\n completionMessage = 'Switch hook completed with error.';\n } else {\n logger.info('NetworkSwitchManager', 'Derived switch hook completed successfully.');\n }\n if (onNetworkSwitchComplete) onNetworkSwitchComplete();\n if (isMountedRef.current) setHasAttemptedSwitch(false);\n logger.info('NetworkSwitchManager', completionMessage);\n }\n }, [\n isSwitchingNetworkViaHook,\n switchNetworkError,\n execSwitchNetwork,\n hasAttemptedSwitch,\n onNetworkSwitchComplete,\n ]);\n\n return null;\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["getOrCreateSharedContext","EMPTY_RESOLVER"],"sources":["../src/version.ts","../src/hooks/AdapterContext.tsx","../src/hooks/AdapterProvider.tsx","../src/hooks/WalletStateContext.ts","../src/hooks/useAdapterContext.ts","../src/hooks/walletSessionRegistry.ts","../src/hooks/WalletStateProvider.tsx","../src/hooks/AnalyticsContext.tsx","../src/hooks/AnalyticsProvider.tsx","../src/hooks/useAnalytics.ts","../src/hooks/useWalletComponents.ts","../src/hooks/useDerivedAccountStatus.ts","../src/hooks/useDerivedSwitchChainStatus.ts","../src/hooks/useDerivedChainInfo.ts","../src/hooks/useDerivedConnectStatus.ts","../src/hooks/useDerivedDisconnect.ts","../src/hooks/useWalletReconnectionHandler.ts","../src/hooks/nameResolution/resolutionConfig.ts","../src/hooks/nameResolution/NameResolutionContext.ts","../src/hooks/nameResolution/resolutionState.ts","../src/hooks/nameResolution/useResolutionEngine.ts","../src/hooks/nameResolution/useResolveName.ts","../src/hooks/nameResolution/mapAddressEngineResult.ts","../src/hooks/nameResolution/useNetworkRuntimeSource.ts","../src/hooks/nameResolution/useResolveAddress.ts","../src/hooks/nameResolution/NameResolutionProvider.tsx","../src/hooks/nameResolution/createNameResolver.ts","../src/hooks/nameResolution/useRuntimeNameResolver.ts","../src/hooks/nameResolution/useNetworkNameResolver.ts","../src/hooks/nameResolution/useNetworkResolveAddress.ts","../src/hooks/runtime/runtimeCreationConfig.ts","../src/hooks/runtime/createResolveRuntime.ts","../src/hooks/runtime/useResolveRuntime.ts","../src/components/WalletConnectionUI.tsx","../src/components/WalletConnectionHeader.tsx","../src/components/NetworkSwitchManager.tsx"],"sourcesContent":["declare const __PACKAGE_VERSION__: string;\nexport const VERSION: string = __PACKAGE_VERSION__;\n","/**\n * AdapterContext.tsx\n *\n * This file defines the React Context used for the runtime singleton pattern.\n * It provides types and the context definition, but the actual implementation\n * is in the `RuntimeProvider` component.\n *\n * The runtime singleton pattern ensures that only one runtime instance exists\n * per network configuration, which is critical for consistent wallet connection\n * state across the application.\n */\nimport { createContext } from 'react';\n\nimport type { EcosystemRuntime, NetworkConfig } from '@openzeppelin/ui-types';\n\n/**\n * Registry type that maps network IDs to their corresponding runtime instances.\n */\nexport interface RuntimeRegistry {\n [networkId: string]: EcosystemRuntime;\n}\n\n/**\n * Context value interface defining what's provided through the context.\n * The main functionality is `getRuntimeForNetwork`, which either returns an existing\n * runtime or initiates loading of a new one.\n */\nexport interface RuntimeContextValue {\n getRuntimeForNetwork: (networkConfig: NetworkConfig | null) => {\n runtime: EcosystemRuntime | null;\n isLoading: boolean;\n };\n /**\n * Evicts a runtime from the registry and calls `dispose()` on it.\n * Used by WalletStateProvider to release superseded runtimes after a safe handoff.\n */\n releaseRuntime: (networkId: string) => void;\n}\n\n/**\n * The React Context that provides runtime registry access throughout the app.\n * Components can access this through the `useRuntimeContext` hook.\n */\nexport const RuntimeContext = createContext<RuntimeContextValue | null>(null);\n","/**\n * RuntimeProvider.tsx\n *\n * This file implements the Runtime Provider component which manages a registry of\n * runtime instances. It's a key part of the runtime singleton pattern which ensures\n * that only one runtime instance exists per network configuration.\n *\n * The runtime registry is shared across the application via React Context, allowing\n * components to access the same runtime instances and maintain consistent wallet\n * connection state.\n *\n * IMPORTANT: This implementation needs special care to avoid React state update errors\n * during component rendering. Direct state updates during render are not allowed, which\n * is why runtime loading is controlled carefully.\n */\nimport { ReactNode, useCallback, useEffect, useMemo, useRef, useState } from 'react';\n\nimport type { EcosystemRuntime, NetworkConfig } from '@openzeppelin/ui-types';\nimport { logger } from '@openzeppelin/ui-utils';\n\nimport { RuntimeContext, RuntimeContextValue, RuntimeRegistry } from './AdapterContext';\n\nexport interface RuntimeProviderProps {\n children: ReactNode;\n /** Function to resolve/create a runtime instance for a given NetworkConfig. */\n resolveRuntime: (networkConfig: NetworkConfig) => Promise<EcosystemRuntime>;\n}\n\n/**\n * Provider component that manages runtime instances centrally\n * to avoid creating multiple instances of the same runtime.\n *\n * This component:\n * 1. Maintains a registry of runtime instances by network ID\n * 2. Tracks loading states for runtimes being initialized\n * 3. Provides a function to get or load runtimes for specific networks\n * 4. Ensures runtime instances are reused when possible\n */\nexport function RuntimeProvider({ children, resolveRuntime }: RuntimeProviderProps) {\n // Registry to store runtime instances by network ID\n const [runtimeRegistry, setRuntimeRegistry] = useState<RuntimeRegistry>({});\n\n // Track loading states by network ID\n const [loadingNetworks, setLoadingNetworks] = useState<Set<string>>(new Set());\n const runtimeRegistryRef = useRef(runtimeRegistry);\n\n // Track networks whose runtime failed to load so we don't retry infinitely.\n // Cleared when resolveRuntime changes (e.g. the host app fixes the issue).\n const failedNetworksRef = useRef<Set<string>>(new Set());\n\n // Bumped when resolveRuntime identity changes so in-flight resolves from a\n // prior resolver cannot repopulate the registry with stale runtimes.\n const resolverGenerationRef = useRef(0);\n const resolverInitializedRef = useRef(false);\n\n useEffect(() => {\n runtimeRegistryRef.current = runtimeRegistry;\n }, [runtimeRegistry]);\n\n // Reset failure tracking and evict all cached runtimes when the resolver\n // function changes (e.g. opt-in toggle). INV-218: full registry disposal —\n // not only failedNetworksRef — so ctor-frozen capabilities cannot go stale.\n useEffect(() => {\n if (resolverInitializedRef.current) {\n resolverGenerationRef.current += 1;\n } else {\n resolverInitializedRef.current = true;\n }\n\n const registry = runtimeRegistryRef.current;\n const runtimes = Object.values(registry);\n const hadCachedRuntimes = runtimes.length > 0;\n\n failedNetworksRef.current = new Set();\n setLoadingNetworks(new Set());\n\n if (hadCachedRuntimes) {\n setRuntimeRegistry({});\n\n // INV-223: deferred disposal — same macrotask discipline as releaseRuntime.\n setTimeout(() => {\n runtimes.forEach((runtime) => {\n try {\n runtime.dispose();\n } catch (error) {\n logger.error(\n 'RuntimeProvider',\n 'Error disposing runtime during registry flush:',\n error\n );\n }\n });\n }, 0);\n }\n }, [resolveRuntime]);\n\n useEffect(() => {\n return () => {\n Object.values(runtimeRegistryRef.current).forEach((runtime) => {\n runtime.dispose();\n });\n };\n }, []);\n\n // Log registry status on changes\n useEffect(() => {\n const runtimeCount = Object.keys(runtimeRegistry).length;\n if (runtimeCount > 0) {\n logger.info('RuntimeProvider', `Registry contains ${runtimeCount} runtimes:`, {\n networkIds: Object.keys(runtimeRegistry),\n loadingCount: loadingNetworks.size,\n loadingNetworkIds: Array.from(loadingNetworks),\n });\n }\n }, [runtimeRegistry, loadingNetworks]);\n\n /**\n * Evicts a runtime from the registry by network ID and disposes it.\n * This is the safe counterpart to `getRuntimeForNetwork`: callers should only\n * release a runtime after they have already promoted its replacement.\n *\n * Disposal is deferred to the next macrotask so React's commit phase\n * (including development-mode prop diffing) can finish reading the old\n * runtime's properties without hitting the RuntimeDisposedError proxy trap.\n */\n const releaseRuntime = useCallback((networkId: string) => {\n const runtime = runtimeRegistryRef.current[networkId];\n if (!runtime) {\n return;\n }\n\n logger.info('RuntimeProvider', `Releasing runtime for network ${networkId}`);\n\n setRuntimeRegistry((prev) => {\n const next = { ...prev };\n delete next[networkId];\n return next;\n });\n\n setTimeout(() => {\n try {\n runtime.dispose();\n } catch (error) {\n logger.error('RuntimeProvider', `Error disposing runtime for network ${networkId}:`, error);\n }\n }, 0);\n }, []);\n\n /**\n * Function to get or create a runtime for a network\n *\n * IMPORTANT: Runtime loading is coordinated carefully to avoid React state updates\n * during render, which would cause errors.\n *\n * This function:\n * 1. Returns existing runtimes immediately if available\n * 2. Reports loading state for runtimes being initialized\n * 3. Initiates runtime loading when needed\n */\n const getRuntimeForNetwork = useCallback(\n (networkConfig: NetworkConfig | null) => {\n if (!networkConfig) {\n return { runtime: null, isLoading: false };\n }\n\n const networkId = networkConfig.id;\n\n // Debug log to track runtime requests\n logger.debug('RuntimeProvider', `Runtime requested for network ${networkId}`);\n\n // If we already have this runtime, return it\n if (runtimeRegistry[networkId]) {\n logger.debug('RuntimeProvider', `Using existing runtime for network ${networkId}`);\n return {\n runtime: runtimeRegistry[networkId],\n isLoading: false,\n };\n }\n\n // If we're already loading this runtime, indicate loading\n if (loadingNetworks.has(networkId)) {\n logger.debug('RuntimeProvider', `Runtime for network ${networkId} is currently loading`);\n return {\n runtime: null,\n isLoading: true,\n };\n }\n\n // If this network previously failed to load, don't retry.\n if (failedNetworksRef.current.has(networkId)) {\n logger.debug(\n 'RuntimeProvider',\n `Runtime for network ${networkId} previously failed; skipping retry`\n );\n return {\n runtime: null,\n isLoading: false,\n };\n }\n\n // Start loading the runtime.\n setLoadingNetworks((prev) => {\n const newSet = new Set(prev);\n newSet.add(networkId);\n return newSet;\n });\n\n logger.info(\n 'RuntimeProvider',\n `Starting runtime initialization for network ${networkId} (${networkConfig.name})`\n );\n\n const generation = resolverGenerationRef.current;\n\n // Use the passed-in resolveRuntime function\n void resolveRuntime(networkConfig)\n .then((runtime) => {\n if (generation !== resolverGenerationRef.current) {\n // Stale resolver — do not add to registry; dispose the orphan.\n setTimeout(() => {\n try {\n runtime.dispose();\n } catch (error) {\n logger.error(\n 'RuntimeProvider',\n `Error disposing stale runtime for network ${networkId}:`,\n error\n );\n }\n }, 0);\n\n setLoadingNetworks((prev) => {\n const newSet = new Set(prev);\n newSet.delete(networkId);\n return newSet;\n });\n return;\n }\n\n logger.info('RuntimeProvider', `Runtime for network ${networkId} loaded successfully`, {\n type: runtime.constructor.name,\n objectId: Object.prototype.toString.call(runtime),\n });\n\n // Update registry with new runtime\n setRuntimeRegistry((prev) => ({\n ...prev,\n [networkId]: runtime,\n }));\n\n // Remove from loading networks\n setLoadingNetworks((prev) => {\n const newSet = new Set(prev);\n newSet.delete(networkId);\n return newSet;\n });\n })\n .catch((error) => {\n if (generation !== resolverGenerationRef.current) {\n setLoadingNetworks((prev) => {\n const newSet = new Set(prev);\n newSet.delete(networkId);\n return newSet;\n });\n return;\n }\n\n logger.error('RuntimeProvider', `Error loading runtime for network ${networkId}:`, error);\n\n failedNetworksRef.current.add(networkId);\n\n setLoadingNetworks((prev) => {\n const newSet = new Set(prev);\n newSet.delete(networkId);\n return newSet;\n });\n });\n\n return {\n runtime: null,\n isLoading: true,\n };\n },\n [runtimeRegistry, loadingNetworks, resolveRuntime]\n );\n\n // Memoize context value to prevent unnecessary re-renders\n const contextValue = useMemo<RuntimeContextValue>(\n () => ({\n getRuntimeForNetwork,\n releaseRuntime,\n }),\n [getRuntimeForNetwork, releaseRuntime]\n );\n\n return <RuntimeContext.Provider value={contextValue}>{children}</RuntimeContext.Provider>;\n}\n","import React, { createContext } from 'react';\n\nimport type {\n EcosystemRuntime,\n EcosystemSpecificReactHooks,\n NetworkConfig,\n UiKitConfiguration,\n} from '@openzeppelin/ui-types';\n\nexport interface WalletStateContextValue {\n // Globally selected network state\n activeNetworkId: string | null;\n setActiveNetworkId: (networkId: string | null) => void;\n activeNetworkConfig: NetworkConfig | null;\n\n // Active runtime state\n activeRuntime: EcosystemRuntime | null;\n isRuntimeLoading: boolean;\n\n // Facade hooks object from the active runtime's UI kit\n // Consumers will call these hooks (e.g., walletFacadeHooks.useAccount())\n walletFacadeHooks: EcosystemSpecificReactHooks | null;\n reconfigureActiveUiKit: (uiKitConfig?: Partial<UiKitConfiguration>) => void;\n}\n\n/**\n * Shared Global Context Pattern\n * =============================\n *\n * WHY THIS EXISTS:\n * When bundlers (like Vite's optimizeDeps with esbuild) pre-bundle dependencies,\n * they may inline transitive dependencies like @openzeppelin/ui-react into the\n * consuming package's bundle. This creates MULTIPLE instances of this module:\n *\n * 1. The app's direct import → packages/react/dist/index.js\n * 2. The adapter package's inlined copy → .vite/deps/@openzeppelin_adapter_evm.js\n *\n * Since React contexts use referential identity, these two module instances have\n * DIFFERENT context objects. When the adapter's components call useWalletState(),\n * they look for a context that was never provided (because WalletStateProvider\n * uses the app's context, not the adapter's inlined copy).\n *\n * SOLUTION:\n * Store the context object on globalThis so ALL module instances share the same\n * React context, regardless of how they were loaded or bundled.\n *\n * WHEN IS THIS NEEDED:\n * - Development with Vite's dependency pre-bundling (optimizeDeps)\n * - When adapters are installed from npm (not workspace-linked with same bundler)\n * - Any scenario where @openzeppelin/ui-react might be duplicated\n *\n * PRODUCTION APPS:\n * In production builds where the app and adapters are bundled together with proper\n * deduplication (e.g., via bundler configuration or peer dependencies), this\n * workaround may not be strictly necessary. However, it provides a safety net\n * and has minimal overhead.\n */\n\n/**\n * Use Symbol.for() to create a globally unique key that is consistent across\n * all module instances. Unlike Symbol(), Symbol.for() returns the same symbol\n * for the same key string, which is essential for cross-module sharing.\n */\nconst WALLET_STATE_CONTEXT_KEY = Symbol.for('@openzeppelin/ui-react/WalletStateContext');\n\n/**\n * Type-safe interface for the global object extension.\n * This provides better type safety than using Record<string, unknown>.\n */\ninterface GlobalWithWalletContext {\n [WALLET_STATE_CONTEXT_KEY]?: React.Context<WalletStateContextValue | undefined>;\n}\n\n/**\n * Retrieves or creates the shared WalletStateContext.\n *\n * NOTE ON ATOMICITY:\n * The check-then-set pattern here is not atomic, but this is acceptable because:\n * 1. JavaScript is single-threaded; module initialization is synchronous\n * 2. Even if multiple modules initialize \"simultaneously\" during parallel loading,\n * they execute sequentially on the main thread\n * 3. The worst case (two contexts created) would only happen if the check and set\n * were somehow interleaved, which cannot occur in JS's execution model\n * 4. For React contexts specifically, having the same context object is what matters,\n * and this pattern guarantees that after initialization\n */\nfunction getOrCreateSharedContext(): React.Context<WalletStateContextValue | undefined> {\n const global = globalThis as GlobalWithWalletContext;\n\n if (!global[WALLET_STATE_CONTEXT_KEY]) {\n global[WALLET_STATE_CONTEXT_KEY] = createContext<WalletStateContextValue | undefined>(\n undefined\n );\n }\n\n return global[WALLET_STATE_CONTEXT_KEY];\n}\n\nexport const WalletStateContext = getOrCreateSharedContext();\n\n/**\n * Hook to access wallet state from WalletStateProvider.\n * @throws Error if used outside of WalletStateProvider\n */\nexport function useWalletState(): WalletStateContextValue {\n const context = React.useContext(WalletStateContext);\n if (context === undefined) {\n throw new Error('useWalletState must be used within a WalletStateProvider');\n }\n return context;\n}\n","/**\n * useAdapterContext.ts\n *\n * This file provides a hook to access the runtime context throughout the application.\n * It's a critical part of the runtime singleton pattern, allowing components to\n * access the centralized runtime registry.\n *\n * The runtime singleton pattern ensures:\n * - Only one runtime instance exists per network\n * - Wallet connection state is consistent across the app\n * - Better performance by eliminating redundant runtime initialization\n */\nimport { useContext } from 'react';\n\nimport { RuntimeContext, RuntimeContextValue } from './AdapterContext';\n\n/**\n * Hook to access the runtime context\n *\n * This hook provides access to the `getRuntimeForNetwork` function which\n * retrieves or creates runtime instances from the singleton registry.\n *\n * Components should typically use the higher-level wallet/runtime hooks instead\n * of this hook directly, as it handles React state update timing properly.\n *\n * @throws Error if used outside of a RuntimeProvider context\n * @returns The runtime context value\n */\nexport function useRuntimeContext(): RuntimeContextValue {\n const context = useContext(RuntimeContext);\n\n if (!context) {\n throw new Error('useRuntimeContext must be used within a RuntimeProvider');\n }\n\n return context;\n}\n","import type React from 'react';\n\nimport type {\n EcosystemReactUiProviderProps,\n EcosystemSpecificReactHooks,\n} from '@openzeppelin/ui-types';\n\n/**\n * Cached wallet session artifacts for a single ecosystem.\n *\n * This keeps the wallet provider and facade hooks stable while network-scoped\n * runtimes are recreated underneath the session.\n */\nexport interface WalletSessionEntry {\n /** Ecosystem identifier, e.g. `evm` or `stellar`. */\n ecosystem: string;\n /** The network whose runtime most recently configured this session. */\n lastConfiguredNetworkId: string;\n /** Ecosystem-scoped React provider root for wallet libraries such as wagmi. */\n providerComponent: React.ComponentType<EcosystemReactUiProviderProps> | null;\n /** Facade hooks resolved from the ecosystem session. */\n hooks: EcosystemSpecificReactHooks | null;\n}\n\n/**\n * Internal registry of dormant and active wallet sessions keyed by ecosystem.\n */\nexport type WalletSessionRegistry = Record<string, WalletSessionEntry>;\n\n/**\n * Returns the cached wallet session for an ecosystem, if present.\n *\n * @param registry - Current internal wallet session registry.\n * @param ecosystem - Ecosystem key to resolve.\n * @returns The cached session entry or `null` when the ecosystem has not been configured yet.\n */\nexport function getWalletSession(\n registry: WalletSessionRegistry,\n ecosystem: string | null | undefined\n): WalletSessionEntry | null {\n if (!ecosystem) {\n return null;\n }\n\n return registry[ecosystem] ?? null;\n}\n\n/**\n * Inserts or replaces the cached wallet session for a single ecosystem.\n *\n * @param registry - Current internal wallet session registry.\n * @param session - Session entry to cache.\n * @returns A new registry object containing the upserted ecosystem session.\n */\nexport function upsertWalletSession(\n registry: WalletSessionRegistry,\n session: WalletSessionEntry\n): WalletSessionRegistry {\n return {\n ...registry,\n [session.ecosystem]: session,\n };\n}\n","import React, { ReactNode, useCallback, useEffect, useMemo, useRef, useState } from 'react';\n\nimport type {\n EcosystemReactUiProviderProps,\n EcosystemRuntime,\n EcosystemSpecificReactHooks,\n NativeConfigLoader,\n NetworkConfig,\n UiKitConfiguration,\n} from '@openzeppelin/ui-types';\nimport { logger } from '@openzeppelin/ui-utils';\n\nimport { useRuntimeContext } from './useAdapterContext';\nimport {\n getWalletSession,\n upsertWalletSession,\n type WalletSessionRegistry,\n} from './walletSessionRegistry';\nimport { WalletStateContext, type WalletStateContextValue } from './WalletStateContext';\n\nexport interface WalletStateProviderProps {\n children: ReactNode;\n /** Optional initial network ID to set as active when the provider mounts. */\n initialNetworkId?: string | null;\n /** Function to retrieve a NetworkConfig object by its ID. */\n getNetworkConfigById: (\n networkId: string\n ) => Promise<NetworkConfig | null | undefined> | NetworkConfig | null | undefined;\n /**\n * Optional generic function to load configuration modules by relative path.\n * The adapter is responsible for constructing the conventional path (e.g., './config/wallet/[kitName].config').\n * @param relativePath The conventional relative path to the configuration module.\n * @returns A Promise resolving to the configuration object (expected to have a default export) or null.\n */\n loadConfigModule?: NativeConfigLoader;\n}\n\n/**\n * Configures the runtime's UI kit capability and returns the ecosystem session artifacts.\n */\nasync function configureRuntimeUiKit(\n runtime: EcosystemRuntime,\n loadConfigModule?: (relativePath: string) => Promise<Record<string, unknown> | null>,\n programmaticOverrides: Partial<UiKitConfiguration> = {}\n): Promise<{\n providerComponent: React.ComponentType<EcosystemReactUiProviderProps> | null;\n hooks: EcosystemSpecificReactHooks | null;\n}> {\n const uiKit = runtime.uiKit;\n if (!uiKit) {\n return { providerComponent: null, hooks: null };\n }\n\n try {\n const hasUiKitOverride = Object.keys(programmaticOverrides).length > 0;\n\n // Always initialize the runtime UI kit so adapter-managed providers (wagmi, Stellar kit, etc.)\n // can hydrate their internal state from app-config defaults even without explicit overrides.\n if (typeof uiKit.configureUiKit === 'function') {\n const nextUiKitConfig = { ...programmaticOverrides };\n logger.info(\n '[WSP configureRuntimeUiKit]',\n hasUiKitOverride\n ? `Applying explicit UI kit overrides for runtime: ${runtime.networkConfig.id}`\n : `Initializing runtime UI kit from adapter/app defaults for runtime: ${runtime.networkConfig.id}`\n );\n await uiKit.configureUiKit(nextUiKitConfig, {\n loadUiKitNativeConfig: loadConfigModule,\n });\n logger.info(\n '[WSP configureRuntimeUiKit] configureUiKit completed for runtime:',\n runtime.networkConfig.id\n );\n }\n\n const providerComponent = uiKit.getEcosystemReactUiContextProvider?.() || null;\n const hooks = uiKit.getEcosystemReactHooks?.() || null;\n\n logger.info('[WSP configureRuntimeUiKit]', 'UI provider and hooks retrieved successfully.');\n\n return { providerComponent, hooks };\n } catch (error) {\n logger.error('[WSP configureRuntimeUiKit]', 'Error during runtime UI setup:', error);\n throw error; // Re-throw to be handled by caller\n }\n}\n\n/**\n * @name WalletStateProvider\n * @description This provider is a central piece of the application's state management for wallet and network interactions.\n * It is responsible for:\n * 1. Managing the globally selected active network ID (`activeNetworkId`).\n * 2. Deriving the full `NetworkConfig` object (`activeNetworkConfig`) for the active network.\n * 3. Fetching and providing the corresponding `EcosystemRuntime` (`activeRuntime`) for the active network,\n * leveraging the `RuntimeProvider` to ensure runtime singletons.\n * 4. Caching ecosystem-scoped wallet session artifacts (provider roots and facade hooks)\n * independently from the network-scoped runtime.\n * 5. Rendering the active ecosystem wallet provider (e.g., WagmiProvider for EVM) around its\n * children, which is essential for the facade hooks to function correctly.\n * 6. Providing a function (`setActiveNetworkId`) to change the globally active network.\n *\n * Consumers use the `useWalletState()` hook to access this global state.\n * It should be placed high in the component tree, inside a `<RuntimeProvider>`.\n */\nexport function WalletStateProvider({\n children,\n initialNetworkId = null,\n getNetworkConfigById,\n loadConfigModule,\n}: WalletStateProviderProps) {\n // State for the ID of the globally selected network.\n const [currentGlobalNetworkId, setCurrentGlobalNetworkIdState] = useState<string | null>(\n initialNetworkId\n );\n // State for the full NetworkConfig object of the globally selected network.\n const [currentGlobalNetworkConfig, setCurrentGlobalNetworkConfig] =\n useState<NetworkConfig | null>(null);\n\n // State for the active EcosystemRuntime corresponding to the currentGlobalNetworkConfig.\n const [globalActiveRuntime, setGlobalActiveRuntime] = useState<EcosystemRuntime | null>(null);\n // Loading state for the globalActiveRuntime.\n const [isGlobalRuntimeLoading, setIsGlobalRuntimeLoading] = useState<boolean>(false);\n // Cache one wallet provider/hooks pair per ecosystem so same-ecosystem network switches do not\n // remount the wallet provider. The active runtime remains network-scoped and disposable.\n const [walletSessionRegistry, setWalletSessionRegistry] = useState<WalletSessionRegistry>({});\n const [activeWalletSessionEcosystem, setActiveWalletSessionEcosystem] = useState<string | null>(\n null\n );\n\n // New state to act as a manual trigger for re-configuring the UI kit.\n const [uiKitConfigVersion, setUiKitConfigVersion] = useState(0);\n // State to hold programmatic overrides for the next reconfiguration.\n const [programmaticUiKitConfig, setProgrammaticUiKitConfig] = useState<\n Partial<UiKitConfiguration> | undefined\n >(undefined);\n\n // Consume RuntimeContext to get the function for fetching runtime instances.\n const { getRuntimeForNetwork, releaseRuntime } = useRuntimeContext();\n\n // Track the network ID of the currently promoted runtime so we can release it\n // after a successful handoff to the next runtime.\n const promotedNetworkIdRef = useRef<string | null>(null);\n\n // Effect to derive the full NetworkConfig object when currentGlobalNetworkId changes.\n useEffect(() => {\n const abortController = new AbortController();\n\n async function fetchNetworkConfig() {\n if (!currentGlobalNetworkId) {\n // If currentGlobalNetworkId is null, clear the config.\n if (!abortController.signal.aborted) {\n setCurrentGlobalNetworkConfig(null);\n }\n return;\n }\n\n try {\n const config = await Promise.resolve(getNetworkConfigById(currentGlobalNetworkId));\n if (!abortController.signal.aborted) {\n setCurrentGlobalNetworkConfig(config || null);\n }\n } catch (error) {\n if (!abortController.signal.aborted) {\n logger.error('[WSP fetchNetworkConfig]', 'Failed to fetch network config:', error);\n setCurrentGlobalNetworkConfig(null);\n }\n }\n }\n\n void fetchNetworkConfig();\n return () => abortController.abort();\n }, [currentGlobalNetworkId, getNetworkConfigById]);\n\n // Effect to load the active runtime and its UI capabilities when currentGlobalNetworkConfig changes.\n // Implements a safe handoff: the previous runtime stays active while the replacement loads and\n // configures its UI kit. Only after the new runtime is promoted does the old one get released.\n useEffect(() => {\n const abortController = new AbortController();\n\n async function loadRuntimeAndConfigureUi() {\n if (!currentGlobalNetworkConfig) {\n if (!abortController.signal.aborted) {\n const prevNetworkId = promotedNetworkIdRef.current;\n setGlobalActiveRuntime(null);\n setIsGlobalRuntimeLoading(false);\n setActiveWalletSessionEcosystem(null);\n\n if (prevNetworkId) {\n releaseRuntime(prevNetworkId);\n promotedNetworkIdRef.current = null;\n }\n }\n return;\n }\n\n const { runtime: newRuntime, isLoading: newIsLoading } = getRuntimeForNetwork(\n currentGlobalNetworkConfig\n ) as { runtime: EcosystemRuntime | null; isLoading: boolean };\n\n if (abortController.signal.aborted) return;\n\n setIsGlobalRuntimeLoading(newIsLoading);\n\n if (newRuntime && !newIsLoading) {\n try {\n const { providerComponent, hooks } = await configureRuntimeUiKit(\n newRuntime,\n loadConfigModule,\n programmaticUiKitConfig\n );\n\n if (!abortController.signal.aborted) {\n const ecosystem = newRuntime.networkConfig.ecosystem;\n const prevNetworkId = promotedNetworkIdRef.current;\n const nextNetworkId = newRuntime.networkConfig.id;\n\n setWalletSessionRegistry((prevRegistry) =>\n upsertWalletSession(prevRegistry, {\n ecosystem,\n lastConfiguredNetworkId: nextNetworkId,\n providerComponent,\n hooks,\n })\n );\n setGlobalActiveRuntime(newRuntime);\n setActiveWalletSessionEcosystem(ecosystem);\n promotedNetworkIdRef.current = nextNetworkId;\n\n // Release the superseded runtime now that the replacement is promoted.\n // Skip when the network ID hasn't changed (e.g. UI-kit reconfiguration).\n if (prevNetworkId && prevNetworkId !== nextNetworkId) {\n releaseRuntime(prevNetworkId);\n }\n }\n } catch (error) {\n if (!abortController.signal.aborted) {\n logger.error(\n '[WSP loadRuntimeAndConfigureUi]',\n 'Error during runtime UI setup:',\n error\n );\n }\n }\n } else if (!newRuntime && !newIsLoading) {\n if (!abortController.signal.aborted) {\n const prevNetworkId = promotedNetworkIdRef.current;\n setGlobalActiveRuntime(null);\n setActiveWalletSessionEcosystem(null);\n\n if (prevNetworkId) {\n releaseRuntime(prevNetworkId);\n promotedNetworkIdRef.current = null;\n }\n }\n }\n // If newIsLoading is true, retain the active wallet session so same-ecosystem switches do\n // not tear down the connected provider while the target runtime is still loading.\n }\n\n void loadRuntimeAndConfigureUi();\n return () => abortController.abort();\n }, [\n currentGlobalNetworkConfig,\n getRuntimeForNetwork,\n releaseRuntime,\n loadConfigModule,\n uiKitConfigVersion,\n programmaticUiKitConfig,\n ]);\n\n /**\n * Callback to set the globally active network ID.\n * Also clears dependent states if the network ID is cleared.\n */\n const setActiveNetworkIdCallback = useCallback((networkId: string | null) => {\n logger.info('WalletStateProvider', `Setting global network ID to: ${networkId}`);\n setCurrentGlobalNetworkIdState(networkId); // This will trigger the fetchNetworkConfig effect.\n if (!networkId) {\n // If clearing the network, proactively clear downstream states.\n // The effects above will also clear them, but this is more immediate.\n setCurrentGlobalNetworkConfig(null);\n setGlobalActiveRuntime(null);\n setIsGlobalRuntimeLoading(false);\n setActiveWalletSessionEcosystem(null);\n }\n }, []); // Empty dependency array as it only uses setters from useState.\n\n /**\n * Callback to explicitly trigger a re-configuration of the active runtime's UI kit.\n * This is useful when a UI kit setting changes (e.g., via a wizard) without a network change.\n */\n const reconfigureActiveUiKit = useCallback(\n (uiKitConfig?: Partial<UiKitConfiguration>) => {\n logger.info(\n 'WalletStateProvider',\n 'Explicitly triggering UI kit re-configuration by bumping version.',\n uiKitConfig\n );\n setProgrammaticUiKitConfig(uiKitConfig);\n setUiKitConfigVersion((v) => v + 1);\n },\n [setProgrammaticUiKitConfig, setUiKitConfigVersion]\n );\n\n const activeWalletSession = useMemo(\n () => getWalletSession(walletSessionRegistry, activeWalletSessionEcosystem),\n [walletSessionRegistry, activeWalletSessionEcosystem]\n );\n\n const walletFacadeHooks: EcosystemSpecificReactHooks | null = activeWalletSession?.hooks ?? null;\n\n // The context value exposes the active network-scoped runtime and the active ecosystem-scoped\n // wallet session hooks. Consumers continue to access the same public fields.\n const contextValue = useMemo<WalletStateContextValue>(\n () => ({\n activeNetworkId: currentGlobalNetworkId,\n setActiveNetworkId: setActiveNetworkIdCallback,\n activeNetworkConfig: currentGlobalNetworkConfig,\n activeRuntime: globalActiveRuntime,\n isRuntimeLoading: isGlobalRuntimeLoading,\n walletFacadeHooks,\n reconfigureActiveUiKit,\n }),\n [\n currentGlobalNetworkId,\n setActiveNetworkIdCallback,\n currentGlobalNetworkConfig,\n globalActiveRuntime,\n isGlobalRuntimeLoading,\n walletFacadeHooks,\n reconfigureActiveUiKit,\n ]\n );\n\n const ActualProviderToRender = activeWalletSession?.providerComponent ?? null;\n let childrenToRender: ReactNode;\n\n if (ActualProviderToRender) {\n // Key the provider by ecosystem instead of network so same-ecosystem network switches keep\n // the wallet provider mounted. Switching ecosystems still remounts the provider cleanly.\n const key = activeWalletSessionEcosystem || 'unknown';\n\n // Runtime-provided UI roots manage their own configuration internally via the UI kit capability.\n logger.debug(\n '[WSP RENDER]',\n 'Rendering runtime-provided UI context provider:',\n ActualProviderToRender.displayName || ActualProviderToRender.name || 'UnknownComponent',\n 'with key:',\n key\n );\n childrenToRender = <ActualProviderToRender key={key}>{children}</ActualProviderToRender>;\n } else {\n logger.debug(\n '[WSP RENDER]',\n 'No runtime UI context provider to render. Rendering direct children.'\n );\n childrenToRender = children;\n }\n\n return (\n <WalletStateContext.Provider value={contextValue}>\n {childrenToRender}\n </WalletStateContext.Provider>\n );\n}\n","import { createContext, useContext } from 'react';\n\n/**\n * Analytics context value interface.\n * Provides access to analytics functionality throughout the React component tree.\n */\nexport interface AnalyticsContextValue {\n /** Google Analytics tag ID */\n tagId?: string;\n /**\n * Check if analytics is enabled via feature flag.\n * Returns fresh state on each call to handle dynamic feature flag changes.\n */\n isEnabled: () => boolean;\n /** Initialize analytics with optional tag ID override */\n initialize: (tagIdOverride?: string) => void;\n /**\n * Track a generic event with custom parameters.\n * Use this for app-specific events.\n *\n * @example\n * ```typescript\n * trackEvent('button_clicked', { button_name: 'submit' });\n * ```\n */\n trackEvent: (eventName: string, parameters: Record<string, string | number>) => void;\n /**\n * Track page view event.\n *\n * @example\n * ```typescript\n * trackPageView('Dashboard', '/dashboard');\n * ```\n */\n trackPageView: (pageName: string, pagePath: string) => void;\n /**\n * Track network selection event.\n *\n * @example\n * ```typescript\n * trackNetworkSelection('ethereum-mainnet', 'evm');\n * ```\n */\n trackNetworkSelection: (networkId: string, ecosystem: string) => void;\n}\n\n/**\n * Analytics context for providing analytics functionality to React components.\n * Must be used within an AnalyticsProvider.\n */\nexport const AnalyticsContext = createContext<AnalyticsContextValue | null>(null);\n\n/**\n * Internal hook to access analytics context.\n * Throws an error if used outside of an AnalyticsProvider.\n *\n * @internal\n * @throws Error if used outside of AnalyticsProvider\n */\nexport const useAnalyticsContext = (): AnalyticsContextValue => {\n const context = useContext(AnalyticsContext);\n if (!context) {\n throw new Error('useAnalyticsContext must be used within an AnalyticsProvider');\n }\n return context;\n};\n","import React, { ReactNode, useEffect, useMemo } from 'react';\n\nimport { AnalyticsService, logger } from '@openzeppelin/ui-utils';\n\nimport { AnalyticsContext, AnalyticsContextValue } from './AnalyticsContext';\n\n/**\n * Props for the AnalyticsProvider component\n */\nexport interface AnalyticsProviderProps {\n /** Google Analytics tag ID (e.g., 'G-XXXXXXXXXX') */\n tagId?: string;\n /** Whether to automatically initialize analytics on mount (default: true) */\n autoInit?: boolean;\n /** Child components */\n children: ReactNode;\n}\n\n/**\n * Analytics Provider component.\n * Provides analytics functionality throughout the React component tree.\n *\n * @example\n * ```tsx\n * function App() {\n * return (\n * <AnalyticsProvider tagId={import.meta.env.VITE_GA_TAG_ID} autoInit={true}>\n * <YourApp />\n * </AnalyticsProvider>\n * );\n * }\n * ```\n */\nexport const AnalyticsProvider: React.FC<AnalyticsProviderProps> = ({\n tagId,\n autoInit = true,\n children,\n}) => {\n useEffect(() => {\n if (autoInit && tagId) {\n AnalyticsService.initialize(tagId);\n }\n }, [tagId, autoInit]);\n\n const contextValue: AnalyticsContextValue = useMemo(\n () => ({\n tagId,\n isEnabled: () => AnalyticsService.isEnabled(),\n initialize: (tagIdOverride?: string) => {\n const effectiveTagId = tagIdOverride || tagId;\n if (effectiveTagId) {\n AnalyticsService.initialize(effectiveTagId);\n }\n },\n trackEvent: (eventName: string, parameters: Record<string, string | number>) => {\n try {\n AnalyticsService.trackEvent(eventName, parameters);\n } catch (error) {\n logger.error('AnalyticsProvider', 'Error tracking event:', error);\n }\n },\n trackPageView: (pageName: string, pagePath: string) => {\n try {\n AnalyticsService.trackPageView(pageName, pagePath);\n } catch (error) {\n logger.error('AnalyticsProvider', 'Error tracking page view:', error);\n }\n },\n trackNetworkSelection: (networkId: string, ecosystem: string) => {\n try {\n AnalyticsService.trackNetworkSelection(networkId, ecosystem);\n } catch (error) {\n logger.error('AnalyticsProvider', 'Error tracking network selection:', error);\n }\n },\n }),\n [tagId]\n );\n\n return <AnalyticsContext.Provider value={contextValue}>{children}</AnalyticsContext.Provider>;\n};\n","import { useAnalyticsContext } from './AnalyticsContext';\n\n/**\n * Custom hook for accessing analytics functionality.\n *\n * This hook provides a convenient interface for tracking user interactions\n * throughout the application. It must be used within an AnalyticsProvider.\n *\n * For app-specific tracking methods, create a wrapper hook that uses this\n * hook and adds your custom tracking functions.\n *\n * @example\n * ```tsx\n * // Basic usage\n * function MyComponent() {\n * const { trackEvent, trackPageView, isEnabled } = useAnalytics();\n *\n * const handleClick = () => {\n * trackEvent('button_clicked', { button_name: 'submit' });\n * };\n *\n * return (\n * <div>\n * Analytics enabled: {isEnabled().toString()}\n * <button onClick={handleClick}>Submit</button>\n * </div>\n * );\n * }\n * ```\n *\n * @example\n * ```tsx\n * // Creating app-specific wrapper hook\n * function useMyAppAnalytics() {\n * const analytics = useAnalytics();\n *\n * return {\n * ...analytics,\n * trackFormSubmit: (formName: string) => {\n * analytics.trackEvent('form_submitted', { form_name: formName });\n * },\n * };\n * }\n * ```\n *\n * @returns Analytics context with tracking methods and state\n * @throws Error if used outside of AnalyticsProvider\n */\nexport const useAnalytics = () => {\n try {\n return useAnalyticsContext();\n } catch {\n throw new Error('useAnalytics must be used within an AnalyticsProvider');\n }\n};\n","import type { EcosystemWalletComponents } from '@openzeppelin/ui-types';\n\nimport { useWalletState } from './WalletStateContext';\n\n/**\n * Hook that provides direct access to wallet UI components from the active runtime.\n *\n * Use this hook when you need full control over the layout and composition of\n * wallet components. For standard layouts, prefer using `WalletConnectionUI`\n * with its props forwarding capabilities.\n *\n * @returns The wallet components object, or null if no runtime is active or\n * the runtime doesn't provide wallet components.\n *\n * @example\n * ```tsx\n * import { useWalletComponents } from '@openzeppelin/ui-react';\n *\n * function CustomWalletSection() {\n * const walletComponents = useWalletComponents();\n *\n * if (!walletComponents) {\n * return <p>Loading wallet...</p>;\n * }\n *\n * const { ConnectButton, NetworkSwitcher, AccountDisplay } = walletComponents;\n *\n * return (\n * <div className=\"flex flex-col gap-4\">\n * {ConnectButton && (\n * <ConnectButton\n * size=\"xl\"\n * variant=\"outline\"\n * fullWidth\n * className=\"font-semibold\"\n * />\n * )}\n * <div className=\"flex gap-2\">\n * {NetworkSwitcher && <NetworkSwitcher size=\"sm\" />}\n * {AccountDisplay && <AccountDisplay size=\"sm\" />}\n * </div>\n * </div>\n * );\n * }\n * ```\n */\nexport function useWalletComponents(): EcosystemWalletComponents | null {\n const { activeNetworkConfig, activeRuntime, isRuntimeLoading } = useWalletState();\n const activeUiKit = activeRuntime?.uiKit;\n const isCrossEcosystemTransition = !!(\n isRuntimeLoading &&\n activeRuntime?.networkConfig?.ecosystem &&\n activeNetworkConfig?.ecosystem &&\n activeRuntime.networkConfig.ecosystem !== activeNetworkConfig.ecosystem\n );\n\n if (\n isCrossEcosystemTransition ||\n !activeUiKit ||\n typeof activeUiKit.getEcosystemWalletComponents !== 'function'\n ) {\n return null;\n }\n\n try {\n return activeUiKit.getEcosystemWalletComponents() ?? null;\n } catch {\n return null;\n }\n}\n","// Assumes WalletStateContext exports useWalletState\nimport { isRecordWithProperties } from '@openzeppelin/ui-utils';\n\nimport { useWalletState } from './WalletStateContext';\n\nexport interface DerivedAccountStatus {\n isConnected: boolean;\n isConnecting: boolean;\n isDisconnected: boolean;\n isReconnecting: boolean;\n status: string;\n address?: string;\n chainId?: number;\n // Potentially add other commonly used and safely extracted properties from useAccount's result\n}\n\nconst defaultAccountStatus: DerivedAccountStatus = {\n isConnected: false,\n isConnecting: false,\n isDisconnected: true,\n isReconnecting: false,\n status: 'disconnected',\n address: undefined,\n chainId: undefined,\n};\n\n/**\n * A custom hook that consumes useWalletState to get the walletFacadeHooks,\n * then calls the useAccount facade hook (if available) and returns a structured,\n * safely-accessed account status (isConnected, address, chainId).\n * Provides default values if the hook or its properties are unavailable.\n */\nexport function useDerivedAccountStatus(): DerivedAccountStatus {\n const { walletFacadeHooks } = useWalletState();\n\n // Call the useAccount hook from the facade\n const accountHookOutput = walletFacadeHooks?.useAccount\n ? walletFacadeHooks.useAccount()\n : undefined;\n\n if (isRecordWithProperties(accountHookOutput)) {\n const isConnected =\n 'isConnected' in accountHookOutput && typeof accountHookOutput.isConnected === 'boolean'\n ? accountHookOutput.isConnected\n : defaultAccountStatus.isConnected;\n const isConnecting =\n 'isConnecting' in accountHookOutput && typeof accountHookOutput.isConnecting === 'boolean'\n ? accountHookOutput.isConnecting\n : defaultAccountStatus.isConnecting;\n const isDisconnected =\n 'isDisconnected' in accountHookOutput && typeof accountHookOutput.isDisconnected === 'boolean'\n ? accountHookOutput.isDisconnected\n : defaultAccountStatus.isDisconnected;\n const isReconnecting =\n 'isReconnecting' in accountHookOutput && typeof accountHookOutput.isReconnecting === 'boolean'\n ? accountHookOutput.isReconnecting\n : defaultAccountStatus.isReconnecting;\n const status =\n 'status' in accountHookOutput && typeof accountHookOutput.status === 'string'\n ? accountHookOutput.status\n : defaultAccountStatus.status;\n const address =\n 'address' in accountHookOutput && typeof accountHookOutput.address === 'string'\n ? accountHookOutput.address\n : defaultAccountStatus.address;\n const chainId =\n 'chainId' in accountHookOutput && typeof accountHookOutput.chainId === 'number'\n ? accountHookOutput.chainId\n : defaultAccountStatus.chainId;\n return {\n isConnected,\n isConnecting,\n isDisconnected,\n isReconnecting,\n status,\n address,\n chainId,\n };\n }\n return defaultAccountStatus;\n}\n","import { isRecordWithProperties } from '@openzeppelin/ui-utils';\n\nimport { useWalletState } from './WalletStateContext';\n\n// Define the expected return shape for the derived hook\nexport interface DerivedSwitchChainStatus {\n /** Function to initiate a network switch. Undefined if not available. */\n switchChain?: (args: { chainId: number }) => void;\n /** True if a network switch is currently in progress. */\n isSwitching: boolean;\n /** Error object if the last switch attempt failed, otherwise null. */\n error: Error | null;\n}\n\nconst defaultSwitchChainStatus: DerivedSwitchChainStatus = {\n switchChain: undefined,\n isSwitching: false,\n error: null,\n};\n\n/**\n * A custom hook that consumes `useWalletState` to get `walletFacadeHooks`,\n * then calls the `useSwitchChain` facade hook (if available) and returns a structured,\n * safely-accessed status and control function for network switching.\n * Provides default values if the hook or its properties are unavailable.\n */\nexport function useDerivedSwitchChainStatus(): DerivedSwitchChainStatus {\n const { walletFacadeHooks } = useWalletState();\n\n const switchChainHookOutput = walletFacadeHooks?.useSwitchChain\n ? walletFacadeHooks.useSwitchChain()\n : undefined;\n\n if (isRecordWithProperties(switchChainHookOutput)) {\n const execSwitchFn =\n 'switchChain' in switchChainHookOutput &&\n typeof switchChainHookOutput.switchChain === 'function'\n ? (switchChainHookOutput.switchChain as (args: { chainId: number }) => void)\n : defaultSwitchChainStatus.switchChain;\n\n const isPending =\n 'isPending' in switchChainHookOutput && typeof switchChainHookOutput.isPending === 'boolean'\n ? switchChainHookOutput.isPending\n : defaultSwitchChainStatus.isSwitching;\n\n const err =\n 'error' in switchChainHookOutput && switchChainHookOutput.error instanceof Error\n ? switchChainHookOutput.error\n : defaultSwitchChainStatus.error;\n\n return { switchChain: execSwitchFn, isSwitching: isPending, error: err };\n }\n\n return defaultSwitchChainStatus;\n}\n","import { logger } from '@openzeppelin/ui-utils';\n\nimport { useWalletState } from './WalletStateContext';\n\nexport interface DerivedChainInfo {\n /** The current chain ID reported by the wallet's active connection, if available. */\n currentChainId?: number;\n /** Array of chains configured in the underlying wallet library (e.g., wagmi). Type is any[] for generic compatibility. */\n availableChains: unknown[];\n}\n\nconst defaultChainInfo: DerivedChainInfo = {\n currentChainId: undefined,\n availableChains: [],\n};\n\n/**\n * A custom hook that consumes `useWalletState` to get `walletFacadeHooks`,\n * then calls the `useChainId` and `useChains` facade hooks (if available)\n * and returns a structured object with this information.\n * Provides default values if the hooks or their properties are unavailable.\n */\nexport function useDerivedChainInfo(): DerivedChainInfo {\n const { walletFacadeHooks } = useWalletState();\n\n let chainIdToReturn: number | undefined = defaultChainInfo.currentChainId;\n const chainIdHookOutput = walletFacadeHooks?.useChainId\n ? walletFacadeHooks.useChainId()\n : undefined;\n // The useChainId hook from wagmi directly returns the number or undefined\n if (typeof chainIdHookOutput === 'number') {\n chainIdToReturn = chainIdHookOutput;\n } else if (chainIdHookOutput !== undefined) {\n // If it's not a number but not undefined, log a warning but use default. Could be an adapter returning unexpected type.\n logger.warn(\n 'useDerivedChainInfo',\n 'useChainId facade hook returned non-numeric value:',\n chainIdHookOutput\n );\n }\n\n let chainsToReturn: unknown[] = defaultChainInfo.availableChains;\n const chainsHookOutput = walletFacadeHooks?.useChains ? walletFacadeHooks.useChains() : undefined;\n // The useChains hook from wagmi directly returns an array of Chain objects\n if (Array.isArray(chainsHookOutput)) {\n chainsToReturn = chainsHookOutput;\n } else if (chainsHookOutput !== undefined) {\n logger.warn(\n 'useDerivedChainInfo',\n 'useChains facade hook returned non-array value:',\n chainsHookOutput\n );\n }\n\n return { currentChainId: chainIdToReturn, availableChains: chainsToReturn };\n}\n","import type { Connector } from '@openzeppelin/ui-types';\nimport { isRecordWithProperties } from '@openzeppelin/ui-utils';\n\nimport { useWalletState } from './WalletStateContext';\n\n// Assuming Connector type is available\n\nexport interface DerivedConnectStatus {\n /** Function to initiate a connection, usually takes a connector. Undefined if not available. */\n connect?: (args?: { connector?: Connector /* or string for id */ }) => void;\n /** Array of available connectors. Type is any[] for broad compatibility until Connector type is fully generic here. */\n connectors: Connector[]; // Or any[] if Connector type from types pkg is too specific for generic hook here\n /** True if a connection attempt is in progress. */\n isConnecting: boolean;\n /** Error object if the last connection attempt failed, otherwise null. */\n error: Error | null;\n /** The connector a connection is pending for, if any. */\n pendingConnector?: Connector; // Or any\n}\n\nconst defaultConnectStatus: DerivedConnectStatus = {\n connect: undefined,\n connectors: [],\n isConnecting: false,\n error: null,\n pendingConnector: undefined,\n};\n\n/**\n * A custom hook that consumes `useWalletState` to get `walletFacadeHooks`,\n * then calls the `useConnect` facade hook (if available) and returns a structured,\n * safely-accessed status and control functions for wallet connection.\n */\nexport function useDerivedConnectStatus(): DerivedConnectStatus {\n const { walletFacadeHooks } = useWalletState();\n\n const connectHookOutput = walletFacadeHooks?.useConnect\n ? walletFacadeHooks.useConnect()\n : undefined;\n\n if (isRecordWithProperties(connectHookOutput)) {\n const connectFn =\n 'connect' in connectHookOutput && typeof connectHookOutput.connect === 'function'\n ? (connectHookOutput.connect as (args?: { connector?: Connector }) => void)\n : defaultConnectStatus.connect;\n\n const conns =\n 'connectors' in connectHookOutput && Array.isArray(connectHookOutput.connectors)\n ? (connectHookOutput.connectors as Connector[])\n : defaultConnectStatus.connectors;\n\n const isPending =\n 'isPending' in connectHookOutput && typeof connectHookOutput.isPending === 'boolean'\n ? connectHookOutput.isPending\n : 'isLoading' in connectHookOutput && typeof connectHookOutput.isLoading === 'boolean'\n ? connectHookOutput.isLoading\n : defaultConnectStatus.isConnecting;\n\n const err =\n 'error' in connectHookOutput && connectHookOutput.error instanceof Error\n ? connectHookOutput.error\n : defaultConnectStatus.error;\n\n const pendingConn =\n 'pendingConnector' in connectHookOutput &&\n typeof connectHookOutput.pendingConnector === 'object' // Assuming Connector is an object\n ? (connectHookOutput.pendingConnector as Connector)\n : defaultConnectStatus.pendingConnector;\n\n return {\n connect: connectFn,\n connectors: conns,\n isConnecting: isPending,\n error: err,\n pendingConnector: pendingConn,\n };\n }\n\n return defaultConnectStatus;\n}\n","import { isRecordWithProperties } from '@openzeppelin/ui-utils';\n\nimport { useWalletState } from './WalletStateContext';\n\nexport interface DerivedDisconnectStatus {\n /** Function to initiate disconnection. Undefined if not available. */\n disconnect?: () => void | Promise<void>; // Can be sync or async\n /** True if a disconnection attempt is in progress (if hook provides this). */\n isDisconnecting: boolean;\n /** Error object if the last disconnection attempt failed (if hook provides this). */\n error: Error | null;\n}\n\nconst defaultDisconnectStatus: DerivedDisconnectStatus = {\n disconnect: undefined,\n isDisconnecting: false,\n error: null,\n};\n\n/**\n * A custom hook that consumes `useWalletState` to get `walletFacadeHooks`,\n * then calls the `useDisconnect` facade hook (if available) and returns a structured,\n * safely-accessed status and control function for wallet disconnection.\n */\nexport function useDerivedDisconnect(): DerivedDisconnectStatus {\n const { walletFacadeHooks } = useWalletState();\n\n const disconnectHookOutput = walletFacadeHooks?.useDisconnect\n ? walletFacadeHooks.useDisconnect()\n : undefined;\n\n if (isRecordWithProperties(disconnectHookOutput)) {\n const disconnectFn =\n 'disconnect' in disconnectHookOutput && typeof disconnectHookOutput.disconnect === 'function'\n ? (disconnectHookOutput.disconnect as () => void | Promise<void>)\n : defaultDisconnectStatus.disconnect;\n\n // wagmi's useDisconnect doesn't have isPending/isLoading directly, but has error and variables (which is the connector it disconnected)\n // We will assume a simple isDisconnecting is not provided by current wagmi facade, but include for future flexibility\n const isPending =\n 'isPending' in disconnectHookOutput && typeof disconnectHookOutput.isPending === 'boolean'\n ? disconnectHookOutput.isPending\n : 'isLoading' in disconnectHookOutput && typeof disconnectHookOutput.isLoading === 'boolean'\n ? disconnectHookOutput.isLoading\n : defaultDisconnectStatus.isDisconnecting;\n\n const err =\n 'error' in disconnectHookOutput && disconnectHookOutput.error instanceof Error\n ? disconnectHookOutput.error\n : defaultDisconnectStatus.error;\n\n return { disconnect: disconnectFn, isDisconnecting: isPending, error: err };\n }\n\n return defaultDisconnectStatus;\n}\n","import { useEffect, useRef } from 'react';\n\nimport type { RuntimeCapability } from '@openzeppelin/ui-types';\nimport { logger } from '@openzeppelin/ui-utils';\n\nimport { useDerivedAccountStatus } from './useDerivedAccountStatus';\n\n/**\n * Hook that detects wallet reconnection and re-queues network switch if needed.\n *\n * When a user disconnects their wallet and then reconnects in the same session,\n * the wallet may connect to a different chain than what's selected in the app.\n * This hook detects that scenario and invokes a callback to re-queue the network switch.\n *\n * @param selectedNetworkConfigId - Currently selected network in the app\n * @param selectedCapability - Currently active wallet capability instance\n * @param networkToSwitchTo - Current network switch queue state (null if no switch pending)\n * @param onRequeueSwitch - Callback invoked when a network switch should be re-queued\n */\nexport function useWalletReconnectionHandler(\n selectedNetworkConfigId: string | null,\n selectedCapability: RuntimeCapability | null,\n networkToSwitchTo: string | null,\n onRequeueSwitch: (networkId: string) => void\n): void {\n const { isConnected, chainId: walletChainId } = useDerivedAccountStatus();\n const prevConnectedRef = useRef(isConnected);\n\n useEffect(() => {\n const wasDisconnected = !prevConnectedRef.current;\n const isNowConnected = isConnected;\n const isReconnection = wasDisconnected && isNowConnected;\n\n // Update ref for next render\n prevConnectedRef.current = isConnected;\n\n if (!isReconnection || !selectedNetworkConfigId || !selectedCapability) {\n return;\n }\n\n // Skip if already queued\n if (networkToSwitchTo === selectedNetworkConfigId) {\n return;\n }\n\n // Check if the selected capability config has chainId (only EVM chains support network switching)\n const selectedNetworkConfig = selectedCapability.networkConfig;\n if (!('chainId' in selectedNetworkConfig) || !walletChainId) {\n return;\n }\n\n const targetChainId = Number(selectedNetworkConfig.chainId);\n if (walletChainId !== targetChainId) {\n logger.info(\n 'useWalletReconnectionHandler',\n `Wallet reconnected with chain ${walletChainId}, but selected network requires ${targetChainId}. Re-queueing switch.`\n );\n onRequeueSwitch(selectedNetworkConfigId);\n }\n }, [\n isConnected,\n walletChainId,\n selectedNetworkConfigId,\n selectedCapability,\n networkToSwitchTo,\n onRequeueSwitch,\n ]);\n}\n","import { QueryClient } from '@tanstack/react-query';\n\nimport type { EcosystemRuntime, NameResolutionError } from '@openzeppelin/ui-types';\n\n/**\n * Cache namespace for a resolution query key. Keeps a forward (name → address)\n * lookup and a reverse (address → name) lookup in disjoint cache entries even\n * when the raw input strings collide. See {@link buildResolutionKey}.\n */\nexport type ResolutionNamespace = 'name' | 'addr';\n\n/**\n * Tunable knobs for the resolution hooks, overridable per-app via\n * `NameResolutionProvider`'s `config` prop and merged over {@link DEFAULT_CONFIG}.\n */\nexport interface ResolutionConfig {\n /** Time a resolved value is considered fresh (no refetch). Maps to react-query `staleTime`. */\n readonly staleTimeMs: number;\n /** Time an unobserved cache entry is retained before GC. Maps to react-query `gcTime`. */\n readonly gcTimeMs: number;\n /** Debounce window (ms) for the forward hook before a settled input becomes the query key. */\n readonly forwardDebounceMs: number;\n /** Debounce window (ms) for the reverse hook. Addresses are pasted/stable, so this defaults to 0. */\n readonly reverseDebounceMs: number;\n /** Max retries for TRANSIENT errors. Definitive negatives are never retried. */\n readonly transientRetryCount: number;\n}\n\n/**\n * Default resolution config. Values sit within the spec's \"seconds-to-minutes\"\n * caching guidance; forward debounce is 300ms (typed char-by-char), reverse is 0.\n */\nexport const DEFAULT_CONFIG: ResolutionConfig = {\n staleTimeMs: 60_000,\n gcTimeMs: 300_000,\n forwardDebounceMs: 300,\n reverseDebounceMs: 0,\n transientRetryCount: 2,\n};\n\n/** Stable prefix for every resolution cache key — namespaces this package's keys. */\nexport const RESOLUTION_KEY_PREFIX = 'oz-name-resolution' as const;\n\n/** `0` when no runtime is mounted; positive ids are assigned per {@link EcosystemRuntime} instance. */\nexport type RuntimeInstanceId = number;\n\nconst runtimeInstanceIds = new WeakMap<object, RuntimeInstanceId>();\nlet nextRuntimeInstanceId = 1;\n\n/**\n * Assign a stable, serializable id to each {@link EcosystemRuntime} object identity.\n * A disposed-and-recreated runtime (e.g. SF-4 opt-in toggle after INV-218 flush) gets\n * a new id so resolution cache keys miss and results refresh (INV-230).\n */\nexport function getRuntimeInstanceId(\n runtime: EcosystemRuntime | null | undefined\n): RuntimeInstanceId {\n if (!runtime) {\n return 0;\n }\n let id = runtimeInstanceIds.get(runtime);\n if (id === undefined) {\n id = nextRuntimeInstanceId;\n nextRuntimeInstanceId += 1;\n runtimeInstanceIds.set(runtime, id);\n }\n return id;\n}\n\n/**\n * Build a network-scoped, namespace-separated cache key (INV-40). A name resolves\n * differently per network and provenance can be chain-scoped, so `networkId` is\n * part of the key; `namespace` keeps forward and reverse lookups disjoint;\n * `runtimeInstanceId` scopes entries to the active capability instance (INV-230).\n *\n * @param namespace - `'name'` (forward) or `'addr'` (reverse).\n * @param networkId - Active network id (`''` when no network is selected).\n * @param normalizedInput - Trimmed + lowercased input (INV-26).\n * @param runtimeInstanceId - Active runtime instance discriminator (`0` when absent).\n * @returns A readonly tuple suitable for a TanStack Query `queryKey`.\n */\nexport function buildResolutionKey(\n namespace: ResolutionNamespace,\n networkId: string,\n normalizedInput: string,\n runtimeInstanceId: RuntimeInstanceId = 0\n): readonly unknown[] {\n return [RESOLUTION_KEY_PREFIX, namespace, networkId, normalizedInput, runtimeInstanceId];\n}\n\n/**\n * Whether a resolution error is transient (worth retrying) versus a definitive,\n * stable answer (INV-47). Exhaustive over SF-1's closed error union with no\n * `default`: adding a new error code to `@openzeppelin/ui-types` will fail to\n * compile here until it is classified — deliberately coupling SF-2 to SF-1 INV-7.\n *\n * @param error - A typed {@link NameResolutionError}.\n * @returns `true` for transient failures (timeout / gateway / adapter), `false`\n * for definitive negatives and unsupported-network answers.\n */\nexport function isTransientError(error: NameResolutionError): boolean {\n switch (error.code) {\n case 'RESOLUTION_TIMEOUT':\n case 'EXTERNAL_GATEWAY_ERROR':\n case 'ADAPTER_ERROR':\n return true;\n case 'NAME_NOT_FOUND':\n case 'ADDRESS_NOT_FOUND':\n case 'UNSUPPORTED_NAME':\n case 'UNSUPPORTED_NETWORK':\n return false;\n }\n}\n\n/**\n * Construct a QueryClient tuned for name resolution: ambient refetch triggers are\n * disabled at the client level (INV-41) so a resolved value never silently\n * changes under the user on window focus or network reconnect.\n */\nexport function createResolutionQueryClient(): QueryClient {\n return new QueryClient({\n defaultOptions: {\n queries: {\n refetchOnWindowFocus: false,\n refetchOnReconnect: false,\n },\n },\n });\n}\n\n/**\n * Process-global key for the default resolution QueryClient. Uses `Symbol.for`\n * so every duplicated module instance (Vite pre-bundling, npm-installed adapters)\n * shares ONE client — the same cross-bundle-singleton pattern as\n * `WalletStateContext`. This is what makes the cache shared with zero wiring\n * (SC-001) and enables cross-instance dedup / warm-cache reuse (INV-33 / INV-37 / INV-48).\n */\nconst DEFAULT_CLIENT_KEY = Symbol.for('@openzeppelin/ui-react/NameResolutionQueryClient');\n\ninterface GlobalWithResolutionClient {\n [DEFAULT_CLIENT_KEY]?: QueryClient;\n}\n\n/**\n * Lazily create (exactly once) and return the process-global resolution\n * QueryClient used when no `NameResolutionProvider` is mounted (INV-48).\n *\n * @returns The shared default resolution QueryClient (stable across calls).\n */\nexport function getDefaultResolutionQueryClient(): QueryClient {\n const globalScope = globalThis as GlobalWithResolutionClient;\n if (!globalScope[DEFAULT_CLIENT_KEY]) {\n globalScope[DEFAULT_CLIENT_KEY] = createResolutionQueryClient();\n }\n return globalScope[DEFAULT_CLIENT_KEY];\n}\n","import type { QueryClient } from '@tanstack/react-query';\nimport { createContext, useContext, type Context } from 'react';\n\nimport {\n DEFAULT_CONFIG,\n getDefaultResolutionQueryClient,\n type ResolutionConfig,\n} from './resolutionConfig';\n\n/**\n * Value carried by {@link NameResolutionContext}: the owned QueryClient (passed\n * explicitly to `useQuery`, never via an ambient `QueryClientProvider`) and the\n * merged resolution config.\n */\nexport interface NameResolutionContextValue {\n /** OWNED resolution QueryClient, passed explicitly to `useQuery` (INV-48). */\n readonly queryClient: QueryClient;\n /** Effective config (Provider overrides merged over {@link DEFAULT_CONFIG}). */\n readonly config: ResolutionConfig;\n}\n\n/**\n * Process-global key for the context object. Uses `Symbol.for` so all duplicated\n * module instances share ONE React context — the same cross-bundle-singleton\n * pattern as `WalletStateContext` (bundler pre-bundling / npm-installed adapters).\n */\nconst CONTEXT_KEY = Symbol.for('@openzeppelin/ui-react/NameResolutionContext');\n\ninterface GlobalWithResolutionContext {\n [CONTEXT_KEY]?: Context<NameResolutionContextValue | null>;\n}\n\nfunction getOrCreateSharedContext(): Context<NameResolutionContextValue | null> {\n const globalScope = globalThis as GlobalWithResolutionContext;\n if (!globalScope[CONTEXT_KEY]) {\n globalScope[CONTEXT_KEY] = createContext<NameResolutionContextValue | null>(null);\n }\n return globalScope[CONTEXT_KEY];\n}\n\n/**\n * React context for resolution. `null` default triggers the module-singleton\n * fallback in {@link useNameResolutionContext} — no Provider is required (INV-48).\n */\nexport const NameResolutionContext = getOrCreateSharedContext();\n\n/**\n * Stable fallback value used when no Provider is mounted. Cached per module so the\n * returned reference is referentially stable across renders (INV-38); it wraps the\n * process-global default client, so caches are still shared even across bundles.\n */\nlet fallbackValue: NameResolutionContextValue | undefined;\n\nfunction getFallbackContextValue(): NameResolutionContextValue {\n if (!fallbackValue) {\n fallbackValue = {\n queryClient: getDefaultResolutionQueryClient(),\n config: DEFAULT_CONFIG,\n };\n }\n return fallbackValue;\n}\n\n/**\n * Read the resolution context, falling back to the zero-wiring default (global\n * singleton client + {@link DEFAULT_CONFIG}) when no `NameResolutionProvider` is\n * mounted (INV-48). Never throws for a missing provider.\n *\n * @returns The active {@link NameResolutionContextValue}.\n */\nexport function useNameResolutionContext(): NameResolutionContextValue {\n return useContext(NameResolutionContext) ?? getFallbackContextValue();\n}\n","import type { NameResolutionError, ResolvedAddress, ResolvedName } from '@openzeppelin/ui-types';\n\n/**\n * Lifecycle status shared by both resolution hooks. `debouncing` is distinct from\n * `loading` so an input field can show a subtle \"typing…\" state separate from an\n * in-flight call (the reverse hook omits `debouncing`; see {@link UseResolveAddressResult}).\n */\nexport type NameResolutionStatus = 'idle' | 'debouncing' | 'loading' | 'resolved' | 'error';\n\n/**\n * Forward-resolution (`useResolveName`) result. A discriminated union keyed on\n * `status` so illegal field combinations — e.g. holding both `data` and `error`,\n * or reading `.data` without narrowing — are unrepresentable (INV-23). This is the\n * component-boundary shadow of SC-004: an unresolved name can never be read as a\n * resolved address.\n */\nexport type UseResolveNameResult =\n | { readonly status: 'idle' }\n | { readonly status: 'debouncing'; readonly name: string }\n | { readonly status: 'loading'; readonly name: string }\n | { readonly status: 'resolved'; readonly name: string; readonly data: ResolvedAddress }\n | {\n readonly status: 'error';\n readonly name: string;\n readonly error: NameResolutionError;\n readonly retry: () => void;\n };\n\n/**\n * Reverse-resolution (`useResolveAddress`) result. No `debouncing` arm — reverse\n * debounce defaults to 0 (addresses are pasted, not typed). A caller-supplied\n * non-zero `debounceMs` surfaces as `loading` rather than widening this type.\n */\nexport type UseResolveAddressResult =\n | { readonly status: 'idle' }\n | { readonly status: 'loading'; readonly address: string }\n | { readonly status: 'resolved'; readonly address: string; readonly data: ResolvedName }\n | {\n readonly status: 'error';\n readonly address: string;\n readonly error: NameResolutionError;\n readonly retry: () => void;\n };\n\n/**\n * Internal, direction-agnostic result the shared engine produces. The public\n * hooks remap the generic `input` field to `name` / `address` (INV-24: the remap\n * keys off the debounced input carried here, never the live prop).\n */\nexport type EngineResult<T> =\n | { readonly status: 'idle' }\n | { readonly status: 'debouncing'; readonly input: string }\n | { readonly status: 'loading'; readonly input: string }\n | { readonly status: 'resolved'; readonly input: string; readonly data: T }\n | {\n readonly status: 'error';\n readonly input: string;\n readonly error: NameResolutionError;\n readonly retry: () => void;\n };\n\n/**\n * Internal error used to bridge SF-1's `ok: false` results (and defensively-caught\n * adapter throws) into TanStack Query's thrown-error channel, carrying the typed\n * {@link NameResolutionError} through so the mapping step can surface it unchanged\n * (INV-43). Never leaks past the hook boundary.\n */\nexport class ResolutionQueryError extends Error {\n readonly resolutionError: NameResolutionError;\n\n /** @param resolutionError - The typed error to carry through react-query. */\n constructor(resolutionError: NameResolutionError) {\n super(`name resolution failed: ${resolutionError.code}`);\n this.name = 'ResolutionQueryError';\n this.resolutionError = resolutionError;\n }\n}\n\n/**\n * Convert any value from react-query's error channel into a typed, closed-union\n * {@link NameResolutionError} (INV-43). A {@link ResolutionQueryError} is\n * unwrapped verbatim; anything else (a react-query internal error) is mapped to\n * `ADAPTER_ERROR` so no untyped throw reaches a component.\n *\n * @param error - The raw `query.error` value (`unknown`).\n * @returns A typed error drawn only from SF-1's closed union.\n */\nexport function toNameResolutionError(error: unknown): NameResolutionError {\n if (error instanceof ResolutionQueryError) {\n return error.resolutionError;\n }\n const message = error instanceof Error ? error.message : String(error);\n return { code: 'ADAPTER_ERROR', message, cause: error };\n}\n\n/**\n * Minimal, React-free view of a settled query's state — the subset the mapping\n * step reads. Keeps {@link mapSettledQuery} unit-testable without react-query.\n */\nexport interface MappableQueryState<T> {\n readonly isSuccess: boolean;\n readonly isError: boolean;\n readonly data: T | undefined;\n readonly error: unknown;\n}\n\n/**\n * Map a settled query's state to exactly one non-gate {@link EngineResult} arm\n * (INV-42, INV-44). The `input` echoed here is the debounced value the query was\n * keyed on, so `data`/`error` are always paired with the input that produced them\n * (INV-24). Success without data degrades to `loading` rather than a resolved arm\n * missing its payload.\n *\n * @param input - The debounced, normalized input keying this query.\n * @param query - The settled query state.\n * @param retry - Bound `refetch` for the `error` arm (INV-34).\n * @returns The `resolved`, `error`, or `loading` arm.\n */\nexport function mapSettledQuery<T>(\n input: string,\n query: MappableQueryState<T>,\n retry: () => void\n): EngineResult<T> {\n if (query.isSuccess && query.data !== undefined) {\n return { status: 'resolved', input, data: query.data };\n }\n if (query.isError) {\n return { status: 'error', input, error: toNameResolutionError(query.error), retry };\n }\n return { status: 'loading', input };\n}\n","import { useQuery } from '@tanstack/react-query';\nimport { useContext, useEffect, useRef, useState } from 'react';\n\nimport type {\n EcosystemRuntime,\n NameResolutionCapability,\n ResolutionResult,\n} from '@openzeppelin/ui-types';\nimport { logger } from '@openzeppelin/ui-utils';\n\nimport { WalletStateContext } from '../WalletStateContext';\nimport { useNameResolutionContext } from './NameResolutionContext';\nimport {\n buildResolutionKey,\n getRuntimeInstanceId,\n isTransientError,\n type ResolutionNamespace,\n} from './resolutionConfig';\nimport { mapSettledQuery, ResolutionQueryError, type EngineResult } from './resolutionState';\nimport type { NetworkRuntimeSource } from './useNetworkRuntimeSource';\n\nconst LOG_SCOPE = 'useResolutionEngine';\n\n/** No-op retry for synthesized errors that never entered the query path (INV-45). */\nconst NOOP_RETRY = (): void => {};\n\n/**\n * Parameters for {@link useResolutionEngine}. The public hooks bind these to select\n * direction, debounce default, cache namespace, and the capability method.\n */\nexport interface ResolutionEngineParams<T> {\n /** Raw input; `null` / `undefined` / empty / whitespace-only gate to `idle` (INV-27). */\n readonly input: string | null | undefined;\n /** Cache namespace — keeps forward and reverse lookups disjoint (INV-40). */\n readonly namespace: ResolutionNamespace;\n /** Concrete debounce window (ms); `<= 0` disables debouncing (INV-29). */\n readonly debounceMs: number;\n /** Concrete enabled flag; `false` forces `idle` with no resolution (INV-28). */\n readonly enabled: boolean;\n /**\n * Whether to attempt resolution for a given normalized input. Forward binds\n * `cap.isValidName` → `idle` (never `error`) for non-names (INV-32); reverse\n * binds `() => true`.\n */\n readonly shouldAttempt: (cap: NameResolutionCapability, normalizedInput: string) => boolean;\n /** The directional method, or `undefined` when the adapter omits it (→ INV-45). */\n readonly getMethod: (\n cap: NameResolutionCapability\n ) => ((input: string) => Promise<ResolutionResult<T>>) | undefined;\n /**\n * When set, resolves against this runtime instead of the wallet-global active\n * runtime. Used by network-scoped hooks (e.g. address-book dropdown).\n */\n readonly runtimeSource?: NetworkRuntimeSource;\n}\n\n/**\n * Shared engine behind `useResolveName` / `useResolveAddress`. Owns input\n * normalization, debouncing, the `useQuery` wiring (explicit-client form), the\n * capability-absence gate, and the mapping to a direction-agnostic\n * {@link EngineResult}. Not exported from the package.\n */\nexport function useResolutionEngine<T>(params: ResolutionEngineParams<T>): EngineResult<T> {\n const { input, namespace, debounceMs, enabled, shouldAttempt, getMethod, runtimeSource } = params;\n\n // Soft read — never throw. Mirrors `useRuntimeNameResolver`: read the context\n // directly (not `useWalletState()`, which throws) so wallet-less consumers\n // degrade to idle / UNSUPPORTED_NETWORK instead of crashing the tree.\n const walletState = useContext(WalletStateContext);\n const activeRuntime: EcosystemRuntime | null = runtimeSource\n ? runtimeSource.runtime\n : (walletState?.activeRuntime ?? null);\n const activeNetworkId = runtimeSource\n ? runtimeSource.networkId\n : (walletState?.activeNetworkId ?? null);\n const isRuntimeLoading = runtimeSource\n ? runtimeSource.isRuntimeLoading\n : (walletState?.isRuntimeLoading ?? false);\n const { queryClient, config } = useNameResolutionContext();\n\n // INV-26: normalize exactly once (trim, then lowercase). The trimmed original-case\n // form is retained for the reverse adapter argument (checksum preservation); the\n // lowercased form is the single value used by the empty gate, the attempt gate,\n // and the cache key.\n const trimmedLive = (input ?? '').trim();\n const normalizedLive = trimmedLive.toLowerCase();\n\n // INV-35 + INV-37: seed the debounced copy to the initial value on mount (a warm\n // cache is not gated behind a debounce window — SC-002), then debounce only\n // subsequent changes; the effect cleanup clears a pending timer on change/unmount.\n const [debouncedTrimmed, setDebouncedTrimmed] = useState(trimmedLive);\n const seededRef = useRef(false);\n\n useEffect(() => {\n if (!seededRef.current) {\n seededRef.current = true;\n return; // mount value already seeded via useState — no timer, no debouncing window\n }\n if (debounceMs <= 0) {\n setDebouncedTrimmed(trimmedLive);\n return;\n }\n const timer = setTimeout(() => setDebouncedTrimmed(trimmedLive), debounceMs);\n return () => clearTimeout(timer);\n }, [trimmedLive, debounceMs]);\n\n const effectiveTrimmed = debounceMs <= 0 ? trimmedLive : debouncedTrimmed;\n const normalizedKey = effectiveTrimmed.toLowerCase();\n\n const capability = activeRuntime?.nameResolution;\n // Canonical selected-network id (INV-40 key scoping, INV-49 UNSUPPORTED_NETWORK payload).\n // `''` when no network is selected — a valid closed-union payload (INV-49).\n const networkId = activeNetworkId ?? '';\n const runtimeInstanceId = getRuntimeInstanceId(activeRuntime);\n const method = capability ? getMethod(capability) : undefined;\n\n const liveAttemptable = capability ? shouldAttempt(capability, normalizedLive) : false;\n const keyAttemptable = capability ? shouldAttempt(capability, normalizedKey) : false;\n\n // The query is enabled only for a settled, attemptable, resolvable input on a\n // capability that exposes the directional method.\n const queryEnabled =\n enabled &&\n trimmedLive !== '' &&\n capability != null &&\n method != null &&\n normalizedKey !== '' &&\n keyAttemptable;\n\n const query = useQuery<T, Error>(\n {\n queryKey: buildResolutionKey(namespace, networkId, normalizedKey, runtimeInstanceId),\n queryFn: async (): Promise<T> => {\n if (!method) {\n // Unreachable: queryEnabled requires a defined method. Guarded (no `!`).\n throw new ResolutionQueryError({\n code: 'ADAPTER_ERROR',\n message: 'resolution method unexpectedly missing',\n });\n }\n // INV-26 (reverse checksum): forward sends the lowercased name; reverse sends\n // the trimmed original-case address while the cache key stays lowercased.\n const adapterArg = namespace === 'addr' ? effectiveTrimmed : normalizedKey;\n\n let result: ResolutionResult<T>;\n try {\n result = await method(adapterArg);\n } catch (cause) {\n // INV-43 backstop: an adapter that throws instead of returning ok:false\n // (SF-1 INV-8 violation). Log once at the throw site, then convert to the\n // closed-union ADAPTER_ERROR so nothing escapes into a render / error boundary.\n logger.warn(LOG_SCOPE, 'resolution method threw instead of returning ok:false', cause);\n throw new ResolutionQueryError({\n code: 'ADAPTER_ERROR',\n message: cause instanceof Error ? cause.message : String(cause),\n cause,\n });\n }\n if (!result.ok) {\n // INV-43: bridge SF-1's ok:false into react-query's thrown-error channel.\n throw new ResolutionQueryError(result.error);\n }\n return result.value;\n },\n enabled: queryEnabled,\n staleTime: config.staleTimeMs, // INV-50 / SC-002\n gcTime: config.gcTimeMs, // INV-39\n // INV-41: resolution never fires from ambient events (belt-and-suspenders with\n // the client-level defaults, in case a consumer injects their own client).\n refetchOnWindowFocus: false,\n refetchOnReconnect: false,\n // INV-47: transient errors retry up to the cap; definitive negatives never retry.\n // failureCount is 0-based at this call site, so `< transientRetryCount` yields\n // exactly `1 + transientRetryCount` attempts.\n retry: (failureCount, error) =>\n (error instanceof ResolutionQueryError ? isTransientError(error.resolutionError) : true) &&\n failureCount < config.transientRetryCount,\n },\n queryClient // INV-48: explicit-client form — no ambient QueryClientProvider required.\n );\n\n // --- Gate → EngineResult (a total function of gate state × query state, INV-31) ---\n\n if (!enabled) {\n return { status: 'idle' }; // INV-28\n }\n if (trimmedLive === '') {\n return { status: 'idle' }; // INV-27\n }\n\n if (capability) {\n if (!liveAttemptable) {\n return { status: 'idle' }; // INV-32: forward non-name → idle, never error\n }\n if (!method) {\n // INV-45: capability present but directional method absent → UNSUPPORTED_NETWORK.\n return unsupportedNetwork(normalizedLive, networkId);\n }\n if (normalizedKey !== normalizedLive) {\n return { status: 'debouncing', input: normalizedLive }; // INV-31: pending debounce window\n }\n } else {\n // INV-46: runtime still loading with a resolvable input → loading, not an error flash.\n if (isRuntimeLoading) {\n return { status: 'loading', input: normalizedLive };\n }\n // INV-45 / INV-49: no runtime (settled) → UNSUPPORTED_NETWORK (networkId may be '').\n return unsupportedNetwork(normalizedLive, networkId);\n }\n\n // Settled and keyed on the debounced input — surface the query's state (INV-42, INV-44).\n const retry = (): void => {\n void query.refetch(); // INV-34: one refetch per call, current input, ignores staleTime\n };\n return mapSettledQuery(normalizedKey, query, retry);\n}\n\n/**\n * Synthesize an `UNSUPPORTED_NETWORK` error arm without touching react-query\n * (INV-45). Drawn from SF-1's closed union so it is indistinguishable from an\n * adapter-returned `UNSUPPORTED_NETWORK`; `networkId` may be `''` (INV-49). Retry\n * is a no-op — there is no query to refetch.\n */\nfunction unsupportedNetwork<T>(input: string, networkId: string): EngineResult<T> {\n return {\n status: 'error',\n input,\n error: { code: 'UNSUPPORTED_NETWORK', networkId },\n retry: NOOP_RETRY,\n };\n}\n","import type { ResolvedAddress } from '@openzeppelin/ui-types';\n\nimport { useNameResolutionContext } from './NameResolutionContext';\nimport { type EngineResult, type UseResolveNameResult } from './resolutionState';\nimport { useResolutionEngine } from './useResolutionEngine';\n\n/** Options for {@link useResolveName}. */\nexport interface UseResolveNameOptions {\n /** Override the forward debounce window (ms). Default: `config.forwardDebounceMs` (300). */\n readonly debounceMs?: number;\n /** When `false`, the hook stays `idle` and issues no resolution. Default `true`. */\n readonly enabled?: boolean;\n}\n\n/**\n * Forward-resolve a name to an address. Soft-reads the active runtime from\n * `WalletStateContext` (never throws when no provider is mounted — degrades to\n * idle / UNSUPPORTED_NETWORK) and calls `runtime.nameResolution?.resolveName`.\n * Debounces `name`, caches per (network, name) via the owned QueryClient, and is\n * protected against out-of-order responses (distinct inputs → distinct query keys).\n *\n * Returns a discriminated union keyed on `status`; it never throws for expected\n * failures (they surface in the `error` arm) and never pairs a name with a\n * different name's resolved address (INV-24).\n *\n * @param name - The name to resolve. `null` / empty / a value failing the\n * adapter's `isValidName` yields `status: 'idle'` (never `error`).\n * @param options - `debounceMs` / `enabled` overrides.\n * @returns The current {@link UseResolveNameResult}.\n */\nexport function useResolveName(\n name: string | null | undefined,\n options?: UseResolveNameOptions\n): UseResolveNameResult {\n const { config } = useNameResolutionContext();\n // INV-29: resolve option defaults at the hook boundary — the engine sees concrete values.\n const debounceMs = options?.debounceMs ?? config.forwardDebounceMs;\n const enabled = options?.enabled ?? true;\n\n const engine = useResolutionEngine<ResolvedAddress>({\n input: name,\n namespace: 'name',\n debounceMs,\n enabled,\n shouldAttempt: (cap, normalized) => cap.isValidName(normalized), // INV-32\n getMethod: (cap) => cap.resolveName,\n });\n\n return toNameResult(engine);\n}\n\n/**\n * Remap the engine's generic `input` to `name` (INV-24: keyed off the debounced\n * input carried in the engine result, not the live prop). Exhaustive over the\n * union so a new status cannot silently fall through (INV-23 / INV-42).\n */\nfunction toNameResult(engine: EngineResult<ResolvedAddress>): UseResolveNameResult {\n switch (engine.status) {\n case 'idle':\n return { status: 'idle' };\n case 'debouncing':\n return { status: 'debouncing', name: engine.input };\n case 'loading':\n return { status: 'loading', name: engine.input };\n case 'resolved':\n return { status: 'resolved', name: engine.input, data: engine.data };\n case 'error':\n return { status: 'error', name: engine.input, error: engine.error, retry: engine.retry };\n }\n}\n","import type { ResolvedName } from '@openzeppelin/ui-types';\n\nimport { type EngineResult, type UseResolveAddressResult } from './resolutionState';\n\n/**\n * Remap the engine's generic `input` to `address` (INV-24). A `debouncing` arm —\n * only reachable when a caller passes a non-zero reverse `debounceMs` — collapses\n * to `loading` so {@link UseResolveAddressResult} keeps no `debouncing` variant.\n */\nexport function mapAddressEngineResult(\n engine: EngineResult<ResolvedName>\n): UseResolveAddressResult {\n switch (engine.status) {\n case 'idle':\n return { status: 'idle' };\n case 'debouncing':\n case 'loading':\n return { status: 'loading', address: engine.input };\n case 'resolved':\n return { status: 'resolved', address: engine.input, data: engine.data };\n case 'error':\n return { status: 'error', address: engine.input, error: engine.error, retry: engine.retry };\n }\n}\n","import { useContext } from 'react';\n\nimport type { EcosystemRuntime, NetworkConfig } from '@openzeppelin/ui-types';\n\nimport { RuntimeContext } from '../AdapterContext';\n\n/**\n * Runtime source for a specific network, loaded through {@link RuntimeProvider}'s\n * registry without mutating the wallet's active network.\n */\nexport interface NetworkRuntimeSource {\n readonly runtime: EcosystemRuntime | null;\n readonly networkId: string;\n readonly isRuntimeLoading: boolean;\n}\n\n/**\n * Soft-reads {@link RuntimeContext} (never throws) and requests the registry\n * runtime for `network`. Call during render so loading state updates propagate.\n *\n * Does not mutate {@link WalletStateProvider}'s active network — wallet-global\n * runtime is never consulted when a scoped network is supplied (INV-154).\n */\nexport function useNetworkRuntimeSource(network: NetworkConfig | null): NetworkRuntimeSource {\n const runtimeContext = useContext(RuntimeContext);\n\n if (!network) {\n return { runtime: null, networkId: '', isRuntimeLoading: false };\n }\n\n if (!runtimeContext) {\n return { runtime: null, networkId: network.id, isRuntimeLoading: false };\n }\n\n const { runtime, isLoading } = runtimeContext.getRuntimeForNetwork(network);\n\n return {\n runtime,\n networkId: network.id,\n isRuntimeLoading: isLoading,\n };\n}\n","import type { NetworkConfig, ResolvedName } from '@openzeppelin/ui-types';\n\nimport { mapAddressEngineResult } from './mapAddressEngineResult';\nimport { useNameResolutionContext } from './NameResolutionContext';\nimport { type UseResolveAddressResult } from './resolutionState';\nimport { useNetworkRuntimeSource } from './useNetworkRuntimeSource';\nimport { useResolutionEngine } from './useResolutionEngine';\n\n/** Options for {@link useResolveAddress}. */\nexport interface UseResolveAddressOptions {\n /** Override the reverse debounce window (ms). Default: `config.reverseDebounceMs` (0). */\n readonly debounceMs?: number;\n /** When `false`, the hook stays `idle` and issues no resolution. Default `true`. */\n readonly enabled?: boolean;\n /**\n * When set, reverse-resolve via {@link RuntimeProvider} for this network\n * instead of the wallet-global active runtime.\n */\n readonly network?: NetworkConfig;\n}\n\n/**\n * Reverse-resolve an address to a name. Soft-reads the active runtime from\n * `WalletStateContext` (never throws when no provider is mounted — degrades to\n * idle / UNSUPPORTED_NETWORK) and calls `runtime.nameResolution?.resolveAddress`.\n * Pass `options.network` to scope resolution to a specific network without\n * changing the wallet's active network. Caches per (network, address) via the\n * owned QueryClient. Not debounced by default — addresses are pasted, not\n * typed char-by-char.\n *\n * Applies no client-side address-shape check (INV-32): resolution is attempted on\n * any non-empty input and malformed addresses are rejected via the adapter's typed\n * error union. Address-shape validation is SF-3's concern.\n *\n * @param address - The address to reverse-resolve. `null` / empty yields\n * `status: 'idle'`.\n * @param options - `debounceMs` / `enabled` / `network` overrides.\n * @returns The current {@link UseResolveAddressResult}.\n */\nexport function useResolveAddress(\n address: string | null | undefined,\n options?: UseResolveAddressOptions\n): UseResolveAddressResult {\n const { config } = useNameResolutionContext();\n // INV-29: resolve option defaults at the hook boundary — the engine sees concrete values.\n const debounceMs = options?.debounceMs ?? config.reverseDebounceMs;\n const enabled = options?.enabled ?? true;\n const networkSource = useNetworkRuntimeSource(options?.network ?? null);\n const useNetworkScoped = options?.network != null;\n\n const engine = useResolutionEngine<ResolvedName>({\n input: address,\n namespace: 'addr',\n debounceMs,\n enabled,\n shouldAttempt: () => true, // INV-32: reverse attempts on any non-empty input\n getMethod: (cap) => cap.resolveAddress,\n runtimeSource: useNetworkScoped ? networkSource : undefined,\n });\n\n return mapAddressEngineResult(engine);\n}\n","import type { QueryClient } from '@tanstack/react-query';\nimport { useMemo, type ReactNode } from 'react';\n\nimport { NameResolutionContext, type NameResolutionContextValue } from './NameResolutionContext';\nimport {\n DEFAULT_CONFIG,\n getDefaultResolutionQueryClient,\n type ResolutionConfig,\n} from './resolutionConfig';\n\n/**\n * Props for {@link NameResolutionProvider}. Mounting the Provider is OPTIONAL —\n * absent it, hooks use the module-singleton client and {@link DEFAULT_CONFIG}\n * (INV-48). Mount it only to override config or share the cache with an existing\n * QueryClient.\n */\nexport interface NameResolutionProviderProps {\n readonly children: ReactNode;\n /** Partial override of the default config (TTLs, debounce, retry count). Merged per-field. */\n readonly config?: Partial<ResolutionConfig>;\n /**\n * Inject a QueryClient to SHARE the resolution cache with the app / adapter\n * client (unified devtools / persistence). Omit for an isolated owned client.\n */\n readonly queryClient?: QueryClient;\n}\n\n/**\n * Optional provider that overrides resolution config and/or the QueryClient for\n * its subtree. It does NOT render a `QueryClientProvider`: the client is passed\n * explicitly to `useQuery` (INV-48), so no ambient react-query provider is needed.\n *\n * Config is merged field-by-field over {@link DEFAULT_CONFIG} (INV-30), and the\n * context value is memoized so a Provider re-render does not cascade to every hook\n * (INV-38).\n */\nexport function NameResolutionProvider({\n children,\n config,\n queryClient,\n}: NameResolutionProviderProps): ReactNode {\n const value = useMemo<NameResolutionContextValue>(\n () => ({\n queryClient: queryClient ?? getDefaultResolutionQueryClient(),\n // INV-30: shallow per-field merge — a partial override never nulls unset knobs.\n config: { ...DEFAULT_CONFIG, ...config },\n }),\n [queryClient, config]\n );\n\n return <NameResolutionContext.Provider value={value}>{children}</NameResolutionContext.Provider>;\n}\n","import type { QueryClient } from '@tanstack/react-query';\n\nimport type {\n NameResolutionCapability,\n NameResolver,\n ResolutionResult,\n ResolvedAddress,\n} from '@openzeppelin/ui-types';\n\nimport {\n buildResolutionKey,\n isTransientError,\n type ResolutionConfig,\n type RuntimeInstanceId,\n} from './resolutionConfig';\nimport { ResolutionQueryError, toNameResolutionError } from './resolutionState';\n\nconst EMPTY_RESOLVER: NameResolver = {};\n\n/**\n * Projects a {@link NameResolutionCapability} into the SF-3 {@link NameResolver}\n * seam with SF-2 cache-key parity (INV-119).\n */\nexport function createNameResolverFromCapability(\n capability: NameResolutionCapability | undefined,\n networkId: string,\n runtimeInstanceId: RuntimeInstanceId,\n queryClient: QueryClient,\n config: ResolutionConfig\n): NameResolver {\n if (!capability) {\n return EMPTY_RESOLVER;\n }\n\n const resolveNameMethod = capability.resolveName;\n\n return {\n isValidName: (name: string): boolean => capability.isValidName(name),\n\n resolveName: resolveNameMethod\n ? async (name: string): Promise<ResolutionResult<ResolvedAddress>> => {\n // Normalize exactly as the SF-2 engine (trim, then lowercase) so the\n // cache key — and the echoed `value.name` — match the hook path (INV-119).\n const normalized = name.trim().toLowerCase();\n try {\n const value = await queryClient.fetchQuery<ResolvedAddress, Error>({\n queryKey: buildResolutionKey('name', networkId, normalized, runtimeInstanceId),\n queryFn: async (): Promise<ResolvedAddress> => {\n // Same ok:false → thrown-error bridge as SF-2's engine, so a\n // field-initiated fetch and a hook-initiated fetch populate the\n // cache identically.\n let result: ResolutionResult<ResolvedAddress>;\n try {\n result = await resolveNameMethod(normalized);\n } catch (cause) {\n // Backstop for an adapter that throws instead of returning\n // ok:false (SF-1 INV-8 violation) — keep the no-throw contract.\n throw new ResolutionQueryError({\n code: 'ADAPTER_ERROR',\n message: cause instanceof Error ? cause.message : String(cause),\n cause,\n });\n }\n if (!result.ok) {\n throw new ResolutionQueryError(result.error);\n }\n return result.value;\n },\n staleTime: config.staleTimeMs,\n gcTime: config.gcTimeMs,\n // Mirror SF-2's retry policy: transient errors up to the cap,\n // definitive negatives never.\n retry: (failureCount, error) =>\n (error instanceof ResolutionQueryError\n ? isTransientError(error.resolutionError)\n : true) && failureCount < config.transientRetryCount,\n });\n return { ok: true, value };\n } catch (error) {\n // Un-bridge back into the seam's no-throw ResolutionResult contract.\n return { ok: false, error: toNameResolutionError(error) };\n }\n }\n : undefined,\n };\n}\n","import { useContext, useMemo } from 'react';\n\nimport type { NameResolver, NetworkConfig } from '@openzeppelin/ui-types';\n\nimport { WalletStateContext } from '../WalletStateContext';\nimport { createNameResolverFromCapability } from './createNameResolver';\nimport { useNameResolutionContext } from './NameResolutionContext';\nimport { getRuntimeInstanceId } from './resolutionConfig';\nimport { useNetworkRuntimeSource } from './useNetworkRuntimeSource';\n\n/**\n * Stable empty resolver returned when no runtime / capability is available.\n * No methods ⇒ the consuming field treats a typed name as unsupported and\n * surfaces `UNSUPPORTED_NETWORK` (INV-119) while hex behavior stays legacy.\n */\nconst EMPTY_RESOLVER: NameResolver = {};\n\n/**\n * Project a runtime's `NameResolutionCapability` into the injected\n * {@link NameResolver} seam consumed by `@openzeppelin/ui-components` (SF-3, D3).\n * Mounted ambiently by the renderer:\n *\n * ```tsx\n * <NameResolverProvider {...useRuntimeNameResolver()}>{form}</NameResolverProvider>\n * ```\n *\n * Pass `scopedNetwork` to resolve against a specific network via\n * {@link RuntimeProvider}'s registry without changing {@link WalletStateProvider}'s\n * active network (e.g. address-book Add Alias dropdown).\n *\n * Each imperative call is backed by SF-2's **owned** resolution `QueryClient`\n * via `fetchQuery`, reusing SF-2's exact query-key convention\n * (`buildResolutionKey('name', networkId, normalizedName)`) — so a name\n * resolved through the field and the same name resolved by a bare\n * `useResolveName` hit the SAME cache entry: shared cache, dedupe, and\n * out-of-order safety, never a parallel cache (INV-119).\n *\n * Degradation, in order (all method-omission, never a throw):\n * - No `WalletStateProvider` mounted (wallet path) or no `RuntimeProvider`\n * (network path) → empty resolver. The context is read directly (not via\n * `useWalletState()`, which throws) so the ambient renderer mount stays safe\n * for wallet-less usage.\n * - No active runtime, or runtime without the capability → empty resolver.\n * - Capability without `resolveName` → `isValidName` only; forward unsupported.\n *\n * When the capability is present but the network itself is unsupported, the\n * adapter's `resolveName` resolves `{ ok: false, error: UNSUPPORTED_NETWORK }`\n * and that result passes through unchanged.\n *\n * @returns A referentially stable {@link NameResolver} for the active runtime.\n */\nexport function useRuntimeNameResolver(scopedNetwork?: NetworkConfig): NameResolver {\n const walletState = useContext(WalletStateContext);\n const { queryClient, config } = useNameResolutionContext();\n const networkSource = useNetworkRuntimeSource(scopedNetwork ?? null);\n const useNetworkScoped = scopedNetwork != null;\n\n const activeRuntime = useNetworkScoped\n ? networkSource.runtime\n : (walletState?.activeRuntime ?? null);\n const capability = activeRuntime?.nameResolution;\n // Canonical selected-network id — part of the shared cache key (INV-119);\n // '' when no network is selected, mirroring the SF-2 engine.\n const networkId = useNetworkScoped\n ? networkSource.networkId\n : (walletState?.activeNetworkId ?? '');\n const runtimeInstanceId = getRuntimeInstanceId(activeRuntime);\n const isRuntimeLoading = useNetworkScoped\n ? networkSource.isRuntimeLoading\n : (walletState?.isRuntimeLoading ?? false);\n\n return useMemo<NameResolver>(() => {\n if (!capability) {\n return EMPTY_RESOLVER;\n }\n\n return createNameResolverFromCapability(\n capability,\n networkId,\n runtimeInstanceId,\n queryClient,\n config\n );\n }, [capability, networkId, runtimeInstanceId, queryClient, config, isRuntimeLoading]);\n}\n","import type { NameResolver, NetworkConfig } from '@openzeppelin/ui-types';\n\nimport { useRuntimeNameResolver } from './useRuntimeNameResolver';\n\n/**\n * @deprecated Pass the network to {@link useRuntimeNameResolver} instead.\n */\nexport function useNetworkNameResolver(network: NetworkConfig | null): NameResolver {\n return useRuntimeNameResolver(network ?? undefined);\n}\n","import type { NetworkConfig } from '@openzeppelin/ui-types';\n\nimport { type UseResolveAddressResult } from './resolutionState';\nimport { useResolveAddress, type UseResolveAddressOptions } from './useResolveAddress';\n\n/**\n * @deprecated Pass `network` in {@link UseResolveAddressOptions} to {@link useResolveAddress}.\n */\nexport function useNetworkResolveAddress(\n address: string | null | undefined,\n network: NetworkConfig | null | undefined,\n options?: Omit<UseResolveAddressOptions, 'network'>\n): UseResolveAddressResult {\n return useResolveAddress(address, network != null ? { ...options, network } : options);\n}\n","import type { CreateRuntimeOptions, NameResolutionRuntimeOptions } from '@openzeppelin/ui-types';\n\n/** Safe default: miss-fallback OFF; no uiKit override. INV-204 */\nexport const DEFAULT_RUNTIME_CREATION_CONFIG: CreateRuntimeOptions = {};\n\n/**\n * Normalizes the opt-in flag to the adapter's strict-enable contract.\n * UIKit uses this at the threading boundary so `false` and `undefined` never\n * emit `enableMainnetL1MissFallback: false` into adapter options (absent = OFF).\n */\nexport function isMainnetL1MissFallbackEnabled(options?: NameResolutionRuntimeOptions): boolean {\n return options?.enableMainnetL1MissFallback === true; // INV-206\n}\n\n/**\n * Builds the `createRuntime` options bag for adapter threading.\n * When OFF, `nameResolution` is omitted (INV-207). When ON, spreads only\n * `{ enableMainnetL1MissFallback: true }` (INV-208).\n */\nexport function buildCreateRuntimeOptions(options?: CreateRuntimeOptions): CreateRuntimeOptions {\n return {\n ...(options?.uiKit !== undefined ? { uiKit: options.uiKit } : {}),\n ...(isMainnetL1MissFallbackEnabled(options?.nameResolution)\n ? { nameResolution: { enableMainnetL1MissFallback: true as const } }\n : {}),\n };\n}\n","import type {\n CreateRuntimeOptions,\n EcosystemExport,\n EcosystemRuntime,\n NetworkConfig,\n ProfileName,\n} from '@openzeppelin/ui-types';\n\nimport { buildCreateRuntimeOptions } from './runtimeCreationConfig';\n\nexport interface ResolveRuntimeParams {\n readonly profile: ProfileName;\n /** Merged over {@link DEFAULT_RUNTIME_CREATION_CONFIG} per field. */\n readonly options?: CreateRuntimeOptions;\n}\n\nexport type ResolveRuntimeFn = (networkConfig: NetworkConfig) => Promise<EcosystemRuntime>;\n\n/**\n * Build a `resolveRuntime` callback for {@link RuntimeProvider} that forwards\n * `CreateRuntimeOptions` to `ecosystemDefinition.createRuntime`.\n *\n * Does not cache — {@link RuntimeProvider} owns the per-network singleton registry.\n */\nexport function createResolveRuntime(\n ecosystemDefinition: EcosystemExport,\n params: ResolveRuntimeParams\n): ResolveRuntimeFn {\n const { profile, options } = params;\n const mergedOptions = buildCreateRuntimeOptions(options);\n\n return (networkConfig: NetworkConfig) =>\n Promise.resolve(ecosystemDefinition.createRuntime(profile, networkConfig, mergedOptions));\n}\n","import { useMemo } from 'react';\n\nimport type { EcosystemExport } from '@openzeppelin/ui-types';\n\nimport {\n createResolveRuntime,\n type ResolveRuntimeFn,\n type ResolveRuntimeParams,\n} from './createResolveRuntime';\n\n/**\n * Memoized {@link createResolveRuntime} for React apps. Recompute when\n * `ecosystemDefinition`, `profile`, or opt-in posture change (INV-217, INV-222).\n *\n * Changing the returned function identity MUST trigger runtime registry flush\n * in {@link RuntimeProvider} (INV-218).\n */\nexport function useResolveRuntime(\n ecosystemDefinition: EcosystemExport,\n params: ResolveRuntimeParams\n): ResolveRuntimeFn {\n const { profile, options } = params;\n const uiKit = options?.uiKit;\n const enableMainnetL1MissFallback = options?.nameResolution?.enableMainnetL1MissFallback;\n\n return useMemo(\n () =>\n createResolveRuntime(ecosystemDefinition, {\n profile,\n options: {\n ...(uiKit !== undefined ? { uiKit } : {}),\n ...(enableMainnetL1MissFallback === true\n ? { nameResolution: { enableMainnetL1MissFallback: true } }\n : {}),\n },\n }),\n [ecosystemDefinition, profile, uiKit, enableMainnetL1MissFallback]\n );\n}\n","import React, { useState } from 'react';\n\nimport { Button } from '@openzeppelin/ui-components';\nimport type { BaseComponentProps } from '@openzeppelin/ui-types';\nimport { cn, logger } from '@openzeppelin/ui-utils';\n\nimport { useWalletState } from '../hooks/WalletStateContext';\n\n/**\n * Props for the WalletConnectionUI component.\n */\nexport interface WalletConnectionUIProps {\n /** Additional CSS classes to apply to the wrapper container */\n className?: string;\n /** Props forwarded to the ConnectButton component */\n connectButtonProps?: BaseComponentProps;\n /** Props forwarded to the AccountDisplay component */\n accountDisplayProps?: BaseComponentProps;\n /** Props forwarded to the NetworkSwitcher component */\n networkSwitcherProps?: BaseComponentProps;\n}\n\n/**\n * Component that displays wallet connection UI components\n * provided by the active adapter.\n *\n * @example\n * ```tsx\n * // Basic usage\n * <WalletConnectionUI />\n *\n * // With custom styling for the connect button\n * <WalletConnectionUI\n * connectButtonProps={{ size: \"lg\", variant: \"outline\", fullWidth: true }}\n * />\n *\n * // Customizing all components\n * <WalletConnectionUI\n * connectButtonProps={{ size: \"lg\" }}\n * accountDisplayProps={{ size: \"lg\" }}\n * networkSwitcherProps={{ size: \"lg\" }}\n * />\n * ```\n */\nexport const WalletConnectionUI: React.FC<WalletConnectionUIProps> = ({\n className,\n connectButtonProps,\n accountDisplayProps,\n networkSwitcherProps,\n}) => {\n const [isError, setIsError] = useState(false);\n const { activeNetworkConfig, activeRuntime, isRuntimeLoading } = useWalletState();\n const activeUiKit = activeRuntime?.uiKit;\n const isCrossEcosystemTransition = !!(\n isRuntimeLoading &&\n activeRuntime?.networkConfig?.ecosystem &&\n activeNetworkConfig?.ecosystem &&\n activeRuntime.networkConfig.ecosystem !== activeNetworkConfig.ecosystem\n );\n\n if (isCrossEcosystemTransition) {\n return null;\n }\n\n const walletComponents = (() => {\n if (!activeUiKit || typeof activeUiKit.getEcosystemWalletComponents !== 'function') {\n return null;\n }\n\n try {\n return activeUiKit.getEcosystemWalletComponents();\n } catch (error) {\n logger.error('WalletConnectionUI', 'Error getting wallet components:', error);\n setIsError(true);\n return null;\n }\n })();\n\n if (!walletComponents) {\n return null;\n }\n\n const { ConnectButton, AccountDisplay, NetworkSwitcher } = walletComponents;\n\n if (isError) {\n return (\n <div className={cn('flex items-center gap-4', className)}>\n <Button variant=\"destructive\" size=\"sm\" onClick={() => window.location.reload()}>\n Wallet Error - Retry\n </Button>\n </div>\n );\n }\n\n return (\n <div className={cn('flex items-center gap-4', className)}>\n {NetworkSwitcher && <NetworkSwitcher {...networkSwitcherProps} />}\n {AccountDisplay && <AccountDisplay {...accountDisplayProps} />}\n {ConnectButton && <ConnectButton {...connectButtonProps} />}\n </div>\n );\n};\n","import React from 'react';\n\nimport { useWalletState } from '../hooks/WalletStateContext';\nimport { WalletConnectionUI } from './WalletConnectionUI';\n\n/**\n * Component that renders the wallet connection UI.\n * Uses useWalletState to get its data.\n */\nexport const WalletConnectionHeader: React.FC = () => {\n const { isRuntimeLoading } = useWalletState();\n\n if (isRuntimeLoading) {\n return <div className=\"h-9 w-28 animate-pulse rounded bg-muted\"></div>;\n }\n\n return <WalletConnectionUI />;\n};\n","import React, { useEffect, useRef, useState } from 'react';\n\nimport { NetworkCatalogCapability, WalletCapability } from '@openzeppelin/ui-types';\nimport { logger } from '@openzeppelin/ui-utils';\n\nimport { useDerivedAccountStatus } from '../hooks/useDerivedAccountStatus';\nimport { useDerivedSwitchChainStatus } from '../hooks/useDerivedSwitchChainStatus';\n\n/**\n * Props for the NetworkSwitchManager component.\n */\nexport interface NetworkSwitchManagerProps {\n /** Wallet capability for the target network */\n wallet: WalletCapability;\n /** Network catalog capability used to validate the target network id */\n networkCatalog: NetworkCatalogCapability;\n /** The network ID we want to switch to */\n targetNetworkId: string;\n /** Callback when network switch completes (success or error) */\n onNetworkSwitchComplete?: () => void;\n}\n\n/**\n * Component that handles wallet network switching based on the selected network.\n *\n * This component manages the lifecycle of network switching operations,\n * coordinating between the wallet's current chain state and the target network.\n * It's designed to be used in any application that needs seamless wallet network switching.\n *\n * Features:\n * - Automatically initiates network switch when mounted with a target network\n * - Handles EVM chain switching gracefully\n * - No-ops for non-EVM networks that don't support chain switching\n * - Tracks switch attempts to prevent duplicate operations\n * - Provides completion callback for parent components to handle state cleanup\n */\nexport const NetworkSwitchManager: React.FC<NetworkSwitchManagerProps> = ({\n wallet,\n networkCatalog,\n targetNetworkId,\n onNetworkSwitchComplete,\n}) => {\n const isMountedRef = useRef(true);\n const [hasAttemptedSwitch, setHasAttemptedSwitch] = useState(false);\n\n const { isConnected, chainId: currentChainIdFromHook } = useDerivedAccountStatus();\n const {\n switchChain: execSwitchNetwork,\n isSwitching: isSwitchingNetworkViaHook,\n error: switchNetworkError,\n } = useDerivedSwitchChainStatus();\n\n useEffect(() => {\n isMountedRef.current = true;\n logger.info(\n 'NetworkSwitchManager',\n `Mounted with target: ${targetNetworkId}, current attempt status: ${hasAttemptedSwitch}`\n );\n setHasAttemptedSwitch(false);\n return () => {\n logger.info('NetworkSwitchManager', `Unmounting, was for target: ${targetNetworkId}`);\n isMountedRef.current = false;\n };\n // hasAttemptedSwitch is intentionally omitted from deps: we only want to reset when targetNetworkId changes,\n // not when hasAttemptedSwitch itself changes (which would cause unnecessary re-renders)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [targetNetworkId]);\n\n useEffect(() => {\n logger.info('NetworkSwitchManager', 'State Update:', {\n target: targetNetworkId,\n walletNetwork: wallet.networkConfig.id,\n isSwitching: isSwitchingNetworkViaHook,\n hookError: !!switchNetworkError,\n canExec: !!execSwitchNetwork,\n connected: isConnected,\n walletChain: currentChainIdFromHook,\n attempted: hasAttemptedSwitch,\n });\n }, [\n wallet,\n targetNetworkId,\n isSwitchingNetworkViaHook,\n switchNetworkError,\n execSwitchNetwork,\n isConnected,\n currentChainIdFromHook,\n hasAttemptedSwitch,\n ]);\n\n // Main Orchestration & Pre-flight Effect\n useEffect(() => {\n const completeOperation = (\n logMessage?: string,\n options: { notifyComplete?: boolean } = { notifyComplete: true }\n ) => {\n if (logMessage) logger.info('NetworkSwitchManager', logMessage);\n if (options.notifyComplete && isMountedRef.current && onNetworkSwitchComplete)\n onNetworkSwitchComplete();\n if (isMountedRef.current) setHasAttemptedSwitch(false);\n };\n\n if (!execSwitchNetwork) {\n completeOperation('No switchChain function available from hook. Operation halted.', {\n notifyComplete: false,\n });\n return;\n }\n\n if (isSwitchingNetworkViaHook && hasAttemptedSwitch) {\n // If hook is pending AND we initiated this attempt, let completion effect handle it.\n logger.info(\n 'NetworkSwitchManager',\n 'Hook reports switch in progress for current attempt. Waiting...'\n );\n return;\n }\n\n // If hasAttemptedSwitch is true here, but hook is NOT pending, means it completed/errored very fast.\n // The completion effect should pick this up.\n if (hasAttemptedSwitch && !isSwitchingNetworkViaHook) {\n logger.info(\n 'NetworkSwitchManager',\n 'Previous switch attempt concluded. Deferring to completion effect.'\n );\n return;\n }\n\n // If we are here, no attempt has been made for the current targetNetworkId yet, or a previous attempt on a *different* target completed.\n // OR, the hook is not pending from a previous unrelated call.\n // Reset attempt flag for a fresh try if it was from a different context/target.\n // Note: setHasAttemptedSwitch(false) is in mount effect for targetNetworkId change.\n\n // === Pre-flight checks for the current targetNetworkId ===\n const targetNetwork = networkCatalog\n .getNetworks()\n .find((network) => network.id === targetNetworkId);\n if (!targetNetwork) {\n completeOperation(\n `Target network ${targetNetworkId} is not present in the network catalog.`,\n {\n notifyComplete: false,\n }\n );\n return;\n }\n if (wallet.networkConfig.id !== targetNetworkId) {\n completeOperation(\n `CRITICAL: Wallet capability (${wallet.networkConfig.id}) vs Target (${targetNetworkId}) mismatch. Operation halted.`,\n {\n notifyComplete: false,\n }\n );\n return;\n }\n if (!isConnected) {\n completeOperation('Wallet not connected (derived status). Awaiting connection.', {\n notifyComplete: false,\n });\n return;\n }\n if (!('chainId' in wallet.networkConfig)) {\n completeOperation(\n 'Network does not support chain switching (non-EVM). Operation complete (no-op).'\n );\n return;\n }\n const targetChainToBeSwitchedTo = Number(wallet.networkConfig.chainId);\n if (currentChainIdFromHook === targetChainToBeSwitchedTo) {\n completeOperation('Already on correct chain (derived status). Operation complete.');\n return;\n }\n\n const performSwitchActual = () => {\n if (!isMountedRef.current || isSwitchingNetworkViaHook || hasAttemptedSwitch) {\n // If hook became pending, or an attempt was already made and concluded, don't re-issue.\n logger.info(\n 'NetworkSwitchManager',\n `Switch attempt aborted in timeout or already handled. Conditions: isSwitching: ${isSwitchingNetworkViaHook}, hasAttempted: ${hasAttemptedSwitch}`\n );\n return;\n }\n logger.info(\n 'NetworkSwitchManager',\n `Attempting switch to ${targetChainToBeSwitchedTo} via derived hook.`\n );\n setHasAttemptedSwitch(true); // Mark that this specific attempt for this target is now starting\n execSwitchNetwork({ chainId: targetChainToBeSwitchedTo });\n };\n\n const timeoutId = setTimeout(performSwitchActual, 100);\n return () => clearTimeout(timeoutId);\n }, [\n wallet,\n networkCatalog,\n targetNetworkId,\n execSwitchNetwork,\n isSwitchingNetworkViaHook,\n onNetworkSwitchComplete,\n isConnected,\n currentChainIdFromHook,\n hasAttemptedSwitch,\n ]);\n\n // Completion/Error Effect (handles outcomes of an initiated execSwitchNetwork call)\n useEffect(() => {\n if (!isMountedRef.current || !execSwitchNetwork || !hasAttemptedSwitch) return;\n\n // Only act if the hook is NOT pending AND an attempt was made\n if (!isSwitchingNetworkViaHook) {\n let completionMessage = 'Switch hook operation concluded.';\n if (switchNetworkError) {\n logger.error('NetworkSwitchManager', 'Error from derived switch hook:', switchNetworkError);\n completionMessage = 'Switch hook completed with error.';\n } else {\n logger.info('NetworkSwitchManager', 'Derived switch hook completed successfully.');\n }\n if (onNetworkSwitchComplete) onNetworkSwitchComplete();\n if (isMountedRef.current) setHasAttemptedSwitch(false);\n logger.info('NetworkSwitchManager', completionMessage);\n }\n }, [\n isSwitchingNetworkViaHook,\n switchNetworkError,\n execSwitchNetwork,\n hasAttemptedSwitch,\n onNetworkSwitchComplete,\n ]);\n\n return null;\n};\n"],"mappings":"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"}