tapimo 0.5.3 → 0.7.0

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 (376) hide show
  1. package/dist/App.d.ts +15830 -7220
  2. package/dist/App.d.ts.map +1 -1
  3. package/dist/App.js +9 -1
  4. package/dist/App.js.map +1 -1
  5. package/dist/Scope.d.ts +4 -0
  6. package/dist/Scope.d.ts.map +1 -1
  7. package/dist/Scope.js +5 -0
  8. package/dist/Scope.js.map +1 -1
  9. package/dist/admin/App.d.ts +1011 -412
  10. package/dist/admin/App.d.ts.map +1 -1
  11. package/dist/admin/App.js +4 -0
  12. package/dist/admin/App.js.map +1 -1
  13. package/dist/admin/apps/api-keys.d.ts +2 -1
  14. package/dist/admin/apps/api-keys.d.ts.map +1 -1
  15. package/dist/admin/apps/early-access.d.ts +148 -0
  16. package/dist/admin/apps/early-access.d.ts.map +1 -0
  17. package/dist/admin/apps/early-access.js +133 -0
  18. package/dist/admin/apps/early-access.js.map +1 -0
  19. package/dist/admin/apps/organizations.d.ts +124 -1
  20. package/dist/admin/apps/organizations.d.ts.map +1 -1
  21. package/dist/admin/apps/organizations.js +97 -6
  22. package/dist/admin/apps/organizations.js.map +1 -1
  23. package/dist/admin/apps/verified-tokens.d.ts +526 -1
  24. package/dist/admin/apps/verified-tokens.d.ts.map +1 -1
  25. package/dist/admin/apps/verified-tokens.js +214 -1
  26. package/dist/admin/apps/verified-tokens.js.map +1 -1
  27. package/dist/admin/ui.generated.d.ts +1 -1
  28. package/dist/admin/ui.generated.d.ts.map +1 -1
  29. package/dist/admin/ui.generated.js +95 -75
  30. package/dist/admin/ui.generated.js.map +1 -1
  31. package/dist/analytics/Analytics.d.ts.map +1 -1
  32. package/dist/analytics/Analytics.js +20 -0
  33. package/dist/analytics/Analytics.js.map +1 -1
  34. package/dist/analytics/tables/requestEvents.d.ts +18 -0
  35. package/dist/analytics/tables/requestEvents.d.ts.map +1 -1
  36. package/dist/analytics/tables/requestEvents.js +36 -7
  37. package/dist/analytics/tables/requestEvents.js.map +1 -1
  38. package/dist/apps/data/App.d.ts +6260 -4924
  39. package/dist/apps/data/App.d.ts.map +1 -1
  40. package/dist/apps/data/App.js +11 -6
  41. package/dist/apps/data/App.js.map +1 -1
  42. package/dist/apps/data/FundingQuoteProviders.d.ts +34 -1
  43. package/dist/apps/data/FundingQuoteProviders.d.ts.map +1 -1
  44. package/dist/apps/data/FundingQuoteProviders.js +194 -16
  45. package/dist/apps/data/FundingQuoteProviders.js.map +1 -1
  46. package/dist/apps/data/FxOracle.d.ts +71 -0
  47. package/dist/apps/data/FxOracle.d.ts.map +1 -0
  48. package/dist/apps/data/FxOracle.js +97 -0
  49. package/dist/apps/data/FxOracle.js.map +1 -0
  50. package/dist/apps/data/index.d.ts +1 -0
  51. package/dist/apps/data/index.d.ts.map +1 -1
  52. package/dist/apps/data/index.js +1 -0
  53. package/dist/apps/data/index.js.map +1 -1
  54. package/dist/apps/data/routes/activities.d.ts +3599 -1382
  55. package/dist/apps/data/routes/activities.d.ts.map +1 -1
  56. package/dist/apps/data/routes/activities.js +250 -88
  57. package/dist/apps/data/routes/activities.js.map +1 -1
  58. package/dist/apps/data/routes/addresses.d.ts +872 -251
  59. package/dist/apps/data/routes/addresses.d.ts.map +1 -1
  60. package/dist/apps/data/routes/addresses.js +11 -9
  61. package/dist/apps/data/routes/addresses.js.map +1 -1
  62. package/dist/apps/data/routes/balances.d.ts +77 -26
  63. package/dist/apps/data/routes/balances.d.ts.map +1 -1
  64. package/dist/apps/data/routes/balances.js +88 -45
  65. package/dist/apps/data/routes/balances.js.map +1 -1
  66. package/dist/apps/data/routes/blocks.d.ts +39 -39
  67. package/dist/apps/data/routes/coingecko.d.ts +54 -54
  68. package/dist/apps/data/routes/exchanges.d.ts +856 -105
  69. package/dist/apps/data/routes/exchanges.d.ts.map +1 -1
  70. package/dist/apps/data/routes/exchanges.js +518 -76
  71. package/dist/apps/data/routes/exchanges.js.map +1 -1
  72. package/dist/apps/data/routes/fee-amm.d.ts +81 -67
  73. package/dist/apps/data/routes/fee-amm.d.ts.map +1 -1
  74. package/dist/apps/data/routes/fee-amm.js +76 -91
  75. package/dist/apps/data/routes/fee-amm.js.map +1 -1
  76. package/dist/apps/data/routes/funding.d.ts +80 -36
  77. package/dist/apps/data/routes/funding.d.ts.map +1 -1
  78. package/dist/apps/data/routes/funding.js +65 -31
  79. package/dist/apps/data/routes/funding.js.map +1 -1
  80. package/dist/apps/data/routes/receipts.d.ts +392 -315
  81. package/dist/apps/data/routes/receipts.d.ts.map +1 -1
  82. package/dist/apps/data/routes/receipts.js +152 -77
  83. package/dist/apps/data/routes/receipts.js.map +1 -1
  84. package/dist/apps/data/routes/tokens.d.ts +167 -174
  85. package/dist/apps/data/routes/tokens.d.ts.map +1 -1
  86. package/dist/apps/data/routes/tokens.js +15 -13
  87. package/dist/apps/data/routes/tokens.js.map +1 -1
  88. package/dist/apps/data/routes/transactions.d.ts +277 -536
  89. package/dist/apps/data/routes/transactions.d.ts.map +1 -1
  90. package/dist/apps/data/routes/transactions.js +96 -77
  91. package/dist/apps/data/routes/transactions.js.map +1 -1
  92. package/dist/apps/data/routes/transfers.d.ts +157 -81
  93. package/dist/apps/data/routes/transfers.d.ts.map +1 -1
  94. package/dist/apps/data/routes/transfers.js +272 -130
  95. package/dist/apps/data/routes/transfers.js.map +1 -1
  96. package/dist/apps/data/routes/valuation.d.ts +307 -0
  97. package/dist/apps/data/routes/valuation.d.ts.map +1 -0
  98. package/dist/apps/data/routes/valuation.js +292 -0
  99. package/dist/apps/data/routes/valuation.js.map +1 -0
  100. package/dist/apps/data/routes/verified-tokens.d.ts +36 -36
  101. package/dist/apps/data/routes/verified-tokens.d.ts.map +1 -1
  102. package/dist/apps/data/routes/verified-tokens.js +7 -5
  103. package/dist/apps/data/routes/verified-tokens.js.map +1 -1
  104. package/dist/apps/data/routes/webhooks.d.ts +103 -123
  105. package/dist/apps/data/routes/webhooks.d.ts.map +1 -1
  106. package/dist/apps/data/routes/webhooks.js +124 -25
  107. package/dist/apps/data/routes/webhooks.js.map +1 -1
  108. package/dist/apps/index.d.ts +2 -1
  109. package/dist/apps/index.d.ts.map +1 -1
  110. package/dist/apps/index.js +2 -1
  111. package/dist/apps/index.js.map +1 -1
  112. package/dist/apps/management/App.d.ts +8554 -1692
  113. package/dist/apps/management/App.d.ts.map +1 -1
  114. package/dist/apps/management/App.js +12 -1
  115. package/dist/apps/management/App.js.map +1 -1
  116. package/dist/apps/management/routes/api-keys.d.ts +7 -0
  117. package/dist/apps/management/routes/api-keys.d.ts.map +1 -1
  118. package/dist/apps/management/routes/api-keys.js +17 -3
  119. package/dist/apps/management/routes/api-keys.js.map +1 -1
  120. package/dist/apps/management/routes/billing.d.ts +157 -162
  121. package/dist/apps/management/routes/billing.d.ts.map +1 -1
  122. package/dist/apps/management/routes/billing.js +31 -23
  123. package/dist/apps/management/routes/billing.js.map +1 -1
  124. package/dist/apps/management/routes/faucet.d.ts +232 -0
  125. package/dist/apps/management/routes/faucet.d.ts.map +1 -0
  126. package/dist/apps/management/routes/faucet.js +154 -0
  127. package/dist/apps/management/routes/faucet.js.map +1 -0
  128. package/dist/apps/management/routes/invitations.d.ts +60 -60
  129. package/dist/apps/management/routes/invite-links.d.ts +49 -20
  130. package/dist/apps/management/routes/invite-links.d.ts.map +1 -1
  131. package/dist/apps/management/routes/invite-links.js +26 -1
  132. package/dist/apps/management/routes/invite-links.js.map +1 -1
  133. package/dist/apps/management/routes/me.d.ts +6 -6
  134. package/dist/apps/management/routes/members.d.ts +6 -6
  135. package/dist/apps/management/routes/orgs.d.ts +42 -42
  136. package/dist/apps/management/routes/orgs.d.ts.map +1 -1
  137. package/dist/apps/management/routes/orgs.js +5 -0
  138. package/dist/apps/management/routes/orgs.js.map +1 -1
  139. package/dist/apps/management/routes/projects.d.ts +44 -44
  140. package/dist/apps/management/routes/usage.d.ts +3 -0
  141. package/dist/apps/management/routes/usage.d.ts.map +1 -1
  142. package/dist/apps/management/routes/usage.js +4 -0
  143. package/dist/apps/management/routes/usage.js.map +1 -1
  144. package/dist/apps/management/routes/verified-token-requests.d.ts +697 -0
  145. package/dist/apps/management/routes/verified-token-requests.d.ts.map +1 -0
  146. package/dist/apps/management/routes/verified-token-requests.js +271 -0
  147. package/dist/apps/management/routes/verified-token-requests.js.map +1 -0
  148. package/dist/apps/management/routes/webhooks.d.ts +6977 -0
  149. package/dist/apps/management/routes/webhooks.d.ts.map +1 -0
  150. package/dist/apps/management/routes/webhooks.js +573 -0
  151. package/dist/apps/management/routes/webhooks.js.map +1 -0
  152. package/dist/apps/mpp/App.d.ts +363 -0
  153. package/dist/apps/mpp/App.d.ts.map +1 -0
  154. package/dist/apps/mpp/App.js +72 -0
  155. package/dist/apps/mpp/App.js.map +1 -0
  156. package/dist/apps/mpp/Mpp.d.ts +178 -0
  157. package/dist/apps/mpp/Mpp.d.ts.map +1 -0
  158. package/dist/apps/mpp/Mpp.js +183 -0
  159. package/dist/apps/mpp/Mpp.js.map +1 -0
  160. package/dist/apps/mpp/index.d.ts +3 -0
  161. package/dist/apps/mpp/index.d.ts.map +1 -0
  162. package/dist/apps/mpp/index.js +3 -0
  163. package/dist/apps/mpp/index.js.map +1 -0
  164. package/dist/apps/relay/App.d.ts.map +1 -1
  165. package/dist/apps/relay/App.js +56 -9
  166. package/dist/apps/relay/App.js.map +1 -1
  167. package/dist/db/Db.d.ts +6 -0
  168. package/dist/db/Db.d.ts.map +1 -1
  169. package/dist/db/Db.js +110 -0
  170. package/dist/db/Db.js.map +1 -1
  171. package/dist/db/Schema.d.ts +53 -0
  172. package/dist/db/Schema.d.ts.map +1 -1
  173. package/dist/db/Schema.js +56 -0
  174. package/dist/db/Schema.js.map +1 -1
  175. package/dist/db/tables/earlyAccess.d.ts +50 -0
  176. package/dist/db/tables/earlyAccess.d.ts.map +1 -0
  177. package/dist/db/tables/earlyAccess.js +63 -0
  178. package/dist/db/tables/earlyAccess.js.map +1 -0
  179. package/dist/db/tables/enabledBillingSources.d.ts +73 -0
  180. package/dist/db/tables/enabledBillingSources.d.ts.map +1 -0
  181. package/dist/db/tables/enabledBillingSources.js +69 -0
  182. package/dist/db/tables/enabledBillingSources.js.map +1 -0
  183. package/dist/db/tables/invitations.d.ts.map +1 -1
  184. package/dist/db/tables/invitations.js +3 -0
  185. package/dist/db/tables/invitations.js.map +1 -1
  186. package/dist/db/tables/inviteLinks.d.ts +10 -2
  187. package/dist/db/tables/inviteLinks.d.ts.map +1 -1
  188. package/dist/db/tables/inviteLinks.js +10 -1
  189. package/dist/db/tables/inviteLinks.js.map +1 -1
  190. package/dist/db/tables/organizations.d.ts +5 -2
  191. package/dist/db/tables/organizations.d.ts.map +1 -1
  192. package/dist/db/tables/organizations.js +13 -2
  193. package/dist/db/tables/organizations.js.map +1 -1
  194. package/dist/db/tables/sponsoredTransactions.d.ts +2 -0
  195. package/dist/db/tables/sponsoredTransactions.d.ts.map +1 -1
  196. package/dist/db/tables/sponsoredTransactions.js +2 -0
  197. package/dist/db/tables/sponsoredTransactions.js.map +1 -1
  198. package/dist/db/tables/verifiedTokenRequests.d.ts +94 -0
  199. package/dist/db/tables/verifiedTokenRequests.d.ts.map +1 -0
  200. package/dist/db/tables/verifiedTokenRequests.js +120 -0
  201. package/dist/db/tables/verifiedTokenRequests.js.map +1 -0
  202. package/dist/db/tables/webhookSubscriptions.d.ts +14 -0
  203. package/dist/db/tables/webhookSubscriptions.d.ts.map +1 -1
  204. package/dist/db/tables/webhookSubscriptions.js +16 -0
  205. package/dist/db/tables/webhookSubscriptions.js.map +1 -1
  206. package/dist/handlers/internal/utils.d.ts +1 -16
  207. package/dist/handlers/internal/utils.d.ts.map +1 -1
  208. package/dist/handlers/internal/utils.js +17 -6
  209. package/dist/handlers/internal/utils.js.map +1 -1
  210. package/dist/handlers/relay.d.ts.map +1 -1
  211. package/dist/handlers/relay.js +37 -5
  212. package/dist/handlers/relay.js.map +1 -1
  213. package/dist/internal/Auth.d.ts +11 -2
  214. package/dist/internal/Auth.d.ts.map +1 -1
  215. package/dist/internal/Auth.js +14 -47
  216. package/dist/internal/Auth.js.map +1 -1
  217. package/dist/internal/FundingQuotes.d.ts +147 -47
  218. package/dist/internal/FundingQuotes.d.ts.map +1 -1
  219. package/dist/internal/FundingQuotes.js +119 -33
  220. package/dist/internal/FundingQuotes.js.map +1 -1
  221. package/dist/internal/Log.d.ts +37 -5
  222. package/dist/internal/Log.d.ts.map +1 -1
  223. package/dist/internal/Log.js +52 -3
  224. package/dist/internal/Log.js.map +1 -1
  225. package/dist/internal/MetricSink.d.ts.map +1 -1
  226. package/dist/internal/MetricSink.js +24 -0
  227. package/dist/internal/MetricSink.js.map +1 -1
  228. package/dist/internal/OpenApi.d.ts +2 -2
  229. package/dist/internal/OpenApi.d.ts.map +1 -1
  230. package/dist/internal/OpenApi.js +3 -0
  231. package/dist/internal/OpenApi.js.map +1 -1
  232. package/dist/internal/Response.d.ts +2 -2
  233. package/dist/internal/Response.d.ts.map +1 -1
  234. package/dist/internal/Response.js +12 -4
  235. package/dist/internal/Response.js.map +1 -1
  236. package/dist/internal/Schema.d.ts +14 -5
  237. package/dist/internal/Schema.d.ts.map +1 -1
  238. package/dist/internal/Schema.js +23 -5
  239. package/dist/internal/Schema.js.map +1 -1
  240. package/dist/internal/Value.d.ts +18 -0
  241. package/dist/internal/Value.d.ts.map +1 -1
  242. package/dist/internal/Value.js +11 -1
  243. package/dist/internal/Value.js.map +1 -1
  244. package/dist/internal/VerifiedTokens.d.ts.map +1 -1
  245. package/dist/internal/VerifiedTokens.js +10 -1
  246. package/dist/internal/VerifiedTokens.js.map +1 -1
  247. package/dist/internal/Webhooks.d.ts +24 -3
  248. package/dist/internal/Webhooks.d.ts.map +1 -1
  249. package/dist/internal/Webhooks.js +34 -8
  250. package/dist/internal/Webhooks.js.map +1 -1
  251. package/package.json +7 -2
  252. package/src/App.test.ts +9 -4
  253. package/src/App.ts +23 -3
  254. package/src/Client.test-d.ts +733 -20
  255. package/src/Scope.ts +5 -0
  256. package/src/admin/App.test.ts +4 -0
  257. package/src/admin/App.ts +4 -0
  258. package/src/admin/apps/early-access.test.ts +125 -0
  259. package/src/admin/apps/early-access.ts +176 -0
  260. package/src/admin/apps/organizations.test.ts +86 -2
  261. package/src/admin/apps/organizations.ts +155 -31
  262. package/src/admin/apps/scopes.test.ts +5 -0
  263. package/src/admin/apps/verified-tokens.test.ts +201 -0
  264. package/src/admin/apps/verified-tokens.ts +532 -286
  265. package/src/admin/ui/plugin.ts +2 -1
  266. package/src/admin/ui/src/lib/early-access.ts +63 -0
  267. package/src/admin/ui/src/lib/organizations.test.ts +81 -1
  268. package/src/admin/ui/src/lib/organizations.ts +92 -0
  269. package/src/admin/ui/src/lib/verified-tokens.ts +79 -0
  270. package/src/admin/ui/src/routeTree.gen.ts +33 -2
  271. package/src/admin/ui/src/routes/__root.tsx +2 -1
  272. package/src/admin/ui/src/routes/api-keys.tsx +1 -1
  273. package/src/admin/ui/src/routes/early-access.tsx +162 -0
  274. package/src/admin/ui/src/routes/organizations_.$orgId.tsx +103 -0
  275. package/src/admin/ui/src/routes/verified-tokens.tsx +230 -19
  276. package/src/admin/ui.generated.ts +95 -94
  277. package/src/analytics/Analytics.test.ts +83 -0
  278. package/src/analytics/Analytics.ts +20 -0
  279. package/src/analytics/tables/requestEvents.ts +60 -7
  280. package/src/apps/composition.test.ts +11 -2
  281. package/src/apps/data/App.test.ts +1 -1
  282. package/src/apps/data/App.ts +22 -14
  283. package/src/apps/data/FundingQuoteProviders.test.ts +297 -4
  284. package/src/apps/data/FundingQuoteProviders.ts +255 -29
  285. package/src/apps/data/FxOracle.test.ts +118 -0
  286. package/src/apps/data/FxOracle.ts +153 -0
  287. package/src/apps/data/index.ts +1 -0
  288. package/src/apps/data/routes/activities.test.ts +177 -3
  289. package/src/apps/data/routes/activities.ts +441 -132
  290. package/src/apps/data/routes/addresses.ts +19 -9
  291. package/src/apps/data/routes/balances.test.ts +148 -11
  292. package/src/apps/data/routes/balances.ts +140 -51
  293. package/src/apps/data/routes/exchanges.test.ts +544 -9
  294. package/src/apps/data/routes/exchanges.ts +690 -103
  295. package/src/apps/data/routes/fee-amm.test.ts +47 -23
  296. package/src/apps/data/routes/fee-amm.ts +98 -119
  297. package/src/apps/data/routes/funding.test.ts +113 -8
  298. package/src/apps/data/routes/funding.ts +77 -51
  299. package/src/apps/data/routes/receipts.test.ts +129 -23
  300. package/src/apps/data/routes/receipts.ts +205 -100
  301. package/src/apps/data/routes/tokens.test.ts +43 -0
  302. package/src/apps/data/routes/tokens.ts +28 -13
  303. package/src/apps/data/routes/transactions.test.ts +72 -19
  304. package/src/apps/data/routes/transactions.ts +162 -92
  305. package/src/apps/data/routes/transfers.test.ts +341 -31
  306. package/src/apps/data/routes/transfers.ts +358 -158
  307. package/src/apps/data/routes/valuation.test.ts +437 -0
  308. package/src/apps/data/routes/valuation.ts +423 -0
  309. package/src/apps/data/routes/verified-tokens.ts +14 -2
  310. package/src/apps/data/routes/webhooks.test.ts +56 -8
  311. package/src/apps/data/routes/webhooks.ts +144 -27
  312. package/src/apps/index.ts +2 -1
  313. package/src/apps/management/App.ts +13 -0
  314. package/src/apps/management/routes/api-keys.test.ts +14 -0
  315. package/src/apps/management/routes/api-keys.ts +27 -3
  316. package/src/apps/management/routes/billing.test.ts +152 -20
  317. package/src/apps/management/routes/billing.ts +34 -18
  318. package/src/apps/management/routes/faucet.test.ts +87 -0
  319. package/src/apps/management/routes/faucet.ts +201 -0
  320. package/src/apps/management/routes/invite-links.test.ts +117 -0
  321. package/src/apps/management/routes/invite-links.ts +43 -1
  322. package/src/apps/management/routes/me.test.ts +9 -99
  323. package/src/apps/management/routes/orgs.test.ts +45 -2
  324. package/src/apps/management/routes/orgs.ts +6 -0
  325. package/src/apps/management/routes/scopes.test.ts +1 -0
  326. package/src/apps/management/routes/usage.test.ts +6 -0
  327. package/src/apps/management/routes/usage.ts +7 -0
  328. package/src/apps/management/routes/verified-token-requests.test.ts +377 -0
  329. package/src/apps/management/routes/verified-token-requests.ts +397 -0
  330. package/src/apps/management/routes/webhooks.test.ts +426 -0
  331. package/src/apps/management/routes/webhooks.ts +672 -0
  332. package/src/apps/mpp/App.ts +86 -0
  333. package/src/apps/mpp/Mpp.test.ts +193 -0
  334. package/src/apps/mpp/Mpp.ts +353 -0
  335. package/src/apps/mpp/index.ts +2 -0
  336. package/src/apps/mpp/internal/Adapter.ts +11 -0
  337. package/src/apps/mpp/internal/Error.ts +9 -0
  338. package/src/apps/mpp/internal/Idempotency.ts +7 -0
  339. package/src/apps/mpp/internal/Screening.ts +10 -0
  340. package/src/apps/relay/App.test.ts +88 -6
  341. package/src/apps/relay/App.ts +61 -9
  342. package/src/db/Db.test.ts +19 -0
  343. package/src/db/Db.ts +125 -0
  344. package/src/db/Schema.ts +68 -0
  345. package/src/db/tables/earlyAccess.ts +86 -0
  346. package/src/db/tables/enabledBillingSources.test.ts +303 -0
  347. package/src/db/tables/enabledBillingSources.ts +118 -0
  348. package/src/db/tables/invitations.ts +3 -0
  349. package/src/db/tables/inviteLinks.ts +22 -3
  350. package/src/db/tables/organizations.test.ts +103 -0
  351. package/src/db/tables/organizations.ts +18 -2
  352. package/src/db/tables/sponsoredTransactions.ts +4 -0
  353. package/src/db/tables/verifiedTokenRequests.test.ts +200 -0
  354. package/src/db/tables/verifiedTokenRequests.ts +182 -0
  355. package/src/db/tables/webhookSubscriptions.ts +31 -0
  356. package/src/handlers/internal/utils.ts +22 -6
  357. package/src/handlers/relay.errors.test.ts +153 -0
  358. package/src/handlers/relay.ts +42 -7
  359. package/src/internal/Auth.test.ts +3 -3
  360. package/src/internal/Auth.ts +15 -47
  361. package/src/internal/FundingQuotes.test.ts +161 -6
  362. package/src/internal/FundingQuotes.ts +141 -39
  363. package/src/internal/Log.test-d.ts +21 -0
  364. package/src/internal/Log.test.ts +141 -0
  365. package/src/internal/Log.ts +93 -6
  366. package/src/internal/MetricSink.test.ts +215 -0
  367. package/src/internal/MetricSink.ts +24 -0
  368. package/src/internal/OpenApi.ts +3 -0
  369. package/src/internal/Response.test.ts +28 -0
  370. package/src/internal/Response.ts +12 -4
  371. package/src/internal/Schema.ts +50 -5
  372. package/src/internal/Value.test.ts +20 -0
  373. package/src/internal/Value.ts +24 -1
  374. package/src/internal/VerifiedTokens.ts +9 -1
  375. package/src/internal/Webhooks.test.ts +140 -0
  376. package/src/internal/Webhooks.ts +53 -9
