@formo/analytics 1.35.2 → 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 (43) hide show
  1. package/dist/cjs/src/FormoAnalytics.d.ts +79 -363
  2. package/dist/cjs/src/FormoAnalytics.js +368 -2234
  3. package/dist/cjs/src/event/EventManager.d.ts +3 -1
  4. package/dist/cjs/src/event/EventManager.js +5 -1
  5. package/dist/cjs/src/event/type.d.ts +1 -0
  6. package/dist/cjs/src/evm/EvmEventTracker.d.ts +175 -0
  7. package/dist/cjs/src/evm/EvmEventTracker.js +1030 -0
  8. package/dist/cjs/src/evm/EvmProviderRegistry.d.ts +132 -0
  9. package/dist/cjs/src/evm/EvmProviderRegistry.js +348 -0
  10. package/dist/cjs/src/evm/EvmRequestTracker.d.ts +120 -0
  11. package/dist/cjs/src/evm/EvmRequestTracker.js +756 -0
  12. package/dist/cjs/src/queue/EventQueue.d.ts +28 -0
  13. package/dist/cjs/src/queue/EventQueue.js +97 -19
  14. package/dist/cjs/src/queue/type.d.ts +1 -0
  15. package/dist/cjs/src/tracking/TrackingPolicy.d.ts +146 -0
  16. package/dist/cjs/src/tracking/TrackingPolicy.js +200 -0
  17. package/dist/cjs/src/validators/address.d.ts +1 -1
  18. package/dist/cjs/src/version.d.ts +1 -1
  19. package/dist/cjs/src/version.js +1 -1
  20. package/dist/cjs/src/wallet/WalletStateStore.d.ts +223 -0
  21. package/dist/cjs/src/wallet/WalletStateStore.js +515 -0
  22. package/dist/esm/src/FormoAnalytics.d.ts +79 -363
  23. package/dist/esm/src/FormoAnalytics.js +369 -2235
  24. package/dist/esm/src/event/EventManager.d.ts +3 -1
  25. package/dist/esm/src/event/EventManager.js +5 -1
  26. package/dist/esm/src/event/type.d.ts +1 -0
  27. package/dist/esm/src/evm/EvmEventTracker.d.ts +175 -0
  28. package/dist/esm/src/evm/EvmEventTracker.js +1027 -0
  29. package/dist/esm/src/evm/EvmProviderRegistry.d.ts +132 -0
  30. package/dist/esm/src/evm/EvmProviderRegistry.js +345 -0
  31. package/dist/esm/src/evm/EvmRequestTracker.d.ts +120 -0
  32. package/dist/esm/src/evm/EvmRequestTracker.js +753 -0
  33. package/dist/esm/src/queue/EventQueue.d.ts +28 -0
  34. package/dist/esm/src/queue/EventQueue.js +97 -19
  35. package/dist/esm/src/queue/type.d.ts +1 -0
  36. package/dist/esm/src/tracking/TrackingPolicy.d.ts +146 -0
  37. package/dist/esm/src/tracking/TrackingPolicy.js +197 -0
  38. package/dist/esm/src/version.d.ts +1 -1
  39. package/dist/esm/src/version.js +1 -1
  40. package/dist/esm/src/wallet/WalletStateStore.d.ts +223 -0
  41. package/dist/esm/src/wallet/WalletStateStore.js +512 -0
  42. package/dist/index.umd.min.js +1 -1
  43. package/package.json +5 -4
