@hugomrdias/foxer 0.1.9 → 0.1.11

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 (55) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.md +71 -1
  3. package/dist/config/env.d.ts +1 -1
  4. package/dist/config/env.d.ts.map +1 -1
  5. package/dist/config/env.js +2 -2
  6. package/dist/config/env.js.map +1 -1
  7. package/dist/db/actions/blocks.d.ts +9 -6
  8. package/dist/db/actions/blocks.d.ts.map +1 -1
  9. package/dist/db/actions/blocks.js +39 -43
  10. package/dist/db/actions/blocks.js.map +1 -1
  11. package/dist/db/client.d.ts +1 -1
  12. package/dist/db/client.d.ts.map +1 -1
  13. package/dist/db/client.js +5 -1
  14. package/dist/db/client.js.map +1 -1
  15. package/dist/db/column-types.d.ts +2 -2
  16. package/dist/db/column-types.d.ts.map +1 -1
  17. package/dist/db/column-types.js +11 -3
  18. package/dist/db/column-types.js.map +1 -1
  19. package/dist/db/schema/blocks.d.ts +16 -16
  20. package/dist/db/schema/index.d.ts +44 -44
  21. package/dist/db/schema/transactions.d.ts +6 -6
  22. package/dist/db/schema/transactions.d.ts.map +1 -1
  23. package/dist/db/schema/transactions.js +3 -1
  24. package/dist/db/schema/transactions.js.map +1 -1
  25. package/dist/hooks/registry.d.ts +3 -3
  26. package/dist/hooks/registry.d.ts.map +1 -1
  27. package/dist/indexer/backfill.d.ts.map +1 -1
  28. package/dist/indexer/backfill.js +19 -12
  29. package/dist/indexer/backfill.js.map +1 -1
  30. package/dist/indexer/process-block.d.ts +4 -4
  31. package/dist/indexer/process-block.d.ts.map +1 -1
  32. package/dist/indexer/process-block.js +4 -29
  33. package/dist/indexer/process-block.js.map +1 -1
  34. package/dist/indexer/queue-block.d.ts.map +1 -1
  35. package/dist/indexer/queue-block.js +19 -1
  36. package/dist/indexer/queue-block.js.map +1 -1
  37. package/dist/indexer/reorg.d.ts +2 -2
  38. package/dist/indexer/reorg.d.ts.map +1 -1
  39. package/dist/rpc/get-logs.js +1 -1
  40. package/dist/rpc/get-logs.js.map +1 -1
  41. package/dist/types.d.ts +2 -0
  42. package/dist/types.d.ts.map +1 -1
  43. package/package.json +11 -9
  44. package/src/config/env.ts +2 -2
  45. package/src/db/actions/blocks.ts +51 -51
  46. package/src/db/client.ts +5 -1
  47. package/src/db/column-types.ts +17 -6
  48. package/src/db/schema/transactions.ts +3 -1
  49. package/src/hooks/registry.ts +3 -3
  50. package/src/indexer/backfill.ts +28 -10
  51. package/src/indexer/process-block.ts +10 -39
  52. package/src/indexer/queue-block.ts +24 -1
  53. package/src/indexer/reorg.ts +2 -2
  54. package/src/rpc/get-logs.ts +1 -1
  55. package/src/types.ts +2 -0
