@formo/analytics 1.36.0 → 1.37.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.
@@ -68,42 +68,20 @@ export declare class EvmRequestTracker {
68
68
  /**
69
69
  * One `transaction` event per call in an EIP-5792 batch.
70
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.
71
+ * The CALL is the unit of attribution: each has its own target, calldata,
72
+ * and value, and folding a batch into one event would misattribute revenue
73
+ * and per-contract activity for every app that adopts smart accounts. How
74
+ * many on-chain transactions a batch becomes depends on execution - an
75
+ * atomic batch lands as ONE transaction, a non-atomic fallback as several -
76
+ * so on-chain volume is `count(distinct transaction_hash)`, wallet actions
77
+ * `count(distinct batch_id)`, never the event count. Each call is reported
78
+ * on its own, carrying the batch id so the calls reassemble downstream.
76
79
  *
77
80
  * Status is per BATCH, because that is what `wallet_getCallsStatus` reports.
78
81
  * When it resolves, every call in the batch moves together, except where
79
82
  * per-call receipts say otherwise on a non-atomic batch.
80
83
  */
81
84
  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
85
  /**
108
86
  * Resolve a batch through `wallet_getCallsStatus`.
109
87
  *
@@ -63,6 +63,7 @@ var logger_1 = require("../logger");
63
63
  var chain_1 = require("../utils/chain");
64
64
  var address_1 = require("../utils/address");
65
65
  var types_1 = require("../types");
66
+ var batch_1 = require("./batch");
66
67
  /**
67
68
  * Decode a hex-encoded `personal_sign` message.
68
69
  *
@@ -514,11 +515,14 @@ var EvmRequestTracker = /** @class */ (function () {
514
515
  /**
515
516
  * One `transaction` event per call in an EIP-5792 batch.
516
517
  *
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.
518
+ * The CALL is the unit of attribution: each has its own target, calldata,
519
+ * and value, and folding a batch into one event would misattribute revenue
520
+ * and per-contract activity for every app that adopts smart accounts. How
521
+ * many on-chain transactions a batch becomes depends on execution - an
522
+ * atomic batch lands as ONE transaction, a non-atomic fallback as several -
523
+ * so on-chain volume is `count(distinct transaction_hash)`, wallet actions
524
+ * `count(distinct batch_id)`, never the event count. Each call is reported
525
+ * on its own, carrying the batch id so the calls reassemble downstream.
522
526
  *
523
527
  * Status is per BATCH, because that is what `wallet_getCallsStatus` reports.
524
528
  * When it resolves, every call in the batch moves together, except where
@@ -589,7 +593,7 @@ var EvmRequestTracker = /** @class */ (function () {
589
593
  return [4 /*yield*/, sendPromise];
590
594
  case 2:
591
595
  result = _d.sent();
592
- batchId = this.readBatchId(result);
596
+ batchId = (0, batch_1.readBatchId)(result);
593
597
  for (_a = 0, payloads_2 = payloads; _a < payloads_2.length; _a++) {
594
598
  p = payloads_2[_a];
595
599
  properties = p.properties, rest = __rest(p, ["properties"]);
@@ -624,54 +628,6 @@ var EvmRequestTracker = /** @class */ (function () {
624
628
  });
625
629
  });
626
630
  };
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
631
  /**
676
632
  * Resolve a batch through `wallet_getCallsStatus`.
677
633
  *
@@ -694,7 +650,7 @@ var EvmRequestTracker = /** @class */ (function () {
694
650
  return [2 /*return*/];
695
651
  attempts = 0;
696
652
  poll = function () { return __awaiter(_this, void 0, void 0, function () {
697
- var res, code_1, receipts_1, e_8;
653
+ var res_1, code_1, e_8;
698
654
  var _this = this;
699
655
  return __generator(this, function (_a) {
700
656
  switch (_a.label) {
@@ -709,13 +665,14 @@ var EvmRequestTracker = /** @class */ (function () {
709
665
  params: [batchId],
710
666
  })];
711
667
  case 2:
712
- res = (_a.sent());
713
- code_1 = typeof (res === null || res === void 0 ? void 0 : res.status) === "number" ? res.status : undefined;
668
+ res_1 = (_a.sent());
669
+ code_1 = (0, batch_1.readBatchStatusCode)(res_1);
714
670
  if (code_1 !== undefined && code_1 >= 200) {
715
- receipts_1 = Array.isArray(res === null || res === void 0 ? void 0 : res.receipts) ? res.receipts : [];
716
671
  payloads.forEach(function (p, index) {
717
- var receipt = receipts_1[index];
718
- var outcome = _this.batchCallOutcome(code_1, receipt);
672
+ // Atomic-aware: one receipt covering the whole batch reaches
673
+ // every call, hash included, not just call 0.
674
+ var receipt = (0, batch_1.batchReceiptForCall)(res_1, index, payloads.length);
675
+ var outcome = (0, batch_1.batchCallOutcome)(code_1, receipt);
719
676
  // 600 means SOME calls reverted, so a call with no receipt of its
720
677
  // own has not been decided. Reporting it either way would invent
721
678
  // a result; leaving it unsettled is the honest answer.
@@ -0,0 +1,94 @@
1
+ import { TransactionStatus } from "../types/events";
2
+ /**
3
+ * EIP-5792 batch settlement, shared by both capture paths.
4
+ *
5
+ * The EIP-1193 request wrapper (`EvmRequestTracker`) and the wagmi cache
6
+ * observer (`WagmiEventHandler`) each see a batch through a different
7
+ * transport, but a settled batch means the same thing in both. Keeping the
8
+ * outcome rules in one place is what stops the two paths from drifting into
9
+ * reporting the same batch differently depending on how the app happened to
10
+ * integrate the SDK.
11
+ */
12
+ /** A settled batch as `wallet_getCallsStatus` (or viem's wrapper) reports it. */
13
+ export type BatchStatusResult = {
14
+ status?: number | string;
15
+ statusCode?: number;
16
+ atomic?: boolean;
17
+ chainId?: number | string;
18
+ receipts?: BatchReceipt[];
19
+ } | null | undefined;
20
+ export type BatchReceipt = {
21
+ status?: string | number;
22
+ transactionHash?: string;
23
+ };
24
+ /**
25
+ * The batch identifier from a `wallet_sendCalls` result.
26
+ *
27
+ * EIP-5792 settled on `{ id }`, but wallets shipped against the earlier
28
+ * draft return a bare string. Both are accepted so a wallet on either
29
+ * version is still grouped.
30
+ */
31
+ export declare function readBatchId(result: unknown): string | undefined;
32
+ /**
33
+ * The numeric EIP-5792 status code from a settlement result.
34
+ *
35
+ * A wallet answers `wallet_getCallsStatus` with a numeric `status`; viem's
36
+ * `getCallsStatus` renames that to `statusCode` and puts a summary string in
37
+ * `status` instead. Both shapes arrive here depending on the capture path,
38
+ * so read the number wherever it is and never trust the string.
39
+ */
40
+ export declare function readBatchStatusCode(res: BatchStatusResult): number | undefined;
41
+ /**
42
+ * The chain a settled batch reports itself on.
43
+ *
44
+ * EIP-5792 v2 puts `chainId` in the `wallet_getCallsStatus` response as hex;
45
+ * viem returns it as a number. Either way it names the chain the batch
46
+ * actually settled on, which outranks a chain merely inferred from the
47
+ * connection at broadcast time - the wallet can move chains while the
48
+ * prompt is up.
49
+ */
50
+ export declare function readBatchChainId(res: BatchStatusResult): number | undefined;
51
+ /**
52
+ * How one call in a settled batch ended.
53
+ *
54
+ * A per-call receipt is authoritative where it exists: that is what makes a
55
+ * partially reverted non-atomic batch report honestly rather than tarring
56
+ * every call with the batch's worst outcome. A receipt whose own status is
57
+ * unreadable falls back to the batch verdict rather than being assumed good.
58
+ *
59
+ * Receipt statuses come in two spellings: raw RPC (`"0x0"`/`"0x1"`, or the
60
+ * numbers) and viem-formatted (`"reverted"`/`"success"`), because the wagmi
61
+ * path sees receipts after viem has normalised them.
62
+ *
63
+ * The codes are EIP-5792's: 200 confirmed, 400 failed BEFORE landing on
64
+ * chain, 500 reverted, 600 partially reverted. 400 is a rejection, not a
65
+ * revert - nothing was mined, so calling it reverted would misreport gas
66
+ * spent and on-chain activity that never happened.
67
+ *
68
+ * Returns undefined when the call cannot be decided, which happens on 600
69
+ * for a call the wallet gave no receipt for.
70
+ */
71
+ export declare function batchCallOutcome(code: number, receipt?: BatchReceipt): TransactionStatus | undefined;
72
+ /**
73
+ * The receipt that decides call `index`, honouring atomic execution.
74
+ *
75
+ * An atomic batch lands as ONE on-chain transaction, so the wallet returns a
76
+ * single receipt covering every call. Indexing receipts positionally there
77
+ * would hand the shared hash to call 0 and leave its siblings hashless and
78
+ * decided only by the batch verdict. Every call in an atomic batch shares
79
+ * the one receipt - same hash, same fate - which is also what makes
80
+ * `count(distinct transaction_hash)` count on-chain transactions correctly.
81
+ *
82
+ * The wallet's own `atomic` flag is authoritative in BOTH directions. An
83
+ * explicit `atomic: false` with a single receipt is a real shape - a
84
+ * non-atomic batch whose execution stopped after one call mined - and
85
+ * sharing that receipt would hand calls that never reached the chain a
86
+ * transaction hash they do not have. Only when the field is ABSENT (a
87
+ * wallet predating it, reached over raw EIP-1193; viem fills the field in,
88
+ * so the wagmi path never lands here) does the conservative inference
89
+ * apply: one receipt for several calls on a batch that is NOT partially
90
+ * reverted can only be atomic execution (600 explicitly means some calls
91
+ * reverted and others did not, which one shared transaction cannot do).
92
+ */
93
+ export declare function batchReceiptForCall(res: BatchStatusResult, index: number, callCount: number): BatchReceipt | undefined;
94
+ //# sourceMappingURL=batch.d.ts.map
@@ -0,0 +1,130 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.readBatchId = readBatchId;
4
+ exports.readBatchStatusCode = readBatchStatusCode;
5
+ exports.readBatchChainId = readBatchChainId;
6
+ exports.batchCallOutcome = batchCallOutcome;
7
+ exports.batchReceiptForCall = batchReceiptForCall;
8
+ var events_1 = require("../types/events");
9
+ /**
10
+ * The batch identifier from a `wallet_sendCalls` result.
11
+ *
12
+ * EIP-5792 settled on `{ id }`, but wallets shipped against the earlier
13
+ * draft return a bare string. Both are accepted so a wallet on either
14
+ * version is still grouped.
15
+ */
16
+ function readBatchId(result) {
17
+ if (typeof result === "string" && result.length > 0)
18
+ return result;
19
+ if (result && typeof result === "object") {
20
+ var id = result.id;
21
+ if (typeof id === "string" && id.length > 0)
22
+ return id;
23
+ }
24
+ return undefined;
25
+ }
26
+ /**
27
+ * The numeric EIP-5792 status code from a settlement result.
28
+ *
29
+ * A wallet answers `wallet_getCallsStatus` with a numeric `status`; viem's
30
+ * `getCallsStatus` renames that to `statusCode` and puts a summary string in
31
+ * `status` instead. Both shapes arrive here depending on the capture path,
32
+ * so read the number wherever it is and never trust the string.
33
+ */
34
+ function readBatchStatusCode(res) {
35
+ if (typeof (res === null || res === void 0 ? void 0 : res.statusCode) === "number")
36
+ return res.statusCode;
37
+ if (typeof (res === null || res === void 0 ? void 0 : res.status) === "number")
38
+ return res.status;
39
+ return undefined;
40
+ }
41
+ /**
42
+ * The chain a settled batch reports itself on.
43
+ *
44
+ * EIP-5792 v2 puts `chainId` in the `wallet_getCallsStatus` response as hex;
45
+ * viem returns it as a number. Either way it names the chain the batch
46
+ * actually settled on, which outranks a chain merely inferred from the
47
+ * connection at broadcast time - the wallet can move chains while the
48
+ * prompt is up.
49
+ */
50
+ function readBatchChainId(res) {
51
+ var raw = res === null || res === void 0 ? void 0 : res.chainId;
52
+ if (typeof raw === "number" && Number.isFinite(raw) && raw > 0)
53
+ return raw;
54
+ if (typeof raw === "string") {
55
+ var parsed = parseInt(raw, 16);
56
+ if (Number.isFinite(parsed) && parsed > 0)
57
+ return parsed;
58
+ }
59
+ return undefined;
60
+ }
61
+ /**
62
+ * How one call in a settled batch ended.
63
+ *
64
+ * A per-call receipt is authoritative where it exists: that is what makes a
65
+ * partially reverted non-atomic batch report honestly rather than tarring
66
+ * every call with the batch's worst outcome. A receipt whose own status is
67
+ * unreadable falls back to the batch verdict rather than being assumed good.
68
+ *
69
+ * Receipt statuses come in two spellings: raw RPC (`"0x0"`/`"0x1"`, or the
70
+ * numbers) and viem-formatted (`"reverted"`/`"success"`), because the wagmi
71
+ * path sees receipts after viem has normalised them.
72
+ *
73
+ * The codes are EIP-5792's: 200 confirmed, 400 failed BEFORE landing on
74
+ * chain, 500 reverted, 600 partially reverted. 400 is a rejection, not a
75
+ * revert - nothing was mined, so calling it reverted would misreport gas
76
+ * spent and on-chain activity that never happened.
77
+ *
78
+ * Returns undefined when the call cannot be decided, which happens on 600
79
+ * for a call the wallet gave no receipt for.
80
+ */
81
+ function batchCallOutcome(code, receipt) {
82
+ var receiptStatus = receipt === null || receipt === void 0 ? void 0 : receipt.status;
83
+ if (receiptStatus !== undefined) {
84
+ return receiptStatus === "0x0" ||
85
+ receiptStatus === 0 ||
86
+ receiptStatus === "reverted"
87
+ ? events_1.TransactionStatus.REVERTED
88
+ : events_1.TransactionStatus.CONFIRMED;
89
+ }
90
+ if (code >= 600)
91
+ return undefined;
92
+ if (code >= 500)
93
+ return events_1.TransactionStatus.REVERTED;
94
+ if (code >= 400)
95
+ return events_1.TransactionStatus.REJECTED;
96
+ return events_1.TransactionStatus.CONFIRMED;
97
+ }
98
+ /**
99
+ * The receipt that decides call `index`, honouring atomic execution.
100
+ *
101
+ * An atomic batch lands as ONE on-chain transaction, so the wallet returns a
102
+ * single receipt covering every call. Indexing receipts positionally there
103
+ * would hand the shared hash to call 0 and leave its siblings hashless and
104
+ * decided only by the batch verdict. Every call in an atomic batch shares
105
+ * the one receipt - same hash, same fate - which is also what makes
106
+ * `count(distinct transaction_hash)` count on-chain transactions correctly.
107
+ *
108
+ * The wallet's own `atomic` flag is authoritative in BOTH directions. An
109
+ * explicit `atomic: false` with a single receipt is a real shape - a
110
+ * non-atomic batch whose execution stopped after one call mined - and
111
+ * sharing that receipt would hand calls that never reached the chain a
112
+ * transaction hash they do not have. Only when the field is ABSENT (a
113
+ * wallet predating it, reached over raw EIP-1193; viem fills the field in,
114
+ * so the wagmi path never lands here) does the conservative inference
115
+ * apply: one receipt for several calls on a batch that is NOT partially
116
+ * reverted can only be atomic execution (600 explicitly means some calls
117
+ * reverted and others did not, which one shared transaction cannot do).
118
+ */
119
+ function batchReceiptForCall(res, index, callCount) {
120
+ var _a;
121
+ var receipts = Array.isArray(res === null || res === void 0 ? void 0 : res.receipts) ? res.receipts : [];
122
+ var code = (_a = readBatchStatusCode(res)) !== null && _a !== void 0 ? _a : 0;
123
+ var atomic = (res === null || res === void 0 ? void 0 : res.atomic) === true ||
124
+ ((res === null || res === void 0 ? void 0 : res.atomic) === undefined &&
125
+ receipts.length === 1 &&
126
+ callCount > 1 &&
127
+ code < 600);
128
+ return atomic ? receipts[0] : receipts[index];
129
+ }
130
+ //# sourceMappingURL=batch.js.map
@@ -1,2 +1,2 @@
1
- export declare const version = "1.36.0";
1
+ export declare const version = "1.37.0";
2
2
  //# sourceMappingURL=version.d.ts.map
@@ -3,5 +3,5 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.version = void 0;
4
4
  // This file is auto-generated by scripts/update-version.js during npm version
5
5
  // Do not edit manually - it will be overwritten
6
- exports.version = '1.36.0';
6
+ exports.version = '1.37.0';
7
7
  //# sourceMappingURL=version.js.map
@@ -96,6 +96,8 @@ export declare class WagmiEventHandler {
96
96
  * broadcast to match the receipt against.
97
97
  */
98
98
  private get pendingTransactions();
99
+ /** Broadcast batches awaiting `callsStatus`, shared like the map above. */
100
+ private get pendingBatches();
99
101
  constructor(formoAnalytics: FormoAnalytics, wagmiConfig: WagmiConfig, queryClient?: QueryClient);
100
102
  /**
101
103
  * Set up listeners for wallet connection, disconnection, and chain changes
@@ -228,6 +230,30 @@ export declare class WagmiEventHandler {
228
230
  * Handle query cache events (transaction confirmations)
229
231
  */
230
232
  private handleQueryEvent;
233
+ /**
234
+ * Settle a just-registered batch from a status query that already ran.
235
+ *
236
+ * Best-effort by design: the minimal QueryClient interface the SDK
237
+ * accepts is not guaranteed to expose cache lookup, and a missing
238
+ * `getAll` just means settlement waits for the next query event, which
239
+ * is where it normally comes from anyway.
240
+ */
241
+ private settleFromCachedCallsStatus;
242
+ /**
243
+ * Settle an EIP-5792 batch from a `callsStatus` query.
244
+ *
245
+ * Only batches whose broadcast this SDK observed are settled: the batch id
246
+ * must be in `pendingBatches`, for the same reason receipt queries are
247
+ * gated on an observed hash - queries are visible to any code sharing the
248
+ * QueryClient, and emitting for an id we never saw broadcast would let a
249
+ * forged query invent transactions.
250
+ *
251
+ * Outcome semantics are shared with the EIP-1193 path (`src/evm/batch.ts`):
252
+ * per-call receipts outrank the batch verdict, an atomic batch's single
253
+ * receipt reaches every call, and a 600 leaves receipt-less calls
254
+ * unsettled rather than guessed.
255
+ */
256
+ private handleCallsStatusQuery;
231
257
  /**
232
258
  * Handle waitForTransactionReceipt query completion
233
259
  * Emits CONFIRMED or REVERTED transaction status
@@ -245,6 +271,23 @@ export declare class WagmiEventHandler {
245
271
  * Handle transaction mutations (sendTransaction, writeContract)
246
272
  */
247
273
  private handleTransactionMutation;
274
+ /**
275
+ * One `transaction` event per call in an EIP-5792 batch, wagmi path.
276
+ *
277
+ * Mirrors `EvmRequestTracker.trackBatchedCalls` exactly: the CALL is the
278
+ * unit of attribution, so each call gets its own STARTED at pending and
279
+ * BROADCASTED (with `batch_id`) when the wallet returns an id. The batch's
280
+ * on-chain outcome arrives through the `callsStatus` query, handled in
281
+ * `handleCallsStatusQuery`.
282
+ *
283
+ * Rejection matches the 1193 path's rule: only a user rejection (4001
284
+ * anywhere in the error chain) marks the calls rejected - one dismissal
285
+ * dismisses the whole prompt, so every call in it is rejected, and
286
+ * reporting only the first would undercount. Any other error (a wallet
287
+ * without EIP-5792 support, a transport failure) emits nothing further:
288
+ * inventing a rejection the user never made would be worse.
289
+ */
290
+ private handleSendCallsMutation;
248
291
  /**
249
292
  * Get the current Wagmi state
250
293
  * Supports both getState() method and direct state property access