@formo/analytics 1.26.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 (87) hide show
  1. package/README.md +6 -4
  2. package/dist/cjs/src/FormoAnalytics.d.ts +71 -12
  3. package/dist/cjs/src/FormoAnalytics.js +274 -147
  4. package/dist/cjs/src/event/EventFactory.d.ts +10 -2
  5. package/dist/cjs/src/event/EventFactory.js +32 -21
  6. package/dist/cjs/src/fetch/index.d.ts +10 -2
  7. package/dist/cjs/src/fetch/index.js +122 -4
  8. package/dist/cjs/src/index.d.ts +4 -0
  9. package/dist/cjs/src/index.js +6 -0
  10. package/dist/cjs/src/privy/index.d.ts +9 -0
  11. package/dist/cjs/src/privy/index.js +12 -0
  12. package/dist/cjs/src/privy/types.d.ts +175 -0
  13. package/dist/cjs/src/privy/types.js +12 -0
  14. package/dist/cjs/src/privy/utils.d.ts +32 -0
  15. package/dist/cjs/src/privy/utils.js +188 -0
  16. package/dist/cjs/src/queue/EventQueue.d.ts +24 -2
  17. package/dist/cjs/src/queue/EventQueue.js +158 -49
  18. package/dist/cjs/src/session/index.js +2 -1
  19. package/dist/cjs/src/solana/SolanaAdapter.d.ts +211 -0
  20. package/dist/cjs/src/solana/SolanaAdapter.js +975 -0
  21. package/dist/cjs/src/solana/SolanaManager.d.ts +24 -0
  22. package/dist/cjs/src/solana/SolanaManager.js +80 -0
  23. package/dist/cjs/src/solana/address.d.ts +72 -0
  24. package/dist/cjs/src/solana/address.js +176 -0
  25. package/dist/cjs/src/solana/index.d.ts +13 -0
  26. package/dist/cjs/src/solana/index.js +32 -0
  27. package/dist/cjs/src/solana/types.d.ts +206 -0
  28. package/dist/cjs/src/solana/types.js +80 -0
  29. package/dist/cjs/src/types/base.d.ts +25 -0
  30. package/dist/cjs/src/types/events.d.ts +9 -6
  31. package/dist/cjs/src/utils/address.d.ts +21 -0
  32. package/dist/cjs/src/utils/address.js +48 -1
  33. package/dist/cjs/src/utils/builderCode.d.ts +30 -0
  34. package/dist/cjs/src/utils/builderCode.js +143 -0
  35. package/dist/cjs/src/utils/index.d.ts +1 -0
  36. package/dist/cjs/src/utils/index.js +1 -0
  37. package/dist/cjs/src/version.d.ts +1 -1
  38. package/dist/cjs/src/version.js +1 -1
  39. package/dist/cjs/src/wagmi/WagmiEventHandler.d.ts +24 -0
  40. package/dist/cjs/src/wagmi/WagmiEventHandler.js +242 -24
  41. package/dist/cjs/src/wagmi/types.d.ts +31 -0
  42. package/dist/cjs/src/wagmi/utils.d.ts +79 -0
  43. package/dist/cjs/src/wagmi/utils.js +218 -0
  44. package/dist/esm/src/FormoAnalytics.d.ts +71 -12
  45. package/dist/esm/src/FormoAnalytics.js +275 -148
  46. package/dist/esm/src/event/EventFactory.d.ts +10 -2
  47. package/dist/esm/src/event/EventFactory.js +34 -23
  48. package/dist/esm/src/fetch/index.d.ts +10 -2
  49. package/dist/esm/src/fetch/index.js +123 -2
  50. package/dist/esm/src/index.d.ts +4 -0
  51. package/dist/esm/src/index.js +3 -0
  52. package/dist/esm/src/privy/index.d.ts +9 -0
  53. package/dist/esm/src/privy/index.js +8 -0
  54. package/dist/esm/src/privy/types.d.ts +175 -0
  55. package/dist/esm/src/privy/types.js +11 -0
  56. package/dist/esm/src/privy/utils.d.ts +32 -0
  57. package/dist/esm/src/privy/utils.js +185 -0
  58. package/dist/esm/src/queue/EventQueue.d.ts +24 -2
  59. package/dist/esm/src/queue/EventQueue.js +158 -49
  60. package/dist/esm/src/session/index.js +2 -1
  61. package/dist/esm/src/solana/SolanaAdapter.d.ts +211 -0
  62. package/dist/esm/src/solana/SolanaAdapter.js +972 -0
  63. package/dist/esm/src/solana/SolanaManager.d.ts +24 -0
  64. package/dist/esm/src/solana/SolanaManager.js +77 -0
  65. package/dist/esm/src/solana/address.d.ts +72 -0
  66. package/dist/esm/src/solana/address.js +167 -0
  67. package/dist/esm/src/solana/index.d.ts +13 -0
  68. package/dist/esm/src/solana/index.js +13 -0
  69. package/dist/esm/src/solana/types.d.ts +206 -0
  70. package/dist/esm/src/solana/types.js +74 -0
  71. package/dist/esm/src/types/base.d.ts +25 -0
  72. package/dist/esm/src/types/events.d.ts +9 -6
  73. package/dist/esm/src/utils/address.d.ts +21 -0
  74. package/dist/esm/src/utils/address.js +45 -0
  75. package/dist/esm/src/utils/builderCode.d.ts +30 -0
  76. package/dist/esm/src/utils/builderCode.js +140 -0
  77. package/dist/esm/src/utils/index.d.ts +1 -0
  78. package/dist/esm/src/utils/index.js +1 -0
  79. package/dist/esm/src/version.d.ts +1 -1
  80. package/dist/esm/src/version.js +1 -1
  81. package/dist/esm/src/wagmi/WagmiEventHandler.d.ts +24 -0
  82. package/dist/esm/src/wagmi/WagmiEventHandler.js +242 -24
  83. package/dist/esm/src/wagmi/types.d.ts +31 -0
  84. package/dist/esm/src/wagmi/utils.d.ts +79 -0
  85. package/dist/esm/src/wagmi/utils.js +211 -0
  86. package/dist/index.umd.min.js +1 -1
  87. package/package.json +20 -4