@@ -1 +1 @@
1
- {"version":3,"file":"process-block.js","sourceRoot":"","sources":["../../src/indexer/process-block.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,yBAAyB,EAAE,MAAM,yBAAyB,CAAA;AAGnE,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAEtD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAGlD,OAAO,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAA;AAMnD;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,IAWlC;IACC,MAAM,EACJ,MAAM,EACN,MAAM,EACN,EAAE,EACF,MAAM,EACN,QAAQ,EACR,WAAW,EACX,KAAK,EAAE,eAAe,EACtB,IAAI,EAAE,cAAc,EACpB,IAAI,EACJ,SAAS,GACV,GAAG,IAAI,CAAA;IACR,MAAM,iBAAiB,GAAG,IAAI,GAAG,EAAqC,CAAA;IAEtE,IAAI,KAA+C,CAAA;IACnD,IAAI,IAAwD,CAAA;IAE5D,IAAI,eAAe,EAAE,CAAC;QACpB,KAAK,GAAG,eAAe,CAAA;IACzB,CAAC;IACD,IAAI,cAAc,EAAE,CAAC;QACnB,IAAI,GAAG,cAAc,CAAA;IACvB,CAAC;IACD,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI,EAAE,CAAC;QACpB,MAAM,CAAC,WAAW,EAAE,UAAU,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YAClD,YAAY,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,EAAE,EAAE,CAAC;YACzC,MAAM,CAAC,OAAO,CAAC;gBACb,OAAO,EAAE,SAAS,CAAC,SAAS;gBAC5B,MAAM,EAAE,SAAS,CAAC,SAAS;gBAC3B,SAAS,EAAE,WAAW;gBACtB,OAAO,EAAE,WAAW;aACrB,CAAC;SACH,CAAC,CAAA;QAEF,KAAK,GAAG,WAAW,CAAA;QACnB,IAAI,GAAG,UAAU,CAAA;IACnB,CAAC;IAED,KAAK,MAAM,EAAE,IAAI,KAAK,CAAC,YAAY,EAAE,CAAC;QACpC,iBAAiB,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAA;IACpC,CAAC;IAED,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;QACpB,MAAM,QAAQ,GAAG,MAAM,sBAAsB,CAAC;YAC5C,MAAM;YACN,EAAE;YACF,MAAM;YACN,KAAK;SACN,CAAC,CAAA;QACF,IAAI,QAAQ,IAAI,IAAI,EAAE,CAAC;YACrB,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAA;QACtC,CAAC;IACH,CAAC;IAED,MAAM,KAAK,GAAG,KAAK,EAAE,EAA6C,EAAE,EAAE;QACpE,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;YACpB,MAAM,yBAAyB,CAAC;gBAC9B,EAAE,EAAE,EAAE;gBACN,KAAK;gBACL,MAAM;aACP,CAAC,CAAA;QACJ,CAAC;QAED,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,MAAM,YAAY,GAAG,SAAS,CAAC,qBAAqB,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;YAEjE,IAAI,CAAC,YAAY,EAAE,CAAC;gBAClB,MAAM,CAAC,KAAK,CACV,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,EACxB,gDAAgD,CACjD,CAAA;gBACD,SAAQ;YACV,CAAC;YACD,MAAM,SAAS,GAAG,GAAG,CAAC,SAAS,CAAA;YAE/B,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;gBACzC,SAAQ;YACV,CAAC;YACD,MAAM,WAAW,GAAG,iBAAiB,CAAC,GAAG,CAAC,GAAG,CAAC,eAAe,CAAC,CAAA;YAE9D,IAAI,CAAC,WAAW,EAAE,CAAC;gBACjB,MAAM,CAAC,KAAK,CACV,EAAE,eAAe,EAAE,GAAG,CAAC,eAAe,EAAE,EAExC,iDAAiD,CAClD,CAAA;gBACD,SAAQ;YACV,CAAC;YACD,MAAM,QAAQ,CAAC,QAAQ,CAAC;gBACtB,GAAG,EAAE,GAAG,YAAY,IAAI,SAAS,EAAW;gBAC5C,IAAI,EAAE,GAAG,CAAC,IAAa;gBACvB,GAAG,EAAE,GAAY;gBACjB,KAAK;gBACL,WAAW;gBACX,OAAO,EAAE;oBACP,EAAE,EAAE,EAAE;oBACN,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;oBAC/B,MAAM;iBACP;aACF,CAAC,CAAA;QACJ,CAAC;IACH,CAAC,CAAA;IAED,IAAI,IAAI,KAAK,UAAU,EAAE,CAAC;QACxB,MAAM,KAAK,CAAC,EAAE,CAAC,CAAA;IACjB,CAAC;SAAM,CAAC;QACN,MAAM,eAAe,CAAC,EAAE,EAAE,KAAK,CAAC,CAAA;IAClC,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,CAAA;AAChC,CAAC"}
1
+ {"version":3,"file":"process-block.js","sourceRoot":"","sources":["../../src/indexer/process-block.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,yBAAyB,EAAE,MAAM,yBAAyB,CAAA;AAGnE,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAItD,OAAO,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAA;AAMnD;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,IAWlC;IACC,MAAM,EACJ,MAAM,EACN,MAAM,EACN,EAAE,EACF,MAAM,EACN,QAAQ,EACR,KAAK,EACL,eAAe,EACf,IAAI,EACJ,IAAI,EACJ,SAAS,GACV,GAAG,IAAI,CAAA;IAER,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;QACpB,MAAM,QAAQ,GAAG,MAAM,sBAAsB,CAAC;YAC5C,MAAM;YACN,EAAE;YACF,MAAM;YACN,KAAK;SACN,CAAC,CAAA;QACF,IAAI,QAAQ,IAAI,IAAI,EAAE,CAAC;YACrB,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAA;QACtC,CAAC;IACH,CAAC;IAED,MAAM,KAAK,GAAG,KAAK,EAAE,EAA6C,EAAE,EAAE;QACpE,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;YACpB,MAAM,yBAAyB,CAAC;gBAC9B,EAAE,EAAE,EAAE;gBACN,MAAM,EAAE,CAAC,KAAK,CAAC;gBACf,YAAY,EAAE,KAAK,CAAC,IAAI,CAAC,eAAe,CAAC,MAAM,EAAE,CAAC;gBAClD,MAAM;aACP,CAAC,CAAA;QACJ,CAAC;QAED,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,MAAM,YAAY,GAAG,SAAS,CAAC,qBAAqB,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;YAEjE,IAAI,CAAC,YAAY,EAAE,CAAC;gBAClB,MAAM,CAAC,KAAK,CACV,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,EACxB,gDAAgD,CACjD,CAAA;gBACD,SAAQ;YACV,CAAC;YACD,MAAM,SAAS,GAAG,GAAG,CAAC,SAAS,CAAA;YAE/B,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;gBACzC,SAAQ;YACV,CAAC;YACD,MAAM,WAAW,GAAG,eAAe,CAAC,GAAG,CAAC,GAAG,CAAC,eAAe,CAAC,CAAA;YAE5D,IAAI,CAAC,WAAW,EAAE,CAAC;gBACjB,MAAM,CAAC,KAAK,CACV,EAAE,eAAe,EAAE,GAAG,CAAC,eAAe,EAAE,EAExC,iDAAiD,CAClD,CAAA;gBACD,SAAQ;YACV,CAAC;YACD,MAAM,QAAQ,CAAC,QAAQ,CAAC;gBACtB,GAAG,EAAE,GAAG,YAAY,IAAI,SAAS,EAAW;gBAC5C,IAAI,EAAE,GAAG,CAAC,IAAa;gBACvB,GAAG,EAAE,GAAY;gBACjB,KAAK;gBACL,WAAW;gBACX,OAAO,EAAE;oBACP,EAAE,EAAE,EAAE;oBACN,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;oBAC/B,MAAM;iBACP;aACF,CAAC,CAAA;QACJ,CAAC;IACH,CAAC,CAAA;IAED,IAAI,IAAI,KAAK,UAAU,EAAE,CAAC;QACxB,MAAM,KAAK,CAAC,EAAE,CAAC,CAAA;IACjB,CAAC;SAAM,CAAC;QACN,MAAM,eAAe,CAAC,EAAE,EAAE,KAAK,CAAC,CAAA;IAClC,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,CAAA;AAChC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"queue-block.d.ts","sourceRoot":"","sources":["../../src/indexer/queue-block.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AAClC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,MAAM,CAAA;AAExC,OAAO,EAAmB,KAAK,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAC1E,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAC/C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAA;AAC9D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AAIxD,MAAM,MAAM,cAAc,GAAG;IAC3B,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAA;IACpC,MAAM,EAAE,cAAc,CAAA;IACtB,EAAE,EAAE,QAAQ,CAAC,OAAO,MAAM,EAAE,OAAO,SAAS,CAAC,CAAA;IAC7C,MAAM,EAAE,YAAY,CAAA;IACpB,QAAQ,EAAE,YAAY,CAAA;IACtB,SAAS,EAAE,MAAM,CAAA;CAClB,CAAA;AAED,wBAAsB,UAAU,CAAC,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAqDpE"}
1
+ {"version":3,"file":"queue-block.d.ts","sourceRoot":"","sources":["../../src/indexer/queue-block.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AAClC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,MAAM,CAAA;AAExC,OAAO,EAAmB,KAAK,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAC1E,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAC/C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAA;AAC9D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AAMxD,MAAM,MAAM,cAAc,GAAG;IAC3B,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAA;IACpC,MAAM,EAAE,cAAc,CAAA;IACtB,EAAE,EAAE,QAAQ,CAAC,OAAO,MAAM,EAAE,OAAO,SAAS,CAAC,CAAA;IAC7C,MAAM,EAAE,YAAY,CAAA;IACpB,QAAQ,EAAE,YAAY,CAAA;IACtB,SAAS,EAAE,MAAM,CAAA;CAClB,CAAA;AAED,wBAAsB,UAAU,CAAC,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CA0EpE"}
@@ -1,4 +1,5 @@
1
1
  import { filterContracts } from "../config/config.js";
2
+ import { safeGetBlock } from "../rpc/get-block.js";
2
3
  import { startClock } from "../utils/timer.js";
3
4
  import { processBlock } from "./process-block.js";
4
5
  export async function queueBlock(args) {
@@ -6,15 +7,32 @@ export async function queueBlock(args) {
6
7
  const endClock = startClock();
7
8
  try {
8
9
  const contracts = filterContracts(config, blockNumber, blockNumber);
10
+ const [blockResult, logsResult] = await Promise.all([
11
+ safeGetBlock({ client, blockNumber, db }),
12
+ client.getLogs({
13
+ address: contracts.addresses,
14
+ events: contracts.eventAbis,
15
+ fromBlock: blockNumber,
16
+ toBlock: blockNumber,
17
+ }),
18
+ ]);
19
+ const { transactions, ..._block } = blockResult;
20
+ const block = _block;
21
+ const transactionsMap = new Map();
22
+ for (const tx of transactions) {
23
+ transactionsMap.set(tx.hash, tx);
24
+ }
9
25
  const result = await processBlock({
10
26
  logger,
11
27
  config,
12
28
  db,
13
29
  client,
14
30
  registry,
15
- blockNumber,
16
31
  type: 'live',
17
32
  contracts,
33
+ block,
34
+ transactionsMap,
35
+ logs: logsResult,
18
36
  });
19
37
  if (result.status === 'reorg') {
20
38
  logger.warn({
@@ -1 +1 @@
1
- {"version":3,"file":"queue-block.js","sourceRoot":"","sources":["../../src/indexer/queue-block.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,eAAe,EAAuB,MAAM,qBAAqB,CAAA;AAI1E,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAajD,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,IAAoB;IACnD,MAAM,EACJ,MAAM,EACN,EAAE,EACF,MAAM,EACN,QAAQ,EACR,WAAW,EACX,MAAM,EACN,QAAQ,EACR,SAAS,GACV,GAAG,IAAI,CAAA;IAER,MAAM,QAAQ,GAAG,UAAU,EAAE,CAAA;IAC7B,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,eAAe,CAAC,MAAM,EAAE,WAAW,EAAE,WAAW,CAAC,CAAA;QACnE,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC;YAChC,MAAM;YACN,MAAM;YACN,EAAE;YACF,MAAM;YACN,QAAQ;YACR,WAAW;YACX,IAAI,EAAE,MAAM;YACZ,SAAS;SACV,CAAC,CAAA;QAEF,IAAI,MAAM,CAAC,MAAM,KAAK,OAAO,EAAE,CAAC;YAC9B,MAAM,CAAC,IAAI,CACT;gBACE,WAAW,EAAE,WAAW,CAAC,QAAQ,EAAE;gBACnC,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,QAAQ,EAAE;aACrC,EACD,kDAAkD,CACnD,CAAA;YACD,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;YACzB,OAAM;QACR,CAAC;QAED,MAAM,CAAC,IAAI,CACT;YACE,QAAQ,EAAE,QAAQ,EAAE;YACpB,WAAW,EAAE,WAAW,CAAC,QAAQ,EAAE;YACnC,SAAS;SACV,EACD,sBAAsB,CACvB,CAAA;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,KAAK,CACV,EAAE,KAAK,EAAE,WAAW,EAAE,WAAW,CAAC,QAAQ,EAAE,EAAE,EAC9C,oCAAoC,CACrC,CAAA;QACD,QAAQ,CAAC,WAAW,GAAG,EAAE,CAAC,CAAA;IAC5B,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"queue-block.js","sourceRoot":"","sources":["../../src/indexer/queue-block.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,eAAe,EAAuB,MAAM,qBAAqB,CAAA;AAI1E,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAElD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAajD,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,IAAoB;IACnD,MAAM,EACJ,MAAM,EACN,EAAE,EACF,MAAM,EACN,QAAQ,EACR,WAAW,EACX,MAAM,EACN,QAAQ,EACR,SAAS,GACV,GAAG,IAAI,CAAA;IAER,MAAM,QAAQ,GAAG,UAAU,EAAE,CAAA;IAC7B,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,eAAe,CAAC,MAAM,EAAE,WAAW,EAAE,WAAW,CAAC,CAAA;QAEnE,MAAM,CAAC,WAAW,EAAE,UAAU,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YAClD,YAAY,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,EAAE,EAAE,CAAC;YACzC,MAAM,CAAC,OAAO,CAAC;gBACb,OAAO,EAAE,SAAS,CAAC,SAAS;gBAC5B,MAAM,EAAE,SAAS,CAAC,SAAS;gBAC3B,SAAS,EAAE,WAAW;gBACtB,OAAO,EAAE,WAAW;aACrB,CAAC;SACH,CAAC,CAAA;QAEF,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,EAAE,GAAG,WAAW,CAAA;QAC/C,MAAM,KAAK,GAAiB,MAAM,CAAA;QAClC,MAAM,eAAe,GAAG,IAAI,GAAG,EAAqC,CAAA;QAEpE,KAAK,MAAM,EAAE,IAAI,YAAY,EAAE,CAAC;YAC9B,eAAe,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAA;QAClC,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC;YAChC,MAAM;YACN,MAAM;YACN,EAAE;YACF,MAAM;YACN,QAAQ;YACR,IAAI,EAAE,MAAM;YACZ,SAAS;YACT,KAAK;YACL,eAAe;YACf,IAAI,EAAE,UAAU;SACjB,CAAC,CAAA;QAEF,IAAI,MAAM,CAAC,MAAM,KAAK,OAAO,EAAE,CAAC;YAC9B,MAAM,CAAC,IAAI,CACT;gBACE,WAAW,EAAE,WAAW,CAAC,QAAQ,EAAE;gBACnC,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,QAAQ,EAAE;aACrC,EACD,kDAAkD,CACnD,CAAA;YACD,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;YACzB,OAAM;QACR,CAAC;QAED,MAAM,CAAC,IAAI,CACT;YACE,QAAQ,EAAE,QAAQ,EAAE;YACpB,WAAW,EAAE,WAAW,CAAC,QAAQ,EAAE;YACnC,SAAS;SACV,EACD,sBAAsB,CACvB,CAAA;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,KAAK,CACV,EAAE,KAAK,EAAE,WAAW,EAAE,WAAW,CAAC,QAAQ,EAAE,EAAE,EAC9C,oCAAoC,CACrC,CAAA;QACD,QAAQ,CAAC,WAAW,GAAG,EAAE,CAAC,CAAA;IAC5B,CAAC;AACH,CAAC"}
@@ -1,6 +1,6 @@
1
1
  import type { PublicClient } from 'viem';
2
2
  import type { Database } from '../db/client.ts';
3
- import type { EncodedBlockWithTransactions } from '../types';
3
+ import type { EncodedBlock } from '../types';
4
4
  import type { Logger } from '../utils/logger.ts';
5
5
  /**
6
6
  * Verifies parent-hash continuity and rolls back divergent canonical rows if needed.
@@ -10,7 +10,7 @@ export declare function ensureParentContinuity(args: {
10
10
  logger: Logger;
11
11
  db: Database;
12
12
  client: PublicClient;
13
- block: EncodedBlockWithTransactions;
13
+ block: EncodedBlock;
14
14
  }): Promise<bigint | null>;
15
15
  /**
16
16
  * Validates recent indexed blocks against chain state on startup.
@@ -1 +1 @@
1
- {"version":3,"file":"reorg.d.ts","sourceRoot":"","sources":["../../src/indexer/reorg.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,MAAM,CAAA;AAGxC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAE/C,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,UAAU,CAAA;AAE5D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAA;AAGhD;;;GAGG;AACH,wBAAsB,sBAAsB,CAAC,IAAI,EAAE;IACjD,MAAM,EAAE,MAAM,CAAA;IACd,EAAE,EAAE,QAAQ,CAAA;IACZ,MAAM,EAAE,YAAY,CAAA;IACpB,KAAK,EAAE,4BAA4B,CAAA;CACpC,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA2DzB;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CAAC,IAAI,EAAE;IAC7C,MAAM,EAAE,MAAM,CAAA;IACd,EAAE,EAAE,QAAQ,CAAA;IACZ,MAAM,EAAE,YAAY,CAAA;IACpB,KAAK,EAAE,MAAM,CAAA;CACd,GAAG,OAAO,CAAC,IAAI,CAAC,CAgChB"}
1
+ {"version":3,"file":"reorg.d.ts","sourceRoot":"","sources":["../../src/indexer/reorg.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,MAAM,CAAA;AAGxC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAE/C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AAE5C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAA;AAGhD;;;GAGG;AACH,wBAAsB,sBAAsB,CAAC,IAAI,EAAE;IACjD,MAAM,EAAE,MAAM,CAAA;IACd,EAAE,EAAE,QAAQ,CAAA;IACZ,MAAM,EAAE,YAAY,CAAA;IACpB,KAAK,EAAE,YAAY,CAAA;CACpB,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA2DzB;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CAAC,IAAI,EAAE;IAC7C,MAAM,EAAE,MAAM,CAAA;IACd,EAAE,EAAE,QAAQ,CAAA;IACZ,MAAM,EAAE,YAAY,CAAA;IACpB,KAAK,EAAE,MAAM,CAAA;CACd,GAAG,OAAO,CAAC,IAAI,CAAC,CAgChB"}
@@ -15,7 +15,7 @@ export async function getLogsInRange(args) {
15
15
  logsByBlock.set(log.blockNumber, byBlock);
16
16
  }
17
17
  logger.trace({
18
- logs: logsByBlock.size,
18
+ logs: logs.length,
19
19
  duration: endClock(),
20
20
  }, 'get logs');
21
21
  return logsByBlock;
@@ -1 +1 @@
1
- {"version":3,"file":"get-logs.js","sourceRoot":"","sources":["../../src/rpc/get-logs.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAE9C,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,IAOpC;IACC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,GAAG,IAAI,CAAA;IAEtE,MAAM,QAAQ,GAAG,UAAU,EAAE,CAAA;IAE7B,MAAM,WAAW,GAAG,IAAI,GAAG,EAAkD,CAAA;IAC7E,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC;QAChC,OAAO,EAAE,SAAS;QAClB,MAAM,EAAE,MAAM;QACd,SAAS,EAAE,SAAS;QACpB,OAAO,EAAE,OAAO;KACjB,CAAC,CAAA;IAEF,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,OAAO,GAAG,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,EAAE,CAAA;QACtD,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;QACjB,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,WAAW,EAAE,OAAO,CAAC,CAAA;IAC3C,CAAC;IACD,MAAM,CAAC,KAAK,CACV;QACE,IAAI,EAAE,WAAW,CAAC,IAAI;QACtB,QAAQ,EAAE,QAAQ,EAAE;KACrB,EACD,UAAU,CACX,CAAA;IACD,OAAO,WAAW,CAAA;AACpB,CAAC"}
1
+ {"version":3,"file":"get-logs.js","sourceRoot":"","sources":["../../src/rpc/get-logs.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAE9C,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,IAOpC;IACC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,GAAG,IAAI,CAAA;IAEtE,MAAM,QAAQ,GAAG,UAAU,EAAE,CAAA;IAE7B,MAAM,WAAW,GAAG,IAAI,GAAG,EAAkD,CAAA;IAC7E,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC;QAChC,OAAO,EAAE,SAAS;QAClB,MAAM,EAAE,MAAM;QACd,SAAS,EAAE,SAAS;QACpB,OAAO,EAAE,OAAO;KACjB,CAAC,CAAA;IAEF,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,OAAO,GAAG,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,EAAE,CAAA;QACtD,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;QACjB,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,WAAW,EAAE,OAAO,CAAC,CAAA;IAC3C,CAAC;IACD,MAAM,CAAC,KAAK,CACV;QACE,IAAI,EAAE,IAAI,CAAC,MAAM;QACjB,QAAQ,EAAE,QAAQ,EAAE;KACrB,EACD,UAAU,CACX,CAAA;IACD,OAAO,WAAW,CAAA;AACpB,CAAC"}
package/dist/types.d.ts CHANGED
@@ -19,4 +19,6 @@ export type EncodedTransaction = Schema['transactions']['$inferInsert'];
19
19
  export type EncodedBlockWithTransactions = Simplify<EncodedBlock & {
20
20
  transactions: EncodedTransaction[];
21
21
  }>;
22
+ export type TransactionsMap = Map<`0x${string}`, EncodedTransaction>;
23
+ export type BlocksMap = Map<bigint, EncodedBlock>;
22
24
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AACzC,OAAO,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,MAAM,CAAA;AAE9C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAC/C;;GAEG;AACH,MAAM,MAAM,WAAW,CAAC,UAAU,GAAG,OAAO,EAAE,SAAS,GAAG,KAAK,IAC3D;IACE,KAAK,EAAE,SAAS,CAAA;IAChB,MAAM,CAAC,EAAE,SAAS,CAAA;CACnB,GACD;IACE,MAAM,EAAE,UAAU,CAAA;IAClB,KAAK,CAAC,EAAE,SAAS,CAAA;CAClB,CAAA;AAEL,MAAM,MAAM,aAAa,GAAG,WAAW,CAAC,OAAO,CAAC,CAAA;AAEhD,MAAM,MAAM,gBAAgB,GAAG,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,CAAA;AACjE,MAAM,MAAM,UAAU,GAAG,KAAK,CAC5B,MAAM,EACN,IAAI,EACJ,QAAQ,GAAG,MAAM,GAAG,WAAW,EAC/B,gBAAgB,CACjB,CAAA;AACD,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC,cAAc,CAAC,CAAA;AAC3D,MAAM,MAAM,kBAAkB,GAAG,MAAM,CAAC,cAAc,CAAC,CAAC,cAAc,CAAC,CAAA;AACvE,MAAM,MAAM,4BAA4B,GAAG,QAAQ,CACjD,YAAY,GAAG;IACb,YAAY,EAAE,kBAAkB,EAAE,CAAA;CACnC,CACF,CAAA"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AACzC,OAAO,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,MAAM,CAAA;AAE9C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAC/C;;GAEG;AACH,MAAM,MAAM,WAAW,CAAC,UAAU,GAAG,OAAO,EAAE,SAAS,GAAG,KAAK,IAC3D;IACE,KAAK,EAAE,SAAS,CAAA;IAChB,MAAM,CAAC,EAAE,SAAS,CAAA;CACnB,GACD;IACE,MAAM,EAAE,UAAU,CAAA;IAClB,KAAK,CAAC,EAAE,SAAS,CAAA;CAClB,CAAA;AAEL,MAAM,MAAM,aAAa,GAAG,WAAW,CAAC,OAAO,CAAC,CAAA;AAEhD,MAAM,MAAM,gBAAgB,GAAG,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,CAAA;AACjE,MAAM,MAAM,UAAU,GAAG,KAAK,CAC5B,MAAM,EACN,IAAI,EACJ,QAAQ,GAAG,MAAM,GAAG,WAAW,EAC/B,gBAAgB,CACjB,CAAA;AACD,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC,cAAc,CAAC,CAAA;AAC3D,MAAM,MAAM,kBAAkB,GAAG,MAAM,CAAC,cAAc,CAAC,CAAC,cAAc,CAAC,CAAA;AACvE,MAAM,MAAM,4BAA4B,GAAG,QAAQ,CACjD,YAAY,GAAG;IACb,YAAY,EAAE,kBAAkB,EAAE,CAAA;CACnC,CACF,CAAA;AACD,MAAM,MAAM,eAAe,GAAG,GAAG,CAAC,KAAK,MAAM,EAAE,EAAE,kBAAkB,CAAC,CAAA;AACpE,MAAM,MAAM,SAAS,GAAG,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hugomrdias/foxer",
3
- "version": "0.1.9",
3
+ "version": "0.1.11",
4
4
  "description": "Foxer is a all-in-one application server for Filecoin.",
5
5
  "license": "Apache-2.0 OR MIT",
6
6
  "author": "Hugo Dias <hugomrdias@gmail.com>",
@@ -51,22 +51,24 @@
51
51
  "prepublishOnly": "bun run build"
52
52
  },
53
53
  "dependencies": {
54
- "@bluwy/giget-core": "^0.1.6",
54
+ "@bluwy/giget-core": "^0.1.7",
55
55
  "@clack/prompts": "^1.1.0",
56
- "@electric-sql/pglite": "^0.3.16",
56
+ "@electric-sql/pglite": "^0.4.1",
57
57
  "@hono/node-server": "^1.19.11",
58
58
  "@libpg-query/parser": "^17.6.3",
59
59
  "@pgsql/traverse": "^17.2.4",
60
60
  "abitype": "^1.2.3",
61
61
  "cleye": "^2.3.0",
62
62
  "dotenv": "^17.2.3",
63
- "drizzle-orm": "1.0.0-beta.15-859cf75",
63
+ "drizzle-orm": "1.0.0-beta.17-67b1795",
64
64
  "exit-hook": "^5.1.0",
65
- "hono": "^4.12.7",
65
+ "hono": "^4.12.9",
66
66
  "hono-pino": "^0.10.3",
67
67
  "http-shutdown": "^1.2.2",
68
+ "iso-base": "^4.3.1",
68
69
  "p-queue": "^9.1.0",
69
70
  "pg": "^8.20.0",
71
+ "pgserve": "^1.1.6",
70
72
  "picocolors": "^1.1.1",
71
73
  "pino": "^10.3.1",
72
74
  "postgres": "^3.4.8",
@@ -77,12 +79,12 @@
77
79
  "devDependencies": {
78
80
  "@pgsql/types": "^17.6.2",
79
81
  "@types/node": "^25.5.0",
80
- "@types/pg": "^8.18.0",
81
- "type-fest": "^5.4.4",
82
+ "@types/pg": "^8.20.0",
83
+ "type-fest": "^5.5.0",
82
84
  "typescript": "^5.9.2"
83
85
  },
84
86
  "peerDependencies": {
85
- "hono": ">=4.5",
86
- "viem": "2.x"
87
+ "hono": ">=4.12.9",
88
+ "viem": "2.47.6"
87
89
  }
88
90
  }
package/src/config/env.ts CHANGED
@@ -17,7 +17,7 @@ const envSchema = z.object({
17
17
  LOG_MODE: z.enum(['pretty', 'json']).default('pretty'),
18
18
  })
19
19
 
20
- export function createEnv(logger: Logger) {
20
+ export function createEnv(_logger: Logger) {
21
21
  const parsed = envSchema.safeParse(process.env)
22
22
 
23
23
  if (!parsed.success) {
@@ -25,6 +25,6 @@ export function createEnv(logger: Logger) {
25
25
  `Failed to parse environment variables: \n ${z.prettifyError(parsed.error)}`
26
26
  )
27
27
  }
28
- logger.debug({ env: parsed.data }, 'env parsed')
28
+ // logger.debug({ env: parsed.data }, 'env parsed')
29
29
  return parsed.data
30
30
  }
@@ -1,6 +1,6 @@
1
1
  /** biome-ignore-all lint/style/noNonNullAssertion: its ok */
2
2
 
3
- import { gte } from 'drizzle-orm'
3
+ import { and, gte, inArray } from 'drizzle-orm'
4
4
  import {
5
5
  getTableConfig,
6
6
  type PgAsyncTransaction,
@@ -8,14 +8,15 @@ import {
8
8
  type PgQueryResultHKT,
9
9
  type PgTable,
10
10
  } from 'drizzle-orm/pg-core'
11
- import type { PublicClient } from 'viem'
11
+ import type { Hash, PublicClient } from 'viem'
12
12
 
13
- import type { FilteredContracts } from '../../config/config.ts'
14
13
  import { MAX_QUERY_PARAMS } from '../../contants.ts'
15
14
  import { safeGetBlock } from '../../rpc/get-block.ts'
16
15
  import type {
17
- EncodedBlockWithTransactions,
16
+ BlocksMap,
17
+ EncodedBlock,
18
18
  EncodedTransaction,
19
+ TransactionsMap,
19
20
  } from '../../types.ts'
20
21
  import type { Logger } from '../../utils/logger.ts'
21
22
  import { startClock } from '../../utils/timer.ts'
@@ -73,19 +74,20 @@ function getTablesWithBlockNumberColumn(fullSchema: Record<string, unknown>) {
73
74
  */
74
75
  export async function cacheBlockAndTransactions(args: {
75
76
  db: Database<typeof schema, typeof relations>
76
- block: EncodedBlockWithTransactions
77
+ blocks: EncodedBlock[]
78
+ transactions: EncodedTransaction[]
77
79
  logger: Logger
78
80
  }): Promise<void> {
79
- const { db, block } = args
81
+ const { db, blocks, transactions } = args
80
82
 
81
83
  await db.transaction(async (tx) => {
82
84
  await insertBlocksInChunks({
83
85
  db: tx,
84
- blocks: [block],
86
+ blocks,
85
87
  })
86
88
  await insertTransactionsInChunks({
87
89
  db: tx,
88
- transactions: block.transactions,
90
+ transactions,
89
91
  })
90
92
  })
91
93
  }
@@ -105,8 +107,8 @@ export async function getBlocksInRange(
105
107
  db: Database<typeof schema, typeof relations>,
106
108
  blockNumbers: bigint[],
107
109
  client: PublicClient,
108
- contracts: FilteredContracts
109
- ): Promise<Map<bigint, EncodedBlockWithTransactions>> {
110
+ logsTxs: Hash[]
111
+ ): Promise<{ blocks: BlocksMap; transactions: TransactionsMap }> {
110
112
  const endClock = startClock()
111
113
  const firstBlockNumber = blockNumbers[0]!
112
114
  const lastBlockNumber = blockNumbers[blockNumbers.length - 1]!
@@ -117,74 +119,72 @@ export async function getBlocksInRange(
117
119
  // contractAddresses: contracts.addresses,
118
120
  // })
119
121
 
120
- const r = await db.query.blocks.findMany({
121
- with: {
122
- transactions: {
123
- where: {
124
- AND: [
125
- { blockNumber: { gte: firstBlockNumber } },
126
- { blockNumber: { lte: lastBlockNumber } },
127
- {
128
- to: {
129
- in: contracts.addresses,
130
- },
131
- },
132
- ],
133
- },
122
+ const [blocks, txs] = await Promise.all([
123
+ db.query.blocks.findMany({
124
+ where: {
125
+ AND: [
126
+ { number: { gte: firstBlockNumber } },
127
+ { number: { lte: lastBlockNumber } },
128
+ ],
134
129
  },
135
- },
136
- where: {
137
- AND: [
138
- { number: { gte: firstBlockNumber } },
139
- { number: { lte: lastBlockNumber } },
140
- ],
141
- },
142
- })
130
+ }),
131
+ db
132
+ .select()
133
+ .from(schema.transactions)
134
+ .where(and(inArray(schema.transactions.hash, logsTxs))),
135
+ ])
136
+
137
+ const transactionByHash = new Map<`0x${string}`, EncodedTransaction>()
138
+ for (const tx of txs) {
139
+ transactionByHash.set(tx.hash, tx)
140
+ }
143
141
 
144
- const blocksByNumber = new Map<bigint, EncodedBlockWithTransactions>()
142
+ const blocksByNumber = new Map<bigint, EncodedBlock>()
145
143
  const missing = new Set(blockNumbers)
146
144
 
147
- for (const block of r) {
145
+ for (const block of blocks) {
148
146
  blocksByNumber.set(block.number, block)
149
147
  missing.delete(block.number)
150
148
  }
151
149
 
152
150
  const missingBlockNumbers = [...missing]
153
- const newBlocks: EncodedBlockWithTransactions[] = []
151
+ const newBlocks: EncodedBlock[] = []
154
152
  const newTransactions: EncodedTransaction[] = []
155
153
 
156
154
  await Promise.all(
157
155
  missingBlockNumbers.map(async (blockNumber) => {
158
156
  const block = await safeGetBlock({ client, blockNumber, db })
159
- const transactions = block.transactions
160
157
  blocksByNumber.set(blockNumber, block)
161
- newBlocks.push(block)
158
+ const { transactions, ..._block } = block
159
+ newBlocks.push(_block)
160
+
162
161
  if (transactions.length > 0) {
163
162
  newTransactions.push(...transactions)
164
163
  }
164
+ for (const tx of transactions) {
165
+ transactionByHash.set(tx.hash, tx)
166
+ }
165
167
  })
166
168
  )
167
169
 
168
- await db.transaction(async (tx) => {
169
- await insertBlocksInChunks({
170
- db: tx,
171
- blocks: newBlocks,
172
- })
173
- await insertTransactionsInChunks({
174
- db: tx,
175
- transactions: newTransactions,
176
- })
170
+ await cacheBlockAndTransactions({
171
+ db,
172
+ blocks: newBlocks,
173
+ transactions: newTransactions,
174
+ logger,
177
175
  })
178
176
 
179
177
  logger.trace(
180
178
  {
181
- blocks: blocksByNumber.size,
182
- missing: missingBlockNumbers.length,
179
+ blocks: blocks.length,
180
+ txs: txs.length,
181
+ newBlocks: newBlocks.length,
182
+ newTxs: newTransactions.length,
183
183
  duration: endClock(),
184
184
  },
185
- 'get blocks'
185
+ 'get blocks and txs'
186
186
  )
187
- return blocksByNumber
187
+ return { blocks: blocksByNumber, transactions: transactionByHash }
188
188
  }
189
189
 
190
190
  /**
@@ -192,7 +192,7 @@ export async function getBlocksInRange(
192
192
  */
193
193
  export async function insertBlocksInChunks(args: {
194
194
  db: PgAsyncTransaction<PgQueryResultHKT, typeof schema>
195
- blocks: EncodedBlockWithTransactions[]
195
+ blocks: EncodedBlock[]
196
196
  }): Promise<void> {
197
197
  const { db, blocks } = args
198
198
  if (blocks.length === 0) return
package/src/db/client.ts CHANGED
@@ -84,8 +84,12 @@ export function createDatabase<
84
84
  // Postgres
85
85
  if (driver === 'postgres' && url) {
86
86
  const pool = new Pool({
87
- ...options,
87
+ application_name: 'foxer',
88
+ connectionTimeoutMillis: 5_000,
89
+ idleTimeoutMillis: 30_000,
90
+ max: 10,
88
91
  connectionString: url,
92
+ ...options,
89
93
  })
90
94
  const db = drizzleNodePostgres({
91
95
  client: pool,
@@ -1,4 +1,5 @@
1
1
  import { customType } from 'drizzle-orm/pg-core'
2
+ import { hex as hexCodec } from 'iso-base/rfc4648'
2
3
  import { type Address, type Hash, type Hex, stringify } from 'viem'
3
4
 
4
5
  export const numeric78 = customType<{ data: bigint; driverData: string }>({
@@ -90,16 +91,26 @@ export const jsonb = customType<{ data: unknown; driverData: string }>({
90
91
  },
91
92
  })
92
93
 
93
- export const bytea = customType<{ data: Hex; driverData: Buffer }>({
94
+ export const bytea = customType<{ data: Hex; driverData: Uint8Array }>({
94
95
  dataType() {
95
96
  return 'bytea'
96
97
  },
97
- toDriver(value: string): Buffer {
98
+ toDriver(value: string): Uint8Array {
98
99
  return Buffer.from(value.slice(2), 'hex')
99
100
  },
100
- fromDriver(value: Buffer): Hex {
101
- const hex = value.toString('hex')
102
- const _value = hex.startsWith('\\x') ? hex.slice(2) : hex
103
- return `0x${_value}` as Hex
101
+ fromDriver(value: unknown): Hex {
102
+ if (typeof value === 'string') {
103
+ return `0x${value.slice(2)}` as Hex
104
+ }
105
+
106
+ if (value instanceof Buffer) {
107
+ return `0x${value.toString('hex')}` as Hex
108
+ }
109
+
110
+ if (value instanceof Uint8Array) {
111
+ return `0x${hexCodec.encode(value)}` as Hex
112
+ }
113
+
114
+ throw new Error('Invalid value')
104
115
  },
105
116
  })
@@ -35,6 +35,8 @@ export const transactions = pgTable(
35
35
  },
36
36
  (table) => [
37
37
  index('transactions_block_number_index').on(table.blockNumber),
38
- index('transactions_to_index').on(table.to),
38
+ index('transactions_to_block_number_index')
39
+ .on(table.to, table.blockNumber)
40
+ .concurrently(),
39
41
  ]
40
42
  )
@@ -3,7 +3,7 @@ import type { GetEventArgs, Log } from 'viem'
3
3
 
4
4
  import type { InternalConfig } from '../config/config'
5
5
  import type { Database } from '../db/client'
6
- import type { EncodedBlockWithTransactions, EncodedTransaction } from '../types'
6
+ import type { EncodedBlock, EncodedTransaction } from '../types'
7
7
  import type { Logger } from '../utils/logger'
8
8
  import type {
9
9
  ContractAbiByEventKey,
@@ -34,7 +34,7 @@ export type DecodedEvent<
34
34
  { EnableUnion: false; IndexedOnly: false; Required: true }
35
35
  >
36
36
  log: Log<bigint, number, false, ContractAbiEventByEventKey<C, Event>>
37
- block: EncodedBlockWithTransactions
37
+ block: EncodedBlock
38
38
  transaction: EncodedTransaction
39
39
  }
40
40
 
@@ -81,7 +81,7 @@ export class HookRegistry<
81
81
  { EnableUnion: false; IndexedOnly: false; Required: true }
82
82
  >
83
83
  log: Log<bigint, number, false, ContractAbiEventByEventKey<C, K>>
84
- block: EncodedBlockWithTransactions
84
+ block: EncodedBlock
85
85
  transaction: EncodedTransaction
86
86
  context: HookContext<TSchema, TRelations>
87
87
  }): Promise<void> {
@@ -1,3 +1,4 @@
1
+ import type { Hash } from 'viem'
1
2
  import { filterContracts, type InternalConfig } from '../config/config.ts'
2
3
  import { getBlocksInRange } from '../db/actions/blocks.ts'
3
4
  import type { Database } from '../db/client.ts'
@@ -61,17 +62,30 @@ export async function runBackfill(args: {
61
62
  blockNumber += 1n
62
63
  }
63
64
 
64
- const [blocksByNumber, logsByBlock] = await Promise.all([
65
- getBlocksInRange(logger, db, batchBlockNumbers, client, windowContracts),
66
- getLogsInRange({
65
+ const logsByBlock = await getLogsInRange({
66
+ logger,
67
+ client,
68
+ addresses: windowContracts.addresses,
69
+ events: windowContracts.eventAbis,
70
+ fromBlock: cursor,
71
+ toBlock,
72
+ })
73
+
74
+ const logsTxsSet = new Set<Hash>()
75
+ for (const logs of logsByBlock.values()) {
76
+ for (const log of logs) {
77
+ logsTxsSet.add(log.transactionHash)
78
+ }
79
+ }
80
+
81
+ const { blocks: blocksByNumber, transactions: transactionsMap } =
82
+ await getBlocksInRange(
67
83
  logger,
84
+ db,
85
+ batchBlockNumbers,
68
86
  client,
69
- addresses: windowContracts.addresses,
70
- events: windowContracts.eventAbis,
71
- fromBlock: cursor,
72
- toBlock,
73
- }),
74
- ])
87
+ Array.from(logsTxsSet)
88
+ )
75
89
 
76
90
  let blockIndex = 0
77
91
 
@@ -81,15 +95,19 @@ export async function runBackfill(args: {
81
95
  const blockNumber = batchBlockNumbers[blockIndex]
82
96
  const prefetchedBlock = blocksByNumber.get(blockNumber)
83
97
 
98
+ if (!prefetchedBlock) {
99
+ throw new Error(`Block ${blockNumber} not found`)
100
+ }
101
+
84
102
  await processBlock({
85
103
  logger,
86
104
  config,
87
105
  db: tx,
88
106
  client,
89
107
  registry,
90
- blockNumber,
91
108
  logs: logsByBlock.get(blockNumber) ?? [],
92
109
  block: prefetchedBlock,
110
+ transactionsMap,
93
111
  type: 'backfill',
94
112
  contracts: windowContracts,
95
113
  })
@@ -6,8 +6,7 @@ import type { Database } from '../db/client.ts'
6
6
  import type { relations, schema } from '../db/schema/index.ts'
7
7
  import { withTransaction } from '../db/transaction.ts'
8
8
  import type { HookRegistry } from '../hooks/registry.ts'
9
- import { safeGetBlock } from '../rpc/get-block.ts'
10
- import type { EncodedBlockWithTransactions, EncodedTransaction } from '../types'
9
+ import type { EncodedBlock, TransactionsMap } from '../types'
11
10
  import type { Logger } from '../utils/logger.ts'
12
11
  import { ensureParentContinuity } from './reorg.ts'
13
12
 
@@ -24,9 +23,9 @@ export async function processBlock(args: {
24
23
  db: Database<typeof schema, typeof relations>
25
24
  client: PublicClient
26
25
  registry: HookRegistry<NonNullable<unknown>>
27
- blockNumber: bigint
28
- logs?: Log<bigint, number, false, AbiEvent>[]
29
- block?: EncodedBlockWithTransactions
26
+ logs: Log<bigint, number, false, AbiEvent>[]
27
+ block: EncodedBlock
28
+ transactionsMap: TransactionsMap
30
29
  type: 'backfill' | 'live'
31
30
  contracts: FilteredContracts
32
31
  }): Promise<ProcessBlockResult> {
@@ -36,41 +35,12 @@ export async function processBlock(args: {
36
35
  db,
37
36
  client,
38
37
  registry,
39
- blockNumber,
40
- block: prefetchedBlock,
41
- logs: prefetchedLogs,
38
+ block,
39
+ transactionsMap,
40
+ logs,
42
41
  type,
43
42
  contracts,
44
43
  } = args
45
- const transactionByHash = new Map<`0x${string}`, EncodedTransaction>()
46
-
47
- let block: EncodedBlockWithTransactions | undefined
48
- let logs: Log<bigint, number, false, AbiEvent>[] | undefined
49
-
50
- if (prefetchedBlock) {
51
- block = prefetchedBlock
52
- }
53
- if (prefetchedLogs) {
54
- logs = prefetchedLogs
55
- }
56
- if (!block || !logs) {
57
- const [blockResult, logsResult] = await Promise.all([
58
- safeGetBlock({ client, blockNumber, db }),
59
- client.getLogs({
60
- address: contracts.addresses,
61
- events: contracts.eventAbis,
62
- fromBlock: blockNumber,
63
- toBlock: blockNumber,
64
- }),
65
- ])
66
-
67
- block = blockResult
68
- logs = logsResult
69
- }
70
-
71
- for (const tx of block.transactions) {
72
- transactionByHash.set(tx.hash, tx)
73
- }
74
44
 
75
45
  if (type === 'live') {
76
46
  const rewindTo = await ensureParentContinuity({
@@ -88,7 +58,8 @@ export async function processBlock(args: {
88
58
  if (type === 'live') {
89
59
  await cacheBlockAndTransactions({
90
60
  db: tx,
91
- block,
61
+ blocks: [block],
62
+ transactions: Array.from(transactionsMap.values()),
92
63
  logger,
93
64
  })
94
65
  }
@@ -108,7 +79,7 @@ export async function processBlock(args: {
108
79
  if (!contracts.eventNames.has(eventName)) {
109
80
  continue
110
81
  }
111
- const transaction = transactionByHash.get(log.transactionHash)
82
+ const transaction = transactionsMap.get(log.transactionHash)
112
83
 
113
84
  if (!transaction) {
114
85
  logger.debug(