@subsquid/ponder 0.16.6

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 (626) hide show
  1. package/CHANGELOG.md +3452 -0
  2. package/LICENSE +21 -0
  3. package/README.md +182 -0
  4. package/dist/esm/bin/commands/codegen.js +46 -0
  5. package/dist/esm/bin/commands/codegen.js.map +1 -0
  6. package/dist/esm/bin/commands/createViews.js +196 -0
  7. package/dist/esm/bin/commands/createViews.js.map +1 -0
  8. package/dist/esm/bin/commands/dev.js +430 -0
  9. package/dist/esm/bin/commands/dev.js.map +1 -0
  10. package/dist/esm/bin/commands/list.js +148 -0
  11. package/dist/esm/bin/commands/list.js.map +1 -0
  12. package/dist/esm/bin/commands/prune.js +223 -0
  13. package/dist/esm/bin/commands/prune.js.map +1 -0
  14. package/dist/esm/bin/commands/serve.js +198 -0
  15. package/dist/esm/bin/commands/serve.js.map +1 -0
  16. package/dist/esm/bin/commands/start.js +253 -0
  17. package/dist/esm/bin/commands/start.js.map +1 -0
  18. package/dist/esm/bin/isolatedController.js +200 -0
  19. package/dist/esm/bin/isolatedController.js.map +1 -0
  20. package/dist/esm/bin/isolatedWorker.js +146 -0
  21. package/dist/esm/bin/isolatedWorker.js.map +1 -0
  22. package/dist/esm/bin/ponder.js +137 -0
  23. package/dist/esm/bin/ponder.js.map +1 -0
  24. package/dist/esm/bin/utils/codegen.js +25 -0
  25. package/dist/esm/bin/utils/codegen.js.map +1 -0
  26. package/dist/esm/bin/utils/exit.js +100 -0
  27. package/dist/esm/bin/utils/exit.js.map +1 -0
  28. package/dist/esm/build/config.js +746 -0
  29. package/dist/esm/build/config.js.map +1 -0
  30. package/dist/esm/build/factory.js +82 -0
  31. package/dist/esm/build/factory.js.map +1 -0
  32. package/dist/esm/build/index.js +567 -0
  33. package/dist/esm/build/index.js.map +1 -0
  34. package/dist/esm/build/plugin.js +53 -0
  35. package/dist/esm/build/plugin.js.map +1 -0
  36. package/dist/esm/build/pre.js +83 -0
  37. package/dist/esm/build/pre.js.map +1 -0
  38. package/dist/esm/build/schema.js +202 -0
  39. package/dist/esm/build/schema.js.map +1 -0
  40. package/dist/esm/build/stacktrace.js +137 -0
  41. package/dist/esm/build/stacktrace.js.map +1 -0
  42. package/dist/esm/client/index.js +441 -0
  43. package/dist/esm/client/index.js.map +1 -0
  44. package/dist/esm/config/address.js +2 -0
  45. package/dist/esm/config/address.js.map +1 -0
  46. package/dist/esm/config/eventFilter.js +2 -0
  47. package/dist/esm/config/eventFilter.js.map +1 -0
  48. package/dist/esm/config/index.js +2 -0
  49. package/dist/esm/config/index.js.map +1 -0
  50. package/dist/esm/config/utilityTypes.js +2 -0
  51. package/dist/esm/config/utilityTypes.js.map +1 -0
  52. package/dist/esm/database/actions.js +445 -0
  53. package/dist/esm/database/actions.js.map +1 -0
  54. package/dist/esm/database/index.js +604 -0
  55. package/dist/esm/database/index.js.map +1 -0
  56. package/dist/esm/database/queryBuilder.js +314 -0
  57. package/dist/esm/database/queryBuilder.js.map +1 -0
  58. package/dist/esm/drizzle/bigint.js +38 -0
  59. package/dist/esm/drizzle/bigint.js.map +1 -0
  60. package/dist/esm/drizzle/bytes.js +47 -0
  61. package/dist/esm/drizzle/bytes.js.map +1 -0
  62. package/dist/esm/drizzle/hex.js +40 -0
  63. package/dist/esm/drizzle/hex.js.map +1 -0
  64. package/dist/esm/drizzle/index.js +28 -0
  65. package/dist/esm/drizzle/index.js.map +1 -0
  66. package/dist/esm/drizzle/json.js +123 -0
  67. package/dist/esm/drizzle/json.js.map +1 -0
  68. package/dist/esm/drizzle/kit/index.js +927 -0
  69. package/dist/esm/drizzle/kit/index.js.map +1 -0
  70. package/dist/esm/drizzle/onchain.js +184 -0
  71. package/dist/esm/drizzle/onchain.js.map +1 -0
  72. package/dist/esm/drizzle/text.js +61 -0
  73. package/dist/esm/drizzle/text.js.map +1 -0
  74. package/dist/esm/graphql/graphiql.html.js +59 -0
  75. package/dist/esm/graphql/graphiql.html.js.map +1 -0
  76. package/dist/esm/graphql/index.js +964 -0
  77. package/dist/esm/graphql/index.js.map +1 -0
  78. package/dist/esm/graphql/json.js +42 -0
  79. package/dist/esm/graphql/json.js.map +1 -0
  80. package/dist/esm/graphql/middleware.js +83 -0
  81. package/dist/esm/graphql/middleware.js.map +1 -0
  82. package/dist/esm/index.js +9 -0
  83. package/dist/esm/index.js.map +1 -0
  84. package/dist/esm/indexing/addStackTrace.js +54 -0
  85. package/dist/esm/indexing/addStackTrace.js.map +1 -0
  86. package/dist/esm/indexing/client.js +675 -0
  87. package/dist/esm/indexing/client.js.map +1 -0
  88. package/dist/esm/indexing/index.js +663 -0
  89. package/dist/esm/indexing/index.js.map +1 -0
  90. package/dist/esm/indexing/profile.js +584 -0
  91. package/dist/esm/indexing/profile.js.map +1 -0
  92. package/dist/esm/indexing-store/cache.js +666 -0
  93. package/dist/esm/indexing-store/cache.js.map +1 -0
  94. package/dist/esm/indexing-store/index.js +461 -0
  95. package/dist/esm/indexing-store/index.js.map +1 -0
  96. package/dist/esm/indexing-store/profile.js +428 -0
  97. package/dist/esm/indexing-store/profile.js.map +1 -0
  98. package/dist/esm/indexing-store/utils.js +111 -0
  99. package/dist/esm/indexing-store/utils.js.map +1 -0
  100. package/dist/esm/internal/common.js +2 -0
  101. package/dist/esm/internal/common.js.map +1 -0
  102. package/dist/esm/internal/errors.js +300 -0
  103. package/dist/esm/internal/errors.js.map +1 -0
  104. package/dist/esm/internal/logger.js +178 -0
  105. package/dist/esm/internal/logger.js.map +1 -0
  106. package/dist/esm/internal/metrics.js +1049 -0
  107. package/dist/esm/internal/metrics.js.map +1 -0
  108. package/dist/esm/internal/options.js +73 -0
  109. package/dist/esm/internal/options.js.map +1 -0
  110. package/dist/esm/internal/shutdown.js +24 -0
  111. package/dist/esm/internal/shutdown.js.map +1 -0
  112. package/dist/esm/internal/telemetry.js +200 -0
  113. package/dist/esm/internal/telemetry.js.map +1 -0
  114. package/dist/esm/internal/types.js +2 -0
  115. package/dist/esm/internal/types.js.map +1 -0
  116. package/dist/esm/rpc/actions.js +987 -0
  117. package/dist/esm/rpc/actions.js.map +1 -0
  118. package/dist/esm/rpc/http.js +130 -0
  119. package/dist/esm/rpc/http.js.map +1 -0
  120. package/dist/esm/rpc/index.js +749 -0
  121. package/dist/esm/rpc/index.js.map +1 -0
  122. package/dist/esm/runtime/events.js +664 -0
  123. package/dist/esm/runtime/events.js.map +1 -0
  124. package/dist/esm/runtime/filter.js +443 -0
  125. package/dist/esm/runtime/filter.js.map +1 -0
  126. package/dist/esm/runtime/fragments.js +478 -0
  127. package/dist/esm/runtime/fragments.js.map +1 -0
  128. package/dist/esm/runtime/historical.js +953 -0
  129. package/dist/esm/runtime/historical.js.map +1 -0
  130. package/dist/esm/runtime/index.js +316 -0
  131. package/dist/esm/runtime/index.js.map +1 -0
  132. package/dist/esm/runtime/isolated.js +463 -0
  133. package/dist/esm/runtime/isolated.js.map +1 -0
  134. package/dist/esm/runtime/multichain.js +510 -0
  135. package/dist/esm/runtime/multichain.js.map +1 -0
  136. package/dist/esm/runtime/omnichain.js +545 -0
  137. package/dist/esm/runtime/omnichain.js.map +1 -0
  138. package/dist/esm/runtime/realtime.js +724 -0
  139. package/dist/esm/runtime/realtime.js.map +1 -0
  140. package/dist/esm/server/error.js +56 -0
  141. package/dist/esm/server/error.js.map +1 -0
  142. package/dist/esm/server/index.js +121 -0
  143. package/dist/esm/server/index.js.map +1 -0
  144. package/dist/esm/sync-historical/index.js +711 -0
  145. package/dist/esm/sync-historical/index.js.map +1 -0
  146. package/dist/esm/sync-historical/portal-transform.js +104 -0
  147. package/dist/esm/sync-historical/portal-transform.js.map +1 -0
  148. package/dist/esm/sync-historical/portal.js +592 -0
  149. package/dist/esm/sync-historical/portal.js.map +1 -0
  150. package/dist/esm/sync-realtime/bloom.js +76 -0
  151. package/dist/esm/sync-realtime/bloom.js.map +1 -0
  152. package/dist/esm/sync-realtime/index.js +917 -0
  153. package/dist/esm/sync-realtime/index.js.map +1 -0
  154. package/dist/esm/sync-store/encode.js +105 -0
  155. package/dist/esm/sync-store/encode.js.map +1 -0
  156. package/dist/esm/sync-store/index.js +885 -0
  157. package/dist/esm/sync-store/index.js.map +1 -0
  158. package/dist/esm/sync-store/migrations.js +1595 -0
  159. package/dist/esm/sync-store/migrations.js.map +1 -0
  160. package/dist/esm/sync-store/schema.js +181 -0
  161. package/dist/esm/sync-store/schema.js.map +1 -0
  162. package/dist/esm/types/db.js +2 -0
  163. package/dist/esm/types/db.js.map +1 -0
  164. package/dist/esm/types/eth.js +2 -0
  165. package/dist/esm/types/eth.js.map +1 -0
  166. package/dist/esm/types/utils.js +2 -0
  167. package/dist/esm/types/utils.js.map +1 -0
  168. package/dist/esm/types/virtual.js +2 -0
  169. package/dist/esm/types/virtual.js.map +1 -0
  170. package/dist/esm/ui/app.js +157 -0
  171. package/dist/esm/ui/app.js.map +1 -0
  172. package/dist/esm/ui/index.js +29 -0
  173. package/dist/esm/ui/index.js.map +1 -0
  174. package/dist/esm/ui/patch.js +140 -0
  175. package/dist/esm/ui/patch.js.map +1 -0
  176. package/dist/esm/utils/abi.js +55 -0
  177. package/dist/esm/utils/abi.js.map +1 -0
  178. package/dist/esm/utils/bigint.js +37 -0
  179. package/dist/esm/utils/bigint.js.map +1 -0
  180. package/dist/esm/utils/chains.js +21 -0
  181. package/dist/esm/utils/chains.js.map +1 -0
  182. package/dist/esm/utils/checkpoint.js +139 -0
  183. package/dist/esm/utils/checkpoint.js.map +1 -0
  184. package/dist/esm/utils/chunk.js +8 -0
  185. package/dist/esm/utils/chunk.js.map +1 -0
  186. package/dist/esm/utils/copy.js +129 -0
  187. package/dist/esm/utils/copy.js.map +1 -0
  188. package/dist/esm/utils/date.js +27 -0
  189. package/dist/esm/utils/date.js.map +1 -0
  190. package/dist/esm/utils/debug.js +2 -0
  191. package/dist/esm/utils/debug.js.map +1 -0
  192. package/dist/esm/utils/decodeAbiParameters.js +290 -0
  193. package/dist/esm/utils/decodeAbiParameters.js.map +1 -0
  194. package/dist/esm/utils/decodeEventLog.js +75 -0
  195. package/dist/esm/utils/decodeEventLog.js.map +1 -0
  196. package/dist/esm/utils/dedupe.js +29 -0
  197. package/dist/esm/utils/dedupe.js.map +1 -0
  198. package/dist/esm/utils/duplicates.js +19 -0
  199. package/dist/esm/utils/duplicates.js.map +1 -0
  200. package/dist/esm/utils/estimate.js +6 -0
  201. package/dist/esm/utils/estimate.js.map +1 -0
  202. package/dist/esm/utils/finality.js +38 -0
  203. package/dist/esm/utils/finality.js.map +1 -0
  204. package/dist/esm/utils/format.js +20 -0
  205. package/dist/esm/utils/format.js.map +1 -0
  206. package/dist/esm/utils/generators.js +121 -0
  207. package/dist/esm/utils/generators.js.map +1 -0
  208. package/dist/esm/utils/hash.js +11 -0
  209. package/dist/esm/utils/hash.js.map +1 -0
  210. package/dist/esm/utils/interval.js +171 -0
  211. package/dist/esm/utils/interval.js.map +1 -0
  212. package/dist/esm/utils/lowercase.js +7 -0
  213. package/dist/esm/utils/lowercase.js.map +1 -0
  214. package/dist/esm/utils/mutex.js +26 -0
  215. package/dist/esm/utils/mutex.js.map +1 -0
  216. package/dist/esm/utils/never.js +4 -0
  217. package/dist/esm/utils/never.js.map +1 -0
  218. package/dist/esm/utils/offset.js +101 -0
  219. package/dist/esm/utils/offset.js.map +1 -0
  220. package/dist/esm/utils/order.js +18 -0
  221. package/dist/esm/utils/order.js.map +1 -0
  222. package/dist/esm/utils/partition.js +46 -0
  223. package/dist/esm/utils/partition.js.map +1 -0
  224. package/dist/esm/utils/pg.js +175 -0
  225. package/dist/esm/utils/pg.js.map +1 -0
  226. package/dist/esm/utils/pglite.js +80 -0
  227. package/dist/esm/utils/pglite.js.map +1 -0
  228. package/dist/esm/utils/port.js +30 -0
  229. package/dist/esm/utils/port.js.map +1 -0
  230. package/dist/esm/utils/print.js +24 -0
  231. package/dist/esm/utils/print.js.map +1 -0
  232. package/dist/esm/utils/promiseAllSettledWithThrow.js +19 -0
  233. package/dist/esm/utils/promiseAllSettledWithThrow.js.map +1 -0
  234. package/dist/esm/utils/promiseWithResolvers.js +13 -0
  235. package/dist/esm/utils/promiseWithResolvers.js.map +1 -0
  236. package/dist/esm/utils/queue.js +150 -0
  237. package/dist/esm/utils/queue.js.map +1 -0
  238. package/dist/esm/utils/range.js +8 -0
  239. package/dist/esm/utils/range.js.map +1 -0
  240. package/dist/esm/utils/result.js +10 -0
  241. package/dist/esm/utils/result.js.map +1 -0
  242. package/dist/esm/utils/sql-parse.js +1326 -0
  243. package/dist/esm/utils/sql-parse.js.map +1 -0
  244. package/dist/esm/utils/timer.js +9 -0
  245. package/dist/esm/utils/timer.js.map +1 -0
  246. package/dist/esm/utils/truncate.js +15 -0
  247. package/dist/esm/utils/truncate.js.map +1 -0
  248. package/dist/esm/utils/wait.js +10 -0
  249. package/dist/esm/utils/wait.js.map +1 -0
  250. package/dist/esm/utils/zipper.js +67 -0
  251. package/dist/esm/utils/zipper.js.map +1 -0
  252. package/dist/types/bin/commands/codegen.d.ts +5 -0
  253. package/dist/types/bin/commands/codegen.d.ts.map +1 -0
  254. package/dist/types/bin/commands/createViews.d.ts +8 -0
  255. package/dist/types/bin/commands/createViews.d.ts.map +1 -0
  256. package/dist/types/bin/commands/dev.d.ts +5 -0
  257. package/dist/types/bin/commands/dev.d.ts.map +1 -0
  258. package/dist/types/bin/commands/list.d.ts +5 -0
  259. package/dist/types/bin/commands/list.d.ts.map +1 -0
  260. package/dist/types/bin/commands/prune.d.ts +5 -0
  261. package/dist/types/bin/commands/prune.d.ts.map +1 -0
  262. package/dist/types/bin/commands/serve.d.ts +5 -0
  263. package/dist/types/bin/commands/serve.d.ts.map +1 -0
  264. package/dist/types/bin/commands/start.d.ts +19 -0
  265. package/dist/types/bin/commands/start.d.ts.map +1 -0
  266. package/dist/types/bin/isolatedController.d.ts +13 -0
  267. package/dist/types/bin/isolatedController.d.ts.map +1 -0
  268. package/dist/types/bin/isolatedWorker.d.ts +9 -0
  269. package/dist/types/bin/isolatedWorker.d.ts.map +1 -0
  270. package/dist/types/bin/ponder.d.ts +37 -0
  271. package/dist/types/bin/ponder.d.ts.map +1 -0
  272. package/dist/types/bin/utils/codegen.d.ts +6 -0
  273. package/dist/types/bin/utils/codegen.d.ts.map +1 -0
  274. package/dist/types/bin/utils/exit.d.ts +10 -0
  275. package/dist/types/bin/utils/exit.d.ts.map +1 -0
  276. package/dist/types/build/config.d.ts +97 -0
  277. package/dist/types/build/config.d.ts.map +1 -0
  278. package/dist/types/build/factory.d.ts +9 -0
  279. package/dist/types/build/factory.d.ts.map +1 -0
  280. package/dist/types/build/index.d.ts +84 -0
  281. package/dist/types/build/index.d.ts.map +1 -0
  282. package/dist/types/build/plugin.d.ts +4 -0
  283. package/dist/types/build/plugin.d.ts.map +1 -0
  284. package/dist/types/build/pre.d.ts +29 -0
  285. package/dist/types/build/pre.d.ts.map +1 -0
  286. package/dist/types/build/schema.d.ts +20 -0
  287. package/dist/types/build/schema.d.ts.map +1 -0
  288. package/dist/types/build/stacktrace.d.ts +13 -0
  289. package/dist/types/build/stacktrace.d.ts.map +1 -0
  290. package/dist/types/client/index.d.ts +27 -0
  291. package/dist/types/client/index.d.ts.map +1 -0
  292. package/dist/types/config/address.d.ts +35 -0
  293. package/dist/types/config/address.d.ts.map +1 -0
  294. package/dist/types/config/eventFilter.d.ts +18 -0
  295. package/dist/types/config/eventFilter.d.ts.map +1 -0
  296. package/dist/types/config/index.d.ts +150 -0
  297. package/dist/types/config/index.d.ts.map +1 -0
  298. package/dist/types/config/utilityTypes.d.ts +43 -0
  299. package/dist/types/config/utilityTypes.d.ts.map +1 -0
  300. package/dist/types/database/actions.d.ts +99 -0
  301. package/dist/types/database/actions.d.ts.map +1 -0
  302. package/dist/types/database/index.d.ts +493 -0
  303. package/dist/types/database/index.d.ts.map +1 -0
  304. package/dist/types/database/queryBuilder.d.ts +65 -0
  305. package/dist/types/database/queryBuilder.d.ts.map +1 -0
  306. package/dist/types/drizzle/bigint.d.ts +25 -0
  307. package/dist/types/drizzle/bigint.d.ts.map +1 -0
  308. package/dist/types/drizzle/bytes.d.ts +31 -0
  309. package/dist/types/drizzle/bytes.d.ts.map +1 -0
  310. package/dist/types/drizzle/hex.d.ts +25 -0
  311. package/dist/types/drizzle/hex.d.ts.map +1 -0
  312. package/dist/types/drizzle/index.d.ts +6 -0
  313. package/dist/types/drizzle/index.d.ts.map +1 -0
  314. package/dist/types/drizzle/json.d.ts +51 -0
  315. package/dist/types/drizzle/json.d.ts.map +1 -0
  316. package/dist/types/drizzle/kit/index.d.ts +187 -0
  317. package/dist/types/drizzle/kit/index.d.ts.map +1 -0
  318. package/dist/types/drizzle/onchain.d.ts +298 -0
  319. package/dist/types/drizzle/onchain.d.ts.map +1 -0
  320. package/dist/types/drizzle/text.d.ts +29 -0
  321. package/dist/types/drizzle/text.d.ts.map +1 -0
  322. package/dist/types/graphql/graphiql.html.d.ts +2 -0
  323. package/dist/types/graphql/graphiql.html.d.ts.map +1 -0
  324. package/dist/types/graphql/index.d.ts +12 -0
  325. package/dist/types/graphql/index.d.ts.map +1 -0
  326. package/dist/types/graphql/json.d.ts +3 -0
  327. package/dist/types/graphql/json.d.ts.map +1 -0
  328. package/dist/types/graphql/middleware.d.ts +29 -0
  329. package/dist/types/graphql/middleware.d.ts.map +1 -0
  330. package/dist/types/index.d.ts +23 -0
  331. package/dist/types/index.d.ts.map +1 -0
  332. package/dist/types/indexing/addStackTrace.d.ts +3 -0
  333. package/dist/types/indexing/addStackTrace.d.ts.map +1 -0
  334. package/dist/types/indexing/client.d.ts +154 -0
  335. package/dist/types/indexing/client.d.ts.map +1 -0
  336. package/dist/types/indexing/index.d.ts +72 -0
  337. package/dist/types/indexing/index.d.ts.map +1 -0
  338. package/dist/types/indexing/profile.d.ts +16 -0
  339. package/dist/types/indexing/profile.d.ts.map +1 -0
  340. package/dist/types/indexing-store/cache.d.ts +115 -0
  341. package/dist/types/indexing-store/cache.d.ts.map +1 -0
  342. package/dist/types/indexing-store/index.d.ts +24 -0
  343. package/dist/types/indexing-store/index.d.ts.map +1 -0
  344. package/dist/types/indexing-store/profile.d.ts +7 -0
  345. package/dist/types/indexing-store/profile.d.ts.map +1 -0
  346. package/dist/types/indexing-store/utils.d.ts +19 -0
  347. package/dist/types/indexing-store/utils.d.ts.map +1 -0
  348. package/dist/types/internal/common.d.ts +15 -0
  349. package/dist/types/internal/common.d.ts.map +1 -0
  350. package/dist/types/internal/errors.d.ts +101 -0
  351. package/dist/types/internal/errors.d.ts.map +1 -0
  352. package/dist/types/internal/logger.d.ts +37 -0
  353. package/dist/types/internal/logger.d.ts.map +1 -0
  354. package/dist/types/internal/metrics.d.ts +120 -0
  355. package/dist/types/internal/metrics.d.ts.map +1 -0
  356. package/dist/types/internal/options.d.ts +62 -0
  357. package/dist/types/internal/options.d.ts.map +1 -0
  358. package/dist/types/internal/shutdown.d.ts +8 -0
  359. package/dist/types/internal/shutdown.d.ts.map +1 -0
  360. package/dist/types/internal/telemetry.d.ts +43 -0
  361. package/dist/types/internal/telemetry.d.ts.map +1 -0
  362. package/dist/types/internal/types.d.ts +435 -0
  363. package/dist/types/internal/types.d.ts.map +1 -0
  364. package/dist/types/rpc/actions.d.ts +360 -0
  365. package/dist/types/rpc/actions.d.ts.map +1 -0
  366. package/dist/types/rpc/http.d.ts +17 -0
  367. package/dist/types/rpc/http.d.ts.map +1 -0
  368. package/dist/types/rpc/index.d.ts +43 -0
  369. package/dist/types/rpc/index.d.ts.map +1 -0
  370. package/dist/types/runtime/events.d.ts +40 -0
  371. package/dist/types/runtime/events.d.ts.map +1 -0
  372. package/dist/types/runtime/filter.d.ts +87 -0
  373. package/dist/types/runtime/filter.d.ts.map +1 -0
  374. package/dist/types/runtime/fragments.d.ts +30 -0
  375. package/dist/types/runtime/fragments.d.ts.map +1 -0
  376. package/dist/types/runtime/historical.d.ts +123 -0
  377. package/dist/types/runtime/historical.d.ts.map +1 -0
  378. package/dist/types/runtime/index.d.ts +89 -0
  379. package/dist/types/runtime/index.d.ts.map +1 -0
  380. package/dist/types/runtime/isolated.d.ts +14 -0
  381. package/dist/types/runtime/isolated.d.ts.map +1 -0
  382. package/dist/types/runtime/multichain.d.ts +13 -0
  383. package/dist/types/runtime/multichain.d.ts.map +1 -0
  384. package/dist/types/runtime/omnichain.d.ts +23 -0
  385. package/dist/types/runtime/omnichain.d.ts.map +1 -0
  386. package/dist/types/runtime/realtime.d.ts +93 -0
  387. package/dist/types/runtime/realtime.d.ts.map +1 -0
  388. package/dist/types/server/error.d.ts +5 -0
  389. package/dist/types/server/error.d.ts.map +1 -0
  390. package/dist/types/server/index.d.ts +13 -0
  391. package/dist/types/server/index.d.ts.map +1 -0
  392. package/dist/types/sync-historical/index.d.ts +36 -0
  393. package/dist/types/sync-historical/index.d.ts.map +1 -0
  394. package/dist/types/sync-historical/portal-transform.d.ts +50 -0
  395. package/dist/types/sync-historical/portal-transform.d.ts.map +1 -0
  396. package/dist/types/sync-historical/portal.d.ts +31 -0
  397. package/dist/types/sync-historical/portal.d.ts.map +1 -0
  398. package/dist/types/sync-realtime/bloom.d.ts +18 -0
  399. package/dist/types/sync-realtime/bloom.d.ts.map +1 -0
  400. package/dist/types/sync-realtime/index.d.ts +47 -0
  401. package/dist/types/sync-realtime/index.d.ts.map +1 -0
  402. package/dist/types/sync-store/encode.d.ts +25 -0
  403. package/dist/types/sync-store/encode.d.ts.map +1 -0
  404. package/dist/types/sync-store/index.d.ts +135 -0
  405. package/dist/types/sync-store/index.d.ts.map +1 -0
  406. package/dist/types/sync-store/migrations.d.ts +8 -0
  407. package/dist/types/sync-store/migrations.d.ts.map +1 -0
  408. package/dist/types/sync-store/schema.d.ts +1828 -0
  409. package/dist/types/sync-store/schema.d.ts.map +1 -0
  410. package/dist/types/types/db.d.ts +213 -0
  411. package/dist/types/types/db.d.ts.map +1 -0
  412. package/dist/types/types/eth.d.ts +196 -0
  413. package/dist/types/types/eth.d.ts.map +1 -0
  414. package/dist/types/types/utils.d.ts +38 -0
  415. package/dist/types/types/utils.d.ts.map +1 -0
  416. package/dist/types/types/virtual.d.ts +99 -0
  417. package/dist/types/types/virtual.d.ts.map +1 -0
  418. package/dist/types/ui/app.d.ts +22 -0
  419. package/dist/types/ui/app.d.ts.map +1 -0
  420. package/dist/types/ui/index.d.ts +5 -0
  421. package/dist/types/ui/index.d.ts.map +1 -0
  422. package/dist/types/ui/patch.d.ts +7 -0
  423. package/dist/types/ui/patch.d.ts.map +1 -0
  424. package/dist/types/utils/abi.d.ts +23 -0
  425. package/dist/types/utils/abi.d.ts.map +1 -0
  426. package/dist/types/utils/bigint.d.ts +15 -0
  427. package/dist/types/utils/bigint.d.ts.map +1 -0
  428. package/dist/types/utils/chains.d.ts +42 -0
  429. package/dist/types/utils/chains.d.ts.map +1 -0
  430. package/dist/types/utils/checkpoint.d.ts +52 -0
  431. package/dist/types/utils/checkpoint.d.ts.map +1 -0
  432. package/dist/types/utils/chunk.d.ts +2 -0
  433. package/dist/types/utils/chunk.d.ts.map +1 -0
  434. package/dist/types/utils/copy.d.ts +16 -0
  435. package/dist/types/utils/copy.d.ts.map +1 -0
  436. package/dist/types/utils/date.d.ts +7 -0
  437. package/dist/types/utils/date.d.ts.map +1 -0
  438. package/dist/types/utils/debug.d.ts +105 -0
  439. package/dist/types/utils/debug.d.ts.map +1 -0
  440. package/dist/types/utils/decodeAbiParameters.d.ts +28 -0
  441. package/dist/types/utils/decodeAbiParameters.d.ts.map +1 -0
  442. package/dist/types/utils/decodeEventLog.d.ts +12 -0
  443. package/dist/types/utils/decodeEventLog.d.ts.map +1 -0
  444. package/dist/types/utils/dedupe.d.ts +20 -0
  445. package/dist/types/utils/dedupe.d.ts.map +1 -0
  446. package/dist/types/utils/duplicates.d.ts +7 -0
  447. package/dist/types/utils/duplicates.d.ts.map +1 -0
  448. package/dist/types/utils/estimate.d.ts +11 -0
  449. package/dist/types/utils/estimate.d.ts.map +1 -0
  450. package/dist/types/utils/finality.d.ts +12 -0
  451. package/dist/types/utils/finality.d.ts.map +1 -0
  452. package/dist/types/utils/format.d.ts +3 -0
  453. package/dist/types/utils/format.d.ts.map +1 -0
  454. package/dist/types/utils/generators.d.ts +42 -0
  455. package/dist/types/utils/generators.d.ts.map +1 -0
  456. package/dist/types/utils/hash.d.ts +11 -0
  457. package/dist/types/utils/hash.d.ts.map +1 -0
  458. package/dist/types/utils/interval.d.ts +53 -0
  459. package/dist/types/utils/interval.d.ts.map +1 -0
  460. package/dist/types/utils/lowercase.d.ts +5 -0
  461. package/dist/types/utils/lowercase.d.ts.map +1 -0
  462. package/dist/types/utils/mutex.d.ts +5 -0
  463. package/dist/types/utils/mutex.d.ts.map +1 -0
  464. package/dist/types/utils/never.d.ts +2 -0
  465. package/dist/types/utils/never.d.ts.map +1 -0
  466. package/dist/types/utils/offset.d.ts +8 -0
  467. package/dist/types/utils/offset.d.ts.map +1 -0
  468. package/dist/types/utils/order.d.ts +2 -0
  469. package/dist/types/utils/order.d.ts.map +1 -0
  470. package/dist/types/utils/partition.d.ts +22 -0
  471. package/dist/types/utils/partition.d.ts.map +1 -0
  472. package/dist/types/utils/pg.d.ts +8 -0
  473. package/dist/types/utils/pg.d.ts.map +1 -0
  474. package/dist/types/utils/pglite.d.ts +25 -0
  475. package/dist/types/utils/pglite.d.ts.map +1 -0
  476. package/dist/types/utils/port.d.ts +5 -0
  477. package/dist/types/utils/port.d.ts.map +1 -0
  478. package/dist/types/utils/print.d.ts +4 -0
  479. package/dist/types/utils/print.d.ts.map +1 -0
  480. package/dist/types/utils/promiseAllSettledWithThrow.d.ts +8 -0
  481. package/dist/types/utils/promiseAllSettledWithThrow.d.ts.map +1 -0
  482. package/dist/types/utils/promiseWithResolvers.d.ts +10 -0
  483. package/dist/types/utils/promiseWithResolvers.d.ts.map +1 -0
  484. package/dist/types/utils/queue.d.ts +33 -0
  485. package/dist/types/utils/queue.d.ts.map +1 -0
  486. package/dist/types/utils/range.d.ts +8 -0
  487. package/dist/types/utils/range.d.ts.map +1 -0
  488. package/dist/types/utils/result.d.ts +17 -0
  489. package/dist/types/utils/result.d.ts.map +1 -0
  490. package/dist/types/utils/sql-parse.d.ts +21 -0
  491. package/dist/types/utils/sql-parse.d.ts.map +1 -0
  492. package/dist/types/utils/timer.d.ts +6 -0
  493. package/dist/types/utils/timer.d.ts.map +1 -0
  494. package/dist/types/utils/truncate.d.ts +9 -0
  495. package/dist/types/utils/truncate.d.ts.map +1 -0
  496. package/dist/types/utils/wait.d.ts +6 -0
  497. package/dist/types/utils/wait.d.ts.map +1 -0
  498. package/dist/types/utils/zipper.d.ts +36 -0
  499. package/dist/types/utils/zipper.d.ts.map +1 -0
  500. package/package.json +117 -0
  501. package/src/bin/commands/codegen.ts +56 -0
  502. package/src/bin/commands/createViews.ts +318 -0
  503. package/src/bin/commands/dev.ts +490 -0
  504. package/src/bin/commands/list.ts +208 -0
  505. package/src/bin/commands/prune.ts +322 -0
  506. package/src/bin/commands/serve.ts +236 -0
  507. package/src/bin/commands/start.ts +319 -0
  508. package/src/bin/isolatedController.ts +300 -0
  509. package/src/bin/isolatedWorker.ts +192 -0
  510. package/src/bin/ponder.ts +208 -0
  511. package/src/bin/utils/codegen.ts +32 -0
  512. package/src/bin/utils/exit.ts +112 -0
  513. package/src/build/config.ts +1142 -0
  514. package/src/build/factory.ts +125 -0
  515. package/src/build/index.ts +790 -0
  516. package/src/build/plugin.ts +58 -0
  517. package/src/build/pre.ts +114 -0
  518. package/src/build/schema.ts +358 -0
  519. package/src/build/stacktrace.ts +137 -0
  520. package/src/client/index.ts +551 -0
  521. package/src/config/address.ts +46 -0
  522. package/src/config/eventFilter.ts +33 -0
  523. package/src/config/index.ts +246 -0
  524. package/src/config/utilityTypes.ts +152 -0
  525. package/src/database/actions.ts +873 -0
  526. package/src/database/index.ts +1029 -0
  527. package/src/database/queryBuilder.ts +537 -0
  528. package/src/drizzle/bigint.ts +57 -0
  529. package/src/drizzle/bytes.ts +68 -0
  530. package/src/drizzle/hex.ts +58 -0
  531. package/src/drizzle/index.ts +40 -0
  532. package/src/drizzle/json.ts +159 -0
  533. package/src/drizzle/kit/index.ts +1348 -0
  534. package/src/drizzle/onchain.ts +476 -0
  535. package/src/drizzle/text.ts +77 -0
  536. package/src/graphql/graphiql.html.ts +59 -0
  537. package/src/graphql/index.ts +1390 -0
  538. package/src/graphql/json.ts +62 -0
  539. package/src/graphql/middleware.ts +115 -0
  540. package/src/index.ts +139 -0
  541. package/src/indexing/addStackTrace.ts +69 -0
  542. package/src/indexing/client.ts +1184 -0
  543. package/src/indexing/index.ts +976 -0
  544. package/src/indexing/profile.ts +771 -0
  545. package/src/indexing-store/cache.ts +1057 -0
  546. package/src/indexing-store/index.ts +630 -0
  547. package/src/indexing-store/profile.ts +557 -0
  548. package/src/indexing-store/utils.ts +164 -0
  549. package/src/internal/common.ts +15 -0
  550. package/src/internal/errors.ts +228 -0
  551. package/src/internal/logger.ts +252 -0
  552. package/src/internal/metrics.ts +1030 -0
  553. package/src/internal/options.ts +130 -0
  554. package/src/internal/shutdown.ts +32 -0
  555. package/src/internal/telemetry.ts +303 -0
  556. package/src/internal/types.ts +598 -0
  557. package/src/rpc/actions.ts +1343 -0
  558. package/src/rpc/http.ts +164 -0
  559. package/src/rpc/index.ts +959 -0
  560. package/src/runtime/events.ts +875 -0
  561. package/src/runtime/filter.ts +664 -0
  562. package/src/runtime/fragments.ts +674 -0
  563. package/src/runtime/historical.ts +1512 -0
  564. package/src/runtime/index.ts +569 -0
  565. package/src/runtime/isolated.ts +778 -0
  566. package/src/runtime/multichain.ts +860 -0
  567. package/src/runtime/omnichain.ts +920 -0
  568. package/src/runtime/realtime.ts +1166 -0
  569. package/src/server/error.ts +68 -0
  570. package/src/server/index.ts +173 -0
  571. package/src/sync-historical/index.ts +1072 -0
  572. package/src/sync-historical/portal-transform.ts +114 -0
  573. package/src/sync-historical/portal.ts +539 -0
  574. package/src/sync-realtime/bloom.ts +102 -0
  575. package/src/sync-realtime/index.ts +1298 -0
  576. package/src/sync-store/encode.ts +153 -0
  577. package/src/sync-store/index.ts +1633 -0
  578. package/src/sync-store/migrations.ts +1801 -0
  579. package/src/sync-store/schema.ts +248 -0
  580. package/src/types/db.ts +292 -0
  581. package/src/types/eth.ts +216 -0
  582. package/src/types/utils.ts +47 -0
  583. package/src/types/virtual.ts +244 -0
  584. package/src/types.d.ts +38 -0
  585. package/src/ui/app.ts +207 -0
  586. package/src/ui/index.ts +37 -0
  587. package/src/ui/patch.ts +204 -0
  588. package/src/utils/abi.ts +103 -0
  589. package/src/utils/bigint.ts +41 -0
  590. package/src/utils/chains.ts +22 -0
  591. package/src/utils/checkpoint.ts +203 -0
  592. package/src/utils/chunk.ts +7 -0
  593. package/src/utils/copy.ts +151 -0
  594. package/src/utils/date.ts +26 -0
  595. package/src/utils/debug.ts +110 -0
  596. package/src/utils/decodeAbiParameters.ts +428 -0
  597. package/src/utils/decodeEventLog.ts +100 -0
  598. package/src/utils/dedupe.ts +32 -0
  599. package/src/utils/duplicates.ts +19 -0
  600. package/src/utils/estimate.ts +27 -0
  601. package/src/utils/finality.ts +40 -0
  602. package/src/utils/format.ts +22 -0
  603. package/src/utils/generators.ts +157 -0
  604. package/src/utils/hash.ts +22 -0
  605. package/src/utils/interval.ts +212 -0
  606. package/src/utils/lowercase.ts +6 -0
  607. package/src/utils/mutex.ts +33 -0
  608. package/src/utils/never.ts +3 -0
  609. package/src/utils/offset.ts +133 -0
  610. package/src/utils/order.ts +16 -0
  611. package/src/utils/partition.ts +53 -0
  612. package/src/utils/pg.ts +230 -0
  613. package/src/utils/pglite.ts +97 -0
  614. package/src/utils/port.ts +34 -0
  615. package/src/utils/print.ts +34 -0
  616. package/src/utils/promiseAllSettledWithThrow.ts +27 -0
  617. package/src/utils/promiseWithResolvers.ts +20 -0
  618. package/src/utils/queue.ts +258 -0
  619. package/src/utils/range.ts +8 -0
  620. package/src/utils/result.ts +26 -0
  621. package/src/utils/sql-parse.ts +1477 -0
  622. package/src/utils/timer.ts +8 -0
  623. package/src/utils/truncate.ts +15 -0
  624. package/src/utils/wait.ts +8 -0
  625. package/src/utils/zipper.ts +80 -0
  626. package/tsconfig.json +12 -0
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Pure transforms: SQD Portal NDJSON → Ponder Sync* / geth callTracer shapes.
3
+ *
4
+ * Portal uses a SPLIT encoding (verified live, block 21,000,000): small bounded
5
+ * values are DECIMAL numbers (status, type, transactionIndex, nonce, chainId,
6
+ * yParity, traceAddress[], subtraces, block number/timestamp) while 256-bit /
7
+ * byte quantities are HEX strings (gas*, value, effectiveGasPrice, v/r/s, all
8
+ * addresses/hashes, and ALL trace action/result quantities). So e.g. `status` is
9
+ * `1` not `"0x1"`, but `gasUsed` is `"0x317fa"`. These functions normalise to
10
+ * viem RPC conventions. Kept pure + exported so they're unit-testable against
11
+ * real captured fixtures.
12
+ */
13
+ import type {
14
+ SyncBlockHeader,
15
+ SyncLog,
16
+ SyncTransaction,
17
+ SyncTransactionReceipt,
18
+ } from "@/internal/types.js";
19
+ import { type Address, type Hex, numberToHex, toHex } from "viem";
20
+
21
+ export type RawHeader = Record<string, any> & { number: number };
22
+
23
+ /** Traces are ~100x denser than logs; buffering a wide chunk's worth over a busy
24
+ * contract OOMs. When a chain has trace sources, cap the chunk to a trace-safe width
25
+ * (PORTAL_TRACE_CHUNK_BLOCKS, default 25k). Pure so it's unit-testable. */
26
+ export const traceSafeChunkBlocks = (base: number, needTraces: boolean, cap = Number(process.env.PORTAL_TRACE_CHUNK_BLOCKS ?? 25_000)): number =>
27
+ needTraces && base > cap ? cap : base;
28
+
29
+ /** An interval reaching past Portal's finalized head must fall back to RPC for the gap.
30
+ * (undefined head = not yet known → treat as no gap.) Pure so it's unit-testable. */
31
+ export const isFinalityGap = (intervalEnd: number, portalHead: number | undefined): boolean =>
32
+ portalHead !== undefined && intervalEnd > portalHead;
33
+
34
+ /** number|decimal-string|hex → 0x-hex; passes existing hex through. */
35
+ export const hx = (v: unknown): Hex => {
36
+ if (typeof v === "string") return (v.startsWith("0x") ? v : toHex(BigInt(v))) as Hex;
37
+ if (typeof v === "number" || typeof v === "bigint") return numberToHex(v);
38
+ return "0x0";
39
+ };
40
+ export const opt = (v: unknown): Hex | undefined => (v === undefined || v === null ? undefined : hx(v));
41
+
42
+ export const toSyncLog = (l: any, h: RawHeader): SyncLog => ({
43
+ address: (l.address as string).toLowerCase(), topics: l.topics ?? [], data: l.data ?? "0x",
44
+ blockNumber: hx(h.number), blockHash: h.hash, transactionHash: l.transactionHash,
45
+ transactionIndex: hx(l.transactionIndex), logIndex: hx(l.logIndex), removed: false,
46
+ }) as unknown as SyncLog;
47
+
48
+ export const toSyncBlockHeader = (h: RawHeader): SyncBlockHeader => ({
49
+ number: hx(h.number), hash: h.hash, parentHash: h.parentHash, timestamp: hx(h.timestamp),
50
+ logsBloom: h.logsBloom, miner: h.miner, gasUsed: opt(h.gasUsed), gasLimit: opt(h.gasLimit),
51
+ baseFeePerGas: opt(h.baseFeePerGas), nonce: h.nonce, mixHash: h.mixHash, stateRoot: h.stateRoot,
52
+ receiptsRoot: h.receiptsRoot, transactionsRoot: h.transactionsRoot, sha3Uncles: h.sha3Uncles,
53
+ size: opt(h.size), difficulty: opt(h.difficulty), totalDifficulty: opt(h.totalDifficulty),
54
+ extraData: h.extraData, transactions: undefined,
55
+ }) as unknown as SyncBlockHeader;
56
+
57
+ export const toSyncTransaction = (tx: any, h: RawHeader): SyncTransaction => ({
58
+ blockHash: h.hash, blockNumber: hx(h.number),
59
+ from: (tx.from as string)?.toLowerCase(), to: tx.to ? (tx.to as string).toLowerCase() : null,
60
+ gas: hx(tx.gas), hash: tx.hash, input: tx.input ?? "0x", nonce: hx(tx.nonce ?? 0),
61
+ transactionIndex: hx(tx.transactionIndex), value: hx(tx.value ?? 0), type: hx(tx.type ?? 0),
62
+ gasPrice: opt(tx.gasPrice), maxFeePerGas: opt(tx.maxFeePerGas), maxPriorityFeePerGas: opt(tx.maxPriorityFeePerGas),
63
+ v: opt(tx.v), r: tx.r, s: tx.s, yParity: tx.yParity !== undefined && tx.yParity !== null ? hx(tx.yParity) : undefined, accessList: tx.accessList,
64
+ }) as unknown as SyncTransaction;
65
+
66
+ /** receipt fields ride on Portal's transaction object; status/type are DECIMAL → hex. */
67
+ export const toSyncReceipt = (tx: any, h: RawHeader): SyncTransactionReceipt => ({
68
+ blockNumber: hx(h.number), blockHash: h.hash, transactionIndex: hx(tx.transactionIndex), transactionHash: tx.hash,
69
+ from: (tx.from as string)?.toLowerCase(), to: tx.to ? (tx.to as string).toLowerCase() : null,
70
+ contractAddress: tx.contractAddress ? (tx.contractAddress as string).toLowerCase() : null,
71
+ logsBloom: tx.logsBloom, gasUsed: hx(tx.gasUsed), cumulativeGasUsed: hx(tx.cumulativeGasUsed),
72
+ effectiveGasPrice: hx(tx.effectiveGasPrice),
73
+ status: tx.status === 1 || tx.status === "0x1" || tx.status === true ? "0x1" : "0x0",
74
+ type: hx(tx.type ?? 0),
75
+ }) as unknown as SyncTransactionReceipt;
76
+
77
+ export const CALL_TYPE: Record<string, string> = { call: "CALL", delegatecall: "DELEGATECALL", staticcall: "STATICCALL", callcode: "CALLCODE" };
78
+
79
+ /** lexicographic, parent-before-child: [] < [0] < [0,0] < [1] — i.e. DFS pre-order. */
80
+ export const cmpTraceAddr = (a: number[], b: number[]): number => {
81
+ const n = Math.min(a.length, b.length);
82
+ for (let i = 0; i < n; i++) if (a[i] !== b[i]) return a[i]! - b[i]!;
83
+ return a.length - b.length;
84
+ };
85
+
86
+ /** Portal Parity trace → geth callTracer CallFrame (+ index/subcalls). Reads nested
87
+ * action/result; callType lives at action.callType. CREATE2 is indistinguishable. */
88
+ export const parityToCallFrame = (t: any, index: number): any | undefined => {
89
+ const a = t.action ?? {}, r = t.result ?? {};
90
+ let type: string, from: any, to: any, value: any, gas: any, gasUsed: any, input: any, output: any;
91
+ if (t.type === "call") {
92
+ type = CALL_TYPE[(a.callType ?? a.type ?? t.callCallType ?? t.callType) as string] ?? "CALL";
93
+ from = a.from ?? t.callFrom; to = a.to ?? t.callTo; value = a.value ?? t.callValue;
94
+ gas = a.gas ?? t.callGas; gasUsed = r.gasUsed ?? t.callResultGasUsed; input = a.input ?? t.callInput ?? "0x"; output = r.output ?? t.callResultOutput;
95
+ } else if (t.type === "create") {
96
+ type = "CREATE"; from = a.from ?? t.createFrom; to = r.address ?? t.createResultAddress; value = a.value ?? t.createValue;
97
+ gas = a.gas ?? t.createGas; gasUsed = r.gasUsed ?? t.createResultGasUsed; input = a.init ?? t.createInit ?? "0x"; output = r.code ?? t.createResultCode;
98
+ } else if (t.type === "suicide") {
99
+ type = "SELFDESTRUCT"; from = a.address ?? t.suicideAddress; to = a.refundAddress ?? t.suicideRefundAddress; value = a.balance ?? t.suicideBalance; gas = 0; gasUsed = 0; input = "0x";
100
+ } else return undefined; // "reward" has no callTracer equivalent
101
+ return {
102
+ type, from: (from as string)?.toLowerCase() as Address, to: to ? (to as string).toLowerCase() as Address : undefined,
103
+ value: opt(value), gas: hx(gas ?? 0), gasUsed: hx(gasUsed ?? 0), input: input ?? "0x", output: output ?? undefined,
104
+ error: t.error ?? undefined, revertReason: t.revertReason ?? undefined, index, subcalls: t.subtraces ?? 0,
105
+ };
106
+ };
107
+
108
+ /** State diffs — Portal has them; Ponder has no state-diff source today, so this is
109
+ * engine-level only (available via the standalone client, not wired into HistoricalSync). */
110
+ export type StateDiff = { transactionIndex: number; address: Address; key: string; kind: "=" | "+" | "*" | "-"; prev: Hex | null; next: Hex | null };
111
+ export const toStateDiff = (d: any): StateDiff => ({
112
+ transactionIndex: d.transactionIndex, address: (d.address as string)?.toLowerCase() as Address,
113
+ key: d.key, kind: d.kind, prev: d.prev ?? null, next: d.next ?? null,
114
+ });
@@ -0,0 +1,539 @@
1
+ import { writeFileSync } from "node:fs";
2
+ import type { Common } from "@/internal/common.js";
3
+ import type {
4
+ Chain,
5
+ FactoryId,
6
+ Filter,
7
+ LogFilter,
8
+ SyncBlock,
9
+ SyncBlockHeader,
10
+ SyncLog,
11
+ SyncTrace,
12
+ SyncTransaction,
13
+ SyncTransactionReceipt,
14
+ } from "@/internal/types.js";
15
+ import {
16
+ getChildAddress,
17
+ isAddressFactory,
18
+ isAddressMatched,
19
+ isBlockFilterMatched,
20
+ isLogFactoryMatched,
21
+ isTraceFilterMatched,
22
+ isTransactionFilterMatched,
23
+ isTransferFilterMatched,
24
+ } from "@/runtime/filter.js";
25
+ import type { Rpc } from "@/rpc/index.js";
26
+ import type { Interval } from "@/utils/interval.js";
27
+ import { type Address, type Hex } from "viem";
28
+ import { type HistoricalSync, createHistoricalSync } from "./index.js";
29
+ import { type RawHeader, hx, isFinalityGap, toSyncLog, toSyncBlockHeader, toSyncTransaction, toSyncReceipt, parityToCallFrame, cmpTraceAddr, traceSafeChunkBlocks } from "./portal-transform.js";
30
+
31
+ /**
32
+ * Portal-backed historical sync with a PARALLEL read-ahead chunk buffer.
33
+ *
34
+ * Ponder feeds small intervals; Portal is latency-bound per request but has huge
35
+ * parallel bandwidth. So we fetch large aligned CHUNKS and serve every interval
36
+ * from cache — and we fetch chunks IN PARALLEL (read-ahead depth N) so the
37
+ * Portal's per-request latency overlaps instead of serializing.
38
+ *
39
+ * Correctness for factory sources: the discovery timeline is decoupled from the
40
+ * data timeline. Each chunk's children are discovered independently (clamped to
41
+ * the factory's real start block), and a data chunk only fetches once discovery
42
+ * is complete THROUGH its own block range — so no child event is missed even
43
+ * though data chunks are fetched out of order.
44
+ *
45
+ * Tunables: PORTAL_CHUNK_BLOCKS (default 500k), PORTAL_READAHEAD (default 6).
46
+ * Selected at runtime/historical.ts when `chain.portal` is set; realtime → rpc.
47
+ */
48
+
49
+ type CreateHistoricalSyncParameters = {
50
+ common: Common;
51
+ chain: Chain;
52
+ rpc: Rpc;
53
+ childAddresses: Map<FactoryId, Map<Address, number>>;
54
+ };
55
+
56
+ type PortalLogRequest = { address?: string[]; topic0?: string[]; topic1?: string[]; topic2?: string[]; topic3?: string[]; transaction?: boolean };
57
+ type ChunkData = {
58
+ headers: Map<number, RawHeader>;
59
+ logs: Map<number, any[]>;
60
+ txs: Map<number, any[]>;
61
+ // for trace/transfer sources: full block + all its traces + its txs, by block number
62
+ traceBlocks: Map<number, { header: RawHeader; traces: any[]; txs: any[] }>;
63
+ // for block-interval sources: headers of blocks matching a BlockFilter (interval/offset)
64
+ blockHeaders: Map<number, RawHeader>;
65
+ // for account transaction sources: blocks + their from/to-matched txs, by block number
66
+ txBlocks: Map<number, { header: RawHeader; txs: any[] }>;
67
+ };
68
+
69
+ const PORTAL_MAX_ADDRESSES = 1000;
70
+ const CHUNK_BLOCKS = Number(process.env.PORTAL_CHUNK_BLOCKS ?? 500_000);
71
+ const READAHEAD = Number(process.env.PORTAL_READAHEAD ?? 6);
72
+ const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));
73
+
74
+ const asArr = (t: Hex | readonly Hex[] | null | undefined): string[] | undefined => {
75
+ if (t === null || t === undefined) return undefined;
76
+ return (Array.isArray(t) ? t : [t]).map((x) => (x as string).toLowerCase());
77
+ };
78
+
79
+ export const createPortalHistoricalSync = (
80
+ args: CreateHistoricalSyncParameters,
81
+ ): HistoricalSync => {
82
+ const portalUrl = args.chain.portal!.replace(/\/$/, "");
83
+ const log = args.common.logger;
84
+ const baseHeaders: Record<string, string> = { "content-type": "application/json", "accept-encoding": "gzip" };
85
+ if (process.env.PORTAL_API_KEY) baseHeaders["x-api-key"] = process.env.PORTAL_API_KEY;
86
+
87
+ const stats = { dataChunks: 0, discChunks: 0, http: 0, logs: 0, errors: 0, retries: 0, bytes: 0, cacheHits: 0, inflight: 0, maxInflight: 0, blocks: 0, txs: 0, receipts: 0, traces: 0, rpcFallback: 0 };
88
+ const dataCache = new Map<number, Promise<ChunkData>>(); // keyed by chunk index
89
+ const discCache = new Map<number, Promise<void>>(); // keyed by chunk index
90
+ const stash = new Map<string, { blocks: SyncBlockHeader[]; txs: SyncTransaction[]; receipts: SyncTransactionReceipt[]; traces: { trace: SyncTrace; block: SyncBlock; transaction: SyncTransaction }[]; closest: SyncBlock | undefined }>();
91
+ const ikey = (i: Interval) => `${i[0]}-${i[1]}`;
92
+ let chunkBlocks = CHUNK_BLOCKS;
93
+ let chunkSizeP: Promise<void> | undefined;
94
+ const idxOf = (n: number) => Math.floor(n / chunkBlocks);
95
+ let discStartIdx: number | undefined; // factory deploy chunk — discovery floor (fixes from-0 scan)
96
+
97
+ // finality-gap fallback: Portal serves only finalized data, and its finalized head can
98
+ // (rarely) lag Ponder's target. Any interval reaching past Portal's head is delegated
99
+ // whole to the stock RPC historical sync. PORTAL_FINALIZED_HEAD overrides for tests/ops.
100
+ let portalHead: number | undefined = process.env.PORTAL_FINALIZED_HEAD ? Number(process.env.PORTAL_FINALIZED_HEAD) : undefined;
101
+ let rpcFallbackInstance: HistoricalSync | undefined;
102
+ const rpcFallback = (): HistoricalSync => (rpcFallbackInstance ??= createHistoricalSync(args));
103
+ const delegated = new Set<string>(); // interval keys routed to RPC
104
+ const refreshPortalHead = async (): Promise<number> => {
105
+ if (process.env.PORTAL_FINALIZED_HEAD) return (portalHead = Number(process.env.PORTAL_FINALIZED_HEAD));
106
+ try { const h = await fetch(`${portalUrl}/finalized-head`, { headers: baseHeaders }).then((r) => r.json()); if (typeof h?.number === "number") portalHead = h.number; } catch { /* keep prior */ }
107
+ return portalHead ?? Number.POSITIVE_INFINITY;
108
+ };
109
+ // instrumentation: per-chain backfill metrics → PORTAL_METRICS_FILE.<chainId> (for the bench harness)
110
+ const METRICS_FILE = process.env.PORTAL_METRICS_FILE;
111
+ let startTime = 0;
112
+ const writeMetrics = () => {
113
+ if (!METRICS_FILE) return;
114
+ try {
115
+ writeFileSync(`${METRICS_FILE}.${args.chain.id}`, JSON.stringify({
116
+ chain: args.chain.name, chainId: args.chain.id, wallMs: startTime ? Date.now() - startTime : 0,
117
+ chunkBlocks, portalFinalizedHead: portalHead ?? null,
118
+ fetch: { dataChunks: stats.dataChunks, discChunks: stats.discChunks, http: stats.http, bytes: stats.bytes, errors: stats.errors, retries: stats.retries, cacheHits: stats.cacheHits, maxInflight: stats.maxInflight },
119
+ inserted: { logs: stats.logs, blocks: stats.blocks, txs: stats.txs, receipts: stats.receipts, traces: stats.traces },
120
+ rpcFallbackIntervals: stats.rpcFallback,
121
+ }));
122
+ } catch { /* best-effort */ }
123
+ };
124
+
125
+ // Scale chunk size by the chain's block density. High-block-rate chains (Arbitrum
126
+ // ~478M blocks ≈ 19× Ethereum) otherwise need 19× more 500k-block chunks = 19× more
127
+ // latency-bound round-trips. CU is charged per Portal data-chunk (data-density based),
128
+ // so larger BLOCK-chunks don't cost more CU — they just cut round-trips. PORTAL_CHUNK_FIXED=1 disables.
129
+ const ensureChunkSize = (): Promise<void> =>
130
+ (chunkSizeP ??= (async () => {
131
+ if (process.env.PORTAL_CHUNK_FIXED) return;
132
+ try {
133
+ const h = await fetch(`${portalUrl}/finalized-head`, { headers: baseHeaders }).then((r) => r.json());
134
+ const density = Math.max(1, Math.round((h.number as number) / 25_000_000));
135
+ chunkBlocks = Math.min(CHUNK_BLOCKS * density, 25_000_000);
136
+ log.debug({ service: "portal", msg: `Portal ${args.chain.name}: head=${h.number} → chunkBlocks=${chunkBlocks} (${density}× density)` });
137
+ } catch { /* keep default */ }
138
+ })());
139
+
140
+ // transient = retry: HTTP 503/529/429 AND network/socket errors (parallel load
141
+ // makes "other side closed" / ECONNRESET / fetch failed routine).
142
+ const isNetworkError = (err: any): boolean => {
143
+ const m = `${err?.message ?? ""} ${err?.cause?.message ?? ""} ${err?.cause?.code ?? ""}`.toLowerCase();
144
+ return /socket|closed|econnreset|fetch failed|terminated|timeout|network|epipe|und_err/.test(m) || err?.name === "AbortError";
145
+ };
146
+
147
+ // one POST+drain; returns blocks or "done" (204); throws (with .retryAfterMs on 503-class).
148
+ async function fetchBatch(body: string, cursor: number): Promise<{ blocks: { header: RawHeader; logs?: any[]; transactions?: any[]; traces?: any[] }[]; last: number } | "done"> {
149
+ stats.inflight++; stats.maxInflight = Math.max(stats.maxInflight, stats.inflight);
150
+ try {
151
+ const res = await fetch(`${portalUrl}/finalized-stream`, { method: "POST", headers: baseHeaders, body });
152
+ stats.http++;
153
+ if (res.status === 204) return "done";
154
+ if (res.status === 503 || res.status === 529 || res.status === 429) {
155
+ await res.body?.cancel().catch(() => {});
156
+ const ra = Number(res.headers.get("retry-after"));
157
+ const e: any = new Error(`Portal ${res.status}`); e.retryAfterMs = Number.isFinite(ra) ? ra * 1000 : undefined;
158
+ throw e;
159
+ }
160
+ if (!res.ok) throw new Error(`Portal ${res.status} @ ${cursor}: ${(await res.text()).slice(0, 200)}`);
161
+ const reader = res.body!.getReader();
162
+ const dec = new TextDecoder();
163
+ let buf = "", last = cursor;
164
+ const blocks: { header: RawHeader; logs?: any[]; transactions?: any[]; traces?: any[] }[] = [];
165
+ const onLine = (line: string) => { if (!line) return; const b = JSON.parse(line); blocks.push(b); if (b.header?.number > last) last = b.header.number; };
166
+ for (;;) { const { done, value } = await reader.read(); if (done) break; stats.bytes += value.byteLength; buf += dec.decode(value, { stream: true }); let nl: number; while ((nl = buf.indexOf("\n")) >= 0) { onLine(buf.slice(0, nl)); buf = buf.slice(nl + 1); } }
167
+ buf += dec.decode(); if (buf) onLine(buf);
168
+ return { blocks, last };
169
+ } finally { stats.inflight--; }
170
+ }
171
+
172
+ async function* stream(query: object, from: number, to: number) {
173
+ let cursor = from;
174
+ while (cursor <= to) {
175
+ const body = JSON.stringify({ ...query, fromBlock: cursor, toBlock: to });
176
+ let attempt = 0;
177
+ let batch: Awaited<ReturnType<typeof fetchBatch>> | undefined;
178
+ while (batch === undefined) {
179
+ try { batch = await fetchBatch(body, cursor); }
180
+ catch (err: any) {
181
+ const retryable = err?.retryAfterMs !== undefined || isNetworkError(err);
182
+ if (!retryable || attempt++ >= 10) throw err;
183
+ stats.errors++; stats.retries++;
184
+ await sleep(err?.retryAfterMs !== undefined ? Math.min(err.retryAfterMs, 30_000) : Math.min(500 * 2 ** attempt, 30_000));
185
+ }
186
+ }
187
+ if (batch === "done") return;
188
+ yield batch.blocks;
189
+ if (batch.last < cursor) throw new Error(`Portal no progress @ ${cursor}`);
190
+ cursor = batch.last + 1;
191
+ }
192
+ }
193
+
194
+ function logRequestsFor(filter: LogFilter): PortalLogRequest[] {
195
+ const base: PortalLogRequest = {};
196
+ if (filter.topic0) base.topic0 = asArr(filter.topic0);
197
+ if (filter.topic1) base.topic1 = asArr(filter.topic1 as any);
198
+ if (filter.topic2) base.topic2 = asArr(filter.topic2 as any);
199
+ if (filter.topic3) base.topic3 = asArr(filter.topic3 as any);
200
+ let addresses: Address[] | undefined;
201
+ if (isAddressFactory(filter.address)) {
202
+ addresses = Array.from(args.childAddresses.get(filter.address.id)?.keys() ?? []);
203
+ if (addresses.length === 0) return [];
204
+ } else if (filter.address === undefined) return [base];
205
+ else addresses = (Array.isArray(filter.address) ? filter.address : [filter.address]).map((a) => a.toLowerCase() as Address);
206
+ const out: PortalLogRequest[] = [];
207
+ for (let i = 0; i < addresses.length; i += PORTAL_MAX_ADDRESSES) out.push({ ...base, address: addresses.slice(i, i + PORTAL_MAX_ADDRESSES) });
208
+ return out;
209
+ }
210
+
211
+ // FILTER/PROJECTION STRATEGY (max Portal leverage): every row filter is pushed to
212
+ // Portal's native server-side filters — logs by address+topics (logRequestsFor),
213
+ // traces by callTo/callFrom/callSighash (tracePortalRequests), account txs by from/to
214
+ // (txPortalRequests). Field projection below requests exactly the columns the sync
215
+ // store persists and no more. The only client-side row filter is block-interval
216
+ // (Portal has no modulo filter), and receipt fields are added only on demand.
217
+ const REQUIRED_BLOCK_FIELDS = ["number", "hash", "parentHash", "timestamp", "logsBloom", "miner", "gasUsed", "gasLimit", "stateRoot", "receiptsRoot", "transactionsRoot", "size", "difficulty", "extraData"];
218
+ const NULLABLE_BLOCK_FIELDS = ["baseFeePerGas", "nonce", "mixHash", "sha3Uncles", "totalDifficulty"];
219
+ const LOG_FIELDS = { address: true, topics: true, data: true, transactionHash: true, transactionIndex: true, logIndex: true };
220
+ // Ponder's event profiler probes event.transaction.hash, so we pull each matched
221
+ // log's parent transaction (Portal `transaction` relation) and store it.
222
+ const TX_FIELDS = { transactionIndex: true, hash: true, from: true, to: true, input: true, value: true, nonce: true, gas: true, gasPrice: true, maxFeePerGas: true, maxPriorityFeePerGas: true, type: true, r: true, s: true, v: true, yParity: true };
223
+ // receipt fields ride on Portal's transaction object (no separate receipt entity)
224
+ const RECEIPT_FIELDS = { status: true, cumulativeGasUsed: true, effectiveGasPrice: true, gasUsed: true, contractAddress: true, logsBloom: true };
225
+ let needReceipts = false; // set from filters on first syncBlockRangeData (stable per chain)
226
+ // trace fields: request both flattened selectors (some Portal builds) AND rely on
227
+ // nested action/result in the response — the transform reads whichever is present.
228
+ const TRACE_FIELDS = {
229
+ transactionIndex: true, traceAddress: true, type: true, subtraces: true, error: true, revertReason: true,
230
+ callFrom: true, callTo: true, callValue: true, callGas: true, callInput: true, callSighash: true, callCallType: true, callResultGasUsed: true, callResultOutput: true,
231
+ createFrom: true, createValue: true, createGas: true, createInit: true, createResultGasUsed: true, createResultCode: true, createResultAddress: true,
232
+ suicideAddress: true, suicideRefundAddress: true, suicideBalance: true,
233
+ };
234
+ let needTraces = false;
235
+ let traceFilters: any[] = [];
236
+ let transferFilters: any[] = [];
237
+ let needBlocks = false;
238
+ let blockFilters: any[] = [];
239
+ let needTxFilter = false;
240
+ let transactionFilters: any[] = [];
241
+ const blockFieldsFor = (filters: Filter[]): Record<string, boolean> => {
242
+ const inc = new Set<string>();
243
+ for (const f of filters) for (const i of f.include ?? []) if (i.startsWith("block.")) inc.add(i.slice(6));
244
+ const fields: Record<string, boolean> = {};
245
+ for (const k of REQUIRED_BLOCK_FIELDS) fields[k] = true;
246
+ for (const k of NULLABLE_BLOCK_FIELDS) if (inc.has(k)) fields[k] = true;
247
+ return fields;
248
+ };
249
+
250
+ // ---- discovery: one sparse stream per chunk, accumulating children (memoized) ----
251
+ function discoverChunk(idx: number, factories: any[]): Promise<void> {
252
+ let p = discCache.get(idx);
253
+ if (p) return p;
254
+ p = (async () => {
255
+ stats.discChunks++;
256
+ const from = idx * chunkBlocks, to = from + chunkBlocks - 1;
257
+ for (const factory of factories) {
258
+ const needsData = factory.childAddressLocation.startsWith("offset");
259
+ const q = { type: "evm", fields: { block: { number: true }, log: { address: true, topics: true, data: needsData } }, logs: [{ address: factory.address ? (Array.isArray(factory.address) ? factory.address : [factory.address]).map((a: string) => a.toLowerCase()) : undefined, topic0: [factory.eventSelector.toLowerCase()] }] };
260
+ const rec = args.childAddresses.get(factory.id)!;
261
+ for await (const blocks of stream(q, from, to)) {
262
+ for (const b of blocks) for (const raw of b.logs ?? []) {
263
+ const sl = { address: (raw.address as string)?.toLowerCase(), topics: raw.topics ?? [], data: raw.data ?? "0x", blockNumber: hx(b.header.number) } as unknown as SyncLog;
264
+ if (isLogFactoryMatched({ factory, log: sl })) {
265
+ const child = getChildAddress({ log: sl, factory }).toLowerCase() as Address;
266
+ const bn = b.header.number; const prev = rec.get(child);
267
+ if (prev === undefined || prev > bn) rec.set(child, bn);
268
+ }
269
+ }
270
+ }
271
+ }
272
+ })();
273
+ discCache.set(idx, p);
274
+ return p;
275
+ }
276
+ // discovery complete THROUGH chunk idx (children of all chunks ≤ idx are known)
277
+ function ensureDiscoveredThrough(idx: number, factories: any[]): Promise<unknown> {
278
+ if (factories.length === 0 || discStartIdx === undefined) return Promise.resolve();
279
+ const ps: Promise<void>[] = [];
280
+ for (let i = discStartIdx; i <= idx; i++) ps.push(discoverChunk(i, factories));
281
+ return Promise.all(ps);
282
+ }
283
+
284
+ // ---- data chunk: gated on discovery-through-this-chunk, then ONE big data stream ----
285
+ function dataChunk(idx: number, factories: any[], filters: LogFilter[]): Promise<ChunkData> {
286
+ let p = dataCache.get(idx);
287
+ if (p) { stats.cacheHits++; return p; }
288
+ p = (async () => {
289
+ await ensureDiscoveredThrough(idx, factories); // correctness: children ≤ this chunk are known
290
+ stats.dataChunks++;
291
+ const from = idx * chunkBlocks, to = from + chunkBlocks - 1;
292
+ const logRequests = filters.flatMap((f) => logRequestsFor(f)).map((r) => ({ ...r, transaction: true }));
293
+ const data: ChunkData = { headers: new Map(), logs: new Map(), txs: new Map(), traceBlocks: new Map(), blockHeaders: new Map(), txBlocks: new Map() };
294
+ if (logRequests.length > 0) {
295
+ const q = { type: "evm", fields: { block: blockFieldsFor(filters), log: LOG_FIELDS, transaction: needReceipts ? { ...TX_FIELDS, ...RECEIPT_FIELDS } : TX_FIELDS }, logs: logRequests };
296
+ for await (const blocks of stream(q, from, to)) {
297
+ for (const b of blocks) if (b.logs?.length) {
298
+ data.headers.set(b.header.number, b.header);
299
+ data.logs.set(b.header.number, (data.logs.get(b.header.number) ?? []).concat(b.logs));
300
+ if (b.transactions?.length) data.txs.set(b.header.number, (data.txs.get(b.header.number) ?? []).concat(b.transactions));
301
+ stats.logs += b.logs.length;
302
+ }
303
+ }
304
+ }
305
+ if (needTraces) {
306
+ const tq = { type: "evm", fields: { block: blockFieldsFor(filters), trace: TRACE_FIELDS, transaction: { transactionIndex: true, hash: true, from: true, to: true, input: true, value: true, nonce: true, gas: true, gasPrice: true, type: true, r: true, s: true, v: true } }, traces: tracePortalRequests() };
307
+ for await (const blocks of stream(tq, from, to)) {
308
+ for (const b of blocks) if (b.traces?.length) {
309
+ const ex = data.traceBlocks.get(b.header.number);
310
+ if (ex) { ex.traces.push(...b.traces); if (b.transactions) ex.txs.push(...b.transactions); }
311
+ else data.traceBlocks.set(b.header.number, { header: b.header, traces: b.traces, txs: b.transactions ?? [] });
312
+ }
313
+ }
314
+ }
315
+ // block-interval sources: includeAllBlocks range-scan (Portal has no modulo filter),
316
+ // keep only headers matching a BlockFilter's interval/offset.
317
+ if (needBlocks) {
318
+ const bq = { type: "evm", includeAllBlocks: true, fields: { block: blockFieldsFor(blockFilters) } };
319
+ for await (const blocks of stream(bq, from, to)) {
320
+ for (const b of blocks) {
321
+ const bn = b.header.number;
322
+ if (blockFilters.some((f) => isBlockFilterMatched({ filter: f, block: { number: BigInt(bn) } }))) data.blockHeaders.set(bn, b.header);
323
+ }
324
+ }
325
+ }
326
+ // account transaction sources: Portal transactions[] from/to filter pushed server-side
327
+ if (needTxFilter) {
328
+ const txReqs = txPortalRequests();
329
+ if (txReqs.length) {
330
+ const tq = { type: "evm", fields: { block: blockFieldsFor(transactionFilters), transaction: needReceipts ? { ...TX_FIELDS, ...RECEIPT_FIELDS } : TX_FIELDS }, transactions: txReqs };
331
+ for await (const blocks of stream(tq, from, to)) {
332
+ for (const b of blocks) if (b.transactions?.length) {
333
+ const ex = data.txBlocks.get(b.header.number);
334
+ if (ex) ex.txs.push(...b.transactions);
335
+ else data.txBlocks.set(b.header.number, { header: b.header, txs: b.transactions });
336
+ }
337
+ }
338
+ }
339
+ }
340
+ return data;
341
+ })();
342
+ dataCache.set(idx, p);
343
+ return p;
344
+ }
345
+
346
+
347
+ const factoryAddrOk = (filterAddr: any, addr: string | undefined, bn: number): boolean =>
348
+ !isAddressFactory(filterAddr) || isAddressMatched({ address: addr as Address, blockNumber: bn, childAddresses: args.childAddresses.get(filterAddr.id)! });
349
+ const traceMatched = (frame: any, bn: number): boolean => {
350
+ const blk = { number: BigInt(bn) } as any;
351
+ for (const f of transferFilters) if (isTransferFilterMatched({ filter: f, trace: frame, block: blk }) && factoryAddrOk(f.fromAddress, frame.from, bn) && factoryAddrOk(f.toAddress, frame.to, bn)) return true;
352
+ for (const f of traceFilters) if (isTraceFilterMatched({ filter: f, trace: frame, block: blk }) && factoryAddrOk(f.fromAddress, frame.from, bn) && factoryAddrOk(f.toAddress, frame.to, bn)) return true;
353
+ return false;
354
+ };
355
+ const buildTraces = (cd: ChunkData, lo: number, hi: number): { trace: SyncTrace; block: SyncBlock; transaction: SyncTransaction }[] => {
356
+ const out: { trace: SyncTrace; block: SyncBlock; transaction: SyncTransaction }[] = [];
357
+ for (const [bn, tb] of cd.traceBlocks) {
358
+ if (bn < lo || bn > hi || !tb.traces?.length) continue;
359
+ const block = toSyncBlockHeader(tb.header) as unknown as SyncBlock; // encodeTrace only reads block.number
360
+ const txByIdx = new Map<number, any>();
361
+ for (const tx of tb.txs ?? []) txByIdx.set(tx.transactionIndex, tx);
362
+ const byTx = new Map<number, any[]>();
363
+ for (const t of tb.traces) { const k = t.transactionIndex ?? 0; if (!byTx.has(k)) byTx.set(k, []); byTx.get(k)!.push(t); }
364
+ for (const [txIndex, traces] of byTx) {
365
+ traces.sort((x, y) => cmpTraceAddr(x.traceAddress ?? [], y.traceAddress ?? []));
366
+ const rawTx = txByIdx.get(txIndex);
367
+ traces.forEach((t, i) => {
368
+ const frame = parityToCallFrame(t, i);
369
+ if (!frame || !traceMatched(frame, bn)) return;
370
+ out.push({ trace: { trace: frame, transactionHash: rawTx?.hash } as unknown as SyncTrace, block, transaction: rawTx ? toSyncTransaction(rawTx, tb.header) : ({ transactionIndex: hx(txIndex) } as unknown as SyncTransaction) });
371
+ });
372
+ }
373
+ }
374
+ return out;
375
+ };
376
+ // push the trace/transfer filters' addresses to Portal (resolving factories → children)
377
+ const tracePortalRequests = (): any[] => {
378
+ const reqs: any[] = [];
379
+ const addrsOf = (a: any): string[] | undefined => {
380
+ if (a === undefined) return undefined;
381
+ if (isAddressFactory(a)) return Array.from(args.childAddresses.get(a.id)?.keys() ?? []);
382
+ return (Array.isArray(a) ? a : [a]).map((x: string) => x.toLowerCase());
383
+ };
384
+ for (const f of [...traceFilters, ...transferFilters]) {
385
+ const req: any = {};
386
+ const to = addrsOf(f.toAddress); if (to?.length) req.callTo = to;
387
+ const from = addrsOf(f.fromAddress); if (from?.length) req.callFrom = from;
388
+ if (f.functionSelector) req.callSighash = (Array.isArray(f.functionSelector) ? f.functionSelector : [f.functionSelector]).map((s: string) => s.toLowerCase());
389
+ req.transaction = true; reqs.push(req);
390
+ }
391
+ return reqs.length ? reqs : [{ transaction: true }];
392
+ };
393
+ // push account TransactionFilters (from/to) to Portal's transactions[] (server-side row filter)
394
+ const txPortalRequests = (): any[] => {
395
+ const reqs: any[] = [];
396
+ const addrsOf = (a: any): string[] | undefined => {
397
+ if (a === undefined) return undefined;
398
+ if (isAddressFactory(a)) return Array.from(args.childAddresses.get(a.id)?.keys() ?? []);
399
+ return (Array.isArray(a) ? a : [a]).map((x: string) => x.toLowerCase());
400
+ };
401
+ for (const f of transactionFilters) {
402
+ const req: any = {};
403
+ const from = addrsOf(f.fromAddress); if (from?.length) req.from = from;
404
+ const to = addrsOf(f.toAddress); if (to?.length) req.to = to;
405
+ if (req.from || req.to) reqs.push(req); // skip match-all (never fetch every tx)
406
+ }
407
+ return reqs;
408
+ };
409
+
410
+ return {
411
+ async syncBlockRangeData(params) {
412
+ const { interval, requiredIntervals, requiredFactoryIntervals, syncStore } = params;
413
+ if (!startTime) startTime = Date.now();
414
+ // finality gap: if this interval reaches past Portal's finalized head, re-confirm
415
+ // (Portal advances) and, if still beyond, delegate the whole interval to RPC.
416
+ if (portalHead === undefined) await refreshPortalHead();
417
+ if (isFinalityGap(interval[1], portalHead)) {
418
+ await refreshPortalHead(); // Portal advances — re-confirm before falling back
419
+ if (isFinalityGap(interval[1], portalHead)) {
420
+ delegated.add(ikey(interval)); stats.rpcFallback++;
421
+ log.debug({ service: "portal", msg: `Portal ${args.chain.name} [${interval[0]},${interval[1]}] past finalized head ${portalHead} → RPC fallback` });
422
+ return rpcFallback().syncBlockRangeData(params);
423
+ }
424
+ }
425
+ await ensureChunkSize(); // scale chunk to chain block-density before any idxOf()
426
+ const filters = requiredIntervals.map((r) => r.filter).filter((f) => f.type === "log") as LogFilter[];
427
+ const factories = [...new Map(requiredFactoryIntervals.map((r) => [r.factory.id, r.factory])).values()];
428
+
429
+ needReceipts ||= requiredIntervals.some((r) => (r.filter as any).hasTransactionReceipt === true);
430
+ if (!needBlocks && requiredIntervals.some((r) => r.filter.type === "block")) {
431
+ blockFilters = requiredIntervals.filter((r) => r.filter.type === "block").map((r) => r.filter);
432
+ needBlocks = blockFilters.length > 0;
433
+ }
434
+ if (!needTxFilter && requiredIntervals.some((r) => r.filter.type === "transaction")) {
435
+ transactionFilters = requiredIntervals.filter((r) => r.filter.type === "transaction").map((r) => r.filter);
436
+ needTxFilter = transactionFilters.length > 0;
437
+ }
438
+ if (!needTraces && requiredIntervals.some((r) => r.filter.type === "trace" || r.filter.type === "transfer")) {
439
+ traceFilters = requiredIntervals.filter((r) => r.filter.type === "trace").map((r) => r.filter);
440
+ transferFilters = requiredIntervals.filter((r) => r.filter.type === "transfer").map((r) => r.filter);
441
+ needTraces = traceFilters.length + transferFilters.length > 0;
442
+ }
443
+ // cap the chunk grid BEFORE any idxOf() for DENSE sources (traces fetch every trace;
444
+ // block sources includeAllBlocks-scan the WHOLE chunk range) — bounds memory + overfetch.
445
+ const capped = traceSafeChunkBlocks(chunkBlocks, needTraces || needBlocks);
446
+ if (capped !== chunkBlocks) {
447
+ chunkBlocks = capped; dataCache.clear(); discCache.clear(); discStartIdx = undefined;
448
+ log.debug({ service: "portal", msg: `Portal ${args.chain.name}: dense sources → chunkBlocks capped to ${chunkBlocks} (grid reset)` });
449
+ }
450
+
451
+ // pin the discovery floor at the factory's real start (NOT block 0) — after any chunk cap
452
+ if (discStartIdx === undefined && factories.length > 0) {
453
+ const starts = requiredFactoryIntervals.map((r) => r.interval[0]).concat(interval[0]);
454
+ discStartIdx = idxOf(Math.min(...starts));
455
+ }
456
+
457
+ const startIdx = idxOf(interval[0]), endIdx = idxOf(interval[1]);
458
+ const idxs: number[] = [];
459
+ for (let i = startIdx; i <= endIdx; i++) idxs.push(i);
460
+ const data = await Promise.all(idxs.map((i) => dataChunk(i, factories, filters)));
461
+ // PARALLEL read-ahead: prefetch the next READAHEAD chunks concurrently
462
+ for (let d = 1; d <= READAHEAD; d++) void dataChunk(endIdx + d, factories, filters).catch(() => {});
463
+
464
+ const syncLogs: SyncLog[] = [];
465
+ const blocksByNumber = new Map<number, SyncBlockHeader>();
466
+ const syncTxs: SyncTransaction[] = [];
467
+ const syncReceipts: SyncTransactionReceipt[] = [];
468
+ const seenTx = new Set<string>();
469
+ for (const cd of data) for (const [bn, hdr] of cd.headers) {
470
+ if (bn < interval[0] || bn > interval[1]) continue;
471
+ const logs = cd.logs.get(bn) ?? [];
472
+ if (logs.length) {
473
+ blocksByNumber.set(bn, toSyncBlockHeader(hdr));
474
+ for (const raw of logs) syncLogs.push(toSyncLog(raw, hdr));
475
+ for (const tx of cd.txs.get(bn) ?? []) if (!seenTx.has(tx.hash)) {
476
+ seenTx.add(tx.hash);
477
+ syncTxs.push(toSyncTransaction(tx, hdr));
478
+ if (needReceipts) syncReceipts.push(toSyncReceipt(tx, hdr));
479
+ }
480
+ }
481
+ }
482
+ // block-interval sources: ensure each matched block is in the blocks table
483
+ if (needBlocks) for (const cd of data) for (const [bn, hdr] of cd.blockHeaders) {
484
+ if (bn >= interval[0] && bn <= interval[1] && !blocksByNumber.has(bn)) blocksByNumber.set(bn, toSyncBlockHeader(hdr));
485
+ }
486
+ // account transaction sources: re-match Portal's from/to-filtered txs (+ factory + range), insert tx/receipt/block
487
+ if (needTxFilter) for (const cd of data) for (const [bn, tb] of cd.txBlocks) {
488
+ if (bn < interval[0] || bn > interval[1]) continue;
489
+ for (const raw of tb.txs) {
490
+ if (seenTx.has(raw.hash)) continue;
491
+ const tx = toSyncTransaction(raw, tb.header);
492
+ if (!transactionFilters.some((f) => isTransactionFilterMatched({ filter: f, transaction: tx }) && factoryAddrOk(f.fromAddress, tx.from, bn) && factoryAddrOk(f.toAddress, (tx.to ?? undefined) as any, bn))) continue;
493
+ seenTx.add(raw.hash);
494
+ blocksByNumber.set(bn, toSyncBlockHeader(tb.header));
495
+ syncTxs.push(tx);
496
+ if (needReceipts) syncReceipts.push(toSyncReceipt(raw, tb.header));
497
+ }
498
+ }
499
+ for (const i of dataCache.keys()) if ((i + 1) * chunkBlocks <= interval[0]) dataCache.delete(i); // evict behind
500
+
501
+ const syncTraces = needTraces ? data.flatMap((cd) => buildTraces(cd, interval[0], interval[1])) : [];
502
+
503
+ let closest: SyncBlock | undefined;
504
+ if (blocksByNumber.size > 0) closest = blocksByNumber.get(Math.max(...blocksByNumber.keys())) as unknown as SyncBlock;
505
+ await syncStore.insertLogs({ logs: syncLogs, chainId: args.chain.id });
506
+ stash.set(ikey(interval), { blocks: [...blocksByNumber.values()], txs: syncTxs, receipts: syncReceipts, traces: syncTraces, closest });
507
+
508
+ log.debug({ service: "portal", msg: `Portal ${args.chain.name} [${interval[0]},${interval[1]}]: ${syncLogs.length} logs (dataChunks=${stats.dataChunks} discChunks=${stats.discChunks} http=${stats.http} hits=${stats.cacheHits} inflight=${stats.maxInflight} err=${stats.errors})` });
509
+ return syncLogs;
510
+ },
511
+
512
+ async syncBlockData(params) {
513
+ const { interval, syncStore } = params;
514
+ if (delegated.has(ikey(interval))) { delegated.delete(ikey(interval)); return rpcFallback().syncBlockData(params); }
515
+ const s = stash.get(ikey(interval));
516
+ stash.delete(ikey(interval));
517
+ if (!s) return undefined;
518
+ const chainId = args.chain.id;
519
+ // merge log blocks/txs with trace blocks/txs (a trace-only block isn't in the log set)
520
+ const blocks = new Map<string, SyncBlockHeader>();
521
+ for (const b of s.blocks) blocks.set(b.number as unknown as string, b);
522
+ const txs = new Map<string, SyncTransaction>();
523
+ for (const t of s.txs) txs.set(t.hash as unknown as string, t);
524
+ for (const { block, transaction } of s.traces) {
525
+ blocks.set((block as any).number, block as unknown as SyncBlockHeader);
526
+ if ((transaction as any)?.hash) txs.set((transaction as any).hash, transaction);
527
+ }
528
+ const blockArr = [...blocks.values()];
529
+ if (blockArr.length === 0) return s.closest;
530
+ await syncStore.insertBlocks({ blocks: blockArr, chainId });
531
+ if (txs.size) await syncStore.insertTransactions({ transactions: [...txs.values()], chainId });
532
+ if (s.receipts.length) await syncStore.insertTransactionReceipts({ transactionReceipts: s.receipts, chainId });
533
+ if (s.traces.length) await syncStore.insertTraces({ traces: s.traces, chainId });
534
+ stats.blocks += blockArr.length; stats.txs += txs.size; stats.receipts += s.receipts.length; stats.traces += s.traces.length;
535
+ writeMetrics();
536
+ return s.closest;
537
+ },
538
+ };
539
+ };