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,874 @@
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
+ import * as Tokens from './tokens.js';
15
+ // Receipts and transactions deliberately form an import cycle (receipts reuses
16
+ // the transaction-type decoder and fee-token embedder; transactions embeds
17
+ // receipts). Neither module may touch the other's exports during module
18
+ // evaluation: schema references across the cycle must stay inside `z.lazy`, and
19
+ // function references are call-time only.
20
+ import { decodeTransactionType, embedFeeTokens, schema as txSchema } from './transactions.js';
21
+ /** Example transaction hash surfaced in OpenAPI docs. */
22
+ const exampleHash = '0x515801d7f9a5ac705e793e85904c9c69b3f1694b465cc8fb6ba3f0298dc82665';
23
+ // Tempo gas prices are denominated in attodollars (10^-18 USD) while fee-token
24
+ // balances use 6 decimals (microdollars, 10^-6 USD), so the fee charged to the
25
+ // fee payer — in the fee token's base units — is
26
+ // `ceil(gasUsed * effectiveGasPrice / 10^12)` (ceiling division). Mirrors
27
+ // `calc_gas_balance_spending` in tempoxyz/tempo.
28
+ const feeScalingFactor = 10n ** 12n;
29
+ /**
30
+ * Computes the fee charged to the fee payer, in the fee token's base units, from
31
+ * the receipt's `gasUsed` and decimal `effectiveGasPrice`. Returns undefined
32
+ * when either input is missing.
33
+ */
34
+ function computeFeeAmount(gasUsed, effectiveGasPrice) {
35
+ if (gasUsed === undefined || effectiveGasPrice === undefined)
36
+ return undefined;
37
+ const product = BigInt(gasUsed) * BigInt(effectiveGasPrice);
38
+ return ((product + feeScalingFactor - 1n) / feeScalingFactor).toString();
39
+ }
40
+ /** Zod schemas owned by the receipt handlers. */
41
+ export var schema;
42
+ (function (schema) {
43
+ // TODO(ox/zod): replace with the zod schema for `ox`'s `Log.Rpc` once ox ships
44
+ // a `zod` entrypoint.
45
+ /** One event log emitted by a transaction (RPC format). */
46
+ schema.Log = z
47
+ .looseObject({
48
+ address: Schema.Address.check(z.describe('The contract address that emitted this event log.')),
49
+ blockHash: z
50
+ .nullable(Schema.Hash)
51
+ .check(z.describe('The block hash once included, or `null` while pending.')),
52
+ blockNumber: z
53
+ .nullable(Schema.Quantity)
54
+ .check(z.describe('The block number once included, or `null` while pending.')),
55
+ blockTimestamp: z
56
+ .nullish(Schema.Quantity)
57
+ .check(z.describe('Tempo-provided block timestamp for this log.')),
58
+ data: Schema.Hex.check(z.describe('ABI-encoded data for the event parameters that are not indexed.'), z.meta({ examples: ['0xdeadbeef'] })),
59
+ logIndex: z
60
+ .nullable(Schema.Quantity)
61
+ .check(z.describe('The log position within the block, or `null` while pending.')),
62
+ removed: z
63
+ .boolean()
64
+ .check(z.describe('Whether this log was removed by a chain reorganization; Tempo finality means finalized logs do not reorg.'), z.meta({ examples: [false] })),
65
+ topics: z
66
+ .array(Schema.Hash)
67
+ .check(z.describe('Indexed event topics, including the event signature hash as the first topic.')),
68
+ transactionHash: z
69
+ .nullable(Schema.Hash)
70
+ .check(z.describe('The transaction hash for this log, or `null` while pending.')),
71
+ transactionIndex: z
72
+ .nullable(Schema.Quantity)
73
+ .check(z.describe('The transaction position within the block, or `null` while pending.')),
74
+ })
75
+ .check(z.describe('One event log emitted by a contract during transaction execution.'));
76
+ /** The verbatim JSON-RPC payloads, surfaced under each resource's `rpc` field. */
77
+ let Rpc;
78
+ (function (Rpc) {
79
+ // TODO(ox/zod): replace with the zod schema for `ox/tempo`'s
80
+ // `TransactionReceipt.Rpc` once ox ships a `zod` entrypoint. `blockHash`,
81
+ // `logsBloom`, and `blockTimestamp` are nullish/optional so the receipt
82
+ // *list* (reconstructed from the indexer, which stores neither block hash nor
83
+ // bloom but does carry the block timestamp) reuses this same shape; the
84
+ // detail endpoint's verbatim RPC receipt populates all of them.
85
+ /** A JSON-RPC transaction receipt, passed through verbatim. */
86
+ Rpc.Receipt = z
87
+ .looseObject({
88
+ blobGasPrice: z
89
+ .optional(Schema.Quantity)
90
+ .check(z.describe('Blob gas price for EIP-4844 blob data.')),
91
+ blobGasUsed: z
92
+ .optional(Schema.Quantity)
93
+ .check(z.describe('Blob gas used by EIP-4844 blob data.')),
94
+ blockHash: z
95
+ .nullable(Schema.Hash)
96
+ .check(z.describe('The block hash, or `null` for receipts reconstructed from the index.')),
97
+ blockNumber: Schema.Quantity.check(z.describe('The block number that included this receipt.')),
98
+ blockTimestamp: z
99
+ .nullish(Schema.Quantity)
100
+ .check(z.describe('Block timestamp added when this receipt is reconstructed from indexed data.')),
101
+ contractAddress: z
102
+ .nullish(Schema.Address)
103
+ .check(z.describe('The created contract address, or `null` if the transaction did not create a contract.')),
104
+ cumulativeGasUsed: Schema.Quantity.check(z.describe('Total gas used in the block up to and including this transaction.')),
105
+ effectiveGasPrice: Schema.Quantity.check(z.describe('Effective gas price paid for this transaction.')),
106
+ feePayer: z
107
+ .nullish(Schema.Address)
108
+ .check(z.describe('The address that paid the transaction fee.')),
109
+ feeToken: z
110
+ .nullish(Schema.Address)
111
+ .check(z.describe('The token used to pay the fee; on Tempo this is a USD stablecoin such as `pathUSD`.')),
112
+ from: Schema.Address.check(z.describe('The address that sent or authorized the transaction.')),
113
+ gasUsed: Schema.Quantity.check(z.describe('Gas actually used to execute the transaction.')),
114
+ logs: z
115
+ .array(schema.Log)
116
+ .check(z.describe('Event logs emitted by contracts while this transaction executed.')),
117
+ logsBloom: z
118
+ .optional(Schema.Hex)
119
+ .check(z.describe('Bloom filter summarizing the logs in this receipt.'), z.meta({ examples: ['0x00'] })),
120
+ root: z.optional(Schema.Hex).check(z.describe('Post-transaction state root used by pre-Byzantium Ethereum receipts.'), z.meta({
121
+ examples: ['0x515801d7f9a5ac705e793e85904c9c69b3f1694b465cc8fb6ba3f0298dc82665'],
122
+ })),
123
+ status: Schema.Quantity.check(z.describe('Execution status as a hex quantity: `0x1` means success and `0x0` means reverted.'), z.meta({ examples: ['0x1'] })),
124
+ to: z
125
+ .nullable(Schema.Address)
126
+ .check(z.describe('The recipient address, or `null` when the transaction created a contract.')),
127
+ transactionHash: Schema.Hash.check(z.describe('A 32-byte hash, `0x`-prefixed and returned in lowercase, that identifies the transaction.')),
128
+ transactionIndex: Schema.Quantity.check(z.describe('The transaction position within its block.')),
129
+ type: Schema.Hex.check(z.describe('Raw transaction type byte, such as `0x2` for EIP-1559 or `0x76` for Tempo.'), z.meta({ examples: ['0x2'] })),
130
+ })
131
+ .check(z.describe('The transaction receipt exactly as returned by JSON-RPC.'));
132
+ })(Rpc = schema.Rpc || (schema.Rpc = {}));
133
+ /**
134
+ * Metadata for a receipt: the verbatim JSON-RPC receipt (always present) plus
135
+ * resources embedded on demand via `include`.
136
+ */
137
+ schema.ReceiptMeta = z
138
+ .object({
139
+ feeToken: z
140
+ .optional(z.lazy(() => Tokens.schema.getToken.Response))
141
+ .check(z.describe('Fee-token details included only when you request `include=feeToken`.')),
142
+ rpc: Rpc.Receipt.check(z.describe('The original JSON-RPC receipt payload.')),
143
+ })
144
+ .check(z.describe('Metadata for this receipt: the original JSON-RPC payload plus any requested `include` resources.'));
145
+ /**
146
+ * A humanized transaction receipt: decoded `status`/`type`, plain-number gas
147
+ * and block fields, and decimal-string fee/price values. The raw event logs
148
+ * are surfaced verbatim under `logs`, and the full JSON-RPC receipt under
149
+ * `meta.rpc`.
150
+ */
151
+ schema.Receipt = z
152
+ .object({
153
+ blockHash: z
154
+ .nullable(Schema.Hash)
155
+ .check(z.describe('The block hash, or `null` when the receipt was reconstructed from indexed data.')),
156
+ blockNumber: z
157
+ .number()
158
+ .check(z.int(), z.nonnegative(), z.describe('The block number that included this receipt.'), z.meta({ examples: [23456789] })),
159
+ contractAddress: z
160
+ .nullable(Schema.Address)
161
+ .check(z.describe('The created contract address, or `null` if the transaction did not create a contract.')),
162
+ cumulativeGasUsed: z
163
+ .number()
164
+ .check(z.int(), z.nonnegative(), z.describe('Total gas used in the block up to and including this transaction.'), z.meta({ examples: [21000] })),
165
+ effectiveGasPrice: Schema.DecimalString.check(z.describe('Effective gas price paid for this transaction, in attodollars per gas.')),
166
+ feeAmount: Schema.DecimalString.check(z.describe('Fee charged to the fee payer, as a decimal integer string in the fee token’s base units.')),
167
+ feePayer: z
168
+ .optional(Schema.Address)
169
+ .check(z.describe('The address that paid the transaction fee.')),
170
+ feeToken: z
171
+ .optional(Schema.Address)
172
+ .check(z.describe('The token used to pay the fee; on Tempo this is a USD stablecoin such as `pathUSD`.')),
173
+ gasUsed: z
174
+ .number()
175
+ .check(z.int(), z.nonnegative(), z.describe('Gas actually used to execute the transaction.'), z.meta({ examples: [21000] })),
176
+ id: Schema.Hash.check(z.describe('Stable resource ID for this API response; it is the transaction hash.')),
177
+ logs: z
178
+ .array(schema.Log)
179
+ .check(z.describe('Event logs emitted by this transaction, returned in JSON-RPC log format.')),
180
+ meta: schema.ReceiptMeta.check(z.describe('The original JSON-RPC payload plus any resources requested with `include`.')),
181
+ recipient: z
182
+ .nullable(Schema.Address)
183
+ .check(z.describe('The recipient address, or `null` when the transaction created a contract.')),
184
+ sender: Schema.Address.check(z.describe('The address that sent or authorized the transaction.')),
185
+ status: z
186
+ .enum(['success', 'reverted'])
187
+ .check(z.describe('Whether the transaction succeeded or reverted.'), z.meta({ examples: ['success'] })),
188
+ timestamp: z
189
+ .nullable(z.iso.datetime())
190
+ .check(z.describe('The block timestamp as ISO 8601, or `null` when it is unavailable.'), z.meta({ examples: ['2024-01-01T00:00:00Z'] })),
191
+ transactionHash: Schema.Hash.check(z.describe('A 32-byte hash, `0x`-prefixed and returned in lowercase, that identifies the transaction.')),
192
+ transactionIndex: z
193
+ .number()
194
+ .check(z.int(), z.nonnegative(), z.describe('The transaction position within its block.'), z.meta({ examples: [0] })),
195
+ type: z.lazy(() => txSchema.TransactionType).check(z.meta({ examples: ['eip1559'] })),
196
+ })
197
+ .check(z.describe('A transaction outcome formatted for API clients, with status, gas, fees, logs, and the original JSON-RPC receipt under `meta.rpc`.'));
198
+ /** Schemas for the getReceipt operation. */
199
+ let getReceipt;
200
+ (function (getReceipt) {
201
+ /** Path parameters for receipt requests. */
202
+ getReceipt.Params = z
203
+ .object({
204
+ transactionHash: Schema.Hash.check(z.describe('A 32-byte hash, `0x`-prefixed and returned in lowercase, that identifies the transaction.'), z.meta({ examples: [exampleHash] })),
205
+ })
206
+ .check(z.describe('Path parameters used to look up a receipt by transaction hash.'));
207
+ /** Optional related resource that callers opt into via `include`. */
208
+ getReceipt.Include = z
209
+ .enum(['feeToken'])
210
+ .check(z.describe('Related resource you can opt into with the `include` query parameter.'));
211
+ /**
212
+ * Parses a comma-separated `include` query value into a list of optional
213
+ * resources to embed. Extra lookups (e.g. fee-token metadata) only run when
214
+ * explicitly requested, keeping the base receipt response fast.
215
+ */
216
+ getReceipt.includeQuery = Schema.includeQuery(getReceipt.Include, 'Comma-separated related resources to include, such as `feeToken`.');
217
+ /** Query parameters for receipt requests. */
218
+ getReceipt.Query = z
219
+ .strictObject({
220
+ chainId: Schema.ChainIdQuery,
221
+ include: getReceipt.includeQuery,
222
+ })
223
+ .check(z.describe('Query parameters for looking up a receipt.'));
224
+ /** A humanized receipt, with the verbatim JSON-RPC payload under `meta.rpc`. */
225
+ getReceipt.Response = schema.Receipt;
226
+ })(getReceipt = schema.getReceipt || (schema.getReceipt = {}));
227
+ /** Schemas for the getReceipts (list) operation. */
228
+ let getReceipts;
229
+ (function (getReceipts) {
230
+ /**
231
+ * Optional resources for the receipt list: the per-row `feeToken` embed
232
+ * plus the response-wide capped `totalCount`. The detail route keeps its
233
+ * own `feeToken`-only {@link getReceipt.Include}.
234
+ */
235
+ getReceipts.Include = z
236
+ .enum(['feeToken', 'totalCount'])
237
+ .check(z.describe('Related resources to include in each receipt row or in response metadata.'));
238
+ /** Parses the comma-separated `include` value for the receipt list. */
239
+ getReceipts.includeQuery = Schema.includeQuery(getReceipts.Include, 'Comma-separated related resources to include, such as `feeToken,totalCount`.');
240
+ /** Query parameters for receipt list requests. */
241
+ getReceipts.Query = z
242
+ .strictObject({
243
+ address: z
244
+ .optional(Schema.Address)
245
+ .check(z.describe('Filter to receipts whose transaction has this address as sender or recipient.')),
246
+ 'blockNumber.from': Schema.blockNumberBound('receipts', 'from'),
247
+ 'blockNumber.to': Schema.blockNumberBound('receipts', 'to'),
248
+ chainId: Schema.ChainIdQuery,
249
+ cursor: Schema.Cursor,
250
+ feePayer: z
251
+ .optional(Schema.Address)
252
+ .check(z.describe('Filter to receipts whose transaction fee was paid by this address.')),
253
+ feeToken: z
254
+ .optional(Schema.TokenAddress)
255
+ .check(z.describe('Filter to receipts whose transaction fee was paid in this token address.')),
256
+ include: getReceipts.includeQuery,
257
+ limit: Schema.Limit,
258
+ order: Schema.Order,
259
+ page: Schema.Page,
260
+ recipient: z
261
+ .optional(Schema.Address)
262
+ .check(z.describe('Filter to receipts for transactions sent to this recipient address.')),
263
+ sender: z
264
+ .optional(Schema.Address)
265
+ .check(z.describe('Filter to receipts for transactions sent by this sender address.')),
266
+ status: z
267
+ .optional(z.enum(['success', 'reverted']))
268
+ .check(z.describe('Filter to receipts with this execution status.'), z.meta({ examples: ['success'] })),
269
+ 'timestamp.from': Schema.timestampBound('receipts', 'from'),
270
+ 'timestamp.to': Schema.timestampBound('receipts', 'to'),
271
+ })
272
+ .check(...Schema.pageChecks(), z.describe('Query parameters for listing receipts.'));
273
+ /**
274
+ * Page of receipts. Each item reuses the humanized receipt returned by
275
+ * `GET /transactions/:transactionHash/receipt`, so the list and detail endpoints expose one
276
+ * identical shape. Indexed list rows are reconstructed from the indexer,
277
+ * which stores neither the block hash nor the event logs, so those fields are
278
+ * `null`/empty in the list `meta.rpc` payload.
279
+ */
280
+ getReceipts.Response = z
281
+ .object({
282
+ data: z.array(getReceipt.Response).check(z.describe('Receipts on this page.')),
283
+ meta: z
284
+ .optional(Schema.CountMeta)
285
+ .check(z.describe('Response-level metadata requested with `include`, such as `totalCount`.')),
286
+ nextCursor: Schema.NextCursor,
287
+ })
288
+ .check(z.describe('A page of receipts ordered by block number and transaction position.'));
289
+ })(getReceipts = schema.getReceipts || (schema.getReceipts = {}));
290
+ })(schema || (schema = {}));
291
+ /** Creates receipt handlers. */
292
+ export function receipts() {
293
+ return new Hono()
294
+ .get('/receipts', Auth.policy({ apiKey: { scopes: ['data:read'] }, mpp: true, public: true }), OpenApi.validate('query', schema.getReceipts.Query, {
295
+ code: 'query_invalid',
296
+ message: 'Invalid query parameters',
297
+ }), OpenApi.describeRoute({
298
+ description: 'List transaction receipts across Tempo, with filters for addresses, blocks, timestamps, fees, and status.',
299
+ operationId: 'getReceipts',
300
+ responses: OpenApi.responses({
301
+ errors: { 502: 'Could not read indexed receipt data from TIDX.' },
302
+ success: {
303
+ description: 'A page of transaction receipts.',
304
+ schema: schema.getReceipts.Response,
305
+ },
306
+ }),
307
+ summary: 'List transaction receipts',
308
+ tags: ['Transactions'],
309
+ }), Cache.response({
310
+ cacheControl: Cache.policies.feed,
311
+ name: 'tempo-api:receipts:v1',
312
+ key: (c) => Cache.urlKey(c, schema.getReceipts.Query),
313
+ }), async (c) => {
314
+ if (Auth.narrowAccess)
315
+ return Auth.paidAccessError(c);
316
+ if (OpenApi.narrowValidation)
317
+ return OpenApi.validationError(c, {
318
+ code: 'query_invalid',
319
+ message: 'Invalid query parameters',
320
+ });
321
+ const query = c.req.valid('query');
322
+ const chainId = query.chainId ?? c.get('chainId');
323
+ // Cursor pages are anchored below the head and effectively immutable, so
324
+ // cache them longer; the head page keeps the route's short `feed` default.
325
+ if (query.cursor !== undefined)
326
+ Cache.setPolicy(c, Cache.policies.immutable);
327
+ try {
328
+ // `totalCount` is opt-in and shares the page's filters; run it
329
+ // concurrently with the page. Best-effort: a count failure omits
330
+ // `meta` rather than failing the page.
331
+ const countPromise = query.include.includes('totalCount')
332
+ ? countReceipts(c, {
333
+ address: query.address,
334
+ chainId,
335
+ feePayer: query.feePayer,
336
+ feeToken: query.feeToken,
337
+ fromBlock: query['blockNumber.from'],
338
+ fromTimestamp: query['timestamp.from'],
339
+ recipient: query.recipient,
340
+ sender: query.sender,
341
+ status: query.status,
342
+ toBlock: query['blockNumber.to'],
343
+ toTimestamp: query['timestamp.to'],
344
+ }).catch(() => undefined)
345
+ : undefined;
346
+ const receipts = await listReceipts(c, {
347
+ address: query.address,
348
+ chainId,
349
+ cursor: query.cursor,
350
+ feePayer: query.feePayer,
351
+ feeToken: query.feeToken,
352
+ fromBlock: query['blockNumber.from'],
353
+ fromTimestamp: query['timestamp.from'],
354
+ include: query.include.includes('feeToken') ? ['feeToken'] : [],
355
+ limit: query.limit,
356
+ order: query.order,
357
+ page: query.page,
358
+ recipient: query.recipient,
359
+ sender: query.sender,
360
+ status: query.status,
361
+ toBlock: query['blockNumber.to'],
362
+ toTimestamp: query['timestamp.to'],
363
+ });
364
+ const meta = countPromise ? await countPromise : undefined;
365
+ return c.json(Response.validated(schema.getReceipts.Response, {
366
+ ...receipts,
367
+ ...(meta ? { meta } : {}),
368
+ }), 200);
369
+ }
370
+ catch (cause) {
371
+ return Response.upstream(c, cause);
372
+ }
373
+ })
374
+ .get('/:transactionHash/receipt', Auth.policy({ apiKey: { scopes: ['data:read'] }, mpp: true, public: true }), OpenApi.validate('param', schema.getReceipt.Params, {
375
+ code: 'transaction_invalid',
376
+ message: 'Invalid transaction hash',
377
+ }), OpenApi.validate('query', schema.getReceipt.Query, {
378
+ code: 'query_invalid',
379
+ message: 'Invalid query parameters',
380
+ }), OpenApi.describeRoute({
381
+ description: 'Get the outcome of one transaction by its transaction hash.',
382
+ operationId: 'getReceipt',
383
+ responses: OpenApi.responses({
384
+ errors: {
385
+ 404: {
386
+ description: 'No receipt was found for that transaction hash.',
387
+ codes: ['receipt_not_found'],
388
+ },
389
+ 502: 'Could not read receipt data from the upstream JSON-RPC node.',
390
+ },
391
+ success: {
392
+ description: 'A single transaction receipt with status, gas, fees, and logs.',
393
+ schema: schema.getReceipt.Response,
394
+ },
395
+ }),
396
+ summary: 'Get a transaction receipt',
397
+ tags: ['Transactions'],
398
+ }), Cache.response({
399
+ // A receipt exists only once the transaction is mined: the lookup is
400
+ // either a 404 (unknown / not-yet-mined hash) or a final 200, never a
401
+ // pending 200. Default to no-store so the 404 isn't cached with a
402
+ // long-lived header (which would mask the receipt once mined); the handler
403
+ // unconditionally upgrades the 200 to `immutable` below.
404
+ cacheControl: Cache.policies.noStore,
405
+ name: 'tempo-api:receipts:v1',
406
+ key: (c) => Cache.urlKey(c, schema.getReceipt.Query),
407
+ }), async (c) => {
408
+ if (Auth.narrowAccess)
409
+ return Auth.paidAccessError(c);
410
+ if (OpenApi.narrowValidation)
411
+ return OpenApi.validationError(c, {
412
+ code: 'transaction_invalid',
413
+ message: 'Invalid request parameters',
414
+ });
415
+ if (OpenApi.narrowValidation)
416
+ return OpenApi.validationError(c, {
417
+ code: 'query_invalid',
418
+ message: 'Invalid request parameters',
419
+ });
420
+ const { transactionHash } = c.req.valid('param');
421
+ const query = c.req.valid('query');
422
+ const chainId = query.chainId ?? c.get('chainId');
423
+ const client = c.get('getClient')(chainId);
424
+ // `Schema.Hash` lowercases to a plain string; re-narrow to the hex
425
+ // template literal viem requires at the RPC boundary.
426
+ const hash = transactionHash;
427
+ try {
428
+ // Fetch the JSON-RPC receipt; a `null` result means the receipt is
429
+ // unknown or the tx is still pending. The verbatim payload is surfaced
430
+ // under `meta.rpc`, with humanized fields layered on top. Unmined
431
+ // lookups are deliberately never cached — confirmation polling must
432
+ // observe the receipt the moment the node has it.
433
+ const rpc = await Timing.time(c, 'transaction_receipt', () => client.request({ method: 'eth_getTransactionReceipt', params: [hash] }));
434
+ if (!rpc)
435
+ return Response.error(c, {
436
+ code: 'receipt_not_found',
437
+ message: 'Transaction receipt not found',
438
+ status: 404,
439
+ });
440
+ const data = humanizeReceipt(Response.validated(schema.Rpc.Receipt, rpc));
441
+ // A receipt is only returned once the transaction is mined, so it is
442
+ // immutable and can be cached aggressively.
443
+ Cache.setPolicy(c, Cache.policies.immutable);
444
+ // Fee-token metadata is opt-in: resolving it is an extra lookup, so it
445
+ // only runs when requested via `include=feeToken` and the receipt
446
+ // declares a fee token.
447
+ const feeToken = data.feeToken;
448
+ if (!query.include.includes('feeToken') || feeToken === undefined)
449
+ return c.json(Response.validated(schema.getReceipt.Response, data), 200);
450
+ const token = await Timing.time(c, 'fee_token', () => Tokens.resolveToken(c, { address: feeToken, chainId }).catch(() => undefined));
451
+ return c.json(Response.validated(schema.getReceipt.Response, token ? { ...data, meta: { ...data.meta, feeToken: token } } : data), 200);
452
+ }
453
+ catch (cause) {
454
+ return Response.upstream(c, cause);
455
+ }
456
+ });
457
+ }
458
+ /**
459
+ * Lists humanized receipts and embeds opt-in `include` resources (currently
460
+ * `feeToken`).
461
+ */
462
+ export async function listReceipts(c, options) {
463
+ const { include, ...rest } = options;
464
+ const page = await getReceipts(c, rest);
465
+ const data = include.includes('feeToken')
466
+ ? await embedFeeTokens(c, page.data, rest.chainId)
467
+ : page.data;
468
+ return { ...page, data };
469
+ }
470
+ export function getReceipts(c, options) {
471
+ const { address, chainId, feePayer, feeToken, fromBlock, fromTimestamp, limit, order, recipient, sender, status, toBlock, toTimestamp, } = options;
472
+ const store = c.get('store');
473
+ const tidx = c.get('getTidx')(chainId);
474
+ const sortDirection = order === 'asc' ? 'ASC' : 'DESC';
475
+ // Keyset pagination: anchor the page below the previous row's `(block, tx_idx)`
476
+ // position instead of a numeric offset, so rows arriving at the head can't
477
+ // shift items across pages. A malformed cursor falls back to the head page.
478
+ const cursor = options.cursor ? Cursor.decode(options.cursor, ['int', 'int']) : undefined;
479
+ // Bounded positional lane (exclusive with `cursor` at the schema): page 1 is
480
+ // the head page, so it shares the head's SQL and cache entries; deeper pages
481
+ // translate to a SQL OFFSET of `(page - 1) * limit` rows.
482
+ const offset = options.page !== undefined && options.page > 1 ? (options.page - 1) * limit : undefined;
483
+ return Timing.time(c, 'receipts', () => Store.memoize(async () => {
484
+ // Page the `receipts` table alone, then enrich each row with the
485
+ // `type`/`fee_token` it does not store via a follow-up `txs` lookup
486
+ // (the blocks-feed pattern). Paging straight off `receipts` avoids the
487
+ // `receipts JOIN txs` shape, which TIDX's planner cannot execute under
488
+ // address scope: a `UNION` of two JOINs (or the `(from = X OR to = X)`
489
+ // form) reliably returns `db error` (HTTP 422) for high-traffic
490
+ // addresses. Two index-friendly single-table queries plan cleanly.
491
+ //
492
+ // Build the shared filters once (everything except the address-scope
493
+ // predicate, which is special-cased below).
494
+ const filters = [];
495
+ if (sender !== undefined)
496
+ filters.push(`"from" = '${sender}'`);
497
+ if (recipient !== undefined)
498
+ filters.push(`"to" = '${recipient}'`);
499
+ if (feePayer !== undefined)
500
+ filters.push(`fee_payer = '${feePayer}'`);
501
+ // `fee_token` lives on `txs`, not `receipts`; filter via a subquery so
502
+ // the page query stays single-table.
503
+ if (feeToken !== undefined)
504
+ filters.push(`tx_hash IN (SELECT hash FROM txs WHERE fee_token = '${feeToken}')`);
505
+ if (fromBlock !== undefined)
506
+ filters.push(`block_num >= ${fromBlock}`);
507
+ if (toBlock !== undefined)
508
+ filters.push(`block_num <= ${toBlock}`);
509
+ if (fromTimestamp !== undefined)
510
+ filters.push(`block_timestamp >= '${fromTimestamp}'`);
511
+ if (toTimestamp !== undefined)
512
+ filters.push(`block_timestamp <= '${toTimestamp}'`);
513
+ if (status !== undefined)
514
+ filters.push(`status = ${status === 'success' ? 1 : 0}`);
515
+ if (cursor !== undefined)
516
+ filters.push(Cursor.keyset([
517
+ { literal: Cursor.literal(cursor[0], 'int'), name: 'block_num', order },
518
+ { literal: Cursor.literal(cursor[1], 'int'), name: 'tx_idx', order },
519
+ ]));
520
+ const columns = `block_num, block_timestamp, tx_idx, tx_hash, "from", "to",
521
+ contract_address, gas_used, cumulative_gas_used, effective_gas_price,
522
+ status, fee_payer`;
523
+ // For address-scoped queries, run one branch per side and `UNION` so
524
+ // each branch returns at most `limit + 1` index-ordered rows, the outer
525
+ // query re-merges, and plain `UNION` dedupes the self-receipt case
526
+ // where `from = to = address`. The sort+limit live *inside* each branch
527
+ // so the merge stays bounded.
528
+ //
529
+ // The `receipts` table only indexes `from`, not `to` (a `to` scan over
530
+ // ~25M rows hits the indexer's statement timeout and returns `db error`,
531
+ // HTTP 422). The `from` branch filters `receipts` directly; the `to`
532
+ // branch resolves matching transaction hashes from `txs` (which indexes
533
+ // `to`) and looks them up by the indexed `receipts.tx_hash`.
534
+ const sideQuery = (side, addr) => {
535
+ const predicate = side === 'from'
536
+ ? `"from" = '${addr}'`
537
+ : `tx_hash IN (SELECT hash FROM txs WHERE "to" = '${addr}')`;
538
+ const where = [predicate, ...filters].join(' AND ');
539
+ // In the offset lane each branch over-fetches the full window
540
+ // (`offset + limit + 1` rows) because the skip applies to the merged
541
+ // set: the outer query re-sorts the union and discards the first
542
+ // `offset` rows.
543
+ return `(SELECT ${columns} FROM receipts WHERE ${where} ORDER BY block_num ${sortDirection}, tx_idx ${sortDirection} LIMIT ${(offset ?? 0) + limit + 1})`;
544
+ };
545
+ const skip = offset !== undefined ? ` OFFSET ${offset}` : '';
546
+ const query = address !== undefined
547
+ ? `
548
+ ${sideQuery('from', address)}
549
+ UNION
550
+ ${sideQuery('to', address)}
551
+ ORDER BY block_num ${sortDirection}, tx_idx ${sortDirection}
552
+ LIMIT ${limit + 1}${skip}
553
+ `
554
+ : `
555
+ SELECT ${columns}
556
+ FROM receipts
557
+ ${filters.length > 0 ? `WHERE ${filters.join(' AND ')}` : ''}
558
+ ORDER BY block_num ${sortDirection}, tx_idx ${sortDirection}
559
+ LIMIT ${limit + 1}${skip}
560
+ `;
561
+ const result = await tidx.fetch({ chainId, query: query });
562
+ // The next page anchors below the last fetched row's `(block, tx_idx)`.
563
+ const page = Cursor.paginate({
564
+ rows: result.rows,
565
+ limit,
566
+ key: (row) => {
567
+ const block = Value.toNumber(row['block_num']);
568
+ const index = Value.toNumber(row['tx_idx']);
569
+ return block !== undefined && index !== undefined ? [block, index] : undefined;
570
+ },
571
+ });
572
+ // Enrich the page with `type`/`fee_token` from `txs` in a single
573
+ // `IN (...)` lookup scoped to the page's transaction hashes. `txs` is
574
+ // sorted by `(block_num, idx)` with no hash index, so the hash list
575
+ // must be paired with the page's block numbers — a bare `hash IN`
576
+ // full-scans the table.
577
+ const hashes = [
578
+ ...new Set(page.rows
579
+ .map((row) => row['tx_hash'])
580
+ .filter((hash) => typeof hash === 'string')),
581
+ ];
582
+ const blocks = [
583
+ ...new Set(page.rows
584
+ .map((row) => Value.toNumber(row['block_num']))
585
+ .filter((block) => block !== undefined)),
586
+ ];
587
+ const txByHash = new Map();
588
+ if (hashes.length > 0 && blocks.length > 0) {
589
+ const txs = await tidx.fetch({
590
+ chainId,
591
+ query: `SELECT hash, type, fee_token FROM txs WHERE block_num IN (${blocks.join(', ')}) AND hash IN (${hashes.map((hash) => `'${hash}'`).join(', ')})`,
592
+ });
593
+ for (const row of txs.rows) {
594
+ const hash = row['hash'];
595
+ if (typeof hash === 'string')
596
+ txByHash.set(hash, { feeToken: row['fee_token'], type: row['type'] });
597
+ }
598
+ }
599
+ // Reconstruct the RPC shape from the indexed columns, then humanize it
600
+ // through the same path as the detail endpoint so the list and detail
601
+ // responses are identical. Rows that fail to reconstruct are skipped.
602
+ const data = [];
603
+ for (const row of page.rows) {
604
+ const tx = typeof row['tx_hash'] === 'string' ? txByHash.get(row['tx_hash']) : undefined;
605
+ const merged = { ...row, fee_token: tx?.feeToken, type: tx?.type };
606
+ const rpc = schema.Rpc.Receipt.safeParse(toRpcReceipt(merged));
607
+ if (rpc.success)
608
+ data.push(humanizeReceipt(rpc.data));
609
+ }
610
+ return { data, nextCursor: page.nextCursor };
611
+ }, {
612
+ key: `receipts:v1:${chainId}:${order}:${address ?? ''}:${sender ?? ''}:${recipient ?? ''}:${feePayer ?? ''}:${feeToken ?? ''}:${fromBlock ?? ''}:${toBlock ?? ''}:${fromTimestamp ?? ''}:${toTimestamp ?? ''}:${status ?? ''}:${cursor ? `cursor:${cursor[0]}:${cursor[1]}` : offset !== undefined ? `page:${options.page}` : 'head'}:${limit}`,
613
+ store,
614
+ ttl: Ttl.seconds(15),
615
+ }));
616
+ }
617
+ /**
618
+ * Builds the non-cursor, non-address-scope filters shared by the receipt page
619
+ * ({@link getReceipts}) and the capped count ({@link countReceipts}), so the
620
+ * count always matches the page it annotates. The address scope is handled
621
+ * separately (its `UNION` workaround differs between page and count).
622
+ */
623
+ function countFilters(options) {
624
+ const filters = [];
625
+ if (options.sender !== undefined)
626
+ filters.push(`"from" = '${options.sender}'`);
627
+ if (options.recipient !== undefined)
628
+ filters.push(`"to" = '${options.recipient}'`);
629
+ if (options.feePayer !== undefined)
630
+ filters.push(`fee_payer = '${options.feePayer}'`);
631
+ if (options.feeToken !== undefined)
632
+ filters.push(`tx_hash IN (SELECT hash FROM txs WHERE fee_token = '${options.feeToken}')`);
633
+ if (options.fromBlock !== undefined)
634
+ filters.push(`block_num >= ${options.fromBlock}`);
635
+ if (options.toBlock !== undefined)
636
+ filters.push(`block_num <= ${options.toBlock}`);
637
+ if (options.fromTimestamp !== undefined)
638
+ filters.push(`block_timestamp >= '${options.fromTimestamp}'`);
639
+ if (options.toTimestamp !== undefined)
640
+ filters.push(`block_timestamp <= '${options.toTimestamp}'`);
641
+ if (options.status !== undefined)
642
+ filters.push(`status = ${options.status === 'success' ? 1 : 0}`);
643
+ return filters;
644
+ }
645
+ /**
646
+ * Capped total count of receipts matching the same filters as the page. The
647
+ * address scope reuses the page's `UNION` workaround (`receipts` indexes `from`
648
+ * but not `to`, so the `to` side resolves hashes via the `txs` index), counting
649
+ * the deduped keyset so self-receipts (`from = to`) aren't double-counted.
650
+ * `totalCountCapped: true` is a lower bound — at least {@link Schema.countCap} matches.
651
+ */
652
+ export function countReceipts(c, options) {
653
+ const { address, chainId } = options;
654
+ const store = c.get('store');
655
+ const tidx = c.get('getTidx')(chainId);
656
+ return Timing.time(c, 'receipts_count', () => Store.memoize(async () => {
657
+ const filters = countFilters(options);
658
+ let query;
659
+ if (address !== undefined) {
660
+ const fromWhere = [`"from" = '${address}'`, ...filters].join(' AND ');
661
+ const toWhere = [
662
+ `tx_hash IN (SELECT hash FROM txs WHERE "to" = '${address}')`,
663
+ ...filters,
664
+ ].join(' AND ');
665
+ query = `
666
+ SELECT count(*) AS total
667
+ FROM (
668
+ (SELECT block_num, tx_idx FROM receipts WHERE ${fromWhere} LIMIT ${Schema.countCap})
669
+ UNION
670
+ (SELECT block_num, tx_idx FROM receipts WHERE ${toWhere} LIMIT ${Schema.countCap})
671
+ ) AS capped
672
+ `;
673
+ }
674
+ else {
675
+ const where = filters.length > 0 ? `WHERE ${filters.join(' AND ')}` : '';
676
+ query = `
677
+ SELECT count(*) AS total
678
+ FROM (
679
+ SELECT 1 FROM receipts ${where} LIMIT ${Schema.countCap}
680
+ ) AS capped
681
+ `;
682
+ }
683
+ const result = await tidx.fetch({ chainId, query: query });
684
+ const totalCount = Value.toNumber(result.rows[0]?.['total']) ?? 0;
685
+ return { totalCountCapped: totalCount >= Schema.countCap, totalCount };
686
+ }, {
687
+ key: `receipts-count:v1:${chainId}:${address ?? ''}:${options.sender ?? ''}:${options.recipient ?? ''}:${options.feePayer ?? ''}:${options.feeToken ?? ''}:${options.fromBlock ?? ''}:${options.toBlock ?? ''}:${options.fromTimestamp ?? ''}:${options.toTimestamp ?? ''}:${options.status ?? ''}`,
688
+ store,
689
+ ttl: Ttl.seconds(15),
690
+ }));
691
+ }
692
+ /**
693
+ * Resolves humanized receipts — including their event logs — for a page of
694
+ * transactions, keyed by lowercased transaction hash. Rows without a receipt
695
+ * (pending, or not yet indexed) are simply absent from the map.
696
+ *
697
+ * Receipt rows come from the indexed `receipts` table (skipped when the caller
698
+ * already paged receipts via the `status` path) and event logs from the raw
699
+ * `logs` table, both fetched once for the whole page. No `Store.memoize` here:
700
+ * the route-level response cache (`Cache.response`) already absorbs repeated
701
+ * requests for the same page.
702
+ */
703
+ export async function embedReceipts(c, options) {
704
+ const { chainId, rows } = options;
705
+ const found = new Map();
706
+ // Only mined rows can have a receipt; their block numbers also feed the
707
+ // block bound the `logs` lookup requires.
708
+ const blocks = [];
709
+ const hashes = new Set();
710
+ for (const row of rows) {
711
+ if (row.blockNumber === null)
712
+ continue;
713
+ blocks.push(row.blockNumber);
714
+ hashes.add(row.hash.toLowerCase());
715
+ }
716
+ if (hashes.size === 0)
717
+ return found;
718
+ const tidx = c.get('getTidx')(chainId);
719
+ const inList = [...hashes].map((hash) => `'${hash}'`).join(', ');
720
+ // Raw-table queries (no signature-decoded CTE), so the inline SQL is cast to
721
+ // `string`. The receipts lookup needs no block bound (`receipts.tx_hash` is
722
+ // indexed — see the `sideQuery` comment in `getReceipts`); the logs lookup
723
+ // REQUIRES one: `logs` is sorted by block position, so a bare `tx_hash IN`
724
+ // scans to genesis (see `blockBound` in `transfers.ts`).
725
+ const [receipts_result, logs_result] = await Timing.time(c, 'receipts_embed', () => Promise.all([
726
+ options.receipts
727
+ ? undefined
728
+ : tidx.fetch({
729
+ chainId,
730
+ query: `SELECT block_num, block_timestamp, tx_idx, tx_hash, "from", "to", contract_address, gas_used, cumulative_gas_used, effective_gas_price, status, fee_payer FROM receipts WHERE tx_hash IN (${inList})`,
731
+ }),
732
+ tidx.fetch({
733
+ chainId,
734
+ query: `SELECT tx_hash, block_num, tx_idx, log_idx, address, selector, topic1, topic2, topic3, data, block_timestamp FROM logs WHERE block_num BETWEEN ${Math.min(...blocks)} AND ${Math.max(...blocks)} AND tx_hash IN (${inList}) ORDER BY block_num ASC, log_idx ASC`,
735
+ }),
736
+ ]));
737
+ // Reconstruct each raw `logs` row into the RPC log shape `schema.Log`
738
+ // consumes (the postgres `logs` table names `topic0` `selector`), grouped by
739
+ // transaction hash. Logs that fail to reconstruct are skipped.
740
+ const logsByHash = new Map();
741
+ for (const row of logs_result.rows) {
742
+ const hash = Value.toText(row['tx_hash'])?.toLowerCase();
743
+ if (!hash)
744
+ continue;
745
+ const timestamp = Value.toIsoDateTime(row['block_timestamp']);
746
+ const log = schema.Log.safeParse({
747
+ address: row['address'],
748
+ blockHash: null,
749
+ blockNumber: Value.toHex(row['block_num']),
750
+ blockTimestamp: timestamp ? Value.toHex(Math.floor(Date.parse(timestamp) / 1000)) : undefined,
751
+ data: Value.toText(row['data']) ?? '0x',
752
+ logIndex: Value.toHex(row['log_idx']),
753
+ removed: false,
754
+ topics: [row['selector'], row['topic1'], row['topic2'], row['topic3']].filter((topic) => typeof topic === 'string' && topic.length > 0),
755
+ transactionHash: hash,
756
+ transactionIndex: Value.toHex(row['tx_idx']),
757
+ });
758
+ if (!log.success)
759
+ continue;
760
+ const logs = logsByHash.get(hash);
761
+ if (logs)
762
+ logs.push(log.data);
763
+ else
764
+ logsByHash.set(hash, [log.data]);
765
+ }
766
+ // Status path: the caller already paged humanized receipts, which the
767
+ // receipts list reconstructs without logs — rebuild each with the page's
768
+ // logs attached (both on the row and inside its `meta.rpc` payload).
769
+ if (options.receipts) {
770
+ for (const receipt of options.receipts) {
771
+ const hash = receipt.transactionHash.toLowerCase();
772
+ const logs = logsByHash.get(hash) ?? [];
773
+ found.set(hash, {
774
+ ...receipt,
775
+ logs,
776
+ meta: { ...receipt.meta, rpc: { ...receipt.meta.rpc, logs } },
777
+ });
778
+ }
779
+ return found;
780
+ }
781
+ const receiptRowByHash = new Map();
782
+ for (const row of receipts_result?.rows ?? []) {
783
+ const hash = Value.toText(row['tx_hash'])?.toLowerCase();
784
+ if (hash)
785
+ receiptRowByHash.set(hash, row);
786
+ }
787
+ // Per transaction row, merge in the `type`/`fee_token` the `receipts` table
788
+ // does not store from the transaction row itself (saving the `txs` follow-up
789
+ // lookup the receipts list needs), inject the reconstructed logs, and
790
+ // humanize through the same path as the receipt endpoints.
791
+ for (const row of rows) {
792
+ const hash = row.hash.toLowerCase();
793
+ const receipt = receiptRowByHash.get(hash);
794
+ if (!receipt)
795
+ continue;
796
+ const merged = {
797
+ ...receipt,
798
+ fee_token: row.feeToken,
799
+ // `toRpcReceipt` hex-encodes numeric columns; decode the row's raw hex
800
+ // `type` byte so it round-trips through the same path.
801
+ type: Value.hexToNumber(row.meta.rpc.type),
802
+ };
803
+ const rpc = schema.Rpc.Receipt.safeParse({
804
+ ...toRpcReceipt(merged),
805
+ logs: logsByHash.get(hash) ?? [],
806
+ });
807
+ if (rpc.success)
808
+ found.set(hash, humanizeReceipt(rpc.data));
809
+ }
810
+ return found;
811
+ }
812
+ /**
813
+ * Humanizes a JSON-RPC transaction receipt: decodes `status`/`type`, converts
814
+ * hex quantities to decimal strings and block fields to numbers, and derives the
815
+ * block `timestamp` from the top-level `blockTimestamp` (present when
816
+ * reconstructed from the index) or the (shared) per-log timestamp — `null` when
817
+ * neither is available. The verbatim payload is preserved under `meta.rpc` and
818
+ * the raw logs under `logs`. Shared by the receipt detail (RPC source) and list
819
+ * (reconstructed from the indexer) endpoints, so both expose an identical shape.
820
+ */
821
+ export function humanizeReceipt(rpc) {
822
+ const gasUsed = Value.hexToNumber(rpc.gasUsed) ?? 0;
823
+ const effectiveGasPrice = Value.fromHex(rpc.effectiveGasPrice) ?? '0';
824
+ return {
825
+ blockHash: rpc.blockHash,
826
+ blockNumber: Value.hexToNumber(rpc.blockNumber) ?? 0,
827
+ contractAddress: rpc.contractAddress ?? null,
828
+ cumulativeGasUsed: Value.hexToNumber(rpc.cumulativeGasUsed) ?? 0,
829
+ effectiveGasPrice,
830
+ feeAmount: computeFeeAmount(gasUsed, effectiveGasPrice) ?? '0',
831
+ feePayer: rpc.feePayer ?? undefined,
832
+ feeToken: rpc.feeToken ?? undefined,
833
+ gasUsed,
834
+ id: rpc.transactionHash,
835
+ logs: rpc.logs,
836
+ meta: { rpc },
837
+ recipient: rpc.to,
838
+ sender: rpc.from,
839
+ status: Value.hexToNumber(rpc.status) === 1 ? 'success' : 'reverted',
840
+ timestamp: Value.hexSecondsToIso(rpc.blockTimestamp ?? rpc.logs[0]?.blockTimestamp) ?? null,
841
+ transactionHash: rpc.transactionHash,
842
+ transactionIndex: Value.hexToNumber(rpc.transactionIndex) ?? 0,
843
+ type: decodeTransactionType(rpc.type),
844
+ };
845
+ }
846
+ /**
847
+ * Re-encodes a TIDX `receipts` row (joined to `txs`) into the JSON-RPC receipt
848
+ * shape, which becomes the `rpc` payload the list endpoint humanizes through the
849
+ * same path as the detail endpoint. Numeric columns become `0x` quantities;
850
+ * fields the indexer does not store — the block hash, logs bloom, and event logs
851
+ * — are null/empty since the `receipts` table records neither.
852
+ */
853
+ export function toRpcReceipt(row) {
854
+ const timestamp = Value.toIsoDateTime(row['block_timestamp']);
855
+ return {
856
+ blockHash: null,
857
+ blockNumber: Value.toHex(row['block_num']),
858
+ blockTimestamp: timestamp ? Value.toHex(Math.floor(Date.parse(timestamp) / 1000)) : undefined,
859
+ contractAddress: typeof row['contract_address'] === 'string' ? row['contract_address'] : null,
860
+ cumulativeGasUsed: Value.toHex(row['cumulative_gas_used']),
861
+ effectiveGasPrice: Value.toHex(row['effective_gas_price']),
862
+ feePayer: typeof row['fee_payer'] === 'string' ? row['fee_payer'] : undefined,
863
+ feeToken: typeof row['fee_token'] === 'string' ? row['fee_token'] : undefined,
864
+ from: row['from'],
865
+ gasUsed: Value.toHex(row['gas_used']),
866
+ logs: [],
867
+ status: Value.toHex(row['status']),
868
+ to: typeof row['to'] === 'string' ? row['to'] : null,
869
+ transactionHash: row['tx_hash'],
870
+ transactionIndex: Value.toHex(row['tx_idx']),
871
+ type: Value.toHex(row['type']),
872
+ };
873
+ }
874
+ //# sourceMappingURL=receipts.js.map