tapimo 0.0.0 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (422) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +119 -0
  3. package/dist/ApiKey.d.ts +65 -0
  4. package/dist/ApiKey.d.ts.map +1 -0
  5. package/dist/ApiKey.js +105 -0
  6. package/dist/ApiKey.js.map +1 -0
  7. package/dist/ApiKeySource.d.ts +166 -0
  8. package/dist/ApiKeySource.d.ts.map +1 -0
  9. package/dist/ApiKeySource.js +234 -0
  10. package/dist/ApiKeySource.js.map +1 -0
  11. package/dist/App.d.ts +19317 -0
  12. package/dist/App.d.ts.map +1 -0
  13. package/dist/App.js +612 -0
  14. package/dist/App.js.map +1 -0
  15. package/dist/Assets.d.ts +44 -0
  16. package/dist/Assets.d.ts.map +1 -0
  17. package/dist/Assets.js +23 -0
  18. package/dist/Assets.js.map +1 -0
  19. package/dist/Auth.d.ts +2 -0
  20. package/dist/Auth.d.ts.map +1 -0
  21. package/dist/Auth.js +2 -0
  22. package/dist/Auth.js.map +1 -0
  23. package/dist/Cli.d.ts +21 -0
  24. package/dist/Cli.d.ts.map +1 -0
  25. package/dist/Cli.js +13 -0
  26. package/dist/Cli.js.map +1 -0
  27. package/dist/Client.d.ts +20 -0
  28. package/dist/Client.d.ts.map +1 -0
  29. package/dist/Client.js +24 -0
  30. package/dist/Client.js.map +1 -0
  31. package/dist/Metrics.d.ts +70 -0
  32. package/dist/Metrics.d.ts.map +1 -0
  33. package/dist/Metrics.js +91 -0
  34. package/dist/Metrics.js.map +1 -0
  35. package/dist/Scope.d.ts +35 -0
  36. package/dist/Scope.d.ts.map +1 -0
  37. package/dist/Scope.js +38 -0
  38. package/dist/Scope.js.map +1 -0
  39. package/dist/Webhooks.d.ts +79 -0
  40. package/dist/Webhooks.d.ts.map +1 -0
  41. package/dist/Webhooks.js +107 -0
  42. package/dist/Webhooks.js.map +1 -0
  43. package/dist/admin/Access.d.ts +41 -0
  44. package/dist/admin/Access.d.ts.map +1 -0
  45. package/dist/admin/Access.js +75 -0
  46. package/dist/admin/Access.js.map +1 -0
  47. package/dist/admin/App.d.ts +1221 -0
  48. package/dist/admin/App.d.ts.map +1 -0
  49. package/dist/admin/App.js +113 -0
  50. package/dist/admin/App.js.map +1 -0
  51. package/dist/admin/apps/api-keys.d.ts +209 -0
  52. package/dist/admin/apps/api-keys.d.ts.map +1 -0
  53. package/dist/admin/apps/api-keys.js +129 -0
  54. package/dist/admin/apps/api-keys.js.map +1 -0
  55. package/dist/admin/apps/scopes.d.ts +46 -0
  56. package/dist/admin/apps/scopes.d.ts.map +1 -0
  57. package/dist/admin/apps/scopes.js +41 -0
  58. package/dist/admin/apps/scopes.js.map +1 -0
  59. package/dist/admin/apps/verified-tokens.d.ts +1206 -0
  60. package/dist/admin/apps/verified-tokens.d.ts.map +1 -0
  61. package/dist/admin/apps/verified-tokens.js +485 -0
  62. package/dist/admin/apps/verified-tokens.js.map +1 -0
  63. package/dist/admin/index.d.ts +3 -0
  64. package/dist/admin/index.d.ts.map +1 -0
  65. package/dist/admin/index.js +3 -0
  66. package/dist/admin/index.js.map +1 -0
  67. package/dist/admin/ui.generated.d.ts +11 -0
  68. package/dist/admin/ui.generated.d.ts.map +1 -0
  69. package/dist/admin/ui.generated.js +43 -0
  70. package/dist/admin/ui.generated.js.map +1 -0
  71. package/dist/apps/activities.d.ts +18725 -0
  72. package/dist/apps/activities.d.ts.map +1 -0
  73. package/dist/apps/activities.js +3138 -0
  74. package/dist/apps/activities.js.map +1 -0
  75. package/dist/apps/addresses.d.ts +3662 -0
  76. package/dist/apps/addresses.d.ts.map +1 -0
  77. package/dist/apps/addresses.js +22 -0
  78. package/dist/apps/addresses.js.map +1 -0
  79. package/dist/apps/balances.d.ts +342 -0
  80. package/dist/apps/balances.d.ts.map +1 -0
  81. package/dist/apps/balances.js +393 -0
  82. package/dist/apps/balances.js.map +1 -0
  83. package/dist/apps/blocks.d.ts +931 -0
  84. package/dist/apps/blocks.d.ts.map +1 -0
  85. package/dist/apps/blocks.js +689 -0
  86. package/dist/apps/blocks.js.map +1 -0
  87. package/dist/apps/coingecko.d.ts +1053 -0
  88. package/dist/apps/coingecko.d.ts.map +1 -0
  89. package/dist/apps/coingecko.js +1015 -0
  90. package/dist/apps/coingecko.js.map +1 -0
  91. package/dist/apps/exchanges.d.ts +3000 -0
  92. package/dist/apps/exchanges.d.ts.map +1 -0
  93. package/dist/apps/exchanges.js +3570 -0
  94. package/dist/apps/exchanges.js.map +1 -0
  95. package/dist/apps/fee-amm.d.ts +640 -0
  96. package/dist/apps/fee-amm.d.ts.map +1 -0
  97. package/dist/apps/fee-amm.js +815 -0
  98. package/dist/apps/fee-amm.js.map +1 -0
  99. package/dist/apps/indexer.d.ts +116 -0
  100. package/dist/apps/indexer.d.ts.map +1 -0
  101. package/dist/apps/indexer.js +270 -0
  102. package/dist/apps/indexer.js.map +1 -0
  103. package/dist/apps/receipts.d.ts +1438 -0
  104. package/dist/apps/receipts.d.ts.map +1 -0
  105. package/dist/apps/receipts.js +874 -0
  106. package/dist/apps/receipts.js.map +1 -0
  107. package/dist/apps/rpc.d.ts +173 -0
  108. package/dist/apps/rpc.d.ts.map +1 -0
  109. package/dist/apps/rpc.js +191 -0
  110. package/dist/apps/rpc.js.map +1 -0
  111. package/dist/apps/tokenlist.d.ts +192 -0
  112. package/dist/apps/tokenlist.d.ts.map +1 -0
  113. package/dist/apps/tokenlist.js +153 -0
  114. package/dist/apps/tokenlist.js.map +1 -0
  115. package/dist/apps/tokens.d.ts +2246 -0
  116. package/dist/apps/tokens.d.ts.map +1 -0
  117. package/dist/apps/tokens.js +1789 -0
  118. package/dist/apps/tokens.js.map +1 -0
  119. package/dist/apps/transactions.d.ts +2327 -0
  120. package/dist/apps/transactions.d.ts.map +1 -0
  121. package/dist/apps/transactions.js +975 -0
  122. package/dist/apps/transactions.js.map +1 -0
  123. package/dist/apps/transfers.d.ts +589 -0
  124. package/dist/apps/transfers.d.ts.map +1 -0
  125. package/dist/apps/transfers.js +769 -0
  126. package/dist/apps/transfers.js.map +1 -0
  127. package/dist/apps/verified-tokens.d.ts +544 -0
  128. package/dist/apps/verified-tokens.d.ts.map +1 -0
  129. package/dist/apps/verified-tokens.js +234 -0
  130. package/dist/apps/verified-tokens.js.map +1 -0
  131. package/dist/apps/webhooks.d.ts +7684 -0
  132. package/dist/apps/webhooks.d.ts.map +1 -0
  133. package/dist/apps/webhooks.js +2112 -0
  134. package/dist/apps/webhooks.js.map +1 -0
  135. package/dist/cloudflare.d.ts +32 -0
  136. package/dist/cloudflare.d.ts.map +1 -0
  137. package/dist/cloudflare.js +45 -0
  138. package/dist/cloudflare.js.map +1 -0
  139. package/dist/index.d.ts +16 -0
  140. package/dist/index.d.ts.map +1 -0
  141. package/dist/index.js +16 -0
  142. package/dist/index.js.map +1 -0
  143. package/dist/internal/Auth.d.ts +298 -0
  144. package/dist/internal/Auth.d.ts.map +1 -0
  145. package/dist/internal/Auth.js +618 -0
  146. package/dist/internal/Auth.js.map +1 -0
  147. package/dist/internal/Cache.d.ts +74 -0
  148. package/dist/internal/Cache.d.ts.map +1 -0
  149. package/dist/internal/Cache.js +215 -0
  150. package/dist/internal/Cache.js.map +1 -0
  151. package/dist/internal/Cursor.d.ts +79 -0
  152. package/dist/internal/Cursor.d.ts.map +1 -0
  153. package/dist/internal/Cursor.js +116 -0
  154. package/dist/internal/Cursor.js.map +1 -0
  155. package/dist/internal/EdgeCache.d.ts +59 -0
  156. package/dist/internal/EdgeCache.d.ts.map +1 -0
  157. package/dist/internal/EdgeCache.js +174 -0
  158. package/dist/internal/EdgeCache.js.map +1 -0
  159. package/dist/internal/Log.d.ts +104 -0
  160. package/dist/internal/Log.d.ts.map +1 -0
  161. package/dist/internal/Log.js +118 -0
  162. package/dist/internal/Log.js.map +1 -0
  163. package/dist/internal/MetricSink.d.ts +65 -0
  164. package/dist/internal/MetricSink.d.ts.map +1 -0
  165. package/dist/internal/MetricSink.js +146 -0
  166. package/dist/internal/MetricSink.js.map +1 -0
  167. package/dist/internal/Mpp.d.ts +71 -0
  168. package/dist/internal/Mpp.d.ts.map +1 -0
  169. package/dist/internal/Mpp.js +105 -0
  170. package/dist/internal/Mpp.js.map +1 -0
  171. package/dist/internal/OpenApi.d.ts +313 -0
  172. package/dist/internal/OpenApi.d.ts.map +1 -0
  173. package/dist/internal/OpenApi.fonts.d.ts +2 -0
  174. package/dist/internal/OpenApi.fonts.d.ts.map +1 -0
  175. package/dist/internal/OpenApi.fonts.js +39 -0
  176. package/dist/internal/OpenApi.fonts.js.map +1 -0
  177. package/dist/internal/OpenApi.js +335 -0
  178. package/dist/internal/OpenApi.js.map +1 -0
  179. package/dist/internal/OpenApi.logo.d.ts +6 -0
  180. package/dist/internal/OpenApi.logo.d.ts.map +1 -0
  181. package/dist/internal/OpenApi.logo.js +19 -0
  182. package/dist/internal/OpenApi.logo.js.map +1 -0
  183. package/dist/internal/Path.d.ts +5 -0
  184. package/dist/internal/Path.d.ts.map +1 -0
  185. package/dist/internal/Path.js +16 -0
  186. package/dist/internal/Path.js.map +1 -0
  187. package/dist/internal/RateLimit.d.ts +60 -0
  188. package/dist/internal/RateLimit.d.ts.map +1 -0
  189. package/dist/internal/RateLimit.js +48 -0
  190. package/dist/internal/RateLimit.js.map +1 -0
  191. package/dist/internal/Response.d.ts +132 -0
  192. package/dist/internal/Response.d.ts.map +1 -0
  193. package/dist/internal/Response.js +72 -0
  194. package/dist/internal/Response.js.map +1 -0
  195. package/dist/internal/Schema.d.ts +257 -0
  196. package/dist/internal/Schema.d.ts.map +1 -0
  197. package/dist/internal/Schema.js +360 -0
  198. package/dist/internal/Schema.js.map +1 -0
  199. package/dist/internal/Store.d.ts +204 -0
  200. package/dist/internal/Store.d.ts.map +1 -0
  201. package/dist/internal/Store.js +291 -0
  202. package/dist/internal/Store.js.map +1 -0
  203. package/dist/internal/Tidx.d.ts +120 -0
  204. package/dist/internal/Tidx.d.ts.map +1 -0
  205. package/dist/internal/Tidx.js +156 -0
  206. package/dist/internal/Tidx.js.map +1 -0
  207. package/dist/internal/Timing.d.ts +22 -0
  208. package/dist/internal/Timing.d.ts.map +1 -0
  209. package/dist/internal/Timing.js +32 -0
  210. package/dist/internal/Timing.js.map +1 -0
  211. package/dist/internal/Ttl.d.ts +25 -0
  212. package/dist/internal/Ttl.d.ts.map +1 -0
  213. package/dist/internal/Ttl.js +49 -0
  214. package/dist/internal/Ttl.js.map +1 -0
  215. package/dist/internal/Value.d.ts +47 -0
  216. package/dist/internal/Value.d.ts.map +1 -0
  217. package/dist/internal/Value.js +122 -0
  218. package/dist/internal/Value.js.map +1 -0
  219. package/dist/internal/VerifiedTokens.d.ts +228 -0
  220. package/dist/internal/VerifiedTokens.d.ts.map +1 -0
  221. package/dist/internal/VerifiedTokens.js +450 -0
  222. package/dist/internal/VerifiedTokens.js.map +1 -0
  223. package/dist/internal/Viem.d.ts +2082 -0
  224. package/dist/internal/Viem.d.ts.map +1 -0
  225. package/dist/internal/Viem.js +63 -0
  226. package/dist/internal/Viem.js.map +1 -0
  227. package/dist/internal/WebhookDestination.d.ts +198 -0
  228. package/dist/internal/WebhookDestination.d.ts.map +1 -0
  229. package/dist/internal/WebhookDestination.js +170 -0
  230. package/dist/internal/WebhookDestination.js.map +1 -0
  231. package/dist/internal/Webhooks.d.ts +433 -0
  232. package/dist/internal/Webhooks.d.ts.map +1 -0
  233. package/dist/internal/Webhooks.js +489 -0
  234. package/dist/internal/Webhooks.js.map +1 -0
  235. package/dist/internal/webhookDestinations/betterStack.d.ts +26 -0
  236. package/dist/internal/webhookDestinations/betterStack.d.ts.map +1 -0
  237. package/dist/internal/webhookDestinations/betterStack.js +76 -0
  238. package/dist/internal/webhookDestinations/betterStack.js.map +1 -0
  239. package/dist/internal/webhookDestinations/slack.d.ts +59 -0
  240. package/dist/internal/webhookDestinations/slack.d.ts.map +1 -0
  241. package/dist/internal/webhookDestinations/slack.js +422 -0
  242. package/dist/internal/webhookDestinations/slack.js.map +1 -0
  243. package/dist/internal/webhookDestinations/url.d.ts +17 -0
  244. package/dist/internal/webhookDestinations/url.d.ts.map +1 -0
  245. package/dist/internal/webhookDestinations/url.js +130 -0
  246. package/dist/internal/webhookDestinations/url.js.map +1 -0
  247. package/dist/openrpc.json +5302 -0
  248. package/package.json +46 -4
  249. package/seeds/31318/icons/0x20c0000000000000000000000000000000000000.svg +1 -0
  250. package/seeds/31318/icons/0x20c0000000000000000000000000000000000001.svg +1 -0
  251. package/seeds/31318/icons/0x20c0000000000000000000000000000000000002.svg +1 -0
  252. package/seeds/31318/icons/0x20c0000000000000000000000000000000000003.svg +1 -0
  253. package/seeds/31318/verified-tokens.json +30 -0
  254. package/seeds/4217/icons/0x20c0000000000000000000000000000000000000.svg +25 -0
  255. package/seeds/4217/icons/0x20c000000000000000000000048c8f36df1c9a4a.svg +1 -0
  256. package/seeds/4217/icons/0x20c0000000000000000000000520792dcccccccc.svg +1 -0
  257. package/seeds/4217/icons/0x20c0000000000000000000000a6da882d075a4c3.svg +4 -0
  258. package/seeds/4217/icons/0x20c000000000000000000000111111111e910f0f.svg +30 -0
  259. package/seeds/4217/icons/0x20c00000000000000000000014f22ca97301eb73.svg +1 -0
  260. package/seeds/4217/icons/0x20c0000000000000000000001621e21f71cf12fb.svg +1 -0
  261. package/seeds/4217/icons/0x20c0000000000000000000002f52d5cc21a3207b.svg +1 -0
  262. package/seeds/4217/icons/0x20c0000000000000000000003158081efd85bfc2.svg +11 -0
  263. package/seeds/4217/icons/0x20c0000000000000000000003554d28269e0f3c2.svg +1 -0
  264. package/seeds/4217/icons/0x20c000000000000000000000383a23bacb546ab9.svg +1 -0
  265. package/seeds/4217/icons/0x20c00000000000000000000042109aef2f8b28e1.png +0 -0
  266. package/seeds/4217/icons/0x20c0000000000000000000005c0bac7cef389a11.svg +1 -0
  267. package/seeds/4217/icons/0x20c0000000000000000000006fd9a167923ba194.png +0 -0
  268. package/seeds/4217/icons/0x20c0000000000000000000007f7ba549dd0251b9.svg +1 -0
  269. package/seeds/4217/icons/0x20c0000000000000000000008191667423f70e67.svg +5 -0
  270. package/seeds/4217/icons/0x20c0000000000000000000008ee4fcff88888888.svg +1 -0
  271. package/seeds/4217/icons/0x20c0000000000000000000009a4a4b17e0dc6651.svg +1 -0
  272. package/seeds/4217/icons/0x20c000000000000000000000ab02d39df30bd17e.svg +1 -0
  273. package/seeds/4217/icons/0x20c000000000000000000000ae247a1130450f09.svg +1 -0
  274. package/seeds/4217/icons/0x20c000000000000000000000aeed2ec36a54d0e5.svg +1 -0
  275. package/seeds/4217/icons/0x20c000000000000000000000b9537d11c60e8b50.svg +1 -0
  276. package/seeds/4217/icons/0x20c000000000000000000000bd95bfb69fbe6ce3.svg +1 -0
  277. package/seeds/4217/icons/0x20c000000000000000000000c412ec89d0c08be5.svg +1 -0
  278. package/seeds/4217/icons/0x20c000000000000000000000f047dd7018e50367.png +0 -0
  279. package/seeds/4217/verified-tokens.json +177 -0
  280. package/seeds/42431/icons/0x20c0000000000000000000000000000000000000.svg +25 -0
  281. package/seeds/42431/icons/0x20c0000000000000000000000000000000000001.svg +1 -0
  282. package/seeds/42431/icons/0x20c0000000000000000000000000000000000002.svg +1 -0
  283. package/seeds/42431/icons/0x20c0000000000000000000000000000000000003.svg +1 -0
  284. package/seeds/42431/icons/0x20c0000000000000000000009e8d7eb59b783726.svg +1 -0
  285. package/seeds/42431/icons/0x20c000000000000000000000d72572838bbee59c.svg +1 -0
  286. package/seeds/42431/verified-tokens.json +44 -0
  287. package/seeds/openrpc/README.md +27 -0
  288. package/seeds/openrpc/execution-apis.json +8989 -0
  289. package/src/ApiKey.test.ts +91 -0
  290. package/src/ApiKey.ts +122 -0
  291. package/src/ApiKeySource.test-d.ts +11 -0
  292. package/src/ApiKeySource.test.ts +295 -0
  293. package/src/ApiKeySource.ts +303 -0
  294. package/src/App.test-d.ts +38 -0
  295. package/src/App.test.ts +65927 -0
  296. package/src/App.ts +855 -0
  297. package/src/Assets.ts +58 -0
  298. package/src/Auth.ts +1 -0
  299. package/src/Cli.test.ts +81 -0
  300. package/src/Cli.ts +38 -0
  301. package/src/Client.test-d.ts +1438 -0
  302. package/src/Client.test.ts +134 -0
  303. package/src/Client.ts +45 -0
  304. package/src/Metrics.ts +154 -0
  305. package/src/Scope.ts +50 -0
  306. package/src/Webhooks.test.ts +49 -0
  307. package/src/Webhooks.ts +172 -0
  308. package/src/admin/Access.test.ts +130 -0
  309. package/src/admin/Access.ts +87 -0
  310. package/src/admin/App.test.ts +225 -0
  311. package/src/admin/App.ts +216 -0
  312. package/src/admin/apps/api-keys.test.ts +156 -0
  313. package/src/admin/apps/api-keys.ts +157 -0
  314. package/src/admin/apps/scopes.test.ts +52 -0
  315. package/src/admin/apps/scopes.ts +47 -0
  316. package/src/admin/apps/verified-tokens.test.ts +415 -0
  317. package/src/admin/apps/verified-tokens.ts +566 -0
  318. package/src/admin/index.ts +2 -0
  319. package/src/admin/ui/plugin.ts +135 -0
  320. package/src/admin/ui/src/components/Checkbox.tsx +22 -0
  321. package/src/admin/ui/src/components/Dialog.tsx +32 -0
  322. package/src/admin/ui/src/components/MobileProfile.tsx +31 -0
  323. package/src/admin/ui/src/components/Page.tsx +53 -0
  324. package/src/admin/ui/src/components/TempoLogo.tsx +37 -0
  325. package/src/admin/ui/src/fonts/Pilat-Demi.woff2 +0 -0
  326. package/src/admin/ui/src/fonts/Pilat-Regular.woff2 +0 -0
  327. package/src/admin/ui/src/lib/api-keys.ts +63 -0
  328. package/src/admin/ui/src/lib/client.ts +15 -0
  329. package/src/admin/ui/src/lib/config.ts +57 -0
  330. package/src/admin/ui/src/lib/query-client.ts +26 -0
  331. package/src/admin/ui/src/lib/scopes.ts +25 -0
  332. package/src/admin/ui/src/lib/verified-tokens.ts +89 -0
  333. package/src/admin/ui/src/lib/wagmi.ts +18 -0
  334. package/src/admin/ui/src/routeTree.gen.ts +104 -0
  335. package/src/admin/ui/src/router.tsx +25 -0
  336. package/src/admin/ui/src/routes/__root.tsx +219 -0
  337. package/src/admin/ui/src/routes/api-keys.tsx +329 -0
  338. package/src/admin/ui/src/routes/index.tsx +7 -0
  339. package/src/admin/ui/src/routes/verified-tokens.tsx +331 -0
  340. package/src/admin/ui/src/styles.css +20 -0
  341. package/src/admin/ui/tsconfig.json +16 -0
  342. package/src/admin/ui/vite.config.ts +29 -0
  343. package/src/admin/ui.generated.ts +60 -0
  344. package/src/apps/activities.test.ts +1020 -0
  345. package/src/apps/activities.ts +3934 -0
  346. package/src/apps/addresses.ts +24 -0
  347. package/src/apps/balances.test.ts +274 -0
  348. package/src/apps/balances.ts +533 -0
  349. package/src/apps/blocks.test.ts +385 -0
  350. package/src/apps/blocks.ts +896 -0
  351. package/src/apps/coingecko.test.ts +218 -0
  352. package/src/apps/coingecko.ts +1383 -0
  353. package/src/apps/exchanges.test.ts +958 -0
  354. package/src/apps/exchanges.ts +4773 -0
  355. package/src/apps/fee-amm.test.ts +321 -0
  356. package/src/apps/fee-amm.ts +1121 -0
  357. package/src/apps/indexer.test.ts +153 -0
  358. package/src/apps/indexer.ts +330 -0
  359. package/src/apps/receipts.test.ts +285 -0
  360. package/src/apps/receipts.ts +1193 -0
  361. package/src/apps/rpc.test.ts +302 -0
  362. package/src/apps/rpc.ts +222 -0
  363. package/src/apps/tokenlist.test.ts +80 -0
  364. package/src/apps/tokenlist.ts +207 -0
  365. package/src/apps/tokens.test.ts +1062 -0
  366. package/src/apps/tokens.ts +2292 -0
  367. package/src/apps/transactions.test.ts +408 -0
  368. package/src/apps/transactions.ts +1363 -0
  369. package/src/apps/transfers.test.ts +371 -0
  370. package/src/apps/transfers.ts +1116 -0
  371. package/src/apps/verified-tokens.test.ts +114 -0
  372. package/src/apps/verified-tokens.ts +295 -0
  373. package/src/apps/webhooks.test.ts +1937 -0
  374. package/src/apps/webhooks.ts +2926 -0
  375. package/src/cloudflare.ts +55 -0
  376. package/src/index.ts +15 -0
  377. package/src/internal/Auth.test.ts +980 -0
  378. package/src/internal/Auth.ts +1012 -0
  379. package/src/internal/Cache.test.ts +240 -0
  380. package/src/internal/Cache.ts +262 -0
  381. package/src/internal/Cursor.test.ts +100 -0
  382. package/src/internal/Cursor.ts +149 -0
  383. package/src/internal/EdgeCache.test.ts +234 -0
  384. package/src/internal/EdgeCache.ts +224 -0
  385. package/src/internal/Errors.ts +14 -0
  386. package/src/internal/Log.test.ts +244 -0
  387. package/src/internal/Log.ts +216 -0
  388. package/src/internal/MetricSink.ts +220 -0
  389. package/src/internal/Mpp.test.ts +64 -0
  390. package/src/internal/Mpp.ts +141 -0
  391. package/src/internal/OpenApi.fonts.ts +38 -0
  392. package/src/internal/OpenApi.logo.ts +18 -0
  393. package/src/internal/OpenApi.ts +488 -0
  394. package/src/internal/Path.test.ts +14 -0
  395. package/src/internal/Path.ts +15 -0
  396. package/src/internal/RateLimit.test.ts +93 -0
  397. package/src/internal/RateLimit.ts +93 -0
  398. package/src/internal/Response.ts +131 -0
  399. package/src/internal/Schema.test.ts +34 -0
  400. package/src/internal/Schema.ts +523 -0
  401. package/src/internal/Store.test.ts +545 -0
  402. package/src/internal/Store.ts +526 -0
  403. package/src/internal/Tidx.local.test.ts +33 -0
  404. package/src/internal/Tidx.test.ts +294 -0
  405. package/src/internal/Tidx.ts +238 -0
  406. package/src/internal/Timing.test.ts +22 -0
  407. package/src/internal/Timing.ts +62 -0
  408. package/src/internal/Ttl.test.ts +18 -0
  409. package/src/internal/Ttl.ts +59 -0
  410. package/src/internal/Value.ts +114 -0
  411. package/src/internal/VerifiedTokens.test.ts +333 -0
  412. package/src/internal/VerifiedTokens.ts +645 -0
  413. package/src/internal/Viem.test.ts +115 -0
  414. package/src/internal/Viem.ts +165 -0
  415. package/src/internal/WebhookDestination.test.ts +648 -0
  416. package/src/internal/WebhookDestination.ts +331 -0
  417. package/src/internal/Webhooks.test.ts +668 -0
  418. package/src/internal/Webhooks.ts +940 -0
  419. package/src/internal/webhookDestinations/betterStack.ts +91 -0
  420. package/src/internal/webhookDestinations/slack.ts +496 -0
  421. package/src/internal/webhookDestinations/url.ts +128 -0
  422. package/src/openrpc.json +5302 -0
