@formo/analytics 1.19.6 → 1.19.8

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.
@@ -45,22 +45,56 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
45
45
  if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
46
46
  }
47
47
  };
48
+ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
49
+ if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
50
+ if (ar || !(i in from)) {
51
+ if (!ar) ar = Array.prototype.slice.call(from, 0, i);
52
+ ar[i] = from[i];
53
+ }
54
+ }
55
+ return to.concat(ar || Array.prototype.slice.call(from));
56
+ };
48
57
  import { createStore } from "mipd";
49
- import { EVENTS_API_URL, EventType, LOCAL_ANONYMOUS_ID_KEY, SESSION_CURRENT_URL_KEY, SESSION_USER_ID_KEY, SESSION_WALLET_DETECTED_KEY, } from "./constants";
58
+ import { EVENTS_API_URL, EventType, LOCAL_ANONYMOUS_ID_KEY, SESSION_CURRENT_URL_KEY, SESSION_USER_ID_KEY, SESSION_WALLET_DETECTED_KEY, DEFAULT_PROVIDER_ICON, } from "./constants";
50
59
  import { cookie, EventManager, EventQueue, initStorageManager, logger, Logger, } from "./lib";
51
- import { SignatureStatus, TransactionStatus, } from "./types";
60
+ import { SignatureStatus, TransactionStatus, WRAPPED_REQUEST_SYMBOL, WRAPPED_REQUEST_REF_SYMBOL, } from "./types";
52
61
  import { toChecksumAddress } from "./utils";
53
62
  import { getValidAddress } from "./utils/address";
54
63
  import { isLocalhost } from "./validators";
55
64
  import { parseChainId } from "./utils/chain";
