@formo/analytics 1.35.0 → 1.35.2
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 +134 -0
- package/dist/cjs/src/FormoAnalytics.js +647 -178
- package/dist/cjs/src/constants/base.d.ts +0 -1
- package/dist/cjs/src/constants/base.js +1 -3
- package/dist/cjs/src/event/EventFactory.js +1 -1
- package/dist/cjs/src/utils/mergeDeepRight.d.ts +22 -0
- package/dist/cjs/src/utils/mergeDeepRight.js +54 -0
- package/dist/cjs/src/version.d.ts +1 -1
- package/dist/cjs/src/version.js +1 -1
- package/dist/cjs/src/wagmi/WagmiEventHandler.d.ts +193 -1
- package/dist/cjs/src/wagmi/WagmiEventHandler.js +1317 -77
- package/dist/cjs/src/wagmi/types.d.ts +6 -0
- package/dist/esm/src/FormoAnalytics.d.ts +134 -0
- package/dist/esm/src/FormoAnalytics.js +647 -178
- package/dist/esm/src/constants/base.d.ts +0 -1
- package/dist/esm/src/constants/base.js +0 -2
- package/dist/esm/src/event/EventFactory.js +1 -1
- package/dist/esm/src/utils/mergeDeepRight.d.ts +22 -0
- package/dist/esm/src/utils/mergeDeepRight.js +51 -0
- package/dist/esm/src/version.d.ts +1 -1
- package/dist/esm/src/version.js +1 -1
- package/dist/esm/src/wagmi/WagmiEventHandler.d.ts +193 -1
- package/dist/esm/src/wagmi/WagmiEventHandler.js +1315 -76
- package/dist/esm/src/wagmi/types.d.ts +6 -0
- package/dist/index.umd.min.js +1 -1
- package/package.json +2 -11
- package/dist/cjs/src/ramda/internal/_curry1.d.ts +0 -10
- package/dist/cjs/src/ramda/internal/_curry1.js +0 -26
- package/dist/cjs/src/ramda/internal/_curry2.d.ts +0 -10
- package/dist/cjs/src/ramda/internal/_curry2.js +0 -43
- package/dist/cjs/src/ramda/internal/_curry3.d.ts +0 -10
- package/dist/cjs/src/ramda/internal/_curry3.js +0 -74
- package/dist/cjs/src/ramda/internal/_has.d.ts +0 -2
- package/dist/cjs/src/ramda/internal/_has.js +0 -7
- package/dist/cjs/src/ramda/internal/_isObject.d.ts +0 -2
- package/dist/cjs/src/ramda/internal/_isObject.js +0 -7
- package/dist/cjs/src/ramda/internal/_isPlaceholder.d.ts +0 -2
- package/dist/cjs/src/ramda/internal/_isPlaceholder.js +0 -7
- package/dist/cjs/src/ramda/mergeDeepRight.d.ts +0 -3
- package/dist/cjs/src/ramda/mergeDeepRight.js +0 -14
- package/dist/cjs/src/ramda/mergeDeepWithKey.d.ts +0 -3
- package/dist/cjs/src/ramda/mergeDeepWithKey.js +0 -20
- package/dist/cjs/src/ramda/mergeWithKey.d.ts +0 -3
- package/dist/cjs/src/ramda/mergeWithKey.js +0 -26
- package/dist/esm/src/ramda/internal/_curry1.d.ts +0 -10
- package/dist/esm/src/ramda/internal/_curry1.js +0 -20
- package/dist/esm/src/ramda/internal/_curry2.d.ts +0 -10
- package/dist/esm/src/ramda/internal/_curry2.js +0 -37
- package/dist/esm/src/ramda/internal/_curry3.d.ts +0 -10
- package/dist/esm/src/ramda/internal/_curry3.js +0 -68
- package/dist/esm/src/ramda/internal/_has.d.ts +0 -2
- package/dist/esm/src/ramda/internal/_has.js +0 -4
- package/dist/esm/src/ramda/internal/_isObject.d.ts +0 -2
- package/dist/esm/src/ramda/internal/_isObject.js +0 -4
- package/dist/esm/src/ramda/internal/_isPlaceholder.d.ts +0 -2
- package/dist/esm/src/ramda/internal/_isPlaceholder.js +0 -4
- package/dist/esm/src/ramda/mergeDeepRight.d.ts +0 -3
- package/dist/esm/src/ramda/mergeDeepRight.js +0 -9
- package/dist/esm/src/ramda/mergeDeepWithKey.d.ts +0 -3
- package/dist/esm/src/ramda/mergeDeepWithKey.js +0 -15
- package/dist/esm/src/ramda/mergeWithKey.d.ts +0 -3
- package/dist/esm/src/ramda/mergeWithKey.js +0 -21
|
@@ -54,7 +54,8 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
|
54
54
|
}
|
|
55
55
|
};
|
|
56
56
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
57
|
-
exports.WagmiEventHandler = void 0;
|
|
57
|
+
exports.WagmiEventHandler = exports.MARKER_GRACE_MS = void 0;
|
|
58
|
+
exports.__resetSeededWallet = __resetSeededWallet;
|
|
58
59
|
var events_1 = require("../types/events");
|
|
59
60
|
var logger_1 = require("../logger");
|
|
60
61
|
var utils_1 = require("./utils");
|
|
@@ -73,6 +74,259 @@ var RESERVED_FIELDS = new Set([
|
|
|
73
74
|
"function_name",
|
|
74
75
|
"function_args",
|
|
75
76
|
]);
|
|
77
|
+
/**
|
|
78
|
+
* Connections already announced to a destination during this page load.
|
|
79
|
+
*
|
|
80
|
+
* Deliberately module scoped rather than per handler or per FormoAnalytics
|
|
81
|
+
* instance. The duplicate this prevents comes from the SDK instance itself
|
|
82
|
+
* being rebuilt - a provider remount, an options change, HMR - while the
|
|
83
|
+
* wallet never disconnected, and per-instance state cannot see that.
|
|
84
|
+
*
|
|
85
|
+
* A real disconnect clears the entry, so a genuine reconnect within the same
|
|
86
|
+
* page load still emits. A new page load starts with a fresh module scope.
|
|
87
|
+
*/
|
|
88
|
+
var announcedConnections = new Set();
|
|
89
|
+
/**
|
|
90
|
+
* Connections announced, keyed by the wagmi connection OBJECT.
|
|
91
|
+
*
|
|
92
|
+
* Wagmi builds a fresh connection object on every `connect()`, and replaces it
|
|
93
|
+
* whenever anything about the connection changes. So finding the SAME object
|
|
94
|
+
* still in the store is proof that nothing happened in between - no
|
|
95
|
+
* disconnect, no reconnect - and the marker can be trusted with no time limit
|
|
96
|
+
* at all. That covers the case the grace period alone gets wrong: an SDK
|
|
97
|
+
* unmounted for a long time while the wallet simply stayed connected.
|
|
98
|
+
*
|
|
99
|
+
* The address-keyed set above remains the fallback for the genuinely
|
|
100
|
+
* ambiguous case, where the object was replaced but a chain switch and a
|
|
101
|
+
* reconnect are indistinguishable after the fact.
|
|
102
|
+
*
|
|
103
|
+
* Weak, so a dead connection is collected rather than pinned.
|
|
104
|
+
*/
|
|
105
|
+
var announcedByConnection = new WeakMap();
|
|
106
|
+
/**
|
|
107
|
+
* Keyed by write key as well as address: two SDK instances for different write
|
|
108
|
+
* keys are separate analytics destinations with separate queues, so the second
|
|
109
|
+
* must still receive its own connect for the same wallet.
|
|
110
|
+
*
|
|
111
|
+
* The connector uid is NOT part of the key. Wagmi keeps that uid stable across
|
|
112
|
+
* a disconnect and reconnect through the same connector, so including it never
|
|
113
|
+
* distinguished one connection occurrence from the next. The observation
|
|
114
|
+
* window below is what makes a reconnect emit again.
|
|
115
|
+
*/
|
|
116
|
+
var announceKey = function (writeKey, address) {
|
|
117
|
+
return "".concat(writeKey, ":").concat(address.toLowerCase());
|
|
118
|
+
};
|
|
119
|
+
/**
|
|
120
|
+
* How long the markers stay trustworthy after the last handler goes away.
|
|
121
|
+
*
|
|
122
|
+
* A marker only means "already announced" while the SDK is actually watching
|
|
123
|
+
* wagmi. With no handler mounted nothing observes a disconnect, so a wallet
|
|
124
|
+
* that disconnects and reconnects in that window would otherwise look
|
|
125
|
+
* unchanged and its genuine connect would be swallowed for the rest of the
|
|
126
|
+
* page load.
|
|
127
|
+
*
|
|
128
|
+
* Rebuilding the SDK tears the old handler down and builds a new one, and
|
|
129
|
+
* `FormoAnalytics.init()` is async, so the gap is real but short. This grace
|
|
130
|
+
* period spans a rebuild and little else: markers survive it, and a longer
|
|
131
|
+
* unmount discards them so the next connect is treated as new.
|
|
132
|
+
*/
|
|
133
|
+
exports.MARKER_GRACE_MS = 3000;
|
|
134
|
+
/**
|
|
135
|
+
* Liveness is tracked PER WRITE KEY, because markers are.
|
|
136
|
+
*
|
|
137
|
+
* A global count would let one destination keep another's markers alive: with
|
|
138
|
+
* destination B still mounted, A's would never expire, so a wallet that
|
|
139
|
+
* disconnected and reconnected while A was unmounted would have its genuine
|
|
140
|
+
* connect suppressed when A came back.
|
|
141
|
+
*/
|
|
142
|
+
var liveHandlers = new Map();
|
|
143
|
+
var markerExpiry = new Map();
|
|
144
|
+
/**
|
|
145
|
+
* @param adoptsLiveConnection whether the arriving handler actually found the
|
|
146
|
+
* announced connection still in place. Only then may it cancel the expiry:
|
|
147
|
+
* a replacement that mounts over a DISCONNECTED store has observed nothing,
|
|
148
|
+
* so cancelling would strand the markers for the rest of the page load and
|
|
149
|
+
* suppress a genuine connect hours later.
|
|
150
|
+
*/
|
|
151
|
+
function retainMarkers(writeKey, adoptsLiveConnection) {
|
|
152
|
+
var _a;
|
|
153
|
+
liveHandlers.set(writeKey, ((_a = liveHandlers.get(writeKey)) !== null && _a !== void 0 ? _a : 0) + 1);
|
|
154
|
+
if (!adoptsLiveConnection)
|
|
155
|
+
return;
|
|
156
|
+
var pending = markerExpiry.get(writeKey);
|
|
157
|
+
if (pending) {
|
|
158
|
+
clearTimeout(pending);
|
|
159
|
+
markerExpiry.delete(writeKey);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
function releaseMarkers(writeKey) {
|
|
163
|
+
var _a, _b, _c;
|
|
164
|
+
var remaining = Math.max(0, ((_a = liveHandlers.get(writeKey)) !== null && _a !== void 0 ? _a : 0) - 1);
|
|
165
|
+
liveHandlers.set(writeKey, remaining);
|
|
166
|
+
var wasLast = remaining === 0;
|
|
167
|
+
if (remaining > 0 || markerExpiry.has(writeKey))
|
|
168
|
+
return wasLast;
|
|
169
|
+
var timer = setTimeout(function () {
|
|
170
|
+
markerExpiry.delete(writeKey);
|
|
171
|
+
var prefix = "".concat(writeKey, ":");
|
|
172
|
+
announcedConnections.forEach(function (key) {
|
|
173
|
+
if (key.startsWith(prefix))
|
|
174
|
+
announcedConnections.delete(key);
|
|
175
|
+
});
|
|
176
|
+
}, exports.MARKER_GRACE_MS);
|
|
177
|
+
// Never hold a Node process (or a test run) open for this.
|
|
178
|
+
(_c = (_b = timer).unref) === null || _c === void 0 ? void 0 : _c.call(_b);
|
|
179
|
+
markerExpiry.set(writeKey, timer);
|
|
180
|
+
return wasLast;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* The one handler allowed to EMIT for a given destination.
|
|
184
|
+
*
|
|
185
|
+
* Two handlers can be alive at once over the same wagmi config and write key:
|
|
186
|
+
* Strict Mode, HMR, or an options change whose replacement mounts before the
|
|
187
|
+
* old one is torn down - which the marker grace period explicitly supports.
|
|
188
|
+
* Non-owners still track state, so whichever survives cleanup is already
|
|
189
|
+
* correct and takes over immediately; they simply do not emit.
|
|
190
|
+
*/
|
|
191
|
+
var emittingOwners = new Map();
|
|
192
|
+
/** Stable per-config id, so the owner key can span SDK instances. */
|
|
193
|
+
var configIds = new WeakMap();
|
|
194
|
+
var nextConfigId = 0;
|
|
195
|
+
var ownerKey = function (writeKey, config) {
|
|
196
|
+
var id = configIds.get(config);
|
|
197
|
+
if (!id) {
|
|
198
|
+
id = "cfg".concat((nextConfigId += 1));
|
|
199
|
+
configIds.set(config, id);
|
|
200
|
+
}
|
|
201
|
+
return "".concat(writeKey, ":").concat(id);
|
|
202
|
+
};
|
|
203
|
+
/**
|
|
204
|
+
* Pending transactions, shared per destination rather than per handler.
|
|
205
|
+
*
|
|
206
|
+
* A broadcast observed by handler A, followed by a replacement B taking over
|
|
207
|
+
* before the receipt arrives, otherwise loses the confirmation entirely: A no
|
|
208
|
+
* longer emits because it is not the owner, and B has no record of the
|
|
209
|
+
* broadcast to match the receipt against.
|
|
210
|
+
*/
|
|
211
|
+
var pendingTransactionsByDestination = new Map();
|
|
212
|
+
/**
|
|
213
|
+
* Pending records outlive a rebuild by the same grace period as the markers.
|
|
214
|
+
* The ordinary rebuild is cleanup THEN remount, so dropping them the instant
|
|
215
|
+
* the last handler goes would lose the receipt for anything broadcast just
|
|
216
|
+
* before teardown.
|
|
217
|
+
*/
|
|
218
|
+
var pendingTransactionExpiry = new Map();
|
|
219
|
+
function schedulePendingTransactionExpiry(key) {
|
|
220
|
+
var _a, _b;
|
|
221
|
+
if (pendingTransactionExpiry.has(key))
|
|
222
|
+
return;
|
|
223
|
+
var timer = setTimeout(function () {
|
|
224
|
+
pendingTransactionExpiry.delete(key);
|
|
225
|
+
pendingTransactionsByDestination.delete(key);
|
|
226
|
+
}, exports.MARKER_GRACE_MS);
|
|
227
|
+
(_b = (_a = timer).unref) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
228
|
+
pendingTransactionExpiry.set(key, timer);
|
|
229
|
+
}
|
|
230
|
+
function cancelPendingTransactionExpiry(key) {
|
|
231
|
+
var timer = pendingTransactionExpiry.get(key);
|
|
232
|
+
if (!timer)
|
|
233
|
+
return;
|
|
234
|
+
clearTimeout(timer);
|
|
235
|
+
pendingTransactionExpiry.delete(key);
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Has this destination already announced this wallet on this very connection?
|
|
239
|
+
*
|
|
240
|
+
* `exact` means the wagmi connection object is unchanged since the
|
|
241
|
+
* announcement, which is proof nothing happened in between and holds however
|
|
242
|
+
* long the SDK was away. `recent` means the object was replaced but the
|
|
243
|
+
* announcement is still inside the observation window.
|
|
244
|
+
*/
|
|
245
|
+
function announcementState(key, connection) {
|
|
246
|
+
var _a;
|
|
247
|
+
if (connection && ((_a = announcedByConnection.get(connection)) === null || _a === void 0 ? void 0 : _a.has(key))) {
|
|
248
|
+
return "exact";
|
|
249
|
+
}
|
|
250
|
+
return announcedConnections.has(key) ? "recent" : "none";
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Undo an announcement claim whose emission failed.
|
|
254
|
+
*
|
|
255
|
+
* `markAnnounced()` is called before the event is handed to FormoAnalytics,
|
|
256
|
+
* so the claim exists while the emission is in flight and a concurrent
|
|
257
|
+
* handler cannot double-report. If that emission then fails, nothing was
|
|
258
|
+
* reported - and leaving the claim standing means a retry or a replacement
|
|
259
|
+
* handler treats the wallet as done, losing its connect for the rest of the
|
|
260
|
+
* page load even after the queue recovers.
|
|
261
|
+
*/
|
|
262
|
+
function releaseAnnouncement(key, connection) {
|
|
263
|
+
var _a;
|
|
264
|
+
announcedConnections.delete(key);
|
|
265
|
+
if (connection)
|
|
266
|
+
(_a = announcedByConnection.get(connection)) === null || _a === void 0 ? void 0 : _a.delete(key);
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Record a connection as announced.
|
|
270
|
+
*
|
|
271
|
+
* Deliberately unbounded. Entries are removed when the wallet disconnects,
|
|
272
|
+
* and pruned against the live connections whenever a handler mounts, so the
|
|
273
|
+
* set can only hold wallets that are actually connected right now. An earlier
|
|
274
|
+
* revision carried a size cap as a backstop; with both of those in place it
|
|
275
|
+
* was unreachable, and the test covering it passed with the cap removed.
|
|
276
|
+
*/
|
|
277
|
+
function markAnnounced(key, connection) {
|
|
278
|
+
var _a;
|
|
279
|
+
if (connection) {
|
|
280
|
+
var keys = (_a = announcedByConnection.get(connection)) !== null && _a !== void 0 ? _a : new Set();
|
|
281
|
+
keys.add(key);
|
|
282
|
+
announcedByConnection.set(connection, keys);
|
|
283
|
+
}
|
|
284
|
+
announcedConnections.add(key);
|
|
285
|
+
}
|
|
286
|
+
/** Test hook. Real page loads reset this naturally. */
|
|
287
|
+
function __resetSeededWallet() {
|
|
288
|
+
emittingOwners.clear();
|
|
289
|
+
pendingTransactionExpiry.forEach(function (t) { return clearTimeout(t); });
|
|
290
|
+
pendingTransactionExpiry.clear();
|
|
291
|
+
pendingTransactionsByDestination.clear();
|
|
292
|
+
announcedConnections.clear();
|
|
293
|
+
liveHandlers.clear();
|
|
294
|
+
markerExpiry.forEach(function (timer) { return clearTimeout(timer); });
|
|
295
|
+
markerExpiry.clear();
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* wagmi accepts an `account` as either a bare address or a viem Account
|
|
299
|
+
* object. Normalise both to an address, or undefined when absent.
|
|
300
|
+
*/
|
|
301
|
+
/**
|
|
302
|
+
* Coerce a wagmi/viem chain id to a number.
|
|
303
|
+
*
|
|
304
|
+
* An EIP-712 domain carries whatever the caller put in it, and viem accepts a
|
|
305
|
+
* hex string or a bigint there as well as a number. Passing those straight
|
|
306
|
+
* through would put a non-number in the event payload and defeat the numeric
|
|
307
|
+
* comparison in `tracking.excludeChains`.
|
|
308
|
+
*/
|
|
309
|
+
function normalizeChainId(value) {
|
|
310
|
+
if (typeof value === "number")
|
|
311
|
+
return Number.isFinite(value) ? value : undefined;
|
|
312
|
+
if (typeof value === "bigint")
|
|
313
|
+
return Number(value);
|
|
314
|
+
if (typeof value === "string" && value.trim() !== "") {
|
|
315
|
+
var parsed = value.startsWith("0x") ? Number(value) : Number(value);
|
|
316
|
+
return Number.isFinite(parsed) ? parsed : undefined;
|
|
317
|
+
}
|
|
318
|
+
return undefined;
|
|
319
|
+
}
|
|
320
|
+
function resolveAccountAddress(account) {
|
|
321
|
+
if (typeof account === "string")
|
|
322
|
+
return account;
|
|
323
|
+
if (account && typeof account === "object") {
|
|
324
|
+
var address = account.address;
|
|
325
|
+
if (typeof address === "string")
|
|
326
|
+
return address;
|
|
327
|
+
}
|
|
328
|
+
return undefined;
|
|
329
|
+
}
|
|
76
330
|
/**
|
|
77
331
|
* Clean up old entries from a Set to prevent memory leaks.
|
|
78
332
|
* Removes oldest entries when size exceeds maxSize.
|
|
@@ -97,6 +351,26 @@ var WagmiEventHandler = /** @class */ (function () {
|
|
|
97
351
|
this.trackingState = {
|
|
98
352
|
isProcessing: false,
|
|
99
353
|
};
|
|
354
|
+
/**
|
|
355
|
+
* Set by `cleanup()`. Every deferred emission checks it, so a handler that
|
|
356
|
+
* has been torn down cannot push an event into a queue the replacement SDK
|
|
357
|
+
* instance no longer owns.
|
|
358
|
+
*/
|
|
359
|
+
this.disposed = false;
|
|
360
|
+
/** Reentrancy guard for `reconcileWithLiveState()`. */
|
|
361
|
+
this.reconciling = false;
|
|
362
|
+
/**
|
|
363
|
+
* Ticket for each active-connection transition. Emissions are awaited, and
|
|
364
|
+
* wagmi can move again in that window; a continuation whose ticket is no
|
|
365
|
+
* longer current must not write stale state over the newer one.
|
|
366
|
+
*/
|
|
367
|
+
this.transitionGeneration = 0;
|
|
368
|
+
/**
|
|
369
|
+
* A `disconnected` status that arrived while the lock was held. Final-state
|
|
370
|
+
* reconciliation cannot see a disconnect/reconnect cycle that completed
|
|
371
|
+
* inside that window, so this records that one happened.
|
|
372
|
+
*/
|
|
373
|
+
this.missedDisconnect = false;
|
|
100
374
|
/**
|
|
101
375
|
* Track processed mutation states to prevent duplicate event emissions
|
|
102
376
|
* Key format: `${mutationId}:${status}`
|
|
@@ -107,17 +381,35 @@ var WagmiEventHandler = /** @class */ (function () {
|
|
|
107
381
|
* Key format: `${queryHash}:${status}`
|
|
108
382
|
*/
|
|
109
383
|
this.processedQueries = new Set();
|
|
110
|
-
/**
|
|
111
|
-
* Store transaction details from BROADCASTED events for use in CONFIRMED/REVERTED
|
|
112
|
-
* Key: transactionHash, Value: transaction details including the original sender address
|
|
113
|
-
*/
|
|
114
|
-
this.pendingTransactions = new Map();
|
|
115
384
|
this.formo = formoAnalytics;
|
|
116
385
|
this.wagmiConfig = wagmiConfig;
|
|
117
386
|
this.queryClient = queryClient;
|
|
118
387
|
logger_1.logger.info("WagmiEventHandler: Initializing Wagmi integration");
|
|
388
|
+
// Keep the page-load markers alive across an SDK rebuild. Must run before
|
|
389
|
+
// the seed, so a handler created moments after its predecessor was torn
|
|
390
|
+
// down still sees what that predecessor announced.
|
|
391
|
+
// Only a handler that finds an ANNOUNCED wallet still connected may hold
|
|
392
|
+
// the markers open. Mounting over a disconnected store has observed
|
|
393
|
+
// nothing; mounting over a DIFFERENT wallet is worse still - it would
|
|
394
|
+
// preserve the previous wallet's marker and suppress its genuine
|
|
395
|
+
// reconnect for the rest of the page load. Markers for wallets no longer
|
|
396
|
+
// in `state.connections` are dropped outright.
|
|
397
|
+
var livesOverConnection = this.pruneMarkersForLostWallets();
|
|
398
|
+
retainMarkers(this.formo.writeKey, livesOverConnection);
|
|
399
|
+
// Claim the right to emit for this destination. The NEWEST handler always
|
|
400
|
+
// wins, deliberately: an app that rebuilds without calling `cleanup()`
|
|
401
|
+
// leaks the old handler, and letting that stale one keep ownership would
|
|
402
|
+
// silence the live one for the rest of the page load - far worse than the
|
|
403
|
+
// duplicate events this is here to prevent.
|
|
404
|
+
this.ownerKey = ownerKey(this.formo.writeKey, wagmiConfig);
|
|
405
|
+
emittingOwners.set(this.ownerKey, this);
|
|
406
|
+
// A remount for this destination cancels the pending-record expiry the
|
|
407
|
+
// previous handler started.
|
|
408
|
+
cancelPendingTransactionExpiry(this.ownerKey);
|
|
119
409
|
// Set up connection/disconnection/chain listeners
|
|
120
410
|
this.setupConnectionListeners();
|
|
411
|
+
// Adopt a wallet that connected before this handler existed.
|
|
412
|
+
this.seedFromCurrentState();
|
|
121
413
|
// Set up mutation and query tracking if QueryClient is provided
|
|
122
414
|
if (this.queryClient) {
|
|
123
415
|
this.setupMutationTracking();
|
|
@@ -127,6 +419,88 @@ var WagmiEventHandler = /** @class */ (function () {
|
|
|
127
419
|
logger_1.logger.warn("WagmiEventHandler: QueryClient not provided, signature and transaction events will not be tracked");
|
|
128
420
|
}
|
|
129
421
|
}
|
|
422
|
+
/**
|
|
423
|
+
* Drop markers for wallets wagmi no longer holds, and report whether any
|
|
424
|
+
* announced wallet is still connected.
|
|
425
|
+
*
|
|
426
|
+
* Called when a handler mounts. Cancelling the expiry timer on the strength
|
|
427
|
+
* of "some connection exists" let a marker for a wallet that had since
|
|
428
|
+
* disconnected survive indefinitely, suppressing its genuine reconnect.
|
|
429
|
+
*/
|
|
430
|
+
WagmiEventHandler.prototype.pruneMarkersForLostWallets = function () {
|
|
431
|
+
var _this = this;
|
|
432
|
+
var state;
|
|
433
|
+
try {
|
|
434
|
+
state = this.getState();
|
|
435
|
+
}
|
|
436
|
+
catch (_a) {
|
|
437
|
+
return false;
|
|
438
|
+
}
|
|
439
|
+
var prefix = "".concat(this.formo.writeKey, ":");
|
|
440
|
+
var live = new Set();
|
|
441
|
+
state.connections.forEach(function (connection) {
|
|
442
|
+
connection.accounts.forEach(function (a) {
|
|
443
|
+
return live.add(announceKey(_this.formo.writeKey, a));
|
|
444
|
+
});
|
|
445
|
+
});
|
|
446
|
+
var announcedStillConnected = false;
|
|
447
|
+
announcedConnections.forEach(function (key) {
|
|
448
|
+
if (!key.startsWith(prefix))
|
|
449
|
+
return;
|
|
450
|
+
if (live.has(key))
|
|
451
|
+
announcedStillConnected = true;
|
|
452
|
+
else
|
|
453
|
+
announcedConnections.delete(key);
|
|
454
|
+
});
|
|
455
|
+
return announcedStillConnected;
|
|
456
|
+
};
|
|
457
|
+
Object.defineProperty(WagmiEventHandler.prototype, "isEmittingOwner", {
|
|
458
|
+
/**
|
|
459
|
+
* Whether this handler is the one allowed to emit for its destination.
|
|
460
|
+
*
|
|
461
|
+
* A handler that lost the race still tracks state, so it is ready to take
|
|
462
|
+
* over the moment the owner goes away; it just does not emit.
|
|
463
|
+
*/
|
|
464
|
+
get: function () {
|
|
465
|
+
if (!this.ownerKey)
|
|
466
|
+
return true;
|
|
467
|
+
var owner = emittingOwners.get(this.ownerKey);
|
|
468
|
+
if (!owner) {
|
|
469
|
+
// Nobody holds it - the previous owner was torn down. Claim it.
|
|
470
|
+
emittingOwners.set(this.ownerKey, this);
|
|
471
|
+
return true;
|
|
472
|
+
}
|
|
473
|
+
return owner === this;
|
|
474
|
+
},
|
|
475
|
+
enumerable: false,
|
|
476
|
+
configurable: true
|
|
477
|
+
});
|
|
478
|
+
Object.defineProperty(WagmiEventHandler.prototype, "pendingTransactions", {
|
|
479
|
+
/**
|
|
480
|
+
* Store transaction details from BROADCASTED events for use in CONFIRMED/REVERTED
|
|
481
|
+
* Key: transactionHash, Value: transaction details including the original sender address
|
|
482
|
+
*/
|
|
483
|
+
/**
|
|
484
|
+
* Shared per destination, not per handler.
|
|
485
|
+
*
|
|
486
|
+
* A broadcast observed by handler A, followed by a replacement B taking
|
|
487
|
+
* over before the receipt arrives, used to lose the confirmation entirely:
|
|
488
|
+
* A no longer emits because it is not the owner, and B had no record of the
|
|
489
|
+
* broadcast to match the receipt against.
|
|
490
|
+
*/
|
|
491
|
+
get: function () {
|
|
492
|
+
var _a;
|
|
493
|
+
var key = (_a = this.ownerKey) !== null && _a !== void 0 ? _a : "";
|
|
494
|
+
var map = pendingTransactionsByDestination.get(key);
|
|
495
|
+
if (!map) {
|
|
496
|
+
map = new Map();
|
|
497
|
+
pendingTransactionsByDestination.set(key, map);
|
|
498
|
+
}
|
|
499
|
+
return map;
|
|
500
|
+
},
|
|
501
|
+
enumerable: false,
|
|
502
|
+
configurable: true
|
|
503
|
+
});
|
|
130
504
|
/**
|
|
131
505
|
* Set up listeners for wallet connection, disconnection, and chain changes
|
|
132
506
|
*/
|
|
@@ -138,83 +512,832 @@ var WagmiEventHandler = /** @class */ (function () {
|
|
|
138
512
|
_this.handleStatusChange(status, prevStatus);
|
|
139
513
|
});
|
|
140
514
|
this.unsubscribers.push(statusUnsubscribe);
|
|
141
|
-
// Subscribe to chain ID changes
|
|
142
|
-
|
|
515
|
+
// Subscribe to chain ID changes.
|
|
516
|
+
//
|
|
517
|
+
// Selects the ACTIVE CONNECTION's chain, with the global as a fallback -
|
|
518
|
+
// the same rule every read site here uses. Selecting only `state.chainId`
|
|
519
|
+
// meant that with several connections, or with `syncConnectedChain: false`,
|
|
520
|
+
// the active connection could move to a new chain while the global stayed
|
|
521
|
+
// put: no callback ran, and both the tracked chain and Formo's central
|
|
522
|
+
// chain silently went stale for every later event.
|
|
523
|
+
var chainIdUnsubscribe = this.wagmiConfig.subscribe(function (state) { var _a; return (_a = _this.getActiveConnectionChainId(state)) !== null && _a !== void 0 ? _a : state.chainId; }, function (chainId, prevChainId) {
|
|
143
524
|
_this.handleChainChange(chainId, prevChainId);
|
|
144
525
|
});
|
|
145
526
|
this.unsubscribers.push(chainIdUnsubscribe);
|
|
527
|
+
// Subscribe to the active connection as a whole: its connector id AND its
|
|
528
|
+
// address, as one string.
|
|
529
|
+
//
|
|
530
|
+
// `state.status` is a single global value, so it stays "connected" when a
|
|
531
|
+
// user switches account inside an already-connected wallet. Without this
|
|
532
|
+
// the switch is invisible: no event, and the tracked address goes stale
|
|
533
|
+
// and mis-attributes every later signature and transaction.
|
|
534
|
+
//
|
|
535
|
+
// The connector id has to be in the key too. Selecting the address alone
|
|
536
|
+
// missed the case where two connectors hold the SAME account - MetaMask
|
|
537
|
+
// and Rabby over one hardware wallet, say - and the current one
|
|
538
|
+
// disconnects: wagmi falls back to the other, the address is unchanged, so
|
|
539
|
+
// nothing fired and the disconnect was lost for good.
|
|
540
|
+
var connectionUnsubscribe = this.wagmiConfig.subscribe(function (state) { var _a, _b; return "".concat((_a = state.current) !== null && _a !== void 0 ? _a : "", "|").concat((_b = _this.getConnectedAddress(state)) !== null && _b !== void 0 ? _b : ""); }, function (key, prevKey) {
|
|
541
|
+
var _a = key.split("|"), address = _a[1];
|
|
542
|
+
var _b = (prevKey !== null && prevKey !== void 0 ? prevKey : "|").split("|"), prevAddress = _b[1];
|
|
543
|
+
_this.handleActiveAddressChange(address || undefined, prevAddress || undefined);
|
|
544
|
+
});
|
|
545
|
+
this.unsubscribers.push(connectionUnsubscribe);
|
|
146
546
|
logger_1.logger.info("WagmiEventHandler: Connection listeners set up successfully");
|
|
147
547
|
};
|
|
548
|
+
/**
|
|
549
|
+
* Adopt a connection that already existed when this handler was created.
|
|
550
|
+
*
|
|
551
|
+
* `config.subscribe` reports *changes* only, so a wallet restored by wagmi's
|
|
552
|
+
* mount-time `reconnect()` is invisible to a handler built afterwards. That
|
|
553
|
+
* is the normal case whenever the host app loads the SDK lazily (dynamic
|
|
554
|
+
* import, `requestIdleCallback`) and can also happen on a plain mount, since
|
|
555
|
+
* `FormoAnalytics.init()` and wagmi's reconnect are both async and race.
|
|
556
|
+
*
|
|
557
|
+
* Without this seed the whole session is lost: no connect event, and
|
|
558
|
+
* `lastAddress` stays undefined so the signature and transaction handlers
|
|
559
|
+
* drop their events too, and the eventual disconnect carries neither address
|
|
560
|
+
* nor chain id.
|
|
561
|
+
*
|
|
562
|
+
* `subscribe(..., { fireImmediately: true })` is not a substitute. It reports
|
|
563
|
+
* the current value with no distinct previous value, so the connect branch's
|
|
564
|
+
* `prevStatus !== "connected"` test never passes, and wagmi v3 does not
|
|
565
|
+
* honour the option at all.
|
|
566
|
+
*
|
|
567
|
+
* Deliberately synchronous, and deliberately does NOT take the
|
|
568
|
+
* `isProcessing` lock. Everything that mutates tracking state runs before
|
|
569
|
+
* any await, so no status change can interleave with it. Holding the lock
|
|
570
|
+
* across the `connect()` emission instead would make `handleStatusChange()`
|
|
571
|
+
* drop - not defer - a disconnect or wallet switch that lands in that
|
|
572
|
+
* window, which is exactly when a lazily loaded SDK is racing app activity.
|
|
573
|
+
*
|
|
574
|
+
* A genuine `connected` transition cannot double-emit alongside this seed.
|
|
575
|
+
* `config.subscribe` only fires on change, and the connect branch treats a
|
|
576
|
+
* re-entry for an already-tracked address as a no-op (or a chain change).
|
|
577
|
+
*/
|
|
578
|
+
WagmiEventHandler.prototype.seedFromCurrentState = function () {
|
|
579
|
+
var _a, _b;
|
|
580
|
+
try {
|
|
581
|
+
var state = this.getState();
|
|
582
|
+
if (state.status !== "connected") {
|
|
583
|
+
return;
|
|
584
|
+
}
|
|
585
|
+
var address = this.getConnectedAddress(state);
|
|
586
|
+
// The active connection's chain is authoritative. `state.chainId` is a
|
|
587
|
+
// single global value that can lag or describe a different connection,
|
|
588
|
+
// and with `syncConnectedChain: false` it stays on the chain the APP
|
|
589
|
+
// selected while the connection reports what the WALLET is on.
|
|
590
|
+
var chainId = (_a = this.getActiveConnectionChainId(state)) !== null && _a !== void 0 ? _a : state.chainId;
|
|
591
|
+
if (!address || chainId === undefined) {
|
|
592
|
+
logger_1.logger.debug("WagmiEventHandler: Connected at init but address or chainId is missing, nothing to seed", { address: address, chainId: chainId });
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
595
|
+
logger_1.logger.info("WagmiEventHandler: Adopting connection that predates this handler", { address: address, chainId: chainId });
|
|
596
|
+
// Sync central state first so tracking.excludeChains is enforced even
|
|
597
|
+
// when connect autocapture is disabled.
|
|
598
|
+
this.formo.syncWalletState({ chainId: chainId, address: address });
|
|
599
|
+
// Only adopt the wallet privately if central state actually accepted it.
|
|
600
|
+
// While tracking is suppressed (opt-out, excluded host or path)
|
|
601
|
+
// syncWalletState deliberately refuses to learn a wallet. Retaining it
|
|
602
|
+
// here anyway would let the mutation handlers attribute signatures and
|
|
603
|
+
// transactions to an address the SDK has decided it must not know -
|
|
604
|
+
// including on a chain that excludeChains rejects, since that check
|
|
605
|
+
// reads the central field rather than the event payload.
|
|
606
|
+
//
|
|
607
|
+
// Compared case-insensitively: syncWalletState stores the checksummed
|
|
608
|
+
// form, which need not match the casing wagmi reported.
|
|
609
|
+
if (((_b = this.formo.currentAddress) === null || _b === void 0 ? void 0 : _b.toLowerCase()) !== address.toLowerCase()) {
|
|
610
|
+
logger_1.logger.debug("WagmiEventHandler: Central state declined the connection, not seeding", { address: address, chainId: chainId });
|
|
611
|
+
return;
|
|
612
|
+
}
|
|
613
|
+
// Adopt the tracking state either way - the mutation handlers need an
|
|
614
|
+
// address - but only emit the first time this page load adopts this
|
|
615
|
+
// wallet. A rebuilt SDK instance over an unchanged connection is a
|
|
616
|
+
// lifecycle event, not a user action.
|
|
617
|
+
this.trackingState.lastAddress = address;
|
|
618
|
+
this.trackingState.lastChainId = chainId;
|
|
619
|
+
this.trackingState.lastStatus = state.status;
|
|
620
|
+
// Always record the connection, including on the deduplicated path
|
|
621
|
+
// below. A replacement handler that adopts a live connection must be
|
|
622
|
+
// able to clear the right marker when it later sees the disconnect.
|
|
623
|
+
this.trackingState.lastConnectionId = state.current;
|
|
624
|
+
if (!this.formo.isAutocaptureEnabled("connect") || !this.isEmittingOwner) {
|
|
625
|
+
// Nothing was announced, so nothing may be marked. Marking here would
|
|
626
|
+
// make a later rebuild with connect autocapture enabled - which is
|
|
627
|
+
// exactly how FormoAnalyticsProvider applies an options change - find
|
|
628
|
+
// the marker and stay silent about a wallet it never reported.
|
|
629
|
+
logger_1.logger.debug("WagmiEventHandler: Connect autocapture disabled, adopted without emitting", { address: address, chainId: chainId });
|
|
630
|
+
return;
|
|
631
|
+
}
|
|
632
|
+
// Nothing that will not actually be sent may be marked. `syncWalletState`
|
|
633
|
+
// accepts a wallet that `trackEvent()` then silently drops - `tracking:
|
|
634
|
+
// false`, or a chain in `excludeChains` - and marking it would make the
|
|
635
|
+
// rebuild that turns tracking back on find the marker and stay quiet
|
|
636
|
+
// about the wallet for the rest of the page load.
|
|
637
|
+
if (!this.formo.willTrackEvent(chainId)) {
|
|
638
|
+
logger_1.logger.debug("WagmiEventHandler: Connect would not be tracked, adopted without emitting", { address: address, chainId: chainId });
|
|
639
|
+
return;
|
|
640
|
+
}
|
|
641
|
+
var walletKey_1 = announceKey(this.formo.writeKey, address);
|
|
642
|
+
var connection_1 = state.current
|
|
643
|
+
? state.connections.get(state.current)
|
|
644
|
+
: undefined;
|
|
645
|
+
if (announcementState(walletKey_1, connection_1) !== "none") {
|
|
646
|
+
logger_1.logger.debug("WagmiEventHandler: Wallet already adopted this page load, not re-emitting connect", { address: address });
|
|
647
|
+
return;
|
|
648
|
+
}
|
|
649
|
+
markAnnounced(walletKey_1, connection_1);
|
|
650
|
+
{
|
|
651
|
+
var connectorName = this.getConnectorName(state);
|
|
652
|
+
// Invoked synchronously, only the completion is fire-and-forget.
|
|
653
|
+
//
|
|
654
|
+
// Deferring the call itself to a microtask and guarding it on
|
|
655
|
+
// `disposed` loses the event outright: a rebuild that tears this
|
|
656
|
+
// handler down before the microtask runs leaves the marker standing,
|
|
657
|
+
// so the replacement handler suppresses its own connect while this one
|
|
658
|
+
// declines to emit. Nobody reports the wallet. Calling now means the
|
|
659
|
+
// marker and the emission are decided together, and a rebuild can only
|
|
660
|
+
// arrive after the event is already handed to FormoAnalytics.
|
|
661
|
+
void Promise.resolve(this.formo.connect({ chainId: chainId, address: address }, __assign({}, (connectorName && { providerName: connectorName })))).catch(function (error) {
|
|
662
|
+
// The marker is a claim, not a record. If the emission failed the
|
|
663
|
+
// wallet was never reported, so release it - otherwise a retry or a
|
|
664
|
+
// replacement handler treats it as done and the connect is lost for
|
|
665
|
+
// the rest of the page load even once the queue recovers.
|
|
666
|
+
releaseAnnouncement(walletKey_1, connection_1);
|
|
667
|
+
logger_1.logger.error("WagmiEventHandler: Error emitting seeded connect event:", error);
|
|
668
|
+
});
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
catch (error) {
|
|
672
|
+
logger_1.logger.error("WagmiEventHandler: Error seeding from current state:", error);
|
|
673
|
+
}
|
|
674
|
+
};
|
|
675
|
+
/**
|
|
676
|
+
* Re-assert the tracked wallet into central state.
|
|
677
|
+
*
|
|
678
|
+
* Needed when an awaited emission clears the central namespace after a newer
|
|
679
|
+
* transition has already adopted a different wallet: the two then disagree,
|
|
680
|
+
* and `trackEvent()` reads the central one.
|
|
681
|
+
*/
|
|
682
|
+
WagmiEventHandler.prototype.restoreCentralStateFromTracking = function () {
|
|
683
|
+
var _a;
|
|
684
|
+
var _b = this.trackingState, lastAddress = _b.lastAddress, lastChainId = _b.lastChainId;
|
|
685
|
+
if (!lastAddress || lastChainId === undefined)
|
|
686
|
+
return;
|
|
687
|
+
// Compares the CHAIN as well as the address.
|
|
688
|
+
//
|
|
689
|
+
// A chain switch on an excluded path is deliberately refused by
|
|
690
|
+
// `syncWalletState()`, so central state keeps the old chain while
|
|
691
|
+
// `lastChainId` moves on. Matching on the address alone returned here,
|
|
692
|
+
// and after navigating back to an allowed path the events that fall back
|
|
693
|
+
// to the central chain were gated against a chain the wallet had left -
|
|
694
|
+
// bypassing an exclusion covering where it actually was.
|
|
695
|
+
if (((_a = this.formo.currentAddress) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === lastAddress.toLowerCase() &&
|
|
696
|
+
this.formo.currentChainId === lastChainId) {
|
|
697
|
+
return;
|
|
698
|
+
}
|
|
699
|
+
logger_1.logger.info("WagmiEventHandler: Restoring central state a stale disconnect cleared", { address: lastAddress, chainId: lastChainId });
|
|
700
|
+
this.formo.syncWalletState({ chainId: lastChainId, address: lastAddress });
|
|
701
|
+
};
|
|
702
|
+
/**
|
|
703
|
+
* Drop the wallet this handler is attributing events to.
|
|
704
|
+
*
|
|
705
|
+
* Used whenever central state refuses to hold the wallet - an excluded path
|
|
706
|
+
* or chain, or an opt-out. Keeping the address privately would let the
|
|
707
|
+
* mutation handlers label signatures and transactions with an identity the
|
|
708
|
+
* SDK has deliberately declined to know.
|
|
709
|
+
*/
|
|
710
|
+
WagmiEventHandler.prototype.releaseTrackedWallet = function () {
|
|
711
|
+
var previous = this.trackingState.lastAddress;
|
|
712
|
+
this.trackingState.lastAddress = undefined;
|
|
713
|
+
this.trackingState.lastChainId = undefined;
|
|
714
|
+
this.trackingState.lastConnectionId = undefined;
|
|
715
|
+
if (previous) {
|
|
716
|
+
announcedConnections.delete(announceKey(this.formo.writeKey, previous));
|
|
717
|
+
}
|
|
718
|
+
};
|
|
719
|
+
/**
|
|
720
|
+
* Adopt the live connection if one exists and nothing is tracked yet.
|
|
721
|
+
*
|
|
722
|
+
* `seedFromCurrentState()` runs once at construction and can legitimately
|
|
723
|
+
* decline: wagmi may still be filling `connections`, the chain may not have
|
|
724
|
+
* arrived, or tracking may be suppressed because the app happens to be on an
|
|
725
|
+
* excluded path. `config.subscribe` only reports *changes*, so an unchanged
|
|
726
|
+
* connection would then stay invisible for the rest of the page load and
|
|
727
|
+
* every later signature and transaction would be dropped.
|
|
728
|
+
*
|
|
729
|
+
* Idempotent. The page-load marker keeps it from re-emitting a connect for a
|
|
730
|
+
* wallet already announced, so this is safe to call from any listener.
|
|
731
|
+
*/
|
|
732
|
+
WagmiEventHandler.prototype.retryAdoption = function () {
|
|
733
|
+
if (this.disposed)
|
|
734
|
+
return;
|
|
735
|
+
// The cached wallet may no longer be the live one. While tracking is
|
|
736
|
+
// suppressed a user can disconnect, or switch account inside the same
|
|
737
|
+
// wallet - which moves only the connection's accounts, so neither the
|
|
738
|
+
// status nor the chain subscription fires and nothing tells this handler.
|
|
739
|
+
// Restoring the cached wallet then resurrects an address the user has
|
|
740
|
+
// left, and the marker makes the check below return before the live one
|
|
741
|
+
// is ever adopted.
|
|
742
|
+
if (this.trackingState.lastAddress && !this.tracksLiveWallet()) {
|
|
743
|
+
logger_1.logger.debug("WagmiEventHandler: Tracked wallet is no longer the live one, releasing before retry", { tracked: this.trackingState.lastAddress });
|
|
744
|
+
this.releaseTrackedWallet();
|
|
745
|
+
}
|
|
746
|
+
// Central state can have been cleared underneath a wallet this handler is
|
|
747
|
+
// still tracking: `optOutTracking()` calls `reset()`, which wipes
|
|
748
|
+
// `currentAddress` / `currentChainId` while the handler keeps its wallet.
|
|
749
|
+
// Left unreconciled, later events carry no wallet at all - and
|
|
750
|
+
// `shouldTrack()` sees no chain, so `excludeChains` stops excluding.
|
|
751
|
+
this.resyncCentralState();
|
|
752
|
+
// Adopted is not the same as announced. A wallet connected on an excluded
|
|
753
|
+
// chain, or while tracking was off, is adopted so mutations can be
|
|
754
|
+
// attributed, but its connect is deliberately not emitted or marked. If
|
|
755
|
+
// this returned merely because an address is present, that wallet would
|
|
756
|
+
// never be reported once the chain or configuration allowed it.
|
|
757
|
+
if (this.trackingState.lastAddress && this.isCurrentWalletAnnounced()) {
|
|
758
|
+
return;
|
|
759
|
+
}
|
|
760
|
+
this.seedFromCurrentState();
|
|
761
|
+
};
|
|
762
|
+
/** Whether the wallet this handler tracks is the one wagmi currently has. */
|
|
763
|
+
WagmiEventHandler.prototype.tracksLiveWallet = function () {
|
|
764
|
+
var tracked = this.trackingState.lastAddress;
|
|
765
|
+
if (!tracked)
|
|
766
|
+
return false;
|
|
767
|
+
try {
|
|
768
|
+
var state = this.getState();
|
|
769
|
+
if (state.status !== "connected")
|
|
770
|
+
return false;
|
|
771
|
+
var live = this.getConnectedAddress(state);
|
|
772
|
+
return !!live && live.toLowerCase() === tracked.toLowerCase();
|
|
773
|
+
}
|
|
774
|
+
catch (error) {
|
|
775
|
+
logger_1.logger.error("WagmiEventHandler: Error reading state to compare wallets:", error);
|
|
776
|
+
return false;
|
|
777
|
+
}
|
|
778
|
+
};
|
|
779
|
+
/**
|
|
780
|
+
* Put the tracked wallet back into central state without emitting anything.
|
|
781
|
+
*
|
|
782
|
+
* Used when the two have diverged for a reason that is not a wallet change:
|
|
783
|
+
* an opt-out `reset()`, or a stale disconnect completing after a newer
|
|
784
|
+
* transition adopted a different wallet.
|
|
785
|
+
*/
|
|
786
|
+
WagmiEventHandler.prototype.resyncCentralState = function () {
|
|
787
|
+
var _a;
|
|
788
|
+
var _b = this.trackingState, lastAddress = _b.lastAddress, lastChainId = _b.lastChainId;
|
|
789
|
+
if (!lastAddress || lastChainId === undefined)
|
|
790
|
+
return;
|
|
791
|
+
// Compares the CHAIN as well as the address.
|
|
792
|
+
//
|
|
793
|
+
// A chain switch on an excluded path is deliberately refused by
|
|
794
|
+
// `syncWalletState()`, so central state keeps the old chain while
|
|
795
|
+
// `lastChainId` moves on. Matching on the address alone returned here,
|
|
796
|
+
// and after navigating back to an allowed path the events that fall back
|
|
797
|
+
// to the central chain were gated against a chain the wallet had left -
|
|
798
|
+
// bypassing an exclusion covering where it actually was.
|
|
799
|
+
if (((_a = this.formo.currentAddress) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === lastAddress.toLowerCase() &&
|
|
800
|
+
this.formo.currentChainId === lastChainId) {
|
|
801
|
+
return;
|
|
802
|
+
}
|
|
803
|
+
logger_1.logger.info("WagmiEventHandler: Re-syncing central state for a tracked wallet", {
|
|
804
|
+
address: lastAddress,
|
|
805
|
+
chainId: lastChainId,
|
|
806
|
+
});
|
|
807
|
+
this.formo.syncWalletState({ chainId: lastChainId, address: lastAddress });
|
|
808
|
+
};
|
|
809
|
+
/** Whether the wallet currently tracked has already had a connect emitted. */
|
|
810
|
+
WagmiEventHandler.prototype.isCurrentWalletAnnounced = function () {
|
|
811
|
+
var address = this.trackingState.lastAddress;
|
|
812
|
+
if (!address)
|
|
813
|
+
return false;
|
|
814
|
+
var connection;
|
|
815
|
+
try {
|
|
816
|
+
var state = this.getState();
|
|
817
|
+
connection = state.current
|
|
818
|
+
? state.connections.get(state.current)
|
|
819
|
+
: undefined;
|
|
820
|
+
}
|
|
821
|
+
catch (_a) {
|
|
822
|
+
/* fall back to the address-keyed marker */
|
|
823
|
+
}
|
|
824
|
+
return (announcementState(announceKey(this.formo.writeKey, address), connection) !==
|
|
825
|
+
"none");
|
|
826
|
+
};
|
|
148
827
|
/**
|
|
149
828
|
* Handle status changes (connect/disconnect)
|
|
150
829
|
*/
|
|
151
830
|
WagmiEventHandler.prototype.handleStatusChange = function (status, prevStatus) {
|
|
152
831
|
return __awaiter(this, void 0, void 0, function () {
|
|
153
|
-
var state, address, chainId, connectorName, error_1;
|
|
154
|
-
|
|
155
|
-
|
|
832
|
+
var state, address, chainId, disconnectedAddress, disconnectedChainId, walletKey, connection, connectorName, error_1, error_2;
|
|
833
|
+
var _a, _b, _c, _d;
|
|
834
|
+
return __generator(this, function (_e) {
|
|
835
|
+
switch (_e.label) {
|
|
156
836
|
case 0:
|
|
157
837
|
// Prevent concurrent processing
|
|
158
838
|
if (this.trackingState.isProcessing) {
|
|
839
|
+
// Dropped, not queued - but remember that a disconnect went past.
|
|
840
|
+
//
|
|
841
|
+
// Reconciliation compares only the FINAL state, so a wallet that
|
|
842
|
+
// disconnects and reconnects entirely inside this window looks
|
|
843
|
+
// unchanged and neither its disconnect nor its genuine reconnect is
|
|
844
|
+
// ever reported. Recording the transient lets reconciliation tell that
|
|
845
|
+
// cycle apart from nothing having happened at all.
|
|
846
|
+
if (status === "disconnected") {
|
|
847
|
+
this.missedDisconnect = true;
|
|
848
|
+
}
|
|
159
849
|
logger_1.logger.debug("WagmiEventHandler: Already processing status change, skipping");
|
|
160
850
|
return [2 /*return*/];
|
|
161
851
|
}
|
|
162
852
|
this.trackingState.isProcessing = true;
|
|
163
|
-
|
|
853
|
+
// A status change outranks any connection transition still in flight. A
|
|
854
|
+
// full disconnect advances no ticket of its own, so without this an older
|
|
855
|
+
// fallback continuation could resume and announce a wallet wagmi no longer
|
|
856
|
+
// has.
|
|
857
|
+
this.transitionGeneration += 1;
|
|
858
|
+
_e.label = 1;
|
|
164
859
|
case 1:
|
|
165
|
-
|
|
860
|
+
_e.trys.push([1, 14, 15, 16]);
|
|
166
861
|
state = this.getState();
|
|
167
862
|
address = this.getConnectedAddress(state);
|
|
168
|
-
chainId = state.chainId;
|
|
863
|
+
chainId = (_a = this.getActiveConnectionChainId(state)) !== null && _a !== void 0 ? _a : state.chainId;
|
|
169
864
|
logger_1.logger.info("WagmiEventHandler: Status changed", {
|
|
170
865
|
status: status,
|
|
171
866
|
prevStatus: prevStatus,
|
|
172
867
|
address: address,
|
|
173
868
|
chainId: chainId,
|
|
174
869
|
});
|
|
175
|
-
if (!(status === "disconnected" &&
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
this.
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
870
|
+
if (!(status === "disconnected" && this.trackingState.lastAddress)) return [3 /*break*/, 4];
|
|
871
|
+
disconnectedAddress = this.trackingState.lastAddress;
|
|
872
|
+
disconnectedChainId = this.trackingState.lastChainId;
|
|
873
|
+
this.trackingState.lastConnectionId = undefined;
|
|
874
|
+
this.trackingState.lastAddress = undefined;
|
|
875
|
+
this.trackingState.lastChainId = undefined;
|
|
876
|
+
this.pendingChainId = undefined;
|
|
877
|
+
this.missedDisconnect = false;
|
|
878
|
+
// A real disconnect ends the adoption, so a genuine reconnect later in
|
|
879
|
+
// this same page load emits again.
|
|
880
|
+
announcedConnections.delete(announceKey(this.formo.writeKey, disconnectedAddress));
|
|
881
|
+
if (!(this.formo.isAutocaptureEnabled("disconnect") && this.isEmittingOwner)) return [3 /*break*/, 3];
|
|
182
882
|
return [4 /*yield*/, this.formo.disconnect({
|
|
183
|
-
chainId:
|
|
184
|
-
address:
|
|
883
|
+
chainId: disconnectedChainId,
|
|
884
|
+
address: disconnectedAddress,
|
|
185
885
|
})];
|
|
186
886
|
case 2:
|
|
187
|
-
|
|
188
|
-
|
|
887
|
+
_e.sent();
|
|
888
|
+
return [3 /*break*/, 4];
|
|
189
889
|
case 3:
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
890
|
+
// Nothing to emit, but central state must still be cleared so a
|
|
891
|
+
// later event cannot carry a stale excluded/!excluded chainId.
|
|
892
|
+
this.formo.syncWalletState({ chainId: disconnectedChainId });
|
|
893
|
+
_e.label = 4;
|
|
193
894
|
case 4:
|
|
194
|
-
if (!(status === "connected" && prevStatus !== "connected")) return [3 /*break*/,
|
|
195
|
-
if (!(address && chainId !== undefined)) return [3 /*break*/,
|
|
895
|
+
if (!(status === "connected" && prevStatus !== "connected")) return [3 /*break*/, 13];
|
|
896
|
+
if (!(address && chainId !== undefined)) return [3 /*break*/, 13];
|
|
897
|
+
if (!(((_b = this.trackingState.lastAddress) === null || _b === void 0 ? void 0 : _b.toLowerCase()) ===
|
|
898
|
+
address.toLowerCase())) return [3 /*break*/, 9];
|
|
899
|
+
if (!(this.trackingState.lastChainId !== chainId)) return [3 /*break*/, 7];
|
|
900
|
+
if (!(this.pendingChainId === chainId)) return [3 /*break*/, 6];
|
|
901
|
+
this.pendingChainId = undefined;
|
|
902
|
+
logger_1.logger.info("WagmiEventHandler: Re-entry carries a chain the subscription dropped, emitting it", { address: address, from: this.trackingState.lastChainId, to: chainId });
|
|
903
|
+
return [4 /*yield*/, this.handleChainChange(chainId, this.trackingState.lastChainId)];
|
|
904
|
+
case 5:
|
|
905
|
+
_e.sent();
|
|
906
|
+
this.trackingState.lastStatus = status;
|
|
907
|
+
return [2 /*return*/];
|
|
908
|
+
case 6:
|
|
909
|
+
logger_1.logger.info("WagmiEventHandler: Tracked wallet re-entered connected on a different chain", { address: address, from: this.trackingState.lastChainId, to: chainId });
|
|
910
|
+
// Sync first, then adopt only if central state accepted it.
|
|
911
|
+
// Writing the private chain first would let the mutation
|
|
912
|
+
// handlers label events with a chain that `shouldTrack()` is
|
|
913
|
+
// still excluding, because that gate reads the central field
|
|
914
|
+
// rather than the event payload.
|
|
915
|
+
this.formo.syncWalletState({ chainId: chainId, address: address });
|
|
916
|
+
if (((_c = this.formo.currentAddress) === null || _c === void 0 ? void 0 : _c.toLowerCase()) ===
|
|
917
|
+
address.toLowerCase()) {
|
|
918
|
+
this.trackingState.lastChainId = chainId;
|
|
919
|
+
}
|
|
920
|
+
else {
|
|
921
|
+
logger_1.logger.debug("WagmiEventHandler: Central state declined the re-entry chain, dropping the wallet", { address: address, chainId: chainId });
|
|
922
|
+
this.releaseTrackedWallet();
|
|
923
|
+
}
|
|
924
|
+
return [3 /*break*/, 8];
|
|
925
|
+
case 7:
|
|
926
|
+
logger_1.logger.debug("WagmiEventHandler: Ignoring re-entry to connected for an already tracked wallet", { address: address, chainId: chainId });
|
|
927
|
+
_e.label = 8;
|
|
928
|
+
case 8:
|
|
929
|
+
this.trackingState.lastStatus = status;
|
|
930
|
+
return [2 /*return*/];
|
|
931
|
+
case 9:
|
|
932
|
+
// Sync central state first so tracking.excludeChains is enforced
|
|
933
|
+
// even when connect autocapture is disabled.
|
|
934
|
+
this.formo.syncWalletState({ chainId: chainId, address: address });
|
|
935
|
+
// Same rule as the seed and the account-switch path: while tracking
|
|
936
|
+
// is suppressed, syncWalletState refuses to learn a wallet, and
|
|
937
|
+
// retaining it privately would let the mutation handlers attribute
|
|
938
|
+
// events to an address the SDK declined to know.
|
|
939
|
+
if (((_d = this.formo.currentAddress) === null || _d === void 0 ? void 0 : _d.toLowerCase()) !== address.toLowerCase()) {
|
|
940
|
+
logger_1.logger.debug("WagmiEventHandler: Central state declined the connection, not adopting", { address: address, chainId: chainId });
|
|
941
|
+
this.trackingState.lastStatus = status;
|
|
942
|
+
return [2 /*return*/];
|
|
943
|
+
}
|
|
944
|
+
// A wallet superseded without an intervening `disconnected` - a
|
|
945
|
+
// `reconnecting` flap landing on a different account - is handled by
|
|
946
|
+
// `reconcileWithLiveState()`, which releases the old wallet (and its
|
|
947
|
+
// marker) before re-seeding. Nothing to do here.
|
|
196
948
|
this.trackingState.lastAddress = address;
|
|
197
949
|
this.trackingState.lastChainId = chainId;
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
950
|
+
this.trackingState.lastConnectionId = state.current;
|
|
951
|
+
if (!(this.formo.isAutocaptureEnabled("connect") &&
|
|
952
|
+
this.isEmittingOwner &&
|
|
953
|
+
this.formo.willTrackEvent(chainId))) return [3 /*break*/, 13];
|
|
954
|
+
walletKey = announceKey(this.formo.writeKey, address);
|
|
955
|
+
connection = state.current
|
|
956
|
+
? state.connections.get(state.current)
|
|
957
|
+
: undefined;
|
|
958
|
+
if (announcementState(walletKey, connection) !== "none") {
|
|
959
|
+
logger_1.logger.debug("WagmiEventHandler: Connect already announced for this wallet, not emitting again", { address: address });
|
|
960
|
+
this.trackingState.lastStatus = status;
|
|
961
|
+
return [2 /*return*/];
|
|
962
|
+
}
|
|
963
|
+
markAnnounced(walletKey, connection);
|
|
202
964
|
connectorName = this.getConnectorName(state);
|
|
965
|
+
_e.label = 10;
|
|
966
|
+
case 10:
|
|
967
|
+
_e.trys.push([10, 12, , 13]);
|
|
203
968
|
return [4 /*yield*/, this.formo.connect({ chainId: chainId, address: address }, __assign({}, (connectorName && { providerName: connectorName })))];
|
|
204
|
-
case
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
case
|
|
969
|
+
case 11:
|
|
970
|
+
_e.sent();
|
|
971
|
+
return [3 /*break*/, 13];
|
|
972
|
+
case 12:
|
|
973
|
+
error_1 = _e.sent();
|
|
974
|
+
// As in the seed: an emission that failed reported nothing, so
|
|
975
|
+
// the claim must not stand.
|
|
976
|
+
releaseAnnouncement(walletKey, connection);
|
|
977
|
+
throw error_1;
|
|
978
|
+
case 13:
|
|
208
979
|
this.trackingState.lastStatus = status;
|
|
209
|
-
return [3 /*break*/,
|
|
210
|
-
case
|
|
211
|
-
|
|
212
|
-
logger_1.logger.error("WagmiEventHandler: Error handling status change:",
|
|
213
|
-
return [3 /*break*/,
|
|
214
|
-
case
|
|
980
|
+
return [3 /*break*/, 16];
|
|
981
|
+
case 14:
|
|
982
|
+
error_2 = _e.sent();
|
|
983
|
+
logger_1.logger.error("WagmiEventHandler: Error handling status change:", error_2);
|
|
984
|
+
return [3 /*break*/, 16];
|
|
985
|
+
case 15:
|
|
215
986
|
this.trackingState.isProcessing = false;
|
|
987
|
+
// A status change that arrived while the lock was held was dropped, not
|
|
988
|
+
// queued. Reconcile against live state so the handler cannot be left
|
|
989
|
+
// permanently disagreeing with wagmi.
|
|
990
|
+
this.reconcileWithLiveState();
|
|
216
991
|
return [7 /*endfinally*/];
|
|
217
|
-
case
|
|
992
|
+
case 16: return [2 /*return*/];
|
|
993
|
+
}
|
|
994
|
+
});
|
|
995
|
+
});
|
|
996
|
+
};
|
|
997
|
+
/**
|
|
998
|
+
* Re-run the status logic when the handler's view diverges from wagmi.
|
|
999
|
+
*
|
|
1000
|
+
* `handleStatusChange()` drops - rather than defers - anything that arrives
|
|
1001
|
+
* while it holds `isProcessing`, and it holds that lock across an awaited
|
|
1002
|
+
* `connect()` / `disconnect()` emission. A wallet that reconnects inside
|
|
1003
|
+
* that window would otherwise leave the handler believing it is
|
|
1004
|
+
* disconnected for the rest of the page load, silently dropping every later
|
|
1005
|
+
* signature and transaction. The mirror case leaves a disconnected wallet
|
|
1006
|
+
* marked connected.
|
|
1007
|
+
*
|
|
1008
|
+
* Converges: each pass either adopts the live wallet or releases the tracked
|
|
1009
|
+
* one, so the next pass finds the two in agreement and returns.
|
|
1010
|
+
*/
|
|
1011
|
+
WagmiEventHandler.prototype.reconcileWithLiveState = function () {
|
|
1012
|
+
var _this = this;
|
|
1013
|
+
if (this.disposed || this.trackingState.isProcessing)
|
|
1014
|
+
return;
|
|
1015
|
+
if (this.reconciling)
|
|
1016
|
+
return;
|
|
1017
|
+
var state;
|
|
1018
|
+
try {
|
|
1019
|
+
state = this.getState();
|
|
1020
|
+
}
|
|
1021
|
+
catch (error) {
|
|
1022
|
+
logger_1.logger.error("WagmiEventHandler: Error reading state to reconcile:", error);
|
|
1023
|
+
return;
|
|
1024
|
+
}
|
|
1025
|
+
// `connecting` and `reconnecting` are transient: the wallet has not gone
|
|
1026
|
+
// anywhere, wagmi is mid-handshake. Treating them as disconnected made
|
|
1027
|
+
// reconciliation synthesize a disconnect for an ordinary reconnect flap
|
|
1028
|
+
// and then emit a second connect when it completed - two spurious events
|
|
1029
|
+
// for a wallet that never left.
|
|
1030
|
+
if (state.status !== "connected" && state.status !== "disconnected") {
|
|
1031
|
+
return;
|
|
1032
|
+
}
|
|
1033
|
+
var live = state.status === "connected" ? this.getConnectedAddress(state) : undefined;
|
|
1034
|
+
var tracked = this.trackingState.lastAddress;
|
|
1035
|
+
if (!live && !tracked)
|
|
1036
|
+
return;
|
|
1037
|
+
if (live && tracked) {
|
|
1038
|
+
if (live.toLowerCase() === tracked.toLowerCase()) {
|
|
1039
|
+
if (this.missedDisconnect) {
|
|
1040
|
+
// The wallet went away and came back entirely while the lock was
|
|
1041
|
+
// held. The addresses match, but this is a new session: report the
|
|
1042
|
+
// disconnect, then let the reconnect announce itself.
|
|
1043
|
+
this.missedDisconnect = false;
|
|
1044
|
+
logger_1.logger.info("WagmiEventHandler: A disconnect/reconnect cycle completed while the lock was held", { address: live });
|
|
1045
|
+
this.reconciling = true;
|
|
1046
|
+
try {
|
|
1047
|
+
void this.handleStatusChange("disconnected", "connected").then(function () {
|
|
1048
|
+
return _this.retryAdoption();
|
|
1049
|
+
});
|
|
1050
|
+
}
|
|
1051
|
+
finally {
|
|
1052
|
+
this.reconciling = false;
|
|
1053
|
+
}
|
|
1054
|
+
return;
|
|
1055
|
+
}
|
|
1056
|
+
// Same wallet. Replay a chain change the chain callback had to drop
|
|
1057
|
+
// because wagmi was still `reconnecting` at the time - the status
|
|
1058
|
+
// callback that would have covered it was then dropped by the
|
|
1059
|
+
// processing guard, so without this both the tracked and the central
|
|
1060
|
+
// chain keep describing the chain the wallet has already left.
|
|
1061
|
+
//
|
|
1062
|
+
// Deliberately keyed on a chain the callback actually dropped rather
|
|
1063
|
+
// than on any difference: in the ordinary flap the chain subscription
|
|
1064
|
+
// still owns the emission, and reconciling on difference alone would
|
|
1065
|
+
// double count it.
|
|
1066
|
+
// A disconnect that completed after a newer transition re-adopted this
|
|
1067
|
+
// same wallet clears the central namespace on its way out. Private
|
|
1068
|
+
// tracking still names the wallet while central state is empty, and
|
|
1069
|
+
// `trackEvent()` reads the central one - so later events lose their
|
|
1070
|
+
// wallet entirely. Put it back.
|
|
1071
|
+
this.resyncCentralState();
|
|
1072
|
+
var dropped = this.pendingChainId;
|
|
1073
|
+
if (dropped !== undefined) {
|
|
1074
|
+
this.pendingChainId = undefined;
|
|
1075
|
+
if (dropped !== this.trackingState.lastChainId) {
|
|
1076
|
+
logger_1.logger.info("WagmiEventHandler: Replaying a chain change dropped while reconnecting", { address: live, from: this.trackingState.lastChainId, to: dropped });
|
|
1077
|
+
void this.handleChainChange(dropped, this.trackingState.lastChainId);
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
return;
|
|
1081
|
+
}
|
|
1082
|
+
// Different wallet. Wagmi moved from one to another while the lock was
|
|
1083
|
+
// held, so BOTH status callbacks were dropped. Returning here would
|
|
1084
|
+
// leave every later signature and transaction attributed to the wallet
|
|
1085
|
+
// the user has already left.
|
|
1086
|
+
logger_1.logger.info("WagmiEventHandler: Wallet changed while the lock was held, re-adopting", { from: tracked, to: live });
|
|
1087
|
+
this.reconciling = true;
|
|
1088
|
+
try {
|
|
1089
|
+
this.releaseTrackedWallet();
|
|
1090
|
+
this.seedFromCurrentState();
|
|
1091
|
+
}
|
|
1092
|
+
finally {
|
|
1093
|
+
this.reconciling = false;
|
|
1094
|
+
}
|
|
1095
|
+
return;
|
|
1096
|
+
}
|
|
1097
|
+
this.reconciling = true;
|
|
1098
|
+
try {
|
|
1099
|
+
if (live) {
|
|
1100
|
+
logger_1.logger.info("WagmiEventHandler: Reconnect landed while the lock was held, adopting it", { address: live });
|
|
1101
|
+
this.retryAdoption();
|
|
1102
|
+
}
|
|
1103
|
+
else {
|
|
1104
|
+
logger_1.logger.info("WagmiEventHandler: Disconnect landed while the lock was held, applying it", { address: tracked });
|
|
1105
|
+
void this.handleStatusChange("disconnected", "connected");
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
finally {
|
|
1109
|
+
this.reconciling = false;
|
|
1110
|
+
}
|
|
1111
|
+
};
|
|
1112
|
+
/**
|
|
1113
|
+
* Handle a switch to a different account on an already-connected wallet.
|
|
1114
|
+
*
|
|
1115
|
+
* Only fires for a genuine in-place switch. A fresh connect and a disconnect
|
|
1116
|
+
* both move `state.status`, and that listener is registered first, so it has
|
|
1117
|
+
* already recorded the new address synchronously by the time this runs. The
|
|
1118
|
+
* `lastAddress` check below is what makes the two paths mutually exclusive.
|
|
1119
|
+
*/
|
|
1120
|
+
WagmiEventHandler.prototype.handleActiveAddressChange = function (address, prevAddress) {
|
|
1121
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
1122
|
+
var state, trackedConnectionId, trackedConnectionGone, connectionChanged, liveChain, generation, chainId, stillConnected, goneChainId, error_3, liveNow, stillCurrent, walletKey, connection, alreadyAnnounced, connectorName, error_4;
|
|
1123
|
+
var _a, _b, _c, _d, _e, _f;
|
|
1124
|
+
return __generator(this, function (_g) {
|
|
1125
|
+
switch (_g.label) {
|
|
1126
|
+
case 0:
|
|
1127
|
+
state = this.getState();
|
|
1128
|
+
if (state.status !== "connected")
|
|
1129
|
+
return [2 /*return*/];
|
|
1130
|
+
trackedConnectionId = this.trackingState.lastConnectionId;
|
|
1131
|
+
trackedConnectionGone = !!trackedConnectionId && !state.connections.has(trackedConnectionId);
|
|
1132
|
+
connectionChanged = this.trackingState.lastConnectionId !== undefined &&
|
|
1133
|
+
state.current !== undefined &&
|
|
1134
|
+
state.current !== this.trackingState.lastConnectionId;
|
|
1135
|
+
if (!trackedConnectionGone && !connectionChanged) {
|
|
1136
|
+
if (!address || address === prevAddress)
|
|
1137
|
+
return [2 /*return*/];
|
|
1138
|
+
// Already handled by the status listener (fresh connect, or the seed).
|
|
1139
|
+
// Case-insensitive, like every other address comparison here.
|
|
1140
|
+
if (((_a = this.trackingState.lastAddress) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === address.toLowerCase()) {
|
|
1141
|
+
return [2 /*return*/];
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
if (!(connectionChanged &&
|
|
1145
|
+
!trackedConnectionGone &&
|
|
1146
|
+
address &&
|
|
1147
|
+
((_b = this.trackingState.lastAddress) === null || _b === void 0 ? void 0 : _b.toLowerCase()) === address.toLowerCase())) return [3 /*break*/, 3];
|
|
1148
|
+
liveChain = (_c = this.getActiveConnectionChainId(state)) !== null && _c !== void 0 ? _c : state.chainId;
|
|
1149
|
+
logger_1.logger.info("WagmiEventHandler: Active connection changed for the same account", { address: address, from: this.trackingState.lastConnectionId, to: state.current });
|
|
1150
|
+
this.trackingState.lastConnectionId = state.current;
|
|
1151
|
+
if (!(liveChain !== undefined && liveChain !== this.trackingState.lastChainId)) return [3 /*break*/, 2];
|
|
1152
|
+
return [4 /*yield*/, this.applyChainForTrackedWallet(address, liveChain)];
|
|
1153
|
+
case 1:
|
|
1154
|
+
_g.sent();
|
|
1155
|
+
_g.label = 2;
|
|
1156
|
+
case 2: return [2 /*return*/];
|
|
1157
|
+
case 3:
|
|
1158
|
+
if (!address)
|
|
1159
|
+
return [2 /*return*/];
|
|
1160
|
+
generation = ++this.transitionGeneration;
|
|
1161
|
+
chainId = (_d = this.getActiveConnectionChainId(state)) !== null && _d !== void 0 ? _d : state.chainId;
|
|
1162
|
+
if (chainId === undefined)
|
|
1163
|
+
return [2 /*return*/];
|
|
1164
|
+
if (!trackedConnectionGone) return [3 /*break*/, 14];
|
|
1165
|
+
stillConnected = !!prevAddress && this.isAddressConnected(state, prevAddress);
|
|
1166
|
+
if (!stillConnected) return [3 /*break*/, 7];
|
|
1167
|
+
// The connector went away but the ACCOUNT did not: another live
|
|
1168
|
+
// connection still holds it. Nothing disconnected from the user's
|
|
1169
|
+
// point of view, so no event - just follow the connection.
|
|
1170
|
+
logger_1.logger.info("WagmiEventHandler: Tracked connector fell away, account still connected elsewhere", { address: prevAddress, connection: state.current });
|
|
1171
|
+
this.trackingState.lastConnectionId = state.current;
|
|
1172
|
+
if (!(((_e = this.trackingState.lastAddress) === null || _e === void 0 ? void 0 : _e.toLowerCase()) === address.toLowerCase())) return [3 /*break*/, 6];
|
|
1173
|
+
if (!(this.trackingState.lastChainId !== chainId)) return [3 /*break*/, 5];
|
|
1174
|
+
return [4 /*yield*/, this.applyChainForTrackedWallet(address, chainId)];
|
|
1175
|
+
case 4:
|
|
1176
|
+
_g.sent();
|
|
1177
|
+
_g.label = 5;
|
|
1178
|
+
case 5: return [2 /*return*/];
|
|
1179
|
+
case 6: return [3 /*break*/, 14];
|
|
1180
|
+
case 7:
|
|
1181
|
+
if (!prevAddress) return [3 /*break*/, 14];
|
|
1182
|
+
logger_1.logger.info("WagmiEventHandler: Tracked connector disconnected, wagmi fell back to another connection", { disconnected: prevAddress, fallback: address });
|
|
1183
|
+
goneChainId = this.trackingState.lastChainId;
|
|
1184
|
+
this.releaseTrackedWallet();
|
|
1185
|
+
if (!(this.formo.isAutocaptureEnabled("disconnect") && this.isEmittingOwner)) return [3 /*break*/, 12];
|
|
1186
|
+
_g.label = 8;
|
|
1187
|
+
case 8:
|
|
1188
|
+
_g.trys.push([8, 10, , 11]);
|
|
1189
|
+
// Emitted before central state is cleared, so excludeChains can
|
|
1190
|
+
// still gate it on the chain the wallet was actually on.
|
|
1191
|
+
return [4 /*yield*/, this.formo.disconnect({
|
|
1192
|
+
chainId: goneChainId,
|
|
1193
|
+
address: prevAddress,
|
|
1194
|
+
})];
|
|
1195
|
+
case 9:
|
|
1196
|
+
// Emitted before central state is cleared, so excludeChains can
|
|
1197
|
+
// still gate it on the chain the wallet was actually on.
|
|
1198
|
+
_g.sent();
|
|
1199
|
+
return [3 /*break*/, 11];
|
|
1200
|
+
case 10:
|
|
1201
|
+
error_3 = _g.sent();
|
|
1202
|
+
logger_1.logger.error("WagmiEventHandler: Error tracking connector fallback disconnect:", error_3);
|
|
1203
|
+
return [3 /*break*/, 11];
|
|
1204
|
+
case 11: return [3 /*break*/, 13];
|
|
1205
|
+
case 12:
|
|
1206
|
+
this.formo.syncWalletState({ chainId: goneChainId });
|
|
1207
|
+
_g.label = 13;
|
|
1208
|
+
case 13:
|
|
1209
|
+
liveNow = void 0;
|
|
1210
|
+
try {
|
|
1211
|
+
liveNow = this.getState();
|
|
1212
|
+
}
|
|
1213
|
+
catch (_h) {
|
|
1214
|
+
/* treated as superseded below */
|
|
1215
|
+
}
|
|
1216
|
+
stillCurrent = !this.disposed &&
|
|
1217
|
+
(liveNow === null || liveNow === void 0 ? void 0 : liveNow.status) === "connected" &&
|
|
1218
|
+
liveNow.current === state.current;
|
|
1219
|
+
if (generation !== this.transitionGeneration || !stillCurrent) {
|
|
1220
|
+
logger_1.logger.debug("WagmiEventHandler: A newer transition superseded this one, stopping", { address: address });
|
|
1221
|
+
// `disconnect()` clears the central wallet namespace as it
|
|
1222
|
+
// completes, and it has just done so on top of whatever the newer
|
|
1223
|
+
// transition adopted. Private tracking still names the new wallet
|
|
1224
|
+
// while central state is empty, which silently drops the address and
|
|
1225
|
+
// chain from every later event. Put back what is actually live.
|
|
1226
|
+
this.restoreCentralStateFromTracking();
|
|
1227
|
+
return [2 /*return*/];
|
|
1228
|
+
}
|
|
1229
|
+
_g.label = 14;
|
|
1230
|
+
case 14:
|
|
1231
|
+
logger_1.logger.info("WagmiEventHandler: Active account switched", {
|
|
1232
|
+
from: prevAddress,
|
|
1233
|
+
to: address,
|
|
1234
|
+
chainId: chainId,
|
|
1235
|
+
});
|
|
1236
|
+
// Sync central state first so tracking.excludeChains is enforced even when
|
|
1237
|
+
// connect autocapture is disabled.
|
|
1238
|
+
this.formo.syncWalletState({ chainId: chainId, address: address });
|
|
1239
|
+
// Same rule as the seed: while tracking is suppressed, syncWalletState
|
|
1240
|
+
// refuses to learn a wallet, and retaining it privately would let the
|
|
1241
|
+
// mutation handlers attribute events to an address the SDK declined to
|
|
1242
|
+
// know. Compared case-insensitively because the checksummed form is stored.
|
|
1243
|
+
if (((_f = this.formo.currentAddress) === null || _f === void 0 ? void 0 : _f.toLowerCase()) !== address.toLowerCase()) {
|
|
1244
|
+
logger_1.logger.debug("WagmiEventHandler: Central state declined the switched account, not adopting", { address: address, chainId: chainId });
|
|
1245
|
+
// Drop the previous wallet too. The user has moved off it, so leaving it
|
|
1246
|
+
// in tracking state would attribute this account's later signatures and
|
|
1247
|
+
// transactions to the account they switched away from - worse than
|
|
1248
|
+
// recording nothing.
|
|
1249
|
+
this.releaseTrackedWallet();
|
|
1250
|
+
return [2 /*return*/];
|
|
1251
|
+
}
|
|
1252
|
+
this.trackingState.lastAddress = address;
|
|
1253
|
+
this.trackingState.lastChainId = chainId;
|
|
1254
|
+
this.trackingState.lastConnectionId = state.current;
|
|
1255
|
+
if (!(this.formo.isAutocaptureEnabled("connect") &&
|
|
1256
|
+
this.isEmittingOwner &&
|
|
1257
|
+
this.formo.willTrackEvent(chainId))) return [3 /*break*/, 18];
|
|
1258
|
+
// Move the page-load marker onto the wallet that is now active.
|
|
1259
|
+
//
|
|
1260
|
+
// Gated on ownership as well, like the ordinary connect path. A
|
|
1261
|
+
// non-owner that marked here would make the real owner find the marker
|
|
1262
|
+
// and stay silent, and the non-owner's own emission dies with its queue
|
|
1263
|
+
// when that instance is torn down - losing the connect entirely.
|
|
1264
|
+
//
|
|
1265
|
+
// Only drop the previous wallet's marker when that wallet is genuinely
|
|
1266
|
+
// gone. On an in-place account switch the old account leaves the
|
|
1267
|
+
// connection and must be able to announce itself again later. But with
|
|
1268
|
+
// several connections the previous wallet is often still connected -
|
|
1269
|
+
// it merely stopped being `state.current` - and forgetting it there
|
|
1270
|
+
// would make it re-announce every time wagmi switched back.
|
|
1271
|
+
if (prevAddress && !this.isAddressConnected(state, prevAddress)) {
|
|
1272
|
+
announcedConnections.delete(announceKey(this.formo.writeKey, prevAddress));
|
|
1273
|
+
}
|
|
1274
|
+
walletKey = announceKey(this.formo.writeKey, address);
|
|
1275
|
+
connection = state.current
|
|
1276
|
+
? state.connections.get(state.current)
|
|
1277
|
+
: undefined;
|
|
1278
|
+
alreadyAnnounced = announcementState(walletKey, connection) !== "none";
|
|
1279
|
+
markAnnounced(walletKey, connection);
|
|
1280
|
+
if (alreadyAnnounced) {
|
|
1281
|
+
// Reached by the connector-fallback path above: wagmi made a
|
|
1282
|
+
// connection active that this page load already reported. Becoming
|
|
1283
|
+
// active is not a new connect.
|
|
1284
|
+
logger_1.logger.debug("WagmiEventHandler: Fallback connection was already announced, not re-emitting connect", { address: address });
|
|
1285
|
+
return [2 /*return*/];
|
|
1286
|
+
}
|
|
1287
|
+
_g.label = 15;
|
|
1288
|
+
case 15:
|
|
1289
|
+
_g.trys.push([15, 17, , 18]);
|
|
1290
|
+
connectorName = this.getConnectorName(state);
|
|
1291
|
+
return [4 /*yield*/, this.formo.connect({ chainId: chainId, address: address }, __assign({}, (connectorName && { providerName: connectorName })))];
|
|
1292
|
+
case 16:
|
|
1293
|
+
_g.sent();
|
|
1294
|
+
return [3 /*break*/, 18];
|
|
1295
|
+
case 17:
|
|
1296
|
+
error_4 = _g.sent();
|
|
1297
|
+
releaseAnnouncement(walletKey, connection);
|
|
1298
|
+
logger_1.logger.error("WagmiEventHandler: Error tracking account switch:", error_4);
|
|
1299
|
+
return [3 /*break*/, 18];
|
|
1300
|
+
case 18: return [2 /*return*/];
|
|
1301
|
+
}
|
|
1302
|
+
});
|
|
1303
|
+
});
|
|
1304
|
+
};
|
|
1305
|
+
/**
|
|
1306
|
+
* Record and emit a chain move for the wallet already being tracked.
|
|
1307
|
+
*
|
|
1308
|
+
* Shared by `handleChainChange()` and the connector-fallback path, which
|
|
1309
|
+
* has to apply the chain itself: the chain callback defers while the new
|
|
1310
|
+
* connection is not adopted yet, so nothing else would.
|
|
1311
|
+
*/
|
|
1312
|
+
WagmiEventHandler.prototype.applyChainForTrackedWallet = function (address, chainId) {
|
|
1313
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
1314
|
+
var error_5;
|
|
1315
|
+
var _a;
|
|
1316
|
+
return __generator(this, function (_b) {
|
|
1317
|
+
switch (_b.label) {
|
|
1318
|
+
case 0:
|
|
1319
|
+
// Sync central state unconditionally so a switch to an excluded chain is
|
|
1320
|
+
// honored even when chain autocapture is disabled.
|
|
1321
|
+
this.formo.syncWalletState({ chainId: chainId, address: address });
|
|
1322
|
+
if (((_a = this.formo.currentAddress) === null || _a === void 0 ? void 0 : _a.toLowerCase()) !== address.toLowerCase()) {
|
|
1323
|
+
logger_1.logger.debug("WagmiEventHandler: Central state declined the new chain, dropping the wallet", { address: address, chainId: chainId });
|
|
1324
|
+
this.releaseTrackedWallet();
|
|
1325
|
+
return [2 /*return*/];
|
|
1326
|
+
}
|
|
1327
|
+
this.trackingState.lastChainId = chainId;
|
|
1328
|
+
if (!(this.formo.isAutocaptureEnabled("chain") && this.isEmittingOwner)) return [3 /*break*/, 4];
|
|
1329
|
+
_b.label = 1;
|
|
1330
|
+
case 1:
|
|
1331
|
+
_b.trys.push([1, 3, , 4]);
|
|
1332
|
+
return [4 /*yield*/, this.formo.chain({ chainId: chainId, address: address })];
|
|
1333
|
+
case 2:
|
|
1334
|
+
_b.sent();
|
|
1335
|
+
return [3 /*break*/, 4];
|
|
1336
|
+
case 3:
|
|
1337
|
+
error_5 = _b.sent();
|
|
1338
|
+
logger_1.logger.error("WagmiEventHandler: Error tracking chain change:", error_5);
|
|
1339
|
+
return [3 /*break*/, 4];
|
|
1340
|
+
case 4: return [2 /*return*/];
|
|
218
1341
|
}
|
|
219
1342
|
});
|
|
220
1343
|
});
|
|
@@ -224,7 +1347,7 @@ var WagmiEventHandler = /** @class */ (function () {
|
|
|
224
1347
|
*/
|
|
225
1348
|
WagmiEventHandler.prototype.handleChainChange = function (chainId, prevChainId) {
|
|
226
1349
|
return __awaiter(this, void 0, void 0, function () {
|
|
227
|
-
var state, address,
|
|
1350
|
+
var state, address, hadAddress, wasAnnounced, announcedNow;
|
|
228
1351
|
return __generator(this, function (_a) {
|
|
229
1352
|
switch (_a.label) {
|
|
230
1353
|
case 0:
|
|
@@ -233,35 +1356,48 @@ var WagmiEventHandler = /** @class */ (function () {
|
|
|
233
1356
|
}
|
|
234
1357
|
state = this.getState();
|
|
235
1358
|
if (state.status !== "connected") {
|
|
1359
|
+
this.pendingChainId = chainId;
|
|
236
1360
|
return [2 /*return*/];
|
|
237
1361
|
}
|
|
1362
|
+
this.pendingChainId = undefined;
|
|
238
1363
|
address = this.getConnectedAddress(state);
|
|
239
1364
|
if (!address) {
|
|
240
1365
|
logger_1.logger.warn("WagmiEventHandler: Chain changed but no address found");
|
|
241
1366
|
return [2 /*return*/];
|
|
242
1367
|
}
|
|
1368
|
+
// Ignore a chain that belongs to a connection this handler has not adopted
|
|
1369
|
+
// yet.
|
|
1370
|
+
//
|
|
1371
|
+
// Both subscriptions fire for one wagmi update and this one can run first.
|
|
1372
|
+
// During a connector fallback that would overwrite `lastChainId` with the
|
|
1373
|
+
// INCOMING connection's chain, so the outgoing wallet's disconnect - which
|
|
1374
|
+
// the connection handler is about to emit - would be labelled with the
|
|
1375
|
+
// chain of the wallet that replaced it, and a spurious `chain` event would
|
|
1376
|
+
// be emitted for a wallet not yet adopted. The connection handler owns
|
|
1377
|
+
// that transition and records the chain itself.
|
|
1378
|
+
if (this.trackingState.lastConnectionId !== undefined &&
|
|
1379
|
+
state.current !== undefined &&
|
|
1380
|
+
state.current !== this.trackingState.lastConnectionId) {
|
|
1381
|
+
logger_1.logger.debug("WagmiEventHandler: Chain change belongs to a connection not adopted yet, deferring", { chainId: chainId, from: this.trackingState.lastConnectionId, to: state.current });
|
|
1382
|
+
return [2 /*return*/];
|
|
1383
|
+
}
|
|
1384
|
+
hadAddress = !!this.trackingState.lastAddress;
|
|
1385
|
+
wasAnnounced = this.isCurrentWalletAnnounced();
|
|
1386
|
+
if (!hadAddress || !wasAnnounced) {
|
|
1387
|
+
this.retryAdoption();
|
|
1388
|
+
announcedNow = !wasAnnounced && this.isCurrentWalletAnnounced();
|
|
1389
|
+
if (announcedNow)
|
|
1390
|
+
return [2 /*return*/];
|
|
1391
|
+
}
|
|
243
1392
|
logger_1.logger.info("WagmiEventHandler: Chain changed", {
|
|
244
1393
|
chainId: chainId,
|
|
245
1394
|
prevChainId: prevChainId,
|
|
246
1395
|
address: address,
|
|
247
1396
|
});
|
|
248
|
-
this.
|
|
249
|
-
// Sync central state unconditionally so a chain switch to an
|
|
250
|
-
// excluded chain is honored even when chain autocapture is disabled.
|
|
251
|
-
this.formo.syncWalletState({ chainId: chainId, address: address });
|
|
252
|
-
if (!this.formo.isAutocaptureEnabled("chain")) return [3 /*break*/, 4];
|
|
253
|
-
_a.label = 1;
|
|
1397
|
+
return [4 /*yield*/, this.applyChainForTrackedWallet(address, chainId)];
|
|
254
1398
|
case 1:
|
|
255
|
-
_a.trys.push([1, 3, , 4]);
|
|
256
|
-
return [4 /*yield*/, this.formo.chain({ chainId: chainId, address: address })];
|
|
257
|
-
case 2:
|
|
258
1399
|
_a.sent();
|
|
259
|
-
return [
|
|
260
|
-
case 3:
|
|
261
|
-
error_2 = _a.sent();
|
|
262
|
-
logger_1.logger.error("WagmiEventHandler: Error tracking chain change:", error_2);
|
|
263
|
-
return [3 /*break*/, 4];
|
|
264
|
-
case 4: return [2 /*return*/];
|
|
1400
|
+
return [2 /*return*/];
|
|
265
1401
|
}
|
|
266
1402
|
});
|
|
267
1403
|
});
|
|
@@ -306,6 +1442,11 @@ var WagmiEventHandler = /** @class */ (function () {
|
|
|
306
1442
|
if (event.type !== "updated") {
|
|
307
1443
|
return;
|
|
308
1444
|
}
|
|
1445
|
+
// Overlapping handlers share one MutationCache/QueryCache, so without
|
|
1446
|
+
// this each of them emits for the same mutation.
|
|
1447
|
+
if (!this.isEmittingOwner) {
|
|
1448
|
+
return;
|
|
1449
|
+
}
|
|
309
1450
|
var query = event.query;
|
|
310
1451
|
var queryKey = query.queryKey;
|
|
311
1452
|
if (!queryKey || queryKey.length === 0) {
|
|
@@ -351,7 +1492,7 @@ var WagmiEventHandler = /** @class */ (function () {
|
|
|
351
1492
|
* Emits CONFIRMED or REVERTED transaction status
|
|
352
1493
|
*/
|
|
353
1494
|
WagmiEventHandler.prototype.handleTransactionReceiptQuery = function (query) {
|
|
354
|
-
var _a;
|
|
1495
|
+
var _a, _b;
|
|
355
1496
|
if (!this.formo.isAutocaptureEnabled("transaction")) {
|
|
356
1497
|
return;
|
|
357
1498
|
}
|
|
@@ -365,7 +1506,8 @@ var WagmiEventHandler = /** @class */ (function () {
|
|
|
365
1506
|
// Query key format: ['waitForTransactionReceipt', { hash, chainId, ... }]
|
|
366
1507
|
var params = queryKey[1];
|
|
367
1508
|
var transactionHash = params === null || params === void 0 ? void 0 : params.hash;
|
|
368
|
-
|
|
1509
|
+
// Resolved after the pending lookup below, so the broadcast chain wins.
|
|
1510
|
+
var queryChainId = params === null || params === void 0 ? void 0 : params.chainId;
|
|
369
1511
|
if (!transactionHash) {
|
|
370
1512
|
logger_1.logger.warn("WagmiEventHandler: Transaction receipt query but no hash found");
|
|
371
1513
|
return;
|
|
@@ -385,6 +1527,17 @@ var WagmiEventHandler = /** @class */ (function () {
|
|
|
385
1527
|
return;
|
|
386
1528
|
}
|
|
387
1529
|
var address = pendingTx.address;
|
|
1530
|
+
// The chain the transaction was actually broadcast on wins, because the
|
|
1531
|
+
// active chain can change between broadcast and receipt and would
|
|
1532
|
+
// otherwise relabel the confirmation.
|
|
1533
|
+
//
|
|
1534
|
+
// Order: a chain the caller named explicitly, then the receipt query's own
|
|
1535
|
+
// chain, then the chain we observed at broadcast time, then the current
|
|
1536
|
+
// one. The inferred broadcast chain sits below the query's because the
|
|
1537
|
+
// query names the chain the receipt was actually read from.
|
|
1538
|
+
var chainId = pendingTx.chainIdWasExplicit
|
|
1539
|
+
? pendingTx.chainId
|
|
1540
|
+
: (_a = queryChainId !== null && queryChainId !== void 0 ? queryChainId : pendingTx.chainId) !== null && _a !== void 0 ? _a : this.trackingState.lastChainId;
|
|
388
1541
|
if (!address) {
|
|
389
1542
|
logger_1.logger.warn("WagmiEventHandler: Transaction receipt query but no address available");
|
|
390
1543
|
return;
|
|
@@ -406,7 +1559,7 @@ var WagmiEventHandler = /** @class */ (function () {
|
|
|
406
1559
|
transactionHash: transactionHash,
|
|
407
1560
|
address: address,
|
|
408
1561
|
chainId: chainId,
|
|
409
|
-
blockNumber: (
|
|
1562
|
+
blockNumber: (_b = receipt === null || receipt === void 0 ? void 0 : receipt.blockNumber) === null || _b === void 0 ? void 0 : _b.toString(),
|
|
410
1563
|
});
|
|
411
1564
|
this.formo.transaction(__assign(__assign(__assign(__assign(__assign({ status: txStatus, chainId: chainId || 0, address: address, transactionHash: transactionHash }, ((pendingTx === null || pendingTx === void 0 ? void 0 : pendingTx.data) && { data: pendingTx.data })), ((pendingTx === null || pendingTx === void 0 ? void 0 : pendingTx.to) && { to: pendingTx.to })), ((pendingTx === null || pendingTx === void 0 ? void 0 : pendingTx.value) && { value: pendingTx.value })), ((pendingTx === null || pendingTx === void 0 ? void 0 : pendingTx.function_name) && { function_name: pendingTx.function_name })), ((pendingTx === null || pendingTx === void 0 ? void 0 : pendingTx.function_args) && { function_args: pendingTx.function_args })),
|
|
412
1565
|
// Spread function args as additional properties (only colliding keys are prefixed)
|
|
@@ -425,6 +1578,11 @@ var WagmiEventHandler = /** @class */ (function () {
|
|
|
425
1578
|
if (event.type !== "updated") {
|
|
426
1579
|
return;
|
|
427
1580
|
}
|
|
1581
|
+
// Overlapping handlers share one MutationCache/QueryCache, so without
|
|
1582
|
+
// this each of them emits for the same mutation.
|
|
1583
|
+
if (!this.isEmittingOwner) {
|
|
1584
|
+
return;
|
|
1585
|
+
}
|
|
428
1586
|
var mutation = event.mutation;
|
|
429
1587
|
var mutationKey = mutation.options.mutationKey;
|
|
430
1588
|
if (!mutationKey || mutationKey.length === 0) {
|
|
@@ -465,13 +1623,28 @@ var WagmiEventHandler = /** @class */ (function () {
|
|
|
465
1623
|
* Handle signature mutations (signMessage, signTypedData)
|
|
466
1624
|
*/
|
|
467
1625
|
WagmiEventHandler.prototype.handleSignatureMutation = function (mutationType, mutation) {
|
|
1626
|
+
var _a, _b, _c, _d;
|
|
468
1627
|
if (!this.formo.isAutocaptureEnabled("signature")) {
|
|
469
1628
|
return;
|
|
470
1629
|
}
|
|
471
1630
|
var state = mutation.state;
|
|
472
1631
|
var variables = state.variables || {};
|
|
473
|
-
|
|
474
|
-
|
|
1632
|
+
// An explicit per-call `account` wins over the active connection. wagmi
|
|
1633
|
+
// lets a caller sign with an account other than the current one, and the
|
|
1634
|
+
// tracked connection describes a different wallet in that case.
|
|
1635
|
+
//
|
|
1636
|
+
// For typed data the signed chain lives in the EIP-712 domain, not at the
|
|
1637
|
+
// top level: `signTypedData` takes `{ domain, types, primaryType, message }`
|
|
1638
|
+
// and the domain's `chainId` is what the signature is actually bound to.
|
|
1639
|
+
// Reading only `variables.chainId` labelled such a signature with the
|
|
1640
|
+
// wallet's current chain, which can be a different one entirely.
|
|
1641
|
+
// Falls back to 0 - "could not resolve" - rather than undefined. A caller
|
|
1642
|
+
// can name an account before any connection chain is known, and an
|
|
1643
|
+
// undefined chain slipped past the exclusion gate, which only refuses an
|
|
1644
|
+
// explicit 0. A chain-scoped event whose chain is unknown must fail
|
|
1645
|
+
// closed like every other.
|
|
1646
|
+
var chainId = (_d = (_c = (_b = normalizeChainId((_a = variables.domain) === null || _a === void 0 ? void 0 : _a.chainId)) !== null && _b !== void 0 ? _b : normalizeChainId(variables.chainId)) !== null && _c !== void 0 ? _c : this.trackingState.lastChainId) !== null && _d !== void 0 ? _d : 0;
|
|
1647
|
+
var address = resolveAccountAddress(variables.account) || this.trackingState.lastAddress;
|
|
475
1648
|
if (!address) {
|
|
476
1649
|
logger_1.logger.warn("WagmiEventHandler: Signature event but no address available");
|
|
477
1650
|
return;
|
|
@@ -525,15 +1698,26 @@ var WagmiEventHandler = /** @class */ (function () {
|
|
|
525
1698
|
}
|
|
526
1699
|
var state = mutation.state;
|
|
527
1700
|
var variables = state.variables || {};
|
|
528
|
-
|
|
529
|
-
//
|
|
530
|
-
//
|
|
531
|
-
//
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
1701
|
+
// Explicit per-call values win over the active connection. wagmi lets a
|
|
1702
|
+
// caller target another account or chain, and the tracked connection
|
|
1703
|
+
// describes a different wallet in that case. Fall back to the connection
|
|
1704
|
+
// for the usual call that omits them.
|
|
1705
|
+
//
|
|
1706
|
+
// For sendTransaction the user's address is `from`; for writeContract
|
|
1707
|
+
// `variables.address` is the contract, not the user.
|
|
1708
|
+
// Distinguish an explicitly requested chain from one merely inferred from
|
|
1709
|
+
// the active connection: only the former is authoritative for the receipt.
|
|
1710
|
+
// Normalized: viem accepts a hex string or a bigint for `chainId`, and a
|
|
1711
|
+
// non-number here would both land in the payload and defeat the numeric
|
|
1712
|
+
// `tracking.excludeChains` comparison.
|
|
1713
|
+
var explicitChainId = normalizeChainId(variables.chainId);
|
|
1714
|
+
// Left undefined on purpose. The emission below already coerces with
|
|
1715
|
+
// `chainId || 0`, and defaulting here instead would write 0 into the
|
|
1716
|
+
// pending-transaction record, where an absent chain is what lets the
|
|
1717
|
+
// receipt query's chain win for the confirmation.
|
|
1718
|
+
var chainId = explicitChainId !== null && explicitChainId !== void 0 ? explicitChainId : this.trackingState.lastChainId;
|
|
1719
|
+
var accountAddress = resolveAccountAddress(variables.account);
|
|
1720
|
+
var userAddress = accountAddress || variables.from || this.trackingState.lastAddress;
|
|
537
1721
|
if (!userAddress) {
|
|
538
1722
|
logger_1.logger.warn("WagmiEventHandler: Transaction event but no address available");
|
|
539
1723
|
return;
|
|
@@ -599,7 +1783,7 @@ var WagmiEventHandler = /** @class */ (function () {
|
|
|
599
1783
|
// Include the sender address to handle wallet switches between broadcast and confirmation
|
|
600
1784
|
if (status_2 === events_1.TransactionStatus.BROADCASTED && transactionHash) {
|
|
601
1785
|
var normalizedHash = transactionHash.toLowerCase();
|
|
602
|
-
var txDetails = __assign(__assign(__assign(__assign(__assign(__assign({ address: userAddress }, (data && { data: data })), (to && { to: to })), (value && { value: value })), (function_name && { function_name: function_name })), (function_args && { function_args: function_args })), (safeFunctionArgs && { safeFunctionArgs: safeFunctionArgs }));
|
|
1786
|
+
var txDetails = __assign(__assign(__assign(__assign(__assign(__assign(__assign(__assign({ address: userAddress }, (chainId !== undefined && { chainId: chainId })), { chainIdWasExplicit: explicitChainId !== undefined }), (data && { data: data })), (to && { to: to })), (value && { value: value })), (function_name && { function_name: function_name })), (function_args && { function_args: function_args })), (safeFunctionArgs && { safeFunctionArgs: safeFunctionArgs }));
|
|
603
1787
|
this.pendingTransactions.set(normalizedHash, txDetails);
|
|
604
1788
|
logger_1.logger.debug("WagmiEventHandler: Stored pending transaction for confirmation", {
|
|
605
1789
|
transactionHash: normalizedHash,
|
|
@@ -644,6 +1828,21 @@ var WagmiEventHandler = /** @class */ (function () {
|
|
|
644
1828
|
chainId: undefined,
|
|
645
1829
|
};
|
|
646
1830
|
};
|
|
1831
|
+
/**
|
|
1832
|
+
* Chain of the connection that is currently active.
|
|
1833
|
+
*
|
|
1834
|
+
* Authoritative, because `state.chainId` is a single global value. With
|
|
1835
|
+
* `syncConnectedChain: false` it stays on the chain the APP selected while
|
|
1836
|
+
* the connection reports the chain the WALLET is actually on, so seeding
|
|
1837
|
+
* from the global could label a wallet on an excluded chain as an allowed
|
|
1838
|
+
* one and send events the exclusion forbids.
|
|
1839
|
+
*/
|
|
1840
|
+
WagmiEventHandler.prototype.getActiveConnectionChainId = function (state) {
|
|
1841
|
+
var _a;
|
|
1842
|
+
if (!state.current)
|
|
1843
|
+
return undefined;
|
|
1844
|
+
return (_a = state.connections.get(state.current)) === null || _a === void 0 ? void 0 : _a.chainId;
|
|
1845
|
+
};
|
|
647
1846
|
/**
|
|
648
1847
|
* Get the currently connected address from Wagmi state
|
|
649
1848
|
*/
|
|
@@ -657,6 +1856,24 @@ var WagmiEventHandler = /** @class */ (function () {
|
|
|
657
1856
|
}
|
|
658
1857
|
return connection.accounts[0];
|
|
659
1858
|
};
|
|
1859
|
+
/**
|
|
1860
|
+
* Whether any live connection still holds this address.
|
|
1861
|
+
*
|
|
1862
|
+
* Distinguishes "the user switched away from this account" (gone from every
|
|
1863
|
+
* connection) from "this account merely stopped being the current one"
|
|
1864
|
+
* (still connected through its own connector).
|
|
1865
|
+
*/
|
|
1866
|
+
WagmiEventHandler.prototype.isAddressConnected = function (state, address) {
|
|
1867
|
+
var wanted = address.toLowerCase();
|
|
1868
|
+
var found = false;
|
|
1869
|
+
state.connections.forEach(function (connection) {
|
|
1870
|
+
if (!found &&
|
|
1871
|
+
connection.accounts.some(function (a) { return a.toLowerCase() === wanted; })) {
|
|
1872
|
+
found = true;
|
|
1873
|
+
}
|
|
1874
|
+
});
|
|
1875
|
+
return found;
|
|
1876
|
+
};
|
|
660
1877
|
/**
|
|
661
1878
|
* Get the connector name from Wagmi state
|
|
662
1879
|
*/
|
|
@@ -672,6 +1889,26 @@ var WagmiEventHandler = /** @class */ (function () {
|
|
|
672
1889
|
*/
|
|
673
1890
|
WagmiEventHandler.prototype.cleanup = function () {
|
|
674
1891
|
logger_1.logger.debug("WagmiEventHandler: Cleaning up subscriptions");
|
|
1892
|
+
if (!this.disposed) {
|
|
1893
|
+
this.disposed = true;
|
|
1894
|
+
// Nothing in flight may complete against a torn-down handler.
|
|
1895
|
+
this.transitionGeneration += 1;
|
|
1896
|
+
// Give up the right to emit. Any other live handler for this
|
|
1897
|
+
// destination claims it the next time it needs to emit.
|
|
1898
|
+
if (this.ownerKey && emittingOwners.get(this.ownerKey) === this) {
|
|
1899
|
+
emittingOwners.delete(this.ownerKey);
|
|
1900
|
+
}
|
|
1901
|
+
// Start the grace period. If nothing re-mounts, the markers are dropped
|
|
1902
|
+
// so a reconnect that happens while unobserved still emits.
|
|
1903
|
+
var wasLastForDestination = releaseMarkers(this.formo.writeKey);
|
|
1904
|
+
// Drop the shared broadcast records rather than leak them - but only
|
|
1905
|
+
// after the same grace period the markers get. The ordinary rebuild is
|
|
1906
|
+
// cleanup THEN remount, so deleting immediately would lose the receipt
|
|
1907
|
+
// for a transaction broadcast moments before teardown.
|
|
1908
|
+
if (wasLastForDestination && this.ownerKey) {
|
|
1909
|
+
schedulePendingTransactionExpiry(this.ownerKey);
|
|
1910
|
+
}
|
|
1911
|
+
}
|
|
675
1912
|
for (var _i = 0, _a = this.unsubscribers; _i < _a.length; _i++) {
|
|
676
1913
|
var unsubscribe = _a[_i];
|
|
677
1914
|
try {
|
|
@@ -684,7 +1921,10 @@ var WagmiEventHandler = /** @class */ (function () {
|
|
|
684
1921
|
this.unsubscribers = [];
|
|
685
1922
|
this.processedMutations.clear();
|
|
686
1923
|
this.processedQueries.clear();
|
|
687
|
-
|
|
1924
|
+
// Deliberately NOT clearing `pendingTransactions`: it is shared per
|
|
1925
|
+
// destination, and a replacement handler needs the broadcast records to
|
|
1926
|
+
// match incoming receipts against. It is dropped when the last handler
|
|
1927
|
+
// for the destination goes away (see below).
|
|
688
1928
|
logger_1.logger.debug("WagmiEventHandler: Cleanup complete");
|
|
689
1929
|
};
|
|
690
1930
|
return WagmiEventHandler;
|