@openzeppelin/ui-builder-adapter-evm 1.2.0 → 1.4.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.
Files changed (105) hide show
  1. package/README.md +33 -18
  2. package/dist/index.cjs +4651 -4448
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +12 -226
  5. package/dist/index.d.ts +12 -226
  6. package/dist/index.js +4737 -4535
  7. package/dist/index.js.map +1 -1
  8. package/package.json +7 -6
  9. package/src/__tests__/adapter-parsing.test.ts +4 -3
  10. package/src/__tests__/getDefaultServiceConfig.test.ts +185 -0
  11. package/src/__tests__/mocks/mock-network-configs.ts +1 -1
  12. package/src/__tests__/provenanceLinks.test.ts +6 -4
  13. package/src/__tests__/providerSelection.test.ts +5 -4
  14. package/src/__tests__/timeouts.test.ts +5 -3
  15. package/src/__tests__/wallet-connect.test.ts +2 -2
  16. package/src/adapter.ts +61 -107
  17. package/src/configuration/execution.ts +1 -52
  18. package/src/configuration/index.ts +2 -3
  19. package/src/configuration/network-services.ts +47 -60
  20. package/src/index.ts +22 -13
  21. package/src/networks/index.ts +2 -1
  22. package/src/networks/mainnet.ts +1 -1
  23. package/src/networks/testnet.ts +1 -1
  24. package/src/query/adapter-query.ts +72 -0
  25. package/src/query/index.ts +2 -2
  26. package/src/transaction/components/useEvmRelayerOptions.ts +5 -3
  27. package/src/transaction/index.ts +1 -5
  28. package/src/types/artifacts.ts +5 -30
  29. package/src/types/providers.ts +7 -18
  30. package/src/wallet/components/EvmWalletUiRoot.tsx +1 -1
  31. package/src/wallet/evmUiKitManager.ts +26 -129
  32. package/src/wallet/hooks/index.ts +0 -1
  33. package/src/wallet/implementation/wagmi-implementation.ts +45 -577
  34. package/src/wallet/index.ts +2 -3
  35. package/src/wallet/rainbowkit/__tests__/export-service.test.ts +1 -2
  36. package/src/wallet/rainbowkit/componentFactory.ts +10 -8
  37. package/src/wallet/rainbowkit/components.tsx +16 -133
  38. package/src/wallet/rainbowkit/index.ts +27 -5
  39. package/src/wallet/utils/__tests__/uiKitService.test.ts +5 -1
  40. package/src/wallet/utils/connection.ts +8 -52
  41. package/src/wallet/utils/index.ts +0 -2
  42. package/src/wallet/utils/uiKitService.ts +7 -3
  43. package/src/wallet/utils/walletImplementationManager.ts +5 -4
  44. package/src/wallet/utils.ts +1 -65
  45. package/src/abi/__tests__/etherscan-v2.test.ts +0 -117
  46. package/src/abi/__tests__/transformer.test.ts +0 -342
  47. package/src/abi/comparison.ts +0 -389
  48. package/src/abi/etherscan-v2.ts +0 -243
  49. package/src/abi/etherscan.ts +0 -158
  50. package/src/abi/index.ts +0 -7
  51. package/src/abi/loader.ts +0 -415
  52. package/src/abi/sourcify.ts +0 -75
  53. package/src/abi/transformer.ts +0 -163
  54. package/src/abi/types.ts +0 -101
  55. package/src/configuration/__tests__/explorer.test.ts +0 -174
  56. package/src/configuration/__tests__/rpc.test.ts +0 -176
  57. package/src/configuration/explorer.ts +0 -243
  58. package/src/configuration/rpc.ts +0 -257
  59. package/src/mapping/__tests__/field-generator.test.ts +0 -137
  60. package/src/mapping/__tests__/type-mapper.test.ts +0 -139
  61. package/src/mapping/constants.ts +0 -57
  62. package/src/mapping/field-generator.ts +0 -115
  63. package/src/mapping/index.ts +0 -4
  64. package/src/mapping/type-mapper.ts +0 -80
  65. package/src/proxy/detection.ts +0 -465
  66. package/src/query/handler.ts +0 -227
  67. package/src/query/view-checker.ts +0 -10
  68. package/src/transaction/eoa.ts +0 -98
  69. package/src/transaction/execution-strategy.ts +0 -33
  70. package/src/transaction/formatter.ts +0 -101
  71. package/src/transaction/relayer.ts +0 -380
  72. package/src/transaction/sender.ts +0 -185
  73. package/src/transform/index.ts +0 -3
  74. package/src/transform/input-parser.ts +0 -177
  75. package/src/transform/output-formatter.ts +0 -64
  76. package/src/types/__tests__/artifacts.test.ts +0 -105
  77. package/src/types.ts +0 -92
  78. package/src/utils/__tests__/artifacts.test.ts +0 -81
  79. package/src/utils/artifacts.ts +0 -30
  80. package/src/utils/formatting.ts +0 -25
  81. package/src/utils/gas.ts +0 -17
  82. package/src/utils/index.ts +0 -6
  83. package/src/utils/json.ts +0 -19
  84. package/src/utils/validation.ts +0 -10
  85. package/src/validation/eoa.ts +0 -33
  86. package/src/validation/index.ts +0 -2
  87. package/src/validation/relayer.ts +0 -13
  88. package/src/wallet/__tests__/utils.test.ts +0 -149
  89. package/src/wallet/components/account/AccountDisplay.tsx +0 -52
  90. package/src/wallet/components/connect/ConnectButton.tsx +0 -125
  91. package/src/wallet/components/connect/ConnectorDialog.tsx +0 -140
  92. package/src/wallet/components/index.ts +0 -4
  93. package/src/wallet/components/network/NetworkSwitcher.tsx +0 -90
  94. package/src/wallet/context/index.ts +0 -1
  95. package/src/wallet/context/wagmi-context.tsx +0 -7
  96. package/src/wallet/hooks/useIsWagmiProviderInitialized.ts +0 -11
  97. package/src/wallet/rainbowkit/config-generator.ts +0 -56
  98. package/src/wallet/rainbowkit/config-service.ts +0 -169
  99. package/src/wallet/rainbowkit/export-service.ts +0 -18
  100. package/src/wallet/rainbowkit/rainbowkitAssetManager.ts +0 -74
  101. package/src/wallet/rainbowkit/types.ts +0 -74
  102. package/src/wallet/rainbowkit/utils.ts +0 -96
  103. package/src/wallet/services/configResolutionService.ts +0 -65
  104. package/src/wallet/utils/SafeWagmiComponent.tsx +0 -72
  105. package/src/wallet/utils/filterWalletComponents.ts +0 -89
