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
@@ -1,3 +1,5 @@
1
+ import * as Value from './Value.js'
2
+
1
3
  /** Tempo mainnet as a CAIP-2 chain reference. */
2
4
  const destinationChain = {
3
5
  addressFormat: 'evm',
@@ -20,12 +22,14 @@ const sourceChains = [
20
22
  providerIds: {
21
23
  across: '1',
22
24
  relay: '1',
25
+ symbiosis: '1',
23
26
  },
24
27
  slug: 'ethereum',
25
28
  tokens: [
26
29
  {
27
30
  address: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
28
31
  coingeckoId: 'usd-coin',
32
+ currency: 'USD',
29
33
  decimals: 6,
30
34
  name: 'USD Coin',
31
35
  standard: 'ERC-20',
@@ -34,6 +38,7 @@ const sourceChains = [
34
38
  {
35
39
  address: '0xdac17f958d2ee523a2206206994597c13d831ec7',
36
40
  coingeckoId: 'tether',
41
+ currency: 'USD',
37
42
  decimals: 6,
38
43
  name: 'Tether USD',
39
44
  standard: 'ERC-20',
@@ -42,6 +47,7 @@ const sourceChains = [
42
47
  {
43
48
  address: '0x6c3ea9036406852006290770bedfcaba0e23a0e8',
44
49
  coingeckoId: 'paypal-usd',
50
+ currency: 'USD',
45
51
  decimals: 6,
46
52
  name: 'PayPal USD',
47
53
  standard: 'ERC-20',
@@ -50,6 +56,7 @@ const sourceChains = [
50
56
  {
51
57
  address: '0x1abaea1f7c830bd89acc67ec4af516284b1bc33c',
52
58
  coingeckoId: 'euro-coin',
59
+ currency: 'EUR',
53
60
  decimals: 6,
54
61
  name: 'Euro Coin',
55
62
  standard: 'ERC-20',
@@ -58,6 +65,7 @@ const sourceChains = [
58
65
  {
59
66
  address: '0x6b175474e89094c44da98b954eedeac495271d0f',
60
67
  coingeckoId: 'dai',
68
+ currency: 'USD',
61
69
  decimals: 18,
62
70
  name: 'Dai Stablecoin',
63
71
  standard: 'ERC-20',
@@ -75,12 +83,14 @@ const sourceChains = [
75
83
  across: '8453',
76
84
  relay: '8453',
77
85
  rhino: 'BASE',
86
+ symbiosis: '8453',
78
87
  },
79
88
  slug: 'base',
80
89
  tokens: [
81
90
  {
82
91
  address: '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913',
83
92
  coingeckoId: 'usd-coin',
93
+ currency: 'USD',
84
94
  decimals: 6,
85
95
  name: 'USD Coin',
86
96
  standard: 'ERC-20',
@@ -89,6 +99,7 @@ const sourceChains = [
89
99
  {
90
100
  address: '0xfde4c96c8593536e31f229ea8f37b2ada2699bb2',
91
101
  coingeckoId: 'tether',
102
+ currency: 'USD',
92
103
  decimals: 6,
93
104
  name: 'Tether USD',
94
105
  standard: 'ERC-20',
@@ -97,6 +108,7 @@ const sourceChains = [
97
108
  {
98
109
  address: '0x60a3e35cc302bfa44cb288bc5a4f316fdb1adb42',
99
110
  coingeckoId: 'euro-coin',
111
+ currency: 'EUR',
100
112
  decimals: 6,
101
113
  name: 'Euro Coin',
102
114
  standard: 'ERC-20',
@@ -113,12 +125,14 @@ const sourceChains = [
113
125
  providerIds: {
114
126
  across: '56',
115
127
  relay: '56',
128
+ symbiosis: '56',
116
129
  },
117
130
  slug: 'bnb',
118
131
  tokens: [
119
132
  {
120
133
  address: '0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d',
121
134
  coingeckoId: 'usd-coin',
135
+ currency: 'USD',
122
136
  decimals: 18,
123
137
  name: 'Binance-Peg USD Coin',
124
138
  standard: 'BEP-20',
@@ -127,6 +141,7 @@ const sourceChains = [
127
141
  {
128
142
  address: '0x55d398326f99059ff775485246999027b3197955',
129
143
  coingeckoId: 'tether',
144
+ currency: 'USD',
130
145
  decimals: 18,
131
146
  name: 'Tether USD',
132
147
  standard: 'BEP-20',
@@ -148,6 +163,7 @@ const sourceChains = [
148
163
  {
149
164
  address: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
150
165
  coingeckoId: 'usd-coin',
166
+ currency: 'USD',
151
167
  decimals: 6,
152
168
  name: 'USD Coin',
153
169
  standard: 'SPL',
@@ -163,12 +179,14 @@ const sourceChains = [
163
179
  name: 'Tron',
164
180
  providerIds: {
165
181
  rhino: 'TRON',
182
+ symbiosis: '728126428',
166
183
  },
167
184
  slug: 'tron',
168
185
  tokens: [
169
186
  {
170
187
  address: 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t',
171
188
  coingeckoId: 'tether',
189
+ currency: 'USD',
172
190
  decimals: 6,
173
191
  name: 'Tether USD',
174
192
  standard: 'TRC-20',
@@ -182,6 +200,7 @@ const sourceChains = [
182
200
  const destinationTokens = [
183
201
  {
184
202
  address: '0x20c0000000000000000000000000000000000000',
203
+ currency: 'USD',
185
204
  decimals: 6,
186
205
  name: 'pathUSD',
187
206
  slug: 'pathusd',
@@ -190,6 +209,7 @@ const destinationTokens = [
190
209
  },
191
210
  {
192
211
  address: '0x20c000000000000000000000b9537d11c60e8b50',
212
+ currency: 'USD',
193
213
  decimals: 6,
194
214
  name: 'Bridged USDC (Stargate)',
195
215
  slug: 'usdce',
@@ -198,6 +218,7 @@ const destinationTokens = [
198
218
  },
199
219
  {
200
220
  address: '0x20c0000000000000000000001621e21f71cf12fb',
221
+ currency: 'EUR',
201
222
  decimals: 6,
202
223
  name: 'Bridged EURC',
203
224
  slug: 'eurce',
@@ -206,6 +227,7 @@ const destinationTokens = [
206
227
  },
207
228
  {
208
229
  address: '0x20c00000000000000000000014f22ca97301eb73',
230
+ currency: 'USD',
209
231
  decimals: 6,
210
232
  name: 'USDT0',
211
233
  slug: 'usdt0',
@@ -214,6 +236,7 @@ const destinationTokens = [
214
236
  },
215
237
  {
216
238
  address: '0x20c0000000000000000000003554d28269e0f3c2',
239
+ currency: 'USD',
217
240
  decimals: 6,
218
241
  name: 'frxUSD',
219
242
  slug: 'frxusd',
@@ -252,6 +275,13 @@ const providers = [
252
275
  requiresApiKey: true,
253
276
  status: 'unconfigured',
254
277
  },
278
+ {
279
+ id: 'symbiosis',
280
+ kind: 'crossChainDex',
281
+ name: 'Symbiosis',
282
+ requiresApiKey: false,
283
+ status: 'unconfigured',
284
+ },
255
285
  ] as const
256
286
 
257
287
  type TokenInput = (typeof sourceChains)[number]['tokens'][number]
@@ -335,8 +365,6 @@ export type QuoteProbeResult = {
335
365
  minimumOutputAmount?: string | undefined
336
366
  /** Expected destination amount in base units. */
337
367
  outputAmount?: string | undefined
338
- /** Expected destination amount rendered with destination token decimals. */
339
- outputAmountFormatted?: string | undefined
340
368
  /** Timestamp for the live quote probe. */
341
369
  sampledAt: string
342
370
  /** Provider-specific quote status for the requested amount. */
@@ -363,6 +391,27 @@ export type ProviderAdapter = {
363
391
  /** Provider adapter registry, keyed by provider id. */
364
392
  export type ProviderAdapters = Partial<Record<ProviderId, ProviderAdapter>>
365
393
 
394
+ /** One configured provider attempt made while resolving a funding quote request. */
395
+ export type ProviderAttempt =
396
+ | {
397
+ /** Wall-clock duration of the provider attempt in milliseconds. */
398
+ durationMs: number
399
+ /** Successful or expected-unavailable provider outcome. */
400
+ outcome: 'available' | 'thin' | 'unavailable'
401
+ /** Provider that handled the quote candidate. */
402
+ provider: ProviderId
403
+ }
404
+ | {
405
+ /** Error thrown by the provider boundary. */
406
+ cause: unknown
407
+ /** Wall-clock duration of the provider attempt in milliseconds. */
408
+ durationMs: number
409
+ /** Failed provider outcome. */
410
+ outcome: 'failed'
411
+ /** Provider that handled the quote candidate. */
412
+ provider: ProviderId
413
+ }
414
+
366
415
  /** A provider adapter failure retained for bounded request diagnostics. */
367
416
  export type ProviderFailure = {
368
417
  /** Error thrown by the provider boundary. */
@@ -377,8 +426,10 @@ const inventory: readonly InventoryEntry[] = [
377
426
  { destinationToken: 'usdce', provider: 'relay', sourceChains: ['ethereum', 'base', 'bnb'], sourceTokens: ['USDC', 'USDT'] }, // prettier-ignore
378
427
  { destinationToken: 'usdce', provider: 'rhino', sourceChains: ['base', 'solana'], sourceTokens: ['USDC'] }, // prettier-ignore
379
428
  { destinationToken: 'usdce', provider: 'squid', sourceChains: ['ethereum', 'base', 'bnb'], sourceTokens: ['USDC'] }, // prettier-ignore
429
+ { destinationToken: 'usdce', provider: 'symbiosis', sourceChains: ['ethereum', 'base', 'bnb'], sourceTokens: ['USDC'] }, // prettier-ignore
380
430
  { destinationToken: 'usdt0', provider: 'rhino', sourceChains: ['tron'], sourceTokens: ['USDT'] }, // prettier-ignore
381
431
  { destinationToken: 'usdt0', provider: 'squid', sourceChains: ['ethereum', 'base', 'bnb'], sourceTokens: ['USDT'] }, // prettier-ignore
432
+ { destinationToken: 'usdt0', provider: 'symbiosis', sourceChains: ['ethereum', 'base', 'bnb', 'tron'], sourceTokens: ['USDT'] }, // prettier-ignore
382
433
  ]
383
434
 
384
435
  type InventoryEntry = {
@@ -428,30 +479,61 @@ export declare namespace listProviders {
428
479
 
429
480
  /** Returns successful live funding quotes from configured providers. */
430
481
  export async function listQuotes(options: listQuotes.Options) {
431
- const candidates = listQuoteCandidates(options)
482
+ const adapters = options.adapters ?? {}
483
+ const candidates = listQuoteCandidates(options).flatMap((candidate) => {
484
+ const adapter = adapters[candidate.provider.id as ProviderId]
485
+ return adapter ? [{ adapter, candidate }] : []
486
+ })
487
+ const attempts = new Map<number, ProviderAttempt>()
432
488
  const results = await Promise.allSettled(
433
- candidates.map((candidate) =>
434
- quoteCandidate(candidate, {
435
- adapters: options.adapters ?? {},
436
- cache: options.cache ?? { bypass: false },
437
- request: {
438
- destinationToken: options.destinationToken,
439
- provider: options.provider,
440
- sourceAmount: options.sourceAmount,
441
- sourceAmountUnits: options.sourceAmountUnits,
442
- sourceChain: options.sourceChain,
443
- sourceToken: options.sourceToken,
444
- },
445
- timeoutMs: options.adapterTimeoutMs ?? defaultAdapterTimeoutMs,
446
- }),
447
- ),
489
+ candidates.map(async ({ adapter, candidate }, index) => {
490
+ const start = performance.now()
491
+ try {
492
+ const result = await quoteCandidate(candidate, {
493
+ adapter,
494
+ cache: options.cache ?? { bypass: false },
495
+ request: {
496
+ destinationToken: options.destinationToken,
497
+ provider: options.provider,
498
+ sourceAmount: options.sourceAmount,
499
+ sourceAmountUnits: options.sourceAmountUnits,
500
+ sourceChain: options.sourceChain,
501
+ sourceToken: options.sourceToken,
502
+ },
503
+ timeoutMs: options.adapterTimeoutMs ?? defaultAdapterTimeoutMs,
504
+ })
505
+ attempts.set(index, {
506
+ durationMs: performance.now() - start,
507
+ outcome: result.outcome,
508
+ provider: candidate.provider.id,
509
+ })
510
+ return 'quote' in result ? result.quote : undefined
511
+ } catch (cause) {
512
+ attempts.set(index, {
513
+ cause,
514
+ durationMs: performance.now() - start,
515
+ outcome: 'failed',
516
+ provider: candidate.provider.id,
517
+ })
518
+ throw cause
519
+ }
520
+ }),
448
521
  )
522
+ for (const index of candidates.keys()) {
523
+ const attempt = attempts.get(index)
524
+ if (!attempt) continue
525
+ try {
526
+ options.onAttempt?.(attempt)
527
+ } catch {
528
+ // Observability hooks never alter funding quote behavior.
529
+ }
530
+ }
449
531
  const quotes = results.flatMap((result) =>
450
532
  result.status === 'fulfilled' && result.value ? [result.value] : [],
451
533
  )
452
534
  const failures = results.flatMap((result, index) =>
453
535
  result.status === 'rejected'
454
- ? [{ cause: result.reason, provider: candidates[index]!.provider.id }]
536
+ ? [{ cause: result.reason, provider: candidates[index]!.candidate.provider.id }]
455
537
  : [],
456
538
  )
457
539
  if (quotes.length === 0 && failures.length > 0) throw new ProviderQuoteError(failures)
@@ -519,6 +601,8 @@ export declare namespace listQuotes {
519
601
  adapterTimeoutMs?: number | undefined
520
602
  /** Cache policy for provider-side discovery and quote lookups. */
521
603
  cache?: CachePolicy | undefined
604
+ /** Receives one bounded observation for every configured provider attempt. */
605
+ onAttempt?: ((attempt: ProviderAttempt) => void) | undefined
522
606
  }
523
607
  }
524
608
 
@@ -544,18 +628,15 @@ function decimalFromBaseUnits(baseUnits: string, decimals: number) {
544
628
  async function quoteCandidate(
545
629
  candidate: QuoteCandidate,
546
630
  options: {
547
- adapters: ProviderAdapters
631
+ adapter: ProviderAdapter
548
632
  cache: CachePolicy
549
633
  request: QuoteRequest
550
634
  timeoutMs: number
551
635
  },
552
636
  ) {
553
- const adapter = options.adapters[candidate.provider.id as ProviderId]
554
- if (!adapter) return undefined
555
-
556
637
  const quote = await runWithTimeout(
557
638
  (signal) =>
558
- adapter.probeQuote(
639
+ options.adapter.probeQuote(
559
640
  {
560
641
  cache: options.cache,
561
642
  destinationChain: candidate.destinationChain,
@@ -570,18 +651,21 @@ async function quoteCandidate(
570
651
  ),
571
652
  options.timeoutMs,
572
653
  )
573
- if (quote.status === 'unavailable') return undefined
654
+ if (quote.status === 'unavailable') return { outcome: quote.status }
574
655
  if (
575
656
  (quote.status !== 'available' && quote.status !== 'thin') ||
576
657
  quote.outputAmount === undefined ||
577
658
  quote.outputAmount === null
578
659
  )
579
660
  throw new InvalidProviderQuoteError()
580
- return formatFundingQuote(candidate, {
581
- ...quote,
582
- outputAmount: quote.outputAmount,
583
- status: quote.status,
584
- })
661
+ return {
662
+ outcome: quote.status,
663
+ quote: formatFundingQuote(candidate, {
664
+ ...quote,
665
+ outputAmount: quote.outputAmount,
666
+ status: quote.status,
667
+ }),
668
+ }
585
669
  }
586
670
 
587
671
  function formatChain(chain: SourceChainInput) {
@@ -592,6 +676,7 @@ function formatChain(chain: SourceChainInput) {
592
676
  function formatDestinationToken(token: DestinationTokenInput) {
593
677
  return {
594
678
  address: token.address,
679
+ currency: token.currency,
595
680
  decimals: token.decimals,
596
681
  metadata: {},
597
682
  name: token.name,
@@ -639,6 +724,7 @@ function formatQuoteCandidate(options: {
639
724
  function formatSourceToken(chain: SourceChainInput, token: TokenInput) {
640
725
  return {
641
726
  address: token.address,
727
+ currency: token.currency,
642
728
  decimals: token.decimals,
643
729
  metadata: { coingeckoId: token.coingeckoId },
644
730
  name: token.name,
@@ -674,7 +760,24 @@ type SuccessfulQuoteProbe = QuoteProbeResult & {
674
760
 
675
761
  function formatFundingQuote(candidate: QuoteCandidate, quote: SuccessfulQuoteProbe) {
676
762
  return {
677
- ...candidate,
763
+ destinationAmount: Value.tokenAmount({
764
+ baseUnits: quote.outputAmount,
765
+ currency: candidate.destinationToken.currency,
766
+ decimals: candidate.destinationToken.decimals,
767
+ }),
768
+ ...(quote.minimumOutputAmount !== undefined
769
+ ? {
770
+ destinationAmountMin: Value.tokenAmount({
771
+ baseUnits: quote.minimumOutputAmount,
772
+ currency: candidate.destinationToken.currency,
773
+ decimals: candidate.destinationToken.decimals,
774
+ }),
775
+ }
776
+ : {}),
777
+ destinationChain: candidate.destinationChain,
778
+ destinationToken: candidate.destinationToken,
779
+ id: candidate.id,
780
+ provider: candidate.provider,
678
781
  quality: {
679
782
  ...(quote.quality?.estimatedSeconds !== undefined
680
783
  ? { estimatedSeconds: quote.quality.estimatedSeconds }
@@ -687,16 +790,15 @@ function formatFundingQuote(candidate: QuoteCandidate, quote: SuccessfulQuotePro
687
790
  },
688
791
  quote: {
689
792
  ...(quote.expiresAt !== undefined ? { expiresAt: quote.expiresAt } : {}),
690
- inputAmount: candidate.sourceAmount.amount,
691
- ...(quote.minimumOutputAmount !== undefined
692
- ? { minimumOutputAmount: quote.minimumOutputAmount }
693
- : {}),
694
- outputAmount: quote.outputAmount,
695
- outputAmountFormatted:
696
- quote.outputAmountFormatted ??
697
- decimalFromBaseUnits(quote.outputAmount, candidate.destinationToken.decimals),
698
793
  sampledAt: quote.sampledAt,
699
794
  },
795
+ sourceAmount: Value.tokenAmount({
796
+ baseUnits: candidate.sourceAmount.amount,
797
+ currency: candidate.sourceToken.currency,
798
+ decimals: candidate.sourceToken.decimals,
799
+ }),
800
+ sourceChain: candidate.sourceChain,
801
+ sourceToken: candidate.sourceToken,
700
802
  } as const
701
803
  }
702
804
 
@@ -0,0 +1,21 @@
1
+ import type * as FundingQuotes from './FundingQuotes.js'
2
+ import type * as Log from './Log.js'
3
+
4
+ test('bounds funding provider metric identifiers', () => {
5
+ const provider = null as never as FundingQuotes.ProviderId
6
+ const id: Log.FundingProviderAttempt['id'] = provider
7
+ const provider_roundTrip: FundingQuotes.ProviderId = id
8
+ void provider_roundTrip
9
+
10
+ const attempt = {
11
+ durationMs: 1,
12
+ id: 'relay',
13
+ operation: 'quote/v2',
14
+ outcome: 'available',
15
+ } satisfies Log.FundingProviderAttempt
16
+ void attempt
17
+
18
+ // @ts-expect-error arbitrary provider names would create unbounded metric tags
19
+ const invalid: Log.FundingProviderAttempt['id'] = 'user-controlled-provider'
20
+ void invalid
21
+ })
@@ -138,6 +138,97 @@ describe('middleware', () => {
138
138
  `)
139
139
  })
140
140
 
141
+ test('records a JSON-RPC error returned with HTTP 200', async () => {
142
+ const entries: Log.Entry[] = []
143
+ const api = app({ emit: (entry) => void entries.push(entry) })
144
+ api.post('/rpc/sponsor', (c) => {
145
+ const body = {
146
+ error: {
147
+ code: -32602,
148
+ data: { code: 'billing_required', retryable: false },
149
+ message: 'Sponsorship requires an active billing source.',
150
+ },
151
+ id: 1,
152
+ jsonrpc: '2.0',
153
+ }
154
+ c.set('rpcResponse', Log.rpcErrors(body))
155
+ return c.json(body)
156
+ })
157
+
158
+ const response = await api.request('/rpc/sponsor', { method: 'POST' })
159
+
160
+ expect(response.status).toBe(200)
161
+ expect(stable(entries[0]!)).toMatchInlineSnapshot(`
162
+ {
163
+ "level": "warn",
164
+ "method": "POST",
165
+ "path": "/rpc/sponsor",
166
+ "route": "/rpc/sponsor",
167
+ "rpc": {
168
+ "code": -32602,
169
+ "dataCode": "billing_required",
170
+ "errors": 1,
171
+ },
172
+ "status": 200,
173
+ }
174
+ `)
175
+ })
176
+
177
+ test('logs JSON-RPC server errors at error level', async () => {
178
+ const entries: Log.Entry[] = []
179
+ const api = app({ emit: (entry) => void entries.push(entry) })
180
+ api.post('/rpc/sponsor', (c) => {
181
+ const body = {
182
+ error: {
183
+ code: -32603,
184
+ data: { code: 'internal_error' },
185
+ message: 'Internal error',
186
+ },
187
+ id: 1,
188
+ jsonrpc: '2.0',
189
+ }
190
+ c.set('rpcResponse', Log.rpcErrors(body))
191
+ return c.json(body)
192
+ })
193
+
194
+ await api.request('/rpc/sponsor', { method: 'POST' })
195
+
196
+ expect(entries[0]?.level).toBe('error')
197
+ })
198
+
199
+ test('counts batch JSON-RPC errors without retaining mixed codes', async () => {
200
+ const entries: Log.Entry[] = []
201
+ const api = app({ emit: (entry) => void entries.push(entry) })
202
+ api.post('/rpc/relay', (c) => {
203
+ const body = [
204
+ { id: 1, jsonrpc: '2.0', result: '0x1' },
205
+ { error: { code: -32602, message: 'Invalid params.' }, id: 2, jsonrpc: '2.0' },
206
+ {
207
+ error: { code: -32603, data: { code: 'Internal Error' }, message: 'Internal error.' },
208
+ id: 3,
209
+ jsonrpc: '2.0',
210
+ },
211
+ ]
212
+ c.set('rpcResponse', Log.rpcErrors(body))
213
+ return c.json(body)
214
+ })
215
+
216
+ await api.request('/rpc/relay', { method: 'POST' })
217
+
218
+ expect(stable(entries[0]!)).toMatchInlineSnapshot(`
219
+ {
220
+ "level": "warn",
221
+ "method": "POST",
222
+ "path": "/rpc/relay",
223
+ "route": "/rpc/relay",
224
+ "rpc": {
225
+ "errors": 2,
226
+ },
227
+ "status": 200,
228
+ }
229
+ `)
230
+ })
231
+
141
232
  test('includes redacted provider failure details', async () => {
142
233
  const entries: Log.Entry[] = []
143
234
  const api = app({ emit: (entry) => void entries.push(entry) })
@@ -177,6 +268,56 @@ describe('middleware', () => {
177
268
  `)
178
269
  })
179
270
 
271
+ test('includes bounded funding provider attempts', async () => {
272
+ const entries: Log.Entry[] = []
273
+ const api = app({ emit: (entry) => void entries.push(entry) })
274
+ api.get('/funding', (c) => {
275
+ c.set('fundingProviderAttempts', [
276
+ {
277
+ durationMs: 12.5,
278
+ id: 'relay',
279
+ operation: 'quote/v2',
280
+ outcome: 'available',
281
+ },
282
+ {
283
+ durationMs: 25,
284
+ failure: 'timeout',
285
+ id: 'across',
286
+ operation: 'swap/approval',
287
+ outcome: 'failed',
288
+ },
289
+ ])
290
+ return c.json({ ok: true }, 200)
291
+ })
292
+
293
+ await api.request('/funding')
294
+
295
+ expect(stable(entries[0]!)).toMatchInlineSnapshot(`
296
+ {
297
+ "fundingProviderAttempts": [
298
+ {
299
+ "durationMs": 12.5,
300
+ "id": "relay",
301
+ "operation": "quote/v2",
302
+ "outcome": "available",
303
+ },
304
+ {
305
+ "durationMs": 25,
306
+ "failure": "timeout",
307
+ "id": "across",
308
+ "operation": "swap/approval",
309
+ "outcome": "failed",
310
+ },
311
+ ],
312
+ "level": "info",
313
+ "method": "GET",
314
+ "path": "/funding",
315
+ "route": "/funding",
316
+ "status": 200,
317
+ }
318
+ `)
319
+ })
320
+
180
321
  test('logs thrown errors at error level', async () => {
181
322
  const entries: Log.Entry[] = []
182
323
  const api = app({ emit: (entry) => void entries.push(entry) })