@formo/analytics 1.37.0 → 1.38.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/src/FormoAnalytics.d.ts +66 -0
- package/dist/cjs/src/FormoAnalytics.js +176 -5
- package/dist/cjs/src/FormoAnalyticsProvider.d.ts +8 -0
- package/dist/cjs/src/FormoAnalyticsProvider.js +42 -32
- package/dist/cjs/src/evm/EvmEventTracker.d.ts +38 -10
- package/dist/cjs/src/evm/EvmEventTracker.js +182 -0
- package/dist/cjs/src/evm/EvmProviderRegistry.js +26 -4
- package/dist/cjs/src/evm/EvmRequestTracker.d.ts +40 -10
- package/dist/cjs/src/evm/EvmRequestTracker.js +219 -59
- package/dist/cjs/src/provider/detection.d.ts +30 -0
- package/dist/cjs/src/provider/detection.js +62 -0
- package/dist/cjs/src/provider/index.d.ts +1 -1
- package/dist/cjs/src/provider/index.js +3 -1
- package/dist/cjs/src/types/base.d.ts +23 -0
- package/dist/cjs/src/types/provider.d.ts +16 -0
- package/dist/cjs/src/types/provider.js +17 -1
- 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 +81 -0
- package/dist/cjs/src/wagmi/WagmiEventHandler.js +478 -38
- package/dist/esm/src/FormoAnalytics.d.ts +66 -0
- package/dist/esm/src/FormoAnalytics.js +176 -5
- package/dist/esm/src/FormoAnalyticsProvider.d.ts +8 -0
- package/dist/esm/src/FormoAnalyticsProvider.js +40 -31
- package/dist/esm/src/evm/EvmEventTracker.d.ts +38 -10
- package/dist/esm/src/evm/EvmEventTracker.js +183 -1
- package/dist/esm/src/evm/EvmProviderRegistry.js +27 -5
- package/dist/esm/src/evm/EvmRequestTracker.d.ts +40 -10
- package/dist/esm/src/evm/EvmRequestTracker.js +218 -58
- package/dist/esm/src/provider/detection.d.ts +30 -0
- package/dist/esm/src/provider/detection.js +60 -0
- package/dist/esm/src/provider/index.d.ts +1 -1
- package/dist/esm/src/provider/index.js +1 -1
- package/dist/esm/src/types/base.d.ts +23 -0
- package/dist/esm/src/types/provider.d.ts +16 -0
- package/dist/esm/src/types/provider.js +16 -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 +81 -0
- package/dist/esm/src/wagmi/WagmiEventHandler.js +478 -38
- package/dist/index.umd.min.js +1 -1
- package/package.json +2 -2
|
@@ -57,9 +57,19 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
57
57
|
}
|
|
58
58
|
return t;
|
|
59
59
|
};
|
|
60
|
+
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
61
|
+
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
62
|
+
if (ar || !(i in from)) {
|
|
63
|
+
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
64
|
+
ar[i] = from[i];
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return to.concat(ar || Array.prototype.slice.call(from));
|
|
68
|
+
};
|
|
60
69
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
61
70
|
exports.EvmRequestTracker = void 0;
|
|
62
71
|
var logger_1 = require("../logger");
|
|
72
|
+
var provider_1 = require("../provider");
|
|
63
73
|
var chain_1 = require("../utils/chain");
|
|
64
74
|
var address_1 = require("../utils/address");
|
|
65
75
|
var types_1 = require("../types");
|
|
@@ -100,6 +110,19 @@ function hexToUtf8(hex) {
|
|
|
100
110
|
* probed - an SDK-issued lookup on a serialising transport can wedge the
|
|
101
111
|
* user's wallet, which is never an acceptable price for a label.
|
|
102
112
|
*/
|
|
113
|
+
/**
|
|
114
|
+
* Providers with a dispatch already on the synchronous call stack.
|
|
115
|
+
*
|
|
116
|
+
* A page can end up with LAYERED Formo wrappers: another library wraps our
|
|
117
|
+
* wrapper, the SDK rebuilds, and the new instance wraps the outer function
|
|
118
|
+
* because the marker is not on it. Both layers route to the same newest
|
|
119
|
+
* live tracker, which would instrument one user request twice. Every
|
|
120
|
+
* dispatch issues its underlying request synchronously (deliberately -
|
|
121
|
+
* the wallet call always goes out first), so an inner shim reached during
|
|
122
|
+
* that window belongs to the SAME user request and passes straight
|
|
123
|
+
* through.
|
|
124
|
+
*/
|
|
125
|
+
var dispatchInFlight = new WeakSet();
|
|
103
126
|
var EvmRequestTracker = /** @class */ (function () {
|
|
104
127
|
function EvmRequestTracker(wallet, registry, deps) {
|
|
105
128
|
this.wallet = wallet;
|
|
@@ -115,12 +138,29 @@ var EvmRequestTracker = /** @class */ (function () {
|
|
|
115
138
|
*/
|
|
116
139
|
this.polls = new Set();
|
|
117
140
|
this.disposed = false;
|
|
141
|
+
/** Providers whose owner list includes this instance; pruned on cleanup. */
|
|
142
|
+
this.wrappedProviders = new Set();
|
|
143
|
+
this.creationSeq = EvmRequestTracker.nextCreationSeq++;
|
|
118
144
|
}
|
|
119
145
|
/** Stop every poll in flight. Terminal, like the event queue's close(). */
|
|
120
146
|
EvmRequestTracker.prototype.cleanup = function () {
|
|
147
|
+
var _this = this;
|
|
121
148
|
this.disposed = true;
|
|
122
149
|
this.polls.forEach(function (timer) { return clearTimeout(timer); });
|
|
123
150
|
this.polls.clear();
|
|
151
|
+
// Remove this instance from every provider's owner list. The lists
|
|
152
|
+
// live on LONG-LIVED provider objects; leaving disposed trackers in
|
|
153
|
+
// them retains each old instance's whole object graph across rebuilds,
|
|
154
|
+
// growing without bound under HMR.
|
|
155
|
+
this.wrappedProviders.forEach(function (provider) {
|
|
156
|
+
var owners = provider[types_1.WRAPPED_REQUEST_OWNER_SYMBOL];
|
|
157
|
+
if (Array.isArray(owners)) {
|
|
158
|
+
var idx = owners.indexOf(_this);
|
|
159
|
+
if (idx !== -1)
|
|
160
|
+
owners.splice(idx, 1);
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
this.wrappedProviders.clear();
|
|
124
164
|
};
|
|
125
165
|
/** Re-arm a poll, unless this tracker has been torn down. */
|
|
126
166
|
EvmRequestTracker.prototype.schedulePoll = function (fn, delayMs) {
|
|
@@ -134,11 +174,24 @@ var EvmRequestTracker = /** @class */ (function () {
|
|
|
134
174
|
this.polls.add(timer);
|
|
135
175
|
};
|
|
136
176
|
/**
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
177
|
+
* Put this tracker into an owner list at its CREATION-ORDER position.
|
|
178
|
+
* Registration can arrive out of order - an older instance's async wrap
|
|
179
|
+
* kick may resolve after a newer instance's - and dispatch picks from
|
|
180
|
+
* the END of the list, so append-on-registration would let resolution
|
|
181
|
+
* order decide which instance owns capture. Creation order is what the
|
|
182
|
+
* newest-live contract promises.
|
|
141
183
|
*/
|
|
184
|
+
EvmRequestTracker.prototype.insertBySeniority = function (owners) {
|
|
185
|
+
var _this = this;
|
|
186
|
+
var idx = owners.indexOf(this);
|
|
187
|
+
if (idx !== -1)
|
|
188
|
+
owners.splice(idx, 1);
|
|
189
|
+
var insertAt = owners.findIndex(function (o) { return o.creationSeq > _this.creationSeq; });
|
|
190
|
+
if (insertAt === -1)
|
|
191
|
+
owners.push(this);
|
|
192
|
+
else
|
|
193
|
+
owners.splice(insertAt, 0, this);
|
|
194
|
+
};
|
|
142
195
|
EvmRequestTracker.prototype.registerRequestListeners = function (provider) {
|
|
143
196
|
var _this = this;
|
|
144
197
|
logger_1.logger.info("registerRequestListeners");
|
|
@@ -146,19 +199,112 @@ var EvmRequestTracker = /** @class */ (function () {
|
|
|
146
199
|
logger_1.logger.error("Provider not found for request (signature, transaction) tracking");
|
|
147
200
|
return false;
|
|
148
201
|
}
|
|
149
|
-
//
|
|
202
|
+
// Already wrapped: take OWNERSHIP rather than skipping. The wrapper
|
|
203
|
+
// survives an SDK rebuild and closes over the instance that installed
|
|
204
|
+
// it, whose queue is closed after cleanup - "skip" made the rebuilt
|
|
205
|
+
// instance report success while every request-derived event died in
|
|
206
|
+
// the dead instance's queue. The wrapper reads the owner slot per call.
|
|
150
207
|
var currentRequest = provider.request;
|
|
151
208
|
if (this.registry.isWrapped(provider, currentRequest)) {
|
|
152
|
-
|
|
209
|
+
var owners = provider[types_1.WRAPPED_REQUEST_OWNER_SYMBOL];
|
|
210
|
+
if (!Array.isArray(owners)) {
|
|
211
|
+
// A wrapper without its owner list cannot be taken over, and
|
|
212
|
+
// claiming success would silence every request event.
|
|
213
|
+
logger_1.logger.warn("wrapped without owner list; cannot rebind");
|
|
214
|
+
return false;
|
|
215
|
+
}
|
|
216
|
+
this.insertBySeniority(owners);
|
|
217
|
+
this.wrappedProviders.add(provider);
|
|
218
|
+
logger_1.logger.info("Provider already wrapped; rebinding the wrapper to this instance.");
|
|
153
219
|
return true;
|
|
154
220
|
}
|
|
155
221
|
var request = provider.request.bind(provider);
|
|
156
222
|
var wrappedRequest = function (_a) { return __awaiter(_this, [_a], void 0, function (_b) {
|
|
223
|
+
var owners, liveOwner;
|
|
224
|
+
var method = _b.method, params = _b.params;
|
|
225
|
+
return __generator(this, function (_c) {
|
|
226
|
+
owners = provider[types_1.WRAPPED_REQUEST_OWNER_SYMBOL];
|
|
227
|
+
if (dispatchInFlight.has(provider)) {
|
|
228
|
+
// An outer Formo wrapper is already instrumenting this very
|
|
229
|
+
// request; this layer only forwards.
|
|
230
|
+
return [2 /*return*/, request({ method: method, params: params })];
|
|
231
|
+
}
|
|
232
|
+
liveOwner = Array.isArray(owners)
|
|
233
|
+
? __spreadArray([], owners, true).reverse().find(function (o) { return !o.disposed; })
|
|
234
|
+
: undefined;
|
|
235
|
+
dispatchInFlight.add(provider);
|
|
236
|
+
try {
|
|
237
|
+
return [2 /*return*/, (liveOwner !== null && liveOwner !== void 0 ? liveOwner : this).dispatchWrappedRequest({ method: method, params: params }, provider, request)];
|
|
238
|
+
}
|
|
239
|
+
finally {
|
|
240
|
+
// Cleared as soon as the dispatch call RETURNS its promise: the
|
|
241
|
+
// underlying request has been issued synchronously by then, so
|
|
242
|
+
// the window covers exactly the nested layers of this one call
|
|
243
|
+
// and never a concurrent request.
|
|
244
|
+
dispatchInFlight.delete(provider);
|
|
245
|
+
}
|
|
246
|
+
return [2 /*return*/];
|
|
247
|
+
});
|
|
248
|
+
}); };
|
|
249
|
+
try {
|
|
250
|
+
// MERGE with any existing list rather than overwriting: a wallet that
|
|
251
|
+
// replaced `request` forces a re-wrap, and discarding the prior list
|
|
252
|
+
// would drop other live instances from ownership - the newest-live
|
|
253
|
+
// fallback then has nobody to fall back to.
|
|
254
|
+
var slot = provider;
|
|
255
|
+
var prior = slot[types_1.WRAPPED_REQUEST_OWNER_SYMBOL];
|
|
256
|
+
var owners = Array.isArray(prior) ? prior : [];
|
|
257
|
+
this.insertBySeniority(owners);
|
|
258
|
+
if (!Array.isArray(prior)) {
|
|
259
|
+
slot[types_1.WRAPPED_REQUEST_OWNER_SYMBOL] = owners;
|
|
260
|
+
}
|
|
261
|
+
this.wrappedProviders.add(provider);
|
|
262
|
+
}
|
|
263
|
+
catch (_a) {
|
|
264
|
+
/* frozen provider: the request write below fails too and aborts */
|
|
265
|
+
}
|
|
266
|
+
return this.installWrappedRequest(provider, wrappedRequest);
|
|
267
|
+
};
|
|
268
|
+
/** Install the wrapper function onto the provider; separated so the
|
|
269
|
+
* routing shim above stays small. */
|
|
270
|
+
EvmRequestTracker.prototype.installWrappedRequest = function (provider, wrappedRequest) {
|
|
271
|
+
// Mark the wrapper so we can detect if request is replaced externally and keep a reference on provider
|
|
272
|
+
wrappedRequest[types_1.WRAPPED_REQUEST_SYMBOL] = true;
|
|
273
|
+
// Both writes go inside the try. A frozen or non-extensible provider
|
|
274
|
+
// throws on the symbol assignment just as readily as on `request`, and
|
|
275
|
+
// that one used to sit outside the guard, so an unwrappable provider
|
|
276
|
+
// aborted registration instead of being skipped.
|
|
277
|
+
try {
|
|
278
|
+
provider[types_1.WRAPPED_REQUEST_REF_SYMBOL] =
|
|
279
|
+
wrappedRequest;
|
|
280
|
+
provider.request = wrappedRequest;
|
|
281
|
+
// Read back: an accessor or Proxy can ACCEPT the assignment without
|
|
282
|
+
// installing it, and success here is a promise that capture works.
|
|
283
|
+
if (provider.request !== wrappedRequest) {
|
|
284
|
+
logger_1.logger.warn("request assignment swallowed; not wrapped");
|
|
285
|
+
return false;
|
|
286
|
+
}
|
|
287
|
+
return true;
|
|
288
|
+
}
|
|
289
|
+
catch (e) {
|
|
290
|
+
logger_1.logger.warn("Failed to wrap provider.request; skipping", e);
|
|
291
|
+
return false;
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
/**
|
|
295
|
+
* The wrapper body proper: everything a request observation does, run
|
|
296
|
+
* against THIS instance's registry, wallet state, and event queue. Kept
|
|
297
|
+
* as a method so a surviving wrapper installed by a previous SDK
|
|
298
|
+
* instance can hand calls to the current one.
|
|
299
|
+
*/
|
|
300
|
+
EvmRequestTracker.prototype.dispatchWrappedRequest = function (_a, provider_2, request_1) {
|
|
301
|
+
return __awaiter(this, arguments, void 0, function (_b, provider, request) {
|
|
157
302
|
var generation_1, responsePromise, capturedChainId_1, response_1, error_1, rpcError, txPromise, txChainId_1, transactionHash_1, error_2, rpcError;
|
|
158
303
|
var _this = this;
|
|
304
|
+
var _c, _d, _e, _f;
|
|
159
305
|
var method = _b.method, params = _b.params;
|
|
160
|
-
return __generator(this, function (
|
|
161
|
-
switch (
|
|
306
|
+
return __generator(this, function (_g) {
|
|
307
|
+
switch (_g.label) {
|
|
162
308
|
case 0:
|
|
163
309
|
// Learn the chain from a call the APP was making anyway.
|
|
164
310
|
//
|
|
@@ -187,6 +333,10 @@ var EvmRequestTracker = /** @class */ (function () {
|
|
|
187
333
|
logger_1.logger.debug("Signature event skipped (autocapture.signature: false)", { method: method });
|
|
188
334
|
return [2 /*return*/, request({ method: method, params: params })];
|
|
189
335
|
}
|
|
336
|
+
if ((_d = (_c = this.deps).shouldSkipRequestCapture) === null || _d === void 0 ? void 0 : _d.call(_c, method, params)) {
|
|
337
|
+
// A pending wagmi mutation owns this capture.
|
|
338
|
+
return [2 /*return*/, request({ method: method, params: params })];
|
|
339
|
+
}
|
|
190
340
|
responsePromise = request({ method: method, params: params });
|
|
191
341
|
// Attach a no-op handler now so a rejection arriving before the await
|
|
192
342
|
// below is never reported as unhandled. The real handling is there.
|
|
@@ -199,7 +349,7 @@ var EvmRequestTracker = /** @class */ (function () {
|
|
|
199
349
|
switch (_a.label) {
|
|
200
350
|
case 0:
|
|
201
351
|
_a.trys.push([0, 2, , 3]);
|
|
202
|
-
return [4 /*yield*/, this.deps.signature(__assign({ status: types_1.SignatureStatus.REQUESTED }, this.buildSignatureEventPayload(method, params, undefined, capturedChainId_1, provider)))];
|
|
352
|
+
return [4 /*yield*/, this.deps.signature(__assign({ status: types_1.SignatureStatus.REQUESTED }, this.buildSignatureEventPayload(method, params, undefined, capturedChainId_1, provider)), this.attributionFor(provider))];
|
|
203
353
|
case 1:
|
|
204
354
|
_a.sent();
|
|
205
355
|
return [3 /*break*/, 3];
|
|
@@ -211,12 +361,12 @@ var EvmRequestTracker = /** @class */ (function () {
|
|
|
211
361
|
}
|
|
212
362
|
});
|
|
213
363
|
}); })();
|
|
214
|
-
|
|
364
|
+
_g.label = 1;
|
|
215
365
|
case 1:
|
|
216
|
-
|
|
366
|
+
_g.trys.push([1, 3, , 4]);
|
|
217
367
|
return [4 /*yield*/, responsePromise];
|
|
218
368
|
case 2:
|
|
219
|
-
response_1 =
|
|
369
|
+
response_1 = _g.sent();
|
|
220
370
|
// Track signature confirmation only for truthy responses
|
|
221
371
|
if (response_1) {
|
|
222
372
|
(function () { return __awaiter(_this, void 0, void 0, function () {
|
|
@@ -225,7 +375,7 @@ var EvmRequestTracker = /** @class */ (function () {
|
|
|
225
375
|
switch (_a.label) {
|
|
226
376
|
case 0:
|
|
227
377
|
_a.trys.push([0, 2, , 3]);
|
|
228
|
-
return [4 /*yield*/, this.deps.signature(__assign({ status: types_1.SignatureStatus.CONFIRMED }, this.buildSignatureEventPayload(method, params, response_1, capturedChainId_1, provider)))];
|
|
378
|
+
return [4 /*yield*/, this.deps.signature(__assign({ status: types_1.SignatureStatus.CONFIRMED }, this.buildSignatureEventPayload(method, params, response_1, capturedChainId_1, provider)), this.attributionFor(provider))];
|
|
229
379
|
case 1:
|
|
230
380
|
_a.sent();
|
|
231
381
|
return [3 /*break*/, 3];
|
|
@@ -240,9 +390,9 @@ var EvmRequestTracker = /** @class */ (function () {
|
|
|
240
390
|
}
|
|
241
391
|
return [2 /*return*/, response_1];
|
|
242
392
|
case 3:
|
|
243
|
-
error_1 =
|
|
393
|
+
error_1 = _g.sent();
|
|
244
394
|
rpcError = error_1;
|
|
245
|
-
if ((
|
|
395
|
+
if ((0, provider_1.isUserRejectionError)(rpcError)) {
|
|
246
396
|
// Use the already cast rpcError to avoid duplication
|
|
247
397
|
(function () { return __awaiter(_this, void 0, void 0, function () {
|
|
248
398
|
var e_3;
|
|
@@ -250,7 +400,7 @@ var EvmRequestTracker = /** @class */ (function () {
|
|
|
250
400
|
switch (_a.label) {
|
|
251
401
|
case 0:
|
|
252
402
|
_a.trys.push([0, 2, , 3]);
|
|
253
|
-
return [4 /*yield*/, this.deps.signature(__assign({ status: types_1.SignatureStatus.REJECTED }, this.buildSignatureEventPayload(method, params, undefined, capturedChainId_1, provider)))];
|
|
403
|
+
return [4 /*yield*/, this.deps.signature(__assign({ status: types_1.SignatureStatus.REJECTED }, this.buildSignatureEventPayload(method, params, undefined, capturedChainId_1, provider)), this.attributionFor(provider))];
|
|
254
404
|
case 1:
|
|
255
405
|
_a.sent();
|
|
256
406
|
return [3 /*break*/, 3];
|
|
@@ -282,6 +432,10 @@ var EvmRequestTracker = /** @class */ (function () {
|
|
|
282
432
|
logger_1.logger.debug("Transaction event skipped (autocapture.transaction: false)", { method: method });
|
|
283
433
|
return [2 /*return*/, request({ method: method, params: params })];
|
|
284
434
|
}
|
|
435
|
+
if ((_f = (_e = this.deps).shouldSkipRequestCapture) === null || _f === void 0 ? void 0 : _f.call(_e, method, params)) {
|
|
436
|
+
// A pending wagmi mutation owns this capture.
|
|
437
|
+
return [2 /*return*/, request({ method: method, params: params })];
|
|
438
|
+
}
|
|
285
439
|
txPromise = request({ method: method, params: params });
|
|
286
440
|
txPromise.catch(function () { return undefined; });
|
|
287
441
|
txChainId_1 = this.registry.resolveChainId(provider);
|
|
@@ -294,7 +448,7 @@ var EvmRequestTracker = /** @class */ (function () {
|
|
|
294
448
|
return [4 /*yield*/, this.buildTransactionEventPayload(params, provider, txChainId_1)];
|
|
295
449
|
case 1:
|
|
296
450
|
payload = _a.sent();
|
|
297
|
-
return [4 /*yield*/, this.deps.transaction(__assign({ status: types_1.TransactionStatus.STARTED }, payload))];
|
|
451
|
+
return [4 /*yield*/, this.deps.transaction(__assign({ status: types_1.TransactionStatus.STARTED }, payload), this.attributionFor(provider))];
|
|
298
452
|
case 2:
|
|
299
453
|
_a.sent();
|
|
300
454
|
return [3 /*break*/, 4];
|
|
@@ -306,12 +460,12 @@ var EvmRequestTracker = /** @class */ (function () {
|
|
|
306
460
|
}
|
|
307
461
|
});
|
|
308
462
|
}); })();
|
|
309
|
-
|
|
463
|
+
_g.label = 5;
|
|
310
464
|
case 5:
|
|
311
|
-
|
|
465
|
+
_g.trys.push([5, 7, , 8]);
|
|
312
466
|
return [4 /*yield*/, txPromise];
|
|
313
467
|
case 6:
|
|
314
|
-
transactionHash_1 =
|
|
468
|
+
transactionHash_1 = _g.sent();
|
|
315
469
|
(function () { return __awaiter(_this, void 0, void 0, function () {
|
|
316
470
|
var payload, e_5;
|
|
317
471
|
return __generator(this, function (_a) {
|
|
@@ -321,7 +475,7 @@ var EvmRequestTracker = /** @class */ (function () {
|
|
|
321
475
|
return [4 /*yield*/, this.buildTransactionEventPayload(params, provider, txChainId_1)];
|
|
322
476
|
case 1:
|
|
323
477
|
payload = _a.sent();
|
|
324
|
-
return [4 /*yield*/, this.deps.transaction(__assign(__assign({ status: types_1.TransactionStatus.BROADCASTED }, payload), { transactionHash: transactionHash_1 }))];
|
|
478
|
+
return [4 /*yield*/, this.deps.transaction(__assign(__assign({ status: types_1.TransactionStatus.BROADCASTED }, payload), { transactionHash: transactionHash_1 }), this.attributionFor(provider))];
|
|
325
479
|
case 2:
|
|
326
480
|
_a.sent();
|
|
327
481
|
// Start async polling for transaction receipt
|
|
@@ -337,9 +491,9 @@ var EvmRequestTracker = /** @class */ (function () {
|
|
|
337
491
|
}); })();
|
|
338
492
|
return [2 /*return*/, transactionHash_1];
|
|
339
493
|
case 7:
|
|
340
|
-
error_2 =
|
|
494
|
+
error_2 = _g.sent();
|
|
341
495
|
rpcError = error_2;
|
|
342
|
-
if ((
|
|
496
|
+
if ((0, provider_1.isUserRejectionError)(rpcError)) {
|
|
343
497
|
// Use the already cast rpcError to avoid duplication
|
|
344
498
|
(function () { return __awaiter(_this, void 0, void 0, function () {
|
|
345
499
|
var payload, e_6;
|
|
@@ -350,7 +504,7 @@ var EvmRequestTracker = /** @class */ (function () {
|
|
|
350
504
|
return [4 /*yield*/, this.buildTransactionEventPayload(params, provider, txChainId_1)];
|
|
351
505
|
case 1:
|
|
352
506
|
payload = _a.sent();
|
|
353
|
-
return [4 /*yield*/, this.deps.transaction(__assign({ status: types_1.TransactionStatus.REJECTED }, payload))];
|
|
507
|
+
return [4 /*yield*/, this.deps.transaction(__assign({ status: types_1.TransactionStatus.REJECTED }, payload), this.attributionFor(provider))];
|
|
354
508
|
case 2:
|
|
355
509
|
_a.sent();
|
|
356
510
|
return [3 /*break*/, 4];
|
|
@@ -367,23 +521,19 @@ var EvmRequestTracker = /** @class */ (function () {
|
|
|
367
521
|
case 8: return [2 /*return*/, request({ method: method, params: params })];
|
|
368
522
|
}
|
|
369
523
|
});
|
|
370
|
-
});
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
}
|
|
383
|
-
catch (e) {
|
|
384
|
-
logger_1.logger.warn("Failed to wrap provider.request; skipping", e);
|
|
385
|
-
return false;
|
|
386
|
-
}
|
|
524
|
+
});
|
|
525
|
+
};
|
|
526
|
+
/**
|
|
527
|
+
* Wallet attribution for request-derived events.
|
|
528
|
+
*
|
|
529
|
+
* Live per read through the registry, so a WalletConnect session names
|
|
530
|
+
* its actual signer ("MetaMask Wallet", "Ledger Live") - the live-test
|
|
531
|
+
* rows had provider_name EMPTY on every signature and transaction, which
|
|
532
|
+
* made per-wallet activity unanswerable in the warehouse.
|
|
533
|
+
*/
|
|
534
|
+
EvmRequestTracker.prototype.attributionFor = function (provider) {
|
|
535
|
+
var info = this.registry.infoFor(provider);
|
|
536
|
+
return { providerName: info.name, rdns: info.rdns };
|
|
387
537
|
};
|
|
388
538
|
EvmRequestTracker.prototype.buildSignatureEventPayload = function (method, params,
|
|
389
539
|
// Intentionally not read. Kept for positional call-site arity.
|
|
@@ -449,7 +599,7 @@ var EvmRequestTracker = /** @class */ (function () {
|
|
|
449
599
|
/**
|
|
450
600
|
* Polls for transaction receipt and emits tx.status = CONFIRMED or REVERTED.
|
|
451
601
|
*/
|
|
452
|
-
EvmRequestTracker.prototype.pollTransactionReceipt = function (
|
|
602
|
+
EvmRequestTracker.prototype.pollTransactionReceipt = function (provider_2, transactionHash_2, payload_1) {
|
|
453
603
|
return __awaiter(this, arguments, void 0, function (provider, transactionHash, payload, maxAttempts, intervalMs) {
|
|
454
604
|
var attempts, poll;
|
|
455
605
|
var _this = this;
|
|
@@ -479,7 +629,7 @@ var EvmRequestTracker = /** @class */ (function () {
|
|
|
479
629
|
// status: 1 = success, 0 = reverted
|
|
480
630
|
if (receipt.status === "0x1" || receipt.status === 1) {
|
|
481
631
|
this.deps
|
|
482
|
-
.transaction(__assign(__assign({ status: types_1.TransactionStatus.CONFIRMED }, payload), { transactionHash: transactionHash }))
|
|
632
|
+
.transaction(__assign(__assign({ status: types_1.TransactionStatus.CONFIRMED }, payload), { transactionHash: transactionHash }), this.attributionFor(provider))
|
|
483
633
|
.catch(function (e) {
|
|
484
634
|
return logger_1.logger.error("Formo: Failed to track transaction confirmation", e);
|
|
485
635
|
});
|
|
@@ -487,7 +637,7 @@ var EvmRequestTracker = /** @class */ (function () {
|
|
|
487
637
|
}
|
|
488
638
|
else if (receipt.status === "0x0" || receipt.status === 0) {
|
|
489
639
|
this.deps
|
|
490
|
-
.transaction(__assign(__assign({ status: types_1.TransactionStatus.REVERTED }, payload), { transactionHash: transactionHash }))
|
|
640
|
+
.transaction(__assign(__assign({ status: types_1.TransactionStatus.REVERTED }, payload), { transactionHash: transactionHash }), this.attributionFor(provider))
|
|
491
641
|
.catch(function (e) {
|
|
492
642
|
return logger_1.logger.error("Formo: Failed to track transaction revert", e);
|
|
493
643
|
});
|
|
@@ -530,10 +680,10 @@ var EvmRequestTracker = /** @class */ (function () {
|
|
|
530
680
|
*/
|
|
531
681
|
EvmRequestTracker.prototype.trackBatchedCalls = function (provider, request, params) {
|
|
532
682
|
return __awaiter(this, void 0, void 0, function () {
|
|
533
|
-
var sendPromise, batch, calls, declared, chainId, address, payloads, _i, payloads_1, p, properties, rest, result, batchId, _a, payloads_2, p, properties, rest, error_3, rpcError, _b, payloads_3, p, properties, rest;
|
|
534
|
-
var _c;
|
|
535
|
-
return __generator(this, function (
|
|
536
|
-
switch (
|
|
683
|
+
var sendPromise, batch, calls, declared, chainId, address, attribution, payloads, _i, payloads_1, p, properties, rest, result, batchId, _a, payloads_2, p, properties, rest, error_3, rpcError, _b, payloads_3, p, properties, rest;
|
|
684
|
+
var _c, _d, _e;
|
|
685
|
+
return __generator(this, function (_f) {
|
|
686
|
+
switch (_f.label) {
|
|
537
687
|
case 0:
|
|
538
688
|
if (!this.deps.isAutocaptureEnabled("transaction")) {
|
|
539
689
|
logger_1.logger.debug("Transaction event skipped (autocapture.transaction: false)", {
|
|
@@ -541,13 +691,17 @@ var EvmRequestTracker = /** @class */ (function () {
|
|
|
541
691
|
});
|
|
542
692
|
return [2 /*return*/, request({ method: "wallet_sendCalls", params: params })];
|
|
543
693
|
}
|
|
694
|
+
if ((_d = (_c = this.deps).shouldSkipRequestCapture) === null || _d === void 0 ? void 0 : _d.call(_c, "wallet_sendCalls", params)) {
|
|
695
|
+
// A pending wagmi sendCalls mutation owns this capture.
|
|
696
|
+
return [2 /*return*/, request({ method: "wallet_sendCalls", params: params })];
|
|
697
|
+
}
|
|
544
698
|
sendPromise = request({ method: "wallet_sendCalls", params: params });
|
|
545
699
|
sendPromise.catch(function () { return undefined; });
|
|
546
700
|
batch = params[0];
|
|
547
701
|
calls = Array.isArray(batch === null || batch === void 0 ? void 0 : batch.calls) ? batch.calls : [];
|
|
548
702
|
declared = typeof (batch === null || batch === void 0 ? void 0 : batch.chainId) === "string" ? (0, chain_1.parseChainId)(batch.chainId) : undefined;
|
|
549
703
|
chainId = declared || this.registry.resolveChainId(provider);
|
|
550
|
-
address = (0, address_1.validateAndChecksumAddress)((
|
|
704
|
+
address = (0, address_1.validateAndChecksumAddress)((_e = batch === null || batch === void 0 ? void 0 : batch.from) !== null && _e !== void 0 ? _e : "");
|
|
551
705
|
if (!address) {
|
|
552
706
|
// Nothing can be attributed without a sender, and inventing one would
|
|
553
707
|
// be worse than reporting nothing. The user's call still goes through.
|
|
@@ -566,16 +720,14 @@ var EvmRequestTracker = /** @class */ (function () {
|
|
|
566
720
|
if (!provider || provider === this.wallet.provider || !this.wallet.provider) {
|
|
567
721
|
this.wallet.backfill(address, chainId, provider);
|
|
568
722
|
}
|
|
723
|
+
attribution = this.attributionFor(provider);
|
|
569
724
|
payloads = calls.map(function (call, index) { return ({
|
|
570
725
|
chainId: chainId,
|
|
571
726
|
address: address,
|
|
572
727
|
to: call === null || call === void 0 ? void 0 : call.to,
|
|
573
728
|
value: call === null || call === void 0 ? void 0 : call.value,
|
|
574
729
|
data: call === null || call === void 0 ? void 0 : call.data,
|
|
575
|
-
properties: {
|
|
576
|
-
batch_size: calls.length,
|
|
577
|
-
batch_index: index,
|
|
578
|
-
},
|
|
730
|
+
properties: __assign({ batch_size: calls.length, batch_index: index }, attribution),
|
|
579
731
|
}); });
|
|
580
732
|
// STARTED carries no batch id: the wallet has not issued one yet, exactly
|
|
581
733
|
// as `eth_sendTransaction` has no hash at this point. Position within the
|
|
@@ -587,12 +739,12 @@ var EvmRequestTracker = /** @class */ (function () {
|
|
|
587
739
|
.transaction(__assign({ status: types_1.TransactionStatus.STARTED }, rest), properties)
|
|
588
740
|
.catch(function (e) { return logger_1.logger.error("Formo: Failed to track batch call start", e); });
|
|
589
741
|
}
|
|
590
|
-
|
|
742
|
+
_f.label = 1;
|
|
591
743
|
case 1:
|
|
592
|
-
|
|
744
|
+
_f.trys.push([1, 3, , 4]);
|
|
593
745
|
return [4 /*yield*/, sendPromise];
|
|
594
746
|
case 2:
|
|
595
|
-
result =
|
|
747
|
+
result = _f.sent();
|
|
596
748
|
batchId = (0, batch_1.readBatchId)(result);
|
|
597
749
|
for (_a = 0, payloads_2 = payloads; _a < payloads_2.length; _a++) {
|
|
598
750
|
p = payloads_2[_a];
|
|
@@ -607,9 +759,9 @@ var EvmRequestTracker = /** @class */ (function () {
|
|
|
607
759
|
this.pollBatchStatus(provider, batchId, payloads);
|
|
608
760
|
return [2 /*return*/, result];
|
|
609
761
|
case 3:
|
|
610
|
-
error_3 =
|
|
762
|
+
error_3 = _f.sent();
|
|
611
763
|
rpcError = error_3;
|
|
612
|
-
if ((
|
|
764
|
+
if ((0, provider_1.isUserRejectionError)(rpcError)) {
|
|
613
765
|
// One rejection dismisses the whole prompt, so every call in it is
|
|
614
766
|
// rejected. Reporting only the first would undercount.
|
|
615
767
|
for (_b = 0, payloads_3 = payloads; _b < payloads_3.length; _b++) {
|
|
@@ -639,7 +791,7 @@ var EvmRequestTracker = /** @class */ (function () {
|
|
|
639
791
|
* what makes a partially reverted non-atomic batch report honestly instead
|
|
640
792
|
* of marking every call with the batch's worst outcome.
|
|
641
793
|
*/
|
|
642
|
-
EvmRequestTracker.prototype.pollBatchStatus = function (
|
|
794
|
+
EvmRequestTracker.prototype.pollBatchStatus = function (provider_2, batchId_1, payloads_4) {
|
|
643
795
|
return __awaiter(this, arguments, void 0, function (provider, batchId, payloads, maxAttempts, intervalMs) {
|
|
644
796
|
var attempts, poll;
|
|
645
797
|
var _this = this;
|
|
@@ -707,6 +859,14 @@ var EvmRequestTracker = /** @class */ (function () {
|
|
|
707
859
|
});
|
|
708
860
|
});
|
|
709
861
|
};
|
|
862
|
+
/**
|
|
863
|
+
* Wrap a provider's `request`. Returns whether the wrapper is installed:
|
|
864
|
+
* a caller that marks the provider as tracked on a false return would
|
|
865
|
+
* never retry it, and every signature and transaction from that wallet
|
|
866
|
+
* would be missed for the rest of the session.
|
|
867
|
+
*/
|
|
868
|
+
/** Monotonic creation stamp; owner precedence is decided by it. */
|
|
869
|
+
EvmRequestTracker.nextCreationSeq = 0;
|
|
710
870
|
return EvmRequestTracker;
|
|
711
871
|
}());
|
|
712
872
|
exports.EvmRequestTracker = EvmRequestTracker;
|
|
@@ -41,6 +41,36 @@ export interface ProviderInfo {
|
|
|
41
41
|
* ```
|
|
42
42
|
*/
|
|
43
43
|
export declare function detectInjectedProviderInfo(provider: EIP1193Provider): ProviderInfo;
|
|
44
|
+
/**
|
|
45
|
+
* Was this error the USER declining, whatever transport delivered it?
|
|
46
|
+
*
|
|
47
|
+
* Three dialects say "the user said no":
|
|
48
|
+
* - EIP-1193: code 4001 (UserRejectedRequest).
|
|
49
|
+
* - WalletConnect sdkErrors: codes 5000-5005 (USER_REJECTED and its
|
|
50
|
+
* variants). A LIVE MetaMask Mobile session rejecting a transaction
|
|
51
|
+
* produced one of these and the SDK's 4001-only match reported nothing -
|
|
52
|
+
* every WalletConnect rejection was silently uncounted.
|
|
53
|
+
* - viem: a typed UserRejectedRequestError, sometimes without the numeric
|
|
54
|
+
* code surviving the wrapping.
|
|
55
|
+
*
|
|
56
|
+
* The real code often hides under `cause` (viem nests, WC wraps), so the
|
|
57
|
+
* chain is walked a few levels.
|
|
58
|
+
*/
|
|
59
|
+
export declare function isUserRejectionError(error: unknown): boolean;
|
|
60
|
+
/**
|
|
61
|
+
* The wallet on the far side of a WalletConnect session.
|
|
62
|
+
*
|
|
63
|
+
* WalletConnect is a transport, not a wallet: the signing wallet (Ledger
|
|
64
|
+
* Live, MetaMask Mobile, Safe, ...) identifies itself in the session's peer
|
|
65
|
+
* metadata. Reporting only "WalletConnect" hides every wallet behind it -
|
|
66
|
+
* production showed Ledger at effectively zero while its sessions were being
|
|
67
|
+
* tracked under the transport's name. Reads synchronous state only; never
|
|
68
|
+
* issues an RPC.
|
|
69
|
+
*/
|
|
70
|
+
export declare function readWalletConnectPeer(provider: EIP1193Provider): {
|
|
71
|
+
name: string;
|
|
72
|
+
url?: string;
|
|
73
|
+
} | undefined;
|
|
44
74
|
/**
|
|
45
75
|
* Validates that a provider implements the required EIP-1193 interface
|
|
46
76
|
*
|
|
@@ -2,9 +2,22 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Provider detection utilities for identifying wallet providers
|
|
4
4
|
*/
|
|
5
|
+
var __assign = (this && this.__assign) || function () {
|
|
6
|
+
__assign = Object.assign || function(t) {
|
|
7
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
8
|
+
s = arguments[i];
|
|
9
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
10
|
+
t[p] = s[p];
|
|
11
|
+
}
|
|
12
|
+
return t;
|
|
13
|
+
};
|
|
14
|
+
return __assign.apply(this, arguments);
|
|
15
|
+
};
|
|
5
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
17
|
exports.DEFAULT_PROVIDER_ICON = void 0;
|
|
7
18
|
exports.detectInjectedProviderInfo = detectInjectedProviderInfo;
|
|
19
|
+
exports.isUserRejectionError = isUserRejectionError;
|
|
20
|
+
exports.readWalletConnectPeer = readWalletConnectPeer;
|
|
8
21
|
exports.isValidProvider = isValidProvider;
|
|
9
22
|
/**
|
|
10
23
|
* Default icon for providers without custom icons
|
|
@@ -81,6 +94,55 @@ function detectInjectedProviderInfo(provider) {
|
|
|
81
94
|
icon: exports.DEFAULT_PROVIDER_ICON,
|
|
82
95
|
};
|
|
83
96
|
}
|
|
97
|
+
/**
|
|
98
|
+
* Was this error the USER declining, whatever transport delivered it?
|
|
99
|
+
*
|
|
100
|
+
* Three dialects say "the user said no":
|
|
101
|
+
* - EIP-1193: code 4001 (UserRejectedRequest).
|
|
102
|
+
* - WalletConnect sdkErrors: codes 5000-5005 (USER_REJECTED and its
|
|
103
|
+
* variants). A LIVE MetaMask Mobile session rejecting a transaction
|
|
104
|
+
* produced one of these and the SDK's 4001-only match reported nothing -
|
|
105
|
+
* every WalletConnect rejection was silently uncounted.
|
|
106
|
+
* - viem: a typed UserRejectedRequestError, sometimes without the numeric
|
|
107
|
+
* code surviving the wrapping.
|
|
108
|
+
*
|
|
109
|
+
* The real code often hides under `cause` (viem nests, WC wraps), so the
|
|
110
|
+
* chain is walked a few levels.
|
|
111
|
+
*/
|
|
112
|
+
function isUserRejectionError(error) {
|
|
113
|
+
var cursor = error;
|
|
114
|
+
for (var depth = 0; cursor && depth < 5; depth++) {
|
|
115
|
+
var code = cursor.code;
|
|
116
|
+
if (code === 4001 ||
|
|
117
|
+
(typeof code === "number" && code >= 5000 && code <= 5005) ||
|
|
118
|
+
cursor.name === "UserRejectedRequestError") {
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
cursor = cursor.cause;
|
|
122
|
+
}
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* The wallet on the far side of a WalletConnect session.
|
|
127
|
+
*
|
|
128
|
+
* WalletConnect is a transport, not a wallet: the signing wallet (Ledger
|
|
129
|
+
* Live, MetaMask Mobile, Safe, ...) identifies itself in the session's peer
|
|
130
|
+
* metadata. Reporting only "WalletConnect" hides every wallet behind it -
|
|
131
|
+
* production showed Ledger at effectively zero while its sessions were being
|
|
132
|
+
* tracked under the transport's name. Reads synchronous state only; never
|
|
133
|
+
* issues an RPC.
|
|
134
|
+
*/
|
|
135
|
+
function readWalletConnectPeer(provider) {
|
|
136
|
+
var _a;
|
|
137
|
+
var session = provider.session;
|
|
138
|
+
var metadata = (_a = session === null || session === void 0 ? void 0 : session.peer) === null || _a === void 0 ? void 0 : _a.metadata;
|
|
139
|
+
if (!metadata || typeof metadata.name !== "string" || metadata.name.length === 0) {
|
|
140
|
+
return undefined;
|
|
141
|
+
}
|
|
142
|
+
return __assign({ name: metadata.name }, (typeof metadata.url === "string" && metadata.url.length > 0
|
|
143
|
+
? { url: metadata.url }
|
|
144
|
+
: {}));
|
|
145
|
+
}
|
|
84
146
|
/**
|
|
85
147
|
* Validates that a provider implements the required EIP-1193 interface
|
|
86
148
|
*
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Provider-related exports
|
|
3
3
|
*/
|
|
4
|
-
export { detectInjectedProviderInfo, isValidProvider, DEFAULT_PROVIDER_ICON, } from './detection';
|
|
4
|
+
export { detectInjectedProviderInfo, isValidProvider, readWalletConnectPeer, isUserRejectionError, DEFAULT_PROVIDER_ICON, } from './detection';
|
|
5
5
|
export type { WalletProviderFlags, ProviderInfo, } from './detection';
|
|
6
6
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -3,9 +3,11 @@
|
|
|
3
3
|
* Provider-related exports
|
|
4
4
|
*/
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.DEFAULT_PROVIDER_ICON = exports.isValidProvider = exports.detectInjectedProviderInfo = void 0;
|
|
6
|
+
exports.DEFAULT_PROVIDER_ICON = exports.isUserRejectionError = exports.readWalletConnectPeer = exports.isValidProvider = exports.detectInjectedProviderInfo = void 0;
|
|
7
7
|
var detection_1 = require("./detection");
|
|
8
8
|
Object.defineProperty(exports, "detectInjectedProviderInfo", { enumerable: true, get: function () { return detection_1.detectInjectedProviderInfo; } });
|
|
9
9
|
Object.defineProperty(exports, "isValidProvider", { enumerable: true, get: function () { return detection_1.isValidProvider; } });
|
|
10
|
+
Object.defineProperty(exports, "readWalletConnectPeer", { enumerable: true, get: function () { return detection_1.readWalletConnectPeer; } });
|
|
11
|
+
Object.defineProperty(exports, "isUserRejectionError", { enumerable: true, get: function () { return detection_1.isUserRejectionError; } });
|
|
10
12
|
Object.defineProperty(exports, "DEFAULT_PROVIDER_ICON", { enumerable: true, get: function () { return detection_1.DEFAULT_PROVIDER_ICON; } });
|
|
11
13
|
//# sourceMappingURL=index.js.map
|