@@ -4,16 +4,21 @@ import type { RequestIdVariables } from 'hono/request-id'
4
4
  import * as ApiKey from '../ApiKey.js'
5
5
  import type * as Auth from './Auth.js'
6
6
  import type * as EdgeCache from './EdgeCache.js'
7
+ import type * as FundingQuotes from './FundingQuotes.js'
7
8
  import type * as Timing from './Timing.js'
8
9
 
9
10
  /** Hono variables read by the request logger and written by other layers. */
10
11
  export type Variables = {
11
12
  /** Stable error code recorded by `Response.error` for the request log entry. */
12
13
  errorCode?: string | undefined
14
+ /** Bounded outcomes for funding providers attempted by the request. */
15
+ fundingProviderAttempts?: Entry['fundingProviderAttempts'] | undefined
13
16
  /** Redacted provider failure details recorded by an upstream adapter. */
14
17
  providerFailure?: Entry['provider'] | undefined
15
18
  /** Redacted provider failures when multiple upstream adapters fail. */
16
19
  providerFailures?: Entry['providerFailures'] | undefined
20
+ /** Bounded JSON-RPC failure metadata recorded by a composed RPC handler. */
21
+ rpcResponse?: Entry['rpc'] | undefined
17
22
  }
18
23
 
19
24
  /** Hono environment shape used by the request logger. */
