univo 0.4.0 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/realtime.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { hexToNumber } from 'viem';
2
- import { c as createLogger, b as iife, m as mutex, r as retry, i as isHexEqual } from './shared/univo.mlthZw0m.mjs';
2
+ import { c as createLogger, b as iife, m as mutex, l as lifecycle, q as queue, r as retry, i as isHexEqual } from './shared/univo.CUNaTwXY.mjs';
3
3
 
4
4
  function defineBlockchain(opts) {
5
5
  const log = createLogger({ quiet: opts.quiet });
@@ -77,156 +77,357 @@ function defineBlockchain(opts) {
77
77
  await reconcile(parentBlock);
78
78
  return await reconcile(newBlock);
79
79
  }
80
+ const enqueue = queue();
80
81
  return {
81
82
  chain,
82
- prune: mutex(prune),
83
- reconcile: mutex(reconcile)
83
+ prune: (tail) => enqueue(() => prune(tail)),
84
+ reconcile: (newBlock) => enqueue(() => reconcile(newBlock))
84
85
  };
85
86
  }
86
87
  const POLLING_INTERVAL_MS = 12 * 1e3;
87
- function realtime(opts) {
88
- const log = createLogger({ quiet: opts.quiet ?? false, prefix: "[realtime]" });
89
- async function getBlockByHash(hash) {
90
- const block = await opts.node.request({ method: "eth_getBlockByHash", params: [hash, false] });
91
- if (block === null) {
92
- return null;
93
- }
94
- return { number: block.number, hash: block.hash, parent_hash: block.parentHash };
95
- }
96
- const promise = iife(async () => {
97
- log.debug("Initialising realtime client for indexer");
98
- const chain = await opts.node.request({ method: "eth_chainId", params: [] });
99
- log.debug(`Determined chain identifier for connected node: ${hexToNumber(chain)}`);
100
- const [chainLatestBlock, chainFinalizedBlock] = await Promise.all([
101
- opts.node.request({ method: "eth_getBlockByNumber", params: ["latest", false] }),
102
- //
103
- opts.node.request({ method: "eth_getBlockByNumber", params: ["finalized", false] })
104
- ]);
105
- if (chainLatestBlock === null) {
106
- throw new Error("Latest block not found");
107
- }
108
- if (chainFinalizedBlock === null) {
109
- throw new Error("Finalized block not found");
110
- }
111
- const unfinalized = defineBlockchain({
112
- getBlockByHash,
113
- quiet: opts.quiet ?? false,
114
- onBlockAdded: async (head) => {
88
+ function realtime(realtime2) {
89
+ const log = createLogger({ quiet: realtime2.quiet, prefix: "[realtime]" });
90
+ const instance = ({ chain, node }) => {
91
+ const { isDisposed, onCleanup, interrupt } = lifecycle({
92
+ onCleanupError: (error) => {
93
+ if (error instanceof Error) {
94
+ log.warn(error.message);
95
+ }
96
+ }
97
+ });
98
+ const promise = iife(async () => {
99
+ const [chainLatestBlock, chainFinalizedBlock] = await Promise.all([
100
+ node.request({ method: "eth_getBlockByNumber", params: ["latest", false] }),
101
+ //
102
+ node.request({ method: "eth_getBlockByNumber", params: ["finalized", false] })
103
+ ]);
104
+ if (isDisposed()) {
105
+ return;
106
+ }
107
+ if (chainLatestBlock === null) {
108
+ throw new Error("Latest block not found");
109
+ }
110
+ if (chainFinalizedBlock === null) {
111
+ throw new Error("Finalized block not found");
112
+ }
113
+ const unfinalized = defineBlockchain({
114
+ quiet: realtime2.quiet,
115
+ getBlockByHash: async (hash) => {
116
+ const block = await node.request({ method: "eth_getBlockByHash", params: [hash, false] });
117
+ if (block === null) {
118
+ return null;
119
+ }
120
+ return { number: block.number, hash: block.hash, parent_hash: block.parentHash };
121
+ },
122
+ onBlockAdded: async (head) => {
123
+ try {
124
+ log.debug("Received unfinalized head");
125
+ await realtime2.indexer.request({
126
+ method: "public_writeUnfinalizedHead",
127
+ params: [{ chain, ...head }]
128
+ });
129
+ log.debug("Delivered unfinalized head");
130
+ } catch (error) {
131
+ if (error instanceof Error) {
132
+ log.warn(`Failed to write unfinalized head: ${error.message}`);
133
+ }
134
+ }
135
+ },
136
+ onBlockReorganised: async (head) => {
137
+ try {
138
+ log.debug("Received reorganised head");
139
+ await retry(
140
+ () => realtime2.indexer.request({ method: "public_deleteReorganisedHead", params: [{ chain, ...head }] }),
141
+ //
142
+ 2
143
+ );
144
+ log.debug("Delivered reorganised head");
145
+ } catch (error) {
146
+ if (error instanceof Error) {
147
+ log.warn(`Failed to write reorganised head: ${error.message}`);
148
+ }
149
+ }
150
+ }
151
+ });
152
+ await unfinalized.reconcile({
153
+ hash: chainLatestBlock.hash,
154
+ number: chainLatestBlock.number,
155
+ parent_hash: chainLatestBlock.parentHash
156
+ });
157
+ if (isDisposed()) {
158
+ return;
159
+ }
160
+ log.debug("Subscribing unfinalized chain to new heads...");
161
+ const unsubscribe = await node.subscribe("newHeads", async (head) => {
162
+ await unfinalized.reconcile({ hash: head.hash, number: head.number, parent_hash: head.parentHash }).catch((error) => {
163
+ if (error instanceof Error) {
164
+ log.error(`Failed to reconcile unfinalized head: ${error.message}`);
165
+ }
166
+ });
167
+ });
168
+ onCleanup(() => unsubscribe());
169
+ if (isDisposed()) {
170
+ return;
171
+ }
172
+ log.debug("Subscribed unfinalized chain to new heads");
173
+ async function finalize() {
115
174
  try {
116
- log.debug("Received unfinalized head");
117
- await opts.indexer.request({
118
- method: "public_writeUnfinalizedHead",
119
- params: [{ chain, ...head }]
120
- });
121
- log.debug("Delivered unfinalized head");
175
+ log.debug("Finalizing indexer...");
176
+ await realtime2.indexer.request({ params: [chain], method: "public_finalize" });
177
+ log.debug("Indexer finalized");
122
178
  } catch (error) {
123
179
  if (error instanceof Error) {
124
- log.warn(`Failed to write unfinalized head: ${error.message}`);
180
+ log.warn(`Failed to finalize: ${error.message}`);
125
181
  }
126
182
  }
127
- },
128
- onBlockReorganised: async (head) => {
183
+ }
184
+ const mutex_finalize = mutex(finalize);
185
+ let chainFinalizedHeight = hexToNumber(chainFinalizedBlock.number);
186
+ async function poll() {
129
187
  try {
130
- log.debug("Received reorganised head");
131
- await retry(
132
- () => opts.indexer.request({ method: "public_deleteReorganisedHead", params: [{ chain, ...head }] }),
133
- //
134
- 2
135
- );
136
- log.debug("Delivered reorganised head");
188
+ log.debug("Polling for finalized height...");
189
+ const finalizedBlock = await node.request({
190
+ method: "eth_getBlockByNumber",
191
+ params: ["finalized", false]
192
+ });
193
+ if (finalizedBlock === null) {
194
+ throw new Error("Finalized block not found");
195
+ }
196
+ const nextFinalizedHeight = hexToNumber(finalizedBlock.number);
197
+ if (nextFinalizedHeight <= chainFinalizedHeight) {
198
+ return log.debug("Chain finalized height has not progressed");
199
+ }
200
+ await realtime2.indexer.request({ params: [chain], method: "public_writeFinalizedHeight" });
201
+ const newFinalizedHeads = unfinalized.chain.filter((head) => {
202
+ if (hexToNumber(head.number) > chainFinalizedHeight && hexToNumber(head.number) <= nextFinalizedHeight) {
203
+ return true;
204
+ }
205
+ return false;
206
+ }).map((head) => {
207
+ return { chain, ...head };
208
+ });
209
+ if (newFinalizedHeads.length === 0) {
210
+ chainFinalizedHeight = nextFinalizedHeight;
211
+ return log.debug("No new finalized heads to process");
212
+ }
213
+ log.debug(`Processing ${newFinalizedHeads.length} finalized head(s) in parallel`);
214
+ const promises = newFinalizedHeads.map(async (head) => {
215
+ try {
216
+ log.debug("Received finalized head");
217
+ await realtime2.indexer.request({ params: [head], method: "public_writeFinalizedHead" });
218
+ log.debug("Delivered finalized head");
219
+ } catch (error) {
220
+ if (error instanceof Error) {
221
+ log.warn(`Failed to write finalized head: ${error.message}`);
222
+ }
223
+ }
224
+ });
225
+ await Promise.allSettled(promises);
226
+ log.debug(`Processed ${newFinalizedHeads.length} finalized head(s) in parallel`);
227
+ chainFinalizedHeight = nextFinalizedHeight;
228
+ await unfinalized.prune({
229
+ hash: finalizedBlock.hash,
230
+ number: finalizedBlock.number,
231
+ parent_hash: finalizedBlock.parentHash
232
+ });
233
+ await mutex_finalize();
137
234
  } catch (error) {
138
235
  if (error instanceof Error) {
139
- log.warn(`Failed to write reorganised head: ${error.message}`);
236
+ log.error(`Failed to reconcile finalized head: ${error.message}`);
140
237
  }
141
238
  }
142
239
  }
240
+ const id = setInterval(poll, POLLING_INTERVAL_MS);
241
+ onCleanup(() => clearInterval(id));
242
+ log.debug("Started polling finalized height");
143
243
  });
144
- await unfinalized.reconcile({
145
- hash: chainLatestBlock.hash,
146
- number: chainLatestBlock.number,
147
- parent_hash: chainLatestBlock.parentHash
148
- });
149
- await opts.node.subscribe("newHeads", async (head) => {
150
- await unfinalized.reconcile({ hash: head.hash, number: head.number, parent_hash: head.parentHash }).catch((error) => {
151
- if (error instanceof Error) {
152
- log.error(`Failed to reconcile unfinalized head: ${error.message}`);
153
- }
154
- });
244
+ promise.catch((error) => {
245
+ if (error instanceof Error) {
246
+ log.error(error.message);
247
+ }
248
+ interrupt();
155
249
  });
156
- log.debug("Subscribed unfinalized chain to new heads");
157
- async function finalize() {
158
- try {
159
- log.debug("Finalizing indexer...");
160
- await opts.indexer.request({ params: [chain], method: "public_finalize" });
161
- log.debug("Indexer finalized");
162
- } catch (error) {
250
+ return interrupt;
251
+ };
252
+ const browser = (browser2) => {
253
+ const { isDisposed, onCleanup, interrupt } = lifecycle({
254
+ onCleanupError: (error) => {
163
255
  if (error instanceof Error) {
164
- log.warn(`Failed to finalize: ${error.message}`);
256
+ log.warn(error.message);
165
257
  }
166
258
  }
259
+ });
260
+ browser2.signal.addEventListener("abort", interrupt, { once: true });
261
+ onCleanup(() => browser2.signal.removeEventListener("abort", interrupt));
262
+ if (browser2.signal.aborted) {
263
+ interrupt();
167
264
  }
168
- const mutex_finalize = mutex(finalize);
169
- let chainFinalizedHeight = hexToNumber(chainFinalizedBlock.number);
170
- async function poll() {
171
- try {
172
- log.debug("Polling for finalized height...");
173
- const finalizedBlock = await opts.node.request({
174
- method: "eth_getBlockByNumber",
175
- params: ["finalized", false]
176
- });
177
- if (finalizedBlock === null) {
178
- throw new Error("Finalized block not found");
265
+ const promise = iife(async () => {
266
+ if (isDisposed()) {
267
+ return;
268
+ }
269
+ const subscriptions = {
270
+ node: /* @__PURE__ */ new Map(),
271
+ abstraction: /* @__PURE__ */ new Map()
272
+ };
273
+ const transport = {
274
+ protocol: "browser",
275
+ request: async (request) => {
276
+ if (request.method !== "eth_unsubscribe") {
277
+ browser2.signal.throwIfAborted();
278
+ }
279
+ const res = await browser2.provider.request({ method: request.method, params: request.params });
280
+ if (request.method !== "eth_unsubscribe" && request.method !== "eth_subscribe") {
281
+ browser2.signal.throwIfAborted();
282
+ }
283
+ return res;
284
+ },
285
+ subscribe: async (param, listener) => {
286
+ const listeners = subscriptions.abstraction.get(param);
287
+ if (listeners === void 0) {
288
+ const listeners2 = /* @__PURE__ */ new Set();
289
+ listeners2.add(listener);
290
+ subscriptions.abstraction.set(param, listeners2);
291
+ const id = await transport.request({ method: "eth_subscribe", params: [param] }).catch((cause) => {
292
+ throw new Error(`Failed to initialise subscription for param ${param}`, { cause });
293
+ });
294
+ subscriptions.node.set(id, param);
295
+ } else {
296
+ listeners.add(listener);
297
+ }
298
+ return async function unsubscribe() {
299
+ const listeners2 = subscriptions.abstraction.get(param);
300
+ if (listeners2 === void 0) {
301
+ return;
302
+ }
303
+ listeners2.delete(listener);
304
+ if (listeners2.size === 0) {
305
+ for (const [id, _param] of subscriptions.node) {
306
+ if (param === _param) {
307
+ await transport.request({ method: "eth_unsubscribe", params: [id] }).catch(() => {
308
+ log.warn("Failed to unsubscribe unused subscription");
309
+ });
310
+ }
311
+ }
312
+ }
313
+ };
179
314
  }
180
- const nextFinalizedHeight = hexToNumber(finalizedBlock.number);
181
- if (nextFinalizedHeight <= chainFinalizedHeight) {
182
- return log.debug("Chain finalized height has not progressed");
315
+ };
316
+ const onMessage = async (message) => {
317
+ if (message.type === "eth_subscription") {
318
+ const param = subscriptions.node.get(message.data.subscription);
319
+ if (param === void 0) {
320
+ return;
321
+ }
322
+ const listeners = subscriptions.abstraction.get(param);
323
+ if (listeners === void 0) {
324
+ return await transport.request({ method: "eth_unsubscribe", params: [message.data.subscription] }).catch(() => {
325
+ log.warn("Failed to unsubscribe stale subscription");
326
+ });
327
+ }
328
+ for (const listener of listeners) {
329
+ try {
330
+ listener(message.data.result);
331
+ } catch (error) {
332
+ if (error instanceof Error) {
333
+ log.error(error.message);
334
+ }
335
+ }
336
+ }
183
337
  }
184
- await opts.indexer.request({ params: [chain], method: "public_writeFinalizedHeight" });
185
- const newFinalizedHeads = unfinalized.chain.filter((head) => {
186
- if (hexToNumber(head.number) > chainFinalizedHeight && hexToNumber(head.number) <= nextFinalizedHeight) {
187
- return true;
188
- }
189
- return false;
190
- }).map((head) => {
191
- return { chain, ...head };
192
- });
193
- if (newFinalizedHeads.length === 0) {
194
- chainFinalizedHeight = nextFinalizedHeight;
195
- return log.debug("No new finalized heads to process");
338
+ };
339
+ browser2.provider.on("message", onMessage);
340
+ onCleanup(() => browser2.provider.removeListener("message", onMessage));
341
+ const _interrupt = instance({ chain: browser2.chain, node: transport });
342
+ onCleanup(() => _interrupt());
343
+ });
344
+ promise.catch((error) => {
345
+ if (error instanceof Error) {
346
+ log.error(error.message);
347
+ }
348
+ interrupt();
349
+ });
350
+ return interrupt;
351
+ };
352
+ iife(async () => {
353
+ try {
354
+ log.debug("Initialising realtime client");
355
+ if (realtime2.node.protocol === "wss") {
356
+ log.debug("Found user provided node");
357
+ const [chain, metadata] = await Promise.all([
358
+ realtime2.node.request({ method: "eth_chainId", params: [] }),
359
+ realtime2.indexer.request({ method: "public_getMetadata", params: [] })
360
+ ]);
361
+ log.debug(`Determined chain identifier for connected node: ${hexToNumber(chain)}`);
362
+ if (!metadata.chains.some((_chain) => _chain === chain)) {
363
+ throw new Error("Provided `node` is connected to a chain not indexed by the provided `indexer`");
196
364
  }
197
- log.debug(`Processing ${newFinalizedHeads.length} finalized head(s) in parallel`);
198
- const promises = newFinalizedHeads.map(async (head) => {
199
- try {
200
- log.debug("Received finalized head");
201
- await opts.indexer.request({ params: [head], method: "public_writeFinalizedHead" });
202
- log.debug("Delivered finalized head");
203
- } catch (error) {
204
- if (error instanceof Error) {
205
- log.warn(`Failed to write finalized head: ${error.message}`);
365
+ instance({ chain, node: realtime2.node });
366
+ return;
367
+ }
368
+ if (realtime2.node.protocol === "browser") {
369
+ log.debug("Looking for browser injected node providers");
370
+ if (typeof window === "undefined") {
371
+ return log.debug("Browser indexing invoked on the server, aborting...");
372
+ }
373
+ const metadata = await realtime2.indexer.request({ method: "public_getMetadata", params: [] });
374
+ const providers = /* @__PURE__ */ new Map();
375
+ const sessions = /* @__PURE__ */ new Map();
376
+ window.addEventListener("eip6963:announceProvider", async (event) => {
377
+ const typed = event;
378
+ const provider = typed.detail;
379
+ if (providers.get(provider.info.uuid) !== void 0) {
380
+ return log.debug(`Found duplicate provider (${provider.info.uuid})`);
381
+ }
382
+ log.debug(`Found provider (${provider.info.uuid})`);
383
+ providers.set(provider.info.uuid, provider);
384
+ let controller = new AbortController();
385
+ const initialise = (initialise2) => {
386
+ for (const [key2, interrupt2] of sessions) {
387
+ if (key2.startsWith(provider.info.uuid)) {
388
+ try {
389
+ interrupt2();
390
+ sessions.delete(key2);
391
+ } catch (error) {
392
+ if (error instanceof Error) {
393
+ log.error(error.message);
394
+ }
395
+ sessions.delete(key2);
396
+ }
397
+ }
206
398
  }
399
+ if (!metadata.chains.includes(initialise2.chain)) {
400
+ return;
401
+ }
402
+ for (const key2 of sessions.keys()) {
403
+ if (key2.endsWith(`:${initialise2.chain}`)) {
404
+ return log.debug(`Already indexing key ${key2}, aborting initialisation...`);
405
+ }
406
+ }
407
+ const key = `${provider.info.uuid}:${initialise2.chain}`;
408
+ log.debug(`Initialising indexing for key ${key}`);
409
+ const interrupt = browser({ signal: initialise2.signal, chain: initialise2.chain, provider: provider.provider });
410
+ sessions.set(key, interrupt);
411
+ };
412
+ provider.provider.on("chainChanged", async (chain) => {
413
+ log.debug(`Provider (${provider.info.uuid}) changed to chain (${hexToNumber(chain)})`);
414
+ controller.abort();
415
+ controller = new AbortController();
416
+ initialise({ chain, signal: controller.signal });
417
+ });
418
+ const initialSignal = controller.signal;
419
+ const initialChain = await provider.provider.request({ method: "eth_chainId" });
420
+ if (initialSignal.aborted) {
421
+ return;
207
422
  }
423
+ initialise({ chain: initialChain, signal: initialSignal });
208
424
  });
209
- await Promise.allSettled(promises);
210
- log.debug(`Processed ${newFinalizedHeads.length} finalized head(s) in parallel`);
211
- chainFinalizedHeight = nextFinalizedHeight;
212
- await unfinalized.prune({
213
- hash: finalizedBlock.hash,
214
- number: finalizedBlock.number,
215
- parent_hash: finalizedBlock.parentHash
216
- });
217
- await mutex_finalize();
218
- } catch (error) {
219
- if (error instanceof Error) {
220
- log.error(`Failed to reconcile finalized head: ${error.message}`);
221
- }
425
+ window.dispatchEvent(new Event("eip6963:requestProvider"));
426
+ }
427
+ } catch (error) {
428
+ if (error instanceof Error) {
429
+ log.error(error.message);
222
430
  }
223
- }
224
- setInterval(poll, POLLING_INTERVAL_MS);
225
- log.debug("Started polling finalized height");
226
- });
227
- promise.catch((error) => {
228
- if (error instanceof Error) {
229
- log.error(`Failed to initialise realtime client: ${error.message}`);
230
431
  }
231
432
  });
232
433
  }
package/dist/rpc.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- export { I as IndexerRpc, R as Rpc } from './shared/univo.DYd1g40o.mjs';
1
+ export { I as IndexerRpc, R as Rpc } from './shared/univo.Ckj3I8tQ.mjs';
2
2
  import 'viem';
3
3
  import './metadata.mjs';
4
4
  import './metadata/adapters.mjs';
package/dist/rpc.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { I as IndexerRpc, R as Rpc } from './shared/univo.CEA4vY0E.js';
1
+ export { I as IndexerRpc, R as Rpc } from './shared/univo.7S1OTdpc.js';
2
2
  import 'viem';
3
3
  import './metadata.js';
4
4
  import './metadata/adapters.js';
@@ -1,11 +1,6 @@
1
1
  import { RpcBlock, RpcTransactionReceipt } from 'viem';
2
2
  import { Storage } from '../metadata.js';
3
3
 
4
- type CamelToSnake<T extends string, P extends string = ""> = string extends T ? string : T extends `${infer C0}${infer R}` ? CamelToSnake<R, `${P}${C0 extends Lowercase<C0> ? "" : "_"}${Lowercase<C0>}`> : P;
5
- type Flatten<T> = {
6
- [K in keyof T as CamelToSnake<Extract<K, string>>]: T[K];
7
- };
8
-
9
4
  type Rpc = {
10
5
  /**
11
6
  * All available methods that can be requested
@@ -47,16 +42,13 @@ type IndexerRpc = {
47
42
  */
48
43
  public_finalize: (chain: `0x${string}`) => Promise<void>;
49
44
  /**
50
- * @returns Specified metadata about the indexer
45
+ * @returns Public metadata about the indexer
51
46
  */
52
- private_getMetadata: () => Promise<Metadata>;
47
+ public_getMetadata: () => Promise<PublicMetadata>;
53
48
  /**
54
- * @returns All events defined on this indexer
49
+ * @returns Private metadata about the indexer
55
50
  */
56
- private_getEvents: () => Promise<{
57
- id: string;
58
- filters: Flatten<Filter>[];
59
- }[]>;
51
+ private_getMetadata: () => Promise<PrivateMetadata>;
60
52
  /**
61
53
  * Accepts a list of events and a batch of minified block data and writes to storage
62
54
  * @returns A list of failures that occured for each event and block pair
@@ -82,10 +74,7 @@ type IndexerRpc = {
82
74
  subscribe: Record<string, never>;
83
75
  };
84
76
 
85
- /**
86
- * Types -----------------------------------------------------------------------------------------------------------------------------------
87
- */
88
- type Protocol = "http" | "wss" | "local";
77
+ type Protocol = "http" | "wss" | "local" | "browser";
89
78
  type Transport<R extends Rpc, P extends Protocol = Protocol> = {
90
79
  /**
91
80
  * The underlying transport protocol.
@@ -104,38 +93,33 @@ type Transport<R extends Rpc, P extends Protocol = Protocol> = {
104
93
  * Subscribes to specific event to receive and invoke a callback for a stream of values
105
94
  * @returns An async function to unsubscribe from the stream
106
95
  */
107
- subscribe: <M extends keyof R["subscribe"]>(param: M, handler: (message: R["subscribe"][M]) => void) => Promise<() => Promise<void>>;
96
+ subscribe: <M extends keyof R["subscribe"]>(param: M, listener: (message: R["subscribe"][M]) => void) => Promise<() => Promise<void>>;
108
97
  };
109
98
  /**
110
- * Local -----------------------------------------------------------------------------------------------------------------------------------
99
+ * local -----------------------------------------------------------------------------------------------------------------------------------
111
100
  */
112
101
  declare function local<R extends Rpc>(rpc: R): Transport<R, "local">;
102
+ /**
103
+ * browser -----------------------------------------------------------------------------------------------------------------------------------
104
+ */
105
+ declare function browser(): Transport<Rpc, "browser">;
113
106
  declare function wss<R extends Rpc>(url: string, opts?: {
114
107
  quiet?: boolean;
115
108
  }): Transport<R, "wss">;
116
109
  /**
117
- * HTTP -----------------------------------------------------------------------------------------------------------------------------------
110
+ * http -----------------------------------------------------------------------------------------------------------------------------------
118
111
  */
119
112
  declare function http<R extends Rpc>(url: string, opts?: {
120
113
  signingKey?: string;
121
114
  }): Transport<R, "http">;
122
115
 
116
+ type CamelToSnake<T extends string, P extends string = ""> = string extends T ? string : T extends `${infer C0}${infer R}` ? CamelToSnake<R, `${P}${C0 extends Lowercase<C0> ? "" : "_"}${Lowercase<C0>}`> : P;
117
+ type Flatten<T> = {
118
+ [K in keyof T as CamelToSnake<Extract<K, string>>]: T[K];
119
+ };
120
+
123
121
  /**
124
- * The minimum RPC data required to process a block.
125
- *
126
- * Extend this interface in your application to expose additional RPC methods to every event:
127
- *
128
- * ```ts
129
- * import "univo";
130
- *
131
- * declare module "univo" {
132
- * interface Block {
133
- * eth_getBalance: `0x${string}`;
134
- * }
135
- * }
136
- * ```
137
- *
138
- * Your `getBlock` function must also return any required fields added by the augmentation.
122
+ * Block
139
123
  */
140
124
  interface Block {
141
125
  eth_chainId: `0x${string}`;
@@ -156,6 +140,8 @@ type Filter = {
156
140
  event?: `0x${string}`;
157
141
  /** Index blocks that involve this address */
158
142
  address?: `0x${string}`;
143
+ /** Index blocks where this function signature was invoked */
144
+ function?: `0x${string}`;
159
145
  };
160
146
  /**
161
147
  * Events -----------------------------------------------------------------------------------------------------------------------------------
@@ -276,9 +262,16 @@ type Head = {
276
262
  number: `0x${string}`;
277
263
  parent_hash: `0x${string}`;
278
264
  };
279
- type Metadata = {
265
+ type PublicMetadata = {
266
+ chains: `0x${string}`[];
267
+ };
268
+ type PrivateMetadata = {
280
269
  version: string;
281
270
  language: string;
271
+ events: {
272
+ id: string;
273
+ filters: Flatten<Filter>[];
274
+ }[];
282
275
  };
283
276
  type Result = {
284
277
  status: string;
@@ -350,5 +343,5 @@ type Indexer = {
350
343
  };
351
344
  declare function defineIndexer(opts: Indexer): Transport<IndexerRpc, "local">;
352
345
 
353
- export { defineEvent as a, defineAction as b, defineIndexer as d, http as h, local as l, wss as w };
354
- export type { Action as A, Block as B, Event as E, Filter as F, Head as H, IndexerRpc as I, Metadata as M, Rpc as R, Transport as T, Indexer as c, Result as e };
346
+ export { defineEvent as a, browser as b, defineAction as c, defineIndexer as d, http as h, local as l, wss as w };
347
+ export type { Action as A, Block as B, Event as E, Filter as F, Head as H, IndexerRpc as I, PublicMetadata as P, Rpc as R, Transport as T, Indexer as e, PrivateMetadata as f, Result as g };