@formo/analytics 1.28.4 → 1.28.6

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