@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.
- package/README.md +33 -18
- package/dist/index.cjs +4651 -4448
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +12 -226
- package/dist/index.d.ts +12 -226
- package/dist/index.js +4737 -4535
- package/dist/index.js.map +1 -1
- package/package.json +7 -6
- package/src/__tests__/adapter-parsing.test.ts +4 -3
- package/src/__tests__/getDefaultServiceConfig.test.ts +185 -0
- package/src/__tests__/mocks/mock-network-configs.ts +1 -1
- package/src/__tests__/provenanceLinks.test.ts +6 -4
- package/src/__tests__/providerSelection.test.ts +5 -4
- package/src/__tests__/timeouts.test.ts +5 -3
- package/src/__tests__/wallet-connect.test.ts +2 -2
- package/src/adapter.ts +61 -107
- package/src/configuration/execution.ts +1 -52
- package/src/configuration/index.ts +2 -3
- package/src/configuration/network-services.ts +47 -60
- package/src/index.ts +22 -13
- package/src/networks/index.ts +2 -1
- package/src/networks/mainnet.ts +1 -1
- package/src/networks/testnet.ts +1 -1
- package/src/query/adapter-query.ts +72 -0
- package/src/query/index.ts +2 -2
- package/src/transaction/components/useEvmRelayerOptions.ts +5 -3
- package/src/transaction/index.ts +1 -5
- package/src/types/artifacts.ts +5 -30
- package/src/types/providers.ts +7 -18
- package/src/wallet/components/EvmWalletUiRoot.tsx +1 -1
- package/src/wallet/evmUiKitManager.ts +26 -129
- package/src/wallet/hooks/index.ts +0 -1
- package/src/wallet/implementation/wagmi-implementation.ts +45 -577
- package/src/wallet/index.ts +2 -3
- package/src/wallet/rainbowkit/__tests__/export-service.test.ts +1 -2
- package/src/wallet/rainbowkit/componentFactory.ts +10 -8
- package/src/wallet/rainbowkit/components.tsx +16 -133
- package/src/wallet/rainbowkit/index.ts +27 -5
- package/src/wallet/utils/__tests__/uiKitService.test.ts +5 -1
- package/src/wallet/utils/connection.ts +8 -52
- package/src/wallet/utils/index.ts +0 -2
- package/src/wallet/utils/uiKitService.ts +7 -3
- package/src/wallet/utils/walletImplementationManager.ts +5 -4
- package/src/wallet/utils.ts +1 -65
- package/src/abi/__tests__/etherscan-v2.test.ts +0 -117
- package/src/abi/__tests__/transformer.test.ts +0 -342
- package/src/abi/comparison.ts +0 -389
- package/src/abi/etherscan-v2.ts +0 -243
- package/src/abi/etherscan.ts +0 -158
- package/src/abi/index.ts +0 -7
- package/src/abi/loader.ts +0 -415
- package/src/abi/sourcify.ts +0 -75
- package/src/abi/transformer.ts +0 -163
- package/src/abi/types.ts +0 -101
- package/src/configuration/__tests__/explorer.test.ts +0 -174
- package/src/configuration/__tests__/rpc.test.ts +0 -176
- package/src/configuration/explorer.ts +0 -243
- package/src/configuration/rpc.ts +0 -257
- package/src/mapping/__tests__/field-generator.test.ts +0 -137
- package/src/mapping/__tests__/type-mapper.test.ts +0 -139
- package/src/mapping/constants.ts +0 -57
- package/src/mapping/field-generator.ts +0 -115
- package/src/mapping/index.ts +0 -4
- package/src/mapping/type-mapper.ts +0 -80
- package/src/proxy/detection.ts +0 -465
- package/src/query/handler.ts +0 -227
- package/src/query/view-checker.ts +0 -10
- package/src/transaction/eoa.ts +0 -98
- package/src/transaction/execution-strategy.ts +0 -33
- package/src/transaction/formatter.ts +0 -101
- package/src/transaction/relayer.ts +0 -380
- package/src/transaction/sender.ts +0 -185
- package/src/transform/index.ts +0 -3
- package/src/transform/input-parser.ts +0 -177
- package/src/transform/output-formatter.ts +0 -64
- package/src/types/__tests__/artifacts.test.ts +0 -105
- package/src/types.ts +0 -92
- package/src/utils/__tests__/artifacts.test.ts +0 -81
- package/src/utils/artifacts.ts +0 -30
- package/src/utils/formatting.ts +0 -25
- package/src/utils/gas.ts +0 -17
- package/src/utils/index.ts +0 -6
- package/src/utils/json.ts +0 -19
- package/src/utils/validation.ts +0 -10
- package/src/validation/eoa.ts +0 -33
- package/src/validation/index.ts +0 -2
- package/src/validation/relayer.ts +0 -13
- package/src/wallet/__tests__/utils.test.ts +0 -149
- package/src/wallet/components/account/AccountDisplay.tsx +0 -52
- package/src/wallet/components/connect/ConnectButton.tsx +0 -125
- package/src/wallet/components/connect/ConnectorDialog.tsx +0 -140
- package/src/wallet/components/index.ts +0 -4
- package/src/wallet/components/network/NetworkSwitcher.tsx +0 -90
- package/src/wallet/context/index.ts +0 -1
- package/src/wallet/context/wagmi-context.tsx +0 -7
- package/src/wallet/hooks/useIsWagmiProviderInitialized.ts +0 -11
- package/src/wallet/rainbowkit/config-generator.ts +0 -56
- package/src/wallet/rainbowkit/config-service.ts +0 -169
- package/src/wallet/rainbowkit/export-service.ts +0 -18
- package/src/wallet/rainbowkit/rainbowkitAssetManager.ts +0 -74
- package/src/wallet/rainbowkit/types.ts +0 -74
- package/src/wallet/rainbowkit/utils.ts +0 -96
- package/src/wallet/services/configResolutionService.ts +0 -65
- package/src/wallet/utils/SafeWagmiComponent.tsx +0 -72
- package/src/wallet/utils/filterWalletComponents.ts +0 -89
|
@@ -1,243 +0,0 @@
|
|
|
1
|
-
import { NetworkConfig, UserExplorerConfig } from '@openzeppelin/ui-types';
|
|
2
|
-
import { appConfigService, logger, userNetworkServiceConfigService } from '@openzeppelin/ui-utils';
|
|
3
|
-
|
|
4
|
-
import { shouldUseV2Api, testEtherscanV2Connection } from '../abi/etherscan-v2';
|
|
5
|
-
import { TypedEvmNetworkConfig } from '../types';
|
|
6
|
-
import { isValidEvmAddress } from '../utils';
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Resolves the explorer configuration for a given EVM network.
|
|
10
|
-
* Priority order:
|
|
11
|
-
* 1. User-configured explorer (from UserExplorerConfigService)
|
|
12
|
-
* 2. For V2 API networks: Global Etherscan V2 API key (from AppConfigService global service configs)
|
|
13
|
-
* 3. App-configured explorer API key (from AppConfigService network service configs)
|
|
14
|
-
* 4. Default network explorer (from NetworkConfig)
|
|
15
|
-
*
|
|
16
|
-
* @param networkConfig - The EVM network configuration.
|
|
17
|
-
* @returns The resolved explorer configuration.
|
|
18
|
-
*/
|
|
19
|
-
export function resolveExplorerConfig(networkConfig: TypedEvmNetworkConfig): UserExplorerConfig {
|
|
20
|
-
// Precompute app-level keys and defaults for merging
|
|
21
|
-
const isV2 =
|
|
22
|
-
networkConfig.supportsEtherscanV2 &&
|
|
23
|
-
networkConfig.primaryExplorerApiIdentifier === 'etherscan-v2';
|
|
24
|
-
const globalV2ApiKey = isV2
|
|
25
|
-
? (appConfigService.getGlobalServiceConfig('etherscanv2')?.apiKey as string | undefined)
|
|
26
|
-
: undefined;
|
|
27
|
-
const appApiKey = networkConfig.primaryExplorerApiIdentifier
|
|
28
|
-
? appConfigService.getExplorerApiKey(networkConfig.primaryExplorerApiIdentifier)
|
|
29
|
-
: undefined;
|
|
30
|
-
|
|
31
|
-
// 1. Check for user-configured explorer via new generic service
|
|
32
|
-
const rawCfg = userNetworkServiceConfigService.get(networkConfig.id, 'explorer');
|
|
33
|
-
if (rawCfg && typeof rawCfg === 'object') {
|
|
34
|
-
const userCfg = rawCfg as Record<string, unknown>;
|
|
35
|
-
logger.info('ExplorerConfig', `Using user-configured explorer for ${networkConfig.name}`);
|
|
36
|
-
return {
|
|
37
|
-
explorerUrl: (userCfg.explorerUrl as string | undefined) ?? networkConfig.explorerUrl,
|
|
38
|
-
apiUrl: (userCfg.apiUrl as string | undefined) ?? networkConfig.apiUrl,
|
|
39
|
-
apiKey: (userCfg.apiKey as string | undefined) ?? globalV2ApiKey ?? appApiKey,
|
|
40
|
-
name: `${networkConfig.name} Explorer`,
|
|
41
|
-
isCustom: true,
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// 2. For V2 API networks using 'etherscan-v2' identifier, check for global Etherscan V2 API key
|
|
46
|
-
if (isV2 && globalV2ApiKey) {
|
|
47
|
-
logger.info('ExplorerConfig', `Using global Etherscan V2 API key for ${networkConfig.name}`);
|
|
48
|
-
return {
|
|
49
|
-
explorerUrl: networkConfig.explorerUrl,
|
|
50
|
-
apiUrl: networkConfig.apiUrl,
|
|
51
|
-
apiKey: globalV2ApiKey,
|
|
52
|
-
name: `${networkConfig.name} Explorer (V2 API)`,
|
|
53
|
-
isCustom: false,
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// 3. Check for app-configured API key (V1 style or other identifiers)
|
|
58
|
-
if (appApiKey) {
|
|
59
|
-
logger.info('ExplorerConfig', `Using app-configured API key for ${networkConfig.name}`);
|
|
60
|
-
return {
|
|
61
|
-
explorerUrl: networkConfig.explorerUrl,
|
|
62
|
-
apiUrl: networkConfig.apiUrl,
|
|
63
|
-
apiKey: appApiKey,
|
|
64
|
-
name: `${networkConfig.name} Explorer`,
|
|
65
|
-
isCustom: false,
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// 4. Use default network explorer (no API key)
|
|
70
|
-
logger.info(
|
|
71
|
-
'ExplorerConfig',
|
|
72
|
-
`Using default explorer for ${networkConfig.name} (no API key configured)`
|
|
73
|
-
);
|
|
74
|
-
return {
|
|
75
|
-
explorerUrl: networkConfig.explorerUrl,
|
|
76
|
-
apiUrl: networkConfig.apiUrl,
|
|
77
|
-
name: `${networkConfig.name} Explorer`,
|
|
78
|
-
isCustom: false,
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Gets a blockchain explorer URL for an EVM address.
|
|
84
|
-
* Uses the resolved explorer configuration.
|
|
85
|
-
*/
|
|
86
|
-
export function getEvmExplorerAddressUrl(
|
|
87
|
-
address: string,
|
|
88
|
-
networkConfig: NetworkConfig
|
|
89
|
-
): string | null {
|
|
90
|
-
if (!isValidEvmAddress(address)) {
|
|
91
|
-
return null;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
const explorerConfig = resolveExplorerConfig(networkConfig as TypedEvmNetworkConfig);
|
|
95
|
-
if (!explorerConfig.explorerUrl) {
|
|
96
|
-
return null;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// Construct the URL using the explorerUrl from the config
|
|
100
|
-
const baseUrl = explorerConfig.explorerUrl.replace(/\/+$/, '');
|
|
101
|
-
return `${baseUrl}/address/${address}`;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Gets a blockchain explorer URL for an EVM transaction.
|
|
106
|
-
* Uses the resolved explorer configuration.
|
|
107
|
-
*/
|
|
108
|
-
export function getEvmExplorerTxUrl(txHash: string, networkConfig: NetworkConfig): string | null {
|
|
109
|
-
if (!txHash) {
|
|
110
|
-
return null;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
const explorerConfig = resolveExplorerConfig(networkConfig as TypedEvmNetworkConfig);
|
|
114
|
-
if (!explorerConfig.explorerUrl) {
|
|
115
|
-
return null;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// Construct the URL using the explorerUrl from the config
|
|
119
|
-
const baseUrl = explorerConfig.explorerUrl.replace(/\/+$/, '');
|
|
120
|
-
return `${baseUrl}/tx/${txHash}`;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* Validates an EVM explorer configuration.
|
|
125
|
-
* Checks URL formats and API key format.
|
|
126
|
-
*/
|
|
127
|
-
export function validateEvmExplorerConfig(explorerConfig: UserExplorerConfig): boolean {
|
|
128
|
-
// Validate URLs if provided
|
|
129
|
-
if (explorerConfig.explorerUrl) {
|
|
130
|
-
try {
|
|
131
|
-
new URL(explorerConfig.explorerUrl);
|
|
132
|
-
} catch {
|
|
133
|
-
return false;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
if (explorerConfig.apiUrl) {
|
|
138
|
-
try {
|
|
139
|
-
new URL(explorerConfig.apiUrl);
|
|
140
|
-
} catch {
|
|
141
|
-
return false;
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
// Basic API key validation (not empty)
|
|
146
|
-
if (explorerConfig.apiKey !== undefined && explorerConfig.apiKey.trim().length === 0) {
|
|
147
|
-
return false;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
return true;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
* Tests the connection to an EVM explorer API.
|
|
155
|
-
* Makes a test API call to verify the API key works.
|
|
156
|
-
*/
|
|
157
|
-
export async function testEvmExplorerConnection(
|
|
158
|
-
explorerConfig: UserExplorerConfig,
|
|
159
|
-
networkConfig?: TypedEvmNetworkConfig
|
|
160
|
-
): Promise<{
|
|
161
|
-
success: boolean;
|
|
162
|
-
latency?: number;
|
|
163
|
-
error?: string;
|
|
164
|
-
}> {
|
|
165
|
-
// Check if API key is required for this network
|
|
166
|
-
const requiresApiKey =
|
|
167
|
-
networkConfig && 'requiresExplorerApiKey' in networkConfig
|
|
168
|
-
? networkConfig.requiresExplorerApiKey !== false
|
|
169
|
-
: true;
|
|
170
|
-
|
|
171
|
-
if (requiresApiKey && !explorerConfig.apiKey) {
|
|
172
|
-
return {
|
|
173
|
-
success: false,
|
|
174
|
-
error: 'API key is required for testing connection to this explorer',
|
|
175
|
-
};
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
// Check if we should use V2 API
|
|
179
|
-
if (networkConfig && shouldUseV2Api(networkConfig)) {
|
|
180
|
-
return testEtherscanV2Connection(networkConfig, explorerConfig.apiKey);
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
// Use V1 API (legacy)
|
|
184
|
-
// Use provided API URL or fall back to network config if available
|
|
185
|
-
let apiUrl = explorerConfig.apiUrl;
|
|
186
|
-
if (!apiUrl && networkConfig?.apiUrl) {
|
|
187
|
-
apiUrl = networkConfig.apiUrl;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
if (!apiUrl) {
|
|
191
|
-
return {
|
|
192
|
-
success: false,
|
|
193
|
-
error:
|
|
194
|
-
'API URL is required for testing connection. Please provide an API URL or ensure the network has a default API URL configured.',
|
|
195
|
-
};
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
const startTime = Date.now();
|
|
199
|
-
|
|
200
|
-
try {
|
|
201
|
-
// Test with a simple API call - get the latest block number
|
|
202
|
-
const url = new URL(apiUrl);
|
|
203
|
-
url.searchParams.append('module', 'proxy');
|
|
204
|
-
url.searchParams.append('action', 'eth_blockNumber');
|
|
205
|
-
if (explorerConfig.apiKey) {
|
|
206
|
-
url.searchParams.append('apikey', explorerConfig.apiKey);
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
const response = await fetch(url.toString());
|
|
210
|
-
const latency = Date.now() - startTime;
|
|
211
|
-
|
|
212
|
-
if (!response.ok) {
|
|
213
|
-
return {
|
|
214
|
-
success: false,
|
|
215
|
-
error: `HTTP ${response.status}: ${response.statusText}`,
|
|
216
|
-
latency,
|
|
217
|
-
};
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
const data = await response.json();
|
|
221
|
-
|
|
222
|
-
// Check for API errors in the response
|
|
223
|
-
if (data.status === '0' && data.message) {
|
|
224
|
-
return {
|
|
225
|
-
success: false,
|
|
226
|
-
error: data.message,
|
|
227
|
-
latency,
|
|
228
|
-
};
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
// Success if we got a valid response
|
|
232
|
-
return {
|
|
233
|
-
success: true,
|
|
234
|
-
latency,
|
|
235
|
-
};
|
|
236
|
-
} catch (error) {
|
|
237
|
-
return {
|
|
238
|
-
success: false,
|
|
239
|
-
error: error instanceof Error ? error.message : 'Connection test failed',
|
|
240
|
-
latency: Date.now() - startTime,
|
|
241
|
-
};
|
|
242
|
-
}
|
|
243
|
-
}
|
package/src/configuration/rpc.ts
DELETED
|
@@ -1,257 +0,0 @@
|
|
|
1
|
-
import type { EvmNetworkConfig, UserRpcProviderConfig } from '@openzeppelin/ui-types';
|
|
2
|
-
import {
|
|
3
|
-
appConfigService,
|
|
4
|
-
isValidUrl,
|
|
5
|
-
logger,
|
|
6
|
-
userNetworkServiceConfigService,
|
|
7
|
-
} from '@openzeppelin/ui-utils';
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Builds a complete RPC URL from a user RPC provider configuration.
|
|
11
|
-
* For simplified RPC configuration, this just returns the URL as-is since
|
|
12
|
-
* users are now providing complete RPC URLs including any API keys.
|
|
13
|
-
*
|
|
14
|
-
* @param config The user RPC provider configuration
|
|
15
|
-
* @returns The RPC URL
|
|
16
|
-
*/
|
|
17
|
-
export function buildRpcUrl(config: UserRpcProviderConfig): string {
|
|
18
|
-
return config.url;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Extracts the user-configured RPC URL from UserNetworkServiceConfigService.
|
|
23
|
-
*
|
|
24
|
-
* @param networkId - The network ID to get the RPC URL for
|
|
25
|
-
* @returns The RPC URL string if configured, undefined otherwise
|
|
26
|
-
*/
|
|
27
|
-
export function getUserRpcUrl(networkId: string): string | undefined {
|
|
28
|
-
const svcCfg = userNetworkServiceConfigService.get(networkId, 'rpc');
|
|
29
|
-
if (svcCfg && typeof svcCfg === 'object' && 'rpcUrl' in svcCfg) {
|
|
30
|
-
return (svcCfg as Record<string, unknown>).rpcUrl as string;
|
|
31
|
-
}
|
|
32
|
-
return undefined;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Resolves the RPC URL for a given EVM network configuration.
|
|
37
|
-
* Priority order:
|
|
38
|
-
* 1. User-provided RPC configuration (from UserRpcConfigService)
|
|
39
|
-
* 2. RPC URL override from AppConfigService
|
|
40
|
-
* 3. Default rpcUrl from the network configuration
|
|
41
|
-
*
|
|
42
|
-
* @param networkConfig - The EVM network configuration.
|
|
43
|
-
* @returns The resolved RPC URL string.
|
|
44
|
-
* @throws If no RPC URL can be resolved (neither user config, override, nor default is present and valid).
|
|
45
|
-
*/
|
|
46
|
-
export function resolveRpcUrl(networkConfig: EvmNetworkConfig): string {
|
|
47
|
-
const logSystem = 'RpcResolver';
|
|
48
|
-
const networkId = networkConfig.id;
|
|
49
|
-
|
|
50
|
-
// First priority: Check adapter-led service config (generic)
|
|
51
|
-
const userRpcUrl = getUserRpcUrl(networkId);
|
|
52
|
-
if (userRpcUrl) {
|
|
53
|
-
const userRpcUrlString = String(userRpcUrl);
|
|
54
|
-
if (isValidUrl(userRpcUrlString)) {
|
|
55
|
-
logger.info(logSystem, `Using user-configured RPC URL for network ${networkId}`);
|
|
56
|
-
return userRpcUrlString;
|
|
57
|
-
} else {
|
|
58
|
-
logger.warn(
|
|
59
|
-
logSystem,
|
|
60
|
-
`User-configured RPC URL for ${networkId} is invalid: ${userRpcUrlString}. Falling back.`
|
|
61
|
-
);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// Second priority: Check AppConfigService for an override
|
|
66
|
-
const rpcOverrideSetting = appConfigService.getRpcEndpointOverride(networkId);
|
|
67
|
-
let rpcUrlFromOverride: string | undefined;
|
|
68
|
-
|
|
69
|
-
if (typeof rpcOverrideSetting === 'string') {
|
|
70
|
-
rpcUrlFromOverride = rpcOverrideSetting;
|
|
71
|
-
} else if (typeof rpcOverrideSetting === 'object' && rpcOverrideSetting) {
|
|
72
|
-
// Check if it's a UserRpcProviderConfig
|
|
73
|
-
if ('url' in rpcOverrideSetting && 'isCustom' in rpcOverrideSetting) {
|
|
74
|
-
const userConfig = rpcOverrideSetting as UserRpcProviderConfig;
|
|
75
|
-
rpcUrlFromOverride = buildRpcUrl(userConfig);
|
|
76
|
-
} else if ('http' in rpcOverrideSetting) {
|
|
77
|
-
// It's an RpcEndpointConfig
|
|
78
|
-
rpcUrlFromOverride = rpcOverrideSetting.http;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
if (rpcUrlFromOverride) {
|
|
83
|
-
logger.info(
|
|
84
|
-
logSystem,
|
|
85
|
-
`Using overridden RPC URL for network ${networkId}: ${rpcUrlFromOverride}`
|
|
86
|
-
);
|
|
87
|
-
if (isValidUrl(rpcUrlFromOverride)) {
|
|
88
|
-
return rpcUrlFromOverride;
|
|
89
|
-
} else {
|
|
90
|
-
logger.warn(
|
|
91
|
-
logSystem,
|
|
92
|
-
`Overridden RPC URL for ${networkId} is invalid: ${rpcUrlFromOverride}. Falling back.`
|
|
93
|
-
);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// Third priority: Fallback to the rpcUrl in the networkConfig
|
|
98
|
-
if (networkConfig.rpcUrl && isValidUrl(networkConfig.rpcUrl)) {
|
|
99
|
-
logger.debug(
|
|
100
|
-
logSystem,
|
|
101
|
-
`Using default RPC URL for network ${networkId}: ${networkConfig.rpcUrl}`
|
|
102
|
-
);
|
|
103
|
-
return networkConfig.rpcUrl;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
logger.error(
|
|
107
|
-
logSystem,
|
|
108
|
-
`No valid RPC URL could be resolved for network ${networkId}. Checked user config, override, and networkConfig.rpcUrl.`
|
|
109
|
-
);
|
|
110
|
-
throw new Error(
|
|
111
|
-
`No valid RPC URL configured for network ${networkConfig.name} (ID: ${networkId}).`
|
|
112
|
-
);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Validates an RPC endpoint configuration for EVM networks.
|
|
117
|
-
* @param rpcConfig - The RPC provider configuration to validate
|
|
118
|
-
* @returns True if the configuration is valid, false otherwise
|
|
119
|
-
*/
|
|
120
|
-
export function validateEvmRpcEndpoint(rpcConfig: UserRpcProviderConfig): boolean {
|
|
121
|
-
try {
|
|
122
|
-
// Check if it's a valid URL (our validator already ensures HTTP/HTTPS)
|
|
123
|
-
if (!isValidUrl(rpcConfig.url)) {
|
|
124
|
-
logger.error('validateEvmRpcEndpoint', `Invalid RPC URL format: ${rpcConfig.url}`);
|
|
125
|
-
return false;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// Additional EVM-specific validation could be added here
|
|
129
|
-
// For example, checking if the URL follows known provider patterns
|
|
130
|
-
|
|
131
|
-
return true;
|
|
132
|
-
} catch (error) {
|
|
133
|
-
logger.error('validateEvmRpcEndpoint', 'Error validating RPC endpoint:', error);
|
|
134
|
-
return false;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* Tests the connection to an EVM RPC endpoint with a timeout.
|
|
140
|
-
* @param rpcConfig - The RPC provider configuration to test
|
|
141
|
-
* @param timeoutMs - Timeout in milliseconds (default: 5000ms)
|
|
142
|
-
* @returns Connection test results including success status, latency, and any errors
|
|
143
|
-
*/
|
|
144
|
-
export async function testEvmRpcConnection(
|
|
145
|
-
rpcConfig: UserRpcProviderConfig,
|
|
146
|
-
timeoutMs: number = 5000
|
|
147
|
-
): Promise<{
|
|
148
|
-
success: boolean;
|
|
149
|
-
latency?: number;
|
|
150
|
-
error?: string;
|
|
151
|
-
}> {
|
|
152
|
-
if (!rpcConfig.url) {
|
|
153
|
-
return { success: false, error: 'RPC URL is required' };
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// Create an AbortController for timeout
|
|
157
|
-
const controller = new AbortController();
|
|
158
|
-
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
159
|
-
|
|
160
|
-
try {
|
|
161
|
-
const startTime = Date.now();
|
|
162
|
-
|
|
163
|
-
// Use fetch to make a JSON-RPC call to test the connection
|
|
164
|
-
const response = await fetch(rpcConfig.url, {
|
|
165
|
-
method: 'POST',
|
|
166
|
-
headers: {
|
|
167
|
-
'Content-Type': 'application/json',
|
|
168
|
-
},
|
|
169
|
-
body: JSON.stringify({
|
|
170
|
-
jsonrpc: '2.0',
|
|
171
|
-
method: 'eth_blockNumber',
|
|
172
|
-
params: [],
|
|
173
|
-
id: 1,
|
|
174
|
-
}),
|
|
175
|
-
signal: controller.signal,
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
if (!response.ok) {
|
|
179
|
-
return { success: false, error: `HTTP error: ${response.status}` };
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
const data = await response.json();
|
|
183
|
-
const latency = Date.now() - startTime;
|
|
184
|
-
|
|
185
|
-
if (data.error) {
|
|
186
|
-
return { success: false, error: data.error.message || 'RPC error' };
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
return { success: true, latency };
|
|
190
|
-
} catch (error) {
|
|
191
|
-
logger.error('testEvmRpcConnection', 'Connection test failed:', error);
|
|
192
|
-
|
|
193
|
-
// Check if the error was due to timeout
|
|
194
|
-
if (error instanceof Error && error.name === 'AbortError') {
|
|
195
|
-
return {
|
|
196
|
-
success: false,
|
|
197
|
-
error: `Connection timeout after ${timeoutMs}ms`,
|
|
198
|
-
};
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
return {
|
|
202
|
-
success: false,
|
|
203
|
-
error: error instanceof Error ? error.message : 'Connection failed',
|
|
204
|
-
};
|
|
205
|
-
} finally {
|
|
206
|
-
// Clear the timeout
|
|
207
|
-
clearTimeout(timeoutId);
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
/**
|
|
212
|
-
* Gets the current block number from an EVM network.
|
|
213
|
-
*
|
|
214
|
-
* @param networkConfig - The EVM network configuration
|
|
215
|
-
* @returns Promise resolving to the current block number
|
|
216
|
-
* @throws Error if the RPC call fails
|
|
217
|
-
*/
|
|
218
|
-
export async function getEvmCurrentBlock(networkConfig: EvmNetworkConfig): Promise<number> {
|
|
219
|
-
const rpcUrl = resolveRpcUrl(networkConfig);
|
|
220
|
-
|
|
221
|
-
try {
|
|
222
|
-
const response = await fetch(rpcUrl, {
|
|
223
|
-
method: 'POST',
|
|
224
|
-
headers: { 'Content-Type': 'application/json' },
|
|
225
|
-
body: JSON.stringify({
|
|
226
|
-
jsonrpc: '2.0',
|
|
227
|
-
method: 'eth_blockNumber',
|
|
228
|
-
params: [],
|
|
229
|
-
id: 1,
|
|
230
|
-
}),
|
|
231
|
-
});
|
|
232
|
-
|
|
233
|
-
if (!response.ok) {
|
|
234
|
-
throw new Error(`RPC request failed with status ${response.status}`);
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
const data = await response.json();
|
|
238
|
-
if (data.error) {
|
|
239
|
-
throw new Error(data.error.message || 'RPC error');
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
// eth_blockNumber returns a hex string
|
|
243
|
-
if (data.result === undefined || data.result === null) {
|
|
244
|
-
throw new Error('RPC response missing result field');
|
|
245
|
-
}
|
|
246
|
-
const blockNumber = parseInt(data.result, 16);
|
|
247
|
-
if (isNaN(blockNumber)) {
|
|
248
|
-
throw new Error(`Invalid block number returned: ${data.result}`);
|
|
249
|
-
}
|
|
250
|
-
return blockNumber;
|
|
251
|
-
} catch (error) {
|
|
252
|
-
logger.error('getEvmCurrentBlock', 'Failed to get current block:', error);
|
|
253
|
-
throw new Error(
|
|
254
|
-
`Failed to get current block: ${error instanceof Error ? error.message : String(error)}`
|
|
255
|
-
);
|
|
256
|
-
}
|
|
257
|
-
}
|
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
|
|
3
|
-
import type { FunctionParameter } from '@openzeppelin/ui-types';
|
|
4
|
-
|
|
5
|
-
import { generateEvmDefaultField } from '../field-generator';
|
|
6
|
-
|
|
7
|
-
// Helper to create a mock function parameter
|
|
8
|
-
const createParam = (type: string, name: string, description?: string): FunctionParameter => ({
|
|
9
|
-
name,
|
|
10
|
-
type,
|
|
11
|
-
displayName: name,
|
|
12
|
-
description,
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
describe('EVM Field Generator', () => {
|
|
16
|
-
describe('generateEvmDefaultField', () => {
|
|
17
|
-
it('should generate a number field for small integer types', () => {
|
|
18
|
-
const param = createParam('uint32', 'count');
|
|
19
|
-
const field = generateEvmDefaultField(param);
|
|
20
|
-
|
|
21
|
-
expect(field.type).toBe('number');
|
|
22
|
-
expect(field.validation.required).toBe(true);
|
|
23
|
-
expect(field.validation.min).toBe(0);
|
|
24
|
-
expect(field.validation.max).toBe(4_294_967_295);
|
|
25
|
-
expect(field.validation.pattern).toBeUndefined();
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
it('should apply numeric bounds for uint8', () => {
|
|
29
|
-
const param = createParam('uint8', 'byte');
|
|
30
|
-
const field = generateEvmDefaultField(param);
|
|
31
|
-
|
|
32
|
-
expect(field.validation.min).toBe(0);
|
|
33
|
-
expect(field.validation.max).toBe(255);
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it('should apply numeric bounds for int8', () => {
|
|
37
|
-
const param = createParam('int8', 'signedByte');
|
|
38
|
-
const field = generateEvmDefaultField(param);
|
|
39
|
-
|
|
40
|
-
expect(field.validation.min).toBe(-128);
|
|
41
|
-
expect(field.validation.max).toBe(127);
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it('should apply numeric bounds for int32', () => {
|
|
45
|
-
const param = createParam('int32', 'signedInt');
|
|
46
|
-
const field = generateEvmDefaultField(param);
|
|
47
|
-
|
|
48
|
-
expect(field.validation.min).toBe(-2_147_483_648);
|
|
49
|
-
expect(field.validation.max).toBe(2_147_483_647);
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it('should generate a bigint field for uint128', () => {
|
|
53
|
-
const param = createParam('uint128', 'amount');
|
|
54
|
-
const field = generateEvmDefaultField(param);
|
|
55
|
-
|
|
56
|
-
expect(field.type).toBe('bigint');
|
|
57
|
-
expect(field.validation.required).toBe(true);
|
|
58
|
-
// BigIntField component handles its own validation and UI guidance
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it('should generate a bigint field for uint256', () => {
|
|
62
|
-
const param = createParam('uint256', 'value');
|
|
63
|
-
const field = generateEvmDefaultField(param);
|
|
64
|
-
|
|
65
|
-
expect(field.type).toBe('bigint');
|
|
66
|
-
expect(field.validation.required).toBe(true);
|
|
67
|
-
// BigIntField component handles its own validation and UI guidance
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
it('should generate a bigint field for int128', () => {
|
|
71
|
-
const param = createParam('int128', 'delta');
|
|
72
|
-
const field = generateEvmDefaultField(param);
|
|
73
|
-
|
|
74
|
-
expect(field.type).toBe('bigint');
|
|
75
|
-
expect(field.validation.required).toBe(true);
|
|
76
|
-
// BigIntField component handles its own validation and UI guidance
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
it('should generate a bigint field for int256', () => {
|
|
80
|
-
const param = createParam('int256', 'offset');
|
|
81
|
-
const field = generateEvmDefaultField(param);
|
|
82
|
-
|
|
83
|
-
expect(field.type).toBe('bigint');
|
|
84
|
-
expect(field.validation.required).toBe(true);
|
|
85
|
-
// BigIntField component handles its own validation and UI guidance
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
it('should preserve parameter description in helper text', () => {
|
|
89
|
-
const param = createParam('uint256', 'tokenId', 'The unique identifier of the NFT');
|
|
90
|
-
const field = generateEvmDefaultField(param);
|
|
91
|
-
|
|
92
|
-
expect(field.type).toBe('bigint');
|
|
93
|
-
expect(field.helperText).toBe('The unique identifier of the NFT');
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
it('should generate a blockchain-address field for address type', () => {
|
|
97
|
-
const param = createParam('address', 'recipient');
|
|
98
|
-
const field = generateEvmDefaultField(param);
|
|
99
|
-
|
|
100
|
-
expect(field.type).toBe('blockchain-address');
|
|
101
|
-
expect(field.validation.required).toBe(true);
|
|
102
|
-
expect(field.validation.pattern).toBeUndefined();
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
it('should generate array field with proper element config for uint256[]', () => {
|
|
106
|
-
const param = createParam('uint256[]', 'amounts');
|
|
107
|
-
const field = generateEvmDefaultField(param);
|
|
108
|
-
|
|
109
|
-
expect(field.type).toBe('array');
|
|
110
|
-
expect(field.elementFieldConfig).toBeDefined();
|
|
111
|
-
expect(field.elementFieldConfig?.type).toBe('bigint');
|
|
112
|
-
// Integer validation is handled by BigIntField component internally
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
it('should apply numeric bounds to array element fields', () => {
|
|
116
|
-
const param = createParam('uint32[]', 'counts');
|
|
117
|
-
const field = generateEvmDefaultField(param);
|
|
118
|
-
|
|
119
|
-
expect(field.type).toBe('array');
|
|
120
|
-
expect(field.elementFieldConfig).toBeDefined();
|
|
121
|
-
expect(field.elementFieldConfig!.validation).toBeDefined();
|
|
122
|
-
expect(field.elementFieldConfig!.validation!.min).toBe(0);
|
|
123
|
-
expect(field.elementFieldConfig!.validation!.max).toBe(4_294_967_295);
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
it('should include proper field metadata', () => {
|
|
127
|
-
const param = createParam('uint256', 'value');
|
|
128
|
-
const field = generateEvmDefaultField(param);
|
|
129
|
-
|
|
130
|
-
expect(field.id).toBeDefined();
|
|
131
|
-
expect(field.name).toBe('value');
|
|
132
|
-
expect(field.label).toBe('Value');
|
|
133
|
-
expect(field.placeholder).toContain('value');
|
|
134
|
-
expect(field.width).toBe('full');
|
|
135
|
-
});
|
|
136
|
-
});
|
|
137
|
-
});
|