tapimo 0.7.1 → 0.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (681) hide show
  1. package/dist/ApiKeys.d.ts +24 -7
  2. package/dist/ApiKeys.d.ts.map +1 -1
  3. package/dist/ApiKeys.js +17 -5
  4. package/dist/ApiKeys.js.map +1 -1
  5. package/dist/App.d.ts +12104 -7861
  6. package/dist/App.d.ts.map +1 -1
  7. package/dist/App.js +191 -128
  8. package/dist/App.js.map +1 -1
  9. package/dist/Assets.d.ts +10 -0
  10. package/dist/Assets.d.ts.map +1 -1
  11. package/dist/Assets.js +17 -4
  12. package/dist/Assets.js.map +1 -1
  13. package/dist/Scope.d.ts +19 -8
  14. package/dist/Scope.d.ts.map +1 -1
  15. package/dist/Scope.js +25 -9
  16. package/dist/Scope.js.map +1 -1
  17. package/dist/Webhooks.d.ts +1 -1
  18. package/dist/admin/App.d.ts +601 -20
  19. package/dist/admin/App.d.ts.map +1 -1
  20. package/dist/admin/App.js +12 -2
  21. package/dist/admin/App.js.map +1 -1
  22. package/dist/admin/apps/api-keys.d.ts +94 -11
  23. package/dist/admin/apps/api-keys.d.ts.map +1 -1
  24. package/dist/admin/apps/api-keys.js +141 -32
  25. package/dist/admin/apps/api-keys.js.map +1 -1
  26. package/dist/admin/apps/earn-vaults.d.ts +1069 -0
  27. package/dist/admin/apps/earn-vaults.d.ts.map +1 -0
  28. package/dist/admin/apps/earn-vaults.js +409 -0
  29. package/dist/admin/apps/earn-vaults.js.map +1 -0
  30. package/dist/admin/apps/verified-tokens.d.ts +166 -166
  31. package/dist/admin/ui.generated.d.ts +1 -1
  32. package/dist/admin/ui.generated.d.ts.map +1 -1
  33. package/dist/admin/ui.generated.js +99 -43
  34. package/dist/admin/ui.generated.js.map +1 -1
  35. package/dist/analytics/Analytics.d.ts +2 -0
  36. package/dist/analytics/Analytics.d.ts.map +1 -1
  37. package/dist/analytics/Analytics.js +18 -2
  38. package/dist/analytics/Analytics.js.map +1 -1
  39. package/dist/analytics/tables/requestEvents.d.ts +22 -1
  40. package/dist/analytics/tables/requestEvents.d.ts.map +1 -1
  41. package/dist/analytics/tables/requestEvents.js +59 -39
  42. package/dist/analytics/tables/requestEvents.js.map +1 -1
  43. package/dist/apps/Zones.d.ts +8335 -0
  44. package/dist/apps/Zones.d.ts.map +1 -0
  45. package/dist/apps/Zones.js +38 -0
  46. package/dist/apps/Zones.js.map +1 -0
  47. package/dist/apps/data/App.d.ts +12125 -7913
  48. package/dist/apps/data/App.d.ts.map +1 -1
  49. package/dist/apps/data/App.js +91 -19
  50. package/dist/apps/data/App.js.map +1 -1
  51. package/dist/apps/data/FxOracle.d.ts +1 -1
  52. package/dist/apps/data/FxOracle.d.ts.map +1 -1
  53. package/dist/apps/data/index.d.ts +0 -1
  54. package/dist/apps/data/index.d.ts.map +1 -1
  55. package/dist/apps/data/index.js +0 -1
  56. package/dist/apps/data/index.js.map +1 -1
  57. package/dist/apps/data/routes/activities.d.ts +32 -32
  58. package/dist/apps/data/routes/activities.d.ts.map +1 -1
  59. package/dist/apps/data/routes/activities.js +130 -65
  60. package/dist/apps/data/routes/activities.js.map +1 -1
  61. package/dist/apps/data/routes/addresses.d.ts +312 -312
  62. package/dist/apps/data/routes/balances.d.ts +18 -18
  63. package/dist/apps/data/routes/balances.js +5 -5
  64. package/dist/apps/data/routes/balances.js.map +1 -1
  65. package/dist/apps/data/routes/coingecko.d.ts +176 -176
  66. package/dist/apps/data/routes/coingecko.d.ts.map +1 -1
  67. package/dist/apps/data/routes/coingecko.js +62 -35
  68. package/dist/apps/data/routes/coingecko.js.map +1 -1
  69. package/dist/apps/data/routes/earn.d.ts +2144 -0
  70. package/dist/apps/data/routes/earn.d.ts.map +1 -0
  71. package/dist/apps/data/routes/earn.js +947 -0
  72. package/dist/apps/data/routes/earn.js.map +1 -0
  73. package/dist/apps/data/routes/exchanges.d.ts +1625 -718
  74. package/dist/apps/data/routes/exchanges.d.ts.map +1 -1
  75. package/dist/apps/data/routes/exchanges.js +656 -193
  76. package/dist/apps/data/routes/exchanges.js.map +1 -1
  77. package/dist/apps/data/routes/fee-amm.d.ts +108 -108
  78. package/dist/apps/data/routes/fee-amm.d.ts.map +1 -1
  79. package/dist/apps/data/routes/fee-amm.js +59 -8
  80. package/dist/apps/data/routes/fee-amm.js.map +1 -1
  81. package/dist/apps/data/routes/funding.d.ts +4092 -387
  82. package/dist/apps/data/routes/funding.d.ts.map +1 -1
  83. package/dist/apps/data/routes/funding.js +1403 -256
  84. package/dist/apps/data/routes/funding.js.map +1 -1
  85. package/dist/apps/data/routes/indexer.d.ts +30 -30
  86. package/dist/apps/data/routes/indexer.d.ts.map +1 -1
  87. package/dist/apps/data/routes/indexer.js +19 -2
  88. package/dist/apps/data/routes/indexer.js.map +1 -1
  89. package/dist/apps/data/routes/receipts.d.ts +263 -263
  90. package/dist/apps/data/routes/receipts.d.ts.map +1 -1
  91. package/dist/apps/data/routes/receipts.js +11 -11
  92. package/dist/apps/data/routes/receipts.js.map +1 -1
  93. package/dist/apps/data/routes/rpc.d.ts +5 -5
  94. package/dist/apps/data/routes/rpc.d.ts.map +1 -1
  95. package/dist/apps/data/routes/rpc.js +211 -22
  96. package/dist/apps/data/routes/rpc.js.map +1 -1
  97. package/dist/apps/data/routes/tokenlist.d.ts +36 -36
  98. package/dist/apps/data/routes/tokenlist.d.ts.map +1 -1
  99. package/dist/apps/data/routes/tokenlist.js +39 -8
  100. package/dist/apps/data/routes/tokenlist.js.map +1 -1
  101. package/dist/apps/data/routes/tokens.d.ts +114 -86
  102. package/dist/apps/data/routes/tokens.d.ts.map +1 -1
  103. package/dist/apps/data/routes/tokens.js +194 -91
  104. package/dist/apps/data/routes/tokens.js.map +1 -1
  105. package/dist/apps/data/routes/transactions.d.ts +53 -53
  106. package/dist/apps/data/routes/transactions.d.ts.map +1 -1
  107. package/dist/apps/data/routes/transactions.js +78 -39
  108. package/dist/apps/data/routes/transactions.js.map +1 -1
  109. package/dist/apps/data/routes/transfers.d.ts +76 -27
  110. package/dist/apps/data/routes/transfers.d.ts.map +1 -1
  111. package/dist/apps/data/routes/transfers.js +165 -67
  112. package/dist/apps/data/routes/transfers.js.map +1 -1
  113. package/dist/apps/data/routes/valuation.d.ts +42 -42
  114. package/dist/apps/data/routes/valuation.js +2 -2
  115. package/dist/apps/data/routes/valuation.js.map +1 -1
  116. package/dist/apps/data/routes/webhooks.d.ts +369 -367
  117. package/dist/apps/data/routes/webhooks.d.ts.map +1 -1
  118. package/dist/apps/data/routes/webhooks.js +161 -59
  119. package/dist/apps/data/routes/webhooks.js.map +1 -1
  120. package/dist/apps/data/routes/zones.d.ts +281 -0
  121. package/dist/apps/data/routes/zones.d.ts.map +1 -0
  122. package/dist/apps/data/routes/zones.js +252 -0
  123. package/dist/apps/data/routes/zones.js.map +1 -0
  124. package/dist/apps/docs/reference.js +6 -5
  125. package/dist/apps/docs/reference.js.map +1 -1
  126. package/dist/apps/index.d.ts +2 -1
  127. package/dist/apps/index.d.ts.map +1 -1
  128. package/dist/apps/index.js +2 -1
  129. package/dist/apps/index.js.map +1 -1
  130. package/dist/apps/management/App.d.ts +670 -670
  131. package/dist/apps/management/Billing.d.ts +8 -0
  132. package/dist/apps/management/Billing.d.ts.map +1 -1
  133. package/dist/apps/management/Billing.js +12 -0
  134. package/dist/apps/management/Billing.js.map +1 -1
  135. package/dist/apps/management/Error.d.ts +12 -0
  136. package/dist/apps/management/Error.d.ts.map +1 -0
  137. package/dist/apps/management/Error.js +10 -0
  138. package/dist/apps/management/Error.js.map +1 -0
  139. package/dist/apps/management/routes/api-keys.d.ts +5 -5
  140. package/dist/apps/management/routes/api-keys.d.ts.map +1 -1
  141. package/dist/apps/management/routes/api-keys.js +2 -8
  142. package/dist/apps/management/routes/api-keys.js.map +1 -1
  143. package/dist/apps/management/routes/billing.d.ts +119 -119
  144. package/dist/apps/management/routes/faucet.d.ts +2 -2
  145. package/dist/apps/management/routes/invitations.d.ts +62 -62
  146. package/dist/apps/management/routes/invitations.d.ts.map +1 -1
  147. package/dist/apps/management/routes/invitations.js +4 -10
  148. package/dist/apps/management/routes/invitations.js.map +1 -1
  149. package/dist/apps/management/routes/invite-links.d.ts +30 -30
  150. package/dist/apps/management/routes/me.d.ts +6 -6
  151. package/dist/apps/management/routes/members.d.ts +2 -2
  152. package/dist/apps/management/routes/orgs.d.ts +48 -48
  153. package/dist/apps/management/routes/orgs.d.ts.map +1 -1
  154. package/dist/apps/management/routes/orgs.js +2 -8
  155. package/dist/apps/management/routes/orgs.js.map +1 -1
  156. package/dist/apps/management/routes/projects.d.ts +9 -9
  157. package/dist/apps/management/routes/scopes.js +2 -2
  158. package/dist/apps/management/routes/scopes.js.map +1 -1
  159. package/dist/apps/management/routes/verified-token-requests.d.ts +36 -36
  160. package/dist/apps/management/routes/webhooks.d.ts +50 -50
  161. package/dist/apps/management/routes/webhooks.d.ts.map +1 -1
  162. package/dist/apps/management/routes/webhooks.js +15 -3
  163. package/dist/apps/management/routes/webhooks.js.map +1 -1
  164. package/dist/apps/mpp/App.d.ts +55 -56
  165. package/dist/apps/mpp/App.d.ts.map +1 -1
  166. package/dist/apps/mpp/App.js +192 -27
  167. package/dist/apps/mpp/App.js.map +1 -1
  168. package/dist/apps/mpp/Mpp.d.ts +0 -20
  169. package/dist/apps/mpp/Mpp.d.ts.map +1 -1
  170. package/dist/apps/mpp/Mpp.js +0 -24
  171. package/dist/apps/mpp/Mpp.js.map +1 -1
  172. package/dist/apps/mpp/internal/Adapter.d.ts +40 -0
  173. package/dist/apps/mpp/internal/Adapter.d.ts.map +1 -0
  174. package/dist/apps/mpp/internal/Adapter.js +66 -0
  175. package/dist/apps/mpp/internal/Adapter.js.map +1 -0
  176. package/dist/apps/mpp/internal/Error.d.ts +4 -0
  177. package/dist/apps/mpp/internal/Error.d.ts.map +1 -0
  178. package/dist/apps/mpp/internal/Error.js +41 -0
  179. package/dist/apps/mpp/internal/Error.js.map +1 -0
  180. package/dist/apps/mpp/internal/Idempotency.d.ts +33 -0
  181. package/dist/apps/mpp/internal/Idempotency.d.ts.map +1 -0
  182. package/dist/apps/mpp/internal/Idempotency.js +28 -0
  183. package/dist/apps/mpp/internal/Idempotency.js.map +1 -0
  184. package/dist/apps/mpp/internal/Policy.d.ts +46 -0
  185. package/dist/apps/mpp/internal/Policy.d.ts.map +1 -0
  186. package/dist/apps/mpp/internal/Policy.js +57 -0
  187. package/dist/apps/mpp/internal/Policy.js.map +1 -0
  188. package/dist/apps/mpp/internal/ScreenManager.d.ts +27 -0
  189. package/dist/apps/mpp/internal/ScreenManager.d.ts.map +1 -0
  190. package/dist/apps/mpp/internal/ScreenManager.js +18 -0
  191. package/dist/apps/mpp/internal/ScreenManager.js.map +1 -0
  192. package/dist/apps/mpp/internal/Screening.d.ts +35 -0
  193. package/dist/apps/mpp/internal/Screening.d.ts.map +1 -0
  194. package/dist/apps/mpp/internal/Screening.js +20 -0
  195. package/dist/apps/mpp/internal/Screening.js.map +1 -0
  196. package/dist/apps/mpp/internal/Sponsorship.d.ts +36 -0
  197. package/dist/apps/mpp/internal/Sponsorship.d.ts.map +1 -0
  198. package/dist/apps/mpp/internal/Sponsorship.js +103 -0
  199. package/dist/apps/mpp/internal/Sponsorship.js.map +1 -0
  200. package/dist/apps/mpp/internal/Utils.d.ts +7 -0
  201. package/dist/apps/mpp/internal/Utils.d.ts.map +1 -0
  202. package/dist/apps/mpp/internal/Utils.js +24 -0
  203. package/dist/apps/mpp/internal/Utils.js.map +1 -0
  204. package/dist/apps/relay/App.js +183 -23
  205. package/dist/apps/relay/App.js.map +1 -1
  206. package/dist/apps/relay/Sponsorships.d.ts +4 -0
  207. package/dist/apps/relay/Sponsorships.d.ts.map +1 -1
  208. package/dist/apps/relay/Sponsorships.js +15 -6
  209. package/dist/apps/relay/Sponsorships.js.map +1 -1
  210. package/dist/db/Db.d.ts +20 -0
  211. package/dist/db/Db.d.ts.map +1 -1
  212. package/dist/db/Db.js +342 -0
  213. package/dist/db/Db.js.map +1 -1
  214. package/dist/db/Schema.d.ts +921 -1
  215. package/dist/db/Schema.d.ts.map +1 -1
  216. package/dist/db/Schema.js +269 -1
  217. package/dist/db/Schema.js.map +1 -1
  218. package/dist/db/tables/earnVaults.d.ts +68 -0
  219. package/dist/db/tables/earnVaults.d.ts.map +1 -0
  220. package/dist/db/tables/earnVaults.js +72 -0
  221. package/dist/db/tables/earnVaults.js.map +1 -0
  222. package/dist/db/tables/fundingCatalog.d.ts +56 -0
  223. package/dist/db/tables/fundingCatalog.d.ts.map +1 -0
  224. package/dist/db/tables/fundingCatalog.js +85 -0
  225. package/dist/db/tables/fundingCatalog.js.map +1 -0
  226. package/dist/db/tables/fundingDepositAddresses.d.ts +159 -0
  227. package/dist/db/tables/fundingDepositAddresses.d.ts.map +1 -0
  228. package/dist/db/tables/fundingDepositAddresses.js +206 -0
  229. package/dist/db/tables/fundingDepositAddresses.js.map +1 -0
  230. package/dist/db/tables/fundingDeposits.d.ts +115 -0
  231. package/dist/db/tables/fundingDeposits.d.ts.map +1 -0
  232. package/dist/db/tables/fundingDeposits.js +100 -0
  233. package/dist/db/tables/fundingDeposits.js.map +1 -0
  234. package/dist/db/tables/fundingIdempotency.d.ts +91 -0
  235. package/dist/db/tables/fundingIdempotency.d.ts.map +1 -0
  236. package/dist/db/tables/fundingIdempotency.js +103 -0
  237. package/dist/db/tables/fundingIdempotency.js.map +1 -0
  238. package/dist/db/tables/fundingTransferEvents.d.ts +12 -0
  239. package/dist/db/tables/fundingTransferEvents.d.ts.map +1 -0
  240. package/dist/db/tables/fundingTransferEvents.js +18 -0
  241. package/dist/db/tables/fundingTransferEvents.js.map +1 -0
  242. package/dist/db/tables/fundingTransferTransactions.d.ts +15 -0
  243. package/dist/db/tables/fundingTransferTransactions.d.ts.map +1 -0
  244. package/dist/db/tables/fundingTransferTransactions.js +27 -0
  245. package/dist/db/tables/fundingTransferTransactions.js.map +1 -0
  246. package/dist/db/tables/fundingTransfers.d.ts +82 -0
  247. package/dist/db/tables/fundingTransfers.d.ts.map +1 -0
  248. package/dist/db/tables/fundingTransfers.js +89 -0
  249. package/dist/db/tables/fundingTransfers.js.map +1 -0
  250. package/dist/db/tables/organizations.d.ts +8 -0
  251. package/dist/db/tables/organizations.d.ts.map +1 -1
  252. package/dist/db/tables/organizations.js +16 -0
  253. package/dist/db/tables/organizations.js.map +1 -1
  254. package/dist/db/tables/projects.d.ts +8 -0
  255. package/dist/db/tables/projects.d.ts.map +1 -1
  256. package/dist/db/tables/projects.js +16 -0
  257. package/dist/db/tables/projects.js.map +1 -1
  258. package/dist/db/tables/requestUsage.d.ts +13 -0
  259. package/dist/db/tables/requestUsage.d.ts.map +1 -1
  260. package/dist/db/tables/requestUsage.js +16 -0
  261. package/dist/db/tables/requestUsage.js.map +1 -1
  262. package/dist/db/tables/sponsoredTransactions.d.ts +2 -2
  263. package/dist/db/tables/sponsoredTransactions.d.ts.map +1 -1
  264. package/dist/db/tables/sponsoredTransactions.js +1 -1
  265. package/dist/db/tables/sponsoredTransactions.js.map +1 -1
  266. package/dist/db/tables/users.d.ts +8 -0
  267. package/dist/db/tables/users.d.ts.map +1 -1
  268. package/dist/db/tables/users.js +16 -0
  269. package/dist/db/tables/users.js.map +1 -1
  270. package/dist/db/tables/verifiedTokens.d.ts +14 -7
  271. package/dist/db/tables/verifiedTokens.d.ts.map +1 -1
  272. package/dist/db/tables/verifiedTokens.js +9 -7
  273. package/dist/db/tables/verifiedTokens.js.map +1 -1
  274. package/dist/db/tables/webhookSubscriptions.d.ts +31 -1
  275. package/dist/db/tables/webhookSubscriptions.d.ts.map +1 -1
  276. package/dist/db/tables/webhookSubscriptions.js +24 -3
  277. package/dist/db/tables/webhookSubscriptions.js.map +1 -1
  278. package/dist/handlers/relay.js +108 -108
  279. package/dist/handlers/relay.js.map +1 -1
  280. package/dist/internal/Auth.d.ts +26 -0
  281. package/dist/internal/Auth.d.ts.map +1 -1
  282. package/dist/internal/Auth.js +35 -2
  283. package/dist/internal/Auth.js.map +1 -1
  284. package/dist/internal/Cache.d.ts.map +1 -1
  285. package/dist/internal/Cache.js +16 -10
  286. package/dist/internal/Cache.js.map +1 -1
  287. package/dist/internal/Cursor.js +1 -1
  288. package/dist/internal/Cursor.js.map +1 -1
  289. package/dist/internal/EarnRates.d.ts +89 -0
  290. package/dist/internal/EarnRates.d.ts.map +1 -0
  291. package/dist/internal/EarnRates.js +266 -0
  292. package/dist/internal/EarnRates.js.map +1 -0
  293. package/dist/internal/EarnVaults.d.ts +171 -0
  294. package/dist/internal/EarnVaults.d.ts.map +1 -0
  295. package/dist/internal/EarnVaults.js +547 -0
  296. package/dist/internal/EarnVaults.js.map +1 -0
  297. package/dist/internal/EdgeCache.d.ts +8 -1
  298. package/dist/internal/EdgeCache.d.ts.map +1 -1
  299. package/dist/internal/EdgeCache.js +33 -7
  300. package/dist/internal/EdgeCache.js.map +1 -1
  301. package/dist/internal/Id.d.ts +11 -0
  302. package/dist/internal/Id.d.ts.map +1 -1
  303. package/dist/internal/Id.js +13 -0
  304. package/dist/internal/Id.js.map +1 -1
  305. package/dist/internal/Idempotency.d.ts +83 -0
  306. package/dist/internal/Idempotency.d.ts.map +1 -0
  307. package/dist/internal/Idempotency.js +113 -0
  308. package/dist/internal/Idempotency.js.map +1 -0
  309. package/dist/internal/Log.d.ts +64 -9
  310. package/dist/internal/Log.d.ts.map +1 -1
  311. package/dist/internal/Log.js +29 -7
  312. package/dist/internal/Log.js.map +1 -1
  313. package/dist/internal/MetricSink.d.ts.map +1 -1
  314. package/dist/internal/MetricSink.js +47 -1
  315. package/dist/internal/MetricSink.js.map +1 -1
  316. package/dist/internal/OpenApi.d.ts +1 -1
  317. package/dist/internal/OpenApi.d.ts.map +1 -1
  318. package/dist/internal/OpenApi.js +3 -0
  319. package/dist/internal/OpenApi.js.map +1 -1
  320. package/dist/internal/Provider.d.ts +14 -0
  321. package/dist/internal/Provider.d.ts.map +1 -0
  322. package/dist/internal/Provider.js +5 -0
  323. package/dist/internal/Provider.js.map +1 -0
  324. package/dist/internal/Schema.d.ts +6 -1
  325. package/dist/internal/Schema.d.ts.map +1 -1
  326. package/dist/internal/Schema.js +20 -11
  327. package/dist/internal/Schema.js.map +1 -1
  328. package/dist/internal/Store.d.ts +6 -0
  329. package/dist/internal/Store.d.ts.map +1 -1
  330. package/dist/internal/Store.js +35 -0
  331. package/dist/internal/Store.js.map +1 -1
  332. package/dist/internal/Tidx.d.ts +43 -24
  333. package/dist/internal/Tidx.d.ts.map +1 -1
  334. package/dist/internal/Tidx.js +79 -11
  335. package/dist/internal/Tidx.js.map +1 -1
  336. package/dist/internal/Viem.d.ts +82 -65
  337. package/dist/internal/Viem.d.ts.map +1 -1
  338. package/dist/internal/Viem.js +83 -12
  339. package/dist/internal/Viem.js.map +1 -1
  340. package/dist/internal/WebhookDestination.d.ts.map +1 -1
  341. package/dist/internal/WebhookDestination.js +34 -28
  342. package/dist/internal/WebhookDestination.js.map +1 -1
  343. package/dist/internal/Webhooks.d.ts +9 -0
  344. package/dist/internal/Webhooks.d.ts.map +1 -1
  345. package/dist/internal/Webhooks.js +15 -10
  346. package/dist/internal/Webhooks.js.map +1 -1
  347. package/dist/internal/exchange/Provider.d.ts +219 -0
  348. package/dist/internal/exchange/Provider.d.ts.map +1 -0
  349. package/dist/internal/exchange/Provider.js +117 -0
  350. package/dist/internal/exchange/Provider.js.map +1 -0
  351. package/dist/internal/exchange/index.d.ts +2 -0
  352. package/dist/internal/exchange/index.d.ts.map +1 -0
  353. package/dist/internal/exchange/index.js +2 -0
  354. package/dist/internal/exchange/index.js.map +1 -0
  355. package/dist/internal/exchange/providers/uniswap.d.ts +57 -0
  356. package/dist/internal/exchange/providers/uniswap.d.ts.map +1 -0
  357. package/dist/internal/exchange/providers/uniswap.js +553 -0
  358. package/dist/internal/exchange/providers/uniswap.js.map +1 -0
  359. package/dist/internal/funding/Action.d.ts +55 -0
  360. package/dist/internal/funding/Action.d.ts.map +1 -0
  361. package/dist/internal/funding/Action.js +74 -0
  362. package/dist/internal/funding/Action.js.map +1 -0
  363. package/dist/internal/funding/Catalog.d.ts +107 -0
  364. package/dist/internal/funding/Catalog.d.ts.map +1 -0
  365. package/dist/internal/funding/Catalog.js +270 -0
  366. package/dist/internal/funding/Catalog.js.map +1 -0
  367. package/dist/internal/funding/Chain.d.ts +76 -0
  368. package/dist/internal/funding/Chain.d.ts.map +1 -0
  369. package/dist/internal/funding/Chain.js +38 -0
  370. package/dist/internal/funding/Chain.js.map +1 -0
  371. package/dist/internal/funding/Deposit.d.ts +366 -0
  372. package/dist/internal/funding/Deposit.d.ts.map +1 -0
  373. package/dist/internal/funding/Deposit.js +286 -0
  374. package/dist/internal/funding/Deposit.js.map +1 -0
  375. package/dist/internal/funding/DepositAddress.d.ts +345 -0
  376. package/dist/internal/funding/DepositAddress.d.ts.map +1 -0
  377. package/dist/internal/funding/DepositAddress.js +189 -0
  378. package/dist/internal/funding/DepositAddress.js.map +1 -0
  379. package/dist/internal/funding/Evidence.d.ts +52 -0
  380. package/dist/internal/funding/Evidence.d.ts.map +1 -0
  381. package/dist/internal/funding/Evidence.js +314 -0
  382. package/dist/internal/funding/Evidence.js.map +1 -0
  383. package/dist/internal/funding/Provider.d.ts +1186 -0
  384. package/dist/internal/funding/Provider.d.ts.map +1 -0
  385. package/dist/internal/funding/Provider.js +1081 -0
  386. package/dist/internal/funding/Provider.js.map +1 -0
  387. package/dist/internal/funding/Reconciliation.d.ts +113 -0
  388. package/dist/internal/funding/Reconciliation.d.ts.map +1 -0
  389. package/dist/internal/funding/Reconciliation.js +676 -0
  390. package/dist/internal/funding/Reconciliation.js.map +1 -0
  391. package/dist/internal/funding/Route.d.ts +21 -0
  392. package/dist/internal/funding/Route.d.ts.map +1 -0
  393. package/dist/internal/funding/Route.js +5 -0
  394. package/dist/internal/funding/Route.js.map +1 -0
  395. package/dist/internal/funding/Subsidy.d.ts +113 -0
  396. package/dist/internal/funding/Subsidy.d.ts.map +1 -0
  397. package/dist/internal/funding/Subsidy.js +138 -0
  398. package/dist/internal/funding/Subsidy.js.map +1 -0
  399. package/dist/internal/funding/Token.d.ts +41 -0
  400. package/dist/internal/funding/Token.d.ts.map +1 -0
  401. package/dist/internal/funding/Token.js +22 -0
  402. package/dist/internal/funding/Token.js.map +1 -0
  403. package/dist/internal/funding/Transfer.d.ts +643 -0
  404. package/dist/internal/funding/Transfer.d.ts.map +1 -0
  405. package/dist/internal/funding/Transfer.js +332 -0
  406. package/dist/internal/funding/Transfer.js.map +1 -0
  407. package/dist/internal/funding/index.d.ts +13 -0
  408. package/dist/internal/funding/index.d.ts.map +1 -0
  409. package/dist/internal/funding/index.js +13 -0
  410. package/dist/internal/funding/index.js.map +1 -0
  411. package/dist/internal/funding/providers/across.d.ts +21 -0
  412. package/dist/internal/funding/providers/across.d.ts.map +1 -0
  413. package/dist/internal/funding/providers/across.js +117 -0
  414. package/dist/internal/funding/providers/across.js.map +1 -0
  415. package/dist/internal/funding/providers/relay.d.ts +46 -0
  416. package/dist/internal/funding/providers/relay.d.ts.map +1 -0
  417. package/dist/internal/funding/providers/relay.js +464 -0
  418. package/dist/internal/funding/providers/relay.js.map +1 -0
  419. package/dist/internal/funding/providers/rhino.d.ts +15 -0
  420. package/dist/internal/funding/providers/rhino.d.ts.map +1 -0
  421. package/dist/internal/funding/providers/rhino.js +116 -0
  422. package/dist/internal/funding/providers/rhino.js.map +1 -0
  423. package/dist/internal/funding/providers/squid.d.ts +19 -0
  424. package/dist/internal/funding/providers/squid.d.ts.map +1 -0
  425. package/dist/internal/funding/providers/squid.js +120 -0
  426. package/dist/internal/funding/providers/squid.js.map +1 -0
  427. package/dist/internal/funding/providers/symbiosis.d.ts +20 -0
  428. package/dist/internal/funding/providers/symbiosis.d.ts.map +1 -0
  429. package/dist/internal/funding/providers/symbiosis.js +176 -0
  430. package/dist/internal/funding/providers/symbiosis.js.map +1 -0
  431. package/dist/internal/index.d.ts +5 -1
  432. package/dist/internal/index.d.ts.map +1 -1
  433. package/dist/internal/index.js +5 -1
  434. package/dist/internal/index.js.map +1 -1
  435. package/dist/internal/webhookDestinations/url.js +61 -57
  436. package/dist/internal/webhookDestinations/url.js.map +1 -1
  437. package/dist/providers/index.d.ts +43 -0
  438. package/dist/providers/index.d.ts.map +1 -0
  439. package/dist/providers/index.js +19 -0
  440. package/dist/providers/index.js.map +1 -0
  441. package/package.json +9 -4
  442. package/src/ApiKeys.test.ts +11 -7
  443. package/src/ApiKeys.ts +37 -9
  444. package/src/App.test.ts +85 -10
  445. package/src/App.ts +224 -137
  446. package/src/Assets.ts +29 -4
  447. package/src/Client.test-d.ts +650 -30
  448. package/src/Scope.test.ts +19 -0
  449. package/src/Scope.ts +28 -11
  450. package/src/Webhooks.test.ts +25 -0
  451. package/src/Webhooks.ts +1 -1
  452. package/src/admin/App.test.ts +9 -2
  453. package/src/admin/App.ts +32 -9
  454. package/src/admin/apps/api-keys.test.ts +167 -23
  455. package/src/admin/apps/api-keys.ts +166 -28
  456. package/src/admin/apps/earn-vaults.test.ts +338 -0
  457. package/src/admin/apps/earn-vaults.ts +572 -0
  458. package/src/admin/apps/scopes.test.ts +16 -1
  459. package/src/admin/ui/plugin.ts +2 -2
  460. package/src/admin/ui/src/components/EarnVaultForm.tsx +137 -0
  461. package/src/admin/ui/src/components/MobileProfile.tsx +2 -2
  462. package/src/admin/ui/src/components/Page.tsx +1 -1
  463. package/src/admin/ui/src/components/ThemeToggle.tsx +1 -1
  464. package/src/admin/ui/src/lib/api-keys.ts +37 -7
  465. package/src/admin/ui/src/lib/client.ts +1 -1
  466. package/src/admin/ui/src/lib/config.ts +1 -1
  467. package/src/admin/ui/src/lib/early-access.ts +8 -9
  468. package/src/admin/ui/src/lib/earn-vaults.ts +207 -0
  469. package/src/admin/ui/src/lib/format.ts +13 -0
  470. package/src/admin/ui/src/lib/organizations.test.ts +14 -4
  471. package/src/admin/ui/src/lib/organizations.ts +7 -1
  472. package/src/admin/ui/src/lib/response.ts +22 -0
  473. package/src/admin/ui/src/lib/scopes.test.ts +19 -11
  474. package/src/admin/ui/src/lib/scopes.ts +28 -7
  475. package/src/admin/ui/src/lib/verified-tokens.ts +10 -9
  476. package/src/admin/ui/src/routeTree.gen.ts +70 -7
  477. package/src/admin/ui/src/router.tsx +2 -2
  478. package/src/admin/ui/src/routes/__root.tsx +9 -8
  479. package/src/admin/ui/src/routes/api-keys.tsx +475 -64
  480. package/src/admin/ui/src/routes/early-access.tsx +5 -12
  481. package/src/admin/ui/src/routes/earn.vaults.tsx +348 -0
  482. package/src/admin/ui/src/routes/earn.vaults_.$vaultId.edit.tsx +154 -0
  483. package/src/admin/ui/src/routes/earn.vaults_.new.tsx +190 -0
  484. package/src/admin/ui/src/routes/organizations.tsx +4 -7
  485. package/src/admin/ui/src/routes/organizations_.$orgId.tsx +56 -54
  486. package/src/admin/ui/src/routes/verified-tokens.tsx +4 -4
  487. package/src/admin/ui/vite.config.ts +4 -1
  488. package/src/admin/ui.generated.ts +113 -43
  489. package/src/analytics/Analytics.test.ts +69 -10
  490. package/src/analytics/Analytics.ts +20 -1
  491. package/src/analytics/tables/requestEvents.ts +79 -37
  492. package/src/apps/Zones.test.ts +33 -0
  493. package/src/apps/Zones.ts +40 -0
  494. package/src/apps/composition.test.ts +84 -1
  495. package/src/apps/data/App.test.ts +28 -11
  496. package/src/apps/data/App.ts +96 -33
  497. package/src/apps/data/FxOracle.ts +1 -1
  498. package/src/apps/data/index.ts +0 -1
  499. package/src/apps/data/routes/activities.test.ts +82 -6
  500. package/src/apps/data/routes/activities.ts +131 -71
  501. package/src/apps/data/routes/balances.test.ts +50 -37
  502. package/src/apps/data/routes/balances.ts +5 -5
  503. package/src/apps/data/routes/blocks.test.ts +2 -0
  504. package/src/apps/data/routes/coingecko.test.ts +22 -0
  505. package/src/apps/data/routes/coingecko.ts +84 -48
  506. package/src/apps/data/routes/earn.test.ts +1108 -0
  507. package/src/apps/data/routes/earn.ts +1391 -0
  508. package/src/apps/data/routes/exchanges.test.ts +82 -33
  509. package/src/apps/data/routes/exchanges.ts +842 -227
  510. package/src/apps/data/routes/fee-amm.test.ts +4 -6
  511. package/src/apps/data/routes/fee-amm.ts +69 -12
  512. package/src/apps/data/routes/funding.test.ts +2712 -221
  513. package/src/apps/data/routes/funding.ts +1604 -387
  514. package/src/apps/data/routes/indexer.test.ts +43 -34
  515. package/src/apps/data/routes/indexer.ts +19 -2
  516. package/src/apps/data/routes/receipts.test.ts +5 -3
  517. package/src/apps/data/routes/receipts.ts +13 -11
  518. package/src/apps/data/routes/rpc.test.ts +446 -28
  519. package/src/apps/data/routes/rpc.ts +243 -25
  520. package/src/apps/data/routes/tokenlist.ts +72 -19
  521. package/src/apps/data/routes/tokens.ts +258 -86
  522. package/src/apps/data/routes/transactions.test.ts +2 -0
  523. package/src/apps/data/routes/transactions.ts +77 -42
  524. package/src/apps/data/routes/transfers.test.ts +166 -4
  525. package/src/apps/data/routes/transfers.ts +216 -78
  526. package/src/apps/data/routes/valuation.test.ts +62 -9
  527. package/src/apps/data/routes/valuation.ts +2 -2
  528. package/src/apps/data/routes/webhooks.test.ts +386 -43
  529. package/src/apps/data/routes/webhooks.ts +172 -71
  530. package/src/apps/data/routes/zones.test.ts +473 -0
  531. package/src/apps/data/routes/zones.ts +338 -0
  532. package/src/apps/docs/reference.ts +8 -6
  533. package/src/apps/index.ts +2 -1
  534. package/src/apps/management/Billing.test.ts +29 -0
  535. package/src/apps/management/Billing.ts +23 -0
  536. package/src/apps/management/Error.ts +12 -0
  537. package/src/apps/management/routes/api-keys.test.ts +5 -3
  538. package/src/apps/management/routes/api-keys.ts +2 -9
  539. package/src/apps/management/routes/invitations.ts +4 -11
  540. package/src/apps/management/routes/orgs.ts +2 -9
  541. package/src/apps/management/routes/scopes.test.ts +7 -2
  542. package/src/apps/management/routes/scopes.ts +2 -2
  543. package/src/apps/management/routes/webhooks.test.ts +6 -1
  544. package/src/apps/management/routes/webhooks.ts +15 -3
  545. package/src/apps/mpp/App.ts +276 -29
  546. package/src/apps/mpp/Mpp.test.ts +631 -26
  547. package/src/apps/mpp/Mpp.ts +0 -42
  548. package/src/apps/mpp/internal/Adapter.ts +108 -3
  549. package/src/apps/mpp/internal/Error.test.ts +33 -0
  550. package/src/apps/mpp/internal/Error.ts +44 -6
  551. package/src/apps/mpp/internal/Idempotency.test.ts +213 -0
  552. package/src/apps/mpp/internal/Idempotency.ts +59 -5
  553. package/src/apps/mpp/internal/Policy.test.ts +122 -0
  554. package/src/apps/mpp/internal/Policy.ts +98 -0
  555. package/src/apps/mpp/internal/ScreenManager.test.ts +68 -0
  556. package/src/apps/mpp/internal/ScreenManager.ts +41 -0
  557. package/src/apps/mpp/internal/Screening.ts +40 -3
  558. package/src/apps/mpp/internal/Sponsorship.ts +144 -0
  559. package/src/apps/mpp/internal/Utils.ts +30 -0
  560. package/src/apps/relay/App.test.ts +493 -62
  561. package/src/apps/relay/App.ts +233 -23
  562. package/src/apps/relay/Sponsorships.test.ts +49 -0
  563. package/src/apps/relay/Sponsorships.ts +18 -6
  564. package/src/db/Db.ts +439 -0
  565. package/src/db/Schema.ts +325 -2
  566. package/src/db/tables/earnVaults.test.ts +185 -0
  567. package/src/db/tables/earnVaults.ts +151 -0
  568. package/src/db/tables/fundingCatalog.test.ts +108 -0
  569. package/src/db/tables/fundingCatalog.ts +157 -0
  570. package/src/db/tables/fundingDepositAddresses.test.ts +580 -0
  571. package/src/db/tables/fundingDepositAddresses.ts +376 -0
  572. package/src/db/tables/fundingDeposits.test.ts +261 -0
  573. package/src/db/tables/fundingDeposits.ts +229 -0
  574. package/src/db/tables/fundingIdempotency.test.ts +182 -0
  575. package/src/db/tables/fundingIdempotency.ts +180 -0
  576. package/src/db/tables/fundingTransferEvents.ts +28 -0
  577. package/src/db/tables/fundingTransferTransactions.test.ts +69 -0
  578. package/src/db/tables/fundingTransferTransactions.ts +37 -0
  579. package/src/db/tables/fundingTransfers.test.ts +89 -0
  580. package/src/db/tables/fundingTransfers.ts +153 -0
  581. package/src/db/tables/organizations.ts +16 -0
  582. package/src/db/tables/projects.ts +16 -0
  583. package/src/db/tables/requestUsage.ts +23 -0
  584. package/src/db/tables/sponsoredTransactions.test.ts +11 -0
  585. package/src/db/tables/sponsoredTransactions.ts +3 -3
  586. package/src/db/tables/users.ts +16 -0
  587. package/src/db/tables/verifiedTokens.ts +19 -14
  588. package/src/db/tables/webhookSubscriptions.ts +58 -3
  589. package/src/handlers/relay.test.ts +2 -0
  590. package/src/handlers/relay.ts +95 -99
  591. package/src/internal/Auth.test.ts +48 -0
  592. package/src/internal/Auth.ts +61 -2
  593. package/src/internal/Cache.ts +13 -9
  594. package/src/internal/Cursor.test.ts +2 -2
  595. package/src/internal/Cursor.ts +1 -1
  596. package/src/internal/EarnRates.test.ts +125 -0
  597. package/src/internal/EarnRates.ts +409 -0
  598. package/src/internal/EarnVaults.test.ts +195 -0
  599. package/src/internal/EarnVaults.ts +876 -0
  600. package/src/internal/EdgeCache.test.ts +19 -0
  601. package/src/internal/EdgeCache.ts +35 -5
  602. package/src/internal/Id.ts +14 -0
  603. package/src/internal/Idempotency.test.ts +109 -0
  604. package/src/internal/Idempotency.ts +237 -0
  605. package/src/internal/Log.test-d.ts +41 -8
  606. package/src/internal/Log.test.ts +189 -8
  607. package/src/internal/Log.ts +124 -17
  608. package/src/internal/MetricSink.test.ts +244 -4
  609. package/src/internal/MetricSink.ts +51 -1
  610. package/src/internal/OpenApi.ts +3 -0
  611. package/src/internal/Provider.test.ts +17 -0
  612. package/src/internal/Provider.ts +17 -0
  613. package/src/internal/Schema.ts +27 -17
  614. package/src/internal/Store.test.ts +25 -0
  615. package/src/internal/Store.ts +46 -0
  616. package/src/internal/Tidx.local.test.ts +78 -3
  617. package/src/internal/Tidx.test.ts +212 -18
  618. package/src/internal/Tidx.ts +112 -32
  619. package/src/internal/VerifiedTokens.test.ts +15 -0
  620. package/src/internal/Viem.test.ts +170 -17
  621. package/src/internal/Viem.ts +111 -28
  622. package/src/internal/WebhookDestination.ts +25 -23
  623. package/src/internal/Webhooks.test.ts +59 -0
  624. package/src/internal/Webhooks.ts +34 -10
  625. package/src/internal/exchange/Provider.test.ts +216 -0
  626. package/src/internal/exchange/Provider.ts +326 -0
  627. package/src/internal/exchange/index.test.ts +6 -0
  628. package/src/internal/exchange/index.ts +1 -0
  629. package/src/internal/exchange/providers/uniswap.test.ts +122 -0
  630. package/src/internal/exchange/providers/uniswap.ts +682 -0
  631. package/src/internal/funding/Action.test.ts +138 -0
  632. package/src/internal/funding/Action.ts +117 -0
  633. package/src/internal/funding/Catalog.test.ts +311 -0
  634. package/src/internal/funding/Catalog.ts +347 -0
  635. package/src/internal/funding/Chain.test.ts +79 -0
  636. package/src/internal/funding/Chain.ts +120 -0
  637. package/src/internal/funding/Deposit.test.ts +26 -0
  638. package/src/internal/funding/Deposit.ts +453 -0
  639. package/src/internal/funding/DepositAddress.test.ts +18 -0
  640. package/src/internal/funding/DepositAddress.ts +312 -0
  641. package/src/internal/funding/Evidence.test.ts +297 -0
  642. package/src/internal/funding/Evidence.ts +430 -0
  643. package/src/internal/funding/Provider.test.ts +1328 -0
  644. package/src/internal/funding/Provider.ts +1796 -0
  645. package/src/internal/funding/Reconciliation.local.test.ts +504 -0
  646. package/src/internal/funding/Reconciliation.test.ts +2020 -0
  647. package/src/internal/funding/Reconciliation.ts +930 -0
  648. package/src/internal/funding/Route.test.ts +66 -0
  649. package/src/internal/funding/Route.ts +29 -0
  650. package/src/internal/funding/Subsidy.local.test.ts +152 -0
  651. package/src/internal/funding/Subsidy.test.ts +75 -0
  652. package/src/internal/funding/Subsidy.ts +260 -0
  653. package/src/internal/funding/Token.test.ts +47 -0
  654. package/src/internal/funding/Token.ts +59 -0
  655. package/src/internal/funding/Transfer.test.ts +369 -0
  656. package/src/internal/funding/Transfer.ts +522 -0
  657. package/src/internal/funding/index.test.ts +20 -0
  658. package/src/internal/funding/index.ts +12 -0
  659. package/src/internal/funding/providers/across.ts +149 -0
  660. package/src/{apps/data/FundingQuoteProviders.test.ts → internal/funding/providers/index.test.ts} +178 -157
  661. package/src/internal/funding/providers/relay.test.ts +474 -0
  662. package/src/internal/funding/providers/relay.ts +559 -0
  663. package/src/internal/funding/providers/rhino.ts +139 -0
  664. package/src/internal/funding/providers/squid.ts +151 -0
  665. package/src/internal/funding/providers/symbiosis.ts +221 -0
  666. package/src/internal/index.test.ts +5 -1
  667. package/src/internal/index.ts +5 -1
  668. package/src/internal/webhookDestinations/url.ts +49 -45
  669. package/src/providers/index.test.ts +14 -0
  670. package/src/providers/index.ts +54 -0
  671. package/dist/apps/data/FundingQuoteProviders.d.ts +0 -174
  672. package/dist/apps/data/FundingQuoteProviders.d.ts.map +0 -1
  673. package/dist/apps/data/FundingQuoteProviders.js +0 -775
  674. package/dist/apps/data/FundingQuoteProviders.js.map +0 -1
  675. package/dist/internal/FundingQuotes.d.ts +0 -864
  676. package/dist/internal/FundingQuotes.d.ts.map +0 -1
  677. package/dist/internal/FundingQuotes.js +0 -672
  678. package/dist/internal/FundingQuotes.js.map +0 -1
  679. package/src/apps/data/FundingQuoteProviders.ts +0 -1043
  680. package/src/internal/FundingQuotes.test.ts +0 -456
  681. package/src/internal/FundingQuotes.ts +0 -944
