@formo/analytics 1.36.0 → 1.38.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 (44) hide show
  1. package/dist/cjs/src/FormoAnalytics.d.ts +64 -0
  2. package/dist/cjs/src/FormoAnalytics.js +155 -5
  3. package/dist/cjs/src/FormoAnalyticsProvider.js +1 -0
  4. package/dist/cjs/src/evm/EvmEventTracker.d.ts +38 -10
  5. package/dist/cjs/src/evm/EvmEventTracker.js +182 -0
  6. package/dist/cjs/src/evm/EvmProviderRegistry.js +26 -4
  7. package/dist/cjs/src/evm/EvmRequestTracker.d.ts +36 -40
  8. package/dist/cjs/src/evm/EvmRequestTracker.js +216 -115
  9. package/dist/cjs/src/evm/batch.d.ts +94 -0
  10. package/dist/cjs/src/evm/batch.js +130 -0
  11. package/dist/cjs/src/provider/detection.d.ts +30 -0
  12. package/dist/cjs/src/provider/detection.js +62 -0
  13. package/dist/cjs/src/provider/index.d.ts +1 -1
  14. package/dist/cjs/src/provider/index.js +3 -1
  15. package/dist/cjs/src/types/base.d.ts +23 -0
  16. package/dist/cjs/src/types/provider.d.ts +16 -0
  17. package/dist/cjs/src/types/provider.js +17 -1
  18. package/dist/cjs/src/version.d.ts +1 -1
  19. package/dist/cjs/src/version.js +1 -1
  20. package/dist/cjs/src/wagmi/WagmiEventHandler.d.ts +87 -0
  21. package/dist/cjs/src/wagmi/WagmiEventHandler.js +536 -13
  22. package/dist/esm/src/FormoAnalytics.d.ts +64 -0
  23. package/dist/esm/src/FormoAnalytics.js +155 -5
  24. package/dist/esm/src/FormoAnalyticsProvider.js +1 -0
  25. package/dist/esm/src/evm/EvmEventTracker.d.ts +38 -10
  26. package/dist/esm/src/evm/EvmEventTracker.js +183 -1
  27. package/dist/esm/src/evm/EvmProviderRegistry.js +27 -5
  28. package/dist/esm/src/evm/EvmRequestTracker.d.ts +36 -40
  29. package/dist/esm/src/evm/EvmRequestTracker.js +215 -114
  30. package/dist/esm/src/evm/batch.d.ts +94 -0
  31. package/dist/esm/src/evm/batch.js +123 -0
  32. package/dist/esm/src/provider/detection.d.ts +30 -0
  33. package/dist/esm/src/provider/detection.js +60 -0
  34. package/dist/esm/src/provider/index.d.ts +1 -1
  35. package/dist/esm/src/provider/index.js +1 -1
  36. package/dist/esm/src/types/base.d.ts +23 -0
  37. package/dist/esm/src/types/provider.d.ts +16 -0
  38. package/dist/esm/src/types/provider.js +16 -0
  39. package/dist/esm/src/version.d.ts +1 -1
  40. package/dist/esm/src/version.js +1 -1
  41. package/dist/esm/src/wagmi/WagmiEventHandler.d.ts +87 -0
  42. package/dist/esm/src/wagmi/WagmiEventHandler.js +536 -13
  43. package/dist/index.umd.min.js +1 -1
  44. package/package.json +4 -3
@@ -54,6 +54,8 @@ 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 { readWalletConnectPeer, isUserRejectionError } from "../provider";
58
+ import { readBatchId, readBatchStatusCode, readBatchChainId, batchCallOutcome, batchReceiptForCall, } from "../evm/batch";
57
59
  import { encodeWriteContractData, concatCalldataWithSuffix, extractFunctionArgs, buildSafeFunctionArgs, } from "./utils";
58
60
  /**
59
61
  * Built-in transaction fields that could collide with function args.
@@ -196,6 +198,48 @@ var ownerKey = function (writeKey, config) {
196
198
  }
197
199
  return "".concat(writeKey, ":").concat(id);
198
200
  };
201
+ // User-rejection detection is shared with the EIP-1193 path (4001, the
202
+ // WalletConnect 5000-family, and viem's typed error): see
203
+ // provider/detection.ts for the dialects and why 4001 alone missed every
204
+ // WalletConnect rejection.
205
+ var isUserRejection = isUserRejectionError;
206
+ /**
207
+ * Real wallet names behind WalletConnect connectors, resolved lazily.
208
+ *
209
+ * WalletConnect is a transport: the signing wallet (Ledger Live, MetaMask
210
+ * Mobile, Safe, ...) names itself in the session's peer metadata, reachable
211
+ * only through the connector's async `getProvider()`. Connect emission is
212
+ * deliberately synchronous (see the marker comments below), so the lookup
213
+ * can never be awaited in line - it is kicked fire-and-forget the first
214
+ * time a WalletConnect connector is seen, and every event after resolution
215
+ * carries the real wallet's name. The first connect may still say
216
+ * "WalletConnect"; that is the honest state at that instant.
217
+ *
218
+ * HONEST STATUS: with the new-connection invalidation below, no event the
219
+ * wagmi path emits TODAY observably carries the resolved name - connects
220
+ * fire before resolution and rebuilds suppress re-emission. The cache
221
+ * exists for the attribution work (wallet names on signature and
222
+ * transaction events), which fires mid-session, after resolution.
223
+ *
224
+ * Names are keyed by the CONNECTOR (stable across a page's sessions) so
225
+ * they actually serve reads; lookups are guarded per CONNECTION (wagmi
226
+ * replaces the connection object per session), so every new session
227
+ * re-resolves and OVERWRITES the name. A reconnect through the same
228
+ * connector to a different wallet can therefore mislabel at most the one
229
+ * event that fires between the new session's start and its resolution -
230
+ * one microtask for an initialised connector - and self-corrects. The
231
+ * alternative, keying names by connection, was tried and kept the name
232
+ * from ever surfacing: the only reader that fires per session runs before
233
+ * any lookup can resolve. WeakMaps, so nothing outlives its object.
234
+ */
235
+ var walletConnectPeerNames = new WeakMap();
236
+ var walletConnectPeerLookups = new WeakSet();
237
+ /** The connection whose lookup may write the connector's name: always the
238
+ * newest kicked one, so a slow resolution from a PREVIOUS session cannot
239
+ * land after the current session's and overwrite it. */
240
+ var walletConnectPeerLatest = new WeakMap();
241
+ /** Connections whose provider already has the hybrid-capture wrapper. */
242
+ var wagmiWrappedConnections = new WeakSet();
199
243
  /**
200
244
  * Pending transactions, shared per destination rather than per handler.
201
245
  *
@@ -205,6 +249,7 @@ var ownerKey = function (writeKey, config) {
205
249
  * broadcast to match the receipt against.
206
250
  */
