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,6 +1,7 @@
1
1
  import * as z from 'zod/mini'
2
2
 
3
3
  import * as FundingQuotes from '../../internal/FundingQuotes.js'
4
+ import * as Store from '../../internal/Store.js'
4
5
 
5
6
  type Fetch = typeof globalThis.fetch
6
7
 
@@ -10,13 +11,23 @@ const defaultAcrossBaseUrl = 'https://app.across.to/api'
10
11
  const defaultRelayBaseUrl = 'https://api.relay.link'
11
12
  const defaultRhinoBaseUrl = 'https://api.rhino.fi/bridge'
12
13
  const defaultSquidBaseUrl = 'https://v2.api.squidrouter.com/v2'
14
+ const defaultSymbiosisBaseUrl = 'https://api.symbiosis.finance/crosschain'
15
+ // Keep one upstream slot free for deployment smoke tests and operational probes.
16
+ const symbiosisRateLimit = 4
17
+ const symbiosisRateLimitWindowMs = 1_000
13
18
  const providerOperations = {
14
19
  across: 'swap/approval',
15
20
  relay: 'quote/v2',
16
21
  rhino: 'quote/bridge-swap/public',
17
22
  squid: 'v2/route',
23
+ symbiosis: 'v2/quote',
18
24
  } as const
19
25
 
26
+ /** Returns the stable upstream operation used by a funding quote provider. */
27
+ export function providerOperation(provider: FundingQuotes.ProviderId) {
28
+ return providerOperations[provider]
29
+ }
30
+
20
31
  namespace schema {
21
32
  const IntegerString = z.string().check(z.regex(/^\d+$/))
22
33
 
@@ -65,6 +76,30 @@ namespace schema {
65
76
  }),
66
77
  }),
67
78
  })
79
+
80
+ export const SymbiosisToken = z.object({
81
+ address: z.string(),
82
+ chainId: z.number().check(z.int(), z.nonnegative()),
83
+ decimals: z.number().check(z.int(), z.nonnegative()),
84
+ })
85
+
86
+ const SymbiosisTokenAmount = z.extend(SymbiosisToken, { amount: IntegerString })
87
+
88
+ export const SymbiosisQuote = z.object({
89
+ estimatedTime: z.optional(z.number().check(z.nonnegative())),
90
+ kind: z.union([z.literal('bridge'), z.literal('crosschain-swap'), z.literal('intent-swap')]),
91
+ routes: z
92
+ .array(
93
+ z.object({
94
+ tokens: z.array(SymbiosisToken).check(z.minLength(1)),
95
+ }),
96
+ )
97
+ .check(z.minLength(1)),
98
+ tokenAmountOut: SymbiosisTokenAmount,
99
+ tokenAmountOutMin: SymbiosisTokenAmount,
100
+ tx: z.object({ chainId: z.number().check(z.int(), z.nonnegative()) }),
101
+ type: z.union([z.literal('evm'), z.literal('tron')]),
102
+ })
68
103
  }
69
104
 
70
105
  /** Creates the live funding quote adapters configured for this deployment. */
@@ -74,6 +109,7 @@ export function createAdapters(options: createAdapters.Options = {}) {
74
109
  ...(options.relay ? { relay: relay(options.relay) } : {}),
75
110
  ...(options.rhino ? { rhino: rhino(options.rhino) } : {}),
76
111
  ...(options.squid ? { squid: squid(options.squid) } : {}),
112
+ ...(options.symbiosis ? { symbiosis: symbiosis(options.symbiosis) } : {}),
77
113
  } satisfies FundingQuotes.ProviderAdapters
78
114
  }
79
115
 
@@ -88,6 +124,8 @@ export declare namespace createAdapters {
88
124
  rhino?: rhino.Options | false | undefined
89
125
  /** Squid quote adapter options. */
90
126
  squid?: squid.Options | false | undefined
127
+ /** Symbiosis quote adapter options. */
128
+ symbiosis?: symbiosis.Options | false | undefined
91
129
  }
