@formo/analytics 1.27.0 → 1.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/dist/cjs/src/FormoAnalytics.d.ts +69 -12
  2. package/dist/cjs/src/FormoAnalytics.js +273 -147
  3. package/dist/cjs/src/event/EventFactory.d.ts +10 -2
  4. package/dist/cjs/src/event/EventFactory.js +32 -21
  5. package/dist/cjs/src/index.d.ts +4 -0
  6. package/dist/cjs/src/index.js +6 -0
  7. package/dist/cjs/src/privy/index.d.ts +9 -0
  8. package/dist/cjs/src/privy/index.js +12 -0
  9. package/dist/cjs/src/privy/types.d.ts +175 -0
  10. package/dist/cjs/src/privy/types.js +12 -0
  11. package/dist/cjs/src/privy/utils.d.ts +32 -0
  12. package/dist/cjs/src/privy/utils.js +188 -0
  13. package/dist/cjs/src/session/index.js +2 -1
  14. package/dist/cjs/src/solana/SolanaAdapter.d.ts +211 -0
  15. package/dist/cjs/src/solana/SolanaAdapter.js +975 -0
  16. package/dist/cjs/src/solana/SolanaManager.d.ts +24 -0
  17. package/dist/cjs/src/solana/SolanaManager.js +80 -0
  18. package/dist/cjs/src/solana/address.d.ts +72 -0
  19. package/dist/cjs/src/solana/address.js +176 -0
  20. package/dist/cjs/src/solana/index.d.ts +13 -0
  21. package/dist/cjs/src/solana/index.js +32 -0
  22. package/dist/cjs/src/solana/types.d.ts +206 -0
  23. package/dist/cjs/src/solana/types.js +80 -0
  24. package/dist/cjs/src/types/base.d.ts +17 -0
  25. package/dist/cjs/src/types/events.d.ts +4 -3
  26. package/dist/cjs/src/utils/address.d.ts +21 -0
  27. package/dist/cjs/src/utils/address.js +48 -1
  28. package/dist/cjs/src/utils/builderCode.d.ts +30 -0
  29. package/dist/cjs/src/utils/builderCode.js +143 -0
  30. package/dist/cjs/src/utils/index.d.ts +1 -0
  31. package/dist/cjs/src/utils/index.js +1 -0
  32. package/dist/cjs/src/version.d.ts +1 -1
  33. package/dist/cjs/src/version.js +1 -1
  34. package/dist/cjs/src/wagmi/WagmiEventHandler.js +13 -15
  35. package/dist/cjs/src/wagmi/utils.d.ts +5 -0
  36. package/dist/cjs/src/wagmi/utils.js +20 -0
  37. package/dist/esm/src/FormoAnalytics.d.ts +69 -12
  38. package/dist/esm/src/FormoAnalytics.js +274 -148
  39. package/dist/esm/src/event/EventFactory.d.ts +10 -2
  40. package/dist/esm/src/event/EventFactory.js +34 -23
  41. package/dist/esm/src/index.d.ts +4 -0
  42. package/dist/esm/src/index.js +3 -0
  43. package/dist/esm/src/privy/index.d.ts +9 -0
  44. package/dist/esm/src/privy/index.js +8 -0
  45. package/dist/esm/src/privy/types.d.ts +175 -0
  46. package/dist/esm/src/privy/types.js +11 -0
  47. package/dist/esm/src/privy/utils.d.ts +32 -0
  48. package/dist/esm/src/privy/utils.js +185 -0
  49. package/dist/esm/src/session/index.js +2 -1
  50. package/dist/esm/src/solana/SolanaAdapter.d.ts +211 -0
  51. package/dist/esm/src/solana/SolanaAdapter.js +972 -0
  52. package/dist/esm/src/solana/SolanaManager.d.ts +24 -0
  53. package/dist/esm/src/solana/SolanaManager.js +77 -0
  54. package/dist/esm/src/solana/address.d.ts +72 -0
  55. package/dist/esm/src/solana/address.js +167 -0
  56. package/dist/esm/src/solana/index.d.ts +13 -0
  57. package/dist/esm/src/solana/index.js +13 -0
  58. package/dist/esm/src/solana/types.d.ts +206 -0
  59. package/dist/esm/src/solana/types.js +74 -0
  60. package/dist/esm/src/types/base.d.ts +17 -0
  61. package/dist/esm/src/types/events.d.ts +4 -3
  62. package/dist/esm/src/utils/address.d.ts +21 -0
  63. package/dist/esm/src/utils/address.js +45 -0
  64. package/dist/esm/src/utils/builderCode.d.ts +30 -0
  65. package/dist/esm/src/utils/builderCode.js +140 -0
  66. package/dist/esm/src/utils/index.d.ts +1 -0
  67. package/dist/esm/src/utils/index.js +1 -0
  68. package/dist/esm/src/version.d.ts +1 -1
  69. package/dist/esm/src/version.js +1 -1
  70. package/dist/esm/src/wagmi/WagmiEventHandler.js +14 -16
  71. package/dist/esm/src/wagmi/utils.d.ts +5 -0
  72. package/dist/esm/src/wagmi/utils.js +19 -0
  73. package/dist/index.umd.min.js +1 -1
  74. package/package.json +15 -3
@@ -67,11 +67,13 @@ var consent_1 = require("./consent");
67
67
  var provider_1 = require("./provider");
68
68
  var session_1 = require("./session");
69
69
  var types_1 = require("./types");
70
- var utils_1 = require("./utils");
71
70
  var address_1 = require("./utils/address");
71
+ var builderCode_1 = require("./utils/builderCode");
72
72
  var validators_1 = require("./validators");
73
73
  var chain_1 = require("./utils/chain");
74
74
  var wagmi_1 = require("./wagmi");
75
+ var solana_1 = require("./solana");
76
+ var SolanaManager_1 = require("./solana/SolanaManager");
75
77
  /**
76
78
  * Constants for provider switching reasons
77
79
  */
