@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
@@ -0,0 +1,972 @@
1
+ /**
2
+ * SolanaAdapter
3
+ *
4
+ * Handles wallet event tracking by hooking into Solana Wallet Adapter events.
5
+ * This provides integration with the @solana/wallet-adapter ecosystem.
6
+ *
7
+ * @see https://github.com/anza-xyz/wallet-adapter
8
+ */
9
+ var __assign = (this && this.__assign) || function () {
10
+ __assign = Object.assign || function(t) {
11
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
12
+ s = arguments[i];
13
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
14
+ t[p] = s[p];
15
+ }
16
+ return t;
17
+ };
18
+ return __assign.apply(this, arguments);
19
+ };
20
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
21
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
22
+ return new (P || (P = Promise))(function (resolve, reject) {
23
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
24
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
25
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
26
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
27
+ });
28
+ };
29
+ var __generator = (this && this.__generator) || function (thisArg, body) {
30
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
31
+ return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
32
+ function verb(n) { return function (v) { return step([n, v]); }; }
33
+ function step(op) {
34
+ if (f) throw new TypeError("Generator is already executing.");
35
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
36
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
37
+ if (y = 0, t) op = [op[0] & 2, t.value];
38
+ switch (op[0]) {
39
+ case 0: case 1: t = op; break;
40
+ case 4: _.label++; return { value: op[1], done: false };
41
+ case 5: _.label++; y = op[1]; op = [0]; continue;
42
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
43
+ default:
44
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
45
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
46
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
47
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
48
+ if (t[2]) _.ops.pop();
49
+ _.trys.pop(); continue;
50
+ }
51
+ op = body.call(thisArg, _);
52
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
53
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
54
+ }
55
+ };
56
+ import { SignatureStatus, TransactionStatus } from "../types/events";
57
+ import { logger } from "../logger";
58
+ import { SOLANA_CHAIN_IDS, isSolanaWalletContext, isSolanaAdapter, } from "./types";
59
+ import { isBlockedSolanaAddress, publicKeyToAddress, } from "./address";
60
+ /**
61
+ * Clean up old entries from a Set to prevent memory leaks.
62
+ */
63
+ function cleanupOldEntries(set, maxSize, removeCount) {
64
+ if (maxSize === void 0) { maxSize = 1000; }
65
+ if (removeCount === void 0) { removeCount = 500; }
66
+ if (set.size > maxSize) {
67
+ var entries = Array.from(set);
68
+ for (var i = 0; i < removeCount && i < entries.length; i++) {
69
+ set.delete(entries[i]);
70
+ }
71
+ }
72
+ }
73
+ /**
74
+ * Convert a Uint8Array to a hex string (browser-compatible alternative to Buffer.from().toString('hex'))
75
+ */
76
+ function uint8ArrayToHex(bytes) {
77
+ return Array.from(bytes)
78
+ .map(function (b) { return b.toString(16).padStart(2, "0"); })
79
+ .join("");
80
+ }
81
+ /**
82
+ * Safely decode a Uint8Array message to string.
83
+ * Falls back to hex representation if TextDecoder is unavailable (some Node environments).
84
+ */
85
+ function safeDecodeMessage(message) {
86
+ try {
87
+ if (typeof TextDecoder !== "undefined") {
88
+ return new TextDecoder().decode(message);
89
+ }
90
+ // Fallback for Node environments without TextDecoder global
91
+ if (typeof Buffer !== "undefined") {
92
+ return Buffer.from(message).toString("utf-8");
93
+ }
94
+ // Last resort: hex representation
95
+ return "0x".concat(uint8ArrayToHex(message));
96
+ }
97
+ catch (_a) {
98
+ return "0x".concat(uint8ArrayToHex(message));
99
+ }
100
+ }
101
+ var SolanaAdapter = /** @class */ (function () {
102
+ function SolanaAdapter(formoAnalytics, options) {
103
+ this.wallet = null;
104
+ this.connection = null;
105
+ this.unsubscribers = [];
106
+ this.connectionState = {
107
+ isProcessing: false,
108
+ };
109
+ /**
110
+ * Track processed signatures to prevent duplicate event emissions
111
+ */
112
+ this.processedSignatures = new Set();
113
+ /**
114
+ * Store pending transaction details for confirmation tracking
115
+ * Key: transaction signature, Value: transaction details
116
+ */
117
+ this.pendingTransactions = new Map();
118
+ /**
119
+ * Track active polling timeout IDs for cleanup
120
+ */
121
+ this.pollingTimeouts = new Set();
122
+ /**
123
+ * Flag to prevent new polls after cleanup is initiated
124
+ */
125
+ this.isCleanedUp = false;
126
+ this.formo = formoAnalytics;
127
+ this.wallet = options.wallet || null;
128
+ this.connection = options.connection || null;
129
+ this.cluster = options.cluster || "mainnet-beta";
130
+ this.chainId = SOLANA_CHAIN_IDS[this.cluster];
131
+ logger.info("SolanaAdapter: Initializing Solana integration", {
132
+ cluster: this.cluster,
133
+ chainId: this.chainId,
134
+ hasWallet: !!this.wallet,
135
+ hasConnection: !!this.connection,
136
+ });
137
+ if (this.wallet) {
138
+ this.setupWalletListeners();
139
+ }
140
+ }
141
+ /**
142
+ * Restore original methods on the wrapped adapter
143
+ */
144
+ SolanaAdapter.prototype.restoreOriginalMethods = function () {
145
+ // Restore adapter methods
146
+ if (this.wrappedAdapter) {
147
+ if (this.originalAdapterSendTransaction) {
148
+ this.wrappedAdapter.sendTransaction = this.originalAdapterSendTransaction;
149
+ }
150
+ if (this.originalAdapterSignMessage) {
151
+ this.wrappedAdapter.signMessage = this.originalAdapterSignMessage;
152
+ }
153
+ if (this.originalAdapterSignTransaction) {
154
+ this.wrappedAdapter.signTransaction = this.originalAdapterSignTransaction;
155
+ }
156
+ this.wrappedAdapter = undefined;
157
+ }
158
+ // Clear original and bound wrapper references
159
+ this.originalAdapterSendTransaction = undefined;
160
+ this.originalAdapterSignMessage = undefined;
161
+ this.originalAdapterSignTransaction = undefined;
162
+ this.boundWrappedSendTransaction = undefined;
163
+ this.boundWrappedSignMessage = undefined;
164
+ this.boundWrappedSignTransaction = undefined;
165
+ };
166
+ /**
167
+ * Update the wallet instance (useful for React context updates)
168
+ */
169
+ SolanaAdapter.prototype.setWallet = function (wallet) {
170
+ // For context-based wallets, if the inner adapter hasn't changed,
171
+ // just update the context reference without tearing down wrapping.
172
+ // This prevents React re-renders from clearing our method wraps.
173
+ if (wallet &&
174
+ isSolanaWalletContext(wallet) &&
175
+ this.wallet &&
176
+ isSolanaWalletContext(this.wallet)) {
177
+ var newAdapter = this.getAdapterFromContext(wallet);
178
+ if (newAdapter && newAdapter === this.wrappedAdapter) {
179
+ // Same adapter, just update the context reference
180
+ this.wallet = wallet;
181
+ return;
182
+ }
183
+ }
184
+ // For raw adapters, skip teardown if it's the same object
185
+ if (wallet && wallet === this.wrappedAdapter) {
186
+ return;
187
+ }
188
+ // Restore original methods on previous wallet before cleaning up
189
+ this.restoreOriginalMethods();
190
+ // Clear stale connection state to prevent the old adapter's
191
+ // address/chainId from leaking into disconnect events
192
+ this.connectionState.lastAddress = undefined;
193
+ this.connectionState.lastChainId = undefined;
194
+ // Clean up previous wallet listeners
195
+ this.cleanupWalletListeners();
196
+ this.wallet = wallet;
197
+ if (this.wallet) {
198
+ // Reset cleanup flag when setting a new wallet to enable polling
199
+ this.isCleanedUp = false;
200
+ this.setupWalletListeners();
201
+ }
202
+ };
203
+ /**
204
+ * Check if the wallet adapter has changed (for context-based wallets) and rebind if needed.
205
+ * Call this in React effects when you know the wallet context may have changed but the
206
+ * context object reference stayed the same (e.g., user switched wallets in the wallet selector).
207
+ *
208
+ * This ensures connect/disconnect events from the new wallet are properly tracked without
209
+ * waiting for the next transaction or signature call.
210
+ *
211
+ * @example
212
+ * ```tsx
213
+ * const wallet = useWallet();
214
+ * useEffect(() => {
215
+ * formo.solana.syncWalletState();
216
+ * }, [wallet.wallet]); // Trigger when inner wallet changes
217
+ * ```
218
+ */
219
+ SolanaAdapter.prototype.syncWalletState = function () {
220
+ this.checkAndRebindContextAdapter();
221
+ };
222
+ /**
223
+ * Update the connection instance
224
+ */
225
+ SolanaAdapter.prototype.setConnection = function (connection) {
226
+ this.connection = connection;
227
+ };
228
+ /**
229
+ * Update the cluster/network
230
+ */
231
+ SolanaAdapter.prototype.setCluster = function (cluster) {
232
+ var previousCluster = this.cluster;
233
+ this.cluster = cluster;
234
+ this.chainId = SOLANA_CHAIN_IDS[cluster];
235
+ // Update connectionState and emit chain event if connected and cluster changed
236
+ if (previousCluster !== cluster && this.connectionState.lastAddress) {
237
+ // Always update connectionState to keep lastChainId in sync for future disconnect events
238
+ this.connectionState.lastChainId = this.chainId;
239
+ if (this.formo.isAutocaptureEnabled("chain")) {
240
+ this.formo.chain({
241
+ chainId: this.chainId,
242
+ address: this.connectionState.lastAddress,
243
+ }).catch(function (error) {
244
+ logger.error("SolanaAdapter: Error emitting chain event", error);
245
+ });
246
+ }
247
+ }
248
+ };
249
+ /**
250
+ * Get the current chain ID
251
+ */
252
+ SolanaAdapter.prototype.getChainId = function () {
253
+ return this.chainId;
254
+ };
255
+ /**
256
+ * Set up listeners for wallet events
257
+ */
258
+ SolanaAdapter.prototype.setupWalletListeners = function () {
259
+ if (!this.wallet) {
260
+ return;
261
+ }
262
+ logger.info("SolanaAdapter: Setting up wallet listeners");
263
+ // Handle both WalletContext (from useWallet) and direct WalletAdapter
264
+ if (isSolanaWalletContext(this.wallet)) {
265
+ this.setupContextListeners(this.wallet);
266
+ }
267
+ else if (isSolanaAdapter(this.wallet)) {
268
+ this.setupAdapterListeners(this.wallet);
269
+ }
270
+ // Check if already connected
271
+ this.checkInitialConnection().catch(function (error) {
272
+ logger.error("SolanaAdapter: Error checking initial connection", error);
273
+ });
274
+ logger.info("SolanaAdapter: Wallet listeners set up successfully");
275
+ };
276
+ /**
277
+ * Set up listeners for a wallet context (useWallet)
278
+ */
279
+ SolanaAdapter.prototype.setupContextListeners = function (context) {
280
+ // The wallet-adapter-react useWallet() returns wallet as { adapter, readyState },
281
+ // so we need to extract the actual adapter which has .on()/.off() methods.
282
+ //
283
+ // IMPORTANT: We wrap methods on the adapter (not the context) because
284
+ // useWallet() returns a new object reference on each render. Components
285
+ // that call useWallet() independently get their own sendTransaction/signMessage
286
+ // callbacks that delegate to adapter.sendTransaction/adapter.signMessage.
287
+ // Wrapping the context object only mutates the FormoProvider's reference,
288
+ // not what other components receive from useWallet().
289
+ var adapter = this.getAdapterFromContext(context);
290
+ if (adapter) {
291
+ this.setupAdapterEventListenersOnly(adapter);
292
+ // Wrap adapter methods so all components using useWallet() are tracked
293
+ this.wrapAdapterMethods(adapter);
294
+ }
295
+ };
296
+ /**
297
+ * Check if the adapter inside a wallet context has changed (e.g., user switched wallets).
298
+ * If so, rebind event listeners and rewrap methods on the new adapter.
299
+ * This handles the case where context.wallet changes but the context object reference stays the same.
300
+ */
301
+ SolanaAdapter.prototype.checkAndRebindContextAdapter = function () {
302
+ if (!this.wallet || !isSolanaWalletContext(this.wallet)) {
303
+ return;
304
+ }
305
+ var currentAdapter = this.getAdapterFromContext(this.wallet);
306
+ // If adapter changed, rebind listeners and rewrap methods
307
+ if (currentAdapter !== this.currentBoundAdapter) {
308
+ logger.info("SolanaAdapter: Detected wallet adapter change, rebinding");
309
+ // Restore methods on old adapter and clean up listeners
310
+ this.restoreOriginalMethods();
311
+ this.cleanupAdapterListenersOnly();
312
+ // Set up on new adapter
313
+ if (currentAdapter) {
314
+ this.setupAdapterEventListenersOnly(currentAdapter);
315
+ this.wrapAdapterMethods(currentAdapter);
316
+ // Check if new adapter is already connected
317
+ this.checkInitialConnection().catch(function (error) {
318
+ logger.error("SolanaAdapter: Error checking initial connection after adapter change", error);
319
+ });
320
+ }
321
+ else {
322
+ // No adapter means disconnected
323
+ this.currentBoundAdapter = undefined;
324
+ if (this.connectionState.lastAddress) {
325
+ this.handleDisconnect();
326
+ }
327
+ }
328
+ }
329
+ };
330
+ /**
331
+ * Clean up only adapter event listeners (not the full cleanup)
332
+ */
333
+ SolanaAdapter.prototype.cleanupAdapterListenersOnly = function () {
334
+ for (var _i = 0, _a = this.unsubscribers; _i < _a.length; _i++) {
335
+ var unsubscribe = _a[_i];
336
+ try {
337
+ unsubscribe();
338
+ }
339
+ catch (error) {
340
+ logger.error("SolanaAdapter: Error cleaning up adapter listener", error);
341
+ }
342
+ }
343
+ this.unsubscribers = [];
344
+ this.currentBoundAdapter = undefined;
345
+ };
346
+ /**
347
+ * Register a listener on an adapter and track its unsubscriber
348
+ */
349
+ SolanaAdapter.prototype.registerAdapterListener = function (adapter, event, handler) {
350
+ // Use 'any' cast to handle the overloaded on/off signatures
351
+ adapter.on(event, handler);
352
+ this.unsubscribers.push(function () { return adapter.off(event, handler); });
353
+ };
354
+ /**
355
+ * Set up event listeners on an adapter (connect/disconnect events)
356
+ */
357
+ SolanaAdapter.prototype.setupAdapterEventListenersOnly = function (adapter) {
358
+ var _this = this;
359
+ this.currentBoundAdapter = adapter;
360
+ this.registerAdapterListener(adapter, "connect", function (publicKey) {
361
+ return _this.handleConnect(publicKey);
362
+ });
363
+ this.registerAdapterListener(adapter, "disconnect", function () {
364
+ return _this.handleDisconnect();
365
+ });
366
+ this.registerAdapterListener(adapter, "error", function (error) {
367
+ return logger.error("SolanaAdapter: Wallet error", error);
368
+ });
369
+ };
370
+ /**
371
+ * Set up listeners for a direct wallet adapter
372
+ */
373
+ SolanaAdapter.prototype.setupAdapterListeners = function (adapter) {
374
+ this.setupAdapterEventListenersOnly(adapter);
375
+ this.wrapAdapterMethods(adapter);
376
+ };
377
+ /**
378
+ * Wrap wallet adapter methods for transaction/signature tracking
379
+ */
380
+ SolanaAdapter.prototype.wrapAdapterMethods = function (adapter) {
381
+ // If we already wrapped this adapter, check if our wraps are still in place.
382
+ // StandardWalletAdapter._reset() overwrites signMessage/signTransaction
383
+ // on every connect/disconnect/feature-change, so we need to re-wrap those methods.
384
+ if (this.wrappedAdapter === adapter) {
385
+ this.rewrapOverwrittenMethods(adapter);
386
+ return;
387
+ }
388
+ // Store reference to adapter for cleanup
389
+ this.wrappedAdapter = adapter;
390
+ // Wrap sendTransaction
391
+ if (adapter.sendTransaction) {
392
+ this.originalAdapterSendTransaction = adapter.sendTransaction.bind(adapter);
393
+ this.boundWrappedSendTransaction = this.wrappedSendTransaction.bind(this);
394
+ adapter.sendTransaction = this.boundWrappedSendTransaction;
395
+ }
396
+ // Wrap signMessage
397
+ if (adapter.signMessage) {
398
+ this.originalAdapterSignMessage = adapter.signMessage.bind(adapter);
399
+ this.boundWrappedSignMessage = this.wrappedSignMessage.bind(this);
400
+ adapter.signMessage = this.boundWrappedSignMessage;
401
+ }
402
+ // Wrap signTransaction
403
+ if (adapter.signTransaction) {
404
+ this.originalAdapterSignTransaction = adapter.signTransaction.bind(adapter);
405
+ this.boundWrappedSignTransaction = this.wrappedSignTransaction.bind(this);
406
+ adapter.signTransaction = this.boundWrappedSignTransaction;
407
+ }
408
+ };
409
+ /**
410
+ * Re-wrap methods that were overwritten by external code.
411
+ *
412
+ * StandardWalletAdapter._reset() overwrites signMessage and signTransaction
413
+ * as own properties on every connect/disconnect/
414
+ * feature-change event. This method detects which wraps were overwritten
415
+ * and re-applies them, capturing the new original methods.
416
+ */
417
+ SolanaAdapter.prototype.rewrapOverwrittenMethods = function (adapter) {
418
+ var rewrapped = false;
419
+ // signMessage
420
+ if (adapter.signMessage && adapter.signMessage !== this.boundWrappedSignMessage) {
421
+ this.originalAdapterSignMessage = adapter.signMessage.bind(adapter);
422
+ if (!this.boundWrappedSignMessage) {
423
+ this.boundWrappedSignMessage = this.wrappedSignMessage.bind(this);
424
+ }
425
+ adapter.signMessage = this.boundWrappedSignMessage;
426
+ rewrapped = true;
427
+ }
428
+ else if (!adapter.signMessage && this.boundWrappedSignMessage) {
429
+ this.originalAdapterSignMessage = undefined;
430
+ }
431
+ // signTransaction
432
+ if (adapter.signTransaction && adapter.signTransaction !== this.boundWrappedSignTransaction) {
433
+ this.originalAdapterSignTransaction = adapter.signTransaction.bind(adapter);
434
+ if (!this.boundWrappedSignTransaction) {
435
+ this.boundWrappedSignTransaction = this.wrappedSignTransaction.bind(this);
436
+ }
437
+ adapter.signTransaction = this.boundWrappedSignTransaction;
438
+ rewrapped = true;
439
+ }
440
+ else if (!adapter.signTransaction && this.boundWrappedSignTransaction) {
441
+ this.originalAdapterSignTransaction = undefined;
442
+ }
443
+ // sendTransaction — unlikely to be overwritten but check for completeness
444
+ if (adapter.sendTransaction && adapter.sendTransaction !== this.boundWrappedSendTransaction) {
445
+ this.originalAdapterSendTransaction = adapter.sendTransaction.bind(adapter);
446
+ if (!this.boundWrappedSendTransaction) {
447
+ this.boundWrappedSendTransaction = this.wrappedSendTransaction.bind(this);
448
+ }
449
+ adapter.sendTransaction = this.boundWrappedSendTransaction;
450
+ rewrapped = true;
451
+ }
452
+ if (rewrapped) {
453
+ logger.debug("SolanaAdapter: Re-wrapped overwritten adapter methods");
454
+ }
455
+ };
456
+ /**
457
+ * Wrapped sendTransaction method for direct adapter
458
+ */
459
+ SolanaAdapter.prototype.wrappedSendTransaction = function (transaction, connection, options) {
460
+ return __awaiter(this, void 0, void 0, function () {
461
+ var chainId, address, signature, error_1;
462
+ return __generator(this, function (_a) {
463
+ switch (_a.label) {
464
+ case 0:
465
+ this.checkAndRebindContextAdapter();
466
+ if (!this.originalAdapterSendTransaction) {
467
+ throw new Error("sendTransaction not available");
468
+ }
469
+ chainId = this.chainId;
470
+ address = this.getCurrentAddress();
471
+ this.emitTransactionEvent(TransactionStatus.STARTED, address, chainId);
472
+ _a.label = 1;
473
+ case 1:
474
+ _a.trys.push([1, 3, , 4]);
475
+ return [4 /*yield*/, this.originalAdapterSendTransaction(transaction, connection, options)];
476
+ case 2:
477
+ signature = _a.sent();
478
+ this.emitTransactionEvent(TransactionStatus.BROADCASTED, address, chainId, signature);
479
+ if (address && this.formo.isAutocaptureEnabled("transaction")) {
480
+ this.pendingTransactions.set(signature, {
481
+ address: address,
482
+ startTime: Date.now(),
483
+ });
484
+ this.pollTransactionConfirmation(signature, address, chainId, connection);
485
+ }
486
+ return [2 /*return*/, signature];
487
+ case 3:
488
+ error_1 = _a.sent();
489
+ this.emitTransactionEvent(TransactionStatus.REJECTED, address, chainId);
490
+ throw error_1;
491
+ case 4: return [2 /*return*/];
492
+ }
493
+ });
494
+ });
495
+ };
496
+ /**
497
+ * Wrapped signMessage method for direct adapter
498
+ */
499
+ SolanaAdapter.prototype.wrappedSignMessage = function (message) {
500
+ return __awaiter(this, void 0, void 0, function () {
501
+ var chainId, address, messageString, signature, signatureHex, error_2;
502
+ return __generator(this, function (_a) {
503
+ switch (_a.label) {
504
+ case 0:
505
+ this.checkAndRebindContextAdapter();
506
+ if (!this.originalAdapterSignMessage) {
507
+ throw new Error("signMessage not available");
508
+ }
509
+ chainId = this.chainId;
510
+ address = this.getCurrentAddress();
511
+ messageString = safeDecodeMessage(message);
512
+ this.emitSignatureEvent(SignatureStatus.REQUESTED, address, chainId, messageString);
513
+ _a.label = 1;
514
+ case 1:
515
+ _a.trys.push([1, 3, , 4]);
516
+ return [4 /*yield*/, this.originalAdapterSignMessage(message)];
517
+ case 2:
518
+ signature = _a.sent();
519
+ signatureHex = uint8ArrayToHex(signature);
520
+ this.emitSignatureEvent(SignatureStatus.CONFIRMED, address, chainId, messageString, signatureHex);
521
+ return [2 /*return*/, signature];
522
+ case 3:
523
+ error_2 = _a.sent();
524
+ this.emitSignatureEvent(SignatureStatus.REJECTED, address, chainId, messageString);
525
+ throw error_2;
526
+ case 4: return [2 /*return*/];
527
+ }
528
+ });
529
+ });
530
+ };
531
+ /**
532
+ * Wrapped signTransaction method for direct adapter
533
+ */
534
+ SolanaAdapter.prototype.wrappedSignTransaction = function (transaction) {
535
+ return __awaiter(this, void 0, void 0, function () {
536
+ var chainId, address, message, signedTx, error_3;
537
+ return __generator(this, function (_a) {
538
+ switch (_a.label) {
539
+ case 0:
540
+ this.checkAndRebindContextAdapter();
541
+ if (!this.originalAdapterSignTransaction) {
542
+ throw new Error("signTransaction not available");
543
+ }
544
+ chainId = this.chainId;
545
+ address = this.getCurrentAddress();
546
+ message = "[Transaction Signature]";
547
+ this.emitSignatureEvent(SignatureStatus.REQUESTED, address, chainId, message);
548
+ _a.label = 1;
549
+ case 1:
550
+ _a.trys.push([1, 3, , 4]);
551
+ return [4 /*yield*/, this.originalAdapterSignTransaction(transaction)];
552
+ case 2:
553
+ signedTx = _a.sent();
554
+ this.emitSignatureEvent(SignatureStatus.CONFIRMED, address, chainId, message);
555
+ return [2 /*return*/, signedTx];
556
+ case 3:
557
+ error_3 = _a.sent();
558
+ this.emitSignatureEvent(SignatureStatus.REJECTED, address, chainId, message);
559
+ throw error_3;
560
+ case 4: return [2 /*return*/];
561
+ }
562
+ });
563
+ });
564
+ };
565
+ /**
566
+ * Check initial connection state
567
+ */
568
+ SolanaAdapter.prototype.checkInitialConnection = function () {
569
+ return __awaiter(this, void 0, void 0, function () {
570
+ var publicKey, address;
571
+ return __generator(this, function (_a) {
572
+ switch (_a.label) {
573
+ case 0:
574
+ publicKey = this.getPublicKey();
575
+ if (!publicKey) return [3 /*break*/, 2];
576
+ address = publicKeyToAddress(publicKey);
577
+ if (!(address && !isBlockedSolanaAddress(address))) return [3 /*break*/, 2];
578
+ // Skip if we already tracked this address to avoid duplicate connect events
579
+ // (e.g., when setWallet is called repeatedly with the same connected wallet)
580
+ if (this.connectionState.lastAddress === address &&
581
+ this.connectionState.lastChainId === this.chainId) {
582
+ logger.debug("SolanaAdapter: Already tracking this address, skipping duplicate connect", { address: address, chainId: this.chainId });
583
+ return [2 /*return*/];
584
+ }
585
+ this.connectionState.lastAddress = address;
586
+ this.connectionState.lastChainId = this.chainId;
587
+ logger.info("SolanaAdapter: Already connected on initialization", {
588
+ address: address,
589
+ chainId: this.chainId,
590
+ });
591
+ if (!this.formo.isAutocaptureEnabled("connect")) return [3 /*break*/, 2];
592
+ return [4 /*yield*/, this.formo.connect({
593
+ chainId: this.chainId,
594
+ address: address,
595
+ }, {
596
+ providerName: this.getWalletName(),
597
+ rdns: this.getWalletRdns(),
598
+ })];
599
+ case 1:
600
+ _a.sent();
601
+ _a.label = 2;
602
+ case 2: return [2 /*return*/];
603
+ }
604
+ });
605
+ });
606
+ };
607
+ /**
608
+ * Handle wallet connect event
609
+ */
610
+ SolanaAdapter.prototype.handleConnect = function (publicKey) {
611
+ return __awaiter(this, void 0, void 0, function () {
612
+ var address, error_4;
613
+ return __generator(this, function (_a) {
614
+ switch (_a.label) {
615
+ case 0:
616
+ if (this.connectionState.isProcessing) {
617
+ logger.debug("SolanaAdapter: Already processing, skipping connect");
618
+ return [2 /*return*/];
619
+ }
620
+ this.connectionState.isProcessing = true;
621
+ _a.label = 1;
622
+ case 1:
623
+ _a.trys.push([1, 4, 5, 6]);
624
+ // Re-wrap methods that may have been overwritten.
625
+ // StandardWalletAdapter._reset() runs before emitting "connect",
626
+ // so signMessage/signTransaction may have been
627
+ // replaced with new own properties by the time we get here.
628
+ if (this.wrappedAdapter) {
629
+ this.rewrapOverwrittenMethods(this.wrappedAdapter);
630
+ }
631
+ address = publicKeyToAddress(publicKey);
632
+ if (!address) {
633
+ logger.warn("SolanaAdapter: Invalid public key on connect");
634
+ return [2 /*return*/];
635
+ }
636
+ if (isBlockedSolanaAddress(address)) {
637
+ logger.debug("SolanaAdapter: Blocked address, skipping connect event");
638
+ return [2 /*return*/];
639
+ }
640
+ logger.info("SolanaAdapter: Wallet connected", {
641
+ address: address,
642
+ chainId: this.chainId,
643
+ walletName: this.getWalletName(),
644
+ });
645
+ this.connectionState.lastAddress = address;
646
+ this.connectionState.lastChainId = this.chainId;
647
+ if (!this.formo.isAutocaptureEnabled("connect")) return [3 /*break*/, 3];
648
+ return [4 /*yield*/, this.formo.connect({
649
+ chainId: this.chainId,
650
+ address: address,
651
+ }, {
652
+ providerName: this.getWalletName(),
653
+ rdns: this.getWalletRdns(),
654
+ })];
655
+ case 2:
656
+ _a.sent();
657
+ _a.label = 3;
658
+ case 3: return [3 /*break*/, 6];
659
+ case 4:
660
+ error_4 = _a.sent();
661
+ logger.error("SolanaAdapter: Error handling connect", error_4);
662
+ return [3 /*break*/, 6];
663
+ case 5:
664
+ this.connectionState.isProcessing = false;
665
+ return [7 /*endfinally*/];
666
+ case 6: return [2 /*return*/];
667
+ }
668
+ });
669
+ });
670
+ };
671
+ /**
672
+ * Handle wallet disconnect event
673
+ */
674
+ SolanaAdapter.prototype.handleDisconnect = function () {
675
+ return __awaiter(this, void 0, void 0, function () {
676
+ var error_5;
677
+ return __generator(this, function (_a) {
678
+ switch (_a.label) {
679
+ case 0:
680
+ if (this.connectionState.isProcessing) {
681
+ logger.debug("SolanaAdapter: Already processing, skipping disconnect");
682
+ return [2 /*return*/];
683
+ }
684
+ // Only emit disconnect if we have a prior tracked connection
685
+ // This prevents emitting events with undefined address/chainId
686
+ if (!this.connectionState.lastAddress) {
687
+ logger.debug("SolanaAdapter: No prior connection tracked, skipping disconnect event");
688
+ return [2 /*return*/];
689
+ }
690
+ this.connectionState.isProcessing = true;
691
+ _a.label = 1;
692
+ case 1:
693
+ _a.trys.push([1, 4, 5, 6]);
694
+ logger.info("SolanaAdapter: Wallet disconnected", {
695
+ address: this.connectionState.lastAddress,
696
+ chainId: this.connectionState.lastChainId,
697
+ });
698
+ if (!this.formo.isAutocaptureEnabled("disconnect")) return [3 /*break*/, 3];
699
+ return [4 /*yield*/, this.formo.disconnect({
700
+ chainId: this.connectionState.lastChainId,
701
+ address: this.connectionState.lastAddress,
702
+ })];
703
+ case 2:
704
+ _a.sent();
705
+ _a.label = 3;
706
+ case 3:
707
+ this.connectionState.lastAddress = undefined;
708
+ this.connectionState.lastChainId = undefined;
709
+ return [3 /*break*/, 6];
710
+ case 4:
711
+ error_5 = _a.sent();
712
+ logger.error("SolanaAdapter: Error handling disconnect", error_5);
713
+ return [3 /*break*/, 6];
714
+ case 5:
715
+ this.connectionState.isProcessing = false;
716
+ return [7 /*endfinally*/];
717
+ case 6: return [2 /*return*/];
718
+ }
719
+ });
720
+ });
721
+ };
722
+ /**
723
+ * Poll for transaction confirmation
724
+ */
725
+ SolanaAdapter.prototype.pollTransactionConfirmation = function (signature_1, address_1, chainId_1, connection_1) {
726
+ return __awaiter(this, arguments, void 0, function (signature, address, chainId, connection, maxAttempts, intervalMs) {
727
+ var conn, attempts, currentTimeoutId, poll;
728
+ var _this = this;
729
+ if (maxAttempts === void 0) { maxAttempts = 30; }
730
+ if (intervalMs === void 0) { intervalMs = 2000; }
731
+ return __generator(this, function (_a) {
732
+ // Don't start polling if already cleaned up
733
+ if (this.isCleanedUp) {
734
+ return [2 /*return*/];
735
+ }
736
+ conn = connection || this.connection;
737
+ // Prefer getSignatureStatuses (standard web3.js API) over getSignatureStatus (custom wrapper)
738
+ if (!conn || (!conn.getSignatureStatuses && !conn.getSignatureStatus)) {
739
+ logger.debug("SolanaAdapter: No connection for confirmation polling");
740
+ // Clean up pendingTransactions entry since we can't poll for confirmation
741
+ this.pendingTransactions.delete(signature);
742
+ return [2 /*return*/];
743
+ }
744
+ attempts = 0;
745
+ currentTimeoutId = null;
746
+ poll = function () { return __awaiter(_this, void 0, void 0, function () {
747
+ var status_1, result, result, signatureKey, isTerminalState, error_6;
748
+ return __generator(this, function (_a) {
749
+ switch (_a.label) {
750
+ case 0:
751
+ // Remove the current timeout ID from tracking since it has fired
752
+ if (currentTimeoutId) {
753
+ this.pollingTimeouts.delete(currentTimeoutId);
754
+ currentTimeoutId = null;
755
+ }
756
+ // Stop polling if cleaned up
757
+ if (this.isCleanedUp) {
758
+ this.pendingTransactions.delete(signature);
759
+ return [2 /*return*/];
760
+ }
761
+ _a.label = 1;
762
+ case 1:
763
+ _a.trys.push([1, 6, , 7]);
764
+ status_1 = null;
765
+ if (!conn.getSignatureStatuses) return [3 /*break*/, 3];
766
+ return [4 /*yield*/, conn.getSignatureStatuses([signature])];
767
+ case 2:
768
+ result = _a.sent();
769
+ status_1 = result.value[0];
770
+ return [3 /*break*/, 5];
771
+ case 3:
772
+ if (!conn.getSignatureStatus) return [3 /*break*/, 5];
773
+ return [4 /*yield*/, conn.getSignatureStatus(signature)];
774
+ case 4:
775
+ result = _a.sent();
776
+ status_1 = result.value;
777
+ _a.label = 5;
778
+ case 5:
779
+ if (status_1) {
780
+ signatureKey = "".concat(signature, ":").concat(status_1.confirmationStatus);
781
+ isTerminalState = !!status_1.err ||
782
+ status_1.confirmationStatus === "confirmed" ||
783
+ status_1.confirmationStatus === "finalized";
784
+ // Check for duplicate processing of terminal states only
785
+ if (isTerminalState && this.processedSignatures.has(signatureKey)) {
786
+ return [2 /*return*/];
787
+ }
788
+ if (isTerminalState) {
789
+ this.processedSignatures.add(signatureKey);
790
+ }
791
+ if (status_1.err) {
792
+ // Transaction failed
793
+ logger.info("SolanaAdapter: Transaction reverted", {
794
+ signature: signature,
795
+ error: status_1.err,
796
+ });
797
+ this.formo.transaction({
798
+ status: TransactionStatus.REVERTED,
799
+ chainId: chainId,
800
+ address: address,
801
+ transactionHash: signature,
802
+ });
803
+ this.pendingTransactions.delete(signature);
804
+ return [2 /*return*/];
805
+ }
806
+ if (status_1.confirmationStatus === "confirmed" ||
807
+ status_1.confirmationStatus === "finalized") {
808
+ // Transaction confirmed
809
+ logger.info("SolanaAdapter: Transaction confirmed", {
810
+ signature: signature,
811
+ confirmationStatus: status_1.confirmationStatus,
812
+ });
813
+ this.formo.transaction({
814
+ status: TransactionStatus.CONFIRMED,
815
+ chainId: chainId,
816
+ address: address,
817
+ transactionHash: signature,
818
+ });
819
+ this.pendingTransactions.delete(signature);
820
+ return [2 /*return*/];
821
+ }
822
+ }
823
+ return [3 /*break*/, 7];
824
+ case 6:
825
+ error_6 = _a.sent();
826
+ logger.error("SolanaAdapter: Error polling transaction status", error_6);
827
+ return [3 /*break*/, 7];
828
+ case 7:
829
+ attempts++;
830
+ if (attempts < maxAttempts && !this.isCleanedUp) {
831
+ currentTimeoutId = setTimeout(poll, intervalMs);
832
+ this.pollingTimeouts.add(currentTimeoutId);
833
+ }
834
+ else {
835
+ // Cleanup after max attempts
836
+ this.pendingTransactions.delete(signature);
837
+ }
838
+ return [2 /*return*/];
839
+ }
840
+ });
841
+ }); };
842
+ // Start polling
843
+ currentTimeoutId = setTimeout(poll, intervalMs);
844
+ this.pollingTimeouts.add(currentTimeoutId);
845
+ // Clean up old processed signatures
846
+ cleanupOldEntries(this.processedSignatures);
847
+ return [2 /*return*/];
848
+ });
849
+ });
850
+ };
851
+ /**
852
+ * Get current wallet public key
853
+ */
854
+ SolanaAdapter.prototype.getPublicKey = function () {
855
+ var _a, _b;
856
+ return (_b = (_a = this.wallet) === null || _a === void 0 ? void 0 : _a.publicKey) !== null && _b !== void 0 ? _b : null;
857
+ };
858
+ /**
859
+ * Get current address
860
+ */
861
+ SolanaAdapter.prototype.getCurrentAddress = function () {
862
+ // First check tracking state
863
+ if (this.connectionState.lastAddress) {
864
+ return this.connectionState.lastAddress;
865
+ }
866
+ // Then check wallet, filtering out blocked addresses (system programs, etc.)
867
+ var publicKey = this.getPublicKey();
868
+ var address = publicKey ? publicKeyToAddress(publicKey) : null;
869
+ if (address && isBlockedSolanaAddress(address)) {
870
+ return null;
871
+ }
872
+ return address;
873
+ };
874
+ // ============================================================
875
+ // Event Emission Helpers
876
+ // ============================================================
877
+ /**
878
+ * Emit a transaction event if address is valid and autocapture is enabled
879
+ */
880
+ SolanaAdapter.prototype.emitTransactionEvent = function (status, address, chainId, transactionHash) {
881
+ if (address && this.formo.isAutocaptureEnabled("transaction")) {
882
+ this.formo.transaction(__assign({ status: status, chainId: chainId, address: address }, (transactionHash && { transactionHash: transactionHash })));
883
+ }
884
+ };
885
+ /**
886
+ * Emit a signature event if address is valid and autocapture is enabled
887
+ */
888
+ SolanaAdapter.prototype.emitSignatureEvent = function (status, address, chainId, message, signatureHash) {
889
+ if (address && this.formo.isAutocaptureEnabled("signature")) {
890
+ this.formo.signature(__assign({ status: status, chainId: chainId, address: address, message: message }, (signatureHash && { signatureHash: signatureHash })));
891
+ }
892
+ };
893
+ /**
894
+ * Extract the actual adapter (with .on/.off) from a wallet context.
895
+ * In @solana/wallet-adapter-react, context.wallet is { adapter, readyState },
896
+ * not a direct adapter.
897
+ */
898
+ SolanaAdapter.prototype.getAdapterFromContext = function (context) {
899
+ var wallet = context.wallet;
900
+ if (!wallet)
901
+ return null;
902
+ // wallet-adapter-react: wallet is { adapter, readyState }
903
+ if (wallet.adapter && typeof wallet.adapter.on === "function") {
904
+ return wallet.adapter;
905
+ }
906
+ return null;
907
+ };
908
+ /**
909
+ * Get wallet name
910
+ */
911
+ SolanaAdapter.prototype.getWalletName = function () {
912
+ if (!this.wallet) {
913
+ return "Unknown Solana Wallet";
914
+ }
915
+ if (isSolanaWalletContext(this.wallet)) {
916
+ var adapter = this.getAdapterFromContext(this.wallet);
917
+ return (adapter === null || adapter === void 0 ? void 0 : adapter.name) || "Unknown Solana Wallet";
918
+ }
919
+ return this.wallet.name;
920
+ };
921
+ /**
922
+ * Get wallet RDNS (reverse domain name)
923
+ * For Solana wallets, we construct an RDNS-like identifier
924
+ */
925
+ SolanaAdapter.prototype.getWalletRdns = function () {
926
+ var name = this.getWalletName().toLowerCase().replace(/\s+/g, "");
927
+ return "sol.wallet.".concat(name);
928
+ };
929
+ /**
930
+ * Clean up wallet listeners
931
+ */
932
+ SolanaAdapter.prototype.cleanupWalletListeners = function () {
933
+ for (var _i = 0, _a = this.unsubscribers; _i < _a.length; _i++) {
934
+ var unsubscribe = _a[_i];
935
+ try {
936
+ unsubscribe();
937
+ }
938
+ catch (error) {
939
+ logger.error("SolanaAdapter: Error during listener cleanup", error);
940
+ }
941
+ }
942
+ this.unsubscribers = [];
943
+ this.currentBoundAdapter = undefined;
944
+ };
945
+ /**
946
+ * Clean up all resources
947
+ */
948
+ SolanaAdapter.prototype.cleanup = function () {
949
+ logger.debug("SolanaAdapter: Cleaning up");
950
+ // Set cleanup flag to stop any ongoing polls
951
+ this.isCleanedUp = true;
952
+ // Cancel all active polling timeouts
953
+ Array.from(this.pollingTimeouts).forEach(function (timeoutId) {
954
+ clearTimeout(timeoutId);
955
+ });
956
+ this.pollingTimeouts.clear();
957
+ this.cleanupWalletListeners();
958
+ this.processedSignatures.clear();
959
+ this.pendingTransactions.clear();
960
+ // Clear connection state to prevent stale data
961
+ this.connectionState.lastAddress = undefined;
962
+ this.connectionState.lastChainId = undefined;
963
+ // Restore original methods on wrapped adapter
964
+ this.restoreOriginalMethods();
965
+ this.wallet = null;
966
+ this.connection = null;
967
+ logger.debug("SolanaAdapter: Cleanup complete");
968
+ };
969
+ return SolanaAdapter;
970
+ }());
971
+ export { SolanaAdapter };
972
+ //# sourceMappingURL=SolanaAdapter.js.map