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