@notuslabs/yail 0.1.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.
Files changed (168) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +226 -0
  3. package/bin/yail.js +7 -0
  4. package/dist/addresses/registry.d.ts +57 -0
  5. package/dist/addresses/registry.d.ts.map +1 -0
  6. package/dist/addresses/registry.js +131 -0
  7. package/dist/addresses/registry.js.map +1 -0
  8. package/dist/cli/index.d.ts +2 -0
  9. package/dist/cli/index.d.ts.map +1 -0
  10. package/dist/cli/index.js +204 -0
  11. package/dist/cli/index.js.map +1 -0
  12. package/dist/config/address.d.ts +32 -0
  13. package/dist/config/address.d.ts.map +1 -0
  14. package/dist/config/address.js +31 -0
  15. package/dist/config/address.js.map +1 -0
  16. package/dist/config/chain-ref.d.ts +5 -0
  17. package/dist/config/chain-ref.d.ts.map +1 -0
  18. package/dist/config/chain-ref.js +13 -0
  19. package/dist/config/chain-ref.js.map +1 -0
  20. package/dist/config/create-config.d.ts +9 -0
  21. package/dist/config/create-config.d.ts.map +1 -0
  22. package/dist/config/create-config.js +86 -0
  23. package/dist/config/create-config.js.map +1 -0
  24. package/dist/config/index.d.ts +5 -0
  25. package/dist/config/index.d.ts.map +1 -0
  26. package/dist/config/index.js +3 -0
  27. package/dist/config/index.js.map +1 -0
  28. package/dist/config/types.d.ts +113 -0
  29. package/dist/config/types.d.ts.map +1 -0
  30. package/dist/config/types.js +2 -0
  31. package/dist/config/types.js.map +1 -0
  32. package/dist/db/batch.d.ts +33 -0
  33. package/dist/db/batch.d.ts.map +1 -0
  34. package/dist/db/batch.js +71 -0
  35. package/dist/db/batch.js.map +1 -0
  36. package/dist/db/client.d.ts +50 -0
  37. package/dist/db/client.d.ts.map +1 -0
  38. package/dist/db/client.js +104 -0
  39. package/dist/db/client.js.map +1 -0
  40. package/dist/db/index.d.ts +11 -0
  41. package/dist/db/index.d.ts.map +1 -0
  42. package/dist/db/index.js +7 -0
  43. package/dist/db/index.js.map +1 -0
  44. package/dist/db/internal.d.ts +2332 -0
  45. package/dist/db/internal.d.ts.map +1 -0
  46. package/dist/db/internal.js +75 -0
  47. package/dist/db/internal.js.map +1 -0
  48. package/dist/db/migrate.d.ts +22 -0
  49. package/dist/db/migrate.d.ts.map +1 -0
  50. package/dist/db/migrate.js +72 -0
  51. package/dist/db/migrate.js.map +1 -0
  52. package/dist/db/serialize.d.ts +14 -0
  53. package/dist/db/serialize.d.ts.map +1 -0
  54. package/dist/db/serialize.js +133 -0
  55. package/dist/db/serialize.js.map +1 -0
  56. package/dist/db/sql.d.ts +44 -0
  57. package/dist/db/sql.d.ts.map +1 -0
  58. package/dist/db/sql.js +122 -0
  59. package/dist/db/sql.js.map +1 -0
  60. package/dist/http/client.d.ts +40 -0
  61. package/dist/http/client.d.ts.map +1 -0
  62. package/dist/http/client.js +128 -0
  63. package/dist/http/client.js.map +1 -0
  64. package/dist/index.d.ts +21 -0
  65. package/dist/index.d.ts.map +1 -0
  66. package/dist/index.js +15 -0
  67. package/dist/index.js.map +1 -0
  68. package/dist/indexer/context.d.ts +84 -0
  69. package/dist/indexer/context.d.ts.map +1 -0
  70. package/dist/indexer/context.js +45 -0
  71. package/dist/indexer/context.js.map +1 -0
  72. package/dist/indexer/events.d.ts +87 -0
  73. package/dist/indexer/events.d.ts.map +1 -0
  74. package/dist/indexer/events.js +88 -0
  75. package/dist/indexer/events.js.map +1 -0
  76. package/dist/indexer/indexer.d.ts +114 -0
  77. package/dist/indexer/indexer.d.ts.map +1 -0
  78. package/dist/indexer/indexer.js +237 -0
  79. package/dist/indexer/indexer.js.map +1 -0
  80. package/dist/indexer/jobs.d.ts +61 -0
  81. package/dist/indexer/jobs.d.ts.map +1 -0
  82. package/dist/indexer/jobs.js +170 -0
  83. package/dist/indexer/jobs.js.map +1 -0
  84. package/dist/indexer/plan.d.ts +73 -0
  85. package/dist/indexer/plan.d.ts.map +1 -0
  86. package/dist/indexer/plan.js +238 -0
  87. package/dist/indexer/plan.js.map +1 -0
  88. package/dist/indexer/runtime.d.ts +100 -0
  89. package/dist/indexer/runtime.d.ts.map +1 -0
  90. package/dist/indexer/runtime.js +572 -0
  91. package/dist/indexer/runtime.js.map +1 -0
  92. package/dist/observability/logger.d.ts +23 -0
  93. package/dist/observability/logger.d.ts.map +1 -0
  94. package/dist/observability/logger.js +72 -0
  95. package/dist/observability/logger.js.map +1 -0
  96. package/dist/observability/metrics.d.ts +61 -0
  97. package/dist/observability/metrics.d.ts.map +1 -0
  98. package/dist/observability/metrics.js +131 -0
  99. package/dist/observability/metrics.js.map +1 -0
  100. package/dist/observability/otel.d.ts +28 -0
  101. package/dist/observability/otel.d.ts.map +1 -0
  102. package/dist/observability/otel.js +49 -0
  103. package/dist/observability/otel.js.map +1 -0
  104. package/dist/rpc/cached-client.d.ts +33 -0
  105. package/dist/rpc/cached-client.d.ts.map +1 -0
  106. package/dist/rpc/cached-client.js +50 -0
  107. package/dist/rpc/cached-client.js.map +1 -0
  108. package/dist/schema/column.d.ts +61 -0
  109. package/dist/schema/column.d.ts.map +1 -0
  110. package/dist/schema/column.js +76 -0
  111. package/dist/schema/column.js.map +1 -0
  112. package/dist/schema/ddl.d.ts +9 -0
  113. package/dist/schema/ddl.d.ts.map +1 -0
  114. package/dist/schema/ddl.js +58 -0
  115. package/dist/schema/ddl.js.map +1 -0
  116. package/dist/schema/index.d.ts +8 -0
  117. package/dist/schema/index.d.ts.map +1 -0
  118. package/dist/schema/index.js +5 -0
  119. package/dist/schema/index.js.map +1 -0
  120. package/dist/schema/table.d.ts +69 -0
  121. package/dist/schema/table.d.ts.map +1 -0
  122. package/dist/schema/table.js +35 -0
  123. package/dist/schema/table.js.map +1 -0
  124. package/dist/schema/view.d.ts +19 -0
  125. package/dist/schema/view.d.ts.map +1 -0
  126. package/dist/schema/view.js +16 -0
  127. package/dist/schema/view.js.map +1 -0
  128. package/dist/server/api.d.ts +11 -0
  129. package/dist/server/api.d.ts.map +1 -0
  130. package/dist/server/api.js +71 -0
  131. package/dist/server/api.js.map +1 -0
  132. package/dist/sources/cached.d.ts +27 -0
  133. package/dist/sources/cached.d.ts.map +1 -0
  134. package/dist/sources/cached.js +111 -0
  135. package/dist/sources/cached.js.map +1 -0
  136. package/dist/sources/esplora.d.ts +22 -0
  137. package/dist/sources/esplora.d.ts.map +1 -0
  138. package/dist/sources/esplora.js +138 -0
  139. package/dist/sources/esplora.js.map +1 -0
  140. package/dist/sources/fixture.d.ts +51 -0
  141. package/dist/sources/fixture.d.ts.map +1 -0
  142. package/dist/sources/fixture.js +102 -0
  143. package/dist/sources/fixture.js.map +1 -0
  144. package/dist/sources/hypersync.d.ts +19 -0
  145. package/dist/sources/hypersync.d.ts.map +1 -0
  146. package/dist/sources/hypersync.js +169 -0
  147. package/dist/sources/hypersync.js.map +1 -0
  148. package/dist/sources/index.d.ts +13 -0
  149. package/dist/sources/index.d.ts.map +1 -0
  150. package/dist/sources/index.js +7 -0
  151. package/dist/sources/index.js.map +1 -0
  152. package/dist/sources/rpc.d.ts +21 -0
  153. package/dist/sources/rpc.d.ts.map +1 -0
  154. package/dist/sources/rpc.js +187 -0
  155. package/dist/sources/rpc.js.map +1 -0
  156. package/dist/sources/types.d.ts +137 -0
  157. package/dist/sources/types.d.ts.map +1 -0
  158. package/dist/sources/types.js +17 -0
  159. package/dist/sources/types.js.map +1 -0
  160. package/dist/testing/index.d.ts +20 -0
  161. package/dist/testing/index.d.ts.map +1 -0
  162. package/dist/testing/index.js +44 -0
  163. package/dist/testing/index.js.map +1 -0
  164. package/dist/util.d.ts +9 -0
  165. package/dist/util.d.ts.map +1 -0
  166. package/dist/util.js +25 -0
  167. package/dist/util.js.map +1 -0
  168. package/package.json +100 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Thalles Passos
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,226 @@
1
+ # yail
2
+
3
+ **Y**et **A**nother **I**ndexer **L**ibrary. Ponder-style developer experience, [HyperSync](https://docs.envio.dev/docs/HyperSync/overview) as the primary EVM data source, [ClickHouse](https://clickhouse.com) as the only storage.
4
+
5
+ ```sh
6
+ pnpm add @notuslabs/yail # Node >= 20, ESM
7
+ ```
8
+
9
+ ```ts
10
+ // yail.config.ts
11
+ import { createConfig, hypersync, esplora, addressSet } from "@notuslabs/yail";
12
+ import { erc20Abi } from "./abis";
13
+
14
+ export default createConfig({
15
+ database: { url: "http://localhost:8123", database: "ledger" },
16
+ chains: {
17
+ base: { id: 8453, source: hypersync({ url: "https://base.hypersync.xyz" }), rpc: process.env.BASE_RPC_URL },
18
+ bitcoin: { id: "bitcoin", source: esplora({ url: "https://mempool.space/api" }) },
19
+ },
20
+ contracts: {
21
+ // Every ERC-20 on Base, but only transfers that touch a registered wallet.
22
+ Erc20: { abi: erc20Abi, chain: "base", startBlock: 30_000_000,
23
+ filter: [{ event: "Transfer", args: { from: addressSet("wallets") } }, { event: "Transfer", args: { to: addressSet("wallets") } }] },
24
+ },
25
+ accounts: {
26
+ Wallets: { chain: "base", address: addressSet("wallets"), startBlock: 30_000_000 },
27
+ BtcWallets: { chain: "bitcoin", address: addressSet("btcWallets"), startBlock: 800_000 },
28
+ },
29
+ });
30
+ ```
31
+
32
+ ```ts
33
+ // src/index.ts
34
+ import { createIndexer } from "@notuslabs/yail";
35
+ import config from "../yail.config";
36
+ import * as schema from "./schema";
37
+
38
+ export const indexer = createIndexer({ config, schema });
39
+
40
+ indexer.on("Erc20:Transfer", async ({ event, context }) => {
41
+ // ^ event.args is typed from the ABI
42
+ if (context.addresses.has("wallets", event.args.to)) {
43
+ context.db.insert(schema.ledger).values({ wallet: event.args.to, token: event.address, amount: event.args.value, /* ... */ });
44
+ }
45
+ });
46
+
47
+ indexer.on("BtcWallets:transaction", async ({ event }) => {
48
+ event.received; event.sent; event.fee; // satoshis, typed because the chain is a Bitcoin source
49
+ });
50
+ ```
51
+
52
+ ```sh
53
+ yail migrate # creates database, tables, materialized views
54
+ yail addresses add --set wallets 0xabc… # queued, backfilled, then live
55
+ yail dev # index, restart on change; status API on :42069
56
+ ```
57
+
58
+ ## Why
59
+
60
+ | | Ponder | HyperIndex (Envio) | yail |
61
+ |---|---|---|---|
62
+ | Handlers | `ponder.on("C:Event")` | `Contract.Event.handler` | `indexer.on("C:Event")` |
63
+ | Data source | JSON-RPC | HyperSync | HyperSync, JSON-RPC, Esplora (Bitcoin), or a cache in front of any |
64
+ | Storage | Postgres | Postgres | ClickHouse |
65
+ | Dynamic addresses | factory pattern | `contractRegister` | factory pattern **and** runtime address sets with backfill |
66
+ | Re-index one entity | re-sync | re-sync | `yail reindex --scope wallet=0x…` |
67
+ | Tests | — | mocks | replay recorded real data, assert against on-chain ground truth |
68
+
69
+ Everything the indexer writes is a plain ClickHouse table. An API reads it with the same `sql` tag and typed `db.rows()` the handlers use, or with any ClickHouse client.
70
+
71
+ ## Concepts
72
+
73
+ ### Sources
74
+
75
+ A source is one interface: `getHeight()` + `fetch(query)` yielding ordered batches of blocks, transactions and logs.
76
+
77
+ | source | use |
78
+ |---|---|
79
+ | `hypersync({ url, apiToken })` | production EVM. Token from `ENVIO_API_TOKEN` if omitted. |
80
+ | `rpc({ url })` | any JSON-RPC node; slower, no token, used to record fixtures |
81
+ | `esplora({ url })` | Bitcoin address history through mempool.space / blockstream / electrs |
82
+ | `cached(source, { db, chain })` | ClickHouse page cache in front of any source; `cache: { source: true }` in the config does this for every chain |
83
+ | `fixtureSource(fixture)` | replay recorded data in tests |
84
+
85
+ The runtime only ever indexes blocks `<= head - finality` (default 20 blocks on EVM, 3 on Bitcoin), so reorgs are a non-issue in practice. HyperSync's rollback guard is still checked; on a mismatch the last window is re-indexed automatically.
86
+
87
+ ### Schema
88
+
89
+ ```ts
90
+ import { t, table, materializedView, sql } from "@notuslabs/yail";
91
+
92
+ export const ledger = table("ledger", {
93
+ chain: t.string().lowCardinality(),
94
+ wallet: t.address(), // lowercase 0x string
95
+ amount: t.int256(), // bigint in TS
96
+ blockTime: t.dateTime(), // Date in TS
97
+ note: t.string().nullable(),
98
+ }, {
99
+ orderBy: ["wallet", "chain", "txHash", "logIndex"], // primary key; duplicates collapse (ReplacingMergeTree)
100
+ partitionBy: "toYYYYMM(block_time)",
101
+ scopes: { wallet: "wallet" }, // enables `reindex --scope wallet=…`
102
+ });
103
+
104
+ export const daily = table("wallet_daily", { wallet: t.address(), day: t.date(), net: t.int256() },
105
+ { orderBy: ["wallet", "day"], engine: "SummingMergeTree", indexMeta: false });
106
+
107
+ export const dailyMv = materializedView("wallet_daily_mv", {
108
+ from: ledger, to: daily,
109
+ query: sql`SELECT wallet, toDate(block_time) AS day, sum(amount) AS net FROM ${ledger} GROUP BY wallet, day`,
110
+ });
111
+ ```
112
+
113
+ - Column names are snake_cased in ClickHouse (`blockTime` → `block_time`), keys stay camelCase in TypeScript.
114
+ - Every table gets three hidden columns (`_yail_chain`, `_yail_block`, `_yail_version`). They make re-indexing and versioned dedup work. Opt out with `indexMeta: false` for aggregate targets.
115
+ - Default engine is `ReplacingMergeTree(_yail_version)`: re-inserting a row with the same key (re-run, backfill overlap, crash replay) never double counts. Read with `FINAL` (`db.rows()` does) or let merges settle.
116
+ - Materialized views are first class: created by `yail migrate`, backfilled over existing data with `yail migrate --populate`.
117
+
118
+ ### Handlers
119
+
120
+ ```ts
121
+ indexer.on("Pool:Swap", async ({ event, context }) => {
122
+ event.args // typed from the ABI
123
+ event.log, event.block, event.address, event.transaction /* with includeTransactions: true */
124
+ context.chain // { name, id, kind }
125
+ context.db // insert (buffered), find (buffer-aware), rows, query, command
126
+ context.client // viem readContract with a ClickHouse cache keyed by block (needs `rpc` on the chain)
127
+ context.http // fetch with optional ClickHouse cache: http.get(url, { cache: true })
128
+ context.addresses // has/register/list for address sets
129
+ context.log // evlog wide event of the current batch: log.set({ ... })
130
+ });
131
+ indexer.on("Wallets:transaction", ...) // EVM: { transaction, block, address, direction: "from"|"to"|"self" }
132
+ // Bitcoin: { tx, block, address, received, sent, net, fee, isSender }
133
+ indexer.on("setup", ...) // once per chain before indexing starts
134
+ ```
135
+
136
+ Writes are buffered per source batch and flushed as one INSERT per table before the checkpoint is written. Events are processed in `(block, txIndex, logIndex)` order per chain; chains run concurrently. Only events with a registered handler are fetched from the source.
137
+
138
+ `context.db.find(table, key)` looks at the current batch's buffer first, then ClickHouse. It is fine for last-value state (a pool's current tick), but the ClickHouse way is to write immutable facts and let `SummingMergeTree`/`AggregatingMergeTree` materialized views maintain aggregates.
139
+
140
+ ### Dynamic addresses and backfill
141
+
142
+ `addressSet("wallets")` can be used as a contract's `address`, in an event `filter`, or as an account's `address`. Members live in `_yail_addresses` and can be added at any time:
143
+
144
+ ```ts
145
+ await indexer.addresses.register({ set: "wallets", address: "0xabc…", fromBlock: 30_000_000 });
146
+ ```
147
+ ```sh
148
+ yail addresses add --set wallets 0xabc… 0xdef…
149
+ curl -X POST :42069/addresses -d '{"set":"wallets","address":["0xabc…"]}'
150
+ ```
151
+
152
+ What happens: the live loop adopts the address at its current block `N` and includes it from then on; a job backfills `[fromBlock, N)` with a query restricted to that address; the address flips to `live`. No gaps, no double processing. Progress is visible on `/status` and `/jobs`. Handlers see `context.backfill === true` during the backfill.
153
+
154
+ `factory({ address, event, parameter })` (Ponder style) discovers child contracts from a factory event; children found inside a batch have their logs fetched for the rest of that batch, so ordering is exact.
155
+
156
+ ### Re-indexing
157
+
158
+ ```sh
159
+ yail reindex --chain base --scope wallet --value 0xabc… # delete rows where wallet = … in every table declaring that scope, re-run the address history
160
+ yail reindex --chain base --from 30000000 --to 30001000 # delete rows by _yail_block range, re-run the range
161
+ ```
162
+ Both go through the same durable job queue (`_yail_jobs`), also reachable via `POST /reindex` and `indexer.reindex()`.
163
+
164
+ ### Caching
165
+
166
+ - `cache.source` (or wrapping a source in `cached()`): raw source pages stored in `_yail_source_cache`, keyed by query shape. Re-runs with the same filters never hit HyperSync again. Off by default: HyperSync is fast. The key includes the address-set membership at query time, so a set that keeps growing produces new keys; the cache pays off for static filters and for re-running the same state (dev loops, handler fixes).
167
+ - `cache.http` (default on): `context.http.get(url, { cache: true | ttlMs })` persists responses in `_yail_http_cache`. Historical prices and swap classification stay deterministic across re-indexes.
168
+ - `cache.rpc` (default on): `context.client.readContract` results keyed by `(chain, block, to, calldata)` in `_yail_rpc_cache`.
169
+
170
+ ### Observability
171
+
172
+ Logs are [evlog](https://www.evlog.dev) wide events: one per batch (chain, block range, events, rows, timings) and one per job, plus structured errors with `why`/`fix`. Metrics and traces use the OpenTelemetry API:
173
+
174
+ | metric | alert on |
175
+ |---|---|
176
+ | `yail.chain.lag_blocks` (gauge, per chain) | growing lag = source or handler problem |
177
+ | `yail.chain.caught_up` | 0 for longer than expected |
178
+ | `yail.errors` (counter, by stage) | any increase |
179
+ | `yail.jobs` (counter, by outcome) | `outcome=failed` |
180
+ | `yail.batch.duration`, `yail.handler.duration`, `yail.flush.duration` | latency histograms |
181
+ | `yail.cache.events` | hit/miss by cache |
182
+
183
+ Export everything with one call:
184
+
185
+ ```ts
186
+ import { startTelemetry } from "@notuslabs/yail/otel";
187
+ startTelemetry({ serviceName: "wallet-ledger", endpoint: "http://otel-collector:4318" }); // traces + metrics over OTLP/HTTP
188
+ ```
189
+ Set `observability.otlpEndpoint` (or `OTEL_EXPORTER_OTLP_ENDPOINT`) to ship logs over OTLP too, and `observability.logsToClickHouse: true` to keep them in `_yail_logs` next to your data (zero extra infrastructure). Recent events are always available at `GET /_evlog/logs`.
190
+
191
+ HTTP status API (`server.port`, default 42069): `/health`, `/ready` (caught up on every chain), `/status`, `/addresses`, `/reindex`, `/jobs`.
192
+
193
+ ### Testing with real data
194
+
195
+ ```ts
196
+ import { startTestClickHouse, fixtureSource, readFixture } from "@notuslabs/yail/testing";
197
+
198
+ const ch = await startTestClickHouse(); // YAIL_TEST_CLICKHOUSE_URL or a testcontainer
199
+ const fixture = readFixture("test/fixtures/base-univ3-weth-usdc.json");
200
+ const config = createConfig({ database: ch.database(), chains: { base: { id: 8453, source: fixtureSource(fixture), finality: 0 } }, contracts: { … } });
201
+ const indexer = createIndexer({ config, schema });
202
+ indexer.on("Pool:Swap", …);
203
+ await indexer.run(); // index to the fixture height, drain jobs, stop
204
+ expect((await indexer.db.find(poolState, { pool }))!.liquidity).toBe(fixture.expected.liquidity);
205
+ ```
206
+
207
+ Fixtures are recorded from a real chain (`yail record --chain base --from … --to … --out fixture.json`, or `recordEvmFixture` in a script) and can carry `expected` ground truth read at the same block. `packages/yail/test/real-data.test.ts` checks a Uniswap V3 pool's price and liquidity against `slot0()`, USDC wallet deltas against `balanceOf()`, and Bitcoin address totals against Esplora stats.
208
+
209
+ ## CLI
210
+
211
+ ```
212
+ yail start [entry] run (entry exports the indexer; default src/index.ts)
213
+ yail dev [entry] run, restart on changes
214
+ yail migrate [entry] --populate | --reset
215
+ yail reindex [entry] --chain … (--from … --to … | --scope … --value …)
216
+ yail addresses add|list --set … [--chain …] [--from-block …]
217
+ yail record [entry] --chain … --from … --to … --out …
218
+ yail status --url http://localhost:42069
219
+ ```
220
+
221
+ ## Limits and notes
222
+
223
+ - Bitcoin is scanned per address through Esplora (HyperSync has no Bitcoin, Bitcoin Core has no address index). Fine for wallet sets in the thousands; for more, point `esplora({ url })` at your own electrs.
224
+ - Traces / internal ETH transfers are not indexed (not exposed by HyperSync on most chains without a paid add-on).
225
+ - ClickHouse has no transactions: a crash between a table flush and the checkpoint is repaired by the ReplacingMergeTree key on the next run, so give every table a real identity key.
226
+ - Cross-chain ordering is not enforced (chains are independent loops).
package/bin/yail.js ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+ import { run } from "../dist/cli/index.js";
3
+
4
+ run(process.argv.slice(2)).catch((err) => {
5
+ console.error(err);
6
+ process.exit(1);
7
+ });
@@ -0,0 +1,57 @@
1
+ import type { Db } from "../db/client.js";
2
+ import { addresses as addressesTable } from "../db/internal.js";
3
+ import type { InferRow } from "../schema/table.js";
4
+ export type AddressStatus = "pending" | "backfilling" | "live" | "failed";
5
+ export type AddressRow = InferRow<typeof addressesTable>;
6
+ export interface RegisterInput {
7
+ set: string;
8
+ chain: string;
9
+ address: string;
10
+ /** First block to index this address from. Defaults to the earliest startBlock using the set on that chain. */
11
+ fromBlock?: number;
12
+ meta?: Record<string, unknown>;
13
+ /** Internal: mark as live immediately (factory children, seeds at genesis). */
14
+ status?: AddressStatus;
15
+ adoptedAtBlock?: number;
16
+ }
17
+ /**
18
+ * In-memory view of `_yail_addresses`, persisted on every change.
19
+ * Single-writer: the indexer process owns it.
20
+ */
21
+ export declare class AddressRegistry {
22
+ private readonly db;
23
+ private readonly normalize;
24
+ private rows;
25
+ private listeners;
26
+ constructor(db: Db, normalize: (chain: string, address: string) => string);
27
+ /**
28
+ * Load rows from the database, merging into memory. A row from the database
29
+ * only replaces the in-memory one when it is newer, so a refresh racing with
30
+ * a write made by another chain loop never reverts that write.
31
+ */
32
+ load(): Promise<void>;
33
+ onChange(fn: (row: AddressRow) => void): () => void;
34
+ get(set: string, chain: string, address: string): AddressRow | undefined;
35
+ has(set: string, chain: string, address: string): boolean;
36
+ /** Addresses the live loop should include for a set on a chain (everything adopted). */
37
+ live(set: string, chain: string): string[];
38
+ list(filter?: {
39
+ set?: string;
40
+ chain?: string;
41
+ status?: AddressStatus;
42
+ }): AddressRow[];
43
+ /** Newly registered addresses on a chain not yet adopted by the live loop. */
44
+ unadopted(chain: string): AddressRow[];
45
+ register(input: RegisterInput | RegisterInput[]): Promise<AddressRow[]>;
46
+ update(set: string, chain: string, address: string, patch: Partial<Pick<AddressRow, "status" | "adopted" | "adoptedAtBlock" | "error" | "fromBlock">>): Promise<AddressRow>;
47
+ updateMany(rows: AddressRow[], patch: Partial<Pick<AddressRow, "status" | "adopted" | "adoptedAtBlock" | "error">>): Promise<void>;
48
+ /** Sets that contain an address on a chain (used by scope re-index). */
49
+ setsContaining(chain: string, address: string): AddressRow[];
50
+ counts(): Promise<Array<{
51
+ set: string;
52
+ chain: string;
53
+ status: string;
54
+ count: number;
55
+ }>>;
56
+ }
57
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/addresses/registry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,131 @@
1
+ import { addresses as addressesTable } from "../db/internal.js";
2
+ import { sql } from "../db/sql.js";
3
+ import { toArray } from "../util.js";
4
+ function key(set, chain, address) {
5
+ return `${set}\u0001${chain}\u0001${address}`;
6
+ }
7
+ /**
8
+ * In-memory view of `_yail_addresses`, persisted on every change.
9
+ * Single-writer: the indexer process owns it.
10
+ */
11
+ export class AddressRegistry {
12
+ db;
13
+ normalize;
14
+ rows = new Map();
15
+ listeners = new Set();
16
+ constructor(db, normalize) {
17
+ this.db = db;
18
+ this.normalize = normalize;
19
+ }
20
+ /**
21
+ * Load rows from the database, merging into memory. A row from the database
22
+ * only replaces the in-memory one when it is newer, so a refresh racing with
23
+ * a write made by another chain loop never reverts that write.
24
+ */
25
+ async load() {
26
+ const rows = await this.db.rows(addressesTable);
27
+ for (const r of rows) {
28
+ const k = key(r.set, r.chain, r.address);
29
+ const existing = this.rows.get(k);
30
+ if (existing && existing.updatedAt.getTime() > r.updatedAt.getTime())
31
+ continue;
32
+ this.rows.set(k, r);
33
+ }
34
+ }
35
+ onChange(fn) {
36
+ this.listeners.add(fn);
37
+ return () => this.listeners.delete(fn);
38
+ }
39
+ get(set, chain, address) {
40
+ return this.rows.get(key(set, chain, this.normalize(chain, address)));
41
+ }
42
+ has(set, chain, address) {
43
+ const r = this.get(set, chain, address);
44
+ return r !== undefined && r.status !== "failed";
45
+ }
46
+ /** Addresses the live loop should include for a set on a chain (everything adopted). */
47
+ live(set, chain) {
48
+ const out = [];
49
+ for (const r of this.rows.values()) {
50
+ if (r.set === set && r.chain === chain && r.status !== "failed" && r.adopted)
51
+ out.push(r.address);
52
+ }
53
+ return out.sort(); // deterministic order: query shapes (and cache keys) must not depend on load order
54
+ }
55
+ list(filter = {}) {
56
+ return [...this.rows.values()].filter((r) => (!filter.set || r.set === filter.set) && (!filter.chain || r.chain === filter.chain) && (!filter.status || r.status === filter.status));
57
+ }
58
+ /** Newly registered addresses on a chain not yet adopted by the live loop. */
59
+ unadopted(chain) {
60
+ return [...this.rows.values()].filter((r) => r.chain === chain && !r.adopted && r.status === "pending");
61
+ }
62
+ async register(input) {
63
+ const inputs = toArray(input);
64
+ const now = new Date();
65
+ const out = [];
66
+ for (const i of inputs) {
67
+ const address = this.normalize(i.chain, i.address);
68
+ const existing = this.rows.get(key(i.set, i.chain, address));
69
+ if (existing && existing.status !== "failed") {
70
+ out.push(existing);
71
+ continue;
72
+ }
73
+ const row = {
74
+ set: i.set,
75
+ chain: i.chain,
76
+ address,
77
+ status: i.status ?? "pending",
78
+ fromBlock: BigInt(i.fromBlock ?? 0),
79
+ adopted: i.adoptedAtBlock !== undefined,
80
+ adoptedAtBlock: BigInt(i.adoptedAtBlock ?? 0),
81
+ meta: i.meta ?? {},
82
+ error: "",
83
+ registeredAt: existing?.registeredAt ?? now,
84
+ updatedAt: now,
85
+ };
86
+ this.rows.set(key(row.set, row.chain, row.address), row);
87
+ out.push(row);
88
+ }
89
+ const fresh = out.filter((r) => r.updatedAt === now);
90
+ if (fresh.length > 0)
91
+ await this.db.insert(addressesTable).values(fresh);
92
+ for (const r of fresh)
93
+ for (const l of this.listeners)
94
+ l(r);
95
+ return out;
96
+ }
97
+ async update(set, chain, address, patch) {
98
+ const k = key(set, chain, this.normalize(chain, address));
99
+ const existing = this.rows.get(k);
100
+ if (!existing)
101
+ throw new Error(`address ${address} not registered in set ${set} on ${chain}`);
102
+ const row = { ...existing, ...patch, updatedAt: new Date() };
103
+ this.rows.set(k, row);
104
+ await this.db.insert(addressesTable).values(row);
105
+ for (const l of this.listeners)
106
+ l(row);
107
+ return row;
108
+ }
109
+ async updateMany(rows, patch) {
110
+ if (rows.length === 0)
111
+ return;
112
+ const now = new Date();
113
+ const updated = rows.map((r) => ({ ...r, ...patch, updatedAt: now }));
114
+ for (const r of updated)
115
+ this.rows.set(key(r.set, r.chain, r.address), r);
116
+ await this.db.insert(addressesTable).values(updated);
117
+ for (const r of updated)
118
+ for (const l of this.listeners)
119
+ l(r);
120
+ }
121
+ /** Sets that contain an address on a chain (used by scope re-index). */
122
+ setsContaining(chain, address) {
123
+ const a = this.normalize(chain, address);
124
+ return [...this.rows.values()].filter((r) => r.chain === chain && r.address === a);
125
+ }
126
+ async counts() {
127
+ const rows = await this.db.query(sql `SELECT set, chain, status, count() AS count FROM ${addressesTable} FINAL GROUP BY set, chain, status ORDER BY set, chain, status`);
128
+ return rows.map((r) => ({ ...r, count: Number(r.count) }));
129
+ }
130
+ }
131
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/addresses/registry.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,IAAI,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAChE,OAAO,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AAEnC,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAiBrC,SAAS,GAAG,CAAC,GAAW,EAAE,KAAa,EAAE,OAAe;IACtD,OAAO,GAAG,GAAG,SAAS,KAAK,SAAS,OAAO,EAAE,CAAC;AAChD,CAAC;AAED;;;GAGG;AACH,MAAM,OAAO,eAAe;IAIG,EAAE;IAAuB,SAAS;IAHvD,IAAI,GAAG,IAAI,GAAG,EAAsB,CAAC;IACrC,SAAS,GAAG,IAAI,GAAG,EAA6B,CAAC;IAEzD,YAA6B,EAAM,EAAmB,SAAqD;kBAA9E,EAAE;yBAAuB,SAAS;IAA+C,CAAC;IAE/G;;;;OAIG;IACH,KAAK,CAAC,IAAI;QACR,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAChD,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;YACrB,MAAM,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC;YACzC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YAClC,IAAI,QAAQ,IAAI,QAAQ,CAAC,SAAS,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,SAAS,CAAC,OAAO,EAAE;gBAAE,SAAS;YAC/E,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACtB,CAAC;IACH,CAAC;IAED,QAAQ,CAAC,EAA6B;QACpC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACvB,OAAO,GAAG,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACzC,CAAC;IAED,GAAG,CAAC,GAAW,EAAE,KAAa,EAAE,OAAe;QAC7C,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;IACxE,CAAC;IAED,GAAG,CAAC,GAAW,EAAE,KAAa,EAAE,OAAe;QAC7C,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;QACxC,OAAO,CAAC,KAAK,SAAS,IAAI,CAAC,CAAC,MAAM,KAAK,QAAQ,CAAC;IAClD,CAAC;IAED,wFAAwF;IACxF,IAAI,CAAC,GAAW,EAAE,KAAa;QAC7B,MAAM,GAAG,GAAa,EAAE,CAAC;QACzB,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;YACnC,IAAI,CAAC,CAAC,GAAG,KAAK,GAAG,IAAI,CAAC,CAAC,KAAK,KAAK,KAAK,IAAI,CAAC,CAAC,MAAM,KAAK,QAAQ,IAAI,CAAC,CAAC,OAAO;gBAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;QACpG,CAAC;QACD,OAAO,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,mFAAmF;IACxG,CAAC;IAED,IAAI,CAAC,MAAM,GAA6D,EAAE;QACxE,OAAO,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CACnC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,KAAK,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,CAC9I,CAAC;IACJ,CAAC;IAED,8EAA8E;IAC9E,SAAS,CAAC,KAAa;QACrB,OAAO,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC;IAC1G,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,KAAsC;QACnD,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;QAC9B,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,GAAG,GAAiB,EAAE,CAAC;QAC7B,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;YACvB,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC;YACnD,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC;YAC7D,IAAI,QAAQ,IAAI,QAAQ,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;gBAC7C,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;gBACnB,SAAS;YACX,CAAC;YACD,MAAM,GAAG,GAAe;gBACtB,GAAG,EAAE,CAAC,CAAC,GAAG;gBACV,KAAK,EAAE,CAAC,CAAC,KAAK;gBACd,OAAO;gBACP,MAAM,EAAE,CAAC,CAAC,MAAM,IAAI,SAAS;gBAC7B,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC;gBACnC,OAAO,EAAE,CAAC,CAAC,cAAc,KAAK,SAAS;gBACvC,cAAc,EAAE,MAAM,CAAC,CAAC,CAAC,cAAc,IAAI,CAAC,CAAC;gBAC7C,IAAI,EAAE,CAAC,CAAC,IAAI,IAAI,EAAE;gBAClB,KAAK,EAAE,EAAE;gBACT,YAAY,EAAE,QAAQ,EAAE,YAAY,IAAI,GAAG;gBAC3C,SAAS,EAAE,GAAG;aACf,CAAC;YACF,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,OAAO,CAAC,EAAE,GAAG,CAAC,CAAC;YACzD,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAChB,CAAC;QACD,MAAM,KAAK,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,GAAG,CAAC,CAAC;QACrD,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;YAAE,MAAM,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACzE,KAAK,MAAM,CAAC,IAAI,KAAK;YAAE,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,SAAS;gBAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QAC5D,OAAO,GAAG,CAAC;IACb,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,GAAW,EAAE,KAAa,EAAE,OAAe,EAAE,KAAiG;QACzJ,MAAM,CAAC,GAAG,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC;QAC1D,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAClC,IAAI,CAAC,QAAQ;YAAE,MAAM,IAAI,KAAK,CAAC,WAAW,OAAO,0BAA0B,GAAG,OAAO,KAAK,EAAE,CAAC,CAAC;QAC9F,MAAM,GAAG,GAAe,EAAE,GAAG,QAAQ,EAAE,GAAG,KAAK,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,EAAE,CAAC;QACzE,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;QACtB,MAAM,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACjD,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,SAAS;YAAE,CAAC,CAAC,GAAG,CAAC,CAAC;QACvC,OAAO,GAAG,CAAC;IACb,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,IAAkB,EAAE,KAAmF;QACtH,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QAC9B,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,GAAG,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC;QACtE,KAAK,MAAM,CAAC,IAAI,OAAO;YAAE,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC;QAC1E,MAAM,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACrD,KAAK,MAAM,CAAC,IAAI,OAAO;YAAE,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,SAAS;gBAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAChE,CAAC;IAED,wEAAwE;IACxE,cAAc,CAAC,KAAa,EAAE,OAAe;QAC3C,MAAM,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QACzC,OAAO,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,KAAK,IAAI,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,CAAC;IACrF,CAAC;IAED,KAAK,CAAC,MAAM;QACV,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,KAAK,CAC9B,GAAG,CAAA,oDAAoD,cAAc,gEAAgE,CACtI,CAAC;QACF,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;IAC7D,CAAC;CACF"}
@@ -0,0 +1,2 @@
1
+ export declare function run(argv: string[]): Promise<void>;
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"AA4BA,wBAAsB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAwHvD"}