@@ -35,7 +40,7 @@ export type ProviderFailure = {
35
40
  /** Stable provider error code, when one is safe to retain. */
36
41
  code?: string | undefined
37
42
  /** Failure boundary that rejected the provider request. */
38
- failure: 'http' | 'network' | 'payload' | 'timeout' | 'unknown'
43
+ failure: 'http' | 'network' | 'payload' | 'rate_limit' | 'timeout' | 'unknown'
39
44
  /** Provider identifier. */
40
45
  id: string
41
46
  /** Provider operation identifier. */
@@ -44,6 +49,20 @@ export type ProviderFailure = {
44
49
  status?: number | undefined
45
50
  }
46
51
 
52
+ /** Bounded operational result for one funding quote provider attempt. */
53
+ export type FundingProviderAttempt = {
54
+ /** Wall-clock duration of the provider attempt in milliseconds. */
55
+ durationMs: number
56
+ /** Failure class, present only when the provider attempt failed. */
57
+ failure?: ProviderFailure['failure'] | undefined
58
+ /** Provider identifier. */
59
+ id: FundingQuotes.ProviderId
60
+ /** Stable upstream operation identifier. */
61
+ operation: string
62
+ /** Provider attempt outcome. */
63
+ outcome: 'available' | 'failed' | 'thin' | 'unavailable'
64
+ }
65
+
47
66
  /** One canonical request log entry. */
48
67
  export type Entry = {
49
68
  /** `'hit'` when the edge response cache served the request without reaching the origin handlers. */
@@ -52,7 +71,9 @@ export type Entry = {
52
71
  duration: number
53
72
  /** Stable error code when the response is a JSON error envelope (e.g. `not_found`, `api_key_invalid`). */
54
73
  errorCode?: string | undefined
55
- /** Log level derived from the response status: 5xx `error`, 4xx `warn`, else `info`. */
74
+ /** Bounded funding provider attempts made while serving the request. */
75
+ fundingProviderAttempts?: readonly FundingProviderAttempt[] | undefined
76
+ /** Log level: 5xx `error`; 4xx or JSON-RPC failure `warn`; otherwise `info`. */
56
77
  level: Level
57
78
  /** Request method. */
58
79
  method: string
@@ -102,7 +123,16 @@ export type Entry = {
102
123
  requestId: string
103
124
  /** Matched handler path (bounded cardinality), e.g. `/v1/tokens/:address`; `/*` when a middleware (e.g. an edge-cache hit) short-circuited routing. */
104
125
  route: string
105
- /** Response status code. */
126
+ /** JSON-RPC failures returned in an otherwise successful HTTP response. */
127
+ rpc?: {
128
+ /** Numeric JSON-RPC error code when every failed response shares one code. */
129
+ code?: number | undefined
130
+ /** Stable application error code from `error.data.code`, when shared by every failed response. */
131
+ dataCode?: string | undefined
132
+ /** Number of failed responses in the single or batch JSON-RPC response. */
133
+ errors: number
134
+ }
135
+ /** HTTP response status code. JSON-RPC failures retain their protocol-native `200`. */
106
136
  status: number
107
137
  /** Upstream operation durations in milliseconds, from the request's Server-Timing metrics (e.g. RPC and TIDX calls). */
108
138
  timings?: Record<string, number> | undefined
@@ -119,8 +149,9 @@ export type Emit = (entry: Entry) => Promise<void> | void
119
149
  *
120
150
  * The entry derives everything from state other layers already produce —
121
151
  * `requestId`, the auth principal, `RateLimit-*` headers, Server-Timing
122
- * metrics, the `errorCode` recorded by `Response.error`, and the edge-cache
123
- * hit marker so handlers never log per-request lines themselves.
152
+ * metrics, the `errorCode` recorded by `Response.error`, JSON-RPC response
153
+ * errors, and the edge-cache hit marker, so handlers never log per-request
154
+ * lines themselves.
124
155
  */
125
156
  export function middleware<environment extends Environment = Environment>(
126
157
  options: middleware.Options = {},
@@ -140,8 +171,12 @@ export function middleware<environment extends Environment = Environment>(
140
171
  // (the same pattern EdgeCache uses for its cross-layer reads).
141
172
  const principal = c.get('principal' as never) as Auth.Principal | undefined
142
173
  const errorCode = c.get('errorCode' as never) as string | undefined
174
+ const fundingProviderAttempts = c.get('fundingProviderAttempts' as never) as
175
+ | Entry['fundingProviderAttempts']
176
+ | undefined
143
177
  const provider = c.get('providerFailure' as never) as Entry['provider']
144
178
  const providerFailures = c.get('providerFailures' as never) as Entry['providerFailures']
179
+ const rpc = c.get('rpcResponse' as never) as Entry['rpc']
145
180
 
146
181
  // The `request` total metric duplicates `duration` (measured here, one
147
182
  // layer further out), so only operation timings are kept.
@@ -170,7 +205,13 @@ export function middleware<environment extends Environment = Environment>(
170
205
  ...(c.get('edgeCacheStatus' as never) === 'hit' ? { cache: 'hit' as const } : {}),
171
206
  duration: round(duration),
172
207
  ...(errorCode === undefined ? {} : { errorCode }),
173
- level: status >= 500 ? 'error' : status >= 400 ? 'warn' : 'info',
208
+ ...(fundingProviderAttempts === undefined ? {} : { fundingProviderAttempts }),
209
+ level:
210
+ status >= 500 || rpcServerError(rpc?.code)
211
+ ? 'error'
212
+ : status >= 400 || rpc
213
+ ? 'warn'
214
+ : 'info',
174
215
  method: c.req.method,
175
216
  path: url.pathname,
176
217
  ...(principal?.payment === undefined ? {} : { payment: principal.payment.reason }),
@@ -197,6 +238,7 @@ export function middleware<environment extends Environment = Environment>(
197
238
  ...(rateLimit === undefined ? {} : { rateLimit }),
198
239
  requestId: c.get('requestId' as never) as string,
199
240
  route: c.req.routePath,
241
+ ...(rpc === undefined ? {} : { rpc }),
200
242
  status,
201
243
  ...(timings === undefined ? {} : { timings }),
202
244
  })
@@ -215,6 +257,51 @@ export function middleware<environment extends Environment = Environment>(
215
257
  }
216
258
  }
217
259
 
260
+ const rpcDataCode = /^[a-z][a-z0-9_]{0,63}$/
261
+
262
+ /** Extracts bounded error metadata from an already-parsed JSON-RPC response. */
263
+ export function rpcErrors(body: unknown): Entry['rpc'] {
264
+ const values = Array.isArray(body) ? body : [body]
265
+ const errors = values.flatMap((value) => {
266
+ if (!value || typeof value !== 'object') return []
267
+ const record = value as Record<string, unknown>
268
+ if (record['jsonrpc'] !== '2.0' || !record['error'] || typeof record['error'] !== 'object')
269
+ return []
270
+ const error = record['error'] as Record<string, unknown>
271
+ if (
272
+ !Number.isInteger(error['code']) ||
273
+ Number(error['code']) < -2_147_483_648 ||
274
+ Number(error['code']) > 2_147_483_647
275
+ )
276
+ return []
277
+ const data = error['data']
278
+ const dataCode_value =
279
+ data && typeof data === 'object' ? (data as Record<string, unknown>)['code'] : undefined
280
+ const dataCode = typeof dataCode_value === 'string' ? dataCode_value : undefined
281
+ return [
282
+ {
283
+ code: error['code'] as number,
284
+ ...(dataCode && rpcDataCode.test(dataCode) ? { dataCode } : {}),
285
+ },
286
+ ]
287
+ })
288
+ if (errors.length === 0) return undefined
289
+
290
+ const codes = new Set(errors.map((error) => error.code))
291
+ const dataCodes = new Set(errors.map((error) => error.dataCode).filter(Boolean))
292
+ return {
293
+ ...(codes.size === 1 ? { code: errors[0]!.code } : {}),
294
+ ...(dataCodes.size === 1 && errors.every((error) => error.dataCode !== undefined)
295
+ ? { dataCode: errors[0]!.dataCode }
296
+ : {}),
297
+ errors: Math.min(errors.length, 65_535),
298
+ }
299
+ }
300
+
301
+ function rpcServerError(code: number | undefined) {
302
+ return code === -32603 || (code !== undefined && code >= -32099 && code <= -32000)
303
+ }
304
+
218
305
  export declare namespace middleware {
219
306
  /** Options for the request logger middleware. */
220
307
  type Options = {
@@ -0,0 +1,215 @@
1
+ import * as Metrics from '../Metrics.js'
2
+ import type * as Log from './Log.js'
3
+ import * as MetricSink from './MetricSink.js'
4
+
5
+ type MetricEvent = {
6
+ name: string
7
+ tags: Metrics.Tags | undefined
8
+ type: 'count' | 'gauge' | 'histogram'
9
+ value: number
10
+ }
11
+
12
+ describe('requests', () => {
13
+ test('records JSON-RPC errors with bounded code tags', () => {
14
+ const events: MetricEvent[] = []
15
+ const metrics = Metrics.from({
16
+ count(name, value, tags) {
17
+ events.push({ name, tags, type: 'count', value })
18
+ },
19
+ flush() {},
20
+ gauge() {},
21
+ histogram() {},
22
+ })
23
+ const record = MetricSink.requests(metrics)
24
+
25
+ record({
26
+ duration: 10,
27
+ level: 'warn',
28
+ method: 'POST',
29
+ path: '/rpc/relay',
30
+ requestId: 'request_private',
31
+ route: '/rpc/relay',
32
+ rpc: { code: -32602, dataCode: 'billing_required', errors: 2 },
33
+ status: 200,
34
+ })
35
+
36
+ expect(events).toMatchInlineSnapshot(`
37
+ [
38
+ {
39
+ "name": "http_response_count",
40
+ "tags": {
41
+ "cache": "miss",
42
+ "method": "POST",
43
+ "principal_environment": "none",
44
+ "principal_type": "unknown",
45
+ "route": "/rpc/relay",
46
+ "status": 200,
47
+ },
48
+ "type": "count",
49
+ "value": 1,
50
+ },
51
+ {
52
+ "name": "rpc_response_error_count",
53
+ "tags": {
54
+ "cache": "miss",
55
+ "method": "POST",
56
+ "principal_environment": "none",
57
+ "principal_type": "unknown",
58
+ "route": "/rpc/relay",
59
+ "rpc_error_code": "-32602",
60
+ },
61
+ "type": "count",
62
+ "value": 2,
63
+ },
64
+ ]
65
+ `)
66
+ })
67
+
68
+ test('records bounded funding provider attempts without caller data', () => {
69
+ const events: MetricEvent[] = []
70
+ let flushes = 0
71
+ const metrics = Metrics.from({
72
+ count(name, value, tags) {
73
+ events.push({ name, tags, type: 'count', value })
74
+ },
75
+ flush() {
76
+ flushes += 1
77
+ },
78
+ gauge(name, value, tags) {
79
+ events.push({ name, tags, type: 'gauge', value })
80
+ },
81
+ histogram(name, value, tags) {
82
+ events.push({ name, tags, type: 'histogram', value })
83
+ },
84
+ })
85
+ const record = MetricSink.requests(metrics)
86
+
87
+ record({
88
+ duration: 100,
89
+ fundingProviderAttempts: [
90
+ {
91
+ durationMs: 10,
92
+ id: 'relay',
93
+ operation: 'quote/v2',
94
+ outcome: 'available',
95
+ },
96
+ {
97
+ durationMs: 20,
98
+ id: 'rhino',
99
+ operation: 'quote/bridge-swap/public',
100
+ outcome: 'thin',
101
+ },
102
+ {
103
+ durationMs: 30,
104
+ id: 'squid',
105
+ operation: 'v2/route',
106
+ outcome: 'unavailable',
107
+ },
108
+ {
109
+ durationMs: 40,
110
+ failure: 'timeout',
111
+ id: 'across',
112
+ operation: 'swap/approval',
113
+ outcome: 'failed',
114
+ },
115
+ ],
116
+ level: 'info',
117
+ method: 'GET',
118
+ path: '/v1/funding/quotes',
119
+ principal: {
120
+ id: 'key_private',
121
+ orgId: 'org_private',
122
+ projectId: 'project_private',
123
+ type: 'api_key',
124
+ },
125
+ query: 'sourceAmount=secret',
126
+ requestId: 'request_private',
127
+ route: '/v1/funding/quotes',
128
+ status: 200,
129
+ } satisfies Log.Entry)
130
+
131
+ const funding = events.filter((event) => event.name.startsWith('funding_quote_provider_'))
132
+ expect(funding).toMatchInlineSnapshot(`
133
+ [
134
+ {
135
+ "name": "funding_quote_provider_attempt_count",
136
+ "tags": {
137
+ "outcome": "available",
138
+ "provider": "relay",
139
+ },
140
+ "type": "count",
141
+ "value": 1,
142
+ },
143
+ {
144
+ "name": "funding_quote_provider_duration_ms",
145
+ "tags": {
146
+ "outcome": "available",
147
+ "provider": "relay",
148
+ },
149
+ "type": "histogram",
150
+ "value": 10,
151
+ },
152
+ {
153
+ "name": "funding_quote_provider_attempt_count",
154
+ "tags": {
155
+ "outcome": "thin",
156
+ "provider": "rhino",
157
+ },
158
+ "type": "count",
159
+ "value": 1,
160
+ },
161
+ {
162
+ "name": "funding_quote_provider_duration_ms",
163
+ "tags": {
164
+ "outcome": "thin",
165
+ "provider": "rhino",
166
+ },
167
+ "type": "histogram",
168
+ "value": 20,
169
+ },
170
+ {
171
+ "name": "funding_quote_provider_attempt_count",
172
+ "tags": {
173
+ "outcome": "unavailable",
174
+ "provider": "squid",
175
+ },
176
+ "type": "count",
177
+ "value": 1,
178
+ },
179
+ {
180
+ "name": "funding_quote_provider_duration_ms",
181
+ "tags": {
182
+ "outcome": "unavailable",
183
+ "provider": "squid",
184
+ },
185
+ "type": "histogram",
186
+ "value": 30,
187
+ },
188
+ {
189
+ "name": "funding_quote_provider_attempt_count",
190
+ "tags": {
191
+ "failure": "timeout",
192
+ "outcome": "failed",
193
+ "provider": "across",
194
+ },
195
+ "type": "count",
196
+ "value": 1,
197
+ },
198
+ {
199
+ "name": "funding_quote_provider_duration_ms",
200
+ "tags": {
201
+ "failure": "timeout",
202
+ "outcome": "failed",
203
+ "provider": "across",
204
+ },
205
+ "type": "histogram",
206
+ "value": 40,
207
+ },
208
+ ]
209
+ `)
210
+ expect(JSON.stringify(funding)).not.toMatch(
211
+ /key_private|org_private|project_private|request_private|secret/,
212
+ )
213
+ expect(flushes).toBe(1)
214
+ })
215
+ })
@@ -85,6 +85,12 @@ export function requests(backend: Metrics): RequestSink {
85
85
  })
86
86
  backend.histogram('http_response_duration_ms', entry.duration, tags)
87
87
 
88
+ if (entry.rpc)
89
+ backend.count('rpc_response_error_count', entry.rpc.errors, {
90
+ ...tags,
91
+ rpc_error_code: rpcErrorCode(entry.rpc.code),
92
+ })
93
+
88
94
  if (entry.status === 429 || entry.errorCode === 'rate_limit_exceeded')
89
95
  backend.count('http_rate_limit_count', 1, {
90
96
  ...tags,
@@ -95,10 +101,28 @@ export function requests(backend: Metrics): RequestSink {
95
101
  for (const [operation, duration] of Object.entries(entry.timings ?? {}))
96
102
  backend.histogram('upstream_duration_ms', duration, { ...tags, operation })
97
103
 
104
+ for (const attempt of entry.fundingProviderAttempts ?? []) {
105
+ const tags_attempt = {
106
+ ...(attempt.failure === undefined ? {} : { failure: attempt.failure }),
107
+ outcome: attempt.outcome,
108
+ provider: attempt.id,
109
+ }
110
+ backend.count('funding_quote_provider_attempt_count', 1, tags_attempt)
111
+ backend.histogram('funding_quote_provider_duration_ms', attempt.durationMs, tags_attempt)
112
+ }
113
+
98
114
  backend.flush()
99
115
  }
100
116
  }
101
117
 
118
+ /** Maps arbitrary upstream JSON-RPC codes into bounded metric tags. */
119
+ function rpcErrorCode(code: number | undefined) {
120
+ if (code === undefined) return 'mixed'
121
+ if ([-32700, -32600, -32601, -32602, -32603].includes(code)) return String(code)
122
+ if (code >= -32099 && code <= -32000) return 'server'
123
+ return 'other'
124
+ }
125
+
102
126
  function routeTags(entry: Log.Entry): RouteTags {
103
127
  return {
104
128
  cache: entry.cache ?? 'miss',
@@ -64,6 +64,7 @@ export const errorCodes = {
64
64
  'address_invalid',
65
65
  'token_invalid',
66
66
  'symbol_invalid',
67
+ 'quote_amount_out_of_range',
67
68
  'pair_invalid',
68
69
  'pair_id_invalid',
69
70
  'transaction_invalid',
@@ -71,6 +72,7 @@ export const errorCodes = {
71
72
  'block_invalid',
72
73
  'chain_id_invalid',
73
74
  'chain_id_unsupported',
75
+ 'destination_transition_invalid',
74
76
  'url_invalid',
75
77
  'api_key_malformed',
76
78
  ],
@@ -85,6 +87,7 @@ export const errorCodes = {
85
87
  'receipt_not_found',
86
88
  'order_not_found',
87
89
  'pair_not_found',
90
+ 'quote_not_available',
88
91
  'verified_token_not_found',
89
92
  'webhook_not_found',
90
93
  'delivery_not_found',
@@ -0,0 +1,28 @@
1
+ import { Hono } from 'hono'
2
+
3
+ import * as Response from './Response.js'
4
+
5
+ describe('upstream', () => {
6
+ test('returns a stable message and preserves the cause on context', async () => {
7
+ const app = new Hono()
8
+ const cause = new Error('provider URL contains secret credentials')
9
+ let captured: Error | undefined
10
+
11
+ app.use(async (c, next) => {
12
+ await next()
13
+ captured = c.error
14
+ })
15
+ app.get('/', (c) => Response.upstream(c, cause))
16
+
17
+ const response = await app.request('/')
18
+ expect(await response.json()).toMatchInlineSnapshot(`
19
+ {
20
+ "error": {
21
+ "code": "upstream_error",
22
+ "message": "Upstream service could not complete the request",
23
+ },
24
+ }
25
+ `)
26
+ expect(captured).toBe(cause)
27
+ })
28
+ })
@@ -39,12 +39,20 @@ export function unsupportedChainId(c: Context, chainId: number, supported: Itera
39
39
  }
40
40
 
41
41
  /**
42
- * Maps an unexpected upstream failure to a 502 JSON error envelope. Rethrows
43
- * non-`Error` causes so the global handler can convert them.
42
+ * Maps an unexpected upstream failure to a stable 502 JSON error envelope.
43
+ * Preserves `Error` causes for reporting and rethrows non-`Error` causes.
44
44
  */
45
45
  export function upstream(c: Context, cause: unknown) {
46
- if (cause instanceof Error)
47
- return error(c, { code: 'upstream_error', message: cause.message, status: 502 })
46
+ if (cause instanceof Error) {
47
+ // Preserve the original error for error-reporting middleware without
48
+ // exposing provider URLs, payloads, or credentials in the response.
49
+ c.error = cause
50
+ return error(c, {
51
+ code: 'upstream_error',
52
+ message: 'Upstream service could not complete the request',
53
+ status: 502,
54
+ })
55
+ }
48
56
  throw cause
49
57
  }
50
58
 
@@ -290,6 +290,35 @@ export const DecimalString = z
290
290
  z.meta({ examples: ['1000000'] }),
291
291
  )
292
292
 
293
+ /** A self-contained token quantity in base units and human-readable form. */
294
+ export const TokenAmount = describe(
295
+ z.object({
296
+ baseUnits: DecimalString.check(z.describe("The quantity in the token's smallest unit.")),
297
+ currency: z
298
+ .string()
299
+ .check(
300
+ z.describe('The monetary denomination of this quantity.'),
301
+ z.meta({ examples: ['USD'] }),
302
+ ),
303
+ decimals: z
304
+ .number()
305
+ .check(
306
+ z.int(),
307
+ z.nonnegative(),
308
+ z.describe('Decimal places used to convert `baseUnits` into `formatted`.'),
309
+ z.meta({ examples: [6] }),
310
+ ),
311
+ formatted: z
312
+ .string()
313
+ .check(
314
+ z.regex(/^\d+(\.\d+)?$/),
315
+ z.describe('The quantity rendered in whole token units.'),
316
+ z.meta({ examples: ['1'] }),
317
+ ),
318
+ }),
319
+ 'A token quantity with its denomination and decimal representation.',
320
+ )
321
+
293
322
  /**
294
323
  * Minimum page size accepted across list endpoints. Floored at 5 because the
295
324
  * indexer rejects signature-decoded `Transfer` queries with `LIMIT < 5`
@@ -306,6 +335,22 @@ export const Limit = z
306
335
  z.meta({ examples: [10] }),
307
336
  )
308
337
 
338
+ /** Valuation denomination query parameter shared across valuation-bearing endpoints. */
339
+ export const Denomination = z
340
+ .optional(
341
+ z.pipe(
342
+ z.string().check(z.regex(/^[a-zA-Z]{3}$/)),
343
+ z.transform((value) => value.toUpperCase()),
344
+ ),
345
+ )
346
+ .check(
347
+ z.describe(
348
+ 'Currency to denominate values in (case-insensitive, e.g. `AUD`). ' +
349
+ 'Must be priced by the configured FX oracle. Defaults to `USD`.',
350
+ ),
351
+ z.meta({ examples: ['AUD'] }),
352
+ )
353
+
309
354
  /** Opaque keyset-pagination cursor query parameter shared across list endpoints. */
310
355
  export const Cursor = z
311
356
  .optional(z.string())
@@ -464,17 +509,17 @@ export function timestampBound(noun: string, bound: 'from' | 'to') {
464
509
 
465
510
  /**
466
511
  * Builds the schema for a comma-separated opt-in query parameter (e.g.
467
- * `?include=token,feeToken`), parsed into a validated array of the given enum's
512
+ * `?include=token,totalCount`), parsed into a validated array of the given enum's
468
513
  * members. Absent or empty values yield `[]`. Centralizes the splitting,
469
514
  * trimming, and OpenAPI array-enum metadata that every list endpoint otherwise
470
515
  * repeats.
471
516
  *
472
- * A repeated parameter (`?include=token&include=feeToken`) arrives from the
517
+ * A repeated parameter (`?include=token&include=totalCount`) arrives from the
473
518
  * validator as a `string[]`, so the input accepts both forms and each entry is
474
- * still comma-split the two styles compose freely.
519
+ * still comma-split, so the two styles compose freely.
475
520
  *
476
- * Pass the endpoint's `Include` enum — kept local so its members drive both the
477
- * parsed type and the docs — plus the param description.
521
+ * Pass the endpoint's local `Include` enum so its members drive the parsed type
522
+ * and documentation, plus the parameter description.
478
523
  */
479
524
  export function includeQuery<const T extends Readonly<Record<string, string>>>(
480
525
  member: z.ZodMiniEnum<T>,
@@ -0,0 +1,20 @@
1
+ import * as Value from './Value.js'
2
+
3
+ describe('tokenAmount', () => {
4
+ test('renders a self-contained token quantity', () => {
5
+ expect(
6
+ Value.tokenAmount({
7
+ baseUnits: '1234567',
8
+ currency: 'USD',
9
+ decimals: 6,
10
+ }),
11
+ ).toMatchInlineSnapshot(`
12
+ {
13
+ "baseUnits": "1234567",
14
+ "currency": "USD",
15
+ "decimals": 6,
16
+ "formatted": "1.234567",
17
+ }
18
+ `)
19
+ })
20
+ })
@@ -1,4 +1,4 @@
1
- import { Hex } from 'ox'
1
+ import { Hex, Value as core_Value } from 'ox'
2
2
 
3
3
  /** Coerce a TIDX numeric column (bigint / number / string) to a JS number. */
4
4
  export function toNumber(value: unknown) {
@@ -39,6 +39,29 @@ export function toIntegerString(value: unknown) {
39
39
  return undefined
40
40
  }
41
41
 
42
+ /** Builds a token amount from its base-unit quantity and immutable token metadata. */
43
+ export function tokenAmount(options: tokenAmount.Options) {
44
+ const baseUnits = options.baseUnits.toString()
45
+ return {
46
+ baseUnits,
47
+ currency: options.currency,
48
+ decimals: options.decimals,
49
+ formatted: core_Value.format(BigInt(baseUnits), options.decimals),
50
+ }
51
+ }
52
+
53
+ export declare namespace tokenAmount {
54
+ /** Inputs needed to render a self-contained token amount. */
55
+ type Options = {
56
+ /** Quantity in the token's smallest unit. */
57
+ baseUnits: bigint | string
58
+ /** Monetary denomination reported by the token. */
59
+ currency: string
60
+ /** Decimal places used by the token. */
61
+ decimals: number
62
+ }
63
+ }
64
+
42
65
  /**
43
66
  * Coerce a TIDX numeric column (bigint / number / decimal string) to a minimal
44
67
  * `0x`-prefixed hex quantity (`0x0` for zero), matching the JSON-RPC wire
@@ -535,7 +535,15 @@ export async function snapshot(c: Context<App.Environment>, chainId: ChainId): P
535
535
 
536
536
  // Soft-stale: serve immediately, refresh in the background. Never blocks.
537
537
  if (cell) {
538
- void refresh(c, Db.get(c.get('dbCached')), chainId, cell)
538
+ const inflight = refresh(c, Db.get(c.get('dbCached')), chainId, cell)
539
+ // Workers cancel post-response I/O unless kept alive via `waitUntil`;
540
+ // without it the refresh dies mid-query and publishes never propagate.
541
+ // Node has no execution context, and fire-and-forget survives there.
542
+ try {
543
+ c.executionCtx.waitUntil(inflight)
544
+ } catch {
545
+ void inflight
546
+ }
539
547
  return cell.snapshot
540
548
  }
541
549