@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.
- package/dist/cjs/src/evm/EvmRequestTracker.d.ts +8 -30
- package/dist/cjs/src/evm/EvmRequestTracker.js +17 -60
- package/dist/cjs/src/evm/batch.d.ts +94 -0
- package/dist/cjs/src/evm/batch.js +130 -0
- package/dist/cjs/src/version.d.ts +1 -1
- package/dist/cjs/src/version.js +1 -1
- package/dist/cjs/src/wagmi/WagmiEventHandler.d.ts +43 -0
- package/dist/cjs/src/wagmi/WagmiEventHandler.js +253 -2
- package/dist/esm/src/evm/EvmRequestTracker.d.ts +8 -30
- package/dist/esm/src/evm/EvmRequestTracker.js +17 -60
- package/dist/esm/src/evm/batch.d.ts +94 -0
- package/dist/esm/src/evm/batch.js +123 -0
- package/dist/esm/src/version.d.ts +1 -1
- package/dist/esm/src/version.js +1 -1
- package/dist/esm/src/wagmi/WagmiEventHandler.d.ts +43 -0
- package/dist/esm/src/wagmi/WagmiEventHandler.js +253 -2
- package/dist/index.umd.min.js +1 -1
- package/package.json +4 -3
|
@@ -58,6 +58,7 @@ exports.WagmiEventHandler = exports.MARKER_GRACE_MS = void 0;
|
|
|
58
58
|
exports.__resetSeededWallet = __resetSeededWallet;
|
|
59
59
|
var events_1 = require("../types/events");
|
|
60
60
|
var logger_1 = require("../logger");
|
|
61
|
+
var batch_1 = require("../evm/batch");
|
|
61
62
|
var utils_1 = require("./utils");
|
|
62
63
|
/**
|
|
63
64
|
* Built-in transaction fields that could collide with function args.
|
|
@@ -200,6 +201,25 @@ var ownerKey = function (writeKey, config) {
|
|
|
200
201
|
}
|
|
201
202
|
return "".concat(writeKey, ":").concat(id);
|
|
202
203
|
};
|
|
204
|
+
/**
|
|
205
|
+
* Was this mutation error the user dismissing the wallet prompt?
|
|
206
|
+
*
|
|
207
|
+
* Matches the EIP-1193 path's rule (code 4001), but a wagmi mutation error
|
|
208
|
+
* arrives wrapped: viem nests the RPC error under `cause`, sometimes twice.
|
|
209
|
+
* Walk the chain rather than trusting the top level, and accept viem's
|
|
210
|
+
* `UserRejectedRequestError` by name for wallets that map the rejection to a
|
|
211
|
+
* typed error without preserving the numeric code.
|
|
212
|
+
*/
|
|
213
|
+
function isUserRejection(error) {
|
|
214
|
+
var cursor = error;
|
|
215
|
+
for (var depth = 0; cursor && depth < 5; depth++) {
|
|
216
|
+
if (cursor.code === 4001 || cursor.name === "UserRejectedRequestError") {
|
|
217
|
+
return true;
|
|
218
|
+
}
|
|
219
|
+
cursor = cursor.cause;
|
|
220
|
+
}
|
|
221
|
+
return false;
|
|
222
|
+
}
|
|
203
223
|
/**
|
|
204
224
|
* Pending transactions, shared per destination rather than per handler.
|
|
205
225
|
*
|
|
@@ -209,6 +229,7 @@ var ownerKey = function (writeKey, config) {
|
|
|
209
229
|
* broadcast to match the receipt against.
|
|
210
230
|
*/
|
|
211
231
|
var pendingTransactionsByDestination = new Map();
|
|
232
|
+
var pendingBatchesByDestination = new Map();
|
|
212
233
|
/**
|
|
213
234
|
* Pending records outlive a rebuild by the same grace period as the markers.
|
|
214
235
|
* The ordinary rebuild is cleanup THEN remount, so dropping them the instant
|
|
@@ -223,6 +244,7 @@ function schedulePendingTransactionExpiry(key) {
|
|
|
223
244
|
var timer = setTimeout(function () {
|
|
224
245
|
pendingTransactionExpiry.delete(key);
|
|
225
246
|
pendingTransactionsByDestination.delete(key);
|
|
247
|
+
pendingBatchesByDestination.delete(key);
|
|
226
248
|
}, exports.MARKER_GRACE_MS);
|
|
227
249
|
(_b = (_a = timer).unref) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
228
250
|
pendingTransactionExpiry.set(key, timer);
|
|
@@ -289,6 +311,7 @@ function __resetSeededWallet() {
|
|
|
289
311
|
pendingTransactionExpiry.forEach(function (t) { return clearTimeout(t); });
|
|
290
312
|
pendingTransactionExpiry.clear();
|
|
291
313
|
pendingTransactionsByDestination.clear();
|
|
314
|
+
pendingBatchesByDestination.clear();
|
|
292
315
|
announcedConnections.clear();
|
|
293
316
|
liveHandlers.clear();
|
|
294
317
|
markerExpiry.forEach(function (timer) { return clearTimeout(timer); });
|
|
@@ -501,6 +524,21 @@ var WagmiEventHandler = /** @class */ (function () {
|
|
|
501
524
|
enumerable: false,
|
|
502
525
|
configurable: true
|
|
503
526
|
});
|
|
527
|
+
Object.defineProperty(WagmiEventHandler.prototype, "pendingBatches", {
|
|
528
|
+
/** Broadcast batches awaiting `callsStatus`, shared like the map above. */
|
|
529
|
+
get: function () {
|
|
530
|
+
var _a;
|
|
531
|
+
var key = (_a = this.ownerKey) !== null && _a !== void 0 ? _a : "";
|
|
532
|
+
var map = pendingBatchesByDestination.get(key);
|
|
533
|
+
if (!map) {
|
|
534
|
+
map = new Map();
|
|
535
|
+
pendingBatchesByDestination.set(key, map);
|
|
536
|
+
}
|
|
537
|
+
return map;
|
|
538
|
+
},
|
|
539
|
+
enumerable: false,
|
|
540
|
+
configurable: true
|
|
541
|
+
});
|
|
504
542
|
/**
|
|
505
543
|
* Set up listeners for wallet connection, disconnection, and chain changes
|
|
506
544
|
*/
|
|
@@ -1453,8 +1491,23 @@ var WagmiEventHandler = /** @class */ (function () {
|
|
|
1453
1491
|
return;
|
|
1454
1492
|
}
|
|
1455
1493
|
var queryType = queryKey[0];
|
|
1456
|
-
// Only
|
|
1457
|
-
|
|
1494
|
+
// Only the two query families that settle something we broadcast:
|
|
1495
|
+
// waitForTransactionReceipt for single transactions, callsStatus for
|
|
1496
|
+
// EIP-5792 batches (both useCallsStatus and useWaitForCallsStatus share
|
|
1497
|
+
// the 'callsStatus' key, in wagmi 2 and 3 alike).
|
|
1498
|
+
if (queryType !== "waitForTransactionReceipt" && queryType !== "callsStatus") {
|
|
1499
|
+
return;
|
|
1500
|
+
}
|
|
1501
|
+
// Batch settlement dedupes on the pending-batch registry, NOT on
|
|
1502
|
+
// processedQueries: settling deletes the registration, so a duplicate
|
|
1503
|
+
// delivery finds nothing to do. A processed-key here would be worse
|
|
1504
|
+
// than redundant - the status query can complete BEFORE the sendCalls
|
|
1505
|
+
// mutation registers the batch (TanStack dispatches a mutation's
|
|
1506
|
+
// success state after its onSuccess callbacks, and apps await the
|
|
1507
|
+
// status inside onSuccess), and a key recorded on that early skip
|
|
1508
|
+
// would block every refetch from ever settling the batch.
|
|
1509
|
+
if (queryType === "callsStatus") {
|
|
1510
|
+
this.handleCallsStatusQuery(query);
|
|
1458
1511
|
return;
|
|
1459
1512
|
}
|
|
1460
1513
|
var state = query.state;
|
|
@@ -1487,6 +1540,111 @@ var WagmiEventHandler = /** @class */ (function () {
|
|
|
1487
1540
|
// Clean up old processed queries to prevent memory leaks
|
|
1488
1541
|
cleanupOldEntries(this.processedQueries);
|
|
1489
1542
|
};
|
|
1543
|
+
/**
|
|
1544
|
+
* Settle a just-registered batch from a status query that already ran.
|
|
1545
|
+
*
|
|
1546
|
+
* Best-effort by design: the minimal QueryClient interface the SDK
|
|
1547
|
+
* accepts is not guaranteed to expose cache lookup, and a missing
|
|
1548
|
+
* `getAll` just means settlement waits for the next query event, which
|
|
1549
|
+
* is where it normally comes from anyway.
|
|
1550
|
+
*/
|
|
1551
|
+
WagmiEventHandler.prototype.settleFromCachedCallsStatus = function (batchId) {
|
|
1552
|
+
var _a, _b, _c;
|
|
1553
|
+
try {
|
|
1554
|
+
var cache = (_a = this.queryClient) === null || _a === void 0 ? void 0 : _a.getQueryCache();
|
|
1555
|
+
var queries = (_b = cache === null || cache === void 0 ? void 0 : cache.getAll) === null || _b === void 0 ? void 0 : _b.call(cache);
|
|
1556
|
+
if (!Array.isArray(queries))
|
|
1557
|
+
return;
|
|
1558
|
+
for (var _i = 0, queries_1 = queries; _i < queries_1.length; _i++) {
|
|
1559
|
+
var query = queries_1[_i];
|
|
1560
|
+
var key = query === null || query === void 0 ? void 0 : query.queryKey;
|
|
1561
|
+
if (Array.isArray(key) &&
|
|
1562
|
+
key[0] === "callsStatus" &&
|
|
1563
|
+
((_c = key[1]) === null || _c === void 0 ? void 0 : _c.id) === batchId) {
|
|
1564
|
+
this.handleCallsStatusQuery(query);
|
|
1565
|
+
}
|
|
1566
|
+
}
|
|
1567
|
+
}
|
|
1568
|
+
catch (error) {
|
|
1569
|
+
logger_1.logger.debug("WagmiEventHandler: cached callsStatus scan failed", error);
|
|
1570
|
+
}
|
|
1571
|
+
};
|
|
1572
|
+
/**
|
|
1573
|
+
* Settle an EIP-5792 batch from a `callsStatus` query.
|
|
1574
|
+
*
|
|
1575
|
+
* Only batches whose broadcast this SDK observed are settled: the batch id
|
|
1576
|
+
* must be in `pendingBatches`, for the same reason receipt queries are
|
|
1577
|
+
* gated on an observed hash - queries are visible to any code sharing the
|
|
1578
|
+
* QueryClient, and emitting for an id we never saw broadcast would let a
|
|
1579
|
+
* forged query invent transactions.
|
|
1580
|
+
*
|
|
1581
|
+
* Outcome semantics are shared with the EIP-1193 path (`src/evm/batch.ts`):
|
|
1582
|
+
* per-call receipts outrank the batch verdict, an atomic batch's single
|
|
1583
|
+
* receipt reaches every call, and a 600 leaves receipt-less calls
|
|
1584
|
+
* unsettled rather than guessed.
|
|
1585
|
+
*/
|
|
1586
|
+
WagmiEventHandler.prototype.handleCallsStatusQuery = function (query) {
|
|
1587
|
+
var _this = this;
|
|
1588
|
+
var _a;
|
|
1589
|
+
if (!this.formo.isAutocaptureEnabled("transaction")) {
|
|
1590
|
+
return;
|
|
1591
|
+
}
|
|
1592
|
+
var queryKey = query.queryKey;
|
|
1593
|
+
// Query key format: ['callsStatus', { id, ... }]
|
|
1594
|
+
var params = queryKey[1];
|
|
1595
|
+
var batchId = params === null || params === void 0 ? void 0 : params.id;
|
|
1596
|
+
if (!batchId) {
|
|
1597
|
+
return;
|
|
1598
|
+
}
|
|
1599
|
+
var pending = this.pendingBatches.get(batchId);
|
|
1600
|
+
if (!pending) {
|
|
1601
|
+
logger_1.logger.debug("WagmiEventHandler: unobserved batch", { batchId: batchId });
|
|
1602
|
+
return;
|
|
1603
|
+
}
|
|
1604
|
+
var state = query.state;
|
|
1605
|
+
if (state.status !== "success" || !state.data) {
|
|
1606
|
+
return;
|
|
1607
|
+
}
|
|
1608
|
+
try {
|
|
1609
|
+
var res_1 = state.data;
|
|
1610
|
+
var code_1 = (0, batch_1.readBatchStatusCode)(res_1);
|
|
1611
|
+
// Below 200 the batch is still pending; the query will update again.
|
|
1612
|
+
if (code_1 === undefined || code_1 < 200) {
|
|
1613
|
+
return;
|
|
1614
|
+
}
|
|
1615
|
+
logger_1.logger.debug("WagmiEventHandler: batch settled", { batchId: batchId, code: code_1 });
|
|
1616
|
+
// An explicit mutation chain is authoritative. Otherwise prefer the
|
|
1617
|
+
// chain the settlement result names - EIP-5792 v2 reports where the
|
|
1618
|
+
// batch actually landed - over one inferred from the connection at
|
|
1619
|
+
// broadcast, which goes stale if the wallet moves chains while the
|
|
1620
|
+
// prompt is up. Same precedence as the single-transaction receipt
|
|
1621
|
+
// path.
|
|
1622
|
+
var settledChainId_1 = pending.chainIdWasExplicit
|
|
1623
|
+
? pending.chainId
|
|
1624
|
+
: (_a = (0, batch_1.readBatchChainId)(res_1)) !== null && _a !== void 0 ? _a : pending.chainId;
|
|
1625
|
+
pending.calls.forEach(function (call, index) {
|
|
1626
|
+
var receipt = (0, batch_1.batchReceiptForCall)(res_1, index, pending.calls.length);
|
|
1627
|
+
var outcome = (0, batch_1.batchCallOutcome)(code_1, receipt);
|
|
1628
|
+
// 600 means SOME calls reverted, so a call with no receipt of its
|
|
1629
|
+
// own has not been decided. Reporting it either way would invent a
|
|
1630
|
+
// result; leaving it unsettled is the honest answer.
|
|
1631
|
+
if (outcome === undefined)
|
|
1632
|
+
return;
|
|
1633
|
+
_this.formo.transaction(__assign(__assign(__assign(__assign({ status: outcome, chainId: settledChainId_1 || 0, address: pending.address }, (call.data && { data: call.data })), (call.to && { to: call.to })), (call.value && { value: call.value })), ((receipt === null || receipt === void 0 ? void 0 : receipt.transactionHash)
|
|
1634
|
+
? { transactionHash: receipt.transactionHash }
|
|
1635
|
+
: {})), {
|
|
1636
|
+
batch_size: pending.calls.length,
|
|
1637
|
+
batch_index: index,
|
|
1638
|
+
batch_id: batchId,
|
|
1639
|
+
});
|
|
1640
|
+
});
|
|
1641
|
+
// Settled; a later refetch of the same query must not re-emit.
|
|
1642
|
+
this.pendingBatches.delete(batchId);
|
|
1643
|
+
}
|
|
1644
|
+
catch (error) {
|
|
1645
|
+
logger_1.logger.error("WagmiEventHandler: callsStatus error:", error);
|
|
1646
|
+
}
|
|
1647
|
+
};
|
|
1490
1648
|
/**
|
|
1491
1649
|
* Handle waitForTransactionReceipt query completion
|
|
1492
1650
|
* Emits CONFIRMED or REVERTED transaction status
|
|
@@ -1616,6 +1774,13 @@ var WagmiEventHandler = /** @class */ (function () {
|
|
|
1616
1774
|
if (mutationType === "sendTransaction" || mutationType === "writeContract") {
|
|
1617
1775
|
this.handleTransactionMutation(mutationType, mutation);
|
|
1618
1776
|
}
|
|
1777
|
+
// Handle EIP-5792 batch mutations (useSendCalls). Absent this branch,
|
|
1778
|
+
// wagmi-mode apps captured nothing for a batch: the EIP-1193 request
|
|
1779
|
+
// wrapper that handles `wallet_sendCalls` is never installed in wagmi
|
|
1780
|
+
// mode, so the mutation was the only place the batch was visible at all.
|
|
1781
|
+
if (mutationType === "sendCalls") {
|
|
1782
|
+
this.handleSendCallsMutation(mutation);
|
|
1783
|
+
}
|
|
1619
1784
|
// Clean up old processed mutations to prevent memory leaks
|
|
1620
1785
|
cleanupOldEntries(this.processedMutations);
|
|
1621
1786
|
};
|
|
@@ -1805,6 +1970,92 @@ var WagmiEventHandler = /** @class */ (function () {
|
|
|
1805
1970
|
logger_1.logger.error("WagmiEventHandler: Error handling transaction mutation:", error);
|
|
1806
1971
|
}
|
|
1807
1972
|
};
|
|
1973
|
+
/**
|
|
1974
|
+
* One `transaction` event per call in an EIP-5792 batch, wagmi path.
|
|
1975
|
+
*
|
|
1976
|
+
* Mirrors `EvmRequestTracker.trackBatchedCalls` exactly: the CALL is the
|
|
1977
|
+
* unit of attribution, so each call gets its own STARTED at pending and
|
|
1978
|
+
* BROADCASTED (with `batch_id`) when the wallet returns an id. The batch's
|
|
1979
|
+
* on-chain outcome arrives through the `callsStatus` query, handled in
|
|
1980
|
+
* `handleCallsStatusQuery`.
|
|
1981
|
+
*
|
|
1982
|
+
* Rejection matches the 1193 path's rule: only a user rejection (4001
|
|
1983
|
+
* anywhere in the error chain) marks the calls rejected - one dismissal
|
|
1984
|
+
* dismisses the whole prompt, so every call in it is rejected, and
|
|
1985
|
+
* reporting only the first would undercount. Any other error (a wallet
|
|
1986
|
+
* without EIP-5792 support, a transport failure) emits nothing further:
|
|
1987
|
+
* inventing a rejection the user never made would be worse.
|
|
1988
|
+
*/
|
|
1989
|
+
WagmiEventHandler.prototype.handleSendCallsMutation = function (mutation) {
|
|
1990
|
+
var _this = this;
|
|
1991
|
+
if (!this.formo.isAutocaptureEnabled("transaction")) {
|
|
1992
|
+
return;
|
|
1993
|
+
}
|
|
1994
|
+
var state = mutation.state;
|
|
1995
|
+
var variables = state.variables || {};
|
|
1996
|
+
var rawCalls = Array.isArray(variables.calls) ? variables.calls : [];
|
|
1997
|
+
if (rawCalls.length === 0) {
|
|
1998
|
+
return;
|
|
1999
|
+
}
|
|
2000
|
+
// Same resolution order as single transactions: an explicit per-call
|
|
2001
|
+
// value beats the tracked connection.
|
|
2002
|
+
var explicitChainId = normalizeChainId(variables.chainId);
|
|
2003
|
+
var chainId = explicitChainId !== null && explicitChainId !== void 0 ? explicitChainId : this.trackingState.lastChainId;
|
|
2004
|
+
var accountAddress = resolveAccountAddress(variables.account);
|
|
2005
|
+
var userAddress = accountAddress || this.trackingState.lastAddress;
|
|
2006
|
+
if (!userAddress) {
|
|
2007
|
+
logger_1.logger.warn("WagmiEventHandler: sendCalls without address");
|
|
2008
|
+
return;
|
|
2009
|
+
}
|
|
2010
|
+
try {
|
|
2011
|
+
var calls_1 = rawCalls.map(function (call) { return ({
|
|
2012
|
+
to: call === null || call === void 0 ? void 0 : call.to,
|
|
2013
|
+
value: (call === null || call === void 0 ? void 0 : call.value) !== undefined ? String(call.value) : undefined,
|
|
2014
|
+
data: call === null || call === void 0 ? void 0 : call.data,
|
|
2015
|
+
}); });
|
|
2016
|
+
var emitAll = function (status, extra) {
|
|
2017
|
+
calls_1.forEach(function (call, index) {
|
|
2018
|
+
_this.formo.transaction(__assign(__assign(__assign({ status: status, chainId: chainId || 0, address: userAddress }, (call.data && { data: call.data })), (call.to && { to: call.to })), (call.value && { value: call.value })), __assign({ batch_size: calls_1.length, batch_index: index }, extra));
|
|
2019
|
+
});
|
|
2020
|
+
};
|
|
2021
|
+
if (state.status === "pending") {
|
|
2022
|
+
// STARTED carries no batch id, exactly as a single transaction has
|
|
2023
|
+
// no hash yet: the wallet has not issued one.
|
|
2024
|
+
logger_1.logger.debug("WagmiEventHandler: sendCalls start", { calls: calls_1.length });
|
|
2025
|
+
emitAll(events_1.TransactionStatus.STARTED);
|
|
2026
|
+
}
|
|
2027
|
+
else if (state.status === "success") {
|
|
2028
|
+
var batchId = (0, batch_1.readBatchId)(state.data);
|
|
2029
|
+
logger_1.logger.debug("WagmiEventHandler: sendCalls broadcast", { batchId: batchId });
|
|
2030
|
+
emitAll(events_1.TransactionStatus.BROADCASTED, batchId ? { batch_id: batchId } : undefined);
|
|
2031
|
+
if (batchId) {
|
|
2032
|
+
this.pendingBatches.set(batchId, __assign(__assign({ address: userAddress }, (chainId !== undefined && { chainId: chainId })), { chainIdWasExplicit: explicitChainId !== undefined, calls: calls_1 }));
|
|
2033
|
+
// Same bound as pendingTransactions, same reason.
|
|
2034
|
+
if (this.pendingBatches.size > 100) {
|
|
2035
|
+
var keys = Array.from(this.pendingBatches.keys());
|
|
2036
|
+
for (var i = 0; i < 50 && i < keys.length; i++) {
|
|
2037
|
+
this.pendingBatches.delete(keys[i]);
|
|
2038
|
+
}
|
|
2039
|
+
}
|
|
2040
|
+
// The status query can have completed BEFORE this registration:
|
|
2041
|
+
// TanStack dispatches a mutation's success state after its
|
|
2042
|
+
// onSuccess callbacks, and apps await waitForCallsStatus inside
|
|
2043
|
+
// onSuccess. That early query event found no registration and did
|
|
2044
|
+
// nothing, so look for its settled result in the cache now.
|
|
2045
|
+
this.settleFromCachedCallsStatus(batchId);
|
|
2046
|
+
}
|
|
2047
|
+
}
|
|
2048
|
+
else if (state.status === "error") {
|
|
2049
|
+
if (isUserRejection(state.error)) {
|
|
2050
|
+
logger_1.logger.debug("WagmiEventHandler: sendCalls rejected");
|
|
2051
|
+
emitAll(events_1.TransactionStatus.REJECTED);
|
|
2052
|
+
}
|
|
2053
|
+
}
|
|
2054
|
+
}
|
|
2055
|
+
catch (error) {
|
|
2056
|
+
logger_1.logger.error("WagmiEventHandler: sendCalls error:", error);
|
|
2057
|
+
}
|
|
2058
|
+
};
|
|
1808
2059
|
/**
|
|
1809
2060
|
* Get the current Wagmi state
|
|
1810
2061
|
* Supports both getState() method and direct state property access
|
|
@@ -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
|
-
*
|
|
72
|
-
*
|
|
73
|
-
* per-contract
|
|
74
|
-
*
|
|
75
|
-
*
|
|
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
|
*
|
|
@@ -60,6 +60,7 @@ import { logger } from "../logger";
|
|
|
60
60
|
import { parseChainId } from "../utils/chain";
|
|
61
61
|
import { validateAndChecksumAddress } from "../utils/address";
|
|
62
62
|
import { SignatureStatus, TransactionStatus, WRAPPED_REQUEST_SYMBOL, WRAPPED_REQUEST_REF_SYMBOL, } from "../types";
|
|
63
|
+
import { readBatchId, readBatchStatusCode, batchCallOutcome, batchReceiptForCall, } from "./batch";
|
|
63
64
|
/**
|
|
64
65
|
* Decode a hex-encoded `personal_sign` message.
|
|
65
66
|
*
|
|
@@ -511,11 +512,14 @@ var EvmRequestTracker = /** @class */ (function () {
|
|
|
511
512
|
/**
|
|
512
513
|
* One `transaction` event per call in an EIP-5792 batch.
|
|
513
514
|
*
|
|
514
|
-
*
|
|
515
|
-
*
|
|
516
|
-
* per-contract
|
|
517
|
-
*
|
|
518
|
-
*
|
|
515
|
+
* The CALL is the unit of attribution: each has its own target, calldata,
|
|
516
|
+
* and value, and folding a batch into one event would misattribute revenue
|
|
517
|
+
* and per-contract activity for every app that adopts smart accounts. How
|
|
518
|
+
* many on-chain transactions a batch becomes depends on execution - an
|
|
519
|
+
* atomic batch lands as ONE transaction, a non-atomic fallback as several -
|
|
520
|
+
* so on-chain volume is `count(distinct transaction_hash)`, wallet actions
|
|
521
|
+
* `count(distinct batch_id)`, never the event count. Each call is reported
|
|
522
|
+
* on its own, carrying the batch id so the calls reassemble downstream.
|
|
519
523
|
*
|
|
520
524
|
* Status is per BATCH, because that is what `wallet_getCallsStatus` reports.
|
|
521
525
|
* When it resolves, every call in the batch moves together, except where
|
|
@@ -586,7 +590,7 @@ var EvmRequestTracker = /** @class */ (function () {
|
|
|
586
590
|
return [4 /*yield*/, sendPromise];
|
|
587
591
|
case 2:
|
|
588
592
|
result = _d.sent();
|
|
589
|
-
batchId =
|
|
593
|
+
batchId = readBatchId(result);
|
|
590
594
|
for (_a = 0, payloads_2 = payloads; _a < payloads_2.length; _a++) {
|
|
591
595
|
p = payloads_2[_a];
|
|
592
596
|
properties = p.properties, rest = __rest(p, ["properties"]);
|
|
@@ -621,54 +625,6 @@ var EvmRequestTracker = /** @class */ (function () {
|
|
|
621
625
|
});
|
|
622
626
|
});
|
|
623
627
|
};
|
|
624
|
-
/**
|
|
625
|
-
* How one call in a settled batch ended.
|
|
626
|
-
*
|
|
627
|
-
* A per-call receipt is authoritative where it exists: that is what makes a
|
|
628
|
-
* partially reverted non-atomic batch report honestly rather than tarring
|
|
629
|
-
* every call with the batch's worst outcome. A receipt whose own status is
|
|
630
|
-
* unreadable falls back to the batch verdict rather than being assumed good.
|
|
631
|
-
*
|
|
632
|
-
* The codes are EIP-5792's: 200 confirmed, 400 failed BEFORE landing on
|
|
633
|
-
* chain, 500 reverted, 600 partially reverted. 400 is a rejection, not a
|
|
634
|
-
* revert - nothing was mined, so calling it reverted would misreport gas
|
|
635
|
-
* spent and on-chain activity that never happened.
|
|
636
|
-
*
|
|
637
|
-
* Returns undefined when the call cannot be decided, which happens on 600
|
|
638
|
-
* for a call the wallet gave no receipt for.
|
|
639
|
-
*/
|
|
640
|
-
EvmRequestTracker.prototype.batchCallOutcome = function (code, receipt) {
|
|
641
|
-
var receiptStatus = receipt === null || receipt === void 0 ? void 0 : receipt.status;
|
|
642
|
-
if (receiptStatus !== undefined) {
|
|
643
|
-
return receiptStatus === "0x0" || receiptStatus === 0
|
|
644
|
-
? TransactionStatus.REVERTED
|
|
645
|
-
: TransactionStatus.CONFIRMED;
|
|
646
|
-
}
|
|
647
|
-
if (code >= 600)
|
|
648
|
-
return undefined;
|
|
649
|
-
if (code >= 500)
|
|
650
|
-
return TransactionStatus.REVERTED;
|
|
651
|
-
if (code >= 400)
|
|
652
|
-
return TransactionStatus.REJECTED;
|
|
653
|
-
return TransactionStatus.CONFIRMED;
|
|
654
|
-
};
|
|
655
|
-
/**
|
|
656
|
-
* The batch identifier from a `wallet_sendCalls` result.
|
|
657
|
-
*
|
|
658
|
-
* EIP-5792 settled on `{ id }`, but wallets shipped against the earlier
|
|
659
|
-
* draft return a bare string. Both are accepted so a wallet on either
|
|
660
|
-
* version is still grouped.
|
|
661
|
-
*/
|
|
662
|
-
EvmRequestTracker.prototype.readBatchId = function (result) {
|
|
663
|
-
if (typeof result === "string" && result.length > 0)
|
|
664
|
-
return result;
|
|
665
|
-
if (result && typeof result === "object") {
|
|
666
|
-
var id = result.id;
|
|
667
|
-
if (typeof id === "string" && id.length > 0)
|
|
668
|
-
return id;
|
|
669
|
-
}
|
|
670
|
-
return undefined;
|
|
671
|
-
};
|
|
672
628
|
/**
|
|
673
629
|
* Resolve a batch through `wallet_getCallsStatus`.
|
|
674
630
|
*
|
|
@@ -691,7 +647,7 @@ var EvmRequestTracker = /** @class */ (function () {
|
|
|
691
647
|
return [2 /*return*/];
|
|
692
648
|
attempts = 0;
|
|
693
649
|
poll = function () { return __awaiter(_this, void 0, void 0, function () {
|
|
694
|
-
var
|
|
650
|
+
var res_1, code_1, e_8;
|
|
695
651
|
var _this = this;
|
|
696
652
|
return __generator(this, function (_a) {
|
|
697
653
|
switch (_a.label) {
|
|
@@ -706,13 +662,14 @@ var EvmRequestTracker = /** @class */ (function () {
|
|
|
706
662
|
params: [batchId],
|
|
707
663
|
})];
|
|
708
664
|
case 2:
|
|
709
|
-
|
|
710
|
-
code_1 =
|
|
665
|
+
res_1 = (_a.sent());
|
|
666
|
+
code_1 = readBatchStatusCode(res_1);
|
|
711
667
|
if (code_1 !== undefined && code_1 >= 200) {
|
|
712
|
-
receipts_1 = Array.isArray(res === null || res === void 0 ? void 0 : res.receipts) ? res.receipts : [];
|
|
713
668
|
payloads.forEach(function (p, index) {
|
|
714
|
-
|
|
715
|
-
|
|
669
|
+
// Atomic-aware: one receipt covering the whole batch reaches
|
|
670
|
+
// every call, hash included, not just call 0.
|
|
671
|
+
var receipt = batchReceiptForCall(res_1, index, payloads.length);
|
|
672
|
+
var outcome = batchCallOutcome(code_1, receipt);
|
|
716
673
|
// 600 means SOME calls reverted, so a call with no receipt of its
|
|
717
674
|
// own has not been decided. Reporting it either way would invent
|
|
718
675
|
// 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
|