@formo/analytics 1.26.0 → 1.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/README.md +6 -4
  2. package/dist/cjs/src/FormoAnalytics.d.ts +3 -1
  3. package/dist/cjs/src/FormoAnalytics.js +3 -2
  4. package/dist/cjs/src/event/EventFactory.d.ts +1 -1
  5. package/dist/cjs/src/event/EventFactory.js +3 -3
  6. package/dist/cjs/src/fetch/index.d.ts +10 -2
  7. package/dist/cjs/src/fetch/index.js +122 -4
  8. package/dist/cjs/src/queue/EventQueue.d.ts +24 -2
  9. package/dist/cjs/src/queue/EventQueue.js +158 -49
  10. package/dist/cjs/src/types/base.d.ts +8 -0
  11. package/dist/cjs/src/types/events.d.ts +5 -3
  12. package/dist/cjs/src/version.d.ts +1 -1
  13. package/dist/cjs/src/version.js +1 -1
  14. package/dist/cjs/src/wagmi/WagmiEventHandler.d.ts +24 -0
  15. package/dist/cjs/src/wagmi/WagmiEventHandler.js +236 -16
  16. package/dist/cjs/src/wagmi/types.d.ts +31 -0
  17. package/dist/cjs/src/wagmi/utils.d.ts +74 -0
  18. package/dist/cjs/src/wagmi/utils.js +198 -0
  19. package/dist/esm/src/FormoAnalytics.d.ts +3 -1
  20. package/dist/esm/src/FormoAnalytics.js +3 -2
  21. package/dist/esm/src/event/EventFactory.d.ts +1 -1
  22. package/dist/esm/src/event/EventFactory.js +3 -3
  23. package/dist/esm/src/fetch/index.d.ts +10 -2
  24. package/dist/esm/src/fetch/index.js +123 -2
  25. package/dist/esm/src/queue/EventQueue.d.ts +24 -2
  26. package/dist/esm/src/queue/EventQueue.js +158 -49
  27. package/dist/esm/src/types/base.d.ts +8 -0
  28. package/dist/esm/src/types/events.d.ts +5 -3
  29. package/dist/esm/src/version.d.ts +1 -1
  30. package/dist/esm/src/version.js +1 -1
  31. package/dist/esm/src/wagmi/WagmiEventHandler.d.ts +24 -0
  32. package/dist/esm/src/wagmi/WagmiEventHandler.js +236 -16
  33. package/dist/esm/src/wagmi/types.d.ts +31 -0
  34. package/dist/esm/src/wagmi/utils.d.ts +74 -0
  35. package/dist/esm/src/wagmi/utils.js +192 -0
  36. package/dist/index.umd.min.js +1 -1
  37. package/package.json +6 -2
@@ -54,6 +54,40 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
54
54
  };
55
55
  import { SignatureStatus, TransactionStatus } from "../types/events";
56
56
  import { logger } from "../logger";
