@valve-tech/tx-tracker 0.14.0 → 0.16.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/CHANGELOG.md +116 -0
- package/dist/events.d.ts +22 -1
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +6 -0
- package/dist/events.js.map +1 -1
- package/dist/index.d.ts +5 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/local-storage-store.d.ts +98 -0
- package/dist/local-storage-store.d.ts.map +1 -0
- package/dist/local-storage-store.js +208 -0
- package/dist/local-storage-store.js.map +1 -0
- package/dist/observations.d.ts +9 -0
- package/dist/observations.d.ts.map +1 -1
- package/dist/observations.js +53 -17
- package/dist/observations.js.map +1 -1
- package/dist/store.d.ts +12 -1
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js.map +1 -1
- package/dist/tracker.d.ts +50 -1
- package/dist/tracker.d.ts.map +1 -1
- package/dist/tracker.js +126 -12
- package/dist/tracker.js.map +1 -1
- package/package.json +2 -2
package/dist/observations.js
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* to its mutable `TrackedRecord`, then emits the returned `events`
|
|
18
18
|
* via its event-bus + store-audit-log machinery.
|
|
19
19
|
*/
|
|
20
|
-
import { buildLeftMempool, buildReplacedBy, buildSeenInBlock, buildSeenInMempool, buildUnseenForNBlocks, } from './events.js';
|
|
20
|
+
import { buildConfirmedTerminal, buildLeftMempool, buildReplacedBy, buildSeenInBlock, buildSeenInMempool, buildUnseenForNBlocks, } from './events.js';
|
|
21
21
|
const EMPTY_RESULT = {
|
|
22
22
|
events: [],
|
|
23
23
|
statusPatch: {},
|
|
@@ -45,7 +45,15 @@ const EMPTY_RESULT = {
|
|
|
45
45
|
* after subscription (no `firstObservedAtBlock` yet).
|
|
46
46
|
*/
|
|
47
47
|
export const decideBlockObservation = (input) => {
|
|
48
|
-
const { record, blockHash, blockNumber, txHashSet, txs, chainId, eventSource, envelope, previousTipNumber, prefetchedReceipts, } = input;
|
|
48
|
+
const { record, blockHash, blockNumber, txHashSet, txs, chainId, eventSource, envelope, previousTipNumber, prefetchedReceipts, confirmationsForTerminal, } = input;
|
|
49
|
+
// Helper for the mined-confirmed terminal transition (v0.15+).
|
|
50
|
+
// Returns true when the current confirmations reach the configured
|
|
51
|
+
// threshold AND the record hasn't already gone terminal via any
|
|
52
|
+
// other path. The check is symmetric in Paths 1 + 2 since both
|
|
53
|
+
// produce a fresh `confirmations` value on this tick.
|
|
54
|
+
const reachedConfirmedTerminal = (confirmations) => confirmationsForTerminal != null &&
|
|
55
|
+
confirmations >= confirmationsForTerminal &&
|
|
56
|
+
record.status.terminalAtBlockNumber == null;
|
|
49
57
|
const wasSeenInThisBlock = txHashSet.has(record.hash);
|
|
50
58
|
if (wasSeenInThisBlock) {
|
|
51
59
|
const tx = txs.find((t) => t.hash === record.hash);
|
|
@@ -69,21 +77,35 @@ export const decideBlockObservation = (input) => {
|
|
|
69
77
|
const receipt = isFreshInclusion
|
|
70
78
|
? prefetchedReceipts?.get(record.hash)
|
|
71
79
|
: undefined;
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
80
|
+
const terminalNow = reachedConfirmedTerminal(confirmations);
|
|
81
|
+
const events = [
|
|
82
|
+
...(isFreshInclusion
|
|
83
|
+
? [
|
|
84
|
+
buildSeenInBlock({
|
|
85
|
+
hash: record.hash,
|
|
86
|
+
chainId,
|
|
87
|
+
source: eventSource,
|
|
88
|
+
at: envelope,
|
|
89
|
+
blockHash,
|
|
90
|
+
blockNumber,
|
|
91
|
+
transactionIndex,
|
|
92
|
+
confirmations,
|
|
93
|
+
...(receipt !== undefined ? { receipt } : {}),
|
|
94
|
+
}),
|
|
95
|
+
]
|
|
96
|
+
: []),
|
|
97
|
+
...(terminalNow
|
|
98
|
+
? [
|
|
99
|
+
buildConfirmedTerminal({
|
|
100
|
+
hash: record.hash,
|
|
101
|
+
chainId,
|
|
102
|
+
source: eventSource,
|
|
103
|
+
at: envelope,
|
|
104
|
+
confirmations,
|
|
105
|
+
}),
|
|
106
|
+
]
|
|
107
|
+
: []),
|
|
108
|
+
];
|
|
87
109
|
return {
|
|
88
110
|
events,
|
|
89
111
|
statusPatch: {
|
|
@@ -91,6 +113,7 @@ export const decideBlockObservation = (input) => {
|
|
|
91
113
|
unseenStreak: 0,
|
|
92
114
|
firstObservedAtBlock: record.status.firstObservedAtBlock ?? blockNumber,
|
|
93
115
|
lastObservedAtBlock: blockNumber,
|
|
116
|
+
...(terminalNow ? { terminalAtBlockNumber: blockNumber } : {}),
|
|
94
117
|
},
|
|
95
118
|
identityPatch: cacheIdentity(record.identity, tx),
|
|
96
119
|
inMempoolPatch: null,
|
|
@@ -103,6 +126,7 @@ export const decideBlockObservation = (input) => {
|
|
|
103
126
|
...record.status.lastSeenInBlock,
|
|
104
127
|
confirmations: bumped,
|
|
105
128
|
};
|
|
129
|
+
const terminalNow = reachedConfirmedTerminal(bumped);
|
|
106
130
|
return {
|
|
107
131
|
events: [
|
|
108
132
|
buildSeenInBlock({
|
|
@@ -115,10 +139,22 @@ export const decideBlockObservation = (input) => {
|
|
|
115
139
|
transactionIndex: updated.transactionIndex,
|
|
116
140
|
confirmations: bumped,
|
|
117
141
|
}),
|
|
142
|
+
...(terminalNow
|
|
143
|
+
? [
|
|
144
|
+
buildConfirmedTerminal({
|
|
145
|
+
hash: record.hash,
|
|
146
|
+
chainId,
|
|
147
|
+
source: eventSource,
|
|
148
|
+
at: envelope,
|
|
149
|
+
confirmations: bumped,
|
|
150
|
+
}),
|
|
151
|
+
]
|
|
152
|
+
: []),
|
|
118
153
|
],
|
|
119
154
|
statusPatch: {
|
|
120
155
|
lastSeenInBlock: updated,
|
|
121
156
|
lastObservedAtBlock: blockNumber,
|
|
157
|
+
...(terminalNow ? { terminalAtBlockNumber: blockNumber } : {}),
|
|
122
158
|
},
|
|
123
159
|
identityPatch: null,
|
|
124
160
|
inMempoolPatch: null,
|
package/dist/observations.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"observations.js","sourceRoot":"","sources":["../src/observations.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAIH,OAAO,EACL,gBAAgB,EAChB,eAAe,EACf,gBAAgB,EAChB,kBAAkB,EAClB,qBAAqB,GAKtB,MAAM,aAAa,CAAA;AAsCpB,MAAM,YAAY,GAAsB;IACtC,MAAM,EAAE,EAAE;IACV,WAAW,EAAE,EAAE;IACf,aAAa,EAAE,IAAI;IACnB,cAAc,EAAE,IAAI;CACrB,CAAA;
|
|
1
|
+
{"version":3,"file":"observations.js","sourceRoot":"","sources":["../src/observations.ts"],"names":[],"mappings":"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"}
|
package/dist/store.d.ts
CHANGED
|
@@ -99,7 +99,18 @@ export interface TxTrackerStore {
|
|
|
99
99
|
put(record: TrackedTxRecord): Promise<void>;
|
|
100
100
|
/** Read the latest record for a hash. Returns null if absent. */
|
|
101
101
|
get(chainId: number, hash: Hash): Promise<TrackedTxRecord | null>;
|
|
102
|
-
/**
|
|
102
|
+
/**
|
|
103
|
+
* Remove a hash. Called when the retention window expires.
|
|
104
|
+
*
|
|
105
|
+
* **Contract: implementations must clear ALL state associated with
|
|
106
|
+
* the hash** — the record itself, the event log (if any), and any
|
|
107
|
+
* other per-hash keys the implementation maintains. A common bug in
|
|
108
|
+
* custom stores is forgetting to delete the event log alongside the
|
|
109
|
+
* record, leaving orphaned log entries that never expire. The
|
|
110
|
+
* first-party `createInMemoryStore` and
|
|
111
|
+
* `createLocalStorageTrackerStore` already enforce this; consumer
|
|
112
|
+
* implementations must do the same.
|
|
113
|
+
*/
|
|
103
114
|
delete(chainId: number, hash: Hash): Promise<void>;
|
|
104
115
|
/**
|
|
105
116
|
* List records that carry at least one durable subscription. Called
|
package/dist/store.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AAEnE;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,IAAI,CAAA;IACV,2DAA2D;IAC3D,MAAM,EAAE,QAAQ,CAAA;IAChB,kDAAkD;IAClD,oBAAoB,EAAE,MAAM,CAAA;IAC5B,8DAA8D;IAC9D,uBAAuB,EAAE,MAAM,CAAA;IAC/B;;;;;OAKG;IACH,6BAA6B,EAAE,MAAM,CAAA;IACrC,qDAAqD;IACrD,aAAa,EAAE,qBAAqB,EAAE,CAAA;CACvC;AAED;;;;;;GAMG;AACH,MAAM,WAAW,qBAAqB;IACpC,mEAAmE;IACnE,EAAE,EAAE,MAAM,CAAA;IACV,0DAA0D;IAC1D,OAAO,EAAE,OAAO,CAAA;IAChB,QAAQ,EAAE,YAAY,GAAG,YAAY,CAAA;CACtC;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,IAAI,CAAA;CACX;AAED;;;;;GAKG;AACH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,WAAW,CAAA;IACjC,yBAAyB;IACzB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,wEAAwE;IACxE,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,OAAO,0BAA0B,EAAE,KAAK,KAAK,OAAO,CAAA;CAClE;AAED;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,GAAG,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAE3C,iEAAiE;IACjE,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAAA;IAEjE
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AAEnE;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,IAAI,CAAA;IACV,2DAA2D;IAC3D,MAAM,EAAE,QAAQ,CAAA;IAChB,kDAAkD;IAClD,oBAAoB,EAAE,MAAM,CAAA;IAC5B,8DAA8D;IAC9D,uBAAuB,EAAE,MAAM,CAAA;IAC/B;;;;;OAKG;IACH,6BAA6B,EAAE,MAAM,CAAA;IACrC,qDAAqD;IACrD,aAAa,EAAE,qBAAqB,EAAE,CAAA;CACvC;AAED;;;;;;GAMG;AACH,MAAM,WAAW,qBAAqB;IACpC,mEAAmE;IACnE,EAAE,EAAE,MAAM,CAAA;IACV,0DAA0D;IAC1D,OAAO,EAAE,OAAO,CAAA;IAChB,QAAQ,EAAE,YAAY,GAAG,YAAY,CAAA;CACtC;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,IAAI,CAAA;CACX;AAED;;;;;GAKG;AACH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,WAAW,CAAA;IACjC,yBAAyB;IACzB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,wEAAwE;IACxE,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,OAAO,0BAA0B,EAAE,KAAK,KAAK,OAAO,CAAA;CAClE;AAED;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,GAAG,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAE3C,iEAAiE;IACjE,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAAA;IAEjE;;;;;;;;;;;OAWG;IACH,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAElD;;;;OAIG;IACH,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,CAAA;IAExD;;;;;OAKG;IACH,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAEvE;;;;;OAKG;IACH,YAAY,CAAC,CACX,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,IAAI,EACV,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,OAAO,EAAE,CAAC,CAAA;CACtB;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,oBAAoB;IACnC,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAC1B;AAcD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,mBAAmB,GAC9B,UAAS,oBAAyB,KACjC,cAsDF,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB,GACjC,qBAAqB,MAAM,EAC3B,kBAAiB,MAAiC,KACjD,MAAuD,CAAA;AAE1D;;;GAGG;AACH,eAAO,MAAM,sBAAsB,KAA2B,CAAA"}
|
package/dist/store.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"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"}
|
package/dist/tracker.d.ts
CHANGED
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
* - **Browser/mobile safe** (§2.4). No Node-only deps; the
|
|
36
36
|
* pub/sub primitive is `chain-source`'s `Subscriptions<E>`.
|
|
37
37
|
*/
|
|
38
|
-
import type { ChainSource, Capabilities, RawTx } from '@valve-tech/chain-source';
|
|
38
|
+
import type { ChainSource, Capabilities, Logger, RawTx } from '@valve-tech/chain-source';
|
|
39
39
|
import type { TxGroupEvent } from './group-events.js';
|
|
40
40
|
import { type Address, type At, type Hash, type TxEvent, type TxStatus } from './events.js';
|
|
41
41
|
import { type BulkSelector, type TxTrackerStore } from './store.js';
|
|
@@ -158,6 +158,24 @@ export interface TrackOptions {
|
|
|
158
158
|
* wins across subscriptions on the same hash.
|
|
159
159
|
*/
|
|
160
160
|
probeTransaction?: ProbeTransaction;
|
|
161
|
+
/**
|
|
162
|
+
* Caller-provided stable identifier for the persisted subscription
|
|
163
|
+
* (only meaningful when `durable: true`). When set, repeated calls
|
|
164
|
+
* with the same `subscriptionId` are idempotent — the persisted
|
|
165
|
+
* subscription is recorded exactly once even across many subscribes
|
|
166
|
+
* (e.g. React component remounts, hot-reloads, page reloads with a
|
|
167
|
+
* cross-process store).
|
|
168
|
+
*
|
|
169
|
+
* Without an explicit id, the tracker auto-dedups by `(durable,
|
|
170
|
+
* selector)`: a second `subscribe` on the same hash with `durable: true`
|
|
171
|
+
* reuses the prior persisted entry rather than appending a duplicate.
|
|
172
|
+
* Use the explicit id when you need a stable handle to a specific
|
|
173
|
+
* persisted entry — e.g., a long-lived component lifecycle that wants
|
|
174
|
+
* to guarantee its persisted entry never multiplies.
|
|
175
|
+
*
|
|
176
|
+
* No-op when `durable` is falsy.
|
|
177
|
+
*/
|
|
178
|
+
subscriptionId?: string;
|
|
161
179
|
}
|
|
162
180
|
/** Bulk subscription options — extends per-hash `TrackOptions`. */
|
|
163
181
|
export interface BulkTrackOptions extends TrackOptions {
|
|
@@ -222,7 +240,38 @@ export interface CreateTxTrackerOptions {
|
|
|
222
240
|
*/
|
|
223
241
|
retentionBlocks?: number;
|
|
224
242
|
onError?: (method: string, err: unknown) => void;
|
|
243
|
+
/**
|
|
244
|
+
* Optional logger callback. Same shape as
|
|
245
|
+
* `@valve-tech/chain-source` — `(level, message, meta?) => void`.
|
|
246
|
+
* The tracker calls it at decision points the consumer might want
|
|
247
|
+
* to surface: rehydration counts, dedup-migration writes, terminal
|
|
248
|
+
* transitions, retention-expiry firings, capability-degradation /
|
|
249
|
+
* recovery transitions. Errors continue to flow through `onError`;
|
|
250
|
+
* the logger covers the "what did the tracker decide" question.
|
|
251
|
+
*/
|
|
252
|
+
logger?: Logger;
|
|
225
253
|
lifecycle?: 'eager' | 'lazy';
|
|
254
|
+
/**
|
|
255
|
+
* Mined-and-confirmed terminal threshold. When non-null and a tracked
|
|
256
|
+
* record's `lastSeenInBlock.confirmations` reaches this value, the
|
|
257
|
+
* record transitions to terminal (`terminalAtBlockNumber` anchored on
|
|
258
|
+
* the current tip — same pattern as the existing replacement /
|
|
259
|
+
* unseen-for-N terminal arms) and `confirmed-terminal` fires once.
|
|
260
|
+
* The retention countdown then begins from that block.
|
|
261
|
+
*
|
|
262
|
+
* Pre-v0.15, normally-mined transactions never reached terminal —
|
|
263
|
+
* retention enforcement only fired on replacement or
|
|
264
|
+
* unseen-for-N-blocks paths, so successful txs accumulated in
|
|
265
|
+
* long-lived stores forever. `null` (default) preserves that
|
|
266
|
+
* behavior; opt in with a value `>= reorgDepthBlocks` to safely
|
|
267
|
+
* retire mined records past the reorg window.
|
|
268
|
+
*
|
|
269
|
+
* Validation: must be `null`, `undefined`, or a positive integer.
|
|
270
|
+
* Recommended: `≥ reorgDepthBlocks` (default reorg depth = 12) to
|
|
271
|
+
* avoid premature terminal during a same-height reorg unmining
|
|
272
|
+
* the tx.
|
|
273
|
+
*/
|
|
274
|
+
confirmationsForTerminal?: number | null;
|
|
226
275
|
/**
|
|
227
276
|
* Cadence for per-hash status polling via
|
|
228
277
|
* `source.getTransaction(hash)` (`eth_getTransactionByHash`). Runs once
|
package/dist/tracker.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tracker.d.ts","sourceRoot":"","sources":["../src/tracker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAEH,OAAO,KAAK,EAEV,WAAW,EACX,YAAY,
|
|
1
|
+
{"version":3,"file":"tracker.d.ts","sourceRoot":"","sources":["../src/tracker.ts"],"names":[],"mappings":"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"}
|
package/dist/tracker.js
CHANGED
|
@@ -46,6 +46,56 @@ import { computeRetentionExpiry, createInMemoryStore, defaultRetentionBlocks, }
|
|
|
46
46
|
// Internals
|
|
47
47
|
// -----------------------------------------------------------------
|
|
48
48
|
const DEFAULT_UNSEEN_THRESHOLD_BLOCKS = 30;
|
|
49
|
+
/**
|
|
50
|
+
* Dedup persisted subscriptions by `(durable, selector)` shape. Pre-v0.15
|
|
51
|
+
* stores may carry structurally-identical entries (same selector kind +
|
|
52
|
+
* address/hash + durable flag) accumulated from repeated subscribes;
|
|
53
|
+
* v0.15+ tracker dedups on insert, but historical records still need to
|
|
54
|
+
* be cleaned up on rehydration so the migrated state flows back via the
|
|
55
|
+
* next `store.put`. Predicate selectors carry a non-serializable
|
|
56
|
+
* function reference and aren't durable per spec §13.2 — they're keyed
|
|
57
|
+
* by id so any anomalous predicate entries in legacy stores stay
|
|
58
|
+
* distinct (defensive; shouldn't normally appear).
|
|
59
|
+
*/
|
|
60
|
+
const dedupPersistedSubscriptions = (subs) => {
|
|
61
|
+
const seen = new Set();
|
|
62
|
+
const result = [];
|
|
63
|
+
for (const sub of subs) {
|
|
64
|
+
const sel = sub.selector;
|
|
65
|
+
let selectorKey;
|
|
66
|
+
// Defensive: legacy persisted records (pre-current `selector`
|
|
67
|
+
// nesting) carry the discriminant fields flat at the top level —
|
|
68
|
+
// `selector` is undefined at runtime even though the type says
|
|
69
|
+
// otherwise. Treat such entries as distinct (keyed by id) so they
|
|
70
|
+
// pass through untouched; downstream code already handles legacy
|
|
71
|
+
// shapes via the per-field defensive reads (v0.11.x pattern).
|
|
72
|
+
if (!sel || typeof sel !== 'object') {
|
|
73
|
+
selectorKey = `legacy:${sub.id}`;
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
switch (sel.kind) {
|
|
77
|
+
case 'hash':
|
|
78
|
+
selectorKey = `hash:${sel.hash}`;
|
|
79
|
+
break;
|
|
80
|
+
case 'from':
|
|
81
|
+
selectorKey = `from:${sel.address ?? ''}`;
|
|
82
|
+
break;
|
|
83
|
+
case 'to':
|
|
84
|
+
selectorKey = `to:${sel.address ?? ''}`;
|
|
85
|
+
break;
|
|
86
|
+
default:
|
|
87
|
+
// Predicate selectors aren't durable; keep distinct by id.
|
|
88
|
+
selectorKey = `predicate:${sub.id}`;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
const key = `${sub.durable ? '1' : '0'}:${selectorKey}`;
|
|
92
|
+
if (seen.has(key))
|
|
93
|
+
continue;
|
|
94
|
+
seen.add(key);
|
|
95
|
+
result.push(sub);
|
|
96
|
+
}
|
|
97
|
+
return result;
|
|
98
|
+
};
|
|
49
99
|
/**
|
|
50
100
|
* Compare two capability snapshots; return per-key transitions
|
|
51
101
|
* (`degraded` / `recovered`). Degradation is "moved away from the
|
|
@@ -117,7 +167,20 @@ const mempoolEventSource = (caps) => caps.newPendingTransactions === 'subscripti
|
|
|
117
167
|
* }
|
|
118
168
|
*/
|
|
119
169
|
export const createTxTracker = (options) => {
|
|
120
|
-
const { source, chainId, store = createInMemoryStore(), lostSignalPolicy: defaultLostSignalPolicy = 'emit-uncertain', reorgDepthBlocks = defaultReorgDepthBlocks, unseenThresholdBlocks = DEFAULT_UNSEEN_THRESHOLD_BLOCKS, maxBulkSubscriptions = defaultMaxBulkSubscriptions, retentionBlocks = defaultRetentionBlocks, onError, lifecycle = 'eager', statusPollEveryBlocks = 1, } = options;
|
|
170
|
+
const { source, chainId, store = createInMemoryStore(), lostSignalPolicy: defaultLostSignalPolicy = 'emit-uncertain', reorgDepthBlocks = defaultReorgDepthBlocks, unseenThresholdBlocks = DEFAULT_UNSEEN_THRESHOLD_BLOCKS, maxBulkSubscriptions = defaultMaxBulkSubscriptions, retentionBlocks = defaultRetentionBlocks, onError, lifecycle = 'eager', statusPollEveryBlocks = 1, confirmationsForTerminal = null, } = options;
|
|
171
|
+
// `log` always callable; consumers can omit `logger` without us
|
|
172
|
+
// peppering the code with optional chains. Used at narrowly-chosen
|
|
173
|
+
// decision points where the consumer would care to observe the
|
|
174
|
+
// tracker's behavior.
|
|
175
|
+
const log = options.logger ?? (() => { });
|
|
176
|
+
// Validate at construction — silent acceptance of zero/negative
|
|
177
|
+
// thresholds would silently disable retention or fire terminal on
|
|
178
|
+
// a not-yet-included tx. Both are footguns.
|
|
179
|
+
if (confirmationsForTerminal != null &&
|
|
180
|
+
(!Number.isInteger(confirmationsForTerminal) ||
|
|
181
|
+
confirmationsForTerminal < 1)) {
|
|
182
|
+
throw new Error(`createTxTracker: confirmationsForTerminal must be a positive integer or null (got ${String(confirmationsForTerminal)})`);
|
|
183
|
+
}
|
|
121
184
|
const tracked = new Map();
|
|
122
185
|
const bulkSubs = new Map();
|
|
123
186
|
const globalSubs = new Subscriptions();
|
|
@@ -266,7 +329,19 @@ export const createTxTracker = (options) => {
|
|
|
266
329
|
// Capability gate: if receiptByHash is unavailable, warn once and
|
|
267
330
|
// fall back to emit-uncertain semantics (the signal-degraded path
|
|
268
331
|
// already handles caller awareness).
|
|
269
|
-
|
|
332
|
+
//
|
|
333
|
+
// The `caps.ready === false` short-circuit (v0.15+) avoids a
|
|
334
|
+
// spurious cold-start warning before the source's capability probe
|
|
335
|
+
// resolves: prior to v0.15, conservative pre-probe defaults set
|
|
336
|
+
// `receiptByHash: 'unavailable'`, which made this gate fire its
|
|
337
|
+
// "permanently unavailable" warning for an RPC that ultimately
|
|
338
|
+
// supports the method. Skipping silently while probing means the
|
|
339
|
+
// warning only fires when the probe has completed and reported the
|
|
340
|
+
// capability as genuinely missing.
|
|
341
|
+
const caps = source.capabilities();
|
|
342
|
+
if (caps.ready === false)
|
|
343
|
+
return;
|
|
344
|
+
if (caps.receiptByHash !== 'available') {
|
|
270
345
|
if (!receiptPollGateWarned) {
|
|
271
346
|
receiptPollGateWarned = true;
|
|
272
347
|
onError?.('tx-tracker.receipt-poll-fallback', new Error('receipt-poll-fallback requested but capability receiptByHash unavailable; falling back to emit-uncertain semantics'));
|
|
@@ -718,14 +793,16 @@ export const createTxTracker = (options) => {
|
|
|
718
793
|
envelope,
|
|
719
794
|
previousTipNumber,
|
|
720
795
|
prefetchedReceipts,
|
|
796
|
+
confirmationsForTerminal,
|
|
721
797
|
});
|
|
722
798
|
applyObservationResult(record, result);
|
|
723
799
|
for (const event of result.events)
|
|
724
800
|
emit(record, event);
|
|
725
801
|
}
|
|
726
802
|
// Retention enforcement (spec §10, audit #2). Records that have
|
|
727
|
-
// reached a terminal state
|
|
728
|
-
//
|
|
803
|
+
// reached a terminal state — `replaced-by`, `unseen-for-N-blocks`,
|
|
804
|
+
// or `confirmed-terminal` (v0.15+) — carry `terminalAtBlockNumber`.
|
|
805
|
+
// Once the chain has
|
|
729
806
|
// moved `retentionBlocks` past that point, drop the record and
|
|
730
807
|
// emit `Stopped({ reason: 'retention-expired' })`. Records still
|
|
731
808
|
// in flight (terminalAtBlockNumber === null) are NOT subject to
|
|
@@ -763,6 +840,10 @@ export const createTxTracker = (options) => {
|
|
|
763
840
|
void store.delete(chainId, record.hash).catch((err) => {
|
|
764
841
|
onError?.('store.delete', err);
|
|
765
842
|
});
|
|
843
|
+
log('info', 'retention expired; record evicted', {
|
|
844
|
+
hash: record.hash,
|
|
845
|
+
terminalAtBlockNumber: record.status.terminalAtBlockNumber,
|
|
846
|
+
});
|
|
766
847
|
}
|
|
767
848
|
// Receipt-poll-fallback: dispatch non-blocking per-record receipt
|
|
768
849
|
// fetches. These run AFTER the synchronous block-observation loop
|
|
@@ -1050,13 +1131,27 @@ export const createTxTracker = (options) => {
|
|
|
1050
1131
|
}
|
|
1051
1132
|
if (opts.durable) {
|
|
1052
1133
|
record.hasDurableSub = true;
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1134
|
+
// Dedup: an explicit subscriptionId matches by id; without one,
|
|
1135
|
+
// auto-dedup by selector (only `kind: 'hash'` here since this
|
|
1136
|
+
// is the per-hash subscribe path). Pre-v0.15 every call pushed
|
|
1137
|
+
// unconditionally, so React component remounts and cross-process
|
|
1138
|
+
// restarts accumulated structurally-identical persisted entries —
|
|
1139
|
+
// identical work, fan-out duplication, store bloat.
|
|
1140
|
+
const desiredId = opts.subscriptionId;
|
|
1141
|
+
const existing = desiredId !== undefined
|
|
1142
|
+
? record.persisted.find((p) => p.id === desiredId)
|
|
1143
|
+
: record.persisted.find((p) => p.selector.kind === 'hash' && p.selector.hash === hash);
|
|
1144
|
+
if (!existing) {
|
|
1145
|
+
const subId = desiredId ?? `sub-${nextSubId++}`;
|
|
1146
|
+
record.persisted.push({
|
|
1147
|
+
id: subId,
|
|
1148
|
+
durable: true,
|
|
1149
|
+
selector: { kind: 'hash', hash },
|
|
1150
|
+
});
|
|
1151
|
+
void store
|
|
1152
|
+
.put(toRecord(record))
|
|
1153
|
+
.catch((err) => onError?.('store.put', err));
|
|
1154
|
+
}
|
|
1060
1155
|
}
|
|
1061
1156
|
const unsub = record.subs.subscribe(cb);
|
|
1062
1157
|
if (opts.emitInitial !== false) {
|
|
@@ -1266,12 +1361,21 @@ export const createTxTracker = (options) => {
|
|
|
1266
1361
|
onError?.('store.listDurable', err);
|
|
1267
1362
|
return;
|
|
1268
1363
|
}
|
|
1364
|
+
log('info', 'rehydrating durable records', {
|
|
1365
|
+
count: durableRecords.length,
|
|
1366
|
+
});
|
|
1269
1367
|
for (const persisted of durableRecords) {
|
|
1270
1368
|
// Skip if a fresh subscribe under the same hash already
|
|
1271
1369
|
// re-created the record between start() and rehydration
|
|
1272
1370
|
// resolving.
|
|
1273
1371
|
if (tracked.has(persisted.hash))
|
|
1274
1372
|
continue;
|
|
1373
|
+
// Dedup persisted subscriptions read back from the store. Pre-v0.15
|
|
1374
|
+
// stores accumulated structurally-identical entries from repeated
|
|
1375
|
+
// subscribes. Self-healing migration: if dedup shrank the list,
|
|
1376
|
+
// immediately re-persist the cleaned record so the store reflects
|
|
1377
|
+
// the migrated state even when no subsequent emit/subscribe occurs.
|
|
1378
|
+
const dedupedSubs = dedupPersistedSubscriptions(persisted.subscriptions);
|
|
1275
1379
|
const record = {
|
|
1276
1380
|
hash: persisted.hash,
|
|
1277
1381
|
status: persisted.status,
|
|
@@ -1281,7 +1385,7 @@ export const createTxTracker = (options) => {
|
|
|
1281
1385
|
unseenThresholdBlocks,
|
|
1282
1386
|
lostSignalPolicy: null,
|
|
1283
1387
|
hasDurableSub: true,
|
|
1284
|
-
persisted:
|
|
1388
|
+
persisted: dedupedSubs,
|
|
1285
1389
|
withReceipts: false,
|
|
1286
1390
|
// Probes are closures and not serializable; durable records
|
|
1287
1391
|
// get no probe until a fresh subscribe attaches one. See spec
|
|
@@ -1292,6 +1396,16 @@ export const createTxTracker = (options) => {
|
|
|
1292
1396
|
statusPollTicksSince: 0,
|
|
1293
1397
|
};
|
|
1294
1398
|
tracked.set(persisted.hash, record);
|
|
1399
|
+
if (dedupedSubs.length !== persisted.subscriptions.length) {
|
|
1400
|
+
log('info', 'dedup migration: collapsed duplicate persisted entries', {
|
|
1401
|
+
hash: persisted.hash,
|
|
1402
|
+
before: persisted.subscriptions.length,
|
|
1403
|
+
after: dedupedSubs.length,
|
|
1404
|
+
});
|
|
1405
|
+
void store
|
|
1406
|
+
.put(toRecord(record))
|
|
1407
|
+
.catch((err) => onError?.('store.put', err));
|
|
1408
|
+
}
|
|
1295
1409
|
}
|
|
1296
1410
|
};
|
|
1297
1411
|
const start = () => {
|