@@ -86,6 +88,11 @@ var FormoAnalytics = /** @class */ (function () {
86
88
  var _a, _b;
87
89
  this.writeKey = writeKey;
88
90
  this.options = options;
91
+ // Per-chain namespace state — isolates EVM and Solana connection state
92
+ this._chainState = {
93
+ evm: {},
94
+ solana: {},
95
+ };
89
96
  this._providerListenersMap = new Map();
90
97
  /**
91
98
  * EIP-6963 provider details discovered through the browser
@@ -165,9 +172,39 @@ var FormoAnalytics = /** @class */ (function () {
165
172
  this.trackEIP1193Provider(provider);
166
173
  }
167
174
  }
175
+ // Initialize Solana manager if Solana options are provided
176
+ if (options.solana) {
177
+ this.solanaManager = new SolanaManager_1.SolanaManager(this, options.solana);
178
+ }
168
179
  this.trackPageHit();
169
180
  this.trackPageHits();
170
181
  }
182
+ Object.defineProperty(FormoAnalytics.prototype, "_provider", {
183
+ // EVM state accessors — EVM listener paths must use these instead of
184
+ // currentAddress/currentChainId to avoid cross-namespace reads.
185
+ get: function () {
186
+ return this._chainState.evm.provider;
187
+ },
188
+ set: function (value) {
189
+ this._chainState.evm.provider = value;
190
+ },
191
+ enumerable: false,
192
+ configurable: true
193
+ });
194
+ Object.defineProperty(FormoAnalytics.prototype, "_evmAddress", {
195
+ get: function () {
196
+ return this._chainState.evm.address;
197
+ },
198
+ enumerable: false,
199
+ configurable: true
200
+ });
201
+ Object.defineProperty(FormoAnalytics.prototype, "_evmChainId", {
202
+ get: function () {
203
+ return this._chainState.evm.chainId;
204
+ },
205
+ enumerable: false,
206
+ configurable: true
207
+ });
171
208
  /**
172
209
  * Helper method to check if a provider is different from the currently active one
173
210
  * @param provider The provider to check
@@ -247,12 +284,17 @@ var FormoAnalytics = /** @class */ (function () {
247
284
  * @returns {void}
248
285
  */
249
286
  FormoAnalytics.prototype.cleanup = function () {
250
- logger_1.logger.info("FormoAnalytics: Cleaning up resources");
287
+ logger_1.logger.debug("FormoAnalytics: Cleaning up resources");
251
288
  // Clean up Wagmi handler if present
252
289
  if (this.wagmiHandler) {
253
290
  this.wagmiHandler.cleanup();
254
291
  this.wagmiHandler = undefined;
255
292
  }
293
+ // Clean up Solana manager if present
294
+ if (this.solanaManager) {
295
+ this.solanaManager.cleanup();
296
+ this.solanaManager = undefined;
297
+ }
256
298
  // Clean up EIP-1193 providers if not in Wagmi mode
257
299
  if (!this.isWagmiMode) {
258
300
  for (var _i = 0, _a = Array.from(this._trackedProviders); _i < _a.length; _i++) {
@@ -260,7 +302,7 @@ var FormoAnalytics = /** @class */ (function () {
260
302
  this.untrackProvider(provider);
261
303
  }
262
304
  }
263
- logger_1.logger.info("FormoAnalytics: Cleanup complete");
305
+ logger_1.logger.debug("FormoAnalytics: Cleanup complete");
264
306
  };
265
307
  /**
266
308
  * Emits a connect wallet event.
@@ -273,7 +315,7 @@ var FormoAnalytics = /** @class */ (function () {
273
315
  */
274
316
  FormoAnalytics.prototype.connect = function (_a, properties_1, context_1, callback_1) {
275
317
  return __awaiter(this, arguments, void 0, function (_b, properties, context, callback) {
276
- var checksummedAddress;
318
+ var validAddress;
277
319
  var chainId = _b.chainId, address = _b.address;
278
320
  return __generator(this, function (_c) {
279
321
  switch (_c.label) {
@@ -286,16 +328,15 @@ var FormoAnalytics = /** @class */ (function () {
286
328
  logger_1.logger.warn("Connect: Address cannot be empty");
287
329
  return [2 /*return*/];
288
330
  }
289
- this.currentChainId = chainId;
290
- checksummedAddress = this.validateAndChecksumAddress(address);
291
- if (!checksummedAddress) {
292
- logger_1.logger.warn("Connect: Invalid address provided (\"".concat(address, "\"). Please provide a valid Ethereum address in checksum format."));
331
+ validAddress = (0, address_1.validateAddress)(address, chainId);
332
+ if (!validAddress) {
333
+ logger_1.logger.warn("Connect: Invalid address provided (\"".concat(address, "\"). Please provide a valid EVM or Solana address."));
293
334
  return [2 /*return*/];
294
335
  }
295
- this.currentAddress = checksummedAddress;
336
+ this.setChainState(chainId, { address: validAddress });
296
337
  return [4 /*yield*/, this.trackEvent(constants_1.EventType.CONNECT, {
297
338
  chainId: chainId,
298
- address: this.currentAddress,
339
+ address: validAddress,
299
340
  }, properties, context, callback)];
300
341
  case 1:
301
342
  _c.sent();
@@ -315,13 +356,14 @@ var FormoAnalytics = /** @class */ (function () {
315
356
  */
316
357
  FormoAnalytics.prototype.disconnect = function (params, properties, context, callback) {
317
358
  return __awaiter(this, void 0, void 0, function () {
318
- var chainId, address, providerInfo, disconnectProperties;
359
+ var chainId, address, isSolana, providerInfo, disconnectProperties;
319
360
  return __generator(this, function (_a) {
320
361
  switch (_a.label) {
321
362
  case 0:
322
363
  chainId = (params === null || params === void 0 ? void 0 : params.chainId) || this.currentChainId;
323
364
  address = (params === null || params === void 0 ? void 0 : params.address) || this.currentAddress;
324
- providerInfo = this._provider
365
+ isSolana = (0, solana_1.isSolanaChainId)(chainId);
366
+ providerInfo = !isSolana && this._provider
325
367
  ? this.getProviderInfo(this._provider)
326
368
  : null;
327
369
  logger_1.logger.info("Disconnect: Emitting disconnect event with:", {
@@ -337,9 +379,9 @@ var FormoAnalytics = /** @class */ (function () {
337
379
  return [4 /*yield*/, this.trackEvent(constants_1.EventType.DISCONNECT, __assign(__assign({}, (chainId && { chainId: chainId })), (address && { address: address })), disconnectProperties, context, callback)];
338
380
  case 1:
339
381
  _a.sent();
340
- this.currentAddress = undefined;
341
- this.currentChainId = undefined;
342
- this.clearActiveProvider();
382
+ // Clear the disconnecting chain's namespace state.
383
+ // Per-chain isolation ensures a Solana disconnect never wipes EVM state (and vice versa).
384
+ this.clearChainState(chainId);
343
385
  logger_1.logger.info("Wallet disconnected: Cleared currentAddress, currentChainId, and provider");
344
386
  return [2 /*return*/];
345
387
  }
@@ -373,7 +415,7 @@ var FormoAnalytics = /** @class */ (function () {
373
415
  logger_1.logger.warn("FormoAnalytics::chain: address was empty and no previous address has been recorded");
374
416
  return [2 /*return*/];
375
417
  }
376
- this.currentChainId = chainId;
418
+ this.setChainState(chainId, {});
377
419
  return [4 /*yield*/, this.trackEvent(constants_1.EventType.CHAIN, {
378
420
  chainId: chainId,
379
421
  address: address || this.currentAddress,
@@ -426,10 +468,10 @@ var FormoAnalytics = /** @class */ (function () {
426
468
  */
427
469
  FormoAnalytics.prototype.transaction = function (_a, properties_1, context_1, callback_1) {
428
470
  return __awaiter(this, arguments, void 0, function (_b, properties, context, callback) {
429
- var status = _b.status, chainId = _b.chainId, address = _b.address, data = _b.data, to = _b.to, value = _b.value, transactionHash = _b.transactionHash, function_name = _b.function_name, function_args = _b.function_args;
471
+ var status = _b.status, chainId = _b.chainId, address = _b.address, data = _b.data, to = _b.to, value = _b.value, transactionHash = _b.transactionHash, function_name = _b.function_name, function_args = _b.function_args, builder_codes = _b.builder_codes;
430
472
  return __generator(this, function (_c) {
431
473
  switch (_c.label) {
432
- case 0: return [4 /*yield*/, this.trackEvent(constants_1.EventType.TRANSACTION, __assign(__assign(__assign({ status: status, chainId: chainId, address: address, data: data, to: to, value: value }, (transactionHash && { transactionHash: transactionHash })), (function_name && { function_name: function_name })), (function_args && { function_args: function_args })), properties, context, callback)];
474
+ case 0: return [4 /*yield*/, this.trackEvent(constants_1.EventType.TRANSACTION, __assign(__assign(__assign(__assign({ status: status, chainId: chainId, address: address, data: data, to: to, value: value }, (transactionHash && { transactionHash: transactionHash })), (function_name && { function_name: function_name })), (function_args && { function_args: function_args })), (builder_codes && { builder_codes: builder_codes })), properties, context, callback)];
433
475
  case 1:
434
476
  _c.sent();
435
477
  return [2 /*return*/];
@@ -439,42 +481,59 @@ var FormoAnalytics = /** @class */ (function () {
439
481
  };
440
482
  /**
441
483
  * Emits an identify event with current wallet address and provider info.
442
- * @param {string} params.address
443
- * @param {string} params.userId
444
- * @param {string} params.rdns
445
- * @param {string} params.providerName
446
- * @param {IFormoEventProperties} properties
484
+ *
485
+ * @param {string} params.address - Wallet address
486
+ * @param {string} params.userId - External user ID
487
+ * @param {string} params.rdns - Provider reverse domain name
488
+ * @param {string} params.providerName - Provider display name
489
+ * @param {IFormoEventProperties} properties - Additional properties to include with the identify event
447
490
  * @param {IFormoEventContext} context
448
491
  * @param {(...args: unknown[]) => void} callback
449
492
  * @returns {Promise<void>}
493
+ *
494
+ * @example
495
+ * ```ts
496
+ * // Basic identify
497
+ * formo.identify({ address: '0x...', userId: 'user123' });
498
+ *
499
+ * // With Privy user
500
+ * import { parsePrivyProperties } from '@formo/analytics';
501
+ * const { user } = usePrivy();
502
+ * if (user) {
503
+ * const { properties, wallets } = parsePrivyProperties(user);
504
+ * for (const wallet of wallets) {
505
+ * formo.identify({ address: wallet.address, userId: user.id }, properties);
506
+ * }
507
+ * }
508
+ * ```
450
509
  */
451
510
  FormoAnalytics.prototype.identify = function (params, properties, context, callback) {
452
511
  return __awaiter(this, void 0, void 0, function () {
453
- var _i, _a, providerDetail, provider, address_2, validAddress_1, err_1, userId, address, providerName, rdns, validAddress, isAlreadyIdentified, e_1;
454
- var _b;
455
- return __generator(this, function (_c) {
456
- switch (_c.label) {
512
+ var _i, _a, providerDetail, provider, address_2, validAddress_1, err_1, address, providerName, userId, rdns, validAddress, isAlreadyIdentified, e_1;
513
+ var _b, _c;
514
+ return __generator(this, function (_d) {
515
+ switch (_d.label) {
457
516
  case 0:
458
- _c.trys.push([0, 12, , 13]);
517
+ _d.trys.push([0, 12, , 13]);
459
518
  if (!!params) return [3 /*break*/, 10];
460
519
  // If no params provided, auto-identify
461
520
  logger_1.logger.info("Auto-identifying with providers:", this._providers.map(function (p) { return p.info.name; }));
462
521
  _i = 0, _a = this._providers;
463
- _c.label = 1;
522
+ _d.label = 1;
464
523
  case 1:
465
524
  if (!(_i < _a.length)) return [3 /*break*/, 9];
466
525
  providerDetail = _a[_i];
467
526
  provider = providerDetail.provider;
468
527
  if (!provider)
469
528
  return [3 /*break*/, 8];
470
- _c.label = 2;
529
+ _d.label = 2;
471
530
  case 2:
472
- _c.trys.push([2, 7, , 8]);
531
+ _d.trys.push([2, 7, , 8]);
473
532
  return [4 /*yield*/, this.getAddress(provider)];
474
533
  case 3:
475
- address_2 = _c.sent();
534
+ address_2 = _d.sent();
476
535
  if (!address_2) return [3 /*break*/, 6];
477
- validAddress_1 = this.validateAndChecksumAddress(address_2);
536
+ validAddress_1 = (0, address_1.validateAndChecksumAddress)(address_2);
478
537
  logger_1.logger.info("Auto-identify: Checking deduplication", {
479
538
  validAddress: validAddress_1,
480
539
  rdns: providerDetail.info.rdns,
@@ -494,16 +553,16 @@ var FormoAnalytics = /** @class */ (function () {
494
553
  }, properties, context, callback)];
495
554
  case 4:
496
555
  // NOTE: do not set this.currentAddress without explicit connect or identify
497
- _c.sent();
556
+ _d.sent();
498
557
  return [3 /*break*/, 6];
499
558
  case 5:
500
559
  if (validAddress_1) {
501
560
  logger_1.logger.info("Auto-identify: Skipping already identified wallet", validAddress_1, providerDetail.info.name, providerDetail.info.rdns);
502
561
  }
503
- _c.label = 6;
562
+ _d.label = 6;
504
563
  case 6: return [3 /*break*/, 8];
505
564
  case 7:
506
- err_1 = _c.sent();
565
+ err_1 = _d.sent();
507
566
  logger_1.logger.error("Failed to identify provider ".concat(providerDetail.info.name, ":"), err_1);
508
567
  return [3 /*break*/, 8];
509
568
  case 8:
@@ -511,32 +570,32 @@ var FormoAnalytics = /** @class */ (function () {
511
570
  return [3 /*break*/, 1];
512
571
  case 9: return [2 /*return*/];
513
572
  case 10:
514
- userId = params.userId, address = params.address, providerName = params.providerName, rdns = params.rdns;
573
+ address = params.address, providerName = params.providerName, userId = params.userId, rdns = params.rdns;
574
+ // Runtime validation: address is required
575
+ if (!address) {
576
+ (_b = logger_1.logger.warn) === null || _b === void 0 ? void 0 : _b.call(logger_1.logger, "identify() called without address - address is required");
577
+ return [2 /*return*/];
578
+ }
579
+ // Explicit identify
515
580
  logger_1.logger.info("Identify", address, userId, providerName, rdns);
516
- validAddress = undefined;
517
- if (address) {
518
- validAddress = this.validateAndChecksumAddress(address);
519
- this.currentAddress = validAddress || undefined;
520
- if (!validAddress) {
521
- (_b = logger_1.logger.warn) === null || _b === void 0 ? void 0 : _b.call(logger_1.logger, "Invalid address provided to identify:", address);
522
- }
581
+ validAddress = (0, address_1.validateAddress)(address);
582
+ if (validAddress) {
583
+ this.currentAddress = validAddress;
523
584
  }
524
585
  else {
525
- this.currentAddress = undefined;
586
+ (_c = logger_1.logger.warn) === null || _c === void 0 ? void 0 : _c.call(logger_1.logger, "Invalid address provided to identify:", address);
587
+ return [2 /*return*/];
526
588
  }
527
589
  if (userId) {
528
590
  this.currentUserId = userId;
529
591
  (0, storage_1.cookie)().set(constants_1.SESSION_USER_ID_KEY, userId);
530
592
  }
531
- isAlreadyIdentified = validAddress
532
- ? this.session.isWalletIdentified(validAddress, rdns || "")
533
- : false;
593
+ isAlreadyIdentified = this.session.isWalletIdentified(validAddress, rdns || "");
534
594
  logger_1.logger.debug("Identify: Checking deduplication", {
535
595
  validAddress: validAddress,
536
596
  rdns: rdns,
537
597
  providerName: providerName,
538
- hasValidAddress: !!validAddress,
539
- hasRdns: !!rdns,
598
+ userId: userId,
540
599
  isAlreadyIdentified: isAlreadyIdentified,
541
600
  });
542
601
  if (isAlreadyIdentified) {
@@ -544,10 +603,7 @@ var FormoAnalytics = /** @class */ (function () {
544
603
  return [2 /*return*/];
545
604
  }
546
605
  // Mark as identified before emitting the event
547
- // Mark even if rdns is empty to prevent duplicate empty identifies
548
- if (validAddress) {
549
- this.session.markWalletIdentified(validAddress, rdns || "");
550
- }
606
+ this.session.markWalletIdentified(validAddress, rdns || "");
551
607
  return [4 /*yield*/, this.trackEvent(constants_1.EventType.IDENTIFY, {
552
608
  address: validAddress,
553
609
  providerName: providerName,
@@ -555,10 +611,10 @@ var FormoAnalytics = /** @class */ (function () {
555
611
  rdns: rdns,
556
612
  }, properties, context, callback)];
557
613
  case 11:
558
- _c.sent();
614
+ _d.sent();
559
615
  return [3 /*break*/, 13];
560
616
  case 12:
561
- e_1 = _c.sent();
617
+ e_1 = _d.sent();
562
618
  logger_1.logger.log("identify error", e_1);
563
619
  return [3 /*break*/, 13];
564
620
  case 13: return [2 /*return*/];
@@ -783,21 +839,21 @@ var FormoAnalytics = /** @class */ (function () {
783
839
  if (!(accounts.length === 0)) return [3 /*break*/, 9];
784
840
  if (!(this._provider === provider)) return [3 /*break*/, 7];
785
841
  logger_1.logger.info("OnAccountsChanged: Detecting disconnect, current state:", {
786
- currentAddress: this.currentAddress,
787
- currentChainId: this.currentChainId,
842
+ evmAddress: this._evmAddress,
843
+ evmChainId: this._evmChainId,
788
844
  providerMatch: this._provider === provider,
789
845
  });
790
846
  if (!this.isAutocaptureEnabled("disconnect")) return [3 /*break*/, 5];
791
847
  _a.label = 1;
792
848
  case 1:
793
849
  _a.trys.push([1, 3, , 4]);
794
- // Pass current state explicitly to ensure we have the data for the disconnect event
850
+ // Pass EVM state explicitly to ensure we have the data for the disconnect event
795
851
  return [4 /*yield*/, this.disconnect({
796
- chainId: this.currentChainId,
797
- address: this.currentAddress,
852
+ chainId: this._evmChainId,
853
+ address: this._evmAddress,
798
854
  })];
799
855
  case 2:
800
- // Pass current state explicitly to ensure we have the data for the disconnect event
856
+ // Pass EVM state explicitly to ensure we have the data for the disconnect event
801
857
  _a.sent();
802
858
  return [3 /*break*/, 4];
803
859
  case 3:
@@ -808,9 +864,7 @@ var FormoAnalytics = /** @class */ (function () {
808
864
  case 5:
809
865
  logger_1.logger.debug("OnAccountsChanged: Disconnect event skipped (autocapture.disconnect: false)");
810
866
  // Still clear state even if not tracking the event
811
- this.currentAddress = undefined;
812
- this.currentChainId = undefined;
813
- this.clearActiveProvider();
867
+ this.clearChainState('evm');
814
868
  _a.label = 6;
815
869
  case 6: return [3 /*break*/, 8];
816
870
  case 7:
@@ -818,14 +872,14 @@ var FormoAnalytics = /** @class */ (function () {
818
872
  _a.label = 8;
819
873
  case 8: return [2 /*return*/];
820
874
  case 9:
821
- address = this.validateAndChecksumAddress(accounts[0]);
875
+ address = (0, address_1.validateAndChecksumAddress)(accounts[0]);
822
876
  if (!address) {
823
877
  logger_1.logger.warn("onAccountsChanged: Invalid address received", accounts[0]);
824
878
  return [2 /*return*/];
825
879
  }
826
- if (!(this._provider && this._provider !== provider)) return [3 /*break*/, 26];
827
- currentStoredAddress = this.currentAddress;
828
- newProviderAddress = this.validateAndChecksumAddress(address);
880
+ if (!(this._provider && this._provider !== provider)) return [3 /*break*/, 25];
881
+ currentStoredAddress = this._evmAddress;
882
+ newProviderAddress = (0, address_1.validateAndChecksumAddress)(address);
829
883
  logger_1.logger.info("OnAccountsChanged: Different provider attempting to connect", {
830
884
  activeProvider: this.getProviderInfo(this._provider).name,
831
885
  eventProvider: this.getProviderInfo(provider).name,
@@ -834,7 +888,7 @@ var FormoAnalytics = /** @class */ (function () {
834
888
  });
835
889
  _a.label = 10;
836
890
  case 10:
837
- _a.trys.push([10, 22, , 26]);
891
+ _a.trys.push([10, 21, , 25]);
838
892
  return [4 /*yield*/, this.getAccounts(this._provider)];
839
893
  case 11:
840
894
  activeProviderAccounts = _a.sent();
@@ -858,8 +912,8 @@ var FormoAnalytics = /** @class */ (function () {
858
912
  });
859
913
  if (!this.isAutocaptureEnabled("disconnect")) return [3 /*break*/, 13];
860
914
  return [4 /*yield*/, this.disconnect({
861
- chainId: this.currentChainId,
862
- address: this.currentAddress,
915
+ chainId: this._evmChainId,
916
+ address: this._evmAddress,
863
917
  })];
864
918
  case 12:
865
919
  _a.sent();
@@ -867,8 +921,7 @@ var FormoAnalytics = /** @class */ (function () {
867
921
  case 13:
868
922
  logger_1.logger.debug("OnAccountsChanged: Disconnect event skipped during provider switch (autocapture.disconnect: false)");
869
923
  // Still clear state even if not tracking the event
870
- this.currentAddress = undefined;
871
- this.currentChainId = undefined;
924
+ this.clearChainState('evm');
872
925
  _a.label = 14;
873
926
  case 14:
874
927
  // Clear state and let the new provider become active
@@ -883,7 +936,7 @@ var FormoAnalytics = /** @class */ (function () {
883
936
  newAddress: newProviderAddress,
884
937
  });
885
938
  return [2 /*return*/];
886
- case 16: return [3 /*break*/, 21];
939
+ case 16: return [3 /*break*/, 20];
887
940
  case 17:
888
941
  logger_1.logger.info("OnAccountsChanged: Current provider has no accounts, switching to new provider", {
889
942
  oldProvider: this.getProviderInfo(this._provider).name,
@@ -892,8 +945,8 @@ var FormoAnalytics = /** @class */ (function () {
892
945
  });
893
946
  if (!this.isAutocaptureEnabled("disconnect")) return [3 /*break*/, 19];
894
947
  return [4 /*yield*/, this.disconnect({
895
- chainId: this.currentChainId,
896
- address: this.currentAddress,
948
+ chainId: this._evmChainId,
949
+ address: this._evmAddress,
897
950
  })];
898
951
  case 18:
899
952
  _a.sent();
@@ -901,15 +954,10 @@ var FormoAnalytics = /** @class */ (function () {
901
954
  case 19:
902
955
  logger_1.logger.debug("OnAccountsChanged: Disconnect event skipped for old provider (autocapture.disconnect: false)");
903
956
  // Still clear state even if not tracking the event
904
- this.currentAddress = undefined;
905
- this.currentChainId = undefined;
957
+ this.clearChainState('evm');
906
958
  _a.label = 20;
907
- case 20:
908
- // Clear state and let the new provider become active
909
- this.clearActiveProvider();
910
- _a.label = 21;
911
- case 21: return [3 /*break*/, 26];
912
- case 22:
959
+ case 20: return [3 /*break*/, 25];
960
+ case 21:
913
961
  error_2 = _a.sent();
914
962
  logger_1.logger.warn("OnAccountsChanged: Could not check current provider accounts, switching to new provider", {
915
963
  error: error_2 instanceof Error ? error_2.message : String(error_2),
@@ -920,40 +968,36 @@ var FormoAnalytics = /** @class */ (function () {
920
968
  newProvider: this.getProviderInfo(provider).name,
921
969
  reason: PROVIDER_SWITCH_REASONS.CHECK_FAILED,
922
970
  });
923
- if (!this.isAutocaptureEnabled("disconnect")) return [3 /*break*/, 24];
971
+ if (!this.isAutocaptureEnabled("disconnect")) return [3 /*break*/, 23];
924
972
  return [4 /*yield*/, this.disconnect({
925
- chainId: this.currentChainId,
926
- address: this.currentAddress,
973
+ chainId: this._evmChainId,
974
+ address: this._evmAddress,
927
975
  })];
928
- case 23:
976
+ case 22:
929
977
  _a.sent();
930
- return [3 /*break*/, 25];
931
- case 24:
978
+ return [3 /*break*/, 24];
979
+ case 23:
932
980
  logger_1.logger.debug("OnAccountsChanged: Disconnect event skipped for failed provider check (autocapture.disconnect: false)");
933
981
  // Still clear state even if not tracking the event
934
- this.currentAddress = undefined;
935
- this.currentChainId = undefined;
936
- _a.label = 25;
982
+ this.clearChainState('evm');
983
+ _a.label = 24;
984
+ case 24: return [3 /*break*/, 25];
937
985
  case 25:
938
- this.clearActiveProvider();
939
- return [3 /*break*/, 26];
940
- case 26:
941
986
  // Set provider if none exists (first connection)
942
987
  if (!this._provider) {
943
988
  this._provider = provider;
944
989
  }
945
990
  // If both the provider and address are the same, no-op
946
- if (this._provider === provider && address === this.currentAddress) {
991
+ if (this._provider === provider && address === this._evmAddress) {
947
992
  return [2 /*return*/];
948
993
  }
949
994
  return [4 /*yield*/, this.getCurrentChainId(provider)];
950
- case 27:
995
+ case 26:
951
996
  nextChainId = _a.sent();
952
- wasDisconnected = !this.currentAddress;
997
+ wasDisconnected = !this._evmAddress;
953
998
  // CRITICAL: Always update state regardless of whether connect tracking is enabled
954
999
  // This ensures disconnect events will have valid address/chainId values
955
- this.currentAddress = address;
956
- this.currentChainId = nextChainId;
1000
+ this.setChainState('evm', { address: address, chainId: nextChainId });
957
1001
  providerInfo = this.getProviderInfo(provider);
958
1002
  effectiveChainId = nextChainId || 0;
959
1003
  if (this.isAutocaptureEnabled("connect")) {
@@ -1014,7 +1058,7 @@ var FormoAnalytics = /** @class */ (function () {
1014
1058
  this.handleProviderMismatch(provider);
1015
1059
  }
1016
1060
  // Chain changes only matter for connected users
1017
- if (!this.currentAddress) {
1061
+ if (!this._evmAddress) {
1018
1062
  logger_1.logger.info("OnChainChanged: No current address, user appears disconnected");
1019
1063
  return [2 /*return*/, Promise.resolve()];
1020
1064
  }
@@ -1022,19 +1066,19 @@ var FormoAnalytics = /** @class */ (function () {
1022
1066
  if (!this._provider) {
1023
1067
  this._provider = provider;
1024
1068
  }
1025
- this.currentChainId = nextChainId;
1069
+ this.setChainState('evm', { chainId: nextChainId });
1026
1070
  try {
1027
- // This is just a chain change since we already confirmed currentAddress exists
1071
+ // This is just a chain change since we already confirmed _evmAddress exists
1028
1072
  if (this.isAutocaptureEnabled("chain")) {
1029
1073
  return [2 /*return*/, this.chain({
1030
- chainId: this.currentChainId,
1031
- address: this.currentAddress,
1074
+ chainId: nextChainId,
1075
+ address: this._evmAddress,
1032
1076
  })];
1033
1077
  }
1034
1078
  else {
1035
1079
  logger_1.logger.debug("OnChainChanged: Chain event skipped (autocapture.chain: false)", {
1036
- chainId: this.currentChainId,
1037
- address: this.currentAddress,
1080
+ chainId: this._evmChainId,
1081
+ address: this._evmAddress,
1038
1082
  });
1039
1083
  }
1040
1084
  }
@@ -1070,8 +1114,8 @@ var FormoAnalytics = /** @class */ (function () {
1070
1114
  if (this._provider !== provider)
1071
1115
  return [2 /*return*/];
1072
1116
  logger_1.logger.info("OnDisconnect: Wallet disconnect event received, current state:", {
1073
- currentAddress: this.currentAddress,
1074
- currentChainId: this.currentChainId,
1117
+ currentAddress: this._evmAddress,
1118
+ currentChainId: this._evmChainId,
1075
1119
  });
1076
1120
  if (!this.isAutocaptureEnabled("disconnect")) return [3 /*break*/, 5];
1077
1121
  _a.label = 1;
@@ -1079,8 +1123,8 @@ var FormoAnalytics = /** @class */ (function () {
1079
1123
  _a.trys.push([1, 3, , 4]);
1080
1124
  // Pass current state explicitly to ensure we have the data for the disconnect event
1081
1125
  return [4 /*yield*/, this.disconnect({
1082
- chainId: this.currentChainId,
1083
- address: this.currentAddress,
1126
+ chainId: this._evmChainId,
1127
+ address: this._evmAddress,
1084
1128
  })];
1085
1129
  case 2:
1086
1130
  // Pass current state explicitly to ensure we have the data for the disconnect event
@@ -1094,9 +1138,7 @@ var FormoAnalytics = /** @class */ (function () {
1094
1138
  case 5:
1095
1139
  logger_1.logger.debug("OnDisconnect: Disconnect event skipped (autocapture.disconnect: false)");
1096
1140
  // Still clear state even if not tracking the event
1097
- this.currentAddress = undefined;
1098
- this.currentChainId = undefined;
1099
- this.clearActiveProvider();
1141
+ this.clearChainState('evm');
1100
1142
  _a.label = 6;
1101
1143
  case 6: return [2 /*return*/];
1102
1144
  }
@@ -1122,7 +1164,7 @@ var FormoAnalytics = /** @class */ (function () {
1122
1164
  case 2:
1123
1165
  address = _a.sent();
1124
1166
  if (chainId && address) {
1125
- wasDisconnected = !this.currentAddress;
1167
+ wasDisconnected = !this._evmAddress;
1126
1168
  // Set provider if none exists
1127
1169
  if (!this._provider) {
1128
1170
  this._provider = provider;
@@ -1131,12 +1173,13 @@ var FormoAnalytics = /** @class */ (function () {
1131
1173
  // CRITICAL: Always update state from active provider regardless of tracking config
1132
1174
  // This ensures disconnect events will have valid address/chainId values
1133
1175
  if (isActiveProvider) {
1134
- this.currentChainId = chainId;
1135
- this.currentAddress =
1136
- this.validateAndChecksumAddress(address) || undefined;
1176
+ this.setChainState('evm', {
1177
+ chainId: chainId,
1178
+ address: (0, address_1.validateAndChecksumAddress)(address) || undefined,
1179
+ });
1137
1180
  }
1138
1181
  // Conditionally emit connect event based on tracking configuration
1139
- if (isActiveProvider && this.currentAddress) {
1182
+ if (isActiveProvider && this._evmAddress) {
1140
1183
  providerInfo = this.getProviderInfo(provider);
1141
1184
  effectiveChainId = chainId || 0;
1142
1185
  if (this.isAutocaptureEnabled("connect")) {
@@ -1219,7 +1262,7 @@ var FormoAnalytics = /** @class */ (function () {
1219
1262
  logger_1.logger.debug("Signature event skipped (autocapture.signature: false)", { method: method });
1220
1263
  return [2 /*return*/, request({ method: method, params: params })];
1221
1264
  }
1222
- _c = this.currentChainId;
1265
+ _c = this._evmChainId;
1223
1266
  if (_c) return [3 /*break*/, 2];
1224
1267
  return [4 /*yield*/, this.getCurrentChainId(provider)];
1225
1268
  case 1:
@@ -1716,14 +1759,37 @@ var FormoAnalytics = /** @class */ (function () {
1716
1759
  enumerable: false,
1717
1760
  configurable: true
1718
1761
  });
1762
+ Object.defineProperty(FormoAnalytics.prototype, "solana", {
1763
+ /**
1764
+ * Access the Solana integration manager.
1765
+ * Lazily creates one if not already initialized.
1766
+ *
1767
+ * @example
1768
+ * ```tsx
1769
+ * formo.solana.setWallet(wallet);
1770
+ * formo.solana.setConnection(connection);
1771
+ * formo.solana.setCluster("devnet");
1772
+ * formo.solana.syncWalletState();
1773
+ * ```
1774
+ */
1775
+ get: function () {
1776
+ if (!this.solanaManager) {
1777
+ this.solanaManager = new SolanaManager_1.SolanaManager(this);
1778
+ }
1779
+ return this.solanaManager;
1780
+ },
1781
+ enumerable: false,
1782
+ configurable: true
1783
+ });
1719
1784
  FormoAnalytics.prototype.getAddress = function (provider) {
1720
1785
  return __awaiter(this, void 0, void 0, function () {
1721
1786
  var p, accounts, err_3, code;
1722
1787
  return __generator(this, function (_a) {
1723
1788
  switch (_a.label) {
1724
1789
  case 0:
1725
- if (this.currentAddress)
1726
- return [2 /*return*/, this.currentAddress];
1790
+ // Use EVM-specific state to avoid returning a Solana address in an EVM context
1791
+ if (this._chainState.evm.address)
1792
+ return [2 /*return*/, this._chainState.evm.address];
1727
1793
  p = provider || this.provider;
1728
1794
  if (!p) {
1729
1795
  logger_1.logger.info("The provider is not set");
@@ -1736,7 +1802,7 @@ var FormoAnalytics = /** @class */ (function () {
1736
1802
  case 2:
1737
1803
  accounts = _a.sent();
1738
1804
  if (accounts && accounts.length > 0) {
1739
- return [2 /*return*/, this.validateAndChecksumAddress(accounts[0]) || null];
1805
+ return [2 /*return*/, (0, address_1.validateAndChecksumAddress)(accounts[0]) || null];
1740
1806
  }
1741
1807
  return [3 /*break*/, 4];
1742
1808
  case 3:
@@ -1754,7 +1820,6 @@ var FormoAnalytics = /** @class */ (function () {
1754
1820
  FormoAnalytics.prototype.getAccounts = function (provider) {
1755
1821
  return __awaiter(this, void 0, void 0, function () {
1756
1822
  var p, res, err_4, code;
1757
- var _this = this;
1758
1823
  return __generator(this, function (_a) {
1759
1824
  switch (_a.label) {
1760
1825
  case 0:
@@ -1770,7 +1835,7 @@ var FormoAnalytics = /** @class */ (function () {
1770
1835
  if (!res || res.length === 0)
1771
1836
  return [2 /*return*/, null];
1772
1837
  return [2 /*return*/, res
1773
- .map(function (e) { return _this.validateAndChecksumAddress(e); })
1838
+ .map(function (e) { return (0, address_1.validateAndChecksumAddress)(e); })
1774
1839
  .filter(function (e) { return e !== undefined; })];
1775
1840
  case 3:
1776
1841
  err_4 = _a.sent();
@@ -1822,12 +1887,12 @@ var FormoAnalytics = /** @class */ (function () {
1822
1887
  var rawAddress = method === "personal_sign"
1823
1888
  ? params[1]
1824
1889
  : params[0];
1825
- var validAddress = this.validateAndChecksumAddress(rawAddress);
1890
+ var validAddress = (0, address_1.validateAndChecksumAddress)(rawAddress);
1826
1891
  if (!validAddress) {
1827
1892
  throw new Error("Invalid address in signature payload: ".concat(rawAddress));
1828
1893
  }
1829
1894
  var basePayload = {
1830
- chainId: (_a = chainId !== null && chainId !== void 0 ? chainId : this.currentChainId) !== null && _a !== void 0 ? _a : undefined,
1895
+ chainId: (_a = chainId !== null && chainId !== void 0 ? chainId : this._evmChainId) !== null && _a !== void 0 ? _a : undefined,
1831
1896
  address: validAddress,
1832
1897
  };
1833
1898
  if (method === "personal_sign") {
@@ -1838,29 +1903,25 @@ var FormoAnalytics = /** @class */ (function () {
1838
1903
  };
1839
1904
  FormoAnalytics.prototype.buildTransactionEventPayload = function (params, provider) {
1840
1905
  return __awaiter(this, void 0, void 0, function () {
1841
- var _a, data, from, to, value, validAddress, _b;
1906
+ var _a, data, from, to, value, validAddress, builder_codes, _b;
1842
1907
  var _c;
1843
1908
  return __generator(this, function (_d) {
1844
1909
  switch (_d.label) {
1845
1910
  case 0:
1846
1911
  _a = params[0], data = _a.data, from = _a.from, to = _a.to, value = _a.value;
1847
- validAddress = this.validateAndChecksumAddress(from);
1912
+ validAddress = (0, address_1.validateAndChecksumAddress)(from);
1848
1913
  if (!validAddress) {
1849
1914
  throw new Error("Invalid address in transaction payload: ".concat(from));
1850
1915
  }
1916
+ builder_codes = (0, builderCode_1.extractBuilderCodes)(data);
1851
1917
  _c = {};
1852
- _b = this.currentChainId;
1918
+ _b = this._evmChainId;
1853
1919
  if (_b) return [3 /*break*/, 2];
1854
1920
  return [4 /*yield*/, this.getCurrentChainId(provider)];
1855
1921
  case 1:
1856
1922
  _b = (_d.sent());
1857
1923
  _d.label = 2;
1858
- case 2: return [2 /*return*/, (_c.chainId = _b,
1859
- _c.data = data,
1860
- _c.address = validAddress,
1861
- _c.to = to,
1862
- _c.value = value,
1863
- _c)];
1924
+ case 2: return [2 /*return*/, __assign.apply(void 0, [(_c.chainId = _b, _c.data = data, _c.address = validAddress, _c.to = to, _c.value = value, _c), (builder_codes && { builder_codes: builder_codes })])];
1864
1925
  }
1865
1926
  });
1866
1927
  });
@@ -2000,19 +2061,84 @@ var FormoAnalytics = /** @class */ (function () {
2000
2061
  // If this is a different provider, allow the switch
2001
2062
  if (this._provider) {
2002
2063
  // Clear any provider-specific state when switching
2003
- this.currentChainId = undefined;
2004
- this.currentAddress = undefined;
2064
+ this.setChainState('evm', { address: undefined, chainId: undefined, provider: provider });
2005
2065
  }
2006
- this._provider = provider;
2066
+ else {
2067
+ this._provider = provider;
2068
+ }
2069
+ };
2070
+ /**
2071
+ * Determine which namespace a chainId belongs to.
2072
+ */
2073
+ FormoAnalytics.prototype.getNamespace = function (chainId) {
2074
+ return (0, solana_1.isSolanaChainId)(chainId) ? 'solana' : 'evm';
2075
+ };
2076
+ /**
2077
+ * Update per-chain state and sync the derived currentAddress/currentChainId.
2078
+ * Accepts either a namespace string ('evm'/'solana') or a chainId number
2079
+ * to resolve the namespace automatically. When a chainId number is passed,
2080
+ * it is also stored as the namespace's chainId (unless explicitly overridden
2081
+ * in the update object).
2082
+ */
2083
+ FormoAnalytics.prototype.setChainState = function (namespaceOrChainId, update) {
2084
+ var namespace = typeof namespaceOrChainId === 'string'
2085
+ ? namespaceOrChainId
2086
+ : this.getNamespace(namespaceOrChainId);
2087
+ var ns = this._chainState[namespace];
2088
+ if ('address' in update)
2089
+ ns.address = update.address;
2090
+ if ('chainId' in update) {
2091
+ ns.chainId = update.chainId;
2092
+ }
2093
+ else if (typeof namespaceOrChainId === 'number') {
2094
+ ns.chainId = namespaceOrChainId;
2095
+ }
2096
+ if (namespace === 'evm' && 'provider' in update) {
2097
+ ns.provider = update.provider;
2098
+ }
2099
+ this._activeNamespace = namespace;
2100
+ this.syncDerivedState();
2007
2101
  };
2008
2102
  /**
2009
- * Helper method to validate and checksum an address
2010
- * @param address The address to validate and checksum
2011
- * @returns The checksummed address or undefined if invalid
2103
+ * Clear per-chain state for a given namespace (or chainId) and sync derived state.
2012
2104
  */
2013
- FormoAnalytics.prototype.validateAndChecksumAddress = function (address) {
2014
- var validAddress = (0, address_1.getValidAddress)(address);
2015
- return validAddress ? (0, utils_1.toChecksumAddress)(validAddress) : undefined;
2105
+ FormoAnalytics.prototype.clearChainState = function (namespaceOrChainId) {
2106
+ var namespace = typeof namespaceOrChainId === 'string'
2107
+ ? namespaceOrChainId
2108
+ : this.getNamespace(namespaceOrChainId);
2109
+ if (namespace === 'evm') {
2110
+ this._chainState.evm = {};
2111
+ }
2112
+ else {
2113
+ this._chainState.solana = {};
2114
+ }
2115
+ this.syncDerivedState();
2116
+ };
2117
+ /**
2118
+ * Synchronize currentAddress/currentChainId from the active namespace.
2119
+ * Last-connected-chain-wins: _activeNamespace takes precedence.
2120
+ */
2121
+ FormoAnalytics.prototype.syncDerivedState = function () {
2122
+ var active = this._activeNamespace;
2123
+ if (active) {
2124
+ var state = this._chainState[active];
2125
+ if (state.address || state.chainId) {
2126
+ this.currentAddress = state.address;
2127
+ this.currentChainId = state.chainId;
2128
+ return;
2129
+ }
2130
+ }
2131
+ // Fall through to the other namespace
2132
+ var other = active === 'evm' ? 'solana' : 'evm';
2133
+ var otherState = this._chainState[other];
2134
+ if (otherState.address || otherState.chainId) {
2135
+ this.currentAddress = otherState.address;
2136
+ this.currentChainId = otherState.chainId;
2137
+ this._activeNamespace = other;
2138
+ return;
2139
+ }
2140
+ this.currentAddress = undefined;
2141
+ this.currentChainId = undefined;
2016
2142
  };
2017
2143
  /**
2018
2144
  * Helper method to clear the active provider state