207
251
  var pendingTransactionsByDestination = new Map();
252
+ var pendingBatchesByDestination = new Map();
208
253
  /**
209
254
  * Pending records outlive a rebuild by the same grace period as the markers.
210
255
  * The ordinary rebuild is cleanup THEN remount, so dropping them the instant
@@ -219,6 +264,7 @@ function schedulePendingTransactionExpiry(key) {
219
264
  var timer = setTimeout(function () {
220
265
  pendingTransactionExpiry.delete(key);
221
266
  pendingTransactionsByDestination.delete(key);
267
+ pendingBatchesByDestination.delete(key);
222
268
  }, MARKER_GRACE_MS);
223
269
  (_b = (_a = timer).unref) === null || _b === void 0 ? void 0 : _b.call(_a);
224
270
  pendingTransactionExpiry.set(key, timer);
@@ -285,6 +331,7 @@ export function __resetSeededWallet() {
285
331
  pendingTransactionExpiry.forEach(function (t) { return clearTimeout(t); });
286
332
  pendingTransactionExpiry.clear();
287
333
  pendingTransactionsByDestination.clear();
334
+ pendingBatchesByDestination.clear();
288
335
  announcedConnections.clear();
289
336
  liveHandlers.clear();
290
337
  markerExpiry.forEach(function (timer) { return clearTimeout(timer); });
@@ -497,6 +544,21 @@ var WagmiEventHandler = /** @class */ (function () {
497
544
  enumerable: false,
498
545
  configurable: true
499
546
  });
547
+ Object.defineProperty(WagmiEventHandler.prototype, "pendingBatches", {
548
+ /** Broadcast batches awaiting `callsStatus`, shared like the map above. */
549
+ get: function () {
550
+ var _a;
551
+ var key = (_a = this.ownerKey) !== null && _a !== void 0 ? _a : "";
552
+ var map = pendingBatchesByDestination.get(key);
553
+ if (!map) {
554
+ map = new Map();
555
+ pendingBatchesByDestination.set(key, map);
556
+ }
557
+ return map;
558
+ },
559
+ enumerable: false,
560
+ configurable: true
561
+ });
500
562
  /**
501
563
  * Set up listeners for wallet connection, disconnection, and chain changes
502
564
  */
