@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,185 +0,0 @@
|
|
|
1
|
-
import type { GetAccountReturnType } from '@wagmi/core';
|
|
2
|
-
import type { TransactionReceipt, WalletClient } from 'viem';
|
|
3
|
-
|
|
4
|
-
import type { ExecutionConfig } from '@openzeppelin/ui-types';
|
|
5
|
-
import { logger } from '@openzeppelin/ui-utils';
|
|
6
|
-
|
|
7
|
-
import type { WriteContractParameters } from '../types';
|
|
8
|
-
import type { WagmiWalletImplementation } from '../wallet/implementation/wagmi-implementation';
|
|
9
|
-
|
|
10
|
-
const SYSTEM_LOG_TAG = 'adapter-evm-sender';
|
|
11
|
-
|
|
12
|
-
// --- Helper Functions ---
|
|
13
|
-
|
|
14
|
-
async function _ensureCorrectNetworkOrSwitch(
|
|
15
|
-
walletImplementation: WagmiWalletImplementation,
|
|
16
|
-
targetChainId: number
|
|
17
|
-
): Promise<GetAccountReturnType> {
|
|
18
|
-
const initialAccountStatus = walletImplementation.getWalletConnectionStatus();
|
|
19
|
-
if (!initialAccountStatus.isConnected || !initialAccountStatus.chainId) {
|
|
20
|
-
logger.error(
|
|
21
|
-
SYSTEM_LOG_TAG,
|
|
22
|
-
'Wallet not connected or chainId unavailable before network check.'
|
|
23
|
-
);
|
|
24
|
-
throw new Error('Wallet not connected or chain ID is unavailable.');
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
if (initialAccountStatus.chainId !== targetChainId) {
|
|
28
|
-
logger.info(
|
|
29
|
-
SYSTEM_LOG_TAG,
|
|
30
|
-
`Wallet on chain ${initialAccountStatus.chainId}, target ${targetChainId}. Switching...`
|
|
31
|
-
);
|
|
32
|
-
try {
|
|
33
|
-
await walletImplementation.switchNetwork(targetChainId);
|
|
34
|
-
const postSwitchAccountStatus = walletImplementation.getWalletConnectionStatus();
|
|
35
|
-
if (postSwitchAccountStatus.chainId !== targetChainId) {
|
|
36
|
-
logger.error(
|
|
37
|
-
SYSTEM_LOG_TAG,
|
|
38
|
-
`Failed to switch to target chain ${targetChainId}. Current: ${postSwitchAccountStatus.chainId}`
|
|
39
|
-
);
|
|
40
|
-
throw new Error(`Failed to switch to the required network (target: ${targetChainId}).`);
|
|
41
|
-
}
|
|
42
|
-
logger.info(SYSTEM_LOG_TAG, `Successfully switched to target chain ${targetChainId}.`);
|
|
43
|
-
return postSwitchAccountStatus;
|
|
44
|
-
} catch (error) {
|
|
45
|
-
logger.error(SYSTEM_LOG_TAG, 'Network switch failed:', error);
|
|
46
|
-
throw error;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
logger.info(SYSTEM_LOG_TAG, 'Wallet already on target chain.');
|
|
50
|
-
return initialAccountStatus;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
async function _getAuthenticatedWalletClient(
|
|
54
|
-
walletImplementation: WagmiWalletImplementation
|
|
55
|
-
): Promise<{
|
|
56
|
-
walletClient: WalletClient;
|
|
57
|
-
accountStatus: GetAccountReturnType;
|
|
58
|
-
}> {
|
|
59
|
-
const walletClient = await walletImplementation.getWalletClient();
|
|
60
|
-
if (!walletClient) {
|
|
61
|
-
logger.error(SYSTEM_LOG_TAG, 'Wallet client not available. Is wallet connected?');
|
|
62
|
-
throw new Error('Wallet is not connected or client is unavailable.');
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const accountStatus = walletImplementation.getWalletConnectionStatus();
|
|
66
|
-
if (!accountStatus.isConnected || !accountStatus.address) {
|
|
67
|
-
logger.error(SYSTEM_LOG_TAG, 'Account not available. Is wallet connected?');
|
|
68
|
-
throw new Error('Wallet is not connected or account address is unavailable.');
|
|
69
|
-
}
|
|
70
|
-
return { walletClient, accountStatus };
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
async function _executeEoaTransaction(
|
|
74
|
-
transactionData: WriteContractParameters,
|
|
75
|
-
walletClient: WalletClient,
|
|
76
|
-
accountStatus: GetAccountReturnType
|
|
77
|
-
): Promise<{ txHash: string }> {
|
|
78
|
-
logger.info(SYSTEM_LOG_TAG, 'Using EOA execution strategy.');
|
|
79
|
-
try {
|
|
80
|
-
logger.debug(SYSTEM_LOG_TAG, 'Calling walletClient.writeContract with:', {
|
|
81
|
-
account: accountStatus.address,
|
|
82
|
-
address: transactionData.address,
|
|
83
|
-
abi: transactionData.abi,
|
|
84
|
-
functionName: transactionData.functionName,
|
|
85
|
-
args: transactionData.args,
|
|
86
|
-
value: transactionData.value,
|
|
87
|
-
chain: walletClient.chain,
|
|
88
|
-
});
|
|
89
|
-
const hash = await walletClient.writeContract({
|
|
90
|
-
account: accountStatus.address!,
|
|
91
|
-
address: transactionData.address,
|
|
92
|
-
abi: transactionData.abi,
|
|
93
|
-
functionName: transactionData.functionName,
|
|
94
|
-
args: transactionData.args,
|
|
95
|
-
value: transactionData.value,
|
|
96
|
-
chain: walletClient.chain,
|
|
97
|
-
});
|
|
98
|
-
logger.info(SYSTEM_LOG_TAG, 'EOA Transaction initiated. Hash:', hash);
|
|
99
|
-
return { txHash: hash };
|
|
100
|
-
} catch (error: unknown) {
|
|
101
|
-
logger.error(SYSTEM_LOG_TAG, 'Error during EOA writeContract call:', error);
|
|
102
|
-
const errorMessage = error instanceof Error ? error.message : 'Unknown EOA transaction error';
|
|
103
|
-
throw new Error(`Transaction failed (EOA): ${errorMessage}`);
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// --- Main Exported Function ---
|
|
108
|
-
|
|
109
|
-
export async function signAndBroadcastEvmTransaction(
|
|
110
|
-
transactionData: WriteContractParameters,
|
|
111
|
-
walletImplementation: WagmiWalletImplementation,
|
|
112
|
-
targetChainId: number,
|
|
113
|
-
executionConfig?: ExecutionConfig
|
|
114
|
-
): Promise<{ txHash: string }> {
|
|
115
|
-
logger.info(SYSTEM_LOG_TAG, 'Sign & Broadcast EVM Tx:', {
|
|
116
|
-
data: transactionData,
|
|
117
|
-
targetChainId,
|
|
118
|
-
execConfig: executionConfig,
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
const currentMethod = executionConfig?.method || 'eoa';
|
|
122
|
-
|
|
123
|
-
await _ensureCorrectNetworkOrSwitch(walletImplementation, targetChainId);
|
|
124
|
-
const { walletClient, accountStatus } = await _getAuthenticatedWalletClient(walletImplementation);
|
|
125
|
-
|
|
126
|
-
switch (currentMethod) {
|
|
127
|
-
case 'eoa':
|
|
128
|
-
return _executeEoaTransaction(transactionData, walletClient, accountStatus);
|
|
129
|
-
|
|
130
|
-
case 'relayer':
|
|
131
|
-
logger.warn(SYSTEM_LOG_TAG, 'Relayer execution method not yet implemented.');
|
|
132
|
-
throw new Error('Relayer execution method not yet implemented.');
|
|
133
|
-
|
|
134
|
-
case 'multisig':
|
|
135
|
-
logger.warn(SYSTEM_LOG_TAG, 'Multisig execution method not yet implemented.');
|
|
136
|
-
throw new Error('Multisig execution method not yet implemented.');
|
|
137
|
-
|
|
138
|
-
default:
|
|
139
|
-
const exhaustiveCheck: never = currentMethod;
|
|
140
|
-
logger.error(SYSTEM_LOG_TAG, `Unsupported execution method encountered: ${exhaustiveCheck}`);
|
|
141
|
-
throw new Error(`Unsupported execution method: ${exhaustiveCheck}`);
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* Waits for a transaction to be confirmed on the blockchain.
|
|
147
|
-
*/
|
|
148
|
-
export async function waitForEvmTransactionConfirmation(
|
|
149
|
-
txHash: string,
|
|
150
|
-
walletImplementation: WagmiWalletImplementation
|
|
151
|
-
): Promise<{
|
|
152
|
-
status: 'success' | 'error';
|
|
153
|
-
receipt?: TransactionReceipt;
|
|
154
|
-
error?: Error;
|
|
155
|
-
}> {
|
|
156
|
-
logger.info(SYSTEM_LOG_TAG, `Waiting for tx: ${txHash}`);
|
|
157
|
-
try {
|
|
158
|
-
// Get the public client
|
|
159
|
-
const resolvedPublicClient = await walletImplementation.getPublicClient();
|
|
160
|
-
if (!resolvedPublicClient) {
|
|
161
|
-
throw new Error('Public client not available to wait for transaction.');
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
// Wait for the transaction receipt
|
|
165
|
-
const receipt = await resolvedPublicClient.waitForTransactionReceipt({
|
|
166
|
-
hash: txHash as `0x${string}`,
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
logger.info(SYSTEM_LOG_TAG, 'Received receipt:', receipt);
|
|
170
|
-
|
|
171
|
-
// Check the status field in the receipt
|
|
172
|
-
if (receipt.status === 'success') {
|
|
173
|
-
return { status: 'success', receipt };
|
|
174
|
-
} else {
|
|
175
|
-
logger.error(SYSTEM_LOG_TAG, 'Transaction reverted:', receipt);
|
|
176
|
-
return { status: 'error', receipt, error: new Error('Transaction reverted.') };
|
|
177
|
-
}
|
|
178
|
-
} catch (error) {
|
|
179
|
-
logger.error(SYSTEM_LOG_TAG, 'Error waiting for transaction confirmation:', error);
|
|
180
|
-
return {
|
|
181
|
-
status: 'error',
|
|
182
|
-
error: error instanceof Error ? error : new Error(String(error)),
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
|
-
}
|
package/src/transform/index.ts
DELETED
|
@@ -1,177 +0,0 @@
|
|
|
1
|
-
import { getAddress, isAddress } from 'viem';
|
|
2
|
-
|
|
3
|
-
import type { FunctionParameter } from '@openzeppelin/ui-types';
|
|
4
|
-
import { logger } from '@openzeppelin/ui-utils';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Recursively parses a raw input value based on its expected ABI type definition.
|
|
8
|
-
*
|
|
9
|
-
* @param param The ABI parameter definition ({ name, type, components?, ... })
|
|
10
|
-
* @param rawValue The raw value obtained from the form input or hardcoded config.
|
|
11
|
-
* @param isRecursive Internal flag to indicate if the call is nested.
|
|
12
|
-
* @returns The parsed and typed value suitable for ABI encoding.
|
|
13
|
-
* @throws {Error} If parsing or type validation fails.
|
|
14
|
-
*/
|
|
15
|
-
export function parseEvmInput(
|
|
16
|
-
param: FunctionParameter,
|
|
17
|
-
rawValue: unknown,
|
|
18
|
-
isRecursive = false
|
|
19
|
-
): unknown {
|
|
20
|
-
const { type, name } = param;
|
|
21
|
-
const baseType = type.replace(/\[\d*\]$/, ''); // Remove array indicators like `[]` or `[2]`
|
|
22
|
-
const isArray = type.endsWith(']');
|
|
23
|
-
|
|
24
|
-
try {
|
|
25
|
-
// --- Handle Arrays --- //
|
|
26
|
-
if (isArray) {
|
|
27
|
-
// Only expect string at the top level, recursive calls get arrays directly
|
|
28
|
-
let parsedArray: unknown[];
|
|
29
|
-
if (!isRecursive) {
|
|
30
|
-
if (typeof rawValue !== 'string') {
|
|
31
|
-
throw new Error('Array input must be a JSON string representation.');
|
|
32
|
-
}
|
|
33
|
-
try {
|
|
34
|
-
parsedArray = JSON.parse(rawValue);
|
|
35
|
-
} catch (e) {
|
|
36
|
-
throw new Error(`Invalid JSON for array: ${(e as Error).message}`);
|
|
37
|
-
}
|
|
38
|
-
} else {
|
|
39
|
-
// If recursive, rawValue should already be an array
|
|
40
|
-
if (!Array.isArray(rawValue)) {
|
|
41
|
-
throw new Error('Internal error: Expected array in recursive call.');
|
|
42
|
-
}
|
|
43
|
-
parsedArray = rawValue;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
if (!Array.isArray(parsedArray)) {
|
|
47
|
-
// Double check after parsing/assignment
|
|
48
|
-
throw new Error('Parsed JSON is not an array.');
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// Recursively parse each element
|
|
52
|
-
const itemAbiParam = { ...param, type: baseType }; // Create a dummy param for the base type
|
|
53
|
-
return parsedArray.map((item) => parseEvmInput(itemAbiParam, item, true)); // Pass isRecursive=true
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// --- Handle Tuples --- //
|
|
57
|
-
if (baseType === 'tuple') {
|
|
58
|
-
if (!param.components) {
|
|
59
|
-
throw new Error(`ABI definition missing 'components' for tuple parameter '${name}'.`);
|
|
60
|
-
}
|
|
61
|
-
// Only expect string at the top level, recursive calls get objects directly
|
|
62
|
-
let parsedObject: Record<string, unknown>;
|
|
63
|
-
if (!isRecursive) {
|
|
64
|
-
if (typeof rawValue !== 'string') {
|
|
65
|
-
throw new Error('Tuple input must be a JSON string representation of an object.');
|
|
66
|
-
}
|
|
67
|
-
try {
|
|
68
|
-
parsedObject = JSON.parse(rawValue);
|
|
69
|
-
} catch (e) {
|
|
70
|
-
throw new Error(`Invalid JSON for tuple: ${(e as Error).message}`);
|
|
71
|
-
}
|
|
72
|
-
} else {
|
|
73
|
-
// If recursive, rawValue should already be an object
|
|
74
|
-
if (typeof rawValue !== 'object' || rawValue === null || Array.isArray(rawValue)) {
|
|
75
|
-
throw new Error('Internal error: Expected object in recursive tuple call.');
|
|
76
|
-
}
|
|
77
|
-
parsedObject = rawValue as Record<string, unknown>; // Cast needed
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
if (
|
|
81
|
-
typeof parsedObject !== 'object' ||
|
|
82
|
-
parsedObject === null ||
|
|
83
|
-
Array.isArray(parsedObject)
|
|
84
|
-
) {
|
|
85
|
-
// Double check
|
|
86
|
-
throw new Error('Parsed JSON is not an object for tuple.');
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// Recursively parse each component
|
|
90
|
-
const resultObject: Record<string, unknown> = {};
|
|
91
|
-
for (const component of param.components) {
|
|
92
|
-
if (!(component.name in parsedObject)) {
|
|
93
|
-
throw new Error(`Missing component '${component.name}' in tuple JSON.`);
|
|
94
|
-
}
|
|
95
|
-
resultObject[component.name] = parseEvmInput(
|
|
96
|
-
component,
|
|
97
|
-
parsedObject[component.name],
|
|
98
|
-
true // Pass isRecursive=true
|
|
99
|
-
);
|
|
100
|
-
}
|
|
101
|
-
// Check for extra, unexpected keys in the provided JSON object
|
|
102
|
-
if (Object.keys(parsedObject).length !== param.components.length) {
|
|
103
|
-
const expectedKeys = param.components.map((c) => c.name).join(', ');
|
|
104
|
-
const actualKeys = Object.keys(parsedObject).join(', ');
|
|
105
|
-
throw new Error(
|
|
106
|
-
`Tuple object has incorrect number of keys. Expected ${param.components.length} (${expectedKeys}), but got ${Object.keys(parsedObject).length} (${actualKeys}).`
|
|
107
|
-
);
|
|
108
|
-
}
|
|
109
|
-
return resultObject;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// --- Handle Bytes --- //
|
|
113
|
-
if (baseType.startsWith('bytes')) {
|
|
114
|
-
if (typeof rawValue !== 'string') {
|
|
115
|
-
throw new Error('Bytes input must be a string.');
|
|
116
|
-
}
|
|
117
|
-
if (!/^0x([0-9a-fA-F]{2})*$/.test(rawValue)) {
|
|
118
|
-
throw new Error(
|
|
119
|
-
`Invalid hex string format for ${type}: must start with 0x and contain only hex characters.`
|
|
120
|
-
);
|
|
121
|
-
}
|
|
122
|
-
// Check byte length for fixed-size bytes? (e.g., bytes32)
|
|
123
|
-
const fixedSizeMatch = baseType.match(/^bytes(\d+)$/);
|
|
124
|
-
if (fixedSizeMatch) {
|
|
125
|
-
const expectedBytes = parseInt(fixedSizeMatch[1], 10);
|
|
126
|
-
const actualBytes = (rawValue.length - 2) / 2;
|
|
127
|
-
if (actualBytes !== expectedBytes) {
|
|
128
|
-
throw new Error(
|
|
129
|
-
`Invalid length for ${type}: expected ${expectedBytes} bytes (${expectedBytes * 2} hex chars), got ${actualBytes} bytes.`
|
|
130
|
-
);
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
return rawValue as `0x${string}`; // Already validated, cast to viem type
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// --- Handle Simple Types --- //
|
|
137
|
-
if (baseType.startsWith('uint') || baseType.startsWith('int')) {
|
|
138
|
-
if (rawValue === '' || rawValue === null || rawValue === undefined)
|
|
139
|
-
throw new Error('Numeric value cannot be empty.');
|
|
140
|
-
try {
|
|
141
|
-
// Use BigInt for all integer types
|
|
142
|
-
return BigInt(rawValue as string | number | bigint);
|
|
143
|
-
} catch {
|
|
144
|
-
throw new Error(`Invalid numeric value: '${rawValue}'.`);
|
|
145
|
-
}
|
|
146
|
-
} else if (baseType === 'address') {
|
|
147
|
-
if (typeof rawValue !== 'string' || !rawValue)
|
|
148
|
-
throw new Error('Address value must be a non-empty string.');
|
|
149
|
-
if (!isAddress(rawValue)) throw new Error(`Invalid address format: '${rawValue}'.`);
|
|
150
|
-
return getAddress(rawValue); // Return checksummed address
|
|
151
|
-
} else if (baseType === 'bool') {
|
|
152
|
-
if (typeof rawValue === 'boolean') return rawValue;
|
|
153
|
-
if (typeof rawValue === 'string') {
|
|
154
|
-
const lowerVal = rawValue.toLowerCase().trim();
|
|
155
|
-
if (lowerVal === 'true') return true;
|
|
156
|
-
if (lowerVal === 'false') return false;
|
|
157
|
-
}
|
|
158
|
-
// Try simple truthy/falsy conversion as fallback
|
|
159
|
-
return Boolean(rawValue);
|
|
160
|
-
} else if (baseType === 'string') {
|
|
161
|
-
// Ensure it's treated as a string
|
|
162
|
-
return String(rawValue);
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
// --- Fallback for unknown types --- //
|
|
166
|
-
logger.warn(
|
|
167
|
-
'parseEvmInput',
|
|
168
|
-
`Unknown EVM parameter type encountered: '${type}'. Using raw value.`
|
|
169
|
-
);
|
|
170
|
-
return rawValue;
|
|
171
|
-
} catch (error) {
|
|
172
|
-
// Add parameter context to the error message
|
|
173
|
-
throw new Error(
|
|
174
|
-
`Failed to parse value for parameter '${name || '(unnamed)'}' (type '${type}'): ${(error as Error).message}`
|
|
175
|
-
);
|
|
176
|
-
}
|
|
177
|
-
}
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import type { ContractFunction } from '@openzeppelin/ui-types';
|
|
2
|
-
import { logger } from '@openzeppelin/ui-utils';
|
|
3
|
-
|
|
4
|
-
import { stringifyWithBigInt } from '../utils';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Formats the decoded result of an EVM view function call into a user-friendly string.
|
|
8
|
-
*
|
|
9
|
-
* @param decodedValue The decoded value (can be primitive, array, object, BigInt).
|
|
10
|
-
* @param functionDetails The ABI details of the function called.
|
|
11
|
-
* @returns A string representation suitable for display.
|
|
12
|
-
*/
|
|
13
|
-
export function formatEvmFunctionResult(
|
|
14
|
-
decodedValue: unknown,
|
|
15
|
-
functionDetails: ContractFunction
|
|
16
|
-
): string {
|
|
17
|
-
if (!functionDetails.outputs || !Array.isArray(functionDetails.outputs)) {
|
|
18
|
-
logger.warn(
|
|
19
|
-
'formatEvmFunctionResult',
|
|
20
|
-
`Output ABI definition missing or invalid for function ${functionDetails.name}.`
|
|
21
|
-
);
|
|
22
|
-
return '[Error: Output ABI definition missing]';
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
try {
|
|
26
|
-
let valueToFormat: unknown;
|
|
27
|
-
// Handle potential array wrapping for single returns from viem
|
|
28
|
-
if (Array.isArray(decodedValue)) {
|
|
29
|
-
if (decodedValue.length === 1) {
|
|
30
|
-
valueToFormat = decodedValue[0]; // Single output, format the inner value
|
|
31
|
-
} else {
|
|
32
|
-
// Multiple outputs, format the whole array as JSON
|
|
33
|
-
valueToFormat = decodedValue;
|
|
34
|
-
}
|
|
35
|
-
} else {
|
|
36
|
-
// Not an array, could be a single value (like from a struct return) or undefined
|
|
37
|
-
valueToFormat = decodedValue;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// Format based on type
|
|
41
|
-
if (typeof valueToFormat === 'bigint') {
|
|
42
|
-
return valueToFormat.toString();
|
|
43
|
-
} else if (
|
|
44
|
-
typeof valueToFormat === 'string' ||
|
|
45
|
-
typeof valueToFormat === 'number' ||
|
|
46
|
-
typeof valueToFormat === 'boolean'
|
|
47
|
-
) {
|
|
48
|
-
return String(valueToFormat);
|
|
49
|
-
} else if (valueToFormat === null || valueToFormat === undefined) {
|
|
50
|
-
return '(null)'; // Represent null/undefined clearly
|
|
51
|
-
} else {
|
|
52
|
-
// Handles arrays with multiple elements or objects (structs) by stringifying
|
|
53
|
-
return stringifyWithBigInt(valueToFormat, 2); // Pretty print with 2 spaces
|
|
54
|
-
}
|
|
55
|
-
} catch (error) {
|
|
56
|
-
const errorMessage = `Error formatting result for ${functionDetails.name}: ${(error as Error).message}`;
|
|
57
|
-
logger.error('formatEvmFunctionResult', errorMessage, {
|
|
58
|
-
functionName: functionDetails.name,
|
|
59
|
-
decodedValue,
|
|
60
|
-
error,
|
|
61
|
-
});
|
|
62
|
-
return `[${errorMessage}]`;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Unit tests for EVM contract artifacts types and validation
|
|
3
|
-
*/
|
|
4
|
-
import { describe, expect, it } from 'vitest';
|
|
5
|
-
|
|
6
|
-
import { isEvmContractArtifacts, type EvmContractArtifacts } from '../artifacts';
|
|
7
|
-
|
|
8
|
-
describe('EVM Contract Artifacts', () => {
|
|
9
|
-
describe('isEvmContractArtifacts', () => {
|
|
10
|
-
it('should return true for valid artifacts with only contractAddress', () => {
|
|
11
|
-
const artifacts = {
|
|
12
|
-
contractAddress: '0x1234567890123456789012345678901234567890',
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
const result = isEvmContractArtifacts(artifacts);
|
|
16
|
-
|
|
17
|
-
expect(result).toBe(true);
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
it('should return true for valid artifacts with all optional properties', () => {
|
|
21
|
-
const artifacts: EvmContractArtifacts = {
|
|
22
|
-
contractAddress: '0x1234567890123456789012345678901234567890',
|
|
23
|
-
contractDefinition: '[{"type":"function","name":"test"}]',
|
|
24
|
-
__proxyDetectionOptions: {
|
|
25
|
-
skipProxyDetection: true,
|
|
26
|
-
},
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
const result = isEvmContractArtifacts(artifacts);
|
|
30
|
-
|
|
31
|
-
expect(result).toBe(true);
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
it('should return false for null', () => {
|
|
35
|
-
const result = isEvmContractArtifacts(null);
|
|
36
|
-
expect(result).toBe(false);
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it('should return false for undefined', () => {
|
|
40
|
-
const result = isEvmContractArtifacts(undefined);
|
|
41
|
-
expect(result).toBe(false);
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it('should return false for non-object types', () => {
|
|
45
|
-
expect(isEvmContractArtifacts('string')).toBe(false);
|
|
46
|
-
expect(isEvmContractArtifacts(123)).toBe(false);
|
|
47
|
-
expect(isEvmContractArtifacts(true)).toBe(false);
|
|
48
|
-
expect(isEvmContractArtifacts([])).toBe(false);
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it('should return false for object without contractAddress', () => {
|
|
52
|
-
const artifacts = {
|
|
53
|
-
contractDefinition: '[{"type":"function","name":"test"}]',
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
const result = isEvmContractArtifacts(artifacts);
|
|
57
|
-
|
|
58
|
-
expect(result).toBe(false);
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it('should return false for object with non-string contractAddress', () => {
|
|
62
|
-
const artifacts = {
|
|
63
|
-
contractAddress: 123,
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
const result = isEvmContractArtifacts(artifacts);
|
|
67
|
-
|
|
68
|
-
expect(result).toBe(false);
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
it('should return true even with extra properties', () => {
|
|
72
|
-
const artifacts = {
|
|
73
|
-
contractAddress: '0x1234567890123456789012345678901234567890',
|
|
74
|
-
extraProperty: 'should be ignored',
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
const result = isEvmContractArtifacts(artifacts);
|
|
78
|
-
|
|
79
|
-
expect(result).toBe(true);
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
it('should handle empty contractAddress string', () => {
|
|
83
|
-
const artifacts = {
|
|
84
|
-
contractAddress: '',
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
const result = isEvmContractArtifacts(artifacts);
|
|
88
|
-
|
|
89
|
-
expect(result).toBe(true); // Type guard only checks type, not validity
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
it('should handle proxy detection options correctly', () => {
|
|
93
|
-
const artifacts = {
|
|
94
|
-
contractAddress: '0x1234567890123456789012345678901234567890',
|
|
95
|
-
__proxyDetectionOptions: {
|
|
96
|
-
skipProxyDetection: false,
|
|
97
|
-
},
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
const result = isEvmContractArtifacts(artifacts);
|
|
101
|
-
|
|
102
|
-
expect(result).toBe(true);
|
|
103
|
-
});
|
|
104
|
-
});
|
|
105
|
-
});
|
package/src/types.ts
DELETED
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
import type { Abi, Chain } from 'viem';
|
|
2
|
-
|
|
3
|
-
import type { EvmNetworkConfig } from '@openzeppelin/ui-types';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* EVM-specific type definitions
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* EVM-specific network configuration with properly typed viem chain
|
|
11
|
-
* This extends the base EvmNetworkConfig with the correct Chain type from viem
|
|
12
|
-
*/
|
|
13
|
-
export interface TypedEvmNetworkConfig extends EvmNetworkConfig {
|
|
14
|
-
/**
|
|
15
|
-
* Viem Chain object for this EVM network.
|
|
16
|
-
* If provided, this will be used directly by Viem clients.
|
|
17
|
-
* If not provided, a fallback chain object will be created.
|
|
18
|
-
*/
|
|
19
|
-
viemChain?: Chain;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Represents an item in an Ethereum ABI
|
|
24
|
-
*/
|
|
25
|
-
export type AbiItem = {
|
|
26
|
-
type: string;
|
|
27
|
-
name?: string;
|
|
28
|
-
inputs?: Array<{
|
|
29
|
-
name: string;
|
|
30
|
-
type: string;
|
|
31
|
-
internalType?: string;
|
|
32
|
-
indexed?: boolean;
|
|
33
|
-
components?: Array<{
|
|
34
|
-
name: string;
|
|
35
|
-
type: string;
|
|
36
|
-
internalType?: string;
|
|
37
|
-
indexed?: boolean;
|
|
38
|
-
}>;
|
|
39
|
-
}>;
|
|
40
|
-
stateMutability?: string;
|
|
41
|
-
anonymous?: boolean;
|
|
42
|
-
outputs?: Array<{
|
|
43
|
-
name: string;
|
|
44
|
-
type: string;
|
|
45
|
-
internalType?: string;
|
|
46
|
-
components?: Array<{
|
|
47
|
-
name: string;
|
|
48
|
-
type: string;
|
|
49
|
-
internalType?: string;
|
|
50
|
-
}>;
|
|
51
|
-
}>;
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* EVM-specific parameter types
|
|
56
|
-
*/
|
|
57
|
-
export enum EVMParameterType {
|
|
58
|
-
ADDRESS = 'address',
|
|
59
|
-
UINT256 = 'uint256',
|
|
60
|
-
UINT8 = 'uint8',
|
|
61
|
-
BOOL = 'bool',
|
|
62
|
-
BYTES = 'bytes',
|
|
63
|
-
BYTES32 = 'bytes32',
|
|
64
|
-
STRING = 'string',
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* EVM-specific chain types
|
|
69
|
-
*/
|
|
70
|
-
export enum EVMChainType {
|
|
71
|
-
ETHEREUM = 'ethereum',
|
|
72
|
-
POLYGON = 'polygon',
|
|
73
|
-
ARBITRUM = 'arbitrum',
|
|
74
|
-
OPTIMISM = 'optimism',
|
|
75
|
-
BSC = 'bsc',
|
|
76
|
-
AVALANCHE = 'avalanche',
|
|
77
|
-
}
|
|
78
|
-
// Import Viem's Abi type
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Defines the structure for parameters required to execute a contract write operation via viem.
|
|
82
|
-
*/
|
|
83
|
-
export interface WriteContractParameters {
|
|
84
|
-
address: `0x${string}`; // Ensure address is a valid hex string type
|
|
85
|
-
abi: Abi;
|
|
86
|
-
functionName: string;
|
|
87
|
-
args: unknown[];
|
|
88
|
-
value?: bigint;
|
|
89
|
-
// Add other potential viem parameters if needed (e.g., gas)
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// Add other adapter-specific internal types here if necessary
|