univo 0.2.7 → 0.2.9

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/cli.mjs CHANGED
@@ -5,7 +5,7 @@ import { config } from 'dotenv';
5
5
  import { WebSocket } from 'partysocket';
6
6
  import { defineCommand, runMain } from 'citty';
7
7
  import { http } from './transport.mjs';
8
- import { c as createLogger, v as version, g as name } from './shared/univo.Crj0ggpm.mjs';
8
+ import { c as createLogger, v as version, f as name } from './shared/univo.RhVE6xw6.mjs';
9
9
  import './shared/univo.C0flyVee.mjs';
10
10
 
11
11
  const log = createLogger({ quiet: false });
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { c as createLogger, d as decompress, a as decoder, h as hexToNumber, i as isHexEqual, r as retry, v as version, n as nonNullable, b as normalizeHex } from './shared/univo.Crj0ggpm.mjs';
1
+ import { c as createLogger, d as decompress, a as decoder, h as hexToNumber, i as isHexEqual, r as retry, v as version, n as normalizeHex } from './shared/univo.RhVE6xw6.mjs';
2
2
  import { c as createException, g as getException, a as catchException } from './shared/univo.C0flyVee.mjs';
3
3
 
4
4
  function matchFilter(block, filter) {
@@ -129,34 +129,62 @@ function indexer(opts) {
129
129
  }
130
130
  return block;
131
131
  }
132
+ const GetBlockError = createException("Failed to load block from the provided `getBlock` function");
132
133
  const public_getFinalizedHeight = async (chain) => {
133
- const [stored] = await metadata.blocks.list({ chain });
134
+ const [[stored], finalizedBlock] = await Promise.all([
135
+ metadata.blocks.list({ chain }),
136
+ //
137
+ getBlock({ chain, number: "finalized" })
138
+ ]);
139
+ if (finalizedBlock === null) {
140
+ log.debug("Failed to fetch finalized block and unable to determine finalized height, aborting...");
141
+ throw new Error(GetBlockError);
142
+ }
143
+ const finalizedHeight = hexToNumber(finalizedBlock.eth_getBlockByNumber.number);
134
144
  if (stored === void 0) {
135
- return null;
145
+ return finalizedHeight;
136
146
  }
137
- return hexToNumber(stored.number) - 1;
147
+ const indexerHeight = hexToNumber(stored.number) - 1;
148
+ return Math.min(indexerHeight, finalizedHeight);
138
149
  };
