@valve-tech/chain-source 0.5.0 → 0.6.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 +78 -0
- package/README.md +76 -18
- package/dist/capabilities.d.ts +42 -0
- package/dist/capabilities.d.ts.map +1 -0
- package/dist/capabilities.js +98 -0
- package/dist/capabilities.js.map +1 -0
- package/dist/index.d.ts +27 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +30 -1
- package/dist/index.js.map +1 -1
- package/dist/mempool.d.ts +37 -0
- package/dist/mempool.d.ts.map +1 -0
- package/dist/mempool.js +54 -0
- package/dist/mempool.js.map +1 -0
- package/dist/source.d.ts +128 -0
- package/dist/source.d.ts.map +1 -0
- package/dist/source.js +198 -0
- package/dist/source.js.map +1 -0
- package/dist/subscriptions.d.ts +42 -0
- package/dist/subscriptions.d.ts.map +1 -0
- package/dist/subscriptions.js +66 -0
- package/dist/subscriptions.js.map +1 -0
- package/dist/transport.d.ts +84 -0
- package/dist/transport.d.ts.map +1 -0
- package/dist/transport.js +106 -0
- package/dist/transport.js.map +1 -0
- package/dist/types.d.ts +161 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +22 -0
- package/dist/types.js.map +1 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,84 @@ this file.
|
|
|
6
6
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
7
7
|
and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
8
8
|
|
|
9
|
+
## [0.6.0] — 2026-05-05
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- **`createChainSource({ client, pollIntervalMs?, poll?, onError? })`** —
|
|
14
|
+
the canonical `ChainSource` primitive lands. Implements the
|
|
15
|
+
`subscribe / on-demand / capabilities / lifecycle` contract from
|
|
16
|
+
[`docs/tx-tracker-spec.md`](https://github.com/valve-tech/evm-toolkit/blob/main/docs/tx-tracker-spec.md)
|
|
17
|
+
§3.2.
|
|
18
|
+
- **`subscribeBlocks(cb)` / `subscribeMempool(cb)`** — first-class
|
|
19
|
+
multi-subscriber streams. One upstream poll cycle fans out to every
|
|
20
|
+
attached subscriber. Returns an idempotent unsubscribe handle.
|
|
21
|
+
- **On-demand RPCs:** `getBlock(tag)`, `getFeeHistory(blockCount, percentiles)`,
|
|
22
|
+
`getMempoolSnapshot()` (returns the normalized form), `getReceipt(hash)`,
|
|
23
|
+
`getTransaction(hash)`. Each follows the `safeRequest`-returns-null
|
|
24
|
+
posture — never throws across the boundary.
|
|
25
|
+
- **`probeCapabilities(client)`** + cached `capabilities()` accessor.
|
|
26
|
+
The probe runs eagerly at construction; `await source.ready()`
|
|
27
|
+
guarantees the cached snapshot is populated before reading.
|
|
28
|
+
Per-method discrimination (`newHeads` / `newPendingTransactions` /
|
|
29
|
+
`txpoolContent` / `receiptByHash`) honors the "no silent downgrade"
|
|
30
|
+
invariant from §2.2 of the spec.
|
|
31
|
+
- **`Subscriptions<E>`** — hand-rolled typed pub/sub primitive,
|
|
32
|
+
browser/mobile-safe (no Node `events` dependency). Per-subscriber
|
|
33
|
+
throws are swallowed so a single bad consumer cannot affect
|
|
34
|
+
delivery to the others; emit fans out to a snapshot of subscribers
|
|
35
|
+
taken at the start of the call so mid-emit registration changes
|
|
36
|
+
are deferred to the next emit.
|
|
37
|
+
- **Type re-exports** of `BlockResult`, `Capabilities`, `EventSource`,
|
|
38
|
+
`FeeHistoryResult`, `NormalizedMempool`, `PollOptions`, `RawTx`,
|
|
39
|
+
`TransactionReceipt`, `TxPoolContent`. These are the wire-format
|
|
40
|
+
contracts used by `@valve-tech/gas-oracle` and `@valve-tech/tx-tracker`
|
|
41
|
+
in their v0.3.x migrations.
|
|
42
|
+
- **Additive method on the spec'd interface: `pollOnce()`**. Drives one
|
|
43
|
+
cycle out-of-band; useful for serverless / manual-refresh flows and
|
|
44
|
+
for deterministic test setups that don't want to engage fake
|
|
45
|
+
timers.
|
|
46
|
+
|
|
47
|
+
### Changed
|
|
48
|
+
|
|
49
|
+
- **`subscribeBlocks` now dedups by `block.hash`.** Previously, every
|
|
50
|
+
successful tick fanned out to block subscribers regardless of
|
|
51
|
+
whether the head had advanced; on a static head that meant an emit
|
|
52
|
+
every `pollIntervalMs`. The stream now only emits when the observed
|
|
53
|
+
block hash differs from the last one delivered. Hash-based (not
|
|
54
|
+
number-based) so a same-height reorg surfaces as a fresh
|
|
55
|
+
observation. Dedup state resets on `stop()` — a paused-then-resumed
|
|
56
|
+
source emits a current snapshot to its consumers on first re-tick
|
|
57
|
+
rather than waiting for the next chain block. `subscribeMempool` is
|
|
58
|
+
intentionally not deduped — txs come and go between blocks even on
|
|
59
|
+
a static head, so every successful snapshot remains fresh data.
|
|
60
|
+
- **The poll cycle now head-probe-gates the full block fetch.** Each
|
|
61
|
+
tick runs a cheap `eth_blockNumber` probe in parallel with the
|
|
62
|
+
mempool fetch; only when the probe shows the head has advanced (or
|
|
63
|
+
the probe failed and we're falling through defensively) does the
|
|
64
|
+
cycle issue the expensive `eth_getBlockByNumber('latest', true)`
|
|
65
|
+
(1–5MB on busy chains). On a static head with the default 10 s
|
|
66
|
+
interval, the per-tick RPC weight drops from "full block + mempool
|
|
67
|
+
+ probe" to "probe + mempool". Mempool fetch still runs every
|
|
68
|
+
cycle. Closes the efficiency-regression risk for the upcoming
|
|
69
|
+
gas-oracle migration to consume `ChainSource` (the soon-to-be-
|
|
70
|
+
deprecated `gas-oracle.blockGatedPolling` option had this behavior
|
|
71
|
+
in v0.5.0; it now lives at the source layer where every consumer
|
|
72
|
+
benefits).
|
|
73
|
+
|
|
74
|
+
### Notes
|
|
75
|
+
|
|
76
|
+
- WebSocket push subscriptions (`eth_subscribe('newHeads')` /
|
|
77
|
+
`eth_subscribe('newPendingTransactions')`) are not yet wired in this
|
|
78
|
+
release. The capability probe discloses what the transport
|
|
79
|
+
*structurally* supports, but the source always uses its interval
|
|
80
|
+
poll cycle in this revision. A future release adds the push path
|
|
81
|
+
without changing the consumer-facing surface.
|
|
82
|
+
- `gas-oracle` and `tx-tracker` migrations to consume `ChainSource`
|
|
83
|
+
land in subsequent PRs of the same v0.3.x track. Until those merge,
|
|
84
|
+
this package is consumable directly but its sibling packages keep
|
|
85
|
+
their v0.3.0 standalone behavior.
|
|
86
|
+
|
|
9
87
|
## [0.5.0] — 2026-05-05
|
|
10
88
|
|
|
11
89
|
### Notes
|
package/README.md
CHANGED
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
# @valve-tech/chain-source
|
|
2
2
|
|
|
3
|
-
> **Status: stub (v0.0.1).** This package is a name reservation. The
|
|
4
|
-
> implementation lands in v0.1.0. See
|
|
5
|
-
> [`docs/tx-tracker-spec.md`](https://github.com/valve-tech/evm-toolkit/blob/main/docs/tx-tracker-spec.md)
|
|
6
|
-
> §3 for the design contract.
|
|
7
|
-
|
|
8
3
|
Canonical EVM chain-observation primitive. Provides a unified push-or-poll
|
|
9
4
|
source for new blocks, mempool snapshots, on-demand receipt and tx
|
|
10
5
|
lookups, and explicit capability disclosure (HTTP / WS / per-method
|
|
@@ -12,35 +7,98 @@ gating). Designed to be consumed by multiple downstream views of chain
|
|
|
12
7
|
state — `@valve-tech/gas-oracle` and `@valve-tech/tx-tracker` are the
|
|
13
8
|
first two.
|
|
14
9
|
|
|
10
|
+
See [`docs/tx-tracker-spec.md`](https://github.com/valve-tech/evm-toolkit/blob/main/docs/tx-tracker-spec.md)
|
|
11
|
+
§3 for the full design contract.
|
|
12
|
+
|
|
13
|
+
## Why this exists
|
|
14
|
+
|
|
15
|
+
Both gas-oracle and tx-tracker need the same upstream signals — new
|
|
16
|
+
blocks, mempool snapshots, capability probing. Re-implementing the
|
|
17
|
+
poll loop in each would mean double-polling for consumers who use
|
|
18
|
+
both. Sharing a `ChainSource` instance between them gives one upstream
|
|
19
|
+
RPC stream feeding multiple derived views.
|
|
20
|
+
|
|
21
|
+
## Install
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
yarn add @valve-tech/chain-source viem
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Quick start
|
|
28
|
+
|
|
15
29
|
```ts
|
|
16
|
-
// v0.1.0+ shape (not yet implemented):
|
|
17
|
-
import { createChainSource } from '@valve-tech/chain-source'
|
|
18
30
|
import { createPublicClient, http } from 'viem'
|
|
19
31
|
import { mainnet } from 'viem/chains'
|
|
32
|
+
import { createChainSource } from '@valve-tech/chain-source'
|
|
20
33
|
|
|
21
34
|
const client = createPublicClient({ chain: mainnet, transport: http() })
|
|
22
35
|
const source = createChainSource({ client })
|
|
36
|
+
|
|
37
|
+
source.subscribeBlocks((block) => {
|
|
38
|
+
console.log('new block', block.number)
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
source.subscribeMempool((snapshot) => {
|
|
42
|
+
console.log('pending senders', Object.keys(snapshot.pending).length)
|
|
43
|
+
})
|
|
44
|
+
|
|
23
45
|
source.start()
|
|
24
46
|
|
|
25
|
-
|
|
26
|
-
source.subscribeMempool((snapshot) => { /* ... */ })
|
|
47
|
+
// On-demand RPCs (don't go through the poll cycle):
|
|
27
48
|
const receipt = await source.getReceipt('0xabc...')
|
|
49
|
+
const tx = await source.getTransaction('0xabc...')
|
|
50
|
+
|
|
51
|
+
// Stop when done — preserves the subscriber registry across restarts.
|
|
52
|
+
source.stop()
|
|
28
53
|
```
|
|
29
54
|
|
|
30
|
-
##
|
|
55
|
+
## API surface
|
|
31
56
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
57
|
+
```ts
|
|
58
|
+
interface ChainSource {
|
|
59
|
+
start(): void
|
|
60
|
+
stop(): void
|
|
61
|
+
pollOnce(): Promise<void>
|
|
62
|
+
ready(): Promise<void>
|
|
37
63
|
|
|
38
|
-
|
|
64
|
+
subscribeBlocks(cb: (block: BlockResult) => void): () => void
|
|
65
|
+
subscribeMempool(cb: (snapshot: NormalizedMempool) => void): () => void
|
|
39
66
|
|
|
40
|
-
|
|
41
|
-
|
|
67
|
+
getBlock(tag: 'latest' | bigint): Promise<BlockResult | null>
|
|
68
|
+
getFeeHistory(blockCount: number, percentiles: number[]): Promise<FeeHistoryResult | null>
|
|
69
|
+
getMempoolSnapshot(): Promise<NormalizedMempool | null>
|
|
70
|
+
getReceipt(hash: string): Promise<TransactionReceipt | null>
|
|
71
|
+
getTransaction(hash: string): Promise<RawTx | null>
|
|
72
|
+
|
|
73
|
+
capabilities(): Capabilities
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
The capability probe runs eagerly at construction. `capabilities()`
|
|
78
|
+
returns a conservative default (everything `unavailable` / `gated`)
|
|
79
|
+
for the brief window before the probe lands; `await source.ready()`
|
|
80
|
+
guarantees the real values are cached.
|
|
81
|
+
|
|
82
|
+
## Multi-subscriber semantics
|
|
83
|
+
|
|
84
|
+
`subscribeBlocks` and `subscribeMempool` are first-class
|
|
85
|
+
multi-subscriber streams. One upstream RPC poll cycle, regardless of
|
|
86
|
+
how many derived views attach:
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
const source = createChainSource({ client })
|
|
90
|
+
const oracle = createGasOracle({ source, chainId: 1 }) // v0.3.x+ shape
|
|
91
|
+
const tracker = createTxTracker({ source, chainId: 1 }) // v0.3.x+ shape
|
|
92
|
+
|
|
93
|
+
source.start()
|
|
94
|
+
oracle.start()
|
|
95
|
+
tracker.start()
|
|
96
|
+
// ↑ one shared poll cycle, two derived views, no double-polling.
|
|
42
97
|
```
|
|
43
98
|
|
|
99
|
+
Stopping a derived view does not stop the source — the consumer that
|
|
100
|
+
constructed the source is the one who calls `source.stop()`.
|
|
101
|
+
|
|
44
102
|
## License
|
|
45
103
|
|
|
46
104
|
MIT
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-method capability probe. Run once at `source.start()`, exposed
|
|
3
|
+
* via `source.capabilities()`, and re-run on transport reconnect when
|
|
4
|
+
* the underlying transport supports reconnection signalling. The
|
|
5
|
+
* probe is the load-bearing place where the toolkit's "no silent
|
|
6
|
+
* downgrade" rule (spec §2.2) is honored — every event the source
|
|
7
|
+
* emits carries a `source` discriminator chosen against this matrix.
|
|
8
|
+
*
|
|
9
|
+
* The probe uses the existing `safeRequest`-returns-null pattern: a
|
|
10
|
+
* thrown error is treated as "method gated/unavailable", a `null`
|
|
11
|
+
* return is treated the same. The distinction between the four states
|
|
12
|
+
* (`subscription` / `poll-only` / `available` / `gated` /
|
|
13
|
+
* `unavailable`) lives in the per-method spec table (§7).
|
|
14
|
+
*
|
|
15
|
+
* **What this probe deliberately does NOT do:** issue a real
|
|
16
|
+
* `eth_subscribe` against the upstream. The viem transport's
|
|
17
|
+
* `subscribe` API surface varies across versions and engaging it just
|
|
18
|
+
* to immediately tear it down is wasteful + can leak handles on
|
|
19
|
+
* misbehaving providers. We detect WS-subscribe capability
|
|
20
|
+
* structurally (`typeof transport.subscribe === 'function'`) and let
|
|
21
|
+
* the actual subscribe attempt happen lazily when a consumer calls
|
|
22
|
+
* `source.subscribeBlocks` / `subscribeMempool`. If the runtime
|
|
23
|
+
* subscribe fails, the source emits a `signal-degraded`-shaped fact
|
|
24
|
+
* (or downstream consumers do — chain-source itself doesn't author
|
|
25
|
+
* editorial events). This is the v0.3.x posture; future revisions
|
|
26
|
+
* may upgrade to live-probing if a provider is found that lies
|
|
27
|
+
* structurally.
|
|
28
|
+
*/
|
|
29
|
+
import type { PublicClient } from 'viem';
|
|
30
|
+
import type { Capabilities } from './types.js';
|
|
31
|
+
interface ProbeOptions {
|
|
32
|
+
/** Same role as on `createChainSource` — sub-RPC failure sink. */
|
|
33
|
+
onError?: (method: string, err: unknown) => void;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Probe the upstream client's per-method capability. The result is a
|
|
37
|
+
* stable snapshot the source caches and exposes via
|
|
38
|
+
* `source.capabilities()`.
|
|
39
|
+
*/
|
|
40
|
+
export declare const probeCapabilities: (client: PublicClient, options?: ProbeOptions) => Promise<Capabilities>;
|
|
41
|
+
export {};
|
|
42
|
+
//# sourceMappingURL=capabilities.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capabilities.d.ts","sourceRoot":"","sources":["../src/capabilities.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,MAAM,CAAA;AAGxC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAE9C,UAAU,YAAY;IACpB,kEAAkE;IAClE,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,KAAK,IAAI,CAAA;CACjD;AAsBD;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,GAC5B,QAAQ,YAAY,EACpB,UAAS,YAAiB,KACzB,OAAO,CAAC,YAAY,CAoDtB,CAAA"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-method capability probe. Run once at `source.start()`, exposed
|
|
3
|
+
* via `source.capabilities()`, and re-run on transport reconnect when
|
|
4
|
+
* the underlying transport supports reconnection signalling. The
|
|
5
|
+
* probe is the load-bearing place where the toolkit's "no silent
|
|
6
|
+
* downgrade" rule (spec §2.2) is honored — every event the source
|
|
7
|
+
* emits carries a `source` discriminator chosen against this matrix.
|
|
8
|
+
*
|
|
9
|
+
* The probe uses the existing `safeRequest`-returns-null pattern: a
|
|
10
|
+
* thrown error is treated as "method gated/unavailable", a `null`
|
|
11
|
+
* return is treated the same. The distinction between the four states
|
|
12
|
+
* (`subscription` / `poll-only` / `available` / `gated` /
|
|
13
|
+
* `unavailable`) lives in the per-method spec table (§7).
|
|
14
|
+
*
|
|
15
|
+
* **What this probe deliberately does NOT do:** issue a real
|
|
16
|
+
* `eth_subscribe` against the upstream. The viem transport's
|
|
17
|
+
* `subscribe` API surface varies across versions and engaging it just
|
|
18
|
+
* to immediately tear it down is wasteful + can leak handles on
|
|
19
|
+
* misbehaving providers. We detect WS-subscribe capability
|
|
20
|
+
* structurally (`typeof transport.subscribe === 'function'`) and let
|
|
21
|
+
* the actual subscribe attempt happen lazily when a consumer calls
|
|
22
|
+
* `source.subscribeBlocks` / `subscribeMempool`. If the runtime
|
|
23
|
+
* subscribe fails, the source emits a `signal-degraded`-shaped fact
|
|
24
|
+
* (or downstream consumers do — chain-source itself doesn't author
|
|
25
|
+
* editorial events). This is the v0.3.x posture; future revisions
|
|
26
|
+
* may upgrade to live-probing if a provider is found that lies
|
|
27
|
+
* structurally.
|
|
28
|
+
*/
|
|
29
|
+
import { safeRequest, zeroHash } from './transport.js';
|
|
30
|
+
/**
|
|
31
|
+
* Inspect the client's transport surface to decide whether push
|
|
32
|
+
* subscriptions are possible. The capability is structural — the
|
|
33
|
+
* subscribe attempt itself is deferred to the actual subscribeBlocks /
|
|
34
|
+
* subscribeMempool call so we don't pay an early eth_subscribe just
|
|
35
|
+
* to learn the answer.
|
|
36
|
+
*
|
|
37
|
+
* Returns `'subscription'` when the transport exposes a `subscribe`
|
|
38
|
+
* function (canonical viem WS shape), `'unavailable'` otherwise.
|
|
39
|
+
* `'poll-only'` is reserved for the future live-probe variant where
|
|
40
|
+
* we can distinguish "transport supports subscribe but upstream
|
|
41
|
+
* rejected this method" from "transport doesn't subscribe at all."
|
|
42
|
+
*/
|
|
43
|
+
const probeSubscribeShape = (client) => {
|
|
44
|
+
const transport = client.transport;
|
|
45
|
+
return typeof transport.subscribe === 'function' ? 'subscription' : 'unavailable';
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Probe the upstream client's per-method capability. The result is a
|
|
49
|
+
* stable snapshot the source caches and exposes via
|
|
50
|
+
* `source.capabilities()`.
|
|
51
|
+
*/
|
|
52
|
+
export const probeCapabilities = async (client, options = {}) => {
|
|
53
|
+
const subscribeShape = probeSubscribeShape(client);
|
|
54
|
+
// 1. txpool_content — distinguishes 'available' / 'gated'. Many
|
|
55
|
+
// public RPCs return 405 / method-not-found for this method.
|
|
56
|
+
const txpoolResult = await safeRequest(client, 'txpool_content', [], options.onError ? (err) => options.onError('txpool_content', err) : undefined);
|
|
57
|
+
const txpoolContent = txpoolResult === null ? 'gated' : 'available';
|
|
58
|
+
// 2. eth_getTransactionReceipt against the zero hash. Per spec §7.2,
|
|
59
|
+
// most providers return null for the zero hash but should not throw.
|
|
60
|
+
// A throw is taken as "method unavailable." Use direct request rather
|
|
61
|
+
// than safeRequest so the throw path is observable.
|
|
62
|
+
let receiptByHash = 'available';
|
|
63
|
+
try {
|
|
64
|
+
await client.request({
|
|
65
|
+
method: 'eth_getTransactionReceipt',
|
|
66
|
+
params: [zeroHash],
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
receiptByHash = 'unavailable';
|
|
71
|
+
if (options.onError)
|
|
72
|
+
options.onError('eth_getTransactionReceipt', err);
|
|
73
|
+
}
|
|
74
|
+
// 3. WS-subscribe capability is the same answer for both newHeads
|
|
75
|
+
// and newPendingTransactions — the discriminator there is whether
|
|
76
|
+
// the upstream supports the *channel*, not the method. Mid-session
|
|
77
|
+
// method-specific failure surfaces via `signal-degraded` events on
|
|
78
|
+
// downstream consumers (the source itself doesn't author editorial
|
|
79
|
+
// events; see §3.2 of the spec).
|
|
80
|
+
return {
|
|
81
|
+
newHeads: subscribeShape,
|
|
82
|
+
newPendingTransactions:
|
|
83
|
+
// If subscribe is unavailable, fall back to whether txpool_content
|
|
84
|
+
// is available (poll fallback for mempool watch). If both are
|
|
85
|
+
// unavailable, mempool watch isn't possible at all.
|
|
86
|
+
subscribeShape === 'subscription'
|
|
87
|
+
? 'subscription'
|
|
88
|
+
: txpoolContent === 'available'
|
|
89
|
+
? 'poll-only'
|
|
90
|
+
: 'unavailable',
|
|
91
|
+
txpoolContent,
|
|
92
|
+
receiptByHash,
|
|
93
|
+
// WS transports are the ones that reconnect; HTTP has no persistent
|
|
94
|
+
// connection so reprobe-on-reconnect is meaningless.
|
|
95
|
+
reprobeOnReconnect: subscribeShape === 'subscription',
|
|
96
|
+
};
|
|
97
|
+
};
|
|
98
|
+
//# sourceMappingURL=capabilities.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capabilities.js","sourceRoot":"","sources":["../src/capabilities.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAIH,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAQtD;;;;;;;;;;;;GAYG;AACH,MAAM,mBAAmB,GAAG,CAC1B,MAAoB,EACY,EAAE;IAClC,MAAM,SAAS,GAAG,MAAM,CAAC,SAAoC,CAAA;IAC7D,OAAO,OAAO,SAAS,CAAC,SAAS,KAAK,UAAU,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,aAAa,CAAA;AACnF,CAAC,CAAA;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,KAAK,EACpC,MAAoB,EACpB,UAAwB,EAAE,EACH,EAAE;IACzB,MAAM,cAAc,GAAG,mBAAmB,CAAC,MAAM,CAAC,CAAA;IAElD,gEAAgE;IAChE,6DAA6D;IAC7D,MAAM,YAAY,GAAG,MAAM,WAAW,CACpC,MAAM,EACN,gBAAgB,EAChB,EAAE,EACF,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAY,EAAE,EAAE,CAAC,OAAO,CAAC,OAAQ,CAAC,gBAAgB,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CACxF,CAAA;IACD,MAAM,aAAa,GACjB,YAAY,KAAK,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,WAAW,CAAA;IAE/C,qEAAqE;IACrE,qEAAqE;IACrE,sEAAsE;IACtE,oDAAoD;IACpD,IAAI,aAAa,GAAgC,WAAW,CAAA;IAC5D,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,OAAO,CAAC;YACnB,MAAM,EAAE,2BAA2B;YACnC,MAAM,EAAE,CAAC,QAAQ,CAAC;SACV,CAAC,CAAA;IACb,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,aAAa,GAAG,aAAa,CAAA;QAC7B,IAAI,OAAO,CAAC,OAAO;YAAE,OAAO,CAAC,OAAO,CAAC,2BAA2B,EAAE,GAAG,CAAC,CAAA;IACxE,CAAC;IAED,kEAAkE;IAClE,kEAAkE;IAClE,mEAAmE;IACnE,mEAAmE;IACnE,mEAAmE;IACnE,iCAAiC;IACjC,OAAO;QACL,QAAQ,EAAE,cAAc;QACxB,sBAAsB;QACpB,mEAAmE;QACnE,8DAA8D;QAC9D,oDAAoD;QACpD,cAAc,KAAK,cAAc;YAC/B,CAAC,CAAC,cAAc;YAChB,CAAC,CAAC,aAAa,KAAK,WAAW;gBAC7B,CAAC,CAAC,WAAW;gBACb,CAAC,CAAC,aAAa;QACrB,aAAa;QACb,aAAa;QACb,oEAAoE;QACpE,qDAAqD;QACrD,kBAAkB,EAAE,cAAc,KAAK,cAAc;KACtD,CAAA;AACH,CAAC,CAAA"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,15 +1,33 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* `@valve-tech/chain-source` — canonical EVM chain-observation primitive
|
|
3
|
+
* for the `valve-tech/evm-toolkit` synchronized release line.
|
|
3
4
|
*
|
|
4
|
-
* The
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* The package is the *foundation* layer for every derived view of
|
|
6
|
+
* chain state in the toolkit. Both `@valve-tech/gas-oracle` (gas-tier
|
|
7
|
+
* reducer) and `@valve-tech/tx-tracker` (per-tx state machine) consume
|
|
8
|
+
* a `ChainSource` rather than re-implementing their own poll cycles.
|
|
9
|
+
* One upstream RPC stream feeds every consumer that attaches.
|
|
7
10
|
*
|
|
8
|
-
*
|
|
9
|
-
* `valve-tech/evm-toolkit` repo, which defines the `ChainSource`
|
|
10
|
-
* interface this package will export.
|
|
11
|
+
* See `docs/tx-tracker-spec.md` §3 for the full design contract.
|
|
11
12
|
*
|
|
12
|
-
*
|
|
13
|
+
* @example
|
|
14
|
+
* import { createPublicClient, http } from 'viem'
|
|
15
|
+
* import { mainnet } from 'viem/chains'
|
|
16
|
+
* import { createChainSource } from '@valve-tech/chain-source'
|
|
17
|
+
*
|
|
18
|
+
* const client = createPublicClient({ chain: mainnet, transport: http() })
|
|
19
|
+
* const source = createChainSource({ client })
|
|
20
|
+
*
|
|
21
|
+
* source.subscribeBlocks((block) => {
|
|
22
|
+
* console.log('block', block.number)
|
|
23
|
+
* })
|
|
24
|
+
* source.start()
|
|
13
25
|
*/
|
|
14
|
-
export {};
|
|
26
|
+
export { createChainSource } from './source.js';
|
|
27
|
+
export type { ChainSource, CreateChainSourceOptions } from './source.js';
|
|
28
|
+
export { Subscriptions } from './subscriptions.js';
|
|
29
|
+
export { normalizeMempool } from './mempool.js';
|
|
30
|
+
export { probeCapabilities } from './capabilities.js';
|
|
31
|
+
export { safeRequest, fetchBlock, fetchHeadBlockNumber, fetchFeeHistory, fetchTxPool, fetchReceipt, fetchTransaction, zeroHash, } from './transport.js';
|
|
32
|
+
export type { BlockResult, Capabilities, EventSource, FeeHistoryResult, NormalizedMempool, PollOptions, RawTx, TransactionReceipt, TxPoolContent, } from './types.js';
|
|
15
33
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAC/C,YAAY,EAAE,WAAW,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAA;AAExE,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAElD,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAE/C,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AAErD,OAAO,EACL,WAAW,EACX,UAAU,EACV,oBAAoB,EACpB,eAAe,EACf,WAAW,EACX,YAAY,EACZ,gBAAgB,EAChB,QAAQ,GACT,MAAM,gBAAgB,CAAA;AAEvB,YAAY,EACV,WAAW,EACX,YAAY,EACZ,WAAW,EACX,gBAAgB,EAChB,iBAAiB,EACjB,WAAW,EACX,KAAK,EACL,kBAAkB,EAClB,aAAa,GACd,MAAM,YAAY,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,31 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* `@valve-tech/chain-source` — canonical EVM chain-observation primitive
|
|
3
|
+
* for the `valve-tech/evm-toolkit` synchronized release line.
|
|
4
|
+
*
|
|
5
|
+
* The package is the *foundation* layer for every derived view of
|
|
6
|
+
* chain state in the toolkit. Both `@valve-tech/gas-oracle` (gas-tier
|
|
7
|
+
* reducer) and `@valve-tech/tx-tracker` (per-tx state machine) consume
|
|
8
|
+
* a `ChainSource` rather than re-implementing their own poll cycles.
|
|
9
|
+
* One upstream RPC stream feeds every consumer that attaches.
|
|
10
|
+
*
|
|
11
|
+
* See `docs/tx-tracker-spec.md` §3 for the full design contract.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* import { createPublicClient, http } from 'viem'
|
|
15
|
+
* import { mainnet } from 'viem/chains'
|
|
16
|
+
* import { createChainSource } from '@valve-tech/chain-source'
|
|
17
|
+
*
|
|
18
|
+
* const client = createPublicClient({ chain: mainnet, transport: http() })
|
|
19
|
+
* const source = createChainSource({ client })
|
|
20
|
+
*
|
|
21
|
+
* source.subscribeBlocks((block) => {
|
|
22
|
+
* console.log('block', block.number)
|
|
23
|
+
* })
|
|
24
|
+
* source.start()
|
|
25
|
+
*/
|
|
26
|
+
export { createChainSource } from './source.js';
|
|
27
|
+
export { Subscriptions } from './subscriptions.js';
|
|
28
|
+
export { normalizeMempool } from './mempool.js';
|
|
29
|
+
export { probeCapabilities } from './capabilities.js';
|
|
30
|
+
export { safeRequest, fetchBlock, fetchHeadBlockNumber, fetchFeeHistory, fetchTxPool, fetchReceipt, fetchTransaction, zeroHash, } from './transport.js';
|
|
2
31
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAG/C,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAElD,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAE/C,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AAErD,OAAO,EACL,WAAW,EACX,UAAU,EACV,oBAAoB,EACpB,eAAe,EACf,WAAW,EACX,YAAY,EACZ,gBAAgB,EAChB,QAAQ,GACT,MAAM,gBAAgB,CAAA"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mempool normalization. The single transformation pass that takes a
|
|
3
|
+
* `txpool_content` response into a canonical `NormalizedMempool` whose
|
|
4
|
+
* outer keys (sender address, nonce) are predictable.
|
|
5
|
+
*
|
|
6
|
+
* Why normalize once at ingest, not on every lookup?
|
|
7
|
+
*
|
|
8
|
+
* - Upstream clients are inconsistent about address case (geth /
|
|
9
|
+
* reth use EIP-55 mixed case, others lowercase, some checksum
|
|
10
|
+
* only some hex chars).
|
|
11
|
+
* - Nonce is also inconsistent — some clients hex-encode (`'0x5'`),
|
|
12
|
+
* some decimal-encode (`'5'`).
|
|
13
|
+
* - A consumer doing direct `pool.pending[address][nonce]` lookups
|
|
14
|
+
* would need a case-fold + format-fold fallback walk on every
|
|
15
|
+
* access. That's both slow and a class of latent bugs (forget
|
|
16
|
+
* the fallback, miss a hit).
|
|
17
|
+
*
|
|
18
|
+
* Normalize once at the source's ingest point, then every lookup is
|
|
19
|
+
* an O(1) two-key access against keys whose form is known. The
|
|
20
|
+
* `NormalizedMempool` type alias signals the invariant.
|
|
21
|
+
*/
|
|
22
|
+
import type { NormalizedMempool, TxPoolContent } from './types.js';
|
|
23
|
+
/**
|
|
24
|
+
* Run the upstream `txpool_content` payload through one normalization
|
|
25
|
+
* pass: every sender address key is lowercased, every nonce key is
|
|
26
|
+
* coerced to its canonical decimal form. The inner `RawTx` values
|
|
27
|
+
* are passed through by reference unchanged — pool normalization is
|
|
28
|
+
* for the OUTER keys only; downstream tx-field comparisons (hash,
|
|
29
|
+
* from, nonce) do their own case-folding.
|
|
30
|
+
*
|
|
31
|
+
* Idempotent — re-normalizing a `NormalizedMempool` returns an
|
|
32
|
+
* equivalent `NormalizedMempool`. Pass `null` / `undefined` to get
|
|
33
|
+
* empty subpools back; this lets callers store the result without
|
|
34
|
+
* `null`-checking on every lookup.
|
|
35
|
+
*/
|
|
36
|
+
export declare const normalizeMempool: (pool: TxPoolContent | null | undefined) => NormalizedMempool;
|
|
37
|
+
//# sourceMappingURL=mempool.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mempool.d.ts","sourceRoot":"","sources":["../src/mempool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAS,aAAa,EAAE,MAAM,YAAY,CAAA;AAmBzE;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,gBAAgB,GAC3B,MAAM,aAAa,GAAG,IAAI,GAAG,SAAS,KACrC,iBAGD,CAAA"}
|
package/dist/mempool.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mempool normalization. The single transformation pass that takes a
|
|
3
|
+
* `txpool_content` response into a canonical `NormalizedMempool` whose
|
|
4
|
+
* outer keys (sender address, nonce) are predictable.
|
|
5
|
+
*
|
|
6
|
+
* Why normalize once at ingest, not on every lookup?
|
|
7
|
+
*
|
|
8
|
+
* - Upstream clients are inconsistent about address case (geth /
|
|
9
|
+
* reth use EIP-55 mixed case, others lowercase, some checksum
|
|
10
|
+
* only some hex chars).
|
|
11
|
+
* - Nonce is also inconsistent — some clients hex-encode (`'0x5'`),
|
|
12
|
+
* some decimal-encode (`'5'`).
|
|
13
|
+
* - A consumer doing direct `pool.pending[address][nonce]` lookups
|
|
14
|
+
* would need a case-fold + format-fold fallback walk on every
|
|
15
|
+
* access. That's both slow and a class of latent bugs (forget
|
|
16
|
+
* the fallback, miss a hit).
|
|
17
|
+
*
|
|
18
|
+
* Normalize once at the source's ingest point, then every lookup is
|
|
19
|
+
* an O(1) two-key access against keys whose form is known. The
|
|
20
|
+
* `NormalizedMempool` type alias signals the invariant.
|
|
21
|
+
*/
|
|
22
|
+
const normalizeSubpool = (sub) => {
|
|
23
|
+
if (!sub)
|
|
24
|
+
return {};
|
|
25
|
+
const out = {};
|
|
26
|
+
for (const [address, byNonce] of Object.entries(sub)) {
|
|
27
|
+
const normalizedByNonce = {};
|
|
28
|
+
for (const [nonce, tx] of Object.entries(byNonce)) {
|
|
29
|
+
// BigInt() accepts both '5' and '0x5'; .toString() gives the
|
|
30
|
+
// canonical decimal form. Idempotent on already-decimal keys.
|
|
31
|
+
normalizedByNonce[BigInt(nonce).toString()] = tx;
|
|
32
|
+
}
|
|
33
|
+
out[address.toLowerCase()] = normalizedByNonce;
|
|
34
|
+
}
|
|
35
|
+
return out;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Run the upstream `txpool_content` payload through one normalization
|
|
39
|
+
* pass: every sender address key is lowercased, every nonce key is
|
|
40
|
+
* coerced to its canonical decimal form. The inner `RawTx` values
|
|
41
|
+
* are passed through by reference unchanged — pool normalization is
|
|
42
|
+
* for the OUTER keys only; downstream tx-field comparisons (hash,
|
|
43
|
+
* from, nonce) do their own case-folding.
|
|
44
|
+
*
|
|
45
|
+
* Idempotent — re-normalizing a `NormalizedMempool` returns an
|
|
46
|
+
* equivalent `NormalizedMempool`. Pass `null` / `undefined` to get
|
|
47
|
+
* empty subpools back; this lets callers store the result without
|
|
48
|
+
* `null`-checking on every lookup.
|
|
49
|
+
*/
|
|
50
|
+
export const normalizeMempool = (pool) => ({
|
|
51
|
+
pending: normalizeSubpool(pool?.pending),
|
|
52
|
+
queued: normalizeSubpool(pool?.queued),
|
|
53
|
+
});
|
|
54
|
+
//# sourceMappingURL=mempool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mempool.js","sourceRoot":"","sources":["../src/mempool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAIH,MAAM,gBAAgB,GAAG,CACvB,GAAsD,EACf,EAAE;IACzC,IAAI,CAAC,GAAG;QAAE,OAAO,EAAE,CAAA;IACnB,MAAM,GAAG,GAA0C,EAAE,CAAA;IACrD,KAAK,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QACrD,MAAM,iBAAiB,GAA0B,EAAE,CAAA;QACnD,KAAK,MAAM,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;YAClD,6DAA6D;YAC7D,8DAA8D;YAC9D,iBAAiB,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,QAAQ,EAAE,CAAC,GAAG,EAAE,CAAA;QAClD,CAAC;QACD,GAAG,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,GAAG,iBAAiB,CAAA;IAChD,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC,CAAA;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAC9B,IAAsC,EACnB,EAAE,CAAC,CAAC;IACvB,OAAO,EAAE,gBAAgB,CAAC,IAAI,EAAE,OAAO,CAAC;IACxC,MAAM,EAAE,gBAAgB,CAAC,IAAI,EAAE,MAAM,CAAC;CACvC,CAAC,CAAA"}
|