@formo/analytics 1.35.1 → 1.36.0

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.
Files changed (95) hide show
  1. package/dist/cjs/src/FormoAnalytics.d.ts +90 -240
  2. package/dist/cjs/src/FormoAnalytics.js +432 -1840
  3. package/dist/cjs/src/constants/base.d.ts +0 -1
  4. package/dist/cjs/src/constants/base.js +1 -3
  5. package/dist/cjs/src/event/EventFactory.js +1 -1
  6. package/dist/cjs/src/event/EventManager.d.ts +3 -1
  7. package/dist/cjs/src/event/EventManager.js +5 -1
  8. package/dist/cjs/src/event/type.d.ts +1 -0
  9. package/dist/cjs/src/evm/EvmEventTracker.d.ts +175 -0
  10. package/dist/cjs/src/evm/EvmEventTracker.js +1030 -0
  11. package/dist/cjs/src/evm/EvmProviderRegistry.d.ts +132 -0
  12. package/dist/cjs/src/evm/EvmProviderRegistry.js +348 -0
  13. package/dist/cjs/src/evm/EvmRequestTracker.d.ts +120 -0
  14. package/dist/cjs/src/evm/EvmRequestTracker.js +756 -0
  15. package/dist/cjs/src/queue/EventQueue.d.ts +28 -0
  16. package/dist/cjs/src/queue/EventQueue.js +97 -19
  17. package/dist/cjs/src/queue/type.d.ts +1 -0
  18. package/dist/cjs/src/tracking/TrackingPolicy.d.ts +146 -0
  19. package/dist/cjs/src/tracking/TrackingPolicy.js +200 -0
  20. package/dist/cjs/src/utils/mergeDeepRight.d.ts +22 -0
  21. package/dist/cjs/src/utils/mergeDeepRight.js +54 -0
  22. package/dist/cjs/src/validators/address.d.ts +1 -1
  23. package/dist/cjs/src/version.d.ts +1 -1
  24. package/dist/cjs/src/version.js +1 -1
  25. package/dist/cjs/src/wagmi/WagmiEventHandler.d.ts +193 -1
  26. package/dist/cjs/src/wagmi/WagmiEventHandler.js +1317 -77
  27. package/dist/cjs/src/wagmi/types.d.ts +6 -0
  28. package/dist/cjs/src/wallet/WalletStateStore.d.ts +223 -0
  29. package/dist/cjs/src/wallet/WalletStateStore.js +515 -0
  30. package/dist/esm/src/FormoAnalytics.d.ts +90 -240
  31. package/dist/esm/src/FormoAnalytics.js +433 -1841
  32. package/dist/esm/src/constants/base.d.ts +0 -1
  33. package/dist/esm/src/constants/base.js +0 -2
  34. package/dist/esm/src/event/EventFactory.js +1 -1
  35. package/dist/esm/src/event/EventManager.d.ts +3 -1
  36. package/dist/esm/src/event/EventManager.js +5 -1
  37. package/dist/esm/src/event/type.d.ts +1 -0
  38. package/dist/esm/src/evm/EvmEventTracker.d.ts +175 -0
  39. package/dist/esm/src/evm/EvmEventTracker.js +1027 -0
  40. package/dist/esm/src/evm/EvmProviderRegistry.d.ts +132 -0
  41. package/dist/esm/src/evm/EvmProviderRegistry.js +345 -0
  42. package/dist/esm/src/evm/EvmRequestTracker.d.ts +120 -0
  43. package/dist/esm/src/evm/EvmRequestTracker.js +753 -0
  44. package/dist/esm/src/queue/EventQueue.d.ts +28 -0
  45. package/dist/esm/src/queue/EventQueue.js +97 -19
  46. package/dist/esm/src/queue/type.d.ts +1 -0
  47. package/dist/esm/src/tracking/TrackingPolicy.d.ts +146 -0
  48. package/dist/esm/src/tracking/TrackingPolicy.js +197 -0
  49. package/dist/esm/src/utils/mergeDeepRight.d.ts +22 -0
  50. package/dist/esm/src/utils/mergeDeepRight.js +51 -0
  51. package/dist/esm/src/version.d.ts +1 -1
  52. package/dist/esm/src/version.js +1 -1
  53. package/dist/esm/src/wagmi/WagmiEventHandler.d.ts +193 -1
  54. package/dist/esm/src/wagmi/WagmiEventHandler.js +1315 -76
  55. package/dist/esm/src/wagmi/types.d.ts +6 -0
  56. package/dist/esm/src/wallet/WalletStateStore.d.ts +223 -0
  57. package/dist/esm/src/wallet/WalletStateStore.js +512 -0
  58. package/dist/index.umd.min.js +1 -1
  59. package/package.json +5 -13
  60. package/dist/cjs/src/ramda/internal/_curry1.d.ts +0 -10
  61. package/dist/cjs/src/ramda/internal/_curry1.js +0 -26
  62. package/dist/cjs/src/ramda/internal/_curry2.d.ts +0 -10
  63. package/dist/cjs/src/ramda/internal/_curry2.js +0 -43
  64. package/dist/cjs/src/ramda/internal/_curry3.d.ts +0 -10
  65. package/dist/cjs/src/ramda/internal/_curry3.js +0 -74
  66. package/dist/cjs/src/ramda/internal/_has.d.ts +0 -2
  67. package/dist/cjs/src/ramda/internal/_has.js +0 -7
  68. package/dist/cjs/src/ramda/internal/_isObject.d.ts +0 -2
  69. package/dist/cjs/src/ramda/internal/_isObject.js +0 -7
  70. package/dist/cjs/src/ramda/internal/_isPlaceholder.d.ts +0 -2
  71. package/dist/cjs/src/ramda/internal/_isPlaceholder.js +0 -7
  72. package/dist/cjs/src/ramda/mergeDeepRight.d.ts +0 -3
  73. package/dist/cjs/src/ramda/mergeDeepRight.js +0 -14
  74. package/dist/cjs/src/ramda/mergeDeepWithKey.d.ts +0 -3
  75. package/dist/cjs/src/ramda/mergeDeepWithKey.js +0 -20
  76. package/dist/cjs/src/ramda/mergeWithKey.d.ts +0 -3
  77. package/dist/cjs/src/ramda/mergeWithKey.js +0 -26
  78. package/dist/esm/src/ramda/internal/_curry1.d.ts +0 -10
  79. package/dist/esm/src/ramda/internal/_curry1.js +0 -20
  80. package/dist/esm/src/ramda/internal/_curry2.d.ts +0 -10
  81. package/dist/esm/src/ramda/internal/_curry2.js +0 -37
  82. package/dist/esm/src/ramda/internal/_curry3.d.ts +0 -10
  83. package/dist/esm/src/ramda/internal/_curry3.js +0 -68
  84. package/dist/esm/src/ramda/internal/_has.d.ts +0 -2
  85. package/dist/esm/src/ramda/internal/_has.js +0 -4
  86. package/dist/esm/src/ramda/internal/_isObject.d.ts +0 -2
  87. package/dist/esm/src/ramda/internal/_isObject.js +0 -4
  88. package/dist/esm/src/ramda/internal/_isPlaceholder.d.ts +0 -2
  89. package/dist/esm/src/ramda/internal/_isPlaceholder.js +0 -4
  90. package/dist/esm/src/ramda/mergeDeepRight.d.ts +0 -3
  91. package/dist/esm/src/ramda/mergeDeepRight.js +0 -9
  92. package/dist/esm/src/ramda/mergeDeepWithKey.d.ts +0 -3
  93. package/dist/esm/src/ramda/mergeDeepWithKey.js +0 -15
  94. package/dist/esm/src/ramda/mergeWithKey.d.ts +0 -3
  95. package/dist/esm/src/ramda/mergeWithKey.js +0 -21
