@formo/analytics 1.27.0 → 1.28.1

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