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