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