57
+ import { encodeWriteContractData, extractFunctionArgs, buildSafeFunctionArgs, } from "./utils";
58
+ /**
59
+ * Built-in transaction fields that could collide with function args.
60
+ * Defined at module level to avoid recreating on every method call.
61
+ */
62
+ var RESERVED_FIELDS = new Set([
63
+ "status",
64
+ "chainId",
65
+ "address",
66
+ "data",
67
+ "to",
68
+ "value",
69
+ "transactionHash",
70
+ "function_name",
71
+ "function_args",
72
+ ]);
73
+ /**
74
+ * Clean up old entries from a Set to prevent memory leaks.
75
+ * Removes oldest entries when size exceeds maxSize.
76
+ *
77
+ * @param set - The Set to clean up
78
+ * @param maxSize - Maximum allowed size before cleanup (default: 1000)
79
+ * @param removeCount - Number of entries to remove (default: 500)
80
+ */
81
+ function cleanupOldEntries(set, maxSize, removeCount) {
82
+ if (maxSize === void 0) { maxSize = 1000; }
83
+ if (removeCount === void 0) { removeCount = 500; }
84
+ if (set.size > maxSize) {
85
+ var entries = Array.from(set);
86
+ for (var i = 0; i < removeCount && i < entries.length; i++) {
87
+ set.delete(entries[i]);
88
+ }
89
+ }
90
+ }
57
91
  var WagmiEventHandler = /** @class */ (function () {
58
92
  function WagmiEventHandler(formoAnalytics, wagmiConfig, queryClient) {
59
93
  this.unsubscribers = [];
@@ -65,15 +99,26 @@ var WagmiEventHandler = /** @class */ (function () {
65
99
  * Key format: `${mutationId}:${status}`
66
100
  */
67
101
  this.processedMutations = new Set();
102
+ /**
103
+ * Track processed query states to prevent duplicate event emissions
104
+ * Key format: `${queryHash}:${status}`
105
+ */
106
+ this.processedQueries = new Set();
107
+ /**
108
+ * Store transaction details from BROADCASTED events for use in CONFIRMED/REVERTED
109
+ * Key: transactionHash, Value: transaction details including the original sender address
110
+ */
111
+ this.pendingTransactions = new Map();
68
112
  this.formo = formoAnalytics;
69
113
  this.wagmiConfig = wagmiConfig;
70
114
  this.queryClient = queryClient;
71
115
  logger.info("WagmiEventHandler: Initializing Wagmi integration");
72
116
  // Set up connection/disconnection/chain listeners
73
117
  this.setupConnectionListeners();
74
- // Set up mutation tracking if QueryClient is provided
118
+ // Set up mutation and query tracking if QueryClient is provided
75
119
  if (this.queryClient) {
76
120
  this.setupMutationTracking();
121
+ this.setupQueryTracking();
77
122
  }
78
123
  else {
79
124
  logger.warn("WagmiEventHandler: QueryClient not provided, signature and transaction events will not be tracked");
@@ -223,6 +268,131 @@ var WagmiEventHandler = /** @class */ (function () {
223
268
  this.unsubscribers.push(unsubscribe);
224
269
  logger.info("WagmiEventHandler: Mutation tracking set up successfully");
225
270
  };
271
+ /**
272
+ * Set up query tracking for transaction confirmations
273
+ * Listens for waitForTransactionReceipt queries to detect CONFIRMED status
274
+ */
275
+ WagmiEventHandler.prototype.setupQueryTracking = function () {
276
+ var _this = this;
277
+ if (!this.queryClient) {
278
+ return;
279
+ }
280
+ logger.info("WagmiEventHandler: Setting up query tracking");
281
+ var queryCache = this.queryClient.getQueryCache();
282
+ var unsubscribe = queryCache.subscribe(function (event) {
283
+ _this.handleQueryEvent(event);
284
+ });
285
+ this.unsubscribers.push(unsubscribe);
286
+ logger.info("WagmiEventHandler: Query tracking set up successfully");
287
+ };
288
+ /**
289
+ * Handle query cache events (transaction confirmations)
290
+ */
291
+ WagmiEventHandler.prototype.handleQueryEvent = function (event) {
292
+ if (event.type !== "updated") {
293
+ return;
294
+ }
295
+ var query = event.query;
296
+ var queryKey = query.queryKey;
297
+ if (!queryKey || queryKey.length === 0) {
298
+ return;
299
+ }
300
+ var queryType = queryKey[0];
301
+ // Only handle waitForTransactionReceipt queries
302
+ if (queryType !== "waitForTransactionReceipt") {
303
+ return;
304
+ }
305
+ var state = query.state;
306
+ // Extract receipt status early to include in deduplication key
307
+ // This ensures CONFIRMED vs REVERTED outcomes are processed separately
308
+ var receipt = state.data;
309
+ var receiptStatus = receipt === null || receipt === void 0 ? void 0 : receipt.status;
310
+ // Create a unique key for this query state to prevent duplicate processing
311
+ // Include receipt status to distinguish between CONFIRMED and REVERTED outcomes
312
+ var queryStateKey = "".concat(query.queryHash, ":").concat(state.status, ":").concat(receiptStatus || "");
313
+ // Skip if we've already processed this query state
314
+ if (this.processedQueries.has(queryStateKey)) {
315
+ logger.debug("WagmiEventHandler: Skipping duplicate query event", {
316
+ queryType: queryType,
317
+ queryHash: query.queryHash,
318
+ status: state.status,
319
+ receiptStatus: receiptStatus,
320
+ });
321
+ return;
322
+ }
323
+ // Mark this query state as processed
324
+ this.processedQueries.add(queryStateKey);
325
+ logger.debug("WagmiEventHandler: Query event", {
326
+ queryType: queryType,
327
+ queryHash: query.queryHash,
328
+ status: state.status,
329
+ });
330
+ // Handle transaction receipt queries
331
+ this.handleTransactionReceiptQuery(query);
332
+ // Clean up old processed queries to prevent memory leaks
333
+ cleanupOldEntries(this.processedQueries);
334
+ };
335
+ /**
336
+ * Handle waitForTransactionReceipt query completion
337
+ * Emits CONFIRMED or REVERTED transaction status
338
+ */
339
+ WagmiEventHandler.prototype.handleTransactionReceiptQuery = function (query) {
340
+ var _a;
341
+ if (!this.formo.isAutocaptureEnabled("transaction")) {
342
+ return;
343
+ }
344
+ var state = query.state;
345
+ var queryKey = query.queryKey;
346
+ // Only handle successful queries (transaction confirmed on chain)
347
+ if (state.status !== "success") {
348
+ return;
349
+ }
350
+ // Extract hash and chainId from query key
351
+ // Query key format: ['waitForTransactionReceipt', { hash, chainId, ... }]
352
+ var params = queryKey[1];
353
+ var transactionHash = params === null || params === void 0 ? void 0 : params.hash;
354
+ var chainId = (params === null || params === void 0 ? void 0 : params.chainId) || this.trackingState.lastChainId;
355
+ if (!transactionHash) {
356
+ logger.warn("WagmiEventHandler: Transaction receipt query but no hash found");
357
+ return;
358
+ }
359
+ // Retrieve stored transaction details from BROADCASTED event
360
+ // Normalize hash to lowercase for consistent lookup
361
+ var normalizedHash = transactionHash.toLowerCase();
362
+ var pendingTx = this.pendingTransactions.get(normalizedHash);
363
+ // Use the original sender address from BROADCASTED event if available,
364
+ // otherwise fall back to current connected address.
365
+ // This handles wallet switches between broadcast and confirmation.
366
+ var address = (pendingTx === null || pendingTx === void 0 ? void 0 : pendingTx.address) || this.trackingState.lastAddress;
367
+ if (!address) {
368
+ logger.warn("WagmiEventHandler: Transaction receipt query but no address available");
369
+ return;
370
+ }
371
+ try {
372
+ // Extract receipt data
373
+ var receipt = state.data;
374
+ // Determine transaction status from receipt
375
+ // receipt.status is 'success' or 'reverted' in viem
376
+ var txStatus = (receipt === null || receipt === void 0 ? void 0 : receipt.status) === "reverted"
377
+ ? TransactionStatus.REVERTED
378
+ : TransactionStatus.CONFIRMED;
379
+ logger.info("WagmiEventHandler: Tracking transaction confirmation", {
380
+ status: txStatus,
381
+ transactionHash: transactionHash,
382
+ address: address,
383
+ chainId: chainId,
384
+ blockNumber: (_a = receipt === null || receipt === void 0 ? void 0 : receipt.blockNumber) === null || _a === void 0 ? void 0 : _a.toString(),
385
+ });
386
+ this.formo.transaction(__assign(__assign(__assign(__assign(__assign({ status: txStatus, chainId: chainId || 0, address: address, transactionHash: transactionHash }, ((pendingTx === null || pendingTx === void 0 ? void 0 : pendingTx.data) && { data: pendingTx.data })), ((pendingTx === null || pendingTx === void 0 ? void 0 : pendingTx.to) && { to: pendingTx.to })), ((pendingTx === null || pendingTx === void 0 ? void 0 : pendingTx.value) && { value: pendingTx.value })), ((pendingTx === null || pendingTx === void 0 ? void 0 : pendingTx.function_name) && { function_name: pendingTx.function_name })), ((pendingTx === null || pendingTx === void 0 ? void 0 : pendingTx.function_args) && { function_args: pendingTx.function_args })),
387
+ // Spread function args as additional properties (only colliding keys are prefixed)
388
+ pendingTx === null || pendingTx === void 0 ? void 0 : pendingTx.safeFunctionArgs);
389
+ // Clean up the pending transaction after confirmation
390
+ this.pendingTransactions.delete(normalizedHash);
391
+ }
392
+ catch (error) {
393
+ logger.error("WagmiEventHandler: Error handling transaction receipt query:", error);
394
+ }
395
+ };
226
396
  /**
227
397
  * Handle mutation cache events (signatures, transactions)
228
398
  */
@@ -264,14 +434,7 @@ var WagmiEventHandler = /** @class */ (function () {
264
434
  this.handleTransactionMutation(mutationType, mutation);
265
435
  }
266
436
  // Clean up old processed mutations to prevent memory leaks
267
- // Keep only recent mutations (max 1000 entries)
268
- if (this.processedMutations.size > 1000) {
269
- var entries = Array.from(this.processedMutations);
270
- // Remove oldest 500 entries
271
- for (var i = 0; i < 500; i++) {
272
- this.processedMutations.delete(entries[i]);
273
- }
274
- }
437
+ cleanupOldEntries(this.processedMutations);
275
438
  };
276
439
  /**
277
440
  * Handle signature mutations (signMessage, signTypedData)
@@ -337,8 +500,15 @@ var WagmiEventHandler = /** @class */ (function () {
337
500
  var state = mutation.state;
338
501
  var variables = state.variables || {};
339
502
  var chainId = this.trackingState.lastChainId || variables.chainId;
340
- var address = this.trackingState.lastAddress || variables.account || variables.address;
341
- if (!address) {
503
+ // For sendTransaction, user's address is the 'from'
504
+ // For writeContract, variables.address is the contract address, not the user
505
+ // variables.account can be a string address or an Account object with an address property
506
+ var accountValue = variables.account;
507
+ var accountAddress = typeof accountValue === "string"
508
+ ? accountValue
509
+ : accountValue === null || accountValue === void 0 ? void 0 : accountValue.address;
510
+ var userAddress = this.trackingState.lastAddress || accountAddress || variables.from;
511
+ if (!userAddress) {
342
512
  logger.warn("WagmiEventHandler: Transaction event but no address available");
343
513
  return;
344
514
  }
@@ -359,18 +529,66 @@ var WagmiEventHandler = /** @class */ (function () {
359
529
  else {
360
530
  return; // Ignore idle state
361
531
  }
362
- // Extract transaction details from variables
363
- var data = variables.data;
364
- var to = variables.to || variables.address;
532
+ // Extract transaction details based on mutation type
533
+ var data = void 0;
534
+ var to = void 0;
535
+ var function_name = void 0;
536
+ var function_args = void 0;
365
537
  var value = (_a = variables.value) === null || _a === void 0 ? void 0 : _a.toString();
538
+ if (mutationType === "writeContract") {
539
+ // For writeContract, extract function info and encode data
540
+ var abi = variables.abi, fnName = variables.functionName, args = variables.args, contractAddress = variables.address;
541
+ to = contractAddress;
542
+ function_name = fnName;
543
+ if (abi && fnName) {
544
+ // Extract function arguments as a name-value map
545
+ function_args = extractFunctionArgs(abi, fnName, args);
546
+ // Encode the function data synchronously if viem is available
547
+ var encodedData = encodeWriteContractData(abi, fnName, args);
548
+ if (encodedData) {
549
+ data = encodedData;
550
+ logger.debug("WagmiEventHandler: Encoded writeContract data", data.substring(0, 10));
551
+ }
552
+ }
553
+ }
554
+ else {
555
+ // For sendTransaction, use variables directly
556
+ // Only data is available, function_name and function_args are not sent
557
+ data = variables.data;
558
+ to = variables.to;
559
+ }
366
560
  logger.info("WagmiEventHandler: Tracking transaction event", {
367
561
  status: status_2,
368
562
  mutationType: mutationType,
369
- address: address,
563
+ address: userAddress,
370
564
  chainId: chainId,
371
565
  transactionHash: transactionHash,
566
+ function_name: function_name,
372
567
  });
373
- this.formo.transaction(__assign(__assign(__assign(__assign({ status: status_2, chainId: chainId || 0, address: address }, (data && { data: data })), (to && { to: to })), (value && { value: value })), (transactionHash && { transactionHash: transactionHash })));
568
+ // Build safeFunctionArgs with collision handling and struct flattening
569
+ var safeFunctionArgs = buildSafeFunctionArgs(function_args, RESERVED_FIELDS);
570
+ // Store transaction details for BROADCASTED status to use in CONFIRMED/REVERTED
571
+ // Normalize hash to lowercase for consistent lookup
572
+ // Include the sender address to handle wallet switches between broadcast and confirmation
573
+ if (status_2 === TransactionStatus.BROADCASTED && transactionHash) {
574
+ var normalizedHash = transactionHash.toLowerCase();
575
+ var txDetails = __assign(__assign(__assign(__assign(__assign(__assign({ address: userAddress }, (data && { data: data })), (to && { to: to })), (value && { value: value })), (function_name && { function_name: function_name })), (function_args && { function_args: function_args })), (safeFunctionArgs && { safeFunctionArgs: safeFunctionArgs }));
576
+ this.pendingTransactions.set(normalizedHash, txDetails);
577
+ logger.debug("WagmiEventHandler: Stored pending transaction for confirmation", {
578
+ transactionHash: normalizedHash,
579
+ });
580
+ // Clean up old pending transactions to prevent memory leaks (keep max 100)
581
+ // Remove oldest 50 entries when limit exceeded to handle high-throughput scenarios
582
+ if (this.pendingTransactions.size > 100) {
583
+ var keys = Array.from(this.pendingTransactions.keys());
584
+ for (var i = 0; i < 50 && i < keys.length; i++) {
585
+ this.pendingTransactions.delete(keys[i]);
586
+ }
587
+ }
588
+ }
589
+ this.formo.transaction(__assign(__assign(__assign(__assign(__assign(__assign({ status: status_2, chainId: chainId || 0, address: userAddress }, (data && { data: data })), (to && { to: to })), (value && { value: value })), (transactionHash && { transactionHash: transactionHash })), (function_name && { function_name: function_name })), (function_args && { function_args: function_args })),
590
+ // Spread function args as additional properties (only colliding keys are prefixed)
591
+ safeFunctionArgs);
374
592
  }
375
593
  catch (error) {
376
594
  logger.error("WagmiEventHandler: Error handling transaction mutation:", error);
@@ -438,6 +656,8 @@ var WagmiEventHandler = /** @class */ (function () {
438
656
  }
439
657
  this.unsubscribers = [];
440
658
  this.processedMutations.clear();
659
+ this.processedQueries.clear();
660
+ this.pendingTransactions.clear();
441
661
  logger.info("WagmiEventHandler: Cleanup complete");
442
662
  };
443
663
  return WagmiEventHandler;
@@ -89,11 +89,42 @@ export interface MutationCacheEvent {
89
89
  export interface MutationCache {
90
90
  subscribe(listener: (event: MutationCacheEvent) => void): () => void;
91
91
  }
92
+ /**
93
+ * React Query query state
94
+ */
95
+ export interface QueryState {
96
+ status: 'pending' | 'success' | 'error';
97
+ data?: any;
98
+ error?: Error | null;
99
+ fetchStatus: 'fetching' | 'paused' | 'idle';
100
+ }
101
+ /**
102
+ * React Query query object
103
+ */
104
+ export interface Query {
105
+ state: QueryState;
106
+ queryKey: readonly unknown[];
107
+ queryHash: string;
108
+ }
109
+ /**
110
+ * React Query query cache event
111
+ */
112
+ export interface QueryCacheEvent {
113
+ type: 'added' | 'removed' | 'updated';
114
+ query: Query;
115
+ }
116
+ /**
117
+ * React Query QueryCache interface
118
+ */
119
+ export interface QueryCache {
120
+ subscribe(listener: (event: QueryCacheEvent) => void): () => void;
121
+ }
92
122
  /**
93
123
  * React Query QueryClient interface
94
124
  */
95
125
  export interface QueryClient {
96
126
  getMutationCache(): MutationCache;
127
+ getQueryCache(): QueryCache;
97
128
  }
98
129
  /**
99
130
  * Unsubscribe function returned by subscriptions
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Utility functions for Wagmi event handling
3
+ *
4
+ * Provides ABI encoding utilities for extracting transaction data from
5
+ * writeContract mutations without requiring viem as a direct dependency.
6
+ */
7
+ /**
8
+ * Flatten a nested object into a flat object with underscore-separated keys.
9
+ * Only leaf values (primitives) are included; intermediate objects are not.
10
+ *
11
+ * Example:
12
+ * Input: { o: { x: "100", inner: { a: "42", b: "0xRecipient" } } }
13
+ * Output: { o_x: "100", o_inner_a: "42", o_inner_b: "0xRecipient" }
14
+ *
15
+ * @param obj - The object to flatten
16
+ * @param prefix - Optional prefix for keys (used in recursion)
17
+ * @returns A flat object with underscore-separated keys
18
+ */
19
+ export declare function flattenObject(obj: Record<string, unknown>, prefix?: string): Record<string, unknown>;
20
+ /**
21
+ * ABI function item type
22
+ */
23
+ export interface AbiItem {
24
+ type: string;
25
+ name?: string;
26
+ inputs?: AbiInput[];
27
+ outputs?: AbiOutput[];
28
+ stateMutability?: string;
29
+ }
30
+ export interface AbiInput {
31
+ name: string;
32
+ type: string;
33
+ indexed?: boolean;
34
+ components?: AbiInput[];
35
+ internalType?: string;
36
+ }
37
+ export interface AbiOutput {
38
+ name: string;
39
+ type: string;
40
+ components?: AbiOutput[];
41
+ internalType?: string;
42
+ }
43
+ /**
44
+ * Encode writeContract data using viem's encodeFunctionData
45
+ *
46
+ * @param abi - The contract ABI
47
+ * @param functionName - The function name to encode
48
+ * @param args - The function arguments
49
+ * @returns The encoded calldata or undefined if encoding fails
50
+ */
51
+ export declare function encodeWriteContractData(abi: AbiItem[], functionName: string, args?: unknown[]): string | undefined;
52
+ /**
53
+ * Extract function arguments as a name-value map from ABI and args array
54
+ *
55
+ * @param abi - The contract ABI
56
+ * @param functionName - The function name
57
+ * @param args - The function arguments array
58
+ * @returns A map of argument names to values, or undefined if extraction fails
59
+ */
60
+ export declare function extractFunctionArgs(abi: AbiItem[], functionName: string, args?: unknown[]): Record<string, unknown> | undefined;
61
+ /**
62
+ * Build safe function args with collision handling and struct flattening.
63
+ *
64
+ * This function:
65
+ * 1. Prefixes top-level args that collide with reserved fields (e.g., 'to' -> 'arg_to')
66
+ * 2. Flattens nested struct values for easier querying (e.g., order.maker -> order_maker)
67
+ * 3. Skips flattened keys that would collide with existing top-level args
68
+ *
69
+ * @param functionArgs - The extracted function arguments
70
+ * @param reservedFields - Set of reserved field names that need prefixing
71
+ * @returns Safe function args object, or undefined if input is undefined
72
+ */
73
+ export declare function buildSafeFunctionArgs(functionArgs: Record<string, unknown> | undefined, reservedFields: Set<string>): Record<string, unknown> | undefined;
74
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1,192 @@
1
+ /**
2
+ * Utility functions for Wagmi event handling
3
+ *
4
+ * Provides ABI encoding utilities for extracting transaction data from
5
+ * writeContract mutations without requiring viem as a direct dependency.
6
+ */
7
+ import { logger } from "../logger";
8
+ /**
9
+ * Flatten a nested object into a flat object with underscore-separated keys.
10
+ * Only leaf values (primitives) are included; intermediate objects are not.
11
+ *
12
+ * Example:
13
+ * Input: { o: { x: "100", inner: { a: "42", b: "0xRecipient" } } }
14
+ * Output: { o_x: "100", o_inner_a: "42", o_inner_b: "0xRecipient" }
15
+ *
16
+ * @param obj - The object to flatten
17
+ * @param prefix - Optional prefix for keys (used in recursion)
18
+ * @returns A flat object with underscore-separated keys
19
+ */
20
+ export function flattenObject(obj, prefix) {
21
+ if (prefix === void 0) { prefix = ""; }
22
+ var result = {};
23
+ for (var _i = 0, _a = Object.entries(obj); _i < _a.length; _i++) {
24
+ var _b = _a[_i], key = _b[0], value = _b[1];
25
+ var newKey = prefix ? "".concat(prefix, "_").concat(key) : key;
26
+ if (value !== null && typeof value === "object" && !Array.isArray(value)) {
27
+ // Recursively flatten nested objects
28
+ var nested = flattenObject(value, newKey);
29
+ Object.assign(result, nested);
30
+ }
31
+ else {
32
+ // Leaf value (primitive or array) - add directly
33
+ result[newKey] = value;
34
+ }
35
+ }
36
+ return result;
37
+ }
38
+ /**
39
+ * Recursively convert all BigInt values to strings for JSON serialization
40
+ * Handles nested objects, arrays, and deeply nested structures (e.g., Solidity structs)
41
+ *
42
+ * @param value - The value to convert
43
+ * @returns The value with all BigInt converted to strings
44
+ */
45
+ function convertBigIntToString(value) {
46
+ if (typeof value === "bigint") {
47
+ return value.toString();
48
+ }
49
+ if (Array.isArray(value)) {
50
+ return value.map(convertBigIntToString);
51
+ }
52
+ if (value !== null && typeof value === "object") {
53
+ var result = {};
54
+ for (var _i = 0, _a = Object.entries(value); _i < _a.length; _i++) {
55
+ var _b = _a[_i], key = _b[0], val = _b[1];
56
+ result[key] = convertBigIntToString(val);
57
+ }
58
+ return result;
59
+ }
60
+ return value;
61
+ }
62
+ // Cached viem module reference
63
+ var viemModule;
64
+ /**
65
+ * Try to load viem synchronously via require
66
+ * Returns null if viem is not available
67
+ */
68
+ function tryLoadViem() {
69
+ if (viemModule !== undefined) {
70
+ return viemModule;
71
+ }
72
+ try {
73
+ // Use require to load viem synchronously
74
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
75
+ var viem = require("viem");
76
+ if (viem === null || viem === void 0 ? void 0 : viem.encodeFunctionData) {
77
+ viemModule = {
78
+ encodeFunctionData: viem.encodeFunctionData,
79
+ };
80
+ return viemModule;
81
+ }
82
+ }
83
+ catch (_a) {
84
+ // viem is not available
85
+ }
86
+ viemModule = null;
87
+ return null;
88
+ }
89
+ /**
90
+ * Encode writeContract data using viem's encodeFunctionData
91
+ *
92
+ * @param abi - The contract ABI
93
+ * @param functionName - The function name to encode
94
+ * @param args - The function arguments
95
+ * @returns The encoded calldata or undefined if encoding fails
96
+ */
97
+ export function encodeWriteContractData(abi, functionName, args) {
98
+ try {
99
+ var viem = tryLoadViem();
100
+ if (!viem) {
101
+ logger.debug("WagmiEventHandler: viem not available, cannot encode function data");
102
+ return undefined;
103
+ }
104
+ var data = viem.encodeFunctionData({
105
+ abi: abi,
106
+ functionName: functionName,
107
+ args: args || [],
108
+ });
109
+ return data;
110
+ }
111
+ catch (error) {
112
+ logger.warn("WagmiEventHandler: Failed to encode function data", error);
113
+ return undefined;
114
+ }
115
+ }
116
+ /**
117
+ * Extract function arguments as a name-value map from ABI and args array
118
+ *
119
+ * @param abi - The contract ABI
120
+ * @param functionName - The function name
121
+ * @param args - The function arguments array
122
+ * @returns A map of argument names to values, or undefined if extraction fails
123
+ */
124
+ export function extractFunctionArgs(abi, functionName, args) {
125
+ if (!abi || !functionName || !args || !Array.isArray(args)) {
126
+ return undefined;
127
+ }
128
+ try {
129
+ // Find the function in the ABI
130
+ var abiItem = abi.find(function (item) { return item.type === "function" && item.name === functionName; });
131
+ if (!(abiItem === null || abiItem === void 0 ? void 0 : abiItem.inputs) || !Array.isArray(abiItem.inputs)) {
132
+ return undefined;
133
+ }
134
+ var result_1 = {};
135
+ abiItem.inputs.forEach(function (input, index) {
136
+ if (index < args.length) {
137
+ var argValue = args[index];
138
+ var argName = input.name || "arg".concat(index);
139
+ // Recursively convert BigInt to string for JSON serialization
140
+ // Handles: direct BigInt, arrays with BigInt, nested objects/structs with BigInt
141
+ result_1[argName] = convertBigIntToString(argValue);
142
+ }
143
+ });
144
+ return result_1;
145
+ }
146
+ catch (error) {
147
+ logger.warn("WagmiEventHandler: Failed to extract function args", error);
148
+ return undefined;
149
+ }
150
+ }
151
+ /**
152
+ * Build safe function args with collision handling and struct flattening.
153
+ *
154
+ * This function:
155
+ * 1. Prefixes top-level args that collide with reserved fields (e.g., 'to' -> 'arg_to')
156
+ * 2. Flattens nested struct values for easier querying (e.g., order.maker -> order_maker)
157
+ * 3. Skips flattened keys that would collide with existing top-level args
158
+ *
159
+ * @param functionArgs - The extracted function arguments
160
+ * @param reservedFields - Set of reserved field names that need prefixing
161
+ * @returns Safe function args object, or undefined if input is undefined
162
+ */
163
+ export function buildSafeFunctionArgs(functionArgs, reservedFields) {
164
+ if (!functionArgs) {
165
+ return undefined;
166
+ }
167
+ var result = {};
168
+ for (var _i = 0, _a = Object.entries(functionArgs); _i < _a.length; _i++) {
169
+ var _b = _a[_i], key = _b[0], val = _b[1];
170
+ var safeKey = reservedFields.has(key) ? "arg_".concat(key) : key;
171
+ result[safeKey] = val;
172
+ // If the value is a nested object (struct), flatten it
173
+ // Skip flattened keys that would overwrite existing top-level args
174
+ if (val !== null && typeof val === "object" && !Array.isArray(val)) {
175
+ var flattened = flattenObject(val, safeKey);
176
+ for (var _c = 0, _d = Object.entries(flattened); _c < _d.length; _c++) {
177
+ var _e = _d[_c], flatKey = _e[0], flatVal = _e[1];
178
+ if (!(flatKey in result)) {
179
+ result[flatKey] = flatVal;
180
+ }
181
+ else {
182
+ logger.debug("WagmiEventHandler: Skipping flattened key collision", {
183
+ flatKey: flatKey,
184
+ existingValue: result[flatKey],
185
+ });
186
+ }
187
+ }
188
+ }
189
+ }
190
+ return result;
191
+ }
192
+ //# sourceMappingURL=utils.js.map