@@ -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
@@ -140,6 +147,7 @@ var FormoAnalytics = /** @class */ (function () {
140
147
  retryCount: options.retryCount,
141
148
  maxQueueSize: options.maxQueueSize,
142
149
  flushInterval: options.flushInterval,
150
+ errorHandler: options.errorHandler,
143
151
  }), options);
144
152
  // Check consent status on initialization
145
153
  if (this.hasOptedOutTracking()) {
@@ -164,9 +172,39 @@ var FormoAnalytics = /** @class */ (function () {
164
172
  this.trackEIP1193Provider(provider);
165
173
  }
166
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
+ }
167
179
  this.trackPageHit();
168
180
  this.trackPageHits();
169
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
+ });
170
208
  /**
171
209
  * Helper method to check if a provider is different from the currently active one
172
210
  * @param provider The provider to check
@@ -246,12 +284,17 @@ var FormoAnalytics = /** @class */ (function () {
246
284
  * @returns {void}
247
285
  */
248
286
  FormoAnalytics.prototype.cleanup = function () {
249
- logger_1.logger.info("FormoAnalytics: Cleaning up resources");
287
+ logger_1.logger.debug("FormoAnalytics: Cleaning up resources");
250
288
  // Clean up Wagmi handler if present
251
289
  if (this.wagmiHandler) {
252
290
  this.wagmiHandler.cleanup();
253
291
  this.wagmiHandler = undefined;
254
292
  }
293
+ // Clean up Solana manager if present
294
+ if (this.solanaManager) {
295
+ this.solanaManager.cleanup();
296
+ this.solanaManager = undefined;
297
+ }
255
298
  // Clean up EIP-1193 providers if not in Wagmi mode
256
299
  if (!this.isWagmiMode) {
257
300
  for (var _i = 0, _a = Array.from(this._trackedProviders); _i < _a.length; _i++) {
@@ -259,7 +302,7 @@ var FormoAnalytics = /** @class */ (function () {
259
302
  this.untrackProvider(provider);
260
303
  }
261
304
  }
262
- logger_1.logger.info("FormoAnalytics: Cleanup complete");
305
+ logger_1.logger.debug("FormoAnalytics: Cleanup complete");
263
306
  };
264
307
  /**
265
308
  * Emits a connect wallet event.
@@ -272,7 +315,7 @@ var FormoAnalytics = /** @class */ (function () {
272
315
  */
273
316
  FormoAnalytics.prototype.connect = function (_a, properties_1, context_1, callback_1) {
274
317
  return __awaiter(this, arguments, void 0, function (_b, properties, context, callback) {
275
- var checksummedAddress;
318
+ var validAddress;
276
319
  var chainId = _b.chainId, address = _b.address;
277
320
  return __generator(this, function (_c) {
278
321
  switch (_c.label) {
@@ -285,16 +328,15 @@ var FormoAnalytics = /** @class */ (function () {
285
328
  logger_1.logger.warn("Connect: Address cannot be empty");
286
329
  return [2 /*return*/];
287
330
  }
288
- this.currentChainId = chainId;
289
- checksummedAddress = this.validateAndChecksumAddress(address);
290
- if (!checksummedAddress) {
291
- 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."));
292
334
  return [2 /*return*/];
293
335
  }
294
- this.currentAddress = checksummedAddress;
336
+ this.setChainState(chainId, { address: validAddress });
295
337
  return [4 /*yield*/, this.trackEvent(constants_1.EventType.CONNECT, {
296
338
  chainId: chainId,
297
- address: this.currentAddress,
339
+ address: validAddress,
298
340
  }, properties, context, callback)];
299
341
  case 1:
300
342
  _c.sent();
@@ -314,13 +356,14 @@ var FormoAnalytics = /** @class */ (function () {
314
356
  */
315
357
  FormoAnalytics.prototype.disconnect = function (params, properties, context, callback) {
316
358
  return __awaiter(this, void 0, void 0, function () {
317
- var chainId, address, providerInfo, disconnectProperties;
359
+ var chainId, address, isSolana, providerInfo, disconnectProperties;
318
360
  return __generator(this, function (_a) {
319
361
  switch (_a.label) {
320
362
  case 0:
321
363
  chainId = (params === null || params === void 0 ? void 0 : params.chainId) || this.currentChainId;
322
364
  address = (params === null || params === void 0 ? void 0 : params.address) || this.currentAddress;
323
- providerInfo = this._provider
365
+ isSolana = (0, solana_1.isSolanaChainId)(chainId);
366
+ providerInfo = !isSolana && this._provider
324
367
  ? this.getProviderInfo(this._provider)
325
368
  : null;
326
369
  logger_1.logger.info("Disconnect: Emitting disconnect event with:", {
@@ -336,9 +379,9 @@ var FormoAnalytics = /** @class */ (function () {
336
379
  return [4 /*yield*/, this.trackEvent(constants_1.EventType.DISCONNECT, __assign(__assign({}, (chainId && { chainId: chainId })), (address && { address: address })), disconnectProperties, context, callback)];
337
380
  case 1:
338
381
  _a.sent();
339
- this.currentAddress = undefined;
340
- this.currentChainId = undefined;
341
- 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);
342
385
  logger_1.logger.info("Wallet disconnected: Cleared currentAddress, currentChainId, and provider");
343
386
  return [2 /*return*/];
344
387
  }
@@ -372,7 +415,7 @@ var FormoAnalytics = /** @class */ (function () {
372
415
  logger_1.logger.warn("FormoAnalytics::chain: address was empty and no previous address has been recorded");
373
416
  return [2 /*return*/];
374
417
  }
375
- this.currentChainId = chainId;
418
+ this.setChainState(chainId, {});
376
419
  return [4 /*yield*/, this.trackEvent(constants_1.EventType.CHAIN, {
377
420
  chainId: chainId,
378
421
  address: address || this.currentAddress,
@@ -425,10 +468,10 @@ var FormoAnalytics = /** @class */ (function () {
425
468
  */
426
469
  FormoAnalytics.prototype.transaction = function (_a, properties_1, context_1, callback_1) {
427
470
  return __awaiter(this, arguments, void 0, function (_b, properties, context, callback) {
428
- var status = _b.status, chainId = _b.chainId, address = _b.address, data = _b.data, to = _b.to, value = _b.value, transactionHash = _b.transactionHash;
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;
429
472
  return __generator(this, function (_c) {
430
473
  switch (_c.label) {
431
- case 0: return [4 /*yield*/, this.trackEvent(constants_1.EventType.TRANSACTION, __assign({ status: status, chainId: chainId, address: address, data: data, to: to, value: value }, (transactionHash && { transactionHash: transactionHash })), 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)];
432
475
  case 1:
433
476
  _c.sent();
434
477
  return [2 /*return*/];
@@ -438,42 +481,59 @@ var FormoAnalytics = /** @class */ (function () {
438
481
  };
439
482
  /**
440
483
  * Emits an identify event with current wallet address and provider info.
441
- * @param {string} params.address
442
- * @param {string} params.userId
443
- * @param {string} params.rdns
444
- * @param {string} params.providerName
445
- * @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
446
490
  * @param {IFormoEventContext} context
447
491
  * @param {(...args: unknown[]) => void} callback
448
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
+ * ```
449
509
  */
450
510
  FormoAnalytics.prototype.identify = function (params, properties, context, callback) {
451
511
  return __awaiter(this, void 0, void 0, function () {
452
- var _i, _a, providerDetail, provider, address_2, validAddress_1, err_1, userId, address, providerName, rdns, validAddress, isAlreadyIdentified, e_1;
453
- var _b;
454
- return __generator(this, function (_c) {
455
- 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) {
456
516
  case 0:
457
- _c.trys.push([0, 12, , 13]);
517
+ _d.trys.push([0, 12, , 13]);
458
518
  if (!!params) return [3 /*break*/, 10];
459
519
  // If no params provided, auto-identify
460
520
  logger_1.logger.info("Auto-identifying with providers:", this._providers.map(function (p) { return p.info.name; }));
461
521
  _i = 0, _a = this._providers;
462
- _c.label = 1;
522
+ _d.label = 1;
463
523
  case 1:
464
524
  if (!(_i < _a.length)) return [3 /*break*/, 9];
465
525
  providerDetail = _a[_i];
466
526
  provider = providerDetail.provider;
467
527
  if (!provider)
468
528
  return [3 /*break*/, 8];
469
- _c.label = 2;
529
+ _d.label = 2;
470
530
  case 2:
471
- _c.trys.push([2, 7, , 8]);
531
+ _d.trys.push([2, 7, , 8]);
472
532
  return [4 /*yield*/, this.getAddress(provider)];
473
533
  case 3:
474
- address_2 = _c.sent();
534
+ address_2 = _d.sent();
475
535
  if (!address_2) return [3 /*break*/, 6];
476
- validAddress_1 = this.validateAndChecksumAddress(address_2);
536
+ validAddress_1 = (0, address_1.validateAndChecksumAddress)(address_2);
477
537
  logger_1.logger.info("Auto-identify: Checking deduplication", {
478
538
  validAddress: validAddress_1,
479
539
  rdns: providerDetail.info.rdns,
@@ -493,16 +553,16 @@ var FormoAnalytics = /** @class */ (function () {
493
553
  }, properties, context, callback)];
494
554
  case 4:
495
555
  // NOTE: do not set this.currentAddress without explicit connect or identify
496
- _c.sent();
556
+ _d.sent();
497
557
  return [3 /*break*/, 6];
498
558
  case 5:
499
559
  if (validAddress_1) {
500
560
  logger_1.logger.info("Auto-identify: Skipping already identified wallet", validAddress_1, providerDetail.info.name, providerDetail.info.rdns);
501
561
  }
502
- _c.label = 6;
562
+ _d.label = 6;
503
563
  case 6: return [3 /*break*/, 8];
504
564
  case 7:
505
- err_1 = _c.sent();
565
+ err_1 = _d.sent();
506
566
  logger_1.logger.error("Failed to identify provider ".concat(providerDetail.info.name, ":"), err_1);
507
567
  return [3 /*break*/, 8];
508
568
  case 8:
@@ -510,32 +570,32 @@ var FormoAnalytics = /** @class */ (function () {
510
570
  return [3 /*break*/, 1];
511
571
  case 9: return [2 /*return*/];
512
572
  case 10:
513
- 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
514
580
  logger_1.logger.info("Identify", address, userId, providerName, rdns);
515
- validAddress = undefined;
516
- if (address) {
517
- validAddress = this.validateAndChecksumAddress(address);
518
- this.currentAddress = validAddress || undefined;
519
- if (!validAddress) {
520
- (_b = logger_1.logger.warn) === null || _b === void 0 ? void 0 : _b.call(logger_1.logger, "Invalid address provided to identify:", address);
521
- }
581
+ validAddress = (0, address_1.validateAddress)(address);
582
+ if (validAddress) {
583
+ this.currentAddress = validAddress;
522
584
  }
523
585
  else {
524
- 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*/];
525
588
  }
526
589
  if (userId) {
527
590
  this.currentUserId = userId;
528
591
  (0, storage_1.cookie)().set(constants_1.SESSION_USER_ID_KEY, userId);
529
592
  }
530
- isAlreadyIdentified = validAddress
531
- ? this.session.isWalletIdentified(validAddress, rdns || "")
532
- : false;
593
+ isAlreadyIdentified = this.session.isWalletIdentified(validAddress, rdns || "");
533
594
  logger_1.logger.debug("Identify: Checking deduplication", {
534
595
  validAddress: validAddress,
535
596
  rdns: rdns,
536
597
  providerName: providerName,
537
- hasValidAddress: !!validAddress,
538
- hasRdns: !!rdns,
598
+ userId: userId,
539
599
  isAlreadyIdentified: isAlreadyIdentified,
540
600
  });
541
601
  if (isAlreadyIdentified) {
@@ -543,10 +603,7 @@ var FormoAnalytics = /** @class */ (function () {
543
603
  return [2 /*return*/];
544
604
  }
545
605
  // Mark as identified before emitting the event
546
- // Mark even if rdns is empty to prevent duplicate empty identifies
547
- if (validAddress) {
548
- this.session.markWalletIdentified(validAddress, rdns || "");
549
- }
606
+ this.session.markWalletIdentified(validAddress, rdns || "");
550
607
  return [4 /*yield*/, this.trackEvent(constants_1.EventType.IDENTIFY, {
551
608
  address: validAddress,
552
609
  providerName: providerName,
@@ -554,10 +611,10 @@ var FormoAnalytics = /** @class */ (function () {
554
611
  rdns: rdns,
555
612
  }, properties, context, callback)];
556
613
  case 11:
557
- _c.sent();
614
+ _d.sent();
558
615
  return [3 /*break*/, 13];
559
616
  case 12:
560
- e_1 = _c.sent();
617
+ e_1 = _d.sent();
561
618
  logger_1.logger.log("identify error", e_1);
562
619
  return [3 /*break*/, 13];
563
620
  case 13: return [2 /*return*/];
@@ -782,21 +839,21 @@ var FormoAnalytics = /** @class */ (function () {
782
839
  if (!(accounts.length === 0)) return [3 /*break*/, 9];
783
840
  if (!(this._provider === provider)) return [3 /*break*/, 7];
784
841
  logger_1.logger.info("OnAccountsChanged: Detecting disconnect, current state:", {
785
- currentAddress: this.currentAddress,
786
- currentChainId: this.currentChainId,
842
+ evmAddress: this._evmAddress,
843
+ evmChainId: this._evmChainId,
787
844
  providerMatch: this._provider === provider,
788
845
  });
789
846
  if (!this.isAutocaptureEnabled("disconnect")) return [3 /*break*/, 5];
790
847
  _a.label = 1;
791
848
  case 1:
792
849
  _a.trys.push([1, 3, , 4]);
793
- // 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
794
851
  return [4 /*yield*/, this.disconnect({
795
- chainId: this.currentChainId,
796
- address: this.currentAddress,
852
+ chainId: this._evmChainId,
853
+ address: this._evmAddress,
797
854
  })];
798
855
  case 2:
799
- // 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
800
857
  _a.sent();
801
858
  return [3 /*break*/, 4];
802
859
  case 3:
@@ -807,9 +864,7 @@ var FormoAnalytics = /** @class */ (function () {
807
864
  case 5:
808
865
  logger_1.logger.debug("OnAccountsChanged: Disconnect event skipped (autocapture.disconnect: false)");
809
866
  // Still clear state even if not tracking the event
810
- this.currentAddress = undefined;
811
- this.currentChainId = undefined;
812
- this.clearActiveProvider();
867
+ this.clearChainState('evm');
813
868
  _a.label = 6;
814
869
  case 6: return [3 /*break*/, 8];
815
870
  case 7:
@@ -817,14 +872,14 @@ var FormoAnalytics = /** @class */ (function () {
817
872
  _a.label = 8;
818
873
  case 8: return [2 /*return*/];
819
874
  case 9:
820
- address = this.validateAndChecksumAddress(accounts[0]);
875
+ address = (0, address_1.validateAndChecksumAddress)(accounts[0]);
821
876
  if (!address) {
822
877
  logger_1.logger.warn("onAccountsChanged: Invalid address received", accounts[0]);
823
878
  return [2 /*return*/];
824
879
  }
825
- if (!(this._provider && this._provider !== provider)) return [3 /*break*/, 26];
826
- currentStoredAddress = this.currentAddress;
827
- 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);
828
883
  logger_1.logger.info("OnAccountsChanged: Different provider attempting to connect", {
829
884
  activeProvider: this.getProviderInfo(this._provider).name,
830
885
  eventProvider: this.getProviderInfo(provider).name,
@@ -833,7 +888,7 @@ var FormoAnalytics = /** @class */ (function () {
833
888
  });
834
889
  _a.label = 10;
835
890
  case 10:
836
- _a.trys.push([10, 22, , 26]);
891
+ _a.trys.push([10, 21, , 25]);
837
892
  return [4 /*yield*/, this.getAccounts(this._provider)];
838
893
  case 11:
839
894
  activeProviderAccounts = _a.sent();
@@ -857,8 +912,8 @@ var FormoAnalytics = /** @class */ (function () {
857
912
  });
858
913
  if (!this.isAutocaptureEnabled("disconnect")) return [3 /*break*/, 13];
859
914
  return [4 /*yield*/, this.disconnect({
860
- chainId: this.currentChainId,
861
- address: this.currentAddress,
915
+ chainId: this._evmChainId,
916
+ address: this._evmAddress,
862
917
  })];
863
918
  case 12:
864
919
  _a.sent();
@@ -866,8 +921,7 @@ var FormoAnalytics = /** @class */ (function () {
866
921
  case 13:
867
922
  logger_1.logger.debug("OnAccountsChanged: Disconnect event skipped during provider switch (autocapture.disconnect: false)");
868
923
  // Still clear state even if not tracking the event
869
- this.currentAddress = undefined;
870
- this.currentChainId = undefined;
924
+ this.clearChainState('evm');
871
925
  _a.label = 14;
872
926
  case 14:
873
927
  // Clear state and let the new provider become active
@@ -882,7 +936,7 @@ var FormoAnalytics = /** @class */ (function () {
882
936
  newAddress: newProviderAddress,
883
937
  });
884
938
  return [2 /*return*/];
885
- case 16: return [3 /*break*/, 21];
939
+ case 16: return [3 /*break*/, 20];
886
940
  case 17:
887
941
  logger_1.logger.info("OnAccountsChanged: Current provider has no accounts, switching to new provider", {
888
942
  oldProvider: this.getProviderInfo(this._provider).name,
@@ -891,8 +945,8 @@ var FormoAnalytics = /** @class */ (function () {
891
945
  });
892
946
  if (!this.isAutocaptureEnabled("disconnect")) return [3 /*break*/, 19];
893
947
  return [4 /*yield*/, this.disconnect({
894
- chainId: this.currentChainId,
895
- address: this.currentAddress,
948
+ chainId: this._evmChainId,
949
+ address: this._evmAddress,
896
950
  })];
897
951
  case 18:
898
952
  _a.sent();
@@ -900,15 +954,10 @@ var FormoAnalytics = /** @class */ (function () {
900
954
  case 19:
901
955
  logger_1.logger.debug("OnAccountsChanged: Disconnect event skipped for old provider (autocapture.disconnect: false)");
902
956
  // Still clear state even if not tracking the event
903
- this.currentAddress = undefined;
904
- this.currentChainId = undefined;
957
+ this.clearChainState('evm');
905
958
  _a.label = 20;
906
- case 20:
907
- // Clear state and let the new provider become active
908
- this.clearActiveProvider();
909
- _a.label = 21;
910
- case 21: return [3 /*break*/, 26];
911
- case 22:
959
+ case 20: return [3 /*break*/, 25];
960
+ case 21:
912
961
  error_2 = _a.sent();
913
962
  logger_1.logger.warn("OnAccountsChanged: Could not check current provider accounts, switching to new provider", {
914
963
  error: error_2 instanceof Error ? error_2.message : String(error_2),
@@ -919,40 +968,36 @@ var FormoAnalytics = /** @class */ (function () {
919
968
  newProvider: this.getProviderInfo(provider).name,
920
969
  reason: PROVIDER_SWITCH_REASONS.CHECK_FAILED,
921
970
  });
922
- if (!this.isAutocaptureEnabled("disconnect")) return [3 /*break*/, 24];
971
+ if (!this.isAutocaptureEnabled("disconnect")) return [3 /*break*/, 23];
923
972
  return [4 /*yield*/, this.disconnect({
924
- chainId: this.currentChainId,
925
- address: this.currentAddress,
973
+ chainId: this._evmChainId,
974
+ address: this._evmAddress,
926
975
  })];
927
- case 23:
976
+ case 22:
928
977
  _a.sent();
929
- return [3 /*break*/, 25];
930
- case 24:
978
+ return [3 /*break*/, 24];
979
+ case 23:
931
980
  logger_1.logger.debug("OnAccountsChanged: Disconnect event skipped for failed provider check (autocapture.disconnect: false)");
932
981
  // Still clear state even if not tracking the event
933
- this.currentAddress = undefined;
934
- this.currentChainId = undefined;
935
- _a.label = 25;
982
+ this.clearChainState('evm');
983
+ _a.label = 24;
984
+ case 24: return [3 /*break*/, 25];
936
985
  case 25:
937
- this.clearActiveProvider();
938
- return [3 /*break*/, 26];
939
- case 26:
940
986
  // Set provider if none exists (first connection)
941
987
  if (!this._provider) {
942
988
  this._provider = provider;
943
989
  }
944
990
  // If both the provider and address are the same, no-op
945
- if (this._provider === provider && address === this.currentAddress) {
991
+ if (this._provider === provider && address === this._evmAddress) {
946
992
  return [2 /*return*/];
947
993
  }
948
994
  return [4 /*yield*/, this.getCurrentChainId(provider)];
949
- case 27:
995
+ case 26:
950
996
  nextChainId = _a.sent();
951
- wasDisconnected = !this.currentAddress;
997
+ wasDisconnected = !this._evmAddress;
952
998
  // CRITICAL: Always update state regardless of whether connect tracking is enabled
953
999
  // This ensures disconnect events will have valid address/chainId values
954
- this.currentAddress = address;
955
- this.currentChainId = nextChainId;
1000
+ this.setChainState('evm', { address: address, chainId: nextChainId });
956
1001
  providerInfo = this.getProviderInfo(provider);
957
1002
  effectiveChainId = nextChainId || 0;
958
1003
  if (this.isAutocaptureEnabled("connect")) {
@@ -1013,7 +1058,7 @@ var FormoAnalytics = /** @class */ (function () {
1013
1058
  this.handleProviderMismatch(provider);
1014
1059
  }
1015
1060
  // Chain changes only matter for connected users
1016
- if (!this.currentAddress) {
1061
+ if (!this._evmAddress) {
1017
1062
  logger_1.logger.info("OnChainChanged: No current address, user appears disconnected");
1018
1063
  return [2 /*return*/, Promise.resolve()];
1019
1064
  }
@@ -1021,19 +1066,19 @@ var FormoAnalytics = /** @class */ (function () {
1021
1066
  if (!this._provider) {
1022
1067
  this._provider = provider;
1023
1068
  }
1024
- this.currentChainId = nextChainId;
1069
+ this.setChainState('evm', { chainId: nextChainId });
1025
1070
  try {
1026
- // 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
1027
1072
  if (this.isAutocaptureEnabled("chain")) {
1028
1073
  return [2 /*return*/, this.chain({
1029
- chainId: this.currentChainId,
1030
- address: this.currentAddress,
1074
+ chainId: nextChainId,
1075
+ address: this._evmAddress,
1031
1076
  })];
1032
1077
  }
1033
1078
  else {
1034
1079
  logger_1.logger.debug("OnChainChanged: Chain event skipped (autocapture.chain: false)", {
1035
- chainId: this.currentChainId,
1036
- address: this.currentAddress,
1080
+ chainId: this._evmChainId,
1081
+ address: this._evmAddress,
1037
1082
  });
1038
1083
  }
1039
1084
  }
@@ -1069,8 +1114,8 @@ var FormoAnalytics = /** @class */ (function () {
1069
1114
  if (this._provider !== provider)
1070
1115
  return [2 /*return*/];
1071
1116
  logger_1.logger.info("OnDisconnect: Wallet disconnect event received, current state:", {
1072
- currentAddress: this.currentAddress,
1073
- currentChainId: this.currentChainId,
1117
+ currentAddress: this._evmAddress,
1118
+ currentChainId: this._evmChainId,
1074
1119
  });
1075
1120
  if (!this.isAutocaptureEnabled("disconnect")) return [3 /*break*/, 5];
1076
1121
  _a.label = 1;
@@ -1078,8 +1123,8 @@ var FormoAnalytics = /** @class */ (function () {
1078
1123
  _a.trys.push([1, 3, , 4]);
1079
1124
  // Pass current state explicitly to ensure we have the data for the disconnect event
1080
1125
  return [4 /*yield*/, this.disconnect({
1081
- chainId: this.currentChainId,
1082
- address: this.currentAddress,
1126
+ chainId: this._evmChainId,
1127
+ address: this._evmAddress,
1083
1128
  })];
1084
1129
  case 2:
1085
1130
  // Pass current state explicitly to ensure we have the data for the disconnect event
@@ -1093,9 +1138,7 @@ var FormoAnalytics = /** @class */ (function () {
1093
1138
  case 5:
1094
1139
  logger_1.logger.debug("OnDisconnect: Disconnect event skipped (autocapture.disconnect: false)");
1095
1140
  // Still clear state even if not tracking the event
1096
- this.currentAddress = undefined;
1097
- this.currentChainId = undefined;
1098
- this.clearActiveProvider();
1141
+ this.clearChainState('evm');
1099
1142
  _a.label = 6;
1100
1143
  case 6: return [2 /*return*/];
1101
1144
  }
@@ -1121,7 +1164,7 @@ var FormoAnalytics = /** @class */ (function () {
1121
1164
  case 2:
1122
1165
  address = _a.sent();
1123
1166
  if (chainId && address) {
1124
- wasDisconnected = !this.currentAddress;
1167
+ wasDisconnected = !this._evmAddress;
1125
1168
  // Set provider if none exists
1126
1169
  if (!this._provider) {
1127
1170
  this._provider = provider;
@@ -1130,12 +1173,13 @@ var FormoAnalytics = /** @class */ (function () {
1130
1173
  // CRITICAL: Always update state from active provider regardless of tracking config
1131
1174
  // This ensures disconnect events will have valid address/chainId values
1132
1175
  if (isActiveProvider) {
1133
- this.currentChainId = chainId;
1134
- this.currentAddress =
1135
- this.validateAndChecksumAddress(address) || undefined;
1176
+ this.setChainState('evm', {
1177
+ chainId: chainId,
1178
+ address: (0, address_1.validateAndChecksumAddress)(address) || undefined,
1179
+ });
1136
1180
  }
1137
1181
  // Conditionally emit connect event based on tracking configuration
1138
- if (isActiveProvider && this.currentAddress) {
1182
+ if (isActiveProvider && this._evmAddress) {
1139
1183
  providerInfo = this.getProviderInfo(provider);
1140
1184
  effectiveChainId = chainId || 0;
1141
1185
  if (this.isAutocaptureEnabled("connect")) {
@@ -1218,7 +1262,7 @@ var FormoAnalytics = /** @class */ (function () {
1218
1262
  logger_1.logger.debug("Signature event skipped (autocapture.signature: false)", { method: method });
1219
1263
  return [2 /*return*/, request({ method: method, params: params })];
1220
1264
  }
1221
- _c = this.currentChainId;
1265
+ _c = this._evmChainId;
1222
1266
  if (_c) return [3 /*break*/, 2];
1223
1267
  return [4 /*yield*/, this.getCurrentChainId(provider)];
1224
1268
  case 1:
@@ -1715,14 +1759,37 @@ var FormoAnalytics = /** @class */ (function () {
1715
1759
  enumerable: false,
1716
1760
  configurable: true
1717
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
+ });
1718
1784
  FormoAnalytics.prototype.getAddress = function (provider) {
1719
1785
  return __awaiter(this, void 0, void 0, function () {
1720
1786
  var p, accounts, err_3, code;
1721
1787
  return __generator(this, function (_a) {
1722
1788
  switch (_a.label) {
1723
1789
  case 0:
1724
- if (this.currentAddress)
1725
- 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];
1726
1793
  p = provider || this.provider;
1727
1794
  if (!p) {
1728
1795
  logger_1.logger.info("The provider is not set");
@@ -1735,7 +1802,7 @@ var FormoAnalytics = /** @class */ (function () {
1735
1802
  case 2:
1736
1803
  accounts = _a.sent();
1737
1804
  if (accounts && accounts.length > 0) {
1738
- return [2 /*return*/, this.validateAndChecksumAddress(accounts[0]) || null];
1805
+ return [2 /*return*/, (0, address_1.validateAndChecksumAddress)(accounts[0]) || null];
1739
1806
  }
1740
1807
  return [3 /*break*/, 4];
1741
1808
  case 3:
@@ -1753,7 +1820,6 @@ var FormoAnalytics = /** @class */ (function () {
1753
1820
  FormoAnalytics.prototype.getAccounts = function (provider) {
1754
1821
  return __awaiter(this, void 0, void 0, function () {
1755
1822
  var p, res, err_4, code;
1756
- var _this = this;
1757
1823
  return __generator(this, function (_a) {
1758
1824
  switch (_a.label) {
1759
1825
  case 0:
@@ -1769,7 +1835,7 @@ var FormoAnalytics = /** @class */ (function () {
1769
1835
  if (!res || res.length === 0)
1770
1836
  return [2 /*return*/, null];
1771
1837
  return [2 /*return*/, res
1772
- .map(function (e) { return _this.validateAndChecksumAddress(e); })
1838
+ .map(function (e) { return (0, address_1.validateAndChecksumAddress)(e); })
1773
1839
  .filter(function (e) { return e !== undefined; })];
1774
1840
  case 3:
1775
1841
  err_4 = _a.sent();
@@ -1821,12 +1887,12 @@ var FormoAnalytics = /** @class */ (function () {
1821
1887
  var rawAddress = method === "personal_sign"
1822
1888
  ? params[1]
1823
1889
  : params[0];
1824
- var validAddress = this.validateAndChecksumAddress(rawAddress);
1890
+ var validAddress = (0, address_1.validateAndChecksumAddress)(rawAddress);
1825
1891
  if (!validAddress) {
1826
1892
  throw new Error("Invalid address in signature payload: ".concat(rawAddress));
1827
1893
  }
1828
1894
  var basePayload = {
1829
- 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,
1830
1896
  address: validAddress,
1831
1897
  };
1832
1898
  if (method === "personal_sign") {
@@ -1837,29 +1903,25 @@ var FormoAnalytics = /** @class */ (function () {
1837
1903
  };
1838
1904
  FormoAnalytics.prototype.buildTransactionEventPayload = function (params, provider) {
1839
1905
  return __awaiter(this, void 0, void 0, function () {
1840
- var _a, data, from, to, value, validAddress, _b;
1906
+ var _a, data, from, to, value, validAddress, builder_codes, _b;
1841
1907
  var _c;
1842
1908
  return __generator(this, function (_d) {
1843
1909
  switch (_d.label) {
1844
1910
  case 0:
1845
1911
  _a = params[0], data = _a.data, from = _a.from, to = _a.to, value = _a.value;
1846
- validAddress = this.validateAndChecksumAddress(from);
1912
+ validAddress = (0, address_1.validateAndChecksumAddress)(from);
1847
1913
  if (!validAddress) {
1848
1914
  throw new Error("Invalid address in transaction payload: ".concat(from));
1849
1915
  }
1916
+ builder_codes = (0, builderCode_1.extractBuilderCodes)(data);
1850
1917
  _c = {};
1851
- _b = this.currentChainId;
1918
+ _b = this._evmChainId;
1852
1919
  if (_b) return [3 /*break*/, 2];
1853
1920
  return [4 /*yield*/, this.getCurrentChainId(provider)];
1854
1921
  case 1:
1855
1922
  _b = (_d.sent());
1856
1923
  _d.label = 2;
1857
- case 2: return [2 /*return*/, (_c.chainId = _b,
1858
- _c.data = data,
1859
- _c.address = validAddress,
1860
- _c.to = to,
1861
- _c.value = value,
1862
- _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 })])];
1863
1925
  }
1864
1926
  });
1865
1927
  });
@@ -1999,19 +2061,84 @@ var FormoAnalytics = /** @class */ (function () {
1999
2061
  // If this is a different provider, allow the switch
2000
2062
  if (this._provider) {
2001
2063
  // Clear any provider-specific state when switching
2002
- this.currentChainId = undefined;
2003
- this.currentAddress = undefined;
2064
+ this.setChainState('evm', { address: undefined, chainId: undefined, provider: provider });
2004
2065
  }
2005
- 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();
2006
2101
  };
2007
2102
  /**
2008
- * Helper method to validate and checksum an address
2009
- * @param address The address to validate and checksum
2010
- * @returns The checksummed address or undefined if invalid
2103
+ * Clear per-chain state for a given namespace (or chainId) and sync derived state.
2011
2104
  */
2012
- FormoAnalytics.prototype.validateAndChecksumAddress = function (address) {
2013
- var validAddress = (0, address_1.getValidAddress)(address);
2014
- 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;
2015
2142
  };
2016
2143
  /**
2017
2144
  * Helper method to clear the active provider state