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