@openzeppelin/ui-builder-adapter-evm 0.15.0 → 0.16.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openzeppelin/ui-builder-adapter-evm",
3
- "version": "0.15.0",
3
+ "version": "0.16.0",
4
4
  "private": false,
5
5
  "description": "EVM Adapter for UI Builder",
6
6
  "keywords": [
@@ -43,10 +43,10 @@
43
43
  "lodash": "^4.17.21",
44
44
  "lucide-react": "^0.510.0",
45
45
  "react-hook-form": "^7.62.0",
46
- "@openzeppelin/ui-builder-react-core": "^0.15.0",
47
- "@openzeppelin/ui-builder-types": "^0.15.0",
48
- "@openzeppelin/ui-builder-ui": "^0.15.0",
49
- "@openzeppelin/ui-builder-utils": "^0.15.0"
46
+ "@openzeppelin/ui-builder-react-core": "^0.16.0",
47
+ "@openzeppelin/ui-builder-types": "^0.16.0",
48
+ "@openzeppelin/ui-builder-ui": "^0.16.0",
49
+ "@openzeppelin/ui-builder-utils": "^0.16.0"
50
50
  },
51
51
  "devDependencies": {
52
52
  "@rainbow-me/rainbowkit": "^2.2.8",
@@ -20,9 +20,35 @@ describe('EVM Field Generator', () => {
20
20
 
21
21
  expect(field.type).toBe('number');
22
22
  expect(field.validation.required).toBe(true);
23
+ expect(field.validation.min).toBe(0);
24
+ expect(field.validation.max).toBe(4_294_967_295);
23
25
  expect(field.validation.pattern).toBeUndefined();
24
26
  });
25
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
+
26
52
  it('should generate a bigint field for uint128', () => {
27
53
  const param = createParam('uint128', 'amount');
28
54
  const field = generateEvmDefaultField(param);
@@ -86,6 +112,17 @@ describe('EVM Field Generator', () => {
86
112
  // Integer validation is handled by BigIntField component internally
87
113
  });
88
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
+
89
126
  it('should include proper field metadata', () => {
90
127
  const param = createParam('uint256', 'value');
91
128
  const field = generateEvmDefaultField(param);
@@ -7,7 +7,11 @@ import type {
7
7
  FormFieldType,
8
8
  FunctionParameter,
9
9
  } from '@openzeppelin/ui-builder-types';
10
- import { getDefaultValueForType } from '@openzeppelin/ui-builder-utils';
10
+ import {
11
+ enhanceNumericValidation,
12
+ getDefaultValueForType,
13
+ type NumericBoundsMap,
14
+ } from '@openzeppelin/ui-builder-utils';
11
15
 
12
16
  import { mapEvmParamTypeToFieldType } from './type-mapper';
13
17
 
@@ -33,6 +37,23 @@ function getDefaultValidation(): FieldValidation {
33
37
  return { required: true };
34
38
  }
35
39
 
40
+ /**
41
+ * EVM numeric type bounds.
42
+ * Maps EVM type names to their min/max value constraints.
43
+ * Note: uint64, uint128, uint256, int64, int128, int256 exceed JavaScript's Number.MAX_SAFE_INTEGER
44
+ * and are handled via BigInt fields, so bounds are not applied here.
45
+ */
46
+ const EVM_NUMERIC_BOUNDS: NumericBoundsMap = {
47
+ uint: { min: 0 },
48
+ uint8: { min: 0, max: 255 },
49
+ uint16: { min: 0, max: 65_535 },
50
+ uint32: { min: 0, max: 4_294_967_295 },
51
+ int: {},
52
+ int8: { min: -128, max: 127 },
53
+ int16: { min: -32_768, max: 32_767 },
54
+ int32: { min: -2_147_483_648, max: 2_147_483_647 },
55
+ };
56
+
36
57
  /**
37
58
  * Generate default field configuration for an EVM function parameter.
38
59
  */
@@ -49,7 +70,11 @@ export function generateEvmDefaultField<T extends FieldType = FieldType>(
49
70
  placeholder: `Enter ${parameter.displayName || parameter.name || parameter.type}`,
50
71
  helperText: parameter.description || '',
51
72
  defaultValue: getDefaultValueForType(fieldType) as FieldValue<T>,
52
- validation: getDefaultValidation(),
73
+ validation: enhanceNumericValidation(
74
+ getDefaultValidation(),
75
+ parameter.type,
76
+ EVM_NUMERIC_BOUNDS
77
+ ),
53
78
  width: 'full',
54
79
  };
55
80
 
@@ -65,7 +90,11 @@ export function generateEvmDefaultField<T extends FieldType = FieldType>(
65
90
  elementType: elementFieldType,
66
91
  elementFieldConfig: {
67
92
  type: elementFieldType,
68
- validation: getDefaultValidation(),
93
+ validation: enhanceNumericValidation(
94
+ getDefaultValidation(),
95
+ elementType,
96
+ EVM_NUMERIC_BOUNDS
97
+ ),
69
98
  placeholder: `Enter ${elementType}`,
70
99
  },
71
100
  };
@@ -99,6 +99,7 @@ export class WagmiWalletImplementation {
99
99
  private unsubscribe?: ReturnType<typeof watchAccount>;
100
100
  private initialized: boolean = false;
101
101
  private walletConnectProjectId?: string;
102
+ private rpcConfigUnsubscribe?: () => void;
102
103
 
103
104
  /**
104
105
  * Constructs the WagmiWalletImplementation.
@@ -121,9 +122,50 @@ export class WagmiWalletImplementation {
121
122
  LOG_SYSTEM,
122
123
  'WagmiWalletImplementation instance initialized (Wagmi config creation deferred).'
123
124
  );
125
+ // Subscribe to RPC configuration changes to invalidate cached config
126
+ this.setupRpcConfigListener();
124
127
  // No config created here by default anymore.
125
128
  }
126
129
 
130
+ /**
131
+ * Sets up a listener for RPC configuration changes to invalidate the cached Wagmi config
132
+ * when user changes RPC settings.
133
+ */
134
+ private setupRpcConfigListener(): void {
135
+ // Import dynamically to avoid circular dependencies
136
+ import('@openzeppelin/ui-builder-utils')
137
+ .then(({ userRpcConfigService }) => {
138
+ // Subscribe to all RPC config changes
139
+ this.rpcConfigUnsubscribe = userRpcConfigService.subscribe('*', (event) => {
140
+ if (event.type === 'rpc-config-changed' || event.type === 'rpc-config-cleared') {
141
+ logger.info(
142
+ LOG_SYSTEM,
143
+ `RPC config changed for network ${event.networkId}. Invalidating cached Wagmi config.`
144
+ );
145
+ // Invalidate the cached config to force recreation with new RPC settings
146
+ this.defaultInstanceConfig = null;
147
+ }
148
+ });
149
+ })
150
+ .catch((error) => {
151
+ logger.error(LOG_SYSTEM, 'Failed to setup RPC config listener:', error);
152
+ });
153
+ }
154
+
155
+ /**
156
+ * Cleanup method to unsubscribe from RPC config changes
157
+ */
158
+ public cleanup(): void {
159
+ if (this.rpcConfigUnsubscribe) {
160
+ this.rpcConfigUnsubscribe();
161
+ this.rpcConfigUnsubscribe = undefined;
162
+ }
163
+ if (this.unsubscribe) {
164
+ this.unsubscribe();
165
+ this.unsubscribe = undefined;
166
+ }
167
+ }
168
+
127
169
  /**
128
170
  * Sets the externally determined, currently active WagmiConfig instance.
129
171
  * This is typically called by EvmUiKitManager after it has resolved the appropriate
@@ -333,8 +375,12 @@ export class WagmiWalletImplementation {
333
375
  );
334
376
  }
335
377
 
336
- // Always rebuild default config to reflect latest RPC overrides/user settings
337
- this.defaultInstanceConfig = this.createDefaultConfig();
378
+ // Reuse existing default config if available, only create if needed
379
+ // This ensures wallet connection state is preserved across network switches
380
+ // Config is automatically invalidated when RPC settings change via setupRpcConfigListener()
381
+ if (!this.defaultInstanceConfig) {
382
+ this.defaultInstanceConfig = this.createDefaultConfig();
383
+ }
338
384
  return this.defaultInstanceConfig;
339
385
  }
340
386