@openzeppelin/ui-utils 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{index-qy1AQMhr.d.ts → index-BxQFU6OX.d.cts} +109 -3
- package/dist/index-BxQFU6OX.d.cts.map +1 -0
- package/dist/{index-DNIN-Squ.d.cts → index-CgrWFuYK.d.ts} +109 -3
- package/dist/index-CgrWFuYK.d.ts.map +1 -0
- package/dist/index.cjs +158 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +109 -3
- package/dist/index.d.ts +109 -3
- package/dist/index.js +155 -1
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/dist/index-DNIN-Squ.d.cts.map +0 -1
- package/dist/index-qy1AQMhr.d.ts.map +0 -1
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["uuid"],"sources":["../src/contractInputs.ts","../src/requiredInputs.ts","../src/addressNormalization.ts","../src/logger.ts","../src/AppConfigService.ts","../src/UserRpcConfigService.ts","../src/UserExplorerConfigService.ts","../src/UserNetworkServiceConfigService.ts","../src/fieldDefaults.ts","../src/fieldValidation.ts","../src/typeguards.ts","../src/cn.ts","../src/formatting.ts","../src/generateId.ts","../src/validators.ts","../src/async.ts","../src/hash.ts","../src/bytesValidation.ts","../src/bytesConversion.ts","../src/environment.ts","../src/RouterService.ts","../src/AnalyticsService.ts","../src/deepLink.ts","../src/sanitize.ts","../src/access/snapshot.ts","../src/access/errors.ts"],"sourcesContent":["import type { ContractAdapter, FormValues } from '@openzeppelin/ui-types';\n\n/**\n * Returns names of adapter-declared required inputs that are missing/empty in values.\n */\nexport function getMissingRequiredContractInputs(\n adapter: ContractAdapter,\n values: FormValues\n): string[] {\n try {\n const inputs = adapter.getContractDefinitionInputs ? adapter.getContractDefinitionInputs() : [];\n const required = inputs.filter((field: unknown) => {\n const f = field as { validation?: { required?: boolean } };\n return f?.validation?.required === true;\n });\n const missing: string[] = [];\n for (const field of required as Array<{ name?: string; id?: string }>) {\n const key = field.name || field.id || '';\n const raw = (values as Record<string, unknown>)[key];\n if (raw == null) {\n missing.push(key);\n continue;\n }\n if (typeof raw === 'string' && raw.trim().length === 0) {\n missing.push(key);\n }\n }\n return missing;\n } catch {\n return [];\n }\n}\n\n/**\n * True if any adapter-declared required inputs are missing/empty.\n */\nexport function hasMissingRequiredContractInputs(\n adapter: ContractAdapter | null | undefined,\n values: FormValues\n): boolean {\n if (!adapter) return false;\n return getMissingRequiredContractInputs(adapter, values).length > 0;\n}\n","import type { ContractAdapter, FormFieldType, FormValues } from '@openzeppelin/ui-types';\n\ntype RequiredInputSnapshot = Record<string, unknown>;\n\nfunction normalizeSnapshotValue(value: unknown): unknown {\n if (value instanceof File) {\n return {\n name: value.name,\n size: value.size,\n lastModified: value.lastModified,\n };\n }\n\n if (typeof value === 'string') {\n return value.trim();\n }\n\n if (value === undefined) {\n return null;\n }\n\n return value;\n}\n\nfunction extractRequiredFields(adapter: ContractAdapter | null): FormFieldType[] {\n if (!adapter || typeof adapter.getContractDefinitionInputs !== 'function') {\n return [];\n }\n\n try {\n const inputs = adapter.getContractDefinitionInputs() || [];\n return inputs.filter((field) => field.validation?.required);\n } catch {\n return [];\n }\n}\n\n/**\n * Builds a snapshot of required form input values.\n * @param adapter - Contract adapter to get field definitions from\n * @param formValues - Current form values\n * @returns Snapshot of required field values, or null if no required fields\n */\nexport function buildRequiredInputSnapshot(\n adapter: ContractAdapter | null,\n formValues: FormValues | null | undefined\n): RequiredInputSnapshot | null {\n if (!formValues) {\n return null;\n }\n\n const requiredFields = extractRequiredFields(adapter);\n if (requiredFields.length === 0) {\n return null;\n }\n\n const snapshot: RequiredInputSnapshot = {};\n const values = formValues as Record<string, unknown>;\n\n for (const field of requiredFields) {\n const key = field.name || field.id;\n if (!key) continue;\n snapshot[key] = normalizeSnapshotValue(values[key]);\n }\n\n return Object.keys(snapshot).length > 0 ? snapshot : null;\n}\n\n/**\n * Compares two required input snapshots for equality.\n * @param a - First snapshot to compare\n * @param b - Second snapshot to compare\n * @returns True if snapshots are equal, false otherwise\n */\nexport function requiredSnapshotsEqual(\n a: RequiredInputSnapshot | null,\n b: RequiredInputSnapshot | null\n): boolean {\n if (a === b) {\n return true;\n }\n\n if (!a || !b) {\n return false;\n }\n\n const keysA = Object.keys(a).sort();\n const keysB = Object.keys(b).sort();\n\n if (keysA.length !== keysB.length) {\n return false;\n }\n\n for (let i = 0; i < keysA.length; i += 1) {\n if (keysA[i] !== keysB[i]) {\n return false;\n }\n\n const valueA = a[keysA[i]];\n const valueB = b[keysA[i]];\n\n if (\n typeof valueA === 'object' &&\n valueA !== null &&\n typeof valueB === 'object' &&\n valueB !== null\n ) {\n if (JSON.stringify(valueA) !== JSON.stringify(valueB)) {\n return false;\n }\n } else if (valueA !== valueB) {\n return false;\n }\n }\n\n return true;\n}\n","/**\n * Normalizes a contract address by trimming whitespace and converting to lowercase.\n * This is useful for case-insensitive and whitespace-insensitive address comparison.\n *\n * @param address - The address to normalize (string, null, or undefined)\n * @returns The normalized address string, or empty string if input is falsy\n *\n * @example\n * ```ts\n * normalizeAddress(' 0xABC123 ') // Returns '0xabc123'\n * normalizeAddress('0xDEF456') // Returns '0xdef456'\n * normalizeAddress(null) // Returns ''\n * normalizeAddress(undefined) // Returns ''\n * ```\n */\nexport function normalizeAddress(address: string | null | undefined): string {\n if (typeof address === 'string') {\n return address.trim().toLowerCase();\n }\n return '';\n}\n\n/**\n * Compares two addresses after normalization.\n * Returns true if both addresses normalize to the same value.\n *\n * @param address1 - First address to compare\n * @param address2 - Second address to compare\n * @returns True if addresses are equal after normalization\n *\n * @example\n * ```ts\n * addressesEqual(' 0xABC ', '0xabc') // Returns true\n * addressesEqual('0xDEF', '0xABC') // Returns false\n * addressesEqual(null, '') // Returns true\n * ```\n */\nexport function addressesEqual(\n address1: string | null | undefined,\n address2: string | null | undefined\n): boolean {\n return normalizeAddress(address1) === normalizeAddress(address2);\n}\n","/**\n * Logger utility for consistent logging across the application.\n * Supports different log levels and can be disabled for testing.\n */\n\ntype LogLevel = 'debug' | 'info' | 'warn' | 'error';\n\ninterface LoggerOptions {\n enabled: boolean;\n level: LogLevel;\n}\n\nclass Logger {\n private static instance: Logger;\n private options: LoggerOptions = {\n enabled: getDefaultLoggerEnabled(),\n level: 'debug',\n };\n\n private constructor() {}\n\n static getInstance(): Logger {\n if (!Logger.instance) {\n Logger.instance = new Logger();\n }\n return Logger.instance;\n }\n\n configure(options: Partial<LoggerOptions>): void {\n this.options = { ...this.options, ...options };\n }\n\n private shouldLog(level: LogLevel): boolean {\n if (!this.options.enabled) return false;\n\n const levels: LogLevel[] = ['debug', 'info', 'warn', 'error'];\n const configuredLevelIndex = levels.indexOf(this.options.level);\n const currentLevelIndex = levels.indexOf(level);\n\n return currentLevelIndex >= configuredLevelIndex;\n }\n\n private formatMessage(level: LogLevel, system: string, message: string): string {\n return `[${level.toUpperCase()}][${system}] ${message}`;\n }\n\n debug(system: string, message: string, ...args: unknown[]): void {\n if (this.shouldLog('debug')) {\n // eslint-disable-next-line no-console\n console.log(this.formatMessage('debug', system, message), ...args);\n }\n }\n\n info(system: string, message: string, ...args: unknown[]): void {\n if (this.shouldLog('info')) {\n // eslint-disable-next-line no-console\n console.log(this.formatMessage('info', system, message), ...args);\n }\n }\n\n warn(system: string, message: string, ...args: unknown[]): void {\n if (this.shouldLog('warn')) {\n // eslint-disable-next-line no-console\n console.warn(this.formatMessage('warn', system, message), ...args);\n }\n }\n\n error(system: string, message: string, ...args: unknown[]): void {\n if (this.shouldLog('error')) {\n // eslint-disable-next-line no-console\n console.error(this.formatMessage('error', system, message), ...args);\n }\n }\n}\n\nexport const logger = Logger.getInstance();\n\n/**\n * Determine whether logging should be enabled by default.\n *\n * - In Vite/browser contexts, use `import.meta.env.DEV`.\n * - In Node/tsup contexts, use `process.env.NODE_ENV`.\n *\n * Defaults to disabled outside development to avoid runtime overhead and noise.\n */\nfunction getDefaultLoggerEnabled(): boolean {\n // Vite/browser context: prefer explicit export env over DEV flag\n try {\n // Use a narrow, typed access pattern to avoid depending on Vite types in this package\n const viteEnv = (\n import.meta as unknown as {\n env?: { DEV?: boolean; MODE?: string; PROD?: boolean; VITE_EXPORT_ENV?: string };\n }\n ).env;\n\n if (viteEnv) {\n const exportEnv = String(viteEnv.VITE_EXPORT_ENV || '').toLowerCase();\n // Force-disable logging for staging/production deployments regardless of DEV\n if (exportEnv === 'staging' || exportEnv === 'production') {\n return false;\n }\n if (typeof viteEnv.DEV === 'boolean') {\n return viteEnv.DEV;\n }\n }\n } catch {\n // Ignore environments where import.meta is not available or lacks env\n }\n\n // Node/tsup context: also honor VITE_EXPORT_ENV, then fall back to NODE_ENV\n if (typeof process !== 'undefined' && typeof process.env !== 'undefined') {\n const exportEnv = String(process.env.VITE_EXPORT_ENV || '').toLowerCase();\n if (exportEnv === 'staging' || exportEnv === 'production') {\n return false;\n }\n const nodeEnv = process.env.NODE_ENV;\n return nodeEnv === 'development' || nodeEnv === 'test';\n }\n\n // Safe fallback: disabled\n return false;\n}\n","import type {\n AppRuntimeConfig,\n FeatureFlags,\n GlobalServiceConfigs,\n IndexerEndpointConfig,\n NetworkServiceConfigs,\n NetworkSpecificRpcEndpoints,\n RpcEndpointConfig,\n ServiceParameterConfig,\n UiKitName,\n UserRpcProviderConfig,\n} from '@openzeppelin/ui-types';\n\nimport { logger } from './logger';\n\n// Changed from @openzeppelin/ui-utils\n\n/**\n * Type for the strategy array in initialize method.\n */\nexport type ConfigLoadStrategy =\n | { type: 'viteEnv'; env: ViteEnv | undefined }\n | { type: 'json'; path?: string }\n | { type: 'localStorage'; key?: string };\n\nconst VITE_ENV_PREFIX = 'VITE_APP_CFG_';\nconst LOG_SYSTEM = 'AppConfigService'; // Define a constant for the system name\n\n// Define a type for Vite's import.meta.env structure if it exists\ninterface ViteEnv {\n [key: string]: string | boolean | undefined;\n}\n\n/**\n * AppConfigService\n *\n * Responsible for loading, merging, and providing access to the application's\n * runtime configuration (`AppRuntimeConfig`).\n */\nexport class AppConfigService {\n private config: AppRuntimeConfig;\n private isInitialized = false;\n\n /**\n * Creates a new AppConfigService with default configuration.\n */\n constructor() {\n // Initialize with sensible hardcoded defaults\n this.config = {\n networkServiceConfigs: {},\n globalServiceConfigs: {},\n rpcEndpoints: {},\n indexerEndpoints: {},\n featureFlags: {},\n defaultLanguage: 'en',\n };\n logger.info(LOG_SYSTEM, 'Service initialized with default configuration.');\n }\n\n private loadFromViteEnvironment(envSource: ViteEnv | undefined): void {\n logger.debug(\n LOG_SYSTEM,\n 'BEGIN loadFromViteEnvironment. envSource received:',\n envSource ? JSON.stringify(envSource) : 'undefined'\n );\n\n if (typeof envSource === 'undefined') {\n logger.warn(\n LOG_SYSTEM,\n 'Vite environment object (envSource) was undefined. Skipping Vite env load.'\n );\n return;\n }\n const env = envSource;\n const loadedNetworkServiceConfigs: NetworkServiceConfigs = {};\n const loadedGlobalServiceConfigs: GlobalServiceConfigs = {};\n const loadedRpcEndpoints: NetworkSpecificRpcEndpoints = {};\n const loadedIndexerEndpoints: Record<string, string> = {};\n const loadedFeatureFlags: FeatureFlags = {};\n\n for (const key in env) {\n if (Object.prototype.hasOwnProperty.call(env, key) && env[key] !== undefined) {\n const value = String(env[key]);\n\n if (key.startsWith(`${VITE_ENV_PREFIX}API_KEY_`)) {\n const serviceIdSuffix = key.substring(`${VITE_ENV_PREFIX}API_KEY_`.length);\n const serviceIdentifier = serviceIdSuffix.toLowerCase().replace(/_/g, '-');\n if (!loadedNetworkServiceConfigs[serviceIdentifier]) {\n loadedNetworkServiceConfigs[serviceIdentifier] = {};\n }\n loadedNetworkServiceConfigs[serviceIdentifier]!.apiKey = value;\n } else if (key.startsWith(`${VITE_ENV_PREFIX}SERVICE_`)) {\n const fullSuffix = key.substring(`${VITE_ENV_PREFIX}SERVICE_`.length); // e.g., WALLETCONNECT_PROJECT_ID or ANOTHER_SERVICE_API_URL\n\n const firstUnderscoreIndex = fullSuffix.indexOf('_');\n if (firstUnderscoreIndex > 0 && firstUnderscoreIndex < fullSuffix.length - 1) {\n // Ensure underscore is present and not at start/end\n const serviceName = fullSuffix.substring(0, firstUnderscoreIndex).toLowerCase(); // e.g., \"walletconnect\", \"anotherservice\"\n const paramNameRaw = fullSuffix.substring(firstUnderscoreIndex + 1); // e.g., \"PROJECT_ID\", \"API_URL\"\n\n // Convert paramNameRaw (e.g., PROJECT_ID or API_URL) to camelCase (projectId, apiUrl)\n const paramName = paramNameRaw\n .toLowerCase()\n .replace(/_([a-z])/g, (g) => g[1].toUpperCase());\n\n if (serviceName && paramName) {\n if (!loadedGlobalServiceConfigs[serviceName]) {\n loadedGlobalServiceConfigs[serviceName] = {};\n }\n loadedGlobalServiceConfigs[serviceName]![paramName] = value;\n logger.debug(\n LOG_SYSTEM,\n `Parsed service: '${serviceName}', param: '${paramName}', value: '${value}' from key: ${key}`\n );\n } else {\n logger.warn(LOG_SYSTEM, `Could not effectively parse service/param from key: ${key}`);\n }\n } else {\n logger.warn(\n LOG_SYSTEM,\n `Could not determine service and param from key (missing underscore separator): ${key}`\n );\n }\n } else if (key === `${VITE_ENV_PREFIX}WALLETCONNECT_PROJECT_ID`) {\n // Directly handle the VITE_APP_CFG_WALLETCONNECT_PROJECT_ID case\n if (!loadedGlobalServiceConfigs.walletconnect) {\n loadedGlobalServiceConfigs.walletconnect = {};\n }\n loadedGlobalServiceConfigs.walletconnect.projectId = value;\n logger.debug(\n LOG_SYSTEM,\n `Parsed WalletConnect Project ID directly from key: ${key}, value: ${value}`\n );\n } else if (key.startsWith(`${VITE_ENV_PREFIX}RPC_ENDPOINT_`)) {\n const networkIdSuffix = key.substring(`${VITE_ENV_PREFIX}RPC_ENDPOINT_`.length);\n const networkId = networkIdSuffix.toLowerCase().replace(/_/g, '-');\n if (networkId) {\n loadedRpcEndpoints[networkId] = value;\n logger.debug(LOG_SYSTEM, `Loaded RPC override for ${networkId}: ${value}`);\n }\n } else if (key.startsWith(`${VITE_ENV_PREFIX}INDEXER_ENDPOINT_`)) {\n const networkIdSuffix = key.substring(`${VITE_ENV_PREFIX}INDEXER_ENDPOINT_`.length);\n const networkId = networkIdSuffix.toLowerCase().replace(/_/g, '-');\n if (networkId) {\n loadedIndexerEndpoints[networkId] = value;\n logger.debug(LOG_SYSTEM, `Loaded indexer endpoint for ${networkId}: ${value}`);\n }\n } else if (key.startsWith(`${VITE_ENV_PREFIX}FEATURE_FLAG_`)) {\n const flagNameSuffix = key.substring(`${VITE_ENV_PREFIX}FEATURE_FLAG_`.length);\n const flagName = flagNameSuffix.toLowerCase();\n loadedFeatureFlags[flagName] = value.toLowerCase() === 'true';\n } else if (key === `${VITE_ENV_PREFIX}DEFAULT_LANGUAGE`) {\n this.config.defaultLanguage = value;\n }\n }\n }\n\n this.config.networkServiceConfigs = {\n ...this.config.networkServiceConfigs,\n ...loadedNetworkServiceConfigs,\n };\n if (Object.keys(loadedGlobalServiceConfigs).length > 0) {\n if (!this.config.globalServiceConfigs) this.config.globalServiceConfigs = {};\n for (const serviceKeyInLoaded in loadedGlobalServiceConfigs) {\n if (Object.prototype.hasOwnProperty.call(loadedGlobalServiceConfigs, serviceKeyInLoaded)) {\n this.config.globalServiceConfigs[serviceKeyInLoaded] = {\n ...(this.config.globalServiceConfigs[serviceKeyInLoaded] || {}),\n ...loadedGlobalServiceConfigs[serviceKeyInLoaded],\n };\n }\n }\n }\n if (Object.keys(loadedRpcEndpoints).length > 0) {\n if (!this.config.rpcEndpoints) this.config.rpcEndpoints = {};\n for (const networkKey in loadedRpcEndpoints) {\n if (Object.prototype.hasOwnProperty.call(loadedRpcEndpoints, networkKey)) {\n this.config.rpcEndpoints[networkKey] = loadedRpcEndpoints[networkKey];\n }\n }\n }\n if (Object.keys(loadedIndexerEndpoints).length > 0) {\n if (!this.config.indexerEndpoints) this.config.indexerEndpoints = {};\n for (const networkKey in loadedIndexerEndpoints) {\n if (Object.prototype.hasOwnProperty.call(loadedIndexerEndpoints, networkKey)) {\n this.config.indexerEndpoints[networkKey] = loadedIndexerEndpoints[networkKey];\n }\n }\n }\n this.config.featureFlags = { ...this.config.featureFlags, ...loadedFeatureFlags };\n\n logger.info(\n LOG_SYSTEM,\n 'Resolved globalServiceConfigs after Vite env processing:',\n this.config.globalServiceConfigs\n ? JSON.stringify(this.config.globalServiceConfigs)\n : 'undefined'\n );\n logger.info(\n LOG_SYSTEM,\n 'Resolved rpcEndpoints after Vite env processing:',\n this.config.rpcEndpoints ? JSON.stringify(this.config.rpcEndpoints) : 'undefined'\n );\n logger.info(\n LOG_SYSTEM,\n 'Configuration loaded/merged from provided Vite environment variables.'\n );\n }\n\n private async loadFromJson(filePath = '/app.config.json'): Promise<void> {\n try {\n const response = await fetch(filePath);\n if (!response.ok) {\n // It's okay if the file is not found, could be optional or for specific environments\n if (response.status === 404) {\n logger.info(\n LOG_SYSTEM,\n `Optional configuration file not found at ${filePath}. Skipping.`\n );\n } else {\n logger.warn(\n LOG_SYSTEM,\n `Failed to fetch config from ${filePath}: ${response.status} ${response.statusText}`\n );\n }\n return;\n }\n const externalConfig = (await response.json()) as Partial<AppRuntimeConfig>; // Use Partial for safer merging\n\n if (externalConfig.networkServiceConfigs) {\n if (!this.config.networkServiceConfigs) this.config.networkServiceConfigs = {};\n for (const key in externalConfig.networkServiceConfigs) {\n if (Object.prototype.hasOwnProperty.call(externalConfig.networkServiceConfigs, key)) {\n this.config.networkServiceConfigs[key] = {\n ...(this.config.networkServiceConfigs[key] || {}),\n ...externalConfig.networkServiceConfigs[key],\n };\n }\n }\n }\n\n if (externalConfig.globalServiceConfigs) {\n if (!this.config.globalServiceConfigs) this.config.globalServiceConfigs = {};\n for (const serviceKey in externalConfig.globalServiceConfigs) {\n if (\n Object.prototype.hasOwnProperty.call(externalConfig.globalServiceConfigs, serviceKey)\n ) {\n this.config.globalServiceConfigs[serviceKey] = {\n ...(this.config.globalServiceConfigs[serviceKey] || {}),\n ...externalConfig.globalServiceConfigs[serviceKey],\n };\n }\n }\n }\n\n if (externalConfig.rpcEndpoints) {\n if (!this.config.rpcEndpoints) this.config.rpcEndpoints = {};\n for (const networkKey in externalConfig.rpcEndpoints) {\n if (Object.prototype.hasOwnProperty.call(externalConfig.rpcEndpoints, networkKey)) {\n this.config.rpcEndpoints[networkKey] = externalConfig.rpcEndpoints[networkKey];\n }\n }\n }\n\n if (externalConfig.indexerEndpoints) {\n if (!this.config.indexerEndpoints) this.config.indexerEndpoints = {};\n for (const networkKey in externalConfig.indexerEndpoints) {\n if (Object.prototype.hasOwnProperty.call(externalConfig.indexerEndpoints, networkKey)) {\n this.config.indexerEndpoints[networkKey] = externalConfig.indexerEndpoints[networkKey];\n }\n }\n }\n\n if (externalConfig.featureFlags) {\n this.config.featureFlags = {\n ...(this.config.featureFlags || {}),\n ...externalConfig.featureFlags,\n };\n }\n\n if (typeof externalConfig.defaultLanguage === 'string') {\n this.config.defaultLanguage = externalConfig.defaultLanguage;\n }\n // Add merging for other AppRuntimeConfig properties here\n\n logger.info(LOG_SYSTEM, `Configuration loaded/merged from ${filePath}`);\n } catch (error) {\n logger.error(LOG_SYSTEM, `Error loading or parsing config from ${filePath}:`, error);\n }\n }\n\n /**\n * Initializes the service by loading configuration from the specified strategies.\n * @param strategies - Array of configuration loading strategies to apply\n */\n public async initialize(strategies: ConfigLoadStrategy[]): Promise<void> {\n logger.info(LOG_SYSTEM, 'Initialization sequence started with strategies:', strategies);\n // Ensure strategies are processed in defined order of precedence\n // Example: Defaults (in constructor) -> Vite Env -> JSON -> LocalStorage\n\n for (const strategy of strategies) {\n if (strategy.type === 'viteEnv') {\n this.loadFromViteEnvironment(strategy.env);\n } else if (strategy.type === 'json') {\n await this.loadFromJson(strategy.path);\n }\n // Add localStorage strategy in Phase 4\n }\n this.isInitialized = true;\n logger.info(LOG_SYSTEM, 'Initialization complete.');\n }\n\n /**\n * Gets the API key for a specific explorer service.\n * @param serviceIdentifier - The service identifier\n * @returns The API key if configured, undefined otherwise\n */\n public getExplorerApiKey(serviceIdentifier: string): string | undefined {\n if (!this.isInitialized) {\n logger.warn(LOG_SYSTEM, 'getExplorerApiKey called before initialization.');\n }\n return this.config.networkServiceConfigs?.[serviceIdentifier]?.apiKey;\n }\n\n /**\n * Gets the configuration for a global service.\n * @param serviceName - The name of the service\n * @returns The service configuration if found, undefined otherwise\n */\n public getGlobalServiceConfig(serviceName: string): ServiceParameterConfig | undefined {\n if (!this.isInitialized) {\n logger.warn(LOG_SYSTEM, 'getGlobalServiceConfig called before initialization.');\n }\n return this.config.globalServiceConfigs?.[serviceName];\n }\n\n /**\n * Checks if a feature flag is enabled.\n * @param flagName - The name of the feature flag\n * @returns True if the feature is enabled, false otherwise\n */\n public isFeatureEnabled(flagName: string): boolean {\n if (!this.isInitialized) {\n logger.warn(LOG_SYSTEM, 'isFeatureEnabled called before initialization.');\n }\n return this.config.featureFlags?.[flagName.toLowerCase()] ?? false;\n }\n\n /**\n * Gets a global service parameter value.\n * @param serviceName The name of the service\n * @param paramName The name of the parameter\n * @returns The parameter value (can be any type including objects, arrays) or undefined if not found\n */\n public getGlobalServiceParam(\n serviceName: string,\n paramName: string\n ): string | number | boolean | object | Array<unknown> | undefined {\n if (!this.isInitialized) {\n logger.warn(LOG_SYSTEM, 'getGlobalServiceParam called before initialization.');\n return undefined;\n }\n const serviceParams = this.config.globalServiceConfigs?.[serviceName.toLowerCase()];\n // Parameter names are stored in camelCase (e.g., projectId)\n return serviceParams?.[paramName];\n }\n\n /**\n * Gets the RPC endpoint override for a specific network.\n * @param networkId - The network identifier\n * @returns The RPC endpoint configuration if found, undefined otherwise\n */\n public getRpcEndpointOverride(\n networkId: string\n ): string | RpcEndpointConfig | UserRpcProviderConfig | undefined {\n if (!this.isInitialized) {\n logger.warn(LOG_SYSTEM, 'getRpcEndpointOverride called before initialization.');\n }\n return this.config.rpcEndpoints?.[networkId];\n }\n\n /**\n * Get the indexer endpoint override for a specific network.\n * Indexer endpoints are used for querying historical blockchain data.\n * @param networkId The network identifier (e.g., 'stellar-testnet')\n * @returns The indexer endpoint configuration, or undefined if not configured\n */\n public getIndexerEndpointOverride(networkId: string): string | IndexerEndpointConfig | undefined {\n if (!this.isInitialized) {\n logger.warn(LOG_SYSTEM, 'getIndexerEndpointOverride called before initialization.');\n }\n return this.config.indexerEndpoints?.[networkId];\n }\n\n /**\n * Returns the entire configuration object.\n * Primarily for debugging or for parts of the app that need a broader view.\n * Use specific getters like `getExplorerApiKey` or `isFeatureEnabled` where possible.\n */\n public getConfig(): Readonly<AppRuntimeConfig> {\n return this.config;\n }\n\n /**\n * Gets a nested configuration object with type safety.\n *\n * This is a helper method to safely access complex nested configuration objects\n * with proper TypeScript type checking.\n *\n * @param serviceName The name of the service (e.g., 'walletui')\n * @param paramName The parameter name that contains the nested object (e.g., 'config')\n * Pass an empty string to get the entire service configuration.\n * @returns The typed nested configuration object or undefined if not found\n *\n * @example\n * // Get a typed UI kit configuration:\n * const uiConfig = appConfigService.getTypedNestedConfig<UiKitConfiguration>('walletui', 'config');\n * if (uiConfig) {\n * console.log(uiConfig.kitName); // Properly typed\n * }\n *\n * // Get entire service configuration:\n * const allAnalytics = appConfigService.getTypedNestedConfig<AnalyticsConfig>('analytics', '');\n */\n public getTypedNestedConfig<T extends object>(\n serviceName: string,\n paramName: string\n ): T | undefined {\n if (!this.isInitialized) {\n logger.warn(LOG_SYSTEM, 'getTypedNestedConfig called before initialization.');\n return undefined;\n }\n\n try {\n // If paramName is empty, return the entire service configuration\n if (paramName === '') {\n const serviceConfig = this.config.globalServiceConfigs?.[serviceName.toLowerCase()];\n if (serviceConfig && typeof serviceConfig === 'object') {\n return serviceConfig as T;\n }\n return undefined;\n }\n\n // Otherwise, get the specific nested parameter\n const param = this.getGlobalServiceParam(serviceName, paramName);\n\n if (param && typeof param === 'object') {\n // We've confirmed it's an object, so we can safely cast it\n return param as T;\n }\n\n return undefined;\n } catch (error) {\n logger.warn(\n LOG_SYSTEM,\n `Error accessing nested configuration for ${serviceName}.${paramName}:`,\n error\n );\n return undefined;\n }\n }\n\n /**\n * Checks if a nested configuration exists and has a specific property.\n *\n * @param serviceName The name of the service\n * @param paramName The parameter name containing the nested object\n * @param propName The property name to check for\n * @returns True if the property exists in the nested configuration\n *\n * @example\n * if (appConfigService.hasNestedConfigProperty('walletui', 'config', 'showInjectedConnector')) {\n * // Do something when the property exists\n * }\n */\n public hasNestedConfigProperty(\n serviceName: string,\n paramName: string,\n propName: string\n ): boolean {\n const nestedConfig = this.getTypedNestedConfig<Record<string, unknown>>(serviceName, paramName);\n\n return (\n nestedConfig !== undefined && Object.prototype.hasOwnProperty.call(nestedConfig, propName)\n );\n }\n\n /**\n * Gets wallet UI configuration for a specific ecosystem.\n * Uses ecosystem-namespaced format with optional default fallback.\n *\n * @param ecosystemId The ecosystem ID (e.g., 'stellar', 'evm', 'solana')\n * @returns The wallet UI configuration for the ecosystem, or undefined\n *\n * @example\n * Configuration format:\n * {\n * \"globalServiceConfigs\": {\n * \"walletui\": {\n * \"stellar\": { \"kitName\": \"stellar-wallets-kit\", \"kitConfig\": {} },\n * \"evm\": { \"kitName\": \"rainbowkit\", \"kitConfig\": {} },\n * \"default\": { \"kitName\": \"custom\", \"kitConfig\": {} }\n * }\n * }\n * }\n * const stellarConfig = appConfigService.getWalletUIConfig('stellar');\n */\n public getWalletUIConfig<\n T extends object = { kitName: UiKitName; kitConfig?: Record<string, unknown> },\n >(ecosystemId?: string): T | undefined {\n if (!this.isInitialized) {\n logger.warn(LOG_SYSTEM, 'getWalletUIConfig called before initialization.');\n return undefined;\n }\n\n try {\n const walletUIService = this.config.globalServiceConfigs?.walletui;\n\n if (!walletUIService) {\n return undefined;\n }\n\n // Check for new ecosystem-namespaced format\n if (\n ecosystemId &&\n walletUIService[ecosystemId] &&\n typeof walletUIService[ecosystemId] === 'object'\n ) {\n logger.debug(LOG_SYSTEM, `Found ecosystem-specific wallet UI config for ${ecosystemId}`);\n return walletUIService[ecosystemId] as T;\n }\n\n // Check for default config in new format\n if (walletUIService.default && typeof walletUIService.default === 'object') {\n logger.debug(LOG_SYSTEM, `Using default wallet UI config for ecosystem ${ecosystemId}`);\n return walletUIService.default as T;\n }\n\n return undefined;\n } catch (error) {\n logger.warn(\n LOG_SYSTEM,\n `Error accessing wallet UI configuration for ecosystem ${ecosystemId}:`,\n error\n );\n return undefined;\n }\n }\n}\n\n// Create a singleton instance of the AppConfigService\nexport const appConfigService = new AppConfigService();\n","import type { UserRpcProviderConfig } from '@openzeppelin/ui-types';\n\nimport { logger } from './logger';\n\n// Event types\ntype RpcConfigEventType = 'rpc-config-changed' | 'rpc-config-cleared';\n\nexport interface RpcConfigEvent {\n type: RpcConfigEventType;\n networkId: string;\n config?: UserRpcProviderConfig;\n}\n\n/**\n * Service for managing user-configured RPC endpoints.\n * Stores RPC configurations in localStorage for persistence across sessions.\n */\nexport class UserRpcConfigService {\n private static readonly STORAGE_PREFIX = 'tfb_rpc_config_';\n private static readonly eventListeners = new Map<string, Set<(event: RpcConfigEvent) => void>>();\n\n /**\n * Emits an RPC configuration event to all registered listeners\n */\n private static emitEvent(event: RpcConfigEvent): void {\n const listeners = this.eventListeners.get(event.networkId) || new Set();\n const globalListeners = this.eventListeners.get('*') || new Set();\n\n [...listeners, ...globalListeners].forEach((listener) => {\n try {\n listener(event);\n } catch (error) {\n logger.error('UserRpcConfigService', 'Error in event listener:', error);\n }\n });\n }\n\n /**\n * Subscribes to RPC configuration changes for a specific network or all networks\n * @param networkId The network identifier or '*' for all networks\n * @param listener The callback to invoke when RPC config changes\n * @returns Unsubscribe function\n */\n static subscribe(networkId: string, listener: (event: RpcConfigEvent) => void): () => void {\n if (!this.eventListeners.has(networkId)) {\n this.eventListeners.set(networkId, new Set());\n }\n\n this.eventListeners.get(networkId)!.add(listener);\n\n // Return unsubscribe function\n return () => {\n const listeners = this.eventListeners.get(networkId);\n if (listeners) {\n listeners.delete(listener);\n if (listeners.size === 0) {\n this.eventListeners.delete(networkId);\n }\n }\n };\n }\n\n /**\n * Saves a user RPC configuration for a specific network.\n * @param networkId The network identifier\n * @param config The RPC configuration to save\n */\n static saveUserRpcConfig(networkId: string, config: UserRpcProviderConfig): void {\n try {\n const storageKey = `${this.STORAGE_PREFIX}${networkId}`;\n localStorage.setItem(storageKey, JSON.stringify(config));\n logger.info('UserRpcConfigService', `Saved RPC config for network ${networkId}`);\n\n // Emit change event\n this.emitEvent({ type: 'rpc-config-changed', networkId, config });\n } catch (error) {\n logger.error('UserRpcConfigService', 'Failed to save RPC config:', error);\n }\n }\n\n /**\n * Retrieves a user RPC configuration for a specific network.\n * @param networkId The network identifier\n * @returns The stored configuration or null if not found\n */\n static getUserRpcConfig(networkId: string): UserRpcProviderConfig | null {\n try {\n const storageKey = `${this.STORAGE_PREFIX}${networkId}`;\n const stored = localStorage.getItem(storageKey);\n\n if (!stored) {\n return null;\n }\n\n const config = JSON.parse(stored) as UserRpcProviderConfig;\n logger.info('UserRpcConfigService', `Retrieved RPC config for network ${networkId}`);\n return config;\n } catch (error) {\n logger.error('UserRpcConfigService', 'Failed to retrieve RPC config:', error);\n return null;\n }\n }\n\n /**\n * Clears a user RPC configuration for a specific network.\n * @param networkId The network identifier\n */\n static clearUserRpcConfig(networkId: string): void {\n try {\n const storageKey = `${this.STORAGE_PREFIX}${networkId}`;\n localStorage.removeItem(storageKey);\n logger.info('UserRpcConfigService', `Cleared RPC config for network ${networkId}`);\n\n // Emit change event\n this.emitEvent({ type: 'rpc-config-cleared', networkId });\n } catch (error) {\n logger.error('UserRpcConfigService', 'Failed to clear RPC config:', error);\n }\n }\n\n /**\n * Clears all user RPC configurations.\n */\n static clearAllUserRpcConfigs(): void {\n try {\n const keysToRemove: string[] = [];\n\n // Find all RPC config keys\n for (let i = 0; i < localStorage.length; i++) {\n const key = localStorage.key(i);\n if (key?.startsWith(this.STORAGE_PREFIX)) {\n keysToRemove.push(key);\n }\n }\n\n // Remove all found keys\n keysToRemove.forEach((key) => localStorage.removeItem(key));\n\n logger.info('UserRpcConfigService', `Cleared ${keysToRemove.length} RPC configs`);\n } catch (error) {\n logger.error('UserRpcConfigService', 'Failed to clear all RPC configs:', error);\n }\n }\n}\n\n// Export as singleton instance for convenience\nexport const userRpcConfigService = UserRpcConfigService;\n","import type { UserExplorerConfig } from '@openzeppelin/ui-types';\n\nimport { logger } from './logger';\n\n// Event types\ntype ExplorerConfigEventType = 'explorer-config-changed' | 'explorer-config-cleared';\n\nexport interface ExplorerConfigEvent {\n type: ExplorerConfigEventType;\n networkId: string;\n config?: UserExplorerConfig;\n}\n\n/**\n * Service for managing user-configured block explorer endpoints and API keys.\n * Stores explorer configurations in localStorage for persistence across sessions.\n */\nexport class UserExplorerConfigService {\n private static readonly STORAGE_PREFIX = 'tfb_explorer_config_';\n private static readonly eventListeners = new Map<\n string,\n Set<(event: ExplorerConfigEvent) => void>\n >();\n\n /**\n * Emits an explorer configuration event to all registered listeners\n */\n private static emitEvent(event: ExplorerConfigEvent): void {\n const listeners = this.eventListeners.get(event.networkId) || new Set();\n const globalListeners = this.eventListeners.get('*') || new Set();\n\n [...listeners, ...globalListeners].forEach((listener) => {\n try {\n listener(event);\n } catch (error) {\n logger.error('UserExplorerConfigService', 'Error in event listener:', error);\n }\n });\n }\n\n /**\n * Subscribes to explorer configuration changes for a specific network or all networks\n * @param networkId The network identifier or '*' for all networks\n * @param listener The callback to invoke when explorer config changes\n * @returns Unsubscribe function\n */\n static subscribe(networkId: string, listener: (event: ExplorerConfigEvent) => void): () => void {\n if (!this.eventListeners.has(networkId)) {\n this.eventListeners.set(networkId, new Set());\n }\n\n this.eventListeners.get(networkId)!.add(listener);\n\n // Return unsubscribe function\n return () => {\n const listeners = this.eventListeners.get(networkId);\n if (listeners) {\n listeners.delete(listener);\n if (listeners.size === 0) {\n this.eventListeners.delete(networkId);\n }\n }\n };\n }\n\n /**\n * Saves a user explorer configuration for a specific network.\n * @param networkId The network identifier\n * @param config The explorer configuration to save\n */\n static saveUserExplorerConfig(networkId: string, config: UserExplorerConfig): void {\n try {\n const storageKey = `${this.STORAGE_PREFIX}${networkId}`;\n localStorage.setItem(storageKey, JSON.stringify(config));\n logger.info('UserExplorerConfigService', `Saved explorer config for network ${networkId}`);\n\n // Emit change event\n this.emitEvent({ type: 'explorer-config-changed', networkId, config });\n } catch (error) {\n logger.error('UserExplorerConfigService', 'Failed to save explorer config:', error);\n }\n }\n\n /**\n * Retrieves a user explorer configuration for a specific network.\n * First checks for global settings, then falls back to network-specific settings.\n * @param networkId The network identifier\n * @returns The stored configuration or null if not found\n */\n static getUserExplorerConfig(networkId: string): UserExplorerConfig | null {\n try {\n // First check for global settings\n const globalKey = `${this.STORAGE_PREFIX}__global__`;\n const globalStored = localStorage.getItem(globalKey);\n\n if (globalStored) {\n const globalConfig = JSON.parse(globalStored) as UserExplorerConfig;\n if (globalConfig.applyToAllNetworks) {\n logger.info(\n 'UserExplorerConfigService',\n `Using global explorer config for network ${networkId}`\n );\n return globalConfig;\n }\n }\n\n // Then check for network-specific settings\n const storageKey = `${this.STORAGE_PREFIX}${networkId}`;\n const stored = localStorage.getItem(storageKey);\n\n if (!stored) {\n return null;\n }\n\n const config = JSON.parse(stored) as UserExplorerConfig;\n logger.info(\n 'UserExplorerConfigService',\n `Retrieved explorer config for network ${networkId}`\n );\n return config;\n } catch (error) {\n logger.error('UserExplorerConfigService', 'Failed to retrieve explorer config:', error);\n return null;\n }\n }\n\n /**\n * Clears a user explorer configuration for a specific network.\n * @param networkId The network identifier\n */\n static clearUserExplorerConfig(networkId: string): void {\n try {\n const storageKey = `${this.STORAGE_PREFIX}${networkId}`;\n localStorage.removeItem(storageKey);\n logger.info('UserExplorerConfigService', `Cleared explorer config for network ${networkId}`);\n\n // Emit change event\n this.emitEvent({ type: 'explorer-config-cleared', networkId });\n } catch (error) {\n logger.error('UserExplorerConfigService', 'Failed to clear explorer config:', error);\n }\n }\n\n /**\n * Clears all user explorer configurations.\n */\n static clearAllUserExplorerConfigs(): void {\n try {\n const keysToRemove: string[] = [];\n\n // Find all explorer config keys\n for (let i = 0; i < localStorage.length; i++) {\n const key = localStorage.key(i);\n if (key?.startsWith(this.STORAGE_PREFIX)) {\n keysToRemove.push(key);\n }\n }\n\n // Remove all found keys\n keysToRemove.forEach((key) => {\n const networkId = key.substring(this.STORAGE_PREFIX.length);\n localStorage.removeItem(key);\n // Emit clear event for each network\n this.emitEvent({ type: 'explorer-config-cleared', networkId });\n });\n\n logger.info('UserExplorerConfigService', `Cleared ${keysToRemove.length} explorer configs`);\n } catch (error) {\n logger.error('UserExplorerConfigService', 'Failed to clear all explorer configs:', error);\n }\n }\n\n /**\n * Gets all network IDs that have explorer configurations.\n * @returns Array of network IDs\n */\n static getConfiguredNetworkIds(): string[] {\n try {\n const networkIds: string[] = [];\n\n // Check all localStorage keys\n for (let i = 0; i < localStorage.length; i++) {\n const key = localStorage.key(i);\n if (key?.startsWith(this.STORAGE_PREFIX)) {\n const networkId = key.substring(this.STORAGE_PREFIX.length);\n // Skip the global config\n if (networkId !== '__global__') {\n networkIds.push(networkId);\n }\n }\n }\n\n return networkIds;\n } catch (error) {\n logger.error('UserExplorerConfigService', 'Failed to get configured network IDs:', error);\n return [];\n }\n }\n}\n\n// Export as singleton instance for convenience\nexport const userExplorerConfigService = UserExplorerConfigService;\n","import { logger } from './logger';\n\n/**\n * Event types emitted by UserNetworkServiceConfigService\n */\ntype ServiceConfigEventType = 'service-config-changed' | 'service-config-cleared';\n\n/**\n * Event emitted when a service configuration changes or is cleared.\n * Used for subscribing to configuration updates across the application.\n *\n * @interface ServiceConfigEvent\n */\nexport interface ServiceConfigEvent {\n /** Type of event: config changed or cleared */\n type: ServiceConfigEventType;\n /** Network ID for which the configuration changed */\n networkId: string;\n /** Service ID (e.g., 'rpc', 'explorer', 'contract-definitions') */\n serviceId: string;\n /** The new configuration data (only present for 'service-config-changed' events) */\n config?: Record<string, unknown>;\n}\n\n/**\n * Service for managing user-defined network service configurations.\n *\n * This service provides a generic, chain-agnostic way to store and retrieve\n * per-network, per-service user configurations.\n *\n * Configurations are stored in localStorage with the key format:\n * `tfb_service_config_{serviceId}__{networkId}`\n *\n * @example\n * ```typescript\n * // Save RPC configuration for Sepolia\n * userNetworkServiceConfigService.save('ethereum-sepolia', 'rpc', {\n * rpcUrl: 'https://sepolia.infura.io/v3/your-key'\n * });\n *\n * // Retrieve configuration\n * const config = userNetworkServiceConfigService.get('ethereum-sepolia', 'rpc');\n *\n * // Subscribe to changes\n * const unsubscribe = userNetworkServiceConfigService.subscribe(\n * 'ethereum-sepolia',\n * 'rpc',\n * (event) => {\n * console.log('Config changed:', event.config);\n * }\n * );\n * ```\n *\n * @class UserNetworkServiceConfigService\n */\nexport class UserNetworkServiceConfigService {\n private static readonly STORAGE_PREFIX = 'tfb_service_config_';\n private static readonly listeners = new Map<string, Set<(event: ServiceConfigEvent) => void>>();\n\n /**\n * Generates a localStorage key for a network-service combination.\n *\n * @private\n * @param networkId - The network ID\n * @param serviceId - The service ID\n * @returns The storage key string\n */\n private static key(networkId: string, serviceId: string): string {\n return `${this.STORAGE_PREFIX}${serviceId}__${networkId}`;\n }\n\n /**\n * Subscribes to configuration change events for a specific network and/or service.\n *\n * Use '*' as a wildcard to listen to all networks or all services.\n * The listener will be called whenever a matching configuration changes or is cleared.\n *\n * @param networkId - Network ID to listen to, or '*' for all networks\n * @param serviceId - Service ID to listen to, or '*' for all services\n * @param listener - Callback function to invoke when matching events occur\n * @returns Unsubscribe function to remove the listener\n *\n * @example\n * ```typescript\n * // Listen to all RPC config changes across all networks\n * const unsubscribe = userNetworkServiceConfigService.subscribe('*', 'rpc', (event) => {\n * console.log(`${event.networkId} RPC config changed`);\n * });\n *\n * // Later, unsubscribe\n * unsubscribe();\n * ```\n */\n static subscribe(\n networkId: string | '*',\n serviceId: string | '*',\n listener: (event: ServiceConfigEvent) => void\n ): () => void {\n const k = `${networkId}:${serviceId}`;\n if (!this.listeners.has(k)) this.listeners.set(k, new Set());\n this.listeners.get(k)!.add(listener);\n return () => {\n const set = this.listeners.get(k);\n if (set) {\n set.delete(listener);\n if (set.size === 0) this.listeners.delete(k);\n }\n };\n }\n\n /**\n * Emits an event to all matching subscribers.\n * Subscribers are matched based on exact network/service IDs or wildcards.\n *\n * @private\n * @param event - The event to emit\n */\n private static emit(event: ServiceConfigEvent): void {\n const targets = [\n `${event.networkId}:${event.serviceId}`,\n `${event.networkId}:*`,\n `*:${event.serviceId}`,\n `*:*`,\n ];\n for (const k of targets) {\n const set = this.listeners.get(k);\n if (!set) continue;\n for (const fn of set) {\n try {\n fn(event);\n } catch (e) {\n logger.error('UserNetworkServiceConfigService', 'Error in event listener:', e);\n }\n }\n }\n }\n\n /**\n * Saves a service configuration for a specific network.\n *\n * The configuration is stored in localStorage and all matching subscribers\n * are notified via a 'service-config-changed' event.\n *\n * @param networkId - The network ID (e.g., 'ethereum-sepolia')\n * @param serviceId - The service ID (e.g., 'rpc', 'explorer', 'contract-definitions')\n * @param config - The configuration object to save\n *\n * @example\n * ```typescript\n * userNetworkServiceConfigService.save('ethereum-sepolia', 'rpc', {\n * rpcUrl: 'https://sepolia.infura.io/v3/your-key'\n * });\n * ```\n */\n static save(networkId: string, serviceId: string, config: Record<string, unknown>): void {\n try {\n localStorage.setItem(this.key(networkId, serviceId), JSON.stringify(config));\n logger.info(\n 'UserNetworkServiceConfigService',\n `Saved config for ${serviceId} on network ${networkId}`\n );\n this.emit({ type: 'service-config-changed', networkId, serviceId, config });\n } catch (e) {\n logger.error('UserNetworkServiceConfigService', 'Failed to save config:', e);\n }\n }\n\n /**\n * Retrieves a saved service configuration for a specific network.\n *\n * @param networkId - The network ID (e.g., 'ethereum-sepolia')\n * @param serviceId - The service ID (e.g., 'rpc', 'explorer', 'contract-definitions')\n * @returns The configuration object, or null if not found or if retrieval fails\n *\n * @example\n * ```typescript\n * const config = userNetworkServiceConfigService.get('ethereum-sepolia', 'rpc');\n * if (config) {\n * console.log('RPC URL:', config.rpcUrl);\n * }\n * ```\n */\n static get(networkId: string, serviceId: string): Record<string, unknown> | null {\n try {\n const raw = localStorage.getItem(this.key(networkId, serviceId));\n return raw ? (JSON.parse(raw) as Record<string, unknown>) : null;\n } catch (e) {\n logger.error('UserNetworkServiceConfigService', 'Failed to retrieve config:', e);\n return null;\n }\n }\n\n /**\n * Clears a saved service configuration for a specific network.\n *\n * Removes the configuration from localStorage and notifies all matching subscribers\n * via a 'service-config-cleared' event.\n *\n * @param networkId - The network ID (e.g., 'ethereum-sepolia')\n * @param serviceId - The service ID (e.g., 'rpc', 'explorer', 'contract-definitions')\n *\n * @example\n * ```typescript\n * userNetworkServiceConfigService.clear('ethereum-sepolia', 'rpc');\n * ```\n */\n static clear(networkId: string, serviceId: string): void {\n try {\n localStorage.removeItem(this.key(networkId, serviceId));\n logger.info(\n 'UserNetworkServiceConfigService',\n `Cleared config for ${serviceId} on network ${networkId}`\n );\n this.emit({ type: 'service-config-cleared', networkId, serviceId });\n } catch (e) {\n logger.error('UserNetworkServiceConfigService', 'Failed to clear config:', e);\n }\n }\n}\n\n/**\n * Singleton instance of UserNetworkServiceConfigService.\n * This is the preferred way to access the service.\n *\n * @example\n * ```typescript\n * import { userNetworkServiceConfigService } from '@openzeppelin/ui-utils';\n *\n * userNetworkServiceConfigService.save('ethereum-sepolia', 'rpc', { rpcUrl: '...' });\n * ```\n */\nexport const userNetworkServiceConfigService = UserNetworkServiceConfigService;\n","import type { FieldType, FieldValue } from '@openzeppelin/ui-types';\n\n/**\n * Get a default value for a field type.\n * This is a chain-agnostic utility that provides appropriate default values\n * based on the UI field type.\n *\n * @param fieldType - The UI field type\n * @returns The appropriate default value for that field type\n */\nexport function getDefaultValueForType<T extends FieldType>(fieldType: T): FieldValue<T> {\n switch (fieldType) {\n case 'checkbox':\n return false as FieldValue<T>;\n case 'number':\n case 'amount':\n return 0 as FieldValue<T>;\n case 'array':\n return [] as FieldValue<T>;\n case 'object':\n return {} as FieldValue<T>;\n case 'array-object':\n return [] as FieldValue<T>;\n case 'map':\n return [] as FieldValue<T>; // Empty array of key-value pairs\n case 'blockchain-address':\n case 'bigint':\n case 'text':\n case 'textarea':\n case 'bytes':\n case 'email':\n case 'password':\n case 'select':\n case 'radio':\n case 'date':\n case 'hidden':\n default:\n return '' as FieldValue<T>;\n }\n}\n","import type { FieldValidation } from '@openzeppelin/ui-types';\n\n/**\n * Numeric type bounds configuration.\n * Each adapter provides its own bounds map based on chain-specific type names.\n */\nexport type NumericBoundsMap = Record<string, { min?: number; max?: number }>;\n\n/**\n * Enhances field validation with numeric bounds based on parameter type.\n * Only applies bounds if they are not already set in the validation object.\n *\n * @param validation - Existing validation rules (may be undefined)\n * @param parameterType - The blockchain parameter type (e.g., 'uint32', 'U32', 'Uint<0..255>')\n * @param boundsMap - Chain-specific map of type names to min/max bounds\n * @returns Enhanced validation object with numeric bounds applied\n *\n * @example\n * ```typescript\n * const stellarBounds = { U32: { min: 0, max: 4_294_967_295 } };\n * const validation = enhanceNumericValidation(undefined, 'U32', stellarBounds);\n * // Returns: { min: 0, max: 4_294_967_295 }\n * ```\n */\nexport function enhanceNumericValidation(\n validation: FieldValidation | undefined,\n parameterType: string,\n boundsMap: NumericBoundsMap\n): FieldValidation {\n const result: FieldValidation = { ...(validation ?? {}) };\n const bounds = boundsMap[parameterType];\n\n if (!bounds) {\n return result;\n }\n\n // Only apply bounds if they're not already set\n if (bounds.min !== undefined && result.min === undefined) {\n result.min = bounds.min;\n }\n\n if (bounds.max !== undefined && result.max === undefined) {\n result.max = bounds.max;\n }\n\n return result;\n}\n","/**\n * Type guard to check if a value is a non-null object (Record<string, unknown>).\n * Useful for safely accessing properties on an 'unknown' type after this check.\n * @param value - The value to check.\n * @returns True if the value is a non-null object, false otherwise.\n */\nexport function isRecordWithProperties(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\n/**\n * Type guard to check if a value is a plain object (not an array, not null).\n * This is useful for distinguishing between objects and arrays, since arrays are technically objects in JavaScript.\n * @param value - The value to check.\n * @returns True if the value is a plain object (not array, not null), false otherwise.\n */\nexport function isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n// Add other type guards here in the future if needed.\n","import { clsx, type ClassValue } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\n/**\n * Combines class names using clsx and tailwind-merge.\n * @param inputs - Class values to combine\n * @returns Merged class name string\n */\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n","/**\n * String and date formatting utility functions\n * These utilities help with common formatting operations\n */\n\n/**\n * Truncates a string (like an Ethereum address) in the middle\n * @param str The string to truncate\n * @param startChars Number of characters to show at the beginning\n * @param endChars Number of characters to show at the end\n * @returns The truncated string with ellipsis in the middle\n */\nexport function truncateMiddle(str: string, startChars = 6, endChars = 4): string {\n if (!str) return '';\n if (str.length <= startChars + endChars) return str;\n\n return `${str.substring(0, startChars)}...${str.substring(str.length - endChars)}`;\n}\n\n/**\n * Formats a timestamp as a relative time string (e.g., \"2h ago\", \"just now\")\n * @param date The date to format\n * @returns A human-readable relative time string\n */\nexport function formatTimestamp(date: Date): string {\n const now = new Date();\n const diffMs = now.getTime() - date.getTime();\n const diffMinutes = Math.floor(diffMs / (1000 * 60));\n const diffHours = Math.floor(diffMs / (1000 * 60 * 60));\n const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));\n\n if (diffMinutes < 1) return 'just now';\n if (diffMinutes < 60) return `${diffMinutes}m ago`;\n if (diffHours < 24) return `${diffHours}h ago`;\n if (diffDays < 7) return `${diffDays}d ago`;\n\n return date.toLocaleDateString();\n}\n\n/**\n * Detects whether a string contains hex-encoded or base64-encoded binary data.\n * Useful for auto-detecting the encoding format of user inputs across blockchain adapters.\n *\n * @param value - The string to analyze\n * @returns 'hex' if the string appears to be hexadecimal, 'base64' if it appears to be base64\n *\n * @example\n * ```typescript\n * detectBytesEncoding(\"48656c6c6f\") // → 'hex'\n * detectBytesEncoding(\"SGVsbG8=\") // → 'base64'\n * detectBytesEncoding(\"0x48656c6c6f\") // → 'hex' (after stripping 0x prefix)\n * ```\n */\nexport function detectBytesEncoding(value: string): 'base64' | 'hex' {\n // Normalize input and handle common prefixes\n const trimmed = value?.trim() ?? '';\n const without0x =\n trimmed.startsWith('0x') || trimmed.startsWith('0X') ? trimmed.slice(2) : trimmed;\n\n const hexRegex = /^[0-9a-fA-F]+$/;\n const base64Regex = /^[A-Za-z0-9+/]*={0,2}$/;\n\n // Prefer hex when clearly valid (even length and hex charset)\n if (hexRegex.test(without0x) && without0x.length > 0 && without0x.length % 2 === 0) {\n return 'hex';\n }\n\n // Validate base64 by pattern + round-trip check\n if (base64Regex.test(trimmed) && trimmed.length % 4 === 0) {\n try {\n const decoded = atob(trimmed);\n const reencoded = btoa(decoded);\n if (reencoded.replace(/=+$/, '') === trimmed.replace(/=+$/, '')) {\n return 'base64';\n }\n } catch {\n // fall through to safer hex default\n }\n }\n\n // Safer default: return 'hex' when ambiguous or invalid\n return 'hex';\n}\n","import { v4 as uuidv4 } from 'uuid';\n\n/**\n * General utility functions, which are not specific to any blockchain\n * It's important to keep these functions as simple as possible and avoid any\n * dependencies from other packages.\n */\n\n/**\n * Generates a unique ID for form fields, components, etc.\n * Uses crypto.getRandomValues() for browser-compatible random ID generation.\n *\n * @param prefix Optional prefix to add before the UUID\n * @returns A string ID that is guaranteed to be unique\n */\nexport function generateId(prefix?: string): string {\n // Generate a browser-compatible UUID v4\n const uuid = uuidv4();\n\n return prefix ? `${prefix}_${uuid}` : uuid;\n}\n","/**\n * URL validation utilities\n */\n\n/**\n * Validates if a string is a valid URL (supports http, https, and ftp protocols).\n * Relies solely on the URL constructor for validation.\n *\n * @param urlString - The string to validate\n * @returns True if the URL is valid, false otherwise\n */\nexport function isValidUrl(urlString: string): boolean {\n if (!urlString || typeof urlString !== 'string') {\n return false;\n }\n\n try {\n // First check with URL constructor for basic validity\n new URL(urlString);\n\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Gets a user-friendly error message for invalid URLs.\n *\n * @returns Standard error message for invalid URLs\n */\nexport function getInvalidUrlMessage(): string {\n return 'Please enter a valid URL (e.g., https://example.com)';\n}\n","/**\n * Utility to add delay between operations\n * @param ms - Milliseconds to delay\n * @returns Promise that resolves after the specified delay\n */\nexport const delay = (ms: number): Promise<void> =>\n new Promise((resolve) => setTimeout(resolve, ms));\n\n/**\n * Executes operations in batches with rate limiting to prevent API overload\n * @param operations - Array of functions that return promises\n * @param batchSize - Number of operations to execute in parallel per batch (default: 2)\n * @param delayMs - Delay in milliseconds between batches (default: 100)\n * @returns Promise that resolves to an array of results from all operations\n */\nexport async function rateLimitedBatch<T>(\n operations: (() => Promise<T>)[],\n batchSize: number = 2,\n delayMs: number = 100\n): Promise<T[]> {\n const results: T[] = [];\n\n for (let i = 0; i < operations.length; i += batchSize) {\n const batch = operations.slice(i, i + batchSize);\n const batchResults = await Promise.all(batch.map((operation) => operation()));\n results.push(...batchResults);\n\n // Add delay between batches (except for the last batch)\n if (i + batchSize < operations.length) {\n await delay(delayMs);\n }\n }\n\n return results;\n}\n\n/**\n * Wraps a promise with a timeout. Rejects with a descriptive Error after timeoutMs.\n *\n * @param promise The promise to wrap\n * @param timeoutMs Timeout in milliseconds\n * @param label Optional label to include in the timeout error message\n */\nexport function withTimeout<T>(promise: Promise<T>, timeoutMs: number, label?: string): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n const timer = setTimeout(() => {\n reject(new Error(`${label ?? 'operation'} timed out after ${timeoutMs}ms`));\n }, timeoutMs);\n\n promise\n .then((value) => {\n clearTimeout(timer);\n resolve(value);\n })\n .catch((err) => {\n clearTimeout(timer);\n reject(err);\n });\n });\n}\n\n// ============================================================================\n// Concurrency-Limited Execution\n// ============================================================================\n//\n// These utilities provide controlled parallel execution with a concurrency limit.\n//\n// WHY USE THESE INSTEAD OF `rateLimitedBatch`?\n//\n// `rateLimitedBatch` processes operations in fixed-size batches with delays between\n// batches. This is good for strict rate limiting but can leave capacity unused:\n// - Batch 1: [op1, op2] → wait for both → delay → Batch 2: [op3, op4] → ...\n// - If op1 finishes quickly, its slot sits idle until the entire batch completes.\n//\n// `promiseAllWithLimit` uses a worker pool approach with a sliding window:\n// - Maintains N concurrent operations at all times (where N = limit)\n// - As soon as one operation completes, the next one starts immediately\n// - No artificial delays between operations\n// - Maximizes throughput while respecting the concurrency limit\n//\n// USE CASES:\n// - `rateLimitedBatch`: When you need enforced pauses (e.g., API rate limits with\n// explicit wait requirements, or when you want predictable batch timing)\n// - `promiseAllWithLimit`: When you want maximum throughput without overwhelming\n// the target service (e.g., RPC endpoints, database connections)\n//\n// ============================================================================\n\n/**\n * Default concurrency limit for parallel operations.\n * Set to a reasonable value that balances performance and service limits.\n */\nexport const DEFAULT_CONCURRENCY_LIMIT = 10;\n\n/**\n * Execute an array of promise-returning functions with a concurrency limit.\n *\n * Uses a worker pool approach that maintains up to `limit` concurrent operations.\n * As soon as one operation completes, the next one starts immediately, maximizing\n * throughput while respecting the concurrency limit.\n *\n * Results are returned in the same order as the input tasks, regardless of\n * completion order.\n *\n * @param tasks Array of functions that return promises\n * @param limit Maximum number of concurrent executions (default: 10)\n * @returns Promise resolving to array of results in same order as input tasks\n *\n * @example\n * ```typescript\n * // Fetch 100 role members with max 10 concurrent RPC requests\n * const tasks = memberIndices.map((index) => () => getRoleMember(contract, role, index));\n * const members = await promiseAllWithLimit(tasks, 10);\n * ```\n */\nexport async function promiseAllWithLimit<T>(\n tasks: (() => Promise<T>)[],\n limit: number = DEFAULT_CONCURRENCY_LIMIT\n): Promise<T[]> {\n if (tasks.length === 0) {\n return [];\n }\n\n // If limit is greater than or equal to task count, just use Promise.all\n if (limit >= tasks.length) {\n return Promise.all(tasks.map((task) => task()));\n }\n\n const results: T[] = new Array(tasks.length);\n let currentIndex = 0;\n\n // Worker function that processes tasks from the queue\n async function worker(): Promise<void> {\n while (currentIndex < tasks.length) {\n const index = currentIndex++;\n const task = tasks[index];\n results[index] = await task();\n }\n }\n\n // Create worker promises up to the concurrency limit\n const workers = Array.from({ length: Math.min(limit, tasks.length) }, () => worker());\n\n await Promise.all(workers);\n\n return results;\n}\n\n/**\n * Execute an array of promise-returning functions with a concurrency limit,\n * settling all promises (similar to Promise.allSettled but with concurrency control).\n *\n * Unlike promiseAllWithLimit, this function does not fail fast on errors.\n * All tasks will be executed regardless of individual failures.\n *\n * @param tasks Array of functions that return promises\n * @param limit Maximum number of concurrent executions (default: 10)\n * @returns Promise resolving to array of settled results in same order as input tasks\n *\n * @example\n * ```typescript\n * const tasks = items.map((item) => () => fetchItem(item.id));\n * const results = await promiseAllSettledWithLimit(tasks, 10);\n *\n * for (const result of results) {\n * if (result.status === 'fulfilled') {\n * console.log('Success:', result.value);\n * } else {\n * console.log('Failed:', result.reason);\n * }\n * }\n * ```\n */\nexport async function promiseAllSettledWithLimit<T>(\n tasks: (() => Promise<T>)[],\n limit: number = DEFAULT_CONCURRENCY_LIMIT\n): Promise<PromiseSettledResult<T>[]> {\n if (tasks.length === 0) {\n return [];\n }\n\n // If limit is greater than or equal to task count, just use Promise.allSettled\n if (limit >= tasks.length) {\n return Promise.allSettled(tasks.map((task) => task()));\n }\n\n const results: PromiseSettledResult<T>[] = new Array(tasks.length);\n let currentIndex = 0;\n\n // Worker function that processes tasks from the queue\n async function worker(): Promise<void> {\n while (currentIndex < tasks.length) {\n const index = currentIndex++;\n const task = tasks[index];\n try {\n const value = await task();\n results[index] = { status: 'fulfilled', value };\n } catch (reason) {\n results[index] = { status: 'rejected', reason };\n }\n }\n }\n\n // Create worker promises up to the concurrency limit\n const workers = Array.from({ length: Math.min(limit, tasks.length) }, () => worker());\n\n await Promise.all(workers);\n\n return results;\n}\n","/**\n * Simple browser-compatible hash utilities\n * These functions provide deterministic hashing for content comparison\n * and are not intended for cryptographic purposes.\n */\n\n/**\n * Creates a simple hash from a string using a non-cryptographic algorithm\n * Suitable for content comparison, caching keys, and quick fingerprinting\n *\n * @param str - The string to hash\n * @returns A hexadecimal hash string (always positive)\n *\n * @example\n * ```typescript\n * const hash1 = simpleHash('{\"name\": \"test\"}');\n * const hash2 = simpleHash('{\"name\": \"test\"}');\n * console.log(hash1 === hash2); // true - deterministic\n * ```\n */\nexport function simpleHash(str: string): string {\n let hash = 0;\n for (let i = 0; i < str.length; i++) {\n const char = str.charCodeAt(i);\n hash = (hash << 5) - hash + char;\n hash = hash & hash; // Convert to 32-bit integer\n }\n return Math.abs(hash).toString(16);\n}\n","import validator from 'validator';\n\n/**\n * Options for bytes validation\n */\nexport interface BytesValidationOptions {\n /**\n * Whether to accept hex, base64, or both formats\n */\n acceptedFormats?: 'hex' | 'base64' | 'both';\n\n /**\n * Maximum length in bytes (not characters)\n */\n maxBytes?: number;\n\n /**\n * Whether to allow 0x prefix for hex values\n */\n allowHexPrefix?: boolean;\n}\n\n/**\n * Result of bytes validation\n */\nexport interface BytesValidationResult {\n /**\n * Whether the input is valid\n */\n isValid: boolean;\n\n /**\n * Error message if validation failed\n */\n error?: string;\n\n /**\n * Detected format of the input\n */\n detectedFormat?: 'hex' | 'base64';\n\n /**\n * Cleaned value (whitespace removed, prefix handled)\n */\n cleanedValue?: string;\n\n /**\n * Size in bytes\n */\n byteSize?: number;\n}\n\n/**\n * Validates bytes input using the established validator.js library.\n *\n * This function provides comprehensive validation for blockchain bytes data including:\n * - Hex encoding validation (with optional 0x prefix)\n * - Base64 encoding validation\n * - Byte length validation\n * - Format detection\n *\n * @param value - The input string to validate\n * @param options - Validation options\n * @returns Validation result with details\n *\n * @example\n * ```typescript\n * validateBytes('48656c6c6f') // → { isValid: true, detectedFormat: 'hex', byteSize: 5 }\n * validateBytes('SGVsbG8=') // → { isValid: true, detectedFormat: 'base64', byteSize: 5 }\n * validateBytes('invalid') // → { isValid: false, error: '...' }\n * ```\n */\nexport function validateBytes(\n value: string,\n options: BytesValidationOptions = {}\n): BytesValidationResult {\n const { acceptedFormats = 'both', maxBytes, allowHexPrefix = true } = options;\n\n // Handle empty values\n if (!value || value.trim() === '') {\n return {\n isValid: true, // Let required validation handle empty values\n cleanedValue: '',\n byteSize: 0,\n };\n }\n\n // Clean the value (remove whitespace)\n const cleanValue = value.trim().replace(/\\s+/g, '');\n\n // Handle hex prefix\n const hasHexPrefix = cleanValue.startsWith('0x');\n const withoutPrefix = hasHexPrefix ? cleanValue.slice(2) : cleanValue;\n\n if (withoutPrefix === '') {\n return {\n isValid: false,\n error: 'Bytes value cannot be empty',\n cleanedValue: cleanValue,\n };\n }\n\n // Detect format using validator.js\n let detectedFormat: 'hex' | 'base64' | null = null;\n let byteSize = 0;\n\n // Try hex validation first (more specific)\n if (validator.isHexadecimal(withoutPrefix)) {\n detectedFormat = 'hex';\n\n // Ensure even length for hex\n if (withoutPrefix.length % 2 !== 0) {\n return {\n isValid: false,\n error: 'Hex string must have even number of characters',\n cleanedValue: cleanValue,\n detectedFormat,\n };\n }\n\n // Calculate byte size after validation\n byteSize = withoutPrefix.length / 2;\n }\n // Try base64 validation\n else if (validator.isBase64(withoutPrefix)) {\n detectedFormat = 'base64';\n\n try {\n // Decode base64 using Web API to calculate byte size\n const decoded = atob(withoutPrefix);\n byteSize = decoded.length;\n } catch {\n return {\n isValid: false,\n error: 'Invalid base64 encoding',\n cleanedValue: cleanValue,\n };\n }\n }\n\n // No format detected\n if (!detectedFormat) {\n return {\n isValid: false,\n error: 'Invalid format. Expected hex or base64 encoding',\n cleanedValue: cleanValue,\n };\n }\n\n // Check if detected format is accepted\n if (acceptedFormats !== 'both') {\n if (acceptedFormats === 'hex' && detectedFormat !== 'hex') {\n return {\n isValid: false,\n error: 'Only hex format is accepted for this field',\n cleanedValue: cleanValue,\n detectedFormat,\n };\n }\n if (acceptedFormats === 'base64' && detectedFormat !== 'base64') {\n return {\n isValid: false,\n error: 'Only base64 format is accepted for this field',\n cleanedValue: cleanValue,\n detectedFormat,\n };\n }\n }\n\n // Check hex prefix policy\n if (detectedFormat === 'hex' && hasHexPrefix && !allowHexPrefix) {\n return {\n isValid: false,\n error: '0x prefix not allowed for this field',\n cleanedValue: cleanValue,\n detectedFormat,\n };\n }\n\n // Check byte length limits\n if (maxBytes && byteSize > maxBytes) {\n const formatInfo =\n detectedFormat === 'hex' ? `${maxBytes * 2} hex characters` : `${maxBytes} bytes`;\n\n return {\n isValid: false,\n error: `Maximum ${maxBytes} bytes allowed (${formatInfo})`,\n cleanedValue: cleanValue,\n detectedFormat,\n byteSize,\n };\n }\n\n return {\n isValid: true,\n cleanedValue: cleanValue,\n detectedFormat,\n byteSize,\n };\n}\n\n/**\n * Simple validation function that returns boolean or error string\n * (for compatibility with existing React Hook Form validation)\n *\n * @param value - The input string to validate\n * @param options - Validation options\n * @returns true if valid, error string if invalid\n */\nexport function validateBytesSimple(\n value: string,\n options: BytesValidationOptions = {}\n): boolean | string {\n const result = validateBytes(value, options);\n return result.isValid ? true : result.error || 'Invalid bytes format';\n}\n\n/**\n * Extracts the size from a Bytes<N> type string, or returns undefined for dynamic Uint8Array\n *\n * @param type - Type string (e.g., \"Bytes<32>\", \"Uint8Array\", \"bytes\")\n * @returns Size in bytes if fixed-size, undefined if dynamic\n *\n * @example\n * ```typescript\n * getBytesSize('Bytes<32>') // → 32\n * getBytesSize('Bytes<64>') // → 64\n * getBytesSize('Uint8Array') // → undefined\n * getBytesSize('bytes') // → undefined\n * ```\n */\nexport function getBytesSize(type: string): number | undefined {\n const match = type.match(/^Bytes<(\\d+)>$/i);\n if (match) {\n return Number.parseInt(match[1], 10);\n }\n return undefined; // Dynamic size (Uint8Array)\n}\n","/**\n * Cross-platform bytes conversion utilities that work in both browser and Node.js\n * without requiring Buffer polyfills.\n */\n\n/**\n * Convert a hex string to Uint8Array using native browser APIs\n * @param hex - Hex string (with or without 0x prefix)\n * @returns Uint8Array representation\n */\nexport function hexToBytes(hex: string): Uint8Array {\n const cleanHex = hex.startsWith('0x') ? hex.slice(2) : hex;\n\n if (cleanHex.length % 2 !== 0) {\n throw new Error('Hex string must have even length');\n }\n\n // Validate hex characters\n if (!/^[0-9a-fA-F]*$/.test(cleanHex)) {\n throw new Error('Invalid hex characters in string');\n }\n\n const bytes = new Uint8Array(cleanHex.length / 2);\n for (let i = 0; i < cleanHex.length; i += 2) {\n bytes[i / 2] = parseInt(cleanHex.substring(i, i + 2), 16);\n }\n\n return bytes;\n}\n\n/**\n * Convert a base64 string to Uint8Array using native browser APIs\n * Handles data URLs by stripping the prefix\n * @param base64 - Base64 encoded string (with optional data URL prefix)\n * @returns Uint8Array representation\n */\nexport function base64ToBytes(base64: string): Uint8Array {\n // Handle data URLs by stripping the prefix\n const cleaned = base64.includes(',') ? base64.split(',')[1] : base64;\n const binaryString = atob(cleaned);\n const len = binaryString.length;\n const bytes = new Uint8Array(len);\n for (let i = 0; i < len; i++) {\n bytes[i] = binaryString.charCodeAt(i);\n }\n return bytes;\n}\n\n/**\n * Convert Uint8Array to hex string\n * @param bytes - Uint8Array to convert\n * @param withPrefix - Whether to include '0x' prefix\n * @returns Hex string representation\n */\nexport function bytesToHex(bytes: Uint8Array, withPrefix = false): string {\n const hex = Array.from(bytes)\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n\n return withPrefix ? `0x${hex}` : hex;\n}\n\n/**\n * Convert string to bytes based on detected encoding (hex or base64)\n * @param value - The string value to convert\n * @param encoding - The detected encoding type\n * @returns Uint8Array representation\n */\nexport function stringToBytes(value: string, encoding: 'hex' | 'base64'): Uint8Array {\n switch (encoding) {\n case 'hex':\n return hexToBytes(value);\n case 'base64':\n return base64ToBytes(value);\n default:\n throw new Error(`Unsupported encoding: ${encoding}. Supported encodings: hex, base64`);\n }\n}\n","/**\n * Utility functions for environment detection\n */\n\n/**\n * Check if the application is running in development or test environment\n * @returns True if NODE_ENV is 'development' or 'test'\n */\nexport function isDevelopmentOrTestEnvironment(): boolean {\n return process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test';\n}\n\n/**\n * Check if the application is running in production environment\n * @returns True if NODE_ENV is 'production'\n */\nexport function isProductionEnvironment(): boolean {\n return process.env.NODE_ENV === 'production';\n}\n","/**\n * RouterService\n *\n * Minimal wrapper to abstract routing. This avoids coupling the app to a\n * specific router implementation and allows future swaps.\n */\nexport interface RouterService {\n /** Returns the current location href (string form). */\n currentLocation(): string;\n /** Returns the value of a query parameter or null if not present. */\n getParam(name: string): string | null;\n /** Navigates to a path (client-side when router is present; falls back to location). */\n navigate(path: string): void;\n}\n\n/**\n * Default implementation that relies on the browser Location API.\n * The builder app can replace this with a router-bound implementation if needed.\n */\nclass BrowserRouterService implements RouterService {\n public currentLocation(): string {\n if (typeof window === 'undefined') return '';\n return window.location.href;\n }\n\n public getParam(name: string): string | null {\n if (typeof window === 'undefined') return null;\n const params = new URLSearchParams(window.location.search);\n return params.get(name);\n }\n\n public navigate(path: string): void {\n if (typeof window === 'undefined') return;\n // Basic implementation: update location. A router-aware version can override this.\n if (path.startsWith('http://') || path.startsWith('https://')) {\n window.location.assign(path);\n } else {\n // Preserve origin and replace pathname/query as provided\n const url = new URL(path, window.location.origin);\n window.history.pushState({}, '', url.toString());\n // Fire a popstate event so listeners can react (if any)\n window.dispatchEvent(new PopStateEvent('popstate'));\n }\n }\n}\n\n/**\n * Singleton instance for global consumption.\n */\nexport const routerService: RouterService = new BrowserRouterService();\n","import { appConfigService } from './AppConfigService';\nimport { logger } from './logger';\n\n/**\n * Google Analytics service for tracking user interactions.\n * Manages Google Analytics initialization and event tracking.\n * Only active when the analytics_enabled feature flag is true.\n *\n * This is a generic service that provides core analytics functionality.\n * App-specific tracking methods should be implemented in app-level hooks\n * that use the generic `trackEvent` method.\n *\n * @example\n * ```typescript\n * // Initialize analytics (typically done once at app startup)\n * AnalyticsService.initialize('G-XXXXXXXXXX');\n *\n * // Track a custom event\n * AnalyticsService.trackEvent('button_clicked', { button_name: 'submit' });\n *\n * // Track page view\n * AnalyticsService.trackPageView('Dashboard', '/dashboard');\n * ```\n */\nexport class AnalyticsService {\n private static initialized = false;\n\n /**\n * Initialize Google Analytics\n * @param tagId - Google Analytics tag ID (e.g., G-N3DZK5FCT1)\n */\n static initialize(tagId: string): void {\n if (!tagId) {\n logger.warn('AnalyticsService', 'No tag ID provided');\n return;\n }\n\n if (!this.isEnabled()) {\n logger.info('AnalyticsService', 'Analytics is disabled via feature flag');\n return;\n }\n\n if (this.initialized) {\n logger.info('AnalyticsService', 'Already initialized');\n return;\n }\n\n try {\n this.loadGtagScript(tagId);\n this.initializeGtag(tagId);\n this.initialized = true;\n logger.info('AnalyticsService', 'Initialized successfully');\n } catch (error) {\n logger.error('AnalyticsService', 'Failed to initialize:', error);\n }\n }\n\n /**\n * Check if analytics is enabled via feature flag\n */\n static isEnabled(): boolean {\n return appConfigService.isFeatureEnabled('analytics_enabled');\n }\n\n /**\n * Reset the analytics service state (primarily for testing)\n */\n static reset(): void {\n this.initialized = false;\n }\n\n /**\n * Generic event tracking method.\n * Use this to track any custom event with arbitrary parameters.\n *\n * @param eventName - Name of the event (e.g., 'button_clicked', 'form_submitted')\n * @param parameters - Key-value pairs of event parameters\n *\n * @example\n * ```typescript\n * AnalyticsService.trackEvent('ecosystem_selected', { ecosystem: 'evm' });\n * AnalyticsService.trackEvent('wizard_step', { step_number: 2, step_name: 'configure' });\n * ```\n */\n static trackEvent(eventName: string, parameters: Record<string, string | number>): void {\n if (!this.isEnabled()) {\n return;\n }\n\n try {\n if (typeof window.gtag === 'function') {\n window.gtag('event', eventName, parameters);\n } else {\n logger.warn('AnalyticsService', 'gtag is not available');\n }\n } catch (error) {\n logger.error('AnalyticsService', `Failed to track event '${eventName}':`, error);\n }\n }\n\n /**\n * Track page view event.\n * Common event shared across all apps.\n *\n * @param pageName - Human-readable name of the page\n * @param pagePath - URL path of the page\n *\n * @example\n * ```typescript\n * AnalyticsService.trackPageView('Dashboard', '/dashboard');\n * ```\n */\n static trackPageView(pageName: string, pagePath: string): void {\n this.trackEvent('page_view', {\n page_title: pageName,\n page_path: pagePath,\n });\n }\n\n /**\n * Track network selection event.\n * Common event shared across all apps that involve network selection.\n *\n * @param networkId - Selected network ID\n * @param ecosystem - Ecosystem the network belongs to (e.g., 'evm', 'stellar')\n *\n * @example\n * ```typescript\n * AnalyticsService.trackNetworkSelection('ethereum-mainnet', 'evm');\n * ```\n */\n static trackNetworkSelection(networkId: string, ecosystem: string): void {\n this.trackEvent('network_selected', {\n network_id: networkId,\n ecosystem,\n });\n }\n\n /**\n * Load the Google Analytics gtag script\n * @private\n */\n private static loadGtagScript(tagId: string): void {\n // Check if script is already loaded\n if (document.querySelector(`script[src*=\"gtag/js?id=${tagId}\"]`)) {\n return;\n }\n\n // Initialize dataLayer\n if (!window.dataLayer) {\n window.dataLayer = [];\n }\n\n // Define gtag function\n window.gtag =\n window.gtag ||\n function gtag() {\n window.dataLayer.push(arguments);\n };\n\n // Create and inject the script\n const script = document.createElement('script');\n script.async = true;\n script.src = `https://www.googletagmanager.com/gtag/js?id=${tagId}`;\n document.head.appendChild(script);\n }\n\n /**\n * Initialize gtag with configuration\n * @private\n */\n private static initializeGtag(tagId: string): void {\n if (typeof window.gtag === 'function') {\n window.gtag('js', new Date());\n window.gtag('config', tagId);\n }\n }\n}\n\n/**\n * Type declarations for Google Analytics gtag\n */\ndeclare global {\n interface Window {\n dataLayer: unknown[];\n gtag: (...args: unknown[]) => void;\n }\n}\n","/**\n * Deep-link utilities (chain-agnostic)\n *\n * NOTE: Stub implementation for TDD. Tests will drive full behavior in Phase 3.3.\n */\nexport type DeepLinkParams = Record<string, string>;\n\n/**\n * Parses URL query parameters into a key-value object.\n * @returns Object containing all URL query parameters\n */\nexport function parseDeepLink(): DeepLinkParams {\n const params = new URLSearchParams(window.location.search);\n const result: DeepLinkParams = {};\n params.forEach((value, key) => {\n result[key] = value;\n });\n return result;\n}\n\n/**\n * Gets the forced service from deep link parameters.\n * @param params - Deep link parameters object\n * @returns Service name if specified, null otherwise\n */\nexport function getForcedService(params: DeepLinkParams): string | null {\n return params.service ?? null;\n}\n\n/**\n * Computes the effective provider preference based on priority order.\n * @param input - Configuration object with provider options\n * @returns The effective provider and its source\n */\nexport function computeEffectiveProviderPreference(input: {\n forcedService?: string | null;\n uiSelection?: string | null;\n appDefault?: string | null;\n adapterDefaultOrder: readonly string[];\n}): { effectiveProvider: string; source: 'urlForced' | 'ui' | 'appConfig' | 'adapterDefault' } {\n if (input.forcedService && input.forcedService.length > 0) {\n return { effectiveProvider: input.forcedService, source: 'urlForced' };\n }\n\n if (input.uiSelection && input.uiSelection.length > 0) {\n return { effectiveProvider: input.uiSelection, source: 'ui' };\n }\n\n if (input.appDefault && input.appDefault.length > 0) {\n return { effectiveProvider: input.appDefault, source: 'appConfig' };\n }\n\n const fallback = input.adapterDefaultOrder[0];\n return { effectiveProvider: fallback, source: 'adapterDefault' };\n}\n","/**\n * Minimal HTML sanitizer for client-side rendering of adapter-provided notes.\n *\n * - Strips <script>/<style> blocks and closing tags\n * - Removes inline event handlers (on*)\n * - Neutralizes javascript: URLs in href/src\n * - Whitelists a small set of tags: a,b,strong,i,em,code,br,ul,ol,li,p\n *\n * This utility is intentionally small and dependency-free. If we decide to\n * allow richer HTML, we can swap this implementation with a vetted library\n * (e.g., DOMPurify) behind the same function signature.\n */\nexport function sanitizeHtml(html: string): string {\n if (!html) return '';\n\n // Remove script/style elements entirely (including contents)\n let out = html\n .replace(/<\\/(?:script|style)>/gi, '')\n .replace(/<(?:script|style)[\\s\\S]*?>[\\s\\S]*?<\\/(?:script|style)>/gi, '');\n\n // Remove inline event handlers like onload=, onclick=\n out = out.replace(/\\son[a-z]+\\s*=\\s*\"[^\"]*\"/gi, '');\n out = out.replace(/\\son[a-z]+\\s*=\\s*'[^']*'/gi, '');\n out = out.replace(/\\son[a-z]+\\s*=\\s*[^\\s>]+/gi, '');\n\n // Neutralize javascript: protocol in href/src\n out = out.replace(/(href|src)\\s*=\\s*\"javascript:[^\"]*\"/gi, '$1=\"#\"');\n out = out.replace(/(href|src)\\s*=\\s*'javascript:[^']*'/gi, '$1=\"#\"');\n\n // Strip non-whitelisted tags (keep: a,b,strong,i,em,code,br,ul,ol,li,p)\n out = out.replace(/<(?!\\/?(?:a|b|strong|i|em|code|br|ul|ol|li|p)\\b)[^>]*>/gi, '');\n\n return out;\n}\n","/**\n * Access Control Snapshot Helpers\n *\n * Utilities for serializing, validating, and working with access control snapshots.\n */\n\nimport type { AccessSnapshot, RoleAssignment, RoleIdentifier } from '@openzeppelin/ui-types';\n\n/**\n * Validates an access snapshot structure\n * @param snapshot The snapshot to validate\n * @returns True if valid, false otherwise\n */\nexport function validateSnapshot(snapshot: AccessSnapshot): boolean {\n if (!snapshot || typeof snapshot !== 'object') {\n return false;\n }\n\n if (!Array.isArray(snapshot.roles)) {\n return false;\n }\n\n // Check for duplicate roles\n const roleIds = new Set<string>();\n for (const roleAssignment of snapshot.roles) {\n if (!roleAssignment || typeof roleAssignment !== 'object') {\n return false;\n }\n\n if (!roleAssignment.role || typeof roleAssignment.role !== 'object') {\n return false;\n }\n\n const roleId = roleAssignment.role.id;\n if (!roleId || typeof roleId !== 'string' || roleId.trim() === '') {\n return false;\n }\n\n if (roleIds.has(roleId)) {\n return false; // Duplicate role\n }\n roleIds.add(roleId);\n\n if (!Array.isArray(roleAssignment.members)) {\n return false;\n }\n\n // Check for duplicate members within a role\n const memberSet = new Set<string>();\n for (const member of roleAssignment.members) {\n if (typeof member !== 'string' || member.trim() === '') {\n return false;\n }\n if (memberSet.has(member)) {\n return false; // Duplicate member\n }\n memberSet.add(member);\n }\n }\n\n // Validate ownership if present\n if (snapshot.ownership !== undefined) {\n if (!snapshot.ownership || typeof snapshot.ownership !== 'object') {\n return false;\n }\n if (\n snapshot.ownership.owner !== null &&\n (typeof snapshot.ownership.owner !== 'string' || snapshot.ownership.owner.trim() === '')\n ) {\n return false;\n }\n }\n\n return true;\n}\n\n/**\n * Serializes an access snapshot to JSON string\n * @param snapshot The snapshot to serialize\n * @returns JSON string representation\n * @throws Error if snapshot is invalid\n */\nexport function serializeSnapshot(snapshot: AccessSnapshot): string {\n if (!validateSnapshot(snapshot)) {\n throw new Error('Invalid snapshot structure');\n }\n return JSON.stringify(snapshot, null, 2);\n}\n\n/**\n * Deserializes a JSON string to an access snapshot\n * @param json The JSON string to deserialize\n * @returns Access snapshot object\n * @throws Error if JSON is invalid or snapshot structure is invalid\n */\nexport function deserializeSnapshot(json: string): AccessSnapshot {\n let parsed: unknown;\n try {\n parsed = JSON.parse(json);\n } catch (error) {\n throw new Error(`Invalid JSON: ${error instanceof Error ? error.message : 'Unknown error'}`);\n }\n\n if (!validateSnapshot(parsed as AccessSnapshot)) {\n throw new Error('Invalid snapshot structure after deserialization');\n }\n\n return parsed as AccessSnapshot;\n}\n\n/**\n * Creates an empty snapshot\n * @returns Empty snapshot with no roles and no ownership\n */\nexport function createEmptySnapshot(): AccessSnapshot {\n return {\n roles: [],\n };\n}\n\n/**\n * Finds a role assignment by role ID\n * @param snapshot The snapshot to search\n * @param roleId The role ID to find\n * @returns The role assignment if found, undefined otherwise\n */\nexport function findRoleAssignment(\n snapshot: AccessSnapshot,\n roleId: string\n): RoleAssignment | undefined {\n return snapshot.roles.find((assignment) => assignment.role.id === roleId);\n}\n\n/**\n * Checks if a snapshot has any roles\n * @param snapshot The snapshot to check\n * @returns True if snapshot has at least one role assignment\n */\nexport function hasRoles(snapshot: AccessSnapshot): boolean {\n return snapshot.roles.length > 0;\n}\n\n/**\n * Checks if a snapshot has ownership information\n * @param snapshot The snapshot to check\n * @returns True if snapshot has ownership information\n */\nexport function hasOwnership(snapshot: AccessSnapshot): boolean {\n return snapshot.ownership !== undefined && snapshot.ownership !== null;\n}\n\n/**\n * Gets the total number of role members across all roles\n * @param snapshot The snapshot to count\n * @returns Total number of unique members across all roles\n */\nexport function getTotalMemberCount(snapshot: AccessSnapshot): number {\n const allMembers = new Set<string>();\n for (const roleAssignment of snapshot.roles) {\n for (const member of roleAssignment.members) {\n allMembers.add(member);\n }\n }\n return allMembers.size;\n}\n\n/**\n * Gets all unique members across all roles\n * @param snapshot The snapshot to extract members from\n * @returns Array of unique member addresses\n */\nexport function getAllMembers(snapshot: AccessSnapshot): string[] {\n const allMembers = new Set<string>();\n for (const roleAssignment of snapshot.roles) {\n for (const member of roleAssignment.members) {\n allMembers.add(member);\n }\n }\n return Array.from(allMembers);\n}\n\n/**\n * Compares two snapshots and returns differences\n * @param snapshot1 First snapshot\n * @param snapshot2 Second snapshot\n * @returns Object describing differences\n */\nexport function compareSnapshots(\n snapshot1: AccessSnapshot,\n snapshot2: AccessSnapshot\n): {\n rolesAdded: RoleAssignment[];\n rolesRemoved: RoleAssignment[];\n rolesModified: Array<{\n role: RoleIdentifier;\n membersAdded: string[];\n membersRemoved: string[];\n }>;\n ownershipChanged: boolean;\n} {\n const rolesAdded: RoleAssignment[] = [];\n const rolesRemoved: RoleAssignment[] = [];\n const rolesModified: Array<{\n role: RoleIdentifier;\n membersAdded: string[];\n membersRemoved: string[];\n }> = [];\n\n const roleMap1 = new Map<string, RoleAssignment>();\n const roleMap2 = new Map<string, RoleAssignment>();\n\n for (const assignment of snapshot1.roles) {\n roleMap1.set(assignment.role.id, assignment);\n }\n\n for (const assignment of snapshot2.roles) {\n roleMap2.set(assignment.role.id, assignment);\n }\n\n // Find added and removed roles\n for (const [roleId, assignment] of roleMap2) {\n if (!roleMap1.has(roleId)) {\n rolesAdded.push(assignment);\n }\n }\n\n for (const [roleId, assignment] of roleMap1) {\n if (!roleMap2.has(roleId)) {\n rolesRemoved.push(assignment);\n }\n }\n\n // Find modified roles\n for (const [roleId, assignment1] of roleMap1) {\n const assignment2 = roleMap2.get(roleId);\n if (assignment2) {\n const members1 = new Set(assignment1.members);\n const members2 = new Set(assignment2.members);\n\n const membersAdded = assignment2.members.filter((m) => !members1.has(m));\n const membersRemoved = assignment1.members.filter((m) => !members2.has(m));\n\n if (membersAdded.length > 0 || membersRemoved.length > 0) {\n rolesModified.push({\n role: assignment1.role,\n membersAdded,\n membersRemoved,\n });\n }\n }\n }\n\n const ownershipChanged = snapshot1.ownership?.owner !== snapshot2.ownership?.owner;\n\n return {\n rolesAdded,\n rolesRemoved,\n rolesModified,\n ownershipChanged,\n };\n}\n","/**\n * Access Control Error Utilities\n *\n * Chain-agnostic helper functions for working with access control errors.\n * These utilities can be used across any adapter that implements access control.\n *\n * Note: These utilities work with the AccessControlError types from @openzeppelin/ui-types\n * but don't import them directly to avoid circular dependencies.\n */\n\n/**\n * Base interface for access control errors (matches the class in types package)\n */\ninterface AccessControlErrorLike extends Error {\n readonly contractAddress?: string;\n}\n\n/**\n * Type guard to check if an error is an AccessControlError\n *\n * @param error The error to check\n * @returns True if the error has the AccessControlError structure\n *\n * @example\n * ```typescript\n * try {\n * await service.grantRole(...);\n * } catch (error) {\n * if (isAccessControlError(error)) {\n * console.log('Access control error:', error.contractAddress);\n * }\n * }\n * ```\n */\nexport function isAccessControlError(error: unknown): error is AccessControlErrorLike {\n return (\n error instanceof Error &&\n 'contractAddress' in error &&\n (typeof (error as AccessControlErrorLike).contractAddress === 'string' ||\n (error as AccessControlErrorLike).contractAddress === undefined)\n );\n}\n\n/**\n * Helper to safely extract error message from unknown error type\n *\n * @param error The error to extract message from\n * @returns The error message string\n *\n * @example\n * ```typescript\n * try {\n * await someOperation();\n * } catch (error) {\n * const message = getErrorMessage(error);\n * logger.error('Operation failed:', message);\n * }\n * ```\n */\nexport function getErrorMessage(error: unknown): string {\n if (error instanceof Error) {\n return error.message;\n }\n return String(error);\n}\n\n/**\n * Helper to create a user-friendly error message with full context\n *\n * This function formats an AccessControlError into a readable multi-line message\n * that includes all relevant context (contract address, roles, operations, etc.).\n *\n * @param error The AccessControlError to format\n * @returns Formatted error message string with all context\n *\n * @example\n * ```typescript\n * import { PermissionDenied } from '@openzeppelin/ui-types';\n * import { formatAccessControlError } from '@openzeppelin/ui-utils';\n *\n * try {\n * await service.grantRole(...);\n * } catch (error) {\n * if (error instanceof PermissionDenied) {\n * const formatted = formatAccessControlError(error);\n * showErrorToUser(formatted);\n * }\n * }\n * ```\n *\n * Output format:\n * ```\n * [ErrorName] Error message\n * Contract: 0x123...\n * [Additional context based on error type]\n * ```\n */\nexport function formatAccessControlError(error: AccessControlErrorLike): string {\n let message = `[${error.name}] ${error.message}`;\n\n if (error.contractAddress) {\n message += `\\nContract: ${error.contractAddress}`;\n }\n\n // Check for specific error types by name and format accordingly\n // This avoids importing the actual classes and keeps utils independent\n const errorWithProperties = error as AccessControlErrorLike & {\n requiredRole?: string;\n callerAddress?: string;\n networkId?: string;\n endpointUrl?: string;\n configField?: string;\n providedValue?: unknown;\n operation?: string;\n cause?: Error;\n missingFeatures?: string[];\n };\n\n if (error.name === 'PermissionDenied') {\n if (errorWithProperties.requiredRole) {\n message += `\\nRequired Role: ${errorWithProperties.requiredRole}`;\n }\n if (errorWithProperties.callerAddress) {\n message += `\\nCaller: ${errorWithProperties.callerAddress}`;\n }\n } else if (error.name === 'IndexerUnavailable') {\n if (errorWithProperties.networkId) {\n message += `\\nNetwork: ${errorWithProperties.networkId}`;\n }\n if (errorWithProperties.endpointUrl) {\n message += `\\nEndpoint: ${errorWithProperties.endpointUrl}`;\n }\n } else if (error.name === 'ConfigurationInvalid') {\n if (errorWithProperties.configField) {\n message += `\\nInvalid Field: ${errorWithProperties.configField}`;\n }\n if (errorWithProperties.providedValue !== undefined) {\n message += `\\nProvided Value: ${JSON.stringify(errorWithProperties.providedValue)}`;\n }\n } else if (error.name === 'OperationFailed') {\n if (errorWithProperties.operation) {\n message += `\\nOperation: ${errorWithProperties.operation}`;\n }\n if (errorWithProperties.cause) {\n message += `\\nCause: ${errorWithProperties.cause.message}`;\n }\n } else if (error.name === 'UnsupportedContractFeatures') {\n if (errorWithProperties.missingFeatures && Array.isArray(errorWithProperties.missingFeatures)) {\n message += `\\nMissing Features: ${errorWithProperties.missingFeatures.join(', ')}`;\n }\n }\n\n return message;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["uuid"],"sources":["../src/contractInputs.ts","../src/requiredInputs.ts","../src/addressNormalization.ts","../src/logger.ts","../src/AppConfigService.ts","../src/UserRpcConfigService.ts","../src/UserExplorerConfigService.ts","../src/UserNetworkServiceConfigService.ts","../src/fieldDefaults.ts","../src/fieldValidation.ts","../src/typeguards.ts","../src/cn.ts","../src/formatting.ts","../src/generateId.ts","../src/validators.ts","../src/async.ts","../src/hash.ts","../src/bytesValidation.ts","../src/bytesConversion.ts","../src/environment.ts","../src/RouterService.ts","../src/AnalyticsService.ts","../src/deepLink.ts","../src/sanitize.ts","../src/access/snapshot.ts","../src/access/errors.ts","../src/walletComponentSizing.ts"],"sourcesContent":["import type { ContractAdapter, FormValues } from '@openzeppelin/ui-types';\n\n/**\n * Returns names of adapter-declared required inputs that are missing/empty in values.\n */\nexport function getMissingRequiredContractInputs(\n adapter: ContractAdapter,\n values: FormValues\n): string[] {\n try {\n const inputs = adapter.getContractDefinitionInputs ? adapter.getContractDefinitionInputs() : [];\n const required = inputs.filter((field: unknown) => {\n const f = field as { validation?: { required?: boolean } };\n return f?.validation?.required === true;\n });\n const missing: string[] = [];\n for (const field of required as Array<{ name?: string; id?: string }>) {\n const key = field.name || field.id || '';\n const raw = (values as Record<string, unknown>)[key];\n if (raw == null) {\n missing.push(key);\n continue;\n }\n if (typeof raw === 'string' && raw.trim().length === 0) {\n missing.push(key);\n }\n }\n return missing;\n } catch {\n return [];\n }\n}\n\n/**\n * True if any adapter-declared required inputs are missing/empty.\n */\nexport function hasMissingRequiredContractInputs(\n adapter: ContractAdapter | null | undefined,\n values: FormValues\n): boolean {\n if (!adapter) return false;\n return getMissingRequiredContractInputs(adapter, values).length > 0;\n}\n","import type { ContractAdapter, FormFieldType, FormValues } from '@openzeppelin/ui-types';\n\ntype RequiredInputSnapshot = Record<string, unknown>;\n\nfunction normalizeSnapshotValue(value: unknown): unknown {\n if (value instanceof File) {\n return {\n name: value.name,\n size: value.size,\n lastModified: value.lastModified,\n };\n }\n\n if (typeof value === 'string') {\n return value.trim();\n }\n\n if (value === undefined) {\n return null;\n }\n\n return value;\n}\n\nfunction extractRequiredFields(adapter: ContractAdapter | null): FormFieldType[] {\n if (!adapter || typeof adapter.getContractDefinitionInputs !== 'function') {\n return [];\n }\n\n try {\n const inputs = adapter.getContractDefinitionInputs() || [];\n return inputs.filter((field) => field.validation?.required);\n } catch {\n return [];\n }\n}\n\n/**\n * Builds a snapshot of required form input values.\n * @param adapter - Contract adapter to get field definitions from\n * @param formValues - Current form values\n * @returns Snapshot of required field values, or null if no required fields\n */\nexport function buildRequiredInputSnapshot(\n adapter: ContractAdapter | null,\n formValues: FormValues | null | undefined\n): RequiredInputSnapshot | null {\n if (!formValues) {\n return null;\n }\n\n const requiredFields = extractRequiredFields(adapter);\n if (requiredFields.length === 0) {\n return null;\n }\n\n const snapshot: RequiredInputSnapshot = {};\n const values = formValues as Record<string, unknown>;\n\n for (const field of requiredFields) {\n const key = field.name || field.id;\n if (!key) continue;\n snapshot[key] = normalizeSnapshotValue(values[key]);\n }\n\n return Object.keys(snapshot).length > 0 ? snapshot : null;\n}\n\n/**\n * Compares two required input snapshots for equality.\n * @param a - First snapshot to compare\n * @param b - Second snapshot to compare\n * @returns True if snapshots are equal, false otherwise\n */\nexport function requiredSnapshotsEqual(\n a: RequiredInputSnapshot | null,\n b: RequiredInputSnapshot | null\n): boolean {\n if (a === b) {\n return true;\n }\n\n if (!a || !b) {\n return false;\n }\n\n const keysA = Object.keys(a).sort();\n const keysB = Object.keys(b).sort();\n\n if (keysA.length !== keysB.length) {\n return false;\n }\n\n for (let i = 0; i < keysA.length; i += 1) {\n if (keysA[i] !== keysB[i]) {\n return false;\n }\n\n const valueA = a[keysA[i]];\n const valueB = b[keysA[i]];\n\n if (\n typeof valueA === 'object' &&\n valueA !== null &&\n typeof valueB === 'object' &&\n valueB !== null\n ) {\n if (JSON.stringify(valueA) !== JSON.stringify(valueB)) {\n return false;\n }\n } else if (valueA !== valueB) {\n return false;\n }\n }\n\n return true;\n}\n","/**\n * Normalizes a contract address by trimming whitespace and converting to lowercase.\n * This is useful for case-insensitive and whitespace-insensitive address comparison.\n *\n * @param address - The address to normalize (string, null, or undefined)\n * @returns The normalized address string, or empty string if input is falsy\n *\n * @example\n * ```ts\n * normalizeAddress(' 0xABC123 ') // Returns '0xabc123'\n * normalizeAddress('0xDEF456') // Returns '0xdef456'\n * normalizeAddress(null) // Returns ''\n * normalizeAddress(undefined) // Returns ''\n * ```\n */\nexport function normalizeAddress(address: string | null | undefined): string {\n if (typeof address === 'string') {\n return address.trim().toLowerCase();\n }\n return '';\n}\n\n/**\n * Compares two addresses after normalization.\n * Returns true if both addresses normalize to the same value.\n *\n * @param address1 - First address to compare\n * @param address2 - Second address to compare\n * @returns True if addresses are equal after normalization\n *\n * @example\n * ```ts\n * addressesEqual(' 0xABC ', '0xabc') // Returns true\n * addressesEqual('0xDEF', '0xABC') // Returns false\n * addressesEqual(null, '') // Returns true\n * ```\n */\nexport function addressesEqual(\n address1: string | null | undefined,\n address2: string | null | undefined\n): boolean {\n return normalizeAddress(address1) === normalizeAddress(address2);\n}\n","/**\n * Logger utility for consistent logging across the application.\n * Supports different log levels and can be disabled for testing.\n */\n\ntype LogLevel = 'debug' | 'info' | 'warn' | 'error';\n\ninterface LoggerOptions {\n enabled: boolean;\n level: LogLevel;\n}\n\nclass Logger {\n private static instance: Logger;\n private options: LoggerOptions = {\n enabled: getDefaultLoggerEnabled(),\n level: 'debug',\n };\n\n private constructor() {}\n\n static getInstance(): Logger {\n if (!Logger.instance) {\n Logger.instance = new Logger();\n }\n return Logger.instance;\n }\n\n configure(options: Partial<LoggerOptions>): void {\n this.options = { ...this.options, ...options };\n }\n\n private shouldLog(level: LogLevel): boolean {\n if (!this.options.enabled) return false;\n\n const levels: LogLevel[] = ['debug', 'info', 'warn', 'error'];\n const configuredLevelIndex = levels.indexOf(this.options.level);\n const currentLevelIndex = levels.indexOf(level);\n\n return currentLevelIndex >= configuredLevelIndex;\n }\n\n private formatMessage(level: LogLevel, system: string, message: string): string {\n return `[${level.toUpperCase()}][${system}] ${message}`;\n }\n\n debug(system: string, message: string, ...args: unknown[]): void {\n if (this.shouldLog('debug')) {\n // eslint-disable-next-line no-console\n console.log(this.formatMessage('debug', system, message), ...args);\n }\n }\n\n info(system: string, message: string, ...args: unknown[]): void {\n if (this.shouldLog('info')) {\n // eslint-disable-next-line no-console\n console.log(this.formatMessage('info', system, message), ...args);\n }\n }\n\n warn(system: string, message: string, ...args: unknown[]): void {\n if (this.shouldLog('warn')) {\n // eslint-disable-next-line no-console\n console.warn(this.formatMessage('warn', system, message), ...args);\n }\n }\n\n error(system: string, message: string, ...args: unknown[]): void {\n if (this.shouldLog('error')) {\n // eslint-disable-next-line no-console\n console.error(this.formatMessage('error', system, message), ...args);\n }\n }\n}\n\nexport const logger = Logger.getInstance();\n\n/**\n * Determine whether logging should be enabled by default.\n *\n * - In Vite/browser contexts, use `import.meta.env.DEV`.\n * - In Node/tsup contexts, use `process.env.NODE_ENV`.\n *\n * Defaults to disabled outside development to avoid runtime overhead and noise.\n */\nfunction getDefaultLoggerEnabled(): boolean {\n // Vite/browser context: prefer explicit export env over DEV flag\n try {\n // Use a narrow, typed access pattern to avoid depending on Vite types in this package\n const viteEnv = (\n import.meta as unknown as {\n env?: { DEV?: boolean; MODE?: string; PROD?: boolean; VITE_EXPORT_ENV?: string };\n }\n ).env;\n\n if (viteEnv) {\n const exportEnv = String(viteEnv.VITE_EXPORT_ENV || '').toLowerCase();\n // Force-disable logging for staging/production deployments regardless of DEV\n if (exportEnv === 'staging' || exportEnv === 'production') {\n return false;\n }\n if (typeof viteEnv.DEV === 'boolean') {\n return viteEnv.DEV;\n }\n }\n } catch {\n // Ignore environments where import.meta is not available or lacks env\n }\n\n // Node/tsup context: also honor VITE_EXPORT_ENV, then fall back to NODE_ENV\n if (typeof process !== 'undefined' && typeof process.env !== 'undefined') {\n const exportEnv = String(process.env.VITE_EXPORT_ENV || '').toLowerCase();\n if (exportEnv === 'staging' || exportEnv === 'production') {\n return false;\n }\n const nodeEnv = process.env.NODE_ENV;\n return nodeEnv === 'development' || nodeEnv === 'test';\n }\n\n // Safe fallback: disabled\n return false;\n}\n","import type {\n AppRuntimeConfig,\n FeatureFlags,\n GlobalServiceConfigs,\n IndexerEndpointConfig,\n NetworkServiceConfigs,\n NetworkSpecificRpcEndpoints,\n RpcEndpointConfig,\n ServiceParameterConfig,\n UiKitName,\n UserRpcProviderConfig,\n} from '@openzeppelin/ui-types';\n\nimport { logger } from './logger';\n\n// Changed from @openzeppelin/ui-utils\n\n/**\n * Type for the strategy array in initialize method.\n */\nexport type ConfigLoadStrategy =\n | { type: 'viteEnv'; env: ViteEnv | undefined }\n | { type: 'json'; path?: string }\n | { type: 'localStorage'; key?: string };\n\nconst VITE_ENV_PREFIX = 'VITE_APP_CFG_';\nconst LOG_SYSTEM = 'AppConfigService'; // Define a constant for the system name\n\n// Define a type for Vite's import.meta.env structure if it exists\ninterface ViteEnv {\n [key: string]: string | boolean | undefined;\n}\n\n/**\n * AppConfigService\n *\n * Responsible for loading, merging, and providing access to the application's\n * runtime configuration (`AppRuntimeConfig`).\n */\nexport class AppConfigService {\n private config: AppRuntimeConfig;\n private isInitialized = false;\n\n /**\n * Creates a new AppConfigService with default configuration.\n */\n constructor() {\n // Initialize with sensible hardcoded defaults\n this.config = {\n networkServiceConfigs: {},\n globalServiceConfigs: {},\n rpcEndpoints: {},\n indexerEndpoints: {},\n featureFlags: {},\n defaultLanguage: 'en',\n };\n logger.info(LOG_SYSTEM, 'Service initialized with default configuration.');\n }\n\n private loadFromViteEnvironment(envSource: ViteEnv | undefined): void {\n logger.debug(\n LOG_SYSTEM,\n 'BEGIN loadFromViteEnvironment. envSource received:',\n envSource ? JSON.stringify(envSource) : 'undefined'\n );\n\n if (typeof envSource === 'undefined') {\n logger.warn(\n LOG_SYSTEM,\n 'Vite environment object (envSource) was undefined. Skipping Vite env load.'\n );\n return;\n }\n const env = envSource;\n const loadedNetworkServiceConfigs: NetworkServiceConfigs = {};\n const loadedGlobalServiceConfigs: GlobalServiceConfigs = {};\n const loadedRpcEndpoints: NetworkSpecificRpcEndpoints = {};\n const loadedIndexerEndpoints: Record<string, string> = {};\n const loadedFeatureFlags: FeatureFlags = {};\n\n for (const key in env) {\n if (Object.prototype.hasOwnProperty.call(env, key) && env[key] !== undefined) {\n const value = String(env[key]);\n\n if (key.startsWith(`${VITE_ENV_PREFIX}API_KEY_`)) {\n const serviceIdSuffix = key.substring(`${VITE_ENV_PREFIX}API_KEY_`.length);\n const serviceIdentifier = serviceIdSuffix.toLowerCase().replace(/_/g, '-');\n if (!loadedNetworkServiceConfigs[serviceIdentifier]) {\n loadedNetworkServiceConfigs[serviceIdentifier] = {};\n }\n loadedNetworkServiceConfigs[serviceIdentifier]!.apiKey = value;\n } else if (key.startsWith(`${VITE_ENV_PREFIX}SERVICE_`)) {\n const fullSuffix = key.substring(`${VITE_ENV_PREFIX}SERVICE_`.length); // e.g., WALLETCONNECT_PROJECT_ID or ANOTHER_SERVICE_API_URL\n\n const firstUnderscoreIndex = fullSuffix.indexOf('_');\n if (firstUnderscoreIndex > 0 && firstUnderscoreIndex < fullSuffix.length - 1) {\n // Ensure underscore is present and not at start/end\n const serviceName = fullSuffix.substring(0, firstUnderscoreIndex).toLowerCase(); // e.g., \"walletconnect\", \"anotherservice\"\n const paramNameRaw = fullSuffix.substring(firstUnderscoreIndex + 1); // e.g., \"PROJECT_ID\", \"API_URL\"\n\n // Convert paramNameRaw (e.g., PROJECT_ID or API_URL) to camelCase (projectId, apiUrl)\n const paramName = paramNameRaw\n .toLowerCase()\n .replace(/_([a-z])/g, (g) => g[1].toUpperCase());\n\n if (serviceName && paramName) {\n if (!loadedGlobalServiceConfigs[serviceName]) {\n loadedGlobalServiceConfigs[serviceName] = {};\n }\n loadedGlobalServiceConfigs[serviceName]![paramName] = value;\n logger.debug(\n LOG_SYSTEM,\n `Parsed service: '${serviceName}', param: '${paramName}', value: '${value}' from key: ${key}`\n );\n } else {\n logger.warn(LOG_SYSTEM, `Could not effectively parse service/param from key: ${key}`);\n }\n } else {\n logger.warn(\n LOG_SYSTEM,\n `Could not determine service and param from key (missing underscore separator): ${key}`\n );\n }\n } else if (key === `${VITE_ENV_PREFIX}WALLETCONNECT_PROJECT_ID`) {\n // Directly handle the VITE_APP_CFG_WALLETCONNECT_PROJECT_ID case\n if (!loadedGlobalServiceConfigs.walletconnect) {\n loadedGlobalServiceConfigs.walletconnect = {};\n }\n loadedGlobalServiceConfigs.walletconnect.projectId = value;\n logger.debug(\n LOG_SYSTEM,\n `Parsed WalletConnect Project ID directly from key: ${key}, value: ${value}`\n );\n } else if (key.startsWith(`${VITE_ENV_PREFIX}RPC_ENDPOINT_`)) {\n const networkIdSuffix = key.substring(`${VITE_ENV_PREFIX}RPC_ENDPOINT_`.length);\n const networkId = networkIdSuffix.toLowerCase().replace(/_/g, '-');\n if (networkId) {\n loadedRpcEndpoints[networkId] = value;\n logger.debug(LOG_SYSTEM, `Loaded RPC override for ${networkId}: ${value}`);\n }\n } else if (key.startsWith(`${VITE_ENV_PREFIX}INDEXER_ENDPOINT_`)) {\n const networkIdSuffix = key.substring(`${VITE_ENV_PREFIX}INDEXER_ENDPOINT_`.length);\n const networkId = networkIdSuffix.toLowerCase().replace(/_/g, '-');\n if (networkId) {\n loadedIndexerEndpoints[networkId] = value;\n logger.debug(LOG_SYSTEM, `Loaded indexer endpoint for ${networkId}: ${value}`);\n }\n } else if (key.startsWith(`${VITE_ENV_PREFIX}FEATURE_FLAG_`)) {\n const flagNameSuffix = key.substring(`${VITE_ENV_PREFIX}FEATURE_FLAG_`.length);\n const flagName = flagNameSuffix.toLowerCase();\n loadedFeatureFlags[flagName] = value.toLowerCase() === 'true';\n } else if (key === `${VITE_ENV_PREFIX}DEFAULT_LANGUAGE`) {\n this.config.defaultLanguage = value;\n }\n }\n }\n\n this.config.networkServiceConfigs = {\n ...this.config.networkServiceConfigs,\n ...loadedNetworkServiceConfigs,\n };\n if (Object.keys(loadedGlobalServiceConfigs).length > 0) {\n if (!this.config.globalServiceConfigs) this.config.globalServiceConfigs = {};\n for (const serviceKeyInLoaded in loadedGlobalServiceConfigs) {\n if (Object.prototype.hasOwnProperty.call(loadedGlobalServiceConfigs, serviceKeyInLoaded)) {\n this.config.globalServiceConfigs[serviceKeyInLoaded] = {\n ...(this.config.globalServiceConfigs[serviceKeyInLoaded] || {}),\n ...loadedGlobalServiceConfigs[serviceKeyInLoaded],\n };\n }\n }\n }\n if (Object.keys(loadedRpcEndpoints).length > 0) {\n if (!this.config.rpcEndpoints) this.config.rpcEndpoints = {};\n for (const networkKey in loadedRpcEndpoints) {\n if (Object.prototype.hasOwnProperty.call(loadedRpcEndpoints, networkKey)) {\n this.config.rpcEndpoints[networkKey] = loadedRpcEndpoints[networkKey];\n }\n }\n }\n if (Object.keys(loadedIndexerEndpoints).length > 0) {\n if (!this.config.indexerEndpoints) this.config.indexerEndpoints = {};\n for (const networkKey in loadedIndexerEndpoints) {\n if (Object.prototype.hasOwnProperty.call(loadedIndexerEndpoints, networkKey)) {\n this.config.indexerEndpoints[networkKey] = loadedIndexerEndpoints[networkKey];\n }\n }\n }\n this.config.featureFlags = { ...this.config.featureFlags, ...loadedFeatureFlags };\n\n logger.info(\n LOG_SYSTEM,\n 'Resolved globalServiceConfigs after Vite env processing:',\n this.config.globalServiceConfigs\n ? JSON.stringify(this.config.globalServiceConfigs)\n : 'undefined'\n );\n logger.info(\n LOG_SYSTEM,\n 'Resolved rpcEndpoints after Vite env processing:',\n this.config.rpcEndpoints ? JSON.stringify(this.config.rpcEndpoints) : 'undefined'\n );\n logger.info(\n LOG_SYSTEM,\n 'Configuration loaded/merged from provided Vite environment variables.'\n );\n }\n\n private async loadFromJson(filePath = '/app.config.json'): Promise<void> {\n try {\n const response = await fetch(filePath);\n if (!response.ok) {\n // It's okay if the file is not found, could be optional or for specific environments\n if (response.status === 404) {\n logger.info(\n LOG_SYSTEM,\n `Optional configuration file not found at ${filePath}. Skipping.`\n );\n } else {\n logger.warn(\n LOG_SYSTEM,\n `Failed to fetch config from ${filePath}: ${response.status} ${response.statusText}`\n );\n }\n return;\n }\n const externalConfig = (await response.json()) as Partial<AppRuntimeConfig>; // Use Partial for safer merging\n\n if (externalConfig.networkServiceConfigs) {\n if (!this.config.networkServiceConfigs) this.config.networkServiceConfigs = {};\n for (const key in externalConfig.networkServiceConfigs) {\n if (Object.prototype.hasOwnProperty.call(externalConfig.networkServiceConfigs, key)) {\n this.config.networkServiceConfigs[key] = {\n ...(this.config.networkServiceConfigs[key] || {}),\n ...externalConfig.networkServiceConfigs[key],\n };\n }\n }\n }\n\n if (externalConfig.globalServiceConfigs) {\n if (!this.config.globalServiceConfigs) this.config.globalServiceConfigs = {};\n for (const serviceKey in externalConfig.globalServiceConfigs) {\n if (\n Object.prototype.hasOwnProperty.call(externalConfig.globalServiceConfigs, serviceKey)\n ) {\n this.config.globalServiceConfigs[serviceKey] = {\n ...(this.config.globalServiceConfigs[serviceKey] || {}),\n ...externalConfig.globalServiceConfigs[serviceKey],\n };\n }\n }\n }\n\n if (externalConfig.rpcEndpoints) {\n if (!this.config.rpcEndpoints) this.config.rpcEndpoints = {};\n for (const networkKey in externalConfig.rpcEndpoints) {\n if (Object.prototype.hasOwnProperty.call(externalConfig.rpcEndpoints, networkKey)) {\n this.config.rpcEndpoints[networkKey] = externalConfig.rpcEndpoints[networkKey];\n }\n }\n }\n\n if (externalConfig.indexerEndpoints) {\n if (!this.config.indexerEndpoints) this.config.indexerEndpoints = {};\n for (const networkKey in externalConfig.indexerEndpoints) {\n if (Object.prototype.hasOwnProperty.call(externalConfig.indexerEndpoints, networkKey)) {\n this.config.indexerEndpoints[networkKey] = externalConfig.indexerEndpoints[networkKey];\n }\n }\n }\n\n if (externalConfig.featureFlags) {\n this.config.featureFlags = {\n ...(this.config.featureFlags || {}),\n ...externalConfig.featureFlags,\n };\n }\n\n if (typeof externalConfig.defaultLanguage === 'string') {\n this.config.defaultLanguage = externalConfig.defaultLanguage;\n }\n // Add merging for other AppRuntimeConfig properties here\n\n logger.info(LOG_SYSTEM, `Configuration loaded/merged from ${filePath}`);\n } catch (error) {\n logger.error(LOG_SYSTEM, `Error loading or parsing config from ${filePath}:`, error);\n }\n }\n\n /**\n * Initializes the service by loading configuration from the specified strategies.\n * @param strategies - Array of configuration loading strategies to apply\n */\n public async initialize(strategies: ConfigLoadStrategy[]): Promise<void> {\n logger.info(LOG_SYSTEM, 'Initialization sequence started with strategies:', strategies);\n // Ensure strategies are processed in defined order of precedence\n // Example: Defaults (in constructor) -> Vite Env -> JSON -> LocalStorage\n\n for (const strategy of strategies) {\n if (strategy.type === 'viteEnv') {\n this.loadFromViteEnvironment(strategy.env);\n } else if (strategy.type === 'json') {\n await this.loadFromJson(strategy.path);\n }\n // Add localStorage strategy in Phase 4\n }\n this.isInitialized = true;\n logger.info(LOG_SYSTEM, 'Initialization complete.');\n }\n\n /**\n * Gets the API key for a specific explorer service.\n * @param serviceIdentifier - The service identifier\n * @returns The API key if configured, undefined otherwise\n */\n public getExplorerApiKey(serviceIdentifier: string): string | undefined {\n if (!this.isInitialized) {\n logger.warn(LOG_SYSTEM, 'getExplorerApiKey called before initialization.');\n }\n return this.config.networkServiceConfigs?.[serviceIdentifier]?.apiKey;\n }\n\n /**\n * Gets the configuration for a global service.\n * @param serviceName - The name of the service\n * @returns The service configuration if found, undefined otherwise\n */\n public getGlobalServiceConfig(serviceName: string): ServiceParameterConfig | undefined {\n if (!this.isInitialized) {\n logger.warn(LOG_SYSTEM, 'getGlobalServiceConfig called before initialization.');\n }\n return this.config.globalServiceConfigs?.[serviceName];\n }\n\n /**\n * Checks if a feature flag is enabled.\n * @param flagName - The name of the feature flag\n * @returns True if the feature is enabled, false otherwise\n */\n public isFeatureEnabled(flagName: string): boolean {\n if (!this.isInitialized) {\n logger.warn(LOG_SYSTEM, 'isFeatureEnabled called before initialization.');\n }\n return this.config.featureFlags?.[flagName.toLowerCase()] ?? false;\n }\n\n /**\n * Gets a global service parameter value.\n * @param serviceName The name of the service\n * @param paramName The name of the parameter\n * @returns The parameter value (can be any type including objects, arrays) or undefined if not found\n */\n public getGlobalServiceParam(\n serviceName: string,\n paramName: string\n ): string | number | boolean | object | Array<unknown> | undefined {\n if (!this.isInitialized) {\n logger.warn(LOG_SYSTEM, 'getGlobalServiceParam called before initialization.');\n return undefined;\n }\n const serviceParams = this.config.globalServiceConfigs?.[serviceName.toLowerCase()];\n // Parameter names are stored in camelCase (e.g., projectId)\n return serviceParams?.[paramName];\n }\n\n /**\n * Gets the RPC endpoint override for a specific network.\n * @param networkId - The network identifier\n * @returns The RPC endpoint configuration if found, undefined otherwise\n */\n public getRpcEndpointOverride(\n networkId: string\n ): string | RpcEndpointConfig | UserRpcProviderConfig | undefined {\n if (!this.isInitialized) {\n logger.warn(LOG_SYSTEM, 'getRpcEndpointOverride called before initialization.');\n }\n return this.config.rpcEndpoints?.[networkId];\n }\n\n /**\n * Get the indexer endpoint override for a specific network.\n * Indexer endpoints are used for querying historical blockchain data.\n * @param networkId The network identifier (e.g., 'stellar-testnet')\n * @returns The indexer endpoint configuration, or undefined if not configured\n */\n public getIndexerEndpointOverride(networkId: string): string | IndexerEndpointConfig | undefined {\n if (!this.isInitialized) {\n logger.warn(LOG_SYSTEM, 'getIndexerEndpointOverride called before initialization.');\n }\n return this.config.indexerEndpoints?.[networkId];\n }\n\n /**\n * Returns the entire configuration object.\n * Primarily for debugging or for parts of the app that need a broader view.\n * Use specific getters like `getExplorerApiKey` or `isFeatureEnabled` where possible.\n */\n public getConfig(): Readonly<AppRuntimeConfig> {\n return this.config;\n }\n\n /**\n * Gets a nested configuration object with type safety.\n *\n * This is a helper method to safely access complex nested configuration objects\n * with proper TypeScript type checking.\n *\n * @param serviceName The name of the service (e.g., 'walletui')\n * @param paramName The parameter name that contains the nested object (e.g., 'config')\n * Pass an empty string to get the entire service configuration.\n * @returns The typed nested configuration object or undefined if not found\n *\n * @example\n * // Get a typed UI kit configuration:\n * const uiConfig = appConfigService.getTypedNestedConfig<UiKitConfiguration>('walletui', 'config');\n * if (uiConfig) {\n * console.log(uiConfig.kitName); // Properly typed\n * }\n *\n * // Get entire service configuration:\n * const allAnalytics = appConfigService.getTypedNestedConfig<AnalyticsConfig>('analytics', '');\n */\n public getTypedNestedConfig<T extends object>(\n serviceName: string,\n paramName: string\n ): T | undefined {\n if (!this.isInitialized) {\n logger.warn(LOG_SYSTEM, 'getTypedNestedConfig called before initialization.');\n return undefined;\n }\n\n try {\n // If paramName is empty, return the entire service configuration\n if (paramName === '') {\n const serviceConfig = this.config.globalServiceConfigs?.[serviceName.toLowerCase()];\n if (serviceConfig && typeof serviceConfig === 'object') {\n return serviceConfig as T;\n }\n return undefined;\n }\n\n // Otherwise, get the specific nested parameter\n const param = this.getGlobalServiceParam(serviceName, paramName);\n\n if (param && typeof param === 'object') {\n // We've confirmed it's an object, so we can safely cast it\n return param as T;\n }\n\n return undefined;\n } catch (error) {\n logger.warn(\n LOG_SYSTEM,\n `Error accessing nested configuration for ${serviceName}.${paramName}:`,\n error\n );\n return undefined;\n }\n }\n\n /**\n * Checks if a nested configuration exists and has a specific property.\n *\n * @param serviceName The name of the service\n * @param paramName The parameter name containing the nested object\n * @param propName The property name to check for\n * @returns True if the property exists in the nested configuration\n *\n * @example\n * if (appConfigService.hasNestedConfigProperty('walletui', 'config', 'showInjectedConnector')) {\n * // Do something when the property exists\n * }\n */\n public hasNestedConfigProperty(\n serviceName: string,\n paramName: string,\n propName: string\n ): boolean {\n const nestedConfig = this.getTypedNestedConfig<Record<string, unknown>>(serviceName, paramName);\n\n return (\n nestedConfig !== undefined && Object.prototype.hasOwnProperty.call(nestedConfig, propName)\n );\n }\n\n /**\n * Gets wallet UI configuration for a specific ecosystem.\n * Uses ecosystem-namespaced format with optional default fallback.\n *\n * @param ecosystemId The ecosystem ID (e.g., 'stellar', 'evm', 'solana')\n * @returns The wallet UI configuration for the ecosystem, or undefined\n *\n * @example\n * Configuration format:\n * {\n * \"globalServiceConfigs\": {\n * \"walletui\": {\n * \"stellar\": { \"kitName\": \"stellar-wallets-kit\", \"kitConfig\": {} },\n * \"evm\": { \"kitName\": \"rainbowkit\", \"kitConfig\": {} },\n * \"default\": { \"kitName\": \"custom\", \"kitConfig\": {} }\n * }\n * }\n * }\n * const stellarConfig = appConfigService.getWalletUIConfig('stellar');\n */\n public getWalletUIConfig<\n T extends object = { kitName: UiKitName; kitConfig?: Record<string, unknown> },\n >(ecosystemId?: string): T | undefined {\n if (!this.isInitialized) {\n logger.warn(LOG_SYSTEM, 'getWalletUIConfig called before initialization.');\n return undefined;\n }\n\n try {\n const walletUIService = this.config.globalServiceConfigs?.walletui;\n\n if (!walletUIService) {\n return undefined;\n }\n\n // Check for new ecosystem-namespaced format\n if (\n ecosystemId &&\n walletUIService[ecosystemId] &&\n typeof walletUIService[ecosystemId] === 'object'\n ) {\n logger.debug(LOG_SYSTEM, `Found ecosystem-specific wallet UI config for ${ecosystemId}`);\n return walletUIService[ecosystemId] as T;\n }\n\n // Check for default config in new format\n if (walletUIService.default && typeof walletUIService.default === 'object') {\n logger.debug(LOG_SYSTEM, `Using default wallet UI config for ecosystem ${ecosystemId}`);\n return walletUIService.default as T;\n }\n\n return undefined;\n } catch (error) {\n logger.warn(\n LOG_SYSTEM,\n `Error accessing wallet UI configuration for ecosystem ${ecosystemId}:`,\n error\n );\n return undefined;\n }\n }\n}\n\n// Create a singleton instance of the AppConfigService\nexport const appConfigService = new AppConfigService();\n","import type { UserRpcProviderConfig } from '@openzeppelin/ui-types';\n\nimport { logger } from './logger';\n\n// Event types\ntype RpcConfigEventType = 'rpc-config-changed' | 'rpc-config-cleared';\n\nexport interface RpcConfigEvent {\n type: RpcConfigEventType;\n networkId: string;\n config?: UserRpcProviderConfig;\n}\n\n/**\n * Service for managing user-configured RPC endpoints.\n * Stores RPC configurations in localStorage for persistence across sessions.\n */\nexport class UserRpcConfigService {\n private static readonly STORAGE_PREFIX = 'tfb_rpc_config_';\n private static readonly eventListeners = new Map<string, Set<(event: RpcConfigEvent) => void>>();\n\n /**\n * Emits an RPC configuration event to all registered listeners\n */\n private static emitEvent(event: RpcConfigEvent): void {\n const listeners = this.eventListeners.get(event.networkId) || new Set();\n const globalListeners = this.eventListeners.get('*') || new Set();\n\n [...listeners, ...globalListeners].forEach((listener) => {\n try {\n listener(event);\n } catch (error) {\n logger.error('UserRpcConfigService', 'Error in event listener:', error);\n }\n });\n }\n\n /**\n * Subscribes to RPC configuration changes for a specific network or all networks\n * @param networkId The network identifier or '*' for all networks\n * @param listener The callback to invoke when RPC config changes\n * @returns Unsubscribe function\n */\n static subscribe(networkId: string, listener: (event: RpcConfigEvent) => void): () => void {\n if (!this.eventListeners.has(networkId)) {\n this.eventListeners.set(networkId, new Set());\n }\n\n this.eventListeners.get(networkId)!.add(listener);\n\n // Return unsubscribe function\n return () => {\n const listeners = this.eventListeners.get(networkId);\n if (listeners) {\n listeners.delete(listener);\n if (listeners.size === 0) {\n this.eventListeners.delete(networkId);\n }\n }\n };\n }\n\n /**\n * Saves a user RPC configuration for a specific network.\n * @param networkId The network identifier\n * @param config The RPC configuration to save\n */\n static saveUserRpcConfig(networkId: string, config: UserRpcProviderConfig): void {\n try {\n const storageKey = `${this.STORAGE_PREFIX}${networkId}`;\n localStorage.setItem(storageKey, JSON.stringify(config));\n logger.info('UserRpcConfigService', `Saved RPC config for network ${networkId}`);\n\n // Emit change event\n this.emitEvent({ type: 'rpc-config-changed', networkId, config });\n } catch (error) {\n logger.error('UserRpcConfigService', 'Failed to save RPC config:', error);\n }\n }\n\n /**\n * Retrieves a user RPC configuration for a specific network.\n * @param networkId The network identifier\n * @returns The stored configuration or null if not found\n */\n static getUserRpcConfig(networkId: string): UserRpcProviderConfig | null {\n try {\n const storageKey = `${this.STORAGE_PREFIX}${networkId}`;\n const stored = localStorage.getItem(storageKey);\n\n if (!stored) {\n return null;\n }\n\n const config = JSON.parse(stored) as UserRpcProviderConfig;\n logger.info('UserRpcConfigService', `Retrieved RPC config for network ${networkId}`);\n return config;\n } catch (error) {\n logger.error('UserRpcConfigService', 'Failed to retrieve RPC config:', error);\n return null;\n }\n }\n\n /**\n * Clears a user RPC configuration for a specific network.\n * @param networkId The network identifier\n */\n static clearUserRpcConfig(networkId: string): void {\n try {\n const storageKey = `${this.STORAGE_PREFIX}${networkId}`;\n localStorage.removeItem(storageKey);\n logger.info('UserRpcConfigService', `Cleared RPC config for network ${networkId}`);\n\n // Emit change event\n this.emitEvent({ type: 'rpc-config-cleared', networkId });\n } catch (error) {\n logger.error('UserRpcConfigService', 'Failed to clear RPC config:', error);\n }\n }\n\n /**\n * Clears all user RPC configurations.\n */\n static clearAllUserRpcConfigs(): void {\n try {\n const keysToRemove: string[] = [];\n\n // Find all RPC config keys\n for (let i = 0; i < localStorage.length; i++) {\n const key = localStorage.key(i);\n if (key?.startsWith(this.STORAGE_PREFIX)) {\n keysToRemove.push(key);\n }\n }\n\n // Remove all found keys\n keysToRemove.forEach((key) => localStorage.removeItem(key));\n\n logger.info('UserRpcConfigService', `Cleared ${keysToRemove.length} RPC configs`);\n } catch (error) {\n logger.error('UserRpcConfigService', 'Failed to clear all RPC configs:', error);\n }\n }\n}\n\n// Export as singleton instance for convenience\nexport const userRpcConfigService = UserRpcConfigService;\n","import type { UserExplorerConfig } from '@openzeppelin/ui-types';\n\nimport { logger } from './logger';\n\n// Event types\ntype ExplorerConfigEventType = 'explorer-config-changed' | 'explorer-config-cleared';\n\nexport interface ExplorerConfigEvent {\n type: ExplorerConfigEventType;\n networkId: string;\n config?: UserExplorerConfig;\n}\n\n/**\n * Service for managing user-configured block explorer endpoints and API keys.\n * Stores explorer configurations in localStorage for persistence across sessions.\n */\nexport class UserExplorerConfigService {\n private static readonly STORAGE_PREFIX = 'tfb_explorer_config_';\n private static readonly eventListeners = new Map<\n string,\n Set<(event: ExplorerConfigEvent) => void>\n >();\n\n /**\n * Emits an explorer configuration event to all registered listeners\n */\n private static emitEvent(event: ExplorerConfigEvent): void {\n const listeners = this.eventListeners.get(event.networkId) || new Set();\n const globalListeners = this.eventListeners.get('*') || new Set();\n\n [...listeners, ...globalListeners].forEach((listener) => {\n try {\n listener(event);\n } catch (error) {\n logger.error('UserExplorerConfigService', 'Error in event listener:', error);\n }\n });\n }\n\n /**\n * Subscribes to explorer configuration changes for a specific network or all networks\n * @param networkId The network identifier or '*' for all networks\n * @param listener The callback to invoke when explorer config changes\n * @returns Unsubscribe function\n */\n static subscribe(networkId: string, listener: (event: ExplorerConfigEvent) => void): () => void {\n if (!this.eventListeners.has(networkId)) {\n this.eventListeners.set(networkId, new Set());\n }\n\n this.eventListeners.get(networkId)!.add(listener);\n\n // Return unsubscribe function\n return () => {\n const listeners = this.eventListeners.get(networkId);\n if (listeners) {\n listeners.delete(listener);\n if (listeners.size === 0) {\n this.eventListeners.delete(networkId);\n }\n }\n };\n }\n\n /**\n * Saves a user explorer configuration for a specific network.\n * @param networkId The network identifier\n * @param config The explorer configuration to save\n */\n static saveUserExplorerConfig(networkId: string, config: UserExplorerConfig): void {\n try {\n const storageKey = `${this.STORAGE_PREFIX}${networkId}`;\n localStorage.setItem(storageKey, JSON.stringify(config));\n logger.info('UserExplorerConfigService', `Saved explorer config for network ${networkId}`);\n\n // Emit change event\n this.emitEvent({ type: 'explorer-config-changed', networkId, config });\n } catch (error) {\n logger.error('UserExplorerConfigService', 'Failed to save explorer config:', error);\n }\n }\n\n /**\n * Retrieves a user explorer configuration for a specific network.\n * First checks for global settings, then falls back to network-specific settings.\n * @param networkId The network identifier\n * @returns The stored configuration or null if not found\n */\n static getUserExplorerConfig(networkId: string): UserExplorerConfig | null {\n try {\n // First check for global settings\n const globalKey = `${this.STORAGE_PREFIX}__global__`;\n const globalStored = localStorage.getItem(globalKey);\n\n if (globalStored) {\n const globalConfig = JSON.parse(globalStored) as UserExplorerConfig;\n if (globalConfig.applyToAllNetworks) {\n logger.info(\n 'UserExplorerConfigService',\n `Using global explorer config for network ${networkId}`\n );\n return globalConfig;\n }\n }\n\n // Then check for network-specific settings\n const storageKey = `${this.STORAGE_PREFIX}${networkId}`;\n const stored = localStorage.getItem(storageKey);\n\n if (!stored) {\n return null;\n }\n\n const config = JSON.parse(stored) as UserExplorerConfig;\n logger.info(\n 'UserExplorerConfigService',\n `Retrieved explorer config for network ${networkId}`\n );\n return config;\n } catch (error) {\n logger.error('UserExplorerConfigService', 'Failed to retrieve explorer config:', error);\n return null;\n }\n }\n\n /**\n * Clears a user explorer configuration for a specific network.\n * @param networkId The network identifier\n */\n static clearUserExplorerConfig(networkId: string): void {\n try {\n const storageKey = `${this.STORAGE_PREFIX}${networkId}`;\n localStorage.removeItem(storageKey);\n logger.info('UserExplorerConfigService', `Cleared explorer config for network ${networkId}`);\n\n // Emit change event\n this.emitEvent({ type: 'explorer-config-cleared', networkId });\n } catch (error) {\n logger.error('UserExplorerConfigService', 'Failed to clear explorer config:', error);\n }\n }\n\n /**\n * Clears all user explorer configurations.\n */\n static clearAllUserExplorerConfigs(): void {\n try {\n const keysToRemove: string[] = [];\n\n // Find all explorer config keys\n for (let i = 0; i < localStorage.length; i++) {\n const key = localStorage.key(i);\n if (key?.startsWith(this.STORAGE_PREFIX)) {\n keysToRemove.push(key);\n }\n }\n\n // Remove all found keys\n keysToRemove.forEach((key) => {\n const networkId = key.substring(this.STORAGE_PREFIX.length);\n localStorage.removeItem(key);\n // Emit clear event for each network\n this.emitEvent({ type: 'explorer-config-cleared', networkId });\n });\n\n logger.info('UserExplorerConfigService', `Cleared ${keysToRemove.length} explorer configs`);\n } catch (error) {\n logger.error('UserExplorerConfigService', 'Failed to clear all explorer configs:', error);\n }\n }\n\n /**\n * Gets all network IDs that have explorer configurations.\n * @returns Array of network IDs\n */\n static getConfiguredNetworkIds(): string[] {\n try {\n const networkIds: string[] = [];\n\n // Check all localStorage keys\n for (let i = 0; i < localStorage.length; i++) {\n const key = localStorage.key(i);\n if (key?.startsWith(this.STORAGE_PREFIX)) {\n const networkId = key.substring(this.STORAGE_PREFIX.length);\n // Skip the global config\n if (networkId !== '__global__') {\n networkIds.push(networkId);\n }\n }\n }\n\n return networkIds;\n } catch (error) {\n logger.error('UserExplorerConfigService', 'Failed to get configured network IDs:', error);\n return [];\n }\n }\n}\n\n// Export as singleton instance for convenience\nexport const userExplorerConfigService = UserExplorerConfigService;\n","import { logger } from './logger';\n\n/**\n * Event types emitted by UserNetworkServiceConfigService\n */\ntype ServiceConfigEventType = 'service-config-changed' | 'service-config-cleared';\n\n/**\n * Event emitted when a service configuration changes or is cleared.\n * Used for subscribing to configuration updates across the application.\n *\n * @interface ServiceConfigEvent\n */\nexport interface ServiceConfigEvent {\n /** Type of event: config changed or cleared */\n type: ServiceConfigEventType;\n /** Network ID for which the configuration changed */\n networkId: string;\n /** Service ID (e.g., 'rpc', 'explorer', 'contract-definitions') */\n serviceId: string;\n /** The new configuration data (only present for 'service-config-changed' events) */\n config?: Record<string, unknown>;\n}\n\n/**\n * Service for managing user-defined network service configurations.\n *\n * This service provides a generic, chain-agnostic way to store and retrieve\n * per-network, per-service user configurations.\n *\n * Configurations are stored in localStorage with the key format:\n * `tfb_service_config_{serviceId}__{networkId}`\n *\n * @example\n * ```typescript\n * // Save RPC configuration for Sepolia\n * userNetworkServiceConfigService.save('ethereum-sepolia', 'rpc', {\n * rpcUrl: 'https://sepolia.infura.io/v3/your-key'\n * });\n *\n * // Retrieve configuration\n * const config = userNetworkServiceConfigService.get('ethereum-sepolia', 'rpc');\n *\n * // Subscribe to changes\n * const unsubscribe = userNetworkServiceConfigService.subscribe(\n * 'ethereum-sepolia',\n * 'rpc',\n * (event) => {\n * console.log('Config changed:', event.config);\n * }\n * );\n * ```\n *\n * @class UserNetworkServiceConfigService\n */\nexport class UserNetworkServiceConfigService {\n private static readonly STORAGE_PREFIX = 'tfb_service_config_';\n private static readonly listeners = new Map<string, Set<(event: ServiceConfigEvent) => void>>();\n\n /**\n * Generates a localStorage key for a network-service combination.\n *\n * @private\n * @param networkId - The network ID\n * @param serviceId - The service ID\n * @returns The storage key string\n */\n private static key(networkId: string, serviceId: string): string {\n return `${this.STORAGE_PREFIX}${serviceId}__${networkId}`;\n }\n\n /**\n * Subscribes to configuration change events for a specific network and/or service.\n *\n * Use '*' as a wildcard to listen to all networks or all services.\n * The listener will be called whenever a matching configuration changes or is cleared.\n *\n * @param networkId - Network ID to listen to, or '*' for all networks\n * @param serviceId - Service ID to listen to, or '*' for all services\n * @param listener - Callback function to invoke when matching events occur\n * @returns Unsubscribe function to remove the listener\n *\n * @example\n * ```typescript\n * // Listen to all RPC config changes across all networks\n * const unsubscribe = userNetworkServiceConfigService.subscribe('*', 'rpc', (event) => {\n * console.log(`${event.networkId} RPC config changed`);\n * });\n *\n * // Later, unsubscribe\n * unsubscribe();\n * ```\n */\n static subscribe(\n networkId: string | '*',\n serviceId: string | '*',\n listener: (event: ServiceConfigEvent) => void\n ): () => void {\n const k = `${networkId}:${serviceId}`;\n if (!this.listeners.has(k)) this.listeners.set(k, new Set());\n this.listeners.get(k)!.add(listener);\n return () => {\n const set = this.listeners.get(k);\n if (set) {\n set.delete(listener);\n if (set.size === 0) this.listeners.delete(k);\n }\n };\n }\n\n /**\n * Emits an event to all matching subscribers.\n * Subscribers are matched based on exact network/service IDs or wildcards.\n *\n * @private\n * @param event - The event to emit\n */\n private static emit(event: ServiceConfigEvent): void {\n const targets = [\n `${event.networkId}:${event.serviceId}`,\n `${event.networkId}:*`,\n `*:${event.serviceId}`,\n `*:*`,\n ];\n for (const k of targets) {\n const set = this.listeners.get(k);\n if (!set) continue;\n for (const fn of set) {\n try {\n fn(event);\n } catch (e) {\n logger.error('UserNetworkServiceConfigService', 'Error in event listener:', e);\n }\n }\n }\n }\n\n /**\n * Saves a service configuration for a specific network.\n *\n * The configuration is stored in localStorage and all matching subscribers\n * are notified via a 'service-config-changed' event.\n *\n * @param networkId - The network ID (e.g., 'ethereum-sepolia')\n * @param serviceId - The service ID (e.g., 'rpc', 'explorer', 'contract-definitions')\n * @param config - The configuration object to save\n *\n * @example\n * ```typescript\n * userNetworkServiceConfigService.save('ethereum-sepolia', 'rpc', {\n * rpcUrl: 'https://sepolia.infura.io/v3/your-key'\n * });\n * ```\n */\n static save(networkId: string, serviceId: string, config: Record<string, unknown>): void {\n try {\n localStorage.setItem(this.key(networkId, serviceId), JSON.stringify(config));\n logger.info(\n 'UserNetworkServiceConfigService',\n `Saved config for ${serviceId} on network ${networkId}`\n );\n this.emit({ type: 'service-config-changed', networkId, serviceId, config });\n } catch (e) {\n logger.error('UserNetworkServiceConfigService', 'Failed to save config:', e);\n }\n }\n\n /**\n * Retrieves a saved service configuration for a specific network.\n *\n * @param networkId - The network ID (e.g., 'ethereum-sepolia')\n * @param serviceId - The service ID (e.g., 'rpc', 'explorer', 'contract-definitions')\n * @returns The configuration object, or null if not found or if retrieval fails\n *\n * @example\n * ```typescript\n * const config = userNetworkServiceConfigService.get('ethereum-sepolia', 'rpc');\n * if (config) {\n * console.log('RPC URL:', config.rpcUrl);\n * }\n * ```\n */\n static get(networkId: string, serviceId: string): Record<string, unknown> | null {\n try {\n const raw = localStorage.getItem(this.key(networkId, serviceId));\n return raw ? (JSON.parse(raw) as Record<string, unknown>) : null;\n } catch (e) {\n logger.error('UserNetworkServiceConfigService', 'Failed to retrieve config:', e);\n return null;\n }\n }\n\n /**\n * Clears a saved service configuration for a specific network.\n *\n * Removes the configuration from localStorage and notifies all matching subscribers\n * via a 'service-config-cleared' event.\n *\n * @param networkId - The network ID (e.g., 'ethereum-sepolia')\n * @param serviceId - The service ID (e.g., 'rpc', 'explorer', 'contract-definitions')\n *\n * @example\n * ```typescript\n * userNetworkServiceConfigService.clear('ethereum-sepolia', 'rpc');\n * ```\n */\n static clear(networkId: string, serviceId: string): void {\n try {\n localStorage.removeItem(this.key(networkId, serviceId));\n logger.info(\n 'UserNetworkServiceConfigService',\n `Cleared config for ${serviceId} on network ${networkId}`\n );\n this.emit({ type: 'service-config-cleared', networkId, serviceId });\n } catch (e) {\n logger.error('UserNetworkServiceConfigService', 'Failed to clear config:', e);\n }\n }\n}\n\n/**\n * Singleton instance of UserNetworkServiceConfigService.\n * This is the preferred way to access the service.\n *\n * @example\n * ```typescript\n * import { userNetworkServiceConfigService } from '@openzeppelin/ui-utils';\n *\n * userNetworkServiceConfigService.save('ethereum-sepolia', 'rpc', { rpcUrl: '...' });\n * ```\n */\nexport const userNetworkServiceConfigService = UserNetworkServiceConfigService;\n","import type { FieldType, FieldValue } from '@openzeppelin/ui-types';\n\n/**\n * Get a default value for a field type.\n * This is a chain-agnostic utility that provides appropriate default values\n * based on the UI field type.\n *\n * @param fieldType - The UI field type\n * @returns The appropriate default value for that field type\n */\nexport function getDefaultValueForType<T extends FieldType>(fieldType: T): FieldValue<T> {\n switch (fieldType) {\n case 'checkbox':\n return false as FieldValue<T>;\n case 'number':\n case 'amount':\n return 0 as FieldValue<T>;\n case 'array':\n return [] as FieldValue<T>;\n case 'object':\n return {} as FieldValue<T>;\n case 'array-object':\n return [] as FieldValue<T>;\n case 'map':\n return [] as FieldValue<T>; // Empty array of key-value pairs\n case 'blockchain-address':\n case 'bigint':\n case 'text':\n case 'textarea':\n case 'bytes':\n case 'email':\n case 'password':\n case 'select':\n case 'radio':\n case 'date':\n case 'hidden':\n default:\n return '' as FieldValue<T>;\n }\n}\n","import type { FieldValidation } from '@openzeppelin/ui-types';\n\n/**\n * Numeric type bounds configuration.\n * Each adapter provides its own bounds map based on chain-specific type names.\n */\nexport type NumericBoundsMap = Record<string, { min?: number; max?: number }>;\n\n/**\n * Enhances field validation with numeric bounds based on parameter type.\n * Only applies bounds if they are not already set in the validation object.\n *\n * @param validation - Existing validation rules (may be undefined)\n * @param parameterType - The blockchain parameter type (e.g., 'uint32', 'U32', 'Uint<0..255>')\n * @param boundsMap - Chain-specific map of type names to min/max bounds\n * @returns Enhanced validation object with numeric bounds applied\n *\n * @example\n * ```typescript\n * const stellarBounds = { U32: { min: 0, max: 4_294_967_295 } };\n * const validation = enhanceNumericValidation(undefined, 'U32', stellarBounds);\n * // Returns: { min: 0, max: 4_294_967_295 }\n * ```\n */\nexport function enhanceNumericValidation(\n validation: FieldValidation | undefined,\n parameterType: string,\n boundsMap: NumericBoundsMap\n): FieldValidation {\n const result: FieldValidation = { ...(validation ?? {}) };\n const bounds = boundsMap[parameterType];\n\n if (!bounds) {\n return result;\n }\n\n // Only apply bounds if they're not already set\n if (bounds.min !== undefined && result.min === undefined) {\n result.min = bounds.min;\n }\n\n if (bounds.max !== undefined && result.max === undefined) {\n result.max = bounds.max;\n }\n\n return result;\n}\n","/**\n * Type guard to check if a value is a non-null object (Record<string, unknown>).\n * Useful for safely accessing properties on an 'unknown' type after this check.\n * @param value - The value to check.\n * @returns True if the value is a non-null object, false otherwise.\n */\nexport function isRecordWithProperties(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\n/**\n * Type guard to check if a value is a plain object (not an array, not null).\n * This is useful for distinguishing between objects and arrays, since arrays are technically objects in JavaScript.\n * @param value - The value to check.\n * @returns True if the value is a plain object (not array, not null), false otherwise.\n */\nexport function isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n// Add other type guards here in the future if needed.\n","import { clsx, type ClassValue } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\n/**\n * Combines class names using clsx and tailwind-merge.\n * @param inputs - Class values to combine\n * @returns Merged class name string\n */\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n","/**\n * String and date formatting utility functions\n * These utilities help with common formatting operations\n */\n\n/**\n * Truncates a string (like an Ethereum address) in the middle\n * @param str The string to truncate\n * @param startChars Number of characters to show at the beginning\n * @param endChars Number of characters to show at the end\n * @returns The truncated string with ellipsis in the middle\n */\nexport function truncateMiddle(str: string, startChars = 6, endChars = 4): string {\n if (!str) return '';\n if (str.length <= startChars + endChars) return str;\n\n return `${str.substring(0, startChars)}...${str.substring(str.length - endChars)}`;\n}\n\n/**\n * Formats a timestamp as a relative time string (e.g., \"2h ago\", \"just now\")\n * @param date The date to format\n * @returns A human-readable relative time string\n */\nexport function formatTimestamp(date: Date): string {\n const now = new Date();\n const diffMs = now.getTime() - date.getTime();\n const diffMinutes = Math.floor(diffMs / (1000 * 60));\n const diffHours = Math.floor(diffMs / (1000 * 60 * 60));\n const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));\n\n if (diffMinutes < 1) return 'just now';\n if (diffMinutes < 60) return `${diffMinutes}m ago`;\n if (diffHours < 24) return `${diffHours}h ago`;\n if (diffDays < 7) return `${diffDays}d ago`;\n\n return date.toLocaleDateString();\n}\n\n/**\n * Detects whether a string contains hex-encoded or base64-encoded binary data.\n * Useful for auto-detecting the encoding format of user inputs across blockchain adapters.\n *\n * @param value - The string to analyze\n * @returns 'hex' if the string appears to be hexadecimal, 'base64' if it appears to be base64\n *\n * @example\n * ```typescript\n * detectBytesEncoding(\"48656c6c6f\") // → 'hex'\n * detectBytesEncoding(\"SGVsbG8=\") // → 'base64'\n * detectBytesEncoding(\"0x48656c6c6f\") // → 'hex' (after stripping 0x prefix)\n * ```\n */\nexport function detectBytesEncoding(value: string): 'base64' | 'hex' {\n // Normalize input and handle common prefixes\n const trimmed = value?.trim() ?? '';\n const without0x =\n trimmed.startsWith('0x') || trimmed.startsWith('0X') ? trimmed.slice(2) : trimmed;\n\n const hexRegex = /^[0-9a-fA-F]+$/;\n const base64Regex = /^[A-Za-z0-9+/]*={0,2}$/;\n\n // Prefer hex when clearly valid (even length and hex charset)\n if (hexRegex.test(without0x) && without0x.length > 0 && without0x.length % 2 === 0) {\n return 'hex';\n }\n\n // Validate base64 by pattern + round-trip check\n if (base64Regex.test(trimmed) && trimmed.length % 4 === 0) {\n try {\n const decoded = atob(trimmed);\n const reencoded = btoa(decoded);\n if (reencoded.replace(/=+$/, '') === trimmed.replace(/=+$/, '')) {\n return 'base64';\n }\n } catch {\n // fall through to safer hex default\n }\n }\n\n // Safer default: return 'hex' when ambiguous or invalid\n return 'hex';\n}\n","import { v4 as uuidv4 } from 'uuid';\n\n/**\n * General utility functions, which are not specific to any blockchain\n * It's important to keep these functions as simple as possible and avoid any\n * dependencies from other packages.\n */\n\n/**\n * Generates a unique ID for form fields, components, etc.\n * Uses crypto.getRandomValues() for browser-compatible random ID generation.\n *\n * @param prefix Optional prefix to add before the UUID\n * @returns A string ID that is guaranteed to be unique\n */\nexport function generateId(prefix?: string): string {\n // Generate a browser-compatible UUID v4\n const uuid = uuidv4();\n\n return prefix ? `${prefix}_${uuid}` : uuid;\n}\n","/**\n * URL validation utilities\n */\n\n/**\n * Validates if a string is a valid URL (supports http, https, and ftp protocols).\n * Relies solely on the URL constructor for validation.\n *\n * @param urlString - The string to validate\n * @returns True if the URL is valid, false otherwise\n */\nexport function isValidUrl(urlString: string): boolean {\n if (!urlString || typeof urlString !== 'string') {\n return false;\n }\n\n try {\n // First check with URL constructor for basic validity\n new URL(urlString);\n\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Gets a user-friendly error message for invalid URLs.\n *\n * @returns Standard error message for invalid URLs\n */\nexport function getInvalidUrlMessage(): string {\n return 'Please enter a valid URL (e.g., https://example.com)';\n}\n","/**\n * Utility to add delay between operations\n * @param ms - Milliseconds to delay\n * @returns Promise that resolves after the specified delay\n */\nexport const delay = (ms: number): Promise<void> =>\n new Promise((resolve) => setTimeout(resolve, ms));\n\n/**\n * Executes operations in batches with rate limiting to prevent API overload\n * @param operations - Array of functions that return promises\n * @param batchSize - Number of operations to execute in parallel per batch (default: 2)\n * @param delayMs - Delay in milliseconds between batches (default: 100)\n * @returns Promise that resolves to an array of results from all operations\n */\nexport async function rateLimitedBatch<T>(\n operations: (() => Promise<T>)[],\n batchSize: number = 2,\n delayMs: number = 100\n): Promise<T[]> {\n const results: T[] = [];\n\n for (let i = 0; i < operations.length; i += batchSize) {\n const batch = operations.slice(i, i + batchSize);\n const batchResults = await Promise.all(batch.map((operation) => operation()));\n results.push(...batchResults);\n\n // Add delay between batches (except for the last batch)\n if (i + batchSize < operations.length) {\n await delay(delayMs);\n }\n }\n\n return results;\n}\n\n/**\n * Wraps a promise with a timeout. Rejects with a descriptive Error after timeoutMs.\n *\n * @param promise The promise to wrap\n * @param timeoutMs Timeout in milliseconds\n * @param label Optional label to include in the timeout error message\n */\nexport function withTimeout<T>(promise: Promise<T>, timeoutMs: number, label?: string): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n const timer = setTimeout(() => {\n reject(new Error(`${label ?? 'operation'} timed out after ${timeoutMs}ms`));\n }, timeoutMs);\n\n promise\n .then((value) => {\n clearTimeout(timer);\n resolve(value);\n })\n .catch((err) => {\n clearTimeout(timer);\n reject(err);\n });\n });\n}\n\n// ============================================================================\n// Concurrency-Limited Execution\n// ============================================================================\n//\n// These utilities provide controlled parallel execution with a concurrency limit.\n//\n// WHY USE THESE INSTEAD OF `rateLimitedBatch`?\n//\n// `rateLimitedBatch` processes operations in fixed-size batches with delays between\n// batches. This is good for strict rate limiting but can leave capacity unused:\n// - Batch 1: [op1, op2] → wait for both → delay → Batch 2: [op3, op4] → ...\n// - If op1 finishes quickly, its slot sits idle until the entire batch completes.\n//\n// `promiseAllWithLimit` uses a worker pool approach with a sliding window:\n// - Maintains N concurrent operations at all times (where N = limit)\n// - As soon as one operation completes, the next one starts immediately\n// - No artificial delays between operations\n// - Maximizes throughput while respecting the concurrency limit\n//\n// USE CASES:\n// - `rateLimitedBatch`: When you need enforced pauses (e.g., API rate limits with\n// explicit wait requirements, or when you want predictable batch timing)\n// - `promiseAllWithLimit`: When you want maximum throughput without overwhelming\n// the target service (e.g., RPC endpoints, database connections)\n//\n// ============================================================================\n\n/**\n * Default concurrency limit for parallel operations.\n * Set to a reasonable value that balances performance and service limits.\n */\nexport const DEFAULT_CONCURRENCY_LIMIT = 10;\n\n/**\n * Execute an array of promise-returning functions with a concurrency limit.\n *\n * Uses a worker pool approach that maintains up to `limit` concurrent operations.\n * As soon as one operation completes, the next one starts immediately, maximizing\n * throughput while respecting the concurrency limit.\n *\n * Results are returned in the same order as the input tasks, regardless of\n * completion order.\n *\n * @param tasks Array of functions that return promises\n * @param limit Maximum number of concurrent executions (default: 10)\n * @returns Promise resolving to array of results in same order as input tasks\n *\n * @example\n * ```typescript\n * // Fetch 100 role members with max 10 concurrent RPC requests\n * const tasks = memberIndices.map((index) => () => getRoleMember(contract, role, index));\n * const members = await promiseAllWithLimit(tasks, 10);\n * ```\n */\nexport async function promiseAllWithLimit<T>(\n tasks: (() => Promise<T>)[],\n limit: number = DEFAULT_CONCURRENCY_LIMIT\n): Promise<T[]> {\n if (tasks.length === 0) {\n return [];\n }\n\n // If limit is greater than or equal to task count, just use Promise.all\n if (limit >= tasks.length) {\n return Promise.all(tasks.map((task) => task()));\n }\n\n const results: T[] = new Array(tasks.length);\n let currentIndex = 0;\n\n // Worker function that processes tasks from the queue\n async function worker(): Promise<void> {\n while (currentIndex < tasks.length) {\n const index = currentIndex++;\n const task = tasks[index];\n results[index] = await task();\n }\n }\n\n // Create worker promises up to the concurrency limit\n const workers = Array.from({ length: Math.min(limit, tasks.length) }, () => worker());\n\n await Promise.all(workers);\n\n return results;\n}\n\n/**\n * Execute an array of promise-returning functions with a concurrency limit,\n * settling all promises (similar to Promise.allSettled but with concurrency control).\n *\n * Unlike promiseAllWithLimit, this function does not fail fast on errors.\n * All tasks will be executed regardless of individual failures.\n *\n * @param tasks Array of functions that return promises\n * @param limit Maximum number of concurrent executions (default: 10)\n * @returns Promise resolving to array of settled results in same order as input tasks\n *\n * @example\n * ```typescript\n * const tasks = items.map((item) => () => fetchItem(item.id));\n * const results = await promiseAllSettledWithLimit(tasks, 10);\n *\n * for (const result of results) {\n * if (result.status === 'fulfilled') {\n * console.log('Success:', result.value);\n * } else {\n * console.log('Failed:', result.reason);\n * }\n * }\n * ```\n */\nexport async function promiseAllSettledWithLimit<T>(\n tasks: (() => Promise<T>)[],\n limit: number = DEFAULT_CONCURRENCY_LIMIT\n): Promise<PromiseSettledResult<T>[]> {\n if (tasks.length === 0) {\n return [];\n }\n\n // If limit is greater than or equal to task count, just use Promise.allSettled\n if (limit >= tasks.length) {\n return Promise.allSettled(tasks.map((task) => task()));\n }\n\n const results: PromiseSettledResult<T>[] = new Array(tasks.length);\n let currentIndex = 0;\n\n // Worker function that processes tasks from the queue\n async function worker(): Promise<void> {\n while (currentIndex < tasks.length) {\n const index = currentIndex++;\n const task = tasks[index];\n try {\n const value = await task();\n results[index] = { status: 'fulfilled', value };\n } catch (reason) {\n results[index] = { status: 'rejected', reason };\n }\n }\n }\n\n // Create worker promises up to the concurrency limit\n const workers = Array.from({ length: Math.min(limit, tasks.length) }, () => worker());\n\n await Promise.all(workers);\n\n return results;\n}\n","/**\n * Simple browser-compatible hash utilities\n * These functions provide deterministic hashing for content comparison\n * and are not intended for cryptographic purposes.\n */\n\n/**\n * Creates a simple hash from a string using a non-cryptographic algorithm\n * Suitable for content comparison, caching keys, and quick fingerprinting\n *\n * @param str - The string to hash\n * @returns A hexadecimal hash string (always positive)\n *\n * @example\n * ```typescript\n * const hash1 = simpleHash('{\"name\": \"test\"}');\n * const hash2 = simpleHash('{\"name\": \"test\"}');\n * console.log(hash1 === hash2); // true - deterministic\n * ```\n */\nexport function simpleHash(str: string): string {\n let hash = 0;\n for (let i = 0; i < str.length; i++) {\n const char = str.charCodeAt(i);\n hash = (hash << 5) - hash + char;\n hash = hash & hash; // Convert to 32-bit integer\n }\n return Math.abs(hash).toString(16);\n}\n","import validator from 'validator';\n\n/**\n * Options for bytes validation\n */\nexport interface BytesValidationOptions {\n /**\n * Whether to accept hex, base64, or both formats\n */\n acceptedFormats?: 'hex' | 'base64' | 'both';\n\n /**\n * Maximum length in bytes (not characters)\n */\n maxBytes?: number;\n\n /**\n * Whether to allow 0x prefix for hex values\n */\n allowHexPrefix?: boolean;\n}\n\n/**\n * Result of bytes validation\n */\nexport interface BytesValidationResult {\n /**\n * Whether the input is valid\n */\n isValid: boolean;\n\n /**\n * Error message if validation failed\n */\n error?: string;\n\n /**\n * Detected format of the input\n */\n detectedFormat?: 'hex' | 'base64';\n\n /**\n * Cleaned value (whitespace removed, prefix handled)\n */\n cleanedValue?: string;\n\n /**\n * Size in bytes\n */\n byteSize?: number;\n}\n\n/**\n * Validates bytes input using the established validator.js library.\n *\n * This function provides comprehensive validation for blockchain bytes data including:\n * - Hex encoding validation (with optional 0x prefix)\n * - Base64 encoding validation\n * - Byte length validation\n * - Format detection\n *\n * @param value - The input string to validate\n * @param options - Validation options\n * @returns Validation result with details\n *\n * @example\n * ```typescript\n * validateBytes('48656c6c6f') // → { isValid: true, detectedFormat: 'hex', byteSize: 5 }\n * validateBytes('SGVsbG8=') // → { isValid: true, detectedFormat: 'base64', byteSize: 5 }\n * validateBytes('invalid') // → { isValid: false, error: '...' }\n * ```\n */\nexport function validateBytes(\n value: string,\n options: BytesValidationOptions = {}\n): BytesValidationResult {\n const { acceptedFormats = 'both', maxBytes, allowHexPrefix = true } = options;\n\n // Handle empty values\n if (!value || value.trim() === '') {\n return {\n isValid: true, // Let required validation handle empty values\n cleanedValue: '',\n byteSize: 0,\n };\n }\n\n // Clean the value (remove whitespace)\n const cleanValue = value.trim().replace(/\\s+/g, '');\n\n // Handle hex prefix\n const hasHexPrefix = cleanValue.startsWith('0x');\n const withoutPrefix = hasHexPrefix ? cleanValue.slice(2) : cleanValue;\n\n if (withoutPrefix === '') {\n return {\n isValid: false,\n error: 'Bytes value cannot be empty',\n cleanedValue: cleanValue,\n };\n }\n\n // Detect format using validator.js\n let detectedFormat: 'hex' | 'base64' | null = null;\n let byteSize = 0;\n\n // Try hex validation first (more specific)\n if (validator.isHexadecimal(withoutPrefix)) {\n detectedFormat = 'hex';\n\n // Ensure even length for hex\n if (withoutPrefix.length % 2 !== 0) {\n return {\n isValid: false,\n error: 'Hex string must have even number of characters',\n cleanedValue: cleanValue,\n detectedFormat,\n };\n }\n\n // Calculate byte size after validation\n byteSize = withoutPrefix.length / 2;\n }\n // Try base64 validation\n else if (validator.isBase64(withoutPrefix)) {\n detectedFormat = 'base64';\n\n try {\n // Decode base64 using Web API to calculate byte size\n const decoded = atob(withoutPrefix);\n byteSize = decoded.length;\n } catch {\n return {\n isValid: false,\n error: 'Invalid base64 encoding',\n cleanedValue: cleanValue,\n };\n }\n }\n\n // No format detected\n if (!detectedFormat) {\n return {\n isValid: false,\n error: 'Invalid format. Expected hex or base64 encoding',\n cleanedValue: cleanValue,\n };\n }\n\n // Check if detected format is accepted\n if (acceptedFormats !== 'both') {\n if (acceptedFormats === 'hex' && detectedFormat !== 'hex') {\n return {\n isValid: false,\n error: 'Only hex format is accepted for this field',\n cleanedValue: cleanValue,\n detectedFormat,\n };\n }\n if (acceptedFormats === 'base64' && detectedFormat !== 'base64') {\n return {\n isValid: false,\n error: 'Only base64 format is accepted for this field',\n cleanedValue: cleanValue,\n detectedFormat,\n };\n }\n }\n\n // Check hex prefix policy\n if (detectedFormat === 'hex' && hasHexPrefix && !allowHexPrefix) {\n return {\n isValid: false,\n error: '0x prefix not allowed for this field',\n cleanedValue: cleanValue,\n detectedFormat,\n };\n }\n\n // Check byte length limits\n if (maxBytes && byteSize > maxBytes) {\n const formatInfo =\n detectedFormat === 'hex' ? `${maxBytes * 2} hex characters` : `${maxBytes} bytes`;\n\n return {\n isValid: false,\n error: `Maximum ${maxBytes} bytes allowed (${formatInfo})`,\n cleanedValue: cleanValue,\n detectedFormat,\n byteSize,\n };\n }\n\n return {\n isValid: true,\n cleanedValue: cleanValue,\n detectedFormat,\n byteSize,\n };\n}\n\n/**\n * Simple validation function that returns boolean or error string\n * (for compatibility with existing React Hook Form validation)\n *\n * @param value - The input string to validate\n * @param options - Validation options\n * @returns true if valid, error string if invalid\n */\nexport function validateBytesSimple(\n value: string,\n options: BytesValidationOptions = {}\n): boolean | string {\n const result = validateBytes(value, options);\n return result.isValid ? true : result.error || 'Invalid bytes format';\n}\n\n/**\n * Extracts the size from a Bytes<N> type string, or returns undefined for dynamic Uint8Array\n *\n * @param type - Type string (e.g., \"Bytes<32>\", \"Uint8Array\", \"bytes\")\n * @returns Size in bytes if fixed-size, undefined if dynamic\n *\n * @example\n * ```typescript\n * getBytesSize('Bytes<32>') // → 32\n * getBytesSize('Bytes<64>') // → 64\n * getBytesSize('Uint8Array') // → undefined\n * getBytesSize('bytes') // → undefined\n * ```\n */\nexport function getBytesSize(type: string): number | undefined {\n const match = type.match(/^Bytes<(\\d+)>$/i);\n if (match) {\n return Number.parseInt(match[1], 10);\n }\n return undefined; // Dynamic size (Uint8Array)\n}\n","/**\n * Cross-platform bytes conversion utilities that work in both browser and Node.js\n * without requiring Buffer polyfills.\n */\n\n/**\n * Convert a hex string to Uint8Array using native browser APIs\n * @param hex - Hex string (with or without 0x prefix)\n * @returns Uint8Array representation\n */\nexport function hexToBytes(hex: string): Uint8Array {\n const cleanHex = hex.startsWith('0x') ? hex.slice(2) : hex;\n\n if (cleanHex.length % 2 !== 0) {\n throw new Error('Hex string must have even length');\n }\n\n // Validate hex characters\n if (!/^[0-9a-fA-F]*$/.test(cleanHex)) {\n throw new Error('Invalid hex characters in string');\n }\n\n const bytes = new Uint8Array(cleanHex.length / 2);\n for (let i = 0; i < cleanHex.length; i += 2) {\n bytes[i / 2] = parseInt(cleanHex.substring(i, i + 2), 16);\n }\n\n return bytes;\n}\n\n/**\n * Convert a base64 string to Uint8Array using native browser APIs\n * Handles data URLs by stripping the prefix\n * @param base64 - Base64 encoded string (with optional data URL prefix)\n * @returns Uint8Array representation\n */\nexport function base64ToBytes(base64: string): Uint8Array {\n // Handle data URLs by stripping the prefix\n const cleaned = base64.includes(',') ? base64.split(',')[1] : base64;\n const binaryString = atob(cleaned);\n const len = binaryString.length;\n const bytes = new Uint8Array(len);\n for (let i = 0; i < len; i++) {\n bytes[i] = binaryString.charCodeAt(i);\n }\n return bytes;\n}\n\n/**\n * Convert Uint8Array to hex string\n * @param bytes - Uint8Array to convert\n * @param withPrefix - Whether to include '0x' prefix\n * @returns Hex string representation\n */\nexport function bytesToHex(bytes: Uint8Array, withPrefix = false): string {\n const hex = Array.from(bytes)\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n\n return withPrefix ? `0x${hex}` : hex;\n}\n\n/**\n * Convert string to bytes based on detected encoding (hex or base64)\n * @param value - The string value to convert\n * @param encoding - The detected encoding type\n * @returns Uint8Array representation\n */\nexport function stringToBytes(value: string, encoding: 'hex' | 'base64'): Uint8Array {\n switch (encoding) {\n case 'hex':\n return hexToBytes(value);\n case 'base64':\n return base64ToBytes(value);\n default:\n throw new Error(`Unsupported encoding: ${encoding}. Supported encodings: hex, base64`);\n }\n}\n","/**\n * Utility functions for environment detection\n */\n\n/**\n * Check if the application is running in development or test environment\n * @returns True if NODE_ENV is 'development' or 'test'\n */\nexport function isDevelopmentOrTestEnvironment(): boolean {\n return process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test';\n}\n\n/**\n * Check if the application is running in production environment\n * @returns True if NODE_ENV is 'production'\n */\nexport function isProductionEnvironment(): boolean {\n return process.env.NODE_ENV === 'production';\n}\n","/**\n * RouterService\n *\n * Minimal wrapper to abstract routing. This avoids coupling the app to a\n * specific router implementation and allows future swaps.\n */\nexport interface RouterService {\n /** Returns the current location href (string form). */\n currentLocation(): string;\n /** Returns the value of a query parameter or null if not present. */\n getParam(name: string): string | null;\n /** Navigates to a path (client-side when router is present; falls back to location). */\n navigate(path: string): void;\n}\n\n/**\n * Default implementation that relies on the browser Location API.\n * The builder app can replace this with a router-bound implementation if needed.\n */\nclass BrowserRouterService implements RouterService {\n public currentLocation(): string {\n if (typeof window === 'undefined') return '';\n return window.location.href;\n }\n\n public getParam(name: string): string | null {\n if (typeof window === 'undefined') return null;\n const params = new URLSearchParams(window.location.search);\n return params.get(name);\n }\n\n public navigate(path: string): void {\n if (typeof window === 'undefined') return;\n // Basic implementation: update location. A router-aware version can override this.\n if (path.startsWith('http://') || path.startsWith('https://')) {\n window.location.assign(path);\n } else {\n // Preserve origin and replace pathname/query as provided\n const url = new URL(path, window.location.origin);\n window.history.pushState({}, '', url.toString());\n // Fire a popstate event so listeners can react (if any)\n window.dispatchEvent(new PopStateEvent('popstate'));\n }\n }\n}\n\n/**\n * Singleton instance for global consumption.\n */\nexport const routerService: RouterService = new BrowserRouterService();\n","import { appConfigService } from './AppConfigService';\nimport { logger } from './logger';\n\n/**\n * Google Analytics service for tracking user interactions.\n * Manages Google Analytics initialization and event tracking.\n * Only active when the analytics_enabled feature flag is true.\n *\n * This is a generic service that provides core analytics functionality.\n * App-specific tracking methods should be implemented in app-level hooks\n * that use the generic `trackEvent` method.\n *\n * @example\n * ```typescript\n * // Initialize analytics (typically done once at app startup)\n * AnalyticsService.initialize('G-XXXXXXXXXX');\n *\n * // Track a custom event\n * AnalyticsService.trackEvent('button_clicked', { button_name: 'submit' });\n *\n * // Track page view\n * AnalyticsService.trackPageView('Dashboard', '/dashboard');\n * ```\n */\nexport class AnalyticsService {\n private static initialized = false;\n\n /**\n * Initialize Google Analytics\n * @param tagId - Google Analytics tag ID (e.g., G-N3DZK5FCT1)\n */\n static initialize(tagId: string): void {\n if (!tagId) {\n logger.warn('AnalyticsService', 'No tag ID provided');\n return;\n }\n\n if (!this.isEnabled()) {\n logger.info('AnalyticsService', 'Analytics is disabled via feature flag');\n return;\n }\n\n if (this.initialized) {\n logger.info('AnalyticsService', 'Already initialized');\n return;\n }\n\n try {\n this.loadGtagScript(tagId);\n this.initializeGtag(tagId);\n this.initialized = true;\n logger.info('AnalyticsService', 'Initialized successfully');\n } catch (error) {\n logger.error('AnalyticsService', 'Failed to initialize:', error);\n }\n }\n\n /**\n * Check if analytics is enabled via feature flag\n */\n static isEnabled(): boolean {\n return appConfigService.isFeatureEnabled('analytics_enabled');\n }\n\n /**\n * Reset the analytics service state (primarily for testing)\n */\n static reset(): void {\n this.initialized = false;\n }\n\n /**\n * Generic event tracking method.\n * Use this to track any custom event with arbitrary parameters.\n *\n * @param eventName - Name of the event (e.g., 'button_clicked', 'form_submitted')\n * @param parameters - Key-value pairs of event parameters\n *\n * @example\n * ```typescript\n * AnalyticsService.trackEvent('ecosystem_selected', { ecosystem: 'evm' });\n * AnalyticsService.trackEvent('wizard_step', { step_number: 2, step_name: 'configure' });\n * ```\n */\n static trackEvent(eventName: string, parameters: Record<string, string | number>): void {\n if (!this.isEnabled()) {\n return;\n }\n\n try {\n if (typeof window.gtag === 'function') {\n window.gtag('event', eventName, parameters);\n } else {\n logger.warn('AnalyticsService', 'gtag is not available');\n }\n } catch (error) {\n logger.error('AnalyticsService', `Failed to track event '${eventName}':`, error);\n }\n }\n\n /**\n * Track page view event.\n * Common event shared across all apps.\n *\n * @param pageName - Human-readable name of the page\n * @param pagePath - URL path of the page\n *\n * @example\n * ```typescript\n * AnalyticsService.trackPageView('Dashboard', '/dashboard');\n * ```\n */\n static trackPageView(pageName: string, pagePath: string): void {\n this.trackEvent('page_view', {\n page_title: pageName,\n page_path: pagePath,\n });\n }\n\n /**\n * Track network selection event.\n * Common event shared across all apps that involve network selection.\n *\n * @param networkId - Selected network ID\n * @param ecosystem - Ecosystem the network belongs to (e.g., 'evm', 'stellar')\n *\n * @example\n * ```typescript\n * AnalyticsService.trackNetworkSelection('ethereum-mainnet', 'evm');\n * ```\n */\n static trackNetworkSelection(networkId: string, ecosystem: string): void {\n this.trackEvent('network_selected', {\n network_id: networkId,\n ecosystem,\n });\n }\n\n /**\n * Load the Google Analytics gtag script\n * @private\n */\n private static loadGtagScript(tagId: string): void {\n // Check if script is already loaded\n if (document.querySelector(`script[src*=\"gtag/js?id=${tagId}\"]`)) {\n return;\n }\n\n // Initialize dataLayer\n if (!window.dataLayer) {\n window.dataLayer = [];\n }\n\n // Define gtag function\n window.gtag =\n window.gtag ||\n function gtag() {\n window.dataLayer.push(arguments);\n };\n\n // Create and inject the script\n const script = document.createElement('script');\n script.async = true;\n script.src = `https://www.googletagmanager.com/gtag/js?id=${tagId}`;\n document.head.appendChild(script);\n }\n\n /**\n * Initialize gtag with configuration\n * @private\n */\n private static initializeGtag(tagId: string): void {\n if (typeof window.gtag === 'function') {\n window.gtag('js', new Date());\n window.gtag('config', tagId);\n }\n }\n}\n\n/**\n * Type declarations for Google Analytics gtag\n */\ndeclare global {\n interface Window {\n dataLayer: unknown[];\n gtag: (...args: unknown[]) => void;\n }\n}\n","/**\n * Deep-link utilities (chain-agnostic)\n *\n * NOTE: Stub implementation for TDD. Tests will drive full behavior in Phase 3.3.\n */\nexport type DeepLinkParams = Record<string, string>;\n\n/**\n * Parses URL query parameters into a key-value object.\n * @returns Object containing all URL query parameters\n */\nexport function parseDeepLink(): DeepLinkParams {\n const params = new URLSearchParams(window.location.search);\n const result: DeepLinkParams = {};\n params.forEach((value, key) => {\n result[key] = value;\n });\n return result;\n}\n\n/**\n * Gets the forced service from deep link parameters.\n * @param params - Deep link parameters object\n * @returns Service name if specified, null otherwise\n */\nexport function getForcedService(params: DeepLinkParams): string | null {\n return params.service ?? null;\n}\n\n/**\n * Computes the effective provider preference based on priority order.\n * @param input - Configuration object with provider options\n * @returns The effective provider and its source\n */\nexport function computeEffectiveProviderPreference(input: {\n forcedService?: string | null;\n uiSelection?: string | null;\n appDefault?: string | null;\n adapterDefaultOrder: readonly string[];\n}): { effectiveProvider: string; source: 'urlForced' | 'ui' | 'appConfig' | 'adapterDefault' } {\n if (input.forcedService && input.forcedService.length > 0) {\n return { effectiveProvider: input.forcedService, source: 'urlForced' };\n }\n\n if (input.uiSelection && input.uiSelection.length > 0) {\n return { effectiveProvider: input.uiSelection, source: 'ui' };\n }\n\n if (input.appDefault && input.appDefault.length > 0) {\n return { effectiveProvider: input.appDefault, source: 'appConfig' };\n }\n\n const fallback = input.adapterDefaultOrder[0];\n return { effectiveProvider: fallback, source: 'adapterDefault' };\n}\n","/**\n * Minimal HTML sanitizer for client-side rendering of adapter-provided notes.\n *\n * - Strips <script>/<style> blocks and closing tags\n * - Removes inline event handlers (on*)\n * - Neutralizes javascript: URLs in href/src\n * - Whitelists a small set of tags: a,b,strong,i,em,code,br,ul,ol,li,p\n *\n * This utility is intentionally small and dependency-free. If we decide to\n * allow richer HTML, we can swap this implementation with a vetted library\n * (e.g., DOMPurify) behind the same function signature.\n */\nexport function sanitizeHtml(html: string): string {\n if (!html) return '';\n\n // Remove script/style elements entirely (including contents)\n let out = html\n .replace(/<\\/(?:script|style)>/gi, '')\n .replace(/<(?:script|style)[\\s\\S]*?>[\\s\\S]*?<\\/(?:script|style)>/gi, '');\n\n // Remove inline event handlers like onload=, onclick=\n out = out.replace(/\\son[a-z]+\\s*=\\s*\"[^\"]*\"/gi, '');\n out = out.replace(/\\son[a-z]+\\s*=\\s*'[^']*'/gi, '');\n out = out.replace(/\\son[a-z]+\\s*=\\s*[^\\s>]+/gi, '');\n\n // Neutralize javascript: protocol in href/src\n out = out.replace(/(href|src)\\s*=\\s*\"javascript:[^\"]*\"/gi, '$1=\"#\"');\n out = out.replace(/(href|src)\\s*=\\s*'javascript:[^']*'/gi, '$1=\"#\"');\n\n // Strip non-whitelisted tags (keep: a,b,strong,i,em,code,br,ul,ol,li,p)\n out = out.replace(/<(?!\\/?(?:a|b|strong|i|em|code|br|ul|ol|li|p)\\b)[^>]*>/gi, '');\n\n return out;\n}\n","/**\n * Access Control Snapshot Helpers\n *\n * Utilities for serializing, validating, and working with access control snapshots.\n */\n\nimport type { AccessSnapshot, RoleAssignment, RoleIdentifier } from '@openzeppelin/ui-types';\n\n/**\n * Validates an access snapshot structure\n * @param snapshot The snapshot to validate\n * @returns True if valid, false otherwise\n */\nexport function validateSnapshot(snapshot: AccessSnapshot): boolean {\n if (!snapshot || typeof snapshot !== 'object') {\n return false;\n }\n\n if (!Array.isArray(snapshot.roles)) {\n return false;\n }\n\n // Check for duplicate roles\n const roleIds = new Set<string>();\n for (const roleAssignment of snapshot.roles) {\n if (!roleAssignment || typeof roleAssignment !== 'object') {\n return false;\n }\n\n if (!roleAssignment.role || typeof roleAssignment.role !== 'object') {\n return false;\n }\n\n const roleId = roleAssignment.role.id;\n if (!roleId || typeof roleId !== 'string' || roleId.trim() === '') {\n return false;\n }\n\n if (roleIds.has(roleId)) {\n return false; // Duplicate role\n }\n roleIds.add(roleId);\n\n if (!Array.isArray(roleAssignment.members)) {\n return false;\n }\n\n // Check for duplicate members within a role\n const memberSet = new Set<string>();\n for (const member of roleAssignment.members) {\n if (typeof member !== 'string' || member.trim() === '') {\n return false;\n }\n if (memberSet.has(member)) {\n return false; // Duplicate member\n }\n memberSet.add(member);\n }\n }\n\n // Validate ownership if present\n if (snapshot.ownership !== undefined) {\n if (!snapshot.ownership || typeof snapshot.ownership !== 'object') {\n return false;\n }\n if (\n snapshot.ownership.owner !== null &&\n (typeof snapshot.ownership.owner !== 'string' || snapshot.ownership.owner.trim() === '')\n ) {\n return false;\n }\n }\n\n return true;\n}\n\n/**\n * Serializes an access snapshot to JSON string\n * @param snapshot The snapshot to serialize\n * @returns JSON string representation\n * @throws Error if snapshot is invalid\n */\nexport function serializeSnapshot(snapshot: AccessSnapshot): string {\n if (!validateSnapshot(snapshot)) {\n throw new Error('Invalid snapshot structure');\n }\n return JSON.stringify(snapshot, null, 2);\n}\n\n/**\n * Deserializes a JSON string to an access snapshot\n * @param json The JSON string to deserialize\n * @returns Access snapshot object\n * @throws Error if JSON is invalid or snapshot structure is invalid\n */\nexport function deserializeSnapshot(json: string): AccessSnapshot {\n let parsed: unknown;\n try {\n parsed = JSON.parse(json);\n } catch (error) {\n throw new Error(`Invalid JSON: ${error instanceof Error ? error.message : 'Unknown error'}`);\n }\n\n if (!validateSnapshot(parsed as AccessSnapshot)) {\n throw new Error('Invalid snapshot structure after deserialization');\n }\n\n return parsed as AccessSnapshot;\n}\n\n/**\n * Creates an empty snapshot\n * @returns Empty snapshot with no roles and no ownership\n */\nexport function createEmptySnapshot(): AccessSnapshot {\n return {\n roles: [],\n };\n}\n\n/**\n * Finds a role assignment by role ID\n * @param snapshot The snapshot to search\n * @param roleId The role ID to find\n * @returns The role assignment if found, undefined otherwise\n */\nexport function findRoleAssignment(\n snapshot: AccessSnapshot,\n roleId: string\n): RoleAssignment | undefined {\n return snapshot.roles.find((assignment) => assignment.role.id === roleId);\n}\n\n/**\n * Checks if a snapshot has any roles\n * @param snapshot The snapshot to check\n * @returns True if snapshot has at least one role assignment\n */\nexport function hasRoles(snapshot: AccessSnapshot): boolean {\n return snapshot.roles.length > 0;\n}\n\n/**\n * Checks if a snapshot has ownership information\n * @param snapshot The snapshot to check\n * @returns True if snapshot has ownership information\n */\nexport function hasOwnership(snapshot: AccessSnapshot): boolean {\n return snapshot.ownership !== undefined && snapshot.ownership !== null;\n}\n\n/**\n * Gets the total number of role members across all roles\n * @param snapshot The snapshot to count\n * @returns Total number of unique members across all roles\n */\nexport function getTotalMemberCount(snapshot: AccessSnapshot): number {\n const allMembers = new Set<string>();\n for (const roleAssignment of snapshot.roles) {\n for (const member of roleAssignment.members) {\n allMembers.add(member);\n }\n }\n return allMembers.size;\n}\n\n/**\n * Gets all unique members across all roles\n * @param snapshot The snapshot to extract members from\n * @returns Array of unique member addresses\n */\nexport function getAllMembers(snapshot: AccessSnapshot): string[] {\n const allMembers = new Set<string>();\n for (const roleAssignment of snapshot.roles) {\n for (const member of roleAssignment.members) {\n allMembers.add(member);\n }\n }\n return Array.from(allMembers);\n}\n\n/**\n * Compares two snapshots and returns differences\n * @param snapshot1 First snapshot\n * @param snapshot2 Second snapshot\n * @returns Object describing differences\n */\nexport function compareSnapshots(\n snapshot1: AccessSnapshot,\n snapshot2: AccessSnapshot\n): {\n rolesAdded: RoleAssignment[];\n rolesRemoved: RoleAssignment[];\n rolesModified: Array<{\n role: RoleIdentifier;\n membersAdded: string[];\n membersRemoved: string[];\n }>;\n ownershipChanged: boolean;\n} {\n const rolesAdded: RoleAssignment[] = [];\n const rolesRemoved: RoleAssignment[] = [];\n const rolesModified: Array<{\n role: RoleIdentifier;\n membersAdded: string[];\n membersRemoved: string[];\n }> = [];\n\n const roleMap1 = new Map<string, RoleAssignment>();\n const roleMap2 = new Map<string, RoleAssignment>();\n\n for (const assignment of snapshot1.roles) {\n roleMap1.set(assignment.role.id, assignment);\n }\n\n for (const assignment of snapshot2.roles) {\n roleMap2.set(assignment.role.id, assignment);\n }\n\n // Find added and removed roles\n for (const [roleId, assignment] of roleMap2) {\n if (!roleMap1.has(roleId)) {\n rolesAdded.push(assignment);\n }\n }\n\n for (const [roleId, assignment] of roleMap1) {\n if (!roleMap2.has(roleId)) {\n rolesRemoved.push(assignment);\n }\n }\n\n // Find modified roles\n for (const [roleId, assignment1] of roleMap1) {\n const assignment2 = roleMap2.get(roleId);\n if (assignment2) {\n const members1 = new Set(assignment1.members);\n const members2 = new Set(assignment2.members);\n\n const membersAdded = assignment2.members.filter((m) => !members1.has(m));\n const membersRemoved = assignment1.members.filter((m) => !members2.has(m));\n\n if (membersAdded.length > 0 || membersRemoved.length > 0) {\n rolesModified.push({\n role: assignment1.role,\n membersAdded,\n membersRemoved,\n });\n }\n }\n }\n\n const ownershipChanged = snapshot1.ownership?.owner !== snapshot2.ownership?.owner;\n\n return {\n rolesAdded,\n rolesRemoved,\n rolesModified,\n ownershipChanged,\n };\n}\n","/**\n * Access Control Error Utilities\n *\n * Chain-agnostic helper functions for working with access control errors.\n * These utilities can be used across any adapter that implements access control.\n *\n * Note: These utilities work with the AccessControlError types from @openzeppelin/ui-types\n * but don't import them directly to avoid circular dependencies.\n */\n\n/**\n * Base interface for access control errors (matches the class in types package)\n */\ninterface AccessControlErrorLike extends Error {\n readonly contractAddress?: string;\n}\n\n/**\n * Type guard to check if an error is an AccessControlError\n *\n * @param error The error to check\n * @returns True if the error has the AccessControlError structure\n *\n * @example\n * ```typescript\n * try {\n * await service.grantRole(...);\n * } catch (error) {\n * if (isAccessControlError(error)) {\n * console.log('Access control error:', error.contractAddress);\n * }\n * }\n * ```\n */\nexport function isAccessControlError(error: unknown): error is AccessControlErrorLike {\n return (\n error instanceof Error &&\n 'contractAddress' in error &&\n (typeof (error as AccessControlErrorLike).contractAddress === 'string' ||\n (error as AccessControlErrorLike).contractAddress === undefined)\n );\n}\n\n/**\n * Helper to safely extract error message from unknown error type\n *\n * @param error The error to extract message from\n * @returns The error message string\n *\n * @example\n * ```typescript\n * try {\n * await someOperation();\n * } catch (error) {\n * const message = getErrorMessage(error);\n * logger.error('Operation failed:', message);\n * }\n * ```\n */\nexport function getErrorMessage(error: unknown): string {\n if (error instanceof Error) {\n return error.message;\n }\n return String(error);\n}\n\n/**\n * Helper to create a user-friendly error message with full context\n *\n * This function formats an AccessControlError into a readable multi-line message\n * that includes all relevant context (contract address, roles, operations, etc.).\n *\n * @param error The AccessControlError to format\n * @returns Formatted error message string with all context\n *\n * @example\n * ```typescript\n * import { PermissionDenied } from '@openzeppelin/ui-types';\n * import { formatAccessControlError } from '@openzeppelin/ui-utils';\n *\n * try {\n * await service.grantRole(...);\n * } catch (error) {\n * if (error instanceof PermissionDenied) {\n * const formatted = formatAccessControlError(error);\n * showErrorToUser(formatted);\n * }\n * }\n * ```\n *\n * Output format:\n * ```\n * [ErrorName] Error message\n * Contract: 0x123...\n * [Additional context based on error type]\n * ```\n */\nexport function formatAccessControlError(error: AccessControlErrorLike): string {\n let message = `[${error.name}] ${error.message}`;\n\n if (error.contractAddress) {\n message += `\\nContract: ${error.contractAddress}`;\n }\n\n // Check for specific error types by name and format accordingly\n // This avoids importing the actual classes and keeps utils independent\n const errorWithProperties = error as AccessControlErrorLike & {\n requiredRole?: string;\n callerAddress?: string;\n networkId?: string;\n endpointUrl?: string;\n configField?: string;\n providedValue?: unknown;\n operation?: string;\n cause?: Error;\n missingFeatures?: string[];\n };\n\n if (error.name === 'PermissionDenied') {\n if (errorWithProperties.requiredRole) {\n message += `\\nRequired Role: ${errorWithProperties.requiredRole}`;\n }\n if (errorWithProperties.callerAddress) {\n message += `\\nCaller: ${errorWithProperties.callerAddress}`;\n }\n } else if (error.name === 'IndexerUnavailable') {\n if (errorWithProperties.networkId) {\n message += `\\nNetwork: ${errorWithProperties.networkId}`;\n }\n if (errorWithProperties.endpointUrl) {\n message += `\\nEndpoint: ${errorWithProperties.endpointUrl}`;\n }\n } else if (error.name === 'ConfigurationInvalid') {\n if (errorWithProperties.configField) {\n message += `\\nInvalid Field: ${errorWithProperties.configField}`;\n }\n if (errorWithProperties.providedValue !== undefined) {\n message += `\\nProvided Value: ${JSON.stringify(errorWithProperties.providedValue)}`;\n }\n } else if (error.name === 'OperationFailed') {\n if (errorWithProperties.operation) {\n message += `\\nOperation: ${errorWithProperties.operation}`;\n }\n if (errorWithProperties.cause) {\n message += `\\nCause: ${errorWithProperties.cause.message}`;\n }\n } else if (error.name === 'UnsupportedContractFeatures') {\n if (errorWithProperties.missingFeatures && Array.isArray(errorWithProperties.missingFeatures)) {\n message += `\\nMissing Features: ${errorWithProperties.missingFeatures.join(', ')}`;\n }\n }\n\n return message;\n}\n","/**\n * Wallet Component Sizing Utilities\n *\n * These utilities provide consistent size mappings for wallet UI components\n * across all ecosystem adapters (EVM, Stellar, Midnight, etc.).\n *\n * The mappings translate the generic WalletComponentSize ('sm' | 'default' | 'lg' | 'xl')\n * to specific Tailwind CSS classes and component props.\n */\n\nimport type { WalletComponentSize, WalletComponentVariant } from '@openzeppelin/ui-types';\n\n// ============================================================\n// Type Definitions\n// ============================================================\n\n/**\n * Size properties for wallet connect button components\n */\nexport interface WalletButtonSizeProps {\n /** Button component size prop */\n size: 'sm' | 'default' | 'lg';\n /** Additional Tailwind classes for fine-tuned sizing */\n className: string;\n /** Icon size class (e.g., 'size-4') */\n iconSize: string;\n}\n\n/**\n * Size properties for wallet account display components\n */\nexport interface WalletAccountDisplaySizeProps {\n /** Primary text size class */\n textSize: string;\n /** Secondary/subtitle text size class */\n subTextSize: string;\n /** Icon button container size class */\n iconButtonSize: string;\n /** Icon size class */\n iconSize: string;\n}\n\n/**\n * Size properties for wallet network switcher components\n */\nexport interface WalletNetworkSwitcherSizeProps {\n /** Select trigger className */\n triggerClassName: string;\n /** Select item className */\n itemClassName: string;\n /** Loader icon size class */\n loaderSize: string;\n}\n\n// ============================================================\n// Size Mapping Functions\n// ============================================================\n\n/**\n * Maps WalletComponentSize to Button component props and styling.\n * Used for ConnectButton and similar button-based wallet components.\n *\n * @param walletSize - The wallet component size ('sm' | 'default' | 'lg' | 'xl')\n * @returns Button size props including component size, className overrides, and icon size\n *\n * @example\n * ```tsx\n * const sizeProps = getWalletButtonSizeProps(size);\n * <Button size={sizeProps.size} className={sizeProps.className}>\n * <Icon className={sizeProps.iconSize} />\n * </Button>\n * ```\n */\nexport function getWalletButtonSizeProps(\n walletSize: WalletComponentSize | undefined\n): WalletButtonSizeProps {\n switch (walletSize) {\n case 'sm':\n return { size: 'sm', className: 'h-7 px-2 text-[11px]', iconSize: 'size-3' };\n case 'lg':\n return { size: 'lg', className: 'h-11 px-5', iconSize: 'size-5' };\n case 'xl':\n return { size: 'lg', className: 'h-12 px-6 text-base', iconSize: 'size-5' };\n case 'default':\n default:\n return { size: 'default', className: 'h-9 px-4 text-sm', iconSize: 'size-4' };\n }\n}\n\n/**\n * Maps WalletComponentSize to AccountDisplay component styling.\n * Provides text sizes and icon button dimensions for account info display.\n *\n * @param walletSize - The wallet component size ('sm' | 'default' | 'lg' | 'xl')\n * @returns Account display size props for text, subtitle, and disconnect button\n *\n * @example\n * ```tsx\n * const sizeProps = getWalletAccountDisplaySizeProps(size);\n * <span className={sizeProps.textSize}>{address}</span>\n * <Button className={sizeProps.iconButtonSize}>\n * <LogOut className={sizeProps.iconSize} />\n * </Button>\n * ```\n */\nexport function getWalletAccountDisplaySizeProps(\n walletSize: WalletComponentSize | undefined\n): WalletAccountDisplaySizeProps {\n switch (walletSize) {\n case 'sm':\n return {\n textSize: 'text-[11px]',\n subTextSize: 'text-[8px]',\n iconButtonSize: 'size-5',\n iconSize: 'size-3',\n };\n case 'lg':\n return {\n textSize: 'text-base',\n subTextSize: 'text-xs',\n iconButtonSize: 'size-8',\n iconSize: 'size-4',\n };\n case 'xl':\n return {\n textSize: 'text-lg',\n subTextSize: 'text-sm',\n iconButtonSize: 'size-10',\n iconSize: 'size-5',\n };\n case 'default':\n default:\n return {\n textSize: 'text-xs',\n subTextSize: 'text-[9px]',\n iconButtonSize: 'size-6',\n iconSize: 'size-3.5',\n };\n }\n}\n\n/**\n * Maps WalletComponentSize to NetworkSwitcher component styling.\n * Provides sizing for select trigger, items, and loading indicator.\n *\n * @param walletSize - The wallet component size ('sm' | 'default' | 'lg' | 'xl')\n * @returns Network switcher size props for select components\n *\n * @example\n * ```tsx\n * const sizeProps = getWalletNetworkSwitcherSizeProps(size);\n * <SelectTrigger className={sizeProps.triggerClassName}>\n * ...\n * </SelectTrigger>\n * <Loader2 className={sizeProps.loaderSize} />\n * ```\n */\nexport function getWalletNetworkSwitcherSizeProps(\n walletSize: WalletComponentSize | undefined\n): WalletNetworkSwitcherSizeProps {\n switch (walletSize) {\n case 'sm':\n return {\n triggerClassName: 'h-8 text-xs px-2 min-w-[90px] max-w-[120px]',\n itemClassName: 'text-xs py-1.5',\n loaderSize: 'h-3 w-3',\n };\n case 'lg':\n return {\n triggerClassName: 'h-10 text-sm px-3 min-w-[120px] max-w-[180px]',\n itemClassName: 'text-sm py-2',\n loaderSize: 'h-4 w-4',\n };\n case 'xl':\n return {\n triggerClassName: 'h-12 text-base px-4 min-w-[140px] max-w-[200px]',\n itemClassName: 'text-base py-2.5',\n loaderSize: 'h-5 w-5',\n };\n case 'default':\n default:\n return {\n triggerClassName: 'h-9 text-sm px-3 min-w-[100px] max-w-[150px]',\n itemClassName: 'text-sm py-1.5',\n loaderSize: 'h-3.5 w-3.5',\n };\n }\n}\n\n/**\n * Maps WalletComponentVariant to NetworkSwitcher SelectTrigger styling.\n * Since Select components don't have a native variant prop like Button,\n * this maps variants to appropriate Tailwind CSS classes.\n *\n * @param variant - The wallet component variant ('default' | 'outline' | 'ghost' | 'secondary')\n * @returns CSS class string for the SelectTrigger\n *\n * @example\n * ```tsx\n * const variantClassName = getWalletNetworkSwitcherVariantClassName(variant);\n * <SelectTrigger className={cn(sizeProps.triggerClassName, variantClassName)}>\n * ...\n * </SelectTrigger>\n * ```\n */\nexport function getWalletNetworkSwitcherVariantClassName(\n variant: WalletComponentVariant | undefined\n): string {\n switch (variant) {\n case 'ghost':\n return 'border-transparent bg-transparent hover:bg-accent';\n case 'secondary':\n return 'border-secondary bg-secondary/10 hover:bg-secondary/20';\n case 'outline':\n return 'border-input bg-transparent hover:bg-accent';\n case 'default':\n default:\n return ''; // Use default SelectTrigger styling\n }\n}\n"],"mappings":"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"}
|