@openzeppelin/ui-renderer 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,2101 @@
1
+ //#region rolldown:runtime
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") {
10
+ for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
11
+ key = keys[i];
12
+ if (!__hasOwnProp.call(to, key) && key !== except) {
13
+ __defProp(to, key, {
14
+ get: ((k) => from[k]).bind(null, key),
15
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
16
+ });
17
+ }
18
+ }
19
+ }
20
+ return to;
21
+ };
22
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
23
+ value: mod,
24
+ enumerable: true
25
+ }) : target, mod));
26
+
27
+ //#endregion
28
+ let lucide_react = require("lucide-react");
29
+ let react = require("react");
30
+ react = __toESM(react);
31
+ let react_hook_form = require("react-hook-form");
32
+ let _openzeppelin_ui_utils = require("@openzeppelin/ui-utils");
33
+ let _openzeppelin_ui_components = require("@openzeppelin/ui-components");
34
+ let react_jsx_runtime = require("react/jsx-runtime");
35
+ let _openzeppelin_ui_react = require("@openzeppelin/ui-react");
36
+
37
+ //#region src/components/ExecutionConfigDisplay/components/EoaConfigDetails.tsx
38
+ const EoaConfigDetails = ({ config }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
39
+ className: "space-y-4",
40
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
41
+ className: "flex items-start space-x-3 p-3 bg-slate-50 rounded-md",
42
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.User, { className: "size-5 text-primary mt-0.5 flex-shrink-0" }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", {
43
+ className: "text-sm font-medium mb-1",
44
+ children: "Externally Owned Account (EOA)"
45
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
46
+ className: "text-sm text-muted-foreground",
47
+ children: "Transaction will be executed directly from the connected wallet."
48
+ })] })]
49
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
50
+ className: "flex items-start space-x-3 p-3 bg-slate-50 rounded-md",
51
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Key, { className: "size-5 text-primary mt-0.5 flex-shrink-0" }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [
52
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", {
53
+ className: "text-sm font-medium mb-1",
54
+ children: "Execution Restrictions"
55
+ }),
56
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
57
+ className: "text-sm text-muted-foreground",
58
+ children: config.allowAny ? "Any connected wallet can try to execute this transaction." : config.specificAddress ? "Only this address can try to execute this transaction:" : "No specific address restrictions defined."
59
+ }),
60
+ config.specificAddress && !config.allowAny && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_openzeppelin_ui_components.AddressDisplay, {
61
+ className: "mt-2",
62
+ address: config.specificAddress
63
+ })
64
+ ] })]
65
+ })]
66
+ });
67
+
68
+ //#endregion
69
+ //#region src/components/ExecutionConfigDisplay/components/ExecutionMethodTrigger.tsx
70
+ const ExecutionMethodTrigger = ({ executionConfig, isValid, error, className }) => {
71
+ const getMethodIcon = (method) => {
72
+ const iconColorClass = !isValid ? "text-red-500" : "text-primary";
73
+ switch (method) {
74
+ case "eoa": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.User, { className: `size-3.5 ${iconColorClass}` });
75
+ case "relayer": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Shield, { className: `size-3.5 ${iconColorClass}` });
76
+ case "multisig": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Users, { className: `size-3.5 ${iconColorClass}` });
77
+ default: return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Key, { className: `size-3.5 ${!isValid ? "text-red-500" : "text-muted"}` });
78
+ }
79
+ };
80
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_openzeppelin_ui_components.DialogTrigger, {
81
+ className: (0, _openzeppelin_ui_utils.cn)("inline-flex items-center gap-2 px-3 py-2 text-xs rounded-md border group", "transition-all duration-200 hover:bg-accent hover:text-accent-foreground", !isValid ? "border-red-300 bg-red-50 text-red-800 hover:bg-red-50/80" : "border-slate-200 bg-white text-slate-700", className),
82
+ style: !isValid ? { animation: "subtle-pulse-scale 2s cubic-bezier(0.4, 0, 0.6, 1) infinite" } : void 0,
83
+ children: [
84
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
85
+ className: "flex items-center gap-1.5",
86
+ children: [
87
+ getMethodIcon(executionConfig.method),
88
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
89
+ className: "font-semibold",
90
+ children: "Execution:"
91
+ }),
92
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
93
+ className: "uppercase",
94
+ children: executionConfig.method
95
+ })
96
+ ]
97
+ }),
98
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
99
+ className: "flex items-center ml-2",
100
+ children: !isValid ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.AlertCircle, { className: "size-3.5 text-red-500" }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Info, { className: "size-3.5 text-muted-foreground transition-colors group-hover:text-foreground" })
101
+ }),
102
+ !isValid && error && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
103
+ className: "sr-only group-hover:not-sr-only group-hover:absolute group-hover:top-full group-hover:left-0 group-hover:mt-1 group-hover:px-2 group-hover:py-1 group-hover:bg-red-900 group-hover:text-white group-hover:text-xs group-hover:rounded group-hover:shadow-lg group-hover:z-50 group-hover:max-w-xs",
104
+ children: error
105
+ }),
106
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("style", { dangerouslySetInnerHTML: { __html: `
107
+ @keyframes subtle-pulse-scale {
108
+ 0%, 100% {
109
+ opacity: 0.65;
110
+ transform: scale(1);
111
+ }
112
+ 50% {
113
+ opacity: 1;
114
+ transform: scale(1.02);
115
+ }
116
+ }
117
+ ` } })
118
+ ]
119
+ });
120
+ };
121
+
122
+ //#endregion
123
+ //#region src/components/ExecutionConfigDisplay/components/ExecutionConfigCard.tsx
124
+ const ExecutionConfigCard = ({ config }) => {
125
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
126
+ className: "flex items-start space-x-3 p-3 bg-slate-50 rounded-md",
127
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Settings, { className: "size-5 text-primary mt-0.5 flex-shrink-0" }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
128
+ className: "flex-1",
129
+ children: [
130
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", {
131
+ className: "text-sm font-medium mb-1",
132
+ children: "Execution Configuration"
133
+ }),
134
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
135
+ className: "text-sm text-muted-foreground mb-2",
136
+ children: "Configuration parameters that will be used for transaction execution."
137
+ }),
138
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
139
+ className: "space-y-2",
140
+ children: [config.transactionOptions && Object.keys(config.transactionOptions).length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
141
+ className: "text-xs text-muted-foreground font-medium",
142
+ children: "Transaction Options:"
143
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
144
+ className: "mt-1 bg-white border rounded p-2",
145
+ children: Object.entries(config.transactionOptions).map(([key, value]) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
146
+ className: "flex items-center justify-between py-1 border-b last:border-b-0",
147
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
148
+ className: "text-xs font-mono text-slate-600",
149
+ children: [key, ":"]
150
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
151
+ className: "text-xs font-mono bg-slate-100 px-2 py-0.5 rounded",
152
+ children: typeof value === "object" && value !== null ? JSON.stringify(value) : String(value)
153
+ })]
154
+ }, key))
155
+ })] }), (!config.transactionOptions || Object.keys(config.transactionOptions).length === 0) && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
156
+ className: "text-xs text-muted-foreground font-medium",
157
+ children: "Transaction Options:"
158
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
159
+ className: "text-xs text-muted-foreground ml-2",
160
+ children: "None configured"
161
+ })] })]
162
+ })
163
+ ]
164
+ })]
165
+ });
166
+ };
167
+
168
+ //#endregion
169
+ //#region src/components/ExecutionConfigDisplay/components/RelayerConfigDetails.tsx
170
+ const RelayerConfigDetails = ({ config, enhancedDetails, loading = false }) => {
171
+ const { relayer } = config;
172
+ const { activeAdapter } = (0, _openzeppelin_ui_react.useWalletState)();
173
+ const labels = activeAdapter?.getUiLabels?.();
174
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
175
+ className: "space-y-4",
176
+ children: [
177
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
178
+ className: "flex items-start space-x-3 p-3 bg-slate-50 rounded-md",
179
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Shield, { className: "size-5 text-primary mt-0.5 flex-shrink-0" }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", {
180
+ className: "text-sm font-medium mb-1",
181
+ children: "OpenZeppelin Relayer"
182
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
183
+ className: "text-sm text-muted-foreground",
184
+ children: "Transaction will be sent via the selected OpenZeppelin Relayer."
185
+ })] })]
186
+ }),
187
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_openzeppelin_ui_components.RelayerDetailsCard, {
188
+ details: relayer,
189
+ enhancedDetails,
190
+ loading,
191
+ labels
192
+ }),
193
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
194
+ className: "flex items-start space-x-3 p-3 bg-slate-50 rounded-md",
195
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Network, { className: "size-5 text-primary mt-0.5 flex-shrink-0" }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", {
196
+ className: "text-sm font-medium mb-1",
197
+ children: "Service Endpoint"
198
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
199
+ className: "text-xs text-muted-foreground font-mono break-all",
200
+ children: config.serviceUrl
201
+ })] })]
202
+ }),
203
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ExecutionConfigCard, { config })
204
+ ]
205
+ });
206
+ };
207
+
208
+ //#endregion
209
+ //#region src/components/ExecutionConfigDisplay/hooks/useExecutionValidation.ts
210
+ const useExecutionValidation = ({ executionConfig, adapter, runtimeApiKey }) => {
211
+ const [validationResult, setValidationResult] = (0, react.useState)({ isValid: true });
212
+ const validateConfig = (0, react.useCallback)(async () => {
213
+ if (!adapter) {
214
+ setValidationResult({
215
+ isValid: false,
216
+ error: "No adapter available for validation"
217
+ });
218
+ return;
219
+ }
220
+ try {
221
+ const result = await adapter.validateExecutionConfig(executionConfig);
222
+ if (result === true) {
223
+ let runtimeError;
224
+ if (executionConfig.method === "relayer" && (!runtimeApiKey || runtimeApiKey.trim() === "")) runtimeError = "Relayer API key is required for transaction execution";
225
+ setValidationResult({
226
+ isValid: !runtimeError,
227
+ error: runtimeError
228
+ });
229
+ } else setValidationResult({
230
+ isValid: false,
231
+ error: result
232
+ });
233
+ } catch (error) {
234
+ setValidationResult({
235
+ isValid: false,
236
+ error: error instanceof Error ? error.message : "Validation failed"
237
+ });
238
+ }
239
+ }, [
240
+ executionConfig,
241
+ adapter,
242
+ runtimeApiKey
243
+ ]);
244
+ (0, react.useEffect)(() => {
245
+ validateConfig();
246
+ }, [validateConfig]);
247
+ return validationResult;
248
+ };
249
+
250
+ //#endregion
251
+ //#region src/components/ExecutionConfigDisplay/ExecutionConfigDisplay.tsx
252
+ const ExecutionConfigDisplay = ({ executionConfig, adapter, error, onRuntimeApiKeyChange }) => {
253
+ const { control, watch } = (0, react_hook_form.useForm)({ defaultValues: { runtimeApiKey: "" } });
254
+ const runtimeApiKey = watch("runtimeApiKey");
255
+ const [isOpen, setIsOpen] = (0, react.useState)(false);
256
+ const [enhancedRelayerDetails, setEnhancedRelayerDetails] = (0, react.useState)(null);
257
+ const [relayerDetailsLoading, setRelayerDetailsLoading] = (0, react.useState)(false);
258
+ const { isValid, error: validationError } = useExecutionValidation({
259
+ executionConfig,
260
+ adapter,
261
+ runtimeApiKey
262
+ });
263
+ (0, react.useEffect)(() => {
264
+ onRuntimeApiKeyChange?.(runtimeApiKey);
265
+ }, [runtimeApiKey, onRuntimeApiKeyChange]);
266
+ (0, react.useEffect)(() => {
267
+ if (isOpen && executionConfig.method === "relayer" && runtimeApiKey && adapter?.getRelayer) {
268
+ const relayerConfig = executionConfig;
269
+ setRelayerDetailsLoading(true);
270
+ adapter.getRelayer(relayerConfig.serviceUrl, runtimeApiKey, relayerConfig.relayer.relayerId).then((details) => {
271
+ setEnhancedRelayerDetails(details);
272
+ }).catch((err) => {
273
+ _openzeppelin_ui_utils.logger.error("ExecutionConfigDisplay", "Failed to fetch enhanced relayer details:", err);
274
+ setEnhancedRelayerDetails(null);
275
+ }).finally(() => {
276
+ setRelayerDetailsLoading(false);
277
+ });
278
+ }
279
+ }, [
280
+ isOpen,
281
+ executionConfig,
282
+ runtimeApiKey,
283
+ adapter
284
+ ]);
285
+ const getExecutionContent = () => {
286
+ switch (executionConfig.method) {
287
+ case "eoa": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EoaConfigDetails, { config: executionConfig });
288
+ case "relayer": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RelayerConfigDetails, {
289
+ config: executionConfig,
290
+ enhancedDetails: enhancedRelayerDetails,
291
+ loading: relayerDetailsLoading
292
+ });
293
+ default: return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_openzeppelin_ui_components.EmptyState, {
294
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.AlertTriangle, { className: "h-6 w-6 text-muted-foreground" }),
295
+ title: "Unknown Execution Method",
296
+ description: "The selected execution method is not recognized. Please check your configuration or contact support.",
297
+ size: "small"
298
+ });
299
+ }
300
+ };
301
+ const displayError = validationError || error || void 0;
302
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_openzeppelin_ui_components.Dialog, {
303
+ open: isOpen,
304
+ onOpenChange: setIsOpen,
305
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
306
+ className: "flex justify-end w-full",
307
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ExecutionMethodTrigger, {
308
+ executionConfig,
309
+ isValid,
310
+ error: displayError
311
+ })
312
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_openzeppelin_ui_components.DialogContent, {
313
+ className: "sm:max-w-[550px] max-h-[90vh] flex flex-col",
314
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_openzeppelin_ui_components.DialogHeader, {
315
+ className: "border-b pb-4 shrink-0",
316
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
317
+ className: "flex items-start justify-between",
318
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_openzeppelin_ui_components.DialogTitle, {
319
+ className: "flex items-center gap-2 text-xl",
320
+ children: ["Execution Method", displayError && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.AlertCircle, { className: "h-5 w-5 text-red-500" })]
321
+ })
322
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_openzeppelin_ui_components.DialogDescription, {
323
+ className: "mt-2",
324
+ children: "This outlines how the transaction will be signed and submitted to the blockchain."
325
+ })]
326
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
327
+ className: "overflow-y-auto grow",
328
+ children: [
329
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
330
+ className: "p-6",
331
+ children: getExecutionContent()
332
+ }),
333
+ executionConfig.method === "relayer" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
334
+ className: "px-6 pb-4",
335
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_openzeppelin_ui_components.PasswordField, {
336
+ id: "runtime-api-key",
337
+ label: "Relayer API Key",
338
+ name: "runtimeApiKey",
339
+ control,
340
+ placeholder: "Enter your API key",
341
+ validation: { required: true },
342
+ helperText: "This key is required to send the transaction and is not stored."
343
+ })
344
+ }),
345
+ displayError && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
346
+ className: "px-6 pb-4",
347
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_openzeppelin_ui_components.Alert, {
348
+ variant: "destructive",
349
+ className: "p-3 border border-red-300",
350
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
351
+ className: "flex items-start",
352
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.AlertCircle, { className: "h-4 w-4 mt-0.5 shrink-0" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_openzeppelin_ui_components.AlertDescription, {
353
+ className: "pl-2 text-sm",
354
+ children: displayError
355
+ })]
356
+ })
357
+ })
358
+ })
359
+ ]
360
+ })]
361
+ })]
362
+ });
363
+ };
364
+
365
+ //#endregion
366
+ //#region src/components/transaction/TransactionExecuteButton.tsx
367
+ /**
368
+ * TransactionExecuteButton Component
369
+ *
370
+ * Displays a button for executing a transaction, which is disabled if the wallet is not connected,
371
+ * the form is invalid, or a transaction is currently being submitted.
372
+ *
373
+ * @param props The component props
374
+ * @returns A React component
375
+ */
376
+ function TransactionExecuteButton({ isWalletConnected, isSubmitting, isFormValid, variant = "default", functionDetails, canExecuteLocally = false }) {
377
+ const canExecute = canExecuteLocally || functionDetails?.stateMutability === "pure";
378
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_openzeppelin_ui_components.LoadingButton, {
379
+ type: "submit",
380
+ disabled: !isWalletConnected && !canExecute || !isFormValid,
381
+ loading: isSubmitting,
382
+ variant,
383
+ size: "lg",
384
+ className: "w-full md:w-auto",
385
+ children: canExecute ? isSubmitting ? "Executing..." : "Execute Locally" : isSubmitting ? "Executing..." : "Execute Transaction"
386
+ });
387
+ }
388
+
389
+ //#endregion
390
+ //#region src/utils/formUtils.ts
391
+ /**
392
+ * Validate a field value against validation rules
393
+ */
394
+ function validateField(value, validation) {
395
+ if (validation?.required && (value === "" || value === null || value === void 0)) return "This field is required";
396
+ if (validation?.min !== void 0 && typeof value === "number" && value < validation.min) return `Value must be at least ${validation.min}`;
397
+ if (validation?.max !== void 0 && typeof value === "number" && value > validation.max) return `Value must be at most ${validation.max}`;
398
+ if (validation?.pattern !== void 0 && typeof value === "string" && new RegExp(validation.pattern).test(value) === false) return "Value does not match the required pattern";
399
+ return null;
400
+ }
401
+ /**
402
+ * Creates a transform for address fields
403
+ *
404
+ * @param adapter The blockchain adapter to use for validation
405
+ * @returns Transform functions for address fields
406
+ */
407
+ function createAddressTransform(adapter) {
408
+ return {
409
+ input: (value) => {
410
+ if (value === null || value === void 0) return "";
411
+ return String(value);
412
+ },
413
+ output: (value) => {
414
+ const address = String(value || "");
415
+ if (adapter.isValidAddress?.(address)) return address;
416
+ return "";
417
+ }
418
+ };
419
+ }
420
+ /**
421
+ * Creates a transform for number fields
422
+ *
423
+ * @returns Transform functions for number fields
424
+ */
425
+ function createNumberTransform() {
426
+ return {
427
+ input: (value) => {
428
+ if (value === void 0 || value === null) return "";
429
+ return String(value);
430
+ },
431
+ output: (value) => {
432
+ const num = Number(value);
433
+ return isNaN(num) ? 0 : num;
434
+ }
435
+ };
436
+ }
437
+ /**
438
+ * Creates a transform for bigint fields (large integers beyond JS Number precision)
439
+ *
440
+ * @returns Transform functions for bigint fields
441
+ */
442
+ function createBigIntTransform() {
443
+ return {
444
+ input: (value) => {
445
+ if (value === void 0 || value === null) return "";
446
+ return String(value);
447
+ },
448
+ output: (value) => {
449
+ const str = String(value || "");
450
+ if (str && !/^-?\d+$/.test(str)) return "";
451
+ return str;
452
+ }
453
+ };
454
+ }
455
+ /**
456
+ * Creates a transform for boolean fields
457
+ *
458
+ * @returns Transform functions for boolean fields
459
+ */
460
+ function createBooleanTransform() {
461
+ return {
462
+ input: (value) => {
463
+ if (value === void 0 || value === null) return false;
464
+ if (typeof value === "string") return value.toLowerCase() === "true" || value === "1";
465
+ return Boolean(value);
466
+ },
467
+ output: (value) => {
468
+ if (typeof value === "string") return value.toLowerCase() === "true" || value === "1";
469
+ return Boolean(value);
470
+ }
471
+ };
472
+ }
473
+ /**
474
+ * Creates a transform for complex type fields (arrays, objects, etc.)
475
+ *
476
+ * @returns Transform functions for complex fields
477
+ */
478
+ function createComplexTypeTransform() {
479
+ return {
480
+ input: (value) => {
481
+ if (value === void 0 || value === null) return "";
482
+ if (typeof value === "string") return value;
483
+ try {
484
+ return JSON.stringify(value, null, 2);
485
+ } catch {
486
+ return "";
487
+ }
488
+ },
489
+ output: (value) => {
490
+ if (typeof value !== "string" || !value) return null;
491
+ try {
492
+ return JSON.parse(value);
493
+ } catch {
494
+ return null;
495
+ }
496
+ }
497
+ };
498
+ }
499
+ /**
500
+ * Creates a transform for text fields
501
+ *
502
+ * @returns Transform functions for text-based fields
503
+ */
504
+ function createTextTransform() {
505
+ return {
506
+ input: (value) => {
507
+ if (value === null || value === void 0) return "";
508
+ return String(value);
509
+ },
510
+ output: (value) => {
511
+ return String(value || "");
512
+ }
513
+ };
514
+ }
515
+ /**
516
+ * Creates a transform function based on field type
517
+ *
518
+ * @param fieldType The type of field to create transforms for
519
+ * @param adapter Optional adapter for address validation
520
+ * @returns Transform functions for the field type
521
+ */
522
+ function createTransformForFieldType(fieldType, adapter) {
523
+ switch (fieldType) {
524
+ case "blockchain-address":
525
+ if (!adapter) throw new Error(`createTransformForFieldType: Adapter is required for 'blockchain-address' field type but was not provided.`);
526
+ return createAddressTransform(adapter);
527
+ case "number":
528
+ case "amount": return createNumberTransform();
529
+ case "bigint": return createBigIntTransform();
530
+ case "checkbox": return createBooleanTransform();
531
+ case "text":
532
+ case "email":
533
+ case "password":
534
+ case "textarea": return createTextTransform();
535
+ case "array": return createArrayTransform();
536
+ case "object": return createObjectTransform();
537
+ case "array-object": return createArrayObjectTransform();
538
+ case "enum": return createComplexTypeTransform();
539
+ default:
540
+ _openzeppelin_ui_utils.logger.warn("formUtils", `createTransformForFieldType: No specific transform for fieldType "${fieldType}". Falling back to createComplexTypeTransform. Ensure adapter maps all expected ABI types to specific FieldTypes.`);
541
+ return createComplexTypeTransform();
542
+ }
543
+ }
544
+ /**
545
+ * Generate a default value for a given field type based on parameter constraints
546
+ */
547
+ function generateDefaultValue(parameterType, constraints = {}) {
548
+ const type = parameterType.toLowerCase();
549
+ if (type.includes("bool")) return false;
550
+ else if (type.includes("int") || type.includes("number")) return constraints.min !== void 0 ? constraints.min : 0;
551
+ else if (type.includes("string") || type.includes("address")) return "";
552
+ else if (type.includes("array") || type.includes("[]")) return [];
553
+ else return null;
554
+ }
555
+ /**
556
+ * Returns the appropriate default value based on field type
557
+ */
558
+ function getDefaultValueByFieldType(fieldType) {
559
+ switch (fieldType) {
560
+ case "checkbox": return false;
561
+ case "number": return "";
562
+ default: return "";
563
+ }
564
+ }
565
+ /**
566
+ * Creates a complete default values object for form initialization
567
+ * Ensures all fields have appropriate default values to avoid React controlled/uncontrolled input warnings
568
+ *
569
+ * @param fields The form field definitions
570
+ * @param existingDefaults Any existing default values to preserve
571
+ * @returns A complete form values object with no undefined values
572
+ */
573
+ function createDefaultFormValues(fields, existingDefaults = {}) {
574
+ const defaults = { ...existingDefaults };
575
+ if (!fields) return defaults;
576
+ fields.forEach((field) => {
577
+ if (defaults[field.name] === void 0) defaults[field.name] = getDefaultValueByFieldType(field.type);
578
+ });
579
+ return defaults;
580
+ }
581
+ /**
582
+ * Creates a transform for array fields
583
+ *
584
+ * @returns Transform functions for array fields
585
+ */
586
+ function createArrayTransform() {
587
+ return {
588
+ input: (value) => {
589
+ if (!Array.isArray(value)) {
590
+ _openzeppelin_ui_utils.logger.warn("formUtils", "createArrayTransform input received non-array value:", value);
591
+ return "[]";
592
+ }
593
+ try {
594
+ return JSON.stringify(value, null, 2);
595
+ } catch {
596
+ return "[]";
597
+ }
598
+ },
599
+ output: (value) => {
600
+ if (typeof value === "string") try {
601
+ const parsed = JSON.parse(value);
602
+ return Array.isArray(parsed) ? parsed : [];
603
+ } catch {
604
+ return [];
605
+ }
606
+ return Array.isArray(value) ? value : [];
607
+ }
608
+ };
609
+ }
610
+ /**
611
+ * Creates a transform for object fields
612
+ *
613
+ * @returns Transform functions for object fields
614
+ */
615
+ function createObjectTransform() {
616
+ return {
617
+ input: (value) => {
618
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
619
+ _openzeppelin_ui_utils.logger.warn("formUtils", "createObjectTransform input received non-object or null value:", value);
620
+ return "{}";
621
+ }
622
+ try {
623
+ return JSON.stringify(value, null, 2);
624
+ } catch {
625
+ return "{}";
626
+ }
627
+ },
628
+ output: (value) => {
629
+ if (typeof value === "string") {
630
+ if (!value.trim()) return {};
631
+ try {
632
+ const parsed = JSON.parse(value);
633
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) return parsed;
634
+ return {};
635
+ } catch {
636
+ return {};
637
+ }
638
+ }
639
+ if (value && typeof value === "object" && !Array.isArray(value)) return value;
640
+ return {};
641
+ }
642
+ };
643
+ }
644
+ /**
645
+ * Creates a transform for array-object fields
646
+ *
647
+ * @returns Transform functions for array-object fields
648
+ */
649
+ function createArrayObjectTransform() {
650
+ return {
651
+ input: (value) => {
652
+ if (!Array.isArray(value)) {
653
+ _openzeppelin_ui_utils.logger.warn("formUtils", "createArrayObjectTransform input received non-array value:", value);
654
+ return "[]";
655
+ }
656
+ try {
657
+ const validItems = value.filter((item) => item && typeof item === "object" && !Array.isArray(item));
658
+ return JSON.stringify(validItems, null, 2);
659
+ } catch {
660
+ return "[]";
661
+ }
662
+ },
663
+ output: (value) => {
664
+ if (typeof value === "string") {
665
+ if (!value.trim()) return [];
666
+ try {
667
+ const parsed = JSON.parse(value);
668
+ if (Array.isArray(parsed)) return parsed.filter((item) => item && typeof item === "object" && !Array.isArray(item));
669
+ return [];
670
+ } catch {
671
+ return [];
672
+ }
673
+ }
674
+ if (Array.isArray(value)) return value.filter((item) => item && typeof item === "object" && !Array.isArray(item));
675
+ return [];
676
+ }
677
+ };
678
+ }
679
+
680
+ //#endregion
681
+ //#region src/utils/runtimeSecretExtractor.ts
682
+ /**
683
+ * Extracts runtime secrets from form submission data and field configuration.
684
+ *
685
+ * Handles two cases:
686
+ * 1. User-provided runtime secret (field value in form data)
687
+ * 2. Hardcoded readonly runtime secret (from field configuration)
688
+ *
689
+ * Returns both the extracted secrets and the cleaned contract arguments
690
+ * (with runtimeSecret fields removed).
691
+ *
692
+ * @param data - Form submission data from React Hook Form
693
+ * @param fields - Field configuration array from the schema
694
+ * @returns Object containing extracted secrets and cleaned contract arguments
695
+ */
696
+ function extractRuntimeSecrets(data, fields) {
697
+ const runtimeSecrets = {};
698
+ const contractArgs = { ...data };
699
+ _openzeppelin_ui_utils.logger.debug("TransactionForm", "All form data values:", data);
700
+ _openzeppelin_ui_utils.logger.debug("TransactionForm", "Schema fields:", fields.map((f) => ({
701
+ name: f.name,
702
+ type: f.type,
703
+ readOnly: f.readOnly
704
+ })));
705
+ fields.forEach((field) => {
706
+ if (field.type === "runtimeSecret" && field.adapterBinding?.key) {
707
+ let secretValue = data[field.name];
708
+ if (field.readOnly && !secretValue && "hardcodedValue" in field) {
709
+ secretValue = field.hardcodedValue;
710
+ _openzeppelin_ui_utils.logger.debug("TransactionForm", `Using hardcoded value for readonly runtimeSecret field: ${field.name}`, { secretValue });
711
+ }
712
+ _openzeppelin_ui_utils.logger.debug("TransactionForm", `Processing runtimeSecret field: ${field.name}`, {
713
+ secretValue,
714
+ readOnly: field.readOnly
715
+ });
716
+ if (secretValue) runtimeSecrets[field.adapterBinding.key] = secretValue;
717
+ delete contractArgs[field.name];
718
+ }
719
+ });
720
+ _openzeppelin_ui_utils.logger.debug("TransactionForm", "Extracted runtime secrets:", Object.keys(runtimeSecrets), runtimeSecrets);
721
+ return {
722
+ runtimeSecrets,
723
+ contractArgs
724
+ };
725
+ }
726
+
727
+ //#endregion
728
+ //#region src/components/fieldRegistry.ts
729
+ /**
730
+ * Registry of field components mapped to their respective types.
731
+ * All field components in this registry are designed specifically for React Hook Form integration
732
+ * and are meant to be used within the DynamicFormField system, not as standalone components.
733
+ */
734
+ const fieldComponents = {
735
+ text: _openzeppelin_ui_components.TextField,
736
+ number: _openzeppelin_ui_components.NumberField,
737
+ bigint: _openzeppelin_ui_components.BigIntField,
738
+ "blockchain-address": _openzeppelin_ui_components.AddressField,
739
+ checkbox: _openzeppelin_ui_components.BooleanField,
740
+ radio: _openzeppelin_ui_components.RadioField,
741
+ select: _openzeppelin_ui_components.SelectField,
742
+ "select-grouped": _openzeppelin_ui_components.SelectGroupedField,
743
+ textarea: _openzeppelin_ui_components.TextAreaField,
744
+ bytes: _openzeppelin_ui_components.BytesField,
745
+ "code-editor": _openzeppelin_ui_components.CodeEditorField,
746
+ date: () => react.default.createElement("div", null, "Date field not implemented yet"),
747
+ email: () => react.default.createElement("div", null, "Email field not implemented yet"),
748
+ password: _openzeppelin_ui_components.PasswordField,
749
+ amount: _openzeppelin_ui_components.AmountField,
750
+ array: _openzeppelin_ui_components.ArrayField,
751
+ object: _openzeppelin_ui_components.ObjectField,
752
+ "array-object": _openzeppelin_ui_components.ArrayObjectField,
753
+ map: _openzeppelin_ui_components.MapField,
754
+ url: _openzeppelin_ui_components.UrlField,
755
+ enum: _openzeppelin_ui_components.EnumField,
756
+ hidden: () => null,
757
+ "file-upload": _openzeppelin_ui_components.FileUploadField,
758
+ runtimeSecret: _openzeppelin_ui_components.PasswordField
759
+ };
760
+
761
+ //#endregion
762
+ //#region src/components/DynamicFormField.tsx
763
+ /**
764
+ * Evaluates whether a field should be rendered based on its visibility conditions
765
+ */
766
+ function useShouldRenderField(field, control) {
767
+ const formValues = (0, react_hook_form.useWatch)({ control });
768
+ if (field.isHidden) return false;
769
+ if (!field.visibleWhen) return true;
770
+ return (Array.isArray(field.visibleWhen) ? field.visibleWhen : [field.visibleWhen]).every((condition) => {
771
+ const dependentValue = formValues[condition.field];
772
+ switch (condition.operator) {
773
+ case "equals": return dependentValue === condition.value;
774
+ case "notEquals": return dependentValue !== condition.value;
775
+ case "contains": return String(dependentValue).includes(String(condition.value || ""));
776
+ case "greaterThan": return Number(dependentValue) > Number(condition.value || 0);
777
+ case "lessThan": return Number(dependentValue) < Number(condition.value || 0);
778
+ case "matches":
779
+ if (typeof condition.value === "string") return new RegExp(condition.value).test(String(dependentValue || ""));
780
+ return false;
781
+ default: return true;
782
+ }
783
+ });
784
+ }
785
+ /**
786
+ * Dynamic Form Field Component
787
+ *
788
+ * Renders the appropriate field component based on the field type defined in the form schema.
789
+ * This component is part of the app rendering system architecture where:
790
+ * 1. Form schemas are generated from contract functions using adapters
791
+ * 2. The schemas are rendered using the TransactionForm component
792
+ * 3. TransactionForm uses DynamicFormField to render appropriate field components based on the schema
793
+ *
794
+ * The field components (TextField, NumberField, AddressField, etc.) are specifically designed
795
+ * for React Hook Form integration and should not be used as standalone components.
796
+ *
797
+ * @returns The rendered form field component or null if the field should not be visible
798
+ */
799
+ function DynamicFormField({ field, control, adapter, contractSchema }) {
800
+ const renderPayloadField = (0, react.useCallback)((payloadField, payloadIndex) => {
801
+ let enhancedPayloadField;
802
+ if (payloadField.originalParameterType) {
803
+ const generatedField = adapter.generateDefaultField({
804
+ name: payloadField.name || `payload_${payloadIndex}`,
805
+ type: payloadField.originalParameterType
806
+ }, contractSchema);
807
+ enhancedPayloadField = {
808
+ ...generatedField,
809
+ ...payloadField,
810
+ type: generatedField.type,
811
+ label: payloadField.label ?? generatedField.label,
812
+ placeholder: payloadField.placeholder ?? generatedField.placeholder,
813
+ helperText: payloadField.helperText ?? generatedField.helperText
814
+ };
815
+ } else enhancedPayloadField = {
816
+ ...payloadField,
817
+ type: payloadField.type ?? "text"
818
+ };
819
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DynamicFormField, {
820
+ field: enhancedPayloadField,
821
+ control,
822
+ adapter,
823
+ contractSchema
824
+ }, `${field.id}-payload-${payloadIndex}`);
825
+ }, [
826
+ field.id,
827
+ control,
828
+ adapter,
829
+ contractSchema
830
+ ]);
831
+ const renderKeyField = (0, react.useCallback)((keyField, entryIndex) => {
832
+ const mappedKeyType = keyField.originalParameterType ? adapter.mapParameterTypeToFieldType(keyField.originalParameterType) : keyField.type;
833
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DynamicFormField, {
834
+ field: {
835
+ ...keyField,
836
+ type: mappedKeyType,
837
+ readOnly: keyField.readOnly ?? field.readOnly
838
+ },
839
+ control,
840
+ adapter,
841
+ contractSchema
842
+ }, `${field.id}-key-${entryIndex}`);
843
+ }, [
844
+ field.id,
845
+ field.readOnly,
846
+ control,
847
+ adapter,
848
+ contractSchema
849
+ ]);
850
+ const renderValueField = (0, react.useCallback)((valueField, entryIndex) => {
851
+ const mappedValueType = valueField.originalParameterType ? adapter.mapParameterTypeToFieldType(valueField.originalParameterType) : valueField.type;
852
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DynamicFormField, {
853
+ field: {
854
+ ...valueField,
855
+ type: mappedValueType,
856
+ readOnly: valueField.readOnly ?? field.readOnly
857
+ },
858
+ control,
859
+ adapter,
860
+ contractSchema
861
+ }, `${field.id}-value-${entryIndex}`);
862
+ }, [
863
+ field.id,
864
+ field.readOnly,
865
+ control,
866
+ adapter,
867
+ contractSchema
868
+ ]);
869
+ if (!useShouldRenderField(field, control)) return null;
870
+ const FieldComponent = fieldComponents[field.type];
871
+ if (!FieldComponent) {
872
+ _openzeppelin_ui_utils.logger.warn("DynamicFormField", `No component registered for field type: ${field.type}`);
873
+ return null;
874
+ }
875
+ const enhancedProps = {
876
+ ...getFieldSpecificProps(field, {
877
+ renderPayloadField,
878
+ renderKeyField,
879
+ renderValueField
880
+ }, contractSchema),
881
+ ...field.type === "array" && { renderElement: (elementField, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DynamicFormField, {
882
+ field: {
883
+ ...elementField,
884
+ readOnly: elementField.readOnly ?? field.readOnly
885
+ },
886
+ control,
887
+ adapter,
888
+ contractSchema
889
+ }, `${field.id}-element-${index}`) },
890
+ ...field.type === "object" && { renderProperty: (propertyField, propertyName) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DynamicFormField, {
891
+ field: {
892
+ ...propertyField,
893
+ readOnly: propertyField.readOnly ?? field.readOnly
894
+ },
895
+ control,
896
+ adapter,
897
+ contractSchema
898
+ }, `${field.id}-property-${propertyName}`) },
899
+ ...field.type === "array-object" && { renderProperty: (propertyField, itemIndex, propertyName) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DynamicFormField, {
900
+ field: {
901
+ ...propertyField,
902
+ readOnly: propertyField.readOnly ?? field.readOnly
903
+ },
904
+ control,
905
+ adapter,
906
+ contractSchema
907
+ }, `${field.id}-item-${itemIndex}-property-${propertyName}`) }
908
+ };
909
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FieldComponent, {
910
+ id: field.id,
911
+ label: field.label,
912
+ placeholder: field.placeholder,
913
+ helperText: field.helperText,
914
+ width: field.width,
915
+ validation: field.validation,
916
+ control,
917
+ name: field.name,
918
+ adapter,
919
+ readOnly: field.readOnly,
920
+ contractSchema,
921
+ ...enhancedProps
922
+ });
923
+ }
924
+ /**
925
+ * Extract field-specific props based on field type
926
+ */
927
+ function getFieldSpecificProps(field, renderFunctions, contractSchema) {
928
+ switch (field.type) {
929
+ case "number": return {
930
+ min: field.validation?.min,
931
+ max: field.validation?.max,
932
+ step: field.options?.find((opt) => opt.label === "step")?.value
933
+ };
934
+ case "array": return {
935
+ elementType: field.elementType || "text",
936
+ minItems: field.validation?.min,
937
+ maxItems: field.validation?.max,
938
+ elementFieldConfig: field.elementFieldConfig
939
+ };
940
+ case "object": return {
941
+ components: field.components || [],
942
+ showCard: true,
943
+ contractSchema
944
+ };
945
+ case "array-object": return {
946
+ components: field.components || [],
947
+ minItems: field.validation?.min,
948
+ maxItems: field.validation?.max,
949
+ collapsible: true,
950
+ defaultCollapsed: false
951
+ };
952
+ case "blockchain-address": return {};
953
+ case "checkbox": return {};
954
+ case "code-editor": return {
955
+ language: field.codeEditorProps?.language || "json",
956
+ theme: field.codeEditorProps?.theme || "light",
957
+ height: field.codeEditorProps?.height || "200px",
958
+ maxHeight: field.codeEditorProps?.maxHeight || "400px",
959
+ performanceThreshold: field.codeEditorProps?.performanceThreshold || 5e3
960
+ };
961
+ case "enum": return {
962
+ enumMetadata: field.enumMetadata,
963
+ renderPayloadField: renderFunctions.renderPayloadField
964
+ };
965
+ case "select": return {
966
+ options: field.options || [],
967
+ defaultValue: field.defaultValue || void 0
968
+ };
969
+ case "radio": return { options: field.options || [] };
970
+ case "map": return {
971
+ mapMetadata: field.mapMetadata,
972
+ minItems: field.validation?.min,
973
+ renderKeyField: renderFunctions.renderKeyField,
974
+ renderValueField: renderFunctions.renderValueField
975
+ };
976
+ default: return {};
977
+ }
978
+ }
979
+
980
+ //#endregion
981
+ //#region src/components/transaction/TransactionHashDisplay.tsx
982
+ /**
983
+ * Renders a transaction hash with proper formatting and optional explorer link.
984
+ * This component ensures transaction hashes are displayed in a consistent way
985
+ * with proper word-breaking for long strings.
986
+ */
987
+ function TransactionHashDisplay({ txHash, explorerUrl }) {
988
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
989
+ className: "text-sm mt-2 relative",
990
+ children: [
991
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
992
+ className: "text-muted-foreground text-xs mb-1",
993
+ children: "Transaction:"
994
+ }),
995
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
996
+ className: "mb-2",
997
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
998
+ className: "font-mono text-xs bg-gray-100 px-2 py-1.5 rounded break-all inline-block",
999
+ children: txHash
1000
+ })
1001
+ }),
1002
+ explorerUrl && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1003
+ className: "relative z-10 pointer-events-auto",
1004
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("a", {
1005
+ href: explorerUrl,
1006
+ target: "_blank",
1007
+ rel: "noopener noreferrer",
1008
+ className: "inline-flex items-center text-xs text-primary hover:text-primary/80 hover:underline py-1 px-2 cursor-pointer",
1009
+ onClick: () => {
1010
+ _openzeppelin_ui_utils.logger.info("TransactionHashDisplay", "Explorer link clicked", explorerUrl);
1011
+ },
1012
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ExternalLink, {
1013
+ size: 12,
1014
+ className: "mr-1 flex-shrink-0"
1015
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "View on explorer" })]
1016
+ })
1017
+ })
1018
+ ]
1019
+ });
1020
+ }
1021
+
1022
+ //#endregion
1023
+ //#region src/components/transaction/TransactionStatusDisplay.tsx
1024
+ /**
1025
+ * Helper function to format error messages that contain transaction hashes
1026
+ * This adds proper word breaking for transaction hashes while maintaining readability
1027
+ */
1028
+ function formatErrorWithHash(errorMsg) {
1029
+ if (!errorMsg) return "An unknown error occurred.";
1030
+ const hashRegex = /(0x[a-fA-F0-9]{40,})/g;
1031
+ if (!hashRegex.test(errorMsg)) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1032
+ className: "break-word",
1033
+ children: errorMsg
1034
+ });
1035
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1036
+ className: "break-word",
1037
+ children: errorMsg.split(hashRegex).map((part, i) => {
1038
+ if (part.match(/^0x[a-fA-F0-9]{40,}$/)) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
1039
+ className: "font-mono px-1 py-0.5 bg-gray-100 rounded text-xs break-all",
1040
+ children: part
1041
+ }, i);
1042
+ return part;
1043
+ })
1044
+ });
1045
+ }
1046
+ /** Displays the current status of a transaction with appropriate icons and messages. */
1047
+ function TransactionStatusDisplay({ status, txHash, error, explorerUrl, className, customTitle, customMessage, result, functionDetails, adapter }) {
1048
+ if (status === "idle") return null;
1049
+ let variant = "default";
1050
+ let defaultTitle = "";
1051
+ let defaultMessage = null;
1052
+ let icon = null;
1053
+ if (status === "pendingSignature") {
1054
+ defaultTitle = "Pending Signature";
1055
+ icon = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Loader2, { className: "size-5 animate-spin text-primary" });
1056
+ defaultMessage = "Please check your wallet to sign. After signing, your transaction will be submitted and confirmed automatically.";
1057
+ variant = "default";
1058
+ } else if (status === "pendingConfirmation") {
1059
+ defaultTitle = "Processing Transaction";
1060
+ icon = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Loader2, { className: "size-5 animate-spin text-primary" });
1061
+ defaultMessage = "Waiting for the transaction to be confirmed on the blockchain...";
1062
+ variant = "default";
1063
+ } else if (status === "pendingRelayer") {
1064
+ defaultTitle = "Waiting for Relayer";
1065
+ icon = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Loader2, { className: "size-5 animate-spin text-primary" });
1066
+ defaultMessage = "The transaction is pending with the relayer and will be submitted shortly.";
1067
+ variant = "default";
1068
+ } else if (status === "success") {
1069
+ defaultTitle = "Transaction Successful";
1070
+ icon = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.CheckCircle, { className: "size-5 text-green-600" });
1071
+ defaultMessage = "Your transaction has been confirmed.";
1072
+ variant = "success";
1073
+ } else if (status === "error") {
1074
+ defaultTitle = "Transaction Failed";
1075
+ icon = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.AlertCircle, { className: "size-5 text-destructive" });
1076
+ variant = "destructive";
1077
+ }
1078
+ const title = customTitle || defaultTitle;
1079
+ let formattedResult = null;
1080
+ if (result !== void 0 && result !== null && adapter && functionDetails) try {
1081
+ formattedResult = adapter.formatFunctionResult(result, functionDetails);
1082
+ } catch {
1083
+ formattedResult = JSON.stringify(result, null, 2);
1084
+ }
1085
+ else if (result !== void 0 && result !== null) formattedResult = JSON.stringify(result, null, 2);
1086
+ let content = null;
1087
+ if (status === "error") content = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [error ? formatErrorWithHash(error) : customMessage ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1088
+ className: "break-word",
1089
+ children: customMessage
1090
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1091
+ className: "break-word",
1092
+ children: "An unknown error occurred."
1093
+ }), txHash && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TransactionHashDisplay, {
1094
+ txHash,
1095
+ explorerUrl: explorerUrl || null
1096
+ })] });
1097
+ else {
1098
+ const messageText = customMessage || defaultMessage || "";
1099
+ content = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1100
+ className: "space-y-3",
1101
+ children: [
1102
+ messageText && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: messageText }),
1103
+ txHash && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TransactionHashDisplay, {
1104
+ txHash,
1105
+ explorerUrl: explorerUrl || null
1106
+ }),
1107
+ formattedResult && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1108
+ className: "mt-3 pt-3 border-t border-border",
1109
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1110
+ className: "text-sm font-medium mb-2",
1111
+ children: "Result:"
1112
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
1113
+ className: "text-xs bg-muted p-3 rounded-md overflow-auto max-h-48",
1114
+ children: formattedResult
1115
+ })]
1116
+ })
1117
+ ]
1118
+ });
1119
+ }
1120
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_openzeppelin_ui_components.Alert, {
1121
+ variant,
1122
+ className: (0, _openzeppelin_ui_utils.cn)("relative py-4 px-5 overflow-hidden", className),
1123
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1124
+ className: "flex items-start",
1125
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1126
+ className: "shrink-0 mr-3 mt-0.5",
1127
+ children: icon
1128
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1129
+ className: "flex-1 min-w-0",
1130
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_openzeppelin_ui_components.AlertTitle, {
1131
+ className: "mb-1 text-base font-medium",
1132
+ children: title
1133
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_openzeppelin_ui_components.AlertDescription, {
1134
+ className: "text-sm overflow-hidden",
1135
+ children: content
1136
+ })]
1137
+ })]
1138
+ })
1139
+ });
1140
+ }
1141
+
1142
+ //#endregion
1143
+ //#region src/components/TransactionForm.tsx
1144
+ /**
1145
+ * Transaction states that should disable the form (pending operations)
1146
+ */
1147
+ const PENDING_STATES = [
1148
+ "pendingSignature",
1149
+ "pendingConfirmation",
1150
+ "pendingRelayer"
1151
+ ];
1152
+ /**
1153
+ * Transaction Form Component
1154
+ *
1155
+ * This is the main entry point for the app rendering system. It represents the top level of
1156
+ * the app rendering architecture:
1157
+ *
1158
+ * 1. TransactionForm receives a schema and adapter from the transaction builder app
1159
+ * 2. It sets up React Hook Form for state management and validation
1160
+ * 3. It renders fields dynamically using the DynamicFormField component
1161
+ * 4. Provides wallet connection UI (demo implementation)
1162
+ * 5. On submission, it processes data through the adapter before passing to handlers
1163
+ *
1164
+ * Note: The previewMode prop is currently used only for demo purposes and does not affect
1165
+ * the visibility of wallet connection or transaction execution UI. In the future, it will be used
1166
+ * to enable/disable actual blockchain interactions without changing the UI structure.
1167
+ *
1168
+ * @returns The rendered form component
1169
+ */
1170
+ function TransactionForm({ schema, contractSchema, adapter, isWalletConnected = false, executionConfig }) {
1171
+ const [formError, setFormError] = (0, react.useState)(null);
1172
+ const [executionConfigError, setExecutionConfigError] = (0, react.useState)(null);
1173
+ const [runtimeApiKey, setRuntimeApiKey] = (0, react.useState)("");
1174
+ const [txStatus, setTxStatus] = (0, react.useState)("idle");
1175
+ const [txHash, setTxHash] = (0, react.useState)(null);
1176
+ const [txError, setTxError] = (0, react.useState)(null);
1177
+ const [txStatusDetails, setTxStatusDetails] = (0, react.useState)(null);
1178
+ const [txResult, setTxResult] = (0, react.useState)(null);
1179
+ const networkConfig = adapter.networkConfig;
1180
+ const methods = (0, react_hook_form.useForm)({
1181
+ mode: schema.validation?.mode || "onChange",
1182
+ defaultValues: createDefaultFormValues(schema.fields, schema.defaultValues)
1183
+ });
1184
+ const { isValid } = methods.formState;
1185
+ const currentFunction = contractSchema?.functions.find((fn) => fn.id === schema.functionId);
1186
+ const canExecuteLocally = currentFunction?.stateMutability === "pure";
1187
+ (0, react.useEffect)(() => {
1188
+ methods.reset(createDefaultFormValues(schema.fields, schema.defaultValues));
1189
+ setTxStatus("idle");
1190
+ setTxHash(null);
1191
+ setTxError(null);
1192
+ setFormError(null);
1193
+ setExecutionConfigError(null);
1194
+ setTxStatusDetails(null);
1195
+ setTxResult(null);
1196
+ }, [schema, methods]);
1197
+ (0, react.useEffect)(() => {
1198
+ const validateExecConfig = async () => {
1199
+ if (executionConfig && adapter && adapter.validateExecutionConfig) try {
1200
+ _openzeppelin_ui_utils.logger.info("TransactionForm", "Re-validating execution config:", executionConfig);
1201
+ const validationResult = await adapter.validateExecutionConfig(executionConfig);
1202
+ if (typeof validationResult === "string") setExecutionConfigError(`Execution Configuration Error: ${validationResult}`);
1203
+ else setExecutionConfigError(null);
1204
+ } catch (error) {
1205
+ _openzeppelin_ui_utils.logger.error("TransactionForm", "Error reactive exec config validation:", error);
1206
+ setExecutionConfigError(`Exec Config Validation Failed: ${error instanceof Error ? error.message : "Unknown error"}`);
1207
+ }
1208
+ else setExecutionConfigError(null);
1209
+ };
1210
+ validateExecConfig();
1211
+ }, [
1212
+ executionConfig,
1213
+ adapter,
1214
+ isWalletConnected
1215
+ ]);
1216
+ const executeTransaction = async (data) => {
1217
+ _openzeppelin_ui_utils.logger.info("TransactionForm", "Internal form submission attempt", data);
1218
+ setTxStatus("idle");
1219
+ setTxHash(null);
1220
+ setTxError(null);
1221
+ setTxStatusDetails(null);
1222
+ setTxResult(null);
1223
+ if (!adapter) {
1224
+ _openzeppelin_ui_utils.logger.error("TransactionForm", "Adapter not provided.");
1225
+ setTxError("Configuration error: Adapter not available.");
1226
+ setTxStatus("error");
1227
+ return;
1228
+ }
1229
+ if (!canExecuteLocally && !isWalletConnected) {
1230
+ _openzeppelin_ui_utils.logger.warn("TransactionForm", "Wallet not connected for submission.");
1231
+ setTxError("Please connect your wallet to submit the transaction.");
1232
+ setTxStatus("error");
1233
+ return;
1234
+ }
1235
+ try {
1236
+ const { contractArgs, runtimeSecrets } = extractRuntimeSecrets(data, schema.fields);
1237
+ const formattedData = adapter.formatTransactionData(contractSchema, schema.functionId, contractArgs, schema.fields);
1238
+ _openzeppelin_ui_utils.logger.info("TransactionForm", "Formatted transaction data:", formattedData);
1239
+ const onStatusChange = (status, details) => {
1240
+ _openzeppelin_ui_utils.logger.info("TransactionForm", `Status Update: ${status}`, details);
1241
+ setTxStatus(status);
1242
+ setTxStatusDetails(details);
1243
+ if (details.transactionId) setTxHash(details.transactionId);
1244
+ if (details.txHash) setTxHash(details.txHash);
1245
+ };
1246
+ const firstSecretValue = Object.values(runtimeSecrets)[0];
1247
+ const { txHash: finalTxHash, result } = await adapter.signAndBroadcast(formattedData, executionConfig || {
1248
+ method: "eoa",
1249
+ allowAny: true
1250
+ }, onStatusChange, runtimeApiKey, firstSecretValue);
1251
+ _openzeppelin_ui_utils.logger.info("TransactionForm", `Transaction submitted with final hash: ${finalTxHash}`);
1252
+ setTxHash(finalTxHash);
1253
+ if (result !== void 0) {
1254
+ setTxResult(result);
1255
+ _openzeppelin_ui_utils.logger.info("TransactionForm", "Execution result received:", result);
1256
+ }
1257
+ if (canExecuteLocally) {
1258
+ setTxStatus("success");
1259
+ setTxError(null);
1260
+ return;
1261
+ }
1262
+ if (adapter.waitForTransactionConfirmation) {
1263
+ setTxStatus("pendingConfirmation");
1264
+ _openzeppelin_ui_utils.logger.info("TransactionForm", `Waiting for confirmation for tx: ${finalTxHash}`);
1265
+ const confirmationResult = await adapter.waitForTransactionConfirmation(finalTxHash);
1266
+ if (confirmationResult.status === "success") {
1267
+ _openzeppelin_ui_utils.logger.info("TransactionForm", `Transaction confirmed: ${finalTxHash}`, confirmationResult.receipt);
1268
+ setTxStatus("success");
1269
+ setTxError(null);
1270
+ } else {
1271
+ _openzeppelin_ui_utils.logger.error("TransactionForm", `Transaction failed confirmation: ${finalTxHash}`, confirmationResult.error);
1272
+ setTxError(confirmationResult.error?.message ?? "Transaction failed during confirmation.");
1273
+ setTxStatus("error");
1274
+ }
1275
+ } else {
1276
+ _openzeppelin_ui_utils.logger.warn("TransactionForm", "Adapter does not support waitForTransactionConfirmation. Marking as success after submission.");
1277
+ setTxStatus("success");
1278
+ setTxError(null);
1279
+ }
1280
+ } catch (error) {
1281
+ _openzeppelin_ui_utils.logger.error("TransactionForm", "Transaction error during submission process:", error);
1282
+ setTxError(error instanceof Error ? error.message : "An unknown error occurred.");
1283
+ setTxStatus("error");
1284
+ }
1285
+ };
1286
+ const renderFormContent = () => {
1287
+ if (!schema.fields || schema.fields.length === 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1288
+ className: "form-empty-state",
1289
+ children: "No fields defined in schema"
1290
+ });
1291
+ const { errors } = methods.formState;
1292
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1293
+ className: "form-fields-container space-y-4",
1294
+ children: schema.fields.map((field) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DynamicFormField, {
1295
+ field,
1296
+ control: methods.control,
1297
+ error: errors[field.name]?.message,
1298
+ adapter,
1299
+ contractSchema
1300
+ }, field.id))
1301
+ });
1302
+ };
1303
+ const getLayoutClasses = () => {
1304
+ return "grid grid-cols-1 gap-4";
1305
+ };
1306
+ const getButtonVariant = () => {
1307
+ const { submitButton } = schema;
1308
+ if (!submitButton?.variant) return "default";
1309
+ switch (submitButton.variant) {
1310
+ case "primary": return "default";
1311
+ case "secondary": return "secondary";
1312
+ case "outline": return "outline";
1313
+ default: return "default";
1314
+ }
1315
+ };
1316
+ const getExplorerTxUrl = (hash) => {
1317
+ if (!adapter || !hash || !networkConfig) return null;
1318
+ if (adapter.getExplorerTxUrl) return adapter.getExplorerTxUrl(hash);
1319
+ _openzeppelin_ui_utils.logger.warn("TransactionForm", "getExplorerTxUrl not implemented by adapter, trying getExplorerUrl as fallback (might expect address).");
1320
+ return adapter.getExplorerUrl ? adapter.getExplorerUrl(hash) : null;
1321
+ };
1322
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_hook_form.FormProvider, {
1323
+ ...methods,
1324
+ children: [
1325
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1326
+ className: "mb-4 flex items-center justify-between",
1327
+ children: schema.title && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
1328
+ className: "text-xl font-bold",
1329
+ children: schema.title
1330
+ })
1331
+ }),
1332
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1333
+ className: "description-container mb-6",
1334
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1335
+ className: "text-muted-foreground rounded-md border border-gray-100 bg-gray-50 p-3 text-sm",
1336
+ children: schema.description || "No description provided."
1337
+ })
1338
+ }),
1339
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1340
+ className: "flex flex-col space-y-4",
1341
+ children: [
1342
+ formError && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1343
+ className: "form-error rounded border border-red-400 bg-red-100 px-4 py-3 text-red-700",
1344
+ children: formError
1345
+ }),
1346
+ txStatus !== "idle" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1347
+ className: "mb-8 pointer-events-auto",
1348
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TransactionStatusDisplay, {
1349
+ status: txStatus,
1350
+ txHash,
1351
+ error: txError,
1352
+ explorerUrl: txHash ? getExplorerTxUrl(txHash) : null,
1353
+ customTitle: txStatusDetails?.title,
1354
+ customMessage: txStatusDetails?.message,
1355
+ result: txResult,
1356
+ functionDetails: currentFunction,
1357
+ adapter
1358
+ })
1359
+ }),
1360
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("form", {
1361
+ className: `transaction-form flex flex-col ${getLayoutClasses()} ${PENDING_STATES.includes(txStatus) ? "opacity-70 pointer-events-none" : ""}`,
1362
+ noValidate: true,
1363
+ onSubmit: methods.handleSubmit(executeTransaction),
1364
+ children: [
1365
+ executionConfigError && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1366
+ className: "form-error rounded border border-red-400 bg-red-100 px-4 py-3 text-red-700",
1367
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.AlertCircle, { className: "mr-2 h-4 w-4" }), executionConfigError]
1368
+ }),
1369
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1370
+ className: "mb-6",
1371
+ children: renderFormContent()
1372
+ }),
1373
+ executionConfig && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1374
+ className: "w-full",
1375
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ExecutionConfigDisplay, {
1376
+ executionConfig,
1377
+ adapter,
1378
+ error: executionConfigError,
1379
+ onRuntimeApiKeyChange: setRuntimeApiKey
1380
+ })
1381
+ }),
1382
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1383
+ className: "mt-4 border-t border-gray-100 pt-4",
1384
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1385
+ className: "flex justify-end items-center gap-2",
1386
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TransactionExecuteButton, {
1387
+ isWalletConnected,
1388
+ isSubmitting: txStatus === "pendingSignature" || txStatus === "pendingConfirmation",
1389
+ isFormValid: isValid && executionConfigError === null,
1390
+ variant: getButtonVariant(),
1391
+ functionDetails: currentFunction,
1392
+ canExecuteLocally
1393
+ })
1394
+ })
1395
+ })
1396
+ ]
1397
+ })
1398
+ ]
1399
+ })
1400
+ ]
1401
+ });
1402
+ }
1403
+
1404
+ //#endregion
1405
+ //#region src/components/ContractStateWidget/components/FunctionResult.tsx
1406
+ /**
1407
+ * Component for displaying formatted function results (strings)
1408
+ */
1409
+ function FunctionResult({ functionDetails, result, loading }) {
1410
+ const formattedResult = result ?? "";
1411
+ const hasResult = typeof result === "string";
1412
+ const isError = hasResult && (result.startsWith("Error:") || result.startsWith("[Error:"));
1413
+ const headerRef = (0, react.useRef)(null);
1414
+ const [fontSize, setFontSize] = (0, react.useState)("xs");
1415
+ const hasScaledDownRef = (0, react.useRef)(false);
1416
+ const lastContentRef = (0, react.useRef)("");
1417
+ const outputs = (0, react.useMemo)(() => functionDetails.outputs || [], [functionDetails.outputs]);
1418
+ const currentContent = `${functionDetails.name}-${outputs.map((o) => o.type).join(",")}`;
1419
+ (0, react.useEffect)(() => {
1420
+ if (lastContentRef.current !== currentContent) {
1421
+ lastContentRef.current = currentContent;
1422
+ hasScaledDownRef.current = false;
1423
+ setFontSize("xs");
1424
+ return;
1425
+ }
1426
+ const checkOverflow = () => {
1427
+ if (!headerRef.current) return;
1428
+ const container = headerRef.current;
1429
+ if (container.scrollWidth > container.clientWidth && fontSize === "xs" && !hasScaledDownRef.current) {
1430
+ hasScaledDownRef.current = true;
1431
+ setFontSize("2xs");
1432
+ }
1433
+ };
1434
+ const timeoutId = setTimeout(checkOverflow, 10);
1435
+ return () => {
1436
+ clearTimeout(timeoutId);
1437
+ };
1438
+ }, [
1439
+ functionDetails.name,
1440
+ outputs,
1441
+ fontSize,
1442
+ currentContent
1443
+ ]);
1444
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1445
+ className: "border rounded-sm p-2",
1446
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1447
+ ref: headerRef,
1448
+ className: (0, _openzeppelin_ui_utils.cn)("font-medium mb-1 flex flex-wrap items-baseline gap-x-2 gap-y-1 leading-tight overflow-hidden", fontSize === "xs" ? "text-xs" : "text-[10px]"),
1449
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1450
+ className: "flex-shrink-0 min-w-0 break-all",
1451
+ children: functionDetails.name
1452
+ }), outputs.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1453
+ className: "text-muted-foreground flex-shrink-0 whitespace-nowrap",
1454
+ children: `→ ${outputs.map((o) => o.type).join(", ")}`
1455
+ })]
1456
+ }), loading ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1457
+ className: "text-xs text-muted-foreground italic animate-pulse",
1458
+ children: "Loading..."
1459
+ }) : hasResult ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
1460
+ className: (0, _openzeppelin_ui_utils.cn)("text-xs p-1 max-h-24 bg-muted overflow-auto rounded whitespace-pre-wrap break-all", "animate-fade-in", isError && "text-destructive"),
1461
+ children: formattedResult
1462
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1463
+ className: "text-xs text-muted-foreground italic",
1464
+ children: "Click Refresh to fetch result"
1465
+ })]
1466
+ });
1467
+ }
1468
+
1469
+ //#endregion
1470
+ //#region src/components/ContractStateWidget/components/ViewFunctionsPanel.tsx
1471
+ /**
1472
+ * Panel for displaying and querying simple view functions (functions without parameters)
1473
+ */
1474
+ function ViewFunctionsPanel({ functions, contractAddress, adapter, contractSchema, className }) {
1475
+ const safeFunctions = adapter.filterAutoQueryableFunctions ? adapter.filterAutoQueryableFunctions(functions) : functions;
1476
+ const [results, setResults] = (0, react.useState)({});
1477
+ const [loadingStates, setLoadingStates] = (0, react.useState)({});
1478
+ const [hasQueried, setHasQueried] = (0, react.useState)(false);
1479
+ const [isQueryInProgress, setIsQueryInProgress] = (0, react.useState)(false);
1480
+ const handleQueryAll = (0, react.useCallback)(async () => {
1481
+ if (safeFunctions.length === 0) return;
1482
+ if (isQueryInProgress) {
1483
+ _openzeppelin_ui_utils.logger.info("ViewFunctionsPanel", "Query already in progress, skipping...");
1484
+ return;
1485
+ }
1486
+ setIsQueryInProgress(true);
1487
+ const initialLoadingStates = {};
1488
+ safeFunctions.forEach((func) => {
1489
+ initialLoadingStates[func.id] = true;
1490
+ });
1491
+ setLoadingStates(initialLoadingStates);
1492
+ try {
1493
+ await (0, _openzeppelin_ui_utils.rateLimitedBatch)(safeFunctions.map((func) => async () => {
1494
+ try {
1495
+ const result = await adapter.queryViewFunction(contractAddress, func.id, [], contractSchema);
1496
+ let formattedResult;
1497
+ try {
1498
+ formattedResult = adapter.formatFunctionResult(result, func);
1499
+ } catch (formatError) {
1500
+ _openzeppelin_ui_utils.logger.error("ViewFunctionsPanel", `Error formatting result for ${func.name}:`, formatError);
1501
+ formattedResult = `Error formatting result: ${formatError instanceof Error ? formatError.message : "Unknown error"}`;
1502
+ }
1503
+ setResults((prev) => ({
1504
+ ...prev,
1505
+ [func.id]: formattedResult
1506
+ }));
1507
+ setLoadingStates((prev) => ({
1508
+ ...prev,
1509
+ [func.id]: false
1510
+ }));
1511
+ return {
1512
+ funcId: func.id,
1513
+ success: true
1514
+ };
1515
+ } catch (err) {
1516
+ _openzeppelin_ui_utils.logger.error("ViewFunctionsPanel", `Error calling ${func.name}:`, err);
1517
+ const errorMessage = `Error: ${err instanceof Error ? err.message : "Unknown error"}`;
1518
+ setResults((prev) => ({
1519
+ ...prev,
1520
+ [func.id]: errorMessage
1521
+ }));
1522
+ setLoadingStates((prev) => ({
1523
+ ...prev,
1524
+ [func.id]: false
1525
+ }));
1526
+ return {
1527
+ funcId: func.id,
1528
+ success: false
1529
+ };
1530
+ }
1531
+ }), 1, 200);
1532
+ } catch (err) {
1533
+ _openzeppelin_ui_utils.logger.error("ViewFunctionsPanel", "Error querying functions:", err);
1534
+ setLoadingStates({});
1535
+ } finally {
1536
+ setHasQueried(true);
1537
+ setIsQueryInProgress(false);
1538
+ }
1539
+ }, [
1540
+ safeFunctions,
1541
+ contractAddress,
1542
+ adapter,
1543
+ contractSchema,
1544
+ isQueryInProgress
1545
+ ]);
1546
+ (0, react.useEffect)(() => {
1547
+ let mounted = true;
1548
+ let timeoutId = null;
1549
+ const performInitialQuery = async () => {
1550
+ if (safeFunctions.length > 0 && mounted && !hasQueried && !isQueryInProgress) timeoutId = setTimeout(() => {
1551
+ if (mounted) handleQueryAll();
1552
+ }, 100);
1553
+ };
1554
+ performInitialQuery();
1555
+ return () => {
1556
+ mounted = false;
1557
+ if (timeoutId) clearTimeout(timeoutId);
1558
+ };
1559
+ }, [
1560
+ safeFunctions.length,
1561
+ hasQueried,
1562
+ isQueryInProgress,
1563
+ handleQueryAll
1564
+ ]);
1565
+ (0, react.useEffect)(() => {
1566
+ const networkId = adapter.networkConfig?.id;
1567
+ if (!networkId) return;
1568
+ return _openzeppelin_ui_utils.userRpcConfigService.subscribe(networkId, (event) => {
1569
+ _openzeppelin_ui_utils.logger.info("ViewFunctionsPanel", "RPC configuration changed:", event);
1570
+ handleQueryAll();
1571
+ });
1572
+ }, [adapter.networkConfig?.id, handleQueryAll]);
1573
+ if (safeFunctions.length === 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1574
+ className: "text-xs text-muted-foreground",
1575
+ children: "No simple view functions found in this contract."
1576
+ });
1577
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1578
+ className: (0, _openzeppelin_ui_utils.cn)("space-y-4", className),
1579
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1580
+ className: "flex items-center justify-between",
1581
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", {
1582
+ className: "text-xs font-medium",
1583
+ children: "View Functions"
1584
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_openzeppelin_ui_components.Button, {
1585
+ onClick: () => {
1586
+ setHasQueried(false);
1587
+ handleQueryAll();
1588
+ },
1589
+ disabled: isQueryInProgress,
1590
+ size: "sm",
1591
+ variant: "outline",
1592
+ className: "h-6 w-6 p-0 rounded-full",
1593
+ title: "Refresh all view functions",
1594
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.RefreshCw, {
1595
+ size: 14,
1596
+ className: `${isQueryInProgress ? "animate-spin" : ""}`
1597
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1598
+ className: "sr-only",
1599
+ children: isQueryInProgress ? "Querying..." : "Refresh All"
1600
+ })]
1601
+ })]
1602
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1603
+ className: "space-y-2 overflow-y-auto pr-1 flex-grow min-h-0",
1604
+ children: safeFunctions.map((func) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FunctionResult, {
1605
+ functionDetails: func,
1606
+ result: results[func.id],
1607
+ loading: loadingStates[func.id] || false
1608
+ }, func.id))
1609
+ })]
1610
+ });
1611
+ }
1612
+
1613
+ //#endregion
1614
+ //#region src/components/ContractStateWidget/ContractStateWidget.tsx
1615
+ /**
1616
+ * ContractStateWidget - Compact widget for displaying contract state
1617
+ * Shows contract state by allowing users to query simple view functions (no parameters)
1618
+ */
1619
+ function ContractStateWidget({ contractSchema, contractAddress, adapter, isVisible = true, onToggle, className, error }) {
1620
+ const [viewFunctions, setViewFunctions] = (0, react.useState)([]);
1621
+ const [animationState, setAnimationState] = (0, react.useState)(isVisible ? "entered" : "exited");
1622
+ const networkConfig = adapter?.networkConfig;
1623
+ const [lastSchema, setLastSchema] = (0, react.useState)(contractSchema ?? null);
1624
+ (0, react.useEffect)(() => {
1625
+ if (contractSchema) setLastSchema(contractSchema);
1626
+ }, [contractSchema]);
1627
+ const effectiveSchema = (0, react.useMemo)(() => contractSchema ?? lastSchema, [contractSchema, lastSchema]);
1628
+ (0, react.useEffect)(() => {
1629
+ if (!effectiveSchema || !adapter) return;
1630
+ setViewFunctions(effectiveSchema.functions.filter((fn) => adapter.isViewFunction(fn)).filter((fn) => fn.inputs.length === 0));
1631
+ }, [effectiveSchema, adapter]);
1632
+ (0, react.useEffect)(() => {
1633
+ if (isVisible) {
1634
+ setAnimationState("entering");
1635
+ const timer = setTimeout(() => {
1636
+ setAnimationState("entered");
1637
+ }, 300);
1638
+ return () => clearTimeout(timer);
1639
+ } else {
1640
+ setAnimationState("exiting");
1641
+ const timer = setTimeout(() => {
1642
+ setAnimationState("exited");
1643
+ }, 500);
1644
+ return () => clearTimeout(timer);
1645
+ }
1646
+ }, [isVisible]);
1647
+ const handleToggle = () => {
1648
+ if (onToggle) onToggle();
1649
+ };
1650
+ if (!contractAddress || !adapter || !networkConfig) return null;
1651
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(animationState === "entering" || animationState === "entered" || animationState === "exiting") && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1652
+ className: (0, _openzeppelin_ui_utils.cn)("fixed inset-0 z-[9998] md:hidden bg-background/60 backdrop-blur-sm transition-opacity duration-500 ease-in-out", animationState === "entering" || animationState === "entered" ? "opacity-100" : "opacity-0"),
1653
+ "aria-hidden": "true",
1654
+ onClick: onToggle ? handleToggle : void 0
1655
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_openzeppelin_ui_components.Card, {
1656
+ className: (0, _openzeppelin_ui_utils.cn)("overflow-hidden p-0 gap-0 flex flex-col transition-transform duration-500 ease-in-out will-change-transform transform-gpu", animationState === "entering" || animationState === "entered" ? "translate-y-0" : "translate-y-[120%]", animationState === "entering" || animationState === "entered" ? "md:scale-100 md:opacity-100" : "md:scale-95 md:opacity-0", "fixed bottom-4 inset-x-4 z-[9999] max-h-[50vh] shadow-xl bg-background backdrop-blur-md rounded-lg border border-border", "md:relative md:inset-auto md:bottom-auto md:shadow-none md:bg-card md:backdrop-blur-none md:z-auto md:mb-2 md:max-h-160", (animationState === "exiting" || animationState === "exited") && "pointer-events-none", animationState === "exited" && "md:hidden", className),
1657
+ "aria-hidden": animationState === "exited",
1658
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_openzeppelin_ui_components.CardHeader, {
1659
+ className: "pb-2 pt-2 px-3 flex-shrink-0 flex flex-row items-center justify-between",
1660
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_openzeppelin_ui_components.CardTitle, {
1661
+ className: "text-sm font-medium",
1662
+ children: "Contract State"
1663
+ }), onToggle && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_openzeppelin_ui_components.Button, {
1664
+ variant: "ghost",
1665
+ size: "sm",
1666
+ className: "h-7 w-7 p-0",
1667
+ onClick: handleToggle,
1668
+ title: "Minimize Contract State",
1669
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Minimize2, { size: 14 }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1670
+ className: "sr-only",
1671
+ children: "Minimize Contract State"
1672
+ })]
1673
+ })]
1674
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_openzeppelin_ui_components.CardContent, {
1675
+ className: "space-y-3 px-3 py-2 flex-grow overflow-y-auto flex flex-col min-h-0",
1676
+ children: error ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1677
+ className: "text-sm text-red-500 bg-red-50 border border-red-200 rounded-md p-3 flex flex-col items-center justify-center h-full",
1678
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1679
+ className: "font-medium text-center",
1680
+ children: "Error loading contract state"
1681
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1682
+ className: "mt-1 text-xs text-center",
1683
+ children: error.message
1684
+ })]
1685
+ }) : !effectiveSchema && !lastSchema || !adapter ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1686
+ className: "flex flex-col items-center justify-center h-full space-y-3 py-6",
1687
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Loader2, { className: "h-8 w-8 text-primary animate-spin opacity-70" }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1688
+ className: "text-center space-y-1",
1689
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1690
+ className: "text-sm font-medium text-muted-foreground",
1691
+ children: "Loading contract info..."
1692
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1693
+ className: "text-xs text-muted-foreground",
1694
+ children: "Retrieving contract data and available functions"
1695
+ })]
1696
+ })]
1697
+ }) : viewFunctions.length > 0 && effectiveSchema ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ViewFunctionsPanel, {
1698
+ functions: viewFunctions,
1699
+ contractAddress,
1700
+ adapter,
1701
+ contractSchema: effectiveSchema,
1702
+ className: "flex-grow flex flex-col min-h-0"
1703
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1704
+ className: "flex flex-col items-center justify-center h-full py-4 text-center",
1705
+ children: [
1706
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1707
+ className: "rounded-full bg-muted p-3 mb-3",
1708
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.FileText, { className: "h-6 w-6 text-muted-foreground" })
1709
+ }),
1710
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1711
+ className: "text-sm font-medium",
1712
+ children: "No simple view functions found"
1713
+ }),
1714
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1715
+ className: "text-xs text-muted-foreground mt-1 mb-4 max-w-[220px]",
1716
+ children: "This contract doesn't have any simple view functions that can be queried without parameters"
1717
+ })
1718
+ ]
1719
+ })
1720
+ })]
1721
+ })] });
1722
+ }
1723
+
1724
+ //#endregion
1725
+ //#region src/components/ContractActionBar/ContractActionBar.tsx
1726
+ /**
1727
+ * ContractActionBar - A composable action bar for contract forms
1728
+ * Displays network information and contract state toggle button
1729
+ * Can be extended with additional actions via children
1730
+ */
1731
+ function ContractActionBar({ networkConfig, contractAddress = null, onToggleContractState, isWidgetExpanded = false, children, className = "" }) {
1732
+ if (!networkConfig) return null;
1733
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1734
+ className: `bg-background border-b mb-6 pb-4 flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between ${className}`,
1735
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1736
+ className: "flex flex-col gap-2 sm:flex-row sm:items-center",
1737
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_openzeppelin_ui_components.NetworkStatusBadge, { network: networkConfig }), contractAddress && onToggleContractState && !isWidgetExpanded && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_openzeppelin_ui_components.ViewContractStateButton, {
1738
+ contractAddress,
1739
+ onToggle: onToggleContractState
1740
+ })]
1741
+ }), children && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1742
+ className: "flex gap-2 sm:ml-auto",
1743
+ children
1744
+ })]
1745
+ });
1746
+ }
1747
+
1748
+ //#endregion
1749
+ //#region src/components/network/NetworkServiceSettingsPanel.tsx
1750
+ /** Panel for configuring network service settings like RPC endpoints. */
1751
+ function NetworkServiceSettingsPanel({ adapter, networkId, service, onSettingsChanged }) {
1752
+ const [isTesting, setIsTesting] = (0, react.useState)(false);
1753
+ const [result, setResult] = (0, react.useState)(null);
1754
+ const { control, handleSubmit, setValue, getValues, watch, formState: { isDirty } } = (0, react_hook_form.useForm)({ defaultValues: {} });
1755
+ const fields = (0, react.useMemo)(() => service.fields, [service]);
1756
+ const primaryFields = (0, react.useMemo)(() => fields.filter((f) => !f.metadata?.section), [fields]);
1757
+ const sectionGroups = (0, react.useMemo)(() => {
1758
+ const groups = {};
1759
+ for (const f of fields) {
1760
+ const md = f.metadata || {};
1761
+ const section = typeof md.section === "string" ? md.section : void 0;
1762
+ if (!section) continue;
1763
+ if (!groups[section]) groups[section] = {
1764
+ label: typeof md.sectionLabel === "string" ? md.sectionLabel : void 0,
1765
+ help: typeof md.sectionHelp === "string" ? md.sectionHelp : void 0,
1766
+ fields: []
1767
+ };
1768
+ if (!groups[section].label && typeof md.sectionLabel === "string") groups[section].label = md.sectionLabel;
1769
+ if (!groups[section].help && typeof md.sectionHelp === "string") groups[section].help = md.sectionHelp;
1770
+ groups[section].fields.push(f);
1771
+ }
1772
+ return groups;
1773
+ }, [fields]);
1774
+ (0, react.useEffect)(() => {
1775
+ try {
1776
+ const existing = _openzeppelin_ui_utils.userNetworkServiceConfigService.get(networkId, service.id);
1777
+ if (existing && typeof existing === "object") for (const f of fields) {
1778
+ const v = existing[f.name];
1779
+ if (v !== void 0) setValue(f.name, v);
1780
+ }
1781
+ for (const f of fields) {
1782
+ const current = getValues()[f.name];
1783
+ if (!(current !== void 0 && current !== null && current !== "") && "defaultValue" in f && f.defaultValue !== void 0) setValue(f.name, f.defaultValue);
1784
+ }
1785
+ } catch (e) {
1786
+ _openzeppelin_ui_utils.logger.error("NetworkServiceSettingsPanel", "Error loading config", e);
1787
+ }
1788
+ }, [
1789
+ networkId,
1790
+ service.id,
1791
+ fields,
1792
+ setValue,
1793
+ getValues
1794
+ ]);
1795
+ const testConnection = (0, react.useCallback)(async () => {
1796
+ if (!adapter.testNetworkServiceConnection) return;
1797
+ setIsTesting(true);
1798
+ setResult(null);
1799
+ try {
1800
+ const data = getValues();
1801
+ const r = await adapter.testNetworkServiceConnection(service.id, data);
1802
+ setResult({
1803
+ success: r.success,
1804
+ message: r.error || (r.success ? "Connection successful" : "Connection failed"),
1805
+ latencyMs: r.latency
1806
+ });
1807
+ } catch (e) {
1808
+ setResult({
1809
+ success: false,
1810
+ message: e instanceof Error ? e.message : "Connection test failed"
1811
+ });
1812
+ } finally {
1813
+ setIsTesting(false);
1814
+ }
1815
+ }, [
1816
+ adapter,
1817
+ service.id,
1818
+ getValues
1819
+ ]);
1820
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("form", {
1821
+ onSubmit: handleSubmit((0, react.useCallback)(async (formData) => {
1822
+ const data = formData;
1823
+ if (adapter.validateNetworkServiceConfig) {
1824
+ if (!await adapter.validateNetworkServiceConfig(service.id, data)) {
1825
+ setResult({
1826
+ success: false,
1827
+ message: "Invalid configuration"
1828
+ });
1829
+ return;
1830
+ }
1831
+ }
1832
+ if (Object.values(data).some((v) => v !== void 0 && v !== null && v !== "")) _openzeppelin_ui_utils.userNetworkServiceConfigService.save(networkId, service.id, data);
1833
+ else _openzeppelin_ui_utils.userNetworkServiceConfigService.clear(networkId, service.id);
1834
+ onSettingsChanged?.();
1835
+ setResult({
1836
+ success: true,
1837
+ message: "Settings saved successfully"
1838
+ });
1839
+ }, [
1840
+ adapter,
1841
+ networkId,
1842
+ service.id,
1843
+ onSettingsChanged
1844
+ ])),
1845
+ className: "space-y-6",
1846
+ children: [
1847
+ service.description && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_openzeppelin_ui_components.Alert, { children: [
1848
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Info, { className: "h-4 w-4" }),
1849
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_openzeppelin_ui_components.AlertTitle, {
1850
+ className: "text-sm",
1851
+ children: service.label
1852
+ }),
1853
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_openzeppelin_ui_components.AlertDescription, {
1854
+ className: "space-y-1 text-xs",
1855
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1856
+ className: "mt-2",
1857
+ children: service.description
1858
+ })
1859
+ })
1860
+ ] }),
1861
+ fields.filter((f) => f.metadata?.note).length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1862
+ className: "space-y-4",
1863
+ children: fields.filter((f) => f.metadata?.note).map((f) => {
1864
+ const note = f.metadata?.note;
1865
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_openzeppelin_ui_components.Alert, { children: [
1866
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(note?.variant === "warning" ? lucide_react.AlertTriangle : lucide_react.Info, { className: "h-4 w-4" }),
1867
+ note?.title && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_openzeppelin_ui_components.AlertTitle, {
1868
+ className: "text-sm",
1869
+ children: note.title
1870
+ }),
1871
+ note?.lines && note.lines.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_openzeppelin_ui_components.AlertDescription, {
1872
+ className: "space-y-1 text-xs",
1873
+ children: note.lines.map((ln, idx) => note.html ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { dangerouslySetInnerHTML: { __html: (0, _openzeppelin_ui_utils.sanitizeHtml)(ln) } }, idx) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: ln }, idx))
1874
+ })
1875
+ ] }, f.id);
1876
+ })
1877
+ }),
1878
+ primaryFields.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1879
+ className: "space-y-4",
1880
+ children: primaryFields.map((field) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DynamicFormField, {
1881
+ field,
1882
+ control,
1883
+ adapter
1884
+ }, field.id))
1885
+ }),
1886
+ Object.keys(sectionGroups).length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_openzeppelin_ui_components.Accordion, {
1887
+ type: "single",
1888
+ collapsible: true,
1889
+ className: "w-full",
1890
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_openzeppelin_ui_components.AccordionItem, {
1891
+ value: "advanced-settings",
1892
+ className: "border rounded-md",
1893
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_openzeppelin_ui_components.AccordionTrigger, {
1894
+ className: "text-sm font-medium px-3 py-2 hover:no-underline",
1895
+ children: "Advanced Settings"
1896
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_openzeppelin_ui_components.AccordionContent, {
1897
+ className: "px-3 pb-4",
1898
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1899
+ className: "space-y-6 pt-2",
1900
+ children: Object.entries(sectionGroups).map(([sectionId, group]) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1901
+ className: "mb-3",
1902
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", {
1903
+ className: "text-sm font-medium text-foreground",
1904
+ children: group.label || sectionId.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase())
1905
+ }), group.help && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1906
+ className: "text-xs text-muted-foreground mt-1",
1907
+ children: group.help
1908
+ })]
1909
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1910
+ className: "space-y-4",
1911
+ children: group.fields.map((field) => {
1912
+ const md = field.metadata || {};
1913
+ const indentUnder = typeof md["nestUnder"] === "string" ? md["nestUnder"] : void 0;
1914
+ const disabledWhen = md["disabledWhen"];
1915
+ let isDisabled = false;
1916
+ if (disabledWhen && typeof disabledWhen.field === "string") {
1917
+ const depVal = watch(disabledWhen.field);
1918
+ if ("equals" in disabledWhen) isDisabled = depVal === disabledWhen.equals;
1919
+ else if ("notEquals" in disabledWhen) isDisabled = depVal !== disabledWhen.notEquals;
1920
+ }
1921
+ const wrappedField = {
1922
+ ...field,
1923
+ readOnly: isDisabled
1924
+ };
1925
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1926
+ className: indentUnder ? "ml-6" : "",
1927
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DynamicFormField, {
1928
+ field: wrappedField,
1929
+ control,
1930
+ adapter
1931
+ })
1932
+ }, field.id);
1933
+ })
1934
+ })] }, sectionId))
1935
+ })
1936
+ })]
1937
+ })
1938
+ }),
1939
+ result && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1940
+ className: `flex items-center gap-2 text-sm ${result.success ? "text-green-600" : "text-red-600"}`,
1941
+ children: [
1942
+ result.success ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.CheckCircle2, { className: "h-4 w-4" }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.XCircle, { className: "h-4 w-4" }),
1943
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: result.message }),
1944
+ result.latencyMs && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1945
+ className: "text-muted-foreground",
1946
+ children: [
1947
+ "(",
1948
+ result.latencyMs,
1949
+ "ms)"
1950
+ ]
1951
+ })
1952
+ ]
1953
+ }),
1954
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1955
+ className: "flex justify-end gap-2",
1956
+ children: [
1957
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_openzeppelin_ui_components.Button, {
1958
+ type: "button",
1959
+ variant: "outline",
1960
+ onClick: () => {
1961
+ for (const f of fields) setValue(f.name, void 0);
1962
+ _openzeppelin_ui_utils.userNetworkServiceConfigService.clear(networkId, service.id);
1963
+ setResult(null);
1964
+ onSettingsChanged?.();
1965
+ },
1966
+ children: "Reset to Default"
1967
+ }),
1968
+ (() => {
1969
+ if (service.supportsConnectionTest === false) return false;
1970
+ const hideTest = fields.some((f) => {
1971
+ return f.metadata?.hideTestConnection === true;
1972
+ });
1973
+ return Boolean(adapter.testNetworkServiceConnection) && !hideTest;
1974
+ })() ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_openzeppelin_ui_components.Button, {
1975
+ type: "button",
1976
+ variant: "outline",
1977
+ onClick: testConnection,
1978
+ disabled: isTesting,
1979
+ children: isTesting ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), "Testing..."] }) : "Test Connection"
1980
+ }) : null,
1981
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_openzeppelin_ui_components.Button, {
1982
+ type: "submit",
1983
+ disabled: !isDirty,
1984
+ children: "Save Settings"
1985
+ })
1986
+ ]
1987
+ })
1988
+ ]
1989
+ });
1990
+ }
1991
+
1992
+ //#endregion
1993
+ //#region src/components/network/NetworkSettingsDialog.tsx
1994
+ const NetworkSettingsDialog = ({ isOpen, onOpenChange, networkConfig, adapter }) => {
1995
+ const services = adapter?.getNetworkServiceForms?.() ?? [];
1996
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_openzeppelin_ui_components.Dialog, {
1997
+ open: isOpen,
1998
+ onOpenChange,
1999
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_openzeppelin_ui_components.DialogContent, {
2000
+ className: "max-w-2xl sm:max-w-3xl",
2001
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_openzeppelin_ui_components.DialogHeader, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_openzeppelin_ui_components.DialogTitle, { children: "Network Settings" }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_openzeppelin_ui_components.DialogDescription, { children: ["Configure settings for ", networkConfig?.name] })] }), networkConfig && adapter && services.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_openzeppelin_ui_components.Tabs, {
2002
+ defaultValue: services[0]?.id,
2003
+ className: "w-full",
2004
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_openzeppelin_ui_components.TabsList, {
2005
+ className: `grid w-full ${services.length <= 2 ? "grid-cols-2" : services.length === 3 ? "grid-cols-3" : "grid-cols-4"}`,
2006
+ children: services.map((svc) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_openzeppelin_ui_components.TabsTrigger, {
2007
+ value: svc.id,
2008
+ children: svc.label
2009
+ }, svc.id))
2010
+ }), services.map((svc) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_openzeppelin_ui_components.TabsContent, {
2011
+ value: svc.id,
2012
+ className: "px-2",
2013
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(NetworkServiceSettingsPanel, {
2014
+ adapter,
2015
+ networkId: networkConfig.id,
2016
+ service: svc,
2017
+ onSettingsChanged: () => {
2018
+ const cfg = _openzeppelin_ui_utils.appConfigService.getTypedNestedConfig("walletui", "config");
2019
+ adapter.configureUiKit?.(cfg ?? {
2020
+ kitName: "custom",
2021
+ kitConfig: {}
2022
+ });
2023
+ }
2024
+ })
2025
+ }, svc.id))]
2026
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2027
+ className: "flex items-center justify-center py-8",
2028
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2029
+ className: "text-center text-muted-foreground",
2030
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2031
+ className: "text-sm",
2032
+ children: "No network configuration available"
2033
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2034
+ className: "text-xs mt-1",
2035
+ children: "This network uses default settings that cannot be customized"
2036
+ })]
2037
+ })
2038
+ })]
2039
+ })
2040
+ });
2041
+ };
2042
+
2043
+ //#endregion
2044
+ //#region src/components/WalletConnectionWithSettings.tsx
2045
+ /**
2046
+ * Enhanced wallet connection header with network settings menu.
2047
+ * Used in exported apps to provide access to RPC and Explorer configuration.
2048
+ */
2049
+ const WalletConnectionWithSettings = () => {
2050
+ const { isAdapterLoading, activeAdapter, activeNetworkConfig } = (0, _openzeppelin_ui_react.useWalletState)();
2051
+ const { setOpenNetworkSettingsHandler } = (0, _openzeppelin_ui_components.useNetworkErrors)();
2052
+ const [showNetworkSettings, setShowNetworkSettings] = (0, react.useState)(false);
2053
+ const openNetworkSettings = (0, react.useCallback)((networkId) => {
2054
+ if (activeNetworkConfig && networkId === activeNetworkConfig.id) setShowNetworkSettings(true);
2055
+ }, [activeNetworkConfig]);
2056
+ (0, react.useEffect)(() => {
2057
+ setOpenNetworkSettingsHandler(openNetworkSettings);
2058
+ }, [openNetworkSettings, setOpenNetworkSettingsHandler]);
2059
+ if (isAdapterLoading) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "h-9 w-28 animate-pulse rounded bg-muted" });
2060
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2061
+ className: "flex items-center gap-2",
2062
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_openzeppelin_ui_react.WalletConnectionUI, {}), activeAdapter && activeNetworkConfig && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_openzeppelin_ui_components.Button, {
2063
+ variant: "ghost",
2064
+ size: "icon",
2065
+ className: "h-9 w-9",
2066
+ title: "Network Settings",
2067
+ onClick: () => setShowNetworkSettings(true),
2068
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Settings, { className: "h-4 w-4" })
2069
+ })]
2070
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(NetworkSettingsDialog, {
2071
+ isOpen: showNetworkSettings,
2072
+ onOpenChange: setShowNetworkSettings,
2073
+ networkConfig: activeNetworkConfig,
2074
+ adapter: activeAdapter
2075
+ })] });
2076
+ };
2077
+
2078
+ //#endregion
2079
+ exports.ContractActionBar = ContractActionBar;
2080
+ exports.ContractStateWidget = ContractStateWidget;
2081
+ exports.DynamicFormField = DynamicFormField;
2082
+ exports.ExecutionConfigDisplay = ExecutionConfigDisplay;
2083
+ exports.NetworkSettingsDialog = NetworkSettingsDialog;
2084
+ exports.TransactionExecuteButton = TransactionExecuteButton;
2085
+ exports.TransactionForm = TransactionForm;
2086
+ exports.WalletConnectionWithSettings = WalletConnectionWithSettings;
2087
+ exports.createAddressTransform = createAddressTransform;
2088
+ exports.createArrayObjectTransform = createArrayObjectTransform;
2089
+ exports.createArrayTransform = createArrayTransform;
2090
+ exports.createBigIntTransform = createBigIntTransform;
2091
+ exports.createBooleanTransform = createBooleanTransform;
2092
+ exports.createComplexTypeTransform = createComplexTypeTransform;
2093
+ exports.createDefaultFormValues = createDefaultFormValues;
2094
+ exports.createNumberTransform = createNumberTransform;
2095
+ exports.createObjectTransform = createObjectTransform;
2096
+ exports.createTextTransform = createTextTransform;
2097
+ exports.createTransformForFieldType = createTransformForFieldType;
2098
+ exports.generateDefaultValue = generateDefaultValue;
2099
+ exports.getDefaultValueByFieldType = getDefaultValueByFieldType;
2100
+ exports.validateField = validateField;
2101
+ //# sourceMappingURL=index.cjs.map