@@ -0,0 +1,1328 @@
1
+ import * as TestFunding from '../../../test/Funding.js'
2
+ import * as FundingCatalog from './Catalog.js'
3
+ import * as FundingProvider from './Provider.js'
4
+ import * as Across from './providers/across.js'
5
+ import * as Relay from './providers/relay.js'
6
+ import * as Rhino from './providers/rhino.js'
7
+ import * as Squid from './providers/squid.js'
8
+ import * as Symbiosis from './providers/symbiosis.js'
9
+
10
+ const providers = [
11
+ Across.across({
12
+ apiKey: 'across-api-key',
13
+ integratorId: '0xdead',
14
+ userAddress: '0x1111111111111111111111111111111111111111',
15
+ }),
16
+ Relay.relay({ userAddress: '0x1111111111111111111111111111111111111111' }),
17
+ Rhino.rhino(),
18
+ Squid.squid({
19
+ integratorId: 'squid-integrator-id',
20
+ userAddress: '0x1111111111111111111111111111111111111111',
21
+ }),
22
+ Symbiosis.symbiosis({ userAddress: '0x1111111111111111111111111111111111111111' }),
23
+ ] as const
24
+
25
+ const baseUsdcRequest = {
26
+ catalog: TestFunding.snapshot,
27
+ provider: 'relay',
28
+ sourceAmount: '1000000',
29
+ sourceAmountUnits: 'baseUnits',
30
+ sourceChain: 'base',
31
+ sourceToken: 'USDC',
32
+ } as const
33
+
34
+ const depositAddressOptions = {
35
+ amount: '100000000',
36
+ catalog: TestFunding.snapshot,
37
+ destinationChainId: 4217,
38
+ destinationToken: '0x20c00000000000000000000014f22ca97301eb73',
39
+ recipient: `0x${'11'.repeat(20)}`,
40
+ refundAddress: 'TJRabPrwbZy45sbavfcjinPJC18kjpRTv8',
41
+ sourceChain: 'tron',
42
+ sourceToken: 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t',
43
+ subsidize: false,
44
+ } as const
45
+
46
+ const prepareOptions = {
47
+ catalog: TestFunding.snapshot,
48
+ destinationChainId: 4217,
49
+ destinationToken: '0x20c0000000000000000000000000000000000000',
50
+ method: 'transaction',
51
+ mode: 'exactSource',
52
+ recipient: `0x${'11'.repeat(20)}`,
53
+ sender: `0x${'22'.repeat(20)}`,
54
+ sourceAmount: '1000000',
55
+ sourceChain: 'base',
56
+ sourceToken: '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913',
57
+ } as const
58
+
59
+ describe('from', () => {
60
+ test('registers the funding provider type', () => {
61
+ expect(providers[0]?.type).toBe('funding')
62
+ })
63
+
64
+ test('rejects provider ids that are not normalized', () => {
65
+ expect(() =>
66
+ FundingProvider.from({
67
+ getQuote: async () => ({ sampledAt: '2026-01-01T00:00:00Z', status: 'unavailable' }),
68
+ id: ' Relay ',
69
+ name: 'Relay',
70
+ }),
71
+ ).toThrowErrorMatchingInlineSnapshot(
72
+ `[FundingProvider.ProviderConfigurationError: Provider ids must be lowercase and trimmed.]`,
73
+ )
74
+ })
75
+ })
76
+
77
+ describe('is', () => {
78
+ test('requires the funding quote capability', () => {
79
+ expect(
80
+ FundingProvider.is({
81
+ id: 'incomplete',
82
+ name: 'Incomplete',
83
+ type: 'funding',
84
+ }),
85
+ ).toBe(false)
86
+ expect(FundingProvider.is(providers[0])).toBe(true)
87
+ })
88
+ })
89
+
90
+ describe('getQuotes', () => {
91
+ test('returns normalized successful provider quotes', async () => {
92
+ const attempts: FundingProvider.ProviderAttempt[] = []
93
+ let seen: FundingProvider.getQuote.Parameters | undefined
94
+ const quotes = await FundingProvider.getQuotes({
95
+ ...baseUsdcRequest,
96
+ onAttempt: (attempt) => attempts.push(attempt),
97
+ providers: [
98
+ withGetQuote('relay', async (input, signal) => {
99
+ seen = input
100
+ expect(signal.aborted).toBe(false)
101
+ return {
102
+ destinationAmountMin: '980000',
103
+ destinationAmount: '990000',
104
+ quality: {
105
+ estimatedSeconds: 4,
106
+ liquiditySource: 'providerQuote',
107
+ sourceDetail: 'providerEndpoint',
108
+ tier: 'liquid',
109
+ },
110
+ sampledAt: '2026-01-01T00:00:00Z',
111
+ status: 'available',
112
+ }
113
+ }),
114
+ ],
115
+ })
116
+
117
+ expect(seen?.candidate.id).toBe('base-usdc-tempo-usdce-relay')
118
+ expect(quotes).toHaveLength(1)
119
+ expect(quotes[0]).toMatchObject({
120
+ destinationAmount: {
121
+ baseUnits: '990000',
122
+ currency: 'USD',
123
+ decimals: 6,
124
+ formatted: '0.99',
125
+ },
126
+ destinationAmountMin: {
127
+ baseUnits: '980000',
128
+ currency: 'USD',
129
+ decimals: 6,
130
+ formatted: '0.98',
131
+ },
132
+ id: 'base-usdc-tempo-usdce-relay',
133
+ quality: {
134
+ liquiditySource: 'providerQuote',
135
+ tier: 'liquid',
136
+ },
137
+ quote: { sampledAt: '2026-01-01T00:00:00Z' },
138
+ sourceAmount: {
139
+ baseUnits: '1000000',
140
+ currency: 'USD',
141
+ decimals: 6,
142
+ formatted: '1',
143
+ },
144
+ })
145
+ expect(attempts.map(stableAttempt)).toEqual([
146
+ { operation: 'getQuote', outcome: 'available', provider: 'relay' },
147
+ ])
148
+ expect(attempts[0]?.durationMs).toEqual(expect.any(Number))
149
+ })
150
+
151
+ test('omits provider-declared unavailable quotes', async () => {
152
+ const attempts: FundingProvider.ProviderAttempt[] = []
153
+ const quotes = FundingProvider.getQuotes({
154
+ ...baseUsdcRequest,
155
+ onAttempt: (attempt) => attempts.push(attempt),
156
+ providers: [
157
+ withGetQuote('relay', async () => ({
158
+ sampledAt: '2026-01-01T00:00:00Z',
159
+ status: 'unavailable',
160
+ })),
161
+ ],
162
+ })
163
+
164
+ await expect(quotes).resolves.toEqual([])
165
+ expect(attempts.map(stableAttempt)).toEqual([
166
+ { operation: 'getQuote', outcome: 'unavailable', provider: 'relay' },
167
+ ])
168
+ })
169
+
170
+ test('rejects provider failures when no quote succeeds', async () => {
171
+ const attempts: FundingProvider.ProviderAttempt[] = []
172
+ const quotes = FundingProvider.getQuotes({
173
+ ...baseUsdcRequest,
174
+ onAttempt: (attempt) => attempts.push(attempt),
175
+ providers: [
176
+ withGetQuote('relay', async () => {
177
+ throw new Error('upstream unavailable')
178
+ }),
179
+ ],
180
+ })
181
+
182
+ const cause = await quotes.catch((error: unknown) => error)
183
+ expect(cause).toMatchObject({
184
+ failures: [{ cause: expect.any(Error), provider: { id: 'relay' } }],
185
+ fundingProviderAttempts: [
186
+ {
187
+ durationMs: expect.any(Number),
188
+ failure: 'unknown',
189
+ id: 'relay',
190
+ operation: 'getQuote',
191
+ outcome: 'failed',
192
+ },
193
+ ],
194
+ name: 'FundingProvider.ProviderQuoteError',
195
+ providerFailure: { failure: 'unknown', id: 'relay', operation: 'getQuote' },
196
+ })
197
+ expect(attempts.map(stableAttempt)).toEqual([
198
+ {
199
+ cause: expect.any(Error),
200
+ operation: 'getQuote',
201
+ outcome: 'failed',
202
+ provider: 'relay',
203
+ },
204
+ ])
205
+ })
206
+
207
+ test('records thin quotes separately from available quotes', async () => {
208
+ const attempts: FundingProvider.ProviderAttempt[] = []
209
+ const quotes = await FundingProvider.getQuotes({
210
+ ...baseUsdcRequest,
211
+ onAttempt: (attempt) => attempts.push(attempt),
212
+ providers: [
213
+ withGetQuote('relay', async () => ({
214
+ destinationAmount: '990000',
215
+ sampledAt: '2026-01-01T00:00:00Z',
216
+ status: 'thin',
217
+ })),
218
+ ],
219
+ })
220
+
221
+ expect(quotes).toHaveLength(1)
222
+ expect(attempts.map(stableAttempt)).toEqual([
223
+ { operation: 'getQuote', outcome: 'thin', provider: 'relay' },
224
+ ])
225
+ })
226
+
227
+ test('rejects successful provider responses without output amounts', async () => {
228
+ const quotes = FundingProvider.getQuotes({
229
+ ...baseUsdcRequest,
230
+ providers: [
231
+ withGetQuote('relay', async () => ({
232
+ sampledAt: '2026-01-01T00:00:00Z',
233
+ status: 'available',
234
+ })),
235
+ ],
236
+ })
237
+
238
+ await expect(quotes).rejects.toThrowErrorMatchingInlineSnapshot(
239
+ `[FundingProvider.ProviderQuoteError]`,
240
+ )
241
+ })
242
+
243
+ test('rejects provider responses outside the shared schema', async () => {
244
+ const quotes = FundingProvider.getQuotes({
245
+ ...baseUsdcRequest,
246
+ providers: [
247
+ withGetQuote(
248
+ 'relay',
249
+ async () =>
250
+ ({
251
+ destinationAmount: 'invalid',
252
+ sampledAt: 'invalid',
253
+ status: 'available',
254
+ }) as never,
255
+ ),
256
+ ],
257
+ })
258
+
259
+ await expect(quotes).rejects.toMatchObject({
260
+ providerFailure: { failure: 'payload', id: 'relay', operation: 'getQuote' },
261
+ })
262
+ })
263
+
264
+ test.each([
265
+ { destinationAmountMin: undefined, destinationAmount: '0' },
266
+ { destinationAmountMin: '0', destinationAmount: '990000' },
267
+ { destinationAmountMin: '1000000', destinationAmount: '990000' },
268
+ ])(
269
+ 'rejects invalid successful amounts %#',
270
+ async ({ destinationAmountMin, destinationAmount }) => {
271
+ const quotes = FundingProvider.getQuotes({
272
+ ...baseUsdcRequest,
273
+ providers: [
274
+ withGetQuote('relay', async () => ({
275
+ ...(destinationAmountMin === undefined ? {} : { destinationAmountMin }),
276
+ destinationAmount,
277
+ sampledAt: '2026-01-01T00:00:00Z',
278
+ status: 'available',
279
+ })),
280
+ ],
281
+ })
282
+
283
+ await expect(quotes).rejects.toMatchObject({
284
+ providerFailure: { failure: 'payload', id: 'relay', operation: 'getQuote' },
285
+ })
286
+ },
287
+ )
288
+
289
+ test('aborts providers that exceed the quote deadline', async () => {
290
+ const attempts: FundingProvider.ProviderAttempt[] = []
291
+ let aborted = false
292
+ const quotes = FundingProvider.getQuotes({
293
+ ...baseUsdcRequest,
294
+ providerTimeoutMs: 1,
295
+ providers: [
296
+ withGetQuote('relay', (_input, signal) => {
297
+ signal.addEventListener('abort', () => {
298
+ aborted = true
299
+ })
300
+ return new Promise<FundingProvider.getQuote.ReturnType>(() => {})
301
+ }),
302
+ ],
303
+ onAttempt: (attempt) => attempts.push(attempt),
304
+ })
305
+
306
+ await expect(quotes).rejects.toThrowErrorMatchingInlineSnapshot(
307
+ `[FundingProvider.ProviderQuoteError]`,
308
+ )
309
+ expect(aborted).toBe(true)
310
+ expect(attempts.map(stableAttempt)).toEqual([
311
+ {
312
+ cause: expect.any(FundingProvider.ProviderTimeoutError),
313
+ operation: 'getQuote',
314
+ outcome: 'failed',
315
+ provider: 'relay',
316
+ },
317
+ ])
318
+ })
319
+
320
+ test('passes cache policy through to providers', async () => {
321
+ let seen: FundingProvider.getQuote.Parameters | undefined
322
+ await FundingProvider.getQuotes({
323
+ ...baseUsdcRequest,
324
+ cache: { bypass: true },
325
+ providers: [
326
+ withGetQuote('relay', async (input) => {
327
+ seen = input
328
+ return { sampledAt: '2026-01-01T00:00:00Z', status: 'unavailable' }
329
+ }),
330
+ ],
331
+ })
332
+
333
+ expect(seen?.cache.bypass).toBe(true)
334
+ })
335
+
336
+ test('returns successful quotes when another provider fails', async () => {
337
+ const attempts: FundingProvider.ProviderAttempt[] = []
338
+ const quotes = await FundingProvider.getQuotes({
339
+ ...baseUsdcRequest,
340
+ onAttempt: (attempt) => attempts.push(attempt),
341
+ provider: undefined,
342
+ providers: [
343
+ withGetQuote('across', async () => {
344
+ throw new Error('upstream unavailable')
345
+ }),
346
+ withGetQuote('relay', async () => ({
347
+ destinationAmount: '990000',
348
+ sampledAt: '2026-01-01T00:00:00Z',
349
+ status: 'available',
350
+ })),
351
+ ],
352
+ })
353
+
354
+ expect(quotes).toHaveLength(1)
355
+ expect(quotes[0]?.provider.id).toBe('relay')
356
+ expect(attempts.map(stableAttempt)).toEqual([
357
+ {
358
+ cause: expect.any(Error),
359
+ operation: 'getQuote',
360
+ outcome: 'failed',
361
+ provider: 'across',
362
+ },
363
+ { operation: 'getQuote', outcome: 'available', provider: 'relay' },
364
+ ])
365
+ })
366
+
367
+ test('does not let attempt observers change quote outcomes', async () => {
368
+ const providers_success = [
369
+ withGetQuote('relay', async () => ({
370
+ destinationAmount: '990000',
371
+ sampledAt: '2026-01-01T00:00:00Z',
372
+ status: 'available',
373
+ })),
374
+ ]
375
+ await expect(
376
+ FundingProvider.getQuotes({
377
+ ...baseUsdcRequest,
378
+ onAttempt() {
379
+ throw new Error('observer failed')
380
+ },
381
+ providers: providers_success,
382
+ }),
383
+ ).resolves.toHaveLength(1)
384
+
385
+ await expect(
386
+ FundingProvider.getQuotes({
387
+ ...baseUsdcRequest,
388
+ onAttempt() {
389
+ throw new Error('observer failed')
390
+ },
391
+ providers: [
392
+ withGetQuote('relay', async () => {
393
+ throw new Error('provider failed')
394
+ }),
395
+ ],
396
+ }),
397
+ ).rejects.toThrowErrorMatchingInlineSnapshot(`[FundingProvider.ProviderQuoteError]`)
398
+ })
399
+ })
400
+
401
+ describe('createDepositAddress', () => {
402
+ test('rejects invalid base-unit amounts', async () => {
403
+ await expect(
404
+ FundingProvider.createDepositAddress({
405
+ ...depositAddressOptions,
406
+ amount: '10.5',
407
+ providers: [withCreateDepositAddress('relay', async () => depositAddressResult())],
408
+ }),
409
+ ).rejects.toThrowErrorMatchingInlineSnapshot(`[FundingProvider.InvalidAmountError]`)
410
+ })
411
+
412
+ test('provisions one deterministic capability-enabled candidate', async () => {
413
+ const attempts: FundingProvider.ProviderAttempt[] = []
414
+ let seen: FundingProvider.createDepositAddress.Parameters | undefined
415
+ const selection = await FundingProvider.createDepositAddress({
416
+ ...depositAddressOptions,
417
+ onAttempt: (attempt) => attempts.push(attempt),
418
+ providers: [
419
+ withCreateDepositAddress('relay', async (input) => {
420
+ seen = input
421
+ return {
422
+ address: 'TJRabPrwbZy45sbavfcjinPJC18kjpRTv8',
423
+ correlation: { checkEndpoint: '/requests/request_1', requestId: 'request_1' },
424
+ destinationAmount: '1000000',
425
+ destinationAmountMin: '1000000',
426
+ fees: [],
427
+ sampledAt: '2026-08-04T00:00:00.000Z',
428
+ }
429
+ }),
430
+ ],
431
+ })
432
+
433
+ expect(seen).toMatchObject({
434
+ amount: depositAddressOptions.amount,
435
+ candidate: {
436
+ destinationToken: { symbol: 'USDT0' },
437
+ sourceChain: { id: 'tron:0x2b6653dc' },
438
+ sourceToken: { symbol: 'USDT' },
439
+ },
440
+ recipient: depositAddressOptions.recipient,
441
+ refundAddress: depositAddressOptions.refundAddress,
442
+ subsidize: false,
443
+ })
444
+ expect(selection?.result).toEqual({
445
+ address: 'TJRabPrwbZy45sbavfcjinPJC18kjpRTv8',
446
+ correlation: { checkEndpoint: '/requests/request_1', requestId: 'request_1' },
447
+ destinationAmount: '1000000',
448
+ destinationAmountMin: '1000000',
449
+ fees: [],
450
+ sampledAt: '2026-08-04T00:00:00.000Z',
451
+ })
452
+ expect(attempts.map(stableAttempt)).toEqual([
453
+ { operation: 'createDepositAddress', outcome: 'available', provider: 'relay' },
454
+ ])
455
+ })
456
+
457
+ test('returns no selection when the provider reports no route', async () => {
458
+ const attempts: FundingProvider.ProviderAttempt[] = []
459
+ await expect(
460
+ FundingProvider.createDepositAddress({
461
+ ...depositAddressOptions,
462
+ onAttempt: (attempt) => attempts.push(attempt),
463
+ providers: [
464
+ withCreateDepositAddress('relay', async () => {
465
+ throw new FundingProvider.ProviderUnavailableError()
466
+ }),
467
+ ],
468
+ }),
469
+ ).resolves.toBeUndefined()
470
+ expect(attempts.map(stableAttempt)).toEqual([
471
+ { operation: 'createDepositAddress', outcome: 'unavailable', provider: 'relay' },
472
+ ])
473
+ })
474
+
475
+ test('rejects malformed provider responses', async () => {
476
+ await expect(
477
+ FundingProvider.createDepositAddress({
478
+ ...depositAddressOptions,
479
+ providers: [
480
+ withCreateDepositAddress('relay', async () => ({
481
+ address: '',
482
+ correlation: { requestId: '' },
483
+ destinationAmount: '1000000',
484
+ destinationAmountMin: '1000000',
485
+ fees: [],
486
+ sampledAt: '2026-08-04T00:00:00.000Z',
487
+ })),
488
+ ],
489
+ }),
490
+ ).rejects.toMatchObject({
491
+ providerFailure: { failure: 'payload', id: 'relay', operation: 'createDepositAddress' },
492
+ })
493
+ })
494
+
495
+ test('rejects provider addresses that do not match the source chain', async () => {
496
+ await expect(
497
+ FundingProvider.createDepositAddress({
498
+ ...depositAddressOptions,
499
+ providers: [
500
+ withCreateDepositAddress('relay', async () => ({
501
+ ...depositAddressResult(),
502
+ address: `0x${'11'.repeat(20)}`,
503
+ })),
504
+ ],
505
+ }),
506
+ ).rejects.toMatchObject({
507
+ providerFailure: { failure: 'payload', id: 'relay', operation: 'createDepositAddress' },
508
+ })
509
+ })
510
+
511
+ test('rejects provider minimums above the estimated destination amount', async () => {
512
+ await expect(
513
+ FundingProvider.createDepositAddress({
514
+ ...depositAddressOptions,
515
+ providers: [
516
+ withCreateDepositAddress('relay', async () => ({
517
+ ...depositAddressResult(),
518
+ destinationAmountMin: '1000001',
519
+ })),
520
+ ],
521
+ }),
522
+ ).rejects.toMatchObject({
523
+ providerFailure: { failure: 'payload', id: 'relay', operation: 'createDepositAddress' },
524
+ })
525
+ })
526
+
527
+ test('rejects expired provider terms', async () => {
528
+ await expect(
529
+ FundingProvider.createDepositAddress({
530
+ ...depositAddressOptions,
531
+ providers: [
532
+ withCreateDepositAddress('relay', async () => ({
533
+ ...depositAddressResult(),
534
+ expiresAt: '2000-01-01T00:00:00.000Z',
535
+ })),
536
+ ],
537
+ }),
538
+ ).rejects.toMatchObject({
539
+ providerFailure: { failure: 'payload', id: 'relay', operation: 'createDepositAddress' },
540
+ })
541
+ })
542
+
543
+ test('rejects invalid refund addresses before calling the provider', async () => {
544
+ await expect(
545
+ FundingProvider.createDepositAddress({
546
+ ...depositAddressOptions,
547
+ providers: [
548
+ withCreateDepositAddress('relay', async () => {
549
+ throw new Error('provider should not be called')
550
+ }),
551
+ ],
552
+ refundAddress: `0x${'11'.repeat(20)}`,
553
+ }),
554
+ ).rejects.toThrowErrorMatchingInlineSnapshot(`[FundingProvider.InvalidRefundAddressError]`)
555
+ })
556
+ })
557
+
558
+ describe('getDepositAddressCandidates', () => {
559
+ test('selects every curated Relay stablecoin route', () => {
560
+ const routes = [
561
+ {
562
+ destinationToken: '0x20c000000000000000000000b9537d11c60e8b50',
563
+ sourceChain: 'ethereum',
564
+ sourceToken: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
565
+ },
566
+ {
567
+ destinationToken: '0x20c00000000000000000000014f22ca97301eb73',
568
+ sourceChain: 'ethereum',
569
+ sourceToken: '0xdac17f958d2ee523a2206206994597c13d831ec7',
570
+ },
571
+ {
572
+ destinationToken: '0x20c000000000000000000000b9537d11c60e8b50',
573
+ sourceChain: 'base',
574
+ sourceToken: '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913',
575
+ },
576
+ {
577
+ destinationToken: '0x20c00000000000000000000014f22ca97301eb73',
578
+ sourceChain: 'base',
579
+ sourceToken: '0xfde4c96c8593536e31f229ea8f37b2ada2699bb2',
580
+ },
581
+ {
582
+ destinationToken: '0x20c000000000000000000000b9537d11c60e8b50',
583
+ sourceChain: 'bnb',
584
+ sourceToken: '0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d',
585
+ },
586
+ {
587
+ destinationToken: '0x20c00000000000000000000014f22ca97301eb73',
588
+ sourceChain: 'bnb',
589
+ sourceToken: '0x55d398326f99059ff775485246999027b3197955',
590
+ },
591
+ {
592
+ destinationToken: '0x20c000000000000000000000b9537d11c60e8b50',
593
+ sourceChain: 'polygon',
594
+ sourceToken: '0x3c499c542cef5e3811e1192ce70d8cc03d5c3359',
595
+ },
596
+ {
597
+ destinationToken: '0x20c00000000000000000000014f22ca97301eb73',
598
+ sourceChain: 'polygon',
599
+ sourceToken: '0xc2132d05d31c914a87c6611c10748aeb04b58e8f',
600
+ },
601
+ {
602
+ destinationToken: '0x20c000000000000000000000b9537d11c60e8b50',
603
+ sourceChain: 'solana',
604
+ sourceToken: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
605
+ },
606
+ {
607
+ destinationToken: '0x20c00000000000000000000014f22ca97301eb73',
608
+ sourceChain: 'solana',
609
+ sourceToken: 'Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB',
610
+ },
611
+ {
612
+ destinationToken: '0x20c00000000000000000000014f22ca97301eb73',
613
+ sourceChain: 'tron',
614
+ sourceToken: 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t',
615
+ },
616
+ ]
617
+
618
+ expect(
619
+ routes.map((route) => {
620
+ const candidate = FundingProvider.getDepositAddressCandidates({
621
+ ...route,
622
+ catalog: TestFunding.snapshot,
623
+ destinationChainId: 4217,
624
+ providers: [providers[1]!],
625
+ })[0]?.candidate
626
+ return candidate
627
+ ? {
628
+ destinationToken: candidate.destinationToken.symbol,
629
+ sourceChain: candidate.sourceChain.id,
630
+ sourceToken: candidate.sourceToken.symbol,
631
+ }
632
+ : undefined
633
+ }),
634
+ ).toMatchInlineSnapshot(`
635
+ [
636
+ {
637
+ "destinationToken": "USDC.e",
638
+ "sourceChain": "eip155:1",
639
+ "sourceToken": "USDC",
640
+ },
641
+ {
642
+ "destinationToken": "USDT0",
643
+ "sourceChain": "eip155:1",
644
+ "sourceToken": "USDT",
645
+ },
646
+ {
647
+ "destinationToken": "USDC.e",
648
+ "sourceChain": "eip155:8453",
649
+ "sourceToken": "USDC",
650
+ },
651
+ {
652
+ "destinationToken": "USDT0",
653
+ "sourceChain": "eip155:8453",
654
+ "sourceToken": "USDT",
655
+ },
656
+ {
657
+ "destinationToken": "USDC.e",
658
+ "sourceChain": "eip155:56",
659
+ "sourceToken": "USDC",
660
+ },
661
+ {
662
+ "destinationToken": "USDT0",
663
+ "sourceChain": "eip155:56",
664
+ "sourceToken": "USDT",
665
+ },
666
+ {
667
+ "destinationToken": "USDC.e",
668
+ "sourceChain": "eip155:137",
669
+ "sourceToken": "USDC",
670
+ },
671
+ {
672
+ "destinationToken": "USDT0",
673
+ "sourceChain": "eip155:137",
674
+ "sourceToken": "USDT",
675
+ },
676
+ {
677
+ "destinationToken": "USDC.e",
678
+ "sourceChain": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",
679
+ "sourceToken": "USDC",
680
+ },
681
+ {
682
+ "destinationToken": "USDT0",
683
+ "sourceChain": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",
684
+ "sourceToken": "USDT",
685
+ },
686
+ {
687
+ "destinationToken": "USDT0",
688
+ "sourceChain": "tron:0x2b6653dc",
689
+ "sourceToken": "USDT",
690
+ },
691
+ ]
692
+ `)
693
+ })
694
+
695
+ test('requires both catalog and provider capability', () => {
696
+ const quoteOnly = FundingProvider.from({
697
+ getQuote: (input, signal) => providers[1]!.getQuote(input, signal),
698
+ id: 'relay',
699
+ name: 'Relay',
700
+ })
701
+ expect(
702
+ FundingProvider.getDepositAddressCandidates({
703
+ ...depositAddressOptions,
704
+ providers: [quoteOnly],
705
+ }),
706
+ ).toEqual([])
707
+ expect(
708
+ FundingProvider.getDepositAddressCandidates({
709
+ ...depositAddressOptions,
710
+ providers: [providers[1]!],
711
+ }),
712
+ ).toHaveLength(1)
713
+ })
714
+ })
715
+
716
+ describe('prepareTransfer', () => {
717
+ test('validates preparation results at the provider boundary', async () => {
718
+ const transfer = FundingProvider.prepareTransfer({
719
+ ...prepareOptions,
720
+ providers: [
721
+ withPrepareTransfer(
722
+ 'relay',
723
+ async () => ({ ...preparedResult(), sampledAt: 'invalid' }) as never,
724
+ ),
725
+ ],
726
+ })
727
+
728
+ await expect(transfer).rejects.toMatchObject({
729
+ providerFailure: { failure: 'payload', id: 'relay', operation: 'prepareTransfer' },
730
+ })
731
+ })
732
+
733
+ test('rounds the slippage floor upward', async () => {
734
+ const prepare = (destinationAmountMin: string) =>
735
+ FundingProvider.prepareTransfer({
736
+ ...prepareOptions,
737
+ providers: [
738
+ withPrepareTransfer('relay', async () => ({
739
+ ...preparedResult(),
740
+ destinationAmount: '10001',
741
+ destinationAmountMin,
742
+ })),
743
+ ],
744
+ slippageBps: 1,
745
+ })
746
+
747
+ await expect(prepare('9999')).rejects.toMatchObject({
748
+ providerFailure: { failure: 'payload', id: 'relay', operation: 'prepareTransfer' },
749
+ })
750
+ await expect(prepare('10000')).resolves.toMatchObject({
751
+ result: { destinationAmount: '10001', destinationAmountMin: '10000' },
752
+ })
753
+ })
754
+
755
+ test.each([
756
+ { expiresAt: '2026-01-01T00:00:59.999Z', sampledAt: '2026-01-01T00:00:00.000Z' },
757
+ { sampledAt: '2025-12-31T23:59:59.999Z' },
758
+ ])('rejects expired preparations %#', async (quote) => {
759
+ vi.useFakeTimers()
760
+ vi.setSystemTime(new Date('2026-01-01T00:01:00.000Z'))
761
+ try {
762
+ await expect(
763
+ FundingProvider.prepareTransfer({
764
+ ...prepareOptions,
765
+ providers: [
766
+ withPrepareTransfer('relay', async () => ({ ...preparedResult(), ...quote })),
767
+ ],
768
+ }),
769
+ ).rejects.toMatchObject({
770
+ providerFailure: { failure: 'payload', id: 'relay', operation: 'prepareTransfer' },
771
+ })
772
+ } finally {
773
+ vi.useRealTimers()
774
+ }
775
+ })
776
+
777
+ test('returns no selection when every provider confirms unavailability', async () => {
778
+ const attempts: FundingProvider.ProviderAttempt[] = []
779
+ await expect(
780
+ FundingProvider.prepareTransfer({
781
+ ...prepareOptions,
782
+ onAttempt: (attempt) => attempts.push(attempt),
783
+ providers: [
784
+ withPrepareTransfer('relay', async () => {
785
+ throw new FundingProvider.ProviderUnavailableError()
786
+ }),
787
+ ],
788
+ }),
789
+ ).resolves.toBeUndefined()
790
+ expect(attempts.map(stableAttempt)).toEqual([
791
+ { operation: 'prepareTransfer', outcome: 'unavailable', provider: 'relay' },
792
+ ])
793
+ })
794
+ })
795
+
796
+ describe('getPrepareCandidates', () => {
797
+ test('restricts candidates to the selected destination chain', () => {
798
+ const provider = withPrepareTransfer('relay', async () => preparedResult())
799
+ expect(
800
+ FundingProvider.getPrepareCandidates({
801
+ ...prepareOptions,
802
+ destinationChainId: 4217,
803
+ providers: [provider],
804
+ }),
805
+ ).toHaveLength(1)
806
+ expect(
807
+ FundingProvider.getPrepareCandidates({
808
+ ...prepareOptions,
809
+ destinationChainId: 421700001,
810
+ providers: [provider],
811
+ }),
812
+ ).toEqual([])
813
+ })
814
+
815
+ test('uses transfer modes without treating deposit addresses as transfers', () => {
816
+ const catalog = FundingCatalog.compile({
817
+ catalog: {
818
+ id: 'default',
819
+ updatedAt: '2026-01-01T00:00:00.000Z',
820
+ version: 'test',
821
+ },
822
+ ...TestFunding.data,
823
+ routes: TestFunding.data.routes.map((route) =>
824
+ route.capabilities
825
+ ? {
826
+ ...route,
827
+ capabilities: {
828
+ depositAddress: true,
829
+ transfer: { modes: ['exactDestination'] },
830
+ },
831
+ }
832
+ : route,
833
+ ),
834
+ })
835
+ const provider = withPrepareTransfer('relay', async () => preparedResult())
836
+ const candidates = (mode: 'exactDestination' | 'exactSource') =>
837
+ FundingProvider.getPrepareCandidates({
838
+ ...prepareOptions,
839
+ catalog,
840
+ mode,
841
+ providers: [provider],
842
+ })
843
+
844
+ expect(candidates('exactDestination')).toHaveLength(1)
845
+ expect(candidates('exactSource')).toEqual([])
846
+
847
+ const depositAddressOnly = FundingCatalog.compile({
848
+ catalog: {
849
+ id: 'default',
850
+ updatedAt: '2026-01-01T00:00:00.000Z',
851
+ version: 'test',
852
+ },
853
+ ...TestFunding.data,
854
+ routes: TestFunding.data.routes.map((route) =>
855
+ route.capabilities ? { ...route, capabilities: { depositAddress: true } } : route,
856
+ ),
857
+ })
858
+ expect(
859
+ FundingProvider.getPrepareCandidates({
860
+ ...prepareOptions,
861
+ catalog: depositAddressOnly,
862
+ providers: [provider],
863
+ }),
864
+ ).toEqual([])
865
+ })
866
+ })
867
+
868
+ function stableAttempt(attempt: FundingProvider.ProviderAttempt) {
869
+ const { durationMs: _durationMs, provider, ...rest } = attempt
870
+ return { ...rest, provider: provider.id }
871
+ }
872
+
873
+ function withGetQuote(
874
+ id: FundingProvider.ProviderId,
875
+ getQuote: FundingProvider.Provider['getQuote'],
876
+ ) {
877
+ const provider = providers.find((entry) => entry.id === id)
878
+ if (!provider) throw new Error(`Missing ${id} provider`)
879
+ return FundingProvider.from({ ...provider, getQuote })
880
+ }
881
+
882
+ function depositAddressResult(): FundingProvider.createDepositAddress.ReturnType {
883
+ return {
884
+ address: 'TJRabPrwbZy45sbavfcjinPJC18kjpRTv8',
885
+ correlation: { requestId: 'request_1' },
886
+ destinationAmount: '1000000',
887
+ destinationAmountMin: '1000000',
888
+ fees: [],
889
+ sampledAt: '2026-08-04T00:00:00.000Z',
890
+ }
891
+ }
892
+
893
+ function withCreateDepositAddress(
894
+ id: FundingProvider.ProviderId,
895
+ createDepositAddress: NonNullable<FundingProvider.Provider['createDepositAddress']>,
896
+ ) {
897
+ const provider = providers.find((entry) => entry.id === id)
898
+ if (!provider) throw new Error(`Missing ${id} provider`)
899
+ return FundingProvider.from({ ...provider, createDepositAddress })
900
+ }
901
+
902
+ function preparedResult(): FundingProvider.prepareTransfer.ReturnType {
903
+ return {
904
+ action: {
905
+ calls: [{ data: '0xe8017952', to: `0x${'44'.repeat(20)}`, value: '0x0' }],
906
+ type: 'evm:calls',
907
+ },
908
+ correlation: { requestId: `0x${'ab'.repeat(32)}` },
909
+ destinationAmount: '990000',
910
+ destinationAmountMin: '980000',
911
+ fees: [],
912
+ sampledAt: '2099-01-01T00:00:00.000Z',
913
+ }
914
+ }
915
+
916
+ function withPrepareTransfer(
917
+ id: FundingProvider.ProviderId,
918
+ prepareTransfer: NonNullable<FundingProvider.Provider['prepareTransfer']>,
919
+ ) {
920
+ const provider = providers.find((entry) => entry.id === id)
921
+ if (!provider) throw new Error(`Missing ${id} provider`)
922
+ return FundingProvider.from({ ...provider, prepareTransfer })
923
+ }
924
+
925
+ describe('getChains', () => {
926
+ test('derives custom chain and token inventory from provider routes', () => {
927
+ const catalog = FundingCatalog.compile({
928
+ catalog: {
929
+ id: 'default',
930
+ updatedAt: '2026-01-01T00:00:00.000Z',
931
+ version: 'custom',
932
+ },
933
+ chains: [
934
+ {
935
+ aliases: [],
936
+ id: 'eip155:10',
937
+ name: 'Optimism',
938
+ parentChainId: null,
939
+ rpcUrls: [],
940
+ slug: 'optimism',
941
+ },
942
+ {
943
+ aliases: [],
944
+ id: 'eip155:4217',
945
+ name: 'Tempo',
946
+ parentChainId: null,
947
+ rpcUrls: [],
948
+ slug: 'tempo',
949
+ },
950
+ ],
951
+ chainTokens: [
952
+ {
953
+ address: '0x0b2c639c533813f4aa9d7837caf62653d097ff85',
954
+ chainId: 'eip155:10',
955
+ decimals: 6,
956
+ name: null,
957
+ standard: 'ERC-20',
958
+ tokenId: 'usdc',
959
+ },
960
+ {
961
+ address: '0x20c000000000000000000000b9537d11c60e8b50',
962
+ chainId: 'eip155:4217',
963
+ decimals: 6,
964
+ name: null,
965
+ standard: 'TIP-20',
966
+ tokenId: 'usdce',
967
+ },
968
+ ],
969
+ routes: [
970
+ {
971
+ destinationChainId: 'eip155:4217',
972
+ destinationTokenId: 'usdce',
973
+ providerId: 'custom',
974
+ sourceChainId: 'eip155:10',
975
+ sourceTokenId: 'usdc',
976
+ },
977
+ ],
978
+ tokens: [
979
+ {
980
+ currency: 'USD',
981
+ id: 'usdc',
982
+ name: 'USD Coin',
983
+ symbol: 'USDC',
984
+ },
985
+ {
986
+ currency: 'USD',
987
+ id: 'usdce',
988
+ name: 'Bridged USDC (Stargate)',
989
+ symbol: 'USDC.e',
990
+ },
991
+ ],
992
+ })
993
+ const provider = FundingProvider.from({
994
+ id: 'custom',
995
+ name: 'Custom',
996
+ async getQuote() {
997
+ return { sampledAt: '2026-01-01T00:00:00Z', status: 'unavailable' }
998
+ },
999
+ })
1000
+
1001
+ expect(FundingProvider.getChains({ catalog, providers: [provider] })).toMatchInlineSnapshot(`
1002
+ [
1003
+ {
1004
+ "addressFormat": "hex",
1005
+ "id": "eip155:10",
1006
+ "kind": "evm",
1007
+ "name": "Optimism",
1008
+ "tokens": [
1009
+ {
1010
+ "address": "0x0b2c639c533813f4aa9d7837caf62653d097ff85",
1011
+ "currency": "USD",
1012
+ "decimals": 6,
1013
+ "name": "USD Coin",
1014
+ "standard": "ERC-20",
1015
+ "symbol": "USDC",
1016
+ "tokenKey": "eip155:10/erc20:0x0b2c639c533813f4aa9d7837caf62653d097ff85",
1017
+ "verified": true,
1018
+ },
1019
+ ],
1020
+ },
1021
+ ]
1022
+ `)
1023
+ })
1024
+
1025
+ test('returns only chain and token inputs with quote inventory', () => {
1026
+ expect(
1027
+ FundingProvider.getChains({ catalog: TestFunding.snapshot, providers }).map(
1028
+ (chain) => chain.id,
1029
+ ),
1030
+ ).toEqual([
1031
+ 'eip155:1',
1032
+ 'eip155:8453',
1033
+ 'eip155:56',
1034
+ 'eip155:137',
1035
+ 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',
1036
+ 'tron:0x2b6653dc',
1037
+ ])
1038
+ })
1039
+
1040
+ test('uses chain-specific token keys for non-EVM inventory', () => {
1041
+ const chains = FundingProvider.getChains({ catalog: TestFunding.snapshot, providers })
1042
+ expect(chains.find((chain) => chain.kind === 'solana')?.tokens[0]?.tokenKey).toBe(
1043
+ 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/token:EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
1044
+ )
1045
+ expect(chains.find((chain) => chain.kind === 'tron')?.tokens[0]?.tokenKey).toBe(
1046
+ 'tron:0x2b6653dc/trc20:TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t',
1047
+ )
1048
+ })
1049
+ })
1050
+
1051
+ describe('getQuoteCandidates', () => {
1052
+ test('validates base-unit syntax without configured providers', () => {
1053
+ expect(() =>
1054
+ FundingProvider.getQuoteCandidates({
1055
+ ...baseUsdcRequest,
1056
+ providers: [],
1057
+ sourceAmount: '1.5',
1058
+ }),
1059
+ ).toThrowErrorMatchingInlineSnapshot(`[FundingProvider.InvalidAmountError]`)
1060
+ })
1061
+
1062
+ test('converts decimal source amounts with token precision', () => {
1063
+ const [candidate] = FundingProvider.getQuoteCandidates({
1064
+ ...baseUsdcRequest,
1065
+ sourceAmount: '1.25',
1066
+ sourceAmountUnits: 'decimal',
1067
+ providers,
1068
+ })
1069
+
1070
+ expect(candidate?.sourceAmount).toEqual({ amount: '1250000', formatted: '1.25' })
1071
+ })
1072
+
1073
+ test('resolves normalized catalog aliases', () => {
1074
+ const catalog = FundingCatalog.compile({
1075
+ catalog: {
1076
+ id: 'default',
1077
+ updatedAt: '2026-01-01T00:00:00.000Z',
1078
+ version: 'aliases',
1079
+ },
1080
+ ...TestFunding.data,
1081
+ chains: TestFunding.data.chains.map((chain) =>
1082
+ chain.id === 'eip155:56' ? { ...chain, aliases: ['BSC'] } : chain,
1083
+ ),
1084
+ })
1085
+
1086
+ expect(
1087
+ FundingProvider.getQuoteCandidates({
1088
+ ...baseUsdcRequest,
1089
+ catalog,
1090
+ providers,
1091
+ sourceChain: 'bsc',
1092
+ })[0]?.sourceChain.id,
1093
+ ).toBe('eip155:56')
1094
+ })
1095
+
1096
+ test('uses canonical token slugs in quote ids', () => {
1097
+ const bridgedAddress = '0x1111111111111111111111111111111111111111'
1098
+ const catalog = FundingCatalog.compile({
1099
+ catalog: {
1100
+ id: 'default',
1101
+ updatedAt: '2026-01-01T00:00:00.000Z',
1102
+ version: 'duplicate-symbols',
1103
+ },
1104
+ ...TestFunding.data,
1105
+ chainTokens: [
1106
+ ...TestFunding.data.chainTokens,
1107
+ {
1108
+ address: bridgedAddress,
1109
+ chainId: 'eip155:8453',
1110
+ decimals: 6,
1111
+ name: null,
1112
+ standard: 'ERC-20',
1113
+ tokenId: 'usdc-bridged',
1114
+ },
1115
+ ],
1116
+ routes: [
1117
+ ...TestFunding.data.routes,
1118
+ {
1119
+ destinationChainId: 'eip155:4217',
1120
+ destinationTokenId: 'usdce',
1121
+ providerId: 'relay',
1122
+ sourceChainId: 'eip155:8453',
1123
+ sourceTokenId: 'usdc-bridged',
1124
+ },
1125
+ ],
1126
+ tokens: [
1127
+ ...TestFunding.data.tokens,
1128
+ {
1129
+ currency: 'USD',
1130
+ id: 'usdc-bridged',
1131
+ name: 'Bridged USD Coin',
1132
+ symbol: 'USDC',
1133
+ },
1134
+ ],
1135
+ })
1136
+ const ids = [
1137
+ FundingProvider.getQuoteCandidates({
1138
+ ...baseUsdcRequest,
1139
+ catalog,
1140
+ providers,
1141
+ sourceToken: TestFunding.data.chainTokens.find(
1142
+ (token) => token.chainId === 'eip155:8453' && token.tokenId === 'usdc',
1143
+ )!.address,
1144
+ })[0]?.id,
1145
+ FundingProvider.getQuoteCandidates({
1146
+ ...baseUsdcRequest,
1147
+ catalog,
1148
+ providers,
1149
+ sourceToken: bridgedAddress,
1150
+ })[0]?.id,
1151
+ ]
1152
+
1153
+ expect(ids).toEqual(['base-usdc-tempo-usdce-relay', 'base-usdc-bridged-tempo-usdce-relay'])
1154
+ })
1155
+
1156
+ test('rejects decimal source amounts beyond token precision', () => {
1157
+ expect(() =>
1158
+ FundingProvider.getQuoteCandidates({
1159
+ ...baseUsdcRequest,
1160
+ sourceAmount: '1.0000001',
1161
+ sourceAmountUnits: 'decimal',
1162
+ providers,
1163
+ }),
1164
+ ).toThrowErrorMatchingInlineSnapshot(`[FundingProvider.InvalidAmountError]`)
1165
+ })
1166
+
1167
+ test('round-trips the advertised Solana chain and token ids', () => {
1168
+ const [candidate] = FundingProvider.getQuoteCandidates({
1169
+ catalog: TestFunding.snapshot,
1170
+ providers,
1171
+ provider: 'rhino',
1172
+ sourceAmount: '1000000',
1173
+ sourceAmountUnits: 'baseUnits',
1174
+ sourceChain: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',
1175
+ sourceToken:
1176
+ 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/token:EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
1177
+ })
1178
+
1179
+ expect(candidate?.id).toBe('solana-usdc-tempo-usdce-rhino')
1180
+ })
1181
+
1182
+ test('keeps non-EVM token identifiers case-sensitive', () => {
1183
+ expect(
1184
+ FundingProvider.getQuoteCandidates({
1185
+ catalog: TestFunding.snapshot,
1186
+ providers,
1187
+ provider: 'rhino',
1188
+ sourceAmount: '1000000',
1189
+ sourceAmountUnits: 'baseUnits',
1190
+ sourceChain: 'solana',
1191
+ sourceToken: 'epjfwdd5aufqssqem2qn1xzybapc8g4wegkzwytdt1v',
1192
+ }),
1193
+ ).toEqual([])
1194
+ })
1195
+
1196
+ test('resolves the complete Tron token key', () => {
1197
+ const [candidate] = FundingProvider.getQuoteCandidates({
1198
+ catalog: TestFunding.snapshot,
1199
+ providers,
1200
+ destinationToken: 'USDT0',
1201
+ provider: 'rhino',
1202
+ sourceAmount: '1000000',
1203
+ sourceAmountUnits: 'baseUnits',
1204
+ sourceChain: 'tron:0x2b6653dc',
1205
+ sourceToken: 'tron:0x2b6653dc/trc20:TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t',
1206
+ })
1207
+
1208
+ expect(candidate?.id).toBe('tron-usdt-tempo-usdt0-rhino')
1209
+ })
1210
+
1211
+ test('selects proven Squid stablecoin corridors', () => {
1212
+ const candidates = [
1213
+ FundingProvider.getQuoteCandidates({
1214
+ catalog: TestFunding.snapshot,
1215
+ providers,
1216
+ destinationToken: 'USDC.e',
1217
+ provider: 'squid',
1218
+ sourceAmount: '1000000',
1219
+ sourceAmountUnits: 'baseUnits',
1220
+ sourceChain: 'base',
1221
+ sourceToken: 'USDC',
1222
+ })[0],
1223
+ FundingProvider.getQuoteCandidates({
1224
+ catalog: TestFunding.snapshot,
1225
+ providers,
1226
+ destinationToken: 'USDT0',
1227
+ provider: 'squid',
1228
+ sourceAmount: '1000000',
1229
+ sourceAmountUnits: 'baseUnits',
1230
+ sourceChain: 'ethereum',
1231
+ sourceToken: 'USDT',
1232
+ })[0],
1233
+ ]
1234
+
1235
+ expect(candidates.map((candidate) => candidate?.id)).toEqual([
1236
+ 'base-usdc-tempo-usdce-squid',
1237
+ 'ethereum-usdt-tempo-usdt0-squid',
1238
+ ])
1239
+ })
1240
+
1241
+ test('selects proven Symbiosis stablecoin corridors', () => {
1242
+ const candidates = [
1243
+ FundingProvider.getQuoteCandidates({
1244
+ catalog: TestFunding.snapshot,
1245
+ providers,
1246
+ destinationToken: 'USDC.e',
1247
+ provider: 'symbiosis',
1248
+ sourceAmount: '1000000',
1249
+ sourceAmountUnits: 'baseUnits',
1250
+ sourceChain: 'base',
1251
+ sourceToken: 'USDC',
1252
+ })[0],
1253
+ FundingProvider.getQuoteCandidates({
1254
+ catalog: TestFunding.snapshot,
1255
+ providers,
1256
+ destinationToken: 'USDT0',
1257
+ provider: 'symbiosis',
1258
+ sourceAmount: '1000000',
1259
+ sourceAmountUnits: 'baseUnits',
1260
+ sourceChain: 'ethereum',
1261
+ sourceToken: 'USDT',
1262
+ })[0],
1263
+ FundingProvider.getQuoteCandidates({
1264
+ catalog: TestFunding.snapshot,
1265
+ providers,
1266
+ destinationToken: 'USDT0',
1267
+ provider: 'symbiosis',
1268
+ sourceAmount: '1000000',
1269
+ sourceAmountUnits: 'baseUnits',
1270
+ sourceChain: 'tron',
1271
+ sourceToken: 'USDT',
1272
+ })[0],
1273
+ ]
1274
+
1275
+ expect(candidates.map((candidate) => candidate?.id)).toEqual([
1276
+ 'base-usdc-tempo-usdce-symbiosis',
1277
+ 'ethereum-usdt-tempo-usdt0-symbiosis',
1278
+ 'tron-usdt-tempo-usdt0-symbiosis',
1279
+ ])
1280
+ })
1281
+ })
1282
+
1283
+ describe('getProviders', () => {
1284
+ test('lists supplied providers', () => {
1285
+ expect(FundingProvider.getProviders({ catalog: TestFunding.snapshot, providers }))
1286
+ .toMatchInlineSnapshot(`
1287
+ [
1288
+ {
1289
+ "id": "across",
1290
+ "name": "Across",
1291
+ },
1292
+ {
1293
+ "id": "relay",
1294
+ "name": "Relay",
1295
+ },
1296
+ {
1297
+ "id": "rhino",
1298
+ "name": "Rhino",
1299
+ },
1300
+ {
1301
+ "id": "squid",
1302
+ "name": "Squid",
1303
+ },
1304
+ {
1305
+ "id": "symbiosis",
1306
+ "name": "Symbiosis",
1307
+ },
1308
+ ]
1309
+ `)
1310
+ })
1311
+
1312
+ test('ignores routes for unregistered provider ids', () => {
1313
+ const catalog = FundingCatalog.compile({
1314
+ catalog: {
1315
+ id: 'default',
1316
+ updatedAt: '2026-01-01T00:00:00.000Z',
1317
+ version: 'test',
1318
+ },
1319
+ ...TestFunding.data,
1320
+ routes: [{ ...TestFunding.data.routes[0]!, providerId: 'unknown' }],
1321
+ })
1322
+
1323
+ expect({
1324
+ chains: FundingProvider.getChains({ catalog, providers: [providers[1]!] }),
1325
+ providers: FundingProvider.getProviders({ catalog, providers: [providers[1]!] }),
1326
+ }).toEqual({ chains: [], providers: [] })
1327
+ })
1328
+ })