139
150
  const public_writeUnfinalizedHeads = async (heads) => {
151
+ if (heads.length === 0) {
152
+ return log.debug("No heads received, returning...");
153
+ }
140
154
  let chain = void 0;
141
155
  for (const head of heads) {
142
156
  if (chain === void 0) {
143
157
  chain = head.chain;
144
158
  }
145
159
  if (!isHexEqual(chain, head.chain)) {
146
- throw new Error(MultipleChainsError);
160
+ throw new Error("Received heads from separate chains");
147
161
  }
148
162
  }
149
163
  if (chain === void 0) {
150
- return;
164
+ throw new Error("Internal error. Expected chain to be defined after checking heads length");
151
165
  }
152
166
  log.debug(`Received ${heads.length} unfinalized heads...`);
153
167
  const blocks_start = Date.now();
154
- const blocks_nullable = await Promise.all(
155
- heads.map(async (head) => {
168
+ const [finalizedBlock, ...blocks_nullable] = await Promise.all([
169
+ getBlock({ chain, number: "finalized" }),
170
+ ...heads.map(async (head) => {
156
171
  return await getBlock({ chain, number: head.number, hash: head.hash });
157
172
  })
158
- );
159
- const blocks = blocks_nullable.filter(nonNullable);
173
+ ]);
174
+ if (finalizedBlock === null) {
175
+ log.debug("Failed to determine finalized height when processing unfinalized heads");
176
+ throw new Error(GetBlockError);
177
+ }
178
+ const finalizedHeight = hexToNumber(finalizedBlock.eth_getBlockByNumber.number);
179
+ const blocks = blocks_nullable.flatMap((block) => {
180
+ if (block === null) {
181
+ return [];
182
+ }
183
+ if (hexToNumber(block.eth_getBlockByNumber.number) <= finalizedHeight) {
184
+ return [];
185
+ }
186
+ return block;
187
+ });
160
188
  if (blocks.length === 0) {
161
189
  return log.debug("All blocks failed to load, aborting early...");
162
190
  }
@@ -239,61 +267,68 @@ function indexer(opts) {
239
267
  await Promise.all(promises);
240
268
  };
241
269
  const public_writeFinalizedHeads = async (heads) => {
270
+ if (heads.length === 0) {
271
+ return log.debug("No heads received, returning...");
272
+ }
273
+ log.debug(`Received ${heads.length} finalized heads...`);
242
274
  let chain = void 0;
243
275
  for (const head of heads) {
244
276
  if (chain === void 0) {
245
277
  chain = head.chain;
246
278
  }
247
279
  if (!isHexEqual(chain, head.chain)) {
248
- throw new Error(MultipleChainsError);
280
+ throw new Error("Received heads from separate chains");
249
281
  }
250
282
  }
251
283
  if (chain === void 0) {
252
- throw new Error(InvalidHeadsError);
284
+ throw new Error("Internal error. Expected chain to be defined after checking heads length");
253
285
  }
254
- log.debug(`Received ${heads.length} finalized heads...`);
255
- const [indexerHeight, finalizedBlock] = await Promise.all([
256
- public_getFinalizedHeight(chain),
286
+ const [firstHead, ...remainingHeads] = heads;
287
+ if (firstHead === void 0) {
288
+ throw new Error("Internal error. Expected firstHead to be defined after checking heads length");
289
+ }
290
+ let previousHead = firstHead;
291
+ for (const head of remainingHeads) {
292
+ if (hexToNumber(head.number) !== hexToNumber(previousHead.number) + 1) {
293
+ throw new Error("Found invalid block number in received heads, aborting...");
294
+ }
295
+ if (!isHexEqual(head.parentHash, previousHead.hash)) {
296
+ throw new Error("Found invalid block hash in received heads, aborting...");
297
+ }
298
+ previousHead = head;
299
+ }
300
+ const [[unfinalizedMetadataBlock], finalizedBlock] = await Promise.all([
301
+ metadata.blocks.list({ chain }),
257
302
  //
258
303
  getBlock({ chain, number: "finalized" })
259
304
  ]);
260
- if (indexerHeight === null) {
261
- return log.debug(`No unfinalized blocks processed for chain ${hexToNumber(chain)}, aborting...`);
262
- }
263
305
  if (finalizedBlock === null) {
264
- log.debug("Failed to load finalized block, aborting...");
306
+ log.debug("Failed to load chain finalized block. Request should be retried");
265
307
  throw new Error(GetBlockError);
266
308
  }
267
309
  const finalizedHeight = hexToNumber(finalizedBlock.eth_getBlockByNumber.number);
268
- log.debug(`Indexer height ${indexerHeight}, finalized height ${finalizedHeight}`);
269
- const newHeads = heads.filter((head) => {
310
+ for (const head of heads) {
270
311
  if (hexToNumber(head.number) > finalizedHeight) {
271
- return false;
312
+ throw new Error("Received invalid finalized heads");
272
313
  }
273
- if (hexToNumber(head.number) > indexerHeight) {
274
- return true;
275
- }
276
- return false;
277
- });
278
- const [firstHead, ...remainingHeads] = newHeads;
279
- if (firstHead === void 0) {
280
- return log.debug("Received no new finalized heads, aborting...");
281
314
  }
282
- if (hexToNumber(firstHead.number) !== indexerHeight + 1) {
283
- log.debug("First unfinalized head received didn't match metadata, aborting...");
284
- throw new Error(InvalidHeadsError);
315
+ if (unfinalizedMetadataBlock === void 0) {
316
+ return log.debug("No heads to finalize, returning...");
285
317
  }
286
- let previousHead = firstHead;
287
- for (const head of remainingHeads) {
288
- if (hexToNumber(head.number) !== hexToNumber(previousHead.number) + 1) {
289
- log.debug("Found invalid block number in received heads, aborting...");
290
- throw new Error(InvalidHeadsError);
291
- }
292
- if (!isHexEqual(head.parentHash, previousHead.hash)) {
293
- log.debug("Found invalid block hash in received heads, aborting...");
294
- throw new Error(InvalidHeadsError);
295
- }
296
- previousHead = head;
318
+ const indexerHeight = hexToNumber(unfinalizedMetadataBlock.number) - 1;
319
+ log.debug(`Indexer height ${indexerHeight}, finalized height ${finalizedHeight}`);
320
+ if (indexerHeight === finalizedHeight) {
321
+ return log.debug("No heads to finalize, returning...");
322
+ }
323
+ const newHeads = heads.filter((head) => {
324
+ return hexToNumber(head.number) > indexerHeight;
325
+ });
326
+ const [firstNewHead] = newHeads;
327
+ if (firstNewHead === void 0) {
328
+ return log.debug("No new heads received, returning...");
329
+ }
330
+ if (hexToNumber(firstNewHead.number) !== indexerHeight + 1) {
331
+ throw new Error("Client advanced too far. Heads received are greater is than the indexer height");
297
332
  }
298
333
  for (const head of newHeads) {
299
334
  const processed = await metadata.blocks.get({ chain: head.chain, number: head.number });
@@ -320,7 +355,7 @@ function indexer(opts) {
320
355
  block = await getBlock({ chain: head.chain, number: head.number, hash: head.hash });
321
356
  }
322
357
  if (block === null) {
323
- throw new Error(InvalidHeadsError);
358
+ throw new Error("Received invalid finalized heads");
324
359
  }
325
360
  const promises = events_grouped_by_storage_map.entries().map(async ([storage, grouped_events]) => {
326
361
  const batch = [];
@@ -445,6 +480,7 @@ function indexer(opts) {
445
480
  };
446
481
  return new Proxy(value, createProxyHandler());
447
482
  };
483
+ const IncompleteBlockError = createException("Received block with missing required property");
448
484
  const private_writeEvents = async (params) => {
449
485
  if (all_events.length === 0) return { failures: [] };
450
486
  const relevant_events = all_events.filter((event2) => params.events.includes(event2.id));
@@ -636,6 +672,7 @@ function indexer(opts) {
636
672
  events_grouped_by_storage_map.set(event2.storage, group);
637
673
  return event2;
638
674
  };
675
+ const UnknownMethodError = createException("The requested method does not exist");
639
676
  const handler = async (req) => {
640
677
  let body_buffer;
641
678
  try {
@@ -702,6 +739,7 @@ function indexer(opts) {
702
739
  } catch (error) {
703
740
  const message = getException(error);
704
741
  if (message) {
742
+ log.error(message);
705
743
  return Response.json(
706
744
  { jsonrpc: "2.0", id: json.id, error: { code: 0, message } },
707
745
  //
@@ -720,10 +758,5 @@ function indexer(opts) {
720
758
  };
721
759
  return { fetch: handler, event };
722
760
  }
723
- const InvalidHeadsError = createException("Received invalid finalized heads");
724
- const UnknownMethodError = createException("The requested method does not exist");
725
- const MultipleChainsError = createException("Received heads from separate chains");
726
- const IncompleteBlockError = createException("Received block with missing required property");
727
- const GetBlockError = createException("Failed to load block from the provided `getBlock` function");
728
761
 
729
762
  export { indexer, matchFilter };
package/dist/realtime.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { c as createLogger, e as iife, h as hexToNumber, f as numberToHex, m as mutex, r as retry, i as isHexEqual } from './shared/univo.Crj0ggpm.mjs';
1
+ import { c as createLogger, b as iife, h as hexToNumber, e as numberToHex, m as mutex, r as retry, i as isHexEqual } from './shared/univo.RhVE6xw6.mjs';
2
2
 
3
3
  const MAX_LENGTH = 1e4;
4
4
  function defineBlockchain(opts) {
@@ -139,18 +139,14 @@ function realtime(opts) {
139
139
  getBlockByHash,
140
140
  quiet: opts.quiet ?? false
141
141
  });
142
- if (initialIndexerHeight === null) {
143
- await indexer.reconcile(latestBlock);
144
- } else {
145
- log.debug(`Indexer last finalized at ${initialIndexerHeight}, reconciling local chain...`);
146
- const indexerBlock = await opts.node.request({
147
- method: "eth_getBlockByNumber",
148
- params: [numberToHex(initialIndexerHeight), false]
149
- });
150
- await indexer.reconcile(indexerBlock);
151
- await indexer.reconcile(latestBlock);
152
- log.debug("Reconciled local indexer chain");
153
- }
142
+ log.debug(`Indexer last finalized at ${initialIndexerHeight}, reconciling local chain...`);
143
+ const indexerBlock = await opts.node.request({
144
+ method: "eth_getBlockByNumber",
145
+ params: [numberToHex(initialIndexerHeight), false]
146
+ });
147
+ await indexer.reconcile(indexerBlock);
148
+ await indexer.reconcile(latestBlock);
149
+ log.debug("Reconciled local indexer chain");
154
150
  await opts.node.subscribe("newHeads", async (head) => {
155
151
  try {
156
152
  await indexer.reconcile(head);
@@ -161,7 +157,7 @@ function realtime(opts) {
161
157
  }
162
158
  });
163
159
  log.debug("Subscribed indexer chain to new heads");
164
- let indexerHeight = initialIndexerHeight || hexToNumber(latestBlock.number);
160
+ let indexerHeight = initialIndexerHeight;
165
161
  const poll = async () => {
166
162
  try {
167
163
  const finalizedBlock = await opts.node.request({
package/dist/rpc.d.mts CHANGED
@@ -57,7 +57,7 @@ type IndexerRpc = {
57
57
  * Accepts a chain identifier
58
58
  * @returns Height of the last finalized block indexed
59
59
  */
60
- public_getFinalizedHeight: (chain: `0x${string}`) => Promise<number | null>;
60
+ public_getFinalizedHeight: (chain: `0x${string}`) => Promise<number>;
61
61
  /**
62
62
  * Accepts a reorganised block and deletes all related events from storage
63
63
  */
package/dist/rpc.d.ts CHANGED
@@ -57,7 +57,7 @@ type IndexerRpc = {
57
57
  * Accepts a chain identifier
58
58
  * @returns Height of the last finalized block indexed
59
59
  */
60
- public_getFinalizedHeight: (chain: `0x${string}`) => Promise<number | null>;
60
+ public_getFinalizedHeight: (chain: `0x${string}`) => Promise<number>;
61
61
  /**
62
62
  * Accepts a reorganised block and deletes all related events from storage
63
63
  */
@@ -1,5 +1,5 @@
1
1
  const name = "univo";
2
- const version = "0.2.7";
2
+ const version = "0.2.9";
3
3
 
4
4
  function createLogger(opts) {
5
5
  let prefix = `[${name}@${version}]`;
@@ -73,9 +73,6 @@ function raise(...args) {
73
73
  if (args[0] instanceof Error) throw args[0];
74
74
  throw new Error(...args);
75
75
  }
76
- function nonNullable(value) {
77
- return value !== null && value !== void 0;
78
- }
79
76
  async function compress(input) {
80
77
  const compressed = new Blob([input]).stream().pipeThrough(new CompressionStream("gzip"));
81
78
  return new Response(compressed).arrayBuffer();
@@ -92,4 +89,4 @@ const normalizeHex = (hex, length) => {
92
89
  return `0x${hex.slice(2).toLowerCase().padStart(length, "0")}`;
93
90
  };
94
91
 
95
- export { decoder as a, normalizeHex as b, createLogger as c, decompress as d, iife as e, numberToHex as f, name as g, hexToNumber as h, isHexEqual as i, compress as j, raise as k, mutex as m, nonNullable as n, retry as r, version as v };
92
+ export { decoder as a, iife as b, createLogger as c, decompress as d, numberToHex as e, name as f, compress as g, hexToNumber as h, isHexEqual as i, raise as j, mutex as m, normalizeHex as n, retry as r, version as v };
@@ -1,7 +1,7 @@
1
1
  import ws from 'ws';
2
2
  import { WebSocket } from 'partysocket';
3
3
  import { c as createException } from './shared/univo.C0flyVee.mjs';
4
- import { j as compress, k as raise, c as createLogger } from './shared/univo.Crj0ggpm.mjs';
4
+ import { g as compress, j as raise, c as createLogger } from './shared/univo.RhVE6xw6.mjs';
5
5
 
6
6
  function createSocket(url, opts) {
7
7
  const socket = new WebSocket(url, [], { WebSocket: ws });
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "univo",
3
3
  "license": "MIT",
4
4
  "type": "module",
5
- "version": "0.2.7",
5
+ "version": "0.2.9",
6
6
  "sideEffects": false,
7
7
  "module": "./dist/index.mjs",
8
8
  "types": "./dist/index.d.ts",