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