@@ -0,0 +1,756 @@
1
+ "use strict";
2
+ var __assign = (this && this.__assign) || function () {
3
+ __assign = Object.assign || function(t) {
4
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
5
+ s = arguments[i];
6
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7
+ t[p] = s[p];
8
+ }
9
+ return t;
10
+ };
11
+ return __assign.apply(this, arguments);
12
+ };
13
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
14
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
15
+ return new (P || (P = Promise))(function (resolve, reject) {
16
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
17
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
18
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
19
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
20
+ });
21
+ };
22
+ var __generator = (this && this.__generator) || function (thisArg, body) {
23
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
24
+ return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
25
+ function verb(n) { return function (v) { return step([n, v]); }; }
26
+ function step(op) {
27
+ if (f) throw new TypeError("Generator is already executing.");
28
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
29
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
30
+ if (y = 0, t) op = [op[0] & 2, t.value];
31
+ switch (op[0]) {
32
+ case 0: case 1: t = op; break;
33
+ case 4: _.label++; return { value: op[1], done: false };
34
+ case 5: _.label++; y = op[1]; op = [0]; continue;
35
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
36
+ default:
37
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
38
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
39
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
40
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
41
+ if (t[2]) _.ops.pop();
42
+ _.trys.pop(); continue;
43
+ }
44
+ op = body.call(thisArg, _);
45
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
46
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
47
+ }
48
+ };
49
+ var __rest = (this && this.__rest) || function (s, e) {
50
+ var t = {};
51
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
52
+ t[p] = s[p];
53
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
54
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
55
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
56
+ t[p[i]] = s[p[i]];
57
+ }
58
+ return t;
59
+ };
60
+ Object.defineProperty(exports, "__esModule", { value: true });
61
+ exports.EvmRequestTracker = void 0;
62
+ var logger_1 = require("../logger");
63
+ var chain_1 = require("../utils/chain");
64
+ var address_1 = require("../utils/address");
65
+ var types_1 = require("../types");
66
+ /**
67
+ * Decode a hex-encoded `personal_sign` message.
68
+ *
69
+ * Deliberately not `Buffer.from(hex, "hex")`. `Buffer` is a Node global with
70
+ * no polyfill in this bundle, so in a browser that line threw and the
71
+ * signature event was dropped with it - silently, which is the worst way for
72
+ * analytics to fail. `TextDecoder` is available everywhere this SDK runs, and
73
+ * `TextEncoder` is already used elsewhere in the queue.
74
+ *
75
+ * Malformed input decodes to as much as can be read rather than throwing: a
76
+ * message we cannot fully read is still worth reporting the signature for.
77
+ */
78
+ function hexToUtf8(hex) {
79
+ var clean = typeof hex === "string" && hex.startsWith("0x") ? hex.slice(2) : hex;
80
+ if (typeof clean !== "string" || clean.length === 0)
81
+ return "";
82
+ var byteCount = Math.floor(clean.length / 2);
83
+ var bytes = new Uint8Array(byteCount);
84
+ for (var i = 0; i < byteCount; i++) {
85
+ var byte = Number.parseInt(clean.substring(i * 2, i * 2 + 2), 16);
86
+ if (Number.isNaN(byte))
87
+ return new TextDecoder().decode(bytes.subarray(0, i));
88
+ bytes[i] = byte;
89
+ }
90
+ return new TextDecoder().decode(bytes);
91
+ }
92
+ /**
93
+ * Autocapture for signatures and transactions, by wrapping a provider's
94
+ * `request`.
95
+ *
96
+ * The wrapper is deliberately thin: it observes the call the dapp was already
97
+ * making and never issues one of its own. That rule is why the chain a
98
+ * request ran on is read from `eth_chainId` calls the app makes, rather than
99
+ * probed - an SDK-issued lookup on a serialising transport can wedge the
100
+ * user's wallet, which is never an acceptable price for a label.
101
+ */
102
+ var EvmRequestTracker = /** @class */ (function () {
103
+ function EvmRequestTracker(wallet, registry, deps) {
104
+ this.wallet = wallet;
105
+ this.registry = registry;
106
+ this.deps = deps;
107
+ /**
108
+ * Timers for receipt and batch-status polling.
109
+ *
110
+ * A poll re-arms for up to thirty seconds, so a torn-down instance would
111
+ * otherwise keep asking a wallet about transactions nobody is listening
112
+ * for, and hold the process open for the whole window. That is the same
113
+ * shape as the batch timer that hung the test suite in #338.
114
+ */
115
+ this.polls = new Set();
116
+ this.disposed = false;
117
+ }
118
+ /** Stop every poll in flight. Terminal, like the event queue's close(). */
119
+ EvmRequestTracker.prototype.cleanup = function () {
120
+ this.disposed = true;
121
+ this.polls.forEach(function (timer) { return clearTimeout(timer); });
122
+ this.polls.clear();
123
+ };
124
+ /** Re-arm a poll, unless this tracker has been torn down. */
125
+ EvmRequestTracker.prototype.schedulePoll = function (fn, delayMs) {
126
+ var _this = this;
127
+ if (this.disposed)
128
+ return;
129
+ var timer = setTimeout(function () {
130
+ _this.polls.delete(timer);
131
+ fn();
132
+ }, delayMs);
133
+ this.polls.add(timer);
134
+ };
135
+ /**
136
+ * Wrap a provider's `request`. Returns whether the wrapper is installed:
137
+ * a caller that marks the provider as tracked on a false return would
138
+ * never retry it, and every signature and transaction from that wallet
139
+ * would be missed for the rest of the session.
140
+ */
141
+ EvmRequestTracker.prototype.registerRequestListeners = function (provider) {
142
+ var _this = this;
143
+ logger_1.logger.info("registerRequestListeners");
144
+ if (!provider) {
145
+ logger_1.logger.error("Provider not found for request (signature, transaction) tracking");
146
+ return false;
147
+ }
148
+ // Check if the provider is already wrapped with our SDK's wrapper
149
+ var currentRequest = provider.request;
150
+ if (this.registry.isWrapped(provider, currentRequest)) {
151
+ logger_1.logger.info("Provider already wrapped with our SDK; skipping request wrapping.");
152
+ return true;
153
+ }
154
+ var request = provider.request.bind(provider);
155
+ var wrappedRequest = function (_a) { return __awaiter(_this, [_a], void 0, function (_b) {
156
+ var generation_1, responsePromise, capturedChainId_1, response_1, error_1, rpcError, txPromise, txChainId_1, transactionHash_1, error_2, rpcError;
157
+ var _this = this;
158
+ var method = _b.method, params = _b.params;
159
+ return __generator(this, function (_c) {
160
+ switch (_c.label) {
161
+ case 0:
162
+ // Learn the chain from a call the APP was making anyway.
163
+ //
164
+ // A standards-compliant provider need not expose a synchronous `chainId`
165
+ // property, and if it connected before the SDK initialised, its
166
+ // `connect` event is never replayed. Such a provider stayed unknown -
167
+ // reported as chain 0, and with `excludeChains` configured its events
168
+ // were dropped even on an allowed chain. This adds no request of its
169
+ // own; it only reads the answer to one the dapp already sent.
170
+ if (method === "eth_chainId") {
171
+ generation_1 = this.registry.chainGeneration(provider);
172
+ return [2 /*return*/, request({ method: method, params: params }).then(function (result) {
173
+ // A `chainChanged` for THIS provider may have landed while this was
174
+ // in flight. It is newer by definition, so it must not be
175
+ // overwritten by this answer.
176
+ if (generation_1 === (_this.registry.chainGeneration(provider)) &&
177
+ typeof result === "string") {
178
+ _this.registry.rememberChain(provider, (0, chain_1.parseChainId)(result));
179
+ }
180
+ return result;
181
+ })];
182
+ }
183
+ if (!(Array.isArray(params) &&
184
+ ["eth_signTypedData_v4", "personal_sign"].includes(method))) return [3 /*break*/, 4];
185
+ if (!this.deps.isAutocaptureEnabled("signature")) {
186
+ logger_1.logger.debug("Signature event skipped (autocapture.signature: false)", { method: method });
187
+ return [2 /*return*/, request({ method: method, params: params })];
188
+ }
189
+ responsePromise = request({ method: method, params: params });
190
+ // Attach a no-op handler now so a rejection arriving before the await
191
+ // below is never reported as unhandled. The real handling is there.
192
+ responsePromise.catch(function () { return undefined; });
193
+ capturedChainId_1 = this.registry.resolveChainId(provider);
194
+ // Fire-and-forget tracking
195
+ (function () { return __awaiter(_this, void 0, void 0, function () {
196
+ var e_1;
197
+ return __generator(this, function (_a) {
198
+ switch (_a.label) {
199
+ case 0:
200
+ _a.trys.push([0, 2, , 3]);
201
+ return [4 /*yield*/, this.deps.signature(__assign({ status: types_1.SignatureStatus.REQUESTED }, this.buildSignatureEventPayload(method, params, undefined, capturedChainId_1, provider)))];
202
+ case 1:
203
+ _a.sent();
204
+ return [3 /*break*/, 3];
205
+ case 2:
206
+ e_1 = _a.sent();
207
+ logger_1.logger.error("Formo: Failed to track signature request", e_1);
208
+ return [3 /*break*/, 3];
209
+ case 3: return [2 /*return*/];
210
+ }
211
+ });
212
+ }); })();
213
+ _c.label = 1;
214
+ case 1:
215
+ _c.trys.push([1, 3, , 4]);
216
+ return [4 /*yield*/, responsePromise];
217
+ case 2:
218
+ response_1 = _c.sent();
219
+ // Track signature confirmation only for truthy responses
220
+ if (response_1) {
221
+ (function () { return __awaiter(_this, void 0, void 0, function () {
222
+ var e_2;
223
+ return __generator(this, function (_a) {
224
+ switch (_a.label) {
225
+ case 0:
226
+ _a.trys.push([0, 2, , 3]);
227
+ return [4 /*yield*/, this.deps.signature(__assign({ status: types_1.SignatureStatus.CONFIRMED }, this.buildSignatureEventPayload(method, params, response_1, capturedChainId_1, provider)))];
228
+ case 1:
229
+ _a.sent();
230
+ return [3 /*break*/, 3];
231
+ case 2:
232
+ e_2 = _a.sent();
233
+ logger_1.logger.error("Formo: Failed to track signature confirmation", e_2);
234
+ return [3 /*break*/, 3];
235
+ case 3: return [2 /*return*/];
236
+ }
237
+ });
238
+ }); })();
239
+ }
240
+ return [2 /*return*/, response_1];
241
+ case 3:
242
+ error_1 = _c.sent();
243
+ rpcError = error_1;
244
+ if ((rpcError === null || rpcError === void 0 ? void 0 : rpcError.code) === 4001) {
245
+ // Use the already cast rpcError to avoid duplication
246
+ (function () { return __awaiter(_this, void 0, void 0, function () {
247
+ var e_3;
248
+ return __generator(this, function (_a) {
249
+ switch (_a.label) {
250
+ case 0:
251
+ _a.trys.push([0, 2, , 3]);
252
+ return [4 /*yield*/, this.deps.signature(__assign({ status: types_1.SignatureStatus.REJECTED }, this.buildSignatureEventPayload(method, params, undefined, capturedChainId_1, provider)))];
253
+ case 1:
254
+ _a.sent();
255
+ return [3 /*break*/, 3];
256
+ case 2:
257
+ e_3 = _a.sent();
258
+ logger_1.logger.error("Formo: Failed to track signature rejection", e_3);
259
+ return [3 /*break*/, 3];
260
+ case 3: return [2 /*return*/];
261
+ }
262
+ });
263
+ }); })();
264
+ }
265
+ throw error_1;
266
+ case 4:
267
+ // Handle EIP-5792 batched calls.
268
+ //
269
+ // Smart accounts send through `wallet_sendCalls` rather than
270
+ // `eth_sendTransaction`, and until now the SDK understood only the
271
+ // latter: those transactions were not captured at all, silently. That
272
+ // is the same failure shape as the missing-connect bug that started
273
+ // this work - nothing errors, the data is simply absent.
274
+ if (method === "wallet_sendCalls" && Array.isArray(params) && params[0]) {
275
+ return [2 /*return*/, this.trackBatchedCalls(provider, request, params)];
276
+ }
277
+ if (!(Array.isArray(params) &&
278
+ method === "eth_sendTransaction" &&
279
+ params[0])) return [3 /*break*/, 8];
280
+ if (!this.deps.isAutocaptureEnabled("transaction")) {
281
+ logger_1.logger.debug("Transaction event skipped (autocapture.transaction: false)", { method: method });
282
+ return [2 /*return*/, request({ method: method, params: params })];
283
+ }
284
+ txPromise = request({ method: method, params: params });
285
+ txPromise.catch(function () { return undefined; });
286
+ txChainId_1 = this.registry.resolveChainId(provider);
287
+ (function () { return __awaiter(_this, void 0, void 0, function () {
288
+ var payload, e_4;
289
+ return __generator(this, function (_a) {
290
+ switch (_a.label) {
291
+ case 0:
292
+ _a.trys.push([0, 3, , 4]);
293
+ return [4 /*yield*/, this.buildTransactionEventPayload(params, provider, txChainId_1)];
294
+ case 1:
295
+ payload = _a.sent();
296
+ return [4 /*yield*/, this.deps.transaction(__assign({ status: types_1.TransactionStatus.STARTED }, payload))];
297
+ case 2:
298
+ _a.sent();
299
+ return [3 /*break*/, 4];
300
+ case 3:
301
+ e_4 = _a.sent();
302
+ logger_1.logger.error("Formo: Failed to track transaction start", e_4);
303
+ return [3 /*break*/, 4];
304
+ case 4: return [2 /*return*/];
305
+ }
306
+ });
307
+ }); })();
308
+ _c.label = 5;
309
+ case 5:
310
+ _c.trys.push([5, 7, , 8]);
311
+ return [4 /*yield*/, txPromise];
312
+ case 6:
313
+ transactionHash_1 = _c.sent();
314
+ (function () { return __awaiter(_this, void 0, void 0, function () {
315
+ var payload, e_5;
316
+ return __generator(this, function (_a) {
317
+ switch (_a.label) {
318
+ case 0:
319
+ _a.trys.push([0, 3, , 4]);
320
+ return [4 /*yield*/, this.buildTransactionEventPayload(params, provider, txChainId_1)];
321
+ case 1:
322
+ payload = _a.sent();
323
+ return [4 /*yield*/, this.deps.transaction(__assign(__assign({ status: types_1.TransactionStatus.BROADCASTED }, payload), { transactionHash: transactionHash_1 }))];
324
+ case 2:
325
+ _a.sent();
326
+ // Start async polling for transaction receipt
327
+ this.pollTransactionReceipt(provider, transactionHash_1, payload);
328
+ return [3 /*break*/, 4];
329
+ case 3:
330
+ e_5 = _a.sent();
331
+ logger_1.logger.error("Formo: Failed to track transaction broadcast", e_5);
332
+ return [3 /*break*/, 4];
333
+ case 4: return [2 /*return*/];
334
+ }
335
+ });
336
+ }); })();
337
+ return [2 /*return*/, transactionHash_1];
338
+ case 7:
339
+ error_2 = _c.sent();
340
+ rpcError = error_2;
341
+ if ((rpcError === null || rpcError === void 0 ? void 0 : rpcError.code) === 4001) {
342
+ // Use the already cast rpcError to avoid duplication
343
+ (function () { return __awaiter(_this, void 0, void 0, function () {
344
+ var payload, e_6;
345
+ return __generator(this, function (_a) {
346
+ switch (_a.label) {
347
+ case 0:
348
+ _a.trys.push([0, 3, , 4]);
349
+ return [4 /*yield*/, this.buildTransactionEventPayload(params, provider, txChainId_1)];
350
+ case 1:
351
+ payload = _a.sent();
352
+ return [4 /*yield*/, this.deps.transaction(__assign({ status: types_1.TransactionStatus.REJECTED }, payload))];
353
+ case 2:
354
+ _a.sent();
355
+ return [3 /*break*/, 4];
356
+ case 3:
357
+ e_6 = _a.sent();
358
+ logger_1.logger.error("Formo: Failed to track transaction rejection", e_6);
359
+ return [3 /*break*/, 4];
360
+ case 4: return [2 /*return*/];
361
+ }
362
+ });
363
+ }); })();
364
+ }
365
+ throw error_2;
366
+ case 8: return [2 /*return*/, request({ method: method, params: params })];
367
+ }
368
+ });
369
+ }); };
370
+ // Mark the wrapper so we can detect if request is replaced externally and keep a reference on provider
371
+ wrappedRequest[types_1.WRAPPED_REQUEST_SYMBOL] = true;
372
+ // Both writes go inside the try. A frozen or non-extensible provider
373
+ // throws on the symbol assignment just as readily as on `request`, and
374
+ // that one used to sit outside the guard, so an unwrappable provider
375
+ // aborted registration instead of being skipped.
376
+ try {
377
+ provider[types_1.WRAPPED_REQUEST_REF_SYMBOL] =
378
+ wrappedRequest;
379
+ provider.request = wrappedRequest;
380
+ return true;
381
+ }
382
+ catch (e) {
383
+ logger_1.logger.warn("Failed to wrap provider.request; skipping", e);
384
+ return false;
385
+ }
386
+ };
387
+ EvmRequestTracker.prototype.buildSignatureEventPayload = function (method, params,
388
+ // Intentionally not read. Kept for positional call-site arity.
389
+ _response, chainId, provider) {
390
+ var _a;
391
+ var rawAddress = method === "personal_sign"
392
+ ? params[1]
393
+ : params[0];
394
+ var validAddress = (0, address_1.validateAndChecksumAddress)(rawAddress);
395
+ if (!validAddress) {
396
+ throw new Error("Invalid address in signature payload: ".concat(rawAddress));
397
+ }
398
+ var effectiveChainId = (_a = chainId !== null && chainId !== void 0 ? chainId : this.wallet.evmChainId) !== null && _a !== void 0 ? _a : undefined;
399
+ // Only the active provider may write central wallet state - see the same
400
+ // guard in buildTransactionEventPayload.
401
+ if (!provider || provider === this.wallet.provider || !this.wallet.provider) {
402
+ this.wallet.backfill(validAddress, effectiveChainId, provider);
403
+ }
404
+ var basePayload = {
405
+ chainId: effectiveChainId,
406
+ address: validAddress,
407
+ };
408
+ if (method === "personal_sign") {
409
+ return __assign(__assign({}, basePayload), { message: hexToUtf8(params[0]) });
410
+ }
411
+ // eth_signTypedData*: params[1] is the full EIP-712 struct.
412
+ return __assign(__assign({}, basePayload), { message: params[1] });
413
+ };
414
+ EvmRequestTracker.prototype.buildTransactionEventPayload = function (params, provider,
415
+ /**
416
+ * Chain resolved once for this request's whole lifecycle. Passing it keeps
417
+ * every status of one transaction on the same chain even if the user
418
+ * switches network while the wallet prompt is open.
419
+ */
420
+ capturedChainId) {
421
+ return __awaiter(this, void 0, void 0, function () {
422
+ var _a, data, from, to, value, validAddress, chainId;
423
+ return __generator(this, function (_b) {
424
+ _a = params[0], data = _a.data, from = _a.from, to = _a.to, value = _a.value;
425
+ validAddress = (0, address_1.validateAndChecksumAddress)(from);
426
+ if (!validAddress) {
427
+ throw new Error("Invalid address in transaction payload: ".concat(from));
428
+ }
429
+ chainId = capturedChainId !== null && capturedChainId !== void 0 ? capturedChainId : this.registry.resolveChainId(provider);
430
+ // Only the ACTIVE provider may write central wallet state. A request from
431
+ // a second, non-active wallet would otherwise overwrite the active
432
+ // provider's address and chain, and every later request through the active
433
+ // provider would then trust the other wallet's chain - persistent
434
+ // mis-attribution, and a way around `excludeChains`.
435
+ if (!provider || provider === this.wallet.provider || !this.wallet.provider) {
436
+ this.wallet.backfill(validAddress, chainId, provider);
437
+ }
438
+ return [2 /*return*/, {
439
+ chainId: chainId,
440
+ data: data,
441
+ address: validAddress,
442
+ to: to,
443
+ value: value,
444
+ }];
445
+ });
446
+ });
447
+ };
448
+ /**
449
+ * Polls for transaction receipt and emits tx.status = CONFIRMED or REVERTED.
450
+ */
451
+ EvmRequestTracker.prototype.pollTransactionReceipt = function (provider_1, transactionHash_2, payload_1) {
452
+ return __awaiter(this, arguments, void 0, function (provider, transactionHash, payload, maxAttempts, intervalMs) {
453
+ var attempts, poll;
454
+ var _this = this;
455
+ if (maxAttempts === void 0) { maxAttempts = 10; }
456
+ if (intervalMs === void 0) { intervalMs = 3000; }
457
+ return __generator(this, function (_a) {
458
+ attempts = 0;
459
+ if (!provider)
460
+ return [2 /*return*/];
461
+ poll = function () { return __awaiter(_this, void 0, void 0, function () {
462
+ var receipt, e_7;
463
+ return __generator(this, function (_a) {
464
+ switch (_a.label) {
465
+ case 0:
466
+ if (this.disposed)
467
+ return [2 /*return*/];
468
+ _a.label = 1;
469
+ case 1:
470
+ _a.trys.push([1, 3, , 4]);
471
+ return [4 /*yield*/, provider.request({
472
+ method: "eth_getTransactionReceipt",
473
+ params: [transactionHash],
474
+ })];
475
+ case 2:
476
+ receipt = (_a.sent());
477
+ if (receipt) {
478
+ // status: 1 = success, 0 = reverted
479
+ if (receipt.status === "0x1" || receipt.status === 1) {
480
+ this.deps
481
+ .transaction(__assign(__assign({ status: types_1.TransactionStatus.CONFIRMED }, payload), { transactionHash: transactionHash }))
482
+ .catch(function (e) {
483
+ return logger_1.logger.error("Formo: Failed to track transaction confirmation", e);
484
+ });
485
+ return [2 /*return*/];
486
+ }
487
+ else if (receipt.status === "0x0" || receipt.status === 0) {
488
+ this.deps
489
+ .transaction(__assign(__assign({ status: types_1.TransactionStatus.REVERTED }, payload), { transactionHash: transactionHash }))
490
+ .catch(function (e) {
491
+ return logger_1.logger.error("Formo: Failed to track transaction revert", e);
492
+ });
493
+ return [2 /*return*/];
494
+ }
495
+ }
496
+ return [3 /*break*/, 4];
497
+ case 3:
498
+ e_7 = _a.sent();
499
+ logger_1.logger.error("Error polling transaction receipt", e_7);
500
+ return [3 /*break*/, 4];
501
+ case 4:
502
+ attempts++;
503
+ if (attempts < maxAttempts)
504
+ this.schedulePoll(poll, intervalMs);
505
+ return [2 /*return*/];
506
+ }
507
+ });
508
+ }); };
509
+ poll();
510
+ return [2 /*return*/];
511
+ });
512
+ });
513
+ };
514
+ /**
515
+ * One `transaction` event per call in an EIP-5792 batch.
516
+ *
517
+ * A batch is not a transaction. It maps to several on-chain transactions,
518
+ * so reporting it as one event would understate volume and make revenue and
519
+ * per-contract attribution wrong for every app that adopts smart accounts.
520
+ * Each call is reported on its own, carrying the batch id so the calls can
521
+ * be reassembled downstream.
522
+ *
523
+ * Status is per BATCH, because that is what `wallet_getCallsStatus` reports.
524
+ * When it resolves, every call in the batch moves together, except where
525
+ * per-call receipts say otherwise on a non-atomic batch.
526
+ */
527
+ EvmRequestTracker.prototype.trackBatchedCalls = function (provider, request, params) {
528
+ return __awaiter(this, void 0, void 0, function () {
529
+ 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;
530
+ var _c;
531
+ return __generator(this, function (_d) {
532
+ switch (_d.label) {
533
+ case 0:
534
+ if (!this.deps.isAutocaptureEnabled("transaction")) {
535
+ logger_1.logger.debug("Transaction event skipped (autocapture.transaction: false)", {
536
+ method: "wallet_sendCalls",
537
+ });
538
+ return [2 /*return*/, request({ method: "wallet_sendCalls", params: params })];
539
+ }
540
+ sendPromise = request({ method: "wallet_sendCalls", params: params });
541
+ sendPromise.catch(function () { return undefined; });
542
+ batch = params[0];
543
+ calls = Array.isArray(batch === null || batch === void 0 ? void 0 : batch.calls) ? batch.calls : [];
544
+ declared = typeof (batch === null || batch === void 0 ? void 0 : batch.chainId) === "string" ? (0, chain_1.parseChainId)(batch.chainId) : undefined;
545
+ chainId = declared || this.registry.resolveChainId(provider);
546
+ address = (0, address_1.validateAndChecksumAddress)((_c = batch === null || batch === void 0 ? void 0 : batch.from) !== null && _c !== void 0 ? _c : "");
547
+ if (!address) {
548
+ // Nothing can be attributed without a sender, and inventing one would
549
+ // be worse than reporting nothing. The user's call still goes through.
550
+ logger_1.logger.warn("Formo: wallet_sendCalls has no valid `from`; not tracking", {
551
+ from: batch === null || batch === void 0 ? void 0 : batch.from,
552
+ });
553
+ return [2 /*return*/, sendPromise];
554
+ }
555
+ if (calls.length === 0) {
556
+ logger_1.logger.debug("Formo: wallet_sendCalls carried no calls");
557
+ return [2 /*return*/, sendPromise];
558
+ }
559
+ // Only the ACTIVE provider may write central wallet state, for the same
560
+ // reason as the single-transaction path: a second wallet would otherwise
561
+ // overwrite the active provider's address and chain.
562
+ if (!provider || provider === this.wallet.provider || !this.wallet.provider) {
563
+ this.wallet.backfill(address, chainId, provider);
564
+ }
565
+ payloads = calls.map(function (call, index) { return ({
566
+ chainId: chainId,
567
+ address: address,
568
+ to: call === null || call === void 0 ? void 0 : call.to,
569
+ value: call === null || call === void 0 ? void 0 : call.value,
570
+ data: call === null || call === void 0 ? void 0 : call.data,
571
+ properties: {
572
+ batch_size: calls.length,
573
+ batch_index: index,
574
+ },
575
+ }); });
576
+ // STARTED carries no batch id: the wallet has not issued one yet, exactly
577
+ // as `eth_sendTransaction` has no hash at this point. Position within the
578
+ // batch is known, and is what groups these until the id arrives.
579
+ for (_i = 0, payloads_1 = payloads; _i < payloads_1.length; _i++) {
580
+ p = payloads_1[_i];
581
+ properties = p.properties, rest = __rest(p, ["properties"]);
582
+ this.deps
583
+ .transaction(__assign({ status: types_1.TransactionStatus.STARTED }, rest), properties)
584
+ .catch(function (e) { return logger_1.logger.error("Formo: Failed to track batch call start", e); });
585
+ }
586
+ _d.label = 1;
587
+ case 1:
588
+ _d.trys.push([1, 3, , 4]);
589
+ return [4 /*yield*/, sendPromise];
590
+ case 2:
591
+ result = _d.sent();
592
+ batchId = this.readBatchId(result);
593
+ for (_a = 0, payloads_2 = payloads; _a < payloads_2.length; _a++) {
594
+ p = payloads_2[_a];
595
+ properties = p.properties, rest = __rest(p, ["properties"]);
596
+ this.deps
597
+ .transaction(__assign({ status: types_1.TransactionStatus.BROADCASTED }, rest), __assign(__assign({}, properties), (batchId ? { batch_id: batchId } : {})))
598
+ .catch(function (e) {
599
+ return logger_1.logger.error("Formo: Failed to track batch call broadcast", e);
600
+ });
601
+ }
602
+ if (batchId)
603
+ this.pollBatchStatus(provider, batchId, payloads);
604
+ return [2 /*return*/, result];
605
+ case 3:
606
+ error_3 = _d.sent();
607
+ rpcError = error_3;
608
+ if ((rpcError === null || rpcError === void 0 ? void 0 : rpcError.code) === 4001) {
609
+ // One rejection dismisses the whole prompt, so every call in it is
610
+ // rejected. Reporting only the first would undercount.
611
+ for (_b = 0, payloads_3 = payloads; _b < payloads_3.length; _b++) {
612
+ p = payloads_3[_b];
613
+ properties = p.properties, rest = __rest(p, ["properties"]);
614
+ this.deps
615
+ .transaction(__assign({ status: types_1.TransactionStatus.REJECTED }, rest), properties)
616
+ .catch(function (e) {
617
+ return logger_1.logger.error("Formo: Failed to track batch call rejection", e);
618
+ });
619
+ }
620
+ }
621
+ throw error_3;
622
+ case 4: return [2 /*return*/];
623
+ }
624
+ });
625
+ });
626
+ };
627
+ /**
628
+ * How one call in a settled batch ended.
629
+ *
630
+ * A per-call receipt is authoritative where it exists: that is what makes a
631
+ * partially reverted non-atomic batch report honestly rather than tarring
632
+ * every call with the batch's worst outcome. A receipt whose own status is
633
+ * unreadable falls back to the batch verdict rather than being assumed good.
634
+ *
635
+ * The codes are EIP-5792's: 200 confirmed, 400 failed BEFORE landing on
636
+ * chain, 500 reverted, 600 partially reverted. 400 is a rejection, not a
637
+ * revert - nothing was mined, so calling it reverted would misreport gas
638
+ * spent and on-chain activity that never happened.
639
+ *
640
+ * Returns undefined when the call cannot be decided, which happens on 600
641
+ * for a call the wallet gave no receipt for.
642
+ */
643
+ EvmRequestTracker.prototype.batchCallOutcome = function (code, receipt) {
644
+ var receiptStatus = receipt === null || receipt === void 0 ? void 0 : receipt.status;
645
+ if (receiptStatus !== undefined) {
646
+ return receiptStatus === "0x0" || receiptStatus === 0
647
+ ? types_1.TransactionStatus.REVERTED
648
+ : types_1.TransactionStatus.CONFIRMED;
649
+ }
650
+ if (code >= 600)
651
+ return undefined;
652
+ if (code >= 500)
653
+ return types_1.TransactionStatus.REVERTED;
654
+ if (code >= 400)
655
+ return types_1.TransactionStatus.REJECTED;
656
+ return types_1.TransactionStatus.CONFIRMED;
657
+ };
658
+ /**
659
+ * The batch identifier from a `wallet_sendCalls` result.
660
+ *
661
+ * EIP-5792 settled on `{ id }`, but wallets shipped against the earlier
662
+ * draft return a bare string. Both are accepted so a wallet on either
663
+ * version is still grouped.
664
+ */
665
+ EvmRequestTracker.prototype.readBatchId = function (result) {
666
+ if (typeof result === "string" && result.length > 0)
667
+ return result;
668
+ if (result && typeof result === "object") {
669
+ var id = result.id;
670
+ if (typeof id === "string" && id.length > 0)
671
+ return id;
672
+ }
673
+ return undefined;
674
+ };
675
+ /**
676
+ * Resolve a batch through `wallet_getCallsStatus`.
677
+ *
678
+ * The status codes are EIP-5792's: 100 pending, 200 confirmed, 400 failed
679
+ * before landing, 500 reverted, 600 partially reverted. Anything below 200
680
+ * means keep waiting.
681
+ *
682
+ * A per-call receipt wins over the batch verdict where one exists, which is
683
+ * what makes a partially reverted non-atomic batch report honestly instead
684
+ * of marking every call with the batch's worst outcome.
685
+ */
686
+ EvmRequestTracker.prototype.pollBatchStatus = function (provider_1, batchId_1, payloads_4) {
687
+ return __awaiter(this, arguments, void 0, function (provider, batchId, payloads, maxAttempts, intervalMs) {
688
+ var attempts, poll;
689
+ var _this = this;
690
+ if (maxAttempts === void 0) { maxAttempts = 10; }
691
+ if (intervalMs === void 0) { intervalMs = 3000; }
692
+ return __generator(this, function (_a) {
693
+ if (!provider)
694
+ return [2 /*return*/];
695
+ attempts = 0;
696
+ poll = function () { return __awaiter(_this, void 0, void 0, function () {
697
+ var res, code_1, receipts_1, e_8;
698
+ var _this = this;
699
+ return __generator(this, function (_a) {
700
+ switch (_a.label) {
701
+ case 0:
702
+ if (this.disposed)
703
+ return [2 /*return*/];
704
+ _a.label = 1;
705
+ case 1:
706
+ _a.trys.push([1, 3, , 4]);
707
+ return [4 /*yield*/, provider.request({
708
+ method: "wallet_getCallsStatus",
709
+ params: [batchId],
710
+ })];
711
+ case 2:
712
+ res = (_a.sent());
713
+ code_1 = typeof (res === null || res === void 0 ? void 0 : res.status) === "number" ? res.status : undefined;
714
+ if (code_1 !== undefined && code_1 >= 200) {
715
+ receipts_1 = Array.isArray(res === null || res === void 0 ? void 0 : res.receipts) ? res.receipts : [];
716
+ payloads.forEach(function (p, index) {
717
+ var receipt = receipts_1[index];
718
+ var outcome = _this.batchCallOutcome(code_1, receipt);
719
+ // 600 means SOME calls reverted, so a call with no receipt of its
720
+ // own has not been decided. Reporting it either way would invent
721
+ // a result; leaving it unsettled is the honest answer.
722
+ if (outcome === undefined)
723
+ return;
724
+ var properties = p.properties, rest = __rest(p, ["properties"]);
725
+ _this.deps
726
+ .transaction(__assign(__assign({ status: outcome }, rest), ((receipt === null || receipt === void 0 ? void 0 : receipt.transactionHash)
727
+ ? { transactionHash: receipt.transactionHash }
728
+ : {})), __assign(__assign({}, properties), { batch_id: batchId }))
729
+ .catch(function (e) {
730
+ return logger_1.logger.error("Formo: Failed to track batch call outcome", e);
731
+ });
732
+ });
733
+ return [2 /*return*/];
734
+ }
735
+ return [3 /*break*/, 4];
736
+ case 3:
737
+ e_8 = _a.sent();
738
+ logger_1.logger.error("Error polling batch call status", e_8);
739
+ return [3 /*break*/, 4];
740
+ case 4:
741
+ attempts++;
742
+ if (attempts < maxAttempts)
743
+ this.schedulePoll(poll, intervalMs);
744
+ return [2 /*return*/];
745
+ }
746
+ });
747
+ }); };
748
+ poll();
749
+ return [2 /*return*/];
750
+ });
751
+ });
752
+ };
753
+ return EvmRequestTracker;
754
+ }());
755
+ exports.EvmRequestTracker = EvmRequestTracker;
756
+ //# sourceMappingURL=EvmRequestTracker.js.map