@@ -1,16 +1,18 @@
1
- import type { EcosystemWalletComponents } from '@openzeppelin/ui-types';
1
+ /**
2
+ * EVM Adapter RainbowKit Component Factory
3
+ *
4
+ * Factory function for creating RainbowKit components.
5
+ * Separated from components.tsx to support React Fast Refresh.
6
+ */
7
+ import { createRainbowKitComponents as coreCreateRainbowKitComponents } from '@openzeppelin/ui-builder-adapter-evm-core';
2
8
 
3
9
  import { RainbowKitConnectButton } from './components';
4
10
 
5
11
  /**
6
- * Creates the complete set of RainbowKit wallet components.
12
+ * Creates the complete set of RainbowKit wallet components for the EVM adapter.
7
13
  *
8
14
  * @returns An object containing all RainbowKit wallet components
9
15
  */
10
- export function createRainbowKitComponents(): EcosystemWalletComponents {
11
- return {
12
- ConnectButton: RainbowKitConnectButton,
13
- // RainbowKit's ConnectButton is comprehensive and typically includes account display
14
- // So we don't provide separate AccountDisplay or NetworkSwitcher components
15
- };
16
+ export function createRainbowKitComponents() {
17
+ return coreCreateRainbowKitComponents(RainbowKitConnectButton);
16
18
  }
@@ -1,140 +1,23 @@
1
- import { Loader2 } from 'lucide-react';
2
- import React, { useContext, useEffect, useRef, useState } from 'react';
3
-
4
- import { Button } from '@openzeppelin/ui-components';
1
+ /**
2
+ * EVM Adapter RainbowKit Components
3
+ *
4
+ * This file exports only React components to support Fast Refresh.
5
+ * Factory functions are in componentFactory.ts.
6
+ */
7
+ import { createRainbowKitConnectButton } from '@openzeppelin/ui-builder-adapter-evm-core';
5
8
  import type { BaseComponentProps } from '@openzeppelin/ui-types';
6
- import { cn, logger } from '@openzeppelin/ui-utils';
7
9
 
8
- import { CustomConnectButton } from '../components';
9
- import { WagmiProviderInitializedContext } from '../context/wagmi-context';
10
10
  import { evmUiKitManager } from '../evmUiKitManager';
11
- import { extractRainbowKitCustomizations } from './types';
12
-
13
- const MIN_COMPONENT_LOADING_DISPLAY_MS = 1000; // 1 second artificial delay
14
11
 
15
12
  /**
16
- * Creates a lazy-loaded RainbowKit ConnectButton component.
17
- *
18
- * @returns A React component that dynamically imports the RainbowKit ConnectButton
13
+ * Props for the RainbowKitConnectButton component.
19
14
  */
20
- export const RainbowKitConnectButton: React.FC<BaseComponentProps> = (props) => {
21
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
22
- const [Component, setComponent] = useState<React.ComponentType<any> | null>(null);
23
- const [error, setError] = useState<Error | null>(null);
24
- const [isLoadingComponent, setIsLoadingComponent] = useState(true);
25
- const [showComponentLoadingOverride, setShowComponentLoadingOverride] = useState(false);
26
- const componentLoadingTimerRef = useRef<NodeJS.Timeout | null>(null);
27
- const [managerState, setManagerState] = useState(evmUiKitManager.getState());
28
-
29
- const isWagmiProviderReady = useContext(WagmiProviderInitializedContext);
30
-
31
- // Subscribe to UI kit manager state changes
32
- useEffect(() => {
33
- const unsubscribe = evmUiKitManager.subscribe(() => {
34
- setManagerState(evmUiKitManager.getState());
35
- });
36
- return unsubscribe;
37
- }, []);
38
-
39
- useEffect(() => {
40
- let isMounted = true;
41
- setIsLoadingComponent(true);
42
- setShowComponentLoadingOverride(true); // Start showing override immediately
43
-
44
- if (componentLoadingTimerRef.current) {
45
- clearTimeout(componentLoadingTimerRef.current);
46
- }
47
- componentLoadingTimerRef.current = setTimeout(() => {
48
- if (isMounted) {
49
- setShowComponentLoadingOverride(false);
50
- }
51
- componentLoadingTimerRef.current = null;
52
- }, MIN_COMPONENT_LOADING_DISPLAY_MS);
53
-
54
- const loadComponent = async () => {
55
- try {
56
- const rainbowKit = await import('@rainbow-me/rainbowkit');
57
- if (isMounted) {
58
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
59
- setComponent(() => rainbowKit.ConnectButton as React.ComponentType<any>);
60
- // Actual component loading is done, but override might still be active
61
- setIsLoadingComponent(false);
62
- }
63
- } catch (err) {
64
- if (isMounted) {
65
- setError(err instanceof Error ? err : new Error(String(err)));
66
- setIsLoadingComponent(false); // Finished loading (with an error)
67
- logger.error('RainbowKitConnectButton', 'Failed to load RainbowKit ConnectButton:', err);
68
- }
69
- }
70
- };
71
-
72
- loadComponent();
15
+ export type RainbowKitConnectButtonProps = BaseComponentProps;
73
16
 
74
- return () => {
75
- isMounted = false;
76
- if (componentLoadingTimerRef.current) {
77
- clearTimeout(componentLoadingTimerRef.current);
78
- }
79
- };
80
- }, []); // Effect for dynamic import runs once
81
-
82
- const renderLoadingPlaceholder = (message: string) => (
83
- <Button
84
- disabled={true}
85
- variant="outline"
86
- size="sm"
87
- className={cn('h-8 px-2 text-xs', props.className)}
88
- >
89
- <Loader2 className="h-3.5 w-3.5 animate-spin mr-1.5" />
90
- {message}
91
- </Button>
92
- );
93
-
94
- if (error) {
95
- logger.warn(
96
- 'RainbowKitConnectButton',
97
- 'Error loading RainbowKit ConnectButton. Displaying fallback CustomConnectButton.'
98
- );
99
- return <CustomConnectButton {...props} />;
100
- }
101
-
102
- // Show "Loading Wallet..." if component is still factually loading OR if the override is active
103
- if (isLoadingComponent || showComponentLoadingOverride) {
104
- return renderLoadingPlaceholder('Loading Wallet...');
105
- }
106
- // At this point, component import has finished (successfully or not handled by error state)
107
- // AND the minimum display time for "Loading Wallet..." has passed.
108
-
109
- // Now check if the provider context is ready
110
- if (!isWagmiProviderReady) {
111
- // No separate delay for this message; it appears if context isn't ready after component load delay.
112
- return renderLoadingPlaceholder('Initializing Provider...');
113
- }
114
-
115
- // Component should be non-null here if no error and not isLoadingComponent
116
- if (!Component) {
117
- // This case should ideally not be hit if logic is correct, but as a safeguard:
118
- logger.warn('RainbowKitConnectButton', 'Component is null after loading phase, falling back.');
119
- return <CustomConnectButton {...props} />;
120
- }
121
-
122
- // Extract custom configuration from the manager state
123
- const kitConfig = managerState.currentFullUiKitConfig?.kitConfig;
124
- const customizations = extractRainbowKitCustomizations(kitConfig);
125
- const connectButtonConfig = customizations?.connectButton;
126
-
127
- // Merge props: base props + custom configuration + any overrides from props
128
- // This allows the config to set defaults while still allowing prop overrides
129
- const finalProps = {
130
- ...connectButtonConfig, // Apply custom configuration from config
131
- ...props, // Allow props to override configuration
132
- };
133
-
134
- logger.debug('RainbowKitConnectButton', 'Rendering with configuration:', {
135
- configFromFile: connectButtonConfig,
136
- finalProps: finalProps,
137
- });
138
-
139
- return <Component {...finalProps} />;
140
- };
17
+ /**
18
+ * RainbowKitConnectButton component configured with the EVM UI kit manager.
19
+ * This component lazily loads the RainbowKit ConnectButton and manages
20
+ * its state through the evmUiKitManager.
21
+ */
22
+ export const RainbowKitConnectButton: React.FC<RainbowKitConnectButtonProps> =
23
+ createRainbowKitConnectButton(evmUiKitManager);
@@ -1,5 +1,27 @@
1
- export * from './components';
2
- export * from './utils';
3
- export * from './componentFactory';
4
- export * from './types';
5
- export { createRainbowKitWagmiConfig, getWagmiConfigForRainbowKit } from './config-service';
1
+ // Re-export types from core
2
+ export type {
3
+ AppInfo,
4
+ RainbowKitConnectButtonProps,
5
+ RainbowKitProviderProps,
6
+ RainbowKitKitConfig,
7
+ RainbowKitCustomizations,
8
+ } from '@openzeppelin/ui-builder-adapter-evm-core';
9
+
10
+ export {
11
+ isRainbowKitCustomizations,
12
+ extractRainbowKitCustomizations,
13
+ validateRainbowKitConfig,
14
+ getRawUserNativeConfig,
15
+ } from '@openzeppelin/ui-builder-adapter-evm-core';
16
+
17
+ // EVM-specific components
18
+ export { RainbowKitConnectButton } from './components';
19
+
20
+ // EVM-specific factory functions
21
+ export { createRainbowKitComponents } from './componentFactory';
22
+
23
+ // Re-export config service from core for convenience
24
+ export {
25
+ createRainbowKitWagmiConfig,
26
+ getWagmiConfigForRainbowKit,
27
+ } from '@openzeppelin/ui-builder-adapter-evm-core';
@@ -1,9 +1,13 @@
1
1
  import { beforeEach, describe, expect, it, vi } from 'vitest';
2
2
 
3
+ import {
4
+ CustomAccountDisplay,
5
+ CustomConnectButton,
6
+ CustomNetworkSwitcher,
7
+ } from '@openzeppelin/ui-builder-adapter-evm-core';
3
8
  import type { ComponentExclusionConfig, UiKitConfiguration } from '@openzeppelin/ui-types';
4
9
  import { logger } from '@openzeppelin/ui-utils';
5
10
 
6
- import { CustomAccountDisplay, CustomConnectButton, CustomNetworkSwitcher } from '../../components';
7
11
  import { getResolvedWalletComponents } from '../uiKitService';
8
12
 
9
13
  // Mock the logger
@@ -1,5 +1,10 @@
1
1
  import type { GetAccountReturnType } from '@wagmi/core';
2
2
 
3
+ import {
4
+ connectAndEnsureCorrectNetworkCore,
5
+ DEFAULT_DISCONNECTED_STATUS,
6
+ type EvmWalletConnectionResult,
7
+ } from '@openzeppelin/ui-builder-adapter-evm-core';
3
8
  import type { Connector } from '@openzeppelin/ui-types';
4
9
  import { logger } from '@openzeppelin/ui-utils';
5
10
 
@@ -41,52 +46,14 @@ export async function getEvmAvailableConnectors(): Promise<Connector[]> {
41
46
  export async function connectAndEnsureCorrectNetwork(
42
47
  connectorId: string,
43
48
  targetChainId: number
44
- ): Promise<{ connected: boolean; address?: string; chainId?: number; error?: string }> {
49
+ ): Promise<EvmWalletConnectionResult> {
45
50
  const impl = await getEvmWalletImplementation();
46
51
  if (!impl) {
47
52
  logger.error(LOG_SYSTEM, 'connectAndEnsureCorrectNetwork: Wallet implementation not ready.');
48
53
  return { connected: false, error: 'Wallet system not initialized.' };
49
54
  }
50
55
 
51
- const connectionResult = await impl.connect(connectorId);
52
- if (!connectionResult.connected || !connectionResult.address || !connectionResult.chainId) {
53
- return { connected: false, error: connectionResult.error || 'Connection failed' };
54
- }
55
-
56
- if (connectionResult.chainId !== targetChainId) {
57
- logger.info(
58
- LOG_SYSTEM,
59
- `Connected to chain ${connectionResult.chainId}, but target is ${targetChainId}. Attempting switch.`
60
- );
61
- try {
62
- await impl.switchNetwork(targetChainId);
63
- const postSwitchStatus = impl.getWalletConnectionStatus();
64
- if (postSwitchStatus.chainId !== targetChainId) {
65
- const switchError = `Failed to switch to target network ${targetChainId}. Current: ${postSwitchStatus.chainId}`;
66
- logger.error(LOG_SYSTEM, switchError);
67
- // Attempt to disconnect to leave a clean state if switch fails
68
- try {
69
- await impl.disconnect();
70
- } catch (e) {
71
- logger.warn(LOG_SYSTEM, 'Failed to disconnect after network switch failure.', e);
72
- }
73
- return { connected: false, error: switchError };
74
- }
75
- logger.info(LOG_SYSTEM, `Successfully switched to target chain ${targetChainId}.`);
76
- return { ...connectionResult, chainId: postSwitchStatus.chainId }; // Return updated chainId
77
- } catch (error) {
78
- const errorMessage = error instanceof Error ? error.message : String(error);
79
- logger.error(LOG_SYSTEM, 'Network switch failed:', errorMessage);
80
- // Attempt to disconnect to leave a clean state if switch fails
81
- try {
82
- await impl.disconnect();
83
- } catch (e) {
84
- logger.warn(LOG_SYSTEM, 'Failed to disconnect after network switch failure.', e);
85
- }
86
- return { connected: false, error: `Network switch failed: ${errorMessage}` };
87
- }
88
- }
89
- return connectionResult;
56
+ return connectAndEnsureCorrectNetworkCore(impl, connectorId, targetChainId, LOG_SYSTEM);
90
57
  }
91
58
 
92
59
  /**
@@ -117,18 +84,7 @@ export function getEvmWalletConnectionStatus(): GetAccountReturnType {
117
84
  LOG_SYSTEM,
118
85
  'getEvmWalletConnectionStatus: Wallet implementation not ready. Returning default disconnected state.'
119
86
  );
120
- return {
121
- isConnected: false,
122
- isConnecting: false,
123
- isDisconnected: true,
124
- isReconnecting: false,
125
- status: 'disconnected',
126
- address: undefined,
127
- addresses: undefined,
128
- chainId: undefined,
129
- chain: undefined,
130
- connector: undefined,
131
- };
87
+ return DEFAULT_DISCONNECTED_STATUS;
132
88
  }
133
89
  return impl.getWalletConnectionStatus();
134
90
  }
@@ -1,6 +1,4 @@
1
1
  export * from './connection';
2
2
  export * from './walletImplementationManager';
3
- export * from './filterWalletComponents';
4
- export * from './SafeWagmiComponent';
5
3
  export * from './uiKitService';
6
4
  export * from './wallet-status';
@@ -1,10 +1,14 @@
1
+ import {
2
+ CustomAccountDisplay,
3
+ CustomConnectButton,
4
+ CustomNetworkSwitcher,
5
+ filterWalletComponents,
6
+ getComponentExclusionsFromConfig,
7
+ } from '@openzeppelin/ui-builder-adapter-evm-core';
1
8
  import type { EcosystemWalletComponents, UiKitConfiguration } from '@openzeppelin/ui-types';
2
9
  import { logger } from '@openzeppelin/ui-utils';
3
10
 
4
- import { CustomAccountDisplay, CustomConnectButton, CustomNetworkSwitcher } from '../components';
5
11
  import { createRainbowKitComponents, validateRainbowKitConfig } from '../rainbowkit';
6
- // Assuming this is the default custom provider
7
- import { filterWalletComponents, getComponentExclusionsFromConfig } from './filterWalletComponents';
8
12
 
9
13
  /** Service for resolving UI kit specific components and providers for the EVM adapter. */
10
14
 
@@ -1,7 +1,8 @@
1
+ import type { WagmiWalletImplementation } from '@openzeppelin/ui-builder-adapter-evm-core';
1
2
  import type { UiKitConfiguration } from '@openzeppelin/ui-types';
2
3
  import { appConfigService, logger } from '@openzeppelin/ui-utils';
3
4
 
4
- import { WagmiWalletImplementation } from '../implementation/wagmi-implementation';
5
+ import { createEvmWalletImplementation } from '../implementation/wagmi-implementation';
5
6
 
6
7
  let walletImplementationInstance: WagmiWalletImplementation | undefined;
7
8
  let walletImplementationPromise: Promise<WagmiWalletImplementation> | undefined;
@@ -37,14 +38,14 @@ export async function getEvmWalletImplementation(): Promise<WagmiWalletImplement
37
38
  | string
38
39
  | undefined;
39
40
 
40
- // Pass initialUiKitConfig to the constructor
41
- const instance = new WagmiWalletImplementation(wcProjectId, initialUiKitConfig);
41
+ // Use factory function for proper EVM configuration
42
+ const instance = createEvmWalletImplementation(wcProjectId, initialUiKitConfig);
42
43
  logger.info(LOG_SYSTEM, 'WagmiWalletImplementation singleton created (async).');
43
44
  walletImplementationInstance = instance;
44
45
  return instance;
45
46
  } catch (error) {
46
47
  logger.error(LOG_SYSTEM, 'Failed to initialize WagmiWalletImplementation (async):', error);
47
- const fallbackInstance = new WagmiWalletImplementation();
48
+ const fallbackInstance = createEvmWalletImplementation();
48
49
  walletImplementationInstance = fallbackInstance;
49
50
  return fallbackInstance;
50
51
  }
@@ -1,9 +1,4 @@
1
- import type {
2
- EcosystemWalletComponents,
3
- NativeConfigLoader,
4
- UiKitConfiguration,
5
- } from '@openzeppelin/ui-types';
6
- import { logger } from '@openzeppelin/ui-utils';
1
+ import type { EcosystemWalletComponents, UiKitConfiguration } from '@openzeppelin/ui-types';
7
2
 
8
3
  // Import the actual service functions instead of using placeholders
9
4
  import { getResolvedWalletComponents as getWalletComponentsFromService } from './utils/uiKitService';
@@ -14,62 +9,3 @@ export function getResolvedWalletComponents(
14
9
  ): EcosystemWalletComponents | undefined {
15
10
  return getWalletComponentsFromService(uiKitConfig);
16
11
  }
17
-
18
- /**
19
- * Resolves and initializes the kit-specific configuration.
20
- * This function acts as a manager to call the appropriate kit's configuration initializer.
21
- *
22
- * @param kitName The name of the UI kit (e.g., 'rainbowkit').
23
- * @param programmaticKitConfig Optional base/programmatic config passed to the kit initializer.
24
- * @param loadConfigModule Optional generic callback to load configuration modules by path.
25
- * @returns A Promise resolving to the final kitConfig (Record<string, unknown>) for the specified kit, or null.
26
- */
27
- export async function resolveAndInitializeKitConfig(
28
- kitName?: string, // kitName is used to construct the path to the conventional config file
29
- programmaticKitConfig?: Record<string, unknown>,
30
- loadConfigModule?: NativeConfigLoader
31
- ): Promise<Record<string, unknown> | null> {
32
- logger.debug(
33
- 'resolveAndInitializeKitConfig',
34
- `Resolving native config for kit: ${kitName || 'none'}`,
35
- {
36
- hasProgrammaticKitConfig: !!programmaticKitConfig,
37
- hasLoadConfigModule: !!loadConfigModule,
38
- }
39
- );
40
-
41
- let userNativeConfig: Record<string, unknown> | null = null;
42
-
43
- // Only attempt to load a native config if a kitName is provided and a loader function exists.
44
- // And the kitName is not 'custom' or 'none' as those typically don't have dedicated native config files.
45
- if (kitName && kitName !== 'custom' && kitName !== 'none' && loadConfigModule) {
46
- const conventionalConfigPath = `./config/wallet/${kitName}.config.ts`;
47
-
48
- try {
49
- userNativeConfig = await loadConfigModule(conventionalConfigPath);
50
- } catch (error) {
51
- logger.warn(
52
- 'resolveAndInitializeKitConfig',
53
- `Call to load native config for ${kitName} from ${conventionalConfigPath} failed. Error:`,
54
- error
55
- );
56
- }
57
- }
58
-
59
- // Merge the loaded user native config with the programmatically passed kitConfig.
60
- // Programmatic config can override or augment the native file's settings.
61
- if (userNativeConfig && programmaticKitConfig) {
62
- const mergedConfig = { ...userNativeConfig, ...programmaticKitConfig };
63
- return mergedConfig;
64
- } else if (userNativeConfig) {
65
- return userNativeConfig;
66
- } else if (programmaticKitConfig) {
67
- return programmaticKitConfig;
68
- }
69
-
70
- logger.debug(
71
- 'resolveAndInitializeKitConfig',
72
- `No native or programmatic kitConfig provided for ${kitName || 'none'}. Returning null.`
73
- );
74
- return null;
75
- }
@@ -1,117 +0,0 @@
1
- import { beforeEach, describe, expect, it, vi } from 'vitest';
2
-
3
- import { TypedEvmNetworkConfig } from '../../types';
4
- import { shouldUseV2Api, testEtherscanV2Connection } from '../etherscan-v2';
5
-
6
- // Mock the dependencies
7
- vi.mock('../../configuration/explorer', () => ({
8
- resolveExplorerConfig: vi.fn(),
9
- }));
10
-
11
- vi.mock('@openzeppelin/ui-utils', () => ({
12
- logger: {
13
- info: vi.fn(),
14
- error: vi.fn(),
15
- warn: vi.fn(),
16
- },
17
- }));
18
-
19
- // Mock global fetch
20
- global.fetch = vi.fn();
21
-
22
- describe('Etherscan V2 API', () => {
23
- const mockNetworkConfig: TypedEvmNetworkConfig = {
24
- id: 'ethereum-mainnet',
25
- name: 'Ethereum Mainnet',
26
- ecosystem: 'evm',
27
- network: 'ethereum',
28
- type: 'mainnet',
29
- isTestnet: false,
30
- chainId: 1,
31
- rpcUrl: 'https://mainnet.infura.io/v3/test',
32
- explorerUrl: 'https://etherscan.io',
33
- supportsEtherscanV2: true,
34
- exportConstName: 'ethereumMainnet',
35
- nativeCurrency: {
36
- name: 'Ether',
37
- symbol: 'ETH',
38
- decimals: 18,
39
- },
40
- };
41
-
42
- beforeEach(() => {
43
- vi.clearAllMocks();
44
- });
45
-
46
- describe('shouldUseV2Api', () => {
47
- it('should return true if network supports V2 and no user config', () => {
48
- expect(shouldUseV2Api(mockNetworkConfig)).toBe(true);
49
- });
50
-
51
- it('should return true for networks that support V2 API', () => {
52
- expect(shouldUseV2Api(mockNetworkConfig)).toBe(true);
53
- });
54
-
55
- it('should return false for networks without V2 support even with user preference', () => {
56
- const networkWithoutV2 = { ...mockNetworkConfig, supportsEtherscanV2: false };
57
- expect(shouldUseV2Api(networkWithoutV2)).toBe(false);
58
- });
59
- });
60
-
61
- describe('testEtherscanV2Connection', () => {
62
- it('should allow testing connection without API key when network does not require it', async () => {
63
- const networkWithoutKeyRequired: TypedEvmNetworkConfig = {
64
- ...mockNetworkConfig,
65
- requiresExplorerApiKey: false,
66
- };
67
-
68
- const mockResponse = {
69
- status: '1',
70
- result: '0x1234567',
71
- };
72
-
73
- vi.mocked(global.fetch).mockResolvedValueOnce({
74
- ok: true,
75
- json: vi.fn().mockResolvedValueOnce(mockResponse),
76
- } as unknown as Response);
77
-
78
- const result = await testEtherscanV2Connection(networkWithoutKeyRequired);
79
-
80
- expect(result.success).toBe(true);
81
- expect(result.latency).toBeDefined();
82
- expect(result.error).toBeUndefined();
83
-
84
- // Verify API call was made without API key
85
- const fetchCall = vi.mocked(global.fetch).mock.calls[0][0] as string;
86
- expect(fetchCall).not.toContain('apikey=');
87
- });
88
-
89
- it('should require API key for testing when network requires it', async () => {
90
- const result = await testEtherscanV2Connection(mockNetworkConfig);
91
-
92
- expect(result.success).toBe(false);
93
- expect(result.error).toBe('API key is required for testing connection to this explorer');
94
- });
95
-
96
- it('should test with API key when provided', async () => {
97
- const mockResponse = {
98
- status: '1',
99
- result: '0x1234567',
100
- };
101
-
102
- vi.mocked(global.fetch).mockResolvedValueOnce({
103
- ok: true,
104
- json: vi.fn().mockResolvedValueOnce(mockResponse),
105
- } as unknown as Response);
106
-
107
- const result = await testEtherscanV2Connection(mockNetworkConfig, 'test-api-key');
108
-
109
- expect(result.success).toBe(true);
110
- expect(result.latency).toBeDefined();
111
-
112
- // Verify API call was made with API key
113
- const fetchCall = vi.mocked(global.fetch).mock.calls[0][0] as string;
114
- expect(fetchCall).toContain('apikey=test-api-key');
115
- });
116
- });
117
- });