65
+ /**
66
+ * Constants for provider switching reasons
67
+ */
68
+ var PROVIDER_SWITCH_REASONS = {
69
+ ADDRESS_MISMATCH: "Address mismatch indicates wallet switch",
70
+ NO_ACCOUNTS: "Current provider has no accounts",
71
+ CHECK_FAILED: "Could not check current provider accounts"
72
+ };
56
73
  var FormoAnalytics = /** @class */ (function () {
57
74
  function FormoAnalytics(writeKey, options) {
58
75
  if (options === void 0) { options = {}; }
59
76
  var _a, _b;
60
77
  this.writeKey = writeKey;
61
78
  this.options = options;
62
- this._providerListeners = {};
79
+ this._providerListenersMap = new Map();
80
+ /**
81
+ * EIP-6963 provider details discovered through the browser
82
+ * This array contains all available providers with their metadata
83
+ */
63
84
  this._providers = [];
85
+ /**
86
+ * Set of providers that have been tracked with event listeners
87
+ * This is separate from _providers because:
88
+ * - _providers contains all discovered providers (EIP-6963)
89
+ * - _trackedProviders contains only providers that have been set up with listeners
90
+ * - A provider can be discovered but not yet tracked (e.g., during initialization)
91
+ * - A provider can be tracked but later removed from discovery
92
+ */
93
+ this._trackedProviders = new Set();
94
+ // Flag to prevent concurrent processing of accountsChanged events
95
+ this._processingAccountsChanged = false;
96
+ // Set to efficiently track seen providers for deduplication and O(1) lookup
97
+ this._seenProviders = new Set();
64
98
  this.currentUserId = "";
65
99
  this.config = {
66
100
  writeKey: writeKey,
@@ -89,14 +123,43 @@ var FormoAnalytics = /** @class */ (function () {
89
123
  maxQueueSize: options.maxQueueSize,
90
124
  flushInterval: options.flushInterval,
91
125
  }));
92
- // TODO: replace with eip6963
93
- var provider = options.provider || (window === null || window === void 0 ? void 0 : window.ethereum);
126
+ // Handle initial provider (injected) as fallback; listeners for EIP-6963 are added later
127
+ var provider = undefined;
128
+ var optProvider = options.provider;
129
+ if (optProvider) {
130
+ provider = optProvider;
131
+ }
132
+ else if (typeof window !== 'undefined' && window.ethereum) {
133
+ provider = window.ethereum;
134
+ }
94
135
  if (provider) {
95
136
  this.trackProvider(provider);
96
137
  }
97
138
  this.trackPageHit();
98
139
  this.trackPageHits();
99
140
  }
141
+ /**
142
+ * Helper method to check if a provider is different from the currently active one
143
+ * @param provider The provider to check
144
+ * @returns true if there's a provider mismatch, false otherwise
145
+ */
146
+ FormoAnalytics.prototype.isProviderMismatch = function (provider) {
147
+ // Only consider it a mismatch if we have an active provider AND the provider is different
148
+ // This allows legitimate provider switching while preventing race conditions
149
+ return this._provider != null && this._provider !== provider;
150
+ };
151
+ /**
152
+ * Check if a provider is in a valid state for switching
153
+ * @param provider The provider to validate
154
+ * @returns true if the provider is in a valid state
155
+ */
156
+ FormoAnalytics.prototype.isProviderInValidState = function (provider) {
157
+ // Basic validation: ensure provider exists and has required methods
158
+ return (provider &&
159
+ typeof provider.request === 'function' &&
160
+ typeof provider.on === 'function' &&
161
+ typeof provider.removeListener === 'function');
162
+ };
100
163
  FormoAnalytics.init = function (writeKey, options) {
101
164
  return __awaiter(this, void 0, void 0, function () {
102
165
  var analytics, _a;
@@ -114,6 +177,7 @@ var FormoAnalytics = /** @class */ (function () {
114
177
  return [4 /*yield*/, analytics.detectWallets(analytics._providers)];
115
178
  case 2:
116
179
  _b.sent();
180
+ analytics.trackProviders(analytics._providers);
117
181
  return [2 /*return*/, analytics];
118
182
  }
119
183
  });
@@ -151,6 +215,7 @@ var FormoAnalytics = /** @class */ (function () {
151
215
  this.currentUserId = undefined;
152
216
  cookie().remove(LOCAL_ANONYMOUS_ID_KEY);
153
217
  cookie().remove(SESSION_USER_ID_KEY);
218
+ cookie().remove(SESSION_WALLET_DETECTED_KEY);
154
219
  };
155
220
  /**
156
221
  * Emits a connect wallet event.
@@ -159,25 +224,30 @@ var FormoAnalytics = /** @class */ (function () {
159
224
  * @param {IFormoEventProperties} properties
160
225
  * @param {IFormoEventContext} context
161
226
  * @param {(...args: unknown[]) => void} callback
162
- * @throws {Error} If chainId or address is empty
163
227
  * @returns {Promise<void>}
164
228
  */
165
229
  FormoAnalytics.prototype.connect = function (_a, properties_1, context_1, callback_1) {
166
230
  return __awaiter(this, arguments, void 0, function (_b, properties, context, callback) {
167
- var validAddress;
231
+ var checksummedAddress;
168
232
  var chainId = _b.chainId, address = _b.address;
169
233
  return __generator(this, function (_c) {
170
234
  switch (_c.label) {
171
235
  case 0:
172
- if (!chainId) {
173
- logger.warn("Connect: Chain ID cannot be empty");
236
+ if (chainId === null || chainId === undefined) {
237
+ logger.warn("Connect: Chain ID cannot be null or undefined");
238
+ return [2 /*return*/];
174
239
  }
175
240
  if (!address) {
176
241
  logger.warn("Connect: Address cannot be empty");
242
+ return [2 /*return*/];
177
243
  }
178
244
  this.currentChainId = chainId;
179
- validAddress = getValidAddress(address);
180
- this.currentAddress = validAddress ? toChecksumAddress(validAddress) : undefined;
245
+ checksummedAddress = this.validateAndChecksumAddress(address);
246
+ if (!checksummedAddress) {
247
+ logger.warn("Connect: Invalid address provided (\"".concat(address, "\"). Please provide a valid Ethereum address in checksum format."));
248
+ return [2 /*return*/];
249
+ }
250
+ this.currentAddress = checksummedAddress;
181
251
  return [4 /*yield*/, this.trackEvent(EventType.CONNECT, {
182
252
  chainId: chainId,
183
253
  address: this.currentAddress,
@@ -200,21 +270,30 @@ var FormoAnalytics = /** @class */ (function () {
200
270
  */
201
271
  FormoAnalytics.prototype.disconnect = function (params, properties, context, callback) {
202
272
  return __awaiter(this, void 0, void 0, function () {
203
- var chainId, address;
273
+ var chainId, address, providerInfo, disconnectProperties;
204
274
  return __generator(this, function (_a) {
205
275
  switch (_a.label) {
206
276
  case 0:
207
277
  chainId = (params === null || params === void 0 ? void 0 : params.chainId) || this.currentChainId;
208
278
  address = (params === null || params === void 0 ? void 0 : params.address) || this.currentAddress;
209
- return [4 /*yield*/, this.trackEvent(EventType.DISCONNECT, {
210
- chainId: chainId,
211
- address: address,
212
- }, properties, context, callback)];
279
+ providerInfo = this._provider ? this.getProviderInfo(this._provider) : null;
280
+ logger.info("Disconnect: Emitting disconnect event with:", {
281
+ chainId: chainId,
282
+ address: address,
283
+ providerName: providerInfo === null || providerInfo === void 0 ? void 0 : providerInfo.name,
284
+ rdns: providerInfo === null || providerInfo === void 0 ? void 0 : providerInfo.rdns
285
+ });
286
+ disconnectProperties = __assign(__assign({}, (providerInfo && {
287
+ providerName: providerInfo.name,
288
+ rdns: providerInfo.rdns
289
+ })), properties);
290
+ return [4 /*yield*/, this.trackEvent(EventType.DISCONNECT, __assign(__assign({}, (chainId && { chainId: chainId })), (address && { address: address })), disconnectProperties, context, callback)];
213
291
  case 1:
214
292
  _a.sent();
215
293
  this.currentAddress = undefined;
216
294
  this.currentChainId = undefined;
217
- logger.info("Wallet disconnected: Cleared currentAddress and currentChainId");
295
+ this._provider = undefined;
296
+ logger.info("Wallet disconnected: Cleared currentAddress, currentChainId, and provider");
218
297
  return [2 /*return*/];
219
298
  }
220
299
  });
@@ -227,8 +306,6 @@ var FormoAnalytics = /** @class */ (function () {
227
306
  * @param {IFormoEventProperties} properties
228
307
  * @param {IFormoEventContext} context
229
308
  * @param {(...args: unknown[]) => void} callback
230
- * @throws {Error} If chainId is empty, zero, or not a valid number
231
- * @throws {Error} If no address is provided and no previous address is recorded
232
309
  * @returns {Promise<void>}
233
310
  */
234
311
  FormoAnalytics.prototype.chain = function (_a, properties_1, context_1, callback_1) {
@@ -238,13 +315,16 @@ var FormoAnalytics = /** @class */ (function () {
238
315
  switch (_c.label) {
239
316
  case 0:
240
317
  if (!chainId || Number(chainId) === 0) {
241
- throw new Error("FormoAnalytics::chain: chainId cannot be empty or 0");
318
+ logger.warn("FormoAnalytics::chain: chainId cannot be empty or 0");
319
+ return [2 /*return*/];
242
320
  }
243
321
  if (isNaN(Number(chainId))) {
244
- throw new Error("FormoAnalytics::chain: chainId must be a valid decimal number");
322
+ logger.warn("FormoAnalytics::chain: chainId must be a valid decimal number");
323
+ return [2 /*return*/];
245
324
  }
246
325
  if (!address && !this.currentAddress) {
247
- throw new Error("FormoAnalytics::chain: address was empty and no previous address has been recorded");
326
+ logger.warn("FormoAnalytics::chain: address was empty and no previous address has been recorded");
327
+ return [2 /*return*/];
248
328
  }
249
329
  this.currentChainId = chainId;
250
330
  return [4 /*yield*/, this.trackEvent(EventType.CHAIN, {
@@ -324,27 +404,28 @@ var FormoAnalytics = /** @class */ (function () {
324
404
  FormoAnalytics.prototype.identify = function (params, properties, context, callback) {
325
405
  return __awaiter(this, void 0, void 0, function () {
326
406
  var _i, _a, providerDetail, provider, address_1, err_1, userId, address, providerName, rdns, validAddress, e_1;
327
- return __generator(this, function (_b) {
328
- switch (_b.label) {
407
+ var _b;
408
+ return __generator(this, function (_c) {
409
+ switch (_c.label) {
329
410
  case 0:
330
- _b.trys.push([0, 11, , 12]);
411
+ _c.trys.push([0, 11, , 12]);
331
412
  if (!!params) return [3 /*break*/, 9];
332
413
  // If no params provided, auto-identify
333
414
  logger.info("Auto-identifying with providers:", this._providers.map(function (p) { return p.info.name; }));
334
415
  _i = 0, _a = this._providers;
335
- _b.label = 1;
416
+ _c.label = 1;
336
417
  case 1:
337
418
  if (!(_i < _a.length)) return [3 /*break*/, 8];
338
419
  providerDetail = _a[_i];
339
420
  provider = providerDetail.provider;
340
421
  if (!provider)
341
422
  return [3 /*break*/, 7];
342
- _b.label = 2;
423
+ _c.label = 2;
343
424
  case 2:
344
- _b.trys.push([2, 6, , 7]);
425
+ _c.trys.push([2, 6, , 7]);
345
426
  return [4 /*yield*/, this.getAddress(provider)];
346
427
  case 3:
347
- address_1 = _b.sent();
428
+ address_1 = _c.sent();
348
429
  if (!address_1) return [3 /*break*/, 5];
349
430
  logger.info("Auto-identifying", address_1, providerDetail.info.name, providerDetail.info.rdns);
350
431
  // NOTE: do not set this.currentAddress without explicit connect or identify
@@ -355,11 +436,11 @@ var FormoAnalytics = /** @class */ (function () {
355
436
  }, properties, context, callback)];
356
437
  case 4:
357
438
  // NOTE: do not set this.currentAddress without explicit connect or identify
358
- _b.sent();
359
- _b.label = 5;
439
+ _c.sent();
440
+ _c.label = 5;
360
441
  case 5: return [3 /*break*/, 7];
361
442
  case 6:
362
- err_1 = _b.sent();
443
+ err_1 = _c.sent();
363
444
  logger.error("Failed to identify provider ".concat(providerDetail.info.name, ":"), err_1);
364
445
  return [3 /*break*/, 7];
365
446
  case 7:
@@ -369,24 +450,32 @@ var FormoAnalytics = /** @class */ (function () {
369
450
  case 9:
370
451
  userId = params.userId, address = params.address, providerName = params.providerName, rdns = params.rdns;
371
452
  logger.info("Identify", address, userId, providerName, rdns);
372
- validAddress = getValidAddress(address);
373
- if (validAddress)
374
- this.currentAddress = toChecksumAddress(validAddress);
453
+ validAddress = undefined;
454
+ if (address) {
455
+ validAddress = this.validateAndChecksumAddress(address);
456
+ this.currentAddress = validAddress || undefined;
457
+ if (!validAddress) {
458
+ (_b = logger.warn) === null || _b === void 0 ? void 0 : _b.call(logger, "Invalid address provided to identify:", address);
459
+ }
460
+ }
461
+ else {
462
+ this.currentAddress = undefined;
463
+ }
375
464
  if (userId) {
376
465
  this.currentUserId = userId;
377
466
  cookie().set(SESSION_USER_ID_KEY, userId);
378
467
  }
379
468
  return [4 /*yield*/, this.trackEvent(EventType.IDENTIFY, {
380
- address: validAddress ? toChecksumAddress(validAddress) : undefined,
469
+ address: validAddress,
381
470
  providerName: providerName,
382
471
  userId: userId,
383
472
  rdns: rdns,
384
473
  }, properties, context, callback)];
385
474
  case 10:
386
- _b.sent();
475
+ _c.sent();
387
476
  return [3 /*break*/, 12];
388
477
  case 11:
389
- e_1 = _b.sent();
478
+ e_1 = _c.sent();
390
479
  logger.log("identify error", e_1);
391
480
  return [3 /*break*/, 12];
392
481
  case 12: return [2 /*return*/];
@@ -449,138 +538,314 @@ var FormoAnalytics = /** @class */ (function () {
449
538
  FormoAnalytics.prototype.trackProvider = function (provider) {
450
539
  logger.info("trackProvider", provider);
451
540
  try {
452
- if (provider === this._provider) {
541
+ if (!provider)
542
+ return;
543
+ if (this._trackedProviders.has(provider)) {
453
544
  logger.warn("TrackProvider: Provider already tracked.");
454
545
  return;
455
546
  }
456
- this.currentChainId = undefined;
457
- this.currentAddress = undefined;
458
- if (this._provider) {
459
- var actions = Object.keys(this._providerListeners);
460
- for (var _i = 0, actions_1 = actions; _i < actions_1.length; _i++) {
461
- var action = actions_1[_i];
462
- this._provider.removeListener(action, this._providerListeners[action]);
463
- delete this._providerListeners[action];
547
+ // Register listeners for this provider first
548
+ this.registerAccountsChangedListener(provider);
549
+ this.registerChainChangedListener(provider);
550
+ this.registerConnectListener(provider);
551
+ this.registerRequestListeners(provider);
552
+ this.registerDisconnectListener(provider);
553
+ // Only add to tracked providers after all listeners are successfully registered
554
+ this._trackedProviders.add(provider);
555
+ }
556
+ catch (error) {
557
+ logger.error("Error tracking provider:", error);
558
+ }
559
+ };
560
+ FormoAnalytics.prototype.trackProviders = function (providers) {
561
+ try {
562
+ for (var _i = 0, providers_1 = providers; _i < providers_1.length; _i++) {
563
+ var eip6963ProviderDetail = providers_1[_i];
564
+ var provider = eip6963ProviderDetail === null || eip6963ProviderDetail === void 0 ? void 0 : eip6963ProviderDetail.provider;
565
+ if (provider && !this._trackedProviders.has(provider)) {
566
+ this.trackProvider(provider);
464
567
  }
465
568
  }
466
- this._provider = provider;
467
- // Register listeners for web3 provider events
468
- this.registerAccountsChangedListener();
469
- this.registerChainChangedListener();
470
- this.registerConnectListener();
471
- this.registerRequestListeners();
472
569
  }
473
570
  catch (error) {
474
- logger.error("Error tracking provider:", error);
571
+ logger.error("Failed to track EIP-6963 providers during initialization:", error);
475
572
  }
476
573
  };
477
- FormoAnalytics.prototype.registerAccountsChangedListener = function () {
574
+ FormoAnalytics.prototype.addProviderListener = function (provider, event, listener) {
575
+ var map = this._providerListenersMap.get(provider) || {};
576
+ map[event] = listener;
577
+ this._providerListenersMap.set(provider, map);
578
+ };
579
+ FormoAnalytics.prototype.registerAccountsChangedListener = function (provider) {
478
580
  var _this = this;
479
- var _a;
480
581
  logger.info("registerAccountsChangedListener");
481
582
  var listener = function () {
482
583
  var args = [];
483
584
  for (var _i = 0; _i < arguments.length; _i++) {
484
585
  args[_i] = arguments[_i];
485
586
  }
486
- return _this.onAccountsChanged(args[0]);
587
+ return _this.onAccountsChanged(provider, args[0]);
487
588
  };
488
- (_a = this._provider) === null || _a === void 0 ? void 0 : _a.on("accountsChanged", listener);
489
- this._providerListeners["accountsChanged"] = listener;
589
+ provider.on("accountsChanged", listener);
590
+ this.addProviderListener(provider, "accountsChanged", listener);
490
591
  };
491
- FormoAnalytics.prototype.onAccountsChanged = function (accounts) {
592
+ FormoAnalytics.prototype.onAccountsChanged = function (provider, accounts) {
492
593
  return __awaiter(this, void 0, void 0, function () {
493
- var validAddress, address, _a;
494
- return __generator(this, function (_b) {
495
- switch (_b.label) {
594
+ return __generator(this, function (_a) {
595
+ switch (_a.label) {
496
596
  case 0:
497
597
  logger.info("onAccountsChanged", accounts);
498
- if (!(accounts.length === 0)) return [3 /*break*/, 2];
499
- // Handle wallet disconnect
500
- return [4 /*yield*/, this.disconnect()];
598
+ // Prevent concurrent processing of accountsChanged events to avoid race conditions
599
+ if (this._processingAccountsChanged) {
600
+ logger.debug("OnAccountsChanged: Already processing accountsChanged, skipping", {
601
+ provider: this.getProviderInfo(provider).name
602
+ });
603
+ return [2 /*return*/];
604
+ }
605
+ this._processingAccountsChanged = true;
606
+ _a.label = 1;
501
607
  case 1:
502
- // Handle wallet disconnect
503
- _b.sent();
504
- return [2 /*return*/];
608
+ _a.trys.push([1, , 3, 4]);
609
+ return [4 /*yield*/, this._handleAccountsChanged(provider, accounts)];
505
610
  case 2:
506
- validAddress = getValidAddress(accounts[0]);
507
- if (!validAddress) {
611
+ _a.sent();
612
+ return [3 /*break*/, 4];
613
+ case 3:
614
+ this._processingAccountsChanged = false;
615
+ return [7 /*endfinally*/];
616
+ case 4: return [2 /*return*/];
617
+ }
618
+ });
619
+ });
620
+ };
621
+ /**
622
+ * Handles changes to the accounts of a given EIP-1193 provider.
623
+ *
624
+ * @param provider - The EIP-1193 provider whose accounts have changed.
625
+ * @param accounts - The new array of account addresses. An empty array indicates a disconnect.
626
+ * @returns A promise that resolves when the account change has been processed.
627
+ *
628
+ * If the accounts array is empty and the provider is the active provider, this method triggers
629
+ * a disconnect flow. Otherwise, it updates the state to reflect the new accounts as needed.
630
+ */
631
+ FormoAnalytics.prototype._handleAccountsChanged = function (provider, accounts) {
632
+ return __awaiter(this, void 0, void 0, function () {
633
+ var error_1, address, currentStoredAddress, newProviderAddress, activeProviderAccounts, error_2, nextChainId, wasDisconnected, providerInfo, effectiveChainId;
634
+ return __generator(this, function (_a) {
635
+ switch (_a.label) {
636
+ case 0:
637
+ if (!(accounts.length === 0)) return [3 /*break*/, 7];
638
+ if (!(this._provider === provider)) return [3 /*break*/, 5];
639
+ logger.info("OnAccountsChanged: Detecting disconnect, current state:", {
640
+ currentAddress: this.currentAddress,
641
+ currentChainId: this.currentChainId,
642
+ providerMatch: this._provider === provider
643
+ });
644
+ _a.label = 1;
645
+ case 1:
646
+ _a.trys.push([1, 3, , 4]);
647
+ // Pass current state explicitly to ensure we have the data for the disconnect event
648
+ return [4 /*yield*/, this.disconnect({
649
+ chainId: this.currentChainId,
650
+ address: this.currentAddress
651
+ })];
652
+ case 2:
653
+ // Pass current state explicitly to ensure we have the data for the disconnect event
654
+ _a.sent();
655
+ return [3 /*break*/, 4];
656
+ case 3:
657
+ error_1 = _a.sent();
658
+ logger.error("Failed to disconnect provider on accountsChanged", error_1);
659
+ return [3 /*break*/, 4];
660
+ case 4: return [3 /*break*/, 6];
661
+ case 5:
662
+ logger.info("OnAccountsChanged: Ignoring disconnect for non-active provider");
663
+ _a.label = 6;
664
+ case 6: return [2 /*return*/];
665
+ case 7:
666
+ address = this.validateAndChecksumAddress(accounts[0]);
667
+ if (!address) {
508
668
  logger.warn("onAccountsChanged: Invalid address received", accounts[0]);
509
669
  return [2 /*return*/];
510
670
  }
511
- address = toChecksumAddress(validAddress);
512
- if (address === this.currentAddress) {
513
- // We have already reported this address
671
+ if (!(this._provider && this._provider !== provider)) return [3 /*break*/, 18];
672
+ currentStoredAddress = this.currentAddress;
673
+ newProviderAddress = this.validateAndChecksumAddress(address);
674
+ logger.info("OnAccountsChanged: Different provider attempting to connect", {
675
+ activeProvider: this.getProviderInfo(this._provider).name,
676
+ eventProvider: this.getProviderInfo(provider).name,
677
+ currentStoredAddress: currentStoredAddress,
678
+ newProviderAddress: newProviderAddress
679
+ });
680
+ _a.label = 8;
681
+ case 8:
682
+ _a.trys.push([8, 16, , 18]);
683
+ return [4 /*yield*/, this.getAccounts(this._provider)];
684
+ case 9:
685
+ activeProviderAccounts = _a.sent();
686
+ logger.info("OnAccountsChanged: Checking current provider accounts", {
687
+ activeProvider: this.getProviderInfo(this._provider).name,
688
+ accountsLength: activeProviderAccounts ? activeProviderAccounts.length : 0,
689
+ accounts: activeProviderAccounts
690
+ });
691
+ if (!(activeProviderAccounts && activeProviderAccounts.length > 0)) return [3 /*break*/, 13];
692
+ if (!(newProviderAddress && currentStoredAddress && newProviderAddress !== currentStoredAddress)) return [3 /*break*/, 11];
693
+ logger.info("OnAccountsChanged: Different address detected, switching providers despite current provider having accounts", {
694
+ activeProvider: this.getProviderInfo(this._provider).name,
695
+ eventProvider: this.getProviderInfo(provider).name,
696
+ currentAddress: currentStoredAddress,
697
+ newAddress: newProviderAddress,
698
+ reason: PROVIDER_SWITCH_REASONS.ADDRESS_MISMATCH
699
+ });
700
+ // Emit disconnect for the old provider
701
+ return [4 /*yield*/, this.disconnect({
702
+ chainId: this.currentChainId,
703
+ address: this.currentAddress
704
+ })];
705
+ case 10:
706
+ // Emit disconnect for the old provider
707
+ _a.sent();
708
+ // Clear state and let the new provider become active
709
+ this._provider = undefined;
710
+ return [3 /*break*/, 12];
711
+ case 11:
712
+ logger.info("OnAccountsChanged: Current provider still has accounts and same address, ignoring new provider", {
713
+ activeProvider: this.getProviderInfo(this._provider).name,
714
+ eventProvider: this.getProviderInfo(provider).name,
715
+ activeProviderAccountsCount: activeProviderAccounts.length,
716
+ currentAddress: currentStoredAddress,
717
+ newAddress: newProviderAddress
718
+ });
719
+ return [2 /*return*/];
720
+ case 12: return [3 /*break*/, 15];
721
+ case 13:
722
+ logger.info("OnAccountsChanged: Current provider has no accounts, switching to new provider", {
723
+ oldProvider: this.getProviderInfo(this._provider).name,
724
+ newProvider: this.getProviderInfo(provider).name,
725
+ reason: PROVIDER_SWITCH_REASONS.NO_ACCOUNTS
726
+ });
727
+ // Emit disconnect for the old provider that didn't signal properly
728
+ return [4 /*yield*/, this.disconnect({
729
+ chainId: this.currentChainId,
730
+ address: this.currentAddress
731
+ })];
732
+ case 14:
733
+ // Emit disconnect for the old provider that didn't signal properly
734
+ _a.sent();
735
+ // Clear state and let the new provider become active
736
+ this._provider = undefined;
737
+ _a.label = 15;
738
+ case 15: return [3 /*break*/, 18];
739
+ case 16:
740
+ error_2 = _a.sent();
741
+ logger.warn("OnAccountsChanged: Could not check current provider accounts, switching to new provider", {
742
+ error: error_2 instanceof Error ? error_2.message : String(error_2),
743
+ errorType: error_2 instanceof Error ? error_2.constructor.name : typeof error_2,
744
+ oldProvider: this._provider ? this.getProviderInfo(this._provider).name : 'unknown',
745
+ newProvider: this.getProviderInfo(provider).name,
746
+ reason: PROVIDER_SWITCH_REASONS.CHECK_FAILED
747
+ });
748
+ // If we can't check the current provider, assume it's disconnected
749
+ return [4 /*yield*/, this.disconnect({
750
+ chainId: this.currentChainId,
751
+ address: this.currentAddress
752
+ })];
753
+ case 17:
754
+ // If we can't check the current provider, assume it's disconnected
755
+ _a.sent();
756
+ this._provider = undefined;
757
+ return [3 /*break*/, 18];
758
+ case 18:
759
+ // Set provider if none exists (first connection)
760
+ if (!this._provider) {
761
+ this._provider = provider;
762
+ }
763
+ // If both the provider and address are the same, no-op
764
+ if (this._provider === provider && address === this.currentAddress) {
514
765
  return [2 /*return*/];
515
766
  }
516
- // Handle wallet connect
767
+ return [4 /*yield*/, this.getCurrentChainId(provider)];
768
+ case 19:
769
+ nextChainId = _a.sent();
770
+ wasDisconnected = !this.currentAddress;
517
771
  this.currentAddress = address;
518
- _a = this;
519
- return [4 /*yield*/, this.getCurrentChainId()];
520
- case 3:
521
- _a.currentChainId = _b.sent();
522
- this.connect({ chainId: this.currentChainId, address: address });
772
+ this.currentChainId = nextChainId;
773
+ providerInfo = this.getProviderInfo(provider);
774
+ logger.info("OnAccountsChanged: Detected wallet connection, emitting connect event", {
775
+ chainId: nextChainId,
776
+ address: address,
777
+ wasDisconnected: wasDisconnected,
778
+ providerName: providerInfo.name,
779
+ rdns: providerInfo.rdns,
780
+ hasChainId: !!nextChainId
781
+ });
782
+ effectiveChainId = nextChainId || 0;
783
+ if (effectiveChainId === 0) {
784
+ logger.info("OnAccountsChanged: Using fallback chainId 0 for connect event");
785
+ }
786
+ this.connect({
787
+ chainId: effectiveChainId,
788
+ address: address
789
+ }, {
790
+ providerName: providerInfo.name,
791
+ rdns: providerInfo.rdns
792
+ }).catch(function (error) {
793
+ logger.error("Failed to track connect event during account change:", error);
794
+ });
523
795
  return [2 /*return*/];
524
796
  }
525
797
  });
526
798
  });
527
799
  };
528
- FormoAnalytics.prototype.registerChainChangedListener = function () {
800
+ FormoAnalytics.prototype.registerChainChangedListener = function (provider) {
529
801
  var _this = this;
530
- var _a;
531
802
  logger.info("registerChainChangedListener");
532
803
  var listener = function () {
533
804
  var args = [];
534
805
  for (var _i = 0; _i < arguments.length; _i++) {
535
806
  args[_i] = arguments[_i];
536
807
  }
537
- return _this.onChainChanged(args[0]);
808
+ return _this.onChainChanged(provider, args[0]);
538
809
  };
539
- (_a = this.provider) === null || _a === void 0 ? void 0 : _a.on("chainChanged", listener);
540
- this._providerListeners["chainChanged"] = listener;
810
+ provider.on("chainChanged", listener);
811
+ this.addProviderListener(provider, "chainChanged", listener);
541
812
  };
542
- FormoAnalytics.prototype.onChainChanged = function (chainIdHex) {
813
+ FormoAnalytics.prototype.onChainChanged = function (provider, chainIdHex) {
543
814
  return __awaiter(this, void 0, void 0, function () {
544
- var address, validAddress;
815
+ var nextChainId;
545
816
  return __generator(this, function (_a) {
546
- switch (_a.label) {
547
- case 0:
548
- logger.info("onChainChanged", chainIdHex);
549
- this.currentChainId = parseChainId(chainIdHex);
550
- if (!!this.currentAddress) return [3 /*break*/, 2];
551
- if (!this.provider) {
552
- logger.info("OnChainChanged: Provider not found. CHAIN_CHANGED not reported");
553
- return [2 /*return*/, Promise.resolve()];
554
- }
555
- return [4 /*yield*/, this.getAddress()];
556
- case 1:
557
- address = _a.sent();
558
- if (!address) {
559
- logger.info("OnChainChanged: Unable to fetch or store connected address");
560
- return [2 /*return*/, Promise.resolve()];
561
- }
562
- validAddress = getValidAddress(address);
563
- this.currentAddress = validAddress ? toChecksumAddress(validAddress) : undefined;
564
- _a.label = 2;
565
- case 2:
566
- // Proceed only if the address exists
567
- if (this.currentAddress) {
568
- return [2 /*return*/, this.chain({
569
- chainId: this.currentChainId,
570
- address: this.currentAddress,
571
- })];
572
- }
573
- else {
574
- logger.info("OnChainChanged: Current connected address is null despite fetch attempt");
575
- }
576
- return [2 /*return*/];
817
+ logger.info("onChainChanged", chainIdHex);
818
+ nextChainId = parseChainId(chainIdHex);
819
+ // Only handle chain changes for the active provider (or if none is set yet)
820
+ if (this.isProviderMismatch(provider)) {
821
+ this.handleProviderMismatch(provider);
822
+ }
823
+ // Chain changes only matter for connected users
824
+ if (!this.currentAddress) {
825
+ logger.info("OnChainChanged: No current address, user appears disconnected");
826
+ return [2 /*return*/, Promise.resolve()];
827
+ }
828
+ // Set provider if none exists
829
+ if (!this._provider) {
830
+ this._provider = provider;
831
+ }
832
+ this.currentChainId = nextChainId;
833
+ try {
834
+ // This is just a chain change since we already confirmed currentAddress exists
835
+ return [2 /*return*/, this.chain({
836
+ chainId: this.currentChainId,
837
+ address: this.currentAddress,
838
+ })];
577
839
  }
840
+ catch (error) {
841
+ logger.error("OnChainChanged: Failed to emit chain event:", error);
842
+ }
843
+ return [2 /*return*/];
578
844
  });
579
845
  });
580
846
  };
581
- FormoAnalytics.prototype.registerConnectListener = function () {
847
+ FormoAnalytics.prototype.registerConnectListener = function (provider) {
582
848
  var _this = this;
583
- var _a;
584
849
  logger.info("registerConnectListener");
585
850
  var listener = function () {
586
851
  var args = [];
@@ -588,14 +853,51 @@ var FormoAnalytics = /** @class */ (function () {
588
853
  args[_i] = arguments[_i];
589
854
  }
590
855
  var connection = args[0];
591
- _this.onConnected(connection);
856
+ _this.onConnected(provider, connection);
592
857
  };
593
- (_a = this._provider) === null || _a === void 0 ? void 0 : _a.on("connect", listener);
594
- this._providerListeners["connect"] = listener;
858
+ provider.on("connect", listener);
859
+ this.addProviderListener(provider, "connect", listener);
860
+ };
861
+ FormoAnalytics.prototype.registerDisconnectListener = function (provider) {
862
+ var _this = this;
863
+ logger.info("registerDisconnectListener");
864
+ var listener = function (_error) { return __awaiter(_this, void 0, void 0, function () {
865
+ var e_2;
866
+ return __generator(this, function (_a) {
867
+ switch (_a.label) {
868
+ case 0:
869
+ if (this._provider !== provider)
870
+ return [2 /*return*/];
871
+ logger.info("OnDisconnect: Wallet disconnect event received, current state:", {
872
+ currentAddress: this.currentAddress,
873
+ currentChainId: this.currentChainId
874
+ });
875
+ _a.label = 1;
876
+ case 1:
877
+ _a.trys.push([1, 3, , 4]);
878
+ // Pass current state explicitly to ensure we have the data for the disconnect event
879
+ return [4 /*yield*/, this.disconnect({
880
+ chainId: this.currentChainId,
881
+ address: this.currentAddress
882
+ })];
883
+ case 2:
884
+ // Pass current state explicitly to ensure we have the data for the disconnect event
885
+ _a.sent();
886
+ return [3 /*break*/, 4];
887
+ case 3:
888
+ e_2 = _a.sent();
889
+ logger.error("Error during disconnect in disconnect listener", e_2);
890
+ return [3 /*break*/, 4];
891
+ case 4: return [2 /*return*/];
892
+ }
893
+ });
894
+ }); };
895
+ provider.on("disconnect", listener);
896
+ this.addProviderListener(provider, "disconnect", listener);
595
897
  };
596
- FormoAnalytics.prototype.onConnected = function (connection) {
898
+ FormoAnalytics.prototype.onConnected = function (provider, connection) {
597
899
  return __awaiter(this, void 0, void 0, function () {
598
- var chainId, address, e_2;
900
+ var chainId, address, wasDisconnected, isActiveProvider, providerInfo, effectiveChainId, providerInfo, e_3;
599
901
  return __generator(this, function (_a) {
600
902
  switch (_a.label) {
601
903
  case 0:
@@ -603,53 +905,105 @@ var FormoAnalytics = /** @class */ (function () {
603
905
  _a.label = 1;
604
906
  case 1:
605
907
  _a.trys.push([1, 3, , 4]);
606
- if (!connection || typeof connection.chainId !== 'string')
908
+ if (!(connection === null || connection === void 0 ? void 0 : connection.chainId) || typeof connection.chainId !== 'string')
607
909
  return [2 /*return*/];
608
910
  chainId = parseChainId(connection.chainId);
609
- return [4 /*yield*/, this.getAddress()];
911
+ return [4 /*yield*/, this.getAddress(provider)];
610
912
  case 2:
611
913
  address = _a.sent();
612
- if (chainId !== null && chainId !== undefined && address) {
613
- this.connect({ chainId: chainId, address: address });
914
+ if (chainId && address) {
915
+ wasDisconnected = !this.currentAddress;
916
+ // Set provider if none exists
917
+ if (!this._provider) {
918
+ this._provider = provider;
919
+ }
920
+ isActiveProvider = this._provider === provider;
921
+ // Only update global state (chainId/address) from the active provider
922
+ if (isActiveProvider) {
923
+ this.currentChainId = chainId;
924
+ this.currentAddress = this.validateAndChecksumAddress(address) || undefined;
925
+ }
926
+ if (isActiveProvider && this.currentAddress) {
927
+ providerInfo = this.getProviderInfo(provider);
928
+ logger.info("OnConnected: Detected wallet connection, emitting connect event", {
929
+ chainId: chainId,
930
+ wasDisconnected: wasDisconnected,
931
+ providerName: providerInfo.name,
932
+ rdns: providerInfo.rdns,
933
+ hasChainId: !!chainId,
934
+ isActiveProvider: isActiveProvider
935
+ });
936
+ effectiveChainId = chainId || 0;
937
+ if (effectiveChainId === 0) {
938
+ logger.info("OnConnected: Using fallback chainId 0 for connect event");
939
+ }
940
+ this.connect({
941
+ chainId: effectiveChainId,
942
+ address: address
943
+ }, {
944
+ providerName: providerInfo.name,
945
+ rdns: providerInfo.rdns
946
+ }).catch(function (error) {
947
+ logger.error("Failed to track connect event during provider connection:", error);
948
+ });
949
+ }
950
+ else if (address && !isActiveProvider) {
951
+ providerInfo = this.getProviderInfo(provider);
952
+ logger.debug("OnConnected: Skipping connect event for non-active provider", {
953
+ chainId: chainId,
954
+ providerName: providerInfo.name,
955
+ rdns: providerInfo.rdns,
956
+ isActiveProvider: isActiveProvider,
957
+ activeProviderInfo: this._provider ? this.getProviderInfo(this._provider) : null
958
+ });
959
+ }
614
960
  }
615
961
  return [3 /*break*/, 4];
616
962
  case 3:
617
- e_2 = _a.sent();
618
- logger.error("Error handling connect event", e_2);
963
+ e_3 = _a.sent();
964
+ logger.error("Error handling connect event", e_3);
619
965
  return [3 /*break*/, 4];
620
966
  case 4: return [2 /*return*/];
621
967
  }
622
968
  });
623
969
  });
624
970
  };
625
- FormoAnalytics.prototype.registerRequestListeners = function () {
971
+ FormoAnalytics.prototype.registerRequestListeners = function (provider) {
626
972
  var _this = this;
627
- var _a;
628
973
  logger.info("registerRequestListeners");
629
- if (!this.provider) {
974
+ if (!provider) {
630
975
  logger.error("Provider not found for request (signature, transaction) tracking");
631
976
  return;
632
977
  }
633
- if (((_a = Object.getOwnPropertyDescriptor(this.provider, "request")) === null || _a === void 0 ? void 0 : _a.writable) ===
634
- false) {
635
- logger.warn("Provider.request is not writable");
978
+ // Check if the provider is already wrapped with our SDK's wrapper
979
+ var currentRequest = provider.request;
980
+ if (this.isProviderAlreadyWrapped(provider, currentRequest)) {
981
+ logger.info("Provider already wrapped with our SDK; skipping request wrapping.");
636
982
  return;
637
983
  }
638
- var request = this.provider.request.bind(this.provider);
639
- this.provider.request = function (_a) { return __awaiter(_this, [_a], void 0, function (_b) {
640
- var response_1, error_1, transactionHash_1, error_2;
984
+ var request = provider.request.bind(provider);
985
+ var wrappedRequest = function (_a) { return __awaiter(_this, [_a], void 0, function (_b) {
986
+ var capturedChainId_1, _c, response_1, error_3, rpcError, transactionHash_1, error_4, rpcError;
641
987
  var _this = this;
642
988
  var method = _b.method, params = _b.params;
643
- return __generator(this, function (_c) {
644
- switch (_c.label) {
989
+ return __generator(this, function (_d) {
990
+ switch (_d.label) {
645
991
  case 0:
646
992
  if (!(Array.isArray(params) &&
647
- ["eth_signTypedData_v4", "personal_sign"].includes(method))) return [3 /*break*/, 4];
993
+ ["eth_signTypedData_v4", "personal_sign"].includes(method))) return [3 /*break*/, 6];
994
+ _c = this.currentChainId;
995
+ if (_c) return [3 /*break*/, 2];
996
+ return [4 /*yield*/, this.getCurrentChainId(provider)];
997
+ case 1:
998
+ _c = (_d.sent());
999
+ _d.label = 2;
1000
+ case 2:
1001
+ capturedChainId_1 = _c;
648
1002
  // Fire-and-forget tracking
649
1003
  (function () { return __awaiter(_this, void 0, void 0, function () {
650
1004
  return __generator(this, function (_a) {
651
1005
  try {
652
- this.signature(__assign({ status: SignatureStatus.REQUESTED }, this.buildSignatureEventPayload(method, params)));
1006
+ this.signature(__assign({ status: SignatureStatus.REQUESTED }, this.buildSignatureEventPayload(method, params, undefined, capturedChainId_1)));
653
1007
  }
654
1008
  catch (e) {
655
1009
  logger.error("Formo: Failed to track signature request", e);
@@ -657,127 +1011,139 @@ var FormoAnalytics = /** @class */ (function () {
657
1011
  return [2 /*return*/];
658
1012
  });
659
1013
  }); })();
660
- _c.label = 1;
661
- case 1:
662
- _c.trys.push([1, 3, , 4]);
1014
+ _d.label = 3;
1015
+ case 3:
1016
+ _d.trys.push([3, 5, , 6]);
663
1017
  return [4 /*yield*/, request({ method: method, params: params })];
664
- case 2:
665
- response_1 = (_c.sent());
666
- (function () { return __awaiter(_this, void 0, void 0, function () {
667
- return __generator(this, function (_a) {
668
- try {
669
- if (response_1) {
670
- this.signature(__assign({ status: SignatureStatus.CONFIRMED }, this.buildSignatureEventPayload(method, params, response_1)));
1018
+ case 4:
1019
+ response_1 = (_d.sent());
1020
+ // Track signature confirmation only for truthy responses
1021
+ if (response_1) {
1022
+ (function () { return __awaiter(_this, void 0, void 0, function () {
1023
+ return __generator(this, function (_a) {
1024
+ try {
1025
+ this.signature(__assign({ status: SignatureStatus.CONFIRMED }, this.buildSignatureEventPayload(method, params, response_1, capturedChainId_1)));
671
1026
  }
672
- }
673
- catch (e) {
674
- logger.error("Formo: Failed to track signature confirmation", e);
675
- }
676
- return [2 /*return*/];
677
- });
678
- }); })();
1027
+ catch (e) {
1028
+ logger.error("Formo: Failed to track signature confirmation", e);
1029
+ }
1030
+ return [2 /*return*/];
1031
+ });
1032
+ }); })();
1033
+ }
679
1034
  return [2 /*return*/, response_1];
680
- case 3:
681
- error_1 = _c.sent();
682
- (function () { return __awaiter(_this, void 0, void 0, function () {
683
- var rpcError;
684
- return __generator(this, function (_a) {
685
- try {
686
- rpcError = error_1;
687
- if (rpcError && (rpcError === null || rpcError === void 0 ? void 0 : rpcError.code) === 4001) {
688
- this.signature(__assign({ status: SignatureStatus.REJECTED }, this.buildSignatureEventPayload(method, params)));
1035
+ case 5:
1036
+ error_3 = _d.sent();
1037
+ rpcError = error_3;
1038
+ if ((rpcError === null || rpcError === void 0 ? void 0 : rpcError.code) === 4001) {
1039
+ // Use the already cast rpcError to avoid duplication
1040
+ (function () { return __awaiter(_this, void 0, void 0, function () {
1041
+ return __generator(this, function (_a) {
1042
+ try {
1043
+ this.signature(__assign({ status: SignatureStatus.REJECTED }, this.buildSignatureEventPayload(method, params, undefined, capturedChainId_1)));
689
1044
  }
690
- }
691
- catch (e) {
692
- logger.error("Formo: Failed to track signature rejection", e);
693
- }
694
- return [2 /*return*/];
695
- });
696
- }); })();
697
- throw error_1;
698
- case 4:
1045
+ catch (e) {
1046
+ logger.error("Formo: Failed to track signature rejection", e);
1047
+ }
1048
+ return [2 /*return*/];
1049
+ });
1050
+ }); })();
1051
+ }
1052
+ throw error_3;
1053
+ case 6:
699
1054
  if (!(Array.isArray(params) &&
700
1055
  method === "eth_sendTransaction" &&
701
- params[0])) return [3 /*break*/, 8];
1056
+ params[0])) return [3 /*break*/, 10];
702
1057
  (function () { return __awaiter(_this, void 0, void 0, function () {
703
- var payload, e_3;
1058
+ var payload, e_4;
704
1059
  return __generator(this, function (_a) {
705
1060
  switch (_a.label) {
706
1061
  case 0:
707
1062
  _a.trys.push([0, 2, , 3]);
708
- return [4 /*yield*/, this.buildTransactionEventPayload(params)];
1063
+ return [4 /*yield*/, this.buildTransactionEventPayload(params, provider)];
709
1064
  case 1:
710
1065
  payload = _a.sent();
711
1066
  this.transaction(__assign({ status: TransactionStatus.STARTED }, payload));
712
1067
  return [3 /*break*/, 3];
713
1068
  case 2:
714
- e_3 = _a.sent();
715
- logger.error("Formo: Failed to track transaction start", e_3);
1069
+ e_4 = _a.sent();
1070
+ logger.error("Formo: Failed to track transaction start", e_4);
716
1071
  return [3 /*break*/, 3];
717
1072
  case 3: return [2 /*return*/];
718
1073
  }
719
1074
  });
720
1075
  }); })();
721
- _c.label = 5;
722
- case 5:
723
- _c.trys.push([5, 7, , 8]);
1076
+ _d.label = 7;
1077
+ case 7:
1078
+ _d.trys.push([7, 9, , 10]);
724
1079
  return [4 /*yield*/, request({
725
1080
  method: method,
726
1081
  params: params,
727
1082
  })];
728
- case 6:
729
- transactionHash_1 = (_c.sent());
1083
+ case 8:
1084
+ transactionHash_1 = (_d.sent());
730
1085
  (function () { return __awaiter(_this, void 0, void 0, function () {
731
- var payload, e_4;
1086
+ var payload, e_5;
732
1087
  return __generator(this, function (_a) {
733
1088
  switch (_a.label) {
734
1089
  case 0:
735
1090
  _a.trys.push([0, 2, , 3]);
736
- return [4 /*yield*/, this.buildTransactionEventPayload(params)];
1091
+ return [4 /*yield*/, this.buildTransactionEventPayload(params, provider)];
737
1092
  case 1:
738
1093
  payload = _a.sent();
739
1094
  this.transaction(__assign(__assign({ status: TransactionStatus.BROADCASTED }, payload), { transactionHash: transactionHash_1 }));
740
1095
  // Start async polling for transaction receipt
741
- this.pollTransactionReceipt(transactionHash_1, payload);
1096
+ this.pollTransactionReceipt(provider, transactionHash_1, payload);
742
1097
  return [3 /*break*/, 3];
743
1098
  case 2:
744
- e_4 = _a.sent();
745
- logger.error("Formo: Failed to track transaction broadcast", e_4);
1099
+ e_5 = _a.sent();
1100
+ logger.error("Formo: Failed to track transaction broadcast", e_5);
746
1101
  return [3 /*break*/, 3];
747
1102
  case 3: return [2 /*return*/];
748
1103
  }
749
1104
  });
750
1105
  }); })();
751
1106
  return [2 /*return*/, transactionHash_1];
752
- case 7:
753
- error_2 = _c.sent();
754
- (function () { return __awaiter(_this, void 0, void 0, function () {
755
- var rpcError, payload, e_5;
756
- return __generator(this, function (_a) {
757
- switch (_a.label) {
758
- case 0:
759
- _a.trys.push([0, 3, , 4]);
760
- rpcError = error_2;
761
- if (!(rpcError && (rpcError === null || rpcError === void 0 ? void 0 : rpcError.code) === 4001)) return [3 /*break*/, 2];
762
- return [4 /*yield*/, this.buildTransactionEventPayload(params)];
763
- case 1:
764
- payload = _a.sent();
765
- this.transaction(__assign({ status: TransactionStatus.REJECTED }, payload));
766
- _a.label = 2;
767
- case 2: return [3 /*break*/, 4];
768
- case 3:
769
- e_5 = _a.sent();
770
- logger.error("Formo: Failed to track transaction rejection", e_5);
771
- return [3 /*break*/, 4];
772
- case 4: return [2 /*return*/];
773
- }
774
- });
775
- }); })();
776
- throw error_2;
777
- case 8: return [2 /*return*/, request({ method: method, params: params })];
1107
+ case 9:
1108
+ error_4 = _d.sent();
1109
+ rpcError = error_4;
1110
+ if ((rpcError === null || rpcError === void 0 ? void 0 : rpcError.code) === 4001) {
1111
+ // Use the already cast rpcError to avoid duplication
1112
+ (function () { return __awaiter(_this, void 0, void 0, function () {
1113
+ var payload, e_6;
1114
+ return __generator(this, function (_a) {
1115
+ switch (_a.label) {
1116
+ case 0:
1117
+ _a.trys.push([0, 2, , 3]);
1118
+ return [4 /*yield*/, this.buildTransactionEventPayload(params, provider)];
1119
+ case 1:
1120
+ payload = _a.sent();
1121
+ this.transaction(__assign({ status: TransactionStatus.REJECTED }, payload));
1122
+ return [3 /*break*/, 3];
1123
+ case 2:
1124
+ e_6 = _a.sent();
1125
+ logger.error("Formo: Failed to track transaction rejection", e_6);
1126
+ return [3 /*break*/, 3];
1127
+ case 3: return [2 /*return*/];
1128
+ }
1129
+ });
1130
+ }); })();
1131
+ }
1132
+ throw error_4;
1133
+ case 10: return [2 /*return*/, request({ method: method, params: params })];
778
1134
  }
779
1135
  });
780
1136
  }); };
1137
+ // Mark the wrapper so we can detect if request is replaced externally and keep a reference on provider
1138
+ wrappedRequest[WRAPPED_REQUEST_SYMBOL] = true;
1139
+ provider[WRAPPED_REQUEST_REF_SYMBOL] = wrappedRequest;
1140
+ try {
1141
+ // Attempt to assign the wrapped request function (rely on try-catch for mutability errors)
1142
+ provider.request = wrappedRequest;
1143
+ }
1144
+ catch (e) {
1145
+ logger.warn("Failed to wrap provider.request; skipping", e);
1146
+ }
781
1147
  };
782
1148
  FormoAnalytics.prototype.onLocationChange = function () {
783
1149
  return __awaiter(this, void 0, void 0, function () {
@@ -831,15 +1197,29 @@ var FormoAnalytics = /** @class */ (function () {
831
1197
  logger.info("Track page hit: Skipping event due to tracking configuration");
832
1198
  return [2 /*return*/];
833
1199
  }
834
- setTimeout(function () { return __awaiter(_this, void 0, void 0, function () {
835
- return __generator(this, function (_a) {
836
- this.trackEvent(EventType.PAGE, {
837
- category: category,
838
- name: name,
839
- }, properties, context, callback);
840
- return [2 /*return*/];
841
- });
842
- }); }, 300);
1200
+ setTimeout(function () {
1201
+ (function () { return __awaiter(_this, void 0, void 0, function () {
1202
+ var e_7;
1203
+ return __generator(this, function (_a) {
1204
+ switch (_a.label) {
1205
+ case 0:
1206
+ _a.trys.push([0, 2, , 3]);
1207
+ return [4 /*yield*/, this.trackEvent(EventType.PAGE, {
1208
+ category: category,
1209
+ name: name,
1210
+ }, properties, context, callback)];
1211
+ case 1:
1212
+ _a.sent();
1213
+ return [3 /*break*/, 3];
1214
+ case 2:
1215
+ e_7 = _a.sent();
1216
+ logger.error("Formo: Failed to track page hit", e_7);
1217
+ return [3 /*break*/, 3];
1218
+ case 3: return [2 /*return*/];
1219
+ }
1220
+ });
1221
+ }); })();
1222
+ }, 300);
843
1223
  return [2 /*return*/];
844
1224
  });
845
1225
  });
@@ -904,24 +1284,167 @@ var FormoAnalytics = /** @class */ (function () {
904
1284
  /*
905
1285
  Utility functions
906
1286
  */
1287
+ /**
1288
+ * Get provider information for a given provider
1289
+ * @param provider The provider to get info for
1290
+ * @returns Provider information
1291
+ */
1292
+ FormoAnalytics.prototype.getProviderInfo = function (provider) {
1293
+ // First check if provider is in our EIP-6963 providers list
1294
+ var eip6963Provider = this._providers.find(function (p) { return p.provider === provider; });
1295
+ if (eip6963Provider) {
1296
+ return {
1297
+ name: eip6963Provider.info.name,
1298
+ rdns: eip6963Provider.info.rdns
1299
+ };
1300
+ }
1301
+ // Fallback to injected provider detection
1302
+ var injectedInfo = this.detectInjectedProviderInfo(provider);
1303
+ return {
1304
+ name: injectedInfo.name,
1305
+ rdns: injectedInfo.rdns
1306
+ };
1307
+ };
1308
+ /**
1309
+ * Attempts to detect information about an injected provider
1310
+ * @param provider The injected provider to analyze
1311
+ * @returns Provider information with fallback values
1312
+ */
1313
+ FormoAnalytics.prototype.detectInjectedProviderInfo = function (provider) {
1314
+ // Try to detect provider type from common properties
1315
+ var name = 'Injected Provider';
1316
+ var rdns = 'io.injected.provider';
1317
+ // Use WalletProviderFlags interface for type safety
1318
+ var flags = provider;
1319
+ // Check if it's MetaMask
1320
+ if (flags.isMetaMask) {
1321
+ name = 'MetaMask';
1322
+ rdns = 'io.metamask';
1323
+ }
1324
+ // Check if it's Coinbase Wallet
1325
+ else if (flags.isCoinbaseWallet) {
1326
+ name = 'Coinbase Wallet';
1327
+ rdns = 'com.coinbase.wallet';
1328
+ }
1329
+ // Check if it's WalletConnect
1330
+ else if (flags.isWalletConnect) {
1331
+ name = 'WalletConnect';
1332
+ rdns = 'com.walletconnect';
1333
+ }
1334
+ // Check if it's Trust Wallet
1335
+ else if (flags.isTrust) {
1336
+ name = 'Trust Wallet';
1337
+ rdns = 'com.trustwallet';
1338
+ }
1339
+ // Check if it's Brave Wallet
1340
+ else if (flags.isBraveWallet) {
1341
+ name = 'Brave Wallet';
1342
+ rdns = 'com.brave.wallet';
1343
+ }
1344
+ // Check if it's Phantom
1345
+ else if (flags.isPhantom) {
1346
+ name = 'Phantom';
1347
+ rdns = 'app.phantom';
1348
+ }
1349
+ return {
1350
+ name: name,
1351
+ rdns: rdns,
1352
+ uuid: "injected-".concat(rdns.replace(/[^a-zA-Z0-9]/g, '-')),
1353
+ icon: DEFAULT_PROVIDER_ICON
1354
+ };
1355
+ };
907
1356
  FormoAnalytics.prototype.getProviders = function () {
908
1357
  return __awaiter(this, void 0, void 0, function () {
909
- var store, providers;
1358
+ var store, providers, injected_1, injectedProviderInfo, injectedDetail, uniqueProviders, _i, uniqueProviders_1, detail;
910
1359
  var _this = this;
911
1360
  return __generator(this, function (_a) {
912
1361
  store = createStore();
913
1362
  providers = store.getProviders();
914
1363
  store.subscribe(function (providerDetails) {
915
1364
  providers = providerDetails;
916
- _this._providers = providers;
1365
+ // Process newly added providers with proper deduplication
1366
+ var newlyAddedDetails = providerDetails.filter(function (detail) {
1367
+ var provider = detail === null || detail === void 0 ? void 0 : detail.provider;
1368
+ return provider && !_this._seenProviders.has(provider);
1369
+ });
1370
+ // Add new providers to the array without overwriting existing ones
1371
+ for (var _i = 0, newlyAddedDetails_1 = newlyAddedDetails; _i < newlyAddedDetails_1.length; _i++) {
1372
+ var detail = newlyAddedDetails_1[_i];
1373
+ _this.safeAddProviderDetail(detail);
1374
+ }
1375
+ // Track listeners for newly discovered providers only
1376
+ var newDetails = providerDetails.filter(function (detail) {
1377
+ var p = detail === null || detail === void 0 ? void 0 : detail.provider;
1378
+ return !!p && !_this._trackedProviders.has(p);
1379
+ });
1380
+ if (newDetails.length > 0) {
1381
+ _this.trackProviders(newDetails);
1382
+ // Detect newly discovered wallets (session de-dupes) with error handling
1383
+ (function () { return __awaiter(_this, void 0, void 0, function () {
1384
+ var e_8;
1385
+ return __generator(this, function (_a) {
1386
+ switch (_a.label) {
1387
+ case 0:
1388
+ _a.trys.push([0, 2, , 3]);
1389
+ return [4 /*yield*/, this.detectWallets(newDetails)];
1390
+ case 1:
1391
+ _a.sent();
1392
+ return [3 /*break*/, 3];
1393
+ case 2:
1394
+ e_8 = _a.sent();
1395
+ logger.error("Formo: Failed to detect wallets", e_8);
1396
+ return [3 /*break*/, 3];
1397
+ case 3: return [2 /*return*/];
1398
+ }
1399
+ });
1400
+ }); })();
1401
+ }
1402
+ // Clean up providers that are no longer available
1403
+ _this.cleanupUnavailableProviders();
917
1404
  });
918
1405
  // Fallback to injected provider if no providers are found
919
1406
  if (providers.length === 0) {
920
- this._providers = (window === null || window === void 0 ? void 0 : window.ethereum) ? [window.ethereum] : [];
1407
+ injected_1 = typeof window !== 'undefined' ? window.ethereum : undefined;
1408
+ if (injected_1) {
1409
+ // If we have already detected and cached the injected provider, and it's the same instance, return the cached result
1410
+ if (this._injectedProviderDetail &&
1411
+ this._injectedProviderDetail.provider === injected_1) {
1412
+ // Ensure it's tracked
1413
+ if (!this._trackedProviders.has(injected_1)) {
1414
+ this.trackProvider(injected_1);
1415
+ }
1416
+ // Merge with existing providers instead of overwriting
1417
+ if (!this._providers.some(function (existing) { return existing.provider === injected_1; })) {
1418
+ this._providers = __spreadArray(__spreadArray([], this._providers, true), [this._injectedProviderDetail], false);
1419
+ }
1420
+ return [2 /*return*/, this._providers];
1421
+ }
1422
+ // Re-check if the injected provider is already tracked just before tracking
1423
+ if (!this._trackedProviders.has(injected_1)) {
1424
+ this.trackProvider(injected_1);
1425
+ }
1426
+ injectedProviderInfo = this.detectInjectedProviderInfo(injected_1);
1427
+ injectedDetail = {
1428
+ provider: injected_1,
1429
+ info: injectedProviderInfo
1430
+ };
1431
+ // Cache the detected injected provider detail
1432
+ this._injectedProviderDetail = injectedDetail;
1433
+ // Merge with existing providers instead of overwriting
1434
+ this.safeAddProviderDetail(injectedDetail);
1435
+ }
921
1436
  return [2 /*return*/, this._providers];
922
1437
  }
923
- this._providers = providers;
924
- return [2 /*return*/, providers];
1438
+ uniqueProviders = providers.filter(function (detail) {
1439
+ var provider = detail === null || detail === void 0 ? void 0 : detail.provider;
1440
+ return provider && !_this._seenProviders.has(provider);
1441
+ });
1442
+ // Add to seen providers and instances, ensuring no duplicates in _providers
1443
+ for (_i = 0, uniqueProviders_1 = uniqueProviders; _i < uniqueProviders_1.length; _i++) {
1444
+ detail = uniqueProviders_1[_i];
1445
+ this.safeAddProviderDetail(detail);
1446
+ }
1447
+ return [2 /*return*/, this._providers];
925
1448
  });
926
1449
  });
927
1450
  };
@@ -934,16 +1457,16 @@ var FormoAnalytics = /** @class */ (function () {
934
1457
  });
935
1458
  FormoAnalytics.prototype.detectWallets = function (providers) {
936
1459
  return __awaiter(this, void 0, void 0, function () {
937
- var _i, providers_1, eip6963ProviderDetail, err_2;
1460
+ var _i, providers_2, eip6963ProviderDetail, err_2;
938
1461
  return __generator(this, function (_a) {
939
1462
  switch (_a.label) {
940
1463
  case 0:
941
1464
  _a.trys.push([0, 5, , 6]);
942
- _i = 0, providers_1 = providers;
1465
+ _i = 0, providers_2 = providers;
943
1466
  _a.label = 1;
944
1467
  case 1:
945
- if (!(_i < providers_1.length)) return [3 /*break*/, 4];
946
- eip6963ProviderDetail = providers_1[_i];
1468
+ if (!(_i < providers_2.length)) return [3 /*break*/, 4];
1469
+ eip6963ProviderDetail = providers_2[_i];
947
1470
  return [4 /*yield*/, this.detect({
948
1471
  providerName: eip6963ProviderDetail === null || eip6963ProviderDetail === void 0 ? void 0 : eip6963ProviderDetail.info.name,
949
1472
  rdns: eip6963ProviderDetail === null || eip6963ProviderDetail === void 0 ? void 0 : eip6963ProviderDetail.info.rdns,
@@ -973,7 +1496,7 @@ var FormoAnalytics = /** @class */ (function () {
973
1496
  });
974
1497
  FormoAnalytics.prototype.getAddress = function (provider) {
975
1498
  return __awaiter(this, void 0, void 0, function () {
976
- var p, accounts, validAddress, err_3;
1499
+ var p, accounts, err_3, code;
977
1500
  return __generator(this, function (_a) {
978
1501
  switch (_a.label) {
979
1502
  case 0:
@@ -991,15 +1514,15 @@ var FormoAnalytics = /** @class */ (function () {
991
1514
  case 2:
992
1515
  accounts = _a.sent();
993
1516
  if (accounts && accounts.length > 0) {
994
- validAddress = getValidAddress(accounts[0]);
995
- if (validAddress) {
996
- return [2 /*return*/, toChecksumAddress(validAddress)];
997
- }
1517
+ return [2 /*return*/, this.validateAndChecksumAddress(accounts[0]) || null];
998
1518
  }
999
1519
  return [3 /*break*/, 4];
1000
1520
  case 3:
1001
1521
  err_3 = _a.sent();
1002
- logger.error("Failed to fetch accounts from provider:", err_3);
1522
+ code = err_3 === null || err_3 === void 0 ? void 0 : err_3.code;
1523
+ if (code !== 4001) {
1524
+ logger.error("FormoAnalytics::getAccounts: eth_accounts threw an error", err_3);
1525
+ }
1003
1526
  return [2 /*return*/, null];
1004
1527
  case 4: return [2 /*return*/, null];
1005
1528
  }
@@ -1008,7 +1531,8 @@ var FormoAnalytics = /** @class */ (function () {
1008
1531
  };
1009
1532
  FormoAnalytics.prototype.getAccounts = function (provider) {
1010
1533
  return __awaiter(this, void 0, void 0, function () {
1011
- var p, res, err_4;
1534
+ var p, res, err_4, code;
1535
+ var _this = this;
1012
1536
  return __generator(this, function (_a) {
1013
1537
  switch (_a.label) {
1014
1538
  case 0:
@@ -1024,12 +1548,12 @@ var FormoAnalytics = /** @class */ (function () {
1024
1548
  if (!res || res.length === 0)
1025
1549
  return [2 /*return*/, null];
1026
1550
  return [2 /*return*/, res
1027
- .map(function (e) { return getValidAddress(e); })
1028
- .filter(function (e) { return e !== null; })
1029
- .map(toChecksumAddress)];
1551
+ .map(function (e) { return _this.validateAndChecksumAddress(e); })
1552
+ .filter(function (e) { return e !== undefined; })];
1030
1553
  case 3:
1031
1554
  err_4 = _a.sent();
1032
- if (err_4.code !== 4001) {
1555
+ code = err_4 === null || err_4 === void 0 ? void 0 : err_4.code;
1556
+ if (code !== 4001) {
1033
1557
  logger.error("FormoAnalytics::getAccounts: eth_accounts threw an error", err_4);
1034
1558
  }
1035
1559
  return [2 /*return*/, null];
@@ -1038,31 +1562,32 @@ var FormoAnalytics = /** @class */ (function () {
1038
1562
  });
1039
1563
  });
1040
1564
  };
1041
- FormoAnalytics.prototype.getCurrentChainId = function () {
1565
+ FormoAnalytics.prototype.getCurrentChainId = function (provider) {
1042
1566
  return __awaiter(this, void 0, void 0, function () {
1043
- var chainIdHex, err_5;
1044
- var _a;
1045
- return __generator(this, function (_b) {
1046
- switch (_b.label) {
1567
+ var p, chainIdHex, err_5;
1568
+ return __generator(this, function (_a) {
1569
+ switch (_a.label) {
1047
1570
  case 0:
1048
- if (!this.provider) {
1571
+ p = provider || this.provider;
1572
+ if (!p) {
1049
1573
  logger.error("Provider not set for chain ID");
1574
+ return [2 /*return*/, 0];
1050
1575
  }
1051
- _b.label = 1;
1576
+ _a.label = 1;
1052
1577
  case 1:
1053
- _b.trys.push([1, 3, , 4]);
1054
- return [4 /*yield*/, ((_a = this.provider) === null || _a === void 0 ? void 0 : _a.request({
1578
+ _a.trys.push([1, 3, , 4]);
1579
+ return [4 /*yield*/, p.request({
1055
1580
  method: "eth_chainId",
1056
- }))];
1581
+ })];
1057
1582
  case 2:
1058
- chainIdHex = _b.sent();
1583
+ chainIdHex = _a.sent();
1059
1584
  if (!chainIdHex) {
1060
1585
  logger.info("Chain id not found");
1061
1586
  return [2 /*return*/, 0];
1062
1587
  }
1063
1588
  return [2 /*return*/, parseChainId(chainIdHex)];
1064
1589
  case 3:
1065
- err_5 = _b.sent();
1590
+ err_5 = _a.sent();
1066
1591
  logger.error("eth_chainId threw an error:", err_5);
1067
1592
  return [2 /*return*/, 0];
1068
1593
  case 4: return [2 /*return*/];
@@ -1070,17 +1595,18 @@ var FormoAnalytics = /** @class */ (function () {
1070
1595
  });
1071
1596
  });
1072
1597
  };
1073
- FormoAnalytics.prototype.buildSignatureEventPayload = function (method, params, response) {
1598
+ FormoAnalytics.prototype.buildSignatureEventPayload = function (method, params, response, chainId) {
1599
+ var _a;
1074
1600
  var rawAddress = method === "personal_sign"
1075
1601
  ? params[1]
1076
1602
  : params[0];
1077
- var validAddress = getValidAddress(rawAddress);
1603
+ var validAddress = this.validateAndChecksumAddress(rawAddress);
1078
1604
  if (!validAddress) {
1079
1605
  throw new Error("Invalid address in signature payload: ".concat(rawAddress));
1080
1606
  }
1081
1607
  var basePayload = {
1082
- chainId: this.currentChainId,
1083
- address: toChecksumAddress(validAddress),
1608
+ chainId: (_a = chainId !== null && chainId !== void 0 ? chainId : this.currentChainId) !== null && _a !== void 0 ? _a : undefined,
1609
+ address: validAddress,
1084
1610
  };
1085
1611
  if (method === "personal_sign") {
1086
1612
  var message = Buffer.from(params[0].slice(2), "hex").toString("utf8");
@@ -1088,7 +1614,7 @@ var FormoAnalytics = /** @class */ (function () {
1088
1614
  }
1089
1615
  return __assign(__assign(__assign({}, basePayload), { message: params[1] }), (response ? { signatureHash: response } : {}));
1090
1616
  };
1091
- FormoAnalytics.prototype.buildTransactionEventPayload = function (params) {
1617
+ FormoAnalytics.prototype.buildTransactionEventPayload = function (params, provider) {
1092
1618
  return __awaiter(this, void 0, void 0, function () {
1093
1619
  var _a, data, from, to, value, validAddress, _b;
1094
1620
  var _c;
@@ -1096,20 +1622,20 @@ var FormoAnalytics = /** @class */ (function () {
1096
1622
  switch (_d.label) {
1097
1623
  case 0:
1098
1624
  _a = params[0], data = _a.data, from = _a.from, to = _a.to, value = _a.value;
1099
- validAddress = getValidAddress(from);
1625
+ validAddress = this.validateAndChecksumAddress(from);
1100
1626
  if (!validAddress) {
1101
1627
  throw new Error("Invalid address in transaction payload: ".concat(from));
1102
1628
  }
1103
1629
  _c = {};
1104
1630
  _b = this.currentChainId;
1105
1631
  if (_b) return [3 /*break*/, 2];
1106
- return [4 /*yield*/, this.getCurrentChainId()];
1632
+ return [4 /*yield*/, this.getCurrentChainId(provider)];
1107
1633
  case 1:
1108
1634
  _b = (_d.sent());
1109
1635
  _d.label = 2;
1110
1636
  case 2: return [2 /*return*/, (_c.chainId = _b,
1111
1637
  _c.data = data,
1112
- _c.address = toChecksumAddress(validAddress),
1638
+ _c.address = validAddress,
1113
1639
  _c.to = to,
1114
1640
  _c.value = value,
1115
1641
  _c)];
@@ -1120,19 +1646,18 @@ var FormoAnalytics = /** @class */ (function () {
1120
1646
  /**
1121
1647
  * Polls for transaction receipt and emits tx.status = CONFIRMED or REVERTED.
1122
1648
  */
1123
- FormoAnalytics.prototype.pollTransactionReceipt = function (transactionHash_2, payload_1) {
1124
- return __awaiter(this, arguments, void 0, function (transactionHash, payload, maxAttempts, intervalMs) {
1125
- var attempts, provider, poll;
1649
+ FormoAnalytics.prototype.pollTransactionReceipt = function (provider_1, transactionHash_2, payload_1) {
1650
+ return __awaiter(this, arguments, void 0, function (provider, transactionHash, payload, maxAttempts, intervalMs) {
1651
+ var attempts, poll;
1126
1652
  var _this = this;
1127
1653
  if (maxAttempts === void 0) { maxAttempts = 10; }
1128
1654
  if (intervalMs === void 0) { intervalMs = 3000; }
1129
1655
  return __generator(this, function (_a) {
1130
1656
  attempts = 0;
1131
- provider = this.provider;
1132
1657
  if (!provider)
1133
1658
  return [2 /*return*/];
1134
1659
  poll = function () { return __awaiter(_this, void 0, void 0, function () {
1135
- var receipt, e_6;
1660
+ var receipt, e_9;
1136
1661
  return __generator(this, function (_a) {
1137
1662
  switch (_a.label) {
1138
1663
  case 0:
@@ -1156,8 +1681,8 @@ var FormoAnalytics = /** @class */ (function () {
1156
1681
  }
1157
1682
  return [3 /*break*/, 3];
1158
1683
  case 2:
1159
- e_6 = _a.sent();
1160
- logger.error("Error polling transaction receipt", e_6);
1684
+ e_9 = _a.sent();
1685
+ logger.error("Error polling transaction receipt", e_9);
1161
1686
  return [3 /*break*/, 3];
1162
1687
  case 3:
1163
1688
  attempts++;
@@ -1173,6 +1698,122 @@ var FormoAnalytics = /** @class */ (function () {
1173
1698
  });
1174
1699
  });
1175
1700
  };
1701
+ FormoAnalytics.prototype.removeProviderListeners = function (provider) {
1702
+ var listeners = this._providerListenersMap.get(provider);
1703
+ if (!listeners)
1704
+ return;
1705
+ for (var _i = 0, _a = Object.entries(listeners); _i < _a.length; _i++) {
1706
+ var _b = _a[_i], event_1 = _b[0], fn = _b[1];
1707
+ try {
1708
+ provider.removeListener(event_1, fn);
1709
+ }
1710
+ catch (e) {
1711
+ logger.warn("Failed to remove listener for ".concat(String(event_1)), e);
1712
+ }
1713
+ }
1714
+ this._providerListenersMap.delete(provider);
1715
+ };
1716
+ // Explicitly untrack a provider: remove listeners, clear wrapper flag and tracking
1717
+ FormoAnalytics.prototype.untrackProvider = function (provider) {
1718
+ try {
1719
+ this.removeProviderListeners(provider);
1720
+ this._trackedProviders.delete(provider);
1721
+ if (this._provider === provider) {
1722
+ this._provider = undefined;
1723
+ }
1724
+ }
1725
+ catch (e) {
1726
+ logger.warn("Failed to untrack provider", e);
1727
+ }
1728
+ };
1729
+ // Debug/monitoring helpers
1730
+ FormoAnalytics.prototype.getTrackedProvidersCount = function () {
1731
+ return this._trackedProviders.size;
1732
+ };
1733
+ /**
1734
+ * Get current provider state for debugging
1735
+ * @returns Object containing current provider state information
1736
+ */
1737
+ FormoAnalytics.prototype.getProviderState = function () {
1738
+ return {
1739
+ totalProviders: this._providers.length,
1740
+ trackedProviders: this._trackedProviders.size,
1741
+ seenProviders: this._seenProviders.size,
1742
+ activeProvider: !!this._provider,
1743
+ };
1744
+ };
1745
+ /**
1746
+ * Clean up providers that are no longer available
1747
+ * This helps maintain consistent state and prevents memory leaks
1748
+ */
1749
+ FormoAnalytics.prototype.cleanupUnavailableProviders = function () {
1750
+ // Remove providers that are no longer in the current providers list
1751
+ var currentProviderInstances = new Set(this._providers.map(function (detail) { return detail.provider; }));
1752
+ for (var _i = 0, _a = Array.from(this._trackedProviders); _i < _a.length; _i++) {
1753
+ var provider = _a[_i];
1754
+ if (!currentProviderInstances.has(provider)) {
1755
+ logger.info("Cleaning up unavailable provider: ".concat(provider.constructor.name));
1756
+ this.untrackProvider(provider);
1757
+ }
1758
+ }
1759
+ };
1760
+ /**
1761
+ * Helper method to check if a provider is already wrapped
1762
+ * @param provider The provider to check
1763
+ * @param currentRequest The current request function
1764
+ * @returns true if the provider is already wrapped
1765
+ */
1766
+ FormoAnalytics.prototype.isProviderAlreadyWrapped = function (provider, currentRequest) {
1767
+ return !!(currentRequest &&
1768
+ typeof currentRequest === 'function' &&
1769
+ currentRequest[WRAPPED_REQUEST_SYMBOL] &&
1770
+ provider[WRAPPED_REQUEST_REF_SYMBOL] === currentRequest);
1771
+ };
1772
+ /**
1773
+ * Handle provider mismatch by switching to the new provider and invalidating old tokens
1774
+ * @param provider The new provider to switch to
1775
+ */
1776
+ FormoAnalytics.prototype.handleProviderMismatch = function (provider) {
1777
+ // If this is a different provider, allow the switch
1778
+ if (this._provider) {
1779
+ // Clear any provider-specific state when switching
1780
+ this.currentChainId = undefined;
1781
+ this.currentAddress = undefined;
1782
+ }
1783
+ this._provider = provider;
1784
+ };
1785
+ /**
1786
+ * Helper method to validate and checksum an address
1787
+ * @param address The address to validate and checksum
1788
+ * @returns The checksummed address or undefined if invalid
1789
+ */
1790
+ FormoAnalytics.prototype.validateAndChecksumAddress = function (address) {
1791
+ var validAddress = getValidAddress(address);
1792
+ return validAddress ? toChecksumAddress(validAddress) : undefined;
1793
+ };
1794
+ /**
1795
+ * Helper method to safely add a provider detail to _providers array, ensuring no duplicates
1796
+ * @param detail The provider detail to add
1797
+ * @returns true if the provider was added, false if it was already present
1798
+ */
1799
+ FormoAnalytics.prototype.safeAddProviderDetail = function (detail) {
1800
+ var provider = detail === null || detail === void 0 ? void 0 : detail.provider;
1801
+ if (!provider)
1802
+ return false;
1803
+ // Check if provider already exists in _providers array
1804
+ var alreadyExists = this._providers.some(function (existing) { return existing.provider === provider; });
1805
+ if (!alreadyExists) {
1806
+ // Add to providers array and mark as seen
1807
+ this._providers = __spreadArray(__spreadArray([], this._providers, true), [detail], false);
1808
+ this._seenProviders.add(provider);
1809
+ return true;
1810
+ }
1811
+ else {
1812
+ // Ensure provider is marked as seen even if it already exists in _providers
1813
+ this._seenProviders.add(provider);
1814
+ return false;
1815
+ }
1816
+ };
1176
1817
  return FormoAnalytics;
1177
1818
  }());
1178
1819
  export { FormoAnalytics };