@@ -0,0 +1,689 @@
1
+ import { Hono } from 'hono';
2
+ import { Hex } from 'ox';
3
+ import * as z from 'zod/mini';
4
+ import * as Auth from '../internal/Auth.js';
5
+ import * as Cache from '../internal/Cache.js';
6
+ import * as Cursor from '../internal/Cursor.js';
7
+ import * as OpenApi from '../internal/OpenApi.js';
8
+ import * as Response from '../internal/Response.js';
9
+ import * as Schema from '../internal/Schema.js';
10
+ import * as Store from '../internal/Store.js';
11
+ import * as Timing from '../internal/Timing.js';
12
+ import * as Ttl from '../internal/Ttl.js';
13
+ import * as Value from '../internal/Value.js';
14
+ /** Example block number surfaced in OpenAPI docs. */
15
+ const exampleBlockNumber = '1000000';
16
+ /** The EVM block tags accepted by the `:block` selector. */
17
+ const blockTags = ['latest', 'finalized'];
18
+ /**
19
+ * ISO 8601 timestamp selector for the `:block` parameter. Shared between the
20
+ * route's param schema (validation + OpenAPI `date-time` format) and
21
+ * {@link parseBlockIdentifier} (runtime dispatch) so the two cannot drift.
22
+ */
23
+ const blockTimestamp = z.iso.datetime({ offset: true });
24
+ /** Zod schemas owned by the block handlers. */
25
+ export var schema;
26
+ (function (schema) {
27
+ /** The verbatim JSON-RPC payloads, surfaced under each resource's `rpc` field. */
28
+ let Rpc;
29
+ (function (Rpc) {
30
+ // TODO(ox/zod): replace with the zod schema for `ox/Block.Rpc` once ox
31
+ // ships a `zod` entrypoint, so the wire format stays in lockstep with
32
+ // ox/viem.
33
+ /** A JSON-RPC block, passed through verbatim. */
34
+ Rpc.Block = z
35
+ .looseObject({
36
+ baseFeePerGas: z
37
+ .nullish(Schema.Quantity)
38
+ .check(z.describe('Base fee per gas for EIP-1559-style fee markets, if present.')),
39
+ difficulty: z
40
+ .optional(Schema.Quantity)
41
+ .check(z.describe('Block difficulty value from legacy proof-of-work fields, when present.')),
42
+ extraData: z
43
+ .optional(Schema.Hex)
44
+ .check(z.describe('Extra data bytes included by the block producer.'), z.meta({ examples: ['0x00'] })),
45
+ gasLimit: Schema.Quantity.check(z.describe('Maximum gas available for all transactions in this block.')),
46
+ gasUsed: Schema.Quantity.check(z.describe('Total gas used by every transaction in this block.')),
47
+ hash: z
48
+ .nullable(Schema.Hash)
49
+ .check(z.describe('The block hash, or `null` for a pending block.')),
50
+ logsBloom: z
51
+ .nullish(Schema.Hex)
52
+ .check(z.describe('Bloom filter summarizing logs in the block, or `null` while pending.'), z.meta({ examples: ['0x00'] })),
53
+ miner: Schema.Address.check(z.describe('The address of the block producer, also called the proposer or miner.')),
54
+ mixHash: z
55
+ .optional(Schema.Hash)
56
+ .check(z.describe('Consensus mix hash carried in the JSON-RPC block payload.')),
57
+ nonce: z
58
+ .nullish(Schema.Hex)
59
+ .check(z.describe('Legacy block nonce, or `null` while pending.'), z.meta({ examples: ['0x00'] })),
60
+ number: z
61
+ .nullable(Schema.Quantity)
62
+ .check(z.describe('The block number, or `null` for a pending block.')),
63
+ parentHash: Schema.Hash.check(z.describe('The hash of the previous block in the chain.')),
64
+ receiptsRoot: Schema.Hash.check(z.describe('Root hash of the receipts trie for this block.')),
65
+ sha3Uncles: z
66
+ .optional(Schema.Hash)
67
+ .check(z.describe('Keccak-256 hash of the uncle blocks list.')),
68
+ size: Schema.Quantity.check(z.describe('Size of the block in bytes.')),
69
+ stateRoot: Schema.Hash.check(z.describe('Root hash of the world-state trie after this block.')),
70
+ timestamp: Schema.Quantity.check(z.describe('Block timestamp as Unix seconds.')),
71
+ totalDifficulty: z
72
+ .optional(Schema.Quantity)
73
+ .check(z.describe('Total cumulative difficulty through this block, when present.')),
74
+ transactions: z
75
+ .array(Schema.Hash)
76
+ .check(z.describe('Transaction hashes included in this ordered batch.')),
77
+ transactionsRoot: Schema.Hash.check(z.describe('Root hash of the transactions trie for this block.')),
78
+ uncles: z
79
+ .optional(z.array(Schema.Hash))
80
+ .check(z.describe('Hashes of uncle blocks included in this block, when present.')),
81
+ withdrawals: z
82
+ .optional(z.array(z.looseObject({})))
83
+ .check(z.describe('EIP-4895 withdrawals included in this block, when present.')),
84
+ withdrawalsRoot: z
85
+ .optional(Schema.Hash)
86
+ .check(z.describe('Root hash of the withdrawals trie, when present.')),
87
+ })
88
+ .check(z.describe('The block exactly as returned by JSON-RPC.'));
89
+ })(Rpc = schema.Rpc || (schema.Rpc = {}));
90
+ /**
91
+ * Metadata for a block: the verbatim JSON-RPC block (always present). Held in
92
+ * a `meta` envelope so future opt-in `include` resources (e.g. embedded
93
+ * transactions) can layer in without breaking the response shape.
94
+ */
95
+ schema.BlockMeta = Schema.describe(z.object({
96
+ rpc: Rpc.Block.check(z.describe('The original JSON-RPC block payload.')),
97
+ }), 'Metadata for this block: the original JSON-RPC payload plus any future `include` resources.');
98
+ /**
99
+ * A humanized block: decimal-string amounts, ISO 8601 timestamps, and plain
100
+ * numbers for block fields. The verbatim JSON-RPC payload is always
101
+ * available under `meta.rpc`. Transactions are omitted from the response —
102
+ * clients fetch them via `GET /blocks/:block/transactions`.
103
+ */
104
+ schema.Block = z
105
+ .object({
106
+ baseFeePerGas: z
107
+ .nullable(Schema.DecimalString)
108
+ .check(z.describe('Base fee per gas for EIP-1559-style fee markets, or `null` when unsupported.')),
109
+ gasLimit: z
110
+ .number()
111
+ .check(z.int(), z.nonnegative(), z.describe('Maximum gas available for all transactions in this block.'), z.meta({ examples: [21000] })),
112
+ gasUsed: z
113
+ .number()
114
+ .check(z.int(), z.nonnegative(), z.describe('Total gas used by transactions in this block.'), z.meta({ examples: [21000] })),
115
+ hash: Schema.Hash.check(z.describe('A 32-byte hash, `0x`-prefixed and returned in lowercase, that identifies this block.')),
116
+ id: Schema.Hash.check(z.describe('Stable resource ID for this API response; it is the block hash.')),
117
+ meta: schema.BlockMeta.check(z.describe('The original JSON-RPC payload plus any resources requested with `include`.')),
118
+ miner: Schema.Address.check(z.describe('The address of the block producer, also called the proposer or miner.')),
119
+ number: z
120
+ .number()
121
+ .check(z.int(), z.nonnegative(), z.describe('The block height, starting from genesis block 0.'), z.meta({ examples: [23456789] })),
122
+ parentHash: Schema.Hash.check(z.describe('The hash of the previous block in the chain.')),
123
+ size: z
124
+ .number()
125
+ .check(z.int(), z.nonnegative(), z.describe('Size of the block in bytes.'), z.meta({ examples: [1024] })),
126
+ timestamp: z.iso
127
+ .datetime()
128
+ .check(z.describe('Block timestamp formatted as ISO 8601.'), z.meta({ examples: ['2024-01-01T00:00:00Z'] })),
129
+ transactionCount: z
130
+ .number()
131
+ .check(z.int(), z.nonnegative(), z.describe('Number of transactions included in this block.'), z.meta({ examples: [25] })),
132
+ })
133
+ .check(z.describe('A block formatted for API clients, with number, timestamp, gas usage, producer, and original JSON-RPC data under `meta.rpc`.'));
134
+ /**
135
+ * A lightweight block row, returned by the `/blocks` list endpoint. Full
136
+ * fields (parentHash, miner, gasUsed/gasLimit, …) would require one RPC
137
+ * roundtrip per row; the list endpoint stays cheap and clients fetch
138
+ * `/blocks/:block` for individual blocks they care about. The block hash is
139
+ * selected from the index so `id` stays tied to the resolved block identity.
140
+ */
141
+ schema.BlockSummary = z
142
+ .object({
143
+ hash: Schema.Hash.check(z.describe('A 32-byte hash, `0x`-prefixed and returned in lowercase, that identifies this block.')),
144
+ id: Schema.Hash.check(z.describe('Stable resource ID for this API response; it is the block hash.')),
145
+ number: z
146
+ .number()
147
+ .check(z.int(), z.nonnegative(), z.describe('The block height, starting from genesis block 0.'), z.meta({ examples: [23456789] })),
148
+ timestamp: z.iso
149
+ .datetime()
150
+ .check(z.describe('Block timestamp formatted as ISO 8601.'), z.meta({ examples: ['2024-01-01T00:00:00Z'] })),
151
+ transactionCount: z
152
+ .number()
153
+ .check(z.int(), z.nonnegative(), z.describe('Number of transactions included in this block.'), z.meta({ examples: [25] })),
154
+ })
155
+ .check(z.describe('A lightweight block summary for list responses.'));
156
+ /** Schemas for the getBlocks operation. */
157
+ let getBlocks;
158
+ (function (getBlocks) {
159
+ /** Query parameters for `GET /blocks`. */
160
+ getBlocks.Query = z
161
+ .strictObject({
162
+ chainId: Schema.ChainIdQuery,
163
+ cursor: Schema.Cursor,
164
+ limit: Schema.Limit,
165
+ order: Schema.Order,
166
+ page: Schema.Page,
167
+ })
168
+ .check(...Schema.pageChecks(), z.describe('Query parameters for listing blocks.'));
169
+ /** Page of lightweight block rows, including empty blocks. */
170
+ getBlocks.Response = z
171
+ .object({
172
+ data: z.array(schema.BlockSummary).check(z.describe('Blocks on this page.')),
173
+ nextCursor: Schema.NextCursor,
174
+ })
175
+ .check(z.describe('A page of blocks ordered by block number.'));
176
+ })(getBlocks = schema.getBlocks || (schema.getBlocks = {}));
177
+ /** Schemas for the getBlock operation. */
178
+ let getBlock;
179
+ (function (getBlock) {
180
+ /**
181
+ * Path parameter for block requests. Resolves a single block from any of:
182
+ * a block tag (`latest`, `finalized`); a decimal or `0x`-prefixed hex block
183
+ * number; a `0x`-prefixed 32-byte block hash; or an ISO 8601 timestamp
184
+ * (`2023-11-14T22:13:20Z`), which resolves to the first block at or after
185
+ * that instant.
186
+ */
187
+ getBlock.Params = z
188
+ .object({
189
+ block: z
190
+ .union([
191
+ // Block tag (`latest`, `finalized`).
192
+ z.enum(blockTags),
193
+ // Block number (decimal or hex) or a 0x-prefixed 32-byte hash.
194
+ z.string().check(z.regex(/^(0x[0-9a-fA-F]+|[0-9]+)$/)),
195
+ // ISO 8601 timestamp, resolved to the first block at or after it.
196
+ blockTimestamp,
197
+ ])
198
+ .check(z.describe('Block selector: `latest`, `finalized`, a decimal or hex block number, a 32-byte block hash, or an ISO 8601 timestamp.'), z.meta({ examples: [exampleBlockNumber, 'latest', '2023-11-14T22:13:20Z'] })),
199
+ })
200
+ .check(z.describe('Path parameter used to select a block.'));
201
+ /** Query parameters for `GET /blocks/:block`. */
202
+ getBlock.Query = z
203
+ .strictObject({
204
+ chainId: Schema.ChainIdQuery,
205
+ })
206
+ .check(z.describe('Query parameters for looking up a block.'));
207
+ /** A humanized block, with the verbatim JSON-RPC payload under `meta.rpc`. */
208
+ getBlock.Response = schema.Block;
209
+ })(getBlock = schema.getBlock || (schema.getBlock = {}));
210
+ /**
211
+ * Schemas for the legacy `getBlockByTimestamp` compatibility operation
212
+ * (`GET /chains/:chainId/blocks/by-timestamp`). Hidden from the docs; kept to
213
+ * keep pre-existing clients of the prior Tempo API working unchanged.
214
+ */
215
+ let getBlockByTimestamp;
216
+ (function (getBlockByTimestamp) {
217
+ /** Path parameters: the numeric Tempo chain id. */
218
+ getBlockByTimestamp.Params = z
219
+ .object({ chainId: Schema.ChainId })
220
+ .check(z.describe('Path parameters for the legacy block-by-timestamp lookup.'));
221
+ /** Query parameters: a required unix timestamp and the search direction. */
222
+ getBlockByTimestamp.Query = z
223
+ .strictObject({
224
+ closest: z
225
+ ._default(z.enum(['before', 'after']), 'before')
226
+ .check(z.describe('Choose whether the closest block should be at or before the timestamp, or at or after it.')),
227
+ timestamp: z.coerce
228
+ .number()
229
+ .check(z.int(), z.nonnegative(), z.describe('Unix timestamp in seconds.')),
230
+ })
231
+ .check(z.describe('Query parameters for the legacy block-by-timestamp lookup.'));
232
+ /** The legacy response envelope: `{ block: { blockNumber, blockTimestamp } }`. */
233
+ getBlockByTimestamp.Response = z
234
+ .object({
235
+ block: z
236
+ .object({
237
+ blockNumber: z
238
+ .number()
239
+ .check(z.int(), z.nonnegative(), z.describe('The block height, starting from genesis block 0.')),
240
+ blockTimestamp: z
241
+ .number()
242
+ .check(z.int(), z.nonnegative(), z.describe('Block timestamp as Unix seconds.')),
243
+ })
244
+ .check(z.describe('The closest indexed block found for the requested timestamp.')),
245
+ })
246
+ .check(z.describe('The closest indexed block for the supplied timestamp.'));
247
+ })(getBlockByTimestamp = schema.getBlockByTimestamp || (schema.getBlockByTimestamp = {}));
248
+ })(schema || (schema = {}));
249
+ /** Window, in seconds, searched on either side of a `by-timestamp` lookup. */
250
+ const byTimestampWindowSeconds = 60;
251
+ /** Creates block handlers. */
252
+ export function blocks() {
253
+ // Only cursor pages of the block list are cacheable: they are anchored below
254
+ // the head and effectively immutable. The head page is a point-in-time read
255
+ // of the live chain tip, so it skips the cache entirely and carries no
256
+ // `Cache-Control` header at all.
257
+ const cacheBlocksList = Cache.response({
258
+ cacheControl: Cache.policies.immutable,
259
+ name: 'tempo-api:blocks:v1',
260
+ key: (c) => Cache.urlKey(c, schema.getBlocks.Query),
261
+ });
262
+ const cacheBlocksListIfCursor = (c, next) => c.req.query('cursor') === undefined ? next() : cacheBlocksList(c, next);
263
+ return new Hono()
264
+ .get('/', Auth.policy({ apiKey: { scopes: ['data:read'] }, mpp: true, public: true }), OpenApi.validate('query', schema.getBlocks.Query, {
265
+ code: 'query_invalid',
266
+ message: 'Invalid query parameters',
267
+ }), OpenApi.describeRoute({
268
+ description: 'List the ordered batches of transactions that make up Tempo.',
269
+ operationId: 'getBlocks',
270
+ responses: OpenApi.responses({
271
+ errors: { 502: 'Could not read indexed block data from TIDX.' },
272
+ success: { description: 'A page of block summaries.', schema: schema.getBlocks.Response },
273
+ }),
274
+ summary: 'List blocks',
275
+ tags: ['Blocks'],
276
+ }), cacheBlocksListIfCursor, async (c) => {
277
+ if (Auth.narrowAccess)
278
+ return Auth.paidAccessError(c);
279
+ if (OpenApi.narrowValidation)
280
+ return OpenApi.validationError(c, {
281
+ code: 'query_invalid',
282
+ message: 'Invalid query parameters',
283
+ });
284
+ const query = c.req.valid('query');
285
+ const chainId = query.chainId ?? c.get('chainId');
286
+ try {
287
+ const page = await listBlocks(c, {
288
+ chainId,
289
+ cursor: query.cursor,
290
+ limit: query.limit,
291
+ order: query.order,
292
+ page: query.page,
293
+ });
294
+ return c.json(Response.validated(schema.getBlocks.Response, page), 200);
295
+ }
296
+ catch (cause) {
297
+ return Response.upstream(c, cause);
298
+ }
299
+ })
300
+ .get('/:block', Auth.policy({ apiKey: { scopes: ['data:read'] }, mpp: true, public: true }), OpenApi.validate('param', schema.getBlock.Params, {
301
+ code: 'block_invalid',
302
+ message: 'Invalid block identifier',
303
+ }), OpenApi.validate('query', schema.getBlock.Query, {
304
+ code: 'query_invalid',
305
+ message: 'Invalid query parameters',
306
+ }), OpenApi.describeRoute({
307
+ description: 'Get one block by tag, number, hash, or timestamp selector.',
308
+ operationId: 'getBlock',
309
+ responses: OpenApi.responses({
310
+ errors: {
311
+ 404: {
312
+ description: 'No block matched the selector, or no indexed block exists at or after the timestamp.',
313
+ codes: ['block_not_found'],
314
+ },
315
+ 502: 'Could not read block data from the upstream JSON-RPC node or TIDX.',
316
+ },
317
+ success: {
318
+ description: 'A single block with number, timestamp, gas usage, producer, and original JSON-RPC data.',
319
+ schema: schema.Block,
320
+ },
321
+ }),
322
+ summary: 'Get a block by selector',
323
+ tags: ['Blocks'],
324
+ }), Cache.response({
325
+ // Default to no-store so a 404 cannot mask the eventual mined block.
326
+ // The handler upgrades a mined block to `immutable` below.
327
+ cacheControl: Cache.policies.noStore,
328
+ name: 'tempo-api:blocks:v1',
329
+ key: (c) => Cache.urlKey(c, schema.getBlock.Query),
330
+ }), async (c) => {
331
+ if (Auth.narrowAccess)
332
+ return Auth.paidAccessError(c);
333
+ if (OpenApi.narrowValidation)
334
+ return OpenApi.validationError(c, {
335
+ code: 'block_invalid',
336
+ message: 'Invalid request parameters',
337
+ });
338
+ if (OpenApi.narrowValidation)
339
+ return OpenApi.validationError(c, {
340
+ code: 'query_invalid',
341
+ message: 'Invalid request parameters',
342
+ });
343
+ const { block } = c.req.valid('param');
344
+ const query = c.req.valid('query');
345
+ const chainId = query.chainId ?? c.get('chainId');
346
+ const client = c.get('getClient')(chainId);
347
+ const identifier = parseBlockIdentifier(block);
348
+ if (!identifier)
349
+ return Response.error(c, {
350
+ code: 'block_invalid',
351
+ message: 'Invalid block identifier',
352
+ status: 400,
353
+ });
354
+ try {
355
+ // A timestamp selector resolves to a concrete block number via the
356
+ // indexer first (the `blocks` table records every block, empty ones
357
+ // included, so this is exact rather than rounding to the next
358
+ // non-empty block). A distinct 404 separates "no indexed block at or
359
+ // after this instant" from a missing block by number/hash.
360
+ let timestampBlock;
361
+ if (identifier.kind === 'timestamp') {
362
+ const tidx = c.get('getTidx')(chainId);
363
+ const store = c.get('store');
364
+ const iso = identifier.iso;
365
+ timestampBlock = await Timing.time(c, 'block_by_timestamp', () => Store.memoize(async () => {
366
+ const result = await tidx.fetch({
367
+ chainId,
368
+ query: `
369
+ SELECT num
370
+ FROM blocks
371
+ WHERE timestamp >= '${iso}'
372
+ ORDER BY num ASC
373
+ LIMIT 1
374
+ `,
375
+ });
376
+ const row = result.rows[0];
377
+ if (!row)
378
+ return undefined;
379
+ return Value.toNumber(row['num']);
380
+ }, {
381
+ key: `blocks:v1:${chainId}:by_timestamp:${iso}`,
382
+ store,
383
+ ttl: Ttl.seconds(30),
384
+ }));
385
+ if (timestampBlock === undefined)
386
+ return Response.error(c, {
387
+ code: 'block_not_found',
388
+ message: 'No indexed block at or after this timestamp',
389
+ status: 404,
390
+ });
391
+ }
392
+ // Dispatch by selector shape: a 32-byte hash uses
393
+ // `eth_getBlockByHash`; tags, numbers, and the timestamp-resolved
394
+ // number all go through `eth_getBlockByNumber`. The moving tags
395
+ // (`latest`/`finalized`) are deliberately never cached — not even
396
+ // briefly — because they are point-in-time reads of the live tip.
397
+ const rpc = await Timing.time(c, 'block', () => {
398
+ if (identifier.kind === 'hash')
399
+ return client.request({
400
+ method: 'eth_getBlockByHash',
401
+ params: [identifier.hash, false],
402
+ });
403
+ const block = identifier.kind === 'tag'
404
+ ? identifier.tag
405
+ : identifier.kind === 'number'
406
+ ? Hex.fromNumber(identifier.number)
407
+ : Hex.fromNumber(BigInt(timestampBlock));
408
+ return client.request({ method: 'eth_getBlockByNumber', params: [block, false] });
409
+ });
410
+ if (!rpc)
411
+ return Response.error(c, {
412
+ code: 'block_not_found',
413
+ message: 'Block not found',
414
+ status: 404,
415
+ });
416
+ // Cache policy by selector: a specific block (number or hash) is
417
+ // immutable; a timestamp can land on the moving head or a historic
418
+ // block, so `state` is the safe middle; the moving tags (`latest`,
419
+ // `finalized`) keep the route's no-store default.
420
+ if (identifier.kind === 'number' || identifier.kind === 'hash')
421
+ Cache.setPolicy(c, Cache.policies.immutable);
422
+ else if (identifier.kind === 'timestamp')
423
+ Cache.setPolicy(c, Cache.policies.state);
424
+ const data = humanizeBlock(Response.validated(schema.Rpc.Block, rpc));
425
+ return c.json(Response.validated(schema.Block, data), 200);
426
+ }
427
+ catch (cause) {
428
+ return Response.upstream(c, cause);
429
+ }
430
+ });
431
+ }
432
+ /**
433
+ * Mounts the hidden legacy `GET /chains/:chainId/blocks/by-timestamp`
434
+ * compatibility route at the app root. The prior Tempo API exposed this shape
435
+ * (chain id in the path, a required unix `timestamp`, and a `closest` direction)
436
+ * and returned `{ block: { blockNumber, blockTimestamp } }`; this preserves it
437
+ * so existing clients keep working while the canonical surface is
438
+ * `GET /v1/blocks/:block` with an ISO 8601 selector.
439
+ *
440
+ * Like the original it searches a ±{@link byTimestampWindowSeconds}s window and
441
+ * returns `404` when no indexed block falls within it. Because the chain id is a
442
+ * path parameter (not the `chainId` query the global guard inspects), this
443
+ * validates it against the deployment's supported chains itself.
444
+ */
445
+ export function blocksByTimestampCompat() {
446
+ return new Hono().get('/:chainId/blocks/by-timestamp', Auth.policy({ apiKey: { scopes: ['data:read'] }, mpp: true, public: true }), OpenApi.validate('param', schema.getBlockByTimestamp.Params, {
447
+ code: 'chain_id_invalid',
448
+ message: 'Invalid chain id',
449
+ }), OpenApi.validate('query', schema.getBlockByTimestamp.Query, {
450
+ code: 'query_invalid',
451
+ message: 'Invalid query parameters',
452
+ }), OpenApi.describeRoute({
453
+ hide: true,
454
+ operationId: 'getBlockByTimestamp',
455
+ responses: OpenApi.responses({
456
+ errors: {
457
+ 404: {
458
+ description: 'No indexed block was found within the timestamp search window.',
459
+ codes: ['block_not_found'],
460
+ },
461
+ 502: 'Could not read indexed block data from TIDX.',
462
+ },
463
+ success: {
464
+ description: 'The closest indexed block for the supplied timestamp.',
465
+ schema: schema.getBlockByTimestamp.Response,
466
+ },
467
+ }),
468
+ summary: 'Get a legacy block by timestamp',
469
+ tags: ['Blocks'],
470
+ }), Cache.response({
471
+ cacheControl: Cache.policies.state,
472
+ name: 'tempo-api:blocks:v1',
473
+ key: (c) => Cache.urlKey(c, schema.getBlockByTimestamp.Query),
474
+ }), async (c) => {
475
+ if (Auth.narrowAccess)
476
+ return Auth.paidAccessError(c);
477
+ if (OpenApi.narrowValidation)
478
+ return OpenApi.validationError(c, {
479
+ code: 'query_invalid',
480
+ message: 'Invalid request parameters',
481
+ });
482
+ const { chainId } = c.req.valid('param');
483
+ const { closest, timestamp } = c.req.valid('query');
484
+ // The global unsupported-chain guard only inspects the `chainId` query, so
485
+ // reject an unserved chain here before it reaches the indexer.
486
+ const supportedChainIds = c.get('supportedChainIds');
487
+ if (!supportedChainIds.has(chainId))
488
+ return Response.unsupportedChainId(c, chainId, supportedChainIds);
489
+ try {
490
+ const block = await findBlockByTimestamp(c, { chainId, closest, timestamp });
491
+ if (!block)
492
+ return Response.error(c, {
493
+ code: 'block_not_found',
494
+ message: 'No indexed block matches the supplied timestamp',
495
+ status: 404,
496
+ });
497
+ return c.json(Response.validated(schema.getBlockByTimestamp.Response, { block }), 200);
498
+ }
499
+ catch (cause) {
500
+ return Response.upstream(c, cause);
501
+ }
502
+ });
503
+ }
504
+ /**
505
+ * Resolves the indexed block closest to `timestamp` within a
506
+ * ±{@link byTimestampWindowSeconds}s window, in the requested direction. Reads
507
+ * the `blocks` table (every block, empty ones included) so the match is exact.
508
+ */
509
+ function findBlockByTimestamp(c, options) {
510
+ const { chainId, closest, timestamp } = options;
511
+ const store = c.get('store');
512
+ const tidx = c.get('getTidx')(chainId);
513
+ const at = new Date(timestamp * 1000).toISOString();
514
+ const bound = new Date((closest === 'before'
515
+ ? timestamp - byTimestampWindowSeconds
516
+ : timestamp + byTimestampWindowSeconds) * 1000).toISOString();
517
+ return Timing.time(c, 'block_by_timestamp', () => Store.memoize(async () => {
518
+ // `before`: latest block in [at - window, at]; `after`: earliest block
519
+ // in [at, at + window]. Order on timestamp then `num` so ties resolve to
520
+ // the block nearest the requested instant.
521
+ const where = closest === 'before'
522
+ ? `timestamp <= '${at}' AND timestamp >= '${bound}'`
523
+ : `timestamp >= '${at}' AND timestamp <= '${bound}'`;
524
+ const direction = closest === 'before' ? 'DESC' : 'ASC';
525
+ const result = await tidx.fetch({
526
+ chainId,
527
+ query: `
528
+ SELECT num, timestamp
529
+ FROM blocks
530
+ WHERE ${where}
531
+ ORDER BY timestamp ${direction}, num ${direction}
532
+ LIMIT 1
533
+ `,
534
+ });
535
+ const row = result.rows[0];
536
+ if (!row)
537
+ return undefined;
538
+ const blockNumber = Value.toNumber(row['num']);
539
+ const iso = Value.toIsoDateTime(row['timestamp']);
540
+ if (blockNumber === undefined || iso === undefined)
541
+ return undefined;
542
+ return { blockNumber, blockTimestamp: Math.floor(Date.parse(iso) / 1000) };
543
+ }, {
544
+ key: `blocks:v1:${chainId}:by_timestamp_compat:${closest}:${timestamp}`,
545
+ store,
546
+ ttl: Ttl.seconds(30),
547
+ }));
548
+ }
549
+ /**
550
+ * Parses a `:block` path parameter, dispatching by shape. Returns the
551
+ * discriminated identifier, or `undefined` when the input matches no known
552
+ * shape (the schema should have caught this earlier; this guards the type at
553
+ * the use site).
554
+ *
555
+ * Selectors are unambiguous by shape: a block tag is a fixed keyword; the
556
+ * 66-char `0x`-prefixed length identifies a 32-byte hash; any other
557
+ * `0x`-prefixed hex (or plain decimal) is a block number; and an ISO 8601
558
+ * timestamp (validated with the same {@link blockTimestamp} schema as the
559
+ * route) is resolved to its UTC instant.
560
+ */
561
+ export function parseBlockIdentifier(value) {
562
+ if (blockTags.includes(value))
563
+ return { kind: 'tag', tag: value };
564
+ if (/^0x[0-9a-fA-F]{64}$/.test(value))
565
+ return { kind: 'hash', hash: value.toLowerCase() };
566
+ if (/^0x[0-9a-fA-F]+$/.test(value))
567
+ return { kind: 'number', number: BigInt(value) };
568
+ if (/^[0-9]+$/.test(value))
569
+ return { kind: 'number', number: BigInt(value) };
570
+ if (blockTimestamp.safeParse(value).success)
571
+ return { kind: 'timestamp', iso: new Date(Date.parse(value)).toISOString() };
572
+ return undefined;
573
+ }
574
+ /**
575
+ * Lists blocks from the indexer. Reads every block from the `blocks` table
576
+ * (so empty blocks are included) and resolves transaction counts via a
577
+ * second narrow `txs` query keyed to the page's block numbers.
578
+ */
579
+ export function listBlocks(c, options) {
580
+ const { chainId, limit, order } = options;
581
+ const store = c.get('store');
582
+ const tidx = c.get('getTidx')(chainId);
583
+ const sortDirection = order === 'asc' ? 'ASC' : 'DESC';
584
+ // Single-column keyset cursor on `num` — block timestamps aren't strictly
585
+ // monotonic on every chain, so the block number alone is the stable order.
586
+ const cursor = options.cursor ? Cursor.decode(options.cursor, ['int']) : undefined;
587
+ // Bounded positional lane (exclusive with `cursor` at the schema): page 1 is
588
+ // the head page, so it shares the head's SQL and cache entries; deeper pages
589
+ // translate to a SQL OFFSET of `(page - 1) * limit` rows.
590
+ const offset = options.page !== undefined && options.page > 1 ? (options.page - 1) * limit : undefined;
591
+ return Timing.time(c, 'blocks', () => Store.memoize(async () => {
592
+ const filters = [];
593
+ if (cursor !== undefined)
594
+ filters.push(Cursor.keyset([{ literal: Cursor.literal(cursor[0], 'int'), name: 'num', order }]));
595
+ const where = filters.length > 0 ? `WHERE ${filters.join(' AND ')}` : '';
596
+ const blocks = await tidx.fetch({
597
+ chainId,
598
+ query: `
599
+ SELECT hash, num, timestamp
600
+ FROM blocks
601
+ ${where}
602
+ ORDER BY num ${sortDirection}
603
+ LIMIT ${limit + 1}${offset !== undefined ? ` OFFSET ${offset}` : ''}
604
+ `,
605
+ });
606
+ const page = Cursor.paginate({
607
+ rows: blocks.rows,
608
+ limit,
609
+ key: (row) => {
610
+ const block = Value.toNumber(row['num']);
611
+ return block !== undefined ? [block] : undefined;
612
+ },
613
+ });
614
+ // Pull transaction counts in a single follow-up query scoped to the
615
+ // page's block numbers so empty blocks correctly report `0`.
616
+ const numbers = [];
617
+ for (const row of page.rows) {
618
+ const block = Value.toNumber(row['num']);
619
+ if (block !== undefined)
620
+ numbers.push(block);
621
+ }
622
+ const countsByBlock = new Map();
623
+ if (numbers.length > 0) {
624
+ const counts = await tidx.fetch({
625
+ chainId,
626
+ engine: 'clickhouse',
627
+ query: `
628
+ SELECT block_num, count() AS transaction_count
629
+ FROM txs
630
+ WHERE block_num IN (${numbers.join(', ')})
631
+ GROUP BY block_num
632
+ `,
633
+ });
634
+ for (const row of counts.rows) {
635
+ const block = Value.toNumber(row['block_num']);
636
+ const count = Value.toNumber(row['transaction_count']);
637
+ if (block !== undefined && count !== undefined)
638
+ countsByBlock.set(block, count);
639
+ }
640
+ }
641
+ const data = [];
642
+ for (const row of page.rows) {
643
+ const hash = Value.toText(row['hash'])?.toLowerCase();
644
+ const number = Value.toNumber(row['num']);
645
+ const timestamp = Value.toIsoDateTime(row['timestamp']);
646
+ if (hash === undefined || number === undefined || timestamp === undefined)
647
+ continue;
648
+ data.push({
649
+ hash,
650
+ id: hash,
651
+ number,
652
+ timestamp,
653
+ transactionCount: countsByBlock.get(number) ?? 0,
654
+ });
655
+ }
656
+ return { data, nextCursor: page.nextCursor };
657
+ }, {
658
+ key: `blocks:v1:${chainId}:${order}:${cursor ? `cursor:${cursor[0]}` : offset !== undefined ? `page:${options.page}` : 'head'}:${limit}`,
659
+ store,
660
+ ttl: Ttl.seconds(15),
661
+ }));
662
+ }
663
+ /**
664
+ * Humanizes a JSON-RPC block: converts hex quantities to decimal strings, hex
665
+ * block fields to numbers, and timestamps to ISO 8601, while preserving the
666
+ * verbatim payload under `meta.rpc`. Transactions are surfaced as a count;
667
+ * full transaction objects live on `GET /transactions` (equal `blockNumber.*` bounds).
668
+ */
669
+ function humanizeBlock(rpc) {
670
+ // A non-pending block always has `number`, `hash`, and `timestamp`. The
671
+ // pending case is uninteresting for this surface (the latest endpoint asks
672
+ // for `'latest'`, which is mined), so these are treated as required after
673
+ // the upstream call returns a non-null result.
674
+ return {
675
+ baseFeePerGas: Value.fromHex(rpc.baseFeePerGas) ?? null,
676
+ gasLimit: Value.hexToNumber(rpc.gasLimit) ?? 0,
677
+ gasUsed: Value.hexToNumber(rpc.gasUsed) ?? 0,
678
+ hash: rpc.hash,
679
+ id: rpc.hash,
680
+ meta: { rpc },
681
+ miner: rpc.miner,
682
+ number: Value.hexToNumber(rpc.number) ?? 0,
683
+ parentHash: rpc.parentHash,
684
+ size: Value.hexToNumber(rpc.size) ?? 0,
685
+ timestamp: Value.hexSecondsToIso(rpc.timestamp) ?? new Date(0).toISOString(),
686
+ transactionCount: rpc.transactions.length,
687
+ };
688
+ }
689
+ //# sourceMappingURL=blocks.js.map