@@ -0,0 +1,132 @@
1
+ import { EIP6963ProviderDetail } from "mipd";
2
+ import { Address, ChainID, EIP1193Provider, WrappedRequestFunction } from "../types";
3
+ /** What the registry needs from the SDK that owns it. */
4
+ export interface EvmProviderRegistryDeps {
5
+ /** The provider the SDK currently attributes activity to. */
6
+ activeProvider(): EIP1193Provider | undefined;
7
+ /** The active provider's chain, per central state. */
8
+ activeChainId(): ChainID | undefined;
9
+ /** The wallet already known for EVM, if any. */
10
+ knownEvmAddress(): Address | undefined;
11
+ /**
12
+ * A provider reported a chain.
13
+ *
14
+ * The registry records it per provider; whether central state should follow
15
+ * is the SDK's decision, not the registry's, because it depends on which
16
+ * namespace is active.
17
+ */
18
+ onChainObserved(provider: EIP1193Provider, chainId: number): void;
19
+ }
20
+ /**
21
+ * Which EVM wallets exist, and what is known about each.
22
+ *
23
+ * Split out of `FormoAnalytics` so "the provider registry" is one thing with
24
+ * one owner. It deliberately holds no event logic: registering listeners and
25
+ * reacting to them stays with the SDK for now, and moves next.
26
+ *
27
+ * Three provider sets, which are not the same thing:
28
+ * - `details` is every EIP-6963 provider discovered, with its metadata.
29
+ * - `tracked` is the subset that has listeners wired up.
30
+ * - `seen` guards `details` against duplicates.
31
+ */
32
+ export declare class EvmProviderRegistry {
33
+ private readonly deps;
34
+ private details;
35
+ private tracked;
36
+ private seen;
37
+ /** Last chain heard from each provider, whoever is active. */
38
+ private chainIds;
39
+ /**
40
+ * Bumped on every chain observation, PER PROVIDER, so a stale answer that
41
+ * arrives late cannot overwrite a newer one.
42
+ */
43
+ private chainGenerations;
44
+ /** Listeners this SDK attached, so teardown can remove exactly those. */
45
+ private listeners;
46
+ /** The window-injected provider, once detected, so it is not re-wrapped. */
47
+ private injectedDetail?;
48
+ constructor(deps: EvmProviderRegistryDeps);
49
+ get all(): readonly EIP6963ProviderDetail[];
50
+ isTracked(provider: EIP1193Provider): boolean;
51
+ markTracked(provider: EIP1193Provider): void;
52
+ forgetTracked(provider: EIP1193Provider): void;
53
+ trackedProviders(): EIP1193Provider[];
54
+ isSeen(provider: EIP1193Provider): boolean;
55
+ get injected(): EIP6963ProviderDetail | undefined;
56
+ set injected(detail: EIP6963ProviderDetail | undefined);
57
+ /** Counts for the public debug helpers. */
58
+ get counts(): {
59
+ totalProviders: number;
60
+ trackedProviders: number;
61
+ seenProviders: number;
62
+ };
63
+ /** Add a discovered provider once. Returns false if already present. */
64
+ add(detail: EIP6963ProviderDetail): boolean;
65
+ /**
66
+ * A provider's display name and rdns.
67
+ *
68
+ * EIP-6963 metadata is authoritative when we have it; otherwise fall back to
69
+ * sniffing the injected provider, which is all a pre-6963 wallet offers.
70
+ */
71
+ infoFor(provider: EIP1193Provider): {
72
+ name: string;
73
+ rdns: string;
74
+ };
75
+ addListener(provider: EIP1193Provider, event: string, listener: (...args: unknown[]) => void): void;
76
+ /** Remove only the listeners this SDK attached, never the host app's. */
77
+ removeListeners(provider: EIP1193Provider): void;
78
+ /** Events still attached to a provider after a failed teardown. */
79
+ attachedEvents(provider: EIP1193Provider): string[];
80
+ /**
81
+ * Whether this provider's `request` is already our wrapper.
82
+ *
83
+ * Checks the wrapper's own marker AND that the provider still points at
84
+ * that exact function, so a wallet that replaced `request` after we wrapped
85
+ * it is re-wrapped rather than left uninstrumented.
86
+ */
87
+ isWrapped(provider: EIP1193Provider, currentRequest: WrappedRequestFunction | undefined): boolean;
88
+ chainIdOf(provider: EIP1193Provider): number | undefined;
89
+ /** Advance and return this provider's chain-observation generation. */
90
+ bumpChainGeneration(provider: EIP1193Provider): number;
91
+ chainGeneration(provider: EIP1193Provider): number;
92
+ /**
93
+ * Record a provider's chain. Fed by `chainChanged` and `connect`, and by
94
+ * the one-off probe at tracking time, never from inside a user request.
95
+ */
96
+ rememberChain(provider: EIP1193Provider | undefined, chainId: number | undefined): void;
97
+ /**
98
+ * Resolve the chain an autocaptured request actually ran on.
99
+ *
100
+ * Central chain state is maintained by `chainChanged` from whichever
101
+ * provider is currently active. When a request arrives from a DIFFERENT
102
+ * tracked provider, which happens whenever a visitor has two wallets
103
+ * installed, that cached value describes the wrong wallet, and tagging the
104
+ * event with it silently mis-attributes the chain.
105
+ *
106
+ * Answered entirely from a per-provider snapshot. Deliberately SYNCHRONOUS
107
+ * and never issues an RPC.
108
+ *
109
+ * An earlier version called `eth_chainId` on the signing provider and
110
+ * time-boxed it with `Promise.race`. That is not safe: the race abandons
111
+ * the SDK's promise but cannot cancel the provider's request. On a
112
+ * transport that serialises (WalletConnect's relay socket, the very case
113
+ * this path exists to serve) an abandoned lookup stays at the head of the
114
+ * wallet's queue, and every later RPC the dapp makes queues behind it until
115
+ * reload. Mislabelling a chain is a reporting defect; wedging the user's
116
+ * wallet is not an acceptable way to avoid one.
117
+ *
118
+ * When nothing is known this reports 0 ("unknown") rather than guessing
119
+ * with the active provider's chain, which is known-wrong for another wallet.
120
+ */
121
+ resolveChainId(provider?: EIP1193Provider): number;
122
+ /**
123
+ * The wallet's first account, or null.
124
+ *
125
+ * Prefers what the SDK already knows, so an EVM context never returns a
126
+ * Solana address and no RPC is issued when the answer is already in hand.
127
+ */
128
+ addressOf(provider?: EIP1193Provider): Promise<Address | null>;
129
+ /** Every checksummed account a provider reports, or null. */
130
+ accountsOf(provider?: EIP1193Provider): Promise<Address[] | null>;
131
+ }
132
+ //# sourceMappingURL=EvmProviderRegistry.d.ts.map
@@ -0,0 +1,345 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ var __generator = (this && this.__generator) || function (thisArg, body) {
11
+ 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);
12
+ return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
13
+ function verb(n) { return function (v) { return step([n, v]); }; }
14
+ function step(op) {
15
+ if (f) throw new TypeError("Generator is already executing.");
16
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
17
+ 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;
18
+ if (y = 0, t) op = [op[0] & 2, t.value];
19
+ switch (op[0]) {
20
+ case 0: case 1: t = op; break;
21
+ case 4: _.label++; return { value: op[1], done: false };
22
+ case 5: _.label++; y = op[1]; op = [0]; continue;
23
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
24
+ default:
25
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
26
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
27
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
28
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
29
+ if (t[2]) _.ops.pop();
30
+ _.trys.pop(); continue;
31
+ }
32
+ op = body.call(thisArg, _);
33
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
34
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
35
+ }
36
+ };
37
+ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
38
+ if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
39
+ if (ar || !(i in from)) {
40
+ if (!ar) ar = Array.prototype.slice.call(from, 0, i);
41
+ ar[i] = from[i];
42
+ }
43
+ }
44
+ return to.concat(ar || Array.prototype.slice.call(from));
45
+ };
46
+ import { logger } from "../logger";
47
+ import { validateAndChecksumAddress } from "../utils/address";
48
+ import { detectInjectedProviderInfo } from "../provider";
49
+ import { WRAPPED_REQUEST_SYMBOL, WRAPPED_REQUEST_REF_SYMBOL, } from "../types";
50
+ /**
51
+ * Which EVM wallets exist, and what is known about each.
52
+ *
53
+ * Split out of `FormoAnalytics` so "the provider registry" is one thing with
54
+ * one owner. It deliberately holds no event logic: registering listeners and
55
+ * reacting to them stays with the SDK for now, and moves next.
56
+ *
57
+ * Three provider sets, which are not the same thing:
58
+ * - `details` is every EIP-6963 provider discovered, with its metadata.
59
+ * - `tracked` is the subset that has listeners wired up.
60
+ * - `seen` guards `details` against duplicates.
61
+ */
62
+ var EvmProviderRegistry = /** @class */ (function () {
63
+ function EvmProviderRegistry(deps) {
64
+ this.deps = deps;
65
+ this.details = [];
66
+ this.tracked = new Set();
67
+ this.seen = new Set();
68
+ /** Last chain heard from each provider, whoever is active. */
69
+ this.chainIds = new WeakMap();
70
+ /**
71
+ * Bumped on every chain observation, PER PROVIDER, so a stale answer that
72
+ * arrives late cannot overwrite a newer one.
73
+ */
74
+ this.chainGenerations = new WeakMap();
75
+ /** Listeners this SDK attached, so teardown can remove exactly those. */
76
+ this.listeners = new Map();
77
+ }
78
+ Object.defineProperty(EvmProviderRegistry.prototype, "all", {
79
+ // ── the provider set ─────────────────────────────────────────────────────
80
+ get: function () {
81
+ return this.details;
82
+ },
83
+ enumerable: false,
84
+ configurable: true
85
+ });
86
+ EvmProviderRegistry.prototype.isTracked = function (provider) {
87
+ return this.tracked.has(provider);
88
+ };
89
+ EvmProviderRegistry.prototype.markTracked = function (provider) {
90
+ this.tracked.add(provider);
91
+ };
92
+ EvmProviderRegistry.prototype.forgetTracked = function (provider) {
93
+ this.tracked.delete(provider);
94
+ };
95
+ EvmProviderRegistry.prototype.trackedProviders = function () {
96
+ return Array.from(this.tracked);
97
+ };
98
+ EvmProviderRegistry.prototype.isSeen = function (provider) {
99
+ return this.seen.has(provider);
100
+ };
101
+ Object.defineProperty(EvmProviderRegistry.prototype, "injected", {
102
+ get: function () {
103
+ return this.injectedDetail;
104
+ },
105
+ set: function (detail) {
106
+ this.injectedDetail = detail;
107
+ },
108
+ enumerable: false,
109
+ configurable: true
110
+ });
111
+ Object.defineProperty(EvmProviderRegistry.prototype, "counts", {
112
+ /** Counts for the public debug helpers. */
113
+ get: function () {
114
+ return {
115
+ totalProviders: this.details.length,
116
+ trackedProviders: this.tracked.size,
117
+ seenProviders: this.seen.size,
118
+ };
119
+ },
120
+ enumerable: false,
121
+ configurable: true
122
+ });
123
+ /** Add a discovered provider once. Returns false if already present. */
124
+ EvmProviderRegistry.prototype.add = function (detail) {
125
+ var provider = detail === null || detail === void 0 ? void 0 : detail.provider;
126
+ if (!provider)
127
+ return false;
128
+ if (this.details.some(function (existing) { return existing.provider === provider; })) {
129
+ return false;
130
+ }
131
+ this.details = __spreadArray(__spreadArray([], this.details, true), [detail], false);
132
+ this.seen.add(provider);
133
+ return true;
134
+ };
135
+ /**
136
+ * A provider's display name and rdns.
137
+ *
138
+ * EIP-6963 metadata is authoritative when we have it; otherwise fall back to
139
+ * sniffing the injected provider, which is all a pre-6963 wallet offers.
140
+ */
141
+ EvmProviderRegistry.prototype.infoFor = function (provider) {
142
+ var announced = this.details.find(function (p) { return p.provider === provider; });
143
+ if (announced) {
144
+ return { name: announced.info.name, rdns: announced.info.rdns };
145
+ }
146
+ var injected = detectInjectedProviderInfo(provider);
147
+ return { name: injected.name, rdns: injected.rdns };
148
+ };
149
+ // ── listener bookkeeping ─────────────────────────────────────────────────
150
+ EvmProviderRegistry.prototype.addListener = function (provider, event, listener) {
151
+ var map = this.listeners.get(provider) || {};
152
+ map[event] = listener;
153
+ this.listeners.set(provider, map);
154
+ };
155
+ /** Remove only the listeners this SDK attached, never the host app's. */
156
+ EvmProviderRegistry.prototype.removeListeners = function (provider) {
157
+ var attached = this.listeners.get(provider);
158
+ if (!attached)
159
+ return;
160
+ // Keep whatever could not be removed. Forgetting a listener that is still
161
+ // attached loses the only reference to it, so nothing can ever try again:
162
+ // the callback stays live for the life of the page and holds the instance
163
+ // it closes over. A provider that throws transiently during teardown gets
164
+ // another chance on the next attempt.
165
+ var stillAttached = {};
166
+ for (var _i = 0, _a = Object.entries(attached); _i < _a.length; _i++) {
167
+ var _b = _a[_i], event_1 = _b[0], fn = _b[1];
168
+ try {
169
+ provider.removeListener(event_1, fn);
170
+ }
171
+ catch (e) {
172
+ logger.warn("Failed to remove listener for ".concat(String(event_1)), e);
173
+ stillAttached[event_1] = fn;
174
+ }
175
+ }
176
+ if (Object.keys(stillAttached).length > 0) {
177
+ this.listeners.set(provider, stillAttached);
178
+ }
179
+ else {
180
+ this.listeners.delete(provider);
181
+ }
182
+ };
183
+ /** Events still attached to a provider after a failed teardown. */
184
+ EvmProviderRegistry.prototype.attachedEvents = function (provider) {
185
+ var _a;
186
+ return Object.keys((_a = this.listeners.get(provider)) !== null && _a !== void 0 ? _a : {});
187
+ };
188
+ /**
189
+ * Whether this provider's `request` is already our wrapper.
190
+ *
191
+ * Checks the wrapper's own marker AND that the provider still points at
192
+ * that exact function, so a wallet that replaced `request` after we wrapped
193
+ * it is re-wrapped rather than left uninstrumented.
194
+ */
195
+ EvmProviderRegistry.prototype.isWrapped = function (provider, currentRequest) {
196
+ return !!(currentRequest &&
197
+ typeof currentRequest === "function" &&
198
+ currentRequest[WRAPPED_REQUEST_SYMBOL] &&
199
+ provider[WRAPPED_REQUEST_REF_SYMBOL] ===
200
+ currentRequest);
201
+ };
202
+ // ── per-provider chain knowledge ─────────────────────────────────────────
203
+ EvmProviderRegistry.prototype.chainIdOf = function (provider) {
204
+ return this.chainIds.get(provider);
205
+ };
206
+ /** Advance and return this provider's chain-observation generation. */
207
+ EvmProviderRegistry.prototype.bumpChainGeneration = function (provider) {
208
+ var _a;
209
+ var next = ((_a = this.chainGenerations.get(provider)) !== null && _a !== void 0 ? _a : 0) + 1;
210
+ this.chainGenerations.set(provider, next);
211
+ return next;
212
+ };
213
+ EvmProviderRegistry.prototype.chainGeneration = function (provider) {
214
+ var _a;
215
+ return (_a = this.chainGenerations.get(provider)) !== null && _a !== void 0 ? _a : 0;
216
+ };
217
+ /**
218
+ * Record a provider's chain. Fed by `chainChanged` and `connect`, and by
219
+ * the one-off probe at tracking time, never from inside a user request.
220
+ */
221
+ EvmProviderRegistry.prototype.rememberChain = function (provider, chainId) {
222
+ if (!provider || !chainId)
223
+ return;
224
+ // Any observation is newer than an `eth_chainId` still in flight for this
225
+ // provider.
226
+ this.bumpChainGeneration(provider);
227
+ this.chainIds.set(provider, chainId);
228
+ this.deps.onChainObserved(provider, chainId);
229
+ };
230
+ /**
231
+ * Resolve the chain an autocaptured request actually ran on.
232
+ *
233
+ * Central chain state is maintained by `chainChanged` from whichever
234
+ * provider is currently active. When a request arrives from a DIFFERENT
235
+ * tracked provider, which happens whenever a visitor has two wallets
236
+ * installed, that cached value describes the wrong wallet, and tagging the
237
+ * event with it silently mis-attributes the chain.
238
+ *
239
+ * Answered entirely from a per-provider snapshot. Deliberately SYNCHRONOUS
240
+ * and never issues an RPC.
241
+ *
242
+ * An earlier version called `eth_chainId` on the signing provider and
243
+ * time-boxed it with `Promise.race`. That is not safe: the race abandons
244
+ * the SDK's promise but cannot cancel the provider's request. On a
245
+ * transport that serialises (WalletConnect's relay socket, the very case
246
+ * this path exists to serve) an abandoned lookup stays at the head of the
247
+ * wallet's queue, and every later RPC the dapp makes queues behind it until
248
+ * reload. Mislabelling a chain is a reporting defect; wedging the user's
249
+ * wallet is not an acceptable way to avoid one.
250
+ *
251
+ * When nothing is known this reports 0 ("unknown") rather than guessing
252
+ * with the active provider's chain, which is known-wrong for another wallet.
253
+ */
254
+ EvmProviderRegistry.prototype.resolveChainId = function (provider) {
255
+ if (provider) {
256
+ var known = this.chainIds.get(provider);
257
+ if (known)
258
+ return known;
259
+ // Only the active provider's chain is described by central state.
260
+ var activeChain = this.deps.activeChainId();
261
+ if (provider === this.deps.activeProvider() && activeChain) {
262
+ return activeChain;
263
+ }
264
+ // A tracked provider we have never heard a chain from. Deliberately no
265
+ // fall back to central state: it belongs to a different wallet.
266
+ return 0;
267
+ }
268
+ return this.deps.activeChainId() || 0;
269
+ };
270
+ // ── reading accounts off a provider ──────────────────────────────────────
271
+ /**
272
+ * The wallet's first account, or null.
273
+ *
274
+ * Prefers what the SDK already knows, so an EVM context never returns a
275
+ * Solana address and no RPC is issued when the answer is already in hand.
276
+ */
277
+ EvmProviderRegistry.prototype.addressOf = function (provider) {
278
+ return __awaiter(this, void 0, void 0, function () {
279
+ var active, p, known, accounts;
280
+ return __generator(this, function (_a) {
281
+ switch (_a.label) {
282
+ case 0:
283
+ active = this.deps.activeProvider();
284
+ p = provider || active;
285
+ // The cached wallet describes the ACTIVE provider, so it may only answer
286
+ // for that one. Returning it for any provider reported the active wallet's
287
+ // address under every other wallet's name and rdns, which is exactly what
288
+ // `identify()` does when it scans the providers it has discovered.
289
+ if (!provider || provider === active) {
290
+ known = this.deps.knownEvmAddress();
291
+ if (known)
292
+ return [2 /*return*/, known];
293
+ }
294
+ if (!p) {
295
+ logger.info("The provider is not set");
296
+ return [2 /*return*/, null];
297
+ }
298
+ return [4 /*yield*/, this.accountsOf(p)];
299
+ case 1:
300
+ accounts = _a.sent();
301
+ if (accounts && accounts.length > 0) {
302
+ return [2 /*return*/, validateAndChecksumAddress(accounts[0]) || null];
303
+ }
304
+ return [2 /*return*/, null];
305
+ }
306
+ });
307
+ });
308
+ };
309
+ /** Every checksummed account a provider reports, or null. */
310
+ EvmProviderRegistry.prototype.accountsOf = function (provider) {
311
+ return __awaiter(this, void 0, void 0, function () {
312
+ var p, res, err_1, code;
313
+ return __generator(this, function (_a) {
314
+ switch (_a.label) {
315
+ case 0:
316
+ p = provider || this.deps.activeProvider();
317
+ _a.label = 1;
318
+ case 1:
319
+ _a.trys.push([1, 3, , 4]);
320
+ return [4 /*yield*/, (p === null || p === void 0 ? void 0 : p.request({
321
+ method: "eth_accounts",
322
+ }))];
323
+ case 2:
324
+ res = _a.sent();
325
+ if (!res || res.length === 0)
326
+ return [2 /*return*/, null];
327
+ return [2 /*return*/, res
328
+ .map(function (e) { return validateAndChecksumAddress(e); })
329
+ .filter(function (e) { return e !== undefined; })];
330
+ case 3:
331
+ err_1 = _a.sent();
332
+ code = err_1 === null || err_1 === void 0 ? void 0 : err_1.code;
333
+ if (code !== 4001) {
334
+ logger.error("EvmProviderRegistry::accountsOf: eth_accounts threw an error", err_1);
335
+ }
336
+ return [2 /*return*/, null];
337
+ case 4: return [2 /*return*/];
338
+ }
339
+ });
340
+ });
341
+ };
342
+ return EvmProviderRegistry;
343
+ }());
344
+ export { EvmProviderRegistry };
345
+ //# sourceMappingURL=EvmProviderRegistry.js.map
@@ -0,0 +1,120 @@
1
+ import { Address, ChainID, EIP1193Provider, IFormoEventProperties, SignatureStatus, TransactionStatus } from "../types";
2
+ import { WalletStateStore } from "../wallet/WalletStateStore";
3
+ import { EvmProviderRegistry } from "./EvmProviderRegistry";
4
+ import { AutocaptureEventType } from "../tracking/TrackingPolicy";
5
+ /** What the request tracker needs from the SDK that owns it. */
6
+ export interface EvmRequestTrackerDeps {
7
+ isAutocaptureEnabled(eventType: AutocaptureEventType): boolean;
8
+ signature(params: {
9
+ status: SignatureStatus;
10
+ chainId?: ChainID;
11
+ address: Address;
12
+ message: string;
13
+ }, properties?: IFormoEventProperties): Promise<void>;
14
+ transaction(params: {
15
+ status: TransactionStatus;
16
+ chainId: ChainID;
17
+ address: Address;
18
+ data?: string;
19
+ to?: string;
20
+ value?: string;
21
+ transactionHash?: string;
22
+ function_name?: string;
23
+ function_args?: Record<string, unknown>;
24
+ }, properties?: IFormoEventProperties): Promise<void>;
25
+ }
26
+ /**
27
+ * Autocapture for signatures and transactions, by wrapping a provider's
28
+ * `request`.
29
+ *
30
+ * The wrapper is deliberately thin: it observes the call the dapp was already
31
+ * making and never issues one of its own. That rule is why the chain a
32
+ * request ran on is read from `eth_chainId` calls the app makes, rather than
33
+ * probed - an SDK-issued lookup on a serialising transport can wedge the
34
+ * user's wallet, which is never an acceptable price for a label.
35
+ */
36
+ export declare class EvmRequestTracker {
37
+ private readonly wallet;
38
+ private readonly registry;
39
+ private readonly deps;
40
+ /**
41
+ * Timers for receipt and batch-status polling.
42
+ *
43
+ * A poll re-arms for up to thirty seconds, so a torn-down instance would
44
+ * otherwise keep asking a wallet about transactions nobody is listening
45
+ * for, and hold the process open for the whole window. That is the same
46
+ * shape as the batch timer that hung the test suite in #338.
47
+ */
48
+ private polls;
49
+ private disposed;
50
+ constructor(wallet: WalletStateStore, registry: EvmProviderRegistry, deps: EvmRequestTrackerDeps);
51
+ /** Stop every poll in flight. Terminal, like the event queue's close(). */
52
+ cleanup(): void;
53
+ /** Re-arm a poll, unless this tracker has been torn down. */
54
+ private schedulePoll;
55
+ /**
56
+ * Wrap a provider's `request`. Returns whether the wrapper is installed:
57
+ * a caller that marks the provider as tracked on a false return would
58
+ * never retry it, and every signature and transaction from that wallet
59
+ * would be missed for the rest of the session.
60
+ */
61
+ registerRequestListeners(provider: EIP1193Provider): boolean;
62
+ private buildSignatureEventPayload;
63
+ private buildTransactionEventPayload;
64
+ /**
65
+ * Polls for transaction receipt and emits tx.status = CONFIRMED or REVERTED.
66
+ */
67
+ private pollTransactionReceipt;
68
+ /**
69
+ * One `transaction` event per call in an EIP-5792 batch.
70
+ *
71
+ * A batch is not a transaction. It maps to several on-chain transactions,
72
+ * so reporting it as one event would understate volume and make revenue and
73
+ * per-contract attribution wrong for every app that adopts smart accounts.
74
+ * Each call is reported on its own, carrying the batch id so the calls can
75
+ * be reassembled downstream.
76
+ *
77
+ * Status is per BATCH, because that is what `wallet_getCallsStatus` reports.
78
+ * When it resolves, every call in the batch moves together, except where
79
+ * per-call receipts say otherwise on a non-atomic batch.
80
+ */
81
+ private trackBatchedCalls;
82
+ /**
83
+ * How one call in a settled batch ended.
84
+ *
85
+ * A per-call receipt is authoritative where it exists: that is what makes a
86
+ * partially reverted non-atomic batch report honestly rather than tarring
87
+ * every call with the batch's worst outcome. A receipt whose own status is
88
+ * unreadable falls back to the batch verdict rather than being assumed good.
89
+ *
90
+ * The codes are EIP-5792's: 200 confirmed, 400 failed BEFORE landing on
91
+ * chain, 500 reverted, 600 partially reverted. 400 is a rejection, not a
92
+ * revert - nothing was mined, so calling it reverted would misreport gas
93
+ * spent and on-chain activity that never happened.
94
+ *
95
+ * Returns undefined when the call cannot be decided, which happens on 600
96
+ * for a call the wallet gave no receipt for.
97
+ */
98
+ private batchCallOutcome;
99
+ /**
100
+ * The batch identifier from a `wallet_sendCalls` result.
101
+ *
102
+ * EIP-5792 settled on `{ id }`, but wallets shipped against the earlier
103
+ * draft return a bare string. Both are accepted so a wallet on either
104
+ * version is still grouped.
105
+ */
106
+ private readBatchId;
107
+ /**
108
+ * Resolve a batch through `wallet_getCallsStatus`.
109
+ *
110
+ * The status codes are EIP-5792's: 100 pending, 200 confirmed, 400 failed
111
+ * before landing, 500 reverted, 600 partially reverted. Anything below 200
112
+ * means keep waiting.
113
+ *
114
+ * A per-call receipt wins over the batch verdict where one exists, which is
115
+ * what makes a partially reverted non-atomic batch report honestly instead
116
+ * of marking every call with the batch's worst outcome.
117
+ */
118
+ private pollBatchStatus;
119
+ }
120
+ //# sourceMappingURL=EvmRequestTracker.d.ts.map