@valve-tech/tx-tracker 0.7.0 → 0.8.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 +15 -0
- package/dist/events.d.ts +11 -1
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +1 -0
- package/dist/events.js.map +1 -1
- package/dist/group-events.d.ts +82 -0
- package/dist/group-events.d.ts.map +1 -0
- package/dist/group-events.js +47 -0
- package/dist/group-events.js.map +1 -0
- package/dist/group.d.ts +31 -0
- package/dist/group.d.ts.map +1 -0
- package/dist/group.js +196 -0
- package/dist/group.js.map +1 -0
- package/dist/index.d.ts +12 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/observations.d.ts +11 -1
- package/dist/observations.d.ts.map +1 -1
- package/dist/observations.js +5 -1
- package/dist/observations.js.map +1 -1
- package/dist/replace-transaction.d.ts +46 -0
- package/dist/replace-transaction.d.ts.map +1 -0
- package/dist/replace-transaction.js +47 -0
- package/dist/replace-transaction.js.map +1 -0
- package/dist/tracker.d.ts +50 -4
- package/dist/tracker.d.ts.map +1 -1
- package/dist/tracker.js +184 -3
- package/dist/tracker.js.map +1 -1
- package/dist/wait-for-pending.d.ts +41 -0
- package/dist/wait-for-pending.d.ts.map +1 -0
- package/dist/wait-for-pending.js +71 -0
- package/dist/wait-for-pending.js.map +1 -0
- package/dist/wait-for-transaction.d.ts +55 -0
- package/dist/wait-for-transaction.d.ts.map +1 -0
- package/dist/wait-for-transaction.js +72 -0
- package/dist/wait-for-transaction.js.map +1 -0
- package/dist/watch-transaction.d.ts +57 -0
- package/dist/watch-transaction.d.ts.map +1 -0
- package/dist/watch-transaction.js +76 -0
- package/dist/watch-transaction.js.map +1 -0
- package/package.json +2 -2
- package/skills/tx-tracker-integration/SKILL.md +30 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `waitForPending` — Promise that resolves when a tx hash is first
|
|
3
|
+
* observed in any mempool. Rejects with `WaitForPendingTimeoutError`
|
|
4
|
+
* if `timeoutBlocks` elapses without observation.
|
|
5
|
+
*
|
|
6
|
+
* Surfaces the "submitted but never arrived" failure mode explicitly:
|
|
7
|
+
* when an RPC accepts a transaction but the tx never appears in any
|
|
8
|
+
* observed mempool, this helper times out rather than hanging.
|
|
9
|
+
*
|
|
10
|
+
* Internally constructs a private ChainSource + TxTracker (or accepts
|
|
11
|
+
* a `_sourceOverride` for tests) and tears them down before settling.
|
|
12
|
+
*/
|
|
13
|
+
import type { PublicClient } from 'viem';
|
|
14
|
+
import type { ChainSource } from '@valve-tech/chain-source';
|
|
15
|
+
import type { Hash, TxEventSeenInMempool } from './events.js';
|
|
16
|
+
export declare class WaitForPendingTimeoutError extends Error {
|
|
17
|
+
readonly hash: Hash;
|
|
18
|
+
readonly observedBlocks: number;
|
|
19
|
+
constructor(hash: Hash, observedBlocks: number);
|
|
20
|
+
}
|
|
21
|
+
export interface WaitForPendingOptions {
|
|
22
|
+
client: PublicClient;
|
|
23
|
+
hash: Hash;
|
|
24
|
+
/**
|
|
25
|
+
* Reject with WaitForPendingTimeoutError if the hash isn't observed
|
|
26
|
+
* in any mempool within this many block ticks. Default 12.
|
|
27
|
+
*/
|
|
28
|
+
timeoutBlocks?: number;
|
|
29
|
+
pollIntervalMs?: number;
|
|
30
|
+
onError?: (method: string, err: unknown) => void;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* @internal
|
|
34
|
+
* Test-injection seam — same shape as the other helpers' seams.
|
|
35
|
+
* Not re-exported from index.ts.
|
|
36
|
+
*/
|
|
37
|
+
export interface WaitForPendingInternalOptions extends WaitForPendingOptions {
|
|
38
|
+
_sourceOverride?: ChainSource;
|
|
39
|
+
}
|
|
40
|
+
export declare const waitForPending: (options: WaitForPendingOptions) => Promise<TxEventSeenInMempool>;
|
|
41
|
+
//# sourceMappingURL=wait-for-pending.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wait-for-pending.d.ts","sourceRoot":"","sources":["../src/wait-for-pending.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,MAAM,CAAA;AACxC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;AAG3D,OAAO,KAAK,EAAE,IAAI,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAG7D,qBAAa,0BAA2B,SAAQ,KAAK;IACnD,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAA;IACnB,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAA;gBACnB,IAAI,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM;CAQ/C;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,YAAY,CAAA;IACpB,IAAI,EAAE,IAAI,CAAA;IACV;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,KAAK,IAAI,CAAA;CACjD;AAED;;;;GAIG;AACH,MAAM,WAAW,6BAA8B,SAAQ,qBAAqB;IAC1E,eAAe,CAAC,EAAE,WAAW,CAAA;CAC9B;AAED,eAAO,MAAM,cAAc,GACzB,SAAS,qBAAqB,KAC7B,OAAO,CAAC,oBAAoB,CA0D9B,CAAA"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `waitForPending` — Promise that resolves when a tx hash is first
|
|
3
|
+
* observed in any mempool. Rejects with `WaitForPendingTimeoutError`
|
|
4
|
+
* if `timeoutBlocks` elapses without observation.
|
|
5
|
+
*
|
|
6
|
+
* Surfaces the "submitted but never arrived" failure mode explicitly:
|
|
7
|
+
* when an RPC accepts a transaction but the tx never appears in any
|
|
8
|
+
* observed mempool, this helper times out rather than hanging.
|
|
9
|
+
*
|
|
10
|
+
* Internally constructs a private ChainSource + TxTracker (or accepts
|
|
11
|
+
* a `_sourceOverride` for tests) and tears them down before settling.
|
|
12
|
+
*/
|
|
13
|
+
import { createChainSource } from '@valve-tech/chain-source';
|
|
14
|
+
import { createTxTracker } from './tracker.js';
|
|
15
|
+
export class WaitForPendingTimeoutError extends Error {
|
|
16
|
+
constructor(hash, observedBlocks) {
|
|
17
|
+
super(`waitForPending: hash ${hash} not observed in any mempool after ${observedBlocks} block(s)`);
|
|
18
|
+
this.name = 'WaitForPendingTimeoutError';
|
|
19
|
+
this.hash = hash;
|
|
20
|
+
this.observedBlocks = observedBlocks;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export const waitForPending = (options) => {
|
|
24
|
+
const internalOptions = options;
|
|
25
|
+
const timeoutBlocks = options.timeoutBlocks ?? 12;
|
|
26
|
+
return new Promise((resolve, reject) => {
|
|
27
|
+
const source = internalOptions._sourceOverride ??
|
|
28
|
+
createChainSource({
|
|
29
|
+
client: options.client,
|
|
30
|
+
pollIntervalMs: options.pollIntervalMs,
|
|
31
|
+
onError: options.onError,
|
|
32
|
+
});
|
|
33
|
+
const tracker = createTxTracker({
|
|
34
|
+
source,
|
|
35
|
+
chainId: 0,
|
|
36
|
+
onError: options.onError,
|
|
37
|
+
});
|
|
38
|
+
source.start();
|
|
39
|
+
tracker.start();
|
|
40
|
+
let teardownSubscribe = null;
|
|
41
|
+
let teardownBlocks = null;
|
|
42
|
+
let observedBlocks = 0;
|
|
43
|
+
// No `settled` flag: tracker.stop() / source.stop() / the unsub handles
|
|
44
|
+
// are all idempotent, and Promise resolve/reject are no-ops on second
|
|
45
|
+
// call. The first finish() call detaches both callbacks, so subsequent
|
|
46
|
+
// events for this hash don't reach them — no double-call path remains.
|
|
47
|
+
const finish = (action) => {
|
|
48
|
+
teardownSubscribe?.();
|
|
49
|
+
teardownBlocks?.();
|
|
50
|
+
tracker.stop();
|
|
51
|
+
source.stop();
|
|
52
|
+
action();
|
|
53
|
+
};
|
|
54
|
+
teardownSubscribe = tracker.subscribe(options.hash, (event) => {
|
|
55
|
+
// Filter for the only event kind that resolves this helper. The
|
|
56
|
+
// synthetic `stopped` event fired during teardownSubscribe also
|
|
57
|
+
// flows through here and falls through the kind check.
|
|
58
|
+
if (event.kind === 'seen-in-mempool') {
|
|
59
|
+
finish(() => resolve(event));
|
|
60
|
+
}
|
|
61
|
+
}, { emitInitial: false });
|
|
62
|
+
teardownBlocks = source.subscribeBlocks(() => {
|
|
63
|
+
observedBlocks++;
|
|
64
|
+
if (observedBlocks >= timeoutBlocks) {
|
|
65
|
+
const err = new WaitForPendingTimeoutError(options.hash, observedBlocks);
|
|
66
|
+
finish(() => reject(err));
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
};
|
|
71
|
+
//# sourceMappingURL=wait-for-pending.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wait-for-pending.js","sourceRoot":"","sources":["../src/wait-for-pending.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAA;AAG5D,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAE9C,MAAM,OAAO,0BAA2B,SAAQ,KAAK;IAGnD,YAAY,IAAU,EAAE,cAAsB;QAC5C,KAAK,CACH,wBAAwB,IAAI,sCAAsC,cAAc,WAAW,CAC5F,CAAA;QACD,IAAI,CAAC,IAAI,GAAG,4BAA4B,CAAA;QACxC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;QAChB,IAAI,CAAC,cAAc,GAAG,cAAc,CAAA;IACtC,CAAC;CACF;AAuBD,MAAM,CAAC,MAAM,cAAc,GAAG,CAC5B,OAA8B,EACC,EAAE;IACjC,MAAM,eAAe,GAAG,OAAwC,CAAA;IAChE,MAAM,aAAa,GAAG,OAAO,CAAC,aAAa,IAAI,EAAE,CAAA;IAEjD,OAAO,IAAI,OAAO,CAAuB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QAC3D,MAAM,MAAM,GACV,eAAe,CAAC,eAAe;YAC/B,iBAAiB,CAAC;gBAChB,MAAM,EAAE,OAAO,CAAC,MAAM;gBACtB,cAAc,EAAE,OAAO,CAAC,cAAc;gBACtC,OAAO,EAAE,OAAO,CAAC,OAAO;aACzB,CAAC,CAAA;QACJ,MAAM,OAAO,GAAG,eAAe,CAAC;YAC9B,MAAM;YACN,OAAO,EAAE,CAAC;YACV,OAAO,EAAE,OAAO,CAAC,OAAO;SACzB,CAAC,CAAA;QAEF,MAAM,CAAC,KAAK,EAAE,CAAA;QACd,OAAO,CAAC,KAAK,EAAE,CAAA;QAEf,IAAI,iBAAiB,GAAwB,IAAI,CAAA;QACjD,IAAI,cAAc,GAAwB,IAAI,CAAA;QAC9C,IAAI,cAAc,GAAG,CAAC,CAAA;QAEtB,wEAAwE;QACxE,sEAAsE;QACtE,uEAAuE;QACvE,uEAAuE;QACvE,MAAM,MAAM,GAAG,CAAC,MAAkB,EAAQ,EAAE;YAC1C,iBAAiB,EAAE,EAAE,CAAA;YACrB,cAAc,EAAE,EAAE,CAAA;YAClB,OAAO,CAAC,IAAI,EAAE,CAAA;YACd,MAAM,CAAC,IAAI,EAAE,CAAA;YACb,MAAM,EAAE,CAAA;QACV,CAAC,CAAA;QAED,iBAAiB,GAAG,OAAO,CAAC,SAAS,CACnC,OAAO,CAAC,IAAI,EACZ,CAAC,KAAK,EAAE,EAAE;YACR,gEAAgE;YAChE,gEAAgE;YAChE,uDAAuD;YACvD,IAAI,KAAK,CAAC,IAAI,KAAK,iBAAiB,EAAE,CAAC;gBACrC,MAAM,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAA;YAC9B,CAAC;QACH,CAAC,EACD,EAAE,WAAW,EAAE,KAAK,EAAE,CACvB,CAAA;QAED,cAAc,GAAG,MAAM,CAAC,eAAe,CAAC,GAAG,EAAE;YAC3C,cAAc,EAAE,CAAA;YAChB,IAAI,cAAc,IAAI,aAAa,EAAE,CAAC;gBACpC,MAAM,GAAG,GAAG,IAAI,0BAA0B,CAAC,OAAO,CAAC,IAAI,EAAE,cAAc,CAAC,CAAA;gBACxE,MAAM,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAA;YAC3B,CAAC;QACH,CAAC,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;AACJ,CAAC,CAAA"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `waitForTransaction` — Promise variant of `watchTransaction`. Use
|
|
3
|
+
* when you'd rather `await` the outcome than register callbacks.
|
|
4
|
+
*
|
|
5
|
+
* Resolves with a discriminated union: { status: 'mined' | 'dropped'
|
|
6
|
+
* | 'replaced' | 'failed', ... }. Rejects only on tracker/source
|
|
7
|
+
* errors — transaction outcomes are part of the resolved value, not
|
|
8
|
+
* exceptions.
|
|
9
|
+
*
|
|
10
|
+
* Internally constructs a private ChainSource + TxTracker and tears
|
|
11
|
+
* them down before resolving.
|
|
12
|
+
*/
|
|
13
|
+
import type { PublicClient } from 'viem';
|
|
14
|
+
import type { ChainSource, TransactionReceipt } from '@valve-tech/chain-source';
|
|
15
|
+
import type { Hash, TxEventReplacedBy, TxEventSeenInBlock } from './events.js';
|
|
16
|
+
export type WaitForTransactionOutcome = {
|
|
17
|
+
status: 'mined';
|
|
18
|
+
event: TxEventSeenInBlock;
|
|
19
|
+
} | {
|
|
20
|
+
status: 'dropped';
|
|
21
|
+
reason: 'unseen-for-N-blocks';
|
|
22
|
+
} | {
|
|
23
|
+
status: 'replaced';
|
|
24
|
+
replacementHash: Hash;
|
|
25
|
+
event: TxEventReplacedBy;
|
|
26
|
+
} | {
|
|
27
|
+
status: 'failed';
|
|
28
|
+
event: TxEventSeenInBlock;
|
|
29
|
+
receipt: TransactionReceipt;
|
|
30
|
+
};
|
|
31
|
+
export interface WaitForTransactionOptions {
|
|
32
|
+
client: PublicClient;
|
|
33
|
+
hash: Hash;
|
|
34
|
+
/** Required confirmations before 'mined' resolves. Default 1. */
|
|
35
|
+
confirmations?: number;
|
|
36
|
+
/** Blocks of "no observation" before 'dropped' resolves. Default 12. */
|
|
37
|
+
staleAfterBlocks?: number;
|
|
38
|
+
/**
|
|
39
|
+
* If true, fetches the receipt at inclusion and resolves with
|
|
40
|
+
* 'failed' when receipt.status === '0x0'. Adds one RPC.
|
|
41
|
+
*/
|
|
42
|
+
withReceipts?: boolean;
|
|
43
|
+
pollIntervalMs?: number;
|
|
44
|
+
onError?: (method: string, err: unknown) => void;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* @internal
|
|
48
|
+
* Test-injection seam — same shape as watchTransaction's seam.
|
|
49
|
+
* Not re-exported from index.ts.
|
|
50
|
+
*/
|
|
51
|
+
export interface WaitForTransactionInternalOptions extends WaitForTransactionOptions {
|
|
52
|
+
_sourceOverride?: ChainSource;
|
|
53
|
+
}
|
|
54
|
+
export declare const waitForTransaction: (options: WaitForTransactionOptions) => Promise<WaitForTransactionOutcome>;
|
|
55
|
+
//# sourceMappingURL=wait-for-transaction.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wait-for-transaction.d.ts","sourceRoot":"","sources":["../src/wait-for-transaction.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,MAAM,CAAA;AACxC,OAAO,KAAK,EACV,WAAW,EACX,kBAAkB,EACnB,MAAM,0BAA0B,CAAA;AAGjC,OAAO,KAAK,EACV,IAAI,EACJ,iBAAiB,EACjB,kBAAkB,EACnB,MAAM,aAAa,CAAA;AAGpB,MAAM,MAAM,yBAAyB,GACjC;IAAE,MAAM,EAAE,OAAO,CAAC;IAAK,KAAK,EAAE,kBAAkB,CAAA;CAAE,GAClD;IAAE,MAAM,EAAE,SAAS,CAAC;IAAG,MAAM,EAAE,qBAAqB,CAAA;CAAE,GACtD;IAAE,MAAM,EAAE,UAAU,CAAC;IAAE,eAAe,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,iBAAiB,CAAA;CAAE,GACxE;IAAE,MAAM,EAAE,QAAQ,CAAC;IAAI,KAAK,EAAE,kBAAkB,CAAC;IAAC,OAAO,EAAE,kBAAkB,CAAA;CAAE,CAAA;AAEnF,MAAM,WAAW,yBAAyB;IACxC,MAAM,EAAE,YAAY,CAAA;IACpB,IAAI,EAAE,IAAI,CAAA;IACV,iEAAiE;IACjE,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,wEAAwE;IACxE,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,KAAK,IAAI,CAAA;CACjD;AAED;;;;GAIG;AACH,MAAM,WAAW,iCAAkC,SAAQ,yBAAyB;IAClF,eAAe,CAAC,EAAE,WAAW,CAAA;CAC9B;AAED,eAAO,MAAM,kBAAkB,GAC7B,SAAS,yBAAyB,KACjC,OAAO,CAAC,yBAAyB,CAkEnC,CAAA"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `waitForTransaction` — Promise variant of `watchTransaction`. Use
|
|
3
|
+
* when you'd rather `await` the outcome than register callbacks.
|
|
4
|
+
*
|
|
5
|
+
* Resolves with a discriminated union: { status: 'mined' | 'dropped'
|
|
6
|
+
* | 'replaced' | 'failed', ... }. Rejects only on tracker/source
|
|
7
|
+
* errors — transaction outcomes are part of the resolved value, not
|
|
8
|
+
* exceptions.
|
|
9
|
+
*
|
|
10
|
+
* Internally constructs a private ChainSource + TxTracker and tears
|
|
11
|
+
* them down before resolving.
|
|
12
|
+
*/
|
|
13
|
+
import { createChainSource } from '@valve-tech/chain-source';
|
|
14
|
+
import { createTxTracker } from './tracker.js';
|
|
15
|
+
export const waitForTransaction = (options) => {
|
|
16
|
+
const internalOptions = options;
|
|
17
|
+
const confirmations = options.confirmations ?? 1;
|
|
18
|
+
const staleAfterBlocks = options.staleAfterBlocks ?? 12;
|
|
19
|
+
return new Promise((resolve) => {
|
|
20
|
+
const source = internalOptions._sourceOverride ??
|
|
21
|
+
createChainSource({
|
|
22
|
+
client: options.client,
|
|
23
|
+
pollIntervalMs: options.pollIntervalMs,
|
|
24
|
+
onError: options.onError,
|
|
25
|
+
});
|
|
26
|
+
const tracker = createTxTracker({
|
|
27
|
+
source,
|
|
28
|
+
chainId: 0,
|
|
29
|
+
onError: options.onError,
|
|
30
|
+
});
|
|
31
|
+
source.start();
|
|
32
|
+
tracker.start();
|
|
33
|
+
let teardownSubscribe = null;
|
|
34
|
+
// No `settled` flag: tracker.stop() / source.stop() / teardownSubscribe
|
|
35
|
+
// are all idempotent, and Promise resolve is a no-op on second call.
|
|
36
|
+
// The first finish() detaches the subscription, so subsequent events
|
|
37
|
+
// for this hash don't reach the callback — no double-call path remains.
|
|
38
|
+
const finish = (outcome) => {
|
|
39
|
+
teardownSubscribe?.();
|
|
40
|
+
tracker.stop();
|
|
41
|
+
source.stop();
|
|
42
|
+
resolve(outcome);
|
|
43
|
+
};
|
|
44
|
+
teardownSubscribe = tracker.subscribe(options.hash, (event) => {
|
|
45
|
+
if (event.kind === 'seen-in-block' && event.confirmations >= confirmations) {
|
|
46
|
+
if (options.withReceipts && event.receipt && event.receipt.status === '0x0') {
|
|
47
|
+
finish({ status: 'failed', event, receipt: event.receipt });
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
finish({ status: 'mined', event });
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
if (event.kind === 'unseen-for-N-blocks' && event.blocks >= staleAfterBlocks) {
|
|
54
|
+
finish({ status: 'dropped', reason: 'unseen-for-N-blocks' });
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
if (event.kind === 'replaced-by') {
|
|
58
|
+
finish({
|
|
59
|
+
status: 'replaced',
|
|
60
|
+
replacementHash: event.replacementHash,
|
|
61
|
+
event,
|
|
62
|
+
});
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
}, {
|
|
66
|
+
emitInitial: false,
|
|
67
|
+
unseenThresholdBlocks: staleAfterBlocks,
|
|
68
|
+
...(options.withReceipts ? { withReceipts: true } : {}),
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
};
|
|
72
|
+
//# sourceMappingURL=wait-for-transaction.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wait-for-transaction.js","sourceRoot":"","sources":["../src/wait-for-transaction.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAOH,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAA;AAO5D,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAiC9C,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAChC,OAAkC,EACE,EAAE;IACtC,MAAM,eAAe,GAAG,OAA4C,CAAA;IACpE,MAAM,aAAa,GAAG,OAAO,CAAC,aAAa,IAAI,CAAC,CAAA;IAChD,MAAM,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,IAAI,EAAE,CAAA;IAEvD,OAAO,IAAI,OAAO,CAA4B,CAAC,OAAO,EAAE,EAAE;QACxD,MAAM,MAAM,GACV,eAAe,CAAC,eAAe;YAC/B,iBAAiB,CAAC;gBAChB,MAAM,EAAE,OAAO,CAAC,MAAM;gBACtB,cAAc,EAAE,OAAO,CAAC,cAAc;gBACtC,OAAO,EAAE,OAAO,CAAC,OAAO;aACzB,CAAC,CAAA;QACJ,MAAM,OAAO,GAAG,eAAe,CAAC;YAC9B,MAAM;YACN,OAAO,EAAE,CAAC;YACV,OAAO,EAAE,OAAO,CAAC,OAAO;SACzB,CAAC,CAAA;QAEF,MAAM,CAAC,KAAK,EAAE,CAAA;QACd,OAAO,CAAC,KAAK,EAAE,CAAA;QAEf,IAAI,iBAAiB,GAAwB,IAAI,CAAA;QAEjD,wEAAwE;QACxE,qEAAqE;QACrE,qEAAqE;QACrE,wEAAwE;QACxE,MAAM,MAAM,GAAG,CAAC,OAAkC,EAAQ,EAAE;YAC1D,iBAAiB,EAAE,EAAE,CAAA;YACrB,OAAO,CAAC,IAAI,EAAE,CAAA;YACd,MAAM,CAAC,IAAI,EAAE,CAAA;YACb,OAAO,CAAC,OAAO,CAAC,CAAA;QAClB,CAAC,CAAA;QAED,iBAAiB,GAAG,OAAO,CAAC,SAAS,CACnC,OAAO,CAAC,IAAI,EACZ,CAAC,KAAK,EAAE,EAAE;YACR,IAAI,KAAK,CAAC,IAAI,KAAK,eAAe,IAAI,KAAK,CAAC,aAAa,IAAI,aAAa,EAAE,CAAC;gBAC3E,IAAI,OAAO,CAAC,YAAY,IAAI,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,KAAK,KAAK,EAAE,CAAC;oBAC5E,MAAM,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC,CAAA;oBAC3D,OAAM;gBACR,CAAC;gBACD,MAAM,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAA;gBAClC,OAAM;YACR,CAAC;YACD,IAAI,KAAK,CAAC,IAAI,KAAK,qBAAqB,IAAI,KAAK,CAAC,MAAM,IAAI,gBAAgB,EAAE,CAAC;gBAC7E,MAAM,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,qBAAqB,EAAE,CAAC,CAAA;gBAC5D,OAAM;YACR,CAAC;YACD,IAAI,KAAK,CAAC,IAAI,KAAK,aAAa,EAAE,CAAC;gBACjC,MAAM,CAAC;oBACL,MAAM,EAAE,UAAU;oBAClB,eAAe,EAAE,KAAK,CAAC,eAAe;oBACtC,KAAK;iBACN,CAAC,CAAA;gBACF,OAAM;YACR,CAAC;QACH,CAAC,EACD;YACE,WAAW,EAAE,KAAK;YAClB,qBAAqB,EAAE,gBAAgB;YACvC,GAAG,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACxD,CACF,CAAA;IACH,CAAC,CAAC,CAAA;AACJ,CAAC,CAAA"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `watchTransaction` — one-shot convenience over createChainSource +
|
|
3
|
+
* createTxTracker. Provex upstream item 4 (memory:
|
|
4
|
+
* upstream-candidates.md#L43). Use this when you don't need the full
|
|
5
|
+
* tracker API — just "tell me when this hash mines or drops."
|
|
6
|
+
*
|
|
7
|
+
* Internally constructs a private ChainSource and TxTracker, sets up
|
|
8
|
+
* a single-hash subscription, and tears down both source + tracker on
|
|
9
|
+
* terminal state or explicit stop.
|
|
10
|
+
*/
|
|
11
|
+
import type { PublicClient } from 'viem';
|
|
12
|
+
import { type ChainSource } from '@valve-tech/chain-source';
|
|
13
|
+
import type { Hash, TxEventSeenInBlock } from './events.js';
|
|
14
|
+
export interface WatchTransactionOptions {
|
|
15
|
+
client: PublicClient;
|
|
16
|
+
hash: Hash;
|
|
17
|
+
/** Required confirmations before onMined fires. Default 1. */
|
|
18
|
+
confirmations?: number;
|
|
19
|
+
/** Blocks of "no observation" before onDropped fires. Default 12. */
|
|
20
|
+
staleAfterBlocks?: number;
|
|
21
|
+
/** Pass through to the internal ChainSource. */
|
|
22
|
+
pollIntervalMs?: number;
|
|
23
|
+
onMined?: (event: TxEventSeenInBlock) => void;
|
|
24
|
+
onDropped?: () => void;
|
|
25
|
+
onError?: (method: string, err: unknown) => void;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Extended options for internal use only — not part of the public API.
|
|
29
|
+
* The `_sourceOverride` seam allows tests to inject a pre-built
|
|
30
|
+
* `ChainSource` (driven by `emitBlock`/`emitMempool`) in place of the
|
|
31
|
+
* default `createChainSource(client)` path. Production callers never
|
|
32
|
+
* need this.
|
|
33
|
+
*
|
|
34
|
+
* @internal
|
|
35
|
+
*/
|
|
36
|
+
export interface WatchTransactionInternalOptions extends WatchTransactionOptions {
|
|
37
|
+
/** @internal — test injection seam; do not use in production code. */
|
|
38
|
+
_sourceOverride?: ChainSource;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Watch a single tx hash. Returns an unsubscribe function — calling
|
|
42
|
+
* it before terminal cancels the watch and tears down the internal
|
|
43
|
+
* source/tracker.
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* const stop = watchTransaction({
|
|
47
|
+
* client,
|
|
48
|
+
* hash: '0xabc...',
|
|
49
|
+
* confirmations: 3,
|
|
50
|
+
* onMined: (event) => console.log('mined at', event.blockNumber),
|
|
51
|
+
* onDropped: () => console.log('dropped'),
|
|
52
|
+
* })
|
|
53
|
+
* // ... later, before terminal
|
|
54
|
+
* stop()
|
|
55
|
+
*/
|
|
56
|
+
export declare const watchTransaction: (options: WatchTransactionOptions | WatchTransactionInternalOptions) => (() => void);
|
|
57
|
+
//# sourceMappingURL=watch-transaction.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watch-transaction.d.ts","sourceRoot":"","sources":["../src/watch-transaction.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,MAAM,CAAA;AAExC,OAAO,EAEL,KAAK,WAAW,EACjB,MAAM,0BAA0B,CAAA;AAEjC,OAAO,KAAK,EACV,IAAI,EACJ,kBAAkB,EACnB,MAAM,aAAa,CAAA;AAGpB,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,YAAY,CAAA;IACpB,IAAI,EAAE,IAAI,CAAA;IACV,8DAA8D;IAC9D,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,qEAAqE;IACrE,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,gDAAgD;IAChD,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,kBAAkB,KAAK,IAAI,CAAA;IAC7C,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;IACtB,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,KAAK,IAAI,CAAA;CACjD;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,+BAAgC,SAAQ,uBAAuB;IAC9E,sEAAsE;IACtE,eAAe,CAAC,EAAE,WAAW,CAAA;CAC9B;AAED;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,gBAAgB,GAC3B,SAAS,uBAAuB,GAAG,+BAA+B,KACjE,CAAC,MAAM,IAAI,CAsDb,CAAA"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `watchTransaction` — one-shot convenience over createChainSource +
|
|
3
|
+
* createTxTracker. Provex upstream item 4 (memory:
|
|
4
|
+
* upstream-candidates.md#L43). Use this when you don't need the full
|
|
5
|
+
* tracker API — just "tell me when this hash mines or drops."
|
|
6
|
+
*
|
|
7
|
+
* Internally constructs a private ChainSource and TxTracker, sets up
|
|
8
|
+
* a single-hash subscription, and tears down both source + tracker on
|
|
9
|
+
* terminal state or explicit stop.
|
|
10
|
+
*/
|
|
11
|
+
import { createChainSource, } from '@valve-tech/chain-source';
|
|
12
|
+
import { createTxTracker } from './tracker.js';
|
|
13
|
+
/**
|
|
14
|
+
* Watch a single tx hash. Returns an unsubscribe function — calling
|
|
15
|
+
* it before terminal cancels the watch and tears down the internal
|
|
16
|
+
* source/tracker.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* const stop = watchTransaction({
|
|
20
|
+
* client,
|
|
21
|
+
* hash: '0xabc...',
|
|
22
|
+
* confirmations: 3,
|
|
23
|
+
* onMined: (event) => console.log('mined at', event.blockNumber),
|
|
24
|
+
* onDropped: () => console.log('dropped'),
|
|
25
|
+
* })
|
|
26
|
+
* // ... later, before terminal
|
|
27
|
+
* stop()
|
|
28
|
+
*/
|
|
29
|
+
export const watchTransaction = (options) => {
|
|
30
|
+
const confirmations = options.confirmations ?? 1;
|
|
31
|
+
const staleAfterBlocks = options.staleAfterBlocks ?? 12;
|
|
32
|
+
const internal = options;
|
|
33
|
+
const source = internal._sourceOverride ?? createChainSource({
|
|
34
|
+
client: options.client,
|
|
35
|
+
pollIntervalMs: options.pollIntervalMs,
|
|
36
|
+
onError: options.onError,
|
|
37
|
+
});
|
|
38
|
+
const tracker = createTxTracker({
|
|
39
|
+
source,
|
|
40
|
+
chainId: 0, // chainId echoed in events but not load-bearing for one-shot
|
|
41
|
+
onError: options.onError,
|
|
42
|
+
});
|
|
43
|
+
source.start();
|
|
44
|
+
tracker.start();
|
|
45
|
+
let teardown = null;
|
|
46
|
+
let done = false;
|
|
47
|
+
const finish = () => {
|
|
48
|
+
if (done)
|
|
49
|
+
return;
|
|
50
|
+
done = true;
|
|
51
|
+
teardown?.();
|
|
52
|
+
teardown = null;
|
|
53
|
+
tracker.stop();
|
|
54
|
+
source.stop();
|
|
55
|
+
};
|
|
56
|
+
const unsub = tracker.subscribe(options.hash, (event) => {
|
|
57
|
+
if (done)
|
|
58
|
+
return;
|
|
59
|
+
if (event.kind === 'seen-in-block' && event.confirmations >= confirmations) {
|
|
60
|
+
options.onMined?.(event);
|
|
61
|
+
finish();
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
if (event.kind === 'unseen-for-N-blocks' && event.blocks >= staleAfterBlocks) {
|
|
65
|
+
options.onDropped?.();
|
|
66
|
+
finish();
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
}, {
|
|
70
|
+
emitInitial: false,
|
|
71
|
+
unseenThresholdBlocks: staleAfterBlocks,
|
|
72
|
+
});
|
|
73
|
+
teardown = unsub;
|
|
74
|
+
return () => finish();
|
|
75
|
+
};
|
|
76
|
+
//# sourceMappingURL=watch-transaction.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watch-transaction.js","sourceRoot":"","sources":["../src/watch-transaction.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,EACL,iBAAiB,GAElB,MAAM,0BAA0B,CAAA;AAMjC,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AA8B9C;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAC9B,OAAkE,EACpD,EAAE;IAChB,MAAM,aAAa,GAAG,OAAO,CAAC,aAAa,IAAI,CAAC,CAAA;IAChD,MAAM,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,IAAI,EAAE,CAAA;IAEvD,MAAM,QAAQ,GAAG,OAA0C,CAAA;IAC3D,MAAM,MAAM,GAAgB,QAAQ,CAAC,eAAe,IAAI,iBAAiB,CAAC;QACxE,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,cAAc,EAAE,OAAO,CAAC,cAAc;QACtC,OAAO,EAAE,OAAO,CAAC,OAAO;KACzB,CAAC,CAAA;IACF,MAAM,OAAO,GAAG,eAAe,CAAC;QAC9B,MAAM;QACN,OAAO,EAAE,CAAC,EAAE,6DAA6D;QACzE,OAAO,EAAE,OAAO,CAAC,OAAO;KACzB,CAAC,CAAA;IAEF,MAAM,CAAC,KAAK,EAAE,CAAA;IACd,OAAO,CAAC,KAAK,EAAE,CAAA;IAEf,IAAI,QAAQ,GAAwB,IAAI,CAAA;IACxC,IAAI,IAAI,GAAG,KAAK,CAAA;IAEhB,MAAM,MAAM,GAAG,GAAS,EAAE;QACxB,IAAI,IAAI;YAAE,OAAM;QAChB,IAAI,GAAG,IAAI,CAAA;QACX,QAAQ,EAAE,EAAE,CAAA;QACZ,QAAQ,GAAG,IAAI,CAAA;QACf,OAAO,CAAC,IAAI,EAAE,CAAA;QACd,MAAM,CAAC,IAAI,EAAE,CAAA;IACf,CAAC,CAAA;IAED,MAAM,KAAK,GAAG,OAAO,CAAC,SAAS,CAC7B,OAAO,CAAC,IAAI,EACZ,CAAC,KAAK,EAAE,EAAE;QACR,IAAI,IAAI;YAAE,OAAM;QAChB,IAAI,KAAK,CAAC,IAAI,KAAK,eAAe,IAAI,KAAK,CAAC,aAAa,IAAI,aAAa,EAAE,CAAC;YAC3E,OAAO,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,CAAA;YACxB,MAAM,EAAE,CAAA;YACR,OAAM;QACR,CAAC;QACD,IAAI,KAAK,CAAC,IAAI,KAAK,qBAAqB,IAAI,KAAK,CAAC,MAAM,IAAI,gBAAgB,EAAE,CAAC;YAC7E,OAAO,CAAC,SAAS,EAAE,EAAE,CAAA;YACrB,MAAM,EAAE,CAAA;YACR,OAAM;QACR,CAAC;IACH,CAAC,EACD;QACE,WAAW,EAAE,KAAK;QAClB,qBAAqB,EAAE,gBAAgB;KACxC,CACF,CAAA;IACD,QAAQ,GAAG,KAAK,CAAA;IAEhB,OAAO,GAAG,EAAE,CAAC,MAAM,EAAE,CAAA;AACvB,CAAC,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@valve-tech/tx-tracker",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "Per-tx state machine for EVM chains: emits neutral observations (`seen-in-mempool`, `seen-in-block`, `replaced-by`, `vanished-from-block`, `unseen-for-N-blocks`, etc.) so wallet UIs, indexers, and relays can write their own interpretations on top. Three consumption shapes (callback, async iterator, snapshot) over one push-based core. Per-method capability detection — works on HTTP, WS, both, or neither. Part of the valve-tech/evm-toolkit synchronized release line — implementation lands in subsequent 0.3.x releases per docs/tx-tracker-spec.md.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://github.com/valve-tech/evm-toolkit/tree/main/packages/tx-tracker#readme",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"prepare": "yarn build"
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
|
-
"@valve-tech/chain-source": "^0.
|
|
52
|
+
"@valve-tech/chain-source": "^0.8.0"
|
|
53
53
|
},
|
|
54
54
|
"peerDependencies": {
|
|
55
55
|
"viem": "^2.0.0"
|
|
@@ -158,6 +158,36 @@ import { createGasOracle } from '@valve-tech/gas-oracle'
|
|
|
158
158
|
dependencies, and almost always `"@valve-tech/chain-source"` alongside
|
|
159
159
|
it (the tracker requires a source).
|
|
160
160
|
|
|
161
|
+
## Speed-up workflow (cross-package)
|
|
162
|
+
|
|
163
|
+
For callers tracking a tx via `@valve-tech/tx-tracker` who want to bump
|
|
164
|
+
it when it stalls or drops, pair with `@valve-tech/gas-oracle`'s
|
|
165
|
+
`recommendBumpTier` + `bumpForReplacement` helpers:
|
|
166
|
+
|
|
167
|
+
```ts
|
|
168
|
+
import { recommendBumpTier, bumpForReplacement } from '@valve-tech/gas-oracle'
|
|
169
|
+
|
|
170
|
+
// tx-tracker tells you the tx is stuck:
|
|
171
|
+
tracker.on('stuck', (stuck) => {
|
|
172
|
+
const tier = recommendBumpTier(
|
|
173
|
+
gasOracleState,
|
|
174
|
+
{ priorityTip: stuck.maxPriorityFeePerGas, identifier: { hash: stuck.hash } },
|
|
175
|
+
)
|
|
176
|
+
if (tier === null) return // Already paying above top tier — caller's call
|
|
177
|
+
|
|
178
|
+
const target = gasOracleState.tiers[tier]
|
|
179
|
+
const gas = bumpForReplacement(
|
|
180
|
+
{ maxFeePerGas: stuck.maxFeePerGas, maxPriorityFeePerGas: stuck.maxPriorityFeePerGas },
|
|
181
|
+
{ maxFeePerGas: target.maxFeePerGas, maxPriorityFeePerGas: target.maxPriorityFeePerGas },
|
|
182
|
+
)
|
|
183
|
+
walletClient.sendTransaction({ ...stuck, ...gas })
|
|
184
|
+
})
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Outpace correction (passing `identifier`) reads `gasOracleState.mempoolSamples`
|
|
188
|
+
to compute the tip needed to outpace the stuck tx in the live
|
|
189
|
+
distribution, on top of the EIP-1559 +10% protocol floor.
|
|
190
|
+
|
|
161
191
|
## Where to find more
|
|
162
192
|
|
|
163
193
|
- Full API + types: `node_modules/@valve-tech/tx-tracker/AGENTS.md`
|