@@ -573,6 +635,17 @@ var WagmiEventHandler = /** @class */ (function () {
573
635
  */
574
636
  WagmiEventHandler.prototype.seedFromCurrentState = function () {
575
637
  var _a, _b;
638
+ // Fire-and-forget here: the seed is synchronous by design, so its own
639
+ // connect may carry the generic name; the status flow awaits instead.
640
+ // A throwing store must not break construction.
641
+ try {
642
+ var snapshot = this.getState();
643
+ this.kickWalletConnectPeerLookup(snapshot);
644
+ this.wrapActiveConnectorProvider(snapshot);
645
+ }
646
+ catch (_c) {
647
+ /* the seed continues; names fall back to the connector's own */
648
+ }
576
649
  try {
577
650
  var state = this.getState();
578
651
  if (state.status !== "connected") {
@@ -825,7 +898,7 @@ var WagmiEventHandler = /** @class */ (function () {
825
898
  */
826
899
  WagmiEventHandler.prototype.handleStatusChange = function (status, prevStatus) {
827
900
  return __awaiter(this, void 0, void 0, function () {
828
- var state, address, chainId, disconnectedAddress, disconnectedChainId, walletKey, connection, connectorName, error_1, error_2;
901
+ var snapshot, state, address, chainId, disconnectedAddress, disconnectedChainId, walletKey, connection, connectorName, error_1, error_2;
829
902
  var _a, _b, _c, _d;
830
903
  return __generator(this, function (_e) {
831
904
  switch (_e.label) {
@@ -846,6 +919,18 @@ var WagmiEventHandler = /** @class */ (function () {
846
919
  return [2 /*return*/];
847
920
  }
848
921
  this.trackingState.isProcessing = true;
922
+ // Start resolving the wallet behind a WalletConnect session. NEVER
923
+ // awaited: every path from a store signal to its emission is
924
+ // synchronous by design, and an await here reorders transitions (it
925
+ // demonstrably drops connects under rapid connect/disconnect cycles).
926
+ try {
927
+ snapshot = this.getState();
928
+ this.kickWalletConnectPeerLookup(snapshot);
929
+ this.wrapActiveConnectorProvider(snapshot);
930
+ }
931
+ catch (_f) {
932
+ // A throwing store must not break the status flow.
933
+ }
849
934
  // A status change outranks any connection transition still in flight. A
850
935
  // full disconnect advances no ticket of its own, so without this an older
851
936
  // fallback continuation could resume and announce a wallet wagmi no longer
@@ -1113,6 +1198,16 @@ var WagmiEventHandler = /** @class */ (function () {
1113
1198
  * already recorded the new address synchronously by the time this runs. The
1114
1199
  * `lastAddress` check below is what makes the two paths mutually exclusive.
1115
1200
  */
1201
+ WagmiEventHandler.prototype.handleActiveAddressChangeEntryKick = function () {
1202
+ // An account switch replaces the connection object; kick a lookup for
1203
+ // the new one so events that follow can name the wallet.
1204
+ try {
1205
+ this.kickWalletConnectPeerLookup(this.getState());
1206
+ }
1207
+ catch (_a) {
1208
+ /* a throwing store must not break the flow */
1209
+ }
1210
+ };
1116
1211
  WagmiEventHandler.prototype.handleActiveAddressChange = function (address, prevAddress) {
1117
1212
  return __awaiter(this, void 0, void 0, function () {
1118
1213
  var state, trackedConnectionId, trackedConnectionGone, connectionChanged, liveChain, generation, chainId, stillConnected, goneChainId, error_3, liveNow, stillCurrent, walletKey, connection, alreadyAnnounced, connectorName, error_4;
@@ -1120,6 +1215,7 @@ var WagmiEventHandler = /** @class */ (function () {
1120
1215
  return __generator(this, function (_g) {
1121
1216
  switch (_g.label) {
1122
1217
  case 0:
1218
+ this.handleActiveAddressChangeEntryKick();
1123
1219
  state = this.getState();
1124
1220
  if (state.status !== "connected")
1125
1221
  return [2 /*return*/];
@@ -1449,8 +1545,23 @@ var WagmiEventHandler = /** @class */ (function () {
1449
1545
  return;
1450
1546
  }
1451
1547
  var queryType = queryKey[0];
1452
- // Only handle waitForTransactionReceipt queries
1453
- if (queryType !== "waitForTransactionReceipt") {
1548
+ // Only the two query families that settle something we broadcast:
1549
+ // waitForTransactionReceipt for single transactions, callsStatus for
1550
+ // EIP-5792 batches (both useCallsStatus and useWaitForCallsStatus share
1551
+ // the 'callsStatus' key, in wagmi 2 and 3 alike).
1552
+ if (queryType !== "waitForTransactionReceipt" && queryType !== "callsStatus") {
1553
+ return;
1554
+ }
1555
+ // Batch settlement dedupes on the pending-batch registry, NOT on
1556
+ // processedQueries: settling deletes the registration, so a duplicate
1557
+ // delivery finds nothing to do. A processed-key here would be worse
1558
+ // than redundant - the status query can complete BEFORE the sendCalls
1559
+ // mutation registers the batch (TanStack dispatches a mutation's
1560
+ // success state after its onSuccess callbacks, and apps await the
1561
+ // status inside onSuccess), and a key recorded on that early skip
1562
+ // would block every refetch from ever settling the batch.
1563
+ if (queryType === "callsStatus") {
1564
+ this.handleCallsStatusQuery(query);
1454
1565
  return;
1455
1566
  }
1456
1567
  var state = query.state;
@@ -1483,6 +1594,109 @@ var WagmiEventHandler = /** @class */ (function () {
1483
1594
  // Clean up old processed queries to prevent memory leaks
1484
1595
  cleanupOldEntries(this.processedQueries);
1485
1596
  };
1597
+ /**
1598
+ * Settle a just-registered batch from a status query that already ran.
1599
+ *
1600
+ * Best-effort by design: the minimal QueryClient interface the SDK
1601
+ * accepts is not guaranteed to expose cache lookup, and a missing
1602
+ * `getAll` just means settlement waits for the next query event, which
1603
+ * is where it normally comes from anyway.
1604
+ */
1605
+ WagmiEventHandler.prototype.settleFromCachedCallsStatus = function (batchId) {
1606
+ var _a, _b, _c;
1607
+ try {
1608
+ var cache = (_a = this.queryClient) === null || _a === void 0 ? void 0 : _a.getQueryCache();
1609
+ var queries = (_b = cache === null || cache === void 0 ? void 0 : cache.getAll) === null || _b === void 0 ? void 0 : _b.call(cache);
1610
+ if (!Array.isArray(queries))
1611
+ return;
1612
+ for (var _i = 0, queries_1 = queries; _i < queries_1.length; _i++) {
1613
+ var query = queries_1[_i];
1614
+ var key = query === null || query === void 0 ? void 0 : query.queryKey;
1615
+ if (Array.isArray(key) &&
1616
+ key[0] === "callsStatus" &&
1617
+ ((_c = key[1]) === null || _c === void 0 ? void 0 : _c.id) === batchId) {
1618
+ this.handleCallsStatusQuery(query);
1619
+ }
1620
+ }
1621
+ }
1622
+ catch (error) {
1623
+ logger.debug("WagmiEventHandler: cached callsStatus scan failed", error);
1624
+ }
1625
+ };
1626
+ /**
1627
+ * Settle an EIP-5792 batch from a `callsStatus` query.
1628
+ *
1629
+ * Only batches whose broadcast this SDK observed are settled: the batch id
1630
+ * must be in `pendingBatches`, for the same reason receipt queries are
1631
+ * gated on an observed hash - queries are visible to any code sharing the
1632
+ * QueryClient, and emitting for an id we never saw broadcast would let a
1633
+ * forged query invent transactions.
1634
+ *
1635
+ * Outcome semantics are shared with the EIP-1193 path (`src/evm/batch.ts`):
1636
+ * per-call receipts outrank the batch verdict, an atomic batch's single
1637
+ * receipt reaches every call, and a 600 leaves receipt-less calls
1638
+ * unsettled rather than guessed.
1639
+ */
1640
+ WagmiEventHandler.prototype.handleCallsStatusQuery = function (query) {
1641
+ var _this = this;
1642
+ var _a;
1643
+ if (!this.formo.isAutocaptureEnabled("transaction")) {
1644
+ return;
1645
+ }
1646
+ var queryKey = query.queryKey;
1647
+ // Query key format: ['callsStatus', { id, ... }]
1648
+ var params = queryKey[1];
1649
+ var batchId = params === null || params === void 0 ? void 0 : params.id;
1650
+ if (!batchId) {
1651
+ return;
1652
+ }
1653
+ var pending = this.pendingBatches.get(batchId);
1654
+ if (!pending) {
1655
+ logger.debug("WagmiEventHandler: unobserved batch", { batchId: batchId });
1656
+ return;
1657
+ }
1658
+ var state = query.state;
1659
+ if (state.status !== "success" || !state.data) {
1660
+ return;
1661
+ }
1662
+ try {
1663
+ var res_1 = state.data;
1664
+ var code_1 = readBatchStatusCode(res_1);
1665
+ // Below 200 the batch is still pending; the query will update again.
1666
+ if (code_1 === undefined || code_1 < 200) {
1667
+ return;
1668
+ }
1669
+ logger.debug("WagmiEventHandler: batch settled", { batchId: batchId, code: code_1 });
1670
+ // An explicit mutation chain is authoritative. Otherwise prefer the
1671
+ // chain the settlement result names - EIP-5792 v2 reports where the
1672
+ // batch actually landed - over one inferred from the connection at
1673
+ // broadcast, which goes stale if the wallet moves chains while the
1674
+ // prompt is up. Same precedence as the single-transaction receipt
1675
+ // path.
1676
+ var settledChainId_1 = pending.chainIdWasExplicit
1677
+ ? pending.chainId
1678
+ : (_a = readBatchChainId(res_1)) !== null && _a !== void 0 ? _a : pending.chainId;
1679
+ pending.calls.forEach(function (call, index) {
1680
+ var receipt = batchReceiptForCall(res_1, index, pending.calls.length);
1681
+ var outcome = batchCallOutcome(code_1, receipt);
1682
+ // 600 means SOME calls reverted, so a call with no receipt of its
1683
+ // own has not been decided. Reporting it either way would invent a
1684
+ // result; leaving it unsettled is the honest answer.
1685
+ if (outcome === undefined)
1686
+ return;
1687
+ _this.formo.transaction(__assign(__assign(__assign(__assign({ status: outcome, chainId: settledChainId_1 || 0, address: pending.address }, (call.data && { data: call.data })), (call.to && { to: call.to })), (call.value && { value: call.value })), ((receipt === null || receipt === void 0 ? void 0 : receipt.transactionHash)
1688
+ ? { transactionHash: receipt.transactionHash }
1689
+ : {})), __assign({ batch_size: pending.calls.length, batch_index: index, batch_id: batchId }, (pending.providerName
1690
+ ? { providerName: pending.providerName }
1691
+ : {})));
1692
+ });
1693
+ // Settled; a later refetch of the same query must not re-emit.
1694
+ this.pendingBatches.delete(batchId);
1695
+ }
1696
+ catch (error) {
1697
+ logger.error("WagmiEventHandler: callsStatus error:", error);
1698
+ }
1699
+ };
1486
1700
  /**
1487
1701
  * Handle waitForTransactionReceipt query completion
1488
1702
  * Emits CONFIRMED or REVERTED transaction status
@@ -1557,9 +1771,9 @@ var WagmiEventHandler = /** @class */ (function () {
1557
1771
  chainId: chainId,
1558
1772
  blockNumber: (_b = receipt === null || receipt === void 0 ? void 0 : receipt.blockNumber) === null || _b === void 0 ? void 0 : _b.toString(),
1559
1773
  });
1560
- 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 })),
1561
- // Spread function args as additional properties (only colliding keys are prefixed)
1562
- pendingTx === null || pendingTx === void 0 ? void 0 : pendingTx.safeFunctionArgs);
1774
+ 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 })), __assign(__assign({}, ((pendingTx === null || pendingTx === void 0 ? void 0 : pendingTx.providerName)
1775
+ ? { providerName: pendingTx.providerName }
1776
+ : this.mutationAttribution())), pendingTx === null || pendingTx === void 0 ? void 0 : pendingTx.safeFunctionArgs));
1563
1777
  // Clean up the pending transaction after confirmation
1564
1778
  this.pendingTransactions.delete(normalizedHash);
1565
1779
  }
@@ -1612,12 +1826,89 @@ var WagmiEventHandler = /** @class */ (function () {
1612
1826
  if (mutationType === "sendTransaction" || mutationType === "writeContract") {
1613
1827
  this.handleTransactionMutation(mutationType, mutation);
1614
1828
  }
1829
+ // Handle EIP-5792 batch mutations (useSendCalls). Absent this branch,
1830
+ // wagmi-mode apps captured nothing for a batch: the EIP-1193 request
1831
+ // wrapper that handles `wallet_sendCalls` is never installed in wagmi
1832
+ // mode, so the mutation was the only place the batch was visible at all.
1833
+ if (mutationType === "sendCalls") {
1834
+ this.handleSendCallsMutation(mutation);
1835
+ }
1615
1836
  // Clean up old processed mutations to prevent memory leaks
1616
1837
  cleanupOldEntries(this.processedMutations);
1617
1838
  };
1618
1839
  /**
1619
1840
  * Handle signature mutations (signMessage, signTypedData)
1620
1841
  */
1842
+ /**
1843
+ * Is a PENDING wagmi mutation already covering this wallet request?
1844
+ *
1845
+ * The hybrid-capture dedup. TanStack dispatches `pending` BEFORE the
1846
+ * mutationFn issues the wallet call (verified against query-core), so a
1847
+ * hook-driven request always finds its mutation here and the request
1848
+ * wrapper stands down; an imperative viem call never does, and the
1849
+ * wrapper captures it. Matching is by mutation type, refined with cheap
1850
+ * parameter checks where the shapes allow, and errs toward NOT skipping:
1851
+ * a duplicate is visible and diagnosable, a silent loss is neither.
1852
+ */
1853
+ WagmiEventHandler.prototype.hasMatchingPendingMutation = function (method, params) {
1854
+ var _a, _b, _c, _d, _e, _f;
1855
+ try {
1856
+ var cache = (_a = this.queryClient) === null || _a === void 0 ? void 0 : _a.getMutationCache();
1857
+ var mutations = (_b = cache === null || cache === void 0 ? void 0 : cache.getAll) === null || _b === void 0 ? void 0 : _b.call(cache);
1858
+ if (!Array.isArray(mutations))
1859
+ return false;
1860
+ var wanted = {
1861
+ personal_sign: ["signMessage"],
1862
+ eth_signTypedData_v4: ["signTypedData"],
1863
+ eth_sendTransaction: ["sendTransaction", "writeContract"],
1864
+ wallet_sendCalls: ["sendCalls"],
1865
+ };
1866
+ var types = wanted[method];
1867
+ if (!types)
1868
+ return false;
1869
+ for (var _i = 0, mutations_1 = mutations; _i < mutations_1.length; _i++) {
1870
+ var mutation = mutations_1[_i];
1871
+ if (((_c = mutation === null || mutation === void 0 ? void 0 : mutation.state) === null || _c === void 0 ? void 0 : _c.status) !== "pending")
1872
+ continue;
1873
+ var key = (_e = (_d = mutation === null || mutation === void 0 ? void 0 : mutation.options) === null || _d === void 0 ? void 0 : _d.mutationKey) === null || _e === void 0 ? void 0 : _e[0];
1874
+ if (typeof key !== "string" || !types.includes(key))
1875
+ continue;
1876
+ // Cheap refinements. On mismatch keep scanning; on no basis to
1877
+ // compare, treat the type-level match as decisive.
1878
+ var variables = (_f = mutation.state) === null || _f === void 0 ? void 0 : _f.variables;
1879
+ if (key === "sendTransaction" && variables) {
1880
+ var req = (Array.isArray(params) ? params[0] : undefined);
1881
+ var mutTo = variables.to;
1882
+ if (typeof (req === null || req === void 0 ? void 0 : req.to) === "string" &&
1883
+ typeof mutTo === "string" &&
1884
+ req.to.toLowerCase() !== mutTo.toLowerCase()) {
1885
+ continue;
1886
+ }
1887
+ }
1888
+ return true;
1889
+ }
1890
+ return false;
1891
+ }
1892
+ catch (_g) {
1893
+ return false;
1894
+ }
1895
+ };
1896
+ /**
1897
+ * Wallet attribution for mutation- and query-derived events.
1898
+ *
1899
+ * Mid-session events (signatures, transactions) fire after the peer
1900
+ * lookup has resolved, so a WalletConnect connector names its actual
1901
+ * signer here - the first observable consumer of the peer cache.
1902
+ */
1903
+ WagmiEventHandler.prototype.mutationAttribution = function () {
1904
+ try {
1905
+ var name_1 = this.getConnectorName(this.getState());
1906
+ return name_1 ? { providerName: name_1 } : undefined;
1907
+ }
1908
+ catch (_a) {
1909
+ return undefined;
1910
+ }
1911
+ };
1621
1912
  WagmiEventHandler.prototype.handleSignatureMutation = function (mutationType, mutation) {
1622
1913
  var _a, _b, _c, _d;
1623
1914
  if (!this.formo.isAutocaptureEnabled("signature")) {
@@ -1678,7 +1969,7 @@ var WagmiEventHandler = /** @class */ (function () {
1678
1969
  chainId: chainId,
1679
1970
  address: address,
1680
1971
  message: message,
1681
- });
1972
+ }, this.mutationAttribution());
1682
1973
  }
1683
1974
  catch (error) {
1684
1975
  logger.error("WagmiEventHandler: Error handling signature mutation:", error);
@@ -1688,7 +1979,7 @@ var WagmiEventHandler = /** @class */ (function () {
1688
1979
  * Handle transaction mutations (sendTransaction, writeContract)
1689
1980
  */
1690
1981
  WagmiEventHandler.prototype.handleTransactionMutation = function (mutationType, mutation) {
1691
- var _a;
1982
+ var _a, _b;
1692
1983
  if (!this.formo.isAutocaptureEnabled("transaction")) {
1693
1984
  return;
1694
1985
  }
@@ -1779,7 +2070,7 @@ var WagmiEventHandler = /** @class */ (function () {
1779
2070
  // Include the sender address to handle wallet switches between broadcast and confirmation
1780
2071
  if (status_2 === TransactionStatus.BROADCASTED && transactionHash) {
1781
2072
  var normalizedHash = transactionHash.toLowerCase();
1782
- var txDetails = __assign(__assign(__assign(__assign(__assign(__assign(__assign(__assign({ address: userAddress }, (chainId !== undefined && { chainId: chainId })), { chainIdWasExplicit: explicitChainId !== undefined }), (data && { data: data })), (to && { to: to })), (value && { value: value })), (function_name && { function_name: function_name })), (function_args && { function_args: function_args })), (safeFunctionArgs && { safeFunctionArgs: safeFunctionArgs }));
2073
+ var txDetails = __assign(__assign(__assign(__assign(__assign(__assign(__assign(__assign(__assign({ address: userAddress }, ((_b = this.mutationAttribution()) !== null && _b !== void 0 ? _b : {})), (chainId !== undefined && { chainId: chainId })), { chainIdWasExplicit: explicitChainId !== undefined }), (data && { data: data })), (to && { to: to })), (value && { value: value })), (function_name && { function_name: function_name })), (function_args && { function_args: function_args })), (safeFunctionArgs && { safeFunctionArgs: safeFunctionArgs }));
1783
2074
  this.pendingTransactions.set(normalizedHash, txDetails);
1784
2075
  logger.debug("WagmiEventHandler: Stored pending transaction for confirmation", {
1785
2076
  transactionHash: normalizedHash,
@@ -1793,14 +2084,99 @@ var WagmiEventHandler = /** @class */ (function () {
1793
2084
  }
1794
2085
  }
1795
2086
  }
1796
- 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 })),
1797
- // Spread function args as additional properties (only colliding keys are prefixed)
1798
- safeFunctionArgs);
2087
+ 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 })), __assign(__assign({}, this.mutationAttribution()), safeFunctionArgs));
1799
2088
  }
1800
2089
  catch (error) {
1801
2090
  logger.error("WagmiEventHandler: Error handling transaction mutation:", error);
1802
2091
  }
1803
2092
  };
2093
+ /**
2094
+ * One `transaction` event per call in an EIP-5792 batch, wagmi path.
2095
+ *
2096
+ * Mirrors `EvmRequestTracker.trackBatchedCalls` exactly: the CALL is the
2097
+ * unit of attribution, so each call gets its own STARTED at pending and
2098
+ * BROADCASTED (with `batch_id`) when the wallet returns an id. The batch's
2099
+ * on-chain outcome arrives through the `callsStatus` query, handled in
2100
+ * `handleCallsStatusQuery`.
2101
+ *
2102
+ * Rejection matches the 1193 path's rule: only a user rejection (4001
2103
+ * anywhere in the error chain) marks the calls rejected - one dismissal
2104
+ * dismisses the whole prompt, so every call in it is rejected, and
2105
+ * reporting only the first would undercount. Any other error (a wallet
2106
+ * without EIP-5792 support, a transport failure) emits nothing further:
2107
+ * inventing a rejection the user never made would be worse.
2108
+ */
2109
+ WagmiEventHandler.prototype.handleSendCallsMutation = function (mutation) {
2110
+ var _this = this;
2111
+ if (!this.formo.isAutocaptureEnabled("transaction")) {
2112
+ return;
2113
+ }
2114
+ var state = mutation.state;
2115
+ var variables = state.variables || {};
2116
+ var rawCalls = Array.isArray(variables.calls) ? variables.calls : [];
2117
+ if (rawCalls.length === 0) {
2118
+ return;
2119
+ }
2120
+ // Same resolution order as single transactions: an explicit per-call
2121
+ // value beats the tracked connection.
2122
+ var explicitChainId = normalizeChainId(variables.chainId);
2123
+ var chainId = explicitChainId !== null && explicitChainId !== void 0 ? explicitChainId : this.trackingState.lastChainId;
2124
+ var accountAddress = resolveAccountAddress(variables.account);
2125
+ var userAddress = accountAddress || this.trackingState.lastAddress;
2126
+ if (!userAddress) {
2127
+ logger.warn("WagmiEventHandler: sendCalls without address");
2128
+ return;
2129
+ }
2130
+ try {
2131
+ var calls_1 = rawCalls.map(function (call) { return ({
2132
+ to: call === null || call === void 0 ? void 0 : call.to,
2133
+ value: (call === null || call === void 0 ? void 0 : call.value) !== undefined ? String(call.value) : undefined,
2134
+ data: call === null || call === void 0 ? void 0 : call.data,
2135
+ }); });
2136
+ var attribution_1 = this.mutationAttribution();
2137
+ var emitAll = function (status, extra) {
2138
+ calls_1.forEach(function (call, index) {
2139
+ _this.formo.transaction(__assign(__assign(__assign({ status: status, chainId: chainId || 0, address: userAddress }, (call.data && { data: call.data })), (call.to && { to: call.to })), (call.value && { value: call.value })), __assign(__assign({ batch_size: calls_1.length, batch_index: index }, attribution_1), extra));
2140
+ });
2141
+ };
2142
+ if (state.status === "pending") {
2143
+ // STARTED carries no batch id, exactly as a single transaction has
2144
+ // no hash yet: the wallet has not issued one.
2145
+ logger.debug("WagmiEventHandler: sendCalls start", { calls: calls_1.length });
2146
+ emitAll(TransactionStatus.STARTED);
2147
+ }
2148
+ else if (state.status === "success") {
2149
+ var batchId = readBatchId(state.data);
2150
+ logger.debug("WagmiEventHandler: sendCalls broadcast", { batchId: batchId });
2151
+ emitAll(TransactionStatus.BROADCASTED, batchId ? { batch_id: batchId } : undefined);
2152
+ if (batchId) {
2153
+ this.pendingBatches.set(batchId, __assign(__assign(__assign({ address: userAddress }, (attribution_1 !== null && attribution_1 !== void 0 ? attribution_1 : {})), (chainId !== undefined && { chainId: chainId })), { chainIdWasExplicit: explicitChainId !== undefined, calls: calls_1 }));
2154
+ // Same bound as pendingTransactions, same reason.
2155
+ if (this.pendingBatches.size > 100) {
2156
+ var keys = Array.from(this.pendingBatches.keys());
2157
+ for (var i = 0; i < 50 && i < keys.length; i++) {
2158
+ this.pendingBatches.delete(keys[i]);
2159
+ }
2160
+ }
2161
+ // The status query can have completed BEFORE this registration:
2162
+ // TanStack dispatches a mutation's success state after its
2163
+ // onSuccess callbacks, and apps await waitForCallsStatus inside
2164
+ // onSuccess. That early query event found no registration and did
2165
+ // nothing, so look for its settled result in the cache now.
2166
+ this.settleFromCachedCallsStatus(batchId);
2167
+ }
2168
+ }
2169
+ else if (state.status === "error") {
2170
+ if (isUserRejection(state.error)) {
2171
+ logger.debug("WagmiEventHandler: sendCalls rejected");
2172
+ emitAll(TransactionStatus.REJECTED);
2173
+ }
2174
+ }
2175
+ }
2176
+ catch (error) {
2177
+ logger.error("WagmiEventHandler: sendCalls error:", error);
2178
+ }
2179
+ };
1804
2180
  /**
1805
2181
  * Get the current Wagmi state
1806
2182
  * Supports both getState() method and direct state property access
@@ -1878,7 +2254,154 @@ var WagmiEventHandler = /** @class */ (function () {
1878
2254
  return undefined;
1879
2255
  }
1880
2256
  var connection = state.connections.get(state.current);
1881
- return connection === null || connection === void 0 ? void 0 : connection.connector.name;
2257
+ var connector = connection === null || connection === void 0 ? void 0 : connection.connector;
2258
+ if (!connection || !connector) {
2259
+ return undefined;
2260
+ }
2261
+ var cached = walletConnectPeerNames.get(connector);
2262
+ if (cached) {
2263
+ return cached;
2264
+ }
2265
+ // Backstop kick; the flow entry points kick earlier so the lookup has
2266
+ // usually resolved by the time an emission reads the name.
2267
+ this.kickWalletConnectPeerLookup(state);
2268
+ return connector.name;
2269
+ };
2270
+ /**
2271
+ * Start resolving the wallet behind a WalletConnect connection.
2272
+ *
2273
+ * Fire-and-forget on purpose: emission paths are synchronous by design
2274
+ * and must never wait (see the connect marker comment). Called at the
2275
+ * START of the status/address flows rather than only at read time - the
2276
+ * lookup is one microtask for an initialised connector, and the emission
2277
+ * sits behind several awaits, so kicking early usually means even the
2278
+ * FIRST connect names the real wallet. When the race is lost the event
2279
+ * honestly says "WalletConnect" and every later event names the peer.
2280
+ */
2281
+ /**
2282
+ * Install the request wrapper on the active connector's provider.
2283
+ *
2284
+ * This is what lets wagmi mode capture IMPERATIVE viem calls
2285
+ * (walletClient.sendTransaction / .signMessage / .writeContract / raw
2286
+ * request), which create no mutation and were silently lost. Hook-driven
2287
+ * calls stay owned by the mutation handlers via the pending-mutation
2288
+ * dedup. Fire-and-forget per connection; a provider that cannot be
2289
+ * produced simply keeps mutation-only capture.
2290
+ */
2291
+ WagmiEventHandler.prototype.wrapActiveConnectorProvider = function (state) {
2292
+ var _this = this;
2293
+ var _a, _b;
2294
+ // OPT-IN only. Wagmi mode's baseline never touches the signing
2295
+ // transport; instrumenting the provider is an explicit integrator
2296
+ // decision (options.wagmi.eip1193Fallback), made auditable in
2297
+ // configuration rather than implied by a version bump.
2298
+ var optedIn = ((_b = (_a = this.formo.options) === null || _a === void 0 ? void 0 : _a.wagmi) === null || _b === void 0 ? void 0 : _b.eip1193Fallback) === true;
2299
+ if (!optedIn) {
2300
+ return;
2301
+ }
2302
+ var connection = state.current
2303
+ ? state.connections.get(state.current)
2304
+ : undefined;
2305
+ var connector = connection === null || connection === void 0 ? void 0 : connection.connector;
2306
+ if (!connection ||
2307
+ typeof (connector === null || connector === void 0 ? void 0 : connector.getProvider) !== "function" ||
2308
+ wagmiWrappedConnections.has(connection)) {
2309
+ return;
2310
+ }
2311
+ wagmiWrappedConnections.add(connection);
2312
+ connector
2313
+ .getProvider()
2314
+ .then(function (provider) {
2315
+ var _a, _b;
2316
+ (_b = (_a = _this.formo)._wrapWagmiProvider) === null || _b === void 0 ? void 0 : _b.call(_a, provider);
2317
+ })
2318
+ .catch(function () {
2319
+ // Mutation-only capture remains; retry on the next connection.
2320
+ wagmiWrappedConnections.delete(connection);
2321
+ });
2322
+ };
2323
+ WagmiEventHandler.prototype.kickWalletConnectPeerLookup = function (state) {
2324
+ var _a, _b;
2325
+ var connection = state.current
2326
+ ? state.connections.get(state.current)
2327
+ : undefined;
2328
+ var connector = connection === null || connection === void 0 ? void 0 : connection.connector;
2329
+ if (!connection ||
2330
+ !connector ||
2331
+ typeof connector.name !== "string" ||
2332
+ !/walletconnect/i.test(connector.name) ||
2333
+ typeof connector.getProvider !== "function" ||
2334
+ walletConnectPeerLookups.has(connection)) {
2335
+ return;
2336
+ }
2337
+ walletConnectPeerLookups.add(connection);
2338
+ // A NEW connection invalidates the cached name SYNCHRONOUSLY. The
2339
+ // connect flow reads the cache in the same tick it kicks the lookup,
2340
+ // so retaining the previous session's name here deterministically
2341
+ // attributed a reconnect-to-a-different-wallet to the OLD wallet.
2342
+ // Wrong is worse than generic: the new session's connect now says
2343
+ // "WalletConnect" and the resolved peer serves the session's LATER
2344
+ // events (signatures, transactions - the attribution work) instead.
2345
+ // A rebuild over the SAME connection does not re-kick (guard above),
2346
+ // so it keeps its already-proven name.
2347
+ if (walletConnectPeerLatest.get(connector) !== connection) {
2348
+ walletConnectPeerNames.delete(connector);
2349
+ }
2350
+ walletConnectPeerLatest.set(connector, connection);
2351
+ var settled = false;
2352
+ // A cached name from a PREVIOUS session is unproven for this one. It
2353
+ // keeps serving only until this session's lookup settles or the grace
2354
+ // timer fires - whichever ends the uncertainty first - so a hung
2355
+ // lookup cannot leave the old wallet's name attached indefinitely.
2356
+ var staleTimer = setTimeout(function () {
2357
+ if (!settled &&
2358
+ walletConnectPeerLatest.get(connector) === connection) {
2359
+ walletConnectPeerNames.delete(connector);
2360
+ }
2361
+ }, 3000);
2362
+ (_b = (_a = staleTimer).unref) === null || _b === void 0 ? void 0 : _b.call(_a);
2363
+ connector
2364
+ .getProvider()
2365
+ .then(function (provider) {
2366
+ settled = true;
2367
+ clearTimeout(staleTimer);
2368
+ // Only the NEWEST session's lookup may write. A previous session's
2369
+ // slow resolution landing late would otherwise overwrite the
2370
+ // current wallet's name with the old one.
2371
+ if (walletConnectPeerLatest.get(connector) !== connection) {
2372
+ return;
2373
+ }
2374
+ var peer = readWalletConnectPeer(provider);
2375
+ if (peer === null || peer === void 0 ? void 0 : peer.name) {
2376
+ walletConnectPeerNames.set(connector, peer.name);
2377
+ logger.debug("WagmiEventHandler: WalletConnect peer resolved", {
2378
+ peer: peer.name,
2379
+ });
2380
+ }
2381
+ else {
2382
+ // Resolved WITHOUT peer metadata: the previous wallet's name is
2383
+ // disproven for this session, not merely unproven. Drop it, and
2384
+ // let a later event retry the lookup - the session may simply
2385
+ // not have populated its peer yet.
2386
+ walletConnectPeerNames.delete(connector);
2387
+ walletConnectPeerLookups.delete(connection);
2388
+ }
2389
+ })
2390
+ .catch(function () {
2391
+ settled = true;
2392
+ clearTimeout(staleTimer);
2393
+ // The new session could not be inspected, so the PREVIOUS wallet's
2394
+ // name must not keep serving: drop it and fall back to the
2395
+ // connector's own name until a later session resolves. Guarded so
2396
+ // an old session's late failure cannot clear a newer resolution.
2397
+ if (walletConnectPeerLatest.get(connector) === connection) {
2398
+ walletConnectPeerNames.delete(connector);
2399
+ }
2400
+ // A failed lookup must not permanently disqualify the connection:
2401
+ // the connector may just have been initialising. A later event
2402
+ // retries.
2403
+ walletConnectPeerLookups.delete(connection);
2404
+ });
1882
2405
  };
1883
2406
  /**
1884
2407
  * Clean up all subscriptions