92
130
  }
93
131
 
@@ -137,6 +175,22 @@ export declare namespace configureSquid {
137
175
  }
138
176
  }
139
177
 
178
+ /** Resolves Symbiosis deployment configuration. */
179
+ export function configureSymbiosis(
180
+ options: configureSymbiosis.Options = {},
181
+ ): symbiosis.Options | undefined {
182
+ if (!options.userAddress) return undefined
183
+ return { userAddress: options.userAddress }
184
+ }
185
+
186
+ export declare namespace configureSymbiosis {
187
+ /** Optional deployment binding required to enable Symbiosis. */
188
+ type Options = {
189
+ /** Symbiosis quote-user binding. */
190
+ userAddress?: string | undefined
191
+ }
192
+ }
193
+
140
194
  /** Creates an Across quote adapter backed by `/swap/approval`. */
141
195
  export function across(options: across.Options): FundingQuotes.ProviderAdapter {
142
196
  assertAcrossConfiguration(options)
@@ -238,7 +292,7 @@ export function relay(options: relay.Options): FundingQuotes.ProviderAdapter {
238
292
  method: 'POST',
239
293
  signal,
240
294
  })
241
- return formatRelayResult(candidate, quote, now())
295
+ return formatRelayResult(quote, now())
242
296
  } catch (cause) {
243
297
  const unavailable = providerUnavailable(cause, [
244
298
  'NO_QUOTES',
@@ -397,9 +451,87 @@ export declare namespace squid {
397
451
  }
398
452
  }
399
453
 
454
+ /** Creates a Symbiosis quote adapter backed by `/v2/quote`. */
455
+ export function symbiosis(options: symbiosis.Options): FundingQuotes.ProviderAdapter {
456
+ assertUserAddress(options.userAddress)
457
+ const baseUrl = normalizeBaseUrl(options.baseUrl ?? defaultSymbiosisBaseUrl)
458
+ const fetcher = options.fetch ?? defaultFetch
459
+ const now = options.now ?? (() => new Date())
460
+ const rateLimitStore = options.rateLimitStore ?? Store.memory()
461
+
462
+ return {
463
+ async probeQuote({ candidate }, signal) {
464
+ const fromChainId = Number(providerChainId(candidate.sourceChain, 'symbiosis'))
465
+ const fromToken = symbiosisSourceToken(candidate.sourceChain, candidate.sourceToken)
466
+ const toChainId = Number(eip155ChainId(candidate.destinationChain))
467
+
468
+ try {
469
+ await consumeSymbiosisQuota(rateLimitStore, now())
470
+ const quote = await requestJson(new URL(`${baseUrl}/v2/quote`), {
471
+ body: {
472
+ from: options.userAddress,
473
+ slippage: 100,
474
+ to: options.userAddress,
475
+ tokenAmountIn: {
476
+ address: fromToken,
477
+ amount: candidate.sourceAmount.amount,
478
+ chainId: fromChainId,
479
+ decimals: candidate.sourceToken.decimals,
480
+ symbol: candidate.sourceToken.symbol,
481
+ },
482
+ tokenOut: {
483
+ address: candidate.destinationToken.address,
484
+ chainId: toChainId,
485
+ decimals: candidate.destinationToken.decimals,
486
+ symbol: candidate.destinationToken.symbol,
487
+ },
488
+ },
489
+ fetcher,
490
+ headers: { 'x-partner-id': 'tempo' },
491
+ method: 'POST',
492
+ signal,
493
+ })
494
+ return formatSymbiosisResult(candidate, quote, now())
495
+ } catch (cause) {
496
+ const unavailable =
497
+ providerUnavailable(cause, [
498
+ 'AMOUNT_TOO_LOW',
499
+ 'NO_ROUTE',
500
+ 'NO_ROUTE_FOUND',
501
+ 'ROUTE_NOT_FOUND',
502
+ 'UNSUPPORTED_ROUTE',
503
+ ]) ?? symbiosisUnavailable(cause)
504
+ if (!unavailable) throw cause
505
+ return unavailableResult({
506
+ detail: 'symbiosis:quote-v2',
507
+ message: unavailable,
508
+ now: now(),
509
+ source: 'providerQuote',
510
+ })
511
+ }
512
+ },
513
+ }
514
+ }
515
+
516
+ export declare namespace symbiosis {
517
+ /** Symbiosis quote adapter options. */
518
+ type Options = {
519
+ /** Override for tests or Symbiosis test environments. */
520
+ baseUrl?: string | undefined
521
+ /** Fetch implementation override for tests. */
522
+ fetch?: Fetch | undefined
523
+ /** Clock override for deterministic tests. */
524
+ now?: (() => Date) | undefined
525
+ /** Atomic store used to enforce the provider-wide quote quota. */
526
+ rateLimitStore?: Store.Store | undefined
527
+ /** Probe wallet address used as the Symbiosis sender and recipient. */
528
+ userAddress: string
529
+ }
530
+ }
531
+
400
532
  /** Returns bounded provider failure details suitable for structured request logs. */
401
533
  export function providerFailure(options: providerFailure.Options): providerFailure.Result {
402
- const operation = providerOperations[options.provider]
534
+ const operation = providerOperation(options.provider)
403
535
  if (
404
536
  options.cause instanceof FundingQuotes.ProviderAdapterTimeoutError ||
405
537
  (options.cause instanceof DOMException && options.cause.name === 'AbortError')
@@ -417,6 +549,8 @@ export function providerFailure(options: providerFailure.Options): providerFailu
417
549
  }
418
550
  if (options.cause instanceof ProviderPayloadError)
419
551
  return { failure: 'payload', id: options.provider, operation }
552
+ if (options.cause instanceof ProviderRateLimitError)
553
+ return { failure: 'rate_limit', id: options.provider, operation }
420
554
  if (options.cause instanceof TypeError)
421
555
  return { failure: 'network', id: options.provider, operation }
422
556
  return { failure: 'unknown', id: options.provider, operation }
@@ -431,7 +565,7 @@ export declare namespace providerFailure {
431
565
  /** Stable provider error code, when one is safe to retain. */
432
566
  code?: string | undefined
433
567
  /** Failure boundary that rejected the provider request. */
434
- failure: 'http' | 'network' | 'payload' | 'timeout' | 'unknown'
568
+ failure: 'http' | 'network' | 'payload' | 'rate_limit' | 'timeout' | 'unknown'
435
569
  /** Provider identifier. */
436
570
  id: FundingQuotes.ProviderId
437
571
  /** Provider operation identifier. */
@@ -466,10 +600,6 @@ function formatAcrossResult(
466
600
  expiresAt: expiresAt.toISOString(),
467
601
  minimumOutputAmount: response.minOutputAmount,
468
602
  outputAmount: response.expectedOutputAmount,
469
- outputAmountFormatted: decimalFromBaseUnits(
470
- response.expectedOutputAmount,
471
- candidate.destinationToken.decimals,
472
- ),
473
603
  quality: {
474
604
  estimatedSeconds: response.expectedFillTime,
475
605
  liquiditySource: 'providerQuote',
@@ -481,11 +611,7 @@ function formatAcrossResult(
481
611
  }
482
612
  }
483
613
 
484
- function formatRelayResult(
485
- candidate: FundingQuotes.QuoteCandidate,
486
- quote: unknown,
487
- now: Date,
488
- ): FundingQuotes.QuoteProbeResult {
614
+ function formatRelayResult(quote: unknown, now: Date): FundingQuotes.QuoteProbeResult {
489
615
  const details = record(record(quote)['details'])
490
616
  const currencyOut = record(details['currencyOut'])
491
617
  const outputAmount = decimalString(currencyOut['amount'])
@@ -496,9 +622,6 @@ function formatRelayResult(
496
622
  return {
497
623
  minimumOutputAmount: decimalString(currencyOut['minimumAmount']),
498
624
  outputAmount,
499
- outputAmountFormatted: outputAmount
500
- ? decimalFromBaseUnits(outputAmount, candidate.destinationToken.decimals)
501
- : undefined,
502
625
  quality: {
503
626
  estimatedSeconds: nonnegativeInteger(details['timeEstimate']),
504
627
  liquiditySource: 'providerQuote',
@@ -535,7 +658,6 @@ function formatRhinoResult(
535
658
 
536
659
  return {
537
660
  outputAmount,
538
- outputAmountFormatted: decimalFromBaseUnits(outputAmount, candidate.destinationToken.decimals),
539
661
  quality: {
540
662
  ...(response.estimatedDuration === undefined || response.estimatedDuration === null
541
663
  ? {}
@@ -570,10 +692,6 @@ function formatSquidResult(
570
692
  return {
571
693
  minimumOutputAmount: response.toAmountMin,
572
694
  outputAmount: response.toAmount,
573
- outputAmountFormatted: decimalFromBaseUnits(
574
- response.toAmount,
575
- candidate.destinationToken.decimals,
576
- ),
577
695
  quality: {
578
696
  estimatedSeconds: Math.ceil(response.estimatedRouteDuration),
579
697
  liquiditySource: 'providerQuote',
@@ -585,6 +703,64 @@ function formatSquidResult(
585
703
  }
586
704
  }
587
705
 
706
+ function formatSymbiosisResult(
707
+ candidate: FundingQuotes.QuoteCandidate,
708
+ quote: unknown,
709
+ now: Date,
710
+ ): FundingQuotes.QuoteProbeResult {
711
+ const parsed = schema.SymbiosisQuote.safeParse(quote)
712
+ if (!parsed.success) throw new ProviderPayloadError()
713
+ const response = parsed.data
714
+ const sourceChainId = Number(providerChainId(candidate.sourceChain, 'symbiosis'))
715
+ const sourceToken = symbiosisSourceToken(candidate.sourceChain, candidate.sourceToken)
716
+ const firstToken = response.routes[0]!.tokens[0]!
717
+ const lastRoute = response.routes[response.routes.length - 1]!
718
+ const lastToken = lastRoute.tokens[lastRoute.tokens.length - 1]!
719
+ if (
720
+ response.tx.chainId !== sourceChainId ||
721
+ response.type !== candidate.sourceChain.kind ||
722
+ !matchesSymbiosisToken(firstToken, {
723
+ address: sourceToken,
724
+ chainId: sourceChainId,
725
+ decimals: candidate.sourceToken.decimals,
726
+ }) ||
727
+ !matchesSymbiosisToken(lastToken, {
728
+ address: candidate.destinationToken.address,
729
+ chainId: Number(eip155ChainId(candidate.destinationChain)),
730
+ decimals: candidate.destinationToken.decimals,
731
+ }) ||
732
+ !matchesSymbiosisToken(response.tokenAmountOut, {
733
+ address: candidate.destinationToken.address,
734
+ chainId: Number(eip155ChainId(candidate.destinationChain)),
735
+ decimals: candidate.destinationToken.decimals,
736
+ }) ||
737
+ !matchesSymbiosisToken(response.tokenAmountOutMin, {
738
+ address: candidate.destinationToken.address,
739
+ chainId: Number(eip155ChainId(candidate.destinationChain)),
740
+ decimals: candidate.destinationToken.decimals,
741
+ }) ||
742
+ BigInt(response.tokenAmountOut.amount) <= 0n ||
743
+ BigInt(response.tokenAmountOutMin.amount) <= 0n ||
744
+ BigInt(response.tokenAmountOutMin.amount) > BigInt(response.tokenAmountOut.amount)
745
+ )
746
+ throw new ProviderPayloadError()
747
+
748
+ return {
749
+ minimumOutputAmount: response.tokenAmountOutMin.amount,
750
+ outputAmount: response.tokenAmountOut.amount,
751
+ quality: {
752
+ ...(response.estimatedTime === undefined
753
+ ? {}
754
+ : { estimatedSeconds: Math.ceil(response.estimatedTime) }),
755
+ liquiditySource: 'providerQuote',
756
+ sourceDetail: 'symbiosis:quote-v2',
757
+ tier: 'liquid',
758
+ },
759
+ sampledAt: now.toISOString(),
760
+ status: 'available',
761
+ }
762
+ }
763
+
588
764
  function unavailableResult(options: {
589
765
  detail: string
590
766
  message: string
@@ -664,6 +840,22 @@ function squidUnavailable(cause: unknown) {
664
840
  return undefined
665
841
  }
666
842
 
843
+ function symbiosisUnavailable(cause: unknown) {
844
+ if (!(cause instanceof ProviderResponseError) || cause.status !== 400) return undefined
845
+ const body = record(cause.body)
846
+ if (body['message'] === 'Amount is too low to cover fees') return 'AMOUNT_TOO_LOW'
847
+ return undefined
848
+ }
849
+
850
+ async function consumeSymbiosisQuota(store: Store.Store, now: Date) {
851
+ const time = now.getTime()
852
+ const bucket = Math.floor(time / symbiosisRateLimitWindowMs)
853
+ const count = await Store.increment(store, `funding:symbiosis:quote:${bucket}`, {
854
+ ttl: symbiosisRateLimitWindowMs * 2,
855
+ })
856
+ if (count > symbiosisRateLimit) throw new ProviderRateLimitError()
857
+ }
858
+
667
859
  function providerChainId(
668
860
  quoteChain: FundingQuotes.QuoteCandidate['sourceChain'],
669
861
  provider: string,
@@ -694,6 +886,16 @@ function rhinoToken(token: FundingQuotes.TokenRef) {
694
886
  return id
695
887
  }
696
888
 
889
+ function symbiosisSourceToken(chain: FundingQuotes.ChainRef, token: FundingQuotes.TokenRef) {
890
+ if (chain.kind !== 'tron') return token.address
891
+ const tokens: Record<string, string> = {
892
+ TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t: '0xa614f803b6fd780986a42c78ec9c7f77e6ded13c',
893
+ }
894
+ const id = tokens[token.address]
895
+ if (!id) throw new UnsupportedProviderTokenError()
896
+ return id
897
+ }
898
+
697
899
  function matchesToken(
698
900
  token: z.output<typeof schema.AcrossQuote>['inputToken'],
699
901
  chain: FundingQuotes.ChainRef,
@@ -718,6 +920,29 @@ function matchesSquidToken(
718
920
  )
719
921
  }
720
922
 
923
+ function matchesSymbiosisToken(
924
+ token: z.output<typeof schema.SymbiosisToken>,
925
+ options: matchesSymbiosisToken.Options,
926
+ ) {
927
+ return (
928
+ token.address.toLowerCase() === options.address.toLowerCase() &&
929
+ token.chainId === options.chainId &&
930
+ token.decimals === options.decimals
931
+ )
932
+ }
933
+
934
+ declare namespace matchesSymbiosisToken {
935
+ /** Expected provider token identity. */
936
+ type Options = {
937
+ /** Provider-specific token address. */
938
+ address: string
939
+ /** Provider-specific chain id. */
940
+ chainId: number
941
+ /** Token decimal precision. */
942
+ decimals: number
943
+ }
944
+ }
945
+
721
946
  function eip155ChainId(chain: { id: string; kind: string }) {
722
947
  if (chain.kind !== 'evm') throw new UnsupportedProviderChainError()
723
948
  const match = /^eip155:(\d+)$/.exec(chain.id)
@@ -760,13 +985,6 @@ function nonnegativeInteger(input: unknown) {
760
985
  return undefined
761
986
  }
762
987
 
763
- function decimalFromBaseUnits(baseUnits: string, decimals: number) {
764
- const padded = baseUnits.padStart(decimals + 1, '0')
765
- const whole = decimals === 0 ? padded : padded.slice(0, -decimals)
766
- const fraction = decimals === 0 ? '' : padded.slice(-decimals).replace(/0+$/, '')
767
- return fraction ? `${whole}.${fraction}` : whole
768
- }
769
-
770
988
  function baseUnitsFromDecimal(value: string, decimals: number) {
771
989
  if (!/^\d+(\.\d+)?$/.test(value)) return undefined
772
990
  const [whole, fraction = ''] = value.split('.')
@@ -785,8 +1003,12 @@ function assertAcrossConfiguration(options: across.Options) {
785
1003
  }
786
1004
 
787
1005
  function assertSquidConfiguration(options: squid.Options) {
788
- if (options.integratorId.length === 0 || !/^0x[\da-fA-F]{40}$/.test(options.userAddress))
789
- throw new ProviderConfigurationError()
1006
+ if (options.integratorId.length === 0) throw new ProviderConfigurationError()
1007
+ assertUserAddress(options.userAddress)
1008
+ }
1009
+
1010
+ function assertUserAddress(userAddress: string) {
1011
+ if (!/^0x[\da-fA-F]{40}$/.test(userAddress)) throw new ProviderConfigurationError()
790
1012
  }
791
1013
 
792
1014
  class ProviderConfigurationError extends Error {
@@ -797,6 +1019,10 @@ class ProviderPayloadError extends Error {
797
1019
  override name = 'FundingQuoteProviders.ProviderPayloadError'
798
1020
  }
799
1021
 
1022
+ class ProviderRateLimitError extends Error {
1023
+ override name = 'FundingQuoteProviders.ProviderRateLimitError'
1024
+ }
1025
+
800
1026
  class ProviderResponseError extends Error {
801
1027
  override name = 'FundingQuoteProviders.ProviderResponseError'
802
1028
 
@@ -0,0 +1,118 @@
1
+ import * as FxOracle from './FxOracle.js'
2
+
3
+ const set = {
4
+ asOf: '2026-01-01T00:00:00.000Z',
5
+ base: 'EUR',
6
+ rates: { AUD: '1.6', USD: '1.25' },
7
+ } satisfies FxOracle.RateSet
8
+
9
+ function feedUrl(usd: string) {
10
+ const body = `<Envelope><Cube><Cube time="2026-01-01"><Cube currency="AUD" rate="1.6"/><Cube currency="USD" rate="${usd}"/></Cube></Cube></Envelope>`
11
+ return `data:application/xml,${encodeURIComponent(body)}`
12
+ }
13
+
14
+ describe('from', () => {
15
+ test('applies the default ttl and preserves overrides', () => {
16
+ const oracle = FxOracle.from({ name: 'test', rates: async () => set })
17
+ expect(oracle.name).toBe('test')
18
+ expect(oracle.ttl).toMatchInlineSnapshot(`3600000`)
19
+ expect(FxOracle.from({ name: 'test', rates: async () => set, ttl: 5 }).ttl).toBe(5)
20
+ })
21
+
22
+ test('resolves the definition rates', async () => {
23
+ const oracle = FxOracle.from({ name: 'test', rates: async () => set })
24
+ await expect(oracle.rates()).resolves.toEqual(set)
25
+ })
26
+
27
+ test('rethrows rates failures as RatesError', async () => {
28
+ const oracle = FxOracle.from({
29
+ name: 'test',
30
+ rates: async () => {
31
+ throw new Error('boom')
32
+ },
33
+ })
34
+ const error = await oracle.rates().catch((error: unknown) => error)
35
+ expect(error).toBeInstanceOf(FxOracle.RatesError)
36
+ expect(error).toMatchInlineSnapshot(
37
+ `[FxOracle.RatesError: FX oracle "test" failed to produce rates]`,
38
+ )
39
+ expect((error as Error).cause).toMatchInlineSnapshot(`[Error: boom]`)
40
+ })
41
+ })
42
+
43
+ describe('ecb', () => {
44
+ test('fetches and parses the daily reference rates', async () => {
45
+ const rates = await FxOracle.ecb({ url: feedUrl('1.25') }).rates()
46
+ expect(rates).toMatchInlineSnapshot(`
47
+ {
48
+ "asOf": "2026-01-01T00:00:00.000Z",
49
+ "base": "EUR",
50
+ "rates": {
51
+ "AUD": "1.6",
52
+ "USD": "1.25",
53
+ },
54
+ }
55
+ `)
56
+ })
57
+
58
+ test.each(['', '-1.25', '1..25'])('rejects malformed rate %j', async (value) => {
59
+ const error = await FxOracle.ecb({ url: feedUrl(value) })
60
+ .rates()
61
+ .catch((error: unknown) => error)
62
+ expect(error).toBeInstanceOf(FxOracle.RatesError)
63
+ expect((error as Error).cause).toMatchInlineSnapshot(
64
+ `[FxOracle.ParseError: Rate feed returned an unrecognized payload]`,
65
+ )
66
+ })
67
+
68
+ test('rejects non-positive rates', async () => {
69
+ const error = await FxOracle.ecb({ url: feedUrl('0.0') })
70
+ .rates()
71
+ .catch((error: unknown) => error)
72
+ expect(error).toBeInstanceOf(FxOracle.RatesError)
73
+ expect((error as Error).cause).toMatchInlineSnapshot(
74
+ `[FxOracle.ParseError: Rate feed returned an unrecognized payload]`,
75
+ )
76
+ })
77
+
78
+ test('rejects rates below fixed-point precision', async () => {
79
+ const error = await FxOracle.ecb({ url: feedUrl('0.0000000000000000001') })
80
+ .rates()
81
+ .catch((error: unknown) => error)
82
+ expect(error).toBeInstanceOf(FxOracle.RatesError)
83
+ expect((error as Error).cause).toMatchInlineSnapshot(
84
+ `[FxOracle.ParseError: Rate feed returned an unrecognized payload]`,
85
+ )
86
+ })
87
+
88
+ test('rejects with RatesError when the feed is unreachable', async () => {
89
+ const oracle = FxOracle.ecb({ url: 'http://127.0.0.1:1' })
90
+ const error = await oracle.rates().catch((error: unknown) => error)
91
+ expect(error).toBeInstanceOf(FxOracle.RatesError)
92
+ expect(error).toMatchInlineSnapshot(
93
+ `[FxOracle.RatesError: FX oracle "ecb" failed to produce rates]`,
94
+ )
95
+ })
96
+ })
97
+
98
+ describe('rate', () => {
99
+ test('derives cross rates through the base', () => {
100
+ expect(FxOracle.rate(set, { from: 'USD', to: 'AUD' })).toMatchInlineSnapshot(
101
+ `1280000000000000000n`,
102
+ )
103
+ expect(FxOracle.rate(set, { from: 'EUR', to: 'USD' })).toMatchInlineSnapshot(
104
+ `1250000000000000000n`,
105
+ )
106
+ expect(FxOracle.rate(set, { from: 'USD', to: 'EUR' })).toMatchInlineSnapshot(
107
+ `800000000000000000n`,
108
+ )
109
+ expect(FxOracle.rate(set, { from: 'USD', to: 'USD' })).toMatchInlineSnapshot(
110
+ `1000000000000000000n`,
111
+ )
112
+ })
113
+
114
+ test('returns undefined for currencies the set does not price', () => {
115
+ expect(FxOracle.rate(set, { from: 'BTC', to: 'USD' })).toBeUndefined()
116
+ expect(FxOracle.rate(set, { from: 'USD', to: 'XXX' })).toBeUndefined()
117
+ })
118
+ })