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,302 @@
1
+ import type * as Viem from '../internal/Viem.js'
2
+ import * as TestApp from '../../test/App.js'
3
+ import * as Runtime from '../../test/runtime.js'
4
+
5
+ const runtime = Runtime.get()
6
+
7
+ describe('POST /rpc', () => {
8
+ test('routes by principal: a resolver sees the request principal and routes accordingly', async () => {
9
+ const fetch = mockRpc()
10
+ // A resolver that routes API-key principals to the private upstream and
11
+ // everything else (here, the keyless `auth: false` caller) to the public one.
12
+ const rpc = ({ principal }: Viem.resolve.Context) =>
13
+ principal?.type === 'api_key'
14
+ ? { basicAuth: 'rpc-secret', url: 'https://internal.rpc.test/json-rpc' }
15
+ : { url: 'https://public.rpc.test/json-rpc' }
16
+
17
+ try {
18
+ // Auth disabled ⇒ no API-key principal ⇒ public RPC, no basic auth.
19
+ const publicApp = TestApp.create({ auth: false, rpc })
20
+ await publicApp.fetch(
21
+ new Request('http://tempo-api.test/rpc', {
22
+ body: JSON.stringify({ id: 1, jsonrpc: '2.0', method: 'eth_chainId' }),
23
+ method: 'POST',
24
+ }),
25
+ )
26
+
27
+ // Valid API key ⇒ api_key principal ⇒ private profile: internal RPC + basic auth.
28
+ const authedApp = TestApp.create({ rpc })
29
+ await authedApp.fetch(
30
+ new Request('http://tempo-api.test/rpc', {
31
+ body: JSON.stringify({ id: 1, jsonrpc: '2.0', method: 'eth_chainId' }),
32
+ headers: { 'tempo-api-key': TestApp.key.token },
33
+ method: 'POST',
34
+ }),
35
+ )
36
+
37
+ expect(fetch.calls[0]?.url).toMatchInlineSnapshot(`"https://public.rpc.test/json-rpc"`)
38
+ expect(fetch.calls[0]?.headers['authorization']).toMatchInlineSnapshot(`null`)
39
+ expect(fetch.calls[1]?.url).toMatchInlineSnapshot(`"https://internal.rpc.test/json-rpc"`)
40
+ expect(fetch.calls[1]?.headers['authorization']).toMatchInlineSnapshot(
41
+ `"Basic cnBjLXNlY3JldA=="`,
42
+ )
43
+ } finally {
44
+ fetch.restore()
45
+ }
46
+ })
47
+
48
+ test('proxies JSON-RPC requests to the configured upstream RPC', async () => {
49
+ const fetch = mockRpc()
50
+
51
+ try {
52
+ const app = TestApp.create({
53
+ auth: false,
54
+ rpc: {
55
+ basicAuth: 'rpc-secret',
56
+ url: (chainId) => `https://${chainId}.rpc.test/json-rpc`,
57
+ },
58
+ })
59
+
60
+ const response = await app.fetch(
61
+ new Request('http://tempo-api.test/rpc', {
62
+ body: JSON.stringify({ id: 1, jsonrpc: '2.0', method: 'eth_chainId', params: [] }),
63
+ headers: {
64
+ authorization: 'Bearer caller-secret',
65
+ 'content-type': 'application/json',
66
+ cookie: 'session=secret',
67
+ 'tempo-api-key': 'caller-key',
68
+ 'x-client-trace': 'trace_test',
69
+ },
70
+ method: 'POST',
71
+ }),
72
+ )
73
+ const body = await response.json()
74
+ const call = fetch.calls[0]!
75
+
76
+ expect(response.status).toMatchInlineSnapshot(`200`)
77
+ expect(response.headers.get('x-upstream')).toMatchInlineSnapshot(`"rpc"`)
78
+ expect(body).toMatchInlineSnapshot(`
79
+ {
80
+ "id": 1,
81
+ "jsonrpc": "2.0",
82
+ "result": "0xa5bf",
83
+ }
84
+ `)
85
+ expect(call.method).toMatchInlineSnapshot(`"POST"`)
86
+ expect(call.url).toBe(`https://${runtime.chainId}.rpc.test/json-rpc`)
87
+ expect(call.body).toMatchInlineSnapshot(
88
+ `"{"id":1,"jsonrpc":"2.0","method":"eth_chainId","params":[]}"`,
89
+ )
90
+ expect(call.headers['authorization']).toMatchInlineSnapshot(`"Basic cnBjLXNlY3JldA=="`)
91
+ expect(call.headers['cookie']).toMatchInlineSnapshot(`null`)
92
+ expect(call.headers['tempo-api-key']).toMatchInlineSnapshot(`null`)
93
+ expect(call.headers['x-client-trace']).toMatchInlineSnapshot(`"trace_test"`)
94
+ } finally {
95
+ fetch.restore()
96
+ }
97
+ })
98
+
99
+ test('routes anonymous requests to the public RPC without basic auth', async () => {
100
+ const fetch = mockRpc()
101
+
102
+ try {
103
+ const app = TestApp.create({
104
+ rpc: {
105
+ basicAuth: 'rpc-secret',
106
+ url: (chainId) => `https://${chainId}.rpc.test/json-rpc`,
107
+ },
108
+ })
109
+
110
+ const response = await app.fetch(
111
+ new Request('http://tempo-api.test/rpc/testnet', {
112
+ body: JSON.stringify({ id: 1, jsonrpc: '2.0', method: 'eth_chainId' }),
113
+ method: 'POST',
114
+ }),
115
+ )
116
+
117
+ expect(response.status).toMatchInlineSnapshot(`200`)
118
+ expect(fetch.calls[0]?.url).toMatchInlineSnapshot(`"https://rpc.testnet.tempo.xyz/"`)
119
+ expect(fetch.calls[0]?.headers['authorization']).toMatchInlineSnapshot(`null`)
120
+ } finally {
121
+ fetch.restore()
122
+ }
123
+ })
124
+
125
+ test('uses the requested chain id for upstream URL resolution', async () => {
126
+ const fetch = mockRpc()
127
+
128
+ try {
129
+ const app = TestApp.create({
130
+ auth: false,
131
+ rpc: { url: (chainId) => `https://${chainId}.rpc.test` },
132
+ })
133
+
134
+ const response = await app.fetch(
135
+ new Request('http://tempo-api.test/rpc/4217', {
136
+ body: JSON.stringify({ id: 1, jsonrpc: '2.0', method: 'eth_chainId' }),
137
+ method: 'POST',
138
+ }),
139
+ )
140
+
141
+ expect(response.status).toMatchInlineSnapshot(`200`)
142
+ expect(fetch.calls[0]?.url).toMatchInlineSnapshot(`"https://4217.rpc.test/"`)
143
+ } finally {
144
+ fetch.restore()
145
+ }
146
+ })
147
+
148
+ test('uses mainnet for the mainnet shortcut', async () => {
149
+ const fetch = mockRpc()
150
+
151
+ try {
152
+ const app = TestApp.create({
153
+ auth: false,
154
+ rpc: { url: (chainId) => `https://${chainId}.rpc.test` },
155
+ })
156
+
157
+ const response = await app.fetch(
158
+ new Request('http://tempo-api.test/rpc/mainnet', {
159
+ body: JSON.stringify({ id: 1, jsonrpc: '2.0', method: 'eth_chainId' }),
160
+ method: 'POST',
161
+ }),
162
+ )
163
+
164
+ expect(response.status).toMatchInlineSnapshot(`200`)
165
+ expect(fetch.calls[0]?.url).toMatchInlineSnapshot(`"https://4217.rpc.test/"`)
166
+ } finally {
167
+ fetch.restore()
168
+ }
169
+ })
170
+
171
+ test('uses testnet for the testnet shortcut', async () => {
172
+ const fetch = mockRpc()
173
+
174
+ try {
175
+ const app = TestApp.create({
176
+ auth: false,
177
+ rpc: { url: (chainId) => `https://${chainId}.rpc.test` },
178
+ })
179
+
180
+ const response = await app.fetch(
181
+ new Request('http://tempo-api.test/rpc/testnet', {
182
+ body: JSON.stringify({ id: 1, jsonrpc: '2.0', method: 'eth_chainId' }),
183
+ method: 'POST',
184
+ }),
185
+ )
186
+
187
+ expect(response.status).toMatchInlineSnapshot(`200`)
188
+ expect(fetch.calls[0]?.url).toMatchInlineSnapshot(`"https://42431.rpc.test/"`)
189
+ } finally {
190
+ fetch.restore()
191
+ }
192
+ })
193
+
194
+ test('rejects invalid chain ids before proxying upstream', async () => {
195
+ const fetch = mockRpc()
196
+
197
+ try {
198
+ const app = TestApp.create({ auth: false })
199
+ const response = await app.fetch(
200
+ new Request('http://tempo-api.test/rpc/0', {
201
+ body: JSON.stringify({ id: 1, jsonrpc: '2.0', method: 'eth_chainId' }),
202
+ method: 'POST',
203
+ }),
204
+ )
205
+ const body = (await response.json()) as Record<string, unknown>
206
+ const { requestId, ...stable } = body
207
+
208
+ expect(response.status).toMatchInlineSnapshot(`400`)
209
+ expect(typeof requestId).toMatchInlineSnapshot(`"string"`)
210
+ expect(stable).toMatchInlineSnapshot(`
211
+ {
212
+ "error": {
213
+ "code": "chain_id_invalid",
214
+ "message": "Invalid chain id",
215
+ },
216
+ }
217
+ `)
218
+ expect(fetch.calls).toMatchInlineSnapshot(`[]`)
219
+ } finally {
220
+ fetch.restore()
221
+ }
222
+ })
223
+
224
+ test('requires the read scope for API keys', async () => {
225
+ const fetch = mockRpc()
226
+
227
+ try {
228
+ const app = TestApp.create({
229
+ auth: {
230
+ source: TestApp.source({
231
+ keys: [
232
+ {
233
+ id: 'key_indexer_only',
234
+ orgId: 'org_test',
235
+ scopes: ['indexer:query'],
236
+ token: 'secret_indexer_key',
237
+ },
238
+ ],
239
+ }),
240
+ },
241
+ })
242
+ const response = await app.fetch(
243
+ new Request('http://tempo-api.test/rpc', {
244
+ body: JSON.stringify({ id: 1, jsonrpc: '2.0', method: 'eth_chainId' }),
245
+ headers: { 'tempo-api-key': 'secret_indexer_key' },
246
+ method: 'POST',
247
+ }),
248
+ )
249
+ const body = (await response.json()) as Record<string, unknown>
250
+ const { requestId, ...stable } = body
251
+
252
+ expect(response.status).toMatchInlineSnapshot(`403`)
253
+ expect(typeof requestId).toMatchInlineSnapshot(`"string"`)
254
+ expect(stable).toMatchInlineSnapshot(`
255
+ {
256
+ "error": {
257
+ "code": "api_key_forbidden",
258
+ "message": "API key missing required scope",
259
+ },
260
+ }
261
+ `)
262
+ expect(fetch.calls).toMatchInlineSnapshot(`[]`)
263
+ } finally {
264
+ fetch.restore()
265
+ }
266
+ })
267
+ })
268
+
269
+ function mockRpc() {
270
+ const original = globalThis.fetch
271
+ const calls: {
272
+ body: string
273
+ headers: Record<string, string | null>
274
+ method: string
275
+ url: string
276
+ }[] = []
277
+ const spy = vi.spyOn(globalThis, 'fetch').mockImplementation(async (input, init) => {
278
+ const request = input instanceof Request ? input : new Request(input, init)
279
+ calls.push({
280
+ body: await request.text(),
281
+ headers: {
282
+ authorization: request.headers.get('authorization'),
283
+ cookie: request.headers.get('cookie'),
284
+ 'tempo-api-key': request.headers.get('tempo-api-key'),
285
+ 'x-client-trace': request.headers.get('x-client-trace'),
286
+ },
287
+ method: request.method,
288
+ url: request.url,
289
+ })
290
+ return new Response(JSON.stringify({ id: 1, jsonrpc: '2.0', result: '0xa5bf' }), {
291
+ headers: { 'content-type': 'application/json', 'x-upstream': 'rpc' },
292
+ })
293
+ })
294
+
295
+ return {
296
+ calls,
297
+ restore() {
298
+ spy.mockRestore()
299
+ globalThis.fetch = original
300
+ },
301
+ }
302
+ }
@@ -0,0 +1,222 @@
1
+ import { Hono, type Context, type TypedResponse } from 'hono'
2
+ import * as z from 'zod/mini'
3
+
4
+ import type * as App from '../App.js'
5
+ import * as Auth from '../internal/Auth.js'
6
+ import * as OpenApi from '../internal/OpenApi.js'
7
+ import * as Response from '../internal/Response.js'
8
+ import * as Schema from '../internal/Schema.js'
9
+ import * as Viem from '../internal/Viem.js'
10
+
11
+ // Request headers that must not be forwarded upstream: hop-by-hop headers, the
12
+ // inbound host, and any caller auth so the consumer's Tempo API credentials never
13
+ // reach the RPC service (the proxy injects its own RPC basic auth instead).
14
+ const strippedHeaders = new Set([
15
+ 'authorization',
16
+ 'connection',
17
+ 'cookie',
18
+ 'host',
19
+ 'keep-alive',
20
+ 'proxy-authorization',
21
+ 'te',
22
+ 'trailer',
23
+ 'transfer-encoding',
24
+ 'upgrade',
25
+ 'tempo-api-key',
26
+ ])
27
+
28
+ /** Never reached; carries typed passthrough response variants into Hono client inference. */
29
+ const narrowPassthrough = false as boolean
30
+
31
+ /** Zod schemas owned by the raw RPC passthrough. */
32
+ export namespace schema {
33
+ /** JSON-RPC request identifier. */
34
+ export const Id = z
35
+ .union([z.string(), z.number(), z.null()])
36
+ .check(z.describe('Client-supplied JSON-RPC request id used to match responses to requests.'))
37
+
38
+ /** JSON-RPC error payload. */
39
+ export const Error = z
40
+ .object({
41
+ code: z
42
+ .number()
43
+ .check(
44
+ z.int(),
45
+ z.describe('Numeric JSON-RPC error code returned by the upstream RPC server.'),
46
+ ),
47
+ data: z
48
+ .optional(z.unknown())
49
+ .check(z.describe('Optional extra error details returned by the upstream RPC server.')),
50
+ message: z.string().check(z.describe('Human-readable JSON-RPC error message.')),
51
+ })
52
+ .check(z.describe('JSON-RPC error object returned when a method fails.'))
53
+
54
+ /** JSON-RPC request payload. */
55
+ export const Request = z
56
+ .object({
57
+ id: z
58
+ .optional(Id)
59
+ .check(
60
+ z.describe('Client-supplied JSON-RPC request id used to match responses to requests.'),
61
+ ),
62
+ jsonrpc: z.literal('2.0').check(z.describe('JSON-RPC protocol version; Tempo uses `2.0`.')),
63
+ method: z
64
+ .string()
65
+ .check(z.describe('Ethereum JSON-RPC method to call, such as `eth_blockNumber`.')),
66
+ params: z
67
+ .optional(z.unknown())
68
+ .check(z.describe('Parameters for the JSON-RPC method, usually an array.')),
69
+ })
70
+ .check(z.describe('One Ethereum JSON-RPC request to send to Tempo.'))
71
+
72
+ /** JSON-RPC response payload. */
73
+ export const Response = z
74
+ .object({
75
+ error: z
76
+ .optional(Error)
77
+ .check(z.describe('JSON-RPC error object returned when a method fails.')),
78
+ id: z
79
+ .optional(Id)
80
+ .check(
81
+ z.describe('Client-supplied JSON-RPC request id used to match responses to requests.'),
82
+ ),
83
+ jsonrpc: z.literal('2.0').check(z.describe('JSON-RPC protocol version; Tempo uses `2.0`.')),
84
+ result: z
85
+ .optional(z.unknown())
86
+ .check(z.describe('Result returned by the upstream RPC method.')),
87
+ })
88
+ .check(z.describe('One JSON-RPC response returned by Tempo.'))
89
+
90
+ /** JSON-RPC request or batch request payload. */
91
+ export const RequestBody = z
92
+ .union([Request, z.array(Request)])
93
+ .check(z.describe('A single JSON-RPC request or an array of requests for batch calls.'))
94
+
95
+ /** JSON-RPC response or batch response payload. */
96
+ export const ResponseBody = z
97
+ .union([Response, z.array(Response)])
98
+ .check(z.describe('A single JSON-RPC response or an array of responses for batch calls.'))
99
+ }
100
+
101
+ /** Creates the raw Tempo RPC passthrough handler. */
102
+ export function rpc() {
103
+ return new Hono<App.Environment>().post(
104
+ '/:chain{mainnet|testnet|[0-9]+}?',
105
+ Auth.policy({ apiKey: { scopes: ['data:read'] }, public: true }),
106
+ OpenApi.describeRoute({
107
+ description:
108
+ 'Sends a single or batch Ethereum JSON-RPC request directly to Tempo. Use `/rpc/testnet`, `/rpc/mainnet`, or `/rpc/:chain` to choose a chain. This passthrough is not part of the stable API contract and is supported on a best-effort basis only. It has no compatibility, latency, availability, or data-freshness guarantees, and breaking changes may happen with limited notice.',
109
+ operationId: 'rpcRequest',
110
+ parameters: [
111
+ {
112
+ description:
113
+ 'Optional chain selector. Use the alias `mainnet` or `testnet`, or a numeric chain id (mainnet is `4217`).',
114
+ example: 'testnet',
115
+ in: 'path' as const,
116
+ name: 'chain',
117
+ required: false,
118
+ schema: { pattern: '^(mainnet|testnet|[0-9]+)$', type: 'string' as const },
119
+ },
120
+ ],
121
+ requestBody: {
122
+ content: {
123
+ 'application/json': {
124
+ example: { jsonrpc: '2.0', id: 1, method: 'eth_blockNumber', params: [] },
125
+ },
126
+ },
127
+ required: true,
128
+ },
129
+ responses: OpenApi.responses({
130
+ errors: {
131
+ 400: {
132
+ description: 'The chain id is invalid or this API deployment does not support it.',
133
+ codes: ['chain_id_invalid', 'chain_id_unsupported'],
134
+ },
135
+ 502: 'The upstream Tempo RPC endpoint could not complete the request.',
136
+ },
137
+ success: {
138
+ description: 'JSON-RPC response returned by the upstream Tempo RPC endpoint.',
139
+ example: { jsonrpc: '2.0', id: 1, result: '0x10f2c' },
140
+ schema: schema.ResponseBody,
141
+ },
142
+ }),
143
+ summary: 'Call JSON-RPC',
144
+ tags: ['RPC'],
145
+ }),
146
+ (c) => {
147
+ if (Auth.narrowAccess) return Auth.accessError(c)
148
+
149
+ const chainId = resolveChainId(c)
150
+ if (!chainId)
151
+ return Response.error(c, {
152
+ code: 'chain_id_invalid',
153
+ message: 'Invalid chain id',
154
+ status: 400,
155
+ })
156
+
157
+ if (narrowPassthrough)
158
+ return c.json(Response.validated(schema.ResponseBody, { jsonrpc: '2.0' }), 200)
159
+ return passthrough(c, chainId)
160
+ },
161
+ )
162
+ }
163
+
164
+ async function passthrough(
165
+ c: Context<App.Environment>,
166
+ chainId: z.output<typeof Schema.ChainId>,
167
+ ): Promise<globalThis.Response & TypedResponse<never, never, never>> {
168
+ const rpc = c.get('rpc')
169
+ const principal = Auth.getPrincipal(c)
170
+ const upstream_rpc = principal?.type === 'public' && typeof rpc !== 'function' ? undefined : rpc
171
+ // Anonymous callers use the built-in public RPC unless a resolver chooses a
172
+ // different public upstream.
173
+ const { basicAuth, url } = Viem.resolve(upstream_rpc, { chainId, principal })
174
+ // The chain id is syntactically valid but this deployment has no RPC upstream
175
+ // configured for it (no `rpc` config and no built-in default).
176
+ if (!url)
177
+ return Response.error(c, {
178
+ code: 'chain_id_unsupported',
179
+ message: 'Unsupported chain id',
180
+ status: 400,
181
+ }) as never
182
+ const upstream = new URL(url)
183
+
184
+ const headers = new Headers()
185
+ for (const [name, value] of c.req.raw.headers)
186
+ if (!strippedHeaders.has(name.toLowerCase())) headers.set(name, value)
187
+ if (basicAuth) headers.set('authorization', `Basic ${btoa(basicAuth)}`)
188
+
189
+ // `duplex` is required when streaming a request body, but is not yet in
190
+ // the DOM `RequestInit` type.
191
+ const init: RequestInit & { duplex?: 'half' } = {
192
+ headers,
193
+ method: c.req.method,
194
+ redirect: 'manual',
195
+ signal: AbortSignal.timeout(35_000),
196
+ }
197
+ if (c.req.raw.body) {
198
+ init.body = c.req.raw.body
199
+ init.duplex = 'half'
200
+ }
201
+
202
+ try {
203
+ const response = await fetch(new Request(upstream, init))
204
+
205
+ // Stream the upstream response back verbatim so JSON-RPC bodies, status
206
+ // codes, and headers survive exactly as clients expect.
207
+ return c.newResponse(response.body, response) as globalThis.Response &
208
+ TypedResponse<never, never, never>
209
+ } catch (cause) {
210
+ // Timeouts and network failures become the standard 502 envelope instead
211
+ // of an unhandled 500.
212
+ return Response.upstream(c, cause) as never
213
+ }
214
+ }
215
+
216
+ function resolveChainId(c: Context<App.Environment>) {
217
+ const parameter = c.req.param('chain')
218
+ if (parameter === undefined) return c.get('chainId')
219
+
220
+ const parsed = Schema.ChainId.safeParse(parameter)
221
+ return parsed.success ? parsed.data : undefined
222
+ }
@@ -0,0 +1,80 @@
1
+ import { Store } from 'cadent'
2
+
3
+ import * as TestApp from '../../test/App.js'
4
+ import * as Runtime from '../../test/runtime.js'
5
+ import * as VerifiedTokens from '../internal/VerifiedTokens.js'
6
+ import * as Tokenlist from './tokenlist.js'
7
+
8
+ /** Reader key for the data routes. */
9
+ const reader = {
10
+ id: 'key_reader',
11
+ orgId: 'org_test',
12
+ scopes: ['data:read'],
13
+ token: 'secret_reader',
14
+ } satisfies TestApp.source.Key
15
+
16
+ /** A known list published into the store before exercising the read endpoint. */
17
+ const seedList = [
18
+ { address: '0x20c0000000000000000000000000000000001001', currency: 'USD', decimals: 6, logoUri: 'https://example.com/susd.svg', name: 'Seed USD', symbol: 'sUSD' }, // prettier-ignore
19
+ { address: '0x20c0000000000000000000000000000000001002', currency: 'EUR', decimals: 2, name: 'Seed EUR', symbol: 'sEUR' }, // prettier-ignore
20
+ ] as const
21
+ const chainId = Runtime.get().chainId
22
+
23
+ /** RequestInit carrying a bearer token for the given key. */
24
+ function as(key: { token: string }) {
25
+ return { headers: { authorization: `Bearer ${key.token}` } } as const
26
+ }
27
+
28
+ /** Client whose verified-token store holds a published {@link seedList} snapshot. */
29
+ async function seeded() {
30
+ const store = Store.memory()
31
+ await VerifiedTokens.replace(store, chainId, seedList)
32
+ return TestApp.client({
33
+ auth: { source: TestApp.source({ keys: [reader] }) },
34
+ verifiedTokens: { store },
35
+ })
36
+ }
37
+
38
+ describe('GET /tokenlist', () => {
39
+ test('serves verified tokens in the Uniswap token-list format', async () => {
40
+ const response = await (await seeded()).v1.tokenlist.$get({ query: {} }, as(reader))
41
+ expect(response.status).toBe(200)
42
+ const body = await TestApp.json(response, Tokenlist.schema.getTokenList.Response)
43
+
44
+ expect(body.name).toMatch(/^Tempo/)
45
+ expect(body.version).toEqual({ major: 1, minor: 0, patch: 2 })
46
+
47
+ // Token entries carry the standard fields, scoped to the requested chain,
48
+ // with no `extensions`; a curated `logoUri` is preserved when present.
49
+ expect(body.tokens).toEqual([
50
+ {
51
+ address: '0x20c0000000000000000000000000000000001001',
52
+ chainId,
53
+ decimals: 6,
54
+ logoURI: 'https://example.com/susd.svg',
55
+ name: 'Seed USD',
56
+ symbol: 'sUSD',
57
+ },
58
+ {
59
+ address: '0x20c0000000000000000000000000000000001002',
60
+ chainId,
61
+ decimals: 2,
62
+ name: 'Seed EUR',
63
+ symbol: 'sEUR',
64
+ },
65
+ ])
66
+ expect(body.tokens[0]).not.toHaveProperty('extensions')
67
+ })
68
+
69
+ test('serves an empty list for a chain with no verified tokens', async () => {
70
+ const response = await (
71
+ await seeded()
72
+ ).v1.tokenlist.$get({ query: { chainId: '4217' } }, as(reader))
73
+ expect(response.status).toBe(200)
74
+ const body = await TestApp.json(response, Tokenlist.schema.getTokenList.Response)
75
+
76
+ expect(body.name).toBe('Tempo Mainnet')
77
+ expect(body.tokens).toEqual([])
78
+ expect(body.version.patch).toBe